[
  {
    "path": ".clang-format",
    "content": "---\nBasedOnStyle: Google\nAlignAfterOpenBracket: Align\nAlignConsecutiveAssignments: 'true'\nAlignConsecutiveDeclarations: 'true'\nAlignOperands: 'true'\nAllowAllParametersOfDeclarationOnNextLine: 'false'\nAllowShortCaseLabelsOnASingleLine: 'false'\nAllowShortFunctionsOnASingleLine: Empty\nAllowShortLoopsOnASingleLine: 'false'\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: 'false'\nBinPackArguments: 'true'\nBinPackParameters: 'true'\nColumnLimit: '1000'\nIndentCaseLabels: 'true'\nIndentPPDirectives: AfterHash\nIndentWidth: '4'\nMaxEmptyLinesToKeep: '1'\nPointerAlignment: Right\nSortIncludes: 'false'\nSpaceBeforeAssignmentOperators: 'true'\nSpaceBeforeParens: ControlStatements\nSpaceInEmptyParentheses: 'false'\nSpacesBeforeTrailingComments: 1\nTabWidth: '4'\nUseTab: Never\n\n...\n"
  },
  {
    "path": ".clangd",
    "content": "CompileFlags:\n  Add: [-Wno-unknown-attributes, -Wno-maybe-uninitialized, -Wno-unknown-warning-option]\n  Remove: [-W*, -mmcu=*, -mcpu=*, -mfpu=*, -mfloat-abi=*, -mno-unaligned-access, -mno-thumb-interwork, -mcall-prologues, -D__has_include*]\n  Compiler: clang\n"
  },
  {
    "path": ".editorconfig",
    "content": "# EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs\n# editorconfig.org\n\nroot = true\n\n[*]\nend_of_line = lf\nindent_style = space\nindent_size = 4\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n # To match GitHub Actions formatting\n[*.{yaml,yml}]\nindent_size = 2\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[{Makefile,*.mk}]\nindent_style = tab\n\n# Don't override anything in `lib/`...\n[lib/**]\nindent_style = unset\nindent_size = unset\ntab_width = unset\nend_of_line = unset\ncharset = unset\nspelling_language = unset\ntrim_trailing_whitespace = unset\ninsert_final_newline = unset\n\n# ...except QMK's `lib/python`.\n[{*.py,lib/python/**.py}]\nend_of_line = lf\nindent_style = space\nindent_size = 4\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nmax_line_length = 200\n"
  },
  {
    "path": ".gitattributes",
    "content": "# auto for anything unspecified\n* text=auto\n\n# sources\n*.c      text eol=lf\n*.cc     text eol=lf\n*.cxx    text eol=lf\n*.cpp    text eol=lf\n*.c++    text eol=lf\n*.hpp    text eol=lf\n*.h      text eol=lf\n*.h++    text eol=lf\n*.hh     text eol=lf\n*.bat    text eol=crlf\n*.cmd    text eol=crlf\n*.coffee text eol=lf\n*.css    text eol=lf\n*.htm    text eol=lf\n*.html   text eol=lf\n*.inc    text eol=lf\n*.ini    text eol=crlf\n*.js     text eol=lf\n*.jsx    text eol=lf\n*.json   text eol=lf\n*.less   text eol=lf\n*.php    text eol=lf\n*.pl     text eol=lf\n*.py     text eol=lf\n*.rb     text eol=lf\n*.sass   text eol=lf\n*.scm    text eol=lf\n*.scss   text eol=lf\n*.sh     text eol=lf\n*.sql    text eol=lf\n*.styl   text eol=lf\n*.ts     text eol=lf\n*.xml    text eol=lf\n*.xhtml  text eol=lf\n\n# make files (need to always use lf for compatibility with Windows 10 bash)\nMakefile eol=lf\n*.mk     eol=lf\n\n# make files (need to always use lf for compatibility with Windows 10 bash)\n*.sh eol=lf\n\n# documentation\n*.markdown   text eol=lf\n*.md         text eol=lf\n*.mdwn       text eol=lf\n*.mdown      text eol=lf\n*.mkd        text eol=lf\n*.mkdn       text eol=lf\n*.mdtxt      text eol=lf\n*.mdtext     text eol=lf\n*.txt        text eol=lf\nAUTHORS      text eol=lf\nCHANGELOG    text eol=lf\nCHANGES      text eol=lf\nCONTRIBUTING text eol=lf\nCOPYING      text eol=lf\nINSTALL      text eol=lf\nlicense      text eol=lf\nLICENSE      text eol=lf\nNEWS         text eol=lf\nreadme       text eol=lf\n*README*     text eol=lf\nTODO         text eol=lf\n\nGRAPHICS\n*.ai   binary\n*.bmp  binary\n*.eps  binary\n*.gif  binary\n*.ico  binary\n*.jng  binary\n*.jp2  binary\n*.jpg  binary\n*.jpeg binary\n*.jpx  binary\n*.jxr  binary\n*.pdf  binary\n*.png  binary\n*.psb  binary\n*.psd  binary\n*.svg  text eol=lf\n*.svgz binary\n*.tif  binary\n*.tiff binary\n*.wbmp binary\n*.webp binary\n\n# hex files\n*.hex binary\n*.eep binary\nnix/sources.nix linguist-generated=true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug report\ndescription: Create a report to help us improve QMK Firmware.\ntitle: \"[Bug] \"\nlabels: [\"bug\", \"help wanted\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Provide a general summary of the bug in the title above.\n  - type: textarea\n    attributes:\n      label: Describe the Bug\n      description: A clear and concise description of what the bug is.\n  - type: input\n    attributes:\n      label: Keyboard Used\n      description: The name of the keyboard from the `make` or `qmk compile`/`qmk flash` commands, eg. `planck/rev6`.\n  - type: input\n    attributes:\n      label: Link to product page (if applicable)\n  - type: input\n    attributes:\n      label: Operating System\n  - type: textarea\n    attributes:\n      label: qmk doctor Output\n      description: Output from running the `qmk doctor` command.\n      render: text\n  - type: checkboxes\n    attributes:\n      label: Is AutoHotKey / Karabiner installed\n      options:\n        - label: AutoHotKey (Windows)\n        - label: Karabiner (macOS)\n  - type: input\n    attributes:\n      label: Other keyboard-related software installed\n  - type: textarea\n    attributes:\n      label: Additional Context\n      description: Add any other relevant information about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: QMK Discord\n    url: https://discord.gg/qmk\n    about: Ask questions, discuss issues and features. Chill.\n  - name: OLKB Subreddit\n    url: https://www.reddit.com/r/olkb\n    about: All things OLKB and QMK.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: Feature request\ndescription: Suggest a new feature or changes to existing features.\ntitle: \"[Feature Request] \"\nlabels: [\"enhancement\", \"help wanted\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Provide a general summary of the changes you want in the title above.\n\n        Please refrain from asking maintainers to add support for specific keyboards -- it is unlikely they will have hardware available, and will not be able to help.\n        Your best bet is to take the initiative, add support, then submit a PR yourself.\n  - type: checkboxes\n    attributes:\n      label: Feature Request Type\n      options:\n        - label: Core functionality\n        - label: Add-on hardware support (eg. audio, RGB, OLED screen, etc.)\n        - label: Alteration (enhancement/optimization) of existing feature(s)\n        - label: New behavior\n  - type: textarea\n    attributes:\n      label: Description\n      description: A few sentences describing what it is that you'd like to see in QMK. Additional information (such as links to spec sheets, licensing info, other related issues or PRs, etc) would be helpful.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/other_issues.yml",
    "content": "name: Other issues\ndescription: Anything else that doesn't fall into the above categories.\nlabels: [\"help wanted\", \"question\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Provide a general summary of the changes you want in the title above.\n  - type: markdown\n    attributes:\n      value: |\n        Please check [https://docs.qmk.fm/#/support](https://docs.qmk.fm/#/support) for additional resources first. If that doesn't answer your question, choose the bug report template instead, as that may be more appropriate.\n\n        Please refrain from asking maintainers to add support for specific keyboards -- it is unlikely they will have hardware available, and will not be able to help.\n        Your best bet is to take the initiative, add support, then submit a PR yourself.\n  - type: textarea\n    attributes:\n      label: Issue Description\n      description: Describe your issue in as much detail as possible.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/zzz_blank.md",
    "content": "---\nname: Blank issue\nabout: If you're 100% sure that you don't need one of the other issue templates, use\n  this one instead.\ntitle: ''\nlabels: help wanted, question\nassignees: ''\n\n---\n\n\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--- Provide a general summary of your changes in the title above. -->\n\n<!--- Anything on lines wrapped in comments like these will not show up in the final text. -->\n\n## Description\n\n<!--- Describe your changes in detail here. -->\n\n## Types of Changes\n\n<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply. -->\n- [ ] Core\n- [ ] Bugfix\n- [ ] New feature\n- [ ] Enhancement/optimization\n- [ ] Keyboard (addition or update)\n- [ ] Keymap/layout (addition or update)\n- [ ] Documentation\n\n## Issues Fixed or Closed by This PR\n\n* \n\n## Checklist\n\n<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->\n<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->\n- [ ] My code follows the code style of this project: [**C**](https://docs.qmk.fm/#/coding_conventions_c), [**Python**](https://docs.qmk.fm/#/coding_conventions_python)\n- [ ] I have read the [**PR Checklist** document](https://docs.qmk.fm/#/pr_checklist) and have made the appropriate changes.\n- [ ] My change requires a change to the documentation.\n- [ ] I have updated the documentation accordingly.\n- [ ] I have read the [**CONTRIBUTING** document](https://docs.qmk.fm/#/contributing).\n- [ ] I have added tests to cover my changes.\n- [ ] I have tested the changes and verified that they work and don't break anything (as well as I can manage).\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    labels: CI\n    reviewers:\n      - \"qmk/collaborators\"\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/labeler.yml",
    "content": "core:\n  - changed-files:\n    - any-glob-to-any-file:\n      - quantum/**\n      - tmk_core/**\n      - drivers/**\n      - tests/**\n      - util/**\n      - platforms/**\n      - builddefs/*.mk\n      - Makefile\n      - '*.mk'\ndependencies:\n  - changed-files:\n    - all-globs-to-any-file:\n      - lib/**\n      - '!lib/python/**'\nkeyboard:\n  - changed-files:\n    - all-globs-to-any-file:\n      - keyboards/**\n      - '!keyboards/**/keymaps/**'\nkeymap:\n  - changed-files:\n    - any-glob-to-any-file:\n      - users/**\n      - layouts/**\n      - keyboards/**/keymaps/**\nvia:\n  - changed-files:\n    - any-glob-to-any-file:\n      - keyboards/**/keymaps/via/*\ncli:\n  - changed-files:\n    - any-glob-to-any-file:\n      - requirements.txt\n      - lib/python/**\npython:\n  - changed-files:\n    - any-glob-to-any-file:\n      - '**/*.py'\ndocumentation:\n  - changed-files:\n    - any-glob-to-any-file:\n      - docs/**\n      - builddefs/docsgen/**\nCI:\n  - changed-files:\n    - any-glob-to-any-file:\n      - .github/**\ndd:\n  - changed-files:\n    - any-glob-to-any-file:\n      - data/constants/**\n      - data/mappings/**\n      - data/schemas/**\ncommunity_module:\n  - changed-files:\n    - any-glob-to-any-file:\n      - modules/**\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build firmware\non:\n  push:\n    paths-ignore:\n      - '**.md'\n  pull_request:\n    paths-ignore:\n      - '**.md'\n  workflow_dispatch:\n\n\njobs:\n  build-firmware:\n    runs-on: ubuntu-latest\n    container: qmkfm/qmk_cli\n    strategy:\n      fail-fast: false\n      matrix:\n        keymap:\n          - default\n          - oryx\n\n    steps:\n    - name: Checkout QMK Firmware\n      uses: actions/checkout@v4\n      with:\n        fetch-depth: 1\n        persist-credentials: false\n        submodules: true\n\n    - name: Build\n      id: build\n      run: |\n        qmk mass-compile -f manufacturer=\"ZSA Technology Labs\" -km ${{ matrix.keymap }} -e SKIP_GIT=1\n"
  },
  {
    "path": ".github/workflows/format.yml",
    "content": "name: PR Lint Format\n\npermissions:\n  contents: read\n\non:\n  pull_request:\n    paths:\n    - 'drivers/**'\n    - 'lib/arm_atsam/**'\n    - 'lib/lib8tion/**'\n    - 'lib/python/**'\n    - 'modules/**'\n    - 'platforms/**'\n    - 'quantum/**'\n    - 'tests/**'\n    - 'tmk_core/**'\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n\n    container: ghcr.io/qmk/qmk_cli\n\n    steps:\n    - name: Disable safe.directory check\n      run : git config --global --add safe.directory '*'\n\n    - uses: actions/checkout@v4\n      with:\n        fetch-depth: 0\n\n    - name: Install dependencies\n      run: |\n        pip3 install -r requirements-dev.txt\n\n    - name: Get changed files\n      id: file_changes\n      uses: tj-actions/changed-files@v46\n      with:\n        use_rest_api: true\n\n    - name: Run qmk formatters\n      shell: 'bash {0}'\n      run: |\n        echo '${{ steps.file_changes.outputs.added_files}}' '${{ steps.file_changes.outputs.modified_files}}' > ~/files_changed.txt\n        qmk format-c --core-only $(< ~/files_changed.txt) || true\n        qmk format-python $(< ~/files_changed.txt) || true\n        qmk format-text $(< ~/files_changed.txt) || true\n\n    - name: Fail when formatting required\n      run: |\n        git diff\n        for file in $(git diff --name-only); do\n          echo \"File '${file}' Requires Formatting\"\n          echo \"::error file=${file}::Requires Formatting\"\n        done\n        test -z \"$(git diff --name-only)\"\n"
  },
  {
    "path": ".github/workflows/lint.yml",
    "content": "name: PR Lint keyboards\n\npermissions:\n  contents: read\n\non:\n  pull_request:\n    paths:\n    - 'keyboards/**'\n\njobs:\n  lint:\n    runs-on: ubuntu-latest\n\n    container: ghcr.io/qmk/qmk_cli\n\n    steps:\n    - name: Disable safe.directory check\n      run : git config --global --add safe.directory '*'\n\n    - uses: actions/checkout@v4\n      with:\n        fetch-depth: 0\n\n    - name: Install dependencies\n      run: pip3 install -r requirements-dev.txt\n\n    - name: Get changed files\n      id: file_changes\n      uses: tj-actions/changed-files@v46\n      with:\n        use_rest_api: true\n\n    - name: Print info\n      run: |\n        git rev-parse --short HEAD\n        echo ${{ github.event.pull_request.base.sha }}\n        echo '${{ steps.file_changes.outputs.all_changed_files}}'\n\n    - name: Run qmk lint\n      if: always()\n      shell: 'bash {0}'\n      run: |\n        QMK_CHANGES=$(echo -e '${{ steps.file_changes.outputs.all_changed_files}}' | sed 's/ /\\n/g')\n        QMK_KEYBOARDS=$(qmk list-keyboards)\n\n        exit_code=0\n\n        for KB in $QMK_KEYBOARDS; do\n          KEYBOARD_CHANGES=$(echo \"$QMK_CHANGES\" | grep -E '^(keyboards/'${KB}'/)')\n          if [[ -z \"$KEYBOARD_CHANGES\" ]]; then\n            # skip as no changes for this keyboard\n            continue\n          fi\n\n          KEYMAP_ONLY=$(echo \"$KEYBOARD_CHANGES\" | grep -cv /keymaps/)\n          if [[ $KEYMAP_ONLY -gt 0 ]]; then\n            echo \"linting ${KB}\"\n\n            qmk lint --keyboard ${KB} && qmk info -l --keyboard ${KB}\n            exit_code=$(($exit_code + $?))\n          fi\n        done\n\n        qmk format-text ${{ steps.file_changes.outputs.all_changed_files}} || true\n        for file in ${{ steps.file_changes.outputs.all_changed_files}}; do\n          if [[ -f $file ]]; then\n            if ! git diff --quiet $file; then\n              echo \"File '${file}' Requires Formatting\"\n              echo \"::error file=${file}::Requires Formatting\"\n              exit_code=$(($exit_code + 1))\n            fi\n          fi\n        done\n\n        if [[ $exit_code -gt 255 ]]; then\n            exit 255\n        fi\n        exit $exit_code\n\n    - name: Verify keyboard aliases\n      if: always()\n      shell: 'bash {0}'\n      run: |\n        git reset --hard\n        git clean -xfd\n        qmk ci-validate-aliases\n"
  },
  {
    "path": ".github/workflows/regen.yml",
    "content": "name: PR Regenerate Files\n\npermissions:\n  contents: read\n\non:\n  pull_request:\n    paths:\n    - 'data/constants/**'\n    - 'lib/python/**'\n\njobs:\n  regen:\n    runs-on: ubuntu-latest\n\n    container: ghcr.io/qmk/qmk_cli\n\n    steps:\n    - name: Disable safe.directory check\n      run : git config --global --add safe.directory '*'\n\n    - uses: actions/checkout@v4\n\n    - name: Run qmk generators\n      run: |\n        util/regen.sh\n        git diff\n\n    - name: Fail when regeneration required\n      run: |\n        git diff\n        for file in $(git diff --name-only); do\n          echo \"File '${file}' Requires Regeneration\"\n          echo \"::error file=${file}::Requires Regeneration\"\n        done\n        test -z \"$(git diff --name-only)\"\n"
  },
  {
    "path": ".github/workflows/unit_test.yml",
    "content": "name: Unit Tests\n\npermissions:\n  contents: read\n\non:\n  push:\n    branches:\n    - firmware[0-9]+\n  pull_request:\n    paths:\n    - 'builddefs/**'\n    - 'quantum/**'\n    - 'platforms/**'\n    - 'tmk_core/**'\n    - 'tests/**'\n    - '*.mk'\n    - 'Makefile'\n    - '.github/workflows/unit_test.yml'\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    container: ghcr.io/qmk/qmk_cli\n\n    steps:\n    - uses: actions/checkout@v4\n      with:\n        submodules: recursive\n    - name: Install dependencies\n      run: pip3 install -r requirements-dev.txt\n    - name: Run tests\n      run: qmk test-c\n"
  },
  {
    "path": ".gitignore",
    "content": "# Junk files\n*.bak\n*.swp\n*~\n.DS_Store\n._*\n\n# Merge files\n*.orig\n*.rej\n\n# Build artifacts\n.clang_complete\n.build/\n*.elf\n*.log\n*.lss\n*.lst\n*.map\n*.o\n*.a\n*.so\n*.dylib\n*.dll\n*.la\n*.stackdump\n*.sym\nindex.html\nfirmware_list.json\n\n# QMK-specific\napi_data/v1\nquantum/version.h\n*.bin\n*.eep\n*.hex\n*.qmk\n*.uf2\n\n# DD config at wrong location\n/keyboards/**/keymaps/*/info.json\n/keyboards/**/keymaps/*/keyboard.json\n\n# Old-style QMK Makefiles\n/keyboards/**/Makefile\n\n# kbfirmware....\n/keyboards/**/kb.h\n/keyboards/**/kb.c\n\n# Eclipse/PyCharm/Other IDE Settings\n*.iml\n.browse.VC.db*\n.cproject\n.idea\n.idea/\n.project\n.settings/\n\n# ?\n.dep\n.history/\nbuild/\ncmake-build-debug\nCMakeLists.txt\n*.pdf\n\n# Let these ones be user specific, since we have so many different configurations\n*.code-workspace\n.stfolder\n.tags\n.vscode/c_cpp_properties.json\n.vscode/ipch/\n.vscode/last.sql\n.vscode/launch.json\n.vscode/tasks.json\n.vscode/temp.sql\ntags\n\n# Ignore image/font files\n*.bmp\n*.wbmp\n*.gif\n*.jpg\n*.jpeg\n*.png\n*.apng\n*.mng\n*.svg\n*.webp\n*.webm\n*.avi\n*.mp4\n*.mpeg\n*.ttf\n*.otf\n\n# Things Travis sees\n/.vs\nid_rsa_*\nsecrets.tar\n\n# Python things\n__pycache__\n.python-version\n.venv\n\n# Prerequisites for updating ChibiOS\n/util/fmpp*\n\n# Allow to exist but don't include it in the repo\nuser_song_list.h\n\n# clangd\ncompile_commands.json\n.clangd/\n.cache/\n\n# VIA(L) files that don't belong in QMK repo\nvia*.json\n/keyboards/**/keymaps/via/*\n/keyboards/**/keymaps/vial/*\n\n# ZSA Specific ignores\n/docs/**\n/users/**\n/layouts/community/**\n/keyboards/**\n!/keyboards/zsa/\n!/keyboards/zsa/**\n/keyboards/zsa/**/keymaps/**\n!/keyboards/zsa/**/keymaps/default\n!/keyboards/zsa/**/keymaps/default/**\n!/keyboards/zsa/**/keymaps/oryx\n!/keyboards/zsa/**/keymaps/oryx/**\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"lib/chibios\"]\n\tpath = lib/chibios\n\turl = https://github.com/qmk/ChibiOS\n\tbranch = master\n[submodule \"lib/chibios-contrib\"]\n\tpath = lib/chibios-contrib\n\turl = https://github.com/qmk/ChibiOS-Contrib\n\tbranch = master\n[submodule \"lib/googletest\"]\n\tpath = lib/googletest\n\turl = https://github.com/qmk/googletest\n[submodule \"lib/lufa\"]\n\tpath = lib/lufa\n\turl = https://github.com/qmk/lufa\n[submodule \"lib/vusb\"]\n\tpath = lib/vusb\n\turl = https://github.com/qmk/v-usb\n[submodule \"lib/printf\"]\n\tpath = lib/printf\n\turl = https://github.com/qmk/printf\n[submodule \"lib/pico-sdk\"]\n\tpath = lib/pico-sdk\n\turl = https://github.com/qmk/pico-sdk.git\n[submodule \"lib/lvgl\"]\n\tpath = lib/lvgl\n\turl = https://github.com/qmk/lvgl.git\n\tbranch = release/v8.2\n[submodule \"modules/zsa\"]\n\tpath = modules/zsa\n\turl = https://github.com/zsa/qmk_modules.git\n"
  },
  {
    "path": "Doxyfile",
    "content": "# Doxyfile 1.8.14\n\n# This file describes the settings to be used by the documentation system\n# doxygen (www.doxygen.org) for qmk_firmware (github.com/qmk/qmk_firmware)\n#\n# All text after a double hash (##) is considered a comment and is placed in\n# front of the TAG it is preceding.\n#\n# All text after a single hash (#) is considered a comment and will be ignored.\n# The format is:\n# TAG = value [value, ...]\n# For lists, items can also be appended using:\n# TAG += value [value, ...]\n# Values that contain spaces should be placed between quotes (\\\" \\\").\n\n#---------------------------------------------------------------------------\n# Project related configuration options\n#---------------------------------------------------------------------------\n\nDOXYFILE_ENCODING      = UTF-8\nPROJECT_NAME           = \"QMK Firmware\"\nPROJECT_NUMBER         = https://github.com/qmk/qmk_firmware\nPROJECT_BRIEF          = \"Keyboard controller firmware for Atmel AVR and ARM USB families\"\nOUTPUT_DIRECTORY       = .build/docs/static/doxygen\nALLOW_UNICODE_NAMES    = NO\nOUTPUT_LANGUAGE        = English\nBRIEF_MEMBER_DESC      = YES\nREPEAT_BRIEF           = YES\nABBREVIATE_BRIEF       = \"The $name class\" \\\n                         \"The $name widget\" \\\n                         \"The $name file\" \\\n                         is \\\n                         provides \\\n                         specifies \\\n                         contains \\\n                         represents \\\n                         a \\\n                         an \\\n                         the\nALWAYS_DETAILED_SEC    = NO\nINLINE_INHERITED_MEMB  = NO\nFULL_PATH_NAMES        = YES\nSTRIP_FROM_PATH        =\nSTRIP_FROM_INC_PATH    =\nSHORT_NAMES            = NO\nJAVADOC_AUTOBRIEF      = NO\nQT_AUTOBRIEF           = NO\nMULTILINE_CPP_IS_BRIEF = NO\nINHERIT_DOCS           = YES\nSEPARATE_MEMBER_PAGES  = NO\nTAB_SIZE               = 4\nALIASES                =\nTCL_SUBST              =\nOPTIMIZE_OUTPUT_FOR_C  = YES\nOPTIMIZE_OUTPUT_JAVA   = NO\nOPTIMIZE_FOR_FORTRAN   = NO\nOPTIMIZE_OUTPUT_VHDL   = NO\nEXTENSION_MAPPING      =\nMARKDOWN_SUPPORT       = YES\nTOC_INCLUDE_HEADINGS   = 2\nAUTOLINK_SUPPORT       = YES\nBUILTIN_STL_SUPPORT    = NO\nCPP_CLI_SUPPORT        = NO\nSIP_SUPPORT            = NO\nIDL_PROPERTY_SUPPORT   = YES\nDISTRIBUTE_GROUP_DOC   = NO\nGROUP_NESTED_COMPOUNDS = NO\nSUBGROUPING            = YES\nINLINE_GROUPED_CLASSES = NO\nINLINE_SIMPLE_STRUCTS  = NO\nTYPEDEF_HIDES_STRUCT   = NO\nLOOKUP_CACHE_SIZE      = 0\n\n#---------------------------------------------------------------------------\n# Build related configuration options\n#---------------------------------------------------------------------------\n\nEXTRACT_ALL            = NO\nEXTRACT_PRIVATE        = NO\nEXTRACT_PACKAGE        = NO\nEXTRACT_STATIC         = NO\nEXTRACT_LOCAL_CLASSES  = YES\nEXTRACT_LOCAL_METHODS  = NO\nEXTRACT_ANON_NSPACES   = NO\nHIDE_UNDOC_MEMBERS     = NO\nHIDE_UNDOC_CLASSES     = NO\nHIDE_FRIEND_COMPOUNDS  = NO\nHIDE_IN_BODY_DOCS      = NO\nINTERNAL_DOCS          = NO\nCASE_SENSE_NAMES       = NO\nHIDE_SCOPE_NAMES       = YES\nHIDE_COMPOUND_REFERENCE= NO\nSHOW_INCLUDE_FILES     = YES\nSHOW_GROUPED_MEMB_INC  = NO\nFORCE_LOCAL_INCLUDES   = NO\nINLINE_INFO            = YES\nSORT_MEMBER_DOCS       = YES\nSORT_BRIEF_DOCS        = NO\nSORT_MEMBERS_CTORS_1ST = NO\nSORT_GROUP_NAMES       = NO\nSORT_BY_SCOPE_NAME     = NO\nSTRICT_PROTO_MATCHING  = NO\nGENERATE_TODOLIST      = YES\nGENERATE_TESTLIST      = YES\nGENERATE_BUGLIST       = YES\nGENERATE_DEPRECATEDLIST= YES\nENABLED_SECTIONS       =\nMAX_INITIALIZER_LINES  = 30\nSHOW_USED_FILES        = YES\nSHOW_FILES             = YES\nSHOW_NAMESPACES        = YES\nFILE_VERSION_FILTER    =\nLAYOUT_FILE            =\nCITE_BIB_FILES         =\n\n#---------------------------------------------------------------------------\n# Configuration options related to warning and progress messages\n#---------------------------------------------------------------------------\n\nQUIET                  = NO\nWARNINGS               = YES\nWARN_IF_UNDOCUMENTED   = YES\nWARN_IF_DOC_ERROR      = YES\nWARN_NO_PARAMDOC       = NO\nWARN_AS_ERROR          = NO\nWARN_FORMAT            = \"$file:$line: $text\"\nWARN_LOGFILE           =\n\n#---------------------------------------------------------------------------\n# Configuration options related to the input files\n#---------------------------------------------------------------------------\n\nINPUT                  = tmk_core quantum drivers\nINPUT_ENCODING         = UTF-8\nFILE_PATTERNS          = *.c \\\n                         *.cc \\\n                         *.cxx \\\n                         *.cpp \\\n                         *.c++ \\\n                         *.h \\\n                         *.hh \\\n                         *.hxx \\\n                         *.hpp \\\n                         *.h++\nRECURSIVE              = YES\nEXCLUDE                =\nEXCLUDE_SYMLINKS       = NO\nEXCLUDE_PATTERNS       =\nEXCLUDE_SYMBOLS        =\nEXAMPLE_PATH           =\nEXAMPLE_PATTERNS       = *\nEXAMPLE_RECURSIVE      = NO\nIMAGE_PATH             =\nINPUT_FILTER           =\nFILTER_PATTERNS        =\nFILTER_SOURCE_FILES    = NO\nFILTER_SOURCE_PATTERNS =\nUSE_MDFILE_AS_MAINPAGE =\n\n#---------------------------------------------------------------------------\n# Configuration options related to source browsing\n#---------------------------------------------------------------------------\n\nSOURCE_BROWSER         = YES\nINLINE_SOURCES         = NO\nSTRIP_CODE_COMMENTS    = YES\nREFERENCED_BY_RELATION = NO\nREFERENCES_RELATION    = NO\nREFERENCES_LINK_SOURCE = YES\nSOURCE_TOOLTIPS        = YES\nUSE_HTAGS              = NO\nVERBATIM_HEADERS       = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the alphabetical class index\n#---------------------------------------------------------------------------\n\nALPHABETICAL_INDEX     = YES\nCOLS_IN_ALPHA_INDEX    = 5\nIGNORE_PREFIX          =\n\n#---------------------------------------------------------------------------\n# Configuration options related to disabled outputs\n#---------------------------------------------------------------------------\n\nGENERATE_HTML          = NO\nGENERATE_LATEX         = NO\nGENERATE_RTF           = NO\nGENERATE_MAN           = NO\nGENERATE_DOCBOOK       = NO\nGENERATE_AUTOGEN_DEF   = NO\nGENERATE_PERLMOD       = NO\n\n#---------------------------------------------------------------------------\n# Configuration options related to the XML output\n#---------------------------------------------------------------------------\n\nGENERATE_XML           = YES\nXML_OUTPUT             = xml\nXML_PROGRAMLISTING     = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to the preprocessor\n#---------------------------------------------------------------------------\n\nENABLE_PREPROCESSING   = YES\nMACRO_EXPANSION        = NO\nEXPAND_ONLY_PREDEF     = NO\nSEARCH_INCLUDES        = YES\nINCLUDE_PATH           =\nINCLUDE_FILE_PATTERNS  =\nPREDEFINED             = __DOXYGEN__ PROGMEM\nEXPAND_AS_DEFINED      =\nSKIP_FUNCTION_MACROS   = YES\n\n#---------------------------------------------------------------------------\n# Configuration options related to external references\n#---------------------------------------------------------------------------\n\nTAGFILES               =\nGENERATE_TAGFILE       =\nALLEXTERNALS           = NO\nEXTERNAL_GROUPS        = YES\nEXTERNAL_PAGES         = YES\nPERL_PATH              = /usr/bin/perl\n\n#---------------------------------------------------------------------------\n# Configuration options related to the dot tool\n#---------------------------------------------------------------------------\n\nCLASS_DIAGRAMS         = YES\nMSCGEN_PATH            =\nDIA_PATH               =\nHIDE_UNDOC_RELATIONS   = YES\nHAVE_DOT               = NO\nDOT_NUM_THREADS        = 0\nDOT_FONTNAME           = Helvetica\nDOT_FONTSIZE           = 10\nDOT_FONTPATH           =\nCLASS_GRAPH            = YES\nCOLLABORATION_GRAPH    = YES\nGROUP_GRAPHS           = YES\nUML_LOOK               = NO\nUML_LIMIT_NUM_FIELDS   = 10\nTEMPLATE_RELATIONS     = NO\nINCLUDE_GRAPH          = YES\nINCLUDED_BY_GRAPH      = YES\nCALL_GRAPH             = NO\nCALLER_GRAPH           = NO\nGRAPHICAL_HIERARCHY    = YES\nDIRECTORY_GRAPH        = YES\nDOT_IMAGE_FORMAT       = png\nINTERACTIVE_SVG        = NO\nDOT_PATH               =\nDOTFILE_DIRS           =\nMSCFILE_DIRS           =\nDIAFILE_DIRS           =\nPLANTUML_JAR_PATH      =\nPLANTUML_CFG_FILE      =\nPLANTUML_INCLUDE_PATH  =\nDOT_GRAPH_MAX_NODES    = 50\nMAX_DOT_GRAPH_DEPTH    = 0\nDOT_TRANSPARENT        = NO\nDOT_MULTI_TARGETS      = NO\nGENERATE_LEGEND        = YES\nDOT_CLEANUP            = YES\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "Makefile",
    "content": "ifndef VERBOSE\n.SILENT:\nendif\n\n# Never run this makefile in parallel, as it could screw things up\n# It won't affect the submakes, so you still get the speedup from specifying -jx\n.NOTPARALLEL:\n\n# Allow the silent with lower caps to work the same way as upper caps\nifdef silent\n    SILENT = $(silent)\nendif\n\nifdef SILENT\n    SUB_IS_SILENT := $(SILENT)\nendif\n\n# We need to make sure that silent is always turned off at the top level\n# Otherwise the [OK], [ERROR] and [WARN] messages won't be displayed correctly\noverride SILENT := false\n\nifeq ($(shell git rev-parse --is-inside-work-tree 2>/dev/null),)\n    export SKIP_GIT := yes\n    export NOT_REPO := yes\nendif\n\nifdef SKIP_VERSION\n    export SKIP_GIT := yes\nendif\n\nifndef SUB_IS_SILENT\nifndef SKIP_GIT\n    QMK_VERSION := $(shell git describe --abbrev=0 --tags 2>/dev/null)\nendif\n\nifneq ($(QMK_VERSION),)\n$(info QMK Firmware $(QMK_VERSION))\nendif\nendif\n\n# Try to determine userspace from qmk config, if set.\nifeq ($(QMK_USERSPACE),)\n    QMK_USERSPACE = $(shell qmk config -ro user.overlay_dir | cut -d= -f2 | sed -e 's@^None$$@@g')\nendif\n\n# Determine which qmk cli to use\nQMK_BIN := qmk\n\n# avoid 'Entering|Leaving directory' messages\nMAKEFLAGS += --no-print-directory\n\nON_ERROR := error_occurred=1\n\nBREAK_ON_ERRORS = no\n\nROOT_DIR := $(dir $(lastword $(MAKEFILE_LIST)))\nifeq ($(ROOT_DIR),)\n    ROOT_DIR := .\nendif\n\ninclude paths.mk\ninclude $(BUILDDEFS_PATH)/support.mk\n\nTEST_OUTPUT_DIR := $(BUILD_DIR)/test\nERROR_FILE := $(BUILD_DIR)/error_occurred\n\n.DEFAULT_GOAL := all:all\n\n\n# Compare the start of the RULE variable with the first argument($1)\n# If the rules equals $1 or starts with $1:, RULE_FOUND is set to true\n#     and $1 is removed from the RULE variable\n# Otherwise the RULE_FOUND variable is set to false, and RULE left as it was\n# The function is a bit tricky, since there's no built in $(startswith) function\ndefine COMPARE_AND_REMOVE_FROM_RULE_HELPER\n    ifeq ($1,$$(RULE))\n        RULE:=\n        RULE_FOUND := true\n    else\n        STARTCOLON_REMOVED=$$(subst START$1:,,START$$(RULE))\n        ifneq ($$(STARTCOLON_REMOVED),START$$(RULE))\n            RULE_FOUND := true\n            RULE := $$(STARTCOLON_REMOVED)\n        else\n            RULE_FOUND := false\n        endif\n    endif\nendef\n\n# This makes it easier to call COMPARE_AND_REMOVE_FROM_RULE, since it makes it behave like\n# a function that returns the value\nCOMPARE_AND_REMOVE_FROM_RULE = $(eval $(call COMPARE_AND_REMOVE_FROM_RULE_HELPER,$1))$(RULE_FOUND)\n\n# Try to find a match for the start of the rule to be checked\n# $1 The list to be checked\n# If a match is found, then RULE_FOUND is set to true\n# and MATCHED_ITEM to the item that was matched\ndefine TRY_TO_MATCH_RULE_FROM_LIST_HELPER\n    # Split on \":\", padding with empty strings to avoid indexing issues\n    TOKEN1:=$$(shell python3 -c \"import sys; print((sys.argv[1].split(':',1)+[''])[0])\" $$(RULE))\n    TOKENr:=$$(shell python3 -c \"import sys; print((sys.argv[1].split(':',1)+[''])[1])\" $$(RULE))\n\n    FOUNDx:=$$(shell echo $1 | tr \" \" \"\\n\" | grep -Fx $$(TOKEN1))\n    ifneq ($$(FOUNDx),)\n        RULE := $$(TOKENr)\n        RULE_FOUND := true\n        MATCHED_ITEM := $$(TOKEN1)\n    else\n        RULE_FOUND := false\n        MATCHED_ITEM :=\n    endif\nendef\n\n# Make it easier to call TRY_TO_MATCH_RULE_FROM_LIST\nTRY_TO_MATCH_RULE_FROM_LIST = $(eval $(call TRY_TO_MATCH_RULE_FROM_LIST_HELPER,$1))$(RULE_FOUND)\n\n# As TRY_TO_MATCH_RULE_FROM_LIST_HELPER, but with additional\n# resolution of DEFAULT_FOLDER and keyboard_aliases.hjson for provided rule \ndefine TRY_TO_MATCH_RULE_FROM_LIST_HELPER_KB\n    # Split on \":\", padding with empty strings to avoid indexing issues\n    TOKEN1:=$$(shell python3 -c \"import sys; print((sys.argv[1].split(':',1)+[''])[0])\" $$(RULE))\n    TOKENr:=$$(shell python3 -c \"import sys; print((sys.argv[1].split(':',1)+[''])[1])\" $$(RULE))\n\n    TOKEN1:=$$(shell $(QMK_BIN) resolve-alias --allow-unknown $$(TOKEN1))\n\n    FOUNDx:=$$(shell echo $1 | tr \" \" \"\\n\" | grep -Fx $$(TOKEN1))\n    ifneq ($$(FOUNDx),)\n        RULE := $$(TOKENr)\n        RULE_FOUND := true\n        MATCHED_ITEM := $$(TOKEN1)\n    else\n        RULE_FOUND := false\n        MATCHED_ITEM :=\n    endif\nendef\n\n# Make it easier to call TRY_TO_MATCH_RULE_FROM_LIST_KB\nTRY_TO_MATCH_RULE_FROM_LIST_KB = $(eval $(call TRY_TO_MATCH_RULE_FROM_LIST_HELPER_KB,$1))$(RULE_FOUND)\n\ndefine ALL_IN_LIST_LOOP\n    OLD_RULE$1 := $$(RULE)\n    $$(eval $$(call $1,$$(ITEM$1)))\n    RULE := $$(OLD_RULE$1)\nendef\n\ndefine PARSE_ALL_IN_LIST\n    $$(foreach ITEM$1,$2,$$(eval $$(call ALL_IN_LIST_LOOP,$1)))\nendef\n\n# The entry point for rule parsing\n# parses a rule in the format <keyboard>:<keymap>:<target>\n# but this particular function only deals with the first <keyboard> part\ndefine PARSE_RULE\n    RULE := $1\n    COMMANDS :=\n    # If the rule starts with all, then continue the parsing from\n    # PARSE_ALL_KEYBOARDS\n    ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all),true)\n        KEYBOARD_RULE=all\n        $$(eval $$(call PARSE_ALL_KEYBOARDS))\n    else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,test),true)\n        $$(eval $$(call PARSE_TEST))\n    # If the rule starts with the name of a known keyboard, then continue\n    # the parsing from PARSE_KEYBOARD\n    else ifeq ($$(call TRY_TO_MATCH_RULE_FROM_LIST_KB,$$(shell $(QMK_BIN) list-keyboards)),true)\n        KEYBOARD_RULE=$$(MATCHED_ITEM)\n        $$(eval $$(call PARSE_KEYBOARD,$$(MATCHED_ITEM)))\n    else\n        $$(info make: *** No rule to make target '$1'. Stop.)\n        $$(info |)\n        $$(info | QMK's make format is:)\n        $$(info |     make keyboard_folder:keymap_folder[:target])\n        $$(info |)\n        $$(info | Where `keyboard_folder` is the path to the keyboard relative to)\n        $$(info | `qmk_firmware/keyboards/`, and `keymap_folder` is the name of the)\n        $$(info | keymap folder under that board's `keymaps/` directory.)\n        $$(info |)\n        $$(info | Examples:)\n        $$(info |     keyboards/dz60, keyboards/dz60/keymaps/default)\n        $$(info |       -> make dz60:default)\n        $$(info |       -> qmk compile -kb dz60 -km default)\n        $$(info |     keyboards/planck/rev6, keyboards/planck/keymaps/default)\n        $$(info |       -> make planck/rev6:default:flash)\n        $$(info |       -> qmk flash -kb planck/rev6 -km default)\n        $$(info |)\n    endif\nendef\n\n# $1 = Keyboard\n# Parses a rule in the format <keymap>:<target>\n# the keyboard is already known when entering this function\ndefine PARSE_KEYBOARD\n    # If we want to compile the default subproject, then we need to\n    # include the correct makefile to determine the actual name of it\n    CURRENT_KB := $1\n\n    # 5/4/3/2/1\n    KEYBOARD_FOLDER_PATH_1 := $$(CURRENT_KB)\n    KEYBOARD_FOLDER_PATH_2 := $$(patsubst %/,%,$$(dir $$(KEYBOARD_FOLDER_PATH_1)))\n    KEYBOARD_FOLDER_PATH_3 := $$(patsubst %/,%,$$(dir $$(KEYBOARD_FOLDER_PATH_2)))\n    KEYBOARD_FOLDER_PATH_4 := $$(patsubst %/,%,$$(dir $$(KEYBOARD_FOLDER_PATH_3)))\n    KEYBOARD_FOLDER_PATH_5 := $$(patsubst %/,%,$$(dir $$(KEYBOARD_FOLDER_PATH_4)))\n\n    KEYMAPS :=\n    # get a list of all keymaps\n    KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(KEYBOARD_FOLDER_PATH_1)/keymaps/*/.)))\n    KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(KEYBOARD_FOLDER_PATH_2)/keymaps/*/.)))\n    KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(KEYBOARD_FOLDER_PATH_3)/keymaps/*/.)))\n    KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(KEYBOARD_FOLDER_PATH_4)/keymaps/*/.)))\n    KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/keyboards/$$(KEYBOARD_FOLDER_PATH_5)/keymaps/*/.)))\n\n    ifneq ($(QMK_USERSPACE),)\n        KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/keyboards/$$(KEYBOARD_FOLDER_PATH_1)/keymaps/*/.)))\n        KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/keyboards/$$(KEYBOARD_FOLDER_PATH_2)/keymaps/*/.)))\n        KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/keyboards/$$(KEYBOARD_FOLDER_PATH_3)/keymaps/*/.)))\n        KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/keyboards/$$(KEYBOARD_FOLDER_PATH_4)/keymaps/*/.)))\n        KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/keyboards/$$(KEYBOARD_FOLDER_PATH_5)/keymaps/*/.)))\n    endif\n\n    KEYBOARD_LAYOUTS := $(shell $(QMK_BIN) list-layouts --keyboard $1)\n    LAYOUT_KEYMAPS :=\n    $$(foreach LAYOUT,$$(KEYBOARD_LAYOUTS),$$(eval LAYOUT_KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(ROOT_DIR)/layouts/*/$$(LAYOUT)/*/.)))))\n    ifneq ($(QMK_USERSPACE),)\n        $$(foreach LAYOUT,$$(KEYBOARD_LAYOUTS),$$(eval LAYOUT_KEYMAPS += $$(notdir $$(patsubst %/.,%,$$(wildcard $(QMK_USERSPACE)/layouts/$$(LAYOUT)/*/.)))))\n    endif\n\n    KEYMAPS := $$(sort $$(KEYMAPS) $$(LAYOUT_KEYMAPS))\n\n    # if the rule after removing the start of it is empty (we haven't specified a kemap or target)\n    # compile all the keymaps\n    ifeq ($$(RULE),)\n        $$(eval $$(call PARSE_ALL_KEYMAPS))\n    # The same if all was specified\n    else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,all),true)\n        $$(eval $$(call PARSE_ALL_KEYMAPS))\n    # List all keymaps for the given keyboard\n    else ifeq ($$(call COMPARE_AND_REMOVE_FROM_RULE,list-keymaps),true)\n        $$(eval $$(call LIST_ALL_KEYMAPS))\n    # Try to match the specified keyamp with the list of known keymaps\n    else ifeq ($$(call TRY_TO_MATCH_RULE_FROM_LIST,$$(KEYMAPS)),true)\n        $$(eval $$(call PARSE_KEYMAP,$$(MATCHED_ITEM)))\n    # Otherwise try to match the keymap from the current folder, or arguments to the make command\n    else ifneq ($$(KEYMAP),)\n        $$(eval $$(call PARSE_KEYMAP,$$(KEYMAP)))\n    # Otherwise if we are running make all:<user> just skip\n    else ifeq ($$(KEYBOARD_RULE),all)\n        # $$(info Skipping: No user keymap for $$(CURRENT_KB))\n    # Otherwise, make all keymaps, again this is consistent with how it works without\n    # any arguments\n    else\n        $$(eval $$(call PARSE_ALL_KEYMAPS))\n    endif\nendef\n\n# if we are going to compile all keyboards, match the rest of the rule\n# for each of them\ndefine PARSE_ALL_KEYBOARDS\n    $$(eval $$(call PARSE_ALL_IN_LIST,PARSE_KEYBOARD,$(shell $(QMK_BIN) list-keyboards --no-resolve-defaults)))\nendef\n\n# Prints a list of all known keymaps for the given keyboard\ndefine LIST_ALL_KEYMAPS\n    COMMAND_true_LIST_KEYMAPS := \\\n        printf \"$$(KEYMAPS)\\n\";\n    COMMAND_false_LIST_KEYMAPS := \\\n        printf \"$$(MSG_AVAILABLE_KEYMAPS)\\n\"; \\\n        printf \"$$(KEYMAPS)\\n\";\n    COMMANDS += LIST_KEYMAPS\nendef\n\n# $1 Keymap\n# This is the meat of compiling a keyboard, when entering this, everything is known\n# keyboard, subproject, and keymap\n# Note that we are not directly calling the command here, but instead building a list,\n# which will later be processed\ndefine PARSE_KEYMAP\n    CURRENT_KM = $1\n    # The rest of the rule is the target\n    # Remove the leading \":\" from the target, as it acts as a separator\n    MAKE_TARGET := $$(patsubst :%,%,$$(RULE))\n    # We need to generate an unique identifier to append to the COMMANDS list\n    CURRENT_KB_UNDER := $$(subst /,_,$$(CURRENT_KB))\n    COMMAND := COMMAND_KEYBOARD_$$(CURRENT_KB_UNDER)_KEYMAP_$$(CURRENT_KM)\n    # If we are compiling a keyboard without a subproject, we want to display just the name\n    # of the keyboard, otherwise keyboard/subproject\n    KB_SP := $$(CURRENT_KB)\n    # Format it in bold\n    KB_SP := $(BOLD)$$(KB_SP)$(NO_COLOR)\n    # Specify the variables that we are passing forward to submake\n    MAKE_VARS := KEYBOARD=$$(CURRENT_KB) KEYMAP=$$(CURRENT_KM) QMK_BIN=$$(QMK_BIN)\n    # And the first part of the make command\n    MAKE_CMD := $$(MAKE) -r -R -C $(ROOT_DIR) -f $(BUILDDEFS_PATH)/build_keyboard.mk $$(MAKE_TARGET)\n    # The message to display\n    MAKE_MSG := $$(MSG_MAKE_KB)\n    # We run the command differently, depending on if we want more output or not\n    # The true version for silent output and the false version otherwise\n    $$(eval $$(call BUILD))\nendef\n\ndefine BUILD\n    MAKE_VARS += VERBOSE=$(VERBOSE) COLOR=$(COLOR)\n    COMMANDS += $$(COMMAND)\n    COMMAND_true_$$(COMMAND) := \\\n        printf \"$$(MAKE_MSG)\" | \\\n        $$(MAKE_MSG_FORMAT); \\\n        LOG=$$$$($$(MAKE_CMD) $$(MAKE_VARS) SILENT=true 2>&1) ; \\\n        if [ $$$$? -gt 0 ]; \\\n            then $$(PRINT_ERROR_PLAIN); \\\n        elif [ \"$$$$LOG\" = \"skipped\" ] ; \\\n            then $$(PRINT_SKIPPED_PLAIN); \\\n        elif [ \"$$$$LOG\" != \"\" ] ; \\\n            then $$(PRINT_WARNING_PLAIN); \\\n        else \\\n            $$(PRINT_OK); \\\n        fi;\n    COMMAND_false_$$(COMMAND) := \\\n        printf \"$$(MAKE_MSG)\\n\\n\"; \\\n        $$(MAKE_CMD) $$(MAKE_VARS) SILENT=false; \\\n        if [ $$$$? -gt 0 ]; \\\n            then error_occurred=1; \\\n        fi;\nendef\n\n# Just parse all the keymaps for a specific keyboard\ndefine PARSE_ALL_KEYMAPS\n    $$(eval $$(call PARSE_ALL_IN_LIST,PARSE_KEYMAP,$$(KEYMAPS)))\nendef\n\ndefine BUILD_TEST\n    TEST_PATH := $1\n    TEST_NAME := $$(notdir $$(TEST_PATH))\n    TEST_FULL_NAME := $$(subst /,_,$$(patsubst $$(ROOT_DIR)tests/%,%,$$(TEST_PATH)))\n    MAKE_TARGET := $2\n    COMMAND := $1\n    MAKE_CMD := $$(MAKE) -r -R -C $(ROOT_DIR) -f $(BUILDDEFS_PATH)/build_test.mk $$(MAKE_TARGET)\n    MAKE_VARS := TEST=$$(TEST_NAME) TEST_OUTPUT=$$(TEST_FULL_NAME) TEST_PATH=$$(TEST_PATH) FULL_TESTS=\"$$(FULL_TESTS)\"\n    MAKE_MSG := $$(MSG_MAKE_TEST)\n    $$(eval $$(call BUILD))\n    ifneq ($$(MAKE_TARGET),clean)\n        TEST_EXECUTABLE := $$(TEST_OUTPUT_DIR)/$$(TEST_FULL_NAME).elf\n        TESTS += $$(TEST_FULL_NAME)\n        TEST_MSG := $$(MSG_TEST)\n        $$(TEST_FULL_NAME)_COMMAND := \\\n            printf \"$$(TEST_MSG)\\n\"; \\\n            $$(TEST_EXECUTABLE); \\\n            if [ $$$$? -gt 0 ]; \\\n                then error_occurred=1; \\\n            fi; \\\n            printf \"\\n\";\n    endif\nendef\n\ndefine LIST_TEST\n    include $(BUILDDEFS_PATH)/testlist.mk\n    FOUND_TESTS := $$(patsubst ./tests/%,%,$$(TEST_LIST))\n    $$(info $$(FOUND_TESTS))\nendef\n\ndefine PARSE_TEST\n    TESTS :=\n    TEST_NAME := $$(firstword $$(subst :, ,$$(RULE)))\n    TEST_TARGET := $$(subst $$(TEST_NAME),,$$(subst $$(TEST_NAME):,,$$(RULE)))\n    include $(BUILDDEFS_PATH)/testlist.mk\n    ifeq ($$(TEST_NAME),all)\n        MATCHED_TESTS := $$(TEST_LIST)\n    else\n        MATCHED_TESTS := $$(foreach TEST, $$(TEST_LIST),$$(if $$(findstring x$$(TEST_NAME)x, x$$(patsubst ./tests/%,%,$$(TEST)x)), $$(TEST),))\n    endif\n    $$(foreach TEST,$$(MATCHED_TESTS),$$(eval $$(call BUILD_TEST,$$(TEST),$$(TEST_TARGET))))\nendef\n\n\n# Set the silent mode depending on if we are trying to compile multiple keyboards or not\n# By default it's on in that case, but it can be overridden by specifying silent=false\n# from the command line\ndefine SET_SILENT_MODE\n    ifdef SUB_IS_SILENT\n        SILENT_MODE := $(SUB_IS_SILENT)\n    else ifeq ($$(words $$(COMMANDS)),1)\n        SILENT_MODE := false\n    else\n        SILENT_MODE := true\n    endif\nendef\n\ninclude $(BUILDDEFS_PATH)/message.mk\n\nifeq ($(strip $(BREAK_ON_ERRORS)), yes)\nHANDLE_ERROR = exit 1\nelse\nHANDLE_ERROR = echo $$error_occurred > $(ERROR_FILE)\nendif\n\n# The empty line is important here, as it will force a new shell to be created for each command\n# Otherwise the command line will become too long with a lot of keyboards and keymaps\ndefine RUN_COMMAND\n+error_occurred=0;\\\n$(COMMAND_$(SILENT_MODE)_$(COMMAND))\\\nif [ $$error_occurred -gt 0 ]; then $(HANDLE_ERROR); fi;\n\n\nendef\ndefine RUN_TEST\n+error_occurred=0;\\\n$($(TEST)_COMMAND)\\\nif [ $$error_occurred -gt 0 ]; then $(HANDLE_ERROR); fi;\n\n\nendef\n\n# Catch everything and parse the command line ourselves.\n.PHONY: %\n%:\n\t# Ensure that $(QMK_BIN) works.\n\tif ! $(QMK_BIN) hello 1> /dev/null 2>&1; then printf \"$(MSG_PYTHON_MISSING)\"; exit 1; fi\nifdef NOT_REPO\n\tprintf \"$(MSG_NOT_REPO)\"\nendif\nifndef SKIP_GIT\n\t$(QMK_BIN) git-submodule --sync\n\t# Check if the submodules are dirty, and display a warning if they are\n\tif ! $(QMK_BIN) git-submodule --check 1> /dev/null 2>&1; then printf \"$(MSG_SUBMODULE_DIRTY)\"; fi\nendif\n\trm -f $(ERROR_FILE) > /dev/null 2>&1\n\t$(eval $(call PARSE_RULE,$@))\n\t$(eval $(call SET_SILENT_MODE))\n\t# Run all the commands in the same shell, notice the + at the first line\n\t# it has to be there to allow parallel execution of the submake\n\t# This always tries to compile everything, even if error occurs in the middle\n\t# But we return the error code at the end, to trigger travis failures\n\t# The sort at this point is to remove duplicates\n\t$(foreach COMMAND,$(sort $(COMMANDS)),$(RUN_COMMAND))\n\tif [ -f $(ERROR_FILE) ]; then printf \"$(MSG_ERRORS)\" & exit 1; fi;\n\t$(foreach TEST,$(sort $(TESTS)),$(RUN_TEST))\n\tif [ -f $(ERROR_FILE) ]; then printf \"$(MSG_ERRORS)\" & exit 1; fi;\n\nlib/%:\n\tgit submodule sync $?\n\tgit submodule update --init $?\n\n.PHONY: git-submodule\ngit-submodule:\n\t$(QMK_BIN) git-submodule\n\n.PHONY: git-submodules\ngit-submodules: git-submodule\n\n.PHONY: list-keyboards\nlist-keyboards:\n\t$(QMK_BIN) list-keyboards --no-resolve-defaults | tr '\\n' ' '\n\n.PHONY: list-tests\nlist-tests:\n\t$(eval $(call LIST_TEST))\n\n.PHONY: generate-keyboards-file\ngenerate-keyboards-file:\n\t$(QMK_BIN) list-keyboards --no-resolve-defaults\n\n.PHONY: clean\nclean:\n\techo -n 'Deleting .build/ ... '\n\trm -rf $(BUILD_DIR)\n\techo 'done.'\n\n.PHONY: distclean distclean_qmk\ndistclean: distclean_qmk\ndistclean_qmk: clean\n\techo -n 'Deleting *.bin, *.hex, and *.uf2 ... '\n\trm -f *.bin *.hex *.uf2\n\techo 'done.'\n\nifneq ($(QMK_USERSPACE),)\n.PHONY: distclean_userspace\ndistclean: distclean_userspace\ndistclean_userspace: clean\n\techo -n 'Deleting userspace *.bin, *.hex, and *.uf2 ... '\n\trm -f $(QMK_USERSPACE)/*.bin $(QMK_USERSPACE)/*.hex $(QMK_USERSPACE)/*.uf2\n\techo 'done.'\nendif\n\n# Extra targets for formatting and/or pytest, running within the qmk/qmk_cli container to match GHA.\nCONTAINER_PREAMBLE := export HOME=\"/tmp\"; export PATH=\"/tmp/.local/bin:\\$$PATH\"; python3 -m pip install --upgrade pip; python3 -m pip install -r requirements-dev.txt\n\n.PHONY: format-core\nformat-core:\n\tRUNTIME=docker ./util/docker_cmd.sh bash -lic \"$(CONTAINER_PREAMBLE); qmk format-c --core-only -a && qmk format-python -a\"\n\n.PHONY: pytest\npytest:\n\tRUNTIME=docker ./util/docker_cmd.sh bash -lic \"$(CONTAINER_PREAMBLE); qmk pytest\"\n\n.PHONY: format-and-pytest\nformat-and-pytest:\n\tRUNTIME=docker ./util/docker_cmd.sh bash -lic \"$(CONTAINER_PREAMBLE); qmk format-c --core-only -a && qmk format-python -a && qmk pytest\"\n"
  },
  {
    "path": "builddefs/build_full_test.mk",
    "content": "# Copyright 2017 Fred Sundvik\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n$(TEST_OUTPUT)_INC := \\\n\ttests/test_common/common_config.h\n\n$(TEST_OUTPUT)_SRC := \\\n\t$(QUANTUM_SRC) \\\n\t$(SRC) \\\n\t$(QUANTUM_PATH)/keymap_introspection.c \\\n\ttests/test_common/matrix.c \\\n\ttests/test_common/pointing_device_driver.c \\\n\ttests/test_common/test_driver.cpp \\\n\ttests/test_common/keyboard_report_util.cpp \\\n\ttests/test_common/mouse_report_util.cpp \\\n\ttests/test_common/test_fixture.cpp \\\n\ttests/test_common/test_keymap_key.cpp \\\n\ttests/test_common/test_logger.cpp \\\n\t$(patsubst $(ROOTDIR)/%,%,$(wildcard $(TEST_PATH)/*.cpp))\n\n$(TEST_OUTPUT)_DEFS := $(OPT_DEFS) \"-DKEYMAP_C=\\\"keymap.c\\\"\"\n\n$(TEST_OUTPUT)_CONFIG := $(TEST_PATH)/config.h\n\nVPATH += $(TOP_DIR)/tests/test_common\n"
  },
  {
    "path": "builddefs/build_json.mk",
    "content": "# Look for a json keymap file\nifneq (\"$(wildcard $(MAIN_KEYMAP_PATH_5)/keymap.json)\",\"\")\n    KEYMAP_JSON := $(MAIN_KEYMAP_PATH_5)/keymap.json\n    KEYMAP_JSON_PATH := $(MAIN_KEYMAP_PATH_5)\nelse ifneq (\"$(wildcard $(MAIN_KEYMAP_PATH_4)/keymap.json)\",\"\")\n    KEYMAP_JSON := $(MAIN_KEYMAP_PATH_4)/keymap.json\n    KEYMAP_JSON_PATH := $(MAIN_KEYMAP_PATH_4)\nelse ifneq (\"$(wildcard $(MAIN_KEYMAP_PATH_3)/keymap.json)\",\"\")\n    KEYMAP_JSON := $(MAIN_KEYMAP_PATH_3)/keymap.json\n    KEYMAP_JSON_PATH := $(MAIN_KEYMAP_PATH_3)\nelse ifneq (\"$(wildcard $(MAIN_KEYMAP_PATH_2)/keymap.json)\",\"\")\n    KEYMAP_JSON := $(MAIN_KEYMAP_PATH_2)/keymap.json\n    KEYMAP_JSON_PATH := $(MAIN_KEYMAP_PATH_2)\nelse ifneq (\"$(wildcard $(MAIN_KEYMAP_PATH_1)/keymap.json)\",\"\")\n    KEYMAP_JSON := $(MAIN_KEYMAP_PATH_1)/keymap.json\n    KEYMAP_JSON_PATH := $(MAIN_KEYMAP_PATH_1)\nendif\n\nifneq ($(QMK_USERSPACE),)\n    ifneq (\"$(wildcard $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_5)/keymap.json)\",\"\")\n        KEYMAP_JSON := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_5)/keymap.json\n        KEYMAP_PATH := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_5)\n    else ifneq (\"$(wildcard $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_4)/keymap.json)\",\"\")\n        KEYMAP_JSON := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_4)/keymap.json\n        KEYMAP_PATH := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_4)\n    else ifneq (\"$(wildcard $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_3)/keymap.json)\",\"\")\n        KEYMAP_JSON := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_3)/keymap.json\n        KEYMAP_PATH := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_3)\n    else ifneq (\"$(wildcard $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_2)/keymap.json)\",\"\")\n        KEYMAP_JSON := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_2)/keymap.json\n        KEYMAP_PATH := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_2)\n    else ifneq (\"$(wildcard $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_1)/keymap.json)\",\"\")\n        KEYMAP_JSON := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_1)/keymap.json\n        KEYMAP_PATH := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_1)\n    endif\nendif\n"
  },
  {
    "path": "builddefs/build_keyboard.mk",
    "content": "# Determine what keyboard we are building and setup the build environment.\n#\n# We support folders up to 5 levels deep below `keyboards/`. This file is\n# responsible for determining which folder is being used and doing the\n# corresponding environment setup.\n\nifndef VERBOSE\n.SILENT:\nendif\n\n.DEFAULT_GOAL := all\n\ninclude paths.mk\ninclude $(BUILDDEFS_PATH)/support.mk\ninclude $(BUILDDEFS_PATH)/message.mk\n\n# Helper to add defines with a 'QMK_' prefix\ndefine add_qmk_prefix_defs\n    ifdef $1\n        # Need to cater for 'STM32L4xx+'\n        OPT_DEFS += -DQMK_$(2)=\"$($1)\" -DQMK_$(2)_$(shell echo $($1) | sed -e 's@+@Plus@g' -e 's@[^a-zA-Z0-9]@_@g' | tr '[:lower:]' '[:upper:]')\n    endif\nendef\n\n# Set the qmk cli to use\nQMK_BIN ?= qmk\n\n# Set the filename for the final firmware binary\nKEYBOARD_FILESAFE := $(subst /,_,$(KEYBOARD))\nTARGET ?= $(KEYBOARD_FILESAFE)_$(KEYMAP)\n\nifeq ($(strip $(DUMP_CI_METADATA)),yes)\n    $(info CI Metadata: KEYBOARD=$(KEYBOARD))\n    $(info CI Metadata: KEYMAP=$(KEYMAP))\nendif\n\n# Force expansion\noverride TARGET := $(TARGET)\n\nifneq ($(FORCE_LAYOUT),)\n    override TARGET := $(TARGET)_$(FORCE_LAYOUT)\nendif\nifneq ($(CONVERT_TO),)\n    override TARGET := $(TARGET)_$(CONVERT_TO)\nendif\n\n# Object files and generated keymap directory\n#     To put object files in current directory, use a dot (.), do NOT make\n#     this an empty or blank macro!\nINTERMEDIATE_OUTPUT := $(BUILD_DIR)/obj_$(TARGET)\n\nifdef SKIP_VERSION\n    OPT_DEFS += -DSKIP_VERSION\nendif\n\n# Generate the version.h file\nVERSION_H_FLAGS :=\nifdef SKIP_VERSION\nVERSION_H_FLAGS += --skip-all\nendif\nifdef SKIP_GIT\nVERSION_H_FLAGS += --skip-git\nendif\n\n# Determine which subfolders exist.\nKEYBOARD_FOLDER_PATH_1 := $(KEYBOARD)\nKEYBOARD_FOLDER_PATH_2 := $(patsubst %/,%,$(dir $(KEYBOARD_FOLDER_PATH_1)))\nKEYBOARD_FOLDER_PATH_3 := $(patsubst %/,%,$(dir $(KEYBOARD_FOLDER_PATH_2)))\nKEYBOARD_FOLDER_PATH_4 := $(patsubst %/,%,$(dir $(KEYBOARD_FOLDER_PATH_3)))\nKEYBOARD_FOLDER_PATH_5 := $(patsubst %/,%,$(dir $(KEYBOARD_FOLDER_PATH_4)))\nKEYBOARD_FOLDER_1 := $(notdir $(KEYBOARD_FOLDER_PATH_1))\nKEYBOARD_FOLDER_2 := $(notdir $(KEYBOARD_FOLDER_PATH_2))\nKEYBOARD_FOLDER_3 := $(notdir $(KEYBOARD_FOLDER_PATH_3))\nKEYBOARD_FOLDER_4 := $(notdir $(KEYBOARD_FOLDER_PATH_4))\nKEYBOARD_FOLDER_5 := $(notdir $(KEYBOARD_FOLDER_PATH_5))\nKEYBOARD_PATHS :=\nKEYBOARD_PATH_1 := keyboards/$(KEYBOARD_FOLDER_PATH_1)\nKEYBOARD_PATH_2 := keyboards/$(KEYBOARD_FOLDER_PATH_2)\nKEYBOARD_PATH_3 := keyboards/$(KEYBOARD_FOLDER_PATH_3)\nKEYBOARD_PATH_4 := keyboards/$(KEYBOARD_FOLDER_PATH_4)\nKEYBOARD_PATH_5 := keyboards/$(KEYBOARD_FOLDER_PATH_5)\n\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/)\",\"\")\n    KEYBOARD_PATHS += $(KEYBOARD_PATH_5)\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_4)/)\",\"\")\n    KEYBOARD_PATHS += $(KEYBOARD_PATH_4)\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_3)/)\",\"\")\n    KEYBOARD_PATHS += $(KEYBOARD_PATH_3)\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_2)/)\",\"\")\n    KEYBOARD_PATHS += $(KEYBOARD_PATH_2)\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_1)/)\",\"\")\n    KEYBOARD_PATHS += $(KEYBOARD_PATH_1)\nendif\n\n\n# Pull in rules.mk files from all our subfolders\n-include $(KEYBOARD_PATH_5)/rules.mk\n-include $(KEYBOARD_PATH_4)/rules.mk\n-include $(KEYBOARD_PATH_3)/rules.mk\n-include $(KEYBOARD_PATH_2)/rules.mk\n-include $(KEYBOARD_PATH_1)/rules.mk\n\n# Create dependencies on DD keyboard config - structure validated elsewhere\nDD_CONFIG_FILES :=\nifneq (\"$(wildcard $(KEYBOARD_PATH_1)/info.json)\",\"\")\n    DD_CONFIG_FILES += $(KEYBOARD_PATH_1)/info.json\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_2)/info.json)\",\"\")\n    DD_CONFIG_FILES += $(KEYBOARD_PATH_2)/info.json\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_3)/info.json)\",\"\")\n    DD_CONFIG_FILES += $(KEYBOARD_PATH_3)/info.json\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_4)/info.json)\",\"\")\n    DD_CONFIG_FILES += $(KEYBOARD_PATH_4)/info.json\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/info.json)\",\"\")\n    DD_CONFIG_FILES += $(KEYBOARD_PATH_5)/info.json\nendif\n\nifneq (\"$(wildcard $(KEYBOARD_PATH_1)/keyboard.json)\",\"\")\n    DD_CONFIG_FILES += $(KEYBOARD_PATH_1)/keyboard.json\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_2)/keyboard.json)\",\"\")\n    DD_CONFIG_FILES += $(KEYBOARD_PATH_2)/keyboard.json\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_3)/keyboard.json)\",\"\")\n    DD_CONFIG_FILES += $(KEYBOARD_PATH_3)/keyboard.json\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_4)/keyboard.json)\",\"\")\n    DD_CONFIG_FILES += $(KEYBOARD_PATH_4)/keyboard.json\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/keyboard.json)\",\"\")\n    DD_CONFIG_FILES += $(KEYBOARD_PATH_5)/keyboard.json\nendif\n\nMAIN_KEYMAP_PATH_1 := $(KEYBOARD_PATH_1)/keymaps/$(KEYMAP)\nMAIN_KEYMAP_PATH_2 := $(KEYBOARD_PATH_2)/keymaps/$(KEYMAP)\nMAIN_KEYMAP_PATH_3 := $(KEYBOARD_PATH_3)/keymaps/$(KEYMAP)\nMAIN_KEYMAP_PATH_4 := $(KEYBOARD_PATH_4)/keymaps/$(KEYMAP)\nMAIN_KEYMAP_PATH_5 := $(KEYBOARD_PATH_5)/keymaps/$(KEYMAP)\n\n# Pull in rules from DD keyboard config\nINFO_RULES_MK = $(shell $(QMK_BIN) generate-rules-mk --quiet --escape --keyboard $(KEYBOARD) --output $(INTERMEDIATE_OUTPUT)/src/info_rules.mk)\ninclude $(INFO_RULES_MK)\n\n# Check for keymap.json first, so we can regenerate keymap.c\ninclude $(BUILDDEFS_PATH)/build_json.mk\n\n# Pull in keymap level rules.mk\nifeq (\"$(wildcard $(KEYMAP_PATH))\", \"\")\n    # Look through the possible keymap folders until we find a matching keymap.c\n    ifneq ($(QMK_USERSPACE),)\n        ifneq (\"$(wildcard $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_1)/keymap.c)\",\"\")\n            -include $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_1)/rules.mk\n            KEYMAP_C := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_1)/keymap.c\n            KEYMAP_PATH := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_1)\n        else ifneq (\"$(wildcard $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_2)/keymap.c)\",\"\")\n            -include $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_2)/rules.mk\n            KEYMAP_C := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_2)/keymap.c\n            KEYMAP_PATH := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_2)\n        else ifneq (\"$(wildcard $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_3)/keymap.c)\",\"\")\n            -include $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_3)/rules.mk\n            KEYMAP_C := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_3)/keymap.c\n            KEYMAP_PATH := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_3)\n        else ifneq (\"$(wildcard $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_4)/keymap.c)\",\"\")\n            -include $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_4)/rules.mk\n            KEYMAP_C := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_4)/keymap.c\n            KEYMAP_PATH := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_4)\n        else ifneq (\"$(wildcard $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_5)/keymap.c)\",\"\")\n            -include $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_5)/rules.mk\n            KEYMAP_C := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_5)/keymap.c\n            KEYMAP_PATH := $(QMK_USERSPACE)/$(MAIN_KEYMAP_PATH_5)\n        endif\n    endif\n    ifeq ($(KEYMAP_PATH),)\n        ifneq (\"$(wildcard $(MAIN_KEYMAP_PATH_1)/keymap.c)\",\"\")\n            -include $(MAIN_KEYMAP_PATH_1)/rules.mk\n            KEYMAP_C := $(MAIN_KEYMAP_PATH_1)/keymap.c\n            KEYMAP_PATH := $(MAIN_KEYMAP_PATH_1)\n        else ifneq (\"$(wildcard $(MAIN_KEYMAP_PATH_2)/keymap.c)\",\"\")\n            -include $(MAIN_KEYMAP_PATH_2)/rules.mk\n            KEYMAP_C := $(MAIN_KEYMAP_PATH_2)/keymap.c\n            KEYMAP_PATH := $(MAIN_KEYMAP_PATH_2)\n        else ifneq (\"$(wildcard $(MAIN_KEYMAP_PATH_3)/keymap.c)\",\"\")\n            -include $(MAIN_KEYMAP_PATH_3)/rules.mk\n            KEYMAP_C := $(MAIN_KEYMAP_PATH_3)/keymap.c\n            KEYMAP_PATH := $(MAIN_KEYMAP_PATH_3)\n        else ifneq (\"$(wildcard $(MAIN_KEYMAP_PATH_4)/keymap.c)\",\"\")\n            -include $(MAIN_KEYMAP_PATH_4)/rules.mk\n            KEYMAP_C := $(MAIN_KEYMAP_PATH_4)/keymap.c\n            KEYMAP_PATH := $(MAIN_KEYMAP_PATH_4)\n        else ifneq (\"$(wildcard $(MAIN_KEYMAP_PATH_5)/keymap.c)\",\"\")\n            -include $(MAIN_KEYMAP_PATH_5)/rules.mk\n            KEYMAP_C := $(MAIN_KEYMAP_PATH_5)/keymap.c\n            KEYMAP_PATH := $(MAIN_KEYMAP_PATH_5)\n        else ifneq ($(LAYOUTS),)\n            # If we haven't found a keymap yet fall back to community layouts\n            include $(BUILDDEFS_PATH)/build_layout.mk\n        else ifeq (\"$(wildcard $(KEYMAP_JSON_PATH))\", \"\") # Not finding keymap.c is fine if we found a keymap.json\n            $(call CATASTROPHIC_ERROR,Invalid keymap,Could not find keymap)\n            # this state should never be reached\n        endif\n    endif\nendif\n\n# Have we found a keymap.json?\nifneq (\"$(wildcard $(KEYMAP_JSON))\", \"\")\n    ifneq (\"$(wildcard $(KEYMAP_C))\", \"\")\n        # Allow a separately-found keymap.c next to keymap.json -- the keymap.c\n        # generator will include the other keymap.c in the process, if supplied.\n        OTHER_KEYMAP_C := $(KEYMAP_C)\n        OPT_DEFS += -DOTHER_KEYMAP_C=\\\"$(OTHER_KEYMAP_C)\\\"\n    endif\n\n    KEYMAP_PATH := $(KEYMAP_JSON_PATH)\n\n    KEYMAP_C := $(INTERMEDIATE_OUTPUT)/src/keymap.c\n    KEYMAP_H := $(INTERMEDIATE_OUTPUT)/src/config.h\n\n    ifeq ($(OTHER_KEYMAP_C),)\n        # Load the keymap-level rules.mk if exists (and we havent already loaded it for keymap.c)\n        -include $(KEYMAP_PATH)/rules.mk\n    endif\n\n    # Load any rules.mk content from keymap.json\n    INFO_RULES_MK = $(shell $(QMK_BIN) generate-rules-mk --quiet --escape --output $(INTERMEDIATE_OUTPUT)/src/rules.mk $(KEYMAP_JSON))\n    include $(INFO_RULES_MK)\n\n# Add rules to generate the keymap files - indentation here is important\n$(INTERMEDIATE_OUTPUT)/src/keymap.c: $(KEYMAP_JSON) $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) json2c --quiet --output $(KEYMAP_C) $(KEYMAP_JSON))\n\t@$(BUILD_CMD)\n\n$(INTERMEDIATE_OUTPUT)/src/config.h: $(KEYMAP_JSON) $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-config-h --quiet --output $(KEYMAP_H) $(KEYMAP_JSON))\n\t@$(BUILD_CMD)\n\n$(INTERMEDIATE_OUTPUT)/src/keymap.h: $(KEYMAP_JSON) $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-keymap-h --quiet --output $(INTERMEDIATE_OUTPUT)/src/keymap.h $(KEYMAP_JSON))\n\t@$(BUILD_CMD)\n\ngenerated-files: $(INTERMEDIATE_OUTPUT)/src/config.h $(INTERMEDIATE_OUTPUT)/src/keymap.c $(INTERMEDIATE_OUTPUT)/src/keymap.h\n\nendif\n\n# Community modules\nCOMMUNITY_RULES_MK = $(shell $(QMK_BIN) generate-community-modules-rules-mk -kb $(KEYBOARD) --quiet --escape --output $(INTERMEDIATE_OUTPUT)/src/community_rules.mk $(KEYMAP_JSON))\ninclude $(COMMUNITY_RULES_MK)\n\n$(INTERMEDIATE_OUTPUT)/src/community_modules.h: $(KEYMAP_JSON) $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-community-modules-h -kb $(KEYBOARD) --quiet --output $(INTERMEDIATE_OUTPUT)/src/community_modules.h $(KEYMAP_JSON))\n\t@$(BUILD_CMD)\n\n$(INTERMEDIATE_OUTPUT)/src/community_modules.c: $(KEYMAP_JSON) $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-community-modules-c -kb $(KEYBOARD) --quiet --output $(INTERMEDIATE_OUTPUT)/src/community_modules.c $(KEYMAP_JSON))\n\t@$(BUILD_CMD)\n\n$(INTERMEDIATE_OUTPUT)/src/community_modules_introspection.c: $(KEYMAP_JSON) $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-community-modules-introspection-c -kb $(KEYBOARD) --quiet --output $(INTERMEDIATE_OUTPUT)/src/community_modules_introspection.c $(KEYMAP_JSON))\n\t@$(BUILD_CMD)\n\n$(INTERMEDIATE_OUTPUT)/src/community_modules_introspection.h: $(KEYMAP_JSON) $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-community-modules-introspection-h -kb $(KEYBOARD) --quiet --output $(INTERMEDIATE_OUTPUT)/src/community_modules_introspection.h $(KEYMAP_JSON))\n\t@$(BUILD_CMD)\n\n$(INTERMEDIATE_OUTPUT)/src/led_matrix_community_modules.inc: $(KEYMAP_JSON) $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-led-matrix-community-modules-inc -kb $(KEYBOARD) --quiet --output $(INTERMEDIATE_OUTPUT)/src/led_matrix_community_modules.inc $(KEYMAP_JSON))\n\t@$(BUILD_CMD)\n\n$(INTERMEDIATE_OUTPUT)/src/rgb_matrix_community_modules.inc: $(KEYMAP_JSON) $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-rgb-matrix-community-modules-inc -kb $(KEYBOARD) --quiet --output $(INTERMEDIATE_OUTPUT)/src/rgb_matrix_community_modules.inc $(KEYMAP_JSON))\n\t@$(BUILD_CMD)\n\nSRC += $(INTERMEDIATE_OUTPUT)/src/community_modules.c\n\ngenerated-files: $(INTERMEDIATE_OUTPUT)/src/community_modules.h $(INTERMEDIATE_OUTPUT)/src/community_modules.c $(INTERMEDIATE_OUTPUT)/src/community_modules_introspection.c $(INTERMEDIATE_OUTPUT)/src/community_modules_introspection.h $(INTERMEDIATE_OUTPUT)/src/led_matrix_community_modules.inc $(INTERMEDIATE_OUTPUT)/src/rgb_matrix_community_modules.inc\n\ninclude $(BUILDDEFS_PATH)/converters.mk\n\n# Generate the board's version.h file.\n$(shell $(QMK_BIN) generate-version-h $(VERSION_H_FLAGS) -q -o $(INTERMEDIATE_OUTPUT)/src/version.h)\n\nMCU_ORIG := $(MCU)\ninclude $(wildcard $(PLATFORM_PATH)/*/mcu_selection.mk)\n\n# PLATFORM_KEY should be detected in DD keyboard config via key 'processor' (or rules.mk 'MCU')\nifeq ($(PLATFORM_KEY),)\n    $(call CATASTROPHIC_ERROR,Platform not defined)\nendif\nPLATFORM=$(shell echo $(PLATFORM_KEY) | tr '[:lower:]' '[:upper:]')\n\n# Find all the C source files to be compiled in subfolders.\nKEYBOARD_SRC :=\n\nKEYBOARD_C_1 := $(KEYBOARD_PATH_1)/$(KEYBOARD_FOLDER_1).c\nKEYBOARD_C_2 := $(KEYBOARD_PATH_2)/$(KEYBOARD_FOLDER_2).c\nKEYBOARD_C_3 := $(KEYBOARD_PATH_3)/$(KEYBOARD_FOLDER_3).c\nKEYBOARD_C_4 := $(KEYBOARD_PATH_4)/$(KEYBOARD_FOLDER_4).c\nKEYBOARD_C_5 := $(KEYBOARD_PATH_5)/$(KEYBOARD_FOLDER_5).c\n\nifneq (\"$(wildcard $(KEYBOARD_C_5))\",\"\")\n    KEYBOARD_SRC += $(KEYBOARD_C_5)\nendif\nifneq (\"$(wildcard $(KEYBOARD_C_4))\",\"\")\n    KEYBOARD_SRC += $(KEYBOARD_C_4)\nendif\nifneq (\"$(wildcard $(KEYBOARD_C_3))\",\"\")\n    KEYBOARD_SRC += $(KEYBOARD_C_3)\nendif\nifneq (\"$(wildcard $(KEYBOARD_C_2))\",\"\")\n    KEYBOARD_SRC += $(KEYBOARD_C_2)\nendif\nifneq (\"$(wildcard $(KEYBOARD_C_1))\",\"\")\n    KEYBOARD_SRC += $(KEYBOARD_C_1)\nendif\n\n# Generate KEYBOARD_name_subname for all levels of the keyboard folder\nKEYBOARD_FILESAFE_1 := $(subst .,,$(subst /,_,$(KEYBOARD_FOLDER_PATH_1)))\nKEYBOARD_FILESAFE_2 := $(subst .,,$(subst /,_,$(KEYBOARD_FOLDER_PATH_2)))\nKEYBOARD_FILESAFE_3 := $(subst .,,$(subst /,_,$(KEYBOARD_FOLDER_PATH_3)))\nKEYBOARD_FILESAFE_4 := $(subst .,,$(subst /,_,$(KEYBOARD_FOLDER_PATH_4)))\nKEYBOARD_FILESAFE_5 := $(subst .,,$(subst /,_,$(KEYBOARD_FOLDER_PATH_5)))\n\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/)\",\"\")\n    OPT_DEFS += -DKEYBOARD_$(KEYBOARD_FILESAFE_5)\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_4)/)\",\"\")\n    OPT_DEFS += -DKEYBOARD_$(KEYBOARD_FILESAFE_4)\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_3)/)\",\"\")\n    OPT_DEFS += -DKEYBOARD_$(KEYBOARD_FILESAFE_3)\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_2)/)\",\"\")\n    OPT_DEFS += -DKEYBOARD_$(KEYBOARD_FILESAFE_2)\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_1)/)\",\"\")\n    OPT_DEFS += -DKEYBOARD_$(KEYBOARD_FILESAFE_1)\nendif\n\n# Setup the define for QMK_KEYBOARD_H. This is used inside of keymaps so\n# that the same keymap may be used on multiple keyboards.\n#\n# We grab the most top-level include file that we can. That file should\n# use #ifdef statements to include all the necessary subfolder includes,\n# as described here:\n#\n#    https://docs.qmk.fm/#/feature_layouts?id=tips-for-making-layouts-keyboard-agnostic\n#\nifneq (\"$(wildcard $(KEYBOARD_PATH_1)/$(KEYBOARD_FOLDER_1).h)\",\"\")\n    FOUND_KEYBOARD_H = $(KEYBOARD_FOLDER_1).h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_2)/$(KEYBOARD_FOLDER_2).h)\",\"\")\n    FOUND_KEYBOARD_H = $(KEYBOARD_FOLDER_2).h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_3)/$(KEYBOARD_FOLDER_3).h)\",\"\")\n    FOUND_KEYBOARD_H = $(KEYBOARD_FOLDER_3).h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_4)/$(KEYBOARD_FOLDER_4).h)\",\"\")\n    FOUND_KEYBOARD_H = $(KEYBOARD_FOLDER_4).h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/$(KEYBOARD_FOLDER_5).h)\",\"\")\n    FOUND_KEYBOARD_H = $(KEYBOARD_FOLDER_5).h\nendif\n\n# Find all of the config.h files and add them to our CONFIG_H define.\nCONFIG_H :=\n\ndefine config_h_community_module_appender\n\tifneq (\"$(wildcard $(1)/config.h)\",\"\")\n\t\tCONFIG_H += $(1)/config.h\n\tendif\nendef\n$(foreach module,$(COMMUNITY_MODULE_PATHS),$(eval $(call config_h_community_module_appender,$(module))))\n\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/config.h)\",\"\")\n    CONFIG_H += $(KEYBOARD_PATH_5)/config.h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_4)/config.h)\",\"\")\n    CONFIG_H += $(KEYBOARD_PATH_4)/config.h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_3)/config.h)\",\"\")\n    CONFIG_H += $(KEYBOARD_PATH_3)/config.h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_2)/config.h)\",\"\")\n    CONFIG_H += $(KEYBOARD_PATH_2)/config.h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_1)/config.h)\",\"\")\n    CONFIG_H += $(KEYBOARD_PATH_1)/config.h\nendif\n\nPOST_CONFIG_H :=\n\ndefine post_config_h_community_module_appender\n\tifneq (\"$(wildcard $(1)/post_config.h)\",\"\")\n\t\tPOST_CONFIG_H += $(1)/post_config.h\n\tendif\nendef\n$(foreach module,$(COMMUNITY_MODULE_PATHS),$(eval $(call post_config_h_community_module_appender,$(module))))\n\nifneq (\"$(wildcard $(KEYBOARD_PATH_1)/post_config.h)\",\"\")\n    POST_CONFIG_H += $(KEYBOARD_PATH_1)/post_config.h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_2)/post_config.h)\",\"\")\n    POST_CONFIG_H += $(KEYBOARD_PATH_2)/post_config.h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_3)/post_config.h)\",\"\")\n    POST_CONFIG_H += $(KEYBOARD_PATH_3)/post_config.h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_4)/post_config.h)\",\"\")\n    POST_CONFIG_H += $(KEYBOARD_PATH_4)/post_config.h\nendif\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/post_config.h)\",\"\")\n    POST_CONFIG_H += $(KEYBOARD_PATH_5)/post_config.h\nendif\n\nCONFIG_H += $(INTERMEDIATE_OUTPUT)/src/info_config.h\nKEYBOARD_SRC += $(INTERMEDIATE_OUTPUT)/src/default_keyboard.c\n\n$(INTERMEDIATE_OUTPUT)/src/info_config.h: $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-config-h --quiet --keyboard $(KEYBOARD) --output $(INTERMEDIATE_OUTPUT)/src/info_config.h)\n\t@$(BUILD_CMD)\n\n$(INTERMEDIATE_OUTPUT)/src/default_keyboard.c: $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-keyboard-c --quiet --keyboard $(KEYBOARD) --output $(INTERMEDIATE_OUTPUT)/src/default_keyboard.c)\n\t@$(BUILD_CMD)\n\n$(INTERMEDIATE_OUTPUT)/src/default_keyboard.h: $(DD_CONFIG_FILES)\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-keyboard-h --quiet --keyboard $(KEYBOARD) --include $(FOUND_KEYBOARD_H) --output $(INTERMEDIATE_OUTPUT)/src/default_keyboard.h)\n\t@$(BUILD_CMD)\n\ngenerated-files: $(INTERMEDIATE_OUTPUT)/src/info_config.h $(INTERMEDIATE_OUTPUT)/src/default_keyboard.c $(INTERMEDIATE_OUTPUT)/src/default_keyboard.h\n\ngenerated-files: $(INTERMEDIATE_OUTPUT)/src/info_deps.d\n\n$(INTERMEDIATE_OUTPUT)/src/info_deps.d:\n\t@$(SILENT) || printf \"$(MSG_GENERATING) $@\" | $(AWK_CMD)\n\t$(eval CMD=$(QMK_BIN) generate-make-dependencies -kb $(KEYBOARD) -km $(KEYMAP) -o $(INTERMEDIATE_OUTPUT)/src/info_deps.d)\n\t@$(BUILD_CMD)\n\n-include $(INTERMEDIATE_OUTPUT)/src/info_deps.d\n\n.INTERMEDIATE : generated-files\n\n# Userspace setup and definitions\nifeq (\"$(USER_NAME)\",\"\")\n    USER_NAME := $(KEYMAP)\nendif\nUSER_PATH := users/$(USER_NAME)\n\n# If we have userspace, then add it to the lookup VPATH\nifneq ($(wildcard $(QMK_USERSPACE)),)\n    VPATH += $(QMK_USERSPACE)\nendif\n\n# If the equivalent users directory exists in userspace, use that in preference to anything currently in the main repo\nifneq ($(wildcard $(QMK_USERSPACE)/$(USER_PATH)),)\n    USER_PATH := $(QMK_USERSPACE)/$(USER_PATH)\nendif\n\n# Pull in user level rules.mk\n-include $(USER_PATH)/rules.mk\nifneq (\"$(wildcard $(USER_PATH)/config.h)\",\"\")\n    CONFIG_H += $(USER_PATH)/config.h\nendif\nifneq (\"$(wildcard $(USER_PATH)/post_config.h)\",\"\")\n    POST_CONFIG_H += $(USER_PATH)/post_config.h\nendif\n\n# Disable features that a keyboard doesn't support\n-include $(BUILDDEFS_PATH)/disable_features.mk\n\nifneq (\"$(CONVERTER)\",\"\")\n    -include $(CONVERTER)/post_converter.mk\nendif\n\n# Pull in post_rules.mk files from all our subfolders\n-include $(KEYBOARD_PATH_1)/post_rules.mk\n-include $(KEYBOARD_PATH_2)/post_rules.mk\n-include $(KEYBOARD_PATH_3)/post_rules.mk\n-include $(KEYBOARD_PATH_4)/post_rules.mk\n-include $(KEYBOARD_PATH_5)/post_rules.mk\n\ndefine post_rules_mk_community_module_includer\n\tifneq (\"$(wildcard $(1)/post_rules.mk)\",\"\")\n\t\tinclude $(1)/post_rules.mk\n\tendif\nendef\n$(foreach module,$(COMMUNITY_MODULE_PATHS),$(eval $(call post_rules_mk_community_module_includer,$(module))))\n\nifneq (\"$(wildcard $(KEYMAP_PATH)/config.h)\",\"\")\n    CONFIG_H += $(KEYMAP_PATH)/config.h\nendif\nifneq (\"$(KEYMAP_H)\",\"\")\n    CONFIG_H += $(KEYMAP_H)\nendif\n\nifeq ($(KEYMAP_C),)\n    $(call CATASTROPHIC_ERROR,Invalid keymap,Could not find keymap)\nendif\n\nOPT_DEFS += -DKEYMAP_C=\\\"$(KEYMAP_C)\\\"\n\n# If a keymap or userspace places their keymap array in another file instead, allow for it to be included\n# !!NOTE!! -- For this to work, the source file cannot be part of $(SRC), so users should not add it via `SRC += <file>`\nifneq ($(strip $(INTROSPECTION_KEYMAP_C)),)\nOPT_DEFS += -DINTROSPECTION_KEYMAP_C=\\\"$(strip $(INTROSPECTION_KEYMAP_C))\\\"\nendif\n\n# project specific files\nSRC += \\\n    $(KEYBOARD_SRC) \\\n    $(QUANTUM_DIR)/keymap_introspection.c \\\n    $(QUANTUM_SRC) \\\n    $(QUANTUM_DIR)/main.c \\\n\n# Optimize size but this may cause error \"relocation truncated to fit\"\n#EXTRALDFLAGS = -Wl,--relax\n\n# Search Path\nVPATH += $(KEYMAP_PATH)\nVPATH += $(USER_PATH)\nVPATH += $(KEYBOARD_PATHS)\nVPATH += $(COMMON_VPATH)\nVPATH += $(INTERMEDIATE_OUTPUT)/src\n\ninclude $(BUILDDEFS_PATH)/common_features.mk\ninclude $(BUILDDEFS_PATH)/generic_features.mk\ninclude $(TMK_PATH)/protocol.mk\ninclude $(PLATFORM_PATH)/common.mk\n\nSRC += $(patsubst %.c,%.clib,$(LIB_SRC))\nSRC += $(patsubst %.c,%.clib,$(QUANTUM_LIB_SRC))\n\n-include $(PLATFORM_PATH)/$(PLATFORM_KEY)/bootloader.mk\ninclude $(PLATFORM_PATH)/$(PLATFORM_KEY)/platform.mk\n-include $(PLATFORM_PATH)/$(PLATFORM_KEY)/flash.mk\n\nifneq ($(strip $(PROTOCOL)),)\nPROTOCOL_KEY = $(strip $(shell echo $(PROTOCOL) | tr '[:upper:]' '[:lower:]'))\nelse\nPROTOCOL_KEY = $(PLATFORM_KEY)\nendif\ninclude $(TMK_PATH)/protocol/$(PROTOCOL_KEY)/$(PROTOCOL_KEY).mk\n\n# Setup definitions based on the selected MCU\n$(eval $(call add_qmk_prefix_defs,MCU_ORIG,MCU))\n$(eval $(call add_qmk_prefix_defs,MCU_ARCH,MCU_ARCH))\n$(eval $(call add_qmk_prefix_defs,MCU_PORT_NAME,MCU_PORT_NAME))\n$(eval $(call add_qmk_prefix_defs,MCU_FAMILY,MCU_FAMILY))\n$(eval $(call add_qmk_prefix_defs,MCU_SERIES,MCU_SERIES))\n$(eval $(call add_qmk_prefix_defs,BOARD,BOARD))\n$(eval $(call add_qmk_prefix_defs,OPT,OPT))\n\n# Control whether intermediate file listings are generated\n# e.g.:\n#    make handwired/onekey/blackpill_f411:default KEEP_INTERMEDIATES=yes\n#    cat .build/obj_handwired_onekey_blackpill_f411_default/quantum/quantum.i | sed -e 's@^#.*@@g' -e 's@^\\s*//.*@@g' -e '/^\\s*$/d' | clang-format\nifeq ($(strip $(KEEP_INTERMEDIATES)), yes)\n    OPT_DEFS += -save-temps=obj\nendif\n\nOUTPUTS := $(INTERMEDIATE_OUTPUT)\n$(INTERMEDIATE_OUTPUT)_SRC := $(SRC) $(PLATFORM_SRC)\n$(INTERMEDIATE_OUTPUT)_DEFS := \\\n\t-DQMK_KEYBOARD=\\\"$(KEYBOARD)\\\" -DQMK_KEYBOARD_H=\\\"$(INTERMEDIATE_OUTPUT)/src/default_keyboard.h\\\" \\\n\t-DQMK_KEYMAP=\\\"$(KEYMAP)\\\" -DQMK_KEYMAP_H=\\\"$(KEYMAP).h\\\" -DQMK_KEYMAP_CONFIG_H=\\\"$(KEYMAP_PATH)/config.h\\\" \\\n\t$(OPT_DEFS)\n$(INTERMEDIATE_OUTPUT)_INC :=  $(VPATH) $(EXTRAINCDIRS) $(KEYBOARD_PATHS)\n$(INTERMEDIATE_OUTPUT)_CONFIG := $(CONFIG_H) $(POST_CONFIG_H)\n\n# Default target.\nall: build check-size\n\nbuild: elf cpfirmware\ncheck-size: build\ncheck-md5: build\nobjs-size: build\n\nifneq ($(strip $(TOP_SYMBOLS)),)\nifeq ($(strip $(TOP_SYMBOLS)),yes)\nNUM_TOP_SYMBOLS := 10\nelse\nNUM_TOP_SYMBOLS := $(strip $(TOP_SYMBOLS))\nendif\nall: top-symbols\ncheck-size: top-symbols\ntop-symbols: build\n\techo \"###########################################\"\n\techo \"# Highest flash usage:\"\n\t$(NM) -Crtd --size-sort $(BUILD_DIR)/$(TARGET).elf | grep ' [RrTt] ' | head -n$(NUM_TOP_SYMBOLS) | sed -e 's#^0000000#       #g' -e 's#^000000#      #g' -e 's#^00000#     #g' -e 's#^0000#    #g' -e 's#^000#   #g' -e 's#^00#  #g' -e 's#^0# #g'\n\techo \"###########################################\"\n\techo \"# Highest RAM usage:\"\n\t$(NM) -Crtd --size-sort $(BUILD_DIR)/$(TARGET).elf | grep ' [BbCDdGgSs] ' | head -n$(NUM_TOP_SYMBOLS) | sed -e 's#^0000000#       #g' -e 's#^000000#      #g' -e 's#^00000#     #g' -e 's#^0000#    #g' -e 's#^000#   #g' -e 's#^00#  #g' -e 's#^0# #g'\n\techo \"###########################################\"\nendif\n\ninclude $(BUILDDEFS_PATH)/show_options.mk\ninclude $(BUILDDEFS_PATH)/common_rules.mk\n\n# Ensure we have generated files available for each of the objects\ndefine GEN_FILES\n$1: generated-files\nendef\n$(foreach O,$(OBJ),$(eval $(call GEN_FILES,$(patsubst %.a,%.o,$(O)))))\n"
  },
  {
    "path": "builddefs/build_layout.mk",
    "content": "LAYOUTS_PATH := layouts\nLAYOUTS_REPOS := $(patsubst %/,%,$(sort $(dir $(wildcard $(LAYOUTS_PATH)/*/))))\n\nifneq ($(QMK_USERSPACE),)\n    LAYOUTS_REPOS += $(patsubst %/,%,$(QMK_USERSPACE)/$(LAYOUTS_PATH))\nendif\n\ndefine SEARCH_LAYOUTS_REPO\n    LAYOUT_KEYMAP_PATH := $$(LAYOUTS_REPO)/$$(LAYOUT)/$$(KEYMAP)\n    LAYOUT_KEYMAP_JSON := $$(LAYOUT_KEYMAP_PATH)/keymap.json\n    LAYOUT_KEYMAP_C := $$(LAYOUT_KEYMAP_PATH)/keymap.c\n    ifneq (\"$$(wildcard $$(LAYOUT_KEYMAP_JSON))\",\"\")\n        -include $$(LAYOUT_KEYMAP_PATH)/rules.mk\n        KEYMAP_JSON := $$(LAYOUT_KEYMAP_JSON)\n        KEYMAP_PATH := $$(LAYOUT_KEYMAP_PATH)\n    else ifneq (\"$$(wildcard $$(LAYOUT_KEYMAP_C))\",\"\")\n        -include $$(LAYOUT_KEYMAP_PATH)/rules.mk\n        KEYMAP_C := $$(LAYOUT_KEYMAP_C)\n        KEYMAP_PATH := $$(LAYOUT_KEYMAP_PATH)\n    endif\nendef\n\ndefine SEARCH_LAYOUTS\n    $$(foreach LAYOUTS_REPO,$$(LAYOUTS_REPOS),$$(eval $$(call SEARCH_LAYOUTS_REPO)))\nendef\n\nifneq ($(FORCE_LAYOUT),)\n    ifneq (,$(findstring $(FORCE_LAYOUT),$(LAYOUTS)))\n        $(info Forcing layout: $(FORCE_LAYOUT))\n        LAYOUTS := $(FORCE_LAYOUT)\n    else\n        $(call CATASTROPHIC_ERROR,Invalid layout,Forced layout does not exist)\n    endif\nendif\n\n$(foreach LAYOUT,$(LAYOUTS),$(eval $(call SEARCH_LAYOUTS)))\n"
  },
  {
    "path": "builddefs/build_test.mk",
    "content": "ifndef VERBOSE\n.SILENT:\nendif\n\n.DEFAULT_GOAL := all\n\nOPT = g\n\ninclude paths.mk\ninclude $(BUILDDEFS_PATH)/support.mk\ninclude $(BUILDDEFS_PATH)/message.mk\n\nTARGET=test/$(TEST_OUTPUT)\n\nGTEST_OUTPUT = $(BUILD_DIR)/gtest\n\nTEST_OBJ = $(BUILD_DIR)/test_obj\n\nOUTPUTS := $(TEST_OBJ)/$(TEST_OUTPUT) $(GTEST_OUTPUT)\n\nGTEST_INC := \\\n\t$(LIB_PATH)/googletest/googletest/include \\\n\t$(LIB_PATH)/googletest/googlemock/include\n\nGTEST_INTERNAL_INC := \\\n\t$(LIB_PATH)/googletest/googletest \\\n\t$(LIB_PATH)/googletest/googlemock\n\n$(GTEST_OUTPUT)_SRC := \\\n\tgoogletest/src/gtest-all.cc\\\n\tgooglemock/src/gmock-all.cc\n\n$(GTEST_OUTPUT)_DEFS :=\n$(GTEST_OUTPUT)_INC := $(GTEST_INC) $(GTEST_INTERNAL_INC)\n\nLDFLAGS += -lstdc++ -lpthread -shared-libgcc\nCREATE_MAP := no\n\nVPATH += \\\n\t$(LIB_PATH)/googletest \\\n\t$(LIB_PATH)/googlemock \\\n\t$(COMMON_VPATH) \\\n\t$(TEST_PATH)\n\nall: elf\n\nPLATFORM:=TEST\nPLATFORM_KEY:=test\nBOOTLOADER_TYPE:=none\n\nDEBUG ?= 0\nifneq ($(strip $(DEBUG)), 0)\nCONSOLE_ENABLE = yes\nendif\n\nifneq ($(filter $(FULL_TESTS),$(TEST)),)\ninclude tests/test_common/build.mk\ninclude $(TEST_PATH)/test.mk\nendif\n\ninclude $(BUILDDEFS_PATH)/common_features.mk\ninclude $(BUILDDEFS_PATH)/generic_features.mk\ninclude $(PLATFORM_PATH)/common.mk\ninclude $(TMK_PATH)/protocol.mk\ninclude $(QUANTUM_PATH)/debounce/tests/rules.mk\ninclude $(QUANTUM_PATH)/encoder/tests/rules.mk\ninclude $(QUANTUM_PATH)/os_detection/tests/rules.mk\ninclude $(QUANTUM_PATH)/sequencer/tests/rules.mk\ninclude $(QUANTUM_PATH)/wear_leveling/tests/rules.mk\ninclude $(QUANTUM_PATH)/logging/print.mk\ninclude $(PLATFORM_PATH)/test/rules.mk\nifneq ($(filter $(FULL_TESTS),$(TEST)),)\ninclude $(BUILDDEFS_PATH)/build_full_test.mk\nendif\n\n$(TEST_OUTPUT)_SRC += \\\n\ttests/test_common/main.cpp \\\n\t$(QUANTUM_PATH)/logging/print.c\n\nifneq ($(strip $(INTROSPECTION_KEYMAP_C)),)\n$(TEST_OUTPUT)_DEFS += -DINTROSPECTION_KEYMAP_C=\\\"$(strip $(INTROSPECTION_KEYMAP_C))\\\"\nendif\n\n$(TEST_OBJ)/$(TEST_OUTPUT)_SRC := $($(TEST_OUTPUT)_SRC)\n$(TEST_OBJ)/$(TEST_OUTPUT)_INC := $($(TEST_OUTPUT)_INC) $(VPATH) $(GTEST_INC)\n$(TEST_OBJ)/$(TEST_OUTPUT)_DEFS := $($(TEST_OUTPUT)_DEFS)\n$(TEST_OBJ)/$(TEST_OUTPUT)_CONFIG := $($(TEST_OUTPUT)_CONFIG)\n\ninclude $(PLATFORM_PATH)/$(PLATFORM_KEY)/platform.mk\ninclude $(BUILDDEFS_PATH)/common_rules.mk\n\n\n$(shell mkdir -p $(BUILD_DIR)/test 2>/dev/null)\n$(shell mkdir -p $(TEST_OBJ) 2>/dev/null)\n"
  },
  {
    "path": "builddefs/common_features.mk",
    "content": "# Copyright 2017 Fred Sundvik\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nQUANTUM_SRC += \\\n    $(QUANTUM_DIR)/quantum.c \\\n    $(QUANTUM_DIR)/bitwise.c \\\n    $(QUANTUM_DIR)/led.c \\\n    $(QUANTUM_DIR)/action.c \\\n    $(QUANTUM_DIR)/action_layer.c \\\n    $(QUANTUM_DIR)/action_tapping.c \\\n    $(QUANTUM_DIR)/action_util.c \\\n    $(QUANTUM_DIR)/eeconfig.c \\\n    $(QUANTUM_DIR)/keyboard.c \\\n    $(QUANTUM_DIR)/keymap_common.c \\\n    $(QUANTUM_DIR)/keycode_config.c \\\n    $(QUANTUM_DIR)/sync_timer.c \\\n    $(QUANTUM_DIR)/logging/debug.c \\\n    $(QUANTUM_DIR)/logging/sendchar.c \\\n    $(QUANTUM_DIR)/process_keycode/process_default_layer.c \\\n\ninclude $(QUANTUM_DIR)/nvm/rules.mk\n\nVPATH += $(QUANTUM_DIR)/logging\n# Fall back to lib/printf if there is no platform provided print\nifeq (\"$(wildcard $(PLATFORM_PATH)/$(PLATFORM_KEY)/printf.mk)\",\"\")\n    include $(QUANTUM_PATH)/logging/print.mk\nelse\n    include $(PLATFORM_PATH)/$(PLATFORM_KEY)/printf.mk\nendif\n\nifeq ($(strip $(DEBUG_MATRIX_SCAN_RATE_ENABLE)), yes)\n    OPT_DEFS += -DDEBUG_MATRIX_SCAN_RATE\n    CONSOLE_ENABLE = yes\nelse ifeq ($(strip $(DEBUG_MATRIX_SCAN_RATE_ENABLE)), api)\n    OPT_DEFS += -DDEBUG_MATRIX_SCAN_RATE\nendif\n\nAUDIO_ENABLE ?= no\nifeq ($(strip $(AUDIO_ENABLE)), yes)\n    ifeq ($(PLATFORM),CHIBIOS)\n        AUDIO_DRIVER ?= dac_basic\n        ifeq ($(strip $(AUDIO_DRIVER)), dac_basic)\n            OPT_DEFS += -DAUDIO_DRIVER_DAC\n        else ifeq ($(strip $(AUDIO_DRIVER)), dac_additive)\n            OPT_DEFS += -DAUDIO_DRIVER_DAC\n        ## stm32f2 and above have a usable DAC unit, f1 do not, and need to use pwm instead\n        else ifeq ($(strip $(AUDIO_DRIVER)), pwm_software)\n            OPT_DEFS += -DAUDIO_DRIVER_PWM\n        else ifeq ($(strip $(AUDIO_DRIVER)), pwm_hardware)\n            OPT_DEFS += -DAUDIO_DRIVER_PWM\n        endif\n    else\n        # fallback for all other platforms is pwm\n        AUDIO_DRIVER ?= pwm_hardware\n        OPT_DEFS += -DAUDIO_DRIVER_PWM\n    endif\n    OPT_DEFS += -DAUDIO_ENABLE\n    COMMON_VPATH += $(QUANTUM_PATH)/audio\n    MUSIC_ENABLE = yes\n    SRC += $(QUANTUM_DIR)/process_keycode/process_audio.c\n    SRC += $(QUANTUM_DIR)/process_keycode/process_clicky.c\n    SRC += $(QUANTUM_DIR)/audio/audio.c ## common audio code, hardware agnostic\n    SRC += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/audio_$(strip $(AUDIO_DRIVER)).c\n    SRC += $(QUANTUM_DIR)/audio/voices.c\n    SRC += $(QUANTUM_DIR)/audio/luts.c\nendif\n\nifeq ($(strip $(SEQUENCER_ENABLE)), yes)\n    MUSIC_ENABLE = yes\nendif\n\nifeq ($(strip $(MIDI_ENABLE)), yes)\n    OPT_DEFS += -DMIDI_ENABLE\n    MUSIC_ENABLE = yes\n    COMMON_VPATH += $(QUANTUM_PATH)/midi\n    SRC += $(QUANTUM_DIR)/midi/midi.c\n    SRC += $(QUANTUM_DIR)/midi/midi_device.c\n    SRC += $(QUANTUM_DIR)/midi/qmk_midi.c\n    SRC += $(QUANTUM_DIR)/midi/sysex_tools.c\n    SRC += $(QUANTUM_DIR)/midi/bytequeue/bytequeue.c\n    SRC += $(QUANTUM_DIR)/midi/bytequeue/interrupt_setting.c\n    SRC += $(QUANTUM_DIR)/process_keycode/process_midi.c\nendif\n\nVALID_STENO_PROTOCOL_TYPES := geminipr txbolt all\nSTENO_PROTOCOL ?= all\nifeq ($(strip $(STENO_ENABLE)), yes)\n    ifeq ($(filter $(STENO_PROTOCOL),$(VALID_STENO_PROTOCOL_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid STENO_PROTOCOL,STENO_PROTOCOL=\"$(STENO_PROTOCOL)\" is not a valid stenography protocol)\n    else\n        OPT_DEFS += -DSTENO_ENABLE\n        VIRTSER_ENABLE ?= yes\n\n        ifeq ($(strip $(STENO_PROTOCOL)), geminipr)\n            OPT_DEFS += -DSTENO_ENABLE_GEMINI\n        endif\n        ifeq ($(strip $(STENO_PROTOCOL)), txbolt)\n            OPT_DEFS += -DSTENO_ENABLE_BOLT\n        endif\n        ifeq ($(strip $(STENO_PROTOCOL)), all)\n            OPT_DEFS += -DSTENO_ENABLE_ALL\n            OPT_DEFS += -DSTENO_ENABLE_GEMINI\n            OPT_DEFS += -DSTENO_ENABLE_BOLT\n        endif\n\n        SRC += $(QUANTUM_DIR)/process_keycode/process_steno.c\n    endif\nendif\n\nifeq ($(strip $(MOUSEKEY_ENABLE)), yes)\n    MOUSE_ENABLE := yes\nendif\n\nVALID_POINTING_DEVICE_DRIVER_TYPES := adns5050 adns9800 analog_joystick azoteq_iqs5xx cirque_pinnacle_i2c cirque_pinnacle_spi paw3204 pmw3320 pmw3360 pmw3389 pimoroni_trackball navigator_trackpad custom\nifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)\n    ifeq ($(filter $(POINTING_DEVICE_DRIVER),$(VALID_POINTING_DEVICE_DRIVER_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid POINTING_DEVICE_DRIVER,POINTING_DEVICE_DRIVER=\"$(POINTING_DEVICE_DRIVER)\" is not a valid pointing device type)\n    else\n        OPT_DEFS += -DPOINTING_DEVICE_ENABLE\n        MOUSE_ENABLE := yes\n        VPATH += $(QUANTUM_DIR)/pointing_device\n        SRC += $(QUANTUM_DIR)/pointing_device/pointing_device.c\n        SRC += $(QUANTUM_DIR)/pointing_device/pointing_device_auto_mouse.c\n        ifneq ($(strip $(POINTING_DEVICE_DRIVER)), custom)\n            SRC += drivers/sensors/$(strip $(POINTING_DEVICE_DRIVER)).c\n            OPT_DEFS += -DPOINTING_DEVICE_DRIVER_$(strip $(shell echo $(POINTING_DEVICE_DRIVER) | tr '[:lower:]' '[:upper:]'))\n        endif\n        OPT_DEFS += -DPOINTING_DEVICE_DRIVER_$(strip $(POINTING_DEVICE_DRIVER))\n        OPT_DEFS += -DPOINTING_DEVICE_DRIVER_NAME=$(strip $(POINTING_DEVICE_DRIVER))\n        ifeq ($(strip $(POINTING_DEVICE_DRIVER)), adns9800)\n            SPI_DRIVER_REQUIRED = yes\n        else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), analog_joystick)\n            ANALOG_DRIVER_REQUIRED = yes\n        else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), azoteq_iqs5xx)\n            I2C_DRIVER_REQUIRED = yes\n        else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), cirque_pinnacle_i2c)\n            I2C_DRIVER_REQUIRED = yes\n            SRC += drivers/sensors/cirque_pinnacle.c\n            SRC += drivers/sensors/cirque_pinnacle_gestures.c\n            SRC += $(QUANTUM_DIR)/pointing_device/pointing_device_gestures.c\n        else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), cirque_pinnacle_spi)\n            SPI_DRIVER_REQUIRED = yes\n            SRC += drivers/sensors/cirque_pinnacle.c\n            SRC += drivers/sensors/cirque_pinnacle_gestures.c\n            SRC += $(QUANTUM_DIR)/pointing_device/pointing_device_gestures.c\n        else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), pimoroni_trackball)\n            I2C_DRIVER_REQUIRED = yes\n        else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), navigator_trackpad)\n            I2C_DRIVER_REQUIRED = yes\n            SRC += drivers/sensors/navigator.c\n        else ifneq ($(filter $(strip $(POINTING_DEVICE_DRIVER)),pmw3360 pmw3389),)\n            SPI_DRIVER_REQUIRED = yes\n            SRC += drivers/sensors/pmw33xx_common.c\n        endif\n    endif\nendif\n\nQUANTUM_PAINTER_ENABLE ?= no\nifeq ($(strip $(QUANTUM_PAINTER_ENABLE)), yes)\n    include $(QUANTUM_DIR)/painter/rules.mk\nendif\n\nVALID_EEPROM_DRIVER_TYPES := vendor custom transient i2c spi wear_leveling legacy_stm32_flash\nEEPROM_DRIVER ?= vendor\nifneq ($(strip $(EEPROM_DRIVER)),none)\n  ifeq ($(filter $(EEPROM_DRIVER),$(VALID_EEPROM_DRIVER_TYPES)),)\n  $(call CATASTROPHIC_ERROR,Invalid EEPROM_DRIVER,EEPROM_DRIVER=\"$(EEPROM_DRIVER)\" is not a valid EEPROM driver)\n  else\n    OPT_DEFS += -DEEPROM_ENABLE\n    COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/eeprom\n    COMMON_VPATH += $(DRIVER_PATH)/eeprom\n    COMMON_VPATH += $(PLATFORM_COMMON_DIR)\n    ifeq ($(strip $(EEPROM_DRIVER)), custom)\n      # Custom EEPROM implementation -- only needs to implement init/erase/read_block/write_block\n      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_CUSTOM\n      SRC += eeprom_driver.c\n    else ifeq ($(strip $(EEPROM_DRIVER)), wear_leveling)\n      # Wear-leveling EEPROM implementation\n      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_WEAR_LEVELING\n      SRC += eeprom_driver.c eeprom_wear_leveling.c\n    else ifeq ($(strip $(EEPROM_DRIVER)), i2c)\n      # External I2C EEPROM implementation\n      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_I2C\n      I2C_DRIVER_REQUIRED = yes\n      SRC += eeprom_driver.c eeprom_i2c.c\n    else ifeq ($(strip $(EEPROM_DRIVER)), spi)\n      # External SPI EEPROM implementation\n      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_SPI\n      SPI_DRIVER_REQUIRED = yes\n      SRC += eeprom_driver.c eeprom_spi.c\n    else ifeq ($(strip $(EEPROM_DRIVER)), legacy_stm32_flash)\n      # STM32 Emulated EEPROM, backed by MCU flash (soon to be deprecated)\n      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_LEGACY_EMULATED_FLASH\n      COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/flash\n      COMMON_VPATH += $(DRIVER_PATH)/flash\n      SRC += eeprom_driver.c eeprom_legacy_emulated_flash.c legacy_flash_ops.c\n    else ifeq ($(strip $(EEPROM_DRIVER)), transient)\n      # Transient EEPROM implementation -- no data storage but provides runtime area for it\n      OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_TRANSIENT\n      SRC += eeprom_driver.c eeprom_transient.c\n    else ifeq ($(strip $(EEPROM_DRIVER)), vendor)\n      # Vendor-implemented EEPROM\n      OPT_DEFS += -DEEPROM_VENDOR\n      ifeq ($(PLATFORM),AVR)\n        # Automatically provided by avr-libc, nothing required\n      else ifeq ($(PLATFORM),CHIBIOS)\n        ifneq ($(filter %_STM32F072xB %_STM32F042x6, $(MCU_SERIES)_$(MCU_LDSCRIPT)),)\n          # STM32 Emulated EEPROM, backed by MCU flash (soon to be deprecated)\n          OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_LEGACY_EMULATED_FLASH\n          COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/flash\n          COMMON_VPATH += $(DRIVER_PATH)/flash\n          SRC += eeprom_driver.c eeprom_legacy_emulated_flash.c legacy_flash_ops.c\n        else ifneq ($(filter $(MCU_SERIES),STM32F1xx STM32F3xx STM32F4xx STM32L4xx STM32G0xx STM32G4xx WB32F3G71xx WB32FQ95xx AT32F415 GD32VF103),)\n          # Wear-leveling EEPROM implementation, backed by MCU flash\n          OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_WEAR_LEVELING\n          SRC += eeprom_driver.c eeprom_wear_leveling.c\n          WEAR_LEVELING_DRIVER ?= embedded_flash\n        else ifneq ($(filter $(MCU_SERIES),STM32L0xx STM32L1xx),)\n          # True EEPROM on STM32L0xx, L1xx\n          OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_STM32_L0_L1\n          SRC += eeprom_driver.c eeprom_stm32_L0_L1.c\n        else ifneq ($(filter $(MCU_SERIES),RP2040),)\n          # Wear-leveling EEPROM implementation, backed by RP2040 flash\n          OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_WEAR_LEVELING\n          SRC += eeprom_driver.c eeprom_wear_leveling.c\n          WEAR_LEVELING_DRIVER ?= rp2040_flash\n        else ifneq ($(filter $(MCU_SERIES),KL2x K20x),)\n          # Teensy EEPROM implementations\n          OPT_DEFS += -DEEPROM_KINETIS_FLEXRAM\n          SRC += eeprom_kinetis_flexram.c\n        else\n          # Fall back to transient, i.e. non-persistent\n          OPT_DEFS += -DEEPROM_DRIVER -DEEPROM_TRANSIENT\n          SRC += eeprom_driver.c eeprom_transient.c\n        endif\n      else ifeq ($(PLATFORM),TEST)\n        # Test harness \"EEPROM\"\n        OPT_DEFS += -DEEPROM_TEST_HARNESS\n        SRC += eeprom.c\n      endif\n    endif\n  endif\nendif\n\nVALID_WEAR_LEVELING_DRIVER_TYPES := custom embedded_flash spi_flash rp2040_flash legacy\nWEAR_LEVELING_DRIVER ?= none\nifneq ($(strip $(WEAR_LEVELING_DRIVER)),none)\n  ifeq ($(filter $(WEAR_LEVELING_DRIVER),$(VALID_WEAR_LEVELING_DRIVER_TYPES)),)\n    $(call CATASTROPHIC_ERROR,Invalid WEAR_LEVELING_DRIVER,WEAR_LEVELING_DRIVER=\"$(WEAR_LEVELING_DRIVER)\" is not a valid wear leveling driver)\n  else\n    FNV_ENABLE := yes\n    OPT_DEFS += -DWEAR_LEVELING_ENABLE\n    OPT_DEFS += -DWEAR_LEVELING_$(strip $(shell echo $(WEAR_LEVELING_DRIVER) | tr '[:lower:]' '[:upper:]'))\n    COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/wear_leveling\n    COMMON_VPATH += $(DRIVER_PATH)/wear_leveling\n    COMMON_VPATH += $(QUANTUM_DIR)/wear_leveling\n    SRC += wear_leveling.c\n    ifeq ($(strip $(WEAR_LEVELING_DRIVER)), embedded_flash)\n      OPT_DEFS += -DHAL_USE_EFL\n      SRC += wear_leveling_efl.c\n    else ifeq ($(strip $(WEAR_LEVELING_DRIVER)), spi_flash)\n      FLASH_DRIVER := spi\n      SRC += wear_leveling_flash_spi.c\n    else ifeq ($(strip $(WEAR_LEVELING_DRIVER)), rp2040_flash)\n      SRC += wear_leveling_rp2040_flash.c\n    else ifeq ($(strip $(WEAR_LEVELING_DRIVER)), legacy)\n      COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/flash\n      SRC += legacy_flash_ops.c wear_leveling_legacy.c\n    endif\n  endif\nendif\n\nVALID_FLASH_DRIVER_TYPES := spi custom\nFLASH_DRIVER ?= none\nifneq ($(strip $(FLASH_DRIVER)), none)\n    ifeq ($(filter $(FLASH_DRIVER),$(VALID_FLASH_DRIVER_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid FLASH_DRIVER,FLASH_DRIVER=\"$(FLASH_DRIVER)\" is not a valid flash driver)\n    else\n        OPT_DEFS += -DFLASH_ENABLE -DFLASH_DRIVER -DFLASH_DRIVER_$(strip $(shell echo $(FLASH_DRIVER) | tr '[:lower:]' '[:upper:]'))\n\t\tCOMMON_VPATH += $(DRIVER_PATH)/flash\n        ifeq ($(strip $(FLASH_DRIVER)),spi)\n            SRC += flash_spi.c\n            SPI_DRIVER_REQUIRED = yes\n        endif\n    endif\nendif\n\nRGBLIGHT_ENABLE ?= no\nVALID_RGBLIGHT_TYPES := ws2812 apa102 custom\n\nifeq ($(strip $(RGBLIGHT_ENABLE)), yes)\n    RGBLIGHT_DRIVER ?= ws2812\n\n    ifeq ($(filter $(RGBLIGHT_DRIVER),$(VALID_RGBLIGHT_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid RGBLIGHT_DRIVER,RGBLIGHT_DRIVER=\"$(RGBLIGHT_DRIVER)\" is not a valid RGB type)\n    else\n        COMMON_VPATH += $(QUANTUM_DIR)/rgblight\n        POST_CONFIG_H += $(QUANTUM_DIR)/rgblight/rgblight_post_config.h\n        OPT_DEFS += -DRGBLIGHT_ENABLE\n        OPT_DEFS += -DRGBLIGHT_$(strip $(shell echo $(RGBLIGHT_DRIVER) | tr '[:lower:]' '[:upper:]'))\n        SRC += $(QUANTUM_DIR)/process_keycode/process_underglow.c\n        SRC += $(QUANTUM_DIR)/color.c\n        SRC += $(QUANTUM_DIR)/rgblight/rgblight.c\n        SRC += $(QUANTUM_DIR)/rgblight/rgblight_drivers.c\n        CIE1931_CURVE := yes\n    endif\n\n    ifeq ($(strip $(RGBLIGHT_DRIVER)), ws2812)\n        WS2812_DRIVER_REQUIRED := yes\n    endif\n\n    ifeq ($(strip $(RGBLIGHT_DRIVER)), apa102)\n        APA102_DRIVER_REQUIRED := yes\n    endif\n\n    ifeq ($(strip $(VELOCIKEY_ENABLE)), yes)\n        OPT_DEFS += -DVELOCIKEY_ENABLE\n    endif\nendif\n\n# Deprecated driver names - do not use\nifeq ($(strip $(LED_MATRIX_DRIVER)), aw20216)\nLED_MATRIX_DRIVER := aw20216s\nendif\nifeq ($(strip $(LED_MATRIX_DRIVER)), ckled2001)\nLED_MATRIX_DRIVER := snled27351\nendif\n\nLED_MATRIX_ENABLE ?= no\nVALID_LED_MATRIX_TYPES := is31fl3218 is31fl3236 is31fl3729 is31fl3731 is31fl3733 is31fl3736 is31fl3737 is31fl3741 is31fl3742a is31fl3743a is31fl3745 is31fl3746a snled27351 custom\n\nifeq ($(strip $(LED_MATRIX_ENABLE)), yes)\n    ifeq ($(filter $(LED_MATRIX_DRIVER),$(VALID_LED_MATRIX_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid LED_MATRIX_DRIVER,LED_MATRIX_DRIVER=\"$(LED_MATRIX_DRIVER)\" is not a valid matrix type)\n    endif\n    OPT_DEFS += -DLED_MATRIX_ENABLE\n    OPT_DEFS += -DLED_MATRIX_$(strip $(shell echo $(LED_MATRIX_DRIVER) | tr '[:lower:]' '[:upper:]'))\n\n    COMMON_VPATH += $(QUANTUM_DIR)/led_matrix\n    COMMON_VPATH += $(QUANTUM_DIR)/led_matrix/animations\n    COMMON_VPATH += $(QUANTUM_DIR)/led_matrix/animations/runners\n    POST_CONFIG_H += $(QUANTUM_DIR)/led_matrix/post_config.h\n    SRC += $(QUANTUM_DIR)/process_keycode/process_led_matrix.c\n    SRC += $(QUANTUM_DIR)/led_matrix/led_matrix.c\n    SRC += $(QUANTUM_DIR)/led_matrix/led_matrix_drivers.c\n    LIB8TION_ENABLE := yes\n    CIE1931_CURVE := yes\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3218)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3218-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3236)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3236-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3729)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3729-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3731)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3731-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3733)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3733-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3736)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3736-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3737)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3737-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3741)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3741-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3742a)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3742a-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3743a)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3743a-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3745)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3745-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), is31fl3746a)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3746a-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_DRIVER)), snled27351)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led\n        SRC += snled27351-mono.c\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_CUSTOM_KB)), yes)\n        OPT_DEFS += -DLED_MATRIX_CUSTOM_KB\n    endif\n\n    ifeq ($(strip $(LED_MATRIX_CUSTOM_USER)), yes)\n        OPT_DEFS += -DLED_MATRIX_CUSTOM_USER\n    endif\nendif\n\n# Deprecated driver names - do not use\nifeq ($(strip $(RGB_MATRIX_DRIVER)), aw20216)\nRGB_MATRIX_DRIVER := aw20216s\nendif\nifeq ($(strip $(RGB_MATRIX_DRIVER)), ckled2001)\nRGB_MATRIX_DRIVER := snled27351\nendif\n\nRGB_MATRIX_ENABLE ?= no\n\nVALID_RGB_MATRIX_TYPES := aw20216s is31fl3218 is31fl3236 is31fl3729 is31fl3731 is31fl3733 is31fl3736 is31fl3737 is31fl3741 is31fl3742a is31fl3743a is31fl3745 is31fl3746a snled27351 ws2812 custom\nifeq ($(strip $(RGB_MATRIX_ENABLE)), yes)\n    ifeq ($(filter $(RGB_MATRIX_DRIVER),$(VALID_RGB_MATRIX_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid RGB_MATRIX_DRIVER,RGB_MATRIX_DRIVER=\"$(RGB_MATRIX_DRIVER)\" is not a valid matrix type)\n    endif\n    OPT_DEFS += -DRGB_MATRIX_ENABLE\n    OPT_DEFS += -DRGB_MATRIX_$(strip $(shell echo $(RGB_MATRIX_DRIVER) | tr '[:lower:]' '[:upper:]'))\n\n    COMMON_VPATH += $(QUANTUM_DIR)/rgb_matrix\n    COMMON_VPATH += $(QUANTUM_DIR)/rgb_matrix/animations\n    COMMON_VPATH += $(QUANTUM_DIR)/rgb_matrix/animations/runners\n    POST_CONFIG_H += $(QUANTUM_DIR)/rgb_matrix/post_config.h\n\n    # TODO: Remove this\n    SRC += $(QUANTUM_DIR)/process_keycode/process_underglow.c\n\n    SRC += $(QUANTUM_DIR)/process_keycode/process_rgb_matrix.c\n    SRC += $(QUANTUM_DIR)/color.c\n    SRC += $(QUANTUM_DIR)/rgb_matrix/rgb_matrix.c\n    SRC += $(QUANTUM_DIR)/rgb_matrix/rgb_matrix_drivers.c\n    LIB8TION_ENABLE := yes\n    CIE1931_CURVE := yes\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), aw20216s)\n        SPI_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led\n        SRC += aw20216s.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3218)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3218.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3236)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3236.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3729)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3729.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3731)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3731.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3733)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3733.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3736)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3736.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3737)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3737.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3741)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3741.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3742a)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3742a.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3743a)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3743a.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3745)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3745.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), is31fl3746a)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led/issi\n        SRC += is31fl3746a.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), snled27351)\n        I2C_DRIVER_REQUIRED = yes\n        COMMON_VPATH += $(DRIVER_PATH)/led\n        SRC += snled27351.c\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), ws2812)\n        WS2812_DRIVER_REQUIRED := yes\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_DRIVER)), apa102)\n        APA102_DRIVER_REQUIRED := yes\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_CUSTOM_KB)), yes)\n        OPT_DEFS += -DRGB_MATRIX_CUSTOM_KB\n    endif\n\n    ifeq ($(strip $(RGB_MATRIX_CUSTOM_USER)), yes)\n        OPT_DEFS += -DRGB_MATRIX_CUSTOM_USER\n    endif\nendif\n\nVARIABLE_TRACE ?= no\nifneq ($(strip $(VARIABLE_TRACE)),no)\n    SRC += $(QUANTUM_DIR)/variable_trace.c\n    OPT_DEFS += -DNUM_TRACED_VARIABLES=$(strip $(VARIABLE_TRACE))\n    ifneq ($(strip $(MAX_VARIABLE_TRACE_SIZE)),)\n        OPT_DEFS += -DMAX_VARIABLE_TRACE_SIZE=$(strip $(MAX_VARIABLE_TRACE_SIZE))\n    endif\nendif\n\nifeq ($(strip $(SLEEP_LED_ENABLE)), yes)\n    SRC += $(PLATFORM_COMMON_DIR)/sleep_led.c\n    OPT_DEFS += -DSLEEP_LED_ENABLE\n\n    NO_SUSPEND_POWER_DOWN := yes\nendif\n\nVALID_BACKLIGHT_TYPES := pwm timer software custom\n\nBACKLIGHT_ENABLE ?= no\nBACKLIGHT_DRIVER ?= pwm\nifeq ($(strip $(BACKLIGHT_ENABLE)), yes)\n    ifeq ($(filter $(BACKLIGHT_DRIVER),$(VALID_BACKLIGHT_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid BACKLIGHT_DRIVER,BACKLIGHT_DRIVER=\"$(BACKLIGHT_DRIVER)\" is not a valid backlight type)\n    endif\n\n    COMMON_VPATH += $(QUANTUM_DIR)/backlight\n    COMMON_VPATH += $(DRIVER_PATH)/backlight\n    SRC += $(QUANTUM_DIR)/backlight/backlight.c\n    SRC += $(QUANTUM_DIR)/process_keycode/process_backlight.c\n    OPT_DEFS += -DBACKLIGHT_ENABLE\n    OPT_DEFS += -DBACKLIGHT_$(strip $(shell echo $(BACKLIGHT_DRIVER) | tr '[:lower:]' '[:upper:]'))\n\n    ifneq ($(strip $(BACKLIGHT_DRIVER)), custom)\n        SRC += $(QUANTUM_DIR)/backlight/backlight_driver_common.c\n\n        ifeq ($(strip $(BACKLIGHT_DRIVER)), software)\n            SRC += $(DRIVER_PATH)/backlight/backlight_software.c\n        else\n            SRC += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/backlight_$(strip $(BACKLIGHT_DRIVER)).c\n        endif\n    endif\nendif\n\nifeq ($(strip $(CIE1931_CURVE)), yes)\n    OPT_DEFS += -DUSE_CIE1931_CURVE\n    LED_TABLES := yes\nendif\n\nifeq ($(strip $(LED_TABLES)), yes)\n    SRC += $(QUANTUM_DIR)/led_tables.c\nendif\n\nifeq ($(strip $(VIA_ENABLE)), yes)\n    DYNAMIC_KEYMAP_ENABLE := yes\n    RAW_ENABLE := yes\n    BOOTMAGIC_ENABLE := yes\n    TRI_LAYER_ENABLE := yes\nendif\n\nifeq ($(strip $(RAW_ENABLE)), yes)\n    OPT_DEFS += -DRAW_ENABLE\n    SRC += raw_hid.c\nendif\n\nifeq ($(strip $(DYNAMIC_KEYMAP_ENABLE)), yes)\n    SEND_STRING_ENABLE := yes\nendif\n\nVALID_CUSTOM_MATRIX_TYPES:= yes lite no\n\nCUSTOM_MATRIX ?= no\nifneq ($(strip $(CUSTOM_MATRIX)), yes)\n    ifeq ($(filter $(CUSTOM_MATRIX),$(VALID_CUSTOM_MATRIX_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid CUSTOM_MATRIX,CUSTOM_MATRIX=\"$(CUSTOM_MATRIX)\" is not a valid custom matrix type)\n    endif\n\n    # Include common stuff for all non custom matrix users\n    QUANTUM_SRC += $(QUANTUM_DIR)/matrix_common.c\n\n    # if 'lite' then skip the actual matrix implementation\n    ifneq ($(strip $(CUSTOM_MATRIX)), lite)\n        # Include the standard or split matrix code if needed\n        QUANTUM_SRC += $(QUANTUM_DIR)/matrix.c\n    endif\nendif\n\n# Debounce Modules. Set DEBOUNCE_TYPE=custom if including one manually.\nDEBOUNCE_TYPE ?= sym_defer_g\nifneq ($(strip $(DEBOUNCE_TYPE)), custom)\n    QUANTUM_SRC += $(QUANTUM_DIR)/debounce/$(strip $(DEBOUNCE_TYPE)).c\nendif\n\n\nVALID_SERIAL_DRIVER_TYPES := bitbang usart vendor\n\nSERIAL_DRIVER ?= bitbang\nifeq ($(filter $(SERIAL_DRIVER),$(VALID_SERIAL_DRIVER_TYPES)),)\n    $(call CATASTROPHIC_ERROR,Invalid SERIAL_DRIVER,SERIAL_DRIVER=\"$(SERIAL_DRIVER)\" is not a valid SERIAL driver)\nendif\n\nifeq ($(strip $(SPLIT_KEYBOARD)), yes)\n    POST_CONFIG_H += $(QUANTUM_DIR)/split_common/post_config.h\n    OPT_DEFS += -DSPLIT_KEYBOARD\n    CRC_ENABLE := yes\n\n    # Include files used by all split keyboards\n    QUANTUM_SRC += $(QUANTUM_DIR)/split_common/split_util.c\n\n    # Determine which (if any) transport files are required\n    ifneq ($(strip $(SPLIT_TRANSPORT)), custom)\n        QUANTUM_SRC += $(QUANTUM_DIR)/split_common/transport.c \\\n                       $(QUANTUM_DIR)/split_common/transactions.c\n\n        OPT_DEFS += -DSPLIT_COMMON_TRANSACTIONS\n\n        # Functions added via QUANTUM_LIB_SRC are only included in the final binary if they're called.\n        # Unused functions are pruned away, which is why we can add multiple drivers here without bloat.\n        ifeq ($(PLATFORM),AVR)\n            ifneq ($(NO_I2C),yes)\n                QUANTUM_LIB_SRC += i2c_master.c \\\n                                   i2c_slave.c\n            endif\n        endif\n\n        OPT_DEFS += -DSERIAL_DRIVER_$(strip $(shell echo $(SERIAL_DRIVER) | tr '[:lower:]' '[:upper:]'))\n        ifeq ($(strip $(SERIAL_DRIVER)), bitbang)\n            QUANTUM_LIB_SRC += serial.c\n        else\n            QUANTUM_LIB_SRC += serial_protocol.c\n            QUANTUM_LIB_SRC += serial_$(strip $(SERIAL_DRIVER)).c\n        endif\n    endif\n    COMMON_VPATH += $(QUANTUM_PATH)/split_common\nendif\n\nifeq ($(strip $(FNV_ENABLE)), yes)\n    OPT_DEFS += -DFNV_ENABLE\n    VPATH += $(LIB_PATH)/fnv\n    SRC += qmk_fnv_type_validation.c hash_32a.c hash_64a.c\nendif\n\nifeq ($(strip $(LIB8TION_ENABLE)), yes)\n    ifneq (,$(filter $(MCU), atmega16u2 atmega32u2 at90usb162))\n        # ATmegaxxU2 does not have hardware MUL instruction - lib8tion must be told to use software multiplication routines\n        OPT_DEFS += -DLIB8_ATTINY\n    endif\n    OPT_DEFS += -DFASTLED_SCALE8_FIXED=1 -DFASTLED_BLEND_FIXED=1\n    SRC += $(LIB_PATH)/lib8tion/lib8tion.c\nendif\n\nVALID_HAPTIC_DRIVER_TYPES := drv2605l solenoid\nifeq ($(strip $(HAPTIC_ENABLE)),yes)\n    ifeq ($(filter $(HAPTIC_DRIVER),$(VALID_HAPTIC_DRIVER_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid HAPTIC_DRIVER,HAPTIC_DRIVER=\"$(HAPTIC_DRIVER)\" is not a valid Haptic driver)\n    else\n        OPT_DEFS += -DHAPTIC_$(strip $(shell echo $(HAPTIC_DRIVER) | tr '[:lower:]' '[:upper:]'))\n        COMMON_VPATH += $(DRIVER_PATH)/haptic\n\n        ifeq ($(strip $(HAPTIC_DRIVER)), drv2605l)\n            I2C_DRIVER_REQUIRED = yes\n            SRC += drv2605l.c\n        endif\n\n        ifeq ($(strip $(HAPTIC_DRIVER)), solenoid)\n            SRC += solenoid.c\n        endif\n    endif\nendif\n\nifeq ($(strip $(HD44780_ENABLE)), yes)\n    OPT_DEFS += -DHD44780_ENABLE\n    COMMON_VPATH += $(DRIVER_PATH)/lcd\n    SRC += hd44780.c\nendif\n\nVALID_OLED_DRIVER_TYPES := custom ssd1306\nOLED_DRIVER ?= ssd1306\nVALID_OLED_TRANSPORT_TYPES := i2c spi custom\nOLED_TRANSPORT ?= i2c\nifeq ($(strip $(OLED_ENABLE)), yes)\n    ifeq ($(filter $(OLED_DRIVER),$(VALID_OLED_DRIVER_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid OLED_DRIVER,OLED_DRIVER=\"$(OLED_DRIVER)\" is not a valid OLED driver)\n    else\n        ifeq ($(filter $(OLED_TRANSPORT),$(VALID_OLED_TRANSPORT_TYPES)),)\n            $(call CATASTROPHIC_ERROR,Invalid OLED_TRANSPORT,OLED_TRANSPORT=\"$(OLED_TRANSPORT)\" is not a valid OLED transport)\n        else\n            OPT_DEFS += -DOLED_ENABLE\n            OPT_DEFS += -DOLED_$(strip $(shell echo $(OLED_DRIVER) | tr '[:lower:]' '[:upper:]'))\n            COMMON_VPATH += $(DRIVER_PATH)/oled\n            ifneq ($(strip $(OLED_DRIVER)), custom)\n                SRC += oled_driver.c\n            endif\n\n            OPT_DEFS += -DOLED_TRANSPORT_$(strip $(shell echo $(OLED_TRANSPORT) | tr '[:lower:]' '[:upper:]'))\n            ifeq ($(strip $(OLED_TRANSPORT)), i2c)\n                I2C_DRIVER_REQUIRED = yes\n            endif\n            ifeq ($(strip $(OLED_TRANSPORT)), spi)\n                SPI_DRIVER_REQUIRED = yes\n            endif\n        endif\n    endif\nendif\n\nifeq ($(strip $(ST7565_ENABLE)), yes)\n    OPT_DEFS += -DST7565_ENABLE\n    SPI_DRIVER_REQUIRED = yes\n    COMMON_VPATH += $(DRIVER_PATH)/oled # For glcdfont.h\n    COMMON_VPATH += $(DRIVER_PATH)/lcd\n    SRC += st7565.c\nendif\n\nifeq ($(strip $(UCIS_ENABLE)), yes)\n    OPT_DEFS += -DUCIS_ENABLE\n    UNICODE_COMMON := yes\n    SRC += $(QUANTUM_DIR)/process_keycode/process_ucis.c \\\n           $(QUANTUM_DIR)/unicode/ucis.c\nendif\n\nifeq ($(strip $(UNICODEMAP_ENABLE)), yes)\n    OPT_DEFS += -DUNICODEMAP_ENABLE\n    UNICODE_COMMON := yes\n    SRC += $(QUANTUM_DIR)/process_keycode/process_unicodemap.c \\\n           $(QUANTUM_DIR)/unicode/unicodemap.c\nendif\n\nifeq ($(strip $(UNICODE_ENABLE)), yes)\n    OPT_DEFS += -DUNICODE_ENABLE\n    UNICODE_COMMON := yes\n    SRC += $(QUANTUM_DIR)/process_keycode/process_unicode.c\nendif\n\nifeq ($(strip $(UNICODE_COMMON)), yes)\n    OPT_DEFS += -DUNICODE_COMMON_ENABLE\n    COMMON_VPATH += $(QUANTUM_DIR)/unicode\n    SRC += $(QUANTUM_DIR)/process_keycode/process_unicode_common.c \\\n           $(QUANTUM_DIR)/unicode/unicode.c \\\n           $(QUANTUM_DIR)/unicode/utf8.c\nendif\n\nifeq ($(strip $(PS2_MOUSE_ENABLE)), yes)\n    PS2_ENABLE := yes\n    MOUSE_ENABLE := yes\n    SRC += ps2_mouse.c\n    OPT_DEFS += -DPS2_MOUSE_ENABLE\nendif\n\nVALID_PS2_DRIVER_TYPES := busywait interrupt usart vendor\n\nPS2_DRIVER ?= busywait\nifeq ($(strip $(PS2_ENABLE)), yes)\n    ifeq ($(filter $(PS2_DRIVER),$(VALID_PS2_DRIVER_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid PS2_DRIVER,PS2_DRIVER=\"$(PS2_DRIVER)\" is not a valid PS/2 driver)\n    endif\n\n    OPT_DEFS += -DPS2_DRIVER_$(strip $(shell echo $(PS2_DRIVER) | tr '[:lower:]' '[:upper:]'))\n\n    COMMON_VPATH += $(DRIVER_PATH)/ps2\n    COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/ps2\n    OPT_DEFS += -DPS2_ENABLE\n\n    ifneq ($(strip $(PS2_DRIVER)), vendor)\n        SRC += ps2_io.c\n    endif\n\n    SRC += ps2_$(strip $(PS2_DRIVER)).c\nendif\n\nJOYSTICK_ENABLE ?= no\nVALID_JOYSTICK_TYPES := analog digital\nJOYSTICK_DRIVER ?= analog\nifeq ($(strip $(JOYSTICK_ENABLE)), yes)\n    ifeq ($(filter $(JOYSTICK_DRIVER),$(VALID_JOYSTICK_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid JOYSTICK_DRIVER,JOYSTICK_DRIVER=\"$(JOYSTICK_DRIVER)\" is not a valid joystick driver)\n    endif\n    OPT_DEFS += -DJOYSTICK_ENABLE\n    OPT_DEFS += -DJOYSTICK_$(strip $(shell echo $(JOYSTICK_DRIVER) | tr '[:lower:]' '[:upper:]'))\n    SRC += $(QUANTUM_DIR)/process_keycode/process_joystick.c\n    SRC += $(QUANTUM_DIR)/joystick.c\n\n    ifeq ($(strip $(JOYSTICK_DRIVER)), analog)\n        ANALOG_DRIVER_REQUIRED = yes\n    endif\nendif\n\nUSBPD_ENABLE ?= no\nVALID_USBPD_DRIVER_TYPES = custom vendor\nUSBPD_DRIVER ?= vendor\nifeq ($(strip $(USBPD_ENABLE)), yes)\n    ifeq ($(filter $(strip $(USBPD_DRIVER)),$(VALID_USBPD_DRIVER_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid USBPD_DRIVER,USBPD_DRIVER=\"$(USBPD_DRIVER)\" is not a valid USBPD driver)\n    else\n        OPT_DEFS += -DUSBPD_ENABLE\n        ifeq ($(strip $(USBPD_DRIVER)), vendor)\n            # Vendor-specific implementations\n            OPT_DEFS += -DUSBPD_VENDOR\n            ifeq ($(strip $(MCU_SERIES)), STM32G4xx)\n                OPT_DEFS += -DUSBPD_STM32G4\n                SRC += usbpd_stm32g4.c\n            else\n                $(call CATASTROPHIC_ERROR,Invalid USBPD_DRIVER,There is no vendor-provided USBPD driver available)\n            endif\n        else ifeq ($(strip $(USBPD_DRIVER)), custom)\n            OPT_DEFS += -DUSBPD_CUSTOM\n            # Board designers can add their own driver to $(SRC)\n        endif\n    endif\nendif\n\nBLUETOOTH_ENABLE ?= no\nVALID_BLUETOOTH_DRIVER_TYPES := bluefruit_le custom rn42\nifeq ($(strip $(BLUETOOTH_ENABLE)), yes)\n    ifeq ($(filter $(strip $(BLUETOOTH_DRIVER)),$(VALID_BLUETOOTH_DRIVER_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid BLUETOOTH_DRIVER,BLUETOOTH_DRIVER=\"$(BLUETOOTH_DRIVER)\" is not a valid Bluetooth driver type)\n    endif\n    OPT_DEFS += -DBLUETOOTH_ENABLE\n    OPT_DEFS += -DBLUETOOTH_$(strip $(shell echo $(BLUETOOTH_DRIVER) | tr '[:lower:]' '[:upper:]'))\n    NO_USB_STARTUP_CHECK := yes\n    CONNECTION_ENABLE := yes\n    COMMON_VPATH += $(DRIVER_PATH)/bluetooth\n    SRC += $(DRIVER_PATH)/bluetooth/bluetooth.c\n\n    ifeq ($(strip $(BLUETOOTH_DRIVER)), bluefruit_le)\n        SPI_DRIVER_REQUIRED = yes\n        SRC += $(DRIVER_PATH)/bluetooth/bluetooth_drivers.c\n        SRC += $(DRIVER_PATH)/bluetooth/bluefruit_le.cpp\n    endif\n\n    ifeq ($(strip $(BLUETOOTH_DRIVER)), rn42)\n        UART_DRIVER_REQUIRED = yes\n        SRC += $(DRIVER_PATH)/bluetooth/bluetooth_drivers.c\n        SRC += $(DRIVER_PATH)/bluetooth/rn42.c\n    endif\nendif\n\nENCODER_ENABLE ?= no\nENCODER_DRIVER ?= quadrature\nVALID_ENCODER_DRIVER_TYPES := quadrature custom\nifeq ($(strip $(ENCODER_ENABLE)), yes)\n    ifeq ($(filter $(ENCODER_DRIVER),$(VALID_ENCODER_DRIVER_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid ENCODER_DRIVER,ENCODER_DRIVER=\"$(ENCODER_DRIVER)\" is not a valid encoder driver)\n    endif\n    SRC += $(QUANTUM_DIR)/encoder.c\n    OPT_DEFS += -DENCODER_ENABLE\n    OPT_DEFS += -DENCODER_DRIVER_$(strip $(shell echo $(ENCODER_DRIVER) | tr '[:lower:]' '[:upper:]'))\n\n    COMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/encoder\n    COMMON_VPATH += $(DRIVER_PATH)/encoder\n\n    ifneq ($(strip $(ENCODER_DRIVER)), custom)\n        SRC += encoder_$(strip $(ENCODER_DRIVER)).c\n    endif\n\n    ifeq ($(strip $(ENCODER_MAP_ENABLE)), yes)\n        OPT_DEFS += -DENCODER_MAP_ENABLE\n    endif\nendif\n\nifeq ($(strip $(DIP_SWITCH_ENABLE)), yes)\n    ifeq ($(strip $(DIP_SWITCH_MAP_ENABLE)), yes)\n        OPT_DEFS += -DDIP_SWITCH_MAP_ENABLE\n    endif\nendif\n\nVALID_BATTERY_DRIVER_TYPES := adc custom vendor\n\nBATTERY_DRIVER ?= adc\nifeq ($(strip $(BATTERY_DRIVER_REQUIRED)), yes)\n    ifeq ($(filter $(BATTERY_DRIVER),$(VALID_BATTERY_DRIVER_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid BATTERY_DRIVER,BATTERY_DRIVER=\"$(BATTERY_DRIVER)\" is not a valid battery driver)\n    endif\n\n    OPT_DEFS += -DBATTERY_DRIVER\n    OPT_DEFS += -DBATTERY_$(strip $(shell echo $(BATTERY_DRIVER) | tr '[:lower:]' '[:upper:]'))\n\n    COMMON_VPATH += $(DRIVER_PATH)/battery\n\n    SRC += battery.c\n    SRC += battery_$(strip $(BATTERY_DRIVER)).c\n\n    # add extra deps\n    ifeq ($(strip $(BATTERY_DRIVER)), adc)\n        ANALOG_DRIVER_REQUIRED = yes\n    endif\nendif\n\nVALID_WS2812_DRIVER_TYPES := bitbang custom i2c pwm spi vendor\n\nWS2812_DRIVER ?= bitbang\nifeq ($(strip $(WS2812_DRIVER_REQUIRED)), yes)\n    ifeq ($(filter $(WS2812_DRIVER),$(VALID_WS2812_DRIVER_TYPES)),)\n        $(call CATASTROPHIC_ERROR,Invalid WS2812_DRIVER,WS2812_DRIVER=\"$(WS2812_DRIVER)\" is not a valid WS2812 driver)\n    endif\n\n    OPT_DEFS += -DWS2812_$(strip $(shell echo $(WS2812_DRIVER) | tr '[:lower:]' '[:upper:]'))\n\n    COMMON_VPATH += $(DRIVER_PATH)/led\n\n    SRC += ws2812.c ws2812_$(strip $(WS2812_DRIVER)).c\n\n    ifeq ($(strip $(PLATFORM)), CHIBIOS)\n        ifeq ($(strip $(WS2812_DRIVER)), pwm)\n            OPT_DEFS += -DSTM32_DMA_REQUIRED=TRUE\n        endif\n    endif\n\n    # add extra deps\n    ifeq ($(strip $(WS2812_DRIVER)), i2c)\n        I2C_DRIVER_REQUIRED = yes\n    endif\nendif\n\nifeq ($(strip $(APA102_DRIVER_REQUIRED)), yes)\n    COMMON_VPATH += $(DRIVER_PATH)/led\n    SRC += apa102.c\nendif\n\nifeq ($(strip $(ANALOG_DRIVER_REQUIRED)), yes)\n    OPT_DEFS += -DHAL_USE_ADC=TRUE\n    QUANTUM_LIB_SRC += analog.c\nendif\n\nifeq ($(strip $(I2C_DRIVER_REQUIRED)), yes)\n    OPT_DEFS += -DHAL_USE_I2C=TRUE\n    QUANTUM_LIB_SRC += i2c_master.c\nendif\n\nifeq ($(strip $(SPI_DRIVER_REQUIRED)), yes)\n    OPT_DEFS += -DHAL_USE_SPI=TRUE\n    QUANTUM_LIB_SRC += spi_master.c\nendif\n\nifeq ($(strip $(UART_DRIVER_REQUIRED)), yes)\n    ifeq ($(strip $(PLATFORM)), CHIBIOS)\n        ifneq ($(filter $(MCU_SERIES),RP2040),)\n            OPT_DEFS += -DHAL_USE_SIO=TRUE\n            QUANTUM_LIB_SRC += uart_sio.c\n        else\n            OPT_DEFS += -DHAL_USE_SERIAL=TRUE\n            QUANTUM_LIB_SRC += uart_serial.c\n        endif\n    else\n        QUANTUM_LIB_SRC += uart.c\n    endif\nendif\n\n\nifeq ($(strip $(ACHORDION_ENABLE)), yes)\n    OPT_DEFS += -DACHORDION_ENABLE -DPERMISSIVE_HOLD\n    SRC += $(QUANTUM_DIR)/process_keycode/process_achordion.c\nendif\n"
  },
  {
    "path": "builddefs/common_rules.mk",
    "content": "# Hey Emacs, this is a -*- makefile -*-\n#----------------------------------------------------------------------------\n\n# Enable vpath searching for source files only\n# Without this, output files, could be read from the wrong .build directories\nVPATH_SRC := $(VPATH)\nvpath %.c $(VPATH_SRC)\nvpath %.h $(VPATH_SRC)\nvpath %.cpp $(VPATH_SRC)\nvpath %.cc $(VPATH_SRC)\nvpath %.hpp $(VPATH_SRC)\nvpath %.S $(VPATH_SRC)\nVPATH :=\n\n# Helper to return the distinct elements of a list\nuniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))\n\n# Convert all SRC to OBJ\ndefine OBJ_FROM_SRC\n$(patsubst %.c,$1/%.o,$(patsubst %.cpp,$1/%.o,$(patsubst %.cc,$1/%.o,$(patsubst %.S,$1/%.o,$(patsubst %.clib,$1/%.a,$($1_SRC))))))\nendef\n$(foreach OUTPUT,$(OUTPUTS),$(eval $(OUTPUT)_OBJ +=$(call OBJ_FROM_SRC,$(OUTPUT))))\n\n# Define a list of all objects\nOBJ := $(foreach OUTPUT,$(OUTPUTS),$($(OUTPUT)_OBJ))\nNO_LTO_OBJ := $(filter %.a,$(OBJ))\n\nMASTER_OUTPUT := $(firstword $(OUTPUTS))\n\n# Output format. (can be srec, ihex, binary)\nFORMAT = ihex\n\n# Optimization level, can be [0, 1, 2, 3, s].\nOPT ?= s\n\n# Compiler flag to set the C and C++ language standard level\nCSTANDARD = -std=gnu11\nCXXSTANDARD = -std=gnu++14\n\n# Speed up recompilations by opt-in usage of ccache\nUSE_CCACHE ?= no\nifneq ($(USE_CCACHE),no)\n    CC_PREFIX ?= ccache\nendif\n\n#---------------- Debug Options ----------------\n\nDEBUG_ENABLE ?= no\nifeq ($(strip $(DEBUG_ENABLE)),yes)\n\tCFLAGS \t += -ggdb3\n\tCXXFLAGS += -ggdb3\n\tASFLAGS  += -ggdb3\n# Create a map file when debugging\n\tLDFLAGS  += -Wl,-Map=$(BUILD_DIR)/$(TARGET).map,--cref\nendif\n\n\n#---------------- C Compiler Options ----------------\n\nifeq ($(strip $(LTO_ENABLE)), yes)\n    CDEFS += -flto\n    CDEFS += -DLTO_ENABLE\nendif\n\nCFLAGS += $(CDEFS)\nCFLAGS += -O$(OPT)\n# add color\nifeq ($(COLOR),true)\nifeq (\"$(shell echo \"int main(){}\" | $(CC) -fdiagnostics-color -x c - -o /dev/null 2>&1)\", \"\")\n\tCFLAGS+= -fdiagnostics-color\nendif\nendif\nCFLAGS += -Wall\nCFLAGS += -Wstrict-prototypes\nifneq ($(strip $(ALLOW_WARNINGS)), yes)\n    CFLAGS += -Werror\nendif\nCFLAGS += $(CSTANDARD)\n\n# This fixes lots of keyboards linking errors but SHOULDN'T BE A FINAL SOLUTION\n# Fixing of multiple variable definitions must be made.\nCFLAGS += -fcommon\n\n#---------------- C++ Compiler Options ----------------\n\nCXXFLAGS += $(CXXDEFS)\nCXXFLAGS += -O$(OPT)\n# to suppress \"warning: only initialized variables can be placed into program memory area\"\nCXXFLAGS += -w\nCXXFLAGS += -Wall\nCXXFLAGS += -Wundef\n\nifneq ($(strip $(ALLOW_WARNINGS)), yes)\n    CXXFLAGS += -Werror\nendif\n\n#---------------- Assembler Options ----------------\n\nASFLAGS += $(ADEFS)\nifeq ($(VERBOSE_AS_CMD),yes)\n\tASFLAGS += -v\nendif\n\n#---------------- Linker Options ----------------\n\nifeq ($(VERBOSE_LD_CMD),yes)\n\tLDFLAGS += -v\nendif\n\nLDFLAGS += $(EXTMEMOPTS)\nLDFLAGS += $(patsubst %,-L%,$(EXTRALIBDIRS))\nLDFLAGS += -lm\n# You can give EXTRALDFLAGS at 'make' command line.\nLDFLAGS += $(EXTRALDFLAGS)\n\n#---------------- Assembler Listings ----------------\n\nADHLNS_ENABLE ?= no\nifeq ($(ADHLNS_ENABLE),yes)\n  # Avoid \"Options to '-Xassembler' do not match\" - only specify assembler options at LTO link time\n  ifeq ($(strip $(LTO_ENABLE)), yes)\n    LDFLAGS  += -Wa,-adhlns=$(BUILD_DIR)/$(TARGET).lst\n  else\n    CFLAGS   += -Wa,-adhlns=$(@:%.o=%.lst)\n    CXXFLAGS += -Wa,-adhlns=$(@:%.o=%.lst)\n\tASFLAGS  += -Wa,-adhlns=$(@:%.o=%.lst),--listing-cont-lines=100\n  endif\nendif\n\n# Define programs and commands.\nSHELL = sh\nSED = sed\nREMOVE = rm -f\nREMOVEDIR = rmdir\nCOPY = cp\nWINSHELL = cmd\nSECHO = $(SILENT) || echo\nMD5SUM ?= md5sum\nifneq ($(filter Darwin FreeBSD,$(shell uname -s)),)\n  MD5SUM = md5\nendif\n\n# UF2 format settings\n# To produce a UF2 file in your build, add to your keyboard's rules.mk:\n#      FIRMWARE_FORMAT = uf2\nUF2CONV = $(TOP_DIR)/util/uf2conv.py\nUF2CONV_ARGS ?=\nUF2_FAMILY ?= 0x0\n\n# Compiler flags to generate dependency files.\n#GENDEPFLAGS = -MMD -MP -MF .dep/$(@F).d\nGENDEPFLAGS = -MMD -MP -MF $(patsubst %.o,%.td,$@)\n\n\n# Combine all necessary flags and optional flags.\n# Add target processor to flags.\n# You can give extra flags at 'make' command line like: make EXTRAFLAGS=-DFOO=bar\nALL_CFLAGS = $(MCUFLAGS) $(CFLAGS) $(EXTRAFLAGS)\nALL_CXXFLAGS = $(MCUFLAGS) -x c++ $(CXXFLAGS) $(EXTRAFLAGS)\nALL_ASFLAGS = $(MCUFLAGS) -x assembler-with-cpp $(ASFLAGS) $(EXTRAFLAGS)\n\ndefine NO_LTO\n$(patsubst %.a,%.o,$1): NOLTO_CFLAGS += -fno-lto\nendef\n$(foreach LOBJ, $(NO_LTO_OBJ), $(eval $(call NO_LTO,$(LOBJ))))\n\nMOVE_DEP = mv -f $(patsubst %.o,%.td,$@) $(patsubst %.o,%.d,$@)\n\n# For a ChibiOS build, ensure that the board files have the hook overrides injected\ndefine BOARDSRC_INJECT_HOOKS\n$(INTERMEDIATE_OUTPUT)/$(patsubst %.c,%.o,$(patsubst ./%,%,$1)): FILE_SPECIFIC_CFLAGS += -include $(TOP_DIR)/tmk_core/protocol/chibios/init_hooks.h\nendef\n$(foreach LOBJ, $(BOARDSRC), $(eval $(call BOARDSRC_INJECT_HOOKS,$(LOBJ))))\n\n# Add QMK specific flags\nDFU_SUFFIX ?= dfu-suffix\nDFU_SUFFIX_ARGS ?=\n\n\nelf: $(BUILD_DIR)/$(TARGET).elf\nhex: $(BUILD_DIR)/$(TARGET).hex\nuf2: $(BUILD_DIR)/$(TARGET).uf2\ncpfirmware_qmk: $(FIRMWARE_FORMAT)\n\t$(SILENT) || printf \"Copying $(TARGET).$(FIRMWARE_FORMAT) to qmk_firmware folder\" | $(AWK_CMD)\n\t$(COPY) $(BUILD_DIR)/$(TARGET).$(FIRMWARE_FORMAT) $(TARGET).$(FIRMWARE_FORMAT) && $(PRINT_OK)\neep: $(BUILD_DIR)/$(TARGET).eep\nlss: $(BUILD_DIR)/$(TARGET).lss\nsym: $(BUILD_DIR)/$(TARGET).sym\nLIBNAME=lib$(TARGET).a\nlib: $(LIBNAME)\n\ncpfirmware: cpfirmware_qmk\n\nifneq ($(QMK_USERSPACE),)\ncpfirmware: cpfirmware_userspace\ncpfirmware_userspace: cpfirmware_qmk\n\t$(SILENT) || printf \"Copying $(TARGET).$(FIRMWARE_FORMAT) to userspace folder\" | $(AWK_CMD)\n\t$(COPY) $(BUILD_DIR)/$(TARGET).$(FIRMWARE_FORMAT) $(QMK_USERSPACE)/$(TARGET).$(FIRMWARE_FORMAT) && $(PRINT_OK)\nendif\n\n# Display size of file, modifying the output so people don't mistakenly grab the hex output\nBINARY_SIZE = $(SIZE) --target=$(FORMAT) $(BUILD_DIR)/$(TARGET).hex | $(SED) -e 's/\\.build\\/.*$$/$(TARGET).$(FIRMWARE_FORMAT)/g'\n\nsizebefore:\n\t@if test -f $(BUILD_DIR)/$(TARGET).hex; then $(SECHO) $(MSG_SIZE_BEFORE); $(SILENT) || $(BINARY_SIZE); \\\n\t2>/dev/null; $(SECHO); fi\n\nsizeafter: $(BUILD_DIR)/$(TARGET).hex\n\t@if test -f $(BUILD_DIR)/$(TARGET).hex; then $(SECHO); $(SECHO) $(MSG_SIZE_AFTER); $(SILENT) || $(BINARY_SIZE); \\\n\t2>/dev/null; $(SECHO); fi\n\n# Display compiler version information.\ngccversion :\n\t@$(SILENT) || $(CC) --version\n\n# Create final output files (.hex, .eep) from ELF output file.\n%.hex: %.elf\n\t$(eval CMD=$(HEX) $< $@)\n\t#@$(SILENT) || printf \"$(MSG_EXECUTING) '$(CMD)':\\n\"\n\t@$(SILENT) || printf \"$(MSG_FLASH) $@\" | $(AWK_CMD)\n\t@$(BUILD_CMD)\n\n%.uf2: %.elf\n\t$(eval CMD=$(HEX) $< $(BUILD_DIR)/$(TARGET).tmp && $(UF2CONV) $(UF2CONV_ARGS) $(BUILD_DIR)/$(TARGET).tmp --output $@ --convert --family $(UF2_FAMILY) >/dev/null 2>&1)\n\t#@$(SILENT) || printf \"$(MSG_EXECUTING) '$(CMD)':\\n\"\n\t@$(SILENT) || printf \"$(MSG_UF2) $@\" | $(AWK_CMD)\n\t@$(BUILD_CMD)\n\n%.eep: %.elf\n\t$(eval CMD=$(EEP) $< $@ || exit 0)\n\t#@$(SILENT) || printf \"$(MSG_EXECUTING) '$(CMD)':\\n\"\n\t@$(SILENT) || printf \"$(MSG_EEPROM) $@\" | $(AWK_CMD)\n\t@$(BUILD_CMD)\n\n# Create extended listing file from ELF output file.\n%.lss: %.elf\n\t$(eval CMD=$(OBJDUMP) -h -S -z $< > $@)\n\t#@$(SILENT) || printf \"$(MSG_EXECUTING) '$(CMD)':\\n\"\n\t@$(SILENT) || printf \"$(MSG_EXTENDED_LISTING) $@\" | $(AWK_CMD)\n\t@$(BUILD_CMD)\n\n# Create a symbol table from ELF output file.\n%.sym: %.elf\n\t$(eval CMD=$(NM) -n $< > $@ )\n\t#@$(SILENT) || printf \"$(MSG_EXECUTING) '$(CMD)':\\n\"\n\t@$(SILENT) || printf \"$(MSG_SYMBOL_TABLE) $@\" | $(AWK_CMD)\n\t@$(BUILD_CMD)\n\n%.bin: %.elf\n\t$(eval CMD=$(BIN) $< $@ || exit 0)\n\t#@$(SILENT) || printf \"$(MSG_EXECUTING) '$(CMD)':\\n\"\n\t@$(SILENT) || printf \"$(MSG_BIN) $@\" | $(AWK_CMD)\n\t@$(BUILD_CMD)\n\tif [ ! -z \"$(DFU_SUFFIX_ARGS)\" ]; then \\\n\t\t$(DFU_SUFFIX) $(DFU_SUFFIX_ARGS) -a $(BUILD_DIR)/$(TARGET).bin 1>/dev/null ;\\\n\tfi\n\t#$(SILENT) || printf \"$(MSG_EXECUTING) '$(DFU_SUFFIX) $(DFU_SUFFIX_ARGS) -a $(BUILD_DIR)/$(TARGET).bin 1>/dev/null':\\n\" ;\\\n\t$(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin;\n\nBEGIN = gccversion sizebefore\n\n# Link: create ELF output file from object files.\n.SECONDARY : $(BUILD_DIR)/$(TARGET).elf\n.PRECIOUS : $(OBJ)\n# Note the obj.txt depeendency is there to force linking if a source file is deleted\n%.elf: $(OBJ) $(MASTER_OUTPUT)/cflags.txt $(MASTER_OUTPUT)/ldflags.txt $(MASTER_OUTPUT)/obj.txt | $(BEGIN)\n\t@$(SILENT) || printf \"$(MSG_LINKING) $@\" | $(AWK_CMD)\n\t$(eval CMD=MAKE=$(MAKE) $(CC) $(ALL_CFLAGS) $(call uniq,$(OBJ)) --output $@ $(LDFLAGS))\n\t@$(BUILD_CMD)\n\n\ndefine GEN_OBJRULE\n$1_INCFLAGS := $$(patsubst %,-I%,$$($1_INC))\nifdef $1_CONFIG\n$1_CONFIG_FLAGS += $$(patsubst %,-include %,$$($1_CONFIG))\nendif\n$1_CFLAGS = $$(ALL_CFLAGS) $$($1_DEFS) $$($1_INCFLAGS) $$($1_CONFIG_FLAGS) $$(NOLTO_CFLAGS)\n$1_CXXFLAGS = $$(ALL_CXXFLAGS) $$($1_DEFS) $$($1_INCFLAGS) $$($1_CONFIG_FLAGS) $$(NOLTO_CFLAGS)\n$1_ASFLAGS = $$(ALL_ASFLAGS) $$($1_DEFS) $$($1_INCFLAGS) $$($1_CONFIG_FLAGS)\n\n# Compile: create object files from C source files.\n$1/%.o : %.c $1/%.d $1/cflags.txt $1/compiler.txt | $(BEGIN)\n\t@mkdir -p $$(@D)\n\t@$$(SILENT) || printf \"$$(MSG_COMPILING) $$<\" | $$(AWK_CMD)\n\t$$(eval CC_EXEC := $$(CC))\n    ifneq ($$(VERBOSE_C_CMD),)\n\t$$(if $$(filter $$(notdir $$(VERBOSE_C_CMD)),$$(notdir $$<)),$$(eval CC_EXEC += -v))\n    endif\n    ifneq ($$(VERBOSE_C_INCLUDE),)\n\t$$(if $$(filter $$(notdir $$(VERBOSE_C_INCLUDE)),$$(notdir $$<)),$$(eval CC_EXEC += -H))\n    endif\n\t$$(eval CMD := $$(CC_EXEC) -c $$($1_CFLAGS) $$(FILE_SPECIFIC_CFLAGS) $$(GENDEPFLAGS) $$< -o $$@ && $$(MOVE_DEP))\n\t@$$(BUILD_CMD)\n    ifneq ($$(DUMP_C_MACROS),)\n\t$$(eval CMD := $$(CC) -E -dM $$($1_CFLAGS) $$(FILE_SPECIFIC_CFLAGS) $$(GENDEPFLAGS) $$<)\n\t@$$(if $$(filter $$(notdir $$(DUMP_C_MACROS)),$$(notdir $$<)),$$(BUILD_CMD))\n    endif\n\n# Compile: create object files from C++ source files.\n$1/%.o : %.cpp $1/%.d $1/cxxflags.txt $1/compiler.txt | $(BEGIN)\n\t@mkdir -p $$(@D)\n\t@$$(SILENT) || printf \"$$(MSG_COMPILING_CXX) $$<\" | $$(AWK_CMD)\n\t$$(eval CMD=$$(CC) -c $$($1_CXXFLAGS) $$(FILE_SPECIFIC_CFLAGS) $$(GENDEPFLAGS) $$< -o $$@ && $$(MOVE_DEP))\n\t@$$(BUILD_CMD)\n\n$1/%.o : %.cc $1/%.d $1/cxxflags.txt $1/compiler.txt | $(BEGIN)\n\t@mkdir -p $$(@D)\n\t@$$(SILENT) || printf \"$$(MSG_COMPILING_CXX) $$<\" | $$(AWK_CMD)\n\t$$(eval CMD=$$(CC) -c $$($1_CXXFLAGS) $$(FILE_SPECIFIC_CFLAGS) $$(GENDEPFLAGS) $$< -o $$@ && $$(MOVE_DEP))\n\t@$$(BUILD_CMD)\n\n# Assemble: create object files from assembler source files.\n$1/%.o : %.S $1/asflags.txt $1/compiler.txt | $(BEGIN)\n\t@mkdir -p $$(@D)\n\t@$(SILENT) || printf \"$$(MSG_ASSEMBLING) $$<\" | $$(AWK_CMD)\n\t$$(eval CMD=$$(CC) -c $$($1_ASFLAGS) $$< -o $$@)\n\t@$$(BUILD_CMD)\n\n$1/%.a : $1/%.o\n\t@mkdir -p $$(@D)\n\t@$(SILENT) || printf \"Archiving: $$<\" | $$(AWK_CMD)\n\t$$(eval CMD=$$(AR) rcs $$@ $$<)\n\t@$$(BUILD_CMD)\n\n$1/force:\n\n$1/cflags.txt: $1/force\n\techo '$$($1_CFLAGS)' | cmp -s - $$@ || echo '$$($1_CFLAGS)' > $$@\n\n$1/cxxflags.txt: $1/force\n\techo '$$($1_CXXFLAGS)' | cmp -s - $$@ || echo '$$($1_CXXFLAGS)' > $$@\n\n$1/asflags.txt: $1/force\n\techo '$$($1_ASFLAGS)' | cmp -s - $$@ || echo '$$($1_ASFLAGS)' > $$@\n\n$1/compiler.txt: $1/force\n\ttest -f $$@ || touch $$@\n\t$$(CC) --version | cmp -s - $$@ || $$(CC) --version > $$@\nendef\n\n.PRECIOUS: $(MASTER_OUTPUT)/obj.txt\n$(MASTER_OUTPUT)/obj.txt: $(MASTER_OUTPUT)/force\n\techo '$(OBJ)' | cmp -s - $@ || echo '$(OBJ)' > $@\n\n.PRECIOUS: $(MASTER_OUTPUT)/ldflags.txt\n$(MASTER_OUTPUT)/ldflags.txt: $(MASTER_OUTPUT)/force\n\techo '$(LDFLAGS)' | cmp -s - $@ || echo '$(LDFLAGS)' > $@\n\n\n# We have to use static rules for the .d files for some reason\nDEPS = $(patsubst %.o,%.d,$(patsubst %.a,%.o,$(OBJ)))\n# Keep the .d files\n.PRECIOUS: $(DEPS)\n# Empty rule to force recompilation if the .d file is missing\n$(DEPS):\n\n\n$(foreach OUTPUT,$(OUTPUTS),$(eval $(call GEN_OBJRULE,$(OUTPUT))))\n\n# Create preprocessed source for use in sending a bug report.\n%.i : %.c | $(BEGIN)\n\t$(CC) -E -mmcu=$(MCU) $(CFLAGS) $< -o $@\n\n# Target: clean project.\nclean:\n\t$(foreach OUTPUT,$(OUTPUTS), $(REMOVE) -r $(OUTPUT) 2>/dev/null)\n\t$(REMOVE) $(BUILD_DIR)/$(TARGET).*\n\nshow_path:\n\t@echo VPATH=$(VPATH)\n\t@echo SRC=$(SRC)\n\t@echo OBJ=$(OBJ)\n\ndump_vars: ERROR_IF_EMPTY=\"\"\ndump_vars: ERROR_IF_NONBOOL=\"\"\ndump_vars: ERROR_IF_UNSET=\"\"\ndump_vars: CATASTROPHIC_ERROR=\"\"\ndump_vars:\n\t@$(foreach V,$(sort $(.VARIABLES)),$(if $(filter-out environment% default automatic,$(origin $V)),$(info $V=$($V))))\n\nobjs-size:\n\tfor i in $(OBJ); do echo $$i; done | sort | xargs $(SIZE)\n\n\n# size check optionally implemented in its platform.mk\ncheck-size:\n\ncheck-md5:\n\t$(MD5SUM) $(BUILD_DIR)/$(TARGET).$(FIRMWARE_FORMAT)\n\n# Create build directory\n$(shell mkdir -p $(BUILD_DIR) 2>/dev/null)\n\n# Create object files directory\n$(eval $(foreach OUTPUT,$(OUTPUTS),$(shell mkdir -p $(OUTPUT) 2>/dev/null)))\n\n# Include the dependency files.\n-include $(patsubst %.o,%.d,$(patsubst %.a,%.o,$(OBJ)))\n\n\n# Listing of phony targets.\n.PHONY : all dump_vars finish sizebefore sizeafter qmkversion \\\ngccversion build elf hex uf2 eep lss sym coff extcoff \\\nclean clean_list debug gdb-config show_path \\\nprogram teensy dfu dfu-ee dfu-start \\\nflash dfu-split-left dfu-split-right \\\navrdude-split-left avrdude-split-right \\\navrdude-loop usbasp\n"
  },
  {
    "path": "builddefs/converters.mk",
    "content": "ifneq (,$(filter $(MCU),atmega32u4))\n    # TODO: opt in rather than assume everything uses a pro micro\n    PIN_COMPATIBLE ?= promicro\nendif\n\n# Remove whitespace from any rule.mk provided vars\n#   - env cannot be overwritten but cannot have whitespace anyway\nCONVERT_TO:=$(strip $(CONVERT_TO))\nifneq ($(CONVERT_TO),)\n\n    # stash so we can overwrite env provided vars if needed\n    ACTIVE_CONVERTER=$(CONVERT_TO)\n\n    ifeq ($(PIN_COMPATIBLE),)\n        $(call CATASTROPHIC_ERROR,Converting to '$(CONVERT_TO)' not possible!)\n    endif\n\n    # glob to search each platfrorm and/or check for valid converter\n    CONVERTER := $(wildcard $(PLATFORM_PATH)/*/converters/$(PIN_COMPATIBLE)_to_$(CONVERT_TO)/)\n    ifeq ($(CONVERTER),)\n        $(call CATASTROPHIC_ERROR,Converting from '$(PIN_COMPATIBLE)' to '$(CONVERT_TO)' not possible!)\n    endif\n\n    -include $(CONVERTER)/pre_converter.mk\n\n    PLATFORM_KEY = $(shell echo $(CONVERTER) | cut -d \"/\" -f2)\n\n    # Configure any defaults\n    OPT_DEFS += -DCONVERT_TO_$(shell echo $(CONVERT_TO) | tr '[:lower:]' '[:upper:]')\n    OPT_DEFS += -DCONVERTER_TARGET=\\\"$(CONVERT_TO)\\\"\n    OPT_DEFS += -DCONVERTER_ENABLED\n    VPATH += $(CONVERTER)\n\n    # Configure for \"alias\" - worst case it produces an idential define\n    OPT_DEFS += -DCONVERT_TO_$(shell echo $(ACTIVE_CONVERTER) | tr '[:lower:]' '[:upper:]')\n\n    # Finally run any converter specific logic\n    include $(CONVERTER)/converter.mk\nendif\n"
  },
  {
    "path": "builddefs/disable_features.mk",
    "content": "# Unconditionally disable features that a keyboard advertises it doesn't support\n\nFEATURE_NAMES :=\nFEATURE_NAMES += AUDIO\nFEATURE_NAMES += BACKLIGHT\nFEATURE_NAMES += BLUETOOTH\nFEATURE_NAMES += DIP_SWITCH\nFEATURE_NAMES += DYNAMIC_KEYMAP\nFEATURE_NAMES += ENCODER\nFEATURE_NAMES += HAPTIC\nFEATURE_NAMES += HD44780\nFEATURE_NAMES += IOS_DEVICE\nFEATURE_NAMES += LCD_BACKLIGHT\nFEATURE_NAMES += LCD\nFEATURE_NAMES += OLED\nFEATURE_NAMES += POINTING_DEVICE\nFEATURE_NAMES += PS2_MOUSE\nFEATURE_NAMES += RGBLIGHT\nFEATURE_NAMES += RGB_MATRIX\nFEATURE_NAMES += SLEEP_LED\nFEATURE_NAMES += STENO\nFEATURE_NAMES += SWAP_HANDS\nFEATURE_NAMES += WATCHDOG\nFEATURE_NAMES += XT\n\n$(foreach AFEATURE,$(FEATURE_NAMES),\\\n\t $(if $(filter $($(AFEATURE)_SUPPORTED),no),$(eval $(AFEATURE)_ENABLE=no)))\n"
  },
  {
    "path": "builddefs/docsgen/.gitignore",
    "content": "node_modules\n.vitepress/cache\n.vitepress/.temp\n.vitepress/dist\ndocs\n"
  },
  {
    "path": "builddefs/docsgen/.vitepress/config.mts",
    "content": "import { defineConfig } from \"vitepress\";\nimport { tabsMarkdownPlugin } from \"vitepress-plugin-tabs\";\nimport sidebar from \"../../../docs/_sidebar.json\";\n\n// https://vitepress.dev/reference/site-config\nexport default defineConfig(({ mode }) => {\n    const prod = mode === \"production\";\n    return {\n        title: \"QMK Firmware\",\n        description: \"Documentation for QMK Firmware\",\n\n        srcDir: prod ? \"docs\" : \"../../docs\",\n        outDir: \"../../.build/docs\",\n        cleanUrls: true,\n\n        markdown: {\n            config(md) {\n                md.use(tabsMarkdownPlugin);\n            },\n        },\n\n        vite: {\n            resolve: {\n                preserveSymlinks: true,\n            },\n        },\n\n        themeConfig: {\n            // https://vitepress.dev/reference/default-theme-config\n            logo: {\n                light: \"/qmk-logo-light.svg\",\n                dark: \"/qmk-logo-dark.svg\",\n            },\n            title: 'QMK Firmware',\n\n            nav: [{ text: \"Home\", link: \"/\" }],\n\n            search: {\n                provider: \"local\",\n            },\n\n            editLink: {\n                pattern: 'https://github.com/qmk/qmk_firmware/edit/master/docs/:path'\n            },\n            lastUpdated: true,\n\n            sidebar: sidebar,\n\n            externalLinkIcon: true,\n\n            socialLinks: [\n                { icon: { svg: '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 50 50\" width=\"50px\" height=\"50px\"><path d=\"M 29 3 C 28.0625 3 27.164063 3.382813 26.5 4 C 25.835938 4.617188 25.363281 5.433594 25 6.40625 C 24.355469 8.140625 24.085938 10.394531 24.03125 13.03125 C 19.234375 13.179688 14.820313 14.421875 11.28125 16.46875 C 10.214844 15.46875 8.855469 14.96875 7.5 14.96875 C 6.089844 14.96875 4.675781 15.511719 3.59375 16.59375 C 1.425781 18.761719 1.425781 22.238281 3.59375 24.40625 L 3.84375 24.65625 C 3.3125 26.035156 3 27.488281 3 29 C 3 33.527344 5.566406 37.585938 9.5625 40.4375 C 13.558594 43.289063 19.007813 45 25 45 C 30.992188 45 36.441406 43.289063 40.4375 40.4375 C 44.433594 37.585938 47 33.527344 47 29 C 47 27.488281 46.6875 26.035156 46.15625 24.65625 L 46.40625 24.40625 C 48.574219 22.238281 48.574219 18.761719 46.40625 16.59375 C 45.324219 15.511719 43.910156 14.96875 42.5 14.96875 C 41.144531 14.96875 39.785156 15.46875 38.71875 16.46875 C 35.195313 14.433594 30.800781 13.191406 26.03125 13.03125 C 26.09375 10.546875 26.363281 8.46875 26.875 7.09375 C 27.164063 6.316406 27.527344 5.757813 27.875 5.4375 C 28.222656 5.117188 28.539063 5 29 5 C 29.460938 5 29.683594 5.125 30.03125 5.40625 C 30.378906 5.6875 30.785156 6.148438 31.3125 6.6875 C 32.253906 7.652344 33.695313 8.714844 36.09375 8.9375 C 36.539063 11.238281 38.574219 13 41 13 C 43.75 13 46 10.75 46 8 C 46 5.25 43.75 3 41 3 C 38.605469 3 36.574219 4.710938 36.09375 6.96875 C 34.3125 6.796875 33.527344 6.109375 32.75 5.3125 C 32.300781 4.851563 31.886719 4.3125 31.3125 3.84375 C 30.738281 3.375 29.9375 3 29 3 Z M 41 5 C 42.667969 5 44 6.332031 44 8 C 44 9.667969 42.667969 11 41 11 C 39.332031 11 38 9.667969 38 8 C 38 6.332031 39.332031 5 41 5 Z M 25 15 C 30.609375 15 35.675781 16.613281 39.28125 19.1875 C 42.886719 21.761719 45 25.226563 45 29 C 45 32.773438 42.886719 36.238281 39.28125 38.8125 C 35.675781 41.386719 30.609375 43 25 43 C 19.390625 43 14.324219 41.386719 10.71875 38.8125 C 7.113281 36.238281 5 32.773438 5 29 C 5 25.226563 7.113281 21.761719 10.71875 19.1875 C 14.324219 16.613281 19.390625 15 25 15 Z M 7.5 16.9375 C 8.203125 16.9375 8.914063 17.148438 9.53125 17.59375 C 7.527344 19.03125 5.886719 20.769531 4.75 22.71875 C 3.582031 21.296875 3.660156 19.339844 5 18 C 5.714844 17.285156 6.609375 16.9375 7.5 16.9375 Z M 42.5 16.9375 C 43.390625 16.9375 44.285156 17.285156 45 18 C 46.339844 19.339844 46.417969 21.296875 45.25 22.71875 C 44.113281 20.769531 42.472656 19.03125 40.46875 17.59375 C 41.085938 17.148438 41.796875 16.9375 42.5 16.9375 Z M 17 22 C 14.800781 22 13 23.800781 13 26 C 13 28.199219 14.800781 30 17 30 C 19.199219 30 21 28.199219 21 26 C 21 23.800781 19.199219 22 17 22 Z M 33 22 C 30.800781 22 29 23.800781 29 26 C 29 28.199219 30.800781 30 33 30 C 35.199219 30 37 28.199219 37 26 C 37 23.800781 35.199219 22 33 22 Z M 17 24 C 18.117188 24 19 24.882813 19 26 C 19 27.117188 18.117188 28 17 28 C 15.882813 28 15 27.117188 15 26 C 15 24.882813 15.882813 24 17 24 Z M 33 24 C 34.117188 24 35 24.882813 35 26 C 35 27.117188 34.117188 28 33 28 C 31.882813 28 31 27.117188 31 26 C 31 24.882813 31.882813 24 33 24 Z M 34.15625 33.84375 C 34.101563 33.851563 34.050781 33.859375 34 33.875 C 33.683594 33.9375 33.417969 34.144531 33.28125 34.4375 C 33.28125 34.4375 32.757813 35.164063 31.4375 36 C 30.117188 36.835938 28.058594 37.6875 25 37.6875 C 21.941406 37.6875 19.882813 36.835938 18.5625 36 C 17.242188 35.164063 16.71875 34.4375 16.71875 34.4375 C 16.492188 34.082031 16.066406 33.90625 15.65625 34 C 15.332031 34.082031 15.070313 34.316406 14.957031 34.632813 C 14.84375 34.945313 14.894531 35.292969 15.09375 35.5625 C 15.09375 35.5625 15.863281 36.671875 17.46875 37.6875 C 19.074219 38.703125 21.558594 39.6875 25 39.6875 C 28.441406 39.6875 30.925781 38.703125 32.53125 37.6875 C 34.136719 36.671875 34.90625 35.5625 34.90625 35.5625 C 35.207031 35.273438 35.296875 34.824219 35.128906 34.441406 C 34.960938 34.058594 34.574219 33.820313 34.15625 33.84375 Z\"/></svg>' }, link: \"https://reddit.com/r/olkb\" },\n                { icon: \"discord\", link: \"https://discord.gg/qmk\" },\n                { icon: \"github\", link: \"https://github.com/qmk/qmk_firmware\" },\n            ],\n        }\n    };\n});\n"
  },
  {
    "path": "builddefs/docsgen/.vitepress/theme/QMKLayout.vue",
    "content": "<script setup>\nimport DefaultTheme from 'vitepress/theme'\nimport { useRouter } from 'vitepress'\nimport { onBeforeMount } from 'vue';\nimport aliases from \"../../../../docs/_aliases.json\";\n\nconst router = useRouter()\nonBeforeMount(async () => {\n    // Convert from docsify-style to vitepress-style URLs\n    let newUrl = window.location.href.replace(/\\/#\\//, '/').replace(/\\?id=/, '#');\n\n    // Convert any aliases\n    let testUrl = new URL(newUrl);\n    while (testUrl.pathname in aliases) {\n        testUrl.pathname = aliases[testUrl.pathname];\n    }\n    newUrl = testUrl.toString();\n\n    // Redirect if required\n    if (newUrl != window.location.href) {\n        window.history.replaceState({}, '', newUrl);\n        await router.go(newUrl);\n    }\n});\n</script>\n\n<template>\n    <DefaultTheme.Layout/>\n</template>\n"
  },
  {
    "path": "builddefs/docsgen/.vitepress/theme/custom.css",
    "content": "/* Override <kbd> as vitepress doesn't put them with borders */\nkbd {\n    border: 1px solid var(--vp-c-text-1);\n    border-radius: 5px;\n    margin: 0.2em;\n    padding: 0.2em;\n}\n\n:root {\n    --vp-nav-logo-height: 32px;\n\n    --vp-layout-max-width: calc(98% + 64px);\n\n    --vp-sidebar-width: 300px;\n}\n\n.VPDoc.has-aside .content-container {\n    max-width: unset !important;\n}\n"
  },
  {
    "path": "builddefs/docsgen/.vitepress/theme/index.ts",
    "content": "import type { Theme } from 'vitepress'\nimport DefaultTheme from 'vitepress/theme'\nimport { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client'\nimport QMKLayout from './QMKLayout.vue'\nimport './custom.css'\n\nexport default {\n  extends: DefaultTheme,\n  Layout: QMKLayout,\n  enhanceApp({ app }) {\n    enhanceAppWithTabs(app)\n  }\n} satisfies Theme\n"
  },
  {
    "path": "builddefs/docsgen/build-docs.sh",
    "content": "#!/usr/bin/env bash\n\ncd \"$(dirname \"$(realpath \"${BASH_SOURCE[0]}\")\")\"\nyarn install\n[[ -e docs ]] || ln -sf ../../docs docs\nDEBUG='vitepress:*,vite:*' yarn run docs:build\n"
  },
  {
    "path": "builddefs/docsgen/package.json",
    "content": "{\n  \"license\": \"GPL-2.0-or-later\",\n  \"devDependencies\": {\n    \"vite\": \"^5.4.18\",\n    \"vitepress\": \"^1.1.0\",\n    \"vitepress-plugin-tabs\": \"^0.5.0\",\n    \"vue\": \"^3.4.24\"\n  },\n  \"scripts\": {\n    \"docs:dev\": \"vitepress dev --host 0.0.0.0\",\n    \"docs:build\": \"vitepress build\",\n    \"docs:preview\": \"vitepress preview --host 0.0.0.0\"\n  }\n}\n"
  },
  {
    "path": "builddefs/docsgen/start-docs.sh",
    "content": "#!/usr/bin/env bash\n\ncd \"$(dirname \"$(realpath \"${BASH_SOURCE[0]}\")\")\"\nyarn install\nDEBUG='vitepress:*,vite:*' yarn run docs:dev\n"
  },
  {
    "path": "builddefs/generic_features.mk",
    "content": "# Copyright 2021 QMK\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nGRAVE_ESC_ENABLE ?= yes\nMAGIC_ENABLE ?= yes\nSEND_STRING_ENABLE ?= yes\nSPACE_CADET_ENABLE ?= yes\n\nGENERIC_FEATURES = \\\n    AUTO_SHIFT \\\n    AUTOCORRECT \\\n    BOOTMAGIC \\\n    CAPS_WORD \\\n    COMBO \\\n    COMMAND \\\n    CONNECTION \\\n    CRC \\\n    DEFERRED_EXEC \\\n    DIGITIZER \\\n    DIP_SWITCH \\\n    DYNAMIC_KEYMAP \\\n    DYNAMIC_MACRO \\\n    DYNAMIC_TAPPING_TERM \\\n    GRAVE_ESC \\\n    HAPTIC \\\n    KEYCODE_STRING \\\n    KEY_LOCK \\\n    KEY_OVERRIDE \\\n    LAYER_LOCK \\\n    LEADER \\\n    MAGIC \\\n    MOUSEKEY \\\n    MUSIC \\\n    OS_DETECTION \\\n    PROGRAMMABLE_BUTTON \\\n    REPEAT_KEY \\\n    SECURE \\\n    SEND_STRING \\\n    SEQUENCER \\\n    SPACE_CADET \\\n    SWAP_HANDS \\\n    TAP_DANCE \\\n    TRI_LAYER \\\n    VIA \\\n    VIRTSER \\\n    WPM \\\n\ndefine HANDLE_GENERIC_FEATURE\n    # $$(info \"Processing: $1_ENABLE $2.c\")\n    SRC += $$(wildcard $$(QUANTUM_DIR)/process_keycode/process_$2.c)\n    SRC += $$(wildcard $$(QUANTUM_DIR)/$2/$2.c)\n    SRC += $$(wildcard $$(QUANTUM_DIR)/$2.c)\n    SRC += $$(wildcard $$(QUANTUM_DIR)/nvm/$$(NVM_DRIVER_LOWER)/nvm_$2.c)\n    VPATH += $$(wildcard $$(QUANTUM_DIR)/$2/)\n    OPT_DEFS += -D$1_ENABLE\nendef\n\n$(foreach F,$(GENERIC_FEATURES),\\\n    $(if $(filter yes, $(strip $($(F)_ENABLE))),\\\n        $(eval $(call HANDLE_GENERIC_FEATURE,$(F),$(shell echo $(F) | tr '[:upper:]' '[:lower:]'))) \\\n    ) \\\n)\n"
  },
  {
    "path": "builddefs/message.mk",
    "content": "COLOR ?= true\n\nifeq ($(COLOR),true)\n\tNO_COLOR=\\033[0m\n\tOK_COLOR=\\033[32;01m\n\tERROR_COLOR=\\033[31;01m\n\tWARN_COLOR=\\033[33;01m\n\tSKIPPED_COLOR=\\033[36;01m\n\tBLUE=\\033[0;34m\n\tBOLD=\\033[1m\nendif\n\nifneq ($(shell echo \"1 2 3\" | awk '{ printf \"%2s\", $$3; }' 2>/dev/null),\" 3\")\n\tAWK=awk\nelse\n\tAWK=cat && test\nendif\n\nON_ERROR ?= exit 1\n\nOK_STRING=$(OK_COLOR)[OK]$(NO_COLOR)\\n\nERROR_STRING=$(ERROR_COLOR)[ERRORS]$(NO_COLOR)\\n\nWARN_STRING=$(WARN_COLOR)[WARNINGS]$(NO_COLOR)\\n\nSKIPPED_STRING=$(SKIPPED_COLOR)[SKIPPED]$(NO_COLOR)\\n\n\nTAB_LOG = printf \"\\n%s\\n\\n\" \"$$LOG\" | $(AWK) '{ sub(/^/,\" | \"); print }'\nTAB_LOG_PLAIN = printf \"%s\\n\" \"$$LOG\"\nAWK_STATUS = $(AWK) '{ printf \" %-10s\\n\", $$1; }'\nAWK_CMD = $(AWK) '{ printf \"%-99s\", $$0; }'\nPRINT_ERROR = ($(SILENT) ||printf \" $(ERROR_STRING)\" | $(AWK_STATUS)) && $(TAB_LOG) && $(ON_ERROR)\nPRINT_WARNING = ($(SILENT) || printf \" $(WARN_STRING)\" | $(AWK_STATUS)) && $(TAB_LOG)\nPRINT_ERROR_PLAIN = ($(SILENT) ||printf \" $(ERROR_STRING)\" | $(AWK_STATUS)) && $(TAB_LOG_PLAIN) && $(ON_ERROR)\nPRINT_WARNING_PLAIN = ($(SILENT) || printf \" $(WARN_STRING)\" | $(AWK_STATUS)) && $(TAB_LOG_PLAIN)\nPRINT_SKIPPED_PLAIN = ($(SILENT) || printf \" $(SKIPPED_STRING)\" | $(AWK_STATUS))\nPRINT_OK = $(SILENT) || printf \" $(OK_STRING)\" | $(AWK_STATUS)\nBUILD_CMD = LOG=$$($(CMD) 2>&1) ; if [ $$? -gt 0 ]; then $(PRINT_ERROR); elif [ \"$$LOG\" != \"\" ] ; then $(PRINT_WARNING); else $(PRINT_OK); fi;\nMAKE_MSG_FORMAT = $(AWK) '{ printf \"%-118s\", $$0;}'\n\n# The UNSYNC_OUTPUT_CMD command disables the `--output-sync` for the current command, if the `--output-sync` granularity is `target` or lower.\n# This is achieved by telling make to treat the current command as if it invokes a recursive make subcommand (as if by calling `$(MAKE)`).\nUNSYNC_OUTPUT_CMD = +true\n\n# Define Messages\n# English\nMSG_ERRORS_NONE = Errors: none\nMSG_ERRORS = $(ERROR_COLOR)Make finished with errors\\n$(NO_COLOR)\nMSG_BEGIN = -------- begin --------\nMSG_END = --------  end  --------\nMSG_SIZE_BEFORE = Size before:\nMSG_SIZE_AFTER = Size after:\nMSG_COFF = Converting to AVR COFF:\nMSG_EXTENDED_COFF = Converting to AVR Extended COFF:\nMSG_FLASH = Creating load file for flashing:\nMSG_UF2 = Creating UF2 file for deployment:\nMSG_EEPROM = Creating load file for EEPROM:\nMSG_BIN = Creating binary load file for flashing:\nMSG_EXTENDED_LISTING = Creating Extended Listing:\nMSG_SYMBOL_TABLE = Creating Symbol Table:\nMSG_EXECUTING = Executing:\nMSG_LINKING = Linking:\nMSG_COMPILING = Compiling:\nMSG_COMPILING_CXX = Compiling:\nMSG_ASSEMBLING = Assembling:\nMSG_CLEANING = Cleaning project:\nMSG_CREATING_LIBRARY = Creating library:\nMSG_GENERATING = Generating:\nMSG_NOT_REPO = $(WARN_COLOR)WARNING:$(NO_COLOR) Target folder is not a git repo, you probably downloaded a zip file instead of cloning.\\n\\\nPlease consider following $(BOLD)https://docs.qmk.fm/\\#/newbs_getting_started$(NO_COLOR).\\n\\n\nMSG_SUBMODULE_DIRTY = $(WARN_COLOR)WARNING:$(NO_COLOR) Some git submodules are out of date or modified.\\n\\\nPlease consider running $(BOLD)qmk git-submodule$(NO_COLOR).\\n\\n\n\ndefine GENERATE_MSG_MAKE_KB\n    MSG_MAKE_KB_ACTUAL := Making $$(KB_SP) with keymap $(BOLD)$$(CURRENT_KM)$(NO_COLOR)\n    ifneq ($$(MAKE_TARGET),)\n        MSG_MAKE_KB_ACTUAL += and target $(BOLD)$$(MAKE_TARGET)$(NO_COLOR)\n    endif\nendef\nMSG_MAKE_KB = $(eval $(call GENERATE_MSG_MAKE_KB))$(MSG_MAKE_KB_ACTUAL)\ndefine GENERATE_MSG_MAKE_TEST\n    MSG_MAKE_TEST_ACTUAL := Making test $(BOLD)$(TEST_NAME)$(NO_COLOR)\n    ifneq ($$(MAKE_TARGET),)\n        MSG_MAKE_TEST_ACTUAL += with target $(BOLD)$$(MAKE_TARGET)$(NO_COLOR)\n    endif\nendef\nMSG_MAKE_TEST = $(eval $(call GENERATE_MSG_MAKE_TEST))$(MSG_MAKE_TEST_ACTUAL)\nMSG_TEST = Testing $(BOLD)$(TEST_NAME)$(NO_COLOR)\ndefine GENERATE_MSG_AVAILABLE_KEYMAPS\n    MSG_AVAILABLE_KEYMAPS_ACTUAL := Available keymaps for $(BOLD)$$(CURRENT_KB)$(NO_COLOR):\nendef\nMSG_AVAILABLE_KEYMAPS = $(eval $(call GENERATE_MSG_AVAILABLE_KEYMAPS))$(MSG_AVAILABLE_KEYMAPS_ACTUAL)\n\nMSG_BOOTLOADER_NOT_FOUND_BASE = Bootloader not found. Make sure the board is in bootloader mode. See https://docs.qmk.fm/\\#/newbs_flashing\\n\nMSG_CHECK_STACKSIZE = Checking stack size of $(TARGET)\nMSG_CHECK_FILESIZE = Checking file size of $(TARGET).$(FIRMWARE_FORMAT)\nMSG_MEMORY_OVERFLOW = $(ERROR_COLOR)RAM usage (not including stack) exceeds available RAM by $(RAM_OVERFLOW_AMOUNT) bytes\\n\nMSG_STACK_SIZE = Available stack size: $(STACK_SIZE) bytes\\n\nMSG_FILE_TOO_BIG = $(ERROR_COLOR)The firmware is too large!$(NO_COLOR) $(CURRENT_SIZE)/$(MAX_SIZE) ($(OVER_SIZE) bytes over)\\n\nMSG_FILE_TOO_SMALL = The firmware is too small! $(CURRENT_SIZE)/$(MAX_SIZE)\\n\nMSG_FILE_JUST_RIGHT = The firmware size is fine - $(CURRENT_SIZE)/$(MAX_SIZE) ($(PERCENT_SIZE)%%, $(FREE_SIZE) bytes free)\\n\nMSG_FILE_NEAR_LIMIT = The firmware size is approaching the maximum - $(CURRENT_SIZE)/$(MAX_SIZE) ($(PERCENT_SIZE)%%, $(FREE_SIZE) bytes free)\\n\nMSG_PYTHON_MISSING = $(ERROR_COLOR)ERROR:$(NO_COLOR) Cannot run \\\"qmk hello\\\"!\\n\\n\\\n\tPlease run $(BOLD)qmk setup$(NO_COLOR) to install all the dependencies QMK requires.\\n\\n\nMSG_FLASH_BOOTLOADER = $(WARN_COLOR)WARNING:$(NO_COLOR) This board's bootloader is not specified or is not supported by the \\\":flash\\\" target at this time.\\n\\n\nMSG_FLASH_ARCH = $(WARN_COLOR)WARNING:$(NO_COLOR) This board's architecture is not supported by the \\\":flash\\\" target at this time.\\n\\n\nMSG_BOOTLOADER_NOT_FOUND = $(ERROR_COLOR)ERROR:$(NO_COLOR) $(MSG_BOOTLOADER_NOT_FOUND_BASE) Trying again in 5s (Ctrl+C to cancel)\\n\nBOOTLOADER_RETRY_TIME ?= 0.5\nMSG_BOOTLOADER_NOT_FOUND_QUICK_RETRY = $(MSG_BOOTLOADER_NOT_FOUND_BASE) Trying again every $(BOOTLOADER_RETRY_TIME)s (Ctrl+C to cancel)\n\ndefine WARNING_MESSAGE\n    $(shell printf \"\\n %-99s $(WARN_STRING)\\n\" \"$1\" >&2)\nendef\n\ndefine CATASTROPHIC_ERROR\n    $(shell printf \"\\n * %-99s $(ERROR_STRING)\\n\" \"$2\" >&2)\n    $(error $1)\nendef\n"
  },
  {
    "path": "builddefs/show_options.mk",
    "content": "BUILD_OPTION_NAMES = \\\n  BOOTMAGIC_ENABLE \\\n  MOUSEKEY_ENABLE \\\n  EXTRAKEY_ENABLE \\\n  CONSOLE_ENABLE \\\n  COMMAND_ENABLE \\\n  NKRO_ENABLE \\\n  CUSTOM_MATRIX \\\n  DEBOUNCE_TYPE \\\n  SPLIT_KEYBOARD \\\n  DYNAMIC_KEYMAP_ENABLE \\\n  USB_HID_ENABLE \\\n  VIA_ENABLE\n\nHARDWARE_OPTION_NAMES = \\\n  SLEEP_LED_ENABLE \\\n  BACKLIGHT_ENABLE \\\n  BACKLIGHT_DRIVER \\\n  RGBLIGHT_ENABLE \\\n  RGBLIGHT_DRIVER \\\n  RGB_MATRIX_ENABLE \\\n  RGB_MATRIX_DRIVER \\\n  CIE1931_CURVE \\\n  MIDI_ENABLE \\\n  BLUETOOTH_ENABLE \\\n  BLUETOOTH_DRIVER \\\n  AUDIO_ENABLE \\\n  HD44780_ENABLE \\\n  ENCODER_ENABLE \\\n  LED_TABLES \\\n  POINTING_DEVICE_ENABLE \\\n  DIP_SWITCH_ENABLE\n\nOTHER_OPTION_NAMES = \\\n  UNICODE_ENABLE \\\n  UCIS_ENABLE \\\n  UNICODEMAP_ENABLE \\\n  UNICODE_COMMON \\\n  AUTO_SHIFT_ENABLE \\\n  DYNAMIC_TAPPING_TERM_ENABLE \\\n  COMBO_ENABLE \\\n  KEY_LOCK_ENABLE \\\n  KEY_OVERRIDE_ENABLE \\\n  LEADER_ENABLE \\\n  STENO_ENABLE \\\n  STENO_PROTOCOL \\\n  TAP_DANCE_ENABLE \\\n  VIRTSER_ENABLE \\\n  OLED_ENABLE \\\n  OLED_DRIVER \\\n  LED_BACK_ENABLE \\\n  LED_UNDERGLOW_ENABLE \\\n  LED_ANIMATIONS \\\n  IOS_DEVICE_ENABLE \\\n  HELIX ZINC \\\n  AUTOLOG_ENABLE \\\n  DEBUG_ENABLE \\\n  ENCODER_MAP_ENABLE \\\n  ENCODER_ENABLE_CUSTOM \\\n  GERMAN_ENABLE \\\n  HAPTIC_ENABLE \\\n  KEYLOGGER_ENABLE \\\n  LCD_BACKLIGHT_ENABLE \\\n  MACROS_ENABLED \\\n  PS2_ENABLE \\\n  PS2_MOUSE_ENABLE \\\n  PS2_DRIVER \\\n  RAW_ENABLE \\\n  SWAP_HANDS_ENABLE \\\n  WATCHDOG_ENABLE \\\n  ERGOINU \\\n  NO_USB_STARTUP_CHECK \\\n  DISABLE_PROMICRO_LEDs \\\n  MITOSIS_DATAGROK_BOTTOMSPACE \\\n  MITOSIS_DATAGROK_SLOWUART \\\n  RGB_MATRIX_KEYPRESSES \\\n  LED_MIRRORED \\\n  RGBLIGHT_FULL_POWER \\\n  LTO_ENABLE \\\n  PROGRAMMABLE_BUTTON_ENABLE \\\n  SECURE_ENABLE \\\n  CAPS_WORD_ENABLE \\\n  AUTOCORRECT_ENABLE \\\n  TRI_LAYER_ENABLE \\\n  REPEAT_KEY_ENABLE\n\ndefine NAME_ECHO\n       @printf \"  %-30s = %-16s # %s\\\\n\" \"$1\" \"$($1)\" \"$(origin $1)\"\n\nendef\n\ndefine YAML_NAME_ECHO\n\t@echo '  $1 : \"$(strip $($1))\"'\n\nendef\n\n.PHONY: show_build_options0 show_build_options\nshow_build_options0:\n\t@echo \" KEYBOARD        = $(KEYBOARD)\"\n\t@echo \" KEYMAP          = $(KEYMAP)\"\n\t@echo \" MCU             = $(MCU)\"\n\t@echo \" MCU_SERIES      = $(MCU_SERIES)\"\n\t@echo \" PLATFORM        = $(PLATFORM)\"\n\t@echo \" BOOTLOADER      = $(BOOTLOADER)\"\n\t@echo \" FIRMWARE_FORMAT = $(FIRMWARE_FORMAT)\"\n\t@echo\n\t@echo \"Build Options:\"\n\t$(foreach A_OPTION_NAME,$(sort $(BUILD_OPTION_NAMES)),\\\n\t\t$(call NAME_ECHO,$(A_OPTION_NAME)))\n\nshow_build_options: show_build_options0\n\t@echo\n\t@echo \"If you want to know more, please try 'show_all_features' or 'show_full_features'\"\n\t@echo\n\n.PHONY: show_all_features\nshow_all_features: show_build_options0\n\t@echo\n\t@echo \"Hardware Options:\"\n\t$(foreach A_OPTION_NAME,$(sort $(HARDWARE_OPTION_NAMES)),\\\n\t\t$(if $($(A_OPTION_NAME)),$(call NAME_ECHO,$(A_OPTION_NAME))))\n\t@echo\n\t@echo \"Other Options:\"\n\t$(foreach A_OPTION_NAME,$(sort $(OTHER_OPTION_NAMES)),\\\n\t\t$(if $($(A_OPTION_NAME)),$(call NAME_ECHO,$(A_OPTION_NAME))))\n\n.PHONY: show_full_features\nshow_full_features: show_build_options0\n\t@echo\n\t@echo \"Hardware Options:\"\n\t$(foreach A_OPTION_NAME,$(sort $(HARDWARE_OPTION_NAMES)),\\\n\t\t$(call NAME_ECHO,$(A_OPTION_NAME)))\n\t@echo\n\t@echo \"Other Options:\"\n\t$(foreach A_OPTION_NAME,$(sort $(OTHER_OPTION_NAMES)),\\\n\t\t$(call NAME_ECHO,$(A_OPTION_NAME)))\n\n.PHONY: yaml_build_options\nyaml_build_options:\n\t@echo '- KEYBOARD : \"$(KEYBOARD)\"'\n\t@echo '  KEYMAP : \"$(KEYMAP)\"'\n\t@echo '  MCU : \"$(MCU)\"'\n\t@echo '  MCU_SERIES : \"$(MCU_SERIES)\"'\n\t@echo '  PLATFORM : \"$(PLATFORM)\"'\n\t@echo '  FIRMWARE_FORMAT : \"$(FIRMWARE_FORMAT)\"'\n\t$(foreach A_OPTION_NAME,$(sort $(BUILD_OPTION_NAMES)),\\\n\t\t$(call YAML_NAME_ECHO,$(A_OPTION_NAME)))\n\t$(foreach A_OPTION_NAME,$(sort $(HARDWARE_OPTION_NAMES)),\\\n\t\t$(if $($(A_OPTION_NAME)),$(call YAML_NAME_ECHO,$(A_OPTION_NAME))))\n\t$(foreach A_OPTION_NAME,$(sort $(OTHER_OPTION_NAMES)),\\\n\t\t$(if $($(A_OPTION_NAME)),$(call YAML_NAME_ECHO,$(A_OPTION_NAME))))\n"
  },
  {
    "path": "builddefs/support.mk",
    "content": "# Helper to determine if a compiler option is supported\n# Args:\n#   $(1) = option to test, if successful will be output\n#   $(2) = option to use if $(1) is not supported\n#   $(3) = additional arguments to pass to the compiler during the test, but aren't contained in the output\ncc-option = $(shell \\\n\tif { echo 'int main(){return 0;}' | $(CC) $(1) $(3) -o /dev/null -x c /dev/null >/dev/null 2>&1; }; \\\n\tthen echo \"$(1)\"; else echo \"$(2)\"; fi)\n\n# Helper to pass comma character to make functions (use with `$(,)` to pass in `$(call ...)` arguments)\n, := ,\n"
  },
  {
    "path": "builddefs/testlist.mk",
    "content": "TEST_LIST = $(sort $(patsubst %/test.mk,%, $(shell find $(ROOT_DIR)tests -type f -name test.mk)))\nFULL_TESTS := $(notdir $(TEST_LIST))\n\ninclude $(QUANTUM_PATH)/debounce/tests/testlist.mk\ninclude $(QUANTUM_PATH)/encoder/tests/testlist.mk\ninclude $(QUANTUM_PATH)/os_detection/tests/testlist.mk\ninclude $(QUANTUM_PATH)/sequencer/tests/testlist.mk\ninclude $(QUANTUM_PATH)/wear_leveling/tests/testlist.mk\ninclude $(PLATFORM_PATH)/test/testlist.mk\n\ndefine VALIDATE_TEST_LIST\n    ifneq ($1,)\n        ifeq ($$(findstring -,$1),-)\n            $$(call CATASTROPHIC_ERROR,Invalid test name,Test names can't contain '-', but '$1' does.)\n        else\n            $$(eval $$(call VALIDATE_TEST_LIST,$$(firstword $2),$$(wordlist 2,9999,$2)))\n        endif\n    endif\nendef\n\n\n$(eval $(call VALIDATE_TEST_LIST,$(firstword $(TEST_LIST)),$(wordlist 2,9999,$(TEST_LIST))))\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_belgian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ² │ & │ é │ \" │ ' │ ( │ § │ è │ ! │ ç │ à │ ) │ - │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ A │ Z │ E │ R │ T │ Y │ U │ I │ O │ P │ ^ │ $ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ Q │ S │ D │ F │ G │ H │ J │ K │ L │ M │ ù │ µ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ W │ X │ C │ V │ B │ N │ , │ ; │ : │ = │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"BE_SUP2\",\n            \"label\": \"²\",\n        }\n        \"KC_1\": {\n            \"key\": \"BE_AMPR\",\n            \"label\": \"&\",\n        }\n        \"KC_2\": {\n            \"key\": \"BE_EACU\",\n            \"label\": \"é\",\n        }\n        \"KC_3\": {\n            \"key\": \"BE_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"KC_4\": {\n            \"key\": \"BE_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_5\": {\n            \"key\": \"BE_LPRN\",\n            \"label\": \"(\",\n        }\n        \"KC_6\": {\n            \"key\": \"BE_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_7\": {\n            \"key\": \"BE_EGRV\",\n            \"label\": \"è\",\n        }\n        \"KC_8\": {\n            \"key\": \"BE_EXLM\",\n            \"label\": \"!\",\n        }\n        \"KC_9\": {\n            \"key\": \"BE_CCED\",\n            \"label\": \"ç\",\n        }\n        \"KC_0\": {\n            \"key\": \"BE_AGRV\",\n            \"label\": \"à\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"BE_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"BE_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_Q\": {\n            \"key\": \"BE_A\",\n            \"label\": \"A\",\n        }\n        \"KC_W\": {\n            \"key\": \"BE_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_E\": {\n            \"key\": \"BE_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"BE_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"BE_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"BE_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"BE_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"BE_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"BE_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"BE_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"BE_DCIR\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"BE_DLR\",\n            \"label\": \"$\",\n        }\n        \"KC_A\": {\n            \"key\": \"BE_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_S\": {\n            \"key\": \"BE_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"BE_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"BE_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"BE_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"BE_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"BE_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"BE_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"BE_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"BE_M\",\n            \"label\": \"M\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"BE_UGRV\",\n            \"label\": \"ù\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"BE_MICR\",\n            \"label\": \"µ\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"BE_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"BE_W\",\n            \"label\": \"W\",\n        }\n        \"KC_X\": {\n            \"key\": \"BE_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"BE_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"BE_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"BE_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"BE_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"BE_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"BE_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"BE_COLN\",\n            \"label\": \":\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"BE_EQL\",\n            \"label\": \"=\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ³ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ° │ _ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ ¨ │ * │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ % │ £ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │ ? │ . │ / │ + │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(BE_SUP2)\": {\n            \"key\": \"BE_SUP3\",\n            \"label\": \"³\",\n        }\n        \"S(BE_AMPR)\": {\n            \"key\": \"BE_1\",\n            \"label\": \"1\",\n        }\n        \"S(BE_EACU)\": {\n            \"key\": \"BE_2\",\n            \"label\": \"2\",\n        }\n        \"S(BE_DQUO)\": {\n            \"key\": \"BE_3\",\n            \"label\": \"3\",\n        }\n        \"S(BE_QUOT)\": {\n            \"key\": \"BE_4\",\n            \"label\": \"4\",\n        }\n        \"S(BE_LPRN)\": {\n            \"key\": \"BE_5\",\n            \"label\": \"5\",\n        }\n        \"S(BE_SECT)\": {\n            \"key\": \"BE_6\",\n            \"label\": \"6\",\n        }\n        \"S(BE_EGRV)\": {\n            \"key\": \"BE_7\",\n            \"label\": \"7\",\n        }\n        \"S(BE_EXLM)\": {\n            \"key\": \"BE_8\",\n            \"label\": \"8\",\n        }\n        \"S(BE_CCED)\": {\n            \"key\": \"BE_9\",\n            \"label\": \"9\",\n        }\n        \"S(BE_AGRV)\": {\n            \"key\": \"BE_0\",\n            \"label\": \"0\",\n        }\n        \"S(BE_RPRN)\": {\n            \"key\": \"BE_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(BE_MINS)\": {\n            \"key\": \"BE_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(BE_DCIR)\": {\n            \"key\": \"BE_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(BE_DLR)\": {\n            \"key\": \"BE_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(BE_UGRV)\": {\n            \"key\": \"BE_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(BE_MICR)\": {\n            \"key\": \"BE_PND\",\n            \"label\": \"£\",\n        }\n        \"S(BE_LABK)\": {\n            \"key\": \"BE_RABK\",\n            \"label\": \">\",\n        }\n        \"S(BE_COMM)\": {\n            \"key\": \"BE_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(BE_SCLN)\": {\n            \"key\": \"BE_DOT\",\n            \"label\": \".\",\n        }\n        \"S(BE_COLN)\": {\n            \"key\": \"BE_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(BE_EQL)\": {\n            \"key\": \"BE_PLUS\",\n            \"label\": \"+\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ | │ @ │ # │   │   │ ^ │   │   │ { │ } │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │   │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ ´ │ ` │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ \\ │   │   │   │   │   │   │   │   │   │ ~ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(BE_AMPR)\": {\n            \"key\": \"BE_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(BE_EACU)\": {\n            \"key\": \"BE_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(BE_DQUO)\": {\n            \"key\": \"BE_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(BE_SECT)\": {\n            \"key\": \"BE_CIRC\",\n            \"label\": \"^\",\n        }\n        \"ALGR(BE_CCED)\": {\n            \"key\": \"BE_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(BE_AGRV)\": {\n            \"key\": \"BE_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(BE_E)\": {\n            \"key\": \"BE_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(BE_DCIR)\": {\n            \"key\": \"BE_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(BE_DLR)\": {\n            \"key\": \"BE_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(BE_UGRV)\": {\n            \"key\": \"BE_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(BE_MICR)\": {\n            \"key\": \"BE_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"ALGR(BE_LABK)\": {\n            \"key\": \"BE_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(BE_EQL)\": {\n            \"key\": \"BE_TILD\",\n            \"label\": \"~\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_bepo_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ $ │ \" │ « │ » │ ( │ ) │ @ │ + │ - │ / │ * │ = │ % │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ B │ É │ P │ O │ È │ ^ │ V │ D │ L │ J │ Z │ W │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ U │ I │ E │ , │ C │ T │ S │ R │ N │ M │ Ç │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ Ê │ À │ Y │ X │ . │ K │ ' │ Q │ G │ H │ F │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"BP_DLR\",\n            \"label\": \"$\",\n        }\n        \"KC_1\": {\n            \"key\": \"BP_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"KC_2\": {\n            \"key\": \"BP_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"KC_3\": {\n            \"key\": \"BP_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"KC_4\": {\n            \"key\": \"BP_LPRN\",\n            \"label\": \"(\",\n        }\n        \"KC_5\": {\n            \"key\": \"BP_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_6\": {\n            \"key\": \"BP_AT\",\n            \"label\": \"@\",\n        }\n        \"KC_7\": {\n            \"key\": \"BP_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_8\": {\n            \"key\": \"BP_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_9\": {\n            \"key\": \"BP_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_0\": {\n            \"key\": \"BP_ASTR\",\n            \"label\": \"*\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"BP_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"BP_PERC\",\n            \"label\": \"%\",\n        }\n        \"KC_Q\": {\n            \"key\": \"BP_B\",\n            \"label\": \"B\",\n        }\n        \"KC_W\": {\n            \"key\": \"BP_EACU\",\n            \"label\": \"É\",\n        }\n        \"KC_E\": {\n            \"key\": \"BP_P\",\n            \"label\": \"P\",\n        }\n        \"KC_R\": {\n            \"key\": \"BP_O\",\n            \"label\": \"O\",\n        }\n        \"KC_T\": {\n            \"key\": \"BP_EGRV\",\n            \"label\": \"È\",\n        }\n        \"KC_Y\": {\n            \"key\": \"BP_DCIR\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_U\": {\n            \"key\": \"BP_V\",\n            \"label\": \"V\",\n        }\n        \"KC_I\": {\n            \"key\": \"BP_D\",\n            \"label\": \"D\",\n        }\n        \"KC_O\": {\n            \"key\": \"BP_L\",\n            \"label\": \"L\",\n        }\n        \"KC_P\": {\n            \"key\": \"BP_J\",\n            \"label\": \"J\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"BP_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"BP_W\",\n            \"label\": \"W\",\n        }\n        \"KC_A\": {\n            \"key\": \"BP_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"BP_U\",\n            \"label\": \"U\",\n        }\n        \"KC_D\": {\n            \"key\": \"BP_I\",\n            \"label\": \"I\",\n        }\n        \"KC_F\": {\n            \"key\": \"BP_E\",\n            \"label\": \"E\",\n        }\n        \"KC_G\": {\n            \"key\": \"BP_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_H\": {\n            \"key\": \"BP_C\",\n            \"label\": \"C\",\n        }\n        \"KC_J\": {\n            \"key\": \"BP_T\",\n            \"label\": \"T\",\n        }\n        \"KC_K\": {\n            \"key\": \"BP_S\",\n            \"label\": \"S\",\n        }\n        \"KC_L\": {\n            \"key\": \"BP_R\",\n            \"label\": \"R\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"BP_N\",\n            \"label\": \"N\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"BP_M\",\n            \"label\": \"M\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"BP_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"BP_ECIR\",\n            \"label\": \"Ê\",\n        }\n        \"KC_Z\": {\n            \"key\": \"BP_AGRV\",\n            \"label\": \"À\",\n        }\n        \"KC_X\": {\n            \"key\": \"BP_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_C\": {\n            \"key\": \"BP_X\",\n            \"label\": \"X\",\n        }\n        \"KC_V\": {\n            \"key\": \"BP_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_B\": {\n            \"key\": \"BP_K\",\n            \"label\": \"K\",\n        }\n        \"KC_N\": {\n            \"key\": \"BP_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_M\": {\n            \"key\": \"BP_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"BP_G\",\n            \"label\": \"G\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"BP_H\",\n            \"label\": \"H\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"BP_F\",\n            \"label\": \"F\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ # │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ° │ ` │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │ ! │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │ ; │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │ : │   │ ? │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(BP_DLR)\": {\n            \"key\": \"BP_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(BP_DQUO)\": {\n            \"key\": \"BP_1\",\n            \"label\": \"1\",\n        }\n        \"S(BP_LDAQ)\": {\n            \"key\": \"BP_2\",\n            \"label\": \"2\",\n        }\n        \"S(BP_RDAQ)\": {\n            \"key\": \"BP_3\",\n            \"label\": \"3\",\n        }\n        \"S(BP_LPRN)\": {\n            \"key\": \"BP_4\",\n            \"label\": \"4\",\n        }\n        \"S(BP_RPRN)\": {\n            \"key\": \"BP_5\",\n            \"label\": \"5\",\n        }\n        \"S(BP_AT)\": {\n            \"key\": \"BP_6\",\n            \"label\": \"6\",\n        }\n        \"S(BP_PLUS)\": {\n            \"key\": \"BP_7\",\n            \"label\": \"7\",\n        }\n        \"S(BP_MINS)\": {\n            \"key\": \"BP_8\",\n            \"label\": \"8\",\n        }\n        \"S(BP_SLSH)\": {\n            \"key\": \"BP_9\",\n            \"label\": \"9\",\n        }\n        \"S(BP_ASTR)\": {\n            \"key\": \"BP_0\",\n            \"label\": \"0\",\n        }\n        \"S(BP_EQL)\": {\n            \"key\": \"BP_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(BP_PERC)\": {\n            \"key\": \"BP_GRV\",\n            \"label\": \"`\",\n        }\n        \"S(BP_DCIR)\": {\n            \"key\": \"BP_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(BP_COMM)\": {\n            \"key\": \"BP_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(BP_DOT)\": {\n            \"key\": \"BP_COLN\",\n            \"label\": \":\",\n        }\n        \"S(BP_QUOT)\": {\n            \"key\": \"BP_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(KC_SPC)\": {\n            \"key\": \"BP_NBSP\",\n            \"label\": \"(non-breaking space)\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ – │ — │ < │ > │ [ │ ] │ ^ │ ± │ − │ ÷ │ × │ ≠ │ ‰ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ | │ ´ │ & │ Œ │ ` │ ¡ │ ˇ │ Ð │ / │ Ĳ │ Ə │ ˘ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ Æ │ Ù │ ¨ │ € │   │ © │ Þ │ ẞ │ ® │ ~ │ ¯ │ ¸ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │ \\ │ { │ } │ … │ ~ │ ¿ │ ° │   │ † │ ˛ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │           _            │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(BP_DLR)\": {\n            \"key\": \"BP_NDSH\",\n            \"label\": \"–\",\n        }\n        \"ALGR(BP_DQUO)\": {\n            \"key\": \"BP_MDSH\",\n            \"label\": \"—\",\n        }\n        \"ALGR(BP_LDAQ)\": {\n            \"key\": \"BP_LABK\",\n            \"label\": \"<\",\n        }\n        \"ALGR(BP_RDAQ)\": {\n            \"key\": \"BP_RABK\",\n            \"label\": \">\",\n        }\n        \"ALGR(BP_LPRN)\": {\n            \"key\": \"BP_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(BP_RPRN)\": {\n            \"key\": \"BP_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(BP_AT)\": {\n            \"key\": \"BP_CIRC\",\n            \"label\": \"^\",\n        }\n        \"ALGR(BP_PLUS)\": {\n            \"key\": \"BP_PLMN\",\n            \"label\": \"±\",\n        }\n        \"ALGR(BP_MINS)\": {\n            \"key\": \"BP_MMNS\",\n            \"label\": \"−\",\n        }\n        \"ALGR(BP_SLSH)\": {\n            \"key\": \"BP_DIV\",\n            \"label\": \"÷\",\n        }\n        \"ALGR(BP_ASTR)\": {\n            \"key\": \"BP_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(BP_EQL)\": {\n            \"key\": \"BP_NEQL\",\n            \"label\": \"≠\",\n        }\n        \"ALGR(BP_PERC)\": {\n            \"key\": \"BP_PERM\",\n            \"label\": \"‰\",\n        }\n        \"ALGR(BP_B)\": {\n            \"key\": \"BP_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(BP_EACU)\": {\n            \"key\": \"BP_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(BP_P)\": {\n            \"key\": \"BP_AMPR\",\n            \"label\": \"&\",\n        }\n        \"ALGR(BP_O)\": {\n            \"key\": \"BP_OE\",\n            \"label\": \"Œ\",\n        }\n        \"ALGR(BP_EGRV)\": {\n            \"key\": \"BP_DGRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"ALGR(BP_DCIR)\": {\n            \"key\": \"BP_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"ALGR(BP_V)\": {\n            \"key\": \"BP_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"ALGR(BP_D)\": {\n            \"key\": \"BP_ETH\",\n            \"label\": \"Ð\",\n        }\n        \"ALGR(BP_L)\": {\n            \"key\": \"BP_DSLS\",\n            \"label\": \"/ (dead)\",\n        }\n        \"ALGR(BP_J)\": {\n            \"key\": \"BP_IJ\",\n            \"label\": \"Ĳ\",\n        }\n        \"ALGR(BP_Z)\": {\n            \"key\": \"BP_SCHW\",\n            \"label\": \"Ə\",\n        }\n        \"ALGR(BP_W)\": {\n            \"key\": \"BP_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"ALGR(BP_A)\": {\n            \"key\": \"BP_AE\",\n            \"label\": \"Æ\",\n        }\n        \"ALGR(BP_U)\": {\n            \"key\": \"BP_UGRV\",\n            \"label\": \"Ù\",\n        }\n        \"ALGR(BP_I)\": {\n            \"key\": \"BP_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"ALGR(BP_E)\": {\n            \"key\": \"BP_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(BP_C)\": {\n            \"key\": \"BP_COPY\",\n            \"label\": \"©\",\n        }\n        \"ALGR(BP_T)\": {\n            \"key\": \"BP_THRN\",\n            \"label\": \"Þ\",\n        }\n        \"ALGR(BP_S)\": {\n            \"key\": \"BP_SS\",\n            \"label\": \"ẞ\",\n        }\n        \"ALGR(BP_R)\": {\n            \"key\": \"BP_REGD\",\n            \"label\": \"®\",\n        }\n        \"ALGR(BP_N)\": {\n            \"key\": \"BP_DTIL\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(BP_M)\": {\n            \"key\": \"BP_MACR\",\n            \"label\": \"¯ (dead)\",\n        }\n        \"ALGR(BP_CCED)\": {\n            \"key\": \"BP_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"ALGR(BP_AGRV)\": {\n            \"key\": \"BP_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(BP_Y)\": {\n            \"key\": \"BP_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(BP_X)\": {\n            \"key\": \"BP_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(BP_DOT)\": {\n            \"key\": \"BP_ELLP\",\n            \"label\": \"…\",\n        }\n        \"ALGR(BP_K)\": {\n            \"key\": \"BP_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(BP_QUES)\": {\n            \"key\": \"BP_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"ALGR(BP_Q)\": {\n            \"key\": \"BP_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"ALGR(BP_G)\": {\n            \"key\": \"BP_DGRK\",\n            \"label\": \"µ (dead Greek key)\",\n        }\n        \"ALGR(BP_H)\": {\n            \"key\": \"BP_DAGG\",\n            \"label\": \"†\",\n        }\n        \"ALGR(BP_F)\": {\n            \"key\": \"BP_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"ALGR(KC_SPC)\": {\n            \"key\": \"BP_UNDS\",\n            \"label\": \"_\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¶ │ „ │ “ │ ” │ ≤ │ ≥ │   │ ¬ │ ¼ │ ½ │ ¾ │ ′ │ ″ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ ¦ │ ˝ │ § │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │ ˙ │ ¤ │ ̛  │ ſ │   │   │ ™ │   │ º │ , │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │ ‘ │ ’ │ · │ ⌨ │ ̉  │ ̣  │   │ ‡ │ ª │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ALGR(BP_DLR))\": {\n            \"key\": \"BP_PARA\",\n            \"label\": \"¶\",\n        }\n        \"S(ALGR(BP_DQUO))\": {\n            \"key\": \"BP_DLQU\",\n            \"label\": \"„\",\n        }\n        \"S(ALGR(BP_LDAQ))\": {\n            \"key\": \"BP_LDQU\",\n            \"label\": \"“\",\n        }\n        \"S(ALGR(BP_RDAQ))\": {\n            \"key\": \"BP_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(ALGR(BP_LPRN))\": {\n            \"key\": \"BP_LEQL\",\n            \"label\": \"≤\",\n        }\n        \"S(ALGR(BP_RPRN))\": {\n            \"key\": \"BP_GEQL\",\n            \"label\": \"≥\",\n        }\n        \"S(ALGR(BP_PLUS))\": {\n            \"key\": \"BP_NOT\",\n            \"label\": \"¬\",\n        }\n        \"S(ALGR(BP_MINS))\": {\n            \"key\": \"BP_QRTR\",\n            \"label\": \"¼\",\n        }\n        \"S(ALGR(BP_SLSH))\": {\n            \"key\": \"BP_HALF\",\n            \"label\": \"½\",\n        }\n        \"S(ALGR(BP_ASTR))\": {\n            \"key\": \"BP_TQTR\",\n            \"label\": \"¾\",\n        }\n        \"S(ALGR(BP_EQL))\": {\n            \"key\": \"BP_PRIM\",\n            \"label\": \"′\",\n        }\n        \"S(ALGR(BP_PERC))\": {\n            \"key\": \"BP_DPRM\",\n            \"label\": \"″\",\n        }\n        \"S(ALGR(BP_B))\": {\n            \"key\": \"BP_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"S(ALGR(BP_EACU))\": {\n            \"key\": \"BP_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"S(ALGR(BP_P))\": {\n            \"key\": \"BP_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(ALGR(BP_I))\": {\n            \"key\": \"BP_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n        \"S(ALGR(BP_E))\": {\n            \"key\": \"BP_CURR\",\n            \"label\": \"¤ (dead)\",\n        }\n        \"S(ALGR(BP_COMM))\": {\n            \"key\": \"BP_HORN\",\n            \"label\": \"̛ (dead)\",\n        }\n        \"S(ALGR(BP_C))\": {\n            \"key\": \"BP_LNGS\",\n            \"label\": \"ſ\",\n        }\n        \"S(ALGR(BP_R))\": {\n            \"key\": \"BP_TM\",\n            \"label\": \"™\",\n        }\n        \"S(ALGR(BP_M))\": {\n            \"key\": \"BP_MORD\",\n            \"label\": \"º\",\n        }\n        \"S(ALGR(BP_CCED))\": {\n            \"key\": \"BP_DCMM\",\n            \"label\": \", (dead)\",\n        }\n        \"S(ALGR(BP_Y))\": {\n            \"key\": \"BP_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"S(ALGR(BP_X))\": {\n            \"key\": \"BP_RSQU\",\n            \"label\": \"’\",\n        }\n        \"S(ALGR(BP_DOT))\": {\n            \"key\": \"BP_MDDT\",\n            \"label\": \"·\",\n        }\n        \"S(ALGR(BP_K))\": {\n            \"key\": \"BP_KEYB\",\n            \"label\": \"⌨\",\n        }\n        \"S(ALGR(BP_QUOT))\": {\n            \"key\": \"BP_HOKA\",\n            \"label\": \"̉ (dead)\",\n        }\n        \"S(ALGR(BP_Q))\": {\n            \"key\": \"BP_DOTB\",\n            \"label\": \"̣ (dead)\",\n        }\n        \"S(ALGR(BP_H))\": {\n            \"key\": \"BP_DDAG\",\n            \"label\": \"‡\",\n        }\n        \"S(ALGR(BP_F))\": {\n            \"key\": \"BP_FORD\",\n            \"label\": \"ª\",\n        }\n        \"S(ALGR(KC_SPC))\": {\n            \"key\": \"BP_NNBS\",\n            \"label\": \"(narrow non-breaking space)\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_brazilian_abnt2_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ' │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ ´ │ [ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ç │ ~ │ ] │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤\n * │    │ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ ; │ / │      │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬──┴─┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"BR_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_1\": {\n            \"key\": \"BR_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"BR_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"BR_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"BR_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"BR_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"BR_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"BR_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"BR_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"BR_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"BR_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"BR_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"BR_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"BR_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"BR_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"BR_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"BR_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"BR_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"BR_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"BR_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"BR_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"BR_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"BR_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"BR_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"BR_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_A\": {\n            \"key\": \"BR_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"BR_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"BR_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"BR_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"BR_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"BR_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"BR_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"BR_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"BR_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"BR_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"BR_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"BR_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"BR_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_Z\": {\n            \"key\": \"BR_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"BR_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"BR_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"BR_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"BR_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"BR_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"BR_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"BR_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"BR_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"BR_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_INT1\": {\n            \"key\": \"BR_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_PCMM\": {\n            \"key\": \"BR_PDOT\",\n            \"label\": \".\",\n        }\n        \"KC_PDOT\": {\n            \"key\": \"BR_PCMM\",\n            \"label\": \",\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ \" │ ! │ @ │ # │ $ │ % │ ¨ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ ` │ { │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ ^ │ } │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤\n * │    │ | │   │   │   │   │   │   │   │ < │ > │ : │ ? │      │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬──┴─┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(BR_QUOT)\": {\n            \"key\": \"BR_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(BR_1)\": {\n            \"key\": \"BR_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(BR_2)\": {\n            \"key\": \"BR_AT\",\n            \"label\": \"@\",\n        }\n        \"S(BR_3)\": {\n            \"key\": \"BR_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(BR_4)\": {\n            \"key\": \"BR_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(BR_5)\": {\n            \"key\": \"BR_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(BR_6)\": {\n            \"key\": \"BR_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(BR_7)\": {\n            \"key\": \"BR_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(BR_8)\": {\n            \"key\": \"BR_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(BR_9)\": {\n            \"key\": \"BR_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(BR_0)\": {\n            \"key\": \"BR_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(BR_MINS)\": {\n            \"key\": \"BR_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(BR_EQL)\": {\n            \"key\": \"BR_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(BR_ACUT)\": {\n            \"key\": \"BR_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(BR_LBRC)\": {\n            \"key\": \"BR_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(BR_TILD)\": {\n            \"key\": \"BR_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(BR_RBRC)\": {\n            \"key\": \"BR_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(BR_BSLS)\": {\n            \"key\": \"BR_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(BR_COMM)\": {\n            \"key\": \"BR_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(BR_DOT)\": {\n            \"key\": \"BR_RABK\",\n            \"label\": \">\",\n        }\n        \"S(BR_SCLN)\": {\n            \"key\": \"BR_COLN\",\n            \"label\": \":\",\n        }\n        \"S(BR_SLSH)\": {\n            \"key\": \"BR_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ¹ │ ² │ ³ │ £ │ ¢ │ ¬ │   │   │   │   │   │ § │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ ° │   │   │   │   │   │   │   │   │ ª │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ º │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤\n * │    │   │   │   │ ₢ │   │   │   │   │   │   │   │   │      │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬──┴─┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(BR_1)\": {\n            \"key\": \"BR_SUP1\",\n            \"label\": \"¹\",\n        }\n        \"ALGR(BR_2)\": {\n            \"key\": \"BR_SUP2\",\n            \"label\": \"²\",\n        }\n        \"ALGR(BR_3)\": {\n            \"key\": \"BR_SUP3\",\n            \"label\": \"³\",\n        }\n        \"ALGR(BR_4)\": {\n            \"key\": \"BR_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(BR_5)\": {\n            \"key\": \"BR_CENT\",\n            \"label\": \"¢\",\n        }\n        \"ALGR(BR_6)\": {\n            \"key\": \"BR_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(BR_EQL)\": {\n            \"key\": \"BR_SECT\",\n            \"label\": \"§\",\n        }\n        \"ALGR(BR_E)\": {\n            \"key\": \"BR_DEG\",\n            \"label\": \"°\",\n        }\n        \"ALGR(BR_LBRC)\": {\n            \"key\": \"BR_FORD\",\n            \"label\": \"ª\",\n        }\n        \"ALGR(BR_RBRC)\": {\n            \"key\": \"BR_MORD\",\n            \"label\": \"º\",\n        }\n        \"ALGR(BR_C)\": {\n            \"key\": \"BR_CRUZ\",\n            \"label\": \"₢\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_canadian_french_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ # │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ ^ │ ¸ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ` │ < │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ « │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ É │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"FR_HASH\",\n            \"label\": \"#\",\n        }\n        \"KC_1\": {\n            \"key\": \"FR_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"FR_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"FR_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"FR_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"FR_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"FR_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"FR_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"FR_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"FR_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"FR_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"FR_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"FR_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"FR_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"FR_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"FR_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"FR_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"FR_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"FR_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"FR_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"FR_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"FR_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"FR_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"FR_DCIR\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"FR_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"FR_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"FR_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"FR_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"FR_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"FR_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"FR_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"FR_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"FR_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"FR_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"FR_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"FR_DGRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"FR_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"FR_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"KC_Z\": {\n            \"key\": \"FR_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"FR_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"FR_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"FR_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"FR_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"FR_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"FR_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"FR_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"FR_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"FR_EACU\",\n            \"label\": \"É\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ | │ ! │ \" │ / │ $ │ % │ ? │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ¨ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ : │   │ > │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ » │   │   │   │   │   │   │   │ ' │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(FR_HASH)\": {\n            \"key\": \"FR_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(FR_1)\": {\n            \"key\": \"FR_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(FR_2)\": {\n            \"key\": \"FR_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(FR_3)\": {\n            \"key\": \"FR_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(FR_4)\": {\n            \"key\": \"FR_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(FR_5)\": {\n            \"key\": \"FR_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(FR_6)\": {\n            \"key\": \"FR_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(FR_7)\": {\n            \"key\": \"FR_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(FR_8)\": {\n            \"key\": \"FR_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(FR_9)\": {\n            \"key\": \"FR_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(FR_0)\": {\n            \"key\": \"FR_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(FR_MINS)\": {\n            \"key\": \"FR_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(FR_EQL)\": {\n            \"key\": \"FR_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(FR_CEDL)\": {\n            \"key\": \"FR_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(FR_SCLN)\": {\n            \"key\": \"FR_COLN\",\n            \"label\": \":\",\n        }\n        \"S(FR_LABK)\": {\n            \"key\": \"FR_RABK\",\n            \"label\": \">\",\n        }\n        \"S(FR_LDAQ)\": {\n            \"key\": \"FR_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(FR_COMM)\": {\n            \"key\": \"FR_QUOT\",\n            \"label\": \"'\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ \\ │ ± │ @ │ £ │ ¢ │ ¤ │ ¬ │ ¦ │ ² │ ³ │ ¼ │ ½ │ ¾ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │ § │ ¶ │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ ~ │ { │ } │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ ° │   │   │   │   │   │   │ µ │ ¯ │ - │ ´ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(FR_HASH)\": {\n            \"key\": \"FR_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(FR_1)\": {\n            \"key\": \"FR_PLMN\",\n            \"label\": \"±\",\n        }\n        \"ALGR(FR_2)\": {\n            \"key\": \"FR_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(FR_3)\": {\n            \"key\": \"FR_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(FR_4)\": {\n            \"key\": \"FR_CENT\",\n            \"label\": \"¢\",\n        }\n        \"ALGR(FR_5)\": {\n            \"key\": \"FR_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(FR_6)\": {\n            \"key\": \"FR_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(FR_7)\": {\n            \"key\": \"FR_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"ALGR(FR_8)\": {\n            \"key\": \"FR_SUP2\",\n            \"label\": \"²\",\n        }\n        \"ALGR(FR_9)\": {\n            \"key\": \"FR_SUP3\",\n            \"label\": \"³\",\n        }\n        \"ALGR(FR_0)\": {\n            \"key\": \"FR_QRTR\",\n            \"label\": \"¼\",\n        }\n        \"ALGR(FR_MINS)\": {\n            \"key\": \"FR_HALF\",\n            \"label\": \"½\",\n        }\n        \"ALGR(FR_EQL)\": {\n            \"key\": \"FR_TQTR\",\n            \"label\": \"¾\",\n        }\n        \"ALGR(FR_O)\": {\n            \"key\": \"FR_SECT\",\n            \"label\": \"§\",\n        }\n        \"ALGR(FR_P)\": {\n            \"key\": \"FR_PARA\",\n            \"label\": \"¶\",\n        }\n        \"ALGR(FR_DCIR)\": {\n            \"key\": \"FR_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(FR_CEDL)\": {\n            \"key\": \"FR_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(FR_SCLN)\": {\n            \"key\": \"FR_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(FR_DGRV)\": {\n            \"key\": \"FR_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(FR_LABK)\": {\n            \"key\": \"FR_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(FR_LDAQ)\": {\n            \"key\": \"FR_DEG\",\n            \"label\": \"°\",\n        }\n        \"ALGR(FR_M)\": {\n            \"key\": \"FR_MICR\",\n            \"label\": \"µ\",\n        }\n        \"ALGR(FR_COMM)\": {\n            \"key\": \"FR_MACR\",\n            \"label\": \"¯\",\n        }\n        \"ALGR(FR_DOT)\": {\n            \"key\": \"FR_SHYP\",\n            \"label\": \"­ (soft hyphen)\",\n        }\n        \"ALGR(FR_EACU)\": {\n            \"key\": \"FR_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_canadian_multilingual_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ / │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ ^ │ Ç │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ È │ À │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ Ù │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ É │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"CA_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_1\": {\n            \"key\": \"CA_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"CA_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"CA_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"CA_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"CA_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"CA_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"CA_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"CA_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"CA_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"CA_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"CA_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"CA_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"CA_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"CA_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"CA_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"CA_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"CA_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"CA_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"CA_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"CA_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"CA_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"CA_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"CA_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"CA_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"KC_A\": {\n            \"key\": \"CA_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"CA_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"CA_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"CA_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"CA_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"CA_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"CA_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"CA_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"CA_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"CA_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"CA_EGRV\",\n            \"label\": \"É\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"CA_AGRV\",\n            \"label\": \"À\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"CA_UGRV\",\n            \"label\": \"Ù\",\n        }\n        \"KC_Z\": {\n            \"key\": \"CA_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"CA_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"CA_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"CA_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"CA_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"CA_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"CA_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"CA_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"CA_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"CA_EACU\",\n            \"label\": \"É\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ \\ │ ! │ @ │ # │ $ │ % │ ? │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ ¨ │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ : │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │ ' │ \" │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(CA_SLSH)\": {\n            \"key\": \"CA_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"S(CA_1)\": {\n            \"key\": \"CA_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(CA_2)\": {\n            \"key\": \"CA_AT\",\n            \"label\": \"@\",\n        }\n        \"S(CA_3)\": {\n            \"key\": \"CA_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(CA_4)\": {\n            \"key\": \"CA_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(CA_5)\": {\n            \"key\": \"CA_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(CA_6)\": {\n            \"key\": \"CA_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(CA_7)\": {\n            \"key\": \"CA_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(CA_8)\": {\n            \"key\": \"CA_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(CA_9)\": {\n            \"key\": \"CA_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(CA_0)\": {\n            \"key\": \"CA_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(CA_MINS)\": {\n            \"key\": \"CA_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(CA_EQL)\": {\n            \"key\": \"CA_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(CA_CIRC)\": {\n            \"key\": \"CA_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(CA_SCLN)\": {\n            \"key\": \"CA_COLN\",\n            \"label\": \":\",\n        }\n        \"S(CA_COMM)\": {\n            \"key\": \"CA_QUOT\",\n            \"label\": \"'\",\n        }\n        \"S(CA_DOT)\": {\n            \"key\": \"CA_DQUO\",\n            \"label\": \"\\\"\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ | │   │   │   │ ¤ │   │   │ { │ } │ [ │ ] │   │ ¬ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │   │ ` │ ~ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ ° │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │ « │ » │   │   │   │   │   │ < │ > │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(CA_SLSH)\": {\n            \"key\": \"CA_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(CA_4)\": {\n            \"key\": \"CA_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(CA_7)\": {\n            \"key\": \"CA_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(CA_8)\": {\n            \"key\": \"CA_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(CA_9)\": {\n            \"key\": \"CA_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(CA_0)\": {\n            \"key\": \"CA_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(CA_EQL)\": {\n            \"key\": \"CA_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(CA_E)\": {\n            \"key\": \"CA_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(CA_CIRC)\": {\n            \"key\": \"CA_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"ALGR(CA_CCED)\": {\n            \"key\": \"CA_DTIL\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(CA_SCLN)\": {\n            \"key\": \"CA_DEG\",\n            \"label\": \"°\",\n        }\n        \"ALGR(CA_Z)\": {\n            \"key\": \"CA_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"ALGR(CA_X)\": {\n            \"key\": \"CA_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"ALGR(CA_COMM)\": {\n            \"key\": \"CA_LABK\",\n            \"label\": \"<\",\n        }\n        \"ALGR(CA_DOT)\": {\n            \"key\": \"CA_RABK\",\n            \"label\": \">\",\n        }\n/* Right Ctrl symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ¹ │ ² │ ³ │ ¼ │ ½ │ ¾ │   │   │   │   │   │ ¸ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Ω │ Ł │ Œ │ ¶ │ Ŧ │ ← │ ↓ │ → │ Ø │ Þ │   │ ~ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ Æ │ ß │ Ð │   │ Ŋ │ Ħ │ Ĳ │ ĸ │ Ŀ │ ´ │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │ ¢ │ “ │ ” │ ŉ │ μ │ ― │ ˙ │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"RCTL(CA_1)\": {\n            \"key\": \"CA_SUP1\",\n            \"label\": \"¹\",\n        }\n        \"RCTL(CA_2)\": {\n            \"key\": \"CA_SUP2\",\n            \"label\": \"²\",\n        }\n        \"RCTL(CA_3)\": {\n            \"key\": \"CA_SUP3\",\n            \"label\": \"³\",\n        }\n        \"RCTL(CA_4)\": {\n            \"key\": \"CA_QRTR\",\n            \"label\": \"¼\",\n        }\n        \"RCTL(CA_5)\": {\n            \"key\": \"CA_HALF\",\n            \"label\": \"½\",\n        }\n        \"RCTL(CA_6)\": {\n            \"key\": \"CA_TQTR\",\n            \"label\": \"¾\",\n        }\n        \"RCTL(CA_EQL)\": {\n            \"key\": \"CA_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"RCTL(CA_Q)\": {\n            \"key\": \"CA_OMEG\",\n            \"label\": \"Ω\",\n        }\n        \"RCTL(CA_W)\": {\n            \"key\": \"CA_LSTR\",\n            \"label\": \"Ł\",\n        }\n        \"RCTL(CA_E)\": {\n            \"key\": \"CA_OE\",\n            \"label\": \"Œ\",\n        }\n        \"RCTL(CA_R)\": {\n            \"key\": \"CA_PARA\",\n            \"label\": \"¶\",\n        }\n        \"RCTL(CA_T)\": {\n            \"key\": \"CA_TSTR\",\n            \"label\": \"Ŧ\",\n        }\n        \"RCTL(CA_Y)\": {\n            \"key\": \"CA_LARR\",\n            \"label\": \"←\",\n        }\n        \"RCTL(CA_U)\": {\n            \"key\": \"CA_DARR\",\n            \"label\": \"↓\",\n        }\n        \"RCTL(CA_I)\": {\n            \"key\": \"CA_RARR\",\n            \"label\": \"→\",\n        }\n        \"RCTL(CA_O)\": {\n            \"key\": \"CA_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"RCTL(CA_P)\": {\n            \"key\": \"CA_THRN\",\n            \"label\": \"Þ\",\n        }\n        \"RCTL(CA_CCED)\": {\n            \"key\": \"CA_TILD\",\n            \"label\": \"~\",\n        }\n        \"RCTL(CA_A)\": {\n            \"key\": \"CA_AE\",\n            \"label\": \"Æ\",\n        }\n        \"RCTL(CA_S)\": {\n            \"key\": \"CA_SS\",\n            \"label\": \"ß\",\n        }\n        \"RCTL(CA_D)\": {\n            \"key\": \"CA_ETH\",\n            \"label\": \"Ð\",\n        }\n        \"RCTL(CA_G)\": {\n            \"key\": \"CA_ENG\",\n            \"label\": \"Ŋ\",\n        }\n        \"RCTL(CA_H)\": {\n            \"key\": \"CA_HSTR\",\n            \"label\": \"Ħ\",\n        }\n        \"RCTL(CA_J)\": {\n            \"key\": \"CA_IJ\",\n            \"label\": \"Ĳ\",\n        }\n        \"RCTL(CA_K)\": {\n            \"key\": \"CA_KRA\",\n            \"label\": \"ĸ\",\n        }\n        \"RCTL(CA_L)\": {\n            \"key\": \"CA_LMDT\",\n            \"label\": \"Ŀ\",\n        }\n        \"RCTL(CA_SCLN)\": {\n            \"key\": \"CA_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"RCTL(CA_C)\": {\n            \"key\": \"CA_CENT\",\n            \"label\": \"¢\",\n        }\n        \"RCTL(CA_V)\": {\n            \"key\": \"CA_LDQU\",\n            \"label\": \"“\",\n        }\n        \"RCTL(CA_B)\": {\n            \"key\": \"CA_RDQU\",\n            \"label\": \"”\",\n        }\n        \"RCTL(CA_N)\": {\n            \"key\": \"CA_APSN\",\n            \"label\": \"ŉ\",\n        }\n        \"RCTL(CA_M)\": {\n            \"key\": \"CA_MICR\",\n            \"label\": \"μ\",\n        }\n        \"RCTL(CA_COMM)\": {\n            \"key\": \"CA_HRZB\",\n            \"label\": \"―\",\n        }\n        \"RCTL(CA_DOT)\": {\n            \"key\": \"CA_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n/* Shift+Right Ctrl symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ - │ ¡ │   │ £ │   │ ⅜ │ ⅝ │ ⅞ │ ™ │ ± │   │ ¿ │ ˛ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │ ® │   │ ¥ │ ↑ │ ı │   │   │ ° │ ¯ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │ § │   │ ª │   │   │   │   │   │ ˝ │ ˇ │ ˘ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ ¦ │   │   │ © │ ‘ │ ’ │ ♪ │ º │ × │ ÷ │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"RCTL(S(CA_SLSH))\": {\n            \"key\": \"CA_SHYP\",\n            \"label\": \"­ (soft hyphen)\",\n        }\n        \"RCTL(S(CA_1))\": {\n            \"key\": \"CA_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"RCTL(S(CA_3))\": {\n            \"key\": \"CA_PND\",\n            \"label\": \"£\",\n        }\n        \"RCTL(S(CA_5))\": {\n            \"key\": \"CA_TEIG\",\n            \"label\": \"⅜\",\n        }\n        \"RCTL(S(CA_6))\": {\n            \"key\": \"CA_FEIG\",\n            \"label\": \"⅝\",\n        }\n        \"RCTL(S(CA_7))\": {\n            \"key\": \"CA_SEIG\",\n            \"label\": \"⅞\",\n        }\n        \"RCTL(S(CA_8))\": {\n            \"key\": \"CA_TM\",\n            \"label\": \"™\",\n        }\n        \"RCTL(S(CA_9))\": {\n            \"key\": \"CA_PLMN\",\n            \"label\": \"±\",\n        }\n        \"RCTL(S(CA_MINS))\": {\n            \"key\": \"CA_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"RCTL(S(CA_EQL))\": {\n            \"key\": \"CA_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"RCTL(S(CA_R))\": {\n            \"key\": \"CA_REGD\",\n            \"label\": \"®\",\n        }\n        \"RCTL(S(CA_Y))\": {\n            \"key\": \"CA_YEN\",\n            \"label\": \"¥\",\n        }\n        \"RCTL(S(CA_U))\": {\n            \"key\": \"CA_UARR\",\n            \"label\": \"↑\",\n        }\n        \"RCTL(S(CA_I))\": {\n            \"key\": \"CA_DLSI\",\n            \"label\": \"ı\",\n        }\n        \"RCTL(S(CA_CIRC))\": {\n            \"key\": \"CA_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"RCTL(S(CA_CCED))\": {\n            \"key\": \"CA_MACR\",\n            \"label\": \"¯ (dead)\",\n        }\n        \"RCTL(S(CA_S))\": {\n            \"key\": \"CA_SECT\",\n            \"label\": \"§\",\n        }\n        \"RCTL(S(CA_F))\": {\n            \"key\": \"CA_FORD\",\n            \"label\": \"ª\",\n        }\n        \"RCTL(S(CA_SCLN))\": {\n            \"key\": \"CA_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"RCTL(S(CA_EGRV))\": {\n            \"key\": \"CA_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"RCTL(S(CA_AGRV))\": {\n            \"key\": \"CA_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"RCTL(S(CA_UGRV))\": {\n            \"key\": \"CA_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"RCTL(S(CA_C))\": {\n            \"key\": \"CA_COPY\",\n            \"label\": \"©\",\n        }\n        \"RCTL(S(CA_V))\": {\n            \"key\": \"CA_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"RCTL(S(CA_B))\": {\n            \"key\": \"CA_RSQU\",\n            \"label\": \"’\",\n        }\n        \"RCTL(S(CA_N))\": {\n            \"key\": \"CA_ENOT\",\n            \"label\": \"♪\",\n        }\n        \"RCTL(S(CA_M))\": {\n            \"key\": \"CA_MORD\",\n            \"label\": \"º\",\n        }\n        \"RCTL(S(CA_COMM))\": {\n            \"key\": \"CA_MUL\",\n            \"label\": \"×\",\n        }\n        \"RCTL(S(CA_DOT))\": {\n            \"key\": \"CA_DIV\",\n            \"label\": \"÷\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_colemak_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ F │ P │ G │ J │ L │ U │ Y │ ; │ [ │ ] │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ R │ S │ T │ D │ H │ N │ E │ I │ O │ ' │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Z │ X │ C │ V │ B │ K │ M │ , │ . │ / │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"CM_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"CM_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"CM_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"CM_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"CM_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"CM_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"CM_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"CM_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"CM_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"CM_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"CM_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"CM_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"CM_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"CM_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"CM_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"CM_F\",\n            \"label\": \"F\",\n        }\n        \"KC_R\": {\n            \"key\": \"CM_P\",\n            \"label\": \"P\",\n        }\n        \"KC_T\": {\n            \"key\": \"CM_G\",\n            \"label\": \"G\",\n        }\n        \"KC_Y\": {\n            \"key\": \"CM_J\",\n            \"label\": \"J\",\n        }\n        \"KC_U\": {\n            \"key\": \"CM_L\",\n            \"label\": \"L\",\n        }\n        \"KC_I\": {\n            \"key\": \"CM_U\",\n            \"label\": \"U\",\n        }\n        \"KC_O\": {\n            \"key\": \"CM_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_P\": {\n            \"key\": \"CM_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"CM_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"CM_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"CM_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"CM_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"CM_R\",\n            \"label\": \"R\",\n        }\n        \"KC_D\": {\n            \"key\": \"CM_S\",\n            \"label\": \"S\",\n        }\n        \"KC_F\": {\n            \"key\": \"CM_T\",\n            \"label\": \"T\",\n        }\n        \"KC_G\": {\n            \"key\": \"CM_D\",\n            \"label\": \"D\",\n        }\n        \"KC_H\": {\n            \"key\": \"CM_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"CM_N\",\n            \"label\": \"N\",\n        }\n        \"KC_K\": {\n            \"key\": \"CM_E\",\n            \"label\": \"E\",\n        }\n        \"KC_L\": {\n            \"key\": \"CM_I\",\n            \"label\": \"I\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"CM_O\",\n            \"label\": \"O\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"CM_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_Z\": {\n            \"key\": \"CM_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"CM_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"CM_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"CM_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"CM_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"CM_K\",\n            \"label\": \"K\",\n        }\n        \"KC_M\": {\n            \"key\": \"CM_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"CM_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"CM_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"CM_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │ : │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(CM_GRV)\": {\n            \"key\": \"CM_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(CM_1)\": {\n            \"key\": \"CM_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(CM_2)\": {\n            \"key\": \"CM_AT\",\n            \"label\": \"@\",\n        }\n        \"S(CM_3)\": {\n            \"key\": \"CM_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(CM_4)\": {\n            \"key\": \"CM_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(CM_5)\": {\n            \"key\": \"CM_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(CM_6)\": {\n            \"key\": \"CM_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(CM_7)\": {\n            \"key\": \"CM_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(CM_8)\": {\n            \"key\": \"CM_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(CM_9)\": {\n            \"key\": \"CM_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(CM_0)\": {\n            \"key\": \"CM_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(CM_MINS)\": {\n            \"key\": \"CM_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(CM_EQL)\": {\n            \"key\": \"CM_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(CM_SCLN)\": {\n            \"key\": \"CM_COLN\",\n            \"label\": \":\",\n        }\n        \"S(CM_LBRC)\": {\n            \"key\": \"CM_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(CM_RBRC)\": {\n            \"key\": \"CM_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(CM_BSLS)\": {\n            \"key\": \"CM_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(CM_QUOT)\": {\n            \"key\": \"CM_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(CM_COMM)\": {\n            \"key\": \"CM_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(CM_DOT)\": {\n            \"key\": \"CM_RABK\",\n            \"label\": \">\",\n        }\n        \"S(CM_SLSH)\": {\n            \"key\": \"CM_QUES\",\n            \"label\": \"?\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_croatian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¸ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ Š │ Đ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Č │ Ć │ Ž │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"HR_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"KC_1\": {\n            \"key\": \"HR_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"HR_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"HR_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"HR_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"HR_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"HR_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"HR_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"HR_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"HR_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"HR_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"HR_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"HR_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_Q\": {\n            \"key\": \"HR_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"HR_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"HR_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"HR_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"HR_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"HR_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"HR_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"HR_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"HR_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"HR_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"HR_SCAR\",\n            \"label\": \"Š\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"HR_DSTR\",\n            \"label\": \"Đ\",\n        }\n        \"KC_A\": {\n            \"key\": \"HR_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"HR_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"HR_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"HR_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"HR_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"HR_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"HR_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"HR_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"HR_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"HR_CCAR\",\n            \"label\": \"Č\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"HR_CACU\",\n            \"label\": \"Ć\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"HR_ZCAR\",\n            \"label\": \"Ž\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"HR_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"HR_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"HR_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"HR_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"HR_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"HR_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"HR_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"HR_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"HR_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"HR_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"HR_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¨ │ ! │ \" │ # │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ * │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(HR_CEDL)\": {\n            \"key\": \"HR_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(HR_1)\": {\n            \"key\": \"HR_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(HR_2)\": {\n            \"key\": \"HR_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(HR_3)\": {\n            \"key\": \"HR_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(HR_4)\": {\n            \"key\": \"HR_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(HR_5)\": {\n            \"key\": \"HR_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(HR_6)\": {\n            \"key\": \"HR_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(HR_7)\": {\n            \"key\": \"HR_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(HR_8)\": {\n            \"key\": \"HR_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(HR_9)\": {\n            \"key\": \"HR_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(HR_0)\": {\n            \"key\": \"HR_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(HR_QUOT)\": {\n            \"key\": \"HR_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(HR_PLUS)\": {\n            \"key\": \"HR_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(HR_LABK)\": {\n            \"key\": \"HR_RABK\",\n            \"label\": \">\",\n        }\n        \"S(HR_COMM)\": {\n            \"key\": \"HR_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(HR_DOT)\": {\n            \"key\": \"HR_COLN\",\n            \"label\": \":\",\n        }\n        \"S(HR_MINS)\": {\n            \"key\": \"HR_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ~ │ ˇ │ ^ │ ˘ │ ° │ ˛ │ ` │ ˙ │ ´ │ ˝ │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ \\ │ | │ € │   │   │   │   │   │   │   │ ÷ │ × │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │ [ │ ] │   │   │ ł │ Ł │   │ ß │ ¤ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │ @ │ { │ } │ § │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(HR_1)\": {\n            \"key\": \"HR_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(HR_2)\": {\n            \"key\": \"HR_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"ALGR(HR_3)\": {\n            \"key\": \"HR_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"ALGR(HR_4)\": {\n            \"key\": \"HR_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"ALGR(HR_5)\": {\n            \"key\": \"HR_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"ALGR(HR_6)\": {\n            \"key\": \"HR_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"ALGR(HR_7)\": {\n            \"key\": \"HR_GRV\",\n            \"label\": \"`\",\n        }\n        \"ALGR(HR_8)\": {\n            \"key\": \"HR_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n        \"ALGR(HR_9)\": {\n            \"key\": \"HR_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(HR_0)\": {\n            \"key\": \"HR_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"ALGR(HR_Q)\": {\n            \"key\": \"HR_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(HR_W)\": {\n            \"key\": \"HR_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(HR_E)\": {\n            \"key\": \"HR_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(HR_SCAR)\": {\n            \"key\": \"HR_DIV\",\n            \"label\": \"÷\",\n        }\n        \"ALGR(HR_DSTR)\": {\n            \"key\": \"HR_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(HR_F)\": {\n            \"key\": \"HR_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(HR_G)\": {\n            \"key\": \"HR_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(HR_K)\": {\n            \"key\": \"HR_LLST\",\n            \"label\": \"ł\",\n        }\n        \"ALGR(HR_L)\": {\n            \"key\": \"HR_CLST\",\n            \"label\": \"Ł\",\n        }\n        \"ALGR(HR_CACU)\": {\n            \"key\": \"HR_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(HR_ZCAR)\": {\n            \"key\": \"HR_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(HR_V)\": {\n            \"key\": \"HR_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(HR_B)\": {\n            \"key\": \"HR_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(HR_N)\": {\n            \"key\": \"HR_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(HR_M)\": {\n            \"key\": \"HR_SECT\",\n            \"label\": \"§\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_czech_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ; │ + │ ě │ š │ č │ ř │ ž │ ý │ á │ í │ é │ = │ ´ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ ú │ ) │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ů │ § │ ¨ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ \\ │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"CZ_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_1\": {\n            \"key\": \"CZ_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_2\": {\n            \"key\": \"CZ_ECAR\",\n            \"label\": \"ě\",\n        }\n        \"KC_3\": {\n            \"key\": \"CZ_SCAR\",\n            \"label\": \"š\",\n        }\n        \"KC_4\": {\n            \"key\": \"CZ_CCAR\",\n            \"label\": \"č\",\n        }\n        \"KC_5\": {\n            \"key\": \"CZ_RCAR\",\n            \"label\": \"ř\",\n        }\n        \"KC_6\": {\n            \"key\": \"CZ_ZCAR\",\n            \"label\": \"ž\",\n        }\n        \"KC_7\": {\n            \"key\": \"CZ_YACU\",\n            \"label\": \"ý\",\n        }\n        \"KC_8\": {\n            \"key\": \"CZ_AACU\",\n            \"label\": \"á\",\n        }\n        \"KC_9\": {\n            \"key\": \"CZ_IACU\",\n            \"label\": \"í\",\n        }\n        \"KC_0\": {\n            \"key\": \"CZ_EACU\",\n            \"label\": \"é\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"CZ_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"CZ_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"CZ_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"CZ_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"CZ_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"CZ_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"CZ_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"CZ_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"CZ_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"CZ_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"CZ_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"CZ_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"CZ_UACU\",\n            \"label\": \"ú\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"CZ_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_A\": {\n            \"key\": \"CZ_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"CZ_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"CZ_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"CZ_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"CZ_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"CZ_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"CZ_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"CZ_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"CZ_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"CZ_URNG\",\n            \"label\": \"ů\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"CZ_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"CZ_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"CZ_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_Z\": {\n            \"key\": \"CZ_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"CZ_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"CZ_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"CZ_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"CZ_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"CZ_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"CZ_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"CZ_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"CZ_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"CZ_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ° │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ % │ ˇ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ / │ ( │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ \" │ ! │ ' │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ | │   │   │   │   │   │   │   │ ? │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(CZ_SCLN)\": {\n            \"key\": \"CZ_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"S(CZ_PLUS)\": {\n            \"key\": \"CZ_1\",\n            \"label\": \"1\",\n        }\n        \"S(CZ_ECAR)\": {\n            \"key\": \"CZ_2\",\n            \"label\": \"2\",\n        }\n        \"S(CZ_SCAR)\": {\n            \"key\": \"CZ_3\",\n            \"label\": \"3\",\n        }\n        \"S(CZ_CCAR)\": {\n            \"key\": \"CZ_4\",\n            \"label\": \"4\",\n        }\n        \"S(CZ_RCAR)\": {\n            \"key\": \"CZ_5\",\n            \"label\": \"5\",\n        }\n        \"S(CZ_ZCAR)\": {\n            \"key\": \"CZ_6\",\n            \"label\": \"6\",\n        }\n        \"S(CZ_YACU)\": {\n            \"key\": \"CZ_7\",\n            \"label\": \"7\",\n        }\n        \"S(CZ_AACU)\": {\n            \"key\": \"CZ_8\",\n            \"label\": \"8\",\n        }\n        \"S(CZ_IACU)\": {\n            \"key\": \"CZ_9\",\n            \"label\": \"9\",\n        }\n        \"S(CZ_EACU)\": {\n            \"key\": \"CZ_0\",\n            \"label\": \"0\",\n        }\n        \"S(CZ_EQL)\": {\n            \"key\": \"CZ_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(CZ_ACUT)\": {\n            \"key\": \"CZ_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"S(CZ_UACU)\": {\n            \"key\": \"CZ_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(CZ_RPRN)\": {\n            \"key\": \"CZ_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(CZ_URNG)\": {\n            \"key\": \"CZ_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(CZ_SECT)\": {\n            \"key\": \"CZ_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(CZ_DIAE)\": {\n            \"key\": \"CZ_QUOT\",\n            \"label\": \"'\",\n        }\n        \"S(CZ_BSLS)\": {\n            \"key\": \"CZ_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(CZ_COMM)\": {\n            \"key\": \"CZ_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(CZ_DOT)\": {\n            \"key\": \"CZ_COLN\",\n            \"label\": \":\",\n        }\n        \"S(CZ_MINS)\": {\n            \"key\": \"CZ_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ~ │   │ ^ │ ˘ │   │ ˛ │ ` │ ˙ │   │ ˝ │   │ ¸ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │   │ ÷ │ × │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │ đ │ Đ │ [ │ ] │   │   │ ł │ Ł │ $ │ ß │ ¤ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │ # │ & │ @ │ { │ } │   │ < │ > │ * │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(CZ_PLUS)\": {\n            \"key\": \"CZ_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(CZ_SCAR)\": {\n            \"key\": \"CZ_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"ALGR(CZ_CCAR)\": {\n            \"key\": \"CZ_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"ALGR(CZ_ZCAR)\": {\n            \"key\": \"CZ_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"ALGR(CZ_YACU)\": {\n            \"key\": \"CZ_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"ALGR(CZ_AACU)\": {\n            \"key\": \"CZ_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n        \"ALGR(CZ_EACU)\": {\n            \"key\": \"CZ_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"ALGR(CZ_ACUT)\": {\n            \"key\": \"CZ_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"ALGR(CZ_E)\": {\n            \"key\": \"CZ_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(CZ_UACU)\": {\n            \"key\": \"CZ_DIV\",\n            \"label\": \"÷\",\n        }\n        \"ALGR(CZ_RPRN)\": {\n            \"key\": \"CZ_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(CZ_S)\": {\n            \"key\": \"CZ_LDST\",\n            \"label\": \"đ\",\n        }\n        \"ALGR(CZ_D)\": {\n            \"key\": \"CZ_CDST\",\n            \"label\": \"Đ\",\n        }\n        \"ALGR(CZ_F)\": {\n            \"key\": \"CZ_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(CZ_G)\": {\n            \"key\": \"CZ_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(CZ_K)\": {\n            \"key\": \"CZ_LLST\",\n            \"label\": \"ł\",\n        }\n        \"ALGR(CZ_L)\": {\n            \"key\": \"CZ_CLST\",\n            \"label\": \"Ł\",\n        }\n        \"ALGR(CZ_URNG)\": {\n            \"key\": \"CZ_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(CZ_SECT)\": {\n            \"key\": \"CZ_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(CZ_DIAE)\": {\n            \"key\": \"CZ_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(CZ_X)\": {\n            \"key\": \"CZ_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(CZ_C)\": {\n            \"key\": \"CZ_AMPR\",\n            \"label\": \"&\",\n        }\n        \"ALGR(CZ_V)\": {\n            \"key\": \"CZ_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(CZ_B)\": {\n            \"key\": \"CZ_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(CZ_N)\": {\n            \"key\": \"CZ_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(CZ_COMM)\": {\n            \"key\": \"CZ_LABK\",\n            \"label\": \"<\",\n        }\n        \"ALGR(CZ_DOT)\": {\n            \"key\": \"CZ_RABK\",\n            \"label\": \">\",\n        }\n        \"ALGR(CZ_MINS)\": {\n            \"key\": \"CZ_ASTR\",\n            \"label\": \"*\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_czech_mac_ansi_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ \\ │ + │ ě │ š │ č │ ř │ ž │ ý │ á │ í │ é │ = │ ' │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ ú │ ) │ ¨ │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ů │ § │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n       \"KC_GRV\": {\n            \"key\": \"CZ_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_1\": {\n            \"key\": \"CZ_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_2\": {\n            \"key\": \"CZ_ECAR\",\n            \"label\": \"ě\",\n        }\n        \"KC_3\": {\n            \"key\": \"CZ_SCAR\",\n            \"label\": \"š\",\n        }\n        \"KC_4\": {\n            \"key\": \"CZ_CCAR\",\n            \"label\": \"č\",\n        }\n        \"KC_5\": {\n            \"key\": \"CZ_RCAR\",\n            \"label\": \"ř\",\n        }\n        \"KC_6\": {\n            \"key\": \"CZ_ZCAR\",\n            \"label\": \"ž\",\n        }\n        \"KC_7\": {\n            \"key\": \"CZ_YACU\",\n            \"label\": \"ý\",\n        }\n        \"KC_8\": {\n            \"key\": \"CZ_AACU\",\n            \"label\": \"á\",\n        }\n        \"KC_9\": {\n            \"key\": \"CZ_IACU\",\n            \"label\": \"í\",\n        }\n        \"KC_0\": {\n            \"key\": \"CZ_EACU\",\n            \"label\": \"é\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"CZ_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"CZ_ACUT\",\n            \"label\": \"' (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"CZ_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"CZ_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"CZ_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"CZ_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"CZ_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"CZ_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"CZ_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"CZ_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"CZ_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"CZ_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"CZ_UACU\",\n            \"label\": \"ú\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"CZ_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"CZ_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"CZ_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"CZ_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"CZ_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"CZ_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"CZ_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"CZ_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"CZ_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"CZ_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"CZ_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"CZ_URNG\",\n            \"label\": \"ů\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"CZ_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_Z\": {\n            \"key\": \"CZ_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"CZ_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"CZ_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"CZ_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"CZ_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"CZ_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"CZ_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"CZ_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"CZ_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"CZ_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ | │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ % │ ˇ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │   │   │   │   │ / │ ( │ ` │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │   │   │   │   │   │   │   │   │   │ \" │ ! │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │   │   │   │   │   │   │   │ ? │ : │ _ │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(CZ_BSLS)\": {\n            \"key\": \"CZ_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(CZ_PLUS)\": {\n            \"key\": \"CZ_1\",\n            \"label\": \"1\",\n        }\n        \"S(CZ_ECAR)\": {\n            \"key\": \"CZ_2\",\n            \"label\": \"2\",\n        }\n        \"S(CZ_SCAR)\": {\n            \"key\": \"CZ_3\",\n            \"label\": \"3\",\n        }\n        \"S(CZ_CCAR)\": {\n            \"key\": \"CZ_4\",\n            \"label\": \"4\",\n        }\n        \"S(CZ_RCAR)\": {\n            \"key\": \"CZ_5\",\n            \"label\": \"5\",\n        }\n        \"S(CZ_ZCAR)\": {\n            \"key\": \"CZ_6\",\n            \"label\": \"6\",\n        }\n        \"S(CZ_YACU)\": {\n            \"key\": \"CZ_7\",\n            \"label\": \"7\",\n        }\n        \"S(CZ_AACU)\": {\n            \"key\": \"CZ_8\",\n            \"label\": \"8\",\n        }\n        \"S(CZ_IACU)\": {\n            \"key\": \"CZ_9\",\n            \"label\": \"9\",\n        }\n        \"S(CZ_EACU)\": {\n            \"key\": \"CZ_0\",\n            \"label\": \"0\",\n        }\n        \"S(CZ_EQL)\": {\n            \"key\": \"CZ_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(CZ_ACUT)\": {\n            \"key\": \"CZ_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"S(CZ_UACU)\": {\n            \"key\": \"CZ_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(CZ_RPRN)\": {\n            \"key\": \"CZ_LPRN\",\n            \"label\": \"(\",\n        }\n         \"S(CZ_DIAE)\": {\n            \"key\": \"CZ_GRV\",\n            \"label\": \"`\",\n        }\n        \"S(CZ_URNG)\": {\n            \"key\": \"CZ_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(CZ_SECT)\": {\n            \"key\": \"CZ_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(CZ_COMM)\": {\n            \"key\": \"CZ_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(CZ_DOT)\": {\n            \"key\": \"CZ_COLN\",\n            \"label\": \":\",\n        }\n        \"S(CZ_MINS)\": {\n            \"key\": \"CZ_UNDS\",\n            \"label\": \"_\",\n        }\n/* Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │   │   │ @ │ # │ $ │ ~ │ ^ │ & │ * │ { │ } │ ° │ ^ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │ ė │ ę │ € │   │   │   │   │   │   │ [ │ ] │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │ ą │ ß │ ∂ │   │   │ ‘ │ ’ │   │ ł │ ; │ ' │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │   │   │   │   │   │ ‚ │   │ < │ > │ – │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"A(CZ_ECAR)\": {\n            \"key\": \"CZ_AT\",\n            \"label\": \"@\",\n        }\n        \"A(CZ_SCAR)\": {\n            \"key\": \"CZ_HASH\",\n            \"label\": \"#\",\n        }\n        \"A(CZ_CCAR)\": {\n            \"key\": \"CZ_DLR\",\n            \"label\": \"$\",\n        }\n        \"A(CZ_RCAR)\": {\n            \"key\": \"CZ_TILD\",\n            \"label\": \"~\",\n        }\n        \"A(CZ_ZCAR)\": {\n            \"key\": \"CZ_CIRC\",\n            \"label\": \"^\",\n        }\n        \"A(CZ_YACU)\": {\n            \"key\": \"CZ_AMPR\",\n            \"label\": \"&\",\n        }\n        \"A(CZ_AACU)\": {\n            \"key\": \"CZ_ASTR\",\n            \"label\": \"*\",\n        }\n        \"A(CZ_IACU)\": {\n            \"key\": \"CZ_LCBR\",\n            \"label\": \"{\",\n        }\n        \"A(CZ_EACU)\": {\n            \"key\": \"CZ_RCBR\",\n            \"label\": \"}\",\n        }\n        \"A(CZ_EQL)\": {\n            \"key\": \"CZ_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"A(CZ_ACUT)\": {\n            \"key\": \"CZ_DCIR\",\n            \"label\": \"^ (dead)\",\n        }\n        \"A(CZ_W)\": {\n            \"key\": \"CZ_LEDT\",\n            \"label\": \"ė\",\n        }\n        \"A(CZ_E)\": {\n            \"key\": \"CZ_LEOG\",\n            \"label\": \"ę\",\n        }\n        \"A(CZ_R)\": {\n            \"key\": \"CZ_EURO\",\n            \"label\": \"€\",\n        }\n        \"A(CZ_Z)\": {\n            \"key\": \"CZ_LZDT\",\n            \"label\": \"ż\",\n        }\n        \"A(CZ_UACU)\": {\n            \"key\": \"CZ_LBRC\",\n            \"label\": \"[\",\n        }\n        \"A(CZ_RPRN)\": {\n            \"key\": \"CZ_RBRC\",\n            \"label\": \"]\",\n        }\n        \"A(CZ_A)\": {\n            \"key\": \"CZ_LAOG\",\n            \"label\": \"ą\",\n        }\n        \"A(CZ_S)\": {\n            \"key\": \"CZ_SS\",\n            \"label\": \"ß\",\n        }\n        \"A(CZ_D)\": {\n            \"key\": \"CZ_PDIF\",\n            \"label\": \"∂\",\n        }\n        \"A(CZ_H)\": {\n            \"key\": \"CZ_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"A(CZ_J)\": {\n            \"key\": \"CZ_RSQU\",\n            \"label\": \"’\",\n        }\n        \"A(CZ_L)\": {\n            \"key\": \"CZ_LLST\",\n            \"label\": \"ł\",\n        }\n        \"A(CZ_URNG)\": {\n            \"key\": \"CZ_SCLN\",\n            \"label\": \";\",\n        }\n        \"A(CZ_SECT)\": {\n            \"key\": \"CZ_QUOT\",\n            \"label\": \"'\",\n        }\n        \"A(CZ_N)\": {\n            \"key\": \"CZ_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"A(CZ_COMM)\": {\n            \"key\": \"CZ_LABK\",\n            \"label\": \"<\",\n        }\n        \"A(CZ_DOT)\": {\n            \"key\": \"CZ_RABK\",\n            \"label\": \">\",\n        }\n        \"A(CZ_MINS)\": {\n            \"key\": \"CZ_NDSH\",\n            \"label\": \"–\",\n        }\n/* Shift+Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │   │ ¬ │ • │ ≠ │ £ │ ◊ │ † │ ¶ │ ÷ │ « │ » │ , │ - │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │ Ė │ Ę │ ® │ ™ │ Ż │   │   │   │   │ ‹ │ › │ \" │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │ Ą │ ∑ │ ∆ │   │   │ “ │ ” │   │ Ł │ … │ ~ │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │   │   │ © │ √ │   │ „ │   │ ≤ │ ≥ │ — │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(A(CZ_1))\": {\n            \"key\": \"CZ_NOT\",\n            \"label\": \"¬\",\n        }\n        \"S(A(CZ_2))\": {\n            \"key\": \"CZ_BULT\",\n            \"label\": \"•\",\n        }\n        \"S(A(CZ_3))\": {\n            \"key\": \"CZ_NEQL\",\n            \"label\": \"≠\",\n        }\n        \"S(A(CZ_4))\": {\n            \"key\": \"CZ_PND\",\n            \"label\": \"£\",\n        }\n        \"S(A(CZ_5))\": {\n            \"key\": \"CZ_LOZN\",\n            \"label\": \"◊\",\n        }\n        \"S(A(CZ_6))\": {\n            \"key\": \"CZ_DAGG\",\n            \"label\": \"†\",\n        }\n        \"S(A(CZ_7))\": {\n            \"key\": \"CZ_PARA\",\n            \"label\": \"¶\",\n        }\n        \"S(A(CZ_8))\": {\n            \"key\": \"CZ_DIV\",\n            \"label\": \"÷\",\n        }\n        \"S(A(CZ_9))\": {\n            \"key\": \"CZ_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"S(A(CZ_0))\": {\n            \"key\": \"CZ_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(A(CZ_EQL))\": {\n            \"key\": \"CZ_DCOM\",\n            \"label\": \", (dead)\",\n        }\n        \"S(A(CZ_ACUT))\": {\n            \"key\": \"CZ_DHPN\",\n            \"label\": \"- (dead)\",\n        }\n        \"S(A(CZ_W))\": {\n            \"key\": \"CZ_CEDT\",\n            \"label\": \"Ė\",\n        }\n        \"S(A(CZ_E))\": {\n            \"key\": \"CZ_CEOG\",\n            \"label\": \"Ę\",\n        }\n        \"S(A(CZ_R))\": {\n            \"key\": \"CZ_REGD\",\n            \"label\": \"®\",\n        }\n        \"S(A(CZ_T))\": {\n            \"key\": \"CZ_TM\",\n            \"label\": \"™\",\n        }\n        \"S(A(CZ_Z))\": {\n            \"key\": \"CZ_CZDT\",\n            \"label\": \"Ż\",\n        }\n        \"S(A(CZ_UACU))\": {\n            \"key\": \"CZ_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"S(A(CZ_RPRN))\": {\n            \"key\": \"CZ_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"S(A(CZ_DIAE))\": {\n            \"key\": \"CZ_DDQT\",\n            \"label\": \"\\\" (dead)\",\n        }\n        \"S(A(CZ_A))\": {\n            \"key\": \"CZ_CAOG\",\n            \"label\": \"Ą\",\n        }\n        \"S(A(CZ_S))\": {\n            \"key\": \"CZ_NARS\",\n            \"label\": \"∑\",\n        }\n        \"S(A(CZ_D))\": {\n            \"key\": \"CZ_INCR\",\n            \"label\": \"∆\",\n        }\n        \"S(A(CZ_H))\": {\n            \"key\": \"CZ_LDQU\",\n            \"label\": \"“\",\n        }\n        \"S(A(CZ_J))\": {\n            \"key\": \"CZ_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(A(CZ_L))\": {\n            \"key\": \"CZ_CLST\",\n            \"label\": \"Ł\",\n        }\n        \"S(A(CZ_URNG))\": {\n            \"key\": \"CZ_ELLP\",\n            \"label\": \"…\",\n        }\n        \"S(A(CZ_SECT))\": {\n            \"key\": \"CZ_DTIL\",\n            \"label\": \"~ (dead)\",\n        }\n        \"S(A(CZ_C))\": {\n            \"key\": \"CZ_COPY\",\n            \"label\": \"©\",\n        }\n        \"S(A(CZ_V))\": {\n            \"key\": \"CZ_SQRT\",\n            \"label\": \"√\",\n        }\n        \"S(A(CZ_N))\": {\n            \"key\": \"CZ_DLQU\",\n            \"label\": \"„\",\n        }\n        \"S(A(CZ_COMM))\": {\n            \"key\": \"CZ_LEQL\",\n            \"label\": \"≤\",\n        }\n        \"S(A(CZ_DOT))\": {\n            \"key\": \"CZ_GEQL\",\n            \"label\": \"≥\",\n        }\n        \"S(A(CZ_MINS))\": {\n            \"key\": \"CZ_MDSH\",\n            \"label\": \"—\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_czech_mac_iso_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │   │ + │ ě │ š │ č │ ř │ ž │ ý │ á │ í │ é │ = │ ' │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ ú │ ) │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ů │ § │ ¨ │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ \\ │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"KC_1\": {\n            \"key\": \"CZ_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_2\": {\n            \"key\": \"CZ_ECAR\",\n            \"label\": \"ě\",\n        }\n        \"KC_3\": {\n            \"key\": \"CZ_SCAR\",\n            \"label\": \"š\",\n        }\n        \"KC_4\": {\n            \"key\": \"CZ_CCAR\",\n            \"label\": \"č\",\n        }\n        \"KC_5\": {\n            \"key\": \"CZ_RCAR\",\n            \"label\": \"ř\",\n        }\n        \"KC_6\": {\n            \"key\": \"CZ_ZCAR\",\n            \"label\": \"ž\",\n        }\n        \"KC_7\": {\n            \"key\": \"CZ_YACU\",\n            \"label\": \"ý\",\n        }\n        \"KC_8\": {\n            \"key\": \"CZ_AACU\",\n            \"label\": \"á\",\n        }\n        \"KC_9\": {\n            \"key\": \"CZ_IACU\",\n            \"label\": \"í\",\n        }\n        \"KC_0\": {\n            \"key\": \"CZ_EACU\",\n            \"label\": \"é\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"CZ_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"CZ_ACUT\",\n            \"label\": \"' (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"CZ_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"CZ_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"CZ_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"CZ_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"CZ_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"CZ_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"CZ_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"CZ_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"CZ_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"CZ_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"CZ_UACU\",\n            \"label\": \"ú\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"CZ_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_A\": {\n            \"key\": \"CZ_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"CZ_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"CZ_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"CZ_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"CZ_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"CZ_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"CZ_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"CZ_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"CZ_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"CZ_URNG\",\n            \"label\": \"ů\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"CZ_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"CZ_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"CZ_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_Z\": {\n            \"key\": \"CZ_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"CZ_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"CZ_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"CZ_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"CZ_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"CZ_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"CZ_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"CZ_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"CZ_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"CZ_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │   │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ % │ ˇ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │   │   │   │   │ / │ ( │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │   │   │   │   │   │   │   │   │   │ \" │ ! │ ` │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ | │   │   │   │   │   │   │   │ ? │ : │ _ │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(CZ_PLUS)\": {\n            \"key\": \"CZ_1\",\n            \"label\": \"1\",\n        }\n        \"S(CZ_ECAR)\": {\n            \"key\": \"CZ_2\",\n            \"label\": \"2\",\n        }\n        \"S(CZ_SCAR)\": {\n            \"key\": \"CZ_3\",\n            \"label\": \"3\",\n        }\n        \"S(CZ_CCAR)\": {\n            \"key\": \"CZ_4\",\n            \"label\": \"4\",\n        }\n        \"S(CZ_RCAR)\": {\n            \"key\": \"CZ_5\",\n            \"label\": \"5\",\n        }\n        \"S(CZ_ZCAR)\": {\n            \"key\": \"CZ_6\",\n            \"label\": \"6\",\n        }\n        \"S(CZ_YACU)\": {\n            \"key\": \"CZ_7\",\n            \"label\": \"7\",\n        }\n        \"S(CZ_AACU)\": {\n            \"key\": \"CZ_8\",\n            \"label\": \"8\",\n        }\n        \"S(CZ_IACU)\": {\n            \"key\": \"CZ_9\",\n            \"label\": \"9\",\n        }\n        \"S(CZ_EACU)\": {\n            \"key\": \"CZ_0\",\n            \"label\": \"0\",\n        }\n        \"S(CZ_EQL)\": {\n            \"key\": \"CZ_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(CZ_ACUT)\": {\n            \"key\": \"CZ_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"S(CZ_UACU)\": {\n            \"key\": \"CZ_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(CZ_RPRN)\": {\n            \"key\": \"CZ_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(CZ_URNG)\": {\n            \"key\": \"CZ_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(CZ_SECT)\": {\n            \"key\": \"CZ_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(CZ_DIAE)\": {\n            \"key\": \"CZ_GRV\",\n            \"label\": \"`\",\n        }\n        \"S(CZ_BSLS)\": {\n            \"key\": \"CZ_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(CZ_COMM)\": {\n            \"key\": \"CZ_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(CZ_DOT)\": {\n            \"key\": \"CZ_COLN\",\n            \"label\": \":\",\n        }\n        \"S(CZ_MINS)\": {\n            \"key\": \"CZ_UNDS\",\n            \"label\": \"_\",\n        }\n/* Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │   │   │ @ │ # │ $ │ ~ │ ^ │ & │ * │ { │ } │ ° │ ^ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │ ė │ ę │ € │   │ ż │   │   │   │   │ [ │ ] │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ ą │ ß │ ∂ │   │   │ ‘ │ ’ │   │ ł │ ; │ ' │   │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │   │   │   │   │   │   │ ‚ │   │ < │ > │ – │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"A(CZ_ECAR)\": {\n            \"key\": \"CZ_AT\",\n            \"label\": \"@\",\n        }\n        \"A(CZ_SCAR)\": {\n            \"key\": \"CZ_HASH\",\n            \"label\": \"#\",\n        }\n        \"A(CZ_CCAR)\": {\n            \"key\": \"CZ_DLR\",\n            \"label\": \"$\",\n        }\n        \"A(CZ_RCAR)\": {\n            \"key\": \"CZ_TILD\",\n            \"label\": \"~\",\n        }\n        \"A(CZ_ZCAR)\": {\n            \"key\": \"CZ_CIRC\",\n            \"label\": \"^\",\n        }\n        \"A(CZ_YACU)\": {\n            \"key\": \"CZ_AMPR\",\n            \"label\": \"&\",\n        }\n        \"A(CZ_AACU)\": {\n            \"key\": \"CZ_ASTR\",\n            \"label\": \"*\",\n        }\n        \"A(CZ_IACU)\": {\n            \"key\": \"CZ_LCBR\",\n            \"label\": \"{\",\n        }\n        \"A(CZ_EACU)\": {\n            \"key\": \"CZ_RCBR\",\n            \"label\": \"}\",\n        }\n        \"A(CZ_EQL)\": {\n            \"key\": \"CZ_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"A(CZ_ACUT)\": {\n            \"key\": \"CZ_DCIR\",\n            \"label\": \"^ (dead)\",\n        }\n        \"A(CZ_W)\": {\n            \"key\": \"CZ_LEDT\",\n            \"label\": \"ė\",\n        }\n        \"A(CZ_E)\": {\n            \"key\": \"CZ_LEOG\",\n            \"label\": \"ę\",\n        }\n        \"A(CZ_R)\": {\n            \"key\": \"CZ_EURO\",\n            \"label\": \"€\",\n        }\n        \"A(CZ_Z)\": {\n            \"key\": \"CZ_LZDT\",\n            \"label\": \"ż\",\n        }\n        \"A(CZ_UACU)\": {\n            \"key\": \"CZ_LBRC\",\n            \"label\": \"[\",\n        }\n        \"A(CZ_RPRN)\": {\n            \"key\": \"CZ_RBRC\",\n            \"label\": \"]\",\n        }\n        \"A(CZ_A)\": {\n            \"key\": \"CZ_LAOG\",\n            \"label\": \"ą\",\n        }\n        \"A(CZ_S)\": {\n            \"key\": \"CZ_SS\",\n            \"label\": \"ß\",\n        }\n        \"A(CZ_D)\": {\n            \"key\": \"CZ_PDIF\",\n            \"label\": \"∂\",\n        }\n        \"A(CZ_H)\": {\n            \"key\": \"CZ_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"A(CZ_J)\": {\n            \"key\": \"CZ_RSQU\",\n            \"label\": \"’\",\n        }\n        \"A(CZ_L)\": {\n            \"key\": \"CZ_LLST\",\n            \"label\": \"ł\",\n        }\n        \"A(CZ_URNG)\": {\n            \"key\": \"CZ_SCLN\",\n            \"label\": \";\",\n        }\n        \"A(CZ_SECT)\": {\n            \"key\": \"CZ_QUOT\",\n            \"label\": \"'\",\n        }\n        \"A(CZ_N)\": {\n            \"key\": \"CZ_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"A(CZ_COMM)\": {\n            \"key\": \"CZ_LABK\",\n            \"label\": \"<\",\n        }\n        \"A(CZ_DOT)\": {\n            \"key\": \"CZ_RABK\",\n            \"label\": \">\",\n        }\n        \"A(CZ_MINS)\": {\n            \"key\": \"CZ_NDSH\",\n            \"label\": \"–\",\n        }\n/* Shift+Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │   │ ¬ │ • │ ≠ │ £ │ ◊ │ † │ ¶ │ ÷ │ « │ » │ , │ - │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │ Ė │ Ę │ ® │ ™ │ Ż │   │   │   │   │ ‹ │ › │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ Ą │ ∑ │ ∆ │   │   │ “ │ ” │   │ Ł │ … │ ~ │ \" │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │   │   │   │ © │ √ │   │ „ │   │ ≤ │ ≥ │ — │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(A(CZ_1))\": {\n            \"key\": \"CZ_NOT\",\n            \"label\": \"¬\",\n        }\n        \"S(A(CZ_2))\": {\n            \"key\": \"CZ_BULT\",\n            \"label\": \"•\",\n        }\n        \"S(A(CZ_3))\": {\n            \"key\": \"CZ_NEQL\",\n            \"label\": \"≠\",\n        }\n        \"S(A(CZ_4))\": {\n            \"key\": \"CZ_PND\",\n            \"label\": \"£\",\n        }\n        \"S(A(CZ_5))\": {\n            \"key\": \"CZ_LOZN\",\n            \"label\": \"◊\",\n        }\n        \"S(A(CZ_6))\": {\n            \"key\": \"CZ_DAGG\",\n            \"label\": \"†\",\n        }\n        \"S(A(CZ_7))\": {\n            \"key\": \"CZ_PARA\",\n            \"label\": \"¶\",\n        }\n        \"S(A(CZ_8))\": {\n            \"key\": \"CZ_DIV\",\n            \"label\": \"÷\",\n        }\n        \"S(A(CZ_9))\": {\n            \"key\": \"CZ_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"S(A(CZ_0))\": {\n            \"key\": \"CZ_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(A(CZ_EQL))\": {\n            \"key\": \"CZ_DCOM\",\n            \"label\": \", (dead)\",\n        }\n        \"S(A(CZ_ACUT))\": {\n            \"key\": \"CZ_DHPN\",\n            \"label\": \"- (dead)\",\n        }\n        \"S(A(CZ_W))\": {\n            \"key\": \"CZ_CEDT\",\n            \"label\": \"Ė\",\n        }\n        \"S(A(CZ_E))\": {\n            \"key\": \"CZ_CEOG\",\n            \"label\": \"Ę\",\n        }\n        \"S(A(CZ_R))\": {\n            \"key\": \"CZ_REGD\",\n            \"label\": \"®\",\n        }\n        \"S(A(CZ_T))\": {\n            \"key\": \"CZ_TM\",\n            \"label\": \"™\",\n        }\n        \"S(A(CZ_Z))\": {\n            \"key\": \"CZ_CZDT\",\n            \"label\": \"Ż\",\n        }\n        \"S(A(CZ_UACU))\": {\n            \"key\": \"CZ_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"S(A(CZ_RPRN))\": {\n            \"key\": \"CZ_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"S(A(CZ_A))\": {\n            \"key\": \"CZ_CAOG\",\n            \"label\": \"Ą\",\n        }\n        \"S(A(CZ_S))\": {\n            \"key\": \"CZ_NARS\",\n            \"label\": \"∑\",\n        }\n        \"S(A(CZ_D))\": {\n            \"key\": \"CZ_INCR\",\n            \"label\": \"∆\",\n        }\n        \"S(A(CZ_H))\": {\n            \"key\": \"CZ_LDQU\",\n            \"label\": \"“\",\n        }\n        \"S(A(CZ_J))\": {\n            \"key\": \"CZ_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(A(CZ_L))\": {\n            \"key\": \"CZ_CLST\",\n            \"label\": \"Ł\",\n        }\n        \"S(A(CZ_URNG))\": {\n            \"key\": \"CZ_ELLP\",\n            \"label\": \"…\",\n        }\n        \"S(A(CZ_SECT))\": {\n            \"key\": \"CZ_DTIL\",\n            \"label\": \"~ (dead)\",\n        }\n        \"S(A(CZ_DIAE))\": {\n            \"key\": \"CZ_DDQT\",\n            \"label\": \"\\\" (dead)\",\n        }\n        \"S(A(CZ_C))\": {\n            \"key\": \"CZ_COPY\",\n            \"label\": \"©\",\n        }\n        \"S(A(CZ_V))\": {\n            \"key\": \"CZ_SQRT\",\n            \"label\": \"√\",\n        }\n        \"S(A(CZ_N))\": {\n            \"key\": \"CZ_DLQU\",\n            \"label\": \"„\",\n        }\n        \"S(A(CZ_COMM))\": {\n            \"key\": \"CZ_LEQL\",\n            \"label\": \"≤\",\n        }\n        \"S(A(CZ_DOT))\": {\n            \"key\": \"CZ_GEQL\",\n            \"label\": \"≥\",\n        }\n        \"S(A(CZ_MINS))\": {\n            \"key\": \"CZ_MDSH\",\n            \"label\": \"—\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_danish_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ½ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ + │ ´ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Å │ ¨ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Æ │ Ø │ ' │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"DK_HALF\",\n            \"label\": \"½\",\n        }\n        \"KC_1\": {\n            \"key\": \"DK_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"DK_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"DK_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"DK_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"DK_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"DK_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"DK_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"DK_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"DK_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"DK_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"DK_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"DK_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"DK_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"DK_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"DK_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"DK_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"DK_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"DK_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"DK_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"DK_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"DK_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"DK_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"DK_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"DK_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"DK_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"DK_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"DK_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"DK_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"DK_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"DK_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"DK_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"DK_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"DK_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"DK_AE\",\n            \"label\": \"Æ\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"DK_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"DK_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"DK_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"DK_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"DK_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"DK_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"DK_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"DK_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"DK_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"DK_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"DK_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"DK_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"DK_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ § │ ! │ \" │ # │ ¤ │ % │ & │ / │ ( │ ) │ = │ ? │ ` │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ^ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ * │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(DK_HALF)\": {\n            \"key\": \"DK_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(DK_1)\": {\n            \"key\": \"DK_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(DK_2)\": {\n            \"key\": \"DK_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(DK_3)\": {\n            \"key\": \"DK_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(DK_4)\": {\n            \"key\": \"DK_CURR\",\n            \"label\": \"¤\",\n        }\n        \"S(DK_5)\": {\n            \"key\": \"DK_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(DK_6)\": {\n            \"key\": \"DK_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(DK_7)\": {\n            \"key\": \"DK_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(DK_8)\": {\n            \"key\": \"DK_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(DK_9)\": {\n            \"key\": \"DK_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(DK_0)\": {\n            \"key\": \"DK_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(DK_PLUS)\": {\n            \"key\": \"DK_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(DK_ACUT)\": {\n            \"key\": \"DK_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(DK_DIAE)\": {\n            \"key\": \"DK_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(DK_QUOT)\": {\n            \"key\": \"DK_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(DK_LABK)\": {\n            \"key\": \"DK_RABK\",\n            \"label\": \">\",\n        }\n        \"S(DK_COMM)\": {\n            \"key\": \"DK_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(DK_DOT)\": {\n            \"key\": \"DK_COLN\",\n            \"label\": \":\",\n        }\n        \"S(DK_MINS)\": {\n            \"key\": \"DK_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │ @ │ £ │ $ │ € │   │ { │ [ │ ] │ } │   │ | │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ~ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ \\ │   │   │   │   │   │   │ µ │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(DK_2)\": {\n            \"key\": \"DK_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(DK_3)\": {\n            \"key\": \"DK_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(DK_4)\": {\n            \"key\": \"DK_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(DK_5)\": {\n            \"key\": \"DK_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(DK_7)\": {\n            \"key\": \"DK_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(DK_8)\": {\n            \"key\": \"DK_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(DK_9)\": {\n            \"key\": \"DK_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(DK_0)\": {\n            \"key\": \"DK_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(DK_ACUT)\": {\n            \"key\": \"DK_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(DK_DIAE)\": {\n            \"key\": \"DK_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(DK_LABK)\": {\n            \"key\": \"DK_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(DK_M)\": {\n            \"key\": \"DK_MICR\",\n            \"label\": \"µ\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_dvorak_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ [ │ ] │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ ' │ , │ . │ P │ Y │ F │ G │ C │ R │ L │ / │ = │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ O │ E │ U │ I │ D │ H │ T │ N │ S │ - │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ ; │ Q │ J │ K │ X │ B │ M │ W │ V │ Z │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"DV_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"DV_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"DV_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"DV_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"DV_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"DV_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"DV_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"DV_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"DV_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"DV_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"DV_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"DV_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"DV_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_Q\": {\n            \"key\": \"DV_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_W\": {\n            \"key\": \"DV_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_E\": {\n            \"key\": \"DV_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_R\": {\n            \"key\": \"DV_P\",\n            \"label\": \"P\",\n        }\n        \"KC_T\": {\n            \"key\": \"DV_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_Y\": {\n            \"key\": \"DV_F\",\n            \"label\": \"F\",\n        }\n        \"KC_U\": {\n            \"key\": \"DV_G\",\n            \"label\": \"G\",\n        }\n        \"KC_I\": {\n            \"key\": \"DV_C\",\n            \"label\": \"C\",\n        }\n        \"KC_O\": {\n            \"key\": \"DV_R\",\n            \"label\": \"R\",\n        }\n        \"KC_P\": {\n            \"key\": \"DV_L\",\n            \"label\": \"L\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"DV_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"DV_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"DV_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"DV_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"DV_O\",\n            \"label\": \"O\",\n        }\n        \"KC_D\": {\n            \"key\": \"DV_E\",\n            \"label\": \"E\",\n        }\n        \"KC_F\": {\n            \"key\": \"DV_U\",\n            \"label\": \"U\",\n        }\n        \"KC_G\": {\n            \"key\": \"DV_I\",\n            \"label\": \"I\",\n        }\n        \"KC_H\": {\n            \"key\": \"DV_D\",\n            \"label\": \"D\",\n        }\n        \"KC_J\": {\n            \"key\": \"DV_H\",\n            \"label\": \"H\",\n        }\n        \"KC_K\": {\n            \"key\": \"DV_T\",\n            \"label\": \"T\",\n        }\n        \"KC_L\": {\n            \"key\": \"DV_N\",\n            \"label\": \"N\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"DV_S\",\n            \"label\": \"S\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"DV_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_Z\": {\n            \"key\": \"DV_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_X\": {\n            \"key\": \"DV_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_C\": {\n            \"key\": \"DV_J\",\n            \"label\": \"J\",\n        }\n        \"KC_V\": {\n            \"key\": \"DV_K\",\n            \"label\": \"K\",\n        }\n        \"KC_B\": {\n            \"key\": \"DV_X\",\n            \"label\": \"X\",\n        }\n        \"KC_N\": {\n            \"key\": \"DV_B\",\n            \"label\": \"B\",\n        }\n        \"KC_M\": {\n            \"key\": \"DV_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"DV_W\",\n            \"label\": \"W\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"DV_V\",\n            \"label\": \"V\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"DV_Z\",\n            \"label\": \"Z\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ { │ } │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ \" │ < │ > │   │   │   │   │   │   │   │ ? │ + │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │ _ │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ : │   │   │   │   │   │   │   │   │   │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(DV_GRV)\": {\n            \"key\": \"DV_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(DV_1)\": {\n            \"key\": \"DV_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(DV_2)\": {\n            \"key\": \"DV_AT\",\n            \"label\": \"@\",\n        }\n        \"S(DV_3)\": {\n            \"key\": \"DV_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(DV_4)\": {\n            \"key\": \"DV_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(DV_5)\": {\n            \"key\": \"DV_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(DV_6)\": {\n            \"key\": \"DV_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(DV_7)\": {\n            \"key\": \"DV_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(DV_8)\": {\n            \"key\": \"DV_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(DV_9)\": {\n            \"key\": \"DV_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(DV_0)\": {\n            \"key\": \"DV_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(DV_LBRC)\": {\n            \"key\": \"DV_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(DV_RBRC)\": {\n            \"key\": \"DV_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(DV_QUOT)\": {\n            \"key\": \"DV_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(DV_COMM)\": {\n            \"key\": \"DV_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(DV_DOT)\": {\n            \"key\": \"DV_RABK\",\n            \"label\": \">\",\n        }\n        \"S(DV_SLSH)\": {\n            \"key\": \"DV_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(DV_EQL)\": {\n            \"key\": \"DV_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(DV_BSLS)\": {\n            \"key\": \"DV_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(DV_MINS)\": {\n            \"key\": \"DV_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(DV_SCLN)\": {\n            \"key\": \"DV_COLN\",\n            \"label\": \":\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_dvorak_fr_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/* Dvorak for the French language\n * Version: 2\n *\n * The layout is designed by Francis Leboutte <dvorak-fr@algo.be>\n *\n * Source: https://algo.be/ergo/dvorak-fr.html\n */\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ « │ » │ / │ - │ è │ \\ │ ^ │ ( │ ` │ ) │ _ │ [ │ ] │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ : │ ' │ é │ G │ . │ H │ V │ C │ M │ K │ Z │ ¨ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ O │ A │ U │ E │ B │ F │ S │ T │ N │ D │ W │ ~ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ à │ ; │ Q │ , │ I │ Y │ X │ R │ L │ P │ J │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"DV_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"KC_1\": {\n            \"key\": \"DV_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"KC_2\": {\n            \"key\": \"DV_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_3\": {\n            \"key\": \"DV_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_4\": {\n            \"key\": \"DV_EGRV\",\n            \"label\": \"è\",\n        }\n        \"KC_5\": {\n            \"key\": \"DV_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_6\": {\n            \"key\": \"DV_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_7\": {\n            \"key\": \"DV_LPRN\",\n            \"label\": \"(\",\n        }\n        \"KC_8\": {\n            \"key\": \"DV_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"KC_9\": {\n            \"key\": \"DV_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_0\": {\n            \"key\": \"DV_UNDS\",\n            \"label\": \"_\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"DV_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"DV_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_Q\": {\n            \"key\": \"DV_COLN\",\n            \"label\": \":\",\n        }\n        \"KC_W\": {\n            \"key\": \"DV_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_E\": {\n            \"key\": \"DV_EACU\",\n            \"label\": \"é\",\n        }\n        \"KC_R\": {\n            \"key\": \"DV_G\",\n            \"label\": \"G\",\n        }\n        \"KC_T\": {\n            \"key\": \"DV_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_Y\": {\n            \"key\": \"DV_H\",\n            \"label\": \"H\",\n        }\n        \"KC_U\": {\n            \"key\": \"DV_V\",\n            \"label\": \"V\",\n        }\n        \"KC_I\": {\n            \"key\": \"DV_C\",\n            \"label\": \"C\",\n        }\n        \"KC_O\": {\n            \"key\": \"DV_M\",\n            \"label\": \"M\",\n        }\n        \"KC_P\": {\n            \"key\": \"DV_K\",\n            \"label\": \"K\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"DV_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"DV_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"DV_O\",\n            \"label\": \"O\",\n        }\n        \"KC_S\": {\n            \"key\": \"DV_A\",\n            \"label\": \"A\",\n        }\n        \"KC_D\": {\n            \"key\": \"DV_U\",\n            \"label\": \"U\",\n        }\n        \"KC_F\": {\n            \"key\": \"DV_E\",\n            \"label\": \"E\",\n        }\n        \"KC_G\": {\n            \"key\": \"DV_B\",\n            \"label\": \"B\",\n        }\n        \"KC_H\": {\n            \"key\": \"DV_F\",\n            \"label\": \"F\",\n        }\n        \"KC_J\": {\n            \"key\": \"DV_S\",\n            \"label\": \"S\",\n        }\n        \"KC_K\": {\n            \"key\": \"DV_T\",\n            \"label\": \"T\",\n        }\n        \"KC_L\": {\n            \"key\": \"DV_N\",\n            \"label\": \"N\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"DV_D\",\n            \"label\": \"D\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"DV_W\",\n            \"label\": \"W\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"DV_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"DV_AGRV\",\n            \"label\": \"à\",\n        }\n        \"KC_Z\": {\n            \"key\": \"DV_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_X\": {\n            \"key\": \"DV_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_C\": {\n            \"key\": \"DV_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_V\": {\n            \"key\": \"DV_I\",\n            \"label\": \"I\",\n        }\n        \"KC_B\": {\n            \"key\": \"DV_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_N\": {\n            \"key\": \"DV_X\",\n            \"label\": \"X\",\n        }\n        \"KC_M\": {\n            \"key\": \"DV_R\",\n            \"label\": \"R\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"DV_L\",\n            \"label\": \"L\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"DV_P\",\n            \"label\": \"P\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"DV_J\",\n            \"label\": \"J\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ * │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 0 │ 0 │ + │ % │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ ? │ < │ > │   │ ! │   │   │   │   │   │   │ = │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ # │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ ç │ | │   │ @ │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(DV_LDAQ)\": {\n            \"key\": \"DV_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(DV_RDAQ)\": {\n            \"key\": \"DV_1\",\n            \"label\": \"1\",\n        }\n        \"S(DV_SLSH)\": {\n            \"key\": \"DV_2\",\n            \"label\": \"2\",\n        }\n        \"S(DV_MINS)\": {\n            \"key\": \"DV_3\",\n            \"label\": \"3\",\n        }\n        \"S(DV_EGRV)\": {\n            \"key\": \"DV_4\",\n            \"label\": \"4\",\n        }\n        \"S(DV_BSLS)\": {\n            \"key\": \"DV_5\",\n            \"label\": \"5\",\n        }\n        \"S(DV_CIRC)\": {\n            \"key\": \"DV_6\",\n            \"label\": \"6\",\n        }\n        \"S(DV_LPRN)\": {\n            \"key\": \"DV_7\",\n            \"label\": \"7\",\n        }\n        \"S(DV_GRV)\": {\n            \"key\": \"DV_8\",\n            \"label\": \"8\",\n        }\n        \"S(DV_RPRN)\": {\n            \"key\": \"DV_9\",\n            \"label\": \"9\",\n        }\n        \"S(DV_UNDS)\": {\n            \"key\": \"DV_0\",\n            \"label\": \"0\",\n        }\n        \"S(DV_LBRC)\": {\n            \"key\": \"DV_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(DV_RBRC)\": {\n            \"key\": \"DV_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(DV_COLN)\": {\n            \"key\": \"DV_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(DV_QUOT)\": {\n            \"key\": \"DV_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(DV_EACU)\": {\n            \"key\": \"DV_RABK\",\n            \"label\": \">\",\n        }\n        \"S(DV_DOT)\": {\n            \"key\": \"DV_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(DV_DIAE)\": {\n            \"key\": \"DV_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(DV_TILD)\": {\n            \"key\": \"DV_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(DV_AGRV)\": {\n            \"key\": \"DV_CCED\",\n            \"label\": \"ç\",\n        }\n        \"S(DV_SCLN)\": {\n            \"key\": \"DV_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(DV_COMM)\": {\n            \"key\": \"DV_AT\",\n            \"label\": \"@\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_dvorak_programmer_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ $ │ & │ [ │ { │ } │ ( │ = │ * │ ) │ + │ ] │ ! │ # │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ ; │ , │ . │ P │ Y │ F │ G │ C │ R │ L │ / │ @ │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ O │ E │ U │ I │ D │ H │ T │ N │ S │ - │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ ' │ Q │ J │ K │ X │ B │ M │ W │ V │ Z │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"DP_DLR\",\n            \"label\": \"$\",\n        }\n        \"KC_1\": {\n            \"key\": \"DP_AMPR\",\n            \"label\": \"&\",\n        }\n        \"KC_2\": {\n            \"key\": \"DP_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_3\": {\n            \"key\": \"DP_LCBR\",\n            \"label\": \"{\",\n        }\n        \"KC_4\": {\n            \"key\": \"DP_RCBR\",\n            \"label\": \"}\",\n        }\n        \"KC_5\": {\n            \"key\": \"DP_LPRN\",\n            \"label\": \"(\",\n        }\n        \"KC_6\": {\n            \"key\": \"DP_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_7\": {\n            \"key\": \"DP_ASTR\",\n            \"label\": \"*\",\n        }\n        \"KC_8\": {\n            \"key\": \"DP_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_9\": {\n            \"key\": \"DP_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_0\": {\n            \"key\": \"DP_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"DP_EXLM\",\n            \"label\": \"!\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"DP_HASH\",\n            \"label\": \"#\",\n        }\n        \"KC_Q\": {\n            \"key\": \"DP_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_W\": {\n            \"key\": \"DP_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_E\": {\n            \"key\": \"DP_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_R\": {\n            \"key\": \"DP_P\",\n            \"label\": \"P\",\n        }\n        \"KC_T\": {\n            \"key\": \"DP_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_Y\": {\n            \"key\": \"DP_F\",\n            \"label\": \"F\",\n        }\n        \"KC_U\": {\n            \"key\": \"DP_G\",\n            \"label\": \"G\",\n        }\n        \"KC_I\": {\n            \"key\": \"DP_C\",\n            \"label\": \"C\",\n        }\n        \"KC_O\": {\n            \"key\": \"DP_R\",\n            \"label\": \"R\",\n        }\n        \"KC_P\": {\n            \"key\": \"DP_L\",\n            \"label\": \"L\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"DP_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"DP_AT\",\n            \"label\": \"@\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"DP_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"DP_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"DP_O\",\n            \"label\": \"O\",\n        }\n        \"KC_D\": {\n            \"key\": \"DP_E\",\n            \"label\": \"E\",\n        }\n        \"KC_F\": {\n            \"key\": \"DP_U\",\n            \"label\": \"U\",\n        }\n        \"KC_G\": {\n            \"key\": \"DP_I\",\n            \"label\": \"I\",\n        }\n        \"KC_H\": {\n            \"key\": \"DP_D\",\n            \"label\": \"D\",\n        }\n        \"KC_J\": {\n            \"key\": \"DP_H\",\n            \"label\": \"H\",\n        }\n        \"KC_K\": {\n            \"key\": \"DP_T\",\n            \"label\": \"T\",\n        }\n        \"KC_L\": {\n            \"key\": \"DP_N\",\n            \"label\": \"N\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"DP_S\",\n            \"label\": \"S\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"DP_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_Z\": {\n            \"key\": \"DP_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_X\": {\n            \"key\": \"DP_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_C\": {\n            \"key\": \"DP_J\",\n            \"label\": \"J\",\n        }\n        \"KC_V\": {\n            \"key\": \"DP_K\",\n            \"label\": \"K\",\n        }\n        \"KC_B\": {\n            \"key\": \"DP_X\",\n            \"label\": \"X\",\n        }\n        \"KC_N\": {\n            \"key\": \"DP_B\",\n            \"label\": \"B\",\n        }\n        \"KC_M\": {\n            \"key\": \"DP_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"DP_W\",\n            \"label\": \"W\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"DP_V\",\n            \"label\": \"V\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"DP_Z\",\n            \"label\": \"Z\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ % │ 7 │ 5 │ 3 │ 1 │ 9 │ 0 │ 2 │ 4 │ 6 │ 8 │ ` │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ : │ < │ > │   │   │   │   │   │   │   │ ? │ ^ │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │ _ │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ \" │   │   │   │   │   │   │   │   │   │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(DP_DLR)\": {\n            \"key\": \"DP_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(DP_AMPR)\": {\n            \"key\": \"DP_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(DP_LBRC)\": {\n            \"key\": \"DP_7\",\n            \"label\": \"7\",\n        }\n        \"S(DP_LCBR)\": {\n            \"key\": \"DP_5\",\n            \"label\": \"5\",\n        }\n        \"S(DP_RCBR)\": {\n            \"key\": \"DP_3\",\n            \"label\": \"3\",\n        }\n        \"S(DP_LPRN)\": {\n            \"key\": \"DP_1\",\n            \"label\": \"1\",\n        }\n        \"S(DP_EQL)\": {\n            \"key\": \"DP_9\",\n            \"label\": \"9\",\n        }\n        \"S(DP_ASTR)\": {\n            \"key\": \"DP_0\",\n            \"label\": \"0\",\n        }\n        \"S(DP_RPRN)\": {\n            \"key\": \"DP_2\",\n            \"label\": \"2\",\n        }\n        \"S(DP_PLUS)\": {\n            \"key\": \"DP_4\",\n            \"label\": \"4\",\n        }\n        \"S(DP_RBRC)\": {\n            \"key\": \"DP_6\",\n            \"label\": \"6\",\n        }\n        \"S(DP_EXLM)\": {\n            \"key\": \"DP_8\",\n            \"label\": \"8\",\n        }\n        \"S(DP_HASH)\": {\n            \"key\": \"DP_GRV\",\n            \"label\": \"`\",\n        }\n        \"S(DP_SCLN)\": {\n            \"key\": \"DP_COLN\",\n            \"label\": \":\",\n        }\n        \"S(DP_COMM)\": {\n            \"key\": \"DP_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(DP_DOT)\": {\n            \"key\": \"DP_RABK\",\n            \"label\": \">\",\n        }\n        \"S(DP_SLSH)\": {\n            \"key\": \"DP_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(DP_AT)\": {\n            \"key\": \"DP_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(DP_BSLS)\": {\n            \"key\": \"DP_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(DP_MINS)\": {\n            \"key\": \"DP_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(DP_QUOT)\": {\n            \"key\": \"DP_DQUO\",\n            \"label\": \"\\\"\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_estonian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ˇ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ + │ ´ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Ü │ Õ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ö │ Ä │ ' │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"EE_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"KC_1\": {\n            \"key\": \"EE_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"EE_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"EE_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"EE_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"EE_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"EE_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"EE_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"EE_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"EE_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"EE_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"EE_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"EE_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"EE_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"EE_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"EE_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"EE_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"EE_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"EE_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"EE_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"EE_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"EE_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"EE_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"EE_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"EE_OTIL\",\n            \"label\": \"Õ\",\n        }\n        \"KC_A\": {\n            \"key\": \"EE_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"EE_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"EE_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"EE_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"EE_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"EE_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"EE_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"EE_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"EE_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"EE_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"EE_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"EE_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"EE_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"EE_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"EE_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"EE_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"EE_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"EE_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"EE_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"EE_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"EE_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"EE_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"EE_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ \" │ # │ ¤ │ % │ & │ / │ ( │ ) │ = │ ? │ ` │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ * │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(EE_CARN)\": {\n            \"key\": \"EE_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"S(EE_1)\": {\n            \"key\": \"EE_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(EE_2)\": {\n            \"key\": \"EE_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(EE_3)\": {\n            \"key\": \"EE_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(EE_4)\": {\n            \"key\": \"EE_CURR\",\n            \"label\": \"¤\",\n        }\n        \"S(EE_5)\": {\n            \"key\": \"EE_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(EE_6)\": {\n            \"key\": \"EE_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(EE_7)\": {\n            \"key\": \"EE_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(EE_8)\": {\n            \"key\": \"EE_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(EE_9)\": {\n            \"key\": \"EE_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(EE_0)\": {\n            \"key\": \"EE_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(EE_PLUS)\": {\n            \"key\": \"EE_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(EE_ACUT)\": {\n            \"key\": \"EE_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(EE_QUOT)\": {\n            \"key\": \"EE_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(EE_LABK)\": {\n            \"key\": \"EE_RABK\",\n            \"label\": \">\",\n        }\n        \"S(EE_COMM)\": {\n            \"key\": \"EE_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(EE_DOT)\": {\n            \"key\": \"EE_COLN\",\n            \"label\": \":\",\n        }\n        \"S(EE_MINS)\": {\n            \"key\": \"EE_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │ @ │ £ │ $ │ € │   │ { │ [ │ ] │ } │ \\ │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ § │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │ š │   │   │   │   │   │   │   │   │ ^ │ ½ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ | │ ž │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(EE_2)\": {\n            \"key\": \"EE_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(EE_3)\": {\n            \"key\": \"EE_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(EE_4)\": {\n            \"key\": \"EE_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(EE_5)\": {\n            \"key\": \"EE_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(EE_7)\": {\n            \"key\": \"EE_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(EE_8)\": {\n            \"key\": \"EE_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(EE_9)\": {\n            \"key\": \"EE_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(EE_0)\": {\n            \"key\": \"EE_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(EE_PLUS)\": {\n            \"key\": \"EE_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(EE_OTIL)\": {\n            \"key\": \"EE_SECT\",\n            \"label\": \"§\",\n        }\n        \"ALGR(EE_S)\": {\n            \"key\": \"EE_SCAR\",\n            \"label\": \"š\",\n        }\n        \"ALGR(EE_ADIA)\": {\n            \"key\": \"EE_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"ALGR(EE_QUOT)\": {\n            \"key\": \"EE_HALF\",\n            \"label\": \"½\",\n        }\n        \"ALGR(EE_LABK)\": {\n            \"key\": \"EE_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(EE_Z)\": {\n            \"key\": \"EE_ZCAR\",\n            \"label\": \"ž\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_eurkey_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"EU_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"EU_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"EU_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"EU_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"EU_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"EU_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"EU_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"EU_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"EU_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"EU_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"EU_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"EU_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"EU_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"EU_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"EU_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"EU_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"EU_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"EU_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"EU_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"EU_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"EU_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"EU_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"EU_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"EU_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"EU_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"EU_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"EU_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"EU_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"EU_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"EU_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"EU_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"EU_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"EU_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"EU_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"EU_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"EU_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"EU_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_Z\": {\n            \"key\": \"EU_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"EU_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"EU_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"EU_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"EU_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"EU_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"EU_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"EU_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"EU_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"EU_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │ : │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(EU_GRV)\": {\n            \"key\": \"EU_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(EU_1)\": {\n            \"key\": \"EU_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(EU_2)\": {\n            \"key\": \"EU_AT\",\n            \"label\": \"@\",\n        }\n        \"S(EU_3)\": {\n            \"key\": \"EU_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(EU_4)\": {\n            \"key\": \"EU_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(EU_5)\": {\n            \"key\": \"EU_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(EU_6)\": {\n            \"key\": \"EU_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(EU_7)\": {\n            \"key\": \"EU_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(EU_8)\": {\n            \"key\": \"EU_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(EU_9)\": {\n            \"key\": \"EU_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(EU_0)\": {\n            \"key\": \"EU_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(EU_MINS)\": {\n            \"key\": \"EU_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(EU_EQL)\": {\n            \"key\": \"EU_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(EU_LBRC)\": {\n            \"key\": \"EU_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(EU_RBRC)\": {\n            \"key\": \"EU_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(EU_BSLS)\": {\n            \"key\": \"EU_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(EU_SCLN)\": {\n            \"key\": \"EU_COLN\",\n            \"label\": \":\",\n        }\n        \"S(EU_QUOT)\": {\n            \"key\": \"EU_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(EU_COMM)\": {\n            \"key\": \"EU_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(EU_DOT)\": {\n            \"key\": \"EU_RABK\",\n            \"label\": \">\",\n        }\n        \"S(EU_SLSH)\": {\n            \"key\": \"EU_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ ¡ │ ª │ º │ £ │ € │ ^ │ ˚ │ „ │ “ │ ” │ – │ × │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ æ │ å │ ë │ ý │ þ │ ÿ │ ü │ ï │ ö │ œ │ « │ » │  ¬  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ ä │ ß │ ð │ è │ é │ ù │ ú │ ĳ │ ø │ ° │ ´ │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ à │ á │ ç │ ì │ í │ ñ │ μ │ ò │ ó │ ¿ │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(EU_GRV)\": {\n            \"key\": \"EU_DGRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"ALGR(EU_1)\": {\n            \"key\": \"EU_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"ALGR(EU_2)\": {\n            \"key\": \"EU_FORD\",\n            \"label\": \"ª\",\n        }\n        \"ALGR(EU_3)\": {\n            \"key\": \"EU_MORD\",\n            \"label\": \"º\",\n        }\n        \"ALGR(EU_4)\": {\n            \"key\": \"EU_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(EU_5)\": {\n            \"key\": \"EU_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(EU_6)\": {\n            \"key\": \"EU_DCIR\",\n            \"label\": \"^ (dead)\",\n        }\n        \"ALGR(EU_7)\": {\n            \"key\": \"EU_RNGA\",\n            \"label\": \"˚ (dead)\",\n        }\n        \"ALGR(EU_8)\": {\n            \"key\": \"EU_DLQU\",\n            \"label\": \"„\",\n        }\n        \"ALGR(EU_9)\": {\n            \"key\": \"EU_LDQU\",\n            \"label\": \"“\",\n        }\n        \"ALGR(EU_0)\": {\n            \"key\": \"EU_RDQU\",\n            \"label\": \"”\",\n        }\n        \"ALGR(EU_MINS)\": {\n            \"key\": \"EU_NDSH\",\n            \"label\": \"–\",\n        }\n        \"ALGR(EU_EQL)\": {\n            \"key\": \"EU_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(EU_Q)\": {\n            \"key\": \"EU_AE\",\n            \"label\": \"æ\",\n        }\n        \"ALGR(EU_W)\": {\n            \"key\": \"EU_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"ALGR(EU_E)\": {\n            \"key\": \"EU_EDIA\",\n            \"label\": \"Ë\",\n        }\n        \"ALGR(EU_R)\": {\n            \"key\": \"EU_YACU\",\n            \"label\": \"Ý\",\n        }\n        \"ALGR(EU_T)\": {\n            \"key\": \"EU_THRN\",\n            \"label\": \"Þ\",\n        }\n        \"ALGR(EU_Y)\": {\n            \"key\": \"EU_YDIA\",\n            \"label\": \"Ÿ\",\n        }\n        \"ALGR(EU_U)\": {\n            \"key\": \"EU_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"ALGR(EU_I)\": {\n            \"key\": \"EU_IDIA\",\n            \"label\": \"Ï\",\n        }\n        \"ALGR(EU_O)\": {\n            \"key\": \"EU_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"ALGR(EU_P)\": {\n            \"key\": \"EU_OE\",\n            \"label\": \"Œ\",\n        }\n        \"ALGR(EU_LBRC)\": {\n            \"key\": \"EU_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"ALGR(EU_RBRC)\": {\n            \"key\": \"EU_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"ALGR(EU_BSLS)\": {\n            \"key\": \"EU_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(EU_A)\": {\n            \"key\": \"EU_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"ALGR(EU_S)\": {\n            \"key\": \"EU_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(EU_D)\": {\n            \"key\": \"EU_ETH\",\n            \"label\": \"Ð\",\n        }\n        \"ALGR(EU_F)\": {\n            \"key\": \"EU_EGRV\",\n            \"label\": \"È\",\n        }\n        \"ALGR(EU_G)\": {\n            \"key\": \"EU_EACU\",\n            \"label\": \"É\",\n        }\n        \"ALGR(EU_H)\": {\n            \"key\": \"EU_UGRV\",\n            \"label\": \"Ù\",\n        }\n        \"ALGR(EU_J)\": {\n            \"key\": \"EU_UACU\",\n            \"label\": \"Ú\",\n        }\n        \"ALGR(EU_K)\": {\n            \"key\": \"EU_IJ\",\n            \"label\": \"Ĳ\",\n        }\n        \"ALGR(EU_L)\": {\n            \"key\": \"EU_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"ALGR(EU_SCLN)\": {\n            \"key\": \"EU_DEG\",\n            \"label\": \"°\",\n        }\n        \"ALGR(EU_QUOT)\": {\n            \"key\": \"EU_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(EU_Z)\": {\n            \"key\": \"EU_AGRV\",\n            \"label\": \"À\",\n        }\n        \"ALGR(EU_X)\": {\n            \"key\": \"EU_AACU\",\n            \"label\": \"Á\",\n        }\n        \"ALGR(EU_C)\": {\n            \"key\": \"EU_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"ALGR(EU_V)\": {\n            \"key\": \"EU_IGRV\",\n            \"label\": \"Ì\",\n        }\n        \"ALGR(EU_B)\": {\n            \"key\": \"EU_IACU\",\n            \"label\": \"Í\",\n        }\n        \"ALGR(EU_N)\": {\n            \"key\": \"EU_NTIL\",\n            \"label\": \"Ñ\",\n        }\n        \"ALGR(EU_M)\": {\n            \"key\": \"EU_DGRK\",\n            \"label\": \"μ (dead Greek key)\",\n        }\n        \"ALGR(EU_COMM)\": {\n            \"key\": \"EU_OGRV\",\n            \"label\": \"Ò\",\n        }\n        \"ALGR(EU_DOT)\": {\n            \"key\": \"EU_OACU\",\n            \"label\": \"Ó\",\n        }\n        \"ALGR(EU_SLSH)\": {\n            \"key\": \"EU_IQUE\",\n            \"label\": \"¿\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ¹ │ ² │ ³ │ ¥ │ ¢ │ ˇ │ ¯ │ ‚ │ ‘ │ ’ │ — │ ÷ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ ‹ │ › │  ¦  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │ § │   │   │   │   │   │   │   │ · │ ¨ │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │   │   │ … │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(EU_TILD)\": {\n            \"key\": \"EU_DTIL\",\n            \"label\": \"~ (dead)\",\n        }\n        \"S(ALGR(EU_1))\": {\n            \"key\": \"EU_SUP1\",\n            \"label\": \"¹\",\n        }\n        \"S(ALGR(EU_2))\": {\n            \"key\": \"EU_SUP2\",\n            \"label\": \"²\",\n        }\n        \"S(ALGR(EU_3))\": {\n            \"key\": \"EU_SUP3\",\n            \"label\": \"³\",\n        }\n        \"ALGR(EU_DLR)\": {\n            \"key\": \"EU_YEN\",\n            \"label\": \"¥\",\n        }\n        \"S(EU_EURO)\": {\n            \"key\": \"EU_CENT\",\n            \"label\": \"¢\",\n        }\n        \"S(EU_DCIR)\": {\n            \"key\": \"EU_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"S(ALGR(EU_7))\": {\n            \"key\": \"EU_MACR\",\n            \"label\": \"¯ (dead)\",\n        }\n        \"S(EU_DLQU)\": {\n            \"key\": \"EU_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"S(EU_LDQU)\": {\n            \"key\": \"EU_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"S(EU_RDQU)\": {\n            \"key\": \"EU_RSQU\",\n            \"label\": \"’\",\n        }\n        \"S(EU_NDSH)\": {\n            \"key\": \"EU_MDSH\",\n            \"label\": \"—\",\n        }\n        \"S(EU_MUL)\": {\n            \"key\": \"EU_DIV\",\n            \"label\": \"÷\",\n        }\n        \"S(EU_LDAQ)\": {\n            \"key\": \"EU_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"S(EU_RDAQ)\": {\n            \"key\": \"EU_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"S(ALGR(EU_BSLS))\": {\n            \"key\": \"EU_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"S(ALGR(EU_S))\": {\n            \"key\": \"EU_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(ALGR(EU_SCLN))\": {\n            \"key\": \"EU_MDDT\",\n            \"label\": \"·\",\n        }\n        \"ALGR(EU_DQUO)\": {\n            \"key\": \"EU_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"ALGR(EU_QUES)\": {\n            \"key\": \"EU_ELLP\",\n            \"label\": \"…\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_farsi_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ۱ │ ۲ │ ۳ │ ۴ │ ۵ │ ۶ │ ۷ │ ۸ │ ۹ │ ۰ │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ ض │ ص │ ث │ ق │ ف │ غ │ ع │ ه │ خ │ ح │ ج │ چ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ ش │ س │ ی │ ب │ ل │ ا │ ت │ ن │ م │ ک │ گ │ \\ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ ظ │ ط │ ز │ ر │ ذ │ د │ پ │ و │ . │ / │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"FA_ZWJ\",\n            \"label\": \"(zero-width joiner)\",\n        }\n        \"KC_1\": {\n            \"key\": \"FA_1A\",\n            \"label\": \"۱\",\n        }\n        \"KC_2\": {\n            \"key\": \"FA_2A\",\n            \"label\": \"۲\",\n        }\n        \"KC_3\": {\n            \"key\": \"FA_3A\",\n            \"label\": \"۳\",\n        }\n        \"KC_4\": {\n            \"key\": \"FA_4A\",\n            \"label\": \"۴\",\n        }\n        \"KC_5\": {\n            \"key\": \"FA_5A\",\n            \"label\": \"۵\",\n        }\n        \"KC_6\": {\n            \"key\": \"FA_6A\",\n            \"label\": \"۶\",\n        }\n        \"KC_7\": {\n            \"key\": \"FA_7A\",\n            \"label\": \"۷\",\n        }\n        \"KC_8\": {\n            \"key\": \"FA_8A\",\n            \"label\": \"۸\",\n        }\n        \"KC_9\": {\n            \"key\": \"FA_9A\",\n            \"label\": \"۹\",\n        }\n        \"KC_0\": {\n            \"key\": \"FA_0A\",\n            \"label\": \"۰\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"FA_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"FA_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"FA_ZAD\",\n            \"label\": \"ض\",\n        }\n        \"KC_W\": {\n            \"key\": \"FA_SAD\",\n            \"label\": \"ص\",\n        }\n        \"KC_E\": {\n            \"key\": \"FA_SE\",\n            \"label\": \"ث\",\n        }\n        \"KC_R\": {\n            \"key\": \"FA_QAF\",\n            \"label\": \"ق\",\n        }\n        \"KC_T\": {\n            \"key\": \"FA_FE\",\n            \"label\": \"ف\",\n        }\n        \"KC_Y\": {\n            \"key\": \"FA_GHYN\",\n            \"label\": \"غ\",\n        }\n        \"KC_U\": {\n            \"key\": \"FA_EYN\",\n            \"label\": \"ع\",\n        }\n        \"KC_I\": {\n            \"key\": \"FA_HE\",\n            \"label\": \"ه\",\n        }\n        \"KC_O\": {\n            \"key\": \"FA_KHE\",\n            \"label\": \"خ\",\n        }\n        \"KC_P\": {\n            \"key\": \"FA_HEJ\",\n            \"label\": \"ح\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"FA_JIM\",\n            \"label\": \"ج\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"FA_CHE\",\n            \"label\": \"چ\",\n        }\n        \"KC_A\": {\n            \"key\": \"FA_SHIN\",\n            \"label\": \"ش\",\n        }\n        \"KC_S\": {\n            \"key\": \"FA_SIN\",\n            \"label\": \"س\",\n        }\n        \"KC_D\": {\n            \"key\": \"FA_YE\",\n            \"label\": \"ی\",\n        }\n        \"KC_F\": {\n            \"key\": \"FA_BE\",\n            \"label\": \"ب\",\n        }\n        \"KC_G\": {\n            \"key\": \"FA_LAM\",\n            \"label\": \"ل\",\n        }\n        \"KC_H\": {\n            \"key\": \"FA_ALEF\",\n            \"label\": \"ا\",\n        }\n        \"KC_J\": {\n            \"key\": \"FA_TE\",\n            \"label\": \"ت\",\n        }\n        \"KC_K\": {\n            \"key\": \"FA_NOON\",\n            \"label\": \"ن\",\n        }\n        \"KC_L\": {\n            \"key\": \"FA_MIM\",\n            \"label\": \"م\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"FA_KAF\",\n            \"label\": \"ک\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"FA_GAF\",\n            \"label\": \"گ\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"FA_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_LT\": {\n            \"key\": \"FA_LT\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"FA_ZA\",\n            \"label\": \"ظ\",\n        }\n        \"KC_X\": {\n            \"key\": \"FA_TA\",\n            \"label\": \"ط\",\n        }\n        \"KC_C\": {\n            \"key\": \"FA_ZE\",\n            \"label\": \"ز\",\n        }\n        \"KC_V\": {\n            \"key\": \"FA_RE\",\n            \"label\": \"ر\",\n        }\n        \"KC_B\": {\n            \"key\": \"FA_ZAL\",\n            \"label\": \"ذ\",\n        }\n        \"KC_N\": {\n            \"key\": \"FA_DAL\",\n            \"label\": \"د\",\n        }\n        \"KC_M\": {\n            \"key\": \"FA_PE\",\n            \"label\": \"پ\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"FA_WAW\",\n            \"label\": \"و\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"FA_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"FA_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_SPC\": {\n            \"key\": \"FA_SPC\",\n            \"label\": \" \",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ÷ │ ! │ ٬ │ ٫ │ ﷼ │ ٪ │ × │ ، │ * │ ) │ ( │ ـ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │  ْ │  ٌ │  ٍ │  ً │  ُ │  ِ │  َ │  ّ │ ] │ [ │ } │ { │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ ؤ │ ئ │ ي │ إ │ أ │ آ │ ة │ » │ « │ : │ ؛ │ | │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │ ك │  ٓ │ ژ │ ٰ  │   │  ٔ │ ء │   │   │ ؟ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(FA_ZWJ)\": {\n            \"key\": \"FA_DIV\",\n            \"label\": \"÷\",\n        }\n        \"S(FA_1A)\": {\n            \"key\": \"FA_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(FA_2A)\": {\n            \"key\": \"FA_THS\",\n            \"label\": \"٬\",\n        }\n        \"S(FA_3A)\": {\n            \"key\": \"FA_DECS\",\n            \"label\": \"٫\",\n        }\n        \"S(FA_4A)\": {\n            \"key\": \"FA_RIAL\",\n            \"label\": \"﷼\",\n        }\n        \"S(FA_5A)\": {\n            \"key\": \"FA_PRCA\",\n            \"label\": \"٪\",\n        }\n        \"S(FA_6A)\": {\n            \"key\": \"FA_MUL\",\n            \"label\": \"×\",\n        }\n        \"S(FA_7A)\": {\n            \"key\": \"FA_COMA\",\n            \"label\": \"،\",\n        }\n        \"S(FA_8A)\": {\n            \"key\": \"FA_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(FA_9A)\": {\n            \"key\": \"FA_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(FA_0A)\": {\n            \"key\": \"FA_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(FA_MINS)\": {\n            \"key\": \"FA_TATW\",\n            \"label\": \"ـ\",\n        }\n        \"S(FA_EQL)\": {\n            \"key\": \"FA_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(FA_ZAD)\": {\n            \"key\": \"FA_SUK\",\n            \"label\": \"ْ\",\n        }\n        \"S(FA_SAD)\": {\n            \"key\": \"FA_DMTN\",\n            \"label\": \"ٌ\",\n        }\n        \"S(FA_SE)\": {\n            \"key\": \"FA_KSTN\",\n            \"label\": \"ٍ\",\n        }\n        \"S(FA_QAF)\": {\n            \"key\": \"FA_FTHN\",\n            \"label\": \"ً\",\n        }\n        \"S(FA_FE)\": {\n            \"key\": \"FA_DMM\",\n            \"label\": \"ُ\",\n        }\n        \"S(FA_GHYN)\": {\n            \"key\": \"FA_KAS\",\n            \"label\": \"ِ\",\n        }\n        \"S(FA_EYN)\": {\n            \"key\": \"FA_FAT\",\n            \"label\": \"َ\",\n        }\n        \"S(FA_HE)\": {\n            \"key\": \"FA_TSDD\",\n            \"label\": \"\",\n        }\n        \"S(FA_KHE)\": {\n            \"key\": \"FA_RBRC\",\n            \"label\": \"]\",\n        }\n        \"S(FA_HEJ)\": {\n            \"key\": \"FA_LBRC\",\n            \"label\": \"[\",\n        }\n        \"S(FA_JIM)\": {\n            \"key\": \"FA_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(FA_CHE)\": {\n            \"key\": \"FA_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(FA_SHIN)\": {\n            \"key\": \"FA_HMZV\",\n            \"label\": \"ؤ\",\n        }\n        \"S(FA_SIN)\": {\n            \"key\": \"FA_HMZY\",\n            \"label\": \"ئ\",\n        }\n        \"S(FA_YE)\": {\n            \"key\": \"FA_YEA\",\n            \"label\": \"ي\",\n        }\n        \"S(FA_BE)\": {\n            \"key\": \"FA_HMZU\",\n            \"label\": \"إ\",\n        }\n        \"S(FA_LAM)\": {\n            \"key\": \"FA_HMZO\",\n            \"label\": \"أ\",\n        }\n        \"S(FA_ALEF)\": {\n            \"key\": \"FA_MALF\",\n            \"label\": \"آ\",\n        }\n        \"S(FA_TE)\": {\n            \"key\": \"FA_TEHM\",\n            \"label\": \"ة\",\n        }\n        \"S(FA_NOON)\": {\n            \"key\": \"FA_RQOT\",\n            \"label\": \"»\",\n        }\n        \"S(FA_MIM)\": {\n            \"key\": \"FA_LQOT\",\n            \"label\": \"«\",\n        }\n        \"S(FA_KAF)\": {\n            \"key\": \"FA_COLN\",\n            \"label\": \":\",\n        }\n        \"S(FA_GAF)\": {\n            \"key\": \"FA_SCLA\",\n            \"label\": \"؛\",\n        }\n        \"S(FA_LT)\": {\n            \"key\": \"FA_GT\",\n            \"label\": \">\",\n        }\n        \"S(FA_ZA)\": {\n            \"key\": \"FA_KAFA\",\n            \"label\": \"ك\",\n        }\n        \"S(FA_TA)\": {\n            \"key\": \"FA_MADO\",\n            \"label\": \"ٓ\",\n        }\n        \"S(FA_ZE)\": {\n            \"key\": \"FA_JEH\",\n            \"label\": \"ژ\",\n        }\n        \"S(FA_RE)\": {\n            \"key\": \"FA_SUPA\",\n            \"label\": \"ٰ\",\n        }\n        \"S(FA_ZAL)\": {\n            \"key\": \"FA_ZWNJ\",\n            \"label\": \"(zero-width non-joiner)\",\n        }\n        \"S(FA_DAL)\": {\n            \"key\": \"FA_HMZA\",\n            \"label\": \"ٔ\",\n        }\n        \"S(FA_PE)\": {\n            \"key\": \"FA_HMZ\",\n            \"label\": \"ء\",\n        }\n        \"S(FA_SLSH)\": {\n            \"key\": \"FA_QSA\",\n            \"label\": \"؟\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ` │ @ │ # │ $ │ % │ ^ │ & │ • │   │   │ _ │ − │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ ° │   │ € │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │ ى │   │   │ ٱ │   │ ﴾ │ ﴿ │ ; │ \" │ ‐ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ | │   │   │   │  ٖ │   │  ٕ │ … │ , │ ' │ ? │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(FA_ZWJ)\": {\n            \"key\": \"FA_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(FA_1A)\": {\n            \"key\": \"FA_GRV\",\n            \"label\": \"`\",\n        }\n        \"ALGR(FA_2A)\": {\n            \"key\": \"FA_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(FA_3A)\": {\n            \"key\": \"FA_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(FA_4A)\": {\n            \"key\": \"FA_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(FA_5A)\": {\n            \"key\": \"FA_PERC\",\n            \"label\": \"%\",\n        }\n        \"ALGR(FA_6A)\": {\n            \"key\": \"FA_CIRC\",\n            \"label\": \"^\",\n        }\n        \"ALGR(FA_7A)\": {\n            \"key\": \"FA_AMPR\",\n            \"label\": \"&\",\n        }\n        \"ALGR(FA_8A)\": {\n            \"key\": \"FA_BULT\",\n            \"label\": \"•\",\n        }\n        \"ALGR(FA_9A)\": {\n            \"key\": \"FA_LRM\",\n            \"label\": \"(left-to-right mark)\",\n        }\n        \"ALGR(FA_0A)\": {\n            \"key\": \"FA_RLM\",\n            \"label\": \"(right-to-left mark)\",\n        }\n        \"ALGR(FA_MINS)\": {\n            \"key\": \"FA_UNDS\",\n            \"label\": \"_\",\n        }\n        \"ALGR(FA_EQL)\": {\n            \"key\": \"FA_DMNS\",\n            \"label\": \"− (dead)\",\n        }\n        \"ALGR(FA_ZAD)\": {\n            \"key\": \"FA_DEG\",\n            \"label\": \"°\",\n        }\n        \"ALGR(FA_SE)\": {\n            \"key\": \"FA_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(FA_HE)\": {\n            \"key\": \"FA_LRO\",\n            \"label\": \"(left-to-right override)\",\n        }\n        \"ALGR(FA_KHE)\": {\n            \"key\": \"FA_RLO\",\n            \"label\": \"(right-to-left override)\",\n        }\n        \"ALGR(FA_HEJ)\": {\n            \"key\": \"FA_PDF\",\n            \"label\": \"(pop directional formatting)\",\n        }\n        \"ALGR(FA_JIM)\": {\n            \"key\": \"FA_LRE\",\n            \"label\": \"(left-to-right embedding)\",\n        }\n        \"ALGR(FA_CHE)\": {\n            \"key\": \"FA_RLE\",\n            \"label\": \"(right-to-left embedding)\",\n        }\n        \"ALGR(FA_YE)\": {\n            \"key\": \"FA_ALFM\",\n            \"label\": \"ى\",\n        }\n        \"ALGR(FA_ALEF)\": {\n            \"key\": \"FA_ALFW\",\n            \"label\": \"ٱ\",\n        }\n        \"ALGR(FA_NOON)\": {\n            \"key\": \"FA_LORP\",\n            \"label\": \"﴾\",\n        }\n        \"ALGR(FA_MIM)\": {\n            \"key\": \"FA_RORP\",\n            \"label\": \"﴿\",\n        }\n        \"ALGR(FA_KAF)\": {\n            \"key\": \"FA_SCLN\",\n            \"label\": \";\",\n        }\n        \"ALGR(FA_GAF)\": {\n            \"key\": \"FA_DQT\",\n            \"label\": \"\\\"\",\n        }\n        \"ALGR(FA_BSLS)\": {\n            \"key\": \"FA_MINA\",\n            \"label\": \"-\",\n        }\n        \"ALGR(FA_ZA)\": {\n            \"key\": \"FA_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(FA_RE)\": {\n            \"key\": \"FA_SUBA\",\n            \"label\": \"ٖ\",\n        }\n        \"ALGR(FA_DAL)\": {\n            \"key\": \"FA_HMZB\",\n            \"label\": \"ء\",\n        }\n        \"ALGR(FA_PE)\": {\n            \"key\": \"FA_ELLP\",\n            \"label\": \"…\",\n        }\n        \"ALGR(FA_WAW)\": {\n            \"key\": \"FA_COMM\",\n            \"label\": \",\",\n        }\n        \"ALGR(FA_DOT)\": {\n            \"key\": \"FA_QUOT\",\n            \"label\": \"'\",\n        }\n        \"ALGR(FA_SLSH)\": {\n            \"key\": \"FA_QUES\",\n            \"label\": \"?\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ ¦ │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ALGR(FA_1A))\": {\n            \"key\": \"FA_1\",\n            \"label\": \"1\",\n        }\n        \"S(ALGR(FA_2A))\": {\n            \"key\": \"FA_2\",\n            \"label\": \"2\",\n        }\n        \"S(ALGR(FA_3A))\": {\n            \"key\": \"FA_3\",\n            \"label\": \"3\",\n        }\n        \"S(ALGR(FA_4A))\": {\n            \"key\": \"FA_4\",\n            \"label\": \"4\",\n        }\n        \"S(ALGR(FA_5A))\": {\n            \"key\": \"FA_5\",\n            \"label\": \"5\",\n        }\n        \"S(ALGR(FA_6A))\": {\n            \"key\": \"FA_6\",\n            \"label\": \"6\",\n        }\n        \"S(ALGR(FA_7A))\": {\n            \"key\": \"FA_7\",\n            \"label\": \"7\",\n        }\n        \"S(ALGR(FA_8A))\": {\n            \"key\": \"FA_8\",\n            \"label\": \"8\",\n        }\n        \"S(ALGR(FA_9A))\": {\n            \"key\": \"FA_9\",\n            \"label\": \"9\",\n        }\n        \"S(ALGR(FA_0A))\": {\n            \"key\": \"FA_0\",\n            \"label\": \"0\",\n        }\n        \"S(ALGR(FA_LT))\": {\n            \"key\": \"FA_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"S(ALGR(FA_SPC))\": {\n            \"key\": \"FA_NNBS\",\n            \"label\": \"(narrow non-breaking space)\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_finnish_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ § │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ + │ ´ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Å │ ¨ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ö │ Ä │ ' │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"FI_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_1\": {\n            \"key\": \"FI_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"FI_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"FI_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"FI_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"FI_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"FI_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"FI_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"FI_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"FI_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"FI_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"FI_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"FI_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"FI_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"FI_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"FI_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"FI_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"FI_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"FI_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"FI_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"FI_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"FI_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"FI_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"FI_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"FI_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"FI_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"FI_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"FI_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"FI_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"FI_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"FI_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"FI_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"FI_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"FI_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"FI_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"FI_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"FI_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"FI_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"FI_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"FI_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"FI_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"FI_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"FI_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"FI_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"FI_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"FI_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"FI_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"FI_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ½ │ ! │ \" │ # │ ¤ │ % │ & │ / │ ( │ ) │ = │ ? │ ` │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ^ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ * │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(FI_SECT)\": {\n            \"key\": \"FI_HALF\",\n            \"label\": \"½\",\n        }\n        \"S(FI_1)\": {\n            \"key\": \"FI_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(FI_2)\": {\n            \"key\": \"FI_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(FI_3)\": {\n            \"key\": \"FI_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(FI_4)\": {\n            \"key\": \"FI_CURR\",\n            \"label\": \"¤\",\n        }\n        \"S(FI_5)\": {\n            \"key\": \"FI_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(FI_6)\": {\n            \"key\": \"FI_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(FI_7)\": {\n            \"key\": \"FI_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(FI_8)\": {\n            \"key\": \"FI_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(FI_9)\": {\n            \"key\": \"FI_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(FI_0)\": {\n            \"key\": \"FI_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(FI_PLUS)\": {\n            \"key\": \"FI_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(FI_ACUT)\": {\n            \"key\": \"FI_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(FI_DIAE)\": {\n            \"key\": \"FI_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(FI_QUOT)\": {\n            \"key\": \"FI_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(FI_LABK)\": {\n            \"key\": \"FI_RABK\",\n            \"label\": \">\",\n        }\n        \"S(FI_COMM)\": {\n            \"key\": \"FI_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(FI_DOT)\": {\n            \"key\": \"FI_COLN\",\n            \"label\": \":\",\n        }\n        \"S(FI_MINS)\": {\n            \"key\": \"FI_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │ @ │ £ │ $ │ € │   │ { │ [ │ ] │ } │ \\ │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ~ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ | │   │   │   │   │   │   │ µ │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(FI_2)\": {\n            \"key\": \"FI_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(FI_3)\": {\n            \"key\": \"FI_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(FI_4)\": {\n            \"key\": \"FI_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(FI_5)\": {\n            \"key\": \"FI_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(FI_7)\": {\n            \"key\": \"FI_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(FI_8)\": {\n            \"key\": \"FI_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(FI_9)\": {\n            \"key\": \"FI_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(FI_0)\": {\n            \"key\": \"FI_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(FI_PLUS)\": {\n            \"key\": \"FI_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(FI_DIAE)\": {\n            \"key\": \"FI_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(FI_LABK)\": {\n            \"key\": \"FI_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(FI_M)\": {\n            \"key\": \"FI_MICR\",\n            \"label\": \"µ\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_french_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ² │ & │ é │ \" │ ' │ ( │ - │ è │ _ │ ç │ à │ ) │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ A │ Z │ E │ R │ T │ Y │ U │ I │ O │ P │ ^ │ $ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ Q │ S │ D │ F │ G │ H │ J │ K │ L │ M │ ù │ * │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ W │ X │ C │ V │ B │ N │ , │ ; │ : │ ! │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"FR_SUP2\",\n            \"label\": \"²\",\n        }\n        \"KC_1\": {\n            \"key\": \"FR_AMPR\",\n            \"label\": \"&\",\n        }\n        \"KC_2\": {\n            \"key\": \"FR_EACU\",\n            \"label\": \"é\",\n        }\n        \"KC_3\": {\n            \"key\": \"FR_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"KC_4\": {\n            \"key\": \"FR_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_5\": {\n            \"key\": \"FR_LPRN\",\n            \"label\": \"(\",\n        }\n        \"KC_6\": {\n            \"key\": \"FR_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_7\": {\n            \"key\": \"FR_EGRV\",\n            \"label\": \"è\",\n        }\n        \"KC_8\": {\n            \"key\": \"FR_UNDS\",\n            \"label\": \"_\",\n        }\n        \"KC_9\": {\n            \"key\": \"FR_CCED\",\n            \"label\": \"ç\",\n        }\n        \"KC_0\": {\n            \"key\": \"FR_AGRV\",\n            \"label\": \"à\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"FR_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"FR_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"FR_A\",\n            \"label\": \"A\",\n        }\n        \"KC_W\": {\n            \"key\": \"FR_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_E\": {\n            \"key\": \"FR_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"FR_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"FR_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"FR_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"FR_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"FR_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"FR_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"FR_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"FR_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"FR_DLR\",\n            \"label\": \"$\",\n        }\n        \"KC_A\": {\n            \"key\": \"FR_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_S\": {\n            \"key\": \"FR_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"FR_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"FR_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"FR_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"FR_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"FR_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"FR_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"FR_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"FR_M\",\n            \"label\": \"M\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"FR_UGRV\",\n            \"label\": \"ù\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"FR_ASTR\",\n            \"label\": \"*\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"FR_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"FR_W\",\n            \"label\": \"W\",\n        }\n        \"KC_X\": {\n            \"key\": \"FR_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"FR_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"FR_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"FR_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"FR_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"FR_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"FR_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"FR_COLN\",\n            \"label\": \":\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"FR_EXLM\",\n            \"label\": \"!\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ° │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ ¨ │ £ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ % │ µ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │ ? │ . │ / │ § │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(FR_AMPR)\": {\n            \"key\": \"FR_1\",\n            \"label\": \"1\",\n        }\n        \"S(FR_EACU)\": {\n            \"key\": \"FR_2\",\n            \"label\": \"2\",\n        }\n        \"S(FR_DQUO)\": {\n            \"key\": \"FR_3\",\n            \"label\": \"3\",\n        }\n        \"S(FR_QUOT)\": {\n            \"key\": \"FR_4\",\n            \"label\": \"4\",\n        }\n        \"S(FR_LPRN)\": {\n            \"key\": \"FR_5\",\n            \"label\": \"5\",\n        }\n        \"S(FR_MINS)\": {\n            \"key\": \"FR_6\",\n            \"label\": \"6\",\n        }\n        \"S(FR_EGRV)\": {\n            \"key\": \"FR_7\",\n            \"label\": \"7\",\n        }\n        \"S(FR_UNDS)\": {\n            \"key\": \"FR_8\",\n            \"label\": \"8\",\n        }\n        \"S(FR_CCED)\": {\n            \"key\": \"FR_9\",\n            \"label\": \"9\",\n        }\n        \"S(FR_AGRV)\": {\n            \"key\": \"FR_0\",\n            \"label\": \"0\",\n        }\n        \"S(FR_RPRN)\": {\n            \"key\": \"FR_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(FR_EQL)\": {\n            \"key\": \"FR_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(FR_CIRC)\": {\n            \"key\": \"FR_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(FR_DLR)\": {\n            \"key\": \"FR_PND\",\n            \"label\": \"£\",\n        }\n        \"S(FR_UGRV)\": {\n            \"key\": \"FR_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(FR_ASTR)\": {\n            \"key\": \"FR_MICR\",\n            \"label\": \"µ\",\n        }\n        \"S(FR_LABK)\": {\n            \"key\": \"FR_RABK\",\n            \"label\": \">\",\n        }\n        \"S(FR_COMM)\": {\n            \"key\": \"FR_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(FR_SCLN)\": {\n            \"key\": \"FR_DOT\",\n            \"label\": \".\",\n        }\n        \"S(FR_COLN)\": {\n            \"key\": \"FR_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(FR_EXLM)\": {\n            \"key\": \"FR_SECT\",\n            \"label\": \"§\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │ ~ │ # │ { │ [ │ | │ ` │ \\ │   │ @ │ ] │ } │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │   │   │ ¤ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(FR_EACU)\": {\n            \"key\": \"FR_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(FR_DQUO)\": {\n            \"key\": \"FR_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(FR_QUOT)\": {\n            \"key\": \"FR_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(FR_LPRN)\": {\n            \"key\": \"FR_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(FR_MINS)\": {\n            \"key\": \"FR_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(FR_EGRV)\": {\n            \"key\": \"FR_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"ALGR(FR_UNDS)\": {\n            \"key\": \"FR_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(FR_AGRV)\": {\n            \"key\": \"FR_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(FR_RPRN)\": {\n            \"key\": \"FR_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(FR_EQL)\": {\n            \"key\": \"FR_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(KC_E)\": {\n            \"key\": \"FR_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(FR_DLR)\": {\n            \"key\": \"FR_CURR\",\n            \"label\": \"¤\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_french_afnor_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/* French AZERTY - AFNOR NF Z71-300\n *\n * A standard for the French keyboard\n *\n * The project was launched at the end of 2015 on the proposal of the General\n * Delegation for the French language and the languages of France (Ministry\n * of Culture), starting from the observation that the current \"azerty\"\n * keyboards constrain the writing of French, languages regional and European\n * languages with Latin alphabet.\n *\n * For the first time, a standard (NF Z71-300) defines the placement of\n * characters on the French keyboard. It offers two layouts, one of which\n * closely follows the QWERTY keyboard used by most people who write in French.\n *\n * However, it is in many ways superior to the old keyboard:\n *\n * - it contains all the characters required to enter text in French (for example É, œ and \")\n * - it is designed to be more ergonomic and allow faster typing\n * - it includes almost 60 additional characters for entering foreign languages, technical content, etc\n * - however, the characters remain easy to locate thanks to intuitive groupings\n *\n * Source: https://norme-azerty.fr\n */\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ @ │ à │ é │ è │ ê │ ( │ ) │ ‘ │ ’ │ « │ » │ ' │ ^ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ A │ Z │ E │ R │ T │ Y │ U │ I │ O │ P │ - │ + │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ Q │ S │ D │ F │ G │ H │ J │ K │ L │ M │ / │ * │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ W │ X │ C │ V │ B │ N │ . │ , │ : │ ; │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"FR_AT\",\n            \"label\": \"@\",\n        }\n        \"KC_1\": {\n            \"key\": \"FR_AGRV\",\n            \"label\": \"à\",\n        }\n        \"KC_2\": {\n            \"key\": \"FR_EACU\",\n            \"label\": \"é\",\n        }\n        \"KC_3\": {\n            \"key\": \"FR_EGRV\",\n            \"label\": \"è\",\n        }\n        \"KC_4\": {\n            \"key\": \"FR_ECIR\",\n            \"label\": \"ê\",\n        }\n        \"KC_5\": {\n            \"key\": \"FR_LPRN\",\n            \"label\": \"(\",\n        }\n        \"KC_6\": {\n            \"key\": \"FR_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_7\": {\n            \"key\": \"FR_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"KC_8\": {\n            \"key\": \"FR_RSQU\",\n            \"label\": \"’\",\n        }\n        \"KC_9\": {\n            \"key\": \"FR_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"KC_0\": {\n            \"key\": \"FR_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"FR_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"FR_DCIR\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"FR_A\",\n            \"label\": \"A\",\n        }\n        \"KC_W\": {\n            \"key\": \"FR_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_E\": {\n            \"key\": \"FR_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"FR_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"FR_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"FR_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"FR_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"FR_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"FR_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"FR_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"FR_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"FR_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_A\": {\n            \"key\": \"FR_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_S\": {\n            \"key\": \"FR_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"FR_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"FR_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"FR_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"FR_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"FR_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"FR_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"FR_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"FR_M\",\n            \"label\": \"M\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"FR_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"FR_ASTR\",\n            \"label\": \"*\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"FR_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"FR_W\",\n            \"label\": \"W\",\n        }\n        \"KC_X\": {\n            \"key\": \"FR_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"FR_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"FR_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"FR_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"FR_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"FR_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"FR_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"FR_COLN\",\n            \"label\": \":\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"FR_SCLN\",\n            \"label\": \";\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ # │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ \" │ ¨ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ – │ ± │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ \\ │ ½ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │ ? │ ! │ … │ = │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(FR_AT)\": {\n            \"key\": \"FR_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(FR_AGRV)\": {\n            \"key\": \"FR_1\",\n            \"label\": \"1\",\n        }\n        \"S(FR_EACU)\": {\n            \"key\": \"FR_2\",\n            \"label\": \"2\",\n        }\n        \"S(FR_EGRV)\": {\n            \"key\": \"FR_3\",\n            \"label\": \"3\",\n        }\n        \"S(FR_ECIR)\": {\n            \"key\": \"FR_4\",\n            \"label\": \"4\",\n        }\n        \"S(FR_LPRN)\": {\n            \"key\": \"FR_5\",\n            \"label\": \"5\",\n        }\n        \"S(FR_RPRN)\": {\n            \"key\": \"FR_6\",\n            \"label\": \"6\",\n        }\n        \"S(FR_LSQU)\": {\n            \"key\": \"FR_7\",\n            \"label\": \"7\",\n        }\n        \"S(FR_RSQU)\": {\n            \"key\": \"FR_8\",\n            \"label\": \"8\",\n        }\n        \"S(FR_LDAQ)\": {\n            \"key\": \"FR_9\",\n            \"label\": \"9\",\n        }\n        \"S(FR_RDAQ)\": {\n            \"key\": \"FR_0\",\n            \"label\": \"0\",\n        }\n        \"S(FR_QUOT)\": {\n            \"key\": \"FR_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(FR_DCIR)\": {\n            \"key\": \"FR_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(FR_MINS)\": {\n            \"key\": \"FR_NDSH\",\n            \"label\": \"–\",\n        }\n        \"S(FR_PLUS)\": {\n            \"key\": \"FR_PLMN\",\n            \"label\": \"±\",\n        }\n        \"S(FR_SLSH)\": {\n            \"key\": \"FR_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"S(FR_ASTR)\": {\n            \"key\": \"FR_HALF\",\n            \"label\": \"½\",\n        }\n        \"S(FR_LABK)\": {\n            \"key\": \"FR_RABK\",\n            \"label\": \">\",\n        }\n        \"S(FR_DOT)\": {\n            \"key\": \"FR_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(FR_COMM)\": {\n            \"key\": \"FR_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(FR_COLN)\": {\n            \"key\": \"FR_ELLP\",\n            \"label\": \"…\",\n        }\n        \"S(FR_SCLN)\": {\n            \"key\": \"FR_EQL\",\n            \"label\": \"=\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ˘ │ § │ ´ │ ` │ & │ [ │ ] │ ¯ │ _ │ “ │ ” │ ° │ ˇ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ æ │ £ │ € │ ® │ { │ } │ ù │ ˙ │ œ │ % │ − │ † │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ θ │ ß │ $ │ ¤ │ µ │ Eu│   │ ∕ │ | │ ∞ │ ÷ │ × │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ ≤ │ ʒ │ © │ ç │ ¸ │ − │ ~ │ ¿ │ ¡ │ · │ ≃ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(FR_AT)\": {\n            \"key\": \"FR_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"ALGR(FR_AGRV)\": {\n            \"key\": \"FR_SECT\",\n            \"label\": \"§\",\n        }\n        \"ALGR(FR_EACU)\": {\n            \"key\": \"FR_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(FR_EGRV)\": {\n            \"key\": \"FR_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"ALGR(FR_ECIR)\": {\n            \"key\": \"FR_AMPR\",\n            \"label\": \"&\",\n        }\n        \"ALGR(FR_LPRN)\": {\n            \"key\": \"FR_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(FR_RPRN)\": {\n            \"key\": \"FR_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(FR_LSQU)\": {\n            \"key\": \"FR_MACR\",\n            \"label\": \"¯ (dead)\",\n        }\n        \"ALGR(FR_RSQU)\": {\n            \"key\": \"FR_UNDS\",\n            \"label\": \"_\",\n        }\n        \"ALGR(FR_LDAQ)\": {\n            \"key\": \"FR_LDQU\",\n            \"label\": \"“\",\n        }\n        \"ALGR(FR_RDAQ)\": {\n            \"key\": \"FR_RDQU\",\n            \"label\": \"”\",\n        }\n        \"ALGR(FR_QUOT)\": {\n            \"key\": \"FR_DEG\",\n            \"label\": \"°\",\n        }\n        \"ALGR(FR_DCIR)\": {\n            \"key\": \"FR_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"ALGR(FR_A)\": {\n            \"key\": \"FR_AE\",\n            \"label\": \"æ\",\n        }\n        \"ALGR(FR_Z)\": {\n            \"key\": \"FR_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(FR_E)\": {\n            \"key\": \"FR_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(FR_R)\": {\n            \"key\": \"FR_REGD\",\n            \"label\": \"®\",\n        }\n        \"ALGR(FR_T)\": {\n            \"key\": \"FR_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(FR_Y)\": {\n            \"key\": \"FR_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(FR_U)\": {\n            \"key\": \"FR_UGRV\",\n            \"label\": \"ù\",\n        }\n        \"ALGR(FR_I)\": {\n            \"key\": \"FR_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n        \"ALGR(FR_O)\": {\n            \"key\": \"FR_OE\",\n            \"label\": \"œ\",\n        }\n        \"ALGR(FR_P)\": {\n            \"key\": \"FR_PERC\",\n            \"label\": \"%\",\n        }\n        \"ALGR(FR_MINS)\": {\n            \"key\": \"FR_MMNS\",\n            \"label\": \"−\",\n        }\n        \"ALGR(FR_PLUS)\": {\n            \"key\": \"FR_DAGG\",\n            \"label\": \"†\",\n        }\n        \"ALGR(FR_Q)\": {\n            \"key\": \"FR_THET\",\n            \"label\": \"θ\",\n        }\n        \"ALGR(FR_S)\": {\n            \"key\": \"FR_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(FR_D)\": {\n            \"key\": \"FR_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(FR_F)\": {\n            \"key\": \"FR_CURR\",\n            \"label\": \"¤ (dead monetary key)\",\n        }\n        \"ALGR(FR_G)\": {\n            \"key\": \"FR_DGRK\",\n            \"label\": \"µ (dead Greek key)\",\n        }\n        \"ALGR(FR_H)\": {\n            \"key\": \"FR_EU\",\n            \"label\": \"Eu (dead European symbol key)\",\n        }\n        \"ALGR(FR_K)\": {\n            \"key\": \"FR_DSLS\",\n            \"label\": \"∕ (dead)\",\n        }\n        \"ALGR(FR_L)\": {\n            \"key\": \"FR_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(FR_M)\": {\n            \"key\": \"FR_INFN\",\n            \"label\": \"∞\",\n        }\n        \"ALGR(FR_SLSH)\": {\n            \"key\": \"FR_DIV\",\n            \"label\": \"÷\",\n        }\n        \"ALGR(FR_ASTR)\": {\n            \"key\": \"FR_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(FR_LABK)\": {\n            \"key\": \"FR_LEQL\",\n            \"label\": \"≤\",\n        }\n        \"ALGR(FR_W)\": {\n            \"key\": \"FR_EZH\",\n            \"label\": \"ʒ\",\n        }\n        \"ALGR(FR_X)\": {\n            \"key\": \"FR_COPY\",\n            \"label\": \"©\",\n        }\n        \"ALGR(FR_C)\": {\n            \"key\": \"FR_CCED\",\n            \"label\": \"ç\",\n        }\n        \"ALGR(FR_V)\": {\n            \"key\": \"FR_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"ALGR(FR_B)\": {\n            \"key\": \"FR_DMNS\",\n            \"label\": \"− (dead)\",\n        }\n        \"ALGR(FR_N)\": {\n            \"key\": \"FR_DTIL\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(FR_DOT)\": {\n            \"key\": \"FR_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"ALGR(FR_COMM)\": {\n            \"key\": \"FR_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"ALGR(FR_COLN)\": {\n            \"key\": \"FR_MDDT\",\n            \"label\": \"·\",\n        }\n        \"ALGR(FR_SCLN)\": {\n            \"key\": \"FR_AEQL\",\n            \"label\": \"≃\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ̑  │   │   │   │   │ ˝ │ ̏  │   │ — │ ‹ │ › │ ˚ │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │ ™ │   │   │ ̣  │   │ ‰ │ ‑ │ ‡ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │ ˍ │   │   │   │   │ √ │ ¼ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ ≥ │   │   │   │ ˛ │   │   │   │ ̦  │   │ ≠ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ALGR(FR_AT))\": {\n            \"key\": \"FR_IBRV\",\n            \"label\": \"̑ (dead)\",\n        }\n        \"S(ALGR(FR_LPRN))\": {\n            \"key\": \"FR_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"S(ALGR(FR_RPRN))\": {\n            \"key\": \"FR_DGRV\",\n            \"label\": \"̏ (dead)\",\n        }\n        \"S(ALGR(FR_RSQU))\": {\n            \"key\": \"FR_MDSH\",\n            \"label\": \"—\",\n        }\n        \"S(ALGR(FR_LDAQ))\": {\n            \"key\": \"FR_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"S(ALGR(FR_RDAQ))\": {\n            \"key\": \"FR_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"S(ALGR(FR_QUOT))\": {\n            \"key\": \"FR_RNGA\",\n            \"label\": \"˚ (dead)\",\n        }\n        \"S(ALGR(FR_T))\": {\n            \"key\": \"FR_TM\",\n            \"label\": \"™\",\n        }\n        \"S(ALGR(FR_I))\": {\n            \"key\": \"FR_DOTB\",\n            \"label\": \"̣ (dead)\",\n        }\n        \"S(ALGR(FR_P))\": {\n            \"key\": \"FR_PERM\",\n            \"label\": \"‰\",\n        }\n        \"S(ALGR(FR_MINS))\": {\n            \"key\": \"FR_NBHY\",\n            \"label\": \"‑ (non-breaking hyphen)\",\n        }\n        \"S(ALGR(FR_PLUS))\": {\n            \"key\": \"FR_DDAG\",\n            \"label\": \"‡\",\n        }\n        \"S(ALGR(FR_H))\": {\n            \"key\": \"FR_MACB\",\n            \"label\": \"ˍ (dead)\",\n        }\n        \"S(ALGR(FR_SLSH))\": {\n            \"key\": \"FR_SQRT\",\n            \"label\": \"√\",\n        }\n        \"S(ALGR(FR_ASTR))\": {\n            \"key\": \"FR_QRTR\",\n            \"label\": \"¼\",\n        }\n        \"S(ALGR(FR_LABK))\": {\n            \"key\": \"FR_GEQL\",\n            \"label\": \"≥\",\n        }\n        \"S(ALGR(FR_V))\": {\n            \"key\": \"FR_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"S(ALGR(FR_COMM))\": {\n            \"key\": \"FR_DCMM\",\n            \"label\": \"̦ (dead)\",\n        }\n        \"S(ALGR(FR_SCLN))\": {\n            \"key\": \"FR_NEQL\",\n            \"label\": \"≠\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_french_mac_iso_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ @ │ & │ é │ \" │ ' │ ( │ § │ è │ ! │ ç │ à │ ) │ - │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ A │ Z │ E │ R │ T │ Y │ U │ I │ O │ P │ ^ │ $ │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ Q │ S │ D │ F │ G │ H │ J │ K │ L │ M │ ù │ ` │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ < │ W │ X │ C │ V │ B │ N │ , │ ; │ : │ = │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"FR_AT\",\n            \"label\": \"@\",\n        }\n        \"KC_1\": {\n            \"key\": \"FR_AMPR\",\n            \"label\": \"&\",\n        }\n        \"KC_2\": {\n            \"key\": \"FR_LEAC\",\n            \"label\": \"é\",\n        }\n        \"KC_3\": {\n            \"key\": \"FR_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"KC_4\": {\n            \"key\": \"FR_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_5\": {\n            \"key\": \"FR_LPRN\",\n            \"label\": \"(\",\n        }\n        \"KC_6\": {\n            \"key\": \"FR_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_7\": {\n            \"key\": \"FR_LEGR\",\n            \"label\": \"è\",\n        }\n        \"KC_8\": {\n            \"key\": \"FR_EXLM\",\n            \"label\": \"!\",\n        }\n        \"KC_9\": {\n            \"key\": \"FR_LCCE\",\n            \"label\": \"ç\",\n        }\n        \"KC_0\": {\n            \"key\": \"FR_LAGR\",\n            \"label\": \"à\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"FR_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"FR_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_Q\": {\n            \"key\": \"FR_A\",\n            \"label\": \"A\",\n        }\n        \"KC_W\": {\n            \"key\": \"FR_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_E\": {\n            \"key\": \"FR_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"FR_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"FR_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"FR_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"FR_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"FR_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"FR_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"FR_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"FR_CIRC\",\n            \"label\": \"^\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"FR_DLR\",\n            \"label\": \"$\",\n        }\n        \"KC_A\": {\n            \"key\": \"FR_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_S\": {\n            \"key\": \"FR_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"FR_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"FR_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"FR_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"FR_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"FR_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"FR_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"FR_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"FR_M\",\n            \"label\": \"M\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"FR_LUGR\",\n            \"label\": \"ù\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"FR_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"FR_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"FR_W\",\n            \"label\": \"W\",\n        }\n        \"KC_X\": {\n            \"key\": \"FR_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"FR_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"FR_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"FR_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"FR_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"FR_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"FR_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"FR_COLN\",\n            \"label\": \":\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"FR_EQL\",\n            \"label\": \"=\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ # │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ° │ _ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │   │   │   │   │ ¨ │ * │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │   │   │   │   │   │   │   │   │   │   │ % │ £ │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ > │   │   │   │   │   │   │ ? │ . │ / │ + │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(FR_AT)\": {\n            \"key\": \"FR_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(FR_AMPR)\": {\n            \"key\": \"FR_1\",\n            \"label\": \"1\",\n        }\n        \"S(FR_LEAC)\": {\n            \"key\": \"FR_2\",\n            \"label\": \"2\",\n        }\n        \"S(FR_DQUO)\": {\n            \"key\": \"FR_3\",\n            \"label\": \"3\",\n        }\n        \"S(FR_QUOT)\": {\n            \"key\": \"FR_4\",\n            \"label\": \"4\",\n        }\n        \"S(FR_LPRN)\": {\n            \"key\": \"FR_5\",\n            \"label\": \"5\",\n        }\n        \"S(FR_SECT)\": {\n            \"key\": \"FR_6\",\n            \"label\": \"6\",\n        }\n        \"S(FR_LEGR)\": {\n            \"key\": \"FR_7\",\n            \"label\": \"7\",\n        }\n        \"S(FR_EXLM)\": {\n            \"key\": \"FR_8\",\n            \"label\": \"8\",\n        }\n        \"S(FR_LCCE)\": {\n            \"key\": \"FR_9\",\n            \"label\": \"9\",\n        }\n        \"S(FR_LAGR)\": {\n            \"key\": \"FR_0\",\n            \"label\": \"0\",\n        }\n        \"S(FR_RPRN)\": {\n            \"key\": \"FR_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(FR_MINS)\": {\n            \"key\": \"FR_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(FR_CIRC)\": {\n            \"key\": \"FR_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(FR_DLR)\": {\n            \"key\": \"FR_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(FR_LUGR)\": {\n            \"key\": \"FR_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(FR_GRV)\": {\n            \"key\": \"FR_PND\",\n            \"label\": \"£\",\n        }\n        \"S(FR_LABK)\": {\n            \"key\": \"FR_RABK\",\n            \"label\": \">\",\n        }\n        \"S(FR_COMM)\": {\n            \"key\": \"FR_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(FR_SCLN)\": {\n            \"key\": \"FR_DOT\",\n            \"label\": \".\",\n        }\n        \"S(FR_COLN)\": {\n            \"key\": \"FR_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(FR_EQL)\": {\n            \"key\": \"FR_PLUS\",\n            \"label\": \"+\",\n        }\n/* Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ • │  │ ë │ “ │ ‘ │ { │ ¶ │ « │ ¡ │ Ç │ Ø │ } │ — │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Æ │ Â │ Ê │ ® │ † │ Ú │ º │ î │ Œ │ π │ Ô │ € │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ ‡ │ Ò │ ∂ │ ƒ │ ﬁ │ Ì │ Ï │ È │ ¬ │ µ │ Ù │   │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≤ │ ‹ │ ≈ │ © │ ◊ │ ß │ ~ │ ∞ │ … │ ÷ │ ≠ │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"A(FR_AT)\": {\n            \"key\": \"FR_BULT\",\n            \"label\": \"•\",\n        }\n        \"A(FR_AMPR)\": {\n            \"key\": \"FR_APPL\",\n            \"label\": \" (Apple logo)\",\n        }\n        \"A(FR_LEAC)\": {\n            \"key\": \"FR_LEDI\",\n            \"label\": \"ë\",\n        }\n        \"A(FR_DQUO)\": {\n            \"key\": \"FR_LDQU\",\n            \"label\": \"“\",\n        }\n        \"A(FR_QUOT)\": {\n            \"key\": \"FR_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"A(FR_LPRN)\": {\n            \"key\": \"FR_LCBR\",\n            \"label\": \"{\",\n        }\n        \"A(FR_SECT)\": {\n            \"key\": \"FR_PILC\",\n            \"label\": \"¶\",\n        }\n        \"A(FR_LEGR)\": {\n            \"key\": \"FR_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"A(FR_EXLM)\": {\n            \"key\": \"FR_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"A(FR_LCCE)\": {\n            \"key\": \"FR_CCCE\",\n            \"label\": \"Ç\",\n        }\n        \"A(FR_LAGR)\": {\n            \"key\": \"FR_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"A(FR_RPRN)\": {\n            \"key\": \"FR_RCBR\",\n            \"label\": \"}\",\n        }\n        \"A(FR_MINS)\": {\n            \"key\": \"FR_MDSH\",\n            \"label\": \"—\",\n        }\n        \"A(FR_A)\": {\n            \"key\": \"FR_AE\",\n            \"label\": \"Æ\",\n        }\n        \"A(FR_Z)\": {\n            \"key\": \"FR_CACI\",\n            \"label\": \"Â\",\n        }\n        \"A(FR_E)\": {\n            \"key\": \"FR_ECIR\",\n            \"label\": \"Ê\",\n        }\n        \"A(FR_R)\": {\n            \"key\": \"FR_REGD\",\n            \"label\": \"®\",\n        }\n        \"A(FR_T)\": {\n            \"key\": \"FR_DAGG\",\n            \"label\": \"†\",\n        }\n        \"A(FR_Y)\": {\n            \"key\": \"FR_CUAC\",\n            \"label\": \"Ú\",\n        }\n        \"A(FR_U)\": {\n            \"key\": \"FR_MORD\",\n            \"label\": \"º\",\n        }\n        \"A(FR_I)\": {\n            \"key\": \"FR_LICI\",\n            \"label\": \"î\",\n        }\n        \"A(FR_O)\": {\n            \"key\": \"FR_OE\",\n            \"label\": \"Œ\",\n        }\n        \"A(FR_P)\": {\n            \"key\": \"FR_PI\",\n            \"label\": \"π\",\n        }\n        \"A(FR_CIRC)\": {\n            \"key\": \"FR_OCIR\",\n            \"label\": \"Ô\",\n        }\n        \"A(FR_DLR)\": {\n            \"key\": \"FR_EURO\",\n            \"label\": \"€\",\n        }\n        \"A(FR_Q)\": {\n            \"key\": \"FR_DDAG\",\n            \"label\": \"‡\",\n        }\n        \"A(FR_S)\": {\n            \"key\": \"FR_COGR\",\n            \"label\": \"Ò\",\n        }\n        \"A(FR_D)\": {\n            \"key\": \"FR_PDIF\",\n            \"label\": \"∂\",\n        }\n        \"A(FR_F)\": {\n            \"key\": \"FR_FHK\",\n            \"label\": \"ƒ\",\n        }\n        \"A(FR_G)\": {\n            \"key\": \"FR_FI\",\n            \"label\": \"ﬁ\",\n        }\n        \"A(FR_H)\": {\n            \"key\": \"FR_CIGR\",\n            \"label\": \"Ì\",\n        }\n        \"A(FR_J)\": {\n            \"key\": \"FR_CIDI\",\n            \"label\": \"Ï\",\n        }\n        \"A(FR_K)\": {\n            \"key\": \"FR_CEGR\",\n            \"label\": \"È\",\n        }\n        \"A(FR_L)\": {\n            \"key\": \"FR_NOT\",\n            \"label\": \"¬\",\n        }\n        \"A(FR_M)\": {\n            \"key\": \"FR_MICR\",\n            \"label\": \"µ\",\n        }\n        \"A(FR_LUGR)\": {\n            \"key\": \"FR_CUGR\",\n            \"label\": \"Ù\",\n        }\n        \"A(FR_LABK)\": {\n            \"key\": \"FR_LTEQ\",\n            \"label\": \"≤\",\n        }\n        \"A(FR_W)\": {\n            \"key\": \"FR_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"A(FR_X)\": {\n            \"key\": \"FR_AEQL\",\n            \"label\": \"≈\",\n        }\n        \"A(FR_C)\": {\n            \"key\": \"FR_COPY\",\n            \"label\": \"©\",\n        }\n        \"A(FR_V)\": {\n            \"key\": \"FR_LOZN\",\n            \"label\": \"◊\",\n        }\n        \"A(FR_B)\": {\n            \"key\": \"FR_SS\",\n            \"label\": \"ß\",\n        }\n        \"A(FR_N)\": {\n            \"key\": \"FR_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"A(FR_COMM)\": {\n            \"key\": \"FR_INFN\",\n            \"label\": \"∞\",\n        }\n        \"A(FR_SCLN)\": {\n            \"key\": \"FR_ELLP\",\n            \"label\": \"…\",\n        }\n        \"A(FR_COLN)\": {\n            \"key\": \"FR_DIV\",\n            \"label\": \"÷\",\n        }\n        \"A(FR_EQL)\": {\n            \"key\": \"FR_NEQL\",\n            \"label\": \"≠\",\n        }\n/* Shift+Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ Ÿ │ ´ │ „ │   │   │ [ │ å │ » │ Û │ Á │   │ ] │ – │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │ Å │   │ ‚ │ ™ │   │ ª │ ï │   │ ∏ │   │ ¥ │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ Ω │ ∑ │ ∆ │ · │ ﬂ │ Î │ Í │ Ë │ | │ Ó │ ‰ │   │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≥ │ › │ ⁄ │ ¢ │ √ │ ∫ │ ı │ ¿ │   │ \\ │ ± │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(A(FR_AT))\": {\n            \"key\": \"FR_CYDI\",\n            \"label\": \"Ÿ\",\n        }\n        \"S(A(FR_AMPR))\": {\n            \"key\": \"FR_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"S(A(FR_LEAC))\": {\n            \"key\": \"FR_DLQU\",\n            \"label\": \"„\",\n        }\n        \"S(A(FR_LPRN))\": {\n            \"key\": \"FR_LBRC\",\n            \"label\": \"[\",\n        }\n        \"S(A(FR_SECT))\": {\n            \"key\": \"FR_LARI\",\n            \"label\": \"å\",\n        }\n        \"S(A(FR_LEGR))\": {\n            \"key\": \"FR_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(A(FR_EXLM))\": {\n            \"key\": \"FR_CUCI\",\n            \"label\": \"Û\",\n        }\n        \"S(A(FR_LCCE))\": {\n            \"key\": \"FR_CAAC\",\n            \"label\": \"Á\",\n        }\n        \"S(A(FR_RPRN))\": {\n            \"key\": \"FR_RBRC\",\n            \"label\": \"]\",\n        }\n        \"S(A(FR_MINS))\": {\n            \"key\": \"FR_NDSH\",\n            \"label\": \"–\",\n        }\n        \"S(A(FR_Z))\": {\n            \"key\": \"FR_CARI\",\n            \"label\": \"Å\",\n        }\n        \"S(A(FR_R))\": {\n            \"key\": \"FR_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"S(A(FR_T))\": {\n            \"key\": \"FR_TM\",\n            \"label\": \"™\",\n        }\n        \"S(A(FR_U))\": {\n            \"key\": \"FR_FORD\",\n            \"label\": \"ª\",\n        }\n        \"S(A(FR_I))\": {\n            \"key\": \"FR_LIDI\",\n            \"label\": \"ï\",\n        }\n        \"S(A(FR_P))\": {\n            \"key\": \"FR_NARP\",\n            \"label\": \"∏\",\n        }\n        \"S(A(FR_DLR))\": {\n            \"key\": \"FR_YEN\",\n            \"label\": \"¥\",\n        }\n        \"S(A(FR_Q))\": {\n            \"key\": \"FR_OMEG\",\n            \"label\": \"Ω\",\n        }\n        \"S(A(FR_S))\": {\n            \"key\": \"FR_NARS\",\n            \"label\": \"∑\",\n        }\n        \"S(A(FR_D))\": {\n            \"key\": \"FR_INCR\",\n            \"label\": \"∆\",\n        }\n        \"S(A(FR_F))\": {\n            \"key\": \"FR_MDDT\",\n            \"label\": \"·\",\n        }\n        \"S(A(FR_G))\": {\n            \"key\": \"FR_FL\",\n            \"label\": \"ﬂ\",\n        }\n        \"S(A(FR_H))\": {\n            \"key\": \"FR_CICI\",\n            \"label\": \"Î\",\n        }\n        \"S(A(FR_J))\": {\n            \"key\": \"FR_CIAC\",\n            \"label\": \"Í\",\n        }\n        \"S(A(FR_K))\": {\n            \"key\": \"FR_CEDI\",\n            \"label\": \"Ë\",\n        }\n        \"S(A(FR_L))\": {\n            \"key\": \"FR_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(A(FR_M))\": {\n            \"key\": \"FR_COAC\",\n            \"label\": \"Ó\",\n        }\n        \"S(A(FR_LUGR))\": {\n            \"key\": \"FR_PERM\",\n            \"label\": \"‰\",\n        }\n        \"S(A(FR_LABK))\": {\n            \"key\": \"FR_GTEQ\",\n            \"label\": \"≥\",\n        }\n        \"S(A(FR_W))\": {\n            \"key\": \"FR_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"S(A(FR_X))\": {\n            \"key\": \"FR_FRSL\",\n            \"label\": \"⁄\",\n        }\n        \"S(A(FR_C))\": {\n            \"key\": \"FR_CENT\",\n            \"label\": \"¢\",\n        }\n        \"S(A(FR_V))\": {\n            \"key\": \"FR_SQRT\",\n            \"label\": \"√\",\n        }\n        \"S(A(FR_B))\": {\n            \"key\": \"FR_INTG\",\n            \"label\": \"∫\",\n        }\n        \"S(A(FR_N))\": {\n            \"key\": \"FR_DLSI\",\n            \"label\": \"ı\",\n        }\n        \"S(A(FR_COMM))\": {\n            \"key\": \"FR_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"S(A(FR_COLN))\": {\n            \"key\": \"FR_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"S(A(FR_EQL))\": {\n            \"key\": \"FR_PLMN\",\n            \"label\": \"±\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_german_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ^ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ß │ ´ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ Ü │ + │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ö │ Ä │ # │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"DE_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_1\": {\n            \"key\": \"DE_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"DE_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"DE_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"DE_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"DE_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"DE_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"DE_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"DE_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"DE_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"DE_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"DE_SS\",\n            \"label\": \"ß\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"DE_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"DE_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"DE_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"DE_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"DE_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"DE_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"DE_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"DE_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"DE_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"DE_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"DE_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"DE_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"DE_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_A\": {\n            \"key\": \"DE_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"DE_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"DE_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"DE_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"DE_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"DE_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"DE_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"DE_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"DE_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"DE_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"DE_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"DE_HASH\",\n            \"label\": \"#\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"DE_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"DE_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"DE_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"DE_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"DE_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"DE_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"DE_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"DE_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"DE_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"DE_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"DE_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ° │ ! │ \" │ § │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ ` │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ * │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ ' │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(DE_CIRC)\": {\n            \"key\": \"DE_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(DE_1)\": {\n            \"key\": \"DE_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(DE_2)\": {\n            \"key\": \"DE_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(DE_3)\": {\n            \"key\": \"DE_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(DE_4)\": {\n            \"key\": \"DE_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(DE_5)\": {\n            \"key\": \"DE_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(DE_6)\": {\n            \"key\": \"DE_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(DE_7)\": {\n            \"key\": \"DE_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(DE_8)\": {\n            \"key\": \"DE_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(DE_9)\": {\n            \"key\": \"DE_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(DE_0)\": {\n            \"key\": \"DE_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(DE_SS)\": {\n            \"key\": \"DE_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(DE_ACUT)\": {\n            \"key\": \"DE_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(DE_PLUS)\": {\n            \"key\": \"DE_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(DE_HASH)\": {\n            \"key\": \"DE_QUOT\",\n            \"label\": \"'\",\n        }\n        \"S(DE_LABK)\": {\n            \"key\": \"DE_RABK\",\n            \"label\": \">\",\n        }\n        \"S(DE_COMM)\": {\n            \"key\": \"DE_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(DE_DOT)\": {\n            \"key\": \"DE_COLN\",\n            \"label\": \":\",\n        }\n        \"S(DE_MINS)\": {\n            \"key\": \"DE_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │ ² │ ³ │   │   │   │ { │ [ │ ] │ } │ \\ │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ @ │   │ € │   │   │   │   │   │   │   │   │ ~ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ | │   │   │   │   │   │   │ µ │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(DE_2)\": {\n            \"key\": \"DE_SUP2\",\n            \"label\": \"²\",\n        }\n        \"ALGR(DE_3)\": {\n            \"key\": \"DE_SUP3\",\n            \"label\": \"³\",\n        }\n        \"ALGR(DE_7)\": {\n            \"key\": \"DE_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(DE_8)\": {\n            \"key\": \"DE_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(DE_9)\": {\n            \"key\": \"DE_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(DE_0)\": {\n            \"key\": \"DE_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(DE_SS)\": {\n            \"key\": \"DE_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(DE_Q)\": {\n            \"key\": \"DE_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(DE_E)\": {\n            \"key\": \"DE_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(DE_PLUS)\": {\n            \"key\": \"DE_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(DE_LABK)\": {\n            \"key\": \"DE_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(DE_M)\": {\n            \"key\": \"DE_MICR\",\n            \"label\": \"µ\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_german_mac_iso_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ^ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ß │ ´ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ Ü │ + │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ö │ Ä │ # │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ < │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"DE_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_1\": {\n            \"key\": \"DE_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"DE_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"DE_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"DE_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"DE_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"DE_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"DE_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"DE_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"DE_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"DE_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"DE_SS\",\n            \"label\": \"ß\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"DE_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"DE_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"DE_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"DE_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"DE_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"DE_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"DE_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"DE_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"DE_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"DE_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"DE_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"DE_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"DE_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_A\": {\n            \"key\": \"DE_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"DE_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"DE_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"DE_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"DE_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"DE_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"DE_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"DE_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"DE_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"DE_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"DE_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"DE_HASH\",\n            \"label\": \"#\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"DE_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"DE_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"DE_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"DE_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"DE_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"DE_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"DE_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"DE_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"DE_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"DE_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"DE_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ° │ ! │ \" │ § │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ ` │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ * │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ ' │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(DE_CIRC)\": {\n            \"key\": \"DE_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(DE_1)\": {\n            \"key\": \"DE_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(DE_2)\": {\n            \"key\": \"DE_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(DE_3)\": {\n            \"key\": \"DE_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(DE_4)\": {\n            \"key\": \"DE_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(DE_5)\": {\n            \"key\": \"DE_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(DE_6)\": {\n            \"key\": \"DE_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(DE_7)\": {\n            \"key\": \"DE_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(DE_8)\": {\n            \"key\": \"DE_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(DE_9)\": {\n            \"key\": \"DE_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(DE_0)\": {\n            \"key\": \"DE_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(DE_SS)\": {\n            \"key\": \"DE_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(DE_ACUT)\": {\n            \"key\": \"DE_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(DE_PLUS)\": {\n            \"key\": \"DE_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(DE_HASH)\": {\n            \"key\": \"DE_QUOT\",\n            \"label\": \"'\",\n        }\n        \"S(DE_LABK)\": {\n            \"key\": \"DE_RABK\",\n            \"label\": \">\",\n        }\n        \"S(DE_COMM)\": {\n            \"key\": \"DE_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(DE_DOT)\": {\n            \"key\": \"DE_COLN\",\n            \"label\": \":\",\n        }\n        \"S(DE_MINS)\": {\n            \"key\": \"DE_UNDS\",\n            \"label\": \"_\",\n        }\n/* Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ „ │ ¡ │ “ │ ¶ │ ¢ │ [ │ ] │ | │ { │ } │ ≠ │ ¿ │   │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ « │ ∑ │ € │ ® │ † │ Ω │ ¨ │ ⁄ │ Ø │ π │ • │ ± │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ Å │ ‚ │ ∂ │ ƒ │ © │ ª │ º │ ∆ │ @ │ Œ │ Æ │ ‘ │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≤ │ ¥ │ ≈ │ Ç │ √ │ ∫ │ ~ │ µ │ ∞ │ … │ – │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"A(DE_CIRC)\": {\n            \"key\": \"DE_DLQU\",\n            \"label\": \"„\",\n        }\n        \"A(DE_1)\": {\n            \"key\": \"DE_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"A(DE_2)\": {\n            \"key\": \"DE_LDQU\",\n            \"label\": \"“\",\n        }\n        \"A(DE_3)\": {\n            \"key\": \"DE_PILC\",\n            \"label\": \"¶\",\n        }\n        \"A(DE_4)\": {\n            \"key\": \"DE_CENT\",\n            \"label\": \"¢\",\n        }\n        \"A(DE_5)\": {\n            \"key\": \"DE_LBRC\",\n            \"label\": \"[\",\n        }\n        \"A(DE_6)\": {\n            \"key\": \"DE_RBRC\",\n            \"label\": \"]\",\n        }\n        \"A(DE_7)\": {\n            \"key\": \"DE_PIPE\",\n            \"label\": \"|\",\n        }\n        \"A(DE_8)\": {\n            \"key\": \"DE_LCBR\",\n            \"label\": \"{\",\n        }\n        \"A(DE_9)\": {\n            \"key\": \"DE_RCBR\",\n            \"label\": \"}\",\n        }\n        \"A(DE_0)\": {\n            \"key\": \"DE_NEQL\",\n            \"label\": \"≠\",\n        }\n        \"A(DE_SS)\": {\n            \"key\": \"DE_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"A(DE_Q)\": {\n            \"key\": \"DE_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"A(DE_W)\": {\n            \"key\": \"DE_NARS\",\n            \"label\": \"∑\",\n        }\n        \"A(DE_E)\": {\n            \"key\": \"DE_EURO\",\n            \"label\": \"€\",\n        }\n        \"A(DE_R)\": {\n            \"key\": \"DE_REGD\",\n            \"label\": \"®\",\n        }\n        \"A(DE_T)\": {\n            \"key\": \"DE_DAGG\",\n            \"label\": \"†\",\n        }\n        \"A(DE_Z)\": {\n            \"key\": \"DE_OMEG\",\n            \"label\": \"Ω\",\n        }\n        \"A(DE_U)\": {\n            \"key\": \"DE_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"A(DE_I)\": {\n            \"key\": \"DE_FRSL\",\n            \"label\": \"⁄\",\n        }\n        \"A(DE_O)\": {\n            \"key\": \"DE_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"A(DE_P)\": {\n            \"key\": \"DE_PI\",\n            \"label\": \"π\",\n        }\n        \"A(DE_UDIA)\": {\n            \"key\": \"DE_BULT\",\n            \"label\": \"•\",\n        }\n        \"A(DE_PLUS)\": {\n            \"key\": \"DE_PLMN\",\n            \"label\": \"±\",\n        }\n        \"A(DE_A)\": {\n            \"key\": \"DE_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"A(DE_S)\": {\n            \"key\": \"DE_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"A(DE_D)\": {\n            \"key\": \"DE_PDIF\",\n            \"label\": \"∂\",\n        }\n        \"A(DE_F)\": {\n            \"key\": \"DE_FHK\",\n            \"label\": \"ƒ\",\n        }\n        \"A(DE_G)\": {\n            \"key\": \"DE_COPY\",\n            \"label\": \"©\",\n        }\n        \"A(DE_H)\": {\n            \"key\": \"DE_FORD\",\n            \"label\": \"ª\",\n        }\n        \"A(DE_J)\": {\n            \"key\": \"DE_MORD\",\n            \"label\": \"º\",\n        }\n        \"A(DE_K)\": {\n            \"key\": \"DE_INCR\",\n            \"label\": \"∆\",\n        }\n        \"A(DE_L)\": {\n            \"key\": \"DE_AT\",\n            \"label\": \"@\",\n        }\n        \"A(DE_ODIA)\": {\n            \"key\": \"DE_OE\",\n            \"label\": \"Œ\",\n        }\n        \"A(DE_ADIA)\": {\n            \"key\": \"DE_AE\",\n            \"label\": \"Æ\",\n        }\n        \"A(DE_HASH)\": {\n            \"key\": \"DE_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"A(DE_LABK)\": {\n            \"key\": \"DE_LTEQ\",\n            \"label\": \"≤\",\n        }\n        \"A(DE_Y)\": {\n            \"key\": \"DE_YEN\",\n            \"label\": \"¥\",\n        }\n        \"A(DE_X)\": {\n            \"key\": \"DE_AEQL\",\n            \"label\": \"≈\",\n        }\n        \"A(DE_C)\": {\n            \"key\": \"DE_CCCE\",\n            \"label\": \"Ç\",\n        }\n        \"A(DE_V)\": {\n            \"key\": \"DE_SQRT\",\n            \"label\": \"√\",\n        }\n        \"A(DE_B)\": {\n            \"key\": \"DE_INTG\",\n            \"label\": \"∫\",\n        }\n        \"A(DE_N)\": {\n            \"key\": \"DE_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"A(DE_M)\": {\n            \"key\": \"DE_MICR\",\n            \"label\": \"µ\",\n        }\n        \"A(DE_COMM)\": {\n            \"key\": \"DE_INFN\",\n            \"label\": \"∞\",\n        }\n        \"A(DE_DOT)\": {\n            \"key\": \"DE_ELLP\",\n            \"label\": \"…\",\n        }\n        \"A(DE_MINS)\": {\n            \"key\": \"DE_NDSH\",\n            \"label\": \"–\",\n        }\n/* Shift+Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │   │ ¬ │ ” │   │ £ │ ﬁ │   │ \\ │ ˜ │ · │ ¯ │ ˙ │ ˚ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ » │   │ ‰ │ ¸ │ ˝ │ ˇ │ Á │ Û │   │ ∏ │   │  │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │   │ Í │ ™ │ Ï │ Ì │ Ó │ ı │   │ ﬂ │   │   │   │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≥ │ ‡ │ Ù │   │ ◊ │ ‹ │ › │ ˘ │ ˛ │ ÷ │ — │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(A(DE_1))\": {\n            \"key\": \"DE_NOT\",\n            \"label\": \"¬\",\n        }\n        \"S(A(DE_2))\": {\n            \"key\": \"DE_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(A(DE_4))\": {\n            \"key\": \"DE_PND\",\n            \"label\": \"£\",\n        }\n        \"S(A(DE_5))\": {\n            \"key\": \"DE_FI\",\n            \"label\": \"ﬁ\",\n        }\n        \"S(A(DE_7))\": {\n            \"key\": \"DE_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"S(A(DE_8))\": {\n            \"key\": \"DE_STIL\",\n            \"label\": \"˜\",\n        }\n        \"S(A(DE_9))\": {\n            \"key\": \"DE_MDDT\",\n            \"label\": \"·\",\n        }\n        \"S(A(DE_0))\": {\n            \"key\": \"DE_MACR\",\n            \"label\": \"¯\",\n        }\n        \"S(A(DE_SS))\": {\n            \"key\": \"DE_DOTA\",\n            \"label\": \"˙\",\n        }\n        \"S(A(DE_ACUT))\": {\n            \"key\": \"DE_RNGA\",\n            \"label\": \"˚\",\n        }\n        \"S(A(DE_Q))\": {\n            \"key\": \"DE_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(A(DE_E))\": {\n            \"key\": \"DE_PERM\",\n            \"label\": \"‰\",\n        }\n        \"S(A(DE_R))\": {\n            \"key\": \"DE_CEDL\",\n            \"label\": \"¸\",\n        }\n        \"S(A(DE_T))\": {\n            \"key\": \"DE_DACU\",\n            \"label\": \"˝\",\n        }\n        \"S(A(DE_Z))\": {\n            \"key\": \"DE_CARN\",\n            \"label\": \"ˇ\",\n        }\n        \"S(A(DE_U))\": {\n            \"key\": \"DE_AACU\",\n            \"label\": \"Á\",\n        }\n        \"S(A(DE_I))\": {\n            \"key\": \"DE_UCIR\",\n            \"label\": \"Û\",\n        }\n        \"S(A(DE_P))\": {\n            \"key\": \"DE_NARP\",\n            \"label\": \"∏\",\n        }\n        \"S(A(DE_PLUS))\": {\n            \"key\": \"DE_APPL\",\n            \"label\": \" (Apple logo)\",\n        }\n        \"S(A(DE_S))\": {\n            \"key\": \"DE_IACU\",\n            \"label\": \"Í\",\n        }\n        \"S(A(DE_D))\": {\n            \"key\": \"DE_TM\",\n            \"label\": \"™\",\n        }\n        \"S(A(DE_F))\": {\n            \"key\": \"DE_IDIA\",\n            \"label\": \"Ï\",\n        }\n        \"S(A(DE_G))\": {\n            \"key\": \"DE_IGRV\",\n            \"label\": \"Ì\",\n        }\n        \"S(A(DE_H))\": {\n            \"key\": \"DE_OACU\",\n            \"label\": \"Ó\",\n        }\n        \"S(A(DE_J))\": {\n            \"key\": \"DE_DLSI\",\n            \"label\": \"ı\",\n        }\n        \"S(A(DE_L))\": {\n            \"key\": \"DE_FL\",\n            \"label\": \"ﬂ\",\n        }\n        \"S(A(DE_LABK))\": {\n            \"key\": \"DE_GTEQ\",\n            \"label\": \"≥\",\n        }\n        \"S(A(DE_Y))\": {\n            \"key\": \"DE_DDAG\",\n            \"label\": \"‡\",\n        }\n        \"S(A(DE_X))\": {\n            \"key\": \"DE_UGRV\",\n            \"label\": \"Ù\",\n        }\n        \"S(A(DE_V))\": {\n            \"key\": \"DE_LOZN\",\n            \"label\": \"◊\",\n        }\n        \"S(A(DE_B))\": {\n            \"key\": \"DE_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"S(A(DE_N))\": {\n            \"key\": \"DE_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"S(A(DE_M))\": {\n            \"key\": \"DE_BREV\",\n            \"label\": \"˘\",\n        }\n        \"S(A(DE_COMM))\": {\n            \"key\": \"DE_OGON\",\n            \"label\": \"˛\",\n        }\n        \"S(A(DE_DOT))\": {\n            \"key\": \"DE_DIV\",\n            \"label\": \"÷\",\n        }\n        \"S(A(DE_MINS))\": {\n            \"key\": \"DE_MDSH\",\n            \"label\": \"—\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_greek_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ ; │ ς │ Ε │ Ρ │ Τ │ Υ │ Θ │ Ι │ Ο │ Π │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ Α │ Σ │ Δ │ Φ │ Γ │ Η │ Ξ │ Κ │ Λ │ ΄ │ ' │ \\ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │ Ζ │ Χ │ Ψ │ Ω │ Β │ Ν │ Μ │ , │ . │ / │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"GR_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"GR_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"GR_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"GR_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"GR_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"GR_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"GR_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"GR_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"GR_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"GR_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"GR_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"GR_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"GR_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"GR_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_W\": {\n            \"key\": \"GR_FSIG\",\n            \"label\": \"ς\",\n        }\n        \"KC_E\": {\n            \"key\": \"GR_EPSL\",\n            \"label\": \"Ε\",\n        }\n        \"KC_R\": {\n            \"key\": \"GR_RHO\",\n            \"label\": \"Ρ\",\n        }\n        \"KC_T\": {\n            \"key\": \"GR_TAU\",\n            \"label\": \"Τ\",\n        }\n        \"KC_Y\": {\n            \"key\": \"GR_UPSL\",\n            \"label\": \"Υ\",\n        }\n        \"KC_U\": {\n            \"key\": \"GR_THET\",\n            \"label\": \"Θ\",\n        }\n        \"KC_I\": {\n            \"key\": \"GR_IOTA\",\n            \"label\": \"Ι\",\n        }\n        \"KC_O\": {\n            \"key\": \"GR_OMCR\",\n            \"label\": \"Ο\",\n        }\n        \"KC_P\": {\n            \"key\": \"GR_PI\",\n            \"label\": \"Π\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"GR_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"GR_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_A\": {\n            \"key\": \"GR_ALPH\",\n            \"label\": \"Α\",\n        }\n        \"KC_S\": {\n            \"key\": \"GR_SIGM\",\n            \"label\": \"Σ\",\n        }\n        \"KC_D\": {\n            \"key\": \"GR_DELT\",\n            \"label\": \"Δ\",\n        }\n        \"KC_F\": {\n            \"key\": \"GR_PHI\",\n            \"label\": \"Φ\",\n        }\n        \"KC_G\": {\n            \"key\": \"GR_GAMM\",\n            \"label\": \"Γ\",\n        }\n        \"KC_H\": {\n            \"key\": \"GR_ETA\",\n            \"label\": \"Η\",\n        }\n        \"KC_J\": {\n            \"key\": \"GR_XI\",\n            \"label\": \"Ξ\",\n        }\n        \"KC_K\": {\n            \"key\": \"GR_KAPP\",\n            \"label\": \"Κ\",\n        }\n        \"KC_L\": {\n            \"key\": \"GR_LAMB\",\n            \"label\": \"Λ\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"GR_TONS\",\n            \"label\": \"΄ (dead)\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"GR_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"GR_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_Z\": {\n            \"key\": \"GR_ZETA\",\n            \"label\": \"Ζ\",\n        }\n        \"KC_X\": {\n            \"key\": \"GR_CHI\",\n            \"label\": \"Χ\",\n        }\n        \"KC_C\": {\n            \"key\": \"GR_PSI\",\n            \"label\": \"Ψ\",\n        }\n        \"KC_V\": {\n            \"key\": \"GR_OMEG\",\n            \"label\": \"Ω\",\n        }\n        \"KC_B\": {\n            \"key\": \"GR_BETA\",\n            \"label\": \"Β\",\n        }\n        \"KC_N\": {\n            \"key\": \"GR_NU\",\n            \"label\": \"Ν\",\n        }\n        \"KC_M\": {\n            \"key\": \"GR_MU\",\n            \"label\": \"Μ\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"GR_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"GR_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"GR_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ : │ ΅ │   │   │   │   │   │   │   │   │ { │ } │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ ¨ │ \" │ | │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(GR_GRV)\": {\n            \"key\": \"GR_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(GR_1)\": {\n            \"key\": \"GR_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(GR_2)\": {\n            \"key\": \"GR_AT\",\n            \"label\": \"@\",\n        }\n        \"S(GR_3)\": {\n            \"key\": \"GR_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(GR_4)\": {\n            \"key\": \"GR_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(GR_5)\": {\n            \"key\": \"GR_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(GR_6)\": {\n            \"key\": \"GR_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(GR_7)\": {\n            \"key\": \"GR_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(GR_8)\": {\n            \"key\": \"GR_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(GR_9)\": {\n            \"key\": \"GR_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(GR_0)\": {\n            \"key\": \"GR_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(GR_MINS)\": {\n            \"key\": \"GR_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(GR_EQL)\": {\n            \"key\": \"GR_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(GR_SCLN)\": {\n            \"key\": \"GR_COLN\",\n            \"label\": \":\",\n        }\n        \"S(GR_FSIG)\": {\n            \"key\": \"GR_DIAT\",\n            \"label\": \"΅ (dead)\",\n        }\n        \"S(GR_LBRC)\": {\n            \"key\": \"GR_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(GR_RBRC)\": {\n            \"key\": \"GR_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(GR_TONS)\": {\n            \"key\": \"GR_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(GR_QUOT)\": {\n            \"key\": \"GR_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(GR_BSLS)\": {\n            \"key\": \"GR_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(GR_COMM)\": {\n            \"key\": \"GR_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(GR_DOT)\": {\n            \"key\": \"GR_RABK\",\n            \"label\": \">\",\n        }\n        \"S(GR_SLSH)\": {\n            \"key\": \"GR_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │ ² │ ³ │ £ │ § │ ¶ │   │ ¤ │ ¦ │ ° │ ± │ ½ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │ ® │   │ ¥ │   │   │   │   │ « │ » │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ ¬ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │ © │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(GR_2)\": {\n            \"key\": \"GR_SUP2\",\n            \"label\": \"²\",\n        }\n        \"ALGR(GR_3)\": {\n            \"key\": \"GR_SUP3\",\n            \"label\": \"³\",\n        }\n        \"ALGR(GR_4)\": {\n            \"key\": \"GR_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(GR_5)\": {\n            \"key\": \"GR_SECT\",\n            \"label\": \"§\",\n        }\n        \"ALGR(GR_6)\": {\n            \"key\": \"GR_PILC\",\n            \"label\": \"¶\",\n        }\n        \"ALGR(GR_8)\": {\n            \"key\": \"GR_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(GR_9)\": {\n            \"key\": \"GR_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"ALGR(GR_0)\": {\n            \"key\": \"GR_DEG\",\n            \"label\": \"°\",\n        }\n        \"ALGR(GR_MINS)\": {\n            \"key\": \"GR_PLMN\",\n            \"label\": \"±\",\n        }\n        \"ALGR(GR_EQL)\": {\n            \"key\": \"GR_HALF\",\n            \"label\": \"½\",\n        }\n        \"ALGR(GR_EPSL)\": {\n            \"key\": \"GR_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(GR_RHO)\": {\n            \"key\": \"GR_REGD\",\n            \"label\": \"®\",\n        }\n        \"ALGR(GR_UPSL)\": {\n            \"key\": \"GR_YEN\",\n            \"label\": \"¥\",\n        }\n        \"ALGR(GR_LBRC)\": {\n            \"key\": \"GR_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"ALGR(GR_RBRC)\": {\n            \"key\": \"GR_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"ALGR(GR_BSLS)\": {\n            \"key\": \"GR_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(GR_PSI)\": {\n            \"key\": \"GR_COPY\",\n            \"label\": \"©\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_hebrew_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ; │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ / │ ' │ פ │ ם │ ן │ ו │ ט │ א │ ר │ ק │ ] │ [ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ ף │ ך │ ל │ ח │ י │ ע │ כ │ ג │ ד │ ש │ , │ \\ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │ ץ │ ת │ צ │ מ │ נ │ ה │ ב │ ס │ ז │ . │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"IL_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_1\": {\n            \"key\": \"IL_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"IL_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"IL_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"IL_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"IL_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"IL_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"IL_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"IL_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"IL_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"IL_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"IL_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"IL_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"IL_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_W\": {\n            \"key\": \"IL_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_E\": {\n            \"key\": \"IL_QOF\",\n            \"label\": \"ק\",\n        }\n        \"KC_R\": {\n            \"key\": \"IL_RESH\",\n            \"label\": \"ר\",\n        }\n        \"KC_T\": {\n            \"key\": \"IL_ALEF\",\n            \"label\": \"א\",\n        }\n        \"KC_Y\": {\n            \"key\": \"IL_TET\",\n            \"label\": \"ט\",\n        }\n        \"KC_U\": {\n            \"key\": \"IL_VAV\",\n            \"label\": \"ו\",\n        }\n        \"KC_I\": {\n            \"key\": \"IL_FNUN\",\n            \"label\": \"ן\",\n        }\n        \"KC_O\": {\n            \"key\": \"IL_FMEM\",\n            \"label\": \"ם\",\n        }\n        \"KC_P\": {\n            \"key\": \"IL_PE\",\n            \"label\": \"פ\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"IL_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"IL_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_A\": {\n            \"key\": \"IL_SHIN\",\n            \"label\": \"ש\",\n        }\n        \"KC_S\": {\n            \"key\": \"IL_DALT\",\n            \"label\": \"ד\",\n        }\n        \"KC_D\": {\n            \"key\": \"IL_GIML\",\n            \"label\": \"ג\",\n        }\n        \"KC_F\": {\n            \"key\": \"IL_KAF\",\n            \"label\": \"כ\",\n        }\n        \"KC_G\": {\n            \"key\": \"IL_AYIN\",\n            \"label\": \"ע\",\n        }\n        \"KC_H\": {\n            \"key\": \"IL_YOD\",\n            \"label\": \"י\",\n        }\n        \"KC_J\": {\n            \"key\": \"IL_HET\",\n            \"label\": \"ח\",\n        }\n        \"KC_K\": {\n            \"key\": \"IL_LAMD\",\n            \"label\": \"ל\",\n        }\n        \"KC_L\": {\n            \"key\": \"IL_FKAF\",\n            \"label\": \"ך\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"IL_FPE\",\n            \"label\": \"ף\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"IL_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"IL_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_Z\": {\n            \"key\": \"IL_ZAYN\",\n            \"label\": \"ז\",\n        }\n        \"KC_X\": {\n            \"key\": \"IL_SMKH\",\n            \"label\": \"ס\",\n        }\n        \"KC_C\": {\n            \"key\": \"IL_BET\",\n            \"label\": \"ב\",\n        }\n        \"KC_V\": {\n            \"key\": \"IL_HE\",\n            \"label\": \"ה\",\n        }\n        \"KC_B\": {\n            \"key\": \"IL_NUN\",\n            \"label\": \"נ\",\n        }\n        \"KC_N\": {\n            \"key\": \"IL_MEM\",\n            \"label\": \"מ\",\n        }\n        \"KC_M\": {\n            \"key\": \"IL_TSDI\",\n            \"label\": \"צ\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"IL_TAV\",\n            \"label\": \"ת\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"IL_FTSD\",\n            \"label\": \"ץ\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"IL_DOT\",\n            \"label\": \".\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ) │ ( │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ } │ { │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ : │ \" │ | │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │ > │ < │ ? │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(IL_SCLN)\": {\n            \"key\": \"IL_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(IL_1)\": {\n            \"key\": \"IL_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(IL_2)\": {\n            \"key\": \"IL_AT\",\n            \"label\": \"@\",\n        }\n        \"S(IL_3)\": {\n            \"key\": \"IL_PND\",\n            \"label\": \"#\",\n        }\n        \"S(IL_4)\": {\n            \"key\": \"IL_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(IL_5)\": {\n            \"key\": \"IL_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(IL_6)\": {\n            \"key\": \"IL_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(IL_7)\": {\n            \"key\": \"IL_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(IL_8)\": {\n            \"key\": \"IL_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(IL_9)\": {\n            \"key\": \"IL_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(IL_0)\": {\n            \"key\": \"IL_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(IL_MINS)\": {\n            \"key\": \"IL_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(IL_EQL)\": {\n            \"key\": \"IL_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(IL_RBRC)\": {\n            \"key\": \"IL_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(IL_LBRC)\": {\n            \"key\": \"IL_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(IL_FPE)\": {\n            \"key\": \"IL_COLN\",\n            \"label\": \":\",\n        }\n        \"S(IL_COMM)\": {\n            \"key\": \"IL_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(IL_BSLS)\": {\n            \"key\": \"IL_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(IL_TAV)\": {\n            \"key\": \"IL_RABK\",\n            \"label\": \">\",\n        }\n        \"S(IL_FTSD)\": {\n            \"key\": \"IL_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(IL_DOT)\": {\n            \"key\": \"IL_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │   │ € │ ₪ │ ° │   │   │ × │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │ װ │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │ ײ │ ױ │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │ ÷ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(IL_3)\": {\n            \"key\": \"IL_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(IL_4)\": {\n            \"key\": \"IL_SHKL\",\n            \"label\": \"₪\",\n        }\n        \"ALGR(IL_5)\": {\n            \"key\": \"IL_DEG\",\n            \"label\": \"°\",\n        }\n        \"ALGR(IL_8)\": {\n            \"key\": \"IL_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(IL_TET)\": {\n            \"key\": \"IL_DVAV\",\n            \"label\": \"װ\",\n        }\n        \"ALGR(IL_AYIN)\": {\n            \"key\": \"IL_VYOD\",\n            \"label\": \"ױ\",\n        }\n        \"ALGR(IL_YOD)\": {\n            \"key\": \"IL_DYOD\",\n            \"label\": \"ײ\",\n        }\n        \"ALGR(IL_DOT)\": {\n            \"key\": \"IL_DIV\",\n            \"label\": \"÷\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_hungarian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ Ö │ Ü │ Ó │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ Ő │ Ú │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ É │ Á │ Ű │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ Í │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"HU_0\",\n            \"label\": \"0\",\n        }\n        \"KC_1\": {\n            \"key\": \"HU_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"HU_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"HU_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"HU_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"HU_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"HU_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"HU_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"HU_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"HU_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"HU_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"HU_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"HU_OACU\",\n            \"label\": \"Ó\",\n        }\n        \"KC_Q\": {\n            \"key\": \"HU_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"HU_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"HU_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"HU_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"HU_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"HU_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"HU_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"HU_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"HU_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"HU_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"HU_ODAC\",\n            \"label\": \"Ő\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"HU_UACU\",\n            \"label\": \"Ú\",\n        }\n        \"KC_A\": {\n            \"key\": \"HU_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"HU_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"HU_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"HU_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"HU_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"HU_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"HU_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"HU_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"HU_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"HU_EACU\",\n            \"label\": \"É\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"HU_AACU\",\n            \"label\": \"Á\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"HU_UDAC\",\n            \"label\": \"Ű\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"HU_IACU\",\n            \"label\": \"Í\",\n        }\n        \"KC_Z\": {\n            \"key\": \"HU_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"HU_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"HU_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"HU_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"HU_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"HU_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"HU_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"HU_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"HU_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"HU_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ § │ ' │ \" │ + │ ! │ % │ / │ = │ ( │ ) │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │ ? │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(HU_0)\": {\n            \"key\": \"HU_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(HU_1)\": {\n            \"key\": \"HU_QUOT\",\n            \"label\": \"'\",\n        }\n        \"S(HU_2)\": {\n            \"key\": \"HU_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(HU_3)\": {\n            \"key\": \"HU_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(HU_4)\": {\n            \"key\": \"HU_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(HU_5)\": {\n            \"key\": \"HU_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(HU_6)\": {\n            \"key\": \"HU_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(HU_7)\": {\n            \"key\": \"HU_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(HU_8)\": {\n            \"key\": \"HU_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(HU_9)\": {\n            \"key\": \"HU_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(HU_COMM)\": {\n            \"key\": \"HU_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(HU_DOT)\": {\n            \"key\": \"HU_COLN\",\n            \"label\": \":\",\n        }\n        \"S(HU_MINS)\": {\n            \"key\": \"HU_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ~ │ ˇ │ ^ │ ˘ │ ° │ ˛ │ ` │ ˙ │ ´ │ ˝ │ ¨ │ ¸ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ \\ │ | │ Ä │   │   │   │ € │   │   │   │ ÷ │ × │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ ä │ đ │ Đ │ [ │ ] │   │   │ ł │ Ł │ $ │ ß │ ¤ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ > │ # │ & │ @ │ { │ } │   │ ; │   │ * │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(HU_1)\": {\n            \"key\": \"HU_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(HU_2)\": {\n            \"key\": \"HU_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"ALGR(HU_3)\": {\n            \"key\": \"HU_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"ALGR(HU_4)\": {\n            \"key\": \"HU_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"ALGR(HU_5)\": {\n            \"key\": \"HU_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"ALGR(HU_6)\": {\n            \"key\": \"HU_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"ALGR(HU_7)\": {\n            \"key\": \"HU_GRV\",\n            \"label\": \"`\",\n        }\n        \"ALGR(HU_8)\": {\n            \"key\": \"HU_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n        \"ALGR(HU_9)\": {\n            \"key\": \"HU_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(HU_ODIA)\": {\n            \"key\": \"HU_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"ALGR(HU_UDIA)\": {\n            \"key\": \"HU_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"ALGR(HU_OACU)\": {\n            \"key\": \"HU_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"ALGR(HU_Q)\": {\n            \"key\": \"HU_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(HU_W)\": {\n            \"key\": \"HU_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(HU_E)\": {\n            \"key\": \"HU_CADI\",\n            \"label\": \"Ä\",\n        }\n        \"ALGR(HU_U)\": {\n            \"key\": \"HU_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(HU_ODAC)\": {\n            \"key\": \"HU_DIV\",\n            \"label\": \"÷\",\n        }\n        \"ALGR(HU_UACU)\": {\n            \"key\": \"HU_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(HU_A)\": {\n            \"key\": \"HU_LADI\",\n            \"label\": \"ä\",\n        }\n        \"ALGR(HU_S)\": {\n            \"key\": \"HU_LDST\",\n            \"label\": \"đ\",\n        }\n        \"ALGR(HU_D)\": {\n            \"key\": \"HU_CDST\",\n            \"label\": \"Đ\",\n        }\n        \"ALGR(HU_F)\": {\n            \"key\": \"HU_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(HU_G)\": {\n            \"key\": \"HU_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(HU_K)\": {\n            \"key\": \"HU_LLST\",\n            \"label\": \"ł\",\n        }\n        \"ALGR(HU_L)\": {\n            \"key\": \"HU_CLST\",\n            \"label\": \"Ł\",\n        }\n        \"ALGR(HU_EACU)\": {\n            \"key\": \"HU_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(HU_AACU)\": {\n            \"key\": \"HU_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(HU_UDAC)\": {\n            \"key\": \"HU_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(HU_IACU)\": {\n            \"key\": \"HU_LABK\",\n            \"label\": \"<\",\n        }\n        \"ALGR(HU_Y)\": {\n            \"key\": \"HU_RABK\",\n            \"label\": \">\",\n        }\n        \"ALGR(HU_X)\": {\n            \"key\": \"HU_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(HU_C)\": {\n            \"key\": \"HU_AMPR\",\n            \"label\": \"&\",\n        }\n        \"ALGR(HU_V)\": {\n            \"key\": \"HU_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(HU_B)\": {\n            \"key\": \"HU_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(HU_N)\": {\n            \"key\": \"HU_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(HU_COMM)\": {\n            \"key\": \"HU_SCLN\",\n            \"label\": \";\",\n        }\n        \"ALGR(HU_MINS)\": {\n            \"key\": \"HU_ASTR\",\n            \"label\": \"*\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_icelandic_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ° │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ Ö │ - │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Ð │ ' │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Æ │ ´ │ + │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ Þ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"IS_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"KC_1\": {\n            \"key\": \"IS_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"IS_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"IS_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"IS_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"IS_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"IS_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"IS_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"IS_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"IS_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"IS_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"IS_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"IS_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_Q\": {\n            \"key\": \"IS_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"IS_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"IS_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"IS_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"IS_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"IS_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"IS_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"IS_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"IS_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"IS_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"IS_ETH\",\n            \"label\": \"Ð\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"IS_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_A\": {\n            \"key\": \"IS_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"IS_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"IS_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"IS_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"IS_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"IS_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"IS_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"IS_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"IS_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"IS_AE\",\n            \"label\": \"Æ\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"IS_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"IS_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"IS_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"IS_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"IS_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"IS_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"IS_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"IS_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"IS_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"IS_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"IS_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"IS_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"IS_THRN\",\n            \"label\": \"Þ\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¨ │ ! │ \" │ # │ $ │ % │ & │ / │ ( │ ) │ = │   │ _ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ? │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ * │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(IS_RNGA)\": {\n            \"key\": \"IS_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(IS_1)\": {\n            \"key\": \"IS_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(IS_2)\": {\n            \"key\": \"IS_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(IS_3)\": {\n            \"key\": \"IS_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(IS_4)\": {\n            \"key\": \"IS_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(IS_5)\": {\n            \"key\": \"IS_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(IS_6)\": {\n            \"key\": \"IS_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(IS_7)\": {\n            \"key\": \"IS_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(IS_8)\": {\n            \"key\": \"IS_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(IS_9)\": {\n            \"key\": \"IS_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(IS_0)\": {\n            \"key\": \"IS_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(IS_MINS)\": {\n            \"key\": \"IS_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(IS_QUOT)\": {\n            \"key\": \"IS_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(IS_PLUS)\": {\n            \"key\": \"IS_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(IS_LABK)\": {\n            \"key\": \"IS_RABK\",\n            \"label\": \">\",\n        }\n        \"S(IS_COMM)\": {\n            \"key\": \"IS_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(IS_DOT)\": {\n            \"key\": \"IS_COLN\",\n            \"label\": \":\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ° │   │   │   │   │   │   │ { │ [ │ ] │ } │ \\ │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ @ │   │ € │   │   │   │   │   │   │   │   │ ~ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ ^ │ ` │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ | │   │   │   │   │   │   │ µ │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(IS_RNGA)\": {\n            \"key\": \"IS_DEG\",\n            \"label\": \"°\",\n        }\n        \"ALGR(IS_7)\": {\n            \"key\": \"IS_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(IS_8)\": {\n            \"key\": \"IS_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(IS_9)\": {\n            \"key\": \"IS_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(IS_0)\": {\n            \"key\": \"IS_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(IS_ODIA)\": {\n            \"key\": \"IS_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(IS_Q)\": {\n            \"key\": \"IS_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(IS_E)\": {\n            \"key\": \"IS_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(IS_QUOT)\": {\n            \"key\": \"IS_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(IS_ACUT)\": {\n            \"key\": \"IS_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"ALGR(IS_PLUS)\": {\n            \"key\": \"IS_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"ALGR(IS_LABK)\": {\n            \"key\": \"IS_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(IS_M)\": {\n            \"key\": \"IS_MICR\",\n            \"label\": \"µ\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_irish_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"IE_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"IE_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"IE_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"IE_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"IE_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"IE_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"IE_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"IE_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"IE_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"IE_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"IE_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"IE_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"IE_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"IE_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"IE_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"IE_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"IE_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"IE_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"IE_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"IE_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"IE_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"IE_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"IE_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"IE_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"IE_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_A\": {\n            \"key\": \"IE_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"IE_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"IE_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"IE_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"IE_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"IE_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"IE_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"IE_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"IE_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"IE_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"IE_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"IE_HASH\",\n            \"label\": \"#\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"IE_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_Z\": {\n            \"key\": \"IE_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"IE_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"IE_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"IE_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"IE_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"IE_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"IE_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"IE_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"IE_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"IE_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¬ │ ! │ \" │ £ │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ : │ @ │ ~ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ | │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(IE_GRV)\": {\n            \"key\": \"IE_NOT\",\n            \"label\": \"¬\",\n        }\n        \"S(IE_1)\": {\n            \"key\": \"IE_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(IE_2)\": {\n            \"key\": \"IE_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(IE_3)\": {\n            \"key\": \"IE_PND\",\n            \"label\": \"£\",\n        }\n        \"S(IE_4)\": {\n            \"key\": \"IE_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(IE_5)\": {\n            \"key\": \"IE_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(IE_6)\": {\n            \"key\": \"IE_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(IE_7)\": {\n            \"key\": \"IE_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(IE_8)\": {\n            \"key\": \"IE_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(IE_9)\": {\n            \"key\": \"IE_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(IE_0)\": {\n            \"key\": \"IE_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(IE_MINS)\": {\n            \"key\": \"IE_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(IE_EQL)\": {\n            \"key\": \"IE_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(IE_LBRC)\": {\n            \"key\": \"IE_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(IE_RBRC)\": {\n            \"key\": \"IE_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(IE_SCLN)\": {\n            \"key\": \"IE_COLN\",\n            \"label\": \":\",\n        }\n        \"S(IE_QUOT)\": {\n            \"key\": \"IE_AT\",\n            \"label\": \"@\",\n        }\n        \"S(IE_HASH)\": {\n            \"key\": \"IE_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(IE_BSLS)\": {\n            \"key\": \"IE_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(IE_COMM)\": {\n            \"key\": \"IE_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(IE_DOT)\": {\n            \"key\": \"IE_RABK\",\n            \"label\": \">\",\n        }\n        \"S(IE_SLSH)\": {\n            \"key\": \"IE_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¦ │   │   │   │ € │   │   │   │   │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ É │   │   │   │ Ú │ Í │ Ó │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ Á │   │   │   │   │   │   │   │   │   │ ´ │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(IE_GRV)\": {\n            \"key\": \"IE_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"ALGR(IE_4)\": {\n            \"key\": \"IE_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(IE_E)\": {\n            \"key\": \"IE_EACU\",\n            \"label\": \"É\",\n        }\n        \"ALGR(IE_U)\": {\n            \"key\": \"IE_UACU\",\n            \"label\": \"Ú\",\n        }\n        \"ALGR(IE_I)\": {\n            \"key\": \"IE_IACU\",\n            \"label\": \"Í\",\n        }\n        \"ALGR(IE_O)\": {\n            \"key\": \"IE_OACU\",\n            \"label\": \"Ó\",\n        }\n        \"ALGR(IE_A)\": {\n            \"key\": \"IE_AACU\",\n            \"label\": \"Á\",\n        }\n        \"ALGR(IE_QUOT)\": {\n            \"key\": \"IE_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_italian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ \\ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ ì │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ è │ + │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ò │ à │ ù │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"IT_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_1\": {\n            \"key\": \"IT_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"IT_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"IT_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"IT_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"IT_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"IT_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"IT_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"IT_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"IT_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"IT_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"IT_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"IT_IGRV\",\n            \"label\": \"ì\",\n        }\n        \"KC_Q\": {\n            \"key\": \"IT_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"IT_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"IT_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"IT_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"IT_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"IT_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"IT_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"IT_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"IT_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"IT_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"IT_EGRV\",\n            \"label\": \"è\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"IT_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_A\": {\n            \"key\": \"IT_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"IT_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"IT_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"IT_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"IT_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"IT_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"IT_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"IT_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"IT_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"IT_OGRV\",\n            \"label\": \"ò\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"IT_AGRV\",\n            \"label\": \"à\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"IT_UGRV\",\n            \"label\": \"ù\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"IT_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"IT_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"IT_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"IT_C\",\n            \"label\": \"C\",\n        }\n        \"KC_B\": {\n            \"key\": \"IT_B\",\n            \"label\": \"B\",\n        }\n        \"KC_V\": {\n            \"key\": \"IT_V\",\n            \"label\": \"V\",\n        }\n        \"KC_N\": {\n            \"key\": \"IT_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"IT_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"IT_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"IT_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"IT_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ | │ ! │ \" │ £ │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ ^ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ é │ * │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ ç │ ° │ § │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(IT_BSLS)\": {\n            \"key\": \"IT_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(IT_1)\": {\n            \"key\": \"IT_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(IT_2)\": {\n            \"key\": \"IT_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(IT_3)\": {\n            \"key\": \"IT_PND\",\n            \"label\": \"£\",\n        }\n        \"S(IT_4)\": {\n            \"key\": \"IT_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(IT_5)\": {\n            \"key\": \"IT_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(IT_6)\": {\n            \"key\": \"IT_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(IT_7)\": {\n            \"key\": \"IT_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(IT_8)\": {\n            \"key\": \"IT_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(IT_9)\": {\n            \"key\": \"IT_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(IT_0)\": {\n            \"key\": \"IT_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(IT_QUOT)\": {\n            \"key\": \"IT_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(IT_IGRV)\": {\n            \"key\": \"IT_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(IT_EGRV)\": {\n            \"key\": \"IT_EACU\",\n            \"label\": \"é\",\n        }\n        \"S(IT_PLUS)\": {\n            \"key\": \"IT_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(IT_OGRV)\": {\n            \"key\": \"IT_CCED\",\n            \"label\": \"ç\",\n        }\n        \"S(IT_AGRV)\": {\n            \"key\": \"IT_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(IT_UGRV)\": {\n            \"key\": \"IT_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(IT_LABK)\": {\n            \"key\": \"IT_RABK\",\n            \"label\": \">\",\n        }\n        \"S(IT_DOT)\": {\n            \"key\": \"IT_COLN\",\n            \"label\": \":\",\n        }\n        \"S(IT_COMM)\": {\n            \"key\": \"IT_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(IT_MINS)\": {\n            \"key\": \"IT_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │   │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ @ │ # │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(IT_E)\": {\n            \"key\": \"IT_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(IT_EGRV)\": {\n            \"key\": \"IT_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(IT_PLUS)\": {\n            \"key\": \"IT_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(IT_OGRV)\": {\n            \"key\": \"IT_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(IT_AGRV)\": {\n            \"key\": \"IT_HASH\",\n            \"label\": \"#\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ALGR(IT_EGRV))\": {\n            \"key\": \"IT_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(ALGR(IT_PLUS))\": {\n            \"key\": \"IT_RCBR\",\n            \"label\": \"}\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_italian_mac_ansi_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ < │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ ì │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ è │ + │ ù │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ò │ à │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"IT_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_1\": {\n            \"key\": \"IT_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"IT_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"IT_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"IT_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"IT_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"IT_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"IT_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"IT_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"IT_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"IT_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"IT_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"IT_IGRV\",\n            \"label\": \"ì\",\n        }\n        \"KC_Q\": {\n            \"key\": \"IT_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"IT_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"IT_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"IT_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"IT_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"IT_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"IT_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"IT_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"IT_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"IT_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"IT_EGRV\",\n            \"label\": \"è\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"IT_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"IT_UGRV\",\n            \"label\": \"ù\",\n        }\n        \"KC_A\": {\n            \"key\": \"IT_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"IT_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"IT_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"IT_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"IT_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"IT_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"IT_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"IT_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"IT_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"IT_OGRV\",\n            \"label\": \"ò\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"IT_AGRV\",\n            \"label\": \"à\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"IT_BSLS\",\n            \"label\": \"(backslash, not physically present)\",\n        }\n        \"KC_Z\": {\n            \"key\": \"IT_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"IT_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"IT_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"IT_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"IT_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"IT_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"IT_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"IT_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"IT_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"IT_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ > │ ! │ \" │ £ │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ ^ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │   │   │   │   │ é │ * │ § │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │   │   │   │   │   │   │   │   │   │ ç │ ° │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │   │   │   │   │   │   │   │ ; │ : │ _ │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(IT_LABK)\": {\n            \"key\": \"IT_RABK\",\n            \"label\": \">\",\n        }\n        \"S(IT_1)\": {\n            \"key\": \"IT_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(IT_2)\": {\n            \"key\": \"IT_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(IT_3)\": {\n            \"key\": \"IT_PND\",\n            \"label\": \"£\",\n        }\n        \"S(IT_4)\": {\n            \"key\": \"IT_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(IT_5)\": {\n            \"key\": \"IT_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(IT_6)\": {\n            \"key\": \"IT_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(IT_7)\": {\n            \"key\": \"IT_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(IT_8)\": {\n            \"key\": \"IT_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(IT_9)\": {\n            \"key\": \"IT_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(IT_0)\": {\n            \"key\": \"IT_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(IT_QUOT)\": {\n            \"key\": \"IT_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(IT_IGRV)\": {\n            \"key\": \"IT_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(IT_EGRV)\": {\n            \"key\": \"IT_EACU\",\n            \"label\": \"é\",\n        }\n        \"S(IT_PLUS)\": {\n            \"key\": \"IT_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(IT_UGRV)\": {\n            \"key\": \"IT_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(IT_OGRV)\": {\n            \"key\": \"IT_LCCE\",\n            \"label\": \"ç\",\n        }\n        \"S(IT_AGRV)\": {\n            \"key\": \"IT_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(IT_BSLS)\": {\n            \"key\": \"IT_PIPE\",\n            \"label\": \"| (not physically present)\",\n        }\n        \"S(IT_COMM)\": {\n            \"key\": \"IT_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(IT_DOT)\": {\n            \"key\": \"IT_COLN\",\n            \"label\": \":\",\n        }\n        \"S(IT_MINS)\": {\n            \"key\": \"IT_UNDS\",\n            \"label\": \"_\",\n        }\n/* Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ≤ │ « │ “ │ ‘ │ ¥ │ ~ │ ‹ │ ÷ │ ´ │ ` │ ≠ │ ¡ │ ˆ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ „ │ Ω │ € │ ® │ ™ │ Æ │ ¨ │ Œ │ Ø │ π │ [ │ ] │ ¶ │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │ Å │ ß │ ∂ │ ƒ │ ∞ │ ∆ │ ª │ º │ ¬ │ @ │ # │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │ ∑ │ † │ © │ √ │ ∫ │ ˜ │ µ │ … │ • │ – │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"A(IT_LABK)\": {\n            \"key\": \"IT_LTEQ\",\n            \"label\": \"≤\",\n        }\n        \"A(IT_1)\": {\n            \"key\": \"IT_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"A(IT_2)\": {\n            \"key\": \"IT_LDQU\",\n            \"label\": \"“\",\n        }\n        \"A(IT_3)\": {\n            \"key\": \"IT_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"A(IT_4)\": {\n            \"key\": \"IT_YEN\",\n            \"label\": \"¥\",\n        }\n        \"A(IT_5)\": {\n            \"key\": \"IT_TILD\",\n            \"label\": \"~\",\n        }\n        \"A(IT_6)\": {\n            \"key\": \"IT_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"A(IT_7)\": {\n            \"key\": \"IT_DIV\",\n            \"label\": \"÷\",\n        }\n        \"A(IT_8)\": {\n            \"key\": \"IT_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"A(IT_9)\": {\n            \"key\": \"IT_DGRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"A(IT_0)\": {\n            \"key\": \"IT_NEQL\",\n            \"label\": \"≠\",\n        }\n        \"A(IT_QUOT)\": {\n            \"key\": \"IT_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"A(IT_IGRV)\": {\n            \"key\": \"IT_DCIR\",\n            \"label\": \"ˆ (dead)\",\n        }\n        \"A(IT_Q)\": {\n            \"key\": \"IT_DLQU\",\n            \"label\": \"„\",\n        }\n        \"A(IT_W)\": {\n            \"key\": \"IT_OMEG\",\n            \"label\": \"Ω\",\n        }\n        \"A(IT_E)\": {\n            \"key\": \"IT_EURO\",\n            \"label\": \"€\",\n        }\n        \"A(IT_R)\": {\n            \"key\": \"IT_REGD\",\n            \"label\": \"®\",\n        }\n        \"A(IT_T)\": {\n            \"key\": \"IT_TM\",\n            \"label\": \"™\",\n        }\n        \"A(IT_Y)\": {\n            \"key\": \"IT_AE\",\n            \"label\": \"Æ\",\n        }\n        \"A(IT_U)\": {\n            \"key\": \"IT_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"A(IT_I)\": {\n            \"key\": \"IT_OE\",\n            \"label\": \"Œ\",\n        }\n        \"A(IT_O)\": {\n            \"key\": \"IT_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"A(IT_P)\": {\n            \"key\": \"IT_PI\",\n            \"label\": \"π\",\n        }\n        \"A(IT_EGRV)\": {\n            \"key\": \"IT_LBRC\",\n            \"label\": \"[\",\n        }\n        \"A(IT_PLUS)\": {\n            \"key\": \"IT_RBRC\",\n            \"label\": \"]\",\n        }\n        \"A(IT_A)\": {\n            \"key\": \"IT_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"A(IT_S)\": {\n            \"key\": \"IT_SS\",\n            \"label\": \"ß\",\n        }\n        \"A(IT_D)\": {\n            \"key\": \"IT_PDIF\",\n            \"label\": \"∂\",\n        }\n        \"A(IT_F)\": {\n            \"key\": \"IT_FHK\",\n            \"label\": \"ƒ\",\n        }\n        \"A(IT_G)\": {\n            \"key\": \"IT_INFN\",\n            \"label\": \"∞\",\n        }\n        \"A(IT_H)\": {\n            \"key\": \"IT_INCR\",\n            \"label\": \"∆\",\n        }\n        \"A(IT_J)\": {\n            \"key\": \"IT_FORD\",\n            \"label\": \"ª\",\n        }\n        \"A(IT_K)\": {\n            \"key\": \"IT_MORD\",\n            \"label\": \"º\",\n        }\n        \"A(IT_L)\": {\n            \"key\": \"IT_NOT\",\n            \"label\": \"¬\",\n        }\n        \"A(IT_OGRV)\": {\n            \"key\": \"IT_AT\",\n            \"label\": \"@\",\n        }\n        \"A(IT_AGRV)\": {\n            \"key\": \"IT_HASH\",\n            \"label\": \"#\",\n        }\n        \"A(IT_UGRV)\": {\n            \"key\": \"IT_PILC\",\n            \"label\": \"¶\",\n        }\n        \"A(IT_BSLS)\": {\n            \"key\": \"IT_GRV\",\n            \"label\": \"` (not physically present)\",\n        }\n        \"A(IT_Z)\": {\n            \"key\": \"IT_NARS\",\n            \"label\": \"∑\",\n        }\n        \"A(IT_X)\": {\n            \"key\": \"IT_DAGG\",\n            \"label\": \"†\",\n        }\n        \"A(IT_C)\": {\n            \"key\": \"IT_COPY\",\n            \"label\": \"©\",\n        }\n        \"A(IT_V)\": {\n            \"key\": \"IT_SQRT\",\n            \"label\": \"√\",\n        }\n        \"A(IT_B)\": {\n            \"key\": \"IT_INTG\",\n            \"label\": \"∫\",\n        }\n        \"A(IT_N)\": {\n            \"key\": \"IT_STIL\",\n            \"label\": \"˜ (dead)\",\n        }\n        \"A(IT_M)\": {\n            \"key\": \"IT_MICR\",\n            \"label\": \"µ\",\n        }\n        \"A(IT_COMM)\": {\n            \"key\": \"IT_ELLP\",\n            \"label\": \"…\",\n        }\n        \"A(IT_DOT)\": {\n            \"key\": \"IT_BULT\",\n            \"label\": \"•\",\n        }\n        \"A(IT_MINS)\": {\n            \"key\": \"IT_NDSH\",\n            \"label\": \"–\",\n        }\n/* Shift+Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ≥ │ » │ ” │ ’ │ ¢ │ ‰ │ › │ ⁄ │  │   │ ≈ │ ¿ │ ± │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ ‚ │ À │ È │ Ì │ Ò │   │ Ù │   │   │ ∏ │ { │ } │ ◊ │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │   │ ¯ │ ˘ │ ˙ │ ˚ │ ¸ │ ˝ │ ˛ │ ˇ │ Ç │ ∞ │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │   │ ‡ │ Á │ É │ Í │ Ó │ Ú │   │ · │ — │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(A(IT_LABK))\": {\n            \"key\": \"IT_GTEQ\",\n            \"label\": \"≥\",\n        }\n        \"S(A(IT_1))\": {\n            \"key\": \"IT_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(A(IT_2))\": {\n            \"key\": \"IT_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(A(IT_3))\": {\n            \"key\": \"IT_RSQU\",\n            \"label\": \"’\",\n        }\n        \"S(A(IT_4))\": {\n            \"key\": \"IT_CENT\",\n            \"label\": \"¢\",\n        }\n        \"S(A(IT_5))\": {\n            \"key\": \"IT_PERM\",\n            \"label\": \"‰\",\n        }\n        \"S(A(IT_6))\": {\n            \"key\": \"IT_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"S(A(IT_7))\": {\n            \"key\": \"IT_FRSL\",\n            \"label\": \"⁄\",\n        }\n        \"S(A(IT_8))\": {\n            \"key\": \"IT_APPL\",\n            \"label\": \" (Apple logo)\",\n        }\n        \"S(A(IT_0))\": {\n            \"key\": \"IT_AEQL\",\n            \"label\": \"≈\",\n        }\n        \"S(A(IT_QUOT))\": {\n            \"key\": \"IT_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"S(A(IT_IGRV))\": {\n            \"key\": \"IT_PLMN\",\n            \"label\": \"±\",\n        }\n        \"S(A(IT_Q))\": {\n            \"key\": \"IT_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"S(A(IT_W))\": {\n            \"key\": \"IT_CAGR\",\n            \"label\": \"À\",\n        }\n        \"S(A(IT_E))\": {\n            \"key\": \"IT_CEGR\",\n            \"label\": \"È\",\n        }\n        \"S(A(IT_R))\": {\n            \"key\": \"IT_CIGR\",\n            \"label\": \"Ì\",\n        }\n        \"S(A(IT_T))\": {\n            \"key\": \"IT_COGR\",\n            \"label\": \"Ò\",\n        }\n        \"S(A(IT_U))\": {\n            \"key\": \"IT_CUGR\",\n            \"label\": \"Ù\",\n        }\n        \"S(A(IT_P))\": {\n            \"key\": \"IT_NARP\",\n            \"label\": \"∏\",\n        }\n        \"S(A(IT_EGRV))\": {\n            \"key\": \"IT_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(A(IT_PLUS))\": {\n            \"key\": \"IT_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(A(IT_UGRV))\": {\n            \"key\": \"IT_LOZN\",\n            \"label\": \"◊\",\n        }\n        \"S(A(IT_S))\": {\n            \"key\": \"IT_MACR\",\n            \"label\": \"¯\",\n        }\n        \"S(A(IT_D))\": {\n            \"key\": \"IT_BREV\",\n            \"label\": \"˘\",\n        }\n        \"S(A(IT_F))\": {\n            \"key\": \"IT_DOTA\",\n            \"label\": \"˙\",\n        }\n        \"S(A(IT_G))\": {\n            \"key\": \"IT_RGNA\",\n            \"label\": \"˚\",\n        }\n        \"S(A(IT_H))\": {\n            \"key\": \"IT_CEDL\",\n            \"label\": \"¸\",\n        }\n        \"S(A(IT_J))\": {\n            \"key\": \"IT_DACU\",\n            \"label\": \"˝\",\n        }\n        \"S(A(IT_K))\": {\n            \"key\": \"IT_OGON\",\n            \"label\": \"˛\",\n        }\n        \"S(A(IT_L))\": {\n            \"key\": \"IT_CARN\",\n            \"label\": \"ˇ\",\n        }\n        \"S(A(IT_OGRV))\": {\n            \"key\": \"IT_CCCE\",\n            \"label\": \"Ç\",\n        }\n        \"S(A(IT_X))\": {\n            \"key\": \"IT_DDAG\",\n            \"label\": \"‡\",\n        }\n        \"S(A(IT_C))\": {\n            \"key\": \"IT_CAAC\",\n            \"label\": \"Á\",\n        }\n        \"S(A(IT_V))\": {\n            \"key\": \"IT_CEAC\",\n            \"label\": \"É\",\n        }\n        \"S(A(IT_B))\": {\n            \"key\": \"IT_CIAC\",\n            \"label\": \"Í\",\n        }\n        \"S(A(IT_N))\": {\n            \"key\": \"IT_COAC\",\n            \"label\": \"Ó\",\n        }\n        \"S(A(IT_M))\": {\n            \"key\": \"IT_CUAC\",\n            \"label\": \"Ú\",\n        }\n        \"S(A(IT_DOT))\": {\n            \"key\": \"IT_MDDT\",\n            \"label\": \"·\",\n        }\n        \"S(A(IT_MINS))\": {\n            \"key\": \"IT_MDSH\",\n            \"label\": \"—\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_italian_mac_iso_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ \\ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ ì │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ è │ + │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ò │ à │ ù │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"IT_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_1\": {\n            \"key\": \"IT_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"IT_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"IT_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"IT_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"IT_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"IT_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"IT_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"IT_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"IT_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"IT_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"IT_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"IT_IGRV\",\n            \"label\": \"ì\",\n        }\n        \"KC_Q\": {\n            \"key\": \"IT_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"IT_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"IT_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"IT_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"IT_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"IT_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"IT_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"IT_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"IT_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"IT_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"IT_EGRV\",\n            \"label\": \"è\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"IT_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_A\": {\n            \"key\": \"IT_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"IT_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"IT_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"IT_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"IT_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"IT_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"IT_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"IT_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"IT_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"IT_OGRV\",\n            \"label\": \"ò\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"IT_AGRV\",\n            \"label\": \"à\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"IT_UGRV\",\n            \"label\": \"ù\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"IT_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"IT_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"IT_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"IT_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"IT_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"IT_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"IT_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"IT_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"IT_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"IT_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"IT_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ | │ ! │ \" │ £ │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ ^ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │   │   │   │   │ é │ * │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │   │   │   │   │   │   │   │   │   │ ç │ ° │ § │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(IT_BSLS)\": {\n            \"key\": \"IT_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(IT_1)\": {\n            \"key\": \"IT_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(IT_2)\": {\n            \"key\": \"IT_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(IT_3)\": {\n            \"key\": \"IT_PND\",\n            \"label\": \"£\",\n        }\n        \"S(IT_4)\": {\n            \"key\": \"IT_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(IT_5)\": {\n            \"key\": \"IT_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(IT_6)\": {\n            \"key\": \"IT_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(IT_7)\": {\n            \"key\": \"IT_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(IT_8)\": {\n            \"key\": \"IT_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(IT_9)\": {\n            \"key\": \"IT_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(IT_0)\": {\n            \"key\": \"IT_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(IT_QUOT)\": {\n            \"key\": \"IT_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(IT_IGRV)\": {\n            \"key\": \"IT_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(IT_EGRV)\": {\n            \"key\": \"IT_EACU\",\n            \"label\": \"é\",\n        }\n        \"S(IT_PLUS)\": {\n            \"key\": \"IT_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(IT_OGRV)\": {\n            \"key\": \"IT_LCCE\",\n            \"label\": \"ç\",\n        }\n        \"S(IT_AGRV)\": {\n            \"key\": \"IT_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(IT_UGRV)\": {\n            \"key\": \"IT_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(IT_LABK)\": {\n            \"key\": \"IT_RABK\",\n            \"label\": \">\",\n        }\n        \"S(IT_COMM)\": {\n            \"key\": \"IT_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(IT_DOT)\": {\n            \"key\": \"IT_COLN\",\n            \"label\": \":\",\n        }\n        \"S(IT_MINS)\": {\n            \"key\": \"IT_UNDS\",\n            \"label\": \"_\",\n        }\n/* Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ` │ « │ “ │ ‘ │ ¥ │ ~ │ ‹ │ ÷ │ ´ │ ` │ ≠ │ ¡ │ ˆ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ „ │ Ω │ € │ ® │ ™ │ Æ │ ¨ │ Œ │ Ø │ π │ [ │ ] │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ Å │ ß │ ∂ │ ƒ │ ∞ │ ∆ │ ª │ º │ ¬ │ @ │ # │ ¶ │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≤ │ ∑ │ † │ © │ √ │ ∫ │ ˜ │ µ │ … │ • │ – │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"A(IT_BSLS)\": {\n            \"key\": \"IT_GRV\",\n            \"label\": \"`\",\n        }\n        \"A(IT_1)\": {\n            \"key\": \"IT_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"A(IT_2)\": {\n            \"key\": \"IT_LDQU\",\n            \"label\": \"“\",\n        }\n        \"A(IT_3)\": {\n            \"key\": \"IT_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"A(IT_4)\": {\n            \"key\": \"IT_YEN\",\n            \"label\": \"¥\",\n        }\n        \"A(IT_5)\": {\n            \"key\": \"IT_TILD\",\n            \"label\": \"~\",\n        }\n        \"A(IT_6)\": {\n            \"key\": \"IT_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"A(IT_7)\": {\n            \"key\": \"IT_DIV\",\n            \"label\": \"÷\",\n        }\n        \"A(IT_8)\": {\n            \"key\": \"IT_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"A(IT_9)\": {\n            \"key\": \"IT_DGRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"A(IT_0)\": {\n            \"key\": \"IT_NEQL\",\n            \"label\": \"≠\",\n        }\n        \"A(IT_QUOT)\": {\n            \"key\": \"IT_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"A(IT_IGRV)\": {\n            \"key\": \"IT_DCIR\",\n            \"label\": \"ˆ (dead)\",\n        }\n        \"A(IT_Q)\": {\n            \"key\": \"IT_DLQU\",\n            \"label\": \"„\",\n        }\n        \"A(IT_W)\": {\n            \"key\": \"IT_OMEG\",\n            \"label\": \"Ω\",\n        }\n        \"A(IT_E)\": {\n            \"key\": \"IT_EURO\",\n            \"label\": \"€\",\n        }\n        \"A(IT_R)\": {\n            \"key\": \"IT_REGD\",\n            \"label\": \"®\",\n        }\n        \"A(IT_T)\": {\n            \"key\": \"IT_TM\",\n            \"label\": \"™\",\n        }\n        \"A(IT_Y)\": {\n            \"key\": \"IT_AE\",\n            \"label\": \"Æ\",\n        }\n        \"A(IT_U)\": {\n            \"key\": \"IT_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"A(IT_I)\": {\n            \"key\": \"IT_OE\",\n            \"label\": \"Œ\",\n        }\n        \"A(IT_O)\": {\n            \"key\": \"IT_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"A(IT_P)\": {\n            \"key\": \"IT_PI\",\n            \"label\": \"π\",\n        }\n        \"A(IT_EGRV)\": {\n            \"key\": \"IT_LBRC\",\n            \"label\": \"[\",\n        }\n        \"A(IT_PLUS)\": {\n            \"key\": \"IT_RBRC\",\n            \"label\": \"]\",\n        }\n        \"A(IT_A)\": {\n            \"key\": \"IT_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"A(IT_S)\": {\n            \"key\": \"IT_SS\",\n            \"label\": \"ß\",\n        }\n        \"A(IT_D)\": {\n            \"key\": \"IT_PDIF\",\n            \"label\": \"∂\",\n        }\n        \"A(IT_F)\": {\n            \"key\": \"IT_FHK\",\n            \"label\": \"ƒ\",\n        }\n        \"A(IT_G)\": {\n            \"key\": \"IT_INFN\",\n            \"label\": \"∞\",\n        }\n        \"A(IT_H)\": {\n            \"key\": \"IT_INCR\",\n            \"label\": \"∆\",\n        }\n        \"A(IT_J)\": {\n            \"key\": \"IT_FORD\",\n            \"label\": \"ª\",\n        }\n        \"A(IT_K)\": {\n            \"key\": \"IT_MORD\",\n            \"label\": \"º\",\n        }\n        \"A(IT_L)\": {\n            \"key\": \"IT_NOT\",\n            \"label\": \"¬\",\n        }\n        \"A(IT_OGRV)\": {\n            \"key\": \"IT_AT\",\n            \"label\": \"@\",\n        }\n        \"A(IT_AGRV)\": {\n            \"key\": \"IT_HASH\",\n            \"label\": \"#\",\n        }\n        \"A(IT_UGRV)\": {\n            \"key\": \"IT_PILC\",\n            \"label\": \"¶\",\n        }\n        \"A(IT_LABK)\": {\n            \"key\": \"IT_LTEQ\",\n            \"label\": \"≤\",\n        }\n        \"A(IT_Z)\": {\n            \"key\": \"IT_NARS\",\n            \"label\": \"∑\",\n        }\n        \"A(IT_X)\": {\n            \"key\": \"IT_DAGG\",\n            \"label\": \"†\",\n        }\n        \"A(IT_C)\": {\n            \"key\": \"IT_COPY\",\n            \"label\": \"©\",\n        }\n        \"A(IT_V)\": {\n            \"key\": \"IT_SQRT\",\n            \"label\": \"√\",\n        }\n        \"A(IT_B)\": {\n            \"key\": \"IT_INTG\",\n            \"label\": \"∫\",\n        }\n        \"A(IT_N)\": {\n            \"key\": \"IT_STIL\",\n            \"label\": \"˜ (dead)\",\n        }\n        \"A(IT_M)\": {\n            \"key\": \"IT_MICR\",\n            \"label\": \"µ\",\n        }\n        \"A(IT_COMM)\": {\n            \"key\": \"IT_ELLP\",\n            \"label\": \"…\",\n        }\n        \"A(IT_DOT)\": {\n            \"key\": \"IT_BULT\",\n            \"label\": \"•\",\n        }\n        \"A(IT_MINS)\": {\n            \"key\": \"IT_NDSH\",\n            \"label\": \"–\",\n        }\n/* Shift+Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ı │ » │ ” │ ’ │ ¢ │ ‰ │ › │ ⁄ │  │   │ ≈ │ ¿ │ ± │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ ‚ │ À │ È │ Ì │ Ò │   │ Ù │   │   │ ∏ │ { │ } │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │   │ ¯ │ ˘ │ ˙ │ ˚ │ ¸ │ ˝ │ ˛ │ ˇ │ Ç │   │ ◊ │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≥ │   │ ‡ │ Á │ É │ Í │ Ó │ Ú │   │ · │ — │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(A(IT_BSLS))\": {\n            \"key\": \"IT_DLSI\",\n            \"label\": \"ı\",\n        }\n        \"S(A(IT_1))\": {\n            \"key\": \"IT_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(A(IT_2))\": {\n            \"key\": \"IT_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(A(IT_3))\": {\n            \"key\": \"IT_RSQU\",\n            \"label\": \"’\",\n        }\n        \"S(A(IT_4))\": {\n            \"key\": \"IT_CENT\",\n            \"label\": \"¢\",\n        }\n        \"S(A(IT_5))\": {\n            \"key\": \"IT_PERM\",\n            \"label\": \"‰\",\n        }\n        \"S(A(IT_6))\": {\n            \"key\": \"IT_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"S(A(IT_7))\": {\n            \"key\": \"IT_FRSL\",\n            \"label\": \"⁄\",\n        }\n        \"S(A(IT_8))\": {\n            \"key\": \"IT_APPL\",\n            \"label\": \" (Apple logo)\",\n        }\n        \"S(A(IT_0))\": {\n            \"key\": \"IT_AEQL\",\n            \"label\": \"≈\",\n        }\n        \"S(A(IT_QUOT))\": {\n            \"key\": \"IT_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"S(A(IT_IGRV))\": {\n            \"key\": \"IT_PLMN\",\n            \"label\": \"±\",\n        }\n        \"S(A(IT_Q))\": {\n            \"key\": \"IT_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"S(A(IT_W))\": {\n            \"key\": \"IT_CAGR\",\n            \"label\": \"À\",\n        }\n        \"S(A(IT_E))\": {\n            \"key\": \"IT_CEGR\",\n            \"label\": \"È\",\n        }\n        \"S(A(IT_R))\": {\n            \"key\": \"IT_CIGR\",\n            \"label\": \"Ì\",\n        }\n        \"S(A(IT_T))\": {\n            \"key\": \"IT_COGR\",\n            \"label\": \"Ò\",\n        }\n        \"S(A(IT_U))\": {\n            \"key\": \"IT_CUGR\",\n            \"label\": \"Ù\",\n        }\n        \"S(A(IT_P))\": {\n            \"key\": \"IT_NARP\",\n            \"label\": \"∏\",\n        }\n        \"S(A(IT_EGRV))\": {\n            \"key\": \"IT_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(A(IT_PLUS))\": {\n            \"key\": \"IT_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(A(IT_S))\": {\n            \"key\": \"IT_MACR\",\n            \"label\": \"¯\",\n        }\n        \"S(A(IT_D))\": {\n            \"key\": \"IT_BREV\",\n            \"label\": \"˘\",\n        }\n        \"S(A(IT_F))\": {\n            \"key\": \"IT_DOTA\",\n            \"label\": \"˙\",\n        }\n        \"S(A(IT_G))\": {\n            \"key\": \"IT_RNGA\",\n            \"label\": \"˚\",\n        }\n        \"S(A(IT_H))\": {\n            \"key\": \"IT_CEDL\",\n            \"label\": \"¸\",\n        }\n        \"S(A(IT_J))\": {\n            \"key\": \"IT_DACU\",\n            \"label\": \"˝\",\n        }\n        \"S(A(IT_K))\": {\n            \"key\": \"IT_OGON\",\n            \"label\": \"˛\",\n        }\n        \"S(A(IT_L))\": {\n            \"key\": \"IT_CARN\",\n            \"label\": \"ˇ\",\n        }\n        \"S(A(IT_OGRV))\": {\n            \"key\": \"IT_CCCE\",\n            \"label\": \"Ç\",\n        }\n        \"S(A(IT_UGRV))\": {\n            \"key\": \"IT_LOZN\",\n            \"label\": \"◊\",\n        }\n        \"S(A(IT_LABK))\": {\n            \"key\": \"IT_GTEQ\",\n            \"label\": \"≥\",\n        }\n        \"S(A(IT_X))\": {\n            \"key\": \"IT_DDAG\",\n            \"label\": \"‡\",\n        }\n        \"S(A(IT_C))\": {\n            \"key\": \"IT_CAAC\",\n            \"label\": \"Á\",\n        }\n        \"S(A(IT_V))\": {\n            \"key\": \"IT_CEAC\",\n            \"label\": \"É\",\n        }\n        \"S(A(IT_B))\": {\n            \"key\": \"IT_CIAC\",\n            \"label\": \"Í\",\n        }\n        \"S(A(IT_N))\": {\n            \"key\": \"IT_COAC\",\n            \"label\": \"Ó\",\n        }\n        \"S(A(IT_M))\": {\n            \"key\": \"IT_CUAC\",\n            \"label\": \"Ú\",\n        }\n        \"S(A(IT_DOT))\": {\n            \"key\": \"IT_MDDT\",\n            \"label\": \"·\",\n        }\n        \"S(A(IT_MINS))\": {\n            \"key\": \"IT_MDSH\",\n            \"label\": \"—\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_japanese_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n * │Z↔H│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ ^ │ ¥ │   │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ @ │ [ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │ Eisū │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ : │ ] │    │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ \\ │      │\n * ├─────┬──┴┬──┴──┬┴───┴┬──┴───┴──┬┴───┴┬──┴┬──┴┬──┴┬──┴┬─────┤\n * │     │   │     │Muhen│         │ Hen │K↔H│   │   │   │     │\n * └─────┴───┴─────┴─────┴─────────┴─────┴───┴───┴───┴───┴─────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"JP_ZKHK\",\n            \"label\": \"Zenkaku ↔ Hankaku ↔ Kanji (半角 ↔ 全角 ↔ 漢字)\",\n        }\n        \"KC_1\": {\n            \"key\": \"JP_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"JP_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"JP_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"JP_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"JP_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"JP_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"JP_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"JP_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"JP_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"JP_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"JP_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"JP_CIRC\",\n            \"label\": \"^\",\n        }\n        \"KC_INT3\": {\n            \"key\": \"JP_YEN\",\n            \"label\": \"¥\",\n        }\n        \"KC_Q\": {\n            \"key\": \"JP_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"JP_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"JP_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"JP_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"JP_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"JP_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"JP_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"JP_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"JP_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"JP_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"JP_AT\",\n            \"label\": \"@\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"JP_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_CAPS\": {\n            \"key\": \"JP_EISU\",\n            \"label\": \"Eisū (英数)\",\n        }\n        \"KC_A\": {\n            \"key\": \"JP_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"JP_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"JP_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"JP_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"JP_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"JP_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"JP_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"JP_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"JP_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"JP_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"JP_COLN\",\n            \"label\": \":\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"JP_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_Z\": {\n            \"key\": \"JP_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"JP_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"JP_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"JP_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"JP_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"JP_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"JP_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"JP_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"JP_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"JP_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_INT1\": {\n            \"key\": \"JP_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_INT5\": {\n            \"key\": \"JP_MHEN\",\n            \"label\": \"Muhenkan (無変換)\",\n        }\n        \"KC_INT4\": {\n            \"key\": \"JP_HENK\",\n            \"label\": \"Henkan (変換)\",\n        }\n        \"KC_INT2\": {\n            \"key\": \"JP_KANA\",\n            \"label\": \"Katakana ↔ Hiragana ↔ Rōmaji (カタカナ ↔ ひらがな ↔ ローマ字)\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n * │   │ ! │ \" │ # │ $ │ % │ & │ ' │ ( │ ) │   │ = │ ~ │ | │   │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n * │     │   │   │   │   │   │   │   │   │   │   │ ` │ { │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │ Caps │   │   │   │   │   │   │   │   │   │ + │ * │ } │    │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │ _ │      │\n * ├─────┬──┴┬──┴──┬┴───┴┬──┴───┴──┬┴───┴┬──┴┬──┴┬──┴┬──┴┬─────┤\n * │     │   │     │     │         │     │   │   │   │   │     │\n * └─────┴───┴─────┴─────┴─────────┴─────┴───┴───┴───┴───┴─────┘\n */\n        \"S(JP_1)\": {\n            \"key\": \"JP_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(JP_2)\": {\n            \"key\": \"JP_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(JP_3)\": {\n            \"key\": \"JP_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(JP_4)\": {\n            \"key\": \"JP_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(JP_5)\": {\n            \"key\": \"JP_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(JP_6)\": {\n            \"key\": \"JP_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(JP_7)\": {\n            \"key\": \"JP_QUOT\",\n            \"label\": \"'\",\n        }\n        \"S(JP_8)\": {\n            \"key\": \"JP_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(JP_9)\": {\n            \"key\": \"JP_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(JP_MINS)\": {\n            \"key\": \"JP_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(JP_CIRC)\": {\n            \"key\": \"JP_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(JP_YEN)\": {\n            \"key\": \"JP_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(JP_AT)\": {\n            \"key\": \"JP_GRV\",\n            \"label\": \"`\",\n        }\n        \"S(JP_LBRC)\": {\n            \"key\": \"JP_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(JP_EISU)\": {\n            \"key\": \"JP_CAPS\",\n            \"label\": \"Caps Lock\",\n        }\n        \"S(JP_SCLN)\": {\n            \"key\": \"JP_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(JP_COLN)\": {\n            \"key\": \"JP_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(JP_RBRC)\": {\n            \"key\": \"JP_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(JP_COMM)\": {\n            \"key\": \"JP_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(JP_DOT)\": {\n            \"key\": \"JP_RABK\",\n            \"label\": \">\",\n        }\n        \"S(JP_SLSH)\": {\n            \"key\": \"JP_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(JP_BSLS)\": {\n            \"key\": \"JP_UNDS\",\n            \"label\": \"_\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_korean_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  ₩  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │\n * ├─────┬──┴┬──┴──┬┴──┬┴───┴───┴───┴──┬┴──┬┴───┴┬──┴┬───┬─────┤\n * │     │   │     │Hnj│               │H↔Y│     │   │   │     │\n * └─────┴───┴─────┴───┴───────────────┴───┴─────┴───┴───┴─────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"KR_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"KR_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"KR_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"KR_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"KR_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"KR_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"KR_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"KR_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"KR_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"KR_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"KR_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"KR_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"KR_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"KR_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"KR_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"KR_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"KR_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"KR_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"KR_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"KR_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"KR_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"KR_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"KR_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"KR_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"KR_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"KR_WON\",\n            \"label\": \"₩\",\n        }\n        \"KC_A\": {\n            \"key\": \"KR_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"KR_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"KR_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"KR_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"KR_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"KR_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"KR_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"KR_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"KR_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"KR_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"KR_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_Z\": {\n            \"key\": \"KR_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"KR_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"KR_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"KR_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"KR_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"KR_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"KR_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"KR_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"KR_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"KR_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_LNG2\": {\n            \"key\": \"KR_HANJ\",\n            \"label\": \"Hanja (한자)\",\n        }\n        \"KC_LNG1\": {\n            \"key\": \"KR_HAEN\",\n            \"label\": \"Han ↔ Yeong (한 ↔ 영)\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │ : │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├─────┬──┴┬──┴──┬┴──┬┴───┴───┴───┴──┬┴──┬┴───┴┬──┴┬───┬─────┤\n * │     │   │     │   │               │   │     │   │   │     │\n * └─────┴───┴─────┴───┴───────────────┴───┴─────┴───┴───┴─────┘\n */\n        \"S(KR_GRV)\": {\n            \"key\": \"KR_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(KR_1)\": {\n            \"key\": \"KR_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(KR_2)\": {\n            \"key\": \"KR_AT\",\n            \"label\": \"@\",\n        }\n        \"S(KR_3)\": {\n            \"key\": \"KR_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(KR_4)\": {\n            \"key\": \"KR_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(KR_5)\": {\n            \"key\": \"KR_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(KR_6)\": {\n            \"key\": \"KR_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(KR_7)\": {\n            \"key\": \"KR_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(KR_8)\": {\n            \"key\": \"KR_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(KR_9)\": {\n            \"key\": \"KR_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(KR_0)\": {\n            \"key\": \"KR_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(KR_MINS)\": {\n            \"key\": \"KR_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(KR_EQL)\": {\n            \"key\": \"KR_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(KR_LBRC)\": {\n            \"key\": \"KR_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(KR_RBRC)\": {\n            \"key\": \"KR_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(KR_WON)\": {\n            \"key\": \"KR_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(KR_SCLN)\": {\n            \"key\": \"KR_COLN\",\n            \"label\": \":\",\n        }\n        \"S(KR_QUOT)\": {\n            \"key\": \"KR_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(KR_COMM)\": {\n            \"key\": \"KR_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(KR_DOT)\": {\n            \"key\": \"KR_RABK\",\n            \"label\": \">\",\n        }\n        \"S(KR_SLSH)\": {\n            \"key\": \"KR_QUES\",\n            \"label\": \"?\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_latvian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ \\ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"LV_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"LV_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"LV_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"LV_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"LV_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"LV_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"LV_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"LV_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"LV_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"LV_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"LV_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"LV_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"LV_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"LV_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"LV_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"LV_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"LV_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"LV_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"LV_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"LV_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"LV_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"LV_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"LV_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"LV_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"LV_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_A\": {\n            \"key\": \"LV_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"LV_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"LV_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"LV_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"LV_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"LV_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"LV_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"LV_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"LV_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"LV_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"LV_QUOT\",\n            \"label\": \"' (dead)\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"LV_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"LV_NUBS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_Z\": {\n            \"key\": \"LV_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"LV_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"LV_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"LV_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"LV_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"LV_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"LV_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"LV_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"LV_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"LV_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ : │ \" │ | │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(LV_GRV)\": {\n            \"key\": \"LV_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(LV_1)\": {\n            \"key\": \"LV_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(LV_2)\": {\n            \"key\": \"LV_AT\",\n            \"label\": \"@\",\n        }\n        \"S(LV_3)\": {\n            \"key\": \"LV_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(LV_4)\": {\n            \"key\": \"LV_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(LV_5)\": {\n            \"key\": \"LV_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(LV_6)\": {\n            \"key\": \"LV_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(LV_7)\": {\n            \"key\": \"LV_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(LV_8)\": {\n            \"key\": \"LV_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(LV_9)\": {\n            \"key\": \"LV_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(LV_0)\": {\n            \"key\": \"LV_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(LV_MINS)\": {\n            \"key\": \"LV_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(LV_EQL)\": {\n            \"key\": \"LV_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(LV_LBRC)\": {\n            \"key\": \"LV_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(LV_RBRC)\": {\n            \"key\": \"LV_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(LV_SCLN)\": {\n            \"key\": \"LV_COLN\",\n            \"label\": \":\",\n        }\n        \"S(LV_QUOT)\": {\n            \"key\": \"LV_DQUO\",\n            \"label\": \"\\\" (dead)\",\n        }\n        \"S(LV_BSLS)\": {\n            \"key\": \"LV_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(LV_COMM)\": {\n            \"key\": \"LV_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(LV_DOT)\": {\n            \"key\": \"LV_RABK\",\n            \"label\": \">\",\n        }\n        \"S(LV_SLSH)\": {\n            \"key\": \"LV_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ - │   │ « │ » │ € │   │ ’ │   │   │   │   │ – │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ Ē │ Ŗ │   │   │ Ū │ Ī │ Ō │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ Ā │ Š │   │   │ Ģ │   │   │ Ķ │ Ļ │   │ ´ │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │ Ž │   │ Č │   │   │ Ņ │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(LV_GRV)\": {\n            \"key\": \"LV_SHYP\",\n            \"label\": \"­ (soft hyphen)\",\n        }\n        \"ALGR(LV_1)\": {\n            \"key\": \"LV_NBSP\",\n            \"label\": \"(non-breaking space)\",\n        }\n        \"ALGR(LV_2)\": {\n            \"key\": \"LV_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"ALGR(LV_3)\": {\n            \"key\": \"LV_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"ALGR(LV_4)\": {\n            \"key\": \"LV_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(LV_6)\": {\n            \"key\": \"LV_RSQU\",\n            \"label\": \"’\",\n        }\n        \"ALGR(LV_MINS)\": {\n            \"key\": \"LV_NDSH\",\n            \"label\": \"–\",\n        }\n        \"ALGR(LV_E)\": {\n            \"key\": \"LV_EMAC\",\n            \"label\": \"Ē\",\n        }\n        \"ALGR(LV_R)\": {\n            \"key\": \"LV_RCED\",\n            \"label\": \"Ŗ\",\n        }\n        \"ALGR(LV_U)\": {\n            \"key\": \"LV_UMAC\",\n            \"label\": \"Ū\",\n        }\n        \"ALGR(LV_I)\": {\n            \"key\": \"LV_IMAC\",\n            \"label\": \"Ī\",\n        }\n        \"ALGR(LV_O)\": {\n            \"key\": \"LV_OMAC\",\n            \"label\": \"Ō\",\n        }\n        \"ALGR(LV_A)\": {\n            \"key\": \"LV_AMAC\",\n            \"label\": \"Ā\",\n        }\n        \"ALGR(LV_S)\": {\n            \"key\": \"LV_SCAR\",\n            \"label\": \"Š\",\n        }\n        \"ALGR(LV_G)\": {\n            \"key\": \"LV_GCED\",\n            \"label\": \"Ģ\",\n        }\n        \"ALGR(LV_K)\": {\n            \"key\": \"LV_KCED\",\n            \"label\": \"Ķ\",\n        }\n        \"ALGR(LV_L)\": {\n            \"key\": \"LV_LCED\",\n            \"label\": \"Ļ\",\n        }\n        \"ALGR(LV_QUOT)\": {\n            \"key\": \"LV_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(LV_Z)\": {\n            \"key\": \"LV_ZCAR\",\n            \"label\": \"Ž\",\n        }\n        \"ALGR(LV_C)\": {\n            \"key\": \"LV_CCAR\",\n            \"label\": \"Č\",\n        }\n        \"ALGR(LV_N)\": {\n            \"key\": \"LV_NCED\",\n            \"label\": \"Ņ\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │   │   │ § │ ° │   │ ± │ × │   │   │ — │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ ¨ │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ALGR(LV_4))\": {\n            \"key\": \"LV_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(ALGR(LV_5))\": {\n            \"key\": \"LV_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(ALGR(LV_7))\": {\n            \"key\": \"LV_PLMN\",\n            \"label\": \"±\",\n        }\n        \"S(ALGR(LV_8))\": {\n            \"key\": \"LV_MUL\",\n            \"label\": \"×\",\n        }\n        \"S(ALGR(LV_MINS))\": {\n            \"key\": \"LV_MDSH\",\n            \"label\": \"—\",\n        }\n        \"S(ALGR(LV_QUOT))\": {\n            \"key\": \"LV_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_lithuanian_azerty_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ ! │ - │ / │ ; │ : │ , │ . │ = │ ( │ ) │ ? │ X │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Ą │ Ž │ E │ R │ T │ Y │ U │ I │ O │ P │ Į │ W │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ Š │ G │ H │ J │ K │ L │ Ų │ Ė │ Q │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ Ū │ C │ V │ B │ N │ M │ Č │ F │ Ę │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"LT_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"LT_EXLM\",\n            \"label\": \"!\",\n        }\n        \"KC_2\": {\n            \"key\": \"LT_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_3\": {\n            \"key\": \"LT_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_4\": {\n            \"key\": \"LT_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_5\": {\n            \"key\": \"LT_COLN\",\n            \"label\": \":\",\n        }\n        \"KC_6\": {\n            \"key\": \"LT_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_7\": {\n            \"key\": \"LT_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_8\": {\n            \"key\": \"LT_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_9\": {\n            \"key\": \"LT_LPRN\",\n            \"label\": \"(\",\n        }\n        \"KC_0\": {\n            \"key\": \"LT_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"LT_QUES\",\n            \"label\": \"?\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"LT_X\",\n            \"label\": \"X\",\n        }\n        \"KC_Q\": {\n            \"key\": \"LT_AOGO\",\n            \"label\": \"Ą\",\n        }\n        \"KC_W\": {\n            \"key\": \"LT_ZCAR\",\n            \"label\": \"Ž\",\n        }\n        \"KC_E\": {\n            \"key\": \"LT_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"LT_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"LT_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"LT_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"LT_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"LT_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"LT_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"LT_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"LT_IOGO\",\n            \"label\": \"Į\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"LT_W\",\n            \"label\": \"W\",\n        }\n        \"KC_A\": {\n            \"key\": \"LT_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"LT_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"LT_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"LT_SCAR\",\n            \"label\": \"Š\",\n        }\n        \"KC_G\": {\n            \"key\": \"LT_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"LT_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"LT_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"LT_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"LT_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"LT_UOGO\",\n            \"label\": \"Ų\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"LT_EDOT\",\n            \"label\": \"Ė\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"LT_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"LT_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"LT_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"LT_UMAC\",\n            \"label\": \"Ū\",\n        }\n        \"KC_C\": {\n            \"key\": \"LT_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"LT_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"LT_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"LT_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"LT_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"LT_CCAR\",\n            \"label\": \"Č\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"LT_F\",\n            \"label\": \"F\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"LT_EOGO\",\n            \"label\": \"Ę\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ + │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(LT_GRV)\": {\n            \"key\": \"LT_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(LT_EXLM)\": {\n            \"key\": \"LT_1\",\n            \"label\": \"1\",\n        }\n        \"S(LT_MINS)\": {\n            \"key\": \"LT_2\",\n            \"label\": \"2\",\n        }\n        \"S(LT_SLSH)\": {\n            \"key\": \"LT_3\",\n            \"label\": \"3\",\n        }\n        \"S(LT_SCLN)\": {\n            \"key\": \"LT_4\",\n            \"label\": \"4\",\n        }\n        \"S(LT_COLN)\": {\n            \"key\": \"LT_5\",\n            \"label\": \"5\",\n        }\n        \"S(LT_COMM)\": {\n            \"key\": \"LT_6\",\n            \"label\": \"6\",\n        }\n        \"S(LT_DOT)\": {\n            \"key\": \"LT_7\",\n            \"label\": \"7\",\n        }\n        \"S(LT_EQL)\": {\n            \"key\": \"LT_8\",\n            \"label\": \"8\",\n        }\n        \"S(LT_LPRN)\": {\n            \"key\": \"LT_9\",\n            \"label\": \"9\",\n        }\n        \"S(LT_RPRN)\": {\n            \"key\": \"LT_0\",\n            \"label\": \"0\",\n        }\n        \"S(LT_QUES)\": {\n            \"key\": \"LT_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(LT_LABK)\": {\n            \"key\": \"LT_RABK\",\n            \"label\": \">\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ´ │ @ │ _ │ # │ $ │ § │ ^ │ & │ * │ [ │ ] │ ' │ % │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │   │ { │ } │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ \" │ | │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ – │   │   │   │   │   │   │   │ „ │ “ │ \\ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(LT_GRV)\": {\n            \"key\": \"LT_ACUT\",\n            \"label\": \"´\",\n        }\n        \"ALGR(LT_EXLM)\": {\n            \"key\": \"LT_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(LT_MINS)\": {\n            \"key\": \"LT_UNDS\",\n            \"label\": \"_\",\n        }\n        \"ALGR(LT_SLSH)\": {\n            \"key\": \"LT_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(LT_SCLN)\": {\n            \"key\": \"LT_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(LT_COLN)\": {\n            \"key\": \"LT_SECT\",\n            \"label\": \"§\",\n        }\n        \"ALGR(LT_COMM)\": {\n            \"key\": \"LT_CIRC\",\n            \"label\": \"^\",\n        }\n        \"ALGR(LT_DOT)\": {\n            \"key\": \"LT_AMPR\",\n            \"label\": \"&\",\n        }\n        \"ALGR(LT_EQL)\": {\n            \"key\": \"LT_ASTR\",\n            \"label\": \"*\",\n        }\n        \"ALGR(LT_LPRN)\": {\n            \"key\": \"LT_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(LT_RPRN)\": {\n            \"key\": \"LT_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(LT_QUES)\": {\n            \"key\": \"LT_QUOT\",\n            \"label\": \"'\",\n        }\n        \"ALGR(LT_X)\": {\n            \"key\": \"LT_PERC\",\n            \"label\": \"%\",\n        }\n        \"ALGR(LT_E)\": {\n            \"key\": \"LT_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(LT_IOGO)\": {\n            \"key\": \"LT_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(LT_W)\": {\n            \"key\": \"LT_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(LT_EDOT)\": {\n            \"key\": \"LT_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"ALGR(LT_Q)\": {\n            \"key\": \"LT_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(LT_LABK)\": {\n            \"key\": \"LT_NDSH\",\n            \"label\": \"–\",\n        }\n        \"ALGR(LT_CCAR)\": {\n            \"key\": \"LT_DLQU\",\n            \"label\": \"„\",\n        }\n        \"ALGR(LT_F)\": {\n            \"key\": \"LT_LDQU\",\n            \"label\": \"“\",\n        }\n        \"ALGR(LT_EOGO)\": {\n            \"key\": \"LT_BSLS\",\n            \"label\": \"\\\\\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_lithuanian_qwerty_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ Ą │ Č │ Ę │ Ė │ Į │ Š │ Ų │ Ū │ 9 │ 0 │ - │ Ž │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"LT_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"LT_AOGO\",\n            \"label\": \"Ą\",\n        }\n        \"KC_2\": {\n            \"key\": \"LT_CCAR\",\n            \"label\": \"Č\",\n        }\n        \"KC_3\": {\n            \"key\": \"LT_EOGO\",\n            \"label\": \"Ę\",\n        }\n        \"KC_4\": {\n            \"key\": \"LT_EDOT\",\n            \"label\": \"Ė\",\n        }\n        \"KC_5\": {\n            \"key\": \"LT_IOGO\",\n            \"label\": \"Į\",\n        }\n        \"KC_6\": {\n            \"key\": \"LT_SCAR\",\n            \"label\": \"Š\",\n        }\n        \"KC_7\": {\n            \"key\": \"LT_UOGO\",\n            \"label\": \"Ų\",\n        }\n        \"KC_8\": {\n            \"key\": \"LT_UMAC\",\n            \"label\": \"Ū\",\n        }\n        \"KC_9\": {\n            \"key\": \"LT_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"LT_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"LT_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"LT_ZCAR\",\n            \"label\": \"Ž\",\n        }\n        \"KC_Q\": {\n            \"key\": \"LT_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"LT_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"LT_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"LT_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"LT_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"LT_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"LT_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"LT_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"LT_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"LT_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"LT_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"LT_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_A\": {\n            \"key\": \"LT_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"LT_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"LT_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"LT_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"LT_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"LT_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"LT_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"LT_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"LT_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"LT_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"LT_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"LT_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_Z\": {\n            \"key\": \"LT_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"LT_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"LT_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"LT_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"LT_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"LT_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"LT_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"LT_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"LT_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"LT_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │   │   │   │   │   │   │   │   │ ( │ ) │ _ │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │ : │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(LT_GRV)\": {\n            \"key\": \"LT_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(LT_9)\": {\n            \"key\": \"LT_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(LT_0)\": {\n            \"key\": \"LT_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(LT_MINS)\": {\n            \"key\": \"LT_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(LT_LBRC)\": {\n            \"key\": \"LT_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(LT_RBRC)\": {\n            \"key\": \"LT_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(LT_SCLN)\": {\n            \"key\": \"LT_COLN\",\n            \"label\": \":\",\n        }\n        \"S(LT_QUOT)\": {\n            \"key\": \"LT_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(LT_BSLS)\": {\n            \"key\": \"LT_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(LT_COMM)\": {\n            \"key\": \"LT_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(LT_DOT)\": {\n            \"key\": \"LT_RABK\",\n            \"label\": \">\",\n        }\n        \"S(LT_SLSH)\": {\n            \"key\": \"LT_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │   │   │   │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │   │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(LT_AOGO)\": {\n            \"key\": \"LT_1\",\n            \"label\": \"1\",\n        }\n        \"ALGR(LT_CCAR)\": {\n            \"key\": \"LT_2\",\n            \"label\": \"2\",\n        }\n        \"ALGR(LT_EOGO)\": {\n            \"key\": \"LT_3\",\n            \"label\": \"3\",\n        }\n        \"ALGR(LT_EDOT)\": {\n            \"key\": \"LT_4\",\n            \"label\": \"4\",\n        }\n        \"ALGR(LT_IOGO)\": {\n            \"key\": \"LT_5\",\n            \"label\": \"5\",\n        }\n        \"ALGR(LT_SCAR)\": {\n            \"key\": \"LT_6\",\n            \"label\": \"6\",\n        }\n        \"ALGR(LT_UOGO)\": {\n            \"key\": \"LT_7\",\n            \"label\": \"7\",\n        }\n        \"ALGR(LT_UMAC)\": {\n            \"key\": \"LT_8\",\n            \"label\": \"8\",\n        }\n        \"ALGR(LT_ZCAR)\": {\n            \"key\": \"LT_EQL\",\n            \"label\": \"=\",\n        }\n        \"ALGR(LT_E)\": {\n            \"key\": \"LT_EURO\",\n            \"label\": \"€\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │   │   │   │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │   │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ALGR(LT_AOGO))\": {\n            \"key\": \"LT_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(ALGR(LT_CCAR))\": {\n            \"key\": \"LT_AT\",\n            \"label\": \"@\",\n        }\n        \"S(ALGR(LT_EOGO))\": {\n            \"key\": \"LT_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(ALGR(LT_EDOT))\": {\n            \"key\": \"LT_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(ALGR(LT_IOGO))\": {\n            \"key\": \"LT_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(ALGR(LT_SCAR))\": {\n            \"key\": \"LT_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(ALGR(LT_UOGO))\": {\n            \"key\": \"LT_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(ALGR(LT_UMAC))\": {\n            \"key\": \"LT_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(ALGR(LT_ZCAR))\": {\n            \"key\": \"LT_PLUS\",\n            \"label\": \"+\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_neo2_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ^ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ ` │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ X │ V │ L │ C │ W │ K │ H │ G │ F │ Q │ ß │ ´ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │  L3  │ U │ I │ A │ E │ O │ S │ N │ R │ T │ D │ Y │ L3│    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │L4 │ Ü │ Ö │ Ä │ P │ Z │ B │ M │ , │ . │ J │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │ L4 │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"NE_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_1\": {\n            \"key\": \"NE_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"NE_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"NE_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"NE_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"NE_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"NE_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"NE_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"NE_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"NE_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"NE_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"NE_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"NE_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"NE_X\",\n            \"label\": \"X\",\n        }\n        \"KC_W\": {\n            \"key\": \"NE_V\",\n            \"label\": \"V\",\n        }\n        \"KC_E\": {\n            \"key\": \"NE_L\",\n            \"label\": \"L\",\n        }\n        \"KC_R\": {\n            \"key\": \"NE_C\",\n            \"label\": \"C\",\n        }\n        \"KC_T\": {\n            \"key\": \"NE_W\",\n            \"label\": \"W\",\n        }\n        \"KC_Y\": {\n            \"key\": \"NE_K\",\n            \"label\": \"K\",\n        }\n        \"KC_U\": {\n            \"key\": \"NE_H\",\n            \"label\": \"H\",\n        }\n        \"KC_I\": {\n            \"key\": \"NE_G\",\n            \"label\": \"G\",\n        }\n        \"KC_O\": {\n            \"key\": \"NE_F\",\n            \"label\": \"F\",\n        }\n        \"KC_P\": {\n            \"key\": \"NE_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"NE_SS\",\n            \"label\": \"ß\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"NE_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_CAPS\": {\n            \"key\": \"NE_L3L\",\n            \"label\": \"(layer 3)\",\n        }\n        \"KC_A\": {\n            \"key\": \"NE_U\",\n            \"label\": \"U\",\n        }\n        \"KC_S\": {\n            \"key\": \"NE_I\",\n            \"label\": \"I\",\n        }\n        \"KC_D\": {\n            \"key\": \"NE_A\",\n            \"label\": \"A\",\n        }\n        \"KC_F\": {\n            \"key\": \"NE_E\",\n            \"label\": \"E\",\n        }\n        \"KC_G\": {\n            \"key\": \"NE_O\",\n            \"label\": \"O\",\n        }\n        \"KC_H\": {\n            \"key\": \"NE_S\",\n            \"label\": \"S\",\n        }\n        \"KC_J\": {\n            \"key\": \"NE_N\",\n            \"label\": \"N\",\n        }\n        \"KC_K\": {\n            \"key\": \"NE_R\",\n            \"label\": \"R\",\n        }\n        \"KC_L\": {\n            \"key\": \"NE_T\",\n            \"label\": \"T\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"NE_D\",\n            \"label\": \"D\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"NE_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"NE_L3R\",\n            \"label\": \"(layer 3)\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"NE_L4L\",\n            \"label\": \"(layer 4)\",\n        }\n        \"KC_Z\": {\n            \"key\": \"NE_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"KC_X\": {\n            \"key\": \"NE_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_C\": {\n            \"key\": \"NE_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"KC_V\": {\n            \"key\": \"NE_P\",\n            \"label\": \"P\",\n        }\n        \"KC_B\": {\n            \"key\": \"NE_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_N\": {\n            \"key\": \"NE_B\",\n            \"label\": \"B\",\n        }\n        \"KC_M\": {\n            \"key\": \"NE_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"NE_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"NE_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"NE_J\",\n            \"label\": \"J\",\n        }\n        \"KC_ALGR\": {\n            \"key\": \"NE_L4R\",\n            \"label\": \"(layer 4)\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_nordic_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n        \"KC_GRV\": {\n            \"key\": \"NO_HALF\"\n        }\n        \"KC_MINS\": {\n            \"key\": \"NO_PLUS\"\n        }\n        \"KC_EQL\": {\n            \"key\": \"NO_ACUT\"\n        }\n        \"KC_LBRC\": {\n            \"key\": \"NO_AM\"\n        }\n        \"KC_RBRC\": {\n            \"key\": \"NO_QUOT\",\n            \"label\": \"this is the \\\"umlaut\\\" char on Nordic keyboards, Apple layout\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"NO_AE\"\n        }\n        \"KC_QUOT\": {\n            \"key\": \"NO_OSLH\"\n        }\n        \"KC_NUHS\": {\n            \"key\": \"NO_APOS\"\n        }\n        \"KC_NUBS\": {\n            \"key\": \"NO_LESS\"\n        }\n        \"KC_SLSH\": {\n            \"key\": \"NO_MINS\"\n        }\n        \"LSFT(NO_HALF)\": {\n            \"key\": \"NO_SECT\"\n        }\n        \"LSFT(KC_2)\": {\n            \"key\": \"NO_QUO2\"\n        }\n        \"LSFT(KC_4)\": {\n            \"key\": \"NO_BULT\"\n        }\n        \"LSFT(KC_6)\": {\n            \"key\": \"NO_AMPR\"\n        }\n        \"LSFT(KC_7)\": {\n            \"key\": \"NO_SLSH\"\n        }\n        \"LSFT(KC_8)\": {\n            \"key\": \"NO_LPRN\"\n        }\n        \"LSFT(KC_9)\": {\n            \"key\": \"NO_RPRN\"\n        }\n        \"LSFT(KC_0)\": {\n            \"key\": \"NO_EQL\"\n        }\n        \"LSFT(NO_PLUS)\": {\n            \"key\": \"NO_QUES\"\n        }\n        \"LSFT(NO_ACUT)\": {\n            \"key\": \"NO_GRV\"\n        }\n        \"LSFT(NO_QUOT)\": {\n            \"key\": \"NO_CIRC\"\n        }\n        \"LSFT(NO_LESS)\": {\n            \"key\": \"NO_GRTR\"\n        }\n        \"LSFT(KC_COMM)\": {\n            \"key\": \"NO_SCLN\"\n        }\n        \"LSFT(KC_DOT)\": {\n            \"key\": \"NO_COLN\"\n        }\n        \"LSFT(NO_MINS)\": {\n            \"key\": \"NO_UNDS\"\n        }\n        \"ALGR(KC_2)\": {\n            \"key\": \"NO_AT\"\n        }\n        \"ALGR(KC_3)\": {\n            \"key\": \"NO_PND\"\n        }\n        \"ALGR(KC_4)\": {\n            \"key\": \"NO_DLR\"\n        }\n        \"ALGR(KC_7)\": {\n            \"key\": \"NO_LCBR\"\n        }\n        \"ALGR(KC_8)\": {\n            \"key\": \"NO_LBRC\"\n        }\n        \"ALGR(KC_9)\": {\n            \"key\": \"NO_RBRC\"\n        }\n        \"ALGR(KC_0)\": {\n            \"key\": \"NO_RCBR\"\n        }\n        \"ALGR(KC_NUBS)\": {\n            \"key\": \"NO_PIPE\"\n        }\n        \"ALGR(KC_E)\": {\n            \"key\": \"NO_EURO\"\n        }\n        \"ALGR(NO_QUOT)\": {\n            \"key\": \"NO_TILD\"\n        }\n        \"ALGR(KC_MINS)\": {\n            \"key\": \"NO_BSLS\"\n        }\n        \"ALGR(KC_M)\": {\n            \"key\": \"NO_MU\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_norman_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ D │ F │ K │ J │ U │ R │ L │ ; │ [ │ ] │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ S │ E │ T │ G │ Y │ N │ I │ O │ H │ ' │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Z │ X │ C │ V │ B │ P │ M │ , │ . │ / │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"NM_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"NM_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"NM_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"NM_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"NM_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"NM_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"NM_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"NM_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"NM_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"NM_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"NM_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"NM_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"NM_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"NM_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"NM_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"NM_D\",\n            \"label\": \"D\",\n        }\n        \"KC_R\": {\n            \"key\": \"NM_F\",\n            \"label\": \"F\",\n        }\n        \"KC_T\": {\n            \"key\": \"NM_K\",\n            \"label\": \"K\",\n        }\n        \"KC_Y\": {\n            \"key\": \"NM_J\",\n            \"label\": \"J\",\n        }\n        \"KC_U\": {\n            \"key\": \"NM_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"NM_R\",\n            \"label\": \"R\",\n        }\n        \"KC_O\": {\n            \"key\": \"NM_L\",\n            \"label\": \"L\",\n        }\n        \"KC_P\": {\n            \"key\": \"NM_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"NM_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"NM_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"NM_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"NM_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"NM_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"NM_E\",\n            \"label\": \"E\",\n        }\n        \"KC_F\": {\n            \"key\": \"NM_T\",\n            \"label\": \"T\",\n        }\n        \"KC_G\": {\n            \"key\": \"NM_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"NM_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_J\": {\n            \"key\": \"NM_N\",\n            \"label\": \"N\",\n        }\n        \"KC_K\": {\n            \"key\": \"NM_I\",\n            \"label\": \"I\",\n        }\n        \"KC_L\": {\n            \"key\": \"NM_O\",\n            \"label\": \"O\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"NM_H\",\n            \"label\": \"H\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"NM_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_Z\": {\n            \"key\": \"NM_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"NM_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"NM_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"NM_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"NM_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"NM_P\",\n            \"label\": \"P\",\n        }\n        \"KC_M\": {\n            \"key\": \"NM_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"NM_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"NM_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"NM_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │ : │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(NM_GRV)\": {\n            \"key\": \"NM_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(NM_1)\": {\n            \"key\": \"NM_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(NM_2)\": {\n            \"key\": \"NM_AT\",\n            \"label\": \"@\",\n        }\n        \"S(NM_3)\": {\n            \"key\": \"NM_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(NM_4)\": {\n            \"key\": \"NM_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(NM_5)\": {\n            \"key\": \"NM_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(NM_6)\": {\n            \"key\": \"NM_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(NM_7)\": {\n            \"key\": \"NM_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(NM_8)\": {\n            \"key\": \"NM_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(NM_9)\": {\n            \"key\": \"NM_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(NM_0)\": {\n            \"key\": \"NM_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(NM_MINS)\": {\n            \"key\": \"NM_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(NM_EQL)\": {\n            \"key\": \"NM_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(NM_SCLN)\": {\n            \"key\": \"NM_COLN\",\n            \"label\": \":\",\n        }\n        \"S(NM_LBRC)\": {\n            \"key\": \"NM_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(NM_RBRC)\": {\n            \"key\": \"NM_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(NM_BSLS)\": {\n            \"key\": \"NM_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(NM_QUOT)\": {\n            \"key\": \"NM_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(NM_COMM)\": {\n            \"key\": \"NM_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(NM_DOT)\": {\n            \"key\": \"NM_RABK\",\n            \"label\": \">\",\n        }\n        \"S(NM_SLSH)\": {\n            \"key\": \"NM_QUES\",\n            \"label\": \"?\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_norwegian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ | │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ + │ \\ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Å │ ¨ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ø │ Æ │ ' │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"NO_PIPE\",\n            \"label\": \"|\",\n        }\n        \"KC_1\": {\n            \"key\": \"NO_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"NO_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"NO_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"NO_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"NO_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"NO_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"NO_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"NO_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"NO_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"NO_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"NO_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"NO_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_Q\": {\n            \"key\": \"NO_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"NO_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"NO_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"NO_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"NO_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"NO_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"NO_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"NO_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"NO_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"NO_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"NO_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"NO_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"NO_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"NO_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"NO_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"NO_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"NO_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"NO_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"NO_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"NO_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"NO_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"NO_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"NO_AE\",\n            \"label\": \"Æ\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"NO_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"NO_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"NO_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"NO_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"NO_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"NO_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"NO_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"NO_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"NO_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"NO_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"NO_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"NO_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ § │ ! │ \" │ # │ ¤ │ % │ & │ / │ ( │ ) │ = │ ? │ ` │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ^ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ * │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(NO_PIPE)\": {\n            \"key\": \"NO_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(NO_1)\": {\n            \"key\": \"NO_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(NO_2)\": {\n            \"key\": \"NO_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(NO_3)\": {\n            \"key\": \"NO_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(NO_4)\": {\n            \"key\": \"NO_CURR\",\n            \"label\": \"¤\",\n        }\n        \"S(NO_5)\": {\n            \"key\": \"NO_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(NO_6)\": {\n            \"key\": \"NO_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(NO_7)\": {\n            \"key\": \"NO_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(NO_8)\": {\n            \"key\": \"NO_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(NO_9)\": {\n            \"key\": \"NO_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(NO_0)\": {\n            \"key\": \"NO_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(NO_PLUS)\": {\n            \"key\": \"NO_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(NO_BSLS)\": {\n            \"key\": \"NO_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(NO_DIAE)\": {\n            \"key\": \"NO_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(NO_QUOT)\": {\n            \"key\": \"NO_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(NO_LABK)\": {\n            \"key\": \"NO_RABK\",\n            \"label\": \">\",\n        }\n        \"S(NO_COMM)\": {\n            \"key\": \"NO_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(NO_DOT)\": {\n            \"key\": \"NO_COLN\",\n            \"label\": \":\",\n        }\n        \"S(NO_MINS)\": {\n            \"key\": \"NO_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │ @ │ £ │ $ │ € │   │ { │ [ │ ] │ } │   │ ´ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ~ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │ µ │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(NO_2)\": {\n            \"key\": \"NO_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(NO_3)\": {\n            \"key\": \"NO_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(NO_4)\": {\n            \"key\": \"NO_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(NO_5)\": {\n            \"key\": \"NO_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(NO_7)\": {\n            \"key\": \"NO_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(NO_8)\": {\n            \"key\": \"NO_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(NO_9)\": {\n            \"key\": \"NO_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(NO_0)\": {\n            \"key\": \"NO_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(NO_BSLS)\": {\n            \"key\": \"NO_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(NO_DIAE)\": {\n            \"key\": \"NO_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(NO_M)\": {\n            \"key\": \"NO_MICR\",\n            \"label\": \"µ\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_plover_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │Num│   │   │   │   │   │   │   │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ S │ T │ P │ H │   │ * │ F │ P │ L │ T │ D │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │ K │ W │ R │   │   │ R │ B │ G │ S │ Z │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │ A │ O │   │ E │ U │   │   │   │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_1\": {\n            \"key\": \"PV_NUM\"\n        }\n        \"KC_Q\": {\n            \"key\": \"PV_LS\"\n        }\n        \"KC_W\": {\n            \"key\": \"PV_LT\"\n        }\n        \"KC_E\": {\n            \"key\": \"PV_LP\"\n        }\n        \"KC_R\": {\n            \"key\": \"PV_LH\"\n        }\n        \"KC_Y\": {\n            \"key\": \"PV_STAR\"\n        }\n        \"KC_U\": {\n            \"key\": \"PV_RF\"\n        }\n        \"KC_I\": {\n            \"key\": \"PV_RP\"\n        }\n        \"KC_O\": {\n            \"key\": \"PV_RL\"\n        }\n        \"KC_P\": {\n            \"key\": \"PV_RT\"\n        }\n        \"KC_LBRC\": {\n            \"key\": \"PV_RD\"\n        }\n        \"KC_S\": {\n            \"key\": \"PV_LK\"\n        }\n        \"KC_D\": {\n            \"key\": \"PV_LW\"\n        }\n        \"KC_F\": {\n            \"key\": \"PV_LR\"\n        }\n        \"KC_J\": {\n            \"key\": \"PV_RR\"\n        }\n        \"KC_K\": {\n            \"key\": \"PV_RB\"\n        }\n        \"KC_L\": {\n            \"key\": \"PV_RG\"\n        }\n        \"KC_SCLN\": {\n            \"key\": \"PV_RS\"\n        }\n        \"KC_QUOT\": {\n            \"key\": \"PV_RZ\"\n        }\n        \"KC_C\": {\n            \"key\": \"PV_A\"\n        }\n        \"KC_V\": {\n            \"key\": \"PV_O\"\n        }\n        \"KC_N\": {\n            \"key\": \"PV_E\"\n        }\n        \"KC_M\": {\n            \"key\": \"PV_U\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_plover_dvorak_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n        \"DV_1\": {\n            \"key\": \"PD_NUM\"\n        }\n        \"DV_Q\": {\n            \"key\": \"PD_LS\"\n        }\n        \"DV_W\": {\n            \"key\": \"PD_LT\"\n        }\n        \"DV_E\": {\n            \"key\": \"PD_LP\"\n        }\n        \"DV_R\": {\n            \"key\": \"PD_LH\"\n        }\n        \"DV_S\": {\n            \"key\": \"PD_LK\"\n        }\n        \"DV_D\": {\n            \"key\": \"PD_LW\"\n        }\n        \"DV_F\": {\n            \"key\": \"PD_LR\"\n        }\n        \"DV_Y\": {\n            \"key\": \"PD_STAR\"\n        }\n        \"DV_U\": {\n            \"key\": \"PD_RF\"\n        }\n        \"DV_I\": {\n            \"key\": \"PD_RP\"\n        }\n        \"DV_O\": {\n            \"key\": \"PD_RL\"\n        }\n        \"DV_P\": {\n            \"key\": \"PD_RT\"\n        }\n        \"DV_LBRC\": {\n            \"key\": \"PD_RD\"\n        }\n        \"DV_J\": {\n            \"key\": \"PD_RR\"\n        }\n        \"DV_K\": {\n            \"key\": \"PD_RB\"\n        }\n        \"DV_L\": {\n            \"key\": \"PD_RG\"\n        }\n        \"DV_SCLN\": {\n            \"key\": \"PD_RS\"\n        }\n        \"DV_QUOT\": {\n            \"key\": \"PD_RZ\"\n        }\n        \"DV_C\": {\n            \"key\": \"PD_A\"\n        }\n        \"DV_V\": {\n            \"key\": \"PD_O\"\n        }\n        \"DV_N\": {\n            \"key\": \"PD_E\"\n        }\n        \"DV_M\": {\n            \"key\": \"PD_U\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_polish_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"PL_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"PL_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"PL_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"PL_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"PL_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"PL_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"PL_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"PL_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"PL_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"PL_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"PL_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"PL_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"PL_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"PL_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"PL_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"PL_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"PL_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"PL_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"PL_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"PL_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"PL_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"PL_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"PL_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"PL_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"PL_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"PL_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"PL_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"PL_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"PL_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"PL_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"PL_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"PL_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"PL_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"PL_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"PL_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"PL_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"PL_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_Z\": {\n            \"key\": \"PL_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"PL_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"PL_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"PL_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"PL_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"PL_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"PL_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"PL_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"PL_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"PL_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │ : │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(PL_GRV)\": {\n            \"key\": \"PL_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(PL_1)\": {\n            \"key\": \"PL_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(PL_2)\": {\n            \"key\": \"PL_AT\",\n            \"label\": \"@\",\n        }\n        \"S(PL_3)\": {\n            \"key\": \"PL_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(PL_4)\": {\n            \"key\": \"PL_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(PL_5)\": {\n            \"key\": \"PL_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(PL_6)\": {\n            \"key\": \"PL_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(PL_7)\": {\n            \"key\": \"PL_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(PL_8)\": {\n            \"key\": \"PL_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(PL_9)\": {\n            \"key\": \"PL_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(PL_0)\": {\n            \"key\": \"PL_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(PL_MINS)\": {\n            \"key\": \"PL_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(PL_EQL)\": {\n            \"key\": \"PL_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(PL_LBRC)\": {\n            \"key\": \"PL_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(PL_RBRC)\": {\n            \"key\": \"PL_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(PL_BSLS)\": {\n            \"key\": \"PL_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(PL_SCLN)\": {\n            \"key\": \"PL_COLN\",\n            \"label\": \":\",\n        }\n        \"S(PL_QUOT)\": {\n            \"key\": \"PL_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(PL_COMM)\": {\n            \"key\": \"PL_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(PL_DOT)\": {\n            \"key\": \"PL_RABK\",\n            \"label\": \">\",\n        }\n        \"S(PL_SLSH)\": {\n            \"key\": \"PL_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ Ę │   │   │   │ € │   │ Ó │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ Ą │ Ś │   │   │   │   │   │   │ Ł │   │   │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Ż │ Ź │ Ć │   │   │ Ń │   │   │   │   │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(PL_E)\": {\n            \"key\": \"PL_EOGO\",\n            \"label\": \"Ę\",\n        }\n        \"ALGR(PL_U)\": {\n            \"key\": \"PL_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(PL_O)\": {\n            \"key\": \"PL_OACU\",\n            \"label\": \"Ó\",\n        }\n        \"ALGR(PL_A)\": {\n            \"key\": \"PL_AOGO\",\n            \"label\": \"Ą\",\n        }\n        \"ALGR(PL_S)\": {\n            \"key\": \"PL_SACU\",\n            \"label\": \"Ś\",\n        }\n        \"ALGR(PL_L)\": {\n            \"key\": \"PL_LSTR\",\n            \"label\": \"Ł\",\n        }\n        \"ALGR(PL_Z)\": {\n            \"key\": \"PL_ZDOT\",\n            \"label\": \"Ż\",\n        }\n        \"ALGR(PL_X)\": {\n            \"key\": \"PL_ZACU\",\n            \"label\": \"Ź\",\n        }\n        \"ALGR(PL_C)\": {\n            \"key\": \"PL_CACU\",\n            \"label\": \"Ć\",\n        }\n        \"ALGR(PL_N)\": {\n            \"key\": \"PL_NACU\",\n            \"label\": \"Ń\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_portuguese_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ \\ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ « │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ + │ ´ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ç │ º │ ~ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"PT_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_1\": {\n            \"key\": \"PT_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"PT_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"PT_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"PT_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"PT_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"PT_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"PT_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"PT_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"PT_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"PT_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"PT_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"PT_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"KC_Q\": {\n            \"key\": \"PT_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"PT_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"PT_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"PT_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"PT_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"PT_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"PT_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"PT_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"PT_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"PT_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"PT_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"PT_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"PT_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"PT_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"PT_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"PT_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"PT_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"PT_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"PT_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"PT_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"PT_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"PT_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"PT_MORD\",\n            \"label\": \"º\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"PT_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"PT_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"PT_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"PT_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"PT_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"PT_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"PT_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"PT_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"PT_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"PT_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"PT_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"PT_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ | │ ! │ \" │ # │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ » │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ * │ ` │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ ª │ ^ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(PT_BSLS)\": {\n            \"key\": \"PT_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(PT_1)\": {\n            \"key\": \"PT_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(PT_2)\": {\n            \"key\": \"PT_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(PT_3)\": {\n            \"key\": \"PT_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(PT_4)\": {\n            \"key\": \"PT_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(PT_5)\": {\n            \"key\": \"PT_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(PT_6)\": {\n            \"key\": \"PT_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(PT_7)\": {\n            \"key\": \"PT_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(PT_8)\": {\n            \"key\": \"PT_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(PT_9)\": {\n            \"key\": \"PT_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(PT_0)\": {\n            \"key\": \"PT_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(PT_QUOT)\": {\n            \"key\": \"PT_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(PT_LDAQ)\": {\n            \"key\": \"PT_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(PT_PLUS)\": {\n            \"key\": \"PT_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(PT_ACUT)\": {\n            \"key\": \"PT_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(PT_MORD)\": {\n            \"key\": \"PT_FORD\",\n            \"label\": \"ª\",\n        }\n        \"S(PT_TILD)\": {\n            \"key\": \"PT_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(PT_LABK)\": {\n            \"key\": \"PT_RABK\",\n            \"label\": \">\",\n        }\n        \"S(PT_COMM)\": {\n            \"key\": \"PT_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(PT_DOT)\": {\n            \"key\": \"PT_COLN\",\n            \"label\": \":\",\n        }\n        \"S(PT_MINS)\": {\n            \"key\": \"PT_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │ @ │ £ │ § │   │   │ { │ [ │ ] │ } │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │   │ ¨ │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(PT_2)\": {\n            \"key\": \"PT_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(PT_3)\": {\n            \"key\": \"PT_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(PT_4)\": {\n            \"key\": \"PT_SECT\",\n            \"label\": \"§\",\n        }\n        \"ALGR(PT_7)\": {\n            \"key\": \"PT_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(PT_8)\": {\n            \"key\": \"PT_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(PT_9)\": {\n            \"key\": \"PT_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(PT_0)\": {\n            \"key\": \"PT_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(PT_PLUS)\": {\n            \"key\": \"PT_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"ALGR(PT_E)\": {\n            \"key\": \"PT_EURO\",\n            \"label\": \"€\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_portuguese_mac_iso_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ § │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ + │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ º │ ´ │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ç │ ~ │ \\ │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"PT_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_1\": {\n            \"key\": \"PT_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"PT_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"PT_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"PT_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"PT_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"PT_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"PT_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"PT_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"PT_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"PT_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"PT_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"PT_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_Q\": {\n            \"key\": \"PT_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"PT_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"PT_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"PT_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"PT_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"PT_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"PT_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"PT_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"PT_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"PT_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"PT_MORD\",\n            \"label\": \"º\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"PT_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"PT_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"PT_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"PT_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"PT_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"PT_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"PT_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"PT_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"PT_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"PT_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"PT_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"PT_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"PT_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"PT_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"PT_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"PT_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"PT_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"PT_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"PT_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"PT_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"PT_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"PT_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"PT_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"PT_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ± │ ! │ \" │ # │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ * │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │   │   │   │   │ ª │ ` │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │   │   │   │   │   │   │   │   │   │   │ ^ │ | │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(PT_SECT)\": {\n            \"key\": \"PT_PLMN\",\n            \"label\": \"±\",\n        }\n        \"S(PT_1)\": {\n            \"key\": \"PT_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(PT_2)\": {\n            \"key\": \"PT_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(PT_3)\": {\n            \"key\": \"PT_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(PT_4)\": {\n            \"key\": \"PT_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(PT_5)\": {\n            \"key\": \"PT_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(PT_6)\": {\n            \"key\": \"PT_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(PT_7)\": {\n            \"key\": \"PT_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(PT_8)\": {\n            \"key\": \"PT_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(PT_9)\": {\n            \"key\": \"PT_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(PT_0)\": {\n            \"key\": \"PT_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(PT_QUOT)\": {\n            \"key\": \"PT_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(PT_PLUS)\": {\n            \"key\": \"PT_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(PT_MORD)\": {\n            \"key\": \"PT_FORD\",\n            \"label\": \"ª\",\n        }\n        \"S(PT_ACUT)\": {\n            \"key\": \"PT_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(PT_TILD)\": {\n            \"key\": \"PT_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(PT_BSLS)\": {\n            \"key\": \"PT_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(PT_LABK)\": {\n            \"key\": \"PT_RABK\",\n            \"label\": \">\",\n        }\n        \"S(PT_COMM)\": {\n            \"key\": \"PT_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(PT_DOT)\": {\n            \"key\": \"PT_COLN\",\n            \"label\": \":\",\n        }\n        \"S(PT_MINS)\": {\n            \"key\": \"PT_UNDS\",\n            \"label\": \"_\",\n        }\n/* Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │   │  │ @ │ € │ £ │ ‰ │ ¶ │ ÷ │ [ │ ] │ ≠ │   │   │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Œ │ ∑ │ Æ │ ® │ ™ │ ¥ │ † │ ı │ Ø │ π │ ° │ ¨ │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ Å │ ß │ ∂ │ ƒ │ ˙ │ ˇ │ ¯ │ „ │ ‘ │ ¸ │ ˜ │ ‹ │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≤ │ Ω │ « │ © │ √ │ ∫ │ ¬ │ µ │ “ │ … │ — │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"A(PT_1)\": {\n            \"key\": \"PT_APPL\",\n            \"label\": \" (Apple logo)\",\n        }\n        \"A(PT_2)\": {\n            \"key\": \"PT_AT\",\n            \"label\": \"@\",\n        }\n        \"A(PT_3)\": {\n            \"key\": \"PT_EURO\",\n            \"label\": \"€\",\n        }\n        \"A(PT_4)\": {\n            \"key\": \"PT_PND\",\n            \"label\": \"£\",\n        }\n        \"A(PT_5)\": {\n            \"key\": \"PT_PERM\",\n            \"label\": \"‰\",\n        }\n        \"A(PT_6)\": {\n            \"key\": \"PT_PILC\",\n            \"label\": \"¶\",\n        }\n        \"A(PT_7)\": {\n            \"key\": \"PT_DIV\",\n            \"label\": \"÷\",\n        }\n        \"A(PT_8)\": {\n            \"key\": \"PT_LBRC\",\n            \"label\": \"[\",\n        }\n        \"A(PT_9)\": {\n            \"key\": \"PT_RBRC\",\n            \"label\": \"]\",\n        }\n        \"A(PT_0)\": {\n            \"key\": \"PT_NEQL\",\n            \"label\": \"≠\",\n        }\n        \"A(PT_Q)\": {\n            \"key\": \"PT_OE\",\n            \"label\": \"Œ\",\n        }\n        \"A(PT_W)\": {\n            \"key\": \"PT_NARS\",\n            \"label\": \"∑\",\n        }\n        \"A(PT_E)\": {\n            \"key\": \"PT_AE\",\n            \"label\": \"Æ\",\n        }\n        \"A(PT_R)\": {\n            \"key\": \"PT_REGD\",\n            \"label\": \"®\",\n        }\n        \"A(PT_T)\": {\n            \"key\": \"PT_TM\",\n            \"label\": \"™\",\n        }\n        \"A(PT_Y)\": {\n            \"key\": \"PT_YEN\",\n            \"label\": \"¥\",\n        }\n        \"A(PT_U)\": {\n            \"key\": \"PT_DAGG\",\n            \"label\": \"†\",\n        }\n        \"A(PT_I)\": {\n            \"key\": \"PT_DLSI\",\n            \"label\": \"ı\",\n        }\n        \"A(PT_O)\": {\n            \"key\": \"PT_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"A(PT_P)\": {\n            \"key\": \"PT_PI\",\n            \"label\": \"π\",\n        }\n        \"A(PT_MORD)\": {\n            \"key\": \"PT_DEG\",\n            \"label\": \"°\",\n        }\n        \"A(PT_ACUT)\": {\n            \"key\": \"PT_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"A(PT_A)\": {\n            \"key\": \"PT_ARNG\",\n            \"label\": \"å\",\n        }\n        \"A(PT_S)\": {\n            \"key\": \"PT_SS\",\n            \"label\": \"ß\",\n        }\n        \"A(PT_D)\": {\n            \"key\": \"PT_PDIF\",\n            \"label\": \"∂\",\n        }\n        \"A(PT_F)\": {\n            \"key\": \"PT_FHK\",\n            \"label\": \"ƒ\",\n        }\n        \"A(PT_G)\": {\n            \"key\": \"PT_DOTA\",\n            \"label\": \"˙\",\n        }\n        \"A(PT_H)\": {\n            \"key\": \"PT_CARN\",\n            \"label\": \"ˇ\",\n        }\n        \"A(PT_J)\": {\n            \"key\": \"PT_MACR\",\n            \"label\": \"¯\",\n        }\n        \"A(PT_K)\": {\n            \"key\": \"PT_DLQU\",\n            \"label\": \"„\",\n        }\n        \"A(PT_L)\": {\n            \"key\": \"PT_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"A(PT_CCED)\": {\n            \"key\": \"PT_CEDL\",\n            \"label\": \"¸\",\n        }\n        \"A(PT_TILD)\": {\n            \"key\": \"PT_STIL\",\n            \"label\": \"˜ (dead)\",\n        }\n        \"A(PT_BSLS)\": {\n            \"key\": \"PT_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"A(PT_LABK)\": {\n            \"key\": \"PT_LTEQ\",\n            \"label\": \"≤\",\n        }\n        \"A(PT_Z)\": {\n            \"key\": \"PT_OMEG\",\n            \"label\": \"Ω\",\n        }\n        \"A(PT_X)\": {\n            \"key\": \"PT_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"A(PT_C)\": {\n            \"key\": \"PT_COPY\",\n            \"label\": \"©\",\n        }\n        \"A(PT_V)\": {\n            \"key\": \"PT_SQRT\",\n            \"label\": \"√\",\n        }\n        \"A(PT_B)\": {\n            \"key\": \"PT_INTG\",\n            \"label\": \"∫\",\n        }\n        \"A(PT_N)\": {\n            \"key\": \"PT_NOT\",\n            \"label\": \"¬\",\n        }\n        \"A(PT_M)\": {\n            \"key\": \"PT_MICR\",\n            \"label\": \"µ\",\n        }\n        \"A(PT_COMM)\": {\n            \"key\": \"PT_LDQU\",\n            \"label\": \"“\",\n        }\n        \"A(PT_DOT)\": {\n            \"key\": \"PT_ELLP\",\n            \"label\": \"…\",\n        }\n        \"A(PT_MINS)\": {\n            \"key\": \"PT_MDSH\",\n            \"label\": \"—\",\n        }\n/* Shift+Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │   │ ¡ │ ﬁ │ ﬂ │ ¢ │ ∞ │ • │ ⁄ │ { │ } │ ≈ │ ¿ │ ◊ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │ ‡ │ ˚ │   │ ∏ │   │ ˝ │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │   │   │ ∆ │   │   │   │   │ ‚ │ ’ │ ˛ │ ˆ │ › │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≥ │   │ » │   │   │   │   │   │ ” │ · │ – │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(A(PT_1))\": {\n            \"key\": \"PT_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"S(A(PT_2))\": {\n            \"key\": \"PT_FI\",\n            \"label\": \"ﬁ\",\n        }\n        \"S(A(PT_3))\": {\n            \"key\": \"PT_FL\",\n            \"label\": \"ﬂ\",\n        }\n        \"S(A(PT_4))\": {\n            \"key\": \"PT_CENT\",\n            \"label\": \"¢\",\n        }\n        \"S(A(PT_5))\": {\n            \"key\": \"PT_INFN\",\n            \"label\": \"∞\",\n        }\n        \"S(A(PT_6))\": {\n            \"key\": \"PT_BULT\",\n            \"label\": \"•\",\n        }\n        \"S(A(PT_7))\": {\n            \"key\": \"PT_FRSL\",\n            \"label\": \"⁄\",\n        }\n        \"S(A(PT_8))\": {\n            \"key\": \"PT_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(A(PT_9))\": {\n            \"key\": \"PT_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(A(PT_0))\": {\n            \"key\": \"PT_AEQL\",\n            \"label\": \"≈\",\n        }\n        \"S(A(PT_QUOT))\": {\n            \"key\": \"PT_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"S(A(PT_PLUS))\": {\n            \"key\": \"PT_LOZN\",\n            \"label\": \"◊\",\n        }\n        \"S(A(PT_U))\": {\n            \"key\": \"PT_DDAG\",\n            \"label\": \"‡\",\n        }\n        \"S(A(PT_I))\": {\n            \"key\": \"PT_RNGA\",\n            \"label\": \"˚\",\n        }\n        \"S(A(PT_P))\": {\n            \"key\": \"PT_NARP\",\n            \"label\": \"∏\",\n        }\n        \"S(A(PT_ACUT))\": {\n            \"key\": \"PT_DACU\",\n            \"label\": \"˝\",\n        }\n        \"S(A(PT_D))\": {\n            \"key\": \"PT_INCR\",\n            \"label\": \"∆\",\n        }\n        \"S(A(PT_K))\": {\n            \"key\": \"PT_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"S(A(PT_L))\": {\n            \"key\": \"PT_RSQU\",\n            \"label\": \"’\",\n        }\n        \"S(A(PT_CCED))\": {\n            \"key\": \"PT_OGON\",\n            \"label\": \"˛\",\n        }\n        \"S(A(PT_TILD))\": {\n            \"key\": \"PT_DCIR\",\n            \"label\": \"ˆ (dead)\",\n        }\n        \"S(A(PT_BSLS))\": {\n            \"key\": \"PT_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"S(A(PT_LABK))\": {\n            \"key\": \"PT_GTEQ\",\n            \"label\": \"≥\",\n        }\n        \"S(A(PT_X))\": {\n            \"key\": \"PT_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(A(PT_COMM))\": {\n            \"key\": \"PT_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(A(PT_DOT))\": {\n            \"key\": \"PT_MDDT\",\n            \"label\": \"·\",\n        }\n        \"S(A(PT_MINS))\": {\n            \"key\": \"PT_NDSH\",\n            \"label\": \"–\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_romanian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ „ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Ă │ Î │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ș │ Ț │ Â │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"RO_DLQU\",\n            \"label\": \"„\",\n        }\n        \"KC_1\": {\n            \"key\": \"RO_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"RO_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"RO_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"RO_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"RO_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"RO_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"RO_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"RO_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"RO_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"RO_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"RO_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"RO_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"RO_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"RO_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"RO_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"RO_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"RO_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"RO_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"RO_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"RO_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"RO_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"RO_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"RO_ABRV\",\n            \"label\": \"Ă\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"RO_ICIR\",\n            \"label\": \"Î\",\n        }\n        \"KC_A\": {\n            \"key\": \"RO_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"RO_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"RO_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"RO_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"RO_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"RO_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"RO_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"RO_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"RO_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"RO_SCOM\",\n            \"label\": \"Ș\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"RO_TCOM\",\n            \"label\": \"Ț\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"RO_ACIR\",\n            \"label\": \"Â\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"RO_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_Z\": {\n            \"key\": \"RO_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"RO_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"RO_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"RO_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"RO_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"RO_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"RO_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"RO_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"RO_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"RO_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ” │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ | │   │   │   │   │   │   │   │ ; │ : │ ? │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(RO_DLQU)\": {\n            \"key\": \"RO_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(RO_1)\": {\n            \"key\": \"RO_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(RO_2)\": {\n            \"key\": \"RO_AT\",\n            \"label\": \"@\",\n        }\n        \"S(RO_3)\": {\n            \"key\": \"RO_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(RO_4)\": {\n            \"key\": \"RO_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(RO_5)\": {\n            \"key\": \"RO_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(RO_6)\": {\n            \"key\": \"RO_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(RO_7)\": {\n            \"key\": \"RO_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(RO_8)\": {\n            \"key\": \"RO_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(RO_9)\": {\n            \"key\": \"RO_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(RO_0)\": {\n            \"key\": \"RO_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(RO_MINS)\": {\n            \"key\": \"RO_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(RO_EQL)\": {\n            \"key\": \"RO_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(RO_BSLS)\": {\n            \"key\": \"RO_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(RO_COMM)\": {\n            \"key\": \"RO_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(RO_DOT)\": {\n            \"key\": \"RO_COLN\",\n            \"label\": \":\",\n        }\n        \"S(RO_SLSH)\": {\n            \"key\": \"RO_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ ~ │ ˇ │ ^ │ ˘ │ ° │ ˛ │ ` │ ˙ │ ´ │ ˝ │ ¨ │ ¸ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │ § │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │ ß │ Đ │   │   │   │   │   │ Ł │   │ ' │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │ © │   │   │   │   │ < │ > │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(RO_DLQU)\": {\n            \"key\": \"RO_GRV\",\n            \"label\": \"`\",\n        }\n        \"ALGR(RO_1)\": {\n            \"key\": \"RO_DTIL\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(RO_2)\": {\n            \"key\": \"RO_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"ALGR(RO_3)\": {\n            \"key\": \"RO_DCIR\",\n            \"label\": \"^ (dead)\",\n        }\n        \"ALGR(RO_4)\": {\n            \"key\": \"RO_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"ALGR(RO_5)\": {\n            \"key\": \"RO_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"ALGR(RO_6)\": {\n            \"key\": \"RO_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"ALGR(RO_7)\": {\n            \"key\": \"RO_DGRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"ALGR(RO_8)\": {\n            \"key\": \"RO_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n        \"ALGR(RO_9)\": {\n            \"key\": \"RO_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(RO_0)\": {\n            \"key\": \"RO_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"ALGR(RO_MINS)\": {\n            \"key\": \"RO_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"ALGR(RO_EQL)\": {\n            \"key\": \"RO_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"ALGR(RO_E)\": {\n            \"key\": \"RO_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(RO_P)\": {\n            \"key\": \"RO_SECT\",\n            \"label\": \"§\",\n        }\n        \"ALGR(RO_ABRV)\": {\n            \"key\": \"RO_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(RO_ICIR)\": {\n            \"key\": \"RO_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(RO_S)\": {\n            \"key\": \"RO_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(RO_D)\": {\n            \"key\": \"RO_DSTR\",\n            \"label\": \"Đ\",\n        }\n        \"ALGR(RO_L)\": {\n            \"key\": \"RO_LSTR\",\n            \"label\": \"Ł\",\n        }\n        \"ALGR(RO_TCOM)\": {\n            \"key\": \"RO_QUOT\",\n            \"label\": \"'\",\n        }\n        \"ALGR(RO_C)\": {\n            \"key\": \"RO_COPY\",\n            \"label\": \"©\",\n        }\n        \"ALGR(RO_COMM)\": {\n            \"key\": \"RO_LABK\",\n            \"label\": \"<\",\n        }\n        \"ALGR(RO_DOT)\": {\n            \"key\": \"RO_RABK\",\n            \"label\": \">\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │   │   │   │   │   │   │   │   │   │   │ – │ ± │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ \" │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │ « │ » │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ALGR(RO_DLQU))\": {\n            \"key\": \"RO_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(ALGR(RO_MINS))\": {\n            \"key\": \"RO_NDSH\",\n            \"label\": \"–\",\n        }\n        \"S(ALGR(RO_EQL))\": {\n            \"key\": \"RO_PLMN\",\n            \"label\": \"±\",\n        }\n        \"S(ALGR(RO_ABRV))\": {\n            \"key\": \"RO_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(ALGR(RO_ICIR))\": {\n            \"key\": \"RO_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(ALGR(RO_TCOM))\": {\n            \"key\": \"RO_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(ALGR(RO_COMM))\": {\n            \"key\": \"RO_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"S(ALGR(RO_DOT))\": {\n            \"key\": \"RO_RDAQ\",\n            \"label\": \"»\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_russian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ Ё │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Й │ Ц │ У │ К │ Е │ Н │ Г │ Ш │ Щ │ З │ Х │ Ъ │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ Ф │ Ы │ В │ А │ П │ Р │ О │ Л │ Д │ Ж │ Э │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Я │ Ч │ С │ М │ И │ Т │ Ь │ Б │ Ю │ . │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"RU_YO\",\n            \"label\": \"Ё\",\n        }\n        \"KC_1\": {\n            \"key\": \"RU_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"RU_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"RU_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"RU_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"RU_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"RU_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"RU_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"RU_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"RU_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"RU_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"RU_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"RU_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"RU_SHTI\",\n            \"label\": \"Й\",\n        }\n        \"KC_W\": {\n            \"key\": \"RU_TSE\",\n            \"label\": \"Ц\",\n        }\n        \"KC_E\": {\n            \"key\": \"RU_U\",\n            \"label\": \"У\",\n        }\n        \"KC_R\": {\n            \"key\": \"RU_KA\",\n            \"label\": \"К\",\n        }\n        \"KC_T\": {\n            \"key\": \"RU_IE\",\n            \"label\": \"Е\",\n        }\n        \"KC_Y\": {\n            \"key\": \"RU_EN\",\n            \"label\": \"Н\",\n        }\n        \"KC_U\": {\n            \"key\": \"RU_GHE\",\n            \"label\": \"Г\",\n        }\n        \"KC_I\": {\n            \"key\": \"RU_SHA\",\n            \"label\": \"Ш\",\n        }\n        \"KC_O\": {\n            \"key\": \"RU_SHCH\",\n            \"label\": \"Щ\",\n        }\n        \"KC_P\": {\n            \"key\": \"RU_ZE\",\n            \"label\": \"З\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"RU_HA\",\n            \"label\": \"Х\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"RU_HARD\",\n            \"label\": \"Ъ\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"RU_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"RU_EF\",\n            \"label\": \"Ф\",\n        }\n        \"KC_S\": {\n            \"key\": \"RU_YERU\",\n            \"label\": \"Ы\",\n        }\n        \"KC_D\": {\n            \"key\": \"RU_VE\",\n            \"label\": \"В\",\n        }\n        \"KC_F\": {\n            \"key\": \"RU_A\",\n            \"label\": \"А\",\n        }\n        \"KC_G\": {\n            \"key\": \"RU_PE\",\n            \"label\": \"П\",\n        }\n        \"KC_H\": {\n            \"key\": \"RU_ER\",\n            \"label\": \"Р\",\n        }\n        \"KC_J\": {\n            \"key\": \"RU_O\",\n            \"label\": \"О\",\n        }\n        \"KC_K\": {\n            \"key\": \"RU_EL\",\n            \"label\": \"Л\",\n        }\n        \"KC_L\": {\n            \"key\": \"RU_DE\",\n            \"label\": \"Д\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"RU_ZHE\",\n            \"label\": \"Ж\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"RU_E\",\n            \"label\": \"Э\",\n        }\n        \"KC_Z\": {\n            \"key\": \"RU_YA\",\n            \"label\": \"Я\",\n        }\n        \"KC_X\": {\n            \"key\": \"RU_CHE\",\n            \"label\": \"Ч\",\n        }\n        \"KC_C\": {\n            \"key\": \"RU_ES\",\n            \"label\": \"С\",\n        }\n        \"KC_V\": {\n            \"key\": \"RU_EM\",\n            \"label\": \"М\",\n        }\n        \"KC_B\": {\n            \"key\": \"RU_I\",\n            \"label\": \"И\",\n        }\n        \"KC_N\": {\n            \"key\": \"RU_TE\",\n            \"label\": \"Т\",\n        }\n        \"KC_M\": {\n            \"key\": \"RU_SOFT\",\n            \"label\": \"Ь\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"RU_BE\",\n            \"label\": \"Б\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"RU_YU\",\n            \"label\": \"Ю\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"RU_DOT\",\n            \"label\": \".\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ! │ \" │ № │ ; │ % │ : │ ? │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │  /  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │   │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │   │   │ , │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(RU_1)\": {\n            \"key\": \"RU_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(RU_2)\": {\n            \"key\": \"RU_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(RU_3)\": {\n            \"key\": \"RU_NUM\",\n            \"label\": \"№\",\n        }\n        \"S(RU_4)\": {\n            \"key\": \"RU_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(RU_5)\": {\n            \"key\": \"RU_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(RU_6)\": {\n            \"key\": \"RU_COLN\",\n            \"label\": \":\",\n        }\n        \"S(RU_7)\": {\n            \"key\": \"RU_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(RU_8)\": {\n            \"key\": \"RU_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(RU_9)\": {\n            \"key\": \"RU_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(RU_0)\": {\n            \"key\": \"RU_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(RU_MINS)\": {\n            \"key\": \"RU_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(RU_EQL)\": {\n            \"key\": \"RU_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(RU_BSLS)\": {\n            \"key\": \"RU_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(RU_DOT)\": {\n            \"key\": \"RU_COMM\",\n            \"label\": \",\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │   │   │   │   │   │   │ ₽ │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │   │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(RU_8)\": {\n            \"key\": \"RU_RUBL\",\n            \"label\": \"₽\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_russian_typewriter_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ | │ № │ - │ / │ \" │ : │ , │ . │ _ │ ? │ % │ ! │ ; │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Й │ Ц │ У │ К │ Е │ Н │ Г │ Ш │ Щ │ З │ Х │ Ъ │  )  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ Ф │ Ы │ В │ А │ П │ Р │ О │ Л │ Д │ Ж │ Э │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Я │ Ч │ С │ М │ И │ Т │ Ь │ Б │ Ю │ Ё │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"RU_PIPE\",\n            \"label\": \"|\",\n        }\n        \"KC_1\": {\n            \"key\": \"RU_NUM\",\n            \"label\": \"№\",\n        }\n        \"KC_2\": {\n            \"key\": \"RU_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_3\": {\n            \"key\": \"RU_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_4\": {\n            \"key\": \"RU_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"KC_5\": {\n            \"key\": \"RU_COLN\",\n            \"label\": \":\",\n        }\n        \"KC_6\": {\n            \"key\": \"RU_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_7\": {\n            \"key\": \"RU_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_8\": {\n            \"key\": \"RU_UNDS\",\n            \"label\": \"_\",\n        }\n        \"KC_9\": {\n            \"key\": \"RU_QUES\",\n            \"label\": \"?\",\n        }\n        \"KC_0\": {\n            \"key\": \"RU_PERC\",\n            \"label\": \"%\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"RU_EXLM\",\n            \"label\": \"!\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"RU_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_Q\": {\n            \"key\": \"RU_SHTI\",\n            \"label\": \"Й\",\n        }\n        \"KC_W\": {\n            \"key\": \"RU_TSE\",\n            \"label\": \"Ц\",\n        }\n        \"KC_E\": {\n            \"key\": \"RU_U\",\n            \"label\": \"У\",\n        }\n        \"KC_R\": {\n            \"key\": \"RU_KA\",\n            \"label\": \"К\",\n        }\n        \"KC_T\": {\n            \"key\": \"RU_IE\",\n            \"label\": \"Е\",\n        }\n        \"KC_Y\": {\n            \"key\": \"RU_EN\",\n            \"label\": \"Н\",\n        }\n        \"KC_U\": {\n            \"key\": \"RU_GHE\",\n            \"label\": \"Г\",\n        }\n        \"KC_I\": {\n            \"key\": \"RU_SHA\",\n            \"label\": \"Ш\",\n        }\n        \"KC_O\": {\n            \"key\": \"RU_SHCH\",\n            \"label\": \"Щ\",\n        }\n        \"KC_P\": {\n            \"key\": \"RU_ZE\",\n            \"label\": \"З\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"RU_HA\",\n            \"label\": \"Х\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"RU_HARD\",\n            \"label\": \"Ъ\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"RU_RPRN\",\n            \"label\": \")\",\n        }\n        \"KC_A\": {\n            \"key\": \"RU_EF\",\n            \"label\": \"Ф\",\n        }\n        \"KC_S\": {\n            \"key\": \"RU_YERU\",\n            \"label\": \"Ы\",\n        }\n        \"KC_D\": {\n            \"key\": \"RU_VE\",\n            \"label\": \"В\",\n        }\n        \"KC_F\": {\n            \"key\": \"RU_A\",\n            \"label\": \"А\",\n        }\n        \"KC_G\": {\n            \"key\": \"RU_PE\",\n            \"label\": \"П\",\n        }\n        \"KC_H\": {\n            \"key\": \"RU_ER\",\n            \"label\": \"Р\",\n        }\n        \"KC_J\": {\n            \"key\": \"RU_O\",\n            \"label\": \"О\",\n        }\n        \"KC_K\": {\n            \"key\": \"RU_EL\",\n            \"label\": \"Л\",\n        }\n        \"KC_L\": {\n            \"key\": \"RU_DE\",\n            \"label\": \"Д\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"RU_ZHE\",\n            \"label\": \"Ж\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"RU_E\",\n            \"label\": \"Э\",\n        }\n        \"KC_Z\": {\n            \"key\": \"RU_YA\",\n            \"label\": \"Я\",\n        }\n        \"KC_X\": {\n            \"key\": \"RU_CHE\",\n            \"label\": \"Ч\",\n        }\n        \"KC_C\": {\n            \"key\": \"RU_ES\",\n            \"label\": \"С\",\n        }\n        \"KC_V\": {\n            \"key\": \"RU_EM\",\n            \"label\": \"М\",\n        }\n        \"KC_B\": {\n            \"key\": \"RU_I\",\n            \"label\": \"И\",\n        }\n        \"KC_N\": {\n            \"key\": \"RU_TE\",\n            \"label\": \"Т\",\n        }\n        \"KC_M\": {\n            \"key\": \"RU_SOFT\",\n            \"label\": \"Ь\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"RU_BE\",\n            \"label\": \"Б\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"RU_YU\",\n            \"label\": \"Ю\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"RU_YO\",\n            \"label\": \"Ё\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ + │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ = │ \\ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │  (  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │   │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(RU_PIPE)\": {\n            \"key\": \"RU_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(RU_NUM)\": {\n            \"key\": \"RU_1\",\n            \"label\": \"1\",\n        }\n        \"S(RU_MINS)\": {\n            \"key\": \"RU_2\",\n            \"label\": \"2\",\n        }\n        \"S(RU_SLSH)\": {\n            \"key\": \"RU_3\",\n            \"label\": \"3\",\n        }\n        \"S(RU_DQUO)\": {\n            \"key\": \"RU_4\",\n            \"label\": \"4\",\n        }\n        \"S(RU_COLN)\": {\n            \"key\": \"RU_5\",\n            \"label\": \"5\",\n        }\n        \"S(RU_COMM)\": {\n            \"key\": \"RU_6\",\n            \"label\": \"6\",\n        }\n        \"S(RU_DOT)\": {\n            \"key\": \"RU_7\",\n            \"label\": \"7\",\n        }\n        \"S(RU_UNDS)\": {\n            \"key\": \"RU_8\",\n            \"label\": \"8\",\n        }\n        \"S(RU_QUES)\": {\n            \"key\": \"RU_9\",\n            \"label\": \"9\",\n        }\n        \"S(RU_PERC)\": {\n            \"key\": \"RU_0\",\n            \"label\": \"0\",\n        }\n        \"S(RU_EXLM)\": {\n            \"key\": \"RU_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(RU_SCLN)\": {\n            \"key\": \"RU_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"S(RU_RPRN)\": {\n            \"key\": \"RU_LPRN\",\n            \"label\": \"(\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │   │   │   │   │   │   │ ₽ │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │   │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(RU_UNDS)\": {\n            \"key\": \"RU_RUBL\",\n            \"label\": \"₽\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_serbian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Љ │ Њ │ Е │ Р │ Т │ З │ У │ И │ О │ П │ Ш │ Ђ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ А │ С │ Д │ Ф │ Г │ Х │ Ј │ К │ Л │ Ч │ Ћ │ Ж │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Ѕ │ Џ │ Ц │ В │ Б │ Н │ М │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"RS_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"RS_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"RS_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"RS_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"RS_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"RS_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"RS_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"RS_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"RS_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"RS_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"RS_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"RS_QUOT\",\n            \"label\": \"' (dead)\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"RS_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_Q\": {\n            \"key\": \"RS_LJE\",\n            \"label\": \"Љ\",\n        }\n        \"KC_W\": {\n            \"key\": \"RS_NJE\",\n            \"label\": \"Њ\",\n        }\n        \"KC_E\": {\n            \"key\": \"RS_IE\",\n            \"label\": \"Е\",\n        }\n        \"KC_R\": {\n            \"key\": \"RS_ER\",\n            \"label\": \"Р\",\n        }\n        \"KC_T\": {\n            \"key\": \"RS_TE\",\n            \"label\": \"Т\",\n        }\n        \"KC_Y\": {\n            \"key\": \"RS_ZE\",\n            \"label\": \"З\",\n        }\n        \"KC_U\": {\n            \"key\": \"RS_U\",\n            \"label\": \"У\",\n        }\n        \"KC_I\": {\n            \"key\": \"RS_I\",\n            \"label\": \"И\",\n        }\n        \"KC_O\": {\n            \"key\": \"RS_O\",\n            \"label\": \"О\",\n        }\n        \"KC_P\": {\n            \"key\": \"RS_PE\",\n            \"label\": \"П\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"RS_SHA\",\n            \"label\": \"Ш\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"RS_DJE\",\n            \"label\": \"Ђ\",\n        }\n        \"KC_A\": {\n            \"key\": \"RS_A\",\n            \"label\": \"А\",\n        }\n        \"KC_S\": {\n            \"key\": \"RS_ES\",\n            \"label\": \"С\",\n        }\n        \"KC_D\": {\n            \"key\": \"RS_DE\",\n            \"label\": \"Д\",\n        }\n        \"KC_F\": {\n            \"key\": \"RS_EF\",\n            \"label\": \"Ф\",\n        }\n        \"KC_G\": {\n            \"key\": \"RS_GHE\",\n            \"label\": \"Г\",\n        }\n        \"KC_H\": {\n            \"key\": \"RS_HA\",\n            \"label\": \"Х\",\n        }\n        \"KC_J\": {\n            \"key\": \"RS_JE\",\n            \"label\": \"Ј\",\n        }\n        \"KC_K\": {\n            \"key\": \"RS_KA\",\n            \"label\": \"К\",\n        }\n        \"KC_L\": {\n            \"key\": \"RS_EL\",\n            \"label\": \"Л\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"RS_CHE\",\n            \"label\": \"Ч\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"RS_TSHE\",\n            \"label\": \"Ћ\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"RS_ZHE\",\n            \"label\": \"Ж\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"RS_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"RS_DZE\",\n            \"label\": \"Ѕ\",\n        }\n        \"KC_X\": {\n            \"key\": \"RS_DZHE\",\n            \"label\": \"Џ\",\n        }\n        \"KC_C\": {\n            \"key\": \"RS_TSE\",\n            \"label\": \"Ц\",\n        }\n        \"KC_V\": {\n            \"key\": \"RS_VE\",\n            \"label\": \"В\",\n        }\n        \"KC_B\": {\n            \"key\": \"RS_BE\",\n            \"label\": \"Б\",\n        }\n        \"KC_N\": {\n            \"key\": \"RS_EN\",\n            \"label\": \"Н\",\n        }\n        \"KC_M\": {\n            \"key\": \"RS_EM\",\n            \"label\": \"М\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"RS_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"RS_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"RS_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ \" │ # │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ * │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(RS_GRV)\": {\n            \"key\": \"RS_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(RS_1)\": {\n            \"key\": \"RS_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(RS_2)\": {\n            \"key\": \"RS_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(RS_3)\": {\n            \"key\": \"RS_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(RS_4)\": {\n            \"key\": \"RS_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(RS_5)\": {\n            \"key\": \"RS_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(RS_6)\": {\n            \"key\": \"RS_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(RS_7)\": {\n            \"key\": \"RS_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(RS_8)\": {\n            \"key\": \"RS_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(RS_9)\": {\n            \"key\": \"RS_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(RS_0)\": {\n            \"key\": \"RS_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(RS_QUOT)\": {\n            \"key\": \"RS_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(RS_PLUS)\": {\n            \"key\": \"RS_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(RS_LABK)\": {\n            \"key\": \"RS_RABK\",\n            \"label\": \">\",\n        }\n        \"S(RS_COMM)\": {\n            \"key\": \"RS_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(RS_DOT)\": {\n            \"key\": \"RS_COLN\",\n            \"label\": \":\",\n        }\n        \"S(RS_MINS)\": {\n            \"key\": \"RS_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(RS_IE)\": {\n            \"key\": \"RS_EURO\",\n            \"label\": \"€\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_serbian_latin_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ‚ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ Š │ Đ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Č │ Ć │ Ž │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"RS_SLQU\",\n            \"label\": \"‚ (dead)\",\n        }\n        \"KC_1\": {\n            \"key\": \"RS_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"RS_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"RS_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"RS_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"RS_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"RS_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"RS_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"RS_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"RS_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"RS_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"RS_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"RS_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_Q\": {\n            \"key\": \"RS_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"RS_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"RS_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"RS_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"RS_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"RS_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"RS_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"RS_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"RS_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"RS_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"RS_SCAR\",\n            \"label\": \"Š\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"RS_DSTR\",\n            \"label\": \"Đ\",\n        }\n        \"KC_A\": {\n            \"key\": \"RS_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"RS_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"RS_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"RS_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"RS_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"RS_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"RS_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"RS_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"RS_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"RS_CCAR\",\n            \"label\": \"Č\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"RS_CACU\",\n            \"label\": \"Ć\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"RS_ZCAR\",\n            \"label\": \"Ž\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"RS_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"RS_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"RS_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"RS_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"RS_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"RS_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"RS_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"RS_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"RS_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"RS_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"RS_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ \" │ # │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ * │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(RS_SLQU)\": {\n            \"key\": \"RS_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(RS_1)\": {\n            \"key\": \"RS_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(RS_2)\": {\n            \"key\": \"RS_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(RS_3)\": {\n            \"key\": \"RS_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(RS_4)\": {\n            \"key\": \"RS_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(RS_5)\": {\n            \"key\": \"RS_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(RS_6)\": {\n            \"key\": \"RS_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(RS_7)\": {\n            \"key\": \"RS_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(RS_8)\": {\n            \"key\": \"RS_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(RS_9)\": {\n            \"key\": \"RS_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(RS_0)\": {\n            \"key\": \"RS_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(RS_QUOT)\": {\n            \"key\": \"RS_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(RS_PLUS)\": {\n            \"key\": \"RS_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(RS_LABK)\": {\n            \"key\": \"RS_RABK\",\n            \"label\": \">\",\n        }\n        \"S(RS_COMM)\": {\n            \"key\": \"RS_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(RS_DOT)\": {\n            \"key\": \"RS_COLN\",\n            \"label\": \":\",\n        }\n        \"S(RS_MINS)\": {\n            \"key\": \"RS_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │ ˇ │ ^ │ ˘ │ ° │ ˛ │ ` │ ˙ │ ´ │ ˝ │ ¨ │ ¸ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ \\ │ | │ € │   │   │   │   │   │   │   │ ÷ │ × │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │ [ │ ] │   │   │ ł │ Ł │   │ ß │ ¤ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │ @ │ { │ } │ § │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(RS_2)\": {\n            \"key\": \"RS_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"ALGR(RS_3)\": {\n            \"key\": \"RS_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"ALGR(RS_4)\": {\n            \"key\": \"RS_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"ALGR(RS_5)\": {\n            \"key\": \"RS_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"ALGR(RS_6)\": {\n            \"key\": \"RS_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"ALGR(RS_7)\": {\n            \"key\": \"RS_GRV\",\n            \"label\": \"`\",\n        }\n        \"ALGR(RS_8)\": {\n            \"key\": \"RS_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n        \"ALGR(RS_9)\": {\n            \"key\": \"RS_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(RS_0)\": {\n            \"key\": \"RS_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"ALGR(RS_QUOT)\": {\n            \"key\": \"RS_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"ALGR(RS_PLUS)\": {\n            \"key\": \"RS_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"ALGR(RS_Q)\": {\n            \"key\": \"RS_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(RS_W)\": {\n            \"key\": \"RS_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(RS_E)\": {\n            \"key\": \"RS_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(RS_SCAR)\": {\n            \"key\": \"RS_DIV\",\n            \"label\": \"÷\",\n        }\n        \"ALGR(RS_DSTR)\": {\n            \"key\": \"RS_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(RS_F)\": {\n            \"key\": \"RS_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(RS_G)\": {\n            \"key\": \"RS_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(RS_K)\": {\n            \"key\": \"RS_LLST\",\n            \"label\": \"ł\",\n        }\n        \"ALGR(RS_L)\": {\n            \"key\": \"RS_CLST\",\n            \"label\": \"Ł\",\n        }\n        \"ALGR(RS_CACU)\": {\n            \"key\": \"RS_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(RS_ZCAR)\": {\n            \"key\": \"RS_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(RS_V)\": {\n            \"key\": \"RS_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(RS_B)\": {\n            \"key\": \"RS_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(RS_N)\": {\n            \"key\": \"RS_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(RS_M)\": {\n            \"key\": \"RS_SECT\",\n            \"label\": \"§\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_slovak_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ; │ + │ ľ │ š │ č │ ť │ ž │ ý │ á │ í │ é │ = │ ´ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ ú │ ä │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ô │ § │ ň │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ & │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"SK_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_1\": {\n            \"key\": \"SK_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_2\": {\n            \"key\": \"SK_LCAR\",\n            \"label\": \"ľ\",\n        }\n        \"KC_3\": {\n            \"key\": \"SK_SCAR\",\n            \"label\": \"š\",\n        }\n        \"KC_4\": {\n            \"key\": \"SK_CCAR\",\n            \"label\": \"č\",\n        }\n        \"KC_5\": {\n            \"key\": \"SK_TCAR\",\n            \"label\": \"ť\",\n        }\n        \"KC_6\": {\n            \"key\": \"SK_ZCAR\",\n            \"label\": \"ž\",\n        }\n        \"KC_7\": {\n            \"key\": \"SK_YACU\",\n            \"label\": \"ý\",\n        }\n        \"KC_8\": {\n            \"key\": \"SK_AACU\",\n            \"label\": \"á\",\n        }\n        \"KC_9\": {\n            \"key\": \"SK_IACU\",\n            \"label\": \"í\",\n        }\n        \"KC_0\": {\n            \"key\": \"SK_EACU\",\n            \"label\": \"é\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"SK_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"SK_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"SK_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"SK_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"SK_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"SK_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"SK_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"SK_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"SK_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"SK_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"SK_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"SK_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"SK_UACU\",\n            \"label\": \"ú\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"SK_ADIA\",\n            \"label\": \"ä\",\n        }\n        \"KC_A\": {\n            \"key\": \"SK_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"SK_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"SK_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"SK_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"SK_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"SK_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"SK_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"SK_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"SK_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"SK_OCIR\",\n            \"label\": \"ô\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"SK_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"SK_NCAR\",\n            \"label\": \"ň\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"SK_AMPR\",\n            \"label\": \"&\",\n        }\n        \"KC_Z\": {\n            \"key\": \"SK_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"SK_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"SK_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"SK_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"SK_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"SK_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"SK_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"SK_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"SK_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"SK_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ° │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ % │ ˇ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ / │ ( │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ \" │ ! │ ) │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ * │   │   │   │   │   │   │   │ ? │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(SK_SCLN)\": {\n            \"key\": \"SK_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"S(SK_PLUS)\": {\n            \"key\": \"SK_1\",\n            \"label\": \"1\",\n        }\n        \"S(SK_LCAR)\": {\n            \"key\": \"SK_2\",\n            \"label\": \"2\",\n        }\n        \"S(SK_SCAR)\": {\n            \"key\": \"SK_3\",\n            \"label\": \"3\",\n        }\n        \"S(SK_CCAR)\": {\n            \"key\": \"SK_4\",\n            \"label\": \"4\",\n        }\n        \"S(SK_TCAR)\": {\n            \"key\": \"SK_5\",\n            \"label\": \"5\",\n        }\n        \"S(SK_ZCAR)\": {\n            \"key\": \"SK_6\",\n            \"label\": \"6\",\n        }\n        \"S(SK_YACU)\": {\n            \"key\": \"SK_7\",\n            \"label\": \"7\",\n        }\n        \"S(SK_AACU)\": {\n            \"key\": \"SK_8\",\n            \"label\": \"8\",\n        }\n        \"S(SK_IACU)\": {\n            \"key\": \"SK_9\",\n            \"label\": \"9\",\n        }\n        \"S(SK_EACU)\": {\n            \"key\": \"SK_0\",\n            \"label\": \"0\",\n        }\n        \"S(SK_EQL)\": {\n            \"key\": \"SK_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(SK_ACUT)\": {\n            \"key\": \"SK_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"S(SK_UACU)\": {\n            \"key\": \"SK_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(SK_ADIA)\": {\n            \"key\": \"SK_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(SK_OCIR)\": {\n            \"key\": \"SK_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(SK_SECT)\": {\n            \"key\": \"SK_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(SK_NCAR)\": {\n            \"key\": \"SK_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(SK_AMPR)\": {\n            \"key\": \"SK_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(SK_COMM)\": {\n            \"key\": \"SK_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(SK_DOT)\": {\n            \"key\": \"SK_COLN\",\n            \"label\": \":\",\n        }\n        \"S(SK_MINS)\": {\n            \"key\": \"SK_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ~ │   │ ^ │ ˘ │ ° │ ˛ │ ` │ ˙ │   │ ˝ │ ¨ │ ¸ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ \\ │ | │ € │   │   │   │   │   │   │ ' │ ÷ │ × │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │ đ │ Đ │ [ │ ] │   │   │ ł │ Ł │ $ │ ß │ ¤ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ > │ # │   │ @ │ { │ } │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(SK_PLUS)\": {\n            \"key\": \"SK_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(SK_SCAR)\": {\n            \"key\": \"SK_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"ALGR(SK_CCAR)\": {\n            \"key\": \"SK_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"ALGR(SK_TCAR)\": {\n            \"key\": \"SK_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"ALGR(SK_ZCAR)\": {\n            \"key\": \"SK_GRV\",\n            \"label\": \"`\",\n        }\n        \"ALGR(SK_YACU)\": {\n            \"key\": \"SK_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n        \"ALGR(SK_EACU)\": {\n            \"key\": \"SK_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"ALGR(SK_EQL)\": {\n            \"key\": \"SK_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"ALGR(SK_ACUT)\": {\n            \"key\": \"SK_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"ALGR(SK_Q)\": {\n            \"key\": \"SK_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(SK_W)\": {\n            \"key\": \"SK_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(SK_E)\": {\n            \"key\": \"SK_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(SK_P)\": {\n            \"key\": \"SK_QUOT\",\n            \"label\": \"'\",\n        }\n        \"ALGR(SK_UACU)\": {\n            \"key\": \"SK_DIV\",\n            \"label\": \"÷\",\n        }\n        \"ALGR(SK_ADIA)\": {\n            \"key\": \"SK_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(SK_S)\": {\n            \"key\": \"SK_LDST\",\n            \"label\": \"đ\",\n        }\n        \"ALGR(SK_D)\": {\n            \"key\": \"SK_CDST\",\n            \"label\": \"Đ\",\n        }\n        \"ALGR(SK_F)\": {\n            \"key\": \"SK_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(SK_G)\": {\n            \"key\": \"SK_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(SK_K)\": {\n            \"key\": \"SK_LLST\",\n            \"label\": \"ł\",\n        }\n        \"ALGR(SK_L)\": {\n            \"key\": \"SK_CLST\",\n            \"label\": \"Ł\",\n        }\n        \"ALGR(SK_OCIR)\": {\n            \"key\": \"SK_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(SK_SECT)\": {\n            \"key\": \"SK_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(SK_NCAR)\": {\n            \"key\": \"SK_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(SK_AMPR)\": {\n            \"key\": \"SK_LABK\",\n            \"label\": \"<\",\n        }\n        \"ALGR(SK_Y)\": {\n            \"key\": \"SK_RABK\",\n            \"label\": \">\",\n        }\n        \"ALGR(SK_X)\": {\n            \"key\": \"SK_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(SK_V)\": {\n            \"key\": \"SK_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(SK_B)\": {\n            \"key\": \"SK_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(SK_N)\": {\n            \"key\": \"SK_RCBR\",\n            \"label\": \"}\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_slovenian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¸ │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ Š │ Đ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Č │ Ć │ Ž │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"SI_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"KC_1\": {\n            \"key\": \"SI_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"SI_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"SI_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"SI_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"SI_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"SI_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"SI_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"SI_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"SI_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"SI_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"SI_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"SI_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_Q\": {\n            \"key\": \"SI_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"SI_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"SI_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"SI_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"SI_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"SI_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"SI_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"SI_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"SI_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"SI_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"SI_SCAR\",\n            \"label\": \"Š\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"SI_DSTR\",\n            \"label\": \"Đ\",\n        }\n        \"KC_A\": {\n            \"key\": \"SI_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"SI_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"SI_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"SI_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"SI_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"SI_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"SI_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"SI_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"SI_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"SI_CCAR\",\n            \"label\": \"Č\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"SI_CACU\",\n            \"label\": \"Ć\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"SI_ZCAR\",\n            \"label\": \"Ž\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"SI_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"SI_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"SI_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"SI_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"SI_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"SI_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"SI_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"SI_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"SI_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"SI_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"SI_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¨ │ ! │ \" │ # │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ * │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(SI_CEDL)\": {\n            \"key\": \"SI_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(SI_1)\": {\n            \"key\": \"SI_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(SI_2)\": {\n            \"key\": \"SI_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(SI_3)\": {\n            \"key\": \"SI_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(SI_4)\": {\n            \"key\": \"SI_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(SI_5)\": {\n            \"key\": \"SI_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(SI_6)\": {\n            \"key\": \"SI_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(SI_7)\": {\n            \"key\": \"SI_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(SI_8)\": {\n            \"key\": \"SI_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(SI_9)\": {\n            \"key\": \"SI_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(SI_0)\": {\n            \"key\": \"SI_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(SI_QUOT)\": {\n            \"key\": \"SI_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(SI_PLUS)\": {\n            \"key\": \"SI_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(SI_LABK)\": {\n            \"key\": \"SI_RABK\",\n            \"label\": \">\",\n        }\n        \"S(SI_COMM)\": {\n            \"key\": \"SI_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(SI_DOT)\": {\n            \"key\": \"SI_COLN\",\n            \"label\": \":\",\n        }\n        \"S(SI_MINS)\": {\n            \"key\": \"SI_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ~ │ ˇ │ ^ │ ˘ │ ° │ ˛ │ ` │ ˙ │ ´ │ ˝ │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ \\ │ | │ € │   │   │   │   │   │   │   │ ÷ │ × │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │ [ │ ] │   │   │ ł │ Ł │   │ ß │ ¤ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │ @ │ { │ } │ § │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(SI_1)\": {\n            \"key\": \"SI_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(SI_2)\": {\n            \"key\": \"SI_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"ALGR(SI_3)\": {\n            \"key\": \"SI_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"ALGR(SI_4)\": {\n            \"key\": \"SI_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"ALGR(SI_5)\": {\n            \"key\": \"SI_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"ALGR(SI_6)\": {\n            \"key\": \"SI_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"ALGR(SI_7)\": {\n            \"key\": \"SI_GRV\",\n            \"label\": \"`\",\n        }\n        \"ALGR(SI_8)\": {\n            \"key\": \"SI_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n        \"ALGR(SI_9)\": {\n            \"key\": \"SI_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(SI_0)\": {\n            \"key\": \"SI_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"ALGR(SI_Q)\": {\n            \"key\": \"SI_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(SI_W)\": {\n            \"key\": \"SI_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(SI_E)\": {\n            \"key\": \"SI_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(SI_SCAR)\": {\n            \"key\": \"SI_DIV\",\n            \"label\": \"÷\",\n        }\n        \"ALGR(SI_DSTR)\": {\n            \"key\": \"SI_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(SI_F)\": {\n            \"key\": \"SI_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(SI_G)\": {\n            \"key\": \"SI_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(SI_K)\": {\n            \"key\": \"SI_LLST\",\n            \"label\": \"ł\",\n        }\n        \"ALGR(SI_L)\": {\n            \"key\": \"SI_CLST\",\n            \"label\": \"Ł\",\n        }\n        \"ALGR(SI_CACU)\": {\n            \"key\": \"SI_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(SI_ZCAR)\": {\n            \"key\": \"SI_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(SI_V)\": {\n            \"key\": \"SI_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(SI_B)\": {\n            \"key\": \"SI_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(SI_N)\": {\n            \"key\": \"SI_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(SI_M)\": {\n            \"key\": \"SI_SECT\",\n            \"label\": \"§\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_spanish_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ º │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ ¡ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ ` │ + │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ñ │ ´ │ Ç │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"ES_MORD\",\n            \"label\": \"º\",\n        }\n        \"KC_1\": {\n            \"key\": \"ES_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"ES_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"ES_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"ES_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"ES_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"ES_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"ES_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"ES_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"ES_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"ES_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"ES_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"ES_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"KC_Q\": {\n            \"key\": \"ES_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"ES_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"ES_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"ES_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"ES_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"ES_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"ES_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"ES_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"ES_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"ES_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"ES_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"ES_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_A\": {\n            \"key\": \"ES_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"ES_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"ES_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"ES_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"ES_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"ES_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"ES_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"ES_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"ES_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"ES_NTIL\",\n            \"label\": \"Ñ\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"ES_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"ES_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"ES_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"ES_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"ES_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"ES_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"ES_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"ES_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"ES_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"ES_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"ES_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"ES_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"ES_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ª │ ! │ \" │ · │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ ¿ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ ^ │ * │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ ¨ │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ES_MORD)\": {\n            \"key\": \"ES_FORD\",\n            \"label\": \"ª\",\n        }\n        \"S(ES_1)\": {\n            \"key\": \"ES_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(ES_2)\": {\n            \"key\": \"ES_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(ES_3)\": {\n            \"key\": \"ES_BULT\",\n            \"label\": \"·\",\n        }\n        \"S(ES_4)\": {\n            \"key\": \"ES_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(ES_5)\": {\n            \"key\": \"ES_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(ES_6)\": {\n            \"key\": \"ES_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(ES_7)\": {\n            \"key\": \"ES_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(ES_8)\": {\n            \"key\": \"ES_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(ES_9)\": {\n            \"key\": \"ES_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(ES_0)\": {\n            \"key\": \"ES_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(ES_QUOT)\": {\n            \"key\": \"ES_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(ES_IEXL)\": {\n            \"key\": \"ES_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"S(ES_GRV)\": {\n            \"key\": \"ES_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(ES_PLUS)\": {\n            \"key\": \"ES_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(ES_ACUT)\": {\n            \"key\": \"ES_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(ES_LABK)\": {\n            \"key\": \"ES_RABK\",\n            \"label\": \">\",\n        }\n        \"S(KC_COMM)\": {\n            \"key\": \"ES_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(KC_DOT)\": {\n            \"key\": \"ES_COLN\",\n            \"label\": \":\",\n        }\n        \"S(ES_MINS)\": {\n            \"key\": \"ES_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ \\ │ | │ @ │ # │ ~ │ € │ ¬ │   │   │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ { │ } │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(ES_MORD)\": {\n            \"key\": \"ES_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(ES_1)\": {\n            \"key\": \"ES_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(ES_2)\": {\n            \"key\": \"ES_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(ES_3)\": {\n            \"key\": \"ES_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(ES_4)\": {\n            \"key\": \"ES_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(ES_5)\": {\n            \"key\": \"ES_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(ES_6)\": {\n            \"key\": \"ES_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(ES_GRV)\": {\n            \"key\": \"ES_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(ES_PLUS)\": {\n            \"key\": \"ES_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(ES_ACUT)\": {\n            \"key\": \"ES_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(ES_CCED)\": {\n            \"key\": \"ES_RCBR\",\n            \"label\": \"}\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_spanish_dvorak_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ º │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ ¡ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ . │ , │ Ñ │ P │ Y │ F │ G │ C │ H │ L │ ` │ + │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ O │ E │ U │ I │ D │ R │ T │ N │ S │ ´ │ Ç │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ - │ Q │ J │ K │ X │ B │ M │ W │ V │ Z │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"DV_MORD\",\n            \"label\": \"º\",\n        }\n        \"KC_1\": {\n            \"key\": \"DV_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"DV_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"DV_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"DV_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"DV_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"DV_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"DV_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"DV_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"DV_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"DV_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"DV_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"DV_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"KC_Q\": {\n            \"key\": \"DV_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_W\": {\n            \"key\": \"DV_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_E\": {\n            \"key\": \"DV_NTIL\",\n            \"label\": \"Ñ\",\n        }\n        \"KC_R\": {\n            \"key\": \"DV_P\",\n            \"label\": \"P\",\n        }\n        \"KC_T\": {\n            \"key\": \"DV_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_Y\": {\n            \"key\": \"DV_F\",\n            \"label\": \"F\",\n        }\n        \"KC_U\": {\n            \"key\": \"DV_G\",\n            \"label\": \"G\",\n        }\n        \"KC_I\": {\n            \"key\": \"DV_C\",\n            \"label\": \"C\",\n        }\n        \"KC_O\": {\n            \"key\": \"DV_H\",\n            \"label\": \"H\",\n        }\n        \"KC_P\": {\n            \"key\": \"DV_L\",\n            \"label\": \"L\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"DV_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"DV_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_A\": {\n            \"key\": \"DV_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"DV_O\",\n            \"label\": \"O\",\n        }\n        \"KC_D\": {\n            \"key\": \"DV_E\",\n            \"label\": \"E\",\n        }\n        \"KC_F\": {\n            \"key\": \"DV_U\",\n            \"label\": \"U\",\n        }\n        \"KC_G\": {\n            \"key\": \"DV_I\",\n            \"label\": \"I\",\n        }\n        \"KC_H\": {\n            \"key\": \"DV_D\",\n            \"label\": \"D\",\n        }\n        \"KC_J\": {\n            \"key\": \"DV_R\",\n            \"label\": \"R\",\n        }\n        \"KC_K\": {\n            \"key\": \"DV_T\",\n            \"label\": \"T\",\n        }\n        \"KC_L\": {\n            \"key\": \"DV_N\",\n            \"label\": \"N\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"DV_S\",\n            \"label\": \"S\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"DV_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"DV_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"DV_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"DV_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_X\": {\n            \"key\": \"DV_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_C\": {\n            \"key\": \"DV_J\",\n            \"label\": \"J\",\n        }\n        \"KC_V\": {\n            \"key\": \"DV_K\",\n            \"label\": \"K\",\n        }\n        \"KC_B\": {\n            \"key\": \"DV_X\",\n            \"label\": \"X\",\n        }\n        \"KC_N\": {\n            \"key\": \"DV_B\",\n            \"label\": \"B\",\n        }\n        \"KC_M\": {\n            \"key\": \"DV_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"DV_W\",\n            \"label\": \"W\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"DV_V\",\n            \"label\": \"V\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"DV_Z\",\n            \"label\": \"Z\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ª │ ! │ \" │ · │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ ¿ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ : │ ; │   │   │   │   │   │   │   │   │ ^ │ * │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ ¨ │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │ _ │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(DV_MORD)\": {\n            \"key\": \"DV_FORD\",\n            \"label\": \"ª\",\n        }\n        \"S(DV_1)\": {\n            \"key\": \"DV_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(DV_2)\": {\n            \"key\": \"DV_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(DV_3)\": {\n            \"key\": \"DV_BULT\",\n            \"label\": \"·\",\n        }\n        \"S(DV_4)\": {\n            \"key\": \"DV_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(DV_5)\": {\n            \"key\": \"DV_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(DV_6)\": {\n            \"key\": \"DV_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(DV_7)\": {\n            \"key\": \"DV_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(DV_8)\": {\n            \"key\": \"DV_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(DV_9)\": {\n            \"key\": \"DV_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(DV_0)\": {\n            \"key\": \"DV_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(DV_QUOT)\": {\n            \"key\": \"DV_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(DV_IEXL)\": {\n            \"key\": \"DV_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"S(DV_DOT)\": {\n            \"key\": \"DV_COLN\",\n            \"label\": \":\",\n        }\n        \"S(DV_COMM)\": {\n            \"key\": \"DV_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(DV_GRV)\": {\n            \"key\": \"DV_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(DV_PLUS)\": {\n            \"key\": \"DV_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(DV_ACUT)\": {\n            \"key\": \"DV_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(DV_LABK)\": {\n            \"key\": \"DV_RABK\",\n            \"label\": \">\",\n        }\n        \"S(DV_MINS)\": {\n            \"key\": \"DV_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ \\ │ | │ @ │ # │ ~ │ € │ ¬ │   │   │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ { │ } │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(DV_MORD)\": {\n            \"key\": \"DV_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(DV_1)\": {\n            \"key\": \"DV_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(DV_2)\": {\n            \"key\": \"DV_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(DV_3)\": {\n            \"key\": \"DV_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(DV_4)\": {\n            \"key\": \"DV_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(DV_5)\": {\n            \"key\": \"DV_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(DV_6)\": {\n            \"key\": \"DV_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(DV_GRV)\": {\n            \"key\": \"DV_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(DV_PLUS)\": {\n            \"key\": \"DV_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(DV_ACUT)\": {\n            \"key\": \"DV_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(DV_CCED)\": {\n            \"key\": \"DV_RCBR\",\n            \"label\": \"}\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_spanish_latin_america_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ | │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ ¿ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ ´ │ + │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ñ │ { │ } │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"ES_PIPE\",\n            \"label\": \"|\",\n        }\n        \"KC_1\": {\n            \"key\": \"ES_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"ES_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"ES_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"ES_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"ES_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"ES_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"ES_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"ES_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"ES_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"ES_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"ES_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"ES_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"KC_Q\": {\n            \"key\": \"ES_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"ES_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"ES_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"ES_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"ES_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"ES_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"ES_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"ES_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"ES_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"ES_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"ES_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"ES_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_A\": {\n            \"key\": \"ES_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"ES_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"ES_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"ES_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"ES_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"ES_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"ES_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"ES_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"ES_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"ES_NTIL\",\n            \"label\": \"Ñ\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"ES_LCBR\",\n            \"label\": \"{\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"ES_RCBR\",\n            \"label\": \"}\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"ES_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"ES_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"ES_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"ES_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"ES_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"ES_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"ES_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"ES_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"ES_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"ES_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"ES_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ° │ ! │ \" │ # │ $ │ % │ & │ / │ ( │ ) │ = │ ? │ ¡ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ ¨ │ * │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ [ │ ] │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ES_PIPE)\": {\n            \"key\": \"ES_MORD\",\n            \"label\": \"°\",\n        }\n        \"S(ES_1)\": {\n            \"key\": \"ES_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(ES_2)\": {\n            \"key\": \"ES_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(ES_3)\": {\n            \"key\": \"ES_NUMB\",\n            \"label\": \"#\",\n        }\n        \"S(ES_4)\": {\n            \"key\": \"ES_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(ES_5)\": {\n            \"key\": \"ES_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(ES_6)\": {\n            \"key\": \"ES_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(ES_7)\": {\n            \"key\": \"ES_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(ES_8)\": {\n            \"key\": \"ES_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(ES_9)\": {\n            \"key\": \"ES_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(ES_0)\": {\n            \"key\": \"ES_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(ES_QUOT)\": {\n            \"key\": \"ES_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(ES_IQUE)\": {\n            \"key\": \"ES_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"S(ES_ACUT)\": {\n            \"key\": \"ES_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(ES_PLUS)\": {\n            \"key\": \"ES_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(ES_LCBR)\": {\n            \"key\": \"ES_LBRC\",\n            \"label\": \"[\",\n        }\n        \"S(ES_RCBR)\": {\n            \"key\": \"ES_RBRC\",\n            \"label\": \"]\",\n        }\n        \"S(ES_LABK)\": {\n            \"key\": \"ES_RABK\",\n            \"label\": \">\",\n        }\n        \"S(ES_COMM)\": {\n            \"key\": \"ES_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(ES_DOT)\": {\n            \"key\": \"ES_COLN\",\n            \"label\": \":\",\n        }\n        \"S(ES_MINS)\": {\n            \"key\": \"ES_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¬ │   │   │   │   │   │   │   │   │   │   │ \\ │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ @ │   │   │   │   │   │   │   │   │   │   │ ~ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ ^ │ ` │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(ES_PIPE)\": {\n            \"key\": \"ES_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(ES_QUOT)\": {\n            \"key\": \"ES_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(ES_Q)\": {\n            \"key\": \"ES_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(ES_PLUS)\": {\n            \"key\": \"ES_TILD\",\n            \"label\": \"~\",\n        }\n        \"ALGR(ES_LCBR)\": {\n            \"key\": \"ES_CIRC\",\n            \"label\": \"^\",\n        }\n        \"ALGR(KC_NUHS)\": {\n            \"key\": \"ES_GRV\",\n            \"label\": \"`\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_swedish_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ § │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ + │ ´ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Å │ ¨ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ö │ Ä │ ' │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"SE_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_1\": {\n            \"key\": \"SE_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"SE_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"SE_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"SE_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"SE_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"SE_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"SE_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"SE_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"SE_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"SE_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"SE_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"SE_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"SE_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"SE_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"SE_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"SE_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"SE_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"SE_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"SE_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"SE_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"SE_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"SE_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"SE_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"SE_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"SE_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"SE_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"SE_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"SE_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"SE_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"SE_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"SE_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"SE_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"SE_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"SE_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"SE_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"SE_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"SE_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"SE_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"SE_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"SE_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"SE_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"SE_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"SE_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"SE_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"SE_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"SE_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"SE_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ½ │ ! │ \" │ # │ ¤ │ % │ & │ / │ ( │ ) │ = │ ? │ ` │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ^ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ * │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(SE_SECT)\": {\n            \"key\": \"SE_HALF\",\n            \"label\": \"½\",\n        }\n        \"S(SE_1)\": {\n            \"key\": \"SE_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(SE_2)\": {\n            \"key\": \"SE_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(SE_3)\": {\n            \"key\": \"SE_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(SE_4)\": {\n            \"key\": \"SE_CURR\",\n            \"label\": \"¤\",\n        }\n        \"S(SE_5)\": {\n            \"key\": \"SE_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(SE_6)\": {\n            \"key\": \"SE_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(SE_7)\": {\n            \"key\": \"SE_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(SE_8)\": {\n            \"key\": \"SE_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(SE_9)\": {\n            \"key\": \"SE_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(SE_0)\": {\n            \"key\": \"SE_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(SE_PLUS)\": {\n            \"key\": \"SE_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(SE_ACUT)\": {\n            \"key\": \"SE_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(SE_DIAE)\": {\n            \"key\": \"SE_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(SE_QUOT)\": {\n            \"key\": \"SE_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(SE_LABK)\": {\n            \"key\": \"SE_RABK\",\n            \"label\": \">\",\n        }\n        \"S(SE_COMM)\": {\n            \"key\": \"SE_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(SE_DOT)\": {\n            \"key\": \"SE_COLN\",\n            \"label\": \":\",\n        }\n        \"S(SE_MINS)\": {\n            \"key\": \"SE_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │ @ │ £ │ $ │ € │   │ { │ [ │ ] │ } │ \\ │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ~ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ | │   │   │   │   │   │   │ µ │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(SE_2)\": {\n            \"key\": \"SE_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(SE_3)\": {\n            \"key\": \"SE_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(SE_4)\": {\n            \"key\": \"SE_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(SE_5)\": {\n            \"key\": \"SE_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(SE_7)\": {\n            \"key\": \"SE_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(SE_8)\": {\n            \"key\": \"SE_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(SE_9)\": {\n            \"key\": \"SE_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(SE_0)\": {\n            \"key\": \"SE_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(SE_PLUS)\": {\n            \"key\": \"SE_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(SE_DIAE)\": {\n            \"key\": \"SE_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(SE_LABK)\": {\n            \"key\": \"SE_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(SE_M)\": {\n            \"key\": \"SE_MICR\",\n            \"label\": \"µ\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_swedish_mac_ansi_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ < │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ + │ ´ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Å │ ¨ │ ' │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ö │ Ä │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"SE_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_1\": {\n            \"key\": \"SE_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"SE_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"SE_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"SE_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"SE_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"SE_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"SE_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"SE_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"SE_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"SE_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"SE_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"SE_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"SE_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"SE_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"SE_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"SE_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"SE_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"SE_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"SE_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"SE_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"SE_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"SE_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"SE_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"SE_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"SE_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_A\": {\n            \"key\": \"SE_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"SE_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"SE_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"SE_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"SE_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"SE_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"SE_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"SE_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"SE_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"SE_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"SE_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"KC_Z\": {\n            \"key\": \"SE_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"SE_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"SE_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"SE_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"SE_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"SE_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"SE_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"SE_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"SE_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"SE_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ > │ ! │ \" │ # │ € │ % │ & │ / │ ( │ ) │ = │ ? │ ` │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ^ │ * │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │   │   │   │   │   │   │   │   │   │   │   │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │   │   │   │   │   │   │   │ ; │ : │ _ │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(SE_LABK)\": {\n            \"key\": \"SE_RABK\",\n            \"label\": \">\",\n        }\n        \"S(SE_1)\": {\n            \"key\": \"SE_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(SE_2)\": {\n            \"key\": \"SE_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(SE_3)\": {\n            \"key\": \"SE_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(SE_4)\": {\n            \"key\": \"SE_EURO\",\n            \"label\": \"€\",\n        }\n        \"S(SE_5)\": {\n            \"key\": \"SE_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(SE_6)\": {\n            \"key\": \"SE_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(SE_7)\": {\n            \"key\": \"SE_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(SE_8)\": {\n            \"key\": \"SE_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(SE_9)\": {\n            \"key\": \"SE_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(SE_0)\": {\n            \"key\": \"SE_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(SE_PLUS)\": {\n            \"key\": \"SE_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(SE_ACUT)\": {\n            \"key\": \"SE_GRV\",\n            \"label\": \"`\",\n        }\n        \"S(SE_DIAE)\": {\n            \"key\": \"SE_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(SE_QUOT)\": {\n            \"key\": \"SE_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(SE_COMM)\": {\n            \"key\": \"SE_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(SE_DOT)\": {\n            \"key\": \"SE_COLN\",\n            \"label\": \":\",\n        }\n        \"S(SE_MINS)\": {\n            \"key\": \"SE_UNDS\",\n            \"label\": \"_\",\n        }\n/* Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ≤ │ © │ ™ │ £ │ $ │ ∞ │ § │ | │ [ │ ] │ ≈ │ ± │   │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ • │ Ω │ É │ ® │ † │ µ │ Ü │ ı │ Œ │ π │ ˙ │ ~ │ @ │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │  │ ß │ ∂ │ ƒ │ ¸ │ ˛ │ √ │ ª │ ﬁ │ Ø │ Æ │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │ ÷ │   │ Ç │ ‹ │ › │ ‘ │ ’ │ ‚ │ … │ – │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n * \n */\n        \"A(SE_LABK)\": {\n            \"key\": \"SE_LTEQ\",\n            \"label\": \"≤\",\n        }\n        \"A(SE_1)\": {\n            \"key\": \"SE_COPY\",\n            \"label\": \"©\",\n        }\n        \"A(SE_2)\": {\n            \"key\": \"SE_TM\",\n            \"label\": \"™\",\n        }\n        \"A(SE_3)\": {\n            \"key\": \"SE_PND\",\n            \"label\": \"£\",\n        }\n        \"A(SE_4)\": {\n            \"key\": \"SE_DLR\",\n            \"label\": \"$\",\n        }\n        \"A(SE_5)\": {\n            \"key\": \"SE_INFN\",\n            \"label\": \"∞\",\n        }\n        \"A(SE_6)\": {\n            \"key\": \"SE_SECT\",\n            \"label\": \"§\",\n        }\n        \"A(SE_7)\": {\n            \"key\": \"SE_PIPE\",\n            \"label\": \"|\",\n        }\n        \"A(SE_8)\": {\n            \"key\": \"SE_LBRC\",\n            \"label\": \"[\",\n        }\n        \"A(SE_9)\": {\n            \"key\": \"SE_RBRC\",\n            \"label\": \"]\",\n        }\n        \"A(SE_0)\": {\n            \"key\": \"SE_AEQL\",\n            \"label\": \"≈\",\n        }\n        \"A(SE_PLUS)\": {\n            \"key\": \"SE_PLMN\",\n            \"label\": \"±\",\n        }\n        \"A(SE_Q)\": {\n            \"key\": \"SE_BULT\",\n            \"label\": \"•\",\n        }\n        \"A(SE_W)\": {\n            \"key\": \"SE_OMEG\",\n            \"label\": \"Ω\",\n        }\n        \"A(SE_E)\": {\n            \"key\": \"SE_EACU\",\n            \"label\": \"É\",\n        }\n        \"A(SE_R)\": {\n            \"key\": \"SE_REGD\",\n            \"label\": \"®\",\n        }\n        \"A(SE_T)\": {\n            \"key\": \"SE_DAGG\",\n            \"label\": \"†\",\n        }\n        \"A(SE_Y)\": {\n            \"key\": \"SE_MICR\",\n            \"label\": \"µ\",\n        }\n        \"A(SE_U)\": {\n            \"key\": \"SE_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"A(SE_I)\": {\n            \"key\": \"SE_DLSI\",\n            \"label\": \"ı\",\n        }\n        \"A(SE_O)\": {\n            \"key\": \"SE_OE\",\n            \"label\": \"Œ\",\n        }\n        \"A(SE_P)\": {\n            \"key\": \"SE_PI\",\n            \"label\": \"π\",\n        }\n        \"A(SE_ARNG)\": {\n            \"key\": \"SE_DOTA\",\n            \"label\": \"˙\",\n        }\n        \"A(SE_DIAE)\": {\n            \"key\": \"SE_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"A(SE_QUOT)\": {\n            \"key\": \"SE_AT\",\n            \"label\": \"@\",\n        }\n        \"A(SE_A)\": {\n            \"key\": \"SE_APPL\",\n            \"label\": \" (Apple logo)\",\n        }\n        \"A(SE_S)\": {\n            \"key\": \"SE_SS\",\n            \"label\": \"ß\",\n        }\n        \"A(SE_D)\": {\n            \"key\": \"SE_PDIF\",\n            \"label\": \"∂\",\n        }\n        \"A(SE_F)\": {\n            \"key\": \"SE_FHK\",\n            \"label\": \"ƒ\",\n        }\n        \"A(SE_G)\": {\n            \"key\": \"SE_CEDL\",\n            \"label\": \"¸\",\n        }\n        \"A(SE_H)\": {\n            \"key\": \"SE_OGON\",\n            \"label\": \"˛\",\n        }\n        \"A(SE_J)\": {\n            \"key\": \"SE_SQRT\",\n            \"label\": \"√\",\n        }\n        \"A(SE_K)\": {\n            \"key\": \"SE_FORD\",\n            \"label\": \"ª\",\n        }\n        \"A(SE_L)\": {\n            \"key\": \"SE_FI\",\n            \"label\": \"ﬁ\",\n        }\n        \"A(SE_ODIA)\": {\n            \"key\": \"SE_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"A(SE_ADIA)\": {\n            \"key\": \"SE_AE\",\n            \"label\": \"Æ\",\n        }\n        \"A(SE_Z)\": {\n            \"key\": \"SE_DIV\",\n            \"label\": \"÷\",\n        }\n        \"A(SE_C)\": {\n            \"key\": \"SE_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"A(SE_V)\": {\n            \"key\": \"SE_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"A(SE_B)\": {\n            \"key\": \"SE_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"A(SE_N)\": {\n            \"key\": \"SE_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"A(SE_M)\": {\n            \"key\": \"SE_RSQU\",\n            \"label\": \"’\",\n        }\n        \"A(SE_COMM)\": {\n            \"key\": \"SE_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"A(SE_DOT)\": {\n            \"key\": \"SE_ELLP\",\n            \"label\": \"…\",\n        }\n        \"A(SE_MINS)\": {\n            \"key\": \"SE_NDSH\",\n            \"label\": \"–\",\n        }\n/* Shift+Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ≥ │ ¡ │   │ ¥ │ ¢ │ ‰ │ ¶ │ \\ │ { │ } │ ≠ │ ¿ │   │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ ° │ ˝ │   │   │ ‡ │ ˜ │   │ ˆ │   │ ∏ │ ˚ │   │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │ ◊ │ ∑ │ ∆ │ ∫ │ ¯ │ ˘ │ ¬ │ º │ ﬂ │   │   │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │ ⁄ │ ˇ │   │ « │ » │ “ │ ” │ „ │ · │ — │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n * \n */\n        \"S(A(SE_LABK))\": {\n            \"key\": \"SE_GTEQ\",\n            \"label\": \"≥\",\n        }\n        \"S(A(SE_1))\": {\n            \"key\": \"SE_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"S(A(SE_3))\": {\n            \"key\": \"SE_YEN\",\n            \"label\": \"¥\",\n        }\n        \"S(A(SE_4))\": {\n            \"key\": \"SE_CENT\",\n            \"label\": \"¢\",\n        }\n        \"S(A(SE_5))\": {\n            \"key\": \"SE_PERM\",\n            \"label\": \"‰\",\n        }\n        \"S(A(SE_6))\": {\n            \"key\": \"SE_PILC\",\n            \"label\": \"¶\",\n        }\n        \"S(A(SE_7))\": {\n            \"key\": \"SE_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"S(A(SE_8))\": {\n            \"key\": \"SE_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(A(SE_9))\": {\n            \"key\": \"SE_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(A(SE_0))\": {\n            \"key\": \"SE_NEQL\",\n            \"label\": \"≠\",\n        }\n        \"S(A(SE_PLUS))\": {\n            \"key\": \"SE_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"S(A(SE_Q))\": {\n            \"key\": \"SE_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(A(SE_W))\": {\n            \"key\": \"SE_DACU\",\n            \"label\": \"˝\",\n        }\n        \"S(A(SE_T))\": {\n            \"key\": \"SE_DDAG\",\n            \"label\": \"‡\",\n        }\n        \"S(A(SE_Y))\": {\n            \"key\": \"SE_STIL\",\n            \"label\": \"˜\",\n        }\n        \"S(A(SE_I))\": {\n            \"key\": \"SE_DCIR\",\n            \"label\": \"ˆ\",\n        }\n        \"S(A(SE_P))\": {\n            \"key\": \"SE_NARP\",\n            \"label\": \"∏\",\n        }\n        \"S(A(SE_ARNG))\": {\n            \"key\": \"SE_RNGA\",\n            \"label\": \"˚\",\n        }\n        \"S(A(SE_A))\": {\n            \"key\": \"SE_LOZN\",\n            \"label\": \"◊\",\n        }\n        \"S(A(SE_S))\": {\n            \"key\": \"SE_NARS\",\n            \"label\": \"∑\",\n        }\n        \"S(A(SE_D))\": {\n            \"key\": \"SE_INCR\",\n            \"label\": \"∆\",\n        }\n        \"S(A(SE_F))\": {\n            \"key\": \"SE_INTG\",\n            \"label\": \"∫\",\n        }\n        \"S(A(SE_G))\": {\n            \"key\": \"SE_MACR\",\n            \"label\": \"¯\",\n        }\n        \"S(A(SE_H))\": {\n            \"key\": \"SE_BREV\",\n            \"label\": \"˘\",\n        }\n        \"S(A(SE_J))\": {\n            \"key\": \"SE_NOT\",\n            \"label\": \"¬\",\n        }\n        \"S(A(SE_K))\": {\n            \"key\": \"SE_MORD\",\n            \"label\": \"º\",\n        }\n        \"S(A(SE_L))\": {\n            \"key\": \"SE_FL\",\n            \"label\": \"ﬂ\",\n        }\n        \"S(A(SE_Z))\": {\n            \"key\": \"SE_FRSL\",\n            \"label\": \"⁄\",\n        }\n        \"S(A(SE_X))\": {\n            \"key\": \"SE_CARN\",\n            \"label\": \"ˇ\",\n        }\n        \"S(A(SE_V))\": {\n            \"key\": \"SE_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"S(A(SE_B))\": {\n            \"key\": \"SE_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(A(SE_N))\": {\n            \"key\": \"SE_LDQU\",\n            \"label\": \"“\",\n        }\n        \"S(A(SE_M))\": {\n            \"key\": \"SE_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(A(SE_COMM))\": {\n            \"key\": \"SE_DLQU\",\n            \"label\": \"„\",\n        }\n        \"S(A(SE_DOT))\": {\n            \"key\": \"SE_MDDT\",\n            \"label\": \"·\",\n        }\n        \"S(A(SE_MINS))\": {\n            \"key\": \"SE_MDSH\",\n            \"label\": \"—\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_swedish_mac_iso_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ § │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ + │ ´ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Å │ ¨ │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ö │ Ä │ ' │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"SE_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_1\": {\n            \"key\": \"SE_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"SE_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"SE_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"SE_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"SE_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"SE_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"SE_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"SE_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"SE_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"SE_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"SE_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"SE_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"SE_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"SE_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"SE_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"SE_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"SE_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"SE_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"SE_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"SE_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"SE_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"SE_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"SE_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"SE_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"SE_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"SE_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"SE_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"SE_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"SE_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"SE_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"SE_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"SE_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"SE_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"SE_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"SE_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"SE_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"SE_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"SE_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"SE_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"SE_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"SE_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"SE_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"SE_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"SE_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"SE_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"SE_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"SE_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ° │ ! │ \" │ # │ € │ % │ & │ / │ ( │ ) │ = │ ? │ ` │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ^ │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ * │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(SE_SECT)\": {\n            \"key\": \"SE_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(SE_1)\": {\n            \"key\": \"SE_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(SE_2)\": {\n            \"key\": \"SE_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(SE_3)\": {\n            \"key\": \"SE_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(SE_4)\": {\n            \"key\": \"SE_EURO\",\n            \"label\": \"€\",\n        }\n        \"S(SE_5)\": {\n            \"key\": \"SE_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(SE_6)\": {\n            \"key\": \"SE_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(SE_7)\": {\n            \"key\": \"SE_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(SE_8)\": {\n            \"key\": \"SE_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(SE_9)\": {\n            \"key\": \"SE_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(SE_0)\": {\n            \"key\": \"SE_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(SE_PLUS)\": {\n            \"key\": \"SE_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(SE_ACUT)\": {\n            \"key\": \"SE_GRV\",\n            \"label\": \"`\",\n        }\n        \"S(SE_DIAE)\": {\n            \"key\": \"SE_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(SE_QUOT)\": {\n            \"key\": \"SE_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(SE_LABK)\": {\n            \"key\": \"SE_RABK\",\n            \"label\": \">\",\n        }\n        \"S(SE_COMM)\": {\n            \"key\": \"SE_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(SE_DOT)\": {\n            \"key\": \"SE_COLN\",\n            \"label\": \":\",\n        }\n        \"S(SE_MINS)\": {\n            \"key\": \"SE_UNDS\",\n            \"label\": \"_\",\n        }\n/* Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ¶ │ © │ ™ │ £ │ $ │ ∞ │   │ | │ [ │ ] │ ≈ │ ± │   │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ • │ Ω │ É │ ® │ † │ µ │ Ü │ ı │ Œ │ π │ ˙ │ ~ │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │  │ ß │ ∂ │ ƒ │ ¸ │ ˛ │ √ │ ª │ ﬁ │ Ø │ Æ │ @ │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≤ │ ÷ │   │ Ç │ ‹ │ › │ ‘ │ ’ │ ‚ │ … │ – │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"A(SE_SECT)\": {\n            \"key\": \"SE_PILC\",\n            \"label\": \"¶\",\n        }\n        \"A(SE_1)\": {\n            \"key\": \"SE_COPY\",\n            \"label\": \"©\",\n        }\n        \"A(SE_2)\": {\n            \"key\": \"SE_TM\",\n            \"label\": \"™\",\n        }\n        \"A(SE_3)\": {\n            \"key\": \"SE_PND\",\n            \"label\": \"£\",\n        }\n        \"A(SE_4)\": {\n            \"key\": \"SE_DLR\",\n            \"label\": \"$\",\n        }\n        \"A(SE_5)\": {\n            \"key\": \"SE_INFN\",\n            \"label\": \"∞\",\n        }\n        \"A(SE_7)\": {\n            \"key\": \"SE_PIPE\",\n            \"label\": \"|\",\n        }\n        \"A(SE_8)\": {\n            \"key\": \"SE_LBRC\",\n            \"label\": \"[\",\n        }\n        \"A(SE_9)\": {\n            \"key\": \"SE_RBRC\",\n            \"label\": \"]\",\n        }\n        \"A(SE_0)\": {\n            \"key\": \"SE_AEQL\",\n            \"label\": \"≈\",\n        }\n        \"A(SE_PLUS)\": {\n            \"key\": \"SE_PLMN\",\n            \"label\": \"±\",\n        }\n        \"A(SE_Q)\": {\n            \"key\": \"SE_BULT\",\n            \"label\": \"•\",\n        }\n        \"A(SE_W)\": {\n            \"key\": \"SE_OMEG\",\n            \"label\": \"Ω\",\n        }\n        \"A(SE_E)\": {\n            \"key\": \"SE_EACU\",\n            \"label\": \"É\",\n        }\n        \"A(SE_R)\": {\n            \"key\": \"SE_REGD\",\n            \"label\": \"®\",\n        }\n        \"A(SE_T)\": {\n            \"key\": \"SE_DAGG\",\n            \"label\": \"†\",\n        }\n        \"A(SE_Y)\": {\n            \"key\": \"SE_MICR\",\n            \"label\": \"µ\",\n        }\n        \"A(SE_U)\": {\n            \"key\": \"SE_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"A(SE_I)\": {\n            \"key\": \"SE_DLSI\",\n            \"label\": \"ı\",\n        }\n        \"A(SE_O)\": {\n            \"key\": \"SE_OE\",\n            \"label\": \"Œ\",\n        }\n        \"A(SE_P)\": {\n            \"key\": \"SE_PI\",\n            \"label\": \"π\",\n        }\n        \"A(SE_ARNG)\": {\n            \"key\": \"SE_DOTA\",\n            \"label\": \"˙\",\n        }\n        \"A(SE_DIAE)\": {\n            \"key\": \"SE_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"A(SE_A)\": {\n            \"key\": \"SE_APPL\",\n            \"label\": \" (Apple logo)\",\n        }\n        \"A(SE_S)\": {\n            \"key\": \"SE_SS\",\n            \"label\": \"ß\",\n        }\n        \"A(SE_D)\": {\n            \"key\": \"SE_PDIF\",\n            \"label\": \"∂\",\n        }\n        \"A(SE_F)\": {\n            \"key\": \"SE_FHK\",\n            \"label\": \"ƒ\",\n        }\n        \"A(SE_G)\": {\n            \"key\": \"SE_CEDL\",\n            \"label\": \"¸\",\n        }\n        \"A(SE_H)\": {\n            \"key\": \"SE_OGON\",\n            \"label\": \"˛\",\n        }\n        \"A(SE_J)\": {\n            \"key\": \"SE_SQRT\",\n            \"label\": \"√\",\n        }\n        \"A(SE_K)\": {\n            \"key\": \"SE_FORD\",\n            \"label\": \"ª\",\n        }\n        \"A(SE_L)\": {\n            \"key\": \"SE_FI\",\n            \"label\": \"ﬁ\",\n        }\n        \"A(SE_ODIA)\": {\n            \"key\": \"SE_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"A(SE_ADIA)\": {\n            \"key\": \"SE_AE\",\n            \"label\": \"Æ\",\n        }\n        \"A(SE_QUOT)\": {\n            \"key\": \"SE_AT\",\n            \"label\": \"@\",\n        }\n        \"A(SE_LABK)\": {\n            \"key\": \"SE_LTEQ\",\n            \"label\": \"≤\",\n        }\n        \"A(SE_Z)\": {\n            \"key\": \"SE_DIV\",\n            \"label\": \"÷\",\n        }\n        \"A(SE_C)\": {\n            \"key\": \"SE_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"A(SE_V)\": {\n            \"key\": \"SE_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"A(SE_B)\": {\n            \"key\": \"SE_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"A(SE_N)\": {\n            \"key\": \"SE_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"A(SE_M)\": {\n            \"key\": \"SE_RSQU\",\n            \"label\": \"’\",\n        }\n        \"A(SE_COMM)\": {\n            \"key\": \"SE_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"A(SE_DOT)\": {\n            \"key\": \"SE_ELLP\",\n            \"label\": \"…\",\n        }\n        \"A(SE_MINS)\": {\n            \"key\": \"SE_NDSH\",\n            \"label\": \"–\",\n        }\n/* Shift+Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │   │ ¡ │ ” │ ¥ │ ¢ │ ‰ │   │ \\ │ { │ } │ ≠ │ ¿ │   │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │ ˝ │   │   │ ‡ │ ˜ │   │ ˆ │   │ ∏ │ ˚ │   │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ ◊ │ ∑ │ ∆ │ ∫ │ ¯ │ ˘ │ ¬ │ º │ ﬂ │   │   │   │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≥ │ ⁄ │ ˇ │   │ « │ » │ “ │ ” │ „ │ · │ — │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(A(SE_1))\": {\n            \"key\": \"SE_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"S(A(SE_3))\": {\n            \"key\": \"SE_YEN\",\n            \"label\": \"¥\",\n        }\n        \"S(A(SE_4))\": {\n            \"key\": \"SE_CENT\",\n            \"label\": \"¢\",\n        }\n        \"S(A(SE_5))\": {\n            \"key\": \"SE_PERM\",\n            \"label\": \"‰\",\n        }\n        \"S(A(SE_7))\": {\n            \"key\": \"SE_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"S(A(SE_8))\": {\n            \"key\": \"SE_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(A(SE_9))\": {\n            \"key\": \"SE_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(A(SE_0))\": {\n            \"key\": \"SE_NEQL\",\n            \"label\": \"≠\",\n        }\n        \"S(A(SE_PLUS))\": {\n            \"key\": \"SE_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"S(A(SE_W))\": {\n            \"key\": \"SE_DACU\",\n            \"label\": \"˝\",\n        }\n        \"S(A(SE_T))\": {\n            \"key\": \"SE_DDAG\",\n            \"label\": \"‡\",\n        }\n        \"S(A(SE_Y))\": {\n            \"key\": \"SE_STIL\",\n            \"label\": \"˜\",\n        }\n        \"S(A(SE_I))\": {\n            \"key\": \"SE_DCIR\",\n            \"label\": \"ˆ\",\n        }\n        \"S(A(SE_P))\": {\n            \"key\": \"SE_NARP\",\n            \"label\": \"∏\",\n        }\n        \"S(A(SE_ARNG))\": {\n            \"key\": \"SE_RNGA\",\n            \"label\": \"˚\",\n        }\n        \"S(A(SE_A))\": {\n            \"key\": \"SE_LOZN\",\n            \"label\": \"◊\",\n        }\n        \"S(A(SE_S))\": {\n            \"key\": \"SE_NARS\",\n            \"label\": \"∑\",\n        }\n        \"S(A(SE_D))\": {\n            \"key\": \"SE_INCR\",\n            \"label\": \"∆\",\n        }\n        \"S(A(SE_F))\": {\n            \"key\": \"SE_INTG\",\n            \"label\": \"∫\",\n        }\n        \"S(A(SE_G))\": {\n            \"key\": \"SE_MACR\",\n            \"label\": \"¯\",\n        }\n        \"S(A(SE_H))\": {\n            \"key\": \"SE_BREV\",\n            \"label\": \"˘\",\n        }\n        \"S(A(SE_J))\": {\n            \"key\": \"SE_NOT\",\n            \"label\": \"¬\",\n        }\n        \"S(A(SE_K))\": {\n            \"key\": \"SE_MORD\",\n            \"label\": \"º\",\n        }\n        \"S(A(SE_L))\": {\n            \"key\": \"SE_FL\",\n            \"label\": \"ﬂ\",\n        }\n        \"S(A(SE_LABK))\": {\n            \"key\": \"SE_GTEQ\",\n            \"label\": \"≥\",\n        }\n        \"S(A(SE_Z))\": {\n            \"key\": \"SE_FRSL\",\n            \"label\": \"⁄\",\n        }\n        \"S(A(SE_X))\": {\n            \"key\": \"SE_CARN\",\n            \"label\": \"ˇ\",\n        }\n        \"S(A(SE_V))\": {\n            \"key\": \"SE_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"S(A(SE_B))\": {\n            \"key\": \"SE_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(A(SE_N))\": {\n            \"key\": \"SE_LDQU\",\n            \"label\": \"“\",\n        }\n        \"S(A(SE_M))\": {\n            \"key\": \"SE_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(A(SE_COMM))\": {\n            \"key\": \"SE_DLQU\",\n            \"label\": \"„\",\n        }\n        \"S(A(SE_DOT))\": {\n            \"key\": \"SE_MDDT\",\n            \"label\": \"·\",\n        }\n        \"S(A(SE_MINS))\": {\n            \"key\": \"SE_MDSH\",\n            \"label\": \"—\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_swedish_pro_mac_ansi_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ < │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ + │ ´ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Å │ ¨ │ ' │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ö │ Ä │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"SE_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_1\": {\n            \"key\": \"SE_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"SE_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"SE_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"SE_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"SE_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"SE_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"SE_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"SE_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"SE_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"SE_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"SE_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"SE_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"SE_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"SE_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"SE_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"SE_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"SE_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"SE_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"SE_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"SE_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"SE_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"SE_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"SE_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"SE_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"SE_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_A\": {\n            \"key\": \"SE_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"SE_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"SE_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"SE_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"SE_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"SE_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"SE_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"SE_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"SE_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"SE_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"SE_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"KC_Z\": {\n            \"key\": \"SE_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"SE_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"SE_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"SE_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"SE_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"SE_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"SE_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"SE_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"SE_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"SE_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ > │ ! │ \" │ # │ € │ % │ & │ / │ ( │ ) │ = │ ? │ ` │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ^ │ * │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │   │   │   │   │   │   │   │   │   │   │   │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │   │   │   │   │   │   │   │ ; │ : │ _ │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(SE_LABK)\": {\n            \"key\": \"SE_RABK\",\n            \"label\": \">\",\n        }\n        \"S(SE_1)\": {\n            \"key\": \"SE_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(SE_2)\": {\n            \"key\": \"SE_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(SE_3)\": {\n            \"key\": \"SE_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(SE_4)\": {\n            \"key\": \"SE_EURO\",\n            \"label\": \"€\",\n        }\n        \"S(SE_5)\": {\n            \"key\": \"SE_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(SE_6)\": {\n            \"key\": \"SE_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(SE_7)\": {\n            \"key\": \"SE_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(SE_8)\": {\n            \"key\": \"SE_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(SE_9)\": {\n            \"key\": \"SE_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(SE_0)\": {\n            \"key\": \"SE_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(SE_PLUS)\": {\n            \"key\": \"SE_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(SE_ACUT)\": {\n            \"key\": \"SE_GRV\",\n            \"label\": \"`\",\n        }\n        \"S(SE_DIAE)\": {\n            \"key\": \"SE_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(SE_QUOT)\": {\n            \"key\": \"SE_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(SE_COMM)\": {\n            \"key\": \"SE_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(SE_DOT)\": {\n            \"key\": \"SE_COLN\",\n            \"label\": \":\",\n        }\n        \"S(SE_MINS)\": {\n            \"key\": \"SE_UNDS\",\n            \"label\": \"_\",\n        }\n/* Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ≤ │ © │ @ │ £ │ $ │ ∞ │ § │ | │ [ │ ] │ ≈ │ ± │   │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ • │ Ω │ É │ ® │ † │ µ │ Ü │ ı │ Œ │ π │ ˙ │ ~ │ ™ │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │  │ ß │ ∂ │ ƒ │ ¸ │ ˛ │ √ │ ª │ ﬁ │ Ø │ Æ │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │ ÷ │   │ Ç │ ‹ │ › │ ‘ │ ’ │ ‚ │ … │ – │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n * \n */\n        \"A(SE_LABK)\": {\n            \"key\": \"SE_LTEQ\",\n            \"label\": \"≤\",\n        }\n        \"A(SE_1)\": {\n            \"key\": \"SE_COPY\",\n            \"label\": \"©\",\n        }\n        \"A(SE_2)\": {\n            \"key\": \"SE_AT\",\n            \"label\": \"@\",\n        }\n        \"A(SE_3)\": {\n            \"key\": \"SE_PND\",\n            \"label\": \"£\",\n        }\n        \"A(SE_4)\": {\n            \"key\": \"SE_DLR\",\n            \"label\": \"$\",\n        }\n        \"A(SE_5)\": {\n            \"key\": \"SE_INFN\",\n            \"label\": \"∞\",\n        }\n        \"A(SE_6)\": {\n            \"key\": \"SE_SECT\",\n            \"label\": \"§\",\n        }\n        \"A(SE_7)\": {\n            \"key\": \"SE_PIPE\",\n            \"label\": \"|\",\n        }\n        \"A(SE_8)\": {\n            \"key\": \"SE_LBRC\",\n            \"label\": \"[\",\n        }\n        \"A(SE_9)\": {\n            \"key\": \"SE_RBRC\",\n            \"label\": \"]\",\n        }\n        \"A(SE_0)\": {\n            \"key\": \"SE_AEQL\",\n            \"label\": \"≈\",\n        }\n        \"A(SE_PLUS)\": {\n            \"key\": \"SE_PLMN\",\n            \"label\": \"±\",\n        }\n        \"A(SE_Q)\": {\n            \"key\": \"SE_BULT\",\n            \"label\": \"•\",\n        }\n        \"A(SE_W)\": {\n            \"key\": \"SE_OMEG\",\n            \"label\": \"Ω\",\n        }\n        \"A(SE_E)\": {\n            \"key\": \"SE_EACU\",\n            \"label\": \"É\",\n        }\n        \"A(SE_R)\": {\n            \"key\": \"SE_REGD\",\n            \"label\": \"®\",\n        }\n        \"A(SE_T)\": {\n            \"key\": \"SE_DAGG\",\n            \"label\": \"†\",\n        }\n        \"A(SE_Y)\": {\n            \"key\": \"SE_MICR\",\n            \"label\": \"µ\",\n        }\n        \"A(SE_U)\": {\n            \"key\": \"SE_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"A(SE_I)\": {\n            \"key\": \"SE_DLSI\",\n            \"label\": \"ı\",\n        }\n        \"A(SE_O)\": {\n            \"key\": \"SE_OE\",\n            \"label\": \"Œ\",\n        }\n        \"A(SE_P)\": {\n            \"key\": \"SE_PI\",\n            \"label\": \"π\",\n        }\n        \"A(SE_ARNG)\": {\n            \"key\": \"SE_DOTA\",\n            \"label\": \"˙\",\n        }\n        \"A(SE_DIAE)\": {\n            \"key\": \"SE_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"A(SE_QUOT)\": {\n            \"key\": \"SE_TM\",\n            \"label\": \"™\",\n        }\n        \"A(SE_A)\": {\n            \"key\": \"SE_APPL\",\n            \"label\": \" (Apple logo)\",\n        }\n        \"A(SE_S)\": {\n            \"key\": \"SE_SS\",\n            \"label\": \"ß\",\n        }\n        \"A(SE_D)\": {\n            \"key\": \"SE_PDIF\",\n            \"label\": \"∂\",\n        }\n        \"A(SE_F)\": {\n            \"key\": \"SE_FHK\",\n            \"label\": \"ƒ\",\n        }\n        \"A(SE_G)\": {\n            \"key\": \"SE_CEDL\",\n            \"label\": \"¸\",\n        }\n        \"A(SE_H)\": {\n            \"key\": \"SE_OGON\",\n            \"label\": \"˛\",\n        }\n        \"A(SE_J)\": {\n            \"key\": \"SE_SQRT\",\n            \"label\": \"√\",\n        }\n        \"A(SE_K)\": {\n            \"key\": \"SE_FORD\",\n            \"label\": \"ª\",\n        }\n        \"A(SE_L)\": {\n            \"key\": \"SE_FI\",\n            \"label\": \"ﬁ\",\n        }\n        \"A(SE_ODIA)\": {\n            \"key\": \"SE_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"A(SE_ADIA)\": {\n            \"key\": \"SE_AE\",\n            \"label\": \"Æ\",\n        }\n        \"A(SE_Z)\": {\n            \"key\": \"SE_DIV\",\n            \"label\": \"÷\",\n        }\n        \"A(SE_C)\": {\n            \"key\": \"SE_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"A(SE_V)\": {\n            \"key\": \"SE_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"A(SE_B)\": {\n            \"key\": \"SE_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"A(SE_N)\": {\n            \"key\": \"SE_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"A(SE_M)\": {\n            \"key\": \"SE_RSQU\",\n            \"label\": \"’\",\n        }\n        \"A(SE_COMM)\": {\n            \"key\": \"SE_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"A(SE_DOT)\": {\n            \"key\": \"SE_ELLP\",\n            \"label\": \"…\",\n        }\n        \"A(SE_MINS)\": {\n            \"key\": \"SE_NDSH\",\n            \"label\": \"–\",\n        }\n/* Shift+Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ≥ │ ¡ │   │ ¥ │ ¢ │ ‰ │ ¶ │ \\ │ { │ } │ ≠ │ ¿ │   │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ ° │ ˝ │   │   │ ‡ │ ˜ │   │ ˆ │   │ ∏ │ ˚ │   │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴───┤\n * │      │ ◊ │ ∑ │ ∆ │ ∫ │ ¯ │ ˘ │ ¬ │ º │ ﬂ │   │   │      │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┤\n * │        │ ⁄ │ ˇ │   │ « │ » │ “ │ ” │ „ │ · │ — │        │\n * ├─────┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n * \n */\n        \"S(A(SE_LABK))\": {\n            \"key\": \"SE_GTEQ\",\n            \"label\": \"≥\",\n        }\n        \"S(A(SE_1))\": {\n            \"key\": \"SE_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"S(A(SE_3))\": {\n            \"key\": \"SE_YEN\",\n            \"label\": \"¥\",\n        }\n        \"S(A(SE_4))\": {\n            \"key\": \"SE_CENT\",\n            \"label\": \"¢\",\n        }\n        \"S(A(SE_5))\": {\n            \"key\": \"SE_PERM\",\n            \"label\": \"‰\",\n        }\n        \"S(A(SE_6))\": {\n            \"key\": \"SE_PILC\",\n            \"label\": \"¶\",\n        }\n        \"S(A(SE_7))\": {\n            \"key\": \"SE_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"S(A(SE_8))\": {\n            \"key\": \"SE_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(A(SE_9))\": {\n            \"key\": \"SE_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(A(SE_0))\": {\n            \"key\": \"SE_NEQL\",\n            \"label\": \"≠\",\n        }\n        \"S(A(SE_PLUS))\": {\n            \"key\": \"SE_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"S(A(SE_Q))\": {\n            \"key\": \"SE_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(A(SE_W))\": {\n            \"key\": \"SE_DACU\",\n            \"label\": \"˝\",\n        }\n        \"S(A(SE_T))\": {\n            \"key\": \"SE_DDAG\",\n            \"label\": \"‡\",\n        }\n        \"S(A(SE_Y))\": {\n            \"key\": \"SE_STIL\",\n            \"label\": \"˜\",\n        }\n        \"S(A(SE_I))\": {\n            \"key\": \"SE_DCIR\",\n            \"label\": \"ˆ\",\n        }\n        \"S(A(SE_P))\": {\n            \"key\": \"SE_NARP\",\n            \"label\": \"∏\",\n        }\n        \"S(A(SE_ARNG))\": {\n            \"key\": \"SE_RNGA\",\n            \"label\": \"˚\",\n        }\n        \"S(A(SE_A))\": {\n            \"key\": \"SE_LOZN\",\n            \"label\": \"◊\",\n        }\n        \"S(A(SE_S))\": {\n            \"key\": \"SE_NARS\",\n            \"label\": \"∑\",\n        }\n        \"S(A(SE_D))\": {\n            \"key\": \"SE_INCR\",\n            \"label\": \"∆\",\n        }\n        \"S(A(SE_F))\": {\n            \"key\": \"SE_INTG\",\n            \"label\": \"∫\",\n        }\n        \"S(A(SE_G))\": {\n            \"key\": \"SE_MACR\",\n            \"label\": \"¯\",\n        }\n        \"S(A(SE_H))\": {\n            \"key\": \"SE_BREV\",\n            \"label\": \"˘\",\n        }\n        \"S(A(SE_J))\": {\n            \"key\": \"SE_NOT\",\n            \"label\": \"¬\",\n        }\n        \"S(A(SE_K))\": {\n            \"key\": \"SE_MORD\",\n            \"label\": \"º\",\n        }\n        \"S(A(SE_L))\": {\n            \"key\": \"SE_FL\",\n            \"label\": \"ﬂ\",\n        }\n        \"S(A(SE_Z))\": {\n            \"key\": \"SE_FRSL\",\n            \"label\": \"⁄\",\n        }\n        \"S(A(SE_X))\": {\n            \"key\": \"SE_CARN\",\n            \"label\": \"ˇ\",\n        }\n        \"S(A(SE_V))\": {\n            \"key\": \"SE_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"S(A(SE_B))\": {\n            \"key\": \"SE_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(A(SE_N))\": {\n            \"key\": \"SE_LDQU\",\n            \"label\": \"“\",\n        }\n        \"S(A(SE_M))\": {\n            \"key\": \"SE_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(A(SE_COMM))\": {\n            \"key\": \"SE_DLQU\",\n            \"label\": \"„\",\n        }\n        \"S(A(SE_DOT))\": {\n            \"key\": \"SE_MDDT\",\n            \"label\": \"·\",\n        }\n        \"S(A(SE_MINS))\": {\n            \"key\": \"SE_MDSH\",\n            \"label\": \"—\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_swedish_pro_mac_iso_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ § │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ + │ ´ │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Å │ ¨ │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ö │ Ä │ ' │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ - │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"SE_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_1\": {\n            \"key\": \"SE_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"SE_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"SE_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"SE_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"SE_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"SE_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"SE_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"SE_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"SE_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"SE_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"SE_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"SE_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"SE_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"SE_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"SE_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"SE_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"SE_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"SE_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"SE_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"SE_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"SE_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"SE_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"SE_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"SE_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"SE_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"SE_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"SE_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"SE_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"SE_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"SE_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"SE_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"SE_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"SE_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"SE_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"SE_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"SE_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"SE_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"SE_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"SE_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"SE_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"SE_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"SE_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"SE_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"SE_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"SE_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"SE_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"SE_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ° │ ! │ \" │ # │ € │ % │ & │ / │ ( │ ) │ = │ ? │ ` │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │ ^ │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ * │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(SE_SECT)\": {\n            \"key\": \"SE_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(SE_1)\": {\n            \"key\": \"SE_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(SE_2)\": {\n            \"key\": \"SE_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(SE_3)\": {\n            \"key\": \"SE_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(SE_4)\": {\n            \"key\": \"SE_EURO\",\n            \"label\": \"€\",\n        }\n        \"S(SE_5)\": {\n            \"key\": \"SE_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(SE_6)\": {\n            \"key\": \"SE_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(SE_7)\": {\n            \"key\": \"SE_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(SE_8)\": {\n            \"key\": \"SE_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(SE_9)\": {\n            \"key\": \"SE_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(SE_0)\": {\n            \"key\": \"SE_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(SE_PLUS)\": {\n            \"key\": \"SE_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(SE_ACUT)\": {\n            \"key\": \"SE_GRV\",\n            \"label\": \"`\",\n        }\n        \"S(SE_DIAE)\": {\n            \"key\": \"SE_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(SE_QUOT)\": {\n            \"key\": \"SE_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(SE_LABK)\": {\n            \"key\": \"SE_RABK\",\n            \"label\": \">\",\n        }\n        \"S(SE_COMM)\": {\n            \"key\": \"SE_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(SE_DOT)\": {\n            \"key\": \"SE_COLN\",\n            \"label\": \":\",\n        }\n        \"S(SE_MINS)\": {\n            \"key\": \"SE_UNDS\",\n            \"label\": \"_\",\n        }\n/* Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │ ¶ │ © │ @ │ £ │ $ │ ∞ │   │ | │ [ │ ] │ ≈ │ ± │   │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │ • │ Ω │ É │ ® │ † │ µ │ Ü │ ı │ Œ │ π │ ˙ │ ~ │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │  │ ß │ ∂ │ ƒ │ ¸ │ ˛ │ √ │ ª │ ﬁ │ Ø │ Æ │ ™ │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≤ │ ÷ │   │ Ç │ ‹ │ › │ ‘ │ ’ │ ‚ │ … │ – │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"A(SE_SECT)\": {\n            \"key\": \"SE_PILC\",\n            \"label\": \"¶\",\n        }\n        \"A(SE_1)\": {\n            \"key\": \"SE_COPY\",\n            \"label\": \"©\",\n        }\n        \"A(SE_2)\": {\n            \"key\": \"SE_AT\",\n            \"label\": \"@\",\n        }\n        \"A(SE_3)\": {\n            \"key\": \"SE_PND\",\n            \"label\": \"£\",\n        }\n        \"A(SE_4)\": {\n            \"key\": \"SE_DLR\",\n            \"label\": \"$\",\n        }\n        \"A(SE_5)\": {\n            \"key\": \"SE_INFN\",\n            \"label\": \"∞\",\n        }\n        \"A(SE_7)\": {\n            \"key\": \"SE_PIPE\",\n            \"label\": \"|\",\n        }\n        \"A(SE_8)\": {\n            \"key\": \"SE_LBRC\",\n            \"label\": \"[\",\n        }\n        \"A(SE_9)\": {\n            \"key\": \"SE_RBRC\",\n            \"label\": \"]\",\n        }\n        \"A(SE_0)\": {\n            \"key\": \"SE_AEQL\",\n            \"label\": \"≈\",\n        }\n        \"A(SE_PLUS)\": {\n            \"key\": \"SE_PLMN\",\n            \"label\": \"±\",\n        }\n        \"A(SE_Q)\": {\n            \"key\": \"SE_BULT\",\n            \"label\": \"•\",\n        }\n        \"A(SE_W)\": {\n            \"key\": \"SE_OMEG\",\n            \"label\": \"Ω\",\n        }\n        \"A(SE_E)\": {\n            \"key\": \"SE_EACU\",\n            \"label\": \"É\",\n        }\n        \"A(SE_R)\": {\n            \"key\": \"SE_REGD\",\n            \"label\": \"®\",\n        }\n        \"A(SE_T)\": {\n            \"key\": \"SE_DAGG\",\n            \"label\": \"†\",\n        }\n        \"A(SE_Y)\": {\n            \"key\": \"SE_MICR\",\n            \"label\": \"µ\",\n        }\n        \"A(SE_U)\": {\n            \"key\": \"SE_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"A(SE_I)\": {\n            \"key\": \"SE_DLSI\",\n            \"label\": \"ı\",\n        }\n        \"A(SE_O)\": {\n            \"key\": \"SE_OE\",\n            \"label\": \"Œ\",\n        }\n        \"A(SE_P)\": {\n            \"key\": \"SE_PI\",\n            \"label\": \"π\",\n        }\n        \"A(SE_ARNG)\": {\n            \"key\": \"SE_DOTA\",\n            \"label\": \"˙\",\n        }\n        \"A(SE_DIAE)\": {\n            \"key\": \"SE_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"A(SE_A)\": {\n            \"key\": \"SE_APPL\",\n            \"label\": \" (Apple logo)\",\n        }\n        \"A(SE_S)\": {\n            \"key\": \"SE_SS\",\n            \"label\": \"ß\",\n        }\n        \"A(SE_D)\": {\n            \"key\": \"SE_PDIF\",\n            \"label\": \"∂\",\n        }\n        \"A(SE_F)\": {\n            \"key\": \"SE_FHK\",\n            \"label\": \"ƒ\",\n        }\n        \"A(SE_G)\": {\n            \"key\": \"SE_CEDL\",\n            \"label\": \"¸\",\n        }\n        \"A(SE_H)\": {\n            \"key\": \"SE_OGON\",\n            \"label\": \"˛\",\n        }\n        \"A(SE_J)\": {\n            \"key\": \"SE_SQRT\",\n            \"label\": \"√\",\n        }\n        \"A(SE_K)\": {\n            \"key\": \"SE_FORD\",\n            \"label\": \"ª\",\n        }\n        \"A(SE_L)\": {\n            \"key\": \"SE_FI\",\n            \"label\": \"ﬁ\",\n        }\n        \"A(SE_ODIA)\": {\n            \"key\": \"SE_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"A(SE_ADIA)\": {\n            \"key\": \"SE_AE\",\n            \"label\": \"Æ\",\n        }\n        \"A(SE_QUOT)\": {\n            \"key\": \"SE_TM\",\n            \"label\": \"™\",\n        }\n        \"A(SE_LABK)\": {\n            \"key\": \"SE_LTEQ\",\n            \"label\": \"≤\",\n        }\n        \"A(SE_Z)\": {\n            \"key\": \"SE_DIV\",\n            \"label\": \"÷\",\n        }\n        \"A(SE_C)\": {\n            \"key\": \"SE_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"A(SE_V)\": {\n            \"key\": \"SE_LSAQ\",\n            \"label\": \"‹\",\n        }\n        \"A(SE_B)\": {\n            \"key\": \"SE_RSAQ\",\n            \"label\": \"›\",\n        }\n        \"A(SE_N)\": {\n            \"key\": \"SE_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"A(SE_M)\": {\n            \"key\": \"SE_RSQU\",\n            \"label\": \"’\",\n        }\n        \"A(SE_COMM)\": {\n            \"key\": \"SE_SLQU\",\n            \"label\": \"‚\",\n        }\n        \"A(SE_DOT)\": {\n            \"key\": \"SE_ELLP\",\n            \"label\": \"…\",\n        }\n        \"A(SE_MINS)\": {\n            \"key\": \"SE_NDSH\",\n            \"label\": \"–\",\n        }\n/* Shift+Alted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬─────┐\n * │   │ ¡ │ ” │ ¥ │ ¢ │ ‰ │   │ \\ │ { │ } │ ≠ │ ¿ │   │     │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬───┤\n * │     │   │ ˝ │   │   │ ‡ │ ˜ │   │ ˆ │   │ ∏ │ ˚ │   │   │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐  │\n * │      │ ◊ │ ∑ │ ∆ │ ∫ │ ¯ │ ˘ │ ¬ │ º │ ﬂ │   │   │   │  │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┤\n * │    │ ≥ │ ⁄ │ ˇ │   │ « │ » │ “ │ ” │ „ │ · │ — │        │\n * ├────┴┬──┴─┬─┴───┼───┴───┴───┴───┴───┴───┼───┴─┬─┴──┬─────┤\n * │     │    │     │                       │     │    │     │\n * └─────┴────┴─────┴───────────────────────┴─────┴────┴─────┘\n */\n        \"S(A(SE_1))\": {\n            \"key\": \"SE_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"S(A(SE_3))\": {\n            \"key\": \"SE_YEN\",\n            \"label\": \"¥\",\n        }\n        \"S(A(SE_4))\": {\n            \"key\": \"SE_CENT\",\n            \"label\": \"¢\",\n        }\n        \"S(A(SE_5))\": {\n            \"key\": \"SE_PERM\",\n            \"label\": \"‰\",\n        }\n        \"S(A(SE_7))\": {\n            \"key\": \"SE_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"S(A(SE_8))\": {\n            \"key\": \"SE_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(A(SE_9))\": {\n            \"key\": \"SE_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(A(SE_0))\": {\n            \"key\": \"SE_NEQL\",\n            \"label\": \"≠\",\n        }\n        \"S(A(SE_PLUS))\": {\n            \"key\": \"SE_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"S(A(SE_W))\": {\n            \"key\": \"SE_DACU\",\n            \"label\": \"˝\",\n        }\n        \"S(A(SE_T))\": {\n            \"key\": \"SE_DDAG\",\n            \"label\": \"‡\",\n        }\n        \"S(A(SE_Y))\": {\n            \"key\": \"SE_STIL\",\n            \"label\": \"˜\",\n        }\n        \"S(A(SE_I))\": {\n            \"key\": \"SE_DCIR\",\n            \"label\": \"ˆ\",\n        }\n        \"S(A(SE_P))\": {\n            \"key\": \"SE_NARP\",\n            \"label\": \"∏\",\n        }\n        \"S(A(SE_ARNG))\": {\n            \"key\": \"SE_RNGA\",\n            \"label\": \"˚\",\n        }\n        \"S(A(SE_A))\": {\n            \"key\": \"SE_LOZN\",\n            \"label\": \"◊\",\n        }\n        \"S(A(SE_S))\": {\n            \"key\": \"SE_NARS\",\n            \"label\": \"∑\",\n        }\n        \"S(A(SE_D))\": {\n            \"key\": \"SE_INCR\",\n            \"label\": \"∆\",\n        }\n        \"S(A(SE_F))\": {\n            \"key\": \"SE_INTG\",\n            \"label\": \"∫\",\n        }\n        \"S(A(SE_G))\": {\n            \"key\": \"SE_MACR\",\n            \"label\": \"¯\",\n        }\n        \"S(A(SE_H))\": {\n            \"key\": \"SE_BREV\",\n            \"label\": \"˘\",\n        }\n        \"S(A(SE_J))\": {\n            \"key\": \"SE_NOT\",\n            \"label\": \"¬\",\n        }\n        \"S(A(SE_K))\": {\n            \"key\": \"SE_MORD\",\n            \"label\": \"º\",\n        }\n        \"S(A(SE_L))\": {\n            \"key\": \"SE_FL\",\n            \"label\": \"ﬂ\",\n        }\n        \"S(A(SE_LABK))\": {\n            \"key\": \"SE_GTEQ\",\n            \"label\": \"≥\",\n        }\n        \"S(A(SE_Z))\": {\n            \"key\": \"SE_FRSL\",\n            \"label\": \"⁄\",\n        }\n        \"S(A(SE_X))\": {\n            \"key\": \"SE_CARN\",\n            \"label\": \"ˇ\",\n        }\n        \"S(A(SE_V))\": {\n            \"key\": \"SE_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"S(A(SE_B))\": {\n            \"key\": \"SE_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"S(A(SE_N))\": {\n            \"key\": \"SE_LDQU\",\n            \"label\": \"“\",\n        }\n        \"S(A(SE_M))\": {\n            \"key\": \"SE_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(A(SE_COMM))\": {\n            \"key\": \"SE_DLQU\",\n            \"label\": \"„\",\n        }\n        \"S(A(SE_DOT))\": {\n            \"key\": \"SE_MDDT\",\n            \"label\": \"·\",\n        }\n        \"S(A(SE_MINS))\": {\n            \"key\": \"SE_MDSH\",\n            \"label\": \"—\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_swiss_de_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ § │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ ^ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ ü │ ¨ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ö │ ä │ $ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"CH_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_1\": {\n            \"key\": \"CH_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"CH_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"CH_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"CH_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"CH_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"CH_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"CH_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"CH_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"CH_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"CH_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"CH_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"CH_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"CH_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"CH_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"CH_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"CH_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"CH_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"CH_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"CH_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"CH_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"CH_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"CH_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"CH_UDIA\",\n            \"label\": \"ü\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"CH_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"CH_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"CH_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"CH_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"CH_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"CH_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"CH_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"CH_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"CH_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"CH_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"CH_ODIA\",\n            \"label\": \"ö\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"CH_ADIA\",\n            \"label\": \"ä\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"CH_DLR\",\n            \"label\": \"$\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"CH_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"CH_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"CH_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"CH_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"CH_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"CH_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"CH_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"CH_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"CH_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"CH_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"CH_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ° │ + │ \" │ * │ ç │ % │ & │ / │ ( │ ) │ = │ ? │ ` │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ è │ ! │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ é │ à │ £ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(CH_SECT)\": {\n            \"key\": \"CH_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(CH_1)\": {\n            \"key\": \"CH_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(CH_2)\": {\n            \"key\": \"CH_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(CH_3)\": {\n            \"key\": \"CH_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(CH_4)\": {\n            \"key\": \"CH_CCED\",\n            \"label\": \"ç\",\n        }\n        \"S(CH_5)\": {\n            \"key\": \"CH_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(CH_6)\": {\n            \"key\": \"CH_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(CH_7)\": {\n            \"key\": \"CH_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(CH_8)\": {\n            \"key\": \"CH_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(CH_9)\": {\n            \"key\": \"CH_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(CH_0)\": {\n            \"key\": \"CH_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(CH_QUOT)\": {\n            \"key\": \"CH_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(CH_CIRC)\": {\n            \"key\": \"CH_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(CH_UDIA)\": {\n            \"key\": \"CH_EGRV\",\n            \"label\": \"è\",\n        }\n        \"S(CH_DIAE)\": {\n            \"key\": \"CH_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(CH_ODIA)\": {\n            \"key\": \"CH_EACU\",\n            \"label\": \"é\",\n        }\n        \"S(CH_ADIA)\": {\n            \"key\": \"CH_AGRV\",\n            \"label\": \"à\",\n        }\n        \"S(CH_DLR)\": {\n            \"key\": \"CH_PND\",\n            \"label\": \"£\",\n        }\n        \"S(CH_LABK)\": {\n            \"key\": \"CH_RABK\",\n            \"label\": \">\",\n        }\n        \"S(CH_COMM)\": {\n            \"key\": \"CH_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(CH_DOT)\": {\n            \"key\": \"CH_COLN\",\n            \"label\": \":\",\n        }\n        \"S(CH_MINS)\": {\n            \"key\": \"CH_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ¦ │ @ │ # │   │   │ ¬ │ | │ ¢ │   │   │ ´ │ ~ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │   │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ { │ } │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ \\ │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(CH_1)\": {\n            \"key\": \"CH_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"ALGR(CH_2)\": {\n            \"key\": \"CH_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(CH_3)\": {\n            \"key\": \"CH_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(CH_6)\": {\n            \"key\": \"CH_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(CH_7)\": {\n            \"key\": \"CH_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(CH_8)\": {\n            \"key\": \"CH_CENT\",\n            \"label\": \"¢\",\n        }\n        \"ALGR(CH_QUOT)\": {\n            \"key\": \"CH_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(CH_CIRC)\": {\n            \"key\": \"CH_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(CH_E)\": {\n            \"key\": \"CH_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(CH_UDIA)\": {\n            \"key\": \"CH_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(CH_DIAE)\": {\n            \"key\": \"CH_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(CH_ADIA)\": {\n            \"key\": \"CH_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(CH_DLR)\": {\n            \"key\": \"CH_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(CH_LABK)\": {\n            \"key\": \"CH_BSLS\",\n            \"label\": \"\\\\\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_swiss_fr_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ § │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ ' │ ^ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Z │ U │ I │ O │ P │ è │ ¨ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ é │ à │ $ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Y │ X │ C │ V │ B │ N │ M │ , │ . │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"CH_SECT\",\n            \"label\": \"§\",\n        }\n        \"KC_1\": {\n            \"key\": \"CH_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"CH_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"CH_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"CH_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"CH_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"CH_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"CH_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"CH_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"CH_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"CH_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"CH_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"CH_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"KC_Q\": {\n            \"key\": \"CH_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"CH_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"CH_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"CH_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"CH_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"CH_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_U\": {\n            \"key\": \"CH_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"CH_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"CH_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"CH_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"CH_EGRV\",\n            \"label\": \"è\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"CH_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"KC_A\": {\n            \"key\": \"CH_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"CH_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"CH_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"CH_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"CH_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"CH_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"CH_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"CH_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"CH_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"CH_EACU\",\n            \"label\": \"é\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"CH_AGRV\",\n            \"label\": \"à\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"CH_DLR\",\n            \"label\": \"$\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"CH_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"CH_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_X\": {\n            \"key\": \"CH_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"CH_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"CH_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"CH_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"CH_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"CH_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"CH_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"CH_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"CH_MINS\",\n            \"label\": \"-\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ° │ + │ \" │ * │ ç │ % │ & │ / │ ( │ ) │ = │ ? │ ` │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ ü │ ! │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ ö │ ä │ £ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │ ; │ : │ _ │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(CH_SECT)\": {\n            \"key\": \"CH_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(CH_1)\": {\n            \"key\": \"CH_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(CH_2)\": {\n            \"key\": \"CH_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(CH_3)\": {\n            \"key\": \"CH_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(CH_4)\": {\n            \"key\": \"CH_CCED\",\n            \"label\": \"ç\",\n        }\n        \"S(CH_5)\": {\n            \"key\": \"CH_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(CH_6)\": {\n            \"key\": \"CH_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(CH_7)\": {\n            \"key\": \"CH_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(CH_8)\": {\n            \"key\": \"CH_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(CH_9)\": {\n            \"key\": \"CH_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(CH_0)\": {\n            \"key\": \"CH_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(CH_QUOT)\": {\n            \"key\": \"CH_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(CH_CIRC)\": {\n            \"key\": \"CH_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"S(CH_EGRV)\": {\n            \"key\": \"CH_UDIA\",\n            \"label\": \"ü\",\n        }\n        \"S(CH_DIAE)\": {\n            \"key\": \"CH_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(CH_EACU)\": {\n            \"key\": \"CH_ODIA\",\n            \"label\": \"ö\",\n        }\n        \"S(CH_AGRV)\": {\n            \"key\": \"CH_ADIA\",\n            \"label\": \"ä\",\n        }\n        \"S(CH_DLR)\": {\n            \"key\": \"CH_PND\",\n            \"label\": \"£\",\n        }\n        \"S(CH_LABK)\": {\n            \"key\": \"CH_RABK\",\n            \"label\": \">\",\n        }\n        \"S(CH_COMM)\": {\n            \"key\": \"CH_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(CH_DOT)\": {\n            \"key\": \"CH_COLN\",\n            \"label\": \":\",\n        }\n        \"S(CH_MINS)\": {\n            \"key\": \"CH_UNDS\",\n            \"label\": \"_\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ¦ │ @ │ # │   │   │ ¬ │ | │ ¢ │   │   │ ´ │ ~ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ € │   │   │   │   │   │   │   │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │ { │ } │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ \\ │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(CH_1)\": {\n            \"key\": \"CH_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"ALGR(CH_2)\": {\n            \"key\": \"CH_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(CH_3)\": {\n            \"key\": \"CH_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(CH_6)\": {\n            \"key\": \"CH_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(CH_7)\": {\n            \"key\": \"CH_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(CH_8)\": {\n            \"key\": \"CH_CENT\",\n            \"label\": \"¢\",\n        }\n        \"ALGR(CH_QUOT)\": {\n            \"key\": \"CH_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(CH_CIRC)\": {\n            \"key\": \"CH_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(CH_E)\": {\n            \"key\": \"CH_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(CH_EGRV)\": {\n            \"key\": \"CH_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(CH_DIAE)\": {\n            \"key\": \"CH_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(CH_AGRV)\": {\n            \"key\": \"CH_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(CH_DLR)\": {\n            \"key\": \"CH_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(CH_LABK)\": {\n            \"key\": \"CH_BSLS\",\n            \"label\": \"\\\\\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_turkish_f_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ + │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ / │ - │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ F │ G │ Ğ │ I │ O │ D │ R │ N │ H │ P │ Q │ W │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ U │ İ │ E │ A │ Ü │ T │ K │ M │ L │ Y │ Ş │ X │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ J │ Ö │ V │ C │ Ç │ Z │ S │ B │ . │ , │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"TR_PLUS\",\n            \"label\": \"+\",\n        }\n        \"KC_1\": {\n            \"key\": \"TR_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"TR_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"TR_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"TR_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"TR_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"TR_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"TR_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"TR_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"TR_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"TR_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"TR_SLSH\",\n            \"label\": \"/\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"TR_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_Q\": {\n            \"key\": \"TR_F\",\n            \"label\": \"F\",\n        }\n        \"KC_W\": {\n            \"key\": \"TR_G\",\n            \"label\": \"G\",\n        }\n        \"KC_E\": {\n            \"key\": \"TR_GBRV\",\n            \"label\": \"Ğ\",\n        }\n        \"KC_R\": {\n            \"key\": \"TR_I\",\n            \"label\": \"I\",\n        }\n        \"KC_T\": {\n            \"key\": \"TR_O\",\n            \"label\": \"O\",\n        }\n        \"KC_Y\": {\n            \"key\": \"TR_D\",\n            \"label\": \"D\",\n        }\n        \"KC_U\": {\n            \"key\": \"TR_R\",\n            \"label\": \"R\",\n        }\n        \"KC_I\": {\n            \"key\": \"TR_N\",\n            \"label\": \"N\",\n        }\n        \"KC_O\": {\n            \"key\": \"TR_H\",\n            \"label\": \"H\",\n        }\n        \"KC_P\": {\n            \"key\": \"TR_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"TR_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"TR_W\",\n            \"label\": \"W\",\n        }\n        \"KC_A\": {\n            \"key\": \"TR_U\",\n            \"label\": \"U\",\n        }\n        \"KC_S\": {\n            \"key\": \"TR_IDOT\",\n            \"label\": \"İ\",\n        }\n        \"KC_D\": {\n            \"key\": \"TR_E\",\n            \"label\": \"E\",\n        }\n        \"KC_F\": {\n            \"key\": \"TR_A\",\n            \"label\": \"A\",\n        }\n        \"KC_G\": {\n            \"key\": \"TR_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"KC_H\": {\n            \"key\": \"TR_T\",\n            \"label\": \"T\",\n        }\n        \"KC_J\": {\n            \"key\": \"TR_K\",\n            \"label\": \"K\",\n        }\n        \"KC_K\": {\n            \"key\": \"TR_M\",\n            \"label\": \"M\",\n        }\n        \"KC_L\": {\n            \"key\": \"TR_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"TR_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"TR_SCED\",\n            \"label\": \"Ş\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"TR_X\",\n            \"label\": \"X\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"TR_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"TR_J\",\n            \"label\": \"J\",\n        }\n        \"KC_X\": {\n            \"key\": \"TR_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_C\": {\n            \"key\": \"TR_V\",\n            \"label\": \"V\",\n        }\n        \"KC_V\": {\n            \"key\": \"TR_C\",\n            \"label\": \"C\",\n        }\n        \"KC_B\": {\n            \"key\": \"TR_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"KC_N\": {\n            \"key\": \"TR_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_M\": {\n            \"key\": \"TR_S\",\n            \"label\": \"S\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"TR_B\",\n            \"label\": \"B\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"TR_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"TR_COMM\",\n            \"label\": \",\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ * │ ! │ \" │ ^ │ $ │ % │ & │ ' │ ( │ ) │ = │ ? │ _ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │   │ : │ ; │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(TR_PLUS)\": {\n            \"key\": \"TR_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(TR_1)\": {\n            \"key\": \"TR_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(TR_2)\": {\n            \"key\": \"TR_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(TR_3)\": {\n            \"key\": \"TR_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(TR_4)\": {\n            \"key\": \"TR_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(TR_5)\": {\n            \"key\": \"TR_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(TR_6)\": {\n            \"key\": \"TR_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(TR_7)\": {\n            \"key\": \"TR_QUOT\",\n            \"label\": \"'\",\n        }\n        \"S(TR_8)\": {\n            \"key\": \"TR_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(TR_9)\": {\n            \"key\": \"TR_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(TR_0)\": {\n            \"key\": \"TR_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(TR_SLSH)\": {\n            \"key\": \"TR_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(TR_MINS)\": {\n            \"key\": \"TR_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(TR_LABK)\": {\n            \"key\": \"TR_RABK\",\n            \"label\": \">\",\n        }\n        \"S(TR_DOT)\": {\n            \"key\": \"TR_COLN\",\n            \"label\": \":\",\n        }\n        \"S(TR_COMM)\": {\n            \"key\": \"TR_SCLN\",\n            \"label\": \";\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¬ │ ¹ │ ² │ # │ ¼ │ ½ │ ¾ │ { │ [ │ ] │ } │ \\ │ | │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ @ │   │   │ ¶ │   │ ¥ │   │   │ Ø │ £ │ ¨ │ ~ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ Æ │ ß │ € │   │   │ ₺ │   │   │   │ ´ │   │ ` │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │ « │ » │ ¢ │   │   │   │ µ │ × │ ÷ │ - │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(TR_PLUS)\": {\n            \"key\": \"TR_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(TR_1)\": {\n            \"key\": \"TR_SUP1\",\n            \"label\": \"¹\",\n        }\n        \"ALGR(TR_2)\": {\n            \"key\": \"TR_SUP2\",\n            \"label\": \"²\",\n        }\n        \"ALGR(TR_3)\": {\n            \"key\": \"TR_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(TR_4)\": {\n            \"key\": \"TR_QRTR\",\n            \"label\": \"¼\",\n        }\n        \"ALGR(TR_5)\": {\n            \"key\": \"TR_HALF\",\n            \"label\": \"½\",\n        }\n        \"ALGR(TR_6)\": {\n            \"key\": \"TR_TQTR\",\n            \"label\": \"¾\",\n        }\n        \"ALGR(TR_7)\": {\n            \"key\": \"TR_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(TR_8)\": {\n            \"key\": \"TR_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(TR_9)\": {\n            \"key\": \"TR_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(TR_0)\": {\n            \"key\": \"TR_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(TR_SLSH)\": {\n            \"key\": \"TR_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(TR_MINS)\": {\n            \"key\": \"TR_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(TR_F)\": {\n            \"key\": \"TR_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(TR_I)\": {\n            \"key\": \"TR_PILC\",\n            \"label\": \"¶\",\n        }\n        \"ALGR(TR_D)\": {\n            \"key\": \"TR_YEN\",\n            \"label\": \"¥\",\n        }\n        \"ALGR(TR_H)\": {\n            \"key\": \"TR_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"ALGR(TR_P)\": {\n            \"key\": \"TR_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(TR_Q)\": {\n            \"key\": \"TR_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"ALGR(TR_W)\": {\n            \"key\": \"TR_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(TR_U)\": {\n            \"key\": \"TR_AE\",\n            \"label\": \"Æ\",\n        }\n        \"ALGR(TR_IDOT)\": {\n            \"key\": \"TR_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(TR_E)\": {\n            \"key\": \"TR_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(TR_T)\": {\n            \"key\": \"TR_LIRA\",\n            \"label\": \"₺\",\n        }\n        \"ALGR(TR_Y)\": {\n            \"key\": \"TR_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(TR_X)\": {\n            \"key\": \"TR_GRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"ALGR(TR_J)\": {\n            \"key\": \"TR_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"ALGR(TR_ODIA)\": {\n            \"key\": \"TR_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"ALGR(TR_V)\": {\n            \"key\": \"TR_CENT\",\n            \"label\": \"¢\",\n        }\n        \"ALGR(TR_S)\": {\n            \"key\": \"TR_MICR\",\n            \"label\": \"µ\",\n        }\n        \"ALGR(TR_B)\": {\n            \"key\": \"TR_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(TR_DOT)\": {\n            \"key\": \"TR_DIV\",\n            \"label\": \"÷\",\n        }\n        \"ALGR(TR_COMM)\": {\n            \"key\": \"TR_SHYP\",\n            \"label\": \"­ (soft hyphen)\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │   │ ³ │ ¤ │   │   │   │   │   │   │ ¿ │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │ ® │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │ § │   │ ª │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ ¦ │   │   │ © │   │   │   │ º │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ALGR(TR_3))\": {\n            \"key\": \"TR_SUP3\",\n            \"label\": \"³\",\n        }\n        \"S(ALGR(TR_4))\": {\n            \"key\": \"TR_CURR\",\n            \"label\": \"¤\",\n        }\n        \"S(ALGR(TR_SLSH))\": {\n            \"key\": \"TR_IQUE\",\n            \"label\": \"¿\",\n        }\n        \"S(ALGR(TR_I))\": {\n            \"key\": \"TR_REGD\",\n            \"label\": \"®\",\n        }\n        \"S(ALGR(TR_IDOT))\": {\n            \"key\": \"TR_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(ALGR(TR_A))\": {\n            \"key\": \"TR_FORD\",\n            \"label\": \"ª\",\n        }\n        \"S(ALGR(TR_LABK))\": {\n            \"key\": \"TR_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"S(ALGR(TR_V))\": {\n            \"key\": \"TR_COPY\",\n            \"label\": \"©\",\n        }\n        \"S(ALGR(TR_S))\": {\n            \"key\": \"TR_MORD\",\n            \"label\": \"º\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_turkish_q_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ \" │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ * │ - │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ Ğ │ Ü │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ş │ İ │ , │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ < │ Z │ X │ C │ V │ B │ N │ M │ Ö │ Ç │ . │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"TR_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"KC_1\": {\n            \"key\": \"TR_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"TR_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"TR_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"TR_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"TR_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"TR_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"TR_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"TR_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"TR_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"TR_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"TR_ASTR\",\n            \"label\": \"*\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"TR_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_Q\": {\n            \"key\": \"TR_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"TR_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"TR_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"TR_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"TR_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"TR_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"TR_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"TR_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"TR_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"TR_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"TR_GBRV\",\n            \"label\": \"Ğ\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"TR_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"KC_A\": {\n            \"key\": \"TR_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"TR_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"TR_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"TR_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"TR_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"TR_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"TR_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"TR_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"TR_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"TR_SCED\",\n            \"label\": \"Ş\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"TR_IDOT\",\n            \"label\": \"İ\",\n        }\n        \"KC_NUHS\": {\n            \"key\": \"TR_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_NUBS\": {\n            \"key\": \"TR_LABK\",\n            \"label\": \"<\",\n        }\n        \"KC_Z\": {\n            \"key\": \"TR_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"TR_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"TR_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"TR_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"TR_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"TR_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"TR_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"TR_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"TR_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"TR_DOT\",\n            \"label\": \".\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ é │ ! │ ' │ ^ │ + │ % │ & │ / │ ( │ ) │ = │ ? │ _ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │   │   │ ; │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ > │   │   │   │   │   │   │   │   │   │ : │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(TR_DQUO)\": {\n            \"key\": \"TR_EACU\",\n            \"label\": \"é\",\n        }\n        \"S(TR_1)\": {\n            \"key\": \"TR_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(TR_2)\": {\n            \"key\": \"TR_QUOT\",\n            \"label\": \"'\",\n        }\n        \"S(TR_3)\": {\n            \"key\": \"TR_CIRC\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(TR_4)\": {\n            \"key\": \"TR_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(TR_5)\": {\n            \"key\": \"TR_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(TR_6)\": {\n            \"key\": \"TR_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(TR_7)\": {\n            \"key\": \"TR_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(TR_8)\": {\n            \"key\": \"TR_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(TR_9)\": {\n            \"key\": \"TR_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(TR_0)\": {\n            \"key\": \"TR_EQL\",\n            \"label\": \"=\",\n        }\n        \"S(TR_ASTR)\": {\n            \"key\": \"TR_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(TR_MINS)\": {\n            \"key\": \"TR_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(TR_COMM)\": {\n            \"key\": \"TR_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(TR_LABK)\": {\n            \"key\": \"TR_RABK\",\n            \"label\": \">\",\n        }\n        \"S(TR_DOT)\": {\n            \"key\": \"TR_COLN\",\n            \"label\": \":\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │ £ │ # │ $ │ ½ │   │ { │ [ │ ] │ } │ \\ │ | │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ @ │   │ € │   │ ₺ │   │   │   │   │   │ ¨ │ ~ │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ Æ │ ß │   │   │   │   │   │   │   │ ´ │   │ ` │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(TR_2)\": {\n            \"key\": \"TR_PND\",\n            \"label\": \"£\",\n        }\n        \"ALGR(TR_3)\": {\n            \"key\": \"TR_HASH\",\n            \"label\": \"#\",\n        }\n        \"ALGR(TR_4)\": {\n            \"key\": \"TR_DLR\",\n            \"label\": \"$\",\n        }\n        \"ALGR(TR_5)\": {\n            \"key\": \"TR_HALF\",\n            \"label\": \"½\",\n        }\n        \"ALGR(TR_7)\": {\n            \"key\": \"TR_LCBR\",\n            \"label\": \"{\",\n        }\n        \"ALGR(TR_8)\": {\n            \"key\": \"TR_LBRC\",\n            \"label\": \"[\",\n        }\n        \"ALGR(TR_9)\": {\n            \"key\": \"TR_RBRC\",\n            \"label\": \"]\",\n        }\n        \"ALGR(TR_0)\": {\n            \"key\": \"TR_RCBR\",\n            \"label\": \"}\",\n        }\n        \"ALGR(TR_ASTR)\": {\n            \"key\": \"TR_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"ALGR(TR_MINS)\": {\n            \"key\": \"TR_PIPE\",\n            \"label\": \"|\",\n        }\n        \"ALGR(TR_Q)\": {\n            \"key\": \"TR_AT\",\n            \"label\": \"@\",\n        }\n        \"ALGR(TR_E)\": {\n            \"key\": \"TR_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(TR_T)\": {\n            \"key\": \"TR_LIRA\",\n            \"label\": \"₺\",\n        }\n        \"ALGR(TR_GBRV)\": {\n            \"key\": \"TR_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"ALGR(TR_UDIA)\": {\n            \"key\": \"TR_TILD\",\n            \"label\": \"~ (dead)\",\n        }\n        \"ALGR(TR_A)\": {\n            \"key\": \"TR_AE\",\n            \"label\": \"Æ\",\n        }\n        \"ALGR(TR_S)\": {\n            \"key\": \"TR_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(TR_SCED)\": {\n            \"key\": \"TR_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(TR_COMM)\": {\n            \"key\": \"TR_GRV\",\n            \"label\": \"` (dead)\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_uk_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"UK_GRV\",\n            \"label\": \"`\"\n        },\n        \"KC_1\": {\n            \"key\": \"UK_1\",\n            \"label\": \"1\"\n        },\n        \"KC_2\": {\n            \"key\": \"UK_2\",\n            \"label\": \"2\"\n        },\n        \"KC_3\": {\n            \"key\": \"UK_3\",\n            \"label\": \"3\"\n        },\n        \"KC_4\": {\n            \"key\": \"UK_4\",\n            \"label\": \"4\"\n        },\n        \"KC_5\": {\n            \"key\": \"UK_5\",\n            \"label\": \"5\"\n        },\n        \"KC_6\": {\n            \"key\": \"UK_6\",\n            \"label\": \"6\"\n        },\n        \"KC_7\": {\n            \"key\": \"UK_7\",\n            \"label\": \"7\"\n        },\n        \"KC_8\": {\n            \"key\": \"UK_8\",\n            \"label\": \"8\"\n        },\n        \"KC_9\": {\n            \"key\": \"UK_9\",\n            \"label\": \"9\"\n        },\n        \"KC_0\": {\n            \"key\": \"UK_0\",\n            \"label\": \"0\"\n        },\n        \"KC_MINS\": {\n            \"key\": \"UK_MINS\",\n            \"label\": \"-\"\n        },\n        \"KC_EQL\": {\n            \"key\": \"UK_EQL\",\n            \"label\": \"=\"\n        },\n        \"KC_Q\": {\n            \"key\": \"UK_Q\",\n            \"label\": \"Q\"\n        },\n        \"KC_W\": {\n            \"key\": \"UK_W\",\n            \"label\": \"W\"\n        },\n        \"KC_E\": {\n            \"key\": \"UK_E\",\n            \"label\": \"E\"\n        },\n        \"KC_R\": {\n            \"key\": \"UK_R\",\n            \"label\": \"R\"\n        },\n        \"KC_T\": {\n            \"key\": \"UK_T\",\n            \"label\": \"T\"\n        },\n        \"KC_Y\": {\n            \"key\": \"UK_Y\",\n            \"label\": \"Y\"\n        },\n        \"KC_U\": {\n            \"key\": \"UK_U\",\n            \"label\": \"U\"\n        },\n        \"KC_I\": {\n            \"key\": \"UK_I\",\n            \"label\": \"I\"\n        },\n        \"KC_O\": {\n            \"key\": \"UK_O\",\n            \"label\": \"O\"\n        },\n        \"KC_P\": {\n            \"key\": \"UK_P\",\n            \"label\": \"P\"\n        },\n        \"KC_LBRC\": {\n            \"key\": \"UK_LBRC\",\n            \"label\": \"[\"\n        },\n        \"KC_RBRC\": {\n            \"key\": \"UK_RBRC\",\n            \"label\": \"]\"\n        },\n        \"KC_A\": {\n            \"key\": \"UK_A\",\n            \"label\": \"A\"\n        },\n        \"KC_S\": {\n            \"key\": \"UK_S\",\n            \"label\": \"S\"\n        },\n        \"KC_D\": {\n            \"key\": \"UK_D\",\n            \"label\": \"D\"\n        },\n        \"KC_F\": {\n            \"key\": \"UK_F\",\n            \"label\": \"F\"\n        },\n        \"KC_G\": {\n            \"key\": \"UK_G\",\n            \"label\": \"G\"\n        },\n        \"KC_H\": {\n            \"key\": \"UK_H\",\n            \"label\": \"H\"\n        },\n        \"KC_J\": {\n            \"key\": \"UK_J\",\n            \"label\": \"J\"\n        },\n        \"KC_K\": {\n            \"key\": \"UK_K\",\n            \"label\": \"K\"\n        },\n        \"KC_L\": {\n            \"key\": \"UK_L\",\n            \"label\": \"L\"\n        },\n        \"KC_SCLN\": {\n            \"key\": \"UK_SCLN\",\n            \"label\": \";\"\n        },\n        \"KC_QUOT\": {\n            \"key\": \"UK_QUOT\",\n            \"label\": \"'\"\n        },\n        \"KC_NUHS\": {\n            \"key\": \"UK_HASH\",\n            \"label\": \"#\"\n        },\n        \"KC_NUBS\": {\n            \"key\": \"UK_BSLS\",\n            \"label\": \"\\\\\"\n        },\n        \"KC_Z\": {\n            \"key\": \"UK_Z\",\n            \"label\": \"Z\"\n        },\n        \"KC_X\": {\n            \"key\": \"UK_X\",\n            \"label\": \"X\"\n        },\n        \"KC_C\": {\n            \"key\": \"UK_C\",\n            \"label\": \"C\"\n        },\n        \"KC_V\": {\n            \"key\": \"UK_V\",\n            \"label\": \"V\"\n        },\n        \"KC_B\": {\n            \"key\": \"UK_B\",\n            \"label\": \"B\"\n        },\n        \"KC_N\": {\n            \"key\": \"UK_N\",\n            \"label\": \"N\"\n        },\n        \"KC_M\": {\n            \"key\": \"UK_M\",\n            \"label\": \"M\"\n        },\n        \"KC_COMM\": {\n            \"key\": \"UK_COMM\",\n            \"label\": \",\"\n        },\n        \"KC_DOT\": {\n            \"key\": \"UK_DOT\",\n            \"label\": \".\"\n        },\n        \"KC_SLSH\": {\n            \"key\": \"UK_SLSH\",\n            \"label\": \"/\"\n        },\n\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¬ │ ! │ \" │ £ │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │   │   │   │   │   │   │   │   │   │ : │ @ │ ~ │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │ | │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(UK_GRV)\": {\n            \"key\": \"UK_NOT\",\n            \"label\": \"¬\"\n        },\n        \"S(UK_1)\": {\n            \"key\": \"UK_EXLM\",\n            \"label\": \"!\"\n        },\n        \"S(UK_2)\": {\n            \"key\": \"UK_DQUO\",\n            \"label\": \"\\\"\"\n        },\n        \"S(UK_3)\": {\n            \"key\": \"UK_PND\",\n            \"label\": \"£\"\n        },\n        \"S(UK_4)\": {\n            \"key\": \"UK_DLR\",\n            \"label\": \"$\"\n        },\n        \"S(UK_5)\": {\n            \"key\": \"UK_PERC\",\n            \"label\": \"%\"\n        },\n        \"S(UK_6)\": {\n            \"key\": \"UK_CIRC\",\n            \"label\": \"^\"\n        },\n        \"S(UK_7)\": {\n            \"key\": \"UK_AMPR\",\n            \"label\": \"&\"\n        },\n        \"S(UK_8)\": {\n            \"key\": \"UK_ASTR\",\n            \"label\": \"*\"\n        },\n        \"S(UK_9)\": {\n            \"key\": \"UK_LPRN\",\n            \"label\": \"(\"\n        },\n        \"S(UK_0)\": {\n            \"key\": \"UK_RPRN\",\n            \"label\": \")\"\n        },\n        \"S(UK_MINS)\": {\n            \"key\": \"UK_UNDS\",\n            \"label\": \"_\"\n        },\n        \"S(UK_EQL)\": {\n            \"key\": \"UK_PLUS\",\n            \"label\": \"+\"\n        },\n        \"S(UK_LBRC)\": {\n            \"key\": \"UK_LCBR\",\n            \"label\": \"{\"\n        },\n        \"S(UK_RBRC)\": {\n            \"key\": \"UK_RCBR\",\n            \"label\": \"}\"\n        },\n        \"S(UK_SCLN)\": {\n            \"key\": \"UK_COLN\",\n            \"label\": \":\"\n        },\n        \"S(UK_QUOT)\": {\n            \"key\": \"UK_AT\",\n            \"label\": \"@\"\n        },\n        \"S(UK_HASH)\": {\n            \"key\": \"UK_TILD\",\n            \"label\": \"~\"\n        },\n        \"S(UK_BSLS)\": {\n            \"key\": \"UK_PIPE\",\n            \"label\": \"|\"\n        },\n        \"S(UK_COMM)\": {\n            \"key\": \"UK_LABK\",\n            \"label\": \"<\"\n        },\n        \"S(UK_DOT)\": {\n            \"key\": \"UK_RABK\",\n            \"label\": \">\"\n        },\n        \"S(UK_SLSH)\": {\n            \"key\": \"UK_QUES\",\n            \"label\": \"?\"\n        },\n\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ¦ │   │   │   │ € │   │   │   │   │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │ É │   │   │   │ Ú │ Í │ Ó │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n * │      │ Á │   │   │   │   │   │   │   │   │   │   │   │    │\n * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n * │    │   │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(UK_GRV)\": {\n            \"key\": \"UK_BRKP\",\n            \"label\": \"¦\"\n        },\n        \"ALGR(UK_4)\": {\n            \"key\": \"UK_EURO\",\n            \"label\": \"€\"\n        },\n        \"ALGR(KC_E)\": {\n            \"key\": \"UK_EACU\",\n            \"label\": \"É\"\n        },\n        \"ALGR(KC_U)\": {\n            \"key\": \"UK_UACU\",\n            \"label\": \"Ú\"\n        },\n        \"ALGR(KC_I)\": {\n            \"key\": \"UK_IACU\",\n            \"label\": \"Í\"\n        },\n        \"ALGR(KC_O)\": {\n            \"key\": \"UK_OACU\",\n            \"label\": \"Ó\"\n        },\n        \"ALGR(KC_A)\": {\n            \"key\": \"UK_AACU\",\n            \"label\": \"Á\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_ukrainian_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ' │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Й │ Ц │ У │ К │ Е │ Н │ Г │ Ш │ Щ │ З │ Х │ Ї │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ Ф │ І │ В │ А │ П │ Р │ О │ Л │ Д │ Ж │ Є │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Я │ Ч │ С │ М │ И │ Т │ Ь │ Б │ Ю │ . │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"UA_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_1\": {\n            \"key\": \"UA_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"UA_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"UA_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"UA_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"UA_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"UA_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"UA_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"UA_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"UA_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"UA_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"UA_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"UA_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"UA_YOT\",\n            \"label\": \"Й\",\n        }\n        \"KC_W\": {\n            \"key\": \"UA_TSE\",\n            \"label\": \"Ц\",\n        }\n        \"KC_E\": {\n            \"key\": \"UA_U\",\n            \"label\": \"У\",\n        }\n        \"KC_R\": {\n            \"key\": \"UA_KA\",\n            \"label\": \"К\",\n        }\n        \"KC_T\": {\n            \"key\": \"UA_E\",\n            \"label\": \"Е\",\n        }\n        \"KC_Y\": {\n            \"key\": \"UA_EN\",\n            \"label\": \"Н\",\n        }\n        \"KC_U\": {\n            \"key\": \"UA_HE\",\n            \"label\": \"Г\",\n        }\n        \"KC_I\": {\n            \"key\": \"UA_SHA\",\n            \"label\": \"Ш\",\n        }\n        \"KC_O\": {\n            \"key\": \"UA_SHCH\",\n            \"label\": \"Щ\",\n        }\n        \"KC_P\": {\n            \"key\": \"UA_ZE\",\n            \"label\": \"З\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"UA_KHA\",\n            \"label\": \"Х\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"UA_YI\",\n            \"label\": \"Ї\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"UA_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"UA_EF\",\n            \"label\": \"Ф\",\n        }\n        \"KC_S\": {\n            \"key\": \"UA_I\",\n            \"label\": \"І\",\n        }\n        \"KC_D\": {\n            \"key\": \"UA_VE\",\n            \"label\": \"В\",\n        }\n        \"KC_F\": {\n            \"key\": \"UA_A\",\n            \"label\": \"А\",\n        }\n        \"KC_G\": {\n            \"key\": \"UA_PE\",\n            \"label\": \"П\",\n        }\n        \"KC_H\": {\n            \"key\": \"UA_ER\",\n            \"label\": \"Р\",\n        }\n        \"KC_J\": {\n            \"key\": \"UA_O\",\n            \"label\": \"О\",\n        }\n        \"KC_K\": {\n            \"key\": \"UA_EL\",\n            \"label\": \"Л\",\n        }\n        \"KC_L\": {\n            \"key\": \"UA_DE\",\n            \"label\": \"Д\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"UA_ZHE\",\n            \"label\": \"Ж\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"UA_YE\",\n            \"label\": \"Є\",\n        }\n        \"KC_Z\": {\n            \"key\": \"UA_YA\",\n            \"label\": \"Я\",\n        }\n        \"KC_X\": {\n            \"key\": \"UA_CHE\",\n            \"label\": \"Ч\",\n        }\n        \"KC_C\": {\n            \"key\": \"UA_ES\",\n            \"label\": \"С\",\n        }\n        \"KC_V\": {\n            \"key\": \"UA_EM\",\n            \"label\": \"М\",\n        }\n        \"KC_B\": {\n            \"key\": \"UA_Y\",\n            \"label\": \"И\",\n        }\n        \"KC_N\": {\n            \"key\": \"UA_TE\",\n            \"label\": \"Т\",\n        }\n        \"KC_M\": {\n            \"key\": \"UA_SOFT\",\n            \"label\": \"Ь\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"UA_BE\",\n            \"label\": \"Б\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"UA_YU\",\n            \"label\": \"Ю\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"UA_DOT\",\n            \"label\": \".\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ₴ │ ! │ \" │ № │ ; │ % │ : │ ? │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │  /  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │   │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │   │   │ , │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(UA_QUOT)\": {\n            \"key\": \"UA_HRYV\",\n            \"label\": \"₴\",\n        }\n        \"S(UA_1)\": {\n            \"key\": \"UA_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(UA_2)\": {\n            \"key\": \"UA_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(UA_3)\": {\n            \"key\": \"UA_NUM\",\n            \"label\": \"№\",\n        }\n        \"S(UA_4)\": {\n            \"key\": \"UA_SCLN\",\n            \"label\": \";\",\n        }\n        \"S(UA_5)\": {\n            \"key\": \"UA_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(UA_6)\": {\n            \"key\": \"UA_COLN\",\n            \"label\": \":\",\n        }\n        \"S(UA_7)\": {\n            \"key\": \"UA_QUES\",\n            \"label\": \"?\",\n        }\n        \"S(UA_8)\": {\n            \"key\": \"UA_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(UA_9)\": {\n            \"key\": \"UA_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(UA_0)\": {\n            \"key\": \"UA_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(UA_MINS)\": {\n            \"key\": \"UA_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(UA_EQL)\": {\n            \"key\": \"UA_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(UA_BSLS)\": {\n            \"key\": \"UA_SLSH\",\n            \"label\": \"/\",\n        }\n        \"S(UA_DOT)\": {\n            \"key\": \"UA_COMM\",\n            \"label\": \",\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │ ґ │   │   │   │   │   │     │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │   │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │   │   │   │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(UA_HE)\": {\n            \"key\": \"UA_GE\",\n            \"label\": \"ґ\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_us_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │ : │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(KC_GRAVE)\": {\n            \"key\": \"KC_TILD\",\n            \"label\": \"~\",\n            \"aliases\": [\n                \"KC_TILDE\"\n            ]\n        },\n        \"S(KC_1)\": {\n            \"key\": \"KC_EXLM\",\n            \"label\": \"!\",\n            \"aliases\": [\n                \"KC_EXCLAIM\"\n            ]\n        },\n        \"S(KC_2)\": {\n            \"key\": \"KC_AT\",\n            \"label\": \"@\"\n        },\n        \"S(KC_3)\": {\n            \"key\": \"KC_HASH\",\n            \"label\": \"#\"\n        },\n        \"S(KC_4)\": {\n            \"key\": \"KC_DLR\",\n            \"label\": \"$\",\n            \"aliases\": [\n                \"KC_DOLLAR\"\n            ]\n        },\n        \"S(KC_5)\": {\n            \"key\": \"KC_PERC\",\n            \"label\": \"%\",\n            \"aliases\": [\n                \"KC_PERCENT\"\n            ]\n        },\n        \"S(KC_6)\": {\n            \"key\": \"KC_CIRC\",\n            \"label\": \"^\",\n            \"aliases\": [\n                \"KC_CIRCUMFLEX\"\n            ]\n        },\n        \"S(KC_7)\": {\n            \"key\": \"KC_AMPR\",\n            \"label\": \"&\",\n            \"aliases\": [\n                \"KC_AMPERSAND\"\n            ]\n        },\n        \"S(KC_8)\": {\n            \"key\": \"KC_ASTR\",\n            \"label\": \"*\",\n            \"aliases\": [\n                \"KC_ASTERISK\"\n            ]\n        },\n        \"S(KC_9)\": {\n            \"key\": \"KC_LPRN\",\n            \"label\": \"(\",\n            \"aliases\": [\n                \"KC_LEFT_PAREN\"\n            ]\n        },\n        \"S(KC_0)\": {\n            \"key\": \"KC_RPRN\",\n            \"label\": \")\",\n            \"aliases\": [\n                \"KC_RIGHT_PAREN\"\n            ]\n        },\n        \"S(KC_MINUS)\": {\n            \"key\": \"KC_UNDS\",\n            \"label\": \"_\",\n            \"aliases\": [\n                \"KC_UNDERSCORE\"\n            ]\n        },\n        \"S(KC_EQUAL)\": {\n            \"key\": \"KC_PLUS\",\n            \"label\": \"+\"\n        },\n        \"S(KC_LEFT_BRACKET)\": {\n            \"key\": \"KC_LCBR\",\n            \"label\": \"{\",\n            \"aliases\": [\n                \"KC_LEFT_CURLY_BRACE\"\n            ]\n        },\n        \"S(KC_RIGHT_BRACKET)\": {\n            \"key\": \"KC_RCBR\",\n            \"label\": \"}\",\n            \"aliases\": [\n                \"KC_RIGHT_CURLY_BRACE\"\n            ]\n        },\n        \"S(KC_BACKSLASH)\": {\n            \"key\": \"KC_PIPE\",\n            \"label\": \"|\"\n        },\n        \"S(KC_SEMICOLON)\": {\n            \"key\": \"KC_COLN\",\n            \"label\": \":\",\n            \"aliases\": [\n                \"KC_COLON\"\n            ]\n        },\n        \"S(KC_QUOTE)\": {\n            \"key\": \"KC_DQUO\",\n            \"label\": \"\\\"\",\n            \"aliases\": [\n                \"KC_DOUBLE_QUOTE\",\n                \"KC_DQT\"\n            ]\n        },\n        \"S(KC_COMMA)\": {\n            \"key\": \"KC_LABK\",\n            \"label\": \"<\",\n            \"aliases\": [\n                \"KC_LEFT_ANGLE_BRACKET\",\n                \"KC_LT\"\n            ]\n        },\n        \"S(KC_DOT)\": {\n            \"key\": \"KC_RABK\",\n            \"label\": \">\",\n            \"aliases\": [\n                \"KC_RIGHT_ANGLE_BRACKET\",\n                \"KC_GT\"\n            ]\n        },\n        \"S(KC_SLASH)\": {\n            \"key\": \"KC_QUES\",\n            \"label\": \"?\",\n            \"aliases\": [\n                \"KC_QUESTION\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_us_extended_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"US_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"US_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"US_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"US_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"US_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"US_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"US_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"US_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"US_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"US_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"US_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"US_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"US_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"US_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"US_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"US_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"US_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"US_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"US_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"US_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"US_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"US_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"US_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"US_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"US_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"US_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"US_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"US_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"US_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"US_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"US_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"US_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"US_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"US_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"US_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"US_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"US_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_Z\": {\n            \"key\": \"US_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"US_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"US_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"US_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"US_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"US_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"US_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"US_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"US_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"US_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │ : │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(US_GRV)\": {\n            \"key\": \"US_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(US_1)\": {\n            \"key\": \"US_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(US_2)\": {\n            \"key\": \"US_AT\",\n            \"label\": \"@\",\n        }\n        \"S(US_3)\": {\n            \"key\": \"US_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(US_4)\": {\n            \"key\": \"US_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(US_5)\": {\n            \"key\": \"US_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(US_6)\": {\n            \"key\": \"US_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(US_7)\": {\n            \"key\": \"US_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(US_8)\": {\n            \"key\": \"US_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(US_9)\": {\n            \"key\": \"US_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(US_0)\": {\n            \"key\": \"US_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(US_MINS)\": {\n            \"key\": \"US_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(US_EQL)\": {\n            \"key\": \"US_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(US_LBRC)\": {\n            \"key\": \"US_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(US_RBRC)\": {\n            \"key\": \"US_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(US_BSLS)\": {\n            \"key\": \"US_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(US_SCLN)\": {\n            \"key\": \"US_COLN\",\n            \"label\": \":\",\n        }\n        \"S(US_QUOT)\": {\n            \"key\": \"US_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(US_COMM)\": {\n            \"key\": \"US_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(US_DOT)\": {\n            \"key\": \"US_RABK\",\n            \"label\": \">\",\n        }\n        \"S(US_SLSH)\": {\n            \"key\": \"US_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ ¹ │ ² │ ³ │ ¤ │ € │ ^ │ ̛  │ ¾ │ ‘ │ ’ │ ¥ │ × │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Ä │ Å │ É │ ® │ Þ │ Ü │ Ú │ Í │ Ó │ Ö │ « │ » │  ¬  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ Á │ ß │ Ð │   │   │   │ Ï │ Œ │ Ø │ ¶ │ ' │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Æ │   │ © │   │   │ Ñ │ µ │ Ç │ ˙ │ ¿ │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(US_GRV)\": {\n            \"key\": \"US_DGRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"ALGR(US_1)\": {\n            \"key\": \"US_SUP1\",\n            \"label\": \"¹\",\n        }\n        \"ALGR(US_2)\": {\n            \"key\": \"US_SUP2\",\n            \"label\": \"²\",\n        }\n        \"ALGR(US_3)\": {\n            \"key\": \"US_SUP3\",\n            \"label\": \"³\",\n        }\n        \"ALGR(US_4)\": {\n            \"key\": \"US_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(US_5)\": {\n            \"key\": \"US_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(US_6)\": {\n            \"key\": \"US_DCIR\",\n            \"label\": \"^ (dead)\",\n        }\n        \"ALGR(US_7)\": {\n            \"key\": \"US_HORN\",\n            \"label\": \"̛ (dead)\",\n        }\n        \"ALGR(US_8)\": {\n            \"key\": \"US_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"ALGR(US_9)\": {\n            \"key\": \"US_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"ALGR(US_0)\": {\n            \"key\": \"US_RSQU\",\n            \"label\": \"’\",\n        }\n        \"ALGR(US_MINS)\": {\n            \"key\": \"US_YEN\",\n            \"label\": \"¥\",\n        }\n        \"ALGR(US_EQL)\": {\n            \"key\": \"US_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(US_Q)\": {\n            \"key\": \"US_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"ALGR(US_W)\": {\n            \"key\": \"US_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"ALGR(US_E)\": {\n            \"key\": \"US_EACU\",\n            \"label\": \"É\",\n        }\n        \"ALGR(US_R)\": {\n            \"key\": \"US_EDIA\",\n            \"label\": \"Ë\",\n        }\n        \"ALGR(US_T)\": {\n            \"key\": \"US_THRN\",\n            \"label\": \"Þ\",\n        }\n        \"ALGR(US_Y)\": {\n            \"key\": \"US_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"ALGR(US_U)\": {\n            \"key\": \"US_UACU\",\n            \"label\": \"Ú\",\n        }\n        \"ALGR(US_I)\": {\n            \"key\": \"US_IACU\",\n            \"label\": \"Í\",\n        }\n        \"ALGR(US_O)\": {\n            \"key\": \"US_OACU\",\n            \"label\": \"Ó\",\n        }\n        \"ALGR(US_P)\": {\n            \"key\": \"US_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"ALGR(US_LBRC)\": {\n            \"key\": \"US_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"ALGR(US_RBRC)\": {\n            \"key\": \"US_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"ALGR(US_BSLS)\": {\n            \"key\": \"US_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(US_A)\": {\n            \"key\": \"US_AACU\",\n            \"label\": \"Á\",\n        }\n        \"ALGR(US_S)\": {\n            \"key\": \"US_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(US_D)\": {\n            \"key\": \"US_ETH\",\n            \"label\": \"Ð\",\n        }\n        \"ALGR(US_J)\": {\n            \"key\": \"US_IDIA\",\n            \"label\": \"Ï\",\n        }\n        \"ALGR(US_K)\": {\n            \"key\": \"US_OE\",\n            \"label\": \"Œ\",\n        }\n        \"ALGR(US_L)\": {\n            \"key\": \"US_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"ALGR(US_SCLN)\": {\n            \"key\": \"US_PILC\",\n            \"label\": \"¶\",\n        }\n        \"ALGR(US_QUOT)\": {\n            \"key\": \"US_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"ALGR(US_Z)\": {\n            \"key\": \"US_AE\",\n            \"label\": \"Æ\",\n        }\n        \"ALGR(US_X)\": {\n            \"key\": \"US_OE_2\",\n            \"label\": \"Œ\",\n        }\n        \"ALGR(US_C)\": {\n            \"key\": \"US_COPY\",\n            \"label\": \"©\",\n        }\n        \"ALGR(US_V)\": {\n            \"key\": \"US_REGD\",\n            \"label\": \"®\",\n        }\n        \"ALGR(US_N)\": {\n            \"key\": \"US_NTIL\",\n            \"label\": \"Ñ\",\n        }\n        \"ALGR(US_M)\": {\n            \"key\": \"US_MICR\",\n            \"label\": \"µ\",\n        }\n        \"ALGR(US_COMM)\": {\n            \"key\": \"US_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"ALGR(US_DOT)\": {\n            \"key\": \"US_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n        \"ALGR(US_SLSH)\": {\n            \"key\": \"US_IQUE\",\n            \"label\": \"¿\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ¡ │ ˝ │ ¯ │ £ │ ¸ │ ¼ │ ½ │ ¾ │ ˘ │ ° │  ̣ │ ÷ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ “ │ ” │  ¦  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │ § │   │   │   │   │   │   │   │ ° │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │ ¢ │   │   │   │   │   │ ˇ │  ̉ │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ALGR(US_GRV))\": {\n            \"key\": \"US_DTIL\",\n            \"label\": \"~ (dead)\",\n        }\n        \"S(ALGR(US_1))\": {\n            \"key\": \"US_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"S(ALGR(US_2))\": {\n            \"key\": \"US_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"S(ALGR(US_3))\": {\n            \"key\": \"US_MACR\",\n            \"label\": \"¯ (dead)\",\n        }\n        \"S(ALGR(US_4))\": {\n            \"key\": \"US_PND\",\n            \"label\": \"£\",\n        }\n        \"S(ALGR(US_5))\": {\n            \"key\": \"US_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"S(ALGR(US_6))\": {\n            \"key\": \"US_QRTR\",\n            \"label\": \"¼\",\n        }\n        \"S(ALGR(US_7))\": {\n            \"key\": \"US_HALF\",\n            \"label\": \"½\",\n        }\n        \"S(ALGR(US_8))\": {\n            \"key\": \"US_TQTR\",\n            \"label\": \"¾\",\n        }\n        \"S(ALGR(US_9))\": {\n            \"key\": \"US_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"S(ALGR(US_0))\": {\n            \"key\": \"US_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"S(ALGR(US_MINS))\": {\n            \"key\": \"US_DOTB\",\n            \"label\": \"̣ (dead)\",\n        }\n        \"S(ALGR(US_EQL))\": {\n            \"key\": \"US_DIV\",\n            \"label\": \"÷\",\n        }\n        \"S(ALGR(US_LBRC))\": {\n            \"key\": \"US_LDQU\",\n            \"label\": \"“\",\n        }\n        \"S(ALGR(US_RBRC))\": {\n            \"key\": \"US_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(ALGR(US_BSLS))\": {\n            \"key\": \"US_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"S(ALGR(US_S))\": {\n            \"key\": \"US_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(ALGR(US_SCLN))\": {\n            \"key\": \"US_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(ALGR(US_QUOT))\": {\n            \"key\": \"US_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(ALGR(US_C))\": {\n            \"key\": \"US_CENT\",\n            \"label\": \"¢\",\n        }\n        \"S(ALGR(US_DOT))\": {\n            \"key\": \"US_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"S(ALGR(US_SLSH))\": {\n            \"key\": \"US_HOKA\",\n            \"label\": \"̉ (dead)\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_us_international_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ´ │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"US_DGRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"KC_1\": {\n            \"key\": \"US_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"US_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"US_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"US_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"US_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"US_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"US_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"US_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"US_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"US_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"US_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"US_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"US_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"US_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"US_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"US_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"US_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"US_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"US_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"US_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"US_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"US_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"US_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"US_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"US_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"US_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"US_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"US_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"US_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"US_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"US_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"US_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"US_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"US_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"US_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"US_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Z\": {\n            \"key\": \"US_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"US_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"US_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"US_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"US_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"US_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"US_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"US_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"US_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"US_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │ : │ ¨ │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(US_DGRV)\": {\n            \"key\": \"US_DTIL\",\n            \"label\": \"~ (dead)\",\n        }\n        \"S(US_1)\": {\n            \"key\": \"US_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(US_2)\": {\n            \"key\": \"US_AT\",\n            \"label\": \"@\",\n        }\n        \"S(US_3)\": {\n            \"key\": \"US_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(US_4)\": {\n            \"key\": \"US_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(US_5)\": {\n            \"key\": \"US_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(US_6)\": {\n            \"key\": \"US_DCIR\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(US_7)\": {\n            \"key\": \"US_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(US_8)\": {\n            \"key\": \"US_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(US_9)\": {\n            \"key\": \"US_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(US_0)\": {\n            \"key\": \"US_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(US_MINS)\": {\n            \"key\": \"US_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(US_EQL)\": {\n            \"key\": \"US_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(US_LBRC)\": {\n            \"key\": \"US_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(US_RBRC)\": {\n            \"key\": \"US_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(US_BSLS)\": {\n            \"key\": \"US_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(US_SCLN)\": {\n            \"key\": \"US_COLN\",\n            \"label\": \":\",\n        }\n        \"S(US_ACUT)\": {\n            \"key\": \"US_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(US_COMM)\": {\n            \"key\": \"US_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(US_DOT)\": {\n            \"key\": \"US_RABK\",\n            \"label\": \">\",\n        }\n        \"S(US_SLSH)\": {\n            \"key\": \"US_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ¡ │ ² │ ³ │ ¤ │ € │ ¼ │ ½ │ ¾ │ ‘ │ ’ │ ¥ │ × │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Ä │ Å │ É │ ® │ Þ │ Ü │ Ú │ Í │ Ó │ Ö │ « │ » │  ¬  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ Á │ ß │ Ð │   │   │   │   │   │ Ø │ ¶ │ ´ │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Æ │   │ © │   │   │ Ñ │ µ │ Ç │   │ ¿ │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(US_1)\": {\n            \"key\": \"US_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"ALGR(US_2)\": {\n            \"key\": \"US_SUP2\",\n            \"label\": \"²\",\n        }\n        \"ALGR(US_3)\": {\n            \"key\": \"US_SUP3\",\n            \"label\": \"³\",\n        }\n        \"ALGR(US_4)\": {\n            \"key\": \"US_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(US_5)\": {\n            \"key\": \"US_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(US_6)\": {\n            \"key\": \"US_QRTR\",\n            \"label\": \"¼\",\n        }\n        \"ALGR(US_7)\": {\n            \"key\": \"US_HALF\",\n            \"label\": \"½\",\n        }\n        \"ALGR(US_8)\": {\n            \"key\": \"US_TQTR\",\n            \"label\": \"¾\",\n        }\n        \"ALGR(US_9)\": {\n            \"key\": \"US_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"ALGR(US_0)\": {\n            \"key\": \"US_RSQU\",\n            \"label\": \"’\",\n        }\n        \"ALGR(US_MINS)\": {\n            \"key\": \"US_YEN\",\n            \"label\": \"¥\",\n        }\n        \"ALGR(US_EQL)\": {\n            \"key\": \"US_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(US_Q)\": {\n            \"key\": \"US_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"ALGR(US_W)\": {\n            \"key\": \"US_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"ALGR(US_E)\": {\n            \"key\": \"US_EACU\",\n            \"label\": \"É\",\n        }\n        \"ALGR(US_R)\": {\n            \"key\": \"US_REGD\",\n            \"label\": \"®\",\n        }\n        \"ALGR(US_T)\": {\n            \"key\": \"US_THRN\",\n            \"label\": \"Þ\",\n        }\n        \"ALGR(US_Y)\": {\n            \"key\": \"US_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"ALGR(US_U)\": {\n            \"key\": \"US_UACU\",\n            \"label\": \"Ú\",\n        }\n        \"ALGR(US_I)\": {\n            \"key\": \"US_IACU\",\n            \"label\": \"Í\",\n        }\n        \"ALGR(US_O)\": {\n            \"key\": \"US_OACU\",\n            \"label\": \"Ó\",\n        }\n        \"ALGR(US_P)\": {\n            \"key\": \"US_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"ALGR(US_LBRC)\": {\n            \"key\": \"US_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"ALGR(US_RBRC)\": {\n            \"key\": \"US_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"ALGR(US_BSLS)\": {\n            \"key\": \"US_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(US_A)\": {\n            \"key\": \"US_AACU\",\n            \"label\": \"Á\",\n        }\n        \"ALGR(US_S)\": {\n            \"key\": \"US_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(US_D)\": {\n            \"key\": \"US_ETH\",\n            \"label\": \"Ð\",\n        }\n        \"ALGR(US_L)\": {\n            \"key\": \"US_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"ALGR(US_SCLN)\": {\n            \"key\": \"US_PILC\",\n            \"label\": \"¶\",\n        }\n        \"ALGR(US_ACUT)\": {\n            \"key\": \"US_NDAC\",\n            \"label\": \"´\",\n        }\n        \"ALGR(US_Z)\": {\n            \"key\": \"US_AE\",\n            \"label\": \"Æ\",\n        }\n        \"ALGR(US_C)\": {\n            \"key\": \"US_COPY\",\n            \"label\": \"©\",\n        }\n        \"ALGR(US_N)\": {\n            \"key\": \"US_NTIL\",\n            \"label\": \"Ñ\",\n        }\n        \"ALGR(US_M)\": {\n            \"key\": \"US_MICR\",\n            \"label\": \"µ\",\n        }\n        \"ALGR(US_COMM)\": {\n            \"key\": \"US_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"ALGR(US_SLSH)\": {\n            \"key\": \"US_IQUE\",\n            \"label\": \"¿\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │   │ ¹ │   │   │ £ │   │   │   │   │   │   │   │ ÷ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │   │   │  ¦  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │ § │   │   │   │   │   │   │   │ ° │ ¨ │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │ ¢ │   │   │   │   │   │   │   │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ALGR(US_1))\": {\n            \"key\": \"US_SUP1\",\n            \"label\": \"¹\",\n        }\n        \"S(ALGR(US_4))\": {\n            \"key\": \"US_PND\",\n            \"label\": \"£\",\n        }\n        \"S(ALGR(US_EQL))\": {\n            \"key\": \"US_DIV\",\n            \"label\": \"÷\",\n        }\n        \"S(ALGR(US_BSLS))\": {\n            \"key\": \"US_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"S(ALGR(US_S))\": {\n            \"key\": \"US_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(ALGR(US_SCLN))\": {\n            \"key\": \"US_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(ALGR(US_ACUT))\": {\n            \"key\": \"US_NDDR\",\n            \"label\": \"¨\",\n        }\n        \"S(ALGR(US_C))\": {\n            \"key\": \"US_CENT\",\n            \"label\": \"¢\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_us_international_linux_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ´ │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"US_DGRV\",\n            \"label\": \"` (dead)\",\n        }\n        \"KC_1\": {\n            \"key\": \"US_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"US_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"US_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"US_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"US_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"US_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"US_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"US_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"US_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"US_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"US_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"US_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"US_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"US_W\",\n            \"label\": \"W\",\n        }\n        \"KC_E\": {\n            \"key\": \"US_E\",\n            \"label\": \"E\",\n        }\n        \"KC_R\": {\n            \"key\": \"US_R\",\n            \"label\": \"R\",\n        }\n        \"KC_T\": {\n            \"key\": \"US_T\",\n            \"label\": \"T\",\n        }\n        \"KC_Y\": {\n            \"key\": \"US_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_U\": {\n            \"key\": \"US_U\",\n            \"label\": \"U\",\n        }\n        \"KC_I\": {\n            \"key\": \"US_I\",\n            \"label\": \"I\",\n        }\n        \"KC_O\": {\n            \"key\": \"US_O\",\n            \"label\": \"O\",\n        }\n        \"KC_P\": {\n            \"key\": \"US_P\",\n            \"label\": \"P\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"US_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"US_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"US_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"US_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"US_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"US_D\",\n            \"label\": \"D\",\n        }\n        \"KC_F\": {\n            \"key\": \"US_F\",\n            \"label\": \"F\",\n        }\n        \"KC_G\": {\n            \"key\": \"US_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"US_H\",\n            \"label\": \"H\",\n        }\n        \"KC_J\": {\n            \"key\": \"US_J\",\n            \"label\": \"J\",\n        }\n        \"KC_K\": {\n            \"key\": \"US_K\",\n            \"label\": \"K\",\n        }\n        \"KC_L\": {\n            \"key\": \"US_L\",\n            \"label\": \"L\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"US_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"US_ACUT\",\n            \"label\": \"´ (dead)\",\n        }\n        \"KC_Z\": {\n            \"key\": \"US_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"US_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"US_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"US_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"US_B\",\n            \"label\": \"B\",\n        }\n        \"KC_N\": {\n            \"key\": \"US_N\",\n            \"label\": \"N\",\n        }\n        \"KC_M\": {\n            \"key\": \"US_M\",\n            \"label\": \"M\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"US_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"US_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"US_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │ : │ ¨ │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(US_DGRV)\": {\n            \"key\": \"US_DTIL\",\n            \"label\": \"~ (dead)\",\n        }\n        \"S(US_1)\": {\n            \"key\": \"US_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(US_2)\": {\n            \"key\": \"US_AT\",\n            \"label\": \"@\",\n        }\n        \"S(US_3)\": {\n            \"key\": \"US_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(US_4)\": {\n            \"key\": \"US_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(US_5)\": {\n            \"key\": \"US_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(US_6)\": {\n            \"key\": \"US_DCIR\",\n            \"label\": \"^ (dead)\",\n        }\n        \"S(US_7)\": {\n            \"key\": \"US_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(US_8)\": {\n            \"key\": \"US_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(US_9)\": {\n            \"key\": \"US_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(US_0)\": {\n            \"key\": \"US_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(US_MINS)\": {\n            \"key\": \"US_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(US_EQL)\": {\n            \"key\": \"US_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(US_LBRC)\": {\n            \"key\": \"US_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(US_RBRC)\": {\n            \"key\": \"US_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(US_BSLS)\": {\n            \"key\": \"US_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(US_SCLN)\": {\n            \"key\": \"US_COLN\",\n            \"label\": \":\",\n        }\n        \"S(US_ACUT)\": {\n            \"key\": \"US_DIAE\",\n            \"label\": \"¨ (dead)\",\n        }\n        \"S(US_COMM)\": {\n            \"key\": \"US_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(US_DOT)\": {\n            \"key\": \"US_RABK\",\n            \"label\": \">\",\n        }\n        \"S(US_SLSH)\": {\n            \"key\": \"US_QUES\",\n            \"label\": \"?\",\n        }\n/* AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ ¡ │ ² │ ³ │ ¤ │ € │ ¼ │ ½ │ ¾ │ ‘ │ ’ │ ¥ │ × │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Ä │ Å │ É │ ® │ Þ │ Ü │ Ú │ Í │ Ó │ Ö │ « │ » │  ¬  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ Á │ ß │ Ð │   │   │   │   │ Œ │ Ø │ ¶ │ ' │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Æ │   │ © │   │   │ Ñ │ µ │ Ç │ ˙ │ ¿ │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"ALGR(US_DGRV)\": {\n            \"key\": \"US_GRV\",\n            \"label\": \"`\",\n        }\n        \"ALGR(US_1)\": {\n            \"key\": \"US_IEXL\",\n            \"label\": \"¡\",\n        }\n        \"ALGR(US_2)\": {\n            \"key\": \"US_SUP2\",\n            \"label\": \"²\",\n        }\n        \"ALGR(US_3)\": {\n            \"key\": \"US_SUP3\",\n            \"label\": \"³\",\n        }\n        \"ALGR(US_4)\": {\n            \"key\": \"US_CURR\",\n            \"label\": \"¤\",\n        }\n        \"ALGR(US_5)\": {\n            \"key\": \"US_EURO\",\n            \"label\": \"€\",\n        }\n        \"ALGR(US_6)\": {\n            \"key\": \"US_QRTR\",\n            \"label\": \"¼\",\n        }\n        \"ALGR(US_7)\": {\n            \"key\": \"US_HALF\",\n            \"label\": \"½\",\n        }\n        \"ALGR(US_8)\": {\n            \"key\": \"US_TQTR\",\n            \"label\": \"¾\",\n        }\n        \"ALGR(US_9)\": {\n            \"key\": \"US_LSQU\",\n            \"label\": \"‘\",\n        }\n        \"ALGR(US_0)\": {\n            \"key\": \"US_RSQU\",\n            \"label\": \"’\",\n        }\n        \"ALGR(US_MINS)\": {\n            \"key\": \"US_YEN\",\n            \"label\": \"¥\",\n        }\n        \"ALGR(US_EQL)\": {\n            \"key\": \"US_MUL\",\n            \"label\": \"×\",\n        }\n        \"ALGR(US_Q)\": {\n            \"key\": \"US_ADIA\",\n            \"label\": \"Ä\",\n        }\n        \"ALGR(US_W)\": {\n            \"key\": \"US_ARNG\",\n            \"label\": \"Å\",\n        }\n        \"ALGR(US_E)\": {\n            \"key\": \"US_EACU\",\n            \"label\": \"É\",\n        }\n        \"ALGR(US_R)\": {\n            \"key\": \"US_REGD\",\n            \"label\": \"®\",\n        }\n        \"ALGR(US_T)\": {\n            \"key\": \"US_THRN\",\n            \"label\": \"Þ\",\n        }\n        \"ALGR(US_Y)\": {\n            \"key\": \"US_UDIA\",\n            \"label\": \"Ü\",\n        }\n        \"ALGR(US_U)\": {\n            \"key\": \"US_UACU\",\n            \"label\": \"Ú\",\n        }\n        \"ALGR(US_I)\": {\n            \"key\": \"US_IACU\",\n            \"label\": \"Í\",\n        }\n        \"ALGR(US_O)\": {\n            \"key\": \"US_OACU\",\n            \"label\": \"Ó\",\n        }\n        \"ALGR(US_P)\": {\n            \"key\": \"US_ODIA\",\n            \"label\": \"Ö\",\n        }\n        \"ALGR(US_LBRC)\": {\n            \"key\": \"US_LDAQ\",\n            \"label\": \"«\",\n        }\n        \"ALGR(US_RBRC)\": {\n            \"key\": \"US_RDAQ\",\n            \"label\": \"»\",\n        }\n        \"ALGR(US_BSLS)\": {\n            \"key\": \"US_NOT\",\n            \"label\": \"¬\",\n        }\n        \"ALGR(US_A)\": {\n            \"key\": \"US_AACU\",\n            \"label\": \"Á\",\n        }\n        \"ALGR(US_S)\": {\n            \"key\": \"US_SS\",\n            \"label\": \"ß\",\n        }\n        \"ALGR(US_D)\": {\n            \"key\": \"US_ETH\",\n            \"label\": \"Ð\",\n        }\n        \"ALGR(US_K)\": {\n            \"key\": \"US_OE\",\n            \"label\": \"Œ\",\n        }\n        \"ALGR(US_L)\": {\n            \"key\": \"US_OSTR\",\n            \"label\": \"Ø\",\n        }\n        \"ALGR(US_SCLN)\": {\n            \"key\": \"US_PILC\",\n            \"label\": \"¶\",\n        }\n        \"ALGR(US_ACUT)\": {\n            \"key\": \"US_QUOT\",\n            \"label\": \"'\",\n        }\n        \"ALGR(US_Z)\": {\n            \"key\": \"US_AE\",\n            \"label\": \"Æ\",\n        }\n        \"ALGR(US_C)\": {\n            \"key\": \"US_COPY\",\n            \"label\": \"©\",\n        }\n        \"ALGR(US_N)\": {\n            \"key\": \"US_NTIL\",\n            \"label\": \"Ñ\",\n        }\n        \"ALGR(US_M)\": {\n            \"key\": \"US_MICR\",\n            \"label\": \"µ\",\n        }\n        \"ALGR(US_COMM)\": {\n            \"key\": \"US_CCED\",\n            \"label\": \"Ç\",\n        }\n        \"ALGR(US_DOT)\": {\n            \"key\": \"US_DOTA\",\n            \"label\": \"˙ (dead)\",\n        }\n        \"ALGR(US_SLSH)\": {\n            \"key\": \"US_IQUE\",\n            \"label\": \"¿\",\n        }\n/* Shift+AltGr symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ¹ │ ˝ │ ¯ │ £ │ ¸ │ ^ │ ̛  │ ˛ │ ˘ │ ° │ ̣  │ ÷ │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │   │ “ │ ” │  ¦  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │ § │   │   │   │   │   │   │   │ ° │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │ ¢ │   │   │   │   │   │ ˇ │ ̉  │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(ALGR(US_DGRV))\": {\n            \"key\": \"US_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(ALGR(US_1))\": {\n            \"key\": \"US_SUP1\",\n            \"label\": \"¹\",\n        }\n        \"S(ALGR(US_2))\": {\n            \"key\": \"US_DACU\",\n            \"label\": \"˝ (dead)\",\n        }\n        \"S(ALGR(US_3))\": {\n            \"key\": \"US_MACR\",\n            \"label\": \"¯ (dead)\",\n        }\n        \"S(ALGR(US_4))\": {\n            \"key\": \"US_PND\",\n            \"label\": \"£\",\n        }\n        \"S(ALGR(US_5))\": {\n            \"key\": \"US_CEDL\",\n            \"label\": \"¸ (dead)\",\n        }\n        \"S(ALGR(US_6))\": {\n            \"key\": \"US_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(ALGR(US_7))\": {\n            \"key\": \"US_HORN\",\n            \"label\": \"̛ (dead)\",\n        }\n        \"S(ALGR(US_8))\": {\n            \"key\": \"US_OGON\",\n            \"label\": \"˛ (dead)\",\n        }\n        \"S(ALGR(US_9))\": {\n            \"key\": \"US_BREV\",\n            \"label\": \"˘ (dead)\",\n        }\n        \"S(ALGR(US_0))\": {\n            \"key\": \"US_RNGA\",\n            \"label\": \"° (dead)\",\n        }\n        \"S(ALGR(US_MINS))\": {\n            \"key\": \"US_DOTB\",\n            \"label\": \"̣ (dead)\",\n        }\n        \"S(ALGR(US_EQL))\": {\n            \"key\": \"US_DIV\",\n            \"label\": \"÷\",\n        }\n        \"S(ALGR(US_LBRC))\": {\n            \"key\": \"US_LDQU\",\n            \"label\": \"“\",\n        }\n        \"S(ALGR(US_RBRC))\": {\n            \"key\": \"US_RDQU\",\n            \"label\": \"”\",\n        }\n        \"S(ALGR(US_BSLS))\": {\n            \"key\": \"US_BRKP\",\n            \"label\": \"¦\",\n        }\n        \"S(ALGR(US_S))\": {\n            \"key\": \"US_SECT\",\n            \"label\": \"§\",\n        }\n        \"S(ALGR(US_SCLN))\": {\n            \"key\": \"US_DEG\",\n            \"label\": \"°\",\n        }\n        \"S(ALGR(US_ACUT))\": {\n            \"key\": \"US_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(ALGR(US_C))\": {\n            \"key\": \"US_CENT\",\n            \"label\": \"¢\",\n        }\n        \"S(ALGR(US_DOT))\": {\n            \"key\": \"US_CARN\",\n            \"label\": \"ˇ (dead)\",\n        }\n        \"S(ALGR(US_SLSH))\": {\n            \"key\": \"US_HOKA\",\n            \"label\": \"̉ (dead)\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_workman_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ D │ R │ W │ B │ J │ F │ U │ P │ ; │ [ │ ] │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ S │ H │ T │ G │ Y │ N │ E │ O │ I │ ' │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Z │ X │ M │ C │ V │ K │ L │ , │ . │ / │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"WK_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"WK_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"WK_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"WK_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"WK_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"WK_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"WK_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"WK_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"WK_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"WK_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"WK_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"WK_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"WK_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"WK_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"WK_D\",\n            \"label\": \"D\",\n        }\n        \"KC_E\": {\n            \"key\": \"WK_R\",\n            \"label\": \"R\",\n        }\n        \"KC_R\": {\n            \"key\": \"WK_W\",\n            \"label\": \"W\",\n        }\n        \"KC_T\": {\n            \"key\": \"WK_B\",\n            \"label\": \"B\",\n        }\n        \"KC_Y\": {\n            \"key\": \"WK_J\",\n            \"label\": \"J\",\n        }\n        \"KC_U\": {\n            \"key\": \"WK_F\",\n            \"label\": \"F\",\n        }\n        \"KC_I\": {\n            \"key\": \"WK_U\",\n            \"label\": \"U\",\n        }\n        \"KC_O\": {\n            \"key\": \"WK_P\",\n            \"label\": \"P\",\n        }\n        \"KC_P\": {\n            \"key\": \"WK_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"WK_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"WK_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"WK_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"WK_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"WK_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"WK_H\",\n            \"label\": \"H\",\n        }\n        \"KC_F\": {\n            \"key\": \"WK_T\",\n            \"label\": \"T\",\n        }\n        \"KC_G\": {\n            \"key\": \"WK_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"WK_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_J\": {\n            \"key\": \"WK_N\",\n            \"label\": \"N\",\n        }\n        \"KC_K\": {\n            \"key\": \"WK_E\",\n            \"label\": \"E\",\n        }\n        \"KC_L\": {\n            \"key\": \"WK_O\",\n            \"label\": \"O\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"WK_I\",\n            \"label\": \"I\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"WK_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_Z\": {\n            \"key\": \"WK_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"WK_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"WK_M\",\n            \"label\": \"M\",\n        }\n        \"KC_V\": {\n            \"key\": \"WK_C\",\n            \"label\": \"C\",\n        }\n        \"KC_B\": {\n            \"key\": \"WK_V\",\n            \"label\": \"V\",\n        }\n        \"KC_N\": {\n            \"key\": \"WK_K\",\n            \"label\": \"K\",\n        }\n        \"KC_M\": {\n            \"key\": \"WK_L\",\n            \"label\": \"L\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"WK_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"WK_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"WK_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │ : │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(WK_GRV)\": {\n            \"key\": \"WK_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(WK_1)\": {\n            \"key\": \"WK_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(WK_2)\": {\n            \"key\": \"WK_AT\",\n            \"label\": \"@\",\n        }\n        \"S(WK_3)\": {\n            \"key\": \"WK_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(WK_4)\": {\n            \"key\": \"WK_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(WK_5)\": {\n            \"key\": \"WK_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(WK_6)\": {\n            \"key\": \"WK_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(WK_7)\": {\n            \"key\": \"WK_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(WK_8)\": {\n            \"key\": \"WK_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(WK_9)\": {\n            \"key\": \"WK_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(WK_0)\": {\n            \"key\": \"WK_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(WK_MINS)\": {\n            \"key\": \"WK_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(WK_EQL)\": {\n            \"key\": \"WK_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(WK_SCLN)\": {\n            \"key\": \"WK_COLN\",\n            \"label\": \":\",\n        }\n        \"S(WK_LBRC)\": {\n            \"key\": \"WK_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(WK_RBRC)\": {\n            \"key\": \"WK_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(WK_BSLS)\": {\n            \"key\": \"WK_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(WK_QUOT)\": {\n            \"key\": \"WK_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(WK_COMM)\": {\n            \"key\": \"WK_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(WK_DOT)\": {\n            \"key\": \"WK_RABK\",\n            \"label\": \">\",\n        }\n        \"S(WK_SLSH)\": {\n            \"key\": \"WK_QUES\",\n            \"label\": \"?\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/extras/keycodes_workman_zxcvm_0.0.1.hjson",
    "content": "{\n    \"aliases\": {\n/*\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │ Q │ D │ R │ W │ B │ J │ F │ U │ P │ ; │ [ │ ] │  \\  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │ A │ S │ H │ T │ G │ Y │ N │ E │ O │ I │ ' │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │ Z │ X │ C │ V │ M │ K │ L │ , │ . │ / │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"KC_GRV\": {\n            \"key\": \"WK_GRV\",\n            \"label\": \"`\",\n        }\n        \"KC_1\": {\n            \"key\": \"WK_1\",\n            \"label\": \"1\",\n        }\n        \"KC_2\": {\n            \"key\": \"WK_2\",\n            \"label\": \"2\",\n        }\n        \"KC_3\": {\n            \"key\": \"WK_3\",\n            \"label\": \"3\",\n        }\n        \"KC_4\": {\n            \"key\": \"WK_4\",\n            \"label\": \"4\",\n        }\n        \"KC_5\": {\n            \"key\": \"WK_5\",\n            \"label\": \"5\",\n        }\n        \"KC_6\": {\n            \"key\": \"WK_6\",\n            \"label\": \"6\",\n        }\n        \"KC_7\": {\n            \"key\": \"WK_7\",\n            \"label\": \"7\",\n        }\n        \"KC_8\": {\n            \"key\": \"WK_8\",\n            \"label\": \"8\",\n        }\n        \"KC_9\": {\n            \"key\": \"WK_9\",\n            \"label\": \"9\",\n        }\n        \"KC_0\": {\n            \"key\": \"WK_0\",\n            \"label\": \"0\",\n        }\n        \"KC_MINS\": {\n            \"key\": \"WK_MINS\",\n            \"label\": \"-\",\n        }\n        \"KC_EQL\": {\n            \"key\": \"WK_EQL\",\n            \"label\": \"=\",\n        }\n        \"KC_Q\": {\n            \"key\": \"WK_Q\",\n            \"label\": \"Q\",\n        }\n        \"KC_W\": {\n            \"key\": \"WK_D\",\n            \"label\": \"D\",\n        }\n        \"KC_E\": {\n            \"key\": \"WK_R\",\n            \"label\": \"R\",\n        }\n        \"KC_R\": {\n            \"key\": \"WK_W\",\n            \"label\": \"W\",\n        }\n        \"KC_T\": {\n            \"key\": \"WK_B\",\n            \"label\": \"B\",\n        }\n        \"KC_Y\": {\n            \"key\": \"WK_J\",\n            \"label\": \"J\",\n        }\n        \"KC_U\": {\n            \"key\": \"WK_F\",\n            \"label\": \"F\",\n        }\n        \"KC_I\": {\n            \"key\": \"WK_U\",\n            \"label\": \"U\",\n        }\n        \"KC_O\": {\n            \"key\": \"WK_P\",\n            \"label\": \"P\",\n        }\n        \"KC_P\": {\n            \"key\": \"WK_SCLN\",\n            \"label\": \";\",\n        }\n        \"KC_LBRC\": {\n            \"key\": \"WK_LBRC\",\n            \"label\": \"[\",\n        }\n        \"KC_RBRC\": {\n            \"key\": \"WK_RBRC\",\n            \"label\": \"]\",\n        }\n        \"KC_BSLS\": {\n            \"key\": \"WK_BSLS\",\n            \"label\": \"\\\\\",\n        }\n        \"KC_A\": {\n            \"key\": \"WK_A\",\n            \"label\": \"A\",\n        }\n        \"KC_S\": {\n            \"key\": \"WK_S\",\n            \"label\": \"S\",\n        }\n        \"KC_D\": {\n            \"key\": \"WK_H\",\n            \"label\": \"H\",\n        }\n        \"KC_F\": {\n            \"key\": \"WK_T\",\n            \"label\": \"T\",\n        }\n        \"KC_G\": {\n            \"key\": \"WK_G\",\n            \"label\": \"G\",\n        }\n        \"KC_H\": {\n            \"key\": \"WK_Y\",\n            \"label\": \"Y\",\n        }\n        \"KC_J\": {\n            \"key\": \"WK_N\",\n            \"label\": \"N\",\n        }\n        \"KC_K\": {\n            \"key\": \"WK_E\",\n            \"label\": \"E\",\n        }\n        \"KC_L\": {\n            \"key\": \"WK_O\",\n            \"label\": \"O\",\n        }\n        \"KC_SCLN\": {\n            \"key\": \"WK_I\",\n            \"label\": \"I\",\n        }\n        \"KC_QUOT\": {\n            \"key\": \"WK_QUOT\",\n            \"label\": \"'\",\n        }\n        \"KC_Z\": {\n            \"key\": \"WK_Z\",\n            \"label\": \"Z\",\n        }\n        \"KC_X\": {\n            \"key\": \"WK_X\",\n            \"label\": \"X\",\n        }\n        \"KC_C\": {\n            \"key\": \"WK_C\",\n            \"label\": \"C\",\n        }\n        \"KC_V\": {\n            \"key\": \"WK_V\",\n            \"label\": \"V\",\n        }\n        \"KC_B\": {\n            \"key\": \"WK_M\",\n            \"label\": \"M\",\n        }\n        \"KC_N\": {\n            \"key\": \"WK_K\",\n            \"label\": \"K\",\n        }\n        \"KC_M\": {\n            \"key\": \"WK_L\",\n            \"label\": \"L\",\n        }\n        \"KC_COMM\": {\n            \"key\": \"WK_COMM\",\n            \"label\": \",\",\n        }\n        \"KC_DOT\": {\n            \"key\": \"WK_DOT\",\n            \"label\": \".\",\n        }\n        \"KC_SLSH\": {\n            \"key\": \"WK_SLSH\",\n            \"label\": \"/\",\n        }\n/* Shifted symbols\n * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │ _ │ + │       │\n * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n * │     │   │   │   │   │   │   │   │   │   │ : │ { │ } │  |  │\n * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n * │      │   │   │   │   │   │   │   │   │   │   │ \" │        │\n * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n * │        │   │   │   │   │   │   │   │ < │ > │ ? │          │\n * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n * │    │    │    │                        │    │    │    │    │\n * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n */\n        \"S(WK_GRV)\": {\n            \"key\": \"WK_TILD\",\n            \"label\": \"~\",\n        }\n        \"S(WK_1)\": {\n            \"key\": \"WK_EXLM\",\n            \"label\": \"!\",\n        }\n        \"S(WK_2)\": {\n            \"key\": \"WK_AT\",\n            \"label\": \"@\",\n        }\n        \"S(WK_3)\": {\n            \"key\": \"WK_HASH\",\n            \"label\": \"#\",\n        }\n        \"S(WK_4)\": {\n            \"key\": \"WK_DLR\",\n            \"label\": \"$\",\n        }\n        \"S(WK_5)\": {\n            \"key\": \"WK_PERC\",\n            \"label\": \"%\",\n        }\n        \"S(WK_6)\": {\n            \"key\": \"WK_CIRC\",\n            \"label\": \"^\",\n        }\n        \"S(WK_7)\": {\n            \"key\": \"WK_AMPR\",\n            \"label\": \"&\",\n        }\n        \"S(WK_8)\": {\n            \"key\": \"WK_ASTR\",\n            \"label\": \"*\",\n        }\n        \"S(WK_9)\": {\n            \"key\": \"WK_LPRN\",\n            \"label\": \"(\",\n        }\n        \"S(WK_0)\": {\n            \"key\": \"WK_RPRN\",\n            \"label\": \")\",\n        }\n        \"S(WK_MINS)\": {\n            \"key\": \"WK_UNDS\",\n            \"label\": \"_\",\n        }\n        \"S(WK_EQL)\": {\n            \"key\": \"WK_PLUS\",\n            \"label\": \"+\",\n        }\n        \"S(WK_SCLN)\": {\n            \"key\": \"WK_COLN\",\n            \"label\": \":\",\n        }\n        \"S(WK_LBRC)\": {\n            \"key\": \"WK_LCBR\",\n            \"label\": \"{\",\n        }\n        \"S(WK_RBRC)\": {\n            \"key\": \"WK_RCBR\",\n            \"label\": \"}\",\n        }\n        \"S(WK_BSLS)\": {\n            \"key\": \"WK_PIPE\",\n            \"label\": \"|\",\n        }\n        \"S(WK_QUOT)\": {\n            \"key\": \"WK_DQUO\",\n            \"label\": \"\\\"\",\n        }\n        \"S(WK_COMM)\": {\n            \"key\": \"WK_LABK\",\n            \"label\": \"<\",\n        }\n        \"S(WK_DOT)\": {\n            \"key\": \"WK_RABK\",\n            \"label\": \">\",\n        }\n        \"S(WK_SLSH)\": {\n            \"key\": \"WK_QUES\",\n            \"label\": \"?\",\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1.hjson",
    "content": "{\n    \"ranges\": {\n        \"0x0000/0x00FF\": {\n            \"define\": \"QK_BASIC\"\n        },\n        \"0x0100/0x1EFF\": {\n            \"define\": \"QK_MODS\"\n        },\n        \"0x2000/0x1FFF\": {\n            \"define\": \"QK_MOD_TAP\"\n        },\n        \"0x4000/0x0FFF\": {\n            \"define\": \"QK_LAYER_TAP\"\n        },\n        \"0x5000/0x01FF\": {\n            \"define\": \"QK_LAYER_MOD\"\n        },\n        \"0x5200/0x001F\": {\n            \"define\": \"QK_TO\"\n        },\n        \"0x5220/0x001F\": {\n            \"define\": \"QK_MOMENTARY\"\n        },\n        \"0x5240/0x001F\": {\n            \"define\": \"QK_DEF_LAYER\"\n        },\n        \"0x5260/0x001F\": {\n            \"define\": \"QK_TOGGLE_LAYER\"\n        },\n        \"0x5280/0x001F\": {\n            \"define\": \"QK_ONE_SHOT_LAYER\"\n        },\n        \"0x52A0/0x001F\": {\n            \"define\": \"QK_ONE_SHOT_MOD\"\n        },\n        \"0x52C0/0x001F\": {\n            \"define\": \"QK_LAYER_TAP_TOGGLE\"\n        },\n        // 0x52E0/0x001F - UNUSED\n        // 0x5300/0x02FF - UNUSED\n        \"0x5600/0x00FF\": {\n            \"define\": \"QK_SWAP_HANDS\"\n        },\n        \"0x5700/0x00FF\": {\n            \"define\": \"QK_TAP_DANCE\"\n        },\n        // 0x5800/0x17FF - UNUSED\n        \"0x7000/0x00FF\": {\n            \"define\": \"QK_MAGIC\"\n        },\n        \"0x7100/0x00FF\": {\n            \"define\": \"QK_MIDI\"\n        },\n        \"0x7200/0x01FF\": {\n            \"define\": \"QK_SEQUENCER\"\n        },\n        \"0x7400/0x003F\": {\n            \"define\": \"QK_JOYSTICK\"\n        },\n        \"0x7440/0x003F\": {\n            \"define\": \"QK_PROGRAMMABLE_BUTTON\"\n        },\n        \"0x7480/0x003F\": {\n            \"define\": \"QK_AUDIO\"\n        },\n        \"0x74C0/0x003F\": {\n            \"define\": \"QK_STENO\"\n        },\n        // 0x7500/0x01FF - UNUSED\n        \"0x7700/0x007F\": {\n            \"define\": \"QK_MACRO\"\n        },\n        // 0x7780/0x007F - UNUSED\n        \"0x7800/0x00FF\": {\n            \"define\": \"QK_LIGHTING\"\n        },\n        // 0x7900/0x02FF - UNUSED\n        \"0x7C00/0x01FF\": {\n            \"define\": \"QK_QUANTUM\"\n        },\n        \"0x7E00/0x00FF\": {\n            \"define\": \"QK_KB\"\n        },\n        \"0x7F00/0x00FF\": {\n            \"define\": \"QK_USER\"\n        },\n        \"0x8000/0x7FFF\": {\n            \"define\": \"QK_UNICODE\"\n        }\n    },\n    \"keycodes\": {\n        \"0x7E00\": {\n            \"key\": \"SAFE_RANGE\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_audio.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7480\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_AUDIO_ON\",\n            \"aliases\": [\n                \"AU_ON\"\n            ]\n        },\n        \"0x7481\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_AUDIO_OFF\",\n            \"aliases\": [\n                \"AU_OFF\"\n            ]\n        },\n        \"0x7482\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_AUDIO_TOGGLE\",\n            \"aliases\": [\n                \"AU_TOGG\"\n            ]\n        },\n\n        \"0x748A\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_AUDIO_CLICKY_TOGGLE\",\n            \"aliases\": [\n                \"CK_TOGG\"\n            ]\n        },\n        \"0x748B\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_AUDIO_CLICKY_ON\",\n            \"aliases\": [\n                \"CK_ON\"\n            ]\n        },\n        \"0x748C\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_AUDIO_CLICKY_OFF\",\n            \"aliases\": [\n                \"CK_OFF\"\n            ]\n        },\n        \"0x748D\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_AUDIO_CLICKY_UP\",\n            \"aliases\": [\n                \"CK_UP\"\n            ]\n        },\n        \"0x748E\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_AUDIO_CLICKY_DOWN\",\n            \"aliases\": [\n                \"CK_DOWN\"\n            ]\n        },\n        \"0x748F\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_AUDIO_CLICKY_RESET\",\n            \"aliases\": [\n                \"CK_RST\"\n            ]\n        },\n\n        \"0x7490\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_MUSIC_ON\",\n            \"aliases\": [\n                \"MU_ON\"\n            ]\n        },\n        \"0x7491\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_MUSIC_OFF\",\n            \"aliases\": [\n                \"MU_OFF\"\n            ]\n        },\n        \"0x7492\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_MUSIC_TOGGLE\",\n            \"aliases\": [\n                \"MU_TOGG\"\n            ]\n        },\n        \"0x7493\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_MUSIC_MODE_NEXT\",\n            \"aliases\": [\n                \"MU_NEXT\"\n            ]\n        },\n\n        \"0x7494\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_AUDIO_VOICE_NEXT\",\n            \"aliases\": [\n                \"AU_NEXT\"\n            ]\n        },\n        \"0x7495\": {\n            \"group\": \"audio\",\n            \"key\": \"QK_AUDIO_VOICE_PREVIOUS\",\n            \"aliases\": [\n                \"AU_PREV\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_basic.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x0000\": {\n            \"group\": \"internal\",\n            \"key\": \"KC_NO\",\n            \"label\": \"\",\n            \"aliases\": [\n                \"XXXXXXX\"\n            ]\n        },\n        \"0x0001\": {\n            \"group\": \"internal\",\n            \"key\": \"KC_TRANSPARENT\",\n            \"label\": \"\",\n            \"aliases\": [\n                \"_______\",\n                \"KC_TRNS\"\n            ]\n        },\n        \"0x0004\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_A\",\n            \"label\": \"A\"\n        },\n        \"0x0005\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_B\",\n            \"label\": \"B\"\n        },\n        \"0x0006\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_C\",\n            \"label\": \"C\"\n        },\n        \"0x0007\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_D\",\n            \"label\": \"D\"\n        },\n        \"0x0008\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_E\",\n            \"label\": \"E\"\n        },\n        \"0x0009\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F\",\n            \"label\": \"F\"\n        },\n        \"0x000A\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_G\",\n            \"label\": \"G\"\n        },\n        \"0x000B\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_H\",\n            \"label\": \"H\"\n        },\n        \"0x000C\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_I\",\n            \"label\": \"I\"\n        },\n        \"0x000D\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_J\",\n            \"label\": \"J\"\n        },\n        \"0x000E\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_K\",\n            \"label\": \"K\"\n        },\n        \"0x000F\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_L\",\n            \"label\": \"L\"\n        },\n        \"0x0010\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_M\",\n            \"label\": \"M\"\n        },\n        \"0x0011\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_N\",\n            \"label\": \"N\"\n        },\n        \"0x0012\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_O\",\n            \"label\": \"O\"\n        },\n        \"0x0013\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_P\",\n            \"label\": \"P\"\n        },\n        \"0x0014\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_Q\",\n            \"label\": \"Q\"\n        },\n        \"0x0015\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_R\",\n            \"label\": \"R\"\n        },\n        \"0x0016\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_S\",\n            \"label\": \"S\"\n        },\n        \"0x0017\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_T\",\n            \"label\": \"T\"\n        },\n        \"0x0018\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_U\",\n            \"label\": \"U\"\n        },\n        \"0x0019\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_V\",\n            \"label\": \"V\"\n        },\n        \"0x001A\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_W\",\n            \"label\": \"W\"\n        },\n        \"0x001B\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_X\",\n            \"label\": \"X\"\n        },\n        \"0x001C\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_Y\",\n            \"label\": \"Y\"\n        },\n        \"0x001D\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_Z\",\n            \"label\": \"Z\"\n        },\n        \"0x001E\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_1\",\n            \"label\": \"1\"\n        },\n        \"0x001F\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_2\",\n            \"label\": \"2\"\n        },\n        \"0x0020\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_3\",\n            \"label\": \"3\"\n        },\n        \"0x0021\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_4\",\n            \"label\": \"4\"\n        },\n        \"0x0022\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_5\",\n            \"label\": \"5\"\n        },\n        \"0x0023\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_6\",\n            \"label\": \"6\"\n        },\n        \"0x0024\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_7\",\n            \"label\": \"7\"\n        },\n        \"0x0025\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_8\",\n            \"label\": \"8\"\n        },\n        \"0x0026\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_9\",\n            \"label\": \"9\"\n        },\n        \"0x0027\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_0\",\n            \"label\": \"0\"\n        },\n        \"0x0028\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_ENTER\",\n            \"label\": \"Enter\",\n            \"aliases\": [\n                \"KC_ENT\"\n            ]\n        },\n        \"0x0029\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_ESCAPE\",\n            \"label\": \"Esc\",\n            \"aliases\": [\n                \"KC_ESC\"\n            ]\n        },\n        \"0x002A\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_BACKSPACE\",\n            \"label\": \"Backspace\",\n            \"aliases\": [\n                \"KC_BSPC\"\n            ]\n        },\n        \"0x002B\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_TAB\",\n            \"label\": \"Tab\"\n        },\n        \"0x002C\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_SPACE\",\n            \"label\": \"Spacebar\",\n            \"aliases\": [\n                \"KC_SPC\"\n            ]\n        },\n        \"0x002D\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_MINUS\",\n            \"label\": \"-\",\n            \"aliases\": [\n                \"KC_MINS\"\n            ]\n        },\n        \"0x002E\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_EQUAL\",\n            \"label\": \"=\",\n            \"aliases\": [\n                \"KC_EQL\"\n            ]\n        },\n        \"0x002F\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LEFT_BRACKET\",\n            \"label\": \"[\",\n            \"aliases\": [\n                \"KC_LBRC\"\n            ]\n        },\n        \"0x0030\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_RIGHT_BRACKET\",\n            \"label\": \"]\",\n            \"aliases\": [\n                \"KC_RBRC\"\n            ]\n        },\n        \"0x0031\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_BACKSLASH\",\n            \"label\": \"\\\\\",\n            \"aliases\": [\n                \"KC_BSLS\"\n            ]\n        },\n        \"0x0032\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_NONUS_HASH\",\n            \"label\": \"#\",\n            \"aliases\": [\n                \"KC_NUHS\"\n            ]\n        },\n        \"0x0033\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_SEMICOLON\",\n            \"label\": \";\",\n            \"aliases\": [\n                \"KC_SCLN\"\n            ]\n        },\n        \"0x0034\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_QUOTE\",\n            \"label\": \"'\",\n            \"aliases\": [\n                \"KC_QUOT\"\n            ]\n        },\n        \"0x0035\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_GRAVE\",\n            \"label\": \"`\",\n            \"aliases\": [\n                \"KC_GRV\"\n            ]\n        },\n        \"0x0036\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_COMMA\",\n            \"label\": \",\",\n            \"aliases\": [\n                \"KC_COMM\"\n            ]\n        },\n        \"0x0037\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_DOT\",\n            \"label\": \".\"\n        },\n        \"0x0038\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_SLASH\",\n            \"label\": \"/\",\n            \"aliases\": [\n                \"KC_SLSH\"\n            ]\n        },\n        \"0x0039\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_CAPS_LOCK\",\n            \"label\": \"Caps Lock\",\n            \"aliases\": [\n                \"KC_CAPS\"\n            ]\n        },\n        \"0x003A\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F1\",\n            \"label\": \"F1\"\n        },\n        \"0x003B\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F2\",\n            \"label\": \"F2\"\n        },\n        \"0x003C\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F3\",\n            \"label\": \"F3\"\n        },\n        \"0x003D\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F4\",\n            \"label\": \"F4\"\n        },\n        \"0x003E\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F5\",\n            \"label\": \"F5\"\n        },\n        \"0x003F\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F6\",\n            \"label\": \"F6\"\n        },\n        \"0x0040\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F7\",\n            \"label\": \"F7\"\n        },\n        \"0x0041\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F8\",\n            \"label\": \"F8\"\n        },\n        \"0x0042\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F9\",\n            \"label\": \"F9\"\n        },\n        \"0x0043\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F10\",\n            \"label\": \"F10\"\n        },\n        \"0x0044\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F11\",\n            \"label\": \"F11\"\n        },\n        \"0x0045\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F12\",\n            \"label\": \"F12\"\n        },\n        \"0x0046\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_PRINT_SCREEN\",\n            \"label\": \"Print Screen\",\n            \"aliases\": [\n                \"KC_PSCR\"\n            ]\n        },\n        \"0x0047\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_SCROLL_LOCK\",\n            \"label\": \"Scroll Lock\",\n            \"aliases\": [\n                \"KC_SCRL\",\n                \"KC_BRMD\"\n            ]\n        },\n        \"0x0048\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_PAUSE\",\n            \"label\": \"Pause\",\n            \"aliases\": [\n                \"KC_PAUS\",\n                \"KC_BRK\",\n                \"KC_BRMU\"\n            ]\n        },\n        \"0x0049\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_INSERT\",\n            \"label\": \"Insert\",\n            \"aliases\": [\n                \"KC_INS\"\n            ]\n        },\n        \"0x004A\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_HOME\",\n            \"label\": \"Home\"\n        },\n        \"0x004B\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_PAGE_UP\",\n            \"label\": \"Page Up\",\n            \"aliases\": [\n                \"KC_PGUP\"\n            ]\n        },\n        \"0x004C\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_DELETE\",\n            \"label\": \"Delete\",\n            \"aliases\": [\n                \"KC_DEL\"\n            ]\n        },\n        \"0x004D\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_END\",\n            \"label\": \"End\"\n        },\n        \"0x004E\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_PAGE_DOWN\",\n            \"label\": \"Page Down\",\n            \"aliases\": [\n                \"KC_PGDN\"\n            ]\n        },\n        \"0x004F\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_RIGHT\",\n            \"label\": \"Right\",\n            \"aliases\": [\n                \"KC_RGHT\"\n            ]\n        },\n        \"0x0050\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LEFT\",\n            \"label\": \"Left\"\n        },\n        \"0x0051\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_DOWN\",\n            \"label\": \"Down\"\n        },\n        \"0x0052\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_UP\",\n            \"label\": \"Up\"\n        },\n        \"0x0053\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_NUM_LOCK\",\n            \"label\": \"Num Lock\",\n            \"aliases\": [\n                \"KC_NUM\"\n            ]\n        },\n        \"0x0054\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_SLASH\",\n            \"label\": \"/\",\n            \"aliases\": [\n                \"KC_PSLS\"\n            ]\n        },\n        \"0x0055\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_ASTERISK\",\n            \"label\": \"*\",\n            \"aliases\": [\n                \"KC_PAST\"\n            ]\n        },\n        \"0x0056\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_MINUS\",\n            \"label\": \"-\",\n            \"aliases\": [\n                \"KC_PMNS\"\n            ]\n        },\n        \"0x0057\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_PLUS\",\n            \"label\": \"+\",\n            \"aliases\": [\n                \"KC_PPLS\"\n            ]\n        },\n        \"0x0058\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_ENTER\",\n            \"label\": \"Enter\",\n            \"aliases\": [\n                \"KC_PENT\"\n            ]\n        },\n        \"0x0059\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_1\",\n            \"label\": \"1\",\n            \"aliases\": [\n                \"KC_P1\"\n            ]\n        },\n        \"0x005A\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_2\",\n            \"label\": \"2\",\n            \"aliases\": [\n                \"KC_P2\"\n            ]\n        },\n        \"0x005B\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_3\",\n            \"label\": \"3\",\n            \"aliases\": [\n                \"KC_P3\"\n            ]\n        },\n        \"0x005C\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_4\",\n            \"label\": \"4\",\n            \"aliases\": [\n                \"KC_P4\"\n            ]\n        },\n        \"0x005D\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_5\",\n            \"label\": \"5\",\n            \"aliases\": [\n                \"KC_P5\"\n            ]\n        },\n        \"0x005E\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_6\",\n            \"label\": \"6\",\n            \"aliases\": [\n                \"KC_P6\"\n            ]\n        },\n        \"0x005F\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_7\",\n            \"label\": \"7\",\n            \"aliases\": [\n                \"KC_P7\"\n            ]\n        },\n        \"0x0060\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_8\",\n            \"label\": \"8\",\n            \"aliases\": [\n                \"KC_P8\"\n            ]\n        },\n        \"0x0061\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_9\",\n            \"label\": \"9\",\n            \"aliases\": [\n                \"KC_P9\"\n            ]\n        },\n        \"0x0062\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_0\",\n            \"label\": \"0\",\n            \"aliases\": [\n                \"KC_P0\"\n            ]\n        },\n        \"0x0063\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_DOT\",\n            \"label\": \".\",\n            \"aliases\": [\n                \"KC_PDOT\"\n            ]\n        },\n        \"0x0064\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_NONUS_BACKSLASH\",\n            \"label\": \"\\\\\",\n            \"aliases\": [\n                \"KC_NUBS\"\n            ]\n        },\n        \"0x0065\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_APPLICATION\",\n            \"label\": \"Application\",\n            \"aliases\": [\n                \"KC_APP\"\n            ]\n        },\n        \"0x0066\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KB_POWER\",\n            \"label\": \"Application\"\n        },\n        \"0x0067\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_EQUAL\",\n            \"label\": \"=\",\n            \"aliases\": [\n                \"KC_PEQL\"\n            ]\n        },\n        \"0x0068\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F13\",\n            \"label\": \"F13\"\n        },\n        \"0x0069\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F14\",\n            \"label\": \"F14\"\n        },\n        \"0x006A\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F15\",\n            \"label\": \"F15\"\n        },\n        \"0x006B\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F16\",\n            \"label\": \"F16\"\n        },\n        \"0x006C\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F17\",\n            \"label\": \"F17\"\n        },\n        \"0x006D\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F18\",\n            \"label\": \"F18\"\n        },\n        \"0x006E\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F19\",\n            \"label\": \"F19\"\n        },\n        \"0x006F\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F20\",\n            \"label\": \"F20\"\n        },\n        \"0x0070\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F21\",\n            \"label\": \"F21\"\n        },\n        \"0x0071\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F22\",\n            \"label\": \"F22\"\n        },\n        \"0x0072\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F23\",\n            \"label\": \"F23\"\n        },\n        \"0x0073\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_F24\",\n            \"label\": \"F24\"\n        },\n        \"0x0074\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_EXECUTE\",\n            \"label\": \"Execute\",\n            \"aliases\": [\n                \"KC_EXEC\"\n            ]\n        },\n        \"0x0075\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_HELP\",\n            \"label\": \"Help\"\n        },\n        \"0x0076\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_MENU\",\n            \"label\": \"Menu\"\n        },\n        \"0x0077\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_SELECT\",\n            \"label\": \"Select\",\n            \"aliases\": [\n                \"KC_SLCT\"\n            ]\n        },\n        \"0x0078\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_STOP\",\n            \"label\": \"Stop\"\n        },\n        \"0x0079\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_AGAIN\",\n            \"label\": \"Again\",\n            \"aliases\": [\n                \"KC_AGIN\"\n            ]\n        },\n        \"0x007A\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_UNDO\",\n            \"label\": \"Undo\"\n        },\n        \"0x007B\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_CUT\",\n            \"label\": \"Cut\"\n        },\n        \"0x007C\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_COPY\",\n            \"label\": \"Copy\"\n        },\n        \"0x007D\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_PASTE\",\n            \"label\": \"Paste\",\n            \"aliases\": [\n                \"KC_PSTE\"\n            ]\n        },\n        \"0x007E\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_FIND\",\n            \"label\": \"Find\"\n        },\n        \"0x007F\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KB_MUTE\",\n            \"label\": \"Mute\"\n        },\n        \"0x0080\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KB_VOLUME_UP\",\n            \"label\": \"Volume Up\"\n        },\n        \"0x0081\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KB_VOLUME_DOWN\",\n            \"label\": \"Volume Down\"\n        },\n        \"0x0082\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LOCKING_CAPS_LOCK\",\n            \"label\": \"Caps Lock\",\n            \"aliases\": [\n                \"KC_LCAP\"\n            ]\n        },\n        \"0x0083\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LOCKING_NUM_LOCK\",\n            \"label\": \"Num Lock\",\n            \"aliases\": [\n                \"KC_LNUM\"\n            ]\n        },\n        \"0x0084\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LOCKING_SCROLL_LOCK\",\n            \"label\": \"Scroll Lock\",\n            \"aliases\": [\n                \"KC_LSCR\"\n            ]\n        },\n        \"0x0085\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_COMMA\",\n            \"label\": \",\",\n            \"aliases\": [\n                \"KC_PCMM\"\n            ]\n        },\n        \"0x0086\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_KP_EQUAL_AS400\",\n            \"label\": \"=\"\n        },\n        \"0x0087\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_INTERNATIONAL_1\",\n            \"label\": \"INT 1\",\n            \"aliases\": [\n                \"KC_INT1\"\n            ]\n        },\n        \"0x0088\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_INTERNATIONAL_2\",\n            \"label\": \"INT 2\",\n            \"aliases\": [\n                \"KC_INT2\"\n            ]\n        },\n        \"0x0089\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_INTERNATIONAL_3\",\n            \"label\": \"INT 3\",\n            \"aliases\": [\n                \"KC_INT3\"\n            ]\n        },\n        \"0x008A\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_INTERNATIONAL_4\",\n            \"label\": \"INT 4\",\n            \"aliases\": [\n                \"KC_INT4\"\n            ]\n        },\n        \"0x008B\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_INTERNATIONAL_5\",\n            \"label\": \"INT 5\",\n            \"aliases\": [\n                \"KC_INT5\"\n            ]\n        },\n        \"0x008C\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_INTERNATIONAL_6\",\n            \"label\": \"INT 6\",\n            \"aliases\": [\n                \"KC_INT6\"\n            ]\n        },\n        \"0x008D\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_INTERNATIONAL_7\",\n            \"label\": \"INT 7\",\n            \"aliases\": [\n                \"KC_INT7\"\n            ]\n        },\n        \"0x008E\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_INTERNATIONAL_8\",\n            \"label\": \"INT 8\",\n            \"aliases\": [\n                \"KC_INT8\"\n            ]\n        },\n        \"0x008F\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_INTERNATIONAL_9\",\n            \"label\": \"INT 9\",\n            \"aliases\": [\n                \"KC_INT9\"\n            ]\n        },\n        \"0x0090\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LANGUAGE_1\",\n            \"label\": \"LANG 1\",\n            \"aliases\": [\n                \"KC_LNG1\"\n            ]\n        },\n        \"0x0091\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LANGUAGE_2\",\n            \"label\": \"LANG 2\",\n            \"aliases\": [\n                \"KC_LNG2\"\n            ]\n        },\n        \"0x0092\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LANGUAGE_3\",\n            \"label\": \"LANG 3\",\n            \"aliases\": [\n                \"KC_LNG3\"\n            ]\n        },\n        \"0x0093\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LANGUAGE_4\",\n            \"label\": \"LANG 4\",\n            \"aliases\": [\n                \"KC_LNG4\"\n            ]\n        },\n        \"0x0094\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LANGUAGE_5\",\n            \"label\": \"LANG 5\",\n            \"aliases\": [\n                \"KC_LNG5\"\n            ]\n        },\n        \"0x0095\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LANGUAGE_6\",\n            \"label\": \"LANG 6\",\n            \"aliases\": [\n                \"KC_LNG6\"\n            ]\n        },\n        \"0x0096\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LANGUAGE_7\",\n            \"label\": \"LANG 7\",\n            \"aliases\": [\n                \"KC_LNG7\"\n            ]\n        },\n        \"0x0097\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LANGUAGE_8\",\n            \"label\": \"LANG 8\",\n            \"aliases\": [\n                \"KC_LNG8\"\n            ]\n        },\n        \"0x0098\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_LANGUAGE_9\",\n            \"label\": \"LANG 9\",\n            \"aliases\": [\n                \"KC_LNG9\"\n            ]\n        },\n        \"0x0099\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_ALTERNATE_ERASE\",\n            \"label\": \"Alternate Erase\",\n            \"aliases\": [\n                \"KC_ERAS\"\n            ]\n        },\n        \"0x009A\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_SYSTEM_REQUEST\",\n            \"label\": \"SysReq/Attention\",\n            \"aliases\": [\n                \"KC_SYRQ\"\n            ]\n        },\n        \"0x009B\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_CANCEL\",\n            \"label\": \"Cancel\",\n            \"aliases\": [\n                \"KC_CNCL\"\n            ]\n        },\n        \"0x009C\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_CLEAR\",\n            \"label\": \"Clear\",\n            \"aliases\": [\n                \"KC_CLR\"\n            ]\n        },\n        \"0x009D\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_PRIOR\",\n            \"label\": \"Prior\",\n            \"aliases\": [\n                \"KC_PRIR\"\n            ]\n        },\n        \"0x009E\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_RETURN\",\n            \"label\": \"Return\",\n            \"aliases\": [\n                \"KC_RETN\"\n            ]\n        },\n        \"0x009F\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_SEPARATOR\",\n            \"label\": \"Separator\",\n            \"aliases\": [\n                \"KC_SEPR\"\n            ]\n        },\n        \"0x00A0\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_OUT\",\n            \"label\": \"Out\"\n        },\n        \"0x00A1\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_OPER\",\n            \"label\": \"Oper\"\n        },\n        \"0x00A2\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_CLEAR_AGAIN\",\n            \"label\": \"Clear/Again\",\n            \"aliases\": [\n                \"KC_CLAG\"\n            ]\n        },\n        \"0x00A3\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_CRSEL\",\n            \"label\": \"CrSel/Props\",\n            \"aliases\": [\n                \"KC_CRSL\"\n            ]\n        },\n        \"0x00A4\": {\n            \"group\": \"basic\",\n            \"key\": \"KC_EXSEL\",\n            \"label\": \"ExSel\",\n            \"aliases\": [\n                \"KC_EXSL\"\n            ]\n        },\n        \"0x00A5\": {\n            \"group\": \"system\",\n            \"key\": \"KC_SYSTEM_POWER\",\n            \"label\": \"System Power Down\",\n            \"aliases\": [\n                \"KC_PWR\"\n            ]\n        },\n        \"0x00A6\": {\n            \"group\": \"system\",\n            \"key\": \"KC_SYSTEM_SLEEP\",\n            \"label\": \"System Sleep\",\n            \"aliases\": [\n                \"KC_SLEP\"\n            ]\n        },\n        \"0x00A7\": {\n            \"group\": \"system\",\n            \"key\": \"KC_SYSTEM_WAKE\",\n            \"label\": \"System Wake\",\n            \"aliases\": [\n                \"KC_WAKE\"\n            ]\n        },\n        \"0x00A8\": {\n            \"group\": \"media\",\n            \"key\": \"KC_AUDIO_MUTE\",\n            \"label\": \"Mute\",\n            \"aliases\": [\n                \"KC_MUTE\"\n            ]\n        },\n        \"0x00A9\": {\n            \"group\": \"media\",\n            \"key\": \"KC_AUDIO_VOL_UP\",\n            \"label\": \"Volume Up\",\n            \"aliases\": [\n                \"KC_VOLU\"\n            ]\n        },\n        \"0x00AA\": {\n            \"group\": \"media\",\n            \"key\": \"KC_AUDIO_VOL_DOWN\",\n            \"label\": \"Volume Down\",\n            \"aliases\": [\n                \"KC_VOLD\"\n            ]\n        },\n        \"0x00AB\": {\n            \"group\": \"media\",\n            \"key\": \"KC_MEDIA_NEXT_TRACK\",\n            \"label\": \"Next\",\n            \"aliases\": [\n                \"KC_MNXT\"\n            ]\n        },\n        \"0x00AC\": {\n            \"group\": \"media\",\n            \"key\": \"KC_MEDIA_PREV_TRACK\",\n            \"label\": \"Previous\",\n            \"aliases\": [\n                \"KC_MPRV\"\n            ]\n        },\n        \"0x00AD\": {\n            \"group\": \"media\",\n            \"key\": \"KC_MEDIA_STOP\",\n            \"label\": \"Stop\",\n            \"aliases\": [\n                \"KC_MSTP\"\n            ]\n        },\n        \"0x00AE\": {\n            \"group\": \"media\",\n            \"key\": \"KC_MEDIA_PLAY_PAUSE\",\n            \"label\": \"Play/Pause Track\",\n            \"aliases\": [\n                \"KC_MPLY\"\n            ]\n        },\n        \"0x00AF\": {\n            \"group\": \"media\",\n            \"key\": \"KC_MEDIA_SELECT\",\n            \"label\": \"Launch Player\",\n            \"aliases\": [\n                \"KC_MSEL\"\n            ]\n        },\n        \"0x00B0\": {\n            \"group\": \"media\",\n            \"key\": \"KC_MEDIA_EJECT\",\n            \"label\": \"Eject\",\n            \"aliases\": [\n                \"KC_EJCT\"\n            ]\n        },\n        \"0x00B1\": {\n            \"group\": \"media\",\n            \"key\": \"KC_MAIL\",\n            \"label\": \"Launch Mail\"\n        },\n        \"0x00B2\": {\n            \"group\": \"media\",\n            \"key\": \"KC_CALCULATOR\",\n            \"label\": \"Launch Calculator\",\n            \"aliases\": [\n                \"KC_CALC\"\n            ]\n        },\n        \"0x00B3\": {\n            \"group\": \"media\",\n            \"key\": \"KC_MY_COMPUTER\",\n            \"label\": \"Launch My Computer\",\n            \"aliases\": [\n                \"KC_MYCM\"\n            ]\n        },\n        \"0x00B4\": {\n            \"group\": \"media\",\n            \"key\": \"KC_WWW_SEARCH\",\n            \"label\": \"Browser Search\",\n            \"aliases\": [\n                \"KC_WSCH\"\n            ]\n        },\n        \"0x00B5\": {\n            \"group\": \"media\",\n            \"key\": \"KC_WWW_HOME\",\n            \"label\": \"Browser Home\",\n            \"aliases\": [\n                \"KC_WHOM\"\n            ]\n        },\n        \"0x00B6\": {\n            \"group\": \"media\",\n            \"key\": \"KC_WWW_BACK\",\n            \"label\": \"Browser Back\",\n            \"aliases\": [\n                \"KC_WBAK\"\n            ]\n        },\n        \"0x00B7\": {\n            \"group\": \"media\",\n            \"key\": \"KC_WWW_FORWARD\",\n            \"label\": \"Browser Forward\",\n            \"aliases\": [\n                \"KC_WFWD\"\n            ]\n        },\n        \"0x00B8\": {\n            \"group\": \"media\",\n            \"key\": \"KC_WWW_STOP\",\n            \"label\": \"Browser Stop\",\n            \"aliases\": [\n                \"KC_WSTP\"\n            ]\n        },\n        \"0x00B9\": {\n            \"group\": \"media\",\n            \"key\": \"KC_WWW_REFRESH\",\n            \"label\": \"Browser Refresh\",\n            \"aliases\": [\n                \"KC_WREF\"\n            ]\n        },\n        \"0x00BA\": {\n            \"group\": \"media\",\n            \"key\": \"KC_WWW_FAVORITES\",\n            \"label\": \"Browser Favorites\",\n            \"aliases\": [\n                \"KC_WFAV\"\n            ]\n        },\n        \"0x00BB\": {\n            \"group\": \"media\",\n            \"key\": \"KC_MEDIA_FAST_FORWARD\",\n            \"label\": \"Next Track\",\n            \"aliases\": [\n                \"KC_MFFD\"\n            ]\n        },\n        \"0x00BC\": {\n            \"group\": \"media\",\n            \"key\": \"KC_MEDIA_REWIND\",\n            \"label\": \"Previous Track\",\n            \"aliases\": [\n                \"KC_MRWD\"\n            ]\n        },\n        \"0x00BD\": {\n            \"group\": \"media\",\n            \"key\": \"KC_BRIGHTNESS_UP\",\n            \"label\": \"Brightness Up\",\n            \"aliases\": [\n                \"KC_BRIU\"\n            ]\n        },\n        \"0x00BE\": {\n            \"group\": \"media\",\n            \"key\": \"KC_BRIGHTNESS_DOWN\",\n            \"label\": \"Brightness Down\",\n            \"aliases\": [\n                \"KC_BRID\"\n            ]\n        },\n        \"0x00BF\": {\n            \"group\": \"media\",\n            \"key\": \"KC_CONTROL_PANEL\",\n            \"label\": \"Open Control Panel\",\n            \"aliases\": [\n                \"KC_CPNL\"\n            ]\n        },\n        \"0x00C0\": {\n            \"group\": \"media\",\n            \"key\": \"KC_ASSISTANT\",\n            \"label\": \"Launch Assistant\",\n            \"aliases\": [\n                \"KC_ASST\"\n            ]\n        },\n\n        \"0x00CD\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_UP\",\n            \"label\": \"Move cursor up\",\n            \"aliases\": [\n                \"KC_MS_U\"\n            ]\n        },\n        \"0x00CE\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_DOWN\",\n            \"label\": \"Move cursor down\",\n            \"aliases\": [\n                \"KC_MS_D\"\n            ]\n        },\n        \"0x00CF\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_LEFT\",\n            \"label\": \"Move cursor left\",\n            \"aliases\": [\n                \"KC_MS_L\"\n            ]\n        },\n        \"0x00D0\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_RIGHT\",\n            \"label\": \"Move cursor right\",\n            \"aliases\": [\n                \"KC_MS_R\"\n            ]\n        },\n        \"0x00D1\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_BTN1\",\n            \"label\": \"Press button 1\",\n            \"aliases\": [\n                \"KC_BTN1\"\n            ]\n        },\n        \"0x00D2\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_BTN2\",\n            \"label\": \"Press button 2\",\n            \"aliases\": [\n                \"KC_BTN2\"\n            ]\n        },\n        \"0x00D3\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_BTN3\",\n            \"label\": \"Press button 3\",\n            \"aliases\": [\n                \"KC_BTN3\"\n            ]\n        },\n        \"0x00D4\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_BTN4\",\n            \"label\": \"Press button 4\",\n            \"aliases\": [\n                \"KC_BTN4\"\n            ]\n        },\n        \"0x00D5\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_BTN5\",\n            \"label\": \"Press button 5\",\n            \"aliases\": [\n                \"KC_BTN5\"\n            ]\n        },\n        \"0x00D6\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_BTN6\",\n            \"label\": \"Press button 6\",\n            \"aliases\": [\n                \"KC_BTN6\"\n            ]\n        },\n        \"0x00D7\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_BTN7\",\n            \"label\": \"Press button 7\",\n            \"aliases\": [\n                \"KC_BTN7\"\n            ]\n        },\n        \"0x00D8\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_BTN8\",\n            \"label\": \"Press button 8\",\n            \"aliases\": [\n                \"KC_BTN8\"\n            ]\n        },\n        \"0x00D9\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_WH_UP\",\n            \"label\": \"Move wheel up\",\n            \"aliases\": [\n                \"KC_WH_U\"\n            ]\n        },\n        \"0x00DA\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_WH_DOWN\",\n            \"label\": \"Move wheel down\",\n            \"aliases\": [\n                \"KC_WH_D\"\n            ]\n        },\n        \"0x00DB\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_WH_LEFT\",\n            \"label\": \"Move wheel left\",\n            \"aliases\": [\n                \"KC_WH_L\"\n            ]\n        },\n        \"0x00DC\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_WH_RIGHT\",\n            \"label\": \"Move wheel right\",\n            \"aliases\": [\n                \"KC_WH_R\"\n            ]\n        },\n        \"0x00DD\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_ACCEL0\",\n            \"label\": \"Set speed to 0\",\n            \"aliases\": [\n                \"KC_ACL0\"\n            ]\n        },\n        \"0x00DE\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_ACCEL1\",\n            \"label\": \"Set speed to 1\",\n            \"aliases\": [\n                \"KC_ACL1\"\n            ]\n        },\n        \"0x00DF\": {\n            \"group\": \"mouse\",\n            \"key\": \"KC_MS_ACCEL2\",\n            \"label\": \"Set speed to 2\",\n            \"aliases\": [\n                \"KC_ACL2\"\n            ]\n        },\n\n        \"0x00E0\": {\n            \"group\": \"modifiers\",\n            \"key\": \"KC_LEFT_CTRL\",\n            \"label\": \"Left Control\",\n            \"aliases\": [\n                \"KC_LCTL\"\n            ]\n        },\n        \"0x00E1\": {\n            \"group\": \"modifiers\",\n            \"key\": \"KC_LEFT_SHIFT\",\n            \"label\": \"Left Shift\",\n            \"aliases\": [\n                \"KC_LSFT\"\n            ]\n        },\n        \"0x00E2\": {\n            \"group\": \"modifiers\",\n            \"key\": \"KC_LEFT_ALT\",\n            \"label\": \"Left Alt\",\n            \"aliases\": [\n                \"KC_LALT\",\n                \"KC_LOPT\"\n            ]\n        },\n        \"0x00E3\": {\n            \"group\": \"modifiers\",\n            \"key\": \"KC_LEFT_GUI\",\n            \"label\": \"Left GUI\",\n            \"aliases\": [\n                \"KC_LGUI\",\n                \"KC_LCMD\",\n                \"KC_LWIN\"\n            ]\n        },\n        \"0x00E4\": {\n            \"group\": \"modifiers\",\n            \"key\": \"KC_RIGHT_CTRL\",\n            \"label\": \"Right Control\",\n            \"aliases\": [\n                \"KC_RCTL\"\n            ]\n        },\n        \"0x00E5\": {\n            \"group\": \"modifiers\",\n            \"key\": \"KC_RIGHT_SHIFT\",\n            \"label\": \"Right Shift\",\n            \"aliases\": [\n                \"KC_RSFT\"\n            ]\n        },\n        \"0x00E6\": {\n            \"group\": \"modifiers\",\n            \"key\": \"KC_RIGHT_ALT\",\n            \"label\": \"Right Alt\",\n            \"aliases\": [\n                \"KC_RALT\",\n                \"KC_ROPT\",\n                \"KC_ALGR\"\n            ]\n        },\n        \"0x00E7\": {\n            \"group\": \"modifiers\",\n            \"key\": \"KC_RIGHT_GUI\",\n            \"label\": \"Right GUI\",\n            \"aliases\": [\n                \"KC_RGUI\",\n                \"KC_RCMD\",\n                \"KC_RWIN\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_joystick.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7400\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_0\",\n            \"aliases\": [\n                \"JS_0\"\n            ]\n        },\n        \"0x7401\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_1\",\n            \"aliases\": [\n                \"JS_1\"\n            ]\n        },\n        \"0x7402\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_2\",\n            \"aliases\": [\n                \"JS_2\"\n            ]\n        },\n        \"0x7403\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_3\",\n            \"aliases\": [\n                \"JS_3\"\n            ]\n        },\n        \"0x7404\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_4\",\n            \"aliases\": [\n                \"JS_4\"\n            ]\n        },\n        \"0x7405\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_5\",\n            \"aliases\": [\n                \"JS_5\"\n            ]\n        },\n        \"0x7406\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_6\",\n            \"aliases\": [\n                \"JS_6\"\n            ]\n        },\n        \"0x7407\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_7\",\n            \"aliases\": [\n                \"JS_7\"\n            ]\n        },\n        \"0x7408\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_8\",\n            \"aliases\": [\n                \"JS_8\"\n            ]\n        },\n        \"0x7409\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_9\",\n            \"aliases\": [\n                \"JS_9\"\n            ]\n        },\n        \"0x740A\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_10\",\n            \"aliases\": [\n                \"JS_10\"\n            ]\n        },\n        \"0x740B\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_11\",\n            \"aliases\": [\n                \"JS_11\"\n            ]\n        },\n        \"0x740C\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_12\",\n            \"aliases\": [\n                \"JS_12\"\n            ]\n        },\n        \"0x740D\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_13\",\n            \"aliases\": [\n                \"JS_13\"\n            ]\n        },\n        \"0x740E\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_14\",\n            \"aliases\": [\n                \"JS_14\"\n            ]\n        },\n        \"0x740F\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_15\",\n            \"aliases\": [\n                \"JS_15\"\n            ]\n        },\n        \"0x7410\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_16\",\n            \"aliases\": [\n                \"JS_16\"\n            ]\n        },\n        \"0x7411\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_17\",\n            \"aliases\": [\n                \"JS_17\"\n            ]\n        },\n        \"0x7412\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_18\",\n            \"aliases\": [\n                \"JS_18\"\n            ]\n        },\n        \"0x7413\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_19\",\n            \"aliases\": [\n                \"JS_19\"\n            ]\n        },\n        \"0x7414\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_20\",\n            \"aliases\": [\n                \"JS_20\"\n            ]\n        },\n        \"0x7415\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_21\",\n            \"aliases\": [\n                \"JS_21\"\n            ]\n        },\n        \"0x7416\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_22\",\n            \"aliases\": [\n                \"JS_22\"\n            ]\n        },\n        \"0x7417\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_23\",\n            \"aliases\": [\n                \"JS_23\"\n            ]\n        },\n        \"0x7418\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_24\",\n            \"aliases\": [\n                \"JS_24\"\n            ]\n        },\n        \"0x7419\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_25\",\n            \"aliases\": [\n                \"JS_25\"\n            ]\n        },\n        \"0x741A\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_26\",\n            \"aliases\": [\n                \"JS_26\"\n            ]\n        },\n        \"0x741B\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_27\",\n            \"aliases\": [\n                \"JS_27\"\n            ]\n        },\n        \"0x741C\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_28\",\n            \"aliases\": [\n                \"JS_28\"\n            ]\n        },\n        \"0x741D\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_29\",\n            \"aliases\": [\n                \"JS_29\"\n            ]\n        },\n        \"0x741E\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_30\",\n            \"aliases\": [\n                \"JS_30\"\n            ]\n        },\n        \"0x741F\": {\n            \"group\": \"joystick\",\n            \"key\": \"QK_JOYSTICK_BUTTON_31\",\n            \"aliases\": [\n                \"JS_31\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_lighting.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7800\": {\n            \"group\": \"backlight\",\n            \"key\": \"QK_BACKLIGHT_ON\",\n            \"aliases\": [\n                \"BL_ON\"\n            ]\n        },\n        \"0x7801\": {\n            \"group\": \"backlight\",\n            \"key\": \"QK_BACKLIGHT_OFF\",\n            \"aliases\": [\n                \"BL_OFF\"\n            ]\n        },\n        \"0x7802\": {\n            \"group\": \"backlight\",\n            \"key\": \"QK_BACKLIGHT_TOGGLE\",\n            \"aliases\": [\n                \"BL_TOGG\"\n            ]\n        },\n        \"0x7803\": {\n            \"group\": \"backlight\",\n            \"key\": \"QK_BACKLIGHT_DOWN\",\n            \"aliases\": [\n                \"BL_DOWN\"\n            ]\n        },\n        \"0x7804\": {\n            \"group\": \"backlight\",\n            \"key\": \"QK_BACKLIGHT_UP\",\n            \"aliases\": [\n                \"BL_UP\"\n            ]\n        },\n        \"0x7805\": {\n            \"group\": \"backlight\",\n            \"key\": \"QK_BACKLIGHT_STEP\",\n            \"aliases\": [\n                \"BL_STEP\"\n            ]\n        },\n        \"0x7806\": {\n            \"group\": \"backlight\",\n            \"key\": \"QK_BACKLIGHT_TOGGLE_BREATHING\",\n            \"aliases\": [\n                \"BL_BRTG\"\n            ]\n        },\n\n        \"0x7820\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_TOG\"\n        },\n        \"0x7821\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_FORWARD\",\n            \"aliases\": [\n                \"RGB_MOD\"\n            ]\n        },\n        \"0x7822\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_REVERSE\",\n            \"aliases\": [\n                \"RGB_RMOD\"\n            ]\n        },\n        \"0x7823\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_HUI\"\n        },\n        \"0x7824\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_HUD\"\n        },\n        \"0x7825\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_SAI\"\n        },\n        \"0x7826\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_SAD\"\n        },\n        \"0x7827\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_VAI\"\n        },\n        \"0x7828\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_VAD\"\n        },\n        \"0x7829\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_SPI\"\n        },\n        \"0x782A\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_SPD\"\n        },\n\n        \"0x782B\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_PLAIN\",\n            \"aliases\": [\n                \"RGB_M_P\"\n            ]\n        },\n        \"0x782C\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_BREATHE\",\n            \"aliases\": [\n                \"RGB_M_B\"\n            ]\n        },\n        \"0x782D\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_RAINBOW\",\n            \"aliases\": [\n                \"RGB_M_R\"\n            ]\n        },\n        \"0x782E\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_SWIRL\",\n            \"aliases\": [\n                \"RGB_M_SW\"\n            ]\n        },\n        \"0x782F\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_SNAKE\",\n            \"aliases\": [\n                \"RGB_M_SN\"\n            ]\n        },\n        \"0x7830\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_KNIGHT\",\n            \"aliases\": [\n                \"RGB_M_K\"\n            ]\n        },\n        \"0x7831\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_XMAS\",\n            \"aliases\": [\n                \"RGB_M_X\"\n            ]\n        },\n        \"0x7832\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_GRADIENT\",\n            \"aliases\": [\n                \"RGB_M_G\"\n            ]\n        },\n        \"0x7833\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_RGBTEST\",\n            \"aliases\": [\n                \"RGB_M_T\"\n            ]\n        },\n        \"0x7834\": {\n            \"group\": \"rgb\",\n            \"key\": \"RGB_MODE_TWINKLE\",\n            \"aliases\": [\n                \"RGB_M_TW\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_macro.hjson",
    "content": "{\n    \"keycodes\": {\n\n        \"0x7700\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_0\",\n            \"aliases\": [\n                \"MC_0\"\n            ]\n        },\n        \"0x7701\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_1\",\n            \"aliases\": [\n                \"MC_1\"\n            ]\n        },\n        \"0x7702\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_2\",\n            \"aliases\": [\n                \"MC_2\"\n            ]\n        },\n        \"0x7703\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_3\",\n            \"aliases\": [\n                \"MC_3\"\n            ]\n        },\n        \"0x7704\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_4\",\n            \"aliases\": [\n                \"MC_4\"\n            ]\n        },\n        \"0x7705\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_5\",\n            \"aliases\": [\n                \"MC_5\"\n            ]\n        },\n        \"0x7706\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_6\",\n            \"aliases\": [\n                \"MC_6\"\n            ]\n        },\n        \"0x7707\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_7\",\n            \"aliases\": [\n                \"MC_7\"\n            ]\n        },\n        \"0x7708\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_8\",\n            \"aliases\": [\n                \"MC_8\"\n            ]\n        },\n        \"0x7709\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_9\",\n            \"aliases\": [\n                \"MC_9\"\n            ]\n        },\n        \"0x770A\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_10\",\n            \"aliases\": [\n                \"MC_10\"\n            ]\n        },\n        \"0x770B\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_11\",\n            \"aliases\": [\n                \"MC_11\"\n            ]\n        },\n        \"0x770C\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_12\",\n            \"aliases\": [\n                \"MC_12\"\n            ]\n        },\n        \"0x770D\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_13\",\n            \"aliases\": [\n                \"MC_13\"\n            ]\n        },\n        \"0x770E\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_14\",\n            \"aliases\": [\n                \"MC_14\"\n            ]\n        },\n        \"0x770F\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_15\",\n            \"aliases\": [\n                \"MC_15\"\n            ]\n        },\n        \"0x7710\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_16\",\n            \"aliases\": [\n                \"MC_16\"\n            ]\n        },\n        \"0x7711\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_17\",\n            \"aliases\": [\n                \"MC_17\"\n            ]\n        },\n        \"0x7712\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_18\",\n            \"aliases\": [\n                \"MC_18\"\n            ]\n        },\n        \"0x7713\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_19\",\n            \"aliases\": [\n                \"MC_19\"\n            ]\n        },\n        \"0x7714\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_20\",\n            \"aliases\": [\n                \"MC_20\"\n            ]\n        },\n        \"0x7715\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_21\",\n            \"aliases\": [\n                \"MC_21\"\n            ]\n        },\n        \"0x7716\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_22\",\n            \"aliases\": [\n                \"MC_22\"\n            ]\n        },\n        \"0x7717\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_23\",\n            \"aliases\": [\n                \"MC_23\"\n            ]\n        },\n        \"0x7718\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_24\",\n            \"aliases\": [\n                \"MC_24\"\n            ]\n        },\n        \"0x7719\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_25\",\n            \"aliases\": [\n                \"MC_25\"\n            ]\n        },\n        \"0x771A\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_26\",\n            \"aliases\": [\n                \"MC_26\"\n            ]\n        },\n        \"0x771B\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_27\",\n            \"aliases\": [\n                \"MC_27\"\n            ]\n        },\n        \"0x771C\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_28\",\n            \"aliases\": [\n                \"MC_28\"\n            ]\n        },\n        \"0x771D\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_29\",\n            \"aliases\": [\n                \"MC_29\"\n            ]\n        },\n        \"0x771E\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_30\",\n            \"aliases\": [\n                \"MC_30\"\n            ]\n        },\n        \"0x771F\": {\n            \"group\": \"macro\",\n            \"key\": \"QK_MACRO_31\",\n            \"aliases\": [\n                \"MC_31\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_magic.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7000\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_SWAP_CONTROL_CAPSLOCK\",\n            \"aliases\": [\n                \"CL_SWAP\"\n            ]\n        },\n        \"0x7001\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNSWAP_CONTROL_CAPSLOCK\",\n            \"aliases\": [\n                \"CL_NORM\"\n            ]\n        },\n        \"0x7002\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_TOGGLE_CONTROL_CAPSLOCK\",\n            \"aliases\": [\n                \"CL_TOGG\"\n            ]\n        },\n        \"0x7003\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNCAPSLOCK_TO_CONTROL\",\n            \"aliases\": [\n                \"CL_CAPS\"\n            ]\n        },\n        \"0x7004\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_CAPSLOCK_TO_CONTROL\",\n            \"aliases\": [\n                \"CL_CTRL\"\n            ]\n        },\n        \"0x7005\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_SWAP_LALT_LGUI\",\n            \"aliases\": [\n                \"LAG_SWP\"\n            ]\n        },\n        \"0x7006\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNSWAP_LALT_LGUI\",\n            \"aliases\": [\n                \"LAG_NRM\"\n            ]\n        },\n        \"0x7007\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_SWAP_RALT_RGUI\",\n            \"aliases\": [\n                \"RAG_SWP\"\n            ]\n        },\n        \"0x7008\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNSWAP_RALT_RGUI\",\n            \"aliases\": [\n                \"RAG_NRM\"\n            ]\n        },\n        \"0x7009\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNNO_GUI\",\n            \"aliases\": [\n                \"GUI_ON\"\n            ]\n        },\n        \"0x700A\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_NO_GUI\",\n            \"aliases\": [\n                \"GUI_OFF\"\n            ]\n        },\n        \"0x700B\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_TOGGLE_GUI\",\n            \"aliases\": [\n                \"GUI_TOG\"\n            ]\n        },\n        \"0x700C\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_SWAP_GRAVE_ESC\",\n            \"aliases\": [\n                \"GE_SWAP\"\n            ]\n        },\n        \"0x700D\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNSWAP_GRAVE_ESC\",\n            \"aliases\": [\n                \"GE_NORM\"\n            ]\n        },\n        \"0x700E\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_SWAP_BACKSLASH_BACKSPACE\",\n            \"aliases\": [\n                \"BS_SWAP\"\n            ]\n        },\n        \"0x700F\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNSWAP_BACKSLASH_BACKSPACE\",\n            \"aliases\": [\n                \"BS_NORM\"\n            ]\n        },\n        \"0x7010\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_TOGGLE_BACKSLASH_BACKSPACE\",\n            \"aliases\": [\n                \"BS_TOGG\"\n            ]\n        },\n        \"0x7011\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_HOST_NKRO\",\n            \"aliases\": [\n                \"NK_ON\"\n            ]\n        },\n        \"0x7012\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNHOST_NKRO\",\n            \"aliases\": [\n                \"NK_OFF\"\n            ]\n        },\n        \"0x7013\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_TOGGLE_NKRO\",\n            \"aliases\": [\n                \"NK_TOGG\"\n            ]\n        },\n        \"0x7014\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_SWAP_ALT_GUI\",\n            \"aliases\": [\n                \"AG_SWAP\"\n            ]\n        },\n        \"0x7015\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNSWAP_ALT_GUI\",\n            \"aliases\": [\n                \"AG_NORM\"\n            ]\n        },\n        \"0x7016\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_TOGGLE_ALT_GUI\",\n            \"aliases\": [\n                \"AG_TOGG\"\n            ]\n        },\n        \"0x7017\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_SWAP_LCTL_LGUI\",\n            \"aliases\": [\n                \"LCG_SWP\"\n            ]\n        },\n        \"0x7018\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNSWAP_LCTL_LGUI\",\n            \"aliases\": [\n                \"LCG_NRM\"\n            ]\n        },\n        \"0x7019\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_SWAP_RCTL_RGUI\",\n            \"aliases\": [\n                \"RCG_SWP\"\n            ]\n        },\n        \"0x701A\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNSWAP_RCTL_RGUI\",\n            \"aliases\": [\n                \"RCG_NRM\"\n            ]\n        },\n        \"0x701B\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_SWAP_CTL_GUI\",\n            \"aliases\": [\n                \"CG_SWAP\"\n            ]\n        },\n        \"0x701C\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNSWAP_CTL_GUI\",\n            \"aliases\": [\n                \"CG_NORM\"\n            ]\n        },\n        \"0x701D\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_TOGGLE_CTL_GUI\",\n            \"aliases\": [\n                \"CG_TOGG\"\n            ]\n        },\n        \"0x701E\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_EE_HANDS_LEFT\",\n            \"aliases\": [\n                \"EH_LEFT\"\n            ]\n        },\n        \"0x701F\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_EE_HANDS_RIGHT\",\n            \"aliases\": [\n                \"EH_RGHT\"\n            ]\n        },\n        \"0x7020\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_SWAP_ESCAPE_CAPSLOCK\",\n            \"aliases\": [\n                \"EC_SWAP\"\n            ]\n        },\n        \"0x7021\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_UNSWAP_ESCAPE_CAPSLOCK\",\n            \"aliases\": [\n                \"EC_NORM\"\n            ]\n        },\n        \"0x7022\": {\n            \"group\": \"magic\",\n            \"key\": \"MAGIC_TOGGLE_ESCAPE_CAPSLOCK\",\n            \"aliases\": [\n                \"EC_TOGG\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_midi.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7100\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_ON\",\n            \"aliases\": [\n                \"MI_ON\"\n            ]\n        },\n        \"0x7101\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OFF\",\n            \"aliases\": [\n                \"MI_OFF\"\n            ]\n        },\n        \"0x7102\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TOGGLE\",\n            \"aliases\": [\n                \"MI_TOGG\"\n            ]\n        },\n        \"0x7110\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_0\",\n            \"aliases\": [\n                \"MI_C\"\n            ]\n        },\n        \"0x7111\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_0\",\n            \"aliases\": [\n                \"MI_Cs\",\n                \"MI_Db\"\n            ]\n        },\n        \"0x7112\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_0\",\n            \"aliases\": [\n                \"MI_D\"\n            ]\n        },\n        \"0x7113\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_0\",\n            \"aliases\": [\n                \"MI_Ds\",\n                \"MI_Eb\"\n            ]\n        },\n        \"0x7114\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_0\",\n            \"aliases\": [\n                \"MI_E\"\n            ]\n        },\n        \"0x7115\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_0\",\n            \"aliases\": [\n                \"MI_F\"\n            ]\n        },\n        \"0x7116\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_0\",\n            \"aliases\": [\n                \"MI_Fs\",\n                \"MI_Gb\"\n            ]\n        },\n        \"0x7117\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_0\",\n            \"aliases\": [\n                \"MI_G\"\n            ]\n        },\n        \"0x7118\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_0\",\n            \"aliases\": [\n                \"MI_Gs\",\n                \"MI_Ab\"\n            ]\n        },\n        \"0x7119\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_0\",\n            \"aliases\": [\n                \"MI_A\"\n            ]\n        },\n        \"0x711A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_0\",\n            \"aliases\": [\n                \"MI_As\",\n                \"MI_Bb\"\n            ]\n        },\n        \"0x711B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_0\",\n            \"aliases\": [\n                \"MI_B\"\n            ]\n        },\n        \"0x7120\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_1\",\n            \"aliases\": [\n                \"MI_C1\"\n            ]\n        },\n        \"0x7121\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_1\",\n            \"aliases\": [\n                \"MI_Cs1\",\n                \"MI_Db1\"\n            ]\n        },\n        \"0x7122\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_1\",\n            \"aliases\": [\n                \"MI_D1\"\n            ]\n        },\n        \"0x7123\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_1\",\n            \"aliases\": [\n                \"MI_Ds1\",\n                \"MI_Eb1\"\n            ]\n        },\n        \"0x7124\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_1\",\n            \"aliases\": [\n                \"MI_E1\"\n            ]\n        },\n        \"0x7125\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_1\",\n            \"aliases\": [\n                \"MI_F1\"\n            ]\n        },\n        \"0x7126\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_1\",\n            \"aliases\": [\n                \"MI_Fs1\",\n                \"MI_Gb1\"\n            ]\n        },\n        \"0x7127\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_1\",\n            \"aliases\": [\n                \"MI_G1\"\n            ]\n        },\n        \"0x7128\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_1\",\n            \"aliases\": [\n                \"MI_Gs1\",\n                \"MI_Ab1\"\n            ]\n        },\n        \"0x7129\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_1\",\n            \"aliases\": [\n                \"MI_A1\"\n            ]\n        },\n        \"0x712A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_1\",\n            \"aliases\": [\n                \"MI_As1\",\n                \"MI_Bb1\"\n            ]\n        },\n        \"0x712B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_1\",\n            \"aliases\": [\n                \"MI_B1\"\n            ]\n        },\n        \"0x7130\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_2\",\n            \"aliases\": [\n                \"MI_C2\"\n            ]\n        },\n        \"0x7131\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_2\",\n            \"aliases\": [\n                \"MI_Cs2\",\n                \"MI_Db2\"\n            ]\n        },\n        \"0x7132\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_2\",\n            \"aliases\": [\n                \"MI_D2\"\n            ]\n        },\n        \"0x7133\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_2\",\n            \"aliases\": [\n                \"MI_Ds2\",\n                \"MI_Eb2\"\n            ]\n        },\n        \"0x7134\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_2\",\n            \"aliases\": [\n                \"MI_E2\"\n            ]\n        },\n        \"0x7135\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_2\",\n            \"aliases\": [\n                \"MI_F2\"\n            ]\n        },\n        \"0x7136\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_2\",\n            \"aliases\": [\n                \"MI_Fs2\",\n                \"MI_Gb2\"\n            ]\n        },\n        \"0x7137\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_2\",\n            \"aliases\": [\n                \"MI_G2\"\n            ]\n        },\n        \"0x7138\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_2\",\n            \"aliases\": [\n                \"MI_Gs2\",\n                \"MI_Ab2\"\n            ]\n        },\n        \"0x7139\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_2\",\n            \"aliases\": [\n                \"MI_A2\"\n            ]\n        },\n        \"0x713A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_2\",\n            \"aliases\": [\n                \"MI_As2\",\n                \"MI_Bb2\"\n            ]\n        },\n        \"0x713B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_2\",\n            \"aliases\": [\n                \"MI_B2\"\n            ]\n        },\n        \"0x7140\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_3\",\n            \"aliases\": [\n                \"MI_C3\"\n            ]\n        },\n        \"0x7141\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_3\",\n            \"aliases\": [\n                \"MI_Cs3\",\n                \"MI_Db3\"\n            ]\n        },\n        \"0x7142\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_3\",\n            \"aliases\": [\n                \"MI_D3\"\n            ]\n        },\n        \"0x7143\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_3\",\n            \"aliases\": [\n                \"MI_Ds3\",\n                \"MI_Eb3\"\n            ]\n        },\n        \"0x7144\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_3\",\n            \"aliases\": [\n                \"MI_E3\"\n            ]\n        },\n        \"0x7145\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_3\",\n            \"aliases\": [\n                \"MI_F3\"\n            ]\n        },\n        \"0x7146\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_3\",\n            \"aliases\": [\n                \"MI_Fs3\",\n                \"MI_Gb3\"\n            ]\n        },\n        \"0x7147\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_3\",\n            \"aliases\": [\n                \"MI_G3\"\n            ]\n        },\n        \"0x7148\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_3\",\n            \"aliases\": [\n                \"MI_Gs3\",\n                \"MI_Ab3\"\n            ]\n        },\n        \"0x7149\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_3\",\n            \"aliases\": [\n                \"MI_A3\"\n            ]\n        },\n        \"0x714A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_3\",\n            \"aliases\": [\n                \"MI_As3\",\n                \"MI_Bb3\"\n            ]\n        },\n        \"0x714B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_3\",\n            \"aliases\": [\n                \"MI_B3\"\n            ]\n        },\n        \"0x7150\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_4\",\n            \"aliases\": [\n                \"MI_C4\"\n            ]\n        },\n        \"0x7151\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_4\",\n            \"aliases\": [\n                \"MI_Cs4\",\n                \"MI_Db4\"\n            ]\n        },\n        \"0x7152\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_4\",\n            \"aliases\": [\n                \"MI_D4\"\n            ]\n        },\n        \"0x7153\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_4\",\n            \"aliases\": [\n                \"MI_Ds4\",\n                \"MI_Eb4\"\n            ]\n        },\n        \"0x7154\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_4\",\n            \"aliases\": [\n                \"MI_E4\"\n            ]\n        },\n        \"0x7155\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_4\",\n            \"aliases\": [\n                \"MI_F4\"\n            ]\n        },\n        \"0x7156\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_4\",\n            \"aliases\": [\n                \"MI_Fs4\",\n                \"MI_Gb4\"\n            ]\n        },\n        \"0x7157\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_4\",\n            \"aliases\": [\n                \"MI_G4\"\n            ]\n        },\n        \"0x7158\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_4\",\n            \"aliases\": [\n                \"MI_Gs4\",\n                \"MI_Ab4\"\n            ]\n        },\n        \"0x7159\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_4\",\n            \"aliases\": [\n                \"MI_A4\"\n            ]\n        },\n        \"0x715A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_4\",\n            \"aliases\": [\n                \"MI_As4\",\n                \"MI_Bb4\"\n            ]\n        },\n        \"0x715B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_4\",\n            \"aliases\": [\n                \"MI_B4\"\n            ]\n        },\n        \"0x7160\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_5\",\n            \"aliases\": [\n                \"MI_C5\"\n            ]\n        },\n        \"0x7161\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_5\",\n            \"aliases\": [\n                \"MI_Cs5\",\n                \"MI_Db5\"\n            ]\n        },\n        \"0x7162\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_5\",\n            \"aliases\": [\n                \"MI_D5\"\n            ]\n        },\n        \"0x7163\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_5\",\n            \"aliases\": [\n                \"MI_Ds5\",\n                \"MI_Eb5\"\n            ]\n        },\n        \"0x7164\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_5\",\n            \"aliases\": [\n                \"MI_E5\"\n            ]\n        },\n        \"0x7165\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_5\",\n            \"aliases\": [\n                \"MI_F5\"\n            ]\n        },\n        \"0x7166\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_5\",\n            \"aliases\": [\n                \"MI_Fs5\",\n                \"MI_Gb5\"\n            ]\n        },\n        \"0x7167\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_5\",\n            \"aliases\": [\n                \"MI_G5\"\n            ]\n        },\n        \"0x7168\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_5\",\n            \"aliases\": [\n                \"MI_Gs5\",\n                \"MI_Ab5\"\n            ]\n        },\n        \"0x7169\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_5\",\n            \"aliases\": [\n                \"MI_A5\"\n            ]\n        },\n        \"0x716A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_5\",\n            \"aliases\": [\n                \"MI_As5\",\n                \"MI_Bb5\"\n            ]\n        },\n        \"0x716B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_5\",\n            \"aliases\": [\n                \"MI_B5\"\n            ]\n        },\n        \"0x7170\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_N2\",\n            \"aliases\": [\n                \"MI_OCN2\"\n            ]\n        },\n        \"0x7171\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_N1\",\n            \"aliases\": [\n                \"MI_OCN1\"\n            ]\n        },\n        \"0x7172\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_0\",\n            \"aliases\": [\n                \"MI_OC0\"\n            ]\n        },\n        \"0x7173\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_1\",\n            \"aliases\": [\n                \"MI_OC1\"\n            ]\n        },\n        \"0x7174\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_2\",\n            \"aliases\": [\n                \"MI_OC2\"\n            ]\n        },\n        \"0x7175\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_3\",\n            \"aliases\": [\n                \"MI_OC3\"\n            ]\n        },\n        \"0x7176\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_4\",\n            \"aliases\": [\n                \"MI_OC4\"\n            ]\n        },\n        \"0x7177\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_5\",\n            \"aliases\": [\n                \"MI_OC5\"\n            ]\n        },\n        \"0x7178\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_6\",\n            \"aliases\": [\n                \"MI_OC6\"\n            ]\n        },\n        \"0x7179\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_7\",\n            \"aliases\": [\n                \"MI_OC7\"\n            ]\n        },\n        \"0x717A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_DOWN\",\n            \"aliases\": [\n                \"MI_OCTD\"\n            ]\n        },\n        \"0x717B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_UP\",\n            \"aliases\": [\n                \"MI_OCTU\"\n            ]\n        },\n        \"0x7180\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N6\",\n            \"aliases\": [\n                \"MI_TRN6\"\n            ]\n        },\n        \"0x7181\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N5\",\n            \"aliases\": [\n                \"MI_TRN5\"\n            ]\n        },\n        \"0x7182\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N4\",\n            \"aliases\": [\n                \"MI_TRN4\"\n            ]\n        },\n        \"0x7183\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N3\",\n            \"aliases\": [\n                \"MI_TRN3\"\n            ]\n        },\n        \"0x7184\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N2\",\n            \"aliases\": [\n                \"MI_TRN2\"\n            ]\n        },\n        \"0x7185\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N1\",\n            \"aliases\": [\n                \"MI_TRN1\"\n            ]\n        },\n        \"0x7186\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_0\",\n            \"aliases\": [\n                \"MI_TR0\"\n            ]\n        },\n        \"0x7187\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_1\",\n            \"aliases\": [\n                \"MI_TR1\"\n            ]\n        },\n        \"0x7188\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_2\",\n            \"aliases\": [\n                \"MI_TR2\"\n            ]\n        },\n        \"0x7189\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_3\",\n            \"aliases\": [\n                \"MI_TR3\"\n            ]\n        },\n        \"0x718A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_4\",\n            \"aliases\": [\n                \"MI_TR4\"\n            ]\n        },\n        \"0x718B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_5\",\n            \"aliases\": [\n                \"MI_TR5\"\n            ]\n        },\n        \"0x718C\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_6\",\n            \"aliases\": [\n                \"MI_TR6\"\n            ]\n        },\n        \"0x718D\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_DOWN\",\n            \"aliases\": [\n                \"MI_TRSD\"\n            ]\n        },\n        \"0x718E\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_UP\",\n            \"aliases\": [\n                \"MI_TRSU\"\n            ]\n        },\n        \"0x7190\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_0\",\n            \"aliases\": [\n                \"MI_VL0\"\n            ]\n        },\n        \"0x7191\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_1\",\n            \"aliases\": [\n                \"MI_VL1\"\n            ]\n        },\n        \"0x7192\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_2\",\n            \"aliases\": [\n                \"MI_VL2\"\n            ]\n        },\n        \"0x7193\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_3\",\n            \"aliases\": [\n                \"MI_VL3\"\n            ]\n        },\n        \"0x7194\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_4\",\n            \"aliases\": [\n                \"MI_VL4\"\n            ]\n        },\n        \"0x7195\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_5\",\n            \"aliases\": [\n                \"MI_VL5\"\n            ]\n        },\n        \"0x7196\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_6\",\n            \"aliases\": [\n                \"MI_VL6\"\n            ]\n        },\n        \"0x7197\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_7\",\n            \"aliases\": [\n                \"MI_VL7\"\n            ]\n        },\n        \"0x7198\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_8\",\n            \"aliases\": [\n                \"MI_VL8\"\n            ]\n        },\n        \"0x7199\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_9\",\n            \"aliases\": [\n                \"MI_VL9\"\n            ]\n        },\n        \"0x719A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_10\",\n            \"aliases\": [\n                \"MI_VL10\"\n            ]\n        },\n        \"0x719B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_DOWN\",\n            \"aliases\": [\n                \"MI_VELD\"\n            ]\n        },\n        \"0x719C\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_UP\",\n            \"aliases\": [\n                \"MI_VELU\"\n            ]\n        },\n        \"0x71A0\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_1\",\n            \"aliases\": [\n                \"MI_CH1\"\n            ]\n        },\n        \"0x71A1\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_2\",\n            \"aliases\": [\n                \"MI_CH2\"\n            ]\n        },\n        \"0x71A2\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_3\",\n            \"aliases\": [\n                \"MI_CH3\"\n            ]\n        },\n        \"0x71A3\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_4\",\n            \"aliases\": [\n                \"MI_CH4\"\n            ]\n        },\n        \"0x71A4\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_5\",\n            \"aliases\": [\n                \"MI_CH5\"\n            ]\n        },\n        \"0x71A5\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_6\",\n            \"aliases\": [\n                \"MI_CH6\"\n            ]\n        },\n        \"0x71A6\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_7\",\n            \"aliases\": [\n                \"MI_CH7\"\n            ]\n        },\n        \"0x71A7\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_8\",\n            \"aliases\": [\n                \"MI_CH8\"\n            ]\n        },\n        \"0x71A8\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_9\",\n            \"aliases\": [\n                \"MI_CH9\"\n            ]\n        },\n        \"0x71A9\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_10\",\n            \"aliases\": [\n                \"MI_CH10\"\n            ]\n        },\n        \"0x71AA\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_11\",\n            \"aliases\": [\n                \"MI_CH11\"\n            ]\n        },\n        \"0x71AB\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_12\",\n            \"aliases\": [\n                \"MI_CH12\"\n            ]\n        },\n        \"0x71AC\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_13\",\n            \"aliases\": [\n                \"MI_CH13\"\n            ]\n        },\n        \"0x71AD\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_14\",\n            \"aliases\": [\n                \"MI_CH14\"\n            ]\n        },\n        \"0x71AE\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_15\",\n            \"aliases\": [\n                \"MI_CH15\"\n            ]\n        },\n        \"0x71AF\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_16\",\n            \"aliases\": [\n                \"MI_CH16\"\n            ]\n        },\n        \"0x71B0\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_DOWN\",\n            \"aliases\": [\n                \"MI_CHND\"\n            ]\n        },\n        \"0x71B1\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_UP\",\n            \"aliases\": [\n                \"MI_CHNU\"\n            ]\n        },\n        \"0x71C0\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_ALL_NOTES_OFF\",\n            \"aliases\": [\n                \"MI_AOFF\"\n            ]\n        },\n        \"0x71C1\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_SUSTAIN\",\n            \"aliases\": [\n                \"MI_SUST\"\n            ]\n        },\n        \"0x71C2\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_PORTAMENTO\",\n            \"aliases\": [\n                \"MI_PORT\"\n            ]\n        },\n        \"0x71C3\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_SOSTENUTO\",\n            \"aliases\": [\n                \"MI_SOST\"\n            ]\n        },\n        \"0x71C4\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_SOFT\",\n            \"aliases\": [\n                \"MI_SOFT\"\n            ]\n        },\n        \"0x71C5\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_LEGATO\",\n            \"aliases\": [\n                \"MI_LEG\"\n            ]\n        },\n        \"0x71C6\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_MODULATION\",\n            \"aliases\": [\n                \"MI_MOD\"\n            ]\n        },\n        \"0x71C7\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_MODULATION_SPEED_DOWN\",\n            \"aliases\": [\n                \"MI_MODD\"\n            ]\n        },\n        \"0x71C8\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_MODULATION_SPEED_UP\",\n            \"aliases\": [\n                \"MI_MODU\"\n            ]\n        },\n        \"0x71C9\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_PITCH_BEND_DOWN\",\n            \"aliases\": [\n                \"MI_BNDD\"\n            ]\n        },\n        \"0x71CA\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_PITCH_BEND_UP\",\n            \"aliases\": [\n                \"MI_BNDU\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_programmable_button.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7440\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_1\",\n            \"aliases\": [\n                \"PB_1\"\n            ]\n        },\n        \"0x7441\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_2\",\n            \"aliases\": [\n                \"PB_2\"\n            ]\n        },\n        \"0x7442\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_3\",\n            \"aliases\": [\n                \"PB_3\"\n            ]\n        },\n        \"0x7443\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_4\",\n            \"aliases\": [\n                \"PB_4\"\n            ]\n        },\n        \"0x7444\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_5\",\n            \"aliases\": [\n                \"PB_5\"\n            ]\n        },\n        \"0x7445\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_6\",\n            \"aliases\": [\n                \"PB_6\"\n            ]\n        },\n        \"0x7446\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_7\",\n            \"aliases\": [\n                \"PB_7\"\n            ]\n        },\n        \"0x7447\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_8\",\n            \"aliases\": [\n                \"PB_8\"\n            ]\n        },\n        \"0x7448\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_9\",\n            \"aliases\": [\n                \"PB_9\"\n            ]\n        },\n        \"0x7449\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_10\",\n            \"aliases\": [\n                \"PB_10\"\n            ]\n        },\n        \"0x744A\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_11\",\n            \"aliases\": [\n                \"PB_11\"\n            ]\n        },\n        \"0x744B\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_12\",\n            \"aliases\": [\n                \"PB_12\"\n            ]\n        },\n        \"0x744C\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_13\",\n            \"aliases\": [\n                \"PB_13\"\n            ]\n        },\n        \"0x744D\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_14\",\n            \"aliases\": [\n                \"PB_14\"\n            ]\n        },\n        \"0x744E\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_15\",\n            \"aliases\": [\n                \"PB_15\"\n            ]\n        },\n        \"0x744F\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_16\",\n            \"aliases\": [\n                \"PB_16\"\n            ]\n        },\n        \"0x7450\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_17\",\n            \"aliases\": [\n                \"PB_17\"\n            ]\n        },\n        \"0x7451\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_18\",\n            \"aliases\": [\n                \"PB_18\"\n            ]\n        },\n        \"0x7452\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_19\",\n            \"aliases\": [\n                \"PB_19\"\n            ]\n        },\n        \"0x7453\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_20\",\n            \"aliases\": [\n                \"PB_20\"\n            ]\n        },\n        \"0x7454\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_21\",\n            \"aliases\": [\n                \"PB_21\"\n            ]\n        },\n        \"0x7455\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_22\",\n            \"aliases\": [\n                \"PB_22\"\n            ]\n        },\n        \"0x7456\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_23\",\n            \"aliases\": [\n                \"PB_23\"\n            ]\n        },\n        \"0x7457\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_24\",\n            \"aliases\": [\n                \"PB_24\"\n            ]\n        },\n        \"0x7458\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_25\",\n            \"aliases\": [\n                \"PB_25\"\n            ]\n        },\n        \"0x7459\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_26\",\n            \"aliases\": [\n                \"PB_26\"\n            ]\n        },\n        \"0x745A\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_27\",\n            \"aliases\": [\n                \"PB_27\"\n            ]\n        },\n        \"0x745B\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_28\",\n            \"aliases\": [\n                \"PB_28\"\n            ]\n        },\n        \"0x745C\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_29\",\n            \"aliases\": [\n                \"PB_29\"\n            ]\n        },\n        \"0x745D\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_30\",\n            \"aliases\": [\n                \"PB_30\"\n            ]\n        },\n        \"0x745E\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_31\",\n            \"aliases\": [\n                \"PB_31\"\n            ]\n        },\n        \"0x745F\": {\n            \"group\": \"programmable_button\",\n            \"key\": \"QK_PROGRAMMABLE_BUTTON_32\",\n            \"aliases\": [\n                \"PB_32\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_quantum.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7C00\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_BOOTLOADER\",\n            \"aliases\": [\n                \"QK_BOOT\"\n            ]\n        },\n        \"0x7C01\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_REBOOT\",\n            \"aliases\": [\n                \"QK_RBT\"\n            ]\n        },\n        \"0x7C02\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_DEBUG_TOGGLE\",\n            \"aliases\": [\n                \"DB_TOGG\"\n            ]\n        },\n        \"0x7C03\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_CLEAR_EEPROM\",\n            \"aliases\": [\n                \"EE_CLR\"\n            ]\n        },\n        \"0x7C04\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_MAKE\"\n        },\n\n        \"0x7C10\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_AUTO_SHIFT_DOWN\",\n            \"aliases\": [\n                \"AS_DOWN\"\n            ]\n        },\n        \"0x7C11\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_AUTO_SHIFT_UP\",\n            \"aliases\": [\n                \"AS_UP\"\n            ]\n        },\n        \"0x7C12\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_AUTO_SHIFT_REPORT\",\n            \"aliases\": [\n                \"AS_RPT\"\n            ]\n        },\n        \"0x7C13\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_AUTO_SHIFT_ON\",\n            \"aliases\": [\n                \"AS_ON\"\n            ]\n        },\n        \"0x7C14\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_AUTO_SHIFT_OFF\",\n            \"aliases\": [\n                \"AS_OFF\"\n            ]\n        },\n        \"0x7C15\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_AUTO_SHIFT_TOGGLE\",\n            \"aliases\": [\n                \"AS_TOGG\"\n            ]\n        },\n\n        \"0x7C16\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_GRAVE_ESCAPE\",\n            \"aliases\": [\n                \"QK_GESC\"\n            ]\n        },\n\n        \"0x7C17\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_VELOCIKEY_TOGGLE\",\n            \"aliases\": [\n                \"VK_TOGG\"\n            ]\n        },\n\n        \"0x7C18\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN\",\n            \"aliases\": [\n                \"SC_LCPO\"\n            ]\n        },\n        \"0x7C19\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE\",\n            \"aliases\": [\n                \"SC_RCPC\"\n            ]\n        },\n        \"0x7C1A\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN\",\n            \"aliases\": [\n                \"SC_LSPO\"\n            ]\n        },\n        \"0x7C1B\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE\",\n            \"aliases\": [\n                \"SC_RSPC\"\n            ]\n        },\n        \"0x7C1C\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN\",\n            \"aliases\": [\n                \"SC_LAPO\"\n            ]\n        },\n        \"0x7C1D\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE\",\n            \"aliases\": [\n                \"SC_RAPC\"\n            ]\n        },\n        \"0x7C1E\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_SPACE_CADET_RIGHT_SHIFT_ENTER\",\n            \"aliases\": [\n                \"SC_SENT\"\n            ]\n        },\n\n        \"0x7C20\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_OUTPUT_AUTO\",\n            \"aliases\": [\n                \"OU_AUTO\"\n            ]\n        },\n        \"0x7C21\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_OUTPUT_USB\",\n            \"aliases\": [\n                \"OU_USB\"\n            ]\n        },\n        \"0x7C22\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_OUTPUT_BLUETOOTH\",\n            \"aliases\": [\n                \"OU_BT\"\n            ]\n        },\n\n        \"0x7C30\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_UNICODE_MODE_NEXT\",\n            \"aliases\": [\n                \"UC_NEXT\"\n            ]\n        },\n        \"0x7C31\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_UNICODE_MODE_PREVIOUS\",\n            \"aliases\": [\n                \"UC_PREV\"\n            ]\n        },\n        \"0x7C32\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_UNICODE_MODE_MACOS\",\n            \"aliases\": [\n                \"UC_MAC\"\n            ]\n        },\n        \"0x7C33\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_UNICODE_MODE_LINUX\",\n            \"aliases\": [\n                \"UC_LINX\"\n            ]\n        },\n        \"0x7C34\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_UNICODE_MODE_WINDOWS\",\n            \"aliases\": [\n                \"UC_WIN\"\n            ]\n        },\n        \"0x7C35\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_UNICODE_MODE_BSD\",\n            \"aliases\": [\n                \"UC_BSD\"\n            ]\n        },\n        \"0x7C36\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_UNICODE_MODE_WINCOMPOSE\",\n            \"aliases\": [\n                \"UC_WINC\"\n            ]\n        },\n        \"0x7C37\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_UNICODE_MODE_EMACS\",\n            \"aliases\": [\n                \"UC_EMAC\"\n            ]\n        },\n\n        \"0x7C40\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_ON\",\n            \"aliases\": [\n                \"HF_ON\"\n            ]\n        },\n        \"0x7C41\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_OFF\",\n            \"aliases\": [\n                \"HF_OFF\"\n            ]\n        },\n        \"0x7C42\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_TOGGLE\",\n            \"aliases\": [\n                \"HF_TOGG\"\n            ]\n        },\n        \"0x7C43\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_RESET\",\n            \"aliases\": [\n                \"HF_RST\"\n            ]\n        },\n        \"0x7C44\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_FEEDBACK_TOGGLE\",\n            \"aliases\": [\n                \"HF_FDBK\"\n            ]\n        },\n        \"0x7C45\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_BUZZ_TOGGLE\",\n            \"aliases\": [\n                \"HF_BUZZ\"\n            ]\n        },\n        \"0x7C46\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_MODE_NEXT\",\n            \"aliases\": [\n                \"HF_NEXT\"\n            ]\n        },\n        \"0x7C47\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_MODE_PREVIOUS\",\n            \"aliases\": [\n                \"HF_PREV\"\n            ]\n        },\n        \"0x7C48\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_CONTINUOUS_TOGGLE\",\n            \"aliases\": [\n                \"HF_CONT\"\n            ]\n        },\n        \"0x7C49\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_CONTINUOUS_UP\",\n            \"aliases\": [\n                \"HF_CONU\"\n            ]\n        },\n        \"0x7C4A\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_CONTINUOUS_DOWN\",\n            \"aliases\": [\n                \"HF_COND\"\n            ]\n        },\n        \"0x7C4B\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_DWELL_UP\",\n            \"aliases\": [\n                \"HF_DWLU\"\n            ]\n        },\n        \"0x7C4C\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_HAPTIC_DWELL_DOWN\",\n            \"aliases\": [\n                \"HF_DWLD\"\n            ]\n        },\n\n        \"0x7C50\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_COMBO_ON\",\n            \"aliases\": [\n                \"CM_ON\"\n            ]\n        },\n        \"0x7C51\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_COMBO_OFF\",\n            \"aliases\": [\n                \"CM_OFF\"\n            ]\n        },\n        \"0x7C52\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_COMBO_TOGGLE\",\n            \"aliases\": [\n                \"CM_TOGG\"\n            ]\n        },\n\n        \"0x7C53\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_DYNAMIC_MACRO_RECORD_START_1\",\n            \"aliases\": [\n                \"DM_REC1\"\n            ]\n        },\n        \"0x7C54\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_DYNAMIC_MACRO_RECORD_START_2\",\n            \"aliases\": [\n                \"DM_REC2\"\n            ]\n        },\n        \"0x7C55\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_DYNAMIC_MACRO_RECORD_STOP\",\n            \"aliases\": [\n                \"DM_RSTP\"\n            ]\n        },\n        \"0x7C56\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_DYNAMIC_MACRO_PLAY_1\",\n            \"aliases\": [\n                \"DM_PLY1\"\n            ]\n        },\n        \"0x7C57\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_DYNAMIC_MACRO_PLAY_2\",\n            \"aliases\": [\n                \"DM_PLY2\"\n            ]\n        },\n\n        \"0x7C58\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_LEADER\",\n            \"aliases\": [\n                \"QK_LEAD\"\n            ]\n        },\n\n        \"0x7C59\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_LOCK\"\n        },\n\n        \"0x7C5A\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_ONE_SHOT_ON\",\n            \"aliases\": [\n                \"OS_ON\"\n            ]\n        },\n        \"0x7C5B\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_ONE_SHOT_OFF\",\n            \"aliases\": [\n                \"OS_OFF\"\n            ]\n        },\n        \"0x7C5C\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_ONE_SHOT_TOGGLE\",\n            \"aliases\": [\n                \"OS_TOGG\"\n            ]\n        },\n\n        \"0x7C5D\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_KEY_OVERRIDE_TOGGLE\",\n            \"aliases\": [\n                \"KO_TOGG\"\n            ]\n        },\n        \"0x7C5E\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_KEY_OVERRIDE_ON\",\n            \"aliases\": [\n                \"KO_ON\"\n            ]\n        },\n        \"0x7C5F\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_KEY_OVERRIDE_OFF\",\n            \"aliases\": [\n                \"KO_OFF\"\n            ]\n        },\n\n        \"0x7C60\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_SECURE_LOCK\",\n            \"aliases\": [\n                \"SE_LOCK\"\n            ]\n        },\n        \"0x7C61\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_SECURE_UNLOCK\",\n            \"aliases\": [\n                \"SE_UNLK\"\n            ]\n        },\n        \"0x7C62\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_SECURE_TOGGLE\",\n            \"aliases\": [\n                \"SE_TOGG\"\n            ]\n        },\n        \"0x7C63\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_SECURE_REQUEST\",\n            \"aliases\": [\n                \"SE_REQ\"\n            ]\n        },\n\n        \"0x7C70\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_DYNAMIC_TAPPING_TERM_PRINT\",\n            \"aliases\": [\n                \"DT_PRNT\"\n            ]\n        },\n        \"0x7C71\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_DYNAMIC_TAPPING_TERM_UP\",\n            \"aliases\": [\n                \"DT_UP\"\n            ]\n        },\n        \"0x7C72\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_DYNAMIC_TAPPING_TERM_DOWN\",\n            \"aliases\": [\n                \"DT_DOWN\"\n            ]\n        },\n\n        \"0x7C73\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_CAPS_WORD_TOGGLE\",\n            \"aliases\": [\n                \"CW_TOGG\"\n            ]\n        },\n\n        \"0x7C74\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_AUTOCORRECT_ON\",\n            \"aliases\": [\n                \"AC_ON\"\n            ]\n        },\n        \"0x7C75\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_AUTOCORRECT_OFF\",\n            \"aliases\": [\n                \"AC_OFF\"\n            ]\n        },\n        \"0x7C76\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_AUTOCORRECT_TOGGLE\",\n            \"aliases\": [\n                \"AC_TOGG\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_sequencer.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7200\": {\n            \"group\": \"sequencer\",\n            \"key\": \"SQ_ON\"\n        },\n        \"0x7201\": {\n            \"group\": \"sequencer\",\n            \"key\": \"SQ_OFF\"\n        },\n        \"0x7202\": {\n            \"group\": \"sequencer\",\n            \"key\": \"SQ_TOG\"\n        },\n        \"0x7203\": {\n            \"group\": \"sequencer\",\n            \"key\": \"SQ_TMPD\"\n        },\n        \"0x7204\": {\n            \"group\": \"sequencer\",\n            \"key\": \"SQ_TMPU\"\n        },\n        \"0x7205\": {\n            \"group\": \"sequencer\",\n            \"key\": \"SQ_RESD\"\n        },\n        \"0x7206\": {\n            \"group\": \"sequencer\",\n            \"key\": \"SQ_RESU\"\n        },\n        \"0x7207\": {\n            \"group\": \"sequencer\",\n            \"key\": \"SQ_SALL\"\n        },\n        \"0x7208\": {\n            \"group\": \"sequencer\",\n            \"key\": \"SQ_SCLR\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_steno.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x74F0\": {\n            \"group\": \"steno\",\n            \"key\": \"QK_STENO_BOLT\"\n        },\n        \"0x74F1\": {\n            \"group\": \"steno\",\n            \"key\": \"QK_STENO_GEMINI\"\n        },\n        \"0x74F2\": {\n            \"group\": \"steno\",\n            \"key\": \"QK_STENO_COMB\"\n        },\n        \"0x74FC\": {\n            \"group\": \"steno\",\n            \"key\": \"QK_STENO_COMB_MAX\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.1_swap_hands.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x56F0\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"SH_TG\"\n        },\n        \"0x56F1\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"SH_TT\"\n        },\n        \"0x56F2\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"SH_MON\"\n        },\n        \"0x56F3\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"SH_MOFF\"\n        },\n        \"0x56F4\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"SH_OFF\"\n        },\n        \"0x56F5\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"SH_ON\"\n        },\n        \"0x56F6\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"SH_OS\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.2.hjson",
    "content": "{\n    \"ranges\": {\n        \"0x7E00/0x00FF\": \"!delete!\",\n        \"0x7F00/0x00FF\": \"!delete!\",\n\n        \"0x7E00/0x003F\": {\n            \"define\": \"QK_KB\"\n        },\n        \"0x7E40/0x01BF\": {\n            \"define\": \"QK_USER\"\n        },\n        \"0x8000/0X3FFF\": {\n            \"define\": \"QK_UNICODEMAP\"\n        },\n        \"0xC000/0X3FFF\": {\n            \"define\": \"QK_UNICODEMAP_PAIR\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.2_basic.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x00C1\": {\n            \"group\": \"media\",\n            \"key\": \"KC_MISSION_CONTROL\",\n            \"label\": \"Open Mission Control\",\n            \"aliases\": [\n                \"KC_MCTL\"\n            ]\n        },\n        \"0x00C2\": {\n            \"group\": \"media\",\n            \"key\": \"KC_LAUNCHPAD\",\n            \"label\": \"Open Launchpad\",\n            \"aliases\": [\n                \"KC_LPAD\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.2_kb.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7E00\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_0\"\n        },\n        \"0x7E01\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_1\"\n        },\n        \"0x7E02\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_2\"\n        },\n        \"0x7E03\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_3\"\n        },\n        \"0x7E04\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_4\"\n        },\n        \"0x7E05\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_5\"\n        },\n        \"0x7E06\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_6\"\n        },\n        \"0x7E07\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_7\"\n        },\n        \"0x7E08\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_8\"\n        },\n        \"0x7E09\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_9\"\n        },\n        \"0x7E0A\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_10\"\n        },\n        \"0x7E0B\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_11\"\n        },\n        \"0x7E0C\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_12\"\n        },\n        \"0x7E0D\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_13\"\n        },\n        \"0x7E0E\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_14\"\n        },\n        \"0x7E0F\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_15\"\n        },\n        \"0x7E10\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_16\"\n        },\n        \"0x7E11\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_17\"\n        },\n         \"0x7E12\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_18\"\n        },\n        \"0x7E13\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_19\"\n        },\n        \"0x7E14\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_20\"\n        },\n        \"0x7E15\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_21\"\n        },\n        \"0x7E16\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_22\"\n        },\n        \"0x7E17\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_23\"\n        },\n        \"0x7E18\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_24\"\n        },\n        \"0x7E19\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_25\"\n        },\n        \"0x7E1A\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_26\"\n        },\n        \"0x7E1B\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_27\"\n        },\n        \"0x7E1C\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_28\"\n        },\n        \"0x7E1D\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_29\"\n        },\n        \"0x7E1E\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_30\"\n        },\n        \"0x7E1F\": {\n            \"group\": \"kb\",\n            \"key\": \"QK_KB_31\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.2_magic.hjson",
    "content": "{\n    \"keycodes\": {\n        \"!reset!\":0,\n\n        \"0x7000\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_SWAP_CONTROL_CAPS_LOCK\",\n            \"aliases\": [\n                \"CL_SWAP\"\n            ]\n        },\n        \"0x7001\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_UNSWAP_CONTROL_CAPS_LOCK\",\n            \"aliases\": [\n                \"CL_NORM\"\n            ]\n        },\n        \"0x7002\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_TOGGLE_CONTROL_CAPS_LOCK\",\n            \"aliases\": [\n                \"CL_TOGG\"\n            ]\n        },\n        \"0x7003\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_CAPS_LOCK_AS_CONTROL_OFF\",\n            \"aliases\": [\n                \"CL_CAPS\"\n            ]\n        },\n        \"0x7004\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_CAPS_LOCK_AS_CONTROL_ON\",\n            \"aliases\": [\n                \"CL_CTRL\"\n            ]\n        },\n        \"0x7005\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_SWAP_LALT_LGUI\",\n            \"aliases\": [\n                \"AG_LSWP\"\n            ]\n        },\n        \"0x7006\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_UNSWAP_LALT_LGUI\",\n            \"aliases\": [\n                \"AG_LNRM\"\n            ]\n        },\n        \"0x7007\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_SWAP_RALT_RGUI\",\n            \"aliases\": [\n                \"AG_RSWP\"\n            ]\n        },\n        \"0x7008\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_UNSWAP_RALT_RGUI\",\n            \"aliases\": [\n                \"AG_RNRM\"\n            ]\n        },\n        \"0x7009\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_GUI_ON\",\n            \"aliases\": [\n                \"GU_ON\"\n            ]\n        },\n        \"0x700A\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_GUI_OFF\",\n            \"aliases\": [\n                \"GU_OFF\"\n            ]\n        },\n        \"0x700B\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_TOGGLE_GUI\",\n            \"aliases\": [\n                \"GU_TOGG\"\n            ]\n        },\n        \"0x700C\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_SWAP_GRAVE_ESC\",\n            \"aliases\": [\n                \"GE_SWAP\"\n            ]\n        },\n        \"0x700D\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_UNSWAP_GRAVE_ESC\",\n            \"aliases\": [\n                \"GE_NORM\"\n            ]\n        },\n        \"0x700E\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_SWAP_BACKSLASH_BACKSPACE\",\n            \"aliases\": [\n                \"BS_SWAP\"\n            ]\n        },\n        \"0x700F\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_UNSWAP_BACKSLASH_BACKSPACE\",\n            \"aliases\": [\n                \"BS_NORM\"\n            ]\n        },\n        \"0x7010\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_TOGGLE_BACKSLASH_BACKSPACE\",\n            \"aliases\": [\n                \"BS_TOGG\"\n            ]\n        },\n        \"0x7011\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_NKRO_ON\",\n            \"aliases\": [\n                \"NK_ON\"\n            ]\n        },\n        \"0x7012\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_NKRO_OFF\",\n            \"aliases\": [\n                \"NK_OFF\"\n            ]\n        },\n        \"0x7013\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_TOGGLE_NKRO\",\n            \"aliases\": [\n                \"NK_TOGG\"\n            ]\n        },\n        \"0x7014\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_SWAP_ALT_GUI\",\n            \"aliases\": [\n                \"AG_SWAP\"\n            ]\n        },\n        \"0x7015\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_UNSWAP_ALT_GUI\",\n            \"aliases\": [\n                \"AG_NORM\"\n            ]\n        },\n        \"0x7016\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_TOGGLE_ALT_GUI\",\n            \"aliases\": [\n                \"AG_TOGG\"\n            ]\n        },\n        \"0x7017\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_SWAP_LCTL_LGUI\",\n            \"aliases\": [\n                \"CG_LSWP\"\n            ]\n        },\n        \"0x7018\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_UNSWAP_LCTL_LGUI\",\n            \"aliases\": [\n                \"CG_LNRM\"\n            ]\n        },\n        \"0x7019\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_SWAP_RCTL_RGUI\",\n            \"aliases\": [\n                \"CG_RSWP\"\n            ]\n        },\n        \"0x701A\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_UNSWAP_RCTL_RGUI\",\n            \"aliases\": [\n                \"CG_RNRM\"\n            ]\n        },\n        \"0x701B\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_SWAP_CTL_GUI\",\n            \"aliases\": [\n                \"CG_SWAP\"\n            ]\n        },\n        \"0x701C\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_UNSWAP_CTL_GUI\",\n            \"aliases\": [\n                \"CG_NORM\"\n            ]\n        },\n        \"0x701D\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_TOGGLE_CTL_GUI\",\n            \"aliases\": [\n                \"CG_TOGG\"\n            ]\n        },\n        \"0x701E\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_EE_HANDS_LEFT\",\n            \"aliases\": [\n                \"EH_LEFT\"\n            ]\n        },\n        \"0x701F\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_EE_HANDS_RIGHT\",\n            \"aliases\": [\n                \"EH_RGHT\"\n            ]\n        },\n        \"0x7020\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_SWAP_ESCAPE_CAPS_LOCK\",\n            \"aliases\": [\n                \"EC_SWAP\"\n            ]\n        },\n        \"0x7021\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_UNSWAP_ESCAPE_CAPS_LOCK\",\n            \"aliases\": [\n                \"EC_NORM\"\n            ]\n        },\n        \"0x7022\": {\n            \"group\": \"magic\",\n            \"key\": \"QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK\",\n            \"aliases\": [\n                \"EC_TOGG\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.2_midi.hjson",
    "content": "{\n    \"keycodes\": {\n        \"!reset!\":0,\n\n        \"0x7100\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_ON\",\n            \"aliases\": [\n                \"MI_ON\"\n            ]\n        },\n        \"0x7101\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OFF\",\n            \"aliases\": [\n                \"MI_OFF\"\n            ]\n        },\n        \"0x7102\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TOGGLE\",\n            \"aliases\": [\n                \"MI_TOGG\"\n            ]\n        },\n        \"0x7103\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_0\",\n            \"aliases\": [\n                \"MI_C\"\n            ]\n        },\n        \"0x7104\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_0\",\n            \"aliases\": [\n                \"MI_Cs\",\n                \"MI_Db\"\n            ]\n        },\n        \"0x7105\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_0\",\n            \"aliases\": [\n                \"MI_D\"\n            ]\n        },\n        \"0x7106\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_0\",\n            \"aliases\": [\n                \"MI_Ds\",\n                \"MI_Eb\"\n            ]\n        },\n        \"0x7107\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_0\",\n            \"aliases\": [\n                \"MI_E\"\n            ]\n        },\n        \"0x7108\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_0\",\n            \"aliases\": [\n                \"MI_F\"\n            ]\n        },\n        \"0x7109\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_0\",\n            \"aliases\": [\n                \"MI_Fs\",\n                \"MI_Gb\"\n            ]\n        },\n        \"0x710A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_0\",\n            \"aliases\": [\n                \"MI_G\"\n            ]\n        },\n        \"0x710B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_0\",\n            \"aliases\": [\n                \"MI_Gs\",\n                \"MI_Ab\"\n            ]\n        },\n        \"0x710C\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_0\",\n            \"aliases\": [\n                \"MI_A\"\n            ]\n        },\n        \"0x710D\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_0\",\n            \"aliases\": [\n                \"MI_As\",\n                \"MI_Bb\"\n            ]\n        },\n        \"0x710E\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_0\",\n            \"aliases\": [\n                \"MI_B\"\n            ]\n        },\n        \"0x710F\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_1\",\n            \"aliases\": [\n                \"MI_C1\"\n            ]\n        },\n        \"0x7110\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_1\",\n            \"aliases\": [\n                \"MI_Cs1\",\n                \"MI_Db1\"\n            ]\n        },\n        \"0x7111\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_1\",\n            \"aliases\": [\n                \"MI_D1\"\n            ]\n        },\n        \"0x7112\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_1\",\n            \"aliases\": [\n                \"MI_Ds1\",\n                \"MI_Eb1\"\n            ]\n        },\n        \"0x7113\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_1\",\n            \"aliases\": [\n                \"MI_E1\"\n            ]\n        },\n        \"0x7114\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_1\",\n            \"aliases\": [\n                \"MI_F1\"\n            ]\n        },\n        \"0x7115\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_1\",\n            \"aliases\": [\n                \"MI_Fs1\",\n                \"MI_Gb1\"\n            ]\n        },\n        \"0x7116\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_1\",\n            \"aliases\": [\n                \"MI_G1\"\n            ]\n        },\n        \"0x7117\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_1\",\n            \"aliases\": [\n                \"MI_Gs1\",\n                \"MI_Ab1\"\n            ]\n        },\n        \"0x7118\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_1\",\n            \"aliases\": [\n                \"MI_A1\"\n            ]\n        },\n        \"0x7119\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_1\",\n            \"aliases\": [\n                \"MI_As1\",\n                \"MI_Bb1\"\n            ]\n        },\n        \"0x711A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_1\",\n            \"aliases\": [\n                \"MI_B1\"\n            ]\n        },\n        \"0x711B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_2\",\n            \"aliases\": [\n                \"MI_C2\"\n            ]\n        },\n        \"0x711C\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_2\",\n            \"aliases\": [\n                \"MI_Cs2\",\n                \"MI_Db2\"\n            ]\n        },\n        \"0x711D\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_2\",\n            \"aliases\": [\n                \"MI_D2\"\n            ]\n        },\n        \"0x711E\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_2\",\n            \"aliases\": [\n                \"MI_Ds2\",\n                \"MI_Eb2\"\n            ]\n        },\n        \"0x711F\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_2\",\n            \"aliases\": [\n                \"MI_E2\"\n            ]\n        },\n        \"0x7120\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_2\",\n            \"aliases\": [\n                \"MI_F2\"\n            ]\n        },\n        \"0x7121\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_2\",\n            \"aliases\": [\n                \"MI_Fs2\",\n                \"MI_Gb2\"\n            ]\n        },\n        \"0x7122\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_2\",\n            \"aliases\": [\n                \"MI_G2\"\n            ]\n        },\n        \"0x7123\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_2\",\n            \"aliases\": [\n                \"MI_Gs2\",\n                \"MI_Ab2\"\n            ]\n        },\n        \"0x7124\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_2\",\n            \"aliases\": [\n                \"MI_A2\"\n            ]\n        },\n        \"0x7125\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_2\",\n            \"aliases\": [\n                \"MI_As2\",\n                \"MI_Bb2\"\n            ]\n        },\n        \"0x7126\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_2\",\n            \"aliases\": [\n                \"MI_B2\"\n            ]\n        },\n        \"0x7127\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_3\",\n            \"aliases\": [\n                \"MI_C3\"\n            ]\n        },\n        \"0x7128\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_3\",\n            \"aliases\": [\n                \"MI_Cs3\",\n                \"MI_Db3\"\n            ]\n        },\n        \"0x7129\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_3\",\n            \"aliases\": [\n                \"MI_D3\"\n            ]\n        },\n        \"0x712A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_3\",\n            \"aliases\": [\n                \"MI_Ds3\",\n                \"MI_Eb3\"\n            ]\n        },\n        \"0x712B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_3\",\n            \"aliases\": [\n                \"MI_E3\"\n            ]\n        },\n        \"0x712C\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_3\",\n            \"aliases\": [\n                \"MI_F3\"\n            ]\n        },\n        \"0x712D\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_3\",\n            \"aliases\": [\n                \"MI_Fs3\",\n                \"MI_Gb3\"\n            ]\n        },\n        \"0x712E\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_3\",\n            \"aliases\": [\n                \"MI_G3\"\n            ]\n        },\n        \"0x712F\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_3\",\n            \"aliases\": [\n                \"MI_Gs3\",\n                \"MI_Ab3\"\n            ]\n        },\n        \"0x7130\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_3\",\n            \"aliases\": [\n                \"MI_A3\"\n            ]\n        },\n        \"0x7131\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_3\",\n            \"aliases\": [\n                \"MI_As3\",\n                \"MI_Bb3\"\n            ]\n        },\n        \"0x7132\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_3\",\n            \"aliases\": [\n                \"MI_B3\"\n            ]\n        },\n        \"0x7133\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_4\",\n            \"aliases\": [\n                \"MI_C4\"\n            ]\n        },\n        \"0x7134\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_4\",\n            \"aliases\": [\n                \"MI_Cs4\",\n                \"MI_Db4\"\n            ]\n        },\n        \"0x7135\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_4\",\n            \"aliases\": [\n                \"MI_D4\"\n            ]\n        },\n        \"0x7136\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_4\",\n            \"aliases\": [\n                \"MI_Ds4\",\n                \"MI_Eb4\"\n            ]\n        },\n        \"0x7137\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_4\",\n            \"aliases\": [\n                \"MI_E4\"\n            ]\n        },\n        \"0x7138\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_4\",\n            \"aliases\": [\n                \"MI_F4\"\n            ]\n        },\n        \"0x7139\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_4\",\n            \"aliases\": [\n                \"MI_Fs4\",\n                \"MI_Gb4\"\n            ]\n        },\n        \"0x713A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_4\",\n            \"aliases\": [\n                \"MI_G4\"\n            ]\n        },\n        \"0x713B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_4\",\n            \"aliases\": [\n                \"MI_Gs4\",\n                \"MI_Ab4\"\n            ]\n        },\n        \"0x713C\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_4\",\n            \"aliases\": [\n                \"MI_A4\"\n            ]\n        },\n        \"0x713D\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_4\",\n            \"aliases\": [\n                \"MI_As4\",\n                \"MI_Bb4\"\n            ]\n        },\n        \"0x713E\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_4\",\n            \"aliases\": [\n                \"MI_B4\"\n            ]\n        },\n        \"0x713F\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_5\",\n            \"aliases\": [\n                \"MI_C5\"\n            ]\n        },\n        \"0x7140\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_C_SHARP_5\",\n            \"aliases\": [\n                \"MI_Cs5\",\n                \"MI_Db5\"\n            ]\n        },\n        \"0x7141\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_5\",\n            \"aliases\": [\n                \"MI_D5\"\n            ]\n        },\n        \"0x7142\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_D_SHARP_5\",\n            \"aliases\": [\n                \"MI_Ds5\",\n                \"MI_Eb5\"\n            ]\n        },\n        \"0x7143\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_E_5\",\n            \"aliases\": [\n                \"MI_E5\"\n            ]\n        },\n        \"0x7144\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_5\",\n            \"aliases\": [\n                \"MI_F5\"\n            ]\n        },\n        \"0x7145\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_F_SHARP_5\",\n            \"aliases\": [\n                \"MI_Fs5\",\n                \"MI_Gb5\"\n            ]\n        },\n        \"0x7146\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_5\",\n            \"aliases\": [\n                \"MI_G5\"\n            ]\n        },\n        \"0x7147\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_G_SHARP_5\",\n            \"aliases\": [\n                \"MI_Gs5\",\n                \"MI_Ab5\"\n            ]\n        },\n        \"0x7148\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_5\",\n            \"aliases\": [\n                \"MI_A5\"\n            ]\n        },\n        \"0x7149\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_A_SHARP_5\",\n            \"aliases\": [\n                \"MI_As5\",\n                \"MI_Bb5\"\n            ]\n        },\n        \"0x714A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_NOTE_B_5\",\n            \"aliases\": [\n                \"MI_B5\"\n            ]\n        },\n        \"0x714B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_N2\",\n            \"aliases\": [\n                \"MI_OCN2\"\n            ]\n        },\n        \"0x714C\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_N1\",\n            \"aliases\": [\n                \"MI_OCN1\"\n            ]\n        },\n        \"0x714D\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_0\",\n            \"aliases\": [\n                \"MI_OC0\"\n            ]\n        },\n        \"0x714E\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_1\",\n            \"aliases\": [\n                \"MI_OC1\"\n            ]\n        },\n        \"0x714F\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_2\",\n            \"aliases\": [\n                \"MI_OC2\"\n            ]\n        },\n        \"0x7150\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_3\",\n            \"aliases\": [\n                \"MI_OC3\"\n            ]\n        },\n        \"0x7151\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_4\",\n            \"aliases\": [\n                \"MI_OC4\"\n            ]\n        },\n        \"0x7152\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_5\",\n            \"aliases\": [\n                \"MI_OC5\"\n            ]\n        },\n        \"0x7153\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_6\",\n            \"aliases\": [\n                \"MI_OC6\"\n            ]\n        },\n        \"0x7154\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_7\",\n            \"aliases\": [\n                \"MI_OC7\"\n            ]\n        },\n        \"0x7155\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_DOWN\",\n            \"aliases\": [\n                \"MI_OCTD\"\n            ]\n        },\n        \"0x7156\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_OCTAVE_UP\",\n            \"aliases\": [\n                \"MI_OCTU\"\n            ]\n        },\n        \"0x7157\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N6\",\n            \"aliases\": [\n                \"MI_TRN6\"\n            ]\n        },\n        \"0x7158\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N5\",\n            \"aliases\": [\n                \"MI_TRN5\"\n            ]\n        },\n        \"0x7159\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N4\",\n            \"aliases\": [\n                \"MI_TRN4\"\n            ]\n        },\n        \"0x715A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N3\",\n            \"aliases\": [\n                \"MI_TRN3\"\n            ]\n        },\n        \"0x715B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N2\",\n            \"aliases\": [\n                \"MI_TRN2\"\n            ]\n        },\n        \"0x715C\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_N1\",\n            \"aliases\": [\n                \"MI_TRN1\"\n            ]\n        },\n        \"0x715D\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_0\",\n            \"aliases\": [\n                \"MI_TR0\"\n            ]\n        },\n        \"0x715E\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_1\",\n            \"aliases\": [\n                \"MI_TR1\"\n            ]\n        },\n        \"0x715F\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_2\",\n            \"aliases\": [\n                \"MI_TR2\"\n            ]\n        },\n        \"0x7160\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_3\",\n            \"aliases\": [\n                \"MI_TR3\"\n            ]\n        },\n        \"0x7161\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_4\",\n            \"aliases\": [\n                \"MI_TR4\"\n            ]\n        },\n        \"0x7162\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_5\",\n            \"aliases\": [\n                \"MI_TR5\"\n            ]\n        },\n        \"0x7163\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_6\",\n            \"aliases\": [\n                \"MI_TR6\"\n            ]\n        },\n        \"0x7164\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_DOWN\",\n            \"aliases\": [\n                \"MI_TRSD\"\n            ]\n        },\n        \"0x7165\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_TRANSPOSE_UP\",\n            \"aliases\": [\n                \"MI_TRSU\"\n            ]\n        },\n        \"0x7166\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_0\",\n            \"aliases\": [\n                \"MI_VL0\"\n            ]\n        },\n        \"0x7167\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_1\",\n            \"aliases\": [\n                \"MI_VL1\"\n            ]\n        },\n        \"0x7168\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_2\",\n            \"aliases\": [\n                \"MI_VL2\"\n            ]\n        },\n        \"0x7169\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_3\",\n            \"aliases\": [\n                \"MI_VL3\"\n            ]\n        },\n        \"0x716A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_4\",\n            \"aliases\": [\n                \"MI_VL4\"\n            ]\n        },\n        \"0x716B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_5\",\n            \"aliases\": [\n                \"MI_VL5\"\n            ]\n        },\n        \"0x716C\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_6\",\n            \"aliases\": [\n                \"MI_VL6\"\n            ]\n        },\n        \"0x716D\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_7\",\n            \"aliases\": [\n                \"MI_VL7\"\n            ]\n        },\n        \"0x716E\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_8\",\n            \"aliases\": [\n                \"MI_VL8\"\n            ]\n        },\n        \"0x716F\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_9\",\n            \"aliases\": [\n                \"MI_VL9\"\n            ]\n        },\n        \"0x7170\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_10\",\n            \"aliases\": [\n                \"MI_VL10\"\n            ]\n        },\n        \"0x7171\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_DOWN\",\n            \"aliases\": [\n                \"MI_VELD\"\n            ]\n        },\n        \"0x7172\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_VELOCITY_UP\",\n            \"aliases\": [\n                \"MI_VELU\"\n            ]\n        },\n        \"0x7173\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_1\",\n            \"aliases\": [\n                \"MI_CH1\"\n            ]\n        },\n        \"0x7174\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_2\",\n            \"aliases\": [\n                \"MI_CH2\"\n            ]\n        },\n        \"0x7175\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_3\",\n            \"aliases\": [\n                \"MI_CH3\"\n            ]\n        },\n        \"0x7176\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_4\",\n            \"aliases\": [\n                \"MI_CH4\"\n            ]\n        },\n        \"0x7177\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_5\",\n            \"aliases\": [\n                \"MI_CH5\"\n            ]\n        },\n        \"0x7178\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_6\",\n            \"aliases\": [\n                \"MI_CH6\"\n            ]\n        },\n        \"0x7179\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_7\",\n            \"aliases\": [\n                \"MI_CH7\"\n            ]\n        },\n        \"0x717A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_8\",\n            \"aliases\": [\n                \"MI_CH8\"\n            ]\n        },\n        \"0x717B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_9\",\n            \"aliases\": [\n                \"MI_CH9\"\n            ]\n        },\n        \"0x717C\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_10\",\n            \"aliases\": [\n                \"MI_CH10\"\n            ]\n        },\n        \"0x717D\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_11\",\n            \"aliases\": [\n                \"MI_CH11\"\n            ]\n        },\n        \"0x717E\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_12\",\n            \"aliases\": [\n                \"MI_CH12\"\n            ]\n        },\n        \"0x717F\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_13\",\n            \"aliases\": [\n                \"MI_CH13\"\n            ]\n        },\n        \"0x7180\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_14\",\n            \"aliases\": [\n                \"MI_CH14\"\n            ]\n        },\n        \"0x7181\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_15\",\n            \"aliases\": [\n                \"MI_CH15\"\n            ]\n        },\n        \"0x7182\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_16\",\n            \"aliases\": [\n                \"MI_CH16\"\n            ]\n        },\n        \"0x7183\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_DOWN\",\n            \"aliases\": [\n                \"MI_CHND\"\n            ]\n        },\n        \"0x7184\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_CHANNEL_UP\",\n            \"aliases\": [\n                \"MI_CHNU\"\n            ]\n        },\n        \"0x7185\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_ALL_NOTES_OFF\",\n            \"aliases\": [\n                \"MI_AOFF\"\n            ]\n        },\n        \"0x7186\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_SUSTAIN\",\n            \"aliases\": [\n                \"MI_SUST\"\n            ]\n        },\n        \"0x7187\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_PORTAMENTO\",\n            \"aliases\": [\n                \"MI_PORT\"\n            ]\n        },\n        \"0x7188\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_SOSTENUTO\",\n            \"aliases\": [\n                \"MI_SOST\"\n            ]\n        },\n        \"0x7189\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_SOFT\",\n            \"aliases\": [\n                \"MI_SOFT\"\n            ]\n        },\n        \"0x718A\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_LEGATO\",\n            \"aliases\": [\n                \"MI_LEG\"\n            ]\n        },\n        \"0x718B\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_MODULATION\",\n            \"aliases\": [\n                \"MI_MOD\"\n            ]\n        },\n        \"0x718C\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_MODULATION_SPEED_DOWN\",\n            \"aliases\": [\n                \"MI_MODD\"\n            ]\n        },\n        \"0x718D\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_MODULATION_SPEED_UP\",\n            \"aliases\": [\n                \"MI_MODU\"\n            ]\n        },\n        \"0x718E\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_PITCH_BEND_DOWN\",\n            \"aliases\": [\n                \"MI_BNDD\"\n            ]\n        },\n        \"0x718F\": {\n            \"group\": \"midi\",\n            \"key\": \"QK_MIDI_PITCH_BEND_UP\",\n            \"aliases\": [\n                \"MI_BNDU\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.2_quantum.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7C77\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_TRI_LAYER_LOWER\",\n            \"aliases\": [\n                \"TL_LOWR\"\n            ]\n        },\n        \"0x7C78\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_TRI_LAYER_UPPER\",\n            \"aliases\": [\n                \"TL_UPPR\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.2_sequencer.hjson",
    "content": "{\n    \"keycodes\": {\n        \"!reset!\":0,\n\n        \"0x7200\": {\n            \"group\": \"sequencer\",\n            \"key\": \"QK_SEQUENCER_ON\",\n            \"aliases\": [\n                \"SQ_ON\"\n            ]\n        },\n        \"0x7201\": {\n            \"group\": \"sequencer\",\n            \"key\": \"QK_SEQUENCER_OFF\",\n            \"aliases\": [\n                \"SQ_OFF\"\n            ]\n        },\n        \"0x7202\": {\n            \"group\": \"sequencer\",\n            \"key\": \"QK_SEQUENCER_TOGGLE\",\n            \"aliases\": [\n                \"SQ_TOGG\"\n            ]\n        },\n        \"0x7203\": {\n            \"group\": \"sequencer\",\n            \"key\": \"QK_SEQUENCER_TEMPO_DOWN\",\n            \"aliases\": [\n                \"SQ_TMPD\"\n            ]\n        },\n        \"0x7204\": {\n            \"group\": \"sequencer\",\n            \"key\": \"QK_SEQUENCER_TEMPO_UP\",\n            \"aliases\": [\n                \"SQ_TMPU\"\n            ]\n        },\n        \"0x7205\": {\n            \"group\": \"sequencer\",\n            \"key\": \"QK_SEQUENCER_RESOLUTION_DOWN\",\n            \"aliases\": [\n                \"SQ_RESD\"\n            ]\n        },\n        \"0x7206\": {\n            \"group\": \"sequencer\",\n            \"key\": \"QK_SEQUENCER_RESOLUTION_UP\",\n            \"aliases\": [\n                \"SQ_RESU\"\n            ]\n        },\n        \"0x7207\": {\n            \"group\": \"sequencer\",\n            \"key\": \"QK_SEQUENCER_STEPS_ALL\",\n            \"aliases\": [\n                \"SQ_SALL\"\n            ]\n        },\n        \"0x7208\": {\n            \"group\": \"sequencer\",\n            \"key\": \"QK_SEQUENCER_STEPS_CLEAR\",\n            \"aliases\": [\n                \"SQ_SCLR\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.2_swap_hands.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x56F0\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"QK_SWAP_HANDS_TOGGLE\",\n            \"aliases\": [\n                \"SH_TOGG\"\n            ]\n        },\n        \"0x56F1\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"QK_SWAP_HANDS_TAP_TOGGLE\",\n            \"aliases\": [\n                \"SH_TT\"\n            ]\n        },\n        \"0x56F2\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"QK_SWAP_HANDS_MOMENTARY_ON\",\n            \"aliases\": [\n                \"SH_MON\"\n            ]\n        },\n        \"0x56F3\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"QK_SWAP_HANDS_MOMENTARY_OFF\",\n            \"aliases\": [\n                \"SH_MOFF\"\n            ]\n        },\n        \"0x56F4\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"QK_SWAP_HANDS_OFF\",\n            \"aliases\": [\n                \"SH_OFF\"\n            ]\n        },\n        \"0x56F5\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"QK_SWAP_HANDS_ON\",\n            \"aliases\": [\n                \"SH_ON\"\n            ]\n        },\n        \"0x56F6\": {\n            \"group\": \"swap_hands\",\n            \"key\": \"QK_SWAP_HANDS_ONE_SHOT\",\n            \"aliases\": [\n                \"SH_OS\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.2_user.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7E40\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_0\"\n        },\n        \"0x7E41\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_1\"\n        },\n        \"0x7E42\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_2\"\n        },\n        \"0x7E43\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_3\"\n        },\n        \"0x7E44\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_4\"\n        },\n        \"0x7E45\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_5\"\n        },\n        \"0x7E46\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_6\"\n        },\n        \"0x7E47\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_7\"\n        },\n        \"0x7E48\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_8\"\n        },\n        \"0x7E49\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_9\"\n        },\n        \"0x7E4A\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_10\"\n        },\n        \"0x7E4B\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_11\"\n        },\n        \"0x7E4C\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_12\"\n        },\n        \"0x7E4D\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_13\"\n        },\n        \"0x7E4E\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_14\"\n        },\n        \"0x7E4F\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_15\"\n        },\n        \"0x7E50\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_16\"\n        },\n        \"0x7E51\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_17\"\n        },\n         \"0x7E52\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_18\"\n        },\n        \"0x7E53\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_19\"\n        },\n        \"0x7E54\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_20\"\n        },\n        \"0x7E55\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_21\"\n        },\n        \"0x7E56\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_22\"\n        },\n        \"0x7E57\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_23\"\n        },\n        \"0x7E58\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_24\"\n        },\n        \"0x7E59\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_25\"\n        },\n        \"0x7E5A\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_26\"\n        },\n        \"0x7E5B\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_27\"\n        },\n        \"0x7E5C\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_28\"\n        },\n        \"0x7E5D\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_29\"\n        },\n        \"0x7E5E\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_30\"\n        },\n        \"0x7E5F\": {\n            \"group\": \"user\",\n            \"key\": \"QK_USER_31\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.3.hjson",
    "content": ""
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.3_quantum.hjson",
    "content": "{\n    \"keycodes\": {\n       \"0x7C79\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_REPEAT_KEY\",\n            \"aliases\": [\n                \"QK_REP\"\n            ]\n        },\n        \"0x7C7A\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_ALT_REPEAT_KEY\",\n            \"aliases\": [\n                \"QK_AREP\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.4.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7C7B\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_LAYER_LOCK\",\n            \"aliases\": [\n                \"QK_LLCK\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.4_lighting.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7810\": {\n            \"group\": \"led_matrix\",\n            \"key\": \"QK_LED_MATRIX_ON\",\n            \"aliases\": [\n                \"LM_ON\"\n            ]\n        },\n        \"0x7811\": {\n            \"group\": \"led_matrix\",\n            \"key\": \"QK_LED_MATRIX_OFF\",\n            \"aliases\": [\n                \"LM_OFF\"\n            ]\n        },\n        \"0x7812\": {\n            \"group\": \"led_matrix\",\n            \"key\": \"QK_LED_MATRIX_TOGGLE\",\n            \"aliases\": [\n                \"LM_TOGG\"\n            ]\n        },\n        \"0x7813\": {\n            \"group\": \"led_matrix\",\n            \"key\": \"QK_LED_MATRIX_MODE_NEXT\",\n            \"aliases\": [\n                \"LM_NEXT\"\n            ]\n        },\n        \"0x7814\": {\n            \"group\": \"led_matrix\",\n            \"key\": \"QK_LED_MATRIX_MODE_PREVIOUS\",\n            \"aliases\": [\n                \"LM_PREV\"\n            ]\n        },\n        \"0x7815\": {\n            \"group\": \"led_matrix\",\n            \"key\": \"QK_LED_MATRIX_BRIGHTNESS_UP\",\n            \"aliases\": [\n                \"LM_BRIU\"\n            ]\n        },\n        \"0x7816\": {\n            \"group\": \"led_matrix\",\n            \"key\": \"QK_LED_MATRIX_BRIGHTNESS_DOWN\",\n            \"aliases\": [\n                \"LM_BRID\"\n            ]\n        },\n        \"0x7817\": {\n            \"group\": \"led_matrix\",\n            \"key\": \"QK_LED_MATRIX_SPEED_UP\",\n            \"aliases\": [\n                \"LM_SPDU\"\n            ]\n        },\n        \"0x7818\": {\n            \"group\": \"led_matrix\",\n            \"key\": \"QK_LED_MATRIX_SPEED_DOWN\",\n            \"aliases\": [\n                \"LM_SPDD\"\n            ]\n        },\n\n        \"0x7820\": {\n            \"group\": \"underglow\",\n            \"key\": \"QK_UNDERGLOW_TOGGLE\",\n            \"aliases\": [\n                \"UG_TOGG\"\n            ]\n        },\n        \"0x7821\": {\n            \"group\": \"underglow\",\n            \"key\": \"QK_UNDERGLOW_MODE_NEXT\",\n            \"aliases\": [\n                \"!reset!\",\n                \"UG_NEXT\"\n            ]\n        },\n        \"0x7822\": {\n            \"group\": \"underglow\",\n            \"key\": \"QK_UNDERGLOW_MODE_PREVIOUS\",\n            \"aliases\": [\n                \"!reset!\",\n                \"UG_PREV\"\n            ]\n        },\n        \"0x7823\": {\n            \"group\": \"underglow\",\n            \"key\": \"QK_UNDERGLOW_HUE_UP\",\n            \"aliases\": [\n                \"UG_HUEU\"\n            ]\n        },\n        \"0x7824\": {\n            \"group\": \"underglow\",\n            \"key\": \"QK_UNDERGLOW_HUE_DOWN\",\n            \"aliases\": [\n                \"UG_HUED\"\n            ]\n        },\n        \"0x7825\": {\n            \"group\": \"underglow\",\n            \"key\": \"QK_UNDERGLOW_SATURATION_UP\",\n            \"aliases\": [\n                \"UG_SATU\"\n            ]\n        },\n        \"0x7826\": {\n            \"group\": \"underglow\",\n            \"key\": \"QK_UNDERGLOW_SATURATION_DOWN\",\n            \"aliases\": [\n                \"UG_SATD\"\n            ]\n        },\n        \"0x7827\": {\n            \"group\": \"underglow\",\n            \"key\": \"QK_UNDERGLOW_VALUE_UP\",\n            \"aliases\": [\n                \"UG_VALU\"\n            ]\n        },\n        \"0x7828\": {\n            \"group\": \"underglow\",\n            \"key\": \"QK_UNDERGLOW_VALUE_DOWN\",\n            \"aliases\": [\n                \"UG_VALD\"\n            ]\n        },\n        \"0x7829\": {\n            \"group\": \"underglow\",\n            \"key\": \"QK_UNDERGLOW_SPEED_UP\",\n            \"aliases\": [\n                \"UG_SPDU\"\n            ]\n        },\n        \"0x782A\": {\n            \"group\": \"underglow\",\n            \"key\": \"QK_UNDERGLOW_SPEED_DOWN\",\n            \"aliases\": [\n                \"UG_SPDD\"\n            ]\n        },\n\n        \"0x7840\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_ON\",\n            \"aliases\": [\n                \"RM_ON\"\n            ]\n        },\n        \"0x7841\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_OFF\",\n            \"aliases\": [\n                \"RM_OFF\"\n            ]\n        },\n        \"0x7842\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_TOGGLE\",\n            \"aliases\": [\n                \"RM_TOGG\"\n            ]\n        },\n        \"0x7843\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_MODE_NEXT\",\n            \"aliases\": [\n                \"RM_NEXT\"\n            ]\n        },\n        \"0x7844\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_MODE_PREVIOUS\",\n            \"aliases\": [\n                \"RM_PREV\"\n            ]\n        },\n        \"0x7845\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_HUE_UP\",\n            \"aliases\": [\n                \"RM_HUEU\"\n            ]\n        },\n        \"0x7846\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_HUE_DOWN\",\n            \"aliases\": [\n                \"RM_HUED\"\n            ]\n        },\n        \"0x7847\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_SATURATION_UP\",\n            \"aliases\": [\n                \"RM_SATU\"\n            ]\n        },\n        \"0x7848\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_SATURATION_DOWN\",\n            \"aliases\": [\n                \"RM_SATD\"\n            ]\n        },\n        \"0x7849\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_VALUE_UP\",\n            \"aliases\": [\n                \"RM_VALU\"\n            ]\n        },\n        \"0x784A\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_VALUE_DOWN\",\n            \"aliases\": [\n                \"RM_VALD\"\n            ]\n        },\n        \"0x784B\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_SPEED_UP\",\n            \"aliases\": [\n                \"RM_SPDU\"\n            ]\n        },\n        \"0x784C\": {\n            \"group\": \"rgb_matrix\",\n            \"key\": \"QK_RGB_MATRIX_SPEED_DOWN\",\n            \"aliases\": [\n                \"RM_SPDD\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.4_quantum.hjson",
    "content": ""
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.5.hjson",
    "content": ""
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.5_basic.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x00CD\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_CURSOR_UP\",\n            \"label\": \"Mouse cursor up\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_UP\"\n            ]\n        },\n        \"0x00CE\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_CURSOR_DOWN\",\n            \"label\": \"Mouse cursor down\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_DOWN\"\n            ]\n        },\n        \"0x00CF\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_CURSOR_LEFT\",\n            \"label\": \"Mouse cursor left\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_LEFT\"\n            ]\n        },\n        \"0x00D0\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_CURSOR_RIGHT\",\n            \"label\": \"Mouse cursor right\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_RGHT\"\n            ]\n        },\n        \"0x00D1\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_BUTTON_1\",\n            \"label\": \"Mouse button 1\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_BTN1\"\n            ]\n        },\n        \"0x00D2\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_BUTTON_2\",\n            \"label\": \"Mouse button 2\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_BTN2\"\n            ]\n        },\n        \"0x00D3\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_BUTTON_3\",\n            \"label\": \"Mouse button 3\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_BTN3\"\n            ]\n        },\n        \"0x00D4\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_BUTTON_4\",\n            \"label\": \"Mouse button 4\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_BTN4\"\n            ]\n        },\n        \"0x00D5\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_BUTTON_5\",\n            \"label\": \"Mouse button 5\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_BTN5\"\n            ]\n        },\n        \"0x00D6\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_BUTTON_6\",\n            \"label\": \"Mouse button 6\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_BTN6\"\n            ]\n        },\n        \"0x00D7\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_BUTTON_7\",\n            \"label\": \"Mouse button 7\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_BTN7\"\n            ]\n        },\n        \"0x00D8\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_BUTTON_8\",\n            \"label\": \"Mouse button 8\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_BTN8\"\n            ]\n        },\n        \"0x00D9\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_WHEEL_UP\",\n            \"label\": \"Mouse wheel up\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_WHLU\"\n            ]\n        },\n        \"0x00DA\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_WHEEL_DOWN\",\n            \"label\": \"Mouse wheel down\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_WHLD\"\n            ]\n        },\n        \"0x00DB\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_WHEEL_LEFT\",\n            \"label\": \"Mouse wheel left\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_WHLL\"\n            ]\n        },\n        \"0x00DC\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_WHEEL_RIGHT\",\n            \"label\": \"Mouse wheel right\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_WHLR\"\n            ]\n        },\n        \"0x00DD\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_ACCELERATION_0\",\n            \"label\": \"Set mouse acceleration to 0\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_ACL0\"\n            ]\n        },\n        \"0x00DE\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_ACCELERATION_1\",\n            \"label\": \"Set mouse acceleration to 1\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_ACL1\"\n            ]\n        },\n        \"0x00DF\": {\n            \"group\": \"mouse\",\n            \"key\": \"QK_MOUSE_ACCELERATION_2\",\n            \"label\": \"Set mouse acceleration to 2\",\n            \"aliases\": [\n                \"!reset!\",\n                \"MS_ACL2\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.6.hjson",
    "content": "{\n    \"ranges\": {\n        \"0x52E0/0x001F\": {\n            \"define\": \"QK_PERSISTENT_DEF_LAYER\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.6_connection.hjson",
    "content": "{\n    \"ranges\": {\n        \"0x7780/0x003F\": {\n            \"define\": \"QK_CONNECTION\"\n        }\n    }\n    \"keycodes\": {\n        \"0x7780\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_OUTPUT_AUTO\",\n            \"aliases\": [\n                \"OU_AUTO\"\n            ]\n        },\n        \"0x7781\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_OUTPUT_NEXT\",\n            \"aliases\": [\n                \"OU_NEXT\"\n            ]\n        },\n        \"0x7782\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_OUTPUT_PREV\",\n            \"aliases\": [\n                \"OU_PREV\"\n            ]\n        },\n        \"0x7783\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_OUTPUT_NONE\",\n            \"aliases\": [\n                \"OU_NONE\"\n            ]\n        },\n        \"0x7784\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_OUTPUT_USB\",\n            \"aliases\": [\n                \"OU_USB\"\n            ]\n        },\n        \"0x7785\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_OUTPUT_2P4GHZ\",\n            \"aliases\": [\n                \"OU_2P4G\"\n            ]\n        },\n        \"0x7786\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_OUTPUT_BLUETOOTH\",\n            \"aliases\": [\n                \"OU_BT\"\n            ]\n        },\n\n        \"0x7790\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_BLUETOOTH_PROFILE_NEXT\",\n            \"aliases\": [\n                \"BT_NEXT\"\n            ]\n        },\n        \"0x7791\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_BLUETOOTH_PROFILE_PREV\",\n            \"aliases\": [\n                \"BT_PREV\"\n            ]\n        },\n        \"0x7792\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_BLUETOOTH_UNPAIR\",\n            \"aliases\": [\n                \"BT_UNPR\"\n            ]\n        }\n        \"0x7793\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_BLUETOOTH_PROFILE1\",\n            \"aliases\": [\n                \"BT_PRF1\"\n            ]\n        },\n        \"0x7794\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_BLUETOOTH_PROFILE2\",\n            \"aliases\": [\n                \"BT_PRF2\"\n            ]\n        },\n        \"0x7795\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_BLUETOOTH_PROFILE3\",\n            \"aliases\": [\n                \"BT_PRF3\"\n            ]\n        },\n        \"0x7796\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_BLUETOOTH_PROFILE4\",\n            \"aliases\": [\n                \"BT_PRF4\"\n            ]\n        },\n        \"0x7797\": {\n            \"group\": \"connection\",\n            \"key\": \"QK_BLUETOOTH_PROFILE5\",\n            \"aliases\": [\n                \"BT_PRF5\"\n            ]\n        },\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.6_quantum.hjson",
    "content": "{\n    \"keycodes\": {\n        \"0x7C20\": \"!delete!\", // old QK_OUTPUT_AUTO\n        \"0x7C21\": \"!delete!\", // old QK_OUTPUT_USB\n        \"0x7C22\": \"!delete!\", // old QK_OUTPUT_BLUETOOTH\n        \"0x7C7B\": {\n            \"group\": \"quantum\",\n            \"key\": \"QK_LAYER_LOCK\",\n            \"aliases\": [\n                \"QK_LLCK\"\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/keycodes/keycodes_0.0.7.hjson",
    "content": "{\n    \"ranges\": {\n        \"0x77C0/0x003F\": {\n            \"define\": \"QK_COMMUNITY_MODULE\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/constants/module_hooks/0.1.0.hjson",
    "content": "{\n    keyboard_pre_init: {\n        ret_type: void\n        args: void\n    }\n    keyboard_post_init: {\n        ret_type: void\n        args: void\n    }\n    pre_process_record: {\n        ret_type: bool\n        args: uint16_t keycode, keyrecord_t *record\n        call_params: keycode, record\n    }\n    process_record: {\n        ret_type: bool\n        args: uint16_t keycode, keyrecord_t *record\n        call_params: keycode, record\n    }\n    post_process_record: {\n        ret_type: void\n        args: uint16_t keycode, keyrecord_t *record\n        call_params: keycode, record\n    }\n}\n"
  },
  {
    "path": "data/constants/module_hooks/1.0.0.hjson",
    "content": "{\n    housekeeping_task: {\n        ret_type: void\n        args: void\n    }\n    suspend_power_down: {\n        ret_type: void\n        args: void\n    }\n    suspend_wakeup_init: {\n        ret_type: void\n        args: void\n    }\n    shutdown: {\n        ret_type: bool\n        args: bool jump_to_bootloader\n        call_params: jump_to_bootloader\n    }\n    process_detected_host_os: {\n        ret_type: bool\n        args: os_variant_t os\n        call_params: os\n        guard: defined(OS_DETECTION_ENABLE)\n        header: os_detection.h\n    }\n}\n"
  },
  {
    "path": "data/constants/module_hooks/1.1.0.hjson",
    "content": "{\n    pointing_device_init: {\n        ret_type: void\n        args: void\n        guard: defined(POINTING_DEVICE_ENABLE)\n    }\n    pointing_device_task: {\n        ret_type: report_mouse_t\n        args: report_mouse_t mouse_report\n        call_params: mouse_report\n        guard: defined(POINTING_DEVICE_ENABLE)\n        header: report.h\n    }\n    rgb_matrix_indicators: {\n        ret_type: bool\n        args: void\n        guard: defined(RGB_MATRIX_ENABLE)\n        header: rgb_matrix.h\n    }\n    rgb_matrix_indicators_advanced: {\n        ret_type: bool\n        args: uint8_t led_min, uint8_t led_max\n        call_params: led_min, led_max\n        guard: defined(RGB_MATRIX_ENABLE)\n        header: rgb_matrix.h\n    }\n    led_matrix_indicators: {\n        ret_type: bool\n        args: void\n        guard: defined(LED_MATRIX_ENABLE)\n        header: led_matrix.h\n    }\n    led_matrix_indicators_advanced: {\n        ret_type: bool\n        args: uint8_t led_min, uint8_t led_max\n        call_params: led_min, led_max\n        guard: defined(LED_MATRIX_ENABLE)\n        header: led_matrix.h\n    }\n    default_layer_state_set: {\n        ret_type: layer_state_t\n        args: layer_state_t state\n        call_params: state\n        guard: !defined(NO_ACTION_LAYER)\n        header: action_layer.h\n    }\n    layer_state_set: {\n        ret_type: layer_state_t\n        args: layer_state_t state\n        call_params: state\n        guard: !defined(NO_ACTION_LAYER)\n        header: action_layer.h\n    }\n\n}\n"
  },
  {
    "path": "data/constants/module_hooks/1.1.1.hjson",
    "content": "{\n    // This version exists to signify addition of LED/RGB effect support.\n}\n"
  },
  {
    "path": "data/mappings/defaults.hjson",
    "content": "{\n    \"development_board\": {\n        \"bit_c_pro\": {\n            \"board\": \"QMK_PM2040\",\n            \"bootloader\": \"rp2040\",\n            \"processor\": \"RP2040\"\n        },\n        \"blackpill_f401\": {\n            \"board\": \"BLACKPILL_STM32_F401\",\n            \"bootloader\": \"stm32-dfu\",\n            \"processor\": \"STM32F401\"\n        },\n        \"blackpill_f411\": {\n            \"board\": \"BLACKPILL_STM32_F411\",\n            \"bootloader\": \"stm32-dfu\",\n            \"processor\": \"STM32F411\"\n        },\n        \"blok\": {\n            \"board\": \"QMK_BLOK\",\n            \"bootloader\": \"rp2040\",\n            \"processor\": \"RP2040\"\n        },\n        \"bluepill\": {\n            \"board\": \"STM32_F103_STM32DUINO\",\n            \"bootloader\": \"stm32duino\",\n            \"processor\": \"STM32F103\"\n        },\n        \"bonsai_c4\": {\n            \"board\": \"BONSAI_C4\",\n            \"bootloader\": \"stm32-dfu\",\n            \"processor\": \"STM32F411\"\n        },\n        \"elite_c\": {\n            \"bootloader\": \"atmel-dfu\",\n            \"pin_compatible\": \"promicro\",\n            \"processor\": \"atmega32u4\"\n        },\n        \"elite_pi\": {\n            \"board\": \"QMK_PM2040\",\n            \"bootloader\": \"rp2040\",\n            \"processor\": \"RP2040\"\n        },\n        \"helios\": {\n            \"board\": \"QMK_PM2040\",\n            \"bootloader\": \"rp2040\",\n            \"processor\": \"RP2040\"\n        },\n        \"imera\": {\n            \"processor\": \"RP2040\",\n            \"bootloader\": \"rp2040\",\n            \"board\": \"QMK_PM2040\"\n        },\n        \"kb2040\": {\n            \"board\": \"QMK_PM2040\",\n            \"bootloader\": \"rp2040\",\n            \"processor\": \"RP2040\"\n        },\n        \"liatris\": {\n            \"board\": \"QMK_PM2040\",\n            \"bootloader\": \"rp2040\",\n            \"processor\": \"RP2040\"\n        },\n        \"michi\": {\n            \"board\": \"QMK_PM2040\",\n            \"bootloader\": \"rp2040\",\n            \"processor\": \"RP2040\"\n        },\n        \"promicro\": {\n            \"bootloader\": \"caterina\",\n            \"pin_compatible\": \"promicro\",\n            \"processor\": \"atmega32u4\"\n        },\n        \"promicro_rp2040\": {\n            \"board\": \"QMK_PM2040\",\n            \"bootloader\": \"rp2040\",\n            \"processor\": \"RP2040\"\n        },\n        \"proton_c\": {\n            \"board\": \"QMK_PROTON_C\",\n            \"bootloader\": \"stm32-dfu\",\n            \"processor\": \"STM32F303\"\n        },\n        \"stemcell\": {\n            \"board\": \"STEMCELL\",\n            \"bootloader\": \"tinyuf2\",\n            \"processor\": \"STM32F411\"\n        },\n        \"svlinky\": {\n            \"board\": \"QMK_PM2040\",\n            \"bootloader\": \"rp2040\",\n            \"processor\": \"RP2040\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/mappings/info_config.hjson",
    "content": "// This file maps keys between `config.h` and `info.json`. It is used by QMK\n// to correctly and consistently map back and forth between the two systems.\n{\n    // Format:\n    // <config.h key>: {\"info_key\": <info.json key>, [\"value_type\": <value_type>], [\"to_json\": <true/false>], [\"to_c\": <true/false>]}\n    // value_type: one of \"array\", \"array.int\", \"bool, \"flag\", \"int\", \"hex\", \"list\", \"mapping\", \"str\", \"raw\"\n    // to_json: Default `true`. Set to `false` to exclude this mapping from info.json\n    // to_c: Default `true`. Set to `false` to exclude this mapping from config.h\n    // warn_duplicate: Default `true`. Set to `false` to turn off warning when a value exists in both places\n    // deprecated: Default `false`. Set to `true` to turn on warning when a value exists\n    // invalid: Default `false`. Set to `true` to generate errors when a value exists\n    // replace_with: use with a key marked deprecated or invalid to designate a replacement\n\n    // APA102\n    \"APA102_CI_PIN\": {\"info_key\": \"apa102.clock_pin\"},\n    \"APA102_DEFAULT_BRIGHTNESS\": {\"info_key\": \"apa102.default_brightness\", \"value_type\": \"int\"},\n    \"APA102_DI_PIN\": {\"info_key\": \"apa102.data_pin\"},\n\n    // Audio\n    \"AUDIO_DEFAULT_ON\": {\"info_key\": \"audio.default.on\", \"value_type\": \"bool\"},\n    \"AUDIO_DEFAULT_CLICKY_ON\": {\"info_key\": \"audio.default.clicky\", \"value_type\": \"bool\"},\n    \"AUDIO_POWER_CONTROL_PIN\": {\"info_key\": \"audio.power_control.pin\"},\n    \"AUDIO_POWER_CONTROL_PIN_ON_STATE\": {\"info_key\": \"audio.power_control.on_state\", \"value_type\": \"int\" },\n    \"AUDIO_VOICES\": {\"info_key\": \"audio.voices\", \"value_type\": \"flag\"},\n    \"SENDSTRING_BELL\": {\"info_key\": \"audio.macro_beep\", \"value_type\": \"flag\"},\n\n    // Backlight\n    \"BACKLIGHT_BREATHING\": {\"info_key\": \"backlight.breathing\", \"value_type\": \"flag\"},\n    \"BACKLIGHT_CAPS_LOCK\": {\"info_key\": \"backlight.as_caps_lock\", \"value_type\": \"flag\"},\n    \"BACKLIGHT_LEVELS\": {\"info_key\": \"backlight.levels\", \"value_type\": \"int\"},\n    \"BACKLIGHT_LIMIT_VAL\": {\"info_key\": \"backlight.max_brightness\", \"value_type\": \"int\"},\n    \"BACKLIGHT_ON_STATE\": {\"info_key\": \"backlight.on_state\", \"value_type\": \"int\"},\n    \"BACKLIGHT_PIN\": {\"info_key\": \"backlight.pin\"},\n    \"BACKLIGHT_PINS\": {\"info_key\": \"backlight.pins\", \"value_type\": \"array\"},\n    \"BREATHING_PERIOD\": {\"info_key\": \"backlight.breathing_period\", \"value_type\": \"int\"},\n    \"BACKLIGHT_DEFAULT_ON\": {\"info_key\": \"backlight.default.on\", \"value_type\": \"bool\"},\n    \"BACKLIGHT_DEFAULT_BREATHING\": {\"info_key\": \"backlight.default.breathing\", \"value_type\": \"bool\"},\n    \"BACKLIGHT_DEFAULT_LEVEL\": {\"info_key\": \"backlight.default.brightness\", \"value_type\": \"int\"},\n\n    // Bootmagic\n    \"BOOTMAGIC_COLUMN\": {\"info_key\": \"bootmagic.matrix.1\", \"value_type\": \"int\"},\n    \"BOOTMAGIC_COLUMN_RIGHT\": {\"info_key\": \"split.bootmagic.matrix.1\", \"value_type\": \"int\"},\n    \"BOOTMAGIC_ROW\": {\"info_key\": \"bootmagic.matrix.0\", \"value_type\": \"int\"},\n    \"BOOTMAGIC_ROW_RIGHT\": {\"info_key\": \"split.bootmagic.matrix.0\", \"value_type\": \"int\"},\n\n    // Caps Word\n    \"BOTH_SHIFTS_TURNS_ON_CAPS_WORD\": {\"info_key\": \"caps_word.both_shifts_turns_on\", \"value_type\": \"flag\"},\n    \"CAPS_WORD_IDLE_TIMEOUT\": {\"info_key\": \"caps_word.idle_timeout\", \"value_type\": \"int\"},\n    \"CAPS_WORD_INVERT_ON_SHIFT\": {\"info_key\": \"caps_word.invert_on_shift\", \"value_type\": \"flag\"},\n    \"DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD\": {\"info_key\": \"caps_word.double_tap_shift_turns_on\", \"value_type\": \"flag\"},\n\n    // Combos\n    \"COMBO_TERM\": {\"info_key\": \"combo.term\", \"value_type\": \"int\"},\n\n    \"DIP_SWITCH_MATRIX_GRID\": {\"info_key\": \"dip_switch.matrix_grid\", \"value_type\": \"array.array.int\", \"to_json\": false},\n    \"DIP_SWITCH_PINS\": {\"info_key\": \"dip_switch.pins\", \"value_type\": \"array\"},\n    \"DIP_SWITCH_PINS_RIGHT\": {\"info_key\": \"split.dip_switch.right.pins\", \"value_type\": \"array\"},\n\n    // Dynamic Keymap\n    \"DYNAMIC_KEYMAP_EEPROM_MAX_ADDR\": {\"info_key\": \"dynamic_keymap.eeprom_max_addr\", \"value_type\": \"int\"},\n    \"DYNAMIC_KEYMAP_LAYER_COUNT\": {\"info_key\": \"dynamic_keymap.layer_count\", \"value_type\": \"int\"},\n\n    // EEPROM\n    \"WEAR_LEVELING_BACKING_SIZE\": {\"info_key\": \"eeprom.wear_leveling.backing_size\", \"value_type\": \"int\", \"to_json\": false},\n    \"WEAR_LEVELING_LOGICAL_SIZE\": {\"info_key\": \"eeprom.wear_leveling.logical_size\", \"value_type\": \"int\", \"to_json\": false},\n\n    // host\n    \"NKRO_DEFAULT_ON\": {\"info_key\": \"host.default.nkro\", \"value_type\": \"bool\"},\n\n    // Layer locking\n    \"LAYER_LOCK_IDLE_TIMEOUT\": {\"info_key\": \"layer_lock.timeout\", \"value_type\": \"int\"},\n\n    // Indicators\n    \"LED_CAPS_LOCK_PIN\": {\"info_key\": \"indicators.caps_lock\"},\n    \"LED_NUM_LOCK_PIN\": {\"info_key\": \"indicators.num_lock\"},\n    \"LED_SCROLL_LOCK_PIN\": {\"info_key\": \"indicators.scroll_lock\"},\n    \"LED_COMPOSE_PIN\": {\"info_key\": \"indicators.compose\"},\n    \"LED_KANA_PIN\": {\"info_key\": \"indicators.kana\"},\n    \"LED_PIN_ON_STATE\": {\"info_key\": \"indicators.on_state\", \"value_type\": \"int\"},\n\n    // Joystick\n    \"JOYSTICK_AXIS_COUNT\": {\"info_key\": \"joystick.axis_count\", \"value_type\": \"int\"},\n    \"JOYSTICK_AXIS_RESOLUTION\": {\"info_key\": \"joystick.axis_resolution\", \"value_type\": \"int\"},\n    \"JOYSTICK_BUTTON_COUNT\": {\"info_key\": \"joystick.button_count\", \"value_type\": \"int\"},\n\n    // Leader Key\n    \"LEADER_PER_KEY_TIMING\": {\"info_key\": \"leader_key.timing\", \"value_type\": \"flag\"},\n    \"LEADER_KEY_STRICT_KEY_PROCESSING\": {\"info_key\": \"leader_key.strict_processing\", \"value_type\": \"flag\"},\n    \"LEADER_TIMEOUT\": {\"info_key\": \"leader_key.timeout\", \"value_type\": \"int\"},\n\n    // LED Matrix\n    \"LED_MATRIX_CENTER\": {\"info_key\": \"led_matrix.center_point\", \"value_type\": \"array.int\"},\n    \"LED_MATRIX_KEYRELEASES\": {\"info_key\": \"led_matrix.react_on_keyup\", \"value_type\": \"flag\"},\n    \"LED_MATRIX_LED_FLUSH_LIMIT\": {\"info_key\": \"led_matrix.led_flush_limit\", \"value_type\": \"int\"},\n    \"LED_MATRIX_LED_PROCESS_LIMIT\": {\"info_key\": \"led_matrix.led_process_limit\", \"value_type\": \"int\", \"to_json\": false},\n    \"LED_MATRIX_MAXIMUM_BRIGHTNESS\": {\"info_key\": \"led_matrix.max_brightness\", \"value_type\": \"int\"},\n    \"LED_MATRIX_SLEEP\": {\"info_key\": \"led_matrix.sleep\", \"value_type\": \"flag\"},\n    \"LED_MATRIX_SPD_STEP\": {\"info_key\": \"led_matrix.speed_steps\", \"value_type\": \"int\"},\n    \"LED_MATRIX_SPLIT\": {\"info_key\": \"led_matrix.split_count\", \"value_type\": \"array.int\"},\n    \"LED_MATRIX_TIMEOUT\": {\"info_key\": \"led_matrix.timeout\", \"value_type\": \"int\"},\n    \"LED_MATRIX_VAL_STEP\": {\"info_key\": \"led_matrix.val_steps\", \"value_type\": \"int\"},\n    \"LED_MATRIX_LED_COUNT\": {\"info_key\": \"led_matrix.led_count\", \"value_type\": \"int\", \"to_json\": false},\n    \"LED_MATRIX_DEFAULT_ON\": {\"info_key\": \"led_matrix.default.on\", \"value_type\": \"bool\"},\n    \"LED_MATRIX_DEFAULT_VAL\": {\"info_key\": \"led_matrix.default.val\", \"value_type\": \"int\"},\n    \"LED_MATRIX_DEFAULT_SPD\": {\"info_key\": \"led_matrix.default.speed\", \"value_type\": \"int\"},\n\n    // Locking Switch\n    \"LOCKING_SUPPORT_ENABLE\": {\"info_key\": \"qmk.locking.enabled\", \"value_type\": \"flag\"},\n    \"LOCKING_RESYNC_ENABLE\": {\"info_key\": \"qmk.locking.resync\", \"value_type\": \"flag\"},\n\n    // LUFA Bootloader\n    \"QMK_ESC_INPUT\": {\"info_key\": \"qmk_lufa_bootloader.esc_input\"},\n    \"QMK_ESC_OUTPUT\": {\"info_key\": \"qmk_lufa_bootloader.esc_output\"},\n    \"QMK_LED\": {\"info_key\": \"qmk_lufa_bootloader.led\"},\n    \"QMK_SPEAKER\": {\"info_key\": \"qmk_lufa_bootloader.speaker\"},\n\n    // Matrix\n    \"DEBOUNCE\": {\"info_key\": \"debounce\", \"value_type\": \"int\"},\n    \"DIODE_DIRECTION\": {\"info_key\": \"diode_direction\"},\n    \"MATRIX_HAS_GHOST\": {\"info_key\": \"matrix_pins.ghost\", \"value_type\": \"flag\"},\n    \"MATRIX_INPUT_PRESSED_STATE\": {\"info_key\": \"matrix_pins.input_pressed_state\", \"value_type\": \"int\"},\n    \"MATRIX_IO_DELAY\": {\"info_key\": \"matrix_pins.io_delay\", \"value_type\": \"int\"},\n\n    // Mouse Keys\n    \"MOUSEKEY_DELAY\": {\"info_key\": \"mousekey.delay\", \"value_type\": \"int\"},\n    \"MOUSEKEY_INTERVAL\": {\"info_key\": \"mousekey.interval\", \"value_type\": \"int\"},\n    \"MOUSEKEY_MAX_SPEED\": {\"info_key\": \"mousekey.max_speed\", \"value_type\": \"int\"},\n    \"MOUSEKEY_TIME_TO_MAX\": {\"info_key\": \"mousekey.time_to_max\", \"value_type\": \"int\"},\n    \"MOUSEKEY_WHEEL_DELAY\": {\"info_key\": \"mousekey.wheel_delay\", \"value_type\": \"int\"},\n\n    // One Shot\n    \"ONESHOT_TIMEOUT\": {\"info_key\": \"oneshot.timeout\", \"value_type\": \"int\"},\n    \"ONESHOT_TAP_TOGGLE\": {\"info_key\": \"oneshot.tap_toggle\", \"value_type\": \"int\"},\n\n    // PS/2\n    \"PS2_CLOCK_PIN\": {\"info_key\": \"ps2.clock_pin\"},\n    \"PS2_DATA_PIN\": {\"info_key\": \"ps2.data_pin\"},\n\n    // RGB Matrix\n    \"RGB_MATRIX_CENTER\": {\"info_key\": \"rgb_matrix.center_point\", \"value_type\": \"array.int\"},\n    \"RGB_MATRIX_HUE_STEP\": {\"info_key\": \"rgb_matrix.hue_steps\", \"value_type\": \"int\"},\n    \"RGB_MATRIX_KEYRELEASES\": {\"info_key\": \"rgb_matrix.react_on_keyup\", \"value_type\": \"flag\"},\n    \"RGB_MATRIX_LED_FLUSH_LIMIT\": {\"info_key\": \"rgb_matrix.led_flush_limit\", \"value_type\": \"int\"},\n    \"RGB_MATRIX_LED_PROCESS_LIMIT\": {\"info_key\": \"rgb_matrix.led_process_limit\", \"value_type\": \"int\", \"to_json\": false},\n    \"RGB_MATRIX_MAXIMUM_BRIGHTNESS\": {\"info_key\": \"rgb_matrix.max_brightness\", \"value_type\": \"int\"},\n    \"RGB_MATRIX_SAT_STEP\": {\"info_key\": \"rgb_matrix.sat_steps\", \"value_type\": \"int\"},\n    \"RGB_MATRIX_SLEEP\": {\"info_key\": \"rgb_matrix.sleep\", \"value_type\": \"flag\"},\n    \"RGB_MATRIX_SPD_STEP\": {\"info_key\": \"rgb_matrix.speed_steps\", \"value_type\": \"int\"},\n    \"RGB_MATRIX_SPLIT\": {\"info_key\": \"rgb_matrix.split_count\", \"value_type\": \"array.int\"},\n    \"RGB_MATRIX_TIMEOUT\": {\"info_key\": \"rgb_matrix.timeout\", \"value_type\": \"int\"},\n    \"RGB_MATRIX_VAL_STEP\": {\"info_key\": \"rgb_matrix.val_steps\", \"value_type\": \"int\"},\n    \"RGB_MATRIX_LED_COUNT\": {\"info_key\": \"rgb_matrix.led_count\", \"value_type\": \"int\", \"to_json\": false},\n    \"RGB_MATRIX_DEFAULT_ON\": {\"info_key\": \"rgb_matrix.default.on\", \"value_type\": \"bool\"},\n    \"RGB_MATRIX_DEFAULT_HUE\": {\"info_key\": \"rgb_matrix.default.hue\", \"value_type\": \"int\"},\n    \"RGB_MATRIX_DEFAULT_SAT\": {\"info_key\": \"rgb_matrix.default.sat\", \"value_type\": \"int\"},\n    \"RGB_MATRIX_DEFAULT_VAL\": {\"info_key\": \"rgb_matrix.default.val\", \"value_type\": \"int\"},\n    \"RGB_MATRIX_DEFAULT_SPD\": {\"info_key\": \"rgb_matrix.default.speed\", \"value_type\": \"int\"},\n\n    // RGBLight\n    \"RGBLED_SPLIT\": {\"info_key\": \"rgblight.split_count\", \"value_type\": \"array.int\"},\n    \"RGBLIGHT_HUE_STEP\": {\"info_key\": \"rgblight.hue_steps\", \"value_type\": \"int\"},\n    \"RGBLIGHT_LAYER_BLINK\": {\"info_key\": \"rgblight.layers.blink\", \"value_type\": \"flag\"},\n    \"RGBLIGHT_LAYERS\": {\"info_key\": \"rgblight.layers.enabled\", \"value_type\": \"flag\"},\n    \"RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF\": {\"info_key\": \"rgblight.layers.override_rgb\", \"value_type\": \"flag\"},\n    \"RGBLIGHT_LED_COUNT\": {\"info_key\": \"rgblight.led_count\", \"value_type\": \"int\"},\n    \"RGBLIGHT_LED_MAP\": {\"info_key\": \"rgblight.led_map\", \"value_type\": \"array.int\"},\n    \"RGBLIGHT_LIMIT_VAL\": {\"info_key\": \"rgblight.max_brightness\", \"value_type\": \"int\"},\n    \"RGBLIGHT_MAX_LAYERS\": {\"info_key\": \"rgblight.layers.max\", \"value_type\": \"int\"},\n    \"RGBLIGHT_SAT_STEP\": {\"info_key\": \"rgblight.saturation_steps\", \"value_type\": \"int\"},\n    \"RGBLIGHT_SLEEP\": {\"info_key\": \"rgblight.sleep\", \"value_type\": \"flag\"},\n    \"RGBLIGHT_SPLIT\": {\"info_key\": \"rgblight.split\", \"value_type\": \"flag\"},\n    \"RGBLIGHT_VAL_STEP\": {\"info_key\": \"rgblight.brightness_steps\", \"value_type\": \"int\"},\n    \"RGBLIGHT_DEFAULT_ON\": {\"info_key\": \"rgblight.default.on\", \"value_type\": \"bool\"},\n    \"RGBLIGHT_DEFAULT_HUE\": {\"info_key\": \"rgblight.default.hue\", \"value_type\": \"int\"},\n    \"RGBLIGHT_DEFAULT_SAT\": {\"info_key\": \"rgblight.default.sat\", \"value_type\": \"int\"},\n    \"RGBLIGHT_DEFAULT_VAL\": {\"info_key\": \"rgblight.default.val\", \"value_type\": \"int\"},\n    \"RGBLIGHT_DEFAULT_SPD\": {\"info_key\": \"rgblight.default.speed\", \"value_type\": \"int\"},\n\n    // Secure\n    \"SECURE_IDLE_TIMEOUT\": {\"info_key\": \"secure.idle_timeout\", \"value_type\": \"int\"},\n    \"SECURE_UNLOCK_SEQUENCE\": {\"info_key\": \"secure.unlock_sequence\", \"value_type\": \"array.array.int\", \"to_json\": false},\n    \"SECURE_UNLOCK_TIMEOUT\": {\"info_key\": \"secure.unlock_timeout\", \"value_type\": \"int\"},\n\n    // Split Keyboard\n    \"SOFT_SERIAL_PIN\": {\"info_key\": \"split.serial.pin\"},\n    \"SOFT_SERIAL_SPEED\": {\"info_key\": \"split.soft_serial_speed\"},\n    \"SPLIT_HAND_MATRIX_GRID\": {\"info_key\": \"split.handedness.matrix_grid\", \"value_type\": \"array\", \"to_c\": false},\n    \"SPLIT_HAND_PIN\": {\"info_key\": \"split.handedness.pin\"},\n    \"SPLIT_USB_DETECT\": {\"info_key\": \"split.usb_detect.enabled\", \"value_type\": \"flag\"},\n    \"SPLIT_USB_TIMEOUT\": {\"info_key\": \"split.usb_detect.timeout\", \"value_type\": \"int\"},\n    \"SPLIT_USB_TIMEOUT_POLL\": {\"info_key\": \"split.usb_detect.polling_interval\", \"value_type\": \"int\"},\n    \"SPLIT_WATCHDOG_ENABLE\": {\"info_key\": \"split.transport.watchdog\", \"value_type\": \"flag\"},\n    \"SPLIT_WATCHDOG_TIMEOUT\": {\"info_key\": \"split.transport.watchdog_timeout\", \"value_type\": \"int\"},\n    \"SPLIT_ACTIVITY_ENABLE\": {\"info_key\": \"split.transport.sync.activity\", \"value_type\": \"flag\"},\n    \"SPLIT_DETECTED_OS_ENABLE\": {\"info_key\": \"split.transport.sync.detected_os\", \"value_type\": \"flag\"},\n    \"SPLIT_HAPTIC_ENABLE\": {\"info_key\": \"split.transport.sync.haptic\", \"value_type\": \"flag\"},\n    \"SPLIT_LAYER_STATE_ENABLE\": {\"info_key\": \"split.transport.sync.layer_state\", \"value_type\": \"flag\"},\n    \"SPLIT_LED_STATE_ENABLE\": {\"info_key\": \"split.transport.sync.indicators\", \"value_type\": \"flag\"},\n    \"SPLIT_TRANSPORT_MIRROR\": {\"info_key\": \"split.transport.sync.matrix_state\", \"value_type\": \"flag\"},\n    \"SPLIT_MODS_ENABLE\": {\"info_key\": \"split.transport.sync.modifiers\", \"value_type\": \"flag\"},\n    \"SPLIT_OLED_ENABLE\": {\"info_key\": \"split.transport.sync.oled\", \"value_type\": \"flag\"},\n    \"SPLIT_ST7565_ENABLE\": {\"info_key\": \"split.transport.sync.st7565\", \"value_type\": \"flag\"},\n    \"SPLIT_WPM_ENABLE\": {\"info_key\": \"split.transport.sync.wpm\", \"value_type\": \"flag\"},\n\n    // Tapping\n    \"CHORDAL_HOLD\": {\"info_key\": \"tapping.chordal_hold\", \"value_type\": \"flag\"},\n    \"FLOW_TAP_TERM\": {\"info_key\": \"tapping.flow_tap_term\", \"value_type\": \"int\"},\n    \"HOLD_ON_OTHER_KEY_PRESS\": {\"info_key\": \"tapping.hold_on_other_key_press\", \"value_type\": \"flag\"},\n    \"HOLD_ON_OTHER_KEY_PRESS_PER_KEY\": {\"info_key\": \"tapping.hold_on_other_key_press_per_key\", \"value_type\": \"flag\"},\n    \"PERMISSIVE_HOLD\": {\"info_key\": \"tapping.permissive_hold\", \"value_type\": \"flag\"},\n    \"PERMISSIVE_HOLD_PER_KEY\": {\"info_key\": \"tapping.permissive_hold_per_key\", \"value_type\": \"flag\"},\n    \"RETRO_TAPPING\": {\"info_key\": \"tapping.retro\", \"value_type\": \"flag\"},\n    \"RETRO_TAPPING_PER_KEY\": {\"info_key\": \"tapping.retro_per_key\", \"value_type\": \"flag\"},\n    \"TAP_CODE_DELAY\": {\"info_key\": \"qmk.tap_keycode_delay\", \"value_type\": \"int\"},\n    \"TAP_HOLD_CAPS_DELAY\": {\"info_key\": \"qmk.tap_capslock_delay\", \"value_type\": \"int\"},\n    \"TAPPING_TERM\": {\"info_key\": \"tapping.term\", \"value_type\": \"int\"},\n    \"TAPPING_TERM_PER_KEY\": {\"info_key\": \"tapping.term_per_key\", \"value_type\": \"flag\"},\n    \"TAPPING_TOGGLE\": {\"info_key\": \"tapping.toggle\", \"value_type\": \"int\"},\n\n    // USB\n    \"USB_MAX_POWER_CONSUMPTION\": {\"info_key\": \"usb.max_power\", \"value_type\": \"int\"},\n    \"USB_POLLING_INTERVAL_MS\": {\"info_key\": \"usb.polling_interval\", \"value_type\": \"int\"},\n    \"USB_SUSPEND_WAKEUP_DELAY\": {\"info_key\": \"usb.suspend_wakeup_delay\", \"value_type\": \"int\"},\n\n    // WS2812\n    \"WS2812_DI_PIN\": {\"info_key\": \"ws2812.pin\"},\n    \"WS2812_I2C_ADDRESS\": {\"info_key\": \"ws2812.i2c_address\", \"value_type\": \"hex\"},\n    \"WS2812_I2C_TIMEOUT\": {\"info_key\": \"ws2812.i2c_timeout\", \"value_type\": \"int\"},\n    \"WS2812_RGBW\": {\"info_key\": \"ws2812.rgbw\", \"value_type\": \"flag\"},\n\n    \"LAYOUTS\": {\"info_key\": \"layout_aliases\", \"value_type\": \"mapping\"},\n\n    // Items we want flagged in lint\n    \"DEBOUNCING_DELAY\": {\"info_key\": \"_invalid.debouncing_delay\", \"invalid\": true, \"replace_with\": \"DEBOUNCE\"},\n    \"DESCRIPTION\": {\"info_key\": \"_invalid.usb_description\", \"invalid\": true},\n    \"IGNORE_MOD_TAP_INTERRUPT\": {\"info_key\": \"_invalid.ignore_mod_tap_interrupt\", \"value_type\": \"flag\", \"invalid\": true},\n    \"IGNORE_MOD_TAP_INTERRUPT_PER_KEY\": {\"info_key\": \"_invalid.ignore_mod_tap_interrupt_per_key\", \"invalid\": true},\n    \"LED_DISABLE_WHEN_USB_SUSPENDED\": {\"info_key\": \"_invalid.led_matrix_sleep\", \"invalid\": true, \"replace_with\": \"LED_MATRIX_SLEEP\"},\n    \"NO_ACTION_FUNCTION\": {\"info_key\": \"_invalid.no_action_function\", \"invalid\": true},\n    \"NO_ACTION_MACRO\": {\"info_key\": \"_invalid.no_action_macro\", \"invalid\": true},\n    \"PREVENT_STUCK_MODIFIERS\": {\"info_key\": \"_invalid.prevent_stuck_mods\", \"invalid\": true},\n    \"QMK_KEYS_PER_SCAN\": {\"info_key\": \"qmk.keys_per_scan\", \"value_type\": \"int\", \"deprecated\": true},\n    \"RGB_DI_PIN\": {\"info_key\": \"rgblight.pin\", \"invalid\": true, \"replace_with\": \"WS2812_DI_PIN or APA102_DI_PIN\"},\n    \"RGBW\": {\"info_key\": \"rgblight.rgbw\", \"invalid\": true, \"replace_with\": \"WS2812_RGBW\"},\n    \"RGB_DISABLE_WHEN_USB_SUSPENDED\": {\"info_key\": \"_invalid.rgb_matrix_sleep\", \"invalid\": true, \"replace_with\": \"RGB_MATRIX_SLEEP\"},\n    \"RGBLIGHT_ANIMATIONS\": {\"info_key\": \"_invalid.rgblight.animations.all\", \"value_type\": \"flag\", \"invalid\": true},\n    \"TAPPING_FORCE_HOLD\": {\"info_key\": \"tapping.force_hold\", \"value_type\": \"flag\", \"deprecated\": true},\n    \"TAPPING_FORCE_HOLD_PER_KEY\": {\"info_key\": \"tapping.force_hold_per_key\", \"value_type\": \"flag\", \"deprecated\": true},\n    \"UNUSED_PINS\": {\"info_key\": \"_invalid.unused_pins\", \"deprecated\": true},\n    \"COMBO_COUNT\": {\"info_key\": \"_invalid.combo.count\", \"invalid\": true},\n\n    // USB params, need to mark as failure when specified in config.h, rather than deprecated\n    \"DEVICE_VER\": {\"info_key\": \"usb.device_version\", \"value_type\": \"bcd_version\", \"deprecated\": true, \"replace_with\": \"`usb.device_version` in info.json\"},\n    \"MANUFACTURER\": {\"info_key\": \"manufacturer\", \"value_type\": \"str\", \"deprecated\": true, \"replace_with\": \"`manufacturer` in info.json\"},\n    \"PRODUCT\": {\"info_key\": \"keyboard_name\", \"warn_duplicate\": false, \"value_type\": \"str\", \"deprecated\": true, \"replace_with\": \"`keyboard_name` in info.json\"},\n    \"PRODUCT_ID\": {\"info_key\": \"usb.pid\", \"value_type\": \"hex\", \"deprecated\": true, \"replace_with\": \"`usb.pid` in info.json\"},\n    \"VENDOR_ID\": {\"info_key\": \"usb.vid\", \"value_type\": \"hex\", \"deprecated\": true, \"replace_with\": \"`usb.vid` in info.json\"},\n    \"FORCE_NKRO\": {\"info_key\": \"usb.force_nkro\", \"value_type\": \"flag\", \"deprecated\": true, \"replace_with\": \"`host.default.nkro` in info.json\"},\n\n    // Items we want flagged in lint\n    \"VIAL_KEYBOARD_UID\": {\"info_key\": \"_invalid.vial_uid\", \"invalid\": true},\n    \"VIAL_UNLOCK_COMBO_COLS\": {\"info_key\": \"_invalid.vial_unlock_cols\", \"invalid\": true},\n    \"VIAL_UNLOCK_COMBO_ROWS\": {\"info_key\": \"_invalid.vial_unlock_rows\", \"invalid\": true}\n}\n"
  },
  {
    "path": "data/mappings/info_defaults.hjson",
    "content": "{\n    \"bootmagic\": {\n        \"matrix\": [0, 0]\n    },\n    \"backlight\": {\n        \"default\": {\n            \"on\": true\n        },\n        \"breathing_period\": 6,\n        \"levels\": 3,\n        \"on_state\": 1\n    },\n    \"debounce\": 5,\n    \"features\": {\n        \"command\": false,\n        \"console\": false\n    },\n    \"indicators\": {\n        \"on_state\": 1\n    },\n    \"led_matrix\": {\n        \"default\": {\n            \"animation\": \"solid\",\n            \"on\": true,\n            \"val\": 255,\n            \"speed\": 128\n        },\n        \"led_flush_limit\": 16,\n        \"max_brightness\": 255,\n        \"sleep\": false,\n        \"speed_steps\": 16,\n        \"val_steps\": 16\n    },\n    \"rgblight\": {\n        \"default\": {\n            \"animation\": \"static_light\",\n            \"on\": true,\n            \"hue\": 0,\n            \"sat\": 255,\n            \"val\": 255,\n            \"speed\": 0\n        },\n        \"brightness_steps\": 17,\n        \"hue_steps\": 8,\n        \"max_brightness\": 255,\n        \"saturation_steps\": 17,\n        \"sleep\": false\n    },\n    \"rgb_matrix\": {\n        \"default\": {\n            \"animation\": \"cycle_left_right\",\n            \"on\": true,\n            \"hue\": 0,\n            \"sat\": 255,\n            \"val\": 255,\n            \"speed\": 128\n        },\n        \"hue_steps\": 8,\n        \"led_flush_limit\": 16,\n        \"max_brightness\": 255,\n        \"sat_steps\": 16,\n        \"sleep\": false,\n        \"speed_steps\": 16,\n        \"val_steps\": 16\n    },\n    \"split\": {\n        \"serial\": {\n            \"driver\": \"bitbang\"\n        }\n    },\n    \"ws2812\": {\n        \"driver\": \"bitbang\"\n    }\n}\n"
  },
  {
    "path": "data/mappings/info_rules.hjson",
    "content": "// This file maps keys between `rules.mk` and `info.json`. It is used by QMK\n// to correctly and consistently map back and forth between the two systems.\n{\n    // Format:\n    // <rules.mk key>: {\"info_key\": <info.json key>, [\"value_type\": <value_type>], [\"to_json\": <true/false>], [\"to_c\": <true/false>]}\n    // value_type: one of \"array\", \"array.int\", \"bool\", \"int\", \"list\", \"hex\", \"mapping\", \"str\", \"raw\"\n    // to_json: Default `true`. Set to `false` to exclude this mapping from info.json\n    // to_c: Default `true`. Set to `false` to exclude this mapping from rules.mk\n    // warn_duplicate: Default `true`. Set to `false` to turn off warning when a value exists in both places\n    // deprecated: Default `false`. Set to `true` to turn on warning when a value exists\n    // invalid: Default `false`. Set to `true` to generate errors when a value exists\n    // replace_with: use with a key marked deprecated or invalid to designate a replacement\n\n    \"AUDIO_DRIVER\": {\"info_key\": \"audio.driver\"},\n    \"BACKLIGHT_DRIVER\": {\"info_key\": \"backlight.driver\"},\n    \"BLUETOOTH_DRIVER\": {\"info_key\": \"bluetooth.driver\"},\n    \"BOARD\": {\"info_key\": \"board\"},\n    \"BOOTLOADER\": {\"info_key\": \"bootloader\", \"warn_duplicate\": false},\n    \"BOOTMAGIC_ENABLE\": {\"info_key\": \"bootmagic.enabled\", \"value_type\": \"bool\"},\n    \"CAPS_WORD_ENABLE\": {\"info_key\": \"caps_word.enabled\", \"value_type\": \"bool\"},\n    \"DIP_SWITCH_ENABLE\": {\"info_key\": \"dip_switch.enabled\", \"value_type\": \"bool\"},\n    \"DEBOUNCE_TYPE\": {\"info_key\": \"build.debounce_type\"},\n    \"EEPROM_DRIVER\": {\"info_key\": \"eeprom.driver\"},\n    \"ENCODER_ENABLE\": {\"info_key\": \"encoder.enabled\", \"value_type\": \"bool\"},\n    \"ENCODER_DRIVER\": {\"info_key\": \"encoder.driver\"},\n    \"FIRMWARE_FORMAT\": {\"info_key\": \"build.firmware_format\"},\n    \"HAPTIC_DRIVER\": {\"info_key\": \"haptic.driver\"},\n    \"JOYSTICK_DRIVER\": {\"info_key\": \"joystick.driver\"},\n    \"JOYSTICK_ENABLE\": {\"info_key\": \"joystick.enabled\", \"value_type\": \"bool\"},\n    \"KEYBOARD_SHARED_EP\": {\"info_key\": \"usb.shared_endpoint.keyboard\", \"value_type\": \"bool\"},\n    \"LAYOUTS\": {\"info_key\": \"community_layouts\", \"value_type\": \"list\"},\n    \"LED_MATRIX_DRIVER\": {\"info_key\": \"led_matrix.driver\"},\n    \"LTO_ENABLE\": {\"info_key\": \"build.lto\", \"value_type\": \"bool\"},\n    \"MCU\": {\"info_key\": \"processor\", \"warn_duplicate\": false},\n    \"MOUSE_SHARED_EP\": {\"info_key\": \"usb.shared_endpoint.mouse\", \"value_type\": \"bool\"},\n    \"MOUSEKEY_ENABLE\": {\"info_key\": \"mouse_key.enabled\", \"value_type\": \"bool\"},\n    \"NO_USB_STARTUP_CHECK\": {\"info_key\": \"usb.no_startup_check\", \"value_type\": \"bool\"},\n    \"PIN_COMPATIBLE\": {\"info_key\": \"pin_compatible\"},\n    \"PLATFORM_KEY\": {\"info_key\": \"platform_key\", \"to_json\": false},\n    \"PS2_DRIVER\": {\"info_key\": \"ps2.driver\"},\n    \"PS2_ENABLE\": {\"info_key\": \"ps2.enabled\", \"value_type\": \"bool\"},\n    \"PS2_MOUSE_ENABLE\": {\"info_key\": \"ps2.mouse_enabled\", \"value_type\": \"bool\"},\n    \"RGB_MATRIX_DRIVER\": {\"info_key\": \"rgb_matrix.driver\"},\n    \"RGBLIGHT_DRIVER\": {\"info_key\": \"rgblight.driver\"},\n    \"SECURE_ENABLE\": {\"info_key\": \"secure.enabled\", \"value_type\": \"bool\"},\n    \"SERIAL_DRIVER\": {\"info_key\": \"split.serial.driver\"},\n    \"SPLIT_KEYBOARD\": {\"info_key\": \"split.enabled\", \"value_type\": \"bool\"},\n    \"SPLIT_TRANSPORT\": {\"info_key\": \"split.transport.protocol\", \"to_c\": false},\n    \"STENO_ENABLE\": {\"info_key\": \"stenography.enabled\", \"value_type\": \"bool\"},\n    \"STENO_PROTOCOL\": {\"info_key\": \"stenography.protocol\"},\n    \"USB_WAIT_FOR_ENUMERATION\": {\"info_key\": \"usb.wait_for_enumeration\", \"value_type\": \"bool\"},\n    \"WEAR_LEVELING_DRIVER\": {\"info_key\": \"eeprom.wear_leveling.driver\"},\n    \"WS2812_DRIVER\": {\"info_key\": \"ws2812.driver\"},\n\n    // Items we want flagged in lint\n    \"DEFAULT_FOLDER\": {\"info_key\": \"_deprecated.default_folder\", \"deprecated\": true},\n    \"CTPC\": {\"info_key\": \"_invalid.ctpc\", \"invalid\": true, \"replace_with\": \"CONVERT_TO=proton_c\"},\n    \"CONVERT_TO_PROTON_C\": {\"info_key\": \"_invalid.ctpc\", \"invalid\": true, \"replace_with\": \"CONVERT_TO=proton_c\"},\n    \"VIAL_ENABLE\": {\"info_key\": \"_invalid.vial\", \"invalid\": true}\n}\n"
  },
  {
    "path": "data/mappings/keyboard_aliases.hjson",
    "content": "{\n    // Format for each entry:\n    // \"<alias>\": {\n    //     \"target\": \"<keyboard_folder>\"\n    // }\n    //\n\n    /* This list of aliases is for testing purposes -- ensures \"linked list\" recursive traversal works correctly. */\n    \"_test_a\": { \"target\": \"_test_b\" },\n    \"_test_b\": { \"target\": \"_test_c\" },\n    \"_test_c\": { \"target\": \"zsa/planck_ez/base\" },\n\n    /* The main list of aliases for moved keyboards within QMK. */\n    \"ergodox_ez\": {\n        \"target\": \"zsa/ergodox_ez/m32u4/base\"\n    },\n    \"ergodox_ez/base\": {\n        \"target\": \"zsa/ergodox_ez/m32u4/base\"\n    },\n    \"ergodox_ez/glow\": {\n        \"target\": \"zsa/ergodox_ez/m32u4/glow\"\n    },\n    \"ergodox_ez/shine\": {\n        \"target\": \"zsa/ergodox_ez/m32u4/shine\"\n    },\n    \"moonlander\": {\n        \"target\": \"zsa/moonlander\"\n    },\n    \"planck/ez\": {\n        \"target\": \"zsa/planck_ez/base\"\n    },\n    \"planck/ez/base\": {\n        \"target\": \"zsa/planck_ez/base\"\n    },\n    \"planck/ez/glow\": {\n        \"target\": \"zsa/planck_ez/glow\"\n    },\n    \"voyager\": {\n        \"target\": \"zsa/voyager\"\n    },\n    \"zsa/ergodox_ez\": {\n        \"target\": \"zsa/ergodox_ez/m32u4/base\"\n    },\n    \"zsa/ergodox_ez/m32u4\": {\n        \"target\": \"zsa/ergodox_ez/m32u4/base\"\n    },\n    \"zsa/ergodox_ez/stm32\": {\n        \"target\": \"zsa/ergodox_ez/stm32/base\"\n    }\n}\n"
  },
  {
    "path": "data/schemas/api_keyboard.jsonschema",
    "content": "{\n    \"$id\": \"qmk.api.keyboard.v1\",\n    \"allOf\": [\n        {\"$ref\": \"./keyboard.jsonschema#\"},\n        {\n            \"properties\": {\n                \"keymaps\": {\n                    \"type\": \"object\",\n                    \"properties\": {\n                        \"url\": {\"type\": \"string\"}\n                    }\n                },\n                \"parse_errors\": {\"$ref\": \"./definitions.jsonschema#/string_array\"},\n                \"parse_warnings\": {\"$ref\": \"./definitions.jsonschema#/string_array\"},\n                \"processor_type\": {\"type\": \"string\"},\n                \"protocol\": {\"type\": \"string\"},\n                \"keyboard_folder\": {\"type\": \"string\"},\n                \"platform\": {\"type\": \"string\"}\n            }\n        }\n    ]\n}\n"
  },
  {
    "path": "data/schemas/community_module.jsonschema",
    "content": "{\n    \"$schema\": \"https://json-schema.org/draft/2020-12/schema#\",\n    \"$id\": \"qmk.community_module.v1\",\n    \"title\": \"Community Module Information\",\n    \"type\": \"object\",\n    \"required\": [\"module_name\", \"maintainer\"],\n    \"properties\": {\n        \"module_name\": {\"$ref\": \"./definitions.jsonschema#/text_identifier\"},\n        \"maintainer\": {\"$ref\": \"./definitions.jsonschema#/text_identifier\"},\n        \"license\": {\"type\": \"string\"},\n        \"url\": {\n            \"type\": \"string\",\n            \"format\": \"uri\"\n        },\n        \"keycodes\": {\"$ref\": \"./definitions.jsonschema#/keycode_decl_array\"},\n        \"features\": {\"$ref\": \"./keyboard.jsonschema#/definitions/features_config\"}\n    }\n}\n"
  },
  {
    "path": "data/schemas/definitions.jsonschema",
    "content": "{\n    \"$schema\": \"https://json-schema.org/draft/2020-12/schema#\",\n    \"$id\": \"qmk.definitions.v1\",\n    \"title\": \"Common definitions used across QMK's jsonschemas.\",\n    \"type\": \"object\",\n    \"bcd_version\": {\n        \"type\": \"string\",\n        \"pattern\": \"^[0-9]{1,2}\\\\.[0-9]\\\\.[0-9]$\"\n    },\n    \"bit\": {\n        \"type\": \"integer\",\n        \"minimum\": 0,\n        \"maximum\": 1\n    },\n    \"boolean_array\": {\n        \"type\": \"object\",\n        \"additionalProperties\": {\"type\": \"boolean\"}\n    },\n    \"filename\": {\n        \"type\": \"string\",\n        \"minLength\": 1,\n        \"pattern\": \"^[0-9a-z_]*$\"\n    },\n    \"hex_number_2d\": {\n        \"type\": \"string\",\n        \"pattern\": \"^0x[0-9A-F]{2}$\"\n    },\n    \"hex_number_4d\": {\n        \"type\": \"string\",\n        \"pattern\": \"^0x[0-9A-F]{4}$\"\n    },\n    \"json_file_path\": {\n        \"type\": \"string\",\n        \"pattern\": \"^[0-9a-z_/\\\\-]+\\\\.json$\"\n    },\n    \"key_unit\": {\n        \"type\": \"number\",\n        \"minimum\": 0\n    },\n    \"keyboard\": {\n        \"type\": \"string\",\n        \"pattern\": \"^[0-9a-z][0-9a-z_/]*$\"\n    },\n    \"keyboard_keymap_tuple\": {\n        \"type\": \"array\",\n        \"prefixItems\": [\n            {\"$ref\": \"#/keyboard\"},\n            {\"$ref\": \"#/filename\"}\n        ],\n        \"minItems\": 2,\n        \"maxItems\": 2,\n        \"unevaluatedItems\": false\n    },\n    \"keyboard_keymap_env\": {\n        \"type\": \"array\",\n        \"prefixItems\": [\n            {\"$ref\": \"#/keyboard\"},\n            {\"$ref\": \"#/filename\"},\n            {\"$ref\": \"#/kvp_object\"}\n        ],\n        \"minItems\": 3,\n        \"maxItems\": 3,\n        \"unevaluatedItems\": false\n    },\n    \"keycode\": {\n        \"type\": \"string\",\n        \"minLength\": 2,\n        \"maxLength\": 50,\n        \"pattern\": \"^[A-Z][A-Zs_0-9]*$\"\n    },\n    \"keycode_decl\": {\n        \"type\": \"object\",\n        \"required\": [\n            \"key\"\n        ],\n        \"properties\": {\n            \"key\": {\"$ref\": \"#/keycode\"},\n            \"label\": {\"$ref\": \"#/text_identifier\"},\n            \"aliases\": {\n                \"type\": \"array\",\n                \"minItems\": 1,\n                \"items\": {\"$ref\": \"#/keycode_short\"}\n            }\n        }\n    },\n    \"keycode_decl_array\": {\n        \"type\": \"array\",\n        \"minItems\": 1,\n        \"items\": {\"$ref\": \"#/keycode_decl\"}\n    },\n    \"keycode_short\": {\n        \"type\": \"string\",\n        \"minLength\": 2,\n        \"maxLength\": 7,\n        \"pattern\": \"^[A-Z][A-Zs_0-9]*$\"\n    },\n    \"kvp_object\": {\n        \"type\": \"object\",\n        \"additionalProperties\": {\"type\": \"string\"}\n    },\n    \"layout_macro\": {\n        \"oneOf\": [\n            {\n                \"type\": \"string\",\n                \"enum\": [\n                    \"LAYOUT\",\n                    \"LAYOUT_1x2uC\",\n                    \"LAYOUT_1x2uL\",\n                    \"LAYOUT_1x2uR\",\n                    \"LAYOUT_2x2uC\",\n                    \"LAYOUT_2x3uC\",\n                    \"LAYOUT_625uC\",\n                    \"LAYOUT_ortho_3x12_1x2uC\",\n                    \"LAYOUT_ortho_4x12_1x2uC\",\n                    \"LAYOUT_ortho_4x12_1x2uL\",\n                    \"LAYOUT_ortho_4x12_1x2uR\",\n                    \"LAYOUT_ortho_5x12_1x2uC\",\n                    \"LAYOUT_ortho_5x12_2x2uC\",\n                    \"LAYOUT_ortho_5x14_1x2uC\",\n                    \"LAYOUT_ortho_5x14_1x2uL\",\n                    \"LAYOUT_ortho_5x14_1x2uR\",\n                    \"LAYOUT_planck_1x2uC\",\n                    \"LAYOUT_planck_1x2uL\",\n                    \"LAYOUT_planck_1x2uR\",\n                    \"LAYOUT_preonic_1x2uC\",\n                    \"LAYOUT_preonic_1x2uL\",\n                    \"LAYOUT_preonic_1x2uR\"\n                ]\n            },\n            {\n                \"type\": \"string\",\n                \"pattern\": \"^LAYOUT_[0-9a-z_]*$\"\n            }\n        ]\n    },\n    \"mcu_pin\": {\n        \"oneOf\": [\n            {\n                \"type\": \"string\",\n                \"enum\": [\"NO_PIN\"]\n            },\n            {\n                \"type\": \"string\",\n                \"pattern\": \"^[A-K]\\\\d{1,2}$\"\n            },\n            {\n                \"type\": \"string\",\n                \"pattern\": \"^LINE_PIN\\\\d{1,2}$\"\n            },\n            {\n                \"type\": \"string\",\n                \"pattern\": \"^GP\\\\d{1,2}$\"\n            },\n            {\"type\": \"integer\"},\n            {\"type\": \"null\"}\n        ]\n    },\n    \"mcu_pin_array\": {\n        \"type\": \"array\",\n        \"items\": {\"$ref\": \"#/mcu_pin\"}\n    },\n    \"signed_decimal\": {\n        \"type\": \"number\"\n    },\n    \"signed_int\": {\n        \"type\": \"integer\"\n    },\n    \"signed_int_8\": {\n        \"type\": \"integer\",\n        \"minimum\": -127,\n        \"maximum\": 127\n    },\n    \"snake_case\": {\n        \"type\": \"string\",\n        \"pattern\": \"^[a-z][a-z0-9_]*$\"\n    },\n    \"string_array\": {\n        \"type\": \"array\",\n        \"items\": {\"type\": \"string\"}\n    },\n    \"string_object\": {\n        \"type\": \"object\",\n        \"additionalProperties\": {\"type\": \"string\"}\n    },\n    \"text_identifier\": {\n        \"type\": \"string\",\n        \"minLength\": 1,\n        \"maxLength\": 250\n    },\n    \"unsigned_decimal\": {\n        \"type\": \"number\",\n        \"minimum\": 0\n    },\n    \"unsigned_int\": {\n        \"type\": \"integer\",\n        \"minimum\": 0\n    },\n    \"unsigned_int_8\": {\n        \"type\": \"integer\",\n        \"minimum\": 0,\n        \"maximum\": 255\n    }\n}\n"
  },
  {
    "path": "data/schemas/keyboard.jsonschema",
    "content": "{\n    \"$schema\": \"https://json-schema.org/draft/2020-12/schema#\",\n    \"$id\": \"qmk.keyboard.v1\",\n    \"title\": \"Keyboard Information\",\n    \"definitions\": {\n        \"encoder_config\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"driver\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"custom\", \"quadrature\"]\n                },\n                \"rotary\": {\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"object\",\n                        \"additionalProperties\": false,\n                        \"required\": [\"pin_a\", \"pin_b\"],\n                        \"properties\": {\n                            \"pin_a\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                            \"pin_b\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                            \"resolution\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n                        }\n                    }\n                }\n            }\n        },\n        \"dip_switch_config\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"pins\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin_array\"}\n            }\n        },\n        \"features_config\": {\n            \"$ref\": \"./definitions.jsonschema#/boolean_array\",\n            \"propertyNames\": {\"$ref\": \"./definitions.jsonschema#/snake_case\"},\n            \"not\": {\"required\": [\"lto\"]}\n        }\n    },\n    \"type\": \"object\",\n    \"not\": {\"required\": [\"vendorId\", \"productId\"]}, // reject via keys...\n    \"properties\": {\n        \"keyboard_name\": {\"$ref\": \"./definitions.jsonschema#/text_identifier\"},\n        \"keyboard_folder\": {\"$ref\": \"./definitions.jsonschema#/keyboard\"},\n        \"maintainer\": {\"$ref\": \"./definitions.jsonschema#/text_identifier\"},\n        \"manufacturer\": {\"$ref\": \"./definitions.jsonschema#/text_identifier\"},\n        \"url\": {\n            \"type\": \"string\",\n            \"format\": \"uri\"\n        },\n        \"development_board\": {\n            \"type\": \"string\",\n            \"enum\": [\"promicro\", \"elite_c\", \"elite_pi\", \"proton_c\", \"kb2040\", \"promicro_rp2040\", \"blok\", \"michi\", \"bit_c_pro\", \"stemcell\", \"bluepill\", \"blackpill_f401\", \"blackpill_f411\", \"bonsai_c4\", \"helios\", \"liatris\", \"imera\", \"svlinky\"]\n        },\n        \"pin_compatible\": {\n            \"type\": \"string\",\n            \"enum\": [\"promicro\", \"elite_c\"]\n        },\n        \"processor\": {\n            \"type\": \"string\",\n            \"enum\": [\n                \"cortex-m0\",\n                \"cortex-m0plus\",\n                \"cortex-m3\",\n                \"cortex-m4\",\n                \"cortex-m7\",\n                \"cortex-m23\",\n                \"cortex-m33\",\n                \"cortex-m35p\",\n                \"cortex-m55\",\n                \"cortex-m85\",\n                \"MKL26Z64\",\n                \"MK20DX128\",\n                \"MK20DX256\",\n                \"MK64FX512\",\n                \"MK66FX1M0\",\n                \"RP2040\",\n                \"STM32F042\",\n                \"STM32F072\",\n                \"STM32F103\",\n                \"STM32F303\",\n                \"STM32F401\",\n                \"STM32F405\",\n                \"STM32F407\",\n                \"STM32F411\",\n                \"STM32F446\",\n                \"STM32G0B1\",\n                \"STM32G431\",\n                \"STM32G474\",\n                \"STM32H723\",\n                \"STM32H733\",\n                \"STM32L412\",\n                \"STM32L422\",\n                \"STM32L432\",\n                \"STM32L433\",\n                \"STM32L442\",\n                \"STM32L443\",\n                \"GD32VF103\",\n                \"WB32F3G71\",\n                \"WB32FQ95\",\n                \"AT32F415\",\n                \"atmega16u2\",\n                \"atmega32u2\",\n                \"atmega16u4\",\n                \"atmega32u4\",\n                \"at90usb162\",\n                \"at90usb646\",\n                \"at90usb647\",\n                \"at90usb1286\",\n                \"at90usb1287\",\n                \"atmega32a\",\n                \"atmega328p\",\n                \"atmega328\",\n                \"attiny85\",\n                \"unknown\"\n            ]\n        },\n        \"apa102\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"data_pin\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"clock_pin\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"default_brightness\": {\n                    \"type\": \"integer\",\n                    \"minimum\": 0,\n                    \"maximum\": 31\n                }\n            }\n        },\n        \"audio\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"default\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"on\": {\"type\": \"boolean\"},\n                        \"clicky\": {\"type\": \"boolean\"}\n                    }\n                },\n                \"driver\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"dac_additive\", \"dac_basic\", \"pwm_software\", \"pwm_hardware\"]\n                },\n                \"macro_beep\": {\"type\": \"boolean\"},\n                \"pins\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin_array\"},\n                \"power_control\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"on_state\": {\"$ref\": \"./definitions.jsonschema#/bit\"},\n                        \"pin\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"}\n                    }\n                },\n                \"voices\": {\"type\": \"boolean\"}\n            }\n        },\n        \"backlight\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"driver\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"custom\", \"pwm\", \"software\", \"timer\"]\n                },\n                \"default\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"on\": {\"type\": \"boolean\"},\n                        \"breathing\": {\"type\": \"boolean\"},\n                        \"brightness\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"}\n                    }\n                },\n                \"breathing\": {\"type\": \"boolean\"},\n                \"breathing_period\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                \"levels\": {\n                    \"type\": \"integer\",\n                    \"minimum\": 1,\n                    \"maximum\": 31\n                },\n                \"max_brightness\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                \"pin\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"pins\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin_array\"},\n                \"on_state\": {\"$ref\": \"./definitions.jsonschema#/bit\"},\n                \"as_caps_lock\": {\"type\": \"boolean\"}\n            }\n        },\n        \"bluetooth\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"driver\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"bluefruit_le\", \"custom\", \"rn42\"]\n                }\n            }\n        },\n        \"bootmagic\":{\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"enabled\": {\"type\": \"boolean\"},\n                \"matrix\": {\n                    \"type\": \"array\",\n                    \"minItems\": 2,\n                    \"maxItems\": 2,\n                    \"items\": {\n                        \"type\": \"integer\",\n                        \"minimum\": 0\n                    }\n                }\n            }\n        },\n        \"board\": {\n            \"type\": \"string\",\n            \"minLength\": 2,\n            \"pattern\": \"^[a-zA-Z_][0-9a-zA-Z_]*$\"\n        },\n        \"bootloader\": {\n            \"type\": \"string\",\n            \"enum\": [\n                \"apm32-dfu\",\n                \"at32-dfu\",\n                \"atmel-dfu\",\n                \"bootloadhid\",\n                \"caterina\",\n                \"custom\",\n                \"gd32v-dfu\",\n                \"halfkay\",\n                \"kiibohd\",\n                \"lufa-dfu\",\n                \"lufa-ms\",\n                \"md-boot\",\n                \"qmk-dfu\",\n                \"qmk-hid\",\n                \"rp2040\",\n                \"stm32-dfu\",\n                \"stm32duino\",\n                \"tinyuf2\",\n                \"uf2boot\",\n                \"unknown\",\n                \"usbasploader\",\n                \"wb32-dfu\"\n            ]\n        },\n        \"bootloader_instructions\": {\n            \"type\": \"string\",\n            \"description\": \"Instructions for putting the keyboard into a mode that allows for firmware flashing.\"\n        },\n        \"build\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"debounce_type\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"asym_eager_defer_pk\", \"custom\", \"sym_defer_g\", \"sym_defer_pk\", \"sym_defer_pr\", \"sym_eager_pk\", \"sym_eager_pr\"]\n                },\n                \"firmware_format\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"bin\", \"hex\", \"uf2\"]\n                },\n                \"lto\": {\"type\": \"boolean\"}\n            }\n        },\n        \"diode_direction\": {\n            \"type\": \"string\",\n            \"enum\": [\"COL2ROW\", \"ROW2COL\"]\n        },\n        \"debounce\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n        \"caps_word\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"enabled\": {\"type\": \"boolean\"},\n                \"both_shifts_turns_on\": {\"type\": \"boolean\"},\n                \"double_tap_shift_turns_on\": {\"type\": \"boolean\"},\n                \"idle_timeout\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"invert_on_shift\": {\"type\": \"boolean\"}\n            }\n        },\n        \"combo\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"count\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"term\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n            }\n        },\n        \"community_layouts\": {\n            \"type\": \"array\",\n            \"items\": {\"$ref\": \"./definitions.jsonschema#/filename\"}\n        },\n        \"dip_switch\": {\n            \"$ref\": \"#/definitions/dip_switch_config\",\n            \"properties\": {\n                \"enabled\": {\"type\": \"boolean\"},\n                \"matrix_grid\": {\n                    \"type\": \"array\",\n                    \"minItems\": 1,\n                    \"items\": {\n                        \"type\": \"array\",\n                        \"minItems\": 2,\n                        \"maxItems\": 2,\n                        \"items\": {\n                            \"type\": \"integer\",\n                            \"minimum\": 0\n                        }\n                    }\n                }\n            }\n        },\n        \"eeprom\": {\n            \"properties\": {\n                \"driver\": {\"type\": \"string\"},\n                \"wear_leveling\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"driver\": {\n                            \"type\": \"string\",\n                            \"enum\": [\"none\", \"custom\", \"embedded_flash\", \"legacy\", \"rp2040_flash\", \"spi_flash\"]\n                        },\n                        \"backing_size\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                        \"logical_size\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n                    }\n                }\n            }\n        },\n        \"encoder\": {\n            \"$ref\": \"#/definitions/encoder_config\",\n            \"properties\": {\n                \"enabled\": {\"type\": \"boolean\"}\n            }\n        },\n        \"features\": { \"$ref\": \"#/definitions/features_config\" },\n        \"indicators\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"caps_lock\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"num_lock\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"scroll_lock\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"compose\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"kana\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"on_state\": {\"$ref\": \"./definitions.jsonschema#/bit\"}\n            }\n        },\n        \"joystick\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"enabled\": {\"type\": \"boolean\"},\n                \"driver\": {\"type\": \"string\"},\n                \"button_count\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"axis_resolution\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"axes\": {\n                    \"type\": \"object\",\n                    \"propertyNames\": {\"enum\": [\"x\", \"y\", \"z\", \"rx\", \"ry\", \"rz\"]},\n                    \"additionalProperties\": {\n                        \"oneOf\": [\n                            {\n                                \"type\": \"object\",\n                                \"properties\": {\n                                    \"input_pin\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                                    \"low\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                                    \"rest\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                                    \"high\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n                                }\n                            },\n                            {\n                                \"type\": \"string\",\n                                \"enum\": [\"virtual\"]\n                            }\n                        ]\n                    }\n                }\n            }\n        },\n        \"keycodes\": {\"$ref\": \"./definitions.jsonschema#/keycode_decl_array\"},\n        \"layer_lock\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"timeout\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n            }\n        },\n        \"layout_aliases\": {\n            \"type\": \"object\",\n            \"additionalProperties\": {\"$ref\": \"./definitions.jsonschema#/layout_macro\"}\n        },\n        \"layouts\": {\n            \"type\": \"object\",\n            \"propertyNames\": {\"$ref\": \"./definitions.jsonschema#/layout_macro\"},\n            \"additionalProperties\": {\n                \"type\": \"object\",\n                \"additionalProperties\": false,\n                \"properties\": {\n                    \"filename\": {\"type\": \"string\"},\n                    \"c_macro\": {\"type\": \"boolean\"},\n                    \"json_layout\": {\"type\": \"boolean\"},\n                    \"layout\": {\n                        \"type\": \"array\",\n                        \"items\": {\n                            \"type\": \"object\",\n                            \"additionalProperties\": false,\n                            \"required\": [\"x\", \"y\"],\n                            \"properties\": {\n                                \"encoder\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                                \"label\": {\n                                    \"type\": \"string\",\n                                    \"pattern\": \"^[^\\\\n]*$\"\n                                },\n                                \"matrix\": {\n                                    \"type\": \"array\",\n                                    \"minItems\": 2,\n                                    \"maxItems\": 2,\n                                    \"items\": {\n                                        \"type\": \"integer\",\n                                        \"minimum\": 0\n                                    }\n                                },\n                                \"r\": {\"$ref\": \"./definitions.jsonschema#/signed_decimal\"},\n                                \"rx\": {\"$ref\": \"./definitions.jsonschema#/unsigned_decimal\"},\n                                \"ry\": {\"$ref\": \"./definitions.jsonschema#/unsigned_decimal\"},\n                                \"h\": {\"$ref\": \"./definitions.jsonschema#/key_unit\"},\n                                \"w\": {\"$ref\": \"./definitions.jsonschema#/key_unit\"},\n                                \"x\": {\"$ref\": \"./definitions.jsonschema#/key_unit\"},\n                                \"y\": {\"$ref\": \"./definitions.jsonschema#/key_unit\"},\n                                \"hand\": {\n                                    \"type\": \"string\",\n                                    \"enum\": [\"L\", \"R\", \"*\"]\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        },\n        \"haptic\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"driver\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"drv2605l\", \"solenoid\"]\n                }\n            }\n        },\n        \"host\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"default\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"nkro\": {\"type\": \"boolean\"}\n                    }\n                }\n            }\n        },\n        \"leader_key\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"timing\": {\"type\": \"boolean\"},\n                \"strict_processing\": {\"type\": \"boolean\"},\n                \"timeout\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n            }\n        },\n        \"matrix_pins\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"custom\": {\"type\": \"boolean\"},\n                \"custom_lite\": {\"type\": \"boolean\"},\n                \"ghost\": {\"type\": \"boolean\"},\n                \"input_pressed_state\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"io_delay\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"direct\": {\n                    \"type\": \"array\",\n                    \"items\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin_array\"}\n                },\n                \"cols\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin_array\"},\n                \"rows\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin_array\"}\n            }\n        },\n        \"modules\": {\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": \"string\"\n            }\n        },\n        \"mouse_key\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"enabled\": {\"type\": \"boolean\"},\n                \"delay\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                \"interval\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                \"max_speed\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                \"time_to_max\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                \"wheel_delay\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"}\n            }\n        },\n        \"oneshot\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"tap_toggle\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"timeout\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n            }\n        },\n        \"led_matrix\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"animations\": {\n                    \"type\": \"object\",\n                    \"propertyNames\": {\"$ref\": \"./definitions.jsonschema#/snake_case\"},\n                    \"additionalProperties\": {\"type\": \"boolean\"}\n                },\n                \"default\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"on\": {\"type\": \"boolean\"},\n                        \"animation\": {\"type\": \"string\"},\n                        \"val\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                        \"speed\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"}\n                    }\n                },\n                \"driver\": {\n                    \"type\": \"string\",\n                    \"enum\": [\n                        \"custom\",\n                        \"is31fl3218\",\n                        \"is31fl3236\",\n                        \"is31fl3729\",\n                        \"is31fl3731\",\n                        \"is31fl3733\",\n                        \"is31fl3736\",\n                        \"is31fl3737\",\n                        \"is31fl3741\",\n                        \"is31fl3742a\",\n                        \"is31fl3743a\",\n                        \"is31fl3745\",\n                        \"is31fl3746a\",\n                        \"snled27351\"\n                    ]\n                },\n                \"center_point\": {\n                    \"type\": \"array\",\n                    \"minItems\": 2,\n                    \"maxItems\": 2,\n                    \"items\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"}\n                },\n                \"max_brightness\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                \"timeout\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"val_steps\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"speed_steps\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"led_flush_limit\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"led_process_limit\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"react_on_keyup\": {\"type\": \"boolean\"},\n                \"sleep\": {\"type\": \"boolean\"},\n                \"split_count\": {\n                    \"type\": \"array\",\n                    \"minItems\": 2,\n                    \"maxItems\": 2,\n                    \"items\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n                },\n                \"layout\": {\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"object\",\n                        \"additionalProperties\": false,\n                        \"required\": [\"x\", \"y\"],\n                        \"properties\": {\n                            \"matrix\": {\n                                \"type\": \"array\",\n                                \"minItems\": 2,\n                                \"maxItems\": 2,\n                                \"items\": {\n                                    \"type\": \"integer\",\n                                    \"minimum\": 0\n                                }\n                            },\n                            \"x\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                            \"y\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                            \"flags\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"}\n                        }\n                    }\n                }\n            }\n        },\n        \"rgb_matrix\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"animations\": {\n                    \"type\": \"object\",\n                    \"propertyNames\": {\"$ref\": \"./definitions.jsonschema#/snake_case\"},\n                    \"additionalProperties\": {\"type\": \"boolean\"}\n                },\n                \"default\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"on\": {\"type\": \"boolean\"},\n                        \"animation\": {\"type\": \"string\"},\n                        \"hue\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                        \"sat\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                        \"val\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                        \"speed\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"}\n                    }\n                },\n                \"driver\": {\n                    \"type\": \"string\",\n                    \"enum\": [\n                        \"aw20216s\",\n                        \"custom\",\n                        \"is31fl3218\",\n                        \"is31fl3236\",\n                        \"is31fl3729\",\n                        \"is31fl3731\",\n                        \"is31fl3733\",\n                        \"is31fl3736\",\n                        \"is31fl3737\",\n                        \"is31fl3741\",\n                        \"is31fl3742a\",\n                        \"is31fl3743a\",\n                        \"is31fl3745\",\n                        \"is31fl3746a\",\n                        \"snled27351\",\n                        \"ws2812\"\n                    ]\n                },\n                \"center_point\": {\n                    \"type\": \"array\",\n                    \"minItems\": 2,\n                    \"maxItems\": 2,\n                    \"items\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"}\n                },\n                \"max_brightness\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                \"timeout\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"hue_steps\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"sat_steps\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"val_steps\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"speed_steps\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"led_flush_limit\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"led_process_limit\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"react_on_keyup\": {\"type\": \"boolean\"},\n                \"sleep\": {\"type\": \"boolean\"},\n                \"split_count\": {\n                    \"type\": \"array\",\n                    \"minItems\": 2,\n                    \"maxItems\": 2,\n                    \"items\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n                },\n                \"layout\": {\n                    \"type\": \"array\",\n                    \"items\": {\n                        \"type\": \"object\",\n                        \"additionalProperties\": false,\n                        \"required\": [\"x\", \"y\"],\n                        \"properties\": {\n                            \"matrix\": {\n                                \"type\": \"array\",\n                                \"minItems\": 2,\n                                \"maxItems\": 2,\n                                \"items\": {\n                                    \"type\": \"integer\",\n                                    \"minimum\": 0\n                                }\n                            },\n                            \"x\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                            \"y\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                            \"flags\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"}\n                        }\n                    }\n                }\n            }\n        },\n        \"rgblight\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"animations\": {\n                    \"type\": \"object\",\n                    \"propertyNames\": {\"$ref\": \"./definitions.jsonschema#/snake_case\"},\n                    \"additionalProperties\": {\"type\": \"boolean\"}\n                },\n                \"brightness_steps\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"default\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"on\": {\"type\": \"boolean\"},\n                        \"animation\": {\"type\": \"string\"},\n                        \"hue\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                        \"sat\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                        \"val\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                        \"speed\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"}\n                    }\n                },\n                \"driver\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"apa102\", \"custom\", \"ws2812\"]\n                },\n                \"hue_steps\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"layers\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"blink\": {\"type\": \"boolean\"},\n                        \"enabled\": {\"type\": \"boolean\"},\n                        \"max\": {\n                            \"type\": \"integer\",\n                            \"minimum\": 1,\n                            \"maximum\": 32\n                        },\n                        \"override_rgb\": {\"type\": \"boolean\"}\n                    }\n                },\n                \"led_count\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"led_map\": {\n                    \"type\": \"array\",\n                    \"minItems\": 2,\n                    \"items\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n                },\n                \"max_brightness\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                \"pin\": {\n                    \"$ref\": \"./definitions.jsonschema#/mcu_pin\",\n                    \"$comment\": \"Deprecated: use ws2812.pin instead\"\n                },\n                \"rgbw\": {\n                    \"type\": \"boolean\",\n                    \"$comment\": \"Deprecated: use ws2812.rgbw instead\"\n                },\n                \"saturation_steps\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"sleep\": {\"type\": \"boolean\"},\n                \"split\": {\"type\": \"boolean\"},\n                \"split_count\": {\n                    \"type\": \"array\",\n                    \"minItems\": 2,\n                    \"maxItems\": 2,\n                    \"items\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n                }\n            }\n        },\n        \"secure\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"enabled\": {\"type\": \"boolean\"},\n                \"unlock_timeout\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"idle_timeout\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"unlock_sequence\": {\n                    \"type\": \"array\",\n                    \"minItems\": 1,\n                    \"maxItems\": 5,\n                    \"items\": {\n                        \"type\": \"array\",\n                        \"minItems\": 2,\n                        \"maxItems\": 2,\n                        \"items\": {\n                            \"type\": \"integer\",\n                            \"minimum\": 0\n                        }\n                    }\n                }\n            }\n        },\n        \"stenography\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"enabled\": {\"type\": \"boolean\"},\n                \"protocol\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"all\", \"geminipr\", \"txbolt\"]\n                }\n            }\n        },\n        \"ps2\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"enabled\": {\"type\": \"boolean\"},\n                \"mouse_enabled\": {\"type\": \"boolean\"},\n                \"clock_pin\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"data_pin\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"driver\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"busywait\", \"interrupt\", \"usart\", \"vendor\"]\n                }\n            }\n        },\n        \"split\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"enabled\": {\"type\": \"boolean\"},\n                \"bootmagic\":{\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"matrix\": {\n                            \"type\": \"array\",\n                            \"minItems\": 2,\n                            \"maxItems\": 2,\n                            \"items\": {\n                                \"type\": \"integer\",\n                                \"minimum\": 0\n                            }\n                        }\n                    }\n                },\n                \"matrix_pins\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"right\": {\n                            \"type\": \"object\",\n                            \"additionalProperties\": false,\n                            \"properties\": {\n                                \"direct\": {\n                                    \"type\": \"array\",\n                                    \"items\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin_array\"}\n                                },\n                                \"cols\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin_array\"},\n                                \"rows\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin_array\"},\n                                \"unused\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin_array\"}\n                            }\n                        }\n                    }\n                },\n                \"dip_switch\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"right\": {\n                            \"$ref\": \"#/definitions/dip_switch_config\"\n                        }\n                    }\n                },\n                \"encoder\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"right\": {\n                            \"$ref\": \"#/definitions/encoder_config\"\n                        }\n                    }\n                },\n                \"handedness\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"pin\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                        \"matrix_grid\": {\n                            \"$ref\": \"./definitions.jsonschema#/mcu_pin_array\",\n                            \"minItems\": 2,\n                            \"maxItems\": 2\n                        }\n                    }\n                },\n                \"soft_serial_pin\": {\n                    \"$ref\": \"./definitions.jsonschema#/mcu_pin\",\n                    \"$comment\": \"Deprecated: use split.serial.pin instead\"\n                },\n                \"soft_serial_speed\": {\n                    \"type\": \"integer\",\n                    \"minimum\": 0,\n                    \"maximum\": 5\n                },\n                \"serial\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"driver\": {\n                            \"type\": \"string\",\n                            \"enum\": [\"bitbang\", \"usart\", \"vendor\"]\n                        },\n                        \"pin\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"}\n                    }\n                },\n                \"transport\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"protocol\": {\n                            \"type\": \"string\",\n                            \"enum\": [\"custom\", \"i2c\", \"serial\"]\n                        },\n                        \"sync\": {\n                            \"type\": \"object\",\n                            \"additionalProperties\": false,\n                            \"properties\": {\n                                \"activity\": {\"type\": \"boolean\"},\n                                \"detected_os\": {\"type\": \"boolean\"},\n                                \"haptic\": {\"type\": \"boolean\"},\n                                \"layer_state\": {\"type\": \"boolean\"},\n                                \"indicators\": {\"type\": \"boolean\"},\n                                \"matrix_state\": {\"type\": \"boolean\"},\n                                \"modifiers\": {\"type\": \"boolean\"},\n                                \"oled\": {\"type\": \"boolean\"},\n                                \"st7565\": {\"type\": \"boolean\"},\n                                \"wpm\": {\"type\": \"boolean\"}\n                            }\n                        },\n                        \"watchdog\": {\"type\": \"boolean\"},\n                        \"watchdog_timeout\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                        \"sync_matrix_state\": {\n                            \"type\": \"boolean\",\n                            \"$comment\": \"Deprecated: use sync.matrix_state instead\"\n                        },\n                        \"sync_modifiers\": {\n                            \"type\": \"boolean\",\n                            \"$comment\": \"Deprecated: use sync.modifiers instead\"\n                        }\n                    }\n                },\n                \"usb_detect\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"enabled\": {\"type\": \"boolean\"},\n                        \"polling_interval\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                        \"timeout\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n                    }\n                },\n                \"main\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"eeprom\", \"left\", \"matrix_grid\", \"pin\", \"right\"],\n                    \"$comment\": \"Deprecated: use config.h options for now\"\n                },\n                \"matrix_grid\": {\n                    \"type\": \"array\",\n                    \"items\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                    \"$comment\": \"Deprecated: use split.handedness.matrix_grid instead\"\n                }\n            }\n        },\n        \"tags\": {\n            \"type\": \"array\",\n            \"items\": {\"type\": \"string\"}\n        },\n        \"tapping\": {\n            \"type\": \"object\",\n            \"properties\": {\n                \"chordal_hold\": {\"type\": \"boolean\"},\n                \"force_hold\": {\"type\": \"boolean\"},\n                \"force_hold_per_key\": {\"type\": \"boolean\"},\n                \"ignore_mod_tap_interrupt\": {\"type\": \"boolean\"},\n                \"hold_on_other_key_press\": {\"type\": \"boolean\"},\n                \"hold_on_other_key_press_per_key\": {\"type\": \"boolean\"},\n                \"permissive_hold\": {\"type\": \"boolean\"},\n                \"permissive_hold_per_key\": {\"type\": \"boolean\"},\n                \"retro\": {\"type\": \"boolean\"},\n                \"retro_per_key\": {\"type\": \"boolean\"},\n                \"term\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"term_per_key\": {\"type\": \"boolean\"},\n                \"toggle\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n            }\n        },\n        \"usb\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"device_ver\": {\n                    \"$ref\": \"./definitions.jsonschema#/hex_number_4d\",\n                    \"$comment\": \"Deprecated: use device_version instead\"\n                },\n                \"device_version\": {\"$ref\": \"./definitions.jsonschema#/bcd_version\"},\n                \"force_nkro\": {\n                    \"type\": \"boolean\",\n                    \"$comment\": \"Deprecated: use host.default.nkro instead\"\n\n                },\n                \"pid\": {\"$ref\": \"./definitions.jsonschema#/hex_number_4d\"},\n                \"vid\": {\"$ref\": \"./definitions.jsonschema#/hex_number_4d\"},\n                \"max_power\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"no_startup_check\": {\"type\": \"boolean\"},\n                \"polling_interval\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                \"shared_endpoint\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"keyboard\": {\"type\": \"boolean\"},\n                        \"mouse\": {\"type\": \"boolean\"}\n                    }\n                },\n                \"suspend_wakeup_delay\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"wait_for_enumeration\": {\"type\": \"boolean\"}\n            }\n        },\n        \"qmk\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"keys_per_scan\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int_8\"},\n                \"tap_keycode_delay\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"tap_capslock_delay\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"},\n                \"locking\": {\n                    \"type\": \"object\",\n                    \"additionalProperties\": false,\n                    \"properties\": {\n                        \"enabled\": {\"type\": \"boolean\"},\n                        \"resync\": {\"type\": \"boolean\"}\n                    }\n                }\n            }\n        },\n        \"qmk_lufa_bootloader\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"esc_output\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"esc_input\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"led\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"speaker\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"}\n            }\n        },\n        \"ws2812\": {\n            \"type\": \"object\",\n            \"additionalProperties\": false,\n            \"properties\": {\n                \"driver\": {\n                    \"type\": \"string\",\n                    \"enum\": [\"bitbang\", \"custom\", \"i2c\", \"pwm\", \"spi\", \"vendor\"]\n                },\n                \"pin\": {\"$ref\": \"./definitions.jsonschema#/mcu_pin\"},\n                \"rgbw\": {\"type\": \"boolean\"},\n                \"i2c_address\": {\"$ref\": \"./definitions.jsonschema#/hex_number_2d\"},\n                \"i2c_timeout\": {\"$ref\": \"./definitions.jsonschema#/unsigned_int\"}\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "data/schemas/keycodes.jsonschema",
    "content": "{\n    \"$schema\": \"https://json-schema.org/draft/2020-12/schema#\",\n    \"$id\": \"qmk.keycodes.v1\",\n    \"title\": \"Keycode Information\",\n    \"type\": \"object\",\n    \"definitions\": {\n        \"define\": {\n            \"type\": \"string\",\n            \"minLength\": 2,\n            \"maxLength\": 50,\n            \"pattern\": \"^[A-Z][A-Zs_0-9]*$\"\n        }\n    },\n    \"properties\": {\n        \"ranges\": {\n            \"type\": \"object\",\n            \"propertyNames\": {\n                \"type\": \"string\"\n            },\n            \"additionalProperties\": {\n                \"type\": \"object\",\n                \"required\": [\n                    \"define\"\n                ],\n                \"properties\": {\n                    \"define\": {\"$ref\": \"#/definitions/define\"}\n                }\n            }\n        },\n        \"keycodes\": {\n            \"type\": \"object\",\n            \"propertyNames\": {\n                \"$ref\": \"./definitions.jsonschema#/hex_number_4d\"\n            },\n            \"additionalProperties\": {\n                \"type\": \"object\", // use './definitions.jsonschema#/keycode_decl' when problem keycodes are removed\n                \"required\": [\n                    \"key\"\n                ],\n                \"properties\": {\n                    \"key\": {\"$ref\": \"#/definitions/define\"},\n                    \"aliases\": {\n                        \"type\": \"array\",\n                        \"minItems\": 1,\n                        \"items\": {\n                            \"type\": \"string\"\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "data/schemas/keymap.jsonschema",
    "content": "{\n    \"$schema\": \"https://json-schema.org/draft/2020-12/schema#\",\n    \"$id\": \"qmk.keymap.v1\",\n    \"title\": \"Keymap Information\",\n    \"type\": \"object\",\n    \"properties\": {\n        \"author\": {\"type\": \"string\"},\n        \"converter\": {\n            \"type\": \"string\",\n            \"minLength\": 1,\n            \"pattern\": \"^[a-z][0-9a-z_]*$\"\n        },\n        \"host_language\": {\"$ref\": \"./definitions.jsonschema#/text_identifier\"},\n        \"keyboard\": {\"$ref\": \"./definitions.jsonschema#/text_identifier\"},\n        \"keymap\": {\"$ref\": \"./definitions.jsonschema#/text_identifier\"},\n        \"layout\": {\"$ref\": \"./definitions.jsonschema#/layout_macro\"},\n        \"layers\": {\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": \"array\",\n                \"items\": {\"type\": \"string\"}\n            }\n        },\n        \"encoders\": {\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": \"array\",\n                \"items\": {\n                    \"type\": \"object\",\n                    \"required\": [\"ccw\", \"cw\"],\n                    \"properties\": {\n                        \"ccw\": {\"type\": \"string\"},\n                        \"cw\": {\"type\": \"string\"}\n                    }\n                }\n            }\n        },\n        \"macros\": {\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": \"array\",\n                \"items\": {\n                    \"oneOf\": [\n                        {\n                            \"type\": \"string\"\n                        },\n                        {\n                            \"type\": \"object\",\n                            \"additionalProperties\": false,\n                            \"properties\": {\n                                \"action\": {\n                                    \"type\": \"string\",\n                                    \"enum\": [\"beep\", \"delay\", \"down\", \"tap\", \"up\"]\n                                },\n                                \"keycodes\": {\n                                    \"type\": \"array\",\n                                    \"items\": {\n                                        \"$ref\": \"./definitions.jsonschema#/text_identifier\"\n                                    }\n                                },\n                                \"duration\": {\n                                    \"$ref\": \"./definitions.jsonschema#/unsigned_int\"\n                                }\n                            }\n                        }\n                    ]\n                }\n            }\n        },\n        \"keycodes\": {\"$ref\": \"./definitions.jsonschema#/keycode_decl_array\"},\n        \"config\": {\"$ref\": \"./keyboard.jsonschema#\"},\n        \"notes\": {\n            \"type\": \"string\"\n        },\n        \"modules\": {\n            \"type\": \"array\",\n            \"items\": {\n                \"type\": \"string\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "data/schemas/user_repo_v0.jsonschema",
    "content": "{\n    \"$schema\": \"https://json-schema.org/draft/2020-12/schema#\",\n    \"$id\": \"qmk.user_repo.v0\",\n    \"title\": \"User Repository Information\",\n    \"type\": \"object\",\n    \"required\": [\n        \"userspace_version\"\n    ],\n    \"properties\": {\n        \"userspace_version\": {\n            \"type\": \"string\"\n        }\n    }\n}\n"
  },
  {
    "path": "data/schemas/user_repo_v1.jsonschema",
    "content": "{\n    \"$schema\": \"https://json-schema.org/draft/2020-12/schema#\",\n    \"$id\": \"qmk.user_repo.v1\",\n    \"title\": \"User Repository Information\",\n    \"type\": \"object\",\n    \"definitions\": {\n        \"build_target\": {\n            \"oneOf\": [\n                {\"$ref\": \"./definitions.jsonschema#/keyboard_keymap_tuple\"},\n                {\"$ref\": \"./definitions.jsonschema#/json_file_path\"}\n            ]\n        }\n    },\n    \"required\": [\n        \"userspace_version\",\n        \"build_targets\"\n    ],\n    \"properties\": {\n        \"userspace_version\": {\n            \"type\": \"string\",\n            \"enum\": [\"1.0\"]\n        },\n        \"build_targets\": {\n            \"type\": \"array\",\n            \"items\": {\n                \"$ref\": \"#/definitions/build_target\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "data/schemas/user_repo_v1_1.jsonschema",
    "content": "{\n    \"$schema\": \"https://json-schema.org/draft/2020-12/schema#\",\n    \"$id\": \"qmk.user_repo.v1_1\",\n    \"title\": \"User Repository Information\",\n    \"type\": \"object\",\n    \"definitions\": {\n        \"build_target\": {\n            \"oneOf\": [\n                {\"$ref\": \"./definitions.jsonschema#/keyboard_keymap_tuple\"},\n                {\"$ref\": \"./definitions.jsonschema#/keyboard_keymap_env\"},\n                {\"$ref\": \"./definitions.jsonschema#/json_file_path\"}\n            ]\n        }\n    },\n    \"required\": [\n        \"userspace_version\",\n        \"build_targets\"\n    ],\n    \"properties\": {\n        \"userspace_version\": {\n            \"type\": \"string\",\n            \"enum\": [\"1.1\"]\n        },\n        \"build_targets\": {\n            \"type\": \"array\",\n            \"items\": {\n                \"$ref\": \"#/definitions/build_target\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "data/templates/api/readme.md",
    "content": "# QMK Keyboard Metadata\n\nThis directory contains machine parsable data about keyboards supported by QMK. The latest version is always available online at <https://keyboards.qmk.fm>.\n\nDo not edit anything here by hand. It is generated with the `qmk generate-api` command.\n"
  },
  {
    "path": "data/templates/config-overrides/chibios/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <board.h>\n\n// #undef STM32_HSE_BYPASS\n"
  },
  {
    "path": "data/templates/config-overrides/chibios/chconf.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n// #define CH_CFG_OPTIMIZE_SPEED TRUE\n\n#include_next <chconf.h>\n"
  },
  {
    "path": "data/templates/config-overrides/chibios/halconf.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n// #define HAL_USE_DAC TRUE\n\n#include_next <halconf.h>\n"
  },
  {
    "path": "data/templates/config-overrides/chibios/mcuconf.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <mcuconf.h>\n\n// #undef STM32_HSE_ENABLED\n// #define STM32_HSE_ENABLED FALSE\n"
  },
  {
    "path": "data/templates/config-overrides/common/lv_conf.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n// #define LV_DITHER_GRADIENT 1\n\n#include_next <lv_conf.h>\n\n// #undef LV_COLOR_16_SWAP\n// #define LV_COLOR_16_SWAP 0\n"
  },
  {
    "path": "data/templates/keyboard/keyboard.json",
    "content": "{\n    \"keyboard_name\": \"%KEYBOARD%\",\n    \"maintainer\": \"%USER_NAME%\",\n    \"manufacturer\": \"%REAL_NAME%\",\n    \"diode_direction\": \"COL2ROW\",\n    \"matrix_pins\": {\n        \"cols\": [\"C2\"],\n        \"rows\": [\"D1\"]\n    },\n    \"usb\": {\n        \"vid\": \"0xFEED\",\n        \"pid\": \"0x0000\",\n        \"device_version\": \"1.0.0\"\n    },\n    \"features\": {\n        \"bootmagic\": true,\n        \"extrakey\": true,\n        \"mousekey\": true,\n        \"nkro\": true\n    }\n}\n"
  },
  {
    "path": "data/templates/keyboard/readme.md",
    "content": "# %KEYBOARD%\n\n![%KEYBOARD%](imgur.com image replace me!)\n\n*A short description of the keyboard/project*\n\n* Keyboard Maintainer: [%REAL_NAME%](https://github.com/%USER_NAME%)\n* Hardware Supported: *The PCBs, controllers supported*\n* Hardware Availability: *Links to where you can find this hardware*\n\nMake example for this keyboard (after setting up your build environment):\n\n    make %KEYBOARD%:default\n\nFlashing example for this keyboard:\n\n    make %KEYBOARD%:default:flash\n\nSee 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).\n\n## Bootloader\n\nEnter the bootloader in 3 ways:\n\n* **Bootmagic reset**: Hold down the key at (0,0) in the matrix (usually the top left key or Escape) and plug in the keyboard\n* **Physical reset button**: Briefly press the button on the back of the PCB - some may have pads you must short instead\n* **Keycode in layout**: Press the key mapped to `QK_BOOT` if it is available\n"
  },
  {
    "path": "doxygen-todo",
    "content": "tmk_core/protocol\ntmk_core/protocol/chibios\ntmk_core/protocol/lufa\ntmk_core/protocol/midi\ntmk_core/protocol/midi/bytequeue\ntmk_core/protocol/midi/Config\ntmk_core/protocol/usb_hid\ntmk_core/protocol/vusb\nquantum\nquantum/audio\nquantum/keymap_extras\nquantum/process_keycode\ndrivers\n"
  },
  {
    "path": "drivers/backlight/backlight_software.c",
    "content": "#include \"backlight.h\"\n#include \"backlight_driver_common.h\"\n#include \"util.h\"\n\n#ifdef BACKLIGHT_BREATHING\n#    error \"Backlight breathing is not available for software PWM. Please disable.\"\n#endif\n\nstatic uint16_t s_duty_pattern = 0;\n\n// clang-format off\n\n/** \\brief PWM duty patterns\n *\n * We scale the current backlight level to an index within this array. This allows\n * backlight_task to focus on just switching LEDs on/off, and we can predict the duty pattern\n */\nstatic const uint16_t backlight_duty_table[] = {\n    0b0000000000000000,\n    0b1000000000000000,\n    0b1000000010000000,\n    0b1000001000010000,\n    0b1000100010001000,\n    0b1001001001001000,\n    0b1010101010101010,\n    0b1110111011101110,\n    0b1111111111111111,\n};\n#define backlight_duty_table_size ARRAY_SIZE(backlight_duty_table)\n\n// clang-format on\n\nstatic uint8_t scale_backlight(uint8_t v) {\n    return v * (backlight_duty_table_size - 1) / BACKLIGHT_LEVELS;\n}\n\nvoid backlight_init_ports(void) {\n    backlight_pins_init();\n}\n\nvoid backlight_set(uint8_t level) {\n    s_duty_pattern = backlight_duty_table[scale_backlight(level)];\n}\n\nvoid backlight_task(void) {\n    static uint8_t backlight_tick = 0;\n\n    if (s_duty_pattern & ((uint16_t)1 << backlight_tick)) {\n        backlight_pins_on();\n    } else {\n        backlight_pins_off();\n    }\n    backlight_tick = (backlight_tick + 1) % 16;\n}\n"
  },
  {
    "path": "drivers/battery/battery.c",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"battery_driver.h\"\n#include \"battery.h\"\n#include \"timer.h\"\n\n#ifndef BATTERY_SAMPLE_INTERVAL\n#    define BATTERY_SAMPLE_INTERVAL 30000\n#endif\n\nstatic uint8_t last_bat_level = 100;\n\nvoid battery_init(void) {\n    battery_driver_init();\n\n    last_bat_level = battery_driver_sample_percent();\n}\n\n__attribute__((weak)) void battery_percent_changed_user(uint8_t level) {}\n__attribute__((weak)) void battery_percent_changed_kb(uint8_t level) {}\n\nstatic void handle_percent_changed(void) {\n    battery_percent_changed_user(last_bat_level);\n    battery_percent_changed_kb(last_bat_level);\n}\n\nvoid battery_task(void) {\n    static uint32_t bat_timer = 0;\n    if (timer_elapsed32(bat_timer) > BATTERY_SAMPLE_INTERVAL) {\n        last_bat_level = battery_driver_sample_percent();\n\n        handle_percent_changed();\n\n        bat_timer = timer_read32();\n    }\n}\n\nuint8_t battery_get_percent(void) {\n    return last_bat_level;\n}\n"
  },
  {
    "path": "drivers/battery/battery.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n\n/**\n * \\file\n *\n * \\defgroup battery Battery API\n *\n * \\brief API to query battery status.\n * \\{\n */\n\n/**\n * \\brief Initialize the battery driver.\n */\nvoid battery_init(void);\n\n/**\n * \\brief Perform housekeeping tasks.\n */\nvoid battery_task(void);\n\n/**\n * \\brief Sample battery level.\n *\n * \\return The battery percentage, in the range 0-100.\n */\nuint8_t battery_get_percent(void);\n\n/**\n * \\brief user hook called when battery level changed.\n *\n */\nvoid battery_percent_changed_user(uint8_t level);\n\n/**\n * \\brief keyboard hook called when battery level changed.\n *\n */\nvoid battery_percent_changed_kb(uint8_t level);\n\n/** \\} */\n"
  },
  {
    "path": "drivers/battery/battery_adc.c",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"analog.h\"\n#include \"gpio.h\"\n\n#ifndef BATTERY_PIN\n#    error(\"BATTERY_PIN not configured!\")\n#endif\n\n#ifndef BATTERY_REF_VOLTAGE_MV\n#    define BATTERY_REF_VOLTAGE_MV 3300\n#endif\n\n#ifndef BATTERY_VOLTAGE_DIVIDER_R1\n#    define BATTERY_VOLTAGE_DIVIDER_R1 100\n#endif\n\n#ifndef BATTERY_VOLTAGE_DIVIDER_R2\n#    define BATTERY_VOLTAGE_DIVIDER_R2 100\n#endif\n\n// TODO: infer from adc config?\n#ifndef BATTERY_ADC_RESOLUTION\n#    define BATTERY_ADC_RESOLUTION 10\n#endif\n\nvoid battery_driver_init(void) {\n    gpio_set_pin_input(BATTERY_PIN);\n}\n\nuint16_t battery_driver_get_mv(void) {\n    uint32_t raw = analogReadPin(BATTERY_PIN);\n\n    uint32_t bat_mv = raw * BATTERY_REF_VOLTAGE_MV / (1 << BATTERY_ADC_RESOLUTION);\n\n#if BATTERY_VOLTAGE_DIVIDER_R1 > 0 && BATTERY_VOLTAGE_DIVIDER_R2 > 0\n    bat_mv = bat_mv * (BATTERY_VOLTAGE_DIVIDER_R1 + BATTERY_VOLTAGE_DIVIDER_R2) / BATTERY_VOLTAGE_DIVIDER_R2;\n#endif\n\n    return bat_mv;\n}\n\nuint8_t battery_driver_sample_percent(void) {\n    uint16_t bat_mv = battery_driver_get_mv();\n\n    // https://github.com/zmkfirmware/zmk/blob/3f7c9d7cc4f46617faad288421025ea2a6b0bd28/app/module/drivers/sensor/battery/battery_common.c#L33\n    if (bat_mv >= 4200) {\n        return 100;\n    } else if (bat_mv <= 3450) {\n        return 0;\n    }\n\n    return bat_mv * 2 / 15 - 459;\n}\n"
  },
  {
    "path": "drivers/battery/battery_driver.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n\n/**\n * \\file\n *\n * \\defgroup battery Battery Driver API\n *\n * \\brief API to query battery status.\n * \\{\n */\n\n/**\n * \\brief Initialize the battery driver. This function must be called only once, before any of the below functions can be called.\n */\nvoid battery_driver_init(void);\n\n/**\n * \\brief Sample battery level.\n *\n * \\return The battery percentage, in the range 0-100.\n */\nuint8_t battery_driver_sample_percent(void);\n\n/** \\} */\n"
  },
  {
    "path": "drivers/bluetooth/bluefruit_le.cpp",
    "content": "#include \"bluefruit_le.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <alloca.h>\n#include \"debug.h\"\n#include \"timer.h\"\n#include \"gpio.h\"\n#include \"ringbuffer.hpp\"\n#include <string.h>\n#include \"spi_master.h\"\n#include \"wait.h\"\n#include \"analog.h\"\n#include \"progmem.h\"\n\n// These are the pin assignments for the 32u4 boards.\n// You may define them to something else in your config.h\n// if yours is wired up differently.\n#ifndef BLUEFRUIT_LE_RST_PIN\n#    define BLUEFRUIT_LE_RST_PIN D4\n#endif\n\n#ifndef BLUEFRUIT_LE_CS_PIN\n#    define BLUEFRUIT_LE_CS_PIN B4\n#endif\n\n#ifndef BLUEFRUIT_LE_IRQ_PIN\n#    define BLUEFRUIT_LE_IRQ_PIN E6\n#endif\n\n#ifndef BLUEFRUIT_LE_SCK_DIVISOR\n#    define BLUEFRUIT_LE_SCK_DIVISOR 2 // 4MHz SCK/8MHz CPU, calculated for Feather 32U4 BLE\n#endif\n\n#define ConnectionUpdateInterval 1000 /* milliseconds */\n\nstatic struct {\n    bool is_connected;\n    bool initialized;\n    bool configured;\n\n#define ProbedEvents 1\n#define UsingEvents 2\n    bool event_flags;\n\n    uint16_t last_connection_update;\n} state;\n\n// Commands are encoded using SDEP and sent via SPI\n// https://github.com/adafruit/Adafruit_BluefruitLE_nRF51/blob/master/SDEP.md\n\n#define SdepMaxPayload 16\nstruct sdep_msg {\n    uint8_t type;\n    uint8_t cmd_low;\n    uint8_t cmd_high;\n    struct __attribute__((packed)) {\n        uint8_t len : 7;\n        uint8_t more : 1;\n    };\n    uint8_t payload[SdepMaxPayload];\n} __attribute__((packed));\n\n// The recv latency is relatively high, so when we're hammering keys quickly,\n// we want to avoid waiting for the responses in the matrix loop.  We maintain\n// a short queue for that.  Since there is quite a lot of space overhead for\n// the AT command representation wrapped up in SDEP, we queue the minimal\n// information here.\n\nenum queue_type {\n    QTKeyReport, // 1-byte modifier + 6-byte key report\n    QTConsumer,  // 16-bit key code\n    QTMouseMove, // 4-byte mouse report\n};\n\nstruct queue_item {\n    enum queue_type queue_type;\n    uint16_t        added;\n    union __attribute__((packed)) {\n        struct __attribute__((packed)) {\n            uint8_t modifier;\n            uint8_t keys[6];\n        } key;\n\n        uint16_t consumer;\n        struct __attribute__((packed)) {\n            int8_t  x, y, scroll, pan;\n            uint8_t buttons;\n        } mousemove;\n    };\n};\n\n// Items that we wish to send\nstatic RingBuffer<queue_item, 40> send_buf;\n// Pending response; while pending, we can't send any more requests.\n// This records the time at which we sent the command for which we\n// are expecting a response.\nstatic RingBuffer<uint16_t, 2> resp_buf;\n\nstatic bool process_queue_item(struct queue_item *item, uint16_t timeout);\n\nenum sdep_type {\n    SdepCommand       = 0x10,\n    SdepResponse      = 0x20,\n    SdepAlert         = 0x40,\n    SdepError         = 0x80,\n    SdepSlaveNotReady = 0xFE, // Try again later\n    SdepSlaveOverflow = 0xFF, // You read more data than is available\n};\n\nenum ble_cmd {\n    BleInitialize = 0xBEEF,\n    BleAtWrapper  = 0x0A00,\n    BleUartTx     = 0x0A01,\n    BleUartRx     = 0x0A02,\n};\n\nenum ble_system_event_bits {\n    BleSystemConnected    = 0,\n    BleSystemDisconnected = 1,\n    BleSystemUartRx       = 8,\n    BleSystemMidiRx       = 10,\n};\n\n#define SdepTimeout 150             /* milliseconds */\n#define SdepShortTimeout 10         /* milliseconds */\n#define SdepBackOff 25              /* microseconds */\n#define BatteryUpdateInterval 10000 /* milliseconds */\n\nstatic bool at_command(const char *cmd, char *resp, uint16_t resplen, bool verbose, uint16_t timeout = SdepTimeout);\nstatic bool at_command_P(const char *cmd, char *resp, uint16_t resplen, bool verbose = false);\n\n// Send a single SDEP packet\nstatic bool sdep_send_pkt(const struct sdep_msg *msg, uint16_t timeout) {\n    spi_start(BLUEFRUIT_LE_CS_PIN, false, 0, BLUEFRUIT_LE_SCK_DIVISOR);\n    uint16_t timerStart = timer_read();\n    bool     success    = false;\n    bool     ready      = false;\n\n    do {\n        ready = spi_write(msg->type) != SdepSlaveNotReady;\n        if (ready) {\n            break;\n        }\n\n        // Release it and let it initialize\n        spi_stop();\n        wait_us(SdepBackOff);\n        spi_start(BLUEFRUIT_LE_CS_PIN, false, 0, BLUEFRUIT_LE_SCK_DIVISOR);\n    } while (timer_elapsed(timerStart) < timeout);\n\n    if (ready) {\n        // Slave is ready; send the rest of the packet\n        spi_transmit(&msg->cmd_low, sizeof(*msg) - (1 + sizeof(msg->payload)) + msg->len);\n        success = true;\n    }\n\n    spi_stop();\n\n    return success;\n}\n\nstatic inline void sdep_build_pkt(struct sdep_msg *msg, uint16_t command, const uint8_t *payload, uint8_t len, bool moredata) {\n    msg->type     = SdepCommand;\n    msg->cmd_low  = command & 0xFF;\n    msg->cmd_high = command >> 8;\n    msg->len      = len;\n    msg->more     = (moredata && len == SdepMaxPayload) ? 1 : 0;\n\n    static_assert(sizeof(*msg) == 20, \"msg is correctly packed\");\n\n    memcpy(msg->payload, payload, len);\n}\n\n// Read a single SDEP packet\nstatic bool sdep_recv_pkt(struct sdep_msg *msg, uint16_t timeout) {\n    bool     success    = false;\n    uint16_t timerStart = timer_read();\n    bool     ready      = false;\n\n    do {\n        ready = gpio_read_pin(BLUEFRUIT_LE_IRQ_PIN);\n        if (ready) {\n            break;\n        }\n        wait_us(1);\n    } while (timer_elapsed(timerStart) < timeout);\n\n    if (ready) {\n        spi_start(BLUEFRUIT_LE_CS_PIN, false, 0, BLUEFRUIT_LE_SCK_DIVISOR);\n\n        do {\n            // Read the command type, waiting for the data to be ready\n            msg->type = spi_read();\n            if (msg->type == SdepSlaveNotReady || msg->type == SdepSlaveOverflow) {\n                // Release it and let it initialize\n                spi_stop();\n                wait_us(SdepBackOff);\n                spi_start(BLUEFRUIT_LE_CS_PIN, false, 0, BLUEFRUIT_LE_SCK_DIVISOR);\n                continue;\n            }\n\n            // Read the rest of the header\n            spi_receive(&msg->cmd_low, sizeof(*msg) - (1 + sizeof(msg->payload)));\n\n            // and get the payload if there is any\n            if (msg->len <= SdepMaxPayload) {\n                spi_receive(msg->payload, msg->len);\n            }\n            success = true;\n            break;\n        } while (timer_elapsed(timerStart) < timeout);\n\n        spi_stop();\n    }\n    return success;\n}\n\nstatic void resp_buf_read_one(bool greedy) {\n    uint16_t last_send;\n    if (!resp_buf.peek(last_send)) {\n        return;\n    }\n\n    if (gpio_read_pin(BLUEFRUIT_LE_IRQ_PIN)) {\n        struct sdep_msg msg;\n\n    again:\n        if (sdep_recv_pkt(&msg, SdepTimeout)) {\n            if (!msg.more) {\n                // We got it; consume this entry\n                resp_buf.get(last_send);\n                dprintf(\"recv latency %dms\\n\", TIMER_DIFF_16(timer_read(), last_send));\n            }\n\n            if (greedy && resp_buf.peek(last_send) && gpio_read_pin(BLUEFRUIT_LE_IRQ_PIN)) {\n                goto again;\n            }\n        }\n\n    } else if (timer_elapsed(last_send) > SdepTimeout * 2) {\n        dprintf(\"waiting_for_result: timeout, resp_buf size %d\\n\", (int)resp_buf.size());\n\n        // Timed out: consume this entry\n        resp_buf.get(last_send);\n    }\n}\n\nstatic void send_buf_send_one(uint16_t timeout = SdepTimeout) {\n    struct queue_item item;\n\n    // Don't send anything more until we get an ACK\n    if (!resp_buf.empty()) {\n        return;\n    }\n\n    if (!send_buf.peek(item)) {\n        return;\n    }\n    if (process_queue_item(&item, timeout)) {\n        // commit that peek\n        send_buf.get(item);\n        dprintf(\"send_buf_send_one: have %d remaining\\n\", (int)send_buf.size());\n    } else {\n        dprint(\"failed to send, will retry\\n\");\n        wait_ms(SdepTimeout);\n        resp_buf_read_one(true);\n    }\n}\n\nstatic void resp_buf_wait(const char *cmd) {\n    bool didPrint = false;\n    while (!resp_buf.empty()) {\n        if (!didPrint) {\n            dprintf(\"wait on buf for %s\\n\", cmd);\n            didPrint = true;\n        }\n        resp_buf_read_one(true);\n    }\n}\n\nvoid bluefruit_le_init(void) {\n    state.initialized  = false;\n    state.configured   = false;\n    state.is_connected = false;\n\n    gpio_set_pin_input(BLUEFRUIT_LE_IRQ_PIN);\n\n    spi_init();\n\n    // Perform a hardware reset\n    gpio_set_pin_output(BLUEFRUIT_LE_RST_PIN);\n    gpio_write_pin_high(BLUEFRUIT_LE_RST_PIN);\n    gpio_write_pin_low(BLUEFRUIT_LE_RST_PIN);\n    wait_ms(10);\n    gpio_write_pin_high(BLUEFRUIT_LE_RST_PIN);\n\n    wait_ms(1000); // Give it a second to initialize\n\n    state.initialized = true;\n}\n\nstatic inline uint8_t min(uint8_t a, uint8_t b) {\n    return a < b ? a : b;\n}\n\nstatic bool read_response(char *resp, uint16_t resplen, bool verbose) {\n    char *dest = resp;\n    char *end  = dest + resplen;\n\n    while (true) {\n        struct sdep_msg msg;\n\n        if (!sdep_recv_pkt(&msg, 2 * SdepTimeout)) {\n            dprint(\"sdep_recv_pkt failed\\n\");\n            return false;\n        }\n\n        if (msg.type != SdepResponse) {\n            *resp = 0;\n            return false;\n        }\n\n        uint8_t len = min(msg.len, end - dest);\n        if (len > 0) {\n            memcpy(dest, msg.payload, len);\n            dest += len;\n        }\n\n        if (!msg.more) {\n            // No more data is expected!\n            break;\n        }\n    }\n\n    // Ensure the response is NUL terminated\n    *dest = 0;\n\n    // \"Parse\" the result text; we want to snip off the trailing OK or ERROR line\n    // Rewind past the possible trailing CRLF so that we can strip it\n    --dest;\n    while (dest > resp && (dest[0] == '\\n' || dest[0] == '\\r')) {\n        *dest = 0;\n        --dest;\n    }\n\n    // Look back for start of preceeding line\n    char *last_line = strrchr(resp, '\\n');\n    if (last_line) {\n        ++last_line;\n    } else {\n        last_line = resp;\n    }\n\n    bool              success       = false;\n    static const char kOK[] PROGMEM = \"OK\";\n\n    success = !strcmp_P(last_line, kOK);\n\n    if (verbose || !success) {\n        dprintf(\"result: %s\\n\", resp);\n    }\n    return success;\n}\n\nstatic bool at_command(const char *cmd, char *resp, uint16_t resplen, bool verbose, uint16_t timeout) {\n    const char *    end = cmd + strlen(cmd);\n    struct sdep_msg msg;\n\n    if (verbose) {\n        dprintf(\"ble send: %s\\n\", cmd);\n    }\n\n    if (resp) {\n        // They want to decode the response, so we need to flush and wait\n        // for all pending I/O to finish before we start this one, so\n        // that we don't confuse the results\n        resp_buf_wait(cmd);\n        *resp = 0;\n    }\n\n    // Fragment the command into a series of SDEP packets\n    while (end - cmd > SdepMaxPayload) {\n        sdep_build_pkt(&msg, BleAtWrapper, (uint8_t *)cmd, SdepMaxPayload, true);\n        if (!sdep_send_pkt(&msg, timeout)) {\n            return false;\n        }\n        cmd += SdepMaxPayload;\n    }\n\n    sdep_build_pkt(&msg, BleAtWrapper, (uint8_t *)cmd, end - cmd, false);\n    if (!sdep_send_pkt(&msg, timeout)) {\n        return false;\n    }\n\n    if (resp == NULL) {\n        uint16_t now = timer_read();\n        while (!resp_buf.enqueue(now)) {\n            resp_buf_read_one(false);\n        }\n        uint16_t later = timer_read();\n        if (TIMER_DIFF_16(later, now) > 0) {\n            dprintf(\"waited %dms for resp_buf\\n\", TIMER_DIFF_16(later, now));\n        }\n        return true;\n    }\n\n    return read_response(resp, resplen, verbose);\n}\n\nbool at_command_P(const char *cmd, char *resp, uint16_t resplen, bool verbose) {\n    char *cmdbuf = (char *)alloca(strlen_P(cmd) + 1);\n    strcpy_P(cmdbuf, cmd);\n    return at_command(cmdbuf, resp, resplen, verbose);\n}\n\nbool bluefruit_le_is_connected(void) {\n    return state.is_connected;\n}\n\nbool bluefruit_le_enable_keyboard(void) {\n    char resbuf[128];\n\n    if (!state.initialized) {\n        return false;\n    }\n\n    state.configured = false;\n\n    // Disable command echo\n    static const char kEcho[] PROGMEM = \"ATE=0\";\n    // Make the advertised name match the keyboard\n    static const char kGapDevName[] PROGMEM = \"AT+GAPDEVNAME=\" PRODUCT;\n    // Turn on keyboard support\n    static const char kHidEnOn[] PROGMEM = \"AT+BLEHIDEN=1\";\n\n    // Adjust intervals to improve latency.  This causes the \"central\"\n    // system (computer/tablet) to poll us every 10-30 ms.  We can't\n    // set a smaller value than 10ms, and 30ms seems to be the natural\n    // processing time on my macbook.  Keeping it constrained to that\n    // feels reasonable to type to.\n    static const char kGapIntervals[] PROGMEM = \"AT+GAPINTERVALS=10,30,,\";\n\n    // Reset the device so that it picks up the above changes\n    static const char kATZ[] PROGMEM = \"ATZ\";\n\n    // Turn down the power level a bit\n    static const char  kPower[] PROGMEM             = \"AT+BLEPOWERLEVEL=-12\";\n    static PGM_P const configure_commands[] PROGMEM = {\n        kEcho, kGapIntervals, kGapDevName, kHidEnOn, kPower, kATZ,\n    };\n\n    uint8_t i;\n    for (i = 0; i < sizeof(configure_commands) / sizeof(configure_commands[0]); ++i) {\n        PGM_P cmd;\n        memcpy_P(&cmd, configure_commands + i, sizeof(cmd));\n\n        if (!at_command_P(cmd, resbuf, sizeof(resbuf))) {\n            dprintf(\"failed BLE command: %S: %s\\n\", cmd, resbuf);\n            goto fail;\n        }\n    }\n\n    state.configured = true;\n\n    // Check connection status in a little while; allow the ATZ time\n    // to kick in.\n    state.last_connection_update = timer_read();\nfail:\n    return state.configured;\n}\n\nstatic void set_connected(bool connected) {\n    if (connected != state.is_connected) {\n        if (connected) {\n            dprint(\"BLE connected\\n\");\n        } else {\n            dprint(\"BLE disconnected\\n\");\n        }\n        state.is_connected = connected;\n\n        // TODO: if modifiers are down on the USB interface and\n        // we cut over to BLE or vice versa, they will remain stuck.\n        // This feels like a good point to do something like clearing\n        // the keyboard and/or generating a fake all keys up message.\n        // However, I've noticed that it takes a couple of seconds\n        // for macOS to to start recognizing key presses after BLE\n        // is in the connected state, so I worry that doing that\n        // here may not be good enough.\n    }\n}\n\nvoid bluefruit_le_task(void) {\n    char resbuf[48];\n\n    if (!state.configured && !bluefruit_le_enable_keyboard()) {\n        return;\n    }\n    resp_buf_read_one(true);\n    send_buf_send_one(SdepShortTimeout);\n\n    if (resp_buf.empty() && (state.event_flags & UsingEvents) && gpio_read_pin(BLUEFRUIT_LE_IRQ_PIN)) {\n        // Must be an event update\n        if (at_command_P(PSTR(\"AT+EVENTSTATUS\"), resbuf, sizeof(resbuf))) {\n            uint32_t mask = strtoul(resbuf, NULL, 16);\n\n            if (mask & BleSystemConnected) {\n                set_connected(true);\n            } else if (mask & BleSystemDisconnected) {\n                set_connected(false);\n            }\n        }\n    }\n\n    if (timer_elapsed(state.last_connection_update) > ConnectionUpdateInterval) {\n        bool shouldPoll = true;\n        if (!(state.event_flags & ProbedEvents)) {\n            // Request notifications about connection status changes.\n            // This only works in SPIFRIEND firmware > 0.6.7, which is why\n            // we check for this conditionally here.\n            // Note that at the time of writing, HID reports only work correctly\n            // with Apple products on firmware version 0.6.7!\n            // https://forums.adafruit.com/viewtopic.php?f=8&t=104052\n            if (at_command_P(PSTR(\"AT+EVENTENABLE=0x1\"), resbuf, sizeof(resbuf))) {\n                at_command_P(PSTR(\"AT+EVENTENABLE=0x2\"), resbuf, sizeof(resbuf));\n                state.event_flags |= UsingEvents;\n            }\n            state.event_flags |= ProbedEvents;\n\n            // leave shouldPoll == true so that we check at least once\n            // before relying solely on events\n        } else {\n            shouldPoll = false;\n        }\n\n        static const char kGetConn[] PROGMEM = \"AT+GAPGETCONN\";\n        state.last_connection_update         = timer_read();\n\n        if (at_command_P(kGetConn, resbuf, sizeof(resbuf))) {\n            set_connected(atoi(resbuf));\n        }\n    }\n}\n\nstatic bool process_queue_item(struct queue_item *item, uint16_t timeout) {\n    char cmdbuf[48];\n    char fmtbuf[64];\n\n    // Arrange to re-check connection after keys have settled\n    state.last_connection_update = timer_read();\n\n#if 1\n    if (TIMER_DIFF_16(state.last_connection_update, item->added) > 0) {\n        dprintf(\"send latency %dms\\n\", TIMER_DIFF_16(state.last_connection_update, item->added));\n    }\n#endif\n\n    switch (item->queue_type) {\n        case QTKeyReport:\n            strcpy_P(fmtbuf, PSTR(\"AT+BLEKEYBOARDCODE=%02x-00-%02x-%02x-%02x-%02x-%02x-%02x\"));\n            snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->key.modifier, item->key.keys[0], item->key.keys[1], item->key.keys[2], item->key.keys[3], item->key.keys[4], item->key.keys[5]);\n            return at_command(cmdbuf, NULL, 0, true, timeout);\n\n#ifdef EXTRAKEY_ENABLE\n        case QTConsumer:\n            strcpy_P(fmtbuf, PSTR(\"AT+BLEHIDCONTROLKEY=0x%04x\"));\n            snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->consumer);\n            return at_command(cmdbuf, NULL, 0, true, timeout);\n#endif\n\n#ifdef MOUSE_ENABLE\n        case QTMouseMove:\n            strcpy_P(fmtbuf, PSTR(\"AT+BLEHIDMOUSEMOVE=%d,%d,%d,%d\"));\n            snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->mousemove.x, item->mousemove.y, item->mousemove.scroll, item->mousemove.pan);\n            if (!at_command(cmdbuf, NULL, 0, true, timeout)) {\n                return false;\n            }\n            strcpy_P(cmdbuf, PSTR(\"AT+BLEHIDMOUSEBUTTON=\"));\n            if (item->mousemove.buttons & MOUSE_BTN1) {\n                strcat(cmdbuf, \"L\");\n            }\n            if (item->mousemove.buttons & MOUSE_BTN2) {\n                strcat(cmdbuf, \"R\");\n            }\n            if (item->mousemove.buttons & MOUSE_BTN3) {\n                strcat(cmdbuf, \"M\");\n            }\n            if (item->mousemove.buttons == 0) {\n                strcat(cmdbuf, \"0\");\n            }\n            return at_command(cmdbuf, NULL, 0, true, timeout);\n#endif\n        default:\n            return true;\n    }\n}\n\nvoid bluefruit_le_send_keyboard(report_keyboard_t *report) {\n    struct queue_item item;\n\n    item.queue_type   = QTKeyReport;\n    item.key.modifier = report->mods;\n    item.key.keys[0]  = report->keys[0];\n    item.key.keys[1]  = report->keys[1];\n    item.key.keys[2]  = report->keys[2];\n    item.key.keys[3]  = report->keys[3];\n    item.key.keys[4]  = report->keys[4];\n    item.key.keys[5]  = report->keys[5];\n\n    while (!send_buf.enqueue(item)) {\n        send_buf_send_one();\n    }\n}\n\nvoid bluefruit_le_send_consumer(uint16_t usage) {\n    struct queue_item item;\n\n    item.queue_type = QTConsumer;\n    item.consumer   = usage;\n\n    while (!send_buf.enqueue(item)) {\n        send_buf_send_one();\n    }\n}\n\nvoid bluefruit_le_send_mouse(report_mouse_t *report) {\n    struct queue_item item;\n\n    item.queue_type        = QTMouseMove;\n    item.mousemove.x       = report->x;\n    item.mousemove.y       = report->y;\n    item.mousemove.scroll  = report->v;\n    item.mousemove.pan     = report->h;\n    item.mousemove.buttons = report->buttons;\n\n    while (!send_buf.enqueue(item)) {\n        send_buf_send_one();\n    }\n}\n\nbool bluefruit_le_set_mode_leds(bool on) {\n    if (!state.configured) {\n        return false;\n    }\n\n    // The \"mode\" led is the red blinky one\n    at_command_P(on ? PSTR(\"AT+HWMODELED=1\") : PSTR(\"AT+HWMODELED=0\"), NULL, 0);\n\n    // Pin 19 is the blue \"connected\" LED; turn that off too.\n    // When turning LEDs back on, don't turn that LED on if we're\n    // not connected, as that would be confusing.\n    at_command_P(on && state.is_connected ? PSTR(\"AT+HWGPIO=19,1\") : PSTR(\"AT+HWGPIO=19,0\"), NULL, 0);\n    return true;\n}\n\n// https://learn.adafruit.com/adafruit-feather-32u4-bluefruit-le/ble-generic#at-plus-blepowerlevel\nbool bluefruit_le_set_power_level(int8_t level) {\n    char cmd[46];\n    if (!state.configured) {\n        return false;\n    }\n    snprintf(cmd, sizeof(cmd), \"AT+BLEPOWERLEVEL=%d\", level);\n    return at_command(cmd, NULL, 0, false);\n}\n"
  },
  {
    "path": "drivers/bluetooth/bluefruit_le.h",
    "content": "/* Bluetooth Low Energy Protocol for QMK.\n * Author: Wez Furlong, 2016\n * Supports the Adafruit BLE board built around the nRF51822 chip.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"report.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Instruct the module to enable HID keyboard support and reset */\nextern bool bluefruit_le_enable_keyboard(void);\n\n/* Query to see if the BLE module is connected */\nextern bool bluefruit_le_query_is_connected(void);\n\n/* Returns true if we believe that the BLE module is connected.\n * This uses our cached understanding that is maintained by\n * calling ble_task() periodically. */\nextern bool bluefruit_le_is_connected(void);\n\nextern void bluefruit_le_init(void);\n\n/* Call this periodically to process BLE-originated things */\nextern void bluefruit_le_task(void);\n\n/* Generates keypress events for a set of keys.\n * The hid modifier mask specifies the state of the modifier keys for\n * this set of keys.\n * Also sends a key release indicator, so that the keys do not remain\n * held down. */\nextern void bluefruit_le_send_keyboard(report_keyboard_t *report);\n\n/* Send a consumer usage.\n * (milliseconds) */\nextern void bluefruit_le_send_consumer(uint16_t usage);\n\n/* Send a mouse/wheel movement report.\n * The parameters are signed and indicate positive or negative direction\n * change. */\nextern void bluefruit_le_send_mouse(report_mouse_t *report);\n\nextern bool bluefruit_le_set_mode_leds(bool on);\nextern bool bluefruit_le_set_power_level(int8_t level);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "drivers/bluetooth/bluetooth.c",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"bluetooth.h\"\n\n__attribute__((weak)) void bluetooth_init(void) {}\n\n__attribute__((weak)) void bluetooth_task(void) {}\n\n__attribute__((weak)) bool bluetooth_is_connected(void) {\n    return true;\n}\n\n__attribute__((weak)) bool bluetooth_can_send_nkro(void) {\n    return false;\n}\n\n__attribute__((weak)) uint8_t bluetooth_keyboard_leds(void) {\n    return 0;\n}\n\n__attribute__((weak)) void bluetooth_send_keyboard(report_keyboard_t *report) {}\n\n__attribute__((weak)) void bluetooth_send_nkro(report_nkro_t *report) {}\n\n__attribute__((weak)) void bluetooth_send_mouse(report_mouse_t *report) {}\n\n__attribute__((weak)) void bluetooth_send_consumer(uint16_t usage) {}\n\n__attribute__((weak)) void bluetooth_send_system(uint16_t usage) {}\n\n__attribute__((weak)) void bluetooth_send_raw_hid(uint8_t *data, uint8_t length) {}\n"
  },
  {
    "path": "drivers/bluetooth/bluetooth.h",
    "content": "/*\n * Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include \"report.h\"\n\n/**\n * \\brief Initialize the Bluetooth system.\n */\nvoid bluetooth_init(void);\n\n/**\n * \\brief Perform housekeeping tasks.\n */\nvoid bluetooth_task(void);\n\n/**\n * \\brief Detects if Bluetooth is connected.\n *\n * \\return `true` if connected, `false` otherwise.\n */\nbool bluetooth_is_connected(void);\n\n/**\n * \\brief Detects if `bluetooth_send_nkro` should be used over `bluetooth_send_keyboard`.\n */\nbool bluetooth_can_send_nkro(void);\n\n/**\n * \\brief Get current LED state.\n */\nuint8_t bluetooth_keyboard_leds(void);\n\n/**\n * \\brief Send a keyboard report.\n *\n * \\param report The keyboard report to send.\n */\nvoid bluetooth_send_keyboard(report_keyboard_t *report);\n\n/**\n * \\brief Send a nkro report.\n *\n * \\param report The nkro report to send.\n */\nvoid bluetooth_send_nkro(report_nkro_t *report);\n\n/**\n * \\brief Send a mouse report.\n *\n * \\param report The mouse report to send.\n */\nvoid bluetooth_send_mouse(report_mouse_t *report);\n\n/**\n * \\brief Send a consumer usage.\n *\n * \\param usage The consumer usage to send.\n */\nvoid bluetooth_send_consumer(uint16_t usage);\n\n/**\n * \\brief Send a system usage.\n *\n * \\param usage The system usage to send.\n */\nvoid bluetooth_send_system(uint16_t usage);\n\n/**\n * \\brief Send a raw_hid packet.\n *\n * \\param data A pointer to the buffer to be sent. Always 32 bytes in length.\n * \\param length The length of the buffer. Always 32.\n */\nvoid bluetooth_send_raw_hid(uint8_t *data, uint8_t length);\n"
  },
  {
    "path": "drivers/bluetooth/bluetooth_drivers.c",
    "content": "/*\n * Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bluetooth.h\"\n\n#if defined(BLUETOOTH_BLUEFRUIT_LE)\n#    include \"bluefruit_le.h\"\n#elif defined(BLUETOOTH_RN42)\n#    include \"rn42.h\"\n#endif\n\nvoid bluetooth_init(void) {\n#if defined(BLUETOOTH_BLUEFRUIT_LE)\n    bluefruit_le_init();\n#elif defined(BLUETOOTH_RN42)\n    rn42_init();\n#endif\n}\n\nvoid bluetooth_task(void) {\n#if defined(BLUETOOTH_BLUEFRUIT_LE)\n    bluefruit_le_task();\n#endif\n}\n\nbool bluetooth_is_connected(void) {\n#if defined(BLUETOOTH_BLUEFRUIT_LE)\n    return bluefruit_le_is_connected();\n#else\n    // TODO: drivers should check if BT is connected here\n    return true;\n#endif\n}\n\nvoid bluetooth_send_keyboard(report_keyboard_t *report) {\n#if defined(BLUETOOTH_BLUEFRUIT_LE)\n    bluefruit_le_send_keyboard(report);\n#elif defined(BLUETOOTH_RN42)\n    rn42_send_keyboard(report);\n#endif\n}\n\nvoid bluetooth_send_mouse(report_mouse_t *report) {\n#if defined(BLUETOOTH_BLUEFRUIT_LE)\n    bluefruit_le_send_mouse(report);\n#elif defined(BLUETOOTH_RN42)\n    rn42_send_mouse(report);\n#endif\n}\n\nvoid bluetooth_send_consumer(uint16_t usage) {\n#if defined(BLUETOOTH_BLUEFRUIT_LE)\n    bluefruit_le_send_consumer(usage);\n#elif defined(BLUETOOTH_RN42)\n    rn42_send_consumer(usage);\n#endif\n}\n"
  },
  {
    "path": "drivers/bluetooth/outputselect.h",
    "content": "/*\nCopyright 2017 Priyadi Iman Nurcahyo\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include \"connection.h\"\n\n// DEPRECATED - DO NOT USE\n\n#define OUTPUT_AUTO CONNECTION_HOST_AUTO\n#define OUTPUT_NONE CONNECTION_HOST_NONE\n#define OUTPUT_USB CONNECTION_HOST_USB\n#define OUTPUT_BLUETOOTH CONNECTION_HOST_BLUETOOTH\n\n#define set_output connection_set_host_noeeprom\n#define where_to_send connection_get_host\n#define auto_detect_output connection_auto_detect_host\n\nvoid set_output_user(uint8_t output);\n"
  },
  {
    "path": "drivers/bluetooth/ringbuffer.hpp",
    "content": "#pragma once\n// A simple ringbuffer holding Size elements of type T\ntemplate <typename T, uint8_t Size>\nclass RingBuffer {\n protected:\n  T buf_[Size];\n  uint8_t head_{0}, tail_{0};\n public:\n  inline uint8_t nextPosition(uint8_t position) {\n    return (position + 1) % Size;\n  }\n\n  inline uint8_t prevPosition(uint8_t position) {\n    if (position == 0) {\n      return Size - 1;\n    }\n    return position - 1;\n  }\n\n  inline bool enqueue(const T &item) {\n    static_assert(Size > 1, \"RingBuffer size must be > 1\");\n    uint8_t next = nextPosition(head_);\n    if (next == tail_) {\n      // Full\n      return false;\n    }\n\n    buf_[head_] = item;\n    head_ = next;\n    return true;\n  }\n\n  inline bool get(T &dest, bool commit = true) {\n    auto tail = tail_;\n    if (tail == head_) {\n      // No more data\n      return false;\n    }\n\n    dest = buf_[tail];\n    tail = nextPosition(tail);\n\n    if (commit) {\n      tail_ = tail;\n    }\n    return true;\n  }\n\n  inline bool empty() const { return head_ == tail_; }\n\n  inline uint8_t size() const {\n    int diff = head_ - tail_;\n    if (diff >= 0) {\n      return diff;\n    }\n    return Size + diff;\n  }\n\n  inline T& front() {\n    return buf_[tail_];\n  }\n\n  inline bool peek(T &item) {\n    return get(item, false);\n  }\n};\n"
  },
  {
    "path": "drivers/bluetooth/rn42.c",
    "content": "/* Copyright 2021\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"rn42.h\"\n\n#include \"report.h\"\n#include \"uart.h\"\n\n#ifndef RN42_BAUD_RATE\n#    define RN42_BAUD_RATE 115200\n#endif\n\n// https://cdn.sparkfun.com/datasheets/Wireless/Bluetooth/bluetooth_cr_UG-v1.0r.pdf#G7.663734\nstatic inline uint16_t rn42_consumer_usage_to_bitmap(uint16_t usage) {\n    switch (usage) {\n        case AC_HOME:\n            return 0x0001;\n        case AL_EMAIL:\n            return 0x0002;\n        case AC_SEARCH:\n            return 0x0004;\n        case AL_KEYBOARD_LAYOUT:\n            return 0x0008;\n        case AUDIO_VOL_UP:\n            return 0x0010;\n        case AUDIO_VOL_DOWN:\n            return 0x0020;\n        case AUDIO_MUTE:\n            return 0x0040;\n        case TRANSPORT_PLAY_PAUSE:\n            return 0x0080;\n        case TRANSPORT_NEXT_TRACK:\n            return 0x0100;\n        case TRANSPORT_PREV_TRACK:\n            return 0x0200;\n        case TRANSPORT_STOP:\n            return 0x0400;\n        case TRANSPORT_EJECT:\n            return 0x0800;\n        case TRANSPORT_FAST_FORWARD:\n            return 0x1000;\n        case TRANSPORT_REWIND:\n            return 0x2000;\n        case TRANSPORT_STOP_EJECT:\n            return 0x4000;\n        case AL_LOCAL_BROWSER:\n            return 0x8000;\n        default:\n            return 0;\n    }\n}\n\nvoid rn42_init(void) {\n    uart_init(RN42_BAUD_RATE);\n}\n\nvoid rn42_send_keyboard(report_keyboard_t *report) {\n    uart_write(0xFD);\n    uart_write(0x09);\n    uart_write(0x01);\n\n    uart_write(report->mods);\n    uart_write(0x00);\n    uart_write(report->keys[0]);\n    uart_write(report->keys[1]);\n    uart_write(report->keys[2]);\n    uart_write(report->keys[3]);\n    uart_write(report->keys[4]);\n    uart_write(report->keys[5]);\n}\n\nvoid rn42_send_mouse(report_mouse_t *report) {\n    uart_write(0xFD);\n    uart_write(0x05);\n    uart_write(0x02);\n\n    uart_write(report->buttons);\n    uart_write(report->x);\n    uart_write(report->y);\n    uart_write(report->v);\n}\n\nvoid rn42_send_consumer(uint16_t usage) {\n    uint16_t bitmap = rn42_consumer_usage_to_bitmap(usage);\n\n    uart_write(0xFD);\n    uart_write(0x03);\n    uart_write(0x03);\n\n    uart_write(bitmap & 0xFF);\n    uart_write(bitmap >> 8);\n}\n"
  },
  {
    "path": "drivers/bluetooth/rn42.h",
    "content": "/* Copyright 2021\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <stdint.h>\n\n#include \"report.h\"\n\nvoid rn42_init(void);\n\nvoid rn42_send_keyboard(report_keyboard_t *report);\n\nvoid rn42_send_mouse(report_mouse_t *report);\n\nvoid rn42_send_consumer(uint16_t usage);\n"
  },
  {
    "path": "drivers/eeprom/eeprom_custom.c-template",
    "content": "/* Copyright 2019 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <stdint.h>\n#include <string.h>\n\n#include \"eeprom_driver.h\"\n\nvoid eeprom_driver_init(void) {\n    /* Any initialisation code */\n }\n\nvoid eeprom_driver_format(bool erase) {\n    /* If erase=false, then only do the absolute minimum initialisation necessary\n       to make sure that the eeprom driver is usable. It doesn't need to guarantee\n       that the content of the eeprom is reset to any particular value. For many\n       eeprom drivers this may be a no-op.\n\n       If erase=true, then in addition to making sure the eeprom driver is in a\n       usable state, also make sure that it is erased.\n     */\n}\n\nvoid eeprom_driver_erase(void) {\n    /* Wipe out the EEPROM, setting values to zero */\n}\n\nvoid eeprom_read_block(void *buf, const void *addr, size_t len) {\n    /*\n        Read a block of data:\n            buf: target buffer\n            addr: 0-based offset within the EEPROM\n            len: length to read\n     */\n}\n\nvoid eeprom_write_block(const void *buf, void *addr, size_t len) {\n    /*\n        Write a block of data:\n            buf: target buffer\n            addr: 0-based offset within the EEPROM\n            len: length to write\n     */\n}\n"
  },
  {
    "path": "drivers/eeprom/eeprom_driver.c",
    "content": "/* Copyright 2019 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <stdint.h>\n#include <string.h>\n\n#include \"eeprom_driver.h\"\n\nuint8_t eeprom_read_byte(const uint8_t *addr) {\n    uint8_t ret = 0;\n    eeprom_read_block(&ret, addr, 1);\n    return ret;\n}\n\nuint16_t eeprom_read_word(const uint16_t *addr) {\n    uint16_t ret = 0;\n    eeprom_read_block(&ret, addr, 2);\n    return ret;\n}\n\nuint32_t eeprom_read_dword(const uint32_t *addr) {\n    uint32_t ret = 0;\n    eeprom_read_block(&ret, addr, 4);\n    return ret;\n}\n\nvoid eeprom_write_byte(uint8_t *addr, uint8_t value) {\n    eeprom_write_block(&value, addr, 1);\n}\n\nvoid eeprom_write_word(uint16_t *addr, uint16_t value) {\n    eeprom_write_block(&value, addr, 2);\n}\n\nvoid eeprom_write_dword(uint32_t *addr, uint32_t value) {\n    eeprom_write_block(&value, addr, 4);\n}\n\nvoid eeprom_update_block(const void *buf, void *addr, size_t len) {\n    uint8_t read_buf[len];\n    eeprom_read_block(read_buf, addr, len);\n    if (memcmp(buf, read_buf, len) != 0) {\n        eeprom_write_block(buf, addr, len);\n    }\n}\n\nvoid eeprom_update_byte(uint8_t *addr, uint8_t value) {\n    uint8_t orig = eeprom_read_byte(addr);\n    if (orig != value) {\n        eeprom_write_byte(addr, value);\n    }\n}\n\nvoid eeprom_update_word(uint16_t *addr, uint16_t value) {\n    uint16_t orig = eeprom_read_word(addr);\n    if (orig != value) {\n        eeprom_write_word(addr, value);\n    }\n}\n\nvoid eeprom_update_dword(uint32_t *addr, uint32_t value) {\n    uint32_t orig = eeprom_read_dword(addr);\n    if (orig != value) {\n        eeprom_write_dword(addr, value);\n    }\n}\n\nvoid eeprom_driver_format(bool erase) __attribute__((weak));\nvoid eeprom_driver_format(bool erase) {\n    (void)erase; /* The default implementation assumes that the eeprom must be erased in order to be usable. */\n    eeprom_driver_erase();\n}\n"
  },
  {
    "path": "drivers/eeprom/eeprom_driver.h",
    "content": "/* Copyright 2019 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include \"eeprom.h\"\n\nvoid eeprom_driver_init(void);\nvoid eeprom_driver_format(bool erase);\nvoid eeprom_driver_erase(void);\n"
  },
  {
    "path": "drivers/eeprom/eeprom_i2c.c",
    "content": "/* Copyright 2019 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <stdint.h>\n#include <string.h>\n#if defined(EXTERNAL_EEPROM_WP_PIN)\n#    include \"gpio.h\"\n#endif\n\n/*\n    Note that the implementations of eeprom_XXXX_YYYY on AVR are normally\n    provided by avr-libc. The same functions are reimplemented below and are\n    rerouted to the external i2c equivalent.\n\n    Seemingly, as this is compiled from within QMK, the object file generated\n    during the build overrides the avr-libc implementation during the linking\n    stage.\n\n    On other platforms such as ARM, there are no provided implementations, so\n    there is nothing to override during linkage.\n*/\n\n#include \"wait.h\"\n#include \"i2c_master.h\"\n#include \"eeprom.h\"\n#include \"eeprom_driver.h\"\n#include \"eeprom_i2c.h\"\n\n// #define DEBUG_EEPROM_OUTPUT\n\n#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)\n#    include \"timer.h\"\n#    include \"debug.h\"\n#endif // DEBUG_EEPROM_OUTPUT\n\nstatic inline void fill_target_address(uint8_t *buffer, const void *addr) {\n    uintptr_t p = (uintptr_t)addr;\n    for (int i = 0; i < EXTERNAL_EEPROM_ADDRESS_SIZE; ++i) {\n        buffer[EXTERNAL_EEPROM_ADDRESS_SIZE - 1 - i] = p & 0xFF;\n        p >>= 8;\n    }\n}\n\nvoid eeprom_driver_init(void) {\n    i2c_init();\n#if defined(EXTERNAL_EEPROM_WP_PIN)\n    /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */\n    gpio_write_pin(EXTERNAL_EEPROM_WP_PIN, 1);\n    gpio_set_pin_input_high(EXTERNAL_EEPROM_WP_PIN);\n#endif\n}\n\nvoid eeprom_driver_format(bool erase) {\n    /* i2c eeproms do not need to be formatted before use */\n    if (erase) {\n        eeprom_driver_erase();\n    }\n}\n\nvoid eeprom_driver_erase(void) {\n#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)\n    uint32_t start = timer_read32();\n#endif\n\n    uint8_t buf[EXTERNAL_EEPROM_PAGE_SIZE];\n    memset(buf, 0x00, EXTERNAL_EEPROM_PAGE_SIZE);\n    for (uint32_t addr = 0; addr < EXTERNAL_EEPROM_BYTE_COUNT; addr += EXTERNAL_EEPROM_PAGE_SIZE) {\n        eeprom_write_block(buf, (void *)(uintptr_t)addr, EXTERNAL_EEPROM_PAGE_SIZE);\n    }\n\n#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)\n    dprintf(\"EEPROM erase took %ldms to complete\\n\", ((long)(timer_read32() - start)));\n#endif\n}\n\nvoid eeprom_read_block(void *buf, const void *addr, size_t len) {\n    uint8_t complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE];\n    fill_target_address(complete_packet, addr);\n\n    i2c_transmit(EXTERNAL_EEPROM_I2C_ADDRESS((uintptr_t)addr), complete_packet, EXTERNAL_EEPROM_ADDRESS_SIZE, 100);\n    i2c_receive(EXTERNAL_EEPROM_I2C_ADDRESS((uintptr_t)addr), buf, len, 100);\n\n#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)\n    dprintf(\"[EEPROM R] 0x%04X: \", ((int)addr));\n    for (size_t i = 0; i < len; ++i) {\n        dprintf(\" %02X\", (int)(((uint8_t *)buf)[i]));\n    }\n    dprintf(\"\\n\");\n#endif // DEBUG_EEPROM_OUTPUT\n}\n\nvoid eeprom_write_block(const void *buf, void *addr, size_t len) {\n    uint8_t   complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE + EXTERNAL_EEPROM_PAGE_SIZE];\n    uint8_t * read_buf    = (uint8_t *)buf;\n    uintptr_t target_addr = (uintptr_t)addr;\n\n#if defined(EXTERNAL_EEPROM_WP_PIN)\n    gpio_set_pin_output(EXTERNAL_EEPROM_WP_PIN);\n    gpio_write_pin(EXTERNAL_EEPROM_WP_PIN, 0);\n#endif\n\n    while (len > 0) {\n        uintptr_t page_offset  = target_addr % EXTERNAL_EEPROM_PAGE_SIZE;\n        int       write_length = EXTERNAL_EEPROM_PAGE_SIZE - page_offset;\n        if (write_length > len) {\n            write_length = len;\n        }\n\n        fill_target_address(complete_packet, (const void *)target_addr);\n        for (uint8_t i = 0; i < write_length; i++) {\n            complete_packet[EXTERNAL_EEPROM_ADDRESS_SIZE + i] = read_buf[i];\n        }\n\n#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)\n        dprintf(\"[EEPROM W] 0x%04X: \", ((int)target_addr));\n        for (uint8_t i = 0; i < write_length; i++) {\n            dprintf(\" %02X\", (int)(read_buf[i]));\n        }\n        dprintf(\"\\n\");\n#endif // DEBUG_EEPROM_OUTPUT\n\n        i2c_transmit(EXTERNAL_EEPROM_I2C_ADDRESS((uintptr_t)addr), complete_packet, EXTERNAL_EEPROM_ADDRESS_SIZE + write_length, 100);\n        wait_ms(EXTERNAL_EEPROM_WRITE_TIME);\n\n        read_buf += write_length;\n        target_addr += write_length;\n        len -= write_length;\n    }\n\n#if defined(EXTERNAL_EEPROM_WP_PIN)\n    /* We are setting the WP pin to high in a way that requires at least two bit-flips to change back to 0 */\n    gpio_write_pin(EXTERNAL_EEPROM_WP_PIN, 1);\n    gpio_set_pin_input_high(EXTERNAL_EEPROM_WP_PIN);\n#endif\n}\n"
  },
  {
    "path": "drivers/eeprom/eeprom_i2c.h",
    "content": "/* Copyright 2019 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n/*\n    Default device configurations:\n\n    For the Sparkfun Qwiic I2C EEPROM module: https://www.sparkfun.com/products/14764\n        #define EEPROM_I2C_CAT24C512 // (part number 24512A)\n        #define EEPROM_I2C_RM24C512C // (part number 24512C)\n\n    For the Sparkfun I2C EEPROM chip: https://www.sparkfun.com/products/525\n        #define EEPROM_I2C_24LC256\n\n    For the Adafruit I2C FRAM chip: https://www.adafruit.com/product/1895\n        #define EEPROM_I2C_MB85RC256V\n*/\n#if defined(EEPROM_I2C_CAT24C512)\n#    define EXTERNAL_EEPROM_BYTE_COUNT 65536\n#    define EXTERNAL_EEPROM_PAGE_SIZE 128\n#    define EXTERNAL_EEPROM_ADDRESS_SIZE 2\n#    define EXTERNAL_EEPROM_WRITE_TIME 5\n#elif defined(EEPROM_I2C_RM24C512C)\n#    define EXTERNAL_EEPROM_BYTE_COUNT 65536\n#    define EXTERNAL_EEPROM_PAGE_SIZE 128\n#    define EXTERNAL_EEPROM_ADDRESS_SIZE 2\n#    define EXTERNAL_EEPROM_WRITE_TIME 3\n#elif defined(EEPROM_I2C_24LC256)\n#    define EXTERNAL_EEPROM_BYTE_COUNT 32768\n#    define EXTERNAL_EEPROM_PAGE_SIZE 64\n#    define EXTERNAL_EEPROM_ADDRESS_SIZE 2\n#    define EXTERNAL_EEPROM_WRITE_TIME 5\n#elif defined(EEPROM_I2C_24LC128)\n#    define EXTERNAL_EEPROM_BYTE_COUNT 16384\n#    define EXTERNAL_EEPROM_PAGE_SIZE 64\n#    define EXTERNAL_EEPROM_ADDRESS_SIZE 2\n#    define EXTERNAL_EEPROM_WRITE_TIME 5\n#elif defined(EEPROM_I2C_24LC64)\n#    define EXTERNAL_EEPROM_BYTE_COUNT 8192\n#    define EXTERNAL_EEPROM_PAGE_SIZE 32\n#    define EXTERNAL_EEPROM_ADDRESS_SIZE 2\n#    define EXTERNAL_EEPROM_WRITE_TIME 5\n#elif defined(EEPROM_I2C_24LC32A)\n#    define EXTERNAL_EEPROM_BYTE_COUNT 4096\n#    define EXTERNAL_EEPROM_PAGE_SIZE 32\n#    define EXTERNAL_EEPROM_ADDRESS_SIZE 2\n#    define EXTERNAL_EEPROM_WRITE_TIME 5\n#elif defined(EEPROM_I2C_MB85RC256V)\n#    define EXTERNAL_EEPROM_BYTE_COUNT 32768\n#    define EXTERNAL_EEPROM_PAGE_SIZE 128\n#    define EXTERNAL_EEPROM_ADDRESS_SIZE 2\n#    define EXTERNAL_EEPROM_WRITE_TIME 0\n#endif\n\n/*\n    The base I2C address of the EEPROM.\n    This needs to be shifted up by 1, to match i2c_master requirements.\n*/\n#ifndef EXTERNAL_EEPROM_I2C_BASE_ADDRESS\n#    define EXTERNAL_EEPROM_I2C_BASE_ADDRESS 0b10100000\n#endif\n\n/*\n    The calculated I2C address based on the input memory location.\n\n    For EEPROM chips that embed part of the memory location in the I2C address\n    such as AT24M02 you can use something similar to the following (ensuring the\n    result is shifted by left by 1):\n\n    #define EXTERNAL_EEPROM_I2C_ADDRESS(loc) \\\n        (EXTERNAL_EEPROM_I2C_BASE_ADDRESS | ((((loc) >> 16) & 0x07) << 1))\n\n*/\n#ifndef EXTERNAL_EEPROM_I2C_ADDRESS\n#    define EXTERNAL_EEPROM_I2C_ADDRESS(loc) (EXTERNAL_EEPROM_I2C_BASE_ADDRESS)\n#endif\n\n/*\n    The total size of the EEPROM, in bytes. The EEPROM datasheet will usually\n    specify this value in kbits, and will require conversion to bytes.\n*/\n#ifndef EXTERNAL_EEPROM_BYTE_COUNT\n#    define EXTERNAL_EEPROM_BYTE_COUNT 8192\n#endif\n\n/*\n    The page size in bytes of the EEPROM, as specified in the datasheet.\n*/\n#ifndef EXTERNAL_EEPROM_PAGE_SIZE\n#    define EXTERNAL_EEPROM_PAGE_SIZE 32\n#endif\n\n/*\n    The address size in bytes of the EEPROM. For EEPROMs with <=256 bytes, this\n    will likely be 1. For EEPROMs >256 and <=65536, this will be 2. For EEPROMs\n    >65536, this will likely need to be 2 with the modified variant of\n    EXTERNAL_EEPROM_I2C_ADDRESS above.\n\n    As expected, consult the datasheet for specifics of your EEPROM.\n*/\n#ifndef EXTERNAL_EEPROM_ADDRESS_SIZE\n#    define EXTERNAL_EEPROM_ADDRESS_SIZE 2\n#endif\n\n/*\n    The write cycle time of the EEPROM in milliseconds, as specified in the\n    datasheet.\n*/\n#ifndef EXTERNAL_EEPROM_WRITE_TIME\n#    define EXTERNAL_EEPROM_WRITE_TIME 5\n#endif\n"
  },
  {
    "path": "drivers/eeprom/eeprom_spi.c",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <stdint.h>\n#include <string.h>\n\n/*\n    Note that the implementations of eeprom_XXXX_YYYY on AVR are normally\n    provided by avr-libc. The same functions are reimplemented below and are\n    rerouted to the external SPI equivalent.\n\n    Seemingly, as this is compiled from within QMK, the object file generated\n    during the build overrides the avr-libc implementation during the linking\n    stage.\n\n    On other platforms such as ARM, there are no provided implementations, so\n    there is nothing to override during linkage.\n*/\n\n#include \"wait.h\"\n#include \"debug.h\"\n#include \"timer.h\"\n#include \"spi_master.h\"\n#include \"eeprom.h\"\n#include \"eeprom_driver.h\"\n#include \"eeprom_spi.h\"\n\n#define CMD_WREN 6\n#define CMD_WRDI 4\n#define CMD_RDSR 5\n#define CMD_WRSR 1\n#define CMD_READ 3\n#define CMD_WRITE 2\n\n#define SR_WIP 0x01\n\n// #define DEBUG_EEPROM_OUTPUT\n\n#ifndef EXTERNAL_EEPROM_SPI_TIMEOUT\n#    define EXTERNAL_EEPROM_SPI_TIMEOUT 100\n#endif\n\nstatic bool spi_eeprom_start(void) {\n    return spi_start(EXTERNAL_EEPROM_SPI_SLAVE_SELECT_PIN, EXTERNAL_EEPROM_SPI_LSBFIRST, EXTERNAL_EEPROM_SPI_MODE, EXTERNAL_EEPROM_SPI_CLOCK_DIVISOR);\n}\n\nstatic spi_status_t spi_eeprom_wait_while_busy(int timeout) {\n    uint32_t     deadline = timer_read32() + timeout;\n    spi_status_t response = SR_WIP;\n    while (response & SR_WIP) {\n        if (!spi_eeprom_start()) {\n            return SPI_STATUS_ERROR;\n        }\n\n        spi_write(CMD_RDSR);\n        response = spi_read();\n        spi_stop();\n\n        if (timer_read32() >= deadline) {\n            return SPI_STATUS_TIMEOUT;\n        }\n    }\n    return SPI_STATUS_SUCCESS;\n}\n\nstatic void spi_eeprom_transmit_address(uintptr_t addr) {\n    uint8_t buffer[EXTERNAL_EEPROM_ADDRESS_SIZE];\n\n    for (int i = 0; i < EXTERNAL_EEPROM_ADDRESS_SIZE; ++i) {\n        buffer[EXTERNAL_EEPROM_ADDRESS_SIZE - 1 - i] = addr & 0xFF;\n        addr >>= 8;\n    }\n\n    spi_transmit(buffer, EXTERNAL_EEPROM_ADDRESS_SIZE);\n}\n\n//----------------------------------------------------------------------------------------------------------------------\n\nvoid eeprom_driver_init(void) {\n    spi_init();\n}\n\nvoid eeprom_driver_format(bool erase) {\n    /* spi eeproms do not need to be formatted before use */\n    if (erase) {\n        eeprom_driver_erase();\n    }\n}\n\nvoid eeprom_driver_erase(void) {\n#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)\n    uint32_t start = timer_read32();\n#endif\n\n    uint8_t buf[EXTERNAL_EEPROM_PAGE_SIZE];\n    memset(buf, 0x00, EXTERNAL_EEPROM_PAGE_SIZE);\n    for (uint32_t addr = 0; addr < EXTERNAL_EEPROM_BYTE_COUNT; addr += EXTERNAL_EEPROM_PAGE_SIZE) {\n        eeprom_write_block(buf, (void *)(uintptr_t)addr, EXTERNAL_EEPROM_PAGE_SIZE);\n    }\n\n#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)\n    dprintf(\"EEPROM erase took %ldms to complete\\n\", ((long)(timer_read32() - start)));\n#endif\n}\n\nvoid eeprom_read_block(void *buf, const void *addr, size_t len) {\n    //-------------------------------------------------\n    // Wait for the write-in-progress bit to be cleared\n    spi_status_t response = spi_eeprom_wait_while_busy(EXTERNAL_EEPROM_SPI_TIMEOUT);\n    if (response != SPI_STATUS_SUCCESS) {\n        spi_stop();\n        memset(buf, 0, len);\n        dprint(\"SPI timeout for WIP check\\n\");\n        return;\n    }\n\n    //-------------------------------------------------\n    // Perform read\n    bool res = spi_eeprom_start();\n    if (!res) {\n        spi_stop();\n        memset(buf, 0, len);\n        dprint(\"failed to start SPI for read\\n\");\n        return;\n    }\n\n    spi_write(CMD_READ);\n    spi_eeprom_transmit_address((uintptr_t)addr);\n    spi_receive(buf, len);\n\n#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)\n    dprintf(\"[EEPROM R] 0x%08lX: \", ((uint32_t)(uintptr_t)addr));\n    for (size_t i = 0; i < len; ++i) {\n        dprintf(\" %02X\", (int)(((uint8_t *)buf)[i]));\n    }\n    dprintf(\"\\n\");\n#endif // DEBUG_EEPROM_OUTPUT\n\n    spi_stop();\n}\n\nvoid eeprom_write_block(const void *buf, void *addr, size_t len) {\n    bool      res;\n    uint8_t * read_buf    = (uint8_t *)buf;\n    uintptr_t target_addr = (uintptr_t)addr;\n\n    while (len > 0) {\n        uintptr_t page_offset  = target_addr % EXTERNAL_EEPROM_PAGE_SIZE;\n        int       write_length = EXTERNAL_EEPROM_PAGE_SIZE - page_offset;\n        if (write_length > len) {\n            write_length = len;\n        }\n\n        //-------------------------------------------------\n        // Wait for the write-in-progress bit to be cleared\n        spi_status_t response = spi_eeprom_wait_while_busy(EXTERNAL_EEPROM_SPI_TIMEOUT);\n        if (response != SPI_STATUS_SUCCESS) {\n            spi_stop();\n            dprint(\"SPI timeout for WIP check\\n\");\n            return;\n        }\n\n        //-------------------------------------------------\n        // Enable writes\n        res = spi_eeprom_start();\n        if (!res) {\n            spi_stop();\n            dprint(\"failed to start SPI for write-enable\\n\");\n            return;\n        }\n\n        spi_write(CMD_WREN);\n        spi_stop();\n\n        //-------------------------------------------------\n        // Perform the write\n        res = spi_eeprom_start();\n        if (!res) {\n            spi_stop();\n            dprint(\"failed to start SPI for write\\n\");\n            return;\n        }\n\n#if defined(CONSOLE_ENABLE) && defined(DEBUG_EEPROM_OUTPUT)\n        dprintf(\"[EEPROM W] 0x%08lX: \", ((uint32_t)(uintptr_t)target_addr));\n        for (size_t i = 0; i < write_length; i++) {\n            dprintf(\" %02X\", (int)(uint8_t)(read_buf[i]));\n        }\n        dprintf(\"\\n\");\n#endif // DEBUG_EEPROM_OUTPUT\n\n        spi_write(CMD_WRITE);\n        spi_eeprom_transmit_address(target_addr);\n        spi_transmit(read_buf, write_length);\n        spi_stop();\n\n        read_buf += write_length;\n        target_addr += write_length;\n        len -= write_length;\n    }\n\n    //-------------------------------------------------\n    // Disable writes\n    res = spi_eeprom_start();\n    if (!res) {\n        dprint(\"failed to start SPI for write-disable\\n\");\n        return;\n    }\n\n    spi_write(CMD_WRDI);\n    spi_stop();\n}\n"
  },
  {
    "path": "drivers/eeprom/eeprom_spi.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n/*\n    Default device configurations:\n\n    For the Adafruit SPI Non-Volatile FRAM Breakout: https://www.adafruit.com/product/1897\n        #define EEPROM_SPI_MB85RS64V\n*/\n#if defined(EEPROM_SPI_MB85RS64V)\n#    define EXTERNAL_EEPROM_BYTE_COUNT 8192\n#    define EXTERNAL_EEPROM_PAGE_SIZE 64 // it's FRAM, so it doesn't actually matter, this just sets the RAM buffer\n#    define EXTERNAL_EEPROM_ADDRESS_SIZE 2\n#endif\n\n/*\n    The slave select pin of the EEPROM.\n    This needs to be a normal GPIO pin_t value, such as A7.\n*/\n#ifndef EXTERNAL_EEPROM_SPI_SLAVE_SELECT_PIN\n#    error \"No chip select pin defined -- missing EXTERNAL_EEPROM_SPI_SLAVE_SELECT_PIN\"\n#endif\n\n/*\n    The clock divisor for SPI to ensure that the MCU is within the\n    specifications of the EEPROM chip. Generally this will be PCLK divided by\n    the intended divisor -- check your clock settings and the datasheet of\n    your EEPROM.\n*/\n#ifndef EXTERNAL_EEPROM_SPI_CLOCK_DIVISOR\n#    ifdef __AVR__\n#        define EXTERNAL_EEPROM_SPI_CLOCK_DIVISOR 8\n#    else\n#        define EXTERNAL_EEPROM_SPI_CLOCK_DIVISOR 64\n#    endif\n#endif\n\n/*\n    The SPI mode to communicate with the EEPROM.\n*/\n#ifndef EXTERNAL_EEPROM_SPI_MODE\n#    define EXTERNAL_EEPROM_SPI_MODE 0\n#endif\n\n/*\n    Whether or not the SPI communication between the MCU and EEPROM should be\n    LSB-first.\n*/\n#ifndef EXTERNAL_EEPROM_SPI_LSBFIRST\n#    define EXTERNAL_EEPROM_SPI_LSBFIRST false\n#endif\n\n/*\n    The total size of the EEPROM, in bytes. The EEPROM datasheet will usually\n    specify this value in kbits, and will require conversion to bytes.\n*/\n#ifndef EXTERNAL_EEPROM_BYTE_COUNT\n#    define EXTERNAL_EEPROM_BYTE_COUNT 8192\n#endif\n\n/*\n    The page size in bytes of the EEPROM, as specified in the datasheet.\n*/\n#ifndef EXTERNAL_EEPROM_PAGE_SIZE\n#    define EXTERNAL_EEPROM_PAGE_SIZE 32\n#endif\n\n/*\n    The address size in bytes of the EEPROM. For EEPROMs with <=256 bytes, this\n    will likely be 1. For EEPROMs >256 and <=65536, this will be 2. For EEPROMs\n    >65536, this will likely need to be 4.\n\n    As expected, consult the datasheet for specifics of your EEPROM.\n*/\n#ifndef EXTERNAL_EEPROM_ADDRESS_SIZE\n#    define EXTERNAL_EEPROM_ADDRESS_SIZE 2\n#endif\n"
  },
  {
    "path": "drivers/eeprom/eeprom_transient.c",
    "content": "/* Copyright 2019 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <stdint.h>\n#include <string.h>\n\n#include \"eeprom_driver.h\"\n#include \"eeprom_transient.h\"\n\n__attribute__((aligned(4))) static uint8_t transientBuffer[TRANSIENT_EEPROM_SIZE] = {0};\n\nsize_t clamp_length(intptr_t offset, size_t len) {\n    if (offset + len > TRANSIENT_EEPROM_SIZE) {\n        len = TRANSIENT_EEPROM_SIZE - offset;\n    }\n\n    return len;\n}\n\nvoid eeprom_driver_init(void) {}\n\nvoid eeprom_driver_format(bool erase) {\n    /* The transient eeprom driver doesn't necessarily need to be formatted before use, and it always starts up filled with zeros, due to placement in the .bss section */\n    if (erase) {\n        eeprom_driver_erase();\n    }\n}\n\nvoid eeprom_driver_erase(void) {\n    memset(transientBuffer, 0x00, TRANSIENT_EEPROM_SIZE);\n}\n\nvoid eeprom_read_block(void *buf, const void *addr, size_t len) {\n    intptr_t offset = (intptr_t)addr;\n    memset(buf, 0x00, len);\n    len = clamp_length(offset, len);\n    if (len > 0) {\n        memcpy(buf, &transientBuffer[offset], len);\n    }\n}\n\nvoid eeprom_write_block(const void *buf, void *addr, size_t len) {\n    intptr_t offset = (intptr_t)addr;\n    len             = clamp_length(offset, len);\n    if (len > 0) {\n        memcpy(&transientBuffer[offset], buf, len);\n    }\n}\n"
  },
  {
    "path": "drivers/eeprom/eeprom_transient.h",
    "content": "/* Copyright 2019 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n/*\n    The size of the transient EEPROM buffer size.\n*/\n#ifndef TRANSIENT_EEPROM_SIZE\n#    include \"nvm_eeprom_eeconfig_internal.h\"\n#    define TRANSIENT_EEPROM_SIZE (((EECONFIG_SIZE + 3) / 4) * 4) // based off eeconfig's current usage, aligned to 4-byte sizes, to deal with LTO\n#endif\n"
  },
  {
    "path": "drivers/eeprom/eeprom_wear_leveling.c",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <stdint.h>\n#include <string.h>\n\n#include \"eeprom_driver.h\"\n#include \"wear_leveling.h\"\n\nvoid eeprom_driver_init(void) {\n    wear_leveling_init();\n}\n\nvoid eeprom_driver_format(bool erase) {\n    /* wear leveling requires the write log data structures to be erased before use. */\n    (void)erase;\n    eeprom_driver_erase();\n}\n\nvoid eeprom_driver_erase(void) {\n    wear_leveling_erase();\n}\n\nvoid eeprom_read_block(void *buf, const void *addr, size_t len) {\n    wear_leveling_read((uint32_t)addr, buf, len);\n}\n\nvoid eeprom_write_block(const void *buf, void *addr, size_t len) {\n    wear_leveling_write((uint32_t)addr, buf, len);\n}\n"
  },
  {
    "path": "drivers/encoder/encoder_quadrature.c",
    "content": "// Copyright 2018 Jack Humbert <jack.humb@gmail.com>\n// Copyright 2018-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <stdint.h>\n#include \"encoder.h\"\n#include \"gpio.h\"\n#include \"keyboard.h\"\n#include \"action.h\"\n#include \"keycodes.h\"\n#include \"wait.h\"\n\n#ifdef SPLIT_KEYBOARD\n#    include \"split_util.h\"\n#endif\n\n// for memcpy\n#include <string.h>\n\n#if !defined(ENCODER_RESOLUTIONS) && !defined(ENCODER_RESOLUTION)\n#    define ENCODER_RESOLUTION 4\n#endif\n\n#undef ENCODER_DEFAULT_PIN_API_IMPL\n#if defined(ENCODER_A_PINS) && defined(ENCODER_B_PINS)\n// Inform the quadrature driver that it needs to implement pin init/read functions\n#    define ENCODER_DEFAULT_PIN_API_IMPL\n#endif\n\nextern volatile bool isLeftHand;\n\n__attribute__((weak)) void    encoder_quadrature_init_pin(uint8_t index, bool pad_b);\n__attribute__((weak)) uint8_t encoder_quadrature_read_pin(uint8_t index, bool pad_b);\n\n#ifdef ENCODER_DEFAULT_PIN_API_IMPL\n\nstatic pin_t encoders_pad_a[NUM_ENCODERS_MAX_PER_SIDE] = ENCODER_A_PINS;\nstatic pin_t encoders_pad_b[NUM_ENCODERS_MAX_PER_SIDE] = ENCODER_B_PINS;\n\n__attribute__((weak)) void encoder_wait_pullup_charge(void) {\n    wait_us(100);\n}\n\n__attribute__((weak)) void encoder_quadrature_init_pin(uint8_t index, bool pad_b) {\n    pin_t pin = pad_b ? encoders_pad_b[index] : encoders_pad_a[index];\n    if (pin != NO_PIN) {\n        gpio_set_pin_input_high(pin);\n    }\n}\n\n__attribute__((weak)) uint8_t encoder_quadrature_read_pin(uint8_t index, bool pad_b) {\n    pin_t pin = pad_b ? encoders_pad_b[index] : encoders_pad_a[index];\n    if (pin != NO_PIN) {\n        return gpio_read_pin(pin) ? 1 : 0;\n    }\n    return 0;\n}\n\n#endif // ENCODER_DEFAULT_PIN_API_IMPL\n\n#ifdef ENCODER_RESOLUTIONS\nstatic uint8_t encoder_resolutions[NUM_ENCODERS] = ENCODER_RESOLUTIONS;\n#endif\n\n#ifndef ENCODER_DIRECTION_FLIP\n#    define ENCODER_CLOCKWISE true\n#    define ENCODER_COUNTER_CLOCKWISE false\n#else\n#    define ENCODER_CLOCKWISE false\n#    define ENCODER_COUNTER_CLOCKWISE true\n#endif\nstatic int8_t encoder_LUT[] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};\n\nstatic uint8_t encoder_state[NUM_ENCODERS]  = {0};\nstatic int8_t  encoder_pulses[NUM_ENCODERS] = {0};\n\n// encoder counts\nstatic uint8_t thisCount;\n#ifdef SPLIT_KEYBOARD\n// encoder offsets for each hand\nstatic uint8_t thisHand, thatHand;\n// encoder counts for each hand\nstatic uint8_t thatCount;\n#endif\n\n__attribute__((weak)) void encoder_quadrature_post_init_kb(void) {\n    extern void encoder_quadrature_handle_read(uint8_t index, uint8_t pin_a_state, uint8_t pin_b_state);\n    // Unused normally, but can be used for things like setting up pin-change interrupts in keyboard code.\n    // During the interrupt, read the pins then call `encoder_handle_read()` with the pin states and it'll queue up an encoder event if needed.\n}\n\nvoid encoder_quadrature_post_init(void) {\n#ifdef ENCODER_DEFAULT_PIN_API_IMPL\n    for (uint8_t i = 0; i < thisCount; i++) {\n        encoder_quadrature_init_pin(i, false);\n        encoder_quadrature_init_pin(i, true);\n    }\n    encoder_wait_pullup_charge();\n    for (uint8_t i = 0; i < thisCount; i++) {\n        encoder_state[i] = (encoder_quadrature_read_pin(i, false) << 0) | (encoder_quadrature_read_pin(i, true) << 1);\n    }\n#else\n    memset(encoder_state, 0, sizeof(encoder_state));\n#endif\n\n    encoder_quadrature_post_init_kb();\n}\n\nvoid encoder_driver_init(void) {\n#ifdef SPLIT_KEYBOARD\n    thisHand  = isLeftHand ? 0 : NUM_ENCODERS_LEFT;\n    thatHand  = NUM_ENCODERS_LEFT - thisHand;\n    thisCount = isLeftHand ? NUM_ENCODERS_LEFT : NUM_ENCODERS_RIGHT;\n    thatCount = isLeftHand ? NUM_ENCODERS_RIGHT : NUM_ENCODERS_LEFT;\n#else // SPLIT_KEYBOARD\n    thisCount                = NUM_ENCODERS;\n#endif\n\n#ifdef ENCODER_TESTS\n    // Annoying that we have to clear out values during initialisation here, but\n    // because all the arrays are static locals, rerunning tests in the same\n    // executable doesn't reset any of these. Kinda crappy having test-only code\n    // here, but it's the simplest solution.\n    memset(encoder_state, 0, sizeof(encoder_state));\n    memset(encoder_pulses, 0, sizeof(encoder_pulses));\n    const pin_t encoders_pad_a_left[] = ENCODER_A_PINS;\n    const pin_t encoders_pad_b_left[] = ENCODER_B_PINS;\n    for (uint8_t i = 0; i < thisCount; i++) {\n        encoders_pad_a[i] = encoders_pad_a_left[i];\n        encoders_pad_b[i] = encoders_pad_b_left[i];\n    }\n#endif\n\n#if defined(SPLIT_KEYBOARD) && defined(ENCODER_A_PINS_RIGHT) && defined(ENCODER_B_PINS_RIGHT)\n    // Re-initialise the pads if it's the right-hand side\n    if (!isLeftHand) {\n        const pin_t encoders_pad_a_right[] = ENCODER_A_PINS_RIGHT;\n        const pin_t encoders_pad_b_right[] = ENCODER_B_PINS_RIGHT;\n        for (uint8_t i = 0; i < thisCount; i++) {\n            encoders_pad_a[i] = encoders_pad_a_right[i];\n            encoders_pad_b[i] = encoders_pad_b_right[i];\n        }\n    }\n#endif // defined(SPLIT_KEYBOARD) && defined(ENCODER_A_PINS_RIGHT) && defined(ENCODER_B_PINS_RIGHT)\n\n    // Encoder resolutions is defined differently in config.h, so concatenate\n#if defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS)\n#    if defined(ENCODER_RESOLUTIONS_RIGHT)\n    static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS_RIGHT;\n#    else  // defined(ENCODER_RESOLUTIONS_RIGHT)\n    static const uint8_t encoder_resolutions_right[NUM_ENCODERS_RIGHT] = ENCODER_RESOLUTIONS;\n#    endif // defined(ENCODER_RESOLUTIONS_RIGHT)\n    for (uint8_t i = 0; i < NUM_ENCODERS_RIGHT; i++) {\n        encoder_resolutions[NUM_ENCODERS_LEFT + i] = encoder_resolutions_right[i];\n    }\n#endif // defined(SPLIT_KEYBOARD) && defined(ENCODER_RESOLUTIONS)\n\n    encoder_quadrature_post_init();\n}\n\nstatic void encoder_handle_state_change(uint8_t index, uint8_t state) {\n    uint8_t i = index;\n\n#ifdef SPLIT_KEYBOARD\n    index += thisHand;\n#endif\n\n#ifdef ENCODER_RESOLUTIONS\n    const uint8_t resolution = encoder_resolutions[index];\n#else\n    const uint8_t resolution = ENCODER_RESOLUTION;\n#endif\n\n    encoder_pulses[i] += encoder_LUT[state & 0xF];\n\n#ifdef ENCODER_DEFAULT_POS\n    if ((encoder_pulses[i] >= resolution) || (encoder_pulses[i] <= -resolution) || ((state & 0x3) == ENCODER_DEFAULT_POS)) {\n        if (encoder_pulses[i] >= 1) {\n#else\n    if (encoder_pulses[i] >= resolution) {\n#endif\n\n            encoder_queue_event(index, ENCODER_COUNTER_CLOCKWISE);\n        }\n\n#ifdef ENCODER_DEFAULT_POS\n        if (encoder_pulses[i] <= -1) {\n#else\n    if (encoder_pulses[i] <= -resolution) { // direction is arbitrary here, but this clockwise\n#endif\n            encoder_queue_event(index, ENCODER_CLOCKWISE);\n        }\n        encoder_pulses[i] %= resolution;\n#ifdef ENCODER_DEFAULT_POS\n        encoder_pulses[i] = 0;\n    }\n#endif\n}\n\nvoid encoder_quadrature_handle_read(uint8_t index, uint8_t pin_a_state, uint8_t pin_b_state) {\n    uint8_t state = pin_a_state | (pin_b_state << 1);\n    if ((encoder_state[index] & 0x3) != state) {\n        encoder_state[index] <<= 2;\n        encoder_state[index] |= state;\n        encoder_handle_state_change(index, encoder_state[index]);\n    }\n}\n\n__attribute__((weak)) void encoder_driver_task(void) {\n    for (uint8_t i = 0; i < thisCount; i++) {\n        encoder_quadrature_handle_read(i, encoder_quadrature_read_pin(i, false), encoder_quadrature_read_pin(i, true));\n    }\n}\n"
  },
  {
    "path": "drivers/flash/flash.h",
    "content": "// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdbool.h>\n\n/**\n * @brief The status of a flash operation.\n */\nenum {\n    FLASH_STATUS_SUCCESS     = 0,  //< The operation completed successfully.\n    FLASH_STATUS_ERROR       = -1, //< An error occurred during the operation.\n    FLASH_STATUS_TIMEOUT     = -2, //< The operation timed out.\n    FLASH_STATUS_BAD_ADDRESS = -3, //< The address is out of bounds.\n    FLASH_STATUS_BUSY        = -4, //< The flash is busy.\n};\n\n/**\n * @brief The status of a flash operation.\n */\ntypedef int16_t flash_status_t;\n\n/**\n * @brief Initializes the flash driver.\n *\n * This function initializes the flash driver and prepares it for use.\n * It should be called before any other flash-related functions are used.\n */\nvoid flash_init(void);\n\n/**\n * @brief Checks if the flash is busy.\n *\n * This function checks if the flash is currently busy with an operation.\n *\n * @return FLASH_STATUS_SUCCESS if the flash is not busy, FLASH_STATUS_BUSY if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.\n */\nflash_status_t flash_is_busy(void);\n\n/**\n * @brief Initiates a chip erase operation.\n *\n * This function does not wait for the flash to become ready.\n *\n * @return FLASH_STATUS_SUCCESS if the erase command was successfully sent, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.\n */\nflash_status_t flash_begin_erase_chip(void);\n\n/**\n * @brief Waits for the chip erase operation to complete.\n *\n * This function waits for the chip erase operation to complete.\n *\n * @return FLASH_STATUS_SUCCESS if the chip erase operation completed successfully, FLASH_STATUS_TIMEOUT if the flash was still busy, or FLASH_STATUS_ERROR if an error occurred.\n */\nflash_status_t flash_wait_erase_chip(void);\n\n/**\n * @brief Erases the entire flash memory chip.\n *\n * This function initiates an erase operation to erase the entire flash memory chip.\n * It waits for the operation to complete.\n *\n * @return FLASH_STATUS_SUCCESS if the erase was successfully executed, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.\n */\nflash_status_t flash_erase_chip(void);\n\n/**\n * @brief Erases a block of flash memory.\n *\n * This function initiates an erase operation to erase a block of flash memory.\n * It waits for the operation to complete.\n *\n * @param addr The address of the block to erase.\n *\n * @return FLASH_STATUS_SUCCESS if the erase was successfully executed, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.\n */\nflash_status_t flash_erase_block(uint32_t addr);\n\n/**\n * @brief Erases a sector of flash memory.\n *\n * This function initiates an erase operation to erase a sector of flash memory.\n * It waits for the operation to complete.\n *\n * @param addr The address of the sector to erase.\n *\n * @return FLASH_STATUS_SUCCESS if the erase was successfully executed, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.\n */\nflash_status_t flash_erase_sector(uint32_t addr);\n\n/**\n * @brief Reads a range of flash memory.\n *\n * This function reads a range of flash memory into a buffer.\n *\n * @param addr The address of the range to read.\n * @param buf A pointer to the buffer to read the range into.\n * @param len The length of the range to read.\n *\n * @return FLASH_STATUS_SUCCESS if the range was successfully read, FLASH_STATUS_BAD_ADDRESS if the address is out of bounds, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.\n */\nflash_status_t flash_read_range(uint32_t addr, void *buf, size_t len);\n\n/**\n * @brief Writes a range of flash memory.\n *\n * This function writes a range of flash memory from a buffer.\n *\n * @param addr The address of the range to write.\n * @param buf A pointer to the buffer to write to the range.\n * @param len The length of the range to write.\n *\n * @return FLASH_STATUS_SUCCESS if the range was successfully written, FLASH_STATUS_BAD_ADDRESS if the address is out of bounds, FLASH_STATUS_TIMEOUT if the flash is busy, or FLASH_STATUS_ERROR if an error occurred.\n */\nflash_status_t flash_write_range(uint32_t addr, const void *buf, size_t len);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "drivers/flash/flash_spi.c",
    "content": "// Copyright 2021 Westberry Technology (ChangZhou) Corp., Ltd\n// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <string.h>\n\n#include \"flash.h\"\n#include \"util.h\"\n#include \"wait.h\"\n#include \"debug.h\"\n#include \"timer.h\"\n#include \"flash_spi.h\"\n#include \"spi_master.h\"\n\n/*\n    The time-out time of spi flash transmission.\n*/\n#ifndef EXTERNAL_FLASH_SPI_TIMEOUT\n#    define EXTERNAL_FLASH_SPI_TIMEOUT 1000\n#endif\n\n/* ID comands */\n#define FLASH_CMD_RDID 0x9F /* RDID (Read Identification) */\n#define FLASH_CMD_RES 0xAB  /* RES (Read Electronic ID) */\n#define FLASH_CMD_REMS 0x90 /* REMS (Read Electronic & Device ID) */\n\n/* register comands */\n#define FLASH_CMD_WRSR 0x01 /* WRSR (Write Status register) */\n#define FLASH_CMD_RDSR 0x05 /* RDSR (Read Status register) */\n\n/* READ comands */\n#define FLASH_CMD_READ 0x03     /* READ (1 x I/O) */\n#define FLASH_CMD_FASTREAD 0x0B /* FAST READ (Fast read data) */\n#define FLASH_CMD_DREAD 0x3B    /* DREAD (1In/2 Out fast read) */\n\n/* Program comands */\n#define FLASH_CMD_WREN 0x06 /* WREN (Write Enable) */\n#define FLASH_CMD_WRDI 0x04 /* WRDI (Write Disable) */\n#define FLASH_CMD_PP 0x02   /* PP (page program) */\n\n/* Erase comands */\n#define FLASH_CMD_SE 0x20 /* SE (Sector Erase) */\n#define FLASH_CMD_BE 0xD8 /* BE (Block Erase) */\n#define FLASH_CMD_CE 0x60 /* CE (Chip Erase) hex code: 60 or C7 */\n\n/* Mode setting comands */\n#define FLASH_CMD_DP 0xB9  /* DP (Deep Power Down) */\n#define FLASH_CMD_RDP 0xAB /* RDP (Release from Deep Power Down) */\n\n/* Status register */\n#define FLASH_FLAG_WIP 0x01 /* Write in progress bit */\n#define FLASH_FLAG_WEL 0x02 /* Write enable latch bit */\n\n// #define DEBUG_FLASH_SPI_OUTPUT\n\nstatic bool spi_flash_start(void) {\n    return spi_start(EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN, EXTERNAL_FLASH_SPI_LSBFIRST, EXTERNAL_FLASH_SPI_MODE, EXTERNAL_FLASH_SPI_CLOCK_DIVISOR);\n}\n\nstatic flash_status_t spi_flash_wait_while_busy_multiplier(int multiplier) {\n    flash_status_t response = FLASH_STATUS_SUCCESS;\n    uint32_t       deadline = timer_read32() + ((EXTERNAL_FLASH_SPI_TIMEOUT)*multiplier);\n    do {\n        if (timer_read32() >= deadline) {\n            response = FLASH_STATUS_TIMEOUT;\n            break;\n        }\n\n        response = flash_is_busy();\n    } while (response == FLASH_STATUS_BUSY);\n    return response;\n}\n\nstatic flash_status_t spi_flash_wait_while_busy(void) {\n    return spi_flash_wait_while_busy_multiplier(1);\n}\n\nflash_status_t flash_is_busy(void) {\n    bool res = spi_flash_start();\n    if (!res) {\n        dprint(\"Failed to start SPI! [spi flash wait while busy]\\n\");\n        return FLASH_STATUS_ERROR;\n    }\n\n    spi_write(FLASH_CMD_RDSR);\n    spi_status_t status = spi_read();\n    spi_stop();\n\n    if (status < 0) {\n        return status;\n    }\n\n    uint8_t sr = (uint8_t)status;\n    return (sr & FLASH_FLAG_WIP) ? FLASH_STATUS_BUSY : FLASH_STATUS_SUCCESS;\n}\n\nstatic flash_status_t spi_flash_write_enable(void) {\n    bool res = spi_flash_start();\n    if (!res) {\n        dprint(\"Failed to start SPI! [spi flash write enable]\\n\");\n        return FLASH_STATUS_ERROR;\n    }\n\n    spi_write(FLASH_CMD_WREN);\n    spi_stop();\n\n    return FLASH_STATUS_SUCCESS;\n}\n\nstatic flash_status_t spi_flash_write_disable(void) {\n    bool res = spi_flash_start();\n    if (!res) {\n        dprint(\"Failed to start SPI! [spi flash write disable]\\n\");\n        return FLASH_STATUS_ERROR;\n    }\n\n    spi_write(FLASH_CMD_WRDI);\n    spi_stop();\n\n    return FLASH_STATUS_SUCCESS;\n}\n\n/* This function is used for read transfer, write transfer and erase transfer. */\nstatic flash_status_t spi_flash_transaction(uint8_t cmd, uint32_t addr, uint8_t *data, size_t len) {\n    flash_status_t response = FLASH_STATUS_SUCCESS;\n    uint8_t        buffer[EXTERNAL_FLASH_ADDRESS_SIZE + 1];\n\n    buffer[0] = cmd;\n    for (int i = 0; i < EXTERNAL_FLASH_ADDRESS_SIZE; ++i) {\n        buffer[EXTERNAL_FLASH_ADDRESS_SIZE - i] = addr & 0xFF;\n        addr >>= 8;\n    }\n\n    bool res = spi_flash_start();\n    if (!res) {\n        dprint(\"Failed to start SPI! [spi flash transmit]\\n\");\n        return FLASH_STATUS_ERROR;\n    }\n\n    response = spi_transmit(buffer, sizeof(buffer));\n\n    if ((!response) && (data != NULL)) {\n        switch (cmd) {\n            case FLASH_CMD_READ:\n                response = spi_receive(data, len);\n                break;\n            case FLASH_CMD_PP:\n                response = spi_transmit(data, len);\n                break;\n            default:\n                response = FLASH_STATUS_ERROR;\n                break;\n        }\n    }\n\n    spi_stop();\n\n    return response;\n}\n\nvoid flash_init(void) {\n    spi_init();\n}\n\nflash_status_t flash_begin_erase_chip(void) {\n    flash_status_t response = FLASH_STATUS_SUCCESS;\n\n    /* Wait for the write-in-progress bit to be cleared. */\n    response = spi_flash_wait_while_busy();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to check WIP flag! [spi flash erase chip]\\n\");\n        return response;\n    }\n\n    /* Enable writes. */\n    response = spi_flash_write_enable();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to write-enable! [spi flash erase chip]\\n\");\n        return response;\n    }\n\n    /* Erase Chip. */\n    bool res = spi_flash_start();\n    if (!res) {\n        dprint(\"Failed to start SPI! [spi flash erase chip]\\n\");\n        return FLASH_STATUS_ERROR;\n    }\n    spi_write(FLASH_CMD_CE);\n    spi_stop();\n    return FLASH_STATUS_SUCCESS;\n}\n\nflash_status_t flash_wait_erase_chip(void) {\n    flash_status_t response = spi_flash_wait_while_busy_multiplier(250); // Chip erase can take a long time, wait 250x the usual timeout\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to check WIP flag! [spi flash erase chip]\\n\");\n        return response;\n    }\n    return response;\n}\n\nflash_status_t flash_erase_chip(void) {\n    flash_status_t response = flash_begin_erase_chip();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to begin erase chip! [spi flash erase chip]\\n\");\n        return response;\n    }\n\n    return flash_wait_erase_chip();\n}\n\nflash_status_t flash_erase_sector(uint32_t addr) {\n    flash_status_t response = FLASH_STATUS_SUCCESS;\n\n    /* Check that the address exceeds the limit. */\n    if ((addr + (EXTERNAL_FLASH_SECTOR_SIZE)) >= (EXTERNAL_FLASH_SIZE) || ((addr % (EXTERNAL_FLASH_SECTOR_SIZE)) != 0)) {\n        dprintf(\"Flash erase sector address over limit! [addr:0x%lx]\\n\", (uint32_t)addr);\n        return FLASH_STATUS_ERROR;\n    }\n\n    /* Wait for the write-in-progress bit to be cleared. */\n    response = spi_flash_wait_while_busy();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to check WIP flag! [spi flash erase sector]\\n\");\n        return response;\n    }\n\n    /* Enable writes. */\n    response = spi_flash_write_enable();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to write-enable! [spi flash erase sector]\\n\");\n        return response;\n    }\n\n    /* Erase Sector. */\n    response = spi_flash_transaction(FLASH_CMD_SE, addr, NULL, 0);\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to erase sector! [spi flash erase sector]\\n\");\n        return response;\n    }\n\n    /* Wait for the write-in-progress bit to be cleared.*/\n    response = spi_flash_wait_while_busy();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to check WIP flag! [spi flash erase sector]\\n\");\n        return response;\n    }\n\n    return response;\n}\n\nflash_status_t flash_erase_block(uint32_t addr) {\n    flash_status_t response = FLASH_STATUS_SUCCESS;\n\n    /* Check that the address exceeds the limit. */\n    if ((addr + (EXTERNAL_FLASH_BLOCK_SIZE)) >= (EXTERNAL_FLASH_SIZE) || ((addr % (EXTERNAL_FLASH_BLOCK_SIZE)) != 0)) {\n        dprintf(\"Flash erase block address over limit! [addr:0x%lx]\\n\", (uint32_t)addr);\n        return FLASH_STATUS_ERROR;\n    }\n\n    /* Wait for the write-in-progress bit to be cleared. */\n    response = spi_flash_wait_while_busy();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to check WIP flag! [spi flash erase block]\\n\");\n        return response;\n    }\n\n    /* Enable writes. */\n    response = spi_flash_write_enable();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to write-enable! [spi flash erase block]\\n\");\n        return response;\n    }\n\n    /* Erase Block. */\n    response = spi_flash_transaction(FLASH_CMD_BE, addr, NULL, 0);\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to erase block! [spi flash erase block]\\n\");\n        return response;\n    }\n\n    /* Wait for the write-in-progress bit to be cleared.*/\n    response = spi_flash_wait_while_busy();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to check WIP flag! [spi flash erase block]\\n\");\n        return response;\n    }\n\n    return response;\n}\n\nflash_status_t flash_read_range(uint32_t addr, void *buf, size_t len) {\n    flash_status_t response = FLASH_STATUS_SUCCESS;\n    uint8_t *      read_buf = (uint8_t *)buf;\n\n    /* Wait for the write-in-progress bit to be cleared. */\n    response = spi_flash_wait_while_busy();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to check WIP flag! [spi flash read block]\\n\");\n        memset(read_buf, 0, len);\n        return response;\n    }\n\n    /* Perform read. */\n    response = spi_flash_transaction(FLASH_CMD_READ, addr, read_buf, len);\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to read block! [spi flash read block]\\n\");\n        memset(read_buf, 0, len);\n        return response;\n    }\n\n#if defined(CONSOLE_ENABLE) && defined(DEBUG_FLASH_SPI_OUTPUT)\n    dprintf(\"[SPI FLASH R] 0x%08lx: \", addr);\n    for (size_t i = 0; i < len; ++i) {\n        dprintf(\" %02X\", (int)(((uint8_t *)read_buf)[i]));\n    }\n    dprintf(\"\\n\");\n#endif // DEBUG_FLASH_SPI_OUTPUT\n\n    return response;\n}\n\nflash_status_t flash_write_range(uint32_t addr, const void *buf, size_t len) {\n    flash_status_t response  = FLASH_STATUS_SUCCESS;\n    uint8_t *      write_buf = (uint8_t *)buf;\n\n    while (len > 0) {\n        uint32_t page_offset  = addr % EXTERNAL_FLASH_PAGE_SIZE;\n        size_t   write_length = EXTERNAL_FLASH_PAGE_SIZE - page_offset;\n        if (write_length > len) {\n            write_length = len;\n        }\n\n        /* Wait for the write-in-progress bit to be cleared. */\n        response = spi_flash_wait_while_busy();\n        if (response != FLASH_STATUS_SUCCESS) {\n            dprint(\"Failed to check WIP flag! [spi flash write block]\\n\");\n            return response;\n        }\n\n        /* Enable writes. */\n        response = spi_flash_write_enable();\n        if (response != FLASH_STATUS_SUCCESS) {\n            dprint(\"Failed to write-enable! [spi flash write block]\\n\");\n            return response;\n        }\n\n#if defined(CONSOLE_ENABLE) && defined(DEBUG_FLASH_SPI_OUTPUT)\n        dprintf(\"[SPI FLASH W] 0x%08lx: \", addr);\n        for (size_t i = 0; i < write_length; i++) {\n            dprintf(\" %02X\", (int)(uint8_t)(write_buf[i]));\n        }\n        dprintf(\"\\n\");\n#endif // DEBUG_FLASH_SPI_OUTPUT\n\n        /* Perform the write. */\n        response = spi_flash_transaction(FLASH_CMD_PP, addr, write_buf, write_length);\n        if (response != FLASH_STATUS_SUCCESS) {\n            dprint(\"Failed to write block! [spi flash write block]\\n\");\n            return response;\n        }\n\n        write_buf += write_length;\n        addr += write_length;\n        len -= write_length;\n    }\n\n    /* Wait for the write-in-progress bit to be cleared. */\n    response = spi_flash_wait_while_busy();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to check WIP flag! [spi flash write block]\\n\");\n        return response;\n    }\n\n    /* Disable writes. */\n    response = spi_flash_write_disable();\n    if (response != FLASH_STATUS_SUCCESS) {\n        dprint(\"Failed to write-disable! [spi flash write block]\\n\");\n        return response;\n    }\n\n    return response;\n}\n"
  },
  {
    "path": "drivers/flash/flash_spi.h",
    "content": "/*\nCopyright (C) 2021 Westberry Technology (ChangZhou) Corp., Ltd\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include \"flash.h\"\n\n/* All the following default configurations are based on MX25L4006E Nor FLASH. */\n\n/*\n    The slave select pin of the FLASH.\n    This needs to be a normal GPIO pin_t value, such as B14.\n*/\n#ifndef EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN\n#    error \"No chip select pin defined -- missing EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN\"\n#endif\n\n/*\n    The clock divisor for SPI to ensure that the MCU is within the\n    specifications of the FLASH chip. Generally this will be PCLK divided by\n    the intended divisor -- check your clock settings and the datasheet of\n    your FLASH.\n*/\n#ifndef EXTERNAL_FLASH_SPI_CLOCK_DIVISOR\n#    ifdef __AVR__\n#        define EXTERNAL_FLASH_SPI_CLOCK_DIVISOR 4\n#    else\n#        define EXTERNAL_FLASH_SPI_CLOCK_DIVISOR 8\n#    endif\n#endif\n\n/*\n    The SPI mode to communicate with the FLASH.\n*/\n#ifndef EXTERNAL_FLASH_SPI_MODE\n#    define EXTERNAL_FLASH_SPI_MODE 0\n#endif\n\n/*\n    Whether or not the SPI communication between the MCU and FLASH should be\n    LSB-first.\n*/\n#ifndef EXTERNAL_FLASH_SPI_LSBFIRST\n#    define EXTERNAL_FLASH_SPI_LSBFIRST false\n#endif\n\n/*\n    The Flash address size in bytes, as specified in datasheet.\n*/\n#ifndef EXTERNAL_FLASH_ADDRESS_SIZE\n#    define EXTERNAL_FLASH_ADDRESS_SIZE 3\n#endif\n\n/*\n    The page size of the FLASH in bytes, as specified in the datasheet.\n*/\n#ifndef EXTERNAL_FLASH_PAGE_SIZE\n#    define EXTERNAL_FLASH_PAGE_SIZE 256\n#endif\n\n/*\n    The sector size of the FLASH in bytes, as specified in the datasheet.\n*/\n#ifndef EXTERNAL_FLASH_SECTOR_SIZE\n#    define EXTERNAL_FLASH_SECTOR_SIZE (4 * 1024L)\n#endif\n\n/*\n    The block size of the FLASH in bytes, as specified in the datasheet.\n*/\n#ifndef EXTERNAL_FLASH_BLOCK_SIZE\n#    define EXTERNAL_FLASH_BLOCK_SIZE (64 * 1024L)\n#endif\n\n/*\n    The total size of the FLASH in bytes, as specified in the datasheet.\n*/\n#ifndef EXTERNAL_FLASH_SIZE\n#    define EXTERNAL_FLASH_SIZE (512 * 1024L)\n#endif\n\n/*\n    The block count of the FLASH, calculated by total FLASH size and block size.\n*/\n#define EXTERNAL_FLASH_BLOCK_COUNT ((EXTERNAL_FLASH_SIZE) / (EXTERNAL_FLASH_BLOCK_SIZE))\n\n/*\n    The sector count of the FLASH, calculated by total FLASH size and sector size.\n*/\n#define EXTERNAL_FLASH_SECTOR_COUNT ((EXTERNAL_FLASH_SIZE) / (EXTERNAL_FLASH_SECTOR_SIZE))\n\n/*\n    The page count of the FLASH, calculated by total FLASH size and page size.\n*/\n#define EXTERNAL_FLASH_PAGE_COUNT ((EXTERNAL_FLASH_SIZE) / (EXTERNAL_FLASH_PAGE_SIZE))\n"
  },
  {
    "path": "drivers/gpio/mcp23018.c",
    "content": "// Copyright 2022 zvecr<git@zvecr.com>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"mcp23018.h\"\n#include \"i2c_master.h\"\n#include \"wait.h\"\n#include \"debug.h\"\n\n#define SLAVE_TO_ADDR(n) (n << 1)\n#ifndef MCP23018_TIMEOUT\n#    define MCP23018_TIMEOUT 100\n#endif // MCP23018_TIMEOUT\n\nenum {\n    CMD_IODIRA = 0x00, // i/o direction register\n    CMD_IODIRB = 0x01,\n    CMD_GPPUA  = 0x0C, // GPIO pull-up resistor register\n    CMD_GPPUB  = 0x0D,\n    CMD_GPIOA  = 0x12, // general purpose i/o port register (write modifies OLAT)\n    CMD_GPIOB  = 0x13,\n};\n\nvoid mcp23018_init(uint8_t addr) {\n    static uint8_t s_init = 0;\n    if (!s_init) {\n        i2c_init();\n        wait_ms(1000);\n\n        s_init = 1;\n    }\n}\n\nbool mcp23018_set_config(uint8_t slave_addr, mcp23018_port_t port, uint8_t conf) {\n    uint8_t addr         = SLAVE_TO_ADDR(slave_addr);\n    uint8_t cmdDirection = port ? CMD_IODIRB : CMD_IODIRA;\n    uint8_t cmdPullup    = port ? CMD_GPPUB : CMD_GPPUA;\n\n    i2c_status_t ret = i2c_write_register(addr, cmdDirection, &conf, sizeof(conf), MCP23018_TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        dprintf(\"mcp23018_set_config::directionFAILED::%u\\n\", ret);\n        return false;\n    }\n\n    ret = i2c_write_register(addr, cmdPullup, &conf, sizeof(conf), MCP23018_TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        dprintf(\"mcp23018_set_config::pullupFAILED::%u\\n\", ret);\n        return false;\n    }\n\n    return true;\n}\n\nbool mcp23018_set_output(uint8_t slave_addr, mcp23018_port_t port, uint8_t conf) {\n    uint8_t addr = SLAVE_TO_ADDR(slave_addr);\n    uint8_t cmd  = port ? CMD_GPIOB : CMD_GPIOA;\n\n    i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), MCP23018_TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        dprintf(\"mcp23018_set_output::FAILED::%u\\n\", ret);\n        return false;\n    }\n\n    return true;\n}\n\nbool mcp23018_set_output_all(uint8_t slave_addr, uint8_t confA, uint8_t confB) {\n    uint8_t addr    = SLAVE_TO_ADDR(slave_addr);\n    uint8_t conf[2] = {confA, confB};\n\n    i2c_status_t ret = i2c_write_register(addr, CMD_GPIOA, &conf[0], sizeof(conf), MCP23018_TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        dprintf(\"mcp23018_set_output::FAILED::%u\\n\", ret);\n        return false;\n    }\n\n    return true;\n}\n\nbool mcp23018_read_pins(uint8_t slave_addr, mcp23018_port_t port, uint8_t* out) {\n    uint8_t addr = SLAVE_TO_ADDR(slave_addr);\n    uint8_t cmd  = port ? CMD_GPIOB : CMD_GPIOA;\n\n    i2c_status_t ret = i2c_read_register(addr, cmd, out, sizeof(uint8_t), MCP23018_TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        dprintf(\"mcp23018_read_pins::FAILED::%u\\n\", ret);\n        return false;\n    }\n\n    return true;\n}\n\nbool mcp23018_read_pins_all(uint8_t slave_addr, uint16_t* out) {\n    uint8_t addr = SLAVE_TO_ADDR(slave_addr);\n\n    typedef union {\n        uint8_t  u8[2];\n        uint16_t u16;\n    } data16;\n\n    data16 data = {.u16 = 0};\n\n    i2c_status_t ret = i2c_read_register(addr, CMD_GPIOA, &data.u8[0], sizeof(data), MCP23018_TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        dprintf(\"mcp23018_read_pins_all::FAILED::%u\\n\", ret);\n        return false;\n    }\n\n    *out = data.u16;\n    return true;\n}\n"
  },
  {
    "path": "drivers/gpio/mcp23018.h",
    "content": "// Copyright 2022 zvecr<git@zvecr.com>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n/**\n * Port ID\n */\ntypedef enum {\n    mcp23018_PORTA,\n    mcp23018_PORTB,\n} mcp23018_port_t;\n\n/**\n * Helpers for set_config\n */\nenum {\n    ALL_OUTPUT = 0,\n    ALL_INPUT  = 0xFF,\n};\n\n/**\n * Helpers for set_output\n */\nenum {\n    ALL_LOW  = 0,\n    ALL_HIGH = 0xFF,\n};\n\n/**\n * Init expander and any other dependent drivers\n */\nvoid mcp23018_init(uint8_t slave_addr);\n\n/**\n * Configure input/output to a given port\n */\nbool mcp23018_set_config(uint8_t slave_addr, mcp23018_port_t port, uint8_t conf);\n\n/**\n * Write high/low to a given port\n */\nbool mcp23018_set_output(uint8_t slave_addr, mcp23018_port_t port, uint8_t conf);\n\n/**\n * Write high/low to both ports sequentially\n *\n *  - slightly faster than multiple set_output\n */\nbool mcp23018_set_output_all(uint8_t slave_addr, uint8_t confA, uint8_t confB);\n\n/**\n * Read state of a given port\n */\nbool mcp23018_read_pins(uint8_t slave_addr, mcp23018_port_t port, uint8_t* ret);\n\n/**\n * Read state of both ports sequentially\n *\n *  - slightly faster than multiple readPins\n */\nbool mcp23018_read_pins_all(uint8_t slave_addr, uint16_t* ret);\n\n// DEPRECATED - DO NOT USE\n\n#define mcp23018_readPins mcp23018_read_pins\n#define mcp23018_readPins_all mcp23018_read_pins_all\n"
  },
  {
    "path": "drivers/gpio/pca9505.c",
    "content": "// Copyright 2022 nirim000\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"i2c_master.h\"\n#include \"pca9505.h\"\n\n#include \"debug.h\"\n\n#define SLAVE_TO_ADDR(n) (n << 1)\n#define TIMEOUT 100\n\nenum {\n    CMD_INPUT_0 = 0,\n    CMD_INPUT_1,\n    CMD_INPUT_2,\n    CMD_INPUT_3,\n    CMD_INPUT_4,\n    CMD_OUTPUT_0 = 8,\n    CMD_OUTPUT_1,\n    CMD_OUTPUT_2,\n    CMD_OUTPUT_3,\n    CMD_OUTPUT_4,\n    CMD_INVERSION_0 = 16,\n    CMD_INVERSION_1,\n    CMD_INVERSION_2,\n    CMD_INVERSION_3,\n    CMD_INVERSION_4,\n    CMD_CONFIG_0 = 24,\n    CMD_CONFIG_1,\n    CMD_CONFIG_2,\n    CMD_CONFIG_3,\n    CMD_CONFIG_4,\n};\n\nvoid pca9505_init(uint8_t slave_addr) {\n    static uint8_t s_init = 0;\n    if (!s_init) {\n        i2c_init();\n\n        s_init = 1;\n    }\n\n    // TODO: could check device connected\n}\n\nbool pca9505_set_config(uint8_t slave_addr, pca9505_port_t port, uint8_t conf) {\n    uint8_t addr = SLAVE_TO_ADDR(slave_addr);\n    uint8_t cmd  = 0;\n    switch (port) {\n        case 0:\n            cmd = CMD_CONFIG_0;\n            break;\n        case 1:\n            cmd = CMD_CONFIG_1;\n            break;\n        case 2:\n            cmd = CMD_CONFIG_2;\n            break;\n        case 3:\n            cmd = CMD_CONFIG_3;\n            break;\n        case 4:\n            cmd = CMD_CONFIG_4;\n            break;\n    }\n\n    i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        print(\"pca9505_set_config::FAILED\\n\");\n        return false;\n    }\n\n    return true;\n}\n\nbool pca9505_set_polarity(uint8_t slave_addr, pca9505_port_t port, uint8_t conf) {\n    uint8_t addr = SLAVE_TO_ADDR(slave_addr);\n    uint8_t cmd  = 0;\n    switch (port) {\n        case 0:\n            cmd = CMD_INVERSION_0;\n            break;\n        case 1:\n            cmd = CMD_INVERSION_1;\n            break;\n        case 2:\n            cmd = CMD_INVERSION_2;\n            break;\n        case 3:\n            cmd = CMD_INVERSION_3;\n            break;\n        case 4:\n            cmd = CMD_INVERSION_4;\n            break;\n    }\n\n    i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        print(\"pca9505_set_polarity::FAILED\\n\");\n        return false;\n    }\n\n    return true;\n}\n\nbool pca9505_set_output(uint8_t slave_addr, pca9505_port_t port, uint8_t conf) {\n    uint8_t addr = SLAVE_TO_ADDR(slave_addr);\n    uint8_t cmd  = 0;\n    switch (port) {\n        case 0:\n            cmd = CMD_OUTPUT_0;\n            break;\n        case 1:\n            cmd = CMD_OUTPUT_1;\n            break;\n        case 2:\n            cmd = CMD_OUTPUT_2;\n            break;\n        case 3:\n            cmd = CMD_OUTPUT_3;\n            break;\n        case 4:\n            cmd = CMD_OUTPUT_4;\n            break;\n    }\n\n    i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        print(\"pca9505_set_output::FAILED\\n\");\n        return false;\n    }\n\n    return true;\n}\n\nbool pca9505_read_pins(uint8_t slave_addr, pca9505_port_t port, uint8_t* out) {\n    uint8_t addr = SLAVE_TO_ADDR(slave_addr);\n    uint8_t cmd  = 0;\n    switch (port) {\n        case 0:\n            cmd = CMD_INPUT_0;\n            break;\n        case 1:\n            cmd = CMD_INPUT_1;\n            break;\n        case 2:\n            cmd = CMD_INPUT_2;\n            break;\n        case 3:\n            cmd = CMD_INPUT_3;\n            break;\n        case 4:\n            cmd = CMD_INPUT_4;\n            break;\n    }\n\n    i2c_status_t ret = i2c_read_register(addr, cmd, out, sizeof(uint8_t), TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        print(\"pca9505_read_pins::FAILED\\n\");\n        return false;\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "drivers/gpio/pca9505.h",
    "content": "// Copyright 2022 nirim000\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n/**\n * Port ID\n */\ntypedef enum {\n    PCA9505_PORT0,\n    PCA9505_PORT1,\n    PCA9505_PORT2,\n    PCA9505_PORT3,\n    PCA9505_PORT4,\n} pca9505_port_t;\n\n/**\n * Helpers for set_config\n */\nenum {\n    ALL_NORMAL   = 0,\n    ALL_INVERTED = 0xFF,\n};\n\n/**\n * Helpers for set_config\n */\nenum {\n    ALL_OUTPUT = 0,\n    ALL_INPUT  = 0xFF,\n};\n\n/**\n * Helpers for set_output\n */\nenum {\n    ALL_LOW  = 0,\n    ALL_HIGH = 0xFF,\n};\n\n/**\n * Init expander and any other dependent drivers\n */\nvoid pca9505_init(uint8_t slave_addr);\n\n/**\n * Configure input/output to a given port\n */\nbool pca9505_set_config(uint8_t slave_addr, pca9505_port_t port, uint8_t conf);\n\n/**\n * Configure polarity to a given port\n */\nbool pca9505_set_polarity(uint8_t slave_addr, pca9505_port_t port, uint8_t conf);\n\n/**\n * Write high/low to a given port\n */\nbool pca9505_set_output(uint8_t slave_addr, pca9505_port_t port, uint8_t conf);\n\n/**\n * Read state of a given port\n */\nbool pca9505_read_pins(uint8_t slave_addr, pca9505_port_t port, uint8_t* ret);\n\n// DEPRECATED - DO NOT USE\n\n#define pca9505_readPins pca9505_read_pins\n"
  },
  {
    "path": "drivers/gpio/pca9555.c",
    "content": "// Copyright 2020 zvecr<git@zvecr.com>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"i2c_master.h\"\n#include \"pca9555.h\"\n\n#include \"debug.h\"\n\n#define SLAVE_TO_ADDR(n) (n << 1)\n#define TIMEOUT 100\n\nenum {\n    CMD_INPUT_0 = 0,\n    CMD_INPUT_1,\n    CMD_OUTPUT_0,\n    CMD_OUTPUT_1,\n    CMD_INVERSION_0,\n    CMD_INVERSION_1,\n    CMD_CONFIG_0,\n    CMD_CONFIG_1,\n};\n\nvoid pca9555_init(uint8_t slave_addr) {\n    static uint8_t s_init = 0;\n    if (!s_init) {\n        i2c_init();\n\n        s_init = 1;\n    }\n\n    // TODO: could check device connected\n}\n\nbool pca9555_set_config(uint8_t slave_addr, pca9555_port_t port, uint8_t conf) {\n    uint8_t addr = SLAVE_TO_ADDR(slave_addr);\n    uint8_t cmd  = port ? CMD_CONFIG_1 : CMD_CONFIG_0;\n\n    i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        print(\"pca9555_set_config::FAILED\\n\");\n        return false;\n    }\n\n    return true;\n}\n\nbool pca9555_set_output(uint8_t slave_addr, pca9555_port_t port, uint8_t conf) {\n    uint8_t addr = SLAVE_TO_ADDR(slave_addr);\n    uint8_t cmd  = port ? CMD_OUTPUT_1 : CMD_OUTPUT_0;\n\n    i2c_status_t ret = i2c_write_register(addr, cmd, &conf, sizeof(conf), TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        print(\"pca9555_set_output::FAILED\\n\");\n        return false;\n    }\n\n    return true;\n}\n\nbool pca9555_set_output_all(uint8_t slave_addr, uint8_t confA, uint8_t confB) {\n    uint8_t addr    = SLAVE_TO_ADDR(slave_addr);\n    uint8_t conf[2] = {confA, confB};\n\n    i2c_status_t ret = i2c_write_register(addr, CMD_OUTPUT_0, &conf[0], sizeof(conf), TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        dprintf(\"pca9555_set_output::FAILED::%u\\n\", ret);\n        return false;\n    }\n\n    return true;\n}\n\nbool pca9555_read_pins(uint8_t slave_addr, pca9555_port_t port, uint8_t* out) {\n    uint8_t addr = SLAVE_TO_ADDR(slave_addr);\n    uint8_t cmd  = port ? CMD_INPUT_1 : CMD_INPUT_0;\n\n    i2c_status_t ret = i2c_read_register(addr, cmd, out, sizeof(uint8_t), TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        print(\"pca9555_read_pins::FAILED\\n\");\n        return false;\n    }\n\n    return true;\n}\n\nbool pca9555_read_pins_all(uint8_t slave_addr, uint16_t* out) {\n    uint8_t addr = SLAVE_TO_ADDR(slave_addr);\n\n    typedef union {\n        uint8_t  u8[2];\n        uint16_t u16;\n    } data16;\n\n    data16 data = {.u16 = 0};\n\n    i2c_status_t ret = i2c_read_register(addr, CMD_INPUT_0, &data.u8[0], sizeof(data), TIMEOUT);\n    if (ret != I2C_STATUS_SUCCESS) {\n        print(\"pca9555_read_pins_all::FAILED\\n\");\n        return false;\n    }\n\n    *out = data.u16;\n    return true;\n}\n"
  },
  {
    "path": "drivers/gpio/pca9555.h",
    "content": "// Copyright 2020 zvecr<git@zvecr.com>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n/*\n            PCA9555\n         ,----------.\n   SDA --| SDA  P00 |-- P00\n   SCL --| SCL  P01 |-- P01\n   INT --| INT  P02 |-- P02\n         |      P03 |-- P03\n   A0  --| A0   P04 |-- P04\n   A1  --| A1   P05 |-- P05\n   A2  --| A2   P06 |-- P06\n         |      P07 |-- P07\n         |          |\n         |      P10 |-- P10\n         |      P11 |-- P11\n         |      P12 |-- P12\n         |      P13 |-- P13\n         |      P14 |-- P14\n         |      P15 |-- P15\n         |      P16 |-- P16\n         |      P17 |-- P17\n         `----------'\n*/\n\n/**\n * Port ID\n */\ntypedef enum {\n    PCA9555_PORT0,\n    PCA9555_PORT1,\n} pca9555_port_t;\n\n/**\n * Helpers for set_config\n */\nenum {\n    ALL_OUTPUT = 0,\n    ALL_INPUT  = 0xFF,\n};\n\n/**\n * Helpers for set_output\n */\nenum {\n    ALL_LOW  = 0,\n    ALL_HIGH = 0xFF,\n};\n\n/**\n * Init expander and any other dependent drivers\n */\nvoid pca9555_init(uint8_t slave_addr);\n\n/**\n * Configure input/output to a given port\n */\nbool pca9555_set_config(uint8_t slave_addr, pca9555_port_t port, uint8_t conf);\n\n/**\n * Write high/low to a given port\n */\nbool pca9555_set_output(uint8_t slave_addr, pca9555_port_t port, uint8_t conf);\n\n/**\n * Write high/low to both ports sequentially\n *\n *  - slightly faster than multiple set_output\n */\nbool pca9555_set_output_all(uint8_t slave_addr, uint8_t confA, uint8_t confB);\n\n/**\n * Read state of a given port\n */\nbool pca9555_read_pins(uint8_t slave_addr, pca9555_port_t port, uint8_t* ret);\n\n/**\n * Read state of both ports sequentially\n *\n *  - slightly faster than multiple readPins\n */\nbool pca9555_read_pins_all(uint8_t slave_addr, uint16_t* ret);\n\n// DEPRECATED - DO NOT USE\n\n#define pca9555_readPins pca9555_read_pins\n#define pca9555_readPins_all pca9555_read_pins_all\n"
  },
  {
    "path": "drivers/gpio/sn74x138.c",
    "content": "/* Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"sn74x138.h\"\n#include \"gpio.h\"\n\n#define ADDRESS_PIN_COUNT 3\n\n#ifndef SN74X138_ADDRESS_PINS\n#    error sn74x138: no address pins defined!\n#endif\n\nstatic const pin_t address_pins[ADDRESS_PIN_COUNT] = SN74X138_ADDRESS_PINS;\n\nvoid sn74x138_init(void) {\n    for (int i = 0; i < ADDRESS_PIN_COUNT; i++) {\n        gpio_set_pin_output(address_pins[i]);\n        gpio_write_pin_low(address_pins[i]);\n    }\n\n#if defined(SN74X138_E1_PIN)\n    gpio_set_pin_output(SN74X138_E1_PIN);\n    gpio_write_pin_high(SN74X138_E1_PIN);\n#endif\n\n#if defined(SN74X138_E2_PIN)\n    gpio_set_pin_output(SN74X138_E2_PIN);\n    gpio_write_pin_high(SN74X138_E2_PIN);\n#endif\n#if defined(SN74X138_E3_PIN)\n    gpio_set_pin_output(SN74X138_E3_PIN);\n    gpio_write_pin_low(SN74X138_E3_PIN);\n#endif\n}\n\nvoid sn74x138_set_enabled(bool enabled) {\n#if defined(SN74X138_E1_PIN)\n    gpio_write_pin(SN74X138_E1_PIN, !enabled);\n#endif\n#if defined(SN74X138_E2_PIN)\n    gpio_write_pin(SN74X138_E2_PIN, !enabled);\n#endif\n#if defined(SN74X138_E3_PIN)\n    gpio_write_pin(SN74X138_E3_PIN, enabled);\n#endif\n}\n\nvoid sn74x138_set_addr(uint8_t address) {\n    for (int i = 0; i < ADDRESS_PIN_COUNT; i++) {\n        gpio_write_pin(address_pins[i], address & (1 << i));\n    }\n}\n"
  },
  {
    "path": "drivers/gpio/sn74x138.h",
    "content": "/* Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n/**\n * Driver for 74x138 3-to-8 decoder/demultiplexer with inverting outputs\n * https://assets.nexperia.com/documents/data-sheet/74HC_HCT138.pdf\n */\n\n/**\n * Initialize the address and output enable pins.\n */\nvoid sn74x138_init(void);\n\n/**\n * Set the enabled state.\n *\n * When enabled is true, pulls the E1 and E2 pins low, and the E3 pin high.\n *\n * \\param enabled The enable state to set.\n */\nvoid sn74x138_set_enabled(bool enabled);\n\n/**\n * Set the output pin address.\n *\n * The selected output pin will be pulled low, while the remaining output pins will be high.\n *\n * \\param address The address to set, from 0 to 7.\n */\nvoid sn74x138_set_addr(uint8_t address);\n"
  },
  {
    "path": "drivers/gpio/sn74x154.c",
    "content": "/* Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"sn74x154.h\"\n#include \"gpio.h\"\n\n#define ADDRESS_PIN_COUNT 4\n\n#ifndef SN74X154_ADDRESS_PINS\n#    error sn74x154: no address pins defined!\n#endif\n\nstatic const pin_t address_pins[ADDRESS_PIN_COUNT] = SN74X154_ADDRESS_PINS;\n\nvoid sn74x154_init(void) {\n    for (int i = 0; i < ADDRESS_PIN_COUNT; i++) {\n        gpio_set_pin_output(address_pins[i]);\n        gpio_write_pin_low(address_pins[i]);\n    }\n\n#if defined(SN74X154_E0_PIN)\n    gpio_set_pin_output(SN74X154_E0_PIN);\n    gpio_write_pin_high(SN74X154_E0_PIN);\n#endif\n\n#if defined(SN74X154_E1_PIN)\n    gpio_set_pin_output(SN74X154_E1_PIN);\n    gpio_write_pin_high(SN74X154_E1_PIN);\n#endif\n}\n\nvoid sn74x154_set_enabled(bool enabled) {\n#if defined(SN74X154_E0_PIN)\n    gpio_write_pin(SN74X154_E0_PIN, !enabled);\n#endif\n#if defined(SN74X154_E1_PIN)\n    gpio_write_pin(SN74X154_E1_PIN, !enabled);\n#endif\n}\n\nvoid sn74x154_set_addr(uint8_t address) {\n    for (int i = 0; i < ADDRESS_PIN_COUNT; i++) {\n        gpio_write_pin(address_pins[i], address & (1 << i));\n    }\n}\n"
  },
  {
    "path": "drivers/gpio/sn74x154.h",
    "content": "/* Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n/**\n * Driver for 74x154 4-to-16 decoder/demultiplexer with inverting outputs\n * https://assets.nexperia.com/documents/data-sheet/74HC_HCT154.pdf\n */\n\n/**\n * Initialize the address and output enable pins.\n */\nvoid sn74x154_init(void);\n\n/**\n * Set the enabled state.\n *\n * When enabled is true, pulls the E0 and E1 pins low.\n *\n * \\param enabled The enable state to set.\n */\nvoid sn74x154_set_enabled(bool enabled);\n\n/**\n * Set the output pin address.\n *\n * The selected output pin will be pulled low, while the remaining output pins will be high.\n *\n * \\param address The address to set, from 0 to 15.\n */\nvoid sn74x154_set_addr(uint8_t address);\n"
  },
  {
    "path": "drivers/haptic/drv2605l.c",
    "content": "/* Copyright 2018 ishtob\n * Driver for DRV2605L written for QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"drv2605l.h\"\n#include \"i2c_master.h\"\n#include <math.h>\n\nuint8_t drv2605l_write_buffer[2];\nuint8_t drv2605l_read_buffer;\n\nvoid drv2605l_write(uint8_t reg_addr, uint8_t data) {\n    drv2605l_write_buffer[0] = reg_addr;\n    drv2605l_write_buffer[1] = data;\n    i2c_transmit(DRV2605L_I2C_ADDRESS << 1, drv2605l_write_buffer, 2, 100);\n}\n\nuint8_t drv2605l_read(uint8_t reg_addr) {\n    i2c_read_register(DRV2605L_I2C_ADDRESS << 1, reg_addr, &drv2605l_read_buffer, 1, 100);\n\n    return drv2605l_read_buffer;\n}\n\nvoid drv2605l_init(void) {\n    i2c_init();\n    /* 0x07 sets DRV2605 into calibration mode */\n    drv2605l_write(DRV2605L_REG_MODE, 0x07);\n\n    //  drv2605l_write(DRV2605L_REG_FEEDBACK_CTRL,0xB6);\n\n#if DRV2605L_FB_ERM_LRA == 0\n    /* ERM settings */\n    drv2605l_write(DRV2605L_REG_RATED_VOLTAGE, (DRV2605L_RATED_VOLTAGE / 21.33) * 1000);\n#    if DRV2605L_ERM_OPEN_LOOP == 0\n    drv2605l_write(DRV2605L_REG_OVERDRIVE_CLAMP_VOLTAGE, (((DRV2605L_V_PEAK * (DRV2605L_DRIVE_TIME + DRV2605L_BLANKING_TIME + DRV2605L_IDISS_TIME)) / 0.02133) / (DRV2605L_DRIVE_TIME - 0.0003)));\n#    elif DRV2605L_ERM_OPEN_LOOP == 1\n    drv2605l_write(DRV2605L_REG_OVERDRIVE_CLAMP_VOLTAGE, (DRV2605L_V_PEAK / 0.02196));\n#    endif\n#elif DRV2605L_FB_ERM_LRA == 1\n    drv2605l_write(DRV2605L_REG_RATED_VOLTAGE, ((DRV2605L_V_RMS * sqrt(1 - ((4 * ((150 + (DRV2605L_SAMPLE_TIME * 50)) * 0.000001)) + 0.0003) * DRV2605L_F_LRA) / 0.02071)));\n#    if DRV2605L_LRA_OPEN_LOOP == 0\n    drv2605l_write(DRV2605L_REG_OVERDRIVE_CLAMP_VOLTAGE, ((DRV2605L_V_PEAK / sqrt(1 - (DRV2605L_F_LRA * 0.0008)) / 0.02133)));\n#    elif DRV2605L_LRA_OPEN_LOOP == 1\n    drv2605l_write(DRV2605L_REG_OVERDRIVE_CLAMP_VOLTAGE, (DRV2605L_V_PEAK / 0.02196));\n#    endif\n#endif\n\n    drv2605l_reg_feedback_ctrl_t reg_feedback_ctrl;\n    reg_feedback_ctrl.bits.ERM_LRA      = DRV2605L_FB_ERM_LRA;\n    reg_feedback_ctrl.bits.BRAKE_FACTOR = DRV2605L_FB_BRAKEFACTOR;\n    reg_feedback_ctrl.bits.LOOP_GAIN    = DRV2605L_FB_LOOPGAIN;\n    reg_feedback_ctrl.bits.BEMF_GAIN    = 0; /* auto-calibration populates this field*/\n    drv2605l_write(DRV2605L_REG_FEEDBACK_CTRL, (uint8_t)reg_feedback_ctrl.raw);\n\n    drv2605l_reg_ctrl1_t reg_ctrl1;\n    reg_ctrl1.bits.C1_DRIVE_TIME    = DRV2605L_DRIVE_TIME;\n    reg_ctrl1.bits.C1_AC_COUPLE     = DRV2605L_AC_COUPLE;\n    reg_ctrl1.bits.C1_STARTUP_BOOST = DRV2605L_STARTUP_BOOST;\n    drv2605l_write(DRV2605L_REG_CTRL1, (uint8_t)reg_ctrl1.raw);\n\n    drv2605l_reg_ctrl2_t reg_ctrl2;\n    reg_ctrl2.bits.C2_BIDIR_INPUT   = DRV2605L_BIDIR_INPUT;\n    reg_ctrl2.bits.C2_BRAKE_STAB    = DRV2605L_BRAKE_STAB;\n    reg_ctrl2.bits.C2_SAMPLE_TIME   = DRV2605L_SAMPLE_TIME;\n    reg_ctrl2.bits.C2_BLANKING_TIME = DRV2605L_BLANKING_TIME;\n    reg_ctrl2.bits.C2_IDISS_TIME    = DRV2605L_IDISS_TIME;\n    drv2605l_write(DRV2605L_REG_CTRL2, (uint8_t)reg_ctrl2.raw);\n\n    drv2605l_reg_ctrl3_t reg_ctrl3;\n    reg_ctrl3.bits.C3_LRA_OPEN_LOOP   = DRV2605L_LRA_OPEN_LOOP;\n    reg_ctrl3.bits.C3_N_PWM_ANALOG    = DRV2605L_N_PWM_ANALOG;\n    reg_ctrl3.bits.C3_LRA_DRIVE_MODE  = DRV2605L_LRA_DRIVE_MODE;\n    reg_ctrl3.bits.C3_DATA_FORMAT_RTO = DRV2605L_DATA_FORMAT_RTO;\n    reg_ctrl3.bits.C3_SUPPLY_COMP_DIS = DRV2605L_SUPPLY_COMP_DIS;\n    reg_ctrl3.bits.C3_ERM_OPEN_LOOP   = DRV2605L_ERM_OPEN_LOOP;\n    reg_ctrl3.bits.C3_NG_THRESH       = DRV2605L_NG_THRESH;\n    drv2605l_write(DRV2605L_REG_CTRL3, (uint8_t)reg_ctrl3.raw);\n\n    drv2605l_reg_ctrl4_t reg_ctrl4;\n    reg_ctrl4.bits.C4_ZC_DET_TIME   = DRV2605L_ZC_DET_TIME;\n    reg_ctrl4.bits.C4_AUTO_CAL_TIME = DRV2605L_AUTO_CAL_TIME;\n    drv2605l_write(DRV2605L_REG_CTRL4, (uint8_t)reg_ctrl4.raw);\n\n    drv2605l_write(DRV2605L_REG_LIBRARY_SELECTION, DRV2605L_LIBRARY);\n\n    drv2605l_write(DRV2605L_REG_GO, 0x01);\n\n    /* 0x00 sets DRV2605 out of standby and to use internal trigger\n     * 0x01 sets DRV2605 out of standby and to use external trigger */\n    drv2605l_write(DRV2605L_REG_MODE, 0x00);\n\n    // Play greeting sequence\n    drv2605l_write(DRV2605L_REG_GO, 0x00);\n    drv2605l_write(DRV2605L_REG_WAVEFORM_SEQUENCER_1, DRV2605L_GREETING);\n    drv2605l_write(DRV2605L_REG_GO, 0x01);\n}\n\nvoid drv2605l_rtp_init(void) {\n    drv2605l_write(DRV2605L_REG_GO, 0x00);\n    drv2605l_write(DRV2605L_REG_RTP_INPUT, 20); // 20 is the lowest value I've found where haptics can still be felt.\n    drv2605l_write(DRV2605L_REG_MODE, 0x05);\n    drv2605l_write(DRV2605L_REG_GO, 0x01);\n}\n\nvoid drv2605l_amplitude(uint8_t amplitude) {\n    drv2605l_write(DRV2605L_REG_RTP_INPUT, amplitude);\n}\n\nvoid drv2605l_pulse(uint8_t sequence) {\n    drv2605l_write(DRV2605L_REG_GO, 0x00);\n    drv2605l_write(DRV2605L_REG_WAVEFORM_SEQUENCER_1, sequence);\n    drv2605l_write(DRV2605L_REG_GO, 0x01);\n}\n"
  },
  {
    "path": "drivers/haptic/drv2605l.h",
    "content": "/* Copyright 2018 ishtob\n * Driver for DRV2605L written for QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n/* Initialization settings\n\n * Feedback Control Settings */\n#ifndef DRV2605L_FB_ERM_LRA\n#    define DRV2605L_FB_ERM_LRA 1 /* For ERM:0 or LRA:1*/\n#endif\n#ifndef DRV2605L_FB_BRAKEFACTOR\n#    define DRV2605L_FB_BRAKEFACTOR 3 /* For 1x:0, 2x:1, 3x:2, 4x:3, 6x:4, 8x:5, 16x:6, Disable Braking:7 */\n#endif\n#ifndef DRV2605L_FB_LOOPGAIN\n#    define DRV2605L_FB_LOOPGAIN 1 /* For  Low:0, Medium:1, High:2, Very High:3 */\n#endif\n\n/* LRA specific settings */\n#if DRV2605L_FB_ERM_LRA == 1\n#    ifndef DRV2605L_V_RMS\n#        define DRV2605L_V_RMS 2.0\n#    endif\n#    ifndef DRV2605L_V_PEAK\n#        define DRV2605L_V_PEAK 2.1\n#    endif\n#    ifndef DRV2605L_F_LRA\n#        define DRV2605L_F_LRA 205\n#    endif\n#    ifndef DRV2605L_RATED_VOLTAGE\n#        define DRV2605L_RATED_VOLTAGE 2 /* 2v as safe range in case device voltage is not set */\n#    endif\n#endif\n\n#ifndef DRV2605L_RATED_VOLTAGE\n#    define DRV2605L_RATED_VOLTAGE 2 /* 2v as safe range in case device voltage is not set */\n#endif\n#ifndef DRV2605L_V_PEAK\n#    define DRV2605L_V_PEAK 2.8\n#endif\n\n/* Library Selection */\n#ifndef DRV2605L_LIBRARY\n#    if DRV2605L_FB_ERM_LRA == 1\n#        define DRV2605L_LIBRARY 6 /* For Empty:0' TS2200 library A to D:1-5, LRA Library: 6 */\n#    else\n#        define DRV2605L_LIBRARY 1\n#    endif\n#endif\n\n#ifndef DRV2605L_GREETING\n#    define DRV2605L_GREETING DRV2605L_EFFECT_750_MS_ALERT_100\n#endif\n#ifndef DRV2605L_DEFAULT_MODE\n#    define DRV2605L_DEFAULT_MODE DRV2605L_EFFECT_STRONG_CLICK_1_100\n#endif\n\n/* Control 1 register settings */\n#ifndef DRV2605L_DRIVE_TIME\n#    define DRV2605L_DRIVE_TIME 25\n#endif\n#ifndef DRV2605L_AC_COUPLE\n#    define DRV2605L_AC_COUPLE 0\n#endif\n#ifndef DRV2605L_STARTUP_BOOST\n#    define DRV2605L_STARTUP_BOOST 1\n#endif\n\n/* Control 2 Settings */\n#ifndef DRV2605L_BIDIR_INPUT\n#    define DRV2605L_BIDIR_INPUT 1\n#endif\n#ifndef DRV2605L_BRAKE_STAB\n#    define DRV2605L_BRAKE_STAB 1 /* Loopgain is reduced when braking is almost complete to improve stability */\n#endif\n#ifndef DRV2605L_SAMPLE_TIME\n#    define DRV2605L_SAMPLE_TIME 3\n#endif\n#ifndef DRV2605L_BLANKING_TIME\n#    define DRV2605L_BLANKING_TIME 1\n#endif\n#ifndef DRV2605L_IDISS_TIME\n#    define DRV2605L_IDISS_TIME 1\n#endif\n\n/* Control 3 settings */\n#ifndef DRV2605L_NG_THRESH\n#    define DRV2605L_NG_THRESH 2\n#endif\n#ifndef DRV2605L_ERM_OPEN_LOOP\n#    define DRV2605L_ERM_OPEN_LOOP 1\n#endif\n#ifndef DRV2605L_SUPPLY_COMP_DIS\n#    define DRV2605L_SUPPLY_COMP_DIS 0\n#endif\n#ifndef DRV2605L_DATA_FORMAT_RTO\n#    define DRV2605L_DATA_FORMAT_RTO 0\n#endif\n#ifndef DRV2605L_LRA_DRIVE_MODE\n#    define DRV2605L_LRA_DRIVE_MODE 0\n#endif\n#ifndef DRV2605L_N_PWM_ANALOG\n#    define DRV2605L_N_PWM_ANALOG 0\n#endif\n#ifndef DRV2605L_LRA_OPEN_LOOP\n#    define DRV2605L_LRA_OPEN_LOOP 0\n#endif\n\n/* Control 4 settings */\n#ifndef DRV2605L_ZC_DET_TIME\n#    define DRV2605L_ZC_DET_TIME 0\n#endif\n#ifndef DRV2605L_AUTO_CAL_TIME\n#    define DRV2605L_AUTO_CAL_TIME 3\n#endif\n\n#define DRV2605L_I2C_ADDRESS 0x5A\n\n#define DRV2605L_REG_STATUS 0x00\n#define DRV2605L_REG_MODE 0x01\n#define DRV2605L_REG_RTP_INPUT 0x02\n#define DRV2605L_REG_LIBRARY_SELECTION 0x03\n#define DRV2605L_REG_WAVEFORM_SEQUENCER_1 0x04\n#define DRV2605L_REG_WAVEFORM_SEQUENCER_2 0x05\n#define DRV2605L_REG_WAVEFORM_SEQUENCER_3 0x06\n#define DRV2605L_REG_WAVEFORM_SEQUENCER_4 0x07\n#define DRV2605L_REG_WAVEFORM_SEQUENCER_5 0x08\n#define DRV2605L_REG_WAVEFORM_SEQUENCER_6 0x09\n#define DRV2605L_REG_WAVEFORM_SEQUENCER_7 0x0A\n#define DRV2605L_REG_WAVEFORM_SEQUENCER_8 0x0B\n#define DRV2605L_REG_GO 0x0C\n#define DRV2605L_REG_OVERDRIVE_TIME_OFFSET 0x0D\n#define DRV2605L_REG_SUSTAIN_TIME_OFFSET_P 0x0E\n#define DRV2605L_REG_SUSTAIN_TIME_OFFSET_N 0x0F\n#define DRV2605L_REG_BRAKE_TIME_OFFSET 0x10\n#define DRV2605L_REG_AUDIO_TO_VIBE_CTRL 0x11\n#define DRV2605L_REG_AUDIO_TO_VIBE_MIN_INPUT 0x12\n#define DRV2605L_REG_AUDIO_TO_VIBE_MAX_INPUT 0x13\n#define DRV2605L_REG_AUDIO_TO_VIBE_MIN_OUTPUT_DRIVE 0x14\n#define DRV2605L_REG_AUDIO_TO_VIBE_MAX_OUTPUT_DRIVE 0x15\n#define DRV2605L_REG_RATED_VOLTAGE 0x16\n#define DRV2605L_REG_OVERDRIVE_CLAMP_VOLTAGE 0x17\n#define DRV2605L_REG_AUTO_CALIBRATION_COMPENSATION_RESULT 0x18\n#define DRV2605L_REG_AUTO_CALIBRATION_BACK_EMF_RESULT 0x19\n#define DRV2605L_REG_FEEDBACK_CTRL 0x1A\n#define DRV2605L_REG_CTRL1 0x1B\n#define DRV2605L_REG_CTRL2 0x1C\n#define DRV2605L_REG_CTRL3 0x1D\n#define DRV2605L_REG_CTRL4 0x1E\n#define DRV2605L_REG_CTRL5 0x1F\n#define DRV2605L_REG_LRA_OPEN_LOOP_PERIOD 0x20\n#define DRV2605L_REG_VBAT_VOLTAGE_MONITOR 0x21\n#define DRV2605L_REG_LRA_RESONANCE_PERIOD 0x22\n\nvoid    drv2605l_init(void);\nvoid    drv2605l_write(const uint8_t reg_addr, const uint8_t data);\nuint8_t drv2605l_read(const uint8_t reg_addr);\nvoid    drv2605l_rtp_init(void);\nvoid    drv2605l_amplitude(const uint8_t amplitude);\nvoid    drv2605l_pulse(const uint8_t sequence);\n\ntypedef enum drv2605l_effect_t {\n    DRV2605L_EFFECT_CLEAR_SEQUENCE,\n    DRV2605L_EFFECT_STRONG_CLICK_100,\n    DRV2605L_EFFECT_STRONG_CLICK_60,\n    DRV2605L_EFFECT_STRONG_CLICK_30,\n    DRV2605L_EFFECT_SHARP_CLICK_100,\n    DRV2605L_EFFECT_SHARP_CLICK_60,\n    DRV2605L_EFFECT_SHARP_CLICK_30,\n    DRV2605L_EFFECT_SOFT_BUMP_100,\n    DRV2605L_EFFECT_SOFT_BUMP_60,\n    DRV2605L_EFFECT_SOFT_BUMP_30,\n    DRV2605L_EFFECT_DOUBLE_CLICK_100,\n    DRV2605L_EFFECT_DOUBLE_CLICK_60,\n    DRV2605L_EFFECT_TRIPLE_CLICK_100,\n    DRV2605L_EFFECT_SOFT_FUZZ_60,\n    DRV2605L_EFFECT_STRONG_BUZZ_100,\n    DRV2605L_EFFECT_750_MS_ALERT_100,\n    DRV2605L_EFFECT_1000_MS_ALERT_100,\n    DRV2605L_EFFECT_STRONG_CLICK_1_100,\n    DRV2605L_EFFECT_STRONG_CLICK_2_80,\n    DRV2605L_EFFECT_STRONG_CLICK_3_60,\n    DRV2605L_EFFECT_STRONG_CLICK_4_30,\n    DRV2605L_EFFECT_MEDIUM_CLICK_1_100,\n    DRV2605L_EFFECT_MEDIUM_CLICK_2_80,\n    DRV2605L_EFFECT_MEDIUM_CLICK_3_60,\n    DRV2605L_EFFECT_SHARP_TICK_1_100,\n    DRV2605L_EFFECT_SHARP_TICK_2_80,\n    DRV2605L_EFFECT_SHARP_TICK_3_60,\n    DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_STRONG_1_100,\n    DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_STRONG_2_80,\n    DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_STRONG_3_60,\n    DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_STRONG_4_30,\n    DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_MEDIUM_1_100,\n    DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_MEDIUM_2_80,\n    DRV2605L_EFFECT_SHORT_DOUBLE_CLICK_MEDIUM_3_60,\n    DRV2605L_EFFECT_SHORT_DOUBLE_SHARP_TICK_1_100,\n    DRV2605L_EFFECT_SHORT_DOUBLE_SHARP_TICK_2_80,\n    DRV2605L_EFFECT_SHORT_DOUBLE_SHARP_TICK_3_60,\n    DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_STRONG_1_100,\n    DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_STRONG_2_80,\n    DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_STRONG_3_60,\n    DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_STRONG_4_30,\n    DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_MEDIUM_1_100,\n    DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_MEDIUM_2_80,\n    DRV2605L_EFFECT_LONG_DOUBLE_SHARP_CLICK_MEDIUM_3_60,\n    DRV2605L_EFFECT_LONG_DOUBLE_SHARP_TICK_1_100,\n    DRV2605L_EFFECT_LONG_DOUBLE_SHARP_TICK_2_80,\n    DRV2605L_EFFECT_LONG_DOUBLE_SHARP_TICK_3_60,\n    DRV2605L_EFFECT_BUZZ_1_100,\n    DRV2605L_EFFECT_BUZZ_2_80,\n    DRV2605L_EFFECT_BUZZ_3_60,\n    DRV2605L_EFFECT_BUZZ_4_40,\n    DRV2605L_EFFECT_BUZZ_5_20,\n    DRV2605L_EFFECT_PULSING_STRONG_1_100,\n    DRV2605L_EFFECT_PULSING_STRONG_2_60,\n    DRV2605L_EFFECT_PULSING_MEDIUM_1_100,\n    DRV2605L_EFFECT_PULSING_MEDIUM_2_60,\n    DRV2605L_EFFECT_PULSING_SHARP_1_100,\n    DRV2605L_EFFECT_PULSING_SHARP_2_60,\n    DRV2605L_EFFECT_TRANSITION_CLICK_1_100,\n    DRV2605L_EFFECT_TRANSITION_CLICK_2_80,\n    DRV2605L_EFFECT_TRANSITION_CLICK_3_60,\n    DRV2605L_EFFECT_TRANSITION_CLICK_4_40,\n    DRV2605L_EFFECT_TRANSITION_CLICK_5_20,\n    DRV2605L_EFFECT_TRANSITION_CLICK_6_10,\n    DRV2605L_EFFECT_TRANSITION_HUM_1_100,\n    DRV2605L_EFFECT_TRANSITION_HUM_2_80,\n    DRV2605L_EFFECT_TRANSITION_HUM_3_60,\n    DRV2605L_EFFECT_TRANSITION_HUM_4_40,\n    DRV2605L_EFFECT_TRANSITION_HUM_5_20,\n    DRV2605L_EFFECT_TRANSITION_HUM_6_10,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SMOOTH_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SMOOTH_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SMOOTH_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SMOOTH_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SHARP_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SHARP_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SHARP_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SHARP_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SHARP_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SHARP_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SMOOTH_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SMOOTH_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SMOOTH_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SMOOTH_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SMOOTH_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SMOOTH_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SHARP_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SHARP_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SHARP_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SHARP_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SHARP_1_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SHARP_2_100,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SMOOTH_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SMOOTH_2_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_2_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SMOOTH_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SMOOTH_2_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SHARP_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_LONG_SHARP_2_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SHARP_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_MEDIUM_SHARP_2_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SHARP_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_DOWN_SHORT_SHARP_2_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SMOOTH_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SMOOTH_2_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SMOOTH_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SMOOTH_2_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SMOOTH_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SMOOTH_2_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SHARP_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_LONG_SHARP_2_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SHARP_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_MEDIUM_SHARP_2_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SHARP_1_50,\n    DRV2605L_EFFECT_TRANSITION_RAMP_UP_SHORT_SHARP_2_50,\n    DRV2605L_EFFECT_LONG_BUZZ_FOR_PROGRAMMATIC_STOPPING,\n    DRV2605L_EFFECT_SMOOTH_HUM_1_50,\n    DRV2605L_EFFECT_SMOOTH_HUM_2_40,\n    DRV2605L_EFFECT_SMOOTH_HUM_3_30,\n    DRV2605L_EFFECT_SMOOTH_HUM_4_20,\n    DRV2605L_EFFECT_SMOOTH_HUM_5_10,\n    DRV2605L_EFFECT_COUNT\n} drv2605l_effect_t;\n\n/* Register bit array unions */\n\ntypedef union { /* register 0x1A */\n    uint8_t raw;\n    struct {\n        uint8_t BEMF_GAIN : 2;\n        uint8_t LOOP_GAIN : 2;\n        uint8_t BRAKE_FACTOR : 3;\n        uint8_t ERM_LRA : 1;\n    } bits;\n} drv2605l_reg_feedback_ctrl_t;\n\ntypedef union { /* register 0x1B */\n    uint8_t raw;\n    struct {\n        uint8_t C1_DRIVE_TIME : 5;\n        uint8_t C1_AC_COUPLE : 1;\n        uint8_t : 1;\n        uint8_t C1_STARTUP_BOOST : 1;\n    } bits;\n} drv2605l_reg_ctrl1_t;\n\ntypedef union { /* register 0x1C */\n    uint8_t raw;\n    struct {\n        uint8_t C2_IDISS_TIME : 2;\n        uint8_t C2_BLANKING_TIME : 2;\n        uint8_t C2_SAMPLE_TIME : 2;\n        uint8_t C2_BRAKE_STAB : 1;\n        uint8_t C2_BIDIR_INPUT : 1;\n    } bits;\n} drv2605l_reg_ctrl2_t;\n\ntypedef union { /* register 0x1D */\n    uint8_t raw;\n    struct {\n        uint8_t C3_LRA_OPEN_LOOP : 1;\n        uint8_t C3_N_PWM_ANALOG : 1;\n        uint8_t C3_LRA_DRIVE_MODE : 1;\n        uint8_t C3_DATA_FORMAT_RTO : 1;\n        uint8_t C3_SUPPLY_COMP_DIS : 1;\n        uint8_t C3_ERM_OPEN_LOOP : 1;\n        uint8_t C3_NG_THRESH : 2;\n    } bits;\n} drv2605l_reg_ctrl3_t;\n\ntypedef union { /* register 0x1E */\n    uint8_t raw;\n    struct {\n        uint8_t C4_OTP_PROGRAM : 1;\n        uint8_t : 1;\n        uint8_t C4_OTP_STATUS : 1;\n        uint8_t : 1;\n        uint8_t C4_AUTO_CAL_TIME : 2;\n        uint8_t C4_ZC_DET_TIME : 2;\n    } bits;\n} drv2605l_reg_ctrl4_t;\n"
  },
  {
    "path": "drivers/haptic/solenoid.c",
    "content": "/* Copyright 2018 mtdjr - modified by ishtob\n * Driver for solenoid written for QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"timer.h\"\n#include \"solenoid.h\"\n#include \"haptic.h\"\n#include \"gpio.h\"\n#include \"usb_device_state.h\"\n#include \"util.h\"\n#include <stdlib.h>\n\nstatic pin_t solenoid_pads[] = SOLENOID_PINS;\n#define NUMBER_OF_SOLENOIDS ARRAY_SIZE(solenoid_pads)\nbool     solenoid_on[NUMBER_OF_SOLENOIDS]      = {false};\nbool     solenoid_buzzing[NUMBER_OF_SOLENOIDS] = {false};\nuint16_t solenoid_start[NUMBER_OF_SOLENOIDS]   = {0};\n#ifdef SOLENOID_PIN_ACTIVE_LOW\n#    define low true\n#    define high false\n#else\n#    define low false\n#    define high true\n#endif\nstatic bool solenoid_active_state[NUMBER_OF_SOLENOIDS];\n\nextern haptic_config_t haptic_config;\n\nvoid solenoid_buzz_on(void) {\n    haptic_set_buzz(1);\n}\n\nvoid solenoid_buzz_off(void) {\n    haptic_set_buzz(0);\n}\n\nvoid solenoid_set_buzz(uint8_t buzz) {\n    haptic_set_buzz(buzz);\n}\n\nvoid solenoid_set_dwell(uint8_t dwell) {\n    haptic_set_dwell(dwell);\n}\n\n/**\n * @brief Stops a specific solenoid\n *\n * @param index select which solenoid to check/stop\n */\nvoid solenoid_stop(uint8_t index) {\n    gpio_write_pin(solenoid_pads[index], !solenoid_active_state[index]);\n    solenoid_on[index]      = false;\n    solenoid_buzzing[index] = false;\n}\n\n/**\n * @brief Fires off a specific solenoid\n *\n * @param index Selects which solenoid to fire\n */\nvoid solenoid_fire(uint8_t index) {\n    if (!haptic_config.buzz && solenoid_on[index]) return;\n    if (haptic_config.buzz && solenoid_buzzing[index]) return;\n\n    solenoid_on[index]      = true;\n    solenoid_buzzing[index] = true;\n    solenoid_start[index]   = timer_read();\n    gpio_write_pin(solenoid_pads[index], solenoid_active_state[index]);\n}\n\n/**\n * @brief Handles selecting a non-active solenoid, and firing it.\n *\n */\nvoid solenoid_fire_handler(void) {\n#ifndef SOLENOID_RANDOM_FIRE\n    if (NUMBER_OF_SOLENOIDS > 1) {\n        uint8_t i = rand() % NUMBER_OF_SOLENOIDS;\n        if (!solenoid_on[i]) {\n            solenoid_fire(i);\n        }\n    } else {\n        solenoid_fire(0);\n    }\n#else\n    for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) {\n        if (!solenoid_on[i]) {\n            solenoid_fire(i);\n            break;\n        }\n    }\n#endif\n}\n\n/**\n * @brief Checks active solenoid to stop them, and to handle buzz mode\n *\n */\nvoid solenoid_check(void) {\n    uint16_t elapsed[NUMBER_OF_SOLENOIDS] = {0};\n\n    for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) {\n        if (!solenoid_on[i]) continue;\n\n        elapsed[i] = timer_elapsed(solenoid_start[i]);\n\n        // Check if it's time to finish this solenoid click cycle\n        if (elapsed[i] > haptic_config.dwell) {\n            solenoid_stop(i);\n            continue;\n        }\n\n        // Check whether to buzz the solenoid on and off\n        if (haptic_config.buzz) {\n            if ((elapsed[i] % (SOLENOID_BUZZ_ACTUATED + SOLENOID_BUZZ_NONACTUATED)) < SOLENOID_BUZZ_ACTUATED) {\n                if (!solenoid_buzzing[i]) {\n                    solenoid_buzzing[i] = true;\n                    gpio_write_pin(solenoid_pads[i], solenoid_active_state[i]);\n                }\n            } else {\n                if (solenoid_buzzing[i]) {\n                    solenoid_buzzing[i] = false;\n                    gpio_write_pin(solenoid_pads[i], !solenoid_active_state[i]);\n                }\n            }\n        }\n    }\n}\n\n/**\n * @brief Initial configuration for solenoids\n *\n */\nvoid solenoid_setup(void) {\n#ifdef SOLENOID_PINS_ACTIVE_STATE\n    bool    state_temp[] = SOLENOID_PINS_ACTIVE_STATE;\n    uint8_t bound_check  = ARRAY_SIZE(state_temp);\n#endif\n\n    for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) {\n#ifdef SOLENOID_PINS_ACTIVE_STATE\n        solenoid_active_state[i] = (bound_check - i) ? state_temp[i] : high;\n#else\n        solenoid_active_state[i] = high;\n#endif\n        gpio_write_pin(solenoid_pads[i], !solenoid_active_state[i]);\n        gpio_set_pin_output(solenoid_pads[i]);\n        if ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state_get_configure_state() == USB_DEVICE_STATE_CONFIGURED)) {\n            solenoid_fire(i);\n        }\n    }\n}\n\n/**\n * @brief stops solenoids prior to device reboot, to prevent them from being locked on\n *\n */\nvoid solenoid_shutdown(void) {\n    for (uint8_t i = 0; i < NUMBER_OF_SOLENOIDS; i++) {\n        gpio_write_pin(solenoid_pads[i], !solenoid_active_state[i]);\n    }\n}\n"
  },
  {
    "path": "drivers/haptic/solenoid.h",
    "content": "/* Copyright 2018 mtdjr - modified by ishtob\n * Driver for solenoid written for QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#ifndef SOLENOID_DEFAULT_DWELL\n#    define SOLENOID_DEFAULT_DWELL 12\n#endif\n\n#ifndef SOLENOID_MAX_DWELL\n#    define SOLENOID_MAX_DWELL 100\n#endif\n\n#ifndef SOLENOID_MIN_DWELL\n#    define SOLENOID_MIN_DWELL 4\n#endif\n\n#ifndef SOLENOID_DWELL_STEP_SIZE\n#    define SOLENOID_DWELL_STEP_SIZE 1\n#endif\n\n#ifndef SOLENOID_DEFAULT_BUZZ\n#    define SOLENOID_DEFAULT_BUZZ 0\n#endif\n\n#ifndef SOLENOID_BUZZ_ACTUATED\n#    define SOLENOID_BUZZ_ACTUATED SOLENOID_MIN_DWELL\n#endif\n\n#ifndef SOLENOID_BUZZ_NONACTUATED\n#    define SOLENOID_BUZZ_NONACTUATED SOLENOID_MIN_DWELL\n#endif\n\n#ifndef SOLENOID_PINS\n#    ifdef SOLENOID_PIN\n#        define SOLENOID_PINS \\\n            { SOLENOID_PIN }\n#    else\n#        error SOLENOID_PINS array not defined\n#    endif\n#endif\n\nvoid solenoid_buzz_on(void);\nvoid solenoid_buzz_off(void);\nvoid solenoid_set_buzz(uint8_t buzz);\n\nvoid solenoid_set_dwell(uint8_t dwell);\n\nvoid solenoid_stop(uint8_t index);\nvoid solenoid_fire(uint8_t index);\nvoid solenoid_fire_handler(void);\n\nvoid solenoid_check(void);\n\nvoid solenoid_setup(void);\nvoid solenoid_shutdown(void);\n"
  },
  {
    "path": "drivers/i2c_master.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n\n/**\n * \\file\n *\n * \\defgroup i2c_master I2C Master API\n *\n * \\brief API to communicate with I2C devices.\n * \\{\n */\n\ntypedef int16_t i2c_status_t;\n\n#define I2C_STATUS_SUCCESS (0)\n#define I2C_STATUS_ERROR (-1)\n#define I2C_STATUS_TIMEOUT (-2)\n\n#define I2C_TIMEOUT_IMMEDIATE (0)\n#define I2C_TIMEOUT_INFINITE (0xFFFF)\n\n/**\n * \\brief Initialize the I2C driver. This function must be called only once, before any of the below functions can be called.\n *\n * This function is weakly defined, meaning it can be overridden if necessary for your particular use case.\n */\nvoid i2c_init(void);\n\n/**\n * \\brief Send multiple bytes to the selected I2C device.\n *\n * \\param address The 7-bit I2C address of the device.\n * \\param data A pointer to the data to transmit.\n * \\param length The number of bytes to write. Take care not to overrun the length of `data`.\n * \\param timeout The time in milliseconds to wait for a response from the target device.\n *\n * \\return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.\n */\ni2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);\n\n/**\n * \\brief Send and receive multiple bytes to the selected I2C device.\n *\n * \\param address The 7-bit I2C address of the device.\n * \\param tx_data A pointer to the data to transmit.\n * \\param tx_length The number of bytes to write. Take care not to overrun the length of `tx_data`.\n * \\param rx_data A pointer to a buffer to read into.\n * \\param rx_length The number of bytes to read. Take care not to overrun the length of `rx_data`.\n * \\param timeout The time in milliseconds to wait for a response from the target device.\n *\n * \\return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.\n */\ni2c_status_t i2c_transmit_and_receive(uint8_t address, const uint8_t* tx_data, uint16_t tx_length, uint8_t* rx_data, uint16_t rx_length, uint16_t timeout);\n\n#if defined(__AVR__) || defined(__DOXYGEN__)\n/**\n * \\brief Send multiple bytes from PROGMEM to the selected I2C device.\n *\n * On ARM devices, this function is simply an alias for i2c_transmit(address, data, length, timeout).\n *\n * \\param address The 7-bit I2C address of the device.\n * \\param data A pointer to the data to transmit.\n * \\param length The number of bytes to write. Take care not to overrun the length of `data`.\n * \\param timeout The time in milliseconds to wait for a response from the target device.\n *\n * \\return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.\n */\ni2c_status_t i2c_transmit_P(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout);\n#else\n#    define i2c_transmit_P(address, data, length, timeout) i2c_transmit(address, data, length, timeout)\n#endif\n\n/**\n * \\brief Receive multiple bytes from the selected I2C device.\n *\n * \\param address The 7-bit I2C address of the device.\n * \\param data A pointer to a buffer to read into.\n * \\param length The number of bytes to read. Take care not to overrun the length of `data`.\n * \\param timeout The time in milliseconds to wait for a response from the target device.\n *\n * \\return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.\n */\ni2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout);\n\n/**\n * \\brief Write to a register with an 8-bit address on the I2C device.\n *\n * \\param devaddr The 7-bit I2C address of the device.\n * \\param regaddr The register address to write to.\n * \\param data A pointer to the data to transmit.\n * \\param length The number of bytes to write. Take care not to overrun the length of `data`.\n * \\param timeout The time in milliseconds to wait for a response from the target device.\n *\n * \\return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.\n */\ni2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);\n\n/**\n * \\brief Write to a register with a 16-bit address (big endian) on the I2C device.\n *\n * \\param devaddr The 7-bit I2C address of the device.\n * \\param regaddr The register address to write to.\n * \\param data A pointer to the data to transmit.\n * \\param length The number of bytes to write. Take care not to overrun the length of `data`.\n * \\param timeout The time in milliseconds to wait for a response from the target device.\n *\n * \\return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.\n */\ni2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout);\n\n/**\n * \\brief Read from a register with an 8-bit address on the I2C device.\n *\n * \\param devaddr The 7-bit I2C address of the device.\n * \\param regaddr The register address to read from.\n * \\param data A pointer to a buffer to read into.\n * \\param length The number of bytes to read. Take care not to overrun the length of `data`.\n * \\param timeout The time in milliseconds to wait for a response from the target device.\n *\n * \\return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.\n */\ni2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);\n\n/**\n * \\brief Read from a register with a 16-bit address (big endian) on the I2C device.\n *\n * \\param devaddr The 7-bit I2C address of the device.\n * \\param regaddr The register address to read from.\n * \\param data A pointer to a buffer to read into.\n * \\param length The number of bytes to read. Take care not to overrun the length of `data`.\n * \\param timeout The time in milliseconds to wait for a response from the target device.\n *\n * \\return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.\n */\ni2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout);\n\n/**\n * \\brief Ping the I2C bus for a specific address.\n *\n * On ChibiOS a \"best effort\" attempt is made by reading a single byte from register 0 at the given address. This should generally work except for I2C devices that do not not respond to a register 0 read request, which will result in a false negative result (unsuccessful response to ping attempt).\n *\n * This function is weakly defined, meaning it can be overridden if necessary for your particular use case.\n *\n * \\param address The 7-bit I2C address of the device.\n * \\param timeout The time in milliseconds to wait for a response from the target device.\n *\n * \\return `I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.\n */\ni2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout);\n\n/** \\} */\n"
  },
  {
    "path": "drivers/lcd/hd44780.c",
    "content": "/*\nCopyright 2022\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"hd44780.h\"\n#include \"gpio.h\"\n#include \"progmem.h\"\n#include \"wait.h\"\n\n#ifndef HD44780_DATA_PINS\n#    error hd44780: no data pins defined!\n#endif\n\n#ifndef HD44780_RS_PIN\n#    error hd44780: no RS pin defined!\n#endif\n\n#ifndef HD44780_RW_PIN\n#    error hd44780: no R/W pin defined!\n#endif\n\n#ifndef HD44780_E_PIN\n#    error hd44780: no E pin defined!\n#endif\n\nstatic const pin_t data_pins[4] = HD44780_DATA_PINS;\n\n#ifndef HD44780_DISPLAY_COLS\n#    define HD44780_DISPLAY_COLS 16\n#endif\n\n#ifndef HD44780_DISPLAY_LINES\n#    define HD44780_DISPLAY_LINES 2\n#endif\n\n#ifndef HD44780_DDRAM_LINE0_ADDR\n#    define HD44780_DDRAM_LINE0_ADDR 0x00\n#endif\n#ifndef HD44780_DDRAM_LINE1_ADDR\n#    define HD44780_DDRAM_LINE1_ADDR 0x40\n#endif\n\n#define HD44780_INIT_DELAY_MS 16\n#define HD44780_ENABLE_DELAY_US 1\n\nstatic void hd44780_latch(void) {\n    gpio_write_pin_high(HD44780_E_PIN);\n    wait_us(HD44780_ENABLE_DELAY_US);\n    gpio_write_pin_low(HD44780_E_PIN);\n}\n\nvoid hd44780_write(uint8_t data, bool isData) {\n    gpio_write_pin(HD44780_RS_PIN, isData);\n    gpio_write_pin_low(HD44780_RW_PIN);\n\n    for (int i = 0; i < 4; i++) {\n        gpio_set_pin_output(data_pins[i]);\n    }\n\n    // Write high nibble\n    for (int i = 0; i < 4; i++) {\n        gpio_write_pin(data_pins[i], (data >> 4) & (1 << i));\n    }\n    hd44780_latch();\n\n    // Write low nibble\n    for (int i = 0; i < 4; i++) {\n        gpio_write_pin(data_pins[i], data & (1 << i));\n    }\n    hd44780_latch();\n\n    for (int i = 0; i < 4; i++) {\n        gpio_write_pin_high(data_pins[i]);\n    }\n}\n\nuint8_t hd44780_read(bool isData) {\n    uint8_t data = 0;\n\n    gpio_write_pin(HD44780_RS_PIN, isData);\n    gpio_write_pin_high(HD44780_RW_PIN);\n\n    for (int i = 0; i < 4; i++) {\n        gpio_set_pin_input(data_pins[i]);\n    }\n\n    gpio_write_pin_high(HD44780_E_PIN);\n    wait_us(HD44780_ENABLE_DELAY_US);\n\n    // Read high nibble\n    for (int i = 0; i < 4; i++) {\n        data |= (gpio_read_pin(data_pins[i]) << i);\n    }\n\n    data <<= 4;\n\n    gpio_write_pin_low(HD44780_E_PIN);\n    wait_us(HD44780_ENABLE_DELAY_US);\n    gpio_write_pin_high(HD44780_E_PIN);\n    wait_us(HD44780_ENABLE_DELAY_US);\n\n    // Read low nibble\n    for (int i = 0; i < 4; i++) {\n        data |= (gpio_read_pin(data_pins[i]) << i);\n    }\n\n    gpio_write_pin_low(HD44780_E_PIN);\n\n    return data;\n}\n\nbool hd44780_busy(void) {\n    return hd44780_read(false) & HD44780_BUSY_FLAG;\n}\n\nvoid hd44780_command(uint8_t command) {\n    while (hd44780_busy())\n        ;\n    hd44780_write(command, false);\n}\n\nvoid hd44780_data(uint8_t data) {\n    while (hd44780_busy())\n        ;\n    hd44780_write(data, true);\n}\n\nvoid hd44780_clear(void) {\n    hd44780_command(HD44780_CMD_CLEAR_DISPLAY);\n}\n\nvoid hd44780_home(void) {\n    hd44780_command(HD44780_CMD_RETURN_HOME);\n}\n\nvoid hd44780_on(bool cursor, bool blink) {\n    if (cursor) {\n        if (blink) {\n            hd44780_command(HD44780_CMD_DISPLAY | HD44780_DISPLAY_ON | HD44780_DISPLAY_CURSOR | HD44780_DISPLAY_BLINK);\n        } else {\n            hd44780_command(HD44780_CMD_DISPLAY | HD44780_DISPLAY_ON | HD44780_DISPLAY_CURSOR);\n        }\n    } else {\n        hd44780_command(HD44780_CMD_DISPLAY | HD44780_DISPLAY_ON);\n    }\n}\n\nvoid hd44780_off(void) {\n    hd44780_command(HD44780_CMD_DISPLAY);\n}\n\nvoid hd44780_set_cgram_address(uint8_t address) {\n    hd44780_command(HD44780_CMD_SET_CGRAM_ADDRESS + (address & 0x3F));\n}\n\nvoid hd44780_set_ddram_address(uint8_t address) {\n    hd44780_command(HD44780_CMD_SET_DDRAM_ADDRESS + (address & 0x7F));\n}\n\nvoid hd44780_init(bool cursor, bool blink) {\n    gpio_set_pin_output(HD44780_RS_PIN);\n    gpio_set_pin_output(HD44780_RW_PIN);\n    gpio_set_pin_output(HD44780_E_PIN);\n\n    for (int i = 0; i < 4; i++) {\n        gpio_set_pin_output(data_pins[i]);\n    }\n\n    wait_ms(HD44780_INIT_DELAY_MS);\n\n    // Manually configure for 4-bit mode - can't use hd44780_command() yet\n    // HD44780U datasheet, Fig. 24 (p46)\n    gpio_write_pin_high(data_pins[0]); // Function set\n    gpio_write_pin_high(data_pins[1]); // DL = 1\n    hd44780_latch();\n    wait_ms(5);\n    // Send again\n    hd44780_latch();\n    wait_us(64);\n    // And again (?)\n    hd44780_latch();\n    wait_us(64);\n\n    gpio_write_pin_low(data_pins[0]); // DL = 0\n    hd44780_latch();\n    wait_us(64);\n\n#if HD44780_DISPLAY_LINES == 1\n    hd44780_command(HD44780_CMD_FUNCTION); // 4 bit, 1 line, 5x8 dots\n#else\n    hd44780_command(HD44780_CMD_FUNCTION | HD44780_FUNCTION_2_LINES); // 4 bit, 2 lines, 5x8 dots\n#endif\n    hd44780_on(cursor, blink);\n    hd44780_clear();\n    hd44780_home();\n    hd44780_command(HD44780_CMD_ENTRY_MODE | HD44780_ENTRY_MODE_INC);\n}\n\nvoid hd44780_set_cursor(uint8_t col, uint8_t line) {\n    register uint8_t address = col;\n\n#if HD44780_DISPLAY_LINES == 1\n    address += HD44780_DDRAM_LINE0_ADDR;\n#elif HD44780_DISPLAY_LINES == 2\n    if (line == 0) {\n        address += HD44780_DDRAM_LINE0_ADDR;\n    } else {\n        address += HD44780_DDRAM_LINE1_ADDR;\n    }\n#endif\n\n    hd44780_set_ddram_address(address);\n}\n\nvoid hd44780_define_char(uint8_t index, uint8_t *data) {\n    hd44780_set_cgram_address((index & 0x7) << 3);\n    for (uint8_t i = 0; i < 8; i++) {\n        hd44780_data(data[i]);\n    }\n}\n\nvoid hd44780_putc(char c) {\n    while (hd44780_busy())\n        ;\n    uint8_t current_position = hd44780_read(false);\n\n    if (c == '\\n') {\n        hd44780_set_cursor(0, current_position < HD44780_DDRAM_LINE1_ADDR ? 1 : 0);\n    } else {\n#if defined(HD44780_WRAP_LINES)\n#    if HD44780_DISPLAY_LINES == 1\n        if (current_position == HD44780_DDRAM_LINE0_ADDR + HD44780_DISPLAY_COLS) {\n            // Go to start of line\n            hd44780_set_cursor(0, 0);\n        }\n#    elif HD44780_DISPLAY_LINES == 2\n        if (current_position == HD44780_DDRAM_LINE0_ADDR + HD44780_DISPLAY_COLS) {\n            // Go to start of second line\n            hd44780_set_cursor(0, 1);\n        } else if (current_position == HD44780_DDRAM_LINE1_ADDR + HD44780_DISPLAY_COLS) {\n            // Go to start of first line\n            hd44780_set_cursor(0, 0);\n        }\n#    endif\n#endif\n        hd44780_data(c);\n    }\n}\n\nvoid hd44780_puts(const char *s) {\n    register char c;\n    while ((c = *s++)) {\n        hd44780_putc(c);\n    }\n}\n\n#if defined(__AVR__)\nvoid hd44780_define_char_P(uint8_t index, const uint8_t *data) {\n    hd44780_set_cgram_address(index << 3);\n    for (uint8_t i = 0; i < 8; i++) {\n        hd44780_data(pgm_read_byte(data++));\n    }\n}\n\nvoid hd44780_puts_P(const char *s) {\n    register char c;\n    while ((c = pgm_read_byte(s++))) {\n        hd44780_putc(c);\n    }\n}\n#endif\n"
  },
  {
    "path": "drivers/lcd/hd44780.h",
    "content": "/*\nCopyright 2022\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n/**\n * \\file\n *\n * \\defgroup hd44780 HD44780 Character LCD Driver\n * \\{\n */\n\n/*\n * HD44780 instructions\n * https://www.sparkfun.com/datasheets/LCD/HD44780.pdf\n * Table 6 (p24)\n */\n// Clear display\n#define HD44780_CMD_CLEAR_DISPLAY 0x01\n// Return home\n#define HD44780_CMD_RETURN_HOME 0x02\n// Entry mode set\n#define HD44780_CMD_ENTRY_MODE 0x04\n#define HD44780_ENTRY_MODE_INC 0x02   // I/D\n#define HD44780_ENTRY_MODE_SHIFT 0x01 // S\n// Display on/off control\n#define HD44780_CMD_DISPLAY 0x08\n#define HD44780_DISPLAY_ON 0x04     // D\n#define HD44780_DISPLAY_CURSOR 0x02 // C\n#define HD44780_DISPLAY_BLINK 0x01  // B\n// Cursor or display shift\n#define HD44780_CMD_MOVE 0x10\n#define HD44780_MOVE_DISPLAY 0x08 // S/C\n#define HD44780_MOVE_RIGHT 0x04   // R/L\n// Function set\n#define HD44780_CMD_FUNCTION 0x20\n#define HD44780_FUNCTION_8_BIT 0x10     // DL\n#define HD44780_FUNCTION_2_LINES 0x08   // N\n#define HD44780_FUNCTION_5X10_DOTS 0x04 // F\n// Set CGRAM address\n#define HD44780_CMD_SET_CGRAM_ADDRESS 0x40\n// Set DDRAM address\n#define HD44780_CMD_SET_DDRAM_ADDRESS 0x80\n\n// Bitmask for busy flag when reading\n#define HD44780_BUSY_FLAG 0x80\n\n/**\n * \\brief Write a byte to the display.\n *\n * \\param data The byte to send to the display.\n * \\param isData Whether the byte is an instruction or character data.\n */\nvoid hd44780_write(uint8_t data, bool isData);\n\n/**\n * \\brief Read a byte from the display.\n *\n * \\param isData Whether to read the current cursor position, or the character at the cursor.\n *\n * \\return If `isData` is `true`, the returned byte will be the character at the current DDRAM address. Otherwise, it will be the current DDRAM address and the busy flag.\n */\nuint8_t hd44780_read(bool isData);\n\n/**\n * \\brief Indicates whether the display is currently processing, and cannot accept instructions.\n *\n * \\return `true` if the display is busy.\n */\nbool hd44780_busy(void);\n\n/**\n * \\brief Send a command to the display. Refer to the datasheet for the valid commands.\n *\n * This function waits for the display to clear the busy flag before sending the command.\n *\n * \\param command The command to send.\n */\nvoid hd44780_command(uint8_t command);\n\n/**\n * \\brief Send a byte of data to the display.\n *\n * This function waits for the display to clear the busy flag before sending the data.\n *\n * \\param data The byte of data to send.\n */\nvoid hd44780_data(uint8_t data);\n\n/**\n * \\brief Clear the display.\n *\n * This function is called on init.\n */\nvoid hd44780_clear(void);\n\n/**\n * \\brief Move the cursor to the home position.\n *\n * This function is called on init.\n */\nvoid hd44780_home(void);\n\n/**\n * \\brief Turn the display on, and/or set the cursor position.\n *\n * This function is called on init.\n *\n * \\param cursor Whether to show the cursor.\n * \\param blink Whether to blink the cursor, if shown.\n */\nvoid hd44780_on(bool cursor, bool blink);\n\n/**\n * \\brief Turn the display off.\n */\nvoid hd44780_off(void);\n\n/**\n * \\brief Set the CGRAM address.\n *\n * This function is used when defining custom characters.\n *\n * \\param address The CGRAM address to move to, from `0x00` to `0x3F`.\n */\nvoid hd44780_set_cgram_address(uint8_t address);\n\n/**\n * \\brief Set the DDRAM address.\n *\n * This function is used when printing characters to the display, and setting the cursor.\n *\n * \\param address The DDRAM address to move to, from `0x00` to `0x7F`.\n */\nvoid hd44780_set_ddram_address(uint8_t address);\n\n/**\n * \\brief Initialize the display.\n *\n * This function should be called only once, before any of the other functions can be called.\n *\n * \\param cursor Whether to show the cursor.\n * \\param blink Whether to blink the cursor, if shown.\n */\nvoid hd44780_init(bool cursor, bool blink);\n\n/**\n * \\brief Move the cursor to the specified position on the display.\n *\n * \\param col The column number to move to, from 0 to 15 on 16x2 displays.\n * \\param line The line number to move to, either 0 or 1 on 16x2 displays.\n */\nvoid hd44780_set_cursor(uint8_t col, uint8_t line);\n\n/**\n * \\brief Define a custom character.\n *\n * \\param index The index of the custom character to define, from 0 to 7.\n * \\param data An array of 8 bytes containing the 5-bit row data of the character, where the first byte is the topmost row, and the least significant bit of each byte is the rightmost column.\n */\nvoid hd44780_define_char(uint8_t index, uint8_t *data);\n\n/**\n * \\brief Print a character to the display. The newline character will move the cursor to the start of the next line.\n *\n * The exact character shown may depend on the ROM code of your particular display - refer to the datasheet for the full character set.\n *\n * \\param c The character to print.\n */\nvoid hd44780_putc(char c);\n\n/**\n * \\brief Print a string of characters to the display.\n *\n * \\param s The string to print.\n */\nvoid hd44780_puts(const char *s);\n\n#if defined(__AVR__) || defined(__DOXYGEN__)\n/**\n * \\brief Define a custom character from PROGMEM.\n *\n * On ARM devices, this function is simply an alias of hd44780_define_char().\n *\n * \\param index The index of the custom character to define, from 0 to 7.\n * \\param data A PROGMEM array of 8 bytes containing the 5-bit row data of the character, where the first byte is the topmost row, and the least significant bit of each byte is the rightmost column.\n */\nvoid hd44780_define_char_P(uint8_t index, const uint8_t *data);\n\n/**\n * \\brief Print a string of characters from PROGMEM to the display.\n *\n * On ARM devices, this function is simply an alias of hd44780_puts().\n *\n * \\param s The PROGMEM string to print.\n */\nvoid hd44780_puts_P(const char *s);\n#else\n#    define hd44780_define_char_P(index, data) hd44780_define_char(index, data)\n#    define hd44780_puts_P(s) hd44780_puts(s)\n#endif\n\n/** \\} */\n"
  },
  {
    "path": "drivers/lcd/st7565.c",
    "content": "/*\nCopyright 2021\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"st7565.h\"\n\n#include <string.h>\n\n#include \"compiler_support.h\"\n#include \"keyboard.h\"\n#include \"progmem.h\"\n#include \"timer.h\"\n#include \"wait.h\"\n\n#include ST7565_FONT_H\n\n// Fundamental Commands\n#define CONTRAST 0x81\n#define DISPLAY_ALL_ON 0xA5\n#define DISPLAY_ALL_ON_RESUME 0xA4\n#define NORMAL_DISPLAY 0xA6\n#define INVERT_DISPLAY 0xA7\n#define DISPLAY_ON 0xAF\n#define DISPLAY_OFF 0xAE\n#define NOP 0xE3\n\n// Addressing Setting Commands\n#define PAM_SETCOLUMN_LSB 0x00\n#define PAM_SETCOLUMN_MSB 0x10\n#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7\n\n// Hardware Configuration Commands\n#define DISPLAY_START_LINE 0x40\n#define SEGMENT_REMAP 0xA0\n#define SEGMENT_REMAP_INV 0xA1\n#define COM_SCAN_INC 0xC0\n#define COM_SCAN_DEC 0xC8\n#define LCD_BIAS_7 0xA3\n#define LCD_BIAS_9 0xA2\n#define RESISTOR_RATIO 0x20\n#define POWER_CONTROL 0x28\n\n// Misc defines\n#ifndef ST7565_BLOCK_COUNT\n#    define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8)\n#endif\n#ifndef ST7565_BLOCK_SIZE\n#    define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT)\n#endif\n\n#define ST7565_ALL_BLOCKS_MASK (((((ST7565_BLOCK_TYPE)1 << (ST7565_BLOCK_COUNT - 1)) - 1) << 1) | 1)\n\n#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)\n\n// Display buffer's is the same as the display memory layout\n// this is so we don't end up with rounding errors with\n// parts of the display unusable or don't get cleared correctly\n// and also allows for drawing & inverting\nuint8_t            st7565_buffer[ST7565_MATRIX_SIZE];\nuint8_t *          st7565_cursor;\nST7565_BLOCK_TYPE  st7565_dirty       = 0;\nbool               st7565_initialized = false;\nbool               st7565_active      = false;\nbool               st7565_inverted    = false;\ndisplay_rotation_t st7565_rotation    = DISPLAY_ROTATION_0;\n#if ST7565_TIMEOUT > 0\nuint32_t st7565_timeout;\n#endif\n#if ST7565_UPDATE_INTERVAL > 0\nuint16_t st7565_update_timeout;\n#endif\n\n// Flips the rendering bits for a character at the current cursor position\nstatic void InvertCharacter(uint8_t *cursor) {\n    const uint8_t *end = cursor + ST7565_FONT_WIDTH;\n    while (cursor < end) {\n        *cursor = ~(*cursor);\n        cursor++;\n    }\n}\n\nbool st7565_init(display_rotation_t rotation) {\n    gpio_set_pin_output(ST7565_A0_PIN);\n    gpio_write_pin_high(ST7565_A0_PIN);\n    gpio_set_pin_output(ST7565_RST_PIN);\n    gpio_write_pin_high(ST7565_RST_PIN);\n\n    st7565_rotation = st7565_init_user(rotation);\n\n    spi_init();\n    spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);\n\n    st7565_reset();\n\n    st7565_send_cmd(LCD_BIAS_7);\n    if (!HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) {\n        st7565_send_cmd(SEGMENT_REMAP);\n        st7565_send_cmd(COM_SCAN_DEC);\n    } else {\n        st7565_send_cmd(SEGMENT_REMAP_INV);\n        st7565_send_cmd(COM_SCAN_INC);\n    }\n    st7565_send_cmd(DISPLAY_START_LINE | 0x00);\n    st7565_send_cmd(CONTRAST);\n    st7565_send_cmd(ST7565_CONTRAST);\n    st7565_send_cmd(RESISTOR_RATIO | 0x01);\n    st7565_send_cmd(POWER_CONTROL | 0x04);\n    wait_ms(50);\n    st7565_send_cmd(POWER_CONTROL | 0x06);\n    wait_ms(50);\n    st7565_send_cmd(POWER_CONTROL | 0x07);\n    wait_ms(10);\n    st7565_send_cmd(DISPLAY_ON);\n    st7565_send_cmd(DISPLAY_ALL_ON_RESUME);\n    st7565_send_cmd(NORMAL_DISPLAY);\n\n    spi_stop();\n\n#if ST7565_TIMEOUT > 0\n    st7565_timeout = timer_read32() + ST7565_TIMEOUT;\n#endif\n\n    st7565_clear();\n    st7565_initialized = true;\n    st7565_active      = true;\n    return true;\n}\n\n__attribute__((weak)) display_rotation_t st7565_init_user(display_rotation_t rotation) {\n    return rotation;\n}\n\nvoid st7565_clear(void) {\n    memset(st7565_buffer, 0, sizeof(st7565_buffer));\n    st7565_cursor = &st7565_buffer[0];\n    st7565_dirty  = ST7565_ALL_BLOCKS_MASK;\n}\n\nuint8_t crot(uint8_t a, int8_t n) {\n    const uint8_t mask = 0x7;\n    n &= mask;\n    return a << n | a >> (-n & mask);\n}\n\nvoid st7565_render(void) {\n    if (!st7565_initialized) {\n        return;\n    }\n\n    // Do we have work to do?\n    st7565_dirty &= ST7565_ALL_BLOCKS_MASK;\n    if (!st7565_dirty) {\n        return;\n    }\n\n    // Find first dirty block\n    uint8_t update_start = 0;\n    while (!(st7565_dirty & ((ST7565_BLOCK_TYPE)1 << update_start))) {\n        ++update_start;\n    }\n\n    // Calculate commands to set memory addressing bounds.\n    uint8_t start_page   = ST7565_BLOCK_SIZE * update_start / ST7565_DISPLAY_WIDTH;\n    uint8_t start_column = ST7565_BLOCK_SIZE * update_start % ST7565_DISPLAY_WIDTH;\n    // IC has 132 segment drivers, for panels with less width we need to offset the starting column\n    if (HAS_FLAGS(st7565_rotation, DISPLAY_ROTATION_180)) {\n        start_column += (132 - ST7565_DISPLAY_WIDTH);\n    }\n\n    spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);\n\n    st7565_send_cmd(PAM_PAGE_ADDR | start_page);\n    st7565_send_cmd(PAM_SETCOLUMN_LSB | ((ST7565_COLUMN_OFFSET + start_column) & 0x0f));\n    st7565_send_cmd(PAM_SETCOLUMN_MSB | ((ST7565_COLUMN_OFFSET + start_column) >> 4 & 0x0f));\n\n    st7565_send_data(&st7565_buffer[ST7565_BLOCK_SIZE * update_start], ST7565_BLOCK_SIZE);\n\n    spi_stop();\n\n    // Turn on display if it is off\n    st7565_on();\n\n    // Clear dirty flag\n    st7565_dirty &= ~((ST7565_BLOCK_TYPE)1 << update_start);\n}\n\nvoid st7565_set_cursor(uint8_t col, uint8_t line) {\n    uint16_t index = line * ST7565_DISPLAY_WIDTH + col * ST7565_FONT_WIDTH;\n\n    // Out of bounds?\n    if (index >= ST7565_MATRIX_SIZE) {\n        index = 0;\n    }\n\n    st7565_cursor = &st7565_buffer[index];\n}\n\nvoid st7565_advance_page(bool clearPageRemainder) {\n    uint16_t index     = st7565_cursor - &st7565_buffer[0];\n    uint8_t  remaining = ST7565_DISPLAY_WIDTH - (index % ST7565_DISPLAY_WIDTH);\n\n    if (clearPageRemainder) {\n        // Remaining Char count\n        remaining = remaining / ST7565_FONT_WIDTH;\n\n        // Write empty character until next line\n        while (remaining--)\n            st7565_write_char(' ', false);\n    } else {\n        // Next page index out of bounds?\n        if (index + remaining >= ST7565_MATRIX_SIZE) {\n            index     = 0;\n            remaining = 0;\n        }\n\n        st7565_cursor = &st7565_buffer[index + remaining];\n    }\n}\n\nvoid st7565_advance_char(void) {\n    uint16_t nextIndex      = st7565_cursor - &st7565_buffer[0] + ST7565_FONT_WIDTH;\n    uint8_t  remainingSpace = ST7565_DISPLAY_WIDTH - (nextIndex % ST7565_DISPLAY_WIDTH);\n\n    // Do we have enough space on the current line for the next character\n    if (remainingSpace < ST7565_FONT_WIDTH) {\n        nextIndex += remainingSpace;\n    }\n\n    // Did we go out of bounds\n    if (nextIndex >= ST7565_MATRIX_SIZE) {\n        nextIndex = 0;\n    }\n\n    // Update cursor position\n    st7565_cursor = &st7565_buffer[nextIndex];\n}\n\n// Main handler that writes character data to the display buffer\nvoid st7565_write_char(const char data, bool invert) {\n    // Advance to the next line if newline\n    if (data == '\\n') {\n        // Old source wrote ' ' until end of line...\n        st7565_advance_page(true);\n        return;\n    }\n\n    if (data == '\\r') {\n        st7565_advance_page(false);\n        return;\n    }\n\n    // copy the current render buffer to check for dirty after\n    static uint8_t st7565_temp_buffer[ST7565_FONT_WIDTH];\n    memcpy(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH);\n\n    STATIC_ASSERT(sizeof(font) >= ((ST7565_FONT_END + 1 - ST7565_FONT_START) * ST7565_FONT_WIDTH), \"ST7565_FONT_END references outside array\");\n\n    // set the reder buffer data\n    uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index\n    if (cast_data < ST7565_FONT_START || cast_data > ST7565_FONT_END) {\n        memset(st7565_cursor, 0x00, ST7565_FONT_WIDTH);\n    } else {\n        const uint8_t *glyph = &font[(cast_data - ST7565_FONT_START) * ST7565_FONT_WIDTH];\n        memcpy_P(st7565_cursor, glyph, ST7565_FONT_WIDTH);\n    }\n\n    // Invert if needed\n    if (invert) {\n        InvertCharacter(st7565_cursor);\n    }\n\n    // Dirty check\n    if (memcmp(&st7565_temp_buffer, st7565_cursor, ST7565_FONT_WIDTH)) {\n        uint16_t index = st7565_cursor - &st7565_buffer[0];\n        st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));\n        // Edgecase check if the written data spans the 2 chunks\n        st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << ((index + ST7565_FONT_WIDTH - 1) / ST7565_BLOCK_SIZE));\n    }\n\n    // Finally move to the next char\n    st7565_advance_char();\n}\n\nvoid st7565_write(const char *data, bool invert) {\n    const char *end = data + strlen(data);\n    while (data < end) {\n        st7565_write_char(*data, invert);\n        data++;\n    }\n}\n\nvoid st7565_write_ln(const char *data, bool invert) {\n    st7565_write(data, invert);\n    st7565_advance_page(true);\n}\n\nvoid st7565_pan(bool left) {\n    uint16_t i = 0;\n    for (uint16_t y = 0; y < ST7565_DISPLAY_HEIGHT / 8; y++) {\n        if (left) {\n            for (uint16_t x = 0; x < ST7565_DISPLAY_WIDTH - 1; x++) {\n                i                = y * ST7565_DISPLAY_WIDTH + x;\n                st7565_buffer[i] = st7565_buffer[i + 1];\n            }\n        } else {\n            for (uint16_t x = ST7565_DISPLAY_WIDTH - 1; x > 0; x--) {\n                i                = y * ST7565_DISPLAY_WIDTH + x;\n                st7565_buffer[i] = st7565_buffer[i - 1];\n            }\n        }\n    }\n    st7565_dirty = ST7565_ALL_BLOCKS_MASK;\n}\n\ndisplay_buffer_reader_t st7565_read_raw(uint16_t start_index) {\n    if (start_index > ST7565_MATRIX_SIZE) start_index = ST7565_MATRIX_SIZE;\n    display_buffer_reader_t ret_reader;\n    ret_reader.current_element         = &st7565_buffer[start_index];\n    ret_reader.remaining_element_count = ST7565_MATRIX_SIZE - start_index;\n    return ret_reader;\n}\n\nvoid st7565_write_raw_byte(const char data, uint16_t index) {\n    if (index > ST7565_MATRIX_SIZE) index = ST7565_MATRIX_SIZE;\n    if (st7565_buffer[index] == data) return;\n    st7565_buffer[index] = data;\n    st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));\n}\n\nvoid st7565_write_raw(const char *data, uint16_t size) {\n    uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0];\n    if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index;\n    for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {\n        uint8_t c = *data++;\n        if (st7565_buffer[i] == c) continue;\n        st7565_buffer[i] = c;\n        st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE));\n    }\n}\n\nvoid st7565_write_pixel(uint8_t x, uint8_t y, bool on) {\n    if (x >= ST7565_DISPLAY_WIDTH) {\n        return;\n    }\n    uint16_t index = x + (y / 8) * ST7565_DISPLAY_WIDTH;\n    if (index >= ST7565_MATRIX_SIZE) {\n        return;\n    }\n    uint8_t data = st7565_buffer[index];\n    if (on) {\n        data |= (1 << (y % 8));\n    } else {\n        data &= ~(1 << (y % 8));\n    }\n    if (st7565_buffer[index] != data) {\n        st7565_buffer[index] = data;\n        st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (index / ST7565_BLOCK_SIZE));\n    }\n}\n\n#if defined(__AVR__)\nvoid st7565_write_P(const char *data, bool invert) {\n    uint8_t c = pgm_read_byte(data);\n    while (c != 0) {\n        st7565_write_char(c, invert);\n        c = pgm_read_byte(++data);\n    }\n}\n\nvoid st7565_write_ln_P(const char *data, bool invert) {\n    st7565_write_P(data, invert);\n    st7565_advance_page(true);\n}\n\nvoid st7565_write_raw_P(const char *data, uint16_t size) {\n    uint16_t cursor_start_index = st7565_cursor - &st7565_buffer[0];\n    if ((size + cursor_start_index) > ST7565_MATRIX_SIZE) size = ST7565_MATRIX_SIZE - cursor_start_index;\n    for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {\n        uint8_t c = pgm_read_byte(data++);\n        if (st7565_buffer[i] == c) continue;\n        st7565_buffer[i] = c;\n        st7565_dirty |= ((ST7565_BLOCK_TYPE)1 << (i / ST7565_BLOCK_SIZE));\n    }\n}\n#endif // defined(__AVR__)\n\nbool st7565_on(void) {\n    if (!st7565_initialized) {\n        return st7565_active;\n    }\n\n#if ST7565_TIMEOUT > 0\n    st7565_timeout = timer_read32() + ST7565_TIMEOUT;\n#endif\n\n    if (!st7565_active) {\n        spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);\n        st7565_send_cmd(DISPLAY_ON);\n        spi_stop();\n        st7565_active = true;\n        st7565_on_user();\n    }\n    return st7565_active;\n}\n\n__attribute__((weak)) void st7565_on_user(void) {}\n\nbool st7565_off(void) {\n    if (!st7565_initialized) {\n        return !st7565_active;\n    }\n\n    if (st7565_active) {\n        spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);\n        st7565_send_cmd(DISPLAY_OFF);\n        spi_stop();\n        st7565_active = false;\n        st7565_off_user();\n    }\n    return !st7565_active;\n}\n\n__attribute__((weak)) void st7565_off_user(void) {}\n\nbool st7565_is_on(void) {\n    return st7565_active;\n}\n\nbool st7565_invert(bool invert) {\n    if (!st7565_initialized) {\n        return st7565_inverted;\n    }\n\n    if (invert != st7565_inverted) {\n        spi_start(ST7565_SS_PIN, false, 0, ST7565_SPI_CLK_DIVISOR);\n        st7565_send_cmd(invert ? INVERT_DISPLAY : NORMAL_DISPLAY);\n        spi_stop();\n        st7565_inverted = invert;\n    }\n    return st7565_inverted;\n}\n\nuint8_t st7565_max_chars(void) {\n    return ST7565_DISPLAY_WIDTH / ST7565_FONT_WIDTH;\n}\n\nuint8_t st7565_max_lines(void) {\n    return ST7565_DISPLAY_HEIGHT / ST7565_FONT_HEIGHT;\n}\n\nvoid st7565_task(void) {\n    if (!st7565_initialized) {\n        return;\n    }\n\n#if ST7565_UPDATE_INTERVAL > 0\n    if (timer_elapsed(st7565_update_timeout) >= ST7565_UPDATE_INTERVAL) {\n        st7565_update_timeout = timer_read();\n        st7565_set_cursor(0, 0);\n        st7565_task_user();\n    }\n#else\n    st7565_set_cursor(0, 0);\n    st7565_task_user();\n#endif\n\n    // Smart render system, no need to check for dirty\n    st7565_render();\n\n    // Display timeout check\n#if ST7565_TIMEOUT > 0\n    if (st7565_active && timer_expired32(timer_read32(), st7565_timeout)) {\n        st7565_off();\n    }\n#endif\n}\n\n__attribute__((weak)) void st7565_task_user(void) {}\n\nvoid st7565_reset(void) {\n    gpio_write_pin_low(ST7565_RST_PIN);\n    wait_ms(20);\n    gpio_write_pin_high(ST7565_RST_PIN);\n    wait_ms(20);\n}\n\nspi_status_t st7565_send_cmd(uint8_t cmd) {\n    gpio_write_pin_low(ST7565_A0_PIN);\n    return spi_write(cmd);\n}\n\nspi_status_t st7565_send_data(uint8_t *data, uint16_t length) {\n    gpio_write_pin_high(ST7565_A0_PIN);\n    return spi_transmit(data, length);\n}\n"
  },
  {
    "path": "drivers/lcd/st7565.h",
    "content": "/*\nCopyright 2021\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"spi_master.h\"\n\n#ifndef ST7565_DISPLAY_WIDTH\n#    define ST7565_DISPLAY_WIDTH 128\n#endif\n#ifndef ST7565_DISPLAY_HEIGHT\n#    define ST7565_DISPLAY_HEIGHT 32\n#endif\n#ifndef ST7565_MATRIX_SIZE\n#    define ST7565_MATRIX_SIZE (ST7565_DISPLAY_HEIGHT / 8 * ST7565_DISPLAY_WIDTH) // 1024 (compile time mathed)\n#endif\n#ifndef ST7565_BLOCK_TYPE\n#    define ST7565_BLOCK_TYPE uint16_t\n#endif\n#ifndef ST7565_BLOCK_COUNT\n#    define ST7565_BLOCK_COUNT (sizeof(ST7565_BLOCK_TYPE) * 8) // 32 (compile time mathed)\n#endif\n#ifndef ST7565_BLOCK_SIZE\n#    define ST7565_BLOCK_SIZE (ST7565_MATRIX_SIZE / ST7565_BLOCK_COUNT) // 32 (compile time mathed)\n#endif\n\n// the column address corresponding to the first column in the display hardware\n#if !defined(ST7565_COLUMN_OFFSET)\n#    define ST7565_COLUMN_OFFSET 0\n#endif\n\n// spi clock divisor\n#if !defined(ST7565_SPI_CLK_DIVISOR)\n#    define ST7565_SPI_CLK_DIVISOR 4\n#endif\n\n// Custom font file to use\n#if !defined(ST7565_FONT_H)\n#    define ST7565_FONT_H \"glcdfont.c\"\n#endif\n// unsigned char value of the first character in the font file\n#if !defined(ST7565_FONT_START)\n#    define ST7565_FONT_START 0\n#endif\n// unsigned char value of the last character in the font file\n#if !defined(ST7565_FONT_END)\n#    define ST7565_FONT_END 223\n#endif\n// Font render width\n#if !defined(ST7565_FONT_WIDTH)\n#    define ST7565_FONT_WIDTH 6\n#endif\n// Font render height\n#if !defined(ST7565_FONT_HEIGHT)\n#    define ST7565_FONT_HEIGHT 8\n#endif\n// Default contrast level\n#if !defined(ST7565_CONTRAST)\n#    define ST7565_CONTRAST 32\n#endif\n\n#if !defined(ST7565_TIMEOUT)\n#    if defined(ST7565_DISABLE_TIMEOUT)\n#        define ST7565_TIMEOUT 0\n#    else\n#        define ST7565_TIMEOUT 60000\n#    endif\n#endif\n\n#if !defined(ST7565_UPDATE_INTERVAL) && defined(SPLIT_KEYBOARD)\n#    define ST7565_UPDATE_INTERVAL 50\n#endif\n\ntypedef struct __attribute__((__packed__)) {\n    uint8_t *current_element;\n    uint16_t remaining_element_count;\n} display_buffer_reader_t;\n\n// Rotation enum values are flags\ntypedef enum { DISPLAY_ROTATION_0, DISPLAY_ROTATION_180 } display_rotation_t;\n\n// Initialize the display, rotating the rendered output based on the define passed in.\n// Returns true if the display was initialized successfully\nbool st7565_init(display_rotation_t rotation);\n\n// Called at the start of st7565_init, weak function overridable by the user\n// rotation - the value passed into st7565_init\n// Return new display_rotation_t if you want to override default rotation\ndisplay_rotation_t st7565_init_user(display_rotation_t rotation);\n\n// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering\nvoid st7565_clear(void);\n\n// Renders the dirty chunks of the buffer to display\nvoid st7565_render(void);\n\n// Moves cursor to character position indicated by column and line, wraps if out of bounds\n// Max column denoted by 'st7565_max_chars()' and max lines by 'st7565_max_lines()' functions\nvoid st7565_set_cursor(uint8_t col, uint8_t line);\n\n// Advances the cursor to the next page, writing ' ' if true\n// Wraps to the begining when out of bounds\nvoid st7565_advance_page(bool clearPageRemainder);\n\n// Moves the cursor forward 1 character length\n// Advance page if there is not enough room for the next character\n// Wraps to the begining when out of bounds\nvoid st7565_advance_char(void);\n\n// Writes a single character to the buffer at current cursor position\n// Advances the cursor while writing, inverts the pixels if true\n// Main handler that writes character data to the display buffer\nvoid st7565_write_char(const char data, bool invert);\n\n// Writes a string to the buffer at current cursor position\n// Advances the cursor while writing, inverts the pixels if true\nvoid st7565_write(const char *data, bool invert);\n\n// Writes a string to the buffer at current cursor position\n// Advances the cursor while writing, inverts the pixels if true\n// Advances the cursor to the next page, wiring ' ' to the remainder of the current page\nvoid st7565_write_ln(const char *data, bool invert);\n\n// Pans the buffer to the right (or left by passing true) by moving contents of the buffer\n// Useful for moving the screen in preparation for new drawing\nvoid st7565_pan(bool left);\n\n// Returns a pointer to the requested start index in the buffer plus remaining\n// buffer length as struct\ndisplay_buffer_reader_t st7565_read_raw(uint16_t start_index);\n\n// Writes a string to the buffer at current cursor position\nvoid st7565_write_raw(const char *data, uint16_t size);\n\n// Writes a single byte into the buffer at the specified index\nvoid st7565_write_raw_byte(const char data, uint16_t index);\n\n// Sets a specific pixel on or off\n// Coordinates start at top-left and go right and down for positive x and y\nvoid st7565_write_pixel(uint8_t x, uint8_t y, bool on);\n\n#if defined(__AVR__)\n// Writes a PROGMEM string to the buffer at current cursor position\n// Advances the cursor while writing, inverts the pixels if true\n// Remapped to call 'void st7565_write(const char *data, bool invert);' on ARM\nvoid st7565_write_P(const char *data, bool invert);\n\n// Writes a PROGMEM string to the buffer at current cursor position\n// Advances the cursor while writing, inverts the pixels if true\n// Advances the cursor to the next page, wiring ' ' to the remainder of the current page\n// Remapped to call 'void st7565_write_ln(const char *data, bool invert);' on ARM\nvoid st7565_write_ln_P(const char *data, bool invert);\n\n// Writes a PROGMEM string to the buffer at current cursor position\nvoid st7565_write_raw_P(const char *data, uint16_t size);\n#else\n#    define st7565_write_P(data, invert) st7565_write(data, invert)\n#    define st7565_write_ln_P(data, invert) st7565_write_ln(data, invert)\n#    define st7565_write_raw_P(data, size) st7565_write_raw(data, size)\n#endif // defined(__AVR__)\n\n// Can be used to manually turn on the screen if it is off\n// Returns true if the screen was on or turns on\nbool st7565_on(void);\n\n// Called when st7565_on() turns on the screen, weak function overridable by the user\n// Not called if the screen is already on\nvoid st7565_on_user(void);\n\n// Can be used to manually turn off the screen if it is on\n// Returns true if the screen was off or turns off\nbool st7565_off(void);\n\n// Called when st7565_off() turns off the screen, weak function overridable by the user\n// Not called if the screen is already off\nvoid st7565_off_user(void);\n\n// Returns true if the screen is currently on, false if it is\n// not\nbool st7565_is_on(void);\n\n// Basically it's st7565_render, but with timeout management and st7565_task_user calling!\nvoid st7565_task(void);\n\n// Called at the start of st7565_task, weak function overridable by the user\nvoid st7565_task_user(void);\n\n// Inverts the display\n// Returns true if the screen was or is inverted\nbool st7565_invert(bool invert);\n\n// Returns the maximum number of characters that will fit on a line\nuint8_t st7565_max_chars(void);\n\n// Returns the maximum number of lines that will fit on the display\nuint8_t st7565_max_lines(void);\n\nvoid st7565_reset(void);\n\nspi_status_t st7565_send_cmd(uint8_t cmd);\n\nspi_status_t st7565_send_data(uint8_t *data, uint16_t length);\n"
  },
  {
    "path": "drivers/led/apa102.c",
    "content": "/* Copyright 2020 Aldehir Rojas\n * Copyright 2017 Mikkel (Duckle29)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"apa102.h\"\n#include \"gpio.h\"\n\n#ifndef APA102_NOPS\n#    if defined(__AVR__)\n#        define APA102_NOPS 0 // AVR at 16 MHz already spends 62.5 ns per clock, so no extra delay is needed\n#    elif defined(PROTOCOL_CHIBIOS)\n#        include \"hal.h\"\n#        include \"chibios_config.h\"\n#        if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) || defined(AT32F415) || defined(GD32VF103) || defined(MCU_RP)\n#            define APA102_NOPS (100 / (1000000000L / (CPU_CLOCK / 4))) // This calculates how many loops of 4 nops to run to delay 100 ns\n#        else\n#            error APA102_NOPS configuration required\n#            define APA102_NOPS 0 // this just pleases the compile so the above error is easier to spot\n#        endif\n#    endif\n#endif\n\n#define io_wait                                 \\\n    do {                                        \\\n        for (int i = 0; i < APA102_NOPS; i++) { \\\n            __asm__ volatile(\"nop\\n\\t\"          \\\n                             \"nop\\n\\t\"          \\\n                             \"nop\\n\\t\"          \\\n                             \"nop\\n\\t\");        \\\n        }                                       \\\n    } while (0)\n\n#define APA102_SEND_BIT(byte, bit)                        \\\n    do {                                                  \\\n        gpio_write_pin(APA102_DI_PIN, (byte >> bit) & 1); \\\n        io_wait;                                          \\\n        gpio_write_pin_high(APA102_CI_PIN);               \\\n        io_wait;                                          \\\n        gpio_write_pin_low(APA102_CI_PIN);                \\\n        io_wait;                                          \\\n    } while (0)\n\nrgb_t   apa102_leds[APA102_LED_COUNT];\nuint8_t apa102_led_brightness = APA102_DEFAULT_BRIGHTNESS;\n\nstatic void apa102_send_byte(uint8_t byte) {\n    APA102_SEND_BIT(byte, 7);\n    APA102_SEND_BIT(byte, 6);\n    APA102_SEND_BIT(byte, 5);\n    APA102_SEND_BIT(byte, 4);\n    APA102_SEND_BIT(byte, 3);\n    APA102_SEND_BIT(byte, 2);\n    APA102_SEND_BIT(byte, 1);\n    APA102_SEND_BIT(byte, 0);\n}\n\nstatic void apa102_start_frame(void) {\n    gpio_write_pin_low(APA102_DI_PIN);\n    gpio_write_pin_low(APA102_CI_PIN);\n\n    for (uint16_t i = 0; i < 4; i++) {\n        apa102_send_byte(0);\n    }\n}\n\nstatic void apa102_end_frame(uint16_t num_leds) {\n    // This function has been taken from: https://github.com/pololu/apa102-arduino/blob/master/APA102.h\n    // and adapted. The code is MIT licensed. I think thats compatible?\n    //\n    // The data stream seen by the last LED in the chain will be delayed by\n    // (count - 1) clock edges, because each LED before it inverts the clock\n    // line and delays the data by one clock edge.  Therefore, to make sure\n    // the last LED actually receives the data we wrote, the number of extra\n    // edges we send at the end of the frame must be at least (count - 1).\n    //\n    // Assuming we only want to send these edges in groups of size K, the\n    // C/C++ expression for the minimum number of groups to send is:\n    //\n    //   ((count - 1) + (K - 1)) / K\n    //\n    // The C/C++ expression above is just (count - 1) divided by K,\n    // rounded up to the nearest whole number if there is a remainder.\n    //\n    // We set K to 16 and use the formula above as the number of frame-end\n    // bytes to transfer.  Each byte has 16 clock edges.\n    //\n    // We are ignoring the specification for the end frame in the APA102\n    // datasheet, which says to send 0xFF four times, because it does not work\n    // when you have 66 LEDs or more, and also it results in unwanted white\n    // pixels if you try to update fewer LEDs than are on your LED strip.\n    uint16_t iterations = (num_leds + 14) / 16;\n    for (uint16_t i = 0; i < iterations; i++) {\n        apa102_send_byte(0);\n    }\n\n    gpio_write_pin_low(APA102_DI_PIN);\n    gpio_write_pin_low(APA102_CI_PIN);\n}\n\nstatic void apa102_send_frame(uint8_t red, uint8_t green, uint8_t blue, uint8_t brightness) {\n    apa102_send_byte(0b11100000 | brightness);\n    apa102_send_byte(blue);\n    apa102_send_byte(green);\n    apa102_send_byte(red);\n}\n\nvoid apa102_init(void) {\n    gpio_set_pin_output(APA102_DI_PIN);\n    gpio_set_pin_output(APA102_CI_PIN);\n}\n\nvoid apa102_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    apa102_leds[index].r = red;\n    apa102_leds[index].g = green;\n    apa102_leds[index].b = blue;\n}\n\nvoid apa102_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (uint16_t i = 0; i < APA102_LED_COUNT; i++) {\n        apa102_set_color(i, red, green, blue);\n    }\n}\n\nvoid apa102_flush(void) {\n    apa102_start_frame();\n    for (uint8_t i = 0; i < APA102_LED_COUNT; i++) {\n        apa102_send_frame(apa102_leds[i].r, apa102_leds[i].g, apa102_leds[i].b, apa102_led_brightness);\n    }\n    apa102_end_frame(APA102_LED_COUNT);\n}\n\nvoid apa102_set_brightness(uint8_t brightness) {\n    if (brightness > APA102_MAX_BRIGHTNESS) {\n        apa102_led_brightness = APA102_MAX_BRIGHTNESS;\n    } else if (brightness < 0) {\n        apa102_led_brightness = 0;\n    } else {\n        apa102_led_brightness = brightness;\n    }\n}\n"
  },
  {
    "path": "drivers/led/apa102.h",
    "content": "/* Copyright 2020 Aldehir Rojas\n * Copyright 2017 Mikkel (Duckle29)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"color.h\"\n\n#if defined(RGBLIGHT_APA102)\n#    define APA102_LED_COUNT RGBLIGHT_LED_COUNT\n#elif defined(RGB_MATRIX_APA102)\n#    define APA102_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#ifndef APA102_DEFAULT_BRIGHTNESS\n#    define APA102_DEFAULT_BRIGHTNESS 31\n#endif\n\n#define APA102_MAX_BRIGHTNESS 31\n\nvoid apa102_init(void);\nvoid apa102_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid apa102_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\nvoid apa102_flush(void);\n\nvoid apa102_set_brightness(uint8_t brightness);\n"
  },
  {
    "path": "drivers/led/aw20216s.c",
    "content": "/* Copyright 2021 Jasper Chan\n *           2023 Huckies <https://github.com/Huckies>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"aw20216s.h\"\n#include \"wait.h\"\n#include \"spi_master.h\"\n\n#define AW20216S_PWM_REGISTER_COUNT 216\n\n#ifndef AW20216S_CONFIGURATION\n#    define AW20216S_CONFIGURATION (AW20216S_CONFIGURATION_SWSEL_1_12 | AW20216S_CONFIGURATION_CHIPEN)\n#endif\n\n#ifndef AW20216S_MIX_FUNCTION\n#    define AW20216S_MIX_FUNCTION (AW20216S_MIX_FUNCTION_LPEN)\n#endif\n\n#ifndef AW20216S_SCALING_MAX\n#    define AW20216S_SCALING_MAX 150\n#endif\n\n#ifndef AW20216S_GLOBAL_CURRENT_MAX\n#    define AW20216S_GLOBAL_CURRENT_MAX 150\n#endif\n\n#ifndef AW20216S_SPI_MODE\n#    define AW20216S_SPI_MODE 0\n#endif\n\n#ifndef AW20216S_SPI_DIVISOR\n#    define AW20216S_SPI_DIVISOR 4\n#endif\n\ntypedef struct aw20216s_driver_t {\n    uint8_t pwm_buffer[AW20216S_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n} PACKED aw20216s_driver_t;\n\naw20216s_driver_t driver_buffers[AW20216S_DRIVER_COUNT] = {{\n    .pwm_buffer       = {0},\n    .pwm_buffer_dirty = false,\n}};\n\nbool aw20216s_write(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t* data, uint8_t len) {\n    static uint8_t s_spi_transfer_buffer[2] = {0};\n\n    if (!spi_start(cs_pin, false, AW20216S_SPI_MODE, AW20216S_SPI_DIVISOR)) {\n        spi_stop();\n        return false;\n    }\n\n    s_spi_transfer_buffer[0] = (AW20216S_ID | page | AW20216S_WRITE);\n    s_spi_transfer_buffer[1] = reg;\n\n    if (spi_transmit(s_spi_transfer_buffer, 2) != SPI_STATUS_SUCCESS) {\n        spi_stop();\n        return false;\n    }\n\n    if (spi_transmit(data, len) != SPI_STATUS_SUCCESS) {\n        spi_stop();\n        return false;\n    }\n\n    spi_stop();\n    return true;\n}\n\nstatic inline bool aw20216s_write_register(pin_t cs_pin, uint8_t page, uint8_t reg, uint8_t value) {\n    // Little wrapper so callers need not care about sending a buffer\n    return aw20216s_write(cs_pin, page, reg, &value, 1);\n}\n\nvoid aw20216s_soft_reset(pin_t cs_pin) {\n    aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_FUNCTION_REG_RESET, AW20216S_RESET_MAGIC);\n}\n\nstatic void aw20216s_init_scaling(pin_t cs_pin) {\n    // Set constant current to the max, control brightness with PWM\n    for (uint8_t i = 0; i < AW20216S_PWM_REGISTER_COUNT; i++) {\n        aw20216s_write_register(cs_pin, AW20216S_PAGE_SCALING, i, AW20216S_SCALING_MAX);\n    }\n}\n\nstatic inline void aw20216s_init_current_limit(pin_t cs_pin) {\n    // Push config\n    aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_FUNCTION_REG_GLOBAL_CURRENT, AW20216S_GLOBAL_CURRENT_MAX);\n}\n\nstatic inline void aw20216s_soft_enable(pin_t cs_pin) {\n    // Push config\n    aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_FUNCTION_REG_CONFIGURATION, AW20216S_CONFIGURATION);\n}\n\nstatic inline void aw20216s_auto_lowpower(pin_t cs_pin) {\n    aw20216s_write_register(cs_pin, AW20216S_PAGE_FUNCTION, AW20216S_FUNCTION_REG_MIX_FUNCTION, AW20216S_MIX_FUNCTION);\n}\n\nvoid aw20216s_init_drivers(void) {\n    spi_init();\n\n#if defined(AW20216S_EN_PIN)\n    gpio_set_pin_output(AW20216S_EN_PIN);\n    gpio_write_pin_high(AW20216S_EN_PIN);\n#endif\n\n    aw20216s_init(AW20216S_CS_PIN_1);\n#if defined(AW20216S_CS_PIN_2)\n    aw20216s_init(AW20216S_CS_PIN_2);\n#endif\n}\n\nvoid aw20216s_init(pin_t cs_pin) {\n    aw20216s_soft_reset(cs_pin);\n    wait_ms(2);\n\n    // Drivers should start with all scaling and PWM registers as off\n    aw20216s_init_current_limit(cs_pin);\n    aw20216s_init_scaling(cs_pin);\n\n    aw20216s_soft_enable(cs_pin);\n    aw20216s_auto_lowpower(cs_pin);\n}\n\nvoid aw20216s_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    aw20216s_led_t led;\n    memcpy_P(&led, (&g_aw20216s_leds[index]), sizeof(led));\n\n    if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n        return;\n    }\n\n    driver_buffers[led.driver].pwm_buffer[led.r] = red;\n    driver_buffers[led.driver].pwm_buffer[led.g] = green;\n    driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n    driver_buffers[led.driver].pwm_buffer_dirty  = true;\n}\n\nvoid aw20216s_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (uint8_t i = 0; i < AW20216S_LED_COUNT; i++) {\n        aw20216s_set_color(i, red, green, blue);\n    }\n}\n\nvoid aw20216s_update_pwm_buffers(pin_t cs_pin, uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        aw20216s_write(cs_pin, AW20216S_PAGE_PWM, 0, driver_buffers[index].pwm_buffer, AW20216S_PWM_REGISTER_COUNT);\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid aw20216s_flush(void) {\n    aw20216s_update_pwm_buffers(AW20216S_CS_PIN_1, 0);\n#if defined(AW20216S_CS_PIN_2)\n    aw20216s_update_pwm_buffers(AW20216S_CS_PIN_2, 1);\n#endif\n}\n"
  },
  {
    "path": "drivers/led/aw20216s.h",
    "content": "/* Copyright 2021 Jasper Chan (Gigahawk)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"gpio.h\"\n#include \"util.h\"\n\n#define AW20216S_ID (0b1010 << 4)\n#define AW20216S_WRITE 0\n#define AW20216S_READ 1\n\n#define AW20216S_PAGE_FUNCTION (0x00 << 1)\n#define AW20216S_PAGE_PWM (0x01 << 1)\n#define AW20216S_PAGE_SCALING (0x02 << 1)\n#define AW20216S_PAGE_PATTERN_CHOICE (0x03 << 1)\n#define AW20216S_PAGE_PWM_SCALING (0x04 << 1)\n\n#define AW20216S_FUNCTION_REG_CONFIGURATION 0x00\n#define AW20216S_CONFIGURATION_SWSEL_1_12 (0b1011 << 4)\n#define AW20216S_CONFIGURATION_CHIPEN (0b1 << 0)\n\n#define AW20216S_FUNCTION_REG_GLOBAL_CURRENT 0x01\n\n#define AW20216S_FUNCTION_REG_RESET 0x2F\n#define AW20216S_RESET_MAGIC 0xAE\n\n#define AW20216S_FUNCTION_REG_MIX_FUNCTION 0x46\n#define AW20216S_MIX_FUNCTION_LPEN (0b1 << 1)\n\n#if defined(RGB_MATRIX_AW20216S)\n#    define AW20216S_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(AW20216S_CS_PIN_2)\n#    define AW20216S_DRIVER_COUNT 2\n#elif defined(AW20216S_CS_PIN_1)\n#    define AW20216S_DRIVER_COUNT 1\n#endif\n\ntypedef struct aw20216s_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED aw20216s_led_t;\n\nextern const aw20216s_led_t PROGMEM g_aw20216s_leds[AW20216S_LED_COUNT];\n\nvoid aw20216s_init_drivers(void);\nvoid aw20216s_init(pin_t cs_pin);\nvoid aw20216s_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid aw20216s_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\nvoid aw20216s_update_pwm_buffers(pin_t cs_pin, uint8_t index);\n\nvoid aw20216s_flush(void);\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n#define SW1_CS17 0x10\n#define SW1_CS18 0x11\n\n#define SW2_CS1 0x12\n#define SW2_CS2 0x13\n#define SW2_CS3 0x14\n#define SW2_CS4 0x15\n#define SW2_CS5 0x16\n#define SW2_CS6 0x17\n#define SW2_CS7 0x18\n#define SW2_CS8 0x19\n#define SW2_CS9 0x1A\n#define SW2_CS10 0x1B\n#define SW2_CS11 0x1C\n#define SW2_CS12 0x1D\n#define SW2_CS13 0x1E\n#define SW2_CS14 0x1F\n#define SW2_CS15 0x20\n#define SW2_CS16 0x21\n#define SW2_CS17 0x22\n#define SW2_CS18 0x23\n\n#define SW3_CS1 0x24\n#define SW3_CS2 0x25\n#define SW3_CS3 0x26\n#define SW3_CS4 0x27\n#define SW3_CS5 0x28\n#define SW3_CS6 0x29\n#define SW3_CS7 0x2A\n#define SW3_CS8 0x2B\n#define SW3_CS9 0x2C\n#define SW3_CS10 0x2D\n#define SW3_CS11 0x2E\n#define SW3_CS12 0x2F\n#define SW3_CS13 0x30\n#define SW3_CS14 0x31\n#define SW3_CS15 0x32\n#define SW3_CS16 0x33\n#define SW3_CS17 0x34\n#define SW3_CS18 0x35\n\n#define SW4_CS1 0x36\n#define SW4_CS2 0x37\n#define SW4_CS3 0x38\n#define SW4_CS4 0x39\n#define SW4_CS5 0x3A\n#define SW4_CS6 0x3B\n#define SW4_CS7 0x3C\n#define SW4_CS8 0x3D\n#define SW4_CS9 0x3E\n#define SW4_CS10 0x3F\n#define SW4_CS11 0x40\n#define SW4_CS12 0x41\n#define SW4_CS13 0x42\n#define SW4_CS14 0x43\n#define SW4_CS15 0x44\n#define SW4_CS16 0x45\n#define SW4_CS17 0x46\n#define SW4_CS18 0x47\n\n#define SW5_CS1 0x48\n#define SW5_CS2 0x49\n#define SW5_CS3 0x4A\n#define SW5_CS4 0x4B\n#define SW5_CS5 0x4C\n#define SW5_CS6 0x4D\n#define SW5_CS7 0x4E\n#define SW5_CS8 0x4F\n#define SW5_CS9 0x50\n#define SW5_CS10 0x51\n#define SW5_CS11 0x52\n#define SW5_CS12 0x53\n#define SW5_CS13 0x54\n#define SW5_CS14 0x55\n#define SW5_CS15 0x56\n#define SW5_CS16 0x57\n#define SW5_CS17 0x58\n#define SW5_CS18 0x59\n\n#define SW6_CS1 0x5A\n#define SW6_CS2 0x5B\n#define SW6_CS3 0x5C\n#define SW6_CS4 0x5D\n#define SW6_CS5 0x5E\n#define SW6_CS6 0x5F\n#define SW6_CS7 0x60\n#define SW6_CS8 0x61\n#define SW6_CS9 0x62\n#define SW6_CS10 0x63\n#define SW6_CS11 0x64\n#define SW6_CS12 0x65\n#define SW6_CS13 0x66\n#define SW6_CS14 0x67\n#define SW6_CS15 0x68\n#define SW6_CS16 0x69\n#define SW6_CS17 0x6A\n#define SW6_CS18 0x6B\n\n#define SW7_CS1 0x6C\n#define SW7_CS2 0x6D\n#define SW7_CS3 0x6E\n#define SW7_CS4 0x6F\n#define SW7_CS5 0x70\n#define SW7_CS6 0x71\n#define SW7_CS7 0x72\n#define SW7_CS8 0x73\n#define SW7_CS9 0x74\n#define SW7_CS10 0x75\n#define SW7_CS11 0x76\n#define SW7_CS12 0x77\n#define SW7_CS13 0x78\n#define SW7_CS14 0x79\n#define SW7_CS15 0x7A\n#define SW7_CS16 0x7B\n#define SW7_CS17 0x7C\n#define SW7_CS18 0x7D\n\n#define SW8_CS1 0x7E\n#define SW8_CS2 0x7F\n#define SW8_CS3 0x80\n#define SW8_CS4 0x81\n#define SW8_CS5 0x82\n#define SW8_CS6 0x83\n#define SW8_CS7 0x84\n#define SW8_CS8 0x85\n#define SW8_CS9 0x86\n#define SW8_CS10 0x87\n#define SW8_CS11 0x88\n#define SW8_CS12 0x89\n#define SW8_CS13 0x8A\n#define SW8_CS14 0x8B\n#define SW8_CS15 0x8C\n#define SW8_CS16 0x8D\n#define SW8_CS17 0x8E\n#define SW8_CS18 0x8F\n\n#define SW9_CS1 0x90\n#define SW9_CS2 0x91\n#define SW9_CS3 0x92\n#define SW9_CS4 0x93\n#define SW9_CS5 0x94\n#define SW9_CS6 0x95\n#define SW9_CS7 0x96\n#define SW9_CS8 0x97\n#define SW9_CS9 0x98\n#define SW9_CS10 0x99\n#define SW9_CS11 0x9A\n#define SW9_CS12 0x9B\n#define SW9_CS13 0x9C\n#define SW9_CS14 0x9D\n#define SW9_CS15 0x9E\n#define SW9_CS16 0x9F\n#define SW9_CS17 0xA0\n#define SW9_CS18 0xA1\n\n#define SW10_CS1 0xA2\n#define SW10_CS2 0xA3\n#define SW10_CS3 0xA4\n#define SW10_CS4 0xA5\n#define SW10_CS5 0xA6\n#define SW10_CS6 0xA7\n#define SW10_CS7 0xA8\n#define SW10_CS8 0xA9\n#define SW10_CS9 0xAA\n#define SW10_CS10 0xAB\n#define SW10_CS11 0xAC\n#define SW10_CS12 0xAD\n#define SW10_CS13 0xAE\n#define SW10_CS14 0xAF\n#define SW10_CS15 0xB0\n#define SW10_CS16 0xB1\n#define SW10_CS17 0xB2\n#define SW10_CS18 0xB3\n\n#define SW11_CS1 0xB4\n#define SW11_CS2 0xB5\n#define SW11_CS3 0xB6\n#define SW11_CS4 0xB7\n#define SW11_CS5 0xB8\n#define SW11_CS6 0xB9\n#define SW11_CS7 0xBA\n#define SW11_CS8 0xBB\n#define SW11_CS9 0xBC\n#define SW11_CS10 0xBD\n#define SW11_CS11 0xBE\n#define SW11_CS12 0xBF\n#define SW11_CS13 0xC0\n#define SW11_CS14 0xC1\n#define SW11_CS15 0xC2\n#define SW11_CS16 0xC3\n#define SW11_CS17 0xC4\n#define SW11_CS18 0xC5\n\n#define SW12_CS1 0xC6\n#define SW12_CS2 0xC7\n#define SW12_CS3 0xC8\n#define SW12_CS4 0xC9\n#define SW12_CS5 0xCA\n#define SW12_CS6 0xCB\n#define SW12_CS7 0xCC\n#define SW12_CS8 0xCD\n#define SW12_CS9 0xCE\n#define SW12_CS10 0xCF\n#define SW12_CS11 0xD0\n#define SW12_CS12 0xD1\n#define SW12_CS13 0xD2\n#define SW12_CS14 0xD3\n#define SW12_CS15 0xD4\n#define SW12_CS16 0xD5\n#define SW12_CS17 0xD6\n#define SW12_CS18 0xD7\n"
  },
  {
    "path": "drivers/led/issi/is31fl3218-mono.c",
    "content": "/* Copyright 2018 Jason Williams (Wilba)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3218-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n\n#define IS31FL3218_PWM_REGISTER_COUNT 18\n#define IS31FL3218_LED_CONTROL_REGISTER_COUNT 3\n\n#ifndef IS31FL3218_I2C_TIMEOUT\n#    define IS31FL3218_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3218_I2C_PERSISTENCE\n#    define IS31FL3218_I2C_PERSISTENCE 0\n#endif\n\ntypedef struct is31fl3218_driver_t {\n    uint8_t pwm_buffer[IS31FL3218_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3218_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3218_driver_t;\n\n// IS31FL3218 has 18 PWM outputs and a fixed I2C address, so no chaining.\nis31fl3218_driver_t driver_buffers = {\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n};\n\nvoid is31fl3218_write_register(uint8_t reg, uint8_t data) {\n#if IS31FL3218_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, IS31FL3218_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3218_write_pwm_buffer(void) {\n#if IS31FL3218_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3218_init(void) {\n    i2c_init();\n\n#if defined(IS31FL3218_SDB_PIN)\n    gpio_set_pin_output(IS31FL3218_SDB_PIN);\n    gpio_write_pin_high(IS31FL3218_SDB_PIN);\n#endif\n\n    // In case we ever want to reinitialize (?)\n    is31fl3218_write_register(IS31FL3218_REG_RESET, 0x00);\n\n    // Turn off software shutdown\n    is31fl3218_write_register(IS31FL3218_REG_SHUTDOWN, 0x01);\n\n    // Set all PWM values to zero\n    for (uint8_t i = 0; i < IS31FL3218_PWM_REGISTER_COUNT; i++) {\n        is31fl3218_write_register(IS31FL3218_REG_PWM + i, 0x00);\n    }\n\n    // turn off all LEDs in the LED control register\n    for (uint8_t i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, 0x00);\n    }\n\n    // Load PWM registers and LED Control register data\n    is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01);\n\n    for (int i = 0; i < IS31FL3218_LED_COUNT; i++) {\n        is31fl3218_set_led_control_register(i, true);\n    }\n\n    is31fl3218_update_led_control_registers();\n}\n\nvoid is31fl3218_set_value(int index, uint8_t value) {\n    is31fl3218_led_t led;\n\n    if (index >= 0 && index < IS31FL3218_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led));\n\n        if (driver_buffers.pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers.pwm_buffer[led.v] = value;\n        driver_buffers.pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3218_set_value_all(uint8_t value) {\n    for (int i = 0; i < IS31FL3218_LED_COUNT; i++) {\n        is31fl3218_set_value(i, value);\n    }\n}\n\nvoid is31fl3218_set_led_control_register(uint8_t index, bool value) {\n    is31fl3218_led_t led;\n    memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led));\n\n    uint8_t control_register = led.v / 6;\n    uint8_t bit_value        = led.v % 6;\n\n    if (value) {\n        driver_buffers.led_control_buffer[control_register] |= (1 << bit_value);\n    } else {\n        driver_buffers.led_control_buffer[control_register] &= ~(1 << bit_value);\n    }\n\n    driver_buffers.led_control_buffer_dirty = true;\n}\n\nvoid is31fl3218_update_pwm_buffers(void) {\n    if (driver_buffers.pwm_buffer_dirty) {\n        is31fl3218_write_pwm_buffer();\n        // Load PWM registers and LED Control register data\n        is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01);\n\n        driver_buffers.pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3218_update_led_control_registers(void) {\n    if (driver_buffers.led_control_buffer_dirty) {\n        for (uint8_t i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, driver_buffers.led_control_buffer[i]);\n        }\n\n        driver_buffers.led_control_buffer_dirty = false;\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3218-mono.h",
    "content": "/* Copyright 2018 Jason Williams (Wilba)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3218_REG_SHUTDOWN 0x00\n#define IS31FL3218_REG_PWM 0x01\n#define IS31FL3218_REG_LED_CONTROL_1 0x13\n#define IS31FL3218_REG_LED_CONTROL_2 0x14\n#define IS31FL3218_REG_LED_CONTROL_3 0x15\n#define IS31FL3218_REG_UPDATE 0x16\n#define IS31FL3218_REG_RESET 0x17\n\n#define IS31FL3218_I2C_ADDRESS 0x54\n\n#if defined(LED_MATRIX_IS31FL3218)\n#    define IS31FL3218_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\ntypedef struct is31fl3218_led_t {\n    uint8_t v;\n} PACKED is31fl3218_led_t;\n\nextern const is31fl3218_led_t PROGMEM g_is31fl3218_leds[IS31FL3218_LED_COUNT];\n\nvoid is31fl3218_init(void);\n\nvoid is31fl3218_write_register(uint8_t reg, uint8_t data);\n\nvoid is31fl3218_set_value(int index, uint8_t value);\n\nvoid is31fl3218_set_value_all(uint8_t value);\n\nvoid is31fl3218_set_led_control_register(uint8_t index, bool value);\n\nvoid is31fl3218_update_pwm_buffers(void);\n\nvoid is31fl3218_update_led_control_registers(void);\n\n#define OUT1 0x00\n#define OUT2 0x01\n#define OUT3 0x02\n#define OUT4 0x03\n#define OUT5 0x04\n#define OUT6 0x05\n#define OUT7 0x06\n#define OUT8 0x07\n#define OUT9 0x08\n#define OUT10 0x09\n#define OUT11 0x0A\n#define OUT12 0x0B\n#define OUT13 0x0C\n#define OUT14 0x0D\n#define OUT15 0x0E\n#define OUT16 0x0F\n#define OUT17 0x10\n#define OUT18 0x11\n"
  },
  {
    "path": "drivers/led/issi/is31fl3218.c",
    "content": "/* Copyright 2018 Jason Williams (Wilba)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3218.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n\n#define IS31FL3218_PWM_REGISTER_COUNT 18\n#define IS31FL3218_LED_CONTROL_REGISTER_COUNT 3\n\n#ifndef IS31FL3218_I2C_TIMEOUT\n#    define IS31FL3218_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3218_I2C_PERSISTENCE\n#    define IS31FL3218_I2C_PERSISTENCE 0\n#endif\n\ntypedef struct is31fl3218_driver_t {\n    uint8_t pwm_buffer[IS31FL3218_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3218_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3218_driver_t;\n\n// IS31FL3218 has 18 PWM outputs and a fixed I2C address, so no chaining.\nis31fl3218_driver_t driver_buffers = {\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n};\n\nvoid is31fl3218_write_register(uint8_t reg, uint8_t data) {\n#if IS31FL3218_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, reg, &data, 1, IS31FL3218_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3218_write_pwm_buffer(void) {\n#if IS31FL3218_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3218_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(IS31FL3218_I2C_ADDRESS << 1, IS31FL3218_REG_PWM, driver_buffers.pwm_buffer, 18, IS31FL3218_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3218_init(void) {\n    i2c_init();\n\n#if defined(IS31FL3218_SDB_PIN)\n    gpio_set_pin_output(IS31FL3218_SDB_PIN);\n    gpio_write_pin_high(IS31FL3218_SDB_PIN);\n#endif\n\n    // In case we ever want to reinitialize (?)\n    is31fl3218_write_register(IS31FL3218_REG_RESET, 0x00);\n\n    // Turn off software shutdown\n    is31fl3218_write_register(IS31FL3218_REG_SHUTDOWN, 0x01);\n\n    // Set all PWM values to zero\n    for (uint8_t i = 0; i < IS31FL3218_PWM_REGISTER_COUNT; i++) {\n        is31fl3218_write_register(IS31FL3218_REG_PWM + i, 0x00);\n    }\n\n    // turn off all LEDs in the LED control register\n    for (uint8_t i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, 0x00);\n    }\n\n    // Load PWM registers and LED Control register data\n    is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01);\n\n    for (int i = 0; i < IS31FL3218_LED_COUNT; i++) {\n        is31fl3218_set_led_control_register(i, true, true, true);\n    }\n\n    is31fl3218_update_led_control_registers();\n}\n\nvoid is31fl3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3218_led_t led;\n\n    if (index >= 0 && index < IS31FL3218_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led));\n\n        if (driver_buffers.pwm_buffer[led.r] == red && driver_buffers.pwm_buffer[led.g] == green && driver_buffers.pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers.pwm_buffer[led.r] = red;\n        driver_buffers.pwm_buffer[led.g] = green;\n        driver_buffers.pwm_buffer[led.b] = blue;\n        driver_buffers.pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < IS31FL3218_LED_COUNT; i++) {\n        is31fl3218_set_color(i, red, green, blue);\n    }\n}\n\nvoid is31fl3218_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {\n    is31fl3218_led_t led;\n    memcpy_P(&led, (&g_is31fl3218_leds[index]), sizeof(led));\n\n    uint8_t control_register_r = led.r / 6;\n    uint8_t control_register_g = led.g / 6;\n    uint8_t control_register_b = led.b / 6;\n    uint8_t bit_r              = led.r % 6;\n    uint8_t bit_g              = led.g % 6;\n    uint8_t bit_b              = led.b % 6;\n\n    if (red) {\n        driver_buffers.led_control_buffer[control_register_r] |= (1 << bit_r);\n    } else {\n        driver_buffers.led_control_buffer[control_register_r] &= ~(1 << bit_r);\n    }\n    if (green) {\n        driver_buffers.led_control_buffer[control_register_g] |= (1 << bit_g);\n    } else {\n        driver_buffers.led_control_buffer[control_register_g] &= ~(1 << bit_g);\n    }\n    if (blue) {\n        driver_buffers.led_control_buffer[control_register_b] |= (1 << bit_b);\n    } else {\n        driver_buffers.led_control_buffer[control_register_b] &= ~(1 << bit_b);\n    }\n\n    driver_buffers.led_control_buffer_dirty = true;\n}\n\nvoid is31fl3218_update_pwm_buffers(void) {\n    if (driver_buffers.pwm_buffer_dirty) {\n        is31fl3218_write_pwm_buffer();\n        // Load PWM registers and LED Control register data\n        is31fl3218_write_register(IS31FL3218_REG_UPDATE, 0x01);\n\n        driver_buffers.pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3218_update_led_control_registers(void) {\n    if (driver_buffers.led_control_buffer_dirty) {\n        for (uint8_t i = 0; i < IS31FL3218_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3218_write_register(IS31FL3218_REG_LED_CONTROL_1 + i, driver_buffers.led_control_buffer[i]);\n        }\n\n        driver_buffers.led_control_buffer_dirty = false;\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3218.h",
    "content": "/* Copyright 2018 Jason Williams (Wilba)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3218_REG_SHUTDOWN 0x00\n#define IS31FL3218_REG_PWM 0x01\n#define IS31FL3218_REG_LED_CONTROL_1 0x13\n#define IS31FL3218_REG_LED_CONTROL_2 0x14\n#define IS31FL3218_REG_LED_CONTROL_3 0x15\n#define IS31FL3218_REG_UPDATE 0x16\n#define IS31FL3218_REG_RESET 0x17\n\n#define IS31FL3218_I2C_ADDRESS 0x54\n\n#if defined(RGB_MATRIX_IS31FL3218)\n#    define IS31FL3218_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\ntypedef struct is31fl3218_led_t {\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED is31fl3218_led_t;\n\nextern const is31fl3218_led_t PROGMEM g_is31fl3218_leds[IS31FL3218_LED_COUNT];\n\nvoid is31fl3218_init(void);\n\nvoid is31fl3218_write_register(uint8_t reg, uint8_t data);\n\nvoid is31fl3218_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3218_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3218_set_led_control_register(uint8_t index, bool red, bool green, bool blue);\n\nvoid is31fl3218_update_pwm_buffers(void);\n\nvoid is31fl3218_update_led_control_registers(void);\n\n#define OUT1 0x00\n#define OUT2 0x01\n#define OUT3 0x02\n#define OUT4 0x03\n#define OUT5 0x04\n#define OUT6 0x05\n#define OUT7 0x06\n#define OUT8 0x07\n#define OUT9 0x08\n#define OUT10 0x09\n#define OUT11 0x0A\n#define OUT12 0x0B\n#define OUT13 0x0C\n#define OUT14 0x0D\n#define OUT15 0x0E\n#define OUT16 0x0F\n#define OUT17 0x10\n#define OUT18 0x11\n"
  },
  {
    "path": "drivers/led/issi/is31fl3236-mono.c",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"is31fl3236-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n\n#define IS31FL3236_PWM_REGISTER_COUNT 36\n#define IS31FL3236_LED_CONTROL_REGISTER_COUNT 36\n\n#ifndef IS31FL3236_I2C_TIMEOUT\n#    define IS31FL3236_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3236_I2C_PERSISTENCE\n#    define IS31FL3236_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3236_PWM_FREQUENCY\n#    define IS31FL3236_PWM_FREQUENCY IS31FL3236_PWM_FREQUENCY_3K_HZ // OFS - IS31FL3236A only\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3236_DRIVER_COUNT] = {\n    IS31FL3236_I2C_ADDRESS_1,\n#ifdef IS31FL3236_I2C_ADDRESS_2\n    IS31FL3236_I2C_ADDRESS_2,\n#    ifdef IS31FL3236_I2C_ADDRESS_3\n    IS31FL3236_I2C_ADDRESS_3,\n#        ifdef IS31FL3236_I2C_ADDRESS_4\n    IS31FL3236_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\ntypedef struct is31fl3236_driver_t {\n    uint8_t pwm_buffer[IS31FL3236_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3236_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3236_driver_t;\n\nis31fl3236_driver_t driver_buffers[IS31FL3236_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid is31fl3236_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3236_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3236_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3236_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3236_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3236_write_pwm_buffer(uint8_t index) {\n#if IS31FL3236_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3236_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3236_REG_PWM, driver_buffers[index].pwm_buffer, 36, IS31FL3236_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, IS31FL3236_REG_PWM, driver_buffers[index].pwm_buffer, 36, IS31FL3236_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3236_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3236_SDB_PIN)\n    gpio_set_pin_output(IS31FL3236_SDB_PIN);\n    gpio_write_pin_high(IS31FL3236_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3236_DRIVER_COUNT; i++) {\n        is31fl3236_init(i);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3236_LED_COUNT; i++) {\n        is31fl3236_set_led_control_register(i, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3236_DRIVER_COUNT; i++) {\n        is31fl3236_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3236_init(uint8_t index) {\n    // In case we ever want to reinitialize (?)\n    is31fl3236_write_register(index, IS31FL3236_REG_RESET, 0x00);\n\n    // Turn off software shutdown\n    is31fl3236_write_register(index, IS31FL3236_REG_SHUTDOWN, 0x01);\n\n    // Set all PWM values to zero\n    for (uint8_t i = 0; i < IS31FL3236_PWM_REGISTER_COUNT; i++) {\n        is31fl3236_write_register(index, IS31FL3236_REG_PWM + i, 0x00);\n    }\n\n    // turn off all LEDs in the LED control register\n    for (uint8_t i = 0; i < IS31FL3236_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3236_write_register(index, IS31FL3236_REG_LED_CONTROL + i, 0x00);\n    }\n\n    // Set PWM frequency (IS31FL3236A)\n    is31fl3236_write_register(index, IS31FL3236_REG_PWM_FREQUENCY, IS31FL3236_PWM_FREQUENCY);\n\n    // Load PWM registers and LED Control register data\n    is31fl3236_write_register(index, IS31FL3236_REG_UPDATE, 0x01);\n}\n\nvoid is31fl3236_set_value(int index, uint8_t value) {\n    is31fl3236_led_t led;\n\n    if (index < IS31FL3236_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3236_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.v] = value;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3236_set_value_all(uint8_t value) {\n    for (uint8_t i = 0; i < IS31FL3236_LED_COUNT; i++) {\n        is31fl3236_set_value(i, value);\n    }\n}\n\nvoid is31fl3236_set_led_control_register(uint8_t index, bool value) {\n    is31fl3236_led_t led;\n    memcpy_P(&led, (&g_is31fl3236_leds[index]), sizeof(led));\n\n    driver_buffers[led.driver].led_control_buffer[led.v] = value ? 0x01 : 0x00;\n    driver_buffers[led.driver].led_control_buffer_dirty  = true;\n}\n\nvoid is31fl3236_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3236_write_pwm_buffer(index);\n        // Load PWM registers and LED Control register data\n        is31fl3236_write_register(index, IS31FL3236_REG_UPDATE, 0x01);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3236_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        for (uint8_t i = 0; i < IS31FL3236_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3236_write_register(index, IS31FL3236_REG_LED_CONTROL + i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3236_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3236_DRIVER_COUNT; i++) {\n        is31fl3236_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3236-mono.h",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3236_REG_SHUTDOWN 0x00\n#define IS31FL3236_REG_PWM 0x01\n#define IS31FL3236_REG_UPDATE 0x25\n#define IS31FL3236_REG_LED_CONTROL 0x26\n#define IS31FL3236_REG_GLOBAL_CONTROL 0x4A\n#define IS31FL3236_REG_PWM_FREQUENCY 0x4B\n#define IS31FL3236_REG_RESET 0x4F\n\n#define IS31FL3236_I2C_ADDRESS_GND 0x3C\n#define IS31FL3236_I2C_ADDRESS_SCL 0x3D\n#define IS31FL3236_I2C_ADDRESS_SDA 0x3E\n#define IS31FL3236_I2C_ADDRESS_VCC 0x3F\n\n#if defined(LED_MATRIX_IS31FL3236)\n#    define IS31FL3236_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3236_I2C_ADDRESS_4)\n#    define IS31FL3236_DRIVER_COUNT 4\n#elif defined(IS31FL3236_I2C_ADDRESS_3)\n#    define IS31FL3236_DRIVER_COUNT 3\n#elif defined(IS31FL3236_I2C_ADDRESS_2)\n#    define IS31FL3236_DRIVER_COUNT 2\n#elif defined(IS31FL3236_I2C_ADDRESS_1)\n#    define IS31FL3236_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3236_led_t {\n    uint8_t driver : 2;\n    uint8_t v;\n} PACKED is31fl3236_led_t;\n\nextern const is31fl3236_led_t PROGMEM g_is31fl3236_leds[IS31FL3236_LED_COUNT];\n\nvoid is31fl3236_init_drivers(void);\n\nvoid is31fl3236_init(uint8_t index);\n\nvoid is31fl3236_write_register(uint8_t index, uint8_t reg, uint8_t data);\n\nvoid is31fl3236_set_value(int index, uint8_t value);\n\nvoid is31fl3236_set_value_all(uint8_t value);\n\nvoid is31fl3236_set_led_control_register(uint8_t index, bool value);\n\nvoid is31fl3236_update_pwm_buffers(uint8_t index);\n\nvoid is31fl3236_update_led_control_registers(uint8_t index);\n\nvoid is31fl3236_flush(void);\n\n#define IS31FL3236_PWM_FREQUENCY_3K_HZ 0b0\n#define IS31FL3236_PWM_FREQUENCY_22K_HZ 0b1\n\n#define OUT1 0x00\n#define OUT2 0x01\n#define OUT3 0x02\n#define OUT4 0x03\n#define OUT5 0x04\n#define OUT6 0x05\n#define OUT7 0x06\n#define OUT8 0x07\n#define OUT9 0x08\n#define OUT10 0x09\n#define OUT11 0x0A\n#define OUT12 0x0B\n#define OUT13 0x0C\n#define OUT14 0x0D\n#define OUT15 0x0E\n#define OUT16 0x0F\n#define OUT17 0x10\n#define OUT18 0x11\n#define OUT19 0x12\n#define OUT20 0x13\n#define OUT21 0x14\n#define OUT22 0x15\n#define OUT23 0x16\n#define OUT24 0x17\n#define OUT25 0x18\n#define OUT26 0x19\n#define OUT27 0x1A\n#define OUT28 0x1B\n#define OUT29 0x1C\n#define OUT30 0x1D\n#define OUT31 0x1E\n#define OUT32 0x1F\n#define OUT33 0x20\n#define OUT34 0x21\n#define OUT35 0x22\n#define OUT36 0x23\n"
  },
  {
    "path": "drivers/led/issi/is31fl3236.c",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"is31fl3236.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n\n#define IS31FL3236_PWM_REGISTER_COUNT 36\n#define IS31FL3236_LED_CONTROL_REGISTER_COUNT 36\n\n#ifndef IS31FL3236_I2C_TIMEOUT\n#    define IS31FL3236_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3236_I2C_PERSISTENCE\n#    define IS31FL3236_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3236_PWM_FREQUENCY\n#    define IS31FL3236_PWM_FREQUENCY IS31FL3236_PWM_FREQUENCY_3K_HZ // OFS - IS31FL3236A only\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3236_DRIVER_COUNT] = {\n    IS31FL3236_I2C_ADDRESS_1,\n#ifdef IS31FL3236_I2C_ADDRESS_2\n    IS31FL3236_I2C_ADDRESS_2,\n#    ifdef IS31FL3236_I2C_ADDRESS_3\n    IS31FL3236_I2C_ADDRESS_3,\n#        ifdef IS31FL3236_I2C_ADDRESS_4\n    IS31FL3236_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\ntypedef struct is31fl3236_driver_t {\n    uint8_t pwm_buffer[IS31FL3236_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3236_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3236_driver_t;\n\nis31fl3236_driver_t driver_buffers[IS31FL3236_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid is31fl3236_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3236_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3236_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3236_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3236_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3236_write_pwm_buffer(uint8_t index) {\n#if IS31FL3236_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3236_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3236_REG_PWM, driver_buffers[index].pwm_buffer, 36, IS31FL3236_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, IS31FL3236_REG_PWM, driver_buffers[index].pwm_buffer, 36, IS31FL3236_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3236_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3236_SDB_PIN)\n    gpio_set_pin_output(IS31FL3236_SDB_PIN);\n    gpio_write_pin_high(IS31FL3236_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3236_DRIVER_COUNT; i++) {\n        is31fl3236_init(i);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3236_LED_COUNT; i++) {\n        is31fl3236_set_led_control_register(i, true, true, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3236_DRIVER_COUNT; i++) {\n        is31fl3236_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3236_init(uint8_t index) {\n    // In case we ever want to reinitialize (?)\n    is31fl3236_write_register(index, IS31FL3236_REG_RESET, 0x00);\n\n    // Turn off software shutdown\n    is31fl3236_write_register(index, IS31FL3236_REG_SHUTDOWN, 0x01);\n\n    // Set all PWM values to zero\n    for (uint8_t i = 0; i < IS31FL3236_PWM_REGISTER_COUNT; i++) {\n        is31fl3236_write_register(index, IS31FL3236_REG_PWM + i, 0x00);\n    }\n\n    // turn off all LEDs in the LED control register\n    for (uint8_t i = 0; i < IS31FL3236_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3236_write_register(index, IS31FL3236_REG_LED_CONTROL + i, 0x00);\n    }\n\n    // Set PWM frequency (IS31FL3236A)\n    is31fl3236_write_register(index, IS31FL3236_REG_PWM_FREQUENCY, IS31FL3236_PWM_FREQUENCY);\n\n    // Load PWM registers and LED Control register data\n    is31fl3236_write_register(index, IS31FL3236_REG_UPDATE, 0x01);\n}\n\nvoid is31fl3236_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3236_led_t led;\n\n    if (index < IS31FL3236_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3236_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.r] = red;\n        driver_buffers[led.driver].pwm_buffer[led.g] = green;\n        driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3236_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (uint8_t i = 0; i < IS31FL3236_LED_COUNT; i++) {\n        is31fl3236_set_color(i, red, green, blue);\n    }\n}\n\nvoid is31fl3236_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {\n    is31fl3236_led_t led;\n    memcpy_P(&led, (&g_is31fl3236_leds[index]), sizeof(led));\n\n    driver_buffers[led.driver].led_control_buffer[led.r] = red ? 0x01 : 0x00;\n    driver_buffers[led.driver].led_control_buffer[led.g] = green ? 0x01 : 0x00;\n    driver_buffers[led.driver].led_control_buffer[led.b] = blue ? 0x01 : 0x00;\n    driver_buffers[led.driver].led_control_buffer_dirty  = true;\n}\n\nvoid is31fl3236_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3236_write_pwm_buffer(index);\n        // Load PWM registers and LED Control register data\n        is31fl3236_write_register(index, IS31FL3236_REG_UPDATE, 0x01);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3236_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        for (uint8_t i = 0; i < IS31FL3236_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3236_write_register(index, IS31FL3236_REG_LED_CONTROL + i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3236_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3236_DRIVER_COUNT; i++) {\n        is31fl3236_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3236.h",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3236_REG_SHUTDOWN 0x00\n#define IS31FL3236_REG_PWM 0x01\n#define IS31FL3236_REG_UPDATE 0x25\n#define IS31FL3236_REG_LED_CONTROL 0x26\n#define IS31FL3236_REG_GLOBAL_CONTROL 0x4A\n#define IS31FL3236_REG_PWM_FREQUENCY 0x4B\n#define IS31FL3236_REG_RESET 0x4F\n\n#define IS31FL3236_I2C_ADDRESS_GND 0x3C\n#define IS31FL3236_I2C_ADDRESS_SCL 0x3D\n#define IS31FL3236_I2C_ADDRESS_SDA 0x3E\n#define IS31FL3236_I2C_ADDRESS_VCC 0x3F\n\n#if defined(RGB_MATRIX_IS31FL3236)\n#    define IS31FL3236_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3236_I2C_ADDRESS_4)\n#    define IS31FL3236_DRIVER_COUNT 4\n#elif defined(IS31FL3236_I2C_ADDRESS_3)\n#    define IS31FL3236_DRIVER_COUNT 3\n#elif defined(IS31FL3236_I2C_ADDRESS_2)\n#    define IS31FL3236_DRIVER_COUNT 2\n#elif defined(IS31FL3236_I2C_ADDRESS_1)\n#    define IS31FL3236_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3236_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED is31fl3236_led_t;\n\nextern const is31fl3236_led_t PROGMEM g_is31fl3236_leds[IS31FL3236_LED_COUNT];\n\nvoid is31fl3236_init_drivers(void);\n\nvoid is31fl3236_init(uint8_t index);\n\nvoid is31fl3236_write_register(uint8_t index, uint8_t reg, uint8_t data);\n\nvoid is31fl3236_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3236_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3236_set_led_control_register(uint8_t index, bool red, bool green, bool blue);\n\nvoid is31fl3236_update_pwm_buffers(uint8_t index);\n\nvoid is31fl3236_update_led_control_registers(uint8_t index);\n\nvoid is31fl3236_flush(void);\n\n#define IS31FL3236_PWM_FREQUENCY_3K_HZ 0b0\n#define IS31FL3236_PWM_FREQUENCY_22K_HZ 0b1\n\n#define OUT1 0x00\n#define OUT2 0x01\n#define OUT3 0x02\n#define OUT4 0x03\n#define OUT5 0x04\n#define OUT6 0x05\n#define OUT7 0x06\n#define OUT8 0x07\n#define OUT9 0x08\n#define OUT10 0x09\n#define OUT11 0x0A\n#define OUT12 0x0B\n#define OUT13 0x0C\n#define OUT14 0x0D\n#define OUT15 0x0E\n#define OUT16 0x0F\n#define OUT17 0x10\n#define OUT18 0x11\n#define OUT19 0x12\n#define OUT20 0x13\n#define OUT21 0x14\n#define OUT22 0x15\n#define OUT23 0x16\n#define OUT24 0x17\n#define OUT25 0x18\n#define OUT26 0x19\n#define OUT27 0x1A\n#define OUT28 0x1B\n#define OUT29 0x1C\n#define OUT30 0x1D\n#define OUT31 0x1E\n#define OUT32 0x1F\n#define OUT33 0x20\n#define OUT34 0x21\n#define OUT35 0x22\n#define OUT36 0x23\n"
  },
  {
    "path": "drivers/led/issi/is31fl3729-mono.c",
    "content": "/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>\n * Copyright 2024 Harrison Chan (Xelus)\n * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3729-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3729_PWM_REGISTER_COUNT 143\n#define IS31FL3729_SCALING_REGISTER_COUNT 16\n\n#ifndef IS31FL3729_I2C_TIMEOUT\n#    define IS31FL3729_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3729_I2C_PERSISTENCE\n#    define IS31FL3729_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3729_CONFIGURATION\n#    define IS31FL3729_CONFIGURATION IS31FL3729_CONFIG_SWS_15_9\n#endif\n\n#ifndef IS31FL3729_GLOBAL_CURRENT\n#    define IS31FL3729_GLOBAL_CURRENT 0x40\n#endif\n\n#ifndef IS31FL3729_SW_PULLDOWN\n#    define IS31FL3729_SW_PULLDOWN IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF\n#endif\n\n#ifndef IS31FL3729_CS_PULLUP\n#    define IS31FL3729_CS_PULLUP IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF\n#endif\n\n#ifndef IS31FL3729_SPREAD_SPECTRUM\n#    define IS31FL3729_SPREAD_SPECTRUM IS31FL3729_SPREAD_SPECTRUM_DISABLE\n#endif\n\n#ifndef IS31FL3729_SPREAD_SPECTRUM_RANGE\n#    define IS31FL3729_SPREAD_SPECTRUM_RANGE IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT\n#endif\n\n#ifndef IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME\n#    define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US\n#endif\n\n#ifndef IS31FL3729_PWM_FREQUENCY\n#    define IS31FL3729_PWM_FREQUENCY IS31FL3729_PWM_FREQUENCY_32K_HZ\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3729_DRIVER_COUNT] = {\n    IS31FL3729_I2C_ADDRESS_1,\n#ifdef IS31FL3729_I2C_ADDRESS_2\n    IS31FL3729_I2C_ADDRESS_2,\n#    ifdef IS31FL3729_I2C_ADDRESS_3\n    IS31FL3729_I2C_ADDRESS_3,\n#        ifdef IS31FL3729_I2C_ADDRESS_4\n    IS31FL3729_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the PWM & scaling registers.\n// Storing them like this is optimal for I2C transfers to the registers.\ntypedef struct is31fl3729_driver_t {\n    uint8_t pwm_buffer[IS31FL3729_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer[IS31FL3729_SCALING_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3729_driver_t;\n\nis31fl3729_driver_t driver_buffers[IS31FL3729_DRIVER_COUNT] = {{\n    .pwm_buffer           = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer       = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3729_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3729_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3729_write_pwm_buffer(uint8_t index) {\n    // Transmit PWM registers in 11 transfers of 13 bytes.\n\n    // Iterate over the pwm_buffer contents at 13 byte intervals.\n    for (uint8_t i = 0; i <= IS31FL3729_PWM_REGISTER_COUNT; i += 13) {\n#if IS31FL3729_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3729_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3729_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3729_SDB_PIN)\n    gpio_set_pin_output(IS31FL3729_SDB_PIN);\n    gpio_write_pin_high(IS31FL3729_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {\n        is31fl3729_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {\n        is31fl3729_set_scaling_register(i, 0xFF);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {\n        is31fl3729_update_scaling_registers(i);\n    }\n}\n\nvoid is31fl3729_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3729_write_register(index, IS31FL3729_REG_PULLDOWNUP, ((IS31FL3729_SW_PULLDOWN & 0b111) << 4) | (IS31FL3729_CS_PULLUP & 0b111));\n    is31fl3729_write_register(index, IS31FL3729_REG_SPREAD_SPECTRUM, ((IS31FL3729_SPREAD_SPECTRUM & 0b1) << 4) | ((IS31FL3729_SPREAD_SPECTRUM_RANGE & 0b11) << 2) | (IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME & 0b11));\n    is31fl3729_write_register(index, IS31FL3729_REG_PWM_FREQUENCY, IS31FL3729_PWM_FREQUENCY);\n    is31fl3729_write_register(index, IS31FL3729_REG_GLOBAL_CURRENT, IS31FL3729_GLOBAL_CURRENT);\n    is31fl3729_write_register(index, IS31FL3729_REG_CONFIGURATION, IS31FL3729_CONFIGURATION);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3729_set_value(int index, uint8_t value) {\n    is31fl3729_led_t led;\n    if (index >= 0 && index < IS31FL3729_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.v] = value;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3729_set_value_all(uint8_t value) {\n    for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {\n        is31fl3729_set_value(i, value);\n    }\n}\n\nvoid is31fl3729_set_scaling_register(uint8_t index, uint8_t value) {\n    is31fl3729_led_t led;\n    memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));\n\n    // need to do a bit of checking here since 3729 scaling is per CS pin.\n    // not the usual per single LED key as per other ISSI drivers\n    // only enable them, since they should be default disabled\n    int cs_value = (led.v & 0x0F) - 1;\n\n    driver_buffers[led.driver].scaling_buffer[cs_value] = value;\n    driver_buffers[led.driver].scaling_buffer_dirty     = true;\n}\n\nvoid is31fl3729_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3729_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3729_update_scaling_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        for (uint8_t i = 0; i < IS31FL3729_SCALING_REGISTER_COUNT; i++) {\n            is31fl3729_write_register(index, IS31FL3729_REG_SCALING + i, driver_buffers[index].scaling_buffer[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3729_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {\n        is31fl3729_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3729-mono.h",
    "content": "/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>\n * Copyright 2024 Harrison Chan (Xelus)\n * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3729_REG_PWM 0x01\n#define IS31FL3729_REG_SCALING 0x90\n#define IS31FL3729_REG_CONFIGURATION 0xA0\n#define IS31FL3729_REG_GLOBAL_CURRENT 0xA1\n#define IS31FL3729_REG_PULLDOWNUP 0xB0\n#define IS31FL3729_REG_SPREAD_SPECTRUM 0xB1\n#define IS31FL3729_REG_PWM_FREQUENCY 0xB2\n#define IS31FL3729_REG_RESET 0xCF\n\n#define IS31FL3729_I2C_ADDRESS_GND 0x34\n#define IS31FL3729_I2C_ADDRESS_SCL 0x35\n#define IS31FL3729_I2C_ADDRESS_SDA 0x36\n#define IS31FL3729_I2C_ADDRESS_VCC 0x37\n\n#if defined(LED_MATRIX_IS31FL3729)\n#    define IS31FL3729_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3729_I2C_ADDRESS_4)\n#    define IS31FL3729_DRIVER_COUNT 4\n#elif defined(IS31FL3729_I2C_ADDRESS_3)\n#    define IS31FL3729_DRIVER_COUNT 3\n#elif defined(IS31FL3729_I2C_ADDRESS_2)\n#    define IS31FL3729_DRIVER_COUNT 2\n#elif defined(IS31FL3729_I2C_ADDRESS_1)\n#    define IS31FL3729_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3729_led_t {\n    uint8_t driver : 2;\n    uint8_t v;\n} PACKED is31fl3729_led_t;\n\nextern const is31fl3729_led_t PROGMEM g_is31fl3729_leds[IS31FL3729_LED_COUNT];\n\nvoid is31fl3729_init_drivers(void);\nvoid is31fl3729_init(uint8_t index);\nvoid is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data);\n\nvoid is31fl3729_set_value(int index, uint8_t value);\nvoid is31fl3729_set_value_all(uint8_t value);\n\nvoid is31fl3729_set_scaling_register(uint8_t index, uint8_t value);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3729_update_pwm_buffers(uint8_t index);\nvoid is31fl3729_update_scaling_registers(uint8_t index);\n\nvoid is31fl3729_flush(void);\n\n#define IS31FL3729_SW_PULLDOWN_0_OHM 0b000\n#define IS31FL3729_SW_PULLDOWN_0K5_OHM_SW_OFF 0b001\n#define IS31FL3729_SW_PULLDOWN_1K_OHM_SW_OFF 0b010\n#define IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF 0b011\n#define IS31FL3729_SW_PULLDOWN_1K_OHM 0b100\n#define IS31FL3729_SW_PULLDOWN_2K_OHM 0b101\n#define IS31FL3729_SW_PULLDOWN_4K_OHM 0b110\n#define IS31FL3729_SW_PULLDOWN_8K_OHM 0b111\n\n#define IS31FL3729_CS_PULLUP_0_OHM 0b000\n#define IS31FL3729_CS_PULLUP_0K5_OHM_CS_OFF 0b001\n#define IS31FL3729_CS_PULLUP_1K_OHM_CS_OFF 0b010\n#define IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF 0b011\n#define IS31FL3729_CS_PULLUP_1K_OHM 0b100\n#define IS31FL3729_CS_PULLUP_2K_OHM 0b101\n#define IS31FL3729_CS_PULLUP_4K_OHM 0b110\n#define IS31FL3729_CS_PULLUP_8K_OHM 0b111\n\n#define IS31FL3729_SPREAD_SPECTRUM_DISABLE 0b0\n#define IS31FL3729_SPREAD_SPECTRUM_ENABLE 0b1\n\n#define IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT 0b00\n#define IS31FL3729_SPREAD_SPECTRUM_RANGE_15_PERCENT 0b01\n#define IS31FL3729_SPREAD_SPECTRUM_RANGE_24_PERCENT 0b10\n#define IS31FL3729_SPREAD_SPECTRUM_RANGE_34_PERCENT 0b11\n\n#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US 0b00\n#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1200_US 0b01\n#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_820_US 0b10\n#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_660_US 0b11\n\n#define IS31FL3729_PWM_FREQUENCY_55K_HZ 0b000\n#define IS31FL3729_PWM_FREQUENCY_32K_HZ 0b001\n#define IS31FL3729_PWM_FREQUENCY_4K_HZ 0b010\n#define IS31FL3729_PWM_FREQUENCY_2K_HZ 0b011\n#define IS31FL3729_PWM_FREQUENCY_1K_HZ 0b100\n#define IS31FL3729_PWM_FREQUENCY_500_HZ 0b101\n#define IS31FL3729_PWM_FREQUENCY_250_HZ 0b110\n#define IS31FL3729_PWM_FREQUENCY_80K_HZ 0b111\n\n#define IS31FL3729_CONFIG_SWS_15_9 0x01 // 15 CS x 9 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_8 0x11 // 16 CS x 8 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_7 0x21 // 16 CS x 7 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_6 0x31 // 16 CS x 6 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_5 0x41 // 16 CS x 5 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_4 0x51 // 16 CS x 4 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_3 0x61 // 16 CS x 3 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_2 0x71 // 16 CS x 2 SW matrix\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n\n#define SW2_CS1 0x10\n#define SW2_CS2 0x11\n#define SW2_CS3 0x12\n#define SW2_CS4 0x13\n#define SW2_CS5 0x14\n#define SW2_CS6 0x15\n#define SW2_CS7 0x16\n#define SW2_CS8 0x17\n#define SW2_CS9 0x18\n#define SW2_CS10 0x19\n#define SW2_CS11 0x1A\n#define SW2_CS12 0x1B\n#define SW2_CS13 0x1C\n#define SW2_CS14 0x1D\n#define SW2_CS15 0x1E\n#define SW2_CS16 0x1F\n\n#define SW3_CS1 0x20\n#define SW3_CS2 0x21\n#define SW3_CS3 0x22\n#define SW3_CS4 0x23\n#define SW3_CS5 0x24\n#define SW3_CS6 0x25\n#define SW3_CS7 0x26\n#define SW3_CS8 0x27\n#define SW3_CS9 0x28\n#define SW3_CS10 0x29\n#define SW3_CS11 0x2A\n#define SW3_CS12 0x2B\n#define SW3_CS13 0x2C\n#define SW3_CS14 0x2D\n#define SW3_CS15 0x2E\n#define SW3_CS16 0x2F\n\n#define SW4_CS1 0x30\n#define SW4_CS2 0x31\n#define SW4_CS3 0x32\n#define SW4_CS4 0x33\n#define SW4_CS5 0x34\n#define SW4_CS6 0x35\n#define SW4_CS7 0x36\n#define SW4_CS8 0x37\n#define SW4_CS9 0x38\n#define SW4_CS10 0x39\n#define SW4_CS11 0x3A\n#define SW4_CS12 0x3B\n#define SW4_CS13 0x3C\n#define SW4_CS14 0x3D\n#define SW4_CS15 0x3E\n#define SW4_CS16 0x3F\n\n#define SW5_CS1 0x40\n#define SW5_CS2 0x41\n#define SW5_CS3 0x42\n#define SW5_CS4 0x43\n#define SW5_CS5 0x44\n#define SW5_CS6 0x45\n#define SW5_CS7 0x46\n#define SW5_CS8 0x47\n#define SW5_CS9 0x48\n#define SW5_CS10 0x49\n#define SW5_CS11 0x4A\n#define SW5_CS12 0x4B\n#define SW5_CS13 0x4C\n#define SW5_CS14 0x4D\n#define SW5_CS15 0x4E\n#define SW5_CS16 0x4F\n\n#define SW6_CS1 0x50\n#define SW6_CS2 0x51\n#define SW6_CS3 0x52\n#define SW6_CS4 0x53\n#define SW6_CS5 0x54\n#define SW6_CS6 0x55\n#define SW6_CS7 0x56\n#define SW6_CS8 0x57\n#define SW6_CS9 0x58\n#define SW6_CS10 0x59\n#define SW6_CS11 0x5A\n#define SW6_CS12 0x5B\n#define SW6_CS13 0x5C\n#define SW6_CS14 0x5D\n#define SW6_CS15 0x5E\n#define SW6_CS16 0x5F\n\n#define SW7_CS1 0x60\n#define SW7_CS2 0x61\n#define SW7_CS3 0x62\n#define SW7_CS4 0x63\n#define SW7_CS5 0x64\n#define SW7_CS6 0x65\n#define SW7_CS7 0x66\n#define SW7_CS8 0x67\n#define SW7_CS9 0x68\n#define SW7_CS10 0x69\n#define SW7_CS11 0x6A\n#define SW7_CS12 0x6B\n#define SW7_CS13 0x6C\n#define SW7_CS14 0x6D\n#define SW7_CS15 0x6E\n#define SW7_CS16 0x6F\n\n#define SW8_CS1 0x70\n#define SW8_CS2 0x71\n#define SW8_CS3 0x72\n#define SW8_CS4 0x73\n#define SW8_CS5 0x74\n#define SW8_CS6 0x75\n#define SW8_CS7 0x76\n#define SW8_CS8 0x77\n#define SW8_CS9 0x78\n#define SW8_CS10 0x79\n#define SW8_CS11 0x7A\n#define SW8_CS12 0x7B\n#define SW8_CS13 0x7C\n#define SW8_CS14 0x7D\n#define SW8_CS15 0x7E\n#define SW8_CS16 0x7F\n\n#define SW9_CS1 0x80\n#define SW9_CS2 0x81\n#define SW9_CS3 0x82\n#define SW9_CS4 0x83\n#define SW9_CS5 0x84\n#define SW9_CS6 0x85\n#define SW9_CS7 0x86\n#define SW9_CS8 0x87\n#define SW9_CS9 0x88\n#define SW9_CS10 0x89\n#define SW9_CS11 0x8A\n#define SW9_CS12 0x8B\n#define SW9_CS13 0x8C\n#define SW9_CS14 0x8D\n#define SW9_CS15 0x8E\n"
  },
  {
    "path": "drivers/led/issi/is31fl3729.c",
    "content": "/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>\n * Copyright 2024 Harrison Chan (Xelus)\n * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3729.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3729_PWM_REGISTER_COUNT 143\n#define IS31FL3729_SCALING_REGISTER_COUNT 16\n\n#ifndef IS31FL3729_I2C_TIMEOUT\n#    define IS31FL3729_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3729_I2C_PERSISTENCE\n#    define IS31FL3729_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3729_CONFIGURATION\n#    define IS31FL3729_CONFIGURATION IS31FL3729_CONFIG_SWS_15_9\n#endif\n\n#ifndef IS31FL3729_GLOBAL_CURRENT\n#    define IS31FL3729_GLOBAL_CURRENT 0x40\n#endif\n\n#ifndef IS31FL3729_SW_PULLDOWN\n#    define IS31FL3729_SW_PULLDOWN IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF\n#endif\n\n#ifndef IS31FL3729_CS_PULLUP\n#    define IS31FL3729_CS_PULLUP IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF\n#endif\n\n#ifndef IS31FL3729_SPREAD_SPECTRUM\n#    define IS31FL3729_SPREAD_SPECTRUM IS31FL3729_SPREAD_SPECTRUM_DISABLE\n#endif\n\n#ifndef IS31FL3729_SPREAD_SPECTRUM_RANGE\n#    define IS31FL3729_SPREAD_SPECTRUM_RANGE IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT\n#endif\n\n#ifndef IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME\n#    define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US\n#endif\n\n#ifndef IS31FL3729_PWM_FREQUENCY\n#    define IS31FL3729_PWM_FREQUENCY IS31FL3729_PWM_FREQUENCY_32K_HZ\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3729_DRIVER_COUNT] = {\n    IS31FL3729_I2C_ADDRESS_1,\n#ifdef IS31FL3729_I2C_ADDRESS_2\n    IS31FL3729_I2C_ADDRESS_2,\n#    ifdef IS31FL3729_I2C_ADDRESS_3\n    IS31FL3729_I2C_ADDRESS_3,\n#        ifdef IS31FL3729_I2C_ADDRESS_4\n    IS31FL3729_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the PWM & scaling registers.\n// Storing them like this is optimal for I2C transfers to the registers.\ntypedef struct is31fl3729_driver_t {\n    uint8_t pwm_buffer[IS31FL3729_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer[IS31FL3729_SCALING_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3729_driver_t;\n\nis31fl3729_driver_t driver_buffers[IS31FL3729_DRIVER_COUNT] = {{\n    .pwm_buffer           = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer       = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3729_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3729_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3729_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3729_write_pwm_buffer(uint8_t index) {\n    // Transmit PWM registers in 11 transfers of 13 bytes.\n\n    // Iterate over the pwm_buffer contents at 13 byte intervals.\n    for (uint8_t i = 0; i <= IS31FL3729_PWM_REGISTER_COUNT; i += 13) {\n#if IS31FL3729_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3729_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, IS31FL3729_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 13, IS31FL3729_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3729_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3729_SDB_PIN)\n    gpio_set_pin_output(IS31FL3729_SDB_PIN);\n    gpio_write_pin_high(IS31FL3729_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {\n        is31fl3729_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {\n        is31fl3729_set_scaling_register(i, 0xFF, 0xFF, 0xFF);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {\n        is31fl3729_update_scaling_registers(i);\n    }\n}\n\nvoid is31fl3729_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3729_write_register(index, IS31FL3729_REG_PULLDOWNUP, ((IS31FL3729_SW_PULLDOWN & 0b111) << 4) | (IS31FL3729_CS_PULLUP & 0b111));\n    is31fl3729_write_register(index, IS31FL3729_REG_SPREAD_SPECTRUM, ((IS31FL3729_SPREAD_SPECTRUM & 0b1) << 4) | ((IS31FL3729_SPREAD_SPECTRUM_RANGE & 0b11) << 2) | (IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME & 0b11));\n    is31fl3729_write_register(index, IS31FL3729_REG_PWM_FREQUENCY, IS31FL3729_PWM_FREQUENCY);\n    is31fl3729_write_register(index, IS31FL3729_REG_GLOBAL_CURRENT, IS31FL3729_GLOBAL_CURRENT);\n    is31fl3729_write_register(index, IS31FL3729_REG_CONFIGURATION, IS31FL3729_CONFIGURATION);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3729_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3729_led_t led;\n    if (index >= 0 && index < IS31FL3729_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.r] = red;\n        driver_buffers[led.driver].pwm_buffer[led.g] = green;\n        driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3729_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < IS31FL3729_LED_COUNT; i++) {\n        is31fl3729_set_color(i, red, green, blue);\n    }\n}\n\nvoid is31fl3729_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3729_led_t led;\n    memcpy_P(&led, (&g_is31fl3729_leds[index]), sizeof(led));\n\n    // need to do a bit of checking here since 3729 scaling is per CS pin.\n    // not the usual per RGB key as per other ISSI drivers\n    // only enable them, since they should be default disabled\n    int cs_red   = (led.r & 0x0F) - 1;\n    int cs_green = (led.g & 0x0F) - 1;\n    int cs_blue  = (led.b & 0x0F) - 1;\n\n    driver_buffers[led.driver].scaling_buffer[cs_red]   = red;\n    driver_buffers[led.driver].scaling_buffer[cs_green] = green;\n    driver_buffers[led.driver].scaling_buffer[cs_blue]  = blue;\n    driver_buffers[led.driver].scaling_buffer_dirty     = true;\n}\n\nvoid is31fl3729_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3729_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3729_update_scaling_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        for (uint8_t i = 0; i < IS31FL3729_SCALING_REGISTER_COUNT; i++) {\n            is31fl3729_write_register(index, IS31FL3729_REG_SCALING + i, driver_buffers[index].scaling_buffer[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3729_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3729_DRIVER_COUNT; i++) {\n        is31fl3729_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3729.h",
    "content": "/* Copyright 2024 HorrorTroll <https://github.com/HorrorTroll>\n * Copyright 2024 Harrison Chan (Xelus)\n * Copyright 2024 Dimitris Mantzouranis <d3xter93@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3729_REG_PWM 0x01\n#define IS31FL3729_REG_SCALING 0x90\n#define IS31FL3729_REG_CONFIGURATION 0xA0\n#define IS31FL3729_REG_GLOBAL_CURRENT 0xA1\n#define IS31FL3729_REG_PULLDOWNUP 0xB0\n#define IS31FL3729_REG_SPREAD_SPECTRUM 0xB1\n#define IS31FL3729_REG_PWM_FREQUENCY 0xB2\n#define IS31FL3729_REG_RESET 0xCF\n\n#define IS31FL3729_I2C_ADDRESS_GND 0x34\n#define IS31FL3729_I2C_ADDRESS_SCL 0x35\n#define IS31FL3729_I2C_ADDRESS_SDA 0x36\n#define IS31FL3729_I2C_ADDRESS_VCC 0x37\n\n#if defined(RGB_MATRIX_IS31FL3729)\n#    define IS31FL3729_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3729_I2C_ADDRESS_4)\n#    define IS31FL3729_DRIVER_COUNT 4\n#elif defined(IS31FL3729_I2C_ADDRESS_3)\n#    define IS31FL3729_DRIVER_COUNT 3\n#elif defined(IS31FL3729_I2C_ADDRESS_2)\n#    define IS31FL3729_DRIVER_COUNT 2\n#elif defined(IS31FL3729_I2C_ADDRESS_1)\n#    define IS31FL3729_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3729_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED is31fl3729_led_t;\n\nextern const is31fl3729_led_t PROGMEM g_is31fl3729_leds[IS31FL3729_LED_COUNT];\n\nvoid is31fl3729_init_drivers(void);\nvoid is31fl3729_init(uint8_t index);\nvoid is31fl3729_write_register(uint8_t index, uint8_t reg, uint8_t data);\n\nvoid is31fl3729_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid is31fl3729_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3729_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3729_update_pwm_buffers(uint8_t index);\nvoid is31fl3729_update_scaling_registers(uint8_t index);\n\nvoid is31fl3729_flush(void);\n\n#define IS31FL3729_SW_PULLDOWN_0_OHM 0b000\n#define IS31FL3729_SW_PULLDOWN_0K5_OHM_SW_OFF 0b001\n#define IS31FL3729_SW_PULLDOWN_1K_OHM_SW_OFF 0b010\n#define IS31FL3729_SW_PULLDOWN_2K_OHM_SW_OFF 0b011\n#define IS31FL3729_SW_PULLDOWN_1K_OHM 0b100\n#define IS31FL3729_SW_PULLDOWN_2K_OHM 0b101\n#define IS31FL3729_SW_PULLDOWN_4K_OHM 0b110\n#define IS31FL3729_SW_PULLDOWN_8K_OHM 0b111\n\n#define IS31FL3729_CS_PULLUP_0_OHM 0b000\n#define IS31FL3729_CS_PULLUP_0K5_OHM_CS_OFF 0b001\n#define IS31FL3729_CS_PULLUP_1K_OHM_CS_OFF 0b010\n#define IS31FL3729_CS_PULLUP_2K_OHM_CS_OFF 0b011\n#define IS31FL3729_CS_PULLUP_1K_OHM 0b100\n#define IS31FL3729_CS_PULLUP_2K_OHM 0b101\n#define IS31FL3729_CS_PULLUP_4K_OHM 0b110\n#define IS31FL3729_CS_PULLUP_8K_OHM 0b111\n\n#define IS31FL3729_SPREAD_SPECTRUM_DISABLE 0b0\n#define IS31FL3729_SPREAD_SPECTRUM_ENABLE 0b1\n\n#define IS31FL3729_SPREAD_SPECTRUM_RANGE_5_PERCENT 0b00\n#define IS31FL3729_SPREAD_SPECTRUM_RANGE_15_PERCENT 0b01\n#define IS31FL3729_SPREAD_SPECTRUM_RANGE_24_PERCENT 0b10\n#define IS31FL3729_SPREAD_SPECTRUM_RANGE_34_PERCENT 0b11\n\n#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1980_US 0b00\n#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_1200_US 0b01\n#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_820_US 0b10\n#define IS31FL3729_SPREAD_SPECTRUM_CYCLE_TIME_660_US 0b11\n\n#define IS31FL3729_PWM_FREQUENCY_55K_HZ 0b000\n#define IS31FL3729_PWM_FREQUENCY_32K_HZ 0b001\n#define IS31FL3729_PWM_FREQUENCY_4K_HZ 0b010\n#define IS31FL3729_PWM_FREQUENCY_2K_HZ 0b011\n#define IS31FL3729_PWM_FREQUENCY_1K_HZ 0b100\n#define IS31FL3729_PWM_FREQUENCY_500_HZ 0b101\n#define IS31FL3729_PWM_FREQUENCY_250_HZ 0b110\n#define IS31FL3729_PWM_FREQUENCY_80K_HZ 0b111\n\n#define IS31FL3729_CONFIG_SWS_15_9 0x01 // 15 CS x 9 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_8 0x11 // 16 CS x 8 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_7 0x21 // 16 CS x 7 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_6 0x31 // 16 CS x 6 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_5 0x41 // 16 CS x 5 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_4 0x51 // 16 CS x 4 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_3 0x61 // 16 CS x 3 SW matrix\n#define IS31FL3729_CONFIG_SWS_16_2 0x71 // 16 CS x 2 SW matrix\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n\n#define SW2_CS1 0x10\n#define SW2_CS2 0x11\n#define SW2_CS3 0x12\n#define SW2_CS4 0x13\n#define SW2_CS5 0x14\n#define SW2_CS6 0x15\n#define SW2_CS7 0x16\n#define SW2_CS8 0x17\n#define SW2_CS9 0x18\n#define SW2_CS10 0x19\n#define SW2_CS11 0x1A\n#define SW2_CS12 0x1B\n#define SW2_CS13 0x1C\n#define SW2_CS14 0x1D\n#define SW2_CS15 0x1E\n#define SW2_CS16 0x1F\n\n#define SW3_CS1 0x20\n#define SW3_CS2 0x21\n#define SW3_CS3 0x22\n#define SW3_CS4 0x23\n#define SW3_CS5 0x24\n#define SW3_CS6 0x25\n#define SW3_CS7 0x26\n#define SW3_CS8 0x27\n#define SW3_CS9 0x28\n#define SW3_CS10 0x29\n#define SW3_CS11 0x2A\n#define SW3_CS12 0x2B\n#define SW3_CS13 0x2C\n#define SW3_CS14 0x2D\n#define SW3_CS15 0x2E\n#define SW3_CS16 0x2F\n\n#define SW4_CS1 0x30\n#define SW4_CS2 0x31\n#define SW4_CS3 0x32\n#define SW4_CS4 0x33\n#define SW4_CS5 0x34\n#define SW4_CS6 0x35\n#define SW4_CS7 0x36\n#define SW4_CS8 0x37\n#define SW4_CS9 0x38\n#define SW4_CS10 0x39\n#define SW4_CS11 0x3A\n#define SW4_CS12 0x3B\n#define SW4_CS13 0x3C\n#define SW4_CS14 0x3D\n#define SW4_CS15 0x3E\n#define SW4_CS16 0x3F\n\n#define SW5_CS1 0x40\n#define SW5_CS2 0x41\n#define SW5_CS3 0x42\n#define SW5_CS4 0x43\n#define SW5_CS5 0x44\n#define SW5_CS6 0x45\n#define SW5_CS7 0x46\n#define SW5_CS8 0x47\n#define SW5_CS9 0x48\n#define SW5_CS10 0x49\n#define SW5_CS11 0x4A\n#define SW5_CS12 0x4B\n#define SW5_CS13 0x4C\n#define SW5_CS14 0x4D\n#define SW5_CS15 0x4E\n#define SW5_CS16 0x4F\n\n#define SW6_CS1 0x50\n#define SW6_CS2 0x51\n#define SW6_CS3 0x52\n#define SW6_CS4 0x53\n#define SW6_CS5 0x54\n#define SW6_CS6 0x55\n#define SW6_CS7 0x56\n#define SW6_CS8 0x57\n#define SW6_CS9 0x58\n#define SW6_CS10 0x59\n#define SW6_CS11 0x5A\n#define SW6_CS12 0x5B\n#define SW6_CS13 0x5C\n#define SW6_CS14 0x5D\n#define SW6_CS15 0x5E\n#define SW6_CS16 0x5F\n\n#define SW7_CS1 0x60\n#define SW7_CS2 0x61\n#define SW7_CS3 0x62\n#define SW7_CS4 0x63\n#define SW7_CS5 0x64\n#define SW7_CS6 0x65\n#define SW7_CS7 0x66\n#define SW7_CS8 0x67\n#define SW7_CS9 0x68\n#define SW7_CS10 0x69\n#define SW7_CS11 0x6A\n#define SW7_CS12 0x6B\n#define SW7_CS13 0x6C\n#define SW7_CS14 0x6D\n#define SW7_CS15 0x6E\n#define SW7_CS16 0x6F\n\n#define SW8_CS1 0x70\n#define SW8_CS2 0x71\n#define SW8_CS3 0x72\n#define SW8_CS4 0x73\n#define SW8_CS5 0x74\n#define SW8_CS6 0x75\n#define SW8_CS7 0x76\n#define SW8_CS8 0x77\n#define SW8_CS9 0x78\n#define SW8_CS10 0x79\n#define SW8_CS11 0x7A\n#define SW8_CS12 0x7B\n#define SW8_CS13 0x7C\n#define SW8_CS14 0x7D\n#define SW8_CS15 0x7E\n#define SW8_CS16 0x7F\n\n#define SW9_CS1 0x80\n#define SW9_CS2 0x81\n#define SW9_CS3 0x82\n#define SW9_CS4 0x83\n#define SW9_CS5 0x84\n#define SW9_CS6 0x85\n#define SW9_CS7 0x86\n#define SW9_CS8 0x87\n#define SW9_CS9 0x88\n#define SW9_CS10 0x89\n#define SW9_CS11 0x8A\n#define SW9_CS12 0x8B\n#define SW9_CS13 0x8C\n#define SW9_CS14 0x8D\n#define SW9_CS15 0x8E\n"
  },
  {
    "path": "drivers/led/issi/is31fl3731-mono.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2019 Clueboard\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3731-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3731_PWM_REGISTER_COUNT 144\n#define IS31FL3731_LED_CONTROL_REGISTER_COUNT 18\n\n#ifndef IS31FL3731_I2C_TIMEOUT\n#    define IS31FL3731_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3731_I2C_PERSISTENCE\n#    define IS31FL3731_I2C_PERSISTENCE 0\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3731_DRIVER_COUNT] = {\n    IS31FL3731_I2C_ADDRESS_1,\n#ifdef IS31FL3731_I2C_ADDRESS_2\n    IS31FL3731_I2C_ADDRESS_2,\n#    ifdef IS31FL3731_I2C_ADDRESS_3\n    IS31FL3731_I2C_ADDRESS_3,\n#        ifdef IS31FL3731_I2C_ADDRESS_4\n    IS31FL3731_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the IS31FL3731 PWM registers 0x24-0xB3.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in is31fl3731_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct is31fl3731_driver_t {\n    uint8_t pwm_buffer[IS31FL3731_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3731_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3731_driver_t;\n\nis31fl3731_driver_t driver_buffers[IS31FL3731_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3731_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3731_select_page(uint8_t index, uint8_t page) {\n    is31fl3731_write_register(index, IS31FL3731_REG_COMMAND, page);\n}\n\nvoid is31fl3731_write_pwm_buffer(uint8_t index) {\n    // Assumes page 0 is already selected.\n    // Transmit PWM registers in 9 transfers of 16 bytes.\n\n    // Iterate over the pwm_buffer contents at 16 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) {\n#if IS31FL3731_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3731_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3731_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3731_SDB_PIN)\n    gpio_set_pin_output(IS31FL3731_SDB_PIN);\n    gpio_write_pin_high(IS31FL3731_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {\n        is31fl3731_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3731_LED_COUNT; i++) {\n        is31fl3731_set_led_control_register(i, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {\n        is31fl3731_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3731_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, first enable software shutdown,\n    // then set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION);\n\n    // enable software shutdown\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00);\n#ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN);\n#endif\n\n    // this delay was copied from other drivers, might not be needed\n    wait_ms(10);\n\n    // picture mode\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE);\n    // display frame 0\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00);\n    // audio sync off\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00);\n\n    is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1);\n\n    // turn off all LEDs in the LED control register\n    for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3731_write_register(index, IS31FL3731_FRAME_REG_LED_CONTROL + i, 0x00);\n    }\n\n    // turn off all LEDs in the blink control register (not really needed)\n    for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3731_write_register(index, IS31FL3731_FRAME_REG_BLINK_CONTROL + i, 0x00);\n    }\n\n    // set PWM on all LEDs to 0\n    for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i++) {\n        is31fl3731_write_register(index, IS31FL3731_FRAME_REG_PWM + i, 0x00);\n    }\n\n    is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION);\n\n    // disable software shutdown\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01);\n\n    // select page 0 and leave it selected.\n    // most usage after initialization is just writing PWM buffers in page 0\n    // as there's not much point in double-buffering\n    is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1);\n}\n\nvoid is31fl3731_set_value(int index, uint8_t value) {\n    is31fl3731_led_t led;\n\n    if (index >= 0 && index < IS31FL3731_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.v] = value;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3731_set_value_all(uint8_t value) {\n    for (int i = 0; i < IS31FL3731_LED_COUNT; i++) {\n        is31fl3731_set_value(i, value);\n    }\n}\n\nvoid is31fl3731_set_led_control_register(uint8_t index, bool value) {\n    is31fl3731_led_t led;\n    memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));\n\n    uint8_t control_register = led.v / 8;\n    uint8_t bit_value        = led.v % 8;\n\n    if (value) {\n        driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value);\n    }\n\n    driver_buffers[led.driver].led_control_buffer_dirty = true;\n}\n\nvoid is31fl3731_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3731_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3731_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3731_write_register(index, i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3731_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {\n        is31fl3731_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3731-mono.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2019 Clueboard\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3731_REG_COMMAND 0xFD\n#define IS31FL3731_COMMAND_FRAME_1 0x00\n#define IS31FL3731_COMMAND_FRAME_2 0x01\n#define IS31FL3731_COMMAND_FRAME_3 0x02\n#define IS31FL3731_COMMAND_FRAME_4 0x03\n#define IS31FL3731_COMMAND_FRAME_5 0x04\n#define IS31FL3731_COMMAND_FRAME_6 0x05\n#define IS31FL3731_COMMAND_FRAME_7 0x06\n#define IS31FL3731_COMMAND_FRAME_8 0x07\n#define IS31FL3731_COMMAND_FUNCTION 0x0B\n\n#define IS31FL3731_FRAME_REG_LED_CONTROL 0x00\n#define IS31FL3731_FRAME_REG_BLINK_CONTROL 0x12\n#define IS31FL3731_FRAME_REG_PWM 0x24\n\n#define IS31FL3731_FUNCTION_REG_CONFIG 0x00\n#define IS31FL3731_CONFIG_MODE_PICTURE 0x00\n#define IS31FL3731_CONFIG_MODE_AUTO_PLAY 0x08\n#define IS31FL3731_CONFIG_MODE_AUDIO_PLAY 0x18\n\n#define IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY 0x01\n#define IS31FL3731_FUNCTION_REG_AUDIO_SYNC 0x06\n#define IS31FL3731_FUNCTION_REG_SHUTDOWN 0x0A\n\n// Not defined in the datasheet -- See AN for IC\n#define IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION 0xC2\n#define IS31FL3731_GHOST_IMAGE_PREVENTION_GEN 0x10\n\n#define IS31FL3731_I2C_ADDRESS_GND 0x74\n#define IS31FL3731_I2C_ADDRESS_SCL 0x75\n#define IS31FL3731_I2C_ADDRESS_SDA 0x76\n#define IS31FL3731_I2C_ADDRESS_VCC 0x77\n\n#if defined(LED_MATRIX_IS31FL3731)\n#    define IS31FL3731_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined IS31FL3731_I2C_ADDRESS_4\n#    define IS31FL3731_DRIVER_COUNT 4\n#elif defined IS31FL3731_I2C_ADDRESS_3\n#    define IS31FL3731_DRIVER_COUNT 3\n#elif defined IS31FL3731_I2C_ADDRESS_2\n#    define IS31FL3731_DRIVER_COUNT 2\n#elif defined IS31FL3731_I2C_ADDRESS_1\n#    define IS31FL3731_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3731_led_t {\n    uint8_t driver : 2;\n    uint8_t v;\n} PACKED is31fl3731_led_t;\n\nextern const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT];\n\nvoid is31fl3731_init_drivers(void);\nvoid is31fl3731_init(uint8_t index);\nvoid is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3731_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3731_set_value(int index, uint8_t value);\nvoid is31fl3731_set_value_all(uint8_t value);\n\nvoid is31fl3731_set_led_control_register(uint8_t index, bool value);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3731_update_pwm_buffers(uint8_t index);\nvoid is31fl3731_update_led_control_registers(uint8_t index);\n\nvoid is31fl3731_flush(void);\n\n#define C1_1 0x00\n#define C1_2 0x01\n#define C1_3 0x02\n#define C1_4 0x03\n#define C1_5 0x04\n#define C1_6 0x05\n#define C1_7 0x06\n#define C1_8 0x07\n\n#define C1_9 0x08\n#define C1_10 0x09\n#define C1_11 0x0A\n#define C1_12 0x0B\n#define C1_13 0x0C\n#define C1_14 0x0D\n#define C1_15 0x0E\n#define C1_16 0x0F\n\n#define C2_1 0x10\n#define C2_2 0x11\n#define C2_3 0x12\n#define C2_4 0x13\n#define C2_5 0x14\n#define C2_6 0x15\n#define C2_7 0x16\n#define C2_8 0x17\n\n#define C2_9 0x18\n#define C2_10 0x19\n#define C2_11 0x1A\n#define C2_12 0x1B\n#define C2_13 0x1C\n#define C2_14 0x1D\n#define C2_15 0x1E\n#define C2_16 0x1F\n\n#define C3_1 0x20\n#define C3_2 0x21\n#define C3_3 0x22\n#define C3_4 0x23\n#define C3_5 0x24\n#define C3_6 0x25\n#define C3_7 0x26\n#define C3_8 0x27\n\n#define C3_9 0x28\n#define C3_10 0x29\n#define C3_11 0x2A\n#define C3_12 0x2B\n#define C3_13 0x2C\n#define C3_14 0x2D\n#define C3_15 0x2E\n#define C3_16 0x2F\n\n#define C4_1 0x30\n#define C4_2 0x31\n#define C4_3 0x32\n#define C4_4 0x33\n#define C4_5 0x34\n#define C4_6 0x35\n#define C4_7 0x36\n#define C4_8 0x37\n\n#define C4_9 0x38\n#define C4_10 0x39\n#define C4_11 0x3A\n#define C4_12 0x3B\n#define C4_13 0x3C\n#define C4_14 0x3D\n#define C4_15 0x3E\n#define C4_16 0x3F\n\n#define C5_1 0x40\n#define C5_2 0x41\n#define C5_3 0x42\n#define C5_4 0x43\n#define C5_5 0x44\n#define C5_6 0x45\n#define C5_7 0x46\n#define C5_8 0x47\n\n#define C5_9 0x48\n#define C5_10 0x49\n#define C5_11 0x4A\n#define C5_12 0x4B\n#define C5_13 0x4C\n#define C5_14 0x4D\n#define C5_15 0x4E\n#define C5_16 0x4F\n\n#define C6_1 0x50\n#define C6_2 0x51\n#define C6_3 0x52\n#define C6_4 0x53\n#define C6_5 0x54\n#define C6_6 0x55\n#define C6_7 0x56\n#define C6_8 0x57\n\n#define C6_9 0x58\n#define C6_10 0x59\n#define C6_11 0x5A\n#define C6_12 0x5B\n#define C6_13 0x5C\n#define C6_14 0x5D\n#define C6_15 0x5E\n#define C6_16 0x5F\n\n#define C7_1 0x60\n#define C7_2 0x61\n#define C7_3 0x62\n#define C7_4 0x63\n#define C7_5 0x64\n#define C7_6 0x65\n#define C7_7 0x66\n#define C7_8 0x67\n\n#define C7_9 0x68\n#define C7_10 0x69\n#define C7_11 0x6A\n#define C7_12 0x6B\n#define C7_13 0x6C\n#define C7_14 0x6D\n#define C7_15 0x6E\n#define C7_16 0x6F\n\n#define C8_1 0x70\n#define C8_2 0x71\n#define C8_3 0x72\n#define C8_4 0x73\n#define C8_5 0x74\n#define C8_6 0x75\n#define C8_7 0x76\n#define C8_8 0x77\n\n#define C8_9 0x78\n#define C8_10 0x79\n#define C8_11 0x7A\n#define C8_12 0x7B\n#define C8_13 0x7C\n#define C8_14 0x7D\n#define C8_15 0x7E\n#define C8_16 0x7F\n\n#define C9_1 0x80\n#define C9_2 0x81\n#define C9_3 0x82\n#define C9_4 0x83\n#define C9_5 0x84\n#define C9_6 0x85\n#define C9_7 0x86\n#define C9_8 0x87\n\n#define C9_9 0x88\n#define C9_10 0x89\n#define C9_11 0x8A\n#define C9_12 0x8B\n#define C9_13 0x8C\n#define C9_14 0x8D\n#define C9_15 0x8E\n#define C9_16 0x8F\n"
  },
  {
    "path": "drivers/led/issi/is31fl3731.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3731.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3731_PWM_REGISTER_COUNT 144\n#define IS31FL3731_LED_CONTROL_REGISTER_COUNT 18\n\n#ifndef IS31FL3731_I2C_TIMEOUT\n#    define IS31FL3731_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3731_I2C_PERSISTENCE\n#    define IS31FL3731_I2C_PERSISTENCE 0\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3731_DRIVER_COUNT] = {\n    IS31FL3731_I2C_ADDRESS_1,\n#ifdef IS31FL3731_I2C_ADDRESS_2\n    IS31FL3731_I2C_ADDRESS_2,\n#    ifdef IS31FL3731_I2C_ADDRESS_3\n    IS31FL3731_I2C_ADDRESS_3,\n#        ifdef IS31FL3731_I2C_ADDRESS_4\n    IS31FL3731_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the IS31FL3731 PWM registers 0x24-0xB3.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in is31fl3731_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct is31fl3731_driver_t {\n    uint8_t pwm_buffer[IS31FL3731_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3731_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3731_driver_t;\n\nis31fl3731_driver_t driver_buffers[IS31FL3731_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3731_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3731_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3731_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3731_select_page(uint8_t index, uint8_t page) {\n    is31fl3731_write_register(index, IS31FL3731_REG_COMMAND, page);\n}\n\nvoid is31fl3731_write_pwm_buffer(uint8_t index) {\n    // Assumes page 0 is already selected.\n    // Transmit PWM registers in 9 transfers of 16 bytes.\n\n    // Iterate over the pwm_buffer contents at 16 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i += 16) {\n#if IS31FL3731_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3731_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, IS31FL3731_FRAME_REG_PWM + i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3731_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3731_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3731_SDB_PIN)\n    gpio_set_pin_output(IS31FL3731_SDB_PIN);\n    gpio_write_pin_high(IS31FL3731_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {\n        is31fl3731_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3731_LED_COUNT; i++) {\n        is31fl3731_set_led_control_register(i, true, true, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {\n        is31fl3731_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3731_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, first enable software shutdown,\n    // then set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION);\n\n    // enable software shutdown\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x00);\n#ifdef IS31FL3731_DEGHOST // set to enable de-ghosting of the array\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION, IS31FL3731_GHOST_IMAGE_PREVENTION_GEN);\n#endif\n\n    // this delay was copied from other drivers, might not be needed\n    wait_ms(10);\n\n    // picture mode\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_CONFIG, IS31FL3731_CONFIG_MODE_PICTURE);\n    // display frame 0\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY, 0x00);\n    // audio sync off\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_AUDIO_SYNC, 0x00);\n\n    is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1);\n\n    // turn off all LEDs in the LED control register\n    for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3731_write_register(index, IS31FL3731_FRAME_REG_LED_CONTROL + i, 0x00);\n    }\n\n    // turn off all LEDs in the blink control register (not really needed)\n    for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3731_write_register(index, IS31FL3731_FRAME_REG_BLINK_CONTROL + i, 0x00);\n    }\n\n    // set PWM on all LEDs to 0\n    for (uint8_t i = 0; i < IS31FL3731_PWM_REGISTER_COUNT; i++) {\n        is31fl3731_write_register(index, IS31FL3731_FRAME_REG_PWM + i, 0x00);\n    }\n\n    is31fl3731_select_page(index, IS31FL3731_COMMAND_FUNCTION);\n\n    // disable software shutdown\n    is31fl3731_write_register(index, IS31FL3731_FUNCTION_REG_SHUTDOWN, 0x01);\n\n    // select page 0 and leave it selected.\n    // most usage after initialization is just writing PWM buffers in page 0\n    // as there's not much point in double-buffering\n    is31fl3731_select_page(index, IS31FL3731_COMMAND_FRAME_1);\n}\n\nvoid is31fl3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3731_led_t led;\n\n    if (index >= 0 && index < IS31FL3731_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.r] = red;\n        driver_buffers[led.driver].pwm_buffer[led.g] = green;\n        driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < IS31FL3731_LED_COUNT; i++) {\n        is31fl3731_set_color(i, red, green, blue);\n    }\n}\n\nvoid is31fl3731_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {\n    is31fl3731_led_t led;\n    memcpy_P(&led, (&g_is31fl3731_leds[index]), sizeof(led));\n\n    uint8_t control_register_r = led.r / 8;\n    uint8_t control_register_g = led.g / 8;\n    uint8_t control_register_b = led.b / 8;\n    uint8_t bit_r              = led.r % 8;\n    uint8_t bit_g              = led.g % 8;\n    uint8_t bit_b              = led.b % 8;\n\n    if (red) {\n        driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);\n    }\n    if (green) {\n        driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);\n    }\n    if (blue) {\n        driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);\n    }\n\n    driver_buffers[led.driver].led_control_buffer_dirty = true;\n}\n\nvoid is31fl3731_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3731_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3731_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        for (uint8_t i = 0; i < IS31FL3731_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3731_write_register(index, i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3731_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3731_DRIVER_COUNT; i++) {\n        is31fl3731_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3731.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3731_REG_COMMAND 0xFD\n#define IS31FL3731_COMMAND_FRAME_1 0x00\n#define IS31FL3731_COMMAND_FRAME_2 0x01\n#define IS31FL3731_COMMAND_FRAME_3 0x02\n#define IS31FL3731_COMMAND_FRAME_4 0x03\n#define IS31FL3731_COMMAND_FRAME_5 0x04\n#define IS31FL3731_COMMAND_FRAME_6 0x05\n#define IS31FL3731_COMMAND_FRAME_7 0x06\n#define IS31FL3731_COMMAND_FRAME_8 0x07\n#define IS31FL3731_COMMAND_FUNCTION 0x0B\n\n#define IS31FL3731_FRAME_REG_LED_CONTROL 0x00\n#define IS31FL3731_FRAME_REG_BLINK_CONTROL 0x12\n#define IS31FL3731_FRAME_REG_PWM 0x24\n\n#define IS31FL3731_FUNCTION_REG_CONFIG 0x00\n#define IS31FL3731_CONFIG_MODE_PICTURE 0x00\n#define IS31FL3731_CONFIG_MODE_AUTO_PLAY 0x08\n#define IS31FL3731_CONFIG_MODE_AUDIO_PLAY 0x18\n\n#define IS31FL3731_FUNCTION_REG_PICTURE_DISPLAY 0x01\n#define IS31FL3731_FUNCTION_REG_AUDIO_SYNC 0x06\n#define IS31FL3731_FUNCTION_REG_SHUTDOWN 0x0A\n\n// Not defined in the datasheet -- See AN for IC\n#define IS31FL3731_FUNCTION_REG_GHOST_IMAGE_PREVENTION 0xC2\n#define IS31FL3731_GHOST_IMAGE_PREVENTION_GEN 0x10\n\n#define IS31FL3731_I2C_ADDRESS_GND 0x74\n#define IS31FL3731_I2C_ADDRESS_SCL 0x75\n#define IS31FL3731_I2C_ADDRESS_SDA 0x76\n#define IS31FL3731_I2C_ADDRESS_VCC 0x77\n\n#if defined(RGB_MATRIX_IS31FL3731)\n#    define IS31FL3731_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3731_I2C_ADDRESS_4)\n#    define IS31FL3731_DRIVER_COUNT 4\n#elif defined(IS31FL3731_I2C_ADDRESS_3)\n#    define IS31FL3731_DRIVER_COUNT 3\n#elif defined(IS31FL3731_I2C_ADDRESS_2)\n#    define IS31FL3731_DRIVER_COUNT 2\n#elif defined(IS31FL3731_I2C_ADDRESS_1)\n#    define IS31FL3731_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3731_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED is31fl3731_led_t;\n\nextern const is31fl3731_led_t PROGMEM g_is31fl3731_leds[IS31FL3731_LED_COUNT];\n\nvoid is31fl3731_init_drivers(void);\nvoid is31fl3731_init(uint8_t index);\nvoid is31fl3731_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3731_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3731_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid is31fl3731_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3731_set_led_control_register(uint8_t index, bool red, bool green, bool blue);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3731_update_pwm_buffers(uint8_t index);\nvoid is31fl3731_update_led_control_registers(uint8_t index);\n\nvoid is31fl3731_flush(void);\n\n#define C1_1 0x00\n#define C1_2 0x01\n#define C1_3 0x02\n#define C1_4 0x03\n#define C1_5 0x04\n#define C1_6 0x05\n#define C1_7 0x06\n#define C1_8 0x07\n\n#define C1_9 0x08\n#define C1_10 0x09\n#define C1_11 0x0A\n#define C1_12 0x0B\n#define C1_13 0x0C\n#define C1_14 0x0D\n#define C1_15 0x0E\n#define C1_16 0x0F\n\n#define C2_1 0x10\n#define C2_2 0x11\n#define C2_3 0x12\n#define C2_4 0x13\n#define C2_5 0x14\n#define C2_6 0x15\n#define C2_7 0x16\n#define C2_8 0x17\n\n#define C2_9 0x18\n#define C2_10 0x19\n#define C2_11 0x1A\n#define C2_12 0x1B\n#define C2_13 0x1C\n#define C2_14 0x1D\n#define C2_15 0x1E\n#define C2_16 0x1F\n\n#define C3_1 0x20\n#define C3_2 0x21\n#define C3_3 0x22\n#define C3_4 0x23\n#define C3_5 0x24\n#define C3_6 0x25\n#define C3_7 0x26\n#define C3_8 0x27\n\n#define C3_9 0x28\n#define C3_10 0x29\n#define C3_11 0x2A\n#define C3_12 0x2B\n#define C3_13 0x2C\n#define C3_14 0x2D\n#define C3_15 0x2E\n#define C3_16 0x2F\n\n#define C4_1 0x30\n#define C4_2 0x31\n#define C4_3 0x32\n#define C4_4 0x33\n#define C4_5 0x34\n#define C4_6 0x35\n#define C4_7 0x36\n#define C4_8 0x37\n\n#define C4_9 0x38\n#define C4_10 0x39\n#define C4_11 0x3A\n#define C4_12 0x3B\n#define C4_13 0x3C\n#define C4_14 0x3D\n#define C4_15 0x3E\n#define C4_16 0x3F\n\n#define C5_1 0x40\n#define C5_2 0x41\n#define C5_3 0x42\n#define C5_4 0x43\n#define C5_5 0x44\n#define C5_6 0x45\n#define C5_7 0x46\n#define C5_8 0x47\n\n#define C5_9 0x48\n#define C5_10 0x49\n#define C5_11 0x4A\n#define C5_12 0x4B\n#define C5_13 0x4C\n#define C5_14 0x4D\n#define C5_15 0x4E\n#define C5_16 0x4F\n\n#define C6_1 0x50\n#define C6_2 0x51\n#define C6_3 0x52\n#define C6_4 0x53\n#define C6_5 0x54\n#define C6_6 0x55\n#define C6_7 0x56\n#define C6_8 0x57\n\n#define C6_9 0x58\n#define C6_10 0x59\n#define C6_11 0x5A\n#define C6_12 0x5B\n#define C6_13 0x5C\n#define C6_14 0x5D\n#define C6_15 0x5E\n#define C6_16 0x5F\n\n#define C7_1 0x60\n#define C7_2 0x61\n#define C7_3 0x62\n#define C7_4 0x63\n#define C7_5 0x64\n#define C7_6 0x65\n#define C7_7 0x66\n#define C7_8 0x67\n\n#define C7_9 0x68\n#define C7_10 0x69\n#define C7_11 0x6A\n#define C7_12 0x6B\n#define C7_13 0x6C\n#define C7_14 0x6D\n#define C7_15 0x6E\n#define C7_16 0x6F\n\n#define C8_1 0x70\n#define C8_2 0x71\n#define C8_3 0x72\n#define C8_4 0x73\n#define C8_5 0x74\n#define C8_6 0x75\n#define C8_7 0x76\n#define C8_8 0x77\n\n#define C8_9 0x78\n#define C8_10 0x79\n#define C8_11 0x7A\n#define C8_12 0x7B\n#define C8_13 0x7C\n#define C8_14 0x7D\n#define C8_15 0x7E\n#define C8_16 0x7F\n\n#define C9_1 0x80\n#define C9_2 0x81\n#define C9_3 0x82\n#define C9_4 0x83\n#define C9_5 0x84\n#define C9_6 0x85\n#define C9_7 0x86\n#define C9_8 0x87\n\n#define C9_9 0x88\n#define C9_10 0x89\n#define C9_11 0x8A\n#define C9_12 0x8B\n#define C9_13 0x8C\n#define C9_14 0x8D\n#define C9_15 0x8E\n#define C9_16 0x8F\n"
  },
  {
    "path": "drivers/led/issi/is31fl3733-mono.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2021 Doni Crosby\n * Copyright 2021 Leo Deng\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3733-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3733_PWM_REGISTER_COUNT 192\n#define IS31FL3733_LED_CONTROL_REGISTER_COUNT 24\n\n#ifndef IS31FL3733_I2C_TIMEOUT\n#    define IS31FL3733_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3733_I2C_PERSISTENCE\n#    define IS31FL3733_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3733_PWM_FREQUENCY\n#    define IS31FL3733_PWM_FREQUENCY IS31FL3733_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3733B only\n#endif\n\n#ifndef IS31FL3733_SW_PULLUP\n#    define IS31FL3733_SW_PULLUP IS31FL3733_PUR_0_OHM\n#endif\n\n#ifndef IS31FL3733_CS_PULLDOWN\n#    define IS31FL3733_CS_PULLDOWN IS31FL3733_PDR_0_OHM\n#endif\n\n#ifndef IS31FL3733_GLOBAL_CURRENT\n#    define IS31FL3733_GLOBAL_CURRENT 0xFF\n#endif\n\n#ifndef IS31FL3733_SYNC_1\n#    define IS31FL3733_SYNC_1 IS31FL3733_SYNC_NONE\n#endif\n#ifndef IS31FL3733_SYNC_2\n#    define IS31FL3733_SYNC_2 IS31FL3733_SYNC_NONE\n#endif\n#ifndef IS31FL3733_SYNC_3\n#    define IS31FL3733_SYNC_3 IS31FL3733_SYNC_NONE\n#endif\n#ifndef IS31FL3733_SYNC_4\n#    define IS31FL3733_SYNC_4 IS31FL3733_SYNC_NONE\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3733_DRIVER_COUNT] = {\n    IS31FL3733_I2C_ADDRESS_1,\n#ifdef IS31FL3733_I2C_ADDRESS_2\n    IS31FL3733_I2C_ADDRESS_2,\n#    ifdef IS31FL3733_I2C_ADDRESS_3\n    IS31FL3733_I2C_ADDRESS_3,\n#        ifdef IS31FL3733_I2C_ADDRESS_4\n    IS31FL3733_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\nconst uint8_t driver_sync[IS31FL3733_DRIVER_COUNT] = {\n    IS31FL3733_SYNC_1,\n#ifdef IS31FL3733_I2C_ADDRESS_2\n    IS31FL3733_SYNC_2,\n#    ifdef IS31FL3733_I2C_ADDRESS_3\n    IS31FL3733_SYNC_3,\n#        ifdef IS31FL3733_I2C_ADDRESS_4\n    IS31FL3733_SYNC_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the IS31FL3733 PWM registers.\n// The control buffers match the page 0 LED On/Off registers.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in is31fl3733_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct is31fl3733_driver_t {\n    uint8_t pwm_buffer[IS31FL3733_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3733_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3733_driver_t;\n\nis31fl3733_driver_t driver_buffers[IS31FL3733_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3733_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3733_select_page(uint8_t index, uint8_t page) {\n    is31fl3733_write_register(index, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3733_write_register(index, IS31FL3733_REG_COMMAND, page);\n}\n\nvoid is31fl3733_write_pwm_buffer(uint8_t index) {\n    // Assumes page 1 is already selected.\n    // Transmit PWM registers in 12 transfers of 16 bytes.\n\n    // Iterate over the pwm_buffer contents at 16 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) {\n#if IS31FL3733_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3733_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3733_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3733_SDB_PIN)\n    gpio_set_pin_output(IS31FL3733_SDB_PIN);\n    gpio_write_pin_high(IS31FL3733_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {\n        is31fl3733_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3733_LED_COUNT; i++) {\n        is31fl3733_set_led_control_register(i, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {\n        is31fl3733_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3733_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3733_write_register(index, i, 0x00);\n    }\n\n    is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM);\n\n    // Set PWM on all LEDs to 0\n    // No need to setup Breath registers to PWM as that is the default.\n    for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) {\n        is31fl3733_write_register(index, i, 0x00);\n    }\n\n    is31fl3733_select_page(index, IS31FL3733_COMMAND_FUNCTION);\n\n    uint8_t sync = driver_sync[index];\n\n    // Set de-ghost pull-up resistors (SWx)\n    is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP);\n    // Set de-ghost pull-down resistors (CSx)\n    is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN);\n    // Set global current to maximum.\n    is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT);\n    // Disable software shutdown.\n    is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3733_set_value(int index, uint8_t value) {\n    is31fl3733_led_t led;\n\n    if (index >= 0 && index < IS31FL3733_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.v] = value;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3733_set_value_all(uint8_t value) {\n    for (int i = 0; i < IS31FL3733_LED_COUNT; i++) {\n        is31fl3733_set_value(i, value);\n    }\n}\n\nvoid is31fl3733_set_led_control_register(uint8_t index, bool value) {\n    is31fl3733_led_t led;\n    memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));\n\n    uint8_t control_register = led.v / 8;\n    uint8_t bit_value        = led.v % 8;\n\n    if (value) {\n        driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value);\n    }\n\n    driver_buffers[led.driver].led_control_buffer_dirty = true;\n}\n\nvoid is31fl3733_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM);\n\n        is31fl3733_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3733_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL);\n\n        for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3733_write_register(index, i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3733_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {\n        is31fl3733_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3733-mono.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2021 Doni Crosby\n * Copyright 2021 Leo Deng\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3733_REG_INTERRUPT_MASK 0xF0\n#define IS31FL3733_REG_INTERRUPT_STATUS 0xF1\n\n#define IS31FL3733_REG_COMMAND 0xFD\n\n#define IS31FL3733_COMMAND_LED_CONTROL 0x00\n#define IS31FL3733_COMMAND_PWM 0x01\n#define IS31FL3733_COMMAND_AUTO_BREATH 0x02\n#define IS31FL3733_COMMAND_FUNCTION 0x03\n\n#define IS31FL3733_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3733_FUNCTION_REG_SW_PULLUP 0x0F\n#define IS31FL3733_FUNCTION_REG_CS_PULLDOWN 0x10\n#define IS31FL3733_FUNCTION_REG_RESET 0x11\n\n#define IS31FL3733_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3733_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3733_I2C_ADDRESS_GND_GND 0x50\n#define IS31FL3733_I2C_ADDRESS_GND_SCL 0x51\n#define IS31FL3733_I2C_ADDRESS_GND_SDA 0x52\n#define IS31FL3733_I2C_ADDRESS_GND_VCC 0x53\n#define IS31FL3733_I2C_ADDRESS_SCL_GND 0x54\n#define IS31FL3733_I2C_ADDRESS_SCL_SCL 0x55\n#define IS31FL3733_I2C_ADDRESS_SCL_SDA 0x56\n#define IS31FL3733_I2C_ADDRESS_SCL_VCC 0x57\n#define IS31FL3733_I2C_ADDRESS_SDA_GND 0x58\n#define IS31FL3733_I2C_ADDRESS_SDA_SCL 0x59\n#define IS31FL3733_I2C_ADDRESS_SDA_SDA 0x5A\n#define IS31FL3733_I2C_ADDRESS_SDA_VCC 0x5B\n#define IS31FL3733_I2C_ADDRESS_VCC_GND 0x5C\n#define IS31FL3733_I2C_ADDRESS_VCC_SCL 0x5D\n#define IS31FL3733_I2C_ADDRESS_VCC_SDA 0x5E\n#define IS31FL3733_I2C_ADDRESS_VCC_VCC 0x5F\n\n#if defined(LED_MATRIX_IS31FL3733)\n#    define IS31FL3733_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3733_I2C_ADDRESS_4)\n#    define IS31FL3733_DRIVER_COUNT 4\n#elif defined(IS31FL3733_I2C_ADDRESS_3)\n#    define IS31FL3733_DRIVER_COUNT 3\n#elif defined(IS31FL3733_I2C_ADDRESS_2)\n#    define IS31FL3733_DRIVER_COUNT 2\n#elif defined(IS31FL3733_I2C_ADDRESS_1)\n#    define IS31FL3733_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3733_led_t {\n    uint8_t driver : 2;\n    uint8_t v;\n} PACKED is31fl3733_led_t;\n\nextern const is31fl3733_led_t PROGMEM g_is31fl3733_leds[IS31FL3733_LED_COUNT];\n\nvoid is31fl3733_init_drivers(void);\nvoid is31fl3733_init(uint8_t index);\nvoid is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3733_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3733_set_value(int index, uint8_t value);\nvoid is31fl3733_set_value_all(uint8_t value);\n\nvoid is31fl3733_set_led_control_register(uint8_t index, bool value);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3733_update_pwm_buffers(uint8_t index);\nvoid is31fl3733_update_led_control_registers(uint8_t index);\n\nvoid is31fl3733_flush(void);\n\n#define IS31FL3733_PDR_0_OHM 0b000   // No pull-down resistor\n#define IS31FL3733_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3733_PDR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3733_PDR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3733_PDR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3733_PDR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3733_PDR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3733_PDR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3733_PUR_0_OHM 0b000   // No pull-up resistor\n#define IS31FL3733_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3733_PUR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3733_PUR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3733_PUR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3733_PUR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3733_PUR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3733_PUR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3733_PWM_FREQUENCY_8K4_HZ 0b000\n#define IS31FL3733_PWM_FREQUENCY_4K2_HZ 0b001\n#define IS31FL3733_PWM_FREQUENCY_26K7_HZ 0b010\n#define IS31FL3733_PWM_FREQUENCY_2K1_HZ 0b011\n#define IS31FL3733_PWM_FREQUENCY_1K05_HZ 0b100\n\n#define IS31FL3733_SYNC_NONE 0b00\n#define IS31FL3733_SYNC_MASTER 0b01\n#define IS31FL3733_SYNC_SLAVE 0b10\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n\n#define SW2_CS1 0x10\n#define SW2_CS2 0x11\n#define SW2_CS3 0x12\n#define SW2_CS4 0x13\n#define SW2_CS5 0x14\n#define SW2_CS6 0x15\n#define SW2_CS7 0x16\n#define SW2_CS8 0x17\n#define SW2_CS9 0x18\n#define SW2_CS10 0x19\n#define SW2_CS11 0x1A\n#define SW2_CS12 0x1B\n#define SW2_CS13 0x1C\n#define SW2_CS14 0x1D\n#define SW2_CS15 0x1E\n#define SW2_CS16 0x1F\n\n#define SW3_CS1 0x20\n#define SW3_CS2 0x21\n#define SW3_CS3 0x22\n#define SW3_CS4 0x23\n#define SW3_CS5 0x24\n#define SW3_CS6 0x25\n#define SW3_CS7 0x26\n#define SW3_CS8 0x27\n#define SW3_CS9 0x28\n#define SW3_CS10 0x29\n#define SW3_CS11 0x2A\n#define SW3_CS12 0x2B\n#define SW3_CS13 0x2C\n#define SW3_CS14 0x2D\n#define SW3_CS15 0x2E\n#define SW3_CS16 0x2F\n\n#define SW4_CS1 0x30\n#define SW4_CS2 0x31\n#define SW4_CS3 0x32\n#define SW4_CS4 0x33\n#define SW4_CS5 0x34\n#define SW4_CS6 0x35\n#define SW4_CS7 0x36\n#define SW4_CS8 0x37\n#define SW4_CS9 0x38\n#define SW4_CS10 0x39\n#define SW4_CS11 0x3A\n#define SW4_CS12 0x3B\n#define SW4_CS13 0x3C\n#define SW4_CS14 0x3D\n#define SW4_CS15 0x3E\n#define SW4_CS16 0x3F\n\n#define SW5_CS1 0x40\n#define SW5_CS2 0x41\n#define SW5_CS3 0x42\n#define SW5_CS4 0x43\n#define SW5_CS5 0x44\n#define SW5_CS6 0x45\n#define SW5_CS7 0x46\n#define SW5_CS8 0x47\n#define SW5_CS9 0x48\n#define SW5_CS10 0x49\n#define SW5_CS11 0x4A\n#define SW5_CS12 0x4B\n#define SW5_CS13 0x4C\n#define SW5_CS14 0x4D\n#define SW5_CS15 0x4E\n#define SW5_CS16 0x4F\n\n#define SW6_CS1 0x50\n#define SW6_CS2 0x51\n#define SW6_CS3 0x52\n#define SW6_CS4 0x53\n#define SW6_CS5 0x54\n#define SW6_CS6 0x55\n#define SW6_CS7 0x56\n#define SW6_CS8 0x57\n#define SW6_CS9 0x58\n#define SW6_CS10 0x59\n#define SW6_CS11 0x5A\n#define SW6_CS12 0x5B\n#define SW6_CS13 0x5C\n#define SW6_CS14 0x5D\n#define SW6_CS15 0x5E\n#define SW6_CS16 0x5F\n\n#define SW7_CS1 0x60\n#define SW7_CS2 0x61\n#define SW7_CS3 0x62\n#define SW7_CS4 0x63\n#define SW7_CS5 0x64\n#define SW7_CS6 0x65\n#define SW7_CS7 0x66\n#define SW7_CS8 0x67\n#define SW7_CS9 0x68\n#define SW7_CS10 0x69\n#define SW7_CS11 0x6A\n#define SW7_CS12 0x6B\n#define SW7_CS13 0x6C\n#define SW7_CS14 0x6D\n#define SW7_CS15 0x6E\n#define SW7_CS16 0x6F\n\n#define SW8_CS1 0x70\n#define SW8_CS2 0x71\n#define SW8_CS3 0x72\n#define SW8_CS4 0x73\n#define SW8_CS5 0x74\n#define SW8_CS6 0x75\n#define SW8_CS7 0x76\n#define SW8_CS8 0x77\n#define SW8_CS9 0x78\n#define SW8_CS10 0x79\n#define SW8_CS11 0x7A\n#define SW8_CS12 0x7B\n#define SW8_CS13 0x7C\n#define SW8_CS14 0x7D\n#define SW8_CS15 0x7E\n#define SW8_CS16 0x7F\n\n#define SW9_CS1 0x80\n#define SW9_CS2 0x81\n#define SW9_CS3 0x82\n#define SW9_CS4 0x83\n#define SW9_CS5 0x84\n#define SW9_CS6 0x85\n#define SW9_CS7 0x86\n#define SW9_CS8 0x87\n#define SW9_CS9 0x88\n#define SW9_CS10 0x89\n#define SW9_CS11 0x8A\n#define SW9_CS12 0x8B\n#define SW9_CS13 0x8C\n#define SW9_CS14 0x8D\n#define SW9_CS15 0x8E\n#define SW9_CS16 0x8F\n\n#define SW10_CS1 0x90\n#define SW10_CS2 0x91\n#define SW10_CS3 0x92\n#define SW10_CS4 0x93\n#define SW10_CS5 0x94\n#define SW10_CS6 0x95\n#define SW10_CS7 0x96\n#define SW10_CS8 0x97\n#define SW10_CS9 0x98\n#define SW10_CS10 0x99\n#define SW10_CS11 0x9A\n#define SW10_CS12 0x9B\n#define SW10_CS13 0x9C\n#define SW10_CS14 0x9D\n#define SW10_CS15 0x9E\n#define SW10_CS16 0x9F\n\n#define SW11_CS1 0xA0\n#define SW11_CS2 0xA1\n#define SW11_CS3 0xA2\n#define SW11_CS4 0xA3\n#define SW11_CS5 0xA4\n#define SW11_CS6 0xA5\n#define SW11_CS7 0xA6\n#define SW11_CS8 0xA7\n#define SW11_CS9 0xA8\n#define SW11_CS10 0xA9\n#define SW11_CS11 0xAA\n#define SW11_CS12 0xAB\n#define SW11_CS13 0xAC\n#define SW11_CS14 0xAD\n#define SW11_CS15 0xAE\n#define SW11_CS16 0xAF\n\n#define SW12_CS1 0xB0\n#define SW12_CS2 0xB1\n#define SW12_CS3 0xB2\n#define SW12_CS4 0xB3\n#define SW12_CS5 0xB4\n#define SW12_CS6 0xB5\n#define SW12_CS7 0xB6\n#define SW12_CS8 0xB7\n#define SW12_CS9 0xB8\n#define SW12_CS10 0xB9\n#define SW12_CS11 0xBA\n#define SW12_CS12 0xBB\n#define SW12_CS13 0xBC\n#define SW12_CS14 0xBD\n#define SW12_CS15 0xBE\n#define SW12_CS16 0xBF\n"
  },
  {
    "path": "drivers/led/issi/is31fl3733.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3733.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3733_PWM_REGISTER_COUNT 192\n#define IS31FL3733_LED_CONTROL_REGISTER_COUNT 24\n\n#ifndef IS31FL3733_I2C_TIMEOUT\n#    define IS31FL3733_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3733_I2C_PERSISTENCE\n#    define IS31FL3733_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3733_PWM_FREQUENCY\n#    define IS31FL3733_PWM_FREQUENCY IS31FL3733_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3733B only\n#endif\n\n#ifndef IS31FL3733_SW_PULLUP\n#    define IS31FL3733_SW_PULLUP IS31FL3733_PUR_0_OHM\n#endif\n\n#ifndef IS31FL3733_CS_PULLDOWN\n#    define IS31FL3733_CS_PULLDOWN IS31FL3733_PDR_0_OHM\n#endif\n\n#ifndef IS31FL3733_GLOBAL_CURRENT\n#    define IS31FL3733_GLOBAL_CURRENT 0xFF\n#endif\n\n#ifndef IS31FL3733_SYNC_1\n#    define IS31FL3733_SYNC_1 IS31FL3733_SYNC_NONE\n#endif\n#ifndef IS31FL3733_SYNC_2\n#    define IS31FL3733_SYNC_2 IS31FL3733_SYNC_NONE\n#endif\n#ifndef IS31FL3733_SYNC_3\n#    define IS31FL3733_SYNC_3 IS31FL3733_SYNC_NONE\n#endif\n#ifndef IS31FL3733_SYNC_4\n#    define IS31FL3733_SYNC_4 IS31FL3733_SYNC_NONE\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3733_DRIVER_COUNT] = {\n    IS31FL3733_I2C_ADDRESS_1,\n#ifdef IS31FL3733_I2C_ADDRESS_2\n    IS31FL3733_I2C_ADDRESS_2,\n#    ifdef IS31FL3733_I2C_ADDRESS_3\n    IS31FL3733_I2C_ADDRESS_3,\n#        ifdef IS31FL3733_I2C_ADDRESS_4\n    IS31FL3733_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\nconst uint8_t driver_sync[IS31FL3733_DRIVER_COUNT] = {\n    IS31FL3733_SYNC_1,\n#ifdef IS31FL3733_I2C_ADDRESS_2\n    IS31FL3733_SYNC_2,\n#    ifdef IS31FL3733_I2C_ADDRESS_3\n    IS31FL3733_SYNC_3,\n#        ifdef IS31FL3733_I2C_ADDRESS_4\n    IS31FL3733_SYNC_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the IS31FL3733 PWM registers.\n// The control buffers match the page 0 LED On/Off registers.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in is31fl3733_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct is31fl3733_driver_t {\n    uint8_t pwm_buffer[IS31FL3733_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3733_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3733_driver_t;\n\nis31fl3733_driver_t driver_buffers[IS31FL3733_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3733_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3733_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3733_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3733_select_page(uint8_t index, uint8_t page) {\n    is31fl3733_write_register(index, IS31FL3733_REG_COMMAND_WRITE_LOCK, IS31FL3733_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3733_write_register(index, IS31FL3733_REG_COMMAND, page);\n}\n\nvoid is31fl3733_write_pwm_buffer(uint8_t index) {\n    // Assumes page 1 is already selected.\n    // Transmit PWM registers in 12 transfers of 16 bytes.\n\n    // Iterate over the pwm_buffer contents at 16 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i += 16) {\n#if IS31FL3733_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3733_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3733_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3733_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3733_SDB_PIN)\n    gpio_set_pin_output(IS31FL3733_SDB_PIN);\n    gpio_write_pin_high(IS31FL3733_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {\n        is31fl3733_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3733_LED_COUNT; i++) {\n        is31fl3733_set_led_control_register(i, true, true, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {\n        is31fl3733_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3733_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3733_write_register(index, i, 0x00);\n    }\n\n    is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM);\n\n    // Set PWM on all LEDs to 0\n    // No need to setup Breath registers to PWM as that is the default.\n    for (uint8_t i = 0; i < IS31FL3733_PWM_REGISTER_COUNT; i++) {\n        is31fl3733_write_register(index, i, 0x00);\n    }\n\n    is31fl3733_select_page(index, IS31FL3733_COMMAND_FUNCTION);\n\n    uint8_t sync = driver_sync[index];\n\n    // Set de-ghost pull-up resistors (SWx)\n    is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_SW_PULLUP, IS31FL3733_SW_PULLUP);\n    // Set de-ghost pull-down resistors (CSx)\n    is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CS_PULLDOWN, IS31FL3733_CS_PULLDOWN);\n    // Set global current to maximum.\n    is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3733_GLOBAL_CURRENT);\n    // Disable software shutdown.\n    is31fl3733_write_register(index, IS31FL3733_FUNCTION_REG_CONFIGURATION, ((sync & 0b11) << 6) | ((IS31FL3733_PWM_FREQUENCY & 0b111) << 3) | 0x01);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3733_led_t led;\n\n    if (index >= 0 && index < IS31FL3733_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.r] = red;\n        driver_buffers[led.driver].pwm_buffer[led.g] = green;\n        driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < IS31FL3733_LED_COUNT; i++) {\n        is31fl3733_set_color(i, red, green, blue);\n    }\n}\n\nvoid is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {\n    is31fl3733_led_t led;\n    memcpy_P(&led, (&g_is31fl3733_leds[index]), sizeof(led));\n\n    uint8_t control_register_r = led.r / 8;\n    uint8_t control_register_g = led.g / 8;\n    uint8_t control_register_b = led.b / 8;\n    uint8_t bit_r              = led.r % 8;\n    uint8_t bit_g              = led.g % 8;\n    uint8_t bit_b              = led.b % 8;\n\n    if (red) {\n        driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);\n    }\n    if (green) {\n        driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);\n    }\n    if (blue) {\n        driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);\n    }\n\n    driver_buffers[led.driver].led_control_buffer_dirty = true;\n}\n\nvoid is31fl3733_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3733_select_page(index, IS31FL3733_COMMAND_PWM);\n\n        is31fl3733_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3733_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        is31fl3733_select_page(index, IS31FL3733_COMMAND_LED_CONTROL);\n\n        for (uint8_t i = 0; i < IS31FL3733_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3733_write_register(index, i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3733_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3733_DRIVER_COUNT; i++) {\n        is31fl3733_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3733.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3733_REG_INTERRUPT_MASK 0xF0\n#define IS31FL3733_REG_INTERRUPT_STATUS 0xF1\n\n#define IS31FL3733_REG_COMMAND 0xFD\n\n#define IS31FL3733_COMMAND_LED_CONTROL 0x00\n#define IS31FL3733_COMMAND_PWM 0x01\n#define IS31FL3733_COMMAND_AUTO_BREATH 0x02\n#define IS31FL3733_COMMAND_FUNCTION 0x03\n\n#define IS31FL3733_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3733_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3733_FUNCTION_REG_SW_PULLUP 0x0F\n#define IS31FL3733_FUNCTION_REG_CS_PULLDOWN 0x10\n#define IS31FL3733_FUNCTION_REG_RESET 0x11\n\n#define IS31FL3733_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3733_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3733_I2C_ADDRESS_GND_GND 0x50\n#define IS31FL3733_I2C_ADDRESS_GND_SCL 0x51\n#define IS31FL3733_I2C_ADDRESS_GND_SDA 0x52\n#define IS31FL3733_I2C_ADDRESS_GND_VCC 0x53\n#define IS31FL3733_I2C_ADDRESS_SCL_GND 0x54\n#define IS31FL3733_I2C_ADDRESS_SCL_SCL 0x55\n#define IS31FL3733_I2C_ADDRESS_SCL_SDA 0x56\n#define IS31FL3733_I2C_ADDRESS_SCL_VCC 0x57\n#define IS31FL3733_I2C_ADDRESS_SDA_GND 0x58\n#define IS31FL3733_I2C_ADDRESS_SDA_SCL 0x59\n#define IS31FL3733_I2C_ADDRESS_SDA_SDA 0x5A\n#define IS31FL3733_I2C_ADDRESS_SDA_VCC 0x5B\n#define IS31FL3733_I2C_ADDRESS_VCC_GND 0x5C\n#define IS31FL3733_I2C_ADDRESS_VCC_SCL 0x5D\n#define IS31FL3733_I2C_ADDRESS_VCC_SDA 0x5E\n#define IS31FL3733_I2C_ADDRESS_VCC_VCC 0x5F\n\n#if defined(RGB_MATRIX_IS31FL3733)\n#    define IS31FL3733_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3733_I2C_ADDRESS_4)\n#    define IS31FL3733_DRIVER_COUNT 4\n#elif defined(IS31FL3733_I2C_ADDRESS_3)\n#    define IS31FL3733_DRIVER_COUNT 3\n#elif defined(IS31FL3733_I2C_ADDRESS_2)\n#    define IS31FL3733_DRIVER_COUNT 2\n#elif defined(IS31FL3733_I2C_ADDRESS_1)\n#    define IS31FL3733_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3733_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED is31fl3733_led_t;\n\nextern const is31fl3733_led_t PROGMEM g_is31fl3733_leds[IS31FL3733_LED_COUNT];\n\nvoid is31fl3733_init_drivers(void);\nvoid is31fl3733_init(uint8_t index);\nvoid is31fl3733_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3733_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3733_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid is31fl3733_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3733_set_led_control_register(uint8_t index, bool red, bool green, bool blue);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3733_update_pwm_buffers(uint8_t index);\nvoid is31fl3733_update_led_control_registers(uint8_t index);\n\nvoid is31fl3733_flush(void);\n\n#define IS31FL3733_PDR_0_OHM 0b000   // No pull-down resistor\n#define IS31FL3733_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3733_PDR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3733_PDR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3733_PDR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3733_PDR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3733_PDR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3733_PDR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3733_PUR_0_OHM 0b000   // No pull-up resistor\n#define IS31FL3733_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3733_PUR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3733_PUR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3733_PUR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3733_PUR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3733_PUR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3733_PUR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3733_PWM_FREQUENCY_8K4_HZ 0b000\n#define IS31FL3733_PWM_FREQUENCY_4K2_HZ 0b001\n#define IS31FL3733_PWM_FREQUENCY_26K7_HZ 0b010\n#define IS31FL3733_PWM_FREQUENCY_2K1_HZ 0b011\n#define IS31FL3733_PWM_FREQUENCY_1K05_HZ 0b100\n\n#define IS31FL3733_SYNC_NONE 0b00\n#define IS31FL3733_SYNC_MASTER 0b01\n#define IS31FL3733_SYNC_SLAVE 0b10\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n\n#define SW2_CS1 0x10\n#define SW2_CS2 0x11\n#define SW2_CS3 0x12\n#define SW2_CS4 0x13\n#define SW2_CS5 0x14\n#define SW2_CS6 0x15\n#define SW2_CS7 0x16\n#define SW2_CS8 0x17\n#define SW2_CS9 0x18\n#define SW2_CS10 0x19\n#define SW2_CS11 0x1A\n#define SW2_CS12 0x1B\n#define SW2_CS13 0x1C\n#define SW2_CS14 0x1D\n#define SW2_CS15 0x1E\n#define SW2_CS16 0x1F\n\n#define SW3_CS1 0x20\n#define SW3_CS2 0x21\n#define SW3_CS3 0x22\n#define SW3_CS4 0x23\n#define SW3_CS5 0x24\n#define SW3_CS6 0x25\n#define SW3_CS7 0x26\n#define SW3_CS8 0x27\n#define SW3_CS9 0x28\n#define SW3_CS10 0x29\n#define SW3_CS11 0x2A\n#define SW3_CS12 0x2B\n#define SW3_CS13 0x2C\n#define SW3_CS14 0x2D\n#define SW3_CS15 0x2E\n#define SW3_CS16 0x2F\n\n#define SW4_CS1 0x30\n#define SW4_CS2 0x31\n#define SW4_CS3 0x32\n#define SW4_CS4 0x33\n#define SW4_CS5 0x34\n#define SW4_CS6 0x35\n#define SW4_CS7 0x36\n#define SW4_CS8 0x37\n#define SW4_CS9 0x38\n#define SW4_CS10 0x39\n#define SW4_CS11 0x3A\n#define SW4_CS12 0x3B\n#define SW4_CS13 0x3C\n#define SW4_CS14 0x3D\n#define SW4_CS15 0x3E\n#define SW4_CS16 0x3F\n\n#define SW5_CS1 0x40\n#define SW5_CS2 0x41\n#define SW5_CS3 0x42\n#define SW5_CS4 0x43\n#define SW5_CS5 0x44\n#define SW5_CS6 0x45\n#define SW5_CS7 0x46\n#define SW5_CS8 0x47\n#define SW5_CS9 0x48\n#define SW5_CS10 0x49\n#define SW5_CS11 0x4A\n#define SW5_CS12 0x4B\n#define SW5_CS13 0x4C\n#define SW5_CS14 0x4D\n#define SW5_CS15 0x4E\n#define SW5_CS16 0x4F\n\n#define SW6_CS1 0x50\n#define SW6_CS2 0x51\n#define SW6_CS3 0x52\n#define SW6_CS4 0x53\n#define SW6_CS5 0x54\n#define SW6_CS6 0x55\n#define SW6_CS7 0x56\n#define SW6_CS8 0x57\n#define SW6_CS9 0x58\n#define SW6_CS10 0x59\n#define SW6_CS11 0x5A\n#define SW6_CS12 0x5B\n#define SW6_CS13 0x5C\n#define SW6_CS14 0x5D\n#define SW6_CS15 0x5E\n#define SW6_CS16 0x5F\n\n#define SW7_CS1 0x60\n#define SW7_CS2 0x61\n#define SW7_CS3 0x62\n#define SW7_CS4 0x63\n#define SW7_CS5 0x64\n#define SW7_CS6 0x65\n#define SW7_CS7 0x66\n#define SW7_CS8 0x67\n#define SW7_CS9 0x68\n#define SW7_CS10 0x69\n#define SW7_CS11 0x6A\n#define SW7_CS12 0x6B\n#define SW7_CS13 0x6C\n#define SW7_CS14 0x6D\n#define SW7_CS15 0x6E\n#define SW7_CS16 0x6F\n\n#define SW8_CS1 0x70\n#define SW8_CS2 0x71\n#define SW8_CS3 0x72\n#define SW8_CS4 0x73\n#define SW8_CS5 0x74\n#define SW8_CS6 0x75\n#define SW8_CS7 0x76\n#define SW8_CS8 0x77\n#define SW8_CS9 0x78\n#define SW8_CS10 0x79\n#define SW8_CS11 0x7A\n#define SW8_CS12 0x7B\n#define SW8_CS13 0x7C\n#define SW8_CS14 0x7D\n#define SW8_CS15 0x7E\n#define SW8_CS16 0x7F\n\n#define SW9_CS1 0x80\n#define SW9_CS2 0x81\n#define SW9_CS3 0x82\n#define SW9_CS4 0x83\n#define SW9_CS5 0x84\n#define SW9_CS6 0x85\n#define SW9_CS7 0x86\n#define SW9_CS8 0x87\n#define SW9_CS9 0x88\n#define SW9_CS10 0x89\n#define SW9_CS11 0x8A\n#define SW9_CS12 0x8B\n#define SW9_CS13 0x8C\n#define SW9_CS14 0x8D\n#define SW9_CS15 0x8E\n#define SW9_CS16 0x8F\n\n#define SW10_CS1 0x90\n#define SW10_CS2 0x91\n#define SW10_CS3 0x92\n#define SW10_CS4 0x93\n#define SW10_CS5 0x94\n#define SW10_CS6 0x95\n#define SW10_CS7 0x96\n#define SW10_CS8 0x97\n#define SW10_CS9 0x98\n#define SW10_CS10 0x99\n#define SW10_CS11 0x9A\n#define SW10_CS12 0x9B\n#define SW10_CS13 0x9C\n#define SW10_CS14 0x9D\n#define SW10_CS15 0x9E\n#define SW10_CS16 0x9F\n\n#define SW11_CS1 0xA0\n#define SW11_CS2 0xA1\n#define SW11_CS3 0xA2\n#define SW11_CS4 0xA3\n#define SW11_CS5 0xA4\n#define SW11_CS6 0xA5\n#define SW11_CS7 0xA6\n#define SW11_CS8 0xA7\n#define SW11_CS9 0xA8\n#define SW11_CS10 0xA9\n#define SW11_CS11 0xAA\n#define SW11_CS12 0xAB\n#define SW11_CS13 0xAC\n#define SW11_CS14 0xAD\n#define SW11_CS15 0xAE\n#define SW11_CS16 0xAF\n\n#define SW12_CS1 0xB0\n#define SW12_CS2 0xB1\n#define SW12_CS3 0xB2\n#define SW12_CS4 0xB3\n#define SW12_CS5 0xB4\n#define SW12_CS6 0xB5\n#define SW12_CS7 0xB6\n#define SW12_CS8 0xB7\n#define SW12_CS9 0xB8\n#define SW12_CS10 0xB9\n#define SW12_CS11 0xBA\n#define SW12_CS12 0xBB\n#define SW12_CS13 0xBC\n#define SW12_CS14 0xBD\n#define SW12_CS15 0xBE\n#define SW12_CS16 0xBF\n"
  },
  {
    "path": "drivers/led/issi/is31fl3736-mono.c",
    "content": "/* Copyright 2018 Jason Williams (Wilba)\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3736-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3736_PWM_REGISTER_COUNT 192 // actually 96\n#define IS31FL3736_LED_CONTROL_REGISTER_COUNT 24\n\n#ifndef IS31FL3736_I2C_TIMEOUT\n#    define IS31FL3736_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3736_I2C_PERSISTENCE\n#    define IS31FL3736_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3736_PWM_FREQUENCY\n#    define IS31FL3736_PWM_FREQUENCY IS31FL3736_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3736B only\n#endif\n\n#ifndef IS31FL3736_SW_PULLUP\n#    define IS31FL3736_SW_PULLUP IS31FL3736_PUR_0_OHM\n#endif\n\n#ifndef IS31FL3736_CS_PULLDOWN\n#    define IS31FL3736_CS_PULLDOWN IS31FL3736_PDR_0_OHM\n#endif\n\n#ifndef IS31FL3736_GLOBAL_CURRENT\n#    define IS31FL3736_GLOBAL_CURRENT 0xFF\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3736_DRIVER_COUNT] = {\n    IS31FL3736_I2C_ADDRESS_1,\n#ifdef IS31FL3736_I2C_ADDRESS_2\n    IS31FL3736_I2C_ADDRESS_2,\n#    ifdef IS31FL3736_I2C_ADDRESS_3\n    IS31FL3736_I2C_ADDRESS_3,\n#        ifdef IS31FL3736_I2C_ADDRESS_4\n    IS31FL3736_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the IS31FL3736 PWM registers.\n// The control buffers match the page 0 LED On/Off registers.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in is31fl3736_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct is31fl3736_driver_t {\n    uint8_t pwm_buffer[IS31FL3736_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3736_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3736_driver_t;\n\nis31fl3736_driver_t driver_buffers[IS31FL3736_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3736_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3736_select_page(uint8_t index, uint8_t page) {\n    is31fl3736_write_register(index, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3736_write_register(index, IS31FL3736_REG_COMMAND, page);\n}\n\nvoid is31fl3736_write_pwm_buffer(uint8_t index) {\n    // Assumes page 1 is already selected.\n    // Transmit PWM registers in 12 transfers of 16 bytes.\n\n    // Iterate over the pwm_buffer contents at 16 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i += 16) {\n#if IS31FL3736_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3736_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3736_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3736_SDB_PIN)\n    gpio_set_pin_output(IS31FL3736_SDB_PIN);\n    gpio_write_pin_high(IS31FL3736_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {\n        is31fl3736_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3736_LED_COUNT; i++) {\n        is31fl3736_set_led_control_register(i, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {\n        is31fl3736_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3736_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3736_write_register(index, i, 0x00);\n    }\n\n    is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM);\n\n    // Set PWM on all LEDs to 0\n    // No need to setup Breath registers to PWM as that is the default.\n    for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i++) {\n        is31fl3736_write_register(index, i, 0x00);\n    }\n\n    is31fl3736_select_page(index, IS31FL3736_COMMAND_FUNCTION);\n\n    // Set de-ghost pull-up resistors (SWx)\n    is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_SW_PULLUP, IS31FL3736_SW_PULLUP);\n    // Set de-ghost pull-down resistors (CSx)\n    is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CS_PULLDOWN, IS31FL3736_CS_PULLDOWN);\n    // Set global current to maximum.\n    is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3736_GLOBAL_CURRENT);\n    // Disable software shutdown.\n    is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3736_set_value(int index, uint8_t value) {\n    is31fl3736_led_t led;\n\n    if (index >= 0 && index < IS31FL3736_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.v] = value;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3736_set_value_all(uint8_t value) {\n    for (int i = 0; i < IS31FL3736_LED_COUNT; i++) {\n        is31fl3736_set_value(i, value);\n    }\n}\n\nvoid is31fl3736_set_led_control_register(uint8_t index, bool value) {\n    is31fl3736_led_t led;\n    memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led));\n\n    // The PWM register for a matrix position (0x00 to 0xBF) is interleaved, so:\n    // A1=0x00 A2=0x02 A3=0x04 A4=0x06 A5=0x08 A6=0x0A A7=0x0C A8=0x0E\n    // B1=0x10 B2=0x12 B3=0x14\n    // But also, the LED control registers (0x00 to 0x17) are also interleaved, so:\n    // A1-A4=0x00 A5-A8=0x01\n\n    uint8_t control_register = led.v / 8;\n    uint8_t bit_value        = led.v % 8;\n\n    if (value) {\n        driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value);\n    }\n\n    driver_buffers[led.driver].led_control_buffer_dirty = true;\n}\n\nvoid is31fl3736_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM);\n\n        is31fl3736_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3736_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL);\n\n        for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3736_write_register(index, i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3736_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {\n        is31fl3736_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3736-mono.h",
    "content": "/* Copyright 2018 Jason Williams (Wilba)\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3736_REG_INTERRUPT_MASK 0xF0\n#define IS31FL3736_REG_INTERRUPT_STATUS 0xF1\n\n#define IS31FL3736_REG_COMMAND 0xFD\n\n#define IS31FL3736_COMMAND_LED_CONTROL 0x00\n#define IS31FL3736_COMMAND_PWM 0x01\n#define IS31FL3736_COMMAND_AUTO_BREATH 0x02\n#define IS31FL3736_COMMAND_FUNCTION 0x03\n\n#define IS31FL3736_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3736_FUNCTION_REG_SW_PULLUP 0x0F\n#define IS31FL3736_FUNCTION_REG_CS_PULLDOWN 0x10\n#define IS31FL3736_FUNCTION_REG_RESET 0x11\n\n#define IS31FL3736_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3736_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3736_I2C_ADDRESS_GND_GND 0x50\n#define IS31FL3736_I2C_ADDRESS_GND_SCL 0x51\n#define IS31FL3736_I2C_ADDRESS_GND_SDA 0x52\n#define IS31FL3736_I2C_ADDRESS_GND_VCC 0x53\n#define IS31FL3736_I2C_ADDRESS_SCL_GND 0x54\n#define IS31FL3736_I2C_ADDRESS_SCL_SCL 0x55\n#define IS31FL3736_I2C_ADDRESS_SCL_SDA 0x56\n#define IS31FL3736_I2C_ADDRESS_SCL_VCC 0x57\n#define IS31FL3736_I2C_ADDRESS_SDA_GND 0x58\n#define IS31FL3736_I2C_ADDRESS_SDA_SCL 0x59\n#define IS31FL3736_I2C_ADDRESS_SDA_SDA 0x5A\n#define IS31FL3736_I2C_ADDRESS_SDA_VCC 0x5B\n#define IS31FL3736_I2C_ADDRESS_VCC_GND 0x5C\n#define IS31FL3736_I2C_ADDRESS_VCC_SCL 0x5D\n#define IS31FL3736_I2C_ADDRESS_VCC_SDA 0x5E\n#define IS31FL3736_I2C_ADDRESS_VCC_VCC 0x5F\n\n#if defined(LED_MATRIX_IS31FL3736)\n#    define IS31FL3736_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3736_I2C_ADDRESS_4)\n#    define IS31FL3736_DRIVER_COUNT 4\n#elif defined(IS31FL3736_I2C_ADDRESS_3)\n#    define IS31FL3736_DRIVER_COUNT 3\n#elif defined(IS31FL3736_I2C_ADDRESS_2)\n#    define IS31FL3736_DRIVER_COUNT 2\n#elif defined(IS31FL3736_I2C_ADDRESS_1)\n#    define IS31FL3736_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3736_led_t {\n    uint8_t driver : 2;\n    uint8_t v;\n} PACKED is31fl3736_led_t;\n\nextern const is31fl3736_led_t PROGMEM g_is31fl3736_leds[IS31FL3736_LED_COUNT];\n\nvoid is31fl3736_init_drivers(void);\nvoid is31fl3736_init(uint8_t index);\nvoid is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3736_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3736_set_value(int index, uint8_t value);\nvoid is31fl3736_set_value_all(uint8_t value);\n\nvoid is31fl3736_set_led_control_register(uint8_t index, bool value);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3736_update_pwm_buffers(uint8_t index);\nvoid is31fl3736_update_led_control_registers(uint8_t index);\n\nvoid is31fl3736_flush(void);\n\n#define IS31FL3736_PDR_0_OHM 0b000   // No pull-down resistor\n#define IS31FL3736_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3736_PDR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3736_PDR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3736_PDR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3736_PDR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3736_PDR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3736_PDR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3736_PUR_0_OHM 0b000   // No pull-up resistor\n#define IS31FL3736_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3736_PUR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3736_PUR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3736_PUR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3736_PUR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3736_PUR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3736_PUR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3736_PWM_FREQUENCY_8K4_HZ 0b000\n#define IS31FL3736_PWM_FREQUENCY_4K2_HZ 0b001\n#define IS31FL3736_PWM_FREQUENCY_26K7_HZ 0b010\n#define IS31FL3736_PWM_FREQUENCY_2K1_HZ 0b011\n#define IS31FL3736_PWM_FREQUENCY_1K05_HZ 0b100\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x02\n#define SW1_CS3 0x04\n#define SW1_CS4 0x06\n#define SW1_CS5 0x08\n#define SW1_CS6 0x0A\n#define SW1_CS7 0x0C\n#define SW1_CS8 0x0E\n\n#define SW2_CS1 0x10\n#define SW2_CS2 0x12\n#define SW2_CS3 0x14\n#define SW2_CS4 0x16\n#define SW2_CS5 0x18\n#define SW2_CS6 0x1A\n#define SW2_CS7 0x1C\n#define SW2_CS8 0x1E\n\n#define SW3_CS1 0x20\n#define SW3_CS2 0x22\n#define SW3_CS3 0x24\n#define SW3_CS4 0x26\n#define SW3_CS5 0x28\n#define SW3_CS6 0x2A\n#define SW3_CS7 0x2C\n#define SW3_CS8 0x2E\n\n#define SW4_CS1 0x30\n#define SW4_CS2 0x32\n#define SW4_CS3 0x34\n#define SW4_CS4 0x36\n#define SW4_CS5 0x38\n#define SW4_CS6 0x3A\n#define SW4_CS7 0x3C\n#define SW4_CS8 0x3E\n\n#define SW5_CS1 0x40\n#define SW5_CS2 0x42\n#define SW5_CS3 0x44\n#define SW5_CS4 0x46\n#define SW5_CS5 0x48\n#define SW5_CS6 0x4A\n#define SW5_CS7 0x4C\n#define SW5_CS8 0x4E\n\n#define SW6_CS1 0x50\n#define SW6_CS2 0x52\n#define SW6_CS3 0x54\n#define SW6_CS4 0x56\n#define SW6_CS5 0x58\n#define SW6_CS6 0x5A\n#define SW6_CS7 0x5C\n#define SW6_CS8 0x5E\n\n#define SW7_CS1 0x60\n#define SW7_CS2 0x62\n#define SW7_CS3 0x64\n#define SW7_CS4 0x66\n#define SW7_CS5 0x68\n#define SW7_CS6 0x6A\n#define SW7_CS7 0x6C\n#define SW7_CS8 0x6E\n\n#define SW8_CS1 0x70\n#define SW8_CS2 0x72\n#define SW8_CS3 0x74\n#define SW8_CS4 0x76\n#define SW8_CS5 0x78\n#define SW8_CS6 0x7A\n#define SW8_CS7 0x7C\n#define SW8_CS8 0x7E\n\n#define SW9_CS1 0x80\n#define SW9_CS2 0x82\n#define SW9_CS3 0x84\n#define SW9_CS4 0x86\n#define SW9_CS5 0x88\n#define SW9_CS6 0x8A\n#define SW9_CS7 0x8C\n#define SW9_CS8 0x8E\n\n#define SW10_CS1 0x90\n#define SW10_CS2 0x92\n#define SW10_CS3 0x94\n#define SW10_CS4 0x96\n#define SW10_CS5 0x98\n#define SW10_CS6 0x9A\n#define SW10_CS7 0x9C\n#define SW10_CS8 0x9E\n\n#define SW11_CS1 0xA0\n#define SW11_CS2 0xA2\n#define SW11_CS3 0xA4\n#define SW11_CS4 0xA6\n#define SW11_CS5 0xA8\n#define SW11_CS6 0xAA\n#define SW11_CS7 0xAC\n#define SW11_CS8 0xAE\n\n#define SW12_CS1 0xB0\n#define SW12_CS2 0xB2\n#define SW12_CS3 0xB4\n#define SW12_CS4 0xB6\n#define SW12_CS5 0xB8\n#define SW12_CS6 0xBA\n#define SW12_CS7 0xBC\n#define SW12_CS8 0xBE\n"
  },
  {
    "path": "drivers/led/issi/is31fl3736.c",
    "content": "/* Copyright 2018 Jason Williams (Wilba)\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3736.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3736_PWM_REGISTER_COUNT 192 // actually 96\n#define IS31FL3736_LED_CONTROL_REGISTER_COUNT 24\n\n#ifndef IS31FL3736_I2C_TIMEOUT\n#    define IS31FL3736_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3736_I2C_PERSISTENCE\n#    define IS31FL3736_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3736_PWM_FREQUENCY\n#    define IS31FL3736_PWM_FREQUENCY IS31FL3736_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3736B only\n#endif\n\n#ifndef IS31FL3736_SW_PULLUP\n#    define IS31FL3736_SW_PULLUP IS31FL3736_PUR_0_OHM\n#endif\n\n#ifndef IS31FL3736_CS_PULLDOWN\n#    define IS31FL3736_CS_PULLDOWN IS31FL3736_PDR_0_OHM\n#endif\n\n#ifndef IS31FL3736_GLOBAL_CURRENT\n#    define IS31FL3736_GLOBAL_CURRENT 0xFF\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3736_DRIVER_COUNT] = {\n    IS31FL3736_I2C_ADDRESS_1,\n#ifdef IS31FL3736_I2C_ADDRESS_2\n    IS31FL3736_I2C_ADDRESS_2,\n#    ifdef IS31FL3736_I2C_ADDRESS_3\n    IS31FL3736_I2C_ADDRESS_3,\n#        ifdef IS31FL3736_I2C_ADDRESS_4\n    IS31FL3736_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the IS31FL3736 PWM registers.\n// The control buffers match the page 0 LED On/Off registers.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in is31fl3736_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct is31fl3736_driver_t {\n    uint8_t pwm_buffer[IS31FL3736_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3736_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3736_driver_t;\n\nis31fl3736_driver_t driver_buffers[IS31FL3736_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3736_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3736_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3736_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3736_select_page(uint8_t index, uint8_t page) {\n    is31fl3736_write_register(index, IS31FL3736_REG_COMMAND_WRITE_LOCK, IS31FL3736_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3736_write_register(index, IS31FL3736_REG_COMMAND, page);\n}\n\nvoid is31fl3736_write_pwm_buffer(uint8_t index) {\n    // Assumes page 1 is already selected.\n    // Transmit PWM registers in 12 transfers of 16 bytes.\n\n    // Iterate over the pwm_buffer contents at 16 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i += 16) {\n#if IS31FL3736_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3736_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3736_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3736_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3736_SDB_PIN)\n    gpio_set_pin_output(IS31FL3736_SDB_PIN);\n    gpio_write_pin_high(IS31FL3736_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {\n        is31fl3736_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3736_LED_COUNT; i++) {\n        is31fl3736_set_led_control_register(i, true, true, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {\n        is31fl3736_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3736_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3736_write_register(index, i, 0x00);\n    }\n\n    is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM);\n\n    // Set PWM on all LEDs to 0\n    // No need to setup Breath registers to PWM as that is the default.\n    for (uint8_t i = 0; i < IS31FL3736_PWM_REGISTER_COUNT; i++) {\n        is31fl3736_write_register(index, i, 0x00);\n    }\n\n    is31fl3736_select_page(index, IS31FL3736_COMMAND_FUNCTION);\n\n    // Set de-ghost pull-up resistors (SWx)\n    is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_SW_PULLUP, IS31FL3736_SW_PULLUP);\n    // Set de-ghost pull-down resistors (CSx)\n    is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CS_PULLDOWN, IS31FL3736_CS_PULLDOWN);\n    // Set global current to maximum.\n    is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3736_GLOBAL_CURRENT);\n    // Disable software shutdown.\n    is31fl3736_write_register(index, IS31FL3736_FUNCTION_REG_CONFIGURATION, ((IS31FL3736_PWM_FREQUENCY & 0b111) << 3) | 0x01);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3736_led_t led;\n\n    if (index >= 0 && index < IS31FL3736_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.r] = red;\n        driver_buffers[led.driver].pwm_buffer[led.g] = green;\n        driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3736_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < IS31FL3736_LED_COUNT; i++) {\n        is31fl3736_set_color(i, red, green, blue);\n    }\n}\n\nvoid is31fl3736_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {\n    is31fl3736_led_t led;\n    memcpy_P(&led, (&g_is31fl3736_leds[index]), sizeof(led));\n\n    // The PWM register for a matrix position (0x00 to 0xBF) is interleaved, so:\n    // A1=0x00 A2=0x02 A3=0x04 A4=0x06 A5=0x08 A6=0x0A A7=0x0C A8=0x0E\n    // B1=0x10 B2=0x12 B3=0x14\n    // But also, the LED control registers (0x00 to 0x17) are also interleaved, so:\n    // A1-A4=0x00 A5-A8=0x01\n\n    uint8_t control_register_r = led.r / 8;\n    uint8_t control_register_g = led.g / 8;\n    uint8_t control_register_b = led.b / 8;\n\n    uint8_t bit_r = led.r % 8;\n    uint8_t bit_g = led.g % 8;\n    uint8_t bit_b = led.b % 8;\n\n    if (red) {\n        driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);\n    }\n    if (green) {\n        driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);\n    }\n    if (blue) {\n        driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);\n    }\n\n    driver_buffers[led.driver].led_control_buffer_dirty = true;\n}\n\nvoid is31fl3736_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3736_select_page(index, IS31FL3736_COMMAND_PWM);\n\n        is31fl3736_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3736_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        is31fl3736_select_page(index, IS31FL3736_COMMAND_LED_CONTROL);\n\n        for (uint8_t i = 0; i < IS31FL3736_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3736_write_register(index, i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3736_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3736_DRIVER_COUNT; i++) {\n        is31fl3736_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3736.h",
    "content": "/* Copyright 2018 Jason Williams (Wilba)\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3736_REG_INTERRUPT_MASK 0xF0\n#define IS31FL3736_REG_INTERRUPT_STATUS 0xF1\n\n#define IS31FL3736_REG_COMMAND 0xFD\n\n#define IS31FL3736_COMMAND_LED_CONTROL 0x00\n#define IS31FL3736_COMMAND_PWM 0x01\n#define IS31FL3736_COMMAND_AUTO_BREATH 0x02\n#define IS31FL3736_COMMAND_FUNCTION 0x03\n\n#define IS31FL3736_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3736_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3736_FUNCTION_REG_SW_PULLUP 0x0F\n#define IS31FL3736_FUNCTION_REG_CS_PULLDOWN 0x10\n#define IS31FL3736_FUNCTION_REG_RESET 0x11\n\n#define IS31FL3736_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3736_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3736_I2C_ADDRESS_GND_GND 0x50\n#define IS31FL3736_I2C_ADDRESS_GND_SCL 0x51\n#define IS31FL3736_I2C_ADDRESS_GND_SDA 0x52\n#define IS31FL3736_I2C_ADDRESS_GND_VCC 0x53\n#define IS31FL3736_I2C_ADDRESS_SCL_GND 0x54\n#define IS31FL3736_I2C_ADDRESS_SCL_SCL 0x55\n#define IS31FL3736_I2C_ADDRESS_SCL_SDA 0x56\n#define IS31FL3736_I2C_ADDRESS_SCL_VCC 0x57\n#define IS31FL3736_I2C_ADDRESS_SDA_GND 0x58\n#define IS31FL3736_I2C_ADDRESS_SDA_SCL 0x59\n#define IS31FL3736_I2C_ADDRESS_SDA_SDA 0x5A\n#define IS31FL3736_I2C_ADDRESS_SDA_VCC 0x5B\n#define IS31FL3736_I2C_ADDRESS_VCC_GND 0x5C\n#define IS31FL3736_I2C_ADDRESS_VCC_SCL 0x5D\n#define IS31FL3736_I2C_ADDRESS_VCC_SDA 0x5E\n#define IS31FL3736_I2C_ADDRESS_VCC_VCC 0x5F\n\n#if defined(RGB_MATRIX_IS31FL3736)\n#    define IS31FL3736_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3736_I2C_ADDRESS_4)\n#    define IS31FL3736_DRIVER_COUNT 4\n#elif defined(IS31FL3736_I2C_ADDRESS_3)\n#    define IS31FL3736_DRIVER_COUNT 3\n#elif defined(IS31FL3736_I2C_ADDRESS_2)\n#    define IS31FL3736_DRIVER_COUNT 2\n#elif defined(IS31FL3736_I2C_ADDRESS_1)\n#    define IS31FL3736_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3736_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED is31fl3736_led_t;\n\nextern const is31fl3736_led_t PROGMEM g_is31fl3736_leds[IS31FL3736_LED_COUNT];\n\nvoid is31fl3736_init_drivers(void);\nvoid is31fl3736_init(uint8_t index);\nvoid is31fl3736_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3736_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3736_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid is31fl3736_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3736_set_led_control_register(uint8_t index, bool red, bool green, bool blue);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3736_update_pwm_buffers(uint8_t index);\nvoid is31fl3736_update_led_control_registers(uint8_t index);\n\nvoid is31fl3736_flush(void);\n\n#define IS31FL3736_PDR_0_OHM 0b000   // No pull-down resistor\n#define IS31FL3736_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3736_PDR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3736_PDR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3736_PDR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3736_PDR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3736_PDR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3736_PDR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3736_PUR_0_OHM 0b000   // No pull-up resistor\n#define IS31FL3736_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3736_PUR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3736_PUR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3736_PUR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3736_PUR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3736_PUR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3736_PUR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3736_PWM_FREQUENCY_8K4_HZ 0b000\n#define IS31FL3736_PWM_FREQUENCY_4K2_HZ 0b001\n#define IS31FL3736_PWM_FREQUENCY_26K7_HZ 0b010\n#define IS31FL3736_PWM_FREQUENCY_2K1_HZ 0b011\n#define IS31FL3736_PWM_FREQUENCY_1K05_HZ 0b100\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x02\n#define SW1_CS3 0x04\n#define SW1_CS4 0x06\n#define SW1_CS5 0x08\n#define SW1_CS6 0x0A\n#define SW1_CS7 0x0C\n#define SW1_CS8 0x0E\n\n#define SW2_CS1 0x10\n#define SW2_CS2 0x12\n#define SW2_CS3 0x14\n#define SW2_CS4 0x16\n#define SW2_CS5 0x18\n#define SW2_CS6 0x1A\n#define SW2_CS7 0x1C\n#define SW2_CS8 0x1E\n\n#define SW3_CS1 0x20\n#define SW3_CS2 0x22\n#define SW3_CS3 0x24\n#define SW3_CS4 0x26\n#define SW3_CS5 0x28\n#define SW3_CS6 0x2A\n#define SW3_CS7 0x2C\n#define SW3_CS8 0x2E\n\n#define SW4_CS1 0x30\n#define SW4_CS2 0x32\n#define SW4_CS3 0x34\n#define SW4_CS4 0x36\n#define SW4_CS5 0x38\n#define SW4_CS6 0x3A\n#define SW4_CS7 0x3C\n#define SW4_CS8 0x3E\n\n#define SW5_CS1 0x40\n#define SW5_CS2 0x42\n#define SW5_CS3 0x44\n#define SW5_CS4 0x46\n#define SW5_CS5 0x48\n#define SW5_CS6 0x4A\n#define SW5_CS7 0x4C\n#define SW5_CS8 0x4E\n\n#define SW6_CS1 0x50\n#define SW6_CS2 0x52\n#define SW6_CS3 0x54\n#define SW6_CS4 0x56\n#define SW6_CS5 0x58\n#define SW6_CS6 0x5A\n#define SW6_CS7 0x5C\n#define SW6_CS8 0x5E\n\n#define SW7_CS1 0x60\n#define SW7_CS2 0x62\n#define SW7_CS3 0x64\n#define SW7_CS4 0x66\n#define SW7_CS5 0x68\n#define SW7_CS6 0x6A\n#define SW7_CS7 0x6C\n#define SW7_CS8 0x6E\n\n#define SW8_CS1 0x70\n#define SW8_CS2 0x72\n#define SW8_CS3 0x74\n#define SW8_CS4 0x76\n#define SW8_CS5 0x78\n#define SW8_CS6 0x7A\n#define SW8_CS7 0x7C\n#define SW8_CS8 0x7E\n\n#define SW9_CS1 0x80\n#define SW9_CS2 0x82\n#define SW9_CS3 0x84\n#define SW9_CS4 0x86\n#define SW9_CS5 0x88\n#define SW9_CS6 0x8A\n#define SW9_CS7 0x8C\n#define SW9_CS8 0x8E\n\n#define SW10_CS1 0x90\n#define SW10_CS2 0x92\n#define SW10_CS3 0x94\n#define SW10_CS4 0x96\n#define SW10_CS5 0x98\n#define SW10_CS6 0x9A\n#define SW10_CS7 0x9C\n#define SW10_CS8 0x9E\n\n#define SW11_CS1 0xA0\n#define SW11_CS2 0xA2\n#define SW11_CS3 0xA4\n#define SW11_CS4 0xA6\n#define SW11_CS5 0xA8\n#define SW11_CS6 0xAA\n#define SW11_CS7 0xAC\n#define SW11_CS8 0xAE\n\n#define SW12_CS1 0xB0\n#define SW12_CS2 0xB2\n#define SW12_CS3 0xB4\n#define SW12_CS4 0xB6\n#define SW12_CS5 0xB8\n#define SW12_CS6 0xBA\n#define SW12_CS7 0xBC\n#define SW12_CS8 0xBE\n"
  },
  {
    "path": "drivers/led/issi/is31fl3737-mono.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3737-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3737_PWM_REGISTER_COUNT 192 // actually 144\n#define IS31FL3737_LED_CONTROL_REGISTER_COUNT 24\n\n#ifndef IS31FL3737_I2C_TIMEOUT\n#    define IS31FL3737_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3737_I2C_PERSISTENCE\n#    define IS31FL3737_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3737_PWM_FREQUENCY\n#    define IS31FL3737_PWM_FREQUENCY IS31FL3737_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3737B only\n#endif\n\n#ifndef IS31FL3737_SW_PULLUP\n#    define IS31FL3737_SW_PULLUP IS31FL3737_PUR_0_OHM\n#endif\n\n#ifndef IS31FL3737_CS_PULLDOWN\n#    define IS31FL3737_CS_PULLDOWN IS31FL3737_PDR_0_OHM\n#endif\n\n#ifndef IS31FL3737_GLOBAL_CURRENT\n#    define IS31FL3737_GLOBAL_CURRENT 0xFF\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3737_DRIVER_COUNT] = {\n    IS31FL3737_I2C_ADDRESS_1,\n#ifdef IS31FL3737_I2C_ADDRESS_2\n    IS31FL3737_I2C_ADDRESS_2,\n#    ifdef IS31FL3737_I2C_ADDRESS_3\n    IS31FL3737_I2C_ADDRESS_3,\n#        ifdef IS31FL3737_I2C_ADDRESS_4\n    IS31FL3737_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the IS31FL3737 PWM registers.\n// The control buffers match the page 0 LED On/Off registers.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in is31fl3737_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct is31fl3737_driver_t {\n    uint8_t pwm_buffer[IS31FL3737_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3737_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3737_driver_t;\n\nis31fl3737_driver_t driver_buffers[IS31FL3737_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3737_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3737_select_page(uint8_t index, uint8_t page) {\n    is31fl3737_write_register(index, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3737_write_register(index, IS31FL3737_REG_COMMAND, page);\n}\n\nvoid is31fl3737_write_pwm_buffer(uint8_t index) {\n    // Assumes page 1 is already selected.\n    // Transmit PWM registers in 12 transfers of 16 bytes.\n\n    // Iterate over the pwm_buffer contents at 16 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i += 16) {\n#if IS31FL3737_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3737_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3737_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3737_SDB_PIN)\n    gpio_set_pin_output(IS31FL3737_SDB_PIN);\n    gpio_write_pin_high(IS31FL3737_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {\n        is31fl3737_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3737_LED_COUNT; i++) {\n        is31fl3737_set_led_control_register(i, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {\n        is31fl3737_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3737_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3737_write_register(index, i, 0x00);\n    }\n\n    is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM);\n\n    // Set PWM on all LEDs to 0\n    // No need to setup Breath registers to PWM as that is the default.\n    for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i++) {\n        is31fl3737_write_register(index, i, 0x00);\n    }\n\n    is31fl3737_select_page(index, IS31FL3737_COMMAND_FUNCTION);\n\n    // Set de-ghost pull-up resistors (SWx)\n    is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_SW_PULLUP, IS31FL3737_SW_PULLUP);\n    // Set de-ghost pull-down resistors (CSx)\n    is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CS_PULLDOWN, IS31FL3737_CS_PULLDOWN);\n    // Set global current to maximum.\n    is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3737_GLOBAL_CURRENT);\n    // Disable software shutdown.\n    is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3737_set_value(int index, uint8_t value) {\n    is31fl3737_led_t led;\n\n    if (index >= 0 && index < IS31FL3737_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.v] = value;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3737_set_value_all(uint8_t value) {\n    for (int i = 0; i < IS31FL3737_LED_COUNT; i++) {\n        is31fl3737_set_value(i, value);\n    }\n}\n\nvoid is31fl3737_set_led_control_register(uint8_t index, bool value) {\n    is31fl3737_led_t led;\n    memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led));\n\n    uint8_t control_register = led.v / 8;\n    uint8_t bit_value        = led.v % 8;\n\n    if (value) {\n        driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value);\n    }\n\n    driver_buffers[led.driver].led_control_buffer_dirty = true;\n}\n\nvoid is31fl3737_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM);\n\n        is31fl3737_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3737_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL);\n\n        for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3737_write_register(index, i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3737_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {\n        is31fl3737_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3737-mono.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3737_REG_INTERRUPT_MASK 0xF0\n#define IS31FL3737_REG_INTERRUPT_STATUS 0xF1\n\n#define IS31FL3737_REG_COMMAND 0xFD\n\n#define IS31FL3737_COMMAND_LED_CONTROL 0x00\n#define IS31FL3737_COMMAND_PWM 0x01\n#define IS31FL3737_COMMAND_AUTO_BREATH 0x02\n#define IS31FL3737_COMMAND_FUNCTION 0x03\n\n#define IS31FL3737_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3737_FUNCTION_REG_SW_PULLUP 0x0F\n#define IS31FL3737_FUNCTION_REG_CS_PULLDOWN 0x10\n#define IS31FL3737_FUNCTION_REG_RESET 0x11\n\n#define IS31FL3737_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3737_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3737_I2C_ADDRESS_GND 0x50\n#define IS31FL3737_I2C_ADDRESS_SCL 0x55\n#define IS31FL3737_I2C_ADDRESS_SDA 0x5A\n#define IS31FL3737_I2C_ADDRESS_VCC 0x5F\n\n#if defined(LED_MATRIX_IS31FL3737)\n#    define IS31FL3737_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3737_I2C_ADDRESS_4)\n#    define IS31FL3737_DRIVER_COUNT 4\n#elif defined(IS31FL3737_I2C_ADDRESS_3)\n#    define IS31FL3737_DRIVER_COUNT 3\n#elif defined(IS31FL3737_I2C_ADDRESS_2)\n#    define IS31FL3737_DRIVER_COUNT 2\n#elif defined(IS31FL3737_I2C_ADDRESS_1)\n#    define IS31FL3737_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3737_led_t {\n    uint8_t driver : 2;\n    uint8_t v;\n} PACKED is31fl3737_led_t;\n\nextern const is31fl3737_led_t PROGMEM g_is31fl3737_leds[IS31FL3737_LED_COUNT];\n\nvoid is31fl3737_init_drivers(void);\nvoid is31fl3737_init(uint8_t index);\nvoid is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3737_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3737_set_value(int index, uint8_t value);\nvoid is31fl3737_set_value_all(uint8_t value);\n\nvoid is31fl3737_set_led_control_register(uint8_t index, bool value);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3737_update_pwm_buffers(uint8_t index);\nvoid is31fl3737_update_led_control_registers(uint8_t index);\n\nvoid is31fl3737_flush(void);\n\n#define IS31FL3737_PDR_0_OHM 0b000   // No pull-down resistor\n#define IS31FL3737_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3737_PDR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3737_PDR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3737_PDR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3737_PDR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3737_PDR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3737_PDR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3737_PUR_0_OHM 0b000   // No pull-up resistor\n#define IS31FL3737_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3737_PUR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3737_PUR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3737_PUR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3737_PUR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3737_PUR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3737_PUR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3737_PWM_FREQUENCY_8K4_HZ 0b000\n#define IS31FL3737_PWM_FREQUENCY_4K2_HZ 0b001\n#define IS31FL3737_PWM_FREQUENCY_26K7_HZ 0b010\n#define IS31FL3737_PWM_FREQUENCY_2K1_HZ 0b011\n#define IS31FL3737_PWM_FREQUENCY_1K05_HZ 0b100\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x08\n#define SW1_CS8 0x09\n#define SW1_CS9 0x0A\n#define SW1_CS10 0x0B\n#define SW1_CS11 0x0C\n#define SW1_CS12 0x0D\n\n#define SW2_CS1 0x10\n#define SW2_CS2 0x11\n#define SW2_CS3 0x12\n#define SW2_CS4 0x13\n#define SW2_CS5 0x14\n#define SW2_CS6 0x15\n#define SW2_CS7 0x18\n#define SW2_CS8 0x19\n#define SW2_CS9 0x1A\n#define SW2_CS10 0x1B\n#define SW2_CS11 0x1C\n#define SW2_CS12 0x1D\n\n#define SW3_CS1 0x20\n#define SW3_CS2 0x21\n#define SW3_CS3 0x22\n#define SW3_CS4 0x23\n#define SW3_CS5 0x24\n#define SW3_CS6 0x25\n#define SW3_CS7 0x28\n#define SW3_CS8 0x29\n#define SW3_CS9 0x2A\n#define SW3_CS10 0x2B\n#define SW3_CS11 0x2C\n#define SW3_CS12 0x2D\n\n#define SW4_CS1 0x30\n#define SW4_CS2 0x31\n#define SW4_CS3 0x32\n#define SW4_CS4 0x33\n#define SW4_CS5 0x34\n#define SW4_CS6 0x35\n#define SW4_CS7 0x38\n#define SW4_CS8 0x39\n#define SW4_CS9 0x3A\n#define SW4_CS10 0x3B\n#define SW4_CS11 0x3C\n#define SW4_CS12 0x3D\n\n#define SW5_CS1 0x40\n#define SW5_CS2 0x41\n#define SW5_CS3 0x42\n#define SW5_CS4 0x43\n#define SW5_CS5 0x44\n#define SW5_CS6 0x45\n#define SW5_CS7 0x48\n#define SW5_CS8 0x49\n#define SW5_CS9 0x4A\n#define SW5_CS10 0x4B\n#define SW5_CS11 0x4C\n#define SW5_CS12 0x4D\n\n#define SW6_CS1 0x50\n#define SW6_CS2 0x51\n#define SW6_CS3 0x52\n#define SW6_CS4 0x53\n#define SW6_CS5 0x54\n#define SW6_CS6 0x55\n#define SW6_CS7 0x58\n#define SW6_CS8 0x59\n#define SW6_CS9 0x5A\n#define SW6_CS10 0x5B\n#define SW6_CS11 0x5C\n#define SW6_CS12 0x5D\n\n#define SW7_CS1 0x60\n#define SW7_CS2 0x61\n#define SW7_CS3 0x62\n#define SW7_CS4 0x63\n#define SW7_CS5 0x64\n#define SW7_CS6 0x65\n#define SW7_CS7 0x68\n#define SW7_CS8 0x69\n#define SW7_CS9 0x6A\n#define SW7_CS10 0x6B\n#define SW7_CS11 0x6C\n#define SW7_CS12 0x6D\n\n#define SW8_CS1 0x70\n#define SW8_CS2 0x71\n#define SW8_CS3 0x72\n#define SW8_CS4 0x73\n#define SW8_CS5 0x74\n#define SW8_CS6 0x75\n#define SW8_CS7 0x78\n#define SW8_CS8 0x79\n#define SW8_CS9 0x7A\n#define SW8_CS10 0x7B\n#define SW8_CS11 0x7C\n#define SW8_CS12 0x7D\n\n#define SW9_CS1 0x80\n#define SW9_CS2 0x81\n#define SW9_CS3 0x82\n#define SW9_CS4 0x83\n#define SW9_CS5 0x84\n#define SW9_CS6 0x85\n#define SW9_CS7 0x88\n#define SW9_CS8 0x89\n#define SW9_CS9 0x8A\n#define SW9_CS10 0x8B\n#define SW9_CS11 0x8C\n#define SW9_CS12 0x8D\n\n#define SW10_CS1 0x90\n#define SW10_CS2 0x91\n#define SW10_CS3 0x92\n#define SW10_CS4 0x93\n#define SW10_CS5 0x94\n#define SW10_CS6 0x95\n#define SW10_CS7 0x98\n#define SW10_CS8 0x99\n#define SW10_CS9 0x9A\n#define SW10_CS10 0x9B\n#define SW10_CS11 0x9C\n#define SW10_CS12 0x9D\n\n#define SW11_CS1 0xA0\n#define SW11_CS2 0xA1\n#define SW11_CS3 0xA2\n#define SW11_CS4 0xA3\n#define SW11_CS5 0xA4\n#define SW11_CS6 0xA5\n#define SW11_CS7 0xA8\n#define SW11_CS8 0xA9\n#define SW11_CS9 0xAA\n#define SW11_CS10 0xAB\n#define SW11_CS11 0xAC\n#define SW11_CS12 0xAD\n\n#define SW12_CS1 0xB0\n#define SW12_CS2 0xB1\n#define SW12_CS3 0xB2\n#define SW12_CS4 0xB3\n#define SW12_CS5 0xB4\n#define SW12_CS6 0xB5\n#define SW12_CS7 0xB8\n#define SW12_CS8 0xB9\n#define SW12_CS9 0xBA\n#define SW12_CS10 0xBB\n#define SW12_CS11 0xBC\n#define SW12_CS12 0xBD\n"
  },
  {
    "path": "drivers/led/issi/is31fl3737.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3737.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3737_PWM_REGISTER_COUNT 192 // actually 144\n#define IS31FL3737_LED_CONTROL_REGISTER_COUNT 24\n\n#ifndef IS31FL3737_I2C_TIMEOUT\n#    define IS31FL3737_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3737_I2C_PERSISTENCE\n#    define IS31FL3737_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3737_PWM_FREQUENCY\n#    define IS31FL3737_PWM_FREQUENCY IS31FL3737_PWM_FREQUENCY_8K4_HZ // PFS - IS31FL3737B only\n#endif\n\n#ifndef IS31FL3737_SW_PULLUP\n#    define IS31FL3737_SW_PULLUP IS31FL3737_PUR_0_OHM\n#endif\n\n#ifndef IS31FL3737_CS_PULLDOWN\n#    define IS31FL3737_CS_PULLDOWN IS31FL3737_PDR_0_OHM\n#endif\n\n#ifndef IS31FL3737_GLOBAL_CURRENT\n#    define IS31FL3737_GLOBAL_CURRENT 0xFF\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3737_DRIVER_COUNT] = {\n    IS31FL3737_I2C_ADDRESS_1,\n#ifdef IS31FL3737_I2C_ADDRESS_2\n    IS31FL3737_I2C_ADDRESS_2,\n#    ifdef IS31FL3737_I2C_ADDRESS_3\n    IS31FL3737_I2C_ADDRESS_3,\n#        ifdef IS31FL3737_I2C_ADDRESS_4\n    IS31FL3737_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the IS31FL3737 PWM registers.\n// The control buffers match the page 0 LED On/Off registers.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in is31fl3737_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct is31fl3737_driver_t {\n    uint8_t pwm_buffer[IS31FL3737_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[IS31FL3737_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED is31fl3737_driver_t;\n\nis31fl3737_driver_t driver_buffers[IS31FL3737_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3737_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3737_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3737_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3737_select_page(uint8_t index, uint8_t page) {\n    is31fl3737_write_register(index, IS31FL3737_REG_COMMAND_WRITE_LOCK, IS31FL3737_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3737_write_register(index, IS31FL3737_REG_COMMAND, page);\n}\n\nvoid is31fl3737_write_pwm_buffer(uint8_t index) {\n    // Assumes page 1 is already selected.\n    // Transmit PWM registers in 12 transfers of 16 bytes.\n\n    // Iterate over the pwm_buffer contents at 16 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i += 16) {\n#if IS31FL3737_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3737_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, IS31FL3737_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3737_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3737_SDB_PIN)\n    gpio_set_pin_output(IS31FL3737_SDB_PIN);\n    gpio_write_pin_high(IS31FL3737_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {\n        is31fl3737_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3737_LED_COUNT; i++) {\n        is31fl3737_set_led_control_register(i, true, true, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {\n        is31fl3737_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3737_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {\n        is31fl3737_write_register(index, i, 0x00);\n    }\n\n    is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM);\n\n    // Set PWM on all LEDs to 0\n    // No need to setup Breath registers to PWM as that is the default.\n    for (uint8_t i = 0; i < IS31FL3737_PWM_REGISTER_COUNT; i++) {\n        is31fl3737_write_register(index, i, 0x00);\n    }\n\n    is31fl3737_select_page(index, IS31FL3737_COMMAND_FUNCTION);\n\n    // Set de-ghost pull-up resistors (SWx)\n    is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_SW_PULLUP, IS31FL3737_SW_PULLUP);\n    // Set de-ghost pull-down resistors (CSx)\n    is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CS_PULLDOWN, IS31FL3737_CS_PULLDOWN);\n    // Set global current to maximum.\n    is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3737_GLOBAL_CURRENT);\n    // Disable software shutdown.\n    is31fl3737_write_register(index, IS31FL3737_FUNCTION_REG_CONFIGURATION, ((IS31FL3737_PWM_FREQUENCY & 0b111) << 3) | 0x01);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3737_led_t led;\n\n    if (index >= 0 && index < IS31FL3737_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.r] = red;\n        driver_buffers[led.driver].pwm_buffer[led.g] = green;\n        driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < IS31FL3737_LED_COUNT; i++) {\n        is31fl3737_set_color(i, red, green, blue);\n    }\n}\n\nvoid is31fl3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {\n    is31fl3737_led_t led;\n    memcpy_P(&led, (&g_is31fl3737_leds[index]), sizeof(led));\n\n    uint8_t control_register_r = led.r / 8;\n    uint8_t control_register_g = led.g / 8;\n    uint8_t control_register_b = led.b / 8;\n    uint8_t bit_r              = led.r % 8;\n    uint8_t bit_g              = led.g % 8;\n    uint8_t bit_b              = led.b % 8;\n\n    if (red) {\n        driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);\n    }\n    if (green) {\n        driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);\n    }\n    if (blue) {\n        driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);\n    }\n\n    driver_buffers[led.driver].led_control_buffer_dirty = true;\n}\n\nvoid is31fl3737_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3737_select_page(index, IS31FL3737_COMMAND_PWM);\n\n        is31fl3737_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3737_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        is31fl3737_select_page(index, IS31FL3737_COMMAND_LED_CONTROL);\n\n        for (uint8_t i = 0; i < IS31FL3737_LED_CONTROL_REGISTER_COUNT; i++) {\n            is31fl3737_write_register(index, i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3737_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3737_DRIVER_COUNT; i++) {\n        is31fl3737_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3737.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2021 Doni Crosby\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3737_REG_INTERRUPT_MASK 0xF0\n#define IS31FL3737_REG_INTERRUPT_STATUS 0xF1\n\n#define IS31FL3737_REG_COMMAND 0xFD\n\n#define IS31FL3737_COMMAND_LED_CONTROL 0x00\n#define IS31FL3737_COMMAND_PWM 0x01\n#define IS31FL3737_COMMAND_AUTO_BREATH 0x02\n#define IS31FL3737_COMMAND_FUNCTION 0x03\n\n#define IS31FL3737_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3737_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3737_FUNCTION_REG_SW_PULLUP 0x0F\n#define IS31FL3737_FUNCTION_REG_CS_PULLDOWN 0x10\n#define IS31FL3737_FUNCTION_REG_RESET 0x11\n\n#define IS31FL3737_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3737_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3737_I2C_ADDRESS_GND 0x50\n#define IS31FL3737_I2C_ADDRESS_SCL 0x55\n#define IS31FL3737_I2C_ADDRESS_SDA 0x5A\n#define IS31FL3737_I2C_ADDRESS_VCC 0x5F\n\n#if defined(RGB_MATRIX_IS31FL3737)\n#    define IS31FL3737_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3737_I2C_ADDRESS_4)\n#    define IS31FL3737_DRIVER_COUNT 4\n#elif defined(IS31FL3737_I2C_ADDRESS_3)\n#    define IS31FL3737_DRIVER_COUNT 3\n#elif defined(IS31FL3737_I2C_ADDRESS_2)\n#    define IS31FL3737_DRIVER_COUNT 2\n#elif defined(IS31FL3737_I2C_ADDRESS_1)\n#    define IS31FL3737_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3737_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED is31fl3737_led_t;\n\nextern const is31fl3737_led_t PROGMEM g_is31fl3737_leds[IS31FL3737_LED_COUNT];\n\nvoid is31fl3737_init_drivers(void);\nvoid is31fl3737_init(uint8_t index);\nvoid is31fl3737_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3737_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3737_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid is31fl3737_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3737_set_led_control_register(uint8_t index, bool red, bool green, bool blue);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3737_update_pwm_buffers(uint8_t index);\nvoid is31fl3737_update_led_control_registers(uint8_t index);\n\nvoid is31fl3737_flush(void);\n\n#define IS31FL3737_PDR_0_OHM 0b000   // No pull-down resistor\n#define IS31FL3737_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3737_PDR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3737_PDR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3737_PDR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3737_PDR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3737_PDR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3737_PDR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3737_PUR_0_OHM 0b000   // No pull-up resistor\n#define IS31FL3737_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3737_PUR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3737_PUR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3737_PUR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3737_PUR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3737_PUR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3737_PUR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3737_PWM_FREQUENCY_8K4_HZ 0b000\n#define IS31FL3737_PWM_FREQUENCY_4K2_HZ 0b001\n#define IS31FL3737_PWM_FREQUENCY_26K7_HZ 0b010\n#define IS31FL3737_PWM_FREQUENCY_2K1_HZ 0b011\n#define IS31FL3737_PWM_FREQUENCY_1K05_HZ 0b100\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x08\n#define SW1_CS8 0x09\n#define SW1_CS9 0x0A\n#define SW1_CS10 0x0B\n#define SW1_CS11 0x0C\n#define SW1_CS12 0x0D\n\n#define SW2_CS1 0x10\n#define SW2_CS2 0x11\n#define SW2_CS3 0x12\n#define SW2_CS4 0x13\n#define SW2_CS5 0x14\n#define SW2_CS6 0x15\n#define SW2_CS7 0x18\n#define SW2_CS8 0x19\n#define SW2_CS9 0x1A\n#define SW2_CS10 0x1B\n#define SW2_CS11 0x1C\n#define SW2_CS12 0x1D\n\n#define SW3_CS1 0x20\n#define SW3_CS2 0x21\n#define SW3_CS3 0x22\n#define SW3_CS4 0x23\n#define SW3_CS5 0x24\n#define SW3_CS6 0x25\n#define SW3_CS7 0x28\n#define SW3_CS8 0x29\n#define SW3_CS9 0x2A\n#define SW3_CS10 0x2B\n#define SW3_CS11 0x2C\n#define SW3_CS12 0x2D\n\n#define SW4_CS1 0x30\n#define SW4_CS2 0x31\n#define SW4_CS3 0x32\n#define SW4_CS4 0x33\n#define SW4_CS5 0x34\n#define SW4_CS6 0x35\n#define SW4_CS7 0x38\n#define SW4_CS8 0x39\n#define SW4_CS9 0x3A\n#define SW4_CS10 0x3B\n#define SW4_CS11 0x3C\n#define SW4_CS12 0x3D\n\n#define SW5_CS1 0x40\n#define SW5_CS2 0x41\n#define SW5_CS3 0x42\n#define SW5_CS4 0x43\n#define SW5_CS5 0x44\n#define SW5_CS6 0x45\n#define SW5_CS7 0x48\n#define SW5_CS8 0x49\n#define SW5_CS9 0x4A\n#define SW5_CS10 0x4B\n#define SW5_CS11 0x4C\n#define SW5_CS12 0x4D\n\n#define SW6_CS1 0x50\n#define SW6_CS2 0x51\n#define SW6_CS3 0x52\n#define SW6_CS4 0x53\n#define SW6_CS5 0x54\n#define SW6_CS6 0x55\n#define SW6_CS7 0x58\n#define SW6_CS8 0x59\n#define SW6_CS9 0x5A\n#define SW6_CS10 0x5B\n#define SW6_CS11 0x5C\n#define SW6_CS12 0x5D\n\n#define SW7_CS1 0x60\n#define SW7_CS2 0x61\n#define SW7_CS3 0x62\n#define SW7_CS4 0x63\n#define SW7_CS5 0x64\n#define SW7_CS6 0x65\n#define SW7_CS7 0x68\n#define SW7_CS8 0x69\n#define SW7_CS9 0x6A\n#define SW7_CS10 0x6B\n#define SW7_CS11 0x6C\n#define SW7_CS12 0x6D\n\n#define SW8_CS1 0x70\n#define SW8_CS2 0x71\n#define SW8_CS3 0x72\n#define SW8_CS4 0x73\n#define SW8_CS5 0x74\n#define SW8_CS6 0x75\n#define SW8_CS7 0x78\n#define SW8_CS8 0x79\n#define SW8_CS9 0x7A\n#define SW8_CS10 0x7B\n#define SW8_CS11 0x7C\n#define SW8_CS12 0x7D\n\n#define SW9_CS1 0x80\n#define SW9_CS2 0x81\n#define SW9_CS3 0x82\n#define SW9_CS4 0x83\n#define SW9_CS5 0x84\n#define SW9_CS6 0x85\n#define SW9_CS7 0x88\n#define SW9_CS8 0x89\n#define SW9_CS9 0x8A\n#define SW9_CS10 0x8B\n#define SW9_CS11 0x8C\n#define SW9_CS12 0x8D\n\n#define SW10_CS1 0x90\n#define SW10_CS2 0x91\n#define SW10_CS3 0x92\n#define SW10_CS4 0x93\n#define SW10_CS5 0x94\n#define SW10_CS6 0x95\n#define SW10_CS7 0x98\n#define SW10_CS8 0x99\n#define SW10_CS9 0x9A\n#define SW10_CS10 0x9B\n#define SW10_CS11 0x9C\n#define SW10_CS12 0x9D\n\n#define SW11_CS1 0xA0\n#define SW11_CS2 0xA1\n#define SW11_CS3 0xA2\n#define SW11_CS4 0xA3\n#define SW11_CS5 0xA4\n#define SW11_CS6 0xA5\n#define SW11_CS7 0xA8\n#define SW11_CS8 0xA9\n#define SW11_CS9 0xAA\n#define SW11_CS10 0xAB\n#define SW11_CS11 0xAC\n#define SW11_CS12 0xAD\n\n#define SW12_CS1 0xB0\n#define SW12_CS2 0xB1\n#define SW12_CS3 0xB2\n#define SW12_CS4 0xB3\n#define SW12_CS5 0xB4\n#define SW12_CS6 0xB5\n#define SW12_CS7 0xB8\n#define SW12_CS8 0xB9\n#define SW12_CS9 0xBA\n#define SW12_CS10 0xBB\n#define SW12_CS11 0xBC\n#define SW12_CS12 0xBD\n"
  },
  {
    "path": "drivers/led/issi/is31fl3741-mono.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3741-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3741_PWM_0_REGISTER_COUNT 180\n#define IS31FL3741_PWM_1_REGISTER_COUNT 171\n#define IS31FL3741_SCALING_0_REGISTER_COUNT 180\n#define IS31FL3741_SCALING_1_REGISTER_COUNT 171\n\n#ifndef IS31FL3741_I2C_TIMEOUT\n#    define IS31FL3741_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3741_I2C_PERSISTENCE\n#    define IS31FL3741_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3741_CONFIGURATION\n#    define IS31FL3741_CONFIGURATION 0x01\n#endif\n\n#ifndef IS31FL3741_PWM_FREQUENCY\n#    define IS31FL3741_PWM_FREQUENCY IS31FL3741_PWM_FREQUENCY_29K_HZ\n#endif\n\n#ifndef IS31FL3741_SW_PULLUP\n#    define IS31FL3741_SW_PULLUP IS31FL3741_PUR_32K_OHM\n#endif\n\n#ifndef IS31FL3741_CS_PULLDOWN\n#    define IS31FL3741_CS_PULLDOWN IS31FL3741_PDR_32K_OHM\n#endif\n\n#ifndef IS31FL3741_GLOBAL_CURRENT\n#    define IS31FL3741_GLOBAL_CURRENT 0xFF\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3741_DRIVER_COUNT] = {\n    IS31FL3741_I2C_ADDRESS_1,\n#ifdef IS31FL3741_I2C_ADDRESS_2\n    IS31FL3741_I2C_ADDRESS_2,\n#    ifdef IS31FL3741_I2C_ADDRESS_3\n    IS31FL3741_I2C_ADDRESS_3,\n#        ifdef IS31FL3741_I2C_ADDRESS_4\n    IS31FL3741_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the IS31FL3741 and IS31FL3741A PWM registers.\n// The scaling buffers match the page 2 and 3 LED On/Off registers.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in is31fl3741_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct is31fl3741_driver_t {\n    uint8_t pwm_buffer_0[IS31FL3741_PWM_0_REGISTER_COUNT];\n    uint8_t pwm_buffer_1[IS31FL3741_PWM_1_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer_0[IS31FL3741_SCALING_0_REGISTER_COUNT];\n    uint8_t scaling_buffer_1[IS31FL3741_SCALING_1_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3741_driver_t;\n\nis31fl3741_driver_t driver_buffers[IS31FL3741_DRIVER_COUNT] = {{\n    .pwm_buffer_0         = {0},\n    .pwm_buffer_1         = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer_0     = {0},\n    .scaling_buffer_1     = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3741_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3741_select_page(uint8_t index, uint8_t page) {\n    is31fl3741_write_register(index, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3741_write_register(index, IS31FL3741_REG_COMMAND, page);\n}\n\nvoid is31fl3741_write_pwm_buffer(uint8_t index) {\n    is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_0);\n\n    // Transmit PWM0 registers in 6 transfers of 30 bytes.\n\n    // Iterate over the pwm_buffer_0 contents at 30 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3741_PWM_0_REGISTER_COUNT; i += 30) {\n#if IS31FL3741_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3741_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT);\n#endif\n    }\n\n    is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_1);\n\n    // Transmit PWM1 registers in 9 transfers of 19 bytes.\n\n    // Iterate over the pwm_buffer_1 contents at 19 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3741_PWM_1_REGISTER_COUNT; i += 19) {\n#if IS31FL3741_I2C_PERSISTENCE > 0\n        for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3741_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3741_SDB_PIN)\n    gpio_set_pin_output(IS31FL3741_SDB_PIN);\n    gpio_write_pin_high(IS31FL3741_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {\n        is31fl3741_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3741_LED_COUNT; i++) {\n        is31fl3741_set_led_control_register(i, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {\n        is31fl3741_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3741_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n    // Unlock the command register.\n\n    is31fl3741_select_page(index, IS31FL3741_COMMAND_FUNCTION);\n\n    // Set to Normal operation\n    is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_CONFIGURATION, IS31FL3741_CONFIGURATION);\n\n    // Set Golbal Current Control Register\n    is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3741_GLOBAL_CURRENT);\n    // Set Pull up & Down for SWx CSy\n    is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PULLDOWNUP, ((IS31FL3741_CS_PULLDOWN << 4) | IS31FL3741_SW_PULLUP));\n    // Set PWM frequency\n    is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111));\n\n    // is31fl3741_update_led_scaling_registers(index, 0xFF, 0xFF, 0xFF);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nuint8_t get_pwm_value(uint8_t driver, uint16_t reg) {\n    if (reg & 0x100) {\n        return driver_buffers[driver].pwm_buffer_1[reg & 0xFF];\n    } else {\n        return driver_buffers[driver].pwm_buffer_0[reg];\n    }\n}\n\nvoid set_pwm_value(uint8_t driver, uint16_t reg, uint8_t value) {\n    if (reg & 0x100) {\n        driver_buffers[driver].pwm_buffer_1[reg & 0xFF] = value;\n    } else {\n        driver_buffers[driver].pwm_buffer_0[reg] = value;\n    }\n}\n\nvoid is31fl3741_set_value(int index, uint8_t value) {\n    is31fl3741_led_t led;\n\n    if (index >= 0 && index < IS31FL3741_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));\n\n        if (get_pwm_value(led.driver, led.v) == value) {\n            return;\n        }\n\n        set_pwm_value(led.driver, led.v, value);\n        driver_buffers[led.driver].pwm_buffer_dirty = true;\n    }\n}\n\nvoid is31fl3741_set_value_all(uint8_t value) {\n    for (int i = 0; i < IS31FL3741_LED_COUNT; i++) {\n        is31fl3741_set_value(i, value);\n    }\n}\n\nvoid set_scaling_value(uint8_t driver, uint16_t reg, uint8_t value) {\n    if (reg & 0x100) {\n        driver_buffers[driver].scaling_buffer_1[reg & 0xFF] = value;\n    } else {\n        driver_buffers[driver].scaling_buffer_0[reg] = value;\n    }\n}\n\nvoid is31fl3741_set_led_control_register(uint8_t index, bool value) {\n    is31fl3741_led_t led;\n    memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));\n\n    set_scaling_value(led.driver, led.v, value ? 0xFF : 0x00);\n\n    driver_buffers[led.driver].scaling_buffer_dirty = true;\n}\n\nvoid is31fl3741_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3741_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t value) {\n    set_pwm_value(pled->driver, pled->v, value);\n    driver_buffers[pled->driver].pwm_buffer_dirty = true;\n}\n\nvoid is31fl3741_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_0);\n\n        for (uint8_t i = 0; i < IS31FL3741_SCALING_0_REGISTER_COUNT; i++) {\n            is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_0[i]);\n        }\n\n        is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_1);\n\n        for (uint8_t i = 0; i < IS31FL3741_SCALING_1_REGISTER_COUNT; i++) {\n            is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_1[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t value) {\n    set_scaling_value(pled->driver, pled->v, value);\n    driver_buffers[pled->driver].scaling_buffer_dirty = true;\n}\n\nvoid is31fl3741_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {\n        is31fl3741_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3741-mono.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3741_REG_INTERRUPT_MASK 0xF0\n#define IS31FL3741_REG_INTERRUPT_STATUS 0xF1\n#define IS31FL3741_REG_ID 0xFC\n\n#define IS31FL3741_REG_COMMAND 0xFD\n\n#define IS31FL3741_COMMAND_PWM_0 0x00\n#define IS31FL3741_COMMAND_PWM_1 0x01\n#define IS31FL3741_COMMAND_SCALING_0 0x02\n#define IS31FL3741_COMMAND_SCALING_1 0x03\n#define IS31FL3741_COMMAND_FUNCTION 0x04\n\n#define IS31FL3741_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3741_FUNCTION_REG_PULLDOWNUP 0x02\n#define IS31FL3741_FUNCTION_REG_PWM_FREQUENCY 0x36\n#define IS31FL3741_FUNCTION_REG_RESET 0x3F\n\n#define IS31FL3741_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3741_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3741_I2C_ADDRESS_GND 0x30\n#define IS31FL3741_I2C_ADDRESS_SCL 0x31\n#define IS31FL3741_I2C_ADDRESS_SDA 0x32\n#define IS31FL3741_I2C_ADDRESS_VCC 0x33\n\n#if defined(LED_MATRIX_IS31FL3741)\n#    define IS31FL3741_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3741_I2C_ADDRESS_4)\n#    define IS31FL3741_DRIVER_COUNT 4\n#elif defined(IS31FL3741_I2C_ADDRESS_3)\n#    define IS31FL3741_DRIVER_COUNT 3\n#elif defined(IS31FL3741_I2C_ADDRESS_2)\n#    define IS31FL3741_DRIVER_COUNT 2\n#elif defined(IS31FL3741_I2C_ADDRESS_1)\n#    define IS31FL3741_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3741_led_t {\n    uint8_t  driver : 2;\n    uint16_t v : 9;\n} PACKED is31fl3741_led_t;\n\nextern const is31fl3741_led_t PROGMEM g_is31fl3741_leds[IS31FL3741_LED_COUNT];\n\nvoid is31fl3741_init_drivers(void);\nvoid is31fl3741_init(uint8_t index);\nvoid is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3741_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3741_set_value(int index, uint8_t value);\nvoid is31fl3741_set_value_all(uint8_t value);\n\nvoid is31fl3741_set_led_control_register(uint8_t index, bool value);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3741_update_pwm_buffers(uint8_t index);\nvoid is31fl3741_update_led_control_registers(uint8_t index);\nvoid is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t value);\n\nvoid is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t value);\n\nvoid is31fl3741_flush(void);\n\n#define IS31FL3741_PDR_0_OHM 0b000   // No pull-down resistor\n#define IS31FL3741_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3741_PDR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3741_PDR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3741_PDR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3741_PDR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3741_PDR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3741_PDR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3741_PUR_0_OHM 0b000   // No pull-up resistor\n#define IS31FL3741_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3741_PUR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3741_PUR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3741_PUR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3741_PUR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3741_PUR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3741_PUR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3741_PWM_FREQUENCY_29K_HZ 0b0000\n#define IS31FL3741_PWM_FREQUENCY_3K6_HZ 0b0011\n#define IS31FL3741_PWM_FREQUENCY_1K8_HZ 0b0111\n#define IS31FL3741_PWM_FREQUENCY_900_HZ 0b1011\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n#define SW1_CS17 0x10\n#define SW1_CS18 0x11\n#define SW1_CS19 0x12\n#define SW1_CS20 0x13\n#define SW1_CS21 0x14\n#define SW1_CS22 0x15\n#define SW1_CS23 0x16\n#define SW1_CS24 0x17\n#define SW1_CS25 0x18\n#define SW1_CS26 0x19\n#define SW1_CS27 0x1A\n#define SW1_CS28 0x1B\n#define SW1_CS29 0x1C\n#define SW1_CS30 0x1D\n\n#define SW2_CS1 0x1E\n#define SW2_CS2 0x1F\n#define SW2_CS3 0x20\n#define SW2_CS4 0x21\n#define SW2_CS5 0x22\n#define SW2_CS6 0x23\n#define SW2_CS7 0x24\n#define SW2_CS8 0x25\n#define SW2_CS9 0x26\n#define SW2_CS10 0x27\n#define SW2_CS11 0x28\n#define SW2_CS12 0x29\n#define SW2_CS13 0x2A\n#define SW2_CS14 0x2B\n#define SW2_CS15 0x2C\n#define SW2_CS16 0x2D\n#define SW2_CS17 0x2E\n#define SW2_CS18 0x2F\n#define SW2_CS19 0x30\n#define SW2_CS20 0x31\n#define SW2_CS21 0x32\n#define SW2_CS22 0x33\n#define SW2_CS23 0x34\n#define SW2_CS24 0x35\n#define SW2_CS25 0x36\n#define SW2_CS26 0x37\n#define SW2_CS27 0x38\n#define SW2_CS28 0x39\n#define SW2_CS29 0x3A\n#define SW2_CS30 0x3B\n\n#define SW3_CS1 0x3C\n#define SW3_CS2 0x3D\n#define SW3_CS3 0x3E\n#define SW3_CS4 0x3F\n#define SW3_CS5 0x40\n#define SW3_CS6 0x41\n#define SW3_CS7 0x42\n#define SW3_CS8 0x43\n#define SW3_CS9 0x44\n#define SW3_CS10 0x45\n#define SW3_CS11 0x46\n#define SW3_CS12 0x47\n#define SW3_CS13 0x48\n#define SW3_CS14 0x49\n#define SW3_CS15 0x4A\n#define SW3_CS16 0x4B\n#define SW3_CS17 0x4C\n#define SW3_CS18 0x4D\n#define SW3_CS19 0x4E\n#define SW3_CS20 0x4F\n#define SW3_CS21 0x50\n#define SW3_CS22 0x51\n#define SW3_CS23 0x52\n#define SW3_CS24 0x53\n#define SW3_CS25 0x54\n#define SW3_CS26 0x55\n#define SW3_CS27 0x56\n#define SW3_CS28 0x57\n#define SW3_CS29 0x58\n#define SW3_CS30 0x59\n\n#define SW4_CS1 0x5A\n#define SW4_CS2 0x5B\n#define SW4_CS3 0x5C\n#define SW4_CS4 0x5D\n#define SW4_CS5 0x5E\n#define SW4_CS6 0x5F\n#define SW4_CS7 0x60\n#define SW4_CS8 0x61\n#define SW4_CS9 0x62\n#define SW4_CS10 0x63\n#define SW4_CS11 0x64\n#define SW4_CS12 0x65\n#define SW4_CS13 0x66\n#define SW4_CS14 0x67\n#define SW4_CS15 0x68\n#define SW4_CS16 0x69\n#define SW4_CS17 0x6A\n#define SW4_CS18 0x6B\n#define SW4_CS19 0x6C\n#define SW4_CS20 0x6D\n#define SW4_CS21 0x6E\n#define SW4_CS22 0x6F\n#define SW4_CS23 0x70\n#define SW4_CS24 0x71\n#define SW4_CS25 0x72\n#define SW4_CS26 0x73\n#define SW4_CS27 0x74\n#define SW4_CS28 0x75\n#define SW4_CS29 0x76\n#define SW4_CS30 0x77\n\n#define SW5_CS1 0x78\n#define SW5_CS2 0x79\n#define SW5_CS3 0x7A\n#define SW5_CS4 0x7B\n#define SW5_CS5 0x7C\n#define SW5_CS6 0x7D\n#define SW5_CS7 0x7E\n#define SW5_CS8 0x7F\n#define SW5_CS9 0x80\n#define SW5_CS10 0x81\n#define SW5_CS11 0x82\n#define SW5_CS12 0x83\n#define SW5_CS13 0x84\n#define SW5_CS14 0x85\n#define SW5_CS15 0x86\n#define SW5_CS16 0x87\n#define SW5_CS17 0x88\n#define SW5_CS18 0x89\n#define SW5_CS19 0x8A\n#define SW5_CS20 0x8B\n#define SW5_CS21 0x8C\n#define SW5_CS22 0x8D\n#define SW5_CS23 0x8E\n#define SW5_CS24 0x8F\n#define SW5_CS25 0x90\n#define SW5_CS26 0x91\n#define SW5_CS27 0x92\n#define SW5_CS28 0x93\n#define SW5_CS29 0x94\n#define SW5_CS30 0x95\n\n#define SW6_CS1 0x96\n#define SW6_CS2 0x97\n#define SW6_CS3 0x98\n#define SW6_CS4 0x99\n#define SW6_CS5 0x9A\n#define SW6_CS6 0x9B\n#define SW6_CS7 0x9C\n#define SW6_CS8 0x9D\n#define SW6_CS9 0x9E\n#define SW6_CS10 0x9F\n#define SW6_CS11 0xA0\n#define SW6_CS12 0xA1\n#define SW6_CS13 0xA2\n#define SW6_CS14 0xA3\n#define SW6_CS15 0xA4\n#define SW6_CS16 0xA5\n#define SW6_CS17 0xA6\n#define SW6_CS18 0xA7\n#define SW6_CS19 0xA8\n#define SW6_CS20 0xA9\n#define SW6_CS21 0xAA\n#define SW6_CS22 0xAB\n#define SW6_CS23 0xAC\n#define SW6_CS24 0xAD\n#define SW6_CS25 0xAE\n#define SW6_CS26 0xAF\n#define SW6_CS27 0xB0\n#define SW6_CS28 0xB1\n#define SW6_CS29 0xB2\n#define SW6_CS30 0xB3\n\n#define SW7_CS1 0x100\n#define SW7_CS2 0x101\n#define SW7_CS3 0x102\n#define SW7_CS4 0x103\n#define SW7_CS5 0x104\n#define SW7_CS6 0x105\n#define SW7_CS7 0x106\n#define SW7_CS8 0x107\n#define SW7_CS9 0x108\n#define SW7_CS10 0x109\n#define SW7_CS11 0x10A\n#define SW7_CS12 0x10B\n#define SW7_CS13 0x10C\n#define SW7_CS14 0x10D\n#define SW7_CS15 0x10E\n#define SW7_CS16 0x10F\n#define SW7_CS17 0x110\n#define SW7_CS18 0x111\n#define SW7_CS19 0x112\n#define SW7_CS20 0x113\n#define SW7_CS21 0x114\n#define SW7_CS22 0x115\n#define SW7_CS23 0x116\n#define SW7_CS24 0x117\n#define SW7_CS25 0x118\n#define SW7_CS26 0x119\n#define SW7_CS27 0x11A\n#define SW7_CS28 0x11B\n#define SW7_CS29 0x11C\n#define SW7_CS30 0x11D\n\n#define SW8_CS1 0x11E\n#define SW8_CS2 0x11F\n#define SW8_CS3 0x120\n#define SW8_CS4 0x121\n#define SW8_CS5 0x122\n#define SW8_CS6 0x123\n#define SW8_CS7 0x124\n#define SW8_CS8 0x125\n#define SW8_CS9 0x126\n#define SW8_CS10 0x127\n#define SW8_CS11 0x128\n#define SW8_CS12 0x129\n#define SW8_CS13 0x12A\n#define SW8_CS14 0x12B\n#define SW8_CS15 0x12C\n#define SW8_CS16 0x12D\n#define SW8_CS17 0x12E\n#define SW8_CS18 0x12F\n#define SW8_CS19 0x130\n#define SW8_CS20 0x131\n#define SW8_CS21 0x132\n#define SW8_CS22 0x133\n#define SW8_CS23 0x134\n#define SW8_CS24 0x135\n#define SW8_CS25 0x136\n#define SW8_CS26 0x137\n#define SW8_CS27 0x138\n#define SW8_CS28 0x139\n#define SW8_CS29 0x13A\n#define SW8_CS30 0x13B\n\n#define SW9_CS1 0x13C\n#define SW9_CS2 0x13D\n#define SW9_CS3 0x13E\n#define SW9_CS4 0x13F\n#define SW9_CS5 0x140\n#define SW9_CS6 0x141\n#define SW9_CS7 0x142\n#define SW9_CS8 0x143\n#define SW9_CS9 0x144\n#define SW9_CS10 0x145\n#define SW9_CS11 0x146\n#define SW9_CS12 0x147\n#define SW9_CS13 0x148\n#define SW9_CS14 0x149\n#define SW9_CS15 0x14A\n#define SW9_CS16 0x14B\n#define SW9_CS17 0x14C\n#define SW9_CS18 0x14D\n#define SW9_CS19 0x14E\n#define SW9_CS20 0x14F\n#define SW9_CS21 0x150\n#define SW9_CS22 0x151\n#define SW9_CS23 0x152\n#define SW9_CS24 0x153\n#define SW9_CS25 0x154\n#define SW9_CS26 0x155\n#define SW9_CS27 0x156\n#define SW9_CS28 0x157\n#define SW9_CS29 0x158\n#define SW9_CS30 0x159\n\n#define SW1_CS31 0x15A\n#define SW1_CS32 0x15B\n#define SW1_CS33 0x15C\n#define SW1_CS34 0x15D\n#define SW1_CS35 0x15E\n#define SW1_CS36 0x15F\n#define SW1_CS37 0x160\n#define SW1_CS38 0x161\n#define SW1_CS39 0x162\n\n#define SW2_CS31 0x163\n#define SW2_CS32 0x164\n#define SW2_CS33 0x165\n#define SW2_CS34 0x166\n#define SW2_CS35 0x167\n#define SW2_CS36 0x168\n#define SW2_CS37 0x169\n#define SW2_CS38 0x16A\n#define SW2_CS39 0x16B\n\n#define SW3_CS31 0x16C\n#define SW3_CS32 0x16D\n#define SW3_CS33 0x16E\n#define SW3_CS34 0x16F\n#define SW3_CS35 0x170\n#define SW3_CS36 0x171\n#define SW3_CS37 0x172\n#define SW3_CS38 0x173\n#define SW3_CS39 0x174\n\n#define SW4_CS31 0x175\n#define SW4_CS32 0x176\n#define SW4_CS33 0x177\n#define SW4_CS34 0x178\n#define SW4_CS35 0x179\n#define SW4_CS36 0x17A\n#define SW4_CS37 0x17B\n#define SW4_CS38 0x17C\n#define SW4_CS39 0x17D\n\n#define SW5_CS31 0x17E\n#define SW5_CS32 0x17F\n#define SW5_CS33 0x180\n#define SW5_CS34 0x181\n#define SW5_CS35 0x182\n#define SW5_CS36 0x183\n#define SW5_CS37 0x184\n#define SW5_CS38 0x185\n#define SW5_CS39 0x186\n\n#define SW6_CS31 0x187\n#define SW6_CS32 0x188\n#define SW6_CS33 0x189\n#define SW6_CS34 0x18A\n#define SW6_CS35 0x18B\n#define SW6_CS36 0x18C\n#define SW6_CS37 0x18D\n#define SW6_CS38 0x18E\n#define SW6_CS39 0x18F\n\n#define SW7_CS31 0x190\n#define SW7_CS32 0x191\n#define SW7_CS33 0x192\n#define SW7_CS34 0x193\n#define SW7_CS35 0x194\n#define SW7_CS36 0x195\n#define SW7_CS37 0x196\n#define SW7_CS38 0x197\n#define SW7_CS39 0x198\n\n#define SW8_CS31 0x199\n#define SW8_CS32 0x19A\n#define SW8_CS33 0x19B\n#define SW8_CS34 0x19C\n#define SW8_CS35 0x19D\n#define SW8_CS36 0x19E\n#define SW8_CS37 0x19F\n#define SW8_CS38 0x1A0\n#define SW8_CS39 0x1A1\n\n#define SW9_CS31 0x1A2\n#define SW9_CS32 0x1A3\n#define SW9_CS33 0x1A4\n#define SW9_CS34 0x1A5\n#define SW9_CS35 0x1A6\n#define SW9_CS36 0x1A7\n#define SW9_CS37 0x1A8\n#define SW9_CS38 0x1A9\n#define SW9_CS39 0x1AA\n"
  },
  {
    "path": "drivers/led/issi/is31fl3741.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3741.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3741_PWM_0_REGISTER_COUNT 180\n#define IS31FL3741_PWM_1_REGISTER_COUNT 171\n#define IS31FL3741_SCALING_0_REGISTER_COUNT 180\n#define IS31FL3741_SCALING_1_REGISTER_COUNT 171\n\n#ifndef IS31FL3741_I2C_TIMEOUT\n#    define IS31FL3741_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3741_I2C_PERSISTENCE\n#    define IS31FL3741_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3741_CONFIGURATION\n#    define IS31FL3741_CONFIGURATION 0x01\n#endif\n\n#ifndef IS31FL3741_PWM_FREQUENCY\n#    define IS31FL3741_PWM_FREQUENCY IS31FL3741_PWM_FREQUENCY_29K_HZ\n#endif\n\n#ifndef IS31FL3741_SW_PULLUP\n#    define IS31FL3741_SW_PULLUP IS31FL3741_PUR_32K_OHM\n#endif\n\n#ifndef IS31FL3741_CS_PULLDOWN\n#    define IS31FL3741_CS_PULLDOWN IS31FL3741_PDR_32K_OHM\n#endif\n\n#ifndef IS31FL3741_GLOBAL_CURRENT\n#    define IS31FL3741_GLOBAL_CURRENT 0xFF\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3741_DRIVER_COUNT] = {\n    IS31FL3741_I2C_ADDRESS_1,\n#ifdef IS31FL3741_I2C_ADDRESS_2\n    IS31FL3741_I2C_ADDRESS_2,\n#    ifdef IS31FL3741_I2C_ADDRESS_3\n    IS31FL3741_I2C_ADDRESS_3,\n#        ifdef IS31FL3741_I2C_ADDRESS_4\n    IS31FL3741_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the IS31FL3741 and IS31FL3741A PWM registers.\n// The scaling buffers match the page 2 and 3 LED On/Off registers.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in is31fl3741_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct is31fl3741_driver_t {\n    uint8_t pwm_buffer_0[IS31FL3741_PWM_0_REGISTER_COUNT];\n    uint8_t pwm_buffer_1[IS31FL3741_PWM_1_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer_0[IS31FL3741_SCALING_0_REGISTER_COUNT];\n    uint8_t scaling_buffer_1[IS31FL3741_SCALING_1_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3741_driver_t;\n\nis31fl3741_driver_t driver_buffers[IS31FL3741_DRIVER_COUNT] = {{\n    .pwm_buffer_0         = {0},\n    .pwm_buffer_1         = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer_0     = {0},\n    .scaling_buffer_1     = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3741_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3741_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3741_select_page(uint8_t index, uint8_t page) {\n    is31fl3741_write_register(index, IS31FL3741_REG_COMMAND_WRITE_LOCK, IS31FL3741_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3741_write_register(index, IS31FL3741_REG_COMMAND, page);\n}\n\nvoid is31fl3741_write_pwm_buffer(uint8_t index) {\n    is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_0);\n\n    // Transmit PWM0 registers in 6 transfers of 30 bytes.\n\n    // Iterate over the pwm_buffer_0 contents at 30 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3741_PWM_0_REGISTER_COUNT; i += 30) {\n#if IS31FL3741_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3741_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_0 + i, 30, IS31FL3741_I2C_TIMEOUT);\n#endif\n    }\n\n    is31fl3741_select_page(index, IS31FL3741_COMMAND_PWM_1);\n\n    // Transmit PWM1 registers in 9 transfers of 19 bytes.\n\n    // Iterate over the pwm_buffer_1 contents at 19 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3741_PWM_1_REGISTER_COUNT; i += 19) {\n#if IS31FL3741_I2C_PERSISTENCE > 0\n        for (uint8_t i = 0; i < IS31FL3741_I2C_PERSISTENCE; i++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer_1 + i, 19, IS31FL3741_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3741_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3741_SDB_PIN)\n    gpio_set_pin_output(IS31FL3741_SDB_PIN);\n    gpio_write_pin_high(IS31FL3741_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {\n        is31fl3741_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3741_LED_COUNT; i++) {\n        is31fl3741_set_led_control_register(i, true, true, true);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {\n        is31fl3741_update_led_control_registers(i);\n    }\n}\n\nvoid is31fl3741_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n    // Unlock the command register.\n\n    is31fl3741_select_page(index, IS31FL3741_COMMAND_FUNCTION);\n\n    // Set to Normal operation\n    is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_CONFIGURATION, IS31FL3741_CONFIGURATION);\n\n    // Set Golbal Current Control Register\n    is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3741_GLOBAL_CURRENT);\n    // Set Pull up & Down for SWx CSy\n    is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PULLDOWNUP, ((IS31FL3741_CS_PULLDOWN << 4) | IS31FL3741_SW_PULLUP));\n    // Set PWM frequency\n    is31fl3741_write_register(index, IS31FL3741_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3741_PWM_FREQUENCY & 0b1111));\n\n    // is31fl3741_update_led_scaling_registers(index, 0xFF, 0xFF, 0xFF);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nuint8_t get_pwm_value(uint8_t driver, uint16_t reg) {\n    if (reg & 0x100) {\n        return driver_buffers[driver].pwm_buffer_1[reg & 0xFF];\n    } else {\n        return driver_buffers[driver].pwm_buffer_0[reg];\n    }\n}\n\nvoid set_pwm_value(uint8_t driver, uint16_t reg, uint8_t value) {\n    if (reg & 0x100) {\n        driver_buffers[driver].pwm_buffer_1[reg & 0xFF] = value;\n    } else {\n        driver_buffers[driver].pwm_buffer_0[reg] = value;\n    }\n}\n\nvoid is31fl3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3741_led_t led;\n\n    if (index >= 0 && index < IS31FL3741_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));\n\n        if (get_pwm_value(led.driver, led.r) == red && get_pwm_value(led.driver, led.g) == green && get_pwm_value(led.driver, led.b) == blue) {\n            return;\n        }\n\n        set_pwm_value(led.driver, led.r, red);\n        set_pwm_value(led.driver, led.g, green);\n        set_pwm_value(led.driver, led.b, blue);\n        driver_buffers[led.driver].pwm_buffer_dirty = true;\n    }\n}\n\nvoid is31fl3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < IS31FL3741_LED_COUNT; i++) {\n        is31fl3741_set_color(i, red, green, blue);\n    }\n}\n\nvoid set_scaling_value(uint8_t driver, uint16_t reg, uint8_t value) {\n    if (reg & 0x100) {\n        driver_buffers[driver].scaling_buffer_1[reg & 0xFF] = value;\n    } else {\n        driver_buffers[driver].scaling_buffer_0[reg] = value;\n    }\n}\n\nvoid is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {\n    is31fl3741_led_t led;\n    memcpy_P(&led, (&g_is31fl3741_leds[index]), sizeof(led));\n\n    set_scaling_value(led.driver, led.r, red ? 0xFF : 0x00);\n    set_scaling_value(led.driver, led.g, green ? 0xFF : 0x00);\n    set_scaling_value(led.driver, led.b, blue ? 0xFF : 0x00);\n\n    driver_buffers[led.driver].scaling_buffer_dirty = true;\n}\n\nvoid is31fl3741_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3741_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue) {\n    set_pwm_value(pled->driver, pled->r, red);\n    set_pwm_value(pled->driver, pled->g, green);\n    set_pwm_value(pled->driver, pled->b, blue);\n    driver_buffers[pled->driver].pwm_buffer_dirty = true;\n}\n\nvoid is31fl3741_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_0);\n\n        for (uint8_t i = 0; i < IS31FL3741_SCALING_0_REGISTER_COUNT; i++) {\n            is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_0[i]);\n        }\n\n        is31fl3741_select_page(index, IS31FL3741_COMMAND_SCALING_1);\n\n        for (uint8_t i = 0; i < IS31FL3741_SCALING_1_REGISTER_COUNT; i++) {\n            is31fl3741_write_register(index, i, driver_buffers[index].scaling_buffer_1[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue) {\n    set_scaling_value(pled->driver, pled->r, red);\n    set_scaling_value(pled->driver, pled->g, green);\n    set_scaling_value(pled->driver, pled->b, blue);\n    driver_buffers[pled->driver].scaling_buffer_dirty = true;\n}\n\nvoid is31fl3741_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3741_DRIVER_COUNT; i++) {\n        is31fl3741_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3741.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3741_REG_INTERRUPT_MASK 0xF0\n#define IS31FL3741_REG_INTERRUPT_STATUS 0xF1\n#define IS31FL3741_REG_ID 0xFC\n\n#define IS31FL3741_REG_COMMAND 0xFD\n\n#define IS31FL3741_COMMAND_PWM_0 0x00\n#define IS31FL3741_COMMAND_PWM_1 0x01\n#define IS31FL3741_COMMAND_SCALING_0 0x02\n#define IS31FL3741_COMMAND_SCALING_1 0x03\n#define IS31FL3741_COMMAND_FUNCTION 0x04\n\n#define IS31FL3741_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3741_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3741_FUNCTION_REG_PULLDOWNUP 0x02\n#define IS31FL3741_FUNCTION_REG_PWM_FREQUENCY 0x36\n#define IS31FL3741_FUNCTION_REG_RESET 0x3F\n\n#define IS31FL3741_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3741_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3741_I2C_ADDRESS_GND 0x30\n#define IS31FL3741_I2C_ADDRESS_SCL 0x31\n#define IS31FL3741_I2C_ADDRESS_SDA 0x32\n#define IS31FL3741_I2C_ADDRESS_VCC 0x33\n\n#if defined(RGB_MATRIX_IS31FL3741)\n#    define IS31FL3741_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3741_I2C_ADDRESS_4)\n#    define IS31FL3741_DRIVER_COUNT 4\n#elif defined(IS31FL3741_I2C_ADDRESS_3)\n#    define IS31FL3741_DRIVER_COUNT 3\n#elif defined(IS31FL3741_I2C_ADDRESS_2)\n#    define IS31FL3741_DRIVER_COUNT 2\n#elif defined(IS31FL3741_I2C_ADDRESS_1)\n#    define IS31FL3741_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3741_led_t {\n    uint8_t  driver : 2;\n    uint16_t r : 9;\n    uint16_t g : 9;\n    uint16_t b : 9;\n} PACKED is31fl3741_led_t;\n\nextern const is31fl3741_led_t PROGMEM g_is31fl3741_leds[IS31FL3741_LED_COUNT];\n\nvoid is31fl3741_init_drivers(void);\nvoid is31fl3741_init(uint8_t index);\nvoid is31fl3741_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3741_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3741_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid is31fl3741_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3741_set_led_control_register(uint8_t index, bool red, bool green, bool blue);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid is31fl3741_update_pwm_buffers(uint8_t index);\nvoid is31fl3741_update_led_control_registers(uint8_t index);\nvoid is31fl3741_set_scaling_registers(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3741_set_pwm_buffer(const is31fl3741_led_t *pled, uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3741_flush(void);\n\n#define IS31FL3741_PDR_0_OHM 0b000   // No pull-down resistor\n#define IS31FL3741_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3741_PDR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3741_PDR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3741_PDR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3741_PDR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3741_PDR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3741_PDR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3741_PUR_0_OHM 0b000   // No pull-up resistor\n#define IS31FL3741_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3741_PUR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3741_PUR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3741_PUR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3741_PUR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3741_PUR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3741_PUR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3741_PWM_FREQUENCY_29K_HZ 0b0000\n#define IS31FL3741_PWM_FREQUENCY_3K6_HZ 0b0011\n#define IS31FL3741_PWM_FREQUENCY_1K8_HZ 0b0111\n#define IS31FL3741_PWM_FREQUENCY_900_HZ 0b1011\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n#define SW1_CS17 0x10\n#define SW1_CS18 0x11\n#define SW1_CS19 0x12\n#define SW1_CS20 0x13\n#define SW1_CS21 0x14\n#define SW1_CS22 0x15\n#define SW1_CS23 0x16\n#define SW1_CS24 0x17\n#define SW1_CS25 0x18\n#define SW1_CS26 0x19\n#define SW1_CS27 0x1A\n#define SW1_CS28 0x1B\n#define SW1_CS29 0x1C\n#define SW1_CS30 0x1D\n\n#define SW2_CS1 0x1E\n#define SW2_CS2 0x1F\n#define SW2_CS3 0x20\n#define SW2_CS4 0x21\n#define SW2_CS5 0x22\n#define SW2_CS6 0x23\n#define SW2_CS7 0x24\n#define SW2_CS8 0x25\n#define SW2_CS9 0x26\n#define SW2_CS10 0x27\n#define SW2_CS11 0x28\n#define SW2_CS12 0x29\n#define SW2_CS13 0x2A\n#define SW2_CS14 0x2B\n#define SW2_CS15 0x2C\n#define SW2_CS16 0x2D\n#define SW2_CS17 0x2E\n#define SW2_CS18 0x2F\n#define SW2_CS19 0x30\n#define SW2_CS20 0x31\n#define SW2_CS21 0x32\n#define SW2_CS22 0x33\n#define SW2_CS23 0x34\n#define SW2_CS24 0x35\n#define SW2_CS25 0x36\n#define SW2_CS26 0x37\n#define SW2_CS27 0x38\n#define SW2_CS28 0x39\n#define SW2_CS29 0x3A\n#define SW2_CS30 0x3B\n\n#define SW3_CS1 0x3C\n#define SW3_CS2 0x3D\n#define SW3_CS3 0x3E\n#define SW3_CS4 0x3F\n#define SW3_CS5 0x40\n#define SW3_CS6 0x41\n#define SW3_CS7 0x42\n#define SW3_CS8 0x43\n#define SW3_CS9 0x44\n#define SW3_CS10 0x45\n#define SW3_CS11 0x46\n#define SW3_CS12 0x47\n#define SW3_CS13 0x48\n#define SW3_CS14 0x49\n#define SW3_CS15 0x4A\n#define SW3_CS16 0x4B\n#define SW3_CS17 0x4C\n#define SW3_CS18 0x4D\n#define SW3_CS19 0x4E\n#define SW3_CS20 0x4F\n#define SW3_CS21 0x50\n#define SW3_CS22 0x51\n#define SW3_CS23 0x52\n#define SW3_CS24 0x53\n#define SW3_CS25 0x54\n#define SW3_CS26 0x55\n#define SW3_CS27 0x56\n#define SW3_CS28 0x57\n#define SW3_CS29 0x58\n#define SW3_CS30 0x59\n\n#define SW4_CS1 0x5A\n#define SW4_CS2 0x5B\n#define SW4_CS3 0x5C\n#define SW4_CS4 0x5D\n#define SW4_CS5 0x5E\n#define SW4_CS6 0x5F\n#define SW4_CS7 0x60\n#define SW4_CS8 0x61\n#define SW4_CS9 0x62\n#define SW4_CS10 0x63\n#define SW4_CS11 0x64\n#define SW4_CS12 0x65\n#define SW4_CS13 0x66\n#define SW4_CS14 0x67\n#define SW4_CS15 0x68\n#define SW4_CS16 0x69\n#define SW4_CS17 0x6A\n#define SW4_CS18 0x6B\n#define SW4_CS19 0x6C\n#define SW4_CS20 0x6D\n#define SW4_CS21 0x6E\n#define SW4_CS22 0x6F\n#define SW4_CS23 0x70\n#define SW4_CS24 0x71\n#define SW4_CS25 0x72\n#define SW4_CS26 0x73\n#define SW4_CS27 0x74\n#define SW4_CS28 0x75\n#define SW4_CS29 0x76\n#define SW4_CS30 0x77\n\n#define SW5_CS1 0x78\n#define SW5_CS2 0x79\n#define SW5_CS3 0x7A\n#define SW5_CS4 0x7B\n#define SW5_CS5 0x7C\n#define SW5_CS6 0x7D\n#define SW5_CS7 0x7E\n#define SW5_CS8 0x7F\n#define SW5_CS9 0x80\n#define SW5_CS10 0x81\n#define SW5_CS11 0x82\n#define SW5_CS12 0x83\n#define SW5_CS13 0x84\n#define SW5_CS14 0x85\n#define SW5_CS15 0x86\n#define SW5_CS16 0x87\n#define SW5_CS17 0x88\n#define SW5_CS18 0x89\n#define SW5_CS19 0x8A\n#define SW5_CS20 0x8B\n#define SW5_CS21 0x8C\n#define SW5_CS22 0x8D\n#define SW5_CS23 0x8E\n#define SW5_CS24 0x8F\n#define SW5_CS25 0x90\n#define SW5_CS26 0x91\n#define SW5_CS27 0x92\n#define SW5_CS28 0x93\n#define SW5_CS29 0x94\n#define SW5_CS30 0x95\n\n#define SW6_CS1 0x96\n#define SW6_CS2 0x97\n#define SW6_CS3 0x98\n#define SW6_CS4 0x99\n#define SW6_CS5 0x9A\n#define SW6_CS6 0x9B\n#define SW6_CS7 0x9C\n#define SW6_CS8 0x9D\n#define SW6_CS9 0x9E\n#define SW6_CS10 0x9F\n#define SW6_CS11 0xA0\n#define SW6_CS12 0xA1\n#define SW6_CS13 0xA2\n#define SW6_CS14 0xA3\n#define SW6_CS15 0xA4\n#define SW6_CS16 0xA5\n#define SW6_CS17 0xA6\n#define SW6_CS18 0xA7\n#define SW6_CS19 0xA8\n#define SW6_CS20 0xA9\n#define SW6_CS21 0xAA\n#define SW6_CS22 0xAB\n#define SW6_CS23 0xAC\n#define SW6_CS24 0xAD\n#define SW6_CS25 0xAE\n#define SW6_CS26 0xAF\n#define SW6_CS27 0xB0\n#define SW6_CS28 0xB1\n#define SW6_CS29 0xB2\n#define SW6_CS30 0xB3\n\n#define SW7_CS1 0x100\n#define SW7_CS2 0x101\n#define SW7_CS3 0x102\n#define SW7_CS4 0x103\n#define SW7_CS5 0x104\n#define SW7_CS6 0x105\n#define SW7_CS7 0x106\n#define SW7_CS8 0x107\n#define SW7_CS9 0x108\n#define SW7_CS10 0x109\n#define SW7_CS11 0x10A\n#define SW7_CS12 0x10B\n#define SW7_CS13 0x10C\n#define SW7_CS14 0x10D\n#define SW7_CS15 0x10E\n#define SW7_CS16 0x10F\n#define SW7_CS17 0x110\n#define SW7_CS18 0x111\n#define SW7_CS19 0x112\n#define SW7_CS20 0x113\n#define SW7_CS21 0x114\n#define SW7_CS22 0x115\n#define SW7_CS23 0x116\n#define SW7_CS24 0x117\n#define SW7_CS25 0x118\n#define SW7_CS26 0x119\n#define SW7_CS27 0x11A\n#define SW7_CS28 0x11B\n#define SW7_CS29 0x11C\n#define SW7_CS30 0x11D\n\n#define SW8_CS1 0x11E\n#define SW8_CS2 0x11F\n#define SW8_CS3 0x120\n#define SW8_CS4 0x121\n#define SW8_CS5 0x122\n#define SW8_CS6 0x123\n#define SW8_CS7 0x124\n#define SW8_CS8 0x125\n#define SW8_CS9 0x126\n#define SW8_CS10 0x127\n#define SW8_CS11 0x128\n#define SW8_CS12 0x129\n#define SW8_CS13 0x12A\n#define SW8_CS14 0x12B\n#define SW8_CS15 0x12C\n#define SW8_CS16 0x12D\n#define SW8_CS17 0x12E\n#define SW8_CS18 0x12F\n#define SW8_CS19 0x130\n#define SW8_CS20 0x131\n#define SW8_CS21 0x132\n#define SW8_CS22 0x133\n#define SW8_CS23 0x134\n#define SW8_CS24 0x135\n#define SW8_CS25 0x136\n#define SW8_CS26 0x137\n#define SW8_CS27 0x138\n#define SW8_CS28 0x139\n#define SW8_CS29 0x13A\n#define SW8_CS30 0x13B\n\n#define SW9_CS1 0x13C\n#define SW9_CS2 0x13D\n#define SW9_CS3 0x13E\n#define SW9_CS4 0x13F\n#define SW9_CS5 0x140\n#define SW9_CS6 0x141\n#define SW9_CS7 0x142\n#define SW9_CS8 0x143\n#define SW9_CS9 0x144\n#define SW9_CS10 0x145\n#define SW9_CS11 0x146\n#define SW9_CS12 0x147\n#define SW9_CS13 0x148\n#define SW9_CS14 0x149\n#define SW9_CS15 0x14A\n#define SW9_CS16 0x14B\n#define SW9_CS17 0x14C\n#define SW9_CS18 0x14D\n#define SW9_CS19 0x14E\n#define SW9_CS20 0x14F\n#define SW9_CS21 0x150\n#define SW9_CS22 0x151\n#define SW9_CS23 0x152\n#define SW9_CS24 0x153\n#define SW9_CS25 0x154\n#define SW9_CS26 0x155\n#define SW9_CS27 0x156\n#define SW9_CS28 0x157\n#define SW9_CS29 0x158\n#define SW9_CS30 0x159\n\n#define SW1_CS31 0x15A\n#define SW1_CS32 0x15B\n#define SW1_CS33 0x15C\n#define SW1_CS34 0x15D\n#define SW1_CS35 0x15E\n#define SW1_CS36 0x15F\n#define SW1_CS37 0x160\n#define SW1_CS38 0x161\n#define SW1_CS39 0x162\n\n#define SW2_CS31 0x163\n#define SW2_CS32 0x164\n#define SW2_CS33 0x165\n#define SW2_CS34 0x166\n#define SW2_CS35 0x167\n#define SW2_CS36 0x168\n#define SW2_CS37 0x169\n#define SW2_CS38 0x16A\n#define SW2_CS39 0x16B\n\n#define SW3_CS31 0x16C\n#define SW3_CS32 0x16D\n#define SW3_CS33 0x16E\n#define SW3_CS34 0x16F\n#define SW3_CS35 0x170\n#define SW3_CS36 0x171\n#define SW3_CS37 0x172\n#define SW3_CS38 0x173\n#define SW3_CS39 0x174\n\n#define SW4_CS31 0x175\n#define SW4_CS32 0x176\n#define SW4_CS33 0x177\n#define SW4_CS34 0x178\n#define SW4_CS35 0x179\n#define SW4_CS36 0x17A\n#define SW4_CS37 0x17B\n#define SW4_CS38 0x17C\n#define SW4_CS39 0x17D\n\n#define SW5_CS31 0x17E\n#define SW5_CS32 0x17F\n#define SW5_CS33 0x180\n#define SW5_CS34 0x181\n#define SW5_CS35 0x182\n#define SW5_CS36 0x183\n#define SW5_CS37 0x184\n#define SW5_CS38 0x185\n#define SW5_CS39 0x186\n\n#define SW6_CS31 0x187\n#define SW6_CS32 0x188\n#define SW6_CS33 0x189\n#define SW6_CS34 0x18A\n#define SW6_CS35 0x18B\n#define SW6_CS36 0x18C\n#define SW6_CS37 0x18D\n#define SW6_CS38 0x18E\n#define SW6_CS39 0x18F\n\n#define SW7_CS31 0x190\n#define SW7_CS32 0x191\n#define SW7_CS33 0x192\n#define SW7_CS34 0x193\n#define SW7_CS35 0x194\n#define SW7_CS36 0x195\n#define SW7_CS37 0x196\n#define SW7_CS38 0x197\n#define SW7_CS39 0x198\n\n#define SW8_CS31 0x199\n#define SW8_CS32 0x19A\n#define SW8_CS33 0x19B\n#define SW8_CS34 0x19C\n#define SW8_CS35 0x19D\n#define SW8_CS36 0x19E\n#define SW8_CS37 0x19F\n#define SW8_CS38 0x1A0\n#define SW8_CS39 0x1A1\n\n#define SW9_CS31 0x1A2\n#define SW9_CS32 0x1A3\n#define SW9_CS33 0x1A4\n#define SW9_CS34 0x1A5\n#define SW9_CS35 0x1A6\n#define SW9_CS36 0x1A7\n#define SW9_CS37 0x1A8\n#define SW9_CS38 0x1A9\n#define SW9_CS39 0x1AA\n"
  },
  {
    "path": "drivers/led/issi/is31fl3742a-mono.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3742a-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3742A_PWM_REGISTER_COUNT 180\n#define IS31FL3742A_SCALING_REGISTER_COUNT 180\n\n#ifndef IS31FL3742A_I2C_TIMEOUT\n#    define IS31FL3742A_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3742A_I2C_PERSISTENCE\n#    define IS31FL3742A_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3742A_CONFIGURATION\n#    define IS31FL3742A_CONFIGURATION 0x31\n#endif\n\n#ifndef IS31FL3742A_PWM_FREQUENCY\n#    define IS31FL3742A_PWM_FREQUENCY IS31FL3742A_PWM_FREQUENCY_29K_HZ\n#endif\n\n#ifndef IS31FL3742A_SW_PULLDOWN\n#    define IS31FL3742A_SW_PULLDOWN IS31FL3742A_PDR_8K_OHM\n#endif\n\n#ifndef IS31FL3742A_CS_PULLUP\n#    define IS31FL3742A_CS_PULLUP IS31FL3742A_PUR_8K_OHM\n#endif\n\n#ifndef IS31FL3742A_GLOBAL_CURRENT\n#    define IS31FL3742A_GLOBAL_CURRENT 0xFF\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3742A_DRIVER_COUNT] = {\n    IS31FL3742A_I2C_ADDRESS_1,\n#ifdef IS31FL3742A_I2C_ADDRESS_2\n    IS31FL3742A_I2C_ADDRESS_2,\n#    ifdef IS31FL3742A_I2C_ADDRESS_3\n    IS31FL3742A_I2C_ADDRESS_3,\n#        ifdef IS31FL3742A_I2C_ADDRESS_4\n    IS31FL3742A_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\ntypedef struct is31fl3742a_driver_t {\n    uint8_t pwm_buffer[IS31FL3742A_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer[IS31FL3742A_SCALING_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3742a_driver_t;\n\nis31fl3742a_driver_t driver_buffers[IS31FL3742A_DRIVER_COUNT] = {{\n    .pwm_buffer           = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer       = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3742A_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3742A_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3742a_select_page(uint8_t index, uint8_t page) {\n    is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND_WRITE_LOCK, IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND, page);\n}\n\nvoid is31fl3742a_write_pwm_buffer(uint8_t index) {\n    // Assumes page 0 is already selected.\n    // Transmit PWM registers in 6 transfers of 30 bytes.\n\n    // Iterate over the pwm_buffer contents at 30 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i += 30) {\n#if IS31FL3742A_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3742A_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3742a_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3742A_SDB_PIN)\n    gpio_set_pin_output(IS31FL3742A_SDB_PIN);\n    gpio_write_pin_high(IS31FL3742A_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {\n        is31fl3742a_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) {\n        is31fl3742a_set_scaling_register(i, 0xFF);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {\n        is31fl3742a_update_scaling_registers(i);\n    }\n}\n\nvoid is31fl3742a_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) {\n        is31fl3742a_write_register(index, i, 0x00);\n    }\n\n    is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM);\n\n    for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i++) {\n        is31fl3742a_write_register(index, i, 0x00);\n    }\n\n    is31fl3742a_select_page(index, IS31FL3742A_COMMAND_FUNCTION);\n\n    is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PULLDOWNUP, (IS31FL3742A_SW_PULLDOWN << 4) | IS31FL3742A_CS_PULLUP);\n    is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3742A_GLOBAL_CURRENT);\n    is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3742A_PWM_FREQUENCY & 0b0111));\n    is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_CONFIGURATION, IS31FL3742A_CONFIGURATION);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3742a_set_value(int index, uint8_t value) {\n    is31fl3742a_led_t led;\n\n    if (index >= 0 && index < IS31FL3742A_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.v] = value;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3742a_set_value_all(uint8_t value) {\n    for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) {\n        is31fl3742a_set_value(i, value);\n    }\n}\n\nvoid is31fl3742a_set_scaling_register(uint8_t index, uint8_t value) {\n    is31fl3742a_led_t led;\n    memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led));\n\n    driver_buffers[led.driver].scaling_buffer[led.v] = value;\n    driver_buffers[led.driver].scaling_buffer_dirty  = true;\n}\n\nvoid is31fl3742a_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM);\n\n        is31fl3742a_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3742a_update_scaling_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING);\n\n        for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) {\n            is31fl3742a_write_register(index, i, driver_buffers[index].scaling_buffer[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3742a_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {\n        is31fl3742a_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3742a-mono.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3742A_REG_INTERRUPT_MASK 0xF0\n#define IS31FL3742A_REG_INTERRUPT_STATUS 0xF1\n#define IS31FL3742A_REG_ID 0xFC\n\n#define IS31FL3742A_REG_COMMAND 0xFD\n\n#define IS31FL3742A_COMMAND_PWM 0x00\n#define IS31FL3742A_COMMAND_SCALING 0x02\n#define IS31FL3742A_COMMAND_FUNCTION 0x04\n\n#define IS31FL3742A_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3742A_FUNCTION_REG_PULLDOWNUP 0x02\n#define IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY 0x36\n#define IS31FL3742A_FUNCTION_REG_RESET 0x3F\n#define IS31FL3742A_FUNCTION_REG_SPREAD_SPECTRUM 0x41\n\n#define IS31FL3742A_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3742A_I2C_ADDRESS_GND 0x30\n#define IS31FL3742A_I2C_ADDRESS_SCL 0x31\n#define IS31FL3742A_I2C_ADDRESS_SDA 0x32\n#define IS31FL3742A_I2C_ADDRESS_VCC 0x33\n\n#if defined(LED_MATRIX_IS31FL3742A)\n#    define IS31FL3742A_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3742A_I2C_ADDRESS_4)\n#    define IS31FL3742A_DRIVER_COUNT 4\n#elif defined(IS31FL3742A_I2C_ADDRESS_3)\n#    define IS31FL3742A_DRIVER_COUNT 3\n#elif defined(IS31FL3742A_I2C_ADDRESS_2)\n#    define IS31FL3742A_DRIVER_COUNT 2\n#elif defined(IS31FL3742A_I2C_ADDRESS_1)\n#    define IS31FL3742A_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3742a_led_t {\n    uint8_t driver : 2;\n    uint8_t v;\n} PACKED is31fl3742a_led_t;\n\nextern const is31fl3742a_led_t PROGMEM g_is31fl3742a_leds[IS31FL3742A_LED_COUNT];\n\nvoid is31fl3742a_init_drivers(void);\nvoid is31fl3742a_init(uint8_t index);\nvoid is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3742a_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3742a_set_value(int index, uint8_t value);\nvoid is31fl3742a_set_value_all(uint8_t value);\n\nvoid is31fl3742a_set_scaling_register(uint8_t index, uint8_t value);\n\nvoid is31fl3742a_update_pwm_buffers(uint8_t index);\nvoid is31fl3742a_update_scaling_registers(uint8_t index);\n\nvoid is31fl3742a_flush(void);\n\n#define IS31FL3742A_PDR_0_OHM 0b000   // No pull-down resistor\n#define IS31FL3742A_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3742A_PDR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3742A_PDR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3742A_PDR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3742A_PDR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3742A_PDR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3742A_PDR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3742A_PUR_0_OHM 0b000   // No pull-up resistor\n#define IS31FL3742A_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3742A_PUR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3742A_PUR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3742A_PUR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3742A_PUR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3742A_PUR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3742A_PUR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3742A_PWM_FREQUENCY_29K_HZ 0b0000\n#define IS31FL3742A_PWM_FREQUENCY_3K6_HZ 0b0011\n#define IS31FL3742A_PWM_FREQUENCY_1K8_HZ 0b0111\n#define IS31FL3742A_PWM_FREQUENCY_900_HZ 0b1011\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n#define SW1_CS17 0x10\n#define SW1_CS18 0x11\n#define SW1_CS19 0x12\n#define SW1_CS20 0x13\n#define SW1_CS21 0x14\n#define SW1_CS22 0x15\n#define SW1_CS23 0x16\n#define SW1_CS24 0x17\n#define SW1_CS25 0x18\n#define SW1_CS26 0x19\n#define SW1_CS27 0x1A\n#define SW1_CS28 0x1B\n#define SW1_CS29 0x1C\n#define SW1_CS30 0x1D\n\n#define SW2_CS1 0x1E\n#define SW2_CS2 0x1F\n#define SW2_CS3 0x20\n#define SW2_CS4 0x21\n#define SW2_CS5 0x22\n#define SW2_CS6 0x23\n#define SW2_CS7 0x24\n#define SW2_CS8 0x25\n#define SW2_CS9 0x26\n#define SW2_CS10 0x27\n#define SW2_CS11 0x28\n#define SW2_CS12 0x29\n#define SW2_CS13 0x2A\n#define SW2_CS14 0x2B\n#define SW2_CS15 0x2C\n#define SW2_CS16 0x2D\n#define SW2_CS17 0x2E\n#define SW2_CS18 0x2F\n#define SW2_CS19 0x30\n#define SW2_CS20 0x31\n#define SW2_CS21 0x32\n#define SW2_CS22 0x33\n#define SW2_CS23 0x34\n#define SW2_CS24 0x35\n#define SW2_CS25 0x36\n#define SW2_CS26 0x37\n#define SW2_CS27 0x38\n#define SW2_CS28 0x39\n#define SW2_CS29 0x3A\n#define SW2_CS30 0x3B\n\n#define SW3_CS1 0x3C\n#define SW3_CS2 0x3D\n#define SW3_CS3 0x3E\n#define SW3_CS4 0x3F\n#define SW3_CS5 0x40\n#define SW3_CS6 0x41\n#define SW3_CS7 0x42\n#define SW3_CS8 0x43\n#define SW3_CS9 0x44\n#define SW3_CS10 0x45\n#define SW3_CS11 0x46\n#define SW3_CS12 0x47\n#define SW3_CS13 0x48\n#define SW3_CS14 0x49\n#define SW3_CS15 0x4A\n#define SW3_CS16 0x4B\n#define SW3_CS17 0x4C\n#define SW3_CS18 0x4D\n#define SW3_CS19 0x4E\n#define SW3_CS20 0x4F\n#define SW3_CS21 0x50\n#define SW3_CS22 0x51\n#define SW3_CS23 0x52\n#define SW3_CS24 0x53\n#define SW3_CS25 0x54\n#define SW3_CS26 0x55\n#define SW3_CS27 0x56\n#define SW3_CS28 0x57\n#define SW3_CS29 0x58\n#define SW3_CS30 0x59\n\n#define SW4_CS1 0x5A\n#define SW4_CS2 0x5B\n#define SW4_CS3 0x5C\n#define SW4_CS4 0x5D\n#define SW4_CS5 0x5E\n#define SW4_CS6 0x5F\n#define SW4_CS7 0x60\n#define SW4_CS8 0x61\n#define SW4_CS9 0x62\n#define SW4_CS10 0x63\n#define SW4_CS11 0x64\n#define SW4_CS12 0x65\n#define SW4_CS13 0x66\n#define SW4_CS14 0x67\n#define SW4_CS15 0x68\n#define SW4_CS16 0x69\n#define SW4_CS17 0x6A\n#define SW4_CS18 0x6B\n#define SW4_CS19 0x6C\n#define SW4_CS20 0x6D\n#define SW4_CS21 0x6E\n#define SW4_CS22 0x6F\n#define SW4_CS23 0x70\n#define SW4_CS24 0x71\n#define SW4_CS25 0x72\n#define SW4_CS26 0x73\n#define SW4_CS27 0x74\n#define SW4_CS28 0x75\n#define SW4_CS29 0x76\n#define SW4_CS30 0x77\n\n#define SW5_CS1 0x78\n#define SW5_CS2 0x79\n#define SW5_CS3 0x7A\n#define SW5_CS4 0x7B\n#define SW5_CS5 0x7C\n#define SW5_CS6 0x7D\n#define SW5_CS7 0x7E\n#define SW5_CS8 0x7F\n#define SW5_CS9 0x80\n#define SW5_CS10 0x81\n#define SW5_CS11 0x82\n#define SW5_CS12 0x83\n#define SW5_CS13 0x84\n#define SW5_CS14 0x85\n#define SW5_CS15 0x86\n#define SW5_CS16 0x87\n#define SW5_CS17 0x88\n#define SW5_CS18 0x89\n#define SW5_CS19 0x8A\n#define SW5_CS20 0x8B\n#define SW5_CS21 0x8C\n#define SW5_CS22 0x8D\n#define SW5_CS23 0x8E\n#define SW5_CS24 0x8F\n#define SW5_CS25 0x90\n#define SW5_CS26 0x91\n#define SW5_CS27 0x92\n#define SW5_CS28 0x93\n#define SW5_CS29 0x94\n#define SW5_CS30 0x95\n\n#define SW6_CS1 0x96\n#define SW6_CS2 0x97\n#define SW6_CS3 0x98\n#define SW6_CS4 0x99\n#define SW6_CS5 0x9A\n#define SW6_CS6 0x9B\n#define SW6_CS7 0x9C\n#define SW6_CS8 0x9D\n#define SW6_CS9 0x9E\n#define SW6_CS10 0x9F\n#define SW6_CS11 0xA0\n#define SW6_CS12 0xA1\n#define SW6_CS13 0xA2\n#define SW6_CS14 0xA3\n#define SW6_CS15 0xA4\n#define SW6_CS16 0xA5\n#define SW6_CS17 0xA6\n#define SW6_CS18 0xA7\n#define SW6_CS19 0xA8\n#define SW6_CS20 0xA9\n#define SW6_CS21 0xAA\n#define SW6_CS22 0xAB\n#define SW6_CS23 0xAC\n#define SW6_CS24 0xAD\n#define SW6_CS25 0xAE\n#define SW6_CS26 0xAF\n#define SW6_CS27 0xB0\n#define SW6_CS28 0xB1\n#define SW6_CS29 0xB2\n#define SW6_CS30 0xB3\n"
  },
  {
    "path": "drivers/led/issi/is31fl3742a.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3742a.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3742A_PWM_REGISTER_COUNT 180\n#define IS31FL3742A_SCALING_REGISTER_COUNT 180\n\n#ifndef IS31FL3742A_I2C_TIMEOUT\n#    define IS31FL3742A_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3742A_I2C_PERSISTENCE\n#    define IS31FL3742A_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3742A_CONFIGURATION\n#    define IS31FL3742A_CONFIGURATION 0x31\n#endif\n\n#ifndef IS31FL3742A_PWM_FREQUENCY\n#    define IS31FL3742A_PWM_FREQUENCY IS31FL3742A_PWM_FREQUENCY_29K_HZ\n#endif\n\n#ifndef IS31FL3742A_SW_PULLDOWN\n#    define IS31FL3742A_SW_PULLDOWN IS31FL3742A_PDR_8K_OHM\n#endif\n\n#ifndef IS31FL3742A_CS_PULLUP\n#    define IS31FL3742A_CS_PULLUP IS31FL3742A_PUR_8K_OHM\n#endif\n\n#ifndef IS31FL3742A_GLOBAL_CURRENT\n#    define IS31FL3742A_GLOBAL_CURRENT 0xFF\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3742A_DRIVER_COUNT] = {\n    IS31FL3742A_I2C_ADDRESS_1,\n#ifdef IS31FL3742A_I2C_ADDRESS_2\n    IS31FL3742A_I2C_ADDRESS_2,\n#    ifdef IS31FL3742A_I2C_ADDRESS_3\n    IS31FL3742A_I2C_ADDRESS_3,\n#        ifdef IS31FL3742A_I2C_ADDRESS_4\n    IS31FL3742A_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\ntypedef struct is31fl3742a_driver_t {\n    uint8_t pwm_buffer[IS31FL3742A_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer[IS31FL3742A_SCALING_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3742a_driver_t;\n\nis31fl3742a_driver_t driver_buffers[IS31FL3742A_DRIVER_COUNT] = {{\n    .pwm_buffer           = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer       = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3742A_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3742A_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3742A_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3742a_select_page(uint8_t index, uint8_t page) {\n    is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND_WRITE_LOCK, IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3742a_write_register(index, IS31FL3742A_REG_COMMAND, page);\n}\n\nvoid is31fl3742a_write_pwm_buffer(uint8_t index) {\n    // Assumes page 0 is already selected.\n    // Transmit PWM registers in 6 transfers of 30 bytes.\n\n    // Iterate over the pwm_buffer contents at 30 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i += 30) {\n#if IS31FL3742A_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3742A_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 30, IS31FL3742A_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3742a_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3742A_SDB_PIN)\n    gpio_set_pin_output(IS31FL3742A_SDB_PIN);\n    gpio_write_pin_high(IS31FL3742A_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {\n        is31fl3742a_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) {\n        is31fl3742a_set_scaling_register(i, 0xFF, 0xFF, 0xFF);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {\n        is31fl3742a_update_scaling_registers(i);\n    }\n}\n\nvoid is31fl3742a_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) {\n        is31fl3742a_write_register(index, i, 0x00);\n    }\n\n    is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM);\n\n    for (uint8_t i = 0; i < IS31FL3742A_PWM_REGISTER_COUNT; i++) {\n        is31fl3742a_write_register(index, i, 0x00);\n    }\n\n    is31fl3742a_select_page(index, IS31FL3742A_COMMAND_FUNCTION);\n\n    is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PULLDOWNUP, (IS31FL3742A_SW_PULLDOWN << 4) | IS31FL3742A_CS_PULLUP);\n    is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3742A_GLOBAL_CURRENT);\n    is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY, (IS31FL3742A_PWM_FREQUENCY & 0b0111));\n    is31fl3742a_write_register(index, IS31FL3742A_FUNCTION_REG_CONFIGURATION, IS31FL3742A_CONFIGURATION);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3742a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3742a_led_t led;\n\n    if (index >= 0 && index < IS31FL3742A_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.r] = red;\n        driver_buffers[led.driver].pwm_buffer[led.g] = green;\n        driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3742a_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < IS31FL3742A_LED_COUNT; i++) {\n        is31fl3742a_set_color(i, red, green, blue);\n    }\n}\n\nvoid is31fl3742a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3742a_led_t led;\n    memcpy_P(&led, (&g_is31fl3742a_leds[index]), sizeof(led));\n\n    driver_buffers[led.driver].scaling_buffer[led.r] = red;\n    driver_buffers[led.driver].scaling_buffer[led.g] = green;\n    driver_buffers[led.driver].scaling_buffer[led.b] = blue;\n    driver_buffers[led.driver].scaling_buffer_dirty  = true;\n}\n\nvoid is31fl3742a_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3742a_select_page(index, IS31FL3742A_COMMAND_PWM);\n\n        is31fl3742a_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3742a_update_scaling_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        is31fl3742a_select_page(index, IS31FL3742A_COMMAND_SCALING);\n\n        for (uint8_t i = 0; i < IS31FL3742A_SCALING_REGISTER_COUNT; i++) {\n            is31fl3742a_write_register(index, i, driver_buffers[index].scaling_buffer[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3742a_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3742A_DRIVER_COUNT; i++) {\n        is31fl3742a_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3742a.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3742A_REG_INTERRUPT_MASK 0xF0\n#define IS31FL3742A_REG_INTERRUPT_STATUS 0xF1\n#define IS31FL3742A_REG_ID 0xFC\n\n#define IS31FL3742A_REG_COMMAND 0xFD\n\n#define IS31FL3742A_COMMAND_PWM 0x00\n#define IS31FL3742A_COMMAND_SCALING 0x02\n#define IS31FL3742A_COMMAND_FUNCTION 0x04\n\n#define IS31FL3742A_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3742A_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3742A_FUNCTION_REG_PULLDOWNUP 0x02\n#define IS31FL3742A_FUNCTION_REG_PWM_FREQUENCY 0x36\n#define IS31FL3742A_FUNCTION_REG_RESET 0x3F\n#define IS31FL3742A_FUNCTION_REG_SPREAD_SPECTRUM 0x41\n\n#define IS31FL3742A_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3742A_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3742A_I2C_ADDRESS_GND 0x30\n#define IS31FL3742A_I2C_ADDRESS_SCL 0x31\n#define IS31FL3742A_I2C_ADDRESS_SDA 0x32\n#define IS31FL3742A_I2C_ADDRESS_VCC 0x33\n\n#if defined(RGB_MATRIX_IS31FL3742A)\n#    define IS31FL3742A_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3742A_I2C_ADDRESS_4)\n#    define IS31FL3742A_DRIVER_COUNT 4\n#elif defined(IS31FL3742A_I2C_ADDRESS_3)\n#    define IS31FL3742A_DRIVER_COUNT 3\n#elif defined(IS31FL3742A_I2C_ADDRESS_2)\n#    define IS31FL3742A_DRIVER_COUNT 2\n#elif defined(IS31FL3742A_I2C_ADDRESS_1)\n#    define IS31FL3742A_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3742a_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED is31fl3742a_led_t;\n\nextern const is31fl3742a_led_t PROGMEM g_is31fl3742a_leds[IS31FL3742A_LED_COUNT];\n\nvoid is31fl3742a_init_drivers(void);\nvoid is31fl3742a_init(uint8_t index);\nvoid is31fl3742a_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3742a_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3742a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid is31fl3742a_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3742a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3742a_update_pwm_buffers(uint8_t index);\nvoid is31fl3742a_update_scaling_registers(uint8_t index);\n\nvoid is31fl3742a_flush(void);\n\n#define IS31FL3742A_PDR_0_OHM 0b000   // No pull-down resistor\n#define IS31FL3742A_PDR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3742A_PDR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3742A_PDR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3742A_PDR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3742A_PDR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3742A_PDR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3742A_PDR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3742A_PUR_0_OHM 0b000   // No pull-up resistor\n#define IS31FL3742A_PUR_0K5_OHM 0b001 // 0.5 kOhm resistor\n#define IS31FL3742A_PUR_1K_OHM 0b010  // 1 kOhm resistor\n#define IS31FL3742A_PUR_2K_OHM 0b011  // 2 kOhm resistor\n#define IS31FL3742A_PUR_4K_OHM 0b100  // 4 kOhm resistor\n#define IS31FL3742A_PUR_8K_OHM 0b101  // 8 kOhm resistor\n#define IS31FL3742A_PUR_16K_OHM 0b110 // 16 kOhm resistor\n#define IS31FL3742A_PUR_32K_OHM 0b111 // 32 kOhm resistor\n\n#define IS31FL3742A_PWM_FREQUENCY_29K_HZ 0b0000\n#define IS31FL3742A_PWM_FREQUENCY_3K6_HZ 0b0011\n#define IS31FL3742A_PWM_FREQUENCY_1K8_HZ 0b0111\n#define IS31FL3742A_PWM_FREQUENCY_900_HZ 0b1011\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n#define SW1_CS17 0x10\n#define SW1_CS18 0x11\n#define SW1_CS19 0x12\n#define SW1_CS20 0x13\n#define SW1_CS21 0x14\n#define SW1_CS22 0x15\n#define SW1_CS23 0x16\n#define SW1_CS24 0x17\n#define SW1_CS25 0x18\n#define SW1_CS26 0x19\n#define SW1_CS27 0x1A\n#define SW1_CS28 0x1B\n#define SW1_CS29 0x1C\n#define SW1_CS30 0x1D\n\n#define SW2_CS1 0x1E\n#define SW2_CS2 0x1F\n#define SW2_CS3 0x20\n#define SW2_CS4 0x21\n#define SW2_CS5 0x22\n#define SW2_CS6 0x23\n#define SW2_CS7 0x24\n#define SW2_CS8 0x25\n#define SW2_CS9 0x26\n#define SW2_CS10 0x27\n#define SW2_CS11 0x28\n#define SW2_CS12 0x29\n#define SW2_CS13 0x2A\n#define SW2_CS14 0x2B\n#define SW2_CS15 0x2C\n#define SW2_CS16 0x2D\n#define SW2_CS17 0x2E\n#define SW2_CS18 0x2F\n#define SW2_CS19 0x30\n#define SW2_CS20 0x31\n#define SW2_CS21 0x32\n#define SW2_CS22 0x33\n#define SW2_CS23 0x34\n#define SW2_CS24 0x35\n#define SW2_CS25 0x36\n#define SW2_CS26 0x37\n#define SW2_CS27 0x38\n#define SW2_CS28 0x39\n#define SW2_CS29 0x3A\n#define SW2_CS30 0x3B\n\n#define SW3_CS1 0x3C\n#define SW3_CS2 0x3D\n#define SW3_CS3 0x3E\n#define SW3_CS4 0x3F\n#define SW3_CS5 0x40\n#define SW3_CS6 0x41\n#define SW3_CS7 0x42\n#define SW3_CS8 0x43\n#define SW3_CS9 0x44\n#define SW3_CS10 0x45\n#define SW3_CS11 0x46\n#define SW3_CS12 0x47\n#define SW3_CS13 0x48\n#define SW3_CS14 0x49\n#define SW3_CS15 0x4A\n#define SW3_CS16 0x4B\n#define SW3_CS17 0x4C\n#define SW3_CS18 0x4D\n#define SW3_CS19 0x4E\n#define SW3_CS20 0x4F\n#define SW3_CS21 0x50\n#define SW3_CS22 0x51\n#define SW3_CS23 0x52\n#define SW3_CS24 0x53\n#define SW3_CS25 0x54\n#define SW3_CS26 0x55\n#define SW3_CS27 0x56\n#define SW3_CS28 0x57\n#define SW3_CS29 0x58\n#define SW3_CS30 0x59\n\n#define SW4_CS1 0x5A\n#define SW4_CS2 0x5B\n#define SW4_CS3 0x5C\n#define SW4_CS4 0x5D\n#define SW4_CS5 0x5E\n#define SW4_CS6 0x5F\n#define SW4_CS7 0x60\n#define SW4_CS8 0x61\n#define SW4_CS9 0x62\n#define SW4_CS10 0x63\n#define SW4_CS11 0x64\n#define SW4_CS12 0x65\n#define SW4_CS13 0x66\n#define SW4_CS14 0x67\n#define SW4_CS15 0x68\n#define SW4_CS16 0x69\n#define SW4_CS17 0x6A\n#define SW4_CS18 0x6B\n#define SW4_CS19 0x6C\n#define SW4_CS20 0x6D\n#define SW4_CS21 0x6E\n#define SW4_CS22 0x6F\n#define SW4_CS23 0x70\n#define SW4_CS24 0x71\n#define SW4_CS25 0x72\n#define SW4_CS26 0x73\n#define SW4_CS27 0x74\n#define SW4_CS28 0x75\n#define SW4_CS29 0x76\n#define SW4_CS30 0x77\n\n#define SW5_CS1 0x78\n#define SW5_CS2 0x79\n#define SW5_CS3 0x7A\n#define SW5_CS4 0x7B\n#define SW5_CS5 0x7C\n#define SW5_CS6 0x7D\n#define SW5_CS7 0x7E\n#define SW5_CS8 0x7F\n#define SW5_CS9 0x80\n#define SW5_CS10 0x81\n#define SW5_CS11 0x82\n#define SW5_CS12 0x83\n#define SW5_CS13 0x84\n#define SW5_CS14 0x85\n#define SW5_CS15 0x86\n#define SW5_CS16 0x87\n#define SW5_CS17 0x88\n#define SW5_CS18 0x89\n#define SW5_CS19 0x8A\n#define SW5_CS20 0x8B\n#define SW5_CS21 0x8C\n#define SW5_CS22 0x8D\n#define SW5_CS23 0x8E\n#define SW5_CS24 0x8F\n#define SW5_CS25 0x90\n#define SW5_CS26 0x91\n#define SW5_CS27 0x92\n#define SW5_CS28 0x93\n#define SW5_CS29 0x94\n#define SW5_CS30 0x95\n\n#define SW6_CS1 0x96\n#define SW6_CS2 0x97\n#define SW6_CS3 0x98\n#define SW6_CS4 0x99\n#define SW6_CS5 0x9A\n#define SW6_CS6 0x9B\n#define SW6_CS7 0x9C\n#define SW6_CS8 0x9D\n#define SW6_CS9 0x9E\n#define SW6_CS10 0x9F\n#define SW6_CS11 0xA0\n#define SW6_CS12 0xA1\n#define SW6_CS13 0xA2\n#define SW6_CS14 0xA3\n#define SW6_CS15 0xA4\n#define SW6_CS16 0xA5\n#define SW6_CS17 0xA6\n#define SW6_CS18 0xA7\n#define SW6_CS19 0xA8\n#define SW6_CS20 0xA9\n#define SW6_CS21 0xAA\n#define SW6_CS22 0xAB\n#define SW6_CS23 0xAC\n#define SW6_CS24 0xAD\n#define SW6_CS25 0xAE\n#define SW6_CS26 0xAF\n#define SW6_CS27 0xB0\n#define SW6_CS28 0xB1\n#define SW6_CS29 0xB2\n#define SW6_CS30 0xB3\n"
  },
  {
    "path": "drivers/led/issi/is31fl3743a-mono.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3743a-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3743A_PWM_REGISTER_COUNT 198\n#define IS31FL3743A_SCALING_REGISTER_COUNT 198\n\n#ifndef IS31FL3743A_I2C_TIMEOUT\n#    define IS31FL3743A_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3743A_I2C_PERSISTENCE\n#    define IS31FL3743A_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3743A_CONFIGURATION\n#    define IS31FL3743A_CONFIGURATION 0x01\n#endif\n\n#ifndef IS31FL3743A_SW_PULLDOWN\n#    define IS31FL3743A_SW_PULLDOWN IS31FL3743A_PDR_2K_OHM_SW_OFF\n#endif\n\n#ifndef IS31FL3743A_CS_PULLUP\n#    define IS31FL3743A_CS_PULLUP IS31FL3743A_PUR_2K_OHM_CS_OFF\n#endif\n\n#ifndef IS31FL3743A_GLOBAL_CURRENT\n#    define IS31FL3743A_GLOBAL_CURRENT 0xFF\n#endif\n\n#ifndef IS31FL3743A_SYNC_1\n#    define IS31FL3743A_SYNC_1 IS31FL3743A_SYNC_NONE\n#endif\n#ifndef IS31FL3743A_SYNC_2\n#    define IS31FL3743A_SYNC_2 IS31FL3743A_SYNC_NONE\n#endif\n#ifndef IS31FL3743A_SYNC_3\n#    define IS31FL3743A_SYNC_3 IS31FL3743A_SYNC_NONE\n#endif\n#ifndef IS31FL3743A_SYNC_4\n#    define IS31FL3743A_SYNC_4 IS31FL3743A_SYNC_NONE\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3743A_DRIVER_COUNT] = {\n    IS31FL3743A_I2C_ADDRESS_1,\n#ifdef IS31FL3743A_I2C_ADDRESS_2\n    IS31FL3743A_I2C_ADDRESS_2,\n#    ifdef IS31FL3743A_I2C_ADDRESS_3\n    IS31FL3743A_I2C_ADDRESS_3,\n#        ifdef IS31FL3743A_I2C_ADDRESS_4\n    IS31FL3743A_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\nconst uint8_t driver_sync[IS31FL3743A_DRIVER_COUNT] = {\n    IS31FL3743A_SYNC_1,\n#ifdef IS31FL3743A_I2C_ADDRESS_2\n    IS31FL3743A_SYNC_2,\n#    ifdef IS31FL3743A_I2C_ADDRESS_3\n    IS31FL3743A_SYNC_3,\n#        ifdef IS31FL3743A_I2C_ADDRESS_4\n    IS31FL3743A_SYNC_4,\n#        endif\n#    endif\n#endif\n};\n\ntypedef struct is31fl3743a_driver_t {\n    uint8_t pwm_buffer[IS31FL3743A_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer[IS31FL3743A_SCALING_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3743a_driver_t;\n\nis31fl3743a_driver_t driver_buffers[IS31FL3743A_DRIVER_COUNT] = {{\n    .pwm_buffer           = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer       = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3743A_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3743A_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3743a_select_page(uint8_t index, uint8_t page) {\n    is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND_WRITE_LOCK, IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND, page);\n}\n\nvoid is31fl3743a_write_pwm_buffer(uint8_t index) {\n    // Assumes page 0 is already selected.\n    // Transmit PWM registers in 11 transfers of 18 bytes.\n\n    // Iterate over the pwm_buffer contents at 18 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i += 18) {\n#if IS31FL3743A_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3743A_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3743a_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3743A_SDB_PIN)\n    gpio_set_pin_output(IS31FL3743A_SDB_PIN);\n    gpio_write_pin_high(IS31FL3743A_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {\n        is31fl3743a_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) {\n        is31fl3743a_set_scaling_register(i, 0xFF);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {\n        is31fl3743a_update_scaling_registers(i);\n    }\n}\n\nvoid is31fl3743a_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) {\n        is31fl3743a_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM);\n\n    for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i++) {\n        is31fl3743a_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3743a_select_page(index, IS31FL3743A_COMMAND_FUNCTION);\n\n    uint8_t sync = driver_sync[index];\n\n    is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_PULLDOWNUP, (IS31FL3743A_SW_PULLDOWN << 4) | IS31FL3743A_CS_PULLUP);\n    is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3743A_GLOBAL_CURRENT);\n    is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6);\n    is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_CONFIGURATION, IS31FL3743A_CONFIGURATION);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3743a_set_value(int index, uint8_t value) {\n    is31fl3743a_led_t led;\n\n    if (index >= 0 && index < IS31FL3743A_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.v] = value;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3743a_set_value_all(uint8_t value) {\n    for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) {\n        is31fl3743a_set_value(i, value);\n    }\n}\n\nvoid is31fl3743a_set_scaling_register(uint8_t index, uint8_t value) {\n    is31fl3743a_led_t led;\n    memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led));\n\n    driver_buffers[led.driver].scaling_buffer[led.v] = value;\n    driver_buffers[led.driver].scaling_buffer_dirty  = true;\n}\n\nvoid is31fl3743a_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM);\n\n        is31fl3743a_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3743a_update_scaling_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING);\n\n        for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) {\n            is31fl3743a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3743a_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {\n        is31fl3743a_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3743a-mono.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3743A_REG_ID 0xFC\n\n#define IS31FL3743A_REG_COMMAND 0xFD\n\n#define IS31FL3743A_COMMAND_PWM 0x00\n#define IS31FL3743A_COMMAND_SCALING 0x01\n#define IS31FL3743A_COMMAND_FUNCTION 0x02\n\n#define IS31FL3743A_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3743A_FUNCTION_REG_PULLDOWNUP 0x02\n#define IS31FL3743A_FUNCTION_REG_TEMPERATURE 0x24\n#define IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM 0x25\n#define IS31FL3743A_FUNCTION_REG_RESET 0x2F\n\n#define IS31FL3743A_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3743A_I2C_ADDRESS_GND_GND 0x20\n#define IS31FL3743A_I2C_ADDRESS_GND_SCL 0x21\n#define IS31FL3743A_I2C_ADDRESS_GND_SDA 0x22\n#define IS31FL3743A_I2C_ADDRESS_GND_VCC 0x23\n#define IS31FL3743A_I2C_ADDRESS_SCL_GND 0x24\n#define IS31FL3743A_I2C_ADDRESS_SCL_SCL 0x25\n#define IS31FL3743A_I2C_ADDRESS_SCL_SDA 0x26\n#define IS31FL3743A_I2C_ADDRESS_SCL_VCC 0x27\n#define IS31FL3743A_I2C_ADDRESS_SDA_GND 0x28\n#define IS31FL3743A_I2C_ADDRESS_SDA_SCL 0x29\n#define IS31FL3743A_I2C_ADDRESS_SDA_SDA 0x2A\n#define IS31FL3743A_I2C_ADDRESS_SDA_VCC 0x2B\n#define IS31FL3743A_I2C_ADDRESS_VCC_GND 0x2C\n#define IS31FL3743A_I2C_ADDRESS_VCC_SCL 0x2D\n#define IS31FL3743A_I2C_ADDRESS_VCC_SDA 0x2E\n#define IS31FL3743A_I2C_ADDRESS_VCC_VCC 0x2F\n\n#if defined(LED_MATRIX_IS31FL3743A)\n#    define IS31FL3743A_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3743A_I2C_ADDRESS_4)\n#    define IS31FL3743A_DRIVER_COUNT 4\n#elif defined(IS31FL3743A_I2C_ADDRESS_3)\n#    define IS31FL3743A_DRIVER_COUNT 3\n#elif defined(IS31FL3743A_I2C_ADDRESS_2)\n#    define IS31FL3743A_DRIVER_COUNT 2\n#elif defined(IS31FL3743A_I2C_ADDRESS_1)\n#    define IS31FL3743A_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3743a_led_t {\n    uint8_t driver : 2;\n    uint8_t v;\n} PACKED is31fl3743a_led_t;\n\nextern const is31fl3743a_led_t PROGMEM g_is31fl3743a_leds[IS31FL3743A_LED_COUNT];\n\nvoid is31fl3743a_init_drivers(void);\nvoid is31fl3743a_init(uint8_t index);\nvoid is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3743a_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3743a_set_value(int index, uint8_t value);\nvoid is31fl3743a_set_value_all(uint8_t value);\n\nvoid is31fl3743a_set_scaling_register(uint8_t index, uint8_t value);\n\nvoid is31fl3743a_update_pwm_buffers(uint8_t index);\nvoid is31fl3743a_update_scaling_registers(uint8_t index);\n\nvoid is31fl3743a_flush(void);\n\n#define IS31FL3743A_PDR_0_OHM 0b000          // No pull-down resistor\n#define IS31FL3743A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time\n#define IS31FL3743A_PDR_1K_OHM_SW_OFF 0b010  // 1 kOhm resistor in SWx off time\n#define IS31FL3743A_PDR_2K_OHM_SW_OFF 0b011  // 2 kOhm resistor in SWx off time\n#define IS31FL3743A_PDR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3743A_PDR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3743A_PDR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3743A_PDR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3743A_PUR_0_OHM 0b000          // No pull-up resistor\n#define IS31FL3743A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time\n#define IS31FL3743A_PUR_1K_OHM_CS_OFF 0b010  // 1 kOhm resistor in CSy off time\n#define IS31FL3743A_PUR_2K_OHM_CS_OFF 0b011  // 2 kOhm resistor in CSy off time\n#define IS31FL3743A_PUR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3743A_PUR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3743A_PUR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3743A_PUR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3743A_SYNC_NONE 0b00\n#define IS31FL3743A_SYNC_SLAVE 0b10\n#define IS31FL3743A_SYNC_MASTER 0b11\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n#define SW1_CS17 0x10\n#define SW1_CS18 0x11\n\n#define SW2_CS1 0x12\n#define SW2_CS2 0x13\n#define SW2_CS3 0x14\n#define SW2_CS4 0x15\n#define SW2_CS5 0x16\n#define SW2_CS6 0x17\n#define SW2_CS7 0x18\n#define SW2_CS8 0x19\n#define SW2_CS9 0x1A\n#define SW2_CS10 0x1B\n#define SW2_CS11 0x1C\n#define SW2_CS12 0x1D\n#define SW2_CS13 0x1E\n#define SW2_CS14 0x1F\n#define SW2_CS15 0x20\n#define SW2_CS16 0x21\n#define SW2_CS17 0x22\n#define SW2_CS18 0x23\n\n#define SW3_CS1 0x24\n#define SW3_CS2 0x25\n#define SW3_CS3 0x26\n#define SW3_CS4 0x27\n#define SW3_CS5 0x28\n#define SW3_CS6 0x29\n#define SW3_CS7 0x2A\n#define SW3_CS8 0x2B\n#define SW3_CS9 0x2C\n#define SW3_CS10 0x2D\n#define SW3_CS11 0x2E\n#define SW3_CS12 0x2F\n#define SW3_CS13 0x30\n#define SW3_CS14 0x31\n#define SW3_CS15 0x32\n#define SW3_CS16 0x33\n#define SW3_CS17 0x34\n#define SW3_CS18 0x35\n\n#define SW4_CS1 0x36\n#define SW4_CS2 0x37\n#define SW4_CS3 0x38\n#define SW4_CS4 0x39\n#define SW4_CS5 0x3A\n#define SW4_CS6 0x3B\n#define SW4_CS7 0x3C\n#define SW4_CS8 0x3D\n#define SW4_CS9 0x3E\n#define SW4_CS10 0x3F\n#define SW4_CS11 0x40\n#define SW4_CS12 0x41\n#define SW4_CS13 0x42\n#define SW4_CS14 0x43\n#define SW4_CS15 0x44\n#define SW4_CS16 0x45\n#define SW4_CS17 0x46\n#define SW4_CS18 0x47\n\n#define SW5_CS1 0x48\n#define SW5_CS2 0x49\n#define SW5_CS3 0x4A\n#define SW5_CS4 0x4B\n#define SW5_CS5 0x4C\n#define SW5_CS6 0x4D\n#define SW5_CS7 0x4E\n#define SW5_CS8 0x4F\n#define SW5_CS9 0x50\n#define SW5_CS10 0x51\n#define SW5_CS11 0x52\n#define SW5_CS12 0x53\n#define SW5_CS13 0x54\n#define SW5_CS14 0x55\n#define SW5_CS15 0x56\n#define SW5_CS16 0x57\n#define SW5_CS17 0x58\n#define SW5_CS18 0x59\n\n#define SW6_CS1 0x5A\n#define SW6_CS2 0x5B\n#define SW6_CS3 0x5C\n#define SW6_CS4 0x5D\n#define SW6_CS5 0x5E\n#define SW6_CS6 0x5F\n#define SW6_CS7 0x60\n#define SW6_CS8 0x61\n#define SW6_CS9 0x62\n#define SW6_CS10 0x63\n#define SW6_CS11 0x64\n#define SW6_CS12 0x65\n#define SW6_CS13 0x66\n#define SW6_CS14 0x67\n#define SW6_CS15 0x68\n#define SW6_CS16 0x69\n#define SW6_CS17 0x6A\n#define SW6_CS18 0x6B\n\n#define SW7_CS1 0x6C\n#define SW7_CS2 0x6D\n#define SW7_CS3 0x6E\n#define SW7_CS4 0x6F\n#define SW7_CS5 0x70\n#define SW7_CS6 0x71\n#define SW7_CS7 0x72\n#define SW7_CS8 0x73\n#define SW7_CS9 0x74\n#define SW7_CS10 0x75\n#define SW7_CS11 0x76\n#define SW7_CS12 0x77\n#define SW7_CS13 0x78\n#define SW7_CS14 0x79\n#define SW7_CS15 0x7A\n#define SW7_CS16 0x7B\n#define SW7_CS17 0x7C\n#define SW7_CS18 0x7D\n\n#define SW8_CS1 0x7E\n#define SW8_CS2 0x7F\n#define SW8_CS3 0x80\n#define SW8_CS4 0x81\n#define SW8_CS5 0x82\n#define SW8_CS6 0x83\n#define SW8_CS7 0x84\n#define SW8_CS8 0x85\n#define SW8_CS9 0x86\n#define SW8_CS10 0x87\n#define SW8_CS11 0x88\n#define SW8_CS12 0x89\n#define SW8_CS13 0x8A\n#define SW8_CS14 0x8B\n#define SW8_CS15 0x8C\n#define SW8_CS16 0x8D\n#define SW8_CS17 0x8E\n#define SW8_CS18 0x8F\n\n#define SW9_CS1 0x90\n#define SW9_CS2 0x91\n#define SW9_CS3 0x92\n#define SW9_CS4 0x93\n#define SW9_CS5 0x94\n#define SW9_CS6 0x95\n#define SW9_CS7 0x96\n#define SW9_CS8 0x97\n#define SW9_CS9 0x98\n#define SW9_CS10 0x99\n#define SW9_CS11 0x9A\n#define SW9_CS12 0x9B\n#define SW9_CS13 0x9C\n#define SW9_CS14 0x9D\n#define SW9_CS15 0x9E\n#define SW9_CS16 0x9F\n#define SW9_CS17 0xA0\n#define SW9_CS18 0xA1\n\n#define SW10_CS1 0xA2\n#define SW10_CS2 0xA3\n#define SW10_CS3 0xA4\n#define SW10_CS4 0xA5\n#define SW10_CS5 0xA6\n#define SW10_CS6 0xA7\n#define SW10_CS7 0xA8\n#define SW10_CS8 0xA9\n#define SW10_CS9 0xAA\n#define SW10_CS10 0xAB\n#define SW10_CS11 0xAC\n#define SW10_CS12 0xAD\n#define SW10_CS13 0xAE\n#define SW10_CS14 0xAF\n#define SW10_CS15 0xB0\n#define SW10_CS16 0xB1\n#define SW10_CS17 0xB2\n#define SW10_CS18 0xB3\n\n#define SW11_CS1 0xB4\n#define SW11_CS2 0xB5\n#define SW11_CS3 0xB6\n#define SW11_CS4 0xB7\n#define SW11_CS5 0xB8\n#define SW11_CS6 0xB9\n#define SW11_CS7 0xBA\n#define SW11_CS8 0xBB\n#define SW11_CS9 0xBC\n#define SW11_CS10 0xBD\n#define SW11_CS11 0xBE\n#define SW11_CS12 0xBF\n#define SW11_CS13 0xC0\n#define SW11_CS14 0xC1\n#define SW11_CS15 0xC2\n#define SW11_CS16 0xC3\n#define SW11_CS17 0xC4\n#define SW11_CS18 0xC5\n"
  },
  {
    "path": "drivers/led/issi/is31fl3743a.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3743a.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3743A_PWM_REGISTER_COUNT 198\n#define IS31FL3743A_SCALING_REGISTER_COUNT 198\n\n#ifndef IS31FL3743A_I2C_TIMEOUT\n#    define IS31FL3743A_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3743A_I2C_PERSISTENCE\n#    define IS31FL3743A_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3743A_CONFIGURATION\n#    define IS31FL3743A_CONFIGURATION 0x01\n#endif\n\n#ifndef IS31FL3743A_SW_PULLDOWN\n#    define IS31FL3743A_SW_PULLDOWN IS31FL3743A_PDR_2K_OHM_SW_OFF\n#endif\n\n#ifndef IS31FL3743A_CS_PULLUP\n#    define IS31FL3743A_CS_PULLUP IS31FL3743A_PUR_2K_OHM_CS_OFF\n#endif\n\n#ifndef IS31FL3743A_GLOBAL_CURRENT\n#    define IS31FL3743A_GLOBAL_CURRENT 0xFF\n#endif\n\n#ifndef IS31FL3743A_SYNC_1\n#    define IS31FL3743A_SYNC_1 IS31FL3743A_SYNC_NONE\n#endif\n#ifndef IS31FL3743A_SYNC_2\n#    define IS31FL3743A_SYNC_2 IS31FL3743A_SYNC_NONE\n#endif\n#ifndef IS31FL3743A_SYNC_3\n#    define IS31FL3743A_SYNC_3 IS31FL3743A_SYNC_NONE\n#endif\n#ifndef IS31FL3743A_SYNC_4\n#    define IS31FL3743A_SYNC_4 IS31FL3743A_SYNC_NONE\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3743A_DRIVER_COUNT] = {\n    IS31FL3743A_I2C_ADDRESS_1,\n#ifdef IS31FL3743A_I2C_ADDRESS_2\n    IS31FL3743A_I2C_ADDRESS_2,\n#    ifdef IS31FL3743A_I2C_ADDRESS_3\n    IS31FL3743A_I2C_ADDRESS_3,\n#        ifdef IS31FL3743A_I2C_ADDRESS_4\n    IS31FL3743A_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\nconst uint8_t driver_sync[IS31FL3743A_DRIVER_COUNT] = {\n    IS31FL3743A_SYNC_1,\n#ifdef IS31FL3743A_I2C_ADDRESS_2\n    IS31FL3743A_SYNC_2,\n#    ifdef IS31FL3743A_I2C_ADDRESS_3\n    IS31FL3743A_SYNC_3,\n#        ifdef IS31FL3743A_I2C_ADDRESS_4\n    IS31FL3743A_SYNC_4,\n#        endif\n#    endif\n#endif\n};\n\ntypedef struct is31fl3743a_driver_t {\n    uint8_t pwm_buffer[IS31FL3743A_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer[IS31FL3743A_SCALING_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3743a_driver_t;\n\nis31fl3743a_driver_t driver_buffers[IS31FL3743A_DRIVER_COUNT] = {{\n    .pwm_buffer           = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer       = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3743A_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3743A_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3743A_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3743a_select_page(uint8_t index, uint8_t page) {\n    is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND_WRITE_LOCK, IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3743a_write_register(index, IS31FL3743A_REG_COMMAND, page);\n}\n\nvoid is31fl3743a_write_pwm_buffer(uint8_t index) {\n    // Assumes page 0 is already selected.\n    // Transmit PWM registers in 11 transfers of 18 bytes.\n\n    // Iterate over the pwm_buffer contents at 18 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i += 18) {\n#if IS31FL3743A_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3743A_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3743A_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3743a_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3743A_SDB_PIN)\n    gpio_set_pin_output(IS31FL3743A_SDB_PIN);\n    gpio_write_pin_high(IS31FL3743A_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {\n        is31fl3743a_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) {\n        is31fl3743a_set_scaling_register(i, 0xFF, 0xFF, 0xFF);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {\n        is31fl3743a_update_scaling_registers(i);\n    }\n}\n\nvoid is31fl3743a_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) {\n        is31fl3743a_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM);\n\n    for (uint8_t i = 0; i < IS31FL3743A_PWM_REGISTER_COUNT; i++) {\n        is31fl3743a_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3743a_select_page(index, IS31FL3743A_COMMAND_FUNCTION);\n\n    uint8_t sync = driver_sync[index];\n\n    is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_PULLDOWNUP, (IS31FL3743A_SW_PULLDOWN << 4) | IS31FL3743A_CS_PULLUP);\n    is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3743A_GLOBAL_CURRENT);\n    is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6);\n    is31fl3743a_write_register(index, IS31FL3743A_FUNCTION_REG_CONFIGURATION, IS31FL3743A_CONFIGURATION);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3743a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3743a_led_t led;\n\n    if (index >= 0 && index < IS31FL3743A_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.r] = red;\n        driver_buffers[led.driver].pwm_buffer[led.g] = green;\n        driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3743a_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < IS31FL3743A_LED_COUNT; i++) {\n        is31fl3743a_set_color(i, red, green, blue);\n    }\n}\n\nvoid is31fl3743a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3743a_led_t led;\n    memcpy_P(&led, (&g_is31fl3743a_leds[index]), sizeof(led));\n\n    driver_buffers[led.driver].scaling_buffer[led.r] = red;\n    driver_buffers[led.driver].scaling_buffer[led.g] = green;\n    driver_buffers[led.driver].scaling_buffer[led.b] = blue;\n    driver_buffers[led.driver].scaling_buffer_dirty  = true;\n}\n\nvoid is31fl3743a_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3743a_select_page(index, IS31FL3743A_COMMAND_PWM);\n\n        is31fl3743a_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3743a_update_scaling_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        is31fl3743a_select_page(index, IS31FL3743A_COMMAND_SCALING);\n\n        for (uint8_t i = 0; i < IS31FL3743A_SCALING_REGISTER_COUNT; i++) {\n            is31fl3743a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3743a_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3743A_DRIVER_COUNT; i++) {\n        is31fl3743a_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3743a.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3743A_REG_ID 0xFC\n\n#define IS31FL3743A_REG_COMMAND 0xFD\n\n#define IS31FL3743A_COMMAND_PWM 0x00\n#define IS31FL3743A_COMMAND_SCALING 0x01\n#define IS31FL3743A_COMMAND_FUNCTION 0x02\n\n#define IS31FL3743A_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3743A_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3743A_FUNCTION_REG_PULLDOWNUP 0x02\n#define IS31FL3743A_FUNCTION_REG_TEMPERATURE 0x24\n#define IS31FL3743A_FUNCTION_REG_SPREAD_SPECTRUM 0x25\n#define IS31FL3743A_FUNCTION_REG_RESET 0x2F\n\n#define IS31FL3743A_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3743A_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3743A_I2C_ADDRESS_GND_GND 0x20\n#define IS31FL3743A_I2C_ADDRESS_GND_SCL 0x21\n#define IS31FL3743A_I2C_ADDRESS_GND_SDA 0x22\n#define IS31FL3743A_I2C_ADDRESS_GND_VCC 0x23\n#define IS31FL3743A_I2C_ADDRESS_SCL_GND 0x24\n#define IS31FL3743A_I2C_ADDRESS_SCL_SCL 0x25\n#define IS31FL3743A_I2C_ADDRESS_SCL_SDA 0x26\n#define IS31FL3743A_I2C_ADDRESS_SCL_VCC 0x27\n#define IS31FL3743A_I2C_ADDRESS_SDA_GND 0x28\n#define IS31FL3743A_I2C_ADDRESS_SDA_SCL 0x29\n#define IS31FL3743A_I2C_ADDRESS_SDA_SDA 0x2A\n#define IS31FL3743A_I2C_ADDRESS_SDA_VCC 0x2B\n#define IS31FL3743A_I2C_ADDRESS_VCC_GND 0x2C\n#define IS31FL3743A_I2C_ADDRESS_VCC_SCL 0x2D\n#define IS31FL3743A_I2C_ADDRESS_VCC_SDA 0x2E\n#define IS31FL3743A_I2C_ADDRESS_VCC_VCC 0x2F\n\n#if defined(RGB_MATRIX_IS31FL3743A)\n#    define IS31FL3743A_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3743A_I2C_ADDRESS_4)\n#    define IS31FL3743A_DRIVER_COUNT 4\n#elif defined(IS31FL3743A_I2C_ADDRESS_3)\n#    define IS31FL3743A_DRIVER_COUNT 3\n#elif defined(IS31FL3743A_I2C_ADDRESS_2)\n#    define IS31FL3743A_DRIVER_COUNT 2\n#elif defined(IS31FL3743A_I2C_ADDRESS_1)\n#    define IS31FL3743A_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3743a_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED is31fl3743a_led_t;\n\nextern const is31fl3743a_led_t PROGMEM g_is31fl3743a_leds[IS31FL3743A_LED_COUNT];\n\nvoid is31fl3743a_init_drivers(void);\nvoid is31fl3743a_init(uint8_t index);\nvoid is31fl3743a_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3743a_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3743a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid is31fl3743a_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3743a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3743a_update_pwm_buffers(uint8_t index);\nvoid is31fl3743a_update_scaling_registers(uint8_t index);\n\nvoid is31fl3743a_flush(void);\n\n#define IS31FL3743A_PDR_0_OHM 0b000          // No pull-down resistor\n#define IS31FL3743A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time\n#define IS31FL3743A_PDR_1K_OHM_SW_OFF 0b010  // 1 kOhm resistor in SWx off time\n#define IS31FL3743A_PDR_2K_OHM_SW_OFF 0b011  // 2 kOhm resistor in SWx off time\n#define IS31FL3743A_PDR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3743A_PDR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3743A_PDR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3743A_PDR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3743A_PUR_0_OHM 0b000          // No pull-up resistor\n#define IS31FL3743A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time\n#define IS31FL3743A_PUR_1K_OHM_CS_OFF 0b010  // 1 kOhm resistor in CSy off time\n#define IS31FL3743A_PUR_2K_OHM_CS_OFF 0b011  // 2 kOhm resistor in CSy off time\n#define IS31FL3743A_PUR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3743A_PUR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3743A_PUR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3743A_PUR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3743A_SYNC_NONE 0b00\n#define IS31FL3743A_SYNC_SLAVE 0b10\n#define IS31FL3743A_SYNC_MASTER 0b11\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n#define SW1_CS17 0x10\n#define SW1_CS18 0x11\n\n#define SW2_CS1 0x12\n#define SW2_CS2 0x13\n#define SW2_CS3 0x14\n#define SW2_CS4 0x15\n#define SW2_CS5 0x16\n#define SW2_CS6 0x17\n#define SW2_CS7 0x18\n#define SW2_CS8 0x19\n#define SW2_CS9 0x1A\n#define SW2_CS10 0x1B\n#define SW2_CS11 0x1C\n#define SW2_CS12 0x1D\n#define SW2_CS13 0x1E\n#define SW2_CS14 0x1F\n#define SW2_CS15 0x20\n#define SW2_CS16 0x21\n#define SW2_CS17 0x22\n#define SW2_CS18 0x23\n\n#define SW3_CS1 0x24\n#define SW3_CS2 0x25\n#define SW3_CS3 0x26\n#define SW3_CS4 0x27\n#define SW3_CS5 0x28\n#define SW3_CS6 0x29\n#define SW3_CS7 0x2A\n#define SW3_CS8 0x2B\n#define SW3_CS9 0x2C\n#define SW3_CS10 0x2D\n#define SW3_CS11 0x2E\n#define SW3_CS12 0x2F\n#define SW3_CS13 0x30\n#define SW3_CS14 0x31\n#define SW3_CS15 0x32\n#define SW3_CS16 0x33\n#define SW3_CS17 0x34\n#define SW3_CS18 0x35\n\n#define SW4_CS1 0x36\n#define SW4_CS2 0x37\n#define SW4_CS3 0x38\n#define SW4_CS4 0x39\n#define SW4_CS5 0x3A\n#define SW4_CS6 0x3B\n#define SW4_CS7 0x3C\n#define SW4_CS8 0x3D\n#define SW4_CS9 0x3E\n#define SW4_CS10 0x3F\n#define SW4_CS11 0x40\n#define SW4_CS12 0x41\n#define SW4_CS13 0x42\n#define SW4_CS14 0x43\n#define SW4_CS15 0x44\n#define SW4_CS16 0x45\n#define SW4_CS17 0x46\n#define SW4_CS18 0x47\n\n#define SW5_CS1 0x48\n#define SW5_CS2 0x49\n#define SW5_CS3 0x4A\n#define SW5_CS4 0x4B\n#define SW5_CS5 0x4C\n#define SW5_CS6 0x4D\n#define SW5_CS7 0x4E\n#define SW5_CS8 0x4F\n#define SW5_CS9 0x50\n#define SW5_CS10 0x51\n#define SW5_CS11 0x52\n#define SW5_CS12 0x53\n#define SW5_CS13 0x54\n#define SW5_CS14 0x55\n#define SW5_CS15 0x56\n#define SW5_CS16 0x57\n#define SW5_CS17 0x58\n#define SW5_CS18 0x59\n\n#define SW6_CS1 0x5A\n#define SW6_CS2 0x5B\n#define SW6_CS3 0x5C\n#define SW6_CS4 0x5D\n#define SW6_CS5 0x5E\n#define SW6_CS6 0x5F\n#define SW6_CS7 0x60\n#define SW6_CS8 0x61\n#define SW6_CS9 0x62\n#define SW6_CS10 0x63\n#define SW6_CS11 0x64\n#define SW6_CS12 0x65\n#define SW6_CS13 0x66\n#define SW6_CS14 0x67\n#define SW6_CS15 0x68\n#define SW6_CS16 0x69\n#define SW6_CS17 0x6A\n#define SW6_CS18 0x6B\n\n#define SW7_CS1 0x6C\n#define SW7_CS2 0x6D\n#define SW7_CS3 0x6E\n#define SW7_CS4 0x6F\n#define SW7_CS5 0x70\n#define SW7_CS6 0x71\n#define SW7_CS7 0x72\n#define SW7_CS8 0x73\n#define SW7_CS9 0x74\n#define SW7_CS10 0x75\n#define SW7_CS11 0x76\n#define SW7_CS12 0x77\n#define SW7_CS13 0x78\n#define SW7_CS14 0x79\n#define SW7_CS15 0x7A\n#define SW7_CS16 0x7B\n#define SW7_CS17 0x7C\n#define SW7_CS18 0x7D\n\n#define SW8_CS1 0x7E\n#define SW8_CS2 0x7F\n#define SW8_CS3 0x80\n#define SW8_CS4 0x81\n#define SW8_CS5 0x82\n#define SW8_CS6 0x83\n#define SW8_CS7 0x84\n#define SW8_CS8 0x85\n#define SW8_CS9 0x86\n#define SW8_CS10 0x87\n#define SW8_CS11 0x88\n#define SW8_CS12 0x89\n#define SW8_CS13 0x8A\n#define SW8_CS14 0x8B\n#define SW8_CS15 0x8C\n#define SW8_CS16 0x8D\n#define SW8_CS17 0x8E\n#define SW8_CS18 0x8F\n\n#define SW9_CS1 0x90\n#define SW9_CS2 0x91\n#define SW9_CS3 0x92\n#define SW9_CS4 0x93\n#define SW9_CS5 0x94\n#define SW9_CS6 0x95\n#define SW9_CS7 0x96\n#define SW9_CS8 0x97\n#define SW9_CS9 0x98\n#define SW9_CS10 0x99\n#define SW9_CS11 0x9A\n#define SW9_CS12 0x9B\n#define SW9_CS13 0x9C\n#define SW9_CS14 0x9D\n#define SW9_CS15 0x9E\n#define SW9_CS16 0x9F\n#define SW9_CS17 0xA0\n#define SW9_CS18 0xA1\n\n#define SW10_CS1 0xA2\n#define SW10_CS2 0xA3\n#define SW10_CS3 0xA4\n#define SW10_CS4 0xA5\n#define SW10_CS5 0xA6\n#define SW10_CS6 0xA7\n#define SW10_CS7 0xA8\n#define SW10_CS8 0xA9\n#define SW10_CS9 0xAA\n#define SW10_CS10 0xAB\n#define SW10_CS11 0xAC\n#define SW10_CS12 0xAD\n#define SW10_CS13 0xAE\n#define SW10_CS14 0xAF\n#define SW10_CS15 0xB0\n#define SW10_CS16 0xB1\n#define SW10_CS17 0xB2\n#define SW10_CS18 0xB3\n\n#define SW11_CS1 0xB4\n#define SW11_CS2 0xB5\n#define SW11_CS3 0xB6\n#define SW11_CS4 0xB7\n#define SW11_CS5 0xB8\n#define SW11_CS6 0xB9\n#define SW11_CS7 0xBA\n#define SW11_CS8 0xBB\n#define SW11_CS9 0xBC\n#define SW11_CS10 0xBD\n#define SW11_CS11 0xBE\n#define SW11_CS12 0xBF\n#define SW11_CS13 0xC0\n#define SW11_CS14 0xC1\n#define SW11_CS15 0xC2\n#define SW11_CS16 0xC3\n#define SW11_CS17 0xC4\n#define SW11_CS18 0xC5\n"
  },
  {
    "path": "drivers/led/issi/is31fl3745-mono.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3745-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3745_PWM_REGISTER_COUNT 144\n#define IS31FL3745_SCALING_REGISTER_COUNT 144\n\n#ifndef IS31FL3745_I2C_TIMEOUT\n#    define IS31FL3745_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3745_I2C_PERSISTENCE\n#    define IS31FL3745_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3745_CONFIGURATION\n#    define IS31FL3745_CONFIGURATION 0x31\n#endif\n\n#ifndef IS31FL3745_SW_PULLDOWN\n#    define IS31FL3745_SW_PULLDOWN IS31FL3745_PDR_2K_OHM_SW_OFF\n#endif\n\n#ifndef IS31FL3745_CS_PULLUP\n#    define IS31FL3745_CS_PULLUP IS31FL3745_PUR_2K_OHM_CS_OFF\n#endif\n\n#ifndef IS31FL3745_GLOBAL_CURRENT\n#    define IS31FL3745_GLOBAL_CURRENT 0xFF\n#endif\n\n#ifndef IS31FL3745_SYNC_1\n#    define IS31FL3745_SYNC_1 IS31FL3745_SYNC_NONE\n#endif\n#ifndef IS31FL3745_SYNC_2\n#    define IS31FL3745_SYNC_2 IS31FL3745_SYNC_NONE\n#endif\n#ifndef IS31FL3745_SYNC_3\n#    define IS31FL3745_SYNC_3 IS31FL3745_SYNC_NONE\n#endif\n#ifndef IS31FL3745_SYNC_4\n#    define IS31FL3745_SYNC_4 IS31FL3745_SYNC_NONE\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3745_DRIVER_COUNT] = {\n    IS31FL3745_I2C_ADDRESS_1,\n#ifdef IS31FL3745_I2C_ADDRESS_2\n    IS31FL3745_I2C_ADDRESS_2,\n#    ifdef IS31FL3745_I2C_ADDRESS_3\n    IS31FL3745_I2C_ADDRESS_3,\n#        ifdef IS31FL3745_I2C_ADDRESS_4\n    IS31FL3745_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\nconst uint8_t driver_sync[IS31FL3745_DRIVER_COUNT] = {\n    IS31FL3745_SYNC_1,\n#ifdef IS31FL3745_I2C_ADDRESS_2\n    IS31FL3745_SYNC_2,\n#    ifdef IS31FL3745_I2C_ADDRESS_3\n    IS31FL3745_SYNC_3,\n#        ifdef IS31FL3745_I2C_ADDRESS_4\n    IS31FL3745_SYNC_4,\n#        endif\n#    endif\n#endif\n};\n\ntypedef struct is31fl3745_driver_t {\n    uint8_t pwm_buffer[IS31FL3745_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer[IS31FL3745_SCALING_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3745_driver_t;\n\nis31fl3745_driver_t driver_buffers[IS31FL3745_DRIVER_COUNT] = {{\n    .pwm_buffer           = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer       = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3745_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3745_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3745_select_page(uint8_t index, uint8_t page) {\n    is31fl3745_write_register(index, IS31FL3745_REG_COMMAND_WRITE_LOCK, IS31FL3745_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3745_write_register(index, IS31FL3745_REG_COMMAND, page);\n}\n\nvoid is31fl3745_write_pwm_buffer(uint8_t index) {\n    // Assumes page 0 is already selected.\n    // Transmit PWM registers in 8 transfers of 18 bytes.\n\n    // Iterate over the pwm_buffer contents at 18 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i += 18) {\n#if IS31FL3745_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3745_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3745_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3745_SDB_PIN)\n    gpio_set_pin_output(IS31FL3745_SDB_PIN);\n    gpio_write_pin_high(IS31FL3745_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {\n        is31fl3745_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3745_LED_COUNT; i++) {\n        is31fl3745_set_scaling_register(i, 0xFF);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {\n        is31fl3745_update_scaling_registers(i);\n    }\n}\n\nvoid is31fl3745_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) {\n        is31fl3745_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM);\n\n    for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i++) {\n        is31fl3745_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3745_select_page(index, IS31FL3745_COMMAND_FUNCTION);\n\n    uint8_t sync = driver_sync[index];\n\n    is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_PULLDOWNUP, (IS31FL3745_SW_PULLDOWN << 4) | IS31FL3745_CS_PULLUP);\n    is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3745_GLOBAL_CURRENT);\n    is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6);\n    is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_CONFIGURATION, IS31FL3745_CONFIGURATION);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3745_set_value(int index, uint8_t value) {\n    is31fl3745_led_t led;\n\n    if (index >= 0 && index < IS31FL3745_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.v] = value;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3745_set_value_all(uint8_t value) {\n    for (int i = 0; i < IS31FL3745_LED_COUNT; i++) {\n        is31fl3745_set_value(i, value);\n    }\n}\n\nvoid is31fl3745_set_scaling_register(uint8_t index, uint8_t value) {\n    is31fl3745_led_t led;\n    memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led));\n\n    driver_buffers[led.driver].scaling_buffer[led.v] = value;\n    driver_buffers[led.driver].scaling_buffer_dirty  = true;\n}\n\nvoid is31fl3745_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM);\n\n        is31fl3745_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3745_update_scaling_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING);\n\n        for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) {\n            is31fl3745_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3745_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {\n        is31fl3745_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3745-mono.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3745_REG_ID 0xFC\n\n#define IS31FL3745_REG_COMMAND 0xFD\n\n#define IS31FL3745_COMMAND_PWM 0x00\n#define IS31FL3745_COMMAND_SCALING 0x01\n#define IS31FL3745_COMMAND_FUNCTION 0x02\n\n#define IS31FL3745_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3745_FUNCTION_REG_PULLDOWNUP 0x02\n#define IS31FL3745_FUNCTION_REG_TEMPERATURE 0x24\n#define IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM 0x25\n#define IS31FL3745_FUNCTION_REG_RESET 0x2F\n\n#define IS31FL3745_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3745_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3745_I2C_ADDRESS_GND_GND 0x20\n#define IS31FL3745_I2C_ADDRESS_GND_SCL 0x21\n#define IS31FL3745_I2C_ADDRESS_GND_SDA 0x22\n#define IS31FL3745_I2C_ADDRESS_GND_VCC 0x23\n#define IS31FL3745_I2C_ADDRESS_SCL_GND 0x24\n#define IS31FL3745_I2C_ADDRESS_SCL_SCL 0x25\n#define IS31FL3745_I2C_ADDRESS_SCL_SDA 0x26\n#define IS31FL3745_I2C_ADDRESS_SCL_VCC 0x27\n#define IS31FL3745_I2C_ADDRESS_SDA_GND 0x28\n#define IS31FL3745_I2C_ADDRESS_SDA_SCL 0x29\n#define IS31FL3745_I2C_ADDRESS_SDA_SDA 0x2A\n#define IS31FL3745_I2C_ADDRESS_SDA_VCC 0x2B\n#define IS31FL3745_I2C_ADDRESS_VCC_GND 0x2C\n#define IS31FL3745_I2C_ADDRESS_VCC_SCL 0x2D\n#define IS31FL3745_I2C_ADDRESS_VCC_SDA 0x2E\n#define IS31FL3745_I2C_ADDRESS_VCC_VCC 0x2F\n\n#if defined(LED_MATRIX_IS31FL3745)\n#    define IS31FL3745_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3745_I2C_ADDRESS_4)\n#    define IS31FL3745_DRIVER_COUNT 4\n#elif defined(IS31FL3745_I2C_ADDRESS_3)\n#    define IS31FL3745_DRIVER_COUNT 3\n#elif defined(IS31FL3745_I2C_ADDRESS_2)\n#    define IS31FL3745_DRIVER_COUNT 2\n#elif defined(IS31FL3745_I2C_ADDRESS_1)\n#    define IS31FL3745_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3745_led_t {\n    uint8_t driver : 2;\n    uint8_t v;\n} PACKED is31fl3745_led_t;\n\nextern const is31fl3745_led_t PROGMEM g_is31fl3745_leds[IS31FL3745_LED_COUNT];\n\nvoid is31fl3745_init_drivers(void);\nvoid is31fl3745_init(uint8_t index);\nvoid is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3745_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3745_set_value(int index, uint8_t value);\nvoid is31fl3745_set_value_all(uint8_t value);\n\nvoid is31fl3745_set_scaling_register(uint8_t index, uint8_t value);\n\nvoid is31fl3745_update_pwm_buffers(uint8_t index);\nvoid is31fl3745_update_scaling_registers(uint8_t index);\n\nvoid is31fl3745_flush(void);\n\n#define IS31FL3745_PDR_0_OHM 0b000          // No pull-down resistor\n#define IS31FL3745_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time\n#define IS31FL3745_PDR_1K_OHM_SW_OFF 0b010  // 1 kOhm resistor in SWx off time\n#define IS31FL3745_PDR_2K_OHM_SW_OFF 0b011  // 2 kOhm resistor in SWx off time\n#define IS31FL3745_PDR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3745_PDR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3745_PDR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3745_PDR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3745_PUR_0_OHM 0b000          // No pull-up resistor\n#define IS31FL3745_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time\n#define IS31FL3745_PUR_1K_OHM_CS_OFF 0b010  // 1 kOhm resistor in CSy off time\n#define IS31FL3745_PUR_2K_OHM_CS_OFF 0b011  // 2 kOhm resistor in CSy off time\n#define IS31FL3745_PUR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3745_PUR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3745_PUR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3745_PUR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3745_SYNC_NONE 0b00\n#define IS31FL3745_SYNC_SLAVE 0b10\n#define IS31FL3745_SYNC_MASTER 0b11\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n#define SW1_CS17 0x10\n#define SW1_CS18 0x11\n\n#define SW2_CS1 0x12\n#define SW2_CS2 0x13\n#define SW2_CS3 0x14\n#define SW2_CS4 0x15\n#define SW2_CS5 0x16\n#define SW2_CS6 0x17\n#define SW2_CS7 0x18\n#define SW2_CS8 0x19\n#define SW2_CS9 0x1A\n#define SW2_CS10 0x1B\n#define SW2_CS11 0x1C\n#define SW2_CS12 0x1D\n#define SW2_CS13 0x1E\n#define SW2_CS14 0x1F\n#define SW2_CS15 0x20\n#define SW2_CS16 0x21\n#define SW2_CS17 0x22\n#define SW2_CS18 0x23\n\n#define SW3_CS1 0x24\n#define SW3_CS2 0x25\n#define SW3_CS3 0x26\n#define SW3_CS4 0x27\n#define SW3_CS5 0x28\n#define SW3_CS6 0x29\n#define SW3_CS7 0x2A\n#define SW3_CS8 0x2B\n#define SW3_CS9 0x2C\n#define SW3_CS10 0x2D\n#define SW3_CS11 0x2E\n#define SW3_CS12 0x2F\n#define SW3_CS13 0x30\n#define SW3_CS14 0x31\n#define SW3_CS15 0x32\n#define SW3_CS16 0x33\n#define SW3_CS17 0x34\n#define SW3_CS18 0x35\n\n#define SW4_CS1 0x36\n#define SW4_CS2 0x37\n#define SW4_CS3 0x38\n#define SW4_CS4 0x39\n#define SW4_CS5 0x3A\n#define SW4_CS6 0x3B\n#define SW4_CS7 0x3C\n#define SW4_CS8 0x3D\n#define SW4_CS9 0x3E\n#define SW4_CS10 0x3F\n#define SW4_CS11 0x40\n#define SW4_CS12 0x41\n#define SW4_CS13 0x42\n#define SW4_CS14 0x43\n#define SW4_CS15 0x44\n#define SW4_CS16 0x45\n#define SW4_CS17 0x46\n#define SW4_CS18 0x47\n\n#define SW5_CS1 0x48\n#define SW5_CS2 0x49\n#define SW5_CS3 0x4A\n#define SW5_CS4 0x4B\n#define SW5_CS5 0x4C\n#define SW5_CS6 0x4D\n#define SW5_CS7 0x4E\n#define SW5_CS8 0x4F\n#define SW5_CS9 0x50\n#define SW5_CS10 0x51\n#define SW5_CS11 0x52\n#define SW5_CS12 0x53\n#define SW5_CS13 0x54\n#define SW5_CS14 0x55\n#define SW5_CS15 0x56\n#define SW5_CS16 0x57\n#define SW5_CS17 0x58\n#define SW5_CS18 0x59\n\n#define SW6_CS1 0x5A\n#define SW6_CS2 0x5B\n#define SW6_CS3 0x5C\n#define SW6_CS4 0x5D\n#define SW6_CS5 0x5E\n#define SW6_CS6 0x5F\n#define SW6_CS7 0x60\n#define SW6_CS8 0x61\n#define SW6_CS9 0x62\n#define SW6_CS10 0x63\n#define SW6_CS11 0x64\n#define SW6_CS12 0x65\n#define SW6_CS13 0x66\n#define SW6_CS14 0x67\n#define SW6_CS15 0x68\n#define SW6_CS16 0x69\n#define SW6_CS17 0x6A\n#define SW6_CS18 0x6B\n\n#define SW7_CS1 0x6C\n#define SW7_CS2 0x6D\n#define SW7_CS3 0x6E\n#define SW7_CS4 0x6F\n#define SW7_CS5 0x70\n#define SW7_CS6 0x71\n#define SW7_CS7 0x72\n#define SW7_CS8 0x73\n#define SW7_CS9 0x74\n#define SW7_CS10 0x75\n#define SW7_CS11 0x76\n#define SW7_CS12 0x77\n#define SW7_CS13 0x78\n#define SW7_CS14 0x79\n#define SW7_CS15 0x7A\n#define SW7_CS16 0x7B\n#define SW7_CS17 0x7C\n#define SW7_CS18 0x7D\n\n#define SW8_CS1 0x7E\n#define SW8_CS2 0x7F\n#define SW8_CS3 0x80\n#define SW8_CS4 0x81\n#define SW8_CS5 0x82\n#define SW8_CS6 0x83\n#define SW8_CS7 0x84\n#define SW8_CS8 0x85\n#define SW8_CS9 0x86\n#define SW8_CS10 0x87\n#define SW8_CS11 0x88\n#define SW8_CS12 0x89\n#define SW8_CS13 0x8A\n#define SW8_CS14 0x8B\n#define SW8_CS15 0x8C\n#define SW8_CS16 0x8D\n#define SW8_CS17 0x8E\n#define SW8_CS18 0x8F\n"
  },
  {
    "path": "drivers/led/issi/is31fl3745.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3745.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3745_PWM_REGISTER_COUNT 144\n#define IS31FL3745_SCALING_REGISTER_COUNT 144\n\n#ifndef IS31FL3745_I2C_TIMEOUT\n#    define IS31FL3745_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3745_I2C_PERSISTENCE\n#    define IS31FL3745_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3745_CONFIGURATION\n#    define IS31FL3745_CONFIGURATION 0x31\n#endif\n\n#ifndef IS31FL3745_SW_PULLDOWN\n#    define IS31FL3745_SW_PULLDOWN IS31FL3745_PDR_2K_OHM_SW_OFF\n#endif\n\n#ifndef IS31FL3745_CS_PULLUP\n#    define IS31FL3745_CS_PULLUP IS31FL3745_PUR_2K_OHM_CS_OFF\n#endif\n\n#ifndef IS31FL3745_GLOBAL_CURRENT\n#    define IS31FL3745_GLOBAL_CURRENT 0xFF\n#endif\n\n#ifndef IS31FL3745_SYNC_1\n#    define IS31FL3745_SYNC_1 IS31FL3745_SYNC_NONE\n#endif\n#ifndef IS31FL3745_SYNC_2\n#    define IS31FL3745_SYNC_2 IS31FL3745_SYNC_NONE\n#endif\n#ifndef IS31FL3745_SYNC_3\n#    define IS31FL3745_SYNC_3 IS31FL3745_SYNC_NONE\n#endif\n#ifndef IS31FL3745_SYNC_4\n#    define IS31FL3745_SYNC_4 IS31FL3745_SYNC_NONE\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3745_DRIVER_COUNT] = {\n    IS31FL3745_I2C_ADDRESS_1,\n#ifdef IS31FL3745_I2C_ADDRESS_2\n    IS31FL3745_I2C_ADDRESS_2,\n#    ifdef IS31FL3745_I2C_ADDRESS_3\n    IS31FL3745_I2C_ADDRESS_3,\n#        ifdef IS31FL3745_I2C_ADDRESS_4\n    IS31FL3745_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\nconst uint8_t driver_sync[IS31FL3745_DRIVER_COUNT] = {\n    IS31FL3745_SYNC_1,\n#ifdef IS31FL3745_I2C_ADDRESS_2\n    IS31FL3745_SYNC_2,\n#    ifdef IS31FL3745_I2C_ADDRESS_3\n    IS31FL3745_SYNC_3,\n#        ifdef IS31FL3745_I2C_ADDRESS_4\n    IS31FL3745_SYNC_4,\n#        endif\n#    endif\n#endif\n};\n\ntypedef struct is31fl3745_driver_t {\n    uint8_t pwm_buffer[IS31FL3745_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer[IS31FL3745_SCALING_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3745_driver_t;\n\nis31fl3745_driver_t driver_buffers[IS31FL3745_DRIVER_COUNT] = {{\n    .pwm_buffer           = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer       = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3745_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3745_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3745_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3745_select_page(uint8_t index, uint8_t page) {\n    is31fl3745_write_register(index, IS31FL3745_REG_COMMAND_WRITE_LOCK, IS31FL3745_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3745_write_register(index, IS31FL3745_REG_COMMAND, page);\n}\n\nvoid is31fl3745_write_pwm_buffer(uint8_t index) {\n    // Assumes page 0 is already selected.\n    // Transmit PWM registers in 8 transfers of 18 bytes.\n\n    // Iterate over the pwm_buffer contents at 18 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i += 18) {\n#if IS31FL3745_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3745_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3745_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3745_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3745_SDB_PIN)\n    gpio_set_pin_output(IS31FL3745_SDB_PIN);\n    gpio_write_pin_high(IS31FL3745_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {\n        is31fl3745_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3745_LED_COUNT; i++) {\n        is31fl3745_set_scaling_register(i, 0xFF, 0xFF, 0xFF);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {\n        is31fl3745_update_scaling_registers(i);\n    }\n}\n\nvoid is31fl3745_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) {\n        is31fl3745_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM);\n\n    for (uint8_t i = 0; i < IS31FL3745_PWM_REGISTER_COUNT; i++) {\n        is31fl3745_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3745_select_page(index, IS31FL3745_COMMAND_FUNCTION);\n\n    uint8_t sync = driver_sync[index];\n\n    is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_PULLDOWNUP, (IS31FL3745_SW_PULLDOWN << 4) | IS31FL3745_CS_PULLUP);\n    is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3745_GLOBAL_CURRENT);\n    is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM, (sync & 0b11) << 6);\n    is31fl3745_write_register(index, IS31FL3745_FUNCTION_REG_CONFIGURATION, IS31FL3745_CONFIGURATION);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3745_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3745_led_t led;\n\n    if (index >= 0 && index < IS31FL3745_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.r] = red;\n        driver_buffers[led.driver].pwm_buffer[led.g] = green;\n        driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3745_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < IS31FL3745_LED_COUNT; i++) {\n        is31fl3745_set_color(i, red, green, blue);\n    }\n}\n\nvoid is31fl3745_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3745_led_t led;\n    memcpy_P(&led, (&g_is31fl3745_leds[index]), sizeof(led));\n\n    driver_buffers[led.driver].scaling_buffer[led.r] = red;\n    driver_buffers[led.driver].scaling_buffer[led.g] = green;\n    driver_buffers[led.driver].scaling_buffer[led.b] = blue;\n    driver_buffers[led.driver].scaling_buffer_dirty  = true;\n}\n\nvoid is31fl3745_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3745_select_page(index, IS31FL3745_COMMAND_PWM);\n\n        is31fl3745_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3745_update_scaling_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        is31fl3745_select_page(index, IS31FL3745_COMMAND_SCALING);\n\n        for (uint8_t i = 0; i < IS31FL3745_SCALING_REGISTER_COUNT; i++) {\n            is31fl3745_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3745_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3745_DRIVER_COUNT; i++) {\n        is31fl3745_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3745.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3745_REG_ID 0xFC\n\n#define IS31FL3745_REG_COMMAND 0xFD\n\n#define IS31FL3745_COMMAND_PWM 0x00\n#define IS31FL3745_COMMAND_SCALING 0x01\n#define IS31FL3745_COMMAND_FUNCTION 0x02\n\n#define IS31FL3745_FUNCTION_REG_CONFIGURATION 0x00\n#define IS31FL3745_FUNCTION_REG_GLOBAL_CURRENT 0x01\n#define IS31FL3745_FUNCTION_REG_PULLDOWNUP 0x02\n#define IS31FL3745_FUNCTION_REG_TEMPERATURE 0x24\n#define IS31FL3745_FUNCTION_REG_SPREAD_SPECTRUM 0x25\n#define IS31FL3745_FUNCTION_REG_RESET 0x2F\n\n#define IS31FL3745_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3745_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3745_I2C_ADDRESS_GND_GND 0x20\n#define IS31FL3745_I2C_ADDRESS_GND_SCL 0x21\n#define IS31FL3745_I2C_ADDRESS_GND_SDA 0x22\n#define IS31FL3745_I2C_ADDRESS_GND_VCC 0x23\n#define IS31FL3745_I2C_ADDRESS_SCL_GND 0x24\n#define IS31FL3745_I2C_ADDRESS_SCL_SCL 0x25\n#define IS31FL3745_I2C_ADDRESS_SCL_SDA 0x26\n#define IS31FL3745_I2C_ADDRESS_SCL_VCC 0x27\n#define IS31FL3745_I2C_ADDRESS_SDA_GND 0x28\n#define IS31FL3745_I2C_ADDRESS_SDA_SCL 0x29\n#define IS31FL3745_I2C_ADDRESS_SDA_SDA 0x2A\n#define IS31FL3745_I2C_ADDRESS_SDA_VCC 0x2B\n#define IS31FL3745_I2C_ADDRESS_VCC_GND 0x2C\n#define IS31FL3745_I2C_ADDRESS_VCC_SCL 0x2D\n#define IS31FL3745_I2C_ADDRESS_VCC_SDA 0x2E\n#define IS31FL3745_I2C_ADDRESS_VCC_VCC 0x2F\n\n#if defined(RGB_MATRIX_IS31FL3745)\n#    define IS31FL3745_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3745_I2C_ADDRESS_4)\n#    define IS31FL3745_DRIVER_COUNT 4\n#elif defined(IS31FL3745_I2C_ADDRESS_3)\n#    define IS31FL3745_DRIVER_COUNT 3\n#elif defined(IS31FL3745_I2C_ADDRESS_2)\n#    define IS31FL3745_DRIVER_COUNT 2\n#elif defined(IS31FL3745_I2C_ADDRESS_1)\n#    define IS31FL3745_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3745_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED is31fl3745_led_t;\n\nextern const is31fl3745_led_t PROGMEM g_is31fl3745_leds[IS31FL3745_LED_COUNT];\n\nvoid is31fl3745_init_drivers(void);\nvoid is31fl3745_init(uint8_t index);\nvoid is31fl3745_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3745_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3745_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid is31fl3745_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3745_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3745_update_pwm_buffers(uint8_t index);\nvoid is31fl3745_update_scaling_registers(uint8_t index);\n\nvoid is31fl3745_flush(void);\n\n#define IS31FL3745_PDR_0_OHM 0b000          // No pull-down resistor\n#define IS31FL3745_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time\n#define IS31FL3745_PDR_1K_OHM_SW_OFF 0b010  // 1 kOhm resistor in SWx off time\n#define IS31FL3745_PDR_2K_OHM_SW_OFF 0b011  // 2 kOhm resistor in SWx off time\n#define IS31FL3745_PDR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3745_PDR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3745_PDR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3745_PDR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3745_PUR_0_OHM 0b000          // No pull-up resistor\n#define IS31FL3745_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time\n#define IS31FL3745_PUR_1K_OHM_CS_OFF 0b010  // 1 kOhm resistor in CSy off time\n#define IS31FL3745_PUR_2K_OHM_CS_OFF 0b011  // 2 kOhm resistor in CSy off time\n#define IS31FL3745_PUR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3745_PUR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3745_PUR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3745_PUR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3745_SYNC_NONE 0b00\n#define IS31FL3745_SYNC_SLAVE 0b10\n#define IS31FL3745_SYNC_MASTER 0b11\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n#define SW1_CS17 0x10\n#define SW1_CS18 0x11\n\n#define SW2_CS1 0x12\n#define SW2_CS2 0x13\n#define SW2_CS3 0x14\n#define SW2_CS4 0x15\n#define SW2_CS5 0x16\n#define SW2_CS6 0x17\n#define SW2_CS7 0x18\n#define SW2_CS8 0x19\n#define SW2_CS9 0x1A\n#define SW2_CS10 0x1B\n#define SW2_CS11 0x1C\n#define SW2_CS12 0x1D\n#define SW2_CS13 0x1E\n#define SW2_CS14 0x1F\n#define SW2_CS15 0x20\n#define SW2_CS16 0x21\n#define SW2_CS17 0x22\n#define SW2_CS18 0x23\n\n#define SW3_CS1 0x24\n#define SW3_CS2 0x25\n#define SW3_CS3 0x26\n#define SW3_CS4 0x27\n#define SW3_CS5 0x28\n#define SW3_CS6 0x29\n#define SW3_CS7 0x2A\n#define SW3_CS8 0x2B\n#define SW3_CS9 0x2C\n#define SW3_CS10 0x2D\n#define SW3_CS11 0x2E\n#define SW3_CS12 0x2F\n#define SW3_CS13 0x30\n#define SW3_CS14 0x31\n#define SW3_CS15 0x32\n#define SW3_CS16 0x33\n#define SW3_CS17 0x34\n#define SW3_CS18 0x35\n\n#define SW4_CS1 0x36\n#define SW4_CS2 0x37\n#define SW4_CS3 0x38\n#define SW4_CS4 0x39\n#define SW4_CS5 0x3A\n#define SW4_CS6 0x3B\n#define SW4_CS7 0x3C\n#define SW4_CS8 0x3D\n#define SW4_CS9 0x3E\n#define SW4_CS10 0x3F\n#define SW4_CS11 0x40\n#define SW4_CS12 0x41\n#define SW4_CS13 0x42\n#define SW4_CS14 0x43\n#define SW4_CS15 0x44\n#define SW4_CS16 0x45\n#define SW4_CS17 0x46\n#define SW4_CS18 0x47\n\n#define SW5_CS1 0x48\n#define SW5_CS2 0x49\n#define SW5_CS3 0x4A\n#define SW5_CS4 0x4B\n#define SW5_CS5 0x4C\n#define SW5_CS6 0x4D\n#define SW5_CS7 0x4E\n#define SW5_CS8 0x4F\n#define SW5_CS9 0x50\n#define SW5_CS10 0x51\n#define SW5_CS11 0x52\n#define SW5_CS12 0x53\n#define SW5_CS13 0x54\n#define SW5_CS14 0x55\n#define SW5_CS15 0x56\n#define SW5_CS16 0x57\n#define SW5_CS17 0x58\n#define SW5_CS18 0x59\n\n#define SW6_CS1 0x5A\n#define SW6_CS2 0x5B\n#define SW6_CS3 0x5C\n#define SW6_CS4 0x5D\n#define SW6_CS5 0x5E\n#define SW6_CS6 0x5F\n#define SW6_CS7 0x60\n#define SW6_CS8 0x61\n#define SW6_CS9 0x62\n#define SW6_CS10 0x63\n#define SW6_CS11 0x64\n#define SW6_CS12 0x65\n#define SW6_CS13 0x66\n#define SW6_CS14 0x67\n#define SW6_CS15 0x68\n#define SW6_CS16 0x69\n#define SW6_CS17 0x6A\n#define SW6_CS18 0x6B\n\n#define SW7_CS1 0x6C\n#define SW7_CS2 0x6D\n#define SW7_CS3 0x6E\n#define SW7_CS4 0x6F\n#define SW7_CS5 0x70\n#define SW7_CS6 0x71\n#define SW7_CS7 0x72\n#define SW7_CS8 0x73\n#define SW7_CS9 0x74\n#define SW7_CS10 0x75\n#define SW7_CS11 0x76\n#define SW7_CS12 0x77\n#define SW7_CS13 0x78\n#define SW7_CS14 0x79\n#define SW7_CS15 0x7A\n#define SW7_CS16 0x7B\n#define SW7_CS17 0x7C\n#define SW7_CS18 0x7D\n\n#define SW8_CS1 0x7E\n#define SW8_CS2 0x7F\n#define SW8_CS3 0x80\n#define SW8_CS4 0x81\n#define SW8_CS5 0x82\n#define SW8_CS6 0x83\n#define SW8_CS7 0x84\n#define SW8_CS8 0x85\n#define SW8_CS9 0x86\n#define SW8_CS10 0x87\n#define SW8_CS11 0x88\n#define SW8_CS12 0x89\n#define SW8_CS13 0x8A\n#define SW8_CS14 0x8B\n#define SW8_CS15 0x8C\n#define SW8_CS16 0x8D\n#define SW8_CS17 0x8E\n#define SW8_CS18 0x8F\n"
  },
  {
    "path": "drivers/led/issi/is31fl3746a-mono.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3746a-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3746A_PWM_REGISTER_COUNT 72\n#define IS31FL3746A_SCALING_REGISTER_COUNT 72\n\n#ifndef IS31FL3746A_I2C_TIMEOUT\n#    define IS31FL3746A_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3746A_I2C_PERSISTENCE\n#    define IS31FL3746A_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3746A_CONFIGURATION\n#    define IS31FL3746A_CONFIGURATION 0x01\n#endif\n\n#ifndef IS31FL3746A_PWM_FREQUENCY\n#    define IS31FL3746A_PWM_FREQUENCY IS31FL3746A_PWM_FREQUENCY_29K_HZ\n#endif\n\n#ifndef IS31FL3746A_SW_PULLDOWN\n#    define IS31FL3746A_SW_PULLDOWN IS31FL3746A_PDR_2K_OHM_SW_OFF\n#endif\n\n#ifndef IS31FL3746A_CS_PULLUP\n#    define IS31FL3746A_CS_PULLUP IS31FL3746A_PUR_2K_OHM_CS_OFF\n#endif\n\n#ifndef IS31FL3746A_GLOBAL_CURRENT\n#    define IS31FL3746A_GLOBAL_CURRENT 0xFF\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3746A_DRIVER_COUNT] = {\n    IS31FL3746A_I2C_ADDRESS_1,\n#ifdef IS31FL3746A_I2C_ADDRESS_2\n    IS31FL3746A_I2C_ADDRESS_2,\n#    ifdef IS31FL3746A_I2C_ADDRESS_3\n    IS31FL3746A_I2C_ADDRESS_3,\n#        ifdef IS31FL3746A_I2C_ADDRESS_4\n    IS31FL3746A_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\ntypedef struct is31fl3746a_driver_t {\n    uint8_t pwm_buffer[IS31FL3746A_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer[IS31FL3746A_SCALING_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3746a_driver_t;\n\nis31fl3746a_driver_t driver_buffers[IS31FL3746A_DRIVER_COUNT] = {{\n    .pwm_buffer           = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer       = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3746A_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3746A_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3746a_select_page(uint8_t index, uint8_t page) {\n    is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND_WRITE_LOCK, IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND, page);\n}\n\nvoid is31fl3746a_write_pwm_buffer(uint8_t index) {\n    // Assumes page 0 is already selected.\n    // Transmit PWM registers in 4 transfers of 18 bytes.\n\n    // Iterate over the pwm_buffer contents at 18 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i += 18) {\n#if IS31FL3746A_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3746A_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3746a_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3746A_SDB_PIN)\n    gpio_set_pin_output(IS31FL3746A_SDB_PIN);\n    gpio_write_pin_high(IS31FL3746A_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {\n        is31fl3746a_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) {\n        is31fl3746a_set_scaling_register(i, 0xFF);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {\n        is31fl3746a_update_scaling_registers(i);\n    }\n}\n\nvoid is31fl3746a_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) {\n        is31fl3746a_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM);\n\n    for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i++) {\n        is31fl3746a_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3746a_select_page(index, IS31FL3746A_COMMAND_FUNCTION);\n\n    is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PULLDOWNUP, (IS31FL3746A_SW_PULLDOWN << 4) | IS31FL3746A_CS_PULLUP);\n    is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3746A_GLOBAL_CURRENT);\n    is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_ENABLE, 0x01);\n    is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY, IS31FL3746A_PWM_FREQUENCY);\n    is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_CONFIGURATION, IS31FL3746A_CONFIGURATION);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3746a_set_value(int index, uint8_t value) {\n    is31fl3746a_led_t led;\n\n    if (index >= 0 && index < IS31FL3746A_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.v] = value;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3746a_set_value_all(uint8_t value) {\n    for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) {\n        is31fl3746a_set_value(i, value);\n    }\n}\n\nvoid is31fl3746a_set_scaling_register(uint8_t index, uint8_t value) {\n    is31fl3746a_led_t led;\n    memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led));\n\n    driver_buffers[led.driver].scaling_buffer[led.v] = value;\n    driver_buffers[led.driver].scaling_buffer_dirty  = true;\n}\n\nvoid is31fl3746a_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM);\n\n        is31fl3746a_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3746a_update_scaling_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING);\n\n        for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) {\n            is31fl3746a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3746a_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {\n        is31fl3746a_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3746a-mono.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3746A_REG_ID 0xFC\n\n#define IS31FL3746A_REG_COMMAND 0xFD\n\n#define IS31FL3746A_COMMAND_PWM 0x00\n#define IS31FL3746A_COMMAND_SCALING 0x01\n#define IS31FL3746A_COMMAND_FUNCTION 0x01\n\n#define IS31FL3746A_FUNCTION_REG_CONFIGURATION 0x50\n#define IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT 0x51\n#define IS31FL3746A_FUNCTION_REG_PULLDOWNUP 0x52\n#define IS31FL3746A_FUNCTION_REG_TEMPERATURE 0x5F\n#define IS31FL3746A_FUNCTION_REG_SPREAD_SPECTRUM 0x60\n#define IS31FL3746A_FUNCTION_REG_RESET 0x8F\n#define IS31FL3746A_FUNCTION_REG_PWM_ENABLE 0xE0\n#define IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY 0xE2\n\n#define IS31FL3746A_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3746A_I2C_ADDRESS_GND_GND 0x60\n#define IS31FL3746A_I2C_ADDRESS_GND_SCL 0x61\n#define IS31FL3746A_I2C_ADDRESS_GND_SDA 0x62\n#define IS31FL3746A_I2C_ADDRESS_GND_VCC 0x63\n#define IS31FL3746A_I2C_ADDRESS_SCL_GND 0x64\n#define IS31FL3746A_I2C_ADDRESS_SCL_SCL 0x65\n#define IS31FL3746A_I2C_ADDRESS_SCL_SDA 0x66\n#define IS31FL3746A_I2C_ADDRESS_SCL_VCC 0x67\n#define IS31FL3746A_I2C_ADDRESS_SDA_GND 0x68\n#define IS31FL3746A_I2C_ADDRESS_SDA_SCL 0x69\n#define IS31FL3746A_I2C_ADDRESS_SDA_SDA 0x6A\n#define IS31FL3746A_I2C_ADDRESS_SDA_VCC 0x6B\n#define IS31FL3746A_I2C_ADDRESS_VCC_GND 0x6C\n#define IS31FL3746A_I2C_ADDRESS_VCC_SCL 0x6D\n#define IS31FL3746A_I2C_ADDRESS_VCC_SDA 0x6E\n#define IS31FL3746A_I2C_ADDRESS_VCC_VCC 0x6F\n\n#if defined(LED_MATRIX_IS31FL3746A)\n#    define IS31FL3746A_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3746A_I2C_ADDRESS_4)\n#    define IS31FL3746A_DRIVER_COUNT 4\n#elif defined(IS31FL3746A_I2C_ADDRESS_3)\n#    define IS31FL3746A_DRIVER_COUNT 3\n#elif defined(IS31FL3746A_I2C_ADDRESS_2)\n#    define IS31FL3746A_DRIVER_COUNT 2\n#elif defined(IS31FL3746A_I2C_ADDRESS_1)\n#    define IS31FL3746A_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3746a_led_t {\n    uint8_t driver : 2;\n    uint8_t v;\n} PACKED is31fl3746a_led_t;\n\nextern const is31fl3746a_led_t PROGMEM g_is31fl3746a_leds[IS31FL3746A_LED_COUNT];\n\nvoid is31fl3746a_init_drivers(void);\nvoid is31fl3746a_init(uint8_t index);\nvoid is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3746a_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3746a_set_value(int index, uint8_t value);\nvoid is31fl3746a_set_value_all(uint8_t value);\n\nvoid is31fl3746a_set_scaling_register(uint8_t index, uint8_t value);\n\nvoid is31fl3746a_update_pwm_buffers(uint8_t index);\nvoid is31fl3746a_update_scaling_registers(uint8_t index);\n\nvoid is31fl3746a_flush(void);\n\n#define IS31FL3746A_PDR_0_OHM 0b000          // No pull-down resistor\n#define IS31FL3746A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time\n#define IS31FL3746A_PDR_1K_OHM_SW_OFF 0b010  // 1 kOhm resistor in SWx off time\n#define IS31FL3746A_PDR_2K_OHM_SW_OFF 0b011  // 2 kOhm resistor in SWx off time\n#define IS31FL3746A_PDR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3746A_PDR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3746A_PDR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3746A_PDR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3746A_PUR_0_OHM 0b000          // No pull-up resistor\n#define IS31FL3746A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time\n#define IS31FL3746A_PUR_1K_OHM_CS_OFF 0b010  // 1 kOhm resistor in CSy off time\n#define IS31FL3746A_PUR_2K_OHM_CS_OFF 0b011  // 2 kOhm resistor in CSy off time\n#define IS31FL3746A_PUR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3746A_PUR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3746A_PUR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3746A_PUR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3746A_PWM_FREQUENCY_29K_HZ 0b000\n#define IS31FL3746A_PWM_FREQUENCY_14K5_HZ 0b001\n#define IS31FL3746A_PWM_FREQUENCY_7K25_HZ 0b010\n#define IS31FL3746A_PWM_FREQUENCY_3K63_HZ 0b011\n#define IS31FL3746A_PWM_FREQUENCY_1K81_HZ 0b100\n#define IS31FL3746A_PWM_FREQUENCY_906_HZ 0b101\n#define IS31FL3746A_PWM_FREQUENCY_453_HZ 0b110\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n#define SW1_CS17 0x10\n#define SW1_CS18 0x11\n\n#define SW2_CS1 0x12\n#define SW2_CS2 0x13\n#define SW2_CS3 0x14\n#define SW2_CS4 0x15\n#define SW2_CS5 0x16\n#define SW2_CS6 0x17\n#define SW2_CS7 0x18\n#define SW2_CS8 0x19\n#define SW2_CS9 0x1A\n#define SW2_CS10 0x1B\n#define SW2_CS11 0x1C\n#define SW2_CS12 0x1D\n#define SW2_CS13 0x1E\n#define SW2_CS14 0x1F\n#define SW2_CS15 0x20\n#define SW2_CS16 0x21\n#define SW2_CS17 0x22\n#define SW2_CS18 0x23\n\n#define SW3_CS1 0x24\n#define SW3_CS2 0x25\n#define SW3_CS3 0x26\n#define SW3_CS4 0x27\n#define SW3_CS5 0x28\n#define SW3_CS6 0x29\n#define SW3_CS7 0x2A\n#define SW3_CS8 0x2B\n#define SW3_CS9 0x2C\n#define SW3_CS10 0x2D\n#define SW3_CS11 0x2E\n#define SW3_CS12 0x2F\n#define SW3_CS13 0x30\n#define SW3_CS14 0x31\n#define SW3_CS15 0x32\n#define SW3_CS16 0x33\n#define SW3_CS17 0x34\n#define SW3_CS18 0x35\n\n#define SW4_CS1 0x36\n#define SW4_CS2 0x37\n#define SW4_CS3 0x38\n#define SW4_CS4 0x39\n#define SW4_CS5 0x3A\n#define SW4_CS6 0x3B\n#define SW4_CS7 0x3C\n#define SW4_CS8 0x3D\n#define SW4_CS9 0x3E\n#define SW4_CS10 0x3F\n#define SW4_CS11 0x40\n#define SW4_CS12 0x41\n#define SW4_CS13 0x42\n#define SW4_CS14 0x43\n#define SW4_CS15 0x44\n#define SW4_CS16 0x45\n#define SW4_CS17 0x46\n#define SW4_CS18 0x47\n"
  },
  {
    "path": "drivers/led/issi/is31fl3746a.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"is31fl3746a.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n#define IS31FL3746A_PWM_REGISTER_COUNT 72\n#define IS31FL3746A_SCALING_REGISTER_COUNT 72\n\n#ifndef IS31FL3746A_I2C_TIMEOUT\n#    define IS31FL3746A_I2C_TIMEOUT 100\n#endif\n\n#ifndef IS31FL3746A_I2C_PERSISTENCE\n#    define IS31FL3746A_I2C_PERSISTENCE 0\n#endif\n\n#ifndef IS31FL3746A_CONFIGURATION\n#    define IS31FL3746A_CONFIGURATION 0x01\n#endif\n\n#ifndef IS31FL3746A_PWM_FREQUENCY\n#    define IS31FL3746A_PWM_FREQUENCY IS31FL3746A_PWM_FREQUENCY_29K_HZ\n#endif\n\n#ifndef IS31FL3746A_SW_PULLDOWN\n#    define IS31FL3746A_SW_PULLDOWN IS31FL3746A_PDR_2K_OHM_SW_OFF\n#endif\n\n#ifndef IS31FL3746A_CS_PULLUP\n#    define IS31FL3746A_CS_PULLUP IS31FL3746A_PUR_2K_OHM_CS_OFF\n#endif\n\n#ifndef IS31FL3746A_GLOBAL_CURRENT\n#    define IS31FL3746A_GLOBAL_CURRENT 0xFF\n#endif\n\nconst uint8_t i2c_addresses[IS31FL3746A_DRIVER_COUNT] = {\n    IS31FL3746A_I2C_ADDRESS_1,\n#ifdef IS31FL3746A_I2C_ADDRESS_2\n    IS31FL3746A_I2C_ADDRESS_2,\n#    ifdef IS31FL3746A_I2C_ADDRESS_3\n    IS31FL3746A_I2C_ADDRESS_3,\n#        ifdef IS31FL3746A_I2C_ADDRESS_4\n    IS31FL3746A_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\ntypedef struct is31fl3746a_driver_t {\n    uint8_t pwm_buffer[IS31FL3746A_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t scaling_buffer[IS31FL3746A_SCALING_REGISTER_COUNT];\n    bool    scaling_buffer_dirty;\n} PACKED is31fl3746a_driver_t;\n\nis31fl3746a_driver_t driver_buffers[IS31FL3746A_DRIVER_COUNT] = {{\n    .pwm_buffer           = {0},\n    .pwm_buffer_dirty     = false,\n    .scaling_buffer       = {0},\n    .scaling_buffer_dirty = false,\n}};\n\nvoid is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if IS31FL3746A_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < IS31FL3746A_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, IS31FL3746A_I2C_TIMEOUT);\n#endif\n}\n\nvoid is31fl3746a_select_page(uint8_t index, uint8_t page) {\n    is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND_WRITE_LOCK, IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC);\n    is31fl3746a_write_register(index, IS31FL3746A_REG_COMMAND, page);\n}\n\nvoid is31fl3746a_write_pwm_buffer(uint8_t index) {\n    // Assumes page 0 is already selected.\n    // Transmit PWM registers in 4 transfers of 18 bytes.\n\n    // Iterate over the pwm_buffer contents at 18 byte intervals.\n    for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i += 18) {\n#if IS31FL3746A_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < IS31FL3746A_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i + 1, driver_buffers[index].pwm_buffer + i, 18, IS31FL3746A_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid is31fl3746a_init_drivers(void) {\n    i2c_init();\n\n#if defined(IS31FL3746A_SDB_PIN)\n    gpio_set_pin_output(IS31FL3746A_SDB_PIN);\n    gpio_write_pin_high(IS31FL3746A_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {\n        is31fl3746a_init(i);\n    }\n\n    for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) {\n        is31fl3746a_set_scaling_register(i, 0xFF, 0xFF, 0xFF);\n    }\n\n    for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {\n        is31fl3746a_update_scaling_registers(i);\n    }\n}\n\nvoid is31fl3746a_init(uint8_t index) {\n    // In order to avoid the LEDs being driven with garbage data\n    // in the LED driver's PWM registers, shutdown is enabled last.\n    // Set up the mode and other settings, clear the PWM registers,\n    // then disable software shutdown.\n\n    is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING);\n\n    // Turn off all LEDs.\n    for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) {\n        is31fl3746a_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM);\n\n    for (uint8_t i = 0; i < IS31FL3746A_PWM_REGISTER_COUNT; i++) {\n        is31fl3746a_write_register(index, i + 1, 0x00);\n    }\n\n    is31fl3746a_select_page(index, IS31FL3746A_COMMAND_FUNCTION);\n\n    is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PULLDOWNUP, (IS31FL3746A_SW_PULLDOWN << 4) | IS31FL3746A_CS_PULLUP);\n    is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT, IS31FL3746A_GLOBAL_CURRENT);\n    is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_ENABLE, 0x01);\n    is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY, IS31FL3746A_PWM_FREQUENCY);\n    is31fl3746a_write_register(index, IS31FL3746A_FUNCTION_REG_CONFIGURATION, IS31FL3746A_CONFIGURATION);\n\n    // Wait 10ms to ensure the device has woken up.\n    wait_ms(10);\n}\n\nvoid is31fl3746a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3746a_led_t led;\n\n    if (index >= 0 && index < IS31FL3746A_LED_COUNT) {\n        memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.r] = red;\n        driver_buffers[led.driver].pwm_buffer[led.g] = green;\n        driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid is31fl3746a_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < IS31FL3746A_LED_COUNT; i++) {\n        is31fl3746a_set_color(i, red, green, blue);\n    }\n}\n\nvoid is31fl3746a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue) {\n    is31fl3746a_led_t led;\n    memcpy_P(&led, (&g_is31fl3746a_leds[index]), sizeof(led));\n\n    driver_buffers[led.driver].scaling_buffer[led.r] = red;\n    driver_buffers[led.driver].scaling_buffer[led.g] = green;\n    driver_buffers[led.driver].scaling_buffer[led.b] = blue;\n    driver_buffers[led.driver].scaling_buffer_dirty  = true;\n}\n\nvoid is31fl3746a_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        is31fl3746a_select_page(index, IS31FL3746A_COMMAND_PWM);\n\n        is31fl3746a_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3746a_update_scaling_registers(uint8_t index) {\n    if (driver_buffers[index].scaling_buffer_dirty) {\n        is31fl3746a_select_page(index, IS31FL3746A_COMMAND_SCALING);\n\n        for (uint8_t i = 0; i < IS31FL3746A_SCALING_REGISTER_COUNT; i++) {\n            is31fl3746a_write_register(index, i + 1, driver_buffers[index].scaling_buffer[i]);\n        }\n\n        driver_buffers[index].scaling_buffer_dirty = false;\n    }\n}\n\nvoid is31fl3746a_flush(void) {\n    for (uint8_t i = 0; i < IS31FL3746A_DRIVER_COUNT; i++) {\n        is31fl3746a_update_pwm_buffers(i);\n    }\n}\n"
  },
  {
    "path": "drivers/led/issi/is31fl3746a.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2020 MelGeek\n * Copyright 2021 MasterSpoon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define IS31FL3746A_REG_ID 0xFC\n\n#define IS31FL3746A_REG_COMMAND 0xFD\n\n#define IS31FL3746A_COMMAND_PWM 0x00\n#define IS31FL3746A_COMMAND_SCALING 0x01\n#define IS31FL3746A_COMMAND_FUNCTION 0x01\n\n#define IS31FL3746A_FUNCTION_REG_CONFIGURATION 0x50\n#define IS31FL3746A_FUNCTION_REG_GLOBAL_CURRENT 0x51\n#define IS31FL3746A_FUNCTION_REG_PULLDOWNUP 0x52\n#define IS31FL3746A_FUNCTION_REG_TEMPERATURE 0x5F\n#define IS31FL3746A_FUNCTION_REG_SPREAD_SPECTRUM 0x60\n#define IS31FL3746A_FUNCTION_REG_RESET 0x8F\n#define IS31FL3746A_FUNCTION_REG_PWM_ENABLE 0xE0\n#define IS31FL3746A_FUNCTION_REG_PWM_FREQUENCY 0xE2\n\n#define IS31FL3746A_REG_COMMAND_WRITE_LOCK 0xFE\n#define IS31FL3746A_COMMAND_WRITE_LOCK_MAGIC 0xC5\n\n#define IS31FL3746A_I2C_ADDRESS_GND_GND 0x60\n#define IS31FL3746A_I2C_ADDRESS_GND_SCL 0x61\n#define IS31FL3746A_I2C_ADDRESS_GND_SDA 0x62\n#define IS31FL3746A_I2C_ADDRESS_GND_VCC 0x63\n#define IS31FL3746A_I2C_ADDRESS_SCL_GND 0x64\n#define IS31FL3746A_I2C_ADDRESS_SCL_SCL 0x65\n#define IS31FL3746A_I2C_ADDRESS_SCL_SDA 0x66\n#define IS31FL3746A_I2C_ADDRESS_SCL_VCC 0x67\n#define IS31FL3746A_I2C_ADDRESS_SDA_GND 0x68\n#define IS31FL3746A_I2C_ADDRESS_SDA_SCL 0x69\n#define IS31FL3746A_I2C_ADDRESS_SDA_SDA 0x6A\n#define IS31FL3746A_I2C_ADDRESS_SDA_VCC 0x6B\n#define IS31FL3746A_I2C_ADDRESS_VCC_GND 0x6C\n#define IS31FL3746A_I2C_ADDRESS_VCC_SCL 0x6D\n#define IS31FL3746A_I2C_ADDRESS_VCC_SDA 0x6E\n#define IS31FL3746A_I2C_ADDRESS_VCC_VCC 0x6F\n\n#if defined(RGB_MATRIX_IS31FL3746A)\n#    define IS31FL3746A_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(IS31FL3746A_I2C_ADDRESS_4)\n#    define IS31FL3746A_DRIVER_COUNT 4\n#elif defined(IS31FL3746A_I2C_ADDRESS_3)\n#    define IS31FL3746A_DRIVER_COUNT 3\n#elif defined(IS31FL3746A_I2C_ADDRESS_2)\n#    define IS31FL3746A_DRIVER_COUNT 2\n#elif defined(IS31FL3746A_I2C_ADDRESS_1)\n#    define IS31FL3746A_DRIVER_COUNT 1\n#endif\n\ntypedef struct is31fl3746a_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED is31fl3746a_led_t;\n\nextern const is31fl3746a_led_t PROGMEM g_is31fl3746a_leds[IS31FL3746A_LED_COUNT];\n\nvoid is31fl3746a_init_drivers(void);\nvoid is31fl3746a_init(uint8_t index);\nvoid is31fl3746a_write_register(uint8_t index, uint8_t reg, uint8_t data);\nvoid is31fl3746a_select_page(uint8_t index, uint8_t page);\n\nvoid is31fl3746a_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid is31fl3746a_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3746a_set_scaling_register(uint8_t index, uint8_t red, uint8_t green, uint8_t blue);\n\nvoid is31fl3746a_update_pwm_buffers(uint8_t index);\nvoid is31fl3746a_update_scaling_registers(uint8_t index);\n\nvoid is31fl3746a_flush(void);\n\n#define IS31FL3746A_PDR_0_OHM 0b000          // No pull-down resistor\n#define IS31FL3746A_PDR_0K5_OHM_SW_OFF 0b001 // 0.5 kOhm resistor in SWx off time\n#define IS31FL3746A_PDR_1K_OHM_SW_OFF 0b010  // 1 kOhm resistor in SWx off time\n#define IS31FL3746A_PDR_2K_OHM_SW_OFF 0b011  // 2 kOhm resistor in SWx off time\n#define IS31FL3746A_PDR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3746A_PDR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3746A_PDR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3746A_PDR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3746A_PUR_0_OHM 0b000          // No pull-up resistor\n#define IS31FL3746A_PUR_0K5_OHM_CS_OFF 0b001 // 0.5 kOhm resistor in CSy off time\n#define IS31FL3746A_PUR_1K_OHM_CS_OFF 0b010  // 1 kOhm resistor in CSy off time\n#define IS31FL3746A_PUR_2K_OHM_CS_OFF 0b011  // 2 kOhm resistor in CSy off time\n#define IS31FL3746A_PUR_1K_OHM 0b100         // 1 kOhm resistor\n#define IS31FL3746A_PUR_2K_OHM 0b101         // 2 kOhm resistor\n#define IS31FL3746A_PUR_4K_OHM 0b110         // 4 kOhm resistor\n#define IS31FL3746A_PUR_8K_OHM 0b111         // 8 kOhm resistor\n\n#define IS31FL3746A_PWM_FREQUENCY_29K_HZ 0b000\n#define IS31FL3746A_PWM_FREQUENCY_14K5_HZ 0b001\n#define IS31FL3746A_PWM_FREQUENCY_7K25_HZ 0b010\n#define IS31FL3746A_PWM_FREQUENCY_3K63_HZ 0b011\n#define IS31FL3746A_PWM_FREQUENCY_1K81_HZ 0b100\n#define IS31FL3746A_PWM_FREQUENCY_906_HZ 0b101\n#define IS31FL3746A_PWM_FREQUENCY_453_HZ 0b110\n\n#define SW1_CS1 0x00\n#define SW1_CS2 0x01\n#define SW1_CS3 0x02\n#define SW1_CS4 0x03\n#define SW1_CS5 0x04\n#define SW1_CS6 0x05\n#define SW1_CS7 0x06\n#define SW1_CS8 0x07\n#define SW1_CS9 0x08\n#define SW1_CS10 0x09\n#define SW1_CS11 0x0A\n#define SW1_CS12 0x0B\n#define SW1_CS13 0x0C\n#define SW1_CS14 0x0D\n#define SW1_CS15 0x0E\n#define SW1_CS16 0x0F\n#define SW1_CS17 0x10\n#define SW1_CS18 0x11\n\n#define SW2_CS1 0x12\n#define SW2_CS2 0x13\n#define SW2_CS3 0x14\n#define SW2_CS4 0x15\n#define SW2_CS5 0x16\n#define SW2_CS6 0x17\n#define SW2_CS7 0x18\n#define SW2_CS8 0x19\n#define SW2_CS9 0x1A\n#define SW2_CS10 0x1B\n#define SW2_CS11 0x1C\n#define SW2_CS12 0x1D\n#define SW2_CS13 0x1E\n#define SW2_CS14 0x1F\n#define SW2_CS15 0x20\n#define SW2_CS16 0x21\n#define SW2_CS17 0x22\n#define SW2_CS18 0x23\n\n#define SW3_CS1 0x24\n#define SW3_CS2 0x25\n#define SW3_CS3 0x26\n#define SW3_CS4 0x27\n#define SW3_CS5 0x28\n#define SW3_CS6 0x29\n#define SW3_CS7 0x2A\n#define SW3_CS8 0x2B\n#define SW3_CS9 0x2C\n#define SW3_CS10 0x2D\n#define SW3_CS11 0x2E\n#define SW3_CS12 0x2F\n#define SW3_CS13 0x30\n#define SW3_CS14 0x31\n#define SW3_CS15 0x32\n#define SW3_CS16 0x33\n#define SW3_CS17 0x34\n#define SW3_CS18 0x35\n\n#define SW4_CS1 0x36\n#define SW4_CS2 0x37\n#define SW4_CS3 0x38\n#define SW4_CS4 0x39\n#define SW4_CS5 0x3A\n#define SW4_CS6 0x3B\n#define SW4_CS7 0x3C\n#define SW4_CS8 0x3D\n#define SW4_CS9 0x3E\n#define SW4_CS10 0x3F\n#define SW4_CS11 0x40\n#define SW4_CS12 0x41\n#define SW4_CS13 0x42\n#define SW4_CS14 0x43\n#define SW4_CS15 0x44\n#define SW4_CS16 0x45\n#define SW4_CS17 0x46\n#define SW4_CS18 0x47\n"
  },
  {
    "path": "drivers/led/snled27351-mono.c",
    "content": "/* Copyright 2021 @ Keychron (https://www.keychron.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"snled27351-mono.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n\n#define SNLED27351_PWM_REGISTER_COUNT 192\n#define SNLED27351_LED_CONTROL_REGISTER_COUNT 24\n\n#ifndef SNLED27351_I2C_TIMEOUT\n#    define SNLED27351_I2C_TIMEOUT 100\n#endif\n\n#ifndef SNLED27351_I2C_PERSISTENCE\n#    define SNLED27351_I2C_PERSISTENCE 0\n#endif\n\n#ifndef SNLED27351_PHASE_CHANNEL\n#    define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL\n#endif\n\n#ifndef SNLED27351_CURRENT_TUNE\n#    define SNLED27351_CURRENT_TUNE \\\n        { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }\n#endif\n\nconst uint8_t i2c_addresses[SNLED27351_DRIVER_COUNT] = {\n    SNLED27351_I2C_ADDRESS_1,\n#ifdef SNLED27351_I2C_ADDRESS_2\n    SNLED27351_I2C_ADDRESS_2,\n#    ifdef SNLED27351_I2C_ADDRESS_3\n    SNLED27351_I2C_ADDRESS_3,\n#        ifdef SNLED27351_I2C_ADDRESS_4\n    SNLED27351_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the SNLED27351 PWM registers.\n// The control buffers match the PG0 LED On/Off registers.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in snled27351_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct snled27351_driver_t {\n    uint8_t pwm_buffer[SNLED27351_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[SNLED27351_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED snled27351_driver_t;\n\nsnled27351_driver_t driver_buffers[SNLED27351_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if SNLED27351_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT);\n#endif\n}\n\nvoid snled27351_select_page(uint8_t index, uint8_t page) {\n    snled27351_write_register(index, SNLED27351_REG_COMMAND, page);\n}\n\nvoid snled27351_write_pwm_buffer(uint8_t index) {\n    // Assumes PG1 is already selected.\n    // Transmit PWM registers in 12 transfers of 16 bytes.\n\n    // Iterate over the pwm_buffer contents at 16 byte intervals.\n    for (uint8_t i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 16) {\n#if SNLED27351_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < SNLED27351_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid snled27351_init_drivers(void) {\n    i2c_init();\n\n#if defined(SNLED27351_SDB_PIN)\n    gpio_set_pin_output(SNLED27351_SDB_PIN);\n    gpio_write_pin_high(SNLED27351_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {\n        snled27351_init(i);\n    }\n\n    for (int i = 0; i < SNLED27351_LED_COUNT; i++) {\n        snled27351_set_led_control_register(i, true);\n    }\n\n    for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {\n        snled27351_update_led_control_registers(i);\n    }\n}\n\nvoid snled27351_init(uint8_t index) {\n    snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);\n\n    // Setting LED driver to shutdown mode\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);\n    // Setting internal channel pulldown/pullup\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED);\n    // Select number of scan phase\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL);\n    // Setting PWM Delay Phase\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE);\n    // Setting Driving/Sinking Channel Slew Rate\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE);\n    // Setting Iref\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0);\n\n    snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);\n\n    for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {\n        snled27351_write_register(index, i, 0x00);\n    }\n\n    snled27351_select_page(index, SNLED27351_COMMAND_PWM);\n\n    for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {\n        snled27351_write_register(index, i, 0x00);\n    }\n\n    snled27351_select_page(index, SNLED27351_COMMAND_CURRENT_TUNE);\n\n    uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE;\n    for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {\n        snled27351_write_register(index, i, current_tune_reg_list[i]);\n    }\n\n    snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);\n\n    for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {\n        snled27351_write_register(index, i, 0xFF);\n    }\n\n    snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);\n\n    // Setting LED driver to normal mode\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);\n}\n\nvoid snled27351_set_value(int index, uint8_t value) {\n    snled27351_led_t led;\n    if (index >= 0 && index < SNLED27351_LED_COUNT) {\n        memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.v] == value) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.v] = value;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid snled27351_set_value_all(uint8_t value) {\n    for (int i = 0; i < SNLED27351_LED_COUNT; i++) {\n        snled27351_set_value(i, value);\n    }\n}\n\nvoid snled27351_set_led_control_register(uint8_t index, bool value) {\n    snled27351_led_t led;\n    memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led));\n\n    uint8_t control_register = led.v / 8;\n    uint8_t bit_value        = led.v % 8;\n\n    if (value) {\n        driver_buffers[led.driver].led_control_buffer[control_register] |= (1 << bit_value);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register] &= ~(1 << bit_value);\n    }\n\n    driver_buffers[led.driver].led_control_buffer_dirty = true;\n}\n\nvoid snled27351_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        snled27351_select_page(index, SNLED27351_COMMAND_PWM);\n\n        snled27351_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid snled27351_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);\n\n        for (uint8_t i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) {\n            snled27351_write_register(index, i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid snled27351_flush(void) {\n    for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {\n        snled27351_update_pwm_buffers(i);\n    }\n}\n\nvoid snled27351_sw_return_normal(uint8_t index) {\n    snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);\n\n    // Setting LED driver to normal mode\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);\n}\n\nvoid snled27351_sw_shutdown(uint8_t index) {\n    snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);\n\n    // Setting LED driver to shutdown mode\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);\n    // Write SW Sleep Register\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE);\n}\n"
  },
  {
    "path": "drivers/led/snled27351-mono.h",
    "content": "/* Copyright 2021 @ Keychron (https://www.keychron.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define SNLED27351_REG_COMMAND 0xFD\n#define SNLED27351_COMMAND_LED_CONTROL 0x00\n#define SNLED27351_COMMAND_PWM 0x01\n#define SNLED27351_COMMAND_FUNCTION 0x03\n#define SNLED27351_COMMAND_CURRENT_TUNE 0x04\n\n#define SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN 0x00\n#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN (0x0 << 0)\n#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL (0x1 << 0)\n\n#define SNLED27351_FUNCTION_REG_ID 0x11\n#define SNLED27351_DRIVER_ID 0x8A\n\n#define SNLED27351_FUNCTION_REG_PULLDOWNUP 0x13\n#define SNLED27351_PULLDOWNUP_ALL_ENABLED 0xAA\n\n#define SNLED27351_FUNCTION_REG_SCAN_PHASE 0x14\n#define SNLED27351_SCAN_PHASE_12_CHANNEL 0x00\n#define SNLED27351_SCAN_PHASE_11_CHANNEL 0x01\n#define SNLED27351_SCAN_PHASE_10_CHANNEL 0x02\n#define SNLED27351_SCAN_PHASE_9_CHANNEL 0x03\n#define SNLED27351_SCAN_PHASE_8_CHANNEL 0x04\n#define SNLED27351_SCAN_PHASE_7_CHANNEL 0x05\n#define SNLED27351_SCAN_PHASE_6_CHANNEL 0x06\n#define SNLED27351_SCAN_PHASE_5_CHANNEL 0x07\n#define SNLED27351_SCAN_PHASE_4_CHANNEL 0x08\n#define SNLED27351_SCAN_PHASE_3_CHANNEL 0x09\n#define SNLED27351_SCAN_PHASE_2_CHANNEL 0x0A\n#define SNLED27351_SCAN_PHASE_1_CHANNEL 0x0B\n\n#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1 0x15\n#define SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE (0b1 << 2)\n\n#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2 0x16\n#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE (0b1 << 6)\n#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE (0b1 << 7)\n\n#define SNLED27351_FUNCTION_REG_OPEN_SHORT_ENABLE 0x17\n#define SNLED27351_OPEN_SHORT_ENABLE_SDS_ENABLE (0b1 << 6)\n#define SNLED27351_OPEN_SHORT_ENABLE_ODS_ENABLE (0b1 << 7)\n\n#define SNLED27351_FUNCTION_REG_OPEN_SHORT_DUTY 0x18\n\n#define SNLED27351_FUNCTION_REG_OPEN_SHORT_FLAG 0x19\n#define SNLED27351_OPEN_SHORT_FLAG_OSINT_ENABLE (0b1 << 6)\n#define SNLED27351_OPEN_SHORT_FLAG_ODINT_ENABLE (0b1 << 7)\n\n#define SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP 0x1A\n#define SNLED27351_SOFTWARE_SLEEP_ENABLE (0b1 << 1)\n\n// LED Control Registers\n#define SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR 0x0\n#define SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR 0x17\n#define SNLED27351_LED_CONTROL_ON_OFF_LENGTH ((SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR - SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR) + 1)\n\n#define SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR 0x18\n#define SNLED27351_LED_CONTROL_OPEN_LAST_ADDR 0x2F\n#define SNLED27351_LED_CONTROL_OPEN_LENGTH ((SNLED27351_LED_CONTROL_OPEN_LAST_ADDR - SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR) + 1)\n\n#define SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR 0x30\n#define SNLED27351_LED_CONTROL_SHORT_LAST_ADDR 0x47\n#define SNLED27351_LED_CONTROL_SHORT_LENGTH ((SNLED27351_LED_CONTROL_SHORT_LAST_ADDR - SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR) + 1)\n\n#define SNLED27351_LED_CONTROL_PAGE_LENGTH 0x48\n\n// LED Control Registers\n#define SNLED27351_LED_PWM_FIRST_ADDR 0x00\n#define SNLED27351_LED_PWM_LAST_ADDR 0xBF\n#define SNLED27351_LED_PWM_LENGTH 0xC0\n\n// Current Tune Registers\n#define SNLED27351_LED_CURRENT_TUNE_FIRST_ADDR 0x00\n#define SNLED27351_LED_CURRENT_TUNE_LAST_ADDR 0x0B\n#define SNLED27351_LED_CURRENT_TUNE_LENGTH 0x0C\n\n#define SNLED27351_I2C_ADDRESS_GND 0x74\n#define SNLED27351_I2C_ADDRESS_SCL 0x75\n#define SNLED27351_I2C_ADDRESS_SDA 0x76\n#define SNLED27351_I2C_ADDRESS_VDDIO 0x77\n\n#if defined(LED_MATRIX_SNLED27351)\n#    define SNLED27351_LED_COUNT LED_MATRIX_LED_COUNT\n#endif\n\n#if defined(SNLED27351_I2C_ADDRESS_4)\n#    define SNLED27351_DRIVER_COUNT 4\n#elif defined(SNLED27351_I2C_ADDRESS_3)\n#    define SNLED27351_DRIVER_COUNT 3\n#elif defined(SNLED27351_I2C_ADDRESS_2)\n#    define SNLED27351_DRIVER_COUNT 2\n#elif defined(SNLED27351_I2C_ADDRESS_1)\n#    define SNLED27351_DRIVER_COUNT 1\n#endif\n\ntypedef struct snled27351_led_t {\n    uint8_t driver : 2;\n    uint8_t v;\n} PACKED snled27351_led_t;\n\nextern const snled27351_led_t PROGMEM g_snled27351_leds[SNLED27351_LED_COUNT];\n\nvoid snled27351_init_drivers(void);\nvoid snled27351_init(uint8_t index);\nvoid snled27351_select_page(uint8_t index, uint8_t page);\nvoid snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data);\n\nvoid snled27351_set_value(int index, uint8_t value);\nvoid snled27351_set_value_all(uint8_t value);\n\nvoid snled27351_set_led_control_register(uint8_t index, bool value);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid snled27351_update_pwm_buffers(uint8_t index);\nvoid snled27351_update_led_control_registers(uint8_t index);\n\nvoid snled27351_flush(void);\n\nvoid snled27351_sw_return_normal(uint8_t index);\nvoid snled27351_sw_shutdown(uint8_t index);\n\n#define CB1_CA1 0x00\n#define CB1_CA2 0x01\n#define CB1_CA3 0x02\n#define CB1_CA4 0x03\n#define CB1_CA5 0x04\n#define CB1_CA6 0x05\n#define CB1_CA7 0x06\n#define CB1_CA8 0x07\n#define CB1_CA9 0x08\n#define CB1_CA10 0x09\n#define CB1_CA11 0x0A\n#define CB1_CA12 0x0B\n#define CB1_CA13 0x0C\n#define CB1_CA14 0x0D\n#define CB1_CA15 0x0E\n#define CB1_CA16 0x0F\n\n#define CB2_CA1 0x10\n#define CB2_CA2 0x11\n#define CB2_CA3 0x12\n#define CB2_CA4 0x13\n#define CB2_CA5 0x14\n#define CB2_CA6 0x15\n#define CB2_CA7 0x16\n#define CB2_CA8 0x17\n#define CB2_CA9 0x18\n#define CB2_CA10 0x19\n#define CB2_CA11 0x1A\n#define CB2_CA12 0x1B\n#define CB2_CA13 0x1C\n#define CB2_CA14 0x1D\n#define CB2_CA15 0x1E\n#define CB2_CA16 0x1F\n\n#define CB3_CA1 0x20\n#define CB3_CA2 0x21\n#define CB3_CA3 0x22\n#define CB3_CA4 0x23\n#define CB3_CA5 0x24\n#define CB3_CA6 0x25\n#define CB3_CA7 0x26\n#define CB3_CA8 0x27\n#define CB3_CA9 0x28\n#define CB3_CA10 0x29\n#define CB3_CA11 0x2A\n#define CB3_CA12 0x2B\n#define CB3_CA13 0x2C\n#define CB3_CA14 0x2D\n#define CB3_CA15 0x2E\n#define CB3_CA16 0x2F\n\n#define CB4_CA1 0x30\n#define CB4_CA2 0x31\n#define CB4_CA3 0x32\n#define CB4_CA4 0x33\n#define CB4_CA5 0x34\n#define CB4_CA6 0x35\n#define CB4_CA7 0x36\n#define CB4_CA8 0x37\n#define CB4_CA9 0x38\n#define CB4_CA10 0x39\n#define CB4_CA11 0x3A\n#define CB4_CA12 0x3B\n#define CB4_CA13 0x3C\n#define CB4_CA14 0x3D\n#define CB4_CA15 0x3E\n#define CB4_CA16 0x3F\n\n#define CB5_CA1 0x40\n#define CB5_CA2 0x41\n#define CB5_CA3 0x42\n#define CB5_CA4 0x43\n#define CB5_CA5 0x44\n#define CB5_CA6 0x45\n#define CB5_CA7 0x46\n#define CB5_CA8 0x47\n#define CB5_CA9 0x48\n#define CB5_CA10 0x49\n#define CB5_CA11 0x4A\n#define CB5_CA12 0x4B\n#define CB5_CA13 0x4C\n#define CB5_CA14 0x4D\n#define CB5_CA15 0x4E\n#define CB5_CA16 0x4F\n\n#define CB6_CA1 0x50\n#define CB6_CA2 0x51\n#define CB6_CA3 0x52\n#define CB6_CA4 0x53\n#define CB6_CA5 0x54\n#define CB6_CA6 0x55\n#define CB6_CA7 0x56\n#define CB6_CA8 0x57\n#define CB6_CA9 0x58\n#define CB6_CA10 0x59\n#define CB6_CA11 0x5A\n#define CB6_CA12 0x5B\n#define CB6_CA13 0x5C\n#define CB6_CA14 0x5D\n#define CB6_CA15 0x5E\n#define CB6_CA16 0x5F\n\n#define CB7_CA1 0x60\n#define CB7_CA2 0x61\n#define CB7_CA3 0x62\n#define CB7_CA4 0x63\n#define CB7_CA5 0x64\n#define CB7_CA6 0x65\n#define CB7_CA7 0x66\n#define CB7_CA8 0x67\n#define CB7_CA9 0x68\n#define CB7_CA10 0x69\n#define CB7_CA11 0x6A\n#define CB7_CA12 0x6B\n#define CB7_CA13 0x6C\n#define CB7_CA14 0x6D\n#define CB7_CA15 0x6E\n#define CB7_CA16 0x6F\n\n#define CB8_CA1 0x70\n#define CB8_CA2 0x71\n#define CB8_CA3 0x72\n#define CB8_CA4 0x73\n#define CB8_CA5 0x74\n#define CB8_CA6 0x75\n#define CB8_CA7 0x76\n#define CB8_CA8 0x77\n#define CB8_CA9 0x78\n#define CB8_CA10 0x79\n#define CB8_CA11 0x7A\n#define CB8_CA12 0x7B\n#define CB8_CA13 0x7C\n#define CB8_CA14 0x7D\n#define CB8_CA15 0x7E\n#define CB8_CA16 0x7F\n\n#define CB9_CA1 0x80\n#define CB9_CA2 0x81\n#define CB9_CA3 0x82\n#define CB9_CA4 0x83\n#define CB9_CA5 0x84\n#define CB9_CA6 0x85\n#define CB9_CA7 0x86\n#define CB9_CA8 0x87\n#define CB9_CA9 0x88\n#define CB9_CA10 0x89\n#define CB9_CA11 0x8A\n#define CB9_CA12 0x8B\n#define CB9_CA13 0x8C\n#define CB9_CA14 0x8D\n#define CB9_CA15 0x8E\n#define CB9_CA16 0x8F\n\n#define CB10_CA1 0x90\n#define CB10_CA2 0x91\n#define CB10_CA3 0x92\n#define CB10_CA4 0x93\n#define CB10_CA5 0x94\n#define CB10_CA6 0x95\n#define CB10_CA7 0x96\n#define CB10_CA8 0x97\n#define CB10_CA9 0x98\n#define CB10_CA10 0x99\n#define CB10_CA11 0x9A\n#define CB10_CA12 0x9B\n#define CB10_CA13 0x9C\n#define CB10_CA14 0x9D\n#define CB10_CA15 0x9E\n#define CB10_CA16 0x9F\n\n#define CB11_CA1 0xA0\n#define CB11_CA2 0xA1\n#define CB11_CA3 0xA2\n#define CB11_CA4 0xA3\n#define CB11_CA5 0xA4\n#define CB11_CA6 0xA5\n#define CB11_CA7 0xA6\n#define CB11_CA8 0xA7\n#define CB11_CA9 0xA8\n#define CB11_CA10 0xA9\n#define CB11_CA11 0xAA\n#define CB11_CA12 0xAB\n#define CB11_CA13 0xAC\n#define CB11_CA14 0xAD\n#define CB11_CA15 0xAE\n#define CB11_CA16 0xAF\n\n#define CB12_CA1 0xB0\n#define CB12_CA2 0xB1\n#define CB12_CA3 0xB2\n#define CB12_CA4 0xB3\n#define CB12_CA5 0xB4\n#define CB12_CA6 0xB5\n#define CB12_CA7 0xB6\n#define CB12_CA8 0xB7\n#define CB12_CA9 0xB8\n#define CB12_CA10 0xB9\n#define CB12_CA11 0xBA\n#define CB12_CA12 0xBB\n#define CB12_CA13 0xBC\n#define CB12_CA14 0xBD\n#define CB12_CA15 0xBE\n#define CB12_CA16 0xBF\n"
  },
  {
    "path": "drivers/led/snled27351.c",
    "content": "/* Copyright 2021 @ Keychron (https://www.keychron.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"snled27351.h\"\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n\n#define SNLED27351_PWM_REGISTER_COUNT 192\n#define SNLED27351_LED_CONTROL_REGISTER_COUNT 24\n\n#ifndef SNLED27351_I2C_TIMEOUT\n#    define SNLED27351_I2C_TIMEOUT 100\n#endif\n\n#ifndef SNLED27351_I2C_PERSISTENCE\n#    define SNLED27351_I2C_PERSISTENCE 0\n#endif\n\n#ifndef SNLED27351_PHASE_CHANNEL\n#    define SNLED27351_PHASE_CHANNEL SNLED27351_SCAN_PHASE_12_CHANNEL\n#endif\n\n#ifndef SNLED27351_CURRENT_TUNE\n#    define SNLED27351_CURRENT_TUNE \\\n        { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }\n#endif\n\nconst uint8_t i2c_addresses[SNLED27351_DRIVER_COUNT] = {\n    SNLED27351_I2C_ADDRESS_1,\n#ifdef SNLED27351_I2C_ADDRESS_2\n    SNLED27351_I2C_ADDRESS_2,\n#    ifdef SNLED27351_I2C_ADDRESS_3\n    SNLED27351_I2C_ADDRESS_3,\n#        ifdef SNLED27351_I2C_ADDRESS_4\n    SNLED27351_I2C_ADDRESS_4,\n#        endif\n#    endif\n#endif\n};\n\n// These buffers match the SNLED27351 PWM registers.\n// The control buffers match the PG0 LED On/Off registers.\n// Storing them like this is optimal for I2C transfers to the registers.\n// We could optimize this and take out the unused registers from these\n// buffers and the transfers in snled27351_write_pwm_buffer() but it's\n// probably not worth the extra complexity.\ntypedef struct snled27351_driver_t {\n    uint8_t pwm_buffer[SNLED27351_PWM_REGISTER_COUNT];\n    bool    pwm_buffer_dirty;\n    uint8_t led_control_buffer[SNLED27351_LED_CONTROL_REGISTER_COUNT];\n    bool    led_control_buffer_dirty;\n} PACKED snled27351_driver_t;\n\nsnled27351_driver_t driver_buffers[SNLED27351_DRIVER_COUNT] = {{\n    .pwm_buffer               = {0},\n    .pwm_buffer_dirty         = false,\n    .led_control_buffer       = {0},\n    .led_control_buffer_dirty = false,\n}};\n\nvoid snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data) {\n#if SNLED27351_I2C_PERSISTENCE > 0\n    for (uint8_t i = 0; i < SNLED27351_I2C_PERSISTENCE; i++) {\n        if (i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n    }\n#else\n    i2c_write_register(i2c_addresses[index] << 1, reg, &data, 1, SNLED27351_I2C_TIMEOUT);\n#endif\n}\n\nvoid snled27351_select_page(uint8_t index, uint8_t page) {\n    snled27351_write_register(index, SNLED27351_REG_COMMAND, page);\n}\n\nvoid snled27351_write_pwm_buffer(uint8_t index) {\n    // Assumes PG1 is already selected.\n    // Transmit PWM registers in 12 transfers of 16 bytes.\n\n    // Iterate over the pwm_buffer contents at 16 byte intervals.\n    for (uint8_t i = 0; i < SNLED27351_PWM_REGISTER_COUNT; i += 16) {\n#if SNLED27351_I2C_PERSISTENCE > 0\n        for (uint8_t j = 0; j < SNLED27351_I2C_PERSISTENCE; j++) {\n            if (i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT) == I2C_STATUS_SUCCESS) break;\n        }\n#else\n        i2c_write_register(i2c_addresses[index] << 1, i, driver_buffers[index].pwm_buffer + i, 16, SNLED27351_I2C_TIMEOUT);\n#endif\n    }\n}\n\nvoid snled27351_init_drivers(void) {\n    i2c_init();\n\n#if defined(SNLED27351_SDB_PIN)\n    gpio_set_pin_output(SNLED27351_SDB_PIN);\n    gpio_write_pin_high(SNLED27351_SDB_PIN);\n#endif\n\n    for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {\n        snled27351_init(i);\n    }\n\n    for (int i = 0; i < SNLED27351_LED_COUNT; i++) {\n        snled27351_set_led_control_register(i, true, true, true);\n    }\n\n    for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {\n        snled27351_update_led_control_registers(i);\n    }\n}\n\nvoid snled27351_init(uint8_t index) {\n    snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);\n\n    // Setting LED driver to shutdown mode\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);\n    // Setting internal channel pulldown/pullup\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_PULLDOWNUP, SNLED27351_PULLDOWNUP_ALL_ENABLED);\n    // Select number of scan phase\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SCAN_PHASE, SNLED27351_PHASE_CHANNEL);\n    // Setting PWM Delay Phase\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1, SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE);\n    // Setting Driving/Sinking Channel Slew Rate\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2, SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE | SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE);\n    // Setting Iref\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, 0);\n\n    snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);\n\n    for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {\n        snled27351_write_register(index, i, 0x00);\n    }\n\n    snled27351_select_page(index, SNLED27351_COMMAND_PWM);\n\n    for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {\n        snled27351_write_register(index, i, 0x00);\n    }\n\n    snled27351_select_page(index, SNLED27351_COMMAND_CURRENT_TUNE);\n\n    uint8_t current_tune_reg_list[SNLED27351_LED_CURRENT_TUNE_LENGTH] = SNLED27351_CURRENT_TUNE;\n    for (int i = 0; i < SNLED27351_LED_CURRENT_TUNE_LENGTH; i++) {\n        snled27351_write_register(index, i, current_tune_reg_list[i]);\n    }\n\n    snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);\n\n    for (int i = 0; i < SNLED27351_LED_CONTROL_ON_OFF_LENGTH; i++) {\n        snled27351_write_register(index, i, 0xFF);\n    }\n\n    snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);\n\n    // Setting LED driver to normal mode\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);\n}\n\nvoid snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    snled27351_led_t led;\n    if (index >= 0 && index < SNLED27351_LED_COUNT) {\n        memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led));\n\n        if (driver_buffers[led.driver].pwm_buffer[led.r] == red && driver_buffers[led.driver].pwm_buffer[led.g] == green && driver_buffers[led.driver].pwm_buffer[led.b] == blue) {\n            return;\n        }\n\n        driver_buffers[led.driver].pwm_buffer[led.r] = red;\n        driver_buffers[led.driver].pwm_buffer[led.g] = green;\n        driver_buffers[led.driver].pwm_buffer[led.b] = blue;\n        driver_buffers[led.driver].pwm_buffer_dirty  = true;\n    }\n}\n\nvoid snled27351_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < SNLED27351_LED_COUNT; i++) {\n        snled27351_set_color(i, red, green, blue);\n    }\n}\n\nvoid snled27351_set_led_control_register(uint8_t index, bool red, bool green, bool blue) {\n    snled27351_led_t led;\n    memcpy_P(&led, (&g_snled27351_leds[index]), sizeof(led));\n\n    uint8_t control_register_r = led.r / 8;\n    uint8_t control_register_g = led.g / 8;\n    uint8_t control_register_b = led.b / 8;\n    uint8_t bit_r              = led.r % 8;\n    uint8_t bit_g              = led.g % 8;\n    uint8_t bit_b              = led.b % 8;\n\n    if (red) {\n        driver_buffers[led.driver].led_control_buffer[control_register_r] |= (1 << bit_r);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_r] &= ~(1 << bit_r);\n    }\n    if (green) {\n        driver_buffers[led.driver].led_control_buffer[control_register_g] |= (1 << bit_g);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_g] &= ~(1 << bit_g);\n    }\n    if (blue) {\n        driver_buffers[led.driver].led_control_buffer[control_register_b] |= (1 << bit_b);\n    } else {\n        driver_buffers[led.driver].led_control_buffer[control_register_b] &= ~(1 << bit_b);\n    }\n\n    driver_buffers[led.driver].led_control_buffer_dirty = true;\n}\n\nvoid snled27351_update_pwm_buffers(uint8_t index) {\n    if (driver_buffers[index].pwm_buffer_dirty) {\n        snled27351_select_page(index, SNLED27351_COMMAND_PWM);\n\n        snled27351_write_pwm_buffer(index);\n\n        driver_buffers[index].pwm_buffer_dirty = false;\n    }\n}\n\nvoid snled27351_update_led_control_registers(uint8_t index) {\n    if (driver_buffers[index].led_control_buffer_dirty) {\n        snled27351_select_page(index, SNLED27351_COMMAND_LED_CONTROL);\n\n        for (uint8_t i = 0; i < SNLED27351_LED_CONTROL_REGISTER_COUNT; i++) {\n            snled27351_write_register(index, i, driver_buffers[index].led_control_buffer[i]);\n        }\n\n        driver_buffers[index].led_control_buffer_dirty = false;\n    }\n}\n\nvoid snled27351_flush(void) {\n    for (uint8_t i = 0; i < SNLED27351_DRIVER_COUNT; i++) {\n        snled27351_update_pwm_buffers(i);\n    }\n}\n\nvoid snled27351_sw_return_normal(uint8_t index) {\n    snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);\n\n    // Setting LED driver to normal mode\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL);\n}\n\nvoid snled27351_sw_shutdown(uint8_t index) {\n    snled27351_select_page(index, SNLED27351_COMMAND_FUNCTION);\n\n    // Setting LED driver to shutdown mode\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN, SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN);\n    // Write SW Sleep Register\n    snled27351_write_register(index, SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP, SNLED27351_SOFTWARE_SLEEP_ENABLE);\n}\n"
  },
  {
    "path": "drivers/led/snled27351.h",
    "content": "/* Copyright 2021 @ Keychron (https://www.keychron.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"util.h\"\n\n#define SNLED27351_REG_COMMAND 0xFD\n#define SNLED27351_COMMAND_LED_CONTROL 0x00\n#define SNLED27351_COMMAND_PWM 0x01\n#define SNLED27351_COMMAND_FUNCTION 0x03\n#define SNLED27351_COMMAND_CURRENT_TUNE 0x04\n\n#define SNLED27351_FUNCTION_REG_SOFTWARE_SHUTDOWN 0x00\n#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_SHUTDOWN (0x0 << 0)\n#define SNLED27351_SOFTWARE_SHUTDOWN_SSD_NORMAL (0x1 << 0)\n\n#define SNLED27351_FUNCTION_REG_ID 0x11\n#define SNLED27351_DRIVER_ID 0x8A\n\n#define SNLED27351_FUNCTION_REG_PULLDOWNUP 0x13\n#define SNLED27351_PULLDOWNUP_ALL_ENABLED 0xAA\n\n#define SNLED27351_FUNCTION_REG_SCAN_PHASE 0x14\n#define SNLED27351_SCAN_PHASE_12_CHANNEL 0x00\n#define SNLED27351_SCAN_PHASE_11_CHANNEL 0x01\n#define SNLED27351_SCAN_PHASE_10_CHANNEL 0x02\n#define SNLED27351_SCAN_PHASE_9_CHANNEL 0x03\n#define SNLED27351_SCAN_PHASE_8_CHANNEL 0x04\n#define SNLED27351_SCAN_PHASE_7_CHANNEL 0x05\n#define SNLED27351_SCAN_PHASE_6_CHANNEL 0x06\n#define SNLED27351_SCAN_PHASE_5_CHANNEL 0x07\n#define SNLED27351_SCAN_PHASE_4_CHANNEL 0x08\n#define SNLED27351_SCAN_PHASE_3_CHANNEL 0x09\n#define SNLED27351_SCAN_PHASE_2_CHANNEL 0x0A\n#define SNLED27351_SCAN_PHASE_1_CHANNEL 0x0B\n\n#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_1 0x15\n#define SNLED27351_SLEW_RATE_CONTROL_MODE_1_PDP_ENABLE (0b1 << 2)\n\n#define SNLED27351_FUNCTION_REG_SLEW_RATE_CONTROL_MODE_2 0x16\n#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_SSL_ENABLE (0b1 << 6)\n#define SNLED27351_SLEW_RATE_CONTROL_MODE_2_DSL_ENABLE (0b1 << 7)\n\n#define SNLED27351_FUNCTION_REG_OPEN_SHORT_ENABLE 0x17\n#define SNLED27351_OPEN_SHORT_ENABLE_SDS_ENABLE (0b1 << 6)\n#define SNLED27351_OPEN_SHORT_ENABLE_ODS_ENABLE (0b1 << 7)\n\n#define SNLED27351_FUNCTION_REG_OPEN_SHORT_DUTY 0x18\n\n#define SNLED27351_FUNCTION_REG_OPEN_SHORT_FLAG 0x19\n#define SNLED27351_OPEN_SHORT_FLAG_OSINT_ENABLE (0b1 << 6)\n#define SNLED27351_OPEN_SHORT_FLAG_ODINT_ENABLE (0b1 << 7)\n\n#define SNLED27351_FUNCTION_REG_SOFTWARE_SLEEP 0x1A\n#define SNLED27351_SOFTWARE_SLEEP_ENABLE (0b1 << 1)\n\n// LED Control Registers\n#define SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR 0x0\n#define SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR 0x17\n#define SNLED27351_LED_CONTROL_ON_OFF_LENGTH ((SNLED27351_LED_CONTROL_ON_OFF_LAST_ADDR - SNLED27351_LED_CONTROL_ON_OFF_FIRST_ADDR) + 1)\n\n#define SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR 0x18\n#define SNLED27351_LED_CONTROL_OPEN_LAST_ADDR 0x2F\n#define SNLED27351_LED_CONTROL_OPEN_LENGTH ((SNLED27351_LED_CONTROL_OPEN_LAST_ADDR - SNLED27351_LED_CONTROL_OPEN_FIRST_ADDR) + 1)\n\n#define SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR 0x30\n#define SNLED27351_LED_CONTROL_SHORT_LAST_ADDR 0x47\n#define SNLED27351_LED_CONTROL_SHORT_LENGTH ((SNLED27351_LED_CONTROL_SHORT_LAST_ADDR - SNLED27351_LED_CONTROL_SHORT_FIRST_ADDR) + 1)\n\n#define SNLED27351_LED_CONTROL_PAGE_LENGTH 0x48\n\n// LED Control Registers\n#define SNLED27351_LED_PWM_FIRST_ADDR 0x00\n#define SNLED27351_LED_PWM_LAST_ADDR 0xBF\n#define SNLED27351_LED_PWM_LENGTH 0xC0\n\n// Current Tune Registers\n#define SNLED27351_LED_CURRENT_TUNE_FIRST_ADDR 0x00\n#define SNLED27351_LED_CURRENT_TUNE_LAST_ADDR 0x0B\n#define SNLED27351_LED_CURRENT_TUNE_LENGTH 0x0C\n\n#define SNLED27351_I2C_ADDRESS_GND 0x74\n#define SNLED27351_I2C_ADDRESS_SCL 0x75\n#define SNLED27351_I2C_ADDRESS_SDA 0x76\n#define SNLED27351_I2C_ADDRESS_VDDIO 0x77\n\n#if defined(RGB_MATRIX_SNLED27351)\n#    define SNLED27351_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#if defined(SNLED27351_I2C_ADDRESS_4)\n#    define SNLED27351_DRIVER_COUNT 4\n#elif defined(SNLED27351_I2C_ADDRESS_3)\n#    define SNLED27351_DRIVER_COUNT 3\n#elif defined(SNLED27351_I2C_ADDRESS_2)\n#    define SNLED27351_DRIVER_COUNT 2\n#elif defined(SNLED27351_I2C_ADDRESS_1)\n#    define SNLED27351_DRIVER_COUNT 1\n#endif\n\ntypedef struct snled27351_led_t {\n    uint8_t driver : 2;\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} PACKED snled27351_led_t;\n\nextern const snled27351_led_t PROGMEM g_snled27351_leds[SNLED27351_LED_COUNT];\n\nvoid snled27351_init_drivers(void);\nvoid snled27351_init(uint8_t index);\nvoid snled27351_select_page(uint8_t index, uint8_t page);\nvoid snled27351_write_register(uint8_t index, uint8_t reg, uint8_t data);\n\nvoid snled27351_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid snled27351_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid snled27351_set_led_control_register(uint8_t index, bool red, bool green, bool blue);\n\n// This should not be called from an interrupt\n// (eg. from a timer interrupt).\n// Call this while idle (in between matrix scans).\n// If the buffer is dirty, it will update the driver with the buffer.\nvoid snled27351_update_pwm_buffers(uint8_t index);\nvoid snled27351_update_led_control_registers(uint8_t index);\n\nvoid snled27351_flush(void);\n\nvoid snled27351_sw_return_normal(uint8_t index);\nvoid snled27351_sw_shutdown(uint8_t index);\n\n#define CB1_CA1 0x00\n#define CB1_CA2 0x01\n#define CB1_CA3 0x02\n#define CB1_CA4 0x03\n#define CB1_CA5 0x04\n#define CB1_CA6 0x05\n#define CB1_CA7 0x06\n#define CB1_CA8 0x07\n#define CB1_CA9 0x08\n#define CB1_CA10 0x09\n#define CB1_CA11 0x0A\n#define CB1_CA12 0x0B\n#define CB1_CA13 0x0C\n#define CB1_CA14 0x0D\n#define CB1_CA15 0x0E\n#define CB1_CA16 0x0F\n\n#define CB2_CA1 0x10\n#define CB2_CA2 0x11\n#define CB2_CA3 0x12\n#define CB2_CA4 0x13\n#define CB2_CA5 0x14\n#define CB2_CA6 0x15\n#define CB2_CA7 0x16\n#define CB2_CA8 0x17\n#define CB2_CA9 0x18\n#define CB2_CA10 0x19\n#define CB2_CA11 0x1A\n#define CB2_CA12 0x1B\n#define CB2_CA13 0x1C\n#define CB2_CA14 0x1D\n#define CB2_CA15 0x1E\n#define CB2_CA16 0x1F\n\n#define CB3_CA1 0x20\n#define CB3_CA2 0x21\n#define CB3_CA3 0x22\n#define CB3_CA4 0x23\n#define CB3_CA5 0x24\n#define CB3_CA6 0x25\n#define CB3_CA7 0x26\n#define CB3_CA8 0x27\n#define CB3_CA9 0x28\n#define CB3_CA10 0x29\n#define CB3_CA11 0x2A\n#define CB3_CA12 0x2B\n#define CB3_CA13 0x2C\n#define CB3_CA14 0x2D\n#define CB3_CA15 0x2E\n#define CB3_CA16 0x2F\n\n#define CB4_CA1 0x30\n#define CB4_CA2 0x31\n#define CB4_CA3 0x32\n#define CB4_CA4 0x33\n#define CB4_CA5 0x34\n#define CB4_CA6 0x35\n#define CB4_CA7 0x36\n#define CB4_CA8 0x37\n#define CB4_CA9 0x38\n#define CB4_CA10 0x39\n#define CB4_CA11 0x3A\n#define CB4_CA12 0x3B\n#define CB4_CA13 0x3C\n#define CB4_CA14 0x3D\n#define CB4_CA15 0x3E\n#define CB4_CA16 0x3F\n\n#define CB5_CA1 0x40\n#define CB5_CA2 0x41\n#define CB5_CA3 0x42\n#define CB5_CA4 0x43\n#define CB5_CA5 0x44\n#define CB5_CA6 0x45\n#define CB5_CA7 0x46\n#define CB5_CA8 0x47\n#define CB5_CA9 0x48\n#define CB5_CA10 0x49\n#define CB5_CA11 0x4A\n#define CB5_CA12 0x4B\n#define CB5_CA13 0x4C\n#define CB5_CA14 0x4D\n#define CB5_CA15 0x4E\n#define CB5_CA16 0x4F\n\n#define CB6_CA1 0x50\n#define CB6_CA2 0x51\n#define CB6_CA3 0x52\n#define CB6_CA4 0x53\n#define CB6_CA5 0x54\n#define CB6_CA6 0x55\n#define CB6_CA7 0x56\n#define CB6_CA8 0x57\n#define CB6_CA9 0x58\n#define CB6_CA10 0x59\n#define CB6_CA11 0x5A\n#define CB6_CA12 0x5B\n#define CB6_CA13 0x5C\n#define CB6_CA14 0x5D\n#define CB6_CA15 0x5E\n#define CB6_CA16 0x5F\n\n#define CB7_CA1 0x60\n#define CB7_CA2 0x61\n#define CB7_CA3 0x62\n#define CB7_CA4 0x63\n#define CB7_CA5 0x64\n#define CB7_CA6 0x65\n#define CB7_CA7 0x66\n#define CB7_CA8 0x67\n#define CB7_CA9 0x68\n#define CB7_CA10 0x69\n#define CB7_CA11 0x6A\n#define CB7_CA12 0x6B\n#define CB7_CA13 0x6C\n#define CB7_CA14 0x6D\n#define CB7_CA15 0x6E\n#define CB7_CA16 0x6F\n\n#define CB8_CA1 0x70\n#define CB8_CA2 0x71\n#define CB8_CA3 0x72\n#define CB8_CA4 0x73\n#define CB8_CA5 0x74\n#define CB8_CA6 0x75\n#define CB8_CA7 0x76\n#define CB8_CA8 0x77\n#define CB8_CA9 0x78\n#define CB8_CA10 0x79\n#define CB8_CA11 0x7A\n#define CB8_CA12 0x7B\n#define CB8_CA13 0x7C\n#define CB8_CA14 0x7D\n#define CB8_CA15 0x7E\n#define CB8_CA16 0x7F\n\n#define CB9_CA1 0x80\n#define CB9_CA2 0x81\n#define CB9_CA3 0x82\n#define CB9_CA4 0x83\n#define CB9_CA5 0x84\n#define CB9_CA6 0x85\n#define CB9_CA7 0x86\n#define CB9_CA8 0x87\n#define CB9_CA9 0x88\n#define CB9_CA10 0x89\n#define CB9_CA11 0x8A\n#define CB9_CA12 0x8B\n#define CB9_CA13 0x8C\n#define CB9_CA14 0x8D\n#define CB9_CA15 0x8E\n#define CB9_CA16 0x8F\n\n#define CB10_CA1 0x90\n#define CB10_CA2 0x91\n#define CB10_CA3 0x92\n#define CB10_CA4 0x93\n#define CB10_CA5 0x94\n#define CB10_CA6 0x95\n#define CB10_CA7 0x96\n#define CB10_CA8 0x97\n#define CB10_CA9 0x98\n#define CB10_CA10 0x99\n#define CB10_CA11 0x9A\n#define CB10_CA12 0x9B\n#define CB10_CA13 0x9C\n#define CB10_CA14 0x9D\n#define CB10_CA15 0x9E\n#define CB10_CA16 0x9F\n\n#define CB11_CA1 0xA0\n#define CB11_CA2 0xA1\n#define CB11_CA3 0xA2\n#define CB11_CA4 0xA3\n#define CB11_CA5 0xA4\n#define CB11_CA6 0xA5\n#define CB11_CA7 0xA6\n#define CB11_CA8 0xA7\n#define CB11_CA9 0xA8\n#define CB11_CA10 0xA9\n#define CB11_CA11 0xAA\n#define CB11_CA12 0xAB\n#define CB11_CA13 0xAC\n#define CB11_CA14 0xAD\n#define CB11_CA15 0xAE\n#define CB11_CA16 0xAF\n\n#define CB12_CA1 0xB0\n#define CB12_CA2 0xB1\n#define CB12_CA3 0xB2\n#define CB12_CA4 0xB3\n#define CB12_CA5 0xB4\n#define CB12_CA6 0xB5\n#define CB12_CA7 0xB6\n#define CB12_CA8 0xB7\n#define CB12_CA9 0xB8\n#define CB12_CA10 0xB9\n#define CB12_CA11 0xBA\n#define CB12_CA12 0xBB\n#define CB12_CA13 0xBC\n#define CB12_CA14 0xBD\n#define CB12_CA15 0xBE\n#define CB12_CA16 0xBF\n"
  },
  {
    "path": "drivers/led/ws2812.c",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"ws2812.h\"\n\n#if defined(WS2812_RGBW)\nvoid ws2812_rgb_to_rgbw(ws2812_led_t *led) {\n    // Determine lowest value in all three colors, put that into\n    // the white channel and then shift all colors by that amount\n    led->w = MIN(led->r, MIN(led->g, led->b));\n    led->r -= led->w;\n    led->g -= led->w;\n    led->b -= led->w;\n}\n#endif\n"
  },
  {
    "path": "drivers/led/ws2812.h",
    "content": "/*\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"util.h\"\n\n/*\n * The WS2812 datasheets define T1H 900ns, T0H 350ns, T1L 350ns, T0L 900ns. Hence, by default, these\n * are chosen to be conservative and avoid problems rather than for maximum throughput; in the code,\n * this is done by default using a WS2812_TIMING parameter that accounts for the whole window (1250ns)\n * and defining T1H and T0H; T1L and T0L are obtained by subtracting their low counterparts from the window.\n *\n * However, there are certain \"WS2812\"-like LEDs, like the SK6812s, which work in a similar\n * communication topology but use different timings for the window and the T1L, T1H, T0L and T0H.\n * This means that, albeit the same driver being applicable, the timings must be adapted.\n */\n\n#ifndef WS2812_TIMING\n#    define WS2812_TIMING 1250\n#endif\n\n#ifndef WS2812_T1H\n#    define WS2812_T1H 900 // Width of a 1 bit in ns\n#endif\n\n#ifndef WS2812_T1L\n#    define WS2812_T1L (WS2812_TIMING - WS2812_T1H) // Width of a 1 bit in ns\n#endif\n\n#ifndef WS2812_T0H\n#    define WS2812_T0H 350 // Width of a 0 bit in ns\n#endif\n\n#ifndef WS2812_T0L\n#    define WS2812_T0L (WS2812_TIMING - WS2812_T0H) // Width of a 0 bit in ns\n#endif\n\n/*\n * Older WS2812s can handle a reset time (TRST) of 50us, but recent\n * component revisions require a minimum of 280us.\n */\n#if !defined(WS2812_TRST_US)\n#    define WS2812_TRST_US 280\n#endif\n\n#if defined(RGBLIGHT_WS2812)\n#    define WS2812_LED_COUNT RGBLIGHT_LED_COUNT\n#elif defined(RGB_MATRIX_WS2812)\n#    define WS2812_LED_COUNT RGB_MATRIX_LED_COUNT\n#endif\n\n#define WS2812_BYTE_ORDER_RGB 0\n#define WS2812_BYTE_ORDER_GRB 1\n#define WS2812_BYTE_ORDER_BGR 2\n\n#ifndef WS2812_BYTE_ORDER\n#    define WS2812_BYTE_ORDER WS2812_BYTE_ORDER_GRB\n#endif\n\ntypedef struct PACKED ws2812_led_t {\n#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)\n    uint8_t g;\n    uint8_t r;\n    uint8_t b;\n#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)\n    uint8_t b;\n    uint8_t g;\n    uint8_t r;\n#endif\n#ifdef WS2812_RGBW\n    uint8_t w;\n#endif\n} ws2812_led_t;\n\nvoid ws2812_init(void);\nvoid ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\nvoid ws2812_flush(void);\n\nvoid ws2812_rgb_to_rgbw(ws2812_led_t *led);\n"
  },
  {
    "path": "drivers/oled/glcdfont.c",
    "content": "#include \"progmem.h\"\n\n// Helidox 8x6 font with QMK Firmware Logo\n// Online editor: http://teripom.x0.com/\n\nstatic const unsigned char font[] PROGMEM = {\n    0x07, 0x08, 0x7F, 0x08, 0x07, 0x00, 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x00, 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 0x00, 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x00, 0x18, 0x3C, 0x7E, 0x3C, 0x18, 0x00, 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x00, 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00, 0x00, 0x18, 0x3C, 0x18, 0x00, 0x00, 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, 0x00, 0x18, 0x24, 0x18, 0x00, 0x00, 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x00, 0x30, 0x48, 0x3A, 0x06, 0x0E, 0x00, 0x26, 0x29, 0x79, 0x29, 0x26, 0x00, 0x40, 0x7F, 0x05, 0x05, 0x07, 0x00, 0x40, 0x7F, 0x05, 0x25, 0x3F, 0x00, 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x00, 0x7F, 0x3E, 0x1C, 0x1C, 0x08, 0x00, 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x00, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x00, 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 0x00, 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, 0x00, 0x66, 0x89, 0x95, 0x6A, 0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, 0x94, 0xA2, 0xFF, 0xA2, 0x94, 0x00, 0x08, 0x04, 0x7E, 0x04, 0x08, 0x00,\n    0x10, 0x20, 0x7E, 0x20, 0x10, 0x00, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x00, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x00, 0x1E, 0x10, 0x10, 0x10, 0x10, 0x00, 0x0C, 0x1E, 0x0C, 0x1E, 0x0C, 0x00, 0x30, 0x38, 0x3E, 0x38, 0x30, 0x00, 0x06, 0x0E, 0x3E, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00, 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00, 0x23, 0x13, 0x08, 0x64, 0x62, 0x00, 0x36, 0x49, 0x56, 0x20, 0x50, 0x00, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, 0x00, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, 0x80, 0x70, 0x30, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x00,\n    0x72, 0x49, 0x49, 0x49, 0x46, 0x00, 0x21, 0x41, 0x49, 0x4D, 0x33, 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, 0x00, 0x27, 0x45, 0x45, 0x45, 0x39, 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x00, 0x41, 0x21, 0x11, 0x09, 0x07, 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, 0x00, 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00, 0x00, 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, 0x00, 0x02, 0x01, 0x59, 0x09, 0x06, 0x00, 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x00, 0x3E, 0x41, 0x41, 0x51, 0x73, 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, 0x00,\n    0x7F, 0x08, 0x14, 0x22, 0x41, 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00, 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x00, 0x26, 0x49, 0x49, 0x49, 0x32, 0x00, 0x03, 0x01, 0x7F, 0x01, 0x03, 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, 0x00, 0x03, 0x04, 0x78, 0x04, 0x03, 0x00, 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, 0x41, 0x41, 0x41, 0x7F, 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x03, 0x07, 0x08, 0x00, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40, 0x00, 0x7F, 0x28, 0x44, 0x44, 0x38, 0x00, 0x38, 0x44, 0x44, 0x44, 0x28, 0x00,\n    0x38, 0x44, 0x44, 0x28, 0x7F, 0x00, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x00, 0x08, 0x7E, 0x09, 0x02, 0x00, 0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, 0x00, 0x20, 0x40, 0x40, 0x3D, 0x00, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, 0x00, 0x7C, 0x04, 0x78, 0x04, 0x78, 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, 0xFC, 0x18, 0x24, 0x24, 0x18, 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x00, 0x48, 0x54, 0x54, 0x54, 0x24, 0x00, 0x04, 0x04, 0x3F, 0x44, 0x24, 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00,\n    0x00, 0x41, 0x36, 0x08, 0x00, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02, 0x00, 0x3C, 0x26, 0x23, 0x26, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0xF0, 0xF8, 0xF8, 0xFF, 0x38, 0xFF, 0xF8, 0xF8, 0x3F, 0xF8, 0xF8, 0xFF, 0x38, 0xFF, 0xF8, 0xF8, 0xF0, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0xC0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x80, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xF0, 0xF8, 0xFC, 0x3E,\n    0x1E, 0x06, 0x01, 0x00, 0x00, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x7F, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x7F, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0x7E, 0x5B, 0x4F, 0x5B, 0xFE, 0xC0, 0x00, 0x00, 0xC0, 0x00, 0xDC, 0xD7, 0xDE, 0xDE, 0xDE, 0xD7, 0xDC, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x49, 0x49, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xDF, 0xBF, 0xBF, 0x00, 0xBF, 0xBF, 0xDF, 0xE0, 0xFF, 0xFF, 0xFF, 0xFF, 0x49, 0x49, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x3F, 0x60, 0x60, 0xE0, 0xBF, 0x1F, 0x00, 0x7F, 0x7F, 0x07, 0x1E, 0x38, 0x1E, 0x07, 0x7F, 0x7F, 0x00, 0x7F, 0x7F, 0x0E, 0x1F, 0x3B, 0x71, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x7F, 0x0C, 0x0C, 0x0C, 0x00, 0x7E, 0x7E, 0x00, 0x7F, 0x7E, 0x03, 0x03, 0x00, 0x7F, 0x7E, 0x03, 0x03, 0x7E, 0x7E, 0x03, 0x03, 0x7F, 0x7E, 0x00, 0x0F,\n    0x3E, 0x70, 0x3C, 0x06, 0x3C, 0x70, 0x3E, 0x0F, 0x00, 0x32, 0x7B, 0x49, 0x49, 0x3F, 0x7E, 0x00, 0x7F, 0x7E, 0x03, 0x03, 0x00, 0x1E, 0x3F, 0x69, 0x69, 0x6F, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0F, 0x1F, 0x3F, 0x3C, 0x78, 0x70, 0x60, 0x00, 0x00, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x7F, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x7F, 0x00, 0x30, 0x7B, 0x7F, 0x78, 0x30, 0x20, 0x20, 0x30, 0x78, 0x7F, 0x3B, 0x00, 0x03, 0x00, 0x0F, 0x7F, 0x0F, 0x0F, 0x0F, 0x7F, 0x0F, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x07, 0x0F, 0x0F, 0x7F, 0x0F, 0x7F, 0x0F, 0x0F, 0x7E, 0x0F, 0x0F, 0x7F, 0x0F, 0x7F, 0x0F, 0x0F, 0x07, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n};\n"
  },
  {
    "path": "drivers/oled/licenses.txt",
    "content": "The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.\n\n\nThis is the Linux-penguin again...\n\nOriginally drewn by Larry Ewing (http://www.isc.tamu.edu/~lewing/)\n(with the GIMP) the Linux Logo has been vectorized by me (Simon Budig,\nhttp://www.home.unix-ag.org/simon/).\n\nThis happened quite some time ago with Corel Draw 4. But luckily\nmeanwhile there are tools available to handle vector graphics with\nLinux. Bernhard Herzog (bernhard@users.sourceforge.net) deserves kudos\nfor creating Sketch (http://sketch.sourceforge.net), a powerful free\ntool for creating vector graphics. He converted the Corel Draw file to\nthe Sketch native format. Since I am unable to maintain the Corel Draw\nfile any longer, the Sketch version now is the \"official\" one.\n\nAnja Gerwinski (anja@gerwinski.de) has created an alternate version of\nthe penguin (penguin-variant.sk) with a thinner mouth line and slightly\naltered gradients. It also features a nifty drop shadow.\n\nThe third bird (penguin-flat.sk) is a version reduced to three colors\n(black/white/yellow) for e.g. silk screen printing. I made this version\nfor a mug, available at the friendly folks at\nhttp://www.kernelconcepts.de/ - they do good stuff, mail Petra\n(pinguin@kernelconcepts.de) if you need something special or don't\nunderstand the german  :-)\n\nThese drawings are copyrighted by Larry Ewing and Simon Budig\n(penguin-variant.sk also by Anja Gerwinski), redistribution is free but\nhas to include this README/Copyright notice.\n\nThe use of these drawings is free. However I am happy about a sample of\nyour mug/t-shirt/whatever with this penguin on it...\n\nHave fun\n    Simon Budig\n\n\nSimon.Budig@unix-ag.org\nhttp://www.home.unix-ag.org/simon/\n\nSimon Budig\nAm Hardtkoeppel 2\nD-61279 Graevenwiesbach\n"
  },
  {
    "path": "drivers/oled/oled_driver.c",
    "content": "/*\nCopyright 2019 Ryan Caltabiano <https://github.com/XScorpion2>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#if defined(OLED_TRANSPORT_SPI)\n#    include \"spi_master.h\"\n#elif defined(OLED_TRANSPORT_I2C)\n#    include \"i2c_master.h\"\n#    if defined(USE_I2C) && defined(SPLIT_KEYBOARD)\n#        include \"keyboard.h\"\n#    endif\n#endif\n\n#include \"compiler_support.h\"\n#include \"oled_driver.h\"\n#include OLED_FONT_H\n#include \"timer.h\"\n#include \"print.h\"\n#include <string.h>\n#include \"progmem.h\"\n#include \"wait.h\"\n\n// Used commands from spec sheet: https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf\n// for SH1106: https://www.velleman.eu/downloads/29/infosheets/sh1106_datasheet.pdf\n// for SH1107: https://www.displayfuture.com/Display/datasheet/controller/SH1107.pdf\n\n// Fundamental Commands\n#define CONTRAST 0x81\n#define DISPLAY_ALL_ON 0xA5\n#define DISPLAY_ALL_ON_RESUME 0xA4\n#define NORMAL_DISPLAY 0xA6\n#define INVERT_DISPLAY 0xA7\n#define DISPLAY_ON 0xAF\n#define DISPLAY_OFF 0xAE\n#define NOP 0xE3\n\n// Scrolling Commands\n#define ACTIVATE_SCROLL 0x2F\n#define DEACTIVATE_SCROLL 0x2E\n#define SCROLL_RIGHT 0x26\n#define SCROLL_LEFT 0x27\n#define SCROLL_RIGHT_UP 0x29\n#define SCROLL_LEFT_UP 0x2A\n\n// Addressing Setting Commands\n#define MEMORY_MODE 0x20\n#define COLUMN_ADDR 0x21\n#define PAGE_ADDR 0x22\n#define PAM_SETCOLUMN_LSB 0x00\n#define PAM_SETCOLUMN_MSB 0x10\n#define PAM_PAGE_ADDR 0xB0 // 0xb0 -- 0xb7\n\n// Hardware Configuration Commands\n#define DISPLAY_START_LINE 0x40\n#define SEGMENT_REMAP 0xA0\n#define SEGMENT_REMAP_INV 0xA1\n#define MULTIPLEX_RATIO 0xA8\n#define COM_SCAN_INC 0xC0\n#define COM_SCAN_DEC 0xC8\n#define DISPLAY_OFFSET 0xD3\n#define COM_PINS 0xDA\n#define COM_PINS_SEQ 0x02\n#define COM_PINS_ALT 0x12\n#define COM_PINS_SEQ_LR 0x22\n#define COM_PINS_ALT_LR 0x32\n\n// Timing & Driving Commands\n#define DISPLAY_CLOCK 0xD5\n#define PRE_CHARGE_PERIOD 0xD9\n#define VCOM_DETECT 0xDB\n\n// Advance Graphic Commands\n#define FADE_BLINK 0x23\n#define ENABLE_FADE 0x20\n#define ENABLE_BLINK 0x30\n\n// Charge Pump Commands\n#define CHARGE_PUMP 0x8D\n\n// Commands specific to the SH1107 chip\n#define SH1107_DISPLAY_START_LINE 0xDC\n#define SH1107_MEMORY_MODE_PAGE 0x20\n#define SH1107_MEMORY_MODE_VERTICAL 0x21\n\n// Misc defines\n#ifndef OLED_BLOCK_COUNT\n#    define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8)\n#endif\n#ifndef OLED_BLOCK_SIZE\n#    define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)\n#endif\n// Default display clock\n#if !defined(OLED_DISPLAY_CLOCK)\n#    define OLED_DISPLAY_CLOCK 0x80\n#endif\n// Default VCOMH deselect value\n#if !defined(OLED_VCOM_DETECT)\n#    define OLED_VCOM_DETECT 0x20\n#endif\n#if !defined(OLED_PRE_CHARGE_PERIOD)\n#    define OLED_PRE_CHARGE_PERIOD 0xF1\n#endif\n\n#define OLED_ALL_BLOCKS_MASK (((((OLED_BLOCK_TYPE)1 << (OLED_BLOCK_COUNT - 1)) - 1) << 1) | 1)\n\n#define OLED_IC_HAS_HORIZONTAL_MODE (OLED_IC == OLED_IC_SSD1306)\n#define OLED_IC_COM_PINS_ARE_COLUMNS (OLED_IC == OLED_IC_SH1107)\n\n#ifndef OLED_COM_PIN_COUNT\n#    if OLED_IC == OLED_IC_SSD1306\n#        define OLED_COM_PIN_COUNT 64\n#    elif OLED_IC == OLED_IC_SH1106\n#        define OLED_COM_PIN_COUNT 64\n#    elif OLED_IC == OLED_IC_SH1107\n#        define OLED_COM_PIN_COUNT 128\n#    else\n#        error Invalid OLED_IC value\n#    endif\n#endif\n\n#ifndef OLED_COM_PIN_OFFSET\n#    define OLED_COM_PIN_OFFSET 0\n#endif\n\n// i2c defines\n#define I2C_CMD 0x00\n#define I2C_DATA 0x40\n\n#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)\n\n// Display buffer's is the same as the OLED memory layout\n// this is so we don't end up with rounding errors with\n// parts of the display unusable or don't get cleared correctly\n// and also allows for drawing & inverting\nuint8_t         oled_buffer[OLED_MATRIX_SIZE];\nuint8_t *       oled_cursor;\nOLED_BLOCK_TYPE oled_dirty          = 0;\nbool            oled_initialized    = false;\nbool            oled_active         = false;\nbool            oled_scrolling      = false;\nbool            oled_inverted       = false;\nuint8_t         oled_brightness     = OLED_BRIGHTNESS;\noled_rotation_t oled_rotation       = 0;\nuint8_t         oled_rotation_width = 0;\nuint8_t         oled_scroll_speed   = 0; // this holds the speed after being remapped to ssd1306 internal values\nuint8_t         oled_scroll_start   = 0;\nuint8_t         oled_scroll_end     = 7;\n#if OLED_TIMEOUT > 0\nuint32_t oled_timeout;\n#endif\n#if OLED_SCROLL_TIMEOUT > 0\nuint32_t oled_scroll_timeout;\n#endif\n#if OLED_UPDATE_INTERVAL > 0\nuint16_t oled_update_timeout;\n#endif\n\n#if defined(OLED_TRANSPORT_SPI)\n#    ifndef OLED_DC_PIN\n#        error \"The OLED driver in SPI needs a D/C pin defined\"\n#    endif\n#    ifndef OLED_CS_PIN\n#        error \"The OLED driver in SPI needs a CS pin defined\"\n#    endif\n#    ifndef OLED_SPI_MODE\n#        define OLED_SPI_MODE 3\n#    endif\n#    ifndef OLED_SPI_DIVISOR\n#        define OLED_SPI_DIVISOR 2\n#    endif\n#elif defined(OLED_TRANSPORT_I2C)\n#    if !defined(OLED_DISPLAY_ADDRESS)\n#        define OLED_DISPLAY_ADDRESS 0x3C\n#    endif\n#endif\n\n// Transmit/Write Funcs.\n__attribute__((weak)) bool oled_send_cmd(const uint8_t *data, uint16_t size) {\n#if defined(OLED_TRANSPORT_SPI)\n    if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) {\n        return false;\n    }\n    // Command Mode\n    gpio_write_pin_low(OLED_DC_PIN);\n    // Send the commands\n    if (spi_transmit(&data[1], size - 1) != SPI_STATUS_SUCCESS) {\n        spi_stop();\n        return false;\n    }\n    spi_stop();\n    return true;\n#elif defined(OLED_TRANSPORT_I2C)\n    i2c_status_t status = i2c_transmit((OLED_DISPLAY_ADDRESS << 1), data, size, OLED_I2C_TIMEOUT);\n\n    return (status == I2C_STATUS_SUCCESS);\n#endif\n}\n\n__attribute__((weak)) bool oled_send_cmd_P(const uint8_t *data, uint16_t size) {\n#if defined(__AVR__)\n#    if defined(OLED_TRANSPORT_SPI)\n    if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) {\n        return false;\n    }\n    spi_status_t status = SPI_STATUS_SUCCESS;\n    // Command Mode\n    gpio_write_pin_low(OLED_DC_PIN);\n    // Send the commands\n    for (uint16_t i = 1; i < size && status >= 0; i++) {\n        status = spi_write(pgm_read_byte((const char *)&data[i]));\n    }\n    spi_stop();\n    return (status >= 0);\n#    elif defined(OLED_TRANSPORT_I2C)\n\n    i2c_status_t status = i2c_transmit_P((OLED_DISPLAY_ADDRESS << 1), data, size, OLED_I2C_TIMEOUT);\n\n    return (status == I2C_STATUS_SUCCESS);\n#    endif\n#else\n    return oled_send_cmd(data, size);\n#endif\n}\n\n__attribute__((weak)) bool oled_send_data(const uint8_t *data, uint16_t size) {\n#if defined(OLED_TRANSPORT_SPI)\n    if (!spi_start(OLED_CS_PIN, false, OLED_SPI_MODE, OLED_SPI_DIVISOR)) {\n        return false;\n    }\n    // Data Mode\n    gpio_write_pin_high(OLED_DC_PIN);\n    // Send the commands\n    if (spi_transmit(data, size) != SPI_STATUS_SUCCESS) {\n        spi_stop();\n        return false;\n    }\n    spi_stop();\n    return true;\n#elif defined(OLED_TRANSPORT_I2C)\n    i2c_status_t status = i2c_write_register((OLED_DISPLAY_ADDRESS << 1), I2C_DATA, data, size, OLED_I2C_TIMEOUT);\n    return (status == I2C_STATUS_SUCCESS);\n#endif\n}\n\n__attribute__((weak)) void oled_driver_init(void) {\n#if defined(OLED_TRANSPORT_SPI)\n    spi_init();\n    gpio_set_pin_output(OLED_CS_PIN);\n    gpio_write_pin_high(OLED_CS_PIN);\n\n    gpio_set_pin_output(OLED_DC_PIN);\n    gpio_write_pin_low(OLED_DC_PIN);\n#    ifdef OLED_RST_PIN\n    /* Reset device */\n    gpio_set_pin_output(OLED_RST_PIN);\n    gpio_write_pin_low(OLED_RST_PIN);\n    wait_ms(20);\n    gpio_write_pin_high(OLED_RST_PIN);\n    wait_ms(20);\n#    endif\n#elif defined(OLED_TRANSPORT_I2C)\n    i2c_init();\n#endif\n}\n\n// Flips the rendering bits for a character at the current cursor position\nstatic void InvertCharacter(uint8_t *cursor) {\n    const uint8_t *end = cursor + OLED_FONT_WIDTH;\n    while (cursor < end) {\n        *cursor = ~(*cursor);\n        cursor++;\n    }\n}\n\nbool oled_init(oled_rotation_t rotation) {\n#if defined(USE_I2C) && defined(SPLIT_KEYBOARD) && defined(OLED_TRANSPORT_I2C)\n    if (!is_keyboard_master()) {\n        return true;\n    }\n#endif\n\n    oled_rotation = oled_init_user(oled_init_kb(rotation));\n    if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {\n        oled_rotation_width = OLED_DISPLAY_WIDTH;\n    } else {\n        oled_rotation_width = OLED_DISPLAY_HEIGHT;\n    }\n    oled_driver_init();\n\n    static const uint8_t PROGMEM display_setup1[] = {\n        I2C_CMD,\n        DISPLAY_OFF,\n        DISPLAY_CLOCK,\n        OLED_DISPLAY_CLOCK,\n        MULTIPLEX_RATIO,\n#if OLED_IC_COM_PINS_ARE_COLUMNS\n        OLED_DISPLAY_WIDTH - 1,\n#else\n        OLED_DISPLAY_HEIGHT - 1,\n#endif\n#if OLED_IC == OLED_IC_SH1107\n        SH1107_DISPLAY_START_LINE,\n        0x00,\n#else\n        DISPLAY_START_LINE | 0x00,\n#endif\n        CHARGE_PUMP,\n        0x14,\n#if OLED_IC_HAS_HORIZONTAL_MODE\n        // MEMORY_MODE is unsupported on SH1106 (Page Addressing only)\n        MEMORY_MODE,\n        0x00, // Horizontal addressing mode\n#elif OLED_IC == OLED_IC_SH1107\n        // Page addressing mode\n        SH1107_MEMORY_MODE_PAGE,\n#endif\n    };\n    if (!oled_send_cmd_P(display_setup1, ARRAY_SIZE(display_setup1))) {\n        print(\"oled_init cmd set 1 failed\\n\");\n        return false;\n    }\n\n    if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_180)) {\n        static const uint8_t PROGMEM display_normal[] = {\n            I2C_CMD, SEGMENT_REMAP_INV, COM_SCAN_DEC, DISPLAY_OFFSET, OLED_COM_PIN_OFFSET,\n        };\n        if (!oled_send_cmd_P(display_normal, ARRAY_SIZE(display_normal))) {\n            print(\"oled_init cmd normal rotation failed\\n\");\n            return false;\n        }\n    } else {\n        static const uint8_t PROGMEM display_flipped[] = {\n            I2C_CMD, SEGMENT_REMAP, COM_SCAN_INC, DISPLAY_OFFSET, (OLED_COM_PIN_COUNT - OLED_COM_PIN_OFFSET) % OLED_COM_PIN_COUNT,\n        };\n        if (!oled_send_cmd_P(display_flipped, ARRAY_SIZE(display_flipped))) {\n            print(\"display_flipped failed\\n\");\n            return false;\n        }\n    }\n\n    static const uint8_t PROGMEM display_setup2[] = {I2C_CMD, COM_PINS, OLED_COM_PINS, CONTRAST, OLED_BRIGHTNESS, PRE_CHARGE_PERIOD, OLED_PRE_CHARGE_PERIOD, VCOM_DETECT, OLED_VCOM_DETECT, DISPLAY_ALL_ON_RESUME, NORMAL_DISPLAY, DEACTIVATE_SCROLL, DISPLAY_ON};\n    if (!oled_send_cmd_P(display_setup2, ARRAY_SIZE(display_setup2))) {\n        print(\"display_setup2 failed\\n\");\n        return false;\n    }\n\n#if OLED_TIMEOUT > 0\n    oled_timeout = timer_read32() + OLED_TIMEOUT;\n#endif\n#if OLED_SCROLL_TIMEOUT > 0\n    oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT;\n#endif\n\n    oled_clear();\n    oled_initialized = true;\n    oled_active      = true;\n    oled_scrolling   = false;\n    return true;\n}\n\n__attribute__((weak)) oled_rotation_t oled_init_kb(oled_rotation_t rotation) {\n    return rotation;\n}\n__attribute__((weak)) oled_rotation_t oled_init_user(oled_rotation_t rotation) {\n    return rotation;\n}\n\nvoid oled_clear(void) {\n    memset(oled_buffer, 0, sizeof(oled_buffer));\n    oled_cursor = &oled_buffer[0];\n    oled_dirty  = OLED_ALL_BLOCKS_MASK;\n}\n\nstatic void calc_bounds(uint8_t update_start, uint8_t *cmd_array) {\n    // Calculate commands to set memory addressing bounds.\n    uint8_t start_page   = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_WIDTH;\n    uint8_t start_column = OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_WIDTH;\n#if !OLED_IC_HAS_HORIZONTAL_MODE\n    // Commands for Page Addressing Mode. Sets starting page and column; has no end bound.\n    // Column value must be split into high and low nybble and sent as two commands.\n    cmd_array[0] = PAM_PAGE_ADDR | start_page;\n    cmd_array[1] = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f);\n    cmd_array[2] = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f);\n#else\n    // Commands for use in Horizontal Addressing mode.\n    cmd_array[1] = start_column + OLED_COLUMN_OFFSET;\n    cmd_array[4] = start_page;\n    cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) % OLED_DISPLAY_WIDTH + cmd_array[1];\n    cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_WIDTH - 1) / OLED_DISPLAY_WIDTH - 1 + cmd_array[4];\n#endif\n}\n\nstatic void calc_bounds_90(uint8_t update_start, uint8_t *cmd_array) {\n    // Block numbering starts from the bottom left corner, going up and then to\n    // the right.  The controller needs the page and column numbers for the top\n    // left and bottom right corners of that block.\n\n    // Total number of pages across the screen height.\n    const uint8_t height_in_pages = OLED_DISPLAY_HEIGHT / 8;\n\n    // Difference of starting page numbers for adjacent blocks; may be 0 if\n    // blocks are large enough to occupy one or more whole 8px columns.\n    const uint8_t page_inc_per_block = OLED_BLOCK_SIZE % OLED_DISPLAY_HEIGHT / 8;\n\n    // Top page number for a block which is at the bottom edge of the screen.\n    const uint8_t bottom_block_top_page = (height_in_pages - page_inc_per_block) % height_in_pages;\n\n#if !OLED_IC_HAS_HORIZONTAL_MODE\n    // Only the Page Addressing Mode is supported\n    uint8_t start_page   = bottom_block_top_page - (OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT / 8);\n    uint8_t start_column = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8;\n    cmd_array[0]         = PAM_PAGE_ADDR | start_page;\n    cmd_array[1]         = PAM_SETCOLUMN_LSB | ((OLED_COLUMN_OFFSET + start_column) & 0x0f);\n    cmd_array[2]         = PAM_SETCOLUMN_MSB | ((OLED_COLUMN_OFFSET + start_column) >> 4 & 0x0f);\n#else\n    cmd_array[1] = OLED_BLOCK_SIZE * update_start / OLED_DISPLAY_HEIGHT * 8 + OLED_COLUMN_OFFSET;\n    cmd_array[4] = bottom_block_top_page - (OLED_BLOCK_SIZE * update_start % OLED_DISPLAY_HEIGHT / 8);\n    cmd_array[2] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8 - 1 + cmd_array[1];\n    cmd_array[5] = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) % OLED_DISPLAY_HEIGHT / 8 + cmd_array[4];\n#endif\n}\n\nuint8_t crot(uint8_t a, int8_t n) {\n    const uint8_t mask = 0x7;\n    n &= mask;\n    return a << n | a >> (-n & mask);\n}\n\nstatic void rotate_90(const uint8_t *src, uint8_t *dest) {\n    for (uint8_t i = 0, shift = 7; i < 8; ++i, --shift) {\n        uint8_t selector = (1 << i);\n        for (uint8_t j = 0; j < 8; ++j) {\n            dest[i] |= crot(src[j] & selector, shift - (int8_t)j);\n        }\n    }\n}\n\nvoid oled_render_dirty(bool all) {\n    // Do we have work to do?\n    oled_dirty &= OLED_ALL_BLOCKS_MASK;\n    if (!oled_dirty || !oled_initialized || oled_scrolling) {\n        return;\n    }\n\n    // Turn on display if it is off\n    oled_on();\n\n    uint8_t update_start  = 0;\n    uint8_t num_processed = 0;\n    while (oled_dirty && (num_processed++ < OLED_UPDATE_PROCESS_LIMIT || all)) { // render all dirty blocks (up to the configured limit)\n        // Find next dirty block\n        while (!(oled_dirty & ((OLED_BLOCK_TYPE)1 << update_start))) {\n            ++update_start;\n        }\n\n        // Set column & page position\n#if OLED_IC_HAS_HORIZONTAL_MODE\n        static uint8_t display_start[] = {I2C_CMD, COLUMN_ADDR, 0, OLED_DISPLAY_WIDTH - 1, PAGE_ADDR, 0, OLED_DISPLAY_HEIGHT / 8 - 1};\n#else\n        static uint8_t display_start[] = {I2C_CMD, PAM_PAGE_ADDR, PAM_SETCOLUMN_LSB, PAM_SETCOLUMN_MSB};\n#endif\n        if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {\n            calc_bounds(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start\n        } else {\n            calc_bounds_90(update_start, &display_start[1]); // Offset from I2C_CMD byte at the start\n        }\n\n        // Send column & page position\n        if (!oled_send_cmd(display_start, ARRAY_SIZE(display_start))) {\n            print(\"oled_render offset command failed\\n\");\n            return;\n        }\n\n        if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {\n            // Send render data chunk as is\n            if (!oled_send_data(&oled_buffer[OLED_BLOCK_SIZE * update_start], OLED_BLOCK_SIZE)) {\n                print(\"oled_render data failed\\n\");\n                return;\n            }\n        } else {\n            // Rotate the render chunks\n            const static uint8_t source_map[] = OLED_SOURCE_MAP;\n            const static uint8_t target_map[] = OLED_TARGET_MAP;\n\n            static uint8_t temp_buffer[OLED_BLOCK_SIZE];\n            memset(temp_buffer, 0, sizeof(temp_buffer));\n            for (uint8_t i = 0; i < sizeof(source_map); ++i) {\n                rotate_90(&oled_buffer[OLED_BLOCK_SIZE * update_start + source_map[i]], &temp_buffer[target_map[i]]);\n            }\n\n#if OLED_IC_HAS_HORIZONTAL_MODE\n            // Send render data chunk after rotating\n            if (!oled_send_data(&temp_buffer[0], OLED_BLOCK_SIZE)) {\n                print(\"oled_render90 data failed\\n\");\n                return;\n            }\n#else\n            // For SH1106 or SH1107 the data chunk must be split into separate pieces for each page\n            const uint8_t columns_in_block = (OLED_BLOCK_SIZE + OLED_DISPLAY_HEIGHT - 1) / OLED_DISPLAY_HEIGHT * 8;\n            const uint8_t num_pages        = OLED_BLOCK_SIZE / columns_in_block;\n            for (uint8_t i = 0; i < num_pages; ++i) {\n                // Send column & page position for all pages except the first one\n                if (i > 0) {\n                    display_start[1]++;\n                    if (!oled_send_cmd(display_start, ARRAY_SIZE(display_start))) {\n                        print(\"oled_render offset command failed\\n\");\n                        return;\n                    }\n                }\n                // Send data for the page\n                if (!oled_send_data(&temp_buffer[columns_in_block * i], columns_in_block)) {\n                    print(\"oled_render90 data failed\\n\");\n                    return;\n                }\n            }\n#endif\n        }\n\n        // Clear dirty flag of just rendered block\n        oled_dirty &= ~((OLED_BLOCK_TYPE)1 << update_start);\n    }\n}\n\nvoid oled_set_cursor(uint8_t col, uint8_t line) {\n    uint16_t index = line * oled_rotation_width + col * OLED_FONT_WIDTH;\n\n    // Out of bounds?\n    if (index >= OLED_MATRIX_SIZE) {\n        index = 0;\n    }\n\n    oled_cursor = &oled_buffer[index];\n}\n\nvoid oled_advance_page(bool clearPageRemainder) {\n    uint16_t index     = oled_cursor - &oled_buffer[0];\n    uint8_t  remaining = oled_rotation_width - (index % oled_rotation_width);\n\n    if (clearPageRemainder) {\n        // Remaining Char count\n        remaining = remaining / OLED_FONT_WIDTH;\n\n        // Write empty character until next line\n        while (remaining--)\n            oled_write_char(' ', false);\n    } else {\n        // Next page index out of bounds?\n        if (index + remaining >= OLED_MATRIX_SIZE) {\n            index     = 0;\n            remaining = 0;\n        }\n\n        oled_cursor = &oled_buffer[index + remaining];\n    }\n}\n\nvoid oled_advance_char(void) {\n    uint16_t nextIndex      = oled_cursor - &oled_buffer[0] + OLED_FONT_WIDTH;\n    uint8_t  remainingSpace = oled_rotation_width - (nextIndex % oled_rotation_width);\n\n    // Do we have enough space on the current line for the next character\n    if (remainingSpace < OLED_FONT_WIDTH) {\n        nextIndex += remainingSpace;\n    }\n\n    // Did we go out of bounds\n    if (nextIndex >= OLED_MATRIX_SIZE) {\n        nextIndex = 0;\n    }\n\n    // Update cursor position\n    oled_cursor = &oled_buffer[nextIndex];\n}\n\n// Main handler that writes character data to the display buffer\nvoid oled_write_char(const char data, bool invert) {\n    // Advance to the next line if newline\n    if (data == '\\n') {\n        // Old source wrote ' ' until end of line...\n        oled_advance_page(true);\n        return;\n    }\n\n    if (data == '\\r') {\n        oled_advance_page(false);\n        return;\n    }\n\n    // copy the current render buffer to check for dirty after\n    static uint8_t oled_temp_buffer[OLED_FONT_WIDTH];\n    memcpy(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH);\n\n    STATIC_ASSERT(sizeof(font) >= ((OLED_FONT_END + 1 - OLED_FONT_START) * OLED_FONT_WIDTH), \"OLED_FONT_END references outside array\");\n\n    // set the reder buffer data\n    uint8_t cast_data = (uint8_t)data; // font based on unsigned type for index\n    if (cast_data < OLED_FONT_START || cast_data > OLED_FONT_END) {\n        memset(oled_cursor, 0x00, OLED_FONT_WIDTH);\n    } else {\n        const uint8_t *glyph = &font[(cast_data - OLED_FONT_START) * OLED_FONT_WIDTH];\n        memcpy_P(oled_cursor, glyph, OLED_FONT_WIDTH);\n    }\n\n    // Invert if needed\n    if (invert) {\n        InvertCharacter(oled_cursor);\n    }\n\n    // Dirty check\n    if (memcmp(&oled_temp_buffer, oled_cursor, OLED_FONT_WIDTH)) {\n        uint16_t index = oled_cursor - &oled_buffer[0];\n        oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));\n        // Edgecase check if the written data spans the 2 chunks\n        oled_dirty |= ((OLED_BLOCK_TYPE)1 << ((index + OLED_FONT_WIDTH - 1) / OLED_BLOCK_SIZE));\n    }\n\n    // Finally move to the next char\n    oled_advance_char();\n}\n\nvoid oled_write(const char *data, bool invert) {\n    const char *end = data + strlen(data);\n    while (data < end) {\n        oled_write_char(*data, invert);\n        data++;\n    }\n}\n\nvoid oled_write_ln(const char *data, bool invert) {\n    oled_write(data, invert);\n    oled_advance_page(true);\n}\n\nvoid oled_pan(bool left) {\n    uint16_t i = 0;\n    for (uint16_t y = 0; y < OLED_DISPLAY_HEIGHT / 8; y++) {\n        if (left) {\n            for (uint16_t x = 0; x < OLED_DISPLAY_WIDTH - 1; x++) {\n                i              = y * OLED_DISPLAY_WIDTH + x;\n                oled_buffer[i] = oled_buffer[i + 1];\n            }\n        } else {\n            for (uint16_t x = OLED_DISPLAY_WIDTH - 1; x > 0; x--) {\n                i              = y * OLED_DISPLAY_WIDTH + x;\n                oled_buffer[i] = oled_buffer[i - 1];\n            }\n        }\n    }\n    oled_dirty = OLED_ALL_BLOCKS_MASK;\n}\n\noled_buffer_reader_t oled_read_raw(uint16_t start_index) {\n    if (start_index > OLED_MATRIX_SIZE) start_index = OLED_MATRIX_SIZE;\n    oled_buffer_reader_t ret_reader;\n    ret_reader.current_element         = &oled_buffer[start_index];\n    ret_reader.remaining_element_count = OLED_MATRIX_SIZE - start_index;\n    return ret_reader;\n}\n\nvoid oled_write_raw_byte(const char data, uint16_t index) {\n    if (index > OLED_MATRIX_SIZE) index = OLED_MATRIX_SIZE;\n    if (oled_buffer[index] == data) return;\n    oled_buffer[index] = data;\n    oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));\n}\n\nvoid oled_write_raw(const char *data, uint16_t size) {\n    uint16_t cursor_start_index = oled_cursor - &oled_buffer[0];\n    if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index;\n    for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {\n        uint8_t c = *data++;\n        if (oled_buffer[i] == c) continue;\n        oled_buffer[i] = c;\n        oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE));\n    }\n}\n\nvoid oled_write_pixel(uint8_t x, uint8_t y, bool on) {\n    if (x >= oled_rotation_width) {\n        return;\n    }\n    uint16_t index = x + (y / 8) * oled_rotation_width;\n    if (index >= OLED_MATRIX_SIZE) {\n        return;\n    }\n    uint8_t data = oled_buffer[index];\n    if (on) {\n        data |= (1 << (y % 8));\n    } else {\n        data &= ~(1 << (y % 8));\n    }\n    if (oled_buffer[index] != data) {\n        oled_buffer[index] = data;\n        oled_dirty |= ((OLED_BLOCK_TYPE)1 << (index / OLED_BLOCK_SIZE));\n    }\n}\n\n#if defined(__AVR__)\nvoid oled_write_P(const char *data, bool invert) {\n    uint8_t c = pgm_read_byte(data);\n    while (c != 0) {\n        oled_write_char(c, invert);\n        c = pgm_read_byte(++data);\n    }\n}\n\nvoid oled_write_ln_P(const char *data, bool invert) {\n    oled_write_P(data, invert);\n    oled_advance_page(true);\n}\n\nvoid oled_write_raw_P(const char *data, uint16_t size) {\n    uint16_t cursor_start_index = oled_cursor - &oled_buffer[0];\n    if ((size + cursor_start_index) > OLED_MATRIX_SIZE) size = OLED_MATRIX_SIZE - cursor_start_index;\n    for (uint16_t i = cursor_start_index; i < cursor_start_index + size; i++) {\n        uint8_t c = pgm_read_byte(data++);\n        if (oled_buffer[i] == c) continue;\n        oled_buffer[i] = c;\n        oled_dirty |= ((OLED_BLOCK_TYPE)1 << (i / OLED_BLOCK_SIZE));\n    }\n}\n#endif // defined(__AVR__)\n\nbool oled_on(void) {\n    if (!oled_initialized) {\n        return oled_active;\n    }\n\n#if OLED_TIMEOUT > 0\n    oled_timeout = timer_read32() + OLED_TIMEOUT;\n#endif\n\n    static const uint8_t PROGMEM display_on[] =\n#ifdef OLED_FADE_OUT\n        {I2C_CMD, FADE_BLINK, 0x00};\n#else\n        {I2C_CMD, DISPLAY_ON};\n#endif\n\n    if (!oled_active) {\n        if (!oled_send_cmd_P(display_on, ARRAY_SIZE(display_on))) {\n            print(\"oled_on cmd failed\\n\");\n            return oled_active;\n        }\n        oled_active = true;\n    }\n    return oled_active;\n}\n\nbool oled_off(void) {\n    if (!oled_initialized) {\n        return !oled_active;\n    }\n\n    static const uint8_t PROGMEM display_off[] =\n#ifdef OLED_FADE_OUT\n        {I2C_CMD, FADE_BLINK, ENABLE_FADE | OLED_FADE_OUT_INTERVAL};\n#else\n        {I2C_CMD, DISPLAY_OFF};\n#endif\n\n    if (oled_active) {\n        if (!oled_send_cmd_P(display_off, ARRAY_SIZE(display_off))) {\n            print(\"oled_off cmd failed\\n\");\n            return oled_active;\n        }\n        oled_active = false;\n    }\n    return !oled_active;\n}\n\nbool is_oled_on(void) {\n    return oled_active;\n}\n\nuint8_t oled_set_brightness(uint8_t level) {\n    if (!oled_initialized) {\n        return oled_brightness;\n    }\n\n    uint8_t set_contrast[] = {I2C_CMD, CONTRAST, level};\n    if (oled_brightness != level) {\n        if (!oled_send_cmd(set_contrast, ARRAY_SIZE(set_contrast))) {\n            print(\"set_brightness cmd failed\\n\");\n            return oled_brightness;\n        }\n        oled_brightness = level;\n    }\n    return oled_brightness;\n}\n\nuint8_t oled_get_brightness(void) {\n    return oled_brightness;\n}\n\n// Set the specific 8 lines rows of the screen to scroll.\n// 0 is the default for start, and 7 for end, which is the entire\n// height of the screen.  For 128x32 screens, rows 4-7 are not used.\nvoid oled_scroll_set_area(uint8_t start_line, uint8_t end_line) {\n    oled_scroll_start = start_line;\n    oled_scroll_end   = end_line;\n}\n\nvoid oled_scroll_set_speed(uint8_t speed) {\n    // Sets the speed for scrolling... does not take effect\n    // until scrolling is either started or restarted\n    // the ssd1306 supports 8 speeds\n    // FrameRate2   speed = 7\n    // FrameRate3   speed = 4\n    // FrameRate4   speed = 5\n    // FrameRate5   speed = 0\n    // FrameRate25  speed = 6\n    // FrameRate64  speed = 1\n    // FrameRate128 speed = 2\n    // FrameRate256 speed = 3\n    // for ease of use these are remaped here to be in order\n    static const uint8_t scroll_remap[8] = {7, 4, 5, 0, 6, 1, 2, 3};\n    oled_scroll_speed                    = scroll_remap[speed];\n}\n\nbool oled_scroll_right(void) {\n    if (!oled_initialized) {\n        return oled_scrolling;\n    }\n\n    // Dont enable scrolling if we need to update the display\n    // This prevents scrolling of bad data from starting the scroll too early after init\n    if (!oled_dirty && !oled_scrolling) {\n        uint8_t display_scroll_right[] = {I2C_CMD, SCROLL_RIGHT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL};\n        if (!oled_send_cmd(display_scroll_right, ARRAY_SIZE(display_scroll_right))) {\n            print(\"oled_scroll_right cmd failed\\n\");\n            return oled_scrolling;\n        }\n        oled_scrolling = true;\n    }\n    return oled_scrolling;\n}\n\nbool oled_scroll_left(void) {\n    if (!oled_initialized) {\n        return oled_scrolling;\n    }\n\n    // Dont enable scrolling if we need to update the display\n    // This prevents scrolling of bad data from starting the scroll too early after init\n    if (!oled_dirty && !oled_scrolling) {\n        uint8_t display_scroll_left[] = {I2C_CMD, SCROLL_LEFT, 0x00, oled_scroll_start, oled_scroll_speed, oled_scroll_end, 0x00, 0xFF, ACTIVATE_SCROLL};\n        if (!oled_send_cmd(display_scroll_left, ARRAY_SIZE(display_scroll_left))) {\n            print(\"oled_scroll_left cmd failed\\n\");\n            return oled_scrolling;\n        }\n        oled_scrolling = true;\n    }\n    return oled_scrolling;\n}\n\nbool oled_scroll_off(void) {\n    if (!oled_initialized) {\n        return !oled_scrolling;\n    }\n\n    if (oled_scrolling) {\n        static const uint8_t PROGMEM display_scroll_off[] = {I2C_CMD, DEACTIVATE_SCROLL};\n        if (!oled_send_cmd_P(display_scroll_off, ARRAY_SIZE(display_scroll_off))) {\n            print(\"oled_scroll_off cmd failed\\n\");\n            return oled_scrolling;\n        }\n        oled_scrolling = false;\n        oled_dirty     = OLED_ALL_BLOCKS_MASK;\n    }\n    return !oled_scrolling;\n}\n\nbool is_oled_scrolling(void) {\n    return oled_scrolling;\n}\n\nbool oled_invert(bool invert) {\n    if (!oled_initialized) {\n        return oled_inverted;\n    }\n\n    if (invert && !oled_inverted) {\n        static const uint8_t PROGMEM display_inverted[] = {I2C_CMD, INVERT_DISPLAY};\n        if (!oled_send_cmd_P(display_inverted, ARRAY_SIZE(display_inverted))) {\n            print(\"oled_invert cmd failed\\n\");\n            return oled_inverted;\n        }\n        oled_inverted = true;\n    } else if (!invert && oled_inverted) {\n        static const uint8_t PROGMEM display_normal[] = {I2C_CMD, NORMAL_DISPLAY};\n        if (!oled_send_cmd_P(display_normal, ARRAY_SIZE(display_normal))) {\n            print(\"oled_invert cmd failed\\n\");\n            return oled_inverted;\n        }\n        oled_inverted = false;\n    }\n\n    return oled_inverted;\n}\n\nuint8_t oled_max_chars(void) {\n    if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {\n        return OLED_DISPLAY_WIDTH / OLED_FONT_WIDTH;\n    }\n    return OLED_DISPLAY_HEIGHT / OLED_FONT_WIDTH;\n}\n\nuint8_t oled_max_lines(void) {\n    if (!HAS_FLAGS(oled_rotation, OLED_ROTATION_90)) {\n        return OLED_DISPLAY_HEIGHT / OLED_FONT_HEIGHT;\n    }\n    return OLED_DISPLAY_WIDTH / OLED_FONT_HEIGHT;\n}\n\nvoid oled_task(void) {\n    if (!oled_initialized) {\n        return;\n    }\n\n#if OLED_UPDATE_INTERVAL > 0\n    if (timer_elapsed(oled_update_timeout) >= OLED_UPDATE_INTERVAL) {\n        oled_update_timeout = timer_read();\n        oled_set_cursor(0, 0);\n        oled_task_kb();\n    }\n#else\n    oled_set_cursor(0, 0);\n    oled_task_kb();\n#endif\n\n#if OLED_SCROLL_TIMEOUT > 0\n    if (oled_dirty && oled_scrolling) {\n        oled_scroll_timeout = timer_read32() + OLED_SCROLL_TIMEOUT;\n        oled_scroll_off();\n    }\n#endif\n\n    // Smart render system, no need to check for dirty\n    oled_render();\n\n    // Display timeout check\n#if OLED_TIMEOUT > 0\n    if (oled_active && timer_expired32(timer_read32(), oled_timeout)) {\n        oled_off();\n    }\n#endif\n\n#if OLED_SCROLL_TIMEOUT > 0\n    if (!oled_scrolling && timer_expired32(timer_read32(), oled_scroll_timeout)) {\n#    ifdef OLED_SCROLL_TIMEOUT_RIGHT\n        oled_scroll_right();\n#    else\n        oled_scroll_left();\n#    endif\n    }\n#endif\n}\n\n__attribute__((weak)) bool oled_task_kb(void) {\n    return oled_task_user();\n}\n__attribute__((weak)) bool oled_task_user(void) {\n    return true;\n}\n"
  },
  {
    "path": "drivers/oled/oled_driver.h",
    "content": "/*\nCopyright 2019 Ryan Caltabiano <https://github.com/XScorpion2>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n// an enumeration of the chips this driver supports\n#define OLED_IC_SSD1306 0\n#define OLED_IC_SH1106 1\n#define OLED_IC_SH1107 2\n\n#if defined(OLED_DISPLAY_CUSTOM)\n// Expected user to implement the necessary defines\n#elif defined(OLED_DISPLAY_128X64)\n// Double height 128x64\n#    ifndef OLED_DISPLAY_WIDTH\n#        define OLED_DISPLAY_WIDTH 128\n#    endif\n#    ifndef OLED_DISPLAY_HEIGHT\n#        define OLED_DISPLAY_HEIGHT 64\n#    endif\n#    ifndef OLED_MATRIX_SIZE\n#        define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) // 1024 (compile time mathed)\n#    endif\n#    ifndef OLED_BLOCK_TYPE\n#        define OLED_BLOCK_TYPE uint16_t\n#    endif\n#    ifndef OLED_BLOCK_COUNT\n#        define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 32 (compile time mathed)\n#    endif\n#    ifndef OLED_BLOCK_SIZE\n#        define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 32 (compile time mathed)\n#    endif\n#    ifndef OLED_COM_PINS\n#        define OLED_COM_PINS COM_PINS_ALT\n#    endif\n\n// For 90 degree rotation, we map our internal matrix to oled matrix using fixed arrays\n// The OLED writes to it's memory horizontally, starting top left, but our memory starts bottom left in this mode\n#    ifndef OLED_SOURCE_MAP\n#        define OLED_SOURCE_MAP \\\n            { 0, 8, 16, 24, 32, 40, 48, 56 }\n#    endif\n#    ifndef OLED_TARGET_MAP\n#        define OLED_TARGET_MAP \\\n            { 56, 48, 40, 32, 24, 16, 8, 0 }\n#    endif\n// If OLED_BLOCK_TYPE is uint32_t, these tables would look like:\n// #define OLED_SOURCE_MAP { 32, 40, 48, 56 }\n// #define OLED_TARGET_MAP { 24, 16, 8, 0 }\n// If OLED_BLOCK_TYPE is uint16_t, these tables would look like:\n// #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56 }\n// #define OLED_TARGET_MAP { 56, 48, 40, 32, 24, 16, 8, 0 }\n// If OLED_BLOCK_TYPE is uint8_t, these tables would look like:\n// #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120 }\n// #define OLED_TARGET_MAP { 56, 120, 48, 112, 40, 104, 32, 96, 24, 88, 16, 80, 8, 72, 0, 64 }\n\n#elif defined(OLED_DISPLAY_64X32)\n#    ifndef OLED_DISPLAY_WIDTH\n#        define OLED_DISPLAY_WIDTH 64\n#    endif\n#    ifndef OLED_DISPLAY_HEIGHT\n#        define OLED_DISPLAY_HEIGHT 32\n#    endif\n#    ifndef OLED_COLUMN_OFFSET\n#        define OLED_COLUMN_OFFSET 32\n#    endif\n#    ifndef OLED_MATRIX_SIZE\n#        define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)\n#    endif\n#    ifndef OLED_BLOCK_TYPE\n#        define OLED_BLOCK_TYPE uint8_t\n#    endif\n#    ifndef OLED_BLOCK_COUNT\n#        define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 8 (compile time mathed)\n#    endif\n#    ifndef OLED_BLOCK_SIZE\n#        define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 32 (compile time mathed)\n#    endif\n#    ifndef OLED_COM_PINS\n#        define OLED_COM_PINS COM_PINS_ALT\n#    endif\n\n#    ifndef OLED_SOURCE_MAP\n#        define OLED_SOURCE_MAP \\\n            { 0, 8, 16, 24 }\n#    endif\n#    ifndef OLED_TARGET_MAP\n#        define OLED_TARGET_MAP \\\n            { 24, 16, 8, 0 }\n#    endif\n\n#elif defined(OLED_DISPLAY_64X48)\n#    ifndef OLED_DISPLAY_WIDTH\n#        define OLED_DISPLAY_WIDTH 64\n#    endif\n#    ifndef OLED_DISPLAY_HEIGHT\n#        define OLED_DISPLAY_HEIGHT 48\n#    endif\n#    ifndef OLED_COLUMN_OFFSET\n#        define OLED_COLUMN_OFFSET 32\n#    endif\n#    ifndef OLED_MATRIX_SIZE\n#        define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)\n#    endif\n#    ifndef OLED_BLOCK_TYPE\n#        define OLED_BLOCK_TYPE uint32_t\n#    endif\n#    ifndef OLED_BLOCK_COUNT\n#        define OLED_BLOCK_COUNT 24\n#    endif\n#    ifndef OLED_BLOCK_SIZE\n#        define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)\n#    endif\n#    ifndef OLED_COM_PINS\n#        define OLED_COM_PINS COM_PINS_ALT\n#    endif\n\n#    ifndef OLED_SOURCE_MAP\n#        define OLED_SOURCE_MAP \\\n            { 0, 8 }\n#    endif\n#    ifndef OLED_TARGET_MAP\n#        define OLED_TARGET_MAP \\\n            { 8, 0 }\n#    endif\n\n#elif defined(OLED_DISPLAY_64X128)\n#    ifndef OLED_DISPLAY_WIDTH\n#        define OLED_DISPLAY_WIDTH 64\n#    endif\n#    ifndef OLED_DISPLAY_HEIGHT\n#        define OLED_DISPLAY_HEIGHT 128\n#    endif\n#    ifndef OLED_IC\n#        define OLED_IC OLED_IC_SH1107\n#    endif\n#    ifndef OLED_COM_PIN_OFFSET\n#        define OLED_COM_PIN_OFFSET 32\n#    endif\n#    ifndef OLED_MATRIX_SIZE\n#        define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH)\n#    endif\n#    ifndef OLED_BLOCK_TYPE\n#        define OLED_BLOCK_TYPE uint16_t\n#    endif\n#    ifndef OLED_BLOCK_COUNT\n#        define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8)\n#    endif\n#    ifndef OLED_BLOCK_SIZE\n#        define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT)\n#    endif\n#    ifndef OLED_COM_PINS\n#        define OLED_COM_PINS COM_PINS_ALT\n#    endif\n\n#    ifndef OLED_SOURCE_MAP\n#        define OLED_SOURCE_MAP \\\n            { 0, 8, 16, 24, 32, 40, 48, 56 }\n#    endif\n#    ifndef OLED_TARGET_MAP\n#        define OLED_TARGET_MAP \\\n            { 56, 48, 40, 32, 24, 16, 8, 0 }\n#    endif\n\n#elif defined(OLED_DISPLAY_128X128)\n// Quad height 128x128\n#    ifndef OLED_DISPLAY_WIDTH\n#        define OLED_DISPLAY_WIDTH 128\n#    endif\n#    ifndef OLED_DISPLAY_HEIGHT\n#        define OLED_DISPLAY_HEIGHT 128\n#    endif\n#    ifndef OLED_IC\n#        define OLED_IC OLED_IC_SH1107\n#    endif\n#    ifndef OLED_MATRIX_SIZE\n#        define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) // 2048 (compile time mathed)\n#    endif\n#    ifndef OLED_BLOCK_TYPE\n#        define OLED_BLOCK_TYPE uint32_t\n#    endif\n#    ifndef OLED_BLOCK_COUNT\n#        define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 32 (compile time mathed)\n#    endif\n#    ifndef OLED_BLOCK_SIZE\n#        define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 64 (compile time mathed)\n#    endif\n#    ifndef OLED_COM_PINS\n#        define OLED_COM_PINS COM_PINS_ALT\n#    endif\n\n// For 90 degree rotation, we map our internal matrix to oled matrix using fixed arrays\n// The OLED writes to it's memory horizontally, starting top left, but our memory starts bottom left in this mode\n#    ifndef OLED_SOURCE_MAP\n#        define OLED_SOURCE_MAP \\\n            { 0, 8, 16, 24, 32, 40, 48, 56 }\n#    endif\n#    ifndef OLED_TARGET_MAP\n#        define OLED_TARGET_MAP \\\n            { 56, 48, 40, 32, 24, 16, 8, 0 }\n#    endif\n#else // defined(OLED_DISPLAY_128X64)\n// Default 128x32\n#    ifndef OLED_DISPLAY_WIDTH\n#        define OLED_DISPLAY_WIDTH 128\n#    endif\n#    ifndef OLED_DISPLAY_HEIGHT\n#        define OLED_DISPLAY_HEIGHT 32\n#    endif\n#    ifndef OLED_MATRIX_SIZE\n#        define OLED_MATRIX_SIZE (OLED_DISPLAY_HEIGHT / 8 * OLED_DISPLAY_WIDTH) // 512 (compile time mathed)\n#    endif\n#    ifndef OLED_BLOCK_TYPE\n#        define OLED_BLOCK_TYPE uint16_t // Type to use for segmenting the oled display for smart rendering, use unsigned types only\n#    endif\n#    ifndef OLED_BLOCK_COUNT\n#        define OLED_BLOCK_COUNT (sizeof(OLED_BLOCK_TYPE) * 8) // 16 (compile time mathed)\n#    endif\n#    ifndef OLED_BLOCK_SIZE\n#        define OLED_BLOCK_SIZE (OLED_MATRIX_SIZE / OLED_BLOCK_COUNT) // 32 (compile time mathed)\n#    endif\n#    ifndef OLED_COM_PINS\n#        define OLED_COM_PINS COM_PINS_SEQ\n#    endif\n\n// For 90 degree rotation, we map our internal matrix to oled matrix using fixed arrays\n// The OLED writes to it's memory horizontally, starting top left, but our memory starts bottom left in this mode\n#    ifndef OLED_SOURCE_MAP\n#        define OLED_SOURCE_MAP \\\n            { 0, 8, 16, 24 }\n#    endif\n#    ifndef OLED_TARGET_MAP\n#        define OLED_TARGET_MAP \\\n            { 24, 16, 8, 0 }\n#    endif\n// If OLED_BLOCK_TYPE is uint8_t, these tables would look like:\n// #define OLED_SOURCE_MAP { 0, 8, 16, 24, 32, 40, 48, 56 }\n// #define OLED_TARGET_MAP { 48, 32, 16, 0, 56, 40, 24, 8 }\n#endif // defined(OLED_DISPLAY_CUSTOM)\n\n#if !defined(OLED_IC)\n#    define OLED_IC OLED_IC_SSD1306\n#endif\n\n// the column address corresponding to the first column in the display hardware\n#if !defined(OLED_COLUMN_OFFSET)\n#    define OLED_COLUMN_OFFSET 0\n#endif\n\n// Address to use for the i2c oled communication\n#if !defined(OLED_DISPLAY_ADDRESS)\n#    define OLED_DISPLAY_ADDRESS 0x3C\n#endif\n\n// Custom font file to use\n#if !defined(OLED_FONT_H)\n#    define OLED_FONT_H \"glcdfont.c\"\n#endif\n// unsigned char value of the first character in the font file\n#if !defined(OLED_FONT_START)\n#    define OLED_FONT_START 0\n#endif\n// unsigned char value of the last character in the font file\n#if !defined(OLED_FONT_END)\n#    define OLED_FONT_END 223\n#endif\n// Font render width\n#if !defined(OLED_FONT_WIDTH)\n#    define OLED_FONT_WIDTH 6\n#endif\n// Font render height\n#if !defined(OLED_FONT_HEIGHT)\n#    define OLED_FONT_HEIGHT 8\n#endif\n// Default brightness level\n#if !defined(OLED_BRIGHTNESS)\n#    define OLED_BRIGHTNESS 255\n#endif\n\n#if !defined(OLED_TIMEOUT)\n#    if defined(OLED_DISABLE_TIMEOUT)\n#        define OLED_TIMEOUT 0\n#    else\n#        define OLED_TIMEOUT 60000\n#    endif\n#endif\n\n#if !defined(OLED_FADE_OUT_INTERVAL)\n#    define OLED_FADE_OUT_INTERVAL 0x00\n#endif\n\n#if OLED_FADE_OUT_INTERVAL > 0x0F || OLED_FADE_OUT_INTERVAL < 0x00\n#    error OLED_FADE_OUT_INTERVAL must be between 0x00 and 0x0F\n#endif\n\n#if !defined(OLED_I2C_TIMEOUT)\n#    define OLED_I2C_TIMEOUT 100\n#endif\n\n#if !defined(OLED_UPDATE_INTERVAL) && defined(SPLIT_KEYBOARD)\n#    define OLED_UPDATE_INTERVAL 50\n#endif\n\n#if !defined(OLED_UPDATE_PROCESS_LIMIT)\n#    define OLED_UPDATE_PROCESS_LIMIT 1\n#endif\n\ntypedef struct __attribute__((__packed__)) {\n    uint8_t *current_element;\n    uint16_t remaining_element_count;\n} oled_buffer_reader_t;\n\n// OLED Rotation enum values are flags\ntypedef enum {\n    OLED_ROTATION_0   = 0,\n    OLED_ROTATION_90  = 1,\n    OLED_ROTATION_180 = 2,\n    OLED_ROTATION_270 = 3, // OLED_ROTATION_90 | OLED_ROTATION_180\n} oled_rotation_t;\n\n// Initialize the oled display, rotating the rendered output based on the define passed in.\n// Returns true if the OLED was initialized successfully\nbool oled_init(oled_rotation_t rotation);\n\n// Send commands and data to screen\nbool oled_send_cmd(const uint8_t *data, uint16_t size);\nbool oled_send_cmd_P(const uint8_t *data, uint16_t size);\nbool oled_send_data(const uint8_t *data, uint16_t size);\nvoid oled_driver_init(void);\n\n// Called at the start of oled_init, weak function overridable by the user\n// rotation - the value passed into oled_init\n// Return new oled_rotation_t if you want to override default rotation\noled_rotation_t oled_init_kb(oled_rotation_t rotation);\noled_rotation_t oled_init_user(oled_rotation_t rotation);\n\n// Clears the display buffer, resets cursor position to 0, and sets the buffer to dirty for rendering\nvoid oled_clear(void);\n\n// Alias to oled_render_dirty to avoid a change in api.\n#define oled_render() oled_render_dirty(false)\n\n// Renders all dirty blocks to the display at one time or a subset depending on the value of\n// all.\nvoid oled_render_dirty(bool all);\n\n// Moves cursor to character position indicated by column and line, wraps if out of bounds\n// Max column denoted by 'oled_max_chars()' and max lines by 'oled_max_lines()' functions\nvoid oled_set_cursor(uint8_t col, uint8_t line);\n\n// Advances the cursor to the next page, writing ' ' if true\n// Wraps to the beginning when out of bounds\nvoid oled_advance_page(bool clearPageRemainder);\n\n// Moves the cursor forward 1 character length\n// Advance page if there is not enough room for the next character\n// Wraps to the beginning when out of bounds\nvoid oled_advance_char(void);\n\n// Writes a single character to the buffer at current cursor position\n// Advances the cursor while writing, inverts the pixels if true\n// Main handler that writes character data to the display buffer\nvoid oled_write_char(const char data, bool invert);\n\n// Writes a string to the buffer at current cursor position\n// Advances the cursor while writing, inverts the pixels if true\nvoid oled_write(const char *data, bool invert);\n\n// Writes a string to the buffer at current cursor position\n// Advances the cursor while writing, inverts the pixels if true\n// Advances the cursor to the next page, wiring ' ' to the remainder of the current page\nvoid oled_write_ln(const char *data, bool invert);\n\n// Pans the buffer to the right (or left by passing true) by moving contents of the buffer\n// Useful for moving the screen in preparation for new drawing\nvoid oled_pan(bool left);\n\n// Returns a pointer to the requested start index in the buffer plus remaining\n// buffer length as struct\noled_buffer_reader_t oled_read_raw(uint16_t start_index);\n\n// Writes a string to the buffer at current cursor position\nvoid oled_write_raw(const char *data, uint16_t size);\n\n// Writes a single byte into the buffer at the specified index\nvoid oled_write_raw_byte(const char data, uint16_t index);\n\n// Sets a specific pixel on or off\n// Coordinates start at top-left and go right and down for positive x and y\nvoid oled_write_pixel(uint8_t x, uint8_t y, bool on);\n\n#if defined(__AVR__)\n// Writes a PROGMEM string to the buffer at current cursor position\n// Advances the cursor while writing, inverts the pixels if true\n// Remapped to call 'void oled_write(const char *data, bool invert);' on ARM\nvoid oled_write_P(const char *data, bool invert);\n\n// Writes a PROGMEM string to the buffer at current cursor position\n// Advances the cursor while writing, inverts the pixels if true\n// Advances the cursor to the next page, wiring ' ' to the remainder of the current page\n// Remapped to call 'void oled_write_ln(const char *data, bool invert);' on ARM\nvoid oled_write_ln_P(const char *data, bool invert);\n\n// Writes a PROGMEM string to the buffer at current cursor position\nvoid oled_write_raw_P(const char *data, uint16_t size);\n#else\n#    define oled_write_P(data, invert) oled_write(data, invert)\n#    define oled_write_ln_P(data, invert) oled_write_ln(data, invert)\n#    define oled_write_raw_P(data, size) oled_write_raw(data, size)\n#endif // defined(__AVR__)\n\n// Can be used to manually turn on the screen if it is off\n// Returns true if the screen was on or turns on\nbool oled_on(void);\n\n// Can be used to manually turn off the screen if it is on\n// Returns true if the screen was off or turns off\nbool oled_off(void);\n\n// Returns true if the oled is currently on, false if it is\n// not\nbool is_oled_on(void);\n\n// Sets the brightness level of the display\nuint8_t oled_set_brightness(uint8_t level);\n\n// Gets the current brightness level of the display\nuint8_t oled_get_brightness(void);\n\n// Basically it's oled_render, but with timeout management and oled_task_user calling!\nvoid oled_task(void);\n\n// Called at the start of oled_task, weak function overridable by the user\nbool oled_task_kb(void);\nbool oled_task_user(void);\n\n// Set the specific 8 lines rows of the screen to scroll.\n// 0 is the default for start, and 7 for end, which is the entire\n// height of the screen.  For 128x32 screens, rows 4-7 are not used.\nvoid oled_scroll_set_area(uint8_t start_line, uint8_t end_line);\n\n// Sets scroll speed, 0-7, fastest to slowest. Default is three.\n// Does not take effect until scrolling is either started or restarted\n// the ssd1306 supports 8 speeds with the delay\n// listed below between each frame of the scrolling effect\n// 0=2, 1=3, 2=4, 3=5, 4=25, 5=64, 6=128, 7=256\nvoid oled_scroll_set_speed(uint8_t speed);\n\n// Begin scrolling the entire display right\n// Returns true if the screen was scrolling or starts scrolling\n// NOTE: display contents cannot be changed while scrolling\nbool oled_scroll_right(void);\n\n// Begin scrolling the entire display left\n// Returns true if the screen was scrolling or starts scrolling\n// NOTE: display contents cannot be changed while scrolling\nbool oled_scroll_left(void);\n\n// Turns off display scrolling\n// Returns true if the screen was not scrolling or stops scrolling\nbool oled_scroll_off(void);\n\n// Returns true if the oled is currently scrolling, false if it is\n// not\nbool is_oled_scrolling(void);\n\n// Inverts the display\n// Returns true if the screen was or is inverted\nbool oled_invert(bool invert);\n\n// Returns the maximum number of characters that will fit on a line\nuint8_t oled_max_chars(void);\n\n// Returns the maximum number of lines that will fit on the oled\nuint8_t oled_max_lines(void);\n"
  },
  {
    "path": "drivers/painter/comms/qp_comms_dummy.c",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#ifdef QUANTUM_PAINTER_DUMMY_COMMS_ENABLE\n\n#    include \"qp_comms_dummy.h\"\n\nstatic bool dummy_comms_init(painter_device_t device) {\n    // No-op.\n    return true;\n}\n\nstatic bool dummy_comms_start(painter_device_t device) {\n    // No-op.\n    return true;\n}\n\nstatic void dummy_comms_stop(painter_device_t device) {\n    // No-op.\n}\n\nuint32_t dummy_comms_send(painter_device_t device, const void *data, uint32_t byte_count) {\n    // No-op.\n    return byte_count;\n}\n\npainter_comms_vtable_t dummy_comms_vtable = {\n    // These are all effective no-op's because they're not actually needed.\n    .comms_init  = dummy_comms_init,\n    .comms_start = dummy_comms_start,\n    .comms_stop  = dummy_comms_stop,\n    .comms_send  = dummy_comms_send};\n\n#endif // QUANTUM_PAINTER_DUMMY_COMMS_ENABLE\n"
  },
  {
    "path": "drivers/painter/comms/qp_comms_dummy.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifdef QUANTUM_PAINTER_DUMMY_COMMS_ENABLE\n\n#    include \"qp_internal.h\"\n\nextern painter_comms_vtable_t dummy_comms_vtable;\n\n#endif // QUANTUM_PAINTER_DUMMY_COMMS_ENABLE\n"
  },
  {
    "path": "drivers/painter/comms/qp_comms_i2c.c",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#ifdef QUANTUM_PAINTER_I2C_ENABLE\n\n#    include \"i2c_master.h\"\n#    include \"qp_comms_i2c.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Helpers\n\nstatic uint32_t qp_comms_i2c_send_raw(painter_device_t device, const void *data, uint32_t byte_count) {\n    painter_driver_t *     driver       = (painter_driver_t *)device;\n    qp_comms_i2c_config_t *comms_config = (qp_comms_i2c_config_t *)driver->comms_config;\n    i2c_status_t           res          = i2c_transmit(comms_config->chip_address << 1, data, byte_count, I2C_TIMEOUT);\n    if (res < 0) {\n        return 0;\n    }\n    return byte_count;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Base I2C support\n\nbool qp_comms_i2c_init(painter_device_t device) {\n    i2c_init();\n    return true;\n}\n\nbool qp_comms_i2c_start(painter_device_t device) {\n    return true;\n}\n\nuint32_t qp_comms_i2c_send_data(painter_device_t device, const void *data, uint32_t byte_count) {\n    return qp_comms_i2c_send_raw(device, data, byte_count);\n}\n\nvoid qp_comms_i2c_stop(painter_device_t device) {}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Command+Data I2C support\n\nstatic const uint8_t cmd_byte  = 0x00;\nstatic const uint8_t data_byte = 0x40;\n\nvoid qp_comms_i2c_cmddata_send_command(painter_device_t device, uint8_t cmd) {\n    uint8_t buf[2] = {cmd_byte, cmd};\n    qp_comms_i2c_send_raw(device, &buf, 2);\n}\n\nuint32_t qp_comms_i2c_cmddata_send_data(painter_device_t device, const void *data, uint32_t byte_count) {\n    uint8_t buf[1 + byte_count];\n    buf[0] = data_byte;\n    memcpy(&buf[1], data, byte_count);\n    if (qp_comms_i2c_send_raw(device, buf, sizeof(buf)) != sizeof(buf)) {\n        return 0;\n    }\n    return byte_count;\n}\n\nvoid qp_comms_i2c_bulk_command_sequence(painter_device_t device, const uint8_t *sequence, size_t sequence_len) {\n    uint8_t buf[32];\n    for (size_t i = 0; i < sequence_len;) {\n        uint8_t command   = sequence[i];\n        uint8_t delay     = sequence[i + 1];\n        uint8_t num_bytes = sequence[i + 2];\n        buf[0]            = cmd_byte;\n        buf[1]            = command;\n        memcpy(&buf[2], &sequence[i + 3], num_bytes);\n        qp_comms_i2c_send_raw(device, buf, num_bytes + 2);\n        if (delay > 0) {\n            wait_ms(delay);\n        }\n        i += (3 + num_bytes);\n    }\n}\n\nconst painter_comms_with_command_vtable_t i2c_comms_cmddata_vtable = {\n    .base =\n        {\n            .comms_init  = qp_comms_i2c_init,\n            .comms_start = qp_comms_i2c_start,\n            .comms_send  = qp_comms_i2c_cmddata_send_data,\n            .comms_stop  = qp_comms_i2c_stop,\n        },\n    .send_command          = qp_comms_i2c_cmddata_send_command,\n    .bulk_command_sequence = qp_comms_i2c_bulk_command_sequence,\n};\n\n#endif // QUANTUM_PAINTER_I2C_ENABLE\n"
  },
  {
    "path": "drivers/painter/comms/qp_comms_i2c.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifdef QUANTUM_PAINTER_I2C_ENABLE\n\n#    include <stdint.h>\n\n#    include \"gpio.h\"\n#    include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Base I2C support\n\ntypedef struct qp_comms_i2c_config_t {\n    uint8_t chip_address;\n} qp_comms_i2c_config_t;\n\nbool     qp_comms_i2c_init(painter_device_t device);\nbool     qp_comms_i2c_start(painter_device_t device);\nuint32_t qp_comms_i2c_send_data(painter_device_t device, const void* data, uint32_t byte_count);\nvoid     qp_comms_i2c_stop(painter_device_t device);\n\nextern const painter_comms_with_command_vtable_t i2c_comms_cmddata_vtable;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#endif // QUANTUM_PAINTER_I2C_ENABLE\n"
  },
  {
    "path": "drivers/painter/comms/qp_comms_spi.c",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#ifdef QUANTUM_PAINTER_SPI_ENABLE\n\n#    include \"spi_master.h\"\n#    include \"qp_comms_spi.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Base SPI support\n\nbool qp_comms_spi_init(painter_device_t device) {\n    painter_driver_t *     driver       = (painter_driver_t *)device;\n    qp_comms_spi_config_t *comms_config = (qp_comms_spi_config_t *)driver->comms_config;\n\n    // Initialize the SPI peripheral\n    spi_init();\n\n    // Set up CS as output high\n    gpio_set_pin_output(comms_config->chip_select_pin);\n    gpio_write_pin_high(comms_config->chip_select_pin);\n\n    return true;\n}\n\nbool qp_comms_spi_start(painter_device_t device) {\n    painter_driver_t *     driver       = (painter_driver_t *)device;\n    qp_comms_spi_config_t *comms_config = (qp_comms_spi_config_t *)driver->comms_config;\n\n    return spi_start(comms_config->chip_select_pin, comms_config->lsb_first, comms_config->mode, comms_config->divisor);\n}\n\nuint32_t qp_comms_spi_send_data(painter_device_t device, const void *data, uint32_t byte_count) {\n    uint32_t       bytes_remaining = byte_count;\n    const uint8_t *p               = (const uint8_t *)data;\n    const uint32_t max_msg_length  = 1024;\n\n    while (bytes_remaining > 0) {\n        uint32_t bytes_this_loop = QP_MIN(bytes_remaining, max_msg_length);\n        spi_transmit(p, bytes_this_loop);\n        p += bytes_this_loop;\n        bytes_remaining -= bytes_this_loop;\n    }\n\n    return byte_count - bytes_remaining;\n}\n\nvoid qp_comms_spi_stop(painter_device_t device) {\n    painter_driver_t *     driver       = (painter_driver_t *)device;\n    qp_comms_spi_config_t *comms_config = (qp_comms_spi_config_t *)driver->comms_config;\n    spi_stop();\n    gpio_write_pin_high(comms_config->chip_select_pin);\n}\n\nconst painter_comms_vtable_t spi_comms_vtable = {\n    .comms_init  = qp_comms_spi_init,\n    .comms_start = qp_comms_spi_start,\n    .comms_send  = qp_comms_spi_send_data,\n    .comms_stop  = qp_comms_spi_stop,\n};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SPI with D/C and RST pins\n\n#    ifdef QUANTUM_PAINTER_SPI_DC_RESET_ENABLE\n\nbool qp_comms_spi_dc_reset_init(painter_device_t device) {\n    if (!qp_comms_spi_init(device)) {\n        return false;\n    }\n\n    painter_driver_t *              driver       = (painter_driver_t *)device;\n    qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;\n\n    // Set up D/C as output low, if specified\n    if (comms_config->dc_pin != NO_PIN) {\n        gpio_set_pin_output(comms_config->dc_pin);\n        gpio_write_pin_low(comms_config->dc_pin);\n    }\n\n    // Set up RST as output, if specified, performing a reset in the process\n    if (comms_config->reset_pin != NO_PIN) {\n        gpio_set_pin_output(comms_config->reset_pin);\n        gpio_write_pin_low(comms_config->reset_pin);\n        wait_ms(20);\n        gpio_write_pin_high(comms_config->reset_pin);\n        wait_ms(20);\n    }\n\n    return true;\n}\n\nuint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void *data, uint32_t byte_count) {\n    painter_driver_t *              driver       = (painter_driver_t *)device;\n    qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;\n    gpio_write_pin_high(comms_config->dc_pin);\n    return qp_comms_spi_send_data(device, data, byte_count);\n}\n\nvoid qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd) {\n    painter_driver_t *              driver       = (painter_driver_t *)device;\n    qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;\n    gpio_write_pin_low(comms_config->dc_pin);\n    spi_write(cmd);\n}\n\nvoid qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const uint8_t *sequence, size_t sequence_len) {\n    painter_driver_t *              driver       = (painter_driver_t *)device;\n    qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;\n    for (size_t i = 0; i < sequence_len;) {\n        uint8_t command   = sequence[i];\n        uint8_t delay     = sequence[i + 1];\n        uint8_t num_bytes = sequence[i + 2];\n        qp_comms_spi_dc_reset_send_command(device, command);\n        if (num_bytes > 0) {\n            if (comms_config->command_params_uses_command_pin) {\n                for (uint8_t j = 0; j < num_bytes; j++) {\n                    qp_comms_spi_dc_reset_send_command(device, sequence[i + 3 + j]);\n                }\n            } else {\n                qp_comms_spi_dc_reset_send_data(device, &sequence[i + 3], num_bytes);\n            }\n        }\n        if (delay > 0) {\n            wait_ms(delay);\n        }\n        i += (3 + num_bytes);\n    }\n}\n\nconst painter_comms_with_command_vtable_t spi_comms_with_dc_vtable = {\n    .base =\n        {\n            .comms_init  = qp_comms_spi_dc_reset_init,\n            .comms_start = qp_comms_spi_start,\n            .comms_send  = qp_comms_spi_dc_reset_send_data,\n            .comms_stop  = qp_comms_spi_stop,\n        },\n    .send_command          = qp_comms_spi_dc_reset_send_command,\n    .bulk_command_sequence = qp_comms_spi_dc_reset_bulk_command_sequence,\n};\n\n#    endif // QUANTUM_PAINTER_SPI_DC_RESET_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#endif // QUANTUM_PAINTER_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/comms/qp_comms_spi.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifdef QUANTUM_PAINTER_SPI_ENABLE\n\n#    include <stdint.h>\n\n#    include \"gpio.h\"\n#    include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Base SPI support\n\ntypedef struct qp_comms_spi_config_t {\n    pin_t    chip_select_pin;\n    uint16_t divisor;\n    bool     lsb_first;\n    int8_t   mode;\n} qp_comms_spi_config_t;\n\nbool     qp_comms_spi_init(painter_device_t device);\nbool     qp_comms_spi_start(painter_device_t device);\nuint32_t qp_comms_spi_send_data(painter_device_t device, const void* data, uint32_t byte_count);\nvoid     qp_comms_spi_stop(painter_device_t device);\n\nextern const painter_comms_vtable_t spi_comms_vtable;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SPI with D/C and RST pins\n\n#    ifdef QUANTUM_PAINTER_SPI_DC_RESET_ENABLE\n\ntypedef struct qp_comms_spi_dc_reset_config_t {\n    qp_comms_spi_config_t spi_config;\n    pin_t                 dc_pin;\n    pin_t                 reset_pin;\n    bool                  command_params_uses_command_pin; // keep D/C held low when sending command sequences for data bytes\n} qp_comms_spi_dc_reset_config_t;\n\nbool     qp_comms_spi_dc_reset_init(painter_device_t device);\nvoid     qp_comms_spi_dc_reset_send_command(painter_device_t device, uint8_t cmd);\nuint32_t qp_comms_spi_dc_reset_send_data(painter_device_t device, const void* data, uint32_t byte_count);\nvoid     qp_comms_spi_dc_reset_bulk_command_sequence(painter_device_t device, const uint8_t* sequence, size_t sequence_len);\n\nextern const painter_comms_with_command_vtable_t spi_comms_with_dc_vtable;\n\n#    endif // QUANTUM_PAINTER_SPI_DC_RESET_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#endif // QUANTUM_PAINTER_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/gc9xxx/qp_gc9107.c",
    "content": "// Copyright 2024 Fernando Birra\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_gc9107.h\"\n#include \"qp_gc9xxx_opcodes.h\"\n#include \"qp_gc9107_opcodes.h\"\n#include \"qp_tft_panel.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver storage\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntft_panel_dc_reset_painter_device_t gc9107_drivers[GC9107_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Initialization\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n__attribute__((weak)) bool qp_gc9107_init(painter_device_t device, painter_rotation_t rotation) {\n    // A lot of these \"unknown\" opcodes are sourced from other OSS projects and are seemingly required for this display to function.\n    // clang-format off\n    const uint8_t gc9107_init_sequence[] = {\n        GC9XXX_SET_INTER_REG_ENABLE1,   5,  0,\n        GC9XXX_SET_INTER_REG_ENABLE2,   5,  0,\n        GC9107_SET_FUNCTION_CTL6, 0, 1, GC9107_ALLOW_SET_COMPLEMENT_RGB | 0x08 | GC9107_ALLOW_SET_FRAMERATE,\n        GC9107_SET_COMPLEMENT_RGB, 0, 1, GC9107_COMPLEMENT_WITH_LSB,\n        0xAB, 0, 1, 0x0E,\n        GC9107_SET_FRAME_RATE, 0, 1, 0x19,\n        GC9XXX_SET_PIXEL_FORMAT, 0, 1, GC9107_PIXEL_FORMAT_16_BPP_IFPF,\n        GC9XXX_CMD_SLEEP_OFF,   120, 0,\n        GC9XXX_CMD_DISPLAY_ON,  20,  0\n    };\n\n    // clang-format on\n    qp_comms_bulk_command_sequence(device, gc9107_init_sequence, sizeof(gc9107_init_sequence));\n\n    // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)\n    const uint8_t madctl[] = {\n        [QP_ROTATION_0]   = GC9XXX_MADCTL_BGR,\n        [QP_ROTATION_90]  = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MX | GC9XXX_MADCTL_MV,\n        [QP_ROTATION_180] = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MX | GC9XXX_MADCTL_MY,\n        [QP_ROTATION_270] = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MV | GC9XXX_MADCTL_MY,\n    };\n    qp_comms_command_databyte(device, GC9XXX_SET_MEM_ACS_CTL, madctl[rotation]);\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nconst tft_panel_dc_reset_painter_driver_vtable_t gc9107_driver_vtable = {\n    .base =\n        {\n            .init            = qp_gc9107_init,\n            .power           = qp_tft_panel_power,\n            .clear           = qp_tft_panel_clear,\n            .flush           = qp_tft_panel_flush,\n            .pixdata         = qp_tft_panel_pixdata,\n            .viewport        = qp_tft_panel_viewport,\n            .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,\n            .append_pixels   = qp_tft_panel_append_pixels_rgb565,\n            .append_pixdata  = qp_tft_panel_append_pixdata,\n        },\n    .num_window_bytes   = 2,\n    .swap_window_coords = false,\n    .opcodes =\n        {\n            .display_on         = GC9XXX_CMD_DISPLAY_ON,\n            .display_off        = GC9XXX_CMD_DISPLAY_OFF,\n            .set_column_address = GC9XXX_SET_COL_ADDR,\n            .set_row_address    = GC9XXX_SET_ROW_ADDR,\n            .enable_writes      = GC9XXX_SET_MEM,\n        },\n};\n\n#ifdef QUANTUM_PAINTER_GC9107_SPI_ENABLE\n// Factory function for creating a handle to the GC9107 device\npainter_device_t qp_gc9107_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < GC9107_NUM_DEVICES; ++i) {\n        tft_panel_dc_reset_painter_device_t *driver = &gc9107_drivers[i];\n        if (!driver->base.driver_vtable) {\n            driver->base.driver_vtable         = (const painter_driver_vtable_t *)&gc9107_driver_vtable;\n            driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;\n            driver->base.native_bits_per_pixel = 16; // RGB565\n            driver->base.panel_width           = panel_width;\n            driver->base.panel_height          = panel_height;\n            driver->base.rotation              = QP_ROTATION_0;\n            driver->base.offset_x              = 2;\n            driver->base.offset_y              = 1;\n\n            // SPI and other pin configuration\n            driver->base.comms_config                                   = &driver->spi_dc_reset_config;\n            driver->spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;\n            driver->spi_dc_reset_config.spi_config.divisor              = spi_divisor;\n            driver->spi_dc_reset_config.spi_config.lsb_first            = false;\n            driver->spi_dc_reset_config.spi_config.mode                 = spi_mode;\n            driver->spi_dc_reset_config.dc_pin                          = dc_pin;\n            driver->spi_dc_reset_config.reset_pin                       = reset_pin;\n            driver->spi_dc_reset_config.command_params_uses_command_pin = false;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_GC9107_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/gc9xxx/qp_gc9107.h",
    "content": "// Copyright 2024 Fernando Birra (@gr1mr3aver)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter GC9107 configurables (add to your keyboard's config.h)\n\n#ifndef GC9107_NUM_DEVICES\n/**\n * @def This controls the maximum number of GC9107 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define GC9107_NUM_DEVICES 1\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter GC9107 device factories\n\n#ifdef QUANTUM_PAINTER_GC9107_SPI_ENABLE\n/**\n * Factory method for an GC9107 SPI LCD device.\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_gc9107_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n\n#endif // QUANTUM_PAINTER_GC9107_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/gc9xxx/qp_gc9107_opcodes.h",
    "content": "// Copyright 2024 Fernando Birra\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter GC9107 command opcodes\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#define GC9107_GET_POWER_MODE 0x0A  // Get power mode\n#define GC9107_GET_MADCTL 0x0B      // Get MADCTL\n#define GC9107_GET_PIXEL_FMT 0x0C   // Get pixel format\n#define GC9107_GET_IMAGE_FMT 0x0D   // Get image format\n#define GC9107_GET_SIGNAL_MODE 0x0E // Get signal mode\n#define GC9107_GET_DIAG_RESULT 0x0F // Get self-diagnostic results\n\n#define GC9107_SET_FRAME_RATE 0xA8        // Set frame rate\n#define GC9107_SET_COMPLEMENT_RGB 0xAC    // Set complement Principle RGB\n#define GC9107_SET_BLANK_PORCH 0xAD       // Set blank porch control, 0;front_porch[6:0],0;back_porch[6:0]\n#define GC9107_SET_FUNCTION_CTL1 0xB1     // Set access to AVDD_VCL_CLK and VGH_VGL_CLK commands\n#define GC9107_SET_FUNCTION_CTL2 0xB2     // Set access to VGH, VGH control commands\n#define GC9107_SET_FUNCTION_CTL3 0xB3     // Set access to Gamma control commands\n#define GC9107_SET_DISPLAY_INVERSION 0xB4 // Set Display Inversion control\n#define GC9107_SET_FUNCTION_CTL6 0xB6     // Set access to commands SET_FRAME_RATE, SET_COMPLEMENT_RGB and SET_BLANK_PORCH\n#define GC9107_SET_CUSTOM_ID_INFO 0xD3    // Set customized display id information\n#define GC9107_AVDD_VCL_CLK 0xE3          // AVDD_CLK\n#define GC9107_SET_VGH 0xE8               // Set VGH\n#define GC9107_SET_VGL 0xE9               // Set VGL\n#define GC9107_SET_VGH_VGL_CLK 0xEA       // Set VGH and VGL clock divisors\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// GC9107 Parameter constants\n\n//      Parameter values for\n//      GC9107_SET_PIXEL_FORMAT\n#define GC9107_PIXEL_FORMAT_12_BPP_IFPF (0b001 << 0) // 12 bits per pixel\n#define GC9107_PIXEL_FORMAT_16_BPP_IFPF (0b101 << 0) // 16 bits per pixel\n#define GC9107_PIXEL_FORMAT_18_BPP_IFPB (0b110 << 0) // 18 bits per pixel\n\n//      Parameter values for\n//      GC9107_SET_COMPLEMENT_RGB\n#define GC9107_COMPLEMENT_WITH_0 0x00   // R0 <- B0 <- 0, except if data is FFh\n#define GC9107_COMPLEMENT_WITH_1 0x40   // R0 <- B0 <- 1, except if data is 00h\n#define GC9107_COMPLEMENT_WITH_MSB 0x80 // R0 <- R5, B0 <- B5\n#define GC9107_COMPLEMENT_WITH_LSB 0xC0 // R0 <- B0 <- G0\n//      Parameter masks for\n//      GC9107_SET_FUNCTION_CTL1\n#define GC9107_ALLOW_AVDD_VCL_CLK 0b00001000 // Allow AVDD_VCL_CLK command\n//      Parameter masks for\n//      GC9107_SET_FUNCTION_CTL2\n#define GC9107_ALLOW_SET_VGH 0b00000001         // Allow GC9107_SET_VGH\n#define GC9107_ALLOW_SET_VGL 0b00000010         // Allow GC9107_SET_VGL\n#define GC9107_ALLOW_SET_VGH_VGL_CLK 0b00000100 // Allow GC9107_SET_VGH_VGL_CLK\n//      Parameter masks for\n//      GC9107_SET_FUNCTION_CTL3\n#define GC9107_ALLOW_SET_GAMMA1 0b00000001 // Allow GC9107_SET_GAMMA1\n#define GC9107_ALLOW_SET_GAMMA2 0b00000010 // Allow GC9107_SET_GAMMA2\n//      Parameter mask for\n//      GC9107_SET_FUNCTION_CTL6\n#define GC9107_ALLOW_SET_FRAMERATE 0b000000001      // Allow GC9107_SET_FRAME_RATE\n#define GC9107_ALLOW_SET_COMPLEMENT_RGB 0b000010000 // Allow GC9107_SET_COMPLEMENT_RGB\n#define GC9107_ALLOW_SET_BLANK_PORCH 0b000100000    // Allow GFC9107_SET_BLANK_PORCH\n//      Parameter values for\n//      AVDD_CLK_AD part (Most significant nibble)\n#define GC9107_AVDD_CLK_AD_2T 0x00\n#define GC9107_AVDD_CLK_AD_3T 0x10\n#define GC9107_AVDD_CLK_AD_4T 0x20\n#define GC9107_AVDD_CLK_AD_5T 0x30\n#define GC9107_AVDD_CLK_AD_6T 0x40\n#define GC9107_AVDD_CLK_AD_7T 0x50\n#define GC9107_AVDD_CLK_AD_8T 0x60\n#define GC9107_AVDD_CLK_AD_9T 0x70\n//      Parameter values for\n//      VCL_CLK_AD part (Least significant nibble)\n#define GC9107_VCL_CLK_AD_2T 0x00\n#define GC9107_VCL_CLK_AD_3T 0x01\n#define GC9107_VCL_CLK_AD_4T 0x02\n#define GC9107_VCL_CLK_AD_5T 0x03\n#define GC9107_VCL_CLK_AD_6T 0x04\n#define GC9107_VCL_CLK_AD_7T 0x05\n#define GC9107_VCL_CLK_AD_8T 0x06\n#define GC9107_VCL_CLK_AD_9T 0x07\n//      Parameter values for\n//      GC9107_SET_VGH\n#define GC9107_VGH_P100 0x20 // +10 V\n#define GC9107_VGH_P110 0x21 // +11 V\n#define GC9107_VGH_P120 0x22 // +12 V\n#define GC9107_VGH_P130 0x23 // +13 V\n#define GC9107_VGH_P140 0x24 // +14 V\n#define GC9107_VGH_P150 0x25 // +15 V\n//      Parameter values for\n//      GC9107_SET_VGL\n#define VGL_N_075 0x40 // -7.5 V\n#define VGL_N_085 0x41 // -8.5 V\n#define VGL_N_095 0x42 // -9.5 V\n#define VGL_N_100 0x43 // -10.0 V\n#define VGL_N_105 0x44 // -10.5 V\n#define VGL_N_110 0x45 // -11.0 V\n#define VGL_N_120 0x46 // -12.0 V\n#define VGL_N_130 0x47 // -13.0 V\n// Parameter masks for\n// GC9107_SET_VGH_VGL_CLK (VGH Divisor)\n#define GC9107_VGH_CLK_DIV_2 0x00  // Clock divisor = 2 -> 6.0 Mhz\n#define GC9107_VGH_CLK_DIV_3 0x10  // Clock divisor = 3 -> 4.0 Mhz\n#define GC9107_VGH_CLK_DIV_4 0x20  // Clock divisor = 4 -> 3.0 Mhz\n#define GC9107_VGH_CLK_DIV_5 0x30  // Clock divisor = 5 -> 2.4 Mhz\n#define GC9107_VGH_CLK_DIV_6 0x40  // Clock divisor = 6 -> 2.0 Mhz\n#define GC9107_VGH_CLK_DIV_7 0x50  // Clock divisor = 7 -> 1.7 Mhz\n#define GC9107_VGH_CLK_DIV_8 0x60  // Clock divisor = 8 -> 1.5 Mhz\n#define GC9107_VGH_CLK_DIV_9 0x70  // Clock divisor = 9 -> 1.3 Mhz\n#define GC9107_VGH_CLK_DIV_10 0x80 // Clock divisor = 10 -> 1.2 Mhz\n#define GC9107_VGH_CLK_DIV_12 0x90 // Clock divisor = 12 -> 1.0 Mhz\n#define GC9107_VGH_CLK_DIV_15 0xA0 // Clock divisor = 15 -> 0.8 Mhz\n#define GC9107_VGH_CLK_DIV_20 0xB0 // Clock divisor = 20 -> 0.6 Mhz\n#define GC9107_VGH_CLK_DIV_24 0xC0 // Clock divisor = 24 -> 0.5 Mhz\n#define GC9107_VGH_CLK_DIV_30 0xD0 // Clock divisor = 30 -> 0.4 Mhz\n#define GC9107_VGH_CLK_DIV_40 0xE0 // Clock divisor = 40 -> 0.3 Mhz\n#define GC9107_VGH_CLK_DIV_60 0xE0 // Clock divisor = 40 -> 0.2 Mhz\n// Parameter masks for\n// GC9107_SET_VGH_VGL_CLK (VGL Divisor)\n#define GC9107_VGL_CLK_DIV_2 0x00  // Clock divisor = 2 -> 6.0 Mhz\n#define GC9107_VGL_CLK_DIV_3 0x01  // Clock divisor = 3 -> 4.0 Mhz\n#define GC9107_VGL_CLK_DIV_4 0x02  // Clock divisor = 4 -> 3.0 Mhz\n#define GC9107_VGL_CLK_DIV_5 0x03  // Clock divisor = 5 -> 2.4 Mhz\n#define GC9107_VGL_CLK_DIV_6 0x04  // Clock divisor = 6 -> 2.0 Mhz\n#define GC9107_VGL_CLK_DIV_7 0x05  // Clock divisor = 7 -> 1.7 Mhz\n#define GC9107_VGL_CLK_DIV_8 0x06  // Clock divisor = 8 -> 1.5 Mhz\n#define GC9107_VGL_CLK_DIV_9 0x07  // Clock divisor = 9 -> 1.3 Mhz\n#define GC9107_VGL_CLK_DIV_10 0x08 // Clock divisor = 10 -> 1.2 Mhz\n#define GC9107_VGL_CLK_DIV_12 0x09 // Clock divisor = 12 -> 1.0 Mhz\n#define GC9107_VGL_CLK_DIV_15 0x0A // Clock divisor = 15 -> 0.8 Mhz\n#define GC9107_VGL_CLK_DIV_20 0x0B // Clock divisor = 20 -> 0.6 Mhz\n#define GC9107_VGL_CLK_DIV_24 0x0C // Clock divisor = 24 -> 0.5 Mhz\n#define GC9107_VGL_CLK_DIV_30 0x0D // Clock divisor = 30 -> 0.4 Mhz\n#define GC9107_VGL_CLK_DIV_40 0x0E // Clock divisor = 40 -> 0.3 Mhz\n#define GC9107_VGL_CLK_DIV_60 0x0E // Clock divisor = 40 -> 0.2 Mhz\n"
  },
  {
    "path": "drivers/painter/gc9xxx/qp_gc9a01.c",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_gc9a01.h\"\n#include \"qp_gc9xxx_opcodes.h\"\n#include \"qp_gc9a01_opcodes.h\"\n#include \"qp_tft_panel.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver storage\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntft_panel_dc_reset_painter_device_t gc9a01_drivers[GC9A01_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Initialization\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n__attribute__((weak)) bool qp_gc9a01_init(painter_device_t device, painter_rotation_t rotation) {\n    // A lot of these \"unknown\" opcodes are sourced from other OSS projects and are seemingly required for this display to function.\n    // clang-format off\n\n    const uint8_t gc9a01_init_sequence[] = {\n        // Command,                 Delay,  N, Data[N]\n        GC9XXX_SET_INTER_REG_ENABLE1,   0,  0,\n        GC9XXX_SET_INTER_REG_ENABLE2,   0,  0,\n        0x84,                           0,  1, 0x40,\n        GC9A01_SET_FUNCTION_CTL,        0,  3, 0x00, GC9A01_SOURCE_OUTPUT_SCAN_DIRECTION_S360_TO_S1 | GC9A01_GATE_OUTPUT_SCAN_DIRECTION_G1_TO_G32, GC9A01_LCD_DRIVE_LINE_240, // Only works if the previous command is present (undocumented)\n        GC9A01_SET_POWER_CTL_2,         0,  1, 0x20,\n        GC9A01_SET_POWER_CTL_3,         0,  1, 0x20,\n        GC9A01_SET_POWER_CTL_4,         0,  1, 0x22,\n        GC9XXX_SET_GAMMA1,              0,  6, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A,\n        GC9XXX_SET_GAMMA2,              0,  6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F,\n        GC9A01_SET_GAMMA3,              0,  6, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A,\n        GC9A01_SET_GAMMA4,              0,  6, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F,\n        0x66,                           0, 10, 0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00,\n        0x67,                           0, 10, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98,\n        GC9XXX_CMD_TEARING_ON,          0,  0,\n        GC9XXX_SET_PIXEL_FORMAT,        0,  1, GC9A01_PIXEL_FORMAT_16_BPP_DBI,\n        GC9XXX_CMD_INVERT_ON,           0,  0,\n        GC9XXX_CMD_SLEEP_OFF,           120, 0,\n        GC9XXX_CMD_DISPLAY_ON,          20,  0\n    };\n    // clang-format on\n\n    qp_comms_bulk_command_sequence(device, gc9a01_init_sequence, sizeof(gc9a01_init_sequence));\n\n    // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)\n    const uint8_t madctl[] = {\n        [QP_ROTATION_0]   = GC9XXX_MADCTL_BGR,\n        [QP_ROTATION_90]  = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MX | GC9XXX_MADCTL_MV,\n        [QP_ROTATION_180] = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MX | GC9XXX_MADCTL_MY,\n        [QP_ROTATION_270] = GC9XXX_MADCTL_BGR | GC9XXX_MADCTL_MV | GC9XXX_MADCTL_MY,\n    };\n    qp_comms_command_databyte(device, GC9XXX_SET_MEM_ACS_CTL, madctl[rotation]);\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nconst tft_panel_dc_reset_painter_driver_vtable_t gc9a01_driver_vtable = {\n    .base =\n        {\n            .init            = qp_gc9a01_init,\n            .power           = qp_tft_panel_power,\n            .clear           = qp_tft_panel_clear,\n            .flush           = qp_tft_panel_flush,\n            .pixdata         = qp_tft_panel_pixdata,\n            .viewport        = qp_tft_panel_viewport,\n            .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,\n            .append_pixels   = qp_tft_panel_append_pixels_rgb565,\n            .append_pixdata  = qp_tft_panel_append_pixdata,\n        },\n    .num_window_bytes   = 2,\n    .swap_window_coords = false,\n    .opcodes =\n        {\n            .display_on         = GC9XXX_CMD_DISPLAY_ON,\n            .display_off        = GC9XXX_CMD_DISPLAY_OFF,\n            .set_column_address = GC9XXX_SET_COL_ADDR,\n            .set_row_address    = GC9XXX_SET_ROW_ADDR,\n            .enable_writes      = GC9XXX_SET_MEM,\n        },\n};\n\n#ifdef QUANTUM_PAINTER_GC9A01_SPI_ENABLE\n// Factory function for creating a handle to the ILI9341 device\npainter_device_t qp_gc9a01_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < GC9A01_NUM_DEVICES; ++i) {\n        tft_panel_dc_reset_painter_device_t *driver = &gc9a01_drivers[i];\n        if (!driver->base.driver_vtable) {\n            driver->base.driver_vtable         = (const painter_driver_vtable_t *)&gc9a01_driver_vtable;\n            driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;\n            driver->base.native_bits_per_pixel = 16; // RGB565\n            driver->base.panel_width           = panel_width;\n            driver->base.panel_height          = panel_height;\n            driver->base.rotation              = QP_ROTATION_0;\n            driver->base.offset_x              = 0;\n            driver->base.offset_y              = 0;\n\n            // SPI and other pin configuration\n            driver->base.comms_config                                   = &driver->spi_dc_reset_config;\n            driver->spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;\n            driver->spi_dc_reset_config.spi_config.divisor              = spi_divisor;\n            driver->spi_dc_reset_config.spi_config.lsb_first            = false;\n            driver->spi_dc_reset_config.spi_config.mode                 = spi_mode;\n            driver->spi_dc_reset_config.dc_pin                          = dc_pin;\n            driver->spi_dc_reset_config.reset_pin                       = reset_pin;\n            driver->spi_dc_reset_config.command_params_uses_command_pin = false;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_GC9A01_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/gc9xxx/qp_gc9a01.h",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter GC9A01 configurables (add to your keyboard's config.h)\n\n#ifndef GC9A01_NUM_DEVICES\n/**\n * @def This controls the maximum number of GC9A01 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define GC9A01_NUM_DEVICES 1\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter GC9A01 device factories\n\n#ifdef QUANTUM_PAINTER_GC9A01_SPI_ENABLE\n/**\n * Factory method for an GC9A01 SPI LCD device.\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_gc9a01_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n#endif // QUANTUM_PAINTER_GC9A01_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/gc9xxx/qp_gc9a01_opcodes.h",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// Copyright 2024 Fernando Birra\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter GC9A01 command opcodes\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#define GC9A01_SET_MEM_CONT 0x3C    // Set memory continue\n#define GC9A01_SET_BRIGHTNESS 0x51  // Set brightness\n#define GC9A01_SET_DISPLAY_CTL 0x53 // Set display ctl\n\n#define GC9A01_SET_RGB_IF_SIG_CTL 0xB0     // RGB IF signal ctl\n#define GC9A01_SET_BLANKING_PORCH_CTL 0xB5 // Set blanking porch ctl\n#define GC9A01_SET_FUNCTION_CTL 0xB6       // Set function ctl\n#define GC9A01_SET_TEARING_EFFECT 0xBA     // Set tering effect control\n#define GC9A01_SET_POWER_CTL_7 0xA7        // Set power ctl 7\n#define GC9A01_SET_POWER_CTL_1 0xC1        // Set power ctl 1\n#define GC9A01_SET_POWER_CTL_2 0xC3        // Set power ctl 2\n#define GC9A01_SET_POWER_CTL_3 0xC4        // Set power ctl 3\n#define GC9A01_SET_POWER_CTL_4 0xC9        // Set power ctl 4\n#define GC9A01_SET_FRAME_RATE 0xE8         // Set frame rate\n#define GC9A01_SET_SPI_2DATA 0xE9          // Set frame rate\n#define GC9A01_SET_GAMMA3 0xF2             // Set gamma 3\n#define GC9A01_SET_GAMMA4 0xF3             // Set gamma 4\n#define GC9A01_SET_IF_CTL 0xF6             // Set interface control\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// GC9A01 MADCTL Flags\n#define GC9A01_MADCTL_MH 0b00000100\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// GC9A01 Parameter constants\n\n//      Parameter values for\n//      GC9A01_SET_PIXEL_FORMAT\n#define GC9A01_PIXEL_FORMAT_12_BPP_DBI (0b011 << 0) // 12 bits/pixel MCU interface format\n#define GC9A01_PIXEL_FORMAT_16_BPP_DBI (0b101 << 0) // 16 bits/pixel MCU interface format\n#define GC9A01_PIXEL_FORMAT_18_BPP_DBI (0b110 << 0) // 18 bits/pixel MCU interface format\n#define GC9A01_PIXEL_FORMAT_16_BPP_DPI (0b101 << 4) // 16 bits/pixel RGB interface format\n#define GC9A01_PIXEL_FORMAT_18_BPP_DPI (0b110 << 4) // 18 bits/pixel RGB interface format\n\n//      Parameter values for\n//      GC9A01_SET_FUNCTION_CTL (2nd parameter)\n#define GC9A01_SOURCE_OUTPUT_SCAN_DIRECTION_S1_TO_S360 0b00000000\n#define GC9A01_SOURCE_OUTPUT_SCAN_DIRECTION_S360_TO_S1 0b00100000\n#define GC9A01_GATE_OUTPUT_SCAN_DIRECTION_G1_TO_G32 0b00000000\n#define GC9A01_GATE_OUTPUT_SCAN_DIRECTION_G32_TO_G1 0b01000000\n#define GC9A01_SCAN_MODE_INTER 0x10\n\n//      Parameter values for\n//      GC9A01_SET_FUNCTION_CTL (3rd parameter)\n#define GC9A01_LCD_DRIVE_LINE_16 0x01\n#define GC9A01_LCD_DRIVE_LINE_24 0x02\n#define GC9A01_LCD_DRIVE_LINE_32 0x03\n#define GC9A01_LCD_DRIVE_LINE_40 0x04\n#define GC9A01_LCD_DRIVE_LINE_48 0x05\n#define GC9A01_LCD_DRIVE_LINE_56 0x06\n#define GC9A01_LCD_DRIVE_LINE_64 0x07\n#define GC9A01_LCD_DRIVE_LINE_72 0x08\n#define GC9A01_LCD_DRIVE_LINE_80 0x09\n#define GC9A01_LCD_DRIVE_LINE_88 0x0A\n#define GC9A01_LCD_DRIVE_LINE_96 0x0B\n#define GC9A01_LCD_DRIVE_LINE_104 0x0C\n#define GC9A01_LCD_DRIVE_LINE_112 0x0D\n#define GC9A01_LCD_DRIVE_LINE_120 0x0E\n#define GC9A01_LCD_DRIVE_LINE_128 0x0F\n#define GC9A01_LCD_DRIVE_LINE_136 0x10\n#define GC9A01_LCD_DRIVE_LINE_144 0x11\n#define GC9A01_LCD_DRIVE_LINE_152 0x12\n#define GC9A01_LCD_DRIVE_LINE_160 0x13\n#define GC9A01_LCD_DRIVE_LINE_168 0x14\n#define GC9A01_LCD_DRIVE_LINE_176 0x15\n#define GC9A01_LCD_DRIVE_LINE_184 0x16\n#define GC9A01_LCD_DRIVE_LINE_192 0x17\n#define GC9A01_LCD_DRIVE_LINE_200 0x18\n#define GC9A01_LCD_DRIVE_LINE_208 0x19\n#define GC9A01_LCD_DRIVE_LINE_216 0x1A\n#define GC9A01_LCD_DRIVE_LINE_224 0x1B\n#define GC9A01_LCD_DRIVE_LINE_232 0x1C\n#define GC9A01_LCD_DRIVE_LINE_240 0x1D\n\n//      Parameter values for\n//      GC9A01_SET_DISPLAY_CTL\n#define GC9A01_BRIGHTNESS_CONTROL_ON 0b00100000\n#define GC9A01_DIMMING_ON 0b00001000\n#define GC9A01_BACKLIGHT_ON 0b00000100\n#define GC9A01_BRIGHTNESS_CONTROL_OFF 0b00000000\n#define GC9A01_DIMMING_OFF 0b00000000\n#define GC9A01_BACKLIGHT_OFF 0b00000000\n\n//      Parameter values for\n//      GC9A01_SET_IF_CTL\n#define GC9A01_DISPLAY_MODE_INTERNAL_CLOCK 0b00000000\n#define GC9A01_DISPLAY_MODE_RGB_INTERFACE 0b00000100\n#define GC9A01_DISPLAY_MODE_VSYNC_INTERFACE 0b00001000\n#define GC9A01_DSISPLAY_MODE_DISABLED 0b00001100\n\n#define GC0A01_GRAM_INTERFACE_VSYNC 0b00000000\n#define GC9A01_GRAM_INTERFACE_RGB 0b00000010\n\n#define GC9A01_RGB_INTERFACE_MODE_1_TRANSFER 0b00000000\n#define GC9A01_RGB_INTERFACE_MODE_3_TRANSFER 0b00000001\n"
  },
  {
    "path": "drivers/painter/gc9xxx/qp_gc9xxx_opcodes.h",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// Copyright 2024 Fernando Birra\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter GC9xxx command opcodes\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#define GC9XXX_GET_ID_INFO 0x04 // Get ID information\n#define GC9XXX_GET_STATUS 0x09  // Get status\n\n#define GC9XXX_CMD_SLEEP_ON 0x10    // Enter sleep mode\n#define GC9XXX_CMD_SLEEP_OFF 0x11   // Exit sleep mode\n#define GC9XXX_CMD_PARTIAL_ON 0x12  // Enter partial mode\n#define GC9XXX_CMD_PARTIAL_OFF 0x13 // Exit partial mode\n\n#define GC9XXX_CMD_INVERT_OFF 0x20  // Exit inverted mode\n#define GC9XXX_CMD_INVERT_ON 0x21   // Enter inverted mode\n#define GC9XXX_CMD_DISPLAY_OFF 0x28 // Disable display\n#define GC9XXX_CMD_DISPLAY_ON 0x29  // Enable display\n#define GC9XXX_SET_COL_ADDR 0x2A    // Set column address (MSB(StartCol),LSB(StartCol),MSB(EndCol),LSB(EndCol)\n#define GC9XXX_SET_ROW_ADDR 0x2B    // Set row address (MSB(StartRow),LSB(StartRow),MSB(EndRow),LSB(EndRow)\n#define GC9XXX_SET_MEM 0x2C         // Set (write) memory\n\n#define GC9XXX_SET_PARTIAL_AREA 0x30      // Set partial area (MSB(StartRow),LSB(StartRow),MSB(EndRow),LSB(EndRow)\n#define GC9XXX_SET_VSCROLL 0x33           // Set vertical scroll MSB(TFA),LSB(TFA),MSB(VSA),LSB(VSA)+ GC9107 extra param: MSB(BFA),LSB(BFA)\n#define GC9XXX_CMD_TEARING_OFF 0x34       // Tearing effect line OFF\n#define GC9XXX_CMD_TEARING_ON 0x35        // Tearing effect line ON\n#define GC9XXX_SET_MEM_ACS_CTL 0x36       // Set mem access ctl\n#define GC9XXX_SET_VSCROLL_ADDR 0x37      // Set vscroll start addr\n#define GC9XXX_CMD_IDLE_OFF 0x38          // Exit idle mode\n#define GC9XXX_CMD_IDLE_ON 0x39           // Enter idle mode\n#define GC9XXX_SET_PIXEL_FORMAT 0x3A      // Set pixel format\n#define GC9XXX_SET_TEAR_SCANLINE 0x44     // Set tearing scanline (Scanline = LS bit of Param 1 (GC9A01) + Param 2(GC9XXX))\n#define GC9XXX_GET_TEAR_SCANLINE 0x45     // Get tearing scanline (Scanline = LS bit of Param 1 (GC9A01) + Param 2(GC9XXX))\n#define GC9XXX_GET_ID1 0xDA               // Get ID1\n#define GC9XXX_GET_ID2 0xDB               // Get ID2\n#define GC9XXX_GET_ID3 0xDC               // Get ID3\n#define GC9XXX_SET_INTER_REG_ENABLE1 0xFE // Enable Inter Register 1\n#define GC9XXX_SET_INTER_REG_ENABLE2 0xEF // Enable Inter Register 2\n#define GC9XXX_SET_GAMMA1 0xF0            // Set gamma 1\n#define GC9XXX_SET_GAMMA2 0xF1            // Set gamma 2\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// MADCTL Flags\n#define GC9XXX_MADCTL_MY 0b10000000 // Mirror Y (row address order)\n#define GC9XXX_MADCTL_MX 0b01000000 // Mirror X (column address order)\n#define GC9XXX_MADCTL_MV 0b00100000 // Vertical Refresh Order (bottom to top)\n#define GC9XXX_MADCTL_ML 0b00010000\n#define GC9XXX_MADCTL_BGR 0b00001000\n#define GC9XXX_MADCTL_RGB 0b00000000\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// GC9XXX Parameter constants\n"
  },
  {
    "path": "drivers/painter/generic/qp_surface.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter surface helpers\n\n// Helper for determining buffer size required for a surface\n#define SURFACE_REQUIRED_BUFFER_BYTE_SIZE(w, h, bpp) ((((w) * (h) * (bpp)) + 7) / 8)\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter surface configurables (add to your keyboard's config.h)\n\n#ifndef SURFACE_NUM_DEVICES\n/**\n * @def This controls the maximum number of surface devices that Quantum Painter can use at any one time.\n *      Increasing this number allows for multiple framebuffers to be used. Each requires its own RAM allocation.\n */\n#    define SURFACE_NUM_DEVICES 1\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Forward declarations\n\n#ifdef QUANTUM_PAINTER_SURFACE_ENABLE\n\n// Surface struct\nstruct surface_painter_device_t;\ntypedef struct surface_painter_device_t surface_painter_device_t;\n\n/**\n * Factory method for an RGB565 surface (aka framebuffer).\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param buffer[in] pointer to a preallocated uint8_t buffer of size `SURFACE_REQUIRED_BUFFER_BYTE_SIZE(panel_width, panel_height, 16)`\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_make_rgb565_surface(uint16_t panel_width, uint16_t panel_height, void *buffer);\n\n/**\n * Factory method for a 1bpp monochrome surface (aka framebuffer).\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param buffer[in] pointer to a preallocated uint8_t buffer of size `SURFACE_REQUIRED_BUFFER_BYTE_SIZE(panel_width, panel_height, 1)`\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_make_mono1bpp_surface(uint16_t panel_width, uint16_t panel_height, void *buffer);\n\n/**\n * Helper method to draw the contents of the framebuffer to the target device.\n *\n * After successful completion, the dirty area is reset.\n *\n * @param surface[in] the surface to copy from\n * @param target[in] the target device to copy into\n * @param x[in] the x-location of the original position of the framebuffer\n * @param y[in] the y-location of the original position of the framebuffer\n * @param entire_surface[in] whether the entire surface should be drawn, instead of just the dirty region\n * @return whether the draw operation completed successfully\n */\nbool qp_surface_draw(painter_device_t surface, painter_device_t target, uint16_t x, uint16_t y, bool entire_surface);\n\n#endif // QUANTUM_PAINTER_SURFACE_ENABLE\n"
  },
  {
    "path": "drivers/painter/generic/qp_surface_common.c",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"color.h\"\n#include \"qp_draw.h\"\n#include \"qp_surface_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver storage\n\nsurface_painter_device_t surface_drivers[SURFACE_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Helpers\n\nvoid qp_surface_increment_pixdata_location(surface_viewport_data_t *viewport) {\n    // Increment the X-position\n    viewport->pixdata_x++;\n\n    // If the x-coord has gone past the right-side edge, loop it back around and increment the y-coord\n    if (viewport->pixdata_x > viewport->viewport_r) {\n        viewport->pixdata_x = viewport->viewport_l;\n        viewport->pixdata_y++;\n    }\n\n    // If the y-coord has gone past the bottom, loop it back to the top\n    if (viewport->pixdata_y > viewport->viewport_b) {\n        viewport->pixdata_y = viewport->viewport_t;\n    }\n}\n\nvoid qp_surface_update_dirty(surface_dirty_data_t *dirty, uint16_t x, uint16_t y) {\n    // Maintain dirty region\n    if (dirty->l > x) {\n        dirty->l        = x;\n        dirty->is_dirty = true;\n    }\n    if (dirty->r < x) {\n        dirty->r        = x;\n        dirty->is_dirty = true;\n    }\n    if (dirty->t > y) {\n        dirty->t        = y;\n        dirty->is_dirty = true;\n    }\n    if (dirty->b < y) {\n        dirty->b        = y;\n        dirty->is_dirty = true;\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n\nbool qp_surface_init(painter_device_t device, painter_rotation_t rotation) {\n    painter_driver_t *        driver  = (painter_driver_t *)device;\n    surface_painter_device_t *surface = (surface_painter_device_t *)driver;\n    memset(surface->buffer, 0, SURFACE_REQUIRED_BUFFER_BYTE_SIZE(driver->panel_width, driver->panel_height, driver->native_bits_per_pixel));\n\n    surface->dirty.l        = 0;\n    surface->dirty.t        = 0;\n    surface->dirty.r        = surface->base.panel_width - 1;\n    surface->dirty.b        = surface->base.panel_height - 1;\n    surface->dirty.is_dirty = true;\n\n    return true;\n}\n\nbool qp_surface_power(painter_device_t device, bool power_on) {\n    // No-op.\n    return true;\n}\n\nbool qp_surface_clear(painter_device_t device) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n    driver->driver_vtable->init(device, driver->rotation); // Re-init the surface\n    return true;\n}\n\nbool qp_surface_flush(painter_device_t device) {\n    painter_driver_t *        driver  = (painter_driver_t *)device;\n    surface_painter_device_t *surface = (surface_painter_device_t *)driver;\n    surface->dirty.l = surface->dirty.t = UINT16_MAX;\n    surface->dirty.r = surface->dirty.b = 0;\n    surface->dirty.is_dirty             = false;\n    return true;\n}\n\nbool qp_surface_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {\n    painter_driver_t *        driver  = (painter_driver_t *)device;\n    surface_painter_device_t *surface = (surface_painter_device_t *)driver;\n\n    // Set the viewport locations\n    surface->viewport.viewport_l = left;\n    surface->viewport.viewport_t = top;\n    surface->viewport.viewport_r = right;\n    surface->viewport.viewport_b = bottom;\n\n    // Reset the write location to the top left\n    surface->viewport.pixdata_x = left;\n    surface->viewport.pixdata_y = top;\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Drawing routine to copy out the dirty region and send it to another device\n\nbool qp_surface_draw(painter_device_t surface, painter_device_t target, uint16_t x, uint16_t y, bool entire_surface) {\n    painter_driver_t *        surface_driver = (painter_driver_t *)surface;\n    surface_painter_device_t *surface_handle = (surface_painter_device_t *)surface_driver;\n    painter_driver_t *        target_driver  = (painter_driver_t *)target;\n\n    // If we're not dirty... we're done.\n    if (!surface_handle->dirty.is_dirty) {\n        qp_dprintf(\"qp_surface_draw: ok (not dirty, skipping)\\n\");\n        return true;\n    }\n\n    // If we have incompatible bit depths, drop out\n    if (surface_driver->native_bits_per_pixel != target_driver->native_bits_per_pixel) {\n        qp_dprintf(\"qp_surface_draw: fail (incompatible bpp: surface=%d, target=%d)\\n\", (int)surface_driver->native_bits_per_pixel, (int)target_driver->native_bits_per_pixel);\n        return false;\n    }\n\n    // Offload to the pixdata transfer function\n    surface_painter_driver_vtable_t *vtable = (surface_painter_driver_vtable_t *)surface_driver->driver_vtable;\n    bool                             ok     = vtable->target_pixdata_transfer(surface_driver, target_driver, x, y, entire_surface);\n    if (!ok) {\n        qp_dprintf(\"qp_surface_draw: fail (could not transfer pixel data)\\n\");\n        return false;\n    }\n\n    // Clear the dirty info for the surface\n    ok = qp_flush(surface);\n    if (!ok) {\n        qp_dprintf(\"qp_surface_draw: fail (could not flush)\\n\");\n        return false;\n    }\n    qp_dprintf(\"qp_surface_draw: ok\\n\");\n    return true;\n}\n"
  },
  {
    "path": "drivers/painter/generic/qp_surface_internal.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifdef QUANTUM_PAINTER_SURFACE_ENABLE\n\n#    include \"qp_surface.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Internal declarations\n\n// Surface vtable\ntypedef struct surface_painter_driver_vtable_t {\n    painter_driver_vtable_t base; // must be first, so it can be cast to/from the painter_driver_vtable_t* type\n\n    bool (*target_pixdata_transfer)(painter_driver_t *surface_driver, painter_driver_t *target_driver, uint16_t x, uint16_t y, bool entire_surface);\n} surface_painter_driver_vtable_t;\n\ntypedef struct surface_dirty_data_t {\n    bool     is_dirty;\n    uint16_t l;\n    uint16_t t;\n    uint16_t r;\n    uint16_t b;\n} surface_dirty_data_t;\n\ntypedef struct surface_viewport_data_t {\n    // Manually manage the viewport for streaming pixel data to the display\n    uint16_t viewport_l;\n    uint16_t viewport_t;\n    uint16_t viewport_r;\n    uint16_t viewport_b;\n\n    // Current write location to the display when streaming pixel data\n    uint16_t pixdata_x;\n    uint16_t pixdata_y;\n} surface_viewport_data_t;\n\n// Surface struct\ntypedef struct surface_painter_device_t {\n    painter_driver_t base; // must be first, so it can be cast to/from the painter_device_t* type\n\n    // The target buffer\n    union {\n        void *    buffer;\n        uint8_t * u8buffer;\n        uint16_t *u16buffer;\n    };\n\n    // Manually manage the viewport for streaming pixel data to the display\n    surface_viewport_data_t viewport;\n\n    // Maintain a dirty region so we can stream only what we need\n    surface_dirty_data_t dirty;\n} surface_painter_device_t;\n\n/**\n * Factory method for an RGB565 surface (aka framebuffer). Accepts an external device table.\n *\n * @param device_table[in] the table of devices to use for instantiation\n * @param device_table_len[in] the length of the table of devices\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param buffer[in] pointer to a preallocated uint8_t buffer of size `SURFACE_REQUIRED_BUFFER_BYTE_SIZE(panel_width, panel_height, 16)`\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_make_rgb565_surface_advanced(surface_painter_device_t *device_table, size_t device_table_len, uint16_t panel_width, uint16_t panel_height, void *buffer);\n\n/**\n * Factory method for a 1bpp monochrome surface (aka framebuffer).\n *\n * @param device_table[in] the table of devices to use for instantiation\n * @param device_table_len[in] the length of the table of devices\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param buffer[in] pointer to a preallocated uint8_t buffer of size `SURFACE_REQUIRED_BUFFER_BYTE_SIZE(panel_width, panel_height, 16)`\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_make_mono1bpp_surface_advanced(surface_painter_device_t *device_table, size_t device_table_len, uint16_t panel_width, uint16_t panel_height, void *buffer);\n\n// Driver storage\nextern surface_painter_device_t surface_drivers[SURFACE_NUM_DEVICES];\n\n// Surface common APIs\nbool qp_surface_init(painter_device_t device, painter_rotation_t rotation);\nbool qp_surface_power(painter_device_t device, bool power_on);\nbool qp_surface_clear(painter_device_t device);\nbool qp_surface_flush(painter_device_t device);\nbool qp_surface_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom);\nvoid qp_surface_increment_pixdata_location(surface_viewport_data_t *viewport);\nvoid qp_surface_update_dirty(surface_dirty_data_t *dirty, uint16_t x, uint16_t y);\n\n#endif // QUANTUM_PAINTER_SURFACE_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Factory functions for creating a handle to a surface\n\n#define SURFACE_FACTORY_FUNCTION_IMPL(function_name, vtable, bpp)                                                                                                             \\\n    painter_device_t(function_name##_advanced)(surface_painter_device_t * device_table, size_t device_table_len, uint16_t panel_width, uint16_t panel_height, void *buffer) { \\\n        for (uint32_t i = 0; i < device_table_len; ++i) {                                                                                                                     \\\n            surface_painter_device_t *driver = &device_table[i];                                                                                                              \\\n            if (!driver->base.driver_vtable) {                                                                                                                                \\\n                driver->base.driver_vtable         = (painter_driver_vtable_t *)&(vtable);                                                                                    \\\n                driver->base.native_bits_per_pixel = (bpp);                                                                                                                   \\\n                driver->base.comms_vtable          = &dummy_comms_vtable;                                                                                                     \\\n                driver->base.panel_width           = panel_width;                                                                                                             \\\n                driver->base.panel_height          = panel_height;                                                                                                            \\\n                driver->base.rotation              = QP_ROTATION_0;                                                                                                           \\\n                driver->base.offset_x              = 0;                                                                                                                       \\\n                driver->base.offset_y              = 0;                                                                                                                       \\\n                driver->buffer                     = buffer;                                                                                                                  \\\n                return (painter_device_t)driver;                                                                                                                              \\\n            }                                                                                                                                                                 \\\n        }                                                                                                                                                                     \\\n        return NULL;                                                                                                                                                          \\\n    }                                                                                                                                                                         \\\n    painter_device_t(function_name)(uint16_t panel_width, uint16_t panel_height, void *buffer) {                                                                              \\\n        return (function_name##_advanced)(surface_drivers, SURFACE_NUM_DEVICES, panel_width, panel_height, buffer);                                                           \\\n    }\n"
  },
  {
    "path": "drivers/painter/generic/qp_surface_mono1bpp.c",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#ifdef QUANTUM_PAINTER_SURFACE_ENABLE\n\n#    include \"color.h\"\n#    include \"qp_draw.h\"\n#    include \"qp_surface_internal.h\"\n#    include \"qp_comms_dummy.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Surface driver impl: mono1bpp\n\nstatic inline void setpixel_mono1bpp(surface_painter_device_t *surface, uint16_t x, uint16_t y, bool mono_pixel) {\n    uint16_t w = surface->base.panel_width;\n    uint16_t h = surface->base.panel_height;\n\n    // Drop out if it's off-screen\n    if (x >= w || y >= h) {\n        return;\n    }\n\n    // Figure out which location needs to be updated\n    uint32_t pixel_num   = y * w + x;\n    uint32_t byte_offset = pixel_num / 8;\n    uint8_t  bit_offset  = pixel_num % 8;\n    bool     curr_val    = (surface->u8buffer[byte_offset] & (1 << bit_offset)) ? true : false;\n\n    // Skip messing with the dirty info if the original value already matches\n    if (curr_val != mono_pixel) {\n        // Update the dirty region\n        qp_surface_update_dirty(&surface->dirty, x, y);\n\n        // Update the pixel data in the buffer\n        if (mono_pixel) {\n            surface->u8buffer[byte_offset] |= (1 << bit_offset);\n        } else {\n            surface->u8buffer[byte_offset] &= ~(1 << bit_offset);\n        }\n    }\n}\n\nstatic inline void append_pixel_mono1bpp(surface_painter_device_t *surface, bool mono_pixel) {\n    setpixel_mono1bpp(surface, surface->viewport.pixdata_x, surface->viewport.pixdata_y, mono_pixel);\n    qp_surface_increment_pixdata_location(&surface->viewport);\n}\n\nstatic inline void stream_pixdata_mono1bpp(surface_painter_device_t *surface, const uint8_t *data, uint32_t native_pixel_count) {\n    for (uint32_t pixel_counter = 0; pixel_counter < native_pixel_count; ++pixel_counter) {\n        uint32_t byte_offset = pixel_counter / 8;\n        uint8_t  bit_offset  = pixel_counter % 8;\n        append_pixel_mono1bpp(surface, (data[byte_offset] & (1 << bit_offset)) ? true : false);\n    }\n}\n\n// Stream pixel data to the current write position in GRAM\nstatic bool qp_surface_pixdata_mono1bpp(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) {\n    painter_driver_t *        driver  = (painter_driver_t *)device;\n    surface_painter_device_t *surface = (surface_painter_device_t *)driver;\n    stream_pixdata_mono1bpp(surface, (const uint8_t *)pixel_data, native_pixel_count);\n    return true;\n}\n\n// Pixel colour conversion\nstatic bool qp_surface_palette_convert_mono1bpp(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {\n    for (int16_t i = 0; i < palette_size; ++i) {\n        palette[i].mono = (palette[i].hsv888.v > 127) ? 1 : 0;\n    }\n    return true;\n}\n\n// Append pixels to the target location, keyed by the pixel index\nstatic bool qp_surface_append_pixels_mono1bpp(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices) {\n    for (uint32_t i = 0; i < pixel_count; ++i) {\n        uint32_t pixel_num   = pixel_offset + i;\n        uint32_t byte_offset = pixel_num / 8;\n        uint8_t  bit_offset  = pixel_num % 8;\n        if (palette[palette_indices[i]].mono) {\n            target_buffer[byte_offset] |= (1 << bit_offset);\n        } else {\n            target_buffer[byte_offset] &= ~(1 << bit_offset);\n        }\n    }\n    return true;\n}\n\nstatic bool mono1bpp_target_pixdata_transfer(painter_driver_t *surface_driver, painter_driver_t *target_driver, uint16_t x, uint16_t y, bool entire_surface) {\n    return false; // Not yet supported.\n}\n\nstatic bool qp_surface_append_pixdata_mono1bpp(painter_device_t device, uint8_t *target_buffer, uint32_t pixdata_offset, uint8_t pixdata_byte) {\n    return false; // Just use 1bpp images.\n}\n\nconst surface_painter_driver_vtable_t mono1bpp_surface_driver_vtable = {\n    .base =\n        {\n            .init            = qp_surface_init,\n            .power           = qp_surface_power,\n            .clear           = qp_surface_clear,\n            .flush           = qp_surface_flush,\n            .pixdata         = qp_surface_pixdata_mono1bpp,\n            .viewport        = qp_surface_viewport,\n            .palette_convert = qp_surface_palette_convert_mono1bpp,\n            .append_pixels   = qp_surface_append_pixels_mono1bpp,\n            .append_pixdata  = qp_surface_append_pixdata_mono1bpp,\n        },\n    .target_pixdata_transfer = mono1bpp_target_pixdata_transfer,\n};\n\nSURFACE_FACTORY_FUNCTION_IMPL(qp_make_mono1bpp_surface, mono1bpp_surface_driver_vtable, 1);\n\n#endif // QUANTUM_PAINTER_SURFACE_ENABLE\n"
  },
  {
    "path": "drivers/painter/generic/qp_surface_rgb565.c",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#ifdef QUANTUM_PAINTER_SURFACE_ENABLE\n\n#    include \"color.h\"\n#    include \"qp_draw.h\"\n#    include \"qp_surface_internal.h\"\n#    include \"qp_comms_dummy.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Surface driver impl: rgb565\n\nstatic inline void setpixel_rgb565(surface_painter_device_t *surface, uint16_t x, uint16_t y, uint16_t rgb565) {\n    uint16_t w = surface->base.panel_width;\n    uint16_t h = surface->base.panel_height;\n\n    // Drop out if it's off-screen\n    if (x >= w || y >= h) {\n        return;\n    }\n\n    // Skip messing with the dirty info if the original value already matches\n    if (surface->u16buffer[y * w + x] != rgb565) {\n        // Update the dirty region\n        qp_surface_update_dirty(&surface->dirty, x, y);\n\n        // Update the pixel data in the buffer\n        surface->u16buffer[y * w + x] = rgb565;\n    }\n}\n\nstatic inline void append_pixel_rgb565(surface_painter_device_t *surface, uint16_t rgb565) {\n    setpixel_rgb565(surface, surface->viewport.pixdata_x, surface->viewport.pixdata_y, rgb565);\n    qp_surface_increment_pixdata_location(&surface->viewport);\n}\n\nstatic inline void stream_pixdata_rgb565(surface_painter_device_t *surface, const uint16_t *data, uint32_t native_pixel_count) {\n    for (uint32_t pixel_counter = 0; pixel_counter < native_pixel_count; ++pixel_counter) {\n        append_pixel_rgb565(surface, data[pixel_counter]);\n    }\n}\n\n// Stream pixel data to the current write position in GRAM\nstatic bool qp_surface_pixdata_rgb565(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) {\n    painter_driver_t *        driver  = (painter_driver_t *)device;\n    surface_painter_device_t *surface = (surface_painter_device_t *)driver;\n    stream_pixdata_rgb565(surface, (const uint16_t *)pixel_data, native_pixel_count);\n    return true;\n}\n\n// Pixel colour conversion\nstatic bool qp_surface_palette_convert_rgb565_swapped(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {\n    for (int16_t i = 0; i < palette_size; ++i) {\n        rgb_t    rgb      = hsv_to_rgb_nocie((hsv_t){palette[i].hsv888.h, palette[i].hsv888.s, palette[i].hsv888.v});\n        uint16_t rgb565   = (((uint16_t)rgb.r) >> 3) << 11 | (((uint16_t)rgb.g) >> 2) << 5 | (((uint16_t)rgb.b) >> 3);\n        palette[i].rgb565 = __builtin_bswap16(rgb565);\n    }\n    return true;\n}\n\n// Append pixels to the target location, keyed by the pixel index\nstatic bool qp_surface_append_pixels_rgb565(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices) {\n    uint16_t *buf = (uint16_t *)target_buffer;\n    for (uint32_t i = 0; i < pixel_count; ++i) {\n        buf[pixel_offset + i] = palette[palette_indices[i]].rgb565;\n    }\n    return true;\n}\n\nstatic bool rgb565_target_pixdata_transfer(painter_driver_t *surface_driver, painter_driver_t *target_driver, uint16_t x, uint16_t y, bool entire_surface) {\n    surface_painter_device_t *surface_handle = (surface_painter_device_t *)surface_driver;\n\n    uint16_t l = entire_surface ? 0 : surface_handle->dirty.l;\n    uint16_t t = entire_surface ? 0 : surface_handle->dirty.t;\n    uint16_t r = entire_surface ? (surface_handle->base.panel_width - 1) : surface_handle->dirty.r;\n    uint16_t b = entire_surface ? (surface_handle->base.panel_height - 1) : surface_handle->dirty.b;\n\n    // Set the target drawing area\n    bool ok = qp_viewport((painter_device_t)target_driver, x + l, y + t, x + r, y + b);\n    if (!ok) {\n        qp_dprintf(\"rgb565_target_pixdata_transfer: fail (could not set target viewport)\\n\");\n        return false;\n    }\n\n    // Housekeeping of the amount of pixels to transfer\n    uint32_t  total_pixel_count = (8 * QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE) / surface_driver->native_bits_per_pixel;\n    uint32_t  pixel_counter     = 0;\n    uint16_t *target_buffer     = (uint16_t *)qp_internal_global_pixdata_buffer;\n\n    // Fill the global pixdata area so that we can start transferring to the panel\n    for (uint16_t y = t; y <= b; ++y) {\n        for (uint16_t x = l; x <= r; ++x) {\n            // Update the target buffer\n            target_buffer[pixel_counter++] = surface_handle->u16buffer[y * surface_handle->base.panel_width + x];\n\n            // If we've accumulated enough data, send it\n            if (pixel_counter == total_pixel_count) {\n                ok = qp_pixdata((painter_device_t)target_driver, qp_internal_global_pixdata_buffer, pixel_counter);\n                if (!ok) {\n                    qp_dprintf(\"rgb565_target_pixdata_transfer: fail (could not stream pixdata to target)\\n\");\n                    return false;\n                }\n                // Reset the counter\n                pixel_counter = 0;\n            }\n        }\n    }\n\n    // If there's any leftover data, send it\n    if (pixel_counter > 0) {\n        ok = qp_pixdata((painter_device_t)target_driver, qp_internal_global_pixdata_buffer, pixel_counter);\n        if (!ok) {\n            qp_dprintf(\"rgb565_target_pixdata_transfer: fail (could not stream pixdata to target)\\n\");\n            return false;\n        }\n    }\n\n    return true;\n}\n\nstatic bool qp_surface_append_pixdata_rgb565(painter_device_t device, uint8_t *target_buffer, uint32_t pixdata_offset, uint8_t pixdata_byte) {\n    target_buffer[pixdata_offset] = pixdata_byte;\n    return true;\n}\n\nconst surface_painter_driver_vtable_t rgb565_surface_driver_vtable = {\n    .base =\n        {\n            .init            = qp_surface_init,\n            .power           = qp_surface_power,\n            .clear           = qp_surface_clear,\n            .flush           = qp_surface_flush,\n            .pixdata         = qp_surface_pixdata_rgb565,\n            .viewport        = qp_surface_viewport,\n            .palette_convert = qp_surface_palette_convert_rgb565_swapped,\n            .append_pixels   = qp_surface_append_pixels_rgb565,\n            .append_pixdata  = qp_surface_append_pixdata_rgb565,\n        },\n    .target_pixdata_transfer = rgb565_target_pixdata_transfer,\n};\n\nSURFACE_FACTORY_FUNCTION_IMPL(qp_make_rgb565_surface, rgb565_surface_driver_vtable, 16);\n\n#endif // QUANTUM_PAINTER_SURFACE_ENABLE\n"
  },
  {
    "path": "drivers/painter/ili9xxx/qp_ili9163.c",
    "content": "// Copyright 2021-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_ili9163.h\"\n#include \"qp_ili9xxx_opcodes.h\"\n#include \"qp_tft_panel.h\"\n\n#ifdef QUANTUM_PAINTER_ILI9163_SPI_ENABLE\n#    include \"qp_comms_spi.h\"\n#endif // QUANTUM_PAINTER_ILI9163_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Common\n\n// Driver storage\ntft_panel_dc_reset_painter_device_t ili9163_drivers[ILI9163_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Initialization\n\n__attribute__((weak)) bool qp_ili9163_init(painter_device_t device, painter_rotation_t rotation) {\n    // clang-format off\n    const uint8_t ili9163_init_sequence[] = {\n        // Command,                 Delay,  N, Data[N]\n        ILI9XXX_CMD_RESET,            120,  0,\n        ILI9XXX_CMD_SLEEP_OFF,          5,  0,\n        ILI9XXX_SET_PIX_FMT,            0,  1, 0x55,\n        ILI9XXX_SET_GAMMA,              0,  1, 0x04,\n        ILI9XXX_ENABLE_3_GAMMA,         0,  1, 0x01,\n        ILI9XXX_SET_FUNCTION_CTL,       0,  2, 0xFF, 0x06,\n        ILI9XXX_SET_PGAMMA,             0, 15, 0x36, 0x29, 0x12, 0x22, 0x1C, 0x15, 0x42, 0xB7, 0x2F, 0x13, 0x12, 0x0A, 0x11, 0x0B, 0x06,\n        ILI9XXX_SET_NGAMMA,             0, 15, 0x09, 0x16, 0x2D, 0x0D, 0x13, 0x15, 0x40, 0x48, 0x53, 0x0C, 0x1D, 0x25, 0x2E, 0x34, 0x39,\n        ILI9XXX_SET_FRAME_CTL_NORMAL,   0,  2, 0x08, 0x02,\n        ILI9XXX_SET_POWER_CTL_1,        0,  2, 0x0A, 0x02,\n        ILI9XXX_SET_POWER_CTL_2,        0,  1, 0x02,\n        ILI9XXX_SET_VCOM_CTL_1,         0,  2, 0x50, 0x63,\n        ILI9XXX_SET_VCOM_CTL_2,         0,  1, 0x00,\n        ILI9XXX_CMD_PARTIAL_OFF,        0,  0,\n        ILI9XXX_CMD_DISPLAY_ON,        20,  0\n    };\n    // clang-format on\n    qp_comms_bulk_command_sequence(device, ili9163_init_sequence, sizeof(ili9163_init_sequence));\n\n    // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)\n    const uint8_t madctl[] = {\n        [QP_ROTATION_0]   = ILI9XXX_MADCTL_BGR,\n        [QP_ROTATION_90]  = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MX | ILI9XXX_MADCTL_MV,\n        [QP_ROTATION_180] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MX | ILI9XXX_MADCTL_MY,\n        [QP_ROTATION_270] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MY,\n    };\n    qp_comms_command_databyte(device, ILI9XXX_SET_MEM_ACS_CTL, madctl[rotation]);\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n\nconst tft_panel_dc_reset_painter_driver_vtable_t ili9163_driver_vtable = {\n    .base =\n        {\n            .init            = qp_ili9163_init,\n            .power           = qp_tft_panel_power,\n            .clear           = qp_tft_panel_clear,\n            .flush           = qp_tft_panel_flush,\n            .pixdata         = qp_tft_panel_pixdata,\n            .viewport        = qp_tft_panel_viewport,\n            .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,\n            .append_pixels   = qp_tft_panel_append_pixels_rgb565,\n            .append_pixdata  = qp_tft_panel_append_pixdata,\n        },\n    .num_window_bytes   = 2,\n    .swap_window_coords = false,\n    .opcodes =\n        {\n            .display_on         = ILI9XXX_CMD_DISPLAY_ON,\n            .display_off        = ILI9XXX_CMD_DISPLAY_OFF,\n            .set_column_address = ILI9XXX_SET_COL_ADDR,\n            .set_row_address    = ILI9XXX_SET_PAGE_ADDR,\n            .enable_writes      = ILI9XXX_SET_MEM,\n        },\n};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SPI\n\n#ifdef QUANTUM_PAINTER_ILI9163_SPI_ENABLE\n\n// Factory function for creating a handle to the ILI9163 device\npainter_device_t qp_ili9163_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < ILI9163_NUM_DEVICES; ++i) {\n        tft_panel_dc_reset_painter_device_t *driver = &ili9163_drivers[i];\n        if (!driver->base.driver_vtable) {\n            driver->base.driver_vtable         = (const painter_driver_vtable_t *)&ili9163_driver_vtable;\n            driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;\n            driver->base.panel_width           = panel_width;\n            driver->base.panel_height          = panel_height;\n            driver->base.rotation              = QP_ROTATION_0;\n            driver->base.offset_x              = 0;\n            driver->base.offset_y              = 0;\n            driver->base.native_bits_per_pixel = 16; // RGB565\n\n            // SPI and other pin configuration\n            driver->base.comms_config                                   = &driver->spi_dc_reset_config;\n            driver->spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;\n            driver->spi_dc_reset_config.spi_config.divisor              = spi_divisor;\n            driver->spi_dc_reset_config.spi_config.lsb_first            = false;\n            driver->spi_dc_reset_config.spi_config.mode                 = spi_mode;\n            driver->spi_dc_reset_config.dc_pin                          = dc_pin;\n            driver->spi_dc_reset_config.reset_pin                       = reset_pin;\n            driver->spi_dc_reset_config.command_params_uses_command_pin = false;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_ILI9163_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "drivers/painter/ili9xxx/qp_ili9163.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ILI9163 configurables (add to your keyboard's config.h)\n\n#ifndef ILI9163_NUM_DEVICES\n/**\n * @def This controls the maximum number of ILI9163 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define ILI9163_NUM_DEVICES 1\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ILI9163 device factories\n\n#ifdef QUANTUM_PAINTER_ILI9163_SPI_ENABLE\n/**\n * Factory method for an ILI9163 SPI LCD device.\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_ili9163_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n#endif // QUANTUM_PAINTER_ILI9163_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/ili9xxx/qp_ili9341.c",
    "content": "// Copyright 2021-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_ili9341.h\"\n#include \"qp_ili9xxx_opcodes.h\"\n#include \"qp_tft_panel.h\"\n\n#ifdef QUANTUM_PAINTER_ILI9341_SPI_ENABLE\n#    include <qp_comms_spi.h>\n#endif // QUANTUM_PAINTER_ILI9341_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Common\n\n// Driver storage\ntft_panel_dc_reset_painter_device_t ili9341_drivers[ILI9341_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Initialization\n\n__attribute__((weak)) bool qp_ili9341_init(painter_device_t device, painter_rotation_t rotation) {\n    // clang-format off\n    const uint8_t ili9341_init_sequence[] = {\n        // Command,                 Delay,  N, Data[N]\n        ILI9XXX_CMD_RESET,            120,  0,\n        ILI9XXX_CMD_SLEEP_OFF,          5,  0,\n        ILI9XXX_POWER_CTL_A,            0,  5, 0x39, 0x2C, 0x00, 0x34, 0x02,\n        ILI9XXX_POWER_CTL_B,            0,  3, 0x00, 0xD9, 0x30,\n        ILI9XXX_POWER_ON_SEQ_CTL,       0,  4, 0x64, 0x03, 0x12, 0x81,\n        ILI9XXX_SET_PUMP_RATIO_CTL,     0,  1, 0x20,\n        ILI9XXX_SET_POWER_CTL_1,        0,  1, 0x26,\n        ILI9XXX_SET_POWER_CTL_2,        0,  1, 0x11,\n        ILI9XXX_SET_VCOM_CTL_1,         0,  2, 0x35, 0x3E,\n        ILI9XXX_SET_VCOM_CTL_2,         0,  1, 0xBE,\n        ILI9XXX_DRV_TIMING_CTL_A,       0,  3, 0x85, 0x10, 0x7A,\n        ILI9XXX_DRV_TIMING_CTL_B,       0,  2, 0x00, 0x00,\n        ILI9XXX_SET_BRIGHTNESS,         0,  1, 0xFF,\n        ILI9XXX_ENABLE_3_GAMMA,         0,  1, 0x00,\n        ILI9XXX_SET_GAMMA,              0,  1, 0x01,\n        ILI9XXX_SET_PGAMMA,             0, 15, 0x0F, 0x29, 0x24, 0x0C, 0x0E, 0x09, 0x4E, 0x78, 0x3C, 0x09, 0x13, 0x05, 0x17, 0x11, 0x00,\n        ILI9XXX_SET_NGAMMA,             0, 15, 0x00, 0x16, 0x1B, 0x04, 0x11, 0x07, 0x31, 0x33, 0x42, 0x05, 0x0C, 0x0A, 0x28, 0x2F, 0x0F,\n        ILI9XXX_SET_PIX_FMT,            0,  1, 0x05,\n        ILI9XXX_SET_FRAME_CTL_NORMAL,   0,  2, 0x00, 0x1B,\n        ILI9XXX_SET_FUNCTION_CTL,       0,  2, 0x0A, 0xA2,\n        ILI9XXX_CMD_PARTIAL_OFF,        0,  0,\n        ILI9XXX_CMD_DISPLAY_ON,        20,  0\n    };\n    // clang-format on\n    qp_comms_bulk_command_sequence(device, ili9341_init_sequence, sizeof(ili9341_init_sequence));\n\n    // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)\n    const uint8_t madctl[] = {\n        [QP_ROTATION_0]   = ILI9XXX_MADCTL_BGR,\n        [QP_ROTATION_90]  = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MX | ILI9XXX_MADCTL_MV,\n        [QP_ROTATION_180] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MX | ILI9XXX_MADCTL_MY,\n        [QP_ROTATION_270] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MY,\n    };\n    qp_comms_command_databyte(device, ILI9XXX_SET_MEM_ACS_CTL, madctl[rotation]);\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n\nconst tft_panel_dc_reset_painter_driver_vtable_t ili9341_driver_vtable = {\n    .base =\n        {\n            .init            = qp_ili9341_init,\n            .power           = qp_tft_panel_power,\n            .clear           = qp_tft_panel_clear,\n            .flush           = qp_tft_panel_flush,\n            .pixdata         = qp_tft_panel_pixdata,\n            .viewport        = qp_tft_panel_viewport,\n            .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,\n            .append_pixels   = qp_tft_panel_append_pixels_rgb565,\n            .append_pixdata  = qp_tft_panel_append_pixdata,\n        },\n    .num_window_bytes   = 2,\n    .swap_window_coords = false,\n    .opcodes =\n        {\n            .display_on         = ILI9XXX_CMD_DISPLAY_ON,\n            .display_off        = ILI9XXX_CMD_DISPLAY_OFF,\n            .set_column_address = ILI9XXX_SET_COL_ADDR,\n            .set_row_address    = ILI9XXX_SET_PAGE_ADDR,\n            .enable_writes      = ILI9XXX_SET_MEM,\n        },\n};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SPI\n\n#ifdef QUANTUM_PAINTER_ILI9341_SPI_ENABLE\n\n// Factory function for creating a handle to the ILI9341 device\npainter_device_t qp_ili9341_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < ILI9341_NUM_DEVICES; ++i) {\n        tft_panel_dc_reset_painter_device_t *driver = &ili9341_drivers[i];\n        if (!driver->base.driver_vtable) {\n            driver->base.driver_vtable         = (const painter_driver_vtable_t *)&ili9341_driver_vtable;\n            driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;\n            driver->base.native_bits_per_pixel = 16; // RGB565\n            driver->base.panel_width           = panel_width;\n            driver->base.panel_height          = panel_height;\n            driver->base.rotation              = QP_ROTATION_0;\n            driver->base.offset_x              = 0;\n            driver->base.offset_y              = 0;\n\n            // SPI and other pin configuration\n            driver->base.comms_config                                   = &driver->spi_dc_reset_config;\n            driver->spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;\n            driver->spi_dc_reset_config.spi_config.divisor              = spi_divisor;\n            driver->spi_dc_reset_config.spi_config.lsb_first            = false;\n            driver->spi_dc_reset_config.spi_config.mode                 = spi_mode;\n            driver->spi_dc_reset_config.dc_pin                          = dc_pin;\n            driver->spi_dc_reset_config.reset_pin                       = reset_pin;\n            driver->spi_dc_reset_config.command_params_uses_command_pin = false;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_ILI9341_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "drivers/painter/ili9xxx/qp_ili9341.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ILI9341 configurables (add to your keyboard's config.h)\n\n#ifndef ILI9341_NUM_DEVICES\n/**\n * @def This controls the maximum number of ILI9341 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define ILI9341_NUM_DEVICES 1\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ILI9341 device factories\n\n#ifdef QUANTUM_PAINTER_ILI9341_SPI_ENABLE\n/**\n * Factory method for an ILI9341 SPI LCD device.\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_ili9341_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n#endif // QUANTUM_PAINTER_ILI9341_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/ili9xxx/qp_ili9486.c",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// Copyright 2023 Pablo Martinez (@elpekenin) <elpekenin@elpekenin.dev>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_ili9486.h\"\n#include \"qp_ili9xxx_opcodes.h\"\n#include \"qp_tft_panel.h\"\n\n#ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE\n#    include \"spi_master.h\"\n#    include <qp_comms_spi.h>\n#endif // QUANTUM_PAINTER_ILI9486_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Common\n\n// Driver storage\ntft_panel_dc_reset_painter_device_t ili9486_drivers[ILI9486_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Initialization\n\nbool qp_ili9486_init(painter_device_t device, painter_rotation_t rotation) {\n    // clang-format off\n    const uint8_t ili9486_init_sequence[] = {\n        // Command,                 Delay,  N, Data[N]\n        ILI9XXX_CMD_RESET,            120,  0,\n        ILI9XXX_SET_PIX_FMT,            0,  1, 0x55,\n        ILI9XXX_SET_PGAMMA,             0, 15, 0x0F, 0x1F, 0x1C, 0x0C, 0x0F, 0x08, 0x48, 0x98, 0x37, 0x0A, 0x13, 0x04, 0x11, 0x0D, 0x00,\n        ILI9XXX_SET_NGAMMA,             0, 15, 0x0F, 0x32, 0x2E, 0x0B, 0x0D, 0x05, 0x47, 0x75, 0x37, 0x06, 0x10, 0x03, 0x24, 0x20, 0x00,\n        ILI9XXX_SET_POWER_CTL_1,        0,  2, 0x0D, 0x0D,\n        ILI9XXX_SET_POWER_CTL_2,        0,  2, 0x43, 0x00,\n        ILI9XXX_SET_POWER_CTL_3,        0,  1, 0x00,\n        ILI9XXX_SET_VCOM_CTL_1,         0,  4, 0x00, 0x48, 0x00, 0x48,\n        ILI9XXX_SET_INVERSION_CTL,      0,  1, 0x02,\n    };\n    // clang-format on\n    qp_comms_bulk_command_sequence(device, ili9486_init_sequence, sizeof(ili9486_init_sequence));\n\n    // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)\n    const uint8_t madctl[] = {\n        [QP_ROTATION_0]   = ILI9XXX_MADCTL_BGR,\n        [QP_ROTATION_90]  = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV,\n        [QP_ROTATION_180] = ILI9XXX_MADCTL_BGR,\n        [QP_ROTATION_270] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV,\n    };\n    const uint8_t functl[] = {\n        [QP_ROTATION_0]   = 0x42,\n        [QP_ROTATION_90]  = 0x62,\n        [QP_ROTATION_180] = 0x22,\n        [QP_ROTATION_270] = 0x02,\n    };\n\n    // clang-format off\n    uint8_t rotation_sequence[] = {\n        // Command,                 Delay,  N, Data[N]\n        ILI9XXX_SET_MEM_ACS_CTL,        0,  1, madctl[rotation],\n        ILI9XXX_SET_FUNCTION_CTL,       0,  2, 0x00, functl[rotation],\n        ILI9XXX_CMD_SLEEP_OFF,          5,  0,\n        ILI9XXX_CMD_DISPLAY_ON,         5,  0,\n    };\n    // clang-format on\n    qp_comms_bulk_command_sequence(device, rotation_sequence, sizeof(rotation_sequence));\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n\n// waveshare variant needs some tweaks due to shift registers\nstatic void qp_comms_spi_dc_reset_send_command_odd_cs_pulse(painter_device_t device, uint8_t cmd) {\n    painter_driver_t *              driver       = (painter_driver_t *)device;\n    qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;\n\n    gpio_write_pin_low(comms_config->spi_config.chip_select_pin);\n    qp_comms_spi_dc_reset_send_command(device, cmd);\n    gpio_write_pin_high(comms_config->spi_config.chip_select_pin);\n}\n\nstatic uint32_t qp_comms_spi_send_data_odd_cs_pulse(painter_device_t device, const void *data, uint32_t byte_count) {\n    painter_driver_t *              driver       = (painter_driver_t *)device;\n    qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;\n\n    uint32_t       bytes_remaining = byte_count;\n    const uint8_t *p               = (const uint8_t *)data;\n    uint32_t       max_msg_length  = 1024;\n\n    gpio_write_pin_high(comms_config->dc_pin);\n    while (bytes_remaining > 0) {\n        uint32_t bytes_this_loop = QP_MIN(bytes_remaining, max_msg_length);\n        bool     odd_bytes       = bytes_this_loop & 1;\n\n        // send data\n        gpio_write_pin_low(comms_config->spi_config.chip_select_pin);\n        spi_transmit(p, bytes_this_loop);\n        p += bytes_this_loop;\n\n        // extra CS toggle, for alignment\n        if (odd_bytes) {\n            gpio_write_pin_high(comms_config->spi_config.chip_select_pin);\n            gpio_write_pin_low(comms_config->spi_config.chip_select_pin);\n        }\n\n        bytes_remaining -= bytes_this_loop;\n    }\n\n    return byte_count - bytes_remaining;\n}\n\nstatic uint32_t qp_ili9486_send_data_toggling(painter_device_t device, const uint8_t *data, uint32_t byte_count) {\n    painter_driver_t *              driver       = (painter_driver_t *)device;\n    qp_comms_spi_dc_reset_config_t *comms_config = (qp_comms_spi_dc_reset_config_t *)driver->comms_config;\n\n    uint32_t ret;\n    for (uint8_t j = 0; j < byte_count; ++j) {\n        gpio_write_pin_low(comms_config->spi_config.chip_select_pin);\n        ret = qp_comms_spi_dc_reset_send_data(device, &data[j], 1);\n        gpio_write_pin_high(comms_config->spi_config.chip_select_pin);\n    }\n\n    return ret;\n}\n\nstatic void qp_comms_spi_send_command_sequence_odd_cs_pulse(painter_device_t device, const uint8_t *sequence, size_t sequence_len) {\n    for (size_t i = 0; i < sequence_len;) {\n        uint8_t command   = sequence[i];\n        uint8_t delay     = sequence[i + 1];\n        uint8_t num_bytes = sequence[i + 2];\n\n        qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, command);\n        if (num_bytes > 0) {\n            qp_ili9486_send_data_toggling(device, &sequence[i + 3], num_bytes);\n        }\n\n        if (delay > 0) {\n            wait_ms(delay);\n        }\n        i += (3 + num_bytes);\n    }\n}\n\nstatic bool qp_ili9486_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {\n    painter_driver_t *                          driver = (painter_driver_t *)device;\n    tft_panel_dc_reset_painter_driver_vtable_t *vtable = (tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable;\n\n    // Fix up the drawing location if required\n    left += driver->offset_x;\n    right += driver->offset_x;\n    top += driver->offset_y;\n    bottom += driver->offset_y;\n\n    // Check if we need to manually swap the window coordinates based on whether or not we're in a sideways rotation\n    if (vtable->swap_window_coords && (driver->rotation == QP_ROTATION_90 || driver->rotation == QP_ROTATION_270)) {\n        uint16_t temp;\n\n        temp = left;\n        left = top;\n        top  = temp;\n\n        temp   = right;\n        right  = bottom;\n        bottom = temp;\n    }\n\n    // Set up the x-window\n    uint8_t xbuf[4] = {left >> 8, left & 0xFF, right >> 8, right & 0xFF};\n    qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.set_column_address);\n    qp_ili9486_send_data_toggling(device, xbuf, 4);\n\n    // Set up the y-window\n    uint8_t ybuf[4] = {top >> 8, top & 0xFF, bottom >> 8, bottom & 0xFF};\n    qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.set_row_address);\n    qp_ili9486_send_data_toggling(device, ybuf, 4);\n\n    // Lock in the window\n    qp_comms_spi_dc_reset_send_command_odd_cs_pulse(device, vtable->opcodes.enable_writes);\n    return true;\n}\n\n// Regular\nconst tft_panel_dc_reset_painter_driver_vtable_t ili9486_driver_vtable = {\n    .base =\n        {\n            .init            = qp_ili9486_init,\n            .power           = qp_tft_panel_power,\n            .clear           = qp_tft_panel_clear,\n            .flush           = qp_tft_panel_flush,\n            .pixdata         = qp_tft_panel_pixdata,\n            .viewport        = qp_tft_panel_viewport,\n            .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,\n            .append_pixels   = qp_tft_panel_append_pixels_rgb565,\n            .append_pixdata  = qp_tft_panel_append_pixdata,\n        },\n    .num_window_bytes   = 2,\n    .swap_window_coords = false,\n    .opcodes =\n        {\n            .display_on         = ILI9XXX_CMD_DISPLAY_ON,\n            .display_off        = ILI9XXX_CMD_DISPLAY_OFF,\n            .set_column_address = ILI9XXX_SET_COL_ADDR,\n            .set_row_address    = ILI9XXX_SET_PAGE_ADDR,\n            .enable_writes      = ILI9XXX_SET_MEM,\n        },\n};\n\n// Waveshare tweaks\nconst tft_panel_dc_reset_painter_driver_vtable_t ili9486_waveshare_driver_vtable = {\n    .base =\n        {\n            .init            = qp_ili9486_init,\n            .power           = qp_tft_panel_power,\n            .clear           = qp_tft_panel_clear,\n            .flush           = qp_tft_panel_flush,\n            .pixdata         = qp_tft_panel_pixdata,\n            .viewport        = qp_ili9486_viewport,\n            .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,\n            .append_pixels   = qp_tft_panel_append_pixels_rgb565,\n            .append_pixdata  = qp_tft_panel_append_pixdata,\n        },\n    .num_window_bytes   = 2,\n    .swap_window_coords = false,\n    .opcodes =\n        {\n            .display_on         = ILI9XXX_CMD_DISPLAY_ON,\n            .display_off        = ILI9XXX_CMD_DISPLAY_OFF,\n            .set_column_address = ILI9XXX_SET_COL_ADDR,\n            .set_row_address    = ILI9XXX_SET_PAGE_ADDR,\n            .enable_writes      = ILI9XXX_SET_MEM,\n        },\n};\n\nstatic const painter_comms_with_command_vtable_t spi_comms_odd_cs_pulse_vtable = {\n    .base =\n        {\n            .comms_init  = qp_comms_spi_dc_reset_init,\n            .comms_start = qp_comms_spi_start,\n            .comms_send  = qp_comms_spi_send_data_odd_cs_pulse,\n            .comms_stop  = qp_comms_spi_stop,\n        },\n    .send_command          = qp_comms_spi_dc_reset_send_command_odd_cs_pulse,\n    .bulk_command_sequence = qp_comms_spi_send_command_sequence_odd_cs_pulse,\n};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SPI\n\n#ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE\n\n// Factory function for creating a handle to the ILI9486 device\npainter_device_t qp_ili9486_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < ILI9486_NUM_DEVICES; ++i) {\n        tft_panel_dc_reset_painter_device_t *driver = &ili9486_drivers[i];\n        if (!driver->base.driver_vtable) {\n            driver->base.driver_vtable         = (const painter_driver_vtable_t *)&ili9486_driver_vtable;\n            driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;\n            driver->base.native_bits_per_pixel = 16; // RGB565\n            driver->base.panel_width           = panel_width;\n            driver->base.panel_height          = panel_height;\n            driver->base.rotation              = QP_ROTATION_0;\n            driver->base.offset_x              = 0;\n            driver->base.offset_y              = 0;\n\n            // SPI and other pin configuration\n            driver->base.comms_config                              = &driver->spi_dc_reset_config;\n            driver->spi_dc_reset_config.spi_config.chip_select_pin = chip_select_pin;\n            driver->spi_dc_reset_config.spi_config.divisor         = spi_divisor;\n            driver->spi_dc_reset_config.spi_config.lsb_first       = false;\n            driver->spi_dc_reset_config.spi_config.mode            = spi_mode;\n            driver->spi_dc_reset_config.dc_pin                     = dc_pin;\n            driver->spi_dc_reset_config.reset_pin                  = reset_pin;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\npainter_device_t qp_ili9486_make_spi_waveshare_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    painter_device_t device = qp_ili9486_make_spi_device(panel_width, panel_height, chip_select_pin, dc_pin, reset_pin, spi_divisor, spi_mode);\n    if (device) {\n        tft_panel_dc_reset_painter_device_t *driver = (tft_panel_dc_reset_painter_device_t *)device;\n        driver->base.driver_vtable                  = (const painter_driver_vtable_t *)&ili9486_waveshare_driver_vtable;\n        driver->base.comms_vtable                   = (const painter_comms_vtable_t *)&spi_comms_odd_cs_pulse_vtable;\n    }\n    return device;\n}\n\n#endif // QUANTUM_PAINTER_ILI9486_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "drivers/painter/ili9xxx/qp_ili9486.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ILI9486 configurables (add to your keyboard's config.h)\n\n#ifndef ILI9486_NUM_DEVICES\n/**\n * @def This controls the maximum number of ILI9486 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define ILI9486_NUM_DEVICES 1\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ILI9486 device factories\n\n#ifdef QUANTUM_PAINTER_ILI9486_SPI_ENABLE\n/**\n * Factory method for an ILI9486 SPI LCD device.\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_ili9486_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n\n/**\n * Factory method for an ILI9486 SPI LCD device.\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_ili9486_make_spi_waveshare_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n\n#endif // QUANTUM_PAINTER_ILI9486_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/ili9xxx/qp_ili9488.c",
    "content": "// Copyright 2021-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_ili9488.h\"\n#include \"qp_ili9xxx_opcodes.h\"\n#include \"qp_tft_panel.h\"\n\n#ifdef QUANTUM_PAINTER_ILI9488_SPI_ENABLE\n#    include <qp_comms_spi.h>\n#endif // QUANTUM_PAINTER_ILI9488_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Common\n\n// Driver storage\ntft_panel_dc_reset_painter_device_t ili9488_drivers[ILI9488_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Initialization\n\n__attribute__((weak)) bool qp_ili9488_init(painter_device_t device, painter_rotation_t rotation) {\n    // clang-format off\n    const uint8_t ili9488_init_sequence[] = {\n        // Command,                 Delay,  N, Data[N]\n        ILI9XXX_CMD_RESET,            120,  0,\n        ILI9XXX_SET_PGAMMA,             0, 15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F,\n        ILI9XXX_SET_NGAMMA,             0, 15, 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F,\n        ILI9XXX_SET_POWER_CTL_1,        0,  2, 0x17, 0x15,\n        ILI9XXX_SET_POWER_CTL_2,        0,  1, 0x41,\n        ILI9XXX_SET_VCOM_CTL_1,         0,  3, 0x00, 0x12, 0x80,\n        ILI9XXX_SET_PIX_FMT,            0,  1, 0x66,\n        ILI9XXX_SET_RGB_IF_SIG_CTL,     0,  1, 0x80,\n        ILI9XXX_SET_FRAME_CTL_NORMAL,   0,  1, 0xA0,\n        ILI9XXX_SET_INVERSION_CTL,      0,  1, 0x02,\n        ILI9XXX_SET_FUNCTION_CTL,       0,  2, 0x02, 0x02,\n        ILI9XXX_SET_IMAGE_FUNCTION,     0,  1, 0x00,\n        ILI9XXX_SET_PUMP_RATIO_CTL,     0,  4, 0xA9, 0x51, 0x2C, 0x82,\n        ILI9XXX_CMD_SLEEP_OFF,          5,  0,\n        ILI9XXX_CMD_DISPLAY_ON,        20,  0\n    };\n    // clang-format on\n    qp_comms_bulk_command_sequence(device, ili9488_init_sequence, sizeof(ili9488_init_sequence));\n\n    // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)\n    const uint8_t madctl[] = {\n        [QP_ROTATION_0]   = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MY,\n        [QP_ROTATION_90]  = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MX | ILI9XXX_MADCTL_MV | ILI9XXX_MADCTL_MY,\n        [QP_ROTATION_180] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MX,\n        [QP_ROTATION_270] = ILI9XXX_MADCTL_BGR | ILI9XXX_MADCTL_MV,\n    };\n    qp_comms_command_databyte(device, ILI9XXX_SET_MEM_ACS_CTL, madctl[rotation]);\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n\nconst tft_panel_dc_reset_painter_driver_vtable_t ili9488_driver_vtable = {\n    .base =\n        {\n            .init            = qp_ili9488_init,\n            .power           = qp_tft_panel_power,\n            .clear           = qp_tft_panel_clear,\n            .flush           = qp_tft_panel_flush,\n            .pixdata         = qp_tft_panel_pixdata,\n            .viewport        = qp_tft_panel_viewport,\n            .palette_convert = qp_tft_panel_palette_convert_rgb888,\n            .append_pixels   = qp_tft_panel_append_pixels_rgb888,\n            .append_pixdata  = qp_tft_panel_append_pixdata,\n        },\n    .num_window_bytes   = 2,\n    .swap_window_coords = false,\n    .opcodes =\n        {\n            .display_on         = ILI9XXX_CMD_DISPLAY_ON,\n            .display_off        = ILI9XXX_CMD_DISPLAY_OFF,\n            .set_column_address = ILI9XXX_SET_COL_ADDR,\n            .set_row_address    = ILI9XXX_SET_PAGE_ADDR,\n            .enable_writes      = ILI9XXX_SET_MEM,\n        },\n};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SPI\n\n#ifdef QUANTUM_PAINTER_ILI9488_SPI_ENABLE\n\n// Factory function for creating a handle to the ILI9488 device\npainter_device_t qp_ili9488_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < ILI9488_NUM_DEVICES; ++i) {\n        tft_panel_dc_reset_painter_device_t *driver = &ili9488_drivers[i];\n        if (!driver->base.driver_vtable) {\n            driver->base.driver_vtable         = (const painter_driver_vtable_t *)&ili9488_driver_vtable;\n            driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;\n            driver->base.native_bits_per_pixel = 24; // RGB888\n            driver->base.panel_width           = panel_width;\n            driver->base.panel_height          = panel_height;\n            driver->base.rotation              = QP_ROTATION_0;\n            driver->base.offset_x              = 0;\n            driver->base.offset_y              = 0;\n\n            // SPI and other pin configuration\n            driver->base.comms_config                                   = &driver->spi_dc_reset_config;\n            driver->spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;\n            driver->spi_dc_reset_config.spi_config.divisor              = spi_divisor;\n            driver->spi_dc_reset_config.spi_config.lsb_first            = false;\n            driver->spi_dc_reset_config.spi_config.mode                 = spi_mode;\n            driver->spi_dc_reset_config.dc_pin                          = dc_pin;\n            driver->spi_dc_reset_config.reset_pin                       = reset_pin;\n            driver->spi_dc_reset_config.command_params_uses_command_pin = false;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_ILI9488_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "drivers/painter/ili9xxx/qp_ili9488.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ILI9488 configurables (add to your keyboard's config.h)\n\n#ifndef ILI9488_NUM_DEVICES\n/**\n * @def This controls the maximum number of ILI9488 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define ILI9488_NUM_DEVICES 1\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ILI9488 device factories\n\n#ifdef QUANTUM_PAINTER_ILI9488_SPI_ENABLE\n/**\n * Factory method for an ILI9488 SPI LCD device.\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_ili9488_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n#endif // QUANTUM_PAINTER_ILI9488_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/ili9xxx/qp_ili9xxx_opcodes.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ILI9xxx command opcodes\n#define ILI9XXX_CMD_NOP 0x00                // No operation\n#define ILI9XXX_CMD_RESET 0x01              // Software reset\n#define ILI9XXX_GET_ID_INFO 0x04            // Get ID information\n#define ILI9XXX_GET_STATUS 0x09             // Get status\n#define ILI9XXX_GET_PWR_MODE 0x0A           // Get power mode\n#define ILI9XXX_GET_MADCTL 0x0B             // Get MADCTL\n#define ILI9XXX_GET_PIX_FMT 0x0C            // Get pixel format\n#define ILI9XXX_GET_IMG_FMT 0x0D            // Get image format\n#define ILI9XXX_GET_SIG_MODE 0x0E           // Get signal mode\n#define ILI9XXX_GET_SELF_DIAG 0x0F          // Get self-diagnostics\n#define ILI9XXX_CMD_SLEEP_ON 0x10           // Enter sleep mode\n#define ILI9XXX_CMD_SLEEP_OFF 0x11          // Exist sleep mode\n#define ILI9XXX_CMD_PARTIAL_ON 0x12         // Enter partial mode\n#define ILI9XXX_CMD_PARTIAL_OFF 0x13        // Exit partial mode\n#define ILI9XXX_CMD_INVERT_OFF 0x20         // Exit inverted mode\n#define ILI9XXX_CMD_INVERT_ON 0x21          // Enter inverted mode\n#define ILI9XXX_SET_GAMMA 0x26              // Set gamma params\n#define ILI9XXX_CMD_DISPLAY_OFF 0x28        // Disable display\n#define ILI9XXX_CMD_DISPLAY_ON 0x29         // Enable display\n#define ILI9XXX_SET_COL_ADDR 0x2A           // Set column address\n#define ILI9XXX_SET_PAGE_ADDR 0x2B          // Set page address\n#define ILI9XXX_SET_MEM 0x2C                // Set memory\n#define ILI9XXX_SET_COLOR 0x2D              // Set color\n#define ILI9XXX_GET_MEM 0x2E                // Get memory\n#define ILI9XXX_SET_PARTIAL_AREA 0x30       // Set partial area\n#define ILI9XXX_SET_VSCROLL 0x33            // Set vertical scroll def\n#define ILI9XXX_CMD_TEARING_ON 0x34         // Tearing line enabled\n#define ILI9XXX_CMD_TEARING_OFF 0x35        // Tearing line disabled\n#define ILI9XXX_SET_MEM_ACS_CTL 0x36        // Set mem access ctl\n#define ILI9XXX_SET_VSCROLL_ADDR 0x37       // Set vscroll start addr\n#define ILI9XXX_CMD_IDLE_OFF 0x38           // Exit idle mode\n#define ILI9XXX_CMD_IDLE_ON 0x39            // Enter idle mode\n#define ILI9XXX_SET_PIX_FMT 0x3A            // Set pixel format\n#define ILI9XXX_SET_MEM_CONT 0x3C           // Set memory continue\n#define ILI9XXX_GET_MEM_CONT 0x3E           // Get memory continue\n#define ILI9XXX_SET_TEAR_SCANLINE 0x44      // Set tearing scanline\n#define ILI9XXX_GET_TEAR_SCANLINE 0x45      // Get tearing scanline\n#define ILI9XXX_SET_BRIGHTNESS 0x51         // Set brightness\n#define ILI9XXX_GET_BRIGHTNESS 0x52         // Get brightness\n#define ILI9XXX_SET_DISPLAY_CTL 0x53        // Set display ctl\n#define ILI9XXX_GET_DISPLAY_CTL 0x54        // Get display ctl\n#define ILI9XXX_SET_CABC 0x55               // Set CABC\n#define ILI9XXX_GET_CABC 0x56               // Get CABC\n#define ILI9XXX_SET_CABC_MIN 0x5E           // Set CABC min\n#define ILI9XXX_GET_CABC_MIN 0x5F           // Set CABC max\n#define ILI9XXX_GET_ID1 0xDA                // Get ID1\n#define ILI9XXX_GET_ID2 0xDB                // Get ID2\n#define ILI9XXX_GET_ID3 0xDC                // Get ID3\n#define ILI9XXX_SET_RGB_IF_SIG_CTL 0xB0     // RGB IF signal ctl\n#define ILI9XXX_SET_FRAME_CTL_NORMAL 0xB1   // Set frame ctl (normal)\n#define ILI9XXX_SET_FRAME_CTL_IDLE 0xB2     // Set frame ctl (idle)\n#define ILI9XXX_SET_FRAME_CTL_PARTIAL 0xB3  // Set frame ctl (partial)\n#define ILI9XXX_SET_INVERSION_CTL 0xB4      // Set inversion ctl\n#define ILI9XXX_SET_BLANKING_PORCH_CTL 0xB5 // Set blanking porch ctl\n#define ILI9XXX_SET_FUNCTION_CTL 0xB6       // Set function ctl\n#define ILI9XXX_SET_ENTRY_MODE 0xB7         // Set entry mode\n#define ILI9XXX_SET_LIGHT_CTL_1 0xB8        // Set backlight ctl 1\n#define ILI9XXX_SET_LIGHT_CTL_2 0xB9        // Set backlight ctl 2\n#define ILI9XXX_SET_LIGHT_CTL_3 0xBA        // Set backlight ctl 3\n#define ILI9XXX_SET_LIGHT_CTL_4 0xBB        // Set backlight ctl 4\n#define ILI9XXX_SET_LIGHT_CTL_5 0xBC        // Set backlight ctl 5\n#define ILI9XXX_SET_LIGHT_CTL_7 0xBE        // Set backlight ctl 7\n#define ILI9XXX_SET_LIGHT_CTL_8 0xBF        // Set backlight ctl 8\n#define ILI9XXX_SET_POWER_CTL_1 0xC0        // Set power ctl 1\n#define ILI9XXX_SET_POWER_CTL_2 0xC1        // Set power ctl 2\n#define ILI9XXX_SET_POWER_CTL_3 0xC2        // Set power ctl 3\n#define ILI9XXX_SET_VCOM_CTL_1 0xC5         // Set VCOM ctl 1\n#define ILI9XXX_SET_VCOM_CTL_2 0xC7         // Set VCOM ctl 2\n#define ILI9XXX_POWER_CTL_A 0xCB            // Set power control A\n#define ILI9XXX_POWER_CTL_B 0xCF            // Set power control B\n#define ILI9XXX_DRV_TIMING_CTL_A 0xE8       // Set driver timing control A\n#define ILI9XXX_DRV_TIMING_CTL_B 0xEA       // Set driver timing control B\n#define ILI9XXX_POWER_ON_SEQ_CTL 0xED       // Set Power on sequence control\n#define ILI9XXX_SET_NVMEM 0xD0              // Set NVMEM data\n#define ILI9XXX_GET_NVMEM_KEY 0xD1          // Get NVMEM protect key\n#define ILI9XXX_GET_NVMEM_STATUS 0xD2       // Get NVMEM status\n#define ILI9XXX_GET_ID4 0xD3                // Get ID4\n#define ILI9XXX_SET_PGAMMA 0xE0             // Set positive gamma\n#define ILI9XXX_SET_NGAMMA 0xE1             // Set negative gamma\n#define ILI9XXX_SET_DGAMMA_CTL_1 0xE2       // Set digital gamma ctl 1\n#define ILI9XXX_SET_DGAMMA_CTL_2 0xE3       // Set digital gamma ctl 2\n#define ILI9XXX_SET_IMAGE_FUNCTION 0xE9     // Set image function\n#define ILI9XXX_ENABLE_3_GAMMA 0xF2         // Enable 3 gamma\n#define ILI9XXX_SET_IF_CTL 0xF6             // Set interface control\n#define ILI9XXX_SET_PUMP_RATIO_CTL 0xF7     // Set pump ratio control\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// MADCTL Flags\n#define ILI9XXX_MADCTL_MY 0b10000000\n#define ILI9XXX_MADCTL_MX 0b01000000\n#define ILI9XXX_MADCTL_MV 0b00100000\n#define ILI9XXX_MADCTL_ML 0b00010000\n#define ILI9XXX_MADCTL_RGB 0b00000000\n#define ILI9XXX_MADCTL_BGR 0b00001000\n#define ILI9XXX_MADCTL_MH 0b00000100\n"
  },
  {
    "path": "drivers/painter/ld7032/qp_ld7032.c",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// Copyright 2023 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_oled_panel.h\"\n#include \"qp_ld7032.h\"\n#include \"qp_ld7032_opcodes.h\"\n#include \"qp_surface.h\"\n#include \"qp_surface_internal.h\"\n\ntypedef void (*ld7032_driver_comms_send_command_and_data_func)(painter_device_t device, uint8_t cmd, uint8_t data);\ntypedef uint32_t (*ld7032_driver_comms_send_command_and_databuf_func)(painter_device_t device, uint8_t cmd, const void *data, uint32_t byte_count);\n\ntypedef struct ld7032_comms_with_command_vtable_t {\n    painter_comms_vtable_t                            base; // must be first, so this object can be cast from the painter_comms_vtable_t* type\n    painter_driver_comms_send_command_func            send_command;\n    painter_driver_comms_bulk_command_sequence        bulk_command_sequence;\n    ld7032_driver_comms_send_command_and_data_func    send_command_data;\n    ld7032_driver_comms_send_command_and_databuf_func send_command_databuf;\n} ld7032_comms_with_command_vtable_t;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// LD7032 Internal API\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid ld7032_comms_i2c_send_command_and_data(painter_device_t device, uint8_t cmd, uint8_t data) {\n    uint8_t buf[2] = {cmd, data};\n    qp_comms_i2c_send_data(device, buf, 2);\n}\n\nvoid ld7032_comms_i2c_bulk_command_sequence(painter_device_t device, const uint8_t *sequence, size_t sequence_len) {\n    uint8_t buf[32];\n    for (size_t i = 0; i < sequence_len;) {\n        uint8_t command   = sequence[i];\n        uint8_t delay     = sequence[i + 1];\n        uint8_t num_bytes = sequence[i + 2];\n        buf[0]            = command;\n        memcpy(&buf[1], &sequence[i + 3], num_bytes);\n        qp_comms_i2c_send_data(device, buf, num_bytes + 1);\n        if (delay > 0) {\n            wait_ms(delay);\n        }\n        i += (3 + num_bytes);\n    }\n}\n\nuint32_t ld7032_comms_i2c_send_command_and_databuf(painter_device_t device, uint8_t cmd, const void *data, uint32_t byte_count) {\n    uint8_t buf[byte_count + 1];\n    memset(buf, 0, sizeof(buf));\n    buf[0] = cmd;\n    memcpy(&buf[1], data, byte_count);\n    return qp_comms_send(device, buf, byte_count + 1);\n}\n\n// Power control\nbool qp_ld7032_power(painter_device_t device, bool power_on) {\n    painter_driver_t *                  driver       = (painter_driver_t *)device;\n    ld7032_comms_with_command_vtable_t *comms_vtable = (ld7032_comms_with_command_vtable_t *)driver->comms_vtable;\n\n    comms_vtable->send_command_data(device, LD7032_DISP_ON_OFF, power_on ? 0x01 : 0x00);\n\n    return true;\n}\n\n// Screen clear\nbool qp_ld7032_clear(painter_device_t device) {\n    qp_rect(device, 0, 0, 127, 127, 0, 0, 0, true); // clear memory\n    painter_driver_t *driver = (painter_driver_t *)device;\n    driver->driver_vtable->init(device, driver->rotation); // Re-init the display\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Flush helpers\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid ld7032_flush_0(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer, bool inverted) {\n    painter_driver_t *                  driver       = (painter_driver_t *)device;\n    ld7032_comms_with_command_vtable_t *comms_vtable = (ld7032_comms_with_command_vtable_t *)driver->comms_vtable;\n\n    int     x_start       = dirty->l >> 3;\n    int     x_end         = dirty->r >> 3;\n    int     y_start       = dirty->t;\n    int     y_end         = dirty->b;\n    int     x_length      = (x_end - x_start) + 1;\n    uint8_t x_view_offset = driver->offset_x >> 3;\n    uint8_t y_view_offset = driver->offset_y;\n\n    for (int y_pos = y_start; y_pos <= y_end; y_pos++) {\n        int y_new_pos = y_pos;\n        if (inverted) {\n            y_new_pos = y_end - y_pos;\n        }\n        uint8_t packet[x_length];\n        memcpy(packet, &framebuffer[(y_pos * (driver->panel_width >> 3)) + x_start], x_length);\n        uint8_t x_write_start = MIN(x_start + x_view_offset, (128 >> 3));\n        uint8_t x_write_end   = MIN(x_end + x_view_offset, (128 >> 3));\n        uint8_t y_write_start = MIN(y_new_pos + y_view_offset, 39);\n        uint8_t y_write_end   = MIN(y_new_pos + y_view_offset, 39);\n\n        comms_vtable->send_command_data(device, LD7032_X_BOX_ADR_START, x_write_start);\n        comms_vtable->send_command_data(device, LD7032_X_BOX_ADR_END, x_write_end);\n        comms_vtable->send_command_data(device, LD7032_Y_BOX_ADR_START, y_write_start);\n        comms_vtable->send_command_data(device, LD7032_Y_BOX_ADR_END, y_write_end);\n        comms_vtable->send_command_databuf(device, LD7032_DATA_RW, packet, x_length);\n    }\n}\n\nvoid ld7032_flush_90(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer, bool inverted) {\n    painter_driver_t *                  driver       = (painter_driver_t *)device;\n    ld7032_comms_with_command_vtable_t *comms_vtable = (ld7032_comms_with_command_vtable_t *)driver->comms_vtable;\n\n    int     x_start       = dirty->t >> 3;\n    int     x_end         = dirty->b >> 3;\n    int     y_start       = dirty->l;\n    int     y_end         = dirty->r;\n    int     x_length      = (x_end - x_start) + 1;\n    uint8_t x_view_offset = driver->offset_x >> 3;\n    uint8_t y_view_offset = driver->offset_y;\n\n    for (int y_pos = y_start; y_pos <= y_end; y_pos++) {\n        int y_new_pos = y_pos;\n        if (inverted) {\n            y_new_pos = y_end - y_pos;\n        }\n        uint8_t packet[x_length];\n        memset(packet, 0, sizeof(packet));\n        int count = 0;\n        for (int x_pos = x_start; x_pos <= x_end; x_pos++) {\n            for (int x = 0; x < 8; ++x) {\n                uint32_t pixel_num   = (((x_pos << 3) + x) * driver->panel_height) + y_pos;\n                uint32_t byte_offset = pixel_num / 8;\n                uint8_t  bit_offset  = pixel_num % 8;\n                packet[count] |= ((framebuffer[byte_offset] & (1 << bit_offset)) >> bit_offset) << x;\n            }\n            count++;\n        }\n        uint8_t x_width       = (driver->panel_width >> 3) - 1;\n        uint8_t x_write_start = MAX((int)x_width - x_end - x_view_offset, 0);\n        uint8_t x_write_end   = MAX((int)x_width - x_start - x_view_offset, 0);\n        uint8_t y_write_start = MIN(y_new_pos + y_view_offset, 39);\n        uint8_t y_write_end   = MIN(y_new_pos + y_view_offset, 39);\n\n        comms_vtable->send_command_data(device, LD7032_X_BOX_ADR_START, x_write_start);\n        comms_vtable->send_command_data(device, LD7032_X_BOX_ADR_END, x_write_end);\n        comms_vtable->send_command_data(device, LD7032_Y_BOX_ADR_START, y_write_start);\n        comms_vtable->send_command_data(device, LD7032_Y_BOX_ADR_END, y_write_end);\n        comms_vtable->send_command_databuf(device, LD7032_DATA_RW, packet, x_length);\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver storage\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntypedef struct ld7032_device_t {\n    oled_panel_painter_device_t oled;\n\n    uint8_t framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(128, 40, 1)];\n} ld7032_device_t;\n\nstatic ld7032_device_t ld7032_drivers[LD7032_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter API implementations\n\n// Initialisation\n__attribute__((weak)) bool qp_ld7032_init(painter_device_t device, painter_rotation_t rotation) {\n    ld7032_device_t *driver = (ld7032_device_t *)device;\n\n    // Change the surface geometry based on the panel rotation\n    if (rotation == QP_ROTATION_90 || rotation == QP_ROTATION_270) {\n        driver->oled.surface.base.panel_width  = driver->oled.base.panel_height;\n        driver->oled.surface.base.panel_height = driver->oled.base.panel_width;\n    } else {\n        driver->oled.surface.base.panel_width  = driver->oled.base.panel_width;\n        driver->oled.surface.base.panel_height = driver->oled.base.panel_height;\n    }\n\n    // Init the internal surface\n    if (!qp_init(&driver->oled.surface.base, QP_ROTATION_0)) {\n        qp_dprintf(\"Failed to init internal surface in qp_ld7032_init\\n\");\n        return false;\n    }\n\n    // clang-format off\n    const uint8_t ld7032_init_sequence[] = {\n        // Command,                    Delay,  N, Data[N]\n        LD7032_DISP_STBY_ON_OFF, 0, 1, 0x00,\n        LD7032_DISP_ON_OFF,      0, 1, 0x00,\n        LD7032_DFRAME,           0, 1, 0x05,\n        //LD7032_WRITE_DIRECTION, 0, 1, 0b00001000, // 0 Right, 1 Up, 2 Vertical, 3 Bit Order, 4-7 Unused\n        LD7032_DISP_DIRECTION,  0, 1, 0x00,\n        LD7032_PEAK_WIDTH,      0, 1, 0x1F,\n        LD7032_PEAK_DELAY,      0, 1, 0x05,\n        LD7032_SCAN_MODE,       0, 1, 0x01,\n        LD7032_DOT_CURRENT,     0, 1, 0x1f,\n        LD7032_VDD_SEL,         0, 1, 0x01,\n    };\n    // clang-format on\n\n    qp_comms_bulk_command_sequence(device, ld7032_init_sequence, sizeof(ld7032_init_sequence));\n\n    uint8_t display_y_start = 40 - driver->oled.base.panel_height;\n    uint8_t display_x_start = (128 - driver->oled.base.panel_width) / 2;\n\n    // clang-format off\n    uint8_t ld7032_memory_setup[] = {\n        // Command,        Delay,  N, Data[N]\n        LD7032_DISP_SIZE_X,     0, 2, 0x00, 0x7F,\n        LD7032_DISP_SIZE_Y,     0, 2, 0x00, 0x27,\n        LD7032_X_DISP_START,    0, 1, 0x0,\n        LD7032_Y_DISP_START,    0, 1, 0x0,\n    };\n    // clang-format on\n\n    ld7032_memory_setup[3]  = display_x_start;\n    ld7032_memory_setup[4]  = display_x_start + driver->oled.base.panel_width - 1;\n    ld7032_memory_setup[8]  = display_y_start;\n    ld7032_memory_setup[9]  = display_y_start + driver->oled.base.panel_height - 1;\n    ld7032_memory_setup[13] = ld7032_memory_setup[4] + 1;\n    ld7032_memory_setup[17] = driver->oled.base.panel_height;\n\n    qp_comms_bulk_command_sequence(device, ld7032_memory_setup, sizeof(ld7032_memory_setup));\n\n    uint8_t write_direction = 0;\n    switch (rotation) {\n        default:\n        case QP_ROTATION_0:\n            write_direction = 0b00001000;\n            break;\n        case QP_ROTATION_90:\n            write_direction = 0b00000001;\n            break;\n        case QP_ROTATION_180:\n            write_direction = 0b00000001;\n            break;\n        case QP_ROTATION_270:\n            write_direction = 0b00001000;\n            break;\n    }\n\n    painter_driver_t *                  pdriver      = (painter_driver_t *)device;\n    ld7032_comms_with_command_vtable_t *comms_vtable = (ld7032_comms_with_command_vtable_t *)pdriver->comms_vtable;\n\n    comms_vtable->send_command_data(device, LD7032_WRITE_DIRECTION, write_direction);\n\n    qp_ld7032_power(device, true);\n\n    return true;\n}\n\n// Screen flush\nbool qp_ld7032_flush(painter_device_t device) {\n    ld7032_device_t *driver = (ld7032_device_t *)device;\n\n    if (!driver->oled.surface.dirty.is_dirty) {\n        return true;\n    }\n\n    switch (driver->oled.base.rotation) {\n        default:\n        case QP_ROTATION_0:\n            ld7032_flush_0(device, &driver->oled.surface.dirty, driver->framebuffer, false);\n            break;\n        case QP_ROTATION_180:\n            ld7032_flush_0(device, &driver->oled.surface.dirty, driver->framebuffer, true);\n            break;\n        case QP_ROTATION_90:\n            ld7032_flush_90(device, &driver->oled.surface.dirty, driver->framebuffer, false);\n            break;\n        case QP_ROTATION_270:\n            ld7032_flush_90(device, &driver->oled.surface.dirty, driver->framebuffer, true);\n            break;\n    }\n\n    // Clear the dirty area\n    qp_flush(&driver->oled.surface);\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nconst painter_driver_vtable_t ld7032_driver_vtable = {\n    .init            = qp_ld7032_init,\n    .power           = qp_ld7032_power,\n    .clear           = qp_ld7032_clear,\n    .flush           = qp_ld7032_flush,\n    .pixdata         = qp_oled_panel_passthru_pixdata,\n    .viewport        = qp_oled_panel_passthru_viewport,\n    .palette_convert = qp_oled_panel_passthru_palette_convert,\n    .append_pixels   = qp_oled_panel_passthru_append_pixels,\n    .append_pixdata  = qp_oled_panel_passthru_append_pixdata,\n};\n\n#ifdef QUANTUM_PAINTER_LD7032_SPI_ENABLE\n\nconst ld7032_comms_with_command_vtable_t ld7032_spi_comms_vtable = {\n    .base =\n        {\n            .comms_init  = qp_comms_spi_dc_reset_init,\n            .comms_start = qp_comms_spi_start,\n            .comms_send  = qp_comms_spi_dc_reset_send_data,\n            .comms_stop  = qp_comms_spi_stop,\n        },\n    .send_command          = qp_comms_spi_dc_reset_send_command,\n    .send_command_data     = qp_comms_command_databyte,\n    .send_command_databuf  = qp_comms_command_databuf,\n    .bulk_command_sequence = qp_comms_spi_dc_reset_bulk_command_sequence,\n};\n\n// Factory function for creating a handle to the LD7032 device\npainter_device_t qp_ld7032_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < LD7032_NUM_DEVICES; ++i) {\n        ld7032_device_t *driver = &ld7032_drivers[i];\n        if (!driver->oled.base.driver_vtable) {\n            painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer);\n            if (!surface) {\n                return NULL;\n            }\n\n            // Setup the OLED device\n            driver->oled.base.driver_vtable         = (const painter_driver_vtable_t *)&ld7032_driver_vtable;\n            driver->oled.base.comms_vtable          = (const painter_comms_vtable_t *)&ld7032_spi_comms_vtable;\n            driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono\n            driver->oled.base.panel_width           = panel_width;\n            driver->oled.base.panel_height          = panel_height;\n            driver->oled.base.rotation              = QP_ROTATION_0;\n            driver->oled.base.offset_x              = 0;\n            driver->oled.base.offset_y              = 0;\n\n            // SPI and other pin configuration\n            driver->oled.base.comms_config                                   = &driver->oled.spi_dc_reset_config;\n            driver->oled.spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;\n            driver->oled.spi_dc_reset_config.spi_config.divisor              = spi_divisor;\n            driver->oled.spi_dc_reset_config.spi_config.lsb_first            = false;\n            driver->oled.spi_dc_reset_config.spi_config.mode                 = spi_mode;\n            driver->oled.spi_dc_reset_config.dc_pin                          = dc_pin;\n            driver->oled.spi_dc_reset_config.reset_pin                       = reset_pin;\n            driver->oled.spi_dc_reset_config.command_params_uses_command_pin = true;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(ld7032_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_LD7032_SPI_ENABLE\n\n#ifdef QUANTUM_PAINTER_LD7032_I2C_ENABLE\n\nconst ld7032_comms_with_command_vtable_t ld7032_i2c_comms_vtable = {\n    .base =\n        {\n            .comms_init  = qp_comms_i2c_init,\n            .comms_start = qp_comms_i2c_start,\n            .comms_send  = qp_comms_i2c_send_data,\n            .comms_stop  = qp_comms_i2c_stop,\n        },\n    .send_command          = NULL,\n    .send_command_data     = ld7032_comms_i2c_send_command_and_data,\n    .send_command_databuf  = ld7032_comms_i2c_send_command_and_databuf,\n    .bulk_command_sequence = ld7032_comms_i2c_bulk_command_sequence,\n};\n\n// Factory function for creating a handle to the LD7032 device\npainter_device_t qp_ld7032_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address) {\n    for (uint32_t i = 0; i < LD7032_NUM_DEVICES; ++i) {\n        ld7032_device_t *driver = &ld7032_drivers[i];\n        if (!driver->oled.base.driver_vtable) {\n            painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer);\n            if (!surface) {\n                return NULL;\n            }\n\n            // Setup the OLED device\n            driver->oled.base.driver_vtable         = (const painter_driver_vtable_t *)&ld7032_driver_vtable;\n            driver->oled.base.comms_vtable          = (const painter_comms_vtable_t *)&ld7032_i2c_comms_vtable;\n            driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono\n            driver->oled.base.panel_width           = panel_width;\n            driver->oled.base.panel_height          = panel_height;\n            driver->oled.base.rotation              = QP_ROTATION_0;\n            driver->oled.base.offset_x              = 0;\n            driver->oled.base.offset_y              = 0;\n\n            // I2C configuration\n            driver->oled.base.comms_config       = &driver->oled.i2c_config;\n            driver->oled.i2c_config.chip_address = i2c_address;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(ld7032_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_LD7032_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/ld7032/qp_ld7032.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter LD7032 configurables (add to your keyboard's config.h)\n\n#if defined(QUANTUM_PAINTER_LD7032_SPI_ENABLE) && !defined(LD7032_NUM_SPI_DEVICES)\n/**\n * @def This controls the maximum number of SPI LD7032 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define LD7032_NUM_SPI_DEVICES 1\n#else\n#    define LD7032_NUM_SPI_DEVICES 0\n#endif\n\n#if defined(QUANTUM_PAINTER_LD7032_I2C_ENABLE) && !defined(LD7032_NUM_I2C_DEVICES)\n/**\n * @def This controls the maximum number of I2C LD7032 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define LD7032_NUM_I2C_DEVICES 1\n#else\n#    define LD7032_NUM_I2C_DEVICES 0\n#endif\n\n#define LD7032_NUM_DEVICES ((LD7032_NUM_SPI_DEVICES) + (LD7032_NUM_I2C_DEVICES))\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter LD7032 device factories\n\n#ifdef QUANTUM_PAINTER_LD7032_SPI_ENABLE\n\n/**\n * Factory method for an LD7032 SPI LCD device.\n *\n * @param panel_width[in] the width of the display in pixels (usually 128)\n * @param panel_height[in] the height of the display in pixels (usually 64)\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_ld7032_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n\n#endif // QUANTUM_PAINTER_LD7032_SPI_ENABLE\n\n#ifdef QUANTUM_PAINTER_LD7032_I2C_ENABLE\n\n/**\n * Factory method for an LD7032 I2C LCD device.\n *\n * @param panel_width[in] the width of the display in pixels (usually 128)\n * @param panel_height[in] the height of the display in pixels (usually 64)\n * @param i2c_address[in] the I2C address to use\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_ld7032_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address);\n\n#endif // QUANTUM_PAINTER_LD7032_I2C_ENABLE\n"
  },
  {
    "path": "drivers/painter/ld7032/qp_ld7032_opcodes.h",
    "content": "// Copyright 2023 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\ntypedef enum {\n    LD7032_SOFTRES          = 0x01,\n    LD7032_DISP_ON_OFF      = 0x02,\n    LD7032_DATA_RW          = 0x08,\n    LD7032_DISP_DIRECTION   = 0x09,\n    LD7032_IFMODE           = 0x0D,\n    LD7032_PEAK_WIDTH       = 0x10,\n    LD7032_DOT_CURRENT      = 0x12,\n    LD7032_SCAN_MODE        = 0x13,\n    LD7032_DISP_STBY_ON_OFF = 0x14,\n    LD7032_PEAK_DELAY       = 0x16,\n    LD7032_ROW_SCAN         = 0x17,\n    LD7032_PRE_C_WIDTH      = 0x18,\n    LD7032_DFRAME           = 0x1A,\n    LD7032_DATA_REVERSE     = 0x1C,\n    LD7032_WRITE_DIRECTION  = 0x1D,\n    LD7032_READREG          = 0x20,\n    LD7032_DISP_SIZE_X      = 0x30,\n    LD7032_DISP_SIZE_Y      = 0x32,\n    LD7032_X_BOX_ADR_START  = 0x34,\n    LD7032_X_BOX_ADR_END    = 0x35,\n    LD7032_Y_BOX_ADR_START  = 0x36,\n    LD7032_Y_BOX_ADR_END    = 0x37,\n    LD7032_X_DISP_START     = 0x38,\n    LD7032_Y_DISP_START     = 0x39,\n    LD7032_XTALK_EN         = 0x3A,\n    LD7032_XTALK_REF        = 0x3B,\n    LD7032_AGING_EN         = 0x3C,\n    LD7032_VDD_SEL          = 0x3D,\n    LD7032_TESTCNT0         = 0x3E,\n    LD7032_VCC_R_SEL        = 0x3F,\n    LD7032_PRE_C_SELECT     = 0x44,\n    LD7032_ROW_OVERLAP      = 0x48,\n    LD7032_S_SLEEP_TIMER    = 0xC0,\n    LD7032_S_SLEEP_START    = 0xC2,\n    LD7032_S_STEP_TIMER     = 0xC3,\n    LD7032_S_STEP_UNIT      = 0xC4,\n    LD7032_S_CONDITION      = 0xCC,\n    LD7032_S_START_STOP     = 0xCD,\n    LD7032_S_SELECT         = 0xCE,\n    LD7032_TESTCNT1         = 0xF0, //-0xFF\n} ld7032_opcodes;\n"
  },
  {
    "path": "drivers/painter/oled_panel/qp_oled_panel.c",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"color.h\"\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_draw.h\"\n#include \"qp_oled_panel.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter API implementations\n\n// Power control\nbool qp_oled_panel_power(painter_device_t device, bool power_on) {\n    painter_driver_t *                  driver = (painter_driver_t *)device;\n    oled_panel_painter_driver_vtable_t *vtable = (oled_panel_painter_driver_vtable_t *)driver->driver_vtable;\n    qp_comms_command(device, power_on ? vtable->opcodes.display_on : vtable->opcodes.display_off);\n    return true;\n}\n\n// Screen clear\nbool qp_oled_panel_clear(painter_device_t device) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n    driver->driver_vtable->init(device, driver->rotation); // Re-init the display\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Surface passthru\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nbool qp_oled_panel_passthru_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) {\n    oled_panel_painter_device_t *driver = (oled_panel_painter_device_t *)device;\n    return driver->surface.base.validate_ok && driver->surface.base.driver_vtable->pixdata(&driver->surface.base, pixel_data, native_pixel_count);\n}\n\nbool qp_oled_panel_passthru_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {\n    oled_panel_painter_device_t *driver = (oled_panel_painter_device_t *)device;\n    return driver->surface.base.validate_ok && driver->surface.base.driver_vtable->viewport(&driver->surface.base, left, top, right, bottom);\n}\n\nbool qp_oled_panel_passthru_palette_convert(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {\n    oled_panel_painter_device_t *driver = (oled_panel_painter_device_t *)device;\n    return driver->surface.base.validate_ok && driver->surface.base.driver_vtable->palette_convert(&driver->surface.base, palette_size, palette);\n}\n\nbool qp_oled_panel_passthru_append_pixels(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices) {\n    oled_panel_painter_device_t *driver = (oled_panel_painter_device_t *)device;\n    return driver->surface.base.validate_ok && driver->surface.base.driver_vtable->append_pixels(&driver->surface.base, target_buffer, palette, pixel_offset, pixel_count, palette_indices);\n}\n\nbool qp_oled_panel_passthru_append_pixdata(painter_device_t device, uint8_t *target_buffer, uint32_t pixdata_offset, uint8_t pixdata_byte) {\n    oled_panel_painter_device_t *driver = (oled_panel_painter_device_t *)device;\n    return driver->surface.base.validate_ok && driver->surface.base.driver_vtable->append_pixdata(&driver->surface.base, target_buffer, pixdata_offset, pixdata_byte);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Flush helpers\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid qp_oled_panel_page_column_flush_rot0(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer) {\n    painter_driver_t *                  driver = (painter_driver_t *)device;\n    oled_panel_painter_driver_vtable_t *vtable = (oled_panel_painter_driver_vtable_t *)driver->driver_vtable;\n\n    // TODO: account for offset_x/y in base driver\n    int min_page   = dirty->t / 8;\n    int max_page   = dirty->b / 8;\n    int min_column = dirty->l;\n    int max_column = dirty->r;\n\n    for (int page = min_page; page <= max_page; ++page) {\n        int     cols_required = max_column - min_column + 1;\n        uint8_t column_data[cols_required];\n        memset(column_data, 0, cols_required);\n        for (int x = min_column; x <= max_column; ++x) {\n            uint16_t data_offset = x - min_column;\n            for (int y = 0; y < 8; ++y) {\n                uint32_t pixel_num   = ((page * 8) + y) * driver->panel_width + x;\n                uint32_t byte_offset = pixel_num / 8;\n                uint8_t  bit_offset  = pixel_num % 8;\n                column_data[data_offset] |= ((framebuffer[byte_offset] & (1 << bit_offset)) >> bit_offset) << y;\n            }\n        }\n\n        int actual_page  = page;\n        int start_column = min_column;\n        qp_comms_command(device, vtable->opcodes.set_page | actual_page);\n        qp_comms_command(device, vtable->opcodes.set_column_lsb | (start_column & 0x0F));\n        qp_comms_command(device, vtable->opcodes.set_column_msb | (start_column & 0xF0) >> 4);\n        qp_comms_send(device, column_data, cols_required);\n    }\n}\n\nvoid qp_oled_panel_page_column_flush_rot90(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer) {\n    painter_driver_t *                  driver = (painter_driver_t *)device;\n    oled_panel_painter_driver_vtable_t *vtable = (oled_panel_painter_driver_vtable_t *)driver->driver_vtable;\n\n    // TODO: account for offset_x/y in base driver\n    int num_columns = driver->panel_width;\n    int min_page    = dirty->l / 8;\n    int max_page    = dirty->r / 8;\n    int min_column  = dirty->t;\n    int max_column  = dirty->b;\n\n    for (int page = min_page; page <= max_page; ++page) {\n        int     cols_required = max_column - min_column + 1;\n        uint8_t column_data[cols_required];\n        memset(column_data, 0, cols_required);\n        for (int y = min_column; y <= max_column; ++y) {\n            uint16_t data_offset = cols_required - 1 - (y - min_column);\n            for (int x = 0; x < 8; ++x) {\n                uint32_t pixel_num   = y * driver->panel_height + ((page * 8) + x);\n                uint32_t byte_offset = pixel_num / 8;\n                uint8_t  bit_offset  = pixel_num % 8;\n                column_data[data_offset] |= ((framebuffer[byte_offset] & (1 << bit_offset)) >> bit_offset) << x;\n            }\n        }\n\n        int actual_page  = page;\n        int start_column = num_columns - 1 - max_column;\n        qp_comms_command(device, vtable->opcodes.set_page | actual_page);\n        qp_comms_command(device, vtable->opcodes.set_column_lsb | (start_column & 0x0F));\n        qp_comms_command(device, vtable->opcodes.set_column_msb | (start_column & 0xF0) >> 4);\n        qp_comms_send(device, column_data, cols_required);\n    }\n}\n\nvoid qp_oled_panel_page_column_flush_rot180(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer) {\n    painter_driver_t *                  driver = (painter_driver_t *)device;\n    oled_panel_painter_driver_vtable_t *vtable = (oled_panel_painter_driver_vtable_t *)driver->driver_vtable;\n\n    // TODO: account for offset_x/y in base driver\n    int num_pages   = driver->panel_height / 8;\n    int num_columns = driver->panel_width;\n    int min_page    = dirty->t / 8;\n    int max_page    = dirty->b / 8;\n    int min_column  = dirty->l;\n    int max_column  = dirty->r;\n\n    for (int page = min_page; page <= max_page; ++page) {\n        int     cols_required = max_column - min_column + 1;\n        uint8_t column_data[cols_required];\n        memset(column_data, 0, cols_required);\n        for (int x = min_column; x <= max_column; ++x) {\n            uint16_t data_offset = cols_required - 1 - (x - min_column);\n            for (int y = 0; y < 8; ++y) {\n                uint32_t pixel_num   = ((page * 8) + y) * driver->panel_width + x;\n                uint32_t byte_offset = pixel_num / 8;\n                uint8_t  bit_offset  = pixel_num % 8;\n                column_data[data_offset] |= ((framebuffer[byte_offset] & (1 << bit_offset)) >> bit_offset) << (7 - y);\n            }\n        }\n\n        int actual_page  = num_pages - 1 - page;\n        int start_column = num_columns - 1 - max_column;\n        qp_comms_command(device, vtable->opcodes.set_page | actual_page);\n        qp_comms_command(device, vtable->opcodes.set_column_lsb | (start_column & 0x0F));\n        qp_comms_command(device, vtable->opcodes.set_column_msb | (start_column & 0xF0) >> 4);\n        qp_comms_send(device, column_data, cols_required);\n    }\n}\n\nvoid qp_oled_panel_page_column_flush_rot270(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer) {\n    painter_driver_t *                  driver = (painter_driver_t *)device;\n    oled_panel_painter_driver_vtable_t *vtable = (oled_panel_painter_driver_vtable_t *)driver->driver_vtable;\n\n    // TODO: account for offset_x/y in base driver\n    int num_pages  = driver->panel_height / 8;\n    int min_page   = dirty->l / 8;\n    int max_page   = dirty->r / 8;\n    int min_column = dirty->t;\n    int max_column = dirty->b;\n\n    for (int page = min_page; page <= max_page; ++page) {\n        int     cols_required = max_column - min_column + 1;\n        uint8_t column_data[cols_required];\n        memset(column_data, 0, cols_required);\n        for (int y = min_column; y <= max_column; ++y) {\n            uint16_t data_offset = y - min_column;\n            for (int x = 0; x < 8; ++x) {\n                uint32_t pixel_num   = y * driver->panel_height + ((page * 8) + x);\n                uint32_t byte_offset = pixel_num / 8;\n                uint8_t  bit_offset  = pixel_num % 8;\n                column_data[data_offset] |= ((framebuffer[byte_offset] & (1 << bit_offset)) >> bit_offset) << (7 - x);\n            }\n        }\n\n        int actual_page  = num_pages - 1 - page;\n        int start_column = min_column;\n        qp_comms_command(device, vtable->opcodes.set_page | actual_page);\n        qp_comms_command(device, vtable->opcodes.set_column_lsb | (start_column & 0x0F));\n        qp_comms_command(device, vtable->opcodes.set_column_msb | (start_column & 0xF0) >> 4);\n        qp_comms_send(device, column_data, cols_required);\n    }\n}\n"
  },
  {
    "path": "drivers/painter/oled_panel/qp_oled_panel.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"color.h\"\n#include \"qp_internal.h\"\n#include \"qp_surface_internal.h\"\n\n#ifdef QUANTUM_PAINTER_SPI_ENABLE\n#    include \"qp_comms_spi.h\"\n#endif // QUANTUM_PAINTER_SPI_ENABLE\n\n#ifdef QUANTUM_PAINTER_I2C_ENABLE\n#    include \"qp_comms_i2c.h\"\n#endif // QUANTUM_PAINTER_I2C_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Common OLED panel implementation\n\n// Driver vtable with extras\ntypedef struct oled_panel_painter_driver_vtable_t {\n    painter_driver_vtable_t base; // must be first, so it can be cast to/from the painter_driver_vtable_t* type\n\n    // Opcodes for normal display operation\n    struct {\n        uint8_t display_on;\n        uint8_t display_off;\n        uint8_t set_page;\n        uint8_t set_column_lsb;\n        uint8_t set_column_msb;\n    } opcodes;\n} oled_panel_painter_driver_vtable_t;\n\n// Device definition\ntypedef struct oled_panel_painter_device_t {\n    painter_driver_t base; // must be first, so it can be cast to/from the painter_device_t* type\n\n    union {\n#ifdef QUANTUM_PAINTER_SPI_ENABLE\n        // SPI-based configurables\n        qp_comms_spi_dc_reset_config_t spi_dc_reset_config;\n#endif // QUANTUM_PAINTER_SPI_ENABLE\n#ifdef QUANTUM_PAINTER_I2C_ENABLE\n        // I2C-based configurables\n        qp_comms_i2c_config_t i2c_config;\n#endif // QUANTUM_PAINTER_I2C_ENABLE\n    };\n\n    surface_painter_device_t surface;\n} oled_panel_painter_device_t;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Forward declarations for injecting into concrete driver vtables\n\nbool qp_oled_panel_power(painter_device_t device, bool power_on);\nbool qp_oled_panel_clear(painter_device_t device);\n\nbool qp_oled_panel_passthru_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count);\nbool qp_oled_panel_passthru_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom);\nbool qp_oled_panel_passthru_palette_convert(painter_device_t device, int16_t palette_size, qp_pixel_t *palette);\nbool qp_oled_panel_passthru_append_pixels(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices);\nbool qp_oled_panel_passthru_append_pixdata(painter_device_t device, uint8_t *target_buffer, uint32_t pixdata_offset, uint8_t pixdata_byte);\n\n// Helpers for flushing data from the dirty region to the correct location on the OLED\nvoid qp_oled_panel_page_column_flush_rot0(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer);\nvoid qp_oled_panel_page_column_flush_rot90(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer);\nvoid qp_oled_panel_page_column_flush_rot180(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer);\nvoid qp_oled_panel_page_column_flush_rot270(painter_device_t device, surface_dirty_data_t *dirty, const uint8_t *framebuffer);\n"
  },
  {
    "path": "drivers/painter/sh1106/qp_sh1106.c",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_oled_panel.h\"\n#include \"qp_sh1106.h\"\n#include \"qp_sh1106_opcodes.h\"\n#include \"qp_surface.h\"\n#include \"qp_surface_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver storage\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntypedef struct sh1106_device_t {\n    oled_panel_painter_device_t oled;\n\n    uint8_t framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(128, 64, 1)];\n} sh1106_device_t;\n\nstatic sh1106_device_t sh1106_drivers[SH1106_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter API implementations\n\n// Initialisation\n__attribute__((weak)) bool qp_sh1106_init(painter_device_t device, painter_rotation_t rotation) {\n    sh1106_device_t *driver = (sh1106_device_t *)device;\n\n    // Change the surface geometry based on the panel rotation\n    if (rotation == QP_ROTATION_90 || rotation == QP_ROTATION_270) {\n        driver->oled.surface.base.panel_width  = driver->oled.base.panel_height;\n        driver->oled.surface.base.panel_height = driver->oled.base.panel_width;\n    } else {\n        driver->oled.surface.base.panel_width  = driver->oled.base.panel_width;\n        driver->oled.surface.base.panel_height = driver->oled.base.panel_height;\n    }\n\n    // Init the internal surface\n    if (!qp_init(&driver->oled.surface.base, QP_ROTATION_0)) {\n        qp_dprintf(\"Failed to init internal surface in qp_sh1106_init\\n\");\n        return false;\n    }\n\n    // clang-format off\n    uint8_t sh1106_init_sequence[] = {\n        // Command,                 Delay,  N, Data[N]\n        SH1106_SET_MUX_RATIO,           0,  1, 0x3F,\n        SH1106_DISPLAY_OFFSET,          0,  1, 0x00,\n        SH1106_DISPLAY_START_LINE,      0,  0,\n        SH1106_SET_SEGMENT_REMAP_INV,   0,  0,\n        SH1106_COM_SCAN_DIR_DEC,        0,  0,\n        SH1106_COM_PADS_HW_CFG,         0,  1, 0x12,\n        SH1106_SET_CONTRAST,            0,  1, 0x7F,\n        SH1106_ALL_ON_RESUME,           0,  0,\n        SH1106_NON_INVERTING_DISPLAY,   0,  0,\n        SH1106_SET_OSC_DIVFREQ,         0,  1, 0x80,\n        SH1106_SET_CHARGE_PUMP,         0,  1, 0x14,\n        SH1106_DISPLAY_ON,              0,  0,\n    };\n    // clang-format on\n\n    // If the display height is anything other than the default 64 pixels, change SH1106_SET_MUX_RATIO data byte to the correct value\n    if (driver->oled.base.panel_height != 64) {\n        sh1106_init_sequence[3] = driver->oled.base.panel_height - 1;\n    }\n\n    // For 128x32 or 96x16 displays, change SH1106_COM_PADS_HW_CFG data byte from alternative (0x12) to sequential (0x02) configuration\n    if (driver->oled.base.panel_height <= 32) {\n        sh1106_init_sequence[20] = 0x02;\n    }\n\n    qp_comms_bulk_command_sequence(device, sh1106_init_sequence, sizeof(sh1106_init_sequence));\n    return true;\n}\n\n// Screen flush\nbool qp_sh1106_flush(painter_device_t device) {\n    sh1106_device_t *driver = (sh1106_device_t *)device;\n\n    if (!driver->oled.surface.dirty.is_dirty) {\n        return true;\n    }\n\n    switch (driver->oled.base.rotation) {\n        default:\n        case QP_ROTATION_0:\n            qp_oled_panel_page_column_flush_rot0(device, &driver->oled.surface.dirty, driver->framebuffer);\n            break;\n        case QP_ROTATION_90:\n            qp_oled_panel_page_column_flush_rot90(device, &driver->oled.surface.dirty, driver->framebuffer);\n            break;\n        case QP_ROTATION_180:\n            qp_oled_panel_page_column_flush_rot180(device, &driver->oled.surface.dirty, driver->framebuffer);\n            break;\n        case QP_ROTATION_270:\n            qp_oled_panel_page_column_flush_rot270(device, &driver->oled.surface.dirty, driver->framebuffer);\n            break;\n    }\n\n    // Clear the dirty area\n    qp_flush(&driver->oled.surface);\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nconst oled_panel_painter_driver_vtable_t sh1106_driver_vtable = {\n    .base =\n        {\n            .init            = qp_sh1106_init,\n            .power           = qp_oled_panel_power,\n            .clear           = qp_oled_panel_clear,\n            .flush           = qp_sh1106_flush,\n            .pixdata         = qp_oled_panel_passthru_pixdata,\n            .viewport        = qp_oled_panel_passthru_viewport,\n            .palette_convert = qp_oled_panel_passthru_palette_convert,\n            .append_pixels   = qp_oled_panel_passthru_append_pixels,\n            .append_pixdata  = qp_oled_panel_passthru_append_pixdata,\n        },\n    .opcodes =\n        {\n            .display_on     = SH1106_DISPLAY_ON,\n            .display_off    = SH1106_DISPLAY_OFF,\n            .set_page       = SH1106_PAGE_ADDR,\n            .set_column_lsb = SH1106_SETCOLUMN_LSB,\n            .set_column_msb = SH1106_SETCOLUMN_MSB,\n        },\n};\n\n#ifdef QUANTUM_PAINTER_SH1106_SPI_ENABLE\n// Factory function for creating a handle to the SH1106 device\npainter_device_t qp_sh1106_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < SH1106_NUM_DEVICES; ++i) {\n        sh1106_device_t *driver = &sh1106_drivers[i];\n        if (!driver->oled.base.driver_vtable) {\n            painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer);\n            if (!surface) {\n                return NULL;\n            }\n\n            // Setup the OLED device\n            driver->oled.base.driver_vtable         = (const painter_driver_vtable_t *)&sh1106_driver_vtable;\n            driver->oled.base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;\n            driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono\n            driver->oled.base.panel_width           = panel_width;\n            driver->oled.base.panel_height          = panel_height;\n            driver->oled.base.rotation              = QP_ROTATION_0;\n            driver->oled.base.offset_x              = 0;\n            driver->oled.base.offset_y              = 0;\n\n            // SPI and other pin configuration\n            driver->oled.base.comms_config                                   = &driver->oled.spi_dc_reset_config;\n            driver->oled.spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;\n            driver->oled.spi_dc_reset_config.spi_config.divisor              = spi_divisor;\n            driver->oled.spi_dc_reset_config.spi_config.lsb_first            = false;\n            driver->oled.spi_dc_reset_config.spi_config.mode                 = spi_mode;\n            driver->oled.spi_dc_reset_config.dc_pin                          = dc_pin;\n            driver->oled.spi_dc_reset_config.reset_pin                       = reset_pin;\n            driver->oled.spi_dc_reset_config.command_params_uses_command_pin = true;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(sh1106_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_SH1106_SPI_ENABLE\n\n#ifdef QUANTUM_PAINTER_SH1106_I2C_ENABLE\n// Factory function for creating a handle to the SH1106 device\npainter_device_t qp_sh1106_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address) {\n    for (uint32_t i = 0; i < SH1106_NUM_DEVICES; ++i) {\n        sh1106_device_t *driver = &sh1106_drivers[i];\n        if (!driver->oled.base.driver_vtable) {\n            // Instantiate the surface, intentional swap of width/high due to transpose\n            painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer);\n            if (!surface) {\n                return NULL;\n            }\n\n            // Setup the OLED device\n            driver->oled.base.driver_vtable         = (const painter_driver_vtable_t *)&sh1106_driver_vtable;\n            driver->oled.base.comms_vtable          = (const painter_comms_vtable_t *)&i2c_comms_cmddata_vtable;\n            driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono\n            driver->oled.base.panel_width           = panel_width;\n            driver->oled.base.panel_height          = panel_height;\n            driver->oled.base.rotation              = QP_ROTATION_0;\n            driver->oled.base.offset_x              = 0;\n            driver->oled.base.offset_y              = 0;\n\n            // I2C configuration\n            driver->oled.base.comms_config       = &driver->oled.i2c_config;\n            driver->oled.i2c_config.chip_address = i2c_address;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(sh1106_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_SH1106_I2C_ENABLE\n"
  },
  {
    "path": "drivers/painter/sh1106/qp_sh1106.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter SH1106 configurables (add to your keyboard's config.h)\n\n#if defined(QUANTUM_PAINTER_SH1106_SPI_ENABLE) && !defined(SH1106_NUM_SPI_DEVICES)\n/**\n * @def This controls the maximum number of SPI SH1106 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define SH1106_NUM_SPI_DEVICES 1\n#else\n#    define SH1106_NUM_SPI_DEVICES 0\n#endif\n\n#if defined(QUANTUM_PAINTER_SH1106_I2C_ENABLE) && !defined(SH1106_NUM_I2C_DEVICES)\n/**\n * @def This controls the maximum number of I2C SH1106 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define SH1106_NUM_I2C_DEVICES 1\n#else\n#    define SH1106_NUM_I2C_DEVICES 0\n#endif\n\n#define SH1106_NUM_DEVICES ((SH1106_NUM_SPI_DEVICES) + (SH1106_NUM_I2C_DEVICES))\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter SH1106 device factories\n\n#ifdef QUANTUM_PAINTER_SH1106_SPI_ENABLE\n\n/**\n * Factory method for an SH1106 SPI LCD device.\n *\n * @param panel_width[in] the width of the display in pixels (usually 128)\n * @param panel_height[in] the height of the display in pixels (usually 64)\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_sh1106_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n\n#endif // QUANTUM_PAINTER_SH1106_SPI_ENABLE\n\n#ifdef QUANTUM_PAINTER_SH1106_I2C_ENABLE\n\n/**\n * Factory method for an SH1106 I2C LCD device.\n *\n * @param panel_width[in] the width of the display in pixels (usually 128)\n * @param panel_height[in] the height of the display in pixels (usually 64)\n * @param i2c_address[in] the I2C address to use\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_sh1106_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address);\n\n#endif // QUANTUM_PAINTER_SH1106_I2C_ENABLE\n"
  },
  {
    "path": "drivers/painter/sh1106/qp_sh1106_opcodes.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#define SH1106_DISPLAY_ON 0xAF\n#define SH1106_DISPLAY_OFF 0xAE\n#define SH1106_SET_OSC_DIVFREQ 0xD5\n#define SH1106_SET_MUX_RATIO 0xA8\n#define SH1106_DISPLAY_OFFSET 0xD3\n#define SH1106_DISPLAY_START_LINE 0x40\n#define SH1106_SET_CHARGE_PUMP 0x8D\n#define SH1106_SET_SEGMENT_REMAP_NORMAL 0xA0\n#define SH1106_SET_SEGMENT_REMAP_INV 0xA1\n#define SH1106_COM_SCAN_DIR_INC 0xC0\n#define SH1106_COM_SCAN_DIR_DEC 0xC8\n#define SH1106_COM_PADS_HW_CFG 0xDA\n#define SH1106_SET_CONTRAST 0x81\n#define SH1106_SET_PRECHARGE_PERIOD 0xD9\n#define SH1106_VCOM_DESELECT_LEVEL 0xDB\n#define SH1106_ALL_ON_RESUME 0xA4\n#define SH1106_NON_INVERTING_DISPLAY 0xA6\n#define SH1106_DEACTIVATE_SCROLL 0x2E\n\n#define SH1106_SETCOLUMN_LSB 0x00\n#define SH1106_SETCOLUMN_MSB 0x10\n#define SH1106_PAGE_ADDR 0xB0\n"
  },
  {
    "path": "drivers/painter/sh1107/qp_sh1107.c",
    "content": "#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_surface_internal.h\"\n#include \"qp_oled_panel.h\"\n#include \"qp_sh1107.h\"\n#include \"qp_sh1107_opcodes.h\"\n#include \"qp_surface.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver storage\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\ntypedef struct sh1107_device_t {\n    oled_panel_painter_device_t oled;\n\n    uint8_t framebuffer[SURFACE_REQUIRED_BUFFER_BYTE_SIZE(128, 128, 1)];\n} sh1107_device_t;\n\nstatic sh1107_device_t sh1107_drivers[SH1107_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter API implementations\n\n// Initialisation\n__attribute__((weak)) bool qp_sh1107_init(painter_device_t device, painter_rotation_t rotation) {\n    sh1107_device_t *driver = (sh1107_device_t *)device;\n\n    // Change the surface geometry based on the panel rotation\n    if (rotation == QP_ROTATION_90 || rotation == QP_ROTATION_270) {\n        driver->oled.surface.base.panel_width  = driver->oled.base.panel_height;\n        driver->oled.surface.base.panel_height = driver->oled.base.panel_width;\n    } else {\n        driver->oled.surface.base.panel_width  = driver->oled.base.panel_width;\n        driver->oled.surface.base.panel_height = driver->oled.base.panel_height;\n    }\n\n    // Init the internal surface\n    if (!qp_init(&driver->oled.surface.base, QP_ROTATION_0)) {\n        qp_dprintf(\"Failed to init internal surface in qp_sh1107_init\\n\");\n        return false;\n    }\n\n    // clang-format off\n    uint8_t sh1107_init_sequence[] = {\n        // Command,                 Delay,  N, Data[N]\n        SH1107_SET_MUX_RATIO,           0,  1, 0x7F,      // 1/128 duty\n        SH1107_DISPLAY_OFFSET,          0,  1, 0x00,\n        SH1107_SET_START_LINE,          0,  1, 0x00,      // Different from SH1106\n        SH1107_SET_SEGMENT_REMAP_INV,   0,  0,\n        SH1107_COM_SCAN_DIR_DEC,        0,  0,\n        SH1107_COM_PADS_HW_CFG,         0,  1, 0x12,\n        SH1107_SET_CONTRAST,            0,  1, 0x7F,\n        SH1107_ALL_ON_RESUME,           0,  0,\n        SH1107_NON_INVERTING_DISPLAY,   0,  0,\n        SH1107_SET_OSC_DIVFREQ,         0,  1, 0x80,\n        SH1107_SET_CHARGE_PUMP,         0,  1, 0x14,\n        SH1107_DISPLAY_ON,              0,  0,\n    };\n    // clang-format on\n\n    // If the display width is anything other than the default 128 pixels, change SH1107_SET_MUX_RATIO data byte to the correct value.\n    if (driver->oled.base.panel_width != 128) {\n        sh1107_init_sequence[3] = driver->oled.base.panel_width - 1;\n    }\n\n    // If the display width is less than the default 128 pixels, change SH1107_DISPLAY_OFFSET to use the center columns.\n    if (driver->oled.base.panel_width < 128) {\n        sh1107_init_sequence[7] = (128U - driver->oled.base.panel_width) / 2;\n    }\n\n    // For smaller displays, change SH1107_COM_PADS_HW_CFG data byte from alternative (0x12) to sequential (0x02) configuration\n    if (driver->oled.base.panel_height <= 64) {\n        sh1107_init_sequence[20] = 0x02;\n    }\n\n    qp_comms_bulk_command_sequence(device, sh1107_init_sequence, sizeof(sh1107_init_sequence));\n    return true;\n}\n\n// Screen flush\nbool qp_sh1107_flush(painter_device_t device) {\n    sh1107_device_t *driver = (sh1107_device_t *)device;\n\n    if (!driver->oled.surface.dirty.is_dirty) {\n        return true;\n    }\n\n    switch (driver->oled.base.rotation) {\n        default:\n        case QP_ROTATION_0:\n            qp_oled_panel_page_column_flush_rot0(device, &driver->oled.surface.dirty, driver->framebuffer);\n            break;\n        case QP_ROTATION_90:\n            qp_oled_panel_page_column_flush_rot90(device, &driver->oled.surface.dirty, driver->framebuffer);\n            break;\n        case QP_ROTATION_180:\n            qp_oled_panel_page_column_flush_rot180(device, &driver->oled.surface.dirty, driver->framebuffer);\n            break;\n        case QP_ROTATION_270:\n            qp_oled_panel_page_column_flush_rot270(device, &driver->oled.surface.dirty, driver->framebuffer);\n            break;\n    }\n\n    // Clear the dirty area\n    qp_flush(&driver->oled.surface);\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nconst oled_panel_painter_driver_vtable_t sh1107_driver_vtable = {\n    .base =\n        {\n            .init            = qp_sh1107_init,\n            .power           = qp_oled_panel_power,\n            .clear           = qp_oled_panel_clear,\n            .flush           = qp_sh1107_flush,\n            .pixdata         = qp_oled_panel_passthru_pixdata,\n            .viewport        = qp_oled_panel_passthru_viewport,\n            .palette_convert = qp_oled_panel_passthru_palette_convert,\n            .append_pixels   = qp_oled_panel_passthru_append_pixels,\n            .append_pixdata  = qp_oled_panel_passthru_append_pixdata,\n        },\n    .opcodes =\n        {\n            .display_on     = SH1107_DISPLAY_ON,\n            .display_off    = SH1107_DISPLAY_OFF,\n            .set_page       = SH1107_PAGE_ADDR,\n            .set_column_lsb = SH1107_SETCOLUMN_LSB,\n            .set_column_msb = SH1107_SETCOLUMN_MSB,\n        },\n};\n\n#ifdef QUANTUM_PAINTER_SH1107_SPI_ENABLE\n// Factory function for creating a handle to the SH1107 device\npainter_device_t qp_sh1107_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < SH1107_NUM_DEVICES; ++i) {\n        sh1107_device_t *driver = &sh1107_drivers[i];\n        if (!driver->oled.base.driver_vtable) {\n            painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer);\n            if (!surface) {\n                return NULL;\n            }\n\n            // Setup the OLED device\n            driver->oled.base.driver_vtable         = (const painter_driver_vtable_t *)&sh1107_driver_vtable;\n            driver->oled.base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;\n            driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono\n            driver->oled.base.panel_width           = panel_width;\n            driver->oled.base.panel_height          = panel_height;\n            driver->oled.base.rotation              = QP_ROTATION_0;\n            driver->oled.base.offset_x              = 0;\n            driver->oled.base.offset_y              = 0;\n\n            // SPI and other pin configuration\n            driver->oled.base.comms_config                                   = &driver->oled.spi_dc_reset_config;\n            driver->oled.spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;\n            driver->oled.spi_dc_reset_config.spi_config.divisor              = spi_divisor;\n            driver->oled.spi_dc_reset_config.spi_config.lsb_first            = false;\n            driver->oled.spi_dc_reset_config.spi_config.mode                 = spi_mode;\n            driver->oled.spi_dc_reset_config.dc_pin                          = dc_pin;\n            driver->oled.spi_dc_reset_config.reset_pin                       = reset_pin;\n            driver->oled.spi_dc_reset_config.command_params_uses_command_pin = true;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(sh1107_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_SH1107_SPI_ENABLE\n\n#ifdef QUANTUM_PAINTER_SH1107_I2C_ENABLE\n// Factory function for creating a handle to the SH1107 device\npainter_device_t qp_sh1107_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address) {\n    for (uint32_t i = 0; i < SH1107_NUM_DEVICES; ++i) {\n        sh1107_device_t *driver = &sh1107_drivers[i];\n        if (!driver->oled.base.driver_vtable) {\n            // Instantiate the surface\n            painter_device_t surface = qp_make_mono1bpp_surface_advanced(&driver->oled.surface, 1, panel_width, panel_height, driver->framebuffer);\n            if (!surface) {\n                return NULL;\n            }\n\n            // Setup the OLED device\n            driver->oled.base.driver_vtable         = (const painter_driver_vtable_t *)&sh1107_driver_vtable;\n            driver->oled.base.comms_vtable          = (const painter_comms_vtable_t *)&i2c_comms_cmddata_vtable;\n            driver->oled.base.native_bits_per_pixel = 1; // 1bpp mono\n            driver->oled.base.panel_width           = panel_width;\n            driver->oled.base.panel_height          = panel_height;\n            driver->oled.base.rotation              = QP_ROTATION_0;\n            driver->oled.base.offset_x              = 0;\n            driver->oled.base.offset_y              = 0;\n\n            // I2C configuration\n            driver->oled.base.comms_config       = &driver->oled.i2c_config;\n            driver->oled.i2c_config.chip_address = i2c_address;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(sh1107_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_SH1107_I2C_ENABLE\n"
  },
  {
    "path": "drivers/painter/sh1107/qp_sh1107.h",
    "content": "#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter SH1107 configurables (add to your keyboard's config.h)\n\n#if defined(QUANTUM_PAINTER_SH1107_SPI_ENABLE) && !defined(SH1107_NUM_SPI_DEVICES)\n/**\n * @def This controls the maximum number of SPI SH1107 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define SH1107_NUM_SPI_DEVICES 1\n#else\n#    define SH1107_NUM_SPI_DEVICES 0\n#endif\n\n#if defined(QUANTUM_PAINTER_SH1107_I2C_ENABLE) && !defined(SH1107_NUM_I2C_DEVICES)\n/**\n * @def This controls the maximum number of I2C SH1107 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define SH1107_NUM_I2C_DEVICES 1\n#else\n#    define SH1107_NUM_I2C_DEVICES 0\n#endif\n\n#define SH1107_NUM_DEVICES ((SH1107_NUM_SPI_DEVICES) + (SH1107_NUM_I2C_DEVICES))\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter SH1107 device factories\n\n#ifdef QUANTUM_PAINTER_SH1107_SPI_ENABLE\n\n/**\n * Factory method for an SH1107 SPI LCD device.\n *\n * @param panel_width[in] the width of the display in pixels (usually 64)\n * @param panel_height[in] the height of the display in pixels (usually 128)\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_sh1107_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n\n#endif // QUANTUM_PAINTER_SH1107_SPI_ENABLE\n\n#ifdef QUANTUM_PAINTER_SH1107_I2C_ENABLE\n\n/**\n * Factory method for an SH1107 I2C LCD device.\n *\n * @param panel_width[in] the width of the display in pixels (usually 64)\n * @param panel_height[in] the height of the display in pixels (usually 128)\n * @param i2c_address[in] the I2C address to use\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_sh1107_make_i2c_device(uint16_t panel_width, uint16_t panel_height, uint8_t i2c_address);\n\n#endif // QUANTUM_PAINTER_SH1107_I2C_ENABLE\n"
  },
  {
    "path": "drivers/painter/sh1107/qp_sh1107_opcodes.h",
    "content": "// Copyright 2024 Steve Branam (@smbranam)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#define SH1107_DISPLAY_ON 0xAF\n#define SH1107_DISPLAY_OFF 0xAE\n#define SH1107_SET_OSC_DIVFREQ 0xD5\n#define SH1107_SET_MUX_RATIO 0xA8\n#define SH1107_DISPLAY_OFFSET 0xD3\n#define SH1107_SET_START_LINE 0xDC // Key/sole difference from SH1106 (which uses 0x40)\n#define SH1107_SET_CHARGE_PUMP 0x8D\n#define SH1107_SET_SEGMENT_REMAP_NORMAL 0xA0\n#define SH1107_SET_SEGMENT_REMAP_INV 0xA1\n#define SH1107_COM_SCAN_DIR_INC 0xC0\n#define SH1107_COM_SCAN_DIR_DEC 0xC8\n#define SH1107_COM_PADS_HW_CFG 0xDA\n#define SH1107_SET_CONTRAST 0x81\n#define SH1107_SET_PRECHARGE_PERIOD 0xD9\n#define SH1107_VCOM_DESELECT_LEVEL 0xDB\n#define SH1107_ALL_ON_RESUME 0xA4\n#define SH1107_NON_INVERTING_DISPLAY 0xA6\n#define SH1107_DEACTIVATE_SCROLL 0x2E\n#define SH1107_SETCOLUMN_LSB 0x00\n#define SH1107_SETCOLUMN_MSB 0x10\n#define SH1107_PAGE_ADDR 0xB0\n"
  },
  {
    "path": "drivers/painter/ssd1351/qp_ssd1351.c",
    "content": "// Copyright 2021-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_ssd1351.h\"\n#include \"qp_ssd1351_opcodes.h\"\n#include \"qp_tft_panel.h\"\n\n#ifdef QUANTUM_PAINTER_SSD1351_SPI_ENABLE\n#    include \"qp_comms_spi.h\"\n#endif // QUANTUM_PAINTER_SSD1351_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Common\n\n// Driver storage\ntft_panel_dc_reset_painter_device_t ssd1351_drivers[SSD1351_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Initialization\n\n__attribute__((weak)) bool qp_ssd1351_init(painter_device_t device, painter_rotation_t rotation) {\n    tft_panel_dc_reset_painter_device_t *driver = (tft_panel_dc_reset_painter_device_t *)device;\n\n    // clang-format off\n    const uint8_t ssd1351_init_sequence[] = {\n        // Command,                 Delay, N, Data[N]\n        SSD1351_COMMANDLOCK,           5,  1, 0x12,\n        SSD1351_COMMANDLOCK,           5,  1, 0xB1,\n        SSD1351_DISPLAYOFF,            5,  0,\n        SSD1351_CLOCKDIV,              5,  1, 0xF1,\n        SSD1351_MUXRATIO,              5,  1, 0x7F,\n        SSD1351_DISPLAYOFFSET,         5,  1, 0x00,\n        SSD1351_SETGPIO,               5,  1, 0x00,\n        SSD1351_FUNCTIONSELECT,        5,  1, 0x01,\n        SSD1351_PRECHARGE,             5,  1, 0x32,\n        SSD1351_VCOMH,                 5,  1, 0x05,\n        SSD1351_NORMALDISPLAY,         5,  0,\n        SSD1351_CONTRASTABC,           5,  3, 0xC8, 0x80, 0xC8,\n        SSD1351_CONTRASTMASTER,        5,  1, 0x0F,\n        SSD1351_SETVSL,                5,  3, 0xA0, 0xB5, 0x55,\n        SSD1351_PRECHARGE2,            5,  1, 0x01,\n        SSD1351_DISPLAYON,             5,  0,\n    };\n    // clang-format on\n    qp_comms_bulk_command_sequence(device, ssd1351_init_sequence, sizeof(ssd1351_init_sequence));\n\n    // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)\n    const uint8_t madctl[] = {\n        [QP_ROTATION_0]   = SSD1351_MADCTL_BGR | SSD1351_MADCTL_MY,\n        [QP_ROTATION_90]  = SSD1351_MADCTL_BGR | SSD1351_MADCTL_MX | SSD1351_MADCTL_MY | SSD1351_MADCTL_MV,\n        [QP_ROTATION_180] = SSD1351_MADCTL_BGR | SSD1351_MADCTL_MX,\n        [QP_ROTATION_270] = SSD1351_MADCTL_BGR | SSD1351_MADCTL_MV,\n    };\n    qp_comms_command_databyte(device, SSD1351_SETREMAP, madctl[rotation]);\n    qp_comms_command_databyte(device, SSD1351_STARTLINE, (rotation == QP_ROTATION_0 || rotation == QP_ROTATION_90) ? driver->base.panel_height : 0);\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n\nconst tft_panel_dc_reset_painter_driver_vtable_t ssd1351_driver_vtable = {\n    .base =\n        {\n            .init            = qp_ssd1351_init,\n            .power           = qp_tft_panel_power,\n            .clear           = qp_tft_panel_clear,\n            .flush           = qp_tft_panel_flush,\n            .pixdata         = qp_tft_panel_pixdata,\n            .viewport        = qp_tft_panel_viewport,\n            .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,\n            .append_pixels   = qp_tft_panel_append_pixels_rgb565,\n            .append_pixdata  = qp_tft_panel_append_pixdata,\n        },\n    .num_window_bytes   = 1,\n    .swap_window_coords = true,\n    .opcodes =\n        {\n            .display_on         = SSD1351_DISPLAYON,\n            .display_off        = SSD1351_DISPLAYOFF,\n            .set_column_address = SSD1351_SETCOLUMN,\n            .set_row_address    = SSD1351_SETROW,\n            .enable_writes      = SSD1351_WRITERAM,\n        },\n};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SPI\n\n#ifdef QUANTUM_PAINTER_SSD1351_SPI_ENABLE\n\n// Factory function for creating a handle to the SSD1351 device\npainter_device_t qp_ssd1351_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < SSD1351_NUM_DEVICES; ++i) {\n        tft_panel_dc_reset_painter_device_t *driver = &ssd1351_drivers[i];\n        if (!driver->base.driver_vtable) {\n            driver->base.driver_vtable         = (const painter_driver_vtable_t *)&ssd1351_driver_vtable;\n            driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;\n            driver->base.panel_width           = panel_width;\n            driver->base.panel_height          = panel_height;\n            driver->base.rotation              = QP_ROTATION_0;\n            driver->base.offset_x              = 0;\n            driver->base.offset_y              = 0;\n            driver->base.native_bits_per_pixel = 16; // RGB565\n\n            // SPI and other pin configuration\n            driver->base.comms_config                                   = &driver->spi_dc_reset_config;\n            driver->spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;\n            driver->spi_dc_reset_config.spi_config.divisor              = spi_divisor;\n            driver->spi_dc_reset_config.spi_config.lsb_first            = false;\n            driver->spi_dc_reset_config.spi_config.mode                 = spi_mode;\n            driver->spi_dc_reset_config.dc_pin                          = dc_pin;\n            driver->spi_dc_reset_config.reset_pin                       = reset_pin;\n            driver->spi_dc_reset_config.command_params_uses_command_pin = false;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_SSD1351_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "drivers/painter/ssd1351/qp_ssd1351.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter SSD1351 configurables (add to your keyboard's config.h)\n\n#ifndef SSD1351_NUM_DEVICES\n/**\n * @def This controls the maximum number of SSD1351 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define SSD1351_NUM_DEVICES 1\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter SSD1351 device factories\n\n#ifdef QUANTUM_PAINTER_SSD1351_SPI_ENABLE\n/**\n * Factory method for an SSD1351 SPI OLED device.\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_ssd1351_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n#endif // QUANTUM_PAINTER_SSD1351_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/ssd1351/qp_ssd1351_opcodes.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter SSD1351 command opcodes\n\n// System function commands\n#define SSD1351_SETCOLUMN 0x15\n#define SSD1351_SETROW 0x75\n#define SSD1351_WRITERAM 0x5C\n#define SSD1351_READRAM 0x5D\n#define SSD1351_SETREMAP 0xA0\n#define SSD1351_STARTLINE 0xA1\n#define SSD1351_DISPLAYOFFSET 0xA2\n#define SSD1351_DISPLAYALLOFF 0xA4\n#define SSD1351_DISPLAYALLON 0xA5\n#define SSD1351_NORMALDISPLAY 0xA6\n#define SSD1351_INVERTDISPLAY 0xA7\n#define SSD1351_FUNCTIONSELECT 0xAB\n#define SSD1351_DISPLAYOFF 0xAE\n#define SSD1351_DISPLAYON 0xAF\n#define SSD1351_PRECHARGE 0xB1\n#define SSD1351_DISPLAYENHANCE 0xB2\n#define SSD1351_CLOCKDIV 0xB3\n#define SSD1351_SETVSL 0xB4\n#define SSD1351_SETGPIO 0xB5\n#define SSD1351_PRECHARGE2 0xB6\n#define SSD1351_SETGRAY 0xB8\n#define SSD1351_USELUT 0xB9\n#define SSD1351_PRECHARGELEVEL 0xBB\n#define SSD1351_VCOMH 0xBE\n#define SSD1351_CONTRASTABC 0xC1\n#define SSD1351_CONTRASTMASTER 0xC7\n#define SSD1351_MUXRATIO 0xCA\n#define SSD1351_COMMANDLOCK 0xFD\n#define SSD1351_HORIZSCROLL 0x96\n#define SSD1351_STOPSCROLL 0x9E\n#define SSD1351_STARTSCROLL 0x9F\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SETREMAP (MADCTL) Flags\n#define SSD1351_MADCTL_MY 0b00010000\n#define SSD1351_MADCTL_MX 0b00000010\n#define SSD1351_MADCTL_MV 0b00000001\n#define SSD1351_MADCTL_RGB 0b01100000\n#define SSD1351_MADCTL_BGR 0b01100100\n"
  },
  {
    "path": "drivers/painter/st77xx/qp_st7735.c",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// Copyright 2021-2023 Nick Brassel (@tzarc)\n// Copyright 2022 David Hoelscher (@customMK)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_st7735.h\"\n#include \"qp_st77xx_opcodes.h\"\n#include \"qp_st7735_opcodes.h\"\n#include \"qp_tft_panel.h\"\n\n#ifdef QUANTUM_PAINTER_ST7735_SPI_ENABLE\n#    include \"qp_comms_spi.h\"\n#endif // QUANTUM_PAINTER_ST7735_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Common\n\n// Driver storage\ntft_panel_dc_reset_painter_device_t st7735_drivers[ST7735_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Automatic viewport offsets\n\n#ifndef ST7735_NO_AUTOMATIC_OFFSETS\nstatic inline void st7735_automatic_viewport_offsets(painter_device_t device, painter_rotation_t rotation) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    // clang-format off\n    const struct {\n        uint16_t offset_x;\n        uint16_t offset_y;\n    } rotation_offsets_80x160[] = {\n        [QP_ROTATION_0]   = { .offset_x = 24, .offset_y =  0 },\n        [QP_ROTATION_90]  = { .offset_x =  0, .offset_y = 24 },\n        [QP_ROTATION_180] = { .offset_x = 24, .offset_y =  0 },\n        [QP_ROTATION_270] = { .offset_x =  0, .offset_y = 24 },\n    };\n    // clang-format on\n\n    if (driver->panel_width == 80 && driver->panel_height == 160) {\n        driver->offset_x = rotation_offsets_80x160[rotation].offset_x;\n        driver->offset_y = rotation_offsets_80x160[rotation].offset_y;\n    }\n}\n#endif // ST7735_NO_AUTOMATIC_OFFSETS\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Initialization\n\n__attribute__((weak)) bool qp_st7735_init(painter_device_t device, painter_rotation_t rotation) {\n    // clang-format off\n    const uint8_t st7735_init_sequence[] = {\n        // Command,                 Delay, N, Data[N]\n        ST77XX_CMD_RESET,            120,  0,\n        ST77XX_CMD_SLEEP_OFF,          5,  0,\n        ST77XX_SET_PIX_FMT,            0,  1, 0x55,\n        ST77XX_CMD_INVERT_OFF,         0,  0,\n        ST77XX_CMD_NORMAL_ON,          0,  0,\n        ST7735_SET_PGAMMA,             0, 16, 0x02, 0x1C, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2D, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10,\n        ST7735_SET_NGAMMA,             0, 16, 0x03, 0x1D, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10,\n        ST77XX_CMD_DISPLAY_ON,        20,  0\n    };\n    // clang-format on\n    qp_comms_bulk_command_sequence(device, st7735_init_sequence, sizeof(st7735_init_sequence));\n\n    // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)\n    const uint8_t madctl[] = {\n        [QP_ROTATION_0]   = ST77XX_MADCTL_BGR,\n        [QP_ROTATION_90]  = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MX | ST77XX_MADCTL_MV,\n        [QP_ROTATION_180] = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MX | ST77XX_MADCTL_MY,\n        [QP_ROTATION_270] = ST77XX_MADCTL_BGR | ST77XX_MADCTL_MV | ST77XX_MADCTL_MY,\n    };\n    qp_comms_command_databyte(device, ST77XX_SET_MADCTL, madctl[rotation]);\n\n#ifndef ST7735_NO_AUTOMATIC_VIEWPORT_OFFSETS\n    st7735_automatic_viewport_offsets(device, rotation);\n#endif // ST7735_NO_AUTOMATIC_VIEWPORT_OFFSETS\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n\nconst tft_panel_dc_reset_painter_driver_vtable_t st7735_driver_vtable = {\n    .base =\n        {\n            .init            = qp_st7735_init,\n            .power           = qp_tft_panel_power,\n            .clear           = qp_tft_panel_clear,\n            .flush           = qp_tft_panel_flush,\n            .pixdata         = qp_tft_panel_pixdata,\n            .viewport        = qp_tft_panel_viewport,\n            .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,\n            .append_pixels   = qp_tft_panel_append_pixels_rgb565,\n            .append_pixdata  = qp_tft_panel_append_pixdata,\n        },\n    .num_window_bytes   = 2,\n    .swap_window_coords = false,\n    .opcodes =\n        {\n            .display_on         = ST77XX_CMD_DISPLAY_ON,\n            .display_off        = ST77XX_CMD_DISPLAY_OFF,\n            .set_column_address = ST77XX_SET_COL_ADDR,\n            .set_row_address    = ST77XX_SET_ROW_ADDR,\n            .enable_writes      = ST77XX_SET_MEM,\n        },\n};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SPI\n\n#ifdef QUANTUM_PAINTER_ST7735_SPI_ENABLE\n\n// Factory function for creating a handle to the ST7735 device\npainter_device_t qp_st7735_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < ST7735_NUM_DEVICES; ++i) {\n        tft_panel_dc_reset_painter_device_t *driver = &st7735_drivers[i];\n        if (!driver->base.driver_vtable) {\n            driver->base.driver_vtable         = (const painter_driver_vtable_t *)&st7735_driver_vtable;\n            driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;\n            driver->base.panel_width           = panel_width;\n            driver->base.panel_height          = panel_height;\n            driver->base.rotation              = QP_ROTATION_0;\n            driver->base.offset_x              = 0;\n            driver->base.offset_y              = 0;\n            driver->base.native_bits_per_pixel = 16; // RGB565\n\n            // SPI and other pin configuration\n            driver->base.comms_config                                   = &driver->spi_dc_reset_config;\n            driver->spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;\n            driver->spi_dc_reset_config.spi_config.divisor              = spi_divisor;\n            driver->spi_dc_reset_config.spi_config.lsb_first            = false;\n            driver->spi_dc_reset_config.spi_config.mode                 = spi_mode;\n            driver->spi_dc_reset_config.dc_pin                          = dc_pin;\n            driver->spi_dc_reset_config.reset_pin                       = reset_pin;\n            driver->spi_dc_reset_config.command_params_uses_command_pin = false;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_ST7735_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "drivers/painter/st77xx/qp_st7735.h",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// Copyright 2021 Nick Brassel (@tzarc)\n// Copyright 2022 David Hoelscher (@customMK)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ST7735 configurables (add to your keyboard's config.h)\n\n#ifndef ST7735_NUM_DEVICES\n/**\n * @def This controls the maximum number of ST7735 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define ST7735_NUM_DEVICES 1\n#endif\n\n// Additional configuration options to be copied to your keyboard's config.h (don't change here):\n\n// If you know exactly which offsets should be used on your panel with respect to selected rotation, then this config\n// option allows you to save some flash space -- you'll need to invoke qp_set_viewport_offsets() instead from your keyboard.\n//       #define ST7735_NO_AUTOMATIC_VIEWPORT_OFFSETS\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ST7735 device factories\n\n#ifdef QUANTUM_PAINTER_ST7735_SPI_ENABLE\n/**\n * Factory method for an ST7735 SPI LCD device.\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_st7735_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n#endif // QUANTUM_PAINTER_ST7735_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/st77xx/qp_st7735_opcodes.h",
    "content": "// Copyright 2022 David Hoelscher (@customMK)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ST7735 additional command opcodes\n\n// Panel Function Commands\n#define ST7735_SET_FRAME_RATE_CTL_1 0xB1 // Set frame rate control 1\n#define ST7735_SET_FRAME_RATE_CTL_2 0xB2 // Set frame rate control 2\n#define ST7735_SET_FRAME_RATE_CTL_3 0xB3 // Set frame rate control 3\n#define ST7735_SET_INVERSION_CTL 0xB4    // Set inversion mode control\n#define ST7735_SET_DISPLAY_CTL 0xB6      // Set display control 5\n#define ST7735_SET_POWER_CTL_1 0xC0      // Set GVDD\n#define ST7735_SET_POWER_CTL_2 0xC1      // Set VGH and VGL\n#define ST7735_SET_POWER_CTL_3 0xC2      // Set normal mode op amp current\n#define ST7735_SET_POWER_CTL_4 0xC3      // Set idle mode op amp current\n#define ST7735_SET_POWER_CTL_5 0xC4      // Set partial mode op amp current\n#define ST7735_SET_VCOM_CTL 0xC5         // Set VCOM voltages\n#define ST7735_SET_VCOM_OFFSET_CTL 0xC7  // Set VCOM offset ctl\n#define ST7735_SET_LCD_ID 0xD1           // Set LCD module version\n#define ST7735_SET_PROJECT_ID 0xD2       // Set product project ID\n#define ST7735_SET_POWER_CTL_6 0xFC      // Set partial+idle op amp current\n#define ST7735_SET_NVMEM_CTL_STATUS 0xD9 // EEPROM Control Status\n#define ST7735_SET_NVMEM_READ_CMD 0xCC   // EEPROM Read Command\n#define ST7735_SET_NVMEM_WRITE_CMD 0xDF  // EEPROM Write Command\n#define ST7735_SET_PGAMMA 0xE0           // Set positive gamma\n#define ST7735_SET_NGAMMA 0xE1           // Set negative gamma\n#define ST7735_SET_EXTENSION_ENABLE 0xF0 // Enable extension command\n#define ST7735_SET_VCOM_DELAY 0xFF       // Set VCOM delay time\n"
  },
  {
    "path": "drivers/painter/st77xx/qp_st7789.c",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// Copyright 2021-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_st7789.h\"\n#include \"qp_st77xx_opcodes.h\"\n#include \"qp_st7789_opcodes.h\"\n#include \"qp_tft_panel.h\"\n\n#ifdef QUANTUM_PAINTER_ST7789_SPI_ENABLE\n#    include \"qp_comms_spi.h\"\n#endif // QUANTUM_PAINTER_ST7789_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Common\n\n// Driver storage\ntft_panel_dc_reset_painter_device_t st7789_drivers[ST7789_NUM_DEVICES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Automatic viewport offsets\n\n#ifndef ST7789_NO_AUTOMATIC_OFFSETS\nstatic inline void st7789_automatic_viewport_offsets(painter_device_t device, painter_rotation_t rotation) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    // clang-format off\n    const struct {\n        uint16_t offset_x;\n        uint16_t offset_y;\n    } rotation_offsets_240x240[] = {\n        [QP_ROTATION_0]   = { .offset_x =  0, .offset_y =  0 },\n        [QP_ROTATION_90]  = { .offset_x =  0, .offset_y =  0 },\n        [QP_ROTATION_180] = { .offset_x =  0, .offset_y = 80 },\n        [QP_ROTATION_270] = { .offset_x = 80, .offset_y =  0 },\n    };\n    // clang-format on\n\n    if (driver->panel_width == 240 && driver->panel_height == 240) {\n        driver->offset_x = rotation_offsets_240x240[rotation].offset_x;\n        driver->offset_y = rotation_offsets_240x240[rotation].offset_y;\n    }\n}\n#endif // ST7789_NO_AUTOMATIC_OFFSETS\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Initialization\n\n__attribute__((weak)) bool qp_st7789_init(painter_device_t device, painter_rotation_t rotation) {\n    // clang-format off\n    const uint8_t st7789_init_sequence[] = {\n        // Command,                 Delay, N, Data[N]\n        ST77XX_CMD_RESET,            120,  0,\n        ST77XX_CMD_SLEEP_OFF,          5,  0,\n        ST77XX_SET_PIX_FMT,            0,  1, 0x55,\n        ST77XX_CMD_INVERT_ON,          0,  0,\n        ST77XX_CMD_NORMAL_ON,          0,  0,\n        ST77XX_CMD_DISPLAY_ON,        20,  0\n    };\n    // clang-format on\n    qp_comms_bulk_command_sequence(device, st7789_init_sequence, sizeof(st7789_init_sequence));\n\n    // Configure the rotation (i.e. the ordering and direction of memory writes in GRAM)\n    const uint8_t madctl[] = {\n        [QP_ROTATION_0]   = ST77XX_MADCTL_RGB,\n        [QP_ROTATION_90]  = ST77XX_MADCTL_RGB | ST77XX_MADCTL_MX | ST77XX_MADCTL_MV,\n        [QP_ROTATION_180] = ST77XX_MADCTL_RGB | ST77XX_MADCTL_MX | ST77XX_MADCTL_MY,\n        [QP_ROTATION_270] = ST77XX_MADCTL_RGB | ST77XX_MADCTL_MV | ST77XX_MADCTL_MY,\n    };\n    qp_comms_command_databyte(device, ST77XX_SET_MADCTL, madctl[rotation]);\n\n#ifndef ST7789_NO_AUTOMATIC_VIEWPORT_OFFSETS\n    st7789_automatic_viewport_offsets(device, rotation);\n#endif // ST7789_NO_AUTOMATIC_VIEWPORT_OFFSETS\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver vtable\n\nconst tft_panel_dc_reset_painter_driver_vtable_t st7789_driver_vtable = {\n    .base =\n        {\n            .init            = qp_st7789_init,\n            .power           = qp_tft_panel_power,\n            .clear           = qp_tft_panel_clear,\n            .flush           = qp_tft_panel_flush,\n            .pixdata         = qp_tft_panel_pixdata,\n            .viewport        = qp_tft_panel_viewport,\n            .palette_convert = qp_tft_panel_palette_convert_rgb565_swapped,\n            .append_pixels   = qp_tft_panel_append_pixels_rgb565,\n            .append_pixdata  = qp_tft_panel_append_pixdata,\n        },\n    .num_window_bytes   = 2,\n    .swap_window_coords = false,\n    .opcodes =\n        {\n            .display_on         = ST77XX_CMD_DISPLAY_ON,\n            .display_off        = ST77XX_CMD_DISPLAY_OFF,\n            .set_column_address = ST77XX_SET_COL_ADDR,\n            .set_row_address    = ST77XX_SET_ROW_ADDR,\n            .enable_writes      = ST77XX_SET_MEM,\n        },\n};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// SPI\n\n#ifdef QUANTUM_PAINTER_ST7789_SPI_ENABLE\n\n// Factory function for creating a handle to the ST7789 device\npainter_device_t qp_st7789_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode) {\n    for (uint32_t i = 0; i < ST7789_NUM_DEVICES; ++i) {\n        tft_panel_dc_reset_painter_device_t *driver = &st7789_drivers[i];\n        if (!driver->base.driver_vtable) {\n            driver->base.driver_vtable         = (const painter_driver_vtable_t *)&st7789_driver_vtable;\n            driver->base.comms_vtable          = (const painter_comms_vtable_t *)&spi_comms_with_dc_vtable;\n            driver->base.panel_width           = panel_width;\n            driver->base.panel_height          = panel_height;\n            driver->base.rotation              = QP_ROTATION_0;\n            driver->base.offset_x              = 0;\n            driver->base.offset_y              = 0;\n            driver->base.native_bits_per_pixel = 16; // RGB565\n\n            // SPI and other pin configuration\n            driver->base.comms_config                                   = &driver->spi_dc_reset_config;\n            driver->spi_dc_reset_config.spi_config.chip_select_pin      = chip_select_pin;\n            driver->spi_dc_reset_config.spi_config.divisor              = spi_divisor;\n            driver->spi_dc_reset_config.spi_config.lsb_first            = false;\n            driver->spi_dc_reset_config.spi_config.mode                 = spi_mode;\n            driver->spi_dc_reset_config.dc_pin                          = dc_pin;\n            driver->spi_dc_reset_config.reset_pin                       = reset_pin;\n            driver->spi_dc_reset_config.command_params_uses_command_pin = false;\n\n            if (!qp_internal_register_device((painter_device_t)driver)) {\n                memset(driver, 0, sizeof(tft_panel_dc_reset_painter_device_t));\n                return NULL;\n            }\n\n            return (painter_device_t)driver;\n        }\n    }\n    return NULL;\n}\n\n#endif // QUANTUM_PAINTER_ST7789_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "drivers/painter/st77xx/qp_st7789.h",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"gpio.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ST7789 configurables (add to your keyboard's config.h)\n\n#ifndef ST7789_NUM_DEVICES\n/**\n * @def This controls the maximum number of ST7789 devices that Quantum Painter can communicate with at any one time.\n *      Increasing this number allows for multiple displays to be used.\n */\n#    define ST7789_NUM_DEVICES 1\n#endif\n\n// Additional configuration options to be copied to your keyboard's config.h (don't change here):\n\n// If you know exactly which offsets should be used on your panel with respect to selected rotation, then this config\n// option allows you to save some flash space -- you'll need to invoke qp_set_viewport_offsets() instead from your keyboard.\n//       #define ST7789_NO_AUTOMATIC_VIEWPORT_OFFSETS\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ST7789 device factories\n\n#ifdef QUANTUM_PAINTER_ST7789_SPI_ENABLE\n/**\n * Factory method for an ST7789 SPI LCD device.\n *\n * @param panel_width[in] the width of the display panel\n * @param panel_height[in] the height of the display panel\n * @param chip_select_pin[in] the GPIO pin used for SPI chip select\n * @param dc_pin[in] the GPIO pin used for D/C control\n * @param reset_pin[in] the GPIO pin used for RST\n * @param spi_divisor[in] the SPI divisor to use when communicating with the display\n * @param spi_mode[in] the SPI mode to use when communicating with the display\n * @return the device handle used with all drawing routines in Quantum Painter\n */\npainter_device_t qp_st7789_make_spi_device(uint16_t panel_width, uint16_t panel_height, pin_t chip_select_pin, pin_t dc_pin, pin_t reset_pin, uint16_t spi_divisor, int spi_mode);\n#endif // QUANTUM_PAINTER_ST7789_SPI_ENABLE\n"
  },
  {
    "path": "drivers/painter/st77xx/qp_st7789_opcodes.h",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ST7789 additional command opcodes\n\n// System function commands\n#define ST7789_GET_SELF_DIAG 0x0F      // Get self-diagnostic result\n#define ST7789_SET_VERT_SCRL 0x33      // Set vertical scroll definition\n#define ST7789_SET_VERT_SCRL_ADDR 0x37 // SEt Vertical scroll start address\n#define ST7789_SET_MEM_CONT 0x3C       // Memory Write continue\n#define ST7789_GET_MEM_CONT 0x3E       // Memory Read continue\n#define ST7789_SET_TEAR_LINE 0x44      // Set tear scanline\n#define ST7789_GET_TEAR_LINE 0x45      // Get tear scanline\n#define ST7789_SET_BRIGHTNESS 0x51     // Set display brightness\n#define ST7789_GET_BRIGHTNESS 0x52     // Get display brightness\n#define ST7789_SET_CTRL 0x53           // Set CTRL display\n#define ST7789_GET_CTRL 0x54           // Get CTRL display value\n#define ST7789_SET_CAB_COLOR 0x55      // Set content adaptive brightness control and color enhancement\n#define ST7789_GET_CAB_COLOR 0x56      // Get content adaptive brightness control and color enhancement\n#define ST7789_SET_CAB_BRIGHTNESS 0x5E // Set content adaptive minimum brightness\n#define ST7789_GET_CAB_BRIGHTNESS 0x5F // Get content adaptive minimum brightness\n#define ST7789_GET_ABC_SELF_DIAG 0x68  // Get Auto brightness control self diagnostics\n\n// Panel Function Commands\n#define ST7789_SET_RAM_CTL 0xB0            // Set RAM control\n#define ST7789_SET_RGB_CTL 0xB1            // Set RGB control\n#define ST7789_SET_PORCH_CTL 0xB2          // Set Porch control\n#define ST7789_SET_FRAME_RATE_CTL_1 0xB3   // Set frame rate control 1\n#define ST7789_SET_PARTIAL_CTL 0xB5        // Set Partial control\n#define ST7789_SET_GATE_CTL 0xB7           // Set gate control\n#define ST7789_SET_GATE_ON_TIMING 0xB8     // Set gate on timing adjustment\n#define ST7789_SET_DIGITAL_GAMMA_ON 0xBA   // Enable digital gamma\n#define ST7789_SET_VCOM 0xBB               // Set VCOM\n#define ST7789_SET_POWER_SAVE 0xBC         // Set power saving mode\n#define ST7789_SET_DISP_OFF_POWER 0xBD     // Set display off power saving\n#define ST7789_SET_LCM_CTL 0xC0            // Set LCM control\n#define ST7789_SET_IDS 0xC1                // Set IDs\n#define ST7789_SET_VDV_VRH_ON 0xC2         // Set VDV and VRH command enable\n#define ST7789_SET_VRH 0xC3                // Set VRH\n#define ST7789_SET_VDV 0xC4                // Set VDV\n#define ST7789_SET_VCOM_OFFSET 0xC5        // Set VCOM offset ctl\n#define ST7789_SET_FRAME_RATE_CTL_2 0xC6   // Set frame rate control 2\n#define ST7789_SET_CABC_CTL 0xC7           // Set CABC Control\n#define ST7789_GET_REG_1 0xC8              // Get register value selection1\n#define ST7789_GET_REG_2 0xCA              // Get register value selection2\n#define ST7789_SET_PWM_FREQ 0xCC           // Set PWM frequency\n#define ST7789_SET_POWER_CTL_1 0xD0        // Set power ctl 1\n#define ST7789_SET_VAP_VAN_ON 0xD2         // Enable VAP/VAN signal output\n#define ST7789_SET_CMD2_ENABLE 0xDF        // Enable command 2\n#define ST7789_SET_PGAMMA 0xE0             // Set positive gamma\n#define ST7789_SET_NGAMMA 0xE1             // Set negative gamma\n#define ST7789_SET_DIGITAL_GAMMA_RED 0xE2  // Set digital gamma lookup table for red\n#define ST7789_SET_DIGITAL_GAMMA_BLUE 0xE3 // Get digital gamma lookup table for blue\n#define ST7789_SET_GATE_CTL_2 0xE4         // Set gate control 2\n#define ST7789_SET_SPI2_ENABLE 0xE7        // Enable SPI2\n#define ST7789_SET_POWER_CTL_2 0xE8        // Set power ctl 2\n#define ST7789_SET_EQ_TIME_CTL 0xE9        // Set equalize time control\n#define ST7789_SET_PROG_CTL 0xEC           // Set program control\n#define ST7789_SET_PROG_MODE_ENABLE 0xFA   // Set program mode enable\n#define ST7789_SET_NVMEM 0xFC              // Set NVMEM data\n#define ST7789_SET_PROG_ACTION 0xFE        // Set program action\n"
  },
  {
    "path": "drivers/painter/st77xx/qp_st77xx_opcodes.h",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter ST77XX command opcodes\n\n// System function commands\n#define ST77XX_CMD_NOP 0x00          // No operation\n#define ST77XX_CMD_RESET 0x01        // Software reset\n#define ST77XX_GET_ID_INFO 0x04      // Get ID information\n#define ST77XX_GET_STATUS 0x09       // Get status\n#define ST77XX_GET_PWR_MODE 0x0A     // Get power mode\n#define ST77XX_GET_MADCTL 0x0B       // Get mem access ctl\n#define ST77XX_GET_PIX_FMT 0x0C      // Get pixel format\n#define ST77XX_GET_IMG_FMT 0x0D      // Get image format\n#define ST77XX_GET_SIG_MODE 0x0E     // Get signal mode\n#define ST77XX_CMD_SLEEP_ON 0x10     // Enter sleep mode\n#define ST77XX_CMD_SLEEP_OFF 0x11    // Exist sleep mode\n#define ST77XX_CMD_PARTIAL_ON 0x12   // Enter partial mode\n#define ST77XX_CMD_NORMAL_ON 0x13    // Exit partial mode\n#define ST77XX_CMD_INVERT_OFF 0x20   // Exit inverted mode\n#define ST77XX_CMD_INVERT_ON 0x21    // Enter inverted mode\n#define ST77XX_SET_GAMMA 0x26        // Set gamma params\n#define ST77XX_CMD_DISPLAY_OFF 0x28  // Disable display\n#define ST77XX_CMD_DISPLAY_ON 0x29   // Enable display\n#define ST77XX_SET_COL_ADDR 0x2A     // Set column address\n#define ST77XX_SET_ROW_ADDR 0x2B     // Set page (row) address\n#define ST77XX_SET_MEM 0x2C          // Set memory\n#define ST77XX_GET_MEM 0x2E          // Get memory\n#define ST77XX_SET_PARTIAL_AREA 0x30 // Set partial area\n#define ST77XX_CMD_TEARING_OFF 0x34  // Tearing line disabled\n#define ST77XX_CMD_TEARING_ON 0x35   // Tearing line enabled\n#define ST77XX_SET_MADCTL 0x36       // Set mem access ctl\n#define ST77XX_CMD_IDLE_OFF 0x38     // Exit idle mode\n#define ST77XX_CMD_IDLE_ON 0x39      // Enter idle mode\n#define ST77XX_SET_PIX_FMT 0x3A      // Set pixel format\n#define ST77XX_GET_ID1 0xDA          // Get ID1\n#define ST77XX_GET_ID2 0xDB          // Get ID2\n#define ST77XX_GET_ID3 0xDC          // Get ID3\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// MADCTL Flags\n#define ST77XX_MADCTL_MY 0b10000000\n#define ST77XX_MADCTL_MX 0b01000000\n#define ST77XX_MADCTL_MV 0b00100000\n#define ST77XX_MADCTL_ML 0b00010000\n#define ST77XX_MADCTL_RGB 0b00000000\n#define ST77XX_MADCTL_BGR 0b00001000\n#define ST77XX_MADCTL_MH 0b00000100\n"
  },
  {
    "path": "drivers/painter/tft_panel/qp_tft_panel.c",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"color.h\"\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_draw.h\"\n#include \"qp_tft_panel.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter API implementations\n\n// Power control\nbool qp_tft_panel_power(painter_device_t device, bool power_on) {\n    painter_driver_t *                          driver = (painter_driver_t *)device;\n    tft_panel_dc_reset_painter_driver_vtable_t *vtable = (tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable;\n    qp_comms_command(device, power_on ? vtable->opcodes.display_on : vtable->opcodes.display_off);\n    return true;\n}\n\n// Screen clear\nbool qp_tft_panel_clear(painter_device_t device) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n    driver->driver_vtable->init(device, driver->rotation); // Re-init the LCD\n    return true;\n}\n\n// Screen flush\nbool qp_tft_panel_flush(painter_device_t device) {\n    // No-op, as there's no framebuffer in RAM for this device.\n    return true;\n}\n\n// Viewport to draw to\nbool qp_tft_panel_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {\n    painter_driver_t *                          driver = (painter_driver_t *)device;\n    tft_panel_dc_reset_painter_driver_vtable_t *vtable = (tft_panel_dc_reset_painter_driver_vtable_t *)driver->driver_vtable;\n\n    // Fix up the drawing location if required\n    left += driver->offset_x;\n    right += driver->offset_x;\n    top += driver->offset_y;\n    bottom += driver->offset_y;\n\n    // Check if we need to manually swap the window coordinates based on whether or not we're in a sideways rotation\n    if (vtable->swap_window_coords && (driver->rotation == QP_ROTATION_90 || driver->rotation == QP_ROTATION_270)) {\n        uint16_t temp;\n\n        temp = left;\n        left = top;\n        top  = temp;\n\n        temp   = right;\n        right  = bottom;\n        bottom = temp;\n    }\n\n    if (vtable->num_window_bytes == 1) {\n        // Set up the x-window\n        uint8_t xbuf[2] = {left & 0xFF, right & 0xFF};\n        qp_comms_command_databuf(device, vtable->opcodes.set_column_address, xbuf, sizeof(xbuf));\n\n        // Set up the y-window\n        uint8_t ybuf[2] = {top & 0xFF, bottom & 0xFF};\n        qp_comms_command_databuf(device, vtable->opcodes.set_row_address, ybuf, sizeof(ybuf));\n    } else if (vtable->num_window_bytes == 2) {\n        // Set up the x-window\n        uint8_t xbuf[4] = {left >> 8, left & 0xFF, right >> 8, right & 0xFF};\n        qp_comms_command_databuf(device, vtable->opcodes.set_column_address, xbuf, sizeof(xbuf));\n\n        // Set up the y-window\n        uint8_t ybuf[4] = {top >> 8, top & 0xFF, bottom >> 8, bottom & 0xFF};\n        qp_comms_command_databuf(device, vtable->opcodes.set_row_address, ybuf, sizeof(ybuf));\n    }\n\n    // Lock in the window\n    qp_comms_command(device, vtable->opcodes.enable_writes);\n    return true;\n}\n\n// Stream pixel data to the current write position in GRAM\nbool qp_tft_panel_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n    qp_comms_send(device, pixel_data, native_pixel_count * driver->native_bits_per_pixel / 8);\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Convert supplied palette entries into their native equivalents\n\nbool qp_tft_panel_palette_convert_rgb565_swapped(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {\n    for (int16_t i = 0; i < palette_size; ++i) {\n        rgb_t    rgb      = hsv_to_rgb_nocie((hsv_t){palette[i].hsv888.h, palette[i].hsv888.s, palette[i].hsv888.v});\n        uint16_t rgb565   = (((uint16_t)rgb.r) >> 3) << 11 | (((uint16_t)rgb.g) >> 2) << 5 | (((uint16_t)rgb.b) >> 3);\n        palette[i].rgb565 = __builtin_bswap16(rgb565);\n    }\n    return true;\n}\n\nbool qp_tft_panel_palette_convert_rgb888(painter_device_t device, int16_t palette_size, qp_pixel_t *palette) {\n    for (int16_t i = 0; i < palette_size; ++i) {\n        rgb_t rgb           = hsv_to_rgb_nocie((hsv_t){palette[i].hsv888.h, palette[i].hsv888.s, palette[i].hsv888.v});\n        palette[i].rgb888.r = rgb.r;\n        palette[i].rgb888.g = rgb.g;\n        palette[i].rgb888.b = rgb.b;\n    }\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Append pixels to the target location, keyed by the pixel index\n\nbool qp_tft_panel_append_pixels_rgb565(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices) {\n    uint16_t *buf = (uint16_t *)target_buffer;\n    for (uint32_t i = 0; i < pixel_count; ++i) {\n        buf[pixel_offset + i] = palette[palette_indices[i]].rgb565;\n    }\n    return true;\n}\n\nbool qp_tft_panel_append_pixels_rgb888(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices) {\n    for (uint32_t i = 0; i < pixel_count; ++i) {\n        target_buffer[(pixel_offset + i) * 3 + 0] = palette[palette_indices[i]].rgb888.r;\n        target_buffer[(pixel_offset + i) * 3 + 1] = palette[palette_indices[i]].rgb888.g;\n        target_buffer[(pixel_offset + i) * 3 + 2] = palette[palette_indices[i]].rgb888.b;\n    }\n    return true;\n}\n\nbool qp_tft_panel_append_pixdata(painter_device_t device, uint8_t *target_buffer, uint32_t pixdata_offset, uint8_t pixdata_byte) {\n    target_buffer[pixdata_offset] = pixdata_byte;\n    return true;\n}\n"
  },
  {
    "path": "drivers/painter/tft_panel/qp_tft_panel.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"color.h\"\n#include \"qp_internal.h\"\n\n#ifdef QUANTUM_PAINTER_SPI_ENABLE\n#    include \"qp_comms_spi.h\"\n#endif // QUANTUM_PAINTER_SPI_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Common TFT panel implementation using D/C, and RST pins.\n\n// Driver vtable with extras\ntypedef struct tft_panel_dc_reset_painter_driver_vtable_t {\n    painter_driver_vtable_t base; // must be first, so it can be cast to/from the painter_driver_vtable_t* type\n\n    // Number of bytes for transmitting x/y coordinates\n    uint8_t num_window_bytes;\n\n    // Whether or not the x/y coords should be swapped on 90/270 rotation\n    bool swap_window_coords;\n\n    // Opcodes for normal display operation\n    struct {\n        uint8_t display_on;\n        uint8_t display_off;\n        uint8_t set_column_address;\n        uint8_t set_row_address;\n        uint8_t enable_writes;\n    } opcodes;\n} tft_panel_dc_reset_painter_driver_vtable_t;\n\n// Device definition\ntypedef struct tft_panel_dc_reset_painter_device_t {\n    painter_driver_t base; // must be first, so it can be cast to/from the painter_device_t* type\n\n    union {\n#ifdef QUANTUM_PAINTER_SPI_ENABLE\n        // SPI-based configurables\n        qp_comms_spi_dc_reset_config_t spi_dc_reset_config;\n#endif // QUANTUM_PAINTER_SPI_ENABLE\n\n        // TODO: I2C/parallel etc.\n    };\n} tft_panel_dc_reset_painter_device_t;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Forward declarations for injecting into concrete driver vtables\n\nbool qp_tft_panel_power(painter_device_t device, bool power_on);\nbool qp_tft_panel_clear(painter_device_t device);\nbool qp_tft_panel_flush(painter_device_t device);\nbool qp_tft_panel_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom);\nbool qp_tft_panel_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count);\n\nbool qp_tft_panel_palette_convert_rgb565_swapped(painter_device_t device, int16_t palette_size, qp_pixel_t *palette);\nbool qp_tft_panel_palette_convert_rgb888(painter_device_t device, int16_t palette_size, qp_pixel_t *palette);\n\nbool qp_tft_panel_append_pixels_rgb565(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices);\nbool qp_tft_panel_append_pixels_rgb888(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices);\n\nbool qp_tft_panel_append_pixdata(painter_device_t device, uint8_t *target_buffer, uint32_t pixdata_offset, uint8_t pixdata_byte);\n"
  },
  {
    "path": "drivers/ps2/ps2.h",
    "content": "/*\nCopyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>\n\nThis software is licensed with a Modified BSD License.\nAll of this is supposed to be Free Software, Open Source, DFSG-free,\nGPL-compatible, and OK to use in both free and proprietary applications.\nAdditions and corrections to this file are welcome.\n\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright\n  notice, this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright\n  notice, this list of conditions and the following disclaimer in\n  the documentation and/or other materials provided with the\n  distribution.\n\n* Neither the name of the copyright holders nor the names of\n  contributors may be used to endorse or promote products derived\n  from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n*/\n\n#pragma once\n\n#include <stdbool.h>\n#include \"wait.h\"\n#include \"ps2_io.h\"\n#include \"print.h\"\n\n/*\n * Primitive PS/2 Library for AVR\n *\n * PS/2 Resources\n * --------------\n * [1] The PS/2 Mouse/Keyboard Protocol\n * http://www.computer-engineering.org/ps2protocol/\n * Concise and thorough primer of PS/2 protocol.\n *\n * [2] Keyboard and Auxiliary Device Controller\n * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf\n * Signal Timing and Format\n *\n * [3] Keyboards(101- and 102-key)\n * http://www.mcamafia.de/pdf/ibm_hitrc11.pdf\n * Keyboard Layout, Scan Code Set, POR, and Commands.\n *\n * [4] PS/2 Reference Manuals\n * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf\n * Collection of IBM Personal System/2 documents.\n *\n * [5] TrackPoint Engineering Specifications for version 3E\n * https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html\n */\n#define PS2_ACK 0xFA\n#define PS2_RESEND 0xFE\n#define PS2_SET_LED 0xED\n\n// TODO: error numbers\n#define PS2_ERR_NONE 0\n#define PS2_ERR_STARTBIT1 1\n#define PS2_ERR_STARTBIT2 2\n#define PS2_ERR_STARTBIT3 3\n#define PS2_ERR_PARITY 0x10\n#define PS2_ERR_NODATA 0x20\n\n#define PS2_LED_SCROLL_LOCK 0\n#define PS2_LED_NUM_LOCK 1\n#define PS2_LED_CAPS_LOCK 2\n\nextern uint8_t ps2_error;\n\nvoid    ps2_host_init(void);\nuint8_t ps2_host_send(uint8_t data);\nuint8_t ps2_host_recv_response(void);\nuint8_t ps2_host_recv(void);\nvoid    ps2_host_set_led(uint8_t usb_led);\nbool    pbuf_has_data(void);\n\n/*--------------------------------------------------------------------\n * static functions\n *------------------------------------------------------------------*/\nstatic inline uint16_t wait_clock_lo(uint16_t us) {\n    while (clock_in() && us) {\n        asm(\"\");\n        wait_us(1);\n        us--;\n    }\n    return us;\n}\nstatic inline uint16_t wait_clock_hi(uint16_t us) {\n    while (!clock_in() && us) {\n        asm(\"\");\n        wait_us(1);\n        us--;\n    }\n    return us;\n}\nstatic inline uint16_t wait_data_lo(uint16_t us) {\n    while (data_in() && us) {\n        asm(\"\");\n        wait_us(1);\n        us--;\n    }\n    return us;\n}\nstatic inline uint16_t wait_data_hi(uint16_t us) {\n    while (!data_in() && us) {\n        asm(\"\");\n        wait_us(1);\n        us--;\n    }\n    return us;\n}\n\n/* idle state that device can send */\nstatic inline void idle(void) {\n    clock_hi();\n    data_hi();\n}\n\n/* inhibit device to send */\nstatic inline void inhibit(void) {\n    clock_lo();\n    data_hi();\n}\n"
  },
  {
    "path": "drivers/ps2/ps2_busywait.c",
    "content": "/*\nCopyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>\n\nThis software is licensed with a Modified BSD License.\nAll of this is supposed to be Free Software, Open Source, DFSG-free,\nGPL-compatible, and OK to use in both free and proprietary applications.\nAdditions and corrections to this file are welcome.\n\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright\n  notice, this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright\n  notice, this list of conditions and the following disclaimer in\n  the documentation and/or other materials provided with the\n  distribution.\n\n* Neither the name of the copyright holders nor the names of\n  contributors may be used to endorse or promote products derived\n  from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n*/\n\n/*\n * PS/2 protocol busywait version\n */\n\n#include <stdbool.h>\n#include \"wait.h\"\n#include \"ps2.h\"\n#include \"ps2_io.h\"\n#include \"debug.h\"\n\n#define WAIT(stat, us, err)     \\\n    do {                        \\\n        if (!wait_##stat(us)) { \\\n            ps2_error = err;    \\\n            goto ERROR;         \\\n        }                       \\\n    } while (0)\n\nuint8_t ps2_error = PS2_ERR_NONE;\n\nvoid ps2_host_init(void) {\n    clock_init();\n    data_init();\n\n    // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)\n    wait_ms(2500);\n\n    inhibit();\n}\n\nuint8_t ps2_host_send(uint8_t data) {\n    bool parity = true;\n    ps2_error   = PS2_ERR_NONE;\n\n    /* terminate a transmission if we have */\n    inhibit();\n    wait_us(100); // 100us [4]p.13, [5]p.50\n\n    /* 'Request to Send' and Start bit */\n    data_lo();\n    clock_hi();\n    WAIT(clock_lo, 10000, 10); // 10ms [5]p.50\n\n    /* Data bit */\n    for (uint8_t i = 0; i < 8; i++) {\n        wait_us(15);\n        if (data & (1 << i)) {\n            parity = !parity;\n            data_hi();\n        } else {\n            data_lo();\n        }\n        WAIT(clock_hi, 50, 2);\n        WAIT(clock_lo, 50, 3);\n    }\n\n    /* Parity bit */\n    wait_us(15);\n    if (parity) {\n        data_hi();\n    } else {\n        data_lo();\n    }\n    WAIT(clock_hi, 50, 4);\n    WAIT(clock_lo, 50, 5);\n\n    /* Stop bit */\n    wait_us(15);\n    data_hi();\n\n    /* Ack */\n    WAIT(data_lo, 50, 6);\n    WAIT(clock_lo, 50, 7);\n\n    /* wait for idle state */\n    WAIT(clock_hi, 50, 8);\n    WAIT(data_hi, 50, 9);\n\n    inhibit();\n    return ps2_host_recv_response();\nERROR:\n    inhibit();\n    return 0;\n}\n\n/* receive data when host want else inhibit communication */\nuint8_t ps2_host_recv_response(void) {\n    // Command may take 25ms/20ms at most([5]p.46, [3]p.21)\n    // 250 * 100us(wait for start bit in ps2_host_recv)\n    uint8_t data = 0;\n    uint8_t try  = 250;\n    do {\n        data = ps2_host_recv();\n    } while (try-- && ps2_error);\n    return data;\n}\n\n/* called after start bit comes */\nuint8_t ps2_host_recv(void) {\n    uint8_t data   = 0;\n    bool    parity = true;\n    ps2_error      = PS2_ERR_NONE;\n\n    /* release lines(idle state) */\n    idle();\n\n    /* start bit [1] */\n    WAIT(clock_lo, 100, 1); // TODO: this is enough?\n    WAIT(data_lo, 1, 2);\n    WAIT(clock_hi, 50, 3);\n\n    /* data [2-9] */\n    for (uint8_t i = 0; i < 8; i++) {\n        WAIT(clock_lo, 50, 4);\n        if (data_in()) {\n            parity = !parity;\n            data |= (1 << i);\n        }\n        WAIT(clock_hi, 50, 5);\n    }\n\n    /* parity [10] */\n    WAIT(clock_lo, 50, 6);\n    if (data_in() != parity) {\n        ps2_error = PS2_ERR_PARITY;\n        goto ERROR;\n    }\n    WAIT(clock_hi, 50, 7);\n\n    /* stop bit [11] */\n    WAIT(clock_lo, 50, 8);\n    WAIT(data_hi, 1, 9);\n    WAIT(clock_hi, 50, 10);\n\n    inhibit();\n    return data;\nERROR:\n    if (ps2_error > PS2_ERR_STARTBIT3) {\n        xprintf(\"x%02X\\n\", ps2_error);\n    }\n    inhibit();\n    return 0;\n}\n\n/* send LED state to keyboard */\nvoid ps2_host_set_led(uint8_t led) {\n    ps2_host_send(0xED);\n    ps2_host_send(led);\n}\n"
  },
  {
    "path": "drivers/ps2/ps2_interrupt.c",
    "content": "/*\nCopyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>\n\nThis software is licensed with a Modified BSD License.\nAll of this is supposed to be Free Software, Open Source, DFSG-free,\nGPL-compatible, and OK to use in both free and proprietary applications.\nAdditions and corrections to this file are welcome.\n\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright\n  notice, this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright\n  notice, this list of conditions and the following disclaimer in\n  the documentation and/or other materials provided with the\n  distribution.\n\n* Neither the name of the copyright holders nor the names of\n  contributors may be used to endorse or promote products derived\n  from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n*/\n\n/*\n * PS/2 protocol Pin interrupt version\n */\n\n#include <stdbool.h>\n\n#if defined(__AVR__)\n#    include <avr/interrupt.h>\n#elif defined(PROTOCOL_CHIBIOS) // TODO: or STM32 ?\n// chibiOS headers\n#    include \"ch.h\"\n#    include \"hal.h\"\n#    include \"gpio.h\"\n#endif\n\n#include \"ps2.h\"\n#include \"ps2_io.h\"\n#include \"print.h\"\n#include \"wait.h\"\n\n#define WAIT(stat, us, err)     \\\n    do {                        \\\n        if (!wait_##stat(us)) { \\\n            ps2_error = err;    \\\n            goto ERROR;         \\\n        }                       \\\n    } while (0)\n\nuint8_t ps2_error = PS2_ERR_NONE;\n\nstatic inline uint8_t pbuf_dequeue(void);\nstatic inline void    pbuf_enqueue(uint8_t data);\nstatic inline void    pbuf_clear(void);\nbool                  pbuf_has_data(void);\n\n#if defined(PROTOCOL_CHIBIOS)\nvoid ps2_interrupt_service_routine(void);\nvoid palCallback(void *arg) {\n    ps2_interrupt_service_routine();\n}\n\n#    define PS2_INT_INIT()                                 \\\n        do {                                               \\\n            palSetLineMode(PS2_CLOCK_PIN, PAL_MODE_INPUT); \\\n        } while (0)\n#    define PS2_INT_ON()                                                    \\\n        do {                                                                \\\n            palEnableLineEvent(PS2_CLOCK_PIN, PAL_EVENT_MODE_FALLING_EDGE); \\\n            palSetLineCallback(PS2_CLOCK_PIN, palCallback, NULL);           \\\n        } while (0)\n#    define PS2_INT_OFF()                       \\\n        do {                                    \\\n            palDisableLineEvent(PS2_CLOCK_PIN); \\\n        } while (0)\n#endif // PROTOCOL_CHIBIOS\n\nvoid ps2_host_init(void) {\n    idle();\n    PS2_INT_INIT();\n    PS2_INT_ON();\n    // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)\n    // wait_ms(2500);\n}\n\nuint8_t ps2_host_send(uint8_t data) {\n    bool parity = true;\n    ps2_error   = PS2_ERR_NONE;\n\n    PS2_INT_OFF();\n\n    /* terminate a transmission if we have */\n    inhibit();\n    wait_us(100); // 100us [4]p.13, [5]p.50\n\n    /* 'Request to Send' and Start bit */\n    data_lo();\n    clock_hi();\n    WAIT(clock_lo, 10000, 10); // 10ms [5]p.50\n\n    /* Data bit[2-9] */\n    for (uint8_t i = 0; i < 8; i++) {\n        if (data & (1 << i)) {\n            parity = !parity;\n            data_hi();\n        } else {\n            data_lo();\n        }\n        WAIT(clock_hi, 50, 2);\n        WAIT(clock_lo, 50, 3);\n    }\n\n    /* Parity bit */\n    wait_us(15);\n    if (parity) {\n        data_hi();\n    } else {\n        data_lo();\n    }\n    WAIT(clock_hi, 50, 4);\n    WAIT(clock_lo, 50, 5);\n\n    /* Stop bit */\n    wait_us(15);\n    data_hi();\n\n    /* Ack */\n    WAIT(data_lo, 50, 6);\n    WAIT(clock_lo, 50, 7);\n\n    /* wait for idle state */\n    WAIT(clock_hi, 50, 8);\n    WAIT(data_hi, 50, 9);\n\n    idle();\n    PS2_INT_ON();\n    return ps2_host_recv_response();\nERROR:\n    idle();\n    PS2_INT_ON();\n    return 0;\n}\n\nuint8_t ps2_host_recv_response(void) {\n    // Command may take 25ms/20ms at most([5]p.46, [3]p.21)\n    uint8_t retry = 25;\n    while (retry-- && !pbuf_has_data()) {\n        wait_ms(1);\n    }\n    return pbuf_dequeue();\n}\n\n/* get data received by interrupt */\nuint8_t ps2_host_recv(void) {\n    if (pbuf_has_data()) {\n        ps2_error = PS2_ERR_NONE;\n        return pbuf_dequeue();\n    } else {\n        ps2_error = PS2_ERR_NODATA;\n        return 0;\n    }\n}\n\nvoid ps2_interrupt_service_routine(void) {\n    static enum {\n        INIT,\n        START,\n        BIT0,\n        BIT1,\n        BIT2,\n        BIT3,\n        BIT4,\n        BIT5,\n        BIT6,\n        BIT7,\n        PARITY,\n        STOP,\n    } state               = INIT;\n    static uint8_t data   = 0;\n    static uint8_t parity = 1;\n\n    // TODO: abort if elapse 100us from previous interrupt\n\n    // return unless falling edge\n    if (clock_in()) {\n        goto RETURN;\n    }\n\n    state++;\n    switch (state) {\n        case START:\n            if (data_in()) goto ERROR;\n            break;\n        case BIT0:\n        case BIT1:\n        case BIT2:\n        case BIT3:\n        case BIT4:\n        case BIT5:\n        case BIT6:\n        case BIT7:\n            data >>= 1;\n            if (data_in()) {\n                data |= 0x80;\n                parity++;\n            }\n            break;\n        case PARITY:\n            if (data_in()) {\n                if (!(parity & 0x01)) goto ERROR;\n            } else {\n                if (parity & 0x01) goto ERROR;\n            }\n            break;\n        case STOP:\n            if (!data_in()) goto ERROR;\n            pbuf_enqueue(data);\n            goto DONE;\n            break;\n        default:\n            goto ERROR;\n    }\n    goto RETURN;\nERROR:\n    ps2_error = state;\nDONE:\n    state  = INIT;\n    data   = 0;\n    parity = 1;\nRETURN:\n    return;\n}\n\n#if defined(__AVR__)\nISR(PS2_INT_VECT) {\n    ps2_interrupt_service_routine();\n}\n#endif\n\n/* send LED state to keyboard */\nvoid ps2_host_set_led(uint8_t led) {\n    ps2_host_send(0xED);\n    ps2_host_send(led);\n}\n\n/*--------------------------------------------------------------------\n * Ring buffer to store scan codes from keyboard\n *------------------------------------------------------------------*/\n#define PBUF_SIZE 32\nstatic uint8_t     pbuf[PBUF_SIZE];\nstatic uint8_t     pbuf_head = 0;\nstatic uint8_t     pbuf_tail = 0;\nstatic inline void pbuf_enqueue(uint8_t data) {\n#if defined(__AVR__)\n    uint8_t sreg = SREG;\n    cli();\n#elif defined(PROTOCOL_CHIBIOS)\n    chSysLockFromISR();\n#endif\n\n    uint8_t next = (pbuf_head + 1) % PBUF_SIZE;\n    if (next != pbuf_tail) {\n        pbuf[pbuf_head] = data;\n        pbuf_head       = next;\n    } else {\n        print(\"pbuf: full\\n\");\n    }\n\n#if defined(__AVR__)\n    SREG = sreg;\n#elif defined(PROTOCOL_CHIBIOS)\n    chSysUnlockFromISR();\n#endif\n}\nstatic inline uint8_t pbuf_dequeue(void) {\n    uint8_t val = 0;\n\n#if defined(__AVR__)\n    uint8_t sreg = SREG;\n    cli();\n#elif defined(PROTOCOL_CHIBIOS)\n    chSysLock();\n#endif\n\n    if (pbuf_head != pbuf_tail) {\n        val       = pbuf[pbuf_tail];\n        pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;\n    }\n\n#if defined(__AVR__)\n    SREG = sreg;\n#elif defined(PROTOCOL_CHIBIOS)\n    chSysUnlock();\n#endif\n\n    return val;\n}\nbool pbuf_has_data(void) {\n#if defined(__AVR__)\n    uint8_t sreg = SREG;\n    cli();\n#elif defined(PROTOCOL_CHIBIOS)\n    chSysLock();\n#endif\n\n    bool has_data = (pbuf_head != pbuf_tail);\n\n#if defined(__AVR__)\n    SREG = sreg;\n#elif defined(PROTOCOL_CHIBIOS)\n    chSysUnlock();\n#endif\n    return has_data;\n}\nstatic inline void pbuf_clear(void) {\n#if defined(__AVR__)\n    uint8_t sreg = SREG;\n    cli();\n#elif defined(PROTOCOL_CHIBIOS)\n    chSysLock();\n#endif\n\n    pbuf_head = pbuf_tail = 0;\n\n#if defined(__AVR__)\n    SREG = sreg;\n#elif defined(PROTOCOL_CHIBIOS)\n    chSysUnlock();\n#endif\n}\n"
  },
  {
    "path": "drivers/ps2/ps2_io.h",
    "content": "#pragma once\n\nvoid clock_init(void);\nvoid clock_lo(void);\nvoid clock_hi(void);\nbool clock_in(void);\n\nvoid data_init(void);\nvoid data_lo(void);\nvoid data_hi(void);\nbool data_in(void);\n"
  },
  {
    "path": "drivers/ps2/ps2_mouse.c",
    "content": "/*\nCopyright 2011,2013 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include <stdbool.h>\n#include \"ps2_mouse.h\"\n#include \"wait.h\"\n#include \"gpio.h\"\n#include \"host.h\"\n#include \"timer.h\"\n#include \"print.h\"\n#include \"report.h\"\n#include \"debug.h\"\n#include \"ps2.h\"\n\n/* ============================= MACROS ============================ */\n\nstatic report_mouse_t mouse_report = {};\n\nstatic inline void ps2_mouse_print_report(report_mouse_t *mouse_report);\nstatic inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report);\nstatic inline void ps2_mouse_clear_report(report_mouse_t *mouse_report);\nstatic inline void ps2_mouse_enable_scrolling(void);\nstatic inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report);\n\n/* ============================= IMPLEMENTATION ============================ */\n\n/* supports only 3 button mouse at this time */\nvoid ps2_mouse_init(void) {\n    ps2_host_init();\n\n    wait_ms(PS2_MOUSE_INIT_DELAY); // wait for powering up\n\n    PS2_MOUSE_SEND(PS2_MOUSE_RESET, \"ps2_mouse_init: sending reset\");\n\n    PS2_MOUSE_RECEIVE(\"ps2_mouse_init: read BAT\");\n    PS2_MOUSE_RECEIVE(\"ps2_mouse_init: read DevID\");\n\n#ifdef PS2_MOUSE_USE_REMOTE_MODE\n    ps2_mouse_set_remote_mode();\n#else\n    ps2_mouse_enable_data_reporting();\n    ps2_mouse_set_stream_mode();\n#endif\n\n#ifdef PS2_MOUSE_ENABLE_SCROLLING\n    ps2_mouse_enable_scrolling();\n#endif\n\n#ifdef PS2_MOUSE_USE_2_1_SCALING\n    ps2_mouse_set_scaling_2_1();\n#endif\n\n    ps2_mouse_init_user();\n}\n\n__attribute__((weak)) void ps2_mouse_init_user(void) {}\n\n__attribute__((weak)) void ps2_mouse_moved_user(report_mouse_t *mouse_report) {}\n\nvoid ps2_mouse_task(void) {\n    static uint8_t buttons_prev = 0;\n    extern int     tp_buttons;\n\n    /* receives packet from mouse */\n#ifdef PS2_MOUSE_USE_REMOTE_MODE\n    uint8_t rcv;\n    rcv = ps2_host_send(PS2_MOUSE_READ_DATA);\n    if (rcv == PS2_ACK) {\n        mouse_report.buttons = ps2_host_recv_response();\n        mouse_report.x       = ps2_host_recv_response();\n        mouse_report.y       = ps2_host_recv_response();\n#    ifdef PS2_MOUSE_ENABLE_SCROLLING\n        mouse_report.v = -(ps2_host_recv_response() & PS2_MOUSE_SCROLL_MASK);\n#    endif\n    } else {\n        if (debug_mouse) print(\"ps2_mouse: fail to get mouse packet\\n\");\n        /* return here to avoid updating the mouse button state */\n        return;\n    }\n#else\n    if (pbuf_has_data()) {\n        mouse_report.buttons = ps2_host_recv_response();\n        mouse_report.x       = ps2_host_recv_response();\n        mouse_report.y       = ps2_host_recv_response();\n#    ifdef PS2_MOUSE_ENABLE_SCROLLING\n        mouse_report.v       = -(ps2_host_recv_response() & PS2_MOUSE_SCROLL_MASK);\n#    endif\n    } else {\n        if (debug_mouse) print(\"ps2_mouse: fail to get mouse packet\\n\");\n        /* return here to avoid updating the mouse button state */\n        return;\n    }\n#endif\n\n    mouse_report.buttons |= tp_buttons;\n    /* if mouse moves or buttons state changes */\n    if (mouse_report.x || mouse_report.y || mouse_report.v || ((mouse_report.buttons ^ buttons_prev) & PS2_MOUSE_BTN_MASK)) {\n#ifdef PS2_MOUSE_DEBUG_RAW\n        // Used to debug raw ps2 bytes from mouse\n        ps2_mouse_print_report(&mouse_report);\n#endif\n        buttons_prev = mouse_report.buttons;\n        ps2_mouse_convert_report_to_hid(&mouse_report);\n#if PS2_MOUSE_SCROLL_BTN_MASK\n        ps2_mouse_scroll_button_task(&mouse_report);\n#endif\n        if (mouse_report.x || mouse_report.y || mouse_report.v) {\n            ps2_mouse_moved_user(&mouse_report);\n        }\n#ifdef PS2_MOUSE_DEBUG_HID\n        // Used to debug the bytes sent to the host\n        ps2_mouse_print_report(&mouse_report);\n#endif\n        host_mouse_send(&mouse_report);\n    }\n\n    ps2_mouse_clear_report(&mouse_report);\n}\n\nvoid ps2_mouse_disable_data_reporting(void) {\n    PS2_MOUSE_SEND(PS2_MOUSE_DISABLE_DATA_REPORTING, \"ps2 mouse disable data reporting\");\n}\n\nvoid ps2_mouse_enable_data_reporting(void) {\n    PS2_MOUSE_SEND(PS2_MOUSE_ENABLE_DATA_REPORTING, \"ps2 mouse enable data reporting\");\n}\n\nvoid ps2_mouse_set_remote_mode(void) {\n    PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_REMOTE_MODE, \"ps2 mouse set remote mode\");\n    ps2_mouse_mode = PS2_MOUSE_REMOTE_MODE;\n}\n\nvoid ps2_mouse_set_stream_mode(void) {\n    PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_STREAM_MODE, \"ps2 mouse set stream mode\");\n    ps2_mouse_mode = PS2_MOUSE_STREAM_MODE;\n}\n\nvoid ps2_mouse_set_scaling_2_1(void) {\n    PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_SCALING_2_1, \"ps2 mouse set scaling 2:1\");\n}\n\nvoid ps2_mouse_set_scaling_1_1(void) {\n    PS2_MOUSE_SEND_SAFE(PS2_MOUSE_SET_SCALING_1_1, \"ps2 mouse set scaling 1:1\");\n}\n\nvoid ps2_mouse_set_resolution(ps2_mouse_resolution_t resolution) {\n    PS2_MOUSE_SET_SAFE(PS2_MOUSE_SET_RESOLUTION, resolution, \"ps2 mouse set resolution\");\n}\n\nvoid ps2_mouse_set_sample_rate(ps2_mouse_sample_rate_t sample_rate) {\n    PS2_MOUSE_SET_SAFE(PS2_MOUSE_SET_SAMPLE_RATE, sample_rate, \"ps2 mouse set sample rate\");\n}\n\n/* ============================= HELPERS ============================ */\n\n#define X_IS_NEG (mouse_report->buttons & (1 << PS2_MOUSE_X_SIGN))\n#define Y_IS_NEG (mouse_report->buttons & (1 << PS2_MOUSE_Y_SIGN))\n#define X_IS_OVF (mouse_report->buttons & (1 << PS2_MOUSE_X_OVFLW))\n#define Y_IS_OVF (mouse_report->buttons & (1 << PS2_MOUSE_Y_OVFLW))\nstatic inline void ps2_mouse_convert_report_to_hid(report_mouse_t *mouse_report) {\n#ifndef MOUSE_EXTENDED_REPORT\n    // PS/2 mouse data is '9-bit integer'(-256 to 255) which is comprised of sign-bit and 8-bit value.\n    // bit: 8    7 ... 0\n    //      sign \\8-bit/\n    //\n    // Meanwhile USB HID mouse indicates 8bit data(-127 to 127), note that -128 is not used.\n    //\n    // This converts PS/2 data into HID value. Use only -127-127 out of PS/2 9-bit.\n    mouse_report->x *= PS2_MOUSE_X_MULTIPLIER;\n    mouse_report->y *= PS2_MOUSE_Y_MULTIPLIER;\n    mouse_report->x = X_IS_NEG ? ((!X_IS_OVF && -127 <= mouse_report->x && mouse_report->x <= -1) ? mouse_report->x : -127) : ((!X_IS_OVF && 0 <= mouse_report->x && mouse_report->x <= 127) ? mouse_report->x : 127);\n    mouse_report->y = Y_IS_NEG ? ((!Y_IS_OVF && -127 <= mouse_report->y && mouse_report->y <= -1) ? mouse_report->y : -127) : ((!Y_IS_OVF && 0 <= mouse_report->y && mouse_report->y <= 127) ? mouse_report->y : 127);\n#else\n    // Sign extend if negative, otherwise leave positive 8-bits as-is\n    mouse_report->x = X_IS_NEG ? (mouse_report->x | ~0xFF) : mouse_report->x;\n    mouse_report->y = Y_IS_NEG ? (mouse_report->y | ~0xFF) : mouse_report->y;\n    mouse_report->x *= PS2_MOUSE_X_MULTIPLIER;\n    mouse_report->y *= PS2_MOUSE_Y_MULTIPLIER;\n#endif\n    mouse_report->v *= PS2_MOUSE_V_MULTIPLIER;\n\n#ifdef PS2_MOUSE_INVERT_BUTTONS\n    // swap left & right buttons\n    bool needs_left       = mouse_report->buttons & (1 << PS2_MOUSE_BTN_RIGHT);\n    bool needs_right      = mouse_report->buttons & (1 << PS2_MOUSE_BTN_LEFT);\n    mouse_report->buttons = (mouse_report->buttons & ~((1 << PS2_MOUSE_BTN_LEFT) | (1 << PS2_MOUSE_BTN_RIGHT))) | (needs_left << PS2_MOUSE_BTN_LEFT) | (needs_right << PS2_MOUSE_BTN_RIGHT);\n#endif\n    // remove sign and overflow flags\n    mouse_report->buttons &= PS2_MOUSE_BTN_MASK;\n\n#ifdef PS2_MOUSE_INVERT_X\n    mouse_report->x = -mouse_report->x;\n#endif\n#ifndef PS2_MOUSE_INVERT_Y // NOTE if not!\n    // invert coordinate of y to conform to USB HID mouse\n    mouse_report->y = -mouse_report->y;\n#endif\n\n#ifdef PS2_MOUSE_ROTATE\n    mouse_xy_report_t x = mouse_report->x;\n    mouse_xy_report_t y = mouse_report->y;\n#    if PS2_MOUSE_ROTATE == 90\n    mouse_report->x = y;\n    mouse_report->y = -x;\n#    elif PS2_MOUSE_ROTATE == 180\n    mouse_report->x = -x;\n    mouse_report->y = -y;\n#    elif PS2_MOUSE_ROTATE == 270\n    mouse_report->x = -y;\n    mouse_report->y = x;\n#    endif\n#endif\n}\n\nstatic inline void ps2_mouse_clear_report(report_mouse_t *mouse_report) {\n    mouse_report->x       = 0;\n    mouse_report->y       = 0;\n    mouse_report->v       = 0;\n    mouse_report->h       = 0;\n    mouse_report->buttons = 0;\n}\n\nstatic inline void ps2_mouse_print_report(report_mouse_t *mouse_report) {\n    if (!debug_mouse) return;\n    print(\"ps2_mouse: [\");\n    print_hex8(mouse_report->buttons);\n    print(\"|\");\n    print_hex8((uint8_t)mouse_report->x);\n    print(\" \");\n    print_hex8((uint8_t)mouse_report->y);\n    print(\" \");\n    print_hex8((uint8_t)mouse_report->v);\n    print(\" \");\n    print_hex8((uint8_t)mouse_report->h);\n    print(\"]\\n\");\n}\n\nstatic inline void ps2_mouse_enable_scrolling(void) {\n    PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, \"Initiaing scroll wheel enable: Set sample rate\");\n    PS2_MOUSE_SEND(200, \"200\");\n    PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, \"Set sample rate\");\n    PS2_MOUSE_SEND(100, \"100\");\n    PS2_MOUSE_SEND(PS2_MOUSE_SET_SAMPLE_RATE, \"Set sample rate\");\n    PS2_MOUSE_SEND(80, \"80\");\n    PS2_MOUSE_SEND(PS2_MOUSE_GET_DEVICE_ID, \"Finished enabling scroll wheel\");\n    wait_ms(20);\n}\n\n#define PRESS_SCROLL_BUTTONS mouse_report->buttons |= (PS2_MOUSE_SCROLL_BTN_MASK)\n#define RELEASE_SCROLL_BUTTONS mouse_report->buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK)\nstatic inline void ps2_mouse_scroll_button_task(report_mouse_t *mouse_report) {\n    static enum {\n        SCROLL_NONE,\n        SCROLL_BTN,\n        SCROLL_SENT,\n    } scroll_state                     = SCROLL_NONE;\n    static uint16_t scroll_button_time = 0;\n    static int16_t  scroll_x, scroll_y;\n\n    if (PS2_MOUSE_SCROLL_BTN_MASK == (mouse_report->buttons & (PS2_MOUSE_SCROLL_BTN_MASK))) {\n        // All scroll buttons are pressed\n\n        if (scroll_state == SCROLL_NONE) {\n            scroll_button_time = timer_read();\n            scroll_state       = SCROLL_BTN;\n            scroll_x           = 0;\n            scroll_y           = 0;\n        }\n\n        // If the mouse has moved, update the report to scroll instead of move the mouse\n        if (mouse_report->x || mouse_report->y) {\n            scroll_state = SCROLL_SENT;\n            scroll_y += mouse_report->y;\n            scroll_x += mouse_report->x;\n            mouse_report->v = -scroll_y / (PS2_MOUSE_SCROLL_DIVISOR_V);\n            mouse_report->h = scroll_x / (PS2_MOUSE_SCROLL_DIVISOR_H);\n            scroll_y += (mouse_report->v * (PS2_MOUSE_SCROLL_DIVISOR_V));\n            scroll_x -= (mouse_report->h * (PS2_MOUSE_SCROLL_DIVISOR_H));\n            mouse_report->x = 0;\n            mouse_report->y = 0;\n#ifdef PS2_MOUSE_INVERT_H\n            mouse_report->h = -mouse_report->h;\n#endif\n#ifdef PS2_MOUSE_INVERT_V\n            mouse_report->v = -mouse_report->v;\n#endif\n        }\n    } else if (0 == (PS2_MOUSE_SCROLL_BTN_MASK & mouse_report->buttons)) {\n        // None of the scroll buttons are pressed\n\n#if PS2_MOUSE_SCROLL_BTN_SEND\n        if (scroll_state == SCROLL_BTN && timer_elapsed(scroll_button_time) < PS2_MOUSE_SCROLL_BTN_SEND) {\n            PRESS_SCROLL_BUTTONS;\n            host_mouse_send(mouse_report);\n            wait_ms(100);\n            RELEASE_SCROLL_BUTTONS;\n        }\n#endif\n        scroll_state = SCROLL_NONE;\n    }\n\n    RELEASE_SCROLL_BUTTONS;\n}\n"
  },
  {
    "path": "drivers/ps2/ps2_mouse.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdbool.h>\n#include \"debug.h\"\n#include \"report.h\"\n\n#define PS2_MOUSE_SEND(command, message)                                                \\\n    do {                                                                                \\\n        __attribute__((unused)) uint8_t rcv = ps2_host_send(command);                   \\\n        if (debug_mouse) {                                                              \\\n            print((message));                                                           \\\n            xprintf(\" command: %X, result: %X, error: %X \\n\", command, rcv, ps2_error); \\\n        }                                                                               \\\n    } while (0)\n\n#define PS2_MOUSE_SEND_SAFE(command, message)          \\\n    do {                                               \\\n        if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \\\n            ps2_mouse_disable_data_reporting();        \\\n        }                                              \\\n        PS2_MOUSE_SEND(command, message);              \\\n        if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \\\n            ps2_mouse_enable_data_reporting();         \\\n        }                                              \\\n    } while (0)\n\n#define PS2_MOUSE_SET_SAFE(command, value, message)    \\\n    do {                                               \\\n        if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \\\n            ps2_mouse_disable_data_reporting();        \\\n        }                                              \\\n        PS2_MOUSE_SEND(command, message);              \\\n        PS2_MOUSE_SEND(value, \"Sending value\");        \\\n        if (PS2_MOUSE_STREAM_MODE == ps2_mouse_mode) { \\\n            ps2_mouse_enable_data_reporting();         \\\n        }                                              \\\n    } while (0)\n\n#define PS2_MOUSE_RECEIVE(message)                                      \\\n    do {                                                                \\\n        __attribute__((unused)) uint8_t rcv = ps2_host_recv_response(); \\\n        if (debug_mouse) {                                              \\\n            print((message));                                           \\\n            xprintf(\" result: %X, error: %X \\n\", rcv, ps2_error);       \\\n        }                                                               \\\n    } while (0)\n\n__attribute__((unused)) static enum ps2_mouse_mode_e {\n    PS2_MOUSE_STREAM_MODE,\n    PS2_MOUSE_REMOTE_MODE,\n} ps2_mouse_mode = PS2_MOUSE_STREAM_MODE;\n\n/*\n * Data format:\n * byte|7       6       5       4       3       2       1       0\n * ----+----------------------------------------------------------------\n *    0|[Yovflw][Xovflw][Ysign ][Xsign ][ 1    ][Middle][Right ][Left  ]\n *    1|[                    X movement(0-255)                         ]\n *    2|[                    Y movement(0-255)                         ]\n */\n#define PS2_MOUSE_BTN_MASK 0x07\n#define PS2_MOUSE_BTN_LEFT 0\n#define PS2_MOUSE_BTN_RIGHT 1\n#define PS2_MOUSE_BTN_MIDDLE 2\n#define PS2_MOUSE_X_SIGN 4\n#define PS2_MOUSE_Y_SIGN 5\n#define PS2_MOUSE_X_OVFLW 6\n#define PS2_MOUSE_Y_OVFLW 7\n\n/* mouse button to start scrolling; set 0 to disable scroll */\n#ifndef PS2_MOUSE_SCROLL_BTN_MASK\n#    define PS2_MOUSE_SCROLL_BTN_MASK (1 << PS2_MOUSE_BTN_MIDDLE)\n#endif\n/* send button event when button is released within this value(ms); set 0 to disable  */\n#ifndef PS2_MOUSE_SCROLL_BTN_SEND\n#    define PS2_MOUSE_SCROLL_BTN_SEND 300\n#endif\n/* divide virtical and horizontal mouse move by this to convert to scroll move */\n#ifndef PS2_MOUSE_SCROLL_DIVISOR_V\n#    define PS2_MOUSE_SCROLL_DIVISOR_V 2\n#endif\n#ifndef PS2_MOUSE_SCROLL_DIVISOR_H\n#    define PS2_MOUSE_SCROLL_DIVISOR_H 2\n#endif\n/* multiply reported mouse values by these */\n#ifndef PS2_MOUSE_X_MULTIPLIER\n#    define PS2_MOUSE_X_MULTIPLIER 1\n#endif\n#ifndef PS2_MOUSE_Y_MULTIPLIER\n#    define PS2_MOUSE_Y_MULTIPLIER 1\n#endif\n#ifndef PS2_MOUSE_V_MULTIPLIER\n#    define PS2_MOUSE_V_MULTIPLIER 1\n#endif\n/* For some mice this will need to be 0x0F */\n#ifndef PS2_MOUSE_SCROLL_MASK\n#    define PS2_MOUSE_SCROLL_MASK 0xFF\n#endif\n#ifndef PS2_MOUSE_INIT_DELAY\n#    define PS2_MOUSE_INIT_DELAY 1000\n#endif\n\nenum ps2_mouse_command_e {\n    PS2_MOUSE_RESET                  = 0xFF,\n    PS2_MOUSE_RESEND                 = 0xFE,\n    PS2_MOUSE_SET_DEFAULTS           = 0xF6,\n    PS2_MOUSE_DISABLE_DATA_REPORTING = 0xF5,\n    PS2_MOUSE_ENABLE_DATA_REPORTING  = 0xF4,\n    PS2_MOUSE_SET_SAMPLE_RATE        = 0xF3,\n    PS2_MOUSE_GET_DEVICE_ID          = 0xF2,\n    PS2_MOUSE_SET_REMOTE_MODE        = 0xF0,\n    PS2_MOUSE_SET_WRAP_MODE          = 0xEC,\n    PS2_MOUSE_READ_DATA              = 0xEB,\n    PS2_MOUSE_SET_STREAM_MODE        = 0xEA,\n    PS2_MOUSE_STATUS_REQUEST         = 0xE9,\n    PS2_MOUSE_SET_RESOLUTION         = 0xE8,\n    PS2_MOUSE_SET_SCALING_2_1        = 0xE7,\n    PS2_MOUSE_SET_SCALING_1_1        = 0xE6,\n};\n\ntypedef enum ps2_mouse_resolution_e {\n    PS2_MOUSE_1_COUNT_MM,\n    PS2_MOUSE_2_COUNT_MM,\n    PS2_MOUSE_4_COUNT_MM,\n    PS2_MOUSE_8_COUNT_MM,\n} ps2_mouse_resolution_t;\n\ntypedef enum ps2_mouse_sample_rate_e {\n    PS2_MOUSE_10_SAMPLES_SEC  = 10,\n    PS2_MOUSE_20_SAMPLES_SEC  = 20,\n    PS2_MOUSE_40_SAMPLES_SEC  = 40,\n    PS2_MOUSE_60_SAMPLES_SEC  = 60,\n    PS2_MOUSE_80_SAMPLES_SEC  = 80,\n    PS2_MOUSE_100_SAMPLES_SEC = 100,\n    PS2_MOUSE_200_SAMPLES_SEC = 200,\n} ps2_mouse_sample_rate_t;\n\nvoid ps2_mouse_init(void);\n\nvoid ps2_mouse_init_user(void);\n\nvoid ps2_mouse_task(void);\n\nvoid ps2_mouse_disable_data_reporting(void);\n\nvoid ps2_mouse_enable_data_reporting(void);\n\nvoid ps2_mouse_set_remote_mode(void);\n\nvoid ps2_mouse_set_stream_mode(void);\n\nvoid ps2_mouse_set_scaling_2_1(void);\n\nvoid ps2_mouse_set_scaling_1_1(void);\n\nvoid ps2_mouse_set_resolution(ps2_mouse_resolution_t resolution);\n\nvoid ps2_mouse_set_sample_rate(ps2_mouse_sample_rate_t sample_rate);\n\nvoid ps2_mouse_moved_user(report_mouse_t *mouse_report);\n"
  },
  {
    "path": "drivers/sensors/adns5050.c",
    "content": "/* Copyright 2021 Colin Lam (Ploopy Corporation)\n * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n * Copyright 2019 Sunjun Kim\n * Copyright 2019 Hiroyuki Okada\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"adns5050.h\"\n#include \"wait.h\"\n#include \"debug.h\"\n#include \"gpio.h\"\n#include \"pointing_device_internal.h\"\n\n// Registers\n// clang-format off\n#define REG_PRODUCT_ID     0x00\n#define REG_REVISION_ID    0x01\n#define REG_MOTION         0x02\n#define REG_DELTA_X        0x03\n#define REG_DELTA_Y        0x04\n#define REG_SQUAL          0x05\n#define REG_SHUTTER_UPPER  0x06\n#define REG_SHUTTER_LOWER  0x07\n#define REG_MAXIMUM_PIXEL  0x08\n#define REG_PIXEL_SUM      0x09\n#define REG_MINIMUM_PIXEL  0x0a\n#define REG_PIXEL_GRAB     0x0b\n#define REG_MOUSE_CONTROL  0x0d\n#define REG_MOUSE_CONTROL2 0x19\n#define REG_LED_DC_MODE    0x22\n#define REG_CHIP_RESET     0x3a\n#define REG_PRODUCT_ID2    0x3e\n#define REG_INV_REV_ID     0x3f\n#define REG_MOTION_BURST   0x63\n// clang-format on\n\nconst pointing_device_driver_t adns5050_pointing_device_driver = {\n    .init       = adns5050_init,\n    .get_report = adns5050_get_report,\n    .set_cpi    = adns5050_set_cpi,\n    .get_cpi    = adns5050_get_cpi,\n};\n\nstatic bool powered_down = false;\n\nvoid adns5050_init(void) {\n    // Initialize the ADNS serial pins.\n    gpio_set_pin_output(ADNS5050_SCLK_PIN);\n    gpio_set_pin_output(ADNS5050_SDIO_PIN);\n    gpio_set_pin_output(ADNS5050_CS_PIN);\n\n    // reboot the adns.\n    // if the adns hasn't initialized yet, this is harmless.\n    adns5050_write_reg(REG_CHIP_RESET, 0x5a);\n\n    // wait maximum time before adns is ready.\n    // this ensures that the adns is actuall ready after reset.\n    wait_ms(55);\n\n    powered_down = false;\n\n    // read a burst from the adns and then discard it.\n    // gets the adns ready for write commands\n    // (for example, setting the dpi).\n    adns5050_read_burst();\n}\n\n// Perform a synchronization with the ADNS.\n// Just as with the serial protocol, this is used by the slave to send a\n// synchronization signal to the master.\nvoid adns5050_sync(void) {\n    gpio_write_pin_low(ADNS5050_CS_PIN);\n    wait_us(1);\n    gpio_write_pin_high(ADNS5050_CS_PIN);\n}\n\nvoid adns5050_cs_select(void) {\n    gpio_write_pin_low(ADNS5050_CS_PIN);\n}\n\nvoid adns5050_cs_deselect(void) {\n    gpio_write_pin_high(ADNS5050_CS_PIN);\n}\n\nuint8_t adns5050_serial_read(void) {\n    gpio_set_pin_input(ADNS5050_SDIO_PIN);\n    uint8_t byte = 0;\n\n    for (uint8_t i = 0; i < 8; ++i) {\n        gpio_write_pin_low(ADNS5050_SCLK_PIN);\n        wait_us(1);\n\n        byte = (byte << 1) | gpio_read_pin(ADNS5050_SDIO_PIN);\n\n        gpio_write_pin_high(ADNS5050_SCLK_PIN);\n        wait_us(1);\n    }\n\n    return byte;\n}\n\nvoid adns5050_serial_write(uint8_t data) {\n    gpio_set_pin_output(ADNS5050_SDIO_PIN);\n\n    for (int8_t b = 7; b >= 0; b--) {\n        gpio_write_pin_low(ADNS5050_SCLK_PIN);\n\n        if (data & (1 << b))\n            gpio_write_pin_high(ADNS5050_SDIO_PIN);\n        else\n            gpio_write_pin_low(ADNS5050_SDIO_PIN);\n\n        wait_us(2);\n\n        gpio_write_pin_high(ADNS5050_SCLK_PIN);\n    }\n\n    // tSWR. See page 15 of the ADNS spec sheet.\n    // Technically, this is only necessary if the next operation is an SDIO\n    // read. This is not guaranteed to be the case, but we're being lazy.\n    wait_us(4);\n\n    // Note that tSWW is never necessary. All write operations require at\n    // least 32us, which exceeds tSWW, so there's never a need to wait for it.\n}\n\n// Read a byte of data from a register on the ADNS.\n// Don't forget to use the register map (as defined in the header file).\nuint8_t adns5050_read_reg(uint8_t reg_addr) {\n    adns5050_cs_select();\n\n    adns5050_serial_write(reg_addr);\n\n    // We don't need a minimum tSRAD here. That's because a 4ms wait time is\n    // already included in adns5050_serial_write(), so we're good.\n    // See page 10 and 15 of the ADNS spec sheet.\n    // wait_us(4);\n\n    uint8_t byte = adns5050_serial_read();\n\n    // tSRW & tSRR. See page 15 of the ADNS spec sheet.\n    // Technically, this is only necessary if the next operation is an SDIO\n    // read or write. This is not guaranteed to be the case.\n    // Honestly, this wait could probably be removed.\n    wait_us(1);\n\n    adns5050_cs_deselect();\n\n    return byte;\n}\n\nvoid adns5050_write_reg(uint8_t reg_addr, uint8_t data) {\n    adns5050_cs_select();\n    adns5050_serial_write(0b10000000 | reg_addr);\n    adns5050_serial_write(data);\n    adns5050_cs_deselect();\n}\n\nreport_adns5050_t adns5050_read_burst(void) {\n    adns5050_cs_select();\n\n    report_adns5050_t data;\n    data.dx = 0;\n    data.dy = 0;\n\n    if (powered_down) {\n        return data;\n    }\n\n    adns5050_serial_write(REG_MOTION_BURST);\n\n    // We don't need a minimum tSRAD here. That's because a 4ms wait time is\n    // already included in adns5050_serial_write(), so we're good.\n    // See page 10 and 15 of the ADNS spec sheet.\n    // wait_us(4);\n\n    uint8_t x = adns5050_serial_read();\n    uint8_t y = adns5050_serial_read();\n\n    // Burst mode returns a bunch of other shit that we don't really need.\n    // Setting CS to high ends burst mode early.\n    adns5050_cs_deselect();\n\n    data.dx = convert_twoscomp(x);\n    data.dy = convert_twoscomp(y);\n\n    return data;\n}\n\n// Convert a two's complement byte from an unsigned data type into a signed\n// data type.\nint8_t convert_twoscomp(uint8_t data) {\n    if ((data & 0x80) == 0x80)\n        return -128 + (data & 0x7F);\n    else\n        return data;\n}\n\n// Don't forget to use the definitions for CPI in the header file.\nvoid adns5050_set_cpi(uint16_t cpi) {\n    uint8_t cpival = constrain((cpi / 125), 0x1, 0xD); // limits to 0--119\n\n    adns5050_write_reg(REG_MOUSE_CONTROL2, 0b10000 | cpival);\n}\n\nuint16_t adns5050_get_cpi(void) {\n    uint8_t cpival = adns5050_read_reg(REG_MOUSE_CONTROL2);\n    return (uint16_t)((cpival & 0b10000) * 125);\n}\n\nbool adns5050_check_signature(void) {\n    uint8_t pid  = adns5050_read_reg(REG_PRODUCT_ID);\n    uint8_t rid  = adns5050_read_reg(REG_REVISION_ID);\n    uint8_t pid2 = adns5050_read_reg(REG_PRODUCT_ID2);\n\n    return (pid == 0x12 && rid == 0x01 && pid2 == 0x26);\n}\n\nvoid adns5050_power_down(void) {\n    if (!powered_down) {\n        powered_down = true;\n        adns5050_write_reg(REG_MOUSE_CONTROL, 0b10);\n    }\n}\n\nreport_mouse_t adns5050_get_report(report_mouse_t mouse_report) {\n    report_adns5050_t data = adns5050_read_burst();\n\n    if (data.dx != 0 || data.dy != 0) {\n        pd_dprintf(\"Raw ] X: %d, Y: %d\\n\", data.dx, data.dy);\n        mouse_report.x = (mouse_xy_report_t)data.dx;\n        mouse_report.y = (mouse_xy_report_t)data.dy;\n    }\n\n    return mouse_report;\n}\n"
  },
  {
    "path": "drivers/sensors/adns5050.h",
    "content": "/* Copyright 2021 Colin Lam (Ploopy Corporation)\n * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n * Copyright 2019 Sunjun Kim\n * Copyright 2019 Hiroyuki Okada\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"pointing_device.h\"\n\n// CPI values\n// clang-format off\n#define CPI125  0x11\n#define CPI250  0x12\n#define CPI375  0x13\n#define CPI500  0x14\n#define CPI625  0x15\n#define CPI750  0x16\n#define CPI875  0x17\n#define CPI1000 0x18\n#define CPI1125 0x19\n#define CPI1250 0x1a\n#define CPI1375 0x1b\n// clang-format on\n\n#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))\n\n// Definitions for the ADNS serial line.\n#ifndef ADNS5050_SCLK_PIN\n#    ifdef POINTING_DEVICE_SCLK_PIN\n#        define ADNS5050_SCLK_PIN POINTING_DEVICE_SCLK_PIN\n#    else\n#        error \"No clock pin defined -- missing POINTING_DEVICE_SCLK_PIN or ADNS5050_SCLK_PIN\"\n#    endif\n#endif\n\n#ifndef ADNS5050_SDIO_PIN\n#    ifdef POINTING_DEVICE_SDIO_PIN\n#        define ADNS5050_SDIO_PIN POINTING_DEVICE_SDIO_PIN\n#    else\n#        error \"No data pin defined -- missing POINTING_DEVICE_SDIO_PIN or ADNS5050_SDIO_PIN\"\n#    endif\n#endif\n\n#ifndef ADNS5050_CS_PIN\n#    ifdef POINTING_DEVICE_CS_PIN\n#        define ADNS5050_CS_PIN POINTING_DEVICE_CS_PIN\n#    else\n#        error \"No chip select pin defined -- missing POINTING_DEVICE_CS_PIN or ADNS5050_CS_PIN define\"\n#    endif\n#endif\n\ntypedef struct {\n    int8_t dx;\n    int8_t dy;\n} report_adns5050_t;\n\nconst pointing_device_driver_t adns5050_pointing_device_driver;\n\n// A bunch of functions to implement the ADNS5050-specific serial protocol.\n// Note that the \"serial.h\" driver is insufficient, because it does not\n// manually manipulate a serial clock signal.\nvoid              adns5050_init(void);\nvoid              adns5050_sync(void);\nuint8_t           adns5050_serial_read(void);\nvoid              adns5050_serial_write(uint8_t data);\nuint8_t           adns5050_read_reg(uint8_t reg_addr);\nvoid              adns5050_write_reg(uint8_t reg_addr, uint8_t data);\nreport_adns5050_t adns5050_read_burst(void);\nvoid              adns5050_set_cpi(uint16_t cpi);\nuint16_t          adns5050_get_cpi(void);\nint8_t            convert_twoscomp(uint8_t data);\nbool              adns5050_check_signature(void);\nvoid              adns5050_power_down(void);\nreport_mouse_t    adns5050_get_report(report_mouse_t mouse_report);\n"
  },
  {
    "path": "drivers/sensors/adns9800.c",
    "content": "/* Copyright 2020 Alexander Tulloh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"spi_master.h\"\n#include \"adns9800.h\"\n#include \"wait.h\"\n\n// registers\n// clang-format off\n#define REG_Product_ID                   0x00\n#define REG_Revision_ID                  0x01\n#define REG_Motion                       0x02\n#define REG_Delta_X_L                    0x03\n#define REG_Delta_X_H                    0x04\n#define REG_Delta_Y_L                    0x05\n#define REG_Delta_Y_H                    0x06\n#define REG_SQUAL                        0x07\n#define REG_Pixel_Sum                    0x08\n#define REG_Maximum_Pixel                0x09\n#define REG_Minimum_Pixel                0x0a\n#define REG_Shutter_Lower                0x0b\n#define REG_Shutter_Upper                0x0c\n#define REG_Frame_Period_Lower           0x0d\n#define REG_Frame_Period_Upper           0x0e\n#define REG_Configuration_I              0x0f\n#define REG_Configuration_II             0x10\n#define REG_Frame_Capture                0x12\n#define REG_SROM_Enable                  0x13\n#define REG_Run_Downshift                0x14\n#define REG_Rest1_Rate                   0x15\n#define REG_Rest1_Downshift              0x16\n#define REG_Rest2_Rate                   0x17\n#define REG_Rest2_Downshift              0x18\n#define REG_Rest3_Rate                   0x19\n#define REG_Frame_Period_Max_Bound_Lower 0x1a\n#define REG_Frame_Period_Max_Bound_Upper 0x1b\n#define REG_Frame_Period_Min_Bound_Lower 0x1c\n#define REG_Frame_Period_Min_Bound_Upper 0x1d\n#define REG_Shutter_Max_Bound_Lower      0x1e\n#define REG_Shutter_Max_Bound_Upper      0x1f\n#define REG_LASER_CTRL0                  0x20\n#define REG_Observation                  0x24\n#define REG_Data_Out_Lower               0x25\n#define REG_Data_Out_Upper               0x26\n#define REG_SROM_ID                      0x2a\n#define REG_Lift_Detection_Thr           0x2e\n#define REG_Configuration_V              0x2f\n#define REG_Configuration_IV             0x39\n#define REG_Power_Up_Reset               0x3a\n#define REG_Shutdown                     0x3b\n#define REG_Inverse_Product_ID           0x3f\n#define REG_Motion_Burst                 0x50\n#define REG_SROM_Load_Burst              0x62\n#define REG_Pixel_Burst                  0x64\n\n#define MIN_CPI             200\n#define MAX_CPI             8200\n#define CPI_STEP            200\n#define CLAMP_CPI(value)    value<MIN_CPI ? MIN_CPI : value> MAX_CPI ? MAX_CPI : value\n#define US_BETWEEN_WRITES   120\n#define US_BETWEEN_READS    20\n#define US_DELAY_AFTER_ADDR 100\n#define US_BEFORE_MOTION    100\n#define MSB1                0x80\n// clang-format on\n\nconst pointing_device_driver_t adns9800_pointing_device_driver = {\n    .init       = adns9800_init,\n    .get_report = adns9800_get_report_driver,\n    .set_cpi    = adns9800_set_cpi,\n    .get_cpi    = adns9800_get_cpi,\n};\n\nuint16_t __attribute__((weak)) adns9800_srom_get_length(void) {\n    return 0;\n}\n\nuint8_t __attribute__((weak)) adns9800_srom_get_byte(uint16_t position) {\n    return 0;\n}\n\nvoid adns9800_spi_start(void) {\n    spi_start(ADNS9800_CS_PIN, false, ADNS9800_SPI_MODE, ADNS9800_SPI_DIVISOR);\n}\n\nvoid adns9800_write(uint8_t reg_addr, uint8_t data) {\n    adns9800_spi_start();\n    spi_write(reg_addr | MSB1);\n    spi_write(data);\n    spi_stop();\n    wait_us(US_BETWEEN_WRITES);\n}\n\nuint8_t adns9800_read(uint8_t reg_addr) {\n    adns9800_spi_start();\n    spi_write(reg_addr & 0x7f);\n    wait_us(US_DELAY_AFTER_ADDR);\n    uint8_t data = spi_read();\n    spi_stop();\n    wait_us(US_BETWEEN_READS);\n\n    return data;\n}\n\nvoid adns9800_init(void) {\n    gpio_set_pin_output(ADNS9800_CS_PIN);\n\n    spi_init();\n\n    // reboot\n    adns9800_write(REG_Power_Up_Reset, 0x5a);\n    wait_ms(50);\n\n    // read registers and discard\n    adns9800_read(REG_Motion);\n    adns9800_read(REG_Delta_X_L);\n    adns9800_read(REG_Delta_X_H);\n    adns9800_read(REG_Delta_Y_L);\n    adns9800_read(REG_Delta_Y_H);\n\n    if (adns9800_srom_get_length() != 0) {\n        // upload firmware\n\n        // 3k firmware mode\n        adns9800_write(REG_Configuration_IV, 0x02);\n\n        // enable initialisation\n        adns9800_write(REG_SROM_Enable, 0x1d);\n\n        // wait a frame\n        wait_ms(10);\n\n        // start SROM download\n        adns9800_write(REG_SROM_Enable, 0x18);\n\n        // write the SROM file\n\n        adns9800_spi_start();\n\n        spi_write(REG_SROM_Load_Burst | 0x80);\n        wait_us(15);\n\n        // send all bytes of the firmware\n        for (uint16_t i = 0; i < adns9800_srom_get_length(); i++) {\n            spi_write(adns9800_srom_get_byte(i));\n            wait_us(15);\n        }\n\n        spi_stop();\n\n        wait_ms(10);\n    } else {\n        // write reset value to REG_Configuration_IV\n        adns9800_write(REG_Configuration_IV, 0x0);\n\n        // write reset value to REG_SROM_Enable\n        adns9800_write(REG_SROM_Enable, 0x0);\n\n        // wait a frame\n        wait_ms(10);\n    }\n\n    // enable laser\n    uint8_t laser_ctrl0 = adns9800_read(REG_LASER_CTRL0);\n    adns9800_write(REG_LASER_CTRL0, laser_ctrl0 & 0xf0);\n\n    adns9800_set_cpi(ADNS9800_CPI);\n}\n\nconfig_adns9800_t adns9800_get_config(void) {\n    uint8_t cpival = adns9800_read(REG_Configuration_I);\n    return (config_adns9800_t){(cpival & 0xFF) * CPI_STEP};\n}\n\nvoid adns9800_set_config(config_adns9800_t config) {\n    uint8_t config_1 = (CLAMP_CPI(config.cpi) / CPI_STEP) & 0xFF;\n    adns9800_write(REG_Configuration_I, config_1);\n}\n\nuint16_t adns9800_get_cpi(void) {\n    uint8_t cpival = adns9800_read(REG_Configuration_I);\n    return (uint16_t)(cpival & 0xFF) * CPI_STEP;\n}\n\nvoid adns9800_set_cpi(uint16_t cpi) {\n    uint8_t config_1 = (CLAMP_CPI(cpi) / CPI_STEP) & 0xFF;\n    adns9800_write(REG_Configuration_I, config_1);\n}\n\nstatic int16_t convertDeltaToInt(uint8_t high, uint8_t low) {\n    // join bytes into twos compliment\n    uint16_t twos_comp = (high << 8) | low;\n\n    // convert twos comp to int\n    if (twos_comp & 0x8000) return -1 * (~twos_comp + 1);\n\n    return twos_comp;\n}\n\nreport_adns9800_t adns9800_get_report(void) {\n    report_adns9800_t report = {0};\n\n    adns9800_spi_start();\n\n    // start burst mode\n    spi_write(REG_Motion_Burst & 0x7f);\n\n    wait_us(US_BEFORE_MOTION);\n\n    uint8_t motion = spi_read();\n\n    if (motion & 0x80) {\n        // clear observation register\n        spi_read();\n\n        // delta registers\n        uint8_t delta_x_l = spi_read();\n        uint8_t delta_x_h = spi_read();\n        uint8_t delta_y_l = spi_read();\n        uint8_t delta_y_h = spi_read();\n\n        report.x = convertDeltaToInt(delta_x_h, delta_x_l);\n        report.y = convertDeltaToInt(delta_y_h, delta_y_l);\n    }\n\n    // clear residual motion\n    spi_write(REG_Motion & 0x7f);\n\n    spi_stop();\n\n    return report;\n}\n\nreport_mouse_t adns9800_get_report_driver(report_mouse_t mouse_report) {\n    report_adns9800_t sensor_report = adns9800_get_report();\n\n    mouse_report.x = CONSTRAIN_HID_XY(sensor_report.x);\n    mouse_report.y = CONSTRAIN_HID_XY(sensor_report.y);\n\n    return mouse_report;\n}\n"
  },
  {
    "path": "drivers/sensors/adns9800.h",
    "content": "/* Copyright 2020 Alexander Tulloh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include \"pointing_device.h\"\n\n#ifndef ADNS9800_CPI\n#    define ADNS9800_CPI 1600\n#endif\n\n#ifndef ADNS9800_CLOCK_SPEED\n#    define ADNS9800_CLOCK_SPEED 2000000\n#endif\n\n#ifndef ADNS9800_SPI_LSBFIRST\n#    define ADNS9800_SPI_LSBFIRST false\n#endif\n\n#ifndef ADNS9800_SPI_MODE\n#    define ADNS9800_SPI_MODE 3\n#endif\n\n#ifndef ADNS9800_SPI_DIVISOR\n#    ifdef __AVR__\n#        define ADNS9800_SPI_DIVISOR (F_CPU / ADNS9800_CLOCK_SPEED)\n#    else\n#        define ADNS9800_SPI_DIVISOR 64\n#    endif\n#endif\n\n#ifndef ADNS9800_CS_PIN\n#    ifdef POINTING_DEVICE_CS_PIN\n#        define ADNS9800_CS_PIN POINTING_DEVICE_CS_PIN\n#    else\n#        error \"No chip select pin defined -- missing POINTING_DEVICE_CS_PIN or ADNS9800_CS_PIN\"\n#    endif\n#endif\n\ntypedef struct {\n    /* 200 - 8200 CPI supported */\n    uint16_t cpi;\n} config_adns9800_t;\n\ntypedef struct {\n    int16_t x;\n    int16_t y;\n} report_adns9800_t;\n\nconst pointing_device_driver_t adns9800_pointing_device_driver;\n\nvoid              adns9800_init(void);\nconfig_adns9800_t adns9800_get_config(void);\nvoid              adns9800_set_config(config_adns9800_t);\nuint16_t          adns9800_get_cpi(void);\nvoid              adns9800_set_cpi(uint16_t cpi);\n/* Reads and clears the current delta values on the ADNS sensor */\nreport_adns9800_t adns9800_get_report(void);\nreport_mouse_t    adns9800_get_report_driver(report_mouse_t mouse_report);\n"
  },
  {
    "path": "drivers/sensors/analog_joystick.c",
    "content": "/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"analog_joystick.h\"\n#include \"analog.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n#include \"timer.h\"\n#include <stdlib.h>\n#include \"pointing_device_internal.h\"\n\nconst pointing_device_driver_t analog_joystick_pointing_device_driver = {\n    .init       = analog_joystick_init,\n    .get_report = analog_joystick_get_report,\n    .set_cpi    = NULL,\n    .get_cpi    = NULL,\n};\n\n// Set Parameters\n#ifndef ANALOG_JOYSTICK_AUTO_AXIS\nuint16_t minAxisValue = ANALOG_JOYSTICK_AXIS_MIN;\nuint16_t maxAxisValue = ANALOG_JOYSTICK_AXIS_MAX;\n#else\nint16_t minAxisValues[2];\nint16_t maxAxisValues[2];\n#endif\n\nuint8_t maxCursorSpeed = ANALOG_JOYSTICK_SPEED_MAX;\nuint8_t speedRegulator = ANALOG_JOYSTICK_SPEED_REGULATOR; // Lower Values Create Faster Movement\n\n#ifdef ANALOG_JOYSTICK_WEIGHTS\nint8_t weights[101] = ANALOG_JOYSTICK_WEIGHTS;\n#endif\n\nint16_t xOrigin, yOrigin;\n\nuint16_t lastCursor = 0;\n\nuint8_t prevValues[2] = {0, 0};\n\nint16_t axisCoordinate(pin_t pin, uint16_t origin, uint8_t axis) {\n    int8_t  direction;\n    int16_t distanceFromOrigin;\n    int16_t range;\n\n    int16_t position = analogReadPin(pin);\n\n    if (origin == position) {\n        return 0;\n    } else if (origin > position) {\n        distanceFromOrigin = origin - position;\n#ifdef ANALOG_JOYSTICK_AUTO_AXIS\n        if (position < minAxisValues[axis]) {\n            minAxisValues[axis] = position;\n        }\n        range = origin - minAxisValues[axis];\n#else\n        range = origin - minAxisValue;\n#endif\n        direction = -1;\n    } else {\n        distanceFromOrigin = position - origin;\n\n#ifdef ANALOG_JOYSTICK_AUTO_AXIS\n        if (position > maxAxisValues[axis]) {\n            maxAxisValues[axis] = position;\n        }\n        range = maxAxisValues[axis] - origin;\n#else\n        range = maxAxisValue - origin;\n#endif\n        direction = 1;\n    }\n\n    float   percent    = (float)distanceFromOrigin / range;\n    int16_t coordinate = (int16_t)(percent * 100);\n    if (coordinate < 0) {\n        return 0;\n    } else if (coordinate > 100) {\n        return 100 * direction;\n    } else {\n        return coordinate * direction;\n    }\n}\n\nint8_t axisToMouseComponent(pin_t pin, int16_t origin, uint8_t maxSpeed, uint8_t axis) {\n    int16_t coordinate = axisCoordinate(pin, origin, axis);\n    int8_t  result;\n#ifndef ANALOG_JOYSTICK_WEIGHTS\n    if (coordinate != 0) {\n        float percent = (float)coordinate / 100;\n        result        = percent * maxCursorSpeed * (abs(coordinate) / speedRegulator);\n    } else {\n        return 0;\n    }\n#else\n    result = weights[abs(coordinate)] * (coordinate < 0 ? -1 : 1) * maxCursorSpeed / speedRegulator;\n#endif\n\n#ifdef ANALOG_JOYSTICK_CUTOFF\n    uint8_t pv       = prevValues[axis];\n    prevValues[axis] = abs(result);\n    if (pv > abs(result)) {\n        return 0;\n    }\n#endif\n\n    return result;\n}\n\nreport_analog_joystick_t analog_joystick_read(void) {\n    report_analog_joystick_t report = {0};\n\n    if (timer_elapsed(lastCursor) > ANALOG_JOYSTICK_READ_INTERVAL) {\n        lastCursor = timer_read();\n        report.x   = axisToMouseComponent(ANALOG_JOYSTICK_X_AXIS_PIN, xOrigin, maxCursorSpeed, 0);\n        report.y   = axisToMouseComponent(ANALOG_JOYSTICK_Y_AXIS_PIN, yOrigin, maxCursorSpeed, 1);\n    }\n#ifdef ANALOG_JOYSTICK_CLICK_PIN\n    report.button = !gpio_read_pin(ANALOG_JOYSTICK_CLICK_PIN);\n#endif\n    return report;\n}\n\nvoid analog_joystick_init(void) {\n    gpio_set_pin_input_high(ANALOG_JOYSTICK_X_AXIS_PIN);\n    gpio_set_pin_input_high(ANALOG_JOYSTICK_Y_AXIS_PIN);\n\n#ifdef ANALOG_JOYSTICK_CLICK_PIN\n    gpio_set_pin_input_high(ANALOG_JOYSTICK_CLICK_PIN);\n#endif\n    // Account for drift\n    xOrigin = analogReadPin(ANALOG_JOYSTICK_X_AXIS_PIN);\n    yOrigin = analogReadPin(ANALOG_JOYSTICK_Y_AXIS_PIN);\n\n#ifdef ANALOG_JOYSTICK_AUTO_AXIS\n    minAxisValues[0] = xOrigin - 100;\n    minAxisValues[1] = yOrigin - 100;\n    maxAxisValues[0] = xOrigin + 100;\n    maxAxisValues[1] = yOrigin + 100;\n#endif\n}\n\nreport_mouse_t analog_joystick_get_report(report_mouse_t mouse_report) {\n    report_analog_joystick_t data = analog_joystick_read();\n\n    pd_dprintf(\"Raw ] X: %d, Y: %d\\n\", data.x, data.y);\n\n    mouse_report.x = data.x;\n    mouse_report.y = data.y;\n\n    mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, data.button, POINTING_DEVICE_BUTTON1);\n\n    return mouse_report;\n}\n"
  },
  {
    "path": "drivers/sensors/analog_joystick.h",
    "content": "/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"pointing_device.h\"\n\n#ifndef ANALOG_JOYSTICK_X_AXIS_PIN\n#    error No pin specified for X Axis\n#endif\n#ifndef ANALOG_JOYSTICK_Y_AXIS_PIN\n#    error No pin specified for Y Axis\n#endif\n\n#ifndef ANALOG_JOYSTICK_AXIS_MIN\n#    define ANALOG_JOYSTICK_AXIS_MIN 0\n#endif\n#ifndef ANALOG_JOYSTICK_AXIS_MAX\n#    define ANALOG_JOYSTICK_AXIS_MAX 1023\n#endif\n#ifndef ANALOG_JOYSTICK_SPEED_REGULATOR\n#    define ANALOG_JOYSTICK_SPEED_REGULATOR 20\n#endif\n#ifndef ANALOG_JOYSTICK_READ_INTERVAL\n#    define ANALOG_JOYSTICK_READ_INTERVAL 10\n#endif\n#ifndef ANALOG_JOYSTICK_SPEED_MAX\n#    define ANALOG_JOYSTICK_SPEED_MAX 2\n#endif\n\nconst pointing_device_driver_t analog_joystick_pointing_device_driver;\n\ntypedef struct {\n    int8_t x;\n    int8_t y;\n    bool   button;\n} report_analog_joystick_t;\nreport_analog_joystick_t analog_joystick_read(void);\nvoid                     analog_joystick_init(void);\nreport_mouse_t           analog_joystick_get_report(report_mouse_t mouse_report);\n"
  },
  {
    "path": "drivers/sensors/azoteq_iqs5xx.c",
    "content": "// Copyright 2023 Dasky (@daskygit)\n// Copyright 2023 George Norton (@george-norton)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"azoteq_iqs5xx.h\"\n#include \"pointing_device_internal.h\"\n#include \"wait.h\"\n\n#ifndef AZOTEQ_IQS5XX_ADDRESS\n#    define AZOTEQ_IQS5XX_ADDRESS (0x74 << 1)\n#endif\n#ifndef AZOTEQ_IQS5XX_TIMEOUT_MS\n#    define AZOTEQ_IQS5XX_TIMEOUT_MS 10\n#endif\n\n#define AZOTEQ_IQS5XX_REG_PRODUCT_NUMBER 0x0000\n#define AZOTEQ_IQS5XX_REG_PREVIOUS_CYCLE_TIME 0x000C\n#define AZOTEQ_IQS5XX_REG_SYSTEM_CONTROL_1 0x0432\n#define AZOTEQ_IQS5XX_REG_REPORT_RATE_ACTIVE 0x057A\n#define AZOTEQ_IQS5XX_REG_IDLE_MODE_TIMEOUT 0x0586\n#define AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_0 0x058E\n#define AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_1 0x058F\n#define AZOTEQ_IQS5XX_REG_X_RESOLUTION 0x066E\n#define AZOTEQ_IQS5XX_REG_XY_CONFIG_0 0x0669\n#define AZOTEQ_IQS5XX_REG_Y_RESOLUTION 0x0670\n#define AZOTEQ_IQS5XX_REG_SINGLE_FINGER_GESTURES 0x06B7\n#define AZOTEQ_IQS5XX_REG_END_COMMS 0xEEEE\n\n// Gesture configuration\n#ifndef AZOTEQ_IQS5XX_TAP_ENABLE\n#    define AZOTEQ_IQS5XX_TAP_ENABLE true\n#endif\n#ifndef AZOTEQ_IQS5XX_PRESS_AND_HOLD_ENABLE\n#    define AZOTEQ_IQS5XX_PRESS_AND_HOLD_ENABLE false\n#endif\n#ifndef AZOTEQ_IQS5XX_TWO_FINGER_TAP_ENABLE\n#    define AZOTEQ_IQS5XX_TWO_FINGER_TAP_ENABLE true\n#endif\n#ifndef AZOTEQ_IQS5XX_SCROLL_ENABLE\n#    define AZOTEQ_IQS5XX_SCROLL_ENABLE true\n#endif\n#ifndef AZOTEQ_IQS5XX_SWIPE_X_ENABLE\n#    define AZOTEQ_IQS5XX_SWIPE_X_ENABLE false\n#endif\n#ifndef AZOTEQ_IQS5XX_SWIPE_Y_ENABLE\n#    define AZOTEQ_IQS5XX_SWIPE_Y_ENABLE false\n#endif\n#ifndef AZOTEQ_IQS5XX_ZOOM_ENABLE\n#    define AZOTEQ_IQS5XX_ZOOM_ENABLE false\n#endif\n#ifndef AZOTEQ_IQS5XX_TAP_TIME\n#    define AZOTEQ_IQS5XX_TAP_TIME 0x96\n#endif\n#ifndef AZOTEQ_IQS5XX_TAP_DISTANCE\n#    define AZOTEQ_IQS5XX_TAP_DISTANCE 0x19\n#endif\n#ifndef AZOTEQ_IQS5XX_HOLD_TIME\n#    define AZOTEQ_IQS5XX_HOLD_TIME 0x12C\n#endif\n#ifndef AZOTEQ_IQS5XX_SWIPE_INITIAL_TIME\n#    define AZOTEQ_IQS5XX_SWIPE_INITIAL_TIME 0x64 // 0x96\n#endif\n#ifndef AZOTEQ_IQS5XX_SWIPE_INITIAL_DISTANCE\n#    define AZOTEQ_IQS5XX_SWIPE_INITIAL_DISTANCE 0x12C\n#endif\n#ifndef AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_TIME\n#    define AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_TIME 0x0\n#endif\n#ifndef AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_DISTANCE\n#    define AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_DISTANCE 0x7D0\n#endif\n#ifndef AZOTEQ_IQS5XX_SCROLL_INITIAL_DISTANCE\n#    define AZOTEQ_IQS5XX_SCROLL_INITIAL_DISTANCE 0x32\n#endif\n#ifndef AZOTEQ_IQS5XX_ZOOM_INITIAL_DISTANCE\n#    define AZOTEQ_IQS5XX_ZOOM_INITIAL_DISTANCE 0x32\n#endif\n#ifndef AZOTEQ_IQS5XX_ZOOM_CONSECUTIVE_DISTANCE\n#    define AZOTEQ_IQS5XX_ZOOM_CONSECUTIVE_DISTANCE 0x19\n#endif\n#ifndef AZOTEQ_IQS5XX_EVENT_MODE\n// Event mode can't be used until the pointing code has changed (stuck buttons)\n#    define AZOTEQ_IQS5XX_EVENT_MODE false\n#endif\n\n#if defined(AZOTEQ_IQS5XX_TPS43)\n#    define AZOTEQ_IQS5XX_WIDTH_MM 43\n#    define AZOTEQ_IQS5XX_HEIGHT_MM 40\n#    define AZOTEQ_IQS5XX_RESOLUTION_X 2048\n#    define AZOTEQ_IQS5XX_RESOLUTION_Y 1792\n#elif defined(AZOTEQ_IQS5XX_TPS65)\n#    define AZOTEQ_IQS5XX_WIDTH_MM 65\n#    define AZOTEQ_IQS5XX_HEIGHT_MM 49\n#    define AZOTEQ_IQS5XX_RESOLUTION_X 3072\n#    define AZOTEQ_IQS5XX_RESOLUTION_Y 2048\n#elif !defined(AZOTEQ_IQS5XX_WIDTH_MM) && !defined(AZOTEQ_IQS5XX_HEIGHT_MM)\n#    error \"You must define one of the available azoteq trackpads or specify at least the width and height\"\n#endif\n\n#define DIVIDE_UNSIGNED_ROUND(numerator, denominator) (((numerator) + ((denominator) / 2)) / (denominator))\n#define AZOTEQ_IQS5XX_INCH_TO_RESOLUTION_X(inch) (DIVIDE_UNSIGNED_ROUND((inch) * (uint32_t)AZOTEQ_IQS5XX_WIDTH_MM * 10, 254))\n#define AZOTEQ_IQS5XX_RESOLUTION_X_TO_INCH(px) (DIVIDE_UNSIGNED_ROUND((px) * (uint32_t)254, AZOTEQ_IQS5XX_WIDTH_MM * 10))\n#define AZOTEQ_IQS5XX_INCH_TO_RESOLUTION_Y(inch) (DIVIDE_UNSIGNED_ROUND((inch) * (uint32_t)AZOTEQ_IQS5XX_HEIGHT_MM * 10, 254))\n#define AZOTEQ_IQS5XX_RESOLUTION_Y_TO_INCH(px) (DIVIDE_UNSIGNED_ROUND((px) * (uint32_t)254, AZOTEQ_IQS5XX_HEIGHT_MM * 10))\n\nconst pointing_device_driver_t azoteq_iqs5xx_pointing_device_driver = {\n    .init       = azoteq_iqs5xx_init,\n    .get_report = azoteq_iqs5xx_get_report,\n    .set_cpi    = azoteq_iqs5xx_set_cpi,\n    .get_cpi    = azoteq_iqs5xx_get_cpi,\n};\n\nstatic uint16_t azoteq_iqs5xx_product_number = AZOTEQ_IQS5XX_UNKNOWN;\n\nstatic struct {\n    uint16_t resolution_x;\n    uint16_t resolution_y;\n} azoteq_iqs5xx_device_resolution_t;\n\ni2c_status_t azoteq_iqs5xx_end_session(void) {\n    const uint8_t END_BYTE = 1; // any data\n    return i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_END_COMMS, &END_BYTE, 1, AZOTEQ_IQS5XX_TIMEOUT_MS);\n}\n\ni2c_status_t azoteq_iqs5xx_get_base_data(azoteq_iqs5xx_base_data_t *base_data) {\n    i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_PREVIOUS_CYCLE_TIME, (uint8_t *)base_data, 10, AZOTEQ_IQS5XX_TIMEOUT_MS);\n    if (status == I2C_STATUS_SUCCESS) {\n        azoteq_iqs5xx_end_session();\n    }\n    return status;\n}\n\ni2c_status_t azoteq_iqs5xx_get_report_rate(azoteq_iqs5xx_report_rate_t *report_rate, azoteq_iqs5xx_charging_modes_t mode, bool end_session) {\n    if (mode > AZOTEQ_IQS5XX_LP2) {\n        pd_dprintf(\"IQS5XX - Invalid mode for get report rate.\\n\");\n        return I2C_STATUS_ERROR;\n    }\n    uint16_t     selected_reg = AZOTEQ_IQS5XX_REG_REPORT_RATE_ACTIVE + (2 * mode);\n    i2c_status_t status       = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, selected_reg, (uint8_t *)report_rate, 2, AZOTEQ_IQS5XX_TIMEOUT_MS);\n    if (end_session) {\n        azoteq_iqs5xx_end_session();\n    }\n    return status;\n}\n\ni2c_status_t azoteq_iqs5xx_set_report_rate(uint16_t report_rate_ms, azoteq_iqs5xx_charging_modes_t mode, bool end_session) {\n    if (mode > AZOTEQ_IQS5XX_LP2) {\n        pd_dprintf(\"IQS5XX - Invalid mode for set report rate.\\n\");\n        return I2C_STATUS_ERROR;\n    }\n    uint16_t                    selected_reg = AZOTEQ_IQS5XX_REG_REPORT_RATE_ACTIVE + (2 * mode);\n    azoteq_iqs5xx_report_rate_t report_rate  = {0};\n    report_rate.h                            = (uint8_t)((report_rate_ms >> 8) & 0xFF);\n    report_rate.l                            = (uint8_t)(report_rate_ms & 0xFF);\n    i2c_status_t status                      = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, selected_reg, (uint8_t *)&report_rate, 2, AZOTEQ_IQS5XX_TIMEOUT_MS);\n    if (end_session) {\n        azoteq_iqs5xx_end_session();\n    }\n    return status;\n}\n\ni2c_status_t azoteq_iqs5xx_set_reati(bool enabled, bool end_session) {\n    azoteq_iqs5xx_system_config_0_t config = {0};\n    i2c_status_t                    status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    if (status == I2C_STATUS_SUCCESS) {\n        config.reati = enabled;\n        status       = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    }\n    if (end_session) {\n        azoteq_iqs5xx_end_session();\n    }\n    return status;\n}\n\ni2c_status_t azoteq_iqs5xx_set_event_mode(bool enabled, bool end_session) {\n    azoteq_iqs5xx_system_config_1_t config = {0};\n    i2c_status_t                    status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    if (status == I2C_STATUS_SUCCESS) {\n        config.event_mode     = enabled;\n        config.touch_event    = true;\n        config.tp_event       = true;\n        config.prox_event     = false;\n        config.snap_event     = false;\n        config.reati_event    = false;\n        config.alp_prox_event = false;\n        config.gesture_event  = true;\n        status                = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    }\n    if (end_session) {\n        azoteq_iqs5xx_end_session();\n    }\n    return status;\n}\n\ni2c_status_t azoteq_iqs5xx_set_gesture_config(bool end_session) {\n    azoteq_iqs5xx_gesture_config_t config = {0};\n    i2c_status_t                   status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SINGLE_FINGER_GESTURES, (uint8_t *)&config, sizeof(azoteq_iqs5xx_gesture_config_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    pd_dprintf(\"azo scroll: %d\\n\", config.multi_finger_gestures.scroll);\n    if (status == I2C_STATUS_SUCCESS) {\n        config.single_finger_gestures.single_tap     = AZOTEQ_IQS5XX_TAP_ENABLE;\n        config.single_finger_gestures.press_and_hold = AZOTEQ_IQS5XX_PRESS_AND_HOLD_ENABLE;\n        config.single_finger_gestures.swipe_x_plus   = AZOTEQ_IQS5XX_SWIPE_X_ENABLE;\n        config.single_finger_gestures.swipe_x_minus  = AZOTEQ_IQS5XX_SWIPE_X_ENABLE;\n        config.single_finger_gestures.swipe_y_plus   = AZOTEQ_IQS5XX_SWIPE_Y_ENABLE;\n        config.single_finger_gestures.swipe_y_minus  = AZOTEQ_IQS5XX_SWIPE_Y_ENABLE;\n        config.multi_finger_gestures.two_finger_tap  = AZOTEQ_IQS5XX_TWO_FINGER_TAP_ENABLE;\n        config.multi_finger_gestures.scroll          = AZOTEQ_IQS5XX_SCROLL_ENABLE;\n        config.multi_finger_gestures.zoom            = AZOTEQ_IQS5XX_ZOOM_ENABLE;\n        config.tap_time                              = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_TAP_TIME);\n        config.tap_distance                          = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_TAP_DISTANCE);\n        config.hold_time                             = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_HOLD_TIME);\n        config.swipe_initial_time                    = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_SWIPE_INITIAL_TIME);\n        config.swipe_initial_distance                = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_SWIPE_INITIAL_DISTANCE);\n        config.swipe_consecutive_time                = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_TIME);\n        config.swipe_consecutive_distance            = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_DISTANCE);\n        config.scroll_initial_distance               = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_SCROLL_INITIAL_DISTANCE);\n        config.zoom_initial_distance                 = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_ZOOM_INITIAL_DISTANCE);\n        config.zoom_consecutive_distance             = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_ZOOM_CONSECUTIVE_DISTANCE);\n        status                                       = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SINGLE_FINGER_GESTURES, (uint8_t *)&config, sizeof(azoteq_iqs5xx_gesture_config_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    }\n    if (end_session) {\n        azoteq_iqs5xx_end_session();\n    }\n    return status;\n}\n\ni2c_status_t azoteq_iqs5xx_set_xy_config(bool flip_x, bool flip_y, bool switch_xy, bool palm_reject, bool end_session) {\n    azoteq_iqs5xx_xy_config_0_t config = {0};\n    i2c_status_t                status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_XY_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_xy_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    if (status == I2C_STATUS_SUCCESS) {\n        if (flip_x) {\n            config.flip_x = !config.flip_x;\n        }\n        if (flip_y) {\n            config.flip_y = !config.flip_y;\n        }\n        if (switch_xy) {\n            config.switch_xy_axis = !config.switch_xy_axis;\n        }\n        config.palm_reject = palm_reject;\n        status             = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_XY_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_xy_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    }\n    if (end_session) {\n        azoteq_iqs5xx_end_session();\n    }\n    return status;\n}\n\ni2c_status_t azoteq_iqs5xx_reset_suspend(bool reset, bool suspend, bool end_session) {\n    azoteq_iqs5xx_system_control_1_t config = {0};\n    i2c_status_t                     status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONTROL_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_control_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    if (status == I2C_STATUS_SUCCESS) {\n        config.reset   = reset;\n        config.suspend = suspend;\n        status         = i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONTROL_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_control_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    }\n    if (end_session) {\n        azoteq_iqs5xx_end_session();\n    }\n    return status;\n}\n\nvoid azoteq_iqs5xx_set_cpi(uint16_t cpi) {\n    if (azoteq_iqs5xx_product_number != AZOTEQ_IQS5XX_UNKNOWN) {\n        azoteq_iqs5xx_resolution_t resolution = {0};\n        resolution.x_resolution               = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(MIN(azoteq_iqs5xx_device_resolution_t.resolution_x, AZOTEQ_IQS5XX_INCH_TO_RESOLUTION_X(cpi)));\n        resolution.y_resolution               = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(MIN(azoteq_iqs5xx_device_resolution_t.resolution_y, AZOTEQ_IQS5XX_INCH_TO_RESOLUTION_Y(cpi)));\n        i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_X_RESOLUTION, (uint8_t *)&resolution, sizeof(azoteq_iqs5xx_resolution_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    }\n}\n\nuint16_t azoteq_iqs5xx_get_cpi(void) {\n    if (azoteq_iqs5xx_product_number != AZOTEQ_IQS5XX_UNKNOWN) {\n        azoteq_iqs5xx_resolution_t resolution = {0};\n        i2c_status_t               status     = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_X_RESOLUTION, (uint8_t *)&resolution, sizeof(azoteq_iqs5xx_resolution_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n        if (status == I2C_STATUS_SUCCESS) {\n            return AZOTEQ_IQS5XX_RESOLUTION_X_TO_INCH(AZOTEQ_IQS5XX_SWAP_H_L_BYTES(resolution.x_resolution));\n        }\n    }\n    return 0;\n}\n\nuint16_t azoteq_iqs5xx_get_product(void) {\n    i2c_status_t status = i2c_read_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_PRODUCT_NUMBER, (uint8_t *)&azoteq_iqs5xx_product_number, sizeof(uint16_t), AZOTEQ_IQS5XX_TIMEOUT_MS);\n    if (status == I2C_STATUS_SUCCESS) {\n        azoteq_iqs5xx_product_number = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(azoteq_iqs5xx_product_number);\n    }\n    pd_dprintf(\"AZOTEQ: Product number %u\\n\", azoteq_iqs5xx_product_number);\n    return azoteq_iqs5xx_product_number;\n}\n\nvoid azoteq_iqs5xx_setup_resolution(void) {\n#if !defined(AZOTEQ_IQS5XX_RESOLUTION_X) && !defined(AZOTEQ_IQS5XX_RESOLUTION_Y)\n    switch (azoteq_iqs5xx_product_number) {\n        case AZOTEQ_IQS550:\n            azoteq_iqs5xx_device_resolution_t.resolution_x = 3584;\n            azoteq_iqs5xx_device_resolution_t.resolution_y = 2304;\n            break;\n        case AZOTEQ_IQS572:\n            azoteq_iqs5xx_device_resolution_t.resolution_x = 2048;\n            azoteq_iqs5xx_device_resolution_t.resolution_y = 1792;\n            break;\n        case AZOTEQ_IQS525:\n            azoteq_iqs5xx_device_resolution_t.resolution_x = 1280;\n            azoteq_iqs5xx_device_resolution_t.resolution_y = 768;\n            break;\n        default:\n            // shouldn't be here\n            azoteq_iqs5xx_device_resolution_t.resolution_x = 0;\n            azoteq_iqs5xx_device_resolution_t.resolution_y = 0;\n            break;\n    }\n#endif\n#ifdef AZOTEQ_IQS5XX_RESOLUTION_X\n    azoteq_iqs5xx_device_resolution_t.resolution_x = AZOTEQ_IQS5XX_RESOLUTION_X;\n#endif\n#ifdef AZOTEQ_IQS5XX_RESOLUTION_Y\n    azoteq_iqs5xx_device_resolution_t.resolution_y = AZOTEQ_IQS5XX_RESOLUTION_Y;\n#endif\n}\n\nstatic i2c_status_t azoteq_iqs5xx_init_status = 1;\n\nvoid azoteq_iqs5xx_init(void) {\n    i2c_init();\n    i2c_ping_address(AZOTEQ_IQS5XX_ADDRESS, 1); // wake\n    azoteq_iqs5xx_reset_suspend(true, false, true);\n    wait_ms(100);\n    i2c_ping_address(AZOTEQ_IQS5XX_ADDRESS, 1); // wake\n    if (azoteq_iqs5xx_get_product() != AZOTEQ_IQS5XX_UNKNOWN) {\n        azoteq_iqs5xx_setup_resolution();\n        azoteq_iqs5xx_init_status = azoteq_iqs5xx_set_report_rate(AZOTEQ_IQS5XX_REPORT_RATE, AZOTEQ_IQS5XX_ACTIVE, false);\n        azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_report_rate(AZOTEQ_IQS5XX_REPORT_RATE, AZOTEQ_IQS5XX_IDLE, false);\n        azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_report_rate(AZOTEQ_IQS5XX_REPORT_RATE, AZOTEQ_IQS5XX_IDLE_TOUCH, false);\n\n        uint8_t no_timeout = 255;\n        azoteq_iqs5xx_init_status |= i2c_write_register16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_IDLE_MODE_TIMEOUT, &no_timeout, 1, AZOTEQ_IQS5XX_TIMEOUT_MS); // Don't enter LP1, LP2 states\n        azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_event_mode(AZOTEQ_IQS5XX_EVENT_MODE, false);\n        azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_reati(true, false);\n#if defined(AZOTEQ_IQS5XX_ROTATION_90)\n        azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_xy_config(false, true, true, true, false);\n#elif defined(AZOTEQ_IQS5XX_ROTATION_180)\n        azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_xy_config(true, true, false, true, false);\n#elif defined(AZOTEQ_IQS5XX_ROTATION_270)\n        azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_xy_config(true, false, true, true, false);\n#else\n        azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_xy_config(false, false, false, true, false);\n#endif\n        azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_gesture_config(true);\n        wait_ms(AZOTEQ_IQS5XX_REPORT_RATE + 1);\n    }\n};\n\nreport_mouse_t azoteq_iqs5xx_get_report(report_mouse_t mouse_report) {\n    report_mouse_t temp_report = {0};\n\n    if (azoteq_iqs5xx_init_status == I2C_STATUS_SUCCESS) {\n        azoteq_iqs5xx_base_data_t base_data       = {0};\n        i2c_status_t              status          = azoteq_iqs5xx_get_base_data(&base_data);\n        bool                      ignore_movement = false;\n\n        if (status == I2C_STATUS_SUCCESS) {\n#ifdef POINTING_DEVICE_DEBUG\n            if (base_data.previous_cycle_time > AZOTEQ_IQS5XX_REPORT_RATE) {\n                pd_dprintf(\"IQS5XX - previous cycle time missed, took: %dms\\n\", base_data.previous_cycle_time);\n            }\n#endif\n            if (base_data.gesture_events_0.single_tap || base_data.gesture_events_0.press_and_hold) {\n                pd_dprintf(\"IQS5XX - Single tap/hold.\\n\");\n                temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON1);\n            } else if (base_data.gesture_events_1.two_finger_tap) {\n                pd_dprintf(\"IQS5XX - Two finger tap.\\n\");\n                temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON2);\n            } else if (base_data.gesture_events_0.swipe_x_neg) {\n                pd_dprintf(\"IQS5XX - X-.\\n\");\n                temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON4);\n                ignore_movement     = true;\n            } else if (base_data.gesture_events_0.swipe_x_pos) {\n                pd_dprintf(\"IQS5XX - X+.\\n\");\n                temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON5);\n                ignore_movement     = true;\n            } else if (base_data.gesture_events_0.swipe_y_neg) {\n                pd_dprintf(\"IQS5XX - Y-.\\n\");\n                temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON6);\n                ignore_movement     = true;\n            } else if (base_data.gesture_events_0.swipe_y_pos) {\n                pd_dprintf(\"IQS5XX - Y+.\\n\");\n                temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON3);\n                ignore_movement     = true;\n            } else if (base_data.gesture_events_1.zoom) {\n                if (AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.x.h, base_data.x.l) < 0) {\n                    pd_dprintf(\"IQS5XX - Zoom out.\\n\");\n                    temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON7);\n                } else if (AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.x.h, base_data.x.l) > 0) {\n                    pd_dprintf(\"IQS5XX - Zoom in.\\n\");\n                    temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON8);\n                }\n            } else if (base_data.gesture_events_1.scroll) {\n                pd_dprintf(\"IQS5XX - Scroll.\\n\");\n                temp_report.h = CONSTRAIN_HID(AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.x.h, base_data.x.l));\n                temp_report.v = CONSTRAIN_HID(AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.y.h, base_data.y.l));\n            }\n            if (base_data.number_of_fingers == 1 && !ignore_movement) {\n                temp_report.x = CONSTRAIN_HID_XY(AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.x.h, base_data.x.l));\n                temp_report.y = CONSTRAIN_HID_XY(AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.y.h, base_data.y.l));\n            }\n\n        } else {\n            pd_dprintf(\"IQS5XX - get report failed, i2c status: %d \\n\", status);\n        }\n    } else {\n        pd_dprintf(\"IQS5XX - Init failed, i2c status: %d \\n\", azoteq_iqs5xx_init_status);\n    }\n\n    return temp_report;\n}\n"
  },
  {
    "path": "drivers/sensors/azoteq_iqs5xx.h",
    "content": "// Copyright 2023 Dasky (@daskygit)\n// Copyright 2023 George Norton (@george-norton)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"compiler_support.h\"\n#include \"i2c_master.h\"\n#include \"pointing_device.h\"\n#include \"util.h\"\n\ntypedef enum {\n    AZOTEQ_IQS5XX_UNKNOWN,\n    AZOTEQ_IQS550 = 40,\n    AZOTEQ_IQS525 = 52,\n    AZOTEQ_IQS572 = 58,\n} azoteq_iqs5xx_product_numbers_t;\ntypedef enum {\n    AZOTEQ_IQS5XX_ACTIVE,\n    AZOTEQ_IQS5XX_IDLE_TOUCH,\n    AZOTEQ_IQS5XX_IDLE,\n    AZOTEQ_IQS5XX_LP1,\n    AZOTEQ_IQS5XX_LP2,\n} azoteq_iqs5xx_charging_modes_t;\n\ntypedef struct {\n    uint8_t h : 8;\n    uint8_t l : 8;\n} azoteq_iqs5xx_report_rate_t;\n\ntypedef struct PACKED {\n    bool    single_tap : 1;     // Single tap gesture status\n    bool    press_and_hold : 1; // Press and hold gesture status\n    bool    swipe_x_neg : 1;    // Swipe in negative X direction status\n    bool    swipe_x_pos : 1;    // Swipe in positive X direction status\n    bool    swipe_y_pos : 1;    // Swipe in positive Y direction status\n    bool    swipe_y_neg : 1;    // Swipe in negative Y direction status\n    uint8_t _unused : 2;        // unused\n} azoteq_iqs5xx_gesture_events_0_t;\n\ntypedef struct PACKED {\n    bool    two_finger_tap : 1; // Two finger tap gesture status\n    bool    scroll : 1;         // Scroll status\n    bool    zoom : 1;           // Zoom gesture status\n    uint8_t _unused : 5;        // unused\n} azoteq_iqs5xx_gesture_events_1_t;\n\ntypedef struct PACKED {\n    azoteq_iqs5xx_charging_modes_t charging_mode : 3;      // Indicates current mode\n    bool                           ati_error : 1;          //\n    bool                           reati_occurred : 1;     //\n    bool                           alp_ati_error : 1;      //\n    bool                           alp_reati_occurred : 1; //\n    bool                           show_reset : 1;         //\n} azoteq_iqs5xx_system_info_0_t;\n\ntypedef struct PACKED {\n    bool    tp_movement : 1;      //\n    bool    palm_detect : 1;      //  Palm detect status\n    bool    too_many_fingers : 1; // Total finger status\n    bool    rr_missed : 1;        // Report rate status\n    bool    snap_toggle : 1;      // Change in any snap channel status\n    bool    switch_state : 1;     // Status of input pin SW_IN\n    uint8_t _unused : 2;          // unused\n} azoteq_iqs5xx_system_info_1_t;\n\ntypedef struct {\n    uint8_t h : 8;\n    uint8_t l : 8;\n} azoteq_iqs5xx_relative_xy_t;\n\ntypedef struct {\n    uint8_t                          previous_cycle_time;\n    azoteq_iqs5xx_gesture_events_0_t gesture_events_0;\n    azoteq_iqs5xx_gesture_events_1_t gesture_events_1;\n    azoteq_iqs5xx_system_info_0_t    system_info_0;\n    azoteq_iqs5xx_system_info_1_t    system_info_1;\n    uint8_t                          number_of_fingers;\n    azoteq_iqs5xx_relative_xy_t      x;\n    azoteq_iqs5xx_relative_xy_t      y;\n} azoteq_iqs5xx_base_data_t;\n\nSTATIC_ASSERT(sizeof(azoteq_iqs5xx_base_data_t) == 10, \"azoteq_iqs5xx_basic_report_t should be 10 bytes\");\n\ntypedef struct {\n    uint8_t                     number_of_fingers;\n    azoteq_iqs5xx_relative_xy_t x;\n    azoteq_iqs5xx_relative_xy_t y;\n} azoteq_iqs5xx_report_data_t;\n\nSTATIC_ASSERT(sizeof(azoteq_iqs5xx_report_data_t) == 5, \"azoteq_iqs5xx_report_data_t should be 5 bytes\");\n\ntypedef struct PACKED {\n    bool sw_input : 1;\n    bool sw_input_select : 1;\n    bool reati : 1;\n    bool alp_reati : 1;\n    bool sw_input_event : 1;\n    bool wdt : 1;\n    bool setup_complete : 1;\n    bool manual_control : 1;\n} azoteq_iqs5xx_system_config_0_t;\n\ntypedef struct PACKED {\n    bool event_mode : 1;\n    bool gesture_event : 1;\n    bool tp_event : 1;\n    bool reati_event : 1;\n    bool alp_prox_event : 1;\n    bool snap_event : 1;\n    bool touch_event : 1;\n    bool prox_event : 1;\n} azoteq_iqs5xx_system_config_1_t;\n\ntypedef struct PACKED {\n    bool    flip_x : 1;\n    bool    flip_y : 1;\n    bool    switch_xy_axis : 1;\n    bool    palm_reject : 1;\n    uint8_t _unused : 4;\n} azoteq_iqs5xx_xy_config_0_t;\n\ntypedef struct PACKED {\n    bool   suspend : 1;\n    bool   reset : 1;\n    int8_t _unused : 6;\n} azoteq_iqs5xx_system_control_1_t;\n\ntypedef struct PACKED {\n    bool   single_tap : 1;\n    bool   press_and_hold : 1;\n    bool   swipe_x_minus : 1;\n    bool   swipe_x_plus : 1;\n    bool   swipe_y_plus : 1;\n    bool   swipe_y_minus : 1;\n    int8_t _unused : 2;\n} azoteq_iqs5xx_single_finger_gesture_enable_t;\n\ntypedef struct PACKED {\n    bool   two_finger_tap : 1;\n    bool   scroll : 1;\n    bool   zoom : 1;\n    int8_t _unused : 5;\n} azoteq_iqs5xx_multi_finger_gesture_enable_t;\n\ntypedef struct PACKED {\n    azoteq_iqs5xx_single_finger_gesture_enable_t single_finger_gestures;\n    azoteq_iqs5xx_multi_finger_gesture_enable_t  multi_finger_gestures;\n    uint16_t                                     tap_time;\n    uint16_t                                     tap_distance;\n    uint16_t                                     hold_time;\n    uint16_t                                     swipe_initial_time;\n    uint16_t                                     swipe_initial_distance;\n    uint16_t                                     swipe_consecutive_time;\n    uint16_t                                     swipe_consecutive_distance;\n    int8_t                                       swipe_angle;\n    uint16_t                                     scroll_initial_distance;\n    int8_t                                       scroll_angle;\n    uint16_t                                     zoom_initial_distance;\n    uint16_t                                     zoom_consecutive_distance;\n} azoteq_iqs5xx_gesture_config_t;\n\nSTATIC_ASSERT(sizeof(azoteq_iqs5xx_gesture_config_t) == 24, \"azoteq_iqs5xx_gesture_config_t should be 24 bytes\");\n\ntypedef struct {\n    uint16_t x_resolution;\n    uint16_t y_resolution;\n} azoteq_iqs5xx_resolution_t;\n\n#define AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(h, l) ((int16_t)(h << 8) | l)\n#define AZOTEQ_IQS5XX_SWAP_H_L_BYTES(b) ((uint16_t)((b & 0xff) << 8) | (b >> 8))\n\n#ifndef AZOTEQ_IQS5XX_REPORT_RATE\n#    define AZOTEQ_IQS5XX_REPORT_RATE 10\n#endif\n#if !defined(POINTING_DEVICE_TASK_THROTTLE_MS) && !defined(POINTING_DEVICE_MOTION_PIN)\n// Polling the Azoteq isn't recommended, ensuring we only poll after the report is ready stops any unexpected NACKs\n#    define POINTING_DEVICE_TASK_THROTTLE_MS AZOTEQ_IQS5XX_REPORT_RATE + 1\n#endif\n\nconst pointing_device_driver_t azoteq_iqs5xx_pointing_device_driver;\n\nvoid           azoteq_iqs5xx_init(void);\ni2c_status_t   azoteq_iqs5xx_wake(void);\nreport_mouse_t azoteq_iqs5xx_get_report(report_mouse_t mouse_report);\ni2c_status_t   azoteq_iqs5xx_get_report_rate(azoteq_iqs5xx_report_rate_t *report_rate, azoteq_iqs5xx_charging_modes_t mode, bool end_session);\ni2c_status_t   azoteq_iqs5xx_set_report_rate(uint16_t report_rate_ms, azoteq_iqs5xx_charging_modes_t mode, bool end_session);\ni2c_status_t   azoteq_iqs5xx_set_event_mode(bool enabled, bool end_session);\ni2c_status_t   azoteq_iqs5xx_set_reati(bool enabled, bool end_session);\ni2c_status_t   azoteq_iqs5xx_set_gesture_config(bool end_session);\ni2c_status_t   azoteq_iqs5xx_set_xy_config(bool flip_x, bool flip_y, bool switch_xy, bool palm_reject, bool end_session);\ni2c_status_t   azoteq_iqs5xx_reset_suspend(bool reset, bool suspend, bool end_session);\ni2c_status_t   azoteq_iqs5xx_get_base_data(azoteq_iqs5xx_base_data_t *base_data);\nvoid           azoteq_iqs5xx_set_cpi(uint16_t cpi);\nuint16_t       azoteq_iqs5xx_get_cpi(void);\nuint16_t       azoteq_iqs5xx_get_product(void);\nvoid           azoteq_iqs5xx_setup_resolution(void);\n"
  },
  {
    "path": "drivers/sensors/cirque_pinnacle.c",
    "content": "// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license\n// based on https://github.com/cirque-corp/Cirque_Pinnacle_1CA027/tree/master/Circular_Trackpad\n// with modifications and changes for QMK\n// refer to documentation: Gen2 and Gen3 (Pinnacle ASIC) at https://www.cirque.com/documentation\n\n#include \"cirque_pinnacle.h\"\n#include \"cirque_pinnacle_gestures.h\"\n#include \"wait.h\"\n#include \"timer.h\"\n\n#include <stdlib.h>\n\n#ifndef CIRQUE_PINNACLE_ATTENUATION\n#    ifdef CIRQUE_PINNACLE_CURVED_OVERLAY\n#        define CIRQUE_PINNACLE_ATTENUATION EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_2X\n#    else\n#        define CIRQUE_PINNACLE_ATTENUATION EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X\n#    endif\n#endif\n\nbool     touchpad_init;\nuint16_t scale_data = CIRQUE_PINNACLE_DEFAULT_SCALE;\n\nvoid cirque_pinnacle_clear_flags(void);\nvoid cirque_pinnacle_enable_feed(bool feedEnable);\nvoid RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count);\nvoid RAP_Write(uint8_t address, uint8_t data);\n\n#if CIRQUE_PINNACLE_POSITION_MODE\n/*  Logical Scaling Functions */\n// Clips raw coordinates to \"reachable\" window of sensor\n// NOTE: values outside this window can only appear as a result of noise\nvoid ClipCoordinates(pinnacle_data_t* coordinates) {\n    if (coordinates->xValue < CIRQUE_PINNACLE_X_LOWER) {\n        coordinates->xValue = CIRQUE_PINNACLE_X_LOWER;\n    } else if (coordinates->xValue > CIRQUE_PINNACLE_X_UPPER) {\n        coordinates->xValue = CIRQUE_PINNACLE_X_UPPER;\n    }\n    if (coordinates->yValue < CIRQUE_PINNACLE_Y_LOWER) {\n        coordinates->yValue = CIRQUE_PINNACLE_Y_LOWER;\n    } else if (coordinates->yValue > CIRQUE_PINNACLE_Y_UPPER) {\n        coordinates->yValue = CIRQUE_PINNACLE_Y_UPPER;\n    }\n}\n#endif\n\nuint16_t cirque_pinnacle_get_scale(void) {\n    return scale_data;\n}\nvoid cirque_pinnacle_set_scale(uint16_t scale) {\n    scale_data = scale;\n}\n\n// Scales data to desired X & Y resolution\nvoid cirque_pinnacle_scale_data(pinnacle_data_t* coordinates, uint16_t xResolution, uint16_t yResolution) {\n#if CIRQUE_PINNACLE_POSITION_MODE\n    uint32_t xTemp = 0;\n    uint32_t yTemp = 0;\n\n    ClipCoordinates(coordinates);\n\n    xTemp = coordinates->xValue;\n    yTemp = coordinates->yValue;\n\n    // translate coordinates to (0, 0) reference by subtracting edge-offset\n    xTemp -= CIRQUE_PINNACLE_X_LOWER;\n    yTemp -= CIRQUE_PINNACLE_Y_LOWER;\n\n    // scale coordinates to (xResolution, yResolution) range\n    coordinates->xValue = (uint16_t)(xTemp * xResolution / CIRQUE_PINNACLE_X_RANGE);\n    coordinates->yValue = (uint16_t)(yTemp * yResolution / CIRQUE_PINNACLE_Y_RANGE);\n#else\n    int32_t        xTemp = 0, yTemp = 0;\n    ldiv_t         temp;\n    static int32_t xRemainder, yRemainder;\n\n    temp       = ldiv(((int32_t)coordinates->xDelta) * (int32_t)xResolution + xRemainder, (int32_t)CIRQUE_PINNACLE_X_RANGE);\n    xTemp      = temp.quot;\n    xRemainder = temp.rem;\n\n    temp       = ldiv(((int32_t)coordinates->yDelta) * (int32_t)yResolution + yRemainder, (int32_t)CIRQUE_PINNACLE_Y_RANGE);\n    yTemp      = temp.quot;\n    yRemainder = temp.rem;\n\n    coordinates->xDelta = (int16_t)xTemp;\n    coordinates->yDelta = (int16_t)yTemp;\n#endif\n}\n\n// Clears Status1 register flags (SW_CC and SW_DR)\nvoid cirque_pinnacle_clear_flags(void) {\n    RAP_Write(HOSTREG__STATUS1, HOSTREG__STATUS1_DEFVAL & ~(HOSTREG__STATUS1__COMMAND_COMPLETE | HOSTREG__STATUS1__DATA_READY));\n    wait_us(50);\n}\n\n// Enables/Disables the feed\nvoid cirque_pinnacle_enable_feed(bool feedEnable) {\n    uint8_t feedconfig1;\n    RAP_ReadBytes(HOSTREG__FEEDCONFIG1, &feedconfig1, 1);\n\n    if (feedEnable) {\n        feedconfig1 |= HOSTREG__FEEDCONFIG1__FEED_ENABLE;\n    } else {\n        feedconfig1 &= ~HOSTREG__FEEDCONFIG1__FEED_ENABLE;\n    }\n    RAP_Write(HOSTREG__FEEDCONFIG1, feedconfig1);\n}\n\n/*  ERA (Extended Register Access) Functions  */\n// Reads <count> bytes from an extended register at <address> (16-bit address),\n// stores values in <*data>\nvoid ERA_ReadBytes(uint16_t address, uint8_t* data, uint16_t count) {\n    uint8_t  ERAControlValue = 0xFF;\n    uint16_t timeout_timer;\n\n    cirque_pinnacle_enable_feed(false); // Disable feed\n\n    RAP_Write(HOSTREG__EXT_REG_AXS_ADDR_HIGH, (uint8_t)(address >> 8));    // Send upper byte of ERA address\n    RAP_Write(HOSTREG__EXT_REG_AXS_ADDR_LOW, (uint8_t)(address & 0x00FF)); // Send lower byte of ERA address\n\n    for (uint16_t i = 0; i < count; i++) {\n        RAP_Write(HOSTREG__EXT_REG_AXS_CTRL, HOSTREG__EREG_AXS__INC_ADDR_READ | HOSTREG__EREG_AXS__READ); // Signal ERA-read (auto-increment) to Pinnacle\n\n        // Wait for status register 0x1E to clear\n        timeout_timer = timer_read();\n        do {\n            RAP_ReadBytes(HOSTREG__EXT_REG_AXS_CTRL, &ERAControlValue, 1);\n        } while ((ERAControlValue != 0x00) && (timer_elapsed(timeout_timer) <= CIRQUE_PINNACLE_TIMEOUT));\n\n        RAP_ReadBytes(HOSTREG__EXT_REG_AXS_VALUE, data + i, 1);\n\n        cirque_pinnacle_clear_flags();\n    }\n}\n\n// Writes a byte, <data>, to an extended register at <address> (16-bit address)\nvoid ERA_WriteByte(uint16_t address, uint8_t data) {\n    uint8_t  ERAControlValue = 0xFF;\n    uint16_t timeout_timer;\n\n    cirque_pinnacle_enable_feed(false); // Disable feed\n\n    RAP_Write(HOSTREG__EXT_REG_AXS_VALUE, data); // Send data byte to be written\n\n    RAP_Write(HOSTREG__EXT_REG_AXS_ADDR_HIGH, (uint8_t)(address >> 8));    // Upper byte of ERA address\n    RAP_Write(HOSTREG__EXT_REG_AXS_ADDR_LOW, (uint8_t)(address & 0x00FF)); // Lower byte of ERA address\n\n    RAP_Write(HOSTREG__EXT_REG_AXS_CTRL, HOSTREG__EREG_AXS__WRITE); // Signal an ERA-write to Pinnacle\n\n    // Wait for status register 0x1E to clear\n    timeout_timer = timer_read();\n    do {\n        RAP_ReadBytes(HOSTREG__EXT_REG_AXS_CTRL, &ERAControlValue, 1);\n    } while ((ERAControlValue != 0x00) && (timer_elapsed(timeout_timer) <= CIRQUE_PINNACLE_TIMEOUT));\n\n    cirque_pinnacle_clear_flags();\n}\n\nbool cirque_pinnacle_set_adc_attenuation(uint8_t adcGain) {\n    uint8_t adcconfig = 0x00;\n\n    ERA_ReadBytes(EXTREG__TRACK_ADCCONFIG, &adcconfig, 1);\n    adcGain &= EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_MASK;\n    if (adcGain == (adcconfig & EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_MASK)) {\n        return false;\n    }\n    adcconfig &= ~EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_MASK;\n    adcconfig |= adcGain;\n    ERA_WriteByte(EXTREG__TRACK_ADCCONFIG, adcconfig);\n    ERA_ReadBytes(EXTREG__TRACK_ADCCONFIG, &adcconfig, 1);\n\n    return true;\n}\n\n// Changes thresholds to improve detection of fingers\n// Not needed for flat overlay?\nvoid cirque_pinnacle_tune_edge_sensitivity(void) {\n    uint8_t widezmin = 0x00;\n\n    ERA_ReadBytes(EXTREG__XAXIS_WIDEZMIN, &widezmin, 1);\n    ERA_WriteByte(EXTREG__XAXIS_WIDEZMIN, 0x04); // magic number from Cirque sample code\n    ERA_ReadBytes(EXTREG__XAXIS_WIDEZMIN, &widezmin, 1);\n\n    ERA_ReadBytes(EXTREG__YAXIS_WIDEZMIN, &widezmin, 1);\n    ERA_WriteByte(EXTREG__YAXIS_WIDEZMIN, 0x03); // magic number from Cirque sample code\n    ERA_ReadBytes(EXTREG__YAXIS_WIDEZMIN, &widezmin, 1);\n}\n\n// Perform calibration\nvoid cirque_pinnacle_calibrate(void) {\n    uint8_t  calconfig;\n    uint16_t timeout_timer;\n\n    RAP_ReadBytes(HOSTREG__CALCONFIG1, &calconfig, 1);\n    calconfig |= HOSTREG__CALCONFIG1__CALIBRATE;\n    RAP_Write(HOSTREG__CALCONFIG1, calconfig);\n\n    // Calibration takes ~100ms according to GT-AN-090624, doubling the timeout just to be safe\n    timeout_timer = timer_read();\n    do {\n        RAP_ReadBytes(HOSTREG__CALCONFIG1, &calconfig, 1);\n    } while ((calconfig & HOSTREG__CALCONFIG1__CALIBRATE) && (timer_elapsed(timeout_timer) <= 200));\n\n    cirque_pinnacle_clear_flags();\n}\n\n// Enable/disable cursor smoothing, smoothing is enabled by default\nvoid cirque_pinnacle_cursor_smoothing(bool enable) {\n    uint8_t feedconfig3;\n\n    RAP_ReadBytes(HOSTREG__FEEDCONFIG3, &feedconfig3, 1);\n    if (enable) {\n        feedconfig3 &= ~HOSTREG__FEEDCONFIG3__DISABLE_CROSS_RATE_SMOOTHING;\n    } else {\n        feedconfig3 |= HOSTREG__FEEDCONFIG3__DISABLE_CROSS_RATE_SMOOTHING;\n    }\n    RAP_Write(HOSTREG__FEEDCONFIG3, feedconfig3);\n}\n\n// Check sensor is connected\nbool cirque_pinnacle_connected(void) {\n    uint8_t current_zidle = 0;\n    uint8_t temp_zidle    = 0;\n    RAP_ReadBytes(HOSTREG__ZIDLE, &current_zidle, 1);\n    RAP_Write(HOSTREG__ZIDLE, HOSTREG__ZIDLE_DEFVAL);\n    RAP_ReadBytes(HOSTREG__ZIDLE, &temp_zidle, 1);\n    if (temp_zidle == HOSTREG__ZIDLE_DEFVAL) {\n        RAP_Write(HOSTREG__ZIDLE, current_zidle);\n        return true;\n    }\n    return false;\n}\n\n/*  Pinnacle-based TM040040/TM035035/TM023023 Functions  */\nvoid cirque_pinnacle_init(void) {\n#if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)\n    spi_init();\n#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c)\n    i2c_init();\n#endif\n\n    touchpad_init = true;\n\n    // send a RESET command now, in case QMK had a soft-reset without a power cycle\n    RAP_Write(HOSTREG__SYSCONFIG1, HOSTREG__SYSCONFIG1__RESET);\n    wait_ms(30); // Pinnacle needs 10-15ms to boot, so wait long enough before configuring\n    RAP_Write(HOSTREG__SYSCONFIG1, HOSTREG__SYSCONFIG1_DEFVAL);\n    wait_us(50);\n\n    // Host clears SW_CC flag\n    cirque_pinnacle_clear_flags();\n\n#if CIRQUE_PINNACLE_POSITION_MODE\n    RAP_Write(HOSTREG__FEEDCONFIG2, HOSTREG__FEEDCONFIG2_DEFVAL);\n#else\n    // FeedConfig2 (Feature flags for Relative Mode Only)\n    uint8_t feedconfig2 = HOSTREG__FEEDCONFIG2__GLIDE_EXTEND_DISABLE | HOSTREG__FEEDCONFIG2__INTELLIMOUSE_MODE;\n#    if !defined(CIRQUE_PINNACLE_TAP_ENABLE)\n    feedconfig2 |= HOSTREG__FEEDCONFIG2__ALL_TAP_DISABLE;\n#    endif\n#    if !defined(CIRQUE_PINNACLE_SECONDARY_TAP_ENABLE)\n    feedconfig2 |= HOSTREG__FEEDCONFIG2__SECONDARY_TAP_DISABLE;\n#    elif !defined(CIRQUE_PINNACLE_TAP_ENABLE)\n#        error CIRQUE_PINNACLE_TAP_ENABLE must be defined for CIRQUE_PINNACLE_SECONDARY_TAP_ENABLE to work\n#    endif\n#    if !defined(CIRQUE_PINNACLE_SIDE_SCROLL_ENABLE)\n    feedconfig2 |= HOSTREG__FEEDCONFIG2__SCROLL_DISABLE;\n#    endif\n    RAP_Write(HOSTREG__FEEDCONFIG2, feedconfig2);\n#endif\n\n    // FeedConfig1 (Data Output Flags)\n    RAP_Write(HOSTREG__FEEDCONFIG1, CIRQUE_PINNACLE_POSITION_MODE ? HOSTREG__FEEDCONFIG1__DATA_TYPE__REL0_ABS1 : HOSTREG__FEEDCONFIG1_DEFVAL);\n\n#if CIRQUE_PINNACLE_POSITION_MODE\n    // Host sets z-idle packet count to 5 (default is 0x1E/30)\n    RAP_Write(HOSTREG__ZIDLE, 5);\n#endif\n\n    bool calibrate = cirque_pinnacle_set_adc_attenuation(CIRQUE_PINNACLE_ATTENUATION);\n\n#ifdef CIRQUE_PINNACLE_CURVED_OVERLAY\n    cirque_pinnacle_tune_edge_sensitivity();\n    calibrate = true;\n#endif\n    if (calibrate) {\n        // Force a calibration after setting ADC attenuation\n        cirque_pinnacle_calibrate();\n    }\n\n    cirque_pinnacle_enable_feed(true);\n\n#ifndef CIRQUE_PINNACLE_SKIP_SENSOR_CHECK\n    touchpad_init = cirque_pinnacle_connected();\n#endif\n}\n\npinnacle_data_t cirque_pinnacle_read_data(void) {\n    uint8_t         data_ready = 0;\n    uint8_t         data[6]    = {0};\n    pinnacle_data_t result     = {0};\n\n    // Check if there is valid data available\n    RAP_ReadBytes(HOSTREG__STATUS1, &data_ready, 1);\n    if ((data_ready & HOSTREG__STATUS1__DATA_READY) == 0) {\n        // no data available yet\n        result.valid = false; // be explicit\n        return result;\n    }\n\n    // Read all data bytes\n    RAP_ReadBytes(HOSTREG__PACKETBYTE_0, data, 6);\n\n    // Get ready for the next data sample\n    cirque_pinnacle_clear_flags();\n\n#if CIRQUE_PINNACLE_POSITION_MODE\n    // Decode data for absolute mode\n    // Register 0x13 is unused in this mode (palm detection area)\n    result.buttonFlags = data[0] & 0x3F;                             // bit0 to bit5 are switch 0-5, only hardware button presses (from input pin on the Pinnacle chip)\n    result.xValue      = data[2] | ((data[4] & 0x0F) << 8);          // merge high and low bits for X\n    result.yValue      = data[3] | ((data[4] & 0xF0) << 4);          // merge high and low bits for Y\n    result.zValue      = data[5] & 0x3F;                             // Z is only lower 6 bits, upper 2 bits are reserved/unused\n    result.touchDown   = (result.xValue != 0 || result.yValue != 0); // (0,0) is a \"magic coordinate\" to indicate \"finger touched down\"\n#else\n    // Decode data for relative mode\n    // Registers 0x16 and 0x17 are unused in this mode\n    result.buttons = data[0] & 0x07; // Only three buttons are supported\n    if ((data[0] & 0x10) && data[1] != 0) {\n        result.xDelta = -((int16_t)256 - (int16_t)(data[1]));\n    } else {\n        result.xDelta = data[1];\n    }\n    if ((data[0] & 0x20) && data[2] != 0) {\n        result.yDelta = ((int16_t)256 - (int16_t)(data[2]));\n    } else {\n        result.yDelta = -((int16_t)data[2]);\n    }\n    result.wheelCount = ((int8_t*)data)[3];\n#endif\n\n#ifdef CIRQUE_PINNACLE_REACHABLE_CALIBRATION\n    static uint16_t xMin = UINT16_MAX, yMin = UINT16_MAX, yMax = 0, xMax = 0;\n    if (result.xValue < xMin) xMin = result.xValue;\n    if (result.xValue > xMax) xMax = result.xValue;\n    if (result.yValue < yMin) yMin = result.yValue;\n    if (result.yValue > yMax) yMax = result.yValue;\n    pd_dprintf(\"%s: xLo=%3d xHi=%3d yLo=%3d yHi=%3d\\n\", __FUNCTION__, xMin, xMax, yMin, yMax);\n#endif\n\n    result.valid = true;\n    return result;\n}\n\n#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE\nstatic bool cursor_glide_enable = true;\n\nstatic cursor_glide_context_t glide = {.config = {\n                                           .coef       = 102, /* Good default friction coef */\n                                           .interval   = 10,  /* 100sps */\n                                           .trigger_px = 10,  /* Default threshold in case of hover, set to 0 if you'd like */\n                                       }};\n\nvoid cirque_pinnacle_enable_cursor_glide(bool enable) {\n    cursor_glide_enable = enable;\n}\n\nvoid cirque_pinnacle_configure_cursor_glide(float trigger_px) {\n    glide.config.trigger_px = trigger_px;\n}\n#endif\n\n#if CIRQUE_PINNACLE_POSITION_MODE\n\n#    ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE\nstatic bool is_touch_down;\n\nbool auto_mouse_activation(report_mouse_t mouse_report) {\n    return is_touch_down || mouse_report.x != 0 || mouse_report.y != 0 || mouse_report.h != 0 || mouse_report.v != 0 || mouse_report.buttons;\n}\n#    endif\n\nreport_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) {\n    uint16_t          scale     = cirque_pinnacle_get_scale();\n    pinnacle_data_t   touchData = cirque_pinnacle_read_data();\n    mouse_xy_report_t report_x = 0, report_y = 0;\n    static uint16_t   x = 0, y = 0, last_scale = 0;\n\n#    if defined(CIRQUE_PINNACLE_TAP_ENABLE)\n    mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1);\n#    endif\n#    ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE\n    cursor_glide_t glide_report = {0};\n\n    if (cursor_glide_enable) {\n        glide_report = cursor_glide_check(&glide);\n    }\n#    endif\n\n    if (!touchData.valid) {\n#    ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE\n        if (cursor_glide_enable && glide_report.valid) {\n            report_x = glide_report.dx;\n            report_y = glide_report.dy;\n            goto mouse_report_update;\n        }\n#    endif\n        return mouse_report;\n    }\n\n    if (touchData.touchDown) {\n        pd_dprintf(\"cirque_pinnacle touchData x=%4d y=%4d z=%2d\\n\", touchData.xValue, touchData.yValue, touchData.zValue);\n    }\n\n#    ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE\n    is_touch_down = touchData.touchDown;\n#    endif\n\n    // Scale coordinates to arbitrary X, Y resolution\n    cirque_pinnacle_scale_data(&touchData, scale, scale);\n\n    if (!cirque_pinnacle_gestures(&mouse_report, touchData)) {\n        if (last_scale && scale == last_scale && x && y && touchData.xValue && touchData.yValue) {\n            report_x = CONSTRAIN_HID_XY((int16_t)(touchData.xValue - x));\n            report_y = CONSTRAIN_HID_XY((int16_t)(touchData.yValue - y));\n        }\n        x          = touchData.xValue;\n        y          = touchData.yValue;\n        last_scale = scale;\n\n#    ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE\n        if (cursor_glide_enable) {\n            if (touchData.touchDown) {\n                cursor_glide_update(&glide, report_x, report_y, touchData.zValue);\n            } else if (!glide_report.valid) {\n                glide_report = cursor_glide_start(&glide);\n                if (glide_report.valid) {\n                    report_x = glide_report.dx;\n                    report_y = glide_report.dy;\n                }\n            }\n        }\n#    endif\n    }\n\n#    ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE\nmouse_report_update:\n#    endif\n    mouse_report.x = report_x;\n    mouse_report.y = report_y;\n\n    return mouse_report;\n}\n\nuint16_t cirque_pinnacle_get_cpi(void) {\n    return CIRQUE_PINNACLE_PX_TO_INCH(cirque_pinnacle_get_scale());\n}\nvoid cirque_pinnacle_set_cpi(uint16_t cpi) {\n    cirque_pinnacle_set_scale(CIRQUE_PINNACLE_INCH_TO_PX(cpi));\n}\n\n// clang-format off\nconst pointing_device_driver_t cirque_pinnacle_pointing_device_driver = {\n    .init       = cirque_pinnacle_init,\n    .get_report = cirque_pinnacle_get_report,\n    .set_cpi    = cirque_pinnacle_set_cpi,\n    .get_cpi    = cirque_pinnacle_get_cpi\n};\n// clang-format on\n#else\nreport_mouse_t cirque_pinnacle_get_report(report_mouse_t mouse_report) {\n    pinnacle_data_t touchData = cirque_pinnacle_read_data();\n\n    // Scale coordinates to arbitrary X, Y resolution\n    cirque_pinnacle_scale_data(&touchData, cirque_pinnacle_get_scale(), cirque_pinnacle_get_scale());\n\n    if (touchData.valid) {\n        mouse_report.buttons = touchData.buttons;\n        mouse_report.x = CONSTRAIN_HID_XY(touchData.xDelta);\n        mouse_report.y = CONSTRAIN_HID_XY(touchData.yDelta);\n        mouse_report.v = touchData.wheelCount;\n    }\n    return mouse_report;\n}\n\n// clang-format off\nconst pointing_device_driver_t cirque_pinnacle_pointing_device_driver = {\n    .init       = cirque_pinnacle_init,\n    .get_report = cirque_pinnacle_get_report,\n    .set_cpi    = cirque_pinnacle_set_scale,\n    .get_cpi    = cirque_pinnacle_get_scale\n};\n// clang-format on\n#endif\n"
  },
  {
    "path": "drivers/sensors/cirque_pinnacle.h",
    "content": "// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license\n\n#pragma once\n\n#include \"cirque_pinnacle_regdefs.h\"\n#include <stdint.h>\n#include <stdbool.h>\n#include \"pointing_device_internal.h\"\n#include \"pointing_device.h\"\n\n#ifndef CIRQUE_PINNACLE_TIMEOUT\n#    define CIRQUE_PINNACLE_TIMEOUT 20 // I2C timeout in milliseconds\n#endif\n\n#define CIRQUE_PINNACLE_ABSOLUTE_MODE 1\n#define CIRQUE_PINNACLE_RELATIVE_MODE 0\n#ifndef CIRQUE_PINNACLE_POSITION_MODE\n#    define CIRQUE_PINNACLE_POSITION_MODE CIRQUE_PINNACLE_ABSOLUTE_MODE\n#endif\n\n#define CIRQUE_PINNACLE_DEFAULT_SCALE 1024\n#ifndef CIRQUE_PINNACLE_DIAMETER_MM\n#    define CIRQUE_PINNACLE_DIAMETER_MM 40\n#endif\n\n#if CIRQUE_PINNACLE_POSITION_MODE\n// Coordinate scaling values\n#    ifndef CIRQUE_PINNACLE_X_LOWER\n#        define CIRQUE_PINNACLE_X_LOWER 127 // min \"reachable\" X value\n#    endif\n#    ifndef CIRQUE_PINNACLE_X_UPPER\n#        define CIRQUE_PINNACLE_X_UPPER 1919 // max \"reachable\" X value\n#    endif\n#    ifndef CIRQUE_PINNACLE_Y_LOWER\n#        define CIRQUE_PINNACLE_Y_LOWER 63 // min \"reachable\" Y value\n#    endif\n#    ifndef CIRQUE_PINNACLE_Y_UPPER\n#        define CIRQUE_PINNACLE_Y_UPPER 1471 // max \"reachable\" Y value\n#    endif\n#    ifndef CIRQUE_PINNACLE_X_RANGE\n#        define CIRQUE_PINNACLE_X_RANGE (CIRQUE_PINNACLE_X_UPPER - CIRQUE_PINNACLE_X_LOWER)\n#    endif\n#    ifndef CIRQUE_PINNACLE_Y_RANGE\n#        define CIRQUE_PINNACLE_Y_RANGE (CIRQUE_PINNACLE_Y_UPPER - CIRQUE_PINNACLE_Y_LOWER)\n#    endif\n#    if defined(POINTING_DEVICE_GESTURES_SCROLL_ENABLE)\n#        define CIRQUE_PINNACLE_CIRCULAR_SCROLL_ENABLE\n#    endif\n#else\n#    define CIRQUE_PINNACLE_X_RANGE 256\n#    define CIRQUE_PINNACLE_Y_RANGE 256\n#    if defined(POINTING_DEVICE_GESTURES_SCROLL_ENABLE)\n#        define CIRQUE_PINNACLE_SIDE_SCROLL_ENABLE\n#    endif\n#endif\n#if !defined(POINTING_DEVICE_TASK_THROTTLE_MS)\n#    define POINTING_DEVICE_TASK_THROTTLE_MS 10 // Cirque Pinnacle in normal operation produces data every 10ms. Advanced configuration for pen/stylus usage might require lower values.\n#endif\n#if defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c)\n#    include \"i2c_master.h\"\n// Cirque's 7-bit I2C Slave Address\n#    ifndef CIRQUE_PINNACLE_ADDR\n#        define CIRQUE_PINNACLE_ADDR I2C_ADDRESS_DEFAULT\n#    endif\n#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)\n#    include \"spi_master.h\"\n#    ifndef CIRQUE_PINNACLE_CLOCK_SPEED\n#        define CIRQUE_PINNACLE_CLOCK_SPEED 10000000\n#    endif\n#    ifndef CIRQUE_PINNACLE_SPI_LSBFIRST\n#        define CIRQUE_PINNACLE_SPI_LSBFIRST false\n#    endif\n#    ifndef CIRQUE_PINNACLE_SPI_MODE\n#        define CIRQUE_PINNACLE_SPI_MODE 1\n#    endif\n#    ifndef CIRQUE_PINNACLE_SPI_DIVISOR\n#        ifdef __AVR__\n#            define CIRQUE_PINNACLE_SPI_DIVISOR (F_CPU / CIRQUE_PINNACLE_CLOCK_SPEED)\n#        else\n#            define CIRQUE_PINNACLE_SPI_DIVISOR 64\n#        endif\n#        ifndef CIRQUE_PINNACLE_SPI_CS_PIN\n#            ifdef POINTING_DEVICE_CS_PIN\n#                define CIRQUE_PINNACLE_SPI_CS_PIN POINTING_DEVICE_CS_PIN\n#            else\n#                error \"No Chip Select pin has been defined -- missing POINTING_DEVICE_CS_PIN or CIRQUE_PINNACLE_SPI_CS_PIN define\"\n#            endif\n#        endif\n#    endif\n#endif\n\n#define DIVIDE_UNSIGNED_ROUND(numerator, denominator) (((numerator) + ((denominator) / 2)) / (denominator))\n#define CIRQUE_PINNACLE_INCH_TO_PX(inch) (DIVIDE_UNSIGNED_ROUND((inch) * (uint32_t)CIRQUE_PINNACLE_DIAMETER_MM * 10, 254))\n#define CIRQUE_PINNACLE_PX_TO_INCH(px) (DIVIDE_UNSIGNED_ROUND((px) * (uint32_t)254, CIRQUE_PINNACLE_DIAMETER_MM * 10))\n\n// Convenient way to store and access measurements\ntypedef struct {\n    bool valid; // true if valid data was read, false if no data was ready\n#if CIRQUE_PINNACLE_POSITION_MODE\n    uint16_t xValue;\n    uint16_t yValue;\n    uint16_t zValue;\n    uint8_t  buttonFlags;\n    bool     touchDown;\n#else\n    int16_t xDelta;\n    int16_t yDelta;\n    int8_t  wheelCount;\n    uint8_t buttons;\n#endif\n} pinnacle_data_t;\n\n#define cirque_pinnacle_i2c_pointing_device_driver cirque_pinnacle_pointing_device_driver\n#define cirque_pinnacle_spi_pointing_device_driver cirque_pinnacle_pointing_device_driver\nconst pointing_device_driver_t cirque_pinnacle_pointing_device_driver;\n\nvoid            cirque_pinnacle_init(void);\nvoid            cirque_pinnacle_calibrate(void);\nvoid            cirque_pinnacle_cursor_smoothing(bool enable);\npinnacle_data_t cirque_pinnacle_read_data(void);\nvoid            cirque_pinnacle_scale_data(pinnacle_data_t* coordinates, uint16_t xResolution, uint16_t yResolution);\nuint16_t        cirque_pinnacle_get_scale(void);\nvoid            cirque_pinnacle_set_scale(uint16_t scale);\nuint16_t        cirque_pinnacle_get_cpi(void);\nvoid            cirque_pinnacle_set_cpi(uint16_t cpi);\nreport_mouse_t  cirque_pinnacle_get_report(report_mouse_t mouse_report);\n"
  },
  {
    "path": "drivers/sensors/cirque_pinnacle_gestures.c",
    "content": "/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n * Copyright 2022 Daniel Kao <daniel.m.kao@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include <stdlib.h>\n#include <lib/lib8tion/lib8tion.h>\n#include \"cirque_pinnacle_gestures.h\"\n#include \"pointing_device.h\"\n#include \"timer.h\"\n#include \"wait.h\"\n#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)\n#    include \"keyboard.h\"\n#endif\n\n#if (defined(CIRQUE_PINNACLE_TAP_ENABLE) || defined(CIRQUE_PINNACLE_CIRCULAR_SCROLL_ENABLE)) && CIRQUE_PINNACLE_POSITION_MODE\nstatic cirque_pinnacle_features_t features = {.tap_enable = true, .circular_scroll_enable = true};\n#endif\n\n#if defined(CIRQUE_PINNACLE_TAP_ENABLE) && CIRQUE_PINNACLE_POSITION_MODE\nstatic trackpad_tap_context_t tap;\n\nstatic report_mouse_t trackpad_tap(report_mouse_t mouse_report, pinnacle_data_t touchData) {\n    if (touchData.touchDown != tap.touchDown) {\n        tap.touchDown = touchData.touchDown;\n        if (!touchData.zValue) {\n            if (timer_elapsed(tap.timer) < CIRQUE_PINNACLE_TAPPING_TERM && tap.timer != 0) {\n                mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1);\n            }\n        }\n        tap.timer = timer_read();\n    }\n    if (timer_elapsed(tap.timer) > (CIRQUE_PINNACLE_TOUCH_DEBOUNCE)) {\n        tap.timer = 0;\n    }\n\n    return mouse_report;\n}\n\nvoid cirque_pinnacle_enable_tap(bool enable) {\n    features.tap_enable = enable;\n}\n#endif\n\n#ifdef CIRQUE_PINNACLE_CIRCULAR_SCROLL_ENABLE\n#    if !CIRQUE_PINNACLE_POSITION_MODE\n#        error \"Circular scroll is not supported in relative mode\"\n#    endif\n/* To set a trackpad exclusively as scroll wheel: outer_ring_pct = 100, trigger_px = 0, trigger_ang = 0 */\nstatic circular_scroll_context_t scroll = {.config = {.outer_ring_pct = 33,\n                                                      .trigger_px     = 16,\n                                                      .trigger_ang    = 9102, /* 50 degrees */\n                                                      .wheel_clicks   = 18}};\n\nstatic inline uint16_t atan2_16(int32_t dy, int32_t dx) {\n    if (dy == 0) {\n        if (dx >= 0) {\n            return 0;\n        } else {\n            return 32768;\n        }\n    }\n\n    int32_t abs_y = dy > 0 ? dy : -dy;\n    int16_t a;\n\n    if (dx >= 0) {\n        a = 8192 - (8192 * (dx - abs_y) / (dx + abs_y));\n    } else {\n        a = 24576 - (8192 * (dx + abs_y) / (abs_y - dx));\n    }\n\n    if (dy < 0) {\n        return -a; // negate if in quad III or IV\n    }\n    return a;\n}\n\nstatic circular_scroll_t circular_scroll(pinnacle_data_t touchData) {\n    circular_scroll_t report = {0, 0, false};\n    int8_t            x, y, wheel_clicks;\n    uint8_t           center = INT8_MAX, mag;\n    int16_t           ang, dot, det, opposite_side, adjacent_side;\n    uint16_t          scale = cirque_pinnacle_get_scale();\n\n    if (touchData.zValue) {\n        /*\n         * Place origin at center of trackpad, treat coordinates as vectors.\n         * Scale to +/-INT8_MAX; angles are independent of resolution.\n         */\n        if (scale) {\n            /* Rotate coordinates into a consistent orientation */\n            report_mouse_t rot = {.x = (int8_t)((int32_t)touchData.xValue * INT8_MAX * 2 / scale - center), .y = (int8_t)((int32_t)touchData.yValue * INT8_MAX * 2 / scale - center)};\n#    if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)\n            if (!is_keyboard_left()) {\n                rot = pointing_device_adjust_by_defines_right(rot);\n            } else\n#    endif\n            {\n                rot = pointing_device_adjust_by_defines(rot);\n            }\n            x = rot.x;\n            y = rot.y;\n        } else {\n            x = 0;\n            y = 0;\n        }\n\n        /* Check if first touch */\n        if (!scroll.z) {\n            report.suppress_touch = false;\n            /* Check if touch falls within outer ring */\n            mag = sqrt16(x * x + y * y);\n            if (mag * 100 / center >= 100 - scroll.config.outer_ring_pct) {\n                scroll.state = SCROLL_DETECTING;\n                scroll.x     = x;\n                scroll.y     = y;\n                scroll.mag   = mag;\n                /*\n                 * Decide scroll axis:\n                 *   Vertical if started from righ half\n                 *   Horizontal if started from left half\n                 * Flipped for left-handed\n                 */\n                scroll.axis = x < 0;\n            }\n        } else if (scroll.state == SCROLL_DETECTING) {\n            report.suppress_touch = true;\n            /* Already detecting scroll, check movement from touchdown location */\n            mag = sqrt16((x - scroll.x) * (x - scroll.x) + (y - scroll.y) * (y - scroll.y));\n            if (mag >= scroll.config.trigger_px) {\n                /*\n                 * Find angle of movement.\n                 * 0 degrees here means movement towards center of circle\n                 */\n                dot           = scroll.x * x + scroll.y * y;\n                det           = scroll.x * y - scroll.y * x;\n                opposite_side = abs(det);                                /* Based on scalar rejection */\n                adjacent_side = abs(scroll.mag * scroll.mag - abs(dot)); /* Based on scalar projection */\n                ang           = (int16_t)atan2_16(opposite_side, adjacent_side);\n                if (ang < scroll.config.trigger_ang) {\n                    /* Not a scroll, release coordinates */\n                    report.suppress_touch = false;\n                    scroll.state          = NOT_SCROLL;\n                } else {\n                    /* Scroll detected */\n                    scroll.state = SCROLL_VALID;\n                }\n            }\n        }\n        if (scroll.state == SCROLL_VALID) {\n            report.suppress_touch = true;\n            dot                   = scroll.x * x + scroll.y * y;\n            det                   = scroll.x * y - scroll.y * x;\n            ang                   = (int16_t)atan2_16(det, dot);\n            wheel_clicks          = ((int32_t)ang * scroll.config.wheel_clicks) / 65536;\n            if (wheel_clicks >= 1 || wheel_clicks <= -1) {\n                if (scroll.config.left_handed) {\n                    if (scroll.axis == 0) {\n                        report.h = -wheel_clicks;\n                    } else {\n                        report.v = wheel_clicks;\n                    }\n                } else {\n                    if (scroll.axis == 0) {\n                        report.v = -wheel_clicks;\n                    } else {\n                        report.h = wheel_clicks;\n                    }\n                }\n                scroll.x = x;\n                scroll.y = y;\n            }\n        }\n    }\n\n    scroll.z = touchData.zValue;\n    if (!scroll.z) scroll.state = SCROLL_UNINITIALIZED;\n\n    return report;\n}\n\nvoid cirque_pinnacle_enable_circular_scroll(bool enable) {\n    features.circular_scroll_enable = enable;\n}\n\nvoid cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks, bool left_handed) {\n    scroll.config.outer_ring_pct = outer_ring_pct;\n    scroll.config.trigger_px     = trigger_px;\n    scroll.config.trigger_ang    = trigger_ang;\n    scroll.config.wheel_clicks   = wheel_clicks;\n    scroll.config.left_handed    = left_handed;\n}\n#endif\n\nbool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData) {\n    bool suppress_mouse_update = false;\n\n#ifdef CIRQUE_PINNACLE_CIRCULAR_SCROLL_ENABLE\n#    if !CIRQUE_PINNACLE_POSITION_MODE\n#        error \"Circular scroll is not supported in relative mode\"\n#    endif\n    circular_scroll_t scroll_report;\n    if (features.circular_scroll_enable) {\n        scroll_report         = circular_scroll(touchData);\n        mouse_report->v       = scroll_report.v;\n        mouse_report->h       = scroll_report.h;\n        suppress_mouse_update = scroll_report.suppress_touch;\n    }\n#endif\n\n#if defined(CIRQUE_PINNACLE_TAP_ENABLE) && CIRQUE_PINNACLE_POSITION_MODE\n    if (features.tap_enable) {\n        *mouse_report = trackpad_tap(*mouse_report, touchData);\n    }\n#endif\n\n    return suppress_mouse_update;\n}\n"
  },
  {
    "path": "drivers/sensors/cirque_pinnacle_gestures.h",
    "content": "/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n * Copyright 2022 Daniel Kao <daniel.m.kao@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include \"cirque_pinnacle.h\"\n#include \"report.h\"\n\ntypedef struct {\n    bool tap_enable;\n    bool circular_scroll_enable;\n} cirque_pinnacle_features_t;\n\n#if defined(CIRQUE_PINNACLE_TAP_ENABLE) && CIRQUE_PINNACLE_POSITION_MODE\n#    ifndef CIRQUE_PINNACLE_TAPPING_TERM\n#        include \"action.h\"\n#        include \"action_tapping.h\"\n#        define CIRQUE_PINNACLE_TAPPING_TERM GET_TAPPING_TERM(QK_MOUSE_BUTTON_1, &(keyrecord_t){})\n#    endif\n#    ifndef CIRQUE_PINNACLE_TOUCH_DEBOUNCE\n#        define CIRQUE_PINNACLE_TOUCH_DEBOUNCE (CIRQUE_PINNACLE_TAPPING_TERM * 8)\n#    endif\n\ntypedef struct {\n    uint16_t timer;\n    bool     touchDown;\n} trackpad_tap_context_t;\n\n/* Enable/disable tap gesture */\nvoid cirque_pinnacle_enable_tap(bool enable);\n#endif\n\n#ifdef CIRQUE_PINNACLE_CIRCULAR_SCROLL_ENABLE\n#    if !CIRQUE_PINNACLE_POSITION_MODE\n#        error \"Circular scroll is not supported in relative mode\"\n#    endif\ntypedef enum {\n    SCROLL_UNINITIALIZED,\n    SCROLL_DETECTING,\n    SCROLL_VALID,\n    NOT_SCROLL,\n} circular_scroll_status_t;\n\ntypedef struct {\n    int8_t v;\n    int8_t h;\n    bool   suppress_touch;\n} circular_scroll_t;\n\ntypedef struct {\n    uint8_t  outer_ring_pct; /* Width of outer ring, given as a percentage of the radius */\n    uint8_t  trigger_px;     /* Amount of movement before triggering scroll validation, in pixels 0~127 */\n    uint16_t trigger_ang;    /* Angle required to validate scroll, in radians where pi = 32768 */\n    uint8_t  wheel_clicks;   /* How many clicks to report in a circle */\n    bool     left_handed;    /* Whether scrolling should be flipped for left handed use */\n} circular_scroll_config_t;\n\ntypedef struct {\n    circular_scroll_config_t config;\n    circular_scroll_status_t state;\n    uint8_t                  mag;\n    int8_t                   x;\n    int8_t                   y;\n    uint16_t                 z;\n    bool                     axis;\n} circular_scroll_context_t;\n\n/* Enable/disable circular scroll gesture */\nvoid cirque_pinnacle_enable_circular_scroll(bool enable);\n\n/*\n * Configure circular scroll gesture.\n * Trackpad can be configured to act exclusively as a scroll wheel with outer_ring_pct = 0, trigger_px = 0, trigger_ang = 0.\n * @param outer_ring_pct Width of outer ring from which to begin scroll validation, given as a percentage of the radius.\n * @param trigger_px Amount of movement before triggering scroll validation. Expressed in pixels, trackpad coordinates are scaled to radius of 128 pixels for circular scroll.\n * @param triger_ang Angle required to validate scroll, angle smaller than this will invalidate scroll. In radians where pi = 32768, 0 means movement towards center of trackpad, 16384 means movement perpendicular to center.\n * @param wheel_clicks Number of scroll wheel clicks to report in a full rotation.\n * @param left_handed Whether scrolling should be flipped for left-handed use.\n */\nvoid cirque_pinnacle_configure_circular_scroll(uint8_t outer_ring_pct, uint8_t trigger_px, uint16_t trigger_ang, uint8_t wheel_clicks, bool left_handed);\n#endif\n\n#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE\n/* Implementation in pointing_device_drivers.c */\n\n/* Enable/disable inertial cursor */\nvoid cirque_pinnacle_enable_cursor_glide(bool enable);\n\n/*\n * Configure inertial cursor.\n * @param trigger_px Movement required to trigger cursor glide, set this to non-zero if you have some amount of hover.\n */\nvoid cirque_pinnacle_configure_cursor_glide(float trigger_px);\n#endif\n\n/* Process available gestures */\nbool cirque_pinnacle_gestures(report_mouse_t* mouse_report, pinnacle_data_t touchData);\n"
  },
  {
    "path": "drivers/sensors/cirque_pinnacle_i2c.c",
    "content": "// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license\n#include \"cirque_pinnacle.h\"\n#include \"i2c_master.h\"\n#include \"stdio.h\"\n\n// Masks for Cirque Register Access Protocol (RAP)\n#define WRITE_MASK 0x80\n#define READ_MASK 0xA0\n\nextern bool touchpad_init;\n\n/*  RAP Functions */\n// Reads <count> Pinnacle registers starting at <address>\nvoid RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count) {\n    uint8_t cmdByte = READ_MASK | address; // Form the READ command byte\n    if (touchpad_init) {\n        i2c_write_register(CIRQUE_PINNACLE_ADDR << 1, cmdByte, NULL, 0, CIRQUE_PINNACLE_TIMEOUT);\n        if (i2c_read_register(CIRQUE_PINNACLE_ADDR << 1, cmdByte, data, count, CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) {\n            pd_dprintf(\"error cirque_pinnacle i2c_read_register\\n\");\n            touchpad_init = false;\n        }\n    }\n}\n\n// Writes single-byte <data> to <address>\nvoid RAP_Write(uint8_t address, uint8_t data) {\n    uint8_t cmdByte = WRITE_MASK | address; // Form the WRITE command byte\n\n    if (touchpad_init) {\n        if (i2c_write_register(CIRQUE_PINNACLE_ADDR << 1, cmdByte, &data, sizeof(data), CIRQUE_PINNACLE_TIMEOUT) != I2C_STATUS_SUCCESS) {\n            pd_dprintf(\"error cirque_pinnacle i2c_write_register\\n\");\n            touchpad_init = false;\n        }\n    }\n}\n"
  },
  {
    "path": "drivers/sensors/cirque_pinnacle_regdefs.h",
    "content": "// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license\n// based on https://github.com/cirque-corp/Cirque_Pinnacle_1CA027/tree/master/Additional_Examples\n// with modifications and changes for QMK\n// refer to documentation: Gen2 and Gen3 (Pinnacle ASIC) at https://www.cirque.com/gen2gen3-asic-details\n\n#pragma once\n\n// clang-format off\n\n#define HostReg__0      (0x00)\n#define HostReg__1      (0x01)\n#define HostReg__2      (0x02)\n#define HostReg__3      (0x03)\n#define HostReg__4      (0x04)\n#define HostReg__5      (0x05)\n#define HostReg__6      (0x06)\n#define HostReg__7      (0x07)\n#define HostReg__8      (0x08)\n#define HostReg__9      (0x09)\n#define HostReg__10     (0x0A)\n#define HostReg__11     (0x0B)\n#define HostReg__12     (0x0C)\n#define HostReg__13     (0x0D)\n#define HostReg__14     (0x0E)\n#define HostReg__15     (0x0F)\n#define HostReg__16     (0x10)\n#define HostReg__17     (0x11)\n#define HostReg__18     (0x12)\n#define HostReg__19     (0x13)\n#define HostReg__20     (0x14)\n#define HostReg__21     (0x15)\n#define HostReg__22     (0x16)\n#define HostReg__23     (0x17)\n#define HostReg__24     (0x18)\n#define HostReg__25     (0x19)\n#define HostReg__26     (0x1A)\n#define HostReg__27     (0x1B)\n#define HostReg__28     (0x1C)\n#define HostReg__29     (0x1D)\n#define HostReg__30     (0x1E)\n#define HostReg__31     (0x1F)\n\n// ---------------- Register Assignments -------------------------------------\n\n/*--------------------------------------------------------------------------*\\\n                           Chip ID / Version\n\\*--------------------------------------------------------------------------*/\n// Chip ID Register\n#define HOSTREG__CHIPID                                             HostReg__0\n\n// Chip Version Register\n#define HOSTREG__VERSION                                            HostReg__1\n\n/*--------------------------------------------------------------------------*\\\n                           Status Register\n\\*--------------------------------------------------------------------------*/\n// Status 1 Register -- MUST BE HOSTREG__2\n#define HOSTREG__STATUS1                                            HostReg__2\n#    define HOSTREG__STATUS1__DATA_READY                            0x04\n#    define HOSTREG__STATUS1__COMMAND_COMPLETE                      0x08\n#define HOSTREG__STATUS1_DEFVAL                                     0x00\n\n/*--------------------------------------------------------------------------*\\\n                           System Config Register\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__SYSCONFIG1                                         HostReg__3\n#    define HOSTREG__SYSCONFIG1__RESET                              0x01\n#    define HOSTREG__SYSCONFIG1__STANDBY                            0x02\n#    define HOSTREG__SYSCONFIG1__AUTO_SLEEP                         0x04\n#    define HOSTREG__SYSCONFIG1__TRACK_DISABLE                      0x08\n#    define HOSTREG__SYSCONFIG1__ANYMEAS_ENABLE                     0x10\n#    define HOSTREG__SYSCONFIG1__GPIO_CTRL_ENABLE                   0x20\n#    define HOSTREG__SYSCONFIG1__WAKEUP_TOGGLE                      0x40\n#    define HOSTREG__SYSCONFIG1__FORCE_WAKEUP                       0x80\n#define HOSTREG__SYSCONFIG1_DEFVAL                                  0x00\n\n/*--------------------------------------------------------------------------*\\\n                           Feed Config Registers\n\\*--------------------------------------------------------------------------*/\n// Feed Config Register1\n#define HOSTREG__FEEDCONFIG1                                        HostReg__4\n#    define HOSTREG__FEEDCONFIG1__FEED_ENABLE                       0x01\n#    define HOSTREG__FEEDCONFIG1__DATA_TYPE__REL0_ABS1              0x02\n#    define HOSTREG__FEEDCONFIG1__FILTER_DISABLE                    0x04\n#    define HOSTREG__FEEDCONFIG1__X_AXIS_DISABLE                    0x08\n#    define HOSTREG__FEEDCONFIG1__Y_AXIS_DISABLE                    0x10\n#    define HOSTREG__FEEDCONFIG1__AXIS_FOR_Z__Y0_X1                 0x20\n#    define HOSTREG__FEEDCONFIG1__X_DATA_INVERT                     0x40\n#    define HOSTREG__FEEDCONFIG1__Y_DATA_INVERT                     0x80\n#define HOSTREG__FEEDCONFIG1_DEFVAL                                 0x00\n\n// Feed Config Register2\n#define HOSTREG__FEEDCONFIG2                                        HostReg__5\n#    define HOSTREG__FEEDCONFIG2__INTELLIMOUSE_MODE                 0x01\n#    define HOSTREG__FEEDCONFIG2__ALL_TAP_DISABLE                   0x02\n#    define HOSTREG__FEEDCONFIG2__SECONDARY_TAP_DISABLE             0x04\n#    define HOSTREG__FEEDCONFIG2__SCROLL_DISABLE                    0x08\n#    define HOSTREG__FEEDCONFIG2__GLIDE_EXTEND_DISABLE              0x10\n#    define HOSTREG__FEEDCONFIG2__PALM_BEFORE_Z_ENABLE              0x20\n#    define HOSTREG__FEEDCONFIG2__BUTNS_46_SCROLL_5_MIDDLE          0x40\n#    define HOSTREG__FEEDCONFIG2__SWAP_XY_RELATIVE                  0x80\n#define HOSTREG__FEEDCONFIG2_DEFVAL                                 0x00\n\n// Feed Config Register3\n#define HOSTREG__FEEDCONFIG3                                        HostReg__6\n#    define HOSTREG__FEEDCONFIG3__BTNS_456_TO_123_IN_REL            0x01\n#    define HOSTREG__FEEDCONFIG3__DISABLE_CROSS_RATE_SMOOTHING      0x02\n#    define HOSTREG__FEEDCONFIG3__DISABLE_PALM_NERD_MEAS            0x04\n#    define HOSTREG__FEEDCONFIG3__DISABLE_NOISE_AVOIDANCE           0x08\n#    define HOSTREG__FEEDCONFIG3__DISABLE_WRAP_LOCKOUT              0x10\n#    define HOSTREG__FEEDCONFIG3__DISABLE_DYNAMIC_EMI_ADJUST        0x20\n#    define HOSTREG__FEEDCONFIG3__DISABLE_HW_EMI_DETECT             0x40\n#    define HOSTREG__FEEDCONFIG3__DISABLE_SW_EMI_DETECT             0x80\n#define HOSTREG__FEEDCONFIG3_DEFVAL                                 0x00\n\n/*--------------------------------------------------------------------------*\\\n                           Calibration Config\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__CALCONFIG1                                         HostReg__7\n#    define HOSTREG__CALCONFIG1__CALIBRATE                          0x01\n#    define HOSTREG__CALCONFIG1__BACKGROUND_COMP_ENABLE             0x02\n#    define HOSTREG__CALCONFIG1__NERD_COMP_ENABLE                   0x04\n#    define HOSTREG__CALCONFIG1__TRACK_ERROR_COMP_ENABLE            0x08\n#    define HOSTREG__CALCONFIG1__TAP_COMP_ENABLE                    0x10\n#    define HOSTREG__CALCONFIG1__PALM_ERROR_COMP_ENABLE             0x20\n#    define HOSTREG__CALCONFIG1__CALIBRATION_MATRIX_DISABLE         0x40\n#    define HOSTREG__CALCONFIG1__FORCE_PRECALIBRATION_NOISE_CHECK   0x80\n#define HOSTREG__CALCONFIG1_DEFVAL                                  (HOSTREG__CALCONFIG1__BACKGROUND_COMP_ENABLE | HOSTREG__CALCONFIG1__NERD_COMP_ENABLE | HOSTREG__CALCONFIG1__TRACK_ERROR_COMP_ENABLE | HOSTREG__CALCONFIG1__TAP_COMP_ENABLE | HOSTREG__CALCONFIG1__PALM_ERROR_COMP_ENABLE)\n\n/*--------------------------------------------------------------------------*\\\n                           PS2 Aux Control Register\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__PS2AUX_CTRL                                        HostReg__8\n#    define HOSTREG__PS2AUX_CTRL__CMD_PASSTHRU_ENABLE               0x01\n#    define HOSTREG__PS2AUX_CTRL__SP_EXTENDED_MODE                  0x02\n#    define HOSTREG__PS2AUX_CTRL__GS_DISABLE                        0x04\n#    define HOSTREG__PS2AUX_CTRL__SP_DISABLE                        0x08\n#    define HOSTREG__PS2AUX_CTRL__GS_COORDINATE_DISABLE             0x10\n#    define HOSTREG__PS2AUX_CTRL__SP_COORDINATE_DISABLE             0x20\n#    define HOSTREG__PS2AUX_CTRL__DISABLE_AA00_DETECT               0x40\n#    define HOSTREG__PS2AUX_CTRL__AUX_PRESENT                       0x80\n#define HOSTREG__PR2AUX_CTRL_DEFVAL                                 0x00\n\n/*--------------------------------------------------------------------------*\\\n                           Sample Rate Value\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__SAMPLERATE                                         HostReg__9\n#    define HOSTREG__SAMPLERATE__10_SPS                             0x0A\n#    define HOSTREG__SAMPLERATE__20_SPS                             0x14\n#    define HOSTREG__SAMPLERATE__40_SPS                             0x28\n#    define HOSTREG__SAMPLERATE__60_SPS                             0x3C\n#    define HOSTREG__SAMPLERATE__80_SPS                             0x50\n#    define HOSTREG__SAMPLERATE__100_SPS                            0x64\n#    define HOSTREG__SAMPLERATE__200_SPS                            0xC8        // 200sps not supported\n                                                                                // only for ps2 compatibility\n                                                                                // rate set to 100sps\n#define HOSTREG__SAMPLERATE_DEFVAL                                  HOSTREG__SAMPLERATE__100_SPS\n\n/*--------------------------------------------------------------------------*\\\n                           Z Idle Value\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__ZIDLE                                              HostReg__10\n#define HOSTREG__ZIDLE_DEFVAL                                       30 // 0x1E\n\n/*--------------------------------------------------------------------------*\\\n                           Z Scaler Value\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__ZSCALER                                            HostReg__11\n#define HOSTREG__ZSCALER_DEFVAL                                     8 // 0x08\n\n/*--------------------------------------------------------------------------*\\\n                           Sleep Interval Value\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__SLEEP_INTERVAL                                     HostReg__12\n#define HOSTREG__SLEEP_INTERVAL_DEFVAL                              73 // 0x49\n\n/*--------------------------------------------------------------------------*\\\n                           Sleep Delay Value\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__SLEEP_DELAY                                        HostReg__13\n#define HOSTREG__SLEEP_DELAY_DEFVAL                                 39 // 0x27\n\n/*--------------------------------------------------------------------------*\\\n                           Dynamic EMI Bad Channel Count Thresholds\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__DYNAMIC_EMI_ADJUST_THRESHOLD                       HostReg__14\n#define HOSTREG__DYNAMIC_EMI_ADJUST_THRESHOLD_DEFVAL                66 // 0x42\n\n/*--------------------------------------------------------------------------*\\\n                           Packet Registers\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__PACKETBYTE_0                                       HostReg__18\n#define HOSTREG__PACKETBYTE_1                                       HostReg__19\n#define HOSTREG__PACKETBYTE_2                                       HostReg__20\n#define HOSTREG__PACKETBYTE_3                                       HostReg__21\n#define HOSTREG__PACKETBYTE_4                                       HostReg__22\n#define HOSTREG__PACKETBYTE_5                                       HostReg__23\n\n/*--------------------------------------------------------------------------*\\\n                           Port A GPIO Control\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__PORTA_GPIO_CTRL                                    HostReg__24\n#define HOSTREG__PORTA_GPIO_CTRL_DEFVAL                             0xFF\n\n/*--------------------------------------------------------------------------*\\\n                           Port A GPIO Data\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__PORTA_GPIO_DATA                                    HostReg__25\n#define HOSTREG__PORTA_GPIO_DATA_DEFVAL                             0x00\n\n/*--------------------------------------------------------------------------*\\\n                           Port B GPIO Control And Data\n\\*--------------------------------------------------------------------------*/\n\n#define HOSTREG__PORTB_GPIO_CTRL_DATA                               HostReg__26\n#    define HOSTREG__PORTB_GPIO_DATA__PB0                           0x01\n#    define HOSTREG__PORTB_GPIO_DATA__PB1                           0x02\n#    define HOSTREG__PORTB_GPIO_DATA__PB2                           0x04\n#    define HOSTREG__PORTB_GPIO_CTRL__PB0                           0x08\n#    define HOSTREG__PORTB_GPIO_CTRL__PB1                           0x10\n#    define HOSTREG__PORTB_GPIO_CTRL__PB2                           0x20\n#    define HOSTREG__PORTB_GPIO_RSVD_0                              0x40\n#    define HOSTREG__PORTB_GPIO_READ1_WRITE0                        0x80\n#define HOSTREG__PORTB_GPIO_CTRL_DATA_DEFVAL                        (HOSTREG__PORTB_GPIO_CTRL__PB0 | HOSTREG__PORTB_GPIO_CTRL__PB1 | HOSTREG__PORTB_GPIO_CTRL__PB2)\n\n/*--------------------------------------------------------------------------*\\\n                           Extended Register Access\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__EXT_REG_AXS_VALUE                                  HostReg__27\n\n#define HOSTREG__EXT_REG_AXS_ADDR_HIGH                              HostReg__28\n#define HOSTREG__EXT_REG_AXS_ADDR_LOW                               HostReg__29\n\n#define HOSTREG__EXT_REG_AXS_CTRL                                   HostReg__30\n#    define HOSTREG__EREG_AXS__READ                                 0x01\n#    define HOSTREG__EREG_AXS__WRITE                                0x02\n#    define HOSTREG__EREG_AXS__INC_ADDR_READ                        0x04\n#    define HOSTREG__EREG_AXS__INC_ADDR_WRITE                       0x08\n#    define HOSTREG__EREG_AXS__RSVD_3                               0x10\n#    define HOSTREG__EREG_AXS__RSVD_2                               0x20\n#    define HOSTREG__EREG_AXS__RSVD_1                               0x40\n#    define HOSTREG__EREG_AXS__RSVD_0                               0x80\n\n#define HOSTREG__EXT_REG_AXS_VALUE_DEFVAL                           0x00\n#define HOSTREG__EXT_REG_AXS_ADDR_HIGH_DEFVAL                       0x00\n#define HOSTREG__EXT_REG_AXS_ADDR_LOW_DEFVAL                        0x00\n#define HOSTREG__EXT_REG_AXS_CTRL_DEFVAL                            0x00\n\n/*--------------------------------------------------------------------------*\\\n                           Product ID\n\\*--------------------------------------------------------------------------*/\n#define HOSTREG__PRODUCT_ID                                         HostReg__31\n\n\n\n//Some useful values\n#define I2C_ADDRESS_DEFAULT                                         0x2A\n#define FIRMWARE_ID                                                 0x07\n#define FIRMWARE_VERSION                                            0x9D\n\n//Anymeas config options\n//First setting is HostReg 5.  This sets toggle frequency (EF) and gain.\n//Gain is upper two bits (0xC0), frequency is lower 6 bits (0x3F)\n#define AnyMeas_AccumBits_ElecFreq                                  HostReg__5\n#    define ADCCNFG_ELEC_FREQ                                       0x3F  /* Bit 4, 3, 2, 1, 0 */\n#        define ADCCNFG_EF_0                                        0x02  // 500,000Hz\n#        define ADCCNFG_EF_1                                        0x03  // 444,444Hz\n#        define ADCCNFG_EF_2                                        0x04  // 400,000Hz\n#        define ADCCNFG_EF_3                                        0x05  // 363,636Hz\n#        define ADCCNFG_EF_4                                        0x06  // 333,333Hz\n#        define ADCCNFG_EF_5                                        0x07  // 307,692Hz\n#        define ADCCNFG_EF_6                                        0x09  // 267,000Hz\n#        define ADCCNFG_EF_7                                        0x0B  // 235,000Hz\n#    define ADCCNFG_ACCUMBITSSELECT                                 0xC0  /* Bit 7, 6 */\n#        define ADCCNFG_ACCBITS_17_14_0                             0x00  //This is about 2x gain\n#        define ADCCNFG_ACCBITS_17_15_1                             0x40  //This is about 1.6x gain\n#        define ADCCNFG_ACCBITS_17_2__80                            0x80  //This is about 1.3x gain\n#        define ADCCNFG_ACCBITS_17_2__C0                            0xC0  //This is lowest gain\n//Note, all frequencies above are based on default 500ns aperture.  If aperture is shorter the frequencies will be faster and if aperture is longer the frequencies will be slower.\n\n//Next is HostReg 6.  This sets the sample length.  There are four possible settings to bit length.  All other settings are not normally used and should be a 0.\n#define AnyMeas_BitLength                                           HostReg__6\n#    define ADCCTRL_BIT_LENGTH                                      0x03  /* Bit 1, 0 */\n#        define ADCCTRL_SAMPLES_32                                  0x00  //Note: this does not work.\n#        define ADCCTRL_SAMPLES_128                                 0x01\n#        define ADCCTRL_SAMPLES_256                                 0x02\n#        define ADCCTRL_SAMPLES_512                                 0x03\n#    define ADCCTRL_ENABLE                                          0x20  /* Bit 5 */\n#    define ADCCTRL_INT_FLAG                                        0x40  /* Bit 6 */\n#    define ADCCTRL_START_BUSY                                      0x80  /* Bit 7 */\n//The smaller the sample length the faster the measurement but the lower the SNR.  For high SNR requirements 512 sample length is recommended.  Alternatively, multiple 128 or 256 length measurements could be averaged.\n\n//Next is HostReg 7.  This sets the sense mux.  Pinnacle has 2 sense lines, Sense N and Sense P1.  There is also a Sense P2 but it is not bonded out, it is only internal.\n//Signal on Sense N will be inverted from signal on Sense P1.  Other than sign inversion, signal strength should be the same.\n#define AnyMeas_ADC_MuxControl                                      HostReg__7\n#    define ADCMUXCTRL_SENSEP1GATE                                  0x01  //Enables Sense P1.  Can be combined with Sense N input or exclusivly Sense P1 alone.\n#    define ADCMUXCTRL_SENSEP2GATE                                  0x02  //Not used.\n#    define ADCMUXCTRL_SENSENGATE                                   0x04  //Enables Sense N.  Can be combined with Sense P inputs or exclusivly Sense N alone.\n#    define ADCMUXCTRL_REF0GATE                                     0x08  //This enables the RefCap0.  This is a capacitor inside the chip that is roughly 0.25pF. It is also controlled with the toggle and polarity bits so those bits must be set properly as well in order to use it.\n#    define ADCMUXCTRL_REF1GATE                                     0x10  //This enables the RefCap1.  This is a capacitor inside the chip that is roughly 0.5pF. It is also controlled with the toggle and polarity bits so those bits must be set properly as well in order to use it.\n#    define ADCMUXCTRL_OSCMEASEN                                    0x80  //this is a test mode for measuring the internal oscillator.  It is for IC test only.\n\n//Next is HostReg 8.  This contains various ADC config settings that are not likely to be used.\n#define AnyMeas_ADC_Config2                                         HostReg__8\n#    define ADCCNFG2_ADC_CLK_SELECT                                 0x01  /* Bit 0 */   //If 0 use the standard 8Mhz clock.  If 1 use a divide by 2, 4Mhz clock.  Only used if extra slow toggle frequencies are required.\n#    define ADCCNFG2_EMI_FLAG                                       0x02  /* Bit 1 */   //EMI flag threshold only used with internal FW.  Not valid in anymeas mode.\n#    define ADCCNFG2_EMI_FLAG_THRESHOLD_0                           0x04  /* Bit 2 */   //EMI flag threshold only used with internal FW.  Not valid in anymeas mode.\n#    define ADCCNFG2_EMI_FLAG_THRESHOLD_1                           0x08  /* Bit 3 */   //EMI flag threshold only used with internal FW.  Not valid in anymeas mode.\n#    define ADCCNFG2_DSX2_EXTEND                                    0x10  /* Bit 4 */   //extend one signal on the receive.  Could also be helpful in situations where sensor cap is extremely high.\n#    define ADCCNFG2_ETOGGLE_DELAY                                  0x20  /* Bit 5 */   //delay a bit before toggling electrodes.  Could be helpful in situations where sensor cap is extremely high.\n\n//Next is HostReg 9.  This sets the aperture length.  Bottom 4 bits set the aperture width\n#define AnyMeas_ADC_AWidth                                          HostReg__9\n#    define ADCAWIDTH_AWIDTHMASK                                    0x0F\n#        define ADCAWIDTH_APERTURE_OPEN                             0x00  //does not work\n#        define ADCAWIDTH_APERTURE_125NS                            0x01  //does not work\n#        define ADCAWIDTH_APERTURE_250NS                            0x02\n#        define ADCAWIDTH_APERTURE_375NS                            0x03\n#        define ADCAWIDTH_APERTURE_500NS                            0x04\n#        define ADCAWIDTH_APERTURE_625NS                            0x05\n#        define ADCAWIDTH_APERTURE_750NS                            0x06\n#        define ADCAWIDTH_APERTURE_875NS                            0x07\n#        define ADCAWIDTH_APERTURE_1000NS                           0x08\n#        define ADCAWIDTH_APERTURE_1125NS                           0x09\n#        define ADCAWIDTH_APERTURE_1250NS                           0x0A\n#        define ADCAWIDTH_APERTURE_1375NS                           0x0B\n#        define ADCAWIDTH_APERTURE_1500NS                           0x0C\n#        define ADCAWIDTH_APERTURE_1625NS                           0x0D\n#        define ADCAWIDTH_APERTURE_1750NS                           0x0E\n#        define ADCAWIDTH_APERTURE_1875NS                           0x0F\n#    define ADCAWIDTH_AWIDTHPLUSHALF                                0x10\n#    define ADCAWIDTH_AOPEN                                         0x20\n#    define ADCAWIDTH_W2WAIT                                        0x40\n\n//next two registers give the high and low bytes to the 16 bit address where Pinnacle will pull the measurement data.  Normally these addresses are within the base 32 registers.\n#define AnyMeas_pADCMeasInfoStart_High_Byte                         HostReg__10\n#define AnyMeas_pADCMeasInfoStart_Low_Byte                          HostReg__11\n\n//Next is the measurement index, this sets the measurement state machine to the start and should be a 0 at start.\n#define AnyMeas_MeasIndex                                           HostReg__12\n#   define ANYMEASSTATE_RESET_START                                 0x00\n#   define ANYMEASSTATE_START_MEASUREMENT                           0x01\n#   define ANYMEASSTATE_WAIT_FOR_MEASUREMENT_AND_HOST               0x02\n\n//next is the state itself of the measurement, should always be 0.\n#define AnyMeas_State                                               HostReg__13\n\n//next is the number of measurements.  Use 0x80 to repeat the single measurement or repeat a number of measurements.\n//0x40 will turn the ADC off after measurements.  This will result in longer startup time for a subsequent measurement, but lower idle power draw.\n#define AnyMeas_Control_NumMeas                                     HostReg__14\n#    define ANYMEAS_CONTROL__NUM_MEAS_MASK                          0x3F\n#    define ANYMEAS_CONTROL__ADC_POST_MEAS_PWR                      0x40\n#    define ANYMEAS_CONTROL__REPEAT                                 0x80\n\n//These are not used\n#define AnyMeas_pADCMeasInfo_High_Byte                              HostReg__15\n#define AnyMeas_pADCMeasInfo_Low_Byte                               HostReg__16\n\n//16 bit result of measurement will be found in these two registers.\n#define AnyMeas_Result_High_Byte                                    HostReg__17\n#define AnyMeas_Result_Low_Byte                                     HostReg__18\n\n// ---------------- Extended Register Assignments ----------------------------\n/*--------------------------------------------------------------------------*\\\n                           ADC Mux Control\n\\*--------------------------------------------------------------------------*/\n#define EXTREG__ADCMUX_CTRL                                         0x00EB\n#    define EXTREG__ADCMUX_CTRL__SNSP_ENABLE                        0x01\n#    define EXTREG__ADCMUX_CTRL__SNSN_ENABLE                        0x04\n\n/*--------------------------------------------------------------------------*\\\n                           Timer Reload Registers\n\\*--------------------------------------------------------------------------*/\n#define EXTREG__PACKET_TIMER_RELOAD                                 0x019F\n#define EXTREG__TRACK_TIMER_RELOAD                                  0x019E\n// These two registers should have matching content.\n#    define EXTREG__TIMER_RELOAD__300_SPS                           0x06\n#    define EXTREG__TIMER_RELOAD__200_SPS                           0x09\n#    define EXTREG__TIMER_RELOAD__100_SPS                           0x13\n\n/*--------------------------------------------------------------------------*\\\n                           Track ADC Config\n\\*--------------------------------------------------------------------------*/\n#define EXTREG__TRACK_ADCCONFIG                                     0x0187\n// ADC-attenuation settings (held in BIT_7 and BIT_6)\n// 1X = most sensitive, 4X = least sensitive\n#    define EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_MASK             0xC0\n#        define EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_1X           0x00\n#        define EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_2X           0x40\n#        define EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_3X           0x80\n#        define EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X           0xC0\n#define EXTREG__TRACK_ADCCONFIG_DEFVAL                              0x4E\n\n\n/*--------------------------------------------------------------------------*\\\n                           Tune Edge Sensitivity\n\\*--------------------------------------------------------------------------*/\n// These registers are not detailed in any publically available documentation\n// Names inferred from debug prints in https://github.com/cirque-corp/Cirque_Pinnacle_1CA027/blob/master/Circular_Trackpad\n#define EXTREG__XAXIS_WIDEZMIN                                      0x0149\n#define EXTREG__YAXIS_WIDEZMIN                                      0x0168\n#define EXTREG__XAXIS_WIDEZMIN_DEFVAL                               0x06\n#define EXTREG__YAXIS_WIDEZMIN_DEFVAL                               0x05\n\n// clang-format on\n"
  },
  {
    "path": "drivers/sensors/cirque_pinnacle_spi.c",
    "content": "// Copyright (c) 2018 Cirque Corp. Restrictions apply. See: www.cirque.com/sw-license\n#include \"cirque_pinnacle.h\"\n#include \"spi_master.h\"\n\n// Masks for Cirque Register Access Protocol (RAP)\n#define WRITE_MASK 0x80\n#define READ_MASK 0xA0\n#define FILLER_BYTE 0xFC\n\nextern bool touchpad_init;\n\n/*  RAP Functions */\n// Reads <count> Pinnacle registers starting at <address>\nvoid RAP_ReadBytes(uint8_t address, uint8_t* data, uint8_t count) {\n    uint8_t cmdByte = READ_MASK | address; // Form the READ command byte\n    if (touchpad_init) {\n        if (spi_start(CIRQUE_PINNACLE_SPI_CS_PIN, CIRQUE_PINNACLE_SPI_LSBFIRST, CIRQUE_PINNACLE_SPI_MODE, CIRQUE_PINNACLE_SPI_DIVISOR)) {\n            spi_write(cmdByte);     // write command byte, receive filler\n            spi_write(FILLER_BYTE); // write & receive filler\n            spi_write(FILLER_BYTE); // write & receive filler\n            for (uint8_t i = 0; i < count; i++) {\n                data[i] = spi_write(FILLER_BYTE); // write filler, receive data on the third filler send\n            }\n        } else {\n            pd_dprintf(\"error cirque_pinnacle spi_start read\\n\");\n            touchpad_init = false;\n        }\n        spi_stop();\n    }\n}\n\n// Writes single-byte <data> to <address>\nvoid RAP_Write(uint8_t address, uint8_t data) {\n    uint8_t cmdByte = WRITE_MASK | address; // Form the WRITE command byte\n\n    if (touchpad_init) {\n        if (spi_start(CIRQUE_PINNACLE_SPI_CS_PIN, CIRQUE_PINNACLE_SPI_LSBFIRST, CIRQUE_PINNACLE_SPI_MODE, CIRQUE_PINNACLE_SPI_DIVISOR)) {\n            spi_write(cmdByte);\n            spi_write(data);\n        } else {\n            pd_dprintf(\"error cirque_pinnacle spi_start write\\n\");\n            touchpad_init = false;\n        }\n        spi_stop();\n    }\n}\n"
  },
  {
    "path": "drivers/sensors/navigator.c",
    "content": "// Copyright 2025 ZSA Technology Labs, Inc <contact@zsa.io>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*\n * Navigator Trackball Smooth Scrolling\n *\n * Enhanced scrolling algorithm that eliminates deadzones and provides natural,\n * responsive scrolling for both slow and fast movements.\n *\n * Key Features:\n * - No initial deadzone - scrolling starts immediately with any movement\n * - Smooth acceleration - speed increases naturally with faster movement\n * - Fractional accumulation - sub-pixel movements accumulate until triggering scroll\n * - Reduced jitter - consistent consumption prevents oscillation\n *\n * Configuration Parameters (add to keymap config.h):\n * - NAVIGATOR_SCROLL_DIVIDER: Lower = more sensitive (default: 10)\n * - NAVIGATOR_SCROLL_THRESHOLD: Minimum to scroll (default: 0f)\n * - NAVIGATOR_SCROLL_ACCELERATION: Speed multiplier (default: 1.5f)\n * - NAVIGATOR_SCROLL_MAX_SPEED: Maximum speed limit (default: 8.0f)\n *\n * Algorithm:\n * 1. Accumulate input as floating-point values\n * 2. When accumulated >= 1.0, trigger scrolling with acceleration\n * 3. Subtract exactly 1.0 from accumulation regardless of output\n * 4. Gentle decay (2% per frame) only after 20 frames of inactivity\n */\n\n#include \"quantum.h\"\n#include \"navigator.h\"\n\nfloat scroll_accumulated_h = 0;\nfloat scroll_accumulated_v = 0;\n\nbool set_scrolling = false;\nbool navigator_turbo = false;\nbool navigator_aim = false;\n\nreport_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) {\n    // Turbo mode is used to increase the speed of the mouse cursor\n    // by multiplying the x and y values by a factor.\n    if (navigator_turbo) {\n        mouse_report.x *= NAVIGATOR_TURBO_MULTIPLIER;\n        mouse_report.y *= NAVIGATOR_TURBO_MULTIPLIER;\n    }\n    // Aim mode is used to slow down the mouse cursor\n    // by dividing the x and y values by a factor.\n    if (navigator_aim) {\n        mouse_report.x /= NAVIGATOR_AIM_DIVIDER;\n        mouse_report.y /= NAVIGATOR_AIM_DIVIDER;\n    }\n    if (set_scrolling) {\n        // Accumulate scroll movement\n        scroll_accumulated_h += (float)mouse_report.x / NAVIGATOR_SCROLL_DIVIDER;\n        scroll_accumulated_v += (float)mouse_report.y / NAVIGATOR_SCROLL_DIVIDER;\n\n        // This allows fractional accumulation to build up before triggering scroll\n        float abs_h = (scroll_accumulated_h < 0) ? -scroll_accumulated_h : scroll_accumulated_h;\n        float abs_v = (scroll_accumulated_v < 0) ? -scroll_accumulated_v : scroll_accumulated_v;\n\n        float scroll_h = 0.0f;\n        float scroll_v = 0.0f;\n\n        if (abs_h >= 1.0f) {\n            // Simple acceleration for faster movements\n            float speed_h = 1.0f + ((abs_h - 1.0f) * NAVIGATOR_SCROLL_ACCELERATION);\n            if (speed_h > NAVIGATOR_SCROLL_MAX_SPEED) {\n                speed_h = NAVIGATOR_SCROLL_MAX_SPEED;\n            }\n            scroll_h = (scroll_accumulated_h > 0) ? speed_h : -speed_h;\n        }\n\n        if (abs_v >= 1.0f) {\n            float speed_v = 1.0f + ((abs_v - 1.0f) * NAVIGATOR_SCROLL_ACCELERATION);\n            if (speed_v > NAVIGATOR_SCROLL_MAX_SPEED) {\n                speed_v = NAVIGATOR_SCROLL_MAX_SPEED;\n            }\n            scroll_v = (scroll_accumulated_v > 0) ? speed_v : -speed_v;\n        }\n\n#ifdef NAVIGATOR_SCROLL_INVERT_X\n        mouse_report.h = (int8_t)scroll_h;\n#else\n        mouse_report.h = (int8_t)-scroll_h;\n#endif\n\n#ifdef NAVIGATOR_SCROLL_INVERT_Y\n        mouse_report.v = (int8_t)-scroll_v;\n#else\n        mouse_report.v = (int8_t)scroll_v;\n#endif\n\n\n        // Subtract proportional to the base scroll (before acceleration) to prevent jitter\n        if (abs_h >= 1.0f) {\n            scroll_accumulated_h -= (scroll_accumulated_h > 0) ? 1.0f : -1.0f;\n        }\n        if (abs_v >= 1.0f) {\n            scroll_accumulated_v -= (scroll_accumulated_v > 0) ? 1.0f : -1.0f;\n        }\n\n\n        // Much gentler decay and only after longer idle periods\n        static uint8_t idle_counter_h = 0, idle_counter_v = 0;\n\n        if (mouse_report.x == 0 && mouse_report.h == 0) {\n            idle_counter_h++;\n            if (idle_counter_h > 20) {  // Only decay after 20 frames of no input\n                scroll_accumulated_h *= 0.98f;  // Very gentle decay\n            }\n        } else {\n            idle_counter_h = 0;\n        }\n\n        if (mouse_report.y == 0 && mouse_report.v == 0) {\n            idle_counter_v++;\n            if (idle_counter_v > 20) {\n                scroll_accumulated_v *= 0.98f;\n            }\n        } else {\n            idle_counter_v = 0;\n        }\n\n        mouse_report.x = 0;\n        mouse_report.y = 0;\n    }\n    return pointing_device_task_user(mouse_report);\n}\n"
  },
  {
    "path": "drivers/sensors/navigator.h",
    "content": "// Copyright 2025 ZSA Technology Labs, Inc <contact@zsa.io>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n#ifndef NAVIGATOR_SCROLL_DIVIDER\n    #define NAVIGATOR_SCROLL_DIVIDER 10\n#endif\n\n#ifndef NAVIGATOR_SCROLL_THRESHOLD\n    #define NAVIGATOR_SCROLL_THRESHOLD 0f\n#endif\n\n#ifndef NAVIGATOR_SCROLL_ACCELERATION\n    #define NAVIGATOR_SCROLL_ACCELERATION 1.5f\n#endif\n\n#ifndef NAVIGATOR_SCROLL_MAX_SPEED\n    #define NAVIGATOR_SCROLL_MAX_SPEED 8.0f\n#endif\n\n\n#ifdef POINTING_DEVICE_DRIVER_navigator_trackball\n#define NAVIGATOR_TURBO_MULTIPLIER 3\n#define NAVIGATOR_AIM_DIVIDER 3\n#endif\n\n#ifdef POINTING_DEVICE_DRIVER_navigator_trackpad\n#define NAVIGATOR_TURBO_MULTIPLIER 2\n#define NAVIGATOR_AIM_DIVIDER 2\n#endif\n"
  },
  {
    "path": "drivers/sensors/navigator_trackpad.c",
    "content": "// Copyright 2025 ZSA Technology Labs, Inc <contact@zsa.io>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n// THIS IS A WORK IN PROGRESS, AS THE TRACKPAD IC's FIRMWARE IS STILL IN DEVELOPMENT\n// DO NOT USE THIS CODE IN PRODUCTION\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <math.h>\n#include \"navigator_trackpad.h\"\n#include \"i2c_master.h\"\n#include \"quantum.h\"\n#include \"timer.h\"\n\n\n#ifdef PROTOCOL_LUFA\n# error \"LUFA is not supported yet\"\n#endif\n\nconst pointing_device_driver_t navigator_trackpad_pointing_device_driver = {.init = navigator_trackpad_device_init, .get_report = navigator_trackpad_get_report, .get_cpi = navigator_trackpad_get_cpi, .set_cpi = navigator_trackpad_set_cpi};\n\nuint16_t    current_cpi = DEFAULT_CPI_TICK;\nuint32_t    gpio_offset_addr;\nuint8_t     has_motion = 0;\nextern bool set_scrolling;\nbool        in_motion;\nbool        touchpad_init;\n\n#if defined(NAVIGATOR_TRACKPAD_PTP_MODE)\ncgen6_report_t ptp_report;\nbool           prev_ptp_flag, prev_tap_clear = false;\nuint8_t        last_contact_count = 0;\nuint16_t       prev_ptp_x, prev_ptp_y;\nuint16_t       tap_timer = 0;\nint16_t        ptp_delta_x, ptp_delta_y;\n#endif\n\ni2c_status_t cirque_gen6_read_report(uint8_t *data, uint16_t cnt) {\n    i2c_status_t res = i2c_receive(NAVIGATOR_TRACKPAD_ADDRESS, data, cnt, NAVIGATOR_TRACKPAD_TIMEOUT);\n    wait_us(cnt * 15);\n    return res;\n}\n\nvoid cirque_gen6_clear(void) {\n    uint8_t buf[CGEN6_MAX_PACKET_SIZE];\n    for (uint8_t i = 0; i < 5; i++) {\n        wait_ms(1);\n        if (cirque_gen6_read_report(buf, CGEN6_MAX_PACKET_SIZE) != I2C_STATUS_SUCCESS) {\n            break;\n        }\n    }\n}\n\nuint8_t cirque_gen6_read_memory(uint32_t addr, uint8_t *data, uint16_t cnt) {\n    uint8_t  cksum = 0;\n    uint8_t  res   = CGEN6_SUCCESS;\n    uint8_t  len[2];\n    uint16_t read = 0;\n\n    uint8_t preamble[8] = {0x01, 0x09, (uint8_t)(addr & (uint32_t)0x000000FF), (uint8_t)((addr & 0x0000FF00) >> 8), (uint8_t)((addr & 0x00FF0000) >> 16), (uint8_t)((addr & 0xFF000000) >> 24), (uint8_t)(cnt & 0x00FF), (uint8_t)((cnt & 0xFF00) >> 8)};\n\n    // Read the length of the data + 3 bytes (first 2 bytes for the length and the last byte for the checksum)\n    // Create a buffer to store the data\n    uint8_t buf[cnt + 3];\n    if (i2c_transmit_and_receive(NAVIGATOR_TRACKPAD_ADDRESS, preamble, 8, buf, cnt + 3, NAVIGATOR_TRACKPAD_TIMEOUT) != I2C_STATUS_SUCCESS) {\n        res |= CGEN6_I2C_FAILED;\n    }\n\n    // Read the data length\n    for (uint8_t i = 0; i < 2; i++) {\n        cksum += len[i] = buf[i];\n        read++;\n    }\n\n    // Populate the data buffer\n    for (uint16_t i = 2; i < cnt + 2; i++) {\n        cksum += data[i - 2] = buf[i];\n        read++;\n    }\n\n    // Check the checksum\n    if (cksum != buf[read]) {\n        res |= CGEN6_CKSUM_FAILED;\n    }\n\n    // Check the length (incremented first to account for the checksum)\n    if (++read != (len[0] | (len[1] << 8))) {\n        res |= CGEN6_LEN_MISMATCH;\n    }\n\n    wait_ms(1);\n\n    return res;\n}\n\nuint8_t cirque_gen6_write_memory(uint32_t addr, uint8_t *data, uint16_t cnt) {\n    uint8_t res   = CGEN6_SUCCESS;\n    uint8_t cksum = 0, i = 0;\n    uint8_t preamble[8] = {0x00, 0x09, (uint8_t)(addr & 0x000000FF), (uint8_t)((addr & 0x0000FF00) >> 8), (uint8_t)((addr & 0x00FF0000) >> 16), (uint8_t)((addr & 0xFF000000) >> 24), (uint8_t)(cnt & 0x00FF), (uint8_t)((cnt & 0xFF00) >> 8)};\n\n    uint8_t buf[cnt + 9];\n    // Calculate the checksum\n    for (; i < 8; i++) {\n        cksum += buf[i] = preamble[i];\n    }\n\n    for (i = 0; i < cnt; i++) {\n        cksum += buf[i + 8] = data[i];\n    }\n\n    buf[cnt + 8] = cksum;\n\n    if (i2c_transmit(NAVIGATOR_TRACKPAD_ADDRESS, buf, cnt + 9, NAVIGATOR_TRACKPAD_TIMEOUT) != I2C_STATUS_SUCCESS) {\n        res |= CGEN6_I2C_FAILED;\n    }\n\n    wait_ms(1);\n\n    return res;\n}\n\nuint8_t cirque_gen6_read_reg(uint32_t addr) {\n    uint8_t data;\n    uint8_t res = cirque_gen6_read_memory(addr, &data, 1);\n    if (res != CGEN6_SUCCESS) {\n        printf(\"Failed to read 8bits from register at address 0x%08X with error 0x%02X\\n\", (u_int)addr, res);\n        return 0;\n    }\n    return data;\n}\n\nuint16_t cirque_gen6_read_reg_16(uint32_t addr) {\n    uint8_t buf[2];\n    uint8_t res = cirque_gen6_read_memory(addr, buf, 2);\n    if (res != CGEN6_SUCCESS) {\n        printf(\"Failed to read 16bits from register at address 0x%08X with error 0x%02X\\n\", (u_int)addr, res);\n        return 0;\n    }\n    return (buf[1] << 8) | buf[0];\n}\n\nuint32_t cirque_gen6_read_reg_32(uint32_t addr) {\n    uint8_t buf[4];\n    uint8_t res = cirque_gen6_read_memory(addr, buf, 4);\n    if (res != CGEN6_SUCCESS) {\n        printf(\"Failed to read 32bits from register at address 0x%08X with error 0x%02X\\n\", (u_int)addr, res);\n        return 0;\n    }\n    return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];\n}\n\nuint8_t cirque_gen6_write_reg(uint32_t addr, uint8_t data) {\n    return cirque_gen6_write_memory(addr, &data, 1);\n}\n\nuint8_t cirque_gen6_write_reg_16(uint32_t addr, uint16_t data) {\n    uint8_t buf[2] = {data & 0xFF, (data >> 8) & 0xFF};\n    return cirque_gen6_write_memory(addr, buf, 2);\n}\n\nuint8_t cirque_gen6_write_reg_32(uint32_t addr, uint32_t data) {\n    uint8_t buf[4] = {data & 0xFF, (data >> 8) & 0xFF, (data >> 16) & 0xFF, (data >> 24) & 0xFF};\n    return cirque_gen6_write_memory(addr, buf, 4);\n}\n\nuint8_t cirque_gen6_set_relative_mode(void) {\n    uint8_t feed_config4 = cirque_gen6_read_reg(CGEN6_FEED_CONFIG4);\n    feed_config4 &= 0xF3;\n    return cirque_gen6_write_reg(CGEN6_FEED_CONFIG4, feed_config4);\n}\n\nuint8_t cirque_gen6_set_ptp_mode(void) {\n    uint8_t feed_config4 = cirque_gen6_read_reg(CGEN6_FEED_CONFIG4);\n    feed_config4 &= 0xF7;\n    feed_config4 |= 0x04;\n    return cirque_gen6_write_reg(CGEN6_FEED_CONFIG4, feed_config4);\n}\n\nuint8_t cirque_gen6_swap_xy(bool set) {\n    uint8_t xy_config = cirque_gen6_read_reg(CGEN6_XY_CONFIG);\n    if (set) {\n        xy_config |= 0x04;\n    } else {\n        xy_config &= ~0x04;\n    }\n    return cirque_gen6_write_reg(CGEN6_XY_CONFIG, xy_config);\n}\n\nuint8_t cirque_gen6_invert_y(bool set) {\n    uint8_t xy_config = cirque_gen6_read_reg(CGEN6_XY_CONFIG);\n    if (set) {\n        xy_config |= 0x02;\n    } else {\n        xy_config &= ~0x02;\n    }\n    return cirque_gen6_write_reg(CGEN6_XY_CONFIG, xy_config);\n}\n\nuint8_t cirque_gen6_invert_x(bool set) {\n    uint8_t xy_config = cirque_gen6_read_reg(CGEN6_XY_CONFIG);\n    if (set) {\n        xy_config |= 0x01;\n    } else {\n        xy_config &= ~0x01;\n    }\n    return cirque_gen6_write_reg(CGEN6_XY_CONFIG, xy_config);\n}\n\nuint8_t cirque_gen6_enable_logical_scaling(bool set) {\n    uint8_t xy_config = cirque_gen6_read_reg(CGEN6_XY_CONFIG);\n    if (set) {\n        xy_config &= ~0x08;\n    } else {\n        xy_config |= ~0x08;\n    }\n    return cirque_gen6_write_reg(CGEN6_XY_CONFIG, xy_config);\n}\n\nbool cirque_gen6_get_gpio_state(uint8_t num) {\n    uint32_t gpio_states = cirque_gen6_read_reg_32(0x43000000 + gpio_offset_addr + 0x0004);\n    return ((gpio_states >> num) & 0x000000001);\n}\n\nuint32_t cirque_gen_6_read_callback(uint32_t trigger_time, void *cb_arg) {\n    if (has_motion) {\n        return NAVIGATOR_TRACKPAD_READ;\n    }\n    uint8_t packet[CGEN6_MAX_PACKET_SIZE];\n    if (cirque_gen6_read_report(packet, CGEN6_MAX_PACKET_SIZE) != I2C_STATUS_SUCCESS) {\n        return false;\n    }\n\n    uint8_t report_id = packet[2];\n#if defined(NAVIGATOR_TRACKPAD_PTP_MODE)\n    if (report_id == CGEN6_PTP_REPORT_ID) {\n        ptp_report.confidence    = packet[3] & 0x01;\n        ptp_report.tip           = (packet[3] & 0x02) >> 1;\n        ptp_report.id            = (packet[3] & 0xFC) >> 2;\n        ptp_report.x             = packet[5] << 8 | packet[4];\n        ptp_report.y             = packet[7] << 8 | packet[6];\n        ptp_report.ts            = packet[9] << 8 | packet[10];\n        ptp_report.contact_count = packet[11];\n        ptp_report.buttons       = packet[12];\n\n        has_motion = 1;\n    }\n#endif\n#if defined(NAVIGATOR_TRACKPAD_RELATIVE_MODE)\n    if (report_id == CGEN6_MOUSE_REPORT_ID) {\n        ptp_report.buttons           = packet[3];\n        ptp_report.xDelta            = packet[4];\n        ptp_report.yDelta            = packet[5];\n        amree ptp_report.scrollDelta = packet[6];\n        ptp_report.panDelta          = packet[7];\n\n        has_motion = 1;\n    }\n#endif\n    return NAVIGATOR_TRACKPAD_READ;\n}\n\nvoid dump_ptp_report(void) {\n#if defined(NAVIGATOR_TRACKPAD_PTP_MODE)\n    printf(\"PTP Report:\\n\");\n    printf(\"  X: %d\\n\", ptp_report.x);\n    printf(\"  Y: %d\\n\", ptp_report.y);\n    printf(\"  Timestamp: %d\\n\", ptp_report.ts);\n    printf(\"  ID: %d\\n\", ptp_report.id);\n    printf(\"  Confidence: %d\\n\", ptp_report.confidence);\n    printf(\"  Tip: %d\\n\", ptp_report.tip);\n    printf(\"  Contact Count: %d\\n\", ptp_report.contact_count);\n    printf(\"  Buttons: %d\\n\", ptp_report.buttons);\n#endif\n}\n\nvoid navigator_trackpad_device_init(void) {\n    i2c_init();\n\n    i2c_status_t status = i2c_ping_address(NAVIGATOR_TRACKPAD_ADDRESS, NAVIGATOR_TRACKPAD_TIMEOUT);\n\n    if (status != I2C_STATUS_SUCCESS) {\n        printf(\"Failed to ping touchpad\\n\");\n        touchpad_init = false;\n        return;\n    }\n\n    cirque_gen6_clear();\n\n    wait_ms(50);\n\n    uint8_t resSize = cirque_gen6_write_reg(0x2001080C, 16);\n    resSize         = cirque_gen6_write_reg(0x2001080D, 16);\n\n    if (resSize != CGEN6_SUCCESS) {\n        printf(\"Failed to set touchpad size\\n\");\n    }\n\n    uint8_t sizeX = cirque_gen6_read_reg(0x2001080C);\n    uint8_t sizeY = cirque_gen6_read_reg(0x2001080D);\n\n    printf(\"Touchpad size: %d x %d\\n\", sizeX, sizeY);\n\n#if defined(NAVIGATOR_TRACKPAD_DEBUG)\n    uint8_t  hardwareId  = cirque_gen6_read_reg(CGEN6_HARDWARE_ID);\n    uint8_t  firmwareId  = cirque_gen6_read_reg(CGEN6_FIRMWARE_ID);\n    uint16_t vendorId    = cirque_gen6_read_reg_16(CGEN6_VENDOR_ID);\n    uint16_t productId   = cirque_gen6_read_reg_16(CGEN6_PRODUCT_ID);\n    uint16_t versionId   = cirque_gen6_read_reg_16(CGEN6_FIRMWARE_REV);\n    uint32_t firmwareRev = cirque_gen6_read_reg_32(CGEN6_FIRMWARE_REV);\n\n    printf(\"Touchpad Hardware ID: 0x%02X\\n\", hardwareId);\n    printf(\"Touchpad Firmware ID: 0x%02X\\n\", firmwareId);\n    printf(\"Touchpad Vendor ID: 0x%04X\\n\", vendorId);\n    printf(\"Touchpad Product ID: 0x%04X\\n\", productId);\n    printf(\"Touchpad Version ID: 0x%04X\\n\", versionId);\n\n    uint32_t revision           = firmwareRev & 0x00ffffff;\n    bool     uncommittedVersion = firmwareRev & 0x80000000;\n    bool     branchVersion      = firmwareRev & 0x40000000;\n    uint8_t  developerId        = firmwareRev & 0x3f000000;\n\n    printf(\"Touchpad Firmware Revision: 0x%08X\\n\", (u_int)revision);\n    printf(\"Touchpad Uncommitted Version: %s\\n\", uncommittedVersion ? \"true\" : \"false\");\n    printf(\"Touchpad Branch Version: %s\\n\", branchVersion ? \"true\" : \"false\");\n    printf(\"Touchpad Developer ID: %d\\n\", developerId);\n#endif\n\n#if defined(NAVIGATOR_TRACKPAD_PTP_MODE)\n    uint8_t res = cirque_gen6_set_ptp_mode();\n#endif\n#if defined(NAVIGATOR_TRACKPAD_RELATIVE_MODE)\n    uint8_t res = cirque_gen6_set_relative_mode();\n#endif\n\n    if (res != CGEN6_SUCCESS) {\n        return;\n    }\n\n    // Reset to the default alignment\n    cirque_gen6_swap_xy(false);\n    cirque_gen6_invert_x(false);\n    cirque_gen6_invert_y(false);\n    cirque_gen6_swap_xy(true);\n    cirque_gen6_invert_x(true);\n    cirque_gen6_invert_y(true);\n    cirque_gen6_enable_logical_scaling(true);\n\n    touchpad_init = true;\n    defer_exec(NAVIGATOR_TRACKPAD_READ, cirque_gen_6_read_callback, NULL);\n}\n\nreport_mouse_t navigator_trackpad_get_report(report_mouse_t mouse_report) {\n    if (!has_motion || !touchpad_init) {\n        if (prev_tap_clear) {\n            prev_tap_clear       = false;\n            mouse_report.buttons = 0;\n        }\n        return mouse_report;\n    }\n\n#if defined(NAVIGATOR_TRACKPAD_RELATIVE_MODE)\n    mouse_report.x       = ptp_report.xDelta;\n    mouse_report.y       = ptp_report.yDelta;\n    mouse_report.v       = ptp_report.scrollDelta;\n    mouse_report.h       = ptp_report.panDelta;\n    mouse_report.buttons = ptp_report.buttons;\n#endif\n#if defined(NAVIGATOR_TRACKPAD_PTP_MODE)\n    if (!prev_ptp_flag && ptp_report.tip) { // Beginning of a motion\n        prev_ptp_x    = ptp_report.x;\n        prev_ptp_y    = ptp_report.y;\n        prev_ptp_flag = true;\n        tap_timer     = timer_read();\n        in_motion     = false;\n    } else if (!ptp_report.tip) { // End of a motion\n        prev_ptp_flag = false;\n        if (in_motion == false) { // Register a tap or double tap\n            if (last_contact_count > 0) {\n                print(\"Double tap detected\\n\");\n#    ifdef NAVIGATOR_TRACKPAD_ENABLE_DOUBLE_TAP\n                mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON2);\n#    endif\n            } else {\n                print(\"Single tap detected\\n\");\n#    ifdef NAVIGATOR_TRACKPAD_ENABLE_TAP\n                mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1);\n#    endif\n            }\n            prev_tap_clear = true;\n        }\n        set_scrolling = false;\n    } else {\n        ptp_delta_x = ptp_report.x - prev_ptp_x;\n        ptp_delta_y = ptp_report.y - prev_ptp_y;\n\n        if (timer_elapsed(tap_timer) >= NAVIGATOR_TRACKPAD_TAP_DEBOUNCE) {\n            if (ptp_report.contact_count > 0) { // More than one finger, return scroll motions\n                set_scrolling = true;\n            }\n\n            if (ptp_delta_x < 0) {\n                mouse_report.x = -powf(-ptp_delta_x, 1.2);\n            } else {\n                mouse_report.x = powf(ptp_delta_x, 1.2);\n            }\n            if (ptp_delta_y < 0) {\n                mouse_report.y = -powf(-ptp_delta_y, 1.2);\n            } else {\n                mouse_report.y = powf(ptp_delta_y, 1.2);\n            }\n\n            prev_ptp_x = ptp_report.x;\n            prev_ptp_y = ptp_report.y;\n\n            in_motion = true;\n        }\n        last_contact_count = ptp_report.contact_count;\n    }\n#endif\n    has_motion = 0;\n    return mouse_report;\n}\n\nvoid set_cirque_cpi(void) {\n    // traverse the sequence by compairing the cpi_x value with the current cpi_x value\n    // set the cpi to the next value in the sequence\n    switch (current_cpi) {\n        case CPI_1: {\n            current_cpi = CPI_2;\n            break;\n        }\n        case CPI_2: {\n            current_cpi = CPI_3;\n            break;\n        }\n        case CPI_3: {\n            current_cpi = CPI_4;\n            break;\n        }\n        case CPI_4: {\n            current_cpi = CPI_5;\n            break;\n        }\n        case CPI_5: {\n            current_cpi = CPI_6;\n            break;\n        }\n        case CPI_6: {\n            current_cpi = CPI_7;\n            break;\n        }\n        case CPI_7: {\n            current_cpi = CPI_1;\n            break;\n        }\n        default: {\n            current_cpi = CPI_4;\n            break;\n        }\n    }\n}\n\nuint16_t navigator_trackpad_get_cpi(void) {\n    return current_cpi;\n}\n\nvoid restore_cpi(uint8_t cpi) {\n    current_cpi = cpi;\n    set_cirque_cpi();\n}\n\nvoid navigator_trackpad_set_cpi(uint16_t cpi) {\n    if (cpi == 0) { // Decrease one tick\n        if (current_cpi > 1) {\n            current_cpi--;\n        }\n    } else {\n        if (current_cpi < CPI_TICKS) {\n            current_cpi++;\n        }\n    }\n    set_cirque_cpi();\n};\n"
  },
  {
    "path": "drivers/sensors/navigator_trackpad.h",
    "content": "// Copyright 2025 ZSA Technology Labs, Inc <contact@zsa.io>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n#include <stdint.h>\n#include <stdbool.h>\n#include \"report.h\"\n#include \"pointing_device.h\"\n\n#    ifndef CIRQUE_PINNACLE_X_LOWER\n#        define CIRQUE_PINNACLE_X_LOWER 127 // min \"reachable\" X value\n#    endif\n#    ifndef CIRQUE_PINNACLE_X_UPPER\n#        define CIRQUE_PINNACLE_X_UPPER 1919 // max \"reachable\" X value\n#    endif\n#    ifndef CIRQUE_PINNACLE_Y_LOWER\n#        define CIRQUE_PINNACLE_Y_LOWER 63 // min \"reachable\" Y value\n#    endif\n#    ifndef CIRQUE_PINNACLE_Y_UPPER\n#        define CIRQUE_PINNACLE_Y_UPPER 1471 // max \"reachable\" Y value\n#    endif\n#    ifndef CIRQUE_PINNACLE_X_RANGE\n#        define CIRQUE_PINNACLE_X_RANGE (CIRQUE_PINNACLE_X_UPPER - CIRQUE_PINNACLE_X_LOWER)\n#    endif\n#    ifndef CIRQUE_PINNACLE_Y_RANGE\n#        define CIRQUE_PINNACLE_Y_RANGE (CIRQUE_PINNACLE_Y_UPPER - CIRQUE_PINNACLE_Y_LOWER)\n#    endif\n\n#define NAVIGATOR_TRACKPAD_READ 7\n#define NAVIGATOR_TRACKPAD_TAPPING_TERM 100\n#define NAVIGATOR_TRACKPAD_TAP_DEBOUNCE 100\n\n#ifndef NAVIGATOR_TRACKPAD_ADDRESS\n#    define NAVIGATOR_TRACKPAD_ADDRESS 0x58\n#endif\n\n#ifndef NAVIGATOR_TRACKPAD_TIMEOUT\n#    define NAVIGATOR_TRACKPAD_TIMEOUT 100\n#endif\n\n#define NAVIGATOR_TRACKPAD_PTP_MODE\n#if !defined(NAVIGATOR_TRACKPAD_RELATIVE_MODE) && !defined(NAVIGATOR_TRACKPAD_PTP_MODE)\n#    define NAVIGATOR_TRACKPAD_PTP_MODE\n#endif\n\n#define CGEN6_MAX_PACKET_SIZE 17\n#define CGEN6_PTP_REPORT_ID 0x01\n#define CGEN6_MOUSE_REPORT_ID 0x06\n#define CGEN6_ABSOLUTE_REPORT_ID 0x09\n\n// C3 error codes when reading memory\n#define CGEN6_SUCCESS 0x00\n#define CGEN6_CKSUM_FAILED 0x01\n#define CGEN6_LEN_MISMATCH 0x02\n#define CGEN6_I2C_FAILED 0x03\n\n// C3 register addresses\n#define CGEN6_REG_BASE 0x20000800\n#define CGEN6_HARDWARE_ID CGEN6_REG_BASE + 0x08\n#define CGEN6_FIRMWARE_ID CGEN6_REG_BASE + 0x09\n#define CGEN6_FIRMWARE_REV CGEN6_REG_BASE + 0x10\n#define CGEN6_VENDOR_ID CGEN6_REG_BASE + 0x0A\n#define CGEN6_PRODUCT_ID CGEN6_REG_BASE + 0x0C\n#define CGEN6_VERSION_ID CGEN6_REG_BASE + 0x0E\n#define CGEN6_FEED_CONFIG4 0x200E000B\n#define CGEN6_FEED_CONFIG3 0x200E000A\n#define CGEN6_SYS_CONFIG1 0x20000008\n#define CGEN6_XY_CONFIG 0x20080018\n#define CGEN6_SFR_BASE 0x40000008\n#define CGEN6_GPIO_BASE 0x00052000\n\n#define CPI_TICKS 7\n#define DEFAULT_CPI_TICK 4\n#define CPI_1 200\n#define CPI_2 400\n#define CPI_3 800\n#define CPI_4 1024\n#define CPI_5 1400\n#define CPI_6 1800\n#define CPI_7 2048\n\n#ifndef NAVIGATOR_TRACKPAD_SCROLL_DIVIDER\n#    define NAVIGATOR_TRACKPAD_SCROLL_DIVIDER 10\n#endif\n\n#if defined(NAVIGATOR_TRACKPAD_PTP_MODE)\n#    ifndef MOUSE_EXTENDED_REPORT\n#        define MOUSE_EXTENDED_REPORT\n#    endif\ntypedef struct {\n    uint16_t x;\n    uint16_t y;\n    uint16_t ts;\n    uint8_t  id;\n    uint8_t  confidence;\n    uint8_t  tip;\n    uint8_t  contact_count;\n    uint8_t  buttons;\n} cgen6_report_t;\n#endif\n\n#if defined(NAVIGATOR_TRACKPAD_ABSOLUTE_MODE)\ntypedef struct {\n    uint16_t x;\n    uint16_t y;\n    uint8_t  palm;\n    uint8_t  z;\n} finger_data_t;\n\ntypedef struct {\n    finger_data_t fingers[3]; // Cirque support 5 fingers, we only need 3 for our application\n    uint8_t       contact_flags;\n    uint8_t       buttons;\n} cgen6_report_t;\n#endif\n\n#if defined(NAVIGATOR_TRACKPAD_RELATIVE_MODE)\ntypedef struct {\n    uint8_t buttons;\n    int8_t  xDelta;\n    int8_t  yDelta;\n    int8_t  scrollDelta;\n    int8_t  panDelta;\n} cgen6_report_t;\n#endif\n\nconst pointing_device_driver_t navigator_trackpad_pointing_device_driver;\nvoid           navigator_trackpad_device_init(void);\nreport_mouse_t navigator_trackpad_get_report(report_mouse_t mouse_report);\nuint16_t       navigator_trackpad_get_cpi(void);\nvoid           navigator_trackpad_set_cpi(uint16_t cpi);\nvoid           restore_cpi(uint8_t cpi);\n"
  },
  {
    "path": "drivers/sensors/paw3204.c",
    "content": "/* Copyright 2021 Gompa (@Gompa)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// https://github.com/shinoaliceKabocha/choco60_track/tree/master/keymaps/default\n\n#include \"paw3204.h\"\n#include \"wait.h\"\n#include \"debug.h\"\n#include \"gpio.h\"\n#include \"pointing_device_internal.h\"\n\n#define REG_PID1 0x00\n#define REG_PID2 0x01\n#define REG_STAT 0x02\n#define REG_X 0x03\n#define REG_Y 0x04\n\n#define REG_SETUP 0x06\n#define REG_IMGQUAL 0x07\n#define REG_IMGREC 0x0E\n#define REG_IMGTRASH 0x0D\n\n#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))\n\n// CPI values\nenum cpi_values {\n    CPI400,  // 0b000\n    CPI500,  // 0b001\n    CPI600,  // 0b010\n    CPI800,  // 0b011\n    CPI1000, // 0b100\n    CPI1200, // 0b101\n    CPI1600, // 0b110\n};\n\nuint8_t paw3204_serial_read(void);\nvoid    paw3204_serial_write(uint8_t reg_addr);\nuint8_t paw3204_read_reg(uint8_t reg_addr);\nvoid    paw3204_write_reg(uint8_t reg_addr, uint8_t data);\n\nconst pointing_device_driver_t paw3204_pointing_device_driver = {\n    .init       = paw3204_init,\n    .get_report = paw3204_get_report,\n    .set_cpi    = paw3204_set_cpi,\n    .get_cpi    = paw3204_get_cpi,\n};\n\nvoid paw3204_init(void) {\n    gpio_set_pin_output(PAW3204_SCLK_PIN);     // setclockpin to output\n    gpio_set_pin_input_high(PAW3204_SDIO_PIN); // set datapin input high\n\n    paw3204_write_reg(REG_SETUP, 0x86); // reset sensor and set 1600cpi\n    wait_us(5);\n\n    paw3204_read_reg(0x00); // read id\n    paw3204_read_reg(0x01); // read id2\n    // PAW3204_write_reg(REG_SETUP,0x06);  // dont reset sensor and set cpi 1600\n    paw3204_write_reg(REG_IMGTRASH, 0x32); // write image trashhold\n}\n\nuint8_t paw3204_serial_read(void) {\n    gpio_set_pin_input(PAW3204_SDIO_PIN);\n    uint8_t byte = 0;\n\n    for (uint8_t i = 0; i < 8; ++i) {\n        gpio_write_pin_low(PAW3204_SCLK_PIN);\n        wait_us(1);\n\n        byte = (byte << 1) | gpio_read_pin(PAW3204_SDIO_PIN);\n\n        gpio_write_pin_high(PAW3204_SCLK_PIN);\n        wait_us(1);\n    }\n\n    return byte;\n}\n\nvoid paw3204_serial_write(uint8_t data) {\n    gpio_write_pin_low(PAW3204_SDIO_PIN);\n    gpio_set_pin_output(PAW3204_SDIO_PIN);\n\n    for (int8_t b = 7; b >= 0; b--) {\n        gpio_write_pin_low(PAW3204_SCLK_PIN);\n        if (data & (1 << b)) {\n            gpio_write_pin_high(PAW3204_SDIO_PIN);\n        } else {\n            gpio_write_pin_low(PAW3204_SDIO_PIN);\n        }\n        gpio_write_pin_high(PAW3204_SCLK_PIN);\n    }\n\n    wait_us(4);\n}\n\nreport_paw3204_t paw3204_read(void) {\n    report_paw3204_t data = {0};\n\n    data.isMotion = paw3204_read_reg(REG_STAT) & (1 << 7); // check for motion only (bit 7 in field)\n    data.x        = (int8_t)paw3204_read_reg(REG_X);\n    data.y        = (int8_t)paw3204_read_reg(REG_Y);\n\n    return data;\n}\n\nvoid paw3204_write_reg(uint8_t reg_addr, uint8_t data) {\n    paw3204_serial_write(0b10000000 | reg_addr);\n    paw3204_serial_write(data);\n}\n\nuint8_t paw3204_read_reg(uint8_t reg_addr) {\n    paw3204_serial_write(reg_addr);\n    wait_us(5);\n    return paw3204_serial_read();\n}\n\nvoid paw3204_set_cpi(uint16_t cpi) {\n    uint8_t cpival = CPI1000;\n    if (cpi <= 450) {\n        cpival = CPI400;\n    } else if (cpi <= 550) {\n        cpival = CPI500;\n    } else if (cpi <= 700) {\n        cpival = CPI600;\n    } else if (cpi <= 900) {\n        cpival = CPI800;\n    } else if (cpi <= 1100) {\n        cpival = CPI1000;\n    } else if (cpi <= 1400) {\n        cpival = CPI1200;\n    } else if (cpi > 1400) {\n        cpival = CPI1600;\n    }\n    paw3204_write_reg(REG_SETUP, cpival);\n}\n\nuint16_t paw3204_get_cpi(void) {\n    uint16_t cpival = 1000;\n\n    switch (paw3204_read_reg(REG_SETUP) & 0b111) {\n        case CPI400:\n            cpival = 400;\n            break;\n        case CPI500:\n            cpival = 500;\n            break;\n        case CPI600:\n            cpival = 600;\n            break;\n        case CPI800:\n            cpival = 800;\n            break;\n        case CPI1000:\n            cpival = 1000;\n            break;\n        case CPI1200:\n            cpival = 1200;\n            break;\n        case CPI1600:\n            cpival = 1600;\n            break;\n    }\n    return cpival;\n}\n\nuint8_t read_pid_paw3204(void) {\n    return paw3204_read_reg(REG_PID1);\n}\n\nreport_mouse_t paw3204_get_report(report_mouse_t mouse_report) {\n    report_paw3204_t data = paw3204_read();\n    if (data.isMotion) {\n        pd_dprintf(\"Raw ] X: %d, Y: %d\\n\", data.x, data.y);\n\n        mouse_report.x = data.x;\n        mouse_report.y = data.y;\n    }\n\n    return mouse_report;\n}\n"
  },
  {
    "path": "drivers/sensors/paw3204.h",
    "content": "/* Copyright 2021 Gompa (@Gompa)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"pointing_device.h\"\n\n#ifndef PAW3204_SCLK_PIN\n#    ifdef POINTING_DEVICE_SCLK_PIN\n#        define PAW3204_SCLK_PIN POINTING_DEVICE_SCLK_PIN\n#    else\n#        error \"No clock pin defined -- missing POINTING_DEVICE_SCLK_PIN or PAW3204_SCLK_PIN\"\n#    endif\n#endif\n#ifndef PAW3204_SDIO_PIN\n#    ifdef POINTING_DEVICE_SDIO_PIN\n#        define PAW3204_SDIO_PIN POINTING_DEVICE_SDIO_PIN\n#    else\n#        error \"No data pin defined -- missing POINTING_DEVICE_SDIO_PIN or PAW3204_SDIO_PIN\"\n#    endif\n#endif\n\ntypedef struct {\n    int16_t x;\n    int16_t y;\n    bool    isMotion;\n} report_paw3204_t;\n\nconst pointing_device_driver_t paw3204_pointing_device_driver;\n\n/**\n * @brief Initializes the sensor so it is in a working state and ready to\n * be polled for data.\n *\n * @return true Initialization was a success\n * @return false Initialization failed, do not proceed operation\n */\nvoid paw3204_init(void);\n\n/**\n * @brief Reads and clears the current delta, and motion register values on the\n * given sensor.\n *\n * @return pmw33xx_report_t Current values of the sensor, if errors occurred all\n * fields are set to zero\n */\n\nreport_paw3204_t paw3204_read(void);\n/**\n * @brief Sets the given CPI value the sensor. CPI is  often refereed to\n * as the sensors sensitivity. Values outside of the allowed range are\n * constrained into legal values.\n *\n * @param cpi CPI value to set\n */\nvoid paw3204_set_cpi(uint16_t cpi);\n\n/**\n * @brief Gets the currently set CPI value from the sensor. CPI is often\n * refereed to as the sensors sensitivity.\n *\n * @return uint16_t Current CPI value of the sensor\n */\nuint16_t paw3204_get_cpi(void);\n\nreport_mouse_t paw3204_get_report(report_mouse_t mouse_report);\n"
  },
  {
    "path": "drivers/sensors/pimoroni_trackball.c",
    "content": "/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n * Copyright 2021 Dasky (@daskygit)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"pointing_device_internal.h\"\n#include \"pimoroni_trackball.h\"\n#include \"i2c_master.h\"\n#include \"timer.h\"\n\n// clang-format off\n#define PIMORONI_TRACKBALL_REG_LED_RED 0x00\n#define PIMORONI_TRACKBALL_REG_LED_GRN 0x01\n#define PIMORONI_TRACKBALL_REG_LED_BLU 0x02\n#define PIMORONI_TRACKBALL_REG_LED_WHT 0x03\n#define PIMORONI_TRACKBALL_REG_LEFT    0x04\n#define PIMORONI_TRACKBALL_REG_RIGHT   0x05\n#define PIMORONI_TRACKBALL_REG_UP      0x06\n#define PIMORONI_TRACKBALL_REG_DOWN    0x07\n// clang-format on\n\nstatic uint16_t precision = 128;\n\nconst pointing_device_driver_t pimoroni_trackball_pointing_device_driver = {\n    .init       = pimoroni_trackball_device_init,\n    .get_report = pimoroni_trackball_get_report,\n    .set_cpi    = pimoroni_trackball_set_cpi,\n    .get_cpi    = pimoroni_trackball_get_cpi,\n};\n\nuint16_t pimoroni_trackball_get_cpi(void) {\n    return (precision * 125);\n}\n/**\n * @brief Sets the scaling value for pimoroni trackball\n *\n * Sets a scaling value for pimoroni trackball to allow runtime adjustment. This isn't used by the sensor and is an\n * approximation so the functions are consistent across drivers.\n *\n * NOTE: This rounds down to the nearest number divisable by 125 that's a positive integer, values below 125 are clamped to 125.\n *\n * @param cpi uint16_t\n */\nvoid pimoroni_trackball_set_cpi(uint16_t cpi) {\n    if (cpi < 249) {\n        precision = 1;\n    } else {\n        precision = (cpi - (cpi % 125)) / 125;\n    }\n}\n\nvoid pimoroni_trackball_set_rgbw(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {\n    uint8_t                              data[4] = {r, g, b, w};\n    __attribute__((unused)) i2c_status_t status  = i2c_write_register(PIMORONI_TRACKBALL_ADDRESS << 1, PIMORONI_TRACKBALL_REG_LED_RED, data, sizeof(data), PIMORONI_TRACKBALL_TIMEOUT);\n\n    pd_dprintf(\"Trackball RGBW i2c_status_t: %d\\n\", status);\n}\n\ni2c_status_t read_pimoroni_trackball(pimoroni_data_t *data) {\n    i2c_status_t status = i2c_read_register(PIMORONI_TRACKBALL_ADDRESS << 1, PIMORONI_TRACKBALL_REG_LEFT, (uint8_t *)data, sizeof(*data), PIMORONI_TRACKBALL_TIMEOUT);\n\n#ifdef POINTING_DEVICE_DEBUG\n    static uint16_t d_timer;\n    if (timer_elapsed(d_timer) > PIMORONI_TRACKBALL_DEBUG_INTERVAL) {\n        pd_dprintf(\"Trackball READ i2c_status_t: %d L: %d R: %d Up: %d D: %d SW: %d\\n\", status, data->left, data->right, data->up, data->down, data->click);\n        d_timer = timer_read();\n    }\n#endif\n\n    return status;\n}\n\n__attribute__((weak)) void pimoroni_trackball_device_init(void) {\n    i2c_init();\n    pimoroni_trackball_set_rgbw(0x00, 0x00, 0x00, 0x00);\n}\n\nint16_t pimoroni_trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale) {\n    uint8_t offset     = 0;\n    bool    isnegative = false;\n    if (negative_dir > positive_dir) {\n        offset     = negative_dir - positive_dir;\n        isnegative = true;\n    } else {\n        offset = positive_dir - negative_dir;\n    }\n    uint16_t magnitude = (scale * offset * offset * precision) >> 7;\n    return isnegative ? -(int16_t)(magnitude) : (int16_t)(magnitude);\n}\n\nmouse_xy_report_t pimoroni_trackball_adapt_values(xy_clamp_range_t *offset) {\n    if (*offset > MOUSE_REPORT_XY_MAX) {\n        *offset -= MOUSE_REPORT_XY_MAX;\n        return (mouse_xy_report_t)MOUSE_REPORT_XY_MAX;\n    } else if (*offset < MOUSE_REPORT_XY_MIN) {\n        *offset += MOUSE_REPORT_XY_MAX;\n        return (mouse_xy_report_t)MOUSE_REPORT_XY_MIN;\n    } else {\n        mouse_xy_report_t temp_return = *offset;\n        *offset                       = 0;\n        return temp_return;\n    }\n}\n\nreport_mouse_t pimoroni_trackball_get_report(report_mouse_t mouse_report) {\n    static uint16_t         debounce      = 0;\n    static uint8_t          error_count   = 0;\n    pimoroni_data_t         pimoroni_data = {0};\n    static xy_clamp_range_t x_offset = 0, y_offset = 0;\n\n    if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT) {\n        i2c_status_t status = read_pimoroni_trackball(&pimoroni_data);\n\n        if (status == I2C_STATUS_SUCCESS) {\n            error_count = 0;\n\n            if (!(pimoroni_data.click & 128)) {\n                mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, false, POINTING_DEVICE_BUTTON1);\n                if (!debounce) {\n                    x_offset += pimoroni_trackball_get_offsets(pimoroni_data.right, pimoroni_data.left, PIMORONI_TRACKBALL_SCALE);\n                    y_offset += pimoroni_trackball_get_offsets(pimoroni_data.down, pimoroni_data.up, PIMORONI_TRACKBALL_SCALE);\n                    mouse_report.x = pimoroni_trackball_adapt_values(&x_offset);\n                    mouse_report.y = pimoroni_trackball_adapt_values(&y_offset);\n                } else {\n                    debounce--;\n                }\n            } else {\n                mouse_report.buttons = pointing_device_handle_buttons(mouse_report.buttons, true, POINTING_DEVICE_BUTTON1);\n                debounce             = PIMORONI_TRACKBALL_DEBOUNCE_CYCLES;\n            }\n        } else {\n            error_count++;\n        }\n    }\n    return mouse_report;\n}\n"
  },
  {
    "path": "drivers/sensors/pimoroni_trackball.h",
    "content": "/* Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n * Copyright 2021 Dasky (@daskygit)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <stdint.h>\n#include \"report.h\"\n#include \"i2c_master.h\"\n#include \"pointing_device.h\"\n\n#ifndef PIMORONI_TRACKBALL_ADDRESS\n#    define PIMORONI_TRACKBALL_ADDRESS 0x0A\n#endif\n#ifndef PIMORONI_TRACKBALL_SCALE\n#    define PIMORONI_TRACKBALL_SCALE 5\n#endif\n#ifndef PIMORONI_TRACKBALL_DEBOUNCE_CYCLES\n#    define PIMORONI_TRACKBALL_DEBOUNCE_CYCLES 20\n#endif\n#ifndef PIMORONI_TRACKBALL_ERROR_COUNT\n#    define PIMORONI_TRACKBALL_ERROR_COUNT 10\n#endif\n\n#ifndef PIMORONI_TRACKBALL_TIMEOUT\n#    define PIMORONI_TRACKBALL_TIMEOUT 100\n#endif\n\n#ifndef PIMORONI_TRACKBALL_DEBUG_INTERVAL\n#    define PIMORONI_TRACKBALL_DEBUG_INTERVAL 100\n#endif\n\ntypedef struct {\n    uint8_t left;\n    uint8_t right;\n    uint8_t up;\n    uint8_t down;\n    uint8_t click;\n} pimoroni_data_t;\n\nconst pointing_device_driver_t pimoroni_trackball_pointing_device_driver;\n\nvoid           pimoroni_trackball_device_init(void);\nvoid           pimoroni_trackball_set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white);\nint16_t        pimoroni_trackball_get_offsets(uint8_t negative_dir, uint8_t positive_dir, uint8_t scale);\nuint16_t       pimoroni_trackball_get_cpi(void);\nvoid           pimoroni_trackball_set_cpi(uint16_t cpi);\ni2c_status_t   read_pimoroni_trackball(pimoroni_data_t* data);\nreport_mouse_t pimoroni_trackball_get_report(report_mouse_t mouse_report);\n"
  },
  {
    "path": "drivers/sensors/pmw3320.c",
    "content": "/* Copyright 2021 Colin Lam (Ploopy Corporation)\n * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n * Copyright 2019 Sunjun Kim\n * Copyright 2019 Hiroyuki Okada\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"pmw3320.h\"\n#include \"wait.h\"\n#include \"debug.h\"\n#include \"gpio.h\"\n#include \"pointing_device_internal.h\"\n\nconst pointing_device_driver_t pmw3320_pointing_device_drivera = {\n    .init       = pmw3320_init,\n    .get_report = pmw3320_get_report,\n    .set_cpi    = pmw3320_set_cpi,\n    .get_cpi    = pmw3320_get_cpi,\n};\n\nvoid pmw3320_init(void) {\n    // Initialize sensor serial pins.\n    gpio_set_pin_output(PMW3320_SCLK_PIN);\n    gpio_set_pin_output(PMW3320_SDIO_PIN);\n    gpio_set_pin_output(PMW3320_CS_PIN);\n\n    // reboot the sensor.\n    pmw3320_write_reg(REG_Power_Up_Reset, 0x5a);\n\n    // wait maximum time before sensor is ready.\n    // this ensures that the sensor is actually ready after reset.\n    wait_ms(55);\n\n    // read a burst from the sensor and then discard it.\n    // gets the sensor ready for write commands\n    // (for example, setting the dpi).\n    pmw3320_read_burst();\n\n    // Pretty sure that this shouldn't be in the driver.\n    // Probably device specific?\n    // Set rest mode to default\n    pmw3320_write_reg(REG_Rest_Mode_Status, 0x00);\n    // Set LED to be always on\n    pmw3320_write_reg(REG_Led_Control, 0x4);\n    // Disable rest mode\n    pmw3320_write_reg(REG_Performance, 0x80);\n}\n\n// Perform a synchronization with sensor.\n// Just as with the serial protocol, this is used by the slave to send a\n// synchronization signal to the master.\nvoid pmw3320_sync(void) {\n    gpio_write_pin_low(PMW3320_CS_PIN);\n    wait_us(1);\n    gpio_write_pin_high(PMW3320_CS_PIN);\n}\n\nvoid pmw3320_cs_select(void) {\n    gpio_write_pin_low(PMW3320_CS_PIN);\n}\n\nvoid pmw3320_cs_deselect(void) {\n    gpio_write_pin_high(PMW3320_CS_PIN);\n}\n\nuint8_t pmw3320_serial_read(void) {\n    gpio_set_pin_input(PMW3320_SDIO_PIN);\n    uint8_t byte = 0;\n\n    for (uint8_t i = 0; i < 8; ++i) {\n        gpio_write_pin_low(PMW3320_SCLK_PIN);\n        wait_us(1);\n\n        byte = (byte << 1) | gpio_read_pin(PMW3320_SDIO_PIN);\n\n        gpio_write_pin_high(PMW3320_SCLK_PIN);\n        wait_us(1);\n    }\n\n    return byte;\n}\n\nvoid pmw3320_serial_write(uint8_t data) {\n    gpio_set_pin_output(PMW3320_SDIO_PIN);\n\n    for (int8_t b = 7; b >= 0; b--) {\n        gpio_write_pin_low(PMW3320_SCLK_PIN);\n\n        if (data & (1 << b))\n            gpio_write_pin_high(PMW3320_SDIO_PIN);\n        else\n            gpio_write_pin_low(PMW3320_SDIO_PIN);\n\n        wait_us(2);\n\n        gpio_write_pin_high(PMW3320_SCLK_PIN);\n    }\n\n    // This was taken from ADNS5050 driver.\n    // There's no any info in PMW3320 datasheet about this...\n    // tSWR. See page 15 of the ADNS5050 spec sheet.\n    // Technically, this is only necessary if the next operation is an SDIO\n    // read. This is not guaranteed to be the case, but we're being lazy.\n    wait_us(4);\n\n    // Note that tSWW is never necessary. All write operations require at\n    // least 32us, which exceeds tSWW, so there's never a need to wait for it.\n}\n\n// Read a byte of data from a register on the sensor.\nuint8_t pmw3320_read_reg(uint8_t reg_addr) {\n    pmw3320_cs_select();\n\n    pmw3320_serial_write(reg_addr);\n\n    uint8_t byte = pmw3320_serial_read();\n\n    // This was taken directly from ADNS5050 driver...\n    // tSRW & tSRR. See page 15 of the ADNS5050 spec sheet.\n    // Technically, this is only necessary if the next operation is an SDIO\n    // read or write. This is not guaranteed to be the case.\n    // Honestly, this wait could probably be removed.\n    wait_us(1);\n\n    pmw3320_cs_deselect();\n\n    return byte;\n}\n\nvoid pmw3320_write_reg(uint8_t reg_addr, uint8_t data) {\n    pmw3320_cs_select();\n    pmw3320_serial_write(0b10000000 | reg_addr);\n    pmw3320_serial_write(data);\n    pmw3320_cs_deselect();\n}\n\nreport_pmw3320_t pmw3320_read_burst(void) {\n    pmw3320_cs_select();\n\n    report_pmw3320_t data;\n    data.dx = 0;\n    data.dy = 0;\n\n    pmw3320_serial_write(REG_Motion_Burst);\n\n    uint8_t x = pmw3320_serial_read();\n    uint8_t y = pmw3320_serial_read();\n\n    // Probably burst mode may include contents of delta_xy register,\n    // which contain HI parts of x/y deltas, but I had no luck finding it.\n    // Probably it's required to activate 12-bit mode to access this data.\n    // So we end burst mode early to not read unneeded information.\n    pmw3320_cs_deselect();\n\n    data.dx = convert_twoscomp(x);\n    data.dy = convert_twoscomp(y);\n\n    return data;\n}\n\n// Convert a two's complement byte from an unsigned data type into a signed\n// data type.\nint8_t convert_twoscomp(uint8_t data) {\n    if ((data & 0x80) == 0x80)\n        return -128 + (data & 0x7F);\n    else\n        return data;\n}\n\nuint16_t pmw3320_get_cpi(void) {\n    uint8_t cpival = pmw3320_read_reg(REG_Resolution);\n    // 0x1F is an inversion of 0x20 which is 0b100000\n    return (uint16_t)((cpival & 0x1F) * PMW3320_CPI_STEP);\n}\n\nvoid pmw3320_set_cpi(uint16_t cpi) {\n    uint8_t cpival = constrain((cpi / PMW3320_CPI_STEP), (PMW3320_CPI_MIN / PMW3320_CPI_STEP), (PMW3320_CPI_MAX / PMW3320_CPI_STEP)) - 1U;\n    // Fifth bit is probably a control bit.\n    // PMW3320 datasheet don't have any info on this, so this is a pure guess.\n    pmw3320_write_reg(REG_Resolution, 0x20 | cpival);\n}\n\nbool pmw3320_check_signature(void) {\n    uint8_t pid  = pmw3320_read_reg(REG_Product_ID);\n    uint8_t pid2 = pmw3320_read_reg(REG_Inverse_Product_ID);\n\n    return (pid == 0x3b && pid2 == 0xc4);\n}\n\nreport_mouse_t pmw3320_get_report(report_mouse_t mouse_report) {\n    report_pmw3320_t data = pmw3320_read_burst();\n\n    if (data.dx != 0 || data.dy != 0) {\n        pd_dprintf(\"Raw ] X: %d, Y: %d\\n\", data.dx, data.dy);\n        mouse_report.x = (mouse_xy_report_t)data.dx;\n        mouse_report.y = (mouse_xy_report_t)data.dy;\n    }\n\n    return mouse_report;\n}\n"
  },
  {
    "path": "drivers/sensors/pmw3320.h",
    "content": "/* Copyright 2021 Colin Lam (Ploopy Corporation)\n * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n * Copyright 2019 Sunjun Kim\n * Copyright 2019 Hiroyuki Okada\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"pointing_device.h\"\n\n#define constrain(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))\n\n// Definitions for the PMW3320 serial line.\n#ifndef PMW3320_SCLK_PIN\n#    ifdef POINTING_DEVICE_SCLK_PIN\n#        define PMW3320_SCLK_PIN POINTING_DEVICE_SCLK_PIN\n#    else\n#        error \"No clock pin defined -- missing POINTING_DEVICE_SCLK_PIN or PMW3320_SCLK_PIN\"\n#    endif\n#endif\n\n#ifndef PMW3320_SDIO_PIN\n#    ifdef POINTING_DEVICE_SDIO_PIN\n#        define PMW3320_SDIO_PIN POINTING_DEVICE_SDIO_PIN\n#    else\n#        error \"No data pin defined -- missing POINTING_DEVICE_SDIO_PIN or PMW3320_SDIO_PIN\"\n#    endif\n#endif\n\n#ifndef PMW3320_CS_PIN\n#    ifdef POINTING_DEVICE_CS_PIN\n#        define PMW3320_CS_PIN POINTING_DEVICE_CS_PIN\n#    else\n#        error \"No chip select pin defined -- missing POINTING_DEVICE_CS_PIN or PMW3320_CS_PIN define\"\n#    endif\n#endif\n\ntypedef struct {\n    int8_t dx;\n    int8_t dy;\n} report_pmw3320_t;\n\nconst pointing_device_driver_t pmw3320_pointing_device_driver;\n\n// A bunch of functions to implement the PMW3320-specific serial protocol.\n// Mostly taken from ADNS5050 driver.\n// Note that the \"serial.h\" driver is insufficient, because it does not\n// manually manipulate a serial clock signal.\nvoid             pmw3320_init(void);\nvoid             pmw3320_sync(void);\nuint8_t          pmw3320_serial_read(void);\nvoid             pmw3320_serial_write(uint8_t data);\nuint8_t          pmw3320_read_reg(uint8_t reg_addr);\nvoid             pmw3320_write_reg(uint8_t reg_addr, uint8_t data);\nreport_pmw3320_t pmw3320_read_burst(void);\nvoid             pmw3320_set_cpi(uint16_t cpi);\nuint16_t         pmw3320_get_cpi(void);\nint8_t           convert_twoscomp(uint8_t data);\nbool             pmw3320_check_signature(void);\nreport_mouse_t   pmw3320_get_report(report_mouse_t mouse_report);\n\n#if !defined(PMW3320_CPI)\n#    define PMW3320_CPI 1000\n#endif\n\n#define PMW3320_CPI_STEP 250\n#define PMW3320_CPI_MIN 250\n#define PMW3320_CPI_MAX 3500\n\n// PMW3320 register addresses\n// clang-format off\n#define REG_Product_ID                 0x00\n#define REG_Revision_ID                0x01\n#define REG_Motion                     0x02\n#define REG_Delta_X                    0x03\n#define REG_Delta_Y                    0x04\n#define REG_SQUAL                      0x05\n#define REG_Shutter_Upper              0x06\n#define REG_Shutter_Lower              0x07\n#define REG_Maximum_Pixel              0x08\n#define REG_Pixel_Accum                0x09\n#define REG_Minimum_Pixel              0x0a\n#define REG_Pixel_Grab                 0x0b\n#define REG_Delta_XY                   0x0c\n#define REG_Resolution                 0x0d\n#define REG_Run_Downshift              0x0e\n#define REG_Rest1_Period               0x0f\n#define REG_Rest1_Downshift            0x10\n#define REG_Rest2_Preiod               0x11\n#define REG_Rest2_Downshift            0x12\n#define REG_Rest3_Period               0x13\n#define REG_Min_SQ_Run                 0x17\n#define REG_Axis_Control               0x1a\n#define REG_Performance                0x22\n#define REG_Low_Motion_Jitter          0x23\n#define REG_Shutter_Max_HI             0x36\n#define REG_Shutter_Max_LO             0x37\n#define REG_Frame_Rate                 0x39\n#define REG_Power_Up_Reset             0x3a\n#define REG_Shutdown                   0x3b\n#define REG_Inverse_Revision_ID        0x3f\n#define REG_Led_Control                0x40\n#define REG_Motion_Control             0x41\n#define REG_Burst_Read_First           0x42\n#define REG_Rest_Mode_Status           0x45\n#define REG_Inverse_Product_ID         0x4f\n#define REG_Motion_Burst               0x63\n// clang-format on\n"
  },
  {
    "path": "drivers/sensors/pmw3360.c",
    "content": "// Copyright 2022 Stefan Kerkmann (KarlK90)\n// Copyright 2022 Ulrich Spörlein (@uqs)\n// Copyright 2021 Alabastard (@Alabastard-64)\n// Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// Copyright 2019 Sunjun Kim\n// Copyright 2020 Ploopy Corporation\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"pmw33xx_common.h\"\n#include \"progmem.h\"\n\nuint16_t pmw33xx_get_cpi(uint8_t sensor) {\n    if (sensor >= pmw33xx_number_of_sensors) {\n        return 0;\n    }\n\n    uint8_t cpival = pmw33xx_read(sensor, REG_Config1);\n    // In some cases (100, 900, 1700, 2500), reading the CPI corrupts the firmware and the sensor stops responding.\n    // To avoid this, we write the value back to the sensor, which seems to prevent the corruption.\n    pmw33xx_write(sensor, REG_Config1, cpival);\n    return (uint16_t)((cpival + 1) & 0xFF) * PMW33XX_CPI_STEP;\n}\n\nvoid pmw33xx_set_cpi(uint8_t sensor, uint16_t cpi) {\n    if (sensor >= pmw33xx_number_of_sensors) {\n        return;\n    }\n\n    uint8_t cpival = CONSTRAIN((cpi / PMW33XX_CPI_STEP), (PMW33XX_CPI_MIN / PMW33XX_CPI_STEP), (PMW33XX_CPI_MAX / PMW33XX_CPI_STEP)) - 1U;\n    pmw33xx_write(sensor, REG_Config1, cpival);\n}\n\n// PID, Inverse PID, SROM version\nconst uint8_t pmw33xx_firmware_signature[2] PROGMEM = {0x42, 0xBD};\n"
  },
  {
    "path": "drivers/sensors/pmw3360.h",
    "content": "// Copyright 2022 Stefan Kerkmann (KarlK90)\n// Copyright 2022 Ulrich Spörlein (@uqs)\n// Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// Copyright 2019 Sunjun Kim\n// Copyright 2020 Ploopy Corporation\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n\n#if !defined(PMW33XX_CPI)\n#    define PMW33XX_CPI 1600U\n#endif\n\n#define PMW33XX_CPI_STEP 100\n#define PMW33XX_CPI_MIN 100\n#define PMW33XX_CPI_MAX 12000\n\n#define PMW33XX_FIRMWARE_LENGTH 4094\n\n// PMW3360 register addresses\n// clang-format off\n#define REG_Product_ID                   0x00\n#define REG_Revision_ID                  0x01\n#define REG_Motion                       0x02\n#define REG_Delta_X_L                    0x03\n#define REG_Delta_X_H                    0x04\n#define REG_Delta_Y_L                    0x05\n#define REG_Delta_Y_H                    0x06\n#define REG_SQUAL                        0x07\n#define REG_Raw_Data_Sum                 0x08\n#define REG_Maximum_Raw_data             0x09\n#define REG_Minimum_Raw_data             0x0a\n#define REG_Shutter_Lower                0x0b\n#define REG_Shutter_Upper                0x0c\n#define REG_Control                      0x0d\n#define REG_Config1                      0x0f\n#define REG_Config2                      0x10\n#define REG_Angle_Tune                   0x11\n#define REG_Frame_Capture                0x12\n#define REG_SROM_Enable                  0x13\n#define REG_Run_Downshift                0x14\n#define REG_Rest1_Rate_Lower             0x15\n#define REG_Rest1_Rate_Upper             0x16\n#define REG_Rest1_Downshift              0x17\n#define REG_Rest2_Rate_Lower             0x18\n#define REG_Rest2_Rate_Upper             0x19\n#define REG_Rest2_Downshift              0x1a\n#define REG_Rest3_Rate_Lower             0x1b\n#define REG_Rest3_Rate_Upper             0x1c\n#define REG_Observation                  0x24\n#define REG_Data_Out_Lower               0x25\n#define REG_Data_Out_Upper               0x26\n#define REG_Raw_Data_Dump                0x29\n#define REG_SROM_ID                      0x2a\n#define REG_Min_SQ_Run                   0x2b\n#define REG_Raw_Data_Threshold           0x2c\n#define REG_Config5                      0x2f\n#define REG_Power_Up_Reset               0x3a\n#define REG_Shutdown                     0x3b\n#define REG_Inverse_Product_ID           0x3f\n#define REG_LiftCutoff_Tune3             0x41\n#define REG_Angle_Snap                   0x42\n#define REG_LiftCutoff_Tune1             0x4a\n#define REG_Motion_Burst                 0x50\n#define REG_LiftCutoff_Tune_Timeout      0x58\n#define REG_LiftCutoff_Tune_Min_Length   0x5a\n#define REG_SROM_Load_Burst              0x62\n#define REG_Lift_Config                  0x63\n#define REG_Raw_Data_Burst               0x64\n#define REG_LiftCutoff_Tune2             0x65\n// clang-format on\n"
  },
  {
    "path": "drivers/sensors/pmw3389.c",
    "content": "// Copyright 2022 Stefan Kerkmann (KarlK90)\n// Copyright 2021 Alabastard (@Alabastard-64)\n// Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// Copyright 2019 Sunjun Kim\n// Copyright 2020 Ploopy Corporation\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"pmw33xx_common.h\"\n#include \"progmem.h\"\n\nuint16_t pmw33xx_get_cpi(uint8_t sensor) {\n    if (sensor >= pmw33xx_number_of_sensors) {\n        return 0;\n    }\n\n    uint16_t cpival = (pmw33xx_read(sensor, REG_Resolution_H) << 8) | pmw33xx_read(sensor, REG_Resolution_L);\n    return (uint16_t)((cpival + 1) & 0xFFFF) * PMW33XX_CPI_STEP;\n}\n\nvoid pmw33xx_set_cpi(uint8_t sensor, uint16_t cpi) {\n    if (sensor >= pmw33xx_number_of_sensors) {\n        return;\n    }\n\n    uint16_t cpival = CONSTRAIN((cpi / PMW33XX_CPI_STEP), (PMW33XX_CPI_MIN / PMW33XX_CPI_STEP), (PMW33XX_CPI_MAX / PMW33XX_CPI_STEP)) - 1U;\n    // Sets upper byte first for more consistent setting of cpi\n    pmw33xx_write(sensor, REG_Resolution_H, (cpival >> 8) & 0xFF);\n    pmw33xx_write(sensor, REG_Resolution_L, cpival & 0xFF);\n}\n\n// PID, Inverse PID\nconst uint8_t pmw33xx_firmware_signature[2] PROGMEM = {0x42, 0xBD};\n"
  },
  {
    "path": "drivers/sensors/pmw3389.h",
    "content": "// Copyright 2022 Stefan Kerkmann (KarlK90)\n// Copyright 2021 Alabastard (@Alabastard-64)\n// Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// Copyright 2019 Sunjun Kim\n// Copyright 2020 Ploopy Corporation\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n\n#if !defined(PMW33XX_CPI)\n#    define PMW33XX_CPI 2000\n#endif\n\n#define PMW33XX_CPI_STEP 50\n#define PMW33XX_CPI_MIN 50\n#define PMW33XX_CPI_MAX 16000\n\n// PMW3389 register addresses\n// clang-format off\n#define REG_Product_ID                 0x00\n#define REG_Revision_ID                0x01\n#define REG_Motion                     0x02\n#define REG_Delta_X_L                  0x03\n#define REG_Delta_X_H                  0x04\n#define REG_Delta_Y_L                  0x05\n#define REG_Delta_Y_H                  0x06\n#define REG_SQUAL                      0x07\n#define REG_RawData_Sum                0x08\n#define REG_Maximum_RawData            0x09\n#define REG_Minimum_RawData            0x0a\n#define REG_Shutter_Lower              0x0b\n#define REG_Shutter_Upper              0x0c\n#define REG_Ripple_Control             0x0d\n#define REG_Resolution_L               0x0e\n#define REG_Resolution_H               0x0f\n#define REG_Config2                    0x10\n#define REG_Angle_Tune                 0x11\n#define REG_Frame_Capture              0x12\n#define REG_SROM_Enable                0x13\n#define REG_Run_Downshift              0x14\n#define REG_Rest1_Rate_Lower           0x15\n#define REG_Rest1_Rate_Upper           0x16\n#define REG_Rest1_Downshift            0x17\n#define REG_Rest2_Rate_Lower           0x18\n#define REG_Rest2_Rate_Upper           0x19\n#define REG_Rest2_Downshift            0x1a\n#define REG_Rest3_Rate_Lower           0x1b\n#define REG_Rest3_Rate_Upper           0x1c\n#define REG_Observation                0x24\n#define REG_Data_Out_Lower             0x25\n#define REG_Data_Out_Upper             0x26\n#define REG_SROM_ID                    0x2a\n#define REG_Min_SQ_Run                 0x2b\n#define REG_RawData_Threshold          0x2c\n#define REG_Control2                   0x2d\n#define REG_Config5_L                  0x2e\n#define REG_Config5_H                  0x2f\n#define REG_Power_Up_Reset             0X3a\n#define REG_Shutdown                   0x3b\n#define REG_Inverse_Product_ID         0x3f\n#define REG_LiftCutoff_Cal3            0x41\n#define REG_Angle_Snap                 0x42\n#define REG_LiftCutoff_Cal1            0x4a\n#define REG_Motion_Burst               0x50\n#define REG_SROM_Load_Burst            0x62\n#define REG_Lift_Config                0x63\n#define REG_RawData_Burst              0x64\n#define REG_LiftCutoff_Cal2            0x65\n#define REG_LiftCutoff_Cal_Timeout     0x71\n#define REG_LiftCutoff_Cal_Min_Length  0x72\n#define REG_PWM_Period_Cnt             0x73\n#define REG_PWM_Width_Cnt              0x74\n// clang-format on\n"
  },
  {
    "path": "drivers/sensors/pmw33xx_common.c",
    "content": "// Copyright 2022 Pablo Martinez (@elpekenin)\n// Copyright 2022 Daniel Kao (dkao)\n// Copyright 2022 Stefan Kerkmann (KarlK90)\n// Copyright 2022 Ulrich Spörlein (@uqs)\n// Copyright 2021 Alabastard (@Alabastard-64)\n// Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// Copyright 2019 Sunjun Kim\n// Copyright 2020 Ploopy Corporation\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"pointing_device_internal.h\"\n#include \"pmw33xx_common.h\"\n#include \"string.h\"\n#include \"wait.h\"\n#include \"spi_master.h\"\n#include \"progmem.h\"\n\nextern const uint8_t pmw33xx_firmware_signature[2] PROGMEM;\n\nstatic const pin_t cs_pins_left[]  = PMW33XX_CS_PINS;\nstatic const pin_t cs_pins_right[] = PMW33XX_CS_PINS_RIGHT;\n\nstatic bool in_burst_left[ARRAY_SIZE(cs_pins_left)]   = {0};\nstatic bool in_burst_right[ARRAY_SIZE(cs_pins_right)] = {0};\n\nbool __attribute__((cold)) pmw33xx_upload_firmware(uint8_t sensor);\nbool __attribute__((cold)) pmw33xx_check_signature(uint8_t sensor);\n\nconst pointing_device_driver_t pmw33xx_pointing_device_driver = {\n    .init       = pmw33xx_init_wrapper,\n    .get_report = pmw33xx_get_report,\n    .set_cpi    = pmw33xx_set_cpi_wrapper,\n    .get_cpi    = pmw33xx_get_cpi_wrapper,\n};\n\nuint16_t __attribute__((weak)) pmw33xx_srom_get_length(void) {\n    return 0;\n}\n\nuint8_t __attribute__((weak)) pmw33xx_srom_get_byte(uint16_t position) {\n    return 0;\n}\n\nvoid pmw33xx_set_cpi_all_sensors(uint16_t cpi) {\n    for (uint8_t sensor = 0; sensor < pmw33xx_number_of_sensors; sensor++) {\n        pmw33xx_set_cpi(sensor, cpi);\n    }\n}\n\nbool pmw33xx_spi_start(uint8_t sensor) {\n    if (!spi_start(cs_pins[sensor], false, 3, PMW33XX_SPI_DIVISOR)) {\n        spi_stop();\n        return false;\n    }\n    // tNCS-SCLK, 10ns\n    wait_us(1);\n    return true;\n}\n\nbool pmw33xx_write(uint8_t sensor, uint8_t reg_addr, uint8_t data) {\n    if (!pmw33xx_spi_start(sensor)) {\n        return false;\n    }\n\n    if (reg_addr != REG_Motion_Burst) {\n        in_burst[sensor] = false;\n    }\n\n    // send address of the register, with MSBit = 1 to indicate it's a write\n    uint8_t command[2] = {reg_addr | 0x80, data};\n    if (spi_transmit(command, sizeof(command)) != SPI_STATUS_SUCCESS) {\n        return false;\n    }\n\n    // tSCLK-NCS for write operation is 35us\n    wait_us(35);\n    spi_stop();\n\n    // tSWW/tSWR (=18us) minus tSCLK-NCS. Could be shortened, but it looks like\n    // a safe lower bound\n    wait_us(145);\n    return true;\n}\n\nuint8_t pmw33xx_read(uint8_t sensor, uint8_t reg_addr) {\n    if (!pmw33xx_spi_start(sensor)) {\n        return 0;\n    }\n\n    // send adress of the register, with MSBit = 0 to indicate it's a read\n    spi_write(reg_addr & 0x7f);\n    // tSRAD (=160us)\n    wait_us(160);\n    uint8_t data = spi_read();\n\n    // tSCLK-NCS, 120ns\n    wait_us(1);\n    spi_stop();\n\n    //  tSRW/tSRR (=20us) mins tSCLK-NCS\n    wait_us(19);\n    return data;\n}\n\nbool pmw33xx_check_signature(uint8_t sensor) {\n    uint8_t signature_dump[2] = {\n        pmw33xx_read(sensor, REG_Product_ID),\n        pmw33xx_read(sensor, REG_Inverse_Product_ID),\n    };\n\n    return memcmp(pmw33xx_firmware_signature, signature_dump, sizeof(signature_dump)) == 0;\n}\n\nbool pmw33xx_upload_firmware(uint8_t sensor) {\n    // Datasheet claims we need to disable REST mode first, but during startup\n    // it's already disabled and we're not turning it on ...\n    // pmw33xx_write(REG_Config2, 0x00);  // disable REST mode\n    if (!pmw33xx_write(sensor, REG_SROM_Enable, 0x1d)) {\n        return false;\n    }\n    wait_ms(10);\n    pmw33xx_write(sensor, REG_SROM_Enable, 0x18);\n\n    if (!pmw33xx_spi_start(sensor)) {\n        return false;\n    }\n\n    spi_write(REG_SROM_Load_Burst | 0x80);\n    wait_us(15);\n\n    for (size_t i = 0; i < pmw33xx_srom_get_length(); i++) {\n        spi_write(pmw33xx_srom_get_byte(i));\n        wait_us(15);\n    }\n\n    spi_stop();\n    wait_us(200);\n\n    pmw33xx_read(sensor, REG_SROM_ID);\n    pmw33xx_write(sensor, REG_Config2, 0x00);\n\n    return true;\n}\n\nbool pmw33xx_init(uint8_t sensor) {\n    if (sensor >= pmw33xx_number_of_sensors) {\n        return false;\n    }\n    spi_init();\n\n    // power up, need to first drive NCS high then low. the datasheet does not\n    // say for how long, 40us works well in practice.\n    if (!pmw33xx_spi_start(sensor)) {\n        return false;\n    }\n    wait_us(40);\n    spi_stop();\n    wait_us(40);\n\n    if (!pmw33xx_write(sensor, REG_Power_Up_Reset, 0x5a)) {\n        return false;\n    }\n    wait_ms(50);\n\n    // read registers and discard\n    pmw33xx_read(sensor, REG_Motion);\n    pmw33xx_read(sensor, REG_Delta_X_L);\n    pmw33xx_read(sensor, REG_Delta_X_H);\n    pmw33xx_read(sensor, REG_Delta_Y_L);\n    pmw33xx_read(sensor, REG_Delta_Y_H);\n\n    if (pmw33xx_srom_get_length() != 0) {\n        if (!pmw33xx_upload_firmware(sensor)) {\n            pd_dprintf(\"PMW33XX (%d): firmware upload failed!\\n\", sensor);\n            return false;\n        }\n    } else {\n        pd_dprintf(\"PMW33XX (%d): firmware upload skipped.\\n\", sensor);\n    }\n\n    spi_stop();\n\n    wait_ms(10);\n    pmw33xx_set_cpi(sensor, PMW33XX_CPI);\n\n    wait_ms(1);\n\n    pmw33xx_write(sensor, REG_Config2, 0x00);\n    pmw33xx_write(sensor, REG_Angle_Tune, CONSTRAIN(ROTATIONAL_TRANSFORM_ANGLE, -127, 127));\n    pmw33xx_write(sensor, REG_Lift_Config, PMW33XX_LIFTOFF_DISTANCE);\n\n    if (!pmw33xx_check_signature(sensor)) {\n        pd_dprintf(\"PMW33XX (%d): firmware signature verification failed!\\n\", sensor);\n        return false;\n    }\n\n    return true;\n}\n\npmw33xx_report_t pmw33xx_read_burst(uint8_t sensor) {\n    pmw33xx_report_t report = {0};\n\n    if (sensor >= pmw33xx_number_of_sensors) {\n        return report;\n    }\n\n    if (!in_burst[sensor]) {\n        pd_dprintf(\"PMW33XX (%d): burst\\n\", sensor);\n        if (!pmw33xx_write(sensor, REG_Motion_Burst, 0x00)) {\n            return report;\n        }\n        in_burst[sensor] = true;\n    }\n\n    if (!pmw33xx_spi_start(sensor)) {\n        return report;\n    }\n\n    spi_write(REG_Motion_Burst);\n    wait_us(35); // waits for tSRAD_MOTBR\n\n    spi_receive((uint8_t *)&report, sizeof(report));\n\n    // panic recovery, sometimes burst mode works weird.\n    if (report.motion.w & 0b111) {\n        in_burst[sensor] = false;\n    }\n\n    spi_stop();\n\n    pd_dprintf(\"PMW33XX (%d): motion: 0x%x dx: %i dy: %i\\n\", sensor, report.motion.w, report.delta_x, report.delta_y);\n\n    report.delta_x *= -1;\n    report.delta_y *= -1;\n\n    return report;\n}\n\nvoid pmw33xx_init_wrapper(void) {\n    pmw33xx_init(0);\n}\n\nvoid pmw33xx_set_cpi_wrapper(uint16_t cpi) {\n    pmw33xx_set_cpi(0, cpi);\n}\n\nuint16_t pmw33xx_get_cpi_wrapper(void) {\n    return pmw33xx_get_cpi(0);\n}\n\nreport_mouse_t pmw33xx_get_report(report_mouse_t mouse_report) {\n    pmw33xx_report_t report    = pmw33xx_read_burst(0);\n    static bool      in_motion = false;\n\n    if (report.motion.b.is_lifted) {\n        return mouse_report;\n    }\n\n    if (!report.motion.b.is_motion) {\n        in_motion = false;\n        return mouse_report;\n    }\n\n    if (!in_motion) {\n        in_motion = true;\n        pd_dprintf(\"PWM3360 (0): starting motion\\n\");\n    }\n\n    mouse_report.x = CONSTRAIN_HID_XY(report.delta_x);\n    mouse_report.y = CONSTRAIN_HID_XY(report.delta_y);\n    return mouse_report;\n}\n"
  },
  {
    "path": "drivers/sensors/pmw33xx_common.h",
    "content": "// Copyright 2022 Pablo Martinez (@elpekenin)\n// Copyright 2022 Daniel Kao (dkao)\n// Copyright 2022 Stefan Kerkmann (KarlK90)\n// Copyright 2022 Ulrich Spörlein (@uqs)\n// Copyright 2021 Alabastard (@Alabastard-64)\n// Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// Copyright 2019 Sunjun Kim\n// Copyright 2020 Ploopy Corporation\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"compiler_support.h\"\n#include \"keyboard.h\"\n#include <stdint.h>\n#include \"spi_master.h\"\n#include \"util.h\"\n#include \"pointing_device.h\"\n\n#if defined(POINTING_DEVICE_DRIVER_pmw3360)\n#    include \"pmw3360.h\"\n#elif defined(POINTING_DEVICE_DRIVER_pmw3389)\n#    include \"pmw3389.h\"\n#endif\n\ntypedef struct __attribute__((packed)) {\n    union {\n        struct {\n            bool    capture_from_raw_data : 1;     // FRAME_RData_1st\n            uint8_t operation_mode : 2;            // OP_MODE\n            bool    is_lifted : 1;                 // Lift_stat\n            bool    raw_data_grab_is_raw_data : 1; // RData_1st\n            uint8_t _reserved : 2;                 // 1 + Reserved\n            bool    is_motion : 1;                 // MOT\n        } b;\n        uint8_t w;\n    } motion;\n    uint8_t observation;\n    int16_t delta_x; // displacement on x directions. Unit: Count. (CPI * Count = Inch value)\n    int16_t delta_y; // displacement on y directions.\n} pmw33xx_report_t;\n\nSTATIC_ASSERT(sizeof(pmw33xx_report_t) == 6, \"pmw33xx_report_t must be 6 bytes in size\");\nSTATIC_ASSERT(sizeof((pmw33xx_report_t){0}.motion) == 1, \"pmw33xx_report_t.motion must be 1 byte in size\");\n\n#if !defined(PMW33XX_CLOCK_SPEED)\n#    define PMW33XX_CLOCK_SPEED 2000000\n#endif\n\n#if !defined(PMW33XX_SPI_DIVISOR)\n#    ifdef __AVR__\n#        define PMW33XX_SPI_DIVISOR (F_CPU / PMW33XX_CLOCK_SPEED)\n#    else\n#        define PMW33XX_SPI_DIVISOR 64\n#    endif\n#endif\n\n#if !defined(PMW33XX_LIFTOFF_DISTANCE)\n#    define PMW33XX_LIFTOFF_DISTANCE 0x02\n#endif\n\n#if !defined(ROTATIONAL_TRANSFORM_ANGLE)\n#    define ROTATIONAL_TRANSFORM_ANGLE 0x00\n#endif\n\n#if ROTATIONAL_TRANSFORM_ANGLE > 127 || ROTATIONAL_TRANSFORM_ANGLE < (-127)\n#    error ROTATIONAL_TRANSFORM_ANGLE has to be in the range of +/- 127 for all PMW33XX sensors.\n#endif\n\n// Support single and plural spellings\n#ifndef PMW33XX_CS_PINS\n#    ifndef PMW33XX_CS_PIN\n#        ifdef POINTING_DEVICE_CS_PIN\n#            define PMW33XX_CS_PIN POINTING_DEVICE_CS_PIN\n#            define PMW33XX_CS_PINS \\\n                { PMW33XX_CS_PIN }\n#        else\n#            error \"No chip select pin defined -- missing PMW33XX_CS_PIN or PMW33XX_CS_PINS\"\n#        endif\n#    else\n#        define PMW33XX_CS_PINS \\\n            { PMW33XX_CS_PIN }\n#    endif\n#endif\n\n// Support single spelling and default to be the same as left side\n#if !defined(PMW33XX_CS_PINS_RIGHT)\n#    if !defined(PMW33XX_CS_PIN_RIGHT)\n#        define PMW33XX_CS_PIN_RIGHT PMW33XX_CS_PIN\n#    endif\n#    define PMW33XX_CS_PINS_RIGHT \\\n        { PMW33XX_CS_PIN_RIGHT }\n#endif\n\n// Defines so the old variable names are swapped by the appropiate value on each half\n#define cs_pins (is_keyboard_left() ? cs_pins_left : cs_pins_right)\n#define in_burst (is_keyboard_left() ? in_burst_left : in_burst_right)\n#define pmw33xx_number_of_sensors (is_keyboard_left() ? ARRAY_SIZE((pin_t[])PMW33XX_CS_PINS) : ARRAY_SIZE((pin_t[])PMW33XX_CS_PINS_RIGHT))\n\n#if PMW33XX_CPI > PMW33XX_CPI_MAX || PMW33XX_CPI < PMW33XX_CPI_MIN || (PMW33XX_CPI % PMW33XX_CPI_STEP) != 0U\n#    pragma message \"PMW33XX_CPI has to be in the range of \" STR(PMW33XX_CPI_MAX) \"-\" STR(PMW33XX_CPI_MIN) \" in increments of \" STR(PMW33XX_CPI_STEP) \". But it is \" STR(PMW33XX_CPI) \".\"\n#    error Use correct PMW33XX_CPI value.\n#endif\n\n#define CONSTRAIN(amt, low, high) ((amt) < (low) ? (low) : ((amt) > (high) ? (high) : (amt)))\n\n#define pmw3360_pointing_device_driver pmw33xx_pointing_device_driver;\n#define pmw3389_pointing_device_driver pmw33xx_pointing_device_driver;\nconst pointing_device_driver_t pmw33xx_pointing_device_driver;\n\n/**\n * @brief Initializes the given sensor so it is in a working state and ready to\n * be polled for data.\n *\n * @param sensor Index of the sensors chip select pin\n * @return true Initialization was a success\n * @return false Initialization failed, do not proceed operation\n */\nbool __attribute__((cold)) pmw33xx_init(uint8_t sensor);\n\n/**\n * @brief Gets the currently set CPI value from the sensor. CPI is often\n * refereed to as the sensors sensitivity.\n *\n * @param sensor Index of the sensors chip select pin\n * @return uint16_t Current CPI value of the sensor\n */\nuint16_t pmw33xx_get_cpi(uint8_t sensor);\n\n/**\n * @brief Sets the given CPI value for the given PMW33XX sensor. CIP is often\n * refereed to as the sensors sensitivity. Values outside of the allow range are\n * constrained into legal values.\n *\n * @param sensor Index of the sensors chip select pin\n * @param cpi CPI value to set, legal range depends on the PMW sensor type\n */\nvoid pmw33xx_set_cpi(uint8_t sensor, uint16_t cpi);\n\n/**\n * @brief Sets the given CPI value to all registered PMW33XX sensors. CPI is\n * often refereed to as the sensors sensitivity. Values outside of the allow\n * range are constrained into legal values.\n *\n * @param sensor Index of the sensors chip select pin\n * @param cpi CPI value to set, legal range depends on the PMW sensor type\n */\nvoid pmw33xx_set_cpi_all_sensors(uint16_t cpi);\n\n/**\n * @brief Reads and clears the current delta, and motion register values on the\n * given sensor.\n *\n * @param sensor Index of the sensors chip select pin\n * @return pmw33xx_report_t Current values of the sensor, if errors occurred all\n * fields are set to zero\n */\npmw33xx_report_t pmw33xx_read_burst(uint8_t sensor);\n\n/**\n * @brief Read one byte of data from the given register on the sensor\n *\n * @param sensor Index of the sensors chip select pin\n * @param reg_addr Register address to read from\n * @return uint8_t\n */\nuint8_t pmw33xx_read(uint8_t sensor, uint8_t reg_addr);\n\n/**\n * @brief Writes one byte of data to the given register on the sensor\n *\n * @param sensor Index of the sensors chip select pin\n * @param reg_addr Registers address to write to\n * @param data Data to write to the register\n * @return true Write was a success\n * @return false Write failed, do not proceed operation\n */\nbool pmw33xx_write(uint8_t sensor, uint8_t reg_addr, uint8_t data);\n\nvoid           pmw33xx_init_wrapper(void);\nvoid           pmw33xx_set_cpi_wrapper(uint16_t cpi);\nuint16_t       pmw33xx_get_cpi_wrapper(void);\nreport_mouse_t pmw33xx_get_report(report_mouse_t mouse_report);\n"
  },
  {
    "path": "drivers/serial.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include <transactions.h>\n\n// initiator is transaction start side\nvoid soft_serial_initiator_init(void);\n// target is interrupt accept side\nvoid soft_serial_target_init(void);\n\nbool soft_serial_transaction(int sstd_index);\n\n#ifdef SERIAL_DEBUG\n#    include <debug.h>\n#    include <print.h>\n#    define serial_dprintf(...) dprintf(__VA_ARGS__)\n#else\n#    define serial_dprintf(...) \\\n        do {                    \\\n        } while (0)\n#endif\n"
  },
  {
    "path": "drivers/spi_master.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"gpio.h\"\n\n/**\n * \\file\n *\n * \\defgroup spi_master SPI Master API\n *\n * \\brief API to communicate with SPI devices.\n * \\{\n */\n\n// Hardware SS pin is defined in the header so that user code can refer to it\n#ifdef __AVR__\n#    if defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)\n#        define SPI_SS_PIN B0\n#    elif defined(__AVR_ATmega32A__)\n#        define SPI_SS_PIN B4\n#    elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)\n#        define SPI_SS_PIN B2\n#    endif\n#endif\n\ntypedef int16_t spi_status_t;\n\n#define SPI_STATUS_SUCCESS (0)\n#define SPI_STATUS_ERROR (-1)\n#define SPI_STATUS_TIMEOUT (-2)\n\n#define SPI_TIMEOUT_IMMEDIATE (0)\n#define SPI_TIMEOUT_INFINITE (0xFFFF)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct spi_start_config_t {\n    pin_t    slave_pin;\n    bool     lsb_first;\n    uint8_t  mode;\n    uint16_t divisor;\n    bool     cs_active_low;\n} spi_start_config_t;\n\n/**\n * \\brief Initialize the SPI driver. This function must be called only once, before any of the below functions can be called.\n */\nvoid spi_init(void);\n\n/**\n * \\brief Start an SPI transaction.\n *\n * \\param slavePin The GPIO pin connected to the desired device's `SS` line.\n * \\param lsbFirst Determines the endianness of the transmission. If `true`, the least significant bit of each byte is sent first.\n * \\param mode The SPI mode to use.\n * \\param divisor The SPI clock divisor.\n *\n * \\return `true` if the operation was successful, otherwise `false` if the supplied parameters are invalid or the SPI peripheral is already in use.\n */\nbool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor);\n\nbool spi_start_extended(spi_start_config_t *start_config);\n\n/**\n * \\brief Write a byte to the selected SPI device.\n *\n * \\param data The byte to write.\n *\n * \\return `SPI_STATUS_TIMEOUT` if the timeout period elapses, or `SPI_STATUS_SUCCESS`.\n */\nspi_status_t spi_write(uint8_t data);\n\n/**\n * \\brief Read a byte from the selected SPI device.\n *\n * \\return `SPI_STATUS_TIMEOUT` if the timeout period elapses, otherwise the byte read from the device.\n */\nspi_status_t spi_read(void);\n\n/**\n * \\brief Send multiple bytes to the selected SPI device.\n *\n * \\param data A pointer to the data to write from.\n * \\param length The number of bytes to write. Take care not to overrun the length of `data`.\n *\n * \\return `SPI_STATUS_TIMEOUT` if the timeout period elapses, `SPI_STATUS_ERROR` if some other error occurs, otherwise `SPI_STATUS_SUCCESS`.\n */\nspi_status_t spi_transmit(const uint8_t *data, uint16_t length);\n\n/**\n * \\brief Receive multiple bytes from the selected SPI device.\n *\n * \\param data A pointer to a buffer to read into.\n * \\param length The number of bytes to read. Take care not to overrun the length of `data`.\n *\n * \\return `SPI_STATUS_TIMEOUT` if the timeout period elapses, `SPI_STATUS_ERROR` if some other error occurs, otherwise `SPI_STATUS_SUCCESS`.\n */\nspi_status_t spi_receive(uint8_t *data, uint16_t length);\n\n/**\n * \\brief End the current SPI transaction. This will deassert the slave select pin and reset the endianness, mode and divisor configured by `spi_start()`.\n *\n */\nvoid spi_stop(void);\n\n#ifdef __cplusplus\n}\n#endif\n\n/** \\} */\n"
  },
  {
    "path": "drivers/uart.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n/**\n * \\file\n *\n * \\defgroup uart UART API\n *\n * \\brief API to communicate with UART devices.\n * \\{\n */\n\n/**\n * \\brief Initialize the UART driver. This function must be called only once, before any of the below functions can be called.\n *\n * \\param baud The baud rate to transmit and receive at. This may depend on the device you are communicating with. Common values are 1200, 2400, 4800, 9600, 19200, 38400, 57600, and 115200.\n */\nvoid uart_init(uint32_t baud);\n\n/**\n * \\brief Transmit a single byte.\n *\n * \\param data The byte to write.\n */\nvoid uart_write(uint8_t data);\n\n/**\n * \\brief Receive a single byte.\n *\n * \\return The byte read from the receive buffer. This function will block if the buffer is empty (ie. no data to read).\n */\nuint8_t uart_read(void);\n\n/**\n * \\brief Transmit multiple bytes.\n *\n * \\param data A pointer to the data to write from.\n * \\param length The number of bytes to write. Take care not to overrun the length of `data`.\n */\nvoid uart_transmit(const uint8_t *data, uint16_t length);\n\n/**\n * \\brief Receive multiple bytes.\n *\n * \\param data A pointer to a buffer to read into.\n * \\param length The number of bytes to read. Take care not to overrun the length of `data`.\n */\nvoid uart_receive(uint8_t *data, uint16_t length);\n\n/**\n * \\brief Return whether the receive buffer contains data. Call this function to determine if `uart_read()` will return data immediately.\n *\n * \\return true if there is data available to read.\n */\nbool uart_available(void);\n\n/** \\} */\n"
  },
  {
    "path": "drivers/usb2422.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include <string.h>\n#include \"usb2422.h\"\n#include \"i2c_master.h\"\n#include \"wait.h\"\n#include \"gpio.h\"\n\n/* -------- USB2422_VID : (USB2422L Offset: 0x00) (R/W 16) Vendor ID -------- */\ntypedef union {\n    struct {\n        uint16_t VID_LSB : 8;\n        uint16_t VID_MSB : 8;\n    } bit;        /*!< Structure used for bit  access                  */\n    uint16_t reg; /*!< Type      used for register access              */\n} USB2422_VID_Type;\n\n/* -------- USB2422_PID : (USB2422L Offset: 0x02) (R/W 16) Product ID -------- */\ntypedef union {\n    struct {\n        uint16_t PID_LSB : 8;\n        uint16_t PID_MSB : 8;\n    } bit;        /*!< Structure used for bit  access                  */\n    uint16_t reg; /*!< Type      used for register access              */\n} USB2422_PID_Type;\n\n/* -------- USB2422_DID : (USB2422L Offset: 0x04) (R/W 16) Device ID -------- */\ntypedef union {\n    struct {\n        uint16_t DID_LSB : 8;\n        uint16_t DID_MSB : 8;\n    } bit;        /*!< Structure used for bit  access                  */\n    uint16_t reg; /*!< Type      used for register access              */\n} USB2422_DID_Type;\n\n/* -------- USB2422_CFG1 : (USB2422L Offset: 0x06) (R/W 8) Configuration Data Byte 1-------- */\ntypedef union {\n    struct {\n        uint8_t PORT_PWR : 1;\n        uint8_t CURRENT_SNS : 2;\n        uint8_t EOP_DISABLE : 1;\n        uint8_t MTT_ENABLE : 1;\n        uint8_t HS_DISABLE : 1;\n        uint8_t : 1;\n        uint8_t SELF_BUS_PWR : 1;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_CFG1_Type;\n\n/* -------- USB2422_CFG2 : (USB2422L Offset: 0x07) (R/W 8) Configuration Data Byte 2-------- */\ntypedef union {\n    struct {\n        uint8_t : 3;\n        uint8_t COMPOUND : 1;\n        uint8_t OC_TIMER : 2;\n        uint8_t : 1;\n        uint8_t DYNAMIC : 1;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_CFG2_Type;\n\n/* -------- USB2422_CFG3 : (USB2422L Offset: 0x08) (R/W 16) Configuration Data Byte 3-------- */\ntypedef union {\n    struct {\n        uint8_t STRING_EN : 1;\n        uint8_t : 2;\n        uint8_t PRTMAP_EN : 1;\n        uint8_t : 4;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_CFG3_Type;\n\n/* -------- USB2422_NRD : (USB2422L Offset: 0x09) (R/W 8) Non Removable Device -------- */\ntypedef union {\n    struct {\n        uint8_t : 5;\n        uint8_t PORT2_NR : 1;\n        uint8_t PORT1_NR : 1;\n        uint8_t : 1;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_NRD_Type;\n\n/* -------- USB2422_PDS : (USB2422L Offset: 0x0A) (R/W 8) Port Diable for Self-Powered Operation -------- */\ntypedef union {\n    struct {\n        uint8_t : 1;\n        uint8_t PORT1_DIS : 1;\n        uint8_t PORT2_DIS : 1;\n        uint8_t : 5;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_PDS_Type;\n\n/* -------- USB2422_PDB : (USB2422L Offset: 0x0B) (R/W 8) Port Diable for Bus-Powered Operation -------- */\n\ntypedef union {\n    struct {\n        uint8_t : 1;\n        uint8_t PORT1_DIS : 1;\n        uint8_t PORT2_DIS : 1;\n        uint8_t : 5;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_PDB_Type;\n\n/* -------- USB2422_MAXPS : (USB2422L Offset: 0x0C) (R/W 8) Max Power for Self-Powered Operation -------- */\ntypedef union {\n    struct {\n        uint8_t MAX_PWR_SP : 8;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_MAXPS_Type;\n\n/* -------- USB2422_MAXPB : (USB2422L Offset: 0x0D) (R/W 8) Max Power for Bus-Powered Operation -------- */\ntypedef union {\n    struct {\n        uint8_t MAX_PWR_BP : 8;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_MAXPB_Type;\n\n/* -------- USB2422_HCMCS : (USB2422L Offset: 0x0E) (R/W 8) Hub Controller Max Current for Self-Powered Operation -------- */\ntypedef union {\n    struct {\n        uint8_t HC_MAX_C_SP : 8;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_HCMCS_Type;\n\n/* -------- USB2422_HCMCB : (USB2422L Offset: 0x0F) (R/W 8) Hub Controller Max Current for Bus-Powered Operation -------- */\ntypedef union {\n    struct {\n        uint8_t HC_MAX_C_BP : 8;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_HCMCB_Type;\n\n/* -------- USB2422_PWRT : (USB2422L Offset: 0x10) (R/W 8) Power On Time -------- */\ntypedef union {\n    struct {\n        uint8_t POWER_ON_TIME : 8;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_PWRT_Type;\n\n/* -------- USB2422_LANGID LSB : (USB2422L Offset: 0x11) (R/W 16) Language ID -------- */\ntypedef union {\n    struct {\n        uint8_t LANGID_LSB : 8;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_LANGID_LSB_Type;\n\n/* -------- USB2422_LANGID MSB : (USB2422L Offset: 0x12) (R/W 16) Language ID -------- */\ntypedef union {\n    struct {\n        uint8_t LANGID_MSB : 8;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_LANGID_MSB_Type;\n\n/* -------- USB2422_MFRSL : (USB2422L Offset: 0x13) (R/W 8) Manufacturer String Length -------- */\ntypedef union {\n    struct {\n        uint8_t MFR_STR_LEN : 8;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_MFRSL_Type;\n\n/* -------- USB2422_PRDSL : (USB2422L Offset: 0x14) (R/W 8) Product String Length -------- */\ntypedef union {\n    struct {\n        uint8_t PRD_STR_LEN : 8;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_PRDSL_Type;\n\n/* -------- USB2422_SERSL : (USB2422L Offset: 0x15) (R/W 8) Serial String Length -------- */\ntypedef union {\n    struct {\n        uint8_t SER_STR_LEN : 8;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_SERSL_Type;\n\n/* -------- USB2422_MFRSTR : (USB2422L Offset: 0x16-53) (R/W 8) Maufacturer String -------- */\ntypedef uint16_t USB2422_MFRSTR_Type;\n\n/* -------- USB2422_PRDSTR : (USB2422L Offset: 0x54-91) (R/W 8) Product String -------- */\ntypedef uint16_t USB2422_PRDSTR_Type;\n\n/* -------- USB2422_SERSTR : (USB2422L Offset: 0x92-CF) (R/W 8) Serial String -------- */\ntypedef uint16_t USB2422_SERSTR_Type;\n\n/* -------- USB2422_BCEN : (USB2422L Offset: 0xD0) (R/W 8) Battery Charging Enable -------- */\n\ntypedef union {\n    struct {\n        uint8_t : 1;\n        uint8_t PORT1_BCE : 1;\n        uint8_t PORT2_BCE : 1;\n        uint8_t : 5;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_BCEN_Type;\n\n/* -------- USB2422_BOOSTUP : (USB2422L Offset: 0xF6) (R/W 8) Boost Upstream -------- */\ntypedef union {\n    struct {\n        uint8_t BOOST : 2;\n        uint8_t : 6;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_BOOSTUP_Type;\n\n/* -------- USB2422_BOOSTDOWN : (USB2422L Offset: 0xF8) (R/W 8) Boost Downstream -------- */\ntypedef union {\n    struct {\n        uint8_t BOOST1 : 2;\n        uint8_t BOOST2 : 2;\n        uint8_t : 4;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_BOOSTDOWN_Type;\n\n/* -------- USB2422_PRTSP : (USB2422L Offset: 0xFA) (R/W 8) Port Swap -------- */\ntypedef union {\n    struct {\n        uint8_t : 1;\n        uint8_t PORT1_SP : 1;\n        uint8_t PORT2_SP : 1;\n        uint8_t : 5;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_PRTSP_Type;\n\n/* -------- USB2422_PRTR12 : (USB2422L Offset: 0xFB) (R/W 8) Port 1/2 Remap -------- */\ntypedef union {\n    struct {\n        uint8_t PORT1_REMAP : 4;\n        uint8_t PORT2_REMAP : 4;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_PRTR12_Type;\n\n#define USB2422_PRTR12_DISABLE 0\n#define USB2422_PRT12_P2TOL1 1\n#define USB2422_PRT12_P2XTOL2 2\n#define USB2422_PRT12_P1TOL1 1\n#define USB2422_PRT12_P1XTOL2 2\n\n/* -------- USB2422_STCD : (USB2422L Offset: 0xFF) (R/W 8) Status Command -------- */\ntypedef union {\n    struct {\n        uint8_t USB_ATTACH : 1;\n        uint8_t RESET : 1;\n        uint8_t INTF_PWRDN : 1;\n        uint8_t : 5;\n    } bit;       /*!< Structure used for bit  access                  */\n    uint8_t reg; /*!< Type      used for register access              */\n} USB2422_STCD_Type;\n\n/** \\brief USB2422 device hardware registers */\ntypedef struct {\n    USB2422_VID_Type        VID;        /**< \\brief Offset: 0x00*/\n    USB2422_PID_Type        PID;        /**< \\brief Offset: 0x02*/\n    USB2422_DID_Type        DID;        /**< \\brief Offset: 0x04*/\n    USB2422_CFG1_Type       CFG1;       /**< \\brief Offset: 0x06*/\n    USB2422_CFG2_Type       CFG2;       /**< \\brief Offset: 0x07*/\n    USB2422_CFG3_Type       CFG3;       /**< \\brief Offset: 0x08*/\n    USB2422_NRD_Type        NRD;        /**< \\brief Offset: 0x09*/\n    USB2422_PDS_Type        PDS;        /**< \\brief Offset: 0x0A*/\n    USB2422_PDB_Type        PDB;        /**< \\brief Offset: 0x0B*/\n    USB2422_MAXPS_Type      MAXPS;      /**< \\brief Offset: 0x0C*/\n    USB2422_MAXPB_Type      MAXPB;      /**< \\brief Offset: 0x0D*/\n    USB2422_HCMCS_Type      HCMCS;      /**< \\brief Offset: 0x0E*/\n    USB2422_HCMCB_Type      HCMCB;      /**< \\brief Offset: 0x0F*/\n    USB2422_PWRT_Type       PWRT;       /**< \\brief Offset: 0x10*/\n    USB2422_LANGID_LSB_Type LANGID_LSB; /**< \\brief Offset: 0x11*/\n    USB2422_LANGID_MSB_Type LANGID_MSB; /**< \\brief Offset: 0x12*/\n    USB2422_MFRSL_Type      MFRSL;      /**< \\brief Offset: 0x13*/\n    USB2422_PRDSL_Type      PRDSL;      /**< \\brief Offset: 0x14*/\n    USB2422_SERSL_Type      SERSL;      /**< \\brief Offset: 0x15*/\n    USB2422_MFRSTR_Type     MFRSTR[31]; /**< \\brief Offset: 0x16*/\n    USB2422_PRDSTR_Type     PRDSTR[31]; /**< \\brief Offset: 0x54*/\n    USB2422_SERSTR_Type     SERSTR[31]; /**< \\brief Offset: 0x92*/\n    USB2422_BCEN_Type       BCEN;       /**< \\brief Offset: 0xD0*/\n    uint8_t                 Reserved1[0x25];\n    USB2422_BOOSTUP_Type    BOOSTUP; /**< \\brief Offset: 0xF6*/\n    uint8_t                 Reserved2[0x1];\n    USB2422_BOOSTDOWN_Type  BOOSTDOWN; /**< \\brief Offset: 0xF8*/\n    uint8_t                 Reserved3[0x1];\n    USB2422_PRTSP_Type      PRTSP;  /**< \\brief Offset: 0xFA*/\n    USB2422_PRTR12_Type     PRTR12; /**< \\brief Offset: 0xFB*/\n    uint8_t                 Reserved4[0x3];\n    USB2422_STCD_Type       STCD; /**< \\brief Offset: 0xFF*/\n} Usb2422_t;\n\n// ***************************************************************\n\nstatic Usb2422_t config;\n\n// ***************************************************************\n\n/** \\brief Handle the conversion to allow simple strings\n */\nstatic void USB2422_strcpy(const char* str, USB2422_MFRSTR_Type* dest, uint8_t len) {\n    for (uint8_t i = 0; i < len; i++) {\n        dest[i] = str[i];\n    }\n}\n\n/** \\brief Handle the conversion to allow simple strings\n */\nstatic void USB2422_write_block(void) {\n    static unsigned char i2c0_buf[34];\n\n    unsigned char* dest = i2c0_buf;\n    unsigned char* src;\n    unsigned char* base = (unsigned char*)&config;\n\n    for (src = base; src < base + 256; src += 32) {\n        dest[0] = src - base;\n        dest[1] = 32;\n        memcpy(&dest[2], src, 32);\n        i2c_transmit(USB2422_ADDRESS, dest, 34, 50000);\n        wait_us(100);\n    }\n}\n\n// ***************************************************************\n\nvoid USB2422_init(void) {\n#ifdef USB2422_RESET_PIN\n    gpio_set_pin_output(USB2422_RESET_PIN);\n#endif\n#ifdef USB2422_ACTIVE_PIN\n    gpio_set_pin_input(USB2422_ACTIVE_PIN);\n#endif\n\n    i2c_init(); // IC2 clk must be high at USB2422 reset release time to signal SMB configuration\n}\n\nvoid USB2422_configure(void) {\n    static const char SERNAME[] = \"Unavailable\";\n\n    memset(&config, 0, sizeof(Usb2422_t));\n\n    // configure Usb2422 registers\n    config.VID.reg               = USB2422_VENDOR_ID;\n    config.PID.reg               = USB2422_PRODUCT_ID;\n    config.DID.reg               = USB2422_DEVICE_VER; // BCD format, eg 01.01\n    config.CFG1.bit.SELF_BUS_PWR = 1;                  // self powered for now\n    config.CFG1.bit.HS_DISABLE   = 1;                  // full or high speed\n    // config.CFG2.bit.COMPOUND = 0; // compound device\n    config.CFG3.bit.STRING_EN = 1; // strings enabled\n    // config.NRD.bit.PORT2_NR = 0; // MCU is non-removable\n    config.MAXPB.reg = 20; // 0mA\n    config.HCMCB.reg = 20; // 0mA\n    config.MFRSL.reg = sizeof(USB2422_MANUFACTURER);\n    config.PRDSL.reg = sizeof(USB2422_PRODUCT);\n    config.SERSL.reg = sizeof(SERNAME);\n    USB2422_strcpy(USB2422_MANUFACTURER, config.MFRSTR, sizeof(USB2422_MANUFACTURER));\n    USB2422_strcpy(USB2422_PRODUCT, config.PRDSTR, sizeof(USB2422_PRODUCT));\n    USB2422_strcpy(SERNAME, config.SERSTR, sizeof(SERNAME));\n    // config.BOOSTUP.bit.BOOST=3;    //upstream port\n    // config.BOOSTDOWN.bit.BOOST1=0; // extra port\n    // config.BOOSTDOWN.bit.BOOST2=2; //MCU is close\n    config.STCD.bit.USB_ATTACH = 1;\n\n    USB2422_write_block();\n}\n\nvoid USB2422_reset(void) {\n#ifdef USB2422_RESET_PIN\n    gpio_write_pin_low(USB2422_RESET_PIN);\n    wait_us(2);\n    gpio_write_pin_high(USB2422_RESET_PIN);\n#endif\n}\n\nbool USB2422_active(void) {\n#ifdef USB2422_ACTIVE_PIN\n    return gpio_read_pin(USB2422_ACTIVE_PIN);\n#else\n    return 1;\n#endif\n}\n"
  },
  {
    "path": "drivers/usb2422.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <stdbool.h>\n\n#ifndef USB2422_ADDRESS\n#    define USB2422_ADDRESS 0x58\n#endif\n\n#ifndef USB2422_VENDOR_ID\n#    define USB2422_VENDOR_ID 0xFEED\n#endif\n#ifndef USB2422_PRODUCT_ID\n#    define USB2422_PRODUCT_ID 0x0001\n#endif\n#ifndef USB2422_DEVICE_VER\n#    define USB2422_DEVICE_VER 0x0001\n#endif\n\n#ifndef USB2422_MANUFACTURER\n#    define USB2422_MANUFACTURER \"QMK\"\n#endif\n#ifndef USB2422_PRODUCT\n#    define USB2422_PRODUCT \"QMK Hub\"\n#endif\n\n/** \\brief Initialises the dependent subsystems */\nvoid USB2422_init(void);\n\n/** \\brief Push configuration to the USB2422 device */\nvoid USB2422_configure(void);\n\n/** \\brief Reset the chip (RESET_N)\n *\n * NOTE:\n * Depends on a valid USB2422_RESET_PIN configuration\n */\nvoid USB2422_reset(void);\n\n/** \\brief Indicates the USB state of the hub (SUSP_IND)\n *\n * NOTE:\n * Depends on a valid USB2422_ACTIVE_PIN configuration\n */\nbool USB2422_active(void);\n"
  },
  {
    "path": "drivers/usbpd.h",
    "content": "/* Copyright 2021 Nick Brassel (@tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\ntypedef enum {\n    USBPD_500MA,\n    USBPD_1500MA,\n    USBPD_3000MA,\n} usbpd_allowance_t;\n\n// Initialises the USBPD subsystem\nvoid usbpd_init(void);\n\n// Gets the current state of the USBPD allowance\nusbpd_allowance_t usbpd_get_allowance(void);\n"
  },
  {
    "path": "drivers/wear_leveling/wear_leveling_flash_spi.c",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <stdbool.h>\n#include <hal.h>\n#include \"util.h\"\n#include \"timer.h\"\n#include \"wear_leveling.h\"\n#include \"wear_leveling_flash_spi_config.h\"\n#include \"wear_leveling_internal.h\"\n\n#ifndef WEAR_LEVELING_EXTERNAL_FLASH_BULK_COUNT\n#    define WEAR_LEVELING_EXTERNAL_FLASH_BULK_COUNT 32\n#endif // WEAR_LEVELING_EXTERNAL_FLASH_BULK_COUNT\n\nbool backing_store_init(void) {\n    bs_dprintf(\"Init\\n\");\n    flash_init();\n    return true;\n}\n\nbool backing_store_unlock(void) {\n    bs_dprintf(\"Unlock\\n\");\n    // No-op -- handled by the flash driver as it is.\n    return true;\n}\n\nbool backing_store_erase(void) {\n#ifdef WEAR_LEVELING_DEBUG_OUTPUT\n    uint32_t start = timer_read32();\n#endif\n\n    bool ret = true;\n    for (int i = 0; i < (WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_COUNT); ++i) {\n        flash_status_t status = flash_erase_block(((WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_OFFSET) + i) * (EXTERNAL_FLASH_BLOCK_SIZE));\n        if (status != FLASH_STATUS_SUCCESS) {\n            ret = false;\n            break;\n        }\n    }\n\n    bs_dprintf(\"Backing store erase took %ldms to complete\\n\", ((long)(timer_read32() - start)));\n    return ret;\n}\n\nbool backing_store_write(uint32_t address, backing_store_int_t value) {\n    return backing_store_write_bulk(address, &value, 1);\n}\n\nbool backing_store_lock(void) {\n    bs_dprintf(\"Lock  \\n\");\n    // No-op -- handled by the flash driver as it is.\n    return true;\n}\n\nbool backing_store_read(uint32_t address, backing_store_int_t *value) {\n    return backing_store_read_bulk(address, value, 1);\n}\n\nbool backing_store_read_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {\n    bs_dprintf(\"Read  \");\n    uint32_t       offset = (WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_OFFSET) * (EXTERNAL_FLASH_BLOCK_SIZE) + address;\n    flash_status_t status = flash_read_range(offset, values, sizeof(backing_store_int_t) * item_count);\n    if (status == FLASH_STATUS_SUCCESS) {\n        for (size_t i = 0; i < item_count; ++i) {\n            values[i] = ~values[i];\n        }\n        wl_dump(offset, values, sizeof(backing_store_int_t) * item_count);\n    }\n    return status == FLASH_STATUS_SUCCESS;\n}\n\nbool backing_store_write_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {\n    uint32_t            offset = (WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_OFFSET) * (EXTERNAL_FLASH_BLOCK_SIZE) + address;\n    size_t              index  = 0;\n    backing_store_int_t temp[WEAR_LEVELING_EXTERNAL_FLASH_BULK_COUNT];\n    do {\n        // Copy out the block of data we want to transmit first\n        size_t this_loop = MIN(item_count, WEAR_LEVELING_EXTERNAL_FLASH_BULK_COUNT);\n        for (size_t i = 0; i < this_loop; ++i) {\n            temp[i] = values[index + i];\n        }\n\n        bs_dprintf(\"Write \");\n        wl_dump(offset, temp, sizeof(backing_store_int_t) * this_loop);\n\n        // Take the complement instead\n        for (size_t i = 0; i < this_loop; ++i) {\n            temp[i] = ~temp[i];\n        }\n\n        // Write out the block\n        if (flash_write_range(offset, temp, sizeof(backing_store_int_t) * this_loop) != FLASH_STATUS_SUCCESS) {\n            return false;\n        }\n\n        offset += this_loop * sizeof(backing_store_int_t);\n        index += this_loop;\n        item_count -= this_loop;\n    } while (item_count > 0);\n\n    return true;\n}\n"
  },
  {
    "path": "drivers/wear_leveling/wear_leveling_flash_spi_config.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifndef __ASSEMBLER__\n#    include <stdlib.h>\n#    include <stdint.h>\n#    include \"flash_spi.h\"\n#endif\n\n// Use 1 block -- check the config for the SPI flash to determine how big it is\n#ifndef WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_COUNT\n#    define WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_COUNT 1\n#endif // WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_COUNT\n\n// Start at the first block of the external flash\n#ifndef WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_OFFSET\n#    define WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_OFFSET 0\n#endif // WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_OFFSET\n\n// 8-byte writes by default\n#ifndef BACKING_STORE_WRITE_SIZE\n#    define BACKING_STORE_WRITE_SIZE 8\n#endif\n\n// The space allocated by the block\n#ifndef WEAR_LEVELING_BACKING_SIZE\n#    define WEAR_LEVELING_BACKING_SIZE ((EXTERNAL_FLASH_BLOCK_SIZE) * (WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_COUNT))\n#endif // WEAR_LEVELING_BACKING_SIZE\n\n// Use half of the backing size for logical EEPROM\n#ifndef WEAR_LEVELING_LOGICAL_SIZE\n#    define WEAR_LEVELING_LOGICAL_SIZE ((WEAR_LEVELING_BACKING_SIZE) / 2)\n#endif // WEAR_LEVELING_LOGICAL_SIZE\n"
  },
  {
    "path": "layouts/default/60_abnt2/default_60_abnt2/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\n#include \"keymap_brazilian_abnt2.h\"\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ ´ │ [ │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ Ç │ ~ │ ] │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ ; │ / │ Shift│\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬──┴─┬────┤\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│ MO1│Ctrl│\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n     */\n    [0] = LAYOUT_60_abnt2(\n        KC_ESC,  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_BSPC,\n        KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,    KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,    BR_ACUT, BR_LBRC,\n        KC_CAPS, KC_A,    KC_S,    KC_D,    KC_F,    KC_G,    KC_H,    KC_J,    KC_K,    KC_L,    BR_CCED, BR_TILD, BR_RBRC, KC_ENT,\n        KC_LSFT, BR_BSLS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  BR_SCLN, BR_SLSH, KC_RSFT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, MO(1),   KC_RCTL\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │ ' │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│ Delete│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │Rst│ T │Ins│Hom│ ↑ │End│PgU│ ´ │ [ │     │\n     * ├─────┴┬──┴┬──┴┬──┴───┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Caps │ A │ S │ D │ F │ G │ H │ ← │ ↓ │ → │PgD│ ~ │ ] │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ ; │ / │ Shift│\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬──┴─┬────┤\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│ MO1│Ctrl│\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n     */\n    [1] = LAYOUT_60_abnt2(\n        BR_QUOT, KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,\n        _______, _______, _______, _______, QK_BOOT, _______, KC_INS,  KC_HOME, KC_UP,   KC_END,  KC_PGUP, _______, _______,\n        _______, _______, _______, _______, _______, _______, _______, KC_LEFT, KC_DOWN, KC_RGHT, KC_PGDN, _______, _______, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                            _______, _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_abnt2/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ABNT2 layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_abnt2\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3},\n                {\"x\":13.25, \"y\":3, \"w\":1.75},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":12.5, \"y\":4, \"w\":1.25},\n                {\"x\":13.75, \"y\":4, \"w\":1.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_abnt2/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\"]\n"
  },
  {
    "path": "layouts/default/60_abnt2/readme.md",
    "content": "# 60_abnt2\n\n![60% ABNT2 layout](https://raw.githubusercontent.com/noroadsleft/qmk_images/master/layouts/default/60_abnt2/keyboard-layout.png)\n"
  },
  {
    "path": "layouts/default/60_ansi/default_60_ansi/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n     */\n    [0] = LAYOUT_60_ansi(\n        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_BSPC,\n        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,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ANSI layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.75},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":12.5, \"y\":4, \"w\":1.25},\n                {\"x\":13.75, \"y\":4, \"w\":1.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_ansi/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\"]\n"
  },
  {
    "path": "layouts/default/60_ansi/readme.md",
    "content": "# 60_ansi\n\n    LAYOUT_60_ansi\n"
  },
  {
    "path": "layouts/default/60_ansi_arrow/default_60_ansi_arrow/keymap.c",
    "content": "// Copyright 2020 QMK / Sendy YK\n// SPDX-License-Identifier: GPL-2.0-or-later/\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┬───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ Shift│ ↑ │ / │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │Space                   │Alt│GUI│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_60_ansi_arrow(\n        QK_GESC, 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_BSPC,\n        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,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_RSFT, KC_UP,   KC_SLSH,\n        KC_LCTL, KC_LGUI, KC_LALT,                   KC_SPC,                             KC_RALT, KC_RGUI, KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_ansi_arrow/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ANSI Arrow Layout\",\n    \"url\": \"https://mr.sendyyk.com\",\n    \"maintainer\": \"Sendy YK <mr@sendyyk.com>\",\n    \"layouts\": {\n        \"LAYOUT_60_ansi_arrow\": {\n            \"layout\": [\n                {\"x\": 0, \"y\": 0},\n                {\"x\": 1, \"y\": 0},\n                {\"x\": 2, \"y\": 0},\n                {\"x\": 3, \"y\": 0},\n                {\"x\": 4, \"y\": 0},\n                {\"x\": 5, \"y\": 0},\n                {\"x\": 6, \"y\": 0},\n                {\"x\": 7, \"y\": 0},\n                {\"x\": 8, \"y\": 0},\n                {\"x\": 9, \"y\": 0},\n                {\"x\": 10, \"y\": 0},\n                {\"x\": 11, \"y\": 0},\n                {\"x\": 12, \"y\": 0},\n                {\"x\": 13, \"y\": 0, \"w\": 2},\n\n                {\"x\": 0, \"y\": 1, \"w\": 1.5},\n                {\"x\": 1.5, \"y\": 1},\n                {\"x\": 2.5, \"y\": 1},\n                {\"x\": 3.5, \"y\": 1},\n                {\"x\": 4.5, \"y\": 1},\n                {\"x\": 5.5, \"y\": 1},\n                {\"x\": 6.5, \"y\": 1},\n                {\"x\": 7.5, \"y\": 1},\n                {\"x\": 8.5, \"y\": 1},\n                {\"x\": 9.5, \"y\": 1},\n                {\"x\": 10.5, \"y\": 1},\n                {\"x\": 11.5, \"y\": 1},\n                {\"x\": 12.5, \"y\": 1},\n                {\"x\": 13.5, \"y\": 1, \"w\": 1.5},\n\n                {\"x\": 0, \"y\": 2, \"w\": 1.75},\n                {\"x\": 1.75, \"y\": 2},\n                {\"x\": 2.75, \"y\": 2},\n                {\"x\": 3.75, \"y\": 2},\n                {\"x\": 4.75, \"y\": 2},\n                {\"x\": 5.75, \"y\": 2},\n                {\"x\": 6.75, \"y\": 2},\n                {\"x\": 7.75, \"y\": 2},\n                {\"x\": 8.75, \"y\": 2},\n                {\"x\": 9.75, \"y\": 2},\n                {\"x\": 10.75, \"y\": 2},\n                {\"x\": 11.75, \"y\": 2},\n                {\"x\": 12.75, \"y\": 2, \"w\": 2.25},\n\n                {\"x\": 0, \"y\": 3, \"w\": 2.25},\n                {\"x\": 2.25, \"y\": 3},\n                {\"x\": 3.25, \"y\": 3},\n                {\"x\": 4.25, \"y\": 3},\n                {\"x\": 5.25, \"y\": 3},\n                {\"x\": 6.25, \"y\": 3},\n                {\"x\": 7.25, \"y\": 3},\n                {\"x\": 8.25, \"y\": 3},\n                {\"x\": 9.25, \"y\": 3},\n                {\"x\": 10.25, \"y\": 3},\n                {\"x\": 11.25, \"y\": 3, \"w\": 1.75},\n                {\"x\": 13, \"y\": 3},\n                {\"x\": 14, \"y\": 3},\n\n                {\"x\": 0, \"y\": 4, \"w\": 1.25},\n                {\"x\": 1.25, \"y\": 4, \"w\": 1.25},\n                {\"x\": 2.5, \"y\": 4, \"w\": 1.25},\n                {\"x\": 3.75, \"y\": 4, \"w\": 6.25},\n                {\"x\": 10, \"y\": 4},\n                {\"x\": 11, \"y\": 4},\n                {\"x\": 12, \"y\": 4},\n                {\"x\": 13, \"y\": 4},\n                {\"x\": 14, \"y\": 4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_ansi_arrow/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\"]\n    "
  },
  {
    "path": "layouts/default/60_ansi_arrow/readme.md",
    "content": "# 60_ansi_arrow Keymap\n\nDefault 60 ANSI Arrow Keymap by [Sendy YK](https://mr.sendyyk.com).\n\n```c\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │Esc│1  │2  │3  │4  │5  │6  │7  │8  │9  │0  │-  │+  │Bspc   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │Tab  │Q  │W  │E  │R  │T  │Y  │U  │I  │O  │P  │[  │]  │\\    │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │Caps  │A  │S  │D  │F  │G  │H  │J  │K  │L  │;  │'  │Enter   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┬───┤\n     * │Shift   │Z  │X  │C  │V  │B  │N  │M  │,  │.  │Shift │↑  │/  │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │Space                   │Alt│GUI│←  │↓  │→  │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n     */\n```\n\n## Build The Firmware\n\nMake example for keyboard (after setting up your build environment):\n\n    make <keyboard_folder>:default_60_ansi_arrow\n\nMore information:\n* [Setting Up Your QMK Environment](https://docs.qmk.fm/#/getting_started_build_tools)\n* [More Detailed make Instructions](https://docs.qmk.fm/#/getting_started_make_guide)\n* [The Complete Newbs Guide To QMK](https://docs.qmk.fm/#/newbs)\n"
  },
  {
    "path": "layouts/default/60_ansi_arrow_split_bs/default_60_ansi_arrow_split_bs/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later/\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ + │ \\ │ ` │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ Bspc│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┬───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ Shift│ ↑ │ / │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │Alt│GUI│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_60_ansi_arrow_split_bs(\n        KC_ESC,  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_BSLS, KC_GRV,\n        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_BSPC,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_RSFT, KC_UP,   KC_SLSH,\n        KC_LCTL, KC_LGUI, KC_LALT,                   KC_SPC,                             KC_RALT, KC_RGUI, KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_ansi_arrow_split_bs/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ANSI Arrow Layout with Split Backspace\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_ansi_arrow_split_bs\": {\n            \"layout\": [\n                {\"x\": 0, \"y\": 0},\n                {\"x\": 1, \"y\": 0},\n                {\"x\": 2, \"y\": 0},\n                {\"x\": 3, \"y\": 0},\n                {\"x\": 4, \"y\": 0},\n                {\"x\": 5, \"y\": 0},\n                {\"x\": 6, \"y\": 0},\n                {\"x\": 7, \"y\": 0},\n                {\"x\": 8, \"y\": 0},\n                {\"x\": 9, \"y\": 0},\n                {\"x\": 10, \"y\": 0},\n                {\"x\": 11, \"y\": 0},\n                {\"x\": 12, \"y\": 0},\n                {\"x\": 13, \"y\": 0},\n                {\"x\": 14, \"y\": 0},\n\n                {\"x\": 0, \"y\": 1, \"w\": 1.5},\n                {\"x\": 1.5, \"y\": 1},\n                {\"x\": 2.5, \"y\": 1},\n                {\"x\": 3.5, \"y\": 1},\n                {\"x\": 4.5, \"y\": 1},\n                {\"x\": 5.5, \"y\": 1},\n                {\"x\": 6.5, \"y\": 1},\n                {\"x\": 7.5, \"y\": 1},\n                {\"x\": 8.5, \"y\": 1},\n                {\"x\": 9.5, \"y\": 1},\n                {\"x\": 10.5, \"y\": 1},\n                {\"x\": 11.5, \"y\": 1},\n                {\"x\": 12.5, \"y\": 1},\n                {\"x\": 13.5, \"y\": 1, \"w\": 1.5},\n\n                {\"x\": 0, \"y\": 2, \"w\": 1.75},\n                {\"x\": 1.75, \"y\": 2},\n                {\"x\": 2.75, \"y\": 2},\n                {\"x\": 3.75, \"y\": 2},\n                {\"x\": 4.75, \"y\": 2},\n                {\"x\": 5.75, \"y\": 2},\n                {\"x\": 6.75, \"y\": 2},\n                {\"x\": 7.75, \"y\": 2},\n                {\"x\": 8.75, \"y\": 2},\n                {\"x\": 9.75, \"y\": 2},\n                {\"x\": 10.75, \"y\": 2},\n                {\"x\": 11.75, \"y\": 2},\n                {\"x\": 12.75, \"y\": 2, \"w\": 2.25},\n\n                {\"x\": 0, \"y\": 3, \"w\": 2.25},\n                {\"x\": 2.25, \"y\": 3},\n                {\"x\": 3.25, \"y\": 3},\n                {\"x\": 4.25, \"y\": 3},\n                {\"x\": 5.25, \"y\": 3},\n                {\"x\": 6.25, \"y\": 3},\n                {\"x\": 7.25, \"y\": 3},\n                {\"x\": 8.25, \"y\": 3},\n                {\"x\": 9.25, \"y\": 3},\n                {\"x\": 10.25, \"y\": 3},\n                {\"x\": 11.25, \"y\": 3, \"w\": 1.75},\n                {\"x\": 13, \"y\": 3},\n                {\"x\": 14, \"y\": 3},\n\n                {\"x\": 0, \"y\": 4, \"w\": 1.25},\n                {\"x\": 1.25, \"y\": 4, \"w\": 1.25},\n                {\"x\": 2.5, \"y\": 4, \"w\": 1.25},\n                {\"x\": 3.75, \"y\": 4, \"w\": 6.25},\n                {\"x\": 10, \"y\": 4},\n                {\"x\": 11, \"y\": 4},\n                {\"x\": 12, \"y\": 4},\n                {\"x\": 13, \"y\": 4},\n                {\"x\": 14, \"y\": 4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_ansi_arrow_split_bs/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/60_ansi_arrow_split_bs/readme.md",
    "content": "# 60_ansi_arrow_split_bs\n\n    LAYOUT_60_ansi_arrow_split_bs\n"
  },
  {
    "path": "layouts/default/60_ansi_split_bs_rshift/default_60_ansi_split_bs_rshift/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Shift │Sft│\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n     */\n    [0] = LAYOUT_60_ansi_split_bs_rshift(\n        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_BSPC, KC_BSPC,\n        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,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_ansi_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ANSI layout with split Backspace and Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_ansi_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":12.5, \"y\":4, \"w\":1.25},\n                {\"x\":13.75, \"y\":4, \"w\":1.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_ansi_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\"]\n"
  },
  {
    "path": "layouts/default/60_ansi_split_bs_rshift/readme.md",
    "content": "# 60_ansi_split_bs_rshift\n\n    LAYOUT_60_ansi_split_bs_rshift\n"
  },
  {
    "path": "layouts/default/60_ansi_tsangan/default_60_ansi_tsangan/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤\n     * │Ctrl │GUI│ Alt │                           │ Alt │GUI│ Ctrl│\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘\n     */\n    [0] = LAYOUT_60_ansi_tsangan(\n        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_BSPC,\n        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,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_RGUI, KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_ansi_tsangan/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ANSI Tsangan layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_ansi_tsangan\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.75},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":1.5, \"y\":4},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":12.5, \"y\":4},\n                {\"x\":13.5, \"y\":4, \"w\":1.5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_ansi_tsangan/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\"]\n"
  },
  {
    "path": "layouts/default/60_ansi_tsangan/readme.md",
    "content": "# 60_ansi_tsangan\n\n    LAYOUT_60_ansi_tsangan\n"
  },
  {
    "path": "layouts/default/60_ansi_tsangan_split_bs_rshift/default_60_ansi_tsangan_split_bs_rshift/keymap.c",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│Sft│\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤\n     * │Ctrl │GUI│ Alt │                           │ Alt │GUI│ Ctrl│\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘\n     */\n    [0] = LAYOUT_60_ansi_tsangan_split_bs_rshift(\n        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_BSPC, KC_BSPC,\n        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,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_RGUI, KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_ansi_tsangan_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ANSI layout with split Backspace, split Right Shift, and Tsangan Bottom Row\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_ansi_tsangan_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":1.5, \"y\":4},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":12.5, \"y\":4},\n                {\"x\":13.5, \"y\":4, \"w\":1.5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_ansi_tsangan_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\"]\n"
  },
  {
    "path": "layouts/default/60_ansi_tsangan_split_bs_rshift/readme.md",
    "content": "# 60_ansi_tsangan_split_bs_rshift\n\n    LAYOUT_60_ansi_tsangan_split_bs_rshift\n"
  },
  {
    "path": "layouts/default/60_ansi_wkl/default_60_ansi_wkl/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤\n     * │Ctrl │   │ Alt │                           │ Alt │   │ Ctrl│\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘\n     */\n    [0] = LAYOUT_60_ansi_wkl(\n        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_BSPC,\n        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,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_ansi_wkl/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ANSI Winkeyless layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_ansi_wkl\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.75},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":13.5, \"y\":4, \"w\":1.5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_ansi_wkl/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\"]\n"
  },
  {
    "path": "layouts/default/60_ansi_wkl/readme.md",
    "content": "# 60_ansi_wkl\n\n    LAYOUT_60_ansi_wkl\n"
  },
  {
    "path": "layouts/default/60_ansi_wkl_split_bs_rshift/default_60_ansi_wkl_split_bs_rshift/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Del│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Shift │Sft│\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤\n     * │Ctrl │   │ Alt │                           │ Alt │   │ Ctrl│\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘\n     */\n    [0] = LAYOUT_60_ansi_wkl_split_bs_rshift(\n        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_BSPC, KC_DEL,\n        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,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_ansi_wkl_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ANSI Winkeyless layout with split Backspace and split Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_ansi_wkl_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":13.5, \"y\":4, \"w\":1.5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_ansi_wkl_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\"]\n"
  },
  {
    "path": "layouts/default/60_ansi_wkl_split_bs_rshift/readme.md",
    "content": "# 60_ansi_wkl_split_bs_rshift\n\n    LAYOUT_60_ansi_wkl_split_bs_rshift\n"
  },
  {
    "path": "layouts/default/60_hhkb/default_60_hhkb/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ \\ │ ` │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │ Bspc│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │ Ctrl │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│MO1│\n     * └─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┘\n     *       │Alt│ GUI │                           │ GUI │Alt│\n     *       └───┴─────┴───────────────────────────┴─────┴───┘\n     */\n    [0] = LAYOUT_60_hhkb(\n        KC_ESC,  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_BSLS, KC_GRV,\n        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_BSPC,\n        KC_LCTL, KC_A,    KC_S,    KC_D,    KC_F,    KC_G,    KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_QUOT,          KC_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, MO(1),\n                          KC_LALT, KC_LGUI,                   KC_SPC,                             KC_RGUI, KC_RALT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Pwr│F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│Ins│Del│\n     * ├───┴─┬─┴───┴───┴───┴───┴───┴───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴───┘\n     * │Caps │ Q │ W │ E │ R │ T │ Y │ U │PSc│Scr│Pse│ ↑ │ ] │ Bspc│\n     * └─────┘┌───┬───┬───┐──┴┬──┴┌───┬──┴┬──┴┬──┴┬──┴┬──┴┬────────┐\n     * │ Ctrl │Vl-│Vl+│Mut│ F │ G │ * │ / │Hom│PgU│ ← │ → │  Enter │\n     * ├──────└───┴───┴───┘─┬─┴─┬─└─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┘\n     * │ Shift  │ Z │ X │ C │ V │ B │ + │ - │End│PgD│ ↓ │ Shift│MO1│\n     * └─────┬──┴┬──┴──┬┴───┴───┴───└───┴───┴───┴───┴───┘┬───┬─┴───┘\n     *       │Alt│ GUI │                           │ GUI │Alt│\n     *       └───┴─────┴───────────────────────────┴─────┴───┘\n     */\n    [1] = LAYOUT_60_hhkb(\n        KC_PWR,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_INS,  KC_DEL,\n        KC_CAPS, _______, _______, _______, _______, _______, _______, _______, KC_PSCR, KC_SCRL, KC_PAUS, KC_UP,   _______, _______,\n        _______, KC_VOLD, KC_VOLU, KC_MUTE, _______, _______, KC_PAST, KC_PSLS, KC_HOME, KC_PGUP, KC_LEFT, KC_RGHT,          KC_PENT,\n        _______,          _______, _______, _______, _______, _______, KC_PPLS, KC_PMNS, KC_END,  KC_PGDN, KC_DOWN, _______, _______,\n                          _______, _______,                   _______,                            _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_hhkb/info.json",
    "content": "{\n    \"keyboard_name\": \"60% HHKB layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_hhkb\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n\n                {\"x\":1.5, \"y\":4},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":12.5, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_hhkb/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\"],\n[{x:1.5},\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/60_hhkb/readme.md",
    "content": "# 60_hhkb\n\n    LAYOUT_60_hhkb\n"
  },
  {
    "path": "layouts/default/60_iso/default_60_iso/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n     */\n    [0] = LAYOUT_60_iso(\n        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_BSPC,\n        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,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ISO layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.75},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":12.5, \"y\":4, \"w\":1.25},\n                {\"x\":13.75, \"y\":4, \"w\":1.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_iso/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\"]\n"
  },
  {
    "path": "layouts/default/60_iso/readme.md",
    "content": "# 60_iso\n\n    LAYOUT_60_iso\n"
  },
  {
    "path": "layouts/default/60_iso_arrow/default_60_iso_arrow/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later/\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ + │ Backsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬──┴┬───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ Shift│ ↑ │ / │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │Alt│GUI│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_60_iso_arrow(\n        QK_GESC, 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_BSPC,\n        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,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_RSFT, KC_UP,   KC_SLSH,\n        KC_LCTL, KC_LGUI, KC_LALT,                   KC_SPC,                             KC_RALT, KC_RGUI, KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_iso_arrow/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ISO Arrow Layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_iso_arrow\": {\n            \"layout\": [\n                {\"x\": 0, \"y\": 0},\n                {\"x\": 1, \"y\": 0},\n                {\"x\": 2, \"y\": 0},\n                {\"x\": 3, \"y\": 0},\n                {\"x\": 4, \"y\": 0},\n                {\"x\": 5, \"y\": 0},\n                {\"x\": 6, \"y\": 0},\n                {\"x\": 7, \"y\": 0},\n                {\"x\": 8, \"y\": 0},\n                {\"x\": 9, \"y\": 0},\n                {\"x\": 10, \"y\": 0},\n                {\"x\": 11, \"y\": 0},\n                {\"x\": 12, \"y\": 0},\n                {\"x\": 13, \"y\": 0, \"w\": 2},\n\n                {\"x\": 0, \"y\": 1, \"w\": 1.5},\n                {\"x\": 1.5, \"y\": 1},\n                {\"x\": 2.5, \"y\": 1},\n                {\"x\": 3.5, \"y\": 1},\n                {\"x\": 4.5, \"y\": 1},\n                {\"x\": 5.5, \"y\": 1},\n                {\"x\": 6.5, \"y\": 1},\n                {\"x\": 7.5, \"y\": 1},\n                {\"x\": 8.5, \"y\": 1},\n                {\"x\": 9.5, \"y\": 1},\n                {\"x\": 10.5, \"y\": 1},\n                {\"x\": 11.5, \"y\": 1},\n                {\"x\": 12.5, \"y\": 1},\n\n                {\"x\": 0, \"y\": 2, \"w\": 1.75},\n                {\"x\": 1.75, \"y\": 2},\n                {\"x\": 2.75, \"y\": 2},\n                {\"x\": 3.75, \"y\": 2},\n                {\"x\": 4.75, \"y\": 2},\n                {\"x\": 5.75, \"y\": 2},\n                {\"x\": 6.75, \"y\": 2},\n                {\"x\": 7.75, \"y\": 2},\n                {\"x\": 8.75, \"y\": 2},\n                {\"x\": 9.75, \"y\": 2},\n                {\"x\": 10.75, \"y\": 2},\n                {\"x\": 11.75, \"y\": 2},\n                {\"x\": 12.75, \"y\": 2},\n                {\"x\": 13.75, \"y\": 1, \"w\": 1.25, \"h\": 2},\n\n                {\"x\": 0, \"y\": 3, \"w\": 1.25},\n                {\"x\": 1.25, \"y\": 3},\n                {\"x\": 2.25, \"y\": 3},\n                {\"x\": 3.25, \"y\": 3},\n                {\"x\": 4.25, \"y\": 3},\n                {\"x\": 5.25, \"y\": 3},\n                {\"x\": 6.25, \"y\": 3},\n                {\"x\": 7.25, \"y\": 3},\n                {\"x\": 8.25, \"y\": 3},\n                {\"x\": 9.25, \"y\": 3},\n                {\"x\": 10.25, \"y\": 3},\n                {\"x\": 11.25, \"y\": 3, \"w\": 1.75},\n                {\"x\": 13, \"y\": 3},\n                {\"x\": 14, \"y\": 3},\n\n                {\"x\": 0, \"y\": 4, \"w\": 1.25},\n                {\"x\": 1.25, \"y\": 4, \"w\": 1.25},\n                {\"x\": 2.5, \"y\": 4, \"w\": 1.25},\n                {\"x\": 3.75, \"y\": 4, \"w\": 6.25},\n                {\"x\": 10, \"y\": 4},\n                {\"x\": 11, \"y\": 4},\n                {\"x\": 12, \"y\": 4},\n                {\"x\": 13, \"y\": 4},\n                {\"x\": 14, \"y\": 4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_iso_arrow/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/60_iso_arrow/readme.md",
    "content": "# 60_iso_arrow\n\n    LAYOUT_60_iso_arrow\n"
  },
  {
    "path": "layouts/default/60_iso_arrow_split_bs/default_60_iso_arrow_split_bs/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later/\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ + │Bsp│Del│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬──┴┬───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ Shift│ ↑ │ / │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │Alt│GUI│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_60_iso_arrow_split_bs(\n        QK_GESC, 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_BSPC, KC_DEL,\n        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,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_RSFT, KC_UP,   KC_SLSH,\n        KC_LCTL, KC_LGUI, KC_LALT,                   KC_SPC,                             KC_RALT, KC_RGUI, KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_iso_arrow_split_bs/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ISO Arrow Layout with Split Backspace\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_iso_arrow_split_bs\": {\n            \"layout\": [\n                {\"x\": 0, \"y\": 0},\n                {\"x\": 1, \"y\": 0},\n                {\"x\": 2, \"y\": 0},\n                {\"x\": 3, \"y\": 0},\n                {\"x\": 4, \"y\": 0},\n                {\"x\": 5, \"y\": 0},\n                {\"x\": 6, \"y\": 0},\n                {\"x\": 7, \"y\": 0},\n                {\"x\": 8, \"y\": 0},\n                {\"x\": 9, \"y\": 0},\n                {\"x\": 10, \"y\": 0},\n                {\"x\": 11, \"y\": 0},\n                {\"x\": 12, \"y\": 0},\n                {\"x\": 13, \"y\": 0},\n                {\"x\": 14, \"y\": 0},\n\n                {\"x\": 0, \"y\": 1, \"w\": 1.5},\n                {\"x\": 1.5, \"y\": 1},\n                {\"x\": 2.5, \"y\": 1},\n                {\"x\": 3.5, \"y\": 1},\n                {\"x\": 4.5, \"y\": 1},\n                {\"x\": 5.5, \"y\": 1},\n                {\"x\": 6.5, \"y\": 1},\n                {\"x\": 7.5, \"y\": 1},\n                {\"x\": 8.5, \"y\": 1},\n                {\"x\": 9.5, \"y\": 1},\n                {\"x\": 10.5, \"y\": 1},\n                {\"x\": 11.5, \"y\": 1},\n                {\"x\": 12.5, \"y\": 1},\n\n                {\"x\": 0, \"y\": 2, \"w\": 1.75},\n                {\"x\": 1.75, \"y\": 2},\n                {\"x\": 2.75, \"y\": 2},\n                {\"x\": 3.75, \"y\": 2},\n                {\"x\": 4.75, \"y\": 2},\n                {\"x\": 5.75, \"y\": 2},\n                {\"x\": 6.75, \"y\": 2},\n                {\"x\": 7.75, \"y\": 2},\n                {\"x\": 8.75, \"y\": 2},\n                {\"x\": 9.75, \"y\": 2},\n                {\"x\": 10.75, \"y\": 2},\n                {\"x\": 11.75, \"y\": 2},\n                {\"x\": 12.75, \"y\": 2},\n                {\"x\": 13.75, \"y\": 1, \"w\": 1.25, \"h\": 2},\n\n                {\"x\": 0, \"y\": 3, \"w\": 1.25},\n                {\"x\": 1.25, \"y\": 3},\n                {\"x\": 2.25, \"y\": 3},\n                {\"x\": 3.25, \"y\": 3},\n                {\"x\": 4.25, \"y\": 3},\n                {\"x\": 5.25, \"y\": 3},\n                {\"x\": 6.25, \"y\": 3},\n                {\"x\": 7.25, \"y\": 3},\n                {\"x\": 8.25, \"y\": 3},\n                {\"x\": 9.25, \"y\": 3},\n                {\"x\": 10.25, \"y\": 3},\n                {\"x\": 11.25, \"y\": 3, \"w\": 1.75},\n                {\"x\": 13, \"y\": 3},\n                {\"x\": 14, \"y\": 3},\n\n                {\"x\": 0, \"y\": 4, \"w\": 1.25},\n                {\"x\": 1.25, \"y\": 4, \"w\": 1.25},\n                {\"x\": 2.5, \"y\": 4, \"w\": 1.25},\n                {\"x\": 3.75, \"y\": 4, \"w\": 6.25},\n                {\"x\": 10, \"y\": 4},\n                {\"x\": 11, \"y\": 4},\n                {\"x\": 12, \"y\": 4},\n                {\"x\": 13, \"y\": 4},\n                {\"x\": 14, \"y\": 4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_iso_arrow_split_bs/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/60_iso_arrow_split_bs/readme.md",
    "content": "# 60_iso_arrow_split_bs\n\n    LAYOUT_60_iso_arrow_split_bs\n"
  },
  {
    "path": "layouts/default/60_iso_split_bs_rshift/default_60_iso_split_bs_rshift/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Shift │Sft│\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n     */\n    [0] = LAYOUT_60_iso_split_bs_rshift(\n        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_BSPC, KC_BSPC,\n        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,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_iso_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ISO layout with split Backspace and Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_iso_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":12.5, \"y\":4, \"w\":1.25},\n                {\"x\":13.75, \"y\":4, \"w\":1.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_iso_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\"]\n"
  },
  {
    "path": "layouts/default/60_iso_split_bs_rshift/readme.md",
    "content": "# 60_iso_split_bs_rshift\n\n    LAYOUT_60_iso_split_bs_rshift\n"
  },
  {
    "path": "layouts/default/60_iso_tsangan/default_60_iso_tsangan/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤\n     * │Ctrl │GUI│ Alt │                           │ Alt │GUI│ Ctrl│\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘\n     */\n    [0] = LAYOUT_60_iso_tsangan(\n        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_BSPC,\n        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,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_RGUI, KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_iso_tsangan/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ISO Tsangan layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_iso_tsangan\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.75},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":1.5, \"y\":4},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":12.5, \"y\":4},\n                {\"x\":13.5, \"y\":4, \"w\":1.5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_iso_tsangan/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\"]\n"
  },
  {
    "path": "layouts/default/60_iso_tsangan/readme.md",
    "content": "# 60_iso_tsangan\n\n    LAYOUT_60_iso_tsangan\n"
  },
  {
    "path": "layouts/default/60_iso_tsangan_split_bs_rshift/default_60_iso_tsangan_split_bs_rshift/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Shift │Sft│\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤\n     * │Ctrl │GUI│ Alt │                           │ Alt │GUI│ Ctrl│\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘\n     */\n    [0] = LAYOUT_60_iso_tsangan_split_bs_rshift(\n        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_BSPC, KC_BSPC,\n        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,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_RGUI, KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_iso_tsangan_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ISO layout with split Backspace, split Right Shift, and Tsangan Bottom Row\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_iso_tsangan_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":1.5, \"y\":4},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":12.5, \"y\":4},\n                {\"x\":13.5, \"y\":4, \"w\":1.5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_iso_tsangan_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\"]\n"
  },
  {
    "path": "layouts/default/60_iso_tsangan_split_bs_rshift/readme.md",
    "content": "# 60_iso_tsangan_split_bs_rshift\n\n    LAYOUT_60_iso_tsangan_split_bs_rshift\n"
  },
  {
    "path": "layouts/default/60_iso_wkl/default_60_iso_wkl/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤\n     * │Ctrl │   │ Alt │                           │ Alt │   │ Ctrl│\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘\n     */\n    [0] = LAYOUT_60_iso_wkl(\n        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_BSPC,\n        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,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_iso_wkl/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ISO Winkeyless layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_iso_wkl\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.75},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":13.5, \"y\":4, \"w\":1.5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_iso_wkl/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\"]\n"
  },
  {
    "path": "layouts/default/60_iso_wkl/readme.md",
    "content": "# 60_iso_wkl\n\n    LAYOUT_60_iso_wkl\n"
  },
  {
    "path": "layouts/default/60_iso_wkl_split_bs_rshift/default_60_iso_wkl_split_bs_rshift/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Del│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Shift │Sft│\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤\n     * │Ctrl │   │ Alt │                           │ Alt │   │ Ctrl│\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘\n     */\n    [0] = LAYOUT_60_iso_wkl_split_bs_rshift(\n        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_BSPC, KC_DEL,\n        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,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_iso_wkl_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"60% ISO Winkeyless with split Backspace and split Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_iso_wkl_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":13.5, \"y\":4, \"w\":1.5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_iso_wkl_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\"]\n"
  },
  {
    "path": "layouts/default/60_iso_wkl_split_bs_rshift/readme.md",
    "content": "# 60_iso_wkl_split_bs_rshift\n\n    LAYOUT_60_iso_wkl_split_bs_rshift\n"
  },
  {
    "path": "layouts/default/60_jis/default_60_jis/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ZHK│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ ^ │ ¥ │Bsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ @ │ [ │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Eisu │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ ] │    │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ \\ │ Shft │\n     * ├────┬───┴┬──┴─┬─┴──┬┴───┴───┴───┴─┬─┴──┬┴───┼───┴┬──┴─┬────┤\n     * │Ctrl│GUI │Alt │Mhen│    Space     │Henk│Kana│Alt │GUI │Ctrl│\n     * └────┴────┴────┴────┴──────────────┴────┴────┴────┴────┴────┘\n     */\n    [0] = LAYOUT_60_jis(\n        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_INT3, KC_BSPC,\n        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,\n        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_NUHS, KC_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_INT1, KC_RSFT,\n        KC_LCTL, KC_LGUI, KC_LALT, KC_INT5,                  KC_SPC,                     KC_INT4, KC_INT2, KC_RALT, KC_RGUI, KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/60_jis/info.json",
    "content": "{\n    \"keyboard_name\": \"60% JIS layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_60_jis\": {\n            \"layout\": [\n                {\"x\": 0, \"y\": 0},\n                {\"x\": 1, \"y\": 0},\n                {\"x\": 2, \"y\": 0},\n                {\"x\": 3, \"y\": 0},\n                {\"x\": 4, \"y\": 0},\n                {\"x\": 5, \"y\": 0},\n                {\"x\": 6, \"y\": 0},\n                {\"x\": 7, \"y\": 0},\n                {\"x\": 8, \"y\": 0},\n                {\"x\": 9, \"y\": 0},\n                {\"x\": 10, \"y\": 0},\n                {\"x\": 11, \"y\": 0},\n                {\"x\": 12, \"y\": 0},\n                {\"x\": 13, \"y\": 0},\n                {\"x\": 14, \"y\": 0},\n\n                {\"x\": 0, \"y\": 1, \"w\": 1.5},\n                {\"x\": 1.5, \"y\": 1},\n                {\"x\": 2.5, \"y\": 1},\n                {\"x\": 3.5, \"y\": 1},\n                {\"x\": 4.5, \"y\": 1},\n                {\"x\": 5.5, \"y\": 1},\n                {\"x\": 6.5, \"y\": 1},\n                {\"x\": 7.5, \"y\": 1},\n                {\"x\": 8.5, \"y\": 1},\n                {\"x\": 9.5, \"y\": 1},\n                {\"x\": 10.5, \"y\": 1},\n                {\"x\": 11.5, \"y\": 1},\n                {\"x\": 12.5, \"y\": 1},\n\n                {\"x\": 0, \"y\": 2, \"w\": 1.75},\n                {\"x\": 1.75, \"y\": 2},\n                {\"x\": 2.75, \"y\": 2},\n                {\"x\": 3.75, \"y\": 2},\n                {\"x\": 4.75, \"y\": 2},\n                {\"x\": 5.75, \"y\": 2},\n                {\"x\": 6.75, \"y\": 2},\n                {\"x\": 7.75, \"y\": 2},\n                {\"x\": 8.75, \"y\": 2},\n                {\"x\": 9.75, \"y\": 2},\n                {\"x\": 10.75, \"y\": 2},\n                {\"x\": 11.75, \"y\": 2},\n                {\"x\": 12.75, \"y\": 2},\n                {\"x\": 13.75, \"y\": 1, \"w\": 1.25, \"h\": 2},\n\n                {\"x\": 0, \"y\": 3, \"w\": 2.25},\n                {\"x\": 2.25, \"y\": 3},\n                {\"x\": 3.25, \"y\": 3},\n                {\"x\": 4.25, \"y\": 3},\n                {\"x\": 5.25, \"y\": 3},\n                {\"x\": 6.25, \"y\": 3},\n                {\"x\": 7.25, \"y\": 3},\n                {\"x\": 8.25, \"y\": 3},\n                {\"x\": 9.25, \"y\": 3},\n                {\"x\": 10.25, \"y\": 3},\n                {\"x\": 11.25, \"y\": 3},\n                {\"x\": 12.25, \"y\": 3},\n                {\"x\": 13.25, \"y\": 3, \"w\": 1.75},\n\n                {\"x\": 0, \"y\": 4, \"w\": 1.25},\n                {\"x\": 1.25, \"y\": 4, \"w\": 1.25},\n                {\"x\": 2.5, \"y\": 4, \"w\": 1.25},\n                {\"x\": 3.75, \"y\": 4, \"w\": 1.25},\n                {\"x\": 5, \"y\": 4, \"w\": 3.75},\n                {\"x\": 8.75, \"y\": 4, \"w\": 1.25},\n                {\"x\": 10, \"y\": 4, \"w\": 1.25},\n                {\"x\": 11.25, \"y\": 4, \"w\": 1.25},\n                {\"x\": 12.5, \"y\": 4, \"w\": 1.25},\n                {\"x\": 13.75, \"y\": 4, \"w\": 1.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/60_jis/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:3.75},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\"]\n"
  },
  {
    "path": "layouts/default/60_jis/readme.md",
    "content": "# 60_jis\n\n    LAYOUT_60_jis\n"
  },
  {
    "path": "layouts/default/64_ansi/default_64_ansi/keymap.c",
    "content": "// Copyright 2020 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬───┬───┤\n     * │ Shift │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Sft│ ↑ │mo1│\n     * ├────┬──┴─┬─┴──┬┴───┴───┴───┴───┴───┴───┼───┼───┼───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │Alt│Ctl│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n     */    \n    [0] = LAYOUT_64_ansi(\n        KC_ESC,  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_BSPC,\n        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,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   MO(1), \n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│ Delete│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │     │   │   │   │   │   │   │   │   │   │   │PSn│SLk│Pause│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │        │\n     * ├──────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬───┬───┤\n     * │       │   │   │   │   │   │   │   │   │   │   │   │PgU│   │\n     * ├────┬──┴─┬─┴──┬┴───┴───┴───┴───┴───┴───┼───┼───┼───┼───┼───┤\n     * │    │    │    │                        │   │mo2│Hom│PgD│End│\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n     */ \n    [1] = LAYOUT_64_ansi(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,           KC_DEL,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PSCR, KC_SCRL,          KC_PAUS,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,                   _______,\n        _______,          _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PGUP, _______, \n        _______, _______, _______,                            _______,                            _______, MO(2),   KC_HOME, KC_PGDN, KC_END\n    ),\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │     │   │   │   │QBt│   │   │   │   │   │   │   │   │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │        │\n     * ├──────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬───┬───┤\n     * │       │   │   │   │   │   │   │   │   │   │   │   │   │   │\n     * ├────┬──┴─┬─┴──┬┴───┴───┴───┴───┴───┴───┼───┼───┼───┼───┼───┤\n     * │    │    │    │                        │   │   │   │   │   │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n     */ \n    [2] = LAYOUT_64_ansi(\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,          _______, \n        _______, _______, _______, _______, QK_BOOT, _______, _______, _______, _______, _______, _______, _______, _______,          _______, \n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,                   _______,\n        _______,          _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, \n        _______, _______, _______,                            _______,                            _______, _______, _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/64_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"60% 64-key ANSI Layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_64_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n\n                {\"x\":0, \"y\":3, \"w\":2},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n                {\"x\":5, \"y\":3},\n                {\"x\":6, \"y\":3},\n                {\"x\":7, \"y\":3},\n                {\"x\":8, \"y\":3},\n                {\"x\":9, \"y\":3},\n                {\"x\":10, \"y\":3},\n                {\"x\":11, \"y\":3},\n                {\"x\":12, \"y\":3},\n                {\"x\":13, \"y\":3},\n                {\"x\":14, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4},\n                {\"x\":11, \"y\":4},\n                {\"x\":12, \"y\":4},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/64_ansi/layout.json",
    "content": "[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/64_ansi/readme.md",
    "content": "# 64_ansi\n\n    LAYOUT_64_ansi\n    ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n    │   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n    ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n    │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n    ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n    │      │   │   │   │   │   │   │   │   │   │   │   │        │\n    ├──────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬───┬───┤\n    │       │   │   │   │   │   │   │   │   │   │   │   │   │   │\n    ├────┬──┴─┬─┴──┬┴───┴───┴───┴───┴───┴───┼───┼───┼───┼───┼───┤\n    │    │    │    │                        │   │   │   │   │   │\n    └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n"
  },
  {
    "path": "layouts/default/64_iso/default_64_iso/keymap.c",
    "content": "// Copyright 2020 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├───┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬───┤\n     * │Sft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Sft│ ↑ │mo1│\n     * ├───┴┬──┴─┬─┴──┬┴───┴───┴───┴───┴───┴───┼───┼───┼───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │Alt│Ctl│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n     */ \n    [0] = LAYOUT_64_iso(\n        KC_ESC,  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_BSPC,\n        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,\n        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_NUHS,          KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   MO(1),\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│ Delete│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │     │   │   │   │   │   │   │   │   │   │   │PSn│SLk│     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n     * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n     * ├───┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬───┤\n     * │   │   │   │   │   │   │   │   │   │   │   │   │   │PgU│   │\n     * ├───┴┬──┴─┬─┴──┬┴───┴───┴───┴───┴───┴───┼───┼───┼───┼───┼───┤\n     * │    │    │    │                        │   │mo2│Hom│PgD│End│\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n     */ \n    [1] = LAYOUT_64_iso(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,           KC_DEL,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PSCR, KC_SCRL,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,          _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PGUP, _______,\n        _______, _______, _______,                            _______,                            _______, MO(2),   KC_HOME, KC_PGDN, KC_END\n    ),\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n     * │   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n     * │     │   │   │   │QBt│   │   │   │   │   │   │   │   │     │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n     * │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n     * ├───┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬───┤\n     * │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n     * ├───┴┬──┴─┬─┴──┬┴───┴───┴───┴───┴───┴───┼───┼───┼───┼───┼───┤\n     * │    │    │    │                        │   │   │   │   │   │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n     */ \n    [2] = LAYOUT_64_iso(\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,          _______,\n        _______, _______, _______, _______, QK_BOOT, _______, _______, _______, _______, _______, _______, _______, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,          _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                            _______, _______, _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/64_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"60% \\\"64-key\\\" ISO Layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_64_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n                {\"x\":5, \"y\":3},\n                {\"x\":6, \"y\":3},\n                {\"x\":7, \"y\":3},\n                {\"x\":8, \"y\":3},\n                {\"x\":9, \"y\":3},\n                {\"x\":10, \"y\":3},\n                {\"x\":11, \"y\":3},\n                {\"x\":12, \"y\":3},\n                {\"x\":13, \"y\":3},\n                {\"x\":14, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4},\n                {\"x\":11, \"y\":4},\n                {\"x\":12, \"y\":4},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/64_iso/layout.json",
    "content": "[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5,w2:1.25,h2:2,x2:0.25},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/64_iso/readme.md",
    "content": "# 64_iso\n\n    LAYOUT_64_iso\n    ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n    │   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n    ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n    │     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n    ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n    │      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n    ├───┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬───┤\n    │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n    ├───┴┬──┴─┬─┴──┬┴───┴───┴───┴───┴───┴───┼───┼───┼───┼───┼───┤\n    │    │    │    │                        │   │   │   │   │   │\n    └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n"
  },
  {
    "path": "layouts/default/65_ansi/default_65_ansi/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │PgD│\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │Alt│ Fn│Ctl│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_65_ansi(\n        KC_ESC,  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_BSPC, KC_HOME,\n        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_PGUP,\n        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_ENT,  KC_PGDN,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                    KC_RALT, MO(1),   KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│ Delete│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│Pause│   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │        │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n     * │    │    │    │                        │   │   │   │   │   │   │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n     */\n    [1] = LAYOUT_65_ansi(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL, KC_PAUS, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,          _______, _______,\n        _______,          _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                   _______, _______, _______, _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ANSI layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4},\n                {\"x\":11, \"y\":4},\n                {\"x\":12, \"y\":4},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_ansi/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_ansi/readme.md",
    "content": "# 65_ansi\n\n    LAYOUT_65_ansi\n\nThis is the 65% ANSI layout made popular by boards such as the [Input Club Whitefox](https://github.com/qmk/qmk_firmware/tree/master/keyboards/whitefox) and [RAMA M65-A](https://github.com/qmk/qmk_firmware/tree/master/keyboards/jc65).\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker/default_65_ansi_blocker/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │PgD│\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │ Alt│  Fn│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_65_ansi_blocker(\n        KC_ESC,  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_BSPC, KC_HOME,\n        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_PGUP,\n        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_ENT,  KC_PGDN,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                    KC_RALT, MO(1),            KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│ Delete│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│Pause│   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │        │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n     * │    │    │    │                        │    │    │ │   │   │   │\n     * └────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n     */\n    [1] = LAYOUT_65_ansi_blocker(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL, KC_PAUS, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,          _______, _______,\n        _______,          _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                   _______, _______,          _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ANSI layout with blocker\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_ansi_blocker\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.5},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker/readme.md",
    "content": "# 65_ansi_blocker\n\n    LAYOUT_65_ansi_blocker\n\nThis is the 65% ANSI layout made popular by boards such as the Percent Canoe.\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_split_bs/default_65_ansi_blocker_split_bs/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │PgD│\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │ Alt│  Fn│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_65_ansi_blocker_split_bs(\n        KC_ESC,  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_BSPC, KC_BSPC, KC_HOME,\n        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_PGUP,\n        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_ENT,  KC_PGDN,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                    KC_RALT, MO(1),            KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│Del│Del│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│Pause│   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │        │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n     * │    │    │    │                        │    │    │ │   │   │   │\n     * └────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n     */\n    [1] = LAYOUT_65_ansi_blocker_split_bs(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL, KC_PAUS, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,          _______, _______,\n        _______,          _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                   _______, _______,          _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_split_bs/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ANSI layout with blocker and split Backspace\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_ansi_blocker_split_bs\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_split_bs/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.5},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_split_bs/readme.md",
    "content": "# 65_ansi_blocker_split_bs\n\n    LAYOUT_65_ansi_blocker_split_bs\n\nThis is the 65% ANSI layout with a blocker next to the arrows and split Backspace.\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_tsangan/default_65_ansi_blocker_tsangan/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │PgD│\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n     * │Ctrl │GUI│Alt  │                           │   Fn│ │ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_65_ansi_blocker_tsangan(\n        KC_ESC,  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_BSPC, KC_HOME,\n        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_PGUP,\n        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_ENT,  KC_PGDN,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             MO(1),            KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│ Delete│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│Pause│   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │        │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n     * │     │   │     │                           │     │ │   │   │   │\n     * └─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n     */\n    [1] = LAYOUT_65_ansi_blocker_tsangan(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL, KC_PAUS, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,          _______, _______,\n        _______,          _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                            _______,          _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_tsangan/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ANSI Tsangan layout with blocker\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_ansi_blocker_tsangan\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":1.5, \"y\":4},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_tsangan/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:0.5},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_tsangan/readme.md",
    "content": "# 65_ansi_blocker_tsangan\n\n    LAYOUT_65_ansi_blocker_tsangan\n\nThis is a Tsangan-inspired 65% ANSI layout with a blocker next to the arrows and 1.5u-1u-1.5u-7u-1.5u bottom row.\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_tsangan_split_bs/default_65_ansi_blocker_tsangan_split_bs/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │PgD│\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n     * │Ctrl │GUI│Alt  │                           │   Fn│ │ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_65_ansi_blocker_tsangan_split_bs(\n        KC_ESC,  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_BSPC, KC_BSPC, KC_HOME,\n        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_PGUP,\n        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_ENT,  KC_PGDN,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             MO(1),            KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│Del│Del│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│Pause│   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │        │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n     * │     │   │     │                           │     │ │   │   │   │\n     * └─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n     */\n    [1] = LAYOUT_65_ansi_blocker_tsangan_split_bs(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL, KC_PAUS, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,          _______, _______,\n        _______,          _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                            _______,          _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_tsangan_split_bs/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ANSI Tsangan layout with blocker and split Backspace\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_ansi_blocker_tsangan_split_bs\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":1.5, \"y\":4},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_tsangan_split_bs/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:0.5},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_ansi_blocker_tsangan_split_bs/readme.md",
    "content": "# 65_ansi_blocker_tsangan_split_bs\n\n    LAYOUT_65_ansi_blocker_tsangan_split_bs\n\nThis is a Tsangan-inspired 65% ANSI layout with a blocker next to the arrows, a split Backspace, and 1.5u-1u-1.5u-7u-1.5u bottom row.\n\n"
  },
  {
    "path": "layouts/default/65_ansi_split_bs/default_65_ansi_split_bs/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = |Bsp|Bsp|Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │PgD│\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │Alt│ Fn│Ctl│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_65_ansi_split_bs(\n        KC_ESC,  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_BSPC, KC_BSPC, KC_HOME,\n        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_PGUP,\n        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_ENT,  KC_PGDN,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                    KC_RALT, MO(1),   KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│Del│Del│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│Pause│   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │        │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n     * │    │    │    │                        │   │   │   │   │   │   │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n     */\n    [1] = LAYOUT_65_ansi_split_bs(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL, KC_PAUS, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,          _______, _______,\n        _______,          _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                   _______, _______, _______, _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_ansi_split_bs/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ANSI layout with split Backspace\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_ansi_split_bs\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4},\n                {\"x\":11, \"y\":4},\n                {\"x\":12, \"y\":4},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_ansi_split_bs/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_ansi_split_bs/readme.md",
    "content": "# 65_ansi_split_bs\n\n    LAYOUT_65_ansi_split_bs\n"
  },
  {
    "path": "layouts/default/65_iso/default_65_iso/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent├───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │PgD│\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │Alt│ Fn│Ctl│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_65_iso(\n        KC_ESC,  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_BSPC, KC_HOME,\n        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_PGUP,\n        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_NUHS, KC_ENT,  KC_PGDN,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                    KC_RALT, MO(1),   KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│ Delete│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│     │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │Pau│    │   │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │    │   │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n     * │    │    │    │                        │   │   │   │   │   │   │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n     */\n    [1] = LAYOUT_65_iso(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL,          _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PAUS, _______, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                   _______, _______, _______, _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ISO layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4},\n                {\"x\":11, \"y\":4},\n                {\"x\":12, \"y\":4},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_iso/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_iso/readme.md",
    "content": "# 65_iso\n\n    LAYOUT_65_iso\n\nThis is the 65% layout made popular by boards such as the [Input Club Whitefox](https://github.com/qmk/qmk_firmware/tree/master/keyboards/whitefox) and [RAMA M65-A](https://github.com/qmk/qmk_firmware/tree/master/keyboards/jc65), in ISO.\n"
  },
  {
    "path": "layouts/default/65_iso_blocker/default_65_iso_blocker/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent├───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │PgD│\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │ Alt│  Fn│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_65_iso_blocker(\n        KC_ESC,  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_BSPC, KC_HOME,\n        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_PGUP,\n        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_NUHS, KC_ENT,  KC_PGDN,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                    KC_RALT, MO(1),            KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│ Delete│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│     │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │Pau│    │   │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │    │   │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n     * │    │    │    │                        │    │    │ │   │   │   │\n     * └────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n     */\n    [1] = LAYOUT_65_iso_blocker(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL,          _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PAUS, _______, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                   _______, _______,          _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_iso_blocker/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ISO layout with blocker\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_iso_blocker\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_iso_blocker/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.5},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_iso_blocker/readme.md",
    "content": "# 65_iso_blocker\n\n    LAYOUT_65_iso_blocker\n\nThis is the 65% ISO layout made popular by boards such as the Percent Canoe.\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_split_bs/default_65_iso_blocker_split_bs/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent├───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │PgD│\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │ Alt│  Fn│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_65_iso_blocker_split_bs(\n        KC_ESC,  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_BSPC, KC_BSPC, KC_HOME,\n        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_PGUP,\n        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_NUHS, KC_ENT,  KC_PGDN,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                    KC_RALT, MO(1),            KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│Del│Del│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│     │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │Pau│    │   │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │    │   │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n     * │    │    │    │                        │    │    │ │   │   │   │\n     * └────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n     */\n    [1] = LAYOUT_65_iso_blocker_split_bs(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL,          _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PAUS, _______, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                   _______, _______,          _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_split_bs/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ISO layout with blocker and split Backspace\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_iso_blocker_split_bs\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_split_bs/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.5},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_split_bs/readme.md",
    "content": "# 65_iso_blocker_split_bs\n\n    LAYOUT_65_iso_blocker_split_bs\n\nThis is the 65% ISO layout with a blocker next to the arrows and split Backspace.\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_tsangan/default_65_iso_blocker_tsangan/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent├───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │PgD│\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n     * │Ctrl │GUI│Alt  │                           │   Fn│ │ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_65_iso_blocker_tsangan(\n        KC_ESC,  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_BSPC, KC_HOME,\n        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_PGUP,\n        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_NUHS, KC_ENT,  KC_PGDN,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             MO(1),            KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│ Delete│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│     │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │Pau│    │   │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │    │   │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n     * │     │   │     │                           │     │ │   │   │   │\n     * └─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n     */\n    [1] = LAYOUT_65_iso_blocker_tsangan(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL,          _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PAUS, _______, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                            _______,          _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_tsangan/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ISO Tsangan layout with blocker\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_iso_blocker_tsangan\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":1.5, \"y\":4},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_tsangan/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:0.5},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_tsangan/readme.md",
    "content": "# 65_iso_blocker_tsangan\n\n    LAYOUT_65_iso_blocker_tsangan\n\nThis is a Tsangan-inspired 65% ISO layout with a blocker next to the arrows and 1.5u-1u-1.5u-7u-1.5u bottom row.\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_tsangan_split_bs/default_65_iso_blocker_tsangan_split_bs/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent├───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │PgD│\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n     * │Ctrl │GUI│Alt  │                           │   Fn│ │ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_65_iso_blocker_tsangan_split_bs(\n        KC_ESC,  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_BSPC, KC_BSPC, KC_HOME,\n        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_PGUP,\n        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_NUHS, KC_ENT,  KC_PGDN,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             MO(1),            KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│Del│Del│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│     │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │Pau│    │   │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │    │   │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n     * │     │   │     │                           │     │ │   │   │   │\n     * └─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n     */\n    [1] = LAYOUT_65_iso_blocker_tsangan_split_bs(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL,          _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PAUS, _______, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                            _______,          _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_tsangan_split_bs/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ISO Tsangan layout with blocker and split Backspace\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_iso_blocker_tsangan_split_bs\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.5},\n                {\"x\":1.5, \"y\":4},\n                {\"x\":2.5, \"y\":4, \"w\":1.5},\n                {\"x\":4, \"y\":4, \"w\":7},\n                {\"x\":11, \"y\":4, \"w\":1.5},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_tsangan_split_bs/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:0.5},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_iso_blocker_tsangan_split_bs/readme.md",
    "content": "# 65_iso_blocker_tsangan_split_bs\n\n    LAYOUT_65_iso_blocker_tsangan_split_bs\n\nThis is a Tsangan-inspired 65% ISO layout with a blocker next to the arrows, a split Backspace, and 1.5u-1u-1.5u-7u-1.5u bottom row.\n"
  },
  {
    "path": "layouts/default/65_iso_split_bs/default_65_iso_split_bs/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent├───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │PgD│\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │Alt│ Fn│Ctl│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_65_iso_split_bs(\n        KC_ESC,  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_BSPC, KC_BSPC, KC_HOME,\n        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_PGUP,\n        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_NUHS, KC_ENT,  KC_PGDN,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                    KC_RALT, MO(1),   KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│Del│Del│   │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n     * │     │   │   │   │   │   │Ins│   │   │   │   │PSc│Scr│     │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n     * │      │   │   │   │   │   │   │   │   │   │   │   │Pau│    │   │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │    │   │   │   │   │   │   │   │Mut│Vl-│Vl+│   │      │   │   │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n     * │    │    │    │                        │   │   │   │   │   │   │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n     */\n    [1] = LAYOUT_65_iso_split_bs(\n        KC_GRV,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_DEL,  KC_DEL,  _______,\n        _______, _______, _______, _______, _______, _______, KC_INS,  _______, _______, _______, _______, KC_PSCR, KC_SCRL,          _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_PAUS, _______, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, _______, _______,\n        _______, _______, _______,                            _______,                   _______, _______, _______, _______, _______, _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/65_iso_split_bs/info.json",
    "content": "{\n    \"keyboard_name\": \"65% ISO layout with split Backspace\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_65_iso_split_bs\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":1.75},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4},\n                {\"x\":11, \"y\":4},\n                {\"x\":12, \"y\":4},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/65_iso_split_bs/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/65_iso_split_bs/readme.md",
    "content": "# 65_iso_split_bs\n\n    LAYOUT_65_iso_split_bs\n"
  },
  {
    "path": "layouts/default/66_ansi/default_66_ansi/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┬─┴─┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift  │ ↑ │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┼────┬───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│GUI│ Ctl│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴───┴────┴───┴───┴───┘\n     */\n    [0] = LAYOUT_66_ansi(\n        KC_ESC,  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_BSPC, KC_PGUP,\n        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_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT, KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/66_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"66% ANSI layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_66_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n                {\"x\":15.5, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n                {\"x\":15.5, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.25},\n                {\"x\":14.5, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4},\n                {\"x\":12.25, \"y\":4, \"w\":1.25},\n                {\"x\":13.5, \"y\":4},\n                {\"x\":14.5, \"y\":4},\n                {\"x\":15.5, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/66_ansi/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.5},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",\"\",{w:1.25},\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/66_ansi/readme.md",
    "content": "# 66_ansi\n\n    LAYOUT_66_ansi\n"
  },
  {
    "path": "layouts/default/66_iso/default_66_iso/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┬─┴─┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift  │ ↑ │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┼────┬───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│GUI│ Ctl│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴───┴────┴───┴───┴───┘\n     */\n    [0] = LAYOUT_66_iso(\n        KC_ESC,  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_BSPC, KC_PGUP,\n        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_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT, KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/66_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"66% ISO layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_66_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n                {\"x\":15.5, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":15.5, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.25},\n                {\"x\":14.5, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4},\n                {\"x\":12.25, \"y\":4, \"w\":1.25},\n                {\"x\":13.5, \"y\":4},\n                {\"x\":14.5, \"y\":4},\n                {\"x\":15.5, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/66_iso/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.5},\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.5},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",\"\",{w:1.25},\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/66_iso/readme.md",
    "content": "# 66_iso\n\n    LAYOUT_66_iso\n"
  },
  {
    "path": "layouts/default/68_ansi/default_68_ansi/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐┌───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp││Ins│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤├───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  ││Del│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤└───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift    ││ ↑ │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬─┬──┴┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Ctrl│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_68_ansi(\n        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_BSPC, KC_INS,  KC_PGUP,\n        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_DEL,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT, KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/68_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"68-key ANSI layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_68_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n                {\"x\":15.25, \"y\":1},\n                {\"x\":16.25, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.75},\n                {\"x\":15.25, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":12.5, \"y\":4, \"w\":1.25},\n                {\"x\":14.25, \"y\":4},\n                {\"x\":15.25, \"y\":4},\n                {\"x\":16.25, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/68_ansi/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:0.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.5},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/68_ansi/readme.md",
    "content": "# 68_ansi\n\n    LAYOUT_68_ansi\n\nThis is the 68 key ANSI layout made popular by boards such as the Magicforce 68 and Varmilo VA68M.\n"
  },
  {
    "path": "layouts/default/68_iso/default_68_iso/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐┌───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp││Ins│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤├───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     ││Del│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│└───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift    ││ ↑ │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬─┬──┴┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Ctrl│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_68_iso(\n        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_BSPC, KC_INS,  KC_PGUP,\n        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_DEL,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT, KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/68_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"68-key ISO layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_68_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":15.25, \"y\":1},\n                {\"x\":16.25, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.75},\n                {\"x\":15.25, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":12.5, \"y\":4, \"w\":1.25},\n                {\"x\":14.25, \"y\":4},\n                {\"x\":15.25, \"y\":4},\n                {\"x\":16.25, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/68_iso/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:0.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.5},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/68_iso/readme.md",
    "content": "# 68_iso\n\n    LAYOUT_68_iso\n\nThis is the 68 (actually 69) key ISO layout made popular by boards such as the Magicforce 69-key and Varmilo VA69M.\n"
  },
  {
    "path": "layouts/default/75_ansi/default_75_ansi/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n     /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│PSc│Pse│Del│\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┴───┼───┤\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │PgD│\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │Alt│GUI│Ctl│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_75_ansi(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_PSCR, KC_PAUS, KC_DEL,\n        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_BSPC,          KC_HOME,\n        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_PGUP,\n        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_ENT,           KC_PGDN,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/75_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"75% ANSI layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_75_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n                {\"x\":12, \"y\":1},\n                {\"x\":13, \"y\":1, \"w\":2},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.5},\n                {\"x\":1.5, \"y\":2},\n                {\"x\":2.5, \"y\":2},\n                {\"x\":3.5, \"y\":2},\n                {\"x\":4.5, \"y\":2},\n                {\"x\":5.5, \"y\":2},\n                {\"x\":6.5, \"y\":2},\n                {\"x\":7.5, \"y\":2},\n                {\"x\":8.5, \"y\":2},\n                {\"x\":9.5, \"y\":2},\n                {\"x\":10.5, \"y\":2},\n                {\"x\":11.5, \"y\":2},\n                {\"x\":12.5, \"y\":2},\n                {\"x\":13.5, \"y\":2, \"w\":1.5},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.75},\n                {\"x\":1.75, \"y\":3},\n                {\"x\":2.75, \"y\":3},\n                {\"x\":3.75, \"y\":3},\n                {\"x\":4.75, \"y\":3},\n                {\"x\":5.75, \"y\":3},\n                {\"x\":6.75, \"y\":3},\n                {\"x\":7.75, \"y\":3},\n                {\"x\":8.75, \"y\":3},\n                {\"x\":9.75, \"y\":3},\n                {\"x\":10.75, \"y\":3},\n                {\"x\":11.75, \"y\":3},\n                {\"x\":12.75, \"y\":3, \"w\":2.25},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":2.25},\n                {\"x\":2.25, \"y\":4},\n                {\"x\":3.25, \"y\":4},\n                {\"x\":4.25, \"y\":4},\n                {\"x\":5.25, \"y\":4},\n                {\"x\":6.25, \"y\":4},\n                {\"x\":7.25, \"y\":4},\n                {\"x\":8.25, \"y\":4},\n                {\"x\":9.25, \"y\":4},\n                {\"x\":10.25, \"y\":4},\n                {\"x\":11.25, \"y\":4},\n                {\"x\":12.25, \"y\":4, \"w\":1.75},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4},\n\n                {\"x\":0, \"y\":5, \"w\":1.25},\n                {\"x\":1.25, \"y\":5, \"w\":1.25},\n                {\"x\":2.5, \"y\":5, \"w\":1.25},\n                {\"x\":3.75, \"y\":5, \"w\":6.25},\n                {\"x\":10, \"y\":5},\n                {\"x\":11, \"y\":5},\n                {\"x\":12, \"y\":5},\n                {\"x\":13, \"y\":5},\n                {\"x\":14, \"y\":5},\n                {\"x\":15, \"y\":5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/75_ansi/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/75_ansi/readme.md",
    "content": "# 75_ansi\n\n    LAYOUT_75_ansi\n"
  },
  {
    "path": "layouts/default/75_iso/default_75_iso/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n     /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│PSc│Pse│Del│\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┴───┼───┤\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│Hom│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │PgU│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent├───┤\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │PgD│\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│ ↑ │End│\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n     * │Ctrl│GUI │Alt │                        │Alt│GUI│Ctl│ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_75_iso(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_PSCR, KC_PAUS, KC_DEL,\n        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_BSPC,          KC_HOME,\n        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_PGUP,\n        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_NUHS, KC_ENT,           KC_PGDN,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT, KC_UP,   KC_END,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/75_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"75% ISO layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_75_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n                {\"x\":12, \"y\":1},\n                {\"x\":13, \"y\":1, \"w\":2},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.5},\n                {\"x\":1.5, \"y\":2},\n                {\"x\":2.5, \"y\":2},\n                {\"x\":3.5, \"y\":2},\n                {\"x\":4.5, \"y\":2},\n                {\"x\":5.5, \"y\":2},\n                {\"x\":6.5, \"y\":2},\n                {\"x\":7.5, \"y\":2},\n                {\"x\":8.5, \"y\":2},\n                {\"x\":9.5, \"y\":2},\n                {\"x\":10.5, \"y\":2},\n                {\"x\":11.5, \"y\":2},\n                {\"x\":12.5, \"y\":2},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.75},\n                {\"x\":1.75, \"y\":3},\n                {\"x\":2.75, \"y\":3},\n                {\"x\":3.75, \"y\":3},\n                {\"x\":4.75, \"y\":3},\n                {\"x\":5.75, \"y\":3},\n                {\"x\":6.75, \"y\":3},\n                {\"x\":7.75, \"y\":3},\n                {\"x\":8.75, \"y\":3},\n                {\"x\":9.75, \"y\":3},\n                {\"x\":10.75, \"y\":3},\n                {\"x\":11.75, \"y\":3},\n                {\"x\":12.75, \"y\":3},\n                {\"x\":13.75, \"y\":2, \"w\":1.25, \"h\":2},\n                {\"x\":15, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4},\n                {\"x\":2.25, \"y\":4},\n                {\"x\":3.25, \"y\":4},\n                {\"x\":4.25, \"y\":4},\n                {\"x\":5.25, \"y\":4},\n                {\"x\":6.25, \"y\":4},\n                {\"x\":7.25, \"y\":4},\n                {\"x\":8.25, \"y\":4},\n                {\"x\":9.25, \"y\":4},\n                {\"x\":10.25, \"y\":4},\n                {\"x\":11.25, \"y\":4},\n                {\"x\":12.25, \"y\":4, \"w\":1.75},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4},\n\n                {\"x\":0, \"y\":5, \"w\":1.25},\n                {\"x\":1.25, \"y\":5, \"w\":1.25},\n                {\"x\":2.5, \"y\":5, \"w\":1.25},\n                {\"x\":3.75, \"y\":5, \"w\":6.25},\n                {\"x\":10, \"y\":5},\n                {\"x\":11, \"y\":5},\n                {\"x\":12, \"y\":5},\n                {\"x\":13, \"y\":5},\n                {\"x\":14, \"y\":5},\n                {\"x\":15, \"y\":5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/75_iso/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/75_iso/readme.md",
    "content": "# 75_iso\n\n    LAYOUT_75_iso\n"
  },
  {
    "path": "layouts/default/96_ansi/default_96_ansi/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│PSc│Scr│Pse│Vo-│Vo+│Mut│\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┴───┼───┼───┼───┼───┤\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│Num│ / │ * │ - │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┼───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ 7 │ 8 │ 9 │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┼───┼───┤ + │\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │ 4 │ 5 │ 6 │   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┼───┼───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Shift │ ↑ │ 1 │ 2 │ 3 │   │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┼───┼───┤Ent│\n     * │Ctrl│GUI │Alt │         Space          │Alt│GUI│Ctl│ ← │ ↓ │ → │ 0 │ . │   │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_96_ansi(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_PSCR, KC_SCRL, KC_PAUS, KC_VOLD, KC_VOLU, KC_MUTE,\n        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_BSPC,          KC_NUM,  KC_PSLS, KC_PAST, KC_PMNS,\n        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_P7,   KC_P8,   KC_P9,   KC_PPLS,\n        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_ENT,           KC_P4,   KC_P5,   KC_P6,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT, KC_UP,   KC_P1,   KC_P2,   KC_P3,   KC_PENT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0,   KC_PDOT\n    )\n};\n"
  },
  {
    "path": "layouts/default/96_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"96% ANSI layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_96_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15, \"y\":0},\n                {\"x\":16, \"y\":0},\n                {\"x\":17, \"y\":0},\n                {\"x\":18, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n                {\"x\":12, \"y\":1},\n                {\"x\":13, \"y\":1, \"w\":2},\n                {\"x\":15, \"y\":1},\n                {\"x\":16, \"y\":1},\n                {\"x\":17, \"y\":1},\n                {\"x\":18, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.5},\n                {\"x\":1.5, \"y\":2},\n                {\"x\":2.5, \"y\":2},\n                {\"x\":3.5, \"y\":2},\n                {\"x\":4.5, \"y\":2},\n                {\"x\":5.5, \"y\":2},\n                {\"x\":6.5, \"y\":2},\n                {\"x\":7.5, \"y\":2},\n                {\"x\":8.5, \"y\":2},\n                {\"x\":9.5, \"y\":2},\n                {\"x\":10.5, \"y\":2},\n                {\"x\":11.5, \"y\":2},\n                {\"x\":12.5, \"y\":2},\n                {\"x\":13.5, \"y\":2, \"w\":1.5},\n                {\"x\":15, \"y\":2},\n                {\"x\":16, \"y\":2},\n                {\"x\":17, \"y\":2},\n                {\"x\":18, \"y\":2, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.75},\n                {\"x\":1.75, \"y\":3},\n                {\"x\":2.75, \"y\":3},\n                {\"x\":3.75, \"y\":3},\n                {\"x\":4.75, \"y\":3},\n                {\"x\":5.75, \"y\":3},\n                {\"x\":6.75, \"y\":3},\n                {\"x\":7.75, \"y\":3},\n                {\"x\":8.75, \"y\":3},\n                {\"x\":9.75, \"y\":3},\n                {\"x\":10.75, \"y\":3},\n                {\"x\":11.75, \"y\":3},\n                {\"x\":12.75, \"y\":3, \"w\":2.25},\n                {\"x\":15, \"y\":3},\n                {\"x\":16, \"y\":3},\n                {\"x\":17, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":2.25},\n                {\"x\":2.25, \"y\":4},\n                {\"x\":3.25, \"y\":4},\n                {\"x\":4.25, \"y\":4},\n                {\"x\":5.25, \"y\":4},\n                {\"x\":6.25, \"y\":4},\n                {\"x\":7.25, \"y\":4},\n                {\"x\":8.25, \"y\":4},\n                {\"x\":9.25, \"y\":4},\n                {\"x\":10.25, \"y\":4},\n                {\"x\":11.25, \"y\":4},\n                {\"x\":12.25, \"y\":4, \"w\":1.75},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4},\n                {\"x\":16, \"y\":4},\n                {\"x\":17, \"y\":4},\n\n                {\"x\":18, \"y\":4, \"h\":2},\n                {\"x\":0, \"y\":5, \"w\":1.25},\n                {\"x\":1.25, \"y\":5, \"w\":1.25},\n                {\"x\":2.5, \"y\":5, \"w\":1.25},\n                {\"x\":3.75, \"y\":5, \"w\":6.25},\n                {\"x\":10, \"y\":5},\n                {\"x\":11, \"y\":5},\n                {\"x\":12, \"y\":5},\n                {\"x\":13, \"y\":5},\n                {\"x\":14, \"y\":5},\n                {\"x\":15, \"y\":5},\n                {\"x\":16, \"y\":5},\n                {\"x\":17, \"y\":5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/96_ansi/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",\"\",\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/96_ansi/readme.md",
    "content": "# 96_ansi\n\n    LAYOUT_96_ansi\n"
  },
  {
    "path": "layouts/default/96_iso/default_96_iso/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│F1 │F2 │F3 │F4 │F5 │F6 │F7 │F8 │F9 │F10│F11│F12│PSc│Scr│Pse│Vo-│Vo+│Mut│\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┴───┼───┼───┼───┼───┤\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│Num│ / │ * │ - │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┼───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ 7 │ 8 │ 9 │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent├───┼───┼───┤ + │\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │ 4 │ 5 │ 6 │   │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┼───┼───┼───┤\n     * │Sft │ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Shift │ ↑ │ 1 │ 2 │ 3 │   │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┼───┼───┤Ent│\n     * │Ctrl│GUI │Alt │         Space          │Alt│GUI│Ctl│ ← │ ↓ │ → │ 0 │ . │   │\n     * └────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_96_iso(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_PSCR, KC_SCRL, KC_PAUS, KC_VOLD, KC_VOLU, KC_MUTE,\n        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_BSPC,          KC_NUM,  KC_PSLS, KC_PAST, KC_PMNS,\n        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_P7,   KC_P8,   KC_P9,   KC_PPLS,\n        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_NUHS, KC_ENT,           KC_P4,   KC_P5,   KC_P6,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT, KC_UP,   KC_P1,   KC_P2,   KC_P3,   KC_PENT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT, KC_P0,   KC_PDOT\n    )\n};\n"
  },
  {
    "path": "layouts/default/96_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"96% ISO layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_96_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15, \"y\":0},\n                {\"x\":16, \"y\":0},\n                {\"x\":17, \"y\":0},\n                {\"x\":18, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n                {\"x\":12, \"y\":1},\n                {\"x\":13, \"y\":1, \"w\":2},\n                {\"x\":15, \"y\":1},\n                {\"x\":16, \"y\":1},\n                {\"x\":17, \"y\":1},\n                {\"x\":18, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.5},\n                {\"x\":1.5, \"y\":2},\n                {\"x\":2.5, \"y\":2},\n                {\"x\":3.5, \"y\":2},\n                {\"x\":4.5, \"y\":2},\n                {\"x\":5.5, \"y\":2},\n                {\"x\":6.5, \"y\":2},\n                {\"x\":7.5, \"y\":2},\n                {\"x\":8.5, \"y\":2},\n                {\"x\":9.5, \"y\":2},\n                {\"x\":10.5, \"y\":2},\n                {\"x\":11.5, \"y\":2},\n                {\"x\":12.5, \"y\":2},\n                {\"x\":15, \"y\":2},\n                {\"x\":16, \"y\":2},\n                {\"x\":17, \"y\":2},\n                {\"x\":18, \"y\":2, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.75},\n                {\"x\":1.75, \"y\":3},\n                {\"x\":2.75, \"y\":3},\n                {\"x\":3.75, \"y\":3},\n                {\"x\":4.75, \"y\":3},\n                {\"x\":5.75, \"y\":3},\n                {\"x\":6.75, \"y\":3},\n                {\"x\":7.75, \"y\":3},\n                {\"x\":8.75, \"y\":3},\n                {\"x\":9.75, \"y\":3},\n                {\"x\":10.75, \"y\":3},\n                {\"x\":11.75, \"y\":3},\n                {\"x\":12.75, \"y\":3},\n                {\"x\":13.75, \"y\":2, \"w\":1.25, \"h\":2},\n                {\"x\":15, \"y\":3},\n                {\"x\":16, \"y\":3},\n                {\"x\":17, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4},\n                {\"x\":2.25, \"y\":4},\n                {\"x\":3.25, \"y\":4},\n                {\"x\":4.25, \"y\":4},\n                {\"x\":5.25, \"y\":4},\n                {\"x\":6.25, \"y\":4},\n                {\"x\":7.25, \"y\":4},\n                {\"x\":8.25, \"y\":4},\n                {\"x\":9.25, \"y\":4},\n                {\"x\":10.25, \"y\":4},\n                {\"x\":11.25, \"y\":4},\n                {\"x\":12.25, \"y\":4, \"w\":1.75},\n                {\"x\":14, \"y\":4},\n                {\"x\":15, \"y\":4},\n                {\"x\":16, \"y\":4},\n                {\"x\":17, \"y\":4},\n                {\"x\":18, \"y\":4, \"h\":2},\n\n                {\"x\":0, \"y\":5, \"w\":1.25},\n                {\"x\":1.25, \"y\":5, \"w\":1.25},\n                {\"x\":2.5, \"y\":5, \"w\":1.25},\n                {\"x\":3.75, \"y\":5, \"w\":6.25},\n                {\"x\":10, \"y\":5},\n                {\"x\":11, \"y\":5},\n                {\"x\":12, \"y\":5},\n                {\"x\":13, \"y\":5},\n                {\"x\":14, \"y\":5},\n                {\"x\":15, \"y\":5},\n                {\"x\":16, \"y\":5},\n                {\"x\":17, \"y\":5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/96_iso/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:1.25},\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/96_iso/readme.md",
    "content": "# 96_iso\n\n    LAYOUT_96_iso\n"
  },
  {
    "path": "layouts/default/alice/default_alice/keymap.c",
    "content": "// Copyright 2020 QMK / MudkipMao\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     *    ┌───┐  ┌───┬───┬───┬───┬───┬───┬───┐         ┌───┬───┬───┬───┬───┬───┬───────┐\n     *    │Esc│  │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │         │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│\n     *   ┌┴──┬┘ ┌┴───┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┘       ┌─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┴┐\n     *   │PUp│  │ Tab │ Q │ W │ E │ R │ T │          │ Y │ U │ I │ O │ P │ [ │ ] │   \\  │\n     *  ┌┴──┬┘ ┌┴─────┼───┼───┼───┼───┼───┤          └┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴──────┴┐\n     *  │PDn│  │ Caps │ A │ S │ D │ F │ G │           │ H │ J │ K │ L │ ; │ ' │   Enter  │\n     *  └───┘ ┌┴──────┴┬──┴┬──┴┬──┴┬──┴┬──┴┐        ┌─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───────┬──┴┐\n     *        │ Shift  │ Z │ X │ C │ V │ B │        │ B │ N │ M │ , │ . │ / │  Shift  │Shf│\n     *        ├─────┬──┴──┬┴───┴┬──┴───┴┬──┴──┐     ├───┴───┴──┬┴───┴┬──┴───┴──────┬──┴──┬┘\n     *        │ Ctl │     │ Alt │       │ GUI │     │          │ Alt │             │ Ctl │\n     *        └─────┘     └─────┴───────┴─────┘     └──────────┴─────┘             └─────┘\n     */\n    [0] = LAYOUT_alice(\n        KC_ESC,  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_BSPC,\n        KC_PGUP, 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,\n        KC_PGDN, 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_ENT,\n                 KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,                      KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,\n                 KC_LCTL,          KC_LALT,          KC_SPC,  KC_LGUI,                   KC_SPC,           KC_RALT,                            KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/alice/info.json",
    "content": "{\n    \"keyboard_name\": \"Alice layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_alice\": {\n            \"layout\": [\n                {\"x\":0.4, \"y\":0},\n                {\"x\":1.55, \"y\":0.1},\n                {\"x\":2.55, \"y\":0.1},\n                {\"x\":3.55, \"y\":0},\n                {\"x\":4.55, \"y\":0.1},\n                {\"x\":5.55, \"y\":0.1},\n                {\"x\":6.55, \"y\":0.1},\n                {\"x\":7.55, \"y\":0.1},\n                {\"x\":9.75, \"y\":0.1},\n                {\"x\":10.75, \"y\":0.1},\n                {\"x\":11.75, \"y\":0.1},\n                {\"x\":12.75, \"y\":0.1},\n                {\"x\":13.75, \"y\":0},\n                {\"x\":14.75, \"y\":0.1},\n                {\"x\":15.75, \"y\":0.1, \"w\":2},\n\n                {\"x\":0.2, \"y\":1.0},\n                {\"x\":1.35, \"y\":1.1, \"w\":1.5},\n                {\"x\":2.85, \"y\":1.1},\n                {\"x\":3.85, \"y\":1.1},\n                {\"x\":4.85, \"y\":1.1},\n                {\"x\":5.85, \"y\":1.1},\n                {\"x\":6.85, \"y\":1.1},\n                {\"x\":9.45, \"y\":1.1},\n                {\"x\":10.45, \"y\":1.1},\n                {\"x\":11.45, \"y\":1.1},\n                {\"x\":12.45, \"y\":1.1},\n                {\"x\":13.45, \"y\":1.1},\n                {\"x\":14.45, \"y\":1.1},\n                {\"x\":15.45, \"y\":1.1},\n                {\"x\":16.45, \"y\":1.1, \"w\":1.5},\n\n                {\"x\":0, \"y\":2.0},\n                {\"x\":1.15, \"y\":2.1, \"w\":1.75},\n                {\"x\":2.9, \"y\":2.1},\n                {\"x\":3.9, \"y\":2.1},\n                {\"x\":4.9, \"y\":2.1},\n                {\"x\":5.9, \"y\":2.1},\n                {\"x\":6.9, \"y\":2.1},\n                {\"x\":9.9, \"y\":2.1},\n                {\"x\":10.9, \"y\":2.1},\n                {\"x\":11.9, \"y\":2.1},\n                {\"x\":12.9, \"y\":2.1},\n                {\"x\":13.9, \"y\":2.1},\n                {\"x\":14.9, \"y\":2.1},\n                {\"x\":15.9, \"y\":2.1, \"w\":2.25},\n\n                {\"x\":0.95, \"y\":3.1, \"w\":2.25},\n                {\"x\":3.2, \"y\":3.1},\n                {\"x\":4.2, \"y\":3.1},\n                {\"x\":5.2, \"y\":3.1},\n                {\"x\":6.2, \"y\":3.1},\n                {\"x\":7.2, \"y\":3.1},\n                {\"x\":9.6, \"y\":3.1},\n                {\"x\":10.6, \"y\":3.1},\n                {\"x\":11.6, \"y\":3.1},\n                {\"x\":12.6, \"y\":3.1},\n                {\"x\":13.6, \"y\":3.1},\n                {\"x\":14.6, \"y\":3.1},\n                {\"x\":15.6, \"y\":3.1, \"w\":1.75},\n                {\"x\":17.35, \"y\":3.1},\n\n                {\"x\":0.95, \"y\":4.1, \"w\":1.5},\n                {\"x\":3.85, \"y\":4.1, \"w\":1.5},\n                {\"x\":5.35, \"y\":4.1, \"w\":2},\n                {\"x\":7.35, \"y\":4.1, \"w\":1.25},\n                {\"x\":9.6, \"y\":4.1, \"w\":2.75},\n                {\"x\":12.35, \"y\":4.1, \"w\":1.5},\n                {\"x\":16.65, \"y\":4.1, \"w\":1.5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/alice/layout.json",
    "content": "[{y:0.9,x:0.55,a:7},\"\",{x:2.15},\"\",{x:8.55},\"\"],\n[{y:-0.9,x:1.7},\"\",\"\",{x:10.55},\"\",{w:2},\"\"],\n[{y:-0.1,x:0.35},\"\"],\n[{y:-0.95,x:12.95},\"\"],\n[{y:-0.95,x:1.5,w:1.5},\"\",\"\",{x:9.95},\"\",\"\",{w:1.5},\"\"],\n[{y:-0.1,x:0.15},\"\"],\n[{y:-0.9,x:1.3,w:1.75},\"\",\"\",{x:9.35},\"\",{x:0},\"\",{w:2.25},\"\"],\n[{x:1.1,w:2.25},\"\",\"\",{x:8.75},\"\",\"\",{w:1.75},\"\",\"\"],\n[{x:1.1,w:1.5},\"\",{x:13.55,w:1.5},\"\"],\n[{r:12,y:-6,x:5},\"\",\"\",\"\",\"\"],\n[{x:4.5},\"\",\"\",\"\",\"\"],\n[{x:4.8},\"\",\"\",\"\",\"\"],\n[{x:5.3},\"\",\"\",\"\",\"\"],\n[{x:6.45,w:2},\"\",{w:1.25},\"\"],\n[{y:-0.95,x:4.95,w:1.5},\"\"],\n[{r:-12,y:-1.45,x:8.55},\"\",\"\",\"\",\"\"],\n[{x:8.05},\"\",\"\",\"\",\"\"],\n[{x:8.2},\"\",\"\",\"\",\"\"],\n[{x:7.75},\"\",\"\",\"\",\"\"],\n[{x:7.75,w:2.75},\"\"],\n[{y:-0.95,x:10.5,w:1.5},\"\"]\n"
  },
  {
    "path": "layouts/default/alice/readme.md",
    "content": "# alice\n\n    LAYOUT_alice\n"
  },
  {
    "path": "layouts/default/alice_split_bs/default_alice_split_bs/keymap.c",
    "content": "// Copyright 2020 QMK / MudkipMao, James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     *    ┌───┐  ┌───┬───┬───┬───┬───┬───┬───┐         ┌───┬───┬───┬───┬───┬───┬───┬───┐\n     *    │Esc│  │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │         │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Del│\n     *   ┌┴──┬┘ ┌┴───┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┘       ┌─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┐\n     *   │PUp│  │ Tab │ Q │ W │ E │ R │ T │          │ Y │ U │ I │ O │ P │ [ │ ] │   \\  │\n     *  ┌┴──┬┘ ┌┴─────┼───┼───┼───┼───┼───┤          └┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴──────┴┐\n     *  │PDn│  │ Caps │ A │ S │ D │ F │ G │           │ H │ J │ K │ L │ ; │ ' │   Enter  │\n     *  └───┘ ┌┴──────┴┬──┴┬──┴┬──┴┬──┴┬──┴┐        ┌─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───────┬──┴┐\n     *        │ Shift  │ Z │ X │ C │ V │ B │        │ B │ N │ M │ , │ . │ / │  Shift  │Shf│\n     *        ├─────┬──┴──┬┴───┴┬──┴───┴┬──┴──┐     ├───┴───┴──┬┴───┴┬──┴───┴──────┬──┴──┬┘\n     *        │ Ctl │     │ Alt │       │ GUI │     │          │ Alt │             │ Ctl │\n     *        └─────┘     └─────┴───────┴─────┘     └──────────┴─────┘             └─────┘\n     */\n    [0] = LAYOUT_alice_split_bs(\n        KC_ESC,  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_BSPC, KC_DEL,\n        KC_PGUP, 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,\n        KC_PGDN, 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_ENT,\n                 KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,                      KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,\n                 KC_LCTL,          KC_LALT,          KC_SPC,  KC_LGUI,                   KC_SPC,           KC_RALT,                            KC_RCTL\n    )\n};\n"
  },
  {
    "path": "layouts/default/alice_split_bs/info.json",
    "content": "{\n    \"keyboard_name\": \"Alice layout with Split Backspace\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_alice_split_bs\": {\n            \"layout\": [\n                {\"x\":0.4, \"y\":0},\n                {\"x\":1.55, \"y\":0.1},\n                {\"x\":2.55, \"y\":0.1},\n                {\"x\":3.55, \"y\":0},\n                {\"x\":4.55, \"y\":0.1},\n                {\"x\":5.55, \"y\":0.1},\n                {\"x\":6.55, \"y\":0.1},\n                {\"x\":7.55, \"y\":0.1},\n                {\"x\":9.75, \"y\":0.1},\n                {\"x\":10.75, \"y\":0.1},\n                {\"x\":11.75, \"y\":0.1},\n                {\"x\":12.75, \"y\":0.1},\n                {\"x\":13.75, \"y\":0},\n                {\"x\":14.75, \"y\":0.1},\n                {\"x\":15.75, \"y\":0.1},\n                {\"x\":16.75, \"y\":0.1},\n\n                {\"x\":0.2, \"y\":1.0},\n                {\"x\":1.35, \"y\":1.1, \"w\":1.5},\n                {\"x\":2.85, \"y\":1.1},\n                {\"x\":3.85, \"y\":1.1},\n                {\"x\":4.85, \"y\":1.1},\n                {\"x\":5.85, \"y\":1.1},\n                {\"x\":6.85, \"y\":1.1},\n                {\"x\":9.45, \"y\":1.1},\n                {\"x\":10.45, \"y\":1.1},\n                {\"x\":11.45, \"y\":1.1},\n                {\"x\":12.45, \"y\":1.1},\n                {\"x\":13.45, \"y\":1.1},\n                {\"x\":14.45, \"y\":1.1},\n                {\"x\":15.45, \"y\":1.1},\n                {\"x\":16.45, \"y\":1.1, \"w\":1.5},\n\n                {\"x\":0, \"y\":2.0},\n                {\"x\":1.15, \"y\":2.1, \"w\":1.75},\n                {\"x\":2.9, \"y\":2.1},\n                {\"x\":3.9, \"y\":2.1},\n                {\"x\":4.9, \"y\":2.1},\n                {\"x\":5.9, \"y\":2.1},\n                {\"x\":6.9, \"y\":2.1},\n                {\"x\":9.9, \"y\":2.1},\n                {\"x\":10.9, \"y\":2.1},\n                {\"x\":11.9, \"y\":2.1},\n                {\"x\":12.9, \"y\":2.1},\n                {\"x\":13.9, \"y\":2.1},\n                {\"x\":14.9, \"y\":2.1},\n                {\"x\":15.9, \"y\":2.1, \"w\":2.25},\n\n                {\"x\":0.95, \"y\":3.1, \"w\":2.25},\n                {\"x\":3.2, \"y\":3.1},\n                {\"x\":4.2, \"y\":3.1},\n                {\"x\":5.2, \"y\":3.1},\n                {\"x\":6.2, \"y\":3.1},\n                {\"x\":7.2, \"y\":3.1},\n                {\"x\":9.6, \"y\":3.1},\n                {\"x\":10.6, \"y\":3.1},\n                {\"x\":11.6, \"y\":3.1},\n                {\"x\":12.6, \"y\":3.1},\n                {\"x\":13.6, \"y\":3.1},\n                {\"x\":14.6, \"y\":3.1},\n                {\"x\":15.6, \"y\":3.1, \"w\":1.75},\n                {\"x\":17.35, \"y\":3.1},\n\n                {\"x\":0.95, \"y\":4.1, \"w\":1.5},\n                {\"x\":3.85, \"y\":4.1, \"w\":1.5},\n                {\"x\":5.35, \"y\":4.1, \"w\":2},\n                {\"x\":7.35, \"y\":4.1, \"w\":1.25},\n                {\"x\":9.6, \"y\":4.1, \"w\":2.75},\n                {\"x\":12.35, \"y\":4.1, \"w\":1.5},\n                {\"x\":16.65, \"y\":4.1, \"w\":1.5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/alice_split_bs/layout.json",
    "content": "[{y:0.9,x:0.55,a:7},\"\",{x:2.15},\"\",{x:8.55},\"\"],\n[{y:-0.9,x:1.7},\"\",\"\",{x:10.55},\"\",\"\",\"\"],\n[{y:-0.1,x:0.35},\"\"],\n[{y:-0.95,x:12.95},\"\"],\n[{y:-0.95,x:1.5,w:1.5},\"\",\"\",{x:9.95},\"\",\"\",{w:1.5},\"\"],\n[{y:-0.1,x:0.15},\"\"],\n[{y:-0.9,x:1.3,w:1.75},\"\",\"\",{x:9.35},\"\",\"\",{w:2.25},\"\"],\n[{x:1.1,w:2.25},\"\",\"\",{x:8.75},\"\",\"\",{w:1.75},\"\",\"\"],\n[{x:1.1,w:1.5},\"\",{x:13.55,w:1.5},\"\"],\n[{r:12,y:-6,x:5},\"\",\"\",\"\",\"\"],\n[{x:4.5},\"\",\"\",\"\",\"\"],\n[{x:4.8},\"\",\"\",\"\",\"\"],\n[{x:5.3},\"\",\"\",\"\",\"\"],\n[{x:6.45,w:2},\"\",{w:1.25},\"\"],\n[{y:-0.95,x:4.95,w:1.5},\"\"],\n[{r:-12,y:-1.45,x:8.55},\"\",\"\",\"\",\"\"],\n[{x:8.05},\"\",\"\",\"\",\"\"],\n[{x:8.2},\"\",\"\",\"\",\"\"],\n[{x:7.75},\"\",\"\",\"\",\"\"],\n[{x:7.75,w:2.75},\"\"],\n[{y:-0.95,x:10.5,w:1.5},\"\"]\n"
  },
  {
    "path": "layouts/default/alice_split_bs/readme.md",
    "content": "# alice_split_bs\n\n    LAYOUT_alice_split_bs\n"
  },
  {
    "path": "layouts/default/ergodox/default_ergodox/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌──────┬───┬───┬───┬───┬───┬───┐                     ┌───┬───┬───┬───┬───┬───┬──────┐\n     * │ `    │ 1 │ 2 │ 3 │ 4 │ 5 │ = │                     │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │    - │\n     * ├──────┼───┼───┼───┼───┼───┼───┤                     ├───┼───┼───┼───┼───┼───┼──────┤\n     * │ Tab  │ Q │ W │ E │ R │ T │ = │                     │ Y │ Y │ U │ I │ O │ P │    \\ │\n     * ├──────┼───┼───┼───┼───┼───┤   │                     │   ├───┼───┼───┼───┼───┼──────┤\n     * │Escape│ A │ S │ D │ F │ G ├───┤                     ├───┤ H │ J │ K │ L │ ; │    ' │\n     * ├──────┼───┼───┼───┼───┼───┤   │                     │   ├───┼───┼───┼───┼───┼──────┤\n     * │Shift │ Z │ X │ C │ V │ B │ B │                     │ N │ N │ M │ , │ . │ / │ Shift│\n     * └──┬───┼───┼───┼───┼───┼───┴───┘ ┌───┬───┐ ┌───┬───┐ └───┴───┼───┼───┼───┼───┼───┬──┘\n     *    │Ctl│F4 │F5 │GUI│Alt│         │ C │ V │ │Alt│ A │         │ ← │ ↓ │ ↑ │ → │GUI│\n     *    └───┴───┴───┴───┴───┘     ┌───┼───┼───┤ ├───┼───┼───┐     └───┴───┴───┴───┴───┘\n     *                              │   │   │PgU│ │PgD│   │   │\n     *                              │Bsp│Bsp├───┤ ├───┤Ent│   │\n     *                              │   │   │Del│ │Ctl│   │   │\n     *                              └───┴───┴───┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_ergodox(\n        KC_GRV,  KC_1,    KC_2,    KC_3,    KC_4,    KC_5,    KC_EQL,\n        KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,    KC_EQL,\n        KC_ESC,  KC_A,    KC_S,    KC_D,    KC_F,    KC_G,\n        KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_B,\n        KC_LCTL, KC_F4,   KC_F5,   KC_LGUI, KC_LALT,\n                                                     KC_C,    KC_V,\n                                                              KC_PGUP,\n                                            KC_BSPC, KC_BSPC, KC_DEL,\n\n        KC_5,    KC_6,    KC_7,    KC_8,    KC_9,    KC_0,    KC_MINS,\n        KC_Y,    KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,    KC_BSLS,\n                 KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_QUOT,\n        KC_N,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT,\n                          KC_LEFT, KC_DOWN, KC_UP,   KC_RGHT, KC_RGUI,\n        KC_RALT, KC_A,\n        KC_PGDN,\n        KC_RCTL, KC_ENT, KC_SPC\n    )\n};\n"
  },
  {
    "path": "layouts/default/ergodox/info.json",
    "content": "{\n    \"keyboard_name\": \"Ergodox layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ergodox\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0.375, \"w\":1.5},\n                {\"x\":1.5, \"y\":0.375},\n                {\"x\":2.5, \"y\":0.125},\n                {\"x\":3.5, \"y\":0},\n                {\"x\":4.5, \"y\":0.125},\n                {\"x\":5.5, \"y\":0.25},\n                {\"x\":6.5, \"y\":0.25},\n\n                {\"x\":0, \"y\":1.375, \"w\":1.5},\n                {\"x\":1.5, \"y\":1.375},\n                {\"x\":2.5, \"y\":1.125},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1.125},\n                {\"x\":5.5, \"y\":1.25},\n                {\"x\":6.5, \"y\":1.25, \"h\":1.5},\n\n                {\"x\":0, \"y\":2.375, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.375},\n                {\"x\":2.5, \"y\":2.125},\n                {\"x\":3.5, \"y\":2},\n                {\"x\":4.5, \"y\":2.125},\n                {\"x\":5.5, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.375, \"w\":1.5},\n                {\"x\":1.5, \"y\":3.375},\n                {\"x\":2.5, \"y\":3.125},\n                {\"x\":3.5, \"y\":3},\n                {\"x\":4.5, \"y\":3.125},\n                {\"x\":5.5, \"y\":3.25},\n                {\"x\":6.5, \"y\":2.75, \"h\":1.5},\n\n                {\"x\":0.5, \"y\":4.375},\n                {\"x\":1.5, \"y\":4.375},\n                {\"x\":2.5, \"y\":4.125},\n                {\"x\":3.5, \"y\":4},\n                {\"x\":4.5, \"y\":4.125},\n\n                {\"x\":7.75, \"y\":4.25},\n                {\"x\":8.75, \"y\":4.25},\n\n                {\"x\":8.75, \"y\":5.25},\n\n                {\"x\":6.75, \"y\":5.25, \"h\":2},\n                {\"x\":7.75, \"y\":5.25, \"h\":2},\n                {\"x\":8.75, \"y\":6.25},\n\n                {\"x\":12.25, \"y\":0.25},\n                {\"x\":13.25, \"y\":0.25},\n                {\"x\":14.25, \"y\":0.125},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0.125},\n                {\"x\":17.25, \"y\":0.375},\n                {\"x\":18.25, \"y\":0.375, \"w\":1.5},\n\n                {\"x\":12.25, \"y\":1.25, \"h\":1.5},\n                {\"x\":13.25, \"y\":1.25},\n                {\"x\":14.25, \"y\":1.125},\n                {\"x\":15.25, \"y\":1},\n                {\"x\":16.25, \"y\":1.125},\n                {\"x\":17.25, \"y\":1.375},\n                {\"x\":18.25, \"y\":1.375, \"w\":1.5},\n\n                {\"x\":13.25, \"y\":2.25},\n                {\"x\":14.25, \"y\":2.125},\n                {\"x\":15.25, \"y\":2},\n                {\"x\":16.25, \"y\":2.125},\n                {\"x\":17.25, \"y\":2.375},\n                {\"x\":18.25, \"y\":2.375, \"w\":1.5},\n\n                {\"x\":12.25, \"y\":2.75, \"h\":1.5},\n                {\"x\":13.25, \"y\":3.25},\n                {\"x\":14.25, \"y\":3.125},\n                {\"x\":15.25, \"y\":3},\n                {\"x\":16.25, \"y\":3.125},\n                {\"x\":17.25, \"y\":3.375},\n                {\"x\":18.25, \"y\":3.375, \"w\":1.5},\n\n                {\"x\":14.25, \"y\":4.125},\n                {\"x\":15.25, \"y\":4},\n                {\"x\":16.25, \"y\":4.125},\n                {\"x\":17.25, \"y\":4.375},\n                {\"x\":18.25, \"y\":4.375},\n\n                {\"x\":10, \"y\":4.25},\n                {\"x\":11, \"y\":4.25},\n\n                {\"x\":10, \"y\":5.25},\n\n                {\"x\":10, \"y\":6.25},\n                {\"x\":11, \"y\":5.25, \"h\":2},\n                {\"x\":12, \"y\":5.25, \"h\":2}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ergodox/layout.json",
    "content": "[{x:3.5,a:7},\"\",{x:10.5},\"\"],\n[{y:-0.875,x:2.5},\"\",{x:1},\"\",{x:8.5},\"\",{x:1},\"\"],\n[{y:-0.875,x:5.5},\"\",\"\",{x:4.5},\"\",\"\"],\n[{y:-0.875,w:1.5},\"\",\"\",{x:14.5},\"\",{w:1.5},\"\"],\n[{y:-0.375,x:3.5},\"\",{x:10.5},\"\"],\n[{y:-0.875,x:2.5},\"\",{x:1},\"\",{x:8.5},\"\",{x:1},\"\"],\n[{y:-0.875,x:5.5},\"\",{h:1.5},\"\",{x:4.5,h:1.5},\"\",\"\"],\n[{y:-0.875,w:1.5},\"\",\"\",{x:14.5},\"\",{w:1.5},\"\"],\n[{y:-0.375,x:3.5},\"\",{x:10.5},\"\"],\n[{y:-0.875,x:2.5},\"\",{x:1},\"\",{x:8.5},\"\",{x:1},\"\"],\n[{y:-0.875,x:5.5},\"\",{x:6.5},\"\"],\n[{y:-0.875,w:1.5},\"\",\"\",{x:14.5},\"\",{w:1.5},\"\"],\n[{y:-0.625,x:6.5,h:1.5},\"\",{x:4.5,h:1.5},\"\"],\n[{y:-0.75,x:3.5},\"\",{x:10.5},\"\"],\n[{y:-0.875,x:2.5},\"\",{x:1},\"\",{x:8.5},\"\",{x:1},\"\"],\n[{y:-0.875,x:5.5},\"\",{x:6.5},\"\"],\n[{y:-0.875,w:1.5},\"\",\"\",{x:14.5},\"\",{w:1.5},\"\"],\n[{y:-0.375,x:3.5},\"\",{x:10.5},\"\"],\n[{y:-0.875,x:2.5},\"\",{x:1},\"\",{x:8.5},\"\",{x:1},\"\"],\n[{y:-0.75,x:0.5},\"\",\"\",{x:14.5},\"\",\"\"],\n[{r:30,rx:6.5,ry:4.25,y:-1,x:1},\"\",\"\"],\n[{h:2},\"\",{h:2},\"\",\"\"],\n[{x:2},\"\"],\n[{r:-30,rx:13,y:-1,x:-3},\"\",\"\"],\n[{x:-3},\"\",{h:2},\"\",{h:2},\"\"],\n[{x:-3},\"\"]\n"
  },
  {
    "path": "layouts/default/ergodox/readme.md",
    "content": "# ergodox\n\n    LAYOUT_ergodox\n"
  },
  {
    "path": "layouts/default/fullsize_ansi/default_fullsize_ansi/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│ │Num│ / │ * │ - │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │Del│End│PgD│ │ 7 │ 8 │ 9 │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘ ├───┼───┼───┤ + │\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │               │ 4 │ 5 │ 6 │   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐     ├───┼───┼───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │     │ 1 │ 2 │ 3 │   │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤Ent│\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│ │ ← │ ↓ │ → │ │   0   │ . │   │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n     */\n    [0] = LAYOUT_fullsize_ansi(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,    KC_NUM,  KC_PSLS, KC_PAST, KC_PMNS,\n        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_DEL,  KC_END,  KC_PGDN,    KC_P7,   KC_P8,   KC_P9,   KC_PPLS,\n        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_ENT,                                   KC_P4,   KC_P5,   KC_P6,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,               KC_P1,   KC_P2,   KC_P3,   KC_PENT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT,    KC_P0,            KC_PDOT\n    )\n};\n"
  },
  {
    "path": "layouts/default/fullsize_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"Fullsize ANSI layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_fullsize_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n                {\"x\":18.5, \"y\":1.25},\n                {\"x\":19.5, \"y\":1.25},\n                {\"x\":20.5, \"y\":1.25},\n                {\"x\":21.5, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n                {\"x\":18.5, \"y\":2.25},\n                {\"x\":19.5, \"y\":2.25},\n                {\"x\":20.5, \"y\":2.25},\n                {\"x\":21.5, \"y\":2.25, \"h\": 2},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n                {\"x\":18.5, \"y\":3.25},\n                {\"x\":19.5, \"y\":3.25},\n                {\"x\":20.5, \"y\":3.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n                {\"x\":18.5, \"y\":4.25},\n                {\"x\":19.5, \"y\":4.25},\n                {\"x\":20.5, \"y\":4.25},\n                {\"x\":21.5, \"y\":4.25, \"h\":2},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25},\n                {\"x\":18.5, \"y\":5.25, \"w\":2},\n                {\"x\":20.5, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/fullsize_ansi/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",{x:3.5},\"\",\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\",{x:1.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25,w:2},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/fullsize_ansi/readme.md",
    "content": "# fullsize_ansi\n\n    LAYOUT_fullsize_ansi\n"
  },
  {
    "path": "layouts/default/fullsize_extended_ansi/default_fullsize_extended_ansi/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│ │Clc│VMT│VDN│VUP│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘ └───┴───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│ │Num│ / │ * │ - │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │Del│End│PgD│ │ 7 │ 8 │ 9 │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘ ├───┼───┼───┤ + │\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │               │ 4 │ 5 │ 6 │   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐     ├───┼───┼───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │     │ 1 │ 2 │ 3 │   │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤Ent│\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│ │ ← │ ↓ │ → │ │   0   │ . │   │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n     */\n    [0] = LAYOUT_fullsize_extended_ansi(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,    KC_CALC, KC_MUTE, KC_VOLD, KC_VOLU,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,    KC_NUM,  KC_PSLS, KC_PAST, KC_PMNS,\n        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_DEL,  KC_END,  KC_PGDN,    KC_P7,   KC_P8,   KC_P9,   KC_PPLS,\n        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_ENT,                                   KC_P4,   KC_P5,   KC_P6,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,               KC_P1,   KC_P2,   KC_P3,   KC_PENT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT,    KC_P0,            KC_PDOT\n    )\n};\n"
  },
  {
    "path": "layouts/default/fullsize_extended_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"Fullsize Extended ANSI layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_fullsize_extended_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n                {\"x\":18.5, \"y\":0},\n                {\"x\":19.5, \"y\":0},\n                {\"x\":20.5, \"y\":0},\n                {\"x\":21.5, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n                {\"x\":18.5, \"y\":1.25},\n                {\"x\":19.5, \"y\":1.25},\n                {\"x\":20.5, \"y\":1.25},\n                {\"x\":21.5, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n                {\"x\":18.5, \"y\":2.25},\n                {\"x\":19.5, \"y\":2.25},\n                {\"x\":20.5, \"y\":2.25},\n                {\"x\":21.5, \"y\":2.25, \"h\": 2},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n                {\"x\":18.5, \"y\":3.25},\n                {\"x\":19.5, \"y\":3.25},\n                {\"x\":20.5, \"y\":3.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n                {\"x\":18.5, \"y\":4.25},\n                {\"x\":19.5, \"y\":4.25},\n                {\"x\":20.5, \"y\":4.25},\n                {\"x\":21.5, \"y\":4.25, \"h\":2},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25},\n                {\"x\":18.5, \"y\":5.25, \"w\":2},\n                {\"x\":20.5, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/fullsize_extended_ansi/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\",{x:3.5},\"\",\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\",{x:1.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25,w:2},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/fullsize_extended_ansi/readme.md",
    "content": "# fullsize_extended_ansi\n\n    LAYOUT_fullsize_extended_ansi\n"
  },
  {
    "path": "layouts/default/fullsize_extended_iso/default_fullsize_extended_iso/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│ │Clc│VMT│VDN│VUP│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘ └───┴───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│ │Num│ / │ * │ - │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │Del│End│PgD│ │ 7 │ 8 │ 9 │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘ ├───┼───┼───┤ + │\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │               │ 4 │ 5 │ 6 │   │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐     ├───┼───┼───┼───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │     │ 1 │ 2 │ 3 │   │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤Ent│\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│ │ ← │ ↓ │ → │ │   0   │ . │   │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n     */\n    [0] = LAYOUT_fullsize_extended_iso(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,    KC_CALC, KC_MUTE, KC_VOLD, KC_VOLU,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,    KC_NUM,  KC_PSLS, KC_PAST, KC_PMNS,\n        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_DEL,  KC_END,  KC_PGDN,    KC_P7,   KC_P8,   KC_P9,   KC_PPLS,\n        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_NUHS, KC_ENT,                                   KC_P4,   KC_P5,   KC_P6,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,               KC_P1,   KC_P2,   KC_P3,   KC_PENT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT,    KC_P0,            KC_PDOT\n    )\n};\n"
  },
  {
    "path": "layouts/default/fullsize_extended_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"Fullsize Extended ISO layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_fullsize_extended_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n                {\"x\":18.5, \"y\":0},\n                {\"x\":19.5, \"y\":0},\n                {\"x\":20.5, \"y\":0},\n                {\"x\":21.5, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n                {\"x\":18.5, \"y\":1.25},\n                {\"x\":19.5, \"y\":1.25},\n                {\"x\":20.5, \"y\":1.25},\n                {\"x\":21.5, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n                {\"x\":18.5, \"y\":2.25},\n                {\"x\":19.5, \"y\":2.25},\n                {\"x\":20.5, \"y\":2.25},\n                {\"x\":21.5, \"y\":2.25, \"h\": 2},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n                {\"x\":18.5, \"y\":3.25},\n                {\"x\":19.5, \"y\":3.25},\n                {\"x\":20.5, \"y\":3.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n                {\"x\":18.5, \"y\":4.25},\n                {\"x\":19.5, \"y\":4.25},\n                {\"x\":20.5, \"y\":4.25},\n                {\"x\":21.5, \"y\":4.25, \"h\":2},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25},\n                {\"x\":18.5, \"y\":5.25, \"w\":2},\n                {\"x\":20.5, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/fullsize_extended_iso/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:4.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\",{x:1.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25,w:2},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/fullsize_extended_iso/readme.md",
    "content": "# fullsize_extended_iso\n\n    LAYOUT_fullsize_extended_iso\n"
  },
  {
    "path": "layouts/default/fullsize_extended_jis/default_fullsize_extended_jis/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│ │Clc│VMT│VDN│VUP│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘ └───┴───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n     * │ZHK│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ ^ │ ¥ │Bsp│ │Ins│Hom│PgU│ │Num│ / │ * │ - │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ @ │ [ │     │ │Del│End│PgD│ │ 7 │ 8 │ 9 │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘ ├───┼───┼───┤ + │\n     * │ Eisu │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ : │ ] │    │               │ 4 │ 5 │ 6 │   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤     ┌───┐     ├───┼───┼───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ \\ │ Shft │     │ ↑ │     │ 1 │ 2 │ 3 │   │\n     * ├────┬───┴┬──┴─┬─┴──┬┴───┴───┴───┼───┴┬──┴─┬─┴─┬─┴─┬─┴─┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤Ent│\n     * │Ctrl│GUI │Alt │Mhen│   Space    │Henk│Kana│Alt│GUI│App│Ctrl│ │ ← │ ↓ │ → │ │   0   │ . │   │\n     * └────┴────┴────┴────┴────────────┴────┴────┴───┴───┴───┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n     */\n    [0] = LAYOUT_fullsize_extended_jis(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,    KC_CALC, KC_MUTE, KC_VOLD, KC_VOLU,\n\n        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_INT3, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,    KC_NUM,  KC_PSLS, KC_PAST, KC_PMNS,\n        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_DEL,  KC_END,  KC_PGDN,    KC_P7,   KC_P8,   KC_P9,   KC_PPLS,\n        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_NUHS, KC_ENT,                                   KC_P4,   KC_P5,   KC_P6,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_INT1, KC_RSFT,             KC_UP,               KC_P1,   KC_P2,   KC_P3,   KC_PENT,\n        KC_LCTL, KC_LGUI, KC_LALT, KC_INT5,               KC_SPC,               KC_INT4, KC_INT2, KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT,    KC_P0,            KC_PDOT\n    )\n};\n"
  },
  {
    "path": "layouts/default/fullsize_extended_jis/info.json",
    "content": "{\n    \"keyboard_name\": \"Fullsize Extended JIS layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_fullsize_extended_jis\": {\n            \"layout\": [\n                {\"x\": 0, \"y\": 0},\n                {\"x\": 2, \"y\": 0},\n                {\"x\": 3, \"y\": 0},\n                {\"x\": 4, \"y\": 0},\n                {\"x\": 5, \"y\": 0},\n                {\"x\": 6.5, \"y\": 0},\n                {\"x\": 7.5, \"y\": 0},\n                {\"x\": 8.5, \"y\": 0},\n                {\"x\": 9.5, \"y\": 0},\n                {\"x\": 11, \"y\": 0},\n                {\"x\": 12, \"y\": 0},\n                {\"x\": 13, \"y\": 0},\n                {\"x\": 14, \"y\": 0},\n                {\"x\": 15.25, \"y\": 0},\n                {\"x\": 16.25, \"y\": 0},\n                {\"x\": 17.25, \"y\": 0},\n                {\"x\":18.5, \"y\":0},\n                {\"x\":19.5, \"y\":0},\n                {\"x\":20.5, \"y\":0},\n                {\"x\":21.5, \"y\":0},\n\n                {\"x\": 0, \"y\": 1.25},\n                {\"x\": 1, \"y\": 1.25},\n                {\"x\": 2, \"y\": 1.25},\n                {\"x\": 3, \"y\": 1.25},\n                {\"x\": 4, \"y\": 1.25},\n                {\"x\": 5, \"y\": 1.25},\n                {\"x\": 6, \"y\": 1.25},\n                {\"x\": 7, \"y\": 1.25},\n                {\"x\": 8, \"y\": 1.25},\n                {\"x\": 9, \"y\": 1.25},\n                {\"x\": 10, \"y\": 1.25},\n                {\"x\": 11, \"y\": 1.25},\n                {\"x\": 12, \"y\": 1.25},\n                {\"x\": 13, \"y\": 1.25},\n                {\"x\": 14, \"y\": 1.25},\n                {\"x\": 15.25, \"y\": 1.25},\n                {\"x\": 16.25, \"y\": 1.25},\n                {\"x\": 17.25, \"y\": 1.25},\n                {\"x\": 18.5, \"y\": 1.25},\n                {\"x\": 19.5, \"y\": 1.25},\n                {\"x\": 20.5, \"y\": 1.25},\n                {\"x\": 21.5, \"y\": 1.25},\n\n                {\"x\": 0, \"y\": 2.25, \"w\": 1.5},\n                {\"x\": 1.5, \"y\": 2.25},\n                {\"x\": 2.5, \"y\": 2.25},\n                {\"x\": 3.5, \"y\": 2.25},\n                {\"x\": 4.5, \"y\": 2.25},\n                {\"x\": 5.5, \"y\": 2.25},\n                {\"x\": 6.5, \"y\": 2.25},\n                {\"x\": 7.5, \"y\": 2.25},\n                {\"x\": 8.5, \"y\": 2.25},\n                {\"x\": 9.5, \"y\": 2.25},\n                {\"x\": 10.5, \"y\": 2.25},\n                {\"x\": 11.5, \"y\": 2.25},\n                {\"x\": 12.5, \"y\": 2.25},\n\n                {\"x\": 15.25, \"y\": 2.25},\n                {\"x\": 16.25, \"y\": 2.25},\n                {\"x\": 17.25, \"y\": 2.25},\n                {\"x\": 18.5, \"y\": 2.25},\n                {\"x\": 19.5, \"y\": 2.25},\n                {\"x\": 20.5, \"y\": 2.25},\n                {\"x\": 21.5, \"y\": 2.25, \"h\": 2},\n\n                {\"x\": 0, \"y\": 3.25, \"w\": 1.75},\n                {\"x\": 1.75, \"y\": 3.25},\n                {\"x\": 2.75, \"y\": 3.25},\n                {\"x\": 3.75, \"y\": 3.25},\n                {\"x\": 4.75, \"y\": 3.25},\n                {\"x\": 5.75, \"y\": 3.25},\n                {\"x\": 6.75, \"y\": 3.25},\n                {\"x\": 7.75, \"y\": 3.25},\n                {\"x\": 8.75, \"y\": 3.25},\n                {\"x\": 9.75, \"y\": 3.25},\n                {\"x\": 10.75, \"y\": 3.25},\n                {\"x\": 11.75, \"y\": 3.25},\n                {\"x\": 12.75, \"y\": 3.25},\n                {\"x\": 13.75, \"y\": 2.25, \"w\": 1.25, \"h\": 2},\n                {\"x\": 18.5, \"y\": 3.25},\n                {\"x\": 19.5, \"y\": 3.25},\n                {\"x\": 20.5, \"y\": 3.25},\n\n                {\"x\": 0, \"y\": 4.25, \"w\": 2.25},\n                {\"x\": 2.25, \"y\": 4.25},\n                {\"x\": 3.25, \"y\": 4.25},\n                {\"x\": 4.25, \"y\": 4.25},\n                {\"x\": 5.25, \"y\": 4.25},\n                {\"x\": 6.25, \"y\": 4.25},\n                {\"x\": 7.25, \"y\": 4.25},\n                {\"x\": 8.25, \"y\": 4.25},\n                {\"x\": 9.25, \"y\": 4.25},\n                {\"x\": 10.25, \"y\": 4.25},\n                {\"x\": 11.25, \"y\": 4.25},\n                {\"x\": 12.25, \"y\": 4.25},\n                {\"x\": 13.25, \"y\": 4.25, \"w\": 1.75},\n                {\"x\": 16.25, \"y\": 4.25},\n                {\"x\": 18.5, \"y\": 4.25},\n                {\"x\": 19.5, \"y\": 4.25},\n                {\"x\": 20.5, \"y\": 4.25},\n                {\"x\": 21.5, \"y\": 4.25, \"h\": 2},\n\n                {\"x\": 0, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 1.25, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 2.5, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 3.75, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 5, \"y\": 5.25, \"w\": 3.25},\n                {\"x\": 8.25, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 9.5, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 10.75, \"y\": 5.25},\n                {\"x\": 11.75, \"y\": 5.25},\n                {\"x\": 12.75, \"y\": 5.25},\n                {\"x\": 13.75, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 15.25, \"y\": 5.25},\n                {\"x\": 16.25, \"y\": 5.25},\n                {\"x\": 17.25, \"y\": 5.25},\n                {\"x\": 18.5, \"y\": 5.25, \"w\": 2},\n                {\"x\": 20.5, \"y\": 5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/fullsize_extended_jis/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:4.75},\"\",\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",{x:1.25},\"\",{x:1.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:3.25},\"\",{w:1.25},\"\",{w:1.25},\"\",\"\",\"\",\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25,w:2},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/fullsize_extended_jis/readme.md",
    "content": "# fullsize_extended_jis\n\n    LAYOUT_fullsize_extended_jis\n"
  },
  {
    "path": "layouts/default/fullsize_iso/default_fullsize_iso/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│ │Num│ / │ * │ - │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │Del│End│PgD│ │ 7 │ 8 │ 9 │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘ ├───┼───┼───┤ + │\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │               │ 4 │ 5 │ 6 │   │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐     ├───┼───┼───┼───┤\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │     │ 1 │ 2 │ 3 │   │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤Ent│\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│ │ ← │ ↓ │ → │ │   0   │ . │   │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n     */\n    [0] = LAYOUT_fullsize_iso(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,    KC_NUM,  KC_PSLS, KC_PAST, KC_PMNS,\n        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_DEL,  KC_END,  KC_PGDN,    KC_P7,   KC_P8,   KC_P9,   KC_PPLS,\n        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_NUHS, KC_ENT,                                   KC_P4,   KC_P5,   KC_P6,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,               KC_P1,   KC_P2,   KC_P3,   KC_PENT,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT,    KC_P0,            KC_PDOT\n    )\n};\n"
  },
  {
    "path": "layouts/default/fullsize_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"Fullsize ISO layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_fullsize_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n                {\"x\":18.5, \"y\":1.25},\n                {\"x\":19.5, \"y\":1.25},\n                {\"x\":20.5, \"y\":1.25},\n                {\"x\":21.5, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n                {\"x\":18.5, \"y\":2.25},\n                {\"x\":19.5, \"y\":2.25},\n                {\"x\":20.5, \"y\":2.25},\n                {\"x\":21.5, \"y\":2.25, \"h\": 2},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n                {\"x\":18.5, \"y\":3.25},\n                {\"x\":19.5, \"y\":3.25},\n                {\"x\":20.5, \"y\":3.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n                {\"x\":18.5, \"y\":4.25},\n                {\"x\":19.5, \"y\":4.25},\n                {\"x\":20.5, \"y\":4.25},\n                {\"x\":21.5, \"y\":4.25, \"h\":2},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25},\n                {\"x\":18.5, \"y\":5.25, \"w\":2},\n                {\"x\":20.5, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/fullsize_iso/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:4.75},\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\",{x:1.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25,w:2},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/fullsize_iso/readme.md",
    "content": "# fullsize_iso\n\n    LAYOUT_fullsize_iso\n"
  },
  {
    "path": "layouts/default/fullsize_jis/default_fullsize_jis/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n     * │ZHK│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ ^ │ ¥ │Bsp│ │Ins│Hom│PgU│ │Num│ / │ * │ - │\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ @ │ [ │     │ │Del│End│PgD│ │ 7 │ 8 │ 9 │   │\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘ ├───┼───┼───┤ + │\n     * │ Eisu │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ : │ ] │    │               │ 4 │ 5 │ 6 │   │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤     ┌───┐     ├───┼───┼───┼───┤\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ \\ │ Shft │     │ ↑ │     │ 1 │ 2 │ 3 │   │\n     * ├────┬───┴┬──┴─┬─┴──┬┴───┴───┴───┼───┴┬──┴─┬─┴─┬─┴─┬─┴─┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤Ent│\n     * │Ctrl│GUI │Alt │Mhen│   Space    │Henk│Kana│Alt│GUI│App│Ctrl│ │ ← │ ↓ │ → │ │   0   │ . │   │\n     * └────┴────┴────┴────┴────────────┴────┴────┴───┴───┴───┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n     */\n    [0] = LAYOUT_fullsize_jis(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_INT3, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,    KC_NUM,  KC_PSLS, KC_PAST, KC_PMNS,\n        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_DEL,  KC_END,  KC_PGDN,    KC_P7,   KC_P8,   KC_P9,   KC_PPLS,\n        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_NUHS, KC_ENT,                                   KC_P4,   KC_P5,   KC_P6,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_INT1, KC_RSFT,             KC_UP,               KC_P1,   KC_P2,   KC_P3,   KC_PENT,\n        KC_LCTL, KC_LGUI, KC_LALT, KC_INT5,               KC_SPC,               KC_INT4, KC_INT2, KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT,    KC_P0,            KC_PDOT\n    )\n};\n"
  },
  {
    "path": "layouts/default/fullsize_jis/info.json",
    "content": "{\n    \"keyboard_name\": \"Fullsize JIS layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_fullsize_jis\": {\n            \"layout\": [\n                {\"x\": 0, \"y\": 0},\n                {\"x\": 2, \"y\": 0},\n                {\"x\": 3, \"y\": 0},\n                {\"x\": 4, \"y\": 0},\n                {\"x\": 5, \"y\": 0},\n                {\"x\": 6.5, \"y\": 0},\n                {\"x\": 7.5, \"y\": 0},\n                {\"x\": 8.5, \"y\": 0},\n                {\"x\": 9.5, \"y\": 0},\n                {\"x\": 11, \"y\": 0},\n                {\"x\": 12, \"y\": 0},\n                {\"x\": 13, \"y\": 0},\n                {\"x\": 14, \"y\": 0},\n                {\"x\": 15.25, \"y\": 0},\n                {\"x\": 16.25, \"y\": 0},\n                {\"x\": 17.25, \"y\": 0},\n\n                {\"x\": 0, \"y\": 1.25},\n                {\"x\": 1, \"y\": 1.25},\n                {\"x\": 2, \"y\": 1.25},\n                {\"x\": 3, \"y\": 1.25},\n                {\"x\": 4, \"y\": 1.25},\n                {\"x\": 5, \"y\": 1.25},\n                {\"x\": 6, \"y\": 1.25},\n                {\"x\": 7, \"y\": 1.25},\n                {\"x\": 8, \"y\": 1.25},\n                {\"x\": 9, \"y\": 1.25},\n                {\"x\": 10, \"y\": 1.25},\n                {\"x\": 11, \"y\": 1.25},\n                {\"x\": 12, \"y\": 1.25},\n                {\"x\": 13, \"y\": 1.25},\n                {\"x\": 14, \"y\": 1.25},\n                {\"x\": 15.25, \"y\": 1.25},\n                {\"x\": 16.25, \"y\": 1.25},\n                {\"x\": 17.25, \"y\": 1.25},\n                {\"x\": 18.5, \"y\": 1.25},\n                {\"x\": 19.5, \"y\": 1.25},\n                {\"x\": 20.5, \"y\": 1.25},\n                {\"x\": 21.5, \"y\": 1.25},\n\n                {\"x\": 0, \"y\": 2.25, \"w\": 1.5},\n                {\"x\": 1.5, \"y\": 2.25},\n                {\"x\": 2.5, \"y\": 2.25},\n                {\"x\": 3.5, \"y\": 2.25},\n                {\"x\": 4.5, \"y\": 2.25},\n                {\"x\": 5.5, \"y\": 2.25},\n                {\"x\": 6.5, \"y\": 2.25},\n                {\"x\": 7.5, \"y\": 2.25},\n                {\"x\": 8.5, \"y\": 2.25},\n                {\"x\": 9.5, \"y\": 2.25},\n                {\"x\": 10.5, \"y\": 2.25},\n                {\"x\": 11.5, \"y\": 2.25},\n                {\"x\": 12.5, \"y\": 2.25},\n                {\"x\": 15.25, \"y\": 2.25},\n                {\"x\": 16.25, \"y\": 2.25},\n                {\"x\": 17.25, \"y\": 2.25},\n                {\"x\": 18.5, \"y\": 2.25},\n                {\"x\": 19.5, \"y\": 2.25},\n                {\"x\": 20.5, \"y\": 2.25},\n                {\"x\": 21.5, \"y\": 2.25, \"h\": 2},\n\n                {\"x\": 0, \"y\": 3.25, \"w\": 1.75},\n                {\"x\": 1.75, \"y\": 3.25},\n                {\"x\": 2.75, \"y\": 3.25},\n                {\"x\": 3.75, \"y\": 3.25},\n                {\"x\": 4.75, \"y\": 3.25},\n                {\"x\": 5.75, \"y\": 3.25},\n                {\"x\": 6.75, \"y\": 3.25},\n                {\"x\": 7.75, \"y\": 3.25},\n                {\"x\": 8.75, \"y\": 3.25},\n                {\"x\": 9.75, \"y\": 3.25},\n                {\"x\": 10.75, \"y\": 3.25},\n                {\"x\": 11.75, \"y\": 3.25},\n                {\"x\": 12.75, \"y\": 3.25},\n                {\"x\": 13.75, \"y\": 2.25, \"w\": 1.25, \"h\": 2},\n                {\"x\": 18.5, \"y\": 3.25},\n                {\"x\": 19.5, \"y\": 3.25},\n                {\"x\": 20.5, \"y\": 3.25},\n\n                {\"x\": 0, \"y\": 4.25, \"w\": 2.25},\n                {\"x\": 2.25, \"y\": 4.25},\n                {\"x\": 3.25, \"y\": 4.25},\n                {\"x\": 4.25, \"y\": 4.25},\n                {\"x\": 5.25, \"y\": 4.25},\n                {\"x\": 6.25, \"y\": 4.25},\n                {\"x\": 7.25, \"y\": 4.25},\n                {\"x\": 8.25, \"y\": 4.25},\n                {\"x\": 9.25, \"y\": 4.25},\n                {\"x\": 10.25, \"y\": 4.25},\n                {\"x\": 11.25, \"y\": 4.25},\n                {\"x\": 12.25, \"y\": 4.25},\n                {\"x\": 13.25, \"y\": 4.25, \"w\": 1.75},\n                {\"x\": 16.25, \"y\": 4.25},\n                {\"x\": 18.5, \"y\": 4.25},\n                {\"x\": 19.5, \"y\": 4.25},\n                {\"x\": 20.5, \"y\": 4.25},\n                {\"x\": 21.5, \"y\": 4.25, \"h\": 2},\n\n                {\"x\": 0, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 1.25, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 2.5, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 3.75, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 5, \"y\": 5.25, \"w\": 3.25},\n                {\"x\": 8.25, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 9.5, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 10.75, \"y\": 5.25},\n                {\"x\": 11.75, \"y\": 5.25},\n                {\"x\": 12.75, \"y\": 5.25},\n                {\"x\": 13.75, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 15.25, \"y\": 5.25},\n                {\"x\": 16.25, \"y\": 5.25},\n                {\"x\": 17.25, \"y\": 5.25},\n                {\"x\": 18.5, \"y\": 5.25, \"w\": 2},\n                {\"x\": 20.5, \"y\": 5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/fullsize_jis/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:4.75},\"\",\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",{x:1.25},\"\",{x:1.25},\"\",\"\",\"\",{h:2},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:3.25},\"\",{w:1.25},\"\",{w:1.25},\"\",\"\",\"\",\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\",{x:0.25,w:2},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/fullsize_jis/readme.md",
    "content": "# fullsize_jis\n\n    LAYOUT_fullsize_jis\n"
  },
  {
    "path": "layouts/default/numpad_4x4/default_numpad_4x4/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┐\n     * │ 7 │ 8 │ 9 │   │\n     * ├───┼───┼───┤ + │\n     * │ 4 │ 5 │ 6 │   │\n     * ├───┼───┼───┼───┤\n     * │ 1 │ 2 │ 3 │   │\n     * ├───┴───┼───┤Ent│\n     * │   0   │ . │   │\n     * └───────┴───┴───┘\n     */\n    [0] = LAYOUT_numpad_4x4(\n        KC_P7,   KC_P8,   KC_P9,   KC_PPLS,\n        KC_P4,   KC_P5,   KC_P6,\n        KC_P1,   KC_P2,   KC_P3,   KC_PENT,\n        KC_P0,            KC_PDOT\n    )\n};\n"
  },
  {
    "path": "layouts/default/numpad_4x4/info.json",
    "content": "{\n    \"keyboard_name\": \"4x4 number pad layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_numpad_4x4\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0, \"h\":2},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":2},\n                {\"x\":2, \"y\":3}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/numpad_4x4/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",{h:2},\"\"],\n[\"\",\"\",\"\"],\n[\"\",\"\",\"\",{h:2},\"\"],\n[{w:2},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/numpad_4x4/readme.md",
    "content": "# numpad_4x4\n\n    LAYOUT_numpad_4x4\n"
  },
  {
    "path": "layouts/default/numpad_5x4/default_numpad_5x4/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┐\n     * │TG1│ / │ * │ - │\n     * ├───┼───┼───┼───┤\n     * │ 7 │ 8 │ 9 │   │\n     * ├───┼───┼───┤ + │\n     * │ 4 │ 5 │ 6 │   │\n     * ├───┼───┼───┼───┤\n     * │ 1 │ 2 │ 3 │   │\n     * ├───┴───┼───┤Ent│\n     * │   0   │ . │   │\n     * └───────┴───┴───┘\n     */\n    [0] = LAYOUT_numpad_5x4(\n        TG(1),   KC_PSLS, KC_PAST, KC_PMNS,\n        KC_P7,   KC_P8,   KC_P9,\n        KC_P4,   KC_P5,   KC_P6,   KC_PPLS,\n        KC_P1,   KC_P2,   KC_P3,\n        KC_P0,   KC_PDOT,          KC_PENT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┐\n     * │TG1│ / │ * │ - │\n     * ┌───┬───┬───┐───┤\n     * │Hom│ ↑ │PgU│   │\n     * ├───┼───┼───┤ + │\n     * │ ← │   │ → │   │\n     * ├───┼───┼───┤───┤\n     * │End│ ↓ │PgD│   │\n     * ├───┴───┼───┤Ent│\n     * │Insert │Del│   │\n     * └───────┴───┘───┘\n     */\n    [1] = LAYOUT_numpad_5x4(\n        _______, _______, _______, _______,\n        KC_HOME, KC_UP,   KC_PGUP,\n        KC_LEFT, XXXXXXX, KC_RGHT, _______,\n        KC_END,  KC_DOWN, KC_PGDN,\n        KC_INS,           KC_DEL,  _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/numpad_5x4/info.json",
    "content": "{\n    \"keyboard_name\": \"5x4 number pad layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_numpad_5x4\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":1, \"h\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":2},\n                {\"x\":2, \"y\":4},\n                {\"x\":3, \"y\":3, \"h\":2}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/numpad_5x4/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",{h:2},\"\"],\n[\"\",\"\",\"\"],\n[\"\",\"\",\"\",{h:2},\"\"],\n[{w:2},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/numpad_5x4/readme.md",
    "content": "# numpad_5x4\n\n    LAYOUT_numpad_5x4\n"
  },
  {
    "path": "layouts/default/numpad_5x6/default_numpad_5x6/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┐\n     * │Esc│ ( │ ) │ / │ * │ - │\n     * ├───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ 7 │ 8 │ 9 │   │\n     * ├───┼───┼───┼───┼───┤ + │\n     * │ C │ D │ 4 │ 5 │ 6 │   │\n     * ├───┼───┼───┼───┼───┼───┤\n     * │ E │ F │ 1 │ 2 │ 3 │   │\n     * ├───┼───┼───┴───┼───┤Ent│\n     * │Ctl│TG1│ 0     │ . │   │\n     * └───┴───┴───────┴───┴───┘\n     */\n    [0] = LAYOUT_numpad_5x6(\n        KC_ESC,  KC_LPRN, KC_RPRN, KC_PSLS, KC_PAST, KC_PMNS,\n        KC_A,    KC_B,    KC_P7,   KC_P8,   KC_P9,\n        KC_C,    KC_D,    KC_P4,   KC_P5,   KC_P6,   KC_PPLS,\n        KC_E,    KC_F,    KC_P1,   KC_P2,   KC_P3,\n        KC_LCTL, TG(1),   KC_P0,            KC_PDOT, KC_PENT\n    ),\n\n    /*\n     * ┌───┐───┬───┬───┬───┬───┐\n     * │Rst│ ( │ ) │ / │ * │ - │\n     * └───┘───┌───┬───┬───┬───┐\n     * │ A │ B │Hom│ ↑ │PgU│   │\n     * ├───┼───├───┼───┼───┤ + │\n     * │ C │ D │ ← │   │ → │   │\n     * ├───┼───├───┼───┼───┼───┤\n     * │ E │ F │End│ ↓ │PgD│   │\n     * ├───┼───├───┴───┼───┤Ent│\n     * │Ctl│TG1│Insert │Del│   │\n     * └───┴───└───────┴───┴───┘\n     */\n    [1] = LAYOUT_numpad_5x6(\n        QK_BOOT,   _______, _______, _______, _______, _______,\n        _______, _______, KC_HOME, KC_UP,   KC_PGUP,\n        _______, _______, KC_LEFT, XXXXXXX, KC_RGHT, _______,\n        _______, _______, KC_END,  KC_DOWN, KC_PGDN,\n        _______, _______, KC_INS,           KC_DEL,  _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/numpad_5x6/info.json",
    "content": "{\n    \"keyboard_name\": \"5x6 number pad layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_numpad_5x6\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n                {\"x\":5, \"y\":1, \"h\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n\n                {\"x\":0, \"y\":4},\n                {\"x\":1, \"y\":4},\n                {\"x\":2, \"y\":4, \"w\":2},\n                {\"x\":4, \"y\":4},\n                {\"x\":5, \"y\":3, \"h\":2}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/numpad_5x6/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",{h:2},\"\"],\n[\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",{h:2},\"\"],\n[\"\",\"\",{w:2},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/numpad_5x6/readme.md",
    "content": "# numpad_5x6\n\n    LAYOUT_numpad_5x6\n"
  },
  {
    "path": "layouts/default/numpad_6x4/default_numpad_6x4/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┐\n     * │Esc│Tab│MO1│Bsp│\n     * ├───┼───┼───┼───┤\n     * │Num│ / │ * │ - │\n     * ├───┼───┼───┼───┤\n     * │ 7 │ 8 │ 9 │   │\n     * ├───┼───┼───┤ + │\n     * │ 4 │ 5 │ 6 │   │\n     * ├───┼───┼───┼───┤\n     * │ 1 │ 2 │ 3 │   │\n     * ├───┴───┼───┤Ent│\n     * │ 0     │ . │   │\n     * └───────┴───┴───┘\n     */\n    [0] = LAYOUT_numpad_6x4(\n        KC_ESC,  KC_TAB,  MO(1),   KC_BSPC,\n        KC_NUM,  KC_PSLS, KC_PAST, KC_PMNS,\n        KC_P7,   KC_P8,   KC_P9,\n        KC_P4,   KC_P5,   KC_P6,   KC_PPLS,\n        KC_P1,   KC_P2,   KC_P3,\n        KC_P0,            KC_PDOT, KC_PENT\n    ),\n\n    /*\n     * ┌───┐───┬───┬───┐\n     * │Rst│Tab│MO1│Bsp│\n     * └───┘───┼───┼───┤\n     * │Num│ / │ * │ - │\n     * ┌───┬───┬───┐───┤\n     * │Hom│ ↑ │PgU│   │\n     * ├───┼───┼───┤ + │\n     * │ ← │   │ → │   │\n     * ├───┼───┼───┤───┤\n     * │End│ ↓ │PgD│   │\n     * ├───┴───┼───┤Ent│\n     * │Insert │Del│   │\n     * └───────┴───┘───┘\n     */\n    [1] = LAYOUT_numpad_6x4(\n        QK_BOOT,   _______, _______, _______,\n        _______, _______, _______, _______,\n        KC_HOME, KC_UP,   KC_PGUP,\n        KC_LEFT, XXXXXXX, KC_RGHT, _______,\n        KC_END,  KC_DOWN, KC_PGDN,\n        KC_INS,           KC_DEL,  _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/numpad_6x4/info.json",
    "content": "{\n    \"keyboard_name\": \"6x4 number pad layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_numpad_6x4\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":2, \"h\":2},\n\n                {\"x\":0, \"y\":4},\n                {\"x\":1, \"y\":4},\n                {\"x\":2, \"y\":4},\n\n                {\"x\":0, \"y\":5, \"w\":2},\n                {\"x\":2, \"y\":5},\n                {\"x\":3, \"y\":4, \"h\":2}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/numpad_6x4/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",{h:2},\"\"],\n[\"\",\"\",\"\"],\n[\"\",\"\",\"\",{h:2},\"\"],\n[{w:2},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/numpad_6x4/readme.md",
    "content": "# numpad_6x4\n\n    LAYOUT_numpad_6x4\n"
  },
  {
    "path": "layouts/default/numpad_6x5/default_numpad_6x5/keymap.c",
    "content": "// Copyright 2021 QMK / NachoxMacho\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┌───┬───┬───┬───┐\n     * │ ` │Esc│Tab│MO1│Bsp│\n     * ├───┼───┼───┼───┼───┤\n     * │ , │Num│ / │ * │ - │\n     * ├───┼───┼───┼───┼───┤\n     * │Gui│ 7 │ 8 │ 9 │   │\n     * ├───┼───┼───┼───┤ + │\n     * │Alt│ 4 │ 5 │ 6 │   │\n     * ├───┼───┼───┼───┼───┤\n     * │Shi│ 1 │ 2 │ 3 │   │\n     * ├───┼───┴───┼───┤Ent│\n     * │Ctr│ 0     │ . │   │\n     * └───┴───────┴───┴───┘\n     */\n    [0] = LAYOUT_numpad_6x5(\n        KC_GRV,  KC_ESC, KC_TAB,  MO(1),   KC_BSPC,\n        KC_COMM, KC_NUM, KC_PSLS, KC_PAST, KC_PMNS,\n        KC_LGUI, KC_P7,  KC_P8,   KC_P9,\n        KC_LALT, KC_P4,  KC_P5,   KC_P6,   KC_PPLS,\n        KC_LSFT, KC_P1,  KC_P2,   KC_P3,\n        KC_LCTL, KC_P0,           KC_PDOT, KC_PENT\n    ),\n\n    /*\n     * ┌───┌───┐───┬───┬───┐\n     * │ ` │Rst│Tab│MO1│Bsp│\n     * ├───┼───┘───┼───┼───┤\n     * │ , │Num│ / │ * │ - │\n     * ├───┼───┼───┼───┼───┤\n     * │Gui│Hom│ ↑ │PgU│   │\n     * ├───┼───┼───┼───┤ + │\n     * │Alt│ ← │   │ → │   │\n     * ├───┼───┼───┼───┤───┤\n     * │Shi│End│ ↓ │PgD│   │\n     * ├───┼───┴───┼───┤Ent│\n     * │Ctr│Insert │Del│   │\n     * └───┴───────┴───┘───┘\n     */\n    [1] = LAYOUT_numpad_6x5(\n        _______, QK_BOOT,   _______, _______, _______,\n        _______, _______, _______, _______, _______,\n        _______, KC_HOME, KC_UP,   KC_PGUP,\n        _______, KC_LEFT, XXXXXXX, KC_RGHT, _______,\n        _______, KC_END,  KC_DOWN, KC_PGDN,\n        _______, KC_INS,           KC_DEL,  _______\n    )\n};\n"
  },
  {
    "path": "layouts/default/numpad_6x5/info.json",
    "content": "{\n    \"keyboard_name\": \"6x5 number pad layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_numpad_6x5\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":2, \"h\":2},\n\n                {\"x\":0, \"y\":4},\n                {\"x\":1, \"y\":4},\n                {\"x\":2, \"y\":4},\n                {\"x\":3, \"y\":4},\n\n                {\"x\":0, \"y\":5},\n                {\"x\":1, \"y\":5, \"w\":2},\n                {\"x\":3, \"y\":5},\n                {\"x\":4, \"y\":4, \"h\":2}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/numpad_6x5/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",{h:2},\"\"],\n[\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",{h:2},\"\"],\n[\"\",{w:2},\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/numpad_6x5/readme.md",
    "content": "# numpad_6x5\n\n    LAYOUT_numpad_6x5\n"
  },
  {
    "path": "layouts/default/ortho_1x1/default_ortho_1x1/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐\n     * │ A │\n     * └───┘\n     */\n    [0] = LAYOUT_ortho_1x1(\n        KC_A\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_1x1/info.json",
    "content": "{\n    \"keyboard_name\": \"1x1 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_1x1\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_1x1/layout.json",
    "content": "[{a:7},\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_1x1/readme.md",
    "content": "# ortho_1x1\n\n    LAYOUT_ortho_1x1\n"
  },
  {
    "path": "layouts/default/ortho_2x3/default_ortho_2x3/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┐\n     * │ A │ B │ C │\n     * ├───┼───┼───┤\n     * │ D │ E │ F │\n     * └───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_2x3(\n        KC_A,    KC_B,    KC_C,\n        KC_D,    KC_E,    KC_F\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_2x3/info.json",
    "content": "{\n    \"keyboard_name\": \"2x3 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_2x3\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_2x3/layout.json",
    "content": "[\"\",\"\",\"\"],\n[\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_2x3/readme.md",
    "content": "# ortho_2x3\n\n    LAYOUT_ortho_2x3\n"
  },
  {
    "path": "layouts/default/ortho_2x6/default_ortho_2x6/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┐\n     * │ A │ B │ C │ D │ E │ F │\n     * ├───┼───┼───┼───┼───┼───┤\n     * │ G │ H │ I │ J │ K │ L │\n     * └───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_2x6(\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,\n        KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_2x6/info.json",
    "content": "{\n    \"keyboard_name\": \"2x6 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_2x6\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_2x6/layout.json",
    "content": "[\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_2x6/readme.md",
    "content": "# ortho_2x6\n\n    LAYOUT_ortho_2x6\n"
  },
  {
    "path": "layouts/default/ortho_3x10/default_ortho_3x10/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_3x10(\n        KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,    KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,\n        KC_A,    KC_S,    KC_D,    KC_F,    KC_G,    KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN,\n        KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_3x10/info.json",
    "content": "{\n    \"keyboard_name\": \"3x10 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_3x10\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n                {\"x\":5, \"y\":2},\n                {\"x\":6, \"y\":2},\n                {\"x\":7, \"y\":2},\n                {\"x\":8, \"y\":2},\n                {\"x\":9, \"y\":2}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_3x10/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_3x10/readme.md",
    "content": "# ortho_3x10\n\n    LAYOUT_ortho_3x10\n"
  },
  {
    "path": "layouts/default/ortho_3x3/default_ortho_3x3/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┐\n     * │ A │ B │ C │\n     * ├───┼───┼───┤\n     * │ D │ E │ F │\n     * ├───┼───┼───┤\n     * │ G │ H │ I │\n     * └───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_3x3(\n        KC_A,    KC_B,    KC_C,\n        KC_D,    KC_E,    KC_F,\n        KC_G,    KC_H,    KC_I\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_3x3/info.json",
    "content": "{\n    \"keyboard_name\": \"3x3 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_3x3\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_3x3/layout.json",
    "content": "[\"\",\"\",\"\"],\n[\"\",\"\",\"\"],\n[\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_3x3/readme.md",
    "content": "# ortho_3x3\n\n    LAYOUT_ortho_3x3\n"
  },
  {
    "path": "layouts/default/ortho_4x10/default_ortho_4x10/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_4x10(\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_4x10/info.json",
    "content": "{\n    \"keyboard_name\": \"4x10 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_4x10\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n                {\"x\":5, \"y\":2},\n                {\"x\":6, \"y\":2},\n                {\"x\":7, \"y\":2},\n                {\"x\":8, \"y\":2},\n                {\"x\":9, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n                {\"x\":5, \"y\":3},\n                {\"x\":6, \"y\":3},\n                {\"x\":7, \"y\":3},\n                {\"x\":8, \"y\":3},\n                {\"x\":9, \"y\":3}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_4x10/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_4x10/readme.md",
    "content": "# ortho_4x10\n\n    LAYOUT_ortho_4x10\n"
  },
  {
    "path": "layouts/default/ortho_4x12/default_ortho_4x12/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_4x12(\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_4x12/info.json",
    "content": "{\n    \"keyboard_name\": \"4x12 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_4x12\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n                {\"x\":5, \"y\":2},\n                {\"x\":6, \"y\":2},\n                {\"x\":7, \"y\":2},\n                {\"x\":8, \"y\":2},\n                {\"x\":9, \"y\":2},\n                {\"x\":10, \"y\":2},\n                {\"x\":11, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n                {\"x\":5, \"y\":3},\n                {\"x\":6, \"y\":3},\n                {\"x\":7, \"y\":3},\n                {\"x\":8, \"y\":3},\n                {\"x\":9, \"y\":3},\n                {\"x\":10, \"y\":3},\n                {\"x\":11, \"y\":3}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_4x12/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_4x12/readme.md",
    "content": "# ortho_4x12\n\n    LAYOUT_ortho_4x12\n"
  },
  {
    "path": "layouts/default/ortho_4x16/default_ortho_4x16/keymap.c",
    "content": "// Copyright 2022 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n\n    /* ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Tab│ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │BS │ 7 │ 8 │ 9 │ / │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┬───┬───┬───┬───┼───┼───┼───┼───┤\n     * │Esc│ A │ S │ D │ F │ G │ H │ J │ K │ L │;: │'\" │ 4 │ 5 │ 6 │ * │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Sft│ Z │ X │ C │ V │ B │ N │ M │,< │.> │/? │Ent│ 1 │ 2 │ 3 │ - │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ ` │Ctl│GUI│Alt│Fn │   │   │Fn2│Lft│Dwn│Up │Rgt│ 0 │ . │Ent│ + │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_4x16(\n        KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,    KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,    KC_BSPC, KC_P7,   KC_P8,   KC_P9,   KC_PSLS,\n        KC_ESC,  KC_A,    KC_S,    KC_D,    KC_F,    KC_G,    KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_QUOT, KC_P4,   KC_P5,   KC_P6,   KC_PAST,\n        KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_ENT,  KC_P1,   KC_P2,   KC_P3,   KC_PMNS,\n        KC_GRV,  KC_LCTL, KC_LGUI, KC_LALT, MO(1),   KC_SPC,  KC_SPC,  MO(2),   KC_LEFT, KC_DOWN, KC_UP,   KC_RGHT, KC_P0,   KC_PDOT, KC_PENT, KC_PPLS\n    ),\n\n    /* Lower\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ~ │ ! │ @ │ # │ $ │ % │ ^ │ & │ * │ ( │ ) │BS │   │   │   │   │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Del│F1 │F2 │F3 │F4 │F5 │F6 │ _ │ + │ { │ } │ │ │   │   │   │   │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │   │F7 │F8 │F9 │F10│F11│F12│#~ │\\| │Hom│End│   │   │   │   │   │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │   │   │   │   │   │   │   │Nxt│Vl─│Vl+│Ply│   │   │   │   │   │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [1] = LAYOUT_ortho_4x16(\n        KC_TILD, KC_EXLM, KC_AT,   KC_HASH, KC_DLR,  KC_PERC, KC_CIRC, KC_AMPR,    KC_ASTR,    KC_LPRN, KC_RPRN, KC_BSPC, _______, _______, _______, _______,\n        KC_DEL,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_UNDS,    KC_PLUS,    KC_LCBR, KC_RCBR, KC_PIPE, _______, _______, _______, _______,\n        _______, KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  S(KC_NUHS), S(KC_NUBS), KC_HOME, KC_END,  _______, _______, _______, _______, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______,    KC_MNXT,    KC_VOLD, KC_VOLU, KC_MPLY, _______, _______, _______, _______\n    ),\n\n    /* Raise\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │BS │   │   │   │   │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Del│F1 │F2 │F3 │F4 │F5 │F6 │ - │ = │ [ │ ] │ \\ │   │   │   │   │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │   │F7 │F8 │F9 │F10│F11│F12│ # │ \\ │PUp│PDn│   │   │   │   │   │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │   │   │   │   │   │   │   │   │Nxt│Vl-│Vl+│Ply│   │   │   │   │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [2] = LAYOUT_ortho_4x16(\n        KC_GRV,  KC_1,    KC_2,    KC_3,    KC_4,    KC_5,    KC_6,    KC_7,    KC_8,    KC_9,    KC_0,    KC_BSPC, _______, _______, _______, _______,\n        KC_DEL,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_MINS, KC_EQL,  KC_LBRC, KC_RBRC, KC_BSLS, _______, _______, _______, _______,\n        _______, KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_NUHS, KC_NUBS, KC_PGUP, KC_PGDN, _______, _______, _______, _______, _______,\n        _______, _______, _______, _______, _______, _______, _______, _______, KC_MNXT, KC_VOLD, KC_VOLU, KC_MPLY, _______, _______, _______, _______\n    ),\n\n};\n"
  },
  {
    "path": "layouts/default/ortho_4x16/info.json",
    "content": "{\n    \"keyboard_name\": \"4x16 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_4x16\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n                {\"x\":12, \"y\":1},\n                {\"x\":13, \"y\":1},\n                {\"x\":14, \"y\":1},\n                {\"x\":15, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n                {\"x\":5, \"y\":2},\n                {\"x\":6, \"y\":2},\n                {\"x\":7, \"y\":2},\n                {\"x\":8, \"y\":2},\n                {\"x\":9, \"y\":2},\n                {\"x\":10, \"y\":2},\n                {\"x\":11, \"y\":2},\n                {\"x\":12, \"y\":2},\n                {\"x\":13, \"y\":2},\n                {\"x\":14, \"y\":2},\n                {\"x\":15, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n                {\"x\":5, \"y\":3},\n                {\"x\":6, \"y\":3},\n                {\"x\":7, \"y\":3},\n                {\"x\":8, \"y\":3},\n                {\"x\":9, \"y\":3},\n                {\"x\":10, \"y\":3},\n                {\"x\":11, \"y\":3},\n                {\"x\":12, \"y\":3},\n                {\"x\":13, \"y\":3},\n                {\"x\":14, \"y\":3},\n                {\"x\":15, \"y\":3}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_4x16/layout.json",
    "content": "[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_4x16/readme.md",
    "content": "# ortho_4x16\n\n    LAYOUT_ortho_4x16\n"
  },
  {
    "path": "layouts/default/ortho_4x4/default_ortho_4x4/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┐\n     * │ 7 │ 8 │ 9 │ / │\n     * ├───┼───┼───┼───┤\n     * │ 4 │ 5 │ 6 │ * │\n     * ├───┼───┼───┼───┤\n     * │ 1 │ 2 │ 3 │ - │\n     * ├───┼───┼───┼───┤\n     * │ 0 │ . │Ent│ + │\n     * └───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_4x4(\n        KC_P7,   KC_P8,   KC_P9,   KC_PSLS,\n        KC_P4,   KC_P5,   KC_P6,   KC_PAST,\n        KC_P1,   KC_P2,   KC_P3,   KC_PMNS,\n        KC_P0,   KC_PDOT, KC_PENT, KC_PPLS\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_4x4/info.json",
    "content": "{\n    \"keyboard_name\": \"4x4 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_4x4\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_4x4/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_4x4/readme.md",
    "content": "# ortho_4x4\n\n    LAYOUT_ortho_4x4\n"
  },
  {
    "path": "layouts/default/ortho_4x6/default_ortho_4x6/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┐\n     * │Tab│ Q │ W │ E │ R │ T │\n     * ├───┼───┼───┼───┼───┼───┤\n     * │Bsp│ A │ S │ D │ F │ G │\n     * ├───┼───┼───┼───┼───┼───┤\n     * │Sft│ Z │ X │ C │ V │ B │\n     * ├───┼───┼───┼───┼───┼───┤\n     * │Ctl│App│GUI│Alt│Spc│Spc│\n     * └───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_4x6(\n        KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,\n        KC_BSPC, KC_A,    KC_S,    KC_D,    KC_F,    KC_G,\n        KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,\n        KC_LCTL, KC_MENU, KC_LGUI, KC_LALT, KC_SPC,  KC_SPC\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_4x6/info.json",
    "content": "{\n    \"keyboard_name\": \"4x6 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_4x6\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n                {\"x\":5, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n                {\"x\":5, \"y\":3}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_4x6/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_4x6/readme.md",
    "content": "# ortho_4x6\n\n    LAYOUT_ortho_4x6\n"
  },
  {
    "path": "layouts/default/ortho_5x10/default_ortho_5x10/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_5x10(\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_5x10/info.json",
    "content": "{\n    \"keyboard_name\": \"5x10 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_5x10\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n                {\"x\":5, \"y\":2},\n                {\"x\":6, \"y\":2},\n                {\"x\":7, \"y\":2},\n                {\"x\":8, \"y\":2},\n                {\"x\":9, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n                {\"x\":5, \"y\":3},\n                {\"x\":6, \"y\":3},\n                {\"x\":7, \"y\":3},\n                {\"x\":8, \"y\":3},\n                {\"x\":9, \"y\":3},\n\n                {\"x\":0, \"y\":4},\n                {\"x\":1, \"y\":4},\n                {\"x\":2, \"y\":4},\n                {\"x\":3, \"y\":4},\n                {\"x\":4, \"y\":4},\n                {\"x\":5, \"y\":4},\n                {\"x\":6, \"y\":4},\n                {\"x\":7, \"y\":4},\n                {\"x\":8, \"y\":4},\n                {\"x\":9, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_5x10/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_5x10/readme.md",
    "content": "# ortho_5x10\n\n    LAYOUT_ortho_5x10\n"
  },
  {
    "path": "layouts/default/ortho_5x12/default_ortho_5x12/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_5x12(\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_5x12/info.json",
    "content": "{\n    \"keyboard_name\": \"5x12 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_5x12\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n                {\"x\":5, \"y\":2},\n                {\"x\":6, \"y\":2},\n                {\"x\":7, \"y\":2},\n                {\"x\":8, \"y\":2},\n                {\"x\":9, \"y\":2},\n                {\"x\":10, \"y\":2},\n                {\"x\":11, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n                {\"x\":5, \"y\":3},\n                {\"x\":6, \"y\":3},\n                {\"x\":7, \"y\":3},\n                {\"x\":8, \"y\":3},\n                {\"x\":9, \"y\":3},\n                {\"x\":10, \"y\":3},\n                {\"x\":11, \"y\":3},\n\n                {\"x\":0, \"y\":4},\n                {\"x\":1, \"y\":4},\n                {\"x\":2, \"y\":4},\n                {\"x\":3, \"y\":4},\n                {\"x\":4, \"y\":4},\n                {\"x\":5, \"y\":4},\n                {\"x\":6, \"y\":4},\n                {\"x\":7, \"y\":4},\n                {\"x\":8, \"y\":4},\n                {\"x\":9, \"y\":4},\n                {\"x\":10, \"y\":4},\n                {\"x\":11, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_5x12/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_5x12/readme.md",
    "content": "# ortho_5x12\n\n    LAYOUT_ortho_5x12\n"
  },
  {
    "path": "layouts/default/ortho_5x13/default_ortho_5x13/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │Bsp│\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Tab│Q  │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │#  │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │Ent│\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Sft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ ↑ │ / │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Ctl│GUI│LWR│Alt│RSE│   │   │   │Alt│Sft│←  │ ↓ │ → │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_5x13(\n        KC_ESC,  KC_1,     KC_2,  KC_3,    KC_4,  KC_5,    KC_6,    KC_7,    KC_8,    KC_9,    KC_0,    KC_MINS, KC_BSPC,\n        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,\n        KC_NUHS, KC_A,     KC_S,  KC_D,    KC_F,  KC_G,    KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_QUOT, KC_ENT,\n        KC_LSFT, KC_NUBS,  KC_Z,  KC_X,    KC_C,  KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_UP,   KC_SLSH,\n        KC_LCTL, KC_LGUI,  TT(1), KC_LALT, TT(2), KC_SPC,  KC_SPC,  KC_SPC,  KC_RALT, KC_RSFT, KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n    [1] = LAYOUT_ortho_5x13(\n       KC_GRV , KC_MUTE, KC_VOLU, KC_VOLD, KC_MPRV, KC_MPLY, KC_MNXT, G(KC_P), KC_SLEP, KC_WAKE, KC_PSCR, KC_DEL , KC_EQL  ,\n       KC_BTN3, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ ,\n       KC_BTN2, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ ,\n       _______, KC_BTN1, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_MS_U, _______ ,\n       _______, KC_BTN4, _______, _______, _______, _______, _______, _______, _______, _______, KC_MS_L, KC_MS_D, KC_MS_R\n    ),\n    [2] = LAYOUT_ortho_5x13(\n      KC_ESC , KC_F1  , KC_F2  , KC_F3  , KC_F4  , KC_F5  , KC_F6  , KC_F7  , KC_F8  , KC_F9  , KC_F10 , KC_F11 , KC_F12  ,\n      _______, _______, _______, _______,  QK_RBT, _______, _______, _______, _______, _______, _______, _______, _______ ,\n      KC_CAPS, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ ,\n      _______, _______, _______, _______, _______, _______, QK_BOOT, _______, _______, _______, _______, KC_WH_U, _______ ,\n      _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_WH_L, KC_WH_D, KC_WH_R\n    ),\n};\n"
  },
  {
    "path": "layouts/default/ortho_5x13/info.json",
    "content": "{\n    \"keyboard_name\": \"5x13 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_5x13\": {\n            \"layout\": [\n                {\"x\":0 , \"y\":0},\n                {\"x\":1 , \"y\":0},\n                {\"x\":2 , \"y\":0},\n                {\"x\":3 , \"y\":0},\n                {\"x\":4 , \"y\":0},\n                {\"x\":5 , \"y\":0},\n                {\"x\":6 , \"y\":0},\n                {\"x\":7 , \"y\":0},\n                {\"x\":8 , \"y\":0},\n                {\"x\":9 , \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n\n                {\"x\":0 , \"y\":1},\n                {\"x\":1 , \"y\":1},\n                {\"x\":2 , \"y\":1},\n                {\"x\":3 , \"y\":1},\n                {\"x\":4 , \"y\":1},\n                {\"x\":5 , \"y\":1},\n                {\"x\":6 , \"y\":1},\n                {\"x\":7 , \"y\":1},\n                {\"x\":8 , \"y\":1},\n                {\"x\":9 , \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n                {\"x\":12, \"y\":1},\n\n                {\"x\":0 , \"y\":2},\n                {\"x\":1 , \"y\":2},\n                {\"x\":2 , \"y\":2},\n                {\"x\":3 , \"y\":2},\n                {\"x\":4 , \"y\":2},\n                {\"x\":5 , \"y\":2},\n                {\"x\":6 , \"y\":2},\n                {\"x\":7 , \"y\":2},\n                {\"x\":8 , \"y\":2},\n                {\"x\":9 , \"y\":2},\n                {\"x\":10, \"y\":2},\n                {\"x\":11, \"y\":2},\n                {\"x\":12, \"y\":2},\n\n                {\"x\":0 , \"y\":3},\n                {\"x\":1 , \"y\":3},\n                {\"x\":2 , \"y\":3},\n                {\"x\":3 , \"y\":3},\n                {\"x\":4 , \"y\":3},\n                {\"x\":5 , \"y\":3},\n                {\"x\":6 , \"y\":3},\n                {\"x\":7 , \"y\":3},\n                {\"x\":8 , \"y\":3},\n                {\"x\":9 , \"y\":3},\n                {\"x\":10, \"y\":3},\n                {\"x\":11, \"y\":3},\n                {\"x\":12, \"y\":3},\n\n                {\"x\":0 , \"y\":4},\n                {\"x\":1 , \"y\":4},\n                {\"x\":2 , \"y\":4},\n                {\"x\":3 , \"y\":4},\n                {\"x\":4 , \"y\":4},\n                {\"x\":5 , \"y\":4},\n                {\"x\":6 , \"y\":4},\n                {\"x\":7 , \"y\":4},\n                {\"x\":8 , \"y\":4},\n                {\"x\":9 , \"y\":4},\n                {\"x\":10, \"y\":4},\n                {\"x\":11, \"y\":4},\n                {\"x\":12, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_5x13/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_5x13/readme.md",
    "content": "# ortho_5x13\n\n    LAYOUT_ortho_5x13\n"
  },
  {
    "path": "layouts/default/ortho_5x14/default_ortho_5x14/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Esc│Tab│ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Del│Cap│ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Sft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Sft│ ↑ │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ ← │ → │Ctl│GUI│Alt│Ent│Ent│   │   │Alt│GUI│App│Ctl│ ↓ │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_5x14(\n        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_BSPC,\n        KC_ESC,  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,\n        KC_DEL,  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_NUHS,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_UP,\n        KC_LEFT, KC_RGHT, KC_LCTL, KC_LGUI, KC_LALT, KC_ENT,  KC_ENT,  KC_SPC,  KC_SPC,  KC_RALT, KC_RGUI, KC_APP,  KC_RCTL, KC_DOWN\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_5x14/info.json",
    "content": "{\n    \"keyboard_name\": \"5x14 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_5x14\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n                {\"x\":12, \"y\":1},\n                {\"x\":13, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n                {\"x\":5, \"y\":2},\n                {\"x\":6, \"y\":2},\n                {\"x\":7, \"y\":2},\n                {\"x\":8, \"y\":2},\n                {\"x\":9, \"y\":2},\n                {\"x\":10, \"y\":2},\n                {\"x\":11, \"y\":2},\n                {\"x\":12, \"y\":2},\n                {\"x\":13, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n                {\"x\":5, \"y\":3},\n                {\"x\":6, \"y\":3},\n                {\"x\":7, \"y\":3},\n                {\"x\":8, \"y\":3},\n                {\"x\":9, \"y\":3},\n                {\"x\":10, \"y\":3},\n                {\"x\":11, \"y\":3},\n                {\"x\":12, \"y\":3},\n                {\"x\":13, \"y\":3},\n\n                {\"x\":0, \"y\":4},\n                {\"x\":1, \"y\":4},\n                {\"x\":2, \"y\":4},\n                {\"x\":3, \"y\":4},\n                {\"x\":4, \"y\":4},\n                {\"x\":5, \"y\":4},\n                {\"x\":6, \"y\":4},\n                {\"x\":7, \"y\":4},\n                {\"x\":8, \"y\":4},\n                {\"x\":9, \"y\":4},\n                {\"x\":10, \"y\":4},\n                {\"x\":11, \"y\":4},\n                {\"x\":12, \"y\":4},\n                {\"x\":13, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_5x14/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_5x14/readme.md",
    "content": "# ortho_5x14\n\n    LAYOUT_ortho_5x14\n"
  },
  {
    "path": "layouts/default/ortho_5x15/default_ortho_5x15/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │ J │ K │ L │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │ J │ K │ L │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │ J │ K │ L │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │ J │ K │ L │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │ F │ G │ H │ I │ J │ K │ L │ J │ K │ L │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_5x15(\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,    KC_J,    KC_K,    KC_L,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,    KC_J,    KC_K,    KC_L,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,    KC_J,    KC_K,    KC_L,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,    KC_J,    KC_K,    KC_L,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,    KC_J,    KC_K,    KC_L\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_5x15/info.json",
    "content": "{\n    \"keyboard_name\": \"5x15 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_5x15\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n                {\"x\":12, \"y\":1},\n                {\"x\":13, \"y\":1},\n                {\"x\":14, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n                {\"x\":5, \"y\":2},\n                {\"x\":6, \"y\":2},\n                {\"x\":7, \"y\":2},\n                {\"x\":8, \"y\":2},\n                {\"x\":9, \"y\":2},\n                {\"x\":10, \"y\":2},\n                {\"x\":11, \"y\":2},\n                {\"x\":12, \"y\":2},\n                {\"x\":13, \"y\":2},\n                {\"x\":14, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n                {\"x\":5, \"y\":3},\n                {\"x\":6, \"y\":3},\n                {\"x\":7, \"y\":3},\n                {\"x\":8, \"y\":3},\n                {\"x\":9, \"y\":3},\n                {\"x\":10, \"y\":3},\n                {\"x\":11, \"y\":3},\n                {\"x\":12, \"y\":3},\n                {\"x\":13, \"y\":3},\n                {\"x\":14, \"y\":3},\n\n                {\"x\":0, \"y\":4},\n                {\"x\":1, \"y\":4},\n                {\"x\":2, \"y\":4},\n                {\"x\":3, \"y\":4},\n                {\"x\":4, \"y\":4},\n                {\"x\":5, \"y\":4},\n                {\"x\":6, \"y\":4},\n                {\"x\":7, \"y\":4},\n                {\"x\":8, \"y\":4},\n                {\"x\":9, \"y\":4},\n                {\"x\":10, \"y\":4},\n                {\"x\":11, \"y\":4},\n                {\"x\":12, \"y\":4},\n                {\"x\":13, \"y\":4},\n                {\"x\":14, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_5x15/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_5x15/readme.md",
    "content": "# ortho_5x15\n\n    LAYOUT_ortho_5x15\n"
  },
  {
    "path": "layouts/default/ortho_5x4/default_ortho_5x4/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nenum custom_keycodes {\n    KC_P00 = SAFE_RANGE\n};\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┐\n     * │TG1│ / │ * │ - │\n     * ├───┼───┼───┼───┤\n     * │ 7 │ 8 │ 9 │ + │\n     * ├───┼───┼───┼───┤\n     * │ 4 │ 5 │ 6 │ % │\n     * ├───┼───┼───┼───┤\n     * │ 1 │ 2 │ 3 │ = │\n     * ├───┼───┼───┼───┤\n     * │ 0 │00 │ . │Ent│\n     * └───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_5x4(\n        TG(1),   KC_PSLS, KC_PAST, KC_PMNS,\n        KC_P7,   KC_P8,   KC_P9,   KC_PPLS,\n        KC_P4,   KC_P5,   KC_P6,   KC_PERC,\n        KC_P1,   KC_P2,   KC_P3,   KC_EQL,\n        KC_P0,   KC_P00,  KC_PDOT, KC_PENT\n    ),\n\n    /*\n     * ┌───┬───┬───┬───┐\n     * │TG1│ / │ * │ - │\n     * ┌───┬───┬───┐───┤\n     * │Hom│ ↑ │PgU│ + │\n     * ├───┼───┼───┤───┤\n     * │ ← │   │ → │ % │\n     * ├───┼───┼───┤───┤\n     * │End│ ↓ │PgD│ = │\n     * ├───┼───┼───┤───┤\n     * │Ins│   │Del│Ent│\n     * └───┴───┴───┘───┘\n     */\n    [1] = LAYOUT_ortho_5x4(\n        _______, _______, _______, _______,\n        KC_HOME, KC_UP,   KC_PGUP, _______,\n        KC_LEFT, XXXXXXX, KC_RGHT, _______,\n        KC_END,  KC_DOWN, KC_PGDN, _______,\n        KC_INS,  XXXXXXX, KC_DEL,  _______\n    )\n};\n\nbool process_record_user(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n        switch(keycode) {\n            case KC_P00:\n                tap_code(KC_P0);\n                tap_code(KC_P0);\n                return false;\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "layouts/default/ortho_5x4/info.json",
    "content": "{\n    \"keyboard_name\": \"5x4 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_5x4\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n\n                {\"x\":0, \"y\":4},\n                {\"x\":1, \"y\":4},\n                {\"x\":2, \"y\":4},\n                {\"x\":3, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_5x4/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_5x4/readme.md",
    "content": "# ortho_5x4\n\n    LAYOUT_ortho_5x4\n"
  },
  {
    "path": "layouts/default/ortho_5x5/default_ortho_5x5/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┐\n     * │ A │ B │ C │ D │ E │\n     * ├───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │\n     * ├───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │\n     * ├───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │\n     * ├───┼───┼───┼───┼───┤\n     * │ A │ B │ C │ D │ E │\n     * └───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_5x5(\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E,\n        KC_A,    KC_B,    KC_C,    KC_D,    KC_E\n    )\n};\n"
  },
  {
    "path": "layouts/default/ortho_5x5/info.json",
    "content": "{\n    \"keyboard_name\": \"5x5 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_5x5\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n\n                {\"x\":0, \"y\":4},\n                {\"x\":1, \"y\":4},\n                {\"x\":2, \"y\":4},\n                {\"x\":3, \"y\":4},\n                {\"x\":4, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_5x5/layout.json",
    "content": "[\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_5x5/readme.md",
    "content": "# ortho_5x5\n\n    LAYOUT_ortho_5x5"
  },
  {
    "path": "layouts/default/ortho_6x13/default_ortho_6x13/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Esc│ F1│ F2│ F3│ F4│ F5│ F6│ F7│ F8│ F9│F10│Del│Bsp│\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Tab│Q  │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │#  │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │Ent│\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Sft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ ↑ │ / │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Ctl│GUI│Lwr│Alt│Rse│   │   │   │Alt│Sft│←  │ ↓ │ → │\n     * └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_6x13(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_DEL,  KC_BSPC,\n        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,\n        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,\n        KC_NUHS, KC_A,    KC_S,    KC_D,    KC_F,    KC_G,    KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_QUOT, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_UP,   KC_SLSH,\n        KC_LCTL, KC_LGUI, TT(0),   KC_LALT, TT(2),   KC_SPC,  KC_SPC,  KC_SPC,  KC_RALT, KC_RSFT, KC_LEFT, KC_DOWN, KC_RGHT\n    ),\n    [1] = LAYOUT_ortho_6x13(\n       KC_GRV , KC_MUTE, KC_VOLU, KC_VOLD, KC_MPRV, KC_MPLY, KC_MNXT, G(KC_P), KC_SLEP, KC_WAKE, KC_PSCR, _______, _______ ,\n       _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ ,\n       KC_BTN3, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ ,\n       KC_BTN2, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ ,\n       _______, KC_BTN1, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_MS_U, _______ ,\n       _______, KC_BTN4, _______, _______, _______, _______, _______, _______, _______, _______, KC_MS_L, KC_MS_D, KC_MS_R\n    ),\n    [2] = LAYOUT_ortho_6x13(\n      KC_ESC , KC_F1  , KC_F2  , KC_F3  , KC_F4  , KC_F5  , KC_F6  , KC_F7  , KC_F8  , KC_F9  , KC_F10 , KC_F11 , KC_F12  ,\n      _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ ,\n      _______, _______, _______, _______,  QK_RBT, _______, _______, _______, _______, _______, _______, _______, _______ ,\n      KC_CAPS, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______ ,\n      _______, _______, _______, _______, _______, _______, QK_BOOT, _______, _______, _______, _______, KC_WH_U, _______ ,\n      _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_WH_L, KC_WH_D, KC_WH_R\n    ),\n};\n"
  },
  {
    "path": "layouts/default/ortho_6x13/info.json",
    "content": "{\n    \"keyboard_name\": \"6x13 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_6x13\": {\n            \"layout\": [\n                {\"x\":0 , \"y\":0},\n                {\"x\":1 , \"y\":0},\n                {\"x\":2 , \"y\":0},\n                {\"x\":3 , \"y\":0},\n                {\"x\":4 , \"y\":0},\n                {\"x\":5 , \"y\":0},\n                {\"x\":6 , \"y\":0},\n                {\"x\":7 , \"y\":0},\n                {\"x\":8 , \"y\":0},\n                {\"x\":9 , \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n\n                {\"x\":0 , \"y\":1},\n                {\"x\":1 , \"y\":1},\n                {\"x\":2 , \"y\":1},\n                {\"x\":3 , \"y\":1},\n                {\"x\":4 , \"y\":1},\n                {\"x\":5 , \"y\":1},\n                {\"x\":6 , \"y\":1},\n                {\"x\":7 , \"y\":1},\n                {\"x\":8 , \"y\":1},\n                {\"x\":9 , \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n                {\"x\":12, \"y\":1},\n\n                {\"x\":0 , \"y\":2},\n                {\"x\":1 , \"y\":2},\n                {\"x\":2 , \"y\":2},\n                {\"x\":3 , \"y\":2},\n                {\"x\":4 , \"y\":2},\n                {\"x\":5 , \"y\":2},\n                {\"x\":6 , \"y\":2},\n                {\"x\":7 , \"y\":2},\n                {\"x\":8 , \"y\":2},\n                {\"x\":9 , \"y\":2},\n                {\"x\":10, \"y\":2},\n                {\"x\":11, \"y\":2},\n                {\"x\":12, \"y\":2},\n\n                {\"x\":0 , \"y\":3},\n                {\"x\":1 , \"y\":3},\n                {\"x\":2 , \"y\":3},\n                {\"x\":3 , \"y\":3},\n                {\"x\":4 , \"y\":3},\n                {\"x\":5 , \"y\":3},\n                {\"x\":6 , \"y\":3},\n                {\"x\":7 , \"y\":3},\n                {\"x\":8 , \"y\":3},\n                {\"x\":9 , \"y\":3},\n                {\"x\":10, \"y\":3},\n                {\"x\":11, \"y\":3},\n                {\"x\":12, \"y\":3},\n\n                {\"x\":0 , \"y\":4},\n                {\"x\":1 , \"y\":4},\n                {\"x\":2 , \"y\":4},\n                {\"x\":3 , \"y\":4},\n                {\"x\":4 , \"y\":4},\n                {\"x\":5 , \"y\":4},\n                {\"x\":6 , \"y\":4},\n                {\"x\":7 , \"y\":4},\n                {\"x\":8 , \"y\":4},\n                {\"x\":9 , \"y\":4},\n                {\"x\":10, \"y\":4},\n                {\"x\":11, \"y\":4},\n                {\"x\":12, \"y\":4},\n               \n                {\"x\":0 , \"y\":5},\n                {\"x\":1 , \"y\":5},\n                {\"x\":2 , \"y\":5},\n                {\"x\":3 , \"y\":5},\n                {\"x\":4 , \"y\":5},\n                {\"x\":5 , \"y\":5},\n                {\"x\":6 , \"y\":5},\n                {\"x\":7 , \"y\":5},\n                {\"x\":8 , \"y\":5},\n                {\"x\":9 , \"y\":5},\n                {\"x\":10, \"y\":5},\n                {\"x\":11, \"y\":5},\n                {\"x\":12, \"y\":5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_6x13/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_6x13/readme.md",
    "content": "# ortho_6x13\n\n    LAYOUT_ortho_6x13\n"
  },
  {
    "path": "layouts/default/ortho_6x4/default_ortho_6x4/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nenum custom_keycodes {\n    KC_P00 = SAFE_RANGE\n};\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┐\n     * │Esc│Tab│MO1│Bsp│\n     * ├───┼───┼───┼───┤\n     * │Num│ / │ * │ - │\n     * ├───┼───┼───┼───┤\n     * │ 7 │ 8 │ 9 │ + │\n     * ├───┼───┼───┼───┤\n     * │ 4 │ 5 │ 6 │ % │\n     * ├───┼───┼───┼───┤\n     * │ 1 │ 2 │ 3 │ = │\n     * ├───┼───┼───┼───┤\n     * │ 0 │00 │ . │Ent│\n     * └───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_ortho_6x4(\n        KC_ESC,  KC_TAB,  MO(1),   KC_BSPC,\n        KC_NUM,  KC_PSLS, KC_PAST, KC_PMNS,\n        KC_P7,   KC_P8,   KC_P9,   KC_PPLS,\n        KC_P4,   KC_P5,   KC_P6,   KC_PERC,\n        KC_P1,   KC_P2,   KC_P3,   KC_EQL,\n        KC_P0,   KC_P00,  KC_PDOT, KC_PENT\n    ),\n\n    /*\n     * ┌───┐───┬───┬───┐\n     * │Rst│Tab│MO1│Bsp│\n     * └───┘───┼───┼───┤\n     * │Num│ / │ * │ - │\n     * ┌───┬───┬───┐───┤\n     * │Hom│ ↑ │PgU│ + │\n     * ├───┼───┼───┤───┤\n     * │ ← │   │ → │ % │\n     * ├───┼───┼───┤───┤\n     * │End│ ↓ │PgD│ = │\n     * ├───┼───┼───┤───┤\n     * │Ins│   │Del│Ent│\n     * └───┴───┴───┘───┘\n     */\n    [1] = LAYOUT_ortho_6x4(\n        QK_BOOT,   _______, _______, _______,\n        _______, _______, _______, _______,\n        KC_HOME, KC_UP,   KC_PGUP, _______,\n        KC_LEFT, XXXXXXX, KC_RGHT, _______,\n        KC_END,  KC_DOWN, KC_PGDN, _______,\n        KC_INS,  XXXXXXX, KC_DEL,  _______\n    )\n};\n\nbool process_record_user(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n        switch (keycode) {\n            case KC_P00:\n                tap_code(KC_P0);\n                tap_code(KC_P0);\n                return false;\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "layouts/default/ortho_6x4/info.json",
    "content": "{\n    \"keyboard_name\": \"6x4 ortholinear layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_ortho_6x4\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n\n                {\"x\":0, \"y\":4},\n                {\"x\":1, \"y\":4},\n                {\"x\":2, \"y\":4},\n                {\"x\":3, \"y\":4},\n\n                {\"x\":0, \"y\":5},\n                {\"x\":1, \"y\":5},\n                {\"x\":2, \"y\":5},\n                {\"x\":3, \"y\":5}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/ortho_6x4/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/ortho_6x4/readme.md",
    "content": "# ortho_6x4\n\n    LAYOUT_ortho_6x4\n"
  },
  {
    "path": "layouts/default/planck_mit/default_planck_mit/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n     * │Tab│ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │Bsp│\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Esc│ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │\n     * ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n     * │Sft│ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Ent│\n     * ├───┼───┼───┼───┼───┼───┴───┼───┼───┼───┼───┼───┤\n     * │App│Ctl│Alt│GUI│ < │       │ > │ ← │ ↓ │ ↑ │ → │\n     * └───┴───┴───┴───┴───┴───────┴───┴───┴───┴───┴───┘\n     */\n    [0] = LAYOUT_planck_mit(\n        KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,    KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,    KC_BSPC,\n        KC_ESC,  KC_A,    KC_S,    KC_D,    KC_F,    KC_G,    KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_QUOT,\n        KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_ENT,\n        KC_APP,  KC_LCTL, KC_LALT, KC_LGUI, KC_LT,       KC_SPC,       KC_GT,   KC_LEFT, KC_DOWN, KC_UP,   KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/planck_mit/info.json",
    "content": "{\n    \"keyboard_name\": \"Planck MIT (4x12) layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_planck_mit\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n\n                {\"x\":0, \"y\":1},\n                {\"x\":1, \"y\":1},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1},\n                {\"x\":5, \"y\":1},\n                {\"x\":6, \"y\":1},\n                {\"x\":7, \"y\":1},\n                {\"x\":8, \"y\":1},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1},\n\n                {\"x\":0, \"y\":2},\n                {\"x\":1, \"y\":2},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2},\n                {\"x\":5, \"y\":2},\n                {\"x\":6, \"y\":2},\n                {\"x\":7, \"y\":2},\n                {\"x\":8, \"y\":2},\n                {\"x\":9, \"y\":2},\n                {\"x\":10, \"y\":2},\n                {\"x\":11, \"y\":2},\n\n                {\"x\":0, \"y\":3},\n                {\"x\":1, \"y\":3},\n                {\"x\":2, \"y\":3},\n                {\"x\":3, \"y\":3},\n                {\"x\":4, \"y\":3},\n                {\"x\":5, \"y\":3, \"w\":2},\n                {\"x\":7, \"y\":3},\n                {\"x\":8, \"y\":3},\n                {\"x\":9, \"y\":3},\n                {\"x\":10, \"y\":3},\n                {\"x\":11, \"y\":3}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/planck_mit/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[\"\",\"\",\"\",\"\",\"\",{w:2},\"\",\"\",\"\",\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/planck_mit/readme.md",
    "content": "# planck_mit\n\n    LAYOUT_planck_mit\n"
  },
  {
    "path": "layouts/default/readme.md",
    "content": "# Community Layouts\n\n## Summary of Layouts\n\n<details>\n<summary>60% Form Factor</summary>\n\n### `LAYOUT_60_abnt2`\n\n60% ABNT2 layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤\n│    │   │   │   │   │   │   │   │   │   │   │   │   │      │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬──┴─┬────┤\n│    │    │    │                        │    │    │    │    │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n```\n\n### `LAYOUT_60_ansi`\n\n60% ANSI layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n│        │   │   │   │   │   │   │   │   │   │   │          │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n│    │    │    │                        │    │    │    │    │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n```\n\n### `LAYOUT_60_ansi_arrow`\n\n60% ANSI layout with arrow cluster\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┬───┤\n│        │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_60_ansi_arrow_split_bs`\n\n60% ANSI layout with arrow cluster and split backspace\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┬───┤\n│        │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_60_ansi_split_bs_rshift`\n\n60% ANSI layout with split backspace and split right shift\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤\n│    │    │    │                        │    │    │    │    │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n```\n\n### `LAYOUT_60_ansi_tsangan`\n\n60% ANSI layout with Tsangan bottom row\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n│        │   │   │   │   │   │   │   │   │   │   │          │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤\n│     │   │     │                           │     │   │     │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘\n```\n\n### `LAYOUT_60_ansi_tsangan_split_bs_rshift`\n\n60% ANSI layout with Tsangan bottom row, split backspace, and split right shift\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤\n│     │   │     │                           │     │   │     │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘\n```\n\n### `LAYOUT_60_ansi_wkl`\n\n60% ANSI layout with no Windows (GUI) keys\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤\n│        │   │   │   │   │   │   │   │   │   │   │          │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤\n│     │   │     │                           │     │   │     │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘\n```\n\n### `LAYOUT_60_ansi_wkl_split_bs_rshift`\n\n60% ANSI layout with no Windows (GUI) keys, split backspace, and split right shift\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤\n│     │   │     │                           │     │   │     │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘\n```\n\n### `LAYOUT_60_hhkb`\n\n60% ANSI layout with HHKB bottom row, split backspace, and split right shift\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │\n└─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┘\n      │   │     │                           │     │   │\n      └───┴─────┴───────────────────────────┴─────┴───┘\n```\n\n### `LAYOUT_60_iso`\n\n60% ISO layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n│    │   │   │   │   │   │   │   │   │   │   │   │          │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤\n│    │    │    │                        │    │    │    │    │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n```\n\n### `LAYOUT_60_iso_arrow`\n\n60% ISO layout with arrow cluster\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬──┴┬───┤\n│    │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_60_iso_arrow_split_bs`\n\n60% ISO layout with arrow cluster and split backspace\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬──┴┬───┤\n│    │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_60_iso_split_bs_rshift`\n\n60% ISO layout with split backspace and split right shift\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤\n│    │    │    │                        │    │    │    │    │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘\n```\n\n### `LAYOUT_60_iso_tsangan`\n\n60% ISO layout with Tsangan bottom row\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n│    │   │   │   │   │   │   │   │   │   │   │   │          │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤\n│     │   │     │                           │     │   │     │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘\n```\n\n### `LAYOUT_60_iso_tsangan_split_bs_rshift`\n\n60% ISO layout with Tsangan bottom row, split backspace, and split right shift\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤\n│     │   │     │                           │     │   │     │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘\n```\n\n### `LAYOUT_60_iso_wkl`\n\n60% ISO layout with no Windows (GUI) keys\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤\n│    │   │   │   │   │   │   │   │   │   │   │   │          │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤\n│     │   │     │                           │     │   │     │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘\n```\n\n### `LAYOUT_60_iso_wkl_split_bs_rshift`\n\n60% ISO layout with no Windows (GUI) keys, split backspace, and split right shift\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤\n│     │   │     │                           │     │   │     │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘\n```\n\n### `LAYOUT_60_jis`\n\n60% JIS layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤\n│        │   │   │   │   │   │   │   │   │   │   │   │      │\n├────┬───┴┬──┴─┬─┴──┬┴───┴───┴───┴─┬─┴──┬┴───┼───┴┬──┴─┬────┤\n│    │    │    │    │              │    │    │    │    │    │\n└────┴────┴────┴────┴──────────────┴────┴────┴────┴────┴────┘\n```\n\n### `LAYOUT_64_ansi`\n\n64% ANSI layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬───┬───┤\n│       │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├────┬──┴─┬─┴──┬┴───┴───┴───┴───┴───┴───┼───┼───┼───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_64_iso`\n\n64% ISO layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├───┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴┬──┴─┬─┴──┬┴───┴───┴───┴───┴───┴───┼───┼───┼───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┘\n```\n</details>\n\n<details>\n<summary>65%+ Form Factor</summary>\n\n### `LAYOUT_65_ansi`\n\n65% ANSI layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_65_ansi_blocker`\n\n65% ANSI layout with blocker\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n│    │    │    │                        │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_65_ansi_blocker_split_bs`\n\n65% ANSI layout with blocker and split backspace\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n│    │    │    │                        │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_65_ansi_blocker_tsangan`\n\n65% ANSI layout with blocker and Tsangan bottom row\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n│     │   │     │                           │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_65_ansi_blocker_tsangan_split_bs`\n\n65% ANSI layout with blocker, Tsangan bottom row, and split backspace\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n│     │   │     │                           │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_65_ansi_split_bs`\n\n65% ANSI layout with split backspace\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_65_iso`\n\n65% ISO layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │   │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_65_iso_blocker`\n\n65% ISO layout with blocker\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │   │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n│    │    │    │                        │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_65_iso_blocker_split_bs`\n\n65% ISO layout with blocker and split backspace\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │   │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬─┬───┼───┼───┤\n│    │    │    │                        │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_65_iso_blocker_tsangan`\n\n65% ISO layout with blocker and Tsangan bottom row\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │   │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n│     │   │     │                           │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_65_iso_blocker_tsangan_split_bs`\n\n65% ISO layout with blocker, Tsangan bottom row, and split backspace\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │   │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬─┬───┼───┼───┤\n│     │   │     │                           │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_65_iso_split_bs`\n\n65% ISO layout with split backspace\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │   │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_66_ansi`\n\n66% ANSI layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴──────┬─┴─┐\n│        │   │   │   │   │   │   │   │   │   │   │        │   │\n├────┬───┼───┴┬──┴───┴───┴───┴───┴───┴─┬─┴──┬┴───┼────┬───┼───┼───┐\n│    │   │    │                        │    │    │    │   │   │   │\n└────┴───┴────┴────────────────────────┴────┴────┴────┴───┴───┴───┘\n```\n\n### `LAYOUT_66_iso`\n\n66% ISO layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴──┬─┴─┐\n│    │   │   │   │   │   │   │   │   │   │   │   │        │   │\n├────┼───┼───┴┬──┴───┴───┴───┴───┴───┴─┬─┴──┬┴───┼────┬───┼───┼───┐\n│    │   │    │                        │    │    │    │   │   │   │\n└────┴───┴────┴────────────────────────┴────┴────┴────┴───┴───┴───┘\n```\n\n### `LAYOUT_68_ansi`\n\n68% ANSI layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐┌───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       ││   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤├───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     ││   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤└───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │          ││   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬─┬──┴┼───┼───┐\n│    │    │    │                        │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_68_iso`\n\n68% ISO layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐┌───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       ││   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤├───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     ││   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │└───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │          ││   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬─┬──┴┼───┼───┐\n│    │    │    │                        │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┘ └───┴───┴───┘\n```\n</details>\n\n<details>\n<summary>75% Form Factor</summary>\n\n### `LAYOUT_75_ansi`\n\n75% ANSI layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┴───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │        │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_75_iso`\n\n75% ISO layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┴───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┤\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │   │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┤\n│    │    │    │                        │   │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┘\n```\n</details>\n\n<details>\n<summary>Tenkeyless (TKL) Form Factor</summary>\n\n### `LAYOUT_tkl_ansi`\n\nTKL ANSI layout\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐\n│    │    │    │                        │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_ansi_split_bs_rshift`\n\nTKL ANSI layout with split backspace and split right shift\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤ ┌───┼───┼───┐\n│    │    │    │                        │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_ansi_tsangan`\n\nTKL ANSI layout with Tsangan bottom row\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_ansi_tsangan_split_bs_rshift`\n\nTKL ANSI layout with Tsangan bottom row, split backspace, and split right shift\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_ansi_wkl`\n\nTKL ANSI layout with no Windows (GUI) keys\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_ansi_wkl_split_bs_rshift`\n\nTKL ANSI layout with no Windows (GUI) keys, split backspace, and split right shift\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_iso`\n\nTKL ISO layout\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐\n│    │    │    │                        │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_iso_split_bs_rshift`\n\nTKL ISO layout with split backspace and split right shift\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤ ┌───┼───┼───┐\n│    │    │    │                        │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_iso_tsangan`\n\nTKL ISO layout with Tsangan bottom row\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_iso_tsangan_split_bs_rshift`\n\nTKL ISO layout with Tsangan bottom row, split backspace, and split right shift\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_iso_wkl`\n\nTKL ISO layout with no Windows (GUI) keys\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_iso_wkl_split_bs_rshift`\n\nTKL ISO layout with no Windows (GUI) keys, split backspace, and split right shift\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_jis`\n\nTKL JIS layout\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │   │      │     │   │\n├────┬───┴┬──┴─┬─┴──┬┴───┴───┴───┴─┬─┴──┬┴───┼───┴┬──┴─┬────┤ ┌───┼───┼───┐\n│    │    │    │    │              │    │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────┴──────────────┴────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_ansi`\n\nTKL ANSI layout with F13 key\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐\n│    │    │    │                        │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_ansi_split_bs_rshift`\n\nTKL ANSI layout with F13 key, split backspace, and split right shift\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤ ┌───┼───┼───┐\n│    │    │    │                        │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_ansi_tsangan`\n\nTKL ANSI layout with F13 key and Tsangan bottom row\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_ansi_tsangan_split_bs_rshift`\n\nTKL ANSI layout with F13 key, Tsangan bottom row, split backspace, and split right shift\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_ansi_wkl`\n\nTKL ANSI layout with F13 key and no Windows (GUI) keys\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_ansi_wkl_split_bs_rshift`\n\nTKL ANSI layout with F13 key, no Windows (GUI) keys, split backspace, and split right shift\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_iso`\n\nTKL ISO layout with F13 key\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐\n│    │    │    │                        │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_iso_split_bs_rshift`\n\nTKL ISO layout with F13 key, split backspace, and split right shift\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤ ┌───┼───┼───┐\n│    │    │    │                        │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_iso_tsangan`\n\nTKL ISO layout with F13 key and Tsangan bottom row\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_iso_tsangan_split_bs_rshift`\n\nTKL ISO layout with F13 key, Tsangan bottom row, split backspace, and split right shift\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_iso_wkl`\n\nTKL ISO layout with F13 key and no Windows (GUI) keys\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_iso_wkl_split_bs_rshift`\n\nTKL ISO layout with F13 key, no Windows (GUI) keys, split backspace, and split right shift\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │     │   │\n├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n│     │   │     │                           │     │   │     │ │   │   │   │\n└─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_f13_jis`\n\nTKL JIS layout with F13 key\n\n```\n┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n│   ││   │   │   │   ││   │   │   │   ││   │   │   │   ││   │ │   │   │   │\n└───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │   │      │     │   │\n├────┬───┴┬──┴─┬─┴──┬┴───┴───┴───┴─┬─┴──┬┴───┼───┴┬──┴─┬────┤ ┌───┼───┼───┐\n│    │    │    │    │              │    │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────┴──────────────┴────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_nofrow_ansi`\n\nTKL ANSI layout with no function row\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │        │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n│        │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐\n│    │    │    │                        │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n\n### `LAYOUT_tkl_nofrow_iso`\n\nTKL ISO layout with no function row\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n│    │   │   │   │   │   │   │   │   │   │   │   │          │     │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐\n│    │    │    │                        │    │    │    │    │ │   │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n```\n</details>\n\n<details>\n<summary>96% Form Factor</summary>\n\n### `LAYOUT_96_ansi`\n\n96% ANSI layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┴───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┼───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┼───┼───┼───┤   │\n│      │   │   │   │   │   │   │   │   │   │   │   │        │   │   │   │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┼───┼───┼───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │      │   │   │   │   │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┼───┼───┤   │\n│    │    │    │                        │   │   │   │   │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_96_iso`\n\n96% ISO layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┴───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┼───┼───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │   │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    ├───┼───┼───┤   │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │   │   │   │   │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┼───┼───┼───┼───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │      │   │   │   │   │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴──┬┴──┬┴──┬───┼───┼───┼───┼───┤   │\n│    │    │    │                        │   │   │   │   │   │   │   │   │   │\n└────┴────┴────┴────────────────────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n</details>\n\n<details>\n<summary>Fullsize (100%+) Form Factor</summary>\n\n### `LAYOUT_fullsize_ansi`\n\nFullsize ANSI layout\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │ │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │ │   │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘ ├───┼───┼───┤   │\n│      │   │   │   │   │   │   │   │   │   │   │   │        │               │   │   │   │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐     ├───┼───┼───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │          │     │   │     │   │   │   │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤   │\n│    │    │    │                        │    │    │    │    │ │   │   │   │ │       │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n```\n\n### `LAYOUT_fullsize_iso`\n\nFullsize ISO layout\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │ │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │ │   │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘ ├───┼───┼───┤   │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │               │   │   │   │   │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐     ├───┼───┼───┼───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │          │     │   │     │   │   │   │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤   │\n│    │    │    │                        │    │    │    │    │ │   │   │   │ │       │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n```\n\n### `LAYOUT_fullsize_jis`\n\nFullsize JIS layout\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │ │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │ │   │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘ ├───┼───┼───┤   │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │               │   │   │   │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤     ┌───┐     ├───┼───┼───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │   │      │     │   │     │   │   │   │   │\n├────┬───┴┬──┴─┬─┴──┬┴───┴───┴───┼───┴┬──┴─┬─┴─┬─┴─┬─┴─┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤   │\n│    │    │    │    │            │    │    │   │   │   │    │ │   │   │   │ │       │   │   │\n└────┴────┴────┴────┴────────────┴────┴────┴───┴───┴───┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n```\n\n### `LAYOUT_fullsize_extended_ansi`\n\nFullsize ANSI layout with additional keys above numpad\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │ │   │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘ └───┴───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │ │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │ │   │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘ ├───┼───┼───┤   │\n│      │   │   │   │   │   │   │   │   │   │   │   │        │               │   │   │   │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐     ├───┼───┼───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │          │     │   │     │   │   │   │   │\n├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤   │\n│    │    │    │                        │    │    │    │    │ │   │   │   │ │       │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n```\n\n### `LAYOUT_fullsize_extended_iso`\n\nFullsize ISO layout with additional keys above numpad\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │ │   │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘ └───┴───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │       │ │   │   │   │ │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │ │   │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘ ├───┼───┼───┤   │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │               │   │   │   │   │\n├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐     ├───┼───┼───┼───┤\n│    │   │   │   │   │   │   │   │   │   │   │   │          │     │   │     │   │   │   │   │\n├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤   │\n│    │    │    │                        │    │    │    │    │ │   │   │   │ │       │   │   │\n└────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n```\n\n### `LAYOUT_fullsize_extended_jis`\n\nFullsize JIS layout with additional keys above numpad\n\n```\n┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n│   │   │   │   │   │   │ │   │   │   │   │ │   │   │   │   │ │   │   │   │ │   │   │   │   │\n└───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘ └───┴───┴───┴───┘\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐ ┌───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │ │   │   │   │ │   │   │   │   │\n├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤ ├───┼───┼───┼───┤\n│     │   │   │   │   │   │   │   │   │   │   │   │   │     │ │   │   │   │ │   │   │   │   │\n├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐    │ └───┴───┴───┘ ├───┼───┼───┤   │\n│      │   │   │   │   │   │   │   │   │   │   │   │   │    │               │   │   │   │   │\n├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤     ┌───┐     ├───┼───┼───┼───┤\n│        │   │   │   │   │   │   │   │   │   │   │   │      │     │   │     │   │   │   │   │\n├────┬───┴┬──┴─┬─┴──┬┴───┴───┴───┼───┴┬──┴─┬─┴─┬─┴─┬─┴─┬────┤ ┌───┼───┼───┐ ├───┴───┼───┤   │\n│    │    │    │    │            │    │    │   │   │   │    │ │   │   │   │ │       │   │   │\n└────┴────┴────┴────┴────────────┴────┴────┴───┴───┴───┴────┘ └───┴───┴───┘ └───────┴───┴───┘\n```\n</details>\n\n<details>\n<summary>Split Layouts</summary>\n\n### `LAYOUT_alice`\n\nAlice-style split layout\n\n```\n  ┌───┐  ┌───┬───┬───┬───┬───┬───┬───┐         ┌───┬───┬───┬───┬───┬───┬───────┐\n  │   │  │   │   │   │   │   │   │   │         │   │   │   │   │   │   │       │\n ┌┴──┬┘ ┌┴───┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┘       ┌─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┴┐\n │   │  │     │   │   │   │   │   │          │   │   │   │   │   │   │   │      │\n┌┴──┬┘ ┌┴─────┼───┼───┼───┼───┼───┤          └┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴──────┴┐\n│   │  │      │   │   │   │   │   │           │   │   │   │   │   │   │          │\n└───┘ ┌┴──────┴┬──┴┬──┴┬──┴┬──┴┬──┴┐        ┌─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───────┬──┴┐\n      │        │   │   │   │   │   │        │   │   │   │   │   │   │         │   │\n      ├─────┬──┴──┬┴───┴┬──┴───┴┬──┴──┐     ├───┴───┴──┬┴───┴┬──┴───┴──────┬──┴──┬┘\n      │     │     │     │       │     │     │          │     │             │     │\n      └─────┘     └─────┴───────┴─────┘     └──────────┴─────┘             └─────┘\n```\n\n### `LAYOUT_alice_split_bs`\n\nAlice-style split layout with split backspace\n\n```\n  ┌───┐  ┌───┬───┬───┬───┬───┬───┬───┐         ┌───┬───┬───┬───┬───┬───┬───┬───┐\n  │   │  │   │   │   │   │   │   │   │         │   │   │   │   │   │   │   │   │\n ┌┴──┬┘ ┌┴───┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┘       ┌─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┐\n │   │  │     │   │   │   │   │   │          │   │   │   │   │   │   │   │      │\n┌┴──┬┘ ┌┴─────┼───┼───┼───┼───┼───┤          └┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴──────┴┐\n│   │  │      │   │   │   │   │   │           │   │   │   │   │   │   │          │\n└───┘ ┌┴──────┴┬──┴┬──┴┬──┴┬──┴┬──┴┐        ┌─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───────┬──┴┐\n      │        │   │   │   │   │   │        │   │   │   │   │   │   │         │   │\n      ├─────┬──┴──┬┴───┴┬──┴───┴┬──┴──┐     ├───┴───┴──┬┴───┴┬──┴───┴──────┬──┴──┬┘\n      │     │     │     │       │     │     │          │     │             │     │\n      └─────┘     └─────┴───────┴─────┘     └──────────┴─────┘             └─────┘\n```\n\n### `LAYOUT_ergodox`\n\nErgodox-style split layout\n\n```\n┌─────┬───┬───┬───┬───┬───┬───┐                   ┌───┬───┬───┬───┬───┬───┬─────┐\n│     │   │   │   │   │   │   │                   │   │   │   │   │   │   │     │\n├─────┼───┼───┼───┼───┼───┼───┤                   ├───┼───┼───┼───┼───┼───┼─────┤\n│     │   │   │   │   │   │   │                   │   │   │   │   │   │   │     │\n├─────┼───┼───┼───┼───┼───┤   │                   │   ├───┼───┼───┼───┼───┼─────┤\n│     │   │   │   │   │   ├───┤                   ├───┤   │   │   │   │   │     │\n├─────┼───┼───┼───┼───┼───┤   │                   │   ├───┼───┼───┼───┼───┼─────┤\n│     │   │   │   │   │   │   │                   │   │   │   │   │   │   │     │\n└─┬───┼───┼───┼───┼───┼───┴───┘                   └───┴───┼───┼───┼───┼───┼───┬─┘\n  │   │   │   │   │   │       ┌───┬───┐   ┌───┬───┐       │   │   │   │   │   │\n  └───┴───┴───┴───┴───┘       │   │   │   │   │   │       └───┴───┴───┴───┴───┘\n                          ┌───┼───┼───┤   ├───┼───┼───┐\n                          │   │   │   │   │   │   │   │\n                          │   │   ├───┤   ├───┤   │   │\n                          │   │   │   │   │   │   │   │\n                          └───┴───┴───┘   └───┴───┴───┘\n```\n\n### `LAYOUT_split_3x5_2`\n\n3x5 split layout with two thumb keys\n\n```\n┌───┬───┬───┬───┬───┐         ┌───┬───┬───┬───┬───┐\n│   │   │   │   │   │         │   │   │   │   │   │\n├───┼───┼───┼───┼───┤         ├───┼───┼───┼───┼───┤\n│   │   │   │   │   │         │   │   │   │   │   │\n├───┼───┼───┼───┼───┤         ├───┼───┼───┼───┼───┤\n│   │   │   │   │   │         │   │   │   │   │   │\n└───┴───┴───┴───┴───┘         └───┴───┴───┴───┴───┘\n                ┌───┬───┐ ┌───┬───┐\n                │   │   │ │   │   │\n                └───┴───┘ └───┴───┘\n```\n\n### `LAYOUT_split_3x5_3`\n\n3x5 split layout with three thumb keys\n\n```\n┌───┬───┬───┬───┬───┐         ┌───┬───┬───┬───┬───┐\n│   │   │   │   │   │         │   │   │   │   │   │\n├───┼───┼───┼───┼───┤         ├───┼───┼───┼───┼───┤\n│   │   │   │   │   │         │   │   │   │   │   │\n├───┼───┼───┼───┼───┤         ├───┼───┼───┼───┼───┤\n│   │   │   │   │   │         │   │   │   │   │   │\n└───┴───┴───┴───┴───┘         └───┴───┴───┴───┴───┘\n            ┌───┬───┬───┐ ┌───┬───┬───┐\n            │   │   │   │ │   │   │   │\n            └───┴───┴───┘ └───┴───┴───┘\n```\n\n### `LAYOUT_split_3x6_3`\n\n3x6 split layout with three thumb keys\n\n```\n┌───┬───┬───┬───┬───┬───┐         ┌───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │         │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┤         ├───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │         │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┤         ├───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │         │   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┘         └───┴───┴───┴───┴───┴───┘\n                ┌───┬───┬───┐ ┌───┬───┬───┐\n                │   │   │   │ │   │   │   │\n                └───┴───┴───┘ └───┴───┴───┘\n```\n</details>\n\n<details>\n<summary>Number Pads</summary>\n\n### `LAYOUT_numpad_4x4`\n\n4x4 number pad\n\n```\n┌───┬───┬───┬───┐\n│   │   │   │   │\n├───┼───┼───┤   │\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┴───┼───┤   │\n│       │   │   │\n└───────┴───┴───┘\n```\n\n### `LAYOUT_numpad_5x4`\n\n5x4 number pad\n\n```\n┌───┬───┬───┬───┐\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┤   │\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┴───┼───┤   │\n│       │   │   │\n└───────┴───┴───┘\n```\n\n### `LAYOUT_numpad_5x6`\n\n5x6 number pad\n\n```\n┌───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │\n├───┼───┼───┼───┼───┤   │\n│   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │\n├───┼───┼───┴───┼───┤   │\n│   │   │       │   │   │\n└───┴───┴───────┴───┴───┘\n```\n\n### `LAYOUT_numpad_6x4`\n\n6x4 number pad\n\n```\n┌───┬───┬───┬───┐\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┤   │\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┴───┼───┤   │\n│       │   │   │\n└───────┴───┴───┘\n```\n\n### `LAYOUT_numpad_6x5`\n\n6x5 number pad\n\n```\n┌───┬───┬───┬───┬───┐\n│   │   │   │   │   │\n├───┼───┼───┼───┼───┤\n│   │   │   │   │   │\n├───┼───┼───┼───┼───┤\n│   │   │   │   │   │\n├───┼───┼───┼───┤   │\n│   │   │   │   │   │\n├───┼───┼───┼───┼───┤\n│   │   │   │   │   │\n├───┼───┴───┼───┤   │\n│   │       │   │   │\n└───┴───────┴───┴───┘\n```\n</details>\n\n<details>\n<summary>Ortholinear Layouts</summary>\n\n### `LAYOUT_ortho_1x1`\n\n1x1 ortholinear layout\n\n```\n┌───┐\n│   │\n└───┘\n```\n\n### `LAYOUT_ortho_2x3`\n\n2x3 ortholinear layout\n\n```\n┌───┬───┬───┐\n│   │   │   │\n├───┼───┼───┤\n│   │   │   │\n└───┴───┴───┘\n```\n\n### `LAYOUT_ortho_2x6`\n\n2x6 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_3x3`\n\n3x3 ortholinear layout\n\n```\n┌───┬───┬───┐\n│   │   │   │\n├───┼───┼───┤\n│   │   │   │\n├───┼───┼───┤\n│   │   │   │\n└───┴───┴───┘\n```\n\n### `LAYOUT_ortho_3x10`\n\n3x10 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_4x10`\n\n4x10 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_4x12`\n\n4x12 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_4x16`\n\n4x16 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_4x4`\n\n4x4 ortholinear layout\n\n```\n┌───┬───┬───┬───┐\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n└───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_4x6`\n\n4x6 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_5x5`\n\n5x5 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┐\n│   │   │   │   │   │\n├───┼───┼───┼───┼───┤\n│   │   │   │   │   │\n├───┼───┼───┼───┼───┤\n│   │   │   │   │   │\n├───┼───┼───┼───┼───┤\n│   │   │   │   │   │\n├───┼───┼───┼───┼───┤\n│   │   │   │   │   │\n└───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_5x10`\n\n5x10 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_5x12`\n\n5x12 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_5x13`\n\n5x13 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_5x14`\n\n5x14 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_5x15`\n\n5x15 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_5x4`\n\n5x4 ortholinear layout\n\n```\n┌───┬───┬───┬───┐\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n└───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_6x4`\n\n6x4 ortholinear layout\n\n```\n┌───┬───┬───┬───┐\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n├───┼───┼───┼───┤\n│   │   │   │   │\n└───┴───┴───┴───┘\n```\n\n### `LAYOUT_ortho_6x13`\n\n6x13 ortholinear layout\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘\n```\n\n### `LAYOUT_planck_mit`\n\n4x12 ortholinear layout with center 2u space\n\n```\n┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐\n│   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │   │   │   │   │   │   │   │\n├───┼───┼───┼───┼───┼───┴───┼───┼───┼───┼───┼───┤\n│   │   │   │   │   │       │   │   │   │   │   │\n└───┴───┴───┴───┴───┴───────┴───┴───┴───┴───┴───┘\n```\n</details>\n"
  },
  {
    "path": "layouts/default/split_3x5_2/default_split_3x5_2/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n     /*\n      * ┌───┬───┬───┬───┬───┐       ┌───┬───┬───┬───┬───┐\n      * │ Q │ W │ E │ R │ T │       │ Y │ U │ I │ O │ P │\n      * ├───┼───┼───┼───┼───┤       ├───┼───┼───┼───┼───┤\n      * │ A │ S │ D │ F │ G │       │ H │ J │ K │ L │ ; │\n      * ├───┼───┼───┼───┼───┤       ├───┼───┼───┼───┼───┤\n      * │ Z │ X │ C │ V │ B │       │ N │ M │ , │ . │ / │\n      * └───┴───┴───┴───┴───┘       └───┴───┴───┴───┴───┘\n      *               ┌───┐           ┌───┐\n      *               │Bsp├───┐   ┌───┤Ent│\n      *               └───┤Tab│   │Spc├───┘\n      *                   └───┘   └───┘\n      */\n    [0] = LAYOUT_split_3x5_2(\n        KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,                               KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,\n        KC_A,    KC_S,    KC_D,    KC_F,    KC_G,                               KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN,\n        KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,                               KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,\n                                            KC_BSPC, KC_TAB,           KC_SPC,  KC_ENT\n    )\n};\n"
  },
  {
    "path": "layouts/default/split_3x5_2/info.json",
    "content": "{\n    \"keyboard_name\": \"3x5+2 split layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_split_3x5_2\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0.25},\n                {\"x\":1, \"y\":0.125},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0.125},\n                {\"x\":4, \"y\":0.25},\n\n                {\"x\":7, \"y\":0.25},\n                {\"x\":8, \"y\":0.125},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0.125},\n                {\"x\":11, \"y\":0.25},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.125},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1.125},\n                {\"x\":4, \"y\":1.25},\n\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.125},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1.125},\n                {\"x\":11, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25},\n                {\"x\":1, \"y\":2.125},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2.125},\n                {\"x\":4, \"y\":2.25},\n\n                {\"x\":7, \"y\":2.25},\n                {\"x\":8, \"y\":2.125},\n                {\"x\":9, \"y\":2},\n                {\"x\":10, \"y\":2.125},\n                {\"x\":11, \"y\":2.25},\n\n                {\"x\":3.5, \"y\":3.25},\n                {\"x\":4.5, \"y\":3.5},\n\n                {\"x\":6.5, \"y\":3.5},\n                {\"x\":7.5, \"y\":3.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/split_3x5_2/layout.json",
    "content": "[{x:2,a:7},\"\",{x:6},\"\"],\n[{y:-0.875,x:1},\"\",{x:1},\"\",{x:4},\"\",{x:1},\"\"],\n[{y:-0.875},\"\",{x:3},\"\",{x:2},\"\",{x:3},\"\"],\n[{y:-0.25,x:2},\"\",{x:6},\"\"],\n[{y:-0.875,x:1},\"\",{x:1},\"\",{x:4},\"\",{x:1},\"\"],\n[{y:-0.875},\"\",{x:3},\"\",{x:2},\"\",{x:3},\"\"],\n[{y:-0.25,x:2},\"\",{x:6},\"\"],\n[{y:-0.875,x:1},\"\",{x:1},\"\",{x:4},\"\",{x:1},\"\"],\n[{y:-0.875},\"\",{x:3},\"\",{x:2},\"\",{x:3},\"\"],\n[{y:0.25},{x:3.5},\"\",{x:3},\"\"],\n[{y:-0.75,x:4.5},\"\",{x:1},\"\"]\n"
  },
  {
    "path": "layouts/default/split_3x5_2/readme.md",
    "content": "# split_3x5_2\n\n    LAYOUT_split_3x5_2\n\nLayout for split keyboards with 3x5 keys and two thumb keys per hand. Examples include the Architeuthis Dux, Ferris, and Cradio.\n"
  },
  {
    "path": "layouts/default/split_3x5_3/default_split_3x5_3/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n     /*\n      * ┌───┬───┬───┬───┬───┐       ┌───┬───┬───┬───┬───┐\n      * │ Q │ W │ E │ R │ T │       │ Y │ U │ I │ O │ P │\n      * ├───┼───┼───┼───┼───┤       ├───┼───┼───┼───┼───┤\n      * │ A │ S │ D │ F │ G │       │ H │ J │ K │ L │ ; │\n      * ├───┼───┼───┼───┼───┤       ├───┼───┼───┼───┼───┤\n      * │ Z │ X │ C │ V │ B │       │ N │ M │ , │ . │ / │\n      * └───┴───┴───┴───┴───┘       └───┴───┴───┴───┴───┘\n      *           ┌───┐                   ┌───┐\n      *           │GUI├───┐           ┌───┤Alt│\n      *           └───┤Bsp├───┐   ┌───┤Ent├───┘\n      *               └───┤   │   │   ├───┘\n      *                   └───┘   └───┘\n      */\n    [0] = LAYOUT_split_3x5_3(\n        KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,                               KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,\n        KC_A,    KC_S,    KC_D,    KC_F,    KC_G,                               KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN,\n        KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,                               KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,\n                                   KC_LGUI, KC_BSPC, KC_SPC,           KC_SPC,  KC_ENT,  KC_RALT\n    )\n};\n"
  },
  {
    "path": "layouts/default/split_3x5_3/info.json",
    "content": "{\n    \"keyboard_name\": \"3x5+3 split layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_split_3x5_3\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0.25},\n                {\"x\":1, \"y\":0.125},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0.125},\n                {\"x\":4, \"y\":0.25},\n\n                {\"x\":7, \"y\":0.25},\n                {\"x\":8, \"y\":0.125},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0.125},\n                {\"x\":11, \"y\":0.25},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.125},\n                {\"x\":2, \"y\":1},\n                {\"x\":3, \"y\":1.125},\n                {\"x\":4, \"y\":1.25},\n\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.125},\n                {\"x\":9, \"y\":1},\n                {\"x\":10, \"y\":1.125},\n                {\"x\":11, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25},\n                {\"x\":1, \"y\":2.125},\n                {\"x\":2, \"y\":2},\n                {\"x\":3, \"y\":2.125},\n                {\"x\":4, \"y\":2.25},\n\n                {\"x\":7, \"y\":2.25},\n                {\"x\":8, \"y\":2.125},\n                {\"x\":9, \"y\":2},\n                {\"x\":10, \"y\":2.125},\n                {\"x\":11, \"y\":2.25},\n\n                {\"x\":2.5, \"y\":3.25},\n                {\"x\":3.5, \"y\":3.5},\n                {\"x\":4.5, \"y\":3.75},\n\n                {\"x\":6.5, \"y\":3.75},\n                {\"x\":7.5, \"y\":3.5},\n                {\"x\":8.5, \"y\":3.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/split_3x5_3/layout.json",
    "content": "[{x:2,a:7},\"\",{x:6},\"\"],\n[{y:-0.875,x:1},\"\",{x:1},\"\",{x:4},\"\",{x:1},\"\"],\n[{y:-0.875},\"\",{x:3},\"\",{x:2},\"\",{x:3},\"\"],\n[{y:-0.25,x:2},\"\",{x:6},\"\"],\n[{y:-0.875,x:1},\"\",{x:1},\"\",{x:4},\"\",{x:1},\"\"],\n[{y:-0.875},\"\",{x:3},\"\",{x:2},\"\",{x:3},\"\"],\n[{y:-0.25,x:2},\"\",{x:6},\"\"],\n[{y:-0.875,x:1},\"\",{x:1},\"\",{x:4},\"\",{x:1},\"\"],\n[{y:-0.875},\"\",{x:3},\"\",{x:2},\"\",{x:3},\"\"],\n[{x:2.5},\"\",{x:5},\"\"],\n[{y:-0.75,x:3.5},\"\",{x:3},\"\"],\n[{y:-0.75,x:4.5},\"\",{x:1},\"\"]\n"
  },
  {
    "path": "layouts/default/split_3x5_3/readme.md",
    "content": "# split_3x5_3\n\n    LAYOUT_split_3x5_3\n\nLayout for split keyboards with 3x5 keys and three thumb keys per hand. Examples include the Minidox, Centromere Mini, and Gergoplex.\n"
  },
  {
    "path": "layouts/default/split_3x6_3/default_split_3x6_3/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n     /*\n      * ┌───┬───┬───┬───┬───┬───┐       ┌───┬───┬───┬───┬───┬───┐\n      * │Tab│ Q │ W │ E │ R │ T │       │ Y │ U │ I │ O │ P │Bsp│\n      * ├───┼───┼───┼───┼───┼───┤       ├───┼───┼───┼───┼───┼───┤\n      * │Ctl│ A │ S │ D │ F │ G │       │ H │ J │ K │ L │ ; │ ' │\n      * ├───┼───┼───┼───┼───┼───┤       ├───┼───┼───┼───┼───┼───┤\n      * │Sft│ Z │ X │ C │ V │ B │       │ N │ M │ , │ . │ / │Sft│\n      * └───┴───┴───┴───┴───┴───┘       └───┴───┴───┴───┴───┴───┘\n      *               ┌───┐                   ┌───┐\n      *               │GUI├───┐           ┌───┤Alt│\n      *               └───┤Bsp├───┐   ┌───┤Ent├───┘\n      *                   └───┤   │   │   ├───┘\n      *                       └───┘   └───┘\n      */\n    [0] = LAYOUT_split_3x6_3(\n        KC_TAB,  KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,                               KC_Y,    KC_U,    KC_I,    KC_O,    KC_P,    KC_BSPC,\n        KC_LCTL, KC_A,    KC_S,    KC_D,    KC_F,    KC_G,                               KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN, KC_QUOT,\n        KC_LSFT, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,                               KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT,\n                                            KC_LGUI, KC_BSPC, KC_SPC,           KC_SPC,  KC_ENT,  KC_RALT\n    )\n};\n"
  },
  {
    "path": "layouts/default/split_3x6_3/info.json",
    "content": "{\n    \"keyboard_name\": \"3x6+3 split layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_split_3x6_3\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0.25},\n                {\"x\":1, \"y\":0.25},\n                {\"x\":2, \"y\":0.125},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0.125},\n                {\"x\":5, \"y\":0.25},\n\n                {\"x\":8, \"y\":0.25},\n                {\"x\":9, \"y\":0.125},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0.125},\n                {\"x\":12, \"y\":0.25},\n                {\"x\":13, \"y\":0.25},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.125},\n                {\"x\":3, \"y\":1},\n                {\"x\":4, \"y\":1.125},\n                {\"x\":5, \"y\":1.25},\n\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.125},\n                {\"x\":10, \"y\":1},\n                {\"x\":11, \"y\":1.125},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25},\n                {\"x\":1, \"y\":2.25},\n                {\"x\":2, \"y\":2.125},\n                {\"x\":3, \"y\":2},\n                {\"x\":4, \"y\":2.125},\n                {\"x\":5, \"y\":2.25},\n\n                {\"x\":8, \"y\":2.25},\n                {\"x\":9, \"y\":2.125},\n                {\"x\":10, \"y\":2},\n                {\"x\":11, \"y\":2.125},\n                {\"x\":12, \"y\":2.25},\n                {\"x\":13, \"y\":2.25},\n\n                {\"x\":3.5, \"y\":3.25},\n                {\"x\":4.5, \"y\":3.5},\n                {\"x\":5.5, \"y\":3.75},\n\n                {\"x\":7.5, \"y\":3.75},\n                {\"x\":8.5, \"y\":3.5},\n                {\"x\":9.5, \"y\":3.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/split_3x6_3/layout.json",
    "content": "[{x:3,a:7},\"\",{x:6},\"\"],\n[{y:-0.875,x:2},\"\",{x:1},\"\",{x:4},\"\",{x:1},\"\"],\n[{y:-0.875},\"\",\"\",{x:3},\"\",{x:2},\"\",{x:3},\"\",\"\"],\n[{y:-0.25,x:3},\"\",{x:6},\"\"],\n[{y:-0.875,x:2},\"\",{x:1},\"\",{x:4},\"\",{x:1},\"\"],\n[{y:-0.875},\"\",\"\",{x:3},\"\",{x:2},\"\",{x:3},\"\",\"\"],\n[{y:-0.25,x:3},\"\",{x:6},\"\"],\n[{y:-0.875,x:2},\"\",{x:1},\"\",{x:4},\"\",{x:1},\"\"],\n[{y:-0.875},\"\",\"\",{x:3},\"\",{x:2},\"\",{x:3},\"\",\"\"],\n[{x:3.5},\"\",{x:5},\"\"],\n[{y:-0.75,x:4.5},\"\",{x:3},\"\"],\n[{y:-0.75,x:5.5},\"\",{x:1},\"\"]\n"
  },
  {
    "path": "layouts/default/split_3x6_3/readme.md",
    "content": "# split_3x6_3\n\n    LAYOUT_split_3x6_3\n\nLayout for split keyboards with 3x6 keys and three thumb keys per hand. Examples include the Crkbd and Centromere.\n"
  },
  {
    "path": "layouts/default/tkl_ansi/default_tkl_ansi/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_ansi(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_ansi/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_ansi/readme.md",
    "content": "# tkl_ansi\n\n    LAYOUT_tkl_ansi\n\nA standard ANSI TKL layout.\n"
  },
  {
    "path": "layouts/default/tkl_ansi_split_bs_rshift/default_tkl_ansi_split_bs_rshift/keymap.c",
    "content": "// Copyright 2022 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤     ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│Sft│     │ ↑ │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤ ┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_ansi_split_bs_rshift(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,                      KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_ansi_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI layout with split Backspace and split Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_ansi_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_ansi_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_ansi_split_bs_rshift/readme.md",
    "content": "# tkl_ansi_split_bs_rshift\n\n    LAYOUT_tkl_ansi_split_bs_rshift\n\nA standard ANSI TKL layout with split Backspace and split Right Shift.\n"
  },
  {
    "path": "layouts/default/tkl_ansi_tsangan/default_tkl_ansi_tsangan/keymap.c",
    "content": "// Copyright 2022 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n     * │Ctrl │GUI│Alt  │                           │  Alt│App│ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_ansi_tsangan(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_ansi_tsangan/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI layout with Tsangan Bottom Row\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_ansi_tsangan\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":5.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":12.5, \"y\":5.25},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_ansi_tsangan/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_ansi_tsangan/readme.md",
    "content": "# tkl_ansi_tsangan\n\n    LAYOUT_tkl_ansi_tsangan\n\nTenkeyless ANSI layout with Tsangan Bottom Row.\n"
  },
  {
    "path": "layouts/default/tkl_ansi_tsangan_split_bs_rshift/default_tkl_ansi_tsangan_split_bs_rshift/keymap.c",
    "content": "// Copyright 2022 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤     ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│Sft│     │ ↑ │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n     * │Ctrl │GUI│Alt  │                           │  Alt│App│ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_ansi_tsangan_split_bs_rshift(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,                      KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_APP,  KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_ansi_tsangan_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI layout with split Backspace, split Right Shift, and Tsangan Bottom Row\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_ansi_tsangan_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":5.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":12.5, \"y\":5.25},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_ansi_tsangan_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_ansi_tsangan_split_bs_rshift/readme.md",
    "content": "# tkl_ansi_tsangan_split_bs_rshift\n\n    LAYOUT_tkl_ansi_tsangan_split_bs_rshift\n\nTenkeyless ANSI layout with split Backspace, split Right Shift, and Tsangan Bottom Row.\n"
  },
  {
    "path": "layouts/default/tkl_ansi_wkl/default_tkl_ansi_wkl/keymap.c",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n     * │Ctrl │   │Alt  │                           │  Alt│   │ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_ansi_wkl(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_ansi_wkl/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI Windows keyless layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_ansi_wkl\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                \n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_ansi_wkl/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_ansi_wkl/readme.md",
    "content": "# tkl_ansi_wkl\n\n    LAYOUT_tkl_ansi_wkl\n"
  },
  {
    "path": "layouts/default/tkl_ansi_wkl_split_bs_rshift/default_tkl_ansi_wkl_split_bs_rshift/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤     ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│Sft│     │ ↑ │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n     * │Ctrl │   │Alt  │                           │  Alt│   │ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_ansi_wkl_split_bs_rshift(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,                      KC_UP,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_ansi_wkl_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI Windows keyless layout with split Backspace and split Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_ansi_wkl_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                \n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_ansi_wkl_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_ansi_wkl_split_bs_rshift/readme.md",
    "content": "# tkl_ansi_wkl_split_bs_rshift\n\n    LAYOUT_tkl_ansi_wkl_split_bs_rshift\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi/default_tkl_f13_ansi/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13││PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘└───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp││Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  ││Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤└───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤    ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │    │ ↑ │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl││ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘└───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_ansi(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI layout with F13 key\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi/readme.md",
    "content": "# tkl_f13_ansi\n\n    LAYOUT_tkl_f13_ansi\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_split_bs_rshift/default_tkl_f13_ansi_split_bs_rshift/keymap.c",
    "content": "// Copyright 2022 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13││PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘└───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp││Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  ││Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤└───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤    ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│   │    │ ↑ │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl││ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘└───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_ansi_split_bs_rshift(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, _______,                      KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI layout with F13 key, split Backspace, and split Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_ansi_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_split_bs_rshift/readme.md",
    "content": "# tkl_f13_ansi_split_bs_rshift\n\n    LAYOUT_tkl_f13_ansi_split_bs_rshift\n\nAn ANSI TKL layout featuring an F13 key, split Backspace, and split Right Shift.\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_tsangan/default_tkl_f13_ansi_tsangan/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13││PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘└───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp││Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  ││Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤└───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤    ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │    │ ↑ │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤┌───┼───┼───┐\n     * │Ctrl │GUI│Alt  │                           │  Alt│App│ Ctrl││ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘└───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_ansi_tsangan(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_tsangan/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI layout with F13 key and Tsangan Bottom Row\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_ansi_tsangan\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":5.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":12.5, \"y\":5.25},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_tsangan/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_tsangan/readme.md",
    "content": "# tkl_f13_ansi_tsangan\n\n    LAYOUT_tkl_f13_ansi_tsangan\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_tsangan_split_bs_rshift/default_tkl_f13_ansi_tsangan_split_bs_rshift/keymap.c",
    "content": "// Copyright 2022 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13││PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘└───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp││Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  ││Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤└───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤    ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Shift │   │    │ ↑ │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤┌───┼───┼───┐\n     * │Ctrl │GUI│Alt  │                           │  Alt│App│ Ctrl││ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘└───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_ansi_tsangan_split_bs_rshift(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, _______,                      KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_APP,  KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_tsangan_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI layout with F13 key, split Backspace, split Right Shift, and Tsangan Bottom Row\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_ansi_tsangan_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":5.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":12.5, \"y\":5.25},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_tsangan_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_tsangan_split_bs_rshift/readme.md",
    "content": "# tkl_f13_ansi_tsangan_split_bs_rshift\n\n    LAYOUT_tkl_f13_ansi_tsangan_split_bs_rshift\n\nAn ANSI TKL layout featuring an F13 key, split Backspace, split Right Shift, and Tsangan bottom row.\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_wkl/default_tkl_f13_ansi_wkl/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13│ │PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n     * │Ctrl │   │Alt  │                           │  Alt│   │ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_ansi_wkl(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_wkl/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI Windows keyless layout with F13 key\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_ansi_wkl\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n                \n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_wkl/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_wkl/readme.md",
    "content": "# tkl_f13_ansi_wkl\n\n    LAYOUT_tkl_f13_ansi_wkl\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_wkl_split_bs_rshift/default_tkl_f13_ansi_wkl_split_bs_rshift/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13│ │PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┬───┤     ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │Shift │Sft│     │ ↑ │\n     * ├─────┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n     * │Ctrl │   │Alt  │                           │  Alt│   │ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_ansi_wkl_split_bs_rshift(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,                      KC_UP,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_wkl_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ANSI Windows keyless layout with F13 key, split Backspace, and split Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_ansi_wkl_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":13.5, \"y\":2.25, \"w\":1.5},\n\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25, \"w\":2.25},\n\n                {\"x\":0, \"y\":4.25, \"w\":2.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_wkl_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_ansi_wkl_split_bs_rshift/readme.md",
    "content": "# tkl_f13_ansi_wkl_split_bs_rshift\n\n    LAYOUT_tkl_f13_ansi_wkl_split_bs_rshift\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso/default_tkl_f13_iso/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13││PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘└───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp││Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     ││Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│└───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤    ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │    │ ↑ │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl││ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘└───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_iso(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO layout with F13 key\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso/readme.md",
    "content": "# tkl_f13_iso\n\n    LAYOUT_tkl_f13_iso\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_split_bs_rshift/default_tkl_f13_iso_split_bs_rshift/keymap.c",
    "content": "// Copyright 2022 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13││PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘└───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp││Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     ││Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│└───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤    ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│   │    │ ↑ │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl││ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘└───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_iso_split_bs_rshift(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, _______,                      KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO layout with F13 key, split Backspace, and split Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_iso_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_split_bs_rshift/readme.md",
    "content": "# tkl_f13_iso_split_bs_rshift\n\n    LAYOUT_tkl_f13_iso_split_bs_rshift\n\nAn ISO TKL layout featuring an F13 key, split Backspace, and split Right Shift.\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_tsangan/default_tkl_f13_iso_tsangan/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13││PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘└───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp││Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     ││Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│└───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤    ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │    │ ↑ │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤┌───┼───┼───┐\n     * │Ctrl │GUI│Alt  │                           │  Alt│App│ Ctrl││ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘└───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_iso_tsangan(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_tsangan/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO layout with F13 key and Tsangan Bottom Row\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_iso_tsangan\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":5.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":12.5, \"y\":5.25},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_tsangan/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_tsangan/readme.md",
    "content": "# tkl_f13_iso_tsangan\n\n    LAYOUT_tkl_f13_iso_tsangan\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_tsangan_split_bs_rshift/default_tkl_f13_iso_tsangan_split_bs_rshift/keymap.c",
    "content": "// Copyright 2022 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13││PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘└───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp││Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     ││Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│└───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤    ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│   │    │ ↑ │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤┌───┼───┼───┐\n     * │Ctrl │GUI│Alt  │                           │  Alt│App│ Ctrl││ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘└───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_iso_tsangan_split_bs_rshift(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, _______,                      KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_APP,  KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_tsangan_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO layout with F13 key, split Backspace, split Right Shift, and Tsangan Bottom Row\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_iso_tsangan_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":5.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":12.5, \"y\":5.25},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_tsangan_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_tsangan_split_bs_rshift/readme.md",
    "content": "# tkl_f13_iso_tsangan_split_bs_rshift\n\n    LAYOUT_tkl_f13_iso_tsangan_split_bs_rshift\n\nAn ISO TKL layout featuring an F13 key, split Backspace, split Right Shift, and Tsangan bottom row.\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_wkl/default_tkl_f13_iso_wkl/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13│ │PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n     * │Ctrl │   │Alt  │                           │  Alt│   │ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_iso_wkl(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_wkl/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO Windows keyless layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_iso_wkl\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n                \n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_wkl/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_wkl/readme.md",
    "content": "# tkl_f13_iso_wkl\n\n    LAYOUT_tkl_f13_iso_wkl\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_wkl_split_bs_rshift/default_tkl_f13_iso_wkl_split_bs_rshift/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐ ┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13│ │PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤     ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│Sft│     │ ↑ │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n     * │Ctrl │   │Alt  │                           │  Alt│   │ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_iso_wkl_split_bs_rshift(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,                      KC_UP,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_wkl_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO Windows keyless layout with F13 key, split Backspace, and split Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_iso_wkl_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1.25, \"y\":0},\n                {\"x\":2.25, \"y\":0},\n                {\"x\":3.25, \"y\":0},\n                {\"x\":4.25, \"y\":0},\n                {\"x\":5.5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.75, \"y\":0},\n                {\"x\":10.75, \"y\":0},\n                {\"x\":11.75, \"y\":0},\n                {\"x\":12.75, \"y\":0},\n                {\"x\":14, \"y\":0},\n                \n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_wkl_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_iso_wkl_split_bs_rshift/readme.md",
    "content": "# tkl_f13_iso_wkl_split_bs_rshift\n\n    LAYOUT_tkl_f13_iso_wkl_split_bs_rshift\n"
  },
  {
    "path": "layouts/default/tkl_f13_jis/default_tkl_f13_jis/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┬───┬───┬───┐┌───┐┌───┬───┬───┐\n     * │Esc││F1 │F2 │F3 │F4 ││F5 │F6 │F7 │F8 ││F9 │F10│F11│F12││F13││PSc│Scr│Pse│\n     * └───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┴───┴───┴───┘└───┘└───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐┌───┬───┬───┐\n     * │ZHK│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ ^ │ ¥ │Bsp││Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ @ │ [ │     ││Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│└───┴───┴───┘\n     * │ Eisu │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ : │ ] │    │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤    ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ \\ │ Shft │    │ ↑ │\n     * ├────┬───┴┬──┴─┬─┴──┬┴───┴───┴───┴─┬─┴──┬┴───┼───┴┬──┴─┬────┤┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │Mhen│    Space     │Henk│Kana│Alt │GUI │Ctrl││ ← │ ↓ │ → │\n     * └────┴────┴────┴────┴──────────────┴────┴────┴────┴────┴────┘└───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_f13_jis(\n        KC_ESC,  KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,  KC_F13,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_INT3, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_INT1, KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT, KC_INT5,                  KC_SPC,                     KC_INT4, KC_INT2, KC_RALT, KC_RGUI, KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_f13_jis/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless JIS layout with F13 key\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_f13_jis\": {\n            \"layout\": [\n                {\"x\": 0, \"y\": 0},\n                {\"x\": 1.25, \"y\": 0},\n                {\"x\": 2.25, \"y\": 0},\n                {\"x\": 3.25, \"y\": 0},\n                {\"x\": 4.25, \"y\": 0},\n                {\"x\": 5.5, \"y\": 0},\n                {\"x\": 6.5, \"y\": 0},\n                {\"x\": 7.5, \"y\": 0},\n                {\"x\": 8.5, \"y\": 0},\n                {\"x\": 9.75, \"y\": 0},\n                {\"x\": 10.75, \"y\": 0},\n                {\"x\": 11.75, \"y\": 0},\n                {\"x\": 12.75, \"y\": 0},\n                {\"x\": 14, \"y\": 0},\n                {\"x\": 15.25, \"y\": 0},\n                {\"x\": 16.25, \"y\": 0},\n                {\"x\": 17.25, \"y\": 0},\n\n                {\"x\": 0, \"y\": 1.25},\n                {\"x\": 1, \"y\": 1.25},\n                {\"x\": 2, \"y\": 1.25},\n                {\"x\": 3, \"y\": 1.25},\n                {\"x\": 4, \"y\": 1.25},\n                {\"x\": 5, \"y\": 1.25},\n                {\"x\": 6, \"y\": 1.25},\n                {\"x\": 7, \"y\": 1.25},\n                {\"x\": 8, \"y\": 1.25},\n                {\"x\": 9, \"y\": 1.25},\n                {\"x\": 10, \"y\": 1.25},\n                {\"x\": 11, \"y\": 1.25},\n                {\"x\": 12, \"y\": 1.25},\n                {\"x\": 13, \"y\": 1.25},\n                {\"x\": 14, \"y\": 1.25},\n                {\"x\": 15.25, \"y\": 1.25},\n                {\"x\": 16.25, \"y\": 1.25},\n                {\"x\": 17.25, \"y\": 1.25},\n                {\"x\": 0, \"y\": 2.25, \"w\": 1.5},\n\n                {\"x\": 1.5, \"y\": 2.25},\n                {\"x\": 2.5, \"y\": 2.25},\n                {\"x\": 3.5, \"y\": 2.25},\n                {\"x\": 4.5, \"y\": 2.25},\n                {\"x\": 5.5, \"y\": 2.25},\n                {\"x\": 6.5, \"y\": 2.25},\n                {\"x\": 7.5, \"y\": 2.25},\n                {\"x\": 8.5, \"y\": 2.25},\n                {\"x\": 9.5, \"y\": 2.25},\n                {\"x\": 10.5, \"y\": 2.25},\n                {\"x\": 11.5, \"y\": 2.25},\n                {\"x\": 12.5, \"y\": 2.25},\n                {\"x\": 15.25, \"y\": 2.25},\n                {\"x\": 16.25, \"y\": 2.25},\n                {\"x\": 17.25, \"y\": 2.25},\n\n                {\"x\": 0, \"y\": 3.25, \"w\": 1.75},\n                {\"x\": 1.75, \"y\": 3.25},\n                {\"x\": 2.75, \"y\": 3.25},\n                {\"x\": 3.75, \"y\": 3.25},\n                {\"x\": 4.75, \"y\": 3.25},\n                {\"x\": 5.75, \"y\": 3.25},\n                {\"x\": 6.75, \"y\": 3.25},\n                {\"x\": 7.75, \"y\": 3.25},\n                {\"x\": 8.75, \"y\": 3.25},\n                {\"x\": 9.75, \"y\": 3.25},\n                {\"x\": 10.75, \"y\": 3.25},\n                {\"x\": 11.75, \"y\": 3.25},\n                {\"x\": 12.75, \"y\": 3.25},\n                {\"x\": 13.75, \"y\": 2.25, \"w\": 1.25, \"h\": 2},\n\n                {\"x\": 0, \"y\": 4.25, \"w\": 2.25},\n                {\"x\": 2.25, \"y\": 4.25},\n                {\"x\": 3.25, \"y\": 4.25},\n                {\"x\": 4.25, \"y\": 4.25},\n                {\"x\": 5.25, \"y\": 4.25},\n                {\"x\": 6.25, \"y\": 4.25},\n                {\"x\": 7.25, \"y\": 4.25},\n                {\"x\": 8.25, \"y\": 4.25},\n                {\"x\": 9.25, \"y\": 4.25},\n                {\"x\": 10.25, \"y\": 4.25},\n                {\"x\": 11.25, \"y\": 4.25},\n                {\"x\": 12.25, \"y\": 4.25},\n                {\"x\": 13.25, \"y\": 4.25, \"w\": 1.75},\n                {\"x\": 16.25, \"y\": 4.25},\n                \n                {\"x\": 0, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 1.25, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 2.5, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 3.75, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 5, \"y\": 5.25, \"w\": 3.75},\n                {\"x\": 8.75, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 10, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 11.25, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 12.5, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 13.75, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 15.25, \"y\": 5.25},\n                {\"x\": 16.25, \"y\": 5.25},\n                {\"x\": 17.25, \"y\": 5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_f13_jis/layout.json",
    "content": "[{a:7},\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\",\"\",{x:0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:3.75},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_f13_jis/readme.md",
    "content": "# tkl_f13_jis\n\n    LAYOUT_tkl_f13_jis\n"
  },
  {
    "path": "layouts/default/tkl_iso/default_tkl_iso/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_iso(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_iso/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_iso/readme.md",
    "content": "# tkl_iso\n\n    LAYOUT_tkl_iso\n\nA standard ISO TKL layout.\n"
  },
  {
    "path": "layouts/default/tkl_iso_split_bs_rshift/default_tkl_iso_split_bs_rshift/keymap.c",
    "content": "// Copyright 2022 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤     ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│Sft│     │ ↑ │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬┴───┤ ┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_iso_split_bs_rshift(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,                      KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_iso_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO layout with split Backspace and split Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_iso_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":3.75, \"y\":5.25, \"w\":6.25},\n                {\"x\":10, \"y\":5.25, \"w\":1.25},\n                {\"x\":11.25, \"y\":5.25, \"w\":1.25},\n                {\"x\":12.5, \"y\":5.25, \"w\":1.25},\n                {\"x\":13.75, \"y\":5.25, \"w\":1.25},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_iso_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_iso_split_bs_rshift/readme.md",
    "content": "# tkl_iso_split_bs_rshift\n\n    LAYOUT_tkl_iso_split_bs_rshift\n\nA standard ISO TKL layout with split Backspace and split Right Shift.\n"
  },
  {
    "path": "layouts/default/tkl_iso_tsangan/default_tkl_iso_tsangan/keymap.c",
    "content": "// Copyright 2022 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n     * │Ctrl │GUI│Alt  │                           │  Alt│App│ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_iso_tsangan(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_iso_tsangan/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO layout with Tsangan Bottom Row\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_iso_tsangan\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":5.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":12.5, \"y\":5.25},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_iso_tsangan/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_iso_tsangan/readme.md",
    "content": "# tkl_iso_tsangan\n\n    LAYOUT_tkl_iso_tsangan\n\nTenkeyless ISO layout with Tsangan Bottom Row.\n"
  },
  {
    "path": "layouts/default/tkl_iso_tsangan_split_bs_rshift/default_tkl_iso_tsangan_split_bs_rshift/keymap.c",
    "content": "// Copyright 2022 QMK / James Young (@noroadsleft)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤     ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│Sft│     │ ↑ │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n     * │Ctrl │GUI│Alt  │                           │  Alt│App│ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┴───┴─────┴───────────────────────────┴─────┴───┴─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_iso_tsangan_split_bs_rshift(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,                      KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                                      KC_RALT, KC_APP,  KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_iso_tsangan_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO layout with split Backspace, split Right Shift, and Tsangan Bottom Row\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_iso_tsangan_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":5.25},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":12.5, \"y\":5.25},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_iso_tsangan_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",\"\",{w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_iso_tsangan_split_bs_rshift/readme.md",
    "content": "# tkl_iso_tsangan_split_bs_rshift\n\n    LAYOUT_tkl_iso_tsangan_split_bs_rshift\n\nTenkeyless ISO layout with split Backspace, split Right Shift, and Tsangan Bottom Row.\n"
  },
  {
    "path": "layouts/default/tkl_iso_wkl/default_tkl_iso_wkl/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─────┤ ┌───┼───┼───┐\n     * │Ctrl │   │Alt  │                           │  Alt│   │ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_iso_wkl(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_iso_wkl/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO Windows keyless layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_iso_wkl\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25, \"w\":2},\n\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":2.75},\n                \n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_iso_wkl/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_iso_wkl/readme.md",
    "content": "# tkl_iso_wkl\n\n    LAYOUT_tkl_iso_wkl\n"
  },
  {
    "path": "layouts/default/tkl_iso_wkl_split_bs_rshift/default_tkl_iso_wkl_split_bs_rshift/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │Bsp│Bsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴┬───┤     ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ Shift│Sft│     │ ↑ │\n     * ├────┴┬──┴┬──┴──┬┴───┴───┴───┴───┴───┴───┴──┬┴───┴┬───┬─┴───┤ ┌───┼───┼───┐\n     * │Ctrl │   │Alt  │                           │  Alt│   │ Ctrl│ │ ← │ ↓ │ → │\n     * └─────┘   └─────┴───────────────────────────┴─────┘   └─────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_iso_wkl_split_bs_rshift(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,              KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_BSPC, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_RSFT, KC_RSFT,                      KC_UP,\n        KC_LCTL,          KC_LALT,                            KC_SPC,                                      KC_RALT,          KC_RCTL,             KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_iso_wkl_split_bs_rshift/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless ISO Windows keyless layout with split Backspace and split Right Shift\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_iso_wkl_split_bs_rshift\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6.5, \"y\":0},\n                {\"x\":7.5, \"y\":0},\n                {\"x\":8.5, \"y\":0},\n                {\"x\":9.5, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0},\n                {\"x\":14, \"y\":0},\n\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1.25},\n                {\"x\":1, \"y\":1.25},\n                {\"x\":2, \"y\":1.25},\n                {\"x\":3, \"y\":1.25},\n                {\"x\":4, \"y\":1.25},\n                {\"x\":5, \"y\":1.25},\n                {\"x\":6, \"y\":1.25},\n                {\"x\":7, \"y\":1.25},\n                {\"x\":8, \"y\":1.25},\n                {\"x\":9, \"y\":1.25},\n                {\"x\":10, \"y\":1.25},\n                {\"x\":11, \"y\":1.25},\n                {\"x\":12, \"y\":1.25},\n                {\"x\":13, \"y\":1.25},\n                {\"x\":14, \"y\":1.25},\n\n                {\"x\":15.25, \"y\":1.25},\n                {\"x\":16.25, \"y\":1.25},\n                {\"x\":17.25, \"y\":1.25},\n\n                {\"x\":0, \"y\":2.25, \"w\":1.5},\n                {\"x\":1.5, \"y\":2.25},\n                {\"x\":2.5, \"y\":2.25},\n                {\"x\":3.5, \"y\":2.25},\n                {\"x\":4.5, \"y\":2.25},\n                {\"x\":5.5, \"y\":2.25},\n                {\"x\":6.5, \"y\":2.25},\n                {\"x\":7.5, \"y\":2.25},\n                {\"x\":8.5, \"y\":2.25},\n                {\"x\":9.5, \"y\":2.25},\n                {\"x\":10.5, \"y\":2.25},\n                {\"x\":11.5, \"y\":2.25},\n                {\"x\":12.5, \"y\":2.25},\n\n                {\"x\":15.25, \"y\":2.25},\n                {\"x\":16.25, \"y\":2.25},\n                {\"x\":17.25, \"y\":2.25},\n\n                {\"x\":0, \"y\":3.25, \"w\":1.75},\n                {\"x\":1.75, \"y\":3.25},\n                {\"x\":2.75, \"y\":3.25},\n                {\"x\":3.75, \"y\":3.25},\n                {\"x\":4.75, \"y\":3.25},\n                {\"x\":5.75, \"y\":3.25},\n                {\"x\":6.75, \"y\":3.25},\n                {\"x\":7.75, \"y\":3.25},\n                {\"x\":8.75, \"y\":3.25},\n                {\"x\":9.75, \"y\":3.25},\n                {\"x\":10.75, \"y\":3.25},\n                {\"x\":11.75, \"y\":3.25},\n                {\"x\":12.75, \"y\":3.25},\n                {\"x\":13.75, \"y\":2.25, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":4.25, \"w\":1.25},\n                {\"x\":1.25, \"y\":4.25},\n                {\"x\":2.25, \"y\":4.25},\n                {\"x\":3.25, \"y\":4.25},\n                {\"x\":4.25, \"y\":4.25},\n                {\"x\":5.25, \"y\":4.25},\n                {\"x\":6.25, \"y\":4.25},\n                {\"x\":7.25, \"y\":4.25},\n                {\"x\":8.25, \"y\":4.25},\n                {\"x\":9.25, \"y\":4.25},\n                {\"x\":10.25, \"y\":4.25},\n                {\"x\":11.25, \"y\":4.25},\n                {\"x\":12.25, \"y\":4.25, \"w\":1.75},\n                {\"x\":14, \"y\":4.25},\n\n                {\"x\":16.25, \"y\":4.25},\n\n                {\"x\":0, \"y\":5.25, \"w\":1.5},\n                {\"x\":2.5, \"y\":5.25, \"w\":1.5},\n                {\"x\":4, \"y\":5.25, \"w\":7},\n                {\"x\":11, \"y\":5.25, \"w\":1.5},\n                {\"x\":13.5, \"y\":5.25, \"w\":1.5},\n\n                {\"x\":15.25, \"y\":5.25},\n                {\"x\":16.25, \"y\":5.25},\n                {\"x\":17.25, \"y\":5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_iso_wkl_split_bs_rshift/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",\"\",{x:1.25},\"\"],\n[{w:1.5},\"\",{x:1,w:1.5},\"\",{w:7},\"\",{w:1.5},\"\",{x:1,w:1.5},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_iso_wkl_split_bs_rshift/readme.md",
    "content": "# tkl_iso_wkl_split_bs_rshift\n\n    LAYOUT_tkl_iso_split_bs_rshift\n"
  },
  {
    "path": "layouts/default/tkl_jis/default_tkl_jis/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┐   ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │Esc│   │F1 │F2 │F3 │F4 │ │F5 │F6 │F7 │F8 │ │F9 │F10│F11│F12│ │PSc│Scr│Pse│\n     * └───┘   └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┴───┘ └───┴───┴───┘\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ ┌───┬───┬───┐\n     * │ZHK│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ ^ │ ¥ │Bsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ @ │ [ │     │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘\n     * │ Eisu │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ : │ ] │    │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────┤     ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │ \\ │ Shft │     │ ↑ │\n     * ├────┬───┴┬──┴─┬─┴──┬┴───┴───┴───┴─┬─┴──┬┴───┼───┴┬──┴─┬────┤ ┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │Mhen│    Space     │Henk│Kana│Alt │GUI │Ctrl│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────┴──────────────┴────┴────┴────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_jis(\n        KC_ESC,           KC_F1,   KC_F2,   KC_F3,   KC_F4,   KC_F5,   KC_F6,   KC_F7,   KC_F8,   KC_F9,   KC_F10,  KC_F11,  KC_F12,     KC_PSCR, KC_SCRL, KC_PAUS,\n\n        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_INT3, KC_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH, KC_INT1, KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT, KC_INT5,                  KC_SPC,                     KC_INT4, KC_INT2, KC_RALT, KC_RGUI, KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_jis/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless JIS layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_jis\": {\n            \"layout\": [\n                {\"x\": 0, \"y\": 0},\n                {\"x\": 2, \"y\": 0},\n                {\"x\": 3, \"y\": 0},\n                {\"x\": 4, \"y\": 0},\n                {\"x\": 5, \"y\": 0},\n                {\"x\": 6.5, \"y\": 0},\n                {\"x\": 7.5, \"y\": 0},\n                {\"x\": 8.5, \"y\": 0},\n                {\"x\": 9.5, \"y\": 0},\n                {\"x\": 11, \"y\": 0},\n                {\"x\": 12, \"y\": 0},\n                {\"x\": 13, \"y\": 0},\n                {\"x\": 14, \"y\": 0},\n                {\"x\": 15.25, \"y\": 0},\n                {\"x\": 16.25, \"y\": 0},\n                {\"x\": 17.25, \"y\": 0},\n\n                {\"x\": 0, \"y\": 1.25},\n                {\"x\": 1, \"y\": 1.25},\n                {\"x\": 2, \"y\": 1.25},\n                {\"x\": 3, \"y\": 1.25},\n                {\"x\": 4, \"y\": 1.25},\n                {\"x\": 5, \"y\": 1.25},\n                {\"x\": 6, \"y\": 1.25},\n                {\"x\": 7, \"y\": 1.25},\n                {\"x\": 8, \"y\": 1.25},\n                {\"x\": 9, \"y\": 1.25},\n                {\"x\": 10, \"y\": 1.25},\n                {\"x\": 11, \"y\": 1.25},\n                {\"x\": 12, \"y\": 1.25},\n                {\"x\": 13, \"y\": 1.25},\n                {\"x\": 14, \"y\": 1.25},\n                {\"x\": 15.25, \"y\": 1.25},\n                {\"x\": 16.25, \"y\": 1.25},\n                {\"x\": 17.25, \"y\": 1.25},\n                {\"x\": 0, \"y\": 2.25, \"w\": 1.5},\n\n                {\"x\": 1.5, \"y\": 2.25},\n                {\"x\": 2.5, \"y\": 2.25},\n                {\"x\": 3.5, \"y\": 2.25},\n                {\"x\": 4.5, \"y\": 2.25},\n                {\"x\": 5.5, \"y\": 2.25},\n                {\"x\": 6.5, \"y\": 2.25},\n                {\"x\": 7.5, \"y\": 2.25},\n                {\"x\": 8.5, \"y\": 2.25},\n                {\"x\": 9.5, \"y\": 2.25},\n                {\"x\": 10.5, \"y\": 2.25},\n                {\"x\": 11.5, \"y\": 2.25},\n                {\"x\": 12.5, \"y\": 2.25},\n                {\"x\": 15.25, \"y\": 2.25},\n                {\"x\": 16.25, \"y\": 2.25},\n                {\"x\": 17.25, \"y\": 2.25},\n\n                {\"x\": 0, \"y\": 3.25, \"w\": 1.75},\n                {\"x\": 1.75, \"y\": 3.25},\n                {\"x\": 2.75, \"y\": 3.25},\n                {\"x\": 3.75, \"y\": 3.25},\n                {\"x\": 4.75, \"y\": 3.25},\n                {\"x\": 5.75, \"y\": 3.25},\n                {\"x\": 6.75, \"y\": 3.25},\n                {\"x\": 7.75, \"y\": 3.25},\n                {\"x\": 8.75, \"y\": 3.25},\n                {\"x\": 9.75, \"y\": 3.25},\n                {\"x\": 10.75, \"y\": 3.25},\n                {\"x\": 11.75, \"y\": 3.25},\n                {\"x\": 12.75, \"y\": 3.25},\n                {\"x\": 13.75, \"y\": 2.25, \"w\": 1.25, \"h\": 2},\n\n                {\"x\": 0, \"y\": 4.25, \"w\": 2.25},\n                {\"x\": 2.25, \"y\": 4.25},\n                {\"x\": 3.25, \"y\": 4.25},\n                {\"x\": 4.25, \"y\": 4.25},\n                {\"x\": 5.25, \"y\": 4.25},\n                {\"x\": 6.25, \"y\": 4.25},\n                {\"x\": 7.25, \"y\": 4.25},\n                {\"x\": 8.25, \"y\": 4.25},\n                {\"x\": 9.25, \"y\": 4.25},\n                {\"x\": 10.25, \"y\": 4.25},\n                {\"x\": 11.25, \"y\": 4.25},\n                {\"x\": 12.25, \"y\": 4.25},\n                {\"x\": 13.25, \"y\": 4.25, \"w\": 1.75},\n                {\"x\": 16.25, \"y\": 4.25},\n                \n                {\"x\": 0, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 1.25, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 2.5, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 3.75, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 5, \"y\": 5.25, \"w\": 3.75},\n                {\"x\": 8.75, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 10, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 11.25, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 12.5, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 13.75, \"y\": 5.25, \"w\": 1.25},\n                {\"x\": 15.25, \"y\": 5.25},\n                {\"x\": 16.25, \"y\": 5.25},\n                {\"x\": 17.25, \"y\": 5.25}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_jis/layout.json",
    "content": "[{a:7},\"\",{x:1},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.5},\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{y:0.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.75},\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:3.75},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_jis/readme.md",
    "content": "# tkl_jis\n\n    LAYOUT_tkl_jis\n\nA standard JIS TKL layout.\n"
  },
  {
    "path": "layouts/default/tkl_nofrow_ansi/default_tkl_nofrow_ansi/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │  \\  │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴─────┤ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │  Enter │\n     * ├──────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴────────┤     ┌───┐\n     * │ Shift  │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │\n     * ├────┬───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_nofrow_ansi(\n        KC_ESC,  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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_ENT,\n        KC_LSFT,          KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_nofrow_ansi/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless No F-Row ANSI Layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_nofrow_ansi\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n                {\"x\":13.5, \"y\":1, \"w\":1.5},\n\n                {\"x\":15.25, \"y\":1},\n                {\"x\":16.25, \"y\":1},\n                {\"x\":17.25, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2, \"w\":2.25},\n\n                {\"x\":0, \"y\":3, \"w\":2.25},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.75},\n\n                {\"x\":16.25, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":12.5, \"y\":4, \"w\":1.25},\n                {\"x\":13.75, \"y\":4, \"w\":1.25},\n\n                {\"x\":15.25, \"y\":4},\n                {\"x\":16.25, \"y\":4},\n                {\"x\":17.25, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_nofrow_ansi/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:1.5},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.25},\"\"],\n[{w:2.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n    \n"
  },
  {
    "path": "layouts/default/tkl_nofrow_ansi/readme.md",
    "content": "# tkl_nofrow_ansi\n\n    LAYOUT_tkl_nofrow_ansi\n"
  },
  {
    "path": "layouts/default/tkl_nofrow_iso/default_tkl_nofrow_iso/keymap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    /*\n     * ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───────┐ ┌───┬───┬───┐\n     * │Esc│ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ Backsp│ │Ins│Hom│PgU│\n     * ├───┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─────┤ ├───┼───┼───┤\n     * │ Tab │ Q │ W │ E │ R │ T │ Y │ U │ I │ O │ P │ [ │ ] │     │ │Del│End│PgD│\n     * ├─────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┐ Ent│ └───┴───┴───┘\n     * │ Caps │ A │ S │ D │ F │ G │ H │ J │ K │ L │ ; │ ' │ # │    │\n     * ├────┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴───┴────┤     ┌───┐\n     * │Shft│ \\ │ Z │ X │ C │ V │ B │ N │ M │ , │ . │ / │    Shift │     │ ↑ │\n     * ├────┼───┴┬──┴─┬─┴───┴───┴───┴───┴───┴──┬┴───┼───┴┬────┬────┤ ┌───┼───┼───┐\n     * │Ctrl│GUI │Alt │                        │ Alt│ GUI│Menu│Ctrl│ │ ← │ ↓ │ → │\n     * └────┴────┴────┴────────────────────────┴────┴────┴────┴────┘ └───┴───┴───┘\n     */\n    [0] = LAYOUT_tkl_nofrow_iso(\n        KC_ESC,  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_BSPC,    KC_INS,  KC_HOME, KC_PGUP,\n        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_DEL,  KC_END,  KC_PGDN,\n        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_NUHS, KC_ENT,\n        KC_LSFT, KC_NUBS, KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH,          KC_RSFT,             KC_UP,\n        KC_LCTL, KC_LGUI, KC_LALT,                            KC_SPC,                             KC_RALT, KC_RGUI, KC_APP,  KC_RCTL,    KC_LEFT, KC_DOWN, KC_RGHT\n    )\n};\n"
  },
  {
    "path": "layouts/default/tkl_nofrow_iso/info.json",
    "content": "{\n    \"keyboard_name\": \"Tenkeyless No F-Row ISO Layout\",\n    \"url\": \"\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT_tkl_nofrow_iso\": {\n            \"layout\": [\n                {\"x\":0, \"y\":0},\n                {\"x\":1, \"y\":0},\n                {\"x\":2, \"y\":0},\n                {\"x\":3, \"y\":0},\n                {\"x\":4, \"y\":0},\n                {\"x\":5, \"y\":0},\n                {\"x\":6, \"y\":0},\n                {\"x\":7, \"y\":0},\n                {\"x\":8, \"y\":0},\n                {\"x\":9, \"y\":0},\n                {\"x\":10, \"y\":0},\n                {\"x\":11, \"y\":0},\n                {\"x\":12, \"y\":0},\n                {\"x\":13, \"y\":0, \"w\":2},\n\n                {\"x\":15.25, \"y\":0},\n                {\"x\":16.25, \"y\":0},\n                {\"x\":17.25, \"y\":0},\n\n                {\"x\":0, \"y\":1, \"w\":1.5},\n                {\"x\":1.5, \"y\":1},\n                {\"x\":2.5, \"y\":1},\n                {\"x\":3.5, \"y\":1},\n                {\"x\":4.5, \"y\":1},\n                {\"x\":5.5, \"y\":1},\n                {\"x\":6.5, \"y\":1},\n                {\"x\":7.5, \"y\":1},\n                {\"x\":8.5, \"y\":1},\n                {\"x\":9.5, \"y\":1},\n                {\"x\":10.5, \"y\":1},\n                {\"x\":11.5, \"y\":1},\n                {\"x\":12.5, \"y\":1},\n\n                {\"x\":15.25, \"y\":1},\n                {\"x\":16.25, \"y\":1},\n                {\"x\":17.25, \"y\":1},\n\n                {\"x\":0, \"y\":2, \"w\":1.75},\n                {\"x\":1.75, \"y\":2},\n                {\"x\":2.75, \"y\":2},\n                {\"x\":3.75, \"y\":2},\n                {\"x\":4.75, \"y\":2},\n                {\"x\":5.75, \"y\":2},\n                {\"x\":6.75, \"y\":2},\n                {\"x\":7.75, \"y\":2},\n                {\"x\":8.75, \"y\":2},\n                {\"x\":9.75, \"y\":2},\n                {\"x\":10.75, \"y\":2},\n                {\"x\":11.75, \"y\":2},\n                {\"x\":12.75, \"y\":2},\n                {\"x\":13.75, \"y\":1, \"w\":1.25, \"h\":2},\n\n                {\"x\":0, \"y\":3, \"w\":1.25},\n                {\"x\":1.25, \"y\":3},\n                {\"x\":2.25, \"y\":3},\n                {\"x\":3.25, \"y\":3},\n                {\"x\":4.25, \"y\":3},\n                {\"x\":5.25, \"y\":3},\n                {\"x\":6.25, \"y\":3},\n                {\"x\":7.25, \"y\":3},\n                {\"x\":8.25, \"y\":3},\n                {\"x\":9.25, \"y\":3},\n                {\"x\":10.25, \"y\":3},\n                {\"x\":11.25, \"y\":3},\n                {\"x\":12.25, \"y\":3, \"w\":2.75},\n\n                {\"x\":16.25, \"y\":3},\n\n                {\"x\":0, \"y\":4, \"w\":1.25},\n                {\"x\":1.25, \"y\":4, \"w\":1.25},\n                {\"x\":2.5, \"y\":4, \"w\":1.25},\n                {\"x\":3.75, \"y\":4, \"w\":6.25},\n                {\"x\":10, \"y\":4, \"w\":1.25},\n                {\"x\":11.25, \"y\":4, \"w\":1.25},\n                {\"x\":12.5, \"y\":4, \"w\":1.25},\n                {\"x\":13.75, \"y\":4, \"w\":1.25},\n\n                {\"x\":15.25, \"y\":4},\n                {\"x\":16.25, \"y\":4},\n                {\"x\":17.25, \"y\":4}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "layouts/default/tkl_nofrow_iso/layout.json",
    "content": "[{a:7},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.5},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"\",{x:0.25},\"\",\"\",\"\"],\n[{w:1.75},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\"],\n[{w:1.25},\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",\"\",{w:2.75},\"\",{x:1.25},\"\"],\n[{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:6.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{w:1.25},\"\",{x:0.25},\"\",\"\",\"\"]\n"
  },
  {
    "path": "layouts/default/tkl_nofrow_iso/readme.md",
    "content": "# tkl_nofrow_iso\n\n    LAYOUT_tkl_nofrow_iso\n"
  },
  {
    "path": "lib/fnv/Makefile",
    "content": "#!/bin/make\n#\n# hash - makefile for FNV hash tools\n#\n# @(#) $Revision: 5.2 $\n# @(#) $Id: Makefile,v 5.2 2012/03/21 01:42:15 chongo Exp $\n# @(#) $Source: /usr/local/src/cmd/fnv/RCS/Makefile,v $\n#\n# See:\n#\thttp://www.isthe.com/chongo/tech/comp/fnv/index.html\n#\n# for the most up to date copy of this code and the FNV hash home page.\n#\n# Please do not copyright this code.  This code is in the public domain.\n#\n# LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO\n# EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF\n# USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n# PERFORMANCE OF THIS SOFTWARE.\n#\n# By:\n#\tchongo <Landon Curt Noll> /\\oo/\\\n#\thttp://www.isthe.com/chongo/\n#\n# Share and Enjoy!\t:-)\n\n# make tools\n#\nSHELL= /bin/sh\nCFLAGS= -O3 -g3\n#CFLAGS= -O2 -g3\n#CC= cc\nAR= ar\nTAR= tar\nEGREP= egrep\nGZIP_BIN= gzip\nINSTALL= install\n\n# If your system needs ranlib use:\n#\tRANLIB= ranlib\n# otherwise use:\n#\tRANLIB= :\n#\n#RANLIB= ranlib\nRANLIB= :\n\n# where to install things\n#\nDESTBIN= /usr/local/bin\nDESTLIB= /usr/local/lib\nDESTINC= /usr/local/include\n\n# what to build\n#\nSRC=\thash_32.c hash_32a.c hash_64.c hash_64a.c \\\n\tfnv32.c fnv64.c \\\n\thave_ulong64.c test_fnv.c\nNO64BIT_SRC= no64bit_fnv64.c no64bit_hash_64.c \\\n\tno64bit_hash_64a.c no64bit_test_fnv.c\nHSRC=\tfnv.h \\\n\tlonglong.h\nALL=\t${SRC} ${HSRC} \\\n\tREADME Makefile\nPROGS=\tfnv032 fnv064 fnv132 fnv164 fnv1a32 fnv1a64\nOBSOLETE_PROGS=\tfnv0_32 fnv0_64 fnv1_32 fnv1_64 fnv1a_32 fnv1a_64\nNO64BIT_PROGS= no64bit_fnv064 no64bit_fnv164 no64bit_fnv1a64\nLIBS=\tlibfnv.a\nLIBOBJ=\thash_32.o hash_64.o hash_32a.o hash_64a.o test_fnv.o\nNO64BIT_OBJ= no64bit_fnv64.o no64bit_hash_64.o \\\n\tno64bit_hash_64a.o no64bit_test_fnv.o\nOTHEROBJ= fnv32.o fnv64.o\nTARGETS= ${LIBOBJ} ${LIBS} ${PROGS}\n\n# default rule\n#\nall: ${TARGETS}\n\n# things to build\n#\nhash_32.o: hash_32.c longlong.h fnv.h\n\t${CC} ${CFLAGS} hash_32.c -c\n\nhash_64.o: hash_64.c longlong.h fnv.h\n\t${CC} ${CFLAGS} hash_64.c -c\n\nhash_32a.o: hash_32a.c longlong.h fnv.h\n\t${CC} ${CFLAGS} hash_32a.c -c\n\nhash_64a.o: hash_64a.c longlong.h fnv.h\n\t${CC} ${CFLAGS} hash_64a.c -c\n\ntest_fnv.o: test_fnv.c longlong.h fnv.h\n\t${CC} ${CFLAGS} test_fnv.c -c\n\nfnv32.o: fnv32.c longlong.h fnv.h\n\t${CC} ${CFLAGS} fnv32.c -c\n\nfnv032: fnv32.o libfnv.a\n\t${CC} fnv32.o libfnv.a -o fnv032\n\nfnv64.o: fnv64.c longlong.h fnv.h\n\t${CC} ${CFLAGS} fnv64.c -c\n\nfnv064: fnv64.o libfnv.a\n\t${CC} fnv64.o libfnv.a -o fnv064\n\nlibfnv.a: ${LIBOBJ}\n\trm -f $@\n\t${AR} rv $@ ${LIBOBJ}\n\t${RANLIB} $@\n\nfnv132: fnv032\n\t-rm -f $@\n\t-cp -f $? $@\n\nfnv1a32: fnv032\n\t-rm -f $@\n\t-cp -f $? $@\n\nfnv164: fnv064\n\t-rm -f $@\n\t-cp -f $? $@\n\nfnv1a64: fnv064\n\t-rm -f $@\n\t-cp -f $? $@\n\nlonglong.h: have_ulong64.c Makefile\n\t-@rm -f have_ulong64 have_ulong64.o ll_tmp longlong.h\n\t@echo 'forming longlong.h'\n\t@echo '/*' > longlong.h\n\t@echo ' * DO NOT EDIT -- generated by the Makefile' >> longlong.h\n\t@echo ' */' >> longlong.h\n\t@echo '' >> longlong.h\n\t@echo '#if !defined(__LONGLONG_H__)' >> longlong.h\n\t@echo '#define __LONGLONG_H__' >> longlong.h\n\t@echo '' >> longlong.h\n\t@echo '/* do we have/want to use a long long type? */' >> longlong.h\n\t-@rm -f have_ulong64.o have_ulong64\n\t-@${CC} ${CFLAGS} have_ulong64.c -c 2>/dev/null; true\n\t-@${CC} ${CFLAGS} have_ulong64.o -o have_ulong64 2>/dev/null; true\n\t-@${SHELL} -c \"./have_ulong64 > ll_tmp 2>/dev/null\" \\\n\t    >/dev/null 2>&1; true\n\t-@if [ -s ll_tmp ]; then \\\n\t    cat ll_tmp >> longlong.h; \\\n\telse \\\n\t    echo '#undef HAVE_64BIT_LONG_LONG\t/* no */' >> longlong.h; \\\n\tfi\n\t@echo '' >> longlong.h\n\t@echo '/*' >> longlong.h\n\t@echo ' * NO64BIT_LONG_LONG undef HAVE_64BIT_LONG_LONG' >> longlong.h\n\t@echo ' */' >> longlong.h\n\t@echo '#if defined(NO64BIT_LONG_LONG)' >> longlong.h\n\t@echo '#undef HAVE_64BIT_LONG_LONG' >> longlong.h\n\t@echo '#endif /* NO64BIT_LONG_LONG */' >> longlong.h\n\t@echo '' >> longlong.h\n\t@echo '#endif /* !__LONGLONG_H__ */' >> longlong.h\n\t-@rm -f have_ulong64 have_ulong64.o ll_tmp\n\t@echo 'longlong.h formed'\n\n# utilities\n#\ninstall: all\n\t-@if [ -d \"${DESTBIN}\" ]; then \\\n\t    echo \"\tmkdir -p ${DESTBIN}\"; \\\n\t    mkdir -p ${DESTBIN}; \\\n\tfi\n\t-@if [ -d \"${DESTLIB}\" ]; then \\\n\t    echo \"\tmkdir -p ${DESTLIB}\"; \\\n\t    mkdir -p ${DESTLIB}; \\\n\tfi\n\t-@if [ -d \"${DESTINC}\" ]; then \\\n\t    echo \"\tmkdir -p ${DESTINC}\"; \\\n\t    mkdir -p ${DESTINC}; \\\n\tfi\n\t${INSTALL} -m 0755 ${PROGS} ${DESTBIN}\n\t${INSTALL} -m 0644 ${LIBS} ${DESTLIB}\n\t${RANLIB} ${DESTLIB}/libfnv.a\n\t${INSTALL} -m 0644 ${HSRC} ${DESTINC}\n\t@# remove osolete programs\n\tfor i in ${OBSOLETE_PROGS}; do \\\n\t    if [ -f \"${DESTBIN}/$$i\" ]; then \\\n\t        echo \"rm -f ${DESTBIN}/$$i\"; \\\n\t\trm -f \"${DESTBIN}/$$i\"; \\\n\t    fi; \\\n\tdone\n\nclean:\n\t-rm -f have_ulong64 have_ulong64.o ll_tmp ll_tmp2 longlong.h\n\t-rm -f ${LIBOBJ}\n\t-rm -f ${OTHEROBJ}\n\nclobber: clean\n\t-rm -f ${TARGETS}\n\t-rm -f ${OBSOLETE_PROGS} lltmp lltmp2 ll_tmp\n\t-rm -f ${NO64BIT_SRC}\n\t-rm -f ${NO64BIT_OBJ}\n\t-rm -f ${NO64BIT_PROGS}\n\t-rm -f vector.c\n\ncheck: ${PROGS}\n\t@echo -n \"FNV-0 32 bit tests: \"\n\t@./fnv032 -t 1 -v\n\t@echo -n \"FNV-1 32 bit tests: \"\n\t@./fnv132 -t 1 -v\n\t@echo -n \"FNV-1a 32 bit tests: \"\n\t@./fnv1a32 -t 1 -v\n\t@echo -n \"FNV-0 64 bit tests: \"\n\t@./fnv064 -t 1 -v\n\t@echo -n \"FNV-1 64 bit tests: \"\n\t@./fnv164 -t 1 -v\n\t@echo -n \"FNV-1a 64 bit tests: \"\n\t@./fnv1a64 -t 1 -v\n\n###############################\n# generate test vector source #\n###############################\n\nno64bit_fnv64.c: fnv64.c\n\t-rm -f $@\n\t-cp -f $? $@\n\nno64bit_hash_64.c: hash_64.c\n\t-rm -f $@\n\t-cp -f $? $@\n\nno64bit_hash_64a.c: hash_64a.c\n\t-rm -f $@\n\t-cp -f $? $@\n\nno64bit_test_fnv.c: test_fnv.c\n\t-rm -f $@\n\t-cp -f $? $@\n\nno64bit_fnv64.o: no64bit_fnv64.c longlong.h fnv.h\n\t${CC} ${CFLAGS} -DNO64BIT_LONG_LONG no64bit_fnv64.c -c\n\nno64bit_hash_64.o: no64bit_hash_64.c longlong.h fnv.h\n\t${CC} ${CFLAGS} -DNO64BIT_LONG_LONG no64bit_hash_64.c -c\n\nno64bit_hash_64a.o: no64bit_hash_64a.c longlong.h fnv.h\n\t${CC} ${CFLAGS} -DNO64BIT_LONG_LONG no64bit_hash_64a.c -c\n\nno64bit_test_fnv.o: no64bit_test_fnv.c longlong.h fnv.h\n\t${CC} ${CFLAGS} -DNO64BIT_LONG_LONG no64bit_test_fnv.c -c\n\nno64bit_fnv064: no64bit_fnv64.o no64bit_hash_64.o \\\n\t\tno64bit_hash_64a.o no64bit_test_fnv.o\n\t${CC} ${CFLAGS} no64bit_fnv64.o no64bit_hash_64.o \\\n\t\t        no64bit_hash_64a.o no64bit_test_fnv.o -o $@\n\nno64bit_fnv164: no64bit_fnv064\n\t-rm -f $@\n\t-cp -f $? $@\n\nno64bit_fnv1a64: no64bit_fnv064\n\t-rm -f $@\n\t-cp -f $? $@\n\nvector.c: ${PROGS} ${NO64BIT_PROGS}\n\t-rm -f $@\n\techo '/* start of output generated by make $@ */' >> $@\n\techo '' >> $@\n\t#@\n\techo '/* FNV-0 32 bit test vectors */' >> $@\n\t./fnv032 -t 0 >> $@\n\techo '' >> $@\n\t#@\n\techo '/* FNV-1 32 bit test vectors */' >> $@\n\t./fnv132 -t 0 >> $@\n\techo '' >> $@\n\t#@\n\techo '/* FNV-1a 32 bit test vectors */' >> $@\n\t./fnv1a32 -t 0 >> $@\n\techo '' >> $@\n\t#@\n\techo '/* FNV-0 64 bit test vectors */' >> $@\n\techo '#if defined(HAVE_64BIT_LONG_LONG)' >> $@\n\t./fnv064 -t 0 >> $@\n\techo '#else /* HAVE_64BIT_LONG_LONG */' >> $@\n\t./no64bit_fnv064 -t 0 >> $@\n\techo '#endif /* HAVE_64BIT_LONG_LONG */' >> $@\n\techo '' >> $@\n\t#@\n\techo '/* FNV-1 64 bit test vectors */' >> $@\n\techo '#if defined(HAVE_64BIT_LONG_LONG)' >> $@\n\t./fnv164 -t 0 >> $@\n\techo '#else /* HAVE_64BIT_LONG_LONG */' >> $@\n\t./no64bit_fnv164 -t 0 >> $@\n\techo '#endif /* HAVE_64BIT_LONG_LONG */' >> $@\n\techo '' >> $@\n\t#@\n\techo '/* FNV-1a 64 bit test vectors */' >> $@\n\techo '#if defined(HAVE_64BIT_LONG_LONG)' >> $@\n\t./fnv1a64 -t 0 >> $@\n\techo '#else /* HAVE_64BIT_LONG_LONG */' >> $@\n\t./no64bit_fnv1a64 -t 0 >> $@\n\techo '#endif /* HAVE_64BIT_LONG_LONG */' >> $@\n\techo '' >> $@\n\t#@\n\techo '/* end of output generated by make $@ */' >> $@\n"
  },
  {
    "path": "lib/fnv/README",
    "content": "#=====================#\n# Fowler/Noll/Vo hash #\n#=====================#\n\nThe basis of this hash algorithm was taken from an idea sent\nas reviewer comments to the IEEE POSIX P1003.2 committee by:\n\n     Phong Vo (http://www.research.att.com/info/kpv)\n     Glenn Fowler (http://www.research.att.com/~gsf/)\n\nIn a subsequent ballot round:\n\n     Landon Curt Noll (http://www.isthe.com/chongo)\n\nimproved on their algorithm.  Some people tried this hash\nand found that it worked rather well.  In an EMail message\nto Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.\n\nFNV hashes are designed to be fast while maintaining a low\ncollision rate. The FNV speed allows one to quickly hash lots\nof data while maintaining a reasonable collision rate.  See:\n\n     http://www.isthe.com/chongo/tech/comp/fnv/index.html\n\nfor more details as well as other forms of the FNV hash.\nComments, questions, bug fixes and suggestions welcome at\nthe address given in the above URL.\n\n\n#==================#\n# FNV hash utility #\n#==================#\n\nTwo hash utilities (32 bit and 64 bit) are provided:\n\n\tfnv032 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...]\n\tfnv132 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...]\n\tfnv1a32 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...]\n\n\tfnv064 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...]\n\tfnv164 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...]\n\tfnv1a64 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...]\n\n\t-b bcnt\t  mask off all but the lower bcnt bits (default: 32)\n \t-m\t  multiple hashes, one per line for each arg\n\t-s\t  hash arg as a string (ignoring terminating NUL bytes)\n\t-t code\t  0 ==> generate test vectors, 1 ==> test FNV hash\n \t-v\t  verbose mode, print arg after hash (implies -m)\n\targ\t  string (if -s was given) or filename (default stdin)\n\nThe fnv032, fnv064 implement the historic FNV-0 hash.\nThe fnv132, fnv164 implement the recommended FNV-1 hash.\nThe fnv1a32, fnv1a64 implement the recommended FNV-1a hash.\n\nThis is the original historic FNV algorithm with a 0 offset basis.\nIt is recommended that FNV-1, with a non-0 offset basis be used instead.\n\nTo test FNV hashes, try:\n\n\tfnv032 -t 1 -v\n\tfnv132 -t 1 -v\n\tfnv1a32 -t 1 -v\n\n\tfnv064 -t 1 -v\n\tfnv164 -t 1 -v\n\tfnv1a64 -t 1 -v\n\nIf you are compiling, try:\n\n\tmake check\n\n\n#==================#\n# FNV hash library #\n#==================#\n\nThe libfnv.a library implements both a 32 bit and a 64 bit FNV hash\non collections of bytes, a NUL terminated strings or on an open file\ndescriptor.\n\nHere is the 32 bit FNV 1 hash:\n\n\tFnv32_t fnv_32_buf(void *buf, int len, Fnv32_t hval);\t/* byte buf */\n\tFnv32_t fnv_32_str(char *string, Fnv32_t hval);\t\t/* string */\n\nHere is the 32 bit FNV 1a hash:\n\n\tFnv32_t fnv_32a_buf(void *buf, int len, Fnv32_t hval);\t/* byte buf */\n\tFnv32_t fnv_32a_str(char *string, Fnv32_t hval);\t/* string */\n\nHere is the 64 bit FNV 1 hash:\n\n\tFnv64_t fnv_64_buf(void *buf, int len, Fnv64_t hval);\t/* byte buf */\n\tFnv64_t fnv_64_str(char *string, Fnv64_t hval);\t\t/* string */\n\nHere is the 64 bit FNV 1a hash:\n\n\tFnv64_t fnv_64a_buf(void *buf, int len, Fnv64_t hval);\t/* byte buf */\n\tFnv64_t fnv_64a_str(char *string, Fnv64_t hval);\t/* string */\n\nOn the first call to a hash function, one must supply the initial basis\nthat is appropriate for the hash in question:\n\n    FNV-0:\t(not recommended)\n\n\tFNV0_32_INIT\t\t/* 32 bit FNV-0 initial basis */\n\tFNV0_64_INIT\t\t/* 64 bit FNV-0 initial basis */\n\n    FNV-1:\n\n\tFNV1_32_INIT\t\t/* 32 bit FNV-1 initial basis */\n\tFNV1_64_INIT\t\t/* 64 bit FNV-1 initial basis */\n\n    FNV-1a:\n\n\tFNV1A_32_INIT\t\t/* 32 bit FNV-1a initial basis */\n\tFNV1A_64_INIT\t\t/* 64 bit FNV-1a initial basis */\n\nFor example to perform a 64 bit FNV-1 hash:\n\n\t#include \"fnv.h\"\n\n\tFnv64_t hash_val;\n\n\thash_val = fnv_64_str(\"a string\", FNV1_64_INIT);\n\thash_val = fnv_64_str(\"more string\", hash_val);\n\nproduces the same final hash value as:\n\n\thash_val = fnv_64_str(\"a stringmore string\", FNV1_64_INIT);\n\nNOTE: If one used 'FNV0_64_INIT' instead of 'FNV1_64_INIT' one would get the\n      historic FNV-0 hash instead recommended FNV-1 hash.\n\nTo perform a 32 bit FNV-1 hash:\n\n\t#include \"fnv.h\"\n\n\tFnv32_t hash_val;\n\n\thash_val = fnv_32_buf(buf, length_of_buf, FNV1_32_INIT);\n\thash_val = fnv_32_str(\"more data\", hash_val);\n\nTo perform a 64 bit FNV-1a hash:\n\n\t#include \"fnv.h\"\n\n\tFnv64_t hash_val;\n\n\thash_val = fnv_64a_buf(buf, length_of_buf, FNV1_64_INIT);\n\thash_val = fnv_64a_str(\"more data\", hash_val);\n\n=-=\n\nchongo <Landon Curt Noll> /\\oo/\\\nhttp://www.isthe.com/chongo\n\nShare and Enjoy!\n"
  },
  {
    "path": "lib/fnv/fnv.h",
    "content": "/*\n * fnv - Fowler/Noll/Vo- hash code\n *\n * @(#) $Revision: 5.4 $\n * @(#) $Id: fnv.h,v 5.4 2009/07/30 22:49:13 chongo Exp $\n * @(#) $Source: /usr/local/src/cmd/fnv/RCS/fnv.h,v $\n *\n ***\n *\n * Fowler/Noll/Vo- hash\n *\n * The basis of this hash algorithm was taken from an idea sent\n * as reviewer comments to the IEEE POSIX P1003.2 committee by:\n *\n *      Phong Vo (http://www.research.att.com/info/kpv/)\n *      Glenn Fowler (http://www.research.att.com/~gsf/)\n *\n * In a subsequent ballot round:\n *\n *      Landon Curt Noll (http://www.isthe.com/chongo/)\n *\n * improved on their algorithm.  Some people tried this hash\n * and found that it worked rather well.  In an EMail message\n * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.\n *\n * FNV hashes are designed to be fast while maintaining a low\n * collision rate. The FNV speed allows one to quickly hash lots\n * of data while maintaining a reasonable collision rate.  See:\n *\n *      http://www.isthe.com/chongo/tech/comp/fnv/index.html\n *\n * for more details as well as other forms of the FNV hash.\n *\n ***\n *\n * NOTE: The FNV-0 historic hash is not recommended.  One should use\n *\t the FNV-1 hash instead.\n *\n * To use the 32 bit FNV-0 historic hash, pass FNV0_32_INIT as the\n * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().\n *\n * To use the 64 bit FNV-0 historic hash, pass FNV0_64_INIT as the\n * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().\n *\n * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the\n * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().\n *\n * To use the recommended 64 bit FNV-1 hash, pass FNV1_64_INIT as the\n * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().\n *\n * To use the recommended 32 bit FNV-1a hash, pass FNV1_32A_INIT as the\n * Fnv32_t hashval argument to fnv_32a_buf() or fnv_32a_str().\n *\n * To use the recommended 64 bit FNV-1a hash, pass FNV1A_64_INIT as the\n * Fnv64_t hashval argument to fnv_64a_buf() or fnv_64a_str().\n *\n ***\n *\n * Please do not copyright this code.  This code is in the public domain.\n *\n * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO\n * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF\n * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n * PERFORMANCE OF THIS SOFTWARE.\n *\n * By:\n *\tchongo <Landon Curt Noll> /\\oo/\\\n *      http://www.isthe.com/chongo/\n *\n * Share and Enjoy!\t:-)\n */\n\n#if !defined(__FNV_H__)\n#define __FNV_H__\n\n#include <stdlib.h>\n#include <stdint.h>\n\n#define FNV_VERSION \"5.0.2\"\t/* @(#) FNV Version */\n\n\n/*\n * 32 bit FNV-0 hash type\n */\ntypedef uint32_t Fnv32_t;\n\n\n/*\n * 32 bit FNV-0 zero initial basis\n *\n * This historic hash is not recommended.  One should use\n * the FNV-1 hash and initial basis instead.\n */\n#define FNV0_32_INIT ((Fnv32_t)0)\n\n\n/*\n * 32 bit FNV-1 and FNV-1a non-zero initial basis\n *\n * The FNV-1 initial basis is the FNV-0 hash of the following 32 octets:\n *\n *              chongo <Landon Curt Noll> /\\../\\\n *\n * NOTE: The \\'s above are not back-slashing escape characters.\n * They are literal ASCII  backslash 0x5c characters.\n *\n * NOTE: The FNV-1a initial basis is the same value as FNV-1 by definition.\n */\n#define FNV1_32_INIT ((Fnv32_t)0x811c9dc5)\n#define FNV1_32A_INIT FNV1_32_INIT\n\n\n/*\n * determine how 64 bit unsigned values are represented\n */\n#include \"longlong.h\"\n\n\n/*\n * 64 bit FNV-0 hash\n */\n#if defined(HAVE_64BIT_LONG_LONG)\ntypedef uint64_t Fnv64_t;\n#else /* HAVE_64BIT_LONG_LONG */\ntypedef struct {\n    uint32_t w32[2]; /* w32[0] is low order, w32[1] is high order word */\n} Fnv64_t;\n#endif /* HAVE_64BIT_LONG_LONG */\n\n\n/*\n * 64 bit FNV-0 zero initial basis\n *\n * This historic hash is not recommended.  One should use\n * the FNV-1 hash and initial basis instead.\n */\n#if defined(HAVE_64BIT_LONG_LONG)\n#define FNV0_64_INIT ((Fnv64_t)0)\n#else /* HAVE_64BIT_LONG_LONG */\nextern const Fnv64_t fnv0_64_init;\n#define FNV0_64_INIT (fnv0_64_init)\n#endif /* HAVE_64BIT_LONG_LONG */\n\n\n/*\n * 64 bit FNV-1 non-zero initial basis\n *\n * The FNV-1 initial basis is the FNV-0 hash of the following 32 octets:\n *\n *              chongo <Landon Curt Noll> /\\../\\\n *\n * NOTE: The \\'s above are not back-slashing escape characters.\n * They are literal ASCII  backslash 0x5c characters.\n *\n * NOTE: The FNV-1a initial basis is the same value as FNV-1 by definition.\n */\n#if defined(HAVE_64BIT_LONG_LONG)\n#define FNV1_64_INIT ((Fnv64_t)0xcbf29ce484222325ULL)\n#define FNV1A_64_INIT FNV1_64_INIT\n#else /* HAVE_64BIT_LONG_LONG */\nextern const fnv1_64_init;\nextern const Fnv64_t fnv1a_64_init;\n#define FNV1_64_INIT (fnv1_64_init)\n#define FNV1A_64_INIT (fnv1a_64_init)\n#endif /* HAVE_64BIT_LONG_LONG */\n\n\n/*\n * hash types\n */\nenum fnv_type {\n    FNV_NONE = 0,\t/* invalid FNV hash type */\n    FNV0_32 = 1,\t/* FNV-0 32 bit hash */\n    FNV1_32 = 2,\t/* FNV-1 32 bit hash */\n    FNV1a_32 = 3,\t/* FNV-1a 32 bit hash */\n    FNV0_64 = 4,\t/* FNV-0 64 bit hash */\n    FNV1_64 = 5,\t/* FNV-1 64 bit hash */\n    FNV1a_64 = 6,\t/* FNV-1a 64 bit hash */\n};\n\n\n/*\n * these test vectors are used as part o the FNV test suite\n */\nstruct test_vector {\n    void *buf;\t\t/* start of test vector buffer */\n    int len;\t\t/* length of test vector */\n};\nstruct fnv0_32_test_vector {\n    struct test_vector *test;\t/* test vector buffer to hash */\n    Fnv32_t fnv0_32;\t\t/* expected FNV-0 32 bit hash value */\n};\nstruct fnv1_32_test_vector {\n    struct test_vector *test;\t/* test vector buffer to hash */\n    Fnv32_t fnv1_32;\t\t/* expected FNV-1 32 bit hash value */\n};\nstruct fnv1a_32_test_vector {\n    struct test_vector *test;\t/* test vector buffer to hash */\n    Fnv32_t fnv1a_32;\t\t/* expected FNV-1a 32 bit hash value */\n};\nstruct fnv0_64_test_vector {\n    struct test_vector *test;\t/* test vector buffer to hash */\n    Fnv64_t fnv0_64;\t\t/* expected FNV-0 64 bit hash value */\n};\nstruct fnv1_64_test_vector {\n    struct test_vector *test;\t/* test vector buffer to hash */\n    Fnv64_t fnv1_64;\t\t/* expected FNV-1 64 bit hash value */\n};\nstruct fnv1a_64_test_vector {\n    struct test_vector *test;\t/* test vector buffer to hash */\n    Fnv64_t fnv1a_64;\t\t/* expected FNV-1a 64 bit hash value */\n};\n\n\n/*\n * external functions\n */\n/* hash_32.c */\nextern Fnv32_t fnv_32_buf(void *buf, size_t len, Fnv32_t hashval);\nextern Fnv32_t fnv_32_str(char *buf, Fnv32_t hashval);\n\n/* hash_32a.c */\nextern Fnv32_t fnv_32a_buf(void *buf, size_t len, Fnv32_t hashval);\nextern Fnv32_t fnv_32a_str(char *buf, Fnv32_t hashval);\n\n/* hash_64.c */\nextern Fnv64_t fnv_64_buf(void *buf, size_t len, Fnv64_t hashval);\nextern Fnv64_t fnv_64_str(char *buf, Fnv64_t hashval);\n\n/* hash_64a.c */\nextern Fnv64_t fnv_64a_buf(void *buf, size_t len, Fnv64_t hashval);\nextern Fnv64_t fnv_64a_str(char *buf, Fnv64_t hashval);\n\n/* test_fnv.c */\nextern struct test_vector fnv_test_str[];\nextern struct fnv0_32_test_vector fnv0_32_vector[];\nextern struct fnv1_32_test_vector fnv1_32_vector[];\nextern struct fnv1a_32_test_vector fnv1a_32_vector[];\nextern struct fnv0_64_test_vector fnv0_64_vector[];\nextern struct fnv1_64_test_vector fnv1_64_vector[];\nextern struct fnv1a_64_test_vector fnv1a_64_vector[];\nextern void unknown_hash_type(char *prog, enum fnv_type type, int code);\nextern void print_fnv32(Fnv32_t hval, Fnv32_t mask, int verbose, char *arg);\nextern void print_fnv64(Fnv64_t hval, Fnv64_t mask, int verbose, char *arg);\n\n\n#endif /* __FNV_H__ */\n"
  },
  {
    "path": "lib/fnv/fnv32.c",
    "content": "/*\n * fnv32 - 32 bit Fowler/Noll/Vo hash of a buffer or string\n *\n * @(#) $Revision: 5.5 $\n * @(#) $Id: fnv32.c,v 5.5 2012/03/21 01:38:12 chongo Exp $\n * @(#) $Source: /usr/local/src/cmd/fnv/RCS/fnv32.c,v $\n *\n ***\n *\n * Fowler/Noll/Vo hash\n *\n * The basis of this hash algorithm was taken from an idea sent\n * as reviewer comments to the IEEE POSIX P1003.2 committee by:\n *\n *      Phong Vo (http://www.research.att.com/info/kpv/)\n *      Glenn Fowler (http://www.research.att.com/~gsf/)\n *\n * In a subsequent ballot round:\n *\n *      Landon Curt Noll (http://www.isthe.com/chongo/)\n *\n * improved on their algorithm.  Some people tried this hash\n * and found that it worked rather well.  In an EMail message\n * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.\n *\n * FNV hashes are designed to be fast while maintaining a low\n * collision rate. The FNV speed allows one to quickly hash lots\n * of data while maintaining a reasonable collision rate.  See:\n *\n *      http://www.isthe.com/chongo/tech/comp/fnv/index.html\n *\n * for more details as well as other forms of the FNV hash.\n *\n ***\n *\n * Please do not copyright this code.  This code is in the public domain.\n *\n * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO\n * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF\n * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n * PERFORMANCE OF THIS SOFTWARE.\n *\n * By:\n *\tchongo <Landon Curt Noll> /\\oo/\\\n *      http://www.isthe.com/chongo/\n *\n * Share and Enjoy!\t:-)\n */\n\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <string.h>\n#include \"longlong.h\"\n#include \"fnv.h\"\n\n#define WIDTH 32\t\t/* bit width of hash */\n\n#define BUF_SIZE (32*1024)\t/* number of bytes to hash at a time */\n\nstatic char *usage =\n\"usage: %s [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...]\\n\"\n\"\\n\"\n\"\\t-b bcnt\\tmask off all but the lower bcnt bits (default 32)\\n\"\n\"\\t-m\\tmultiple hashes, one per line for each arg\\n\"\n\"\\t-s\\thash arg as a string (ignoring terminating NUL bytes)\\n\"\n\"\\t-t code\\t  test hash code: (0 ==> generate test vectors\\n\"\n\"\\t\\t\\t\\t   1 ==> validate against FNV test vectors)\\n\"\n\"\\t-v\\tverbose mode, print arg after hash (implies -m)\\n\"\n\"\\targ\\tstring (if -s was given) or filename (default stdin)\\n\"\n\"\\n\"\n\"\\tNOTE: Programs that begin with fnv0 implement the FNV-0 hash.\\n\"\n\"\\t      The FNV-0 hash is historic FNV algorithm that is now deprecated.\\n\"\n\"\\n\"\n\"\\tSee http://www.isthe.com/chongo/tech/comp/fnv/index.html for more info.\\n\"\n\"\\n\"\n\"\\t@(#) FNV Version: %s\\n\";\nstatic char *program;\t/* our name */\n\n\n/*\n * test_fnv32 - test the FNV32 hash\n *\n * given:\n *\thash_type\ttype of FNV hash to test\n *\tinit_hval\tinitial hash value\n *\tmask\t  \tlower bit mask\n *\tv_flag\t  \t1 => print test failure info on stderr\n *\tcode\t  0 ==> generate FNV test vectors\n *\t\t  1 ==> validate against FNV test vectors\n *\n * returns:\t0 ==> OK, else test vector failure number\n */\nstatic int\ntest_fnv32(enum fnv_type hash_type, Fnv32_t init_hval,\n\t   Fnv32_t mask, int v_flag, int code)\n{\n    struct test_vector *t;\t/* FNV test vestor */\n    Fnv32_t hval;\t\t/* current hash value */\n    int tstnum;\t\t\t/* test vector that failed, starting at 1 */\n\n    /*\n     * print preamble if generating test vectors\n     */\n    if (code == 0) {\n\tswitch (hash_type) {\n\tcase FNV0_32:\n\t    printf(\"struct fnv0_32_test_vector fnv0_32_vector[] = {\\n\");\n\t    break;\n\tcase FNV1_32:\n\t    printf(\"struct fnv1_32_test_vector fnv1_32_vector[] = {\\n\");\n\t    break;\n\tcase FNV1a_32:\n\t    printf(\"struct fnv1a_32_test_vector fnv1a_32_vector[] = {\\n\");\n\t    break;\n\tdefault:\n\t    unknown_hash_type(program, hash_type, 12);\t/* exit(12) */\n\t    /*NOTREACHED*/\n    \t}\n    }\n\n    /*\n     * loop thru all test vectors\n     */\n    for (t = fnv_test_str, tstnum = 1; t->buf != NULL; ++t, ++tstnum) {\n\n        /*\n\t * compute the FNV hash\n\t */\n\thval = init_hval;\n\tswitch (hash_type) {\n\tcase FNV0_32:\n\tcase FNV1_32:\n\t    hval = fnv_32_buf(t->buf, t->len, hval);\n\t    break;\n\tcase FNV1a_32:\n\t    hval = fnv_32a_buf(t->buf, t->len, hval);\n\t    break;\n\tdefault:\n\t    unknown_hash_type(program, hash_type, 13);\t/* exit(13) */\n\t    /*NOTREACHED*/\n\t}\n\n\t/*\n\t * print the vector\n\t */\n\tswitch (code) {\n\tcase 0:\t\t/* generate the test vector */\n\t    printf(\"    { &fnv_test_str[%d], (Fnv32_t) 0x%08lxUL },\\n\",\n\t    \t    tstnum-1, hval & mask);\n    \t    break;\n\tcase 1:\t\t/* validate against test vector */\n\t    switch (hash_type) {\n\t    case FNV0_32:\n\t\tif ((hval&mask) != (fnv0_32_vector[tstnum-1].fnv0_32 & mask)) {\n\t\t    if (v_flag) {\n\t\t    \tfprintf(stderr, \"%s: failed fnv0_32 test # %d\\n\",\n\t\t\t\tprogram, tstnum);\n\t\t    \tfprintf(stderr, \"%s: test # 1 is 1st test\\n\", program);\n\t\t\tfprintf(stderr,\n\t\t\t    \"%s: expected 0x%08lx != generated: 0x%08lx\\n\",\n\t\t\t    program, (hval&mask),\n\t\t\t    (fnv0_32_vector[tstnum-1].fnv0_32 & mask));\n\t\t    }\n\t\t    return tstnum;\n\t\t}\n\t    \tbreak;\n\t    case FNV1_32:\n\t\tif ((hval&mask) != (fnv1_32_vector[tstnum-1].fnv1_32 & mask)) {\n\t\t    if (v_flag) {\n\t\t    \tfprintf(stderr, \"%s: failed fnv1_32 test # %d\\n\",\n\t\t\t\tprogram, tstnum);\n\t\t    \tfprintf(stderr, \"%s: test # 1 is 1st test\\n\", program);\n\t\t\tfprintf(stderr,\n\t\t\t    \"%s: expected 0x%08lx != generated: 0x%08lx\\n\",\n\t\t\t    program, (hval&mask),\n\t\t\t    (fnv1_32_vector[tstnum-1].fnv1_32 & mask));\n\t\t    }\n\t\t    return tstnum;\n\t\t}\n\t    \tbreak;\n\t    case FNV1a_32:\n\t\tif ((hval&mask) != (fnv1a_32_vector[tstnum-1].fnv1a_32 &mask)) {\n\t\t    if (v_flag) {\n\t\t    \tfprintf(stderr, \"%s: failed fnv1a_32 test # %d\\n\",\n\t\t\t\tprogram, tstnum);\n\t\t    \tfprintf(stderr, \"%s: test # 1 is 1st test\\n\", program);\n\t\t\tfprintf(stderr,\n\t\t\t    \"%s: expected 0x%08lx != generated: 0x%08lx\\n\",\n\t\t\t    program, (hval&mask),\n\t\t\t    (fnv1a_32_vector[tstnum-1].fnv1a_32 & mask));\n\t\t    }\n\t\t    return tstnum;\n\t\t}\n\t    \tbreak;\n\t    }\n\t    break;\n    \tdefault:\n\t    fprintf(stderr, \"%s: -m %d not implemented yet\\n\", program, code);\n\t    exit(14);\n    \t}\n    }\n\n    /*\n     * print completion if generating test vectors\n     */\n    if (code == 0) {\n\tprintf(\"    { NULL, 0 }\\n\");\n\tprintf(\"};\\n\");\n    }\n\n    /*\n     * no failures, return code 0 ==> all OK\n     */\n    return 0;\n}\n\n\n/*\n * main - the main function\n *\n * See the above usage for details.\n */\nint\nmain(int argc, char *argv[])\n{\n    char buf[BUF_SIZE+1];\t/* read buffer */\n    int readcnt;\t\t/* number of characters written */\n    Fnv32_t hval;\t\t/* current hash value */\n    int s_flag = 0;\t\t/* 1 => -s was given, hash args as strings */\n    int m_flag = 0;\t\t/* 1 => print multiple hashes, one per arg */\n    int v_flag = 0;\t\t/* 1 => verbose hash print */\n    int b_flag = WIDTH;\t\t/* -b flag value */\n    int t_flag = -1;\t\t/* FNV test vector code (0=>print, 1=>test) */\n    enum fnv_type hash_type = FNV_NONE;\t/* type of FNV hash to perform */\n    Fnv32_t bmask;\t\t/* mask to apply to output */\n    extern char *optarg;\t/* option argument */\n    extern int optind;\t\t/* argv index of the next arg */\n    int fd;\t\t\t/* open file to process */\n    char *p;\n    int i;\n\n    /*\n     * parse args\n     */\n    program = argv[0];\n    while ((i = getopt(argc, argv, \"b:mst:v\")) != -1) {\n\tswitch (i) {\n\tcase 'b':\t/* bcnt bit mask count */\n\t    b_flag = atoi(optarg);\n\t    break;\n\tcase 'm':\t/* print multiple hashes, one per arg */\n\t    m_flag = 1;\n\t    break;\n\tcase 's':\t/* hash args as strings */\n\t    s_flag = 1;\n\t    break;\n\tcase 't':\t/* FNV test vector code */\n\t    t_flag = atoi(optarg);\n\t    if (t_flag < 0 || t_flag > 1) {\n\t\tfprintf(stderr, \"%s: -t code must be 0 or 1\\n\", program);\n\t\tfprintf(stderr, usage, program, FNV_VERSION);\n\t\texit(1);\n\t    }\n\t    m_flag = 1;\n\t    break;\n\tcase 'v':\t/* verbose hash print */\n\t    m_flag = 1;\n\t    v_flag = 1;\n\t    break;\n\tdefault:\n\t    fprintf(stderr, usage, program, FNV_VERSION);\n\t    exit(1);\n\t}\n    }\n    /* -t code incompatible with -b, -m and args */\n    if (t_flag >= 0) {\n    \tif (b_flag != WIDTH) {\n\t    fprintf(stderr, \"%s: -t code incompatible with -b\\n\", program);\n\t    exit(2);\n\t}\n    \tif (s_flag != 0) {\n\t    fprintf(stderr, \"%s: -t code incompatible with -s\\n\", program);\n\t    exit(3);\n\t}\n\tif (optind < argc) {\n\t    fprintf(stderr, \"%s: -t code incompatible args\\n\", program);\n\t    exit(4);\n\t}\n    }\n    /* -s requires at least 1 arg */\n    if (s_flag && optind >= argc) {\n\tfprintf(stderr, usage, program, FNV_VERSION);\n\texit(5);\n    }\n    /* limit -b values */\n    if (b_flag < 0 || b_flag > WIDTH) {\n\tfprintf(stderr, \"%s: -b bcnt: %d must be >= 0 and < %d\\n\",\n\t\tprogram, b_flag, WIDTH);\n\texit(6);\n    }\n    if (b_flag == WIDTH) {\n\tbmask = (Fnv32_t)0xffffffff;\n    } else {\n\tbmask = (Fnv32_t)((1 << b_flag) - 1);\n    }\n\n    /*\n     * start with the initial basis depending on the hash type\n     */\n    p = strrchr(program, '/');\n    if (p == NULL) {\n\tp = program;\n    } else {\n\t++p;\n    }\n    if (strcmp(p, \"fnv032\") == 0) {\n\t/* using non-recommended FNV-0 and zero initial basis */\n\thval = FNV0_32_INIT;\n\thash_type = FNV0_32;\n    } else if (strcmp(p, \"fnv132\") == 0) {\n\t/* using FNV-1 and non-zero initial basis */\n\thval = FNV1_32_INIT;\n\thash_type = FNV1_32;\n    } else if (strcmp(p, \"fnv1a32\") == 0) {\n\t /* start with the FNV-1a initial basis */\n\thval = FNV1_32A_INIT;\n\thash_type = FNV1a_32;\n    } else {\n\tfprintf(stderr, \"%s: unknown program name, unknown hash type\\n\",\n\t\tprogram);\n\texit(7);\n    }\n\n    /*\n     * FNV test vector processing, if needed\n     */\n    if (t_flag >= 0) {\n\tint code;\t\t/* test vector that failed, starting at 1 */\n\n\t/*\n\t * perform all tests\n\t */\n\tcode = test_fnv32(hash_type, hval, bmask, v_flag, t_flag);\n\n\t/*\n\t * evaluate the tests\n\t */\n\tif (code == 0) {\n\t    if (v_flag) {\n\t    \tprintf(\"passed\\n\");\n\t    }\n\t    exit(0);\n\t} else {\n\t    printf(\"failed vector (1 is 1st test): %d\\n\", code);\n\t    exit(8);\n\t}\n    }\n\n    /*\n     * string hashing\n     */\n    if (s_flag) {\n\n\t/* hash any other strings */\n\tfor (i=optind; i < argc; ++i) {\n\t    switch (hash_type) {\n\t    case FNV0_32:\n\t    case FNV1_32:\n\t\thval = fnv_32_str(argv[i], hval);\n\t    \tbreak;\n\t    case FNV1a_32:\n\t\thval = fnv_32a_str(argv[i], hval);\n\t\tbreak;\n\t    default:\n\t\tunknown_hash_type(program, hash_type, 9);\t/* exit(9) */\n\t\t/*NOTREACHED*/\n\t    }\n\t    if (m_flag) {\n\t\tprint_fnv32(hval, bmask, v_flag, argv[i]);\n\t    }\n\t}\n\n\n    /*\n     * file hashing\n     */\n    } else {\n\n\t/*\n\t * case: process only stdin\n\t */\n\tif (optind >= argc) {\n\n\t    /* case: process only stdin */\n\t    while ((readcnt = read(0, buf, BUF_SIZE)) > 0) {\n\t\tswitch (hash_type) {\n\t\tcase FNV0_32:\n\t\tcase FNV1_32:\n\t\t    hval = fnv_32_buf(buf, readcnt, hval);\n\t\t    break;\n\t\tcase FNV1a_32:\n\t\t    hval = fnv_32a_buf(buf, readcnt, hval);\n\t\t    break;\n\t\tdefault:\n\t\t    unknown_hash_type(program, hash_type, 10);\t/* exit(10) */\n\t\t    /*NOTREACHED*/\n\t\t}\n\t    }\n\t    if (m_flag) {\n\t\tprint_fnv32(hval, bmask, v_flag, \"(stdin)\");\n\t    }\n\n\t} else {\n\n\t    /*\n\t     * process any other files\n\t     */\n\t    for (i=optind; i < argc; ++i) {\n\n\t\t/* open the file */\n\t\tfd = open(argv[i], O_RDONLY);\n\t\tif (fd < 0) {\n\t\t    fprintf(stderr, \"%s: unable to open file: %s\\n\",\n\t\t\t    program, argv[i]);\n\t\t    exit(4);\n\t\t}\n\n\t\t/*  hash the file */\n\t\twhile ((readcnt = read(fd, buf, BUF_SIZE)) > 0) {\n\t\t    switch (hash_type) {\n\t\t    case FNV0_32:\n\t\t    case FNV1_32:\n\t\t\thval = fnv_32_buf(buf, readcnt, hval);\n\t\t\tbreak;\n\t\t    case FNV1a_32:\n\t\t\thval = fnv_32a_buf(buf, readcnt, hval);\n\t\t\tbreak;\n\t\t    default:\n\t\t\tunknown_hash_type(program, hash_type, 11);/* exit(11) */\n\t\t\t/*NOTREACHED*/\n\t\t    }\n\t\t}\n\n\t\t/* finish processing the file */\n\t\tif (m_flag) {\n\t\t    print_fnv32(hval, bmask, v_flag, argv[i]);\n\t\t}\n\t\tclose(fd);\n\t    }\n\t}\n    }\n\n    /*\n     * report hash and exit\n     */\n    if (!m_flag) {\n\tprint_fnv32(hval, bmask, v_flag, \"\");\n    }\n    return 0;\t/* exit(0); */\n}\n"
  },
  {
    "path": "lib/fnv/fnv64.c",
    "content": "/*\n * fnv_64 - 64 bit Fowler/Noll/Vo hash of a buffer or string\n *\n * @(#) $Revision: 5.5 $\n * @(#) $Id: fnv64.c,v 5.5 2012/03/21 01:38:12 chongo Exp $\n * @(#) $Source: /usr/local/src/cmd/fnv/RCS/fnv64.c,v $\n *\n ***\n *\n * Fowler/Noll/Vo hash\n *\n * The basis of this hash algorithm was taken from an idea sent\n * as reviewer comments to the IEEE POSIX P1003.2 committee by:\n *\n *      Phong Vo (http://www.research.att.com/info/kpv/)\n *      Glenn Fowler (http://www.research.att.com/~gsf/)\n *\n * In a subsequent ballot round:\n *\n *      Landon Curt Noll (http://www.isthe.com/chongo/)\n *\n * improved on their algorithm.  Some people tried this hash\n * and found that it worked rather well.  In an EMail message\n * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.\n *\n * FNV hashes are designed to be fast while maintaining a low\n * collision rate. The FNV speed allows one to quickly hash lots\n * of data while maintaining a reasonable collision rate.  See:\n *\n *      http://www.isthe.com/chongo/tech/comp/fnv/index.html\n *\n * for more details as well as other forms of the FNV hash.\n *\n ***\n *\n * Please do not copyright this code.  This code is in the public domain.\n *\n * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO\n * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF\n * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n * PERFORMANCE OF THIS SOFTWARE.\n *\n * By:\n *\tchongo <Landon Curt Noll> /\\oo/\\\n *      http://www.isthe.com/chongo/\n *\n * Share and Enjoy!\t:-)\n */\n\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <string.h>\n#include \"longlong.h\"\n#include \"fnv.h\"\n\n#define WIDTH 64\t/* bit width of hash */\n\n#define BUF_SIZE (32*1024)\t/* number of bytes to hash at a time */\n\nstatic char *usage =\n\"usage: %s [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...]\\n\"\n\"\\n\"\n\"\\t-b bcnt\\tmask off all but the lower bcnt bits (default 64)\\n\"\n\"\\t-m\\tmultiple hashes, one per line for each arg\\n\"\n\"\\t-s\\thash arg as a string (ignoring terminating NUL bytes)\\n\"\n\"\\t-t code\\t  test hash code: (0 ==> generate test vectors\\n\"\n\"\\t\\t\\t\\t   1 ==> validate against FNV test vectors)\\n\"\n\"\\t-v\\tverbose mode, print arg after hash (implies -m)\\n\"\n\"\\targ\\tstring (if -s was given) or filename (default stdin)\\n\"\n\"\\n\"\n\"\\tNOTE: Programs that begin with fnv0 implement the FNV-0 hash.\\n\"\n\"\\t      The FNV-0 hash is historic FNV algorithm that is now deprecated.\\n\"\n\"\\n\"\n\"\\tSee http://www.isthe.com/chongo/tech/comp/fnv/index.html for more info.\\n\"\n\"\\n\"\n\"\\t@(#) FNV Version: %s\\n\";\nstatic char *program;\t/* our name */\n\n\n/*\n * test_fnv64 - test the FNV64 hash\n *\n * given:\n *\thash_type\ttype of FNV hash to test\n *\tinit_hval\tinitial hash value\n *\tmask\t  \tlower bit mask\n *\tv_flag\t  \t1 => print test failure info on stderr\n *\tcode\t  0 ==> generate FNV test vectors\n *\t\t  1 ==> validate against FNV test vectors\n *\n * returns:\t0 ==> OK, else test vector failure number\n */\nstatic int\ntest_fnv64(enum fnv_type hash_type, Fnv64_t init_hval,\n\t   Fnv64_t mask, int v_flag, int code)\n{\n    struct test_vector *t;\t/* FNV test vestor */\n    Fnv64_t hval;\t\t/* current hash value */\n    int tstnum;\t\t\t/* test vector that failed, starting at 1 */\n\n    /*\n     * print preamble if generating test vectors\n     */\n    if (code == 0) {\n\tswitch (hash_type) {\n\tcase FNV0_64:\n\t    printf(\"struct fnv0_64_test_vector fnv0_64_vector[] = {\\n\");\n\t    break;\n\tcase FNV1_64:\n\t    printf(\"struct fnv1_64_test_vector fnv1_64_vector[] = {\\n\");\n\t    break;\n\tcase FNV1a_64:\n\t    printf(\"struct fnv1a_64_test_vector fnv1a_64_vector[] = {\\n\");\n\t    break;\n\tdefault:\n\t    unknown_hash_type(program, hash_type, 12);\t/* exit(12) */\n\t    /*NOTREACHED*/\n    \t}\n    }\n\n    /*\n     * loop thru all test vectors\n     */\n    for (t = fnv_test_str, tstnum = 1; t->buf != NULL; ++t, ++tstnum) {\n\n        /*\n\t * compute the FNV hash\n\t */\n\thval = init_hval;\n\tswitch (hash_type) {\n\tcase FNV0_64:\n\tcase FNV1_64:\n\t    hval = fnv_64_buf(t->buf, t->len, hval);\n\t    break;\n\tcase FNV1a_64:\n\t    hval = fnv_64a_buf(t->buf, t->len, hval);\n\t    break;\n\tdefault:\n\t    unknown_hash_type(program, hash_type, 13);\t/* exit(13) */\n\t    /*NOTREACHED*/\n\t}\n\n\t/*\n\t * print the vector\n\t */\n#if defined(HAVE_64BIT_LONG_LONG)\n\t/*\n\t * HAVE_64BIT_LONG_LONG testing\n\t */\n\tswitch (code) {\n\tcase 0:\t\t/* generate the test vector */\n\t    printf(\"    { &fnv_test_str[%d], (Fnv64_t) 0x%016llxULL },\\n\",\n\t    \t   tstnum-1, hval & mask);\n\t    break;\n\n\tcase 1:\t\t/* validate against test vector */\n\t    switch (hash_type) {\n\t    case FNV0_64:\n\t\tif ((hval&mask) != (fnv0_64_vector[tstnum-1].fnv0_64 & mask)) {\n\t\t    if (v_flag) {\n\t\t    \tfprintf(stderr, \"%s: failed fnv0_64 test # %d\\n\",\n\t\t\t\tprogram, tstnum);\n\t\t    \tfprintf(stderr, \"%s: test # 1 is 1st test\\n\", program);\n\t\t\tfprintf(stderr,\n\t\t\t    \"%s: expected 0x%016llx != generated: 0x%016llx\\n\",\n\t\t\t    program,\n\t\t\t    (hval&mask),\n\t\t\t    (fnv0_64_vector[tstnum-1].fnv0_64 & mask));\n\t\t    }\n\t\t    return tstnum;\n\t\t}\n\t    \tbreak;\n\t    case FNV1_64:\n\t\tif ((hval&mask) != (fnv1_64_vector[tstnum-1].fnv1_64 & mask)) {\n\t\t    if (v_flag) {\n\t\t    \tfprintf(stderr, \"%s: failed fnv1_64 test # %d\\n\",\n\t\t\t\tprogram, tstnum);\n\t\t    \tfprintf(stderr, \"%s: test # 1 is 1st test\\n\", program);\n\t\t\tfprintf(stderr,\n\t\t\t    \"%s: expected 0x%016llx != generated: 0x%016llx\\n\",\n\t\t\t    program,\n\t\t\t    (hval&mask),\n\t\t\t    (fnv1_64_vector[tstnum-1].fnv1_64 & mask));\n\t\t    }\n\t\t    return tstnum;\n\t\t}\n\t    \tbreak;\n\t    case FNV1a_64:\n\t\tif ((hval&mask) != (fnv1a_64_vector[tstnum-1].fnv1a_64 &mask)) {\n\t\t    if (v_flag) {\n\t\t    \tfprintf(stderr, \"%s: failed fnv1a_64 test # %d\\n\",\n\t\t\t\tprogram, tstnum);\n\t\t    \tfprintf(stderr, \"%s: test # 1 is 1st test\\n\", program);\n\t\t\tfprintf(stderr,\n\t\t\t    \"%s: expected 0x%016llx != generated: 0x%016llx\\n\",\n\t\t\t    program,\n\t\t\t    (hval&mask),\n\t\t\t    (fnv1a_64_vector[tstnum-1].fnv1a_64 & mask));\n\t\t    }\n\t\t    return tstnum;\n\t\t}\n\t    \tbreak;\n\t    }\n\t    break;\n\n    \tdefault:\n\t    fprintf(stderr, \"%s: -m %d not implemented yet\\n\", program, code);\n\t    exit(14);\n    \t}\n#else /* HAVE_64BIT_LONG_LONG */\n\t/*\n\t * non HAVE_64BIT_LONG_LONG testing\n\t */\n\tswitch (code) {\n\tcase 0:\t\t/* generate the test vector */\n\t    printf(\"    { &fnv_test_str[%d], \"\n\t    \t   \"(Fnv64_t) {0x%08lxUL, 0x%08lxUL} },\\n\",\n\t    \t   tstnum-1,\n\t\t   (hval.w32[0] & mask.w32[0]),\n\t\t   (hval.w32[1] & mask.w32[1]));\n\t    break;\n\n\tcase 1:\t\t/* validate against test vector */\n\t    switch (hash_type) {\n\t    case FNV0_64:\n\t\tif (((hval.w32[0] & mask.w32[0]) !=\n\t\t     (fnv0_64_vector[tstnum-1].fnv0_64.w32[0] &\n\t\t      mask.w32[0])) &&\n\t\t    ((hval.w32[1] & mask.w32[1]) !=\n\t\t     (fnv0_64_vector[tstnum-1].fnv0_64.w32[1] &\n\t\t      mask.w32[1]))) {\n\t\t    if (v_flag) {\n\t\t    \tfprintf(stderr, \"%s: failed fnv0_64 test # %d\\n\",\n\t\t\t\tprogram, tstnum);\n\t\t    \tfprintf(stderr, \"%s: test # 1 is 1st test\\n\", program);\n\t\t\tfprintf(stderr,\n\t\t\t    \"%s: expected 0x%08llx%08llx != \"\n\t\t\t    \"generated: 0x%08llx%08llx\\n\",\n\t\t\t    program,\n\t\t\t    (hval.w32[0] & mask.w32[0]),\n\t\t\t    (hval.w32[1] & mask.w32[1]),\n\t\t\t    ((fnv0_64_vector[tstnum-1].fnv0_64.w32[0] &\n\t\t\t     mask.w32[0])),\n\t\t\t    ((fnv0_64_vector[tstnum-1].fnv0_64.w32[1] &\n\t\t\t     mask.w32[1])));\n\t\t    }\n\t\t    return tstnum;\n\t\t}\n\t    \tbreak;\n\t    case FNV1_64:\n\t\tif (((hval.w32[0] & mask.w32[0]) !=\n\t\t     (fnv1_64_vector[tstnum-1].fnv1_64.w32[0] &\n\t\t      mask.w32[0])) &&\n\t\t    ((hval.w32[1] & mask.w32[1]) !=\n\t\t     (fnv1_64_vector[tstnum-1].fnv1_64.w32[1] &\n\t\t      mask.w32[1]))) {\n\t\t    if (v_flag) {\n\t\t    \tfprintf(stderr, \"%s: failed fnv1_64 test # %d\\n\",\n\t\t\t\tprogram, tstnum);\n\t\t    \tfprintf(stderr, \"%s: test # 1 is 1st test\\n\", program);\n\t\t\tfprintf(stderr,\n\t\t\t    \"%s: expected 0x%08llx%08llx != \"\n\t\t\t    \"generated: 0x%08llx%08llx\\n\",\n\t\t\t    program,\n\t\t\t    (hval.w32[0] & mask.w32[0]),\n\t\t\t    (hval.w32[1] & mask.w32[1]),\n\t\t\t    ((fnv1_64_vector[tstnum-1].fnv1_64.w32[0] &\n\t\t\t     mask.w32[0])),\n\t\t\t    ((fnv1_64_vector[tstnum-1].fnv1_64.w32[1] &\n\t\t\t     mask.w32[1])));\n\t\t    }\n\t\t    return tstnum;\n\t\t}\n\t    \tbreak;\n\t    case FNV1a_64:\n\t\tif (((hval.w32[0] & mask.w32[0]) !=\n\t\t     (fnv1a_64_vector[tstnum-1].fnv1a_64.w32[0] &\n\t\t      mask.w32[0])) &&\n\t\t    ((hval.w32[1] & mask.w32[1]) !=\n\t\t     (fnv1a_64_vector[tstnum-1].fnv1a_64.w32[1] &\n\t\t      mask.w32[1]))) {\n\t\t    if (v_flag) {\n\t\t    \tfprintf(stderr, \"%s: failed fnv1a_64 test # %d\\n\",\n\t\t\t\tprogram, tstnum);\n\t\t    \tfprintf(stderr, \"%s: test # 1 is 1st test\\n\", program);\n\t\t\tfprintf(stderr,\n\t\t\t    \"%s: expected 0x%08llx%08llx != \"\n\t\t\t    \"generated: 0x%08llx%08llx\\n\",\n\t\t\t    program,\n\t\t\t    (hval.w32[0] & mask.w32[0]),\n\t\t\t    (hval.w32[1] & mask.w32[1]),\n\t\t\t    ((fnv1a_64_vector[tstnum-1].fnv1a_64.w32[0] &\n\t\t\t     mask.w32[0])),\n\t\t\t    ((fnv1a_64_vector[tstnum-1].fnv1a_64.w32[1] &\n\t\t\t     mask.w32[1])));\n\t\t    }\n\t\t    return tstnum;\n\t\t}\n\t    \tbreak;\n\t    }\n\t    break;\n\n    \tdefault:\n\t    fprintf(stderr, \"%s: -m %d not implemented yet\\n\", program, code);\n\t    exit(15);\n    \t}\n#endif /* HAVE_64BIT_LONG_LONG */\n    }\n\n    /*\n     * print completion if generating test vectors\n     */\n    if (code == 0) {\n#if defined(HAVE_64BIT_LONG_LONG)\n\tprintf(\"    { NULL, (Fnv64_t) 0 }\\n\");\n#else /* HAVE_64BIT_LONG_LONG */\n\tprintf(\"    { NULL, (Fnv64_t) {0,0} }\\n\");\n#endif /* HAVE_64BIT_LONG_LONG */\n\tprintf(\"};\\n\");\n    }\n\n    /*\n     * no failures, return code 0 ==> all OK\n     */\n    return 0;\n}\n\n\n/*\n * main - the main function\n *\n * See the above usage for details.\n */\nint\nmain(int argc, char *argv[])\n{\n    char buf[BUF_SIZE+1];\t/* read buffer */\n    int readcnt;\t\t/* number of characters written */\n    Fnv64_t hval;\t\t/* current hash value */\n    int s_flag = 0;\t\t/* 1 => -s was given, hash args as strings */\n    int m_flag = 0;\t\t/* 1 => print multiple hashes, one per arg */\n    int v_flag = 0;\t\t/* 1 => verbose hash print */\n    int b_flag = WIDTH;\t\t/* -b flag value */\n    int t_flag = -1;\t\t/* FNV test vector code (0=>print, 1=>test) */\n    enum fnv_type hash_type = FNV_NONE;\t/* type of FNV hash to perform */\n    Fnv64_t bmask;\t\t/* mask to apply to output */\n    extern char *optarg;\t/* option argument */\n    extern int optind;\t\t/* argv index of the next arg */\n    int fd;\t\t\t/* open file to process */\n    char *p;\n    int i;\n\n    /*\n     * parse args\n     */\n    program = argv[0];\n    while ((i = getopt(argc, argv, \"b:mst:v\")) != -1) {\n\tswitch (i) {\n\tcase 'b':\t/* bcnt bit mask count */\n\t    b_flag = atoi(optarg);\n\t    break;\n\tcase 'm':\t/* print multiple hashes, one per arg */\n\t    m_flag = 1;\n\t    break;\n\tcase 's':\t/* hash args as strings */\n\t    s_flag = 1;\n\t    break;\n\tcase 't':\t/* FNV test vector code */\n\t    t_flag = atoi(optarg);\n\t    if (t_flag < 0 || t_flag > 1) {\n\t\tfprintf(stderr, \"%s: -t code must be 0 or 1\\n\", program);\n\t\tfprintf(stderr, usage, program, FNV_VERSION);\n\t\texit(1);\n\t    }\n\t    m_flag = 1;\n\t    break;\n\tcase 'v':\t/* verbose hash print */\n\t    m_flag = 1;\n\t    v_flag = 1;\n\t    break;\n\tdefault:\n\t    fprintf(stderr, usage, program, FNV_VERSION);\n\t    exit(1);\n\t}\n    }\n    /* -t code incompatible with -b, -m and args */\n    if (t_flag >= 0) {\n    \tif (b_flag != WIDTH) {\n\t    fprintf(stderr, \"%s: -t code incompatible with -b\\n\", program);\n\t    exit(2);\n\t}\n    \tif (s_flag != 0) {\n\t    fprintf(stderr, \"%s: -t code incompatible with -s\\n\", program);\n\t    exit(3);\n\t}\n\tif (optind < argc) {\n\t    fprintf(stderr, \"%s: -t code incompatible args\\n\", program);\n\t    exit(4);\n\t}\n    }\n    /* -s requires at least 1 arg */\n    if (s_flag && optind >= argc) {\n\tfprintf(stderr, usage, program, FNV_VERSION);\n\texit(5);\n    }\n    /* limit -b values */\n    if (b_flag < 0 || b_flag > WIDTH) {\n\tfprintf(stderr, \"%s: -b bcnt: %d must be >= 0 and < %d\\n\",\n\t\tprogram, b_flag, WIDTH);\n\texit(6);\n    }\n#if defined(HAVE_64BIT_LONG_LONG)\n    if (b_flag == WIDTH) {\n\tbmask = (Fnv64_t)0xffffffffffffffffULL;\n    } else {\n\tbmask = (Fnv64_t)((1ULL << b_flag) - 1ULL);\n    }\n#else /* HAVE_64BIT_LONG_LONG */\n    if (b_flag == WIDTH) {\n\tbmask.w32[0] = 0xffffffffUL;\n\tbmask.w32[1] = 0xffffffffUL;\n    } else if (b_flag >= WIDTH/2) {\n\tbmask.w32[0] = 0xffffffffUL;\n\tbmask.w32[1] = ((1UL << (b_flag-(WIDTH/2))) - 1UL);\n    } else {\n\tbmask.w32[0] = ((1UL << b_flag) - 1UL);\n\tbmask.w32[1] = 0UL;\n    }\n#endif /* HAVE_64BIT_LONG_LONG */\n\n    /*\n     * start with the initial basis depending on the hash type\n     */\n    p = strrchr(program, '/');\n    if (p == NULL) {\n\tp = program;\n    } else {\n\t++p;\n    }\n    if (strcmp(p, \"fnv064\") == 0 || strcmp(p, \"no64bit_fnv064\") == 0) {\n\t/* using non-recommended FNV-0 and zero initial basis */\n\thval = FNV0_64_INIT;\n\thash_type = FNV0_64;\n    } else if (strcmp(p, \"fnv164\") == 0 || strcmp(p, \"no64bit_fnv164\") == 0) {\n\t/* using FNV-1 and non-zero initial basis */\n\thval = FNV1_64_INIT;\n\thash_type = FNV1_64;\n    } else if (strcmp(p, \"fnv1a64\") == 0 || strcmp(p, \"no64bit_fnv1a64\") == 0) {\n\t /* start with the FNV-1a initial basis */\n\thval = FNV1A_64_INIT;\n\thash_type = FNV1a_64;\n    } else {\n\tfprintf(stderr, \"%s: unknown program name, unknown hash type\\n\",\n\t\tprogram);\n\texit(7);\n    }\n\n    /*\n     * FNV test vector processing, if needed\n     */\n    if (t_flag >= 0) {\n\tint code;\t\t/* test vector that failed, starting at 1 */\n\n\t/*\n\t * perform all tests\n\t */\n\tcode = test_fnv64(hash_type, hval, bmask, v_flag, t_flag);\n\n\t/*\n\t * evaluate the tests\n\t */\n\tif (code == 0) {\n\t    if (v_flag) {\n\t    \tprintf(\"passed\\n\");\n\t    }\n\t    exit(0);\n\t} else {\n\t    printf(\"failed vector (1 is 1st test): %d\\n\", code);\n\t    exit(8);\n\t}\n    }\n\n    /*\n     * string hashing\n     */\n    if (s_flag) {\n\n\t/* hash any other strings */\n\tfor (i=optind; i < argc; ++i) {\n\t    switch (hash_type) {\n\t    case FNV0_64:\n\t    case FNV1_64:\n\t\thval = fnv_64_str(argv[i], hval);\n\t    \tbreak;\n\t    case FNV1a_64:\n\t\thval = fnv_64a_str(argv[i], hval);\n\t\tbreak;\n\t    default:\n\t\tunknown_hash_type(program, hash_type, 9);\t/* exit(9) */\n\t\t/*NOTREACHED*/\n\t    }\n\t    if (m_flag) {\n\t\tprint_fnv64(hval, bmask, v_flag, argv[i]);\n\t    }\n\t}\n\n\n    /*\n     * file hashing\n     */\n    } else {\n\n\t/*\n\t * case: process only stdin\n\t */\n\tif (optind >= argc) {\n\n\t    /* case: process only stdin */\n\t    while ((readcnt = read(0, buf, BUF_SIZE)) > 0) {\n\t\tswitch (hash_type) {\n\t\tcase FNV0_64:\n\t\tcase FNV1_64:\n\t\t    hval = fnv_64_buf(buf, readcnt, hval);\n\t\t    break;\n\t\tcase FNV1a_64:\n\t\t    hval = fnv_64a_buf(buf, readcnt, hval);\n\t\t    break;\n\t\tdefault:\n\t\t    unknown_hash_type(program, hash_type, 10);\t/* exit(10) */\n\t\t    /*NOTREACHED*/\n\t\t}\n\t    }\n\t    if (m_flag) {\n\t\tprint_fnv64(hval, bmask, v_flag, \"(stdin)\");\n\t    }\n\n\t} else {\n\n\t    /*\n\t     * process any other files\n\t     */\n\t    for (i=optind; i < argc; ++i) {\n\n\t\t/* open the file */\n\t\tfd = open(argv[i], O_RDONLY);\n\t\tif (fd < 0) {\n\t\t    fprintf(stderr, \"%s: unable to open file: %s\\n\",\n\t\t\t    program, argv[i]);\n\t\t    exit(4);\n\t\t}\n\n\t\t/*  hash the file */\n\t\twhile ((readcnt = read(fd, buf, BUF_SIZE)) > 0) {\n\t\t    switch (hash_type) {\n\t\t    case FNV0_64:\n\t\t    case FNV1_64:\n\t\t\thval = fnv_64_buf(buf, readcnt, hval);\n\t\t\tbreak;\n\t\t    case FNV1a_64:\n\t\t\thval = fnv_64a_buf(buf, readcnt, hval);\n\t\t\tbreak;\n\t\t    default:\n\t\t\tunknown_hash_type(program, hash_type, 11);/* exit(11) */\n\t\t\t/*NOTREACHED*/\n\t\t    }\n\t\t}\n\n\t\t/* finish processing the file */\n\t\tif (m_flag) {\n\t\t    print_fnv64(hval, bmask, v_flag, argv[i]);\n\t\t}\n\t\tclose(fd);\n\t    }\n\t}\n    }\n\n    /*\n     * report hash and exit\n     */\n    if (!m_flag) {\n\tprint_fnv64(hval, bmask, v_flag, \"\");\n    }\n    return 0;\t/* exit(0); */\n}\n"
  },
  {
    "path": "lib/fnv/hash_32.c",
    "content": "/*\n * hash_32 - 32 bit Fowler/Noll/Vo hash code\n *\n * @(#) $Revision: 5.1 $\n * @(#) $Id: hash_32.c,v 5.1 2009/06/30 09:13:32 chongo Exp $\n * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_32.c,v $\n *\n ***\n *\n * Fowler/Noll/Vo hash\n *\n * The basis of this hash algorithm was taken from an idea sent\n * as reviewer comments to the IEEE POSIX P1003.2 committee by:\n *\n *      Phong Vo (http://www.research.att.com/info/kpv/)\n *      Glenn Fowler (http://www.research.att.com/~gsf/)\n *\n * In a subsequent ballot round:\n *\n *      Landon Curt Noll (http://www.isthe.com/chongo/)\n *\n * improved on their algorithm.  Some people tried this hash\n * and found that it worked rather well.  In an EMail message\n * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.\n *\n * FNV hashes are designed to be fast while maintaining a low\n * collision rate. The FNV speed allows one to quickly hash lots\n * of data while maintaining a reasonable collision rate.  See:\n *\n *      http://www.isthe.com/chongo/tech/comp/fnv/index.html\n *\n * for more details as well as other forms of the FNV hash.\n ***\n *\n * NOTE: The FNV-0 historic hash is not recommended.  One should use\n *\t the FNV-1 hash instead.\n *\n * To use the 32 bit FNV-0 historic hash, pass FNV0_32_INIT as the\n * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().\n *\n * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the\n * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().\n *\n ***\n *\n * Please do not copyright this code.  This code is in the public domain.\n *\n * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO\n * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF\n * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n * PERFORMANCE OF THIS SOFTWARE.\n *\n * By:\n *\tchongo <Landon Curt Noll> /\\oo/\\\n *      http://www.isthe.com/chongo/\n *\n * Share and Enjoy!\t:-)\n */\n\n#include <stdlib.h>\n#include \"fnv.h\"\n\n\n/*\n * 32 bit magic FNV-0 and FNV-1 prime\n */\n#define FNV_32_PRIME ((Fnv32_t)0x01000193)\n\n\n/*\n * fnv_32_buf - perform a 32 bit Fowler/Noll/Vo hash on a buffer\n *\n * input:\n *\tbuf\t- start of buffer to hash\n *\tlen\t- length of buffer in octets\n *\thval\t- previous hash value or 0 if first call\n *\n * returns:\n *\t32 bit hash as a static hash type\n *\n * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval\n *\t argument on the first call to either fnv_32_buf() or fnv_32_str().\n *\n * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval\n *\t argument on the first call to either fnv_32_buf() or fnv_32_str().\n */\nFnv32_t\nfnv_32_buf(void *buf, size_t len, Fnv32_t hval)\n{\n    unsigned char *bp = (unsigned char *)buf;\t/* start of buffer */\n    unsigned char *be = bp + len;\t\t/* beyond end of buffer */\n\n    /*\n     * FNV-1 hash each octet in the buffer\n     */\n    while (bp < be) {\n\n\t/* multiply by the 32 bit FNV magic prime mod 2^32 */\n#if defined(NO_FNV_GCC_OPTIMIZATION)\n\thval *= FNV_32_PRIME;\n#else\n\thval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);\n#endif\n\n\t/* xor the bottom with the current octet */\n\thval ^= (Fnv32_t)*bp++;\n    }\n\n    /* return our new hash value */\n    return hval;\n}\n\n\n/*\n * fnv_32_str - perform a 32 bit Fowler/Noll/Vo hash on a string\n *\n * input:\n *\tstr\t- string to hash\n *\thval\t- previous hash value or 0 if first call\n *\n * returns:\n *\t32 bit hash as a static hash type\n *\n * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval\n *\t argument on the first call to either fnv_32_buf() or fnv_32_str().\n *\n * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval\n *\t argument on the first call to either fnv_32_buf() or fnv_32_str().\n */\nFnv32_t\nfnv_32_str(char *str, Fnv32_t hval)\n{\n    unsigned char *s = (unsigned char *)str;\t/* unsigned string */\n\n    /*\n     * FNV-1 hash each octet in the buffer\n     */\n    while (*s) {\n\n\t/* multiply by the 32 bit FNV magic prime mod 2^32 */\n#if defined(NO_FNV_GCC_OPTIMIZATION)\n\thval *= FNV_32_PRIME;\n#else\n\thval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);\n#endif\n\n\t/* xor the bottom with the current octet */\n\thval ^= (Fnv32_t)*s++;\n    }\n\n    /* return our new hash value */\n    return hval;\n}\n"
  },
  {
    "path": "lib/fnv/hash_32a.c",
    "content": "/*\n * hash_32 - 32 bit Fowler/Noll/Vo FNV-1a hash code\n *\n * @(#) $Revision: 5.1 $\n * @(#) $Id: hash_32a.c,v 5.1 2009/06/30 09:13:32 chongo Exp $\n * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_32a.c,v $\n *\n ***\n *\n * Fowler/Noll/Vo hash\n *\n * The basis of this hash algorithm was taken from an idea sent\n * as reviewer comments to the IEEE POSIX P1003.2 committee by:\n *\n *      Phong Vo (http://www.research.att.com/info/kpv/)\n *      Glenn Fowler (http://www.research.att.com/~gsf/)\n *\n * In a subsequent ballot round:\n *\n *      Landon Curt Noll (http://www.isthe.com/chongo/)\n *\n * improved on their algorithm.  Some people tried this hash\n * and found that it worked rather well.  In an EMail message\n * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.\n *\n * FNV hashes are designed to be fast while maintaining a low\n * collision rate. The FNV speed allows one to quickly hash lots\n * of data while maintaining a reasonable collision rate.  See:\n *\n *      http://www.isthe.com/chongo/tech/comp/fnv/index.html\n *\n * for more details as well as other forms of the FNV hash.\n ***\n *\n * To use the recommended 32 bit FNV-1a hash, pass FNV1_32A_INIT as the\n * Fnv32_t hashval argument to fnv_32a_buf() or fnv_32a_str().\n *\n ***\n *\n * Please do not copyright this code.  This code is in the public domain.\n *\n * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO\n * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF\n * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n * PERFORMANCE OF THIS SOFTWARE.\n *\n * By:\n *\tchongo <Landon Curt Noll> /\\oo/\\\n *      http://www.isthe.com/chongo/\n *\n * Share and Enjoy!\t:-)\n */\n\n#include <stdlib.h>\n#include \"fnv.h\"\n\n\n/*\n * 32 bit magic FNV-1a prime\n */\n#define FNV_32_PRIME ((Fnv32_t)0x01000193)\n\n\n/*\n * fnv_32a_buf - perform a 32 bit Fowler/Noll/Vo FNV-1a hash on a buffer\n *\n * input:\n *\tbuf\t- start of buffer to hash\n *\tlen\t- length of buffer in octets\n *\thval\t- previous hash value or 0 if first call\n *\n * returns:\n *\t32 bit hash as a static hash type\n *\n * NOTE: To use the recommended 32 bit FNV-1a hash, use FNV1_32A_INIT as the\n * \t hval arg on the first call to either fnv_32a_buf() or fnv_32a_str().\n */\nFnv32_t\nfnv_32a_buf(void *buf, size_t len, Fnv32_t hval)\n{\n    unsigned char *bp = (unsigned char *)buf;\t/* start of buffer */\n    unsigned char *be = bp + len;\t\t/* beyond end of buffer */\n\n    /*\n     * FNV-1a hash each octet in the buffer\n     */\n    while (bp < be) {\n\n\t/* xor the bottom with the current octet */\n\thval ^= (Fnv32_t)*bp++;\n\n\t/* multiply by the 32 bit FNV magic prime mod 2^32 */\n#if defined(NO_FNV_GCC_OPTIMIZATION)\n\thval *= FNV_32_PRIME;\n#else\n\thval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);\n#endif\n    }\n\n    /* return our new hash value */\n    return hval;\n}\n\n\n/*\n * fnv_32a_str - perform a 32 bit Fowler/Noll/Vo FNV-1a hash on a string\n *\n * input:\n *\tstr\t- string to hash\n *\thval\t- previous hash value or 0 if first call\n *\n * returns:\n *\t32 bit hash as a static hash type\n *\n * NOTE: To use the recommended 32 bit FNV-1a hash, use FNV1_32A_INIT as the\n *  \t hval arg on the first call to either fnv_32a_buf() or fnv_32a_str().\n */\nFnv32_t\nfnv_32a_str(char *str, Fnv32_t hval)\n{\n    unsigned char *s = (unsigned char *)str;\t/* unsigned string */\n\n    /*\n     * FNV-1a hash each octet in the buffer\n     */\n    while (*s) {\n\n\t/* xor the bottom with the current octet */\n\thval ^= (Fnv32_t)*s++;\n\n\t/* multiply by the 32 bit FNV magic prime mod 2^32 */\n#if defined(NO_FNV_GCC_OPTIMIZATION)\n\thval *= FNV_32_PRIME;\n#else\n\thval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);\n#endif\n    }\n\n    /* return our new hash value */\n    return hval;\n}\n"
  },
  {
    "path": "lib/fnv/hash_64.c",
    "content": "/*\n * hash_64 - 64 bit Fowler/Noll/Vo-0 hash code\n *\n * @(#) $Revision: 5.1 $\n * @(#) $Id: hash_64.c,v 5.1 2009/06/30 09:01:38 chongo Exp $\n * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64.c,v $\n *\n ***\n *\n * Fowler/Noll/Vo hash\n *\n * The basis of this hash algorithm was taken from an idea sent\n * as reviewer comments to the IEEE POSIX P1003.2 committee by:\n *\n *      Phong Vo (http://www.research.att.com/info/kpv/)\n *      Glenn Fowler (http://www.research.att.com/~gsf/)\n *\n * In a subsequent ballot round:\n *\n *      Landon Curt Noll (http://www.isthe.com/chongo/)\n *\n * improved on their algorithm.  Some people tried this hash\n * and found that it worked rather well.  In an EMail message\n * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.\n *\n * FNV hashes are designed to be fast while maintaining a low\n * collision rate. The FNV speed allows one to quickly hash lots\n * of data while maintaining a reasonable collision rate.  See:\n *\n *      http://www.isthe.com/chongo/tech/comp/fnv/index.html\n *\n * for more details as well as other forms of the FNV hash.\n *\n ***\n *\n * NOTE: The FNV-0 historic hash is not recommended.  One should use\n *\t the FNV-1 hash instead.\n *\n * To use the 64 bit FNV-0 historic hash, pass FNV0_64_INIT as the\n * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().\n *\n * To use the recommended 64 bit FNV-1 hash, pass FNV1_64_INIT as the\n * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().\n *\n ***\n *\n * Please do not copyright this code.  This code is in the public domain.\n *\n * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO\n * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF\n * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n * PERFORMANCE OF THIS SOFTWARE.\n *\n * By:\n *\tchongo <Landon Curt Noll> /\\oo/\\\n *      http://www.isthe.com/chongo/\n *\n * Share and Enjoy!\t:-)\n */\n\n#include <stdlib.h>\n#include \"fnv.h\"\n\n\n/*\n * FNV-0 defines the initial basis to be zero\n */\n#if !defined(HAVE_64BIT_LONG_LONG)\nconst Fnv64_t fnv0_64_init = { 0UL, 0UL };\n#endif /* ! HAVE_64BIT_LONG_LONG */\n\n\n/*\n * FNV-1 defines the initial basis to be non-zero\n */\n#if !defined(HAVE_64BIT_LONG_LONG)\nconst Fnv64_t fnv1_64_init = { 0x84222325UL, 0xcbf29ce4UL };\n#endif /* ! HAVE_64BIT_LONG_LONG */\n\n\n/*\n * 64 bit magic FNV-0 and FNV-1 prime\n */\n#if defined(HAVE_64BIT_LONG_LONG)\n#define FNV_64_PRIME ((Fnv64_t)0x100000001b3ULL)\n#else /* HAVE_64BIT_LONG_LONG */\n#define FNV_64_PRIME_LOW ((unsigned long)0x1b3)\t/* lower bits of FNV prime */\n#define FNV_64_PRIME_SHIFT (8)\t\t/* top FNV prime shift above 2^32 */\n#endif /* HAVE_64BIT_LONG_LONG */\n\n\n/*\n * fnv_64_buf - perform a 64 bit Fowler/Noll/Vo hash on a buffer\n *\n * input:\n *\tbuf\t- start of buffer to hash\n *\tlen\t- length of buffer in octets\n *\thval\t- previous hash value or 0 if first call\n *\n * returns:\n *\t64 bit hash as a static hash type\n *\n * NOTE: To use the 64 bit FNV-0 historic hash, use FNV0_64_INIT as the hval\n *\t argument on the first call to either fnv_64_buf() or fnv_64_str().\n *\n * NOTE: To use the recommended 64 bit FNV-1 hash, use FNV1_64_INIT as the hval\n *\t argument on the first call to either fnv_64_buf() or fnv_64_str().\n */\nFnv64_t\nfnv_64_buf(void *buf, size_t len, Fnv64_t hval)\n{\n    unsigned char *bp = (unsigned char *)buf;\t/* start of buffer */\n    unsigned char *be = bp + len;\t\t/* beyond end of buffer */\n\n#if defined(HAVE_64BIT_LONG_LONG)\n\n    /*\n     * FNV-1 hash each octet of the buffer\n     */\n    while (bp < be) {\n\n\t/* multiply by the 64 bit FNV magic prime mod 2^64 */\n#if defined(NO_FNV_GCC_OPTIMIZATION)\n\thval *= FNV_64_PRIME;\n#else /* NO_FNV_GCC_OPTIMIZATION */\n\thval += (hval << 1) + (hval << 4) + (hval << 5) +\n\t\t(hval << 7) + (hval << 8) + (hval << 40);\n#endif /* NO_FNV_GCC_OPTIMIZATION */\n\n\t/* xor the bottom with the current octet */\n\thval ^= (Fnv64_t)*bp++;\n    }\n\n#else /* HAVE_64BIT_LONG_LONG */\n\n    unsigned long val[4];\t\t\t/* hash value in base 2^16 */\n    unsigned long tmp[4];\t\t\t/* tmp 64 bit value */\n\n    /*\n     * Convert Fnv64_t hval into a base 2^16 array\n     */\n    val[0] = hval.w32[0];\n    val[1] = (val[0] >> 16);\n    val[0] &= 0xffff;\n    val[2] = hval.w32[1];\n    val[3] = (val[2] >> 16);\n    val[2] &= 0xffff;\n\n    /*\n     * FNV-1 hash each octet of the buffer\n     */\n    while (bp < be) {\n\n\t/*\n\t * multiply by the 64 bit FNV magic prime mod 2^64\n\t *\n\t * Using 0x100000001b3 we have the following digits base 2^16:\n\t *\n\t *\t0x0\t0x100\t0x0\t0x1b3\n\t *\n\t * which is the same as:\n\t *\n\t *\t0x0\t1<<FNV_64_PRIME_SHIFT\t0x0\tFNV_64_PRIME_LOW\n\t */\n\t/* multiply by the lowest order digit base 2^16 */\n\ttmp[0] = val[0] * FNV_64_PRIME_LOW;\n\ttmp[1] = val[1] * FNV_64_PRIME_LOW;\n\ttmp[2] = val[2] * FNV_64_PRIME_LOW;\n\ttmp[3] = val[3] * FNV_64_PRIME_LOW;\n\t/* multiply by the other non-zero digit */\n\ttmp[2] += val[0] << FNV_64_PRIME_SHIFT;\t/* tmp[2] += val[0] * 0x100 */\n\ttmp[3] += val[1] << FNV_64_PRIME_SHIFT;\t/* tmp[3] += val[1] * 0x100 */\n\t/* propagate carries */\n\ttmp[1] += (tmp[0] >> 16);\n\tval[0] = tmp[0] & 0xffff;\n\ttmp[2] += (tmp[1] >> 16);\n\tval[1] = tmp[1] & 0xffff;\n\tval[3] = tmp[3] + (tmp[2] >> 16);\n\tval[2] = tmp[2] & 0xffff;\n\t/*\n\t * Doing a val[3] &= 0xffff; is not really needed since it simply\n\t * removes multiples of 2^64.  We can discard these excess bits\n\t * outside of the loop when we convert to Fnv64_t.\n\t */\n\n\t/* xor the bottom with the current octet */\n\tval[0] ^= (unsigned long)*bp++;\n    }\n\n    /*\n     * Convert base 2^16 array back into an Fnv64_t\n     */\n    hval.w32[1] = ((val[3]<<16) | val[2]);\n    hval.w32[0] = ((val[1]<<16) | val[0]);\n\n#endif /* HAVE_64BIT_LONG_LONG */\n\n    /* return our new hash value */\n    return hval;\n}\n\n\n/*\n * fnv_64_str - perform a 64 bit Fowler/Noll/Vo hash on a buffer\n *\n * input:\n *\tbuf\t- start of buffer to hash\n *\thval\t- previous hash value or 0 if first call\n *\n * returns:\n *\t64 bit hash as a static hash type\n *\n * NOTE: To use the 64 bit FNV-0 historic hash, use FNV0_64_INIT as the hval\n *\t argument on the first call to either fnv_64_buf() or fnv_64_str().\n *\n * NOTE: To use the recommended 64 bit FNV-1 hash, use FNV1_64_INIT as the hval\n *\t argument on the first call to either fnv_64_buf() or fnv_64_str().\n */\nFnv64_t\nfnv_64_str(char *str, Fnv64_t hval)\n{\n    unsigned char *s = (unsigned char *)str;\t/* unsigned string */\n\n#if defined(HAVE_64BIT_LONG_LONG)\n\n    /*\n     * FNV-1 hash each octet of the string\n     */\n    while (*s) {\n\n\t/* multiply by the 64 bit FNV magic prime mod 2^64 */\n#if defined(NO_FNV_GCC_OPTIMIZATION)\n\thval *= FNV_64_PRIME;\n#else /* NO_FNV_GCC_OPTIMIZATION */\n\thval += (hval << 1) + (hval << 4) + (hval << 5) +\n\t\t(hval << 7) + (hval << 8) + (hval << 40);\n#endif /* NO_FNV_GCC_OPTIMIZATION */\n\n\t/* xor the bottom with the current octet */\n\thval ^= (Fnv64_t)*s++;\n    }\n\n#else /* !HAVE_64BIT_LONG_LONG */\n\n    unsigned long val[4];\t/* hash value in base 2^16 */\n    unsigned long tmp[4];\t/* tmp 64 bit value */\n\n    /*\n     * Convert Fnv64_t hval into a base 2^16 array\n     */\n    val[0] = hval.w32[0];\n    val[1] = (val[0] >> 16);\n    val[0] &= 0xffff;\n    val[2] = hval.w32[1];\n    val[3] = (val[2] >> 16);\n    val[2] &= 0xffff;\n\n    /*\n     * FNV-1 hash each octet of the string\n     */\n    while (*s) {\n\n\t/*\n\t * multiply by the 64 bit FNV magic prime mod 2^64\n\t *\n\t * Using 1099511628211, we have the following digits base 2^16:\n\t *\n\t *\t0x0\t0x100\t0x0\t0x1b3\n\t *\n\t * which is the same as:\n\t *\n\t *\t0x0\t1<<FNV_64_PRIME_SHIFT\t0x0\tFNV_64_PRIME_LOW\n\t */\n\t/* multiply by the lowest order digit base 2^16 */\n\ttmp[0] = val[0] * FNV_64_PRIME_LOW;\n\ttmp[1] = val[1] * FNV_64_PRIME_LOW;\n\ttmp[2] = val[2] * FNV_64_PRIME_LOW;\n\ttmp[3] = val[3] * FNV_64_PRIME_LOW;\n\t/* multiply by the other non-zero digit */\n\ttmp[2] += val[0] << FNV_64_PRIME_SHIFT;\t/* tmp[2] += val[0] * 0x100 */\n\ttmp[3] += val[1] << FNV_64_PRIME_SHIFT;\t/* tmp[3] += val[1] * 0x100 */\n\t/* propagate carries */\n\ttmp[1] += (tmp[0] >> 16);\n\tval[0] = tmp[0] & 0xffff;\n\ttmp[2] += (tmp[1] >> 16);\n\tval[1] = tmp[1] & 0xffff;\n\tval[3] = tmp[3] + (tmp[2] >> 16);\n\tval[2] = tmp[2] & 0xffff;\n\t/*\n\t * Doing a val[3] &= 0xffff; is not really needed since it simply\n\t * removes multiples of 2^64.  We can discard these excess bits\n\t * outside of the loop when we convert to Fnv64_t.\n\t */\n\n\t/* xor the bottom with the current octet */\n\tval[0] ^= (unsigned long)(*s++);\n    }\n\n    /*\n     * Convert base 2^16 array back into an Fnv64_t\n     */\n    hval.w32[1] = ((val[3]<<16) | val[2]);\n    hval.w32[0] = ((val[1]<<16) | val[0]);\n\n#endif /* !HAVE_64BIT_LONG_LONG */\n\n    /* return our new hash value */\n    return hval;\n}\n"
  },
  {
    "path": "lib/fnv/hash_64a.c",
    "content": "/*\n * hash_64 - 64 bit Fowler/Noll/Vo-0 FNV-1a hash code\n *\n * @(#) $Revision: 5.1 $\n * @(#) $Id: hash_64a.c,v 5.1 2009/06/30 09:01:38 chongo Exp $\n * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_64a.c,v $\n *\n ***\n *\n * Fowler/Noll/Vo hash\n *\n * The basis of this hash algorithm was taken from an idea sent\n * as reviewer comments to the IEEE POSIX P1003.2 committee by:\n *\n *      Phong Vo (http://www.research.att.com/info/kpv/)\n *      Glenn Fowler (http://www.research.att.com/~gsf/)\n *\n * In a subsequent ballot round:\n *\n *      Landon Curt Noll (http://www.isthe.com/chongo/)\n *\n * improved on their algorithm.  Some people tried this hash\n * and found that it worked rather well.  In an EMail message\n * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.\n *\n * FNV hashes are designed to be fast while maintaining a low\n * collision rate. The FNV speed allows one to quickly hash lots\n * of data while maintaining a reasonable collision rate.  See:\n *\n *      http://www.isthe.com/chongo/tech/comp/fnv/index.html\n *\n * for more details as well as other forms of the FNV hash.\n *\n ***\n *\n * To use the recommended 64 bit FNV-1a hash, pass FNV1A_64_INIT as the\n * Fnv64_t hashval argument to fnv_64a_buf() or fnv_64a_str().\n *\n ***\n *\n * Please do not copyright this code.  This code is in the public domain.\n *\n * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO\n * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF\n * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n * PERFORMANCE OF THIS SOFTWARE.\n *\n * By:\n *\tchongo <Landon Curt Noll> /\\oo/\\\n *      http://www.isthe.com/chongo/\n *\n * Share and Enjoy!\t:-)\n */\n\n#include <stdlib.h>\n#include \"fnv.h\"\n\n\n/*\n * FNV-1a defines the initial basis to be non-zero\n */\n#if !defined(HAVE_64BIT_LONG_LONG)\nconst Fnv64_t fnv1a_64_init = { 0x84222325, 0xcbf29ce4 };\n#endif /* ! HAVE_64BIT_LONG_LONG */\n\n\n/*\n * 64 bit magic FNV-1a prime\n */\n#if defined(HAVE_64BIT_LONG_LONG)\n#define FNV_64_PRIME ((Fnv64_t)0x100000001b3ULL)\n#else /* HAVE_64BIT_LONG_LONG */\n#define FNV_64_PRIME_LOW ((unsigned long)0x1b3)\t/* lower bits of FNV prime */\n#define FNV_64_PRIME_SHIFT (8)\t\t/* top FNV prime shift above 2^32 */\n#endif /* HAVE_64BIT_LONG_LONG */\n\n\n/*\n * fnv_64a_buf - perform a 64 bit Fowler/Noll/Vo FNV-1a hash on a buffer\n *\n * input:\n *\tbuf\t- start of buffer to hash\n *\tlen\t- length of buffer in octets\n *\thval\t- previous hash value or 0 if first call\n *\n * returns:\n *\t64 bit hash as a static hash type\n *\n * NOTE: To use the recommended 64 bit FNV-1a hash, use FNV1A_64_INIT as the\n * \t hval arg on the first call to either fnv_64a_buf() or fnv_64a_str().\n */\nFnv64_t\nfnv_64a_buf(void *buf, size_t len, Fnv64_t hval)\n{\n    unsigned char *bp = (unsigned char *)buf;\t/* start of buffer */\n    unsigned char *be = bp + len;\t\t/* beyond end of buffer */\n\n#if defined(HAVE_64BIT_LONG_LONG)\n    /*\n     * FNV-1a hash each octet of the buffer\n     */\n    while (bp < be) {\n\n\t/* xor the bottom with the current octet */\n\thval ^= (Fnv64_t)*bp++;\n\n\t/* multiply by the 64 bit FNV magic prime mod 2^64 */\n#if defined(NO_FNV_GCC_OPTIMIZATION)\n\thval *= FNV_64_PRIME;\n#else /* NO_FNV_GCC_OPTIMIZATION */\n\thval += (hval << 1) + (hval << 4) + (hval << 5) +\n\t\t(hval << 7) + (hval << 8) + (hval << 40);\n#endif /* NO_FNV_GCC_OPTIMIZATION */\n    }\n\n#else /* HAVE_64BIT_LONG_LONG */\n\n    unsigned long val[4];\t\t\t/* hash value in base 2^16 */\n    unsigned long tmp[4];\t\t\t/* tmp 64 bit value */\n\n    /*\n     * Convert Fnv64_t hval into a base 2^16 array\n     */\n    val[0] = hval.w32[0];\n    val[1] = (val[0] >> 16);\n    val[0] &= 0xffff;\n    val[2] = hval.w32[1];\n    val[3] = (val[2] >> 16);\n    val[2] &= 0xffff;\n\n    /*\n     * FNV-1a hash each octet of the buffer\n     */\n    while (bp < be) {\n\n\t/* xor the bottom with the current octet */\n\tval[0] ^= (unsigned long)*bp++;\n\n\t/*\n\t * multiply by the 64 bit FNV magic prime mod 2^64\n\t *\n\t * Using 0x100000001b3 we have the following digits base 2^16:\n\t *\n\t *\t0x0\t0x100\t0x0\t0x1b3\n\t *\n\t * which is the same as:\n\t *\n\t *\t0x0\t1<<FNV_64_PRIME_SHIFT\t0x0\tFNV_64_PRIME_LOW\n\t */\n\t/* multiply by the lowest order digit base 2^16 */\n\ttmp[0] = val[0] * FNV_64_PRIME_LOW;\n\ttmp[1] = val[1] * FNV_64_PRIME_LOW;\n\ttmp[2] = val[2] * FNV_64_PRIME_LOW;\n\ttmp[3] = val[3] * FNV_64_PRIME_LOW;\n\t/* multiply by the other non-zero digit */\n\ttmp[2] += val[0] << FNV_64_PRIME_SHIFT;\t/* tmp[2] += val[0] * 0x100 */\n\ttmp[3] += val[1] << FNV_64_PRIME_SHIFT;\t/* tmp[3] += val[1] * 0x100 */\n\t/* propagate carries */\n\ttmp[1] += (tmp[0] >> 16);\n\tval[0] = tmp[0] & 0xffff;\n\ttmp[2] += (tmp[1] >> 16);\n\tval[1] = tmp[1] & 0xffff;\n\tval[3] = tmp[3] + (tmp[2] >> 16);\n\tval[2] = tmp[2] & 0xffff;\n\t/*\n\t * Doing a val[3] &= 0xffff; is not really needed since it simply\n\t * removes multiples of 2^64.  We can discard these excess bits\n\t * outside of the loop when we convert to Fnv64_t.\n\t */\n    }\n\n    /*\n     * Convert base 2^16 array back into an Fnv64_t\n     */\n    hval.w32[1] = ((val[3]<<16) | val[2]);\n    hval.w32[0] = ((val[1]<<16) | val[0]);\n\n#endif /* HAVE_64BIT_LONG_LONG */\n\n    /* return our new hash value */\n    return hval;\n}\n\n\n/*\n * fnv_64a_str - perform a 64 bit Fowler/Noll/Vo FNV-1a hash on a buffer\n *\n * input:\n *\tbuf\t- start of buffer to hash\n *\thval\t- previous hash value or 0 if first call\n *\n * returns:\n *\t64 bit hash as a static hash type\n *\n * NOTE: To use the recommended 64 bit FNV-1a hash, use FNV1A_64_INIT as the\n * \t hval arg on the first call to either fnv_64a_buf() or fnv_64a_str().\n */\nFnv64_t\nfnv_64a_str(char *str, Fnv64_t hval)\n{\n    unsigned char *s = (unsigned char *)str;\t/* unsigned string */\n\n#if defined(HAVE_64BIT_LONG_LONG)\n\n    /*\n     * FNV-1a hash each octet of the string\n     */\n    while (*s) {\n\n\t/* xor the bottom with the current octet */\n\thval ^= (Fnv64_t)*s++;\n\n\t/* multiply by the 64 bit FNV magic prime mod 2^64 */\n#if defined(NO_FNV_GCC_OPTIMIZATION)\n\thval *= FNV_64_PRIME;\n#else /* NO_FNV_GCC_OPTIMIZATION */\n\thval += (hval << 1) + (hval << 4) + (hval << 5) +\n\t\t(hval << 7) + (hval << 8) + (hval << 40);\n#endif /* NO_FNV_GCC_OPTIMIZATION */\n    }\n\n#else /* !HAVE_64BIT_LONG_LONG */\n\n    unsigned long val[4];\t/* hash value in base 2^16 */\n    unsigned long tmp[4];\t/* tmp 64 bit value */\n\n    /*\n     * Convert Fnv64_t hval into a base 2^16 array\n     */\n    val[0] = hval.w32[0];\n    val[1] = (val[0] >> 16);\n    val[0] &= 0xffff;\n    val[2] = hval.w32[1];\n    val[3] = (val[2] >> 16);\n    val[2] &= 0xffff;\n\n    /*\n     * FNV-1a hash each octet of the string\n     */\n    while (*s) {\n\n\t/* xor the bottom with the current octet */\n\n\t/*\n\t * multiply by the 64 bit FNV magic prime mod 2^64\n\t *\n\t * Using 1099511628211, we have the following digits base 2^16:\n\t *\n\t *\t0x0\t0x100\t0x0\t0x1b3\n\t *\n\t * which is the same as:\n\t *\n\t *\t0x0\t1<<FNV_64_PRIME_SHIFT\t0x0\tFNV_64_PRIME_LOW\n\t */\n\t/* multiply by the lowest order digit base 2^16 */\n\ttmp[0] = val[0] * FNV_64_PRIME_LOW;\n\ttmp[1] = val[1] * FNV_64_PRIME_LOW;\n\ttmp[2] = val[2] * FNV_64_PRIME_LOW;\n\ttmp[3] = val[3] * FNV_64_PRIME_LOW;\n\t/* multiply by the other non-zero digit */\n\ttmp[2] += val[0] << FNV_64_PRIME_SHIFT;\t/* tmp[2] += val[0] * 0x100 */\n\ttmp[3] += val[1] << FNV_64_PRIME_SHIFT;\t/* tmp[3] += val[1] * 0x100 */\n\t/* propagate carries */\n\ttmp[1] += (tmp[0] >> 16);\n\tval[0] = tmp[0] & 0xffff;\n\ttmp[2] += (tmp[1] >> 16);\n\tval[1] = tmp[1] & 0xffff;\n\tval[3] = tmp[3] + (tmp[2] >> 16);\n\tval[2] = tmp[2] & 0xffff;\n\t/*\n\t * Doing a val[3] &= 0xffff; is not really needed since it simply\n\t * removes multiples of 2^64.  We can discard these excess bits\n\t * outside of the loop when we convert to Fnv64_t.\n\t */\n\tval[0] ^= (unsigned long)(*s++);\n    }\n\n    /*\n     * Convert base 2^16 array back into an Fnv64_t\n     */\n    hval.w32[1] = ((val[3]<<16) | val[2]);\n    hval.w32[0] = ((val[1]<<16) | val[0]);\n\n#endif /* !HAVE_64BIT_LONG_LONG */\n\n    /* return our new hash value */\n    return hval;\n}\n"
  },
  {
    "path": "lib/fnv/have_ulong64.c",
    "content": "/*\n * have_ulong64 - Determine if we have a 64 bit unsigned long long\n *\n * usage:\n *\thave_ulong64 > longlong.h\n *\n * Not all systems have a 'long long type' so this may not compile on\n * your system.\n *\n * This prog outputs the define:\n *\n *\tHAVE_64BIT_LONG_LONG\n *\t\tdefined ==> we have a 64 bit unsigned long long\n *\t\tundefined ==> we must simulate a 64 bit unsigned long long\n */\n/*\n *\n * Please do not copyright this code.  This code is in the public domain.\n *\n * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO\n * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF\n * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n * PERFORMANCE OF THIS SOFTWARE.\n *\n * By:\n *\tchongo <Landon Curt Noll> /\\oo/\\\n *      http://www.isthe.com/chongo/\n *\n * Share and Enjoy!\t:-)\n */\n\n/*\n * have the compiler try its hand with unsigned and signed long longs\n */\n#if ! defined(NO64BIT_LONG_LONG)\nunsigned long long val = 1099511628211ULL;\n#endif /* NO64BIT_LONG_LONG */\n\nint\nmain(void)\n{\n\t/*\n\t * ensure that the length of long long val is what we expect\n\t */\n#if defined(NO64BIT_LONG_LONG)\n\tprintf(\"#undef HAVE_64BIT_LONG_LONG\\t/* no */\\n\");\n#else /* NO64BIT_LONG_LONG */\n\tif (val == 1099511628211ULL && sizeof(val) == 8) {\n\t\tprintf(\"#define HAVE_64BIT_LONG_LONG\\t/* yes */\\n\");\n\t}\n#endif /* NO64BIT_LONG_LONG */\n\n\t/* exit(0); */\n\treturn 0;\n}\n"
  },
  {
    "path": "lib/fnv/longlong.h",
    "content": "/*\n * DO NOT EDIT -- generated by the Makefile\n */\n\n#if !defined(__LONGLONG_H__)\n#define __LONGLONG_H__\n\n/* do we have/want to use a long long type? */\n#define HAVE_64BIT_LONG_LONG\t/* yes */\n\n/*\n * NO64BIT_LONG_LONG undef HAVE_64BIT_LONG_LONG\n */\n#if defined(NO64BIT_LONG_LONG)\n#undef HAVE_64BIT_LONG_LONG\n#endif /* NO64BIT_LONG_LONG */\n\n#endif /* !__LONGLONG_H__ */\n"
  },
  {
    "path": "lib/fnv/qmk_fnv_type_validation.c",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include \"fnv.h\"\n#include \"compiler_support.h\"\n\n// This library was originally sourced from:\n//     http://www.isthe.com/chongo/tech/comp/fnv/index.html\n//\n// Version at the time of retrieval on 2022-06-26: v5.0.3\n\nSTATIC_ASSERT(sizeof(long long) == 8, \"long long should be 64 bits\");\nSTATIC_ASSERT(sizeof(unsigned long long) == 8, \"unsigned long long should be 64 bits\");\n\nSTATIC_ASSERT(sizeof(Fnv32_t) == 4, \"Fnv32_t should be 32 bits\");\nSTATIC_ASSERT(sizeof(Fnv64_t) == 8, \"Fnv64_t should be 64 bits\");\n"
  },
  {
    "path": "lib/fnv/test_fnv.c",
    "content": "/*\n * test_fnv - FNV test suite\n *\n * @(#) $Revision: 5.3 $\n * @(#) $Id: test_fnv.c,v 5.3 2009/06/30 11:50:41 chongo Exp $\n * @(#) $Source: /usr/local/src/cmd/fnv/RCS/test_fnv.c,v $\n *\n ***\n *\n * Fowler/Noll/Vo hash\n *\n * The basis of this hash algorithm was taken from an idea sent\n * as reviewer comments to the IEEE POSIX P1003.2 committee by:\n *\n *      Phong Vo (http://www.research.att.com/info/kpv/)\n *      Glenn Fowler (http://www.research.att.com/~gsf/)\n *\n * In a subsequent ballot round:\n *\n *      Landon Curt Noll (http://www.isthe.com/chongo/)\n *\n * improved on their algorithm.  Some people tried this hash\n * and found that it worked rather well.  In an EMail message\n * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.\n *\n * FNV hashes are designed to be fast while maintaining a low\n * collision rate. The FNV speed allows one to quickly hash lots\n * of data while maintaining a reasonable collision rate.  See:\n *\n *      http://www.isthe.com/chongo/tech/comp/fnv/index.html\n *\n * for more details as well as other forms of the FNV hash.\n *\n ***\n *\n * Please do not copyright this code.  This code is in the public domain.\n *\n * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,\n * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO\n * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR\n * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF\n * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\n * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n * PERFORMANCE OF THIS SOFTWARE.\n *\n * By:\n *\tchongo <Landon Curt Noll> /\\oo/\\\n *      http://www.isthe.com/chongo/\n *\n * Share and Enjoy!\t:-)\n */\n\n#include <stdio.h>\n#include \"longlong.h\"\n#include \"fnv.h\"\n\n#define LEN(x) (sizeof(x)-1)\n/* TEST macro does not include trailing NUL byte in the test vector */\n#define TEST(x) {x, LEN(x)}\n/* TEST0 macro includes the trailing NUL byte in the test vector */\n#define TEST0(x) {x, sizeof(x)}\n/* REPEAT500 - repeat a string 500 times */\n#define R500(x) R100(x)R100(x)R100(x)R100(x)R100(x)\n#define R100(x) R10(x)R10(x)R10(x)R10(x)R10(x)R10(x)R10(x)R10(x)R10(x)R10(x)\n#define R10(x) x x x x x x x x x x\n\n/*\n * FNV test vectors\n *\n * NOTE: A NULL pointer marks beyond the end of the test vectors.\n *\n * NOTE: The order of the fnv_test_str[] test vectors is 1-to-1 with:\n *\n *\tstruct fnv0_32_test_vector fnv0_32_vector[];\n *\tstruct fnv1_32_test_vector fnv1_32_vector[];\n *\tstruct fnv1a_32_test_vector fnv1a_32_vector[];\n *\tstruct fnv0_64_test_vector fnv0_64_vector[];\n *\tstruct fnv1_64_test_vector fnv1_64_vector[];\n *\tstruct fnv1a_64_test_vector fnv1a_64_vector[];\n *\n * IMPORTANT NOTE:\n *\n *\tIf you change the fnv_test_str[] array, you need\n *\tto also change ALL of the above fnv*_vector arrays!!!\n *\n *\tTo rebuild, try:\n *\n *\t\tmake vector.c\n *\n *\tand then fold the results into the source file.\n *\tOf course, you better make sure that the vaules\n *\tproduced by the above command are valid, otherwise\n * \tyou will be testing against invalid vectors!\n */\nstruct test_vector fnv_test_str[] = {\n  TEST(\"\"),\n  TEST(\"a\"),\n  TEST(\"b\"),\n  TEST(\"c\"),\n  TEST(\"d\"),\n  TEST(\"e\"),\n  TEST(\"f\"),\n  TEST(\"fo\"),\n  TEST(\"foo\"),\n  TEST(\"foob\"),\n  TEST(\"fooba\"),\n  TEST(\"foobar\"),\n  TEST0(\"\"),\n  TEST0(\"a\"),\n  TEST0(\"b\"),\n  TEST0(\"c\"),\n  TEST0(\"d\"),\n  TEST0(\"e\"),\n  TEST0(\"f\"),\n  TEST0(\"fo\"),\n  TEST0(\"foo\"),\n  TEST0(\"foob\"),\n  TEST0(\"fooba\"),\n  TEST0(\"foobar\"),\n  TEST(\"ch\"),\n  TEST(\"cho\"),\n  TEST(\"chon\"),\n  TEST(\"chong\"),\n  TEST(\"chongo\"),\n  TEST(\"chongo \"),\n  TEST(\"chongo w\"),\n  TEST(\"chongo wa\"),\n  TEST(\"chongo was\"),\n  TEST(\"chongo was \"),\n  TEST(\"chongo was h\"),\n  TEST(\"chongo was he\"),\n  TEST(\"chongo was her\"),\n  TEST(\"chongo was here\"),\n  TEST(\"chongo was here!\"),\n  TEST(\"chongo was here!\\n\"),\n  TEST0(\"ch\"),\n  TEST0(\"cho\"),\n  TEST0(\"chon\"),\n  TEST0(\"chong\"),\n  TEST0(\"chongo\"),\n  TEST0(\"chongo \"),\n  TEST0(\"chongo w\"),\n  TEST0(\"chongo wa\"),\n  TEST0(\"chongo was\"),\n  TEST0(\"chongo was \"),\n  TEST0(\"chongo was h\"),\n  TEST0(\"chongo was he\"),\n  TEST0(\"chongo was her\"),\n  TEST0(\"chongo was here\"),\n  TEST0(\"chongo was here!\"),\n  TEST0(\"chongo was here!\\n\"),\n  TEST(\"cu\"),\n  TEST(\"cur\"),\n  TEST(\"curd\"),\n  TEST(\"curds\"),\n  TEST(\"curds \"),\n  TEST(\"curds a\"),\n  TEST(\"curds an\"),\n  TEST(\"curds and\"),\n  TEST(\"curds and \"),\n  TEST(\"curds and w\"),\n  TEST(\"curds and wh\"),\n  TEST(\"curds and whe\"),\n  TEST(\"curds and whey\"),\n  TEST(\"curds and whey\\n\"),\n  TEST0(\"cu\"),\n  TEST0(\"cur\"),\n  TEST0(\"curd\"),\n  TEST0(\"curds\"),\n  TEST0(\"curds \"),\n  TEST0(\"curds a\"),\n  TEST0(\"curds an\"),\n  TEST0(\"curds and\"),\n  TEST0(\"curds and \"),\n  TEST0(\"curds and w\"),\n  TEST0(\"curds and wh\"),\n  TEST0(\"curds and whe\"),\n  TEST0(\"curds and whey\"),\n  TEST0(\"curds and whey\\n\"),\n  TEST(\"hi\"), TEST0(\"hi\"),\n  TEST(\"hello\"), TEST0(\"hello\"),\n  TEST(\"\\xff\\x00\\x00\\x01\"), TEST(\"\\x01\\x00\\x00\\xff\"),\n  TEST(\"\\xff\\x00\\x00\\x02\"), TEST(\"\\x02\\x00\\x00\\xff\"),\n  TEST(\"\\xff\\x00\\x00\\x03\"), TEST(\"\\x03\\x00\\x00\\xff\"),\n  TEST(\"\\xff\\x00\\x00\\x04\"), TEST(\"\\x04\\x00\\x00\\xff\"),\n  TEST(\"\\x40\\x51\\x4e\\x44\"), TEST(\"\\x44\\x4e\\x51\\x40\"),\n  TEST(\"\\x40\\x51\\x4e\\x4a\"), TEST(\"\\x4a\\x4e\\x51\\x40\"),\n  TEST(\"\\x40\\x51\\x4e\\x54\"), TEST(\"\\x54\\x4e\\x51\\x40\"),\n  TEST(\"127.0.0.1\"), TEST0(\"127.0.0.1\"),\n  TEST(\"127.0.0.2\"), TEST0(\"127.0.0.2\"),\n  TEST(\"127.0.0.3\"), TEST0(\"127.0.0.3\"),\n  TEST(\"64.81.78.68\"), TEST0(\"64.81.78.68\"),\n  TEST(\"64.81.78.74\"), TEST0(\"64.81.78.74\"),\n  TEST(\"64.81.78.84\"), TEST0(\"64.81.78.84\"),\n  TEST(\"feedface\"), TEST0(\"feedface\"),\n  TEST(\"feedfacedaffdeed\"), TEST0(\"feedfacedaffdeed\"),\n  TEST(\"feedfacedeadbeef\"), TEST0(\"feedfacedeadbeef\"),\n  TEST(\"line 1\\nline 2\\nline 3\"),\n  TEST(\"chongo <Landon Curt Noll> /\\\\../\\\\\"),\n  TEST0(\"chongo <Landon Curt Noll> /\\\\../\\\\\"),\n  TEST(\"chongo (Landon Curt Noll) /\\\\../\\\\\"),\n  TEST0(\"chongo (Landon Curt Noll) /\\\\../\\\\\"),\n  TEST(\"http://antwrp.gsfc.nasa.gov/apod/astropix.html\"),\n  TEST(\"http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash\"),\n  TEST(\"http://epod.usra.edu/\"),\n  TEST(\"http://exoplanet.eu/\"),\n  TEST(\"http://hvo.wr.usgs.gov/cam3/\"),\n  TEST(\"http://hvo.wr.usgs.gov/cams/HMcam/\"),\n  TEST(\"http://hvo.wr.usgs.gov/kilauea/update/deformation.html\"),\n  TEST(\"http://hvo.wr.usgs.gov/kilauea/update/images.html\"),\n  TEST(\"http://hvo.wr.usgs.gov/kilauea/update/maps.html\"),\n  TEST(\"http://hvo.wr.usgs.gov/volcanowatch/current_issue.html\"),\n  TEST(\"http://neo.jpl.nasa.gov/risk/\"),\n  TEST(\"http://norvig.com/21-days.html\"),\n  TEST(\"http://primes.utm.edu/curios/home.php\"),\n  TEST(\"http://slashdot.org/\"),\n  TEST(\"http://tux.wr.usgs.gov/Maps/155.25-19.5.html\"),\n  TEST(\"http://volcano.wr.usgs.gov/kilaueastatus.php\"),\n  TEST(\"http://www.avo.alaska.edu/activity/Redoubt.php\"),\n  TEST(\"http://www.dilbert.com/fast/\"),\n  TEST(\"http://www.fourmilab.ch/gravitation/orbits/\"),\n  TEST(\"http://www.fpoa.net/\"),\n  TEST(\"http://www.ioccc.org/index.html\"),\n  TEST(\"http://www.isthe.com/cgi-bin/number.cgi\"),\n  TEST(\"http://www.isthe.com/chongo/bio.html\"),\n  TEST(\"http://www.isthe.com/chongo/index.html\"),\n  TEST(\"http://www.isthe.com/chongo/src/calc/lucas-calc\"),\n  TEST(\"http://www.isthe.com/chongo/tech/astro/venus2004.html\"),\n  TEST(\"http://www.isthe.com/chongo/tech/astro/vita.html\"),\n  TEST(\"http://www.isthe.com/chongo/tech/comp/c/expert.html\"),\n  TEST(\"http://www.isthe.com/chongo/tech/comp/calc/index.html\"),\n  TEST(\"http://www.isthe.com/chongo/tech/comp/fnv/index.html\"),\n  TEST(\"http://www.isthe.com/chongo/tech/math/number/howhigh.html\"),\n  TEST(\"http://www.isthe.com/chongo/tech/math/number/number.html\"),\n  TEST(\"http://www.isthe.com/chongo/tech/math/prime/mersenne.html\"),\n  TEST(\"http://www.isthe.com/chongo/tech/math/prime/mersenne.html#largest\"),\n  TEST(\"http://www.lavarnd.org/cgi-bin/corpspeak.cgi\"),\n  TEST(\"http://www.lavarnd.org/cgi-bin/haiku.cgi\"),\n  TEST(\"http://www.lavarnd.org/cgi-bin/rand-none.cgi\"),\n  TEST(\"http://www.lavarnd.org/cgi-bin/randdist.cgi\"),\n  TEST(\"http://www.lavarnd.org/index.html\"),\n  TEST(\"http://www.lavarnd.org/what/nist-test.html\"),\n  TEST(\"http://www.macosxhints.com/\"),\n  TEST(\"http://www.mellis.com/\"),\n  TEST(\"http://www.nature.nps.gov/air/webcams/parks/havoso2alert/havoalert.cfm\"),\n  TEST(\"http://www.nature.nps.gov/air/webcams/parks/havoso2alert/timelines_24.cfm\"),\n  TEST(\"http://www.paulnoll.com/\"),\n  TEST(\"http://www.pepysdiary.com/\"),\n  TEST(\"http://www.sciencenews.org/index/home/activity/view\"),\n  TEST(\"http://www.skyandtelescope.com/\"),\n  TEST(\"http://www.sput.nl/~rob/sirius.html\"),\n  TEST(\"http://www.systemexperts.com/\"),\n  TEST(\"http://www.tq-international.com/phpBB3/index.php\"),\n  TEST(\"http://www.travelquesttours.com/index.htm\"),\n  TEST(\"http://www.wunderground.com/global/stations/89606.html\"),\n  TEST(R10(\"21701\")),\n  TEST(R10(\"M21701\")),\n  TEST(R10(\"2^21701-1\")),\n  TEST(R10(\"\\x54\\xc5\")),\n  TEST(R10(\"\\xc5\\x54\")),\n  TEST(R10(\"23209\")),\n  TEST(R10(\"M23209\")),\n  TEST(R10(\"2^23209-1\")),\n  TEST(R10(\"\\x5a\\xa9\")),\n  TEST(R10(\"\\xa9\\x5a\")),\n  TEST(R10(\"391581216093\")),\n  TEST(R10(\"391581*2^216093-1\")),\n  TEST(R10(\"\\x05\\xf9\\x9d\\x03\\x4c\\x81\")),\n  TEST(R10(\"FEDCBA9876543210\")),\n  TEST(R10(\"\\xfe\\xdc\\xba\\x98\\x76\\x54\\x32\\x10\")),\n  TEST(R10(\"EFCDAB8967452301\")),\n  TEST(R10(\"\\xef\\xcd\\xab\\x89\\x67\\x45\\x23\\x01\")),\n  TEST(R10(\"0123456789ABCDEF\")),\n  TEST(R10(\"\\x01\\x23\\x45\\x67\\x89\\xab\\xcd\\xef\")),\n  TEST(R10(\"1032547698BADCFE\")),\n  TEST(R10(\"\\x10\\x32\\x54\\x76\\x98\\xba\\xdc\\xfe\")),\n  TEST(R500(\"\\x00\")),\n  TEST(R500(\"\\x07\")),\n  TEST(R500(\"~\")),\n  TEST(R500(\"\\x7f\")),\n  {NULL, 0}\t/* MUST BE LAST */\n};\n\n\n/*\n * insert the contents of vector.c below\n *\n *\tmake vector.c\n *\t:r vector.c\n */\n/* start of output generated by make vector.c */\n\n/* FNV-0 32 bit test vectors */\nstruct fnv0_32_test_vector fnv0_32_vector[] = {\n    { &fnv_test_str[0], (Fnv32_t) 0x00000000UL },\n    { &fnv_test_str[1], (Fnv32_t) 0x00000061UL },\n    { &fnv_test_str[2], (Fnv32_t) 0x00000062UL },\n    { &fnv_test_str[3], (Fnv32_t) 0x00000063UL },\n    { &fnv_test_str[4], (Fnv32_t) 0x00000064UL },\n    { &fnv_test_str[5], (Fnv32_t) 0x00000065UL },\n    { &fnv_test_str[6], (Fnv32_t) 0x00000066UL },\n    { &fnv_test_str[7], (Fnv32_t) 0x6600a0fdUL },\n    { &fnv_test_str[8], (Fnv32_t) 0x8ffd6e28UL },\n    { &fnv_test_str[9], (Fnv32_t) 0xd3f4689aUL },\n    { &fnv_test_str[10], (Fnv32_t) 0x43c0aa0fUL },\n    { &fnv_test_str[11], (Fnv32_t) 0xb74bb5efUL },\n    { &fnv_test_str[12], (Fnv32_t) 0x00000000UL },\n    { &fnv_test_str[13], (Fnv32_t) 0x610098b3UL },\n    { &fnv_test_str[14], (Fnv32_t) 0x62009a46UL },\n    { &fnv_test_str[15], (Fnv32_t) 0x63009bd9UL },\n    { &fnv_test_str[16], (Fnv32_t) 0x64009d6cUL },\n    { &fnv_test_str[17], (Fnv32_t) 0x65009effUL },\n    { &fnv_test_str[18], (Fnv32_t) 0x6600a092UL },\n    { &fnv_test_str[19], (Fnv32_t) 0x8ffd6e47UL },\n    { &fnv_test_str[20], (Fnv32_t) 0xd3f468f8UL },\n    { &fnv_test_str[21], (Fnv32_t) 0x43c0aa6eUL },\n    { &fnv_test_str[22], (Fnv32_t) 0xb74bb59dUL },\n    { &fnv_test_str[23], (Fnv32_t) 0x7b2f673dUL },\n    { &fnv_test_str[24], (Fnv32_t) 0x63009bb1UL },\n    { &fnv_test_str[25], (Fnv32_t) 0x8af517ccUL },\n    { &fnv_test_str[26], (Fnv32_t) 0x8bd4764aUL },\n    { &fnv_test_str[27], (Fnv32_t) 0x69763619UL },\n    { &fnv_test_str[28], (Fnv32_t) 0x1e172934UL },\n    { &fnv_test_str[29], (Fnv32_t) 0x9275dcfcUL },\n    { &fnv_test_str[30], (Fnv32_t) 0x8b8ae0c3UL },\n    { &fnv_test_str[31], (Fnv32_t) 0x6e9fd298UL },\n    { &fnv_test_str[32], (Fnv32_t) 0xbd98853bUL },\n    { &fnv_test_str[33], (Fnv32_t) 0xb219bbc1UL },\n    { &fnv_test_str[34], (Fnv32_t) 0x1f8290bbUL },\n    { &fnv_test_str[35], (Fnv32_t) 0x5589d604UL },\n    { &fnv_test_str[36], (Fnv32_t) 0xabfbe83eUL },\n    { &fnv_test_str[37], (Fnv32_t) 0xfb8e99ffUL },\n    { &fnv_test_str[38], (Fnv32_t) 0x007c6c4cUL },\n    { &fnv_test_str[39], (Fnv32_t) 0x0fde7baeUL },\n    { &fnv_test_str[40], (Fnv32_t) 0x8af517a3UL },\n    { &fnv_test_str[41], (Fnv32_t) 0x8bd47624UL },\n    { &fnv_test_str[42], (Fnv32_t) 0x6976367eUL },\n    { &fnv_test_str[43], (Fnv32_t) 0x1e17295bUL },\n    { &fnv_test_str[44], (Fnv32_t) 0x9275dcdcUL },\n    { &fnv_test_str[45], (Fnv32_t) 0x8b8ae0b4UL },\n    { &fnv_test_str[46], (Fnv32_t) 0x6e9fd2f9UL },\n    { &fnv_test_str[47], (Fnv32_t) 0xbd988548UL },\n    { &fnv_test_str[48], (Fnv32_t) 0xb219bbe1UL },\n    { &fnv_test_str[49], (Fnv32_t) 0x1f8290d3UL },\n    { &fnv_test_str[50], (Fnv32_t) 0x5589d661UL },\n    { &fnv_test_str[51], (Fnv32_t) 0xabfbe84cUL },\n    { &fnv_test_str[52], (Fnv32_t) 0xfb8e999aUL },\n    { &fnv_test_str[53], (Fnv32_t) 0x007c6c6dUL },\n    { &fnv_test_str[54], (Fnv32_t) 0x0fde7ba4UL },\n    { &fnv_test_str[55], (Fnv32_t) 0xa93cb2eaUL },\n    { &fnv_test_str[56], (Fnv32_t) 0x63009bacUL },\n    { &fnv_test_str[57], (Fnv32_t) 0x85f50fb6UL },\n    { &fnv_test_str[58], (Fnv32_t) 0x96c7bbe6UL },\n    { &fnv_test_str[59], (Fnv32_t) 0x426ccb61UL },\n    { &fnv_test_str[60], (Fnv32_t) 0xf2442993UL },\n    { &fnv_test_str[61], (Fnv32_t) 0xf44d7208UL },\n    { &fnv_test_str[62], (Fnv32_t) 0x9dea82f6UL },\n    { &fnv_test_str[63], (Fnv32_t) 0x8e2c2926UL },\n    { &fnv_test_str[64], (Fnv32_t) 0xf584c6f2UL },\n    { &fnv_test_str[65], (Fnv32_t) 0x72052e81UL },\n    { &fnv_test_str[66], (Fnv32_t) 0xff28357bUL },\n    { &fnv_test_str[67], (Fnv32_t) 0x274c30c4UL },\n    { &fnv_test_str[68], (Fnv32_t) 0xa0f0c4f5UL },\n    { &fnv_test_str[69], (Fnv32_t) 0x50060da5UL },\n    { &fnv_test_str[70], (Fnv32_t) 0x85f50fc4UL },\n    { &fnv_test_str[71], (Fnv32_t) 0x96c7bb82UL },\n    { &fnv_test_str[72], (Fnv32_t) 0x426ccb12UL },\n    { &fnv_test_str[73], (Fnv32_t) 0xf24429b3UL },\n    { &fnv_test_str[74], (Fnv32_t) 0xf44d7269UL },\n    { &fnv_test_str[75], (Fnv32_t) 0x9dea8298UL },\n    { &fnv_test_str[76], (Fnv32_t) 0x8e2c2942UL },\n    { &fnv_test_str[77], (Fnv32_t) 0xf584c6d2UL },\n    { &fnv_test_str[78], (Fnv32_t) 0x72052ef6UL },\n    { &fnv_test_str[79], (Fnv32_t) 0xff283513UL },\n    { &fnv_test_str[80], (Fnv32_t) 0x274c30a1UL },\n    { &fnv_test_str[81], (Fnv32_t) 0xa0f0c48cUL },\n    { &fnv_test_str[82], (Fnv32_t) 0x50060dafUL },\n    { &fnv_test_str[83], (Fnv32_t) 0x9e877abfUL },\n    { &fnv_test_str[84], (Fnv32_t) 0x6800a3d1UL },\n    { &fnv_test_str[85], (Fnv32_t) 0x8a01e203UL },\n    { &fnv_test_str[86], (Fnv32_t) 0xec6d6be8UL },\n    { &fnv_test_str[87], (Fnv32_t) 0x1840de38UL },\n    { &fnv_test_str[88], (Fnv32_t) 0xa7cc97b4UL },\n    { &fnv_test_str[89], (Fnv32_t) 0x3ee6b3b4UL },\n    { &fnv_test_str[90], (Fnv32_t) 0xa7cc97b7UL },\n    { &fnv_test_str[91], (Fnv32_t) 0x7dcd6669UL },\n    { &fnv_test_str[92], (Fnv32_t) 0xa7cc97b6UL },\n    { &fnv_test_str[93], (Fnv32_t) 0xbcb4191eUL },\n    { &fnv_test_str[94], (Fnv32_t) 0xa7cc97b1UL },\n    { &fnv_test_str[95], (Fnv32_t) 0xfb9acdd3UL },\n    { &fnv_test_str[96], (Fnv32_t) 0x89380433UL },\n    { &fnv_test_str[97], (Fnv32_t) 0x8acd2855UL },\n    { &fnv_test_str[98], (Fnv32_t) 0x8938043dUL },\n    { &fnv_test_str[99], (Fnv32_t) 0xcaeed493UL },\n    { &fnv_test_str[100], (Fnv32_t) 0x89380423UL },\n    { &fnv_test_str[101], (Fnv32_t) 0x59382a25UL },\n    { &fnv_test_str[102], (Fnv32_t) 0x567f75d7UL },\n    { &fnv_test_str[103], (Fnv32_t) 0x01a68175UL },\n    { &fnv_test_str[104], (Fnv32_t) 0x567f75d4UL },\n    { &fnv_test_str[105], (Fnv32_t) 0xfea67cbcUL },\n    { &fnv_test_str[106], (Fnv32_t) 0x567f75d5UL },\n    { &fnv_test_str[107], (Fnv32_t) 0xffa67e4fUL },\n    { &fnv_test_str[108], (Fnv32_t) 0xd131b668UL },\n    { &fnv_test_str[109], (Fnv32_t) 0xb94225b8UL },\n    { &fnv_test_str[110], (Fnv32_t) 0xd231b7d7UL },\n    { &fnv_test_str[111], (Fnv32_t) 0xbb446775UL },\n    { &fnv_test_str[112], (Fnv32_t) 0xdf31cc6eUL },\n    { &fnv_test_str[113], (Fnv32_t) 0xc964d12aUL },\n    { &fnv_test_str[114], (Fnv32_t) 0x23af8f9fUL },\n    { &fnv_test_str[115], (Fnv32_t) 0xcc5f174dUL },\n    { &fnv_test_str[116], (Fnv32_t) 0x96b29b8cUL },\n    { &fnv_test_str[117], (Fnv32_t) 0xc72add64UL },\n    { &fnv_test_str[118], (Fnv32_t) 0x528fb7efUL },\n    { &fnv_test_str[119], (Fnv32_t) 0xe73e8d3dUL },\n    { &fnv_test_str[120], (Fnv32_t) 0x876386feUL },\n    { &fnv_test_str[121], (Fnv32_t) 0x811c9dc5UL },\n    { &fnv_test_str[122], (Fnv32_t) 0x050c5d1fUL },\n    { &fnv_test_str[123], (Fnv32_t) 0x14bf7238UL },\n    { &fnv_test_str[124], (Fnv32_t) 0xe160ce28UL },\n    { &fnv_test_str[125], (Fnv32_t) 0x89dc5a75UL },\n    { &fnv_test_str[126], (Fnv32_t) 0xd89b69a0UL },\n    { &fnv_test_str[127], (Fnv32_t) 0x94471a88UL },\n    { &fnv_test_str[128], (Fnv32_t) 0xe78db65fUL },\n    { &fnv_test_str[129], (Fnv32_t) 0x0c3009a2UL },\n    { &fnv_test_str[130], (Fnv32_t) 0x122dff03UL },\n    { &fnv_test_str[131], (Fnv32_t) 0xb4cd8875UL },\n    { &fnv_test_str[132], (Fnv32_t) 0xf4dba725UL },\n    { &fnv_test_str[133], (Fnv32_t) 0x41a16560UL },\n    { &fnv_test_str[134], (Fnv32_t) 0x9c0f941fUL },\n    { &fnv_test_str[135], (Fnv32_t) 0x451a5348UL },\n    { &fnv_test_str[136], (Fnv32_t) 0x3f1d1d89UL },\n    { &fnv_test_str[137], (Fnv32_t) 0x1b91b57aUL },\n    { &fnv_test_str[138], (Fnv32_t) 0x3e99b577UL },\n    { &fnv_test_str[139], (Fnv32_t) 0x4c9de07aUL },\n    { &fnv_test_str[140], (Fnv32_t) 0x1ddf7572UL },\n    { &fnv_test_str[141], (Fnv32_t) 0x64e81976UL },\n    { &fnv_test_str[142], (Fnv32_t) 0x1106a888UL },\n    { &fnv_test_str[143], (Fnv32_t) 0xa498d8e5UL },\n    { &fnv_test_str[144], (Fnv32_t) 0x3c03d2e3UL },\n    { &fnv_test_str[145], (Fnv32_t) 0x26568b28UL },\n    { &fnv_test_str[146], (Fnv32_t) 0x70d7fb42UL },\n    { &fnv_test_str[147], (Fnv32_t) 0xd3ae1d22UL },\n    { &fnv_test_str[148], (Fnv32_t) 0xac8ea5f4UL },\n    { &fnv_test_str[149], (Fnv32_t) 0x4d0abd60UL },\n    { &fnv_test_str[150], (Fnv32_t) 0x48f5e086UL },\n    { &fnv_test_str[151], (Fnv32_t) 0xa8f6241bUL },\n    { &fnv_test_str[152], (Fnv32_t) 0x572f864fUL },\n    { &fnv_test_str[153], (Fnv32_t) 0xa5340803UL },\n    { &fnv_test_str[154], (Fnv32_t) 0x22881aa8UL },\n    { &fnv_test_str[155], (Fnv32_t) 0xc2e2f5a2UL },\n    { &fnv_test_str[156], (Fnv32_t) 0xebf5aec7UL },\n    { &fnv_test_str[157], (Fnv32_t) 0x3cdbfb85UL },\n    { &fnv_test_str[158], (Fnv32_t) 0xbb859704UL },\n    { &fnv_test_str[159], (Fnv32_t) 0xc956fe11UL },\n    { &fnv_test_str[160], (Fnv32_t) 0x8f11a7c9UL },\n    { &fnv_test_str[161], (Fnv32_t) 0x36c48ecfUL },\n    { &fnv_test_str[162], (Fnv32_t) 0x24bfa27eUL },\n    { &fnv_test_str[163], (Fnv32_t) 0xf2596ad1UL },\n    { &fnv_test_str[164], (Fnv32_t) 0xf14a9b45UL },\n    { &fnv_test_str[165], (Fnv32_t) 0x7d45835aUL },\n    { &fnv_test_str[166], (Fnv32_t) 0x6e49334dUL },\n    { &fnv_test_str[167], (Fnv32_t) 0x71767337UL },\n    { &fnv_test_str[168], (Fnv32_t) 0x858a1a8aUL },\n    { &fnv_test_str[169], (Fnv32_t) 0x16e75ac2UL },\n    { &fnv_test_str[170], (Fnv32_t) 0x409f99dfUL },\n    { &fnv_test_str[171], (Fnv32_t) 0x6d6652ddUL },\n    { &fnv_test_str[172], (Fnv32_t) 0x2761a9ffUL },\n    { &fnv_test_str[173], (Fnv32_t) 0x41f0d616UL },\n    { &fnv_test_str[174], (Fnv32_t) 0x0e2d0d0fUL },\n    { &fnv_test_str[175], (Fnv32_t) 0x06adc8fdUL },\n    { &fnv_test_str[176], (Fnv32_t) 0x60e0d4b9UL },\n    { &fnv_test_str[177], (Fnv32_t) 0x5ddc79d3UL },\n    { &fnv_test_str[178], (Fnv32_t) 0x1e6d0b46UL },\n    { &fnv_test_str[179], (Fnv32_t) 0x1d1514d8UL },\n    { &fnv_test_str[180], (Fnv32_t) 0xb1903a4eUL },\n    { &fnv_test_str[181], (Fnv32_t) 0x8200c318UL },\n    { &fnv_test_str[182], (Fnv32_t) 0x15e22888UL },\n    { &fnv_test_str[183], (Fnv32_t) 0x57591760UL },\n    { &fnv_test_str[184], (Fnv32_t) 0x02462efcUL },\n    { &fnv_test_str[185], (Fnv32_t) 0x7651ec44UL },\n    { &fnv_test_str[186], (Fnv32_t) 0x7c24e9d4UL },\n    { &fnv_test_str[187], (Fnv32_t) 0x1952a034UL },\n    { &fnv_test_str[188], (Fnv32_t) 0xd4c46864UL },\n    { &fnv_test_str[189], (Fnv32_t) 0xcb57cde0UL },\n    { &fnv_test_str[190], (Fnv32_t) 0x71136a70UL },\n    { &fnv_test_str[191], (Fnv32_t) 0x0618fb40UL },\n    { &fnv_test_str[192], (Fnv32_t) 0x69a24fc0UL },\n    { &fnv_test_str[193], (Fnv32_t) 0x6a9be510UL },\n    { &fnv_test_str[194], (Fnv32_t) 0xe0477040UL },\n    { &fnv_test_str[195], (Fnv32_t) 0x85aa94b0UL },\n    { &fnv_test_str[196], (Fnv32_t) 0xc6d76240UL },\n    { &fnv_test_str[197], (Fnv32_t) 0xa9f09e40UL },\n    { &fnv_test_str[198], (Fnv32_t) 0xa0291540UL },\n    { &fnv_test_str[199], (Fnv32_t) 0x00000000UL },\n    { &fnv_test_str[200], (Fnv32_t) 0x2e672aa4UL },\n    { &fnv_test_str[201], (Fnv32_t) 0x84b1aa48UL },\n    { &fnv_test_str[202], (Fnv32_t) 0xfc24ba24UL },\n    { NULL, 0 }\n};\n\n/* FNV-1 32 bit test vectors */\nstruct fnv1_32_test_vector fnv1_32_vector[] = {\n    { &fnv_test_str[0], (Fnv32_t) 0x811c9dc5UL },\n    { &fnv_test_str[1], (Fnv32_t) 0x050c5d7eUL },\n    { &fnv_test_str[2], (Fnv32_t) 0x050c5d7dUL },\n    { &fnv_test_str[3], (Fnv32_t) 0x050c5d7cUL },\n    { &fnv_test_str[4], (Fnv32_t) 0x050c5d7bUL },\n    { &fnv_test_str[5], (Fnv32_t) 0x050c5d7aUL },\n    { &fnv_test_str[6], (Fnv32_t) 0x050c5d79UL },\n    { &fnv_test_str[7], (Fnv32_t) 0x6b772514UL },\n    { &fnv_test_str[8], (Fnv32_t) 0x408f5e13UL },\n    { &fnv_test_str[9], (Fnv32_t) 0xb4b1178bUL },\n    { &fnv_test_str[10], (Fnv32_t) 0xfdc80fb0UL },\n    { &fnv_test_str[11], (Fnv32_t) 0x31f0b262UL },\n    { &fnv_test_str[12], (Fnv32_t) 0x050c5d1fUL },\n    { &fnv_test_str[13], (Fnv32_t) 0x70772d5aUL },\n    { &fnv_test_str[14], (Fnv32_t) 0x6f772bc7UL },\n    { &fnv_test_str[15], (Fnv32_t) 0x6e772a34UL },\n    { &fnv_test_str[16], (Fnv32_t) 0x6d7728a1UL },\n    { &fnv_test_str[17], (Fnv32_t) 0x6c77270eUL },\n    { &fnv_test_str[18], (Fnv32_t) 0x6b77257bUL },\n    { &fnv_test_str[19], (Fnv32_t) 0x408f5e7cUL },\n    { &fnv_test_str[20], (Fnv32_t) 0xb4b117e9UL },\n    { &fnv_test_str[21], (Fnv32_t) 0xfdc80fd1UL },\n    { &fnv_test_str[22], (Fnv32_t) 0x31f0b210UL },\n    { &fnv_test_str[23], (Fnv32_t) 0xffe8d046UL },\n    { &fnv_test_str[24], (Fnv32_t) 0x6e772a5cUL },\n    { &fnv_test_str[25], (Fnv32_t) 0x4197aebbUL },\n    { &fnv_test_str[26], (Fnv32_t) 0xfcc8100fUL },\n    { &fnv_test_str[27], (Fnv32_t) 0xfdf147faUL },\n    { &fnv_test_str[28], (Fnv32_t) 0xbcd44ee1UL },\n    { &fnv_test_str[29], (Fnv32_t) 0x23382c13UL },\n    { &fnv_test_str[30], (Fnv32_t) 0x846d619eUL },\n    { &fnv_test_str[31], (Fnv32_t) 0x1630abdbUL },\n    { &fnv_test_str[32], (Fnv32_t) 0xc99e89b2UL },\n    { &fnv_test_str[33], (Fnv32_t) 0x1692c316UL },\n    { &fnv_test_str[34], (Fnv32_t) 0x9f091bcaUL },\n    { &fnv_test_str[35], (Fnv32_t) 0x2556be9bUL },\n    { &fnv_test_str[36], (Fnv32_t) 0x628e0e73UL },\n    { &fnv_test_str[37], (Fnv32_t) 0x98a0bf6cUL },\n    { &fnv_test_str[38], (Fnv32_t) 0xb10d5725UL },\n    { &fnv_test_str[39], (Fnv32_t) 0xdd002f35UL },\n    { &fnv_test_str[40], (Fnv32_t) 0x4197aed4UL },\n    { &fnv_test_str[41], (Fnv32_t) 0xfcc81061UL },\n    { &fnv_test_str[42], (Fnv32_t) 0xfdf1479dUL },\n    { &fnv_test_str[43], (Fnv32_t) 0xbcd44e8eUL },\n    { &fnv_test_str[44], (Fnv32_t) 0x23382c33UL },\n    { &fnv_test_str[45], (Fnv32_t) 0x846d61e9UL },\n    { &fnv_test_str[46], (Fnv32_t) 0x1630abbaUL },\n    { &fnv_test_str[47], (Fnv32_t) 0xc99e89c1UL },\n    { &fnv_test_str[48], (Fnv32_t) 0x1692c336UL },\n    { &fnv_test_str[49], (Fnv32_t) 0x9f091ba2UL },\n    { &fnv_test_str[50], (Fnv32_t) 0x2556befeUL },\n    { &fnv_test_str[51], (Fnv32_t) 0x628e0e01UL },\n    { &fnv_test_str[52], (Fnv32_t) 0x98a0bf09UL },\n    { &fnv_test_str[53], (Fnv32_t) 0xb10d5704UL },\n    { &fnv_test_str[54], (Fnv32_t) 0xdd002f3fUL },\n    { &fnv_test_str[55], (Fnv32_t) 0x1c4a506fUL },\n    { &fnv_test_str[56], (Fnv32_t) 0x6e772a41UL },\n    { &fnv_test_str[57], (Fnv32_t) 0x26978421UL },\n    { &fnv_test_str[58], (Fnv32_t) 0xe184ff97UL },\n    { &fnv_test_str[59], (Fnv32_t) 0x9b5e5ac6UL },\n    { &fnv_test_str[60], (Fnv32_t) 0x5b88e592UL },\n    { &fnv_test_str[61], (Fnv32_t) 0xaa8164b7UL },\n    { &fnv_test_str[62], (Fnv32_t) 0x20b18c7bUL },\n    { &fnv_test_str[63], (Fnv32_t) 0xf28025c5UL },\n    { &fnv_test_str[64], (Fnv32_t) 0x84bb753fUL },\n    { &fnv_test_str[65], (Fnv32_t) 0x3219925aUL },\n    { &fnv_test_str[66], (Fnv32_t) 0x384163c6UL },\n    { &fnv_test_str[67], (Fnv32_t) 0x54f010d7UL },\n    { &fnv_test_str[68], (Fnv32_t) 0x8cea820cUL },\n    { &fnv_test_str[69], (Fnv32_t) 0xe12ab8eeUL },\n    { &fnv_test_str[70], (Fnv32_t) 0x26978453UL },\n    { &fnv_test_str[71], (Fnv32_t) 0xe184fff3UL },\n    { &fnv_test_str[72], (Fnv32_t) 0x9b5e5ab5UL },\n    { &fnv_test_str[73], (Fnv32_t) 0x5b88e5b2UL },\n    { &fnv_test_str[74], (Fnv32_t) 0xaa8164d6UL },\n    { &fnv_test_str[75], (Fnv32_t) 0x20b18c15UL },\n    { &fnv_test_str[76], (Fnv32_t) 0xf28025a1UL },\n    { &fnv_test_str[77], (Fnv32_t) 0x84bb751fUL },\n    { &fnv_test_str[78], (Fnv32_t) 0x3219922dUL },\n    { &fnv_test_str[79], (Fnv32_t) 0x384163aeUL },\n    { &fnv_test_str[80], (Fnv32_t) 0x54f010b2UL },\n    { &fnv_test_str[81], (Fnv32_t) 0x8cea8275UL },\n    { &fnv_test_str[82], (Fnv32_t) 0xe12ab8e4UL },\n    { &fnv_test_str[83], (Fnv32_t) 0x64411eaaUL },\n    { &fnv_test_str[84], (Fnv32_t) 0x6977223cUL },\n    { &fnv_test_str[85], (Fnv32_t) 0x428ae474UL },\n    { &fnv_test_str[86], (Fnv32_t) 0xb6fa7167UL },\n    { &fnv_test_str[87], (Fnv32_t) 0x73408525UL },\n    { &fnv_test_str[88], (Fnv32_t) 0xb78320a1UL },\n    { &fnv_test_str[89], (Fnv32_t) 0x0caf4135UL },\n    { &fnv_test_str[90], (Fnv32_t) 0xb78320a2UL },\n    { &fnv_test_str[91], (Fnv32_t) 0xcdc88e80UL },\n    { &fnv_test_str[92], (Fnv32_t) 0xb78320a3UL },\n    { &fnv_test_str[93], (Fnv32_t) 0x8ee1dbcbUL },\n    { &fnv_test_str[94], (Fnv32_t) 0xb78320a4UL },\n    { &fnv_test_str[95], (Fnv32_t) 0x4ffb2716UL },\n    { &fnv_test_str[96], (Fnv32_t) 0x860632aaUL },\n    { &fnv_test_str[97], (Fnv32_t) 0xcc2c5c64UL },\n    { &fnv_test_str[98], (Fnv32_t) 0x860632a4UL },\n    { &fnv_test_str[99], (Fnv32_t) 0x2a7ec4a6UL },\n    { &fnv_test_str[100], (Fnv32_t) 0x860632baUL },\n    { &fnv_test_str[101], (Fnv32_t) 0xfefe8e14UL },\n    { &fnv_test_str[102], (Fnv32_t) 0x0a3cffd8UL },\n    { &fnv_test_str[103], (Fnv32_t) 0xf606c108UL },\n    { &fnv_test_str[104], (Fnv32_t) 0x0a3cffdbUL },\n    { &fnv_test_str[105], (Fnv32_t) 0xf906c5c1UL },\n    { &fnv_test_str[106], (Fnv32_t) 0x0a3cffdaUL },\n    { &fnv_test_str[107], (Fnv32_t) 0xf806c42eUL },\n    { &fnv_test_str[108], (Fnv32_t) 0xc07167d7UL },\n    { &fnv_test_str[109], (Fnv32_t) 0xc9867775UL },\n    { &fnv_test_str[110], (Fnv32_t) 0xbf716668UL },\n    { &fnv_test_str[111], (Fnv32_t) 0xc78435b8UL },\n    { &fnv_test_str[112], (Fnv32_t) 0xc6717155UL },\n    { &fnv_test_str[113], (Fnv32_t) 0xb99568cfUL },\n    { &fnv_test_str[114], (Fnv32_t) 0x7662e0d6UL },\n    { &fnv_test_str[115], (Fnv32_t) 0x33a7f0e2UL },\n    { &fnv_test_str[116], (Fnv32_t) 0xc2732f95UL },\n    { &fnv_test_str[117], (Fnv32_t) 0xb053e78fUL },\n    { &fnv_test_str[118], (Fnv32_t) 0x3a19c02aUL },\n    { &fnv_test_str[119], (Fnv32_t) 0xa089821eUL },\n    { &fnv_test_str[120], (Fnv32_t) 0x31ae8f83UL },\n    { &fnv_test_str[121], (Fnv32_t) 0x995fa9c4UL },\n    { &fnv_test_str[122], (Fnv32_t) 0x35983f8cUL },\n    { &fnv_test_str[123], (Fnv32_t) 0x5036a251UL },\n    { &fnv_test_str[124], (Fnv32_t) 0x97018583UL },\n    { &fnv_test_str[125], (Fnv32_t) 0xb4448d60UL },\n    { &fnv_test_str[126], (Fnv32_t) 0x025dfe59UL },\n    { &fnv_test_str[127], (Fnv32_t) 0xc5eab3afUL },\n    { &fnv_test_str[128], (Fnv32_t) 0x7d21ba1eUL },\n    { &fnv_test_str[129], (Fnv32_t) 0x7704cddbUL },\n    { &fnv_test_str[130], (Fnv32_t) 0xd0071bfeUL },\n    { &fnv_test_str[131], (Fnv32_t) 0x0ff3774cUL },\n    { &fnv_test_str[132], (Fnv32_t) 0xb0fea0eaUL },\n    { &fnv_test_str[133], (Fnv32_t) 0x58177303UL },\n    { &fnv_test_str[134], (Fnv32_t) 0x4f599cdaUL },\n    { &fnv_test_str[135], (Fnv32_t) 0x3e590a47UL },\n    { &fnv_test_str[136], (Fnv32_t) 0x965595f8UL },\n    { &fnv_test_str[137], (Fnv32_t) 0xc37f178dUL },\n    { &fnv_test_str[138], (Fnv32_t) 0x9711dd26UL },\n    { &fnv_test_str[139], (Fnv32_t) 0x23c99b7fUL },\n    { &fnv_test_str[140], (Fnv32_t) 0x6e568b17UL },\n    { &fnv_test_str[141], (Fnv32_t) 0x43f0245bUL },\n    { &fnv_test_str[142], (Fnv32_t) 0xbcb7a001UL },\n    { &fnv_test_str[143], (Fnv32_t) 0x12e6dffeUL },\n    { &fnv_test_str[144], (Fnv32_t) 0x0792f2d6UL },\n    { &fnv_test_str[145], (Fnv32_t) 0xb966936bUL },\n    { &fnv_test_str[146], (Fnv32_t) 0x46439ac5UL },\n    { &fnv_test_str[147], (Fnv32_t) 0x728d49afUL },\n    { &fnv_test_str[148], (Fnv32_t) 0xd33745c9UL },\n    { &fnv_test_str[149], (Fnv32_t) 0xbc382a57UL },\n    { &fnv_test_str[150], (Fnv32_t) 0x4bda1d31UL },\n    { &fnv_test_str[151], (Fnv32_t) 0xce35ccaeUL },\n    { &fnv_test_str[152], (Fnv32_t) 0x3b6eed94UL },\n    { &fnv_test_str[153], (Fnv32_t) 0x445c9c58UL },\n    { &fnv_test_str[154], (Fnv32_t) 0x3db8bf9dUL },\n    { &fnv_test_str[155], (Fnv32_t) 0x2dee116dUL },\n    { &fnv_test_str[156], (Fnv32_t) 0xc18738daUL },\n    { &fnv_test_str[157], (Fnv32_t) 0x5b156176UL },\n    { &fnv_test_str[158], (Fnv32_t) 0x2aa7d593UL },\n    { &fnv_test_str[159], (Fnv32_t) 0xb2409658UL },\n    { &fnv_test_str[160], (Fnv32_t) 0xe1489528UL },\n    { &fnv_test_str[161], (Fnv32_t) 0xfe1ee07eUL },\n    { &fnv_test_str[162], (Fnv32_t) 0xe8842315UL },\n    { &fnv_test_str[163], (Fnv32_t) 0x3a6a63a2UL },\n    { &fnv_test_str[164], (Fnv32_t) 0x06d2c18cUL },\n    { &fnv_test_str[165], (Fnv32_t) 0xf8ef7225UL },\n    { &fnv_test_str[166], (Fnv32_t) 0x843d3300UL },\n    { &fnv_test_str[167], (Fnv32_t) 0xbb24f7aeUL },\n    { &fnv_test_str[168], (Fnv32_t) 0x878c0ec9UL },\n    { &fnv_test_str[169], (Fnv32_t) 0xb557810fUL },\n    { &fnv_test_str[170], (Fnv32_t) 0x57423246UL },\n    { &fnv_test_str[171], (Fnv32_t) 0x87f7505eUL },\n    { &fnv_test_str[172], (Fnv32_t) 0xbb809f20UL },\n    { &fnv_test_str[173], (Fnv32_t) 0x8932abb5UL },\n    { &fnv_test_str[174], (Fnv32_t) 0x0a9b3aa0UL },\n    { &fnv_test_str[175], (Fnv32_t) 0xb8682a24UL },\n    { &fnv_test_str[176], (Fnv32_t) 0xa7ac1c56UL },\n    { &fnv_test_str[177], (Fnv32_t) 0x11409252UL },\n    { &fnv_test_str[178], (Fnv32_t) 0xa987f517UL },\n    { &fnv_test_str[179], (Fnv32_t) 0xf309e7edUL },\n    { &fnv_test_str[180], (Fnv32_t) 0xc9e8f417UL },\n    { &fnv_test_str[181], (Fnv32_t) 0x7f447bddUL },\n    { &fnv_test_str[182], (Fnv32_t) 0xb929adc5UL },\n    { &fnv_test_str[183], (Fnv32_t) 0x57022879UL },\n    { &fnv_test_str[184], (Fnv32_t) 0xdcfd2c49UL },\n    { &fnv_test_str[185], (Fnv32_t) 0x6edafff5UL },\n    { &fnv_test_str[186], (Fnv32_t) 0xf04fb1f1UL },\n    { &fnv_test_str[187], (Fnv32_t) 0xfb7de8b9UL },\n    { &fnv_test_str[188], (Fnv32_t) 0xc5f1d7e9UL },\n    { &fnv_test_str[189], (Fnv32_t) 0x32c1f439UL },\n    { &fnv_test_str[190], (Fnv32_t) 0x7fd3eb7dUL },\n    { &fnv_test_str[191], (Fnv32_t) 0x81597da5UL },\n    { &fnv_test_str[192], (Fnv32_t) 0x05eb7a25UL },\n    { &fnv_test_str[193], (Fnv32_t) 0x9c0fa1b5UL },\n    { &fnv_test_str[194], (Fnv32_t) 0x53ccb1c5UL },\n    { &fnv_test_str[195], (Fnv32_t) 0xfabece15UL },\n    { &fnv_test_str[196], (Fnv32_t) 0x4ad745a5UL },\n    { &fnv_test_str[197], (Fnv32_t) 0xe5bdc495UL },\n    { &fnv_test_str[198], (Fnv32_t) 0x23b3c0a5UL },\n    { &fnv_test_str[199], (Fnv32_t) 0xfa823dd5UL },\n    { &fnv_test_str[200], (Fnv32_t) 0x0c6c58b9UL },\n    { &fnv_test_str[201], (Fnv32_t) 0xe2dbccd5UL },\n    { &fnv_test_str[202], (Fnv32_t) 0xdb7f50f9UL },\n    { NULL, 0 }\n};\n\n/* FNV-1a 32 bit test vectors */\nstruct fnv1a_32_test_vector fnv1a_32_vector[] = {\n    { &fnv_test_str[0], (Fnv32_t) 0x811c9dc5UL },\n    { &fnv_test_str[1], (Fnv32_t) 0xe40c292cUL },\n    { &fnv_test_str[2], (Fnv32_t) 0xe70c2de5UL },\n    { &fnv_test_str[3], (Fnv32_t) 0xe60c2c52UL },\n    { &fnv_test_str[4], (Fnv32_t) 0xe10c2473UL },\n    { &fnv_test_str[5], (Fnv32_t) 0xe00c22e0UL },\n    { &fnv_test_str[6], (Fnv32_t) 0xe30c2799UL },\n    { &fnv_test_str[7], (Fnv32_t) 0x6222e842UL },\n    { &fnv_test_str[8], (Fnv32_t) 0xa9f37ed7UL },\n    { &fnv_test_str[9], (Fnv32_t) 0x3f5076efUL },\n    { &fnv_test_str[10], (Fnv32_t) 0x39aaa18aUL },\n    { &fnv_test_str[11], (Fnv32_t) 0xbf9cf968UL },\n    { &fnv_test_str[12], (Fnv32_t) 0x050c5d1fUL },\n    { &fnv_test_str[13], (Fnv32_t) 0x2b24d044UL },\n    { &fnv_test_str[14], (Fnv32_t) 0x9d2c3f7fUL },\n    { &fnv_test_str[15], (Fnv32_t) 0x7729c516UL },\n    { &fnv_test_str[16], (Fnv32_t) 0xb91d6109UL },\n    { &fnv_test_str[17], (Fnv32_t) 0x931ae6a0UL },\n    { &fnv_test_str[18], (Fnv32_t) 0x052255dbUL },\n    { &fnv_test_str[19], (Fnv32_t) 0xbef39fe6UL },\n    { &fnv_test_str[20], (Fnv32_t) 0x6150ac75UL },\n    { &fnv_test_str[21], (Fnv32_t) 0x9aab3a3dUL },\n    { &fnv_test_str[22], (Fnv32_t) 0x519c4c3eUL },\n    { &fnv_test_str[23], (Fnv32_t) 0x0c1c9eb8UL },\n    { &fnv_test_str[24], (Fnv32_t) 0x5f299f4eUL },\n    { &fnv_test_str[25], (Fnv32_t) 0xef8580f3UL },\n    { &fnv_test_str[26], (Fnv32_t) 0xac297727UL },\n    { &fnv_test_str[27], (Fnv32_t) 0x4546b9c0UL },\n    { &fnv_test_str[28], (Fnv32_t) 0xbd564e7dUL },\n    { &fnv_test_str[29], (Fnv32_t) 0x6bdd5c67UL },\n    { &fnv_test_str[30], (Fnv32_t) 0xdd77ed30UL },\n    { &fnv_test_str[31], (Fnv32_t) 0xf4ca9683UL },\n    { &fnv_test_str[32], (Fnv32_t) 0x4aeb9bd0UL },\n    { &fnv_test_str[33], (Fnv32_t) 0xe0e67ad0UL },\n    { &fnv_test_str[34], (Fnv32_t) 0xc2d32fa8UL },\n    { &fnv_test_str[35], (Fnv32_t) 0x7f743fb7UL },\n    { &fnv_test_str[36], (Fnv32_t) 0x6900631fUL },\n    { &fnv_test_str[37], (Fnv32_t) 0xc59c990eUL },\n    { &fnv_test_str[38], (Fnv32_t) 0x448524fdUL },\n    { &fnv_test_str[39], (Fnv32_t) 0xd49930d5UL },\n    { &fnv_test_str[40], (Fnv32_t) 0x1c85c7caUL },\n    { &fnv_test_str[41], (Fnv32_t) 0x0229fe89UL },\n    { &fnv_test_str[42], (Fnv32_t) 0x2c469265UL },\n    { &fnv_test_str[43], (Fnv32_t) 0xce566940UL },\n    { &fnv_test_str[44], (Fnv32_t) 0x8bdd8ec7UL },\n    { &fnv_test_str[45], (Fnv32_t) 0x34787625UL },\n    { &fnv_test_str[46], (Fnv32_t) 0xd3ca6290UL },\n    { &fnv_test_str[47], (Fnv32_t) 0xddeaf039UL },\n    { &fnv_test_str[48], (Fnv32_t) 0xc0e64870UL },\n    { &fnv_test_str[49], (Fnv32_t) 0xdad35570UL },\n    { &fnv_test_str[50], (Fnv32_t) 0x5a740578UL },\n    { &fnv_test_str[51], (Fnv32_t) 0x5b004d15UL },\n    { &fnv_test_str[52], (Fnv32_t) 0x6a9c09cdUL },\n    { &fnv_test_str[53], (Fnv32_t) 0x2384f10aUL },\n    { &fnv_test_str[54], (Fnv32_t) 0xda993a47UL },\n    { &fnv_test_str[55], (Fnv32_t) 0x8227df4fUL },\n    { &fnv_test_str[56], (Fnv32_t) 0x4c298165UL },\n    { &fnv_test_str[57], (Fnv32_t) 0xfc563735UL },\n    { &fnv_test_str[58], (Fnv32_t) 0x8cb91483UL },\n    { &fnv_test_str[59], (Fnv32_t) 0x775bf5d0UL },\n    { &fnv_test_str[60], (Fnv32_t) 0xd5c428d0UL },\n    { &fnv_test_str[61], (Fnv32_t) 0x34cc0ea3UL },\n    { &fnv_test_str[62], (Fnv32_t) 0xea3b4cb7UL },\n    { &fnv_test_str[63], (Fnv32_t) 0x8e59f029UL },\n    { &fnv_test_str[64], (Fnv32_t) 0x2094de2bUL },\n    { &fnv_test_str[65], (Fnv32_t) 0xa65a0ad4UL },\n    { &fnv_test_str[66], (Fnv32_t) 0x9bbee5f4UL },\n    { &fnv_test_str[67], (Fnv32_t) 0xbe836343UL },\n    { &fnv_test_str[68], (Fnv32_t) 0x22d5344eUL },\n    { &fnv_test_str[69], (Fnv32_t) 0x19a1470cUL },\n    { &fnv_test_str[70], (Fnv32_t) 0x4a56b1ffUL },\n    { &fnv_test_str[71], (Fnv32_t) 0x70b8e86fUL },\n    { &fnv_test_str[72], (Fnv32_t) 0x0a5b4a39UL },\n    { &fnv_test_str[73], (Fnv32_t) 0xb5c3f670UL },\n    { &fnv_test_str[74], (Fnv32_t) 0x53cc3f70UL },\n    { &fnv_test_str[75], (Fnv32_t) 0xc03b0a99UL },\n    { &fnv_test_str[76], (Fnv32_t) 0x7259c415UL },\n    { &fnv_test_str[77], (Fnv32_t) 0x4095108bUL },\n    { &fnv_test_str[78], (Fnv32_t) 0x7559bdb1UL },\n    { &fnv_test_str[79], (Fnv32_t) 0xb3bf0bbcUL },\n    { &fnv_test_str[80], (Fnv32_t) 0x2183ff1cUL },\n    { &fnv_test_str[81], (Fnv32_t) 0x2bd54279UL },\n    { &fnv_test_str[82], (Fnv32_t) 0x23a156caUL },\n    { &fnv_test_str[83], (Fnv32_t) 0x64e2d7e4UL },\n    { &fnv_test_str[84], (Fnv32_t) 0x683af69aUL },\n    { &fnv_test_str[85], (Fnv32_t) 0xaed2346eUL },\n    { &fnv_test_str[86], (Fnv32_t) 0x4f9f2cabUL },\n    { &fnv_test_str[87], (Fnv32_t) 0x02935131UL },\n    { &fnv_test_str[88], (Fnv32_t) 0xc48fb86dUL },\n    { &fnv_test_str[89], (Fnv32_t) 0x2269f369UL },\n    { &fnv_test_str[90], (Fnv32_t) 0xc18fb3b4UL },\n    { &fnv_test_str[91], (Fnv32_t) 0x50ef1236UL },\n    { &fnv_test_str[92], (Fnv32_t) 0xc28fb547UL },\n    { &fnv_test_str[93], (Fnv32_t) 0x96c3bf47UL },\n    { &fnv_test_str[94], (Fnv32_t) 0xbf8fb08eUL },\n    { &fnv_test_str[95], (Fnv32_t) 0xf3e4d49cUL },\n    { &fnv_test_str[96], (Fnv32_t) 0x32179058UL },\n    { &fnv_test_str[97], (Fnv32_t) 0x280bfee6UL },\n    { &fnv_test_str[98], (Fnv32_t) 0x30178d32UL },\n    { &fnv_test_str[99], (Fnv32_t) 0x21addaf8UL },\n    { &fnv_test_str[100], (Fnv32_t) 0x4217a988UL },\n    { &fnv_test_str[101], (Fnv32_t) 0x772633d6UL },\n    { &fnv_test_str[102], (Fnv32_t) 0x08a3d11eUL },\n    { &fnv_test_str[103], (Fnv32_t) 0xb7e2323aUL },\n    { &fnv_test_str[104], (Fnv32_t) 0x07a3cf8bUL },\n    { &fnv_test_str[105], (Fnv32_t) 0x91dfb7d1UL },\n    { &fnv_test_str[106], (Fnv32_t) 0x06a3cdf8UL },\n    { &fnv_test_str[107], (Fnv32_t) 0x6bdd3d68UL },\n    { &fnv_test_str[108], (Fnv32_t) 0x1d5636a7UL },\n    { &fnv_test_str[109], (Fnv32_t) 0xd5b808e5UL },\n    { &fnv_test_str[110], (Fnv32_t) 0x1353e852UL },\n    { &fnv_test_str[111], (Fnv32_t) 0xbf16b916UL },\n    { &fnv_test_str[112], (Fnv32_t) 0xa55b89edUL },\n    { &fnv_test_str[113], (Fnv32_t) 0x3c1a2017UL },\n    { &fnv_test_str[114], (Fnv32_t) 0x0588b13cUL },\n    { &fnv_test_str[115], (Fnv32_t) 0xf22f0174UL },\n    { &fnv_test_str[116], (Fnv32_t) 0xe83641e1UL },\n    { &fnv_test_str[117], (Fnv32_t) 0x6e69b533UL },\n    { &fnv_test_str[118], (Fnv32_t) 0xf1760448UL },\n    { &fnv_test_str[119], (Fnv32_t) 0x64c8bd58UL },\n    { &fnv_test_str[120], (Fnv32_t) 0x97b4ea23UL },\n    { &fnv_test_str[121], (Fnv32_t) 0x9a4e92e6UL },\n    { &fnv_test_str[122], (Fnv32_t) 0xcfb14012UL },\n    { &fnv_test_str[123], (Fnv32_t) 0xf01b2511UL },\n    { &fnv_test_str[124], (Fnv32_t) 0x0bbb59c3UL },\n    { &fnv_test_str[125], (Fnv32_t) 0xce524afaUL },\n    { &fnv_test_str[126], (Fnv32_t) 0xdd16ef45UL },\n    { &fnv_test_str[127], (Fnv32_t) 0x60648bb3UL },\n    { &fnv_test_str[128], (Fnv32_t) 0x7fa4bcfcUL },\n    { &fnv_test_str[129], (Fnv32_t) 0x5053ae17UL },\n    { &fnv_test_str[130], (Fnv32_t) 0xc9302890UL },\n    { &fnv_test_str[131], (Fnv32_t) 0x956ded32UL },\n    { &fnv_test_str[132], (Fnv32_t) 0x9136db84UL },\n    { &fnv_test_str[133], (Fnv32_t) 0xdf9d3323UL },\n    { &fnv_test_str[134], (Fnv32_t) 0x32bb6cd0UL },\n    { &fnv_test_str[135], (Fnv32_t) 0xc8f8385bUL },\n    { &fnv_test_str[136], (Fnv32_t) 0xeb08bfbaUL },\n    { &fnv_test_str[137], (Fnv32_t) 0x62cc8e3dUL },\n    { &fnv_test_str[138], (Fnv32_t) 0xc3e20f5cUL },\n    { &fnv_test_str[139], (Fnv32_t) 0x39e97f17UL },\n    { &fnv_test_str[140], (Fnv32_t) 0x7837b203UL },\n    { &fnv_test_str[141], (Fnv32_t) 0x319e877bUL },\n    { &fnv_test_str[142], (Fnv32_t) 0xd3e63f89UL },\n    { &fnv_test_str[143], (Fnv32_t) 0x29b50b38UL },\n    { &fnv_test_str[144], (Fnv32_t) 0x5ed678b8UL },\n    { &fnv_test_str[145], (Fnv32_t) 0xb0d5b793UL },\n    { &fnv_test_str[146], (Fnv32_t) 0x52450be5UL },\n    { &fnv_test_str[147], (Fnv32_t) 0xfa72d767UL },\n    { &fnv_test_str[148], (Fnv32_t) 0x95066709UL },\n    { &fnv_test_str[149], (Fnv32_t) 0x7f52e123UL },\n    { &fnv_test_str[150], (Fnv32_t) 0x76966481UL },\n    { &fnv_test_str[151], (Fnv32_t) 0x063258b0UL },\n    { &fnv_test_str[152], (Fnv32_t) 0x2ded6e8aUL },\n    { &fnv_test_str[153], (Fnv32_t) 0xb07d7c52UL },\n    { &fnv_test_str[154], (Fnv32_t) 0xd0c71b71UL },\n    { &fnv_test_str[155], (Fnv32_t) 0xf684f1bdUL },\n    { &fnv_test_str[156], (Fnv32_t) 0x868ecfa8UL },\n    { &fnv_test_str[157], (Fnv32_t) 0xf794f684UL },\n    { &fnv_test_str[158], (Fnv32_t) 0xd19701c3UL },\n    { &fnv_test_str[159], (Fnv32_t) 0x346e171eUL },\n    { &fnv_test_str[160], (Fnv32_t) 0x91f8f676UL },\n    { &fnv_test_str[161], (Fnv32_t) 0x0bf58848UL },\n    { &fnv_test_str[162], (Fnv32_t) 0x6317b6d1UL },\n    { &fnv_test_str[163], (Fnv32_t) 0xafad4c54UL },\n    { &fnv_test_str[164], (Fnv32_t) 0x0f25681eUL },\n    { &fnv_test_str[165], (Fnv32_t) 0x91b18d49UL },\n    { &fnv_test_str[166], (Fnv32_t) 0x7d61c12eUL },\n    { &fnv_test_str[167], (Fnv32_t) 0x5147d25cUL },\n    { &fnv_test_str[168], (Fnv32_t) 0x9a8b6805UL },\n    { &fnv_test_str[169], (Fnv32_t) 0x4cd2a447UL },\n    { &fnv_test_str[170], (Fnv32_t) 0x1e549b14UL },\n    { &fnv_test_str[171], (Fnv32_t) 0x2fe1b574UL },\n    { &fnv_test_str[172], (Fnv32_t) 0xcf0cd31eUL },\n    { &fnv_test_str[173], (Fnv32_t) 0x6c471669UL },\n    { &fnv_test_str[174], (Fnv32_t) 0x0e5eef1eUL },\n    { &fnv_test_str[175], (Fnv32_t) 0x2bed3602UL },\n    { &fnv_test_str[176], (Fnv32_t) 0xb26249e0UL },\n    { &fnv_test_str[177], (Fnv32_t) 0x2c9b86a4UL },\n    { &fnv_test_str[178], (Fnv32_t) 0xe415e2bbUL },\n    { &fnv_test_str[179], (Fnv32_t) 0x18a98d1dUL },\n    { &fnv_test_str[180], (Fnv32_t) 0xb7df8b7bUL },\n    { &fnv_test_str[181], (Fnv32_t) 0x241e9075UL },\n    { &fnv_test_str[182], (Fnv32_t) 0x063f70ddUL },\n    { &fnv_test_str[183], (Fnv32_t) 0x0295aed9UL },\n    { &fnv_test_str[184], (Fnv32_t) 0x56a7f781UL },\n    { &fnv_test_str[185], (Fnv32_t) 0x253bc645UL },\n    { &fnv_test_str[186], (Fnv32_t) 0x46610921UL },\n    { &fnv_test_str[187], (Fnv32_t) 0x7c1577f9UL },\n    { &fnv_test_str[188], (Fnv32_t) 0x512b2851UL },\n    { &fnv_test_str[189], (Fnv32_t) 0x76823999UL },\n    { &fnv_test_str[190], (Fnv32_t) 0xc0586935UL },\n    { &fnv_test_str[191], (Fnv32_t) 0xf3415c85UL },\n    { &fnv_test_str[192], (Fnv32_t) 0x0ae4ff65UL },\n    { &fnv_test_str[193], (Fnv32_t) 0x58b79725UL },\n    { &fnv_test_str[194], (Fnv32_t) 0xdea43aa5UL },\n    { &fnv_test_str[195], (Fnv32_t) 0x2bb3be35UL },\n    { &fnv_test_str[196], (Fnv32_t) 0xea777a45UL },\n    { &fnv_test_str[197], (Fnv32_t) 0x8f21c305UL },\n    { &fnv_test_str[198], (Fnv32_t) 0x5c9d0865UL },\n    { &fnv_test_str[199], (Fnv32_t) 0xfa823dd5UL },\n    { &fnv_test_str[200], (Fnv32_t) 0x21a27271UL },\n    { &fnv_test_str[201], (Fnv32_t) 0x83c5c6d5UL },\n    { &fnv_test_str[202], (Fnv32_t) 0x813b0881UL },\n    { NULL, 0 }\n};\n\n/* FNV-0 64 bit test vectors */\n#if defined(HAVE_64BIT_LONG_LONG)\nstruct fnv0_64_test_vector fnv0_64_vector[] = {\n    { &fnv_test_str[0], (Fnv64_t) 0x0000000000000000ULL },\n    { &fnv_test_str[1], (Fnv64_t) 0x0000000000000061ULL },\n    { &fnv_test_str[2], (Fnv64_t) 0x0000000000000062ULL },\n    { &fnv_test_str[3], (Fnv64_t) 0x0000000000000063ULL },\n    { &fnv_test_str[4], (Fnv64_t) 0x0000000000000064ULL },\n    { &fnv_test_str[5], (Fnv64_t) 0x0000000000000065ULL },\n    { &fnv_test_str[6], (Fnv64_t) 0x0000000000000066ULL },\n    { &fnv_test_str[7], (Fnv64_t) 0x000066000000ad3dULL },\n    { &fnv_test_str[8], (Fnv64_t) 0x015a8f0001265ec8ULL },\n    { &fnv_test_str[9], (Fnv64_t) 0x733fc501f4330dbaULL },\n    { &fnv_test_str[10], (Fnv64_t) 0x08697c51f2c0536fULL },\n    { &fnv_test_str[11], (Fnv64_t) 0x0b91ae3f7ccdc5efULL },\n    { &fnv_test_str[12], (Fnv64_t) 0x0000000000000000ULL },\n    { &fnv_test_str[13], (Fnv64_t) 0x000061000000a4d3ULL },\n    { &fnv_test_str[14], (Fnv64_t) 0x000062000000a686ULL },\n    { &fnv_test_str[15], (Fnv64_t) 0x000063000000a839ULL },\n    { &fnv_test_str[16], (Fnv64_t) 0x000064000000a9ecULL },\n    { &fnv_test_str[17], (Fnv64_t) 0x000065000000ab9fULL },\n    { &fnv_test_str[18], (Fnv64_t) 0x000066000000ad52ULL },\n    { &fnv_test_str[19], (Fnv64_t) 0x015a8f0001265ea7ULL },\n    { &fnv_test_str[20], (Fnv64_t) 0x733fc501f4330dd8ULL },\n    { &fnv_test_str[21], (Fnv64_t) 0x08697c51f2c0530eULL },\n    { &fnv_test_str[22], (Fnv64_t) 0x0b91ae3f7ccdc59dULL },\n    { &fnv_test_str[23], (Fnv64_t) 0x765104e111a7551dULL },\n    { &fnv_test_str[24], (Fnv64_t) 0x000063000000a851ULL },\n    { &fnv_test_str[25], (Fnv64_t) 0x01508a00011e01ccULL },\n    { &fnv_test_str[26], (Fnv64_t) 0x59dc4a01e5fd0dcaULL },\n    { &fnv_test_str[27], (Fnv64_t) 0xae5f8b39ccfe6e59ULL },\n    { &fnv_test_str[28], (Fnv64_t) 0x4ac7ec3754558154ULL },\n    { &fnv_test_str[29], (Fnv64_t) 0x6737b6044d4ac19cULL },\n    { &fnv_test_str[30], (Fnv64_t) 0xae6be54f5606fc63ULL },\n    { &fnv_test_str[31], (Fnv64_t) 0x685308cf2ddedc58ULL },\n    { &fnv_test_str[32], (Fnv64_t) 0x23f4500af1b069fbULL },\n    { &fnv_test_str[33], (Fnv64_t) 0xc88dfd98aec415a1ULL },\n    { &fnv_test_str[34], (Fnv64_t) 0x8d5b8b70f730c0fbULL },\n    { &fnv_test_str[35], (Fnv64_t) 0x634eebf407d7eae4ULL },\n    { &fnv_test_str[36], (Fnv64_t) 0x9705d3a953e4211eULL },\n    { &fnv_test_str[37], (Fnv64_t) 0x8307c6b98ca4459fULL },\n    { &fnv_test_str[38], (Fnv64_t) 0x4a7c4c49fb224d0cULL },\n    { &fnv_test_str[39], (Fnv64_t) 0xb382adb5bb48eb6eULL },\n    { &fnv_test_str[40], (Fnv64_t) 0x01508a00011e01a3ULL },\n    { &fnv_test_str[41], (Fnv64_t) 0x59dc4a01e5fd0da4ULL },\n    { &fnv_test_str[42], (Fnv64_t) 0xae5f8b39ccfe6e3eULL },\n    { &fnv_test_str[43], (Fnv64_t) 0x4ac7ec375455813bULL },\n    { &fnv_test_str[44], (Fnv64_t) 0x6737b6044d4ac1bcULL },\n    { &fnv_test_str[45], (Fnv64_t) 0xae6be54f5606fc14ULL },\n    { &fnv_test_str[46], (Fnv64_t) 0x685308cf2ddedc39ULL },\n    { &fnv_test_str[47], (Fnv64_t) 0x23f4500af1b06988ULL },\n    { &fnv_test_str[48], (Fnv64_t) 0xc88dfd98aec41581ULL },\n    { &fnv_test_str[49], (Fnv64_t) 0x8d5b8b70f730c093ULL },\n    { &fnv_test_str[50], (Fnv64_t) 0x634eebf407d7ea81ULL },\n    { &fnv_test_str[51], (Fnv64_t) 0x9705d3a953e4216cULL },\n    { &fnv_test_str[52], (Fnv64_t) 0x8307c6b98ca445faULL },\n    { &fnv_test_str[53], (Fnv64_t) 0x4a7c4c49fb224d2dULL },\n    { &fnv_test_str[54], (Fnv64_t) 0xb382adb5bb48eb64ULL },\n    { &fnv_test_str[55], (Fnv64_t) 0x4ff899cd3ce80beaULL },\n    { &fnv_test_str[56], (Fnv64_t) 0x000063000000a84cULL },\n    { &fnv_test_str[57], (Fnv64_t) 0x01508500011df956ULL },\n    { &fnv_test_str[58], (Fnv64_t) 0x59cb5501e5eead46ULL },\n    { &fnv_test_str[59], (Fnv64_t) 0x832eb839b4906d81ULL },\n    { &fnv_test_str[60], (Fnv64_t) 0x78d08b0dd16a1213ULL },\n    { &fnv_test_str[61], (Fnv64_t) 0xb46e5b7ad73cb628ULL },\n    { &fnv_test_str[62], (Fnv64_t) 0xd43b99bbbc298596ULL },\n    { &fnv_test_str[63], (Fnv64_t) 0xcacbd000ba8dfd86ULL },\n    { &fnv_test_str[64], (Fnv64_t) 0x264ff73cff45ca92ULL },\n    { &fnv_test_str[65], (Fnv64_t) 0x5fabaea5c3973661ULL },\n    { &fnv_test_str[66], (Fnv64_t) 0x27f024ab59f166bbULL },\n    { &fnv_test_str[67], (Fnv64_t) 0xce750a29d5318fa4ULL },\n    { &fnv_test_str[68], (Fnv64_t) 0x026fe915433713d5ULL },\n    { &fnv_test_str[69], (Fnv64_t) 0x5b3ce4213696b2e5ULL },\n    { &fnv_test_str[70], (Fnv64_t) 0x01508500011df924ULL },\n    { &fnv_test_str[71], (Fnv64_t) 0x59cb5501e5eead22ULL },\n    { &fnv_test_str[72], (Fnv64_t) 0x832eb839b4906df2ULL },\n    { &fnv_test_str[73], (Fnv64_t) 0x78d08b0dd16a1233ULL },\n    { &fnv_test_str[74], (Fnv64_t) 0xb46e5b7ad73cb649ULL },\n    { &fnv_test_str[75], (Fnv64_t) 0xd43b99bbbc2985f8ULL },\n    { &fnv_test_str[76], (Fnv64_t) 0xcacbd000ba8dfde2ULL },\n    { &fnv_test_str[77], (Fnv64_t) 0x264ff73cff45cab2ULL },\n    { &fnv_test_str[78], (Fnv64_t) 0x5fabaea5c3973616ULL },\n    { &fnv_test_str[79], (Fnv64_t) 0x27f024ab59f166d3ULL },\n    { &fnv_test_str[80], (Fnv64_t) 0xce750a29d5318fc1ULL },\n    { &fnv_test_str[81], (Fnv64_t) 0x026fe915433713acULL },\n    { &fnv_test_str[82], (Fnv64_t) 0x5b3ce4213696b2efULL },\n    { &fnv_test_str[83], (Fnv64_t) 0x9f2a896fc211fb1fULL },\n    { &fnv_test_str[84], (Fnv64_t) 0x000068000000b0d1ULL },\n    { &fnv_test_str[85], (Fnv64_t) 0x01618900012c7323ULL },\n    { &fnv_test_str[86], (Fnv64_t) 0x3fa86e63bc7d03c8ULL },\n    { &fnv_test_str[87], (Fnv64_t) 0xa8375b79486d6cd8ULL },\n    { &fnv_test_str[88], (Fnv64_t) 0xa0d18504e316ac54ULL },\n    { &fnv_test_str[89], (Fnv64_t) 0x08a97b0004e7fe54ULL },\n    { &fnv_test_str[90], (Fnv64_t) 0xa0d18504e316ac57ULL },\n    { &fnv_test_str[91], (Fnv64_t) 0x1152f60009cffda9ULL },\n    { &fnv_test_str[92], (Fnv64_t) 0xa0d18504e316ac56ULL },\n    { &fnv_test_str[93], (Fnv64_t) 0x19fc71000eb7fcfeULL },\n    { &fnv_test_str[94], (Fnv64_t) 0xa0d18504e316ac51ULL },\n    { &fnv_test_str[95], (Fnv64_t) 0x22a5ec00139ffa53ULL },\n    { &fnv_test_str[96], (Fnv64_t) 0x29bed00139779a33ULL },\n    { &fnv_test_str[97], (Fnv64_t) 0x4dbc81014e3c19f5ULL },\n    { &fnv_test_str[98], (Fnv64_t) 0x29bed00139779a3dULL },\n    { &fnv_test_str[99], (Fnv64_t) 0x81a72b016b9f7573ULL },\n    { &fnv_test_str[100], (Fnv64_t) 0x29bed00139779a23ULL },\n    { &fnv_test_str[101], (Fnv64_t) 0xd85411019cbbce45ULL },\n    { &fnv_test_str[102], (Fnv64_t) 0xf548616b8621d657ULL },\n    { &fnv_test_str[103], (Fnv64_t) 0xebd3e0b4eb7f35d5ULL },\n    { &fnv_test_str[104], (Fnv64_t) 0xf548616b8621d654ULL },\n    { &fnv_test_str[105], (Fnv64_t) 0xebd3ddb4eb7f30bcULL },\n    { &fnv_test_str[106], (Fnv64_t) 0xf548616b8621d655ULL },\n    { &fnv_test_str[107], (Fnv64_t) 0xebd3deb4eb7f326fULL },\n    { &fnv_test_str[108], (Fnv64_t) 0x581cb60340ab0968ULL },\n    { &fnv_test_str[109], (Fnv64_t) 0x63d2af86e2a0fbb8ULL },\n    { &fnv_test_str[110], (Fnv64_t) 0x581cb70340ab0b37ULL },\n    { &fnv_test_str[111], (Fnv64_t) 0x63d63186e2a40e75ULL },\n    { &fnv_test_str[112], (Fnv64_t) 0x581cc40340ab212eULL },\n    { &fnv_test_str[113], (Fnv64_t) 0x64023f86e2c9612aULL },\n    { &fnv_test_str[114], (Fnv64_t) 0xdbda6a26c33c909fULL },\n    { &fnv_test_str[115], (Fnv64_t) 0xd0b2feddbfe9be2dULL },\n    { &fnv_test_str[116], (Fnv64_t) 0x9c9eae3f5d037decULL },\n    { &fnv_test_str[117], (Fnv64_t) 0x252001ab0ceef804ULL },\n    { &fnv_test_str[118], (Fnv64_t) 0x4456a56f9e05cfefULL },\n    { &fnv_test_str[119], (Fnv64_t) 0x250b0ba983e0531dULL },\n    { &fnv_test_str[120], (Fnv64_t) 0x52b007213b27b33eULL },\n    { &fnv_test_str[121], (Fnv64_t) 0xcbf29ce484222325ULL },\n    { &fnv_test_str[122], (Fnv64_t) 0xaf63bd4c8601b7dfULL },\n    { &fnv_test_str[123], (Fnv64_t) 0x128599ccddae09f8ULL },\n    { &fnv_test_str[124], (Fnv64_t) 0x270e4f1caebaf068ULL },\n    { &fnv_test_str[125], (Fnv64_t) 0x01517d497446a395ULL },\n    { &fnv_test_str[126], (Fnv64_t) 0x9af5a29a89450b40ULL },\n    { &fnv_test_str[127], (Fnv64_t) 0xb502f6c063ba72e8ULL },\n    { &fnv_test_str[128], (Fnv64_t) 0xacf41561498ca7dfULL },\n    { &fnv_test_str[129], (Fnv64_t) 0x6be8c2423a351542ULL },\n    { &fnv_test_str[130], (Fnv64_t) 0xd04f1f6da96ce4a3ULL },\n    { &fnv_test_str[131], (Fnv64_t) 0x69eb9a8f282c7235ULL },\n    { &fnv_test_str[132], (Fnv64_t) 0x6a7e5a418f77cfc5ULL },\n    { &fnv_test_str[133], (Fnv64_t) 0xbcaf568ddc2ecba0ULL },\n    { &fnv_test_str[134], (Fnv64_t) 0xb03b5cc4c38f8b1fULL },\n    { &fnv_test_str[135], (Fnv64_t) 0xf89a9f51432db828ULL },\n    { &fnv_test_str[136], (Fnv64_t) 0x549e856be6103429ULL },\n    { &fnv_test_str[137], (Fnv64_t) 0x3cf50d224d29377aULL },\n    { &fnv_test_str[138], (Fnv64_t) 0xdb762df418c10c37ULL },\n    { &fnv_test_str[139], (Fnv64_t) 0xfeeb4226b0e9a6baULL },\n    { &fnv_test_str[140], (Fnv64_t) 0x7004a4cd9310c052ULL },\n    { &fnv_test_str[141], (Fnv64_t) 0xd1c727d7f5329276ULL },\n    { &fnv_test_str[142], (Fnv64_t) 0xbe313796596ce908ULL },\n    { &fnv_test_str[143], (Fnv64_t) 0x768f67ede090fcc5ULL },\n    { &fnv_test_str[144], (Fnv64_t) 0xa81563cc9db9bfc3ULL },\n    { &fnv_test_str[145], (Fnv64_t) 0x47194043c55197a8ULL },\n    { &fnv_test_str[146], (Fnv64_t) 0xc99d81864aebab02ULL },\n    { &fnv_test_str[147], (Fnv64_t) 0xcc1f161b235ea4a2ULL },\n    { &fnv_test_str[148], (Fnv64_t) 0xaadab0c420ecd434ULL },\n    { &fnv_test_str[149], (Fnv64_t) 0x6b3c034d6f44d740ULL },\n    { &fnv_test_str[150], (Fnv64_t) 0x73a45e850602cbc6ULL },\n    { &fnv_test_str[151], (Fnv64_t) 0x72360f04f0cd227bULL },\n    { &fnv_test_str[152], (Fnv64_t) 0xa9ca80be384a778fULL },\n    { &fnv_test_str[153], (Fnv64_t) 0xd4085e66906889e3ULL },\n    { &fnv_test_str[154], (Fnv64_t) 0x93aa8b2748efdbc8ULL },\n    { &fnv_test_str[155], (Fnv64_t) 0x6f8cd678407436a2ULL },\n    { &fnv_test_str[156], (Fnv64_t) 0xf39a43d4dc8be4c7ULL },\n    { &fnv_test_str[157], (Fnv64_t) 0xd7f5cec91125d245ULL },\n    { &fnv_test_str[158], (Fnv64_t) 0x691d7b73be18adc4ULL },\n    { &fnv_test_str[159], (Fnv64_t) 0xf4361e01caf6b691ULL },\n    { &fnv_test_str[160], (Fnv64_t) 0xde7d8264f64be089ULL },\n    { &fnv_test_str[161], (Fnv64_t) 0xa34ff43e5545c06fULL },\n    { &fnv_test_str[162], (Fnv64_t) 0x181f0b8e908a2bdeULL },\n    { &fnv_test_str[163], (Fnv64_t) 0x28a965b78ddbc071ULL },\n    { &fnv_test_str[164], (Fnv64_t) 0xead9cea0e3cc6ae5ULL },\n    { &fnv_test_str[165], (Fnv64_t) 0x0b6743153b43ebbaULL },\n    { &fnv_test_str[166], (Fnv64_t) 0xa7aa3f012c74528dULL },\n    { &fnv_test_str[167], (Fnv64_t) 0x2d5d8ad7f9dffeb7ULL },\n    { &fnv_test_str[168], (Fnv64_t) 0x00750fb6e19624eaULL },\n    { &fnv_test_str[169], (Fnv64_t) 0x01c125a4e6c76c82ULL },\n    { &fnv_test_str[170], (Fnv64_t) 0x3fde3afac0722f1fULL },\n    { &fnv_test_str[171], (Fnv64_t) 0xd7c3eaf4abaa379dULL },\n    { &fnv_test_str[172], (Fnv64_t) 0xd2217e1c923c9f3fULL },\n    { &fnv_test_str[173], (Fnv64_t) 0x82d0a2e3b725caf6ULL },\n    { &fnv_test_str[174], (Fnv64_t) 0x0a10bee8eeb72e4fULL },\n    { &fnv_test_str[175], (Fnv64_t) 0xc530e8723e72c6fdULL },\n    { &fnv_test_str[176], (Fnv64_t) 0xd8d34dcd2e7bad99ULL },\n    { &fnv_test_str[177], (Fnv64_t) 0xecf77466e9a2baf3ULL },\n    { &fnv_test_str[178], (Fnv64_t) 0xde3d2ddb043b9666ULL },\n    { &fnv_test_str[179], (Fnv64_t) 0xd1cc824e1a8157d8ULL },\n    { &fnv_test_str[180], (Fnv64_t) 0x7d5c68ecbc90512eULL },\n    { &fnv_test_str[181], (Fnv64_t) 0x2f7c691b1d7c76d8ULL },\n    { &fnv_test_str[182], (Fnv64_t) 0x5d88c2bad3a46bc8ULL },\n    { &fnv_test_str[183], (Fnv64_t) 0xdf107320276647a0ULL },\n    { &fnv_test_str[184], (Fnv64_t) 0x0f78f22e7e70e9bcULL },\n    { &fnv_test_str[185], (Fnv64_t) 0x8c67be5c80f67d04ULL },\n    { &fnv_test_str[186], (Fnv64_t) 0x07c1adfa4d019194ULL },\n    { &fnv_test_str[187], (Fnv64_t) 0xce1312420c5b1af4ULL },\n    { &fnv_test_str[188], (Fnv64_t) 0x043a41b2dc53ab24ULL },\n    { &fnv_test_str[189], (Fnv64_t) 0x0b038eebf7340860ULL },\n    { &fnv_test_str[190], (Fnv64_t) 0x1bcd837353fb69b0ULL },\n    { &fnv_test_str[191], (Fnv64_t) 0x46f992fc59eff180ULL },\n    { &fnv_test_str[192], (Fnv64_t) 0x497678ee29ae79c0ULL },\n    { &fnv_test_str[193], (Fnv64_t) 0xb10a62280ddd4450ULL },\n    { &fnv_test_str[194], (Fnv64_t) 0x35eb228db4d68140ULL },\n    { &fnv_test_str[195], (Fnv64_t) 0x8b350e86d9470870ULL },\n    { &fnv_test_str[196], (Fnv64_t) 0x4e1fbdb2812e9540ULL },\n    { &fnv_test_str[197], (Fnv64_t) 0x051e080df69a0600ULL },\n    { &fnv_test_str[198], (Fnv64_t) 0x45e1e8ae54dadb40ULL },\n    { &fnv_test_str[199], (Fnv64_t) 0x0000000000000000ULL },\n    { &fnv_test_str[200], (Fnv64_t) 0xcd73806290557064ULL },\n    { &fnv_test_str[201], (Fnv64_t) 0x2613a37bbe0317c8ULL },\n    { &fnv_test_str[202], (Fnv64_t) 0x1480e21fcf2ae5e4ULL },\n    { NULL, (Fnv64_t) 0 }\n};\n#else /* HAVE_64BIT_LONG_LONG */\nstruct fnv0_64_test_vector fnv0_64_vector[] = {\n    { &fnv_test_str[0], (Fnv64_t) {0x00000000UL, 0x00000000UL} },\n    { &fnv_test_str[1], (Fnv64_t) {0x00000061UL, 0x00000000UL} },\n    { &fnv_test_str[2], (Fnv64_t) {0x00000062UL, 0x00000000UL} },\n    { &fnv_test_str[3], (Fnv64_t) {0x00000063UL, 0x00000000UL} },\n    { &fnv_test_str[4], (Fnv64_t) {0x00000064UL, 0x00000000UL} },\n    { &fnv_test_str[5], (Fnv64_t) {0x00000065UL, 0x00000000UL} },\n    { &fnv_test_str[6], (Fnv64_t) {0x00000066UL, 0x00000000UL} },\n    { &fnv_test_str[7], (Fnv64_t) {0x0000ad3dUL, 0x00006600UL} },\n    { &fnv_test_str[8], (Fnv64_t) {0x01265ec8UL, 0x015a8f00UL} },\n    { &fnv_test_str[9], (Fnv64_t) {0xf4330dbaUL, 0x733fc501UL} },\n    { &fnv_test_str[10], (Fnv64_t) {0xf2c0536fUL, 0x08697c51UL} },\n    { &fnv_test_str[11], (Fnv64_t) {0x7ccdc5efUL, 0x0b91ae3fUL} },\n    { &fnv_test_str[12], (Fnv64_t) {0x00000000UL, 0x00000000UL} },\n    { &fnv_test_str[13], (Fnv64_t) {0x0000a4d3UL, 0x00006100UL} },\n    { &fnv_test_str[14], (Fnv64_t) {0x0000a686UL, 0x00006200UL} },\n    { &fnv_test_str[15], (Fnv64_t) {0x0000a839UL, 0x00006300UL} },\n    { &fnv_test_str[16], (Fnv64_t) {0x0000a9ecUL, 0x00006400UL} },\n    { &fnv_test_str[17], (Fnv64_t) {0x0000ab9fUL, 0x00006500UL} },\n    { &fnv_test_str[18], (Fnv64_t) {0x0000ad52UL, 0x00006600UL} },\n    { &fnv_test_str[19], (Fnv64_t) {0x01265ea7UL, 0x015a8f00UL} },\n    { &fnv_test_str[20], (Fnv64_t) {0xf4330dd8UL, 0x733fc501UL} },\n    { &fnv_test_str[21], (Fnv64_t) {0xf2c0530eUL, 0x08697c51UL} },\n    { &fnv_test_str[22], (Fnv64_t) {0x7ccdc59dUL, 0x0b91ae3fUL} },\n    { &fnv_test_str[23], (Fnv64_t) {0x11a7551dUL, 0x765104e1UL} },\n    { &fnv_test_str[24], (Fnv64_t) {0x0000a851UL, 0x00006300UL} },\n    { &fnv_test_str[25], (Fnv64_t) {0x011e01ccUL, 0x01508a00UL} },\n    { &fnv_test_str[26], (Fnv64_t) {0xe5fd0dcaUL, 0x59dc4a01UL} },\n    { &fnv_test_str[27], (Fnv64_t) {0xccfe6e59UL, 0xae5f8b39UL} },\n    { &fnv_test_str[28], (Fnv64_t) {0x54558154UL, 0x4ac7ec37UL} },\n    { &fnv_test_str[29], (Fnv64_t) {0x4d4ac19cUL, 0x6737b604UL} },\n    { &fnv_test_str[30], (Fnv64_t) {0x5606fc63UL, 0xae6be54fUL} },\n    { &fnv_test_str[31], (Fnv64_t) {0x2ddedc58UL, 0x685308cfUL} },\n    { &fnv_test_str[32], (Fnv64_t) {0xf1b069fbUL, 0x23f4500aUL} },\n    { &fnv_test_str[33], (Fnv64_t) {0xaec415a1UL, 0xc88dfd98UL} },\n    { &fnv_test_str[34], (Fnv64_t) {0xf730c0fbUL, 0x8d5b8b70UL} },\n    { &fnv_test_str[35], (Fnv64_t) {0x07d7eae4UL, 0x634eebf4UL} },\n    { &fnv_test_str[36], (Fnv64_t) {0x53e4211eUL, 0x9705d3a9UL} },\n    { &fnv_test_str[37], (Fnv64_t) {0x8ca4459fUL, 0x8307c6b9UL} },\n    { &fnv_test_str[38], (Fnv64_t) {0xfb224d0cUL, 0x4a7c4c49UL} },\n    { &fnv_test_str[39], (Fnv64_t) {0xbb48eb6eUL, 0xb382adb5UL} },\n    { &fnv_test_str[40], (Fnv64_t) {0x011e01a3UL, 0x01508a00UL} },\n    { &fnv_test_str[41], (Fnv64_t) {0xe5fd0da4UL, 0x59dc4a01UL} },\n    { &fnv_test_str[42], (Fnv64_t) {0xccfe6e3eUL, 0xae5f8b39UL} },\n    { &fnv_test_str[43], (Fnv64_t) {0x5455813bUL, 0x4ac7ec37UL} },\n    { &fnv_test_str[44], (Fnv64_t) {0x4d4ac1bcUL, 0x6737b604UL} },\n    { &fnv_test_str[45], (Fnv64_t) {0x5606fc14UL, 0xae6be54fUL} },\n    { &fnv_test_str[46], (Fnv64_t) {0x2ddedc39UL, 0x685308cfUL} },\n    { &fnv_test_str[47], (Fnv64_t) {0xf1b06988UL, 0x23f4500aUL} },\n    { &fnv_test_str[48], (Fnv64_t) {0xaec41581UL, 0xc88dfd98UL} },\n    { &fnv_test_str[49], (Fnv64_t) {0xf730c093UL, 0x8d5b8b70UL} },\n    { &fnv_test_str[50], (Fnv64_t) {0x07d7ea81UL, 0x634eebf4UL} },\n    { &fnv_test_str[51], (Fnv64_t) {0x53e4216cUL, 0x9705d3a9UL} },\n    { &fnv_test_str[52], (Fnv64_t) {0x8ca445faUL, 0x8307c6b9UL} },\n    { &fnv_test_str[53], (Fnv64_t) {0xfb224d2dUL, 0x4a7c4c49UL} },\n    { &fnv_test_str[54], (Fnv64_t) {0xbb48eb64UL, 0xb382adb5UL} },\n    { &fnv_test_str[55], (Fnv64_t) {0x3ce80beaUL, 0x4ff899cdUL} },\n    { &fnv_test_str[56], (Fnv64_t) {0x0000a84cUL, 0x00006300UL} },\n    { &fnv_test_str[57], (Fnv64_t) {0x011df956UL, 0x01508500UL} },\n    { &fnv_test_str[58], (Fnv64_t) {0xe5eead46UL, 0x59cb5501UL} },\n    { &fnv_test_str[59], (Fnv64_t) {0xb4906d81UL, 0x832eb839UL} },\n    { &fnv_test_str[60], (Fnv64_t) {0xd16a1213UL, 0x78d08b0dUL} },\n    { &fnv_test_str[61], (Fnv64_t) {0xd73cb628UL, 0xb46e5b7aUL} },\n    { &fnv_test_str[62], (Fnv64_t) {0xbc298596UL, 0xd43b99bbUL} },\n    { &fnv_test_str[63], (Fnv64_t) {0xba8dfd86UL, 0xcacbd000UL} },\n    { &fnv_test_str[64], (Fnv64_t) {0xff45ca92UL, 0x264ff73cUL} },\n    { &fnv_test_str[65], (Fnv64_t) {0xc3973661UL, 0x5fabaea5UL} },\n    { &fnv_test_str[66], (Fnv64_t) {0x59f166bbUL, 0x27f024abUL} },\n    { &fnv_test_str[67], (Fnv64_t) {0xd5318fa4UL, 0xce750a29UL} },\n    { &fnv_test_str[68], (Fnv64_t) {0x433713d5UL, 0x026fe915UL} },\n    { &fnv_test_str[69], (Fnv64_t) {0x3696b2e5UL, 0x5b3ce421UL} },\n    { &fnv_test_str[70], (Fnv64_t) {0x011df924UL, 0x01508500UL} },\n    { &fnv_test_str[71], (Fnv64_t) {0xe5eead22UL, 0x59cb5501UL} },\n    { &fnv_test_str[72], (Fnv64_t) {0xb4906df2UL, 0x832eb839UL} },\n    { &fnv_test_str[73], (Fnv64_t) {0xd16a1233UL, 0x78d08b0dUL} },\n    { &fnv_test_str[74], (Fnv64_t) {0xd73cb649UL, 0xb46e5b7aUL} },\n    { &fnv_test_str[75], (Fnv64_t) {0xbc2985f8UL, 0xd43b99bbUL} },\n    { &fnv_test_str[76], (Fnv64_t) {0xba8dfde2UL, 0xcacbd000UL} },\n    { &fnv_test_str[77], (Fnv64_t) {0xff45cab2UL, 0x264ff73cUL} },\n    { &fnv_test_str[78], (Fnv64_t) {0xc3973616UL, 0x5fabaea5UL} },\n    { &fnv_test_str[79], (Fnv64_t) {0x59f166d3UL, 0x27f024abUL} },\n    { &fnv_test_str[80], (Fnv64_t) {0xd5318fc1UL, 0xce750a29UL} },\n    { &fnv_test_str[81], (Fnv64_t) {0x433713acUL, 0x026fe915UL} },\n    { &fnv_test_str[82], (Fnv64_t) {0x3696b2efUL, 0x5b3ce421UL} },\n    { &fnv_test_str[83], (Fnv64_t) {0xc211fb1fUL, 0x9f2a896fUL} },\n    { &fnv_test_str[84], (Fnv64_t) {0x0000b0d1UL, 0x00006800UL} },\n    { &fnv_test_str[85], (Fnv64_t) {0x012c7323UL, 0x01618900UL} },\n    { &fnv_test_str[86], (Fnv64_t) {0xbc7d03c8UL, 0x3fa86e63UL} },\n    { &fnv_test_str[87], (Fnv64_t) {0x486d6cd8UL, 0xa8375b79UL} },\n    { &fnv_test_str[88], (Fnv64_t) {0xe316ac54UL, 0xa0d18504UL} },\n    { &fnv_test_str[89], (Fnv64_t) {0x04e7fe54UL, 0x08a97b00UL} },\n    { &fnv_test_str[90], (Fnv64_t) {0xe316ac57UL, 0xa0d18504UL} },\n    { &fnv_test_str[91], (Fnv64_t) {0x09cffda9UL, 0x1152f600UL} },\n    { &fnv_test_str[92], (Fnv64_t) {0xe316ac56UL, 0xa0d18504UL} },\n    { &fnv_test_str[93], (Fnv64_t) {0x0eb7fcfeUL, 0x19fc7100UL} },\n    { &fnv_test_str[94], (Fnv64_t) {0xe316ac51UL, 0xa0d18504UL} },\n    { &fnv_test_str[95], (Fnv64_t) {0x139ffa53UL, 0x22a5ec00UL} },\n    { &fnv_test_str[96], (Fnv64_t) {0x39779a33UL, 0x29bed001UL} },\n    { &fnv_test_str[97], (Fnv64_t) {0x4e3c19f5UL, 0x4dbc8101UL} },\n    { &fnv_test_str[98], (Fnv64_t) {0x39779a3dUL, 0x29bed001UL} },\n    { &fnv_test_str[99], (Fnv64_t) {0x6b9f7573UL, 0x81a72b01UL} },\n    { &fnv_test_str[100], (Fnv64_t) {0x39779a23UL, 0x29bed001UL} },\n    { &fnv_test_str[101], (Fnv64_t) {0x9cbbce45UL, 0xd8541101UL} },\n    { &fnv_test_str[102], (Fnv64_t) {0x8621d657UL, 0xf548616bUL} },\n    { &fnv_test_str[103], (Fnv64_t) {0xeb7f35d5UL, 0xebd3e0b4UL} },\n    { &fnv_test_str[104], (Fnv64_t) {0x8621d654UL, 0xf548616bUL} },\n    { &fnv_test_str[105], (Fnv64_t) {0xeb7f30bcUL, 0xebd3ddb4UL} },\n    { &fnv_test_str[106], (Fnv64_t) {0x8621d655UL, 0xf548616bUL} },\n    { &fnv_test_str[107], (Fnv64_t) {0xeb7f326fUL, 0xebd3deb4UL} },\n    { &fnv_test_str[108], (Fnv64_t) {0x40ab0968UL, 0x581cb603UL} },\n    { &fnv_test_str[109], (Fnv64_t) {0xe2a0fbb8UL, 0x63d2af86UL} },\n    { &fnv_test_str[110], (Fnv64_t) {0x40ab0b37UL, 0x581cb703UL} },\n    { &fnv_test_str[111], (Fnv64_t) {0xe2a40e75UL, 0x63d63186UL} },\n    { &fnv_test_str[112], (Fnv64_t) {0x40ab212eUL, 0x581cc403UL} },\n    { &fnv_test_str[113], (Fnv64_t) {0xe2c9612aUL, 0x64023f86UL} },\n    { &fnv_test_str[114], (Fnv64_t) {0xc33c909fUL, 0xdbda6a26UL} },\n    { &fnv_test_str[115], (Fnv64_t) {0xbfe9be2dUL, 0xd0b2feddUL} },\n    { &fnv_test_str[116], (Fnv64_t) {0x5d037decUL, 0x9c9eae3fUL} },\n    { &fnv_test_str[117], (Fnv64_t) {0x0ceef804UL, 0x252001abUL} },\n    { &fnv_test_str[118], (Fnv64_t) {0x9e05cfefUL, 0x4456a56fUL} },\n    { &fnv_test_str[119], (Fnv64_t) {0x83e0531dUL, 0x250b0ba9UL} },\n    { &fnv_test_str[120], (Fnv64_t) {0x3b27b33eUL, 0x52b00721UL} },\n    { &fnv_test_str[121], (Fnv64_t) {0x84222325UL, 0xcbf29ce4UL} },\n    { &fnv_test_str[122], (Fnv64_t) {0x8601b7dfUL, 0xaf63bd4cUL} },\n    { &fnv_test_str[123], (Fnv64_t) {0xddae09f8UL, 0x128599ccUL} },\n    { &fnv_test_str[124], (Fnv64_t) {0xaebaf068UL, 0x270e4f1cUL} },\n    { &fnv_test_str[125], (Fnv64_t) {0x7446a395UL, 0x01517d49UL} },\n    { &fnv_test_str[126], (Fnv64_t) {0x89450b40UL, 0x9af5a29aUL} },\n    { &fnv_test_str[127], (Fnv64_t) {0x63ba72e8UL, 0xb502f6c0UL} },\n    { &fnv_test_str[128], (Fnv64_t) {0x498ca7dfUL, 0xacf41561UL} },\n    { &fnv_test_str[129], (Fnv64_t) {0x3a351542UL, 0x6be8c242UL} },\n    { &fnv_test_str[130], (Fnv64_t) {0xa96ce4a3UL, 0xd04f1f6dUL} },\n    { &fnv_test_str[131], (Fnv64_t) {0x282c7235UL, 0x69eb9a8fUL} },\n    { &fnv_test_str[132], (Fnv64_t) {0x8f77cfc5UL, 0x6a7e5a41UL} },\n    { &fnv_test_str[133], (Fnv64_t) {0xdc2ecba0UL, 0xbcaf568dUL} },\n    { &fnv_test_str[134], (Fnv64_t) {0xc38f8b1fUL, 0xb03b5cc4UL} },\n    { &fnv_test_str[135], (Fnv64_t) {0x432db828UL, 0xf89a9f51UL} },\n    { &fnv_test_str[136], (Fnv64_t) {0xe6103429UL, 0x549e856bUL} },\n    { &fnv_test_str[137], (Fnv64_t) {0x4d29377aUL, 0x3cf50d22UL} },\n    { &fnv_test_str[138], (Fnv64_t) {0x18c10c37UL, 0xdb762df4UL} },\n    { &fnv_test_str[139], (Fnv64_t) {0xb0e9a6baUL, 0xfeeb4226UL} },\n    { &fnv_test_str[140], (Fnv64_t) {0x9310c052UL, 0x7004a4cdUL} },\n    { &fnv_test_str[141], (Fnv64_t) {0xf5329276UL, 0xd1c727d7UL} },\n    { &fnv_test_str[142], (Fnv64_t) {0x596ce908UL, 0xbe313796UL} },\n    { &fnv_test_str[143], (Fnv64_t) {0xe090fcc5UL, 0x768f67edUL} },\n    { &fnv_test_str[144], (Fnv64_t) {0x9db9bfc3UL, 0xa81563ccUL} },\n    { &fnv_test_str[145], (Fnv64_t) {0xc55197a8UL, 0x47194043UL} },\n    { &fnv_test_str[146], (Fnv64_t) {0x4aebab02UL, 0xc99d8186UL} },\n    { &fnv_test_str[147], (Fnv64_t) {0x235ea4a2UL, 0xcc1f161bUL} },\n    { &fnv_test_str[148], (Fnv64_t) {0x20ecd434UL, 0xaadab0c4UL} },\n    { &fnv_test_str[149], (Fnv64_t) {0x6f44d740UL, 0x6b3c034dUL} },\n    { &fnv_test_str[150], (Fnv64_t) {0x0602cbc6UL, 0x73a45e85UL} },\n    { &fnv_test_str[151], (Fnv64_t) {0xf0cd227bUL, 0x72360f04UL} },\n    { &fnv_test_str[152], (Fnv64_t) {0x384a778fUL, 0xa9ca80beUL} },\n    { &fnv_test_str[153], (Fnv64_t) {0x906889e3UL, 0xd4085e66UL} },\n    { &fnv_test_str[154], (Fnv64_t) {0x48efdbc8UL, 0x93aa8b27UL} },\n    { &fnv_test_str[155], (Fnv64_t) {0x407436a2UL, 0x6f8cd678UL} },\n    { &fnv_test_str[156], (Fnv64_t) {0xdc8be4c7UL, 0xf39a43d4UL} },\n    { &fnv_test_str[157], (Fnv64_t) {0x1125d245UL, 0xd7f5cec9UL} },\n    { &fnv_test_str[158], (Fnv64_t) {0xbe18adc4UL, 0x691d7b73UL} },\n    { &fnv_test_str[159], (Fnv64_t) {0xcaf6b691UL, 0xf4361e01UL} },\n    { &fnv_test_str[160], (Fnv64_t) {0xf64be089UL, 0xde7d8264UL} },\n    { &fnv_test_str[161], (Fnv64_t) {0x5545c06fUL, 0xa34ff43eUL} },\n    { &fnv_test_str[162], (Fnv64_t) {0x908a2bdeUL, 0x181f0b8eUL} },\n    { &fnv_test_str[163], (Fnv64_t) {0x8ddbc071UL, 0x28a965b7UL} },\n    { &fnv_test_str[164], (Fnv64_t) {0xe3cc6ae5UL, 0xead9cea0UL} },\n    { &fnv_test_str[165], (Fnv64_t) {0x3b43ebbaUL, 0x0b674315UL} },\n    { &fnv_test_str[166], (Fnv64_t) {0x2c74528dUL, 0xa7aa3f01UL} },\n    { &fnv_test_str[167], (Fnv64_t) {0xf9dffeb7UL, 0x2d5d8ad7UL} },\n    { &fnv_test_str[168], (Fnv64_t) {0xe19624eaUL, 0x00750fb6UL} },\n    { &fnv_test_str[169], (Fnv64_t) {0xe6c76c82UL, 0x01c125a4UL} },\n    { &fnv_test_str[170], (Fnv64_t) {0xc0722f1fUL, 0x3fde3afaUL} },\n    { &fnv_test_str[171], (Fnv64_t) {0xabaa379dUL, 0xd7c3eaf4UL} },\n    { &fnv_test_str[172], (Fnv64_t) {0x923c9f3fUL, 0xd2217e1cUL} },\n    { &fnv_test_str[173], (Fnv64_t) {0xb725caf6UL, 0x82d0a2e3UL} },\n    { &fnv_test_str[174], (Fnv64_t) {0xeeb72e4fUL, 0x0a10bee8UL} },\n    { &fnv_test_str[175], (Fnv64_t) {0x3e72c6fdUL, 0xc530e872UL} },\n    { &fnv_test_str[176], (Fnv64_t) {0x2e7bad99UL, 0xd8d34dcdUL} },\n    { &fnv_test_str[177], (Fnv64_t) {0xe9a2baf3UL, 0xecf77466UL} },\n    { &fnv_test_str[178], (Fnv64_t) {0x043b9666UL, 0xde3d2ddbUL} },\n    { &fnv_test_str[179], (Fnv64_t) {0x1a8157d8UL, 0xd1cc824eUL} },\n    { &fnv_test_str[180], (Fnv64_t) {0xbc90512eUL, 0x7d5c68ecUL} },\n    { &fnv_test_str[181], (Fnv64_t) {0x1d7c76d8UL, 0x2f7c691bUL} },\n    { &fnv_test_str[182], (Fnv64_t) {0xd3a46bc8UL, 0x5d88c2baUL} },\n    { &fnv_test_str[183], (Fnv64_t) {0x276647a0UL, 0xdf107320UL} },\n    { &fnv_test_str[184], (Fnv64_t) {0x7e70e9bcUL, 0x0f78f22eUL} },\n    { &fnv_test_str[185], (Fnv64_t) {0x80f67d04UL, 0x8c67be5cUL} },\n    { &fnv_test_str[186], (Fnv64_t) {0x4d019194UL, 0x07c1adfaUL} },\n    { &fnv_test_str[187], (Fnv64_t) {0x0c5b1af4UL, 0xce131242UL} },\n    { &fnv_test_str[188], (Fnv64_t) {0xdc53ab24UL, 0x043a41b2UL} },\n    { &fnv_test_str[189], (Fnv64_t) {0xf7340860UL, 0x0b038eebUL} },\n    { &fnv_test_str[190], (Fnv64_t) {0x53fb69b0UL, 0x1bcd8373UL} },\n    { &fnv_test_str[191], (Fnv64_t) {0x59eff180UL, 0x46f992fcUL} },\n    { &fnv_test_str[192], (Fnv64_t) {0x29ae79c0UL, 0x497678eeUL} },\n    { &fnv_test_str[193], (Fnv64_t) {0x0ddd4450UL, 0xb10a6228UL} },\n    { &fnv_test_str[194], (Fnv64_t) {0xb4d68140UL, 0x35eb228dUL} },\n    { &fnv_test_str[195], (Fnv64_t) {0xd9470870UL, 0x8b350e86UL} },\n    { &fnv_test_str[196], (Fnv64_t) {0x812e9540UL, 0x4e1fbdb2UL} },\n    { &fnv_test_str[197], (Fnv64_t) {0xf69a0600UL, 0x051e080dUL} },\n    { &fnv_test_str[198], (Fnv64_t) {0x54dadb40UL, 0x45e1e8aeUL} },\n    { &fnv_test_str[199], (Fnv64_t) {0x00000000UL, 0x00000000UL} },\n    { &fnv_test_str[200], (Fnv64_t) {0x90557064UL, 0xcd738062UL} },\n    { &fnv_test_str[201], (Fnv64_t) {0xbe0317c8UL, 0x2613a37bUL} },\n    { &fnv_test_str[202], (Fnv64_t) {0xcf2ae5e4UL, 0x1480e21fUL} },\n    { NULL, (Fnv64_t) {0,0} }\n};\n#endif /* HAVE_64BIT_LONG_LONG */\n\n/* FNV-1 64 bit test vectors */\n#if defined(HAVE_64BIT_LONG_LONG)\nstruct fnv1_64_test_vector fnv1_64_vector[] = {\n    { &fnv_test_str[0], (Fnv64_t) 0xcbf29ce484222325ULL },\n    { &fnv_test_str[1], (Fnv64_t) 0xaf63bd4c8601b7beULL },\n    { &fnv_test_str[2], (Fnv64_t) 0xaf63bd4c8601b7bdULL },\n    { &fnv_test_str[3], (Fnv64_t) 0xaf63bd4c8601b7bcULL },\n    { &fnv_test_str[4], (Fnv64_t) 0xaf63bd4c8601b7bbULL },\n    { &fnv_test_str[5], (Fnv64_t) 0xaf63bd4c8601b7baULL },\n    { &fnv_test_str[6], (Fnv64_t) 0xaf63bd4c8601b7b9ULL },\n    { &fnv_test_str[7], (Fnv64_t) 0x08326207b4eb2f34ULL },\n    { &fnv_test_str[8], (Fnv64_t) 0xd8cbc7186ba13533ULL },\n    { &fnv_test_str[9], (Fnv64_t) 0x0378817ee2ed65cbULL },\n    { &fnv_test_str[10], (Fnv64_t) 0xd329d59b9963f790ULL },\n    { &fnv_test_str[11], (Fnv64_t) 0x340d8765a4dda9c2ULL },\n    { &fnv_test_str[12], (Fnv64_t) 0xaf63bd4c8601b7dfULL },\n    { &fnv_test_str[13], (Fnv64_t) 0x08326707b4eb37daULL },\n    { &fnv_test_str[14], (Fnv64_t) 0x08326607b4eb3627ULL },\n    { &fnv_test_str[15], (Fnv64_t) 0x08326507b4eb3474ULL },\n    { &fnv_test_str[16], (Fnv64_t) 0x08326407b4eb32c1ULL },\n    { &fnv_test_str[17], (Fnv64_t) 0x08326307b4eb310eULL },\n    { &fnv_test_str[18], (Fnv64_t) 0x08326207b4eb2f5bULL },\n    { &fnv_test_str[19], (Fnv64_t) 0xd8cbc7186ba1355cULL },\n    { &fnv_test_str[20], (Fnv64_t) 0x0378817ee2ed65a9ULL },\n    { &fnv_test_str[21], (Fnv64_t) 0xd329d59b9963f7f1ULL },\n    { &fnv_test_str[22], (Fnv64_t) 0x340d8765a4dda9b0ULL },\n    { &fnv_test_str[23], (Fnv64_t) 0x50a6d3b724a774a6ULL },\n    { &fnv_test_str[24], (Fnv64_t) 0x08326507b4eb341cULL },\n    { &fnv_test_str[25], (Fnv64_t) 0xd8d5c8186ba98bfbULL },\n    { &fnv_test_str[26], (Fnv64_t) 0x1ccefc7ef118dbefULL },\n    { &fnv_test_str[27], (Fnv64_t) 0x0c92fab3ad3db77aULL },\n    { &fnv_test_str[28], (Fnv64_t) 0x9b77794f5fdec421ULL },\n    { &fnv_test_str[29], (Fnv64_t) 0x0ac742dfe7874433ULL },\n    { &fnv_test_str[30], (Fnv64_t) 0xd7dad5766ad8e2deULL },\n    { &fnv_test_str[31], (Fnv64_t) 0xa1bb96378e897f5bULL },\n    { &fnv_test_str[32], (Fnv64_t) 0x5b3f9b6733a367d2ULL },\n    { &fnv_test_str[33], (Fnv64_t) 0xb07ce25cbea969f6ULL },\n    { &fnv_test_str[34], (Fnv64_t) 0x8d9e9997f9df0d6aULL },\n    { &fnv_test_str[35], (Fnv64_t) 0x838c673d9603cb7bULL },\n    { &fnv_test_str[36], (Fnv64_t) 0x8b5ee8a5e872c273ULL },\n    { &fnv_test_str[37], (Fnv64_t) 0x4507c4e9fb00690cULL },\n    { &fnv_test_str[38], (Fnv64_t) 0x4c9ca59581b27f45ULL },\n    { &fnv_test_str[39], (Fnv64_t) 0xe0aca20b624e4235ULL },\n    { &fnv_test_str[40], (Fnv64_t) 0xd8d5c8186ba98b94ULL },\n    { &fnv_test_str[41], (Fnv64_t) 0x1ccefc7ef118db81ULL },\n    { &fnv_test_str[42], (Fnv64_t) 0x0c92fab3ad3db71dULL },\n    { &fnv_test_str[43], (Fnv64_t) 0x9b77794f5fdec44eULL },\n    { &fnv_test_str[44], (Fnv64_t) 0x0ac742dfe7874413ULL },\n    { &fnv_test_str[45], (Fnv64_t) 0xd7dad5766ad8e2a9ULL },\n    { &fnv_test_str[46], (Fnv64_t) 0xa1bb96378e897f3aULL },\n    { &fnv_test_str[47], (Fnv64_t) 0x5b3f9b6733a367a1ULL },\n    { &fnv_test_str[48], (Fnv64_t) 0xb07ce25cbea969d6ULL },\n    { &fnv_test_str[49], (Fnv64_t) 0x8d9e9997f9df0d02ULL },\n    { &fnv_test_str[50], (Fnv64_t) 0x838c673d9603cb1eULL },\n    { &fnv_test_str[51], (Fnv64_t) 0x8b5ee8a5e872c201ULL },\n    { &fnv_test_str[52], (Fnv64_t) 0x4507c4e9fb006969ULL },\n    { &fnv_test_str[53], (Fnv64_t) 0x4c9ca59581b27f64ULL },\n    { &fnv_test_str[54], (Fnv64_t) 0xe0aca20b624e423fULL },\n    { &fnv_test_str[55], (Fnv64_t) 0x13998e580afa800fULL },\n    { &fnv_test_str[56], (Fnv64_t) 0x08326507b4eb3401ULL },\n    { &fnv_test_str[57], (Fnv64_t) 0xd8d5ad186ba95dc1ULL },\n    { &fnv_test_str[58], (Fnv64_t) 0x1c72e17ef0ca4e97ULL },\n    { &fnv_test_str[59], (Fnv64_t) 0x2183c1b327c38ae6ULL },\n    { &fnv_test_str[60], (Fnv64_t) 0xb66d096c914504f2ULL },\n    { &fnv_test_str[61], (Fnv64_t) 0x404bf57ad8476757ULL },\n    { &fnv_test_str[62], (Fnv64_t) 0x887976bd815498bbULL },\n    { &fnv_test_str[63], (Fnv64_t) 0x3afd7f02c2bf85a5ULL },\n    { &fnv_test_str[64], (Fnv64_t) 0xfc4476b0eb70177fULL },\n    { &fnv_test_str[65], (Fnv64_t) 0x186d2da00f77ecbaULL },\n    { &fnv_test_str[66], (Fnv64_t) 0xf97140fa48c74066ULL },\n    { &fnv_test_str[67], (Fnv64_t) 0xa2b1cf49aa926d37ULL },\n    { &fnv_test_str[68], (Fnv64_t) 0x0690712cd6cf940cULL },\n    { &fnv_test_str[69], (Fnv64_t) 0xf7045b3102b8906eULL },\n    { &fnv_test_str[70], (Fnv64_t) 0xd8d5ad186ba95db3ULL },\n    { &fnv_test_str[71], (Fnv64_t) 0x1c72e17ef0ca4ef3ULL },\n    { &fnv_test_str[72], (Fnv64_t) 0x2183c1b327c38a95ULL },\n    { &fnv_test_str[73], (Fnv64_t) 0xb66d096c914504d2ULL },\n    { &fnv_test_str[74], (Fnv64_t) 0x404bf57ad8476736ULL },\n    { &fnv_test_str[75], (Fnv64_t) 0x887976bd815498d5ULL },\n    { &fnv_test_str[76], (Fnv64_t) 0x3afd7f02c2bf85c1ULL },\n    { &fnv_test_str[77], (Fnv64_t) 0xfc4476b0eb70175fULL },\n    { &fnv_test_str[78], (Fnv64_t) 0x186d2da00f77eccdULL },\n    { &fnv_test_str[79], (Fnv64_t) 0xf97140fa48c7400eULL },\n    { &fnv_test_str[80], (Fnv64_t) 0xa2b1cf49aa926d52ULL },\n    { &fnv_test_str[81], (Fnv64_t) 0x0690712cd6cf9475ULL },\n    { &fnv_test_str[82], (Fnv64_t) 0xf7045b3102b89064ULL },\n    { &fnv_test_str[83], (Fnv64_t) 0x74f762479f9d6aeaULL },\n    { &fnv_test_str[84], (Fnv64_t) 0x08326007b4eb2b9cULL },\n    { &fnv_test_str[85], (Fnv64_t) 0xd8c4c9186b9b1a14ULL },\n    { &fnv_test_str[86], (Fnv64_t) 0x7b495389bdbdd4c7ULL },\n    { &fnv_test_str[87], (Fnv64_t) 0x3b6dba0d69908e25ULL },\n    { &fnv_test_str[88], (Fnv64_t) 0xd6b2b17bf4b71261ULL },\n    { &fnv_test_str[89], (Fnv64_t) 0x447bfb7f98e615b5ULL },\n    { &fnv_test_str[90], (Fnv64_t) 0xd6b2b17bf4b71262ULL },\n    { &fnv_test_str[91], (Fnv64_t) 0x3bd2807f93fe1660ULL },\n    { &fnv_test_str[92], (Fnv64_t) 0xd6b2b17bf4b71263ULL },\n    { &fnv_test_str[93], (Fnv64_t) 0x3329057f8f16170bULL },\n    { &fnv_test_str[94], (Fnv64_t) 0xd6b2b17bf4b71264ULL },\n    { &fnv_test_str[95], (Fnv64_t) 0x2a7f8a7f8a2e19b6ULL },\n    { &fnv_test_str[96], (Fnv64_t) 0x23d3767e64b2f98aULL },\n    { &fnv_test_str[97], (Fnv64_t) 0xff768d7e4f9d86a4ULL },\n    { &fnv_test_str[98], (Fnv64_t) 0x23d3767e64b2f984ULL },\n    { &fnv_test_str[99], (Fnv64_t) 0xccd1837e334e4aa6ULL },\n    { &fnv_test_str[100], (Fnv64_t) 0x23d3767e64b2f99aULL },\n    { &fnv_test_str[101], (Fnv64_t) 0x7691fd7e028f6754ULL },\n    { &fnv_test_str[102], (Fnv64_t) 0x34ad3b1041204318ULL },\n    { &fnv_test_str[103], (Fnv64_t) 0xa29e749ea9d201c8ULL },\n    { &fnv_test_str[104], (Fnv64_t) 0x34ad3b104120431bULL },\n    { &fnv_test_str[105], (Fnv64_t) 0xa29e779ea9d206e1ULL },\n    { &fnv_test_str[106], (Fnv64_t) 0x34ad3b104120431aULL },\n    { &fnv_test_str[107], (Fnv64_t) 0xa29e769ea9d2052eULL },\n    { &fnv_test_str[108], (Fnv64_t) 0x02a17ebca4aa3497ULL },\n    { &fnv_test_str[109], (Fnv64_t) 0x229ef18bcd375c95ULL },\n    { &fnv_test_str[110], (Fnv64_t) 0x02a17dbca4aa32c8ULL },\n    { &fnv_test_str[111], (Fnv64_t) 0x229b6f8bcd3449d8ULL },\n    { &fnv_test_str[112], (Fnv64_t) 0x02a184bca4aa3ed5ULL },\n    { &fnv_test_str[113], (Fnv64_t) 0x22b3618bcd48c3efULL },\n    { &fnv_test_str[114], (Fnv64_t) 0x5c2c346706186f36ULL },\n    { &fnv_test_str[115], (Fnv64_t) 0xb78c410f5b84f8c2ULL },\n    { &fnv_test_str[116], (Fnv64_t) 0xed9478212b267395ULL },\n    { &fnv_test_str[117], (Fnv64_t) 0xd9bbb55c5256662fULL },\n    { &fnv_test_str[118], (Fnv64_t) 0x8c54f0203249438aULL },\n    { &fnv_test_str[119], (Fnv64_t) 0xbd9790b5727dc37eULL },\n    { &fnv_test_str[120], (Fnv64_t) 0xa64e5f36c9e2b0e3ULL },\n    { &fnv_test_str[121], (Fnv64_t) 0x8fd0680da3088a04ULL },\n    { &fnv_test_str[122], (Fnv64_t) 0x67aad32c078284ccULL },\n    { &fnv_test_str[123], (Fnv64_t) 0xb37d55d81c57b331ULL },\n    { &fnv_test_str[124], (Fnv64_t) 0x55ac0f3829057c43ULL },\n    { &fnv_test_str[125], (Fnv64_t) 0xcb27f4b8e1b6cc20ULL },\n    { &fnv_test_str[126], (Fnv64_t) 0x26caf88bcbef2d19ULL },\n    { &fnv_test_str[127], (Fnv64_t) 0x8e6e063b97e61b8fULL },\n    { &fnv_test_str[128], (Fnv64_t) 0xb42750f7f3b7c37eULL },\n    { &fnv_test_str[129], (Fnv64_t) 0xf3c6ba64cf7ca99bULL },\n    { &fnv_test_str[130], (Fnv64_t) 0xebfb69b427ea80feULL },\n    { &fnv_test_str[131], (Fnv64_t) 0x39b50c3ed970f46cULL },\n    { &fnv_test_str[132], (Fnv64_t) 0x5b9b177aa3eb3e8aULL },\n    { &fnv_test_str[133], (Fnv64_t) 0x6510063ecf4ec903ULL },\n    { &fnv_test_str[134], (Fnv64_t) 0x2b3bbd2c00797c7aULL },\n    { &fnv_test_str[135], (Fnv64_t) 0xf1d6204ff5cb4aa7ULL },\n    { &fnv_test_str[136], (Fnv64_t) 0x4836e27ccf099f38ULL },\n    { &fnv_test_str[137], (Fnv64_t) 0x82efbb0dd073b44dULL },\n    { &fnv_test_str[138], (Fnv64_t) 0x4a80c282ffd7d4c6ULL },\n    { &fnv_test_str[139], (Fnv64_t) 0x305d1a9c9ee43bdfULL },\n    { &fnv_test_str[140], (Fnv64_t) 0x15c366948ffc6997ULL },\n    { &fnv_test_str[141], (Fnv64_t) 0x80153ae218916e7bULL },\n    { &fnv_test_str[142], (Fnv64_t) 0xfa23e2bdf9e2a9e1ULL },\n    { &fnv_test_str[143], (Fnv64_t) 0xd47e8d8a2333c6deULL },\n    { &fnv_test_str[144], (Fnv64_t) 0x7e128095f688b056ULL },\n    { &fnv_test_str[145], (Fnv64_t) 0x2f5356890efcedabULL },\n    { &fnv_test_str[146], (Fnv64_t) 0x95c2b383014f55c5ULL },\n    { &fnv_test_str[147], (Fnv64_t) 0x4727a5339ce6070fULL },\n    { &fnv_test_str[148], (Fnv64_t) 0xb0555ecd575108e9ULL },\n    { &fnv_test_str[149], (Fnv64_t) 0x48d785770bb4af37ULL },\n    { &fnv_test_str[150], (Fnv64_t) 0x09d4701c12af02b1ULL },\n    { &fnv_test_str[151], (Fnv64_t) 0x79f031e78f3cf62eULL },\n    { &fnv_test_str[152], (Fnv64_t) 0x52a1ee85db1b5a94ULL },\n    { &fnv_test_str[153], (Fnv64_t) 0x6bd95b2eb37fa6b8ULL },\n    { &fnv_test_str[154], (Fnv64_t) 0x74971b7077aef85dULL },\n    { &fnv_test_str[155], (Fnv64_t) 0xb4e4fae2ffcc1aadULL },\n    { &fnv_test_str[156], (Fnv64_t) 0x2bd48bd898b8f63aULL },\n    { &fnv_test_str[157], (Fnv64_t) 0xe9966ac1556257f6ULL },\n    { &fnv_test_str[158], (Fnv64_t) 0x92a3d1cd078ba293ULL },\n    { &fnv_test_str[159], (Fnv64_t) 0xf81175a482e20ab8ULL },\n    { &fnv_test_str[160], (Fnv64_t) 0x5bbb3de722e73048ULL },\n    { &fnv_test_str[161], (Fnv64_t) 0x6b4f363492b9f2beULL },\n    { &fnv_test_str[162], (Fnv64_t) 0xc2d559df73d59875ULL },\n    { &fnv_test_str[163], (Fnv64_t) 0xf75f62284bc7a8c2ULL },\n    { &fnv_test_str[164], (Fnv64_t) 0xda8dd8e116a9f1ccULL },\n    { &fnv_test_str[165], (Fnv64_t) 0xbdc1e6ab76057885ULL },\n    { &fnv_test_str[166], (Fnv64_t) 0xfec6a4238a1224a0ULL },\n    { &fnv_test_str[167], (Fnv64_t) 0xc03f40f3223e290eULL },\n    { &fnv_test_str[168], (Fnv64_t) 0x1ed21673466ffda9ULL },\n    { &fnv_test_str[169], (Fnv64_t) 0xdf70f906bb0dd2afULL },\n    { &fnv_test_str[170], (Fnv64_t) 0xf3dcda369f2af666ULL },\n    { &fnv_test_str[171], (Fnv64_t) 0x9ebb11573cdcebdeULL },\n    { &fnv_test_str[172], (Fnv64_t) 0x81c72d9077fedca0ULL },\n    { &fnv_test_str[173], (Fnv64_t) 0x0ec074a31be5fb15ULL },\n    { &fnv_test_str[174], (Fnv64_t) 0x2a8b3280b6c48f20ULL },\n    { &fnv_test_str[175], (Fnv64_t) 0xfd31777513309344ULL },\n    { &fnv_test_str[176], (Fnv64_t) 0x194534a86ad006b6ULL },\n    { &fnv_test_str[177], (Fnv64_t) 0x3be6fdf46e0cfe12ULL },\n    { &fnv_test_str[178], (Fnv64_t) 0x017cc137a07eb057ULL },\n    { &fnv_test_str[179], (Fnv64_t) 0x9428fc6e7d26b54dULL },\n    { &fnv_test_str[180], (Fnv64_t) 0x9aaa2e3603ef8ad7ULL },\n    { &fnv_test_str[181], (Fnv64_t) 0x82c6d3f3a0ccdf7dULL },\n    { &fnv_test_str[182], (Fnv64_t) 0xc86eeea00cf09b65ULL },\n    { &fnv_test_str[183], (Fnv64_t) 0x705f8189dbb58299ULL },\n    { &fnv_test_str[184], (Fnv64_t) 0x415a7f554391ca69ULL },\n    { &fnv_test_str[185], (Fnv64_t) 0xcfe3d49fa2bdc555ULL },\n    { &fnv_test_str[186], (Fnv64_t) 0xf0f9c56039b25191ULL },\n    { &fnv_test_str[187], (Fnv64_t) 0x7075cb6abd1d32d9ULL },\n    { &fnv_test_str[188], (Fnv64_t) 0x43c94e2c8b277509ULL },\n    { &fnv_test_str[189], (Fnv64_t) 0x3cbfd4e4ea670359ULL },\n    { &fnv_test_str[190], (Fnv64_t) 0xc05887810f4d019dULL },\n    { &fnv_test_str[191], (Fnv64_t) 0x14468ff93ac22dc5ULL },\n    { &fnv_test_str[192], (Fnv64_t) 0xebed699589d99c05ULL },\n    { &fnv_test_str[193], (Fnv64_t) 0x6d99f6df321ca5d5ULL },\n    { &fnv_test_str[194], (Fnv64_t) 0x0cd410d08c36d625ULL },\n    { &fnv_test_str[195], (Fnv64_t) 0xef1b2a2c86831d35ULL },\n    { &fnv_test_str[196], (Fnv64_t) 0x3b349c4d69ee5f05ULL },\n    { &fnv_test_str[197], (Fnv64_t) 0x55248ce88f45f035ULL },\n    { &fnv_test_str[198], (Fnv64_t) 0xaa69ca6a18a4c885ULL },\n    { &fnv_test_str[199], (Fnv64_t) 0x1fe3fce62bd816b5ULL },\n    { &fnv_test_str[200], (Fnv64_t) 0x0289a488a8df69d9ULL },\n    { &fnv_test_str[201], (Fnv64_t) 0x15e96e1613df98b5ULL },\n    { &fnv_test_str[202], (Fnv64_t) 0xe6be57375ad89b99ULL },\n    { NULL, (Fnv64_t) 0 }\n};\n#else /* HAVE_64BIT_LONG_LONG */\nstruct fnv1_64_test_vector fnv1_64_vector[] = {\n    { &fnv_test_str[0], (Fnv64_t) {0x84222325UL, 0xcbf29ce4UL} },\n    { &fnv_test_str[1], (Fnv64_t) {0x8601b7beUL, 0xaf63bd4cUL} },\n    { &fnv_test_str[2], (Fnv64_t) {0x8601b7bdUL, 0xaf63bd4cUL} },\n    { &fnv_test_str[3], (Fnv64_t) {0x8601b7bcUL, 0xaf63bd4cUL} },\n    { &fnv_test_str[4], (Fnv64_t) {0x8601b7bbUL, 0xaf63bd4cUL} },\n    { &fnv_test_str[5], (Fnv64_t) {0x8601b7baUL, 0xaf63bd4cUL} },\n    { &fnv_test_str[6], (Fnv64_t) {0x8601b7b9UL, 0xaf63bd4cUL} },\n    { &fnv_test_str[7], (Fnv64_t) {0xb4eb2f34UL, 0x08326207UL} },\n    { &fnv_test_str[8], (Fnv64_t) {0x6ba13533UL, 0xd8cbc718UL} },\n    { &fnv_test_str[9], (Fnv64_t) {0xe2ed65cbUL, 0x0378817eUL} },\n    { &fnv_test_str[10], (Fnv64_t) {0x9963f790UL, 0xd329d59bUL} },\n    { &fnv_test_str[11], (Fnv64_t) {0xa4dda9c2UL, 0x340d8765UL} },\n    { &fnv_test_str[12], (Fnv64_t) {0x8601b7dfUL, 0xaf63bd4cUL} },\n    { &fnv_test_str[13], (Fnv64_t) {0xb4eb37daUL, 0x08326707UL} },\n    { &fnv_test_str[14], (Fnv64_t) {0xb4eb3627UL, 0x08326607UL} },\n    { &fnv_test_str[15], (Fnv64_t) {0xb4eb3474UL, 0x08326507UL} },\n    { &fnv_test_str[16], (Fnv64_t) {0xb4eb32c1UL, 0x08326407UL} },\n    { &fnv_test_str[17], (Fnv64_t) {0xb4eb310eUL, 0x08326307UL} },\n    { &fnv_test_str[18], (Fnv64_t) {0xb4eb2f5bUL, 0x08326207UL} },\n    { &fnv_test_str[19], (Fnv64_t) {0x6ba1355cUL, 0xd8cbc718UL} },\n    { &fnv_test_str[20], (Fnv64_t) {0xe2ed65a9UL, 0x0378817eUL} },\n    { &fnv_test_str[21], (Fnv64_t) {0x9963f7f1UL, 0xd329d59bUL} },\n    { &fnv_test_str[22], (Fnv64_t) {0xa4dda9b0UL, 0x340d8765UL} },\n    { &fnv_test_str[23], (Fnv64_t) {0x24a774a6UL, 0x50a6d3b7UL} },\n    { &fnv_test_str[24], (Fnv64_t) {0xb4eb341cUL, 0x08326507UL} },\n    { &fnv_test_str[25], (Fnv64_t) {0x6ba98bfbUL, 0xd8d5c818UL} },\n    { &fnv_test_str[26], (Fnv64_t) {0xf118dbefUL, 0x1ccefc7eUL} },\n    { &fnv_test_str[27], (Fnv64_t) {0xad3db77aUL, 0x0c92fab3UL} },\n    { &fnv_test_str[28], (Fnv64_t) {0x5fdec421UL, 0x9b77794fUL} },\n    { &fnv_test_str[29], (Fnv64_t) {0xe7874433UL, 0x0ac742dfUL} },\n    { &fnv_test_str[30], (Fnv64_t) {0x6ad8e2deUL, 0xd7dad576UL} },\n    { &fnv_test_str[31], (Fnv64_t) {0x8e897f5bUL, 0xa1bb9637UL} },\n    { &fnv_test_str[32], (Fnv64_t) {0x33a367d2UL, 0x5b3f9b67UL} },\n    { &fnv_test_str[33], (Fnv64_t) {0xbea969f6UL, 0xb07ce25cUL} },\n    { &fnv_test_str[34], (Fnv64_t) {0xf9df0d6aUL, 0x8d9e9997UL} },\n    { &fnv_test_str[35], (Fnv64_t) {0x9603cb7bUL, 0x838c673dUL} },\n    { &fnv_test_str[36], (Fnv64_t) {0xe872c273UL, 0x8b5ee8a5UL} },\n    { &fnv_test_str[37], (Fnv64_t) {0xfb00690cUL, 0x4507c4e9UL} },\n    { &fnv_test_str[38], (Fnv64_t) {0x81b27f45UL, 0x4c9ca595UL} },\n    { &fnv_test_str[39], (Fnv64_t) {0x624e4235UL, 0xe0aca20bUL} },\n    { &fnv_test_str[40], (Fnv64_t) {0x6ba98b94UL, 0xd8d5c818UL} },\n    { &fnv_test_str[41], (Fnv64_t) {0xf118db81UL, 0x1ccefc7eUL} },\n    { &fnv_test_str[42], (Fnv64_t) {0xad3db71dUL, 0x0c92fab3UL} },\n    { &fnv_test_str[43], (Fnv64_t) {0x5fdec44eUL, 0x9b77794fUL} },\n    { &fnv_test_str[44], (Fnv64_t) {0xe7874413UL, 0x0ac742dfUL} },\n    { &fnv_test_str[45], (Fnv64_t) {0x6ad8e2a9UL, 0xd7dad576UL} },\n    { &fnv_test_str[46], (Fnv64_t) {0x8e897f3aUL, 0xa1bb9637UL} },\n    { &fnv_test_str[47], (Fnv64_t) {0x33a367a1UL, 0x5b3f9b67UL} },\n    { &fnv_test_str[48], (Fnv64_t) {0xbea969d6UL, 0xb07ce25cUL} },\n    { &fnv_test_str[49], (Fnv64_t) {0xf9df0d02UL, 0x8d9e9997UL} },\n    { &fnv_test_str[50], (Fnv64_t) {0x9603cb1eUL, 0x838c673dUL} },\n    { &fnv_test_str[51], (Fnv64_t) {0xe872c201UL, 0x8b5ee8a5UL} },\n    { &fnv_test_str[52], (Fnv64_t) {0xfb006969UL, 0x4507c4e9UL} },\n    { &fnv_test_str[53], (Fnv64_t) {0x81b27f64UL, 0x4c9ca595UL} },\n    { &fnv_test_str[54], (Fnv64_t) {0x624e423fUL, 0xe0aca20bUL} },\n    { &fnv_test_str[55], (Fnv64_t) {0x0afa800fUL, 0x13998e58UL} },\n    { &fnv_test_str[56], (Fnv64_t) {0xb4eb3401UL, 0x08326507UL} },\n    { &fnv_test_str[57], (Fnv64_t) {0x6ba95dc1UL, 0xd8d5ad18UL} },\n    { &fnv_test_str[58], (Fnv64_t) {0xf0ca4e97UL, 0x1c72e17eUL} },\n    { &fnv_test_str[59], (Fnv64_t) {0x27c38ae6UL, 0x2183c1b3UL} },\n    { &fnv_test_str[60], (Fnv64_t) {0x914504f2UL, 0xb66d096cUL} },\n    { &fnv_test_str[61], (Fnv64_t) {0xd8476757UL, 0x404bf57aUL} },\n    { &fnv_test_str[62], (Fnv64_t) {0x815498bbUL, 0x887976bdUL} },\n    { &fnv_test_str[63], (Fnv64_t) {0xc2bf85a5UL, 0x3afd7f02UL} },\n    { &fnv_test_str[64], (Fnv64_t) {0xeb70177fUL, 0xfc4476b0UL} },\n    { &fnv_test_str[65], (Fnv64_t) {0x0f77ecbaUL, 0x186d2da0UL} },\n    { &fnv_test_str[66], (Fnv64_t) {0x48c74066UL, 0xf97140faUL} },\n    { &fnv_test_str[67], (Fnv64_t) {0xaa926d37UL, 0xa2b1cf49UL} },\n    { &fnv_test_str[68], (Fnv64_t) {0xd6cf940cUL, 0x0690712cUL} },\n    { &fnv_test_str[69], (Fnv64_t) {0x02b8906eUL, 0xf7045b31UL} },\n    { &fnv_test_str[70], (Fnv64_t) {0x6ba95db3UL, 0xd8d5ad18UL} },\n    { &fnv_test_str[71], (Fnv64_t) {0xf0ca4ef3UL, 0x1c72e17eUL} },\n    { &fnv_test_str[72], (Fnv64_t) {0x27c38a95UL, 0x2183c1b3UL} },\n    { &fnv_test_str[73], (Fnv64_t) {0x914504d2UL, 0xb66d096cUL} },\n    { &fnv_test_str[74], (Fnv64_t) {0xd8476736UL, 0x404bf57aUL} },\n    { &fnv_test_str[75], (Fnv64_t) {0x815498d5UL, 0x887976bdUL} },\n    { &fnv_test_str[76], (Fnv64_t) {0xc2bf85c1UL, 0x3afd7f02UL} },\n    { &fnv_test_str[77], (Fnv64_t) {0xeb70175fUL, 0xfc4476b0UL} },\n    { &fnv_test_str[78], (Fnv64_t) {0x0f77eccdUL, 0x186d2da0UL} },\n    { &fnv_test_str[79], (Fnv64_t) {0x48c7400eUL, 0xf97140faUL} },\n    { &fnv_test_str[80], (Fnv64_t) {0xaa926d52UL, 0xa2b1cf49UL} },\n    { &fnv_test_str[81], (Fnv64_t) {0xd6cf9475UL, 0x0690712cUL} },\n    { &fnv_test_str[82], (Fnv64_t) {0x02b89064UL, 0xf7045b31UL} },\n    { &fnv_test_str[83], (Fnv64_t) {0x9f9d6aeaUL, 0x74f76247UL} },\n    { &fnv_test_str[84], (Fnv64_t) {0xb4eb2b9cUL, 0x08326007UL} },\n    { &fnv_test_str[85], (Fnv64_t) {0x6b9b1a14UL, 0xd8c4c918UL} },\n    { &fnv_test_str[86], (Fnv64_t) {0xbdbdd4c7UL, 0x7b495389UL} },\n    { &fnv_test_str[87], (Fnv64_t) {0x69908e25UL, 0x3b6dba0dUL} },\n    { &fnv_test_str[88], (Fnv64_t) {0xf4b71261UL, 0xd6b2b17bUL} },\n    { &fnv_test_str[89], (Fnv64_t) {0x98e615b5UL, 0x447bfb7fUL} },\n    { &fnv_test_str[90], (Fnv64_t) {0xf4b71262UL, 0xd6b2b17bUL} },\n    { &fnv_test_str[91], (Fnv64_t) {0x93fe1660UL, 0x3bd2807fUL} },\n    { &fnv_test_str[92], (Fnv64_t) {0xf4b71263UL, 0xd6b2b17bUL} },\n    { &fnv_test_str[93], (Fnv64_t) {0x8f16170bUL, 0x3329057fUL} },\n    { &fnv_test_str[94], (Fnv64_t) {0xf4b71264UL, 0xd6b2b17bUL} },\n    { &fnv_test_str[95], (Fnv64_t) {0x8a2e19b6UL, 0x2a7f8a7fUL} },\n    { &fnv_test_str[96], (Fnv64_t) {0x64b2f98aUL, 0x23d3767eUL} },\n    { &fnv_test_str[97], (Fnv64_t) {0x4f9d86a4UL, 0xff768d7eUL} },\n    { &fnv_test_str[98], (Fnv64_t) {0x64b2f984UL, 0x23d3767eUL} },\n    { &fnv_test_str[99], (Fnv64_t) {0x334e4aa6UL, 0xccd1837eUL} },\n    { &fnv_test_str[100], (Fnv64_t) {0x64b2f99aUL, 0x23d3767eUL} },\n    { &fnv_test_str[101], (Fnv64_t) {0x028f6754UL, 0x7691fd7eUL} },\n    { &fnv_test_str[102], (Fnv64_t) {0x41204318UL, 0x34ad3b10UL} },\n    { &fnv_test_str[103], (Fnv64_t) {0xa9d201c8UL, 0xa29e749eUL} },\n    { &fnv_test_str[104], (Fnv64_t) {0x4120431bUL, 0x34ad3b10UL} },\n    { &fnv_test_str[105], (Fnv64_t) {0xa9d206e1UL, 0xa29e779eUL} },\n    { &fnv_test_str[106], (Fnv64_t) {0x4120431aUL, 0x34ad3b10UL} },\n    { &fnv_test_str[107], (Fnv64_t) {0xa9d2052eUL, 0xa29e769eUL} },\n    { &fnv_test_str[108], (Fnv64_t) {0xa4aa3497UL, 0x02a17ebcUL} },\n    { &fnv_test_str[109], (Fnv64_t) {0xcd375c95UL, 0x229ef18bUL} },\n    { &fnv_test_str[110], (Fnv64_t) {0xa4aa32c8UL, 0x02a17dbcUL} },\n    { &fnv_test_str[111], (Fnv64_t) {0xcd3449d8UL, 0x229b6f8bUL} },\n    { &fnv_test_str[112], (Fnv64_t) {0xa4aa3ed5UL, 0x02a184bcUL} },\n    { &fnv_test_str[113], (Fnv64_t) {0xcd48c3efUL, 0x22b3618bUL} },\n    { &fnv_test_str[114], (Fnv64_t) {0x06186f36UL, 0x5c2c3467UL} },\n    { &fnv_test_str[115], (Fnv64_t) {0x5b84f8c2UL, 0xb78c410fUL} },\n    { &fnv_test_str[116], (Fnv64_t) {0x2b267395UL, 0xed947821UL} },\n    { &fnv_test_str[117], (Fnv64_t) {0x5256662fUL, 0xd9bbb55cUL} },\n    { &fnv_test_str[118], (Fnv64_t) {0x3249438aUL, 0x8c54f020UL} },\n    { &fnv_test_str[119], (Fnv64_t) {0x727dc37eUL, 0xbd9790b5UL} },\n    { &fnv_test_str[120], (Fnv64_t) {0xc9e2b0e3UL, 0xa64e5f36UL} },\n    { &fnv_test_str[121], (Fnv64_t) {0xa3088a04UL, 0x8fd0680dUL} },\n    { &fnv_test_str[122], (Fnv64_t) {0x078284ccUL, 0x67aad32cUL} },\n    { &fnv_test_str[123], (Fnv64_t) {0x1c57b331UL, 0xb37d55d8UL} },\n    { &fnv_test_str[124], (Fnv64_t) {0x29057c43UL, 0x55ac0f38UL} },\n    { &fnv_test_str[125], (Fnv64_t) {0xe1b6cc20UL, 0xcb27f4b8UL} },\n    { &fnv_test_str[126], (Fnv64_t) {0xcbef2d19UL, 0x26caf88bUL} },\n    { &fnv_test_str[127], (Fnv64_t) {0x97e61b8fUL, 0x8e6e063bUL} },\n    { &fnv_test_str[128], (Fnv64_t) {0xf3b7c37eUL, 0xb42750f7UL} },\n    { &fnv_test_str[129], (Fnv64_t) {0xcf7ca99bUL, 0xf3c6ba64UL} },\n    { &fnv_test_str[130], (Fnv64_t) {0x27ea80feUL, 0xebfb69b4UL} },\n    { &fnv_test_str[131], (Fnv64_t) {0xd970f46cUL, 0x39b50c3eUL} },\n    { &fnv_test_str[132], (Fnv64_t) {0xa3eb3e8aUL, 0x5b9b177aUL} },\n    { &fnv_test_str[133], (Fnv64_t) {0xcf4ec903UL, 0x6510063eUL} },\n    { &fnv_test_str[134], (Fnv64_t) {0x00797c7aUL, 0x2b3bbd2cUL} },\n    { &fnv_test_str[135], (Fnv64_t) {0xf5cb4aa7UL, 0xf1d6204fUL} },\n    { &fnv_test_str[136], (Fnv64_t) {0xcf099f38UL, 0x4836e27cUL} },\n    { &fnv_test_str[137], (Fnv64_t) {0xd073b44dUL, 0x82efbb0dUL} },\n    { &fnv_test_str[138], (Fnv64_t) {0xffd7d4c6UL, 0x4a80c282UL} },\n    { &fnv_test_str[139], (Fnv64_t) {0x9ee43bdfUL, 0x305d1a9cUL} },\n    { &fnv_test_str[140], (Fnv64_t) {0x8ffc6997UL, 0x15c36694UL} },\n    { &fnv_test_str[141], (Fnv64_t) {0x18916e7bUL, 0x80153ae2UL} },\n    { &fnv_test_str[142], (Fnv64_t) {0xf9e2a9e1UL, 0xfa23e2bdUL} },\n    { &fnv_test_str[143], (Fnv64_t) {0x2333c6deUL, 0xd47e8d8aUL} },\n    { &fnv_test_str[144], (Fnv64_t) {0xf688b056UL, 0x7e128095UL} },\n    { &fnv_test_str[145], (Fnv64_t) {0x0efcedabUL, 0x2f535689UL} },\n    { &fnv_test_str[146], (Fnv64_t) {0x014f55c5UL, 0x95c2b383UL} },\n    { &fnv_test_str[147], (Fnv64_t) {0x9ce6070fUL, 0x4727a533UL} },\n    { &fnv_test_str[148], (Fnv64_t) {0x575108e9UL, 0xb0555ecdUL} },\n    { &fnv_test_str[149], (Fnv64_t) {0x0bb4af37UL, 0x48d78577UL} },\n    { &fnv_test_str[150], (Fnv64_t) {0x12af02b1UL, 0x09d4701cUL} },\n    { &fnv_test_str[151], (Fnv64_t) {0x8f3cf62eUL, 0x79f031e7UL} },\n    { &fnv_test_str[152], (Fnv64_t) {0xdb1b5a94UL, 0x52a1ee85UL} },\n    { &fnv_test_str[153], (Fnv64_t) {0xb37fa6b8UL, 0x6bd95b2eUL} },\n    { &fnv_test_str[154], (Fnv64_t) {0x77aef85dUL, 0x74971b70UL} },\n    { &fnv_test_str[155], (Fnv64_t) {0xffcc1aadUL, 0xb4e4fae2UL} },\n    { &fnv_test_str[156], (Fnv64_t) {0x98b8f63aUL, 0x2bd48bd8UL} },\n    { &fnv_test_str[157], (Fnv64_t) {0x556257f6UL, 0xe9966ac1UL} },\n    { &fnv_test_str[158], (Fnv64_t) {0x078ba293UL, 0x92a3d1cdUL} },\n    { &fnv_test_str[159], (Fnv64_t) {0x82e20ab8UL, 0xf81175a4UL} },\n    { &fnv_test_str[160], (Fnv64_t) {0x22e73048UL, 0x5bbb3de7UL} },\n    { &fnv_test_str[161], (Fnv64_t) {0x92b9f2beUL, 0x6b4f3634UL} },\n    { &fnv_test_str[162], (Fnv64_t) {0x73d59875UL, 0xc2d559dfUL} },\n    { &fnv_test_str[163], (Fnv64_t) {0x4bc7a8c2UL, 0xf75f6228UL} },\n    { &fnv_test_str[164], (Fnv64_t) {0x16a9f1ccUL, 0xda8dd8e1UL} },\n    { &fnv_test_str[165], (Fnv64_t) {0x76057885UL, 0xbdc1e6abUL} },\n    { &fnv_test_str[166], (Fnv64_t) {0x8a1224a0UL, 0xfec6a423UL} },\n    { &fnv_test_str[167], (Fnv64_t) {0x223e290eUL, 0xc03f40f3UL} },\n    { &fnv_test_str[168], (Fnv64_t) {0x466ffda9UL, 0x1ed21673UL} },\n    { &fnv_test_str[169], (Fnv64_t) {0xbb0dd2afUL, 0xdf70f906UL} },\n    { &fnv_test_str[170], (Fnv64_t) {0x9f2af666UL, 0xf3dcda36UL} },\n    { &fnv_test_str[171], (Fnv64_t) {0x3cdcebdeUL, 0x9ebb1157UL} },\n    { &fnv_test_str[172], (Fnv64_t) {0x77fedca0UL, 0x81c72d90UL} },\n    { &fnv_test_str[173], (Fnv64_t) {0x1be5fb15UL, 0x0ec074a3UL} },\n    { &fnv_test_str[174], (Fnv64_t) {0xb6c48f20UL, 0x2a8b3280UL} },\n    { &fnv_test_str[175], (Fnv64_t) {0x13309344UL, 0xfd317775UL} },\n    { &fnv_test_str[176], (Fnv64_t) {0x6ad006b6UL, 0x194534a8UL} },\n    { &fnv_test_str[177], (Fnv64_t) {0x6e0cfe12UL, 0x3be6fdf4UL} },\n    { &fnv_test_str[178], (Fnv64_t) {0xa07eb057UL, 0x017cc137UL} },\n    { &fnv_test_str[179], (Fnv64_t) {0x7d26b54dUL, 0x9428fc6eUL} },\n    { &fnv_test_str[180], (Fnv64_t) {0x03ef8ad7UL, 0x9aaa2e36UL} },\n    { &fnv_test_str[181], (Fnv64_t) {0xa0ccdf7dUL, 0x82c6d3f3UL} },\n    { &fnv_test_str[182], (Fnv64_t) {0x0cf09b65UL, 0xc86eeea0UL} },\n    { &fnv_test_str[183], (Fnv64_t) {0xdbb58299UL, 0x705f8189UL} },\n    { &fnv_test_str[184], (Fnv64_t) {0x4391ca69UL, 0x415a7f55UL} },\n    { &fnv_test_str[185], (Fnv64_t) {0xa2bdc555UL, 0xcfe3d49fUL} },\n    { &fnv_test_str[186], (Fnv64_t) {0x39b25191UL, 0xf0f9c560UL} },\n    { &fnv_test_str[187], (Fnv64_t) {0xbd1d32d9UL, 0x7075cb6aUL} },\n    { &fnv_test_str[188], (Fnv64_t) {0x8b277509UL, 0x43c94e2cUL} },\n    { &fnv_test_str[189], (Fnv64_t) {0xea670359UL, 0x3cbfd4e4UL} },\n    { &fnv_test_str[190], (Fnv64_t) {0x0f4d019dUL, 0xc0588781UL} },\n    { &fnv_test_str[191], (Fnv64_t) {0x3ac22dc5UL, 0x14468ff9UL} },\n    { &fnv_test_str[192], (Fnv64_t) {0x89d99c05UL, 0xebed6995UL} },\n    { &fnv_test_str[193], (Fnv64_t) {0x321ca5d5UL, 0x6d99f6dfUL} },\n    { &fnv_test_str[194], (Fnv64_t) {0x8c36d625UL, 0x0cd410d0UL} },\n    { &fnv_test_str[195], (Fnv64_t) {0x86831d35UL, 0xef1b2a2cUL} },\n    { &fnv_test_str[196], (Fnv64_t) {0x69ee5f05UL, 0x3b349c4dUL} },\n    { &fnv_test_str[197], (Fnv64_t) {0x8f45f035UL, 0x55248ce8UL} },\n    { &fnv_test_str[198], (Fnv64_t) {0x18a4c885UL, 0xaa69ca6aUL} },\n    { &fnv_test_str[199], (Fnv64_t) {0x2bd816b5UL, 0x1fe3fce6UL} },\n    { &fnv_test_str[200], (Fnv64_t) {0xa8df69d9UL, 0x0289a488UL} },\n    { &fnv_test_str[201], (Fnv64_t) {0x13df98b5UL, 0x15e96e16UL} },\n    { &fnv_test_str[202], (Fnv64_t) {0x5ad89b99UL, 0xe6be5737UL} },\n    { NULL, (Fnv64_t) {0,0} }\n};\n#endif /* HAVE_64BIT_LONG_LONG */\n\n/* FNV-1a 64 bit test vectors */\n#if defined(HAVE_64BIT_LONG_LONG)\nstruct fnv1a_64_test_vector fnv1a_64_vector[] = {\n    { &fnv_test_str[0], (Fnv64_t) 0xcbf29ce484222325ULL },\n    { &fnv_test_str[1], (Fnv64_t) 0xaf63dc4c8601ec8cULL },\n    { &fnv_test_str[2], (Fnv64_t) 0xaf63df4c8601f1a5ULL },\n    { &fnv_test_str[3], (Fnv64_t) 0xaf63de4c8601eff2ULL },\n    { &fnv_test_str[4], (Fnv64_t) 0xaf63d94c8601e773ULL },\n    { &fnv_test_str[5], (Fnv64_t) 0xaf63d84c8601e5c0ULL },\n    { &fnv_test_str[6], (Fnv64_t) 0xaf63db4c8601ead9ULL },\n    { &fnv_test_str[7], (Fnv64_t) 0x08985907b541d342ULL },\n    { &fnv_test_str[8], (Fnv64_t) 0xdcb27518fed9d577ULL },\n    { &fnv_test_str[9], (Fnv64_t) 0xdd120e790c2512afULL },\n    { &fnv_test_str[10], (Fnv64_t) 0xcac165afa2fef40aULL },\n    { &fnv_test_str[11], (Fnv64_t) 0x85944171f73967e8ULL },\n    { &fnv_test_str[12], (Fnv64_t) 0xaf63bd4c8601b7dfULL },\n    { &fnv_test_str[13], (Fnv64_t) 0x089be207b544f1e4ULL },\n    { &fnv_test_str[14], (Fnv64_t) 0x08a61407b54d9b5fULL },\n    { &fnv_test_str[15], (Fnv64_t) 0x08a2ae07b54ab836ULL },\n    { &fnv_test_str[16], (Fnv64_t) 0x0891b007b53c4869ULL },\n    { &fnv_test_str[17], (Fnv64_t) 0x088e4a07b5396540ULL },\n    { &fnv_test_str[18], (Fnv64_t) 0x08987c07b5420ebbULL },\n    { &fnv_test_str[19], (Fnv64_t) 0xdcb28a18fed9f926ULL },\n    { &fnv_test_str[20], (Fnv64_t) 0xdd1270790c25b935ULL },\n    { &fnv_test_str[21], (Fnv64_t) 0xcac146afa2febf5dULL },\n    { &fnv_test_str[22], (Fnv64_t) 0x8593d371f738acfeULL },\n    { &fnv_test_str[23], (Fnv64_t) 0x34531ca7168b8f38ULL },\n    { &fnv_test_str[24], (Fnv64_t) 0x08a25607b54a22aeULL },\n    { &fnv_test_str[25], (Fnv64_t) 0xf5faf0190cf90df3ULL },\n    { &fnv_test_str[26], (Fnv64_t) 0xf27397910b3221c7ULL },\n    { &fnv_test_str[27], (Fnv64_t) 0x2c8c2b76062f22e0ULL },\n    { &fnv_test_str[28], (Fnv64_t) 0xe150688c8217b8fdULL },\n    { &fnv_test_str[29], (Fnv64_t) 0xf35a83c10e4f1f87ULL },\n    { &fnv_test_str[30], (Fnv64_t) 0xd1edd10b507344d0ULL },\n    { &fnv_test_str[31], (Fnv64_t) 0x2a5ee739b3ddb8c3ULL },\n    { &fnv_test_str[32], (Fnv64_t) 0xdcfb970ca1c0d310ULL },\n    { &fnv_test_str[33], (Fnv64_t) 0x4054da76daa6da90ULL },\n    { &fnv_test_str[34], (Fnv64_t) 0xf70a2ff589861368ULL },\n    { &fnv_test_str[35], (Fnv64_t) 0x4c628b38aed25f17ULL },\n    { &fnv_test_str[36], (Fnv64_t) 0x9dd1f6510f78189fULL },\n    { &fnv_test_str[37], (Fnv64_t) 0xa3de85bd491270ceULL },\n    { &fnv_test_str[38], (Fnv64_t) 0x858e2fa32a55e61dULL },\n    { &fnv_test_str[39], (Fnv64_t) 0x46810940eff5f915ULL },\n    { &fnv_test_str[40], (Fnv64_t) 0xf5fadd190cf8edaaULL },\n    { &fnv_test_str[41], (Fnv64_t) 0xf273ed910b32b3e9ULL },\n    { &fnv_test_str[42], (Fnv64_t) 0x2c8c5276062f6525ULL },\n    { &fnv_test_str[43], (Fnv64_t) 0xe150b98c821842a0ULL },\n    { &fnv_test_str[44], (Fnv64_t) 0xf35aa3c10e4f55e7ULL },\n    { &fnv_test_str[45], (Fnv64_t) 0xd1ed680b50729265ULL },\n    { &fnv_test_str[46], (Fnv64_t) 0x2a5f0639b3dded70ULL },\n    { &fnv_test_str[47], (Fnv64_t) 0xdcfbaa0ca1c0f359ULL },\n    { &fnv_test_str[48], (Fnv64_t) 0x4054ba76daa6a430ULL },\n    { &fnv_test_str[49], (Fnv64_t) 0xf709c7f5898562b0ULL },\n    { &fnv_test_str[50], (Fnv64_t) 0x4c62e638aed2f9b8ULL },\n    { &fnv_test_str[51], (Fnv64_t) 0x9dd1a8510f779415ULL },\n    { &fnv_test_str[52], (Fnv64_t) 0xa3de2abd4911d62dULL },\n    { &fnv_test_str[53], (Fnv64_t) 0x858e0ea32a55ae0aULL },\n    { &fnv_test_str[54], (Fnv64_t) 0x46810f40eff60347ULL },\n    { &fnv_test_str[55], (Fnv64_t) 0xc33bce57bef63eafULL },\n    { &fnv_test_str[56], (Fnv64_t) 0x08a24307b54a0265ULL },\n    { &fnv_test_str[57], (Fnv64_t) 0xf5b9fd190cc18d15ULL },\n    { &fnv_test_str[58], (Fnv64_t) 0x4c968290ace35703ULL },\n    { &fnv_test_str[59], (Fnv64_t) 0x07174bd5c64d9350ULL },\n    { &fnv_test_str[60], (Fnv64_t) 0x5a294c3ff5d18750ULL },\n    { &fnv_test_str[61], (Fnv64_t) 0x05b3c1aeb308b843ULL },\n    { &fnv_test_str[62], (Fnv64_t) 0xb92a48da37d0f477ULL },\n    { &fnv_test_str[63], (Fnv64_t) 0x73cdddccd80ebc49ULL },\n    { &fnv_test_str[64], (Fnv64_t) 0xd58c4c13210a266bULL },\n    { &fnv_test_str[65], (Fnv64_t) 0xe78b6081243ec194ULL },\n    { &fnv_test_str[66], (Fnv64_t) 0xb096f77096a39f34ULL },\n    { &fnv_test_str[67], (Fnv64_t) 0xb425c54ff807b6a3ULL },\n    { &fnv_test_str[68], (Fnv64_t) 0x23e520e2751bb46eULL },\n    { &fnv_test_str[69], (Fnv64_t) 0x1a0b44ccfe1385ecULL },\n    { &fnv_test_str[70], (Fnv64_t) 0xf5ba4b190cc2119fULL },\n    { &fnv_test_str[71], (Fnv64_t) 0x4c962690ace2baafULL },\n    { &fnv_test_str[72], (Fnv64_t) 0x0716ded5c64cda19ULL },\n    { &fnv_test_str[73], (Fnv64_t) 0x5a292c3ff5d150f0ULL },\n    { &fnv_test_str[74], (Fnv64_t) 0x05b3e0aeb308ecf0ULL },\n    { &fnv_test_str[75], (Fnv64_t) 0xb92a5eda37d119d9ULL },\n    { &fnv_test_str[76], (Fnv64_t) 0x73ce41ccd80f6635ULL },\n    { &fnv_test_str[77], (Fnv64_t) 0xd58c2c132109f00bULL },\n    { &fnv_test_str[78], (Fnv64_t) 0xe78baf81243f47d1ULL },\n    { &fnv_test_str[79], (Fnv64_t) 0xb0968f7096a2ee7cULL },\n    { &fnv_test_str[80], (Fnv64_t) 0xb425a84ff807855cULL },\n    { &fnv_test_str[81], (Fnv64_t) 0x23e4e9e2751b56f9ULL },\n    { &fnv_test_str[82], (Fnv64_t) 0x1a0b4eccfe1396eaULL },\n    { &fnv_test_str[83], (Fnv64_t) 0x54abd453bb2c9004ULL },\n    { &fnv_test_str[84], (Fnv64_t) 0x08ba5f07b55ec3daULL },\n    { &fnv_test_str[85], (Fnv64_t) 0x337354193006cb6eULL },\n    { &fnv_test_str[86], (Fnv64_t) 0xa430d84680aabd0bULL },\n    { &fnv_test_str[87], (Fnv64_t) 0xa9bc8acca21f39b1ULL },\n    { &fnv_test_str[88], (Fnv64_t) 0x6961196491cc682dULL },\n    { &fnv_test_str[89], (Fnv64_t) 0xad2bb1774799dfe9ULL },\n    { &fnv_test_str[90], (Fnv64_t) 0x6961166491cc6314ULL },\n    { &fnv_test_str[91], (Fnv64_t) 0x8d1bb3904a3b1236ULL },\n    { &fnv_test_str[92], (Fnv64_t) 0x6961176491cc64c7ULL },\n    { &fnv_test_str[93], (Fnv64_t) 0xed205d87f40434c7ULL },\n    { &fnv_test_str[94], (Fnv64_t) 0x6961146491cc5faeULL },\n    { &fnv_test_str[95], (Fnv64_t) 0xcd3baf5e44f8ad9cULL },\n    { &fnv_test_str[96], (Fnv64_t) 0xe3b36596127cd6d8ULL },\n    { &fnv_test_str[97], (Fnv64_t) 0xf77f1072c8e8a646ULL },\n    { &fnv_test_str[98], (Fnv64_t) 0xe3b36396127cd372ULL },\n    { &fnv_test_str[99], (Fnv64_t) 0x6067dce9932ad458ULL },\n    { &fnv_test_str[100], (Fnv64_t) 0xe3b37596127cf208ULL },\n    { &fnv_test_str[101], (Fnv64_t) 0x4b7b10fa9fe83936ULL },\n    { &fnv_test_str[102], (Fnv64_t) 0xaabafe7104d914beULL },\n    { &fnv_test_str[103], (Fnv64_t) 0xf4d3180b3cde3edaULL },\n    { &fnv_test_str[104], (Fnv64_t) 0xaabafd7104d9130bULL },\n    { &fnv_test_str[105], (Fnv64_t) 0xf4cfb20b3cdb5bb1ULL },\n    { &fnv_test_str[106], (Fnv64_t) 0xaabafc7104d91158ULL },\n    { &fnv_test_str[107], (Fnv64_t) 0xf4cc4c0b3cd87888ULL },\n    { &fnv_test_str[108], (Fnv64_t) 0xe729bac5d2a8d3a7ULL },\n    { &fnv_test_str[109], (Fnv64_t) 0x74bc0524f4dfa4c5ULL },\n    { &fnv_test_str[110], (Fnv64_t) 0xe72630c5d2a5b352ULL },\n    { &fnv_test_str[111], (Fnv64_t) 0x6b983224ef8fb456ULL },\n    { &fnv_test_str[112], (Fnv64_t) 0xe73042c5d2ae266dULL },\n    { &fnv_test_str[113], (Fnv64_t) 0x8527e324fdeb4b37ULL },\n    { &fnv_test_str[114], (Fnv64_t) 0x0a83c86fee952abcULL },\n    { &fnv_test_str[115], (Fnv64_t) 0x7318523267779d74ULL },\n    { &fnv_test_str[116], (Fnv64_t) 0x3e66d3d56b8caca1ULL },\n    { &fnv_test_str[117], (Fnv64_t) 0x956694a5c0095593ULL },\n    { &fnv_test_str[118], (Fnv64_t) 0xcac54572bb1a6fc8ULL },\n    { &fnv_test_str[119], (Fnv64_t) 0xa7a4c9f3edebf0d8ULL },\n    { &fnv_test_str[120], (Fnv64_t) 0x7829851fac17b143ULL },\n    { &fnv_test_str[121], (Fnv64_t) 0x2c8f4c9af81bcf06ULL },\n    { &fnv_test_str[122], (Fnv64_t) 0xd34e31539740c732ULL },\n    { &fnv_test_str[123], (Fnv64_t) 0x3605a2ac253d2db1ULL },\n    { &fnv_test_str[124], (Fnv64_t) 0x08c11b8346f4a3c3ULL },\n    { &fnv_test_str[125], (Fnv64_t) 0x6be396289ce8a6daULL },\n    { &fnv_test_str[126], (Fnv64_t) 0xd9b957fb7fe794c5ULL },\n    { &fnv_test_str[127], (Fnv64_t) 0x05be33da04560a93ULL },\n    { &fnv_test_str[128], (Fnv64_t) 0x0957f1577ba9747cULL },\n    { &fnv_test_str[129], (Fnv64_t) 0xda2cc3acc24fba57ULL },\n    { &fnv_test_str[130], (Fnv64_t) 0x74136f185b29e7f0ULL },\n    { &fnv_test_str[131], (Fnv64_t) 0xb2f2b4590edb93b2ULL },\n    { &fnv_test_str[132], (Fnv64_t) 0xb3608fce8b86ae04ULL },\n    { &fnv_test_str[133], (Fnv64_t) 0x4a3a865079359063ULL },\n    { &fnv_test_str[134], (Fnv64_t) 0x5b3a7ef496880a50ULL },\n    { &fnv_test_str[135], (Fnv64_t) 0x48fae3163854c23bULL },\n    { &fnv_test_str[136], (Fnv64_t) 0x07aaa640476e0b9aULL },\n    { &fnv_test_str[137], (Fnv64_t) 0x2f653656383a687dULL },\n    { &fnv_test_str[138], (Fnv64_t) 0xa1031f8e7599d79cULL },\n    { &fnv_test_str[139], (Fnv64_t) 0xa31908178ff92477ULL },\n    { &fnv_test_str[140], (Fnv64_t) 0x097edf3c14c3fb83ULL },\n    { &fnv_test_str[141], (Fnv64_t) 0xb51ca83feaa0971bULL },\n    { &fnv_test_str[142], (Fnv64_t) 0xdd3c0d96d784f2e9ULL },\n    { &fnv_test_str[143], (Fnv64_t) 0x86cd26a9ea767d78ULL },\n    { &fnv_test_str[144], (Fnv64_t) 0xe6b215ff54a30c18ULL },\n    { &fnv_test_str[145], (Fnv64_t) 0xec5b06a1c5531093ULL },\n    { &fnv_test_str[146], (Fnv64_t) 0x45665a929f9ec5e5ULL },\n    { &fnv_test_str[147], (Fnv64_t) 0x8c7609b4a9f10907ULL },\n    { &fnv_test_str[148], (Fnv64_t) 0x89aac3a491f0d729ULL },\n    { &fnv_test_str[149], (Fnv64_t) 0x32ce6b26e0f4a403ULL },\n    { &fnv_test_str[150], (Fnv64_t) 0x614ab44e02b53e01ULL },\n    { &fnv_test_str[151], (Fnv64_t) 0xfa6472eb6eef3290ULL },\n    { &fnv_test_str[152], (Fnv64_t) 0x9e5d75eb1948eb6aULL },\n    { &fnv_test_str[153], (Fnv64_t) 0xb6d12ad4a8671852ULL },\n    { &fnv_test_str[154], (Fnv64_t) 0x88826f56eba07af1ULL },\n    { &fnv_test_str[155], (Fnv64_t) 0x44535bf2645bc0fdULL },\n    { &fnv_test_str[156], (Fnv64_t) 0x169388ffc21e3728ULL },\n    { &fnv_test_str[157], (Fnv64_t) 0xf68aac9e396d8224ULL },\n    { &fnv_test_str[158], (Fnv64_t) 0x8e87d7e7472b3883ULL },\n    { &fnv_test_str[159], (Fnv64_t) 0x295c26caa8b423deULL },\n    { &fnv_test_str[160], (Fnv64_t) 0x322c814292e72176ULL },\n    { &fnv_test_str[161], (Fnv64_t) 0x8a06550eb8af7268ULL },\n    { &fnv_test_str[162], (Fnv64_t) 0xef86d60e661bcf71ULL },\n    { &fnv_test_str[163], (Fnv64_t) 0x9e5426c87f30ee54ULL },\n    { &fnv_test_str[164], (Fnv64_t) 0xf1ea8aa826fd047eULL },\n    { &fnv_test_str[165], (Fnv64_t) 0x0babaf9a642cb769ULL },\n    { &fnv_test_str[166], (Fnv64_t) 0x4b3341d4068d012eULL },\n    { &fnv_test_str[167], (Fnv64_t) 0xd15605cbc30a335cULL },\n    { &fnv_test_str[168], (Fnv64_t) 0x5b21060aed8412e5ULL },\n    { &fnv_test_str[169], (Fnv64_t) 0x45e2cda1ce6f4227ULL },\n    { &fnv_test_str[170], (Fnv64_t) 0x50ae3745033ad7d4ULL },\n    { &fnv_test_str[171], (Fnv64_t) 0xaa4588ced46bf414ULL },\n    { &fnv_test_str[172], (Fnv64_t) 0xc1b0056c4a95467eULL },\n    { &fnv_test_str[173], (Fnv64_t) 0x56576a71de8b4089ULL },\n    { &fnv_test_str[174], (Fnv64_t) 0xbf20965fa6dc927eULL },\n    { &fnv_test_str[175], (Fnv64_t) 0x569f8383c2040882ULL },\n    { &fnv_test_str[176], (Fnv64_t) 0xe1e772fba08feca0ULL },\n    { &fnv_test_str[177], (Fnv64_t) 0x4ced94af97138ac4ULL },\n    { &fnv_test_str[178], (Fnv64_t) 0xc4112ffb337a82fbULL },\n    { &fnv_test_str[179], (Fnv64_t) 0xd64a4fd41de38b7dULL },\n    { &fnv_test_str[180], (Fnv64_t) 0x4cfc32329edebcbbULL },\n    { &fnv_test_str[181], (Fnv64_t) 0x0803564445050395ULL },\n    { &fnv_test_str[182], (Fnv64_t) 0xaa1574ecf4642ffdULL },\n    { &fnv_test_str[183], (Fnv64_t) 0x694bc4e54cc315f9ULL },\n    { &fnv_test_str[184], (Fnv64_t) 0xa3d7cb273b011721ULL },\n    { &fnv_test_str[185], (Fnv64_t) 0x577c2f8b6115bfa5ULL },\n    { &fnv_test_str[186], (Fnv64_t) 0xb7ec8c1a769fb4c1ULL },\n    { &fnv_test_str[187], (Fnv64_t) 0x5d5cfce63359ab19ULL },\n    { &fnv_test_str[188], (Fnv64_t) 0x33b96c3cd65b5f71ULL },\n    { &fnv_test_str[189], (Fnv64_t) 0xd845097780602bb9ULL },\n    { &fnv_test_str[190], (Fnv64_t) 0x84d47645d02da3d5ULL },\n    { &fnv_test_str[191], (Fnv64_t) 0x83544f33b58773a5ULL },\n    { &fnv_test_str[192], (Fnv64_t) 0x9175cbb2160836c5ULL },\n    { &fnv_test_str[193], (Fnv64_t) 0xc71b3bc175e72bc5ULL },\n    { &fnv_test_str[194], (Fnv64_t) 0x636806ac222ec985ULL },\n    { &fnv_test_str[195], (Fnv64_t) 0xb6ef0e6950f52ed5ULL },\n    { &fnv_test_str[196], (Fnv64_t) 0xead3d8a0f3dfdaa5ULL },\n    { &fnv_test_str[197], (Fnv64_t) 0x922908fe9a861ba5ULL },\n    { &fnv_test_str[198], (Fnv64_t) 0x6d4821de275fd5c5ULL },\n    { &fnv_test_str[199], (Fnv64_t) 0x1fe3fce62bd816b5ULL },\n    { &fnv_test_str[200], (Fnv64_t) 0xc23e9fccd6f70591ULL },\n    { &fnv_test_str[201], (Fnv64_t) 0xc1af12bdfe16b5b5ULL },\n    { &fnv_test_str[202], (Fnv64_t) 0x39e9f18f2f85e221ULL },\n    { NULL, (Fnv64_t) 0 }\n};\n#else /* HAVE_64BIT_LONG_LONG */\nstruct fnv1a_64_test_vector fnv1a_64_vector[] = {\n    { &fnv_test_str[0], (Fnv64_t) {0x84222325UL, 0xcbf29ce4UL} },\n    { &fnv_test_str[1], (Fnv64_t) {0x8601ec8cUL, 0xaf63dc4cUL} },\n    { &fnv_test_str[2], (Fnv64_t) {0x8601f1a5UL, 0xaf63df4cUL} },\n    { &fnv_test_str[3], (Fnv64_t) {0x8601eff2UL, 0xaf63de4cUL} },\n    { &fnv_test_str[4], (Fnv64_t) {0x8601e773UL, 0xaf63d94cUL} },\n    { &fnv_test_str[5], (Fnv64_t) {0x8601e5c0UL, 0xaf63d84cUL} },\n    { &fnv_test_str[6], (Fnv64_t) {0x8601ead9UL, 0xaf63db4cUL} },\n    { &fnv_test_str[7], (Fnv64_t) {0xb541d342UL, 0x08985907UL} },\n    { &fnv_test_str[8], (Fnv64_t) {0xfed9d577UL, 0xdcb27518UL} },\n    { &fnv_test_str[9], (Fnv64_t) {0x0c2512afUL, 0xdd120e79UL} },\n    { &fnv_test_str[10], (Fnv64_t) {0xa2fef40aUL, 0xcac165afUL} },\n    { &fnv_test_str[11], (Fnv64_t) {0xf73967e8UL, 0x85944171UL} },\n    { &fnv_test_str[12], (Fnv64_t) {0x8601b7dfUL, 0xaf63bd4cUL} },\n    { &fnv_test_str[13], (Fnv64_t) {0xb544f1e4UL, 0x089be207UL} },\n    { &fnv_test_str[14], (Fnv64_t) {0xb54d9b5fUL, 0x08a61407UL} },\n    { &fnv_test_str[15], (Fnv64_t) {0xb54ab836UL, 0x08a2ae07UL} },\n    { &fnv_test_str[16], (Fnv64_t) {0xb53c4869UL, 0x0891b007UL} },\n    { &fnv_test_str[17], (Fnv64_t) {0xb5396540UL, 0x088e4a07UL} },\n    { &fnv_test_str[18], (Fnv64_t) {0xb5420ebbUL, 0x08987c07UL} },\n    { &fnv_test_str[19], (Fnv64_t) {0xfed9f926UL, 0xdcb28a18UL} },\n    { &fnv_test_str[20], (Fnv64_t) {0x0c25b935UL, 0xdd127079UL} },\n    { &fnv_test_str[21], (Fnv64_t) {0xa2febf5dUL, 0xcac146afUL} },\n    { &fnv_test_str[22], (Fnv64_t) {0xf738acfeUL, 0x8593d371UL} },\n    { &fnv_test_str[23], (Fnv64_t) {0x168b8f38UL, 0x34531ca7UL} },\n    { &fnv_test_str[24], (Fnv64_t) {0xb54a22aeUL, 0x08a25607UL} },\n    { &fnv_test_str[25], (Fnv64_t) {0x0cf90df3UL, 0xf5faf019UL} },\n    { &fnv_test_str[26], (Fnv64_t) {0x0b3221c7UL, 0xf2739791UL} },\n    { &fnv_test_str[27], (Fnv64_t) {0x062f22e0UL, 0x2c8c2b76UL} },\n    { &fnv_test_str[28], (Fnv64_t) {0x8217b8fdUL, 0xe150688cUL} },\n    { &fnv_test_str[29], (Fnv64_t) {0x0e4f1f87UL, 0xf35a83c1UL} },\n    { &fnv_test_str[30], (Fnv64_t) {0x507344d0UL, 0xd1edd10bUL} },\n    { &fnv_test_str[31], (Fnv64_t) {0xb3ddb8c3UL, 0x2a5ee739UL} },\n    { &fnv_test_str[32], (Fnv64_t) {0xa1c0d310UL, 0xdcfb970cUL} },\n    { &fnv_test_str[33], (Fnv64_t) {0xdaa6da90UL, 0x4054da76UL} },\n    { &fnv_test_str[34], (Fnv64_t) {0x89861368UL, 0xf70a2ff5UL} },\n    { &fnv_test_str[35], (Fnv64_t) {0xaed25f17UL, 0x4c628b38UL} },\n    { &fnv_test_str[36], (Fnv64_t) {0x0f78189fUL, 0x9dd1f651UL} },\n    { &fnv_test_str[37], (Fnv64_t) {0x491270ceUL, 0xa3de85bdUL} },\n    { &fnv_test_str[38], (Fnv64_t) {0x2a55e61dUL, 0x858e2fa3UL} },\n    { &fnv_test_str[39], (Fnv64_t) {0xeff5f915UL, 0x46810940UL} },\n    { &fnv_test_str[40], (Fnv64_t) {0x0cf8edaaUL, 0xf5fadd19UL} },\n    { &fnv_test_str[41], (Fnv64_t) {0x0b32b3e9UL, 0xf273ed91UL} },\n    { &fnv_test_str[42], (Fnv64_t) {0x062f6525UL, 0x2c8c5276UL} },\n    { &fnv_test_str[43], (Fnv64_t) {0x821842a0UL, 0xe150b98cUL} },\n    { &fnv_test_str[44], (Fnv64_t) {0x0e4f55e7UL, 0xf35aa3c1UL} },\n    { &fnv_test_str[45], (Fnv64_t) {0x50729265UL, 0xd1ed680bUL} },\n    { &fnv_test_str[46], (Fnv64_t) {0xb3dded70UL, 0x2a5f0639UL} },\n    { &fnv_test_str[47], (Fnv64_t) {0xa1c0f359UL, 0xdcfbaa0cUL} },\n    { &fnv_test_str[48], (Fnv64_t) {0xdaa6a430UL, 0x4054ba76UL} },\n    { &fnv_test_str[49], (Fnv64_t) {0x898562b0UL, 0xf709c7f5UL} },\n    { &fnv_test_str[50], (Fnv64_t) {0xaed2f9b8UL, 0x4c62e638UL} },\n    { &fnv_test_str[51], (Fnv64_t) {0x0f779415UL, 0x9dd1a851UL} },\n    { &fnv_test_str[52], (Fnv64_t) {0x4911d62dUL, 0xa3de2abdUL} },\n    { &fnv_test_str[53], (Fnv64_t) {0x2a55ae0aUL, 0x858e0ea3UL} },\n    { &fnv_test_str[54], (Fnv64_t) {0xeff60347UL, 0x46810f40UL} },\n    { &fnv_test_str[55], (Fnv64_t) {0xbef63eafUL, 0xc33bce57UL} },\n    { &fnv_test_str[56], (Fnv64_t) {0xb54a0265UL, 0x08a24307UL} },\n    { &fnv_test_str[57], (Fnv64_t) {0x0cc18d15UL, 0xf5b9fd19UL} },\n    { &fnv_test_str[58], (Fnv64_t) {0xace35703UL, 0x4c968290UL} },\n    { &fnv_test_str[59], (Fnv64_t) {0xc64d9350UL, 0x07174bd5UL} },\n    { &fnv_test_str[60], (Fnv64_t) {0xf5d18750UL, 0x5a294c3fUL} },\n    { &fnv_test_str[61], (Fnv64_t) {0xb308b843UL, 0x05b3c1aeUL} },\n    { &fnv_test_str[62], (Fnv64_t) {0x37d0f477UL, 0xb92a48daUL} },\n    { &fnv_test_str[63], (Fnv64_t) {0xd80ebc49UL, 0x73cdddccUL} },\n    { &fnv_test_str[64], (Fnv64_t) {0x210a266bUL, 0xd58c4c13UL} },\n    { &fnv_test_str[65], (Fnv64_t) {0x243ec194UL, 0xe78b6081UL} },\n    { &fnv_test_str[66], (Fnv64_t) {0x96a39f34UL, 0xb096f770UL} },\n    { &fnv_test_str[67], (Fnv64_t) {0xf807b6a3UL, 0xb425c54fUL} },\n    { &fnv_test_str[68], (Fnv64_t) {0x751bb46eUL, 0x23e520e2UL} },\n    { &fnv_test_str[69], (Fnv64_t) {0xfe1385ecUL, 0x1a0b44ccUL} },\n    { &fnv_test_str[70], (Fnv64_t) {0x0cc2119fUL, 0xf5ba4b19UL} },\n    { &fnv_test_str[71], (Fnv64_t) {0xace2baafUL, 0x4c962690UL} },\n    { &fnv_test_str[72], (Fnv64_t) {0xc64cda19UL, 0x0716ded5UL} },\n    { &fnv_test_str[73], (Fnv64_t) {0xf5d150f0UL, 0x5a292c3fUL} },\n    { &fnv_test_str[74], (Fnv64_t) {0xb308ecf0UL, 0x05b3e0aeUL} },\n    { &fnv_test_str[75], (Fnv64_t) {0x37d119d9UL, 0xb92a5edaUL} },\n    { &fnv_test_str[76], (Fnv64_t) {0xd80f6635UL, 0x73ce41ccUL} },\n    { &fnv_test_str[77], (Fnv64_t) {0x2109f00bUL, 0xd58c2c13UL} },\n    { &fnv_test_str[78], (Fnv64_t) {0x243f47d1UL, 0xe78baf81UL} },\n    { &fnv_test_str[79], (Fnv64_t) {0x96a2ee7cUL, 0xb0968f70UL} },\n    { &fnv_test_str[80], (Fnv64_t) {0xf807855cUL, 0xb425a84fUL} },\n    { &fnv_test_str[81], (Fnv64_t) {0x751b56f9UL, 0x23e4e9e2UL} },\n    { &fnv_test_str[82], (Fnv64_t) {0xfe1396eaUL, 0x1a0b4eccUL} },\n    { &fnv_test_str[83], (Fnv64_t) {0xbb2c9004UL, 0x54abd453UL} },\n    { &fnv_test_str[84], (Fnv64_t) {0xb55ec3daUL, 0x08ba5f07UL} },\n    { &fnv_test_str[85], (Fnv64_t) {0x3006cb6eUL, 0x33735419UL} },\n    { &fnv_test_str[86], (Fnv64_t) {0x80aabd0bUL, 0xa430d846UL} },\n    { &fnv_test_str[87], (Fnv64_t) {0xa21f39b1UL, 0xa9bc8accUL} },\n    { &fnv_test_str[88], (Fnv64_t) {0x91cc682dUL, 0x69611964UL} },\n    { &fnv_test_str[89], (Fnv64_t) {0x4799dfe9UL, 0xad2bb177UL} },\n    { &fnv_test_str[90], (Fnv64_t) {0x91cc6314UL, 0x69611664UL} },\n    { &fnv_test_str[91], (Fnv64_t) {0x4a3b1236UL, 0x8d1bb390UL} },\n    { &fnv_test_str[92], (Fnv64_t) {0x91cc64c7UL, 0x69611764UL} },\n    { &fnv_test_str[93], (Fnv64_t) {0xf40434c7UL, 0xed205d87UL} },\n    { &fnv_test_str[94], (Fnv64_t) {0x91cc5faeUL, 0x69611464UL} },\n    { &fnv_test_str[95], (Fnv64_t) {0x44f8ad9cUL, 0xcd3baf5eUL} },\n    { &fnv_test_str[96], (Fnv64_t) {0x127cd6d8UL, 0xe3b36596UL} },\n    { &fnv_test_str[97], (Fnv64_t) {0xc8e8a646UL, 0xf77f1072UL} },\n    { &fnv_test_str[98], (Fnv64_t) {0x127cd372UL, 0xe3b36396UL} },\n    { &fnv_test_str[99], (Fnv64_t) {0x932ad458UL, 0x6067dce9UL} },\n    { &fnv_test_str[100], (Fnv64_t) {0x127cf208UL, 0xe3b37596UL} },\n    { &fnv_test_str[101], (Fnv64_t) {0x9fe83936UL, 0x4b7b10faUL} },\n    { &fnv_test_str[102], (Fnv64_t) {0x04d914beUL, 0xaabafe71UL} },\n    { &fnv_test_str[103], (Fnv64_t) {0x3cde3edaUL, 0xf4d3180bUL} },\n    { &fnv_test_str[104], (Fnv64_t) {0x04d9130bUL, 0xaabafd71UL} },\n    { &fnv_test_str[105], (Fnv64_t) {0x3cdb5bb1UL, 0xf4cfb20bUL} },\n    { &fnv_test_str[106], (Fnv64_t) {0x04d91158UL, 0xaabafc71UL} },\n    { &fnv_test_str[107], (Fnv64_t) {0x3cd87888UL, 0xf4cc4c0bUL} },\n    { &fnv_test_str[108], (Fnv64_t) {0xd2a8d3a7UL, 0xe729bac5UL} },\n    { &fnv_test_str[109], (Fnv64_t) {0xf4dfa4c5UL, 0x74bc0524UL} },\n    { &fnv_test_str[110], (Fnv64_t) {0xd2a5b352UL, 0xe72630c5UL} },\n    { &fnv_test_str[111], (Fnv64_t) {0xef8fb456UL, 0x6b983224UL} },\n    { &fnv_test_str[112], (Fnv64_t) {0xd2ae266dUL, 0xe73042c5UL} },\n    { &fnv_test_str[113], (Fnv64_t) {0xfdeb4b37UL, 0x8527e324UL} },\n    { &fnv_test_str[114], (Fnv64_t) {0xee952abcUL, 0x0a83c86fUL} },\n    { &fnv_test_str[115], (Fnv64_t) {0x67779d74UL, 0x73185232UL} },\n    { &fnv_test_str[116], (Fnv64_t) {0x6b8caca1UL, 0x3e66d3d5UL} },\n    { &fnv_test_str[117], (Fnv64_t) {0xc0095593UL, 0x956694a5UL} },\n    { &fnv_test_str[118], (Fnv64_t) {0xbb1a6fc8UL, 0xcac54572UL} },\n    { &fnv_test_str[119], (Fnv64_t) {0xedebf0d8UL, 0xa7a4c9f3UL} },\n    { &fnv_test_str[120], (Fnv64_t) {0xac17b143UL, 0x7829851fUL} },\n    { &fnv_test_str[121], (Fnv64_t) {0xf81bcf06UL, 0x2c8f4c9aUL} },\n    { &fnv_test_str[122], (Fnv64_t) {0x9740c732UL, 0xd34e3153UL} },\n    { &fnv_test_str[123], (Fnv64_t) {0x253d2db1UL, 0x3605a2acUL} },\n    { &fnv_test_str[124], (Fnv64_t) {0x46f4a3c3UL, 0x08c11b83UL} },\n    { &fnv_test_str[125], (Fnv64_t) {0x9ce8a6daUL, 0x6be39628UL} },\n    { &fnv_test_str[126], (Fnv64_t) {0x7fe794c5UL, 0xd9b957fbUL} },\n    { &fnv_test_str[127], (Fnv64_t) {0x04560a93UL, 0x05be33daUL} },\n    { &fnv_test_str[128], (Fnv64_t) {0x7ba9747cUL, 0x0957f157UL} },\n    { &fnv_test_str[129], (Fnv64_t) {0xc24fba57UL, 0xda2cc3acUL} },\n    { &fnv_test_str[130], (Fnv64_t) {0x5b29e7f0UL, 0x74136f18UL} },\n    { &fnv_test_str[131], (Fnv64_t) {0x0edb93b2UL, 0xb2f2b459UL} },\n    { &fnv_test_str[132], (Fnv64_t) {0x8b86ae04UL, 0xb3608fceUL} },\n    { &fnv_test_str[133], (Fnv64_t) {0x79359063UL, 0x4a3a8650UL} },\n    { &fnv_test_str[134], (Fnv64_t) {0x96880a50UL, 0x5b3a7ef4UL} },\n    { &fnv_test_str[135], (Fnv64_t) {0x3854c23bUL, 0x48fae316UL} },\n    { &fnv_test_str[136], (Fnv64_t) {0x476e0b9aUL, 0x07aaa640UL} },\n    { &fnv_test_str[137], (Fnv64_t) {0x383a687dUL, 0x2f653656UL} },\n    { &fnv_test_str[138], (Fnv64_t) {0x7599d79cUL, 0xa1031f8eUL} },\n    { &fnv_test_str[139], (Fnv64_t) {0x8ff92477UL, 0xa3190817UL} },\n    { &fnv_test_str[140], (Fnv64_t) {0x14c3fb83UL, 0x097edf3cUL} },\n    { &fnv_test_str[141], (Fnv64_t) {0xeaa0971bUL, 0xb51ca83fUL} },\n    { &fnv_test_str[142], (Fnv64_t) {0xd784f2e9UL, 0xdd3c0d96UL} },\n    { &fnv_test_str[143], (Fnv64_t) {0xea767d78UL, 0x86cd26a9UL} },\n    { &fnv_test_str[144], (Fnv64_t) {0x54a30c18UL, 0xe6b215ffUL} },\n    { &fnv_test_str[145], (Fnv64_t) {0xc5531093UL, 0xec5b06a1UL} },\n    { &fnv_test_str[146], (Fnv64_t) {0x9f9ec5e5UL, 0x45665a92UL} },\n    { &fnv_test_str[147], (Fnv64_t) {0xa9f10907UL, 0x8c7609b4UL} },\n    { &fnv_test_str[148], (Fnv64_t) {0x91f0d729UL, 0x89aac3a4UL} },\n    { &fnv_test_str[149], (Fnv64_t) {0xe0f4a403UL, 0x32ce6b26UL} },\n    { &fnv_test_str[150], (Fnv64_t) {0x02b53e01UL, 0x614ab44eUL} },\n    { &fnv_test_str[151], (Fnv64_t) {0x6eef3290UL, 0xfa6472ebUL} },\n    { &fnv_test_str[152], (Fnv64_t) {0x1948eb6aUL, 0x9e5d75ebUL} },\n    { &fnv_test_str[153], (Fnv64_t) {0xa8671852UL, 0xb6d12ad4UL} },\n    { &fnv_test_str[154], (Fnv64_t) {0xeba07af1UL, 0x88826f56UL} },\n    { &fnv_test_str[155], (Fnv64_t) {0x645bc0fdUL, 0x44535bf2UL} },\n    { &fnv_test_str[156], (Fnv64_t) {0xc21e3728UL, 0x169388ffUL} },\n    { &fnv_test_str[157], (Fnv64_t) {0x396d8224UL, 0xf68aac9eUL} },\n    { &fnv_test_str[158], (Fnv64_t) {0x472b3883UL, 0x8e87d7e7UL} },\n    { &fnv_test_str[159], (Fnv64_t) {0xa8b423deUL, 0x295c26caUL} },\n    { &fnv_test_str[160], (Fnv64_t) {0x92e72176UL, 0x322c8142UL} },\n    { &fnv_test_str[161], (Fnv64_t) {0xb8af7268UL, 0x8a06550eUL} },\n    { &fnv_test_str[162], (Fnv64_t) {0x661bcf71UL, 0xef86d60eUL} },\n    { &fnv_test_str[163], (Fnv64_t) {0x7f30ee54UL, 0x9e5426c8UL} },\n    { &fnv_test_str[164], (Fnv64_t) {0x26fd047eUL, 0xf1ea8aa8UL} },\n    { &fnv_test_str[165], (Fnv64_t) {0x642cb769UL, 0x0babaf9aUL} },\n    { &fnv_test_str[166], (Fnv64_t) {0x068d012eUL, 0x4b3341d4UL} },\n    { &fnv_test_str[167], (Fnv64_t) {0xc30a335cUL, 0xd15605cbUL} },\n    { &fnv_test_str[168], (Fnv64_t) {0xed8412e5UL, 0x5b21060aUL} },\n    { &fnv_test_str[169], (Fnv64_t) {0xce6f4227UL, 0x45e2cda1UL} },\n    { &fnv_test_str[170], (Fnv64_t) {0x033ad7d4UL, 0x50ae3745UL} },\n    { &fnv_test_str[171], (Fnv64_t) {0xd46bf414UL, 0xaa4588ceUL} },\n    { &fnv_test_str[172], (Fnv64_t) {0x4a95467eUL, 0xc1b0056cUL} },\n    { &fnv_test_str[173], (Fnv64_t) {0xde8b4089UL, 0x56576a71UL} },\n    { &fnv_test_str[174], (Fnv64_t) {0xa6dc927eUL, 0xbf20965fUL} },\n    { &fnv_test_str[175], (Fnv64_t) {0xc2040882UL, 0x569f8383UL} },\n    { &fnv_test_str[176], (Fnv64_t) {0xa08feca0UL, 0xe1e772fbUL} },\n    { &fnv_test_str[177], (Fnv64_t) {0x97138ac4UL, 0x4ced94afUL} },\n    { &fnv_test_str[178], (Fnv64_t) {0x337a82fbUL, 0xc4112ffbUL} },\n    { &fnv_test_str[179], (Fnv64_t) {0x1de38b7dUL, 0xd64a4fd4UL} },\n    { &fnv_test_str[180], (Fnv64_t) {0x9edebcbbUL, 0x4cfc3232UL} },\n    { &fnv_test_str[181], (Fnv64_t) {0x45050395UL, 0x08035644UL} },\n    { &fnv_test_str[182], (Fnv64_t) {0xf4642ffdUL, 0xaa1574ecUL} },\n    { &fnv_test_str[183], (Fnv64_t) {0x4cc315f9UL, 0x694bc4e5UL} },\n    { &fnv_test_str[184], (Fnv64_t) {0x3b011721UL, 0xa3d7cb27UL} },\n    { &fnv_test_str[185], (Fnv64_t) {0x6115bfa5UL, 0x577c2f8bUL} },\n    { &fnv_test_str[186], (Fnv64_t) {0x769fb4c1UL, 0xb7ec8c1aUL} },\n    { &fnv_test_str[187], (Fnv64_t) {0x3359ab19UL, 0x5d5cfce6UL} },\n    { &fnv_test_str[188], (Fnv64_t) {0xd65b5f71UL, 0x33b96c3cUL} },\n    { &fnv_test_str[189], (Fnv64_t) {0x80602bb9UL, 0xd8450977UL} },\n    { &fnv_test_str[190], (Fnv64_t) {0xd02da3d5UL, 0x84d47645UL} },\n    { &fnv_test_str[191], (Fnv64_t) {0xb58773a5UL, 0x83544f33UL} },\n    { &fnv_test_str[192], (Fnv64_t) {0x160836c5UL, 0x9175cbb2UL} },\n    { &fnv_test_str[193], (Fnv64_t) {0x75e72bc5UL, 0xc71b3bc1UL} },\n    { &fnv_test_str[194], (Fnv64_t) {0x222ec985UL, 0x636806acUL} },\n    { &fnv_test_str[195], (Fnv64_t) {0x50f52ed5UL, 0xb6ef0e69UL} },\n    { &fnv_test_str[196], (Fnv64_t) {0xf3dfdaa5UL, 0xead3d8a0UL} },\n    { &fnv_test_str[197], (Fnv64_t) {0x9a861ba5UL, 0x922908feUL} },\n    { &fnv_test_str[198], (Fnv64_t) {0x275fd5c5UL, 0x6d4821deUL} },\n    { &fnv_test_str[199], (Fnv64_t) {0x2bd816b5UL, 0x1fe3fce6UL} },\n    { &fnv_test_str[200], (Fnv64_t) {0xd6f70591UL, 0xc23e9fccUL} },\n    { &fnv_test_str[201], (Fnv64_t) {0xfe16b5b5UL, 0xc1af12bdUL} },\n    { &fnv_test_str[202], (Fnv64_t) {0x2f85e221UL, 0x39e9f18fUL} },\n    { NULL, (Fnv64_t) {0,0} }\n};\n#endif /* HAVE_64BIT_LONG_LONG */\n\n/* end of output generated by make vector.c */\n/*\n * insert the contents of vector.c above\n */\n\n\n/*\n * unknown_hash_type - report an unknown hash type error\n *\n * NOTE: Does not return.\n */\nvoid\nunknown_hash_type(char *prog, enum fnv_type type, int code)\n{\n    fprintf(stderr, \"%s: unknown or unexpexted hash type: %d\\n\", prog, type);\n    exit(code);\n}\n\n\n/*\n * print_fnv32 - print an FNV hash\n *\n * given:\n *\thval\t  the hash value to print\n *\tmask\t  lower bit mask\n *\tverbose\t  1 => print arg with hash\n *\targ\t  string or filename arg\n */\nvoid\nprint_fnv32(Fnv32_t hval, Fnv32_t mask, int verbose, char *arg)\n{\n    if (verbose) {\n\tprintf(\"0x%08lx %s\\n\", hval & mask, arg);\n    } else {\n\tprintf(\"0x%08lx\\n\", hval & mask);\n    }\n}\n\n\n/*\n * print_fnv64 - print an FNV hash\n *\n * given:\n *\thval\t  the hash value to print\n *\tmask\t  lower bit mask\n *\tverbose\t  1 => print arg with hash\n *\targ\t  string or filename arg\n */\nvoid\nprint_fnv64(Fnv64_t hval, Fnv64_t mask, int verbose, char *arg)\n{\n#if defined(HAVE_64BIT_LONG_LONG)\n    if (verbose) {\n\tprintf(\"0x%016llx %s\\n\", hval & mask, arg);\n    } else {\n\tprintf(\"0x%016llx\\n\", hval & mask);\n    }\n#else\n    if (verbose) {\n\tprintf(\"0x%08lx%08lx %s\\n\",\n\t       hval.w32[1] & mask.w32[1],\n\t       hval.w32[0] & mask.w32[0],\n\t       arg);\n    } else {\n\tprintf(\"0x%08lx%08lx\\n\",\n\t       hval.w32[1] & mask.w32[1],\n\t       hval.w32[0] & mask.w32[0]);\n    }\n#endif\n}\n"
  },
  {
    "path": "lib/lib8tion/LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2013 FastLED\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "lib/lib8tion/lib8tion.c",
    "content": "#define FASTLED_INTERNAL\n#include <stdint.h>\n\n#define RAND16_SEED  1337\nuint16_t rand16seed = RAND16_SEED;\n\n\n// memset8, memcpy8, memmove8:\n//  optimized avr replacements for the standard \"C\" library\n//  routines memset, memcpy, and memmove.\n//\n//  There are two techniques that make these routines\n//  faster than the standard avr-libc routines.\n//  First, the loops are unrolled 2X, meaning that\n//  the average loop overhead is cut in half.\n//  And second, the compare-and-branch at the bottom\n//  of each loop decrements the low byte of the\n//  counter, and if the carry is clear, it branches\n//  back up immediately.  Only if the low byte math\n//  causes carry do we bother to decrement the high\n//  byte and check that result for carry as well.\n//  Results for a 100-byte buffer are 20-40% faster\n//  than standard avr-libc, at a cost of a few extra\n//  bytes of code.\n\n#if defined(__AVR__)\n//__attribute__ ((noinline))\nvoid * memset8 ( void * ptr, uint8_t val, uint16_t num )\n{\n    asm volatile(\n         \"  movw r26, %[ptr]        \\n\\t\"\n         \"  sbrs %A[num], 0         \\n\\t\"\n         \"  rjmp Lseteven_%=        \\n\\t\"\n         \"  rjmp Lsetodd_%=         \\n\\t\"\n         \"Lsetloop_%=:              \\n\\t\"\n         \"  st X+, %[val]           \\n\\t\"\n         \"Lsetodd_%=:               \\n\\t\"\n         \"  st X+, %[val]           \\n\\t\"\n         \"Lseteven_%=:              \\n\\t\"\n         \"  subi %A[num], 2         \\n\\t\"\n         \"  brcc Lsetloop_%=        \\n\\t\"\n         \"  sbci %B[num], 0         \\n\\t\"\n         \"  brcc Lsetloop_%=        \\n\\t\"\n         : [num] \"+r\" (num)\n         : [ptr]  \"r\" (ptr),\n           [val]  \"r\" (val)\n         : \"memory\"\n         );\n    return ptr;\n}\n\n\n\n//__attribute__ ((noinline))\nvoid * memcpy8 ( void * dst, const void* src, uint16_t num )\n{\n    asm volatile(\n         \"  movw r30, %[src]        \\n\\t\"\n         \"  movw r26, %[dst]        \\n\\t\"\n         \"  sbrs %A[num], 0         \\n\\t\"\n         \"  rjmp Lcpyeven_%=        \\n\\t\"\n         \"  rjmp Lcpyodd_%=         \\n\\t\"\n         \"Lcpyloop_%=:              \\n\\t\"\n         \"  ld __tmp_reg__, Z+      \\n\\t\"\n         \"  st X+, __tmp_reg__      \\n\\t\"\n         \"Lcpyodd_%=:               \\n\\t\"\n         \"  ld __tmp_reg__, Z+      \\n\\t\"\n         \"  st X+, __tmp_reg__      \\n\\t\"\n         \"Lcpyeven_%=:              \\n\\t\"\n         \"  subi %A[num], 2         \\n\\t\"\n         \"  brcc Lcpyloop_%=        \\n\\t\"\n         \"  sbci %B[num], 0         \\n\\t\"\n         \"  brcc Lcpyloop_%=        \\n\\t\"\n         : [num] \"+r\" (num)\n         : [src] \"r\" (src),\n           [dst] \"r\" (dst)\n         : \"memory\"\n         );\n    return dst;\n}\n\n//__attribute__ ((noinline))\nvoid * memmove8 ( void * dst, const void* src, uint16_t num )\n{\n    if( src > dst) {\n        // if src > dst then we can use the forward-stepping memcpy8\n        return memcpy8( dst, src, num);\n    } else {\n        // if src < dst then we have to step backward:\n        dst = (char*)dst + num;\n        src = (char*)src + num;\n        asm volatile(\n             \"  movw r30, %[src]        \\n\\t\"\n             \"  movw r26, %[dst]        \\n\\t\"\n             \"  sbrs %A[num], 0         \\n\\t\"\n             \"  rjmp Lmoveven_%=        \\n\\t\"\n             \"  rjmp Lmovodd_%=         \\n\\t\"\n             \"Lmovloop_%=:              \\n\\t\"\n             \"  ld __tmp_reg__, -Z      \\n\\t\"\n             \"  st -X, __tmp_reg__      \\n\\t\"\n             \"Lmovodd_%=:               \\n\\t\"\n             \"  ld __tmp_reg__, -Z      \\n\\t\"\n             \"  st -X, __tmp_reg__      \\n\\t\"\n             \"Lmoveven_%=:              \\n\\t\"\n             \"  subi %A[num], 2         \\n\\t\"\n             \"  brcc Lmovloop_%=        \\n\\t\"\n             \"  sbci %B[num], 0         \\n\\t\"\n             \"  brcc Lmovloop_%=        \\n\\t\"\n             : [num] \"+r\" (num)\n             : [src] \"r\" (src),\n               [dst] \"r\" (dst)\n             : \"memory\"\n             );\n        return dst;\n    }\n}\n\n#endif /* AVR */\n\n\n\n\n#if 0\n// TEST / VERIFICATION CODE ONLY BELOW THIS POINT\n#include <Arduino.h>\n#include \"lib8tion.h\"\n\nvoid test1abs( int8_t i)\n{\n    Serial.print(\"abs(\"); Serial.print(i); Serial.print(\") = \");\n    int8_t j = abs8(i);\n    Serial.print(j); Serial.println(\" \");\n}\n\nvoid testabs()\n{\n    delay(5000);\n    for( int8_t q = -128; q != 127; q++) {\n        test1abs(q);\n    }\n    for(;;){};\n}\n\n\nvoid testmul8()\n{\n    delay(5000);\n    byte r, c;\n\n    Serial.println(\"mul8:\");\n    for( r = 0; r <= 20; r += 1) {\n        Serial.print(r); Serial.print(\" : \");\n        for( c = 0; c <= 20; c += 1) {\n            byte t;\n            t = mul8( r, c);\n            Serial.print(t); Serial.print(' ');\n        }\n        Serial.println(' ');\n    }\n    Serial.println(\"done.\");\n    for(;;){};\n}\n\n\nvoid testscale8()\n{\n    delay(5000);\n    byte r, c;\n\n    Serial.println(\"scale8:\");\n    for( r = 0; r <= 240; r += 10) {\n        Serial.print(r); Serial.print(\" : \");\n        for( c = 0; c <= 240; c += 10) {\n            byte t;\n            t = scale8( r, c);\n            Serial.print(t); Serial.print(' ');\n        }\n        Serial.println(' ');\n    }\n\n    Serial.println(' ');\n    Serial.println(\"scale8_video:\");\n\n    for( r = 0; r <= 100; r += 4) {\n        Serial.print(r); Serial.print(\" : \");\n        for( c = 0; c <= 100; c += 4) {\n            byte t;\n            t = scale8_video( r, c);\n            Serial.print(t); Serial.print(' ');\n        }\n        Serial.println(' ');\n    }\n\n    Serial.println(\"done.\");\n    for(;;){};\n}\n\n\n\nvoid testqadd8()\n{\n    delay(5000);\n    byte r, c;\n    for( r = 0; r <= 240; r += 10) {\n        Serial.print(r); Serial.print(\" : \");\n        for( c = 0; c <= 240; c += 10) {\n            byte t;\n            t = qadd8( r, c);\n            Serial.print(t); Serial.print(' ');\n        }\n        Serial.println(' ');\n    }\n    Serial.println(\"done.\");\n    for(;;){};\n}\n\nvoid testnscale8x3()\n{\n    delay(5000);\n    byte r, g, b, sc;\n    for( byte z = 0; z < 10; z++) {\n        r = random8(); g = random8(); b = random8(); sc = random8();\n\n        Serial.print(\"nscale8x3_video( \");\n        Serial.print(r); Serial.print(\", \");\n        Serial.print(g); Serial.print(\", \");\n        Serial.print(b); Serial.print(\", \");\n        Serial.print(sc); Serial.print(\") = [ \");\n\n        nscale8x3_video( r, g, b, sc);\n\n        Serial.print(r); Serial.print(\", \");\n        Serial.print(g); Serial.print(\", \");\n        Serial.print(b); Serial.print(\"]\");\n\n        Serial.println(' ');\n    }\n    Serial.println(\"done.\");\n    for(;;){};\n}\n\n#endif\n"
  },
  {
    "path": "lib/lib8tion/lib8tion.h",
    "content": "#ifndef __INC_LIB8TION_H\n#define __INC_LIB8TION_H\n\n/*\n\n Fast, efficient 8-bit math functions specifically\n designed for high-performance LED programming.\n\n Because of the AVR(Arduino) and ARM assembly language\n implementations provided, using these functions often\n results in smaller and faster code than the equivalent\n program using plain \"C\" arithmetic and logic.\n\n\n Included are:\n\n\n - Saturating unsigned 8-bit add and subtract.\n   Instead of wrapping around if an overflow occurs,\n   these routines just 'clamp' the output at a maxumum\n   of 255, or a minimum of 0.  Useful for adding pixel\n   values.  E.g., qadd8( 200, 100) = 255.\n\n     qadd8( i, j) == MIN( (i + j), 0xFF )\n     qsub8( i, j) == MAX( (i - j), 0 )\n\n - Saturating signed 8-bit (\"7-bit\") add.\n     qadd7( i, j) == MIN( (i + j), 0x7F)\n\n\n - Scaling (down) of unsigned 8- and 16- bit values.\n   Scaledown value is specified in 1/256ths.\n     scale8( i, sc) == (i * sc) / 256\n     scale16by8( i, sc) == (i * sc) / 256\n\n   Example: scaling a 0-255 value down into a\n   range from 0-99:\n     downscaled = scale8( originalnumber, 100);\n\n   A special version of scale8 is provided for scaling\n   LED brightness values, to make sure that they don't\n   accidentally scale down to total black at low\n   dimming levels, since that would look wrong:\n     scale8_video( i, sc) = ((i * sc) / 256) +? 1\n\n   Example: reducing an LED brightness by a\n   dimming factor:\n     new_bright = scale8_video( orig_bright, dimming);\n\n\n - Fast 8- and 16- bit unsigned random numbers.\n   Significantly faster than Arduino random(), but\n   also somewhat less random.  You can add entropy.\n     random8()       == random from 0..255\n     random8( n)     == random from 0..(N-1)\n     random8( n, m)  == random from N..(M-1)\n\n     random16()      == random from 0..65535\n     random16( n)    == random from 0..(N-1)\n     random16( n, m) == random from N..(M-1)\n\n     random16_set_seed( k)    ==  seed = k\n     random16_add_entropy( k) ==  seed += k\n\n\n - Absolute value of a signed 8-bit value.\n     abs8( i)     == abs( i)\n\n\n - 8-bit math operations which return 8-bit values.\n   These are provided mostly for completeness,\n   not particularly for performance.\n     mul8( i, j)  == (i * j) & 0xFF\n     add8( i, j)  == (i + j) & 0xFF\n     sub8( i, j)  == (i - j) & 0xFF\n\n\n - Fast 16-bit approximations of sin and cos.\n   Input angle is a uint16_t from 0-65535.\n   Output is a signed int16_t from -32767 to 32767.\n      sin16( x)  == sin( (x/32768.0) * pi) * 32767\n      cos16( x)  == cos( (x/32768.0) * pi) * 32767\n   Accurate to more than 99% in all cases.\n\n - Fast 8-bit approximations of sin and cos.\n   Input angle is a uint8_t from 0-255.\n   Output is an UNsigned uint8_t from 0 to 255.\n       sin8( x)  == (sin( (x/128.0) * pi) * 128) + 128\n       cos8( x)  == (cos( (x/128.0) * pi) * 128) + 128\n   Accurate to within about 2%.\n\n\n - Fast 8-bit \"easing in/out\" function.\n     ease8InOutCubic(x) == 3(x^i) - 2(x^3)\n     ease8InOutApprox(x) ==\n       faster, rougher, approximation of cubic easing\n     ease8InOutQuad(x) == quadratic (vs cubic) easing\n\n - Cubic, Quadratic, and Triangle wave functions.\n   Input is a uint8_t representing phase withing the wave,\n     similar to how sin8 takes an angle 'theta'.\n   Output is a uint8_t representing the amplitude of\n     the wave at that point.\n       cubicwave8( x)\n       quadwave8( x)\n       triwave8( x)\n\n - Square root for 16-bit integers.  About three times\n   faster and five times smaller than Arduino's built-in\n   generic 32-bit sqrt routine.\n     sqrt16( uint16_t x ) == sqrt( x)\n\n - Dimming and brightening functions for 8-bit\n   light values.\n     dim8_video( x)  == scale8_video( x, x)\n     dim8_raw( x)    == scale8( x, x)\n     dim8_lin( x)    == (x<128) ? ((x+1)/2) : scale8(x,x)\n     brighten8_video( x) == 255 - dim8_video( 255 - x)\n     brighten8_raw( x) == 255 - dim8_raw( 255 - x)\n     brighten8_lin( x) == 255 - dim8_lin( 255 - x)\n   The dimming functions in particular are suitable\n   for making LED light output appear more 'linear'.\n\n\n - Linear interpolation between two values, with the\n   fraction between them expressed as an 8- or 16-bit\n   fixed point fraction (fract8 or fract16).\n     lerp8by8(   fromU8, toU8, fract8 )\n     lerp16by8(  fromU16, toU16, fract8 )\n     lerp15by8(  fromS16, toS16, fract8 )\n       == from + (( to - from ) * fract8) / 256)\n     lerp16by16( fromU16, toU16, fract16 )\n       == from + (( to - from ) * fract16) / 65536)\n     map8( in, rangeStart, rangeEnd)\n       == map( in, 0, 255, rangeStart, rangeEnd);\n\n - Optimized memmove, memcpy, and memset, that are\n   faster than standard avr-libc 1.8.\n      memmove8( dest, src,  bytecount)\n      memcpy8(  dest, src,  bytecount)\n      memset8(  buf, value, bytecount)\n\n - Beat generators which return sine or sawtooth\n   waves in a specified number of Beats Per Minute.\n   Sine wave beat generators can specify a low and\n   high range for the output.  Sawtooth wave beat\n   generators always range 0-255 or 0-65535.\n     beatsin8( BPM, low8, high8)\n         = (sine(beatphase) * (high8-low8)) + low8\n     beatsin16( BPM, low16, high16)\n         = (sine(beatphase) * (high16-low16)) + low16\n     beatsin88( BPM88, low16, high16)\n         = (sine(beatphase) * (high16-low16)) + low16\n     beat8( BPM)  = 8-bit repeating sawtooth wave\n     beat16( BPM) = 16-bit repeating sawtooth wave\n     beat88( BPM88) = 16-bit repeating sawtooth wave\n   BPM is beats per minute in either simple form\n   e.g. 120, or Q8.8 fixed-point form.\n   BPM88 is beats per minute in ONLY Q8.8 fixed-point\n   form.\n\nLib8tion is pronounced like 'libation': lie-BAY-shun\n\n*/\n\n\n\n#include <stdint.h>\n\n#define LIB8STATIC static inline\n#define LIB8STATIC_ALWAYS_INLINE static inline\n\n#if !defined(__AVR__)\n#include <string.h>\n// for memmove, memcpy, and memset if not defined here\n#endif\n\n#if defined(__arm__)\n\n#if defined(FASTLED_TEENSY3)\n// Can use Cortex M4 DSP instructions\n#define QADD8_C 0\n#define QADD7_C 0\n#define QADD8_ARM_DSP_ASM 1\n#define QADD7_ARM_DSP_ASM 1\n#else\n// Generic ARM\n#define QADD8_C 1\n#define QADD7_C 1\n#endif\n\n#define QSUB8_C 1\n#define SCALE8_C 1\n#define SCALE16BY8_C 1\n#define SCALE16_C 1\n#define ABS8_C 1\n#define MUL8_C 1\n#define QMUL8_C 1\n#define ADD8_C 1\n#define SUB8_C 1\n#define EASE8_C 1\n#define AVG8_C 1\n#define AVG7_C 1\n#define AVG16_C 1\n#define AVG15_C 1\n#define BLEND8_C 1\n\n\n#elif defined(__AVR__)\n\n// AVR ATmega and friends Arduino\n\n#define QADD8_C 0\n#define QADD7_C 0\n#define QSUB8_C 0\n#define ABS8_C 0\n#define ADD8_C 0\n#define SUB8_C 0\n#define AVG8_C 0\n#define AVG7_C 0\n#define AVG16_C 0\n#define AVG15_C 0\n\n#define QADD8_AVRASM 1\n#define QADD7_AVRASM 1\n#define QSUB8_AVRASM 1\n#define ABS8_AVRASM 1\n#define ADD8_AVRASM 1\n#define SUB8_AVRASM 1\n#define AVG8_AVRASM 1\n#define AVG7_AVRASM 1\n#define AVG16_AVRASM 1\n#define AVG15_AVRASM 1\n\n// Note: these require hardware MUL instruction\n//       -- sorry, ATtiny!\n#if !defined(LIB8_ATTINY)\n#define SCALE8_C 0\n#define SCALE16BY8_C 0\n#define SCALE16_C 0\n#define MUL8_C 0\n#define QMUL8_C 0\n#define EASE8_C 0\n#define BLEND8_C 0\n#define SCALE8_AVRASM 1\n#define SCALE16BY8_AVRASM 1\n#define SCALE16_AVRASM 1\n#define MUL8_AVRASM 1\n#define QMUL8_AVRASM 1\n#define EASE8_AVRASM 1\n#define CLEANUP_R1_AVRASM 1\n#define BLEND8_AVRASM 1\n#else\n// On ATtiny, we just use C implementations\n#define SCALE8_C 1\n#define SCALE16BY8_C 1\n#define SCALE16_C 1\n#define MUL8_C 1\n#define QMUL8_C 1\n#define EASE8_C 1\n#define BLEND8_C 1\n#define SCALE8_AVRASM 0\n#define SCALE16BY8_AVRASM 0\n#define SCALE16_AVRASM 0\n#define MUL8_AVRASM 0\n#define QMUL8_AVRASM 0\n#define EASE8_AVRASM 0\n#define BLEND8_AVRASM 0\n#endif\n\n#else\n\n// unspecified architecture, so\n// no ASM, everything in C\n#define QADD8_C 1\n#define QADD7_C 1\n#define QSUB8_C 1\n#define SCALE8_C 1\n#define SCALE16BY8_C 1\n#define SCALE16_C 1\n#define ABS8_C 1\n#define MUL8_C 1\n#define QMUL8_C 1\n#define ADD8_C 1\n#define SUB8_C 1\n#define EASE8_C 1\n#define AVG8_C 1\n#define AVG7_C 1\n#define AVG16_C 1\n#define AVG15_C 1\n#define BLEND8_C 1\n\n#endif\n\n///@defgroup lib8tion Fast math functions\n///A variety of functions for working with numbers.\n///@{\n\n\n///////////////////////////////////////////////////////////////////////\n//\n// typdefs for fixed-point fractional types.\n//\n// sfract7 should be interpreted as signed 128ths.\n// fract8 should be interpreted as unsigned 256ths.\n// sfract15 should be interpreted as signed 32768ths.\n// fract16 should be interpreted as unsigned 65536ths.\n//\n// Example: if a fract8 has the value \"64\", that should be interpreted\n//          as 64/256ths, or one-quarter.\n//\n//\n//  fract8   range is 0 to 0.99609375\n//                 in steps of 0.00390625\n//\n//  sfract7  range is -0.9921875 to 0.9921875\n//                 in steps of 0.0078125\n//\n//  fract16  range is 0 to 0.99998474121\n//                 in steps of 0.00001525878\n//\n//  sfract15 range is -0.99996948242 to 0.99996948242\n//                 in steps of 0.00003051757\n//\n\n/// ANSI unsigned short _Fract.  range is 0 to 0.99609375\n///                 in steps of 0.00390625\ntypedef uint8_t   fract8;   ///< ANSI: unsigned short _Fract\n\n///  ANSI: signed short _Fract.  range is -0.9921875 to 0.9921875\n///                 in steps of 0.0078125\ntypedef int8_t    sfract7;  ///< ANSI: signed   short _Fract\n\n///  ANSI: unsigned _Fract.  range is 0 to 0.99998474121\n///                 in steps of 0.00001525878\ntypedef uint16_t  fract16;  ///< ANSI: unsigned       _Fract\n\n///  ANSI: signed _Fract.  range is -0.99996948242 to 0.99996948242\n///                 in steps of 0.00003051757\ntypedef int16_t   sfract15; ///< ANSI: signed         _Fract\n\n\n// accumXY types should be interpreted as X bits of integer,\n//         and Y bits of fraction.\n//         E.g., accum88 has 8 bits of int, 8 bits of fraction\n\ntypedef uint16_t  accum88;  ///< ANSI: unsigned short _Accum.  8 bits int, 8 bits fraction\ntypedef int16_t   saccum78; ///< ANSI: signed   short _Accum.  7 bits int, 8 bits fraction\ntypedef uint32_t  accum1616;///< ANSI: signed         _Accum. 16 bits int, 16 bits fraction\ntypedef int32_t   saccum1516;///< ANSI: signed         _Accum. 15 bits int, 16 bits fraction\ntypedef uint16_t  accum124; ///< no direct ANSI counterpart. 12 bits int, 4 bits fraction\ntypedef int32_t   saccum114;///< no direct ANSI counterpart. 1 bit int, 14 bits fraction\n\n\n\n#include \"math8.h\"\n#include \"scale8.h\"\n#include \"random8.h\"\n#include \"trig8.h\"\n\n///////////////////////////////////////////////////////////////////////\n\n\n\n\n\n\n\n///////////////////////////////////////////////////////////////////////\n//\n// float-to-fixed and fixed-to-float conversions\n//\n// Note that anything involving a 'float' on AVR will be slower.\n\n/// sfract15ToFloat: conversion from sfract15 fixed point to\n///                  IEEE754 32-bit float.\nLIB8STATIC float sfract15ToFloat( sfract15 y)\n{\n    return y / 32768.0;\n}\n\n/// conversion from IEEE754 float in the range (-1,1)\n///                  to 16-bit fixed point.  Note that the extremes of\n///                  one and negative one are NOT representable.  The\n///                  representable range is basically\nLIB8STATIC sfract15 floatToSfract15( float f)\n{\n    return f * 32768.0;\n}\n\n\n\n///////////////////////////////////////////////////////////////////////\n//\n// memmove8, memcpy8, and memset8:\n//   alternatives to memmove, memcpy, and memset that are\n//   faster on AVR than standard avr-libc 1.8\n\n#if defined(__AVR__)\nvoid * memmove8( void * dst, const void * src, uint16_t num );\nvoid * memcpy8 ( void * dst, const void * src, uint16_t num )  __attribute__ ((noinline));\nvoid * memset8 ( void * ptr, uint8_t value, uint16_t num ) __attribute__ ((noinline)) ;\n#else\n// on non-AVR platforms, these names just call standard libc.\n#define memmove8 memmove\n#define memcpy8 memcpy\n#define memset8 memset\n#endif\n\n\n///////////////////////////////////////////////////////////////////////\n//\n// linear interpolation, such as could be used for Perlin noise, etc.\n//\n\n// A note on the structure of the lerp functions:\n// The cases for b>a and b<=a are handled separately for\n// speed: without knowing the relative order of a and b,\n// the value (a-b) might be overflow the width of a or b,\n// and have to be promoted to a wider, slower type.\n// To avoid that, we separate the two cases, and are able\n// to do all the math in the same width as the arguments,\n// which is much faster and smaller on AVR.\n\n/// linear interpolation between two unsigned 8-bit values,\n/// with 8-bit fraction\nLIB8STATIC uint8_t lerp8by8( uint8_t a, uint8_t b, fract8 frac)\n{\n    uint8_t result;\n    if( b > a) {\n        uint8_t delta = b - a;\n        uint8_t scaled = scale8( delta, frac);\n        result = a + scaled;\n    } else {\n        uint8_t delta = a - b;\n        uint8_t scaled = scale8( delta, frac);\n        result = a - scaled;\n    }\n    return result;\n}\n\n/// linear interpolation between two unsigned 16-bit values,\n/// with 16-bit fraction\nLIB8STATIC uint16_t lerp16by16( uint16_t a, uint16_t b, fract16 frac)\n{\n    uint16_t result;\n    if( b > a ) {\n        uint16_t delta = b - a;\n        uint16_t scaled = scale16(delta, frac);\n        result = a + scaled;\n    } else {\n        uint16_t delta = a - b;\n        uint16_t scaled = scale16( delta, frac);\n        result = a - scaled;\n    }\n    return result;\n}\n\n/// linear interpolation between two unsigned 16-bit values,\n/// with 8-bit fraction\nLIB8STATIC uint16_t lerp16by8( uint16_t a, uint16_t b, fract8 frac)\n{\n    uint16_t result;\n    if( b > a) {\n        uint16_t delta = b - a;\n        uint16_t scaled = scale16by8( delta, frac);\n        result = a + scaled;\n    } else {\n        uint16_t delta = a - b;\n        uint16_t scaled = scale16by8( delta, frac);\n        result = a - scaled;\n    }\n    return result;\n}\n\n/// linear interpolation between two signed 15-bit values,\n/// with 8-bit fraction\nLIB8STATIC int16_t lerp15by8( int16_t a, int16_t b, fract8 frac)\n{\n    int16_t result;\n    if( b > a) {\n        uint16_t delta = b - a;\n        uint16_t scaled = scale16by8( delta, frac);\n        result = a + scaled;\n    } else {\n        uint16_t delta = a - b;\n        uint16_t scaled = scale16by8( delta, frac);\n        result = a - scaled;\n    }\n    return result;\n}\n\n/// linear interpolation between two signed 15-bit values,\n/// with 8-bit fraction\nLIB8STATIC int16_t lerp15by16( int16_t a, int16_t b, fract16 frac)\n{\n    int16_t result;\n    if( b > a) {\n        uint16_t delta = b - a;\n        uint16_t scaled = scale16( delta, frac);\n        result = a + scaled;\n    } else {\n        uint16_t delta = a - b;\n        uint16_t scaled = scale16( delta, frac);\n        result = a - scaled;\n    }\n    return result;\n}\n\n///  map8: map from one full-range 8-bit value into a narrower\n/// range of 8-bit values, possibly a range of hues.\n///\n/// E.g. map myValue into a hue in the range blue..purple..pink..red\n/// hue = map8( myValue, HUE_BLUE, HUE_RED);\n///\n/// Combines nicely with the waveform functions (like sin8, etc)\n/// to produce continuous hue gradients back and forth:\n///\n///          hue = map8( sin8( myValue), HUE_BLUE, HUE_RED);\n///\n/// Mathematically simiar to lerp8by8, but arguments are more\n/// like Arduino's \"map\"; this function is similar to\n///\n///          map( in, 0, 255, rangeStart, rangeEnd)\n///\n/// but faster and specifically designed for 8-bit values.\nLIB8STATIC uint8_t map8( uint8_t in, uint8_t rangeStart, uint8_t rangeEnd)\n{\n    uint8_t rangeWidth = rangeEnd - rangeStart;\n    uint8_t out = scale8( in, rangeWidth);\n    out += rangeStart;\n    return out;\n}\n\n\n///////////////////////////////////////////////////////////////////////\n//\n// easing functions; see http://easings.net\n//\n\n/// ease8InOutQuad: 8-bit quadratic ease-in / ease-out function\n///                Takes around 13 cycles on AVR\n#if EASE8_C == 1\nLIB8STATIC uint8_t ease8InOutQuad( uint8_t i)\n{\n    uint8_t j = i;\n    if( j & 0x80 ) {\n        j = 255 - j;\n    }\n    uint8_t jj  = scale8(  j, j);\n    uint8_t jj2 = jj << 1;\n    if( i & 0x80 ) {\n        jj2 = 255 - jj2;\n    }\n    return jj2;\n}\n\n#elif EASE8_AVRASM == 1\n// This AVR asm version of ease8InOutQuad preserves one more\n// low-bit of precision than the C version, and is also slightly\n// smaller and faster.\nLIB8STATIC uint8_t ease8InOutQuad(uint8_t val) {\n    uint8_t j=val;\n    asm volatile (\n      \"sbrc %[val], 7 \\n\"\n      \"com %[j]       \\n\"\n      \"mul %[j], %[j] \\n\"\n      \"add r0, %[j]   \\n\"\n      \"ldi %[j], 0    \\n\"\n      \"adc %[j], r1   \\n\"\n      \"lsl r0         \\n\" // carry = high bit of low byte of mul product\n      \"rol %[j]       \\n\" // j = (j * 2) + carry // preserve add'l bit of precision\n      \"sbrc %[val], 7 \\n\"\n      \"com %[j]       \\n\"\n      \"clr __zero_reg__   \\n\"\n      : [j] \"+&a\" (j)\n      : [val] \"a\" (val)\n      : \"r0\", \"r1\"\n      );\n    return j;\n}\n\n#else\n#error \"No implementation for ease8InOutQuad available.\"\n#endif\n\n/// ease16InOutQuad: 16-bit quadratic ease-in / ease-out function\n// C implementation at this point\nLIB8STATIC uint16_t ease16InOutQuad( uint16_t i)\n{\n    uint16_t j = i;\n    if( j & 0x8000 ) {\n        j = 65535 - j;\n    }\n    uint16_t jj  = scale16( j, j);\n    uint16_t jj2 = jj << 1;\n    if( i & 0x8000 ) {\n        jj2 = 65535 - jj2;\n    }\n    return jj2;\n}\n\n\n/// ease8InOutCubic: 8-bit cubic ease-in / ease-out function\n///                 Takes around 18 cycles on AVR\nLIB8STATIC fract8 ease8InOutCubic( fract8 i)\n{\n    uint8_t ii  = scale8_LEAVING_R1_DIRTY(  i, i);\n    uint8_t iii = scale8_LEAVING_R1_DIRTY( ii, i);\n\n    uint16_t r1 = (3 * (uint16_t)(ii)) - ( 2 * (uint16_t)(iii));\n\n    /* the code generated for the above *'s automatically\n       cleans up R1, so there's no need to explicitily call\n       cleanup_R1(); */\n\n    uint8_t result = r1;\n\n    // if we got \"256\", return 255:\n    if( r1 & 0x100 ) {\n        result = 255;\n    }\n    return result;\n}\n\n/// ease8InOutApprox: fast, rough 8-bit ease-in/ease-out function\n///                   shaped approximately like 'ease8InOutCubic',\n///                   it's never off by more than a couple of percent\n///                   from the actual cubic S-curve, and it executes\n///                   more than twice as fast.  Use when the cycles\n///                   are more important than visual smoothness.\n///                   Asm version takes around 7 cycles on AVR.\n\n#if EASE8_C == 1\nLIB8STATIC fract8 ease8InOutApprox( fract8 i)\n{\n    if( i < 64) {\n        // start with slope 0.5\n        i /= 2;\n    } else if( i > (255 - 64)) {\n        // end with slope 0.5\n        i = 255 - i;\n        i /= 2;\n        i = 255 - i;\n    } else {\n        // in the middle, use slope 192/128 = 1.5\n        i -= 64;\n        i += (i / 2);\n        i += 32;\n    }\n\n    return i;\n}\n\n#elif EASE8_AVRASM == 1\nLIB8STATIC uint8_t ease8InOutApprox( fract8 i)\n{\n    // takes around 7 cycles on AVR\n    asm volatile (\n        \"  subi %[i], 64         \\n\\t\"\n        \"  cpi  %[i], 128        \\n\\t\"\n        \"  brcc Lshift_%=        \\n\\t\"\n\n        // middle case\n        \"  mov __tmp_reg__, %[i] \\n\\t\"\n        \"  lsr __tmp_reg__       \\n\\t\"\n        \"  add %[i], __tmp_reg__ \\n\\t\"\n        \"  subi %[i], 224        \\n\\t\"\n        \"  rjmp Ldone_%=         \\n\\t\"\n\n        // start or end case\n        \"Lshift_%=:              \\n\\t\"\n        \"  lsr %[i]              \\n\\t\"\n        \"  subi %[i], 96         \\n\\t\"\n\n        \"Ldone_%=:               \\n\\t\"\n\n        : [i] \"+&a\" (i)\n        :\n        : \"r0\", \"r1\"\n        );\n    return i;\n}\n#else\n#error \"No implementation for ease8 available.\"\n#endif\n\n\n\n/// triwave8: triangle (sawtooth) wave generator.  Useful for\n///           turning a one-byte ever-increasing value into a\n///           one-byte value that oscillates up and down.\n///\n///           input         output\n///           0..127        0..254 (positive slope)\n///           128..255      254..0 (negative slope)\n///\n/// On AVR this function takes just three cycles.\n///\nLIB8STATIC uint8_t triwave8(uint8_t in)\n{\n    if( in & 0x80) {\n        in = 255 - in;\n    }\n    uint8_t out = in << 1;\n    return out;\n}\n\n\n// quadwave8 and cubicwave8: S-shaped wave generators (like 'sine').\n//           Useful for turning a one-byte 'counter' value into a\n//           one-byte oscillating value that moves smoothly up and down,\n//           with an 'acceleration' and 'deceleration' curve.\n//\n//           These are even faster than 'sin8', and have\n//           slightly different curve shapes.\n//\n\n/// quadwave8: quadratic waveform generator.  Spends just a little more\n///            time at the limits than 'sine' does.\nLIB8STATIC uint8_t quadwave8(uint8_t in)\n{\n    return ease8InOutQuad( triwave8( in));\n}\n\n/// cubicwave8: cubic waveform generator.  Spends visibly more time\n///             at the limits than 'sine' does.\nLIB8STATIC uint8_t cubicwave8(uint8_t in)\n{\n    return ease8InOutCubic( triwave8( in));\n}\n\n/// squarewave8: square wave generator.  Useful for\n///           turning a one-byte ever-increasing value\n///           into a one-byte value that is either 0 or 255.\n///           The width of the output 'pulse' is\n///           determined by the pulsewidth argument:\n///\n///~~~\n///           If pulsewidth is 255, output is always 255.\n///           If pulsewidth < 255, then\n///             if input < pulsewidth  then output is 255\n///             if input >= pulsewidth then output is 0\n///~~~\n///\n/// the output looking like:\n///\n///~~~\n///     255   +--pulsewidth--+\n///      .    |              |\n///      0    0              +--------(256-pulsewidth)--------\n///~~~\n///\n/// @param in\n/// @param pulsewidth\n/// @returns square wave output\nLIB8STATIC uint8_t squarewave8( uint8_t in, uint8_t pulsewidth)\n{\n    if( in < pulsewidth || (pulsewidth == 255)) {\n        return 255;\n    } else {\n        return 0;\n    }\n}\n\n\n// Beat generators - These functions produce waves at a given\n//                   number of 'beats per minute'.  Internally, they use\n//                   the Arduino function 'millis' to track elapsed time.\n//                   Accuracy is a bit better than one part in a thousand.\n//\n//       beat8( BPM ) returns an 8-bit value that cycles 'BPM' times\n//                    per minute, rising from 0 to 255, resetting to zero,\n//                    rising up again, etc..  The output of this function\n//                    is suitable for feeding directly into sin8, and cos8,\n//                    triwave8, quadwave8, and cubicwave8.\n//       beat16( BPM ) returns a 16-bit value that cycles 'BPM' times\n//                    per minute, rising from 0 to 65535, resetting to zero,\n//                    rising up again, etc.  The output of this function is\n//                    suitable for feeding directly into sin16 and cos16.\n//       beat88( BPM88) is the same as beat16, except that the BPM88 argument\n//                    MUST be in Q8.8 fixed point format, e.g. 120BPM must\n//                    be specified as 120*256 = 30720.\n//       beatsin8( BPM, uint8_t low, uint8_t high) returns an 8-bit value that\n//                    rises and falls in a sine wave, 'BPM' times per minute,\n//                    between the values of 'low' and 'high'.\n//       beatsin16( BPM, uint16_t low, uint16_t high) returns a 16-bit value\n//                    that rises and falls in a sine wave, 'BPM' times per\n//                    minute, between the values of 'low' and 'high'.\n//       beatsin88( BPM88, ...) is the same as beatsin16, except that the\n//                    BPM88 argument MUST be in Q8.8 fixed point format,\n//                    e.g. 120BPM must be specified as 120*256 = 30720.\n//\n//  BPM can be supplied two ways.  The simpler way of specifying BPM is as\n//  a simple 8-bit integer from 1-255, (e.g., \"120\").\n//  The more sophisticated way of specifying BPM allows for fractional\n//  \"Q8.8\" fixed point number (an 'accum88') with an 8-bit integer part and\n//  an 8-bit fractional part.  The easiest way to construct this is to multiply\n//  a floating point BPM value (e.g. 120.3) by 256, (e.g. resulting in 30796\n//  in this case), and pass that as the 16-bit BPM argument.\n//  \"BPM88\" MUST always be specified in Q8.8 format.\n//\n//  Originally designed to make an entire animation project pulse with brightness.\n//  For that effect, add this line just above your existing call to \"FastLED.show()\":\n//\n//     uint8_t bright = beatsin8( 60 /*BPM*/, 192 /*dimmest*/, 255 /*brightest*/ ));\n//     FastLED.setBrightness( bright );\n//     FastLED.show();\n//\n//  The entire animation will now pulse between brightness 192 and 255 once per second.\n\n\n// The beat generators need access to a millisecond counter.\n// On Arduino, this is \"millis()\".  On other platforms, you'll\n// need to provide a function with this signature:\n//   uint32_t get_millisecond_timer();\n// that provides similar functionality.\n// You can also force use of the get_millisecond_timer function\n// by #defining USE_GET_MILLISECOND_TIMER.\n#if (defined(ARDUINO) || defined(SPARK) || defined(FASTLED_HAS_MILLIS)) && !defined(USE_GET_MILLISECOND_TIMER)\n// Forward declaration of Arduino function 'millis'.\n//uint32_t millis();\n#define GET_MILLIS millis\n#else\nuint32_t get_millisecond_timer(void);\n#define GET_MILLIS get_millisecond_timer\n#endif\n\n// beat16 generates a 16-bit 'sawtooth' wave at a given BPM,\n///        with BPM specified in Q8.8 fixed-point format; e.g.\n///        for this function, 120 BPM MUST BE specified as\n///        120*256 = 30720.\n///        If you just want to specify \"120\", use beat16 or beat8.\nLIB8STATIC uint16_t beat88( accum88 beats_per_minute_88, uint32_t timebase)\n{\n    // BPM is 'beats per minute', or 'beats per 60000ms'.\n    // To avoid using the (slower) division operator, we\n    // want to convert 'beats per 60000ms' to 'beats per 65536ms',\n    // and then use a simple, fast bit-shift to divide by 65536.\n    //\n    // The ratio 65536:60000 is 279.620266667:256; we'll call it 280:256.\n    // The conversion is accurate to about 0.05%, more or less,\n    // e.g. if you ask for \"120 BPM\", you'll get about \"119.93\".\n    return (((GET_MILLIS()) - timebase) * beats_per_minute_88 * 280) >> 16;\n}\n\n/// beat16 generates a 16-bit 'sawtooth' wave at a given BPM\nLIB8STATIC uint16_t beat16( accum88 beats_per_minute, uint32_t timebase)\n{\n    // Convert simple 8-bit BPM's to full Q8.8 accum88's if needed\n    if( beats_per_minute < 256) beats_per_minute <<= 8;\n    return beat88(beats_per_minute, timebase);\n}\n\n/// beat8 generates an 8-bit 'sawtooth' wave at a given BPM\nLIB8STATIC uint8_t beat8( accum88 beats_per_minute, uint32_t timebase)\n{\n    return beat16( beats_per_minute, timebase) >> 8;\n}\n\n/// beatsin88 generates a 16-bit sine wave at a given BPM,\n///           that oscillates within a given range.\n///           For this function, BPM MUST BE SPECIFIED as\n///           a Q8.8 fixed-point value; e.g. 120BPM must be\n///           specified as 120*256 = 30720.\n///           If you just want to specify \"120\", use beatsin16 or beatsin8.\nLIB8STATIC uint16_t beatsin88( accum88 beats_per_minute_88, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset)\n{\n    uint16_t beat = beat88( beats_per_minute_88, timebase);\n    uint16_t beatsin = (sin16( beat + phase_offset) + 32768);\n    uint16_t rangewidth = highest - lowest;\n    uint16_t scaledbeat = scale16( beatsin, rangewidth);\n    uint16_t result = lowest + scaledbeat;\n    return result;\n}\n\n/// beatsin16 generates a 16-bit sine wave at a given BPM,\n///           that oscillates within a given range.\nLIB8STATIC uint16_t beatsin16(accum88 beats_per_minute, uint16_t lowest, uint16_t highest, uint32_t timebase, uint16_t phase_offset)\n{\n    uint16_t beat = beat16( beats_per_minute, timebase);\n    uint16_t beatsin = (sin16( beat + phase_offset) + 32768);\n    uint16_t rangewidth = highest - lowest;\n    uint16_t scaledbeat = scale16( beatsin, rangewidth);\n    uint16_t result = lowest + scaledbeat;\n    return result;\n}\n\n/// beatsin8 generates an 8-bit sine wave at a given BPM,\n///           that oscillates within a given range.\nLIB8STATIC uint8_t beatsin8( accum88 beats_per_minute, uint8_t lowest, uint8_t highest, uint32_t timebase, uint8_t phase_offset)\n{\n    uint8_t beat = beat8( beats_per_minute, timebase);\n    uint8_t beatsin = sin8( beat + phase_offset);\n    uint8_t rangewidth = highest - lowest;\n    uint8_t scaledbeat = scale8( beatsin, rangewidth);\n    uint8_t result = lowest + scaledbeat;\n    return result;\n}\n\n\n/// Return the current seconds since boot in a 16-bit value.  Used as part of the\n/// \"every N time-periods\" mechanism\nLIB8STATIC uint16_t seconds16(void)\n{\n    uint32_t ms = GET_MILLIS();\n    uint16_t s16;\n    s16 = ms / 1000;\n    return s16;\n}\n\n/// Return the current minutes since boot in a 16-bit value.  Used as part of the\n/// \"every N time-periods\" mechanism\nLIB8STATIC uint16_t minutes16(void)\n{\n    uint32_t ms = GET_MILLIS();\n    uint16_t m16;\n    m16 = (ms / (60000L)) & 0xFFFF;\n    return m16;\n}\n\n/// Return the current hours since boot in an 8-bit value.  Used as part of the\n/// \"every N time-periods\" mechanism\nLIB8STATIC uint8_t hours8(void)\n{\n    uint32_t ms = GET_MILLIS();\n    uint8_t h8;\n    h8 = (ms / (3600000L)) & 0xFF;\n    return h8;\n}\n\n///@}\n\n#endif\n"
  },
  {
    "path": "lib/lib8tion/math8.h",
    "content": "#ifndef __INC_LIB8TION_MATH_H\n#define __INC_LIB8TION_MATH_H\n\n#include \"scale8.h\"\n\n///@ingroup lib8tion\n\n///@defgroup Math Basic math operations\n/// Fast, efficient 8-bit math functions specifically\n/// designed for high-performance LED programming.\n///\n/// Because of the AVR(Arduino) and ARM assembly language\n/// implementations provided, using these functions often\n/// results in smaller and faster code than the equivalent\n/// program using plain \"C\" arithmetic and logic.\n///@{\n\n\n/// add one byte to another, saturating at 0xFF\n/// @param i - first byte to add\n/// @param j - second byte to add\n/// @returns the sum of i & j, capped at 0xFF\nLIB8STATIC_ALWAYS_INLINE uint8_t qadd8( uint8_t i, uint8_t j)\n{\n#if QADD8_C == 1\n    uint16_t t = i + j;\n    if (t > 255) t = 255;\n    return t;\n#elif QADD8_AVRASM == 1\n    asm volatile(\n         /* First, add j to i, conditioning the C flag */\n         \"add %0, %1    \\n\\t\"\n\n         /* Now test the C flag.\n           If C is clear, we branch around a load of 0xFF into i.\n           If C is set, we go ahead and load 0xFF into i.\n         */\n         \"brcc L_%=     \\n\\t\"\n         \"ldi %0, 0xFF  \\n\\t\"\n         \"L_%=: \"\n         : \"+a\" (i)\n         : \"a\"  (j) );\n    return i;\n#elif QADD8_ARM_DSP_ASM == 1\n    asm volatile( \"uqadd8 %0, %0, %1\" : \"+r\" (i) : \"r\" (j));\n    return i;\n#else\n#error \"No implementation for qadd8 available.\"\n#endif\n}\n\n/// Add one byte to another, saturating at 0x7F\n/// @param i - first byte to add\n/// @param j - second byte to add\n/// @returns the sum of i & j, capped at 0xFF\nLIB8STATIC_ALWAYS_INLINE int8_t qadd7( int8_t i, int8_t j)\n{\n#if QADD7_C == 1\n    int16_t t = i + j;\n    if (t > 127) t = 127;\n    return t;\n#elif QADD7_AVRASM == 1\n    asm volatile(\n         /* First, add j to i, conditioning the V flag */\n         \"add %0, %1    \\n\\t\"\n\n         /* Now test the V flag.\n          If V is clear, we branch around a load of 0x7F into i.\n          If V is set, we go ahead and load 0x7F into i.\n          */\n         \"brvc L_%=     \\n\\t\"\n         \"ldi %0, 0x7F  \\n\\t\"\n         \"L_%=: \"\n         : \"+a\" (i)\n         : \"a\"  (j) );\n\n    return i;\n#elif QADD7_ARM_DSP_ASM == 1\n    asm volatile( \"qadd8 %0, %0, %1\" : \"+r\" (i) : \"r\" (j));\n    return i;\n#else\n#error \"No implementation for qadd7 available.\"\n#endif\n}\n\n/// subtract one byte from another, saturating at 0x00\n/// @returns i - j with a floor of 0\nLIB8STATIC_ALWAYS_INLINE uint8_t qsub8( uint8_t i, uint8_t j)\n{\n#if QSUB8_C == 1\n    int16_t t = i - j;\n    if (t < 0) t = 0;\n    return t;\n#elif QSUB8_AVRASM == 1\n\n    asm volatile(\n         /* First, subtract j from i, conditioning the C flag */\n         \"sub %0, %1    \\n\\t\"\n\n         /* Now test the C flag.\n          If C is clear, we branch around a load of 0x00 into i.\n          If C is set, we go ahead and load 0x00 into i.\n          */\n         \"brcc L_%=     \\n\\t\"\n         \"ldi %0, 0x00  \\n\\t\"\n         \"L_%=: \"\n         : \"+a\" (i)\n         : \"a\"  (j) );\n\n    return i;\n#else\n#error \"No implementation for qsub8 available.\"\n#endif\n}\n\n/// add one byte to another, with one byte result\nLIB8STATIC_ALWAYS_INLINE uint8_t add8( uint8_t i, uint8_t j)\n{\n#if ADD8_C == 1\n    uint16_t t = i + j;\n    return t;\n#elif ADD8_AVRASM == 1\n    // Add j to i, period.\n    asm volatile( \"add %0, %1\" : \"+a\" (i) : \"a\" (j));\n    return i;\n#else\n#error \"No implementation for add8 available.\"\n#endif\n}\n\n/// add one byte to another, with one byte result\nLIB8STATIC_ALWAYS_INLINE uint16_t add8to16( uint8_t i, uint16_t j)\n{\n#if ADD8_C == 1\n    uint16_t t = i + j;\n    return t;\n#elif ADD8_AVRASM == 1\n    // Add i(one byte) to j(two bytes)\n    asm volatile( \"add %A[j], %[i]              \\n\\t\"\n                  \"adc %B[j], __zero_reg__      \\n\\t\"\n                 : [j] \"+a\" (j)\n                 : [i] \"a\"  (i)\n                 );\n    return i;\n#else\n#error \"No implementation for add8to16 available.\"\n#endif\n}\n\n\n/// subtract one byte from another, 8-bit result\nLIB8STATIC_ALWAYS_INLINE uint8_t sub8( uint8_t i, uint8_t j)\n{\n#if SUB8_C == 1\n    int16_t t = i - j;\n    return t;\n#elif SUB8_AVRASM == 1\n    // Subtract j from i, period.\n    asm volatile( \"sub %0, %1\" : \"+a\" (i) : \"a\" (j));\n    return i;\n#else\n#error \"No implementation for sub8 available.\"\n#endif\n}\n\n/// Calculate an integer average of two unsigned\n///       8-bit integer values (uint8_t).\n///       Fractional results are rounded down, e.g. avg8(20,41) = 30\nLIB8STATIC_ALWAYS_INLINE uint8_t avg8( uint8_t i, uint8_t j)\n{\n#if AVG8_C == 1\n    return (i + j) >> 1;\n#elif AVG8_AVRASM == 1\n    asm volatile(\n         /* First, add j to i, 9th bit overflows into C flag */\n         \"add %0, %1    \\n\\t\"\n         /* Divide by two, moving C flag into high 8th bit */\n         \"ror %0        \\n\\t\"\n         : \"+a\" (i)\n         : \"a\"  (j) );\n    return i;\n#else\n#error \"No implementation for avg8 available.\"\n#endif\n}\n\n/// Calculate an integer average of two unsigned\n///       16-bit integer values (uint16_t).\n///       Fractional results are rounded down, e.g. avg16(20,41) = 30\nLIB8STATIC_ALWAYS_INLINE uint16_t avg16( uint16_t i, uint16_t j)\n{\n#if AVG16_C == 1\n    return (uint32_t)((uint32_t)(i) + (uint32_t)(j)) >> 1;\n#elif AVG16_AVRASM == 1\n    asm volatile(\n                 /* First, add jLo (heh) to iLo, 9th bit overflows into C flag */\n                 \"add %A[i], %A[j]    \\n\\t\"\n                 /* Now, add C + jHi to iHi, 17th bit overflows into C flag */\n                 \"adc %B[i], %B[j]    \\n\\t\"\n                 /* Divide iHi by two, moving C flag into high 16th bit, old 9th bit now in C */\n                 \"ror %B[i]        \\n\\t\"\n                 /* Divide iLo by two, moving C flag into high 8th bit */\n                 \"ror %A[i]        \\n\\t\"\n                 : [i] \"+a\" (i)\n                 : [j] \"a\"  (j) );\n    return i;\n#else\n#error \"No implementation for avg16 available.\"\n#endif\n}\n\n\n/// Calculate an integer average of two signed 7-bit\n///       integers (int8_t)\n///       If the first argument is even, result is rounded down.\n///       If the first argument is odd, result is result up.\nLIB8STATIC_ALWAYS_INLINE int8_t avg7( int8_t i, int8_t j)\n{\n#if AVG7_C == 1\n    return ((i + j) >> 1) + (i & 0x1);\n#elif AVG7_AVRASM == 1\n    asm volatile(\n                 \"asr %1        \\n\\t\"\n                 \"asr %0        \\n\\t\"\n                 \"adc %0, %1    \\n\\t\"\n                 : \"+a\" (i)\n                 : \"a\"  (j) );\n    return i;\n#else\n#error \"No implementation for avg7 available.\"\n#endif\n}\n\n/// Calculate an integer average of two signed 15-bit\n///       integers (int16_t)\n///       If the first argument is even, result is rounded down.\n///       If the first argument is odd, result is result up.\nLIB8STATIC_ALWAYS_INLINE int16_t avg15( int16_t i, int16_t j)\n{\n#if AVG15_C == 1\n    return ((int32_t)((int32_t)(i) + (int32_t)(j)) >> 1) + (i & 0x1);\n#elif AVG15_AVRASM == 1\n    asm volatile(\n                 /* first divide j by 2, throwing away lowest bit */\n                 \"asr %B[j]          \\n\\t\"\n                 \"ror %A[j]          \\n\\t\"\n                 /* now divide i by 2, with lowest bit going into C */\n                 \"asr %B[i]          \\n\\t\"\n                 \"ror %A[i]          \\n\\t\"\n                 /* add j + C to i */\n                 \"adc %A[i], %A[j]   \\n\\t\"\n                 \"adc %B[i], %B[j]   \\n\\t\"\n                 : [i] \"+a\" (i)\n                 : [j] \"a\"  (j) );\n    return i;\n#else\n#error \"No implementation for avg15 available.\"\n#endif\n}\n\n\n///       Calculate the remainder of one unsigned 8-bit\n///       value divided by anoter, aka A % M.\n///       Implemented by repeated subtraction, which is\n///       very compact, and very fast if A is 'probably'\n///       less than M.  If A is a large multiple of M,\n///       the loop has to execute multiple times.  However,\n///       even in that case, the loop is only two\n///       instructions long on AVR, i.e., quick.\nLIB8STATIC_ALWAYS_INLINE uint8_t mod8( uint8_t a, uint8_t m)\n{\n#if defined(__AVR__)\n    asm volatile (\n                  \"L_%=:  sub %[a],%[m]    \\n\\t\"\n                  \"       brcc L_%=        \\n\\t\"\n                  \"       add %[a],%[m]    \\n\\t\"\n                  : [a] \"+r\" (a)\n                  : [m] \"r\"  (m)\n                  );\n#else\n    while( a >= m) a -= m;\n#endif\n    return a;\n}\n\n///          Add two numbers, and calculate the modulo\n///          of the sum and a third number, M.\n///          In other words, it returns (A+B) % M.\n///          It is designed as a compact mechanism for\n///          incrementing a 'mode' switch and wrapping\n///          around back to 'mode 0' when the switch\n///          goes past the end of the available range.\n///          e.g. if you have seven modes, this switches\n///          to the next one and wraps around if needed:\n///            mode = addmod8( mode, 1, 7);\n///LIB8STATIC_ALWAYS_INLINESee 'mod8' for notes on performance.\nLIB8STATIC uint8_t addmod8( uint8_t a, uint8_t b, uint8_t m)\n{\n#if defined(__AVR__)\n    asm volatile (\n                  \"       add %[a],%[b]    \\n\\t\"\n                  \"L_%=:  sub %[a],%[m]    \\n\\t\"\n                  \"       brcc L_%=        \\n\\t\"\n                  \"       add %[a],%[m]    \\n\\t\"\n                  : [a] \"+r\" (a)\n                  : [b] \"r\"  (b), [m] \"r\" (m)\n                  );\n#else\n    a += b;\n    while( a >= m) a -= m;\n#endif\n    return a;\n}\n\n///          Subtract two numbers, and calculate the modulo\n///          of the difference and a third number, M.\n///          In other words, it returns (A-B) % M.\n///          It is designed as a compact mechanism for\n///          incrementing a 'mode' switch and wrapping\n///          around back to 'mode 0' when the switch\n///          goes past the end of the available range.\n///          e.g. if you have seven modes, this switches\n///          to the next one and wraps around if needed:\n///            mode = addmod8( mode, 1, 7);\n///LIB8STATIC_ALWAYS_INLINESee 'mod8' for notes on performance.\nLIB8STATIC uint8_t submod8( uint8_t a, uint8_t b, uint8_t m)\n{\n#if defined(__AVR__)\n    asm volatile (\n                  \"       sub %[a],%[b]    \\n\\t\"\n                  \"L_%=:  sub %[a],%[m]    \\n\\t\"\n                  \"       brcc L_%=        \\n\\t\"\n                  \"       add %[a],%[m]    \\n\\t\"\n                  : [a] \"+r\" (a)\n                  : [b] \"r\"  (b), [m] \"r\" (m)\n                  );\n#else\n    a -= b;\n    while( a >= m) a -= m;\n#endif\n    return a;\n}\n\n/// 8x8 bit multiplication, with 8 bit result\nLIB8STATIC_ALWAYS_INLINE uint8_t mul8( uint8_t i, uint8_t j)\n{\n#if MUL8_C == 1\n    return ((uint16_t)i * (uint16_t)(j) ) & 0xFF;\n#elif MUL8_AVRASM == 1\n    asm volatile(\n         /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */\n         \"mul %0, %1          \\n\\t\"\n         /* Extract the LOW 8-bits (r0) */\n         \"mov %0, r0          \\n\\t\"\n         /* Restore r1 to \"0\"; it's expected to always be that */\n         \"clr __zero_reg__    \\n\\t\"\n         : \"+a\" (i)\n         : \"a\"  (j)\n         : \"r0\", \"r1\");\n\n    return i;\n#else\n#error \"No implementation for mul8 available.\"\n#endif\n}\n\n\n/// saturating 8x8 bit multiplication, with 8 bit result\n/// @returns the product of i * j, capping at 0xFF\nLIB8STATIC_ALWAYS_INLINE uint8_t qmul8( uint8_t i, uint8_t j)\n{\n#if QMUL8_C == 1\n    int p = ((uint16_t)i * (uint16_t)(j) );\n    if( p > 255) p = 255;\n    return p;\n#elif QMUL8_AVRASM == 1\n    asm volatile(\n                 /* Multiply 8-bit i * 8-bit j, giving 16-bit r1,r0 */\n                 \"  mul %0, %1          \\n\\t\"\n                 /* If high byte of result is zero, all is well. */\n                 \"  tst r1              \\n\\t\"\n                 \"  breq Lnospill_%=    \\n\\t\"\n                 /* If high byte of result > 0, saturate low byte to 0xFF */\n                 \"  ldi %0,0xFF         \\n\\t\"\n                 \"  rjmp Ldone_%=       \\n\\t\"\n                 \"Lnospill_%=:          \\n\\t\"\n                 /* Extract the LOW 8-bits (r0) */\n                 \"  mov %0, r0          \\n\\t\"\n                 \"Ldone_%=:             \\n\\t\"\n                 /* Restore r1 to \"0\"; it's expected to always be that */\n                 \"  clr __zero_reg__    \\n\\t\"\n                 : \"+a\" (i)\n                 : \"a\"  (j)\n                 : \"r0\", \"r1\");\n\n    return i;\n#else\n#error \"No implementation for qmul8 available.\"\n#endif\n}\n\n\n/// take abs() of a signed 8-bit uint8_t\nLIB8STATIC_ALWAYS_INLINE int8_t abs8( int8_t i)\n{\n#if ABS8_C == 1\n    if( i < 0) i = -i;\n    return i;\n#elif ABS8_AVRASM == 1\n\n\n    asm volatile(\n         /* First, check the high bit, and prepare to skip if it's clear */\n         \"sbrc %0, 7 \\n\"\n\n         /* Negate the value */\n         \"neg %0     \\n\"\n\n         : \"+r\" (i) : \"r\" (i) );\n    return i;\n#else\n#error \"No implementation for abs8 available.\"\n#endif\n}\n\n///         square root for 16-bit integers\n///         About three times faster and five times smaller\n///         than Arduino's general sqrt on AVR.\nLIB8STATIC uint8_t sqrt16(uint16_t x)\n{\n    if( x <= 1) {\n        return x;\n    }\n\n    uint8_t low = 1; // lower bound\n    uint8_t hi, mid;\n\n    if( x > 7904) {\n        hi = 255;\n    } else {\n        hi = (x >> 5) + 8; // initial estimate for upper bound\n    }\n\n    do {\n        mid = (low + hi) >> 1;\n        if ((uint16_t)(mid * mid) > x) {\n            hi = mid - 1;\n        } else {\n            if( mid == 255) {\n                return 255;\n            }\n            low = mid + 1;\n        }\n    } while (hi >= low);\n\n    return low - 1;\n}\n\n/// blend a variable proproportion(0-255) of one byte to another\n/// @param a - the starting byte value\n/// @param b - the byte value to blend toward\n/// @param amountOfB - the proportion (0-255) of b to blend\n/// @returns a byte value between a and b, inclusive\n#if (FASTLED_BLEND_FIXED == 1)\nLIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB)\n{\n#if BLEND8_C == 1\n    uint16_t partial;\n    uint8_t result;\n\n    uint8_t amountOfA = 255 - amountOfB;\n\n    partial = (a * amountOfA);\n#if (FASTLED_SCALE8_FIXED == 1)\n    partial += a;\n    //partial = add8to16( a, partial);\n#endif\n\n    partial += (b * amountOfB);\n#if (FASTLED_SCALE8_FIXED == 1)\n    partial += b;\n    //partial = add8to16( b, partial);\n#endif\n\n    result = partial >> 8;\n\n    return result;\n\n#elif BLEND8_AVRASM == 1\n    uint16_t partial;\n    uint8_t result;\n\n    asm volatile (\n        /* partial = b * amountOfB */\n        \"  mul %[b], %[amountOfB]        \\n\\t\"\n        \"  movw %A[partial], r0          \\n\\t\"\n\n        /* amountOfB (aka amountOfA) = 255 - amountOfB */\n        \"  com %[amountOfB]              \\n\\t\"\n\n        /* partial += a * amountOfB (aka amountOfA) */\n        \"  mul %[a], %[amountOfB]        \\n\\t\"\n\n        \"  add %A[partial], r0           \\n\\t\"\n        \"  adc %B[partial], r1           \\n\\t\"\n\n        \"  clr __zero_reg__              \\n\\t\"\n\n#if (FASTLED_SCALE8_FIXED == 1)\n        /* partial += a */\n        \"  add %A[partial], %[a]         \\n\\t\"\n        \"  adc %B[partial], __zero_reg__ \\n\\t\"\n\n        // partial += b\n        \"  add %A[partial], %[b]         \\n\\t\"\n        \"  adc %B[partial], __zero_reg__ \\n\\t\"\n#endif\n\n        : [partial] \"=r\" (partial),\n          [amountOfB] \"+a\" (amountOfB)\n        : [a] \"a\" (a),\n          [b] \"a\" (b)\n        : \"r0\", \"r1\"\n    );\n\n    result = partial >> 8;\n\n    return result;\n\n#else\n#error \"No implementation for blend8 available.\"\n#endif\n}\n\n#else\nLIB8STATIC uint8_t blend8( uint8_t a, uint8_t b, uint8_t amountOfB)\n{\n    // This version loses precision in the integer math\n    // and can actually return results outside of the range\n    // from a to b.  Its use is not recommended.\n    uint8_t result;\n    uint8_t amountOfA = 255 - amountOfB;\n    result = scale8_LEAVING_R1_DIRTY( a, amountOfA)\n           + scale8_LEAVING_R1_DIRTY( b, amountOfB);\n    cleanup_R1();\n    return result;\n}\n#endif\n\n\n///@}\n#endif\n"
  },
  {
    "path": "lib/lib8tion/random8.h",
    "content": "#ifndef __INC_LIB8TION_RANDOM_H\n#define __INC_LIB8TION_RANDOM_H\n///@ingroup lib8tion\n\n///@defgroup Random Fast random number generators\n/// Fast 8- and 16- bit unsigned random numbers.\n///  Significantly faster than Arduino random(), but\n///  also somewhat less random.  You can add entropy.\n///@{\n\n// X(n+1) = (2053 * X(n)) + 13849)\n#define FASTLED_RAND16_2053  ((uint16_t)(2053))\n#define FASTLED_RAND16_13849 ((uint16_t)(13849))\n\n/// random number seed\nextern uint16_t rand16seed;// = RAND16_SEED;\n\n/// Generate an 8-bit random number\nLIB8STATIC uint8_t random8(void)\n{\n    rand16seed = (rand16seed * FASTLED_RAND16_2053) + FASTLED_RAND16_13849;\n    // return the sum of the high and low bytes, for better\n    //  mixing and non-sequential correlation\n    return (uint8_t)(((uint8_t)(rand16seed & 0xFF)) +\n                     ((uint8_t)(rand16seed >> 8)));\n}\n\n/// Generate a 16 bit random number\nLIB8STATIC uint16_t random16(void)\n{\n    rand16seed = (rand16seed * FASTLED_RAND16_2053) + FASTLED_RAND16_13849;\n    return rand16seed;\n}\n\n/// Generate an 8-bit random number between 0 and lim\n/// @param lim the upper bound for the result\nLIB8STATIC uint8_t random8_max(uint8_t lim)\n{\n    uint8_t r = random8();\n    r = (r*lim) >> 8;\n    return r;\n}\n\n/// Generate an 8-bit random number in the given range\n/// @param min the lower bound for the random number\n/// @param lim the upper bound for the random number\nLIB8STATIC uint8_t random8_min_max(uint8_t min, uint8_t lim)\n{\n    uint8_t delta = lim - min;\n    uint8_t r = random8_max(delta) + min;\n    return r;\n}\n\n/// Generate an 16-bit random number between 0 and lim\n/// @param lim the upper bound for the result\nLIB8STATIC uint16_t random16_max(uint16_t lim)\n{\n    uint16_t r = random16();\n    uint32_t p = (uint32_t)lim * (uint32_t)r;\n    r = p >> 16;\n    return r;\n}\n\n/// Generate an 16-bit random number in the given range\n/// @param min the lower bound for the random number\n/// @param lim the upper bound for the random number\nLIB8STATIC uint16_t random16_min_max( uint16_t min, uint16_t lim)\n{\n    uint16_t delta = lim - min;\n    uint16_t r = random16_max(delta) + min;\n    return r;\n}\n\n/// Set the 16-bit seed used for the random number generator\nLIB8STATIC void random16_set_seed(uint16_t seed)\n{\n    rand16seed = seed;\n}\n\n/// Get the current seed value for the random number generator\nLIB8STATIC uint16_t random16_get_seed(void)\n{\n    return rand16seed;\n}\n\n/// Add entropy into the random number generator\nLIB8STATIC void random16_add_entropy(uint16_t entropy)\n{\n    rand16seed += entropy;\n}\n\n///@}\n\n#endif\n"
  },
  {
    "path": "lib/lib8tion/scale8.h",
    "content": "#ifndef __INC_LIB8TION_SCALE_H\n#define __INC_LIB8TION_SCALE_H\n\n///@ingroup lib8tion\n\n///@defgroup Scaling Scaling functions\n/// Fast, efficient 8-bit scaling functions specifically\n/// designed for high-performance LED programming.\n///\n/// Because of the AVR(Arduino) and ARM assembly language\n/// implementations provided, using these functions often\n/// results in smaller and faster code than the equivalent\n/// program using plain \"C\" arithmetic and logic.\n///@{\n\n///  scale one byte by a second one, which is treated as\n///  the numerator of a fraction whose denominator is 256\n///  In other words, it computes i * (scale / 256)\n///  4 clocks AVR with MUL, 2 clocks ARM\nLIB8STATIC_ALWAYS_INLINE uint8_t scale8( uint8_t i, fract8 scale)\n{\n#if SCALE8_C == 1\n#if (FASTLED_SCALE8_FIXED == 1)\n    return (((uint16_t)i) * (1+(uint16_t)(scale))) >> 8;\n#else\n    return ((uint16_t)i * (uint16_t)(scale) ) >> 8;\n#endif\n#elif SCALE8_AVRASM == 1\n#if defined(LIB8_ATTINY)\n#if (FASTLED_SCALE8_FIXED == 1)\n    uint8_t work=i;\n#else\n    uint8_t work=0;\n#endif\n    uint8_t cnt=0x80;\n    asm volatile(\n#if (FASTLED_SCALE8_FIXED == 1)\n        \"  inc %[scale]                 \\n\\t\"\n        \"  breq DONE_%=                 \\n\\t\"\n        \"  clr %[work]                  \\n\\t\"\n#endif\n        \"LOOP_%=:                       \\n\\t\"\n        /*\"  sbrc %[scale], 0             \\n\\t\"\n        \"  add %[work], %[i]            \\n\\t\"\n        \"  ror %[work]                  \\n\\t\"\n        \"  lsr %[scale]                 \\n\\t\"\n        \"  clc                          \\n\\t\"*/\n        \"  sbrc %[scale], 0             \\n\\t\"\n        \"  add %[work], %[i]            \\n\\t\"\n        \"  ror %[work]                  \\n\\t\"\n        \"  lsr %[scale]                 \\n\\t\"\n        \"  lsr %[cnt]                   \\n\\t\"\n        \"brcc LOOP_%=                   \\n\\t\"\n        \"DONE_%=:                       \\n\\t\"\n        : [work] \"+r\" (work), [cnt] \"+r\" (cnt)\n        : [scale] \"r\" (scale), [i] \"r\" (i)\n        :\n      );\n    return work;\n#else\n    asm volatile(\n#if (FASTLED_SCALE8_FIXED==1)\n        // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0\n        \"mul %0, %1          \\n\\t\"\n        // Add i to r0, possibly setting the carry flag\n        \"add r0, %0         \\n\\t\"\n        // load the immediate 0 into i (note, this does _not_ touch any flags)\n        \"ldi %0, 0x00       \\n\\t\"\n        // walk and chew gum at the same time\n        \"adc %0, r1          \\n\\t\"\n#else\n         /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */\n         \"mul %0, %1          \\n\\t\"\n         /* Move the high 8-bits of the product (r1) back to i */\n         \"mov %0, r1          \\n\\t\"\n         /* Restore r1 to \"0\"; it's expected to always be that */\n#endif\n         \"clr __zero_reg__    \\n\\t\"\n\n         : \"+a\" (i)      /* writes to i */\n         : \"a\"  (scale)  /* uses scale */\n         : \"r0\", \"r1\"    /* clobbers r0, r1 */ );\n\n    /* Return the result */\n    return i;\n#endif\n#else\n#error \"No implementation for scale8 available.\"\n#endif\n}\n\n\n///  The \"video\" version of scale8 guarantees that the output will\n///  be only be zero if one or both of the inputs are zero.  If both\n///  inputs are non-zero, the output is guaranteed to be non-zero.\n///  This makes for better 'video'/LED dimming, at the cost of\n///  several additional cycles.\nLIB8STATIC_ALWAYS_INLINE uint8_t scale8_video( uint8_t i, fract8 scale)\n{\n#if SCALE8_C == 1 || defined(LIB8_ATTINY)\n    uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);\n    // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;\n    // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;\n    return j;\n#elif SCALE8_AVRASM == 1\n    uint8_t j=0;\n    asm volatile(\n        \"  tst %[i]\\n\\t\"\n        \"  breq L_%=\\n\\t\"\n        \"  mul %[i], %[scale]\\n\\t\"\n        \"  mov %[j], r1\\n\\t\"\n        \"  clr __zero_reg__\\n\\t\"\n        \"  cpse %[scale], r1\\n\\t\"\n        \"  subi %[j], 0xFF\\n\\t\"\n        \"L_%=: \\n\\t\"\n        : [j] \"+a\" (j)\n        : [i] \"a\" (i), [scale] \"a\" (scale)\n        : \"r0\", \"r1\");\n\n    return j;\n    // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;\n    // asm volatile(\n    //      \"      tst %0           \\n\"\n    //      \"      breq L_%=        \\n\"\n    //      \"      mul %0, %1       \\n\"\n    //      \"      mov %0, r1       \\n\"\n    //      \"      add %0, %2       \\n\"\n    //      \"      clr __zero_reg__ \\n\"\n    //      \"L_%=:                  \\n\"\n\n    //      : \"+a\" (i)\n    //      : \"a\" (scale), \"a\" (nonzeroscale)\n    //      : \"r0\", \"r1\");\n\n    // // Return the result\n    // return i;\n#else\n#error \"No implementation for scale8_video available.\"\n#endif\n}\n\n\n/// This version of scale8 does not clean up the R1 register on AVR\n/// If you are doing several 'scale8's in a row, use this, and\n/// then explicitly call cleanup_R1.\nLIB8STATIC_ALWAYS_INLINE uint8_t scale8_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)\n{\n#if SCALE8_C == 1\n#if (FASTLED_SCALE8_FIXED == 1)\n    return (((uint16_t)i) * ((uint16_t)(scale)+1)) >> 8;\n#else\n    return ((int)i * (int)(scale) ) >> 8;\n#endif\n#elif SCALE8_AVRASM == 1\n    asm volatile(\n      #if (FASTLED_SCALE8_FIXED==1)\n              // Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0\n              \"mul %0, %1          \\n\\t\"\n              // Add i to r0, possibly setting the carry flag\n              \"add r0, %0         \\n\\t\"\n              // load the immediate 0 into i (note, this does _not_ touch any flags)\n              \"ldi %0, 0x00       \\n\\t\"\n              // walk and chew gum at the same time\n              \"adc %0, r1          \\n\\t\"\n      #else\n         /* Multiply 8-bit i * 8-bit scale, giving 16-bit r1,r0 */\n         \"mul %0, %1    \\n\\t\"\n         /* Move the high 8-bits of the product (r1) back to i */\n         \"mov %0, r1    \\n\\t\"\n      #endif\n         /* R1 IS LEFT DIRTY HERE; YOU MUST ZERO IT OUT YOURSELF  */\n         /* \"clr __zero_reg__    \\n\\t\" */\n\n         : \"+a\" (i)      /* writes to i */\n         : \"a\"  (scale)  /* uses scale */\n         : \"r0\", \"r1\"    /* clobbers r0, r1 */ );\n\n    // Return the result\n    return i;\n#else\n#error \"No implementation for scale8_LEAVING_R1_DIRTY available.\"\n#endif\n}\n\n\n/// This version of scale8_video does not clean up the R1 register on AVR\n/// If you are doing several 'scale8_video's in a row, use this, and\n/// then explicitly call cleanup_R1.\nLIB8STATIC_ALWAYS_INLINE uint8_t scale8_video_LEAVING_R1_DIRTY( uint8_t i, fract8 scale)\n{\n#if SCALE8_C == 1 || defined(LIB8_ATTINY)\n    uint8_t j = (((int)i * (int)scale) >> 8) + ((i&&scale)?1:0);\n    // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;\n    // uint8_t j = (i == 0) ? 0 : (((int)i * (int)(scale) ) >> 8) + nonzeroscale;\n    return j;\n#elif SCALE8_AVRASM == 1\n    uint8_t j=0;\n    asm volatile(\n        \"  tst %[i]\\n\\t\"\n        \"  breq L_%=\\n\\t\"\n        \"  mul %[i], %[scale]\\n\\t\"\n        \"  mov %[j], r1\\n\\t\"\n        \"  breq L_%=\\n\\t\"\n        \"  subi %[j], 0xFF\\n\\t\"\n        \"L_%=: \\n\\t\"\n        : [j] \"+a\" (j)\n        : [i] \"a\" (i), [scale] \"a\" (scale)\n        : \"r0\", \"r1\");\n\n    return j;\n    // uint8_t nonzeroscale = (scale != 0) ? 1 : 0;\n    // asm volatile(\n    //      \"      tst %0           \\n\"\n    //      \"      breq L_%=        \\n\"\n    //      \"      mul %0, %1       \\n\"\n    //      \"      mov %0, r1       \\n\"\n    //      \"      add %0, %2       \\n\"\n    //      \"      clr __zero_reg__ \\n\"\n    //      \"L_%=:                  \\n\"\n\n    //      : \"+a\" (i)\n    //      : \"a\" (scale), \"a\" (nonzeroscale)\n    //      : \"r0\", \"r1\");\n\n    // // Return the result\n    // return i;\n#else\n#error \"No implementation for scale8_video_LEAVING_R1_DIRTY available.\"\n#endif\n}\n\n/// Clean up the r1 register after a series of *LEAVING_R1_DIRTY calls\nLIB8STATIC_ALWAYS_INLINE void cleanup_R1(void)\n{\n#if CLEANUP_R1_AVRASM == 1\n    // Restore r1 to \"0\"; it's expected to always be that\n    asm volatile( \"clr __zero_reg__  \\n\\t\" : : : \"r1\" );\n#endif\n}\n\n\n/// scale a 16-bit unsigned value by an 8-bit value,\n///         considered as numerator of a fraction whose denominator\n///         is 256. In other words, it computes i * (scale / 256)\n\nLIB8STATIC_ALWAYS_INLINE uint16_t scale16by8( uint16_t i, fract8 scale )\n{\n#if SCALE16BY8_C == 1\n    uint16_t result;\n#if FASTLED_SCALE8_FIXED == 1\n    result = (i * (1+((uint16_t)scale))) >> 8;\n#else\n    result = (i * scale) / 256;\n#endif\n    return result;\n#elif SCALE16BY8_AVRASM == 1\n#if FASTLED_SCALE8_FIXED == 1\n    uint16_t result = 0;\n    asm volatile(\n                 // result.A = HighByte( (i.A x scale) + i.A )\n                 \"  mul %A[i], %[scale]                 \\n\\t\"\n                 \"  add r0, %A[i]                       \\n\\t\"\n            //   \"  adc r1, [zero]                      \\n\\t\"\n            //   \"  mov %A[result], r1                  \\n\\t\"\n                 \"  adc %A[result], r1                  \\n\\t\"\n\n                 // result.A-B += i.B x scale\n                 \"  mul %B[i], %[scale]                 \\n\\t\"\n                 \"  add %A[result], r0                  \\n\\t\"\n                 \"  adc %B[result], r1                  \\n\\t\"\n\n                 // cleanup r1\n                 \"  clr __zero_reg__                    \\n\\t\"\n\n                 // result.A-B += i.B\n                 \"  add %A[result], %B[i]               \\n\\t\"\n                 \"  adc %B[result], __zero_reg__        \\n\\t\"\n\n                 : [result] \"+r\" (result)\n                 : [i] \"r\" (i), [scale] \"r\" (scale)\n                 : \"r0\", \"r1\"\n                 );\n    return result;\n#else\n    uint16_t result = 0;\n    asm volatile(\n         // result.A = HighByte(i.A x j )\n         \"  mul %A[i], %[scale]                 \\n\\t\"\n         \"  mov %A[result], r1                  \\n\\t\"\n         //\"  clr %B[result]                      \\n\\t\"\n\n         // result.A-B += i.B x j\n         \"  mul %B[i], %[scale]                 \\n\\t\"\n         \"  add %A[result], r0                  \\n\\t\"\n         \"  adc %B[result], r1                  \\n\\t\"\n\n         // cleanup r1\n         \"  clr __zero_reg__                    \\n\\t\"\n\n         : [result] \"+r\" (result)\n         : [i] \"r\" (i), [scale] \"r\" (scale)\n         : \"r0\", \"r1\"\n         );\n    return result;\n#endif\n#else\n    #error \"No implementation for scale16by8 available.\"\n#endif\n}\n\n/// scale a 16-bit unsigned value by a 16-bit value,\n///         considered as numerator of a fraction whose denominator\n///         is 65536. In other words, it computes i * (scale / 65536)\n\nLIB8STATIC uint16_t scale16( uint16_t i, fract16 scale )\n{\n  #if SCALE16_C == 1\n    uint16_t result;\n#if FASTLED_SCALE8_FIXED == 1\n    result = ((uint32_t)(i) * (1+(uint32_t)(scale))) / 65536;\n#else\n    result = ((uint32_t)(i) * (uint32_t)(scale)) / 65536;\n#endif\n    return result;\n#elif SCALE16_AVRASM == 1\n#if FASTLED_SCALE8_FIXED == 1\n    // implemented sort of like\n    //   result = ((i * scale) + i ) / 65536\n    //\n    // why not like this, you may ask?\n    //   result = (i * (scale+1)) / 65536\n    // the answer is that if scale is 65535, then scale+1\n    // will be zero, which is not what we want.\n    uint32_t result;\n    asm volatile(\n                 // result.A-B  = i.A x scale.A\n                 \"  mul %A[i], %A[scale]                 \\n\\t\"\n                 //  save results...\n                 // basic idea:\n                 //\"  mov %A[result], r0                 \\n\\t\"\n                 //\"  mov %B[result], r1                 \\n\\t\"\n                 // which can be written as...\n                 \"  movw %A[result], r0                   \\n\\t\"\n                 // Because we're going to add i.A-B to\n                 // result.A-D, we DO need to keep both\n                 // the r0 and r1 portions of the product\n                 // UNlike in the 'unfixed scale8' version.\n                 // So the movw here is needed.\n                 : [result] \"=r\" (result)\n                 : [i] \"r\" (i),\n                 [scale] \"r\" (scale)\n                 : \"r0\", \"r1\"\n                 );\n\n    asm volatile(\n                 // result.C-D  = i.B x scale.B\n                 \"  mul %B[i], %B[scale]                 \\n\\t\"\n                 //\"  mov %C[result], r0                 \\n\\t\"\n                 //\"  mov %D[result], r1                 \\n\\t\"\n                 \"  movw %C[result], r0                   \\n\\t\"\n                 : [result] \"+r\" (result)\n                 : [i] \"r\" (i),\n                 [scale] \"r\" (scale)\n                 : \"r0\", \"r1\"\n                 );\n\n    const uint8_t  zero = 0;\n    asm volatile(\n                 // result.B-D += i.B x scale.A\n                 \"  mul %B[i], %A[scale]                 \\n\\t\"\n\n                 \"  add %B[result], r0                   \\n\\t\"\n                 \"  adc %C[result], r1                   \\n\\t\"\n                 \"  adc %D[result], %[zero]              \\n\\t\"\n\n                 // result.B-D += i.A x scale.B\n                 \"  mul %A[i], %B[scale]                 \\n\\t\"\n\n                 \"  add %B[result], r0                   \\n\\t\"\n                 \"  adc %C[result], r1                   \\n\\t\"\n                 \"  adc %D[result], %[zero]              \\n\\t\"\n\n                 // cleanup r1\n                 \"  clr r1                               \\n\\t\"\n\n                 : [result] \"+r\" (result)\n                 : [i] \"r\" (i),\n                 [scale] \"r\" (scale),\n                 [zero] \"r\" (zero)\n                 : \"r0\", \"r1\"\n                 );\n\n    asm volatile(\n                 // result.A-D += i.A-B\n                 \"  add %A[result], %A[i]                \\n\\t\"\n                 \"  adc %B[result], %B[i]                \\n\\t\"\n                 \"  adc %C[result], %[zero]              \\n\\t\"\n                 \"  adc %D[result], %[zero]              \\n\\t\"\n                 : [result] \"+r\" (result)\n                 : [i] \"r\" (i),\n                 [zero] \"r\" (zero)\n                 );\n\n    result = result >> 16;\n    return result;\n#else\n    uint32_t result;\n    asm volatile(\n                 // result.A-B  = i.A x scale.A\n                 \"  mul %A[i], %A[scale]                 \\n\\t\"\n                 //  save results...\n                 // basic idea:\n                 //\"  mov %A[result], r0                 \\n\\t\"\n                 //\"  mov %B[result], r1                 \\n\\t\"\n                 // which can be written as...\n                 \"  movw %A[result], r0                   \\n\\t\"\n                 // We actually don't need to do anything with r0,\n                 // as result.A is never used again here, so we\n                 // could just move the high byte, but movw is\n                 // one clock cycle, just like mov, so might as\n                 // well, in case we want to use this code for\n                 // a generic 16x16 multiply somewhere.\n\n                 : [result] \"=r\" (result)\n                 : [i] \"r\" (i),\n                   [scale] \"r\" (scale)\n                 : \"r0\", \"r1\"\n                 );\n\n    asm volatile(\n                 // result.C-D  = i.B x scale.B\n                 \"  mul %B[i], %B[scale]                 \\n\\t\"\n                 //\"  mov %C[result], r0                 \\n\\t\"\n                 //\"  mov %D[result], r1                 \\n\\t\"\n                 \"  movw %C[result], r0                   \\n\\t\"\n                 : [result] \"+r\" (result)\n                 : [i] \"r\" (i),\n                   [scale] \"r\" (scale)\n                 : \"r0\", \"r1\"\n                 );\n\n    const uint8_t  zero = 0;\n    asm volatile(\n                 // result.B-D += i.B x scale.A\n                 \"  mul %B[i], %A[scale]                 \\n\\t\"\n\n                 \"  add %B[result], r0                   \\n\\t\"\n                 \"  adc %C[result], r1                   \\n\\t\"\n                 \"  adc %D[result], %[zero]              \\n\\t\"\n\n                 // result.B-D += i.A x scale.B\n                 \"  mul %A[i], %B[scale]                 \\n\\t\"\n\n                 \"  add %B[result], r0                   \\n\\t\"\n                 \"  adc %C[result], r1                   \\n\\t\"\n                 \"  adc %D[result], %[zero]              \\n\\t\"\n\n                 // cleanup r1\n                 \"  clr r1                               \\n\\t\"\n\n                 : [result] \"+r\" (result)\n                 : [i] \"r\" (i),\n                   [scale] \"r\" (scale),\n                   [zero] \"r\" (zero)\n                 : \"r0\", \"r1\"\n                 );\n\n    result = result >> 16;\n    return result;\n#endif\n#else\n    #error \"No implementation for scale16 available.\"\n#endif\n}\n///@}\n\n///@defgroup Dimming Dimming and brightening functions\n///\n/// Dimming and brightening functions\n///\n/// The eye does not respond in a linear way to light.\n/// High speed PWM'd LEDs at 50% duty cycle appear far\n/// brighter then the 'half as bright' you might expect.\n///\n/// If you want your midpoint brightness leve (128) to\n/// appear half as bright as 'full' brightness (255), you\n/// have to apply a 'dimming function'.\n///@{\n\n/// Adjust a scaling value for dimming\nLIB8STATIC uint8_t dim8_raw( uint8_t x)\n{\n    return scale8( x, x);\n}\n\n/// Adjust a scaling value for dimming for video (value will never go below 1)\nLIB8STATIC uint8_t dim8_video( uint8_t x)\n{\n    return scale8_video( x, x);\n}\n\n/// Linear version of the dimming function that halves for values < 128\nLIB8STATIC uint8_t dim8_lin( uint8_t x )\n{\n    if( x & 0x80 ) {\n        x = scale8( x, x);\n    } else {\n        x += 1;\n        x /= 2;\n    }\n    return x;\n}\n\n/// inverse of the dimming function, brighten a value\nLIB8STATIC uint8_t brighten8_raw( uint8_t x)\n{\n    uint8_t ix = 255 - x;\n    return 255 - scale8( ix, ix);\n}\n\n/// inverse of the dimming function, brighten a value\nLIB8STATIC uint8_t brighten8_video( uint8_t x)\n{\n    uint8_t ix = 255 - x;\n    return 255 - scale8_video( ix, ix);\n}\n\n/// inverse of the dimming function, brighten a value\nLIB8STATIC uint8_t brighten8_lin( uint8_t x )\n{\n    uint8_t ix = 255 - x;\n    if( ix & 0x80 ) {\n        ix = scale8( ix, ix);\n    } else {\n        ix += 1;\n        ix /= 2;\n    }\n    return 255 - ix;\n}\n\n///@}\n#endif\n"
  },
  {
    "path": "lib/lib8tion/trig8.h",
    "content": "#ifndef __INC_LIB8TION_TRIG_H\n#define __INC_LIB8TION_TRIG_H\n\n///@ingroup lib8tion\n\n///@defgroup Trig Fast trig functions\n/// Fast 8 and 16-bit approximations of sin(x) and cos(x).\n///        Don't use these approximations for calculating the\n///        trajectory of a rocket to Mars, but they're great\n///        for art projects and LED displays.\n///\n///        On Arduino/AVR, the 16-bit approximation is more than\n///        10X faster than floating point sin(x) and cos(x), while\n/// the 8-bit approximation is more than 20X faster.\n///@{\n\n#if defined(__AVR__)\n#define sin16 sin16_avr\n#else\n#define sin16 sin16_C\n#endif\n\n/// Fast 16-bit approximation of sin(x). This approximation never varies more than\n/// 0.69% from the floating point value you'd get by doing\n///\n///     float s = sin(x) * 32767.0;\n///\n/// @param theta input angle from 0-65535\n/// @returns sin of theta, value between -32767 to 32767.\nLIB8STATIC int16_t sin16_avr( uint16_t theta )\n{\n    static const uint8_t data[] =\n    { 0,         0,         49, 0, 6393%256,   6393/256, 48, 0,\n      12539%256, 12539/256, 44, 0, 18204%256, 18204/256, 38, 0,\n      23170%256, 23170/256, 31, 0, 27245%256, 27245/256, 23, 0,\n      30273%256, 30273/256, 14, 0, 32137%256, 32137/256,  4 /*,0*/ };\n\n    uint16_t offset = (theta & 0x3FFF);\n\n    // AVR doesn't have a multi-bit shift instruction,\n    // so if we say \"offset >>= 3\", gcc makes a tiny loop.\n    // Inserting empty volatile statements between each\n    // bit shift forces gcc to unroll the loop.\n    offset >>= 1; // 0..8191\n    asm volatile(\"\");\n    offset >>= 1; // 0..4095\n    asm volatile(\"\");\n    offset >>= 1; // 0..2047\n\n    if( theta & 0x4000 ) offset = 2047 - offset;\n\n    uint8_t sectionX4;\n    sectionX4 = offset / 256;\n    sectionX4 *= 4;\n\n    uint8_t m;\n\n    union {\n        uint16_t b;\n        struct {\n            uint8_t blo;\n            uint8_t bhi;\n        };\n    } u;\n\n    //in effect u.b = blo + (256 * bhi);\n    u.blo = data[ sectionX4 ];\n    u.bhi = data[ sectionX4 + 1];\n    m     = data[ sectionX4 + 2];\n\n    uint8_t secoffset8 = (uint8_t)(offset) / 2;\n\n    uint16_t mx = m * secoffset8;\n\n    int16_t  y  = mx + u.b;\n    if( theta & 0x8000 ) y = -y;\n\n    return y;\n}\n\n/// Fast 16-bit approximation of sin(x). This approximation never varies more than\n/// 0.69% from the floating point value you'd get by doing\n///\n///     float s = sin(x) * 32767.0;\n///\n/// @param theta input angle from 0-65535\n/// @returns sin of theta, value between -32767 to 32767.\nLIB8STATIC int16_t sin16_C( uint16_t theta )\n{\n    static const uint16_t base[] =\n    { 0, 6393, 12539, 18204, 23170, 27245, 30273, 32137 };\n    static const uint8_t slope[] =\n    { 49, 48, 44, 38, 31, 23, 14, 4 };\n\n    uint16_t offset = (theta & 0x3FFF) >> 3; // 0..2047\n    if( theta & 0x4000 ) offset = 2047 - offset;\n\n    uint8_t section = offset / 256; // 0..7\n    uint16_t b   = base[section];\n    uint8_t  m   = slope[section];\n\n    uint8_t secoffset8 = (uint8_t)(offset) / 2;\n\n    uint16_t mx = m * secoffset8;\n    int16_t  y  = mx + b;\n\n    if( theta & 0x8000 ) y = -y;\n\n    return y;\n}\n\n\n/// Fast 16-bit approximation of cos(x). This approximation never varies more than\n/// 0.69% from the floating point value you'd get by doing\n///\n///     float s = cos(x) * 32767.0;\n///\n/// @param theta input angle from 0-65535\n/// @returns sin of theta, value between -32767 to 32767.\nLIB8STATIC int16_t cos16( uint16_t theta)\n{\n    return sin16( theta + 16384);\n}\n\n///////////////////////////////////////////////////////////////////////\n\n// sin8 & cos8\n//        Fast 8-bit approximations of sin(x) & cos(x).\n//        Input angle is an unsigned int from 0-255.\n//        Output is an unsigned int from 0 to 255.\n//\n//        This approximation can vary to to 2%\n//        from the floating point value you'd get by doing\n//          float s = (sin( x ) * 128.0) + 128;\n//\n//        Don't use this approximation for calculating the\n//        \"real\" trigonometric calculations, but it's great\n//        for art projects and LED displays.\n//\n//        On Arduino/AVR, this approximation is more than\n//        20X faster than floating point sin(x) and cos(x)\n\n#if defined(__AVR__) && !defined(LIB8_ATTINY)\n#define sin8 sin8_avr\n#else\n#define sin8 sin8_C\n#endif\n\n\nstatic const uint8_t b_m16_interleave[8] = { 0, 49, 49, 41, 90, 27, 117, 10 };\n\n/// Fast 8-bit approximation of sin(x). This approximation never varies more than\n/// 2% from the floating point value you'd get by doing\n///\n///     float s = (sin(x) * 128.0) + 128;\n///\n/// @param theta input angle from 0-255\n/// @returns sin of theta, value between 0 and 255\nLIB8STATIC uint8_t  sin8_avr( uint8_t theta)\n{\n    uint8_t offset = theta;\n\n    asm volatile(\n                 \"sbrc %[theta],6         \\n\\t\"\n                 \"com  %[offset]           \\n\\t\"\n                 : [theta] \"+r\" (theta), [offset] \"+r\" (offset)\n                 );\n\n    offset &= 0x3F; // 0..63\n\n    uint8_t secoffset  = offset & 0x0F; // 0..15\n    if( theta & 0x40) secoffset++;\n\n    uint8_t m16; uint8_t b;\n\n    uint8_t section = offset >> 4; // 0..3\n    uint8_t s2 = section * 2;\n\n    const uint8_t* p = b_m16_interleave;\n    p += s2;\n    b   = *p;\n    p++;\n    m16 = *p;\n\n    uint8_t mx;\n    uint8_t xr1;\n    asm volatile(\n                 \"mul %[m16],%[secoffset]   \\n\\t\"\n                 \"mov %[mx],r0              \\n\\t\"\n                 \"mov %[xr1],r1             \\n\\t\"\n                 \"eor  r1, r1               \\n\\t\"\n                 \"swap %[mx]                \\n\\t\"\n                 \"andi %[mx],0x0F           \\n\\t\"\n                 \"swap %[xr1]               \\n\\t\"\n                 \"andi %[xr1], 0xF0         \\n\\t\"\n                 \"or   %[mx], %[xr1]        \\n\\t\"\n                 : [mx] \"=d\" (mx), [xr1] \"=d\" (xr1)\n                 : [m16] \"d\" (m16), [secoffset] \"d\" (secoffset)\n                 );\n\n    int8_t y = mx + b;\n    if( theta & 0x80 ) y = -y;\n\n    y += 128;\n\n    return y;\n}\n\n\n/// Fast 8-bit approximation of sin(x). This approximation never varies more than\n/// 2% from the floating point value you'd get by doing\n///\n///     float s = (sin(x) * 128.0) + 128;\n///\n/// @param theta input angle from 0-255\n/// @returns sin of theta, value between 0 and 255\nLIB8STATIC uint8_t sin8_C( uint8_t theta)\n{\n    uint8_t offset = theta;\n    if( theta & 0x40 ) {\n        offset = (uint8_t)255 - offset;\n    }\n    offset &= 0x3F; // 0..63\n\n    uint8_t secoffset  = offset & 0x0F; // 0..15\n    if( theta & 0x40) secoffset++;\n\n    uint8_t section = offset >> 4; // 0..3\n    uint8_t s2 = section * 2;\n    const uint8_t* p = b_m16_interleave;\n    p += s2;\n    uint8_t b   =  *p;\n    p++;\n    uint8_t m16 =  *p;\n\n    uint8_t mx = (m16 * secoffset) >> 4;\n\n    int8_t y = mx + b;\n    if( theta & 0x80 ) y = -y;\n\n    y += 128;\n\n    return y;\n}\n\n/// Fast 8-bit approximation of cos(x). This approximation never varies more than\n/// 2% from the floating point value you'd get by doing\n///\n///     float s = (cos(x) * 128.0) + 128;\n///\n/// @param theta input angle from 0-255\n/// @returns sin of theta, value between 0 and 255\nLIB8STATIC uint8_t cos8( uint8_t theta)\n{\n    return sin8( theta + 64);\n}\n\n/// Fast 16-bit approximation of atan2(x).\n/// @returns atan2, value between 0 and 255\nLIB8STATIC uint8_t atan2_8(int16_t dy, int16_t dx)\n{\n    if (dy == 0)\n    {\n        if (dx >= 0)\n            return 0;\n        else\n            return 128;\n    }\n\n    int16_t abs_y = dy > 0 ? dy : -dy;\n    int8_t a;\n\n    if (dx >= 0)\n        a = 32 - (32 * (dx - abs_y) / (dx + abs_y));\n    else\n        a = 96 - (32 * (dx + abs_y) / (abs_y - dx));\n\n    if (dy < 0)\n        return -a;     // negate if in quad III or IV\n    return a;\n}\n\n///@}\n#endif\n"
  },
  {
    "path": "lib/python/kle2xy.py",
    "content": "\"\"\" Original code from https://github.com/skullydazed/kle2xy\n\"\"\"\n\nimport hjson\nfrom decimal import Decimal\n\n\nclass KLE2xy(list):\n    \"\"\"Abstract interface for interacting with a KLE layout.\n    \"\"\"\n    def __init__(self, layout=None, name='', invert_y=True):\n        super(KLE2xy, self).__init__()\n\n        self.name = name\n        self.invert_y = invert_y\n        self.key_width = Decimal('19.05')\n        self.key_skel = {'decal': False, 'border_color': 'none', 'keycap_profile': '', 'keycap_color': 'grey', 'label_color': 'black', 'label_size': 3, 'label_style': 4, 'width': Decimal('1'), 'height': Decimal('1')}\n        self.rows = Decimal(0)\n        self.columns = Decimal(0)\n\n        if layout:\n            self.parse_layout(layout)\n\n    @property\n    def width(self):\n        \"\"\"Returns the width of the keyboard plate.\n        \"\"\"\n        return (Decimal(self.columns) * self.key_width) + self.key_width / 2\n\n    @property\n    def height(self):\n        \"\"\"Returns the height of the keyboard plate.\n        \"\"\"\n        return (self.rows * self.key_width) + self.key_width / 2\n\n    @property\n    def size(self):\n        \"\"\"Returns the size of the keyboard plate.\n        \"\"\"\n        return (self.width, self.height)\n\n    def attrs(self, properties):\n        \"\"\"Parse the keyboard properties dictionary.\n        \"\"\"\n        # FIXME: Store more than just the keyboard name.\n        if 'name' in properties:\n            self.name = properties['name']\n\n    def parse_layout(self, layout):  # noqa  FIXME(skullydazed): flake8 says this has a complexity of 25, it should be refactored.\n        # Wrap this in a dictionary so hjson will parse KLE raw data\n        layout = '{\"layout\": [' + layout + ']}'\n        layout = hjson.loads(layout)['layout']\n\n        # Initialize our state machine\n        current_key = self.key_skel.copy()\n        current_row = Decimal(0)\n        current_col = Decimal(0)\n\n        if isinstance(layout[0], dict):\n            self.attrs(layout[0])\n            layout = layout[1:]\n\n        for row_num, row in enumerate(layout):\n            self.append([])\n\n            # Process the current row\n            for key in row:\n                if isinstance(key, dict):\n                    if 'w' in key and key['w'] != Decimal(1):\n                        current_key['width'] = Decimal(key['w'])\n                    if 'w2' in key and 'h2' in key and key['w2'] == 1.5 and key['h2'] == 1:\n                        # FIXME: ISO Key uses these params: {x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25}\n                        current_key['isoenter'] = True\n                    if 'h' in key and key['h'] != Decimal(1):\n                        current_key['height'] = Decimal(key['h'])\n                    if 'a' in key:\n                        current_key['label_style'] = self.key_skel['label_style'] = max(min(int(key['a']), 9), 0)\n                    if 'f' in key:\n                        current_key['label_size'] = self.key_skel['label_size'] = max(min(int(key['f']), 9), 1)\n                    if 'p' in key:\n                        current_key['keycap_profile'] = self.key_skel['keycap_profile'] = key['p']\n                    if 'c' in key:\n                        current_key['keycap_color'] = self.key_skel['keycap_color'] = key['c']\n                    if 't' in key:\n                        # FIXME: Need to do better validation, plus figure out how to support multiple colors\n                        if '\\n' in key['t']:\n                            key['t'] = key['t'].split('\\n')[0]\n                        if key['t'] == \"0\":\n                            key['t'] = \"#000000\"\n                        current_key['label_color'] = self.key_skel['label_color'] = key['t']\n                    if 'x' in key:\n                        current_col += Decimal(key['x'])\n                    if 'y' in key:\n                        current_row += Decimal(key['y'])\n                    if 'd' in key:\n                        current_key['decal'] = True\n\n                else:\n                    current_key['name'] = key\n                    current_key['row'] = round(current_row, 2)\n                    current_key['column'] = round(current_col, 2)\n\n                    # x,y (units mm) is the center of the key\n                    x_center = current_col + current_key['width'] / 2\n                    y_center = current_row + current_key['height'] / 2\n                    current_key['x'] = x_center * self.key_width\n                    current_key['y'] = y_center * self.key_width\n\n                    # Tend to our row/col count\n                    current_col += current_key['width']\n                    if current_col > self.columns:\n                        self.columns = current_col\n\n                    # Invert the y-axis if neccesary\n                    if self.invert_y:\n                        current_key['y'] = -current_key['y']\n\n                    # Store this key\n                    self[-1].append(current_key)\n                    current_key = self.key_skel.copy()\n\n            # Move to the next row\n            current_col = Decimal(0)\n            current_row += Decimal(1)\n            if current_row > self.rows:\n                self.rows = Decimal(current_row)\n"
  },
  {
    "path": "lib/python/qmk/__init__.py",
    "content": ""
  },
  {
    "path": "lib/python/qmk/build_targets.py",
    "content": "# Copyright 2023-2024 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\nimport json\nimport shutil\nfrom typing import Dict, List, Union\nfrom pathlib import Path\nfrom dotty_dict import dotty, Dotty\nfrom milc import cli\nfrom qmk.constants import QMK_FIRMWARE, INTERMEDIATE_OUTPUT_PREFIX, HAS_QMK_USERSPACE, QMK_USERSPACE\nfrom qmk.commands import find_make, get_make_parallel_args, parse_configurator_json\nfrom qmk.keyboard import keyboard_folder\nfrom qmk.info import keymap_json\nfrom qmk.keymap import locate_keymap\nfrom qmk.path import is_under_qmk_firmware, is_under_qmk_userspace, unix_style_path\nfrom qmk.compilation_database import write_compilation_database\n\n# These must be kept in the order in which they're applied to $(TARGET) in the makefiles in order to ensure consistency.\nTARGET_FILENAME_MODIFIERS = ['FORCE_LAYOUT', 'CONVERT_TO']\n\n\nclass BuildTarget:\n    def __init__(self, keyboard: str, keymap: str, json: Union[dict, Dotty] = None):\n        self._keyboard = keyboard_folder(keyboard)\n        self._keyboard_safe = self._keyboard.replace('/', '_')\n        self._keymap = keymap\n        self._parallel = 1\n        self._clean = False\n        self._compiledb = False\n        self._extra_args = {}\n        self._json = json.to_dict() if isinstance(json, Dotty) else json\n\n    def __str__(self):\n        return f'{self.keyboard}:{self.keymap}'\n\n    def __repr__(self):\n        if len(self._extra_args.items()) > 0:\n            return f'BuildTarget(keyboard={self.keyboard}, keymap={self.keymap}, extra_args={json.dumps(self._extra_args, sort_keys=True)})'\n        return f'BuildTarget(keyboard={self.keyboard}, keymap={self.keymap})'\n\n    def __lt__(self, __value: object) -> bool:\n        return self.__repr__() < __value.__repr__()\n\n    def __eq__(self, __value: object) -> bool:\n        if not isinstance(__value, BuildTarget):\n            return False\n        return self.__repr__() == __value.__repr__()\n\n    def __hash__(self) -> int:\n        return self.__repr__().__hash__()\n\n    def configure(self, parallel: int = None, clean: bool = None, compiledb: bool = None) -> None:\n        if parallel is not None:\n            self._parallel = parallel\n        if clean is not None:\n            self._clean = clean\n        if compiledb is not None:\n            self._compiledb = compiledb\n\n    @property\n    def keyboard(self) -> str:\n        return self._keyboard\n\n    @property\n    def keymap(self) -> str:\n        return self._keymap\n\n    @property\n    def json(self) -> dict:\n        if not self._json:\n            self._load_json()\n        if not self._json:\n            return {}\n        return self._json\n\n    @property\n    def dotty(self) -> Dotty:\n        return dotty(self.json)\n\n    @property\n    def extra_args(self) -> Dict[str, str]:\n        return {k: v for k, v in self._extra_args.items()}\n\n    @extra_args.setter\n    def extra_args(self, ex_args: Dict[str, str]):\n        if ex_args is not None and isinstance(ex_args, dict):\n            self._extra_args = {k: v for k, v in ex_args.items()}\n\n    def target_name(self, **env_vars) -> str:\n        # Work out the intended target name\n        target = f'{self._keyboard_safe}_{self.keymap}'\n        vars = self._all_vars(**env_vars)\n        for modifier in TARGET_FILENAME_MODIFIERS:\n            if modifier in vars:\n                target += f\"_{vars[modifier]}\"\n        return target\n\n    def _all_vars(self, **env_vars) -> Dict[str, str]:\n        vars = {k: v for k, v in env_vars.items()}\n        for k, v in self._extra_args.items():\n            vars[k] = v\n        return vars\n\n    def _intermediate_output(self, **env_vars) -> Path:\n        return Path(f'{INTERMEDIATE_OUTPUT_PREFIX}{self.target_name(**env_vars)}')\n\n    def _common_make_args(self, dry_run: bool = False, build_target: str = None, **env_vars):\n        compile_args = [\n            find_make(),\n            *get_make_parallel_args(self._parallel),\n            '-r',\n            '-R',\n            '-f',\n            'builddefs/build_keyboard.mk',\n        ]\n\n        if not cli.config.general.verbose:\n            compile_args.append('-s')\n\n        verbose = 'true' if cli.config.general.verbose else 'false'\n        color = 'true' if cli.config.general.color else 'false'\n\n        if dry_run:\n            compile_args.append('-n')\n\n        if build_target:\n            compile_args.append(build_target)\n\n        compile_args.extend([\n            f'KEYBOARD={self.keyboard}',\n            f'KEYMAP={self.keymap}',\n            f'KEYBOARD_FILESAFE={self._keyboard_safe}',\n            f'TARGET={self._keyboard_safe}_{self.keymap}',  # don't use self.target_name() here, it's rebuilt on the makefile side\n            f'VERBOSE={verbose}',\n            f'COLOR={color}',\n            'SILENT=false',\n            'QMK_BIN=\"qmk\"',\n        ])\n\n        vars = self._all_vars(**env_vars)\n        for k, v in vars.items():\n            compile_args.append(f'{k}={v}')\n\n        return compile_args\n\n    def prepare_build(self, build_target: str = None, dry_run: bool = False, **env_vars) -> None:\n        raise NotImplementedError(\"prepare_build() not implemented in base class\")\n\n    def compile_command(self, build_target: str = None, dry_run: bool = False, **env_vars) -> List[str]:\n        raise NotImplementedError(\"compile_command() not implemented in base class\")\n\n    def generate_compilation_database(self, build_target: str = None, skip_clean: bool = False, **env_vars) -> None:\n        self.prepare_build(build_target=build_target, **env_vars)\n        command = self.compile_command(build_target=build_target, dry_run=True, **env_vars)\n        output_path = QMK_FIRMWARE / 'compile_commands.json'\n        ret = write_compilation_database(command=command, output_path=output_path, skip_clean=skip_clean, **env_vars)\n        if ret and output_path.exists() and HAS_QMK_USERSPACE:\n            shutil.copy(str(output_path), str(QMK_USERSPACE / 'compile_commands.json'))\n        return ret\n\n    def compile(self, build_target: str = None, dry_run: bool = False, **env_vars) -> None:\n        if self._clean or self._compiledb:\n            command = [find_make(), \"clean\"]\n            if dry_run:\n                command.append('-n')\n            cli.log.info('Cleaning with {fg_cyan}%s', ' '.join(command))\n            cli.run(command, capture_output=False)\n\n        if self._compiledb and not dry_run:\n            self.generate_compilation_database(build_target=build_target, skip_clean=True, **env_vars)\n\n        self.prepare_build(build_target=build_target, dry_run=dry_run, **env_vars)\n        command = self.compile_command(build_target=build_target, **env_vars)\n        cli.log.info('Compiling keymap with {fg_cyan}%s', ' '.join(command))\n        if not dry_run:\n            cli.echo('\\n')\n            ret = cli.run(command, capture_output=False)\n            if ret.returncode:\n                return ret.returncode\n\n\nclass KeyboardKeymapBuildTarget(BuildTarget):\n    def __init__(self, keyboard: str, keymap: str, json: dict = None):\n        super().__init__(keyboard=keyboard, keymap=keymap, json=json)\n\n    def __repr__(self):\n        if len(self._extra_args.items()) > 0:\n            return f'KeyboardKeymapTarget(keyboard={self.keyboard}, keymap={self.keymap}, extra_args={self._extra_args})'\n        return f'KeyboardKeymapTarget(keyboard={self.keyboard}, keymap={self.keymap})'\n\n    def _load_json(self):\n        self._json = keymap_json(self.keyboard, self.keymap)\n\n    def prepare_build(self, build_target: str = None, dry_run: bool = False, **env_vars) -> None:\n        pass\n\n    def compile_command(self, build_target: str = None, dry_run: bool = False, **env_vars) -> List[str]:\n        compile_args = self._common_make_args(dry_run=dry_run, build_target=build_target, **env_vars)\n\n        # Need to override the keymap path if the keymap is a userspace directory.\n        # This also ensures keyboard aliases as per `keyboard_aliases.hjson` still work if the userspace has the keymap\n        # in an equivalent historical location.\n        vars = self._all_vars(**env_vars)\n        keymap_location = locate_keymap(self.keyboard, self.keymap, force_layout=vars.get('FORCE_LAYOUT'))\n        if is_under_qmk_userspace(keymap_location) and not is_under_qmk_firmware(keymap_location):\n            keymap_directory = keymap_location.parent\n            compile_args.extend([\n                f'MAIN_KEYMAP_PATH_1={unix_style_path(keymap_directory)}',\n                f'MAIN_KEYMAP_PATH_2={unix_style_path(keymap_directory)}',\n                f'MAIN_KEYMAP_PATH_3={unix_style_path(keymap_directory)}',\n                f'MAIN_KEYMAP_PATH_4={unix_style_path(keymap_directory)}',\n                f'MAIN_KEYMAP_PATH_5={unix_style_path(keymap_directory)}',\n            ])\n\n        return compile_args\n\n\nclass JsonKeymapBuildTarget(BuildTarget):\n    def __init__(self, json_path):\n        if isinstance(json_path, Path):\n            self.json_path = json_path\n        else:\n            self.json_path = None\n\n        json = parse_configurator_json(json_path)  # Will load from stdin if provided\n\n        # In case the user passes a keymap.json from a keymap directory directly to the CLI.\n        # e.g.: qmk compile - < keyboards/clueboard/california/keymaps/default/keymap.json\n        json[\"keymap\"] = json.get(\"keymap\", \"default_json\")\n\n        super().__init__(keyboard=json['keyboard'], keymap=json['keymap'], json=json)\n\n    def __repr__(self):\n        if len(self._extra_args.items()) > 0:\n            return f'JsonKeymapTarget(keyboard={self.keyboard}, keymap={self.keymap}, path={self.json_path}, extra_args={self._extra_args})'\n        return f'JsonKeymapTarget(keyboard={self.keyboard}, keymap={self.keymap}, path={self.json_path})'\n\n    def _load_json(self):\n        pass  # Already loaded in constructor\n\n    def prepare_build(self, build_target: str = None, dry_run: bool = False, **env_vars) -> None:\n        intermediate_output = self._intermediate_output(**env_vars)\n        generated_files_path = intermediate_output / 'src'\n        keymap_json = generated_files_path / 'keymap.json'\n\n        if self._clean:\n            if intermediate_output.exists():\n                shutil.rmtree(intermediate_output)\n\n        # begin with making the deepest folder in the tree\n        generated_files_path.mkdir(exist_ok=True, parents=True)\n\n        # Compare minified to ensure consistent comparison\n        new_content = json.dumps(self.json, separators=(',', ':'))\n        if keymap_json.exists():\n            old_content = json.dumps(json.loads(keymap_json.read_text(encoding='utf-8')), separators=(',', ':'))\n            if old_content == new_content:\n                new_content = None\n\n        # Write the keymap.json file if different so timestamps are only updated\n        # if the content changes -- running `make` won't treat it as modified.\n        if new_content:\n            keymap_json.write_text(new_content, encoding='utf-8')\n\n    def compile_command(self, build_target: str = None, dry_run: bool = False, **env_vars) -> List[str]:\n        compile_args = self._common_make_args(dry_run=dry_run, build_target=build_target, **env_vars)\n        intermediate_output = self._intermediate_output(**env_vars)\n        generated_files_path = intermediate_output / 'src'\n        keymap_json = generated_files_path / 'keymap.json'\n        compile_args.extend([\n            f'MAIN_KEYMAP_PATH_1={unix_style_path(intermediate_output)}',\n            f'MAIN_KEYMAP_PATH_2={unix_style_path(intermediate_output)}',\n            f'MAIN_KEYMAP_PATH_3={unix_style_path(intermediate_output)}',\n            f'MAIN_KEYMAP_PATH_4={unix_style_path(intermediate_output)}',\n            f'MAIN_KEYMAP_PATH_5={unix_style_path(intermediate_output)}',\n            f'KEYMAP_JSON={keymap_json}',\n            f'KEYMAP_PATH={generated_files_path}',\n        ])\n\n        return compile_args\n"
  },
  {
    "path": "lib/python/qmk/c_parse.py",
    "content": "\"\"\"Functions for working with config.h files.\n\"\"\"\nfrom pygments.lexers.c_cpp import CLexer\nfrom pygments.token import Token\nfrom pygments import lex\nfrom itertools import islice\nfrom pathlib import Path\nimport re\n\nfrom milc import cli\n\nfrom qmk.comment_remover import comment_remover\n\ndefault_key_entry = {'x': -1, 'y': 0}\nsingle_comment_regex = re.compile(r'\\s+/[/*].*$')\nmulti_comment_regex = re.compile(r'/\\*(.|\\n)*?\\*/', re.MULTILINE)\nlayout_macro_define_regex = re.compile(r'^#\\s*define')\n\n\ndef _get_chunks(it, size):\n    \"\"\"Break down a collection into smaller parts\n    \"\"\"\n    it = iter(it)\n    return iter(lambda: tuple(islice(it, size)), ())\n\n\ndef preprocess_c_file(file):\n    \"\"\"Load file and strip comments\n    \"\"\"\n    file_contents = file.read_text(encoding='utf-8')\n    file_contents = comment_remover(file_contents)\n    return file_contents.replace('\\\\\\n', '')\n\n\ndef strip_line_comment(string):\n    \"\"\"Removes comments from a single line string.\n    \"\"\"\n    return single_comment_regex.sub('', string)\n\n\ndef strip_multiline_comment(string):\n    \"\"\"Removes comments from a single line string.\n    \"\"\"\n    return multi_comment_regex.sub('', string)\n\n\ndef c_source_files(dir_names):\n    \"\"\"Returns a list of all *.c, *.h, and *.cpp files for a given list of directories\n\n    Args:\n\n        dir_names\n            List of directories relative to `qmk_firmware`.\n    \"\"\"\n    files = []\n    for dir in dir_names:\n        files.extend(file for file in Path(dir).glob('**/*') if file.suffix in ['.c', '.h', '.cpp'])\n    return files\n\n\ndef find_layouts(file):\n    \"\"\"Returns list of parsed LAYOUT preprocessor macros found in the supplied include file.\n    \"\"\"\n    file = Path(file)\n    aliases = {}  # Populated with all `#define`s that aren't functions\n    parsed_layouts = {}\n\n    # Search the file for LAYOUT macros and aliases\n    file_contents = preprocess_c_file(file)\n\n    for line in file_contents.split('\\n'):\n        if layout_macro_define_regex.match(line.lstrip()) and '(' in line and 'LAYOUT' in line:\n            # We've found a LAYOUT macro\n            macro_name, layout, matrix = _parse_layout_macro(line.strip())\n\n            # Reject bad macro names\n            if macro_name.startswith('LAYOUT_kc') or not macro_name.startswith('LAYOUT'):\n                continue\n\n            # Parse the matrix data\n            matrix_locations = _parse_matrix_locations(matrix, file, macro_name)\n\n            # Parse the layout entries into a basic structure\n            default_key_entry['x'] = -1  # Set to -1 so _default_key(key) will increment it to 0\n            layout = layout.strip()\n            parsed_layout = [_default_key(key) for key in layout.split(',')]\n\n            for i, key in enumerate(parsed_layout):\n                if 'label' not in key:\n                    cli.log.error('Invalid LAYOUT macro in %s: Empty parameter name in macro %s at pos %s.', file, macro_name, i)\n                elif key['label'] not in matrix_locations:\n                    cli.log.error('Invalid LAYOUT macro in %s: Key %s in macro %s has no matrix position!', file, key['label'], macro_name)\n                elif len(matrix_locations.get(key['label'])) > 1:\n                    cli.log.error('Invalid LAYOUT macro in %s: Key %s in macro %s has multiple matrix positions (%s)', file, key['label'], macro_name, ', '.join(str(x) for x in matrix_locations[key['label']]))\n                else:\n                    key['matrix'] = matrix_locations[key['label']][0]\n\n            parsed_layouts[macro_name] = {\n                'layout': parsed_layout,\n                'filename': str(file),\n            }\n\n        elif '#define' in line:\n            # Attempt to extract a new layout alias\n            try:\n                _, pp_macro_name, pp_macro_text = line.strip().split(' ', 2)\n                aliases[pp_macro_name] = pp_macro_text\n            except ValueError:\n                continue\n\n    return parsed_layouts, aliases\n\n\ndef parse_config_h_file(config_h_file, config_h=None):\n    \"\"\"Extract defines from a config.h file.\n    \"\"\"\n    if not config_h:\n        config_h = {}\n\n    config_h_file = Path(config_h_file)\n\n    if config_h_file.exists():\n        config_h_text = config_h_file.read_text(encoding='utf-8')\n        config_h_text = config_h_text.replace('\\\\\\n', '')\n        config_h_text = strip_multiline_comment(config_h_text)\n\n        for linenum, line in enumerate(config_h_text.split('\\n')):\n            line = strip_line_comment(line).strip()\n\n            if not line:\n                continue\n\n            line = line.split()\n\n            if line[0] == '#define':\n                if len(line) == 1:\n                    cli.log.error('%s: Incomplete #define! On or around line %s' % (config_h_file, linenum))\n                elif len(line) == 2:\n                    config_h[line[1]] = True\n                else:\n                    config_h[line[1]] = ' '.join(line[2:])\n\n            elif line[0] == '#undef':\n                if len(line) == 2:\n                    if line[1] in config_h:\n                        if config_h[line[1]] is True:\n                            del config_h[line[1]]\n                        else:\n                            config_h[line[1]] = False\n                else:\n                    cli.log.error('%s: Incomplete #undef! On or around line %s' % (config_h_file, linenum))\n\n    return config_h\n\n\ndef _default_key(label=None):\n    \"\"\"Increment x and return a copy of the default_key_entry.\n    \"\"\"\n    default_key_entry['x'] += 1\n    new_key = default_key_entry.copy()\n\n    if label:\n        new_key['label'] = label\n\n    return new_key\n\n\ndef _parse_layout_macro(layout_macro):\n    \"\"\"Split the LAYOUT macro into its constituent parts\n    \"\"\"\n    layout_macro = layout_macro.replace('\\\\', '').replace(' ', '').replace('\\t', '').replace('#define', '')\n    macro_name, layout = layout_macro.split('(', 1)\n    layout, matrix = layout.split(')', 1)\n\n    return macro_name, layout, matrix\n\n\ndef _parse_matrix_locations(matrix, file, macro_name):\n    \"\"\"Parse raw matrix data into a dictionary keyed by the LAYOUT identifier.\n    \"\"\"\n    matrix_locations = {}\n\n    for row_num, row in enumerate(matrix.split('},{')):\n        if row.startswith('LAYOUT'):\n            cli.log.error('%s: %s: Nested layout macro detected. Matrix data not available!', file, macro_name)\n            break\n\n        row = row.replace('{', '').replace('}', '')\n        for col_num, identifier in enumerate(row.split(',')):\n            if identifier != 'KC_NO':\n                if identifier not in matrix_locations:\n                    matrix_locations[identifier] = []\n                matrix_locations[identifier].append([row_num, col_num])\n\n    return matrix_locations\n\n\ndef _coerce_led_token(_type, value):\n    \"\"\" Convert token to valid info.json content\n    \"\"\"\n    value_map = {\n        'NO_LED': None,\n        'LED_FLAG_ALL': 0xFF,\n        'LED_FLAG_NONE': 0x00,\n        'LED_FLAG_MODIFIER': 0x01,\n        'LED_FLAG_UNDERGLOW': 0x02,\n        'LED_FLAG_KEYLIGHT': 0x04,\n        'LED_FLAG_INDICATOR': 0x08,\n    }\n    if _type is Token.Literal.Number.Integer:\n        return int(value)\n    if _type is Token.Literal.Number.Float:\n        return float(value)\n    if _type is Token.Literal.Number.Hex:\n        return int(value, 0)\n    if _type is Token.Name and value in value_map.keys():\n        return value_map[value]\n\n\ndef _validate_led_config(matrix, matrix_rows, matrix_cols, matrix_indexes, position, position_raw, flags):\n    # TODO: Improve crude parsing/validation\n    if len(matrix) != matrix_rows and len(matrix) != (matrix_rows / 2):\n        raise ValueError(\"Unable to parse g_led_config matrix data\")\n    for index, row in enumerate(matrix):\n        if len(row) != matrix_cols:\n            raise ValueError(f\"Number of columns in row {index} ({len(row)}) does not match matrix ({matrix_cols})\")\n    if len(position) != len(flags):\n        raise ValueError(f\"Number of g_led_config physical positions ({len(position)}) does not match number of flags ({len(flags)})\")\n    if len(matrix_indexes) and (max(matrix_indexes) >= len(flags)):\n        raise ValueError(f\"LED index {max(matrix_indexes)} is OOB in g_led_config - should be < {len(flags)}\")\n    if not all(isinstance(n, int) for n in matrix_indexes):\n        raise ValueError(\"matrix indexes are not all ints\")\n    if (len(position_raw) % 2) != 0:\n        raise ValueError(\"Malformed g_led_config position data\")\n\n\ndef _parse_led_config(file, matrix_cols, matrix_rows):\n    \"\"\"Return any 'raw' led/rgb matrix config\n    \"\"\"\n    matrix = []\n    position_raw = []\n    flags = []\n\n    found_led_config_t = False\n    found_g_led_config = False\n    bracket_count = 0\n    section = 0\n    current_row_index = 0\n    current_row = []\n\n    for _type, value in lex(preprocess_c_file(file), CLexer()):\n        if not found_g_led_config:\n            # Check for type\n            if value == 'led_config_t':\n                found_led_config_t = True\n            # Type found, now check for name\n            elif found_led_config_t and value == 'g_led_config':\n                found_g_led_config = True\n        elif value == ';':\n            found_g_led_config = False\n        else:\n            # Assume bracket count hints to section of config we are within\n            if value == '{':\n                bracket_count += 1\n                if bracket_count == 2:\n                    section += 1\n            elif value == '}':\n                if section == 1 and bracket_count == 3:\n                    matrix.append(current_row)\n                    current_row = []\n                    current_row_index += 1\n                bracket_count -= 1\n            else:\n                # Assume any non whitespace value here is important enough to stash\n                if _type in [Token.Literal.Number.Integer, Token.Literal.Number.Float, Token.Literal.Number.Hex, Token.Name]:\n                    if section == 1 and bracket_count == 3:\n                        current_row.append(_coerce_led_token(_type, value))\n                    if section == 2 and bracket_count == 3:\n                        position_raw.append(_coerce_led_token(_type, value))\n                    if section == 3 and bracket_count == 2:\n                        flags.append(_coerce_led_token(_type, value))\n                elif _type in [Token.Comment.Preproc]:\n                    # TODO: Promote to error\n                    return None\n\n    # Slightly better intrim format\n    position = list(_get_chunks(position_raw, 2))\n    matrix_indexes = list(filter(lambda x: x is not None, sum(matrix, [])))\n\n    # If we have not found anything - bail with no error\n    if not section:\n        return None\n\n    # Throw any validation errors\n    _validate_led_config(matrix, matrix_rows, matrix_cols, matrix_indexes, position, position_raw, flags)\n\n    return (matrix, position, flags)\n\n\ndef find_led_config(file, matrix_cols, matrix_rows):\n    \"\"\"Search file for led/rgb matrix config\n    \"\"\"\n    found = _parse_led_config(file, matrix_cols, matrix_rows)\n    if not found:\n        return None\n\n    # Expand collected content\n    (matrix, position, flags) = found\n\n    # Align to output format\n    led_config = []\n    for index, item in enumerate(position, start=0):\n        led_config.append({\n            'x': item[0],\n            'y': item[1],\n            'flags': flags[index],\n        })\n    for r in range(len(matrix)):\n        for c in range(len(matrix[r])):\n            index = matrix[r][c]\n            if index is not None:\n                led_config[index]['matrix'] = [r, c]\n\n    return led_config\n"
  },
  {
    "path": "lib/python/qmk/cli/__init__.py",
    "content": "\"\"\"QMK CLI Subcommands\n\nWe list each subcommand here explicitly because all the reliable ways of searching for modules are slow and delay startup.\n\"\"\"\nimport os\nimport platform\nimport platformdirs\nimport shlex\nimport sys\nfrom importlib.util import find_spec\nfrom pathlib import Path\nfrom subprocess import run\n\nfrom milc import cli, __VERSION__\nfrom milc.questions import yesno\n\n\ndef _get_default_distrib_path():\n    if 'windows' in platform.platform().lower():\n        try:\n            result = cli.run(['cygpath', '-w', '/opt/qmk'])\n            if result.returncode == 0:\n                return result.stdout.strip()\n        except Exception:\n            pass\n\n    return platformdirs.user_data_dir('qmk')\n\n\n# Ensure the QMK distribution is on the `$PATH` if present. This must be kept in sync with qmk/qmk_cli.\nQMK_DISTRIB_DIR = Path(os.environ.get('QMK_DISTRIB_DIR', _get_default_distrib_path()))\nif QMK_DISTRIB_DIR.exists():\n    os.environ['PATH'] = str(QMK_DISTRIB_DIR / 'bin') + os.pathsep + os.environ['PATH']\n\n# Prepend any user-defined path prefix\nif 'QMK_PATH_PREFIX' in os.environ:\n    os.environ['PATH'] = os.environ['QMK_PATH_PREFIX'] + os.pathsep + os.environ['PATH']\n\nimport_names = {\n    # A mapping of package name to importable name\n    'pep8-naming': 'pep8ext_naming',\n    'pyserial': 'serial',\n    'pyusb': 'usb.core',\n    'qmk-dotty-dict': 'dotty_dict',\n    'pillow': 'PIL'\n}\n\nsafe_commands = [\n    # A list of subcommands we always run, even when the module imports fail\n    'clone',\n    'config',\n    'doctor',\n    'env',\n    'setup',\n]\n\nsubcommands = [\n    'qmk.cli.ci.validate_aliases',\n    'qmk.cli.bux',\n    'qmk.cli.c2json',\n    'qmk.cli.cd',\n    'qmk.cli.chibios.confmigrate',\n    'qmk.cli.clean',\n    'qmk.cli.compile',\n    'qmk.cli.docs',\n    'qmk.cli.doctor',\n    'qmk.cli.find',\n    'qmk.cli.flash',\n    'qmk.cli.format.c',\n    'qmk.cli.format.json',\n    'qmk.cli.format.python',\n    'qmk.cli.format.text',\n    'qmk.cli.generate.api',\n    'qmk.cli.generate.autocorrect_data',\n    'qmk.cli.generate.compilation_database',\n    'qmk.cli.generate.community_modules',\n    'qmk.cli.generate.config_h',\n    'qmk.cli.generate.develop_pr_list',\n    'qmk.cli.generate.dfu_header',\n    'qmk.cli.generate.docs',\n    'qmk.cli.generate.info_json',\n    'qmk.cli.generate.keyboard_c',\n    'qmk.cli.generate.keyboard_h',\n    'qmk.cli.generate.keycodes',\n    'qmk.cli.generate.keymap_h',\n    'qmk.cli.generate.make_dependencies',\n    'qmk.cli.generate.rgb_breathe_table',\n    'qmk.cli.generate.rules_mk',\n    'qmk.cli.generate.version_h',\n    'qmk.cli.git.submodule',\n    'qmk.cli.hello',\n    'qmk.cli.import.kbfirmware',\n    'qmk.cli.import.keyboard',\n    'qmk.cli.import.keymap',\n    'qmk.cli.info',\n    'qmk.cli.json2c',\n    'qmk.cli.license_check',\n    'qmk.cli.lint',\n    'qmk.cli.kle2json',\n    'qmk.cli.list.keyboards',\n    'qmk.cli.list.keymaps',\n    'qmk.cli.list.layouts',\n    'qmk.cli.mass_compile',\n    'qmk.cli.migrate',\n    'qmk.cli.new.keyboard',\n    'qmk.cli.new.keymap',\n    'qmk.cli.painter',\n    'qmk.cli.pytest',\n    'qmk.cli.resolve_alias',\n    'qmk.cli.test.c',\n    'qmk.cli.userspace.add',\n    'qmk.cli.userspace.compile',\n    'qmk.cli.userspace.doctor',\n    'qmk.cli.userspace.list',\n    'qmk.cli.userspace.path',\n    'qmk.cli.userspace.remove',\n    'qmk.cli.via2json',\n]\n\n\ndef _install_deps(requirements):\n    \"\"\"Perform the installation of missing requirements.\n\n    If we detect that we are running in a virtualenv we can't write into we'll use sudo to perform the pip install.\n    \"\"\"\n    command = [sys.executable, '-m', 'pip', 'install']\n\n    if sys.prefix != sys.base_prefix:\n        # We are in a virtualenv, check to see if we need to use sudo to write to it\n        if not os.access(sys.prefix, os.W_OK):\n            print('Notice: Using sudo to install modules to location owned by root:', sys.prefix)\n            command.insert(0, 'sudo')\n\n    elif not os.access(sys.prefix, os.W_OK):\n        # We can't write to sys.prefix, attempt to install locally\n        command.append('--user')\n\n    return _run_cmd(*command, '-r', requirements)\n\n\ndef _run_cmd(*command):\n    \"\"\"Run a command in a subshell.\n    \"\"\"\n    if 'windows' in cli.platform.lower():\n        safecmd = map(shlex.quote, command)\n        safecmd = ' '.join(safecmd)\n        command = [os.environ['SHELL'], '-c', safecmd]\n\n    return run(command)\n\n\ndef _find_broken_requirements(requirements):\n    \"\"\" Check if the modules in the given requirements.txt are available.\n\n    Args:\n\n        requirements\n            The path to a requirements.txt file\n\n    Returns a list of modules that couldn't be imported\n    \"\"\"\n    with Path(requirements).open() as fd:\n        broken_modules = []\n\n        for line in fd.readlines():\n            line = line.strip().replace('<', '=').replace('>', '=')\n\n            if len(line) == 0 or line[0] == '#' or line.startswith('-r'):\n                continue\n\n            if '#' in line:\n                line = line.split('#')[0]\n\n            module_name = line.split('=')[0] if '=' in line else line\n            module_import = module_name.replace('-', '_')\n\n            # Not every module is importable by its own name.\n            if module_name in import_names:\n                module_import = import_names[module_name]\n\n            if not find_spec(module_import):\n                broken_modules.append(module_name)\n\n        return broken_modules\n\n\ndef _broken_module_imports(requirements):\n    \"\"\"Make sure we can import all the python modules.\n    \"\"\"\n    broken_modules = _find_broken_requirements(requirements)\n\n    for module in broken_modules:\n        print('Could not find module %s!' % module)\n\n    if broken_modules:\n        return True\n\n    return False\n\n\ndef _yesno(*args):\n    \"\"\"Wrapper to only prompt if interactive\n    \"\"\"\n    return sys.stdout.isatty() and yesno(*args)\n\n\ndef _eprint(errmsg):\n    \"\"\"Wrapper to print to stderr\n    \"\"\"\n    print(errmsg, file=sys.stderr)\n\n\n# Make sure our python is new enough\n#\n# Supported version information\n#\n# Based on the OSes we support these are the minimum python version available by default.\n# Last update: 2024 Jun 24\n#\n# Arch: 3.12\n# Debian 11: 3.9\n# Debian 12: 3.11\n# Fedora 39: 3.12\n# Fedora 40: 3.12\n# FreeBSD: 3.11\n# Gentoo: 3.12\n# macOS: 3.12 (from homebrew)\n# msys2: 3.11\n# Slackware: 3.9\n# solus: 3.10\n# Ubuntu 22.04: 3.10\n# Ubuntu 24.04: 3.12\n# void: 3.12\n\nif sys.version_info[0] != 3 or sys.version_info[1] < 9:\n    _eprint('Error: Your Python is too old! Please upgrade to Python 3.9 or later.')\n    exit(127)\n\nmilc_version = __VERSION__.split('.')\n\nif int(milc_version[0]) < 2 and int(milc_version[1]) < 9:\n    requirements = Path('requirements.txt').resolve()\n\n    _eprint(f'Your MILC library is too old! Please upgrade: python3 -m pip install -U -r {str(requirements)}')\n    exit(127)\n\n# Make sure we can run binaries in the same directory as our Python interpreter\npython_dir = os.path.dirname(sys.executable)\n\nif python_dir not in os.environ['PATH'].split(os.pathsep):\n    os.environ['PATH'] = os.pathsep.join((python_dir, os.environ['PATH']))\n\n# Check to make sure we have all our dependencies\nmsg_install = f'\\nPlease run `{sys.executable} -m pip install -r %s` to install required python dependencies.'\nargs = sys.argv[1:]\nwhile args and args[0][0] == '-':\n    del args[0]\n\nsafe_command = args and args[0] in safe_commands\n\nif not safe_command:\n    if _broken_module_imports('requirements.txt'):\n        if _yesno('Would you like to install the required Python modules?'):\n            _install_deps('requirements.txt')\n        else:\n            _eprint(msg_install % (str(Path('requirements.txt').resolve()),))\n            exit(1)\n\n    if cli.config.user.developer and _broken_module_imports('requirements-dev.txt'):\n        if _yesno('Would you like to install the required developer Python modules?'):\n            _install_deps('requirements-dev.txt')\n        elif _yesno('Would you like to disable developer mode?'):\n            _run_cmd(sys.argv[0], 'config', 'user.developer=None')\n        else:\n            _eprint(msg_install % (str(Path('requirements-dev.txt').resolve()),))\n            _eprint('You can also turn off developer mode: qmk config user.developer=None')\n            exit(1)\n\n# Import our subcommands\nfor subcommand in subcommands:\n    try:\n        __import__(subcommand)\n\n    except (ImportError, ModuleNotFoundError) as e:\n        if safe_command:\n            _eprint(f'Warning: Could not import {subcommand}: {e.__class__.__name__}, {e}')\n        else:\n            raise\n"
  },
  {
    "path": "lib/python/qmk/cli/bux.py",
    "content": "\"\"\"QMK Bux\n\nWorld domination secret weapon.\n\"\"\"\nfrom milc import cli\nfrom milc.subcommand import config\n\n\n@cli.subcommand('QMK Bux miner.', hidden=True)\ndef bux(cli):\n    \"\"\"QMK bux\n    \"\"\"\n    if not cli.config.user.bux:\n        bux = 0\n    else:\n        bux = cli.config.user.bux\n\n    cli.args.read_only = False\n    config.set_config('user', 'bux', bux + 1)\n    cli.save_config()\n\n    buck = r\"\"\"\n@@BBBBBBBBBBBBBBBBBBBBK    `vP8#####BE2~   x###g_     `S###q  n##}  -j#Bl.   vBBBBBBBBBBBBBBBBBBBB@@\n@B   `:!:                 ^#@#]-   `!t@@&. 7@@B@#^   _Q@Q@@R  y@@l:P@#1'                   `!!_   B@\n@B  r@@@B                 g@@|      ` N@@u 7@@iv@@u *#@z\"@@R  y@@&@@Q-                    l@@@D   B@\n@B   !#@B                 ^#@#x-   I@B@@&' 7@@i \"B@Q@@r _@@R  y@@l.k#@W:                  `:@@D   B@\n@B    B@B                  `v3g#####B0N#d. v##x  'ckk:  -##A  u##i  `lB#I_                  @@D   B@\n@B    B@B                                                                                   @@D   B@\n@B    B@B                                    `._\":!!!=~^*|)r^~:'                            @@D   B@\n@B    ~*~                             `,=)]}y2tjIIfKfKfaPsffsWsUyx~.                        **!   B@\n@B          .*r***r=              _*]yzKsqKUfz22IAA3HzzUjtktzHWsHsIz].                            B@\n@B         )v`  ,  !1-         -rysHHUzUzo2jzoI22ztzkyykt2zjzUzIa3qPsl'        !r*****`           B@\n@B        :}    @`  .j       `xzqdAfzKWsj2kkcycczqAsk2zHbg&ER5q55SNN5U~   !RBB#d`c#1 f#\\BQ&v      B@\n@B        _y   ]#   ,c       vUWNWWPsfsssN9WyccnckAfUfWb0DR0&R5RRRddq2_  `@D`jr@2U@#c3@1@Qc-      B@\n@B         !7!    .r]`       }AE0RdRqNd9dNR9fUIzzosPqqAddNNdER9EE9dPy!    BQ!zy@iU@.Q@@y@8x-      B@\n@B           :****>.         '7adddDdR&gRNdRbd&dNNbbRdNdd5NdRRD0RSf}-     .k0&EW`xR .8Q=NRRx      B@\n@B        =**-rx*r}r~}\"        ;n2jkzsf3N3zsKsP5dddRddddRddNNqPzy\\\"            '~****\"            B@\n@B        :!!~!;=~r>:*_         `:^vxikylulKfHkyjzzozoIoklix|^!-`                                 B@\n@B                                    ```'-_\"\"::::!:_-.``                                         B@\n@B   `-                                                                                       .`  B@\n@B   r@=                                  In source we trust                                  @H  B@\n@B   r@=                                                                                      @H  B@\n@B  -g@=                `}&###E7  W#g. :#Q n####~   R###8k ;#&  `##.7#8-`R#z                 t@H  B@\n@B   r@=                8@R=-=R@g R@@#:!@@ 2@&!:`   8@1=@@!*@B  `@@- v@#8@y                   @H  B@\n@B   r@=               :@@-   _@@_R@fB#}@@ 2@@@#    8@@#@Q.*@B  `@@-  y@@N                    @H  B@\n@B   `.                 g@9=_~D@g R@}`&@@@ 2@&__`   8@u_Q@2!@@^-x@@` Y@QD@z                   .`  B@\n@@BBBBBBBBBBBBBBBBBBB_  `c8@@@81` S#] `N#B l####v   D###BA. vg@@#0~ i#&' 5#K   RBBBBBBBBBBBBBBBBBB@@\n\"\"\" # noqa: Do not care about the ASCII art\n    print(f\"{buck}\\nYou've been blessed by the QMK gods!\\nYou have {cli.config.user.bux} QMK bux.\")\n"
  },
  {
    "path": "lib/python/qmk/cli/c2json.py",
    "content": "\"\"\"Generate a keymap.json from a keymap.c file.\n\"\"\"\nimport re\nimport json\n\nfrom argcomplete.completers import FilesCompleter\nfrom milc import cli\n\nimport qmk.path\nfrom qmk.json_encoders import InfoJSONEncoder\nfrom qmk.decorators import automagic_keyboard, automagic_keymap\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.keymap import locate_keymap, find_keymap_from_dir, generate_json, c2json as c2json_impl\nfrom qmk.errors import CppError\nfrom qmk.commands import dump_lines\n\n\n@cli.argument('--no-cpp', arg_only=True, action='store_false', help='Do not use \\'cpp\\' on keymap.c')\n@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard\\'s name')\n@cli.argument('-km', '--keymap', help='The keymap\\'s name')\n@cli.argument('filename', nargs='?', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.c'), help='keymap.c file')\n@cli.subcommand('Creates a keymap.json from a keymap.c file.')\n@automagic_keyboard\n@automagic_keymap\ndef c2json(cli):\n    \"\"\"Generate a keymap.json from a keymap.c file.\n\n    This command uses the `qmk.keymap` module to generate a keymap.json from a keymap.c file. The generated keymap is written to stdout, or to a file if -o is provided.\n    \"\"\"\n    filename = cli.args.filename\n    keyboard = cli.config.c2json.keyboard\n    keymap = cli.config.c2json.keymap\n\n    if filename:\n        if not keyboard and not keymap:\n            # fallback to inferring keyboard/keymap from path\n            (keymap, found_type) = find_keymap_from_dir(filename)\n            if found_type == 'keymap_directory':\n                keyboard = re.search(fr\"keyboards/(.+)/keymaps/{keymap}/.*\", filename.as_posix()).group(1)\n\n    elif keyboard and keymap:\n        if not filename:\n            # fallback to inferring keyboard/keymap from path\n            filename = locate_keymap(keyboard, keymap)\n\n    if not all((filename, keyboard, keymap)):\n        cli.log.error('You must supply keyboard and keymap, a path to a keymap.c within qmk_firmware, or absolute filename and keyboard and keymap')\n        cli.print_help()\n        return False\n\n    try:\n        keymap_json = c2json_impl(keyboard, keymap, filename, use_cpp=cli.args.no_cpp)\n    except CppError as e:\n        if cli.config.general.verbose:\n            cli.log.debug('The C pre-processor ran into a fatal error: %s', e)\n        cli.log.error('Something went wrong. Try to use --no-cpp.\\nUse the CLI in verbose mode to find out more.')\n        return False\n\n    # Generate the keymap.json\n    try:\n        keymap_json = generate_json(keymap_json['keymap'], keymap_json['keyboard'], keymap_json['layout'], keymap_json['layers'])\n    except KeyError:\n        cli.log.error('Something went wrong. Try to use --no-cpp.')\n        return False\n\n    if cli.args.output:\n        keymap_lines = [json.dumps(keymap_json, cls=InfoJSONEncoder, sort_keys=True)]\n    else:\n        keymap_lines = [json.dumps(keymap_json)]\n\n    dump_lines(cli.args.output, keymap_lines, cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/cli/cd.py",
    "content": "\"\"\"Open a shell in the QMK Home directory\n\"\"\"\nimport sys\nimport os\nimport subprocess\n\nfrom milc import cli\n\nfrom qmk.path import under_qmk_firmware\n\n\n@cli.subcommand('Go to QMK Home')\ndef cd(cli):\n    \"\"\"Go to QMK Home\n    \"\"\"\n    if not sys.stdout.isatty():\n        cli.log.error(\"This command is for interactive usage only. For non-interactive usage, 'cd $(qmk env QMK_HOME)' is more robust.\")\n        sys.exit(1)\n\n    if not under_qmk_firmware():\n        # Only do anything if the user is not under qmk_firmware already\n        # in order to reduce the possibility of starting multiple shells\n        cli.log.info(\"Spawning a subshell in your QMK_HOME directory.\")\n        cli.log.info(\"Type 'exit' to get back to the parent shell.\")\n        if not cli.platform.lower().startswith('windows'):\n            # For Linux/Mac/etc\n            # Check the user's login shell from 'passwd'\n            # alternatively fall back to $SHELL env var\n            # and finally to '/bin/bash'.\n            import getpass\n            import pwd\n            shell = pwd.getpwnam(getpass.getuser()).pw_shell\n            if not shell:\n                shell = os.environ.get('SHELL', '/bin/bash')\n            # Start the new subshell\n            os.execl(shell, shell)\n        else:\n            # For Windows\n            # Check the $SHELL env var\n            # and fall back to '/usr/bin/bash'.\n            qmk_env = os.environ.copy()\n            # Set the prompt for the new shell\n            qmk_env['MSYS2_PS1'] = qmk_env['PS1']\n            # Start the new subshell\n            subprocess.run([os.environ.get('SHELL', '/usr/bin/bash')], env=qmk_env)\n    else:\n        cli.log.info(\"Already within qmk_firmware directory.\")\n"
  },
  {
    "path": "lib/python/qmk/cli/chibios/__init__.py",
    "content": ""
  },
  {
    "path": "lib/python/qmk/cli/chibios/confmigrate.py",
    "content": "\"\"\"This script automates the copying of the default keymap into your own keymap.\n\"\"\"\nimport re\nimport sys\nimport os\n\nfrom qmk.constants import QMK_FIRMWARE\nfrom qmk.path import normpath\nfrom milc import cli\n\n\ndef eprint(*args, **kwargs):\n    print(*args, file=sys.stderr, **kwargs)\n\n\nfile_header = \"\"\"\\\n/* Copyright 2020 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * This file was auto-generated by:\n *    `qmk chibios-confmigrate -i {0} -r {1}`\n */\n\n#pragma once\n\"\"\"\n\n\ndef collect_defines(filepath):\n    with open(filepath, 'r', encoding='utf-8') as f:\n        content = f.read()\n        define_search = re.compile(r'(?m)^#\\s*define\\s+(?:.*\\\\\\r?\\n)*.*$', re.MULTILINE)\n        value_search = re.compile(r'^#\\s*define\\s+(?P<name>[a-zA-Z0-9_]+(\\([^\\)]*\\))?)\\s*(?P<value>.*)', re.DOTALL)\n        define_matches = define_search.findall(content)\n\n        defines = {\"keys\": [], \"dict\": {}}\n        for define_match in define_matches:\n            value_match = value_search.search(define_match)\n            defines[\"keys\"].append(value_match.group(\"name\"))\n            defines[\"dict\"][value_match.group(\"name\")] = value_match.group(\"value\")\n        return defines\n\n\ndef check_diffs(input_defs, reference_defs):\n    not_present_in_input = []\n    not_present_in_reference = []\n    to_override = []\n\n    for key in reference_defs[\"keys\"]:\n        if key not in input_defs[\"dict\"]:\n            not_present_in_input.append(key)\n            continue\n\n    for key in input_defs[\"keys\"]:\n        if key not in input_defs[\"dict\"]:\n            not_present_in_input.append(key)\n            continue\n\n    for key in input_defs[\"keys\"]:\n        if key in reference_defs[\"keys\"] and input_defs[\"dict\"][key] != reference_defs[\"dict\"][key]:\n            to_override.append((key, input_defs[\"dict\"][key]))\n\n    return (to_override, not_present_in_input, not_present_in_reference)\n\n\ndef migrate_chconf_h(to_override, outfile):\n    print(file_header.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile)\n\n    for override in to_override:\n        print(\"#define %s %s\" % (override[0], override[1]), file=outfile)\n        print(\"\", file=outfile)\n\n    print(\"#include_next <chconf.h>\\n\", file=outfile)\n\n\ndef migrate_halconf_h(to_override, outfile):\n    print(file_header.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile)\n\n    for override in to_override:\n        print(\"#define %s %s\" % (override[0], override[1]), file=outfile)\n        print(\"\", file=outfile)\n\n    print(\"#include_next <halconf.h>\\n\", file=outfile)\n\n\ndef migrate_mcuconf_h(to_override, outfile):\n    print(file_header.format(cli.args.input.relative_to(QMK_FIRMWARE), cli.args.reference.relative_to(QMK_FIRMWARE)), file=outfile)\n\n    print(\"#include_next <mcuconf.h>\\n\", file=outfile)\n\n    for override in to_override:\n        print(\"#undef %s\" % (override[0]), file=outfile)\n        print(\"#define %s %s\" % (override[0], override[1]), file=outfile)\n        print(\"\", file=outfile)\n\n\n@cli.argument('-i', '--input', type=normpath, arg_only=True, required=True, help='Specify input config file.')\n@cli.argument('-r', '--reference', type=normpath, arg_only=True, required=True, help='Specify the reference file to compare against')\n@cli.argument('-o', '--overwrite', arg_only=True, action='store_true', help='Overwrites the input file during migration.')\n@cli.argument('-d', '--delete', arg_only=True, action='store_true', help='If the file has no overrides, migration will delete the input file.')\n@cli.argument('-f', '--force', arg_only=True, action='store_true', help='Re-migrates an already migrated file, even if it doesn\\'t detect a full ChibiOS config.')\n@cli.subcommand('Generates a migrated ChibiOS configuration file, as a result of comparing the input against a reference')\ndef chibios_confmigrate(cli):\n    \"\"\"Generates a usable ChibiOS replacement configuration file, based on a fully-defined conf and a reference config.\n    \"\"\"\n\n    input_defs = collect_defines(cli.args.input)\n    reference_defs = collect_defines(cli.args.reference)\n\n    (to_override, not_present_in_input, not_present_in_reference) = check_diffs(input_defs, reference_defs)\n\n    if len(not_present_in_input) > 0:\n        eprint(\"Keys not in input, but present inside reference (potential manual migration required):\")\n        for key in not_present_in_input:\n            eprint(\"   %s\" % (key))\n\n    if len(not_present_in_reference) > 0:\n        eprint(\"Keys not in reference, but present inside input (potential manual migration required):\")\n        for key in not_present_in_reference:\n            eprint(\"   %s\" % (key))\n\n    if len(to_override) == 0:\n        eprint('No overrides found! If there were no missing keys above, it should be safe to delete the input file.')\n        if cli.args.delete:\n            os.remove(cli.args.input)\n    else:\n        eprint('Overrides found:')\n        for override in to_override:\n            eprint(\"%40s: %s -> %s\" % (override[0], reference_defs[\"dict\"][override[0]].encode('unicode_escape').decode(\"utf-8\"), override[1].encode('unicode_escape').decode(\"utf-8\")))\n\n        eprint('--------------------------------------')\n\n        if cli.args.input.name == \"chconf.h\" and (\"CHCONF_H\" in input_defs[\"dict\"] or \"_CHCONF_H_\" in input_defs[\"dict\"] or cli.args.force):\n            migrate_chconf_h(to_override, outfile=sys.stdout)\n            if cli.args.overwrite:\n                with open(cli.args.input, \"w\", encoding='utf-8') as out_file:\n                    migrate_chconf_h(to_override, outfile=out_file)\n\n        elif cli.args.input.name == \"halconf.h\" and (\"HALCONF_H\" in input_defs[\"dict\"] or \"_HALCONF_H_\" in input_defs[\"dict\"] or cli.args.force):\n            migrate_halconf_h(to_override, outfile=sys.stdout)\n            if cli.args.overwrite:\n                with open(cli.args.input, \"w\", encoding='utf-8') as out_file:\n                    migrate_halconf_h(to_override, outfile=out_file)\n\n        elif cli.args.input.name == \"mcuconf.h\" and (\"MCUCONF_H\" in input_defs[\"dict\"] or \"_MCUCONF_H_\" in input_defs[\"dict\"] or cli.args.force):\n            migrate_mcuconf_h(to_override, outfile=sys.stdout)\n            if cli.args.overwrite:\n                with open(cli.args.input, \"w\", encoding='utf-8') as out_file:\n                    migrate_mcuconf_h(to_override, outfile=out_file)\n"
  },
  {
    "path": "lib/python/qmk/cli/ci/__init__.py",
    "content": ""
  },
  {
    "path": "lib/python/qmk/cli/ci/validate_aliases.py",
    "content": "\"\"\"Validates the list of keyboard aliases.\n\"\"\"\nfrom milc import cli\n\nfrom qmk.keyboard import keyboard_folder, keyboard_alias_definitions\n\n\ndef _safe_keyboard_folder(target):\n    try:\n        return keyboard_folder(target)  # throws ValueError if it's invalid\n    except Exception:\n        return None\n\n\ndef _target_keyboard_exists(target):\n    # If there's no target, then we can't build it.\n    if not target:\n        return False\n\n    # If the target directory exists but it itself has an invalid alias or invalid rules.mk, then we can't build it either.\n    if not _safe_keyboard_folder(target):\n        return False\n\n    # As far as we can tell, we can build it!\n    return True\n\n\ndef _alias_not_self(alias):\n    \"\"\"Check if alias points to itself, either directly or within a circular reference\n    \"\"\"\n    aliases = keyboard_alias_definitions()\n\n    found = set()\n    while alias in aliases:\n        found.add(alias)\n        alias = aliases[alias].get('target', alias)\n        if alias in found:\n            return False\n\n    return True\n\n\n@cli.subcommand('Validates the list of keyboard aliases.', hidden=True)\ndef ci_validate_aliases(cli):\n    aliases = keyboard_alias_definitions()\n\n    success = True\n    for alias in aliases.keys():\n        target = aliases[alias].get('target', None)\n        if not _alias_not_self(alias):\n            cli.log.error(f'Keyboard alias {alias} should not point to itself')\n            success = False\n\n        elif not _target_keyboard_exists(target):\n            cli.log.error(f'Keyboard alias {alias} has a target that doesn\\'t exist: {target}')\n            success = False\n\n    return success\n"
  },
  {
    "path": "lib/python/qmk/cli/clean.py",
    "content": "\"\"\"Clean the QMK firmware folder of build artifacts.\n\"\"\"\nfrom subprocess import DEVNULL\n\nfrom qmk.commands import find_make\nfrom milc import cli\n\n\n@cli.argument('-a', '--all', arg_only=True, action='store_true', help='Remove *.hex and *.bin files in the QMK root as well.')\n@cli.subcommand('Clean the QMK firmware folder of build artifacts.')\ndef clean(cli):\n    \"\"\"Runs `make clean` (or `make distclean` if --all is passed)\n    \"\"\"\n    cli.run([find_make(), 'distclean' if cli.args.all else 'clean'], capture_output=False, stdin=DEVNULL)\n"
  },
  {
    "path": "lib/python/qmk/cli/compile.py",
    "content": "\"\"\"Compile a QMK Firmware.\n\nYou can compile a keymap already in the repo or using a QMK Configurator export.\n\"\"\"\nfrom argcomplete.completers import FilesCompleter\n\nfrom milc import cli\n\nimport qmk.path\nfrom qmk.decorators import automagic_keyboard, automagic_keymap\nfrom qmk.commands import build_environment\nfrom qmk.keyboard import keyboard_completer, keyboard_folder_or_all, is_all_keyboards\nfrom qmk.keymap import keymap_completer, locate_keymap\nfrom qmk.build_targets import KeyboardKeymapBuildTarget, JsonKeymapBuildTarget\n\n\n@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='The configurator export to compile')\n@cli.argument('-kb', '--keyboard', type=keyboard_folder_or_all, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')\n@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')\n@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help=\"Don't actually build, just show the make command to be run.\")\n@cli.argument('-j', '--parallel', type=int, default=1, help=\"Set the number of parallel make jobs; 0 means unlimited.\")\n@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help=\"Set a variable to be passed to make. May be passed multiple times.\")\n@cli.argument('-c', '--clean', arg_only=True, action='store_true', help=\"Remove object files before compiling.\")\n@cli.argument('-t', '--target', type=str, default=None, help=\"Intended alternative build target, such as `production` in `make planck/rev4:default:production`.\")\n@cli.argument('--compiledb', arg_only=True, action='store_true', help=\"Generates the clang compile_commands.json file during build. Implies --clean.\")\n@cli.subcommand('Compile a QMK Firmware.')\n@automagic_keyboard\n@automagic_keymap\ndef compile(cli):\n    \"\"\"Compile a QMK Firmware.\n\n    If a Configurator export is supplied this command will create a new keymap, overwriting an existing keymap if one exists.\n\n    If a keyboard and keymap are provided this command will build a firmware based on that.\n    \"\"\"\n\n    # If we've received `-kb all`, reroute it to mass-compile.\n    if is_all_keyboards(cli.args.keyboard):\n        from .mass_compile import mass_compile\n        cli.args.builds = []\n        cli.args.filter = []\n        cli.config.mass_compile.keymap = cli.config.compile.keymap\n        cli.config.mass_compile.parallel = cli.config.compile.parallel\n        cli.args.no_temp = False\n        return mass_compile(cli)\n\n    # If we've received `-km all`, reroute it to mass-compile.\n    if cli.args.keymap == 'all':\n        from .mass_compile import mass_compile\n        cli.args.builds = [f'{cli.config.compile.keyboard}:all']\n        cli.args.filter = []\n        cli.config.mass_compile.keymap = None\n        cli.config.mass_compile.parallel = cli.config.compile.parallel\n        cli.args.no_temp = False\n        return mass_compile(cli)\n\n    # Build the environment vars\n    envs = build_environment(cli.args.env)\n\n    # Handler for the build target\n    target = None\n\n    if cli.args.filename:\n        # if we were given a filename, assume we have a json build target\n        target = JsonKeymapBuildTarget(cli.args.filename)\n\n    elif cli.config.compile.keyboard and cli.config.compile.keymap:\n        # if we got a keyboard and keymap, attempt to find it\n        if not locate_keymap(cli.config.compile.keyboard, cli.config.compile.keymap):\n            cli.log.error('Invalid keymap argument.')\n            cli.print_help()\n            return False\n\n        # If we got here, then we have a valid keyboard and keymap for a build target\n        target = KeyboardKeymapBuildTarget(cli.config.compile.keyboard, cli.config.compile.keymap)\n\n    if not target:\n        cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')\n        cli.print_help()\n        return False\n\n    target.configure(parallel=cli.config.compile.parallel, clean=cli.args.clean, compiledb=cli.args.compiledb)\n    return target.compile(cli.args.target, dry_run=cli.args.dry_run, **envs)\n"
  },
  {
    "path": "lib/python/qmk/cli/docs.py",
    "content": "\"\"\"Serve QMK documentation locally\n\"\"\"\nimport shutil\nfrom qmk.docs import prepare_docs_build_area, run_docs_command\n\nfrom milc import cli\n\n\n@cli.argument('-p', '--port', default=8936, type=int, help='Port number to use.')\n@cli.argument('-b', '--browser', action='store_true', help='Open the docs in the default browser.')\n@cli.subcommand('Run a local webserver for QMK documentation.', hidden=False if cli.config.user.developer else True)\ndef docs(cli):\n    \"\"\"Spin up a local HTTP server for the QMK docs.\n    \"\"\"\n\n    if not shutil.which('doxygen'):\n        cli.log.error('doxygen is not installed. Please install it and try again.')\n        return\n\n    if not shutil.which('yarn'):\n        cli.log.error('yarn is not installed. Please install it and try again.')\n        return\n\n    if not prepare_docs_build_area(is_production=False):\n        return False\n\n    cmd = ['docs:dev', '--port', f'{cli.args.port}']\n    if cli.args.browser:\n        cmd.append('--open')\n    run_docs_command('run', cmd)\n"
  },
  {
    "path": "lib/python/qmk/cli/doctor/__init__.py",
    "content": "\"\"\"QMK Doctor\n\nCheck out the user's QMK environment and make sure it's ready to compile.\n\"\"\"\nfrom .main import doctor\n"
  },
  {
    "path": "lib/python/qmk/cli/doctor/check.py",
    "content": "\"\"\"Check for specific programs.\n\"\"\"\nfrom enum import Enum\nimport shutil\nfrom subprocess import DEVNULL, TimeoutExpired\nfrom tempfile import TemporaryDirectory\nfrom pathlib import Path\n\nfrom milc import cli\nfrom qmk import submodules\nfrom qmk.commands import find_make\n\n\nclass CheckStatus(Enum):\n    OK = 1\n    WARNING = 2\n    ERROR = 3\n\n\nWHICH_MAKE = Path(find_make()).name\n\nESSENTIAL_BINARIES = {\n    WHICH_MAKE: {},\n    'git': {},\n    'dos2unix': {},\n    'diff': {},\n    'dfu-programmer': {},\n    'avrdude': {},\n    'dfu-util': {},\n    'avr-gcc': {\n        'version_arg': '-dumpversion'\n    },\n    'arm-none-eabi-gcc': {\n        'version_arg': '-dumpversion'\n    },\n}\n\n\ndef _check_make_version():\n    last_line = ESSENTIAL_BINARIES[WHICH_MAKE]['output'].split('\\n')[0]\n    version_number = last_line.split()[2]\n    cli.log.info('Found %s version %s', WHICH_MAKE, version_number)\n\n    return CheckStatus.OK\n\n\ndef _check_git_version():\n    last_line = ESSENTIAL_BINARIES['git']['output'].split('\\n')[0]\n    version_number = last_line.split()[2]\n    cli.log.info('Found git version %s', version_number)\n\n    return CheckStatus.OK\n\n\ndef _check_dos2unix_version():\n    last_line = ESSENTIAL_BINARIES['dos2unix']['output'].split('\\n')[0]\n    version_number = last_line.split()[1]\n    cli.log.info('Found dos2unix version %s', version_number)\n\n    return CheckStatus.OK\n\n\ndef _check_diff_version():\n    last_line = ESSENTIAL_BINARIES['diff']['output'].split('\\n')[0]\n    if 'Apple diff' in last_line:\n        version_number = last_line\n    else:\n        version_number = last_line.split()[3]\n    cli.log.info('Found diff version %s', version_number)\n\n    return CheckStatus.OK\n\n\ndef _check_arm_gcc_version():\n    \"\"\"Returns True if the arm-none-eabi-gcc version is not known to cause problems.\n    \"\"\"\n    version_number = ESSENTIAL_BINARIES['arm-none-eabi-gcc']['output'].strip()\n    cli.log.info('Found arm-none-eabi-gcc version %s', version_number)\n\n    # Right now all known ARM versions are ok, so check that it can produce binaries\n    return _check_arm_gcc_installation()\n\n\ndef _check_arm_gcc_installation():\n    \"\"\"Returns OK if the arm-none-eabi-gcc is fully installed and can produce binaries.\n    \"\"\"\n    with TemporaryDirectory() as temp_dir:\n        temp_in = Path(temp_dir) / 'test.c'\n        temp_out = Path(temp_dir) / 'test.elf'\n\n        temp_in.write_text('#include <newlib.h>\\nint main() { return __NEWLIB__ * __NEWLIB_MINOR__ * __NEWLIB_PATCHLEVEL__; }', encoding='utf-8')\n\n        args = ['arm-none-eabi-gcc', '-mcpu=cortex-m0', '-mthumb', '-mno-thumb-interwork', '--specs=nosys.specs', '--specs=nano.specs', '-x', 'c', '-o', str(temp_out), str(temp_in)]\n        result = cli.run(args, stdout=None, stderr=None)\n        if result.returncode == 0:\n            cli.log.info('Successfully compiled using arm-none-eabi-gcc')\n        else:\n            cli.log.error(f'Failed to compile a simple program with arm-none-eabi-gcc, return code {result.returncode}')\n            cli.log.error(f'Command: {\" \".join(args)}')\n            return CheckStatus.ERROR\n\n        args = ['arm-none-eabi-size', str(temp_out)]\n        result = cli.run(args, stdout=None, stderr=None)\n        if result.returncode == 0:\n            cli.log.info('Successfully tested arm-none-eabi-binutils using arm-none-eabi-size')\n        else:\n            cli.log.error(f'Failed to execute arm-none-eabi-size, perhaps corrupt arm-none-eabi-binutils, return code {result.returncode}')\n            cli.log.error(f'Command: {\" \".join(args)}')\n            return CheckStatus.ERROR\n\n        return CheckStatus.OK\n\n\ndef _check_avr_gcc_version():\n    \"\"\"Returns True if the avr-gcc version is not known to cause problems.\n    \"\"\"\n    version_number = ESSENTIAL_BINARIES['avr-gcc']['output'].strip()\n    cli.log.info('Found avr-gcc version %s', version_number)\n\n    # Right now all known AVR versions are ok, so check that it can produce binaries\n    return _check_avr_gcc_installation()\n\n\ndef _check_avr_gcc_installation():\n    \"\"\"Returns OK if the avr-gcc is fully installed and can produce binaries.\n    \"\"\"\n    with TemporaryDirectory() as temp_dir:\n        temp_in = Path(temp_dir) / 'test.c'\n        temp_out = Path(temp_dir) / 'test.elf'\n\n        temp_in.write_text('int main() { return 0; }', encoding='utf-8')\n\n        args = ['avr-gcc', '-mmcu=atmega32u4', '-x', 'c', '-o', str(temp_out), str(temp_in)]\n        result = cli.run(args, stdout=None, stderr=None)\n        if result.returncode == 0:\n            cli.log.info('Successfully compiled using avr-gcc')\n        else:\n            cli.log.error(f'Failed to compile a simple program with avr-gcc, return code {result.returncode}')\n            cli.log.error(f'Command: {\" \".join(args)}')\n            return CheckStatus.ERROR\n\n        args = ['avr-size', str(temp_out)]\n        result = cli.run(args, stdout=None, stderr=None)\n        if result.returncode == 0:\n            cli.log.info('Successfully tested avr-binutils using avr-size')\n        else:\n            cli.log.error(f'Failed to execute avr-size, perhaps corrupt avr-binutils, return code {result.returncode}')\n            cli.log.error(f'Command: {\" \".join(args)}')\n            return CheckStatus.ERROR\n\n        return CheckStatus.OK\n\n\ndef _check_avrdude_version():\n    last_line = ESSENTIAL_BINARIES['avrdude']['output'].split('\\n')[-2]\n    version_number = last_line.split()[2][:-1]\n    cli.log.info('Found avrdude version %s', version_number)\n\n    return CheckStatus.OK\n\n\ndef _check_dfu_util_version():\n    first_line = ESSENTIAL_BINARIES['dfu-util']['output'].split('\\n')[0]\n    version_number = first_line.split()[1]\n    cli.log.info('Found dfu-util version %s', version_number)\n\n    return CheckStatus.OK\n\n\ndef _check_dfu_programmer_version():\n    first_line = ESSENTIAL_BINARIES['dfu-programmer']['output'].split('\\n')[0]\n    version_number = first_line.split()[1]\n    cli.log.info('Found dfu-programmer version %s', version_number)\n\n    return CheckStatus.OK\n\n\ndef check_binaries():\n    \"\"\"Iterates through ESSENTIAL_BINARIES and tests them.\n    \"\"\"\n    ok = CheckStatus.OK\n    missing_from_path = []\n\n    for binary in sorted(ESSENTIAL_BINARIES):\n        try:\n            if not is_in_path(binary):\n                ok = CheckStatus.ERROR\n                missing_from_path.append(binary)\n            elif not is_executable(binary):\n                ok = CheckStatus.ERROR\n        except TimeoutExpired:\n            cli.log.debug('Timeout checking %s', binary)\n            if ok != CheckStatus.ERROR:\n                ok = CheckStatus.WARNING\n\n    if missing_from_path:\n        location_noun = 'its location' if len(missing_from_path) == 1 else 'their locations'\n        cli.log.error('{fg_red}' + ', '.join(missing_from_path) + f' may need to be installed, or {location_noun} added to your path.')\n\n    return ok\n\n\ndef check_binary_versions():\n    \"\"\"Check the versions of ESSENTIAL_BINARIES\n    \"\"\"\n    checks = {\n        WHICH_MAKE: _check_make_version,\n        'git': _check_git_version,\n        'dos2unix': _check_dos2unix_version,\n        'diff': _check_diff_version,\n        'arm-none-eabi-gcc': _check_arm_gcc_version,\n        'avr-gcc': _check_avr_gcc_version,\n        'avrdude': _check_avrdude_version,\n        'dfu-util': _check_dfu_util_version,\n        'dfu-programmer': _check_dfu_programmer_version,\n    }\n\n    versions = []\n    for binary in sorted(ESSENTIAL_BINARIES):\n        if 'output' not in ESSENTIAL_BINARIES[binary]:\n            cli.log.warning('Unknown version for %s', binary)\n            versions.append(CheckStatus.WARNING)\n            continue\n\n        check = checks[binary]\n        versions.append(check())\n    return versions\n\n\ndef check_submodules():\n    \"\"\"Iterates through all submodules to make sure they're cloned and up to date.\n    \"\"\"\n    for submodule in submodules.status().values():\n        if submodule['status'] is None:\n            return CheckStatus.ERROR\n        elif not submodule['status']:\n            return CheckStatus.WARNING\n\n    return CheckStatus.OK\n\n\ndef is_in_path(command):\n    \"\"\"Returns True if command is found in the path.\n    \"\"\"\n    if shutil.which(command) is None:\n        cli.log.error(\"{fg_red}Can't find %s in your path.\", command)\n        return False\n    return True\n\n\ndef is_executable(command):\n    \"\"\"Returns True if command can be executed.\n    \"\"\"\n    # Make sure the command can be executed\n    version_arg = ESSENTIAL_BINARIES[command].get('version_arg', '--version')\n    check = cli.run([command, version_arg], combined_output=True, stdin=DEVNULL, timeout=5)\n\n    ESSENTIAL_BINARIES[command]['output'] = check.stdout\n\n    if check.returncode in [0, 1]:  # Older versions of dfu-programmer exit 1\n        cli.log.debug('Found {fg_cyan}%s', command)\n        return True\n\n    cli.log.error(\"{fg_red}Can't run `%s %s`\", command, version_arg)\n    return False\n\n\ndef release_info(file='/etc/os-release'):\n    \"\"\"Parse release info to dict\n    \"\"\"\n    ret = {}\n    try:\n        with open(file) as f:\n            for line in f:\n                if '=' in line:\n                    key, value = map(str.strip, line.split('=', 1))\n                    if value.startswith('\"') and value.endswith('\"'):\n                        value = value[1:-1]\n                    ret[key] = value\n    except (PermissionError, FileNotFoundError):\n        pass\n\n    return ret\n"
  },
  {
    "path": "lib/python/qmk/cli/doctor/linux.py",
    "content": "\"\"\"OS-specific functions for: Linux\n\"\"\"\nimport platform\nimport shutil\nfrom pathlib import Path\n\nfrom milc import cli\n\nfrom qmk.constants import QMK_FIRMWARE, BOOTLOADER_VIDS_PIDS\nfrom .check import CheckStatus, release_info\n\n\ndef _is_wsl():\n    return 'microsoft' in platform.uname().release.lower()\n\n\ndef _udev_rule(vid, pid=None, *args):\n    \"\"\" Helper function that return udev rules\n    \"\"\"\n    rule = \"\"\n    if pid:\n        rule = 'SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"%s\", ATTRS{idProduct}==\"%s\", TAG+=\"uaccess\"' % (\n            vid,\n            pid,\n        )\n    else:\n        rule = 'SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"%s\", TAG+=\"uaccess\"' % vid\n    if args:\n        rule = ', '.join([rule, *args])\n    return rule\n\n\ndef _generate_desired_rules(bootloader_vids_pids):\n    rules = dict()\n    for bl in bootloader_vids_pids.keys():\n        rules[bl] = set()\n        for vid_pid in bootloader_vids_pids[bl]:\n            if bl == 'caterina' or bl == 'md-boot':\n                rules[bl].add(_udev_rule(vid_pid[0], vid_pid[1], 'ENV{ID_MM_DEVICE_IGNORE}=\"1\"'))\n            else:\n                rules[bl].add(_udev_rule(vid_pid[0], vid_pid[1]))\n    return rules\n\n\ndef _deprecated_udev_rule(vid, pid=None):\n    \"\"\" Helper function that return udev rules\n\n    Note: these are no longer the recommended rules, this is just used to check for them\n    \"\"\"\n    if pid:\n        return 'SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"%s\", ATTRS{idProduct}==\"%s\", MODE:=\"0666\"' % (vid, pid)\n    else:\n        return 'SUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"%s\", MODE:=\"0666\"' % vid\n\n\ndef check_udev_rules():\n    \"\"\"Make sure the udev rules look good.\n    \"\"\"\n    rc = CheckStatus.OK\n    udev_dirs = [\n        Path(\"/usr/lib/udev/rules.d/\"),\n        Path(\"/usr/local/lib/udev/rules.d/\"),\n        Path(\"/run/udev/rules.d/\"),\n        Path(\"/etc/udev/rules.d/\"),\n    ]\n\n    desired_rules = _generate_desired_rules(BOOTLOADER_VIDS_PIDS)\n\n    # These rules are no longer recommended, only use them to check for their presence.\n    deprecated_rules = {\n        'atmel-dfu': {_deprecated_udev_rule(\"03eb\", \"2ff4\"), _deprecated_udev_rule(\"03eb\", \"2ffb\"), _deprecated_udev_rule(\"03eb\", \"2ff0\")},\n        'kiibohd': {_deprecated_udev_rule(\"1c11\")},\n        'stm32': {_deprecated_udev_rule(\"1eaf\", \"0003\"), _deprecated_udev_rule(\"0483\", \"df11\")},\n        'bootloadhid': {_deprecated_udev_rule(\"16c0\", \"05df\")},\n        'caterina': {'ATTRS{idVendor}==\"2a03\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"', 'ATTRS{idVendor}==\"2341\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"'},\n        'tmk': {_deprecated_udev_rule(\"feed\")}\n    }\n\n    if any(udev_dir.exists() for udev_dir in udev_dirs):\n        udev_rules = [rule_file for udev_dir in udev_dirs for rule_file in udev_dir.glob('*.rules')]\n        current_rules = set()\n\n        # Collect all rules from the config files\n        for rule_file in udev_rules:\n            try:\n                for line in rule_file.read_text(encoding='utf-8').split('\\n'):\n                    line = line.strip()\n                    if not line.startswith(\"#\") and len(line):\n                        current_rules.add(line)\n            except (PermissionError, FileNotFoundError):\n                cli.log.debug(\"Failed to read: %s\", rule_file)\n\n        # Check if the desired rules are among the currently present rules\n        for bootloader, rules in desired_rules.items():\n            if not rules.issubset(current_rules):\n                deprecated_rule = deprecated_rules.get(bootloader)\n                if deprecated_rule and deprecated_rule.issubset(current_rules):\n                    cli.log.warning(\"{fg_yellow}Found old, deprecated udev rules for '%s' boards. The new rules on https://docs.qmk.fm/#/faq_build?id=linux-udev-rules offer better security with the same functionality.\", bootloader)\n                else:\n                    # For caterina, check if ModemManager is running\n                    if bootloader == \"caterina\" and check_modem_manager():\n                        cli.log.warning(\"{fg_yellow}Detected ModemManager without the necessary udev rules. Please either disable it or set the appropriate udev rules if you are using a Pro Micro.\")\n\n                    rc = CheckStatus.WARNING\n                    cli.log.warning(\"{fg_yellow}Missing or outdated udev rules for '%s' boards. Run 'sudo cp %s/util/udev/50-qmk.rules /etc/udev/rules.d/'.\", bootloader, QMK_FIRMWARE)\n\n    else:\n        cli.log.warning(\"{fg_yellow}Can't find udev rules, skipping udev rule checking...\")\n        cli.log.debug(\"Checked directories: %s\", ', '.join(str(udev_dir) for udev_dir in udev_dirs))\n\n    return rc\n\n\ndef check_systemd():\n    \"\"\"Check if it's a systemd system\n    \"\"\"\n    return bool(shutil.which(\"systemctl\"))\n\n\ndef check_modem_manager():\n    \"\"\"Returns True if ModemManager is running.\n\n    \"\"\"\n    if check_systemd():\n        mm_check = cli.run([\"systemctl\", \"--quiet\", \"is-active\", \"ModemManager.service\"], timeout=10)\n        if mm_check.returncode == 0:\n            return True\n    else:\n        \"\"\"(TODO): Add check for non-systemd systems\n        \"\"\"\n    return False\n\n\ndef os_test_linux():\n    \"\"\"Run the Linux specific tests.\n    \"\"\"\n    info = release_info()\n    release_id = info.get('PRETTY_NAME', info.get('ID', 'Unknown'))\n    plat = 'WSL, ' if _is_wsl() else ''\n\n    cli.log.info(f\"Detected {{fg_cyan}}Linux ({plat}{release_id}){{fg_reset}}.\")\n\n    # Don't bother with udev on WSL, for now\n    if _is_wsl():\n        # https://github.com/microsoft/WSL/issues/4197\n        if QMK_FIRMWARE.as_posix().startswith(\"/mnt\"):\n            cli.log.warning(\"I/O performance on /mnt may be extremely slow.\")\n            return CheckStatus.WARNING\n\n    else:\n        rc = check_udev_rules()\n        if rc != CheckStatus.OK:\n            return rc\n\n    return CheckStatus.OK\n"
  },
  {
    "path": "lib/python/qmk/cli/doctor/macos.py",
    "content": "import platform\n\nfrom milc import cli\n\nfrom .check import CheckStatus\n\n\ndef os_test_macos():\n    \"\"\"Run the Mac specific tests.\n    \"\"\"\n    cli.log.info(\"Detected {fg_cyan}macOS %s (%s){fg_reset}.\", platform.mac_ver()[0], 'Apple Silicon' if platform.processor() == 'arm' else 'Intel')\n\n    return CheckStatus.OK\n"
  },
  {
    "path": "lib/python/qmk/cli/doctor/main.py",
    "content": "\"\"\"QMK Doctor\n\nCheck out the user's QMK environment and make sure it's ready to compile.\n\"\"\"\nimport platform\n\nfrom milc import cli\nfrom milc.questions import yesno\n\nfrom qmk import submodules\nfrom qmk.constants import QMK_FIRMWARE, QMK_FIRMWARE_UPSTREAM, QMK_USERSPACE, HAS_QMK_USERSPACE\nfrom .check import CheckStatus, check_binaries, check_binary_versions, check_submodules\nfrom qmk.git import git_check_repo, git_get_branch, git_get_tag, git_get_last_log_entry, git_get_common_ancestor, git_is_dirty, git_get_remotes, git_check_deviation\nfrom qmk.commands import in_virtualenv\nfrom qmk.userspace import qmk_userspace_paths, qmk_userspace_validate, UserspaceValidationError\n\n\ndef distrib_tests():\n    def _load_kvp_file(file):\n        \"\"\"Load a simple key=value file into a dictionary\n        \"\"\"\n        vars = {}\n        with open(file, 'r') as f:\n            for line in f:\n                if '=' in line:\n                    key, value = line.split('=', 1)\n                    vars[key.strip()] = value.strip()\n        return vars\n\n    def _parse_toolchain_release_file(file):\n        \"\"\"Parse the QMK toolchain release info file\n        \"\"\"\n        try:\n            vars = _load_kvp_file(file)\n            return f'{vars.get(\"TOOLCHAIN_HOST\", \"unknown\")}:{vars.get(\"TOOLCHAIN_TARGET\", \"unknown\")}:{vars.get(\"COMMIT_HASH\", \"unknown\")}'\n        except Exception as e:\n            cli.log.warning('Error reading QMK toolchain release info file: %s', e)\n            return f'Unknown toolchain release info file: {file}'\n\n    def _parse_flashutils_release_file(file):\n        \"\"\"Parse the QMK flashutils release info file\n        \"\"\"\n        try:\n            vars = _load_kvp_file(file)\n            return f'{vars.get(\"FLASHUTILS_HOST\", \"unknown\")}:{vars.get(\"COMMIT_HASH\", \"unknown\")}'\n        except Exception as e:\n            cli.log.warning('Error reading QMK flashutils release info file: %s', e)\n            return f'Unknown flashutils release info file: {file}'\n\n    try:\n        from qmk.cli import QMK_DISTRIB_DIR\n        if (QMK_DISTRIB_DIR / 'etc').exists():\n            cli.log.info('Found QMK tools distribution directory: {fg_cyan}%s', QMK_DISTRIB_DIR)\n\n            toolchains = [_parse_toolchain_release_file(file) for file in (QMK_DISTRIB_DIR / 'etc').glob('toolchain_release_*')]\n            if len(toolchains) > 0:\n                cli.log.info('Found QMK toolchains: {fg_cyan}%s', ', '.join(toolchains))\n            else:\n                cli.log.warning('No QMK toolchains manifest found.')\n\n            flashutils = [_parse_flashutils_release_file(file) for file in (QMK_DISTRIB_DIR / 'etc').glob('flashutils_release_*')]\n            if len(flashutils) > 0:\n                cli.log.info('Found QMK flashutils: {fg_cyan}%s', ', '.join(flashutils))\n            else:\n                cli.log.warning('No QMK flashutils manifest found.')\n    except ImportError:\n        cli.log.info('QMK tools distribution not found.')\n\n    return CheckStatus.OK\n\n\ndef os_tests():\n    \"\"\"Determine our OS and run platform specific tests\n    \"\"\"\n    platform_id = platform.platform().lower()\n\n    if 'darwin' in platform_id or 'macos' in platform_id:\n        from .macos import os_test_macos\n        return os_test_macos()\n    elif 'linux' in platform_id:\n        from .linux import os_test_linux\n        return os_test_linux()\n    elif 'windows' in platform_id:\n        from .windows import os_test_windows\n        return os_test_windows()\n    else:\n        cli.log.warning('Unsupported OS detected: %s', platform_id)\n        return CheckStatus.WARNING\n\n\ndef git_tests():\n    \"\"\"Run Git-related checks\n    \"\"\"\n    status = CheckStatus.OK\n\n    # Make sure our QMK home is a Git repo\n    git_ok = git_check_repo()\n    if not git_ok:\n        cli.log.warning(\"{fg_yellow}QMK home does not appear to be a Git repository! (no .git folder)\")\n        status = CheckStatus.WARNING\n    else:\n        git_branch = git_get_branch()\n        if git_branch:\n            cli.log.info('Git branch: %s', git_branch)\n\n            repo_version = git_get_tag()\n            if repo_version:\n                cli.log.info('Repo version: %s', repo_version)\n\n            git_dirty = git_is_dirty()\n            if git_dirty:\n                cli.log.warning('{fg_yellow}Git has unstashed/uncommitted changes.')\n                status = CheckStatus.WARNING\n            git_remotes = git_get_remotes()\n            if 'upstream' not in git_remotes.keys() or QMK_FIRMWARE_UPSTREAM not in git_remotes['upstream'].get('url', ''):\n                cli.log.warning('{fg_yellow}The official repository does not seem to be configured as git remote \"upstream\".')\n                status = CheckStatus.WARNING\n            else:\n                git_deviation = git_check_deviation(git_branch)\n                if git_branch in ['master', 'develop'] and git_deviation:\n                    cli.log.warning('{fg_yellow}The local \"%s\" branch contains commits not found in the upstream branch.', git_branch)\n                    status = CheckStatus.WARNING\n                for branch in [git_branch, 'upstream/master', 'upstream/develop']:\n                    cli.log.info('- Latest %s: %s', branch, git_get_last_log_entry(branch))\n                for branch in ['upstream/master', 'upstream/develop']:\n                    cli.log.info('- Common ancestor with %s: %s', branch, git_get_common_ancestor(branch, 'HEAD'))\n\n    return status\n\n\ndef output_submodule_status():\n    \"\"\"Prints out information related to the submodule status.\n    \"\"\"\n    cli.log.info('Submodule status:')\n    sub_status = submodules.status()\n    for s in sub_status.keys():\n        sub_info = sub_status[s]\n        if 'name' in sub_info:\n            sub_name = sub_info['name']\n            sub_shorthash = sub_info['shorthash'] if 'shorthash' in sub_info else ''\n            sub_describe = sub_info['describe'] if 'describe' in sub_info else ''\n            sub_last_log_timestamp = sub_info['last_log_timestamp'] if 'last_log_timestamp' in sub_info else ''\n            if sub_last_log_timestamp != '':\n                cli.log.info(f'- {sub_name}: {sub_last_log_timestamp} -- {sub_describe} ({sub_shorthash})')\n            else:\n                cli.log.error(f'- {sub_name}: <<< missing or unknown >>>')\n\n\ndef userspace_tests(qmk_firmware):\n    if qmk_firmware:\n        cli.log.info(f'QMK home: {{fg_cyan}}{qmk_firmware}')\n\n    for path in qmk_userspace_paths():\n        try:\n            qmk_userspace_validate(path)\n            cli.log.info(f'Testing userspace candidate: {{fg_cyan}}{path}{{fg_reset}} -- {{fg_green}}Valid `qmk.json`')\n        except FileNotFoundError:\n            cli.log.warning(f'Testing userspace candidate: {{fg_cyan}}{path}{{fg_reset}} -- {{fg_red}}Missing `qmk.json`')\n        except UserspaceValidationError as err:\n            cli.log.warning(f'Testing userspace candidate: {{fg_cyan}}{path}{{fg_reset}} -- {{fg_red}}Invalid `qmk.json`')\n            cli.log.warning(f' -- {{fg_cyan}}{path}/qmk.json{{fg_reset}} validation error: {err}')\n\n    if QMK_USERSPACE is not None:\n        cli.log.info(f'QMK userspace: {{fg_cyan}}{QMK_USERSPACE}')\n    cli.log.info(f'Userspace enabled: {{fg_cyan}}{HAS_QMK_USERSPACE}')\n\n\n@cli.argument('-y', '--yes', action='store_true', arg_only=True, help='Answer yes to all questions.')\n@cli.argument('-n', '--no', action='store_true', arg_only=True, help='Answer no to all questions.')\n@cli.subcommand('Basic QMK environment checks')\ndef doctor(cli):\n    \"\"\"Basic QMK environment checks.\n\n    This is currently very simple, it just checks that all the expected binaries are on your system.\n\n    TODO(unclaimed):\n        * [ ] Compile a trivial program with each compiler\n    \"\"\"\n    cli.log.info('QMK Doctor is checking your environment.')\n    cli.log.info('Python version: %s', platform.python_version())\n    cli.log.info('CLI version: %s', cli.version)\n    cli.log.info('QMK home: {fg_cyan}%s', QMK_FIRMWARE)\n\n    status = os_status = os_tests()\n    distrib_tests()\n\n    userspace_tests(None)\n\n    git_status = git_tests()\n\n    if git_status == CheckStatus.ERROR or (os_status == CheckStatus.OK and git_status == CheckStatus.WARNING):\n        status = git_status\n\n    if in_virtualenv():\n        cli.log.info('CLI installed in virtualenv.')\n\n    # Make sure the basic CLI tools we need are available and can be executed.\n    bin_ok = check_binaries()\n    if bin_ok == CheckStatus.OK:\n        cli.log.info('All dependencies are installed.')\n    elif bin_ok == CheckStatus.WARNING:\n        cli.log.warning('Issues encountered while checking dependencies.')\n    else:\n        status = CheckStatus.ERROR\n\n    # Make sure the tools are at the correct version\n    ver_ok = check_binary_versions()\n    if CheckStatus.ERROR in ver_ok:\n        status = CheckStatus.ERROR\n    elif CheckStatus.WARNING in ver_ok and status == CheckStatus.OK:\n        status = CheckStatus.WARNING\n\n    # Check out the QMK submodules\n    sub_ok = check_submodules()\n    if sub_ok == CheckStatus.OK:\n        cli.log.info('Submodules are up to date.')\n    else:\n        if git_check_repo() and yesno('Would you like to clone the submodules?', default=True):\n            submodules.update()\n            sub_ok = check_submodules()\n\n        if sub_ok == CheckStatus.ERROR:\n            status = CheckStatus.ERROR\n        elif sub_ok == CheckStatus.WARNING and status == CheckStatus.OK:\n            status = CheckStatus.WARNING\n\n    output_submodule_status()\n\n    # Report a summary of our findings to the user\n    if status == CheckStatus.OK:\n        cli.log.info('{fg_green}QMK is ready to go')\n        return 0\n    elif status == CheckStatus.WARNING:\n        cli.log.info('{fg_yellow}QMK is ready to go, but minor problems were found')\n        return 1\n    else:\n        cli.log.info('{fg_red}Major problems detected, please fix these problems before proceeding.{fg_reset}')\n        cli.log.info('{fg_blue}If you\\'re missing dependencies, try following the instructions on: https://docs.qmk.fm/newbs_getting_started{fg_reset}')\n        cli.log.info('{fg_blue}Additionally, check out the FAQ (https://docs.qmk.fm/#/faq_build) or join the QMK Discord (https://discord.gg/qmk) for help.{fg_reset}')\n        return 2\n"
  },
  {
    "path": "lib/python/qmk/cli/doctor/windows.py",
    "content": "import platform\n\nfrom milc import cli\n\nfrom .check import CheckStatus, release_info\n\n\ndef os_test_windows():\n    \"\"\"Run the Windows specific tests.\n    \"\"\"\n    win32_ver = platform.win32_ver()\n    cli.log.info(\"Detected {fg_cyan}Windows %s (%s){fg_reset}.\", win32_ver[0], win32_ver[1])\n\n    # MSYS really does not like \"/\" files - resolve manually\n    file = cli.run(['cygpath', '-m', '/etc/qmk-release']).stdout.strip()\n    qmk_distro_version = release_info(file).get('VERSION', None)\n    if qmk_distro_version:\n        cli.log.info('QMK MSYS version: %s', qmk_distro_version)\n\n    return CheckStatus.OK\n"
  },
  {
    "path": "lib/python/qmk/cli/find.py",
    "content": "\"\"\"Command to search through all keyboards and keymaps for a given search criteria.\n\"\"\"\nimport os\nfrom milc import cli\nfrom qmk.search import filter_help, search_keymap_targets\nfrom qmk.util import maybe_exit_config\n\n\n@cli.argument(\n    '-f',\n    '--filter',\n    arg_only=True,\n    action='append',\n    default=[],\n    help=  # noqa: `format-python` and `pytest` don't agree here.\n    f\"Filter the list of keyboards based on their info.json data. Accepts the formats key=value, function(key), or function(key,value), eg. 'features.rgblight=true'. Valid functions are {filter_help()}. May be passed multiple times; all filters need to match. Value may include wildcards such as '*' and '?'.\"  # noqa: `format-python` and `pytest` don't agree here.\n)\n@cli.argument('-p', '--print', arg_only=True, action='append', default=[], help=\"For each matched target, print the value of the supplied info.json key. May be passed multiple times.\")\n@cli.argument('-km', '--keymap', type=str, default='default', help=\"The keymap name to build. Default is 'default'.\")\n@cli.subcommand('Find builds which match supplied search criteria.')\ndef find(cli):\n    \"\"\"Search through all keyboards and keymaps for a given search criteria.\n    \"\"\"\n    os.environ.setdefault('SKIP_SCHEMA_VALIDATION', '1')\n    maybe_exit_config(should_exit=False, should_reraise=True)\n\n    targets = search_keymap_targets([('all', cli.config.find.keymap)], cli.args.filter)\n    for target in sorted(targets, key=lambda t: (t.keyboard, t.keymap)):\n        print(f'{target}')\n\n        for key in cli.args.print:\n            print(f'    {key}={target.dotty.get(key, None)}')\n"
  },
  {
    "path": "lib/python/qmk/cli/flash.py",
    "content": "\"\"\"Compile and flash QMK Firmware\n\nYou can compile a keymap already in the repo or using a QMK Configurator export.\nA bootloader must be specified.\n\"\"\"\nfrom argcomplete.completers import FilesCompleter\nfrom pathlib import Path\n\nfrom milc import cli\n\nimport qmk.path\nfrom qmk.decorators import automagic_keyboard, automagic_keymap\nfrom qmk.commands import build_environment\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.keymap import keymap_completer, locate_keymap\nfrom qmk.flashers import flasher\nfrom qmk.build_targets import KeyboardKeymapBuildTarget, JsonKeymapBuildTarget\n\n\ndef _list_bootloaders():\n    \"\"\"Prints the available bootloaders listed in docs.qmk.fm.\n    \"\"\"\n    cli.print_help()\n    cli.log.info('Here are the available bootloaders:')\n    cli.echo('\\tavrdude')\n    cli.echo('\\tbootloadhid')\n    cli.echo('\\tdfu')\n    cli.echo('\\tdfu-util')\n    cli.echo('\\tmdloader')\n    cli.echo('\\tst-flash')\n    cli.echo('\\tst-link-cli')\n    cli.log.info('Enhanced variants for split keyboards:')\n    cli.echo('\\tavrdude-split-left')\n    cli.echo('\\tavrdude-split-right')\n    cli.echo('\\tdfu-ee')\n    cli.echo('\\tdfu-split-left')\n    cli.echo('\\tdfu-split-right')\n    cli.echo('\\tdfu-util-split-left')\n    cli.echo('\\tdfu-util-split-right')\n    cli.echo('\\tuf2-split-left')\n    cli.echo('\\tuf2-split-right')\n    cli.echo('For more info, visit https://docs.qmk.fm/#/flashing')\n    return False\n\n\ndef _flash_binary(filename, mcu):\n    \"\"\"Try to flash binary firmware\n    \"\"\"\n    cli.echo('Flashing binary firmware...\\nPlease reset your keyboard into bootloader mode now!\\nPress Ctrl-C to exit.\\n')\n    try:\n        err, msg = flasher(mcu, filename)\n        if err:\n            cli.log.error(msg)\n            return False\n    except KeyboardInterrupt:\n        cli.log.info('Ctrl-C was pressed, exiting...')\n    return True\n\n\n@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='A configurator export JSON to be compiled and flashed or a pre-compiled binary firmware file (bin/hex) to be flashed.')\n@cli.argument('-b', '--bootloaders', action='store_true', help='List the available bootloaders.')\n@cli.argument('-bl', '--bootloader', default='flash', help='The flash command, corresponding to qmk\\'s make options of bootloaders.')\n@cli.argument('-m', '--mcu', help='The MCU name. Required for HalfKay, HID, USBAspLoader and ISP flashing.')\n@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')\n@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')\n@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help=\"Don't actually build, just show the make command to be run.\")\n@cli.argument('-j', '--parallel', type=int, default=1, help=\"Set the number of parallel make jobs; 0 means unlimited.\")\n@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help=\"Set a variable to be passed to make. May be passed multiple times.\")\n@cli.argument('-c', '--clean', arg_only=True, action='store_true', help=\"Remove object files before compiling.\")\n@cli.subcommand('QMK Flash.')\n@automagic_keyboard\n@automagic_keymap\ndef flash(cli):\n    \"\"\"Compile and or flash QMK Firmware or keyboard/layout\n\n    If a binary firmware is supplied, try to flash that.\n\n    If a Configurator export is supplied this command will create a new keymap, overwriting an existing keymap if one exists.\n\n    If a keyboard and keymap are provided this command will build a firmware based on that.\n\n    If bootloader is omitted the make system will use the configured bootloader for that keyboard.\n    \"\"\"\n    if cli.args.filename and isinstance(cli.args.filename, Path) and cli.args.filename.suffix in ['.bin', '.hex', '.uf2']:\n        return _flash_binary(cli.args.filename, cli.args.mcu)\n\n    if cli.args.bootloaders:\n        return _list_bootloaders()\n\n    # Build the environment vars\n    envs = build_environment(cli.args.env)\n\n    # Handler for the build target\n    target = None\n\n    if cli.args.filename:\n        # if we were given a filename, assume we have a json build target\n        target = JsonKeymapBuildTarget(cli.args.filename)\n\n    elif cli.config.flash.keyboard and cli.config.flash.keymap:\n        # if we got a keyboard and keymap, attempt to find it\n        if not locate_keymap(cli.config.flash.keyboard, cli.config.flash.keymap):\n            cli.log.error('Invalid keymap argument.')\n            cli.print_help()\n            return False\n\n        # If we got here, then we have a valid keyboard and keymap for a build target\n        target = KeyboardKeymapBuildTarget(cli.config.flash.keyboard, cli.config.flash.keymap)\n\n    if not target:\n        cli.log.error('You must supply a configurator export, both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')\n        cli.print_help()\n        return False\n\n    target.configure(parallel=cli.config.flash.parallel, clean=cli.args.clean)\n    return target.compile(cli.args.bootloader, dry_run=cli.args.dry_run, **envs)\n"
  },
  {
    "path": "lib/python/qmk/cli/format/__init__.py",
    "content": ""
  },
  {
    "path": "lib/python/qmk/cli/format/c.py",
    "content": "\"\"\"Format C code according to QMK's style.\n\"\"\"\nfrom shutil import which\nfrom subprocess import CalledProcessError, DEVNULL, Popen, PIPE\n\nfrom argcomplete.completers import FilesCompleter\nfrom milc import cli\n\nfrom qmk.path import normpath\nfrom qmk.c_parse import c_source_files\n\nc_file_suffixes = ('c', 'h', 'cpp', 'hpp')\ncore_dirs = ('drivers', 'quantum', 'tests', 'tmk_core', 'platforms', 'modules')\nignored = ('tmk_core/protocol/usb_hid', 'platforms/chibios/boards')\n\n\ndef is_relative_to(file, other):\n    \"\"\"Provide similar behavior to PurePath.is_relative_to in Python > 3.9\n    \"\"\"\n    return str(normpath(file).resolve()).startswith(str(normpath(other).resolve()))\n\n\ndef find_clang_format():\n    \"\"\"Returns the path to clang-format.\n    \"\"\"\n    for clang_version in range(20, 6, -1):\n        binary = f'clang-format-{clang_version}'\n\n        if which(binary):\n            return binary\n\n    return 'clang-format'\n\n\ndef find_diffs(files):\n    \"\"\"Run clang-format and diff it against a file.\n    \"\"\"\n    found_diffs = False\n\n    for file in files:\n        cli.log.debug('Checking for changes in %s', file)\n        clang_format = Popen([find_clang_format(), file], stdout=PIPE, stderr=PIPE, universal_newlines=True)\n        diff = cli.run(['diff', '-u', f'--label=a/{file}', f'--label=b/{file}', str(file), '-'], stdin=clang_format.stdout, capture_output=True)\n\n        if diff.returncode != 0:\n            print(diff.stdout)\n            found_diffs = True\n\n    return found_diffs\n\n\ndef cformat_run(files):\n    \"\"\"Spawn clang-format subprocess with proper arguments\n    \"\"\"\n    # Determine which version of clang-format to use\n    clang_format = [find_clang_format(), '-i']\n\n    try:\n        cli.run([*clang_format, *map(str, files)], check=True, capture_output=False, stdin=DEVNULL)\n        cli.log.info('Successfully formatted the C code.')\n        return True\n\n    except CalledProcessError as e:\n        cli.log.error('Error formatting C code!')\n        cli.log.debug('%s exited with returncode %s', e.cmd, e.returncode)\n        cli.log.debug('STDOUT:')\n        cli.log.debug(e.stdout)\n        cli.log.debug('STDERR:')\n        cli.log.debug(e.stderr)\n        return False\n\n\ndef filter_files(files, core_only=False):\n    \"\"\"Yield only files to be formatted and skip the rest\n    \"\"\"\n    files = list(map(normpath, filter(None, files)))\n\n    for file in files:\n        if core_only:\n            # The following statement checks each file to see if the file path is\n            # - in the core directories\n            # - not in the ignored directories\n            if not any(is_relative_to(file, i) for i in core_dirs) or any(is_relative_to(file, i) for i in ignored):\n                cli.log.debug(\"Skipping non-core file %s, as '--core-only' is used.\", file)\n                continue\n\n        if file.suffix[1:] in c_file_suffixes:\n            yield file\n        else:\n            cli.log.debug('Skipping file %s', file)\n\n\n@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help=\"Flag only, don't automatically format.\")\n@cli.argument('-b', '--base-branch', default='origin/master', help='Branch to compare to diffs to.')\n@cli.argument('-a', '--all-files', arg_only=True, action='store_true', help='Format all core files.')\n@cli.argument('--core-only', arg_only=True, action='store_true', help='Format core files only.')\n@cli.argument('files', nargs='*', arg_only=True, type=normpath, completer=FilesCompleter('.c'), help='Filename(s) to format.')\n@cli.subcommand(\"Format C code according to QMK's style.\", hidden=False if cli.config.user.developer else True)\ndef format_c(cli):\n    \"\"\"Format C code according to QMK's style.\n    \"\"\"\n    # Find the list of files to format\n    if cli.args.files:\n        files = list(filter_files(cli.args.files, cli.args.core_only))\n\n        if not files:\n            cli.log.error('No C files in filelist: %s', ', '.join(map(str, cli.args.files)))\n            exit(0)\n\n        if cli.args.all_files:\n            cli.log.warning('Filenames passed with -a, only formatting: %s', ','.join(map(str, files)))\n\n    elif cli.args.all_files:\n        all_files = c_source_files(core_dirs)\n        files = list(filter_files(all_files, True))\n\n    else:\n        git_diff_cmd = ['git', 'diff', '--name-only', cli.args.base_branch, *core_dirs]\n        git_diff = cli.run(git_diff_cmd, stdin=DEVNULL)\n\n        if git_diff.returncode != 0:\n            cli.log.error(\"Error running %s\", git_diff_cmd)\n            print(git_diff.stderr)\n            return git_diff.returncode\n\n        changed_files = git_diff.stdout.strip().split('\\n')\n        files = list(filter_files(changed_files, True))\n\n    # Sanity check\n    if not files:\n        cli.log.error('No changed files detected. Use \"qmk format-c -a\" to format all core files')\n        return False\n\n    # Run clang-format on the files we've found\n    if cli.args.dry_run:\n        return not find_diffs(files)\n    else:\n        return cformat_run(files)\n"
  },
  {
    "path": "lib/python/qmk/cli/format/json.py",
    "content": "\"\"\"JSON Formatting Script\n\nSpits out a JSON file formatted with one of QMK's formatters.\n\"\"\"\nimport json\n\nfrom jsonschema import ValidationError\nfrom milc import cli\n\nfrom qmk.info import info_json\nfrom qmk.json_schema import json_load, validate\nfrom qmk.json_encoders import InfoJSONEncoder, KeymapJSONEncoder, UserspaceJSONEncoder, CommunityModuleJSONEncoder\nfrom qmk.path import normpath\n\n\ndef _detect_json_format(file, json_data):\n    \"\"\"Detect the format of a json file.\n    \"\"\"\n    json_encoder = None\n    try:\n        validate(json_data, 'qmk.user_repo.v1_1')\n        json_encoder = UserspaceJSONEncoder\n    except ValidationError:\n        pass\n\n    if json_encoder is None:\n        try:\n            validate(json_data, 'qmk.user_repo.v1')\n            json_encoder = UserspaceJSONEncoder\n        except ValidationError:\n            pass\n\n    if json_encoder is None:\n        try:\n            validate(json_data, 'qmk.community_module.v1')\n            json_encoder = CommunityModuleJSONEncoder\n        except ValidationError:\n            pass\n\n    if json_encoder is None:\n        try:\n            validate(json_data, 'qmk.keyboard.v1')\n            json_encoder = InfoJSONEncoder\n        except ValidationError as e:\n            cli.log.warning('File %s did not validate as a keyboard info.json or userspace qmk.json:\\n\\t%s', file, e)\n            cli.log.info('Treating %s as a keymap file.', file)\n            json_encoder = KeymapJSONEncoder\n\n    return json_encoder\n\n\ndef _get_json_encoder(file, json_data):\n    \"\"\"Get the json encoder for a file.\n    \"\"\"\n    json_encoder = None\n    if cli.args.format == 'auto':\n        json_encoder = _detect_json_format(file, json_data)\n    elif cli.args.format == 'keyboard':\n        json_encoder = InfoJSONEncoder\n    elif cli.args.format == 'keymap':\n        json_encoder = KeymapJSONEncoder\n    elif cli.args.format == 'userspace':\n        json_encoder = UserspaceJSONEncoder\n    elif cli.args.format == 'community_module':\n        json_encoder = CommunityModuleJSONEncoder\n    else:\n        # This should be impossible\n        cli.log.error('Unknown format: %s', cli.args.format)\n    return json_encoder\n\n\n@cli.argument('json_file', arg_only=True, type=normpath, help='JSON file to format')\n@cli.argument('-f', '--format', choices=['auto', 'keyboard', 'keymap', 'userspace', 'community_module'], default='auto', arg_only=True, help='JSON formatter to use (Default: autodetect)')\n@cli.argument('-i', '--inplace', action='store_true', arg_only=True, help='If set, will operate in-place on the input file')\n@cli.argument('-p', '--print', action='store_true', arg_only=True, help='If set, will print the formatted json to stdout ')\n@cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True)\ndef format_json(cli):\n    \"\"\"Format a json file.\n    \"\"\"\n    json_data = json_load(cli.args.json_file)\n\n    json_encoder = _get_json_encoder(cli.args.json_file, json_data)\n    if json_encoder is None:\n        return False\n\n    if json_encoder == KeymapJSONEncoder and 'layout' in json_data:\n        # Attempt to format the keycodes.\n        layout = json_data['layout']\n        info_data = info_json(json_data['keyboard'])\n\n        if layout in info_data.get('layout_aliases', {}):\n            layout = json_data['layout'] = info_data['layout_aliases'][layout]\n\n        if layout in info_data.get('layouts'):\n            for layer_num, layer in enumerate(json_data['layers']):\n                current_layer = []\n                last_row = 0\n\n                for keymap_key, info_key in zip(layer, info_data['layouts'][layout]['layout']):\n                    if last_row != info_key['y']:\n                        current_layer.append('JSON_NEWLINE')\n                        last_row = info_key['y']\n\n                    current_layer.append(keymap_key)\n\n                json_data['layers'][layer_num] = current_layer\n\n    output = json.dumps(json_data, cls=json_encoder, sort_keys=True)\n\n    if cli.args.inplace:\n        with open(cli.args.json_file, 'w+', encoding='utf-8', newline='\\n') as outfile:\n            outfile.write(output + '\\n')\n\n    # Display the results if print was set\n    # We don't operate in-place by default, so also display to stdout\n    # if in-place is not set.\n    if cli.args.print or not cli.args.inplace:\n        print(output)\n"
  },
  {
    "path": "lib/python/qmk/cli/format/python.py",
    "content": "\"\"\"Format python code according to QMK's style.\n\"\"\"\nfrom subprocess import CalledProcessError, DEVNULL\n\nfrom milc import cli\n\nfrom qmk.path import normpath\n\npy_file_suffixes = ('py',)\npy_dirs = ['lib/python', 'util/ci']\n\n\ndef yapf_run(files):\n    edit = '--diff' if cli.args.dry_run else '--in-place'\n    yapf_cmd = ['yapf', '-vv', '--recursive', edit, *files]\n    try:\n        cli.run(yapf_cmd, check=True, capture_output=False, stdin=DEVNULL)\n        cli.log.info('Successfully formatted the python code.')\n\n    except CalledProcessError:\n        cli.log.error(f'Python code in {\",\".join(py_dirs)} incorrectly formatted!')\n        return False\n\n\ndef filter_files(files):\n    \"\"\"Yield only files to be formatted and skip the rest\n    \"\"\"\n    files = list(map(normpath, filter(None, files)))\n    for file in files:\n        if file.suffix[1:] in py_file_suffixes:\n            yield file\n        else:\n            cli.log.debug('Skipping file %s', file)\n\n\n@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help=\"Don't actually format.\")\n@cli.argument('-b', '--base-branch', default='origin/master', help='Branch to compare to diffs to.')\n@cli.argument('-a', '--all-files', arg_only=True, action='store_true', help='Format all files.')\n@cli.argument('files', nargs='*', arg_only=True, type=normpath, help='Filename(s) to format.')\n@cli.subcommand(\"Format python code according to QMK's style.\", hidden=False if cli.config.user.developer else True)\ndef format_python(cli):\n    \"\"\"Format python code according to QMK's style.\n    \"\"\"\n    # Find the list of files to format\n    if cli.args.files:\n        files = list(filter_files(cli.args.files))\n\n        if not files:\n            cli.log.error('No Python files in filelist: %s', ', '.join(map(str, cli.args.files)))\n            exit(0)\n\n        if cli.args.all_files:\n            cli.log.warning('Filenames passed with -a, only formatting: %s', ','.join(map(str, files)))\n\n    elif cli.args.all_files:\n        git_ls_cmd = ['git', 'ls-files', *py_dirs]\n        git_ls = cli.run(git_ls_cmd, stdin=DEVNULL)\n        files = list(filter_files(git_ls.stdout.split('\\n')))\n\n    else:\n        git_diff_cmd = ['git', 'diff', '--name-only', cli.args.base_branch, *py_dirs]\n        git_diff = cli.run(git_diff_cmd, stdin=DEVNULL)\n        files = list(filter_files(git_diff.stdout.split('\\n')))\n\n    # Sanity check\n    if not files:\n        cli.log.error('No changed files detected. Use \"qmk format-python -a\" to format all files')\n        return False\n\n    return yapf_run(files)\n"
  },
  {
    "path": "lib/python/qmk/cli/format/text.py",
    "content": "\"\"\"Ensure text files have the proper line endings.\n\"\"\"\nfrom itertools import islice\nfrom subprocess import DEVNULL\n\nfrom milc import cli\n\nfrom qmk.path import normpath\n\n\ndef _get_chunks(it, size):\n    \"\"\"Break down a collection into smaller parts\n    \"\"\"\n    it = iter(it)\n    return iter(lambda: tuple(islice(it, size)), ())\n\n\ndef dos2unix_run(files):\n    \"\"\"Spawn multiple dos2unix subprocess avoiding too long commands on formatting everything\n    \"\"\"\n    for chunk in _get_chunks([normpath(file).as_posix() for file in files], 10):\n        dos2unix = cli.run(['dos2unix', *chunk])\n\n        if dos2unix.returncode:\n            return False\n\n\n@cli.argument('-b', '--base-branch', default='origin/master', help='Branch to compare to diffs to.')\n@cli.argument('-a', '--all-files', arg_only=True, action='store_true', help='Format all files.')\n@cli.argument('files', nargs='*', arg_only=True, type=normpath, help='Filename(s) to format.')\n@cli.subcommand(\"Ensure text files have the proper line endings.\", hidden=True)\ndef format_text(cli):\n    \"\"\"Ensure text files have the proper line endings.\n    \"\"\"\n    # Find the list of files to format\n    if cli.args.files:\n        files = list(cli.args.files)\n\n        if cli.args.all_files:\n            cli.log.warning('Filenames passed with -a, only formatting: %s', ','.join(map(str, files)))\n\n    elif cli.args.all_files:\n        git_ls_cmd = ['git', 'ls-files']\n        git_ls = cli.run(git_ls_cmd, stdin=DEVNULL)\n        files = list(filter(None, git_ls.stdout.split('\\n')))\n\n    else:\n        git_diff_cmd = ['git', 'diff', '--name-only', cli.args.base_branch]\n        git_diff = cli.run(git_diff_cmd, stdin=DEVNULL)\n        files = list(filter(None, git_diff.stdout.split('\\n')))\n\n    # Sanity check\n    if not files:\n        cli.log.error('No changed files detected. Use \"qmk format-text -a\" to format all files')\n        return False\n\n    return dos2unix_run(files)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/__init__.py",
    "content": ""
  },
  {
    "path": "lib/python/qmk/cli/generate/api.py",
    "content": "\"\"\"This script automates the generation of the QMK API data.\n\"\"\"\nfrom pathlib import Path\nimport shutil\nimport json\n\nfrom milc import cli\n\nimport qmk.path\nfrom qmk.datetime import current_datetime\nfrom qmk.info import info_json\nfrom qmk.json_schema import json_load\nfrom qmk.keymap import list_keymaps\nfrom qmk.keyboard import find_readme, list_keyboards, keyboard_alias_definitions\nfrom qmk.keycodes import load_spec, list_versions, list_languages\n\nDATA_PATH = Path('data')\nTEMPLATE_PATH = DATA_PATH / 'templates/api/'\nBUILD_API_PATH = Path('.build/api_data/')\n\n\ndef _list_constants(output_folder):\n    \"\"\"Produce a map of available constants\n    \"\"\"\n    ret = {}\n    for file in (output_folder / 'constants').glob('**/*_[0-9].[0-9].[0-9].json'):\n        name, version = file.stem.rsplit('_', 1)\n        if name not in ret:\n            ret[name] = []\n        ret[name].append(version)\n\n    # Ensure content is sorted\n    for name in ret:\n        ret[name] = sorted(ret[name])\n\n    return ret\n\n\ndef _resolve_keycode_specs(output_folder):\n    \"\"\"To make it easier for consumers, publish pre-merged spec files\n    \"\"\"\n    for version in list_versions():\n        overall = load_spec(version)\n\n        output_file = output_folder / f'constants/keycodes_{version}.json'\n        output_file.write_text(json.dumps(overall, separators=(',', ':')), encoding='utf-8')\n\n    for lang in list_languages():\n        for version in list_versions(lang):\n            overall = load_spec(version, lang)\n\n            output_file = output_folder / f'constants/keycodes_{lang}_{version}.json'\n            output_file.write_text(json.dumps(overall, separators=(',', ':')), encoding='utf-8')\n\n    # Purge files consumed by 'load_spec'\n    shutil.rmtree(output_folder / 'constants/keycodes/')\n\n\ndef _filtered_copy(src, dst):\n    src = Path(src)\n    dst = Path(dst)\n\n    if dst.suffix == '.hjson':\n        data = json_load(src)\n\n        dst = dst.with_suffix('.json')\n        dst.write_text(json.dumps(data, separators=(',', ':')), encoding='utf-8')\n        return dst\n\n    if dst.suffix == '.jsonschema':\n        data = json_load(src)\n\n        dst.write_text(json.dumps(data), encoding='utf-8')\n        return dst\n\n    return shutil.copy2(src, dst)\n\n\ndef _filtered_keyboard_list():\n    \"\"\"Perform basic filtering of list_keyboards\n    \"\"\"\n    keyboard_list = list_keyboards()\n    if cli.args.filter:\n        kb_list = []\n        for keyboard_name in keyboard_list:\n            if any(i in keyboard_name for i in cli.args.filter):\n                kb_list.append(keyboard_name)\n        keyboard_list = kb_list\n    return keyboard_list\n\n\n@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help=\"Don't write the data to disk.\")\n@cli.argument('-f', '--filter', arg_only=True, action='append', default=[], help=\"Filter the list of keyboards based on partial name matches the supplied value. May be passed multiple times.\")\n@cli.subcommand('Generate QMK API data', hidden=False if cli.config.user.developer else True)\ndef generate_api(cli):\n    \"\"\"Generates the QMK API data.\n    \"\"\"\n    v1_dir = BUILD_API_PATH / 'v1'\n    keyboard_all_file = v1_dir / 'keyboards.json'  # A massive JSON containing everything\n    keyboard_list_file = v1_dir / 'keyboard_list.json'  # A simple list of keyboard targets\n    keyboard_aliases_file = v1_dir / 'keyboard_aliases.json'  # A list of historical keyboard names and their new name\n    keyboard_metadata_file = v1_dir / 'keyboard_metadata.json'  # All the data configurator/via needs for initialization\n    constants_metadata_file = v1_dir / 'constants_metadata.json'  # Metadata for available constants\n    usb_file = v1_dir / 'usb.json'  # A mapping of USB VID/PID -> keyboard target\n\n    if BUILD_API_PATH.exists():\n        shutil.rmtree(BUILD_API_PATH)\n\n    shutil.copytree(TEMPLATE_PATH, BUILD_API_PATH)\n    shutil.copytree(DATA_PATH, v1_dir, copy_function=_filtered_copy)\n\n    # Filter down when required\n    keyboard_list = _filtered_keyboard_list()\n\n    kb_all = {}\n    usb_list = {}\n\n    # Generate and write keyboard specific JSON files\n    for keyboard_name in keyboard_list:\n        kb_json = info_json(keyboard_name)\n        kb_all[keyboard_name] = kb_json\n\n        keyboard_dir = v1_dir / 'keyboards' / keyboard_name\n        keyboard_info = keyboard_dir / 'info.json'\n        keyboard_readme = keyboard_dir / 'readme.md'\n        keyboard_readme_src = find_readme(keyboard_name)\n\n        # Populate the list of JSON keymaps\n        for keymap in list_keymaps(keyboard_name, c=False, fullpath=True):\n            keymap_rel = qmk.path.under_qmk_firmware(keymap)\n            if keymap_rel is None:\n                cli.log.debug('Skipping keymap %s (not in qmk_firmware)', keymap)\n                continue\n\n            if (keymap_rel / 'keymap.c').exists():\n                cli.log.debug('Skipping keymap %s (not pure dd keymap)', keymap)\n                continue\n\n            kb_json['keymaps'][keymap.name] = {\n                # TODO: deprecate 'url' as consumer needs to know its potentially hjson\n                'url': f'https://raw.githubusercontent.com/qmk/qmk_firmware/master/{keymap_rel}/keymap.json',\n\n                # Instead consumer should grab from API and not repo directly\n                'path': (keymap_rel / 'keymap.json').as_posix(),\n            }\n\n        keyboard_dir.mkdir(parents=True, exist_ok=True)\n        keyboard_json = json.dumps({'last_updated': current_datetime(), 'keyboards': {keyboard_name: kb_json}}, separators=(',', ':'))\n        if not cli.args.dry_run:\n            keyboard_info.write_text(keyboard_json, encoding='utf-8')\n            cli.log.debug('Wrote file %s', keyboard_info)\n\n            if keyboard_readme_src:\n                shutil.copyfile(keyboard_readme_src, keyboard_readme)\n                cli.log.debug('Copied %s -> %s', keyboard_readme_src, keyboard_readme)\n\n            # resolve keymaps as json\n            for keymap in kb_json['keymaps']:\n                keymap_hjson = kb_json['keymaps'][keymap]['path']\n                keymap_json = v1_dir / keymap_hjson\n                keymap_json.parent.mkdir(parents=True, exist_ok=True)\n                keymap_json.write_text(json.dumps(json_load(Path(keymap_hjson)), separators=(',', ':')), encoding='utf-8')\n                cli.log.debug('Wrote keymap %s', keymap_json)\n\n        if 'usb' in kb_json:\n            usb = kb_json['usb']\n\n            if 'vid' in usb and usb['vid'] not in usb_list:\n                usb_list[usb['vid']] = {}\n\n            if 'pid' in usb and usb['pid'] not in usb_list[usb['vid']]:\n                usb_list[usb['vid']][usb['pid']] = {}\n\n            if 'vid' in usb and 'pid' in usb:\n                usb_list[usb['vid']][usb['pid']][keyboard_name] = usb\n\n    # Generate data for the global files\n    keyboard_list = sorted(kb_all)\n    keyboard_aliases = keyboard_alias_definitions()\n    keyboard_metadata = {\n        'last_updated': current_datetime(),\n        'keyboards': keyboard_list,\n        'keyboard_aliases': keyboard_aliases,\n        'usb': usb_list,\n    }\n\n    # Feature specific handling\n    _resolve_keycode_specs(v1_dir)\n\n    # Write the global JSON files\n    keyboard_all_json = json.dumps({'last_updated': current_datetime(), 'keyboards': kb_all}, separators=(',', ':'))\n    usb_json = json.dumps({'last_updated': current_datetime(), 'usb': usb_list}, separators=(',', ':'))\n    keyboard_list_json = json.dumps({'last_updated': current_datetime(), 'keyboards': keyboard_list}, separators=(',', ':'))\n    keyboard_aliases_json = json.dumps({'last_updated': current_datetime(), 'keyboard_aliases': keyboard_aliases}, separators=(',', ':'))\n    keyboard_metadata_json = json.dumps(keyboard_metadata, separators=(',', ':'))\n    constants_metadata_json = json.dumps({'last_updated': current_datetime(), 'constants': _list_constants(v1_dir)}, separators=(',', ':'))\n\n    if not cli.args.dry_run:\n        keyboard_all_file.write_text(keyboard_all_json, encoding='utf-8')\n        usb_file.write_text(usb_json, encoding='utf-8')\n        keyboard_list_file.write_text(keyboard_list_json, encoding='utf-8')\n        keyboard_aliases_file.write_text(keyboard_aliases_json, encoding='utf-8')\n        keyboard_metadata_file.write_text(keyboard_metadata_json, encoding='utf-8')\n        constants_metadata_file.write_text(constants_metadata_json, encoding='utf-8')\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/autocorrect_data.py",
    "content": "# Copyright 2021 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Python program to make autocorrect_data.h.\nThis program reads from a prepared dictionary file and generates a C source file\n\"autocorrect_data.h\" with a serialized trie embedded as an array. Run this\nprogram and pass it as the first argument like:\n$ qmk generate-autocorrect-data autocorrect_dict.txt\nEach line of the dict file defines one typo and its correction with the syntax\n\"typo -> correction\". Blank lines or lines starting with '#' are ignored.\nExample:\n  :thier        -> their\n  fitler        -> filter\n  lenght        -> length\n  ouput         -> output\n  widht         -> width\nFor full documentation, see QMK Docs\n\"\"\"\n\nimport textwrap\nfrom typing import Any, Dict, Iterator, List, Tuple\n\nfrom milc import cli\n\nfrom qmk.commands import dump_lines\nfrom qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.keymap import keymap_completer, locate_keymap\nfrom qmk.path import normpath\nfrom qmk.util import maybe_exit\n\nKC_A = 4\nKC_SPC = 0x2c\nKC_QUOT = 0x34\n\nTYPO_CHARS = dict([\n    (\"'\", KC_QUOT),\n    (':', KC_SPC),  # \"Word break\" character.\n] + [(chr(c), c + KC_A - ord('a')) for c in range(ord('a'),\n                                                  ord('z') + 1)])  # Characters a-z.\n\n\ndef parse_file(file_name: str) -> List[Tuple[str, str]]:\n    \"\"\"Parses autocorrections dictionary file.\n  Each line of the file defines one typo and its correction with the syntax\n  \"typo -> correction\". Blank lines or lines starting with '#' are ignored. The\n  function validates that typos only have characters a-z and that typos are not\n  substrings of other typos, otherwise the longer typo would never trigger.\n  Args:\n    file_name: String, path of the autocorrections dictionary.\n  Returns:\n    List of (typo, correction) tuples.\n  \"\"\"\n\n    try:\n        import english_words\n        correct_words = english_words.get_english_words_set(['web2'], lower=True, alpha=True)\n    except AttributeError:\n        from english_words import english_words_lower_alpha_set as correct_words\n        if not cli.args.quiet:\n            cli.echo('The english_words package is outdated, update by running:')\n            cli.echo('  {fg_cyan}python3 -m pip install english_words --upgrade')\n    except ImportError:\n        if not cli.args.quiet:\n            cli.echo('Autocorrection will falsely trigger when a typo is a substring of a correctly spelled word.')\n            cli.echo('To check for this, install the english_words package and rerun this script:')\n            cli.echo('  {fg_cyan}python3 -m pip install english_words')\n        # Use a minimal word list as a fallback.\n        correct_words = ('information', 'available', 'international', 'language', 'loosest', 'reference', 'wealthier', 'entertainment', 'association', 'provides', 'technology', 'statehood')\n\n    autocorrections = []\n    typos = set()\n    for line_number, typo, correction in parse_file_lines(file_name):\n        if typo in typos:\n            cli.log.warning('{fg_red}Error:%d:{fg_reset} Ignoring duplicate typo: \"{fg_cyan}%s{fg_reset}\"', line_number, typo)\n            continue\n\n        # Check that `typo` is valid.\n        if not (all([c in TYPO_CHARS for c in typo])):\n            cli.log.error('{fg_red}Error:%d:{fg_reset} Typo \"{fg_cyan}%s{fg_reset}\" has characters other than a-z, \\' and :.', line_number, typo)\n            maybe_exit(1)\n        for other_typo in typos:\n            if typo in other_typo or other_typo in typo:\n                cli.log.error('{fg_red}Error:%d:{fg_reset} Typos may not be substrings of one another, otherwise the longer typo would never trigger: \"{fg_cyan}%s{fg_reset}\" vs. \"{fg_cyan}%s{fg_reset}\".', line_number, typo, other_typo)\n                maybe_exit(1)\n        if len(typo) < 5:\n            cli.log.warning('{fg_yellow}Warning:%d:{fg_reset} It is suggested that typos are at least 5 characters long to avoid false triggers: \"{fg_cyan}%s{fg_reset}\"', line_number, typo)\n        if len(typo) > 127:\n            cli.log.error('{fg_red}Error:%d:{fg_reset} Typo exceeds 127 chars: \"{fg_cyan}%s{fg_reset}\"', line_number, typo)\n            maybe_exit(1)\n\n        check_typo_against_dictionary(typo, line_number, correct_words)\n\n        autocorrections.append((typo, correction))\n        typos.add(typo)\n\n    return autocorrections\n\n\ndef make_trie(autocorrections: List[Tuple[str, str]]) -> Dict[str, Any]:\n    \"\"\"Makes a trie from the the typos, writing in reverse.\n  Args:\n    autocorrections: List of (typo, correction) tuples.\n  Returns:\n    Dict of dict, representing the trie.\n  \"\"\"\n    trie = {}\n    for typo, correction in autocorrections:\n        node = trie\n        for letter in typo[::-1]:\n            node = node.setdefault(letter, {})\n        node['LEAF'] = (typo, correction)\n\n    return trie\n\n\ndef parse_file_lines(file_name: str) -> Iterator[Tuple[int, str, str]]:\n    \"\"\"Parses lines read from `file_name` into typo-correction pairs.\"\"\"\n\n    line_number = 0\n    for line in open(file_name, 'rt'):\n        line_number += 1\n        line = line.strip()\n        if line and line[0] != '#':\n            # Parse syntax \"typo -> correction\", using strip to ignore indenting.\n            tokens = [token.strip() for token in line.split('->', 1)]\n            if len(tokens) != 2 or not tokens[0]:\n                print(f'Error:{line_number}: Invalid syntax: \"{line}\"')\n                maybe_exit(1)\n\n            typo, correction = tokens\n            typo = typo.lower()  # Force typos to lowercase.\n            typo = typo.replace(' ', ':')\n\n            yield line_number, typo, correction\n\n\ndef check_typo_against_dictionary(typo: str, line_number: int, correct_words) -> None:\n    \"\"\"Checks `typo` against English dictionary words.\"\"\"\n\n    if typo.startswith(':') and typo.endswith(':'):\n        if typo[1:-1] in correct_words:\n            cli.log.warning('{fg_yellow}Warning:%d:{fg_reset} Typo \"{fg_cyan}%s{fg_reset}\" is a correctly spelled dictionary word.', line_number, typo)\n    elif typo.startswith(':') and not typo.endswith(':'):\n        for word in correct_words:\n            if word.startswith(typo[1:]):\n                cli.log.warning('{fg_yellow}Warning:%d: {fg_reset}Typo \"{fg_cyan}%s{fg_reset}\" would falsely trigger on correctly spelled word \"{fg_cyan}%s{fg_reset}\".', line_number, typo, word)\n    elif not typo.startswith(':') and typo.endswith(':'):\n        for word in correct_words:\n            if word.endswith(typo[:-1]):\n                cli.log.warning('{fg_yellow}Warning:%d:{fg_reset} Typo \"{fg_cyan}%s{fg_reset}\" would falsely trigger on correctly spelled word \"{fg_cyan}%s{fg_reset}\".', line_number, typo, word)\n    elif not typo.startswith(':') and not typo.endswith(':'):\n        for word in correct_words:\n            if typo in word:\n                cli.log.warning('{fg_yellow}Warning:%d:{fg_reset} Typo \"{fg_cyan}%s{fg_reset}\" would falsely trigger on correctly spelled word \"{fg_cyan}%s{fg_reset}\".', line_number, typo, word)\n\n\ndef serialize_trie(autocorrections: List[Tuple[str, str]], trie: Dict[str, Any]) -> List[int]:\n    \"\"\"Serializes trie and correction data in a form readable by the C code.\n  Args:\n    autocorrections: List of (typo, correction) tuples.\n    trie: Dict of dicts.\n  Returns:\n    List of ints in the range 0-255.\n  \"\"\"\n    table = []\n\n    # Traverse trie in depth first order.\n    def traverse(trie_node):\n        if 'LEAF' in trie_node:  # Handle a leaf trie node.\n            typo, correction = trie_node['LEAF']\n            word_boundary_ending = typo[-1] == ':'\n            typo = typo.strip(':')\n            i = 0  # Make the autocorrection data for this entry and serialize it.\n            while i < min(len(typo), len(correction)) and typo[i] == correction[i]:\n                i += 1\n            backspaces = len(typo) - i - 1 + word_boundary_ending\n            assert 0 <= backspaces <= 63\n            correction = correction[i:]\n            bs_count = [backspaces + 128]\n            data = bs_count + list(bytes(correction, 'ascii')) + [0]\n\n            entry = {'data': data, 'links': [], 'byte_offset': 0}\n            table.append(entry)\n        elif len(trie_node) == 1:  # Handle trie node with a single child.\n            c, trie_node = next(iter(trie_node.items()))\n            entry = {'chars': c, 'byte_offset': 0}\n\n            # It's common for a trie to have long chains of single-child nodes. We\n            # find the whole chain so that we can serialize it more efficiently.\n            while len(trie_node) == 1 and 'LEAF' not in trie_node:\n                c, trie_node = next(iter(trie_node.items()))\n                entry['chars'] += c\n\n            table.append(entry)\n            entry['links'] = [traverse(trie_node)]\n        else:  # Handle trie node with multiple children.\n            entry = {'chars': ''.join(sorted(trie_node.keys())), 'byte_offset': 0}\n            table.append(entry)\n            entry['links'] = [traverse(trie_node[c]) for c in entry['chars']]\n        return entry\n\n    traverse(trie)\n\n    def serialize(e: Dict[str, Any]) -> List[int]:\n        if not e['links']:  # Handle a leaf table entry.\n            return e['data']\n        elif len(e['links']) == 1:  # Handle a chain table entry.\n            return [TYPO_CHARS[c] for c in e['chars']] + [0]  # + encode_link(e['links'][0]))\n        else:  # Handle a branch table entry.\n            data = []\n            for c, link in zip(e['chars'], e['links']):\n                data += [TYPO_CHARS[c] | (0 if data else 64)] + encode_link(link)\n            return data + [0]\n\n    byte_offset = 0\n    for e in table:  # To encode links, first compute byte offset of each entry.\n        e['byte_offset'] = byte_offset\n        byte_offset += len(serialize(e))\n        assert 0 <= byte_offset <= 0xffff\n\n    return [b for e in table for b in serialize(e)]  # Serialize final table.\n\n\ndef encode_link(link: Dict[str, Any]) -> List[int]:\n    \"\"\"Encodes a node link as two bytes.\"\"\"\n    byte_offset = link['byte_offset']\n    if not (0 <= byte_offset <= 0xffff):\n        cli.log.error('{fg_red}Error:{fg_reset} The autocorrection table is too large, a node link exceeds 64KB limit. Try reducing the autocorrection dict to fewer entries.')\n        maybe_exit(1)\n    return [byte_offset & 255, byte_offset >> 8]\n\n\ndef typo_len(e: Tuple[str, str]) -> int:\n    return len(e[0])\n\n\ndef to_hex(b: int) -> str:\n    return f'0x{b:02X}'\n\n\n@cli.argument('filename', type=normpath, help='The autocorrection database file')\n@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a output file is supplied.')\n@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a output file is supplied.')\n@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.subcommand('Generate the autocorrection data file from a dictionary file.')\ndef generate_autocorrect_data(cli):\n    autocorrections = parse_file(cli.args.filename)\n    trie = make_trie(autocorrections)\n    data = serialize_trie(autocorrections, trie)\n\n    current_keyboard = cli.args.keyboard or cli.config.user.keyboard or cli.config.generate_autocorrect_data.keyboard\n    current_keymap = cli.args.keymap or cli.config.user.keymap or cli.config.generate_autocorrect_data.keymap\n\n    if not cli.args.output and current_keyboard and current_keymap:\n        cli.args.output = locate_keymap(current_keyboard, current_keymap).parent / 'autocorrect_data.h'\n\n    assert all(0 <= b <= 255 for b in data)\n\n    min_typo = min(autocorrections, key=typo_len)[0]\n    max_typo = max(autocorrections, key=typo_len)[0]\n\n    # Build the autocorrect_data.h file.\n    autocorrect_data_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '']\n\n    autocorrect_data_h_lines.append(f'// Autocorrection dictionary ({len(autocorrections)} entries):')\n    for typo, correction in autocorrections:\n        autocorrect_data_h_lines.append(f'//   {typo:<{len(max_typo)}} -> {correction}')\n\n    autocorrect_data_h_lines.append('')\n    autocorrect_data_h_lines.append(f'#define AUTOCORRECT_MIN_LENGTH {len(min_typo)} // \"{min_typo}\"')\n    autocorrect_data_h_lines.append(f'#define AUTOCORRECT_MAX_LENGTH {len(max_typo)} // \"{max_typo}\"')\n    autocorrect_data_h_lines.append(f'#define DICTIONARY_SIZE {len(data)}')\n    autocorrect_data_h_lines.append('')\n    autocorrect_data_h_lines.append('static const uint8_t autocorrect_data[DICTIONARY_SIZE] PROGMEM = {')\n    autocorrect_data_h_lines.append(textwrap.fill('    %s' % (', '.join(map(to_hex, data))), width=100, subsequent_indent='    '))\n    autocorrect_data_h_lines.append('};')\n\n    # Show the results\n    dump_lines(cli.args.output, autocorrect_data_h_lines, cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/community_modules.py",
    "content": "import contextlib\nfrom argcomplete.completers import FilesCompleter\nfrom pathlib import Path\n\nfrom milc import cli\n\nimport qmk.path\nfrom qmk.info import get_modules\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.commands import dump_lines\nfrom qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE\nfrom qmk.community_modules import module_api_list, load_module_jsons, find_module_path\n\n\n@contextlib.contextmanager\ndef _render_api_guard(lines, api):\n    if api.guard:\n        lines.append(f'#if {api.guard}')\n    yield\n    if api.guard:\n        lines.append(f'#endif  // {api.guard}')\n\n\ndef _render_api_header(api):\n    lines = []\n    if api.header:\n        lines.append('')\n        with _render_api_guard(lines, api):\n            lines.append(f'#include <{api.header}>')\n    return lines\n\n\ndef _render_keycodes(module_jsons):\n    lines = []\n    lines.append('')\n    lines.append('enum {')\n    first = True\n    for module_json in module_jsons:\n        module_name = Path(module_json['module']).name\n        keycodes = module_json.get('keycodes', [])\n        if len(keycodes) > 0:\n            lines.append(f'    // From module: {module_name}')\n            for keycode in keycodes:\n                key = keycode.get('key', None)\n                if first:\n                    lines.append(f'    {key} = QK_COMMUNITY_MODULE,')\n                    first = False\n                else:\n                    lines.append(f'    {key},')\n                for alias in keycode.get('aliases', []):\n                    lines.append(f'    {alias} = {key},')\n            lines.append('')\n    lines.append('    LAST_COMMUNITY_MODULE_KEY')\n    lines.append('};')\n    lines.append('STATIC_ASSERT((int)LAST_COMMUNITY_MODULE_KEY <= (int)(QK_COMMUNITY_MODULE_MAX+1), \"Too many community module keycodes\");')\n    return lines\n\n\ndef _render_api_declarations(api, module, user_kb=True):\n    lines = []\n    lines.append('')\n    with _render_api_guard(lines, api):\n        if user_kb:\n            lines.append(f'{api.ret_type} {api.name}_{module}_user({api.args});')\n            lines.append(f'{api.ret_type} {api.name}_{module}_kb({api.args});')\n        lines.append(f'{api.ret_type} {api.name}_{module}({api.args});')\n    return lines\n\n\ndef _render_api_implementations(api, module):\n    module_name = Path(module).name\n    lines = []\n    lines.append('')\n    with _render_api_guard(lines, api):\n        # _user\n        lines.append(f'__attribute__((weak)) {api.ret_type} {api.name}_{module_name}_user({api.args}) {{')\n        if api.ret_type == 'bool':\n            lines.append('    return true;')\n        elif api.ret_type in ['layer_state_t', 'report_mouse_t']:\n            lines.append(f'    return {api.call_params};')\n        else:\n            pass\n        lines.append('}')\n        lines.append('')\n\n        # _kb\n        lines.append(f'__attribute__((weak)) {api.ret_type} {api.name}_{module_name}_kb({api.args}) {{')\n        if api.ret_type == 'bool':\n            lines.append(f'    if(!{api.name}_{module_name}_user({api.call_params})) {{ return false; }}')\n            lines.append('    return true;')\n        elif api.ret_type in ['layer_state_t', 'report_mouse_t']:\n            lines.append(f'    return {api.name}_{module_name}_user({api.call_params});')\n        else:\n            lines.append(f'    {api.name}_{module_name}_user({api.call_params});')\n        lines.append('}')\n        lines.append('')\n\n        # module (non-suffixed)\n        lines.append(f'__attribute__((weak)) {api.ret_type} {api.name}_{module_name}({api.args}) {{')\n        if api.ret_type == 'bool':\n            lines.append(f'    if(!{api.name}_{module_name}_kb({api.call_params})) {{ return false; }}')\n            lines.append('    return true;')\n        elif api.ret_type in ['layer_state_t', 'report_mouse_t']:\n            lines.append(f'    return {api.name}_{module_name}_kb({api.call_params});')\n        else:\n            lines.append(f'    {api.name}_{module_name}_kb({api.call_params});')\n        lines.append('}')\n    return lines\n\n\ndef _render_core_implementation(api, modules):\n    lines = []\n    lines.append('')\n    with _render_api_guard(lines, api):\n        lines.append(f'{api.ret_type} {api.name}_modules({api.args}) {{')\n        if api.ret_type == 'bool':\n            lines.append('    return true')\n        for module in modules:\n            module_name = Path(module).name\n            if api.ret_type == 'bool':\n                lines.append(f'        && {api.name}_{module_name}({api.call_params})')\n            elif api.ret_type in ['layer_state_t', 'report_mouse_t']:\n                lines.append(f'    {api.call_params} = {api.name}_{module_name}({api.call_params});')\n            else:\n                lines.append(f'    {api.name}_{module_name}({api.call_params});')\n        if api.ret_type == 'bool':\n            lines.append('    ;')\n        elif api.ret_type in ['layer_state_t', 'report_mouse_t']:\n            lines.append(f'    return {api.call_params};')\n        lines.append('}')\n    return lines\n\n\ndef _generate_features_rules(features_dict):\n    lines = []\n    for feature, enabled in features_dict.items():\n        feature = feature.upper()\n        enabled = 'yes' if enabled else 'no'\n        lines.append(f'{feature}_ENABLE={enabled}')\n    return lines\n\n\ndef _generate_modules_rules(keyboard, filename):\n    lines = []\n    modules = get_modules(keyboard, filename)\n    if len(modules) > 0:\n        lines.append('')\n        lines.append('OPT_DEFS += -DCOMMUNITY_MODULES_ENABLE=TRUE')\n        for module in modules:\n            module_path = qmk.path.unix_style_path(find_module_path(module))\n            if not module_path:\n                raise FileNotFoundError(f\"Module '{module}' not found.\")\n            lines.append('')\n            lines.append(f'COMMUNITY_MODULES += {module_path.name}')  # use module_path here instead of module as it may be a subdirectory\n            lines.append(f'OPT_DEFS += -DCOMMUNITY_MODULE_{module_path.name.upper()}_ENABLE=TRUE')\n            lines.append(f'COMMUNITY_MODULE_PATHS += {module_path}')\n            lines.append(f'VPATH += {module_path}')\n            lines.append(f'SRC += $(wildcard {module_path}/{module_path.name}.c)')\n            lines.append(f'MODULE_NAME_{module_path.name.upper()} := {module_path.name}')\n            lines.append(f'MODULE_PATH_{module_path.name.upper()} := {module_path}')\n            lines.append(f'-include {module_path}/rules.mk')\n\n        module_jsons = load_module_jsons(modules)\n        for module_json in module_jsons:\n            if 'features' in module_json:\n                lines.append('')\n                lines.append(f'# Module: {module_json[\"module_name\"]}')\n                lines.extend(_generate_features_rules(module_json['features']))\n    return lines\n\n\n@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-e', '--escape', arg_only=True, action='store_true', help=\"Escape spaces in quiet mode\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate rules.mk for.')\n@cli.argument('filename', nargs='?', arg_only=True, type=qmk.path.FileType('r'), completer=FilesCompleter('.json'), help='A configurator export JSON to be compiled and flashed or a pre-compiled binary firmware file (bin/hex) to be flashed.')\n@cli.subcommand('Creates a community_modules_rules_mk from a keymap.json file.')\ndef generate_community_modules_rules_mk(cli):\n\n    rules_mk_lines = [GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE]\n\n    rules_mk_lines.extend(_generate_modules_rules(cli.args.keyboard, cli.args.filename))\n\n    # Show the results\n    dump_lines(cli.args.output, rules_mk_lines)\n\n    if cli.args.output:\n        if cli.args.quiet:\n            if cli.args.escape:\n                print(cli.args.output.as_posix().replace(' ', '\\\\ '))\n            else:\n                print(cli.args.output)\n        else:\n            cli.log.info('Wrote rules.mk to %s.', cli.args.output)\n\n\n@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate community_modules.h for.')\n@cli.argument('filename', nargs='?', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')\n@cli.subcommand('Creates a community_modules.h from a keymap.json file.')\ndef generate_community_modules_h(cli):\n    \"\"\"Creates a community_modules.h from a keymap.json file\n    \"\"\"\n    if cli.args.output and cli.args.output.name == '-':\n        cli.args.output = None\n\n    api_list, api_version, ver_major, ver_minor, ver_patch = module_api_list()\n\n    lines = [\n        GPL2_HEADER_C_LIKE,\n        GENERATED_HEADER_C_LIKE,\n        '#pragma once',\n        '#include <stdint.h>',\n        '#include <stdbool.h>',\n        '#include <keycodes.h>',\n        '',\n        '#include \"compiler_support.h\"',\n        '',\n        '#define COMMUNITY_MODULES_API_VERSION_BUILDER(ver_major,ver_minor,ver_patch) (((((uint32_t)(ver_major))&0xFF) << 24) | ((((uint32_t)(ver_minor))&0xFF) << 16) | (((uint32_t)(ver_patch))&0xFF))',\n        f'#define COMMUNITY_MODULES_API_VERSION COMMUNITY_MODULES_API_VERSION_BUILDER({ver_major},{ver_minor},{ver_patch})',\n        f'#define ASSERT_COMMUNITY_MODULES_MIN_API_VERSION(ver_major,ver_minor,ver_patch) STATIC_ASSERT(COMMUNITY_MODULES_API_VERSION_BUILDER(ver_major,ver_minor,ver_patch) <= COMMUNITY_MODULES_API_VERSION, \"Community module requires a newer version of QMK modules API -- needs: \" #ver_major \".\" #ver_minor \".\" #ver_patch \", current: {api_version}.\")',\n        '',\n        'typedef struct keyrecord_t keyrecord_t; // forward declaration so we don\\'t need to include quantum.h',\n        '',\n    ]\n\n    modules = get_modules(cli.args.keyboard, cli.args.filename)\n    module_jsons = load_module_jsons(modules)\n    if len(modules) > 0:\n        lines.extend(_render_keycodes(module_jsons))\n\n        for api in api_list:\n            lines.extend(_render_api_header(api))\n\n        for module in modules:\n            lines.append('')\n            lines.append(f'// From module: {module}')\n            for api in api_list:\n                lines.extend(_render_api_declarations(api, Path(module).name))\n        lines.append('')\n\n        lines.append('// Core wrapper')\n        for api in api_list:\n            lines.extend(_render_api_declarations(api, 'modules', user_kb=False))\n\n    dump_lines(cli.args.output, lines, cli.args.quiet, remove_repeated_newlines=True)\n\n\n@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate community_modules.c for.')\n@cli.argument('filename', nargs='?', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')\n@cli.subcommand('Creates a community_modules.c from a keymap.json file.')\ndef generate_community_modules_c(cli):\n    \"\"\"Creates a community_modules.c from a keymap.json file\n    \"\"\"\n    if cli.args.output and cli.args.output.name == '-':\n        cli.args.output = None\n\n    api_list, _, _, _, _ = module_api_list()\n\n    lines = [\n        GPL2_HEADER_C_LIKE,\n        GENERATED_HEADER_C_LIKE,\n        '',\n        '#include \"community_modules.h\"',\n    ]\n\n    modules = get_modules(cli.args.keyboard, cli.args.filename)\n    if len(modules) > 0:\n\n        for module in modules:\n            for api in api_list:\n                lines.extend(_render_api_implementations(api, Path(module).name))\n\n        for api in api_list:\n            lines.extend(_render_core_implementation(api, modules))\n\n    dump_lines(cli.args.output, lines, cli.args.quiet, remove_repeated_newlines=True)\n\n\ndef _generate_include_per_module(cli, include_file_name):\n    \"\"\"Generates C code to include \"<module_path>/include_file_name\" for each module.\"\"\"\n    if cli.args.output and cli.args.output.name == '-':\n        cli.args.output = None\n\n    lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE]\n\n    for module in get_modules(cli.args.keyboard, cli.args.filename):\n        full_path = f'{find_module_path(module)}/{include_file_name}'\n        lines.append('')\n        lines.append(f'#if __has_include(\"{full_path}\")')\n        lines.append(f'#include \"{full_path}\"')\n        lines.append(f'#endif  // __has_include(\"{full_path}\")')\n\n    dump_lines(cli.args.output, lines, cli.args.quiet, remove_repeated_newlines=True)\n\n\n@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate community_modules_introspection.h for.')\n@cli.argument('filename', nargs='?', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')\n@cli.subcommand('Creates a community_modules_introspection.h from a keymap.json file.')\ndef generate_community_modules_introspection_h(cli):\n    \"\"\"Creates a community_modules_introspection.h from a keymap.json file\n    \"\"\"\n    _generate_include_per_module(cli, 'introspection.h')\n\n\n@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate community_modules.c for.')\n@cli.argument('filename', nargs='?', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')\n@cli.subcommand('Creates a community_modules_introspection.c from a keymap.json file.')\ndef generate_community_modules_introspection_c(cli):\n    \"\"\"Creates a community_modules_introspection.c from a keymap.json file\n    \"\"\"\n    _generate_include_per_module(cli, 'introspection.c')\n\n\n@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate led_matrix_community_modules.inc for.')\n@cli.argument('filename', nargs='?', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')\n@cli.subcommand('Creates an led_matrix_community_modules.inc from a keymap.json file.')\ndef generate_led_matrix_community_modules_inc(cli):\n    \"\"\"Creates an led_matrix_community_modules.inc from a keymap.json file\n    \"\"\"\n    _generate_include_per_module(cli, 'led_matrix_module.inc')\n\n\n@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate rgb_matrix_community_modules.inc for.')\n@cli.argument('filename', nargs='?', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')\n@cli.subcommand('Creates an rgb_matrix_community_modules.inc from a keymap.json file.')\ndef generate_rgb_matrix_community_modules_inc(cli):\n    \"\"\"Creates an rgb_matrix_community_modules.inc from a keymap.json file\n    \"\"\"\n    _generate_include_per_module(cli, 'rgb_matrix_module.inc')\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/compilation_database.py",
    "content": "from milc import cli\n\n\n@cli.argument('-kb', '--keyboard', help='[unused] The keyboard\\'s name')\n@cli.argument('-km', '--keymap', help='[unused] The keymap\\'s name')\n@cli.subcommand('[deprecated] Create a compilation database.')\ndef generate_compilation_database(cli):\n    cli.log.error('This command is deprecated and has effectively been removed. Please use the `--compiledb` flag with `qmk compile` instead.')\n    return False\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/config_h.py",
    "content": "\"\"\"Used by the make system to generate info_config.h from info.json.\n\"\"\"\nfrom pathlib import Path\nfrom dotty_dict import dotty\n\nfrom argcomplete.completers import FilesCompleter\nfrom milc import cli\n\nfrom qmk.info import info_json\nfrom qmk.json_schema import json_load\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.commands import dump_lines, parse_configurator_json\nfrom qmk.path import normpath, FileType\nfrom qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE\n\n\ndef generate_define(define, value=None):\n    is_keymap = cli.args.filename\n    value = f' {value}' if value is not None else ''\n    if is_keymap:\n        return f\"\"\"\n#undef {define}\n#define {define}{value}\"\"\"\n    return f\"\"\"\n#ifndef {define}\n#    define {define}{value}\n#endif // {define}\"\"\"\n\n\ndef direct_pins(direct_pins, postfix):\n    \"\"\"Return the config.h lines that set the direct pins.\n    \"\"\"\n    rows = []\n\n    for row in direct_pins:\n        cols = ','.join(map(str, [col or 'NO_PIN' for col in row]))\n        rows.append('{' + cols + '}')\n\n    return generate_define(f'DIRECT_PINS{postfix}', f'{{ {\", \".join(rows)} }}')\n\n\ndef pin_array(define, pins, postfix):\n    \"\"\"Return the config.h lines that set a pin array.\n    \"\"\"\n    pin_array = ', '.join(map(str, [pin or 'NO_PIN' for pin in pins]))\n\n    return generate_define(f'{define}_PINS{postfix}', f'{{ {pin_array} }}')\n\n\ndef matrix_pins(matrix_pins, postfix=''):\n    \"\"\"Add the matrix config to the config.h.\n    \"\"\"\n    pins = []\n\n    if 'direct' in matrix_pins:\n        pins.append(direct_pins(matrix_pins['direct'], postfix))\n\n    if 'cols' in matrix_pins:\n        pins.append(pin_array('MATRIX_COL', matrix_pins['cols'], postfix))\n\n    if 'rows' in matrix_pins:\n        pins.append(pin_array('MATRIX_ROW', matrix_pins['rows'], postfix))\n\n    return '\\n'.join(pins)\n\n\ndef generate_matrix_size(kb_info_json, config_h_lines):\n    \"\"\"Add the matrix size to the config.h.\n    \"\"\"\n    if 'matrix_size' in kb_info_json:\n        config_h_lines.append(generate_define('MATRIX_COLS', kb_info_json['matrix_size']['cols']))\n        config_h_lines.append(generate_define('MATRIX_ROWS', kb_info_json['matrix_size']['rows']))\n\n\ndef generate_config_items(kb_info_json, config_h_lines):\n    \"\"\"Iterate through the info_config map to generate basic config values.\n    \"\"\"\n    info_config_map = json_load(Path('data/mappings/info_config.hjson'))\n\n    for config_key, info_dict in info_config_map.items():\n        info_key = info_dict['info_key']\n        key_type = info_dict.get('value_type', 'raw')\n        to_c = info_dict.get('to_c', True)\n\n        if not to_c:\n            continue\n\n        try:\n            config_value = kb_info_json[info_key]\n        except KeyError:\n            continue\n\n        if key_type.startswith('array.array'):\n            config_h_lines.append(generate_define(config_key, f'{{ {\", \".join([\"{\" + \",\".join(list(map(str, x))) + \"}\" for x in config_value])} }}'))\n        elif key_type.startswith('array'):\n            config_h_lines.append(generate_define(config_key, f'{{ {\", \".join(map(str, config_value))} }}'))\n        elif key_type == 'bool':\n            config_h_lines.append(generate_define(config_key, 'true' if config_value else 'false'))\n        elif key_type == 'flag':\n            if config_value:\n                config_h_lines.append(generate_define(config_key))\n        elif key_type == 'mapping':\n            for key, value in config_value.items():\n                config_h_lines.append(generate_define(key, value))\n        elif key_type == 'str':\n            escaped_str = config_value.replace('\\\\', '\\\\\\\\').replace('\"', '\\\\\"')\n            config_h_lines.append(generate_define(config_key, f'\"{escaped_str}\"'))\n        elif key_type == 'bcd_version':\n            (major, minor, revision) = config_value.split('.')\n            config_h_lines.append(generate_define(config_key, f'0x{major.zfill(2)}{minor}{revision}'))\n        else:\n            config_h_lines.append(generate_define(config_key, config_value))\n\n\ndef generate_encoder_config(encoder_json, config_h_lines, postfix=''):\n    \"\"\"Generate the config.h lines for encoders.\"\"\"\n    a_pads = []\n    b_pads = []\n    resolutions = []\n    for encoder in encoder_json.get(\"rotary\", []):\n        a_pads.append(encoder[\"pin_a\"])\n        b_pads.append(encoder[\"pin_b\"])\n        resolutions.append(encoder.get(\"resolution\", None))\n\n    config_h_lines.append(generate_define(f'ENCODER_A_PINS{postfix}', f'{{ {\", \".join(a_pads)} }}'))\n    config_h_lines.append(generate_define(f'ENCODER_B_PINS{postfix}', f'{{ {\", \".join(b_pads)} }}'))\n\n    if len(resolutions) == 0 or all(r is None for r in resolutions):\n        cli.log.debug(f\"Skipping ENCODER_RESOLUTION{postfix} configuration\")\n        return\n\n    resolutions = [4 if r is None else r for r in resolutions]\n    if len(set(resolutions)) == 1:\n        config_h_lines.append(generate_define(f'ENCODER_RESOLUTION{postfix}', resolutions[0]))\n    else:\n        config_h_lines.append(generate_define(f'ENCODER_RESOLUTIONS{postfix}', f'{{ {\", \".join(map(str,resolutions))} }}'))\n\n\ndef generate_split_config(kb_info_json, config_h_lines):\n    \"\"\"Generate the config.h lines for split boards.\"\"\"\n    if 'handedness' in kb_info_json['split']:\n        # TODO: change SPLIT_HAND_MATRIX_GRID to require brackets\n        handedness = kb_info_json['split']['handedness']\n        if 'matrix_grid' in handedness:\n            config_h_lines.append(generate_define('SPLIT_HAND_MATRIX_GRID', ', '.join(handedness['matrix_grid'])))\n\n    if 'protocol' in kb_info_json['split'].get('transport', {}):\n        if kb_info_json['split']['transport']['protocol'] == 'i2c':\n            config_h_lines.append(generate_define('USE_I2C'))\n\n    if 'right' in kb_info_json['split'].get('matrix_pins', {}):\n        config_h_lines.append(matrix_pins(kb_info_json['split']['matrix_pins']['right'], '_RIGHT'))\n\n    if 'right' in kb_info_json['split'].get('encoder', {}):\n        generate_encoder_config(kb_info_json['split']['encoder']['right'], config_h_lines, '_RIGHT')\n\n\ndef generate_led_animations_config(feature, led_feature_json, config_h_lines, enable_prefix, animation_prefix):\n    if 'animation' in led_feature_json.get('default', {}):\n        config_h_lines.append(generate_define(f'{feature.upper()}_DEFAULT_MODE', f'{animation_prefix}{led_feature_json[\"default\"][\"animation\"].upper()}'))\n\n    for animation in led_feature_json.get('animations', {}):\n        if led_feature_json['animations'][animation]:\n            config_h_lines.append(generate_define(f'{enable_prefix}{animation.upper()}'))\n\n\n@cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), completer=FilesCompleter('.json'), help='A configurator export JSON to be compiled and flashed or a pre-compiled binary firmware file (bin/hex) to be flashed.')\n@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate config.h for.')\n@cli.subcommand('Used by the make system to generate info_config.h from info.json', hidden=True)\ndef generate_config_h(cli):\n    \"\"\"Generates the info_config.h file.\n    \"\"\"\n    # Determine our keyboard/keymap\n    if cli.args.filename:\n        user_keymap = parse_configurator_json(cli.args.filename)\n        kb_info_json = dotty(user_keymap.get('config', {}))\n    elif cli.args.keyboard:\n        kb_info_json = dotty(info_json(cli.args.keyboard))\n    else:\n        cli.log.error('You must supply a configurator export or `--keyboard`.')\n        cli.subcommands['generate-config-h'].print_help()\n        return False\n\n    # Build the info_config.h file.\n    config_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once']\n\n    generate_config_items(kb_info_json, config_h_lines)\n\n    generate_matrix_size(kb_info_json, config_h_lines)\n\n    if 'matrix_pins' in kb_info_json:\n        config_h_lines.append(matrix_pins(kb_info_json['matrix_pins']))\n\n    if 'encoder' in kb_info_json:\n        generate_encoder_config(kb_info_json['encoder'], config_h_lines)\n\n    if 'split' in kb_info_json:\n        generate_split_config(kb_info_json, config_h_lines)\n\n    if 'led_matrix' in kb_info_json:\n        generate_led_animations_config('led_matrix', kb_info_json['led_matrix'], config_h_lines, 'ENABLE_LED_MATRIX_', 'LED_MATRIX_')\n\n    if 'rgb_matrix' in kb_info_json:\n        generate_led_animations_config('rgb_matrix', kb_info_json['rgb_matrix'], config_h_lines, 'ENABLE_RGB_MATRIX_', 'RGB_MATRIX_')\n\n    if 'rgblight' in kb_info_json:\n        generate_led_animations_config('rgblight', kb_info_json['rgblight'], config_h_lines, 'RGBLIGHT_EFFECT_', 'RGBLIGHT_MODE_')\n\n    # Show the results\n    dump_lines(cli.args.output, config_h_lines, cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/develop_pr_list.py",
    "content": "\"\"\"Export the initial list of PRs associated with a `develop` merge to `master`.\n\"\"\"\nimport os\nimport re\nfrom pathlib import Path\nfrom subprocess import DEVNULL\n\nfrom milc import cli\n\ncache_timeout = 7 * 86400\nfix_expr = re.compile(r'fix', flags=re.IGNORECASE)\nclean1_expr = re.compile(r'\\[(develop|keyboard|keymap|core|cli|bug|docs|feature)\\]', flags=re.IGNORECASE)\nclean2_expr = re.compile(r'^(develop|keyboard|keymap|core|cli|bug|docs|feature):', flags=re.IGNORECASE)\n\nignored_titles = [\"Format code according to conventions\"]\n\n\ndef _is_ignored(title):\n    for ignore in ignored_titles:\n        if ignore in title:\n            return True\n    return False\n\n\ndef _get_pr_info(cache, gh, pr_num):\n    pull = cache.get(f'pull:{pr_num}')\n    if pull is None:\n        print(f'Retrieving info for PR #{pr_num}')\n        pull = gh.pulls.get(owner='qmk', repo='qmk_firmware', pull_number=pr_num)\n        cache.set(f'pull:{pr_num}', pull, cache_timeout)\n    return pull\n\n\ndef _try_open_cache(cli):\n    # These dependencies are manually handled because people complain. Fun.\n    try:\n        from sqlite_cache.sqlite_cache import SqliteCache\n    except ImportError:\n        return None\n\n    cache_loc = Path(cli.config_file).parent\n    return SqliteCache(cache_loc)\n\n\ndef _get_github():\n    try:\n        from ghapi.all import GhApi\n    except ImportError:\n        return None\n\n    return GhApi()\n\n\n@cli.argument('-f', '--from-ref', default='0.11.0', help='Git revision/tag/reference/branch to begin search')\n@cli.argument('-b', '--branch', default='upstream/develop', help='Git branch to iterate (default: \"upstream/develop\")')\n@cli.subcommand('Creates the develop PR list.', hidden=False if cli.config.user.developer else True)\ndef generate_develop_pr_list(cli):\n    \"\"\"Retrieves information from GitHub regarding the list of PRs associated\n    with a merge of `develop` branch into `master`.\n\n    Requires environment variable GITHUB_TOKEN to be set.\n    \"\"\"\n\n    if 'GITHUB_TOKEN' not in os.environ or os.environ['GITHUB_TOKEN'] == '':\n        cli.log.error('Environment variable \"GITHUB_TOKEN\" is not set.')\n        return 1\n\n    cache = _try_open_cache(cli)\n    gh = _get_github()\n\n    git_args = ['git', 'rev-list', '--oneline', '--no-merges', '--reverse', f'{cli.args.from_ref}...{cli.args.branch}', '^upstream/master']\n    commit_list = cli.run(git_args, capture_output=True, stdin=DEVNULL)\n\n    if cache is None or gh is None:\n        cli.log.error('Missing one or more dependent python packages: \"ghapi\", \"python-sqlite-cache\"')\n        return 1\n\n    pr_list_bugs = []\n    pr_list_dependencies = []\n    pr_list_core = []\n    pr_list_keyboards = []\n    pr_list_keyboard_fixes = []\n    pr_list_cli = []\n    pr_list_others = []\n\n    def _categorise_commit(commit_info):\n        def fix_or_normal(info, fixes_collection, normal_collection):\n            if \"bug\" in info['pr_labels'] or fix_expr.search(info['title']):\n                fixes_collection.append(info)\n            else:\n                normal_collection.append(info)\n\n        if _is_ignored(commit_info['title']):\n            return\n        elif \"dependencies\" in commit_info['pr_labels']:\n            fix_or_normal(commit_info, pr_list_bugs, pr_list_dependencies)\n        elif \"core\" in commit_info['pr_labels']:\n            fix_or_normal(commit_info, pr_list_bugs, pr_list_core)\n        elif \"keyboard\" in commit_info['pr_labels'] or \"keymap\" in commit_info['pr_labels'] or \"via\" in commit_info['pr_labels']:\n            fix_or_normal(commit_info, pr_list_keyboard_fixes, pr_list_keyboards)\n        elif \"cli\" in commit_info['pr_labels']:\n            fix_or_normal(commit_info, pr_list_bugs, pr_list_cli)\n        else:\n            fix_or_normal(commit_info, pr_list_bugs, pr_list_others)\n\n    git_expr = re.compile(r'^(?P<hash>[a-f0-9]+) (?P<title>.*) \\(#(?P<pr>[0-9]+)\\)$')\n    for line in commit_list.stdout.split('\\n'):\n        match = git_expr.search(line)\n        if match:\n            pr_info = _get_pr_info(cache, gh, match.group(\"pr\"))\n            commit_info = {'hash': match.group(\"hash\"), 'title': pr_info['title'], 'pr_num': int(match.group(\"pr\")), 'pr_labels': [label.name for label in pr_info.labels.items]}\n            _categorise_commit(commit_info)\n\n    def _dump_commit_list(name, collection):\n        if len(collection) == 0:\n            return\n        print(\"\")\n        print(f\"{name}:\")\n        for commit in sorted(collection, key=lambda x: x['pr_num']):\n            title = clean1_expr.sub('', clean2_expr.sub('', commit['title'])).strip()\n            pr_num = commit['pr_num']\n            print(f'* {title} ([#{pr_num}](https://github.com/qmk/qmk_firmware/pull/{pr_num}))')\n\n    _dump_commit_list(\"Core\", pr_list_core)\n    _dump_commit_list(\"CLI\", pr_list_cli)\n    _dump_commit_list(\"Submodule updates\", pr_list_dependencies)\n    _dump_commit_list(\"Keyboards\", pr_list_keyboards)\n    _dump_commit_list(\"Keyboard fixes\", pr_list_keyboard_fixes)\n    _dump_commit_list(\"Others\", pr_list_others)\n    _dump_commit_list(\"Bugs\", pr_list_bugs)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/dfu_header.py",
    "content": "\"\"\"Used by the make system to generate LUFA Keyboard.h from info.json\n\"\"\"\nfrom dotty_dict import dotty\nfrom milc import cli\n\nfrom qmk.decorators import automagic_keyboard\nfrom qmk.info import info_json\nfrom qmk.path import is_keyboard, normpath\nfrom qmk.keyboard import keyboard_completer\nfrom qmk.commands import dump_lines\nfrom qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE\n\n\n@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', completer=keyboard_completer, help='Keyboard to generate LUFA Keyboard.h for.')\n@cli.subcommand('Used by the make system to generate LUFA Keyboard.h from info.json', hidden=True)\n@automagic_keyboard\ndef generate_dfu_header(cli):\n    \"\"\"Generates the Keyboard.h file.\n    \"\"\"\n    # Determine our keyboard(s)\n    if not cli.config.generate_dfu_header.keyboard:\n        cli.log.error('Missing parameter: --keyboard')\n        cli.subcommands['info'].print_help()\n        return False\n\n    if not is_keyboard(cli.config.generate_dfu_header.keyboard):\n        cli.log.error('Invalid keyboard: \"%s\"', cli.config.generate_dfu_header.keyboard)\n        return False\n\n    # Build the Keyboard.h file.\n    kb_info_json = dotty(info_json(cli.config.generate_dfu_header.keyboard))\n\n    keyboard_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once']\n    keyboard_h_lines.append(f'#define MANUFACTURER \"{kb_info_json[\"manufacturer\"]}\"')\n    keyboard_h_lines.append(f'#define PRODUCT \"{kb_info_json[\"keyboard_name\"]} Bootloader\"')\n\n    # Optional\n    if 'qmk_lufa_bootloader.esc_output' in kb_info_json:\n        keyboard_h_lines.append(f'#define QMK_ESC_OUTPUT {kb_info_json[\"qmk_lufa_bootloader.esc_output\"]}')\n    if 'qmk_lufa_bootloader.esc_input' in kb_info_json:\n        keyboard_h_lines.append(f'#define QMK_ESC_INPUT {kb_info_json[\"qmk_lufa_bootloader.esc_input\"]}')\n    if 'qmk_lufa_bootloader.led' in kb_info_json:\n        keyboard_h_lines.append(f'#define QMK_LED {kb_info_json[\"qmk_lufa_bootloader.led\"]}')\n    if 'qmk_lufa_bootloader.speaker' in kb_info_json:\n        keyboard_h_lines.append(f'#define QMK_SPEAKER {kb_info_json[\"qmk_lufa_bootloader.speaker\"]}')\n\n    # Show the results\n    dump_lines(cli.args.output, keyboard_h_lines, cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/docs.py",
    "content": "\"\"\"Build QMK documentation locally\n\"\"\"\nimport shutil\nfrom qmk.docs import prepare_docs_build_area, run_docs_command, BUILD_DOCS_PATH\n\nfrom milc import cli\n\n\n@cli.argument('-s', '--serve', arg_only=True, action='store_true', help=\"Serves the generated docs once built.\")\n@cli.subcommand('Build QMK documentation.', hidden=False if cli.config.user.developer else True)\ndef generate_docs(cli):\n    \"\"\"Invoke the docs generation process\n\n    TODO(unclaimed):\n        * [ ] Add a real build step... something static docs\n    \"\"\"\n\n    if not shutil.which('doxygen'):\n        cli.log.error('doxygen is not installed. Please install it and try again.')\n        return\n\n    if not shutil.which('yarn'):\n        cli.log.error('yarn is not installed. Please install it and try again.')\n        return\n\n    if not prepare_docs_build_area(is_production=True):\n        return False\n\n    cli.log.info('Building vitepress docs')\n    run_docs_command('run', ['docs:build'])\n    cli.log.info('Successfully generated docs to %s.', BUILD_DOCS_PATH)\n\n    if cli.args.serve:\n        run_docs_command('run', ['docs:preview'])\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/info_json.py",
    "content": "\"\"\"Keyboard information script.\n\nCompile an info.json for a particular keyboard and pretty-print it.\n\"\"\"\nimport json\n\nfrom argcomplete.completers import FilesCompleter\nfrom jsonschema import Draft202012Validator, RefResolver, validators\nfrom milc import cli\nfrom pathlib import Path\n\nfrom qmk.decorators import automagic_keyboard, automagic_keymap\nfrom qmk.info import info_json\nfrom qmk.json_encoders import InfoJSONEncoder\nfrom qmk.json_schema import compile_schema_store\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.path import is_keyboard, normpath\n\n\ndef pruning_validator(validator_class):\n    \"\"\"Extends Draft202012Validator to remove properties that aren't specified in the schema.\n    \"\"\"\n    validate_properties = validator_class.VALIDATORS[\"properties\"]\n\n    def remove_additional_properties(validator, properties, instance, schema):\n        for prop in list(instance.keys()):\n            if prop not in properties:\n                del instance[prop]\n\n        for error in validate_properties(validator, properties, instance, schema):\n            yield error\n\n    return validators.extend(validator_class, {\"properties\": remove_additional_properties})\n\n\ndef strip_info_json(kb_info_json):\n    \"\"\"Remove the API-only properties from the info.json.\n    \"\"\"\n    schema_store = compile_schema_store()\n    pruning_draft_validator = pruning_validator(Draft202012Validator)\n    schema = schema_store['qmk.keyboard.v1']\n    resolver = RefResolver.from_schema(schema_store['qmk.keyboard.v1'], store=schema_store)\n    validator = pruning_draft_validator(schema, resolver=resolver).validate\n\n    return validator(kb_info_json)\n\n\n@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.')\n@cli.argument('-km', '--keymap', help='Show the layers for a JSON keymap too.')\n@cli.argument('-o', '--output', arg_only=True, completer=FilesCompleter, help='Write the output the specified file, overwriting if necessary.')\n@cli.argument('-ow', '--overwrite', arg_only=True, action='store_true', help='Overwrite the existing info.json. (Overrides the location of --output)')\n@cli.subcommand('Generate an info.json file for a keyboard.', hidden=False if cli.config.user.developer else True)\n@automagic_keyboard\n@automagic_keymap\ndef generate_info_json(cli):\n    \"\"\"Generate an info.json file for a keyboard\n    \"\"\"\n    # Determine our keyboard(s)\n    if not cli.config.generate_info_json.keyboard:\n        cli.log.error('Missing parameter: --keyboard')\n        cli.subcommands['info'].print_help()\n        return False\n\n    if not is_keyboard(cli.config.generate_info_json.keyboard):\n        cli.log.error('Invalid keyboard: \"%s\"', cli.config.generate_info_json.keyboard)\n        return False\n\n    if cli.args.overwrite:\n        output_path = (Path('keyboards') / cli.config.generate_info_json.keyboard / 'info.json').resolve()\n\n        if cli.args.output:\n            cli.log.warning('Overwriting user supplied --output with %s', output_path)\n\n        cli.args.output = output_path\n\n    # Build the info.json file\n    kb_info_json = info_json(cli.config.generate_info_json.keyboard)\n    strip_info_json(kb_info_json)\n    info_json_text = json.dumps(kb_info_json, indent=4, cls=InfoJSONEncoder, sort_keys=True)\n\n    if cli.args.output:\n        # Write to a file\n        output_path = normpath(cli.args.output)\n\n        if output_path.exists():\n            cli.log.warning('Overwriting output file %s', output_path)\n\n        output_path.write_text(info_json_text + '\\n')\n        cli.log.info('Wrote info.json to %s.', output_path)\n\n    else:\n        # Display the results\n        print(info_json_text)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/keyboard_c.py",
    "content": "\"\"\"Used by the make system to generate keyboard.c from info.json.\n\"\"\"\nimport bisect\nimport dataclasses\nfrom typing import Optional\n\nfrom milc import cli\n\nfrom qmk.info import info_json\nfrom qmk.commands import dump_lines\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.path import normpath\nfrom qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, JOYSTICK_AXES\n\n\ndef _gen_led_configs(info_data):\n    lines = []\n\n    if 'layout' in info_data.get('rgb_matrix', {}):\n        lines.extend(_gen_led_config(info_data, 'rgb_matrix'))\n\n    if 'layout' in info_data.get('led_matrix', {}):\n        lines.extend(_gen_led_config(info_data, 'led_matrix'))\n\n    return lines\n\n\ndef _gen_led_config(info_data, config_type):\n    \"\"\"Convert info.json content to g_led_config\n    \"\"\"\n    cols = info_data['matrix_size']['cols']\n    rows = info_data['matrix_size']['rows']\n\n    lines = []\n\n    matrix = [['NO_LED'] * cols for _ in range(rows)]\n    pos = []\n    flags = []\n\n    led_layout = info_data[config_type]['layout']\n    for index, led_data in enumerate(led_layout):\n        if 'matrix' in led_data:\n            row, col = led_data['matrix']\n            matrix[row][col] = str(index)\n        pos.append(f'{{{led_data.get(\"x\", 0)}, {led_data.get(\"y\", 0)}}}')\n        flags.append(str(led_data.get('flags', 0)))\n\n    if config_type == 'rgb_matrix':\n        lines.append('#ifdef RGB_MATRIX_ENABLE')\n        lines.append('#include \"rgb_matrix.h\"')\n    elif config_type == 'led_matrix':\n        lines.append('#ifdef LED_MATRIX_ENABLE')\n        lines.append('#include \"led_matrix.h\"')\n\n    lines.append('__attribute__ ((weak)) led_config_t g_led_config = {')\n    lines.append('  {')\n    for line in matrix:\n        lines.append(f'    {{ {\", \".join(line)} }},')\n    lines.append('  },')\n    lines.append(f'  {{ {\", \".join(pos)} }},')\n    lines.append(f'  {{ {\", \".join(flags)} }},')\n    lines.append('};')\n    lines.append('#endif')\n    lines.append('')\n\n    return lines\n\n\ndef _gen_matrix_mask(info_data):\n    \"\"\"Convert info.json content to matrix_mask\n    \"\"\"\n    cols = info_data['matrix_size']['cols']\n    rows = info_data['matrix_size']['rows']\n\n    # Default mask to everything disabled\n    mask = [['0'] * cols for _ in range(rows)]\n\n    # Mirror layout macros squashed on top of each other\n    for layout_name, layout_data in info_data['layouts'].items():\n        for key_data in layout_data['layout']:\n            row, col = key_data['matrix']\n            if row >= rows or col >= cols:\n                cli.log.error(f'Skipping matrix_mask due to {layout_name} containing invalid matrix values')\n                return []\n            mask[row][col] = '1'\n\n    lines = []\n    lines.append('#ifdef MATRIX_MASKED')\n    lines.append('__attribute__((weak)) const matrix_row_t matrix_mask[] = {')\n    for i in range(rows):\n        lines.append(f'    0b{\"\".join(reversed(mask[i]))},')\n    lines.append('};')\n    lines.append('#endif')\n    lines.append('')\n\n    return lines\n\n\ndef _gen_joystick_axes(info_data):\n    \"\"\"Convert info.json content to joystick_axes\n    \"\"\"\n    if 'axes' not in info_data.get('joystick', {}):\n        return []\n\n    axes = info_data['joystick']['axes']\n    axes_keys = list(axes.keys())\n\n    lines = []\n    lines.append('#ifdef JOYSTICK_ENABLE')\n    lines.append('joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = {')\n\n    # loop over all available axes - injecting virtual axis for those not specified\n    for index, cur in enumerate(JOYSTICK_AXES):\n        # bail out if we have generated all requested axis\n        if len(axes_keys) == 0:\n            break\n\n        axis = 'virtual'\n        if cur in axes:\n            axis = axes[cur]\n            axes_keys.remove(cur)\n\n        if axis == 'virtual':\n            lines.append(f\"    [{index}] = JOYSTICK_AXIS_VIRTUAL,\")\n        else:\n            lines.append(f\"    [{index}] = JOYSTICK_AXIS_IN({axis['input_pin']}, {axis['low']}, {axis['rest']}, {axis['high']}),\")\n\n    lines.append('};')\n    lines.append('#endif')\n    lines.append('')\n\n    return lines\n\n\n@dataclasses.dataclass\nclass LayoutKey:\n    \"\"\"Geometric info for one key in a layout.\"\"\"\n    row: int\n    col: int\n    x: float\n    y: float\n    w: float = 1.0\n    h: float = 1.0\n    hand: Optional[str] = None\n\n    @staticmethod\n    def from_json(key_json):\n        row, col = key_json['matrix']\n        return LayoutKey(\n            row=row,\n            col=col,\n            x=key_json['x'],\n            y=key_json['y'],\n            w=key_json.get('w', 1.0),\n            h=key_json.get('h', 1.0),\n            hand=key_json.get('hand', None),\n        )\n\n    @property\n    def cx(self):\n        \"\"\"Center x coordinate of the key.\"\"\"\n        return self.x + self.w / 2.0\n\n    @property\n    def cy(self):\n        \"\"\"Center y coordinate of the key.\"\"\"\n        return self.y + self.h / 2.0\n\n\nclass Layout:\n    \"\"\"Geometric info of a layout.\"\"\"\n    def __init__(self, layout_json):\n        self.keys = [LayoutKey.from_json(key_json) for key_json in layout_json['layout']]\n        self.x_min = min(key.cx for key in self.keys)\n        self.x_max = max(key.cx for key in self.keys)\n        self.x_mid = (self.x_min + self.x_max) / 2\n        # If there is one key with width >= 6u, it is probably the spacebar.\n        i = [i for i, key in enumerate(self.keys) if key.w >= 6.0]\n        self.spacebar = self.keys[i[0]] if len(i) == 1 else None\n\n    def is_symmetric(self, tol: float = 0.02):\n        \"\"\"Whether the key positions are symmetric about x_mid.\"\"\"\n        x = sorted([key.cx for key in self.keys])\n        for i in range(len(x)):\n            x_i_mirrored = 2.0 * self.x_mid - x[i]\n            # Find leftmost x element greater than or equal to (x_i_mirrored - tol).\n            j = bisect.bisect_left(x, x_i_mirrored - tol)\n            if j == len(x) or abs(x[j] - x_i_mirrored) > tol:\n                return False\n\n        return True\n\n    def widest_horizontal_gap(self):\n        \"\"\"Finds the x midpoint of the widest horizontal gap between keys.\"\"\"\n        x = sorted([key.cx for key in self.keys])\n        x_mid = self.x_mid\n        max_sep = 0\n        for i in range(len(x) - 1):\n            sep = x[i + 1] - x[i]\n            if sep > max_sep:\n                max_sep = sep\n                x_mid = (x[i + 1] + x[i]) / 2\n\n        return x_mid\n\n\ndef _gen_chordal_hold_layout(info_data):\n    \"\"\"Convert info.json content to chordal_hold_layout\n    \"\"\"\n    # NOTE: If there are multiple layouts, only the first is read.\n    for layout_name, layout_json in info_data['layouts'].items():\n        layout = Layout(layout_json)\n        break\n\n    if layout.is_symmetric():\n        # If the layout is symmetric (e.g. most split keyboards), guess the\n        # handedness based on the sign of (x - layout.x_mid).\n        hand_signs = [key.x - layout.x_mid for key in layout.keys]\n    elif layout.spacebar is not None:\n        # If the layout has a spacebar, form a dividing line through the spacebar,\n        # nearly vertical but with a slight angle to follow typical row stagger.\n        x0 = layout.spacebar.cx - 0.05\n        y0 = layout.spacebar.cy - 1.0\n        hand_signs = [(key.x - x0) - (key.y - y0) / 3.0 for key in layout.keys]\n    else:\n        # Fallback: assume handedness based on the widest horizontal separation.\n        x_mid = layout.widest_horizontal_gap()\n        hand_signs = [key.x - x_mid for key in layout.keys]\n\n    for key, hand_sign in zip(layout.keys, hand_signs):\n        if key.hand is None:\n            if key == layout.spacebar or abs(hand_sign) <= 0.02:\n                key.hand = '*'\n            else:\n                key.hand = 'L' if hand_sign < 0.0 else 'R'\n\n    lines = []\n    lines.append('#ifdef CHORDAL_HOLD')\n    line = ('__attribute__((weak)) const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = ' + layout_name + '(')\n\n    x_prev = None\n    for key in layout.keys:\n        if x_prev is None or key.x < x_prev:\n            lines.append(line)\n            line = '  '\n        line += f\"'{key.hand}', \"\n        x_prev = key.x\n\n    lines.append(line[:-2])\n    lines.append(');')\n    lines.append('#endif')\n\n    return lines\n\n\n@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='Keyboard to generate keyboard.c for.')\n@cli.subcommand('Used by the make system to generate keyboard.c from info.json', hidden=True)\ndef generate_keyboard_c(cli):\n    \"\"\"Generates the keyboard.h file.\n    \"\"\"\n    kb_info_json = info_json(cli.args.keyboard)\n\n    # Build the layouts.h file.\n    keyboard_c_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#include QMK_KEYBOARD_H', '']\n\n    keyboard_c_lines.extend(_gen_led_configs(kb_info_json))\n    keyboard_c_lines.extend(_gen_matrix_mask(kb_info_json))\n    keyboard_c_lines.extend(_gen_joystick_axes(kb_info_json))\n    keyboard_c_lines.extend(_gen_chordal_hold_layout(kb_info_json))\n\n    # Show the results\n    dump_lines(cli.args.output, keyboard_c_lines, cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/keyboard_h.py",
    "content": "\"\"\"Used by the make system to generate keyboard.h from info.json.\n\"\"\"\nfrom pathlib import Path\n\nfrom milc import cli\n\nfrom qmk.path import normpath\nfrom qmk.info import info_json\nfrom qmk.commands import dump_lines\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.constants import COL_LETTERS, ROW_LETTERS, GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE\n\n\ndef _generate_layouts(keyboard, kb_info_json):\n    \"\"\"Generates the layouts macros.\n    \"\"\"\n    if 'matrix_size' not in kb_info_json:\n        cli.log.error(f'{keyboard}: Invalid matrix config.')\n        return []\n\n    col_num = kb_info_json['matrix_size']['cols']\n    row_num = kb_info_json['matrix_size']['rows']\n\n    lines = []\n    lines.append('')\n    lines.append('// Layout content')\n    lines.append('')\n    lines.append('#define XXX KC_NO')\n\n    for layout_name, layout_data in kb_info_json['layouts'].items():\n        if layout_data['c_macro']:\n            continue\n\n        if not all('matrix' in key_data for key_data in layout_data['layout']):\n            cli.log.debug(f'{keyboard}/{layout_name}: No or incomplete matrix data!')\n            continue\n\n        layout_keys = []\n        layout_matrix = [['XXX'] * col_num for _ in range(row_num)]\n\n        for key_data in layout_data['layout']:\n            row, col = key_data['matrix']\n            identifier = f'k{ROW_LETTERS[row]}{COL_LETTERS[col]}'\n            if row >= row_num or col >= col_num:\n                cli.log.error(f'Skipping layouts due to {layout_name} containing invalid matrix values')\n                return []\n\n            layout_matrix[row][col] = identifier\n            layout_keys.append(identifier)\n\n        lines.append('')\n        lines.append(f'#define {layout_name}({\", \".join(layout_keys)}) {{ \\\\')\n\n        rows = ', \\\\\\n'.join(['    { ' + ', '.join(row) + ' }' for row in layout_matrix])\n        rows += ' \\\\'\n        lines.append(rows)\n        lines.append('}')\n\n    for alias, target in kb_info_json.get('layout_aliases', {}).items():\n        lines.append('')\n        lines.append(f'#ifndef {alias}')\n        lines.append(f'#   define {alias} {target}')\n        lines.append('#endif')\n\n    return lines\n\n\ndef _generate_keycodes(kb_info_json):\n    \"\"\"Generates keyboard level keycodes.\n    \"\"\"\n    if 'keycodes' not in kb_info_json:\n        return []\n\n    lines = []\n    lines.append('')\n    lines.append('// Keycode content')\n    lines.append('')\n    lines.append('enum keyboard_keycodes {')\n\n    for index, item in enumerate(kb_info_json.get('keycodes')):\n        key = item[\"key\"]\n        if index == 0:\n            lines.append(f'  {key} = QK_KB_0,')\n        else:\n            lines.append(f'  {key},')\n\n    lines.append('};')\n\n    for item in kb_info_json.get('keycodes', []):\n        key = item[\"key\"]\n        for alias in item.get(\"aliases\", []):\n            lines.append(f'#define {alias} {key}')\n\n    return lines\n\n\n@cli.argument('-i', '--include', nargs='?', arg_only=True, help='Optional file to include')\n@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='Keyboard to generate keyboard.h for.')\n@cli.subcommand('Used by the make system to generate keyboard.h from info.json', hidden=True)\ndef generate_keyboard_h(cli):\n    \"\"\"Generates the keyboard.h file.\n    \"\"\"\n    # Build the info.json file\n    kb_info_json = info_json(cli.args.keyboard)\n\n    keyboard_h = cli.args.include\n    dd_layouts = _generate_layouts(cli.args.keyboard, kb_info_json)\n    dd_keycodes = _generate_keycodes(kb_info_json)\n    valid_config = dd_layouts or keyboard_h\n\n    # Build the layouts.h file.\n    keyboard_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '', '#include \"quantum.h\"']\n\n    if dd_layouts:\n        keyboard_h_lines.extend(dd_layouts)\n\n    if keyboard_h:\n        keyboard_h_lines.append(f'#include \"{Path(keyboard_h).name}\"')\n\n    if dd_keycodes:\n        keyboard_h_lines.extend(dd_keycodes)\n\n    # Protect against poorly configured keyboards\n    if not valid_config:\n        keyboard_h_lines.append('#error(\"<keyboard>.h is required unless your keyboard uses data-driven configuration. Please rename your keyboard\\'s header file to <keyboard>.h\")')\n\n    # Show the results\n    dump_lines(cli.args.output, keyboard_h_lines, cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/keycodes.py",
    "content": "\"\"\"Used by the make system to generate keycodes.h from keycodes_{version}.json\n\"\"\"\nfrom milc import cli\n\nfrom qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE\nfrom qmk.commands import dump_lines\nfrom qmk.path import normpath\nfrom qmk.keycodes import load_spec\n\n\ndef _translate_group(group):\n    \"\"\"Fix up any issues with badly chosen values\n    \"\"\"\n    if group == 'modifiers':\n        return 'modifier'\n    if group == 'media':\n        return 'consumer'\n    return group\n\n\ndef _render_key(key):\n    width = 7\n    if 'S(' in key:\n        width += len('S()')\n    if 'A(' in key:\n        width += len('A()')\n    if 'RCTL(' in key:\n        width += len('RCTL()')\n    if 'ALGR(' in key:\n        width += len('ALGR()')\n    return key.ljust(width)\n\n\ndef _render_label(label):\n    label = label.replace(\"\\\\\", \"(backslash)\")\n    return label\n\n\ndef _generate_ranges(lines, keycodes):\n    lines.append('')\n    lines.append('enum qk_keycode_ranges {')\n    lines.append('// Ranges')\n    for key, value in keycodes[\"ranges\"].items():\n        lo, mask = map(lambda x: int(x, 16), key.split(\"/\"))\n        hi = lo + mask\n        define = value.get(\"define\")\n        lines.append(f'    {define.ljust(30)} = 0x{lo:04X},')\n        lines.append(f'    {(define + \"_MAX\").ljust(30)} = 0x{hi:04X},')\n    lines.append('};')\n\n\ndef _generate_defines(lines, keycodes):\n    lines.append('')\n    lines.append('enum qk_keycode_defines {')\n    lines.append('// Keycodes')\n    for key, value in keycodes[\"keycodes\"].items():\n        lines.append(f'    {value.get(\"key\")} = {key},')\n\n    lines.append('')\n    lines.append('// Alias')\n    for key, value in keycodes[\"keycodes\"].items():\n        temp = value.get(\"key\")\n        for alias in value.get(\"aliases\", []):\n            lines.append(f'    {alias.ljust(10)} = {temp},')\n\n    lines.append('};')\n\n\ndef _generate_helpers(lines, keycodes):\n    lines.append('')\n    lines.append('// Range Helpers')\n    for value in keycodes[\"ranges\"].values():\n        define = value.get(\"define\")\n        lines.append(f'#define IS_{define}(code) ((code) >= {define} && (code) <= {define + \"_MAX\"})')\n\n    # extract min/max\n    temp = {}\n    for key, value in keycodes[\"keycodes\"].items():\n        group = value.get('group', None)\n        if not group:\n            continue\n        if group not in temp:\n            temp[group] = [0xFFFF, 0]\n        key = int(key, 16)\n        if key < temp[group][0]:\n            temp[group][0] = key\n        if key > temp[group][1]:\n            temp[group][1] = key\n\n    lines.append('')\n    lines.append('// Group Helpers')\n    for group, codes in temp.items():\n        lo = keycodes[\"keycodes\"][f'0x{codes[0]:04X}']['key']\n        hi = keycodes[\"keycodes\"][f'0x{codes[1]:04X}']['key']\n        lines.append(f'#define IS_{_translate_group(group).upper()}_KEYCODE(code) ((code) >= {lo} && (code) <= {hi})')\n\n    lines.append('')\n    lines.append('// Switch statement Helpers')\n    for group, codes in temp.items():\n        lo = keycodes[\"keycodes\"][f'0x{codes[0]:04X}']['key']\n        hi = keycodes[\"keycodes\"][f'0x{codes[1]:04X}']['key']\n        name = f'{_translate_group(group).upper()}_KEYCODE_RANGE'\n        lines.append(f'#define {name.ljust(35)} {lo} ... {hi}')\n\n\ndef _generate_aliases(lines, keycodes):\n    # Work around ChibiOS ch.h include guard\n    if 'CH_H' in [value['key'] for value in keycodes['aliases'].values()]:\n        lines.append('')\n        lines.append('#undef CH_H')\n\n    lines.append('')\n    lines.append('// Aliases')\n    for key, value in keycodes[\"aliases\"].items():\n        define = _render_key(value.get(\"key\"))\n        val = _render_key(key)\n        if 'label' in value:\n            lines.append(f'#define {define} {val} // {_render_label(value.get(\"label\"))}')\n        else:\n            lines.append(f'#define {define} {val}')\n\n    lines.append('')\n    for key, value in keycodes[\"aliases\"].items():\n        for alias in value.get(\"aliases\", []):\n            lines.append(f'#define {alias} {value.get(\"key\")}')\n\n\ndef _generate_version(lines, keycodes, prefix=''):\n    version = keycodes['version']\n    major, minor, patch = map(int, version.split('.'))\n\n    bcd = f'0x{major:02d}{minor:02d}{patch:04d}'\n\n    lines.append('')\n    lines.append(f'#define QMK_{prefix}KEYCODES_VERSION \"{version}\"')\n    lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_BCD {bcd}')\n    lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_MAJOR {major}')\n    lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_MINOR {minor}')\n    lines.append(f'#define QMK_{prefix}KEYCODES_VERSION_PATCH {patch}')\n\n\n@cli.argument('-v', '--version', arg_only=True, required=True, help='Version of keycodes to generate.')\n@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.subcommand('Used by the make system to generate keycodes.h from keycodes_{version}.json', hidden=True)\ndef generate_keycodes(cli):\n    \"\"\"Generates the keycodes.h file.\n    \"\"\"\n\n    # Build the keycodes.h file.\n    keycodes_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '// clang-format off']\n\n    keycodes = load_spec(cli.args.version)\n\n    _generate_version(keycodes_h_lines, keycodes)\n    _generate_ranges(keycodes_h_lines, keycodes)\n    _generate_defines(keycodes_h_lines, keycodes)\n    _generate_helpers(keycodes_h_lines, keycodes)\n\n    # Show the results\n    dump_lines(cli.args.output, keycodes_h_lines, cli.args.quiet)\n\n\n@cli.argument('-v', '--version', arg_only=True, required=True, help='Version of keycodes to generate.')\n@cli.argument('-l', '--lang', arg_only=True, required=True, help='Language of keycodes to generate.')\n@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.subcommand('Used by the make system to generate keymap_{lang}.h from keycodes_{lang}_{version}.json', hidden=True)\ndef generate_keycode_extras(cli):\n    \"\"\"Generates the header file.\n    \"\"\"\n\n    # Build the header file.\n    keycodes_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '#include \"keycodes.h\"', '// clang-format off']\n\n    keycodes = load_spec(cli.args.version, cli.args.lang)\n\n    _generate_version(keycodes_h_lines, keycodes, f'{cli.args.lang.upper()}_')\n    _generate_aliases(keycodes_h_lines, keycodes)\n\n    # Show the results\n    dump_lines(cli.args.output, keycodes_h_lines, cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/keymap_h.py",
    "content": "from argcomplete.completers import FilesCompleter\n\nfrom milc import cli\n\nimport qmk.path\nfrom qmk.commands import dump_lines\nfrom qmk.commands import parse_configurator_json\nfrom qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE\n\n\ndef _generate_keycodes_function(keymap_json):\n    \"\"\"Generates keymap level keycodes.\n    \"\"\"\n    lines = []\n    lines.append('enum keymap_keycodes {')\n\n    for index, item in enumerate(keymap_json.get('keycodes', [])):\n        key = item[\"key\"]\n        if index == 0:\n            lines.append(f'  {key} = QK_USER_0,')\n        else:\n            lines.append(f'  {key},')\n\n    lines.append('};')\n\n    for item in keymap_json.get('keycodes', []):\n        key = item[\"key\"]\n        for alias in item.get(\"aliases\", []):\n            lines.append(f'#define {alias} {key}')\n\n    return lines\n\n\n@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('filename', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')\n@cli.subcommand('Creates a keymap.h from a QMK Configurator export.')\ndef generate_keymap_h(cli):\n    \"\"\"Creates a keymap.h from a QMK Configurator export\n    \"\"\"\n    if cli.args.output and cli.args.output.name == '-':\n        cli.args.output = None\n\n    keymap_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '// clang-format off']\n\n    keymap_json = parse_configurator_json(cli.args.filename)\n\n    if 'keycodes' in keymap_json and keymap_json['keycodes'] is not None:\n        keymap_h_lines += _generate_keycodes_function(keymap_json)\n\n    dump_lines(cli.args.output, keymap_h_lines, cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/make_dependencies.py",
    "content": "\"\"\"Used by the make system to generate dependency lists for each of the generated files.\n\"\"\"\nfrom pathlib import Path\nfrom milc import cli\n\nfrom argcomplete.completers import FilesCompleter\n\nfrom qmk.commands import dump_lines\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.keymap import keymap_completer, locate_keymap\nfrom qmk.path import normpath, FileType, unix_style_path\n\n\n@cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), completer=FilesCompleter('.json'), help='A configurator export JSON.')\n@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, required=True, help='Keyboard to generate dependency file for.')\n@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')\n@cli.subcommand('Generates the list of dependencies associated with a keyboard build and its generated files.', hidden=True)\ndef generate_make_dependencies(cli):\n    \"\"\"Generates the list of dependent config files for a keyboard.\n    \"\"\"\n    interesting_files = [\n        'info.json',\n        'keyboard.json',\n        'rules.mk',\n        'post_rules.mk',\n        'config.h',\n        'post_config.h',\n    ]\n\n    check_files = []\n\n    # Walk up the keyboard's directory tree looking for the files we're interested in\n    keyboards_root = Path('keyboards')\n    parent_path = Path('keyboards') / cli.args.keyboard\n    while parent_path != keyboards_root:\n        for file in interesting_files:\n            check_files.append(parent_path / file)\n        parent_path = parent_path.parent\n\n    # Find the keymap and include any of the interesting files\n    if cli.args.keymap is not None:\n        km = locate_keymap(cli.args.keyboard, cli.args.keymap)\n        if km is not None:\n            # keymap.json is only valid for the keymap, so check this one separately\n            check_files.append(km.parent / 'keymap.json')\n            # Add all the interesting files\n            for file in interesting_files:\n                check_files.append(km.parent / file)\n\n    # If we have a matching userspace, include those too\n    for file in interesting_files:\n        check_files.append(Path('users') / cli.args.keymap / file)\n\n    dump_lines(cli.args.output, [f'generated-files: $(wildcard {unix_style_path(found)})\\n' for found in check_files])\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/rgb_breathe_table.py",
    "content": "\"\"\"Generate rgblight_breathe_table.h\n\"\"\"\nimport math\nfrom argparse import ArgumentTypeError\n\nfrom milc import cli\n\nfrom qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE\nfrom qmk.commands import dump_lines\nfrom qmk.path import normpath\n\n\ndef breathing_center(value):\n    value = float(value)\n    if value >= 1 and value <= 2.7:\n        return value\n    else:\n        raise ArgumentTypeError('Breathing center must be between 1 and 2.7')\n\n\ndef breathing_max(value):\n    value = int(value)\n    if value in range(0, 256):\n        return value\n    else:\n        raise ArgumentTypeError('Breathing max must be between 0 and 255')\n\n\ndef _generate_table(lines, center, maximum):\n    breathe_values = [0] * 256\n    for pos in range(0, 256):\n        breathe_values[pos] = (int)((math.exp(math.sin((pos / 255) * math.pi)) - center / math.e) * (maximum / (math.e - 1 / math.e)))\n\n    values_template = ''\n    for s in range(0, 3):\n        step = 1 << s\n\n        values_template += '#if RGBLIGHT_BREATHE_TABLE_SIZE == {}\\n'.format(256 >> s)\n\n        for pos in range(0, 256, step):\n            values_template += '    ' if pos % 8 == 0 else ''\n            values_template += '0x{:02X}'.format(breathe_values[pos])\n            values_template += ',' if (pos + step) < 256 else ''\n            values_template += '\\n' if (pos + step) % 8 == 0 else ' '\n\n        values_template += '#endif'\n        values_template += '\\n\\n' if s < 2 else ''\n\n    table_template = '''#define RGBLIGHT_EFFECT_BREATHE_TABLE\n\n// Breathing center: {0:.2f}\n// Breathing max:    {1:d}\n\nconst uint8_t PROGMEM rgblight_effect_breathe_table[] = {{\n{2}\n}};\n\nstatic const int table_scale = 256 / sizeof(rgblight_effect_breathe_table);\n'''.format(center, maximum, values_template)\n    lines.append(table_template)\n\n\n@cli.argument('-c', '--center', arg_only=True, type=breathing_center, default=1.85, help='The breathing center value, from 1 to 2.7. Default: 1.85')\n@cli.argument('-m', '--max', arg_only=True, type=breathing_max, default=255, help='The breathing maximum value, from 0 to 255. Default: 255')\n@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help='Quiet mode, only output error messages')\n@cli.subcommand('Generates an RGB Light breathing table header.')\ndef generate_rgb_breathe_table(cli):\n    \"\"\"Generate a rgblight_breathe_table.h file containing a breathing LUT for RGB Lighting (Underglow) feature.\n    \"\"\"\n\n    # Build the header file.\n    header_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once', '// clang-format off']\n\n    _generate_table(header_lines, cli.args.center, cli.args.max)\n\n    # Show the results\n    dump_lines(cli.args.output, header_lines, cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/rules_mk.py",
    "content": "\"\"\"Used by the make system to generate a rules.mk\n\"\"\"\nfrom pathlib import Path\nfrom dotty_dict import dotty\n\nfrom argcomplete.completers import FilesCompleter\nfrom milc import cli\n\nfrom qmk.info import info_json\nfrom qmk.json_schema import json_load\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.commands import dump_lines, parse_configurator_json\nfrom qmk.path import normpath, FileType\nfrom qmk.constants import GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE\n\n\ndef generate_rule(rules_key, rules_value):\n    is_keymap = cli.args.filename\n    rule_assignment_operator = '=' if is_keymap else '?='\n    return f'{rules_key} {rule_assignment_operator} {rules_value}'\n\n\ndef process_mapping_rule(kb_info_json, rules_key, info_dict):\n    \"\"\"Return the rules.mk line(s) for a mapping rule.\n    \"\"\"\n    if not info_dict.get('to_c', True):\n        return None\n\n    info_key = info_dict['info_key']\n    key_type = info_dict.get('value_type', 'raw')\n\n    try:\n        rules_value = kb_info_json[info_key]\n    except KeyError:\n        return None\n\n    if key_type in ['array', 'list']:\n        return generate_rule(rules_key, \" \".join(rules_value))\n    elif key_type == 'bool':\n        return generate_rule(rules_key, \"yes\" if rules_value else \"no\")\n    elif key_type == 'mapping':\n        return '\\n'.join([generate_rule(key, value) for key, value in rules_value.items()])\n    elif key_type == 'str':\n        return generate_rule(rules_key, f'\"{rules_value}\"')\n\n    return generate_rule(rules_key, rules_value)\n\n\ndef generate_features_rules(features_dict):\n    lines = []\n    for feature, enabled in features_dict.items():\n        feature = feature.upper()\n        enabled = 'yes' if enabled else 'no'\n        lines.append(generate_rule(f'{feature}_ENABLE', enabled))\n    return lines\n\n\n@cli.argument('filename', nargs='?', arg_only=True, type=FileType('r'), completer=FilesCompleter('.json'), help='A configurator export JSON to be compiled and flashed or a pre-compiled binary firmware file (bin/hex) to be flashed.')\n@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('-e', '--escape', arg_only=True, action='store_true', help=\"Escape spaces in quiet mode\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, help='Keyboard to generate rules.mk for.')\n@cli.subcommand('Used by the make system to generate rules.mk from info.json', hidden=True)\ndef generate_rules_mk(cli):\n    \"\"\"Generates a rules.mk file from info.json.\n    \"\"\"\n    converter = None\n    # Determine our keyboard/keymap\n    if cli.args.filename:\n        user_keymap = parse_configurator_json(cli.args.filename)\n        kb_info_json = dotty(user_keymap.get('config', {}))\n        converter = user_keymap.get('converter', None)\n    elif cli.args.keyboard:\n        kb_info_json = dotty(info_json(cli.args.keyboard))\n    else:\n        cli.log.error('You must supply a configurator export or `--keyboard`.')\n        cli.subcommands['generate-rules-mk'].print_help()\n        return False\n\n    info_rules_map = json_load(Path('data/mappings/info_rules.hjson'))\n    rules_mk_lines = [GPL2_HEADER_SH_LIKE, GENERATED_HEADER_SH_LIKE]\n\n    # Iterate through the info_rules map to generate basic rules\n    for rules_key, info_dict in info_rules_map.items():\n        new_entry = process_mapping_rule(kb_info_json, rules_key, info_dict)\n\n        if new_entry:\n            rules_mk_lines.append(new_entry)\n\n    # Iterate through features to enable/disable them\n    if 'features' in kb_info_json:\n        rules_mk_lines.extend(generate_features_rules(kb_info_json['features']))\n\n    # Set SPLIT_TRANSPORT, if needed\n    if kb_info_json.get('split', {}).get('transport', {}).get('protocol') == 'custom':\n        rules_mk_lines.append(generate_rule('SPLIT_TRANSPORT', 'custom'))\n\n    # Set CUSTOM_MATRIX, if needed\n    if kb_info_json.get('matrix_pins', {}).get('custom_lite'):\n        rules_mk_lines.append(generate_rule('CUSTOM_MATRIX', 'lite'))\n    elif kb_info_json.get('matrix_pins', {}).get('custom'):\n        rules_mk_lines.append(generate_rule('CUSTOM_MATRIX', 'yes'))\n\n    if converter:\n        rules_mk_lines.append(generate_rule('CONVERT_TO', converter))\n\n    # Show the results\n    dump_lines(cli.args.output, rules_mk_lines)\n\n    if cli.args.output:\n        if cli.args.quiet:\n            if cli.args.escape:\n                print(cli.args.output.as_posix().replace(' ', '\\\\ '))\n            else:\n                print(cli.args.output)\n        else:\n            cli.log.info('Wrote rules.mk to %s.', cli.args.output)\n"
  },
  {
    "path": "lib/python/qmk/cli/generate/version_h.py",
    "content": "\"\"\"Used by the make system to generate version.h for use in code.\n\"\"\"\nfrom time import strftime\n\nfrom milc import cli\n\nfrom qmk.path import normpath\nfrom qmk.commands import dump_lines\nfrom qmk.git import git_get_qmk_hash, git_get_version, git_is_dirty\nfrom qmk.constants import GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE\nfrom qmk.util import triplet_to_bcd\n\nTIME_FMT = '%Y-%m-%d-%H:%M:%S'\n\n\n@cli.argument('-o', '--output', arg_only=True, type=normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('--skip-git', arg_only=True, action='store_true', help='Skip Git operations')\n@cli.argument('--skip-all', arg_only=True, action='store_true', help='Use placeholder values for all defines (implies --skip-git)')\n@cli.subcommand('Used by the make system to generate version.h for use in code', hidden=True)\ndef generate_version_h(cli):\n    \"\"\"Generates the version.h file.\n    \"\"\"\n    if cli.args.skip_all:\n        cli.args.skip_git = True\n\n    if cli.args.skip_all:\n        current_time = \"1970-01-01-00:00:00\"\n    else:\n        current_time = strftime(TIME_FMT)\n\n    if cli.args.skip_git:\n        git_dirty = False\n        git_version = \"NA\"\n        git_qmk_hash = \"NA\"\n        git_bcd_version = \"0x00000000\"\n        chibios_version = \"NA\"\n        chibios_contrib_version = \"NA\"\n    else:\n        git_dirty = git_is_dirty()\n        git_version = git_get_version() or current_time\n        git_qmk_hash = git_get_qmk_hash() or \"Unknown\"\n        git_bcd_version = triplet_to_bcd(git_version)\n        chibios_version = git_get_version(\"chibios\", \"os\") or current_time\n        chibios_contrib_version = git_get_version(\"chibios-contrib\", \"os\") or current_time\n\n    # Build the version.h file.\n    version_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#pragma once']\n\n    version_h_lines.append(\n        f\"\"\"\n#define QMK_VERSION \"{git_version}\"\n#define QMK_BUILDDATE \"{current_time}\"\n#define QMK_VERSION_BCD {git_bcd_version}\n#define QMK_GIT_HASH  \"{git_qmk_hash}{'*' if git_dirty else ''}\"\n#define CHIBIOS_VERSION \"{chibios_version}\"\n#define CHIBIOS_CONTRIB_VERSION \"{chibios_contrib_version}\"\n\"\"\"\n    )\n\n    # Show the results\n    dump_lines(cli.args.output, version_h_lines, cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/cli/git/__init__.py",
    "content": ""
  },
  {
    "path": "lib/python/qmk/cli/git/submodule.py",
    "content": "import shutil\nfrom pathlib import Path\n\nfrom milc import cli\n\nfrom qmk import submodules\n\nREMOVE_DIRS = [\n    'lib/ugfx',\n    'lib/chibios-contrib/ext/mcux-sdk',\n]\n\nIGNORE_DIRS = [\n    'lib/arm_atsam',\n    'lib/fnv',\n    'lib/lib8tion',\n    'lib/python',\n    'lib/usbhost',\n]\n\n\n@cli.argument('--check', arg_only=True, action='store_true', help='Check if the submodules are dirty, and display a warning if they are.')\n@cli.argument('--sync', arg_only=True, action='store_true', help='Shallow clone any missing submodules.')\n@cli.argument('-f', '--force', action='store_true', help='Flag to remove unexpected directories')\n@cli.subcommand('Git Submodule actions.')\ndef git_submodule(cli):\n    \"\"\"Git Submodule actions\n    \"\"\"\n    if cli.args.check:\n        return all(item['status'] for item in submodules.status().values())\n\n    if cli.args.sync:\n        cli.run(['git', 'submodule', 'sync', '--recursive'])\n        for name, item in submodules.status().items():\n            if item['status'] is None:\n                cli.run(['git', 'submodule', 'update', '--depth=50', '--init', name], capture_output=False)\n        return True\n\n    # can be the default behavior with: qmk config git_submodule.force=True\n    remove_dirs = REMOVE_DIRS\n    if cli.config.git_submodule.force:\n        # Also trash everything that isnt marked as \"safe\"\n        for path in Path('lib').iterdir():\n            if not any(ignore in path.as_posix() for ignore in IGNORE_DIRS):\n                remove_dirs.append(path)\n\n    for folder in map(Path, remove_dirs):\n        if folder.is_dir():\n            print(f\"Removing '{folder}'\")\n            shutil.rmtree(folder)\n\n    cli.run(['git', 'submodule', 'sync', '--recursive'], capture_output=False)\n    cli.run(['git', 'submodule', 'update', '--init', '--recursive', '--progress'], capture_output=False)\n"
  },
  {
    "path": "lib/python/qmk/cli/hello.py",
    "content": "\"\"\"QMK Python Hello World\n\nThis is an example QMK CLI script.\n\"\"\"\nfrom milc import cli\n\n\n@cli.argument('-n', '--name', default='World', help='Name to greet.')\n@cli.subcommand('QMK Hello World.', hidden=False if cli.config.user.developer else True)\ndef hello(cli):\n    \"\"\"Log a friendly greeting.\n    \"\"\"\n    cli.log.info('Hello, %s!', cli.config.hello.name)\n"
  },
  {
    "path": "lib/python/qmk/cli/import/__init__.py",
    "content": ""
  },
  {
    "path": "lib/python/qmk/cli/import/kbfirmware.py",
    "content": "from milc import cli\n\nfrom qmk.importers import import_kbfirmware as _import_kbfirmware\nfrom qmk.path import FileType\nfrom qmk.json_schema import json_load\n\n\n@cli.argument('filename', type=FileType('r'), nargs='+', arg_only=True, help='file')\n@cli.subcommand('Import kbfirmware json export')\ndef import_kbfirmware(cli):\n    filename = cli.args.filename[0]\n\n    data = json_load(filename)\n\n    cli.log.info(f'{{style_bright}}Importing {filename.name}.{{style_normal}}')\n    cli.echo('')\n\n    cli.log.warning(\"Support here is basic - Consider using 'qmk new-keyboard' instead\")\n\n    kb_name = _import_kbfirmware(data)\n\n    cli.log.info(f'{{fg_green}}Imported a new keyboard named {{fg_cyan}}{kb_name}{{fg_green}}.{{fg_reset}}')\n    cli.log.info(f'To start working on things, `cd` into {{fg_cyan}}keyboards/{kb_name}{{fg_reset}},')\n    cli.log.info('or open the directory in your preferred text editor.')\n    cli.log.info(f\"And build with {{fg_yellow}}qmk compile -kb {kb_name} -km default{{fg_reset}}.\")\n"
  },
  {
    "path": "lib/python/qmk/cli/import/keyboard.py",
    "content": "from milc import cli\n\nfrom qmk.importers import import_keyboard as _import_keyboard\nfrom qmk.path import FileType\nfrom qmk.json_schema import json_load\n\n\n@cli.argument('filename', type=FileType('r'), nargs='+', arg_only=True, help='file')\n@cli.subcommand('Import data-driven keyboard')\ndef import_keyboard(cli):\n    filename = cli.args.filename[0]\n\n    data = json_load(filename)\n\n    cli.log.info(f'{{style_bright}}Importing {filename.name}.{{style_normal}}')\n    cli.echo('')\n\n    kb_name = _import_keyboard(data)\n\n    cli.log.info(f'{{fg_green}}Imported a new keyboard named {{fg_cyan}}{kb_name}{{fg_green}}.{{fg_reset}}')\n    cli.log.info(f'To start working on things, `cd` into {{fg_cyan}}keyboards/{kb_name}{{fg_reset}},')\n    cli.log.info('or open the directory in your preferred text editor.')\n    cli.log.info(f\"And build with {{fg_yellow}}qmk compile -kb {kb_name} -km default{{fg_reset}}.\")\n"
  },
  {
    "path": "lib/python/qmk/cli/import/keymap.py",
    "content": "from milc import cli\n\nfrom qmk.importers import import_keymap as _import_keymap\nfrom qmk.path import FileType\nfrom qmk.json_schema import json_load\n\n\n@cli.argument('filename', type=FileType('r'), nargs='+', arg_only=True, help='file')\n@cli.subcommand('Import data-driven keymap')\ndef import_keymap(cli):\n    filename = cli.args.filename[0]\n\n    data = json_load(filename)\n\n    cli.log.info(f'{{style_bright}}Importing {filename.name}.{{style_normal}}')\n    cli.echo('')\n\n    kb_name, km_name = _import_keymap(data)\n\n    cli.log.info(f'{{fg_green}}Imported a new keymap named {{fg_cyan}}{km_name}{{fg_green}}.{{fg_reset}}')\n    cli.log.info(f'To start working on things, `cd` into {{fg_cyan}}keyboards/{kb_name}/keymaps/{km_name}{{fg_reset}},')\n    cli.log.info('or open the directory in your preferred text editor.')\n    cli.log.info(f\"And build with {{fg_yellow}}qmk compile -kb {kb_name} -km {km_name}{{fg_reset}}.\")\n"
  },
  {
    "path": "lib/python/qmk/cli/info.py",
    "content": "\"\"\"Keyboard information script.\n\nCompile an info.json for a particular keyboard and pretty-print it.\n\"\"\"\nimport sys\nimport json\n\nfrom milc import cli\n\nfrom qmk.json_encoders import InfoJSONEncoder\nfrom qmk.constants import COL_LETTERS, ROW_LETTERS\nfrom qmk.decorators import automagic_keyboard, automagic_keymap\nfrom qmk.keyboard import keyboard_completer, keyboard_folder, render_layouts, render_layout, rules_mk\nfrom qmk.info import info_json, keymap_json\nfrom qmk.keymap import locate_keymap\nfrom qmk.path import is_keyboard\n\nUNICODE_SUPPORT = sys.stdout.encoding.lower().startswith('utf')\n\n\ndef _strip_api_content(info_json):\n    # Ideally this would only be added in the API pathway.\n    info_json.pop('platform', None)\n    info_json.pop('platform_key', None)\n    info_json.pop('processor_type', None)\n    info_json.pop('protocol', None)\n    info_json.pop('config_h_features', None)\n    info_json.pop('keymaps', None)\n    info_json.pop('keyboard_folder', None)\n    info_json.pop('parse_errors', None)\n    info_json.pop('parse_warnings', None)\n\n    for layout in info_json.get('layouts', {}).values():\n        layout.pop('filename', None)\n        layout.pop('c_macro', None)\n        layout.pop('json_layout', None)\n\n    if 'matrix_pins' in info_json:\n        info_json.pop('matrix_size', None)\n\n    for feature in ['rgb_matrix', 'led_matrix']:\n        if info_json.get(feature, {}).get(\"layout\", None):\n            info_json[feature].pop('led_count', None)\n\n    return info_json\n\n\ndef show_keymap(kb_info_json, title_caps=True):\n    \"\"\"Render the keymap in ascii art.\n    \"\"\"\n    keymap_path = locate_keymap(cli.config.info.keyboard, cli.config.info.keymap)\n\n    if keymap_path and keymap_path.suffix == '.json':\n        keymap_data = json.load(keymap_path.open(encoding='utf-8'))\n\n        # cater for layout-less keymap.json\n        if 'layout' not in keymap_data:\n            return\n\n        layout_name = keymap_data['layout']\n        layout_name = kb_info_json.get('layout_aliases', {}).get(layout_name, layout_name)  # Resolve alias names\n\n        for layer_num, layer in enumerate(keymap_data['layers']):\n            if title_caps:\n                cli.echo('{fg_cyan}Keymap %s Layer %s{fg_reset}:', cli.config.info.keymap, layer_num)\n            else:\n                cli.echo('{fg_cyan}keymap.%s.layer.%s{fg_reset}:', cli.config.info.keymap, layer_num)\n\n            print(render_layout(kb_info_json['layouts'][layout_name]['layout'], cli.config.info.ascii, layer))\n\n\ndef show_layouts(kb_info_json, title_caps=True):\n    \"\"\"Render the layouts with info.json labels.\n    \"\"\"\n    for layout_name, layout_art in render_layouts(kb_info_json, cli.config.info.ascii).items():\n        title = f'Layout {layout_name.title()}' if title_caps else f'layouts.{layout_name}'\n        cli.echo('{fg_cyan}%s{fg_reset}:', title)\n        print(layout_art)  # Avoid passing dirty data to cli.echo()\n\n\ndef show_matrix(kb_info_json, title_caps=True):\n    \"\"\"Render the layout with matrix labels in ascii art.\n    \"\"\"\n    for layout_name, layout in kb_info_json['layouts'].items():\n        # Build our label list\n        labels = []\n        for key in layout['layout']:\n            if 'matrix' in key:\n                row = ROW_LETTERS[key['matrix'][0]]\n                col = COL_LETTERS[key['matrix'][1]]\n\n                labels.append(row + col)\n            else:\n                labels.append('')\n\n        # Print the header\n        if title_caps:\n            cli.echo('{fg_blue}Matrix for \"%s\"{fg_reset}:', layout_name)\n        else:\n            cli.echo('{fg_blue}matrix_%s{fg_reset}:', layout_name)\n\n        print(render_layout(kb_info_json['layouts'][layout_name]['layout'], cli.config.info.ascii, labels))\n\n\ndef show_leds(kb_info_json, title_caps=True):\n    \"\"\"Render LED indices per key, using the keyboard's key layout geometry.\n\n    We build a map from (row, col) -> LED index using rgb_matrix/led_matrix layout,\n    then label each key with its LED index. Keys without an associated LED are left blank.\n    \"\"\"\n    # Prefer rgb_matrix, fall back to led_matrix\n    led_feature = None\n    for feature in ['rgb_matrix', 'led_matrix']:\n        if 'layout' in kb_info_json.get(feature, {}):\n            led_feature = feature\n            break\n\n    if not led_feature:\n        cli.echo('{fg_yellow}No rgb_matrix/led_matrix layout found to derive LED indices.{fg_reset}')\n        return\n\n    # Build mapping from matrix position -> LED indices for faster lookup later\n    by_matrix = {}\n    for idx, led in enumerate(kb_info_json[led_feature]['layout']):\n        if 'matrix' in led:\n            led_key = tuple(led.get('matrix'))\n            by_matrix[led_key] = idx\n\n    # For each keyboard layout (e.g., LAYOUT), render keys labeled with LED index (or blank)\n    for layout_name, layout in kb_info_json['layouts'].items():\n        labels = []\n        for key in layout['layout']:\n            led_key = tuple(key.get('matrix'))\n            label = str(by_matrix[led_key]) if led_key in by_matrix else ''\n\n            labels.append(label)\n\n        # Header\n        if title_caps:\n            cli.echo('{fg_blue}LED indices for \"%s\"{fg_reset}:', layout_name)\n        else:\n            cli.echo('{fg_blue}leds_%s{fg_reset}:', layout_name)\n\n        print(render_layout(kb_info_json['layouts'][layout_name]['layout'], cli.config.info.ascii, labels))\n\n\ndef print_friendly_output(kb_info_json):\n    \"\"\"Print the info.json in a friendly text format.\n    \"\"\"\n    cli.echo('{fg_blue}Keyboard Name{fg_reset}: %s', kb_info_json.get('keyboard_name', 'Unknown'))\n    cli.echo('{fg_blue}Manufacturer{fg_reset}: %s', kb_info_json.get('manufacturer', 'Unknown'))\n    if 'url' in kb_info_json:\n        cli.echo('{fg_blue}Website{fg_reset}: %s', kb_info_json.get('url', ''))\n    if kb_info_json.get('maintainer', 'qmk') == 'qmk':\n        cli.echo('{fg_blue}Maintainer{fg_reset}: QMK Community')\n    else:\n        cli.echo('{fg_blue}Maintainer{fg_reset}: %s', kb_info_json['maintainer'])\n    cli.echo('{fg_blue}Layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))\n    cli.echo('{fg_blue}Processor{fg_reset}: %s', kb_info_json.get('processor', 'Unknown'))\n    cli.echo('{fg_blue}Bootloader{fg_reset}: %s', kb_info_json.get('bootloader', 'Unknown'))\n    if 'layout_aliases' in kb_info_json:\n        aliases = [f'{key}={value}' for key, value in kb_info_json['layout_aliases'].items()]\n        cli.echo('{fg_blue}Layout aliases:{fg_reset} %s' % (', '.join(aliases),))\n\n\ndef print_text_output(kb_info_json):\n    \"\"\"Print the info.json in a plain text format.\n    \"\"\"\n    for key in sorted(kb_info_json):\n        if key == 'layouts':\n            cli.echo('{fg_blue}layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))\n        else:\n            cli.echo('{fg_blue}%s{fg_reset}: %s', key, kb_info_json[key])\n\n    if cli.config.info.layouts:\n        show_layouts(kb_info_json, False)\n\n    if cli.config.info.matrix:\n        show_matrix(kb_info_json, False)\n\n    if cli.config_source.info.keymap and cli.config_source.info.keymap != 'config_file':\n        show_keymap(kb_info_json, False)\n\n\ndef print_dotted_output(kb_info_json, prefix=''):\n    \"\"\"Print the info.json in a plain text format with dot-joined keys.\n    \"\"\"\n    for key in sorted(kb_info_json):\n        new_prefix = f'{prefix}.{key}' if prefix else key\n\n        if key in ['parse_errors', 'parse_warnings']:\n            continue\n        elif key == 'layouts' and prefix == '':\n            cli.echo('{fg_blue}layouts{fg_reset}: %s', ', '.join(sorted(kb_info_json['layouts'].keys())))\n        elif isinstance(kb_info_json[key], dict):\n            print_dotted_output(kb_info_json[key], new_prefix)\n        elif isinstance(kb_info_json[key], list):\n            cli.echo('{fg_blue}%s{fg_reset}: %s', new_prefix, ', '.join(map(str, sorted(kb_info_json[key]))))\n        else:\n            cli.echo('{fg_blue}%s{fg_reset}: %s', new_prefix, kb_info_json[key])\n\n\ndef print_parsed_rules_mk(keyboard_name):\n    rules = rules_mk(keyboard_name)\n    for k in sorted(rules.keys()):\n        print('%s = %s' % (k, rules[k]))\n    return\n\n\n@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Keyboard to show info for.')\n@cli.argument('-km', '--keymap', help='Keymap to show info for (Optional).')\n@cli.argument('-l', '--layouts', action='store_true', help='Render the layouts.')\n@cli.argument('-m', '--matrix', action='store_true', help='Render the layouts with matrix information.')\n@cli.argument('-L', '--leds', action='store_true', help='Render the LED layout with LED indices (rgb_matrix/led_matrix).')\n@cli.argument('-f', '--format', default='friendly', arg_only=True, help='Format to display the data in (friendly, text, json) (Default: friendly).')\n@cli.argument('--ascii', action='store_true', default=not UNICODE_SUPPORT, help='Render layout box drawings in ASCII only.')\n@cli.argument('-r', '--rules-mk', action='store_true', help='Render the parsed values of the keyboard\\'s rules.mk file.')\n@cli.argument('-a', '--api', action='store_true', help='Show fully processed info intended for API consumption.')\n@cli.subcommand('Keyboard information.')\n@automagic_keyboard\n@automagic_keymap\ndef info(cli):\n    \"\"\"Compile an info.json for a particular keyboard and pretty-print it.\n    \"\"\"\n    # Determine our keyboard(s)\n    if not cli.config.info.keyboard:\n        cli.log.error('Missing parameter: --keyboard')\n        cli.subcommands['info'].print_help()\n        return False\n\n    if not is_keyboard(cli.config.info.keyboard):\n        cli.log.error('Invalid keyboard: \"%s\"', cli.config.info.keyboard)\n        return False\n\n    if bool(cli.args.rules_mk):\n        print_parsed_rules_mk(cli.config.info.keyboard)\n        return False\n\n    # default keymap stored in config file should be ignored\n    if cli.config_source.info.keymap == 'config_file':\n        cli.config_source.info.keymap = None\n\n    # Build the info.json file\n    if cli.config.info.keymap:\n        kb_info_json = keymap_json(cli.config.info.keyboard, cli.config.info.keymap)\n    else:\n        kb_info_json = info_json(cli.config.info.keyboard)\n\n    if not cli.args.api:\n        kb_info_json = _strip_api_content(kb_info_json)\n\n    # Output in the requested format\n    if cli.args.format == 'json':\n        print(json.dumps(kb_info_json, cls=InfoJSONEncoder, sort_keys=True))\n        return True\n    elif cli.args.format == 'text':\n        print_dotted_output(kb_info_json)\n        title_caps = False\n    elif cli.args.format == 'friendly':\n        print_friendly_output(kb_info_json)\n        title_caps = True\n    else:\n        cli.log.error('Unknown format: %s', cli.args.format)\n        return False\n\n    # Output requested extras\n    if cli.config.info.layouts:\n        show_layouts(kb_info_json, title_caps)\n\n    if cli.config.info.matrix:\n        show_matrix(kb_info_json, title_caps)\n\n    if cli.config.info.leds:\n        show_leds(kb_info_json, title_caps)\n\n    if cli.config.info.keymap:\n        show_keymap(kb_info_json, title_caps)\n"
  },
  {
    "path": "lib/python/qmk/cli/json2c.py",
    "content": "\"\"\"Generate a keymap.c from a configurator export.\n\"\"\"\nfrom argcomplete.completers import FilesCompleter\nfrom milc import cli\n\nimport qmk.keymap\nimport qmk.path\nfrom qmk.commands import dump_lines, parse_configurator_json\n\n\n@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('filename', type=qmk.path.FileType('r'), arg_only=True, completer=FilesCompleter('.json'), help='Configurator JSON file')\n@cli.subcommand('Creates a keymap.c from a QMK Configurator export.')\ndef json2c(cli):\n    \"\"\"Generate a keymap.c from a configurator export.\n\n    This command uses the `qmk.keymap` module to generate a keymap.c from a configurator export. The generated keymap is written to stdout, or to a file if -o is provided.\n    \"\"\"\n\n    # Parse the configurator from json file (or stdin)\n    user_keymap = parse_configurator_json(cli.args.filename)\n\n    # Generate the keymap\n    keymap_c = qmk.keymap.generate_c(user_keymap)\n\n    # Show the results\n    dump_lines(cli.args.output, keymap_c.split('\\n'), cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/cli/kle2json.py",
    "content": "\"\"\"Convert raw KLE to JSON\n\"\"\"\nimport json\nimport os\nfrom pathlib import Path\n\nfrom argcomplete.completers import FilesCompleter\nfrom milc import cli\nfrom kle2xy import KLE2xy\n\nfrom qmk.converter import kle2qmk\nfrom qmk.json_encoders import InfoJSONEncoder\n\n\n@cli.argument('filename', completer=FilesCompleter('.json'), help='The KLE raw txt to convert')\n@cli.argument('-f', '--force', action='store_true', help='Flag to overwrite current info.json')\n@cli.subcommand('Convert a KLE layout to a Configurator JSON', hidden=False if cli.config.user.developer else True)\ndef kle2json(cli):\n    \"\"\"Convert a KLE layout to QMK's layout format.\n    \"\"\"  # If filename is a path\n    if cli.args.filename.startswith(\"/\") or cli.args.filename.startswith(\"./\"):\n        file_path = Path(cli.args.filename)\n    # Otherwise assume it is a file name\n    else:\n        file_path = Path(os.environ['ORIG_CWD'], cli.args.filename)\n    # Check for valid file_path for more graceful failure\n    if not file_path.exists():\n        cli.log.error('File {fg_cyan}%s{style_reset_all} was not found.', file_path)\n        return False\n    out_path = file_path.parent\n    raw_code = file_path.read_text(encoding='utf-8')\n    # Check if info.json exists, allow overwrite with force\n    if Path(out_path, \"info.json\").exists() and not cli.args.force:\n        cli.log.error('File {fg_cyan}%s/info.json{style_reset_all} already exists, use -f or --force to overwrite.', out_path)\n        return False\n    try:\n        # Convert KLE raw to x/y coordinates (using kle2xy package from skullydazed)\n        kle = KLE2xy(raw_code)\n    except Exception as e:\n        cli.log.error('Could not parse KLE raw data: %s', raw_code)\n        cli.log.exception(e)\n        return False\n    keyboard = {\n        'keyboard_name': kle.name,\n        'url': '',\n        'maintainer': 'qmk',\n        'layouts': {\n            'LAYOUT': {\n                'layout': kle2qmk(kle)\n            }\n        },\n    }\n\n    # Write our info.json\n    keyboard = json.dumps(keyboard, indent=4, separators=(', ', ': '), sort_keys=False, cls=InfoJSONEncoder)\n    info_json_file = out_path / 'info.json'\n\n    info_json_file.write_text(keyboard)\n    cli.log.info('Wrote out {fg_cyan}%s/info.json', out_path)\n"
  },
  {
    "path": "lib/python/qmk/cli/license_check.py",
    "content": "# Copyright 2023 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\nimport re\nfrom milc import cli\nfrom qmk.constants import LICENSE_TEXTS\nfrom qmk.path import normpath\n\nL_PAREN = re.compile(r'\\(\\[\\{\\<')\nR_PAREN = re.compile(r'\\)\\]\\}\\>')\nPUNCTUATION = re.compile(r'[\\.,;:]+')\nTRASH_PREFIX = re.compile(r'^(\\s|/|\\*|#)+')\nTRASH_SUFFIX = re.compile(r'(\\s|/|\\*|#|\\\\)+$')\nSPACE = re.compile(r'\\s+')\nSUFFIXES = ['.c', '.h', '.cpp', '.cxx', '.hpp', '.hxx']\n\n\ndef _simplify_text(input):\n    lines = input.lower().split('\\n')\n    lines = [PUNCTUATION.sub('', line) for line in lines]\n    lines = [TRASH_PREFIX.sub('', line) for line in lines]\n    lines = [TRASH_SUFFIX.sub('', line) for line in lines]\n    lines = [SPACE.sub(' ', line) for line in lines]\n    lines = [L_PAREN.sub('(', line) for line in lines]\n    lines = [R_PAREN.sub(')', line) for line in lines]\n    lines = [line.strip() for line in lines]\n    lines = [line for line in lines if line is not None and line != '']\n    return ' '.join(lines)\n\n\ndef _preformat_license_texts():\n    # Pre-format all the licenses\n    for _, long_licenses in LICENSE_TEXTS:\n        for i in range(len(long_licenses)):\n            long_licenses[i] = _simplify_text(long_licenses[i])\n\n\ndef _determine_suffix_condition(extensions):\n    def _default_suffix_condition(s):\n        return s in SUFFIXES\n\n    conditional = _default_suffix_condition\n\n    if extensions is not None and len(extensions) > 0:\n        suffixes = [f'.{s}' if not s.startswith('.') else s for s in extensions]\n\n        def _specific_suffix_condition(s):\n            return s in suffixes\n\n        conditional = _specific_suffix_condition\n\n    return conditional\n\n\ndef _determine_file_list(inputs, conditional):\n    check_list = set()\n    for filename in inputs:\n        if filename.is_dir():\n            for file in sorted(filename.rglob('*')):\n                if file.is_file() and conditional(file.suffix):\n                    check_list.add(file)\n        elif filename.is_file():\n            if conditional(filename.suffix):\n                check_list.add(filename)\n\n    return list(sorted(check_list))\n\n\ndef _detect_license_from_file_contents(filename, absolute=False, short=False):\n    data = filename.read_text(encoding='utf-8', errors='ignore')\n    filename_out = str(filename.absolute()) if absolute else str(filename)\n\n    if 'SPDX-License-Identifier:' in data:\n        res = data.split('SPDX-License-Identifier:')\n        license = re.split(r'\\s|//|\\*', res[1].strip())[0].strip()\n        found = False\n        for short_license, _ in LICENSE_TEXTS:\n            if license.lower() == short_license.lower():\n                license = short_license\n                found = True\n                break\n\n        if not found:\n            if short:\n                print(f'{filename_out} UNKNOWN')\n            else:\n                cli.log.error(f'{{fg_cyan}}{filename_out}{{fg_reset}} -- unknown license, or no license detected!')\n            return False\n\n        if short:\n            print(f'{filename_out} {license}')\n        else:\n            cli.log.info(f'{{fg_cyan}}{filename_out}{{fg_reset}} -- license detected: {license} (SPDX License Identifier)')\n        return True\n\n    else:\n        simple_text = _simplify_text(data)\n        for short_license, long_licenses in LICENSE_TEXTS:\n            for long_license in long_licenses:\n                if long_license in simple_text:\n                    if short:\n                        print(f'{filename_out} {short_license}')\n                    else:\n                        cli.log.info(f'{{fg_cyan}}{filename_out}{{fg_reset}} -- license detected: {short_license} (Full text)')\n                    return True\n\n        if short:\n            print(f'{filename_out} UNKNOWN')\n        else:\n            cli.log.error(f'{{fg_cyan}}{filename_out}{{fg_reset}} -- unknown license, or no license detected!')\n\n    return False\n\n\n@cli.argument('inputs', nargs='*', arg_only=True, type=normpath, help='List of input files or directories.')\n@cli.argument('-s', '--short', action='store_true', help='Short output.')\n@cli.argument('-a', '--absolute', action='store_true', help='Print absolute paths.')\n@cli.argument('-e', '--extension', arg_only=True, action='append', default=[], help='Override list of extensions. Can be specified multiple times for multiple extensions.')\n@cli.subcommand('File license check.', hidden=False if cli.config.user.developer else True)\ndef license_check(cli):\n    _preformat_license_texts()\n\n    conditional = _determine_suffix_condition(cli.args.extension)\n    check_list = _determine_file_list(cli.args.inputs, conditional)\n\n    failed = False\n    for filename in sorted(check_list):\n        if not _detect_license_from_file_contents(filename, absolute=cli.args.absolute, short=cli.args.short):\n            failed = True\n\n    if failed:\n        return False\n"
  },
  {
    "path": "lib/python/qmk/cli/lint.py",
    "content": "\"\"\"Command to look over a keyboard/keymap and check for common mistakes.\n\"\"\"\nfrom dotty_dict import dotty\nfrom pathlib import Path\n\nfrom milc import cli\n\nfrom qmk.decorators import automagic_keyboard, automagic_keymap\nfrom qmk.info import info_json\nfrom qmk.keyboard import keyboard_completer, keyboard_folder_or_all, is_all_keyboards, list_keyboards\nfrom qmk.keymap import locate_keymap, list_keymaps\nfrom qmk.path import keyboard\nfrom qmk.git import git_get_ignored_files\nfrom qmk.c_parse import c_source_files, preprocess_c_file\nfrom qmk.json_schema import json_load\n\nCHIBIOS_CONF_CHECKS = ['chconf.h', 'halconf.h', 'mcuconf.h', 'board.h']\nINVALID_KB_FEATURES = set(['encoder_map', 'dip_switch_map', 'combo', 'tap_dance', 'via'])\nINVALID_KM_NAMES = ['via', 'vial']\n\n\ndef _list_defaultish_keymaps(kb):\n    \"\"\"Return default like keymaps for a given keyboard\n    \"\"\"\n    defaultish = ['ansi', 'iso']\n\n    # This is only here to flag it as \"testable\", so it doesn't fly under the radar during PR\n    defaultish.extend(INVALID_KM_NAMES)\n\n    keymaps = set()\n    for x in list_keymaps(kb, include_userspace=False):\n        if x in defaultish or x.startswith('default'):\n            keymaps.add(x)\n\n    return keymaps\n\n\ndef _get_readme_files(kb, km=None):\n    \"\"\"Return potential keyboard/keymap readme files\n    \"\"\"\n    search_path = locate_keymap(kb, km).parent if km else keyboard(kb)\n\n    readme_files = []\n\n    if not km:\n        current_path = Path(search_path.parts[0])\n        for path_part in search_path.parts[1:]:\n            current_path = current_path / path_part\n            readme_files.extend(current_path.glob('*readme.md'))\n\n    for file in search_path.glob(\"**/*readme.md\"):\n        # Ignore keymaps when only globing keyboard files\n        if not km and 'keymaps' in file.parts:\n            continue\n        readme_files.append(file)\n\n    return set(readme_files)\n\n\ndef _get_build_files(kb, km=None):\n    \"\"\"Return potential keyboard/keymap build files\n    \"\"\"\n    search_path = locate_keymap(kb, km).parent if km else keyboard(kb)\n\n    build_files = []\n\n    if not km:\n        current_path = Path()\n        for path_part in search_path.parts:\n            current_path = current_path / path_part\n            build_files.extend(current_path.glob('*rules.mk'))\n\n    for file in search_path.glob(\"**/*rules.mk\"):\n        # Ignore keymaps when only globing keyboard files\n        if not km and 'keymaps' in file.parts:\n            continue\n        build_files.append(file)\n\n    return set(build_files)\n\n\ndef _get_code_files(kb, km=None):\n    \"\"\"Return potential keyboard/keymap code files\n    \"\"\"\n    search_path = locate_keymap(kb, km).parent if km else keyboard(kb)\n\n    code_files = []\n\n    if not km:\n        current_path = Path()\n        for path_part in search_path.parts:\n            current_path = current_path / path_part\n            code_files.extend(current_path.glob('*.h'))\n            code_files.extend(current_path.glob('*.c'))\n\n    for file in c_source_files([search_path]):\n        # Ignore keymaps when only globing keyboard files\n        if not km and 'keymaps' in file.parts:\n            continue\n        code_files.append(file)\n\n    return code_files\n\n\ndef _is_invalid_readme(file):\n    \"\"\"Check if file contains any unfilled content\n    \"\"\"\n    tokens = [\n        '%KEYBOARD%',\n        '%REAL_NAME%',\n        '%USER_NAME%',\n        'image replace me!',\n        'A short description of the keyboard/project',\n        'The PCBs, controllers supported',\n        'Links to where you can find this hardware',\n    ]\n\n    for line in file.read_text(encoding='utf-8').split(\"\\n\"):\n        if any(token in line for token in tokens):\n            return True\n    return False\n\n\ndef _is_empty_rules(file):\n    \"\"\"Check if file contains any useful content\n    \"\"\"\n    for line in file.read_text(encoding='utf-8').split(\"\\n\"):\n        if len(line) > 0 and not line.isspace() and not line.startswith('#'):\n            return False\n    return True\n\n\ndef _is_empty_include(file):\n    \"\"\"Check if file contains any useful content\n    \"\"\"\n    for line in preprocess_c_file(file).split(\"\\n\"):\n        if len(line) > 0 and not line.isspace() and not line.startswith('#pragma once'):\n            return False\n    return True\n\n\ndef _has_license(file):\n    \"\"\"Check file has a license header\n    \"\"\"\n    # Crude assumption that first line of license header is a comment\n    fline = open(file).readline().rstrip()\n    return fline.startswith((\"/*\", \"//\"))\n\n\ndef _handle_json_errors(kb, info):\n    \"\"\"Convert any json errors into lint errors\n    \"\"\"\n    ok = True\n    # Check for errors in the json\n    if info['parse_errors']:\n        ok = False\n        cli.log.error(f'{kb}: Errors found when generating info.json.')\n\n    if cli.config.lint.strict and info['parse_warnings']:\n        ok = False\n        cli.log.error(f'{kb}: Warnings found when generating info.json (Strict mode enabled.)')\n    return ok\n\n\ndef _handle_invalid_features(kb, info):\n    \"\"\"Check for features that should never be enabled at the keyboard level\n    \"\"\"\n    ok = True\n    features = set(info.get('features', []))\n    for found in features & INVALID_KB_FEATURES:\n        ok = False\n        cli.log.error(f'{kb}: Invalid keyboard level feature detected - {found}')\n    return ok\n\n\ndef _handle_invalid_config(kb, info):\n    \"\"\"Check for invalid keyboard level config\n    \"\"\"\n    if info.get('url') == \"\":\n        cli.log.warning(f'{kb}: Invalid keyboard level config detected - Optional field \"url\" should not be empty.')\n    return True\n\n\ndef _chibios_conf_includenext_check(target):\n    \"\"\"Check the ChibiOS conf.h for the correct inclusion of the next conf.h\n    \"\"\"\n    for i, line in enumerate(target.open()):\n        if f'#include_next \"{target.name}\"' in line:\n            return f'Found `#include_next \"{target.name}\"` on line {i} of {target}, should be `#include_next <{target.name}>` (use angle brackets, not quotes)'\n    return None\n\n\ndef _rules_mk_assignment_only(rules_mk):\n    \"\"\"Check the keyboard-level rules.mk to ensure it only has assignments.\n    \"\"\"\n    errors = []\n    continuation = None\n    for i, line in enumerate(rules_mk.open()):\n        line = line.strip()\n\n        if '#' in line:\n            line = line[:line.index('#')]\n\n        if continuation:\n            line = continuation + line\n            continuation = None\n\n        if line:\n            if line[-1] == '\\\\':\n                continuation = line[:-1]\n                continue\n\n            if line and '=' not in line:\n                errors.append(f'Non-assignment code on line +{i} {rules_mk}: {line}')\n\n    return errors\n\n\ndef _handle_duplicating_code_defaults(kb, info):\n    def _collect_dotted_output(kb_info_json, prefix=''):\n        \"\"\"Print the info.json in a plain text format with dot-joined keys.\n        \"\"\"\n        for key in sorted(kb_info_json):\n            new_prefix = f'{prefix}.{key}' if prefix else key\n\n            if isinstance(kb_info_json[key], dict):\n                yield from _collect_dotted_output(kb_info_json[key], new_prefix)\n            elif isinstance(kb_info_json[key], list):\n                # TODO: handle non primitives?\n                yield (new_prefix, kb_info_json[key])\n            else:\n                yield (new_prefix, kb_info_json[key])\n\n    defaults_map = json_load(Path('data/mappings/info_defaults.hjson'))\n    dotty_info = dotty(info)\n\n    for key, v_default in _collect_dotted_output(defaults_map):\n        v_info = dotty_info.get(key)\n        if v_default == v_info:\n            cli.log.warning(f'{kb}: Option \"{key}\" duplicates default value of \"{v_default}\"')\n\n    return True\n\n\ndef keymap_check(kb, km):\n    \"\"\"Perform the keymap level checks.\n    \"\"\"\n    ok = True\n    keymap_path = locate_keymap(kb, km)\n\n    if not keymap_path:\n        ok = False\n        cli.log.error(\"%s: Can't find %s keymap.\", kb, km)\n        return ok\n\n    if km in INVALID_KM_NAMES:\n        ok = False\n        cli.log.error(\"%s: The keymap %s should not exist!\", kb, km)\n        return ok\n\n    # Additional checks\n    invalid_files = git_get_ignored_files(keymap_path.parent.as_posix())\n    for file in invalid_files:\n        cli.log.error(f'{kb}/{km}: The file \"{file}\" should not exist!')\n        ok = False\n\n    for file in _get_code_files(kb, km):\n        if not _has_license(file):\n            cli.log.error(f'{kb}/{km}: The file \"{file}\" does not have a license header!')\n            ok = False\n\n        if file.name in CHIBIOS_CONF_CHECKS:\n            check_error = _chibios_conf_includenext_check(file)\n            if check_error is not None:\n                cli.log.error(f'{kb}/{km}: {check_error}')\n                ok = False\n\n    return ok\n\n\ndef keyboard_check(kb):  # noqa C901\n    \"\"\"Perform the keyboard level checks.\n    \"\"\"\n    ok = True\n    kb_info = info_json(kb)\n\n    if not _handle_json_errors(kb, kb_info):\n        ok = False\n\n    # Additional checks\n    if not _handle_invalid_features(kb, kb_info):\n        ok = False\n\n    if not _handle_invalid_config(kb, kb_info):\n        ok = False\n\n    if not _handle_duplicating_code_defaults(kb, kb_info):\n        ok = False\n\n    invalid_files = git_get_ignored_files(f'keyboards/{kb}/')\n    for file in invalid_files:\n        if 'keymap' in file:\n            continue\n        cli.log.error(f'{kb}: The file \"{file}\" should not exist!')\n        ok = False\n\n    if not _get_readme_files(kb):\n        cli.log.error(f'{kb}: Is missing a readme.md file!')\n        ok = False\n\n    for file in _get_readme_files(kb):\n        if _is_invalid_readme(file):\n            cli.log.error(f'{kb}: The file \"{file}\" still contains template tokens!')\n            ok = False\n\n    for file in _get_build_files(kb):\n        if _is_empty_rules(file):\n            cli.log.error(f'{kb}: The file \"{file}\" is effectively empty and should be removed!')\n            ok = False\n\n        if file.suffix in ['rules.mk']:\n            rules_mk_assignment_errors = _rules_mk_assignment_only(file)\n            if rules_mk_assignment_errors:\n                ok = False\n                cli.log.error('%s: Non-assignment code found in rules.mk. Move it to post_rules.mk instead.', kb)\n                for assignment_error in rules_mk_assignment_errors:\n                    cli.log.error(assignment_error)\n\n    for file in _get_code_files(kb):\n        if not _has_license(file):\n            cli.log.error(f'{kb}: The file \"{file}\" does not have a license header!')\n            ok = False\n\n        if file.name in ['config.h']:\n            if _is_empty_include(file):\n                cli.log.error(f'{kb}: The file \"{file}\" is effectively empty and should be removed!')\n                ok = False\n\n        if file.name in CHIBIOS_CONF_CHECKS:\n            check_error = _chibios_conf_includenext_check(file)\n            if check_error is not None:\n                cli.log.error(f'{kb}: {check_error}')\n                ok = False\n\n    return ok\n\n\n@cli.argument('--strict', action='store_true', help='Treat warnings as errors')\n@cli.argument('-kb', '--keyboard', action='append', type=keyboard_folder_or_all, completer=keyboard_completer, help='Keyboard to check. May be passed multiple times.')\n@cli.argument('-km', '--keymap', help='The keymap to check')\n@cli.subcommand('Check keyboard and keymap for common mistakes.')\n@automagic_keyboard\n@automagic_keymap\ndef lint(cli):\n    \"\"\"Check keyboard and keymap for common mistakes.\n    \"\"\"\n    # Determine our keyboard list\n    if not cli.config.lint.keyboard:\n        cli.log.error('Missing required arguments: --keyboard')\n        cli.print_help()\n        return False\n\n    if isinstance(cli.config.lint.keyboard, str):\n        # if provided via config - string not array\n        keyboard_list = [cli.config.lint.keyboard]\n    elif any(is_all_keyboards(kb) for kb in cli.args.keyboard):\n        keyboard_list = list_keyboards()\n    else:\n        keyboard_list = list(set(cli.config.lint.keyboard))\n\n    failed = []\n\n    # Lint each keyboard\n    for kb in keyboard_list:\n        # Determine keymaps to also check\n        if cli.args.keymap == 'all':\n            keymaps = list_keymaps(kb)\n        elif cli.config.lint.keymap:\n            keymaps = {cli.config.lint.keymap}\n        else:\n            keymaps = _list_defaultish_keymaps(kb)\n            # Ensure that at least a 'default' keymap always exists\n            keymaps.add('default')\n\n        ok = True\n\n        # keyboard level checks\n        if not keyboard_check(kb):\n            ok = False\n\n        # Keymap specific checks\n        for keymap in keymaps:\n            if not keymap_check(kb, keymap):\n                ok = False\n\n        # Report status\n        if not ok:\n            failed.append(kb)\n\n    # Check and report the overall status\n    if failed:\n        cli.log.error('Lint check failed for: %s', ', '.join(failed))\n        return False\n\n    cli.log.info('Lint check passed!')\n    return True\n"
  },
  {
    "path": "lib/python/qmk/cli/list/__init__.py",
    "content": ""
  },
  {
    "path": "lib/python/qmk/cli/list/keyboards.py",
    "content": "\"\"\"List the keyboards currently defined within QMK\n\"\"\"\nfrom milc import cli\n\nimport qmk.keyboard\n\n\n@cli.subcommand(\"List the keyboards currently defined within QMK\")\ndef list_keyboards(cli):\n    \"\"\"List the keyboards currently defined within QMK\n    \"\"\"\n    for keyboard_name in qmk.keyboard.list_keyboards():\n        print(keyboard_name)\n"
  },
  {
    "path": "lib/python/qmk/cli/list/keymaps.py",
    "content": "\"\"\"List the keymaps for a specific keyboard\n\"\"\"\nfrom milc import cli\n\nimport qmk.keymap\nfrom qmk.decorators import automagic_keyboard\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\n\n\n@cli.argument(\"-kb\", \"--keyboard\", type=keyboard_folder, completer=keyboard_completer, help=\"Specify keyboard name. Example: 1upkeyboards/1up60hse\")\n@cli.subcommand(\"List the keymaps for a specific keyboard\")\n@automagic_keyboard\ndef list_keymaps(cli):\n    \"\"\"List the keymaps for a specific keyboard\n    \"\"\"\n    if not cli.config.list_keymaps.keyboard:\n        cli.log.error('Missing required arguments: --keyboard')\n        cli.subcommands['list-keymaps'].print_help()\n        return False\n\n    for name in qmk.keymap.list_keymaps(cli.config.list_keymaps.keyboard):\n        print(name)\n"
  },
  {
    "path": "lib/python/qmk/cli/list/layouts.py",
    "content": "\"\"\"List the keymaps for a specific keyboard\n\"\"\"\nfrom milc import cli\n\nfrom qmk.decorators import automagic_keyboard\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.info import info_json\n\n\n@cli.argument(\"-kb\", \"--keyboard\", type=keyboard_folder, completer=keyboard_completer, help=\"Specify keyboard name. Example: monarch\")\n@cli.subcommand(\"List the layouts for a specific keyboard\")\n@automagic_keyboard\ndef list_layouts(cli):\n    \"\"\"List the layouts for a specific keyboard\n    \"\"\"\n    if not cli.config.list_layouts.keyboard:\n        cli.log.error('Missing required arguments: --keyboard')\n        cli.subcommands['list-layouts'].print_help()\n        return False\n\n    info_data = info_json(cli.config.list_layouts.keyboard)\n    for name in sorted(info_data.get('community_layouts', [])):\n        print(name)\n"
  },
  {
    "path": "lib/python/qmk/cli/mass_compile.py",
    "content": "\"\"\"Compile all keyboards.\n\nThis will compile everything in parallel, for testing purposes.\n\"\"\"\nimport os\nfrom typing import List\nfrom pathlib import Path\nfrom subprocess import DEVNULL\nfrom milc import cli\nimport shlex\n\nfrom qmk.constants import QMK_FIRMWARE\nfrom qmk.commands import find_make, get_make_parallel_args, build_environment\nfrom qmk.search import search_keymap_targets, search_make_targets\nfrom qmk.build_targets import BuildTarget, JsonKeymapBuildTarget\nfrom qmk.util import maybe_exit_config\n\n\ndef mass_compile_targets(targets: List[BuildTarget], clean: bool, dry_run: bool, no_temp: bool, parallel: int, print_failures: bool, **env):\n    if len(targets) == 0:\n        return\n\n    os.environ.setdefault('SKIP_SCHEMA_VALIDATION', '1')\n\n    make_cmd = find_make()\n    builddir = Path(QMK_FIRMWARE) / '.build'\n    makefile = builddir / 'parallel_kb_builds.mk'\n\n    if dry_run:\n        cli.log.info('Compilation targets:')\n        for target in sorted(targets, key=lambda t: (t.keyboard, t.keymap)):\n            extra_args = ' '.join([f\"-e {shlex.quote(f'{k}={v}')}\" for k, v in target.extra_args.items()])\n            cli.log.info(f\"{{fg_cyan}}qmk compile -kb {target.keyboard} -km {target.keymap} {extra_args}{{fg_reset}}\")\n    else:\n        if clean:\n            cli.run([make_cmd, 'clean'], capture_output=False, stdin=DEVNULL)\n\n        builddir.mkdir(parents=True, exist_ok=True)\n        with open(makefile, \"w\") as f:\n            # yapf: disable\n            f.write(\n                f\"\"\"\\\n# This file is auto-generated by qmk mass-compile\n# Do not edit this file directly.\nall: print_failures\n.PHONY: all_targets print_failures\nprint_failures: all_targets\n\"\"\"# noqa\n            )\n            if print_failures:\n                f.write(\n                    f\"\"\"\\\n\t@for f in $$(ls .build/failed.log.{os.getpid()}.* 2>/dev/null | sort); do \\\\\n\t\techo; \\\\\n\t\techo \"======================================================================================\"; \\\\\n\t\techo \"Failed build log: $$f\"; \\\\\n\t\techo \"------------------------------------------------------\"; \\\\\n\t\tcat $$f; \\\\\n\t\techo \"------------------------------------------------------\"; \\\\\n\tdone\n\"\"\"# noqa\n                )\n            # yapf: enable\n            for target in sorted(targets, key=lambda t: (t.keyboard, t.keymap)):\n                keyboard_name = target.keyboard\n                keymap_name = target.keymap\n                keyboard_safe = keyboard_name.replace('/', '_')\n                target_filename = target.target_name(**env)\n                target.configure(parallel=1)  # We ignore parallelism on a per-build basis as we defer to the parent make invocation\n                target.prepare_build(**env)  # If we've got json targets, allow them to write out any extra info to .build before we kick off `make`\n                command = target.compile_command(**env)\n                command[0] = '+@$(MAKE)'  # Override the make so that we can use jobserver to handle parallelism\n                extra_args = '_'.join([f\"{k}_{v}\" for k, v in target.extra_args.items()])\n                build_log = f\"{QMK_FIRMWARE}/.build/build.log.{os.getpid()}.{keyboard_safe}.{keymap_name}\"\n                failed_log = f\"{QMK_FIRMWARE}/.build/failed.log.{os.getpid()}.{keyboard_safe}.{keymap_name}\"\n                target_suffix = ''\n                if len(extra_args) > 0:\n                    build_log += f\".{extra_args}\"\n                    failed_log += f\".{extra_args}\"\n                    target_suffix = f\"_{extra_args}\"\n                # yapf: disable\n                f.write(\n                    f\"\"\"\\\n.PHONY: {target_filename}{target_suffix}_binary\nall_targets: {target_filename}{target_suffix}_binary\n{target_filename}{target_suffix}_binary:\n\t@rm -f \"{build_log}\" || true\n\t@echo \"Compiling QMK Firmware for target: '{keyboard_name}:{keymap_name}'...\" >>\"{build_log}\"\n\t{' '.join(command)} \\\\\n\t\t>>\"{build_log}\" 2>&1 \\\\\n\t\t|| cp \"{build_log}\" \"{failed_log}\"\n\t@{{ grep '\\\\[ERRORS\\\\]' \"{build_log}\" >/dev/null 2>&1 && printf \"Build %-64s \\\\e[1;31m[ERRORS]\\\\e[0m\\\\n\" \"{keyboard_name}:{keymap_name}\" ; }} \\\\\n\t\t|| {{ grep '\\\\[WARNINGS\\\\]' \"{build_log}\" >/dev/null 2>&1 && printf \"Build %-64s \\\\e[1;33m[WARNINGS]\\\\e[0m\\\\n\" \"{keyboard_name}:{keymap_name}\" ; }} \\\\\n\t\t|| printf \"Build %-64s \\\\e[1;32m[OK]\\\\e[0m\\\\n\" \"{keyboard_name}:{keymap_name}\"\n\t@rm -f \"{build_log}\" || true\n\"\"\"# noqa\n                )\n                # yapf: enable\n\n                if no_temp:\n                    # yapf: disable\n                    f.write(\n                        f\"\"\"\\\n\t@rm -rf \"{QMK_FIRMWARE}/.build/{target_filename}.elf\" 2>/dev/null || true\n\t@rm -rf \"{QMK_FIRMWARE}/.build/{target_filename}.map\" 2>/dev/null || true\n\t@rm -rf \"{QMK_FIRMWARE}/.build/obj_{target_filename}\" || true\n\"\"\"# noqa\n                    )\n                    # yapf: enable\n                f.write('\\n')\n\n        cli.run([find_make(), *get_make_parallel_args(parallel), '-f', makefile.as_posix(), 'all'], capture_output=False, stdin=DEVNULL)\n\n        # Check for failures\n        failures = [f for f in builddir.glob(f'failed.log.{os.getpid()}.*')]\n        if len(failures) > 0:\n            return False\n\n\n@cli.argument('builds', nargs='*', arg_only=True, help=\"List of builds in form <keyboard>:<keymap> to compile in parallel. Specifying this overrides all other target search options.\")\n@cli.argument('-t', '--no-temp', arg_only=True, action='store_true', help=\"Remove temporary files during build.\")\n@cli.argument('-j', '--parallel', type=int, default=1, help=\"Set the number of parallel make jobs; 0 means unlimited.\")\n@cli.argument('-c', '--clean', arg_only=True, action='store_true', help=\"Remove object files before compiling.\")\n@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help=\"Don't actually build, just show the commands to be run.\")\n@cli.argument('-p', '--print-failures', arg_only=True, action='store_true', help=\"Print failed builds.\")\n@cli.argument(\n    '-f',\n    '--filter',\n    arg_only=True,\n    action='append',\n    default=[],\n    help=  # noqa: `format-python` and `pytest` don't agree here.\n    \"Filter the list of keyboards based on the supplied value in rules.mk. Matches info.json structure, and accepts the formats 'features.rgblight=true' or 'exists(matrix_pins.direct)'. May be passed multiple times, all filters need to match. Value may include wildcards such as '*' and '?'.\"  # noqa: `format-python` and `pytest` don't agree here.\n)\n@cli.argument('-km', '--keymap', type=str, default='default', help=\"The keymap name to build. Default is 'default'.\")\n@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help=\"Set a variable to be passed to make. May be passed multiple times.\")\n@cli.subcommand('Compile QMK Firmware for all keyboards.', hidden=False if cli.config.user.developer else True)\ndef mass_compile(cli):\n    \"\"\"Compile QMK Firmware against all keyboards.\n    \"\"\"\n    maybe_exit_config(should_exit=False, should_reraise=True)\n\n    if len(cli.args.builds) > 0:\n        json_like_targets = list([Path(p) for p in filter(lambda e: Path(e).exists() and Path(e).suffix == '.json', cli.args.builds)])\n        make_like_targets = list(filter(lambda e: Path(e) not in json_like_targets, cli.args.builds))\n        targets = search_make_targets(make_like_targets)\n        targets.extend([JsonKeymapBuildTarget(e) for e in json_like_targets])\n    else:\n        targets = search_keymap_targets([('all', cli.config.mass_compile.keymap)], cli.args.filter)\n\n    return mass_compile_targets(targets, cli.args.clean, cli.args.dry_run, cli.args.no_temp, cli.config.mass_compile.parallel, cli.args.print_failures, **build_environment(cli.args.env))\n"
  },
  {
    "path": "lib/python/qmk/cli/migrate.py",
    "content": "\"\"\"Migrate keyboard configuration to \"Data Driven\"\n\"\"\"\nimport json\nfrom pathlib import Path\nfrom dotty_dict import dotty\n\nfrom milc import cli\n\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.info import info_json, find_info_json\nfrom qmk.json_encoders import InfoJSONEncoder\nfrom qmk.json_schema import json_load\n\n\ndef _candidate_files(keyboard):\n    kb_dir = Path(keyboard)\n\n    cur_dir = Path('keyboards')\n    files = []\n    for dir in kb_dir.parts:\n        cur_dir = cur_dir / dir\n        files.append(cur_dir / 'config.h')\n        files.append(cur_dir / 'rules.mk')\n\n    return [file for file in files if file.exists()]\n\n\n@cli.argument('-f', '--filter', arg_only=True, action='append', default=[], help=\"Filter the performed migrations based on the supplied value. Supported format is 'KEY' located from 'data/mappings'. May be passed multiple times.\")\n@cli.argument('-kb', '--keyboard', arg_only=True, type=keyboard_folder, completer=keyboard_completer, required=True, help='The keyboard\\'s name')\n@cli.subcommand('Migrate keyboard config to \"Data Driven\".', hidden=True)\ndef migrate(cli):\n    \"\"\"Migrate keyboard configuration to \"Data Driven\"\n    \"\"\"\n    # Merge mappings as we do not care to where \"KEY\" is found just that its removed\n    info_config_map = json_load(Path('data/mappings/info_config.hjson'))\n    info_rules_map = json_load(Path('data/mappings/info_rules.hjson'))\n    info_map = {**info_config_map, **info_rules_map}\n\n    # Parse target info.json which will receive updates\n    target_info = Path(find_info_json(cli.args.keyboard)[0])\n    info_data = dotty(json_load(target_info))\n\n    # Already parsed used for updates\n    kb_info_json = dotty(info_json(cli.args.keyboard))\n\n    # List of candidate files\n    files = _candidate_files(cli.args.keyboard)\n\n    # Filter down keys if requested\n    keys = list(filter(lambda key: info_map[key].get(\"to_json\", True), info_map.keys()))\n    if cli.args.filter:\n        keys = list(set(keys) & set(cli.args.filter))\n        rejected = set(cli.args.filter) - set(keys)\n        for key in rejected:\n            cli.log.info(f'{{fg_yellow}}Skipping {key} as migration not possible...')\n\n    cli.log.info(f'{{fg_green}}Migrating keyboard {{fg_cyan}}{cli.args.keyboard}{{fg_green}}.{{fg_reset}}')\n\n    # Start migration\n    for file in files:\n        cli.log.info(f'  Migrating file {file}')\n        file_contents = file.read_text(encoding='utf-8').split('\\n')\n        for key in keys:\n            for num, line in enumerate(file_contents):\n                if line.startswith(f'{key} =') or line.startswith(f'#define {key} '):\n                    cli.log.info(f'    Migrating {key}...')\n\n                    while line.rstrip().endswith('\\\\'):\n                        file_contents.pop(num)\n                        line = file_contents[num]\n                    file_contents.pop(num)\n\n                    update_key = info_map[key][\"info_key\"]\n                    if update_key in kb_info_json:\n                        info_data[update_key] = kb_info_json[update_key]\n\n        file.write_text('\\n'.join(file_contents), encoding='utf-8')\n\n    # Finally write out updated info.json\n    cli.log.info(f'  Updating {target_info}')\n    target_info.write_text(json.dumps(info_data.to_dict(), cls=InfoJSONEncoder, sort_keys=True))\n\n    cli.log.info(f'{{fg_green}}Migration of keyboard {{fg_cyan}}{cli.args.keyboard}{{fg_green}} complete!{{fg_reset}}')\n    cli.log.info(f\"Verify build with {{fg_yellow}}qmk compile -kb {cli.args.keyboard} -km default{{fg_reset}}.\")\n"
  },
  {
    "path": "lib/python/qmk/cli/new/__init__.py",
    "content": ""
  },
  {
    "path": "lib/python/qmk/cli/new/keyboard.py",
    "content": "\"\"\"This script automates the creation of new keyboard directories using a starter template.\n\"\"\"\nimport re\nimport json\nimport shutil\nfrom datetime import date\nfrom pathlib import Path\nfrom dotty_dict import dotty\n\nfrom milc import cli\nfrom milc.questions import choice, question, yesno\n\nfrom qmk.git import git_get_username\nfrom qmk.json_schema import load_jsonschema\nfrom qmk.path import keyboard\nfrom qmk.json_encoders import InfoJSONEncoder\nfrom qmk.json_schema import deep_update\nfrom qmk.constants import MCU2BOOTLOADER, QMK_FIRMWARE\n\nCOMMUNITY = Path('layouts/default/')\nTEMPLATE = Path('data/templates/keyboard/')\n\n# defaults\nschema = dotty(load_jsonschema('keyboard'))\nmcu_types = sorted(schema[\"properties.processor.enum\"], key=str.casefold)\ndev_boards = sorted(schema[\"properties.development_board.enum\"], key=str.casefold)\navailable_layouts = sorted([x.name for x in COMMUNITY.iterdir() if x.is_dir()])\n\n\ndef mcu_type(mcu):\n    \"\"\"Callable for argparse validation.\n    \"\"\"\n    if mcu not in (dev_boards + mcu_types):\n        raise ValueError\n    return mcu\n\n\ndef layout_type(layout):\n    \"\"\"Callable for argparse validation.\n    \"\"\"\n    if layout not in available_layouts:\n        raise ValueError\n    return layout\n\n\ndef keyboard_name(name):\n    \"\"\"Callable for argparse validation.\n    \"\"\"\n    if not validate_keyboard_name(name):\n        raise ValueError\n    return name\n\n\ndef validate_keyboard_name(name):\n    \"\"\"Returns True if the given keyboard name contains only lowercase a-z, 0-9 and underscore characters.\n    \"\"\"\n    regex = re.compile(r'^[a-z0-9][a-z0-9/_]+$')\n    return bool(regex.match(name))\n\n\ndef select_default_bootloader(mcu):\n    \"\"\"Provide sane defaults for bootloader\n    \"\"\"\n    return MCU2BOOTLOADER.get(mcu, \"custom\")\n\n\ndef replace_placeholders(src, dest, tokens):\n    \"\"\"Replaces the given placeholders in each template file.\n    \"\"\"\n    content = src.read_text()\n    for key, value in tokens.items():\n        content = content.replace(f'%{key}%', value)\n\n    dest.write_text(content)\n\n\ndef replace_string(src, token, value):\n    src.write_text(src.read_text().replace(token, value))\n\n\ndef augment_community_info(config, src, dest):\n    \"\"\"Splice in any additional data into info.json\n    \"\"\"\n    info = json.loads(src.read_text())\n    template = json.loads(dest.read_text())\n\n    # merge community with template\n    deep_update(info, template)\n    deep_update(info, config)\n\n    # avoid assumptions on macro name by using the first available\n    first_layout = next(iter(info[\"layouts\"].values()))[\"layout\"]\n\n    # guess at width and height now its optional\n    width, height = (0, 0)\n    for item in first_layout:\n        width = max(width, int(item[\"x\"]) + 1)\n        height = max(height, int(item[\"y\"]) + 1)\n\n    info[\"matrix_pins\"] = {\n        \"cols\": [\"C2\"] * width,\n        \"rows\": [\"D1\"] * height,\n    }\n\n    # assume a 1:1 mapping on matrix to electrical\n    for item in first_layout:\n        item[\"matrix\"] = [int(item[\"y\"]), int(item[\"x\"])]\n\n    # finally write out the updated json\n    dest.write_text(json.dumps(info, cls=InfoJSONEncoder, sort_keys=True))\n\n\ndef _question(*args, **kwargs):\n    \"\"\"Ugly workaround until 'milc' learns to display a repromt msg\n    \"\"\"\n    # TODO: Remove this once milc.questions.question handles reprompt messages\n\n    reprompt = kwargs[\"reprompt\"]\n    del kwargs[\"reprompt\"]\n    validate = kwargs[\"validate\"]\n    del kwargs[\"validate\"]\n\n    prompt = args[0]\n    ret = None\n    while not ret:\n        ret = question(prompt, **kwargs)\n        if not validate(ret):\n            ret = None\n            prompt = reprompt\n\n    return ret\n\n\ndef prompt_heading_subheading(heading, subheading):\n    cli.log.info(f\"{{fg_yellow}}{heading}{{style_reset_all}}\")\n    cli.log.info(subheading)\n\n\ndef prompt_keyboard():\n    prompt_heading_subheading(\"Name Your Keyboard Project\", \"\"\"For more information, see:\nhttps://docs.qmk.fm/hardware_keyboard_guidelines#naming-your-keyboard-project\"\"\")\n\n    errmsg = 'Keyboard already exists! Please choose a different name:'\n\n    return _question(\"Keyboard Name?\", reprompt=errmsg, validate=lambda x: not keyboard(x).exists())\n\n\ndef prompt_user():\n    prompt_heading_subheading(\"Attribution\", \"Used for maintainer, copyright, etc.\")\n\n    return question(\"Your GitHub Username?\", default=git_get_username())\n\n\ndef prompt_name(def_name):\n    prompt_heading_subheading(\"More Attribution\", \"Used for maintainer, copyright, etc.\")\n\n    return question(\"Your Real Name?\", default=def_name)\n\n\ndef prompt_layout():\n    prompt_heading_subheading(\"Pick Base Layout\", \"\"\"As a starting point, one of the common layouts can be used to\nbootstrap the process\"\"\")\n\n    # avoid overwhelming user - remove some?\n    filtered_layouts = [x for x in available_layouts if not any(xs in x for xs in ['_split', '_blocker', '_tsangan', '_f13'])]\n    filtered_layouts.append(\"none of the above\")\n\n    return choice(\"Default Layout?\", filtered_layouts, default=len(filtered_layouts) - 1)\n\n\ndef prompt_mcu_type():\n    prompt_heading_subheading(\n        \"What Powers Your Project\", \"\"\"Is your board using a separate development board, such as a Pro Micro,\nor is the microcontroller integrated onto the PCB?\n\nFor more information, see:\nhttps://docs.qmk.fm/compatible_microcontrollers\"\"\"\n    )\n\n    return yesno(\"Using a Development Board?\")\n\n\ndef prompt_dev_board():\n    prompt_heading_subheading(\"Select Development Board\", \"\"\"For more information, see:\nhttps://docs.qmk.fm/compatible_microcontrollers\"\"\")\n\n    return choice(\"Development Board?\", dev_boards, default=dev_boards.index(\"promicro\"))\n\n\ndef prompt_mcu():\n    prompt_heading_subheading(\"Select Microcontroller\", \"\"\"For more information, see:\nhttps://docs.qmk.fm/compatible_microcontrollers\"\"\")\n\n    # remove any options strictly used for compatibility\n    filtered_mcu = [x for x in mcu_types if not any(xs in x for xs in ['cortex', 'unknown'])]\n\n    return choice(\"Microcontroller?\", filtered_mcu, default=filtered_mcu.index(\"atmega32u4\"))\n\n\n@cli.argument('-kb', '--keyboard', help='Specify the name for the new keyboard directory', arg_only=True, type=keyboard_name)\n@cli.argument('-l', '--layout', help='Community layout to bootstrap with', arg_only=True, type=layout_type)\n@cli.argument('-t', '--type', help='Specify the keyboard MCU type (or \"development_board\" preset)', arg_only=True, type=mcu_type)\n@cli.argument('-u', '--username', help='Specify your username (default from Git config)', dest='name')\n@cli.argument('-n', '--realname', help='Specify your real name if you want to use that. Defaults to username', arg_only=True)\n@cli.subcommand('Creates a new keyboard directory')\ndef new_keyboard(cli):\n    \"\"\"Creates a new keyboard.\n    \"\"\"\n    cli.log.info('{style_bright}Generating a new QMK keyboard directory{style_normal}')\n    cli.echo('')\n\n    kb_name = cli.args.keyboard if cli.args.keyboard else prompt_keyboard()\n    if not validate_keyboard_name(kb_name):\n        cli.log.error('Keyboard names must contain only {fg_cyan}lowercase a-z{fg_reset}, {fg_cyan}0-9{fg_reset}, and {fg_cyan}_{fg_reset}! Please choose a different name.')\n        return 1\n\n    if keyboard(kb_name).exists():\n        cli.log.error(f'Keyboard {{fg_cyan}}{kb_name}{{fg_reset}} already exists! Please choose a different name.')\n        return 1\n\n    user_name = cli.config.new_keyboard.name if cli.config.new_keyboard.name else prompt_user()\n    real_name = cli.args.realname or cli.config.new_keyboard.name if cli.args.realname or cli.config.new_keyboard.name else prompt_name(user_name)\n    default_layout = cli.args.layout if cli.args.layout else prompt_layout()\n\n    if cli.args.type:\n        mcu = cli.args.type\n    else:\n        mcu = prompt_dev_board() if prompt_mcu_type() else prompt_mcu()\n\n    config = {}\n    if mcu in dev_boards:\n        config['development_board'] = mcu\n    else:\n        config['processor'] = mcu\n        config['bootloader'] = select_default_bootloader(mcu)\n\n    detach_layout = False\n    if default_layout == 'none of the above':\n        default_layout = \"ortho_4x4\"\n        detach_layout = True\n\n    tokens = {  # Comment here is to force multiline formatting\n        'YEAR': str(date.today().year),\n        'KEYBOARD': kb_name,\n        'USER_NAME': user_name,\n        'REAL_NAME': real_name\n    }\n\n    # begin with making the deepest folder in the tree\n    keymaps_path = keyboard(kb_name) / 'keymaps/'\n    keymaps_path.mkdir(parents=True)\n\n    # copy in keymap.c or keymap.json\n    community_keymap = Path(COMMUNITY / f'{default_layout}/default_{default_layout}/')\n    shutil.copytree(community_keymap, keymaps_path / 'default')\n\n    # process template files\n    for file in list(TEMPLATE.iterdir()):\n        replace_placeholders(file, keyboard(kb_name) / file.name, tokens)\n\n    # merge in infos\n    community_info = Path(COMMUNITY / f'{default_layout}/info.json')\n    augment_community_info(config, community_info, keyboard(kb_name) / 'keyboard.json')\n\n    # detach community layout and rename to just \"LAYOUT\"\n    if detach_layout:\n        replace_string(keyboard(kb_name) / 'keyboard.json', 'LAYOUT_ortho_4x4', 'LAYOUT')\n        replace_string(keymaps_path / 'default/keymap.c', 'LAYOUT_ortho_4x4', 'LAYOUT')\n\n    cli.log.info(f'{{fg_green}}Created a new keyboard called {{fg_cyan}}{kb_name}{{fg_green}}.{{fg_reset}}')\n    cli.log.info(f\"Build Command: {{fg_yellow}}qmk compile -kb {kb_name} -km default{{fg_reset}}.\")\n    cli.log.info(f'Project Location: {{fg_cyan}}{QMK_FIRMWARE}/{keyboard(kb_name)}{{fg_reset}}.')\n    cli.log.info(\"{fg_yellow}Now update the config files to match the hardware!{fg_reset}\")\n"
  },
  {
    "path": "lib/python/qmk/cli/new/keymap.py",
    "content": "\"\"\"This script automates the copying of the default keymap into your own keymap.\n\"\"\"\nimport re\nimport json\nimport shutil\nfrom pathlib import Path\n\nfrom milc import cli\nfrom milc.questions import question, choice\n\nfrom qmk.constants import HAS_QMK_USERSPACE, QMK_USERSPACE\nfrom qmk.errors import NoSuchKeyboardError\nfrom qmk.path import is_keyboard, keymaps, keymap\nfrom qmk.git import git_get_username\nfrom qmk.decorators import automagic_keyboard, automagic_keymap\nfrom qmk.keyboard import keyboard_completer, keyboard_folder\nfrom qmk.userspace import UserspaceDefs\nfrom qmk.json_schema import json_load\nfrom qmk.json_encoders import KeymapJSONEncoder\nfrom qmk.info import info_json\n\n\ndef _list_available_converters(kb_name):\n    \"\"\"Search for converters that can be applied to a given keyboard\n    \"\"\"\n    if not is_keyboard(kb_name):\n        return None\n\n    info = info_json(kb_name)\n    pin_compatible = info.get('pin_compatible')\n    if not pin_compatible:\n        return None\n\n    return sorted(folder.name.split('_to_')[-1] for folder in Path('platforms').glob(f'*/converters/{pin_compatible}_to_*'))\n\n\ndef _set_converter(file, converter):\n    \"\"\"add/overwrite any existing converter specified in keymap.json\n    \"\"\"\n    json_data = json_load(file) if file.exists() else {}\n\n    json_data['converter'] = converter\n\n    output = json.dumps(json_data, cls=KeymapJSONEncoder, sort_keys=True)\n    file.write_text(output + '\\n', encoding='utf-8')\n\n\ndef validate_keymap_name(name):\n    \"\"\"Returns True if the given keymap name contains only a-z, 0-9 and underscore characters.\n    \"\"\"\n    regex = re.compile(r'^[a-zA-Z0-9][a-zA-Z0-9_]+$')\n    return bool(regex.match(name))\n\n\ndef prompt_keyboard():\n    prompt = \"\"\"{fg_yellow}Select Keyboard{style_reset_all}\nIf you're unsure you can view a full list of supported keyboards with {fg_yellow}qmk list-keyboards{style_reset_all}.\n\nKeyboard Name? \"\"\"\n    return question(prompt)\n\n\ndef prompt_user():\n    prompt = \"\"\"\n{fg_yellow}Name Your Keymap{style_reset_all}\n\nKeymap name? \"\"\"\n    return question(prompt, default=git_get_username())\n\n\ndef prompt_converter(kb_name):\n    prompt = \"\"\"\n{fg_yellow}Configure Development Board{style_reset_all}\nFor more information, see:\nhttps://docs.qmk.fm/feature_converters\n\nUse converter? \"\"\"\n\n    converters = _list_available_converters(kb_name)\n    if not converters:\n        return None\n\n    choices = ['No (default)', *converters]\n\n    answer = choice(prompt, options=choices, default=0)\n    return None if choices.index(answer) == 0 else answer\n\n\n@cli.argument('-kb', '--keyboard', type=keyboard_folder, completer=keyboard_completer, help='Specify keyboard name. Example: 1upkeyboards/1up60hse')\n@cli.argument('-km', '--keymap', help='Specify the name for the new keymap directory')\n@cli.argument('--converter', help='Specify the name of a converter to configure')\n@cli.argument('--skip-converter', arg_only=True, action='store_true', help='Skip converter')\n@cli.subcommand('Creates a new keymap for the keyboard of your choosing')\n@automagic_keyboard\n@automagic_keymap\ndef new_keymap(cli):\n    \"\"\"Creates a new keymap for the keyboard of your choosing.\n    \"\"\"\n    cli.log.info('{style_bright}Generating a new keymap{style_normal}')\n    cli.echo('')\n\n    # ask for user input if keyboard or keymap was not provided in the command line\n    kb_name = cli.config.new_keymap.keyboard if cli.config.new_keymap.keyboard else prompt_keyboard()\n    user_name = cli.config.new_keymap.keymap if cli.config.new_keymap.keymap else prompt_user()\n    converter = cli.config.new_keymap.converter if cli.args.skip_converter or cli.config.new_keymap.converter else prompt_converter(kb_name)\n\n    # check directories\n    try:\n        kb_name = keyboard_folder(kb_name)\n    except ValueError:\n        cli.log.error(f'Keyboard {{fg_cyan}}{kb_name}{{fg_reset}} does not exist! Please choose a valid name.')\n        return False\n\n    # validate before any keymap ops\n    try:\n        keymaps_dirs = keymaps(kb_name)\n        keymap_path_new = keymaps_dirs[0] / user_name\n    except NoSuchKeyboardError:\n        cli.log.error(f'Keymap folder for {{fg_cyan}}{kb_name}{{fg_reset}} does not exist!')\n        return False\n\n    keymap_path_default = keymap(kb_name, 'default')\n\n    if not keymap_path_default:\n        cli.log.error(f'Default keymap for {{fg_cyan}}{kb_name}{{fg_reset}} does not exist!')\n        return False\n\n    if not validate_keymap_name(user_name):\n        cli.log.error('Keymap names must contain only {fg_cyan}a-z{fg_reset}, {fg_cyan}0-9{fg_reset} and {fg_cyan}_{fg_reset}! Please choose a different name.')\n        return False\n\n    if keymap_path_new.exists():\n        cli.log.error(f'Keymap {{fg_cyan}}{user_name}{{fg_reset}} already exists! Please choose a different name.')\n        return False\n\n    # create user directory with default keymap files\n    shutil.copytree(keymap_path_default, keymap_path_new, symlinks=True)\n\n    if converter:\n        _set_converter(keymap_path_new / 'keymap.json', converter)\n\n    # end message to user\n    cli.log.info(f'{{fg_green}}Created a new keymap called {{fg_cyan}}{user_name}{{fg_green}} in: {{fg_cyan}}{keymap_path_new}{{fg_reset}}.')\n    cli.log.info(f\"Compile a firmware with your new keymap by typing: {{fg_yellow}}qmk compile -kb {kb_name} -km {user_name}{{fg_reset}}.\")\n\n    # Add to userspace compile if we have userspace available\n    if HAS_QMK_USERSPACE:\n        userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json')\n        userspace.add_target(keyboard=kb_name, keymap=user_name, do_print=False)\n        return userspace.save()\n"
  },
  {
    "path": "lib/python/qmk/cli/painter/__init__.py",
    "content": "from . import convert_graphics\nfrom . import make_font\n"
  },
  {
    "path": "lib/python/qmk/cli/painter/convert_graphics.py",
    "content": "\"\"\"This script tests QGF functionality.\n\"\"\"\nfrom io import BytesIO\nfrom qmk.path import normpath\nfrom qmk.painter import generate_subs, render_header, render_source, valid_formats\nfrom milc import cli\nfrom PIL import Image\n\n\n@cli.argument('-v', '--verbose', arg_only=True, action='store_true', help='Turns on verbose output.')\n@cli.argument('-i', '--input', required=True, help='Specify input graphic file.')\n@cli.argument('-o', '--output', default='', help='Specify output directory. Defaults to same directory as input.')\n@cli.argument('-f', '--format', required=True, help=f'Output format, valid types: {\", \".join(valid_formats.keys())}')\n@cli.argument('-r', '--no-rle', arg_only=True, action='store_true', help='Disables the use of RLE when encoding images.')\n@cli.argument('-d', '--no-deltas', arg_only=True, action='store_true', help='Disables the use of delta frames when encoding animations.')\n@cli.argument('-w', '--raw', arg_only=True, action='store_true', help='Writes out the QGF file as raw data instead of c/h combo.')\n@cli.subcommand('Converts an input image to something QMK understands')\ndef painter_convert_graphics(cli):\n    \"\"\"Converts an image file to a format that Quantum Painter understands.\n\n    This command uses the `qmk.painter` module to generate a Quantum Painter image defintion from an image. The generated definitions are written to a files next to the input -- `INPUT.c` and `INPUT.h`.\n    \"\"\"\n    # Work out the input file\n    if cli.args.input != '-':\n        cli.args.input = normpath(cli.args.input)\n\n        # Error checking\n        if not cli.args.input.exists():\n            cli.log.error('Input image file does not exist!')\n            cli.print_usage()\n            return False\n\n    # Work out the output directory\n    if len(cli.args.output) == 0:\n        cli.args.output = cli.args.input.parent\n    cli.args.output = normpath(cli.args.output)\n\n    # Ensure we have a valid format\n    if cli.args.format not in valid_formats.keys():\n        cli.log.error('Output format %s is invalid. Allowed values: %s' % (cli.args.format, ', '.join(valid_formats.keys())))\n        cli.print_usage()\n        return False\n\n    # Work out the encoding parameters\n    format = valid_formats[cli.args.format]\n\n    # Load the input image\n    input_img = Image.open(cli.args.input)\n\n    # Convert the image to QGF using PIL\n    out_data = BytesIO()\n    metadata = []\n    input_img.save(out_data, \"QGF\", use_deltas=(not cli.args.no_deltas), use_rle=(not cli.args.no_rle), qmk_format=format, verbose=cli.args.verbose, metadata=metadata)\n    out_bytes = out_data.getvalue()\n\n    if cli.args.raw:\n        raw_file = cli.args.output / f\"{cli.args.input.stem}.qgf\"\n        with open(raw_file, 'wb') as raw:\n            raw.write(out_bytes)\n        return\n\n    # Work out the text substitutions for rendering the output data\n    subs = generate_subs(cli, out_bytes, image_metadata=metadata, command_name=\"painter_convert_graphics\")\n\n    # Render and write the header file\n    header_text = render_header(subs)\n    header_file = cli.args.output / f\"{cli.args.input.stem}.qgf.h\"\n    with open(header_file, 'w') as header:\n        print(f\"Writing {header_file}...\")\n        header.write(header_text)\n\n    # Render and write the source file\n    source_text = render_source(subs)\n    source_file = cli.args.output / f\"{cli.args.input.stem}.qgf.c\"\n    with open(source_file, 'w') as source:\n        print(f\"Writing {source_file}...\")\n        source.write(source_text)\n"
  },
  {
    "path": "lib/python/qmk/cli/painter/make_font.py",
    "content": "\"\"\"This script automates the conversion of font files into a format QMK firmware understands.\n\"\"\"\n\nfrom io import BytesIO\nfrom qmk.path import normpath\nfrom qmk.painter_qff import _generate_font_glyphs_list, QFFFont\nfrom qmk.painter import generate_subs, render_header, render_source, valid_formats\nfrom milc import cli\n\n\n@cli.argument('-f', '--font', required=True, help='Specify input font file.')\n@cli.argument('-o', '--output', required=True, help='Specify output image path.')\n@cli.argument('-s', '--size', default=12, help='Specify font size. Default 12.')\n@cli.argument('-n', '--no-ascii', arg_only=True, action='store_true', help='Disables output of the full ASCII character set (0x20..0x7E), exporting only the glyphs specified.')\n@cli.argument('-u', '--unicode-glyphs', default='', help='Also generate the specified unicode glyphs.')\n@cli.argument('-a', '--no-aa', arg_only=True, action='store_true', help='Disable anti-aliasing on fonts.')\n@cli.subcommand('Converts an input font to something QMK understands')\ndef painter_make_font_image(cli):\n    # Create the font object\n    font = QFFFont(cli)\n    # Read from the input file\n    cli.args.font = normpath(cli.args.font)\n    font.generate_image(cli.args.font, cli.args.size, include_ascii_glyphs=(not cli.args.no_ascii), unicode_glyphs=cli.args.unicode_glyphs, use_aa=(False if cli.args.no_aa else True))\n    # Render out the data\n    font.save_to_image(normpath(cli.args.output))\n\n\n@cli.argument('-i', '--input', help='Specify input graphic file.')\n@cli.argument('-o', '--output', default='', help='Specify output directory. Defaults to same directory as input.')\n@cli.argument('-n', '--no-ascii', arg_only=True, action='store_true', help='Disables output of the full ASCII character set (0x20..0x7E), exporting only the glyphs specified.')\n@cli.argument('-u', '--unicode-glyphs', default='', help='Also generate the specified unicode glyphs.')\n@cli.argument('-f', '--format', required=True, help=f'Output format, valid types: {\", \".join(valid_formats.keys())}')\n@cli.argument('-r', '--no-rle', arg_only=True, action='store_true', help='Disable the use of RLE to minimise converted image size.')\n@cli.argument('-w', '--raw', arg_only=True, action='store_true', help='Writes out the QFF file as raw data instead of c/h combo.')\n@cli.subcommand('Converts an input font image to something QMK firmware understands')\ndef painter_convert_font_image(cli):\n    # Work out the format\n    format = valid_formats[cli.args.format]\n\n    # Create the font object\n    font = QFFFont(cli.log)\n\n    # Read from the input file\n    cli.args.input = normpath(cli.args.input)\n    font.read_from_image(cli.args.input, include_ascii_glyphs=(not cli.args.no_ascii), unicode_glyphs=cli.args.unicode_glyphs)\n\n    # Work out the output directory\n    if len(cli.args.output) == 0:\n        cli.args.output = cli.args.input.parent\n    cli.args.output = normpath(cli.args.output)\n\n    # Render out the data\n    out_data = BytesIO()\n    font.save_to_qff(format, not cli.args.no_rle, out_data)\n    out_bytes = out_data.getvalue()\n\n    if cli.args.raw:\n        raw_file = cli.args.output / f\"{cli.args.input.stem}.qff\"\n        with open(raw_file, 'wb') as raw:\n            raw.write(out_bytes)\n        return\n\n    # Work out the text substitutions for rendering the output data\n    metadata = {\"glyphs\": _generate_font_glyphs_list(not cli.args.no_ascii, cli.args.unicode_glyphs)}\n    subs = generate_subs(cli, out_bytes, font_metadata=metadata, command_name=\"painter_convert_font_image\")\n\n    # Render and write the header file\n    header_text = render_header(subs)\n    header_file = cli.args.output / f\"{cli.args.input.stem}.qff.h\"\n    with open(header_file, 'w') as header:\n        print(f\"Writing {header_file}...\")\n        header.write(header_text)\n\n    # Render and write the source file\n    source_text = render_source(subs)\n    source_file = cli.args.output / f\"{cli.args.input.stem}.qff.c\"\n    with open(source_file, 'w') as source:\n        print(f\"Writing {source_file}...\")\n        source.write(source_text)\n"
  },
  {
    "path": "lib/python/qmk/cli/pytest.py",
    "content": "\"\"\"QMK Python Unit Tests\n\nQMK script to run unit and integration tests against our python code.\n\"\"\"\nfrom subprocess import DEVNULL\n\nfrom milc import cli\n\n\n@cli.argument('-t', '--test', arg_only=True, action='append', default=[], help=\"Mapped to nose2 'testNames' positional argument - https://docs.nose2.io/en/latest/usage.html#specifying-tests-to-run\")\n@cli.subcommand('QMK Python Unit Tests', hidden=False if cli.config.user.developer else True)\ndef pytest(cli):\n    \"\"\"Run several linting/testing commands.\n    \"\"\"\n    nose2 = cli.run(['nose2', '-v', '-t', 'lib/python', *cli.args.test], capture_output=False, stdin=DEVNULL)\n    flake8 = cli.run(['flake8', 'lib/python'], capture_output=False, stdin=DEVNULL)\n\n    return flake8.returncode | nose2.returncode\n"
  },
  {
    "path": "lib/python/qmk/cli/resolve_alias.py",
    "content": "from qmk.keyboard import keyboard_folder\n\nfrom milc import cli\n\n\n@cli.argument('--allow-unknown', arg_only=True, action='store_true', help=\"Return original if rule is not a valid keyboard.\")\n@cli.argument('keyboard', arg_only=True, help='The keyboard\\'s name')\n@cli.subcommand('Resolve any keyboard_aliases for provided rule')\ndef resolve_alias(cli):\n    try:\n        print(keyboard_folder(cli.args.keyboard))\n    except ValueError:\n        if cli.args.allow_unknown:\n            print(cli.args.keyboard)\n        else:\n            raise\n"
  },
  {
    "path": "lib/python/qmk/cli/test/__init__.py",
    "content": ""
  },
  {
    "path": "lib/python/qmk/cli/test/c.py",
    "content": "import fnmatch\nimport re\nfrom subprocess import DEVNULL\n\nfrom milc import cli\n\nfrom qmk.commands import find_make, get_make_parallel_args, build_environment\n\n\n@cli.argument('-j', '--parallel', type=int, default=1, help=\"Set the number of parallel make jobs; 0 means unlimited.\")\n@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help=\"Set a variable to be passed to make. May be passed multiple times.\")\n@cli.argument('-c', '--clean', arg_only=True, action='store_true', help=\"Remove object files before compiling.\")\n@cli.argument('-l', '--list', arg_only=True, action='store_true', help='List available tests.')\n@cli.argument('-t', '--test', arg_only=True, action='append', default=[], help=\"Test to run from the available list. Supports wildcard globs. May be passed multiple times.\")\n@cli.subcommand(\"QMK C Unit Tests.\", hidden=False if cli.config.user.developer else True)\ndef test_c(cli):\n    \"\"\"Run native unit tests.\n    \"\"\"\n    list_tests = cli.run([find_make(), 'list-tests', 'SILENT=true'])\n    available_tests = sorted(list_tests.stdout.strip().split())\n\n    if cli.args.list:\n        return print(\"\\n\".join(available_tests))\n\n    # expand any wildcards\n    filtered_tests = set()\n    for test in cli.args.test:\n        regex = re.compile(fnmatch.translate(test))\n        filtered_tests |= set(filter(regex.match, available_tests))\n\n    for invalid in filtered_tests - set(available_tests):\n        cli.log.warning(f'Invalid test provided: {invalid}')\n\n    # convert test names to build targets\n    targets = list(map(lambda x: f'test:{x}', filtered_tests or ['all']))\n\n    if cli.args.clean:\n        targets.insert(0, 'clean')\n\n    # Add in the environment vars\n    for key, value in build_environment(cli.args.env).items():\n        targets.append(f'{key}={value}')\n\n    command = [find_make(), *get_make_parallel_args(cli.config.test_c.parallel), *targets]\n\n    cli.log.info('Compiling tests with {fg_cyan}%s', ' '.join(command))\n    return cli.run(command, capture_output=False, stdin=DEVNULL).returncode\n"
  },
  {
    "path": "lib/python/qmk/cli/userspace/__init__.py",
    "content": "from . import doctor\nfrom . import add\nfrom . import remove\nfrom . import list\nfrom . import compile\n"
  },
  {
    "path": "lib/python/qmk/cli/userspace/add.py",
    "content": "# Copyright 2023-2024 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\nfrom pathlib import Path\nfrom milc import cli\n\nfrom qmk.commands import parse_env_vars\nfrom qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE\nfrom qmk.keyboard import keyboard_completer, keyboard_folder_or_all\nfrom qmk.keymap import keymap_completer, is_keymap_target\nfrom qmk.userspace import UserspaceDefs\n\n\n@cli.argument('builds', nargs='*', arg_only=True, help=\"List of builds in form <keyboard>:<keymap>, or path to a keymap JSON file.\")\n@cli.argument('-kb', '--keyboard', type=keyboard_folder_or_all, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')\n@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')\n@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help=\"Extra variables to set during build. May be passed multiple times.\")\n@cli.subcommand('Adds a build target to userspace `qmk.json`.')\ndef userspace_add(cli):\n    if not HAS_QMK_USERSPACE:\n        cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.')\n        return False\n\n    build_env = None if len(cli.args.env) == 0 else parse_env_vars(cli.args.env)\n\n    userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json')\n\n    if len(cli.args.builds) > 0:\n        json_like_targets = list([Path(p) for p in filter(lambda e: Path(e).exists() and Path(e).suffix == '.json', cli.args.builds)])\n        make_like_targets = list(filter(lambda e: Path(e) not in json_like_targets, cli.args.builds))\n\n        for e in json_like_targets:\n            userspace.add_target(json_path=e)\n\n        for e in make_like_targets:\n            s = e.split(':')\n            userspace.add_target(keyboard=s[0], keymap=s[1])\n\n    else:\n        failed = False\n        try:\n            if not is_keymap_target(cli.args.keyboard, cli.args.keymap):\n                failed = True\n        except KeyError:\n            failed = True\n\n        if failed:\n            from qmk.cli.new.keymap import new_keymap\n            cli.config.new_keymap.keyboard = cli.args.keyboard\n            cli.config.new_keymap.keymap = cli.args.keymap\n            cli.args.skip_converter = True\n            if new_keymap(cli) is not False:\n                userspace.add_target(keyboard=cli.args.keyboard, keymap=cli.args.keymap, build_env=build_env)\n        else:\n            userspace.add_target(keyboard=cli.args.keyboard, keymap=cli.args.keymap, build_env=build_env)\n\n    return userspace.save()\n"
  },
  {
    "path": "lib/python/qmk/cli/userspace/compile.py",
    "content": "# Copyright 2023-2024 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\nfrom pathlib import Path\nfrom milc import cli\n\nfrom qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE\nfrom qmk.commands import build_environment\nfrom qmk.userspace import UserspaceDefs\nfrom qmk.build_targets import JsonKeymapBuildTarget\nfrom qmk.search import search_keymap_targets\nfrom qmk.cli.mass_compile import mass_compile_targets\nfrom qmk.util import maybe_exit_config\n\n\ndef _extra_arg_setter(target, extra_args):\n    target.extra_args = extra_args\n\n\n@cli.argument('-t', '--no-temp', arg_only=True, action='store_true', help=\"Remove temporary files during build.\")\n@cli.argument('-j', '--parallel', type=int, default=1, help=\"Set the number of parallel make jobs; 0 means unlimited.\")\n@cli.argument('-c', '--clean', arg_only=True, action='store_true', help=\"Remove object files before compiling.\")\n@cli.argument('-n', '--dry-run', arg_only=True, action='store_true', help=\"Don't actually build, just show the commands to be run.\")\n@cli.argument('-p', '--print-failures', arg_only=True, action='store_true', help=\"Print failed builds.\")\n@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help=\"Set a variable to be passed to make. May be passed multiple times.\")\n@cli.subcommand('Compiles the build targets specified in userspace `qmk.json`.')\ndef userspace_compile(cli):\n    if not HAS_QMK_USERSPACE:\n        cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.')\n        return False\n\n    maybe_exit_config(should_exit=False, should_reraise=True)\n\n    userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json')\n\n    build_targets = []\n    keyboard_keymap_targets = []\n    for e in userspace.build_targets:\n        if isinstance(e, Path):\n            build_targets.append(JsonKeymapBuildTarget(e))\n        elif isinstance(e, dict):\n            f = e['env'] if 'env' in e else None\n            keyboard_keymap_targets.append((e['keyboard'], e['keymap'], f))\n    if len(keyboard_keymap_targets) > 0:\n        build_targets.extend(search_keymap_targets(keyboard_keymap_targets))\n\n    return mass_compile_targets(list(set(build_targets)), cli.args.clean, cli.args.dry_run, cli.config.userspace_compile.no_temp, cli.config.userspace_compile.parallel, cli.args.print_failures, **build_environment(cli.args.env))\n"
  },
  {
    "path": "lib/python/qmk/cli/userspace/doctor.py",
    "content": "# Copyright 2023 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\nfrom milc import cli\n\nfrom qmk.constants import QMK_FIRMWARE, HAS_QMK_USERSPACE\nfrom qmk.cli.doctor.main import userspace_tests\n\n\n@cli.subcommand('Checks userspace configuration.')\ndef userspace_doctor(cli):\n    userspace_tests(QMK_FIRMWARE)\n\n    return 0 if HAS_QMK_USERSPACE else 1\n"
  },
  {
    "path": "lib/python/qmk/cli/userspace/list.py",
    "content": "# Copyright 2023-2024 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\nfrom pathlib import Path\nfrom dotty_dict import Dotty\nfrom milc import cli\n\nfrom qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE\nfrom qmk.userspace import UserspaceDefs\nfrom qmk.build_targets import BuildTarget\nfrom qmk.keyboard import is_all_keyboards, keyboard_folder\nfrom qmk.keymap import is_keymap_target\nfrom qmk.search import search_keymap_targets\nfrom qmk.util import maybe_exit_config\n\n\ndef _extra_arg_setter(target, extra_args):\n    target.extra_args = extra_args\n\n\n@cli.argument('-e', '--expand', arg_only=True, action='store_true', help=\"Expands any use of `all` for either keyboard or keymap.\")\n@cli.subcommand('Lists the build targets specified in userspace `qmk.json`.')\ndef userspace_list(cli):\n    if not HAS_QMK_USERSPACE:\n        cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.')\n        return False\n\n    maybe_exit_config(should_exit=False, should_reraise=True)\n\n    userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json')\n\n    if cli.args.expand:\n        build_targets = []\n        keyboard_keymap_targets = []\n        for e in userspace.build_targets:\n            if isinstance(e, Path):\n                build_targets.append(e)\n            elif isinstance(e, dict) or isinstance(e, Dotty):\n                f = e['env'] if 'env' in e else None\n                keyboard_keymap_targets.append((e['keyboard'], e['keymap'], f))\n        if len(keyboard_keymap_targets) > 0:\n            build_targets.extend(search_keymap_targets(keyboard_keymap_targets))\n    else:\n        build_targets = userspace.build_targets\n\n    for e in build_targets:\n        if isinstance(e, Path):\n            # JSON keymap from userspace\n            cli.log.info(f'JSON keymap: {{fg_cyan}}{e}{{fg_reset}}')\n            continue\n        elif isinstance(e, dict) or isinstance(e, Dotty):\n            # keyboard/keymap dict from userspace\n            keyboard = e['keyboard']\n            keymap = e['keymap']\n            extra_args = e.get('env')\n        elif isinstance(e, BuildTarget):\n            # BuildTarget from search_keymap_targets()\n            keyboard = e.keyboard\n            keymap = e.keymap\n            extra_args = e.extra_args\n\n        extra_args_str = ''\n        if extra_args is not None and len(extra_args) > 0:\n            extra_args_str = ', '.join([f'{{fg_cyan}}{k}={v}{{fg_reset}}' for k, v in extra_args.items()])\n            extra_args_str = f' ({{fg_cyan}}{extra_args_str}{{fg_reset}})'\n\n        if is_all_keyboards(keyboard) or is_keymap_target(keyboard_folder(keyboard), keymap):\n            cli.log.info(f'Keyboard: {{fg_cyan}}{keyboard}{{fg_reset}}, keymap: {{fg_cyan}}{keymap}{{fg_reset}}{extra_args_str}')\n        else:\n            cli.log.warning(f'Keyboard: {{fg_cyan}}{keyboard}{{fg_reset}}, keymap: {{fg_cyan}}{keymap}{{fg_reset}}{extra_args_str} -- not found!')\n"
  },
  {
    "path": "lib/python/qmk/cli/userspace/path.py",
    "content": "from milc import cli\nfrom qmk.constants import QMK_USERSPACE\n\n\n@cli.subcommand('Detected path to QMK Userspace.', hidden=True)\ndef userspace_path(cli):\n    print(QMK_USERSPACE or '')\n    return\n"
  },
  {
    "path": "lib/python/qmk/cli/userspace/remove.py",
    "content": "# Copyright 2023-2024 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\nfrom pathlib import Path\nfrom milc import cli\n\nfrom qmk.commands import parse_env_vars\nfrom qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE\nfrom qmk.keyboard import keyboard_completer, keyboard_folder_or_all\nfrom qmk.keymap import keymap_completer\nfrom qmk.userspace import UserspaceDefs\n\n\n@cli.argument('builds', nargs='*', arg_only=True, help=\"List of builds in form <keyboard>:<keymap>, or path to a keymap JSON file.\")\n@cli.argument('-kb', '--keyboard', type=keyboard_folder_or_all, completer=keyboard_completer, help='The keyboard to build a firmware for. Ignored when a configurator export is supplied.')\n@cli.argument('-km', '--keymap', completer=keymap_completer, help='The keymap to build a firmware for. Ignored when a configurator export is supplied.')\n@cli.argument('-e', '--env', arg_only=True, action='append', default=[], help=\"Extra variables to set during build. May be passed multiple times.\")\n@cli.subcommand('Removes a build target from userspace `qmk.json`.')\ndef userspace_remove(cli):\n    if not HAS_QMK_USERSPACE:\n        cli.log.error('Could not determine QMK userspace location. Please run `qmk doctor` or `qmk userspace-doctor` to diagnose.')\n        return False\n\n    build_env = None if len(cli.args.env) == 0 else parse_env_vars(cli.args.env)\n\n    userspace = UserspaceDefs(QMK_USERSPACE / 'qmk.json')\n\n    if len(cli.args.builds) > 0:\n        json_like_targets = list([Path(p) for p in filter(lambda e: Path(e).exists() and Path(e).suffix == '.json', cli.args.builds)])\n        make_like_targets = list(filter(lambda e: Path(e) not in json_like_targets, cli.args.builds))\n\n        for e in json_like_targets:\n            userspace.remove_target(json_path=e)\n\n        for e in make_like_targets:\n            s = e.split(':')\n            userspace.remove_target(keyboard=s[0], keymap=s[1], build_env=build_env)\n\n    else:\n        userspace.remove_target(keyboard=cli.args.keyboard, keymap=cli.args.keymap, build_env=build_env)\n\n    return userspace.save()\n"
  },
  {
    "path": "lib/python/qmk/cli/via2json.py",
    "content": "\"\"\"Generate a keymap.c from a configurator export.\n\"\"\"\nimport json\nimport re\n\nfrom milc import cli\n\nimport qmk.keyboard\nimport qmk.path\nfrom qmk.info import info_json\nfrom qmk.json_encoders import KeymapJSONEncoder\nfrom qmk.commands import dump_lines\nfrom qmk.keymap import generate_json\n\n\ndef _find_via_layout_macro(keyboard_data):\n    \"\"\"Assume layout macro when only 1 is available\n    \"\"\"\n    layouts = list(keyboard_data['layouts'].keys())\n    return layouts[0] if len(layouts) == 1 else None\n\n\ndef _convert_macros(via_macros):\n    via_macros = list(filter(lambda f: bool(f), via_macros))\n    if len(via_macros) == 0:\n        return list()\n    split_regex = re.compile(r'(}\\,)|(\\,{)')\n    macro_group_regex = re.compile(r'({.+?})')\n    macros = list()\n    for via_macro in via_macros:\n        # Split VIA macro to its elements\n        macro = split_regex.split(via_macro)\n        # Remove junk elements (None, '},' and ',{')\n        macro = list(filter(lambda f: False if f in (None, '},', ',{') else True, macro))\n        macro_data = list()\n        for m in macro:\n            if '{' in m or '}' in m:\n                # Split macro groups\n                macro_groups = macro_group_regex.findall(m)\n                for macro_group in macro_groups:\n                    # Remove whitespaces and curly braces from around group\n                    macro_group = macro_group.strip(' {}')\n\n                    macro_action = 'tap'\n                    macro_keycodes = []\n\n                    if macro_group[0] == '+':\n                        macro_action = 'down'\n                        macro_keycodes.append(macro_group[1:])\n                    elif macro_group[0] == '-':\n                        macro_action = 'up'\n                        macro_keycodes.append(macro_group[1:])\n                    else:\n                        macro_keycodes.extend(macro_group.split(',') if ',' in macro_group else [macro_group])\n\n                    # Remove the KC prefixes\n                    macro_keycodes = list(map(lambda s: s.replace('KC_', ''), macro_keycodes))\n\n                    macro_data.append({\"action\": macro_action, \"keycodes\": macro_keycodes})\n            else:\n                # Found text\n                macro_data.append(m)\n        macros.append(macro_data)\n\n    return macros\n\n\ndef _fix_macro_keys(keymap_data):\n    macro_no = re.compile(r'MACRO0?\\(([0-9]{1,2})\\)')\n    for i in range(0, len(keymap_data)):\n        for j in range(0, len(keymap_data[i])):\n            kc = keymap_data[i][j]\n            m = macro_no.match(kc)\n            if m:\n                keymap_data[i][j] = f'MC_{m.group(1)}'\n    return keymap_data\n\n\ndef _via_to_keymap(via_backup, keyboard_data, keymap_layout):\n    # Check if passed LAYOUT is correct\n    layout_data = keyboard_data['layouts'].get(keymap_layout)\n    if not layout_data:\n        cli.log.error(f'LAYOUT macro {keymap_layout} is not a valid one for keyboard {cli.args.keyboard}!')\n        return None\n\n    layout_data = layout_data['layout']\n    sorting_hat = list()\n    for index, data in enumerate(layout_data):\n        sorting_hat.append([index, data['matrix']])\n\n    sorting_hat.sort(key=lambda k: (k[1][0], k[1][1]))\n\n    pos = 0\n    for row_num in range(0, keyboard_data['matrix_size']['rows']):\n        for col_num in range(0, keyboard_data['matrix_size']['cols']):\n            if pos >= len(sorting_hat) or sorting_hat[pos][1][0] != row_num or sorting_hat[pos][1][1] != col_num:\n                sorting_hat.insert(pos, [None, [row_num, col_num]])\n            else:\n                sorting_hat.append([None, [row_num, col_num]])\n            pos += 1\n\n    keymap_data = list()\n    for layer in via_backup['layers']:\n        pos = 0\n        layer_data = list()\n        for key in layer:\n            if sorting_hat[pos][0] is not None:\n                layer_data.append([sorting_hat[pos][0], key])\n            pos += 1\n        layer_data.sort()\n        layer_data = [kc[1] for kc in layer_data]\n        keymap_data.append(layer_data)\n\n    return keymap_data\n\n\n@cli.argument('-o', '--output', arg_only=True, type=qmk.path.normpath, help='File to write to')\n@cli.argument('-q', '--quiet', arg_only=True, action='store_true', help=\"Quiet mode, only output error messages\")\n@cli.argument('filename', type=qmk.path.FileType('r'), arg_only=True, help='VIA Backup JSON file')\n@cli.argument('-kb', '--keyboard', type=qmk.keyboard.keyboard_folder, completer=qmk.keyboard.keyboard_completer, arg_only=True, required=True, help='The keyboard\\'s name')\n@cli.argument('-km', '--keymap', arg_only=True, default='via2json', help='The keymap\\'s name')\n@cli.argument('-l', '--layout', arg_only=True, help='The keymap\\'s layout')\n@cli.subcommand('Convert a VIA backup json to keymap.json format.')\ndef via2json(cli):\n    \"\"\"Convert a VIA backup json to keymap.json format.\n\n    This command uses the `qmk.keymap` module to generate a keymap.json from a VIA backup json. The generated keymap is written to stdout, or to a file if -o is provided.\n    \"\"\"\n    # Load the VIA backup json\n    with cli.args.filename.open('r') as fd:\n        via_backup = json.load(fd)\n\n    keyboard_data = info_json(cli.args.keyboard)\n\n    # Find appropriate layout macro\n    keymap_layout = cli.args.layout if cli.args.layout else _find_via_layout_macro(keyboard_data)\n    if not keymap_layout:\n        cli.log.error(f\"Couldn't find LAYOUT macro for keyboard {cli.args.keyboard}. Please specify it with the '-l' argument.\")\n        return False\n\n    # Get keycode array\n    keymap_data = _via_to_keymap(via_backup, keyboard_data, keymap_layout)\n    if not keymap_data:\n        cli.log.error(f'Could not extract valid keycode data from VIA backup matching keyboard {cli.args.keyboard}!')\n        return False\n\n    # Convert macros\n    macro_data = list()\n    if via_backup.get('macros'):\n        macro_data = _convert_macros(via_backup['macros'])\n\n        # Replace VIA macro keys with JSON keymap ones\n        keymap_data = _fix_macro_keys(keymap_data)\n\n    # Generate the keymap.json\n    keymap_json = generate_json(cli.args.keymap, cli.args.keyboard, keymap_layout, keymap_data, macro_data)\n\n    keymap_lines = [json.dumps(keymap_json, cls=KeymapJSONEncoder, sort_keys=True)]\n    dump_lines(cli.args.output, keymap_lines, cli.args.quiet)\n"
  },
  {
    "path": "lib/python/qmk/commands.py",
    "content": "\"\"\"Helper functions for commands.\n\"\"\"\nimport os\nimport sys\nimport shutil\nfrom pathlib import Path\n\nfrom milc import cli\nimport jsonschema\n\nfrom qmk.constants import QMK_USERSPACE, HAS_QMK_USERSPACE\nfrom qmk.json_schema import json_load, validate\nfrom qmk.keyboard import keyboard_alias_definitions\nfrom qmk.util import maybe_exit\nfrom qmk.path import unix_style_path\n\n\ndef find_make():\n    \"\"\"Returns the correct make command for this environment.\n    \"\"\"\n    make_cmd = os.environ.get('MAKE')\n\n    if not make_cmd:\n        make_cmd = 'gmake' if shutil.which('gmake') else 'make'\n\n    return make_cmd\n\n\ndef get_make_parallel_args(parallel=1):\n    \"\"\"Returns the arguments for running the specified number of parallel jobs.\n    \"\"\"\n    parallel_args = []\n\n    if int(parallel) <= 0:\n        # 0 or -1 means -j without argument (unlimited jobs)\n        parallel_args.append('--jobs')\n    elif int(parallel) > 1:\n        parallel_args.append('--jobs=' + str(parallel))\n\n    if int(parallel) != 1:\n        # If more than 1 job is used, synchronize parallel output by target\n        parallel_args.append('--output-sync=target')\n\n    return parallel_args\n\n\ndef parse_configurator_json(configurator_file):\n    \"\"\"Open and parse a configurator json export\n    \"\"\"\n    user_keymap = json_load(configurator_file)\n    # Validate against the jsonschema\n    try:\n        validate(user_keymap, 'qmk.keymap.v1')\n\n    except jsonschema.ValidationError as e:\n        cli.log.error(f'Invalid JSON keymap: {configurator_file} : {e.message}')\n        maybe_exit(1)\n\n    keyboard = user_keymap.get('keyboard', None)\n    aliases = keyboard_alias_definitions()\n\n    while keyboard in aliases:\n        last_keyboard = keyboard\n        keyboard = aliases[keyboard].get('target', keyboard)\n        if keyboard == last_keyboard:\n            break\n\n    user_keymap['keyboard'] = keyboard\n    return user_keymap\n\n\ndef parse_env_vars(args):\n    \"\"\"Common processing for cli.args.env\n    \"\"\"\n    envs = {}\n    for env in args:\n        if '=' in env:\n            key, value = env.split('=', 1)\n            envs[key] = value\n        else:\n            cli.log.warning('Invalid environment variable: %s', env)\n    return envs\n\n\ndef build_environment(args):\n    envs = parse_env_vars(args)\n\n    if HAS_QMK_USERSPACE:\n        envs['QMK_USERSPACE'] = unix_style_path(Path(QMK_USERSPACE).resolve())\n\n    return envs\n\n\ndef in_virtualenv():\n    \"\"\"Check if running inside a virtualenv.\n    Based on https://stackoverflow.com/a/1883251\n    \"\"\"\n    active_prefix = getattr(sys, \"base_prefix\", None) or getattr(sys, \"real_prefix\", None) or sys.prefix\n    return active_prefix != sys.prefix\n\n\ndef dump_lines(output_file, lines, quiet=True, remove_repeated_newlines=False):\n    \"\"\"Handle dumping to stdout or file\n    Creates parent folders if required\n    \"\"\"\n    generated = '\\n'.join(lines) + '\\n'\n    if remove_repeated_newlines:\n        while '\\n\\n\\n' in generated:\n            generated = generated.replace('\\n\\n\\n', '\\n\\n')\n    if output_file and output_file.name != '-':\n        output_file.parent.mkdir(parents=True, exist_ok=True)\n        if output_file.exists():\n            with open(output_file, 'r', encoding='utf-8', newline='\\n') as f:\n                existing = f.read()\n            if existing == generated:\n                if not quiet:\n                    cli.log.info(f'No changes to {output_file.name}.')\n                return\n            output_file.replace(output_file.parent / (output_file.name + '.bak'))\n        with open(output_file, 'w', encoding='utf-8', newline='\\n') as f:\n            f.write(generated)\n        # output_file.write_text(generated, encoding='utf-8', newline='\\n') # `newline` needs Python 3.10\n\n        if not quiet:\n            cli.log.info(f'Wrote {output_file.name} to {output_file}.')\n    else:\n        print(generated)\n"
  },
  {
    "path": "lib/python/qmk/comment_remover.py",
    "content": "\"\"\"Removes C/C++ style comments from text.\n\nGratefully adapted from https://stackoverflow.com/a/241506\n\"\"\"\nimport re\n\ncomment_pattern = re.compile(r'//.*?$|/\\*.*?\\*/|\\'(?:\\\\.|[^\\\\\\'])*\\'|\"(?:\\\\.|[^\\\\\"])*\"', re.DOTALL | re.MULTILINE)\n\n\ndef _comment_stripper(match):\n    \"\"\"Removes C/C++ style comments from a regex match.\n    \"\"\"\n    s = match.group(0)\n    return ' ' if s.startswith('/') else s\n\n\ndef comment_remover(text):\n    \"\"\"Remove C/C++ style comments from text.\n    \"\"\"\n    return re.sub(comment_pattern, _comment_stripper, text)\n"
  },
  {
    "path": "lib/python/qmk/community_modules.py",
    "content": "import os\n\nfrom pathlib import Path\nfrom functools import lru_cache\n\nfrom milc.attrdict import AttrDict\n\nfrom qmk.json_schema import json_load, validate, merge_ordered_dicts\nfrom qmk.util import truthy\nfrom qmk.constants import QMK_FIRMWARE, QMK_USERSPACE, HAS_QMK_USERSPACE\nfrom qmk.path import under_qmk_firmware, under_qmk_userspace\n\nCOMMUNITY_MODULE_JSON_FILENAME = 'qmk_module.json'\n\n\nclass ModuleAPI(AttrDict):\n    def __init__(self, **kwargs):\n        super().__init__()\n        for key, value in kwargs.items():\n            self[key] = value\n\n\n@lru_cache(maxsize=1)\ndef module_api_list():\n    module_definition_files = sorted(set(QMK_FIRMWARE.glob('data/constants/module_hooks/*.hjson')))\n    module_definition_jsons = [json_load(f) for f in module_definition_files]\n    module_definitions = merge_ordered_dicts(module_definition_jsons)\n    latest_module_version = module_definition_files[-1].stem\n    latest_module_version_parts = latest_module_version.split('.')\n\n    api_list = []\n    for name, mod in module_definitions.items():\n        api_list.append(ModuleAPI(\n            ret_type=mod['ret_type'],\n            name=name,\n            args=mod['args'],\n            call_params=mod.get('call_params', ''),\n            guard=mod.get('guard', None),\n            header=mod.get('header', None),\n        ))\n\n    return api_list, latest_module_version, latest_module_version_parts[0], latest_module_version_parts[1], latest_module_version_parts[2]\n\n\ndef find_available_module_paths():\n    \"\"\"Find all available modules.\n    \"\"\"\n    search_dirs = []\n    if HAS_QMK_USERSPACE:\n        search_dirs.append(QMK_USERSPACE / 'modules')\n    search_dirs.append(QMK_FIRMWARE / 'modules')\n\n    modules = []\n    for search_dir in search_dirs:\n        for module_json_path in search_dir.rglob(COMMUNITY_MODULE_JSON_FILENAME):\n            modules.append(module_json_path.parent)\n    return modules\n\n\ndef find_module_path(module):\n    \"\"\"Find a module by name.\n    \"\"\"\n    for module_path in find_available_module_paths():\n        # Ensure the module directory is under QMK Firmware or QMK Userspace\n        relative_path = under_qmk_firmware(module_path)\n        if not relative_path:\n            relative_path = under_qmk_userspace(module_path)\n        if not relative_path:\n            continue\n\n        lhs = str(relative_path.as_posix())[len('modules/'):]\n        rhs = str(Path(module).as_posix())\n\n        if relative_path and lhs == rhs:\n            return module_path\n    return None\n\n\ndef load_module_json(module):\n    \"\"\"Load a module JSON file.\n    \"\"\"\n    module_path = find_module_path(module)\n    if not module_path:\n        raise FileNotFoundError(f'Module not found: {module}')\n\n    module_json = json_load(module_path / COMMUNITY_MODULE_JSON_FILENAME)\n\n    if not truthy(os.environ.get('SKIP_SCHEMA_VALIDATION'), False):\n        validate(module_json, 'qmk.community_module.v1')\n\n    module_json['module'] = module\n    module_json['module_path'] = module_path\n\n    return module_json\n\n\ndef load_module_jsons(modules):\n    \"\"\"Load the module JSON files, matching the specified order.\n    \"\"\"\n    return list(map(load_module_json, modules))\n"
  },
  {
    "path": "lib/python/qmk/compilation_database.py",
    "content": "\"\"\"Creates a compilation database for the given keyboard build.\n\"\"\"\n\nimport json\nimport os\nimport re\nimport shlex\nimport shutil\nfrom functools import lru_cache\nfrom pathlib import Path\nfrom typing import Dict, Iterator, List\n\nfrom milc import cli\n\nfrom qmk.commands import find_make\nfrom qmk.constants import QMK_FIRMWARE\n\n\n@lru_cache(maxsize=10)\ndef system_libs(binary: str) -> List[Path]:\n    \"\"\"Find the system include directory that the given build tool uses.\n    \"\"\"\n    cli.log.debug(\"searching for system library directory for binary: %s\", binary)\n\n    # Actually query xxxxxx-gcc to find its include paths.\n    if binary.endswith(\"gcc\") or binary.endswith(\"g++\"):\n        # (TODO): Remove 'stdin' once 'input' no longer causes issues under MSYS\n        result = cli.run([binary, '-E', '-Wp,-v', '-'], capture_output=True, check=True, stdin=None, input='\\n')\n        paths = []\n        for line in result.stderr.splitlines():\n            if line.startswith(\" \"):\n                paths.append(Path(line.strip()).resolve())\n        return paths\n\n    return list(Path(binary).resolve().parent.parent.glob(\"*/include\")) if binary else []\n\n\n@lru_cache(maxsize=10)\ndef cpu_defines(binary: str, compiler_args: str) -> List[str]:\n    cli.log.debug(\"gathering definitions for compilation: %s %s\", binary, compiler_args)\n    if binary.endswith(\"gcc\") or binary.endswith(\"g++\"):\n        invocation = [binary, '-dM', '-E']\n        if binary.endswith(\"gcc\"):\n            invocation.extend(['-x', 'c', '-std=gnu11'])\n        elif binary.endswith(\"g++\"):\n            invocation.extend(['-x', 'c++', '-std=gnu++14'])\n        invocation.extend(shlex.split(compiler_args))\n        invocation.append('-')\n        result = cli.run(invocation, capture_output=True, check=True, stdin=None, input='\\n')\n        define_args = []\n        for line in result.stdout.splitlines():\n            line_args = line.split(' ', 2)\n            if len(line_args) == 3 and line_args[0] == '#define':\n                define_args.append(f'-D{line_args[1]}={line_args[2]}')\n            elif len(line_args) == 2 and line_args[0] == '#define':\n                define_args.append(f'-D{line_args[1]}')\n\n        type_filter = re.compile(\n            r'^-D__(SIZE|INT|UINT|WINT|WCHAR|BYTE|SHRT|SIG|FLOAT|LONG|CHAR|SCHAR|DBL|FLT|LDBL|PTRDIFF|QQ|DQ|DA|HA|HQ|SA|SQ|TA|TQ|UDA|UDQ|UHA|UHQ|USQ|USA|UTQ|UTA|UQQ|UQA|ACCUM|FRACT|UACCUM|UFRACT|LACCUM|LFRACT|ULACCUM|ULFRACT|LLACCUM|LLFRACT|ULLACCUM|ULLFRACT|SACCUM|SFRACT|USACCUM|USFRACT)'\n        )\n        return list(sorted(set(filter(lambda x: not type_filter.match(x), define_args))))\n    return []\n\n\nfile_re = re.compile(r'printf \"Compiling: ([^\"]+)')\ncmd_re = re.compile(r'LOG=\\$\\((.+?)&&')\n\n\ndef parse_make_n(f: Iterator[str]) -> List[Dict[str, str]]:\n    \"\"\"parse the output of `make -n <target>`\n\n    This function makes many assumptions about the format of your build log.\n    This happens to work right now for qmk.\n    \"\"\"\n\n    state = 'start'\n    this_file = None\n    records = []\n    for line in f:\n        if state == 'start':\n            m = file_re.search(line)\n            if m:\n                this_file = m.group(1)\n                state = 'cmd'\n\n        if state == 'cmd':\n            assert this_file\n            m = cmd_re.search(line)\n            if m:\n                # we have a hit!\n                this_cmd = m.group(1)\n                args = shlex.split(this_cmd)\n                binary = shutil.which(args[0])\n                compiler_args = set(filter(lambda x: x.startswith('-m') or x.startswith('-f'), args))\n                for s in system_libs(binary):\n                    args += ['-isystem', '%s' % s]\n                args.extend(cpu_defines(binary, ' '.join(shlex.quote(s) for s in compiler_args)))\n                args[0] = binary\n                records.append({\"arguments\": args, \"directory\": str(QMK_FIRMWARE.resolve()), \"file\": this_file})\n                state = 'start'\n\n    return records\n\n\ndef write_compilation_database(keyboard: str = None, keymap: str = None, output_path: Path = QMK_FIRMWARE / 'compile_commands.json', skip_clean: bool = False, command: List[str] = None, **env_vars) -> bool:\n    # Generate the make command for a specific keyboard/keymap.\n    if not command:\n        from qmk.build_targets import KeyboardKeymapBuildTarget  # Lazy load due to circular references\n        target = KeyboardKeymapBuildTarget(keyboard, keymap)\n        command = target.compile_command(dry_run=True, **env_vars)\n\n    if not command:\n        cli.log.error('You must supply both `--keyboard` and `--keymap`, or be in a directory for a keyboard or keymap.')\n        cli.echo('usage: qmk generate-compilation-database [-kb KEYBOARD] [-km KEYMAP]')\n        return False\n\n    # remove any environment variable overrides which could trip us up\n    env = os.environ.copy()\n    env.pop(\"MAKEFLAGS\", None)\n\n    # re-use same executable as the main make invocation (might be gmake)\n    if not skip_clean:\n        clean_command = [find_make(), \"clean\"]\n        cli.log.info('Making clean with {fg_cyan}%s', ' '.join(clean_command))\n        cli.run(clean_command, capture_output=False, check=True, env=env)\n\n    cli.log.info('Gathering build instructions from {fg_cyan}%s', ' '.join(command))\n\n    result = cli.run(command, capture_output=True, check=True, env=env)\n    db = parse_make_n(result.stdout.splitlines())\n    if not db:\n        cli.log.error(\"Failed to parse output from make output:\\n%s\", result.stdout)\n        return False\n\n    cli.log.info(\"Found %s compile commands\", len(db))\n\n    cli.log.info(f\"Writing build database to {output_path}\")\n    output_path.write_text(json.dumps(db, indent=4))\n\n    return True\n"
  },
  {
    "path": "lib/python/qmk/constants.py",
    "content": "\"\"\"Information that should be available to the python library.\n\"\"\"\nfrom os import environ\nfrom datetime import date\nfrom pathlib import Path\n\nfrom qmk.userspace import detect_qmk_userspace\n\n# The root of the qmk_firmware tree.\nQMK_FIRMWARE = Path.cwd()\n\n# The detected userspace tree\nQMK_USERSPACE = detect_qmk_userspace()\n\n# Whether or not we have a separate userspace directory\nHAS_QMK_USERSPACE = True if QMK_USERSPACE is not None else False\n\n# Upstream repo url\nQMK_FIRMWARE_UPSTREAM = 'qmk/qmk_firmware'\n\n# This is the number of directories under `qmk_firmware/keyboards` that will be traversed. This is currently a limitation of our make system.\nMAX_KEYBOARD_SUBFOLDERS = 5\n\n# Supported processor types\nCHIBIOS_PROCESSORS = 'cortex-m0', 'cortex-m0plus', 'cortex-m3', 'cortex-m4', 'MKL26Z64', 'MK20DX128', 'MK20DX256', 'MK64FX512', 'MK66FX1M0', 'RP2040', 'STM32F042', 'STM32F072', 'STM32F103', 'STM32F303', 'STM32F401', 'STM32F405', 'STM32F407', 'STM32F411', 'STM32F446', 'STM32G0B1', 'STM32G431', 'STM32G474', 'STM32H723', 'STM32H733', 'STM32L412', 'STM32L422', 'STM32L432', 'STM32L433', 'STM32L442', 'STM32L443', 'GD32VF103', 'WB32F3G71', 'WB32FQ95', 'AT32F415'\nLUFA_PROCESSORS = 'at90usb162', 'atmega16u2', 'atmega32u2', 'atmega16u4', 'atmega32u4', 'at90usb646', 'at90usb647', 'at90usb1286', 'at90usb1287', None\nVUSB_PROCESSORS = 'atmega32a', 'atmega328p', 'atmega328', 'attiny85'\n\n# Bootloaders of the supported processors\nMCU2BOOTLOADER = {\n    \"RP2040\": \"rp2040\",\n    \"MKL26Z64\": \"halfkay\",\n    \"MK20DX128\": \"halfkay\",\n    \"MK20DX256\": \"halfkay\",\n    \"MK66FX1M0\": \"halfkay\",\n    \"STM32F042\": \"stm32-dfu\",\n    \"STM32F072\": \"stm32-dfu\",\n    \"STM32F103\": \"stm32duino\",\n    \"STM32F303\": \"stm32-dfu\",\n    \"STM32F401\": \"stm32-dfu\",\n    \"STM32F405\": \"stm32-dfu\",\n    \"STM32F407\": \"stm32-dfu\",\n    \"STM32F411\": \"stm32-dfu\",\n    \"STM32F446\": \"stm32-dfu\",\n    \"STM32G0B1\": \"stm32-dfu\",\n    \"STM32G431\": \"stm32-dfu\",\n    \"STM32G474\": \"stm32-dfu\",\n    \"STM32H723\": \"stm32-dfu\",\n    \"STM32H733\": \"stm32-dfu\",\n    \"STM32L412\": \"stm32-dfu\",\n    \"STM32L422\": \"stm32-dfu\",\n    \"STM32L432\": \"stm32-dfu\",\n    \"STM32L433\": \"stm32-dfu\",\n    \"STM32L442\": \"stm32-dfu\",\n    \"STM32L443\": \"stm32-dfu\",\n    \"GD32VF103\": \"gd32v-dfu\",\n    \"WB32F3G71\": \"wb32-dfu\",\n    \"WB32FQ95\": \"wb32-dfu\",\n    \"AT32F415\": \"at32-dfu\",\n    \"atmega16u2\": \"atmel-dfu\",\n    \"atmega32u2\": \"atmel-dfu\",\n    \"atmega16u4\": \"atmel-dfu\",\n    \"atmega32u4\": \"atmel-dfu\",\n    \"at90usb162\": \"atmel-dfu\",\n    \"at90usb646\": \"atmel-dfu\",\n    \"at90usb647\": \"atmel-dfu\",\n    \"at90usb1286\": \"atmel-dfu\",\n    \"at90usb1287\": \"atmel-dfu\",\n    \"atmega32a\": \"bootloadhid\",\n    \"atmega328p\": \"usbasploader\",\n    \"atmega328\": \"usbasploader\",\n}\n\n# Map of legacy keycodes that can be automatically updated\nLEGACY_KEYCODES = {  # Comment here is to force multiline formatting\n    'RESET': 'QK_BOOT'\n}\n\n# Map VID:PID values to bootloaders\nBOOTLOADER_VIDS_PIDS = {\n    'atmel-dfu': {\n        (\"03eb\", \"2fef\"),  # ATmega16U2\n        (\"03eb\", \"2ff0\"),  # ATmega32U2\n        (\"03eb\", \"2ff3\"),  # ATmega16U4\n        (\"03eb\", \"2ff4\"),  # ATmega32U4\n        (\"03eb\", \"2ff9\"),  # AT90USB64\n        (\"03eb\", \"2ffa\"),  # AT90USB162\n        (\"03eb\", \"2ffb\")  # AT90USB128\n    },\n    'kiibohd': {(\"1c11\", \"b007\")},\n    'stm32-dfu': {\n        (\"1eaf\", \"0003\"),  # STM32duino\n        (\"0483\", \"df11\")  # STM32 DFU\n    },\n    'apm32-dfu': {(\"314b\", \"0106\")},\n    'gd32v-dfu': {(\"28e9\", \"0189\")},\n    'wb32-dfu': {(\"342d\", \"dfa0\")},\n    'at32-dfu': {(\"2e3c\", \"df11\")},\n    'bootloadhid': {(\"16c0\", \"05df\")},\n    'usbasploader': {(\"16c0\", \"05dc\")},\n    'usbtinyisp': {(\"1782\", \"0c9f\")},\n    'md-boot': {(\"03eb\", \"6124\")},\n    'caterina': {\n        # pid.codes shared PID\n        (\"1209\", \"2302\"),  # Keyboardio Atreus 2 Bootloader\n        # Spark Fun Electronics\n        (\"1b4f\", \"9203\"),  # Pro Micro 3V3/8MHz\n        (\"1b4f\", \"9205\"),  # Pro Micro 5V/16MHz\n        (\"1b4f\", \"9207\"),  # LilyPad 3V3/8MHz (and some Pro Micro clones)\n        # Pololu Electronics\n        (\"1ffb\", \"0101\"),  # A-Star 32U4\n        # Arduino SA\n        (\"2341\", \"0036\"),  # Leonardo\n        (\"2341\", \"0037\"),  # Micro\n        # Adafruit Industries LLC\n        (\"239a\", \"000c\"),  # Feather 32U4\n        (\"239a\", \"000d\"),  # ItsyBitsy 32U4 3V3/8MHz\n        (\"239a\", \"000e\"),  # ItsyBitsy 32U4 5V/16MHz\n        # dog hunter AG\n        (\"2a03\", \"0036\"),  # Leonardo\n        (\"2a03\", \"0037\")  # Micro\n    },\n    'hid-bootloader': {\n        (\"03eb\", \"2067\"),  # QMK HID\n        (\"16c0\", \"0478\")  # PJRC halfkay\n    }\n}\n\n# Common format strings\nDATE_FORMAT = '%Y-%m-%d'\nDATETIME_FORMAT = '%Y-%m-%d %H:%M:%S %Z'\nTIME_FORMAT = '%H:%M:%S'\n\n# Used when generating matrix locations\nCOL_LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijilmnopqrstuvwxyz'\nROW_LETTERS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop'\n\n# Constants that should match their counterparts in make\nBUILD_DIR = environ.get('BUILD_DIR', '.build')\nINTERMEDIATE_OUTPUT_PREFIX = f'{BUILD_DIR}/obj_'\n\n# Headers for generated files\nGPL2_HEADER_C_LIKE = f'''\\\n// Copyright {date.today().year} QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n'''\n\nGPL2_HEADER_SH_LIKE = f'''\\\n# Copyright {date.today().year} QMK\n# SPDX-License-Identifier: GPL-2.0-or-later\n'''\n\nGENERATED_HEADER_C_LIKE = '''\\\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n'''\n\nGENERATED_HEADER_SH_LIKE = '''\\\n################################################################################\n#\n# 88888888888 888      d8b                .d888 d8b 888               d8b\n#     888     888      Y8P               d88P\"  Y8P 888               Y8P\n#     888     888                        888        888\n#     888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n#     888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n#     888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n#     888     888  888 888      X88      888    888 888 Y8b.          888      X88\n#     888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n#\n#                                                       888                 888\n#                                                       888                 888\n#                                                       888                 888\n#    .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n#   d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n#   888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n#   Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n#    \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n#        888\n#   Y8b d88P\n#    \"Y88P\"\n#\n################################################################################\n'''\n\nLICENSE_TEXTS = [\n    (\n        'GPL-2.0-or-later', [\n            \"\"\"\\\n        This program is free software; you can redistribute it and/or\n        modify it under the terms of the GNU General Public License\n        as published by the Free Software Foundation; either version 2\n        of the License, or (at your option) any later version.\n        \"\"\", \"\"\"\\\n        This program is free software; you can redistribute it and/or\n        modify it under the terms of the GNU General Public License\n        as published by the Free Software Foundation; either version 2\n        of the License, or any later version.\n        \"\"\"\n        ]\n    ),\n    ('GPL-2.0-only', [\"\"\"\\\n        This program is free software; you can redistribute it and/or\n        modify it under the terms of the GNU General Public License as\n        published by the Free Software Foundation; version 2.\n        \"\"\"]),\n    (\n        'GPL-3.0-or-later', [\n            \"\"\"\\\n        This program is free software: you can redistribute it and/or\n        modify it under the terms of the GNU General Public License as\n        published by the Free Software Foundation, either version 3 of\n        the License, or (at your option) any later version.\n        \"\"\", \"\"\"\\\n        This program is free software: you can redistribute it and/or\n        modify it under the terms of the GNU General Public License as\n        published by the Free Software Foundation, either version 3 of\n        the License, or any later version.\n        \"\"\"\n        ]\n    ),\n    ('GPL-3.0-only', [\"\"\"\\\n        This program is free software: you can redistribute it and/or\n        modify it under the terms of the GNU General Public License as\n        published by the Free Software Foundation, version 3.\n        \"\"\"]),\n    (\n        'LGPL-2.1-or-later', [\n            \"\"\"\\\n        This program is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License\n        as published by the Free Software Foundation; either version 2.1\n        of the License, or (at your option) any later version.\n        \"\"\", \"\"\"\\\n        This program is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License\n        as published by the Free Software Foundation; either version 2.1\n        of the License, or any later version.\n        \"\"\", \"\"\"\\\n        This library is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License\n        as published by the Free Software Foundation; either version 2.1\n        of the License, or (at your option) any later version.\n        \"\"\", \"\"\"\\\n        This library is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License\n        as published by the Free Software Foundation; either version 2.1\n        of the License, or any later version.\n        \"\"\"\n        ]\n    ),\n    (\n        'LGPL-2.1-only', [\n            \"\"\"\\\n        This program is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License as\n        published by the Free Software Foundation; version 2.1.\n        \"\"\", \"\"\"\\\n        This library is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License as\n        published by the Free Software Foundation; version 2.1.\n        \"\"\"\n        ]\n    ),\n    (\n        'LGPL-3.0-or-later', [\n            \"\"\"\\\n        This program is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License\n        as published by the Free Software Foundation; either version 3\n        of the License, or (at your option) any later version.\n        \"\"\", \"\"\"\\\n        This program is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License\n        as published by the Free Software Foundation; either version 3\n        of the License, or any later version.\n        \"\"\", \"\"\"\\\n        This library is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License\n        as published by the Free Software Foundation; either version 3\n        of the License, or (at your option) any later version.\n        \"\"\", \"\"\"\\\n        This library is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License\n        as published by the Free Software Foundation; either version 3\n        of the License, or any later version.\n        \"\"\"\n        ]\n    ),\n    (\n        'LGPL-3.0-only', [\n            \"\"\"\\\n        This program is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License as\n        published by the Free Software Foundation; version 3.\n        \"\"\", \"\"\"\\\n        This library is free software; you can redistribute it and/or\n        modify it under the terms of the GNU Lesser General Public License as\n        published by the Free Software Foundation; version 3.\n        \"\"\"\n        ]\n    ),\n    ('Apache-2.0', [\"\"\"\\\n        Licensed under the Apache License, Version 2.0 (the \"License\");\n        you may not use this file except in compliance with the License.\n        \"\"\"]),\n]\n\nJOYSTICK_AXES = ['x', 'y', 'z', 'rx', 'ry', 'rz']\n"
  },
  {
    "path": "lib/python/qmk/converter.py",
    "content": "\"\"\"Functions to convert to and from QMK formats\n\"\"\"\nfrom collections import OrderedDict\n\n\ndef kle2qmk(kle):\n    \"\"\"Convert a KLE layout to QMK's layout format.\n    \"\"\"\n    layout = []\n\n    for row in kle:\n        for key in row:\n            if key['decal']:\n                continue\n\n            qmk_key = OrderedDict(\n                label=\"\",\n                x=key['column'],\n                y=key['row'],\n            )\n\n            if key['width'] != 1:\n                qmk_key['w'] = key['width']\n            if key['height'] != 1:\n                qmk_key['h'] = key['height']\n            if 'name' in key and key['name']:\n                qmk_key['label'] = key['name'].split('\\n', 1)[0]\n            else:\n                del (qmk_key['label'])\n\n            layout.append(qmk_key)\n\n    return layout\n"
  },
  {
    "path": "lib/python/qmk/datetime.py",
    "content": "\"\"\"Functions to work with dates and times in a uniform way.\n\nThe results of these functions are cached for 5 seconds to provide uniform time strings across short running processes. Long running processes that need more precise timekeeping should not use these functions.\n\"\"\"\nfrom time import gmtime, strftime\n\nfrom qmk.constants import DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT\nfrom qmk.decorators import lru_cache\n\n\n@lru_cache(timeout=5)\ndef current_date():\n    \"\"\"Returns the current time in UTZ as a formatted string.\n    \"\"\"\n    return strftime(DATE_FORMAT, gmtime())\n\n\n@lru_cache(timeout=5)\ndef current_datetime():\n    \"\"\"Returns the current time in UTZ as a formatted string.\n    \"\"\"\n    return strftime(DATETIME_FORMAT, gmtime())\n\n\n@lru_cache(timeout=5)\ndef current_time():\n    \"\"\"Returns the current time in UTZ as a formatted string.\n    \"\"\"\n    return strftime(TIME_FORMAT, gmtime())\n"
  },
  {
    "path": "lib/python/qmk/decorators.py",
    "content": "\"\"\"Helpful decorators that subcommands can use.\n\"\"\"\nimport functools\nfrom time import monotonic\n\nfrom milc import cli\n\nfrom qmk.keyboard import find_keyboard_from_dir, keyboard_folder\nfrom qmk.keymap import find_keymap_from_dir\n\n\ndef _get_subcommand_name():\n    \"\"\"Handle missing cli.subcommand_name on older versions of milc\n    \"\"\"\n    try:\n        return cli.subcommand_name\n    except AttributeError:\n        return cli._subcommand.__name__\n\n\ndef automagic_keyboard(func):\n    \"\"\"Sets `cli.config.<subcommand>.keyboard` based on environment.\n\n    This will rewrite cli.config.<subcommand>.keyboard if the user did not pass `--keyboard` and the directory they are currently in is a keyboard or keymap directory.\n    \"\"\"\n    @functools.wraps(func)\n    def wrapper(*args, **kwargs):\n        cmd = _get_subcommand_name()\n\n        # TODO: Workaround for if config file contains \"old\" keyboard name\n        #         Potential long-term fix needs to be within global cli or milc\n        if cli.config_source[cmd]['keyboard'] == 'config_file':\n            cli.config[cmd]['keyboard'] = keyboard_folder(cli.config[cmd]['keyboard'])\n\n        # Ensure that `--keyboard` was not passed and CWD is under `qmk_firmware/keyboards`\n        if cli.config_source[cmd]['keyboard'] != 'argument':\n            keyboard = find_keyboard_from_dir()\n\n            if keyboard:\n                cli.config[cmd]['keyboard'] = keyboard\n                cli.config_source[cmd]['keyboard'] = 'keyboard_directory'\n\n        return func(*args, **kwargs)\n\n    return wrapper\n\n\ndef automagic_keymap(func):\n    \"\"\"Sets `cli.config.<subcommand>.keymap` based on environment.\n\n    This will rewrite cli.config.<subcommand>.keymap if the user did not pass `--keymap` and the directory they are currently in is a keymap, layout, or user directory.\n    \"\"\"\n    @functools.wraps(func)\n    def wrapper(*args, **kwargs):\n        cmd = _get_subcommand_name()\n\n        # Ensure that `--keymap` was not passed and that we're under `qmk_firmware`\n        if cli.config_source[cmd]['keymap'] != 'argument':\n            keymap_name, keymap_type = find_keymap_from_dir()\n\n            if keymap_name:\n                cli.config[cmd]['keymap'] = keymap_name\n                cli.config_source[cmd]['keymap'] = keymap_type\n\n        return func(*args, **kwargs)\n\n    return wrapper\n\n\ndef lru_cache(timeout=10, maxsize=128, typed=False):\n    \"\"\"Least Recently Used Cache- cache the result of a function.\n\n    Args:\n\n        timeout\n            How many seconds to cache results for.\n\n        maxsize\n            The maximum size of the cache in bytes\n\n        typed\n            When `True` argument types will be taken into consideration, for example `3` and `3.0` will be treated as different keys.\n    \"\"\"\n    def wrapper_cache(func):\n        func = functools.lru_cache(maxsize=maxsize, typed=typed)(func)\n        func.expiration = monotonic() + timeout\n\n        @functools.wraps(func)\n        def wrapped_func(*args, **kwargs):\n            if monotonic() >= func.expiration:\n                func.expiration = monotonic() + timeout\n\n                func.cache_clear()\n\n            return func(*args, **kwargs)\n\n        wrapped_func.cache_info = func.cache_info\n        wrapped_func.cache_clear = func.cache_clear\n\n        return wrapped_func\n\n    return wrapper_cache\n"
  },
  {
    "path": "lib/python/qmk/docs.py",
    "content": "\"\"\"Handlers for the QMK documentation generator (docusaurus).\n\"\"\"\nimport shutil\nfrom pathlib import Path\nfrom subprocess import DEVNULL\nfrom os import chdir, environ, makedirs, pathsep\nfrom milc import cli\n\nfrom qmk.constants import QMK_FIRMWARE\n\nDOCS_PATH = QMK_FIRMWARE / 'docs'\nBUILDDEFS_PATH = QMK_FIRMWARE / 'builddefs' / 'docsgen'\nBUILD_PATH = QMK_FIRMWARE / '.build'\nCACHE_PATH = BUILD_PATH / 'cache'\nNODE_MODULES_PATH = BUILD_PATH / 'node_modules'\nBUILD_DOCS_PATH = BUILD_PATH / 'docs'\nDOXYGEN_PATH = BUILD_DOCS_PATH / 'static' / 'doxygen'\n\n\ndef run_docs_command(verb, cmd_args=None):\n    environ['PATH'] += pathsep + str(NODE_MODULES_PATH / '.bin')\n\n    args = {'capture_output': False, 'check': True}\n    docs_env = environ.copy()\n    if cli.config.general.verbose:\n        docs_env['DEBUG'] = 'vitepress:*,vite:*'\n    args['env'] = docs_env\n\n    arg_list = ['yarn', verb]\n    if cmd_args:\n        arg_list.extend(cmd_args)\n\n    chdir(BUILDDEFS_PATH)\n    cli.run(arg_list, **args)\n\n\ndef prepare_docs_build_area(is_production):\n    if is_production:\n        # Set up a symlink for docs to be inside builddefs -- vitepress can't handle source files in parent directories\n        try:\n            docs_link = Path(BUILDDEFS_PATH / 'docs')\n            if not docs_link.exists():\n                docs_link.symlink_to(DOCS_PATH)\n        except NotImplementedError:\n            cli.log.error('Symlinks are not supported on this platform.')\n            return False\n\n    if BUILD_DOCS_PATH.exists():\n        shutil.rmtree(BUILD_DOCS_PATH)\n\n    # When not verbose we want to hide all output\n    args = {'capture_output': False if cli.config.general.verbose else True, 'check': True, 'stdin': DEVNULL}\n\n    makedirs(DOXYGEN_PATH)\n    cli.log.info('Generating doxygen docs at %s', DOXYGEN_PATH)\n    cli.run(['doxygen', 'Doxyfile'], **args)\n\n    cli.log.info('Installing vitepress dependencies')\n    run_docs_command('install')\n\n    return True\n"
  },
  {
    "path": "lib/python/qmk/errors.py",
    "content": "class NoSuchKeyboardError(Exception):\n    \"\"\"Raised when we can't find a keyboard/keymap directory.\n    \"\"\"\n    def __init__(self, message):\n        self.message = message\n\n\nclass CppError(Exception):\n    \"\"\"Raised when 'cpp' cannot process a file.\n    \"\"\"\n    def __init__(self, message):\n        self.message = message\n"
  },
  {
    "path": "lib/python/qmk/flashers.py",
    "content": "import platform\nimport shutil\nimport time\nimport os\nimport signal\n\nimport usb.core\n\nfrom qmk.constants import BOOTLOADER_VIDS_PIDS\nfrom milc import cli\n\n# yapf: disable\n_PID_TO_MCU = {\n    '2fef': 'atmega16u2',\n    '2ff0': 'atmega32u2',\n    '2ff3': 'atmega16u4',\n    '2ff4': 'atmega32u4',\n    '2ff9': 'at90usb64',\n    '2ffa': 'at90usb162',\n    '2ffb': 'at90usb128'\n}\n\nAVRDUDE_MCU = {\n    'atmega32a': 'm32',\n    'atmega328p': 'm328p',\n    'atmega328': 'm328',\n}\n# yapf: enable\n\n\nclass DelayedKeyboardInterrupt:\n    # Custom interrupt handler to delay the processing of Ctrl-C\n    # https://stackoverflow.com/a/21919644\n    def __enter__(self):\n        self.signal_received = False\n        self.old_handler = signal.signal(signal.SIGINT, self.handler)\n\n    def handler(self, sig, frame):\n        self.signal_received = (sig, frame)\n\n    def __exit__(self, type, value, traceback):\n        signal.signal(signal.SIGINT, self.old_handler)\n        if self.signal_received:\n            self.old_handler(*self.signal_received)\n\n\n# TODO: Make this more generic, so cli/doctor/check.py and flashers.py can share the code\ndef _check_dfu_programmer_version():\n    # Return True if version is higher than 0.7.0: supports '--force'\n    check = cli.run(['dfu-programmer', '--version'], combined_output=True, timeout=5)\n    first_line = check.stdout.split('\\n')[0]\n    version_number = first_line.split()[1]\n    maj, min_, bug = version_number.split('.')\n    if int(maj) >= 0 and int(min_) >= 7:\n        return True\n    else:\n        return False\n\n\ndef _find_usb_device(vid_hex, pid_hex):\n    # WSL doesnt have access to USB - use powershell instead...?\n    if 'microsoft' in platform.uname().release.lower():\n        ret = cli.run(['powershell.exe', '-command', 'Get-PnpDevice -PresentOnly | Select-Object -Property InstanceId'])\n        if f'USB\\\\VID_{vid_hex:04X}&PID_{pid_hex:04X}' in ret.stdout:\n            return (vid_hex, pid_hex)\n    else:\n        with DelayedKeyboardInterrupt():\n            # PyUSB does not like to be interrupted by Ctrl-C\n            # therefore we catch the interrupt with a custom handler\n            # and only process it once pyusb finished\n            return usb.core.find(idVendor=vid_hex, idProduct=pid_hex)\n\n\ndef _find_uf2_devices():\n    \"\"\"Delegate to uf2conv.py as VID:PID pairs can potentially fluctuate more than other bootloaders\n    \"\"\"\n    return cli.run(['util/uf2conv.py', '--list']).stdout.splitlines()\n\n\ndef _find_bootloader():\n    # To avoid running forever in the background, only look for bootloaders for 10min\n    start_time = time.time()\n    while time.time() - start_time < 600:\n        for bl in BOOTLOADER_VIDS_PIDS:\n            for vid, pid in BOOTLOADER_VIDS_PIDS[bl]:\n                vid_hex = int(f'0x{vid}', 0)\n                pid_hex = int(f'0x{pid}', 0)\n                dev = _find_usb_device(vid_hex, pid_hex)\n                if dev:\n                    if bl == 'atmel-dfu':\n                        details = _PID_TO_MCU[pid]\n                    elif bl == 'caterina':\n                        details = (vid_hex, pid_hex)\n                    elif bl == 'hid-bootloader':\n                        if vid == '16c0' and pid == '0478':\n                            details = 'halfkay'\n                        else:\n                            details = 'qmk-hid'\n                    elif bl in {'apm32-dfu', 'at32-dfu', 'gd32v-dfu', 'kiibohd', 'stm32-dfu'}:\n                        details = (vid, pid)\n                    else:\n                        details = None\n                    return (bl, details)\n        if _find_uf2_devices():\n            return ('_uf2_compatible_', None)\n        time.sleep(0.1)\n    return (None, None)\n\n\ndef _find_serial_port(vid, pid):\n    if 'windows' in cli.platform.lower():\n        from serial.tools.list_ports_windows import comports\n        platform = 'windows'\n    else:\n        from serial.tools.list_ports_posix import comports\n        platform = 'posix'\n\n    start_time = time.time()\n    # Caterina times out after 8 seconds\n    while time.time() - start_time < 8:\n        for port in comports():\n            port, desc, hwid = port\n            if f'{vid:04x}:{pid:04x}' in hwid.casefold():\n                if platform == 'windows':\n                    time.sleep(1)\n                    return port\n                else:\n                    start_time = time.time()\n                    # Wait until the port becomes writable before returning\n                    while time.time() - start_time < 8:\n                        if os.access(port, os.W_OK):\n                            return port\n                        else:\n                            time.sleep(0.5)\n                return None\n    return None\n\n\ndef _flash_caterina(details, file):\n    port = _find_serial_port(details[0], details[1])\n    if port:\n        cli.run(['avrdude', '-p', 'atmega32u4', '-c', 'avr109', '-U', f'flash:w:{file}:i', '-P', port], capture_output=False)\n        return False\n    else:\n        return True\n\n\ndef _flash_atmel_dfu(mcu, file):\n    force = '--force' if _check_dfu_programmer_version() else ''\n    cli.run(['dfu-programmer', mcu, 'erase', force], capture_output=False)\n    cli.run(['dfu-programmer', mcu, 'flash', force, file], capture_output=False)\n    cli.run(['dfu-programmer', mcu, 'reset'], capture_output=False)\n\n\ndef _flash_hid_bootloader(mcu, details, file):\n    cmd = None\n    if details == 'halfkay':\n        if shutil.which('teensy_loader_cli'):\n            cmd = 'teensy_loader_cli'\n        elif shutil.which('teensy-loader-cli'):\n            cmd = 'teensy-loader-cli'\n\n    # Use 'hid_bootloader_cli' for QMK HID and as a fallback for HalfKay\n    if not cmd:\n        if shutil.which('hid_bootloader_cli'):\n            cmd = 'hid_bootloader_cli'\n        else:\n            return True\n\n    cli.run([cmd, f'-mmcu={mcu}', '-w', '-v', file], capture_output=False)\n\n\ndef _flash_dfu_util(details, file):\n    # STM32duino\n    if details[0] == '1eaf' and details[1] == '0003':\n        cli.run(['dfu-util', '-a', '2', '-d', f'{details[0]}:{details[1]}', '-R', '-D', file], capture_output=False)\n    # kiibohd\n    elif details[0] == '1c11' and details[1] == 'b007':\n        cli.run(['dfu-util', '-a', '0', '-d', f'{details[0]}:{details[1]}', '-D', file], capture_output=False)\n    # STM32, APM32, AT32, or GD32V DFU\n    else:\n        cli.run(['dfu-util', '-a', '0', '-d', f'{details[0]}:{details[1]}', '-s', '0x08000000:leave', '-D', file], capture_output=False)\n\n\ndef _flash_wb32_dfu_updater(file):\n    if shutil.which('wb32-dfu-updater_cli'):\n        cmd = 'wb32-dfu-updater_cli'\n    else:\n        return True\n\n    cli.run([cmd, '-t', '-s', '0x08000000', '-D', file], capture_output=False)\n\n\ndef _flash_isp(mcu, programmer, file):\n    programmer = 'usbasp' if programmer == 'usbasploader' else 'usbtiny'\n    # Check if the provided mcu has an avrdude-specific name, otherwise pass on what the user provided\n    mcu = AVRDUDE_MCU.get(mcu, mcu)\n    cli.run(['avrdude', '-p', mcu, '-c', programmer, '-U', f'flash:w:{file}:i'], capture_output=False)\n\n\ndef _flash_mdloader(file):\n    cli.run(['mdloader', '--first', '--download', file, '--restart'], capture_output=False)\n\n\ndef _flash_uf2(file):\n    output = cli.run(['util/uf2conv.py', '--info', file]).stdout\n    if 'UF2 File' not in output:\n        return True\n\n    cli.run(['util/uf2conv.py', '--deploy', file], capture_output=False)\n\n\ndef flasher(mcu, file):\n    # Avoid \"expected string or bytes-like object, got 'WindowsPath\" issues\n    file = file.as_posix()\n    bl, details = _find_bootloader()\n    # Add a small sleep to avoid race conditions\n    time.sleep(1)\n    if bl == 'atmel-dfu':\n        _flash_atmel_dfu(details, file)\n    elif bl == 'caterina':\n        if _flash_caterina(details, file):\n            return (True, \"The Caterina bootloader was found but is not writable. Check 'qmk doctor' output for advice.\")\n    elif bl == 'hid-bootloader':\n        if mcu:\n            if _flash_hid_bootloader(mcu, details, file):\n                return (True, \"Please make sure 'teensy_loader_cli' or 'hid_bootloader_cli' is available on your system.\")\n        else:\n            return (True, \"Specifying the MCU with '-m' is necessary for HalfKay/HID bootloaders!\")\n    elif bl in {'apm32-dfu', 'at32-dfu', 'gd32v-dfu', 'kiibohd', 'stm32-dfu'}:\n        _flash_dfu_util(details, file)\n    elif bl == 'wb32-dfu':\n        if _flash_wb32_dfu_updater(file):\n            return (True, \"Please make sure 'wb32-dfu-updater_cli' is available on your system.\")\n    elif bl == 'usbasploader' or bl == 'usbtinyisp':\n        if mcu:\n            _flash_isp(mcu, bl, file)\n        else:\n            return (True, \"Specifying the MCU with '-m' is necessary for ISP flashing!\")\n    elif bl == 'md-boot':\n        _flash_mdloader(file)\n    elif bl == '_uf2_compatible_':\n        if _flash_uf2(file):\n            return (True, \"Flashing only supports uf2 format files.\")\n    else:\n        return (True, \"Known bootloader found but flashing not currently supported!\")\n\n    return (False, None)\n"
  },
  {
    "path": "lib/python/qmk/git.py",
    "content": "\"\"\"Functions for working with the QMK repo.\n\"\"\"\nfrom subprocess import DEVNULL\nfrom pathlib import Path\n\nfrom milc import cli\n\nfrom qmk.constants import QMK_FIRMWARE\n\n\ndef git_get_version(repo_dir='.', check_dir='.'):\n    \"\"\"Returns the current git version for a repo, or None.\n    \"\"\"\n    git_describe_cmd = ['git', 'describe', '--abbrev=6', '--dirty', '--always', '--tags']\n\n    if repo_dir != '.':\n        repo_dir = Path('lib') / repo_dir\n\n    if check_dir != '.':\n        check_dir = repo_dir / check_dir\n\n    if Path(check_dir).exists():\n        git_describe = cli.run(git_describe_cmd, stdin=DEVNULL, cwd=repo_dir)\n\n        if git_describe.returncode == 0:\n            return git_describe.stdout.strip()\n\n        else:\n            cli.log.warning(f'\"{\" \".join(git_describe_cmd)}\" returned error code {git_describe.returncode}')\n            print(git_describe.stderr)\n            return None\n\n    return None\n\n\ndef git_get_username():\n    \"\"\"Retrieves user's username from Git config, if set.\n    \"\"\"\n    git_username = cli.run(['git', 'config', '--get', 'user.name'])\n\n    if git_username.returncode == 0 and git_username.stdout:\n        return git_username.stdout.strip()\n\n\ndef git_get_branch():\n    \"\"\"Returns the current branch for a repo, or None.\n    \"\"\"\n    git_branch = cli.run(['git', 'branch', '--show-current'])\n    if not git_branch.returncode != 0 or not git_branch.stdout:\n        # Workaround for Git pre-2.22\n        git_branch = cli.run(['git', 'rev-parse', '--abbrev-ref', 'HEAD'])\n\n    if git_branch.returncode == 0:\n        return git_branch.stdout.strip()\n\n\ndef git_get_tag():\n    \"\"\"Returns the current tag for a repo, or None.\n    \"\"\"\n    git_tag = cli.run(['git', 'describe', '--abbrev=0', '--tags'])\n    if git_tag.returncode == 0:\n        return git_tag.stdout.strip()\n\n\ndef git_get_last_log_entry(branch_name):\n    \"\"\"Retrieves the last log entry for the branch being worked on.\n    \"\"\"\n    git_lastlog = cli.run(['git', '--no-pager', 'log', '--pretty=format:%ad (%h) -- %s', '--date=iso', '-n1', branch_name])\n\n    if git_lastlog.returncode == 0 and git_lastlog.stdout:\n        return git_lastlog.stdout.strip()\n\n\ndef git_get_common_ancestor(branch_a, branch_b):\n    \"\"\"Retrieves the common ancestor between for the two supplied branches.\n    \"\"\"\n    git_merge_base = cli.run(['git', 'merge-base', branch_a, branch_b])\n    git_branchpoint_log = cli.run(['git', '--no-pager', 'log', '--pretty=format:%ad (%h) -- %s', '--date=iso', '-n1', git_merge_base.stdout.strip()])\n\n    if git_branchpoint_log.returncode == 0 and git_branchpoint_log.stdout:\n        return git_branchpoint_log.stdout.strip()\n\n\ndef git_get_remotes():\n    \"\"\"Returns the current remotes for a repo.\n    \"\"\"\n    remotes = {}\n\n    git_remote_show_cmd = ['git', 'remote', 'show']\n    git_remote_get_cmd = ['git', 'remote', 'get-url']\n\n    git_remote_show = cli.run(git_remote_show_cmd)\n    if git_remote_show.returncode == 0:\n        for name in git_remote_show.stdout.splitlines():\n            git_remote_name = cli.run([*git_remote_get_cmd, name])\n            remotes[name.strip()] = {\"url\": git_remote_name.stdout.strip()}\n\n    return remotes\n\n\ndef git_is_dirty():\n    \"\"\"Returns 1 if repo is dirty, or 0 if clean\n    \"\"\"\n    git_diff_staged_cmd = ['git', 'diff', '--quiet']\n    git_diff_unstaged_cmd = [*git_diff_staged_cmd, '--cached']\n\n    unstaged = cli.run(git_diff_staged_cmd)\n    staged = cli.run(git_diff_unstaged_cmd)\n\n    return unstaged.returncode != 0 or staged.returncode != 0\n\n\ndef git_check_repo():\n    \"\"\"Checks that the .git directory exists inside QMK_HOME.\n\n    This is a decent enough indicator that the qmk_firmware directory is a\n    proper Git repository, rather than a .zip download from GitHub.\n    \"\"\"\n    dot_git_dir = QMK_FIRMWARE / '.git'\n\n    return dot_git_dir.is_dir()\n\n\ndef git_check_deviation(active_branch):\n    \"\"\"Return True if branch has custom commits\n    \"\"\"\n    cli.run(['git', 'fetch', 'upstream', active_branch])\n    deviations = cli.run(['git', '--no-pager', 'log', f'upstream/{active_branch}...{active_branch}'])\n    return bool(deviations.returncode)\n\n\ndef git_get_ignored_files(check_dir='.'):\n    \"\"\"Return a list of files that would be captured by the current .gitignore\n    \"\"\"\n    invalid = cli.run(['git', 'ls-files', '-c', '-o', '-i', '--exclude-from=.gitignore', check_dir])\n    if invalid.returncode != 0:\n        return []\n    return invalid.stdout.strip().splitlines()\n\n\ndef git_get_qmk_hash():\n    output = cli.run(['git', 'rev-parse', '--short', 'HEAD'])\n    if output.returncode != 0:\n        return None\n\n    return output.stdout.strip()\n"
  },
  {
    "path": "lib/python/qmk/importers.py",
    "content": "from dotty_dict import dotty\nfrom datetime import date\nfrom pathlib import Path\nimport json\n\nfrom qmk.git import git_get_username\nfrom qmk.json_schema import validate\nfrom qmk.path import keyboard, keymaps\nfrom qmk.constants import MCU2BOOTLOADER, LEGACY_KEYCODES\nfrom qmk.json_encoders import InfoJSONEncoder, KeymapJSONEncoder\nfrom qmk.json_schema import deep_update, json_load\n\nTEMPLATE = Path('data/templates/keyboard/')\n\n\ndef replace_placeholders(src, dest, tokens):\n    \"\"\"Replaces the given placeholders in each template file.\n    \"\"\"\n    content = src.read_text()\n    for key, value in tokens.items():\n        content = content.replace(f'%{key}%', value)\n\n    dest.write_text(content)\n\n\ndef _gen_dummy_keymap(name, info_data):\n    # Pick the first layout macro and just dump in KC_NOs or something?\n    (layout_name, layout_data), *_ = info_data[\"layouts\"].items()\n    layout_length = len(layout_data[\"layout\"])\n\n    keymap_data = {\n        \"keyboard\": name,\n        \"layout\": layout_name,\n        \"layers\": [[\"KC_NO\" for _ in range(0, layout_length)]],\n    }\n\n    return keymap_data\n\n\ndef _extract_kbfirmware_layout(kbf_data):\n    layout = []\n    for key in kbf_data['keyboard.keys']:\n        item = {\n            'matrix': [key['row'], key['col']],\n            'x': key['state']['x'],\n            'y': key['state']['y'],\n        }\n        if key['state']['w'] != 1:\n            item['w'] = key['state']['w']\n        if key['state']['h'] != 1:\n            item['h'] = key['state']['h']\n        layout.append(item)\n\n    return layout\n\n\ndef _extract_kbfirmware_keymap(kbf_data):\n    keymap_data = {\n        'keyboard': kbf_data['keyboard.settings.name'].lower(),\n        'layout': 'LAYOUT',\n        'layers': [],\n    }\n\n    for i in range(15):\n        layer = []\n        for key in kbf_data['keyboard.keys']:\n            keycode = key['keycodes'][i]['id']\n            keycode = LEGACY_KEYCODES.get(keycode, keycode)\n            if '()' in keycode:\n                fields = key['keycodes'][i]['fields']\n                keycode = f'{keycode.split(\")\")[0]}{\",\".join(map(str, fields))})'\n            layer.append(keycode)\n        if set(layer) == {'KC_TRNS'}:\n            break\n        keymap_data['layers'].append(layer)\n\n    return keymap_data\n\n\ndef import_keymap(keymap_data):\n    # Validate to ensure we don't have to deal with bad data - handles stdin/file\n    validate(keymap_data, 'qmk.keymap.v1')\n\n    kb_name = keymap_data['keyboard']\n    km_name = keymap_data['keymap']\n\n    km_folder = keymaps(kb_name)[0] / km_name\n    keyboard_keymap = km_folder / 'keymap.json'\n\n    # This is the deepest folder in the expected tree\n    keyboard_keymap.parent.mkdir(parents=True, exist_ok=True)\n\n    # Dump out all those lovely files\n    keyboard_keymap.write_text(json.dumps(keymap_data, cls=KeymapJSONEncoder, sort_keys=True))\n\n    return (kb_name, km_name)\n\n\ndef import_keyboard(info_data, keymap_data=None):\n    # Validate to ensure we don't have to deal with bad data - handles stdin/file\n    validate(info_data, 'qmk.api.keyboard.v1')\n\n    # And validate some more as everything is optional\n    if not all(key in info_data for key in ['keyboard_name', 'layouts']):\n        raise ValueError('invalid json config')\n\n    kb_name = info_data['keyboard_name']\n\n    # bail\n    kb_folder = keyboard(kb_name)\n    if kb_folder.exists():\n        raise ValueError(f'Keyboard {{fg_cyan}}{kb_name}{{fg_reset}} already exists! Please choose a different name.')\n\n    if not keymap_data:\n        # TODO: if supports community then grab that instead\n        keymap_data = _gen_dummy_keymap(kb_name, info_data)\n\n    keyboard_json = kb_folder / 'keyboard.json'\n    keyboard_keymap = kb_folder / 'keymaps' / 'default' / 'keymap.json'\n\n    # begin with making the deepest folder in the tree\n    keyboard_keymap.parent.mkdir(parents=True, exist_ok=True)\n\n    user_name = git_get_username()\n    if not user_name:\n        user_name = 'TODO'\n\n    tokens = {  # Comment here is to force multiline formatting\n        'YEAR': str(date.today().year),\n        'KEYBOARD': kb_name,\n        'USER_NAME': user_name,\n        'REAL_NAME': user_name,\n    }\n\n    # Dump out all those lovely files\n    for file in list(TEMPLATE.iterdir()):\n        replace_placeholders(file, kb_folder / file.name, tokens)\n\n    temp = json_load(keyboard_json)\n    deep_update(temp, info_data)\n\n    keyboard_json.write_text(json.dumps(temp, cls=InfoJSONEncoder, sort_keys=True))\n    keyboard_keymap.write_text(json.dumps(keymap_data, cls=KeymapJSONEncoder, sort_keys=True))\n\n    return kb_name\n\n\ndef import_kbfirmware(kbfirmware_data):\n    kbf_data = dotty(kbfirmware_data)\n\n    diode_direction = [\"COL2ROW\", \"ROW2COL\"][kbf_data['keyboard.settings.diodeDirection']]\n    mcu = [\"atmega32u2\", \"atmega32u4\", \"at90usb1286\"][kbf_data['keyboard.controller']]\n    bootloader = MCU2BOOTLOADER.get(mcu, \"custom\")\n\n    layout = _extract_kbfirmware_layout(kbf_data)\n    keymap_data = _extract_kbfirmware_keymap(kbf_data)\n\n    # convert to d/d info.json\n    info_data = dotty({\n        \"keyboard_name\": kbf_data['keyboard.settings.name'].lower(),\n        \"processor\": mcu,\n        \"bootloader\": bootloader,\n        \"diode_direction\": diode_direction,\n        \"matrix_pins\": {\n            \"cols\": kbf_data['keyboard.pins.col'],\n            \"rows\": kbf_data['keyboard.pins.row'],\n        },\n        \"layouts\": {\n            \"LAYOUT\": {\n                \"layout\": layout,\n            }\n        }\n    })\n\n    if kbf_data['keyboard.pins.num'] or kbf_data['keyboard.pins.caps'] or kbf_data['keyboard.pins.scroll']:\n        if kbf_data['keyboard.pins.num']:\n            info_data['indicators.num_lock'] = kbf_data['keyboard.pins.num']\n        if kbf_data['keyboard.pins.caps']:\n            info_data['indicators.caps_lock'] = kbf_data['keyboard.pins.caps']\n        if kbf_data['keyboard.pins.scroll']:\n            info_data['indicators.scroll_lock'] = kbf_data['keyboard.pins.scroll']\n\n    if kbf_data['keyboard.pins.rgb']:\n        info_data['rgblight.animations'] = {  # Comment here is to force multiline formatting\n            \"breathing\": True,\n            \"rainbow_mood\": True,\n            \"rainbow_swirl\": True,\n            \"snake\": True,\n            \"knight\": True,\n            \"static_gradient\": True,\n            \"twinkle\": True\n        }\n        info_data['rgblight.led_count'] = kbf_data['keyboard.settings.rgbNum']\n        info_data['ws2812.pin'] = kbf_data['keyboard.pins.rgb']\n\n    if kbf_data['keyboard.pins.led']:\n        info_data['backlight.levels'] = kbf_data['keyboard.settings.backlightLevels']\n        info_data['backlight.pin'] = kbf_data['keyboard.pins.led']\n\n    # delegate as if it were a regular keyboard import\n    return import_keyboard(info_data.to_dict(), keymap_data)\n"
  },
  {
    "path": "lib/python/qmk/info.py",
    "content": "\"\"\"Functions that help us generate and use info.json files.\n\"\"\"\nimport re\nimport os\nfrom pathlib import Path\nimport jsonschema\nfrom dotty_dict import dotty\nfrom enum import IntFlag\n\nfrom milc import cli\n\nfrom qmk.constants import COL_LETTERS, ROW_LETTERS, CHIBIOS_PROCESSORS, LUFA_PROCESSORS, VUSB_PROCESSORS, JOYSTICK_AXES\nfrom qmk.c_parse import find_layouts, parse_config_h_file, find_led_config\nfrom qmk.json_schema import deep_update, json_load, validate\nfrom qmk.keyboard import config_h, rules_mk\nfrom qmk.commands import parse_configurator_json\nfrom qmk.makefile import parse_rules_mk_file\nfrom qmk.math_ops import compute\nfrom qmk.util import maybe_exit, truthy\n\ntrue_values = ['1', 'on', 'yes']\nfalse_values = ['0', 'off', 'no']\n\n\nclass LedFlags(IntFlag):\n    ALL = 0xFF\n    NONE = 0x00\n    MODIFIER = 0x01\n    UNDERGLOW = 0x02\n    KEYLIGHT = 0x04\n    INDICATOR = 0x08\n\n\ndef _keyboard_in_layout_name(keyboard, layout):\n    \"\"\"Validate that a layout macro does not contain name of keyboard\n    \"\"\"\n    # TODO: reduce this list down\n    safe_layout_tokens = {\n        'ansi',\n        'iso',\n        'jp',\n        'jis',\n        'ortho',\n        'wkl',\n        'tkl',\n        'preonic',\n        'planck',\n    }\n\n    # Ignore tokens like 'split_3x7_4' or just '2x4'\n    layout = re.sub(r\"_split_\\d+x\\d+_\\d+\", '', layout)\n    layout = re.sub(r\"_\\d+x\\d+\", '', layout)\n\n    name_fragments = set(keyboard.split('/')) - safe_layout_tokens\n\n    return any(fragment in layout for fragment in name_fragments)\n\n\ndef _valid_community_layout(layout):\n    \"\"\"Validate that a declared community list exists\n    \"\"\"\n    return (Path('layouts/default') / layout).exists()\n\n\ndef _get_key_left_position(key):\n    # Special case for ISO enter\n    return key['x'] - 0.25 if key.get('h', 1) == 2 and key.get('w', 1) == 1.25 else key['x']\n\n\ndef _find_invalid_encoder_index(info_data):\n    \"\"\"Perform additional validation of encoders\n    \"\"\"\n    enc_left = info_data.get('encoder', {}).get('rotary', [])\n    enc_right = []\n\n    if info_data.get('split', {}).get('enabled', False):\n        enc_right = info_data.get('split', {}).get('encoder', {}).get('right', {}).get('rotary', enc_left)\n\n    enc_count = len(enc_left) + len(enc_right)\n\n    ret = []\n    layouts = info_data.get('layouts', {})\n    for layout_name, layout_data in layouts.items():\n        found = set()\n        for key in layout_data['layout']:\n            if 'encoder' in key:\n                if enc_count == 0:\n                    ret.append((layout_name, key['encoder'], 'non-configured'))\n                elif key['encoder'] >= enc_count:\n                    ret.append((layout_name, key['encoder'], 'out of bounds'))\n                elif key['encoder'] in found:\n                    ret.append((layout_name, key['encoder'], 'duplicate'))\n                found.add(key['encoder'])\n\n    return ret\n\n\ndef _validate_build_target(keyboard, info_data):\n    \"\"\"Non schema checks\n    \"\"\"\n    keyboard_json_path = Path('keyboards') / keyboard / 'keyboard.json'\n    config_files = find_info_json(keyboard)\n\n    # keyboard.json can only exist at the deepest part of the tree\n    keyboard_json_count = 0\n    for info_file in config_files:\n        if info_file.name == 'keyboard.json':\n            keyboard_json_count += 1\n            if info_file != keyboard_json_path:\n                _log_error(info_data, f'Invalid keyboard.json location detected: {info_file}.')\n\n    # No keyboard.json next to info.json\n    for conf_file in config_files:\n        if conf_file.name == 'keyboard.json':\n            info_file = conf_file.parent / 'info.json'\n            if info_file.exists():\n                _log_error(info_data, f'Invalid info.json location detected: {info_file}.')\n\n    # Moving forward keyboard.json should be used as a build target\n    if keyboard_json_count == 0:\n        _log_warning(info_data, 'Build marker \"keyboard.json\" not found.')\n\n\ndef _validate_layouts(keyboard, info_data):  # noqa C901\n    \"\"\"Non schema checks\n    \"\"\"\n    col_num = info_data.get('matrix_size', {}).get('cols', 0)\n    row_num = info_data.get('matrix_size', {}).get('rows', 0)\n    layouts = info_data.get('layouts', {})\n    layout_aliases = info_data.get('layout_aliases', {})\n    community_layouts = info_data.get('community_layouts', [])\n    community_layouts_names = list(map(lambda layout: f'LAYOUT_{layout}', community_layouts))\n\n    # Make sure we have at least one layout\n    if len(layouts) == 0 or all(not layout.get('json_layout', False) for layout in layouts.values()):\n        _log_error(info_data, 'No LAYOUTs defined! Need at least one layout defined in info.json.')\n\n    # Make sure all layouts are DD\n    for layout_name, layout_data in layouts.items():\n        if layout_data.get('c_macro', False):\n            _log_error(info_data, f'{layout_name}: Layout macro should not be defined within \".h\" files.')\n\n    # Make sure all matrix values are in bounds\n    for layout_name, layout_data in layouts.items():\n        for index, key_data in enumerate(layout_data['layout']):\n            row, col = key_data['matrix']\n            key_name = key_data.get('label', f'k{ROW_LETTERS[row]}{COL_LETTERS[col]}')\n            if row >= row_num:\n                _log_error(info_data, f'{layout_name}: Matrix row for key {index} ({key_name}) is {row} but must be less than {row_num}')\n            if col >= col_num:\n                _log_error(info_data, f'{layout_name}: Matrix column for key {index} ({key_name}) is {col} but must be less than {col_num}')\n\n    # Reject duplicate matrix locations\n    for layout_name, layout_data in layouts.items():\n        seen = set()\n        for index, key_data in enumerate(layout_data['layout']):\n            key = f\"{key_data['matrix']}\"\n            if key in seen:\n                _log_error(info_data, f'{layout_name}: Matrix location for key {index} is not unique {key_data}')\n            seen.add(key)\n\n    # Warn if physical positions are offset (at least one key should be at x=0, and at least one key at y=0)\n    for layout_name, layout_data in layouts.items():\n        offset_x = min([_get_key_left_position(k) for k in layout_data['layout']])\n        if offset_x > 0:\n            _log_warning(info_data, f'Layout \"{layout_name}\" is offset on X axis by {offset_x}')\n\n        offset_y = min([k['y'] for k in layout_data['layout']])\n        if offset_y > 0:\n            _log_warning(info_data, f'Layout \"{layout_name}\" is offset on Y axis by {offset_y}')\n\n    # Providing only LAYOUT_all \"because I define my layouts in a 3rd party tool\"\n    if len(layouts) == 1 and 'LAYOUT_all' in layouts:\n        _log_warning(info_data, '\"LAYOUT_all\" should be \"LAYOUT\" unless additional layouts are provided.')\n\n    # Extended layout name checks - ignoring community_layouts and \"safe\" values\n    potential_layouts = set(layouts.keys()) - set(community_layouts_names)\n    for layout in potential_layouts:\n        if _keyboard_in_layout_name(keyboard, layout):\n            _log_warning(info_data, f'Layout \"{layout}\" should not contain name of keyboard.')\n\n    # Filter out any non-existing community layouts\n    for layout in community_layouts:\n        if not _valid_community_layout(layout):\n            # Ignore layout from future checks\n            info_data['community_layouts'].remove(layout)\n            _log_error(info_data, 'Claims to support a community layout that does not exist: %s' % (layout))\n\n    # Make sure we supply layout macros for the community layouts we claim to support\n    for layout_name in community_layouts_names:\n        if layout_name not in layouts and layout_name not in layout_aliases:\n            _log_error(info_data, 'Claims to support community layout %s but no %s() macro found' % (layout, layout_name))\n\n\ndef _validate_keycodes(keyboard, info_data):\n    \"\"\"Non schema checks\n    \"\"\"\n    # keycodes with length > 7 must have short forms for visualisation purposes\n    for decl in info_data.get('keycodes', []):\n        if len(decl[\"key\"]) > 7:\n            if not decl.get(\"aliases\", []):\n                _log_error(info_data, f'Keycode {decl[\"key\"]} has no short form alias')\n\n\ndef _validate_encoders(keyboard, info_data):\n    \"\"\"Non schema checks\n    \"\"\"\n    # encoder IDs in layouts must be in range and not duplicated\n    found = _find_invalid_encoder_index(info_data)\n    for layout_name, encoder_index, reason in found:\n        _log_error(info_data, f'Layout \"{layout_name}\" contains {reason} encoder index {encoder_index}.')\n\n\ndef _validate(keyboard, info_data):\n    \"\"\"Perform various validation on the provided info.json data\n    \"\"\"\n    # First validate against the jsonschema\n    try:\n        validate(info_data, 'qmk.api.keyboard.v1')\n\n        # Additional validation\n        _validate_build_target(keyboard, info_data)\n        _validate_layouts(keyboard, info_data)\n        _validate_keycodes(keyboard, info_data)\n        _validate_encoders(keyboard, info_data)\n\n    except jsonschema.ValidationError as e:\n        json_path = '.'.join([str(p) for p in e.absolute_path])\n        cli.log.error('Invalid API data: %s: %s: %s', keyboard, json_path, e.message)\n        maybe_exit(1)\n\n\ndef info_json(keyboard, force_layout=None):\n    \"\"\"Generate the info.json data for a specific keyboard.\n    \"\"\"\n    info_data = {\n        'keyboard_name': str(keyboard),\n        'keyboard_folder': str(keyboard),\n        'keymaps': {},\n        'layouts': {},\n        'parse_errors': [],\n        'parse_warnings': [],\n        'maintainer': 'qmk',\n    }\n\n    # Populate layout data\n    layouts, aliases = _search_keyboard_h(keyboard)\n\n    if aliases:\n        info_data['layout_aliases'] = aliases\n\n    for layout_name, layout_json in layouts.items():\n        if not layout_name.startswith('LAYOUT_kc'):\n            layout_json['c_macro'] = True\n            layout_json['json_layout'] = False\n            info_data['layouts'][layout_name] = layout_json\n\n    # Merge in the data from info.json, config.h, and rules.mk\n    info_data = merge_info_jsons(keyboard, info_data)\n    info_data = _process_defaults(info_data)\n    info_data = _extract_rules_mk(info_data, rules_mk(str(keyboard)))\n    info_data = _extract_config_h(info_data, config_h(str(keyboard)))\n\n    # Ensure that we have various calculated values\n    info_data = _matrix_size(info_data)\n    info_data = _joystick_axis_count(info_data)\n    info_data = _matrix_masked(info_data)\n\n    # Merge in data from <keyboard.c>\n    info_data = _extract_led_config(info_data, str(keyboard))\n\n    # Force a community layout if requested\n    community_layouts = info_data.get(\"community_layouts\", [])\n    if force_layout in community_layouts:\n        info_data[\"community_layouts\"] = [force_layout]\n\n    # Validate\n    # Skip processing if necessary\n    if not truthy(os.environ.get('SKIP_SCHEMA_VALIDATION'), False):\n        _validate(keyboard, info_data)\n\n    # Check that the reported matrix size is consistent with the actual matrix size\n    _check_matrix(info_data)\n\n    return info_data\n\n\ndef _extract_features(info_data, rules):\n    \"\"\"Find all the features enabled in rules.mk.\n    \"\"\"\n    # Process booleans rules\n    for key, value in rules.items():\n        if key.endswith('_ENABLE'):\n            key = '_'.join(key.split('_')[:-1]).lower()\n            value = True if value.lower() in true_values else False if value.lower() in false_values else value\n\n            if key in ['lto']:\n                continue\n\n            if 'config_h_features' not in info_data:\n                info_data['config_h_features'] = {}\n\n            if 'features' not in info_data:\n                info_data['features'] = {}\n\n            if key in info_data['features']:\n                _log_warning(info_data, 'Feature %s is specified in both info.json (%s) and rules.mk (%s). The rules.mk value wins.' % (key, info_data['features'], value))\n\n            info_data['features'][key] = value\n            info_data['config_h_features'][key] = value\n\n    return info_data\n\n\ndef _extract_matrix_rules(info_data, rules):\n    \"\"\"Find all the features enabled in rules.mk.\n    \"\"\"\n    if rules.get('CUSTOM_MATRIX', 'no') != 'no':\n        if 'matrix_pins' in info_data and 'custom' in info_data['matrix_pins']:\n            _log_warning(info_data, 'Custom Matrix is specified in both info.json and rules.mk, the rules.mk values win.')\n\n        if 'matrix_pins' not in info_data:\n            info_data['matrix_pins'] = {}\n\n        if rules['CUSTOM_MATRIX'] == 'lite':\n            info_data['matrix_pins']['custom_lite'] = True\n        else:\n            info_data['matrix_pins']['custom'] = True\n\n    return info_data\n\n\ndef _pin_name(pin):\n    \"\"\"Returns the proper representation for a pin.\n    \"\"\"\n    pin = pin.strip()\n\n    if not pin:\n        return None\n\n    elif pin.isdigit():\n        return int(pin)\n\n    elif pin == 'NO_PIN':\n        return None\n\n    return pin\n\n\ndef _extract_pins(pins):\n    \"\"\"Returns a list of pins from a comma separated string of pins.\n    \"\"\"\n    return [_pin_name(pin) for pin in pins.split(',')]\n\n\ndef _extract_2d_array(raw):\n    \"\"\"Return a 2d array of strings\n    \"\"\"\n    out_array = []\n\n    while raw[-1] != '}':\n        raw = raw[:-1]\n\n    for row in raw.split('},{'):\n        if row.startswith('{'):\n            row = row[1:]\n\n        if row.endswith('}'):\n            row = row[:-1]\n\n        out_array.append([])\n\n        for val in row.split(','):\n            out_array[-1].append(val)\n\n    return out_array\n\n\ndef _extract_2d_int_array(raw):\n    \"\"\"Return a 2d array of ints\n    \"\"\"\n    ret = _extract_2d_array(raw)\n\n    return [list(map(int, x)) for x in ret]\n\n\ndef _extract_direct_matrix(direct_pins):\n    \"\"\"extract direct_matrix\n    \"\"\"\n    direct_pin_array = _extract_2d_array(direct_pins)\n\n    for i in range(len(direct_pin_array)):\n        for j in range(len(direct_pin_array[i])):\n            if direct_pin_array[i][j] == 'NO_PIN':\n                direct_pin_array[i][j] = None\n\n    return direct_pin_array\n\n\ndef _extract_audio(info_data, config_c):\n    \"\"\"Populate data about the audio configuration\n    \"\"\"\n    audio_pins = []\n\n    for pin in 'B5', 'B6', 'B7', 'C4', 'C5', 'C6':\n        if config_c.get(f'{pin}_AUDIO'):\n            audio_pins.append(pin)\n\n    if audio_pins:\n        info_data['audio'] = {'pins': audio_pins}\n\n\ndef _extract_encoders_values(config_c, postfix=''):\n    \"\"\"Common encoder extraction logic\n    \"\"\"\n    a_pad = config_c.get(f'ENCODER_A_PINS{postfix}', '').replace(' ', '')[1:-1]\n    b_pad = config_c.get(f'ENCODER_B_PINS{postfix}', '').replace(' ', '')[1:-1]\n    resolutions = config_c.get(f'ENCODER_RESOLUTIONS{postfix}', '').replace(' ', '')[1:-1]\n\n    default_resolution = config_c.get('ENCODER_RESOLUTION', None)\n\n    if a_pad and b_pad:\n        a_pad = list(filter(None, a_pad.split(',')))\n        b_pad = list(filter(None, b_pad.split(',')))\n        resolutions = list(filter(None, resolutions.split(',')))\n        if default_resolution:\n            resolutions += [default_resolution] * (len(a_pad) - len(resolutions))\n\n        encoders = []\n        for index in range(len(a_pad)):\n            encoder = {'pin_a': a_pad[index], 'pin_b': b_pad[index]}\n            if index < len(resolutions):\n                encoder['resolution'] = int(resolutions[index])\n            encoders.append(encoder)\n\n        return encoders\n\n\ndef _extract_encoders(info_data, config_c):\n    \"\"\"Populate data about encoder pins\n    \"\"\"\n    encoders = _extract_encoders_values(config_c)\n    if encoders:\n        if 'encoder' not in info_data:\n            info_data['encoder'] = {}\n\n        if 'rotary' in info_data['encoder']:\n            _log_warning(info_data, 'Encoder config is specified in both config.h (%s) and info.json (%s). The config.h value wins.' % (encoders, info_data['encoder']['rotary']))\n\n        info_data['encoder']['rotary'] = encoders\n\n    # TODO: some logic still assumes ENCODER_ENABLED would partially create encoder dict\n    if info_data.get('features', {}).get('encoder', False):\n        if 'encoder' not in info_data:\n            info_data['encoder'] = {}\n        info_data['encoder']['enabled'] = True\n\n\ndef _extract_split_encoders(info_data, config_c):\n    \"\"\"Populate data about split encoder pins\n    \"\"\"\n    encoders = _extract_encoders_values(config_c, '_RIGHT')\n    if encoders:\n        if 'split' not in info_data:\n            info_data['split'] = {}\n\n        if 'encoder' not in info_data['split']:\n            info_data['split']['encoder'] = {}\n\n        if 'right' not in info_data['split']['encoder']:\n            info_data['split']['encoder']['right'] = {}\n\n        if 'rotary' in info_data['split']['encoder']['right']:\n            _log_warning(info_data, 'Encoder config is specified in both config.h and info.json (encoder.rotary) (Value: %s), the config.h value wins.' % info_data['split']['encoder']['right']['rotary'])\n\n        info_data['split']['encoder']['right']['rotary'] = encoders\n\n\ndef _extract_secure_unlock(info_data, config_c):\n    \"\"\"Populate data about the secure unlock sequence\n    \"\"\"\n    unlock = config_c.get('SECURE_UNLOCK_SEQUENCE', '').replace(' ', '')[1:-1]\n    if unlock:\n        unlock_array = _extract_2d_int_array(unlock)\n        if 'secure' not in info_data:\n            info_data['secure'] = {}\n\n        if 'unlock_sequence' in info_data['secure']:\n            _log_warning(info_data, 'Secure unlock sequence is specified in both config.h (SECURE_UNLOCK_SEQUENCE) and info.json (secure.unlock_sequence) (Value: %s), the config.h value wins.' % info_data['secure']['unlock_sequence'])\n\n        info_data['secure']['unlock_sequence'] = unlock_array\n\n\ndef _extract_split_handedness(info_data, config_c):\n    # Migrate\n    split = info_data.get('split', {})\n    if 'matrix_grid' in split:\n        split['handedness'] = split.get('handedness', {})\n        split['handedness']['matrix_grid'] = split.pop('matrix_grid')\n\n\ndef _extract_split_serial(info_data, config_c):\n    # Migrate\n    split = info_data.get('split', {})\n    if 'soft_serial_pin' in split:\n        split['serial'] = split.get('serial', {})\n        split['serial']['pin'] = split.pop('soft_serial_pin')\n    if 'soft_serial_speed' in split:\n        split['serial'] = split.get('serial', {})\n        split['serial']['speed'] = split.pop('soft_serial_speed')\n\n\ndef _extract_split_transport(info_data, config_c):\n    # Figure out the transport method\n    if config_c.get('USE_I2C') is True:\n        if 'split' not in info_data:\n            info_data['split'] = {}\n\n        if 'transport' not in info_data['split']:\n            info_data['split']['transport'] = {}\n\n        if 'protocol' in info_data['split']['transport']:\n            _log_warning(info_data, 'Split transport is specified in both config.h (USE_I2C) and info.json (split.transport.protocol) (Value: %s), the config.h value wins.' % info_data['split']['transport'])\n\n        info_data['split']['transport']['protocol'] = 'i2c'\n\n    # Ignore transport defaults if \"SPLIT_KEYBOARD\" is unset\n    elif 'enabled' in info_data.get('split', {}):\n        if 'split' not in info_data:\n            info_data['split'] = {}\n\n        if 'transport' not in info_data['split']:\n            info_data['split']['transport'] = {}\n\n        if 'protocol' not in info_data['split']['transport']:\n            info_data['split']['transport']['protocol'] = 'serial'\n\n    # Migrate\n    transport = info_data.get('split', {}).get('transport', {})\n    if 'sync_matrix_state' in transport:\n        transport['sync'] = transport.get('sync', {})\n        transport['sync']['matrix_state'] = transport.pop('sync_matrix_state')\n    if 'sync_modifiers' in transport:\n        transport['sync'] = transport.get('sync', {})\n        transport['sync']['modifiers'] = transport.pop('sync_modifiers')\n\n\ndef _extract_split_right_pins(info_data, config_c):\n    # Figure out the right half matrix pins\n    row_pins = config_c.get('MATRIX_ROW_PINS_RIGHT', '').replace('{', '').replace('}', '').strip()\n    col_pins = config_c.get('MATRIX_COL_PINS_RIGHT', '').replace('{', '').replace('}', '').strip()\n    direct_pins = config_c.get('DIRECT_PINS_RIGHT', '').replace(' ', '')[1:-1]\n\n    if row_pins or col_pins or direct_pins:\n        if info_data.get('split', {}).get('matrix_pins', {}).get('right', None):\n            _log_warning(info_data, 'Right hand matrix data is specified in both info.json and config.h, the config.h values win.')\n\n        if 'split' not in info_data:\n            info_data['split'] = {}\n\n        if 'matrix_pins' not in info_data['split']:\n            info_data['split']['matrix_pins'] = {}\n\n        if 'right' not in info_data['split']['matrix_pins']:\n            info_data['split']['matrix_pins']['right'] = {}\n\n        if col_pins:\n            info_data['split']['matrix_pins']['right']['cols'] = _extract_pins(col_pins)\n\n        if row_pins:\n            info_data['split']['matrix_pins']['right']['rows'] = _extract_pins(row_pins)\n\n        if direct_pins:\n            info_data['split']['matrix_pins']['right']['direct'] = _extract_direct_matrix(direct_pins)\n\n\ndef _extract_matrix_info(info_data, config_c):\n    \"\"\"Populate the matrix information.\n    \"\"\"\n    row_pins = config_c.get('MATRIX_ROW_PINS', '').replace('{', '').replace('}', '').strip()\n    col_pins = config_c.get('MATRIX_COL_PINS', '').replace('{', '').replace('}', '').strip()\n    direct_pins = config_c.get('DIRECT_PINS', '').replace(' ', '')[1:-1]\n\n    if 'MATRIX_ROWS' in config_c and 'MATRIX_COLS' in config_c:\n        if 'matrix_size' in info_data:\n            _log_warning(info_data, 'Matrix size is specified in both info.json and config.h, the config.h values win.')\n\n        info_data['matrix_size'] = {\n            'cols': compute(config_c.get('MATRIX_COLS', '0')),\n            'rows': compute(config_c.get('MATRIX_ROWS', '0')),\n        }\n\n    if row_pins and col_pins:\n        if 'matrix_pins' in info_data and 'cols' in info_data['matrix_pins'] and 'rows' in info_data['matrix_pins']:\n            _log_warning(info_data, 'Matrix pins are specified in both info.json and config.h, the config.h values win.')\n\n        if 'matrix_pins' not in info_data:\n            info_data['matrix_pins'] = {}\n\n        info_data['matrix_pins']['cols'] = _extract_pins(col_pins)\n        info_data['matrix_pins']['rows'] = _extract_pins(row_pins)\n\n    if direct_pins:\n        if 'matrix_pins' in info_data and 'direct' in info_data['matrix_pins']:\n            _log_warning(info_data, 'Direct pins are specified in both info.json and config.h, the config.h values win.')\n\n        if 'matrix_pins' not in info_data:\n            info_data['matrix_pins'] = {}\n\n        info_data['matrix_pins']['direct'] = _extract_direct_matrix(direct_pins)\n\n    return info_data\n\n\ndef _config_to_json(key_type, config_value):\n    \"\"\"Convert config value using spec\n    \"\"\"\n    if key_type.startswith('array'):\n        if key_type.count('.') > 1:\n            raise Exception(f\"Conversion of {key_type} not possible\")\n\n        if '.' in key_type:\n            key_type, array_type = key_type.split('.', 1)\n        else:\n            array_type = None\n\n        config_value = config_value.replace('{', '').replace('}', '').strip()\n\n        if array_type == 'int':\n            return list(map(int, config_value.split(',')))\n        else:\n            return list(map(str.strip, config_value.split(',')))\n\n    elif key_type in ['bool', 'flag']:\n        if isinstance(config_value, bool):\n            return config_value\n        return config_value in true_values\n\n    elif key_type == 'hex':\n        return '0x' + config_value[2:].upper()\n\n    elif key_type == 'list':\n        return config_value.split()\n\n    elif key_type == 'int':\n        return int(config_value)\n\n    elif key_type == 'str':\n        return config_value.strip('\"').replace('\\\\\"', '\"').replace('\\\\\\\\', '\\\\')\n\n    elif key_type == 'bcd_version':\n        major = int(config_value[2:4])\n        minor = int(config_value[4])\n        revision = int(config_value[5])\n\n        return f'{major}.{minor}.{revision}'\n\n    return config_value\n\n\ndef _extract_config_h(info_data, config_c):\n    \"\"\"Pull some keyboard information from existing config.h files\n    \"\"\"\n    # Pull in data from the json map\n    dotty_info = dotty(info_data)\n    info_config_map = json_load(Path('data/mappings/info_config.hjson'))\n\n    for config_key, info_dict in info_config_map.items():\n        info_key = info_dict['info_key']\n        key_type = info_dict.get('value_type', 'raw')\n\n        try:\n            replace_with = info_dict.get('replace_with')\n            if config_key in config_c and info_dict.get('invalid', False):\n                if replace_with:\n                    _log_error(info_data, '%s in config.h is no longer a valid option and should be replaced with %s' % (config_key, replace_with))\n                else:\n                    _log_error(info_data, '%s in config.h is no longer a valid option and should be removed' % config_key)\n            elif config_key in config_c and info_dict.get('deprecated', False):\n                if replace_with:\n                    _log_warning(info_data, '%s in config.h is deprecated in favor of %s and will be removed at a later date' % (config_key, replace_with))\n                else:\n                    _log_warning(info_data, '%s in config.h is deprecated and will be removed at a later date' % config_key)\n\n            if config_key in config_c and info_dict.get('to_json', True):\n                if dotty_info.get(info_key) and info_dict.get('warn_duplicate', True):\n                    _log_warning(info_data, '%s in config.h is overwriting %s in info.json' % (config_key, info_key))\n\n                dotty_info[info_key] = _config_to_json(key_type, config_c[config_key])\n\n        except Exception as e:\n            _log_warning(info_data, f'{config_key}->{info_key}: {e}')\n\n    info_data.update(dotty_info)\n\n    # Pull data that easily can't be mapped in json\n    _extract_matrix_info(info_data, config_c)\n    _extract_audio(info_data, config_c)\n    _extract_secure_unlock(info_data, config_c)\n    _extract_split_handedness(info_data, config_c)\n    _extract_split_serial(info_data, config_c)\n    _extract_split_transport(info_data, config_c)\n    _extract_split_right_pins(info_data, config_c)\n    _extract_encoders(info_data, config_c)\n    _extract_split_encoders(info_data, config_c)\n\n    return info_data\n\n\ndef _process_defaults(info_data):\n    \"\"\"Process any additional defaults based on currently discovered information\n    \"\"\"\n    defaults_map = json_load(Path('data/mappings/defaults.hjson'))\n    for default_type in defaults_map.keys():\n        thing_map = defaults_map[default_type]\n        if default_type in info_data:\n            merged_count = 0\n            thing_items = thing_map.get(info_data[default_type], {}).items()\n            for key, value in thing_items:\n                if key not in info_data:\n                    info_data[key] = value\n                    merged_count += 1\n\n            if merged_count == 0 and len(thing_items) > 0:\n                _log_warning(info_data, 'All defaults for \\'%s\\' were skipped, potential redundant config or misconfiguration detected' % (default_type))\n\n    return info_data\n\n\ndef _extract_rules_mk(info_data, rules):\n    \"\"\"Pull some keyboard information from existing rules.mk files\n    \"\"\"\n    info_data['processor'] = rules.get('MCU', info_data.get('processor', 'atmega32u4'))\n\n    if info_data['processor'] in CHIBIOS_PROCESSORS:\n        arm_processor_rules(info_data, rules)\n\n    elif info_data['processor'] in LUFA_PROCESSORS + VUSB_PROCESSORS:\n        avr_processor_rules(info_data, rules)\n\n    else:\n        cli.log.warning(\"%s: Unknown MCU: %s\" % (info_data['keyboard_folder'], info_data['processor']))\n        unknown_processor_rules(info_data, rules)\n\n    # Pull in data from the json map\n    dotty_info = dotty(info_data)\n    info_rules_map = json_load(Path('data/mappings/info_rules.hjson'))\n\n    for rules_key, info_dict in info_rules_map.items():\n        info_key = info_dict['info_key']\n        key_type = info_dict.get('value_type', 'raw')\n\n        try:\n            replace_with = info_dict.get('replace_with')\n            if rules_key in rules and info_dict.get('invalid', False):\n                if replace_with:\n                    _log_error(info_data, '%s in rules.mk is no longer a valid option and should be replaced with %s' % (rules_key, replace_with))\n                else:\n                    _log_error(info_data, '%s in rules.mk is no longer a valid option and should be removed' % rules_key)\n            elif rules_key in rules and info_dict.get('deprecated', False):\n                if replace_with:\n                    _log_warning(info_data, '%s in rules.mk is deprecated in favor of %s and will be removed at a later date' % (rules_key, replace_with))\n                else:\n                    _log_warning(info_data, '%s in rules.mk is deprecated and will be removed at a later date' % rules_key)\n\n            if rules_key in rules and info_dict.get('to_json', True):\n                if dotty_info.get(info_key) and info_dict.get('warn_duplicate', True):\n                    _log_warning(info_data, '%s in rules.mk is overwriting %s in info.json' % (rules_key, info_key))\n\n                dotty_info[info_key] = _config_to_json(key_type, rules[rules_key])\n\n        except Exception as e:\n            _log_warning(info_data, f'{rules_key}->{info_key}: {e}')\n\n    info_data.update(dotty_info)\n\n    # Merge in config values that can't be easily mapped\n    _extract_features(info_data, rules)\n    _extract_matrix_rules(info_data, rules)\n\n    return info_data\n\n\ndef find_keyboard_c(keyboard):\n    \"\"\"Find all <keyboard>.c files\n    \"\"\"\n    keyboard = Path(keyboard)\n    current_path = Path('keyboards/')\n\n    files = []\n    for directory in keyboard.parts:\n        current_path = current_path / directory\n        keyboard_c_path = current_path / f'{directory}.c'\n        if keyboard_c_path.exists():\n            files.append(keyboard_c_path)\n\n    return files\n\n\ndef _extract_led_config(info_data, keyboard):\n    \"\"\"Scan all <keyboard>.c files for led config\n    \"\"\"\n    for feature in ['rgb_matrix', 'led_matrix']:\n        if info_data.get('features', {}).get(feature, False) or feature in info_data:\n            # Only attempt search if dd led config is missing\n            if 'layout' not in info_data.get(feature, {}):\n                cols = info_data.get('matrix_size', {}).get('cols')\n                rows = info_data.get('matrix_size', {}).get('rows')\n                if cols and rows:\n                    # Process\n                    for file in find_keyboard_c(keyboard):\n                        try:\n                            ret = find_led_config(file, cols, rows)\n                            if ret:\n                                info_data[feature] = info_data.get(feature, {})\n                                info_data[feature]['layout'] = ret\n                        except Exception as e:\n                            _log_warning(info_data, f'led_config: {file.name}: {e}')\n                else:\n                    _log_warning(info_data, 'led_config: matrix size required to parse g_led_config')\n\n            if info_data[feature].get('layout', None) and not info_data[feature].get('led_count', None):\n                info_data[feature]['led_count'] = len(info_data[feature]['layout'])\n\n            if info_data[feature].get('layout', None) and not info_data[feature].get('flag_steps', None):\n                flags = {LedFlags.ALL, LedFlags.NONE}\n                default_flags = {LedFlags.MODIFIER | LedFlags.KEYLIGHT, LedFlags.UNDERGLOW}\n\n                # if only a single flag is used, assume only all+none flags\n                kb_flags = set(x.get('flags', LedFlags.NONE) for x in info_data[feature]['layout'])\n                if len(kb_flags) > 1:\n                    # check if any part of LED flag is with the defaults\n                    unique_flags = set()\n                    for candidate in default_flags:\n                        if any(candidate & flag for flag in kb_flags):\n                            unique_flags.add(candidate)\n\n                    # if we still have a single flag, assume only all+none\n                    if len(unique_flags) > 1:\n                        flags.update(unique_flags)\n\n                info_data[feature]['flag_steps'] = sorted([int(flag) for flag in flags], reverse=True)\n\n    return info_data\n\n\ndef _matrix_size(info_data):\n    \"\"\"Add info_data['matrix_size'] if it doesn't exist.\n    \"\"\"\n    if 'matrix_size' not in info_data and 'matrix_pins' in info_data:\n        info_data['matrix_size'] = {}\n\n        if 'direct' in info_data['matrix_pins']:\n            info_data['matrix_size']['cols'] = len(info_data['matrix_pins']['direct'][0])\n            info_data['matrix_size']['rows'] = len(info_data['matrix_pins']['direct'])\n        elif 'cols' in info_data['matrix_pins'] and 'rows' in info_data['matrix_pins']:\n            info_data['matrix_size']['cols'] = len(info_data['matrix_pins']['cols'])\n            info_data['matrix_size']['rows'] = len(info_data['matrix_pins']['rows'])\n\n        # Assumption of split common\n        if 'split' in info_data:\n            if info_data['split'].get('enabled', False):\n                info_data['matrix_size']['rows'] *= 2\n\n    return info_data\n\n\ndef _joystick_axis_count(info_data):\n    \"\"\"Add info_data['joystick.axis_count'] if required\n    \"\"\"\n    if 'axes' in info_data.get('joystick', {}):\n        axes_keys = info_data['joystick']['axes'].keys()\n        info_data['joystick']['axis_count'] = max(JOYSTICK_AXES.index(a) for a in axes_keys) + 1 if axes_keys else 0\n\n    return info_data\n\n\ndef _matrix_masked(info_data):\n    \"\"\"\"Add info_data['matrix_pins.masked'] if required\"\"\"\n    mask_required = False\n\n    if 'matrix_grid' in info_data.get('dip_switch', {}):\n        mask_required = True\n    if 'matrix_grid' in info_data.get('split', {}).get('handedness', {}):\n        mask_required = True\n\n    if mask_required:\n        if 'masked' not in info_data.get('matrix_pins', {}):\n            if 'matrix_pins' not in info_data:\n                info_data['matrix_pins'] = {}\n\n            info_data['matrix_pins']['masked'] = True\n\n    return info_data\n\n\ndef _check_matrix(info_data):\n    \"\"\"Check the matrix to ensure that row/column count is consistent.\n    \"\"\"\n    if 'matrix_pins' in info_data and 'matrix_size' in info_data:\n        actual_col_count = info_data['matrix_size'].get('cols', 0)\n        actual_row_count = info_data['matrix_size'].get('rows', 0)\n        col_count = row_count = 0\n\n        if 'direct' in info_data['matrix_pins']:\n            col_count = len(info_data['matrix_pins']['direct'][0])\n            row_count = len(info_data['matrix_pins']['direct'])\n        elif 'cols' in info_data['matrix_pins'] and 'rows' in info_data['matrix_pins']:\n            col_count = len(info_data['matrix_pins']['cols'])\n            row_count = len(info_data['matrix_pins']['rows'])\n        elif 'cols' not in info_data['matrix_pins'] and 'rows' not in info_data['matrix_pins']:\n            # This case caters for custom matrix implementations where normal rows/cols are specified\n            return\n\n        if col_count != actual_col_count and col_count != (actual_col_count / 2):\n            # FIXME: once we can we should detect if split is enabled to do the actual_col_count/2 check.\n            _log_error(info_data, f'MATRIX_COLS is inconsistent with the size of MATRIX_COL_PINS: {col_count} != {actual_col_count}')\n\n        if row_count != actual_row_count and row_count != (actual_row_count / 2):\n            # FIXME: once we can we should detect if split is enabled to do the actual_row_count/2 check.\n            _log_error(info_data, f'MATRIX_ROWS is inconsistent with the size of MATRIX_ROW_PINS: {row_count} != {actual_row_count}')\n\n\ndef _search_keyboard_h(keyboard):\n    keyboard = Path(keyboard)\n    current_path = Path('keyboards/')\n    aliases = {}\n    layouts = {}\n\n    for directory in keyboard.parts:\n        current_path = current_path / directory\n        keyboard_h = '%s.h' % (directory,)\n        keyboard_h_path = current_path / keyboard_h\n        if keyboard_h_path.exists():\n            new_layouts, new_aliases = find_layouts(keyboard_h_path)\n            layouts.update(new_layouts)\n\n            for alias, alias_text in new_aliases.items():\n                if alias_text in layouts:\n                    aliases[alias] = alias_text\n\n    return layouts, aliases\n\n\ndef _log_error(info_data, message):\n    \"\"\"Send an error message to both JSON and the log.\n    \"\"\"\n    info_data['parse_errors'].append(message)\n    cli.log.error('%s: %s', info_data.get('keyboard_folder', 'Unknown Keyboard!'), message)\n\n\ndef _log_warning(info_data, message):\n    \"\"\"Send a warning message to both JSON and the log.\n    \"\"\"\n    info_data['parse_warnings'].append(message)\n    cli.log.warning('%s: %s', info_data.get('keyboard_folder', 'Unknown Keyboard!'), message)\n\n\ndef arm_processor_rules(info_data, rules):\n    \"\"\"Setup the default info for an ARM board.\n    \"\"\"\n    info_data['processor_type'] = 'arm'\n    info_data['protocol'] = 'ChibiOS'\n    info_data['platform_key'] = 'chibios'\n\n    if 'STM32' in info_data['processor']:\n        info_data['platform'] = 'STM32'\n    elif 'MCU_SERIES' in rules:\n        info_data['platform'] = rules['MCU_SERIES']\n\n    return info_data\n\n\ndef avr_processor_rules(info_data, rules):\n    \"\"\"Setup the default info for an AVR board.\n    \"\"\"\n    info_data['processor_type'] = 'avr'\n    info_data['platform'] = rules['ARCH'] if 'ARCH' in rules else 'unknown'\n    info_data['platform_key'] = 'avr'\n    info_data['protocol'] = 'V-USB' if info_data['processor'] in VUSB_PROCESSORS else 'LUFA'\n\n    # FIXME(fauxpark/anyone): Eventually we should detect the protocol by looking at PROTOCOL inherited from mcu_selection.mk:\n    # info_data['protocol'] = 'V-USB' if rules.get('PROTOCOL') == 'VUSB' else 'LUFA'\n\n    return info_data\n\n\ndef unknown_processor_rules(info_data, rules):\n    \"\"\"Setup the default keyboard info for unknown boards.\n    \"\"\"\n    info_data['bootloader'] = 'unknown'\n    info_data['platform'] = 'unknown'\n    info_data['processor'] = 'unknown'\n    info_data['processor_type'] = 'unknown'\n    info_data['protocol'] = 'unknown'\n\n    return info_data\n\n\ndef merge_info_jsons(keyboard, info_data):\n    \"\"\"Return a merged copy of all the info.json files for a keyboard.\n    \"\"\"\n    config_files = find_info_json(keyboard)\n\n    for info_file in config_files:\n        # Load and validate the JSON data\n        new_info_data = json_load(info_file)\n\n        if not isinstance(new_info_data, dict):\n            _log_error(info_data, \"Invalid file %s, root object should be a dictionary.\" % (str(info_file),))\n            continue\n\n        if not truthy(os.environ.get('SKIP_SCHEMA_VALIDATION'), False):\n            try:\n                validate(new_info_data, 'qmk.keyboard.v1')\n            except jsonschema.ValidationError as e:\n                json_path = '.'.join([str(p) for p in e.absolute_path])\n                cli.log.error('Not including data from file: %s', info_file)\n                cli.log.error('\\t%s: %s', json_path, e.message)\n                continue\n\n        # Merge layout data in\n        if 'layout_aliases' in new_info_data:\n            info_data['layout_aliases'] = {**info_data.get('layout_aliases', {}), **new_info_data['layout_aliases']}\n            del new_info_data['layout_aliases']\n\n        for layout_name, layout in new_info_data.get('layouts', {}).items():\n            if layout_name in info_data.get('layout_aliases', {}):\n                _log_warning(info_data, f\"info.json uses alias name {layout_name} instead of {info_data['layout_aliases'][layout_name]}\")\n                layout_name = info_data['layout_aliases'][layout_name]\n\n            if layout_name in info_data['layouts']:\n                if len(info_data['layouts'][layout_name]['layout']) != len(layout['layout']):\n                    msg = 'Number of keys for %s does not match! info.json specifies %d keys, C macro specifies %d'\n                    _log_error(info_data, msg % (layout_name, len(layout['layout']), len(info_data['layouts'][layout_name]['layout'])))\n                else:\n                    info_data['layouts'][layout_name]['json_layout'] = True\n                    for new_key, existing_key in zip(layout['layout'], info_data['layouts'][layout_name]['layout']):\n                        existing_key.update(new_key)\n            else:\n                if not all('matrix' in key_data.keys() for key_data in layout['layout']):\n                    _log_error(info_data, f'Layout \"{layout_name}\" has no \"matrix\" definition in either \"info.json\" or \"<keyboard>.h\"!')\n                else:\n                    layout['c_macro'] = False\n                    layout['json_layout'] = True\n                    info_data['layouts'][layout_name] = layout\n\n        # Update info_data with the new data\n        if 'layouts' in new_info_data:\n            del new_info_data['layouts']\n\n        deep_update(info_data, new_info_data)\n\n    return info_data\n\n\ndef find_info_json(keyboard):\n    \"\"\"Finds all the info.json files associated with a keyboard.\n    \"\"\"\n    # Find the most specific first\n    base_path = Path('keyboards')\n    keyboard_path = base_path / keyboard\n    keyboard_parent = keyboard_path.parent\n    info_jsons = [keyboard_path / 'info.json', keyboard_path / 'keyboard.json']\n\n    # Add in parent folders for least specific\n    for _ in range(5):\n        if keyboard_parent == base_path:\n            break\n        info_jsons.append(keyboard_parent / 'info.json')\n        info_jsons.append(keyboard_parent / 'keyboard.json')\n        keyboard_parent = keyboard_parent.parent\n\n    # Return a list of the info.json files that actually exist\n    return [info_json for info_json in info_jsons if info_json.exists()]\n\n\ndef keymap_json_config(keyboard, keymap, force_layout=None):\n    \"\"\"Extract keymap level config\n    \"\"\"\n    # TODO: resolve keymap.py and info.py circular dependencies\n    from qmk.keymap import locate_keymap\n\n    keymap_folder = locate_keymap(keyboard, keymap, force_layout=force_layout).parent\n\n    km_info_json = parse_configurator_json(keymap_folder / 'keymap.json')\n    return km_info_json.get('config', {})\n\n\ndef keymap_json(keyboard, keymap, force_layout=None):\n    \"\"\"Generate the info.json data for a specific keymap.\n    \"\"\"\n    # TODO: resolve keymap.py and info.py circular dependencies\n    from qmk.keymap import locate_keymap\n\n    keymap_folder = locate_keymap(keyboard, keymap, force_layout=force_layout).parent\n\n    # Files to scan\n    keymap_config = keymap_folder / 'config.h'\n    keymap_rules = keymap_folder / 'rules.mk'\n    keymap_file = keymap_folder / 'keymap.json'\n\n    # Build the info.json file\n    kb_info_json = info_json(keyboard, force_layout=force_layout)\n\n    # Merge in the data from keymap.json\n    km_info_json = keymap_json_config(keyboard, keymap, force_layout=force_layout) if keymap_file.exists() else {}\n    deep_update(kb_info_json, km_info_json)\n\n    # Merge in the data from config.h, and rules.mk\n    _extract_rules_mk(kb_info_json, parse_rules_mk_file(keymap_rules))\n    _extract_config_h(kb_info_json, parse_config_h_file(keymap_config))\n\n    return kb_info_json\n\n\ndef get_modules(keyboard, keymap_filename):\n    \"\"\"Get the modules for a keyboard/keymap.\n    \"\"\"\n    modules = []\n\n    kb_info_json = info_json(keyboard)\n    modules.extend(kb_info_json.get('modules', []))\n\n    if keymap_filename:\n        keymap_json = parse_configurator_json(keymap_filename)\n\n        if keymap_json:\n            modules.extend(keymap_json.get('modules', []))\n\n    return list(dict.fromkeys(modules))  # remove dupes\n"
  },
  {
    "path": "lib/python/qmk/json_encoders.py",
    "content": "\"\"\"Class that pretty-prints QMK info.json files.\n\"\"\"\nimport json\nfrom decimal import Decimal\n\nnewline = '\\n'\n\n\nclass QMKJSONEncoder(json.JSONEncoder):\n    \"\"\"Base class for all QMK JSON encoders.\n    \"\"\"\n    container_types = (list, tuple, dict)\n    indentation_char = \" \"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.indentation_level = 0\n\n        if not self.indent:\n            self.indent = 4\n\n    def encode_decimal(self, obj):\n        \"\"\"Encode a decimal object.\n        \"\"\"\n        if obj == int(obj):  # I can't believe Decimal objects don't have .is_integer()\n            return int(obj)\n\n        return float(obj)\n\n    def encode_dict(self, obj, path):\n        \"\"\"Encode a dict-like object.\n        \"\"\"\n        if obj:\n            self.indentation_level += 1\n\n            items = sorted(obj.items(), key=self.sort_dict) if self.sort_keys else obj.items()\n            output = [self.indent_str + f\"{json.dumps(key)}: {self.encode(value, path + [key])}\" for key, value in items]\n\n            self.indentation_level -= 1\n\n            return \"{\\n\" + \",\\n\".join(output) + \"\\n\" + self.indent_str + \"}\"\n        else:\n            return \"{}\"\n\n    def encode_dict_single_line(self, obj, path):\n        \"\"\"Encode a dict-like object onto a single line.\n        \"\"\"\n        return \"{\" + \", \".join(f\"{json.dumps(key)}: {self.encode(value, path + [key])}\" for key, value in sorted(obj.items(), key=self.sort_layout)) + \"}\"\n\n    def encode_list(self, obj, path):\n        \"\"\"Encode a list-like object.\n        \"\"\"\n        if self.primitives_only(obj):\n            return \"[\" + \", \".join(self.encode(value, path + [index]) for index, value in enumerate(obj)) + \"]\"\n\n        else:\n            self.indentation_level += 1\n\n            if path[-1] in ('layout', 'rotary'):\n                # These are part of a LED layout or encoder config, put them on a single line\n                output = [self.indent_str + self.encode_dict_single_line(value, path + [index]) for index, value in enumerate(obj)]\n            else:\n                output = [self.indent_str + self.encode(value, path + [index]) for index, value in enumerate(obj)]\n\n            self.indentation_level -= 1\n\n            return \"[\\n\" + \",\\n\".join(output) + \"\\n\" + self.indent_str + \"]\"\n\n    def encode(self, obj, path=[]):\n        \"\"\"Encode JSON objects for QMK.\n        \"\"\"\n        if isinstance(obj, Decimal):\n            return self.encode_decimal(obj)\n\n        elif isinstance(obj, (list, tuple)):\n            return self.encode_list(obj, path)\n\n        elif isinstance(obj, dict):\n            return self.encode_dict(obj, path)\n\n        else:\n            return super().encode(obj)\n\n    def primitives_only(self, obj):\n        \"\"\"Returns true if the object doesn't have any container type objects (list, tuple, dict).\n        \"\"\"\n        if isinstance(obj, dict):\n            obj = obj.values()\n\n        return not any(isinstance(element, self.container_types) for element in obj)\n\n    @property\n    def indent_str(self):\n        return self.indentation_char * (self.indentation_level * self.indent)\n\n\nclass InfoJSONEncoder(QMKJSONEncoder):\n    \"\"\"Custom encoder to make info.json's a little nicer to work with.\n    \"\"\"\n    def sort_layout(self, item):\n        \"\"\"Sorts the hashes in a nice way.\n        \"\"\"\n        key = item[0]\n\n        if key == 'label':\n            return '00label'\n\n        elif key == 'matrix':\n            return '01matrix'\n\n        elif key == 'x':\n            return '02x'\n\n        elif key == 'y':\n            return '03y'\n\n        elif key == 'w':\n            return '04w'\n\n        elif key == 'h':\n            return '05h'\n\n        elif key == 'flags':\n            return '06flags'\n\n        return key\n\n    def sort_dict(self, item):\n        \"\"\"Forces layout to the back of the sort order.\n        \"\"\"\n        key = item[0]\n\n        if self.indentation_level == 1:\n            if key == 'manufacturer':\n                return '10manufacturer'\n\n            elif key == 'keyboard_name':\n                return '11keyboard_name'\n\n            elif key == 'maintainer':\n                return '12maintainer'\n\n            elif key == 'community_layouts':\n                return '97community_layouts'\n\n            elif key == 'layout_aliases':\n                return '98layout_aliases'\n\n            elif key == 'layouts':\n                return '99layouts'\n\n            else:\n                return '50' + str(key)\n\n        return key\n\n\nclass KeymapJSONEncoder(QMKJSONEncoder):\n    \"\"\"Custom encoder to make keymap.json's a little nicer to work with.\n    \"\"\"\n    def encode_list(self, obj, path):\n        \"\"\"Encode a list-like object.\n        \"\"\"\n        if self.indentation_level == 2:\n            indent_level = self.indentation_level + 1\n            # We have a list of keycodes\n            layer = [[]]\n\n            for key in obj:\n                if key == 'JSON_NEWLINE':\n                    layer.append([])\n                else:\n                    if isinstance(key, dict):\n                        # We have a macro\n\n                        # TODO: Add proper support for nicely formatting keymap.json macros\n                        layer[-1].append(f'{self.encode(key)}')\n                    else:\n                        layer[-1].append(f'\"{key}\"')\n\n            layer = [f\"{self.indent_str * indent_level}{', '.join(row)}\" for row in layer]\n\n            return f\"{self.indent_str}[\\n{newline.join(layer)}\\n{self.indent_str * self.indentation_level}]\"\n\n        elif self.primitives_only(obj):\n            return \"[\" + \", \".join(self.encode(element) for element in obj) + \"]\"\n\n        else:\n            self.indentation_level += 1\n            output = [self.indent_str + self.encode(element) for element in obj]\n            self.indentation_level -= 1\n\n            return \"[\\n\" + \",\\n\".join(output) + \"\\n\" + self.indent_str + \"]\"\n\n    def sort_dict(self, item):\n        \"\"\"Sorts the hashes in a nice way.\n        \"\"\"\n        key = item[0]\n\n        if self.indentation_level == 1:\n            if key == 'version':\n                return '00version'\n\n            elif key == 'author':\n                return '01author'\n\n            elif key == 'notes':\n                return '02notes'\n\n            elif key == 'layers':\n                return '98layers'\n\n            elif key == 'documentation':\n                return '99documentation'\n\n            else:\n                return '50' + str(key)\n\n        return key\n\n\nclass UserspaceJSONEncoder(QMKJSONEncoder):\n    \"\"\"Custom encoder to make userspace qmk.json's a little nicer to work with.\n    \"\"\"\n    def sort_dict(self, item):\n        \"\"\"Sorts the hashes in a nice way.\n        \"\"\"\n        key = item[0]\n\n        if self.indentation_level == 1:\n            if key == 'userspace_version':\n                return '00userspace_version'\n\n            if key == 'build_targets':\n                return '01build_targets'\n\n        return key\n\n\nclass CommunityModuleJSONEncoder(QMKJSONEncoder):\n    \"\"\"Custom encoder to make qmk_module.json's a little nicer to work with.\n    \"\"\"\n    def sort_dict(self, item):\n        \"\"\"Sorts the hashes in a nice way.\n        \"\"\"\n        key = item[0]\n\n        if self.indentation_level == 1:\n            if key == 'module_name':\n                return '00module_name'\n            if key == 'maintainer':\n                return '01maintainer'\n            if key == 'license':\n                return '02license'\n            if key == 'url':\n                return '03url'\n            if key == 'features':\n                return '04features'\n            if key == 'keycodes':\n                return '05keycodes'\n        elif self.indentation_level == 3:  # keycodes\n            if key == 'key':\n                return '00key'\n            if key == 'aliases':\n                return '01aliases'\n\n        return key\n"
  },
  {
    "path": "lib/python/qmk/json_schema.py",
    "content": "\"\"\"Functions that help us generate and use info.json files.\n\"\"\"\nimport json\nimport hjson\nimport jsonschema\nfrom collections.abc import Mapping\nfrom functools import lru_cache\nfrom typing import OrderedDict\nfrom pathlib import Path\nfrom copy import deepcopy\n\nfrom milc import cli\n\nfrom qmk.util import maybe_exit\n\n\ndef _dict_raise_on_duplicates(ordered_pairs):\n    \"\"\"Reject duplicate keys.\"\"\"\n    d = {}\n    for k, v in ordered_pairs:\n        if k in d:\n            raise ValueError(\"duplicate key: %r\" % (k,))\n        else:\n            d[k] = v\n    return d\n\n\n@lru_cache(maxsize=20)\ndef _json_load_impl(json_file, strict=True):\n    \"\"\"Load a json file from disk.\n\n    Note: file must be a Path object.\n    \"\"\"\n    try:\n        # Get the IO Stream for Path objects\n        # Not necessary if the data is provided via stdin\n        if isinstance(json_file, Path):\n            json_file = json_file.open(encoding='utf-8')\n        return hjson.load(json_file, object_pairs_hook=_dict_raise_on_duplicates if strict else None)\n\n    except (json.decoder.JSONDecodeError, hjson.HjsonDecodeError) as e:\n        cli.log.error('Invalid JSON encountered attempting to load {fg_cyan}%s{fg_reset}:\\n\\t{fg_red}%s', json_file, e)\n        maybe_exit(1)\n    except Exception as e:\n        cli.log.error('Unknown error attempting to load {fg_cyan}%s{fg_reset}:\\n\\t{fg_red}%s', json_file, e)\n        maybe_exit(1)\n\n\ndef json_load(json_file, strict=True):\n    return deepcopy(_json_load_impl(json_file=json_file, strict=strict))\n\n\n@lru_cache(maxsize=20)\ndef load_jsonschema(schema_name):\n    \"\"\"Read a jsonschema file from disk.\n    \"\"\"\n    if Path(schema_name).exists():\n        return json_load(schema_name)\n\n    schema_path = Path(f'data/schemas/{schema_name}.jsonschema')\n\n    if not schema_path.exists():\n        schema_path = Path('data/schemas/false.jsonschema')\n\n    return json_load(schema_path)\n\n\n@lru_cache(maxsize=1)\ndef compile_schema_store():\n    \"\"\"Compile all our schemas into a schema store.\n    \"\"\"\n    schema_store = {}\n\n    for schema_file in Path('data/schemas').glob('*.jsonschema'):\n        schema_data = load_jsonschema(schema_file)\n        if not isinstance(schema_data, dict):\n            cli.log.debug('Skipping schema file %s', schema_file)\n            continue\n\n        # `$id`-based references\n        schema_store[schema_data['$id']] = schema_data\n\n        # Path-based references\n        schema_store[Path(schema_file).name] = schema_data\n\n    return schema_store\n\n\n@lru_cache(maxsize=20)\ndef create_validator(schema):\n    \"\"\"Creates a validator for the given schema id.\n    \"\"\"\n    schema_store = compile_schema_store()\n    resolver = jsonschema.RefResolver.from_schema(schema_store[schema], store=schema_store)\n\n    return jsonschema.Draft202012Validator(schema_store[schema], resolver=resolver).validate\n\n\ndef validate(data, schema):\n    \"\"\"Validates data against a schema.\n    \"\"\"\n    validator = create_validator(schema)\n\n    return validator(data)\n\n\ndef deep_update(origdict, newdict):\n    \"\"\"Update a dictionary in place, recursing to do a depth-first deep copy.\n    \"\"\"\n    for key, value in newdict.items():\n        if isinstance(value, Mapping):\n            origdict[key] = deep_update(origdict.get(key, {}), value)\n\n        else:\n            origdict[key] = value\n\n    return origdict\n\n\ndef merge_ordered_dicts(dicts):\n    \"\"\"Merges nested OrderedDict objects resulting from reading a hjson file.\n    Later input dicts overrides earlier dicts for plain values.\n    If any value is \"!delete!\", the existing value will be removed from its parent.\n    Arrays will be appended. If the first entry of an array is \"!reset!\", the contents of the array will be cleared and replaced with RHS.\n    Dictionaries will be recursively merged. If any entry is \"!reset!\", the contents of the dictionary will be cleared and replaced with RHS.\n    \"\"\"\n    result = OrderedDict()\n\n    def add_entry(target, k, v):\n        if k in target and isinstance(v, (OrderedDict, dict)):\n            if \"!reset!\" in v:\n                target[k] = v\n            else:\n                target[k] = merge_ordered_dicts([target[k], v])\n            if \"!reset!\" in target[k]:\n                del target[k][\"!reset!\"]\n        elif k in target and isinstance(v, list):\n            if v[0] == '!reset!':\n                target[k] = v[1:]\n            else:\n                target[k] = target[k] + v\n        elif v == \"!delete!\" and isinstance(target, (OrderedDict, dict)):\n            del target[k]\n        else:\n            target[k] = v\n\n    for d in dicts:\n        for (k, v) in d.items():\n            add_entry(result, k, v)\n\n    return result\n"
  },
  {
    "path": "lib/python/qmk/keyboard.py",
    "content": "\"\"\"Functions that help us work with keyboards.\n\"\"\"\nfrom array import array\nfrom functools import lru_cache\nfrom math import ceil\nfrom pathlib import Path\nimport os\nfrom glob import glob\n\nimport qmk.path\nfrom qmk.c_parse import parse_config_h_file\nfrom qmk.json_schema import json_load\nfrom qmk.makefile import parse_rules_mk_file\n\nBOX_DRAWING_CHARACTERS = {\n    \"unicode\": {\n        \"tl\": \"┌\",\n        \"tr\": \"┐\",\n        \"bl\": \"└\",\n        \"br\": \"┘\",\n        \"v\": \"│\",\n        \"h\": \"─\",\n    },\n    \"ascii\": {\n        \"tl\": \" \",\n        \"tr\": \" \",\n        \"bl\": \"|\",\n        \"br\": \"|\",\n        \"v\": \"|\",\n        \"h\": \"_\",\n    },\n}\nENC_DRAWING_CHARACTERS = {\n    \"unicode\": {\n        \"tl\": \"╭\",\n        \"tr\": \"╮\",\n        \"bl\": \"╰\",\n        \"br\": \"╯\",\n        \"vl\": \"▲\",\n        \"vr\": \"▼\",\n        \"v\": \"│\",\n        \"h\": \"─\",\n    },\n    \"ascii\": {\n        \"tl\": \" \",\n        \"tr\": \" \",\n        \"bl\": \"\\\\\",\n        \"br\": \"/\",\n        \"v\": \"|\",\n        \"vl\": \"/\",\n        \"vr\": \"\\\\\",\n        \"h\": \"_\",\n    },\n}\n\n\nclass AllKeyboards:\n    \"\"\"Represents all keyboards.\n    \"\"\"\n    def __str__(self):\n        return 'all'\n\n    def __repr__(self):\n        return 'all'\n\n    def __eq__(self, other):\n        return isinstance(other, AllKeyboards)\n\n\nbase_path = os.path.join(os.getcwd(), \"keyboards\") + os.path.sep\n\n\n@lru_cache(maxsize=1)\ndef keyboard_alias_definitions():\n    return json_load(Path('data/mappings/keyboard_aliases.hjson'))\n\n\ndef is_all_keyboards(keyboard):\n    \"\"\"Returns True if the keyboard is an AllKeyboards object.\n    \"\"\"\n    if isinstance(keyboard, str):\n        return (keyboard == 'all')\n    return isinstance(keyboard, AllKeyboards)\n\n\ndef find_keyboard_from_dir():\n    \"\"\"Returns a keyboard name based on the user's current directory.\n    \"\"\"\n    relative_cwd = qmk.path.under_qmk_userspace()\n    if not relative_cwd:\n        relative_cwd = qmk.path.under_qmk_firmware()\n\n    if relative_cwd and len(relative_cwd.parts) > 1 and relative_cwd.parts[0] == 'keyboards':\n        # Attempt to extract the keyboard name from the current directory\n        current_path = Path('/'.join(relative_cwd.parts[1:]))\n\n        if 'keymaps' in current_path.parts:\n            # Strip current_path of anything after `keymaps`\n            keymap_index = len(current_path.parts) - current_path.parts.index('keymaps') - 1\n            current_path = current_path.parents[keymap_index]\n\n        if qmk.path.is_keyboard(current_path):\n            return str(current_path)\n\n\ndef find_readme(keyboard):\n    \"\"\"Returns the readme for this keyboard.\n    \"\"\"\n    cur_dir = qmk.path.keyboard(keyboard)\n    keyboards_dir = Path('keyboards')\n    while not (cur_dir / 'readme.md').exists():\n        if cur_dir == keyboards_dir:\n            return None\n        cur_dir = cur_dir.parent\n\n    return cur_dir / 'readme.md'\n\n\ndef keyboard_folder(keyboard):\n    \"\"\"Returns the actual keyboard folder.\n\n    This checks aliases to resolve the actual path for a keyboard.\n    \"\"\"\n    aliases = keyboard_alias_definitions()\n\n    while keyboard in aliases:\n        last_keyboard = keyboard\n        keyboard = aliases[keyboard].get('target', keyboard)\n        if keyboard == last_keyboard:\n            break\n\n    if not qmk.path.is_keyboard(keyboard):\n        raise ValueError(f'Invalid keyboard: {keyboard}')\n\n    return keyboard\n\n\ndef keyboard_aliases(keyboard):\n    \"\"\"Returns the list of aliases for the supplied keyboard.\n\n    Includes the keyboard itself.\n    \"\"\"\n    aliases = json_load(Path('data/mappings/keyboard_aliases.hjson'))\n\n    if keyboard in aliases:\n        keyboard = aliases[keyboard].get('target', keyboard)\n\n    keyboards = set(filter(lambda k: aliases[k].get('target', '') == keyboard, aliases.keys()))\n    keyboards.add(keyboard)\n    keyboards = list(sorted(keyboards))\n    return keyboards\n\n\ndef keyboard_folder_or_all(keyboard):\n    \"\"\"Returns the actual keyboard folder.\n\n    This checks aliases to resolve the actual path for a keyboard.\n    If the supplied argument is \"all\", it returns an AllKeyboards object.\n    \"\"\"\n    if keyboard == 'all':\n        return AllKeyboards()\n\n    return keyboard_folder(keyboard)\n\n\ndef _find_name(path):\n    \"\"\"Determine the keyboard name by stripping off the base_path and filename.\n    \"\"\"\n    return path.replace(base_path, \"\").rsplit(os.path.sep, 1)[0]\n\n\ndef keyboard_completer(prefix, action, parser, parsed_args):\n    \"\"\"Returns a list of keyboards for tab completion.\n    \"\"\"\n    return list_keyboards()\n\n\n@lru_cache(maxsize=None)\ndef list_keyboards():\n    \"\"\"Returns a list of all keyboards.\n    \"\"\"\n    # We avoid pathlib here because this is performance critical code.\n    kb_wildcard = os.path.join(base_path, \"**\", 'keyboard.json')\n    paths = [path for path in glob(kb_wildcard, recursive=True) if os.path.sep + 'keymaps' + os.path.sep not in path]\n\n    found = map(_find_name, paths)\n\n    # Convert to posix paths for consistency\n    found = map(lambda x: str(Path(x).as_posix()), found)\n\n    return sorted(set(found))\n\n\ndef config_h(keyboard):\n    \"\"\"Parses all the config.h files for a keyboard.\n\n    Args:\n        keyboard: name of the keyboard\n\n    Returns:\n        a dictionary representing the content of the entire config.h tree for a keyboard\n    \"\"\"\n    config = {}\n    cur_dir = Path('keyboards')\n    keyboard = Path(keyboard)\n\n    for dir in keyboard.parts:\n        cur_dir = cur_dir / dir\n        config = {**config, **parse_config_h_file(cur_dir / 'config.h')}\n\n    return config\n\n\ndef rules_mk(keyboard):\n    \"\"\"Get a rules.mk for a keyboard\n\n    Args:\n        keyboard: name of the keyboard\n\n    Returns:\n        a dictionary representing the content of the entire rules.mk tree for a keyboard\n    \"\"\"\n    cur_dir = Path('keyboards')\n    keyboard = Path(keyboard)\n    rules = parse_rules_mk_file(cur_dir / keyboard / 'rules.mk')\n\n    for i, dir in enumerate(keyboard.parts):\n        cur_dir = cur_dir / dir\n        rules = parse_rules_mk_file(cur_dir / 'rules.mk', rules)\n\n    return rules\n\n\ndef render_layout(layout_data, render_ascii, key_labels=None):\n    \"\"\"Renders a single layout.\n    \"\"\"\n    textpad = [array('u', ' ' * 200) for x in range(100)]\n    style = 'ascii' if render_ascii else 'unicode'\n\n    for key in layout_data:\n        x = key.get('x', 0)\n        y = key.get('y', 0)\n        w = key.get('w', 1)\n        h = key.get('h', 1)\n\n        if key_labels:\n            label = key_labels.pop(0)\n            if label.startswith('KC_'):\n                label = label[3:]\n        else:\n            label = key.get('label', '')\n\n        if 'encoder' in key:\n            render_encoder(textpad, x, y, w, h, label, style)\n        elif x >= 0.25 and w == 1.25 and h == 2:\n            render_key_isoenter(textpad, x, y, w, h, label, style)\n        elif w == 1.5 and h == 2:\n            render_key_baenter(textpad, x, y, w, h, label, style)\n        else:\n            render_key_rect(textpad, x, y, w, h, label, style)\n\n    lines = []\n    for line in textpad:\n        if line.tounicode().strip():\n            lines.append(line.tounicode().rstrip())\n\n    return '\\n'.join(lines)\n\n\ndef render_layouts(info_json, render_ascii):\n    \"\"\"Renders all the layouts from an `info_json` structure.\n    \"\"\"\n    layouts = {}\n\n    for layout in info_json['layouts']:\n        layout_data = info_json['layouts'][layout]['layout']\n        layouts[layout] = render_layout(layout_data, render_ascii)\n\n    return layouts\n\n\ndef render_key_rect(textpad, x, y, w, h, label, style):\n    box_chars = BOX_DRAWING_CHARACTERS[style]\n    x = ceil(x * 4)\n    y = ceil(y * 3)\n    w = ceil(w * 4)\n    h = ceil(h * 3)\n\n    label_len = w - 2\n    label_leftover = label_len - len(label)\n\n    if len(label) > label_len:\n        label = label[:label_len]\n\n    label_blank = ' ' * label_len\n    label_border = box_chars['h'] * label_len\n    label_middle = label + ' ' * label_leftover\n\n    top_line = array('u', box_chars['tl'] + label_border + box_chars['tr'])\n    lab_line = array('u', box_chars['v'] + label_middle + box_chars['v'])\n    mid_line = array('u', box_chars['v'] + label_blank + box_chars['v'])\n    bot_line = array('u', box_chars['bl'] + label_border + box_chars['br'])\n\n    textpad[y][x:x + w] = top_line\n    textpad[y + 1][x:x + w] = lab_line\n    for i in range(h - 3):\n        textpad[y + i + 2][x:x + w] = mid_line\n    textpad[y + h - 1][x:x + w] = bot_line\n\n\ndef render_key_isoenter(textpad, x, y, w, h, label, style):\n    box_chars = BOX_DRAWING_CHARACTERS[style]\n    x = ceil(x * 4)\n    y = ceil(y * 3)\n    w = ceil(w * 4)\n    h = ceil(h * 3)\n\n    label_len = w - 1\n    label_leftover = label_len - len(label)\n\n    if len(label) > label_len:\n        label = label[:label_len]\n\n    label_blank = ' ' * (label_len - 1)\n    label_border_top = box_chars['h'] * label_len\n    label_border_bottom = box_chars['h'] * (label_len - 1)\n    label_middle = label + ' ' * label_leftover\n\n    top_line = array('u', box_chars['tl'] + label_border_top + box_chars['tr'])\n    lab_line = array('u', box_chars['v'] + label_middle + box_chars['v'])\n    crn_line = array('u', box_chars['bl'] + box_chars['tr'] + label_blank + box_chars['v'])\n    mid_line = array('u', box_chars['v'] + label_blank + box_chars['v'])\n    bot_line = array('u', box_chars['bl'] + label_border_bottom + box_chars['br'])\n\n    textpad[y][x - 1:x + w] = top_line\n    textpad[y + 1][x - 1:x + w] = lab_line\n    textpad[y + 2][x - 1:x + w] = crn_line\n    textpad[y + 3][x:x + w] = mid_line\n    textpad[y + 4][x:x + w] = mid_line\n    textpad[y + 5][x:x + w] = bot_line\n\n\ndef render_key_baenter(textpad, x, y, w, h, label, style):\n    box_chars = BOX_DRAWING_CHARACTERS[style]\n    x = ceil(x * 4)\n    y = ceil(y * 3)\n    w = ceil(w * 4)\n    h = ceil(h * 3)\n\n    label_len = w + 1\n    label_leftover = label_len - len(label)\n\n    if len(label) > label_len:\n        label = label[:label_len]\n\n    label_blank = ' ' * (label_len - 3)\n    label_border_top = box_chars['h'] * (label_len - 3)\n    label_border_bottom = box_chars['h'] * label_len\n    label_middle = label + ' ' * label_leftover\n\n    top_line = array('u', box_chars['tl'] + label_border_top + box_chars['tr'])\n    mid_line = array('u', box_chars['v'] + label_blank + box_chars['v'])\n    crn_line = array('u', box_chars['tl'] + box_chars['h'] + box_chars['h'] + box_chars['br'] + label_blank + box_chars['v'])\n    lab_line = array('u', box_chars['v'] + label_middle + box_chars['v'])\n    bot_line = array('u', box_chars['bl'] + label_border_bottom + box_chars['br'])\n\n    textpad[y][x:x + w] = top_line\n    textpad[y + 1][x:x + w] = mid_line\n    textpad[y + 2][x:x + w] = mid_line\n    textpad[y + 3][x - 3:x + w] = crn_line\n    textpad[y + 4][x - 3:x + w] = lab_line\n    textpad[y + 5][x - 3:x + w] = bot_line\n\n\ndef render_encoder(textpad, x, y, w, h, label, style):\n    box_chars = ENC_DRAWING_CHARACTERS[style]\n    x = ceil(x * 4)\n    y = ceil(y * 3)\n    w = ceil(w * 4)\n    h = ceil(h * 3)\n\n    label_len = w - 2\n    label_leftover = label_len - len(label)\n\n    if len(label) > label_len:\n        label = label[:label_len]\n\n    label_blank = ' ' * label_len\n    label_border = box_chars['h'] * label_len\n    label_middle = label + ' ' * label_leftover\n\n    top_line = array('u', box_chars['tl'] + label_border + box_chars['tr'])\n    lab_line = array('u', box_chars['vl'] + label_middle + box_chars['vr'])\n    mid_line = array('u', box_chars['v'] + label_blank + box_chars['v'])\n    bot_line = array('u', box_chars['bl'] + label_border + box_chars['br'])\n\n    textpad[y][x:x + w] = top_line\n    textpad[y + 1][x:x + w] = lab_line\n    for i in range(h - 3):\n        textpad[y + i + 2][x:x + w] = mid_line\n    textpad[y + h - 1][x:x + w] = bot_line\n"
  },
  {
    "path": "lib/python/qmk/keycodes.py",
    "content": "from pathlib import Path\n\nfrom qmk.json_schema import merge_ordered_dicts, deep_update, json_load, validate\n\nCONSTANTS_PATH = Path('data/constants/')\nKEYCODES_PATH = CONSTANTS_PATH / 'keycodes'\nEXTRAS_PATH = KEYCODES_PATH / 'extras'\n\n\ndef _find_versions(path, prefix):\n    ret = []\n    for file in path.glob(f'{prefix}_[0-9].[0-9].[0-9].hjson'):\n        ret.append(file.stem.split('_')[-1])\n\n    ret.sort(reverse=True)\n    return ret\n\n\ndef _potential_search_versions(version, lang=None):\n    versions = list_versions(lang)\n    versions.reverse()\n\n    loc = versions.index(version) + 1\n\n    return versions[:loc]\n\n\ndef _search_path(lang=None):\n    return EXTRAS_PATH if lang else KEYCODES_PATH\n\n\ndef _search_prefix(lang=None):\n    return f'keycodes_{lang}' if lang else 'keycodes'\n\n\ndef _locate_files(path, prefix, versions):\n    # collate files by fragment \"type\"\n    files = {'_': []}\n    for version in versions:\n        files['_'].append(path / f'{prefix}_{version}.hjson')\n\n        for file in path.glob(f'{prefix}_{version}_*.hjson'):\n            fragment = file.stem.replace(f'{prefix}_{version}_', '')\n            if fragment not in files:\n                files[fragment] = []\n            files[fragment].append(file)\n\n    return files\n\n\ndef _process_files(files):\n    # allow override within types of fragments - but not globally\n    spec = {}\n    for category in files.values():\n        specs = []\n        for file in category:\n            specs.append(json_load(file))\n\n        deep_update(spec, merge_ordered_dicts(specs))\n\n    return spec\n\n\ndef _validate(spec):\n    # first throw it to the jsonschema\n    validate(spec, 'qmk.keycodes.v1')\n\n    # no duplicate keycodes\n    keycodes = []\n    for value in spec['keycodes'].values():\n        keycodes.append(value['key'])\n        keycodes.extend(value.get('aliases', []))\n    duplicates = set([x for x in keycodes if keycodes.count(x) > 1])\n    if duplicates:\n        raise ValueError(f'Keycode spec contains duplicate keycodes! ({\",\".join(duplicates)})')\n\n\ndef load_spec(version, lang=None):\n    \"\"\"Build keycode data from the requested spec file\n    \"\"\"\n    if version == 'latest':\n        version = list_versions(lang)[0]\n\n    path = _search_path(lang)\n    prefix = _search_prefix(lang)\n    versions = _potential_search_versions(version, lang)\n\n    # Load bases + any fragments\n    spec = _process_files(_locate_files(path, prefix, versions))\n\n    # Sort?\n    spec['version'] = version\n    spec['keycodes'] = dict(sorted(spec.get('keycodes', {}).items()))\n    spec['ranges'] = dict(sorted(spec.get('ranges', {}).items()))\n\n    # Validate?\n    _validate(spec)\n\n    return spec\n\n\ndef list_versions(lang=None):\n    \"\"\"Return available versions - sorted newest first\n    \"\"\"\n    path = _search_path(lang)\n    prefix = _search_prefix(lang)\n\n    return _find_versions(path, prefix)\n\n\ndef list_languages():\n    \"\"\"Return available languages\n    \"\"\"\n    ret = set()\n    for file in EXTRAS_PATH.glob('keycodes_*_[0-9].[0-9].[0-9].hjson'):\n        ret.add(file.stem.split('_')[1])\n\n    return ret\n"
  },
  {
    "path": "lib/python/qmk/keymap.py",
    "content": "\"\"\"Functions that help you work with QMK keymaps.\n\"\"\"\nimport json\nimport sys\nfrom pathlib import Path\nfrom subprocess import DEVNULL\n\nimport argcomplete\nfrom milc import cli\nfrom pygments.lexers.c_cpp import CLexer\nfrom pygments.token import Token\nfrom pygments import lex\n\nimport qmk.path\nfrom qmk.constants import QMK_FIRMWARE, QMK_USERSPACE, HAS_QMK_USERSPACE\nfrom qmk.keyboard import find_keyboard_from_dir, keyboard_folder, keyboard_aliases\nfrom qmk.errors import CppError\nfrom qmk.info import info_json\n\n# The `keymap.c` template to use when a keyboard doesn't have its own\nDEFAULT_KEYMAP_C = \"\"\"#include QMK_KEYBOARD_H\n#if __has_include(\"keymap.h\")\n#    include \"keymap.h\"\n#endif\n__INCLUDES__\n\n/* THIS FILE WAS GENERATED!\n *\n * This file was generated by qmk json2c. You may or may not want to\n * edit it directly.\n */\n\n__KEYMAP_GOES_HERE__\n__ENCODER_MAP_GOES_HERE__\n__DIP_SWITCH_MAP_GOES_HERE__\n__MACRO_OUTPUT_GOES_HERE__\n\n#ifdef OTHER_KEYMAP_C\n#    include OTHER_KEYMAP_C\n#endif // OTHER_KEYMAP_C\n\"\"\"\n\n\ndef _generate_keymap_table(keymap_json):\n    lines = ['const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {']\n    for layer_num, layer in enumerate(keymap_json['layers']):\n        if layer_num != 0:\n            lines[-1] = lines[-1] + ','\n        layer = map(_strip_any, layer)\n        layer_keys = ', '.join(layer)\n        lines.append('    [%s] = %s(%s)' % (layer_num, keymap_json['layout'], layer_keys))\n    lines.append('};')\n    return lines\n\n\ndef _generate_encodermap_table(keymap_json):\n    lines = [\n        '#if defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE)',\n        'const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS] = {',\n    ]\n    for layer_num, layer in enumerate(keymap_json['encoders']):\n        if layer_num != 0:\n            lines[-1] = lines[-1] + ','\n        encoder_keycode_txt = ', '.join([f'ENCODER_CCW_CW({_strip_any(e[\"ccw\"])}, {_strip_any(e[\"cw\"])})' for e in layer])\n        lines.append('    [%s] = {%s}' % (layer_num, encoder_keycode_txt))\n    lines.extend(['};', '#endif // defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE)'])\n    return lines\n\n\ndef _generate_dipswitchmap_table(keymap_json):\n    lines = [\n        '#if defined(DIP_SWITCH_ENABLE) && defined(DIP_SWITCH_MAP_ENABLE)',\n        'const uint16_t PROGMEM dip_switch_map[NUM_DIP_SWITCHES][NUM_DIP_STATES] = {',\n    ]\n    for index, switch in enumerate(keymap_json['dip_switches']):\n        if index != 0:\n            lines[-1] = lines[-1] + ','\n        lines.append(f'    DIP_SWITCH_OFF_ON({_strip_any(switch[\"off\"])}, {_strip_any(switch[\"on\"])})')\n    lines.extend(['};', '#endif // defined(DIP_SWITCH_ENABLE) && defined(DIP_SWITCH_MAP_ENABLE)'])\n    return lines\n\n\ndef _generate_macros_function(keymap_json):\n    macro_txt = [\n        'bool process_record_user(uint16_t keycode, keyrecord_t *record) {',\n        '    if (record->event.pressed) {',\n        '        switch (keycode) {',\n    ]\n\n    for i, macro_array in enumerate(keymap_json['macros']):\n        macro = []\n\n        for macro_fragment in macro_array:\n            if isinstance(macro_fragment, str):\n                macro_fragment = macro_fragment.replace('\\\\', '\\\\\\\\')\n                macro_fragment = macro_fragment.replace('\\r\\n', r'\\n')\n                macro_fragment = macro_fragment.replace('\\n', r'\\n')\n                macro_fragment = macro_fragment.replace('\\r', r'\\n')\n                macro_fragment = macro_fragment.replace('\\t', r'\\t')\n                macro_fragment = macro_fragment.replace('\"', r'\\\"')\n\n                macro.append(f'\"{macro_fragment}\"')\n\n            elif isinstance(macro_fragment, dict):\n                newstring = []\n\n                if macro_fragment['action'] == 'delay':\n                    newstring.append(f\"SS_DELAY({macro_fragment['duration']})\")\n\n                elif macro_fragment['action'] == 'beep':\n                    newstring.append(r'\"\\a\"')\n\n                elif macro_fragment['action'] == 'tap' and len(macro_fragment['keycodes']) > 1:\n                    last_keycode = macro_fragment['keycodes'].pop()\n\n                    for keycode in macro_fragment['keycodes']:\n                        newstring.append(f'SS_DOWN(X_{keycode})')\n\n                    newstring.append(f'SS_TAP(X_{last_keycode})')\n\n                    for keycode in reversed(macro_fragment['keycodes']):\n                        newstring.append(f'SS_UP(X_{keycode})')\n\n                else:\n                    for keycode in macro_fragment['keycodes']:\n                        newstring.append(f\"SS_{macro_fragment['action'].upper()}(X_{keycode})\")\n\n                macro.append(''.join(newstring))\n\n        new_macro = \"\".join(macro)\n        new_macro = new_macro.replace('\"\"', '')\n        macro_txt.append(f'            case QK_MACRO_{i}:')\n        macro_txt.append(f'                SEND_STRING({new_macro});')\n        macro_txt.append('                return false;')\n\n    macro_txt.append('        }')\n    macro_txt.append('    }')\n    macro_txt.append('\\n    return true;')\n    macro_txt.append('};')\n    macro_txt.append('')\n    return macro_txt\n\n\ndef _strip_any(keycode):\n    \"\"\"Remove ANY() from a keycode.\n    \"\"\"\n    if keycode.startswith('ANY(') and keycode.endswith(')'):\n        keycode = keycode[4:-1]\n\n    return keycode\n\n\ndef find_keymap_from_dir(*args):\n    \"\"\"Returns `(keymap_name, source)` for the directory provided (or cwd if not specified).\n    \"\"\"\n    def _impl_find_keymap_from_dir(relative_path):\n        if relative_path and len(relative_path.parts) > 1:\n            # If we're in `qmk_firmware/keyboards` and `keymaps` is in our path, try to find the keyboard name.\n            if relative_path.parts[0] == 'keyboards' and 'keymaps' in relative_path.parts:\n                current_path = Path('/'.join(relative_path.parts[1:]))  # Strip 'keyboards' from the front\n\n                if 'keymaps' in current_path.parts and current_path.name != 'keymaps':\n                    while current_path.parent.name != 'keymaps':\n                        current_path = current_path.parent\n\n                    return current_path.name, 'keymap_directory'\n\n            # If we're in `qmk_firmware/layouts` guess the name from the community keymap they're in\n            elif relative_path.parts[0] == 'layouts' and is_keymap_dir(relative_path):\n                return relative_path.name, 'layouts_directory'\n\n            # If we're in `qmk_firmware/users` guess the name from the userspace they're in\n            elif relative_path.parts[0] == 'users':\n                # Guess the keymap name based on which userspace they're in\n                return relative_path.parts[1], 'users_directory'\n        return None, None\n\n    if HAS_QMK_USERSPACE:\n        name, source = _impl_find_keymap_from_dir(qmk.path.under_qmk_userspace(*args))\n        if name and source:\n            return name, source\n\n    name, source = _impl_find_keymap_from_dir(qmk.path.under_qmk_firmware(*args))\n    if name and source:\n        return name, source\n\n    return (None, None)\n\n\ndef keymap_completer(prefix, action, parser, parsed_args):\n    \"\"\"Returns a list of keymaps for tab completion.\n    \"\"\"\n    try:\n        if parsed_args.keyboard:\n            return list_keymaps(parsed_args.keyboard)\n\n        keyboard = find_keyboard_from_dir()\n\n        if keyboard:\n            return list_keymaps(keyboard)\n\n    except Exception as e:\n        argcomplete.warn(f'Error: {e.__class__.__name__}: {str(e)}')\n        return []\n\n    return []\n\n\ndef is_keymap_dir(keymap, c=True, json=True, additional_files=None):\n    \"\"\"Return True if Path object `keymap` has a keymap file inside.\n\n    Args:\n        keymap\n            A Path() object for the keymap directory you want to check.\n\n        c\n            When true include `keymap.c` keymaps.\n\n        json\n            When true include `keymap.json` keymaps.\n\n        additional_files\n            A sequence of additional filenames to check against to determine if a directory is a keymap. All files must exist for a match to happen. For example, if you want to match a C keymap with both a `config.h` and `rules.mk` file: `is_keymap_dir(keymap_dir, json=False, additional_files=['config.h', 'rules.mk'])`\n    \"\"\"\n    files = []\n\n    if c:\n        files.append('keymap.c')\n\n    if json:\n        files.append('keymap.json')\n\n    for file in files:\n        if (keymap / file).is_file():\n            if additional_files:\n                for additional_file in additional_files:\n                    if not (keymap / additional_file).is_file():\n                        return False\n\n            return True\n\n\ndef generate_json(keymap, keyboard, layout, layers, macros=None):\n    \"\"\"Returns a `keymap.json` for the specified keyboard, layout, and layers.\n\n    Args:\n        keymap\n            A name for this keymap.\n\n        keyboard\n            The name of the keyboard.\n\n        layout\n            The LAYOUT macro this keymap uses.\n\n        layers\n            An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.\n\n        macros\n            A sequence of strings containing macros to implement for this keyboard.\n    \"\"\"\n    new_keymap = {'keyboard': keyboard}\n    new_keymap['keymap'] = keymap\n    new_keymap['layout'] = layout\n    new_keymap['layers'] = layers\n    if macros:\n        new_keymap['macros'] = macros\n\n    return new_keymap\n\n\ndef generate_c(keymap_json):\n    \"\"\"Returns a `keymap.c`.\n\n    `keymap_json` is a dictionary with the following keys:\n\n        keyboard\n            The name of the keyboard\n\n        layout\n            The LAYOUT macro this keymap uses.\n\n        layers\n            An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.\n\n        macros\n            A sequence of strings containing macros to implement for this keyboard.\n    \"\"\"\n    new_keymap = DEFAULT_KEYMAP_C\n\n    keymap = ''\n    if 'layers' in keymap_json and keymap_json['layers'] is not None:\n        layer_txt = _generate_keymap_table(keymap_json)\n        keymap = '\\n'.join(layer_txt)\n    new_keymap = new_keymap.replace('__KEYMAP_GOES_HERE__', keymap)\n\n    encodermap = ''\n    if 'encoders' in keymap_json and keymap_json['encoders'] is not None:\n        encoder_txt = _generate_encodermap_table(keymap_json)\n        encodermap = '\\n'.join(encoder_txt)\n    new_keymap = new_keymap.replace('__ENCODER_MAP_GOES_HERE__', encodermap)\n\n    dipswitchmap = ''\n    if 'dip_switches' in keymap_json and keymap_json['dip_switches'] is not None:\n        dip_txt = _generate_dipswitchmap_table(keymap_json)\n        dipswitchmap = '\\n'.join(dip_txt)\n    new_keymap = new_keymap.replace('__DIP_SWITCH_MAP_GOES_HERE__', dipswitchmap)\n\n    macros = ''\n    if 'macros' in keymap_json and keymap_json['macros'] is not None:\n        macro_txt = _generate_macros_function(keymap_json)\n        macros = '\\n'.join(macro_txt)\n    new_keymap = new_keymap.replace('__MACRO_OUTPUT_GOES_HERE__', macros)\n\n    hostlang = ''\n    if 'host_language' in keymap_json and keymap_json['host_language'] is not None:\n        hostlang = f'#include \"keymap_{keymap_json[\"host_language\"]}.h\"\\n#include \"sendstring_{keymap_json[\"host_language\"]}.h\"\\n'\n    new_keymap = new_keymap.replace('__INCLUDES__', hostlang)\n\n    return new_keymap\n\n\ndef write_file(keymap_filename, keymap_content):\n    keymap_filename.parent.mkdir(parents=True, exist_ok=True)\n    keymap_filename.write_text(keymap_content)\n\n    cli.log.info('Wrote keymap to {fg_cyan}%s', keymap_filename)\n\n    return keymap_filename\n\n\ndef write_json(keyboard, keymap, layout, layers, macros=None):\n    \"\"\"Generate the `keymap.json` and write it to disk.\n\n    Returns the filename written to.\n\n    Args:\n        keyboard\n            The name of the keyboard\n\n        keymap\n            The name of the keymap\n\n        layout\n            The LAYOUT macro this keymap uses.\n\n        layers\n            An array of arrays describing the keymap. Each item in the inner array should be a string that is a valid QMK keycode.\n    \"\"\"\n    keymap_json = generate_json(keyboard, keymap, layout, layers, macros=None)\n    keymap_content = json.dumps(keymap_json)\n    keymap_file = qmk.path.keymaps(keyboard)[0] / keymap / 'keymap.json'\n\n    return write_file(keymap_file, keymap_content)\n\n\ndef locate_keymap(keyboard, keymap, force_layout=None):\n    \"\"\"Returns the path to a keymap for a specific keyboard.\n    \"\"\"\n    if not qmk.path.is_keyboard(keyboard):\n        raise KeyError('Invalid keyboard: ' + repr(keyboard))\n\n    # Check the keyboard folder first, last match wins\n    keymap_path = ''\n\n    search_conf = {QMK_FIRMWARE: [keyboard_folder(keyboard)]}\n    if HAS_QMK_USERSPACE:\n        # When we've got userspace, check there _last_ as we want them to override anything in the main repo.\n        # We also want to search for any aliases as QMK's folder structure may have changed, with an alias, but the user\n        # hasn't updated their keymap location yet.\n        search_conf[QMK_USERSPACE] = list(set([keyboard_folder(keyboard), *keyboard_aliases(keyboard)]))\n\n    for search_dir, keyboard_dirs in search_conf.items():\n        for keyboard_dir in keyboard_dirs:\n            checked_dirs = ''\n            for folder_name in keyboard_dir.split('/'):\n                if checked_dirs:\n                    checked_dirs = '/'.join((checked_dirs, folder_name))\n                else:\n                    checked_dirs = folder_name\n\n                keymap_dir = Path(search_dir) / Path('keyboards') / checked_dirs / 'keymaps'\n\n                if (keymap_dir / keymap / 'keymap.c').exists():\n                    keymap_path = keymap_dir / keymap / 'keymap.c'\n                if (keymap_dir / keymap / 'keymap.json').exists():\n                    keymap_path = keymap_dir / keymap / 'keymap.json'\n\n        if keymap_path:\n            return keymap_path\n\n    # Check community layouts as a fallback\n    info = info_json(keyboard, force_layout=force_layout)\n\n    community_parents = list(Path('layouts').glob('*/'))\n    if HAS_QMK_USERSPACE and (Path(QMK_USERSPACE) / \"layouts\").exists():\n        community_parents.append(Path(QMK_USERSPACE) / \"layouts\")\n\n    for community_parent in community_parents:\n        for layout in info.get(\"community_layouts\", []):\n            community_layout = community_parent / layout / keymap\n            if community_layout.exists():\n                if (community_layout / 'keymap.json').exists():\n                    return community_layout / 'keymap.json'\n                if (community_layout / 'keymap.c').exists():\n                    return community_layout / 'keymap.c'\n\n\ndef is_keymap_target(keyboard, keymap):\n    if keymap == 'all':\n        return True\n\n    if locate_keymap(keyboard, keymap):\n        return True\n\n    return False\n\n\ndef list_keymaps(keyboard, c=True, json=True, additional_files=None, fullpath=False, include_userspace=True):\n    \"\"\"List the available keymaps for a keyboard.\n\n    Args:\n        keyboard\n            The keyboards full name with vendor and revision if necessary, example: clueboard/66/rev3\n\n        c\n            When true include `keymap.c` keymaps.\n\n        json\n            When true include `keymap.json` keymaps.\n\n        additional_files\n            A sequence of additional filenames to check against to determine if a directory is a keymap. All files must exist for a match to happen. For example, if you want to match a C keymap with both a `config.h` and `rules.mk` file: `is_keymap_dir(keymap_dir, json=False, additional_files=['config.h', 'rules.mk'])`\n\n        fullpath\n            When set to True the full path of the keymap relative to the `qmk_firmware` root will be provided.\n\n        include_userspace\n            When set to True, also search userspace for available keymaps\n\n    Returns:\n        a sorted list of valid keymap names.\n    \"\"\"\n    names = set()\n\n    has_userspace = HAS_QMK_USERSPACE and include_userspace\n\n    # walk up the directory tree until keyboards_dir\n    # and collect all directories' name with keymap.c file in it\n    for search_dir in [QMK_FIRMWARE, QMK_USERSPACE] if has_userspace else [QMK_FIRMWARE]:\n        keyboards_dir = search_dir / Path('keyboards')\n        kb_path = keyboards_dir / keyboard\n\n        while kb_path != keyboards_dir:\n            keymaps_dir = kb_path / \"keymaps\"\n            if keymaps_dir.is_dir():\n                for keymap in keymaps_dir.iterdir():\n                    if is_keymap_dir(keymap, c, json, additional_files):\n                        keymap = keymap if fullpath else keymap.name\n                        names.add(keymap)\n\n            kb_path = kb_path.parent\n\n    # Check community layouts as a fallback\n    info = info_json(keyboard)\n\n    community_parents = list(Path('layouts').glob('*/'))\n    if has_userspace and (Path(QMK_USERSPACE) / \"layouts\").exists():\n        community_parents.append(Path(QMK_USERSPACE) / \"layouts\")\n\n    for community_parent in community_parents:\n        for layout in info.get(\"community_layouts\", []):\n            cl_path = community_parent / layout\n            if cl_path.is_dir():\n                for keymap in cl_path.iterdir():\n                    if is_keymap_dir(keymap, c, json, additional_files):\n                        keymap = keymap if fullpath else keymap.name\n                        names.add(keymap)\n\n    return sorted(names)\n\n\ndef _c_preprocess(path, stdin=DEVNULL):\n    \"\"\" Run a file through the C pre-processor\n\n    Args:\n        path: path of the keymap.c file (set None to use stdin)\n        stdin: stdin pipe (e.g. sys.stdin)\n\n    Returns:\n        the stdout of the pre-processor\n    \"\"\"\n    cmd = ['cpp', str(path)] if path else ['cpp']\n    pre_processed_keymap = cli.run(cmd, stdin=stdin)\n    if 'fatal error' in pre_processed_keymap.stderr:\n        for line in pre_processed_keymap.stderr.split('\\n'):\n            if 'fatal error' in line:\n                raise (CppError(line))\n    return pre_processed_keymap.stdout\n\n\ndef _get_layers(keymap):  # noqa C901 : until someone has a good idea how to simplify/split up this code\n    \"\"\" Find the layers in a keymap.c file.\n\n    Args:\n        keymap: the content of the keymap.c file\n\n    Returns:\n        a dictionary containing the parsed keymap\n    \"\"\"\n    layers = list()\n    opening_braces = '({['\n    closing_braces = ')}]'\n    keymap_certainty = brace_depth = 0\n    is_keymap = is_layer = is_adv_kc = False\n    layer = dict(name=False, layout=False, keycodes=list())\n    for line in lex(keymap, CLexer()):\n        if line[0] is Token.Name:\n            if is_keymap:\n                # If we are inside the keymap array\n                # we know the keymap's name and the layout macro will come,\n                # followed by the keycodes\n                if not layer['name']:\n                    if line[1].startswith('LAYOUT') or line[1].startswith('KEYMAP'):\n                        # This can happen if the keymap array only has one layer,\n                        # for macropads and such\n                        layer['name'] = '0'\n                        layer['layout'] = line[1]\n                    else:\n                        layer['name'] = line[1]\n                elif not layer['layout']:\n                    layer['layout'] = line[1]\n                elif is_layer:\n                    # If we are inside a layout macro,\n                    # collect all keycodes\n                    if line[1] == '_______':\n                        kc = 'KC_TRNS'\n                    elif line[1] == 'XXXXXXX':\n                        kc = 'KC_NO'\n                    else:\n                        kc = line[1]\n                    if is_adv_kc:\n                        # If we are inside an advanced keycode\n                        # collect everything and hope the user\n                        # knew what he/she was doing\n                        layer['keycodes'][-1] += kc\n                    else:\n                        layer['keycodes'].append(kc)\n\n        # The keymaps array's signature:\n        # const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS]\n        #\n        # Only if we've found all 6 keywords in this specific order\n        # can we know for sure that we are inside the keymaps array\n            elif line[1] == 'PROGMEM' and keymap_certainty == 2:\n                keymap_certainty = 3\n            elif line[1] == 'keymaps' and keymap_certainty == 3:\n                keymap_certainty = 4\n            elif line[1] == 'MATRIX_ROWS' and keymap_certainty == 4:\n                keymap_certainty = 5\n            elif line[1] == 'MATRIX_COLS' and keymap_certainty == 5:\n                keymap_certainty = 6\n        elif line[0] is Token.Keyword:\n            if line[1] == 'const' and keymap_certainty == 0:\n                keymap_certainty = 1\n        elif line[0] is Token.Keyword.Type:\n            if line[1] == 'uint16_t' and keymap_certainty == 1:\n                keymap_certainty = 2\n        elif line[0] is Token.Punctuation:\n            if line[1] in opening_braces:\n                brace_depth += 1\n                if is_keymap:\n                    if is_layer:\n                        # We found the beginning of a non-basic keycode\n                        is_adv_kc = True\n                        layer['keycodes'][-1] += line[1]\n                    elif line[1] == '(' and brace_depth == 2:\n                        # We found the beginning of a layer\n                        is_layer = True\n                elif line[1] == '{' and keymap_certainty == 6:\n                    # We found the beginning of the keymaps array\n                    is_keymap = True\n            elif line[1] in closing_braces:\n                brace_depth -= 1\n                if is_keymap:\n                    if is_adv_kc:\n                        layer['keycodes'][-1] += line[1]\n                        if brace_depth == 2:\n                            # We found the end of a non-basic keycode\n                            is_adv_kc = False\n                    elif line[1] == ')' and brace_depth == 1:\n                        # We found the end of a layer\n                        is_layer = False\n                        layers.append(layer)\n                        layer = dict(name=False, layout=False, keycodes=list())\n                    elif line[1] == '}' and brace_depth == 0:\n                        # We found the end of the keymaps array\n                        is_keymap = False\n                        keymap_certainty = 0\n            elif is_adv_kc:\n                # Advanced keycodes can contain other punctuation\n                # e.g.: MT(MOD_LCTL | MOD_LSFT, KC_ESC)\n                layer['keycodes'][-1] += line[1]\n\n        elif line[0] is Token.Literal.Number.Integer and is_keymap and not is_adv_kc:\n            # If the pre-processor finds the 'meaning' of the layer names,\n            # they will be numbers\n            if not layer['name']:\n                layer['name'] = line[1]\n\n        else:\n            # We only care about\n            # operators and such if we\n            # are inside an advanced keycode\n            # e.g.: MT(MOD_LCTL | MOD_LSFT, KC_ESC)\n            if is_adv_kc:\n                layer['keycodes'][-1] += line[1]\n\n    return layers\n\n\ndef parse_keymap_c(keymap_file, use_cpp=True):\n    \"\"\" Parse a keymap.c file.\n\n    Currently only cares about the keymaps array.\n\n    Args:\n        keymap_file: path of the keymap.c file (or '-' to use stdin)\n\n        use_cpp: if True, pre-process the file with the C pre-processor\n\n    Returns:\n        a dictionary containing the parsed keymap\n    \"\"\"\n    if not isinstance(keymap_file, (Path, str)) or keymap_file == '-':\n        if use_cpp:\n            keymap_file = _c_preprocess(None, sys.stdin)\n        else:\n            keymap_file = sys.stdin.read()\n    else:\n        if use_cpp:\n            keymap_file = _c_preprocess(keymap_file)\n        else:\n            keymap_file = keymap_file.read_text(encoding='utf-8')\n\n    keymap = dict()\n    keymap['layers'] = _get_layers(keymap_file)\n    return keymap\n\n\ndef c2json(keyboard, keymap, keymap_file, use_cpp=True):\n    \"\"\" Convert keymap.c to keymap.json\n\n    Args:\n        keyboard: The name of the keyboard\n\n        keymap: The name of the keymap\n\n        layout: The LAYOUT macro this keymap uses.\n\n        keymap_file: path of the keymap.c file\n\n        use_cpp: if True, pre-process the file with the C pre-processor\n\n    Returns:\n        a dictionary in keymap.json format\n    \"\"\"\n    keymap_json = parse_keymap_c(keymap_file, use_cpp)\n\n    dirty_layers = keymap_json.pop('layers', None)\n    keymap_json['layers'] = list()\n    for layer in dirty_layers:\n        layer.pop('name')\n        layout = layer.pop('layout')\n        if not keymap_json.get('layout', False):\n            keymap_json['layout'] = layout\n        keymap_json['layers'].append(layer.pop('keycodes'))\n\n    keymap_json['keyboard'] = keyboard\n    keymap_json['keymap'] = keymap\n    return keymap_json\n"
  },
  {
    "path": "lib/python/qmk/makefile.py",
    "content": "\"\"\" Functions for working with Makefiles\n\"\"\"\nfrom pathlib import Path\n\n\ndef parse_rules_mk_file(file, rules_mk=None):\n    \"\"\"Turn a rules.mk file into a dictionary.\n\n    Args:\n        file: path to the rules.mk file\n        rules_mk: already parsed rules.mk the new file should be merged with\n\n    Returns:\n        a dictionary with the file's content\n    \"\"\"\n    if not rules_mk:\n        rules_mk = {}\n\n    file = Path(file)\n    if file.exists():\n        rules_mk_lines = file.read_text(encoding='utf-8').split(\"\\n\")\n\n        for line in rules_mk_lines:\n            # Filter out comments\n            if line.strip().startswith(\"#\"):\n                continue\n\n            # Strip in-line comments\n            if '#' in line:\n                line = line[:line.index('#')].strip()\n\n            if '=' in line:\n                # Append\n                if '+=' in line:\n                    key, value = line.split('+=', 1)\n                    if key.strip() not in rules_mk:\n                        rules_mk[key.strip()] = value.strip()\n                    else:\n                        rules_mk[key.strip()] += ' ' + value.strip()\n                # Set if absent\n                elif \"?=\" in line:\n                    key, value = line.split('?=', 1)\n                    if key.strip() not in rules_mk:\n                        rules_mk[key.strip()] = value.strip()\n                else:\n                    if \":=\" in line:\n                        line.replace(\":\", \"\")\n                    key, value = line.split('=', 1)\n                    rules_mk[key.strip()] = value.strip()\n\n    return rules_mk\n"
  },
  {
    "path": "lib/python/qmk/math.py",
    "content": "\"\"\"Parse arbitrary math equations in a safe way.\n\nGratefully copied from https://stackoverflow.com/a/9558001\n\"\"\"\nimport ast\nimport operator as op\n\n# supported operators\noperators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv, ast.Pow: op.pow, ast.BitXor: op.xor, ast.USub: op.neg}\n\n\ndef compute(expr):\n    \"\"\"Parse a mathematical expression and return the answer.\n\n    >>> compute('2^6')\n    4\n    >>> compute('2**6')\n    64\n    >>> compute('1 + 2*3**(4^5) / (6 + -7)')\n    -5.0\n    \"\"\"\n    return _eval(ast.parse(expr, mode='eval').body)\n\n\ndef _eval(node):\n    if isinstance(node, ast.Num):  # <number>\n        return node.n\n    elif isinstance(node, ast.BinOp):  # <left> <operator> <right>\n        return operators[type(node.op)](_eval(node.left), _eval(node.right))\n    elif isinstance(node, ast.UnaryOp):  # <operator> <operand> e.g., -1\n        return operators[type(node.op)](_eval(node.operand))\n    else:\n        raise TypeError(node)\n"
  },
  {
    "path": "lib/python/qmk/math_ops.py",
    "content": "\"\"\"Parse arbitrary math equations in a safe way.\n\nGratefully copied from https://stackoverflow.com/a/9558001\n\"\"\"\nimport ast\nimport operator as op\n\n# supported operators\noperators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv, ast.Pow: op.pow, ast.BitXor: op.xor, ast.USub: op.neg}\n\n\ndef compute(expr):\n    \"\"\"Parse a mathematical expression and return the answer.\n\n    >>> compute('2^6')\n    4\n    >>> compute('2**6')\n    64\n    >>> compute('1 + 2*3**(4^5) / (6 + -7)')\n    -5.0\n    \"\"\"\n    return _eval(ast.parse(expr, mode='eval').body)\n\n\ndef _eval(node):\n    if isinstance(node, ast.Constant):  # <number>\n        return node.value\n    elif isinstance(node, ast.BinOp):  # <left> <operator> <right>\n        return operators[type(node.op)](_eval(node.left), _eval(node.right))\n    elif isinstance(node, ast.UnaryOp):  # <operator> <operand> e.g., -1\n        return operators[type(node.op)](_eval(node.operand))\n    else:\n        raise TypeError(node)\n"
  },
  {
    "path": "lib/python/qmk/painter.py",
    "content": "\"\"\"Functions that help us work with Quantum Painter's file formats.\n\"\"\"\nimport datetime\nimport math\nimport re\nfrom pathlib import Path\nfrom string import Template\nfrom PIL import Image, ImageOps\n\n# The list of valid formats Quantum Painter supports\nvalid_formats = {\n    'rgb888': {\n        'image_format': 'IMAGE_FORMAT_RGB888',\n        'bpp': 24,\n        'has_palette': False,\n        'num_colors': 16777216,\n        'image_format_byte': 0x09,  # see qp_internal_formats.h\n    },\n    'rgb565': {\n        'image_format': 'IMAGE_FORMAT_RGB565',\n        'bpp': 16,\n        'has_palette': False,\n        'num_colors': 65536,\n        'image_format_byte': 0x08,  # see qp_internal_formats.h\n    },\n    'pal256': {\n        'image_format': 'IMAGE_FORMAT_PALETTE',\n        'bpp': 8,\n        'has_palette': True,\n        'num_colors': 256,\n        'image_format_byte': 0x07,  # see qp_internal_formats.h\n    },\n    'pal16': {\n        'image_format': 'IMAGE_FORMAT_PALETTE',\n        'bpp': 4,\n        'has_palette': True,\n        'num_colors': 16,\n        'image_format_byte': 0x06,  # see qp_internal_formats.h\n    },\n    'pal4': {\n        'image_format': 'IMAGE_FORMAT_PALETTE',\n        'bpp': 2,\n        'has_palette': True,\n        'num_colors': 4,\n        'image_format_byte': 0x05,  # see qp_internal_formats.h\n    },\n    'pal2': {\n        'image_format': 'IMAGE_FORMAT_PALETTE',\n        'bpp': 1,\n        'has_palette': True,\n        'num_colors': 2,\n        'image_format_byte': 0x04,  # see qp_internal_formats.h\n    },\n    'mono256': {\n        'image_format': 'IMAGE_FORMAT_GRAYSCALE',\n        'bpp': 8,\n        'has_palette': False,\n        'num_colors': 256,\n        'image_format_byte': 0x03,  # see qp_internal_formats.h\n    },\n    'mono16': {\n        'image_format': 'IMAGE_FORMAT_GRAYSCALE',\n        'bpp': 4,\n        'has_palette': False,\n        'num_colors': 16,\n        'image_format_byte': 0x02,  # see qp_internal_formats.h\n    },\n    'mono4': {\n        'image_format': 'IMAGE_FORMAT_GRAYSCALE',\n        'bpp': 2,\n        'has_palette': False,\n        'num_colors': 4,\n        'image_format_byte': 0x01,  # see qp_internal_formats.h\n    },\n    'mono2': {\n        'image_format': 'IMAGE_FORMAT_GRAYSCALE',\n        'bpp': 1,\n        'has_palette': False,\n        'num_colors': 2,\n        'image_format_byte': 0x00,  # see qp_internal_formats.h\n    }\n}\n\n\ndef _render_text(values):\n    # FIXME: May need more chars with GIFs containing lots of frames (or longer durations)\n    return \"|\".join([f\"{i:4d}\" for i in values])\n\n\ndef _render_numeration(metadata):\n    return _render_text(range(len(metadata)))\n\n\ndef _render_values(metadata, key):\n    return _render_text([i[key] for i in metadata])\n\n\ndef _render_image_metadata(metadata):\n    size = metadata.pop(0)\n\n    lines = [\n        \"// Image's metadata\",\n        \"// ----------------\",\n        f\"// Width: {size['width']}\",\n        f\"// Height: {size['height']}\",\n    ]\n\n    if len(metadata) == 1:\n        lines.append(\"// Single frame\")\n\n    else:\n        lines.extend([\n            f\"//        Frame: {_render_numeration(metadata)}\",\n            f\"// Duration(ms): {_render_values(metadata, 'delay')}\",\n            f\"//  Compression: {_render_values(metadata, 'compression')} >> See qp.h, painter_compression_t\",\n            f\"//        Delta: {_render_values(metadata, 'delta')}\",\n        ])\n\n        deltas = []\n        for i, v in enumerate(metadata):\n            # Not a delta frame, go to next one\n            if not v[\"delta\"]:\n                continue\n\n            # Unpack rect's coords\n            l, t, r, b = v[\"delta_rect\"]\n\n            delta_px = (r - l) * (b - t)\n            px = size[\"width\"] * size[\"height\"]\n\n            # FIXME: May need need more chars here too\n            deltas.append(f\"// Frame {i:3d}: ({l:3d}, {t:3d}) - ({r:3d}, {b:3d}) >> {delta_px:4d}/{px:4d} pixels ({100 * delta_px / px:.2f}%)\")\n\n        if deltas:\n            lines.append(\"// Areas on delta frames\")\n            lines.extend(deltas)\n\n    return \"\\n\".join(lines)\n\n\ndef command_args_str(cli, command_name):\n    \"\"\"Given a command name, introspect milc to get the arguments passed in.\"\"\"\n\n    args = {}\n    max_length = 0\n    for arg_name, was_passed in cli.args_passed[command_name].items():\n        max_length = max(max_length, len(arg_name))\n\n        val = getattr(cli.args, arg_name.replace(\"-\", \"_\"))\n\n        # do not leak full paths, keep just file name\n        if isinstance(val, Path):\n            val = val.name\n\n        args[arg_name] = val\n\n    return \"\\n\".join(f\"//    {arg_name.ljust(max_length)} | {val}\" for arg_name, val in args.items())\n\n\ndef generate_subs(cli, out_bytes, *, font_metadata=None, image_metadata=None, command_name):\n    if font_metadata is not None and image_metadata is not None:\n        raise ValueError(\"Cant generate subs for font and image at the same time\")\n\n    args = command_args_str(cli, command_name)\n\n    subs = {\n        \"year\": datetime.date.today().strftime(\"%Y\"),\n        \"input_file\": cli.args.input.name,\n        \"sane_name\": re.sub(r\"[^a-zA-Z0-9]\", \"_\", cli.args.input.stem),\n        \"byte_count\": len(out_bytes),\n        \"bytes_lines\": render_bytes(out_bytes),\n        \"format\": cli.args.format,\n        \"generator_command\": command_name.replace(\"_\", \"-\"),\n        \"command_args\": args,\n    }\n\n    if font_metadata is not None:\n        subs.update({\n            \"generated_type\": \"font\",\n            \"var_prefix\": \"font\",\n            # not using triple quotes to avoid extra indentation/weird formatted code\n            \"metadata\": \"\\n\".join([\n                \"// Font's metadata\",\n                \"// ---------------\",\n                f\"// Glyphs: {', '.join([i for i in font_metadata['glyphs']])}\",\n            ]),\n        })\n\n    elif image_metadata is not None:\n        subs.update({\n            \"generated_type\": \"image\",\n            \"var_prefix\": \"gfx\",\n            \"generator_command\": command_name,\n            \"metadata\": _render_image_metadata(image_metadata),\n        })\n\n    else:\n        raise ValueError(\"Pass metadata for either an image or a font\")\n\n    subs.update({\"license\": render_license(subs)})\n\n    return subs\n\n\nlicense_template = \"\"\"\\\n// Copyright ${year} QMK -- generated source code only, ${generated_type} retains original copyright\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n// This file was auto-generated by `${generator_command}` with arguments:\n${command_args}\n\"\"\"\n\n\ndef render_license(subs):\n    license_txt = Template(license_template)\n    return license_txt.substitute(subs)\n\n\nheader_file_template = \"\"\"\\\n${license}\n#pragma once\n\n#include <qp.h>\n\nextern const uint32_t ${var_prefix}_${sane_name}_length;\nextern const uint8_t  ${var_prefix}_${sane_name}[${byte_count}];\n\"\"\"\n\n\ndef render_header(subs):\n    header_txt = Template(header_file_template)\n    return header_txt.substitute(subs)\n\n\nsource_file_template = \"\"\"\\\n${license}\n${metadata}\n\n#include <qp.h>\n\nconst uint32_t ${var_prefix}_${sane_name}_length = ${byte_count};\n\n// clang-format off\nconst uint8_t ${var_prefix}_${sane_name}[${byte_count}] = {\n${bytes_lines}\n};\n// clang-format on\n\"\"\"\n\n\ndef render_source(subs):\n    source_txt = Template(source_file_template)\n    return source_txt.substitute(subs)\n\n\ndef render_bytes(bytes, newline_after=16):\n    lines = ''\n    for n in range(len(bytes)):\n        if n % newline_after == 0 and n > 0 and n != len(bytes):\n            lines = lines + \"\\n   \"\n        elif n == 0:\n            lines = lines + \"   \"\n        lines = lines + \" 0x{0:02X},\".format(bytes[n])\n    return lines.rstrip()\n\n\ndef clean_output(str):\n    str = re.sub(r'\\r', '', str)\n    str = re.sub(r'[\\n]{3,}', r'\\n\\n', str)\n    return str\n\n\ndef rescale_byte(val, maxval):\n    \"\"\"Rescales a byte value to the supplied range, i.e. [0,255] -> [0,maxval].\n    \"\"\"\n    return int(round(val * maxval / 255.0))\n\n\ndef convert_requested_format(im, format):\n    \"\"\"Convert an image to the requested format.\n    \"\"\"\n\n    # Work out the requested format\n    ncolors = format[\"num_colors\"]\n    image_format = format[\"image_format\"]\n\n    # -- Check if ncolors is valid\n    # Formats accepting several options\n    if image_format in ['IMAGE_FORMAT_GRAYSCALE', 'IMAGE_FORMAT_PALETTE']:\n        valid = [2, 4, 8, 16, 256]\n\n    # Formats expecting a particular number\n    else:\n        # Read number from specs dict, instead of hardcoding\n        for _, fmt in valid_formats.items():\n            if fmt[\"image_format\"] == image_format:\n                # has to be an iterable, to use `in`\n                valid = [fmt[\"num_colors\"]]\n                break\n\n    if ncolors not in valid:\n        raise ValueError(f\"Number of colors must be: {', '.join(valid)}.\")\n\n    # Work out where we're getting the bytes from\n    if image_format == 'IMAGE_FORMAT_GRAYSCALE':\n        # If mono, convert input to grayscale, then to RGB, then grab the raw bytes corresponding to the intensity of the red channel\n        im = ImageOps.grayscale(im)\n        im = im.convert(\"RGB\")\n    elif image_format == 'IMAGE_FORMAT_PALETTE':\n        # If color, convert input to RGB, palettize based on the supplied number of colors, then get the raw palette bytes\n        im = im.convert(\"RGB\")\n        im = im.convert(\"P\", palette=Image.ADAPTIVE, colors=ncolors)\n    elif image_format in ['IMAGE_FORMAT_RGB565', 'IMAGE_FORMAT_RGB888']:\n        # Convert input to RGB\n        im = im.convert(\"RGB\")\n\n    return im\n\n\ndef rgb_to565(r, g, b):\n    msb = ((r >> 3 & 0x1F) << 3) + (g >> 5 & 0x07)\n    lsb = ((g >> 2 & 0x07) << 5) + (b >> 3 & 0x1F)\n    return [msb, lsb]\n\n\ndef convert_image_bytes(im, format):\n    \"\"\"Convert the supplied image to the equivalent bytes required by the QMK firmware.\n    \"\"\"\n\n    # Work out the requested format\n    ncolors = format[\"num_colors\"]\n    image_format = format[\"image_format\"]\n    shifter = int(math.log2(ncolors))\n    pixels_per_byte = int(8 / math.log2(ncolors))\n    bytes_per_pixel = math.ceil(math.log2(ncolors) / 8)\n    (width, height) = im.size\n    if (pixels_per_byte != 0):\n        expected_byte_count = ((width * height) + (pixels_per_byte - 1)) // pixels_per_byte\n    else:\n        expected_byte_count = width * height * bytes_per_pixel\n\n    if image_format == 'IMAGE_FORMAT_GRAYSCALE':\n        # Take the red channel\n        image_bytes = im.tobytes(\"raw\", \"R\")\n        image_bytes_len = len(image_bytes)\n\n        # No palette\n        palette = None\n\n        bytearray = []\n        for x in range(expected_byte_count):\n            byte = 0\n            for n in range(pixels_per_byte):\n                byte_offset = x * pixels_per_byte + n\n                if byte_offset < image_bytes_len:\n                    # If mono, each input byte is a grayscale [0,255] pixel -- rescale to the range we want then pack together\n                    byte = byte | (rescale_byte(image_bytes[byte_offset], ncolors - 1) << int(n * shifter))\n            bytearray.append(byte)\n\n    elif image_format == 'IMAGE_FORMAT_PALETTE':\n        # Convert each pixel to the palette bytes\n        image_bytes = im.tobytes(\"raw\", \"P\")\n        image_bytes_len = len(image_bytes)\n\n        # Export the palette\n        palette = []\n        pal = im.getpalette()\n        for n in range(0, ncolors * 3, 3):\n            palette.append((pal[n + 0], pal[n + 1], pal[n + 2]))\n\n        bytearray = []\n        for x in range(expected_byte_count):\n            byte = 0\n            for n in range(pixels_per_byte):\n                byte_offset = x * pixels_per_byte + n\n                if byte_offset < image_bytes_len:\n                    # If color, each input byte is the index into the color palette -- pack them together\n                    byte = byte | ((image_bytes[byte_offset] & (ncolors - 1)) << int(n * shifter))\n            bytearray.append(byte)\n\n    if image_format == 'IMAGE_FORMAT_RGB565':\n        # Take the red, green, and blue channels\n        red = im.tobytes(\"raw\", \"R\")\n        green = im.tobytes(\"raw\", \"G\")\n        blue = im.tobytes(\"raw\", \"B\")\n\n        # No palette\n        palette = None\n\n        bytearray = [byte for r, g, b in zip(red, green, blue) for byte in rgb_to565(r, g, b)]\n\n    if image_format == 'IMAGE_FORMAT_RGB888':\n        # Take the red, green, and blue channels\n        red = im.tobytes(\"raw\", \"R\")\n        green = im.tobytes(\"raw\", \"G\")\n        blue = im.tobytes(\"raw\", \"B\")\n\n        # No palette\n        palette = None\n\n        bytearray = [byte for r, g, b in zip(red, green, blue) for byte in (r, g, b)]\n\n    if len(bytearray) != expected_byte_count:\n        raise Exception(f\"Wrong byte count, was {len(bytearray)}, expected {expected_byte_count}\")\n\n    return (palette, bytearray)\n\n\ndef compress_bytes_qmk_rle(bytearray):\n    debug_dump = False\n    output = []\n    temp = []\n    repeat = False\n\n    def append_byte(c):\n        if debug_dump:\n            print('Appending byte:', '0x{0:02X}'.format(int(c)), '=', c)\n        output.append(c)\n\n    def append_range(r):\n        append_byte(127 + len(r))\n        if debug_dump:\n            print('Appending {0} byte(s):'.format(len(r)), '[', ', '.join(['{0:02X}'.format(e) for e in r]), ']')\n        output.extend(r)\n\n    for n in range(0, len(bytearray) + 1):\n        end = True if n == len(bytearray) else False\n        if not end:\n            c = bytearray[n]\n            temp.append(c)\n            if len(temp) <= 1:\n                continue\n\n        if debug_dump:\n            print('Temp buffer state {0:3d} bytes:'.format(len(temp)), '[', ', '.join(['{0:02X}'.format(e) for e in temp]), ']')\n\n        if repeat:\n            if temp[-1] != temp[-2]:\n                repeat = False\n            if not repeat or len(temp) == 128 or end:\n                append_byte(len(temp) if end else len(temp) - 1)\n                append_byte(temp[0])\n                temp = [temp[-1]]\n                repeat = False\n        else:\n            if len(temp) >= 2 and temp[-1] == temp[-2]:\n                repeat = True\n                if len(temp) > 2:\n                    append_range(temp[0:(len(temp) - 2)])\n                    temp = [temp[-1], temp[-1]]\n                continue\n            if len(temp) == 128 or end:\n                append_range(temp)\n                temp = []\n                repeat = False\n    return output\n"
  },
  {
    "path": "lib/python/qmk/painter_qff.py",
    "content": "# Copyright 2021 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\n\n# Quantum Font File \"QFF\" Font File Format.\n# See https://docs.qmk.fm/#/quantum_painter_qff for more information.\n\nfrom pathlib import Path\nfrom typing import Dict, Any\nfrom colorsys import rgb_to_hsv\nfrom PIL import Image, ImageDraw, ImageFont, ImageChops\nfrom PIL._binary import o8, o16le as o16, o32le as o32\nfrom qmk.painter_qgf import QGFBlockHeader, QGFFramePaletteDescriptorV1\nfrom milc.attrdict import AttrDict\nimport qmk.painter\n\n\ndef o24(i):\n    return o16(i & 0xFFFF) + o8((i & 0xFF0000) >> 16)\n\n\n########################################################################################################################\n\n\nclass QFFGlyphInfo(AttrDict):\n    def __init__(self, *args, **kwargs):\n        super().__init__()\n\n        for n, value in enumerate(args):\n            self[f'arg:{n}'] = value\n\n        for key, value in kwargs.items():\n            self[key] = value\n\n    def write(self, fp, include_code_point):\n        if include_code_point is True:\n            fp.write(o24(ord(self.code_point)))\n\n        value = ((self.data_offset << 6) & 0xFFFFC0) | (self.w & 0x3F)\n        fp.write(o24(value))\n\n\n########################################################################################################################\n\n\nclass QFFFontDescriptor:\n    type_id = 0x00\n    length = 20\n    magic = 0x464651\n\n    def __init__(self):\n        self.header = QGFBlockHeader()\n        self.header.type_id = QFFFontDescriptor.type_id\n        self.header.length = QFFFontDescriptor.length\n        self.version = 1\n        self.total_file_size = 0\n        self.line_height = 0\n        self.has_ascii_table = False\n        self.unicode_glyph_count = 0\n        self.format = 0xFF\n        self.flags = 0\n        self.compression = 0xFF\n        self.transparency_index = 0xFF  # TODO: Work out how to retrieve the transparent palette entry from the PIL gif loader\n\n    def write(self, fp):\n        self.header.write(fp)\n        fp.write(\n            b''  # start off with empty bytes...\n            + o24(QFFFontDescriptor.magic)  # magic\n            + o8(self.version)  # version\n            + o32(self.total_file_size)  # file size\n            + o32((~self.total_file_size) & 0xFFFFFFFF)  # negated file size\n            + o8(self.line_height)  # line height\n            + o8(1 if self.has_ascii_table is True else 0)  # whether or not we have an ascii table present\n            + o16(self.unicode_glyph_count & 0xFFFF)  # number of unicode glyphs present\n            + o8(self.format)  # format\n            + o8(self.flags)  # flags\n            + o8(self.compression)  # compression\n            + o8(self.transparency_index)  # transparency index\n        )\n\n    @property\n    def is_transparent(self):\n        return (self.flags & 0x01) == 0x01\n\n    @is_transparent.setter\n    def is_transparent(self, val):\n        if val:\n            self.flags |= 0x01\n        else:\n            self.flags &= ~0x01\n\n\n########################################################################################################################\n\n\nclass QFFAsciiGlyphTableV1:\n    type_id = 0x01\n    length = 95 * 3  # We have 95 glyphs: [0x20...0x7E]\n\n    def __init__(self):\n        self.header = QGFBlockHeader()\n        self.header.type_id = QFFAsciiGlyphTableV1.type_id\n        self.header.length = QFFAsciiGlyphTableV1.length\n\n        # Each glyph is key=code_point, value=QFFGlyphInfo\n        self.glyphs = {}\n\n    def add_glyph(self, glyph: QFFGlyphInfo):\n        self.glyphs[ord(glyph.code_point)] = glyph\n\n    def write(self, fp):\n        self.header.write(fp)\n\n        for n in range(0x20, 0x7F):\n            self.glyphs[n].write(fp, False)\n\n\n########################################################################################################################\n\n\nclass QFFUnicodeGlyphTableV1:\n    type_id = 0x02\n\n    def __init__(self):\n        self.header = QGFBlockHeader()\n        self.header.type_id = QFFUnicodeGlyphTableV1.type_id\n        self.header.length = 0\n\n        # Each glyph is key=code_point, value=QFFGlyphInfo\n        self.glyphs = {}\n\n    def add_glyph(self, glyph: QFFGlyphInfo):\n        self.glyphs[ord(glyph.code_point)] = glyph\n\n    def write(self, fp):\n        self.header.length = len(self.glyphs.keys()) * 6\n        self.header.write(fp)\n\n        for n in sorted(self.glyphs.keys()):\n            self.glyphs[n].write(fp, True)\n\n\n########################################################################################################################\n\n\nclass QFFFontDataDescriptorV1:\n    type_id = 0x04\n\n    def __init__(self):\n        self.header = QGFBlockHeader()\n        self.header.type_id = QFFFontDataDescriptorV1.type_id\n        self.data = []\n\n    def write(self, fp):\n        self.header.length = len(self.data)\n        self.header.write(fp)\n        fp.write(bytes(self.data))\n\n\n########################################################################################################################\n\n\ndef _generate_font_glyphs_list(use_ascii, unicode_glyphs):\n    # The set of glyphs that we want to generate images for\n    glyphs = {}\n\n    # Add ascii charset if requested\n    if use_ascii is True:\n        for c in range(0x20, 0x7F):  # does not include 0x7F!\n            glyphs[chr(c)] = True\n\n    # Append any extra unicode glyphs\n    unicode_glyphs = list(unicode_glyphs)\n    for c in unicode_glyphs:\n        glyphs[c] = True\n\n    return sorted(glyphs.keys())\n\n\nclass QFFFont:\n    def __init__(self, logger):\n        self.logger = logger\n        self.image = None\n        self.glyph_data = {}\n        self.glyph_height = 0\n        return\n\n    def _extract_glyphs(self, format):\n        total_data_size = 0\n        total_rle_data_size = 0\n\n        converted_img = qmk.painter.convert_requested_format(self.image, format)\n        (self.palette, _) = qmk.painter.convert_image_bytes(converted_img, format)\n\n        # Work out how many bytes used for RLE vs. non-RLE\n        for _, glyph_entry in self.glyph_data.items():\n            glyph_img = converted_img.crop((glyph_entry.x, 1, glyph_entry.x + glyph_entry.w, 1 + self.glyph_height))\n            (_, this_glyph_image_bytes) = qmk.painter.convert_image_bytes(glyph_img, format)\n            this_glyph_rle_bytes = qmk.painter.compress_bytes_qmk_rle(this_glyph_image_bytes)\n            total_data_size += len(this_glyph_image_bytes)\n            total_rle_data_size += len(this_glyph_rle_bytes)\n            glyph_entry['image_uncompressed_bytes'] = this_glyph_image_bytes\n            glyph_entry['image_compressed_bytes'] = this_glyph_rle_bytes\n\n        return (total_data_size, total_rle_data_size)\n\n    def _parse_image(self, img, include_ascii_glyphs: bool = True, unicode_glyphs: str = ''):\n        # Clear out any existing font metadata\n        self.image = None\n        # Each glyph is key=code_point, value={ x: ?, w: ? }\n        self.glyph_data = {}\n        self.glyph_height = 0\n\n        # Work out the list of glyphs required\n        glyphs = _generate_font_glyphs_list(include_ascii_glyphs, unicode_glyphs)\n\n        # Work out the geometry\n        (width, height) = img.size\n\n        # Work out the glyph offsets/widths\n        glyph_pixel_offsets = []\n        glyph_pixel_widths = []\n        pixels = img.load()\n\n        # Run through the markers and work out where each glyph starts/stops\n        glyph_split_color = pixels[0, 0]  # top left pixel is the marker color we're going to use to split each glyph\n        glyph_pixel_offsets.append(0)\n        last_offset = 0\n        for x in range(1, width):\n            if pixels[x, 0] == glyph_split_color:\n                glyph_pixel_offsets.append(x)\n                glyph_pixel_widths.append(x - last_offset)\n                last_offset = x\n        glyph_pixel_widths.append(width - last_offset)\n\n        # Make sure the number of glyphs we're attempting to generate matches the input image\n        if len(glyph_pixel_offsets) != len(glyphs):\n            self.logger.error('The number of glyphs to generate doesn\\'t match the number of detected glyphs in the input image.')\n            return\n\n        # Set up the required metadata for each glyph\n        for n in range(0, len(glyph_pixel_offsets)):\n            self.glyph_data[glyphs[n]] = QFFGlyphInfo(code_point=glyphs[n], x=glyph_pixel_offsets[n], w=glyph_pixel_widths[n])\n\n        # Parsing was successful, keep the image in this instance\n        self.image = img\n        self.glyph_height = height - 1  # subtract the line with the markers\n\n    def generate_image(self, ttf_file: Path, font_size: int, include_ascii_glyphs: bool = True, unicode_glyphs: str = '', include_before_left: bool = False, use_aa: bool = True):\n        # Load the font\n        font = ImageFont.truetype(str(ttf_file), int(font_size))\n        # Work out the max font size\n        max_font_size = font.font.ascent + abs(font.font.descent)\n        # Work out the list of glyphs required\n        glyphs = _generate_font_glyphs_list(include_ascii_glyphs, unicode_glyphs)\n\n        baseline_offset = 9999999\n        total_glyph_width = 0\n        max_glyph_height = -1\n\n        # Measure each glyph to determine the overall baseline offset required\n        for glyph in glyphs:\n            (ls_l, ls_t, ls_r, ls_b) = font.getbbox(glyph, anchor='ls')\n            glyph_width = (ls_r - ls_l) if include_before_left else (ls_r)\n            glyph_height = font.getbbox(glyph, anchor='la')[3]\n            if max_glyph_height < glyph_height:\n                max_glyph_height = glyph_height\n            total_glyph_width += glyph_width\n            if baseline_offset > ls_t:\n                baseline_offset = ls_t\n\n        # Create the output image\n        img = Image.new(\"RGB\", (total_glyph_width + 1, max_font_size * 2 + 1), (0, 0, 0, 255))\n        cur_x_pos = 0\n\n        # Loop through each glyph...\n        for glyph in glyphs:\n            # Work out this glyph's bounding box\n            (ls_l, ls_t, ls_r, ls_b) = font.getbbox(glyph, anchor='ls')\n            glyph_width = (ls_r - ls_l) if include_before_left else (ls_r)\n            glyph_height = ls_b - ls_t\n            x_offset = -ls_l\n            y_offset = ls_t - baseline_offset\n\n            # Draw each glyph to its own image so we don't get anti-aliasing applied to the final image when straddling edges\n            glyph_img = Image.new(\"RGB\", (glyph_width, max_font_size), (0, 0, 0, 255))\n            glyph_draw = ImageDraw.Draw(glyph_img)\n            if not use_aa:\n                glyph_draw.fontmode = \"1\"\n            glyph_draw.text((x_offset, y_offset), glyph, font=font, anchor='lt')\n\n            # Place the glyph-specific image in the correct location overall\n            img.paste(glyph_img, (cur_x_pos, 1))\n\n            # Set up the marker for start of each glyph\n            pixels = img.load()\n            pixels[cur_x_pos, 0] = (255, 0, 255)\n\n            # Increment for the next glyph's position\n            cur_x_pos += glyph_width\n\n        # Add the ending marker so that the difference/crop works\n        pixels = img.load()\n        pixels[cur_x_pos, 0] = (255, 0, 255)\n\n        # Determine the usable font area\n        dummy_img = Image.new(\"RGB\", (total_glyph_width + 1, max_font_size + 1), (0, 0, 0, 255))\n        bbox = ImageChops.difference(img, dummy_img).getbbox()\n        bbox = (bbox[0], bbox[1], bbox[2] - 1, bbox[3])  # remove the unused end-marker\n\n        # Crop and re-parse the resulting image to ensure we're generating the correct format\n        self._parse_image(img.crop(bbox), include_ascii_glyphs, unicode_glyphs)\n\n    def save_to_image(self, img_file: Path):\n        # Drop out if there's no image loaded\n        if self.image is None:\n            self.logger.error('No image is loaded.')\n            return\n\n        # Save the image to the supplied file\n        self.image.save(str(img_file))\n\n    def read_from_image(self, img_file: Path, include_ascii_glyphs: bool = True, unicode_glyphs: str = ''):\n        # Load and parse the supplied image file\n        self._parse_image(Image.open(str(img_file)), include_ascii_glyphs, unicode_glyphs)\n        return\n\n    def save_to_qff(self, format: Dict[str, Any], use_rle: bool, fp):\n        # Drop out if there's no image loaded\n        if self.image is None:\n            self.logger.error('No image is loaded.')\n            return\n\n        # Work out if we want to use RLE at all, skipping it if it's not any smaller (it's applied per-glyph)\n        (total_data_size, total_rle_data_size) = self._extract_glyphs(format)\n        if use_rle:\n            use_rle = (total_rle_data_size < total_data_size)\n\n        # For each glyph, work out which image data we want to use and append it to the image buffer, recording the byte-wise offset\n        img_buffer = bytes()\n        for _, glyph_entry in self.glyph_data.items():\n            glyph_entry['data_offset'] = len(img_buffer)\n            glyph_img_bytes = glyph_entry.image_compressed_bytes if use_rle else glyph_entry.image_uncompressed_bytes\n            img_buffer += bytes(glyph_img_bytes)\n\n        font_descriptor = QFFFontDescriptor()\n        ascii_table = QFFAsciiGlyphTableV1()\n        unicode_table = QFFUnicodeGlyphTableV1()\n        data_descriptor = QFFFontDataDescriptorV1()\n        data_descriptor.data = img_buffer\n\n        # Check if we have all the ASCII glyphs present\n        include_ascii_glyphs = all([chr(n) in self.glyph_data for n in range(0x20, 0x7F)])\n\n        # Helper for populating the blocks\n        for code_point, glyph_entry in self.glyph_data.items():\n            if ord(code_point) >= 0x20 and ord(code_point) <= 0x7E and include_ascii_glyphs:\n                ascii_table.add_glyph(glyph_entry)\n            else:\n                unicode_table.add_glyph(glyph_entry)\n\n        # Configure the font descriptor\n        font_descriptor.line_height = self.glyph_height\n        font_descriptor.has_ascii_table = include_ascii_glyphs\n        font_descriptor.unicode_glyph_count = len(unicode_table.glyphs.keys())\n        font_descriptor.is_transparent = False\n        font_descriptor.format = format['image_format_byte']\n        font_descriptor.compression = 0x01 if use_rle else 0x00\n\n        # Write a dummy font descriptor -- we'll have to come back and write it properly once we've rendered out everything else\n        font_descriptor_location = fp.tell()\n        font_descriptor.write(fp)\n\n        # Write out the ASCII table if required\n        if font_descriptor.has_ascii_table:\n            ascii_table.write(fp)\n\n        # Write out the unicode table if required\n        if font_descriptor.unicode_glyph_count > 0:\n            unicode_table.write(fp)\n\n        # Write out the palette if required\n        if format['has_palette']:\n            palette_descriptor = QGFFramePaletteDescriptorV1()\n\n            # Helper to convert from RGB888 to the QMK \"dialect\" of HSV888\n            def rgb888_to_qmk_hsv888(e):\n                hsv = rgb_to_hsv(e[0] / 255.0, e[1] / 255.0, e[2] / 255.0)\n                return (int(hsv[0] * 255.0), int(hsv[1] * 255.0), int(hsv[2] * 255.0))\n\n            # Convert all palette entries to HSV888 and write to the output\n            palette_descriptor.palette_entries = list(map(rgb888_to_qmk_hsv888, self.palette))\n            palette_descriptor.write(fp)\n\n        # Write out the image data\n        data_descriptor.write(fp)\n\n        # Now fix up the overall font descriptor, then write it in the correct location\n        font_descriptor.total_file_size = fp.tell()\n        fp.seek(font_descriptor_location, 0)\n        font_descriptor.write(fp)\n"
  },
  {
    "path": "lib/python/qmk/painter_qgf.py",
    "content": "# Copyright 2021 Nick Brassel (@tzarc)\n# Copyright 2023 Pablo Martinez (@elpekenin) <elpekenin@elpekenin.dev>\n# SPDX-License-Identifier: GPL-2.0-or-later\n\n# Quantum Graphics File \"QGF\" Image File Format.\n# See https://docs.qmk.fm/#/quantum_painter_qgf for more information.\n\nimport functools\nfrom colorsys import rgb_to_hsv\nfrom types import FunctionType\nfrom PIL import Image, ImageFile, ImageChops\nfrom PIL._binary import o8, o16le as o16, o32le as o32\nimport qmk.painter\n\n\ndef o24(i):\n    return o16(i & 0xFFFF) + o8((i & 0xFF0000) >> 16)\n\n\n# Helper to convert from RGB888 to the QMK \"dialect\" of HSV888\ndef rgb888_to_qmk_hsv888(e):\n    hsv = rgb_to_hsv(e[0] / 255.0, e[1] / 255.0, e[2] / 255.0)\n    return (int(hsv[0] * 255.0), int(hsv[1] * 255.0), int(hsv[2] * 255.0))\n\n\n########################################################################################################################\n\n\nclass QGFBlockHeader:\n    block_size = 5\n\n    def write(self, fp):\n        fp.write(b''  # start off with empty bytes...\n                 + o8(self.type_id)  # block type id\n                 + o8((~self.type_id) & 0xFF)  # negated block type id\n                 + o24(self.length)  # blob length\n                 )\n\n\n########################################################################################################################\n\n\nclass QGFGraphicsDescriptor:\n    type_id = 0x00\n    length = 18\n    magic = 0x464751\n\n    def __init__(self):\n        self.header = QGFBlockHeader()\n        self.header.type_id = QGFGraphicsDescriptor.type_id\n        self.header.length = QGFGraphicsDescriptor.length\n        self.version = 1\n        self.total_file_size = 0\n        self.image_width = 0\n        self.image_height = 0\n        self.frame_count = 0\n\n    def write(self, fp):\n        self.header.write(fp)\n        fp.write(\n            b''  # start off with empty bytes...\n            + o24(QGFGraphicsDescriptor.magic)  # magic\n            + o8(self.version)  # version\n            + o32(self.total_file_size)  # file size\n            + o32((~self.total_file_size) & 0xFFFFFFFF)  # negated file size\n            + o16(self.image_width)  # width\n            + o16(self.image_height)  # height\n            + o16(self.frame_count)  # frame count\n        )\n\n    @property\n    def image_size(self):\n        return self.image_width, self.image_height\n\n    @image_size.setter\n    def image_size(self, size):\n        self.image_width, self.image_height = size\n\n\n########################################################################################################################\n\n\nclass QGFFrameOffsetDescriptorV1:\n    type_id = 0x01\n\n    def __init__(self, frame_count):\n        self.header = QGFBlockHeader()\n        self.header.type_id = QGFFrameOffsetDescriptorV1.type_id\n        self.frame_offsets = [0xFFFFFFFF] * frame_count\n        self.frame_count = frame_count\n\n    def write(self, fp):\n        self.header.length = len(self.frame_offsets) * 4\n        self.header.write(fp)\n        for offset in self.frame_offsets:\n            fp.write(b''  # start off with empty bytes...\n                     + o32(offset)  # offset\n                     )\n\n\n########################################################################################################################\n\n\nclass QGFFrameDescriptorV1:\n    type_id = 0x02\n    length = 6\n\n    def __init__(self):\n        self.header = QGFBlockHeader()\n        self.header.type_id = QGFFrameDescriptorV1.type_id\n        self.header.length = QGFFrameDescriptorV1.length\n        self.format = 0xFF\n        self.flags = 0\n        self.compression = 0xFF\n        self.transparency_index = 0xFF  # TODO: Work out how to retrieve the transparent palette entry from the PIL gif loader\n        self.delay = 1000  # Placeholder until it gets read from the animation\n\n    def write(self, fp):\n        self.header.write(fp)\n        fp.write(b''  # start off with empty bytes...\n                 + o8(self.format)  # format\n                 + o8(self.flags)  # flags\n                 + o8(self.compression)  # compression\n                 + o8(self.transparency_index)  # transparency index\n                 + o16(self.delay)  # delay\n                 )\n\n    @property\n    def is_transparent(self):\n        return (self.flags & 0x01) == 0x01\n\n    @is_transparent.setter\n    def is_transparent(self, val):\n        if val:\n            self.flags |= 0x01\n        else:\n            self.flags &= ~0x01\n\n    @property\n    def is_delta(self):\n        return (self.flags & 0x02) == 0x02\n\n    @is_delta.setter\n    def is_delta(self, val):\n        if val:\n            self.flags |= 0x02\n        else:\n            self.flags &= ~0x02\n\n\n########################################################################################################################\n\n\nclass QGFFramePaletteDescriptorV1:\n    type_id = 0x03\n\n    def __init__(self):\n        self.header = QGFBlockHeader()\n        self.header.type_id = QGFFramePaletteDescriptorV1.type_id\n        self.header.length = 0\n        self.palette_entries = [(0xFF, 0xFF, 0xFF)] * 4\n\n    def write(self, fp):\n        self.header.length = len(self.palette_entries) * 3\n        self.header.write(fp)\n        for entry in self.palette_entries:\n            fp.write(b''  # start off with empty bytes...\n                     + o8(entry[0])  # h\n                     + o8(entry[1])  # s\n                     + o8(entry[2])  # v\n                     )\n\n\n########################################################################################################################\n\n\nclass QGFFrameDeltaDescriptorV1:\n    type_id = 0x04\n    length = 8\n\n    def __init__(self):\n        self.header = QGFBlockHeader()\n        self.header.type_id = QGFFrameDeltaDescriptorV1.type_id\n        self.header.length = QGFFrameDeltaDescriptorV1.length\n        self.left = 0\n        self.top = 0\n        self.right = 0\n        self.bottom = 0\n\n    def write(self, fp):\n        self.header.write(fp)\n        fp.write(b''  # start off with empty bytes...\n                 + o16(self.left)  # left\n                 + o16(self.top)  # top\n                 + o16(self.right)  # right\n                 + o16(self.bottom)  # bottom\n                 )\n\n    @property\n    def bbox(self):\n        return self.left, self.top, self.right, self.bottom\n\n    @bbox.setter\n    def bbox(self, bbox):\n        self.left, self.top, self.right, self.bottom = bbox\n\n\n########################################################################################################################\n\n\nclass QGFFrameDataDescriptorV1:\n    type_id = 0x05\n\n    def __init__(self):\n        self.header = QGFBlockHeader()\n        self.header.type_id = QGFFrameDataDescriptorV1.type_id\n        self.data = []\n\n    def write(self, fp):\n        self.header.length = len(self.data)\n        self.header.write(fp)\n        fp.write(bytes(self.data))\n\n\n########################################################################################################################\n\n\nclass QGFImageFile(ImageFile.ImageFile):\n\n    format = \"QGF\"\n    format_description = \"Quantum Graphics File Format\"\n\n    def _open(self):\n        raise NotImplementedError(\"Reading QGF files is not supported\")\n\n\n########################################################################################################################\n\n\ndef _accept(prefix):\n    \"\"\"Helper method used by PIL to work out if it can parse an input file.\n\n    Currently unimplemented.\n    \"\"\"\n    return False\n\n\ndef _for_all_frames(x: FunctionType, /, images):\n    frame_num = 0\n    last_frame = None\n    for frame in images:\n        # Get number of of frames in this image\n        nfr = getattr(frame, \"n_frames\", 1)\n        for idx in range(nfr):\n            frame.seek(idx)\n            frame.load()\n            copy = frame.copy().convert(\"RGB\")\n            x(frame_num, copy, last_frame)\n            last_frame = copy\n            frame_num += 1\n\n\ndef _compress_image(frame, last_frame, *, use_rle, use_deltas, format_, **_kwargs):\n    # Convert the original frame so we can do comparisons\n    converted = qmk.painter.convert_requested_format(frame, format_)\n    graphic_data = qmk.painter.convert_image_bytes(converted, format_)\n\n    # Convert the raw data to RLE-encoded if requested\n    raw_data = graphic_data[1]\n    if use_rle:\n        rle_data = qmk.painter.compress_bytes_qmk_rle(graphic_data[1])\n    use_raw_this_frame = not use_rle or len(raw_data) <= len(rle_data)\n    image_data = raw_data if use_raw_this_frame else rle_data\n\n    # Work out if a delta frame is smaller than injecting it directly\n    use_delta_this_frame = False\n    bbox = None\n    if use_deltas and last_frame is not None:\n        # If we want to use deltas, then find the difference\n        diff = ImageChops.difference(frame, last_frame)\n\n        # Get the bounding box of those differences\n        bbox = diff.getbbox()\n\n        # If we have a valid bounding box...\n        if bbox:\n            # ...create the delta frame by cropping the original.\n            delta_frame = frame.crop(bbox)\n\n            # Convert the delta frame to the requested format\n            delta_converted = qmk.painter.convert_requested_format(delta_frame, format_)\n            delta_graphic_data = qmk.painter.convert_image_bytes(delta_converted, format_)\n\n            # Work out how large the delta frame is going to be with compression etc.\n            delta_raw_data = delta_graphic_data[1]\n            if use_rle:\n                delta_rle_data = qmk.painter.compress_bytes_qmk_rle(delta_graphic_data[1])\n            delta_use_raw_this_frame = not use_rle or len(delta_raw_data) <= len(delta_rle_data)\n            delta_image_data = delta_raw_data if delta_use_raw_this_frame else delta_rle_data\n\n            # If the size of the delta frame (plus delta descriptor) is smaller than the original, use that instead\n            # This ensures that if a non-delta is overall smaller in size, we use that in preference due to flash\n            # sizing constraints.\n            if (len(delta_image_data) + QGFFrameDeltaDescriptorV1.length) < len(image_data):\n                # Copy across all the delta equivalents so that the rest of the processing acts on those\n                graphic_data = delta_graphic_data\n                raw_data = delta_raw_data\n                rle_data = delta_rle_data\n                use_raw_this_frame = delta_use_raw_this_frame\n                image_data = delta_image_data\n                use_delta_this_frame = True\n\n        # Default to whole image\n        bbox = bbox or [0, 0, *frame.size]\n        # Fix sze (as per #20296), we need to cast first as tuples are inmutable\n        bbox = list(bbox)\n        bbox[2] -= 1\n        bbox[3] -= 1\n\n    return {\n        \"bbox\": bbox,\n        \"graphic_data\": graphic_data,\n        \"image_data\": image_data,\n        \"use_delta_this_frame\": use_delta_this_frame,\n        \"use_raw_this_frame\": use_raw_this_frame,\n    }\n\n\n# Helper function to save each frame to the output file\ndef _write_frame(idx, frame, last_frame, *, fp, frame_offsets, metadata, **kwargs):\n    # Not an argument of the function as it would then not be part of kwargs\n    # This would cause an issue with `_compress_image(**kwargs)` missing an argument\n    format_ = kwargs[\"format_\"]\n\n    # (potentially) Apply RLE and/or delta, and work out output image's information\n    outputs = _compress_image(frame, last_frame, **kwargs)\n    bbox = outputs[\"bbox\"]\n    graphic_data = outputs[\"graphic_data\"]\n    image_data = outputs[\"image_data\"]\n    use_delta_this_frame = outputs[\"use_delta_this_frame\"]\n    use_raw_this_frame = outputs[\"use_raw_this_frame\"]\n\n    # Write out the frame descriptor\n    frame_offsets.frame_offsets[idx] = fp.tell()\n    vprint(f'{f\"Frame {idx:3d} base\":26s} {fp.tell():5d}d / {fp.tell():04X}h')\n    frame_descriptor = QGFFrameDescriptorV1()\n    frame_descriptor.is_delta = use_delta_this_frame\n    frame_descriptor.is_transparent = False\n    frame_descriptor.format = format_['image_format_byte']\n    frame_descriptor.compression = 0x00 if use_raw_this_frame else 0x01  # See qp.h, painter_compression_t\n    frame_descriptor.delay = frame.info.get('duration', 1000)  # If we're not an animation, just pretend we're delaying for 1000ms\n    frame_descriptor.write(fp)\n\n    # Write out the palette if required\n    if format_['has_palette']:\n        palette = graphic_data[0]\n        palette_descriptor = QGFFramePaletteDescriptorV1()\n\n        # Convert all palette entries to HSV888 and write to the output\n        palette_descriptor.palette_entries = list(map(rgb888_to_qmk_hsv888, palette))\n        vprint(f'{f\"Frame {idx:3d} palette\":26s} {fp.tell():5d}d / {fp.tell():04X}h')\n        palette_descriptor.write(fp)\n\n    # Write out the delta info if required\n    if use_delta_this_frame:\n        # Set up the rendering location of where the delta frame should be situated\n        delta_descriptor = QGFFrameDeltaDescriptorV1()\n        delta_descriptor.bbox = bbox\n\n        # Write the delta frame to the output\n        vprint(f'{f\"Frame {idx:3d} delta\":26s} {fp.tell():5d}d / {fp.tell():04X}h')\n        delta_descriptor.write(fp)\n\n    # Store metadata, showed later in a comment in the generated file\n    frame_metadata = {\n        \"compression\": frame_descriptor.compression,\n        \"delta\": frame_descriptor.is_delta,\n        \"delay\": frame_descriptor.delay,\n    }\n    if frame_metadata[\"delta\"]:\n        frame_metadata.update({\"delta_rect\": [\n            delta_descriptor.left,\n            delta_descriptor.top,\n            delta_descriptor.right,\n            delta_descriptor.bottom,\n        ]})\n    metadata.append(frame_metadata)\n\n    # Write out the data for this frame to the output\n    data_descriptor = QGFFrameDataDescriptorV1()\n    data_descriptor.data = image_data\n    vprint(f'{f\"Frame {idx:3d} data\":26s} {fp.tell():5d}d / {fp.tell():04X}h')\n    data_descriptor.write(fp)\n\n\ndef _save(im, fp, _filename):\n    \"\"\"Helper method used by PIL to write to an output file.\n    \"\"\"\n    # Work out from the parameters if we need to do anything special\n    encoderinfo = im.encoderinfo.copy()\n\n    # Store image file in metadata structure\n    metadata = encoderinfo.get(\"metadata\", [])\n    metadata.append({\"width\": im.width, \"height\": im.height})\n\n    # Helper for prints, noop taking any args if not verbose\n    global vprint\n    verbose = encoderinfo.get(\"verbose\", False)\n    vprint = print if verbose else lambda *_args, **_kwargs: None\n\n    # Helper to iterate through all frames in the input image\n    append_images = list(encoderinfo.get(\"append_images\", []))\n    for_all_frames = functools.partial(_for_all_frames, images=[im, *append_images])\n\n    # Collect all the frame sizes\n    frame_sizes = []\n    for_all_frames(lambda _idx, frame, _last_frame: frame_sizes.append(frame.size))\n\n    # Make sure all frames are the same size\n    if len(set(frame_sizes)) != 1:\n        raise ValueError(\"Mismatching sizes on frames\")\n\n    # Write out the initial graphics descriptor (and write a dummy value), so that we can come back and fill in the\n    # correct values once we've written all the frames to the output\n    graphics_descriptor_location = fp.tell()\n    graphics_descriptor = QGFGraphicsDescriptor()\n    graphics_descriptor.frame_count = len(frame_sizes)\n    graphics_descriptor.image_size = frame_sizes[0]\n    vprint(f'{\"Graphics descriptor block\":26s} {fp.tell():5d}d / {fp.tell():04X}h')\n    graphics_descriptor.write(fp)\n\n    # Work out the frame offset descriptor location (and write a dummy value), so that we can come back and fill in the\n    # correct offsets once we've written all the frames to the output\n    frame_offset_location = fp.tell()\n    frame_offsets = QGFFrameOffsetDescriptorV1(graphics_descriptor.frame_count)\n    vprint(f'{\"Frame offsets block\":26s} {fp.tell():5d}d / {fp.tell():04X}h')\n    frame_offsets.write(fp)\n\n    # Iterate over each if the input frames, writing it to the output in the process\n    write_frame = functools.partial(_write_frame, format_=encoderinfo[\"qmk_format\"], fp=fp, use_deltas=encoderinfo.get(\"use_deltas\", True), use_rle=encoderinfo.get(\"use_rle\", True), frame_offsets=frame_offsets, metadata=metadata)\n    for_all_frames(write_frame)\n\n    # Go back and update the graphics descriptor now that we can determine the final file size\n    graphics_descriptor.total_file_size = fp.tell()\n    fp.seek(graphics_descriptor_location, 0)\n    graphics_descriptor.write(fp)\n\n    # Go back and update the frame offsets now that they're written to the file\n    fp.seek(frame_offset_location, 0)\n    frame_offsets.write(fp)\n\n\n########################################################################################################################\n\n# Register with PIL so that it knows about the QGF format\nImage.register_open(QGFImageFile.format, QGFImageFile, _accept)\nImage.register_save(QGFImageFile.format, _save)\nImage.register_save_all(QGFImageFile.format, _save)\nImage.register_extension(QGFImageFile.format, f\".{QGFImageFile.format.lower()}\")\nImage.register_mime(QGFImageFile.format, f\"image/{QGFImageFile.format.lower()}\")\n"
  },
  {
    "path": "lib/python/qmk/path.py",
    "content": "\"\"\"Functions that help us work with files and folders.\n\"\"\"\nimport logging\nimport os\nimport argparse\nfrom pathlib import Path, PureWindowsPath, PurePosixPath\n\nfrom qmk.constants import MAX_KEYBOARD_SUBFOLDERS, QMK_FIRMWARE, QMK_USERSPACE, HAS_QMK_USERSPACE\nfrom qmk.errors import NoSuchKeyboardError\n\n\ndef is_keyboard(keyboard_name):\n    \"\"\"Returns True if `keyboard_name` is a keyboard we can compile.\n    \"\"\"\n    if not keyboard_name:\n        return False\n\n    # keyboard_name values of 'c:/something' or '/something' trigger append issues\n    # due to \"If the argument is an absolute path, the previous path is ignored\"\n    # however it should always be a folder located under qmk_firmware/keyboards\n    if Path(keyboard_name).is_absolute():\n        return False\n\n    keyboard_json = QMK_FIRMWARE / 'keyboards' / keyboard_name / 'keyboard.json'\n\n    return keyboard_json.exists()\n\n\ndef under_qmk_firmware(path=Path(os.environ['ORIG_CWD'])):\n    \"\"\"Returns a Path object representing the relative path under qmk_firmware, or None.\n    \"\"\"\n    try:\n        return path.relative_to(QMK_FIRMWARE)\n    except ValueError:\n        return None\n\n\ndef under_qmk_userspace(path=Path(os.environ['ORIG_CWD'])):\n    \"\"\"Returns a Path object representing the relative path under $QMK_USERSPACE, or None.\n    \"\"\"\n    try:\n        if HAS_QMK_USERSPACE:\n            return path.relative_to(QMK_USERSPACE)\n    except ValueError:\n        pass\n    return None\n\n\ndef is_under_qmk_firmware(path=Path(os.environ['ORIG_CWD'])):\n    \"\"\"Returns a boolean if the input path is a child under qmk_firmware.\n    \"\"\"\n    if path is None:\n        return False\n    try:\n        return Path(os.path.commonpath([Path(path), QMK_FIRMWARE])) == QMK_FIRMWARE\n    except ValueError:\n        return False\n\n\ndef is_under_qmk_userspace(path=Path(os.environ['ORIG_CWD'])):\n    \"\"\"Returns a boolean if the input path is a child under $QMK_USERSPACE.\n    \"\"\"\n    if path is None:\n        return False\n    try:\n        if HAS_QMK_USERSPACE:\n            return Path(os.path.commonpath([Path(path), QMK_USERSPACE])) == QMK_USERSPACE\n    except ValueError:\n        return False\n\n\ndef keyboard(keyboard_name):\n    \"\"\"Returns the path to a keyboard's directory relative to the qmk root.\n    \"\"\"\n    return Path('keyboards') / keyboard_name\n\n\ndef keymaps(keyboard_name):\n    \"\"\"Returns all of the `keymaps/` directories for a given keyboard.\n\n    Args:\n\n        keyboard_name\n            The name of the keyboard. Example: clueboard/66/rev3\n    \"\"\"\n    keyboard_folder = keyboard(keyboard_name)\n    found_dirs = []\n\n    if HAS_QMK_USERSPACE:\n        this_keyboard_folder = Path(QMK_USERSPACE) / keyboard_folder\n        for _ in range(MAX_KEYBOARD_SUBFOLDERS):\n            if (this_keyboard_folder / 'keymaps').exists():\n                found_dirs.append((this_keyboard_folder / 'keymaps').resolve())\n\n            this_keyboard_folder = this_keyboard_folder.parent\n            if this_keyboard_folder.resolve() == QMK_USERSPACE.resolve():\n                break\n\n        # We don't have any relevant keymap directories in userspace, so we'll use the fully-qualified path instead.\n        if len(found_dirs) == 0:\n            found_dirs.append((QMK_USERSPACE / keyboard_folder / 'keymaps').resolve())\n\n    this_keyboard_folder = QMK_FIRMWARE / keyboard_folder\n    for _ in range(MAX_KEYBOARD_SUBFOLDERS):\n        if (this_keyboard_folder / 'keymaps').exists():\n            found_dirs.append((this_keyboard_folder / 'keymaps').resolve())\n\n        this_keyboard_folder = this_keyboard_folder.parent\n        if this_keyboard_folder.resolve() == QMK_FIRMWARE.resolve():\n            break\n\n    if len(found_dirs) > 0:\n        return found_dirs\n\n    logging.error('Could not find the keymaps directory!')\n    raise NoSuchKeyboardError('Could not find keymaps directory for: %s' % keyboard_name)\n\n\ndef keymap(keyboard_name, keymap_name):\n    \"\"\"Locate the directory of a given keymap.\n\n    Args:\n\n        keyboard_name\n            The name of the keyboard. Example: clueboard/66/rev3\n        keymap_name\n            The name of the keymap. Example: default\n    \"\"\"\n    for keymap_dir in keymaps(keyboard_name):\n        if (keymap_dir / keymap_name).exists():\n            return (keymap_dir / keymap_name).resolve()\n\n\ndef normpath(path):\n    \"\"\"Returns a `pathlib.Path()` object for a given path.\n\n    This will use the path to a file as seen from the directory the script was called from. You should use this to normalize filenames supplied from the command line.\n    \"\"\"\n    path = Path(path)\n\n    if path.is_absolute():\n        return path\n\n    return Path(os.environ['ORIG_CWD']) / path\n\n\ndef unix_style_path(path):\n    \"\"\"Converts a Windows-style path with drive letter to a Unix path.\n\n    Path().as_posix() normally returns the path with drive letter and forward slashes, so is inappropriate for `Makefile` paths.\n\n    Passes through unadulterated if the path is not a Windows-style path.\n\n    Args:\n\n        path\n            The path to convert.\n\n    Returns:\n        The input path converted to Unix format.\n    \"\"\"\n    if isinstance(path, PureWindowsPath):\n        p = list(path.parts)\n        p[0] = f'/{p[0][0].lower()}'  # convert from `X:/` to `/x`\n        path = PurePosixPath(*p)\n    return path\n\n\nclass FileType(argparse.FileType):\n    def __init__(self, *args, **kwargs):\n        # Use UTF8 by default for stdin\n        if 'encoding' not in kwargs:\n            kwargs['encoding'] = 'UTF-8'\n        return super().__init__(*args, **kwargs)\n\n    def __call__(self, string):\n        \"\"\"normalize and check exists\n            otherwise magic strings like '-' for stdin resolve to bad paths\n        \"\"\"\n        norm = normpath(string)\n        return norm if norm.exists() else super().__call__(string)\n"
  },
  {
    "path": "lib/python/qmk/search.py",
    "content": "\"\"\"Functions for searching through QMK keyboards and keymaps.\n\"\"\"\nfrom dataclasses import dataclass\nimport contextlib\nimport functools\nimport fnmatch\nimport json\nimport logging\nimport re\nfrom typing import Callable, Dict, List, Optional, Tuple, Union\nfrom dotty_dict import dotty, Dotty\nfrom milc import cli\n\nfrom qmk.util import parallel_map\nfrom qmk.info import keymap_json\nfrom qmk.keyboard import list_keyboards, keyboard_folder\nfrom qmk.keymap import list_keymaps, locate_keymap\nfrom qmk.build_targets import KeyboardKeymapBuildTarget, BuildTarget\n\n\n@dataclass\nclass KeyboardKeymapDesc:\n    keyboard: str\n    keymap: str\n    data: dict = None\n    extra_args: dict = None\n\n    def __hash__(self) -> int:\n        return self.keyboard.__hash__() ^ self.keymap.__hash__() ^ json.dumps(self.extra_args, sort_keys=True).__hash__()\n\n    def __lt__(self, other) -> bool:\n        return (self.keyboard, self.keymap, json.dumps(self.extra_args, sort_keys=True)) < (other.keyboard, other.keymap, json.dumps(other.extra_args, sort_keys=True))\n\n    def load_data(self):\n        data = keymap_json(self.keyboard, self.keymap)\n        self.data = data.to_dict() if isinstance(data, Dotty) else data\n\n    @property\n    def dotty(self) -> Dotty:\n        return dotty(self.data) if self.data is not None else None\n\n    def to_build_target(self) -> KeyboardKeymapBuildTarget:\n        target = KeyboardKeymapBuildTarget(keyboard=self.keyboard, keymap=self.keymap, json=self.data)\n        target.extra_args = self.extra_args\n        return target\n\n\n# by using a class for filters, we dont need to worry about capturing values\n# see details <https://github.com/qmk/qmk_firmware/pull/21090>\nclass FilterFunction:\n    \"\"\"Base class for filters.\n    It provides:\n        - __init__: capture key and value\n\n    Each subclass should provide:\n        - func_name: how it will be specified on CLI\n            >>> qmk find -f <func_name>...\n        - apply: function that actually applies the filter\n            ie: return whether the input kb/km satisfies the condition\n    \"\"\"\n\n    key: str\n    value: Optional[str]\n\n    func_name: str\n    apply: Callable[[KeyboardKeymapDesc], bool]\n\n    def __init__(self, key, value):\n        self.key = key\n        self.value = value\n\n\nclass Exists(FilterFunction):\n    func_name = \"exists\"\n\n    def apply(self, target_info: KeyboardKeymapDesc) -> bool:\n        return self.key in target_info.dotty\n\n\nclass Absent(FilterFunction):\n    func_name = \"absent\"\n\n    def apply(self, target_info: KeyboardKeymapDesc) -> bool:\n        return self.key not in target_info.dotty\n\n\nclass Length(FilterFunction):\n    func_name = \"length\"\n\n    def apply(self, target_info: KeyboardKeymapDesc) -> bool:\n        info_dotty = target_info.dotty\n        return (self.key in info_dotty and len(info_dotty[self.key]) == int(self.value))\n\n\nclass Contains(FilterFunction):\n    func_name = \"contains\"\n\n    def apply(self, target_info: KeyboardKeymapDesc) -> bool:\n        info_dotty = target_info.dotty\n        return (self.key in info_dotty and self.value in info_dotty[self.key])\n\n\ndef _get_filter_class(func_name: str, key: str, value: str) -> Optional[FilterFunction]:\n    \"\"\"Initialize a filter subclass based on regex findings and return it.\n    None if no there's no filter with the name queried.\n    \"\"\"\n\n    for subclass in FilterFunction.__subclasses__():\n        if func_name == subclass.func_name:\n            return subclass(key, value)\n\n    return None\n\n\ndef filter_help() -> str:\n    names = [f\"'{f.func_name}'\" for f in FilterFunction.__subclasses__()]\n    return \", \".join(names[:-1]) + f\" and {names[-1]}\"\n\n\ndef _set_log_level(level):\n    cli.acquire_lock()\n    try:\n        old = cli.log_level\n        cli.log_level = level\n    except AttributeError:\n        old = cli.log.level\n    cli.log.setLevel(level)\n    logging.root.setLevel(level)\n    cli.release_lock()\n    return old\n\n\n@contextlib.contextmanager\ndef ignore_logging():\n    old = _set_log_level(logging.CRITICAL)\n    yield\n    _set_log_level(old)\n\n\ndef _all_keymaps(keyboard) -> List[KeyboardKeymapDesc]:\n    \"\"\"Returns a list of KeyboardKeymapDesc for all keymaps for the given keyboard.\n    \"\"\"\n    with ignore_logging():\n        keyboard = keyboard_folder(keyboard)\n        return [KeyboardKeymapDesc(keyboard, keymap) for keymap in list_keymaps(keyboard)]\n\n\ndef _keymap_exists(keyboard, keymap):\n    \"\"\"Returns the keyboard name if the keyboard+keymap combination exists, otherwise None.\n    \"\"\"\n    with ignore_logging():\n        return keyboard if locate_keymap(keyboard, keymap) is not None else None\n\n\ndef _load_keymap_info(target: KeyboardKeymapDesc) -> KeyboardKeymapDesc:\n    \"\"\"Ensures a KeyboardKeymapDesc has its data loaded.\n    \"\"\"\n    with ignore_logging():\n        target.load_data()  # Ensure we load the data first\n        return target\n\n\ndef expand_make_targets(targets: List[Union[str, Tuple[str, Dict[str, str]]]]) -> List[KeyboardKeymapDesc]:\n    \"\"\"Expand a list of make targets into a list of KeyboardKeymapDesc.\n\n    Caters for 'all' in either keyboard or keymap, or both.\n    \"\"\"\n    split_targets = []\n    for target in targets:\n        extra_args = None\n        if isinstance(target, tuple):\n            split_target = target[0].split(':')\n            extra_args = target[1]\n        else:\n            split_target = target.split(':')\n        if len(split_target) != 2:\n            cli.log.error(f\"Invalid build target: {target}\")\n            return []\n        split_targets.append(KeyboardKeymapDesc(split_target[0], split_target[1], extra_args=extra_args))\n    return expand_keymap_targets(split_targets)\n\n\ndef _expand_keymap_target(target: KeyboardKeymapDesc, all_keyboards: List[str] = None) -> List[KeyboardKeymapDesc]:\n    \"\"\"Expand a keyboard input and keymap input into a list of KeyboardKeymapDesc.\n\n    Caters for 'all' in either keyboard or keymap, or both.\n    \"\"\"\n    if all_keyboards is None:\n        all_keyboards = list_keyboards()\n\n    if target.keyboard == 'all':\n        if target.keymap == 'all':\n            cli.log.info('Retrieving list of all keyboards and keymaps...')\n            targets = []\n            for kb in parallel_map(_all_keymaps, all_keyboards):\n                targets.extend(kb)\n            for t in targets:\n                t.extra_args = target.extra_args\n            return targets\n        else:\n            cli.log.info(f'Retrieving list of keyboards with keymap \"{target.keymap}\"...')\n            keyboard_filter = functools.partial(_keymap_exists, keymap=target.keymap)\n            return [KeyboardKeymapDesc(kb, target.keymap, extra_args=target.extra_args) for kb in filter(lambda e: e is not None, parallel_map(keyboard_filter, all_keyboards))]\n    else:\n        if target.keymap == 'all':\n            cli.log.info(f'Retrieving list of keymaps for keyboard \"{target.keyboard}\"...')\n            targets = _all_keymaps(target.keyboard)\n            for t in targets:\n                t.extra_args = target.extra_args\n            return targets\n        else:\n            return [target]\n\n\ndef expand_keymap_targets(targets: List[KeyboardKeymapDesc]) -> List[KeyboardKeymapDesc]:\n    \"\"\"Expand a list of KeyboardKeymapDesc inclusive of 'all', into a list of explicit KeyboardKeymapDesc.\n    \"\"\"\n    overall_targets = []\n    all_keyboards = list_keyboards()\n    for target in targets:\n        overall_targets.extend(_expand_keymap_target(target, all_keyboards))\n    return list(sorted(set(overall_targets)))\n\n\ndef _construct_build_target(e: KeyboardKeymapDesc):\n    return e.to_build_target()\n\n\ndef _filter_keymap_targets(target_list: List[KeyboardKeymapDesc], filters: List[str] = []) -> List[KeyboardKeymapDesc]:\n    \"\"\"Filter a list of KeyboardKeymapDesc based on the supplied filters.\n\n    Optionally includes the values of the queried info.json keys.\n    \"\"\"\n    if len(filters) == 0:\n        cli.log.info('Preparing target list...')\n        targets = target_list\n    else:\n        cli.log.info('Parsing data for all matching keyboard/keymap combinations...')\n        valid_targets = parallel_map(_load_keymap_info, target_list)\n\n        function_re = re.compile(r'^(?P<function>[a-zA-Z]+)\\((?P<key>[a-zA-Z0-9_\\.]+)(,\\s*(?P<value>[^#]+))?\\)$')\n        comparison_re = re.compile(r'^(?P<key>[a-zA-Z0-9_\\.]+)\\s*(?P<op>[\\<\\>\\!=]=|\\<|\\>)\\s*(?P<value>[^#]+)$')\n\n        for filter_expr in filters:\n            function_match = function_re.match(filter_expr)\n            comparison_match = comparison_re.match(filter_expr)\n\n            if function_match is not None:\n                func_name = function_match.group('function').lower()\n                key = function_match.group('key')\n                value = function_match.group('value')\n\n                filter_class = _get_filter_class(func_name, key, value)\n                if filter_class is None:\n                    cli.log.warning(f'Unrecognized filter expression: {function_match.group(0)}')\n                    continue\n                valid_targets = filter(filter_class.apply, valid_targets)\n\n                value_str = f\", {{fg_cyan}}{value}{{fg_reset}}\" if value is not None else \"\"\n                cli.log.info(f'Filtering on condition: {{fg_green}}{func_name}{{fg_reset}}({{fg_cyan}}{key}{{fg_reset}}{value_str})...')\n\n            elif comparison_match is not None:\n                key = comparison_match.group('key')\n                op = comparison_match.group('op')\n                value = comparison_match.group('value')\n                cli.log.info(f'Filtering on condition: {{fg_cyan}}{key}{{fg_reset}} {op} {{fg_cyan}}{value}{{fg_reset}}...')\n\n                def _make_filter(k, o, v):\n                    expr = fnmatch.translate(v)\n                    rule = re.compile(f'^{expr}$', re.IGNORECASE)\n\n                    def f(e: KeyboardKeymapDesc):\n                        lhs = e.dotty.get(k)\n                        rhs = v\n\n                        if o in ['<', '>', '<=', '>=']:\n                            lhs = int(False if lhs is None else lhs)\n                            rhs = int(rhs)\n\n                            if o == '<':\n                                return lhs < rhs\n                            elif o == '>':\n                                return lhs > rhs\n                            elif o == '<=':\n                                return lhs <= rhs\n                            elif o == '>=':\n                                return lhs >= rhs\n                        else:\n                            lhs = str(False if lhs is None else lhs)\n\n                            if o == '!=':\n                                return rule.search(lhs) is None\n                            elif o == '==':\n                                return rule.search(lhs) is not None\n\n                    return f\n\n                valid_targets = filter(_make_filter(key, op, value), valid_targets)\n            else:\n                cli.log.warning(f'Unrecognized filter expression: {filter_expr}')\n                continue\n\n        cli.log.info('Preparing target list...')\n        targets = list(sorted(set(valid_targets)))\n\n    return targets\n\n\ndef search_keymap_targets(targets: List[Union[Tuple[str, str], Tuple[str, str, Dict[str, str]]]] = [('all', 'default')], filters: List[str] = []) -> List[BuildTarget]:\n    \"\"\"Search for build targets matching the supplied criteria.\n    \"\"\"\n    def _make_desc(e):\n        if len(e) == 3:\n            return KeyboardKeymapDesc(keyboard=e[0], keymap=e[1], extra_args=e[2])\n        else:\n            return KeyboardKeymapDesc(keyboard=e[0], keymap=e[1])\n\n    targets = map(_make_desc, targets)\n    targets = _filter_keymap_targets(expand_keymap_targets(targets), filters)\n    targets = list(set(parallel_map(_construct_build_target, list(targets))))\n    return sorted(targets)\n\n\ndef search_make_targets(targets: List[Union[str, Tuple[str, Dict[str, str]]]], filters: List[str] = []) -> List[BuildTarget]:\n    \"\"\"Search for build targets matching the supplied criteria.\n    \"\"\"\n    targets = _filter_keymap_targets(expand_make_targets(targets), filters)\n    targets = list(set(parallel_map(_construct_build_target, list(targets))))\n    return sorted(targets)\n"
  },
  {
    "path": "lib/python/qmk/submodules.py",
    "content": "\"\"\"Functions for working with QMK's submodules.\n\"\"\"\nfrom milc import cli\n\n\ndef status():\n    \"\"\"Returns a dictionary of submodules.\n\n    Each entry is a dict of the form:\n\n        {\n            'name': 'submodule_name',\n            'status': None/False/True,\n            'githash': '<sha-1 hash for the submodule>'\n            'shorthash': '<short hash for the submodule>'\n            'describe': '<output of `git describe --tags`>'\n            'last_log_message': 'log message'\n            'last_log_timestamp': 'timestamp'\n        }\n\n    status is None when the submodule doesn't exist, False when it's out of date, and True when it's current\n    \"\"\"\n    submodules = {}\n    gitmodule_config = cli.run(['git', 'config', '-f', '.gitmodules', '-l'], timeout=30)\n    for line in gitmodule_config.stdout.splitlines():\n        key, value = line.split('=', maxsplit=2)\n        if key.endswith('.path'):\n            submodules[value] = {'name': value, 'status': None}\n\n    git_cmd = cli.run(['git', 'submodule', 'status'], timeout=30)\n    for line in git_cmd.stdout.splitlines():\n        status = line[0]\n        githash, submodule = line[1:].split()[:2]\n        submodules[submodule]['githash'] = githash\n\n        if status == '-':\n            submodules[submodule]['status'] = None\n        elif status == '+':\n            submodules[submodule]['status'] = False\n        elif status == ' ':\n            submodules[submodule]['status'] = True\n        else:\n            raise ValueError('Unknown `git submodule status` sha-1 prefix character: \"%s\"' % status)\n\n    submodule_logs = cli.run(['git', 'submodule', '-q', 'foreach', 'git --no-pager log --no-show-signature --pretty=format:\"$sm_path%x01%h%x01%ad%x01%s%x0A\" --date=iso -n1'])\n    for log_line in submodule_logs.stdout.splitlines():\n        r = log_line.split('\\x01')\n        submodule = r[0]\n        submodules[submodule]['shorthash'] = r[1] if len(r) > 1 else ''\n        submodules[submodule]['last_log_timestamp'] = r[2] if len(r) > 2 else ''\n        submodules[submodule]['last_log_message'] = r[3] if len(r) > 3 else ''\n\n    submodule_tags = cli.run(['git', 'submodule', '-q', 'foreach', '\\'echo $sm_path `git describe --tags`\\''])\n    for log_line in submodule_tags.stdout.splitlines():\n        r = log_line.split()\n        submodule = r[0]\n        submodules[submodule]['describe'] = r[1] if len(r) > 1 else ''\n\n    return submodules\n\n\ndef update(submodules=None):\n    \"\"\"Update the submodules.\n\n        submodules\n            A string containing a single submodule or a list of submodules.\n    \"\"\"\n    git_sync_cmd = ['git', 'submodule', 'sync']\n    git_update_cmd = ['git', 'submodule', 'update', '--init']\n\n    if submodules is None:\n        # Update everything\n        git_sync_cmd.append('--recursive')\n        git_update_cmd.append('--recursive')\n        cli.run(git_sync_cmd, check=True)\n        cli.run(git_update_cmd, check=True)\n\n    else:\n        if isinstance(submodules, str):\n            # Update only a single submodule\n            git_sync_cmd.append(submodules)\n            git_update_cmd.append(submodules)\n            cli.run(git_sync_cmd, check=True)\n            cli.run(git_update_cmd, check=True)\n\n        else:\n            # Update submodules in a list\n            for submodule in submodules:\n                cli.run([*git_sync_cmd, submodule], check=True)\n                cli.run([*git_update_cmd, submodule], check=True)\n"
  },
  {
    "path": "lib/python/qmk/tests/.gitignore",
    "content": "# Ignore generated info.json from pytest\ninfo.json\n"
  },
  {
    "path": "lib/python/qmk/tests/__init__.py",
    "content": ""
  },
  {
    "path": "lib/python/qmk/tests/attrdict.py",
    "content": "class AttrDict(dict):\n    \"\"\"A dictionary that can be accessed by attributes.\n\n    This should only be used to mock objects for unit testing. Please do not use this outside of qmk.tests.\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        super(AttrDict, self).__init__(*args, **kwargs)\n        self.__dict__ = self\n"
  },
  {
    "path": "lib/python/qmk/tests/kle.txt",
    "content": "[\"¬\\n`\",\"!\\n1\",\"\\\"\\n2\",\"£\\n3\",\"$\\n4\",\"%\\n5\",\"^\\n6\",\"&\\n7\",\"*\\n8\",\"(\\n9\",\")\\n0\",\"_\\n-\",\"+\\n=\",{w:2},\"Backspace\"],\n[{w:1.5},\"Tab\",\"Q\",\"W\",\"E\",\"R\",\"T\",\"Y\",\"U\",\"I\",\"O\",\"P\",\"{\\n[\",\"}\\n]\",{x:0.25,w:1.25,h:2,w2:1.5,h2:1,x2:-0.25},\"Enter\"],\n[{w:1.75},\"Caps Lock\",\"A\",\"S\",\"D\",\"F\",\"G\",\"H\",\"J\",\"K\",\"L\",\":\\n;\",\"@\\n'\",\"~\\n#\"],\n[{w:1.25},\"Shift\",\"|\\n\\\\\",\"Z\",\"X\",\"C\",\"V\",\"B\",\"N\",\"M\",\"<\\n,\",\">\\n.\",\"?\\n/\",{w:2.75},\"Shift\"],\n[{w:1.25},\"Ctrl\",{w:1.25},\"Win\",{w:1.25},\"Alt\",{a:7,w:6.25},\"\",{a:4,w:1.25},\"AltGr\",{w:1.25},\"Win\",{w:1.25},\"Menu\",{w:1.25},\"Ctrl\"]\n"
  },
  {
    "path": "lib/python/qmk/tests/minimal_info.json",
    "content": "{\n    \"keyboard_name\": \"tester\",\n    \"maintainer\": \"qmk\",\n    \"layouts\": {\n        \"LAYOUT\": {\n            \"layout\": [\n                {\"label\": \"KC_A\", \"matrix\": [0, 0], \"x\": 0, \"y\": 0}\n            ]\n        }\n    }\n}\n"
  },
  {
    "path": "lib/python/qmk/tests/minimal_keymap.json",
    "content": "{\n    \"keyboard\": \"handwired/pytest/basic\",\n    \"keymap\": \"test\",\n    \"layers\": [[\"KC_A\"]],\n    \"layout\": \"LAYOUT_ortho_1x1\",\n    \"version\": 1\n}\n"
  },
  {
    "path": "lib/python/qmk/tests/test_cli_commands.py",
    "content": "import platform\nfrom subprocess import DEVNULL\n\nfrom milc import cli\n\nis_windows = 'windows' in platform.platform().lower()\n\n\ndef check_subcommand(command, *args):\n    cmd = ['qmk', command, *args]\n    result = cli.run(cmd, stdin=DEVNULL, combined_output=True)\n    return result\n\n\ndef check_subcommand_stdin(file_to_read, command, *args):\n    \"\"\"Pipe content of a file to a command and return output.\n    \"\"\"\n    with open(file_to_read, encoding='utf-8') as my_file:\n        cmd = ['qmk', command, *args]\n        result = cli.run(cmd, stdin=my_file, combined_output=True)\n    return result\n\n\ndef check_returncode(result, expected=[0]):\n    \"\"\"Print stdout if `result.returncode` does not match `expected`.\n    \"\"\"\n    if result.returncode not in expected:\n        print('`%s` stdout:' % ' '.join(result.args))\n        print(result.stdout)\n        print('returncode:', result.returncode)\n    assert result.returncode in expected\n\n\ndef test_format_c():\n    result = check_subcommand('format-c', '-n', 'quantum/matrix.c')\n    check_returncode(result)\n\n\ndef test_format_c_all():\n    result = check_subcommand('format-c', '-n', '-a')\n    check_returncode(result, [0, 1])\n\n\ndef test_compile():\n    result = check_subcommand('compile', '-kb', 'handwired/pytest/basic', '-km', 'default', '-n')\n    check_returncode(result)\n\n\ndef test_compile_json():\n    result = check_subcommand('compile', '-kb', 'handwired/pytest/basic', '-km', 'default_json', '-n')\n    check_returncode(result)\n\n\ndef test_flash():\n    result = check_subcommand('flash', '-kb', 'handwired/pytest/basic', '-km', 'default', '-n')\n    check_returncode(result)\n\n\ndef test_flash_bootloaders():\n    result = check_subcommand('flash', '-b')\n    check_returncode(result, [1])\n\n\ndef test_kle2json():\n    result = check_subcommand('kle2json', 'lib/python/qmk/tests/kle.txt', '-f')\n    check_returncode(result)\n    assert 'Wrote out' in result.stdout\n\n\ndef test_doctor():\n    result = check_subcommand('doctor', '-n')\n    check_returncode(result, [0, 1])\n    assert 'QMK Doctor is checking your environment.' in result.stdout\n    assert 'QMK is ready to go' in result.stdout\n\n\ndef test_hello():\n    result = check_subcommand('hello')\n    check_returncode(result)\n    assert 'Hello,' in result.stdout\n\n\ndef test_format_python():\n    result = check_subcommand('format-python', '-n', '-a')\n    check_returncode(result)\n    assert 'Successfully formatted the python code.' in result.stdout\n\n\ndef test_list_keyboards():\n    result = check_subcommand('list-keyboards')\n    check_returncode(result)\n    # check to see if a known keyboard is returned\n    # this will fail if handwired/pytest/basic is removed\n    assert 'handwired/pytest/basic' in result.stdout\n\n\ndef test_list_keymaps():\n    result = check_subcommand('list-keymaps', '-kb', 'handwired/pytest/basic')\n    check_returncode(result)\n    assert 'default' in result.stdout\n    assert 'default_json' in result.stdout\n\n\ndef test_list_keymaps_long():\n    result = check_subcommand('list-keymaps', '--keyboard', 'handwired/pytest/basic')\n    check_returncode(result)\n    assert 'default' in result.stdout\n    assert 'default_json' in result.stdout\n\n\ndef test_list_keymaps_community():\n    result = check_subcommand('list-keymaps', '--keyboard', 'handwired/pytest/has_community')\n    check_returncode(result)\n    assert 'test' in result.stdout\n\n\ndef test_list_keymaps_kb_only():\n    result = check_subcommand('list-keymaps', '-kb', 'contra')\n    check_returncode(result)\n    assert 'default' in result.stdout\n\n\ndef test_list_keymaps_vendor_kb():\n    result = check_subcommand('list-keymaps', '-kb', 'ai03/lunar')\n    check_returncode(result)\n    assert 'default' in result.stdout\n\n\ndef test_list_keymaps_vendor_kb_rev():\n    result = check_subcommand('list-keymaps', '-kb', 'kbdfans/kbd67/mkiirgb/v2')\n    check_returncode(result)\n    assert 'default' in result.stdout\n\n\ndef test_list_keymaps_no_keyboard_found():\n    result = check_subcommand('list-keymaps', '-kb', 'asdfghjkl')\n    check_returncode(result, [2])\n    assert 'invalid keyboard_folder value' in result.stdout\n\n\ndef test_json2c():\n    result = check_subcommand('json2c', 'keyboards/handwired/pytest/basic/keymaps/default_json/keymap.json')\n    check_returncode(result)\n    assert result.stdout == \"\"\"#include QMK_KEYBOARD_H\n#if __has_include(\"keymap.h\")\n#    include \"keymap.h\"\n#endif\n\n\n/* THIS FILE WAS GENERATED!\n *\n * This file was generated by qmk json2c. You may or may not want to\n * edit it directly.\n */\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    [0] = LAYOUT_ortho_1x1(KC_A)\n};\n\n\n\n\n#ifdef OTHER_KEYMAP_C\n#    include OTHER_KEYMAP_C\n#endif // OTHER_KEYMAP_C\n\n\n\"\"\"\n\n\ndef test_json2c_macros():\n    result = check_subcommand(\"json2c\", 'keyboards/handwired/pytest/macro/keymaps/default/keymap.json')\n    check_returncode(result)\n    assert 'LAYOUT_ortho_1x1(QK_MACRO_0)' in result.stdout\n    assert 'case QK_MACRO_0:' in result.stdout\n    assert 'SEND_STRING(\"Hello, World!\"SS_TAP(X_ENTER));' in result.stdout\n\n\ndef test_json2c_stdin():\n    result = check_subcommand_stdin('keyboards/handwired/pytest/basic/keymaps/default_json/keymap.json', 'json2c', '-')\n    check_returncode(result)\n    assert result.stdout == \"\"\"#include QMK_KEYBOARD_H\n#if __has_include(\"keymap.h\")\n#    include \"keymap.h\"\n#endif\n\n\n/* THIS FILE WAS GENERATED!\n *\n * This file was generated by qmk json2c. You may or may not want to\n * edit it directly.\n */\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    [0] = LAYOUT_ortho_1x1(KC_A)\n};\n\n\n\n\n#ifdef OTHER_KEYMAP_C\n#    include OTHER_KEYMAP_C\n#endif // OTHER_KEYMAP_C\n\n\n\"\"\"\n\n\ndef test_json2c_no_json():\n    result = check_subcommand('json2c', 'keyboards/handwired/pytest/basic/keymaps/default/keymap.c')\n    check_returncode(result, [1])\n    assert 'Invalid JSON encountered' in result.stdout\n\n\ndef test_info():\n    result = check_subcommand('info', '-kb', 'handwired/pytest/basic')\n    check_returncode(result)\n    assert 'Keyboard Name: pytest' in result.stdout\n    assert 'Processor: atmega32u4' in result.stdout\n    assert 'Layout:' not in result.stdout\n    assert 'k0' not in result.stdout\n\n\ndef test_info_keyboard_render():\n    result = check_subcommand('info', '-kb', 'handwired/pytest/basic', '-l')\n    check_returncode(result)\n    assert 'Keyboard Name: pytest' in result.stdout\n    assert 'Processor: atmega32u4' in result.stdout\n    assert 'Layouts:' in result.stdout\n\n    if is_windows:\n        assert '|  |' in result.stdout\n    else:\n        assert '│  │' in result.stdout\n\n\ndef test_info_keymap_render():\n    result = check_subcommand('info', '-kb', 'handwired/pytest/basic', '-km', 'default_json')\n    check_returncode(result)\n    assert 'Keyboard Name: pytest' in result.stdout\n    assert 'Processor: atmega32u4' in result.stdout\n\n    if is_windows:\n        assert '|A |' in result.stdout\n    else:\n        assert '│A │' in result.stdout\n\n\ndef test_info_matrix_render():\n    result = check_subcommand('info', '-kb', 'handwired/pytest/basic', '-m')\n    check_returncode(result)\n    assert 'Keyboard Name: pytest' in result.stdout\n    assert 'Processor: atmega32u4' in result.stdout\n    assert 'LAYOUT_ortho_1x1' in result.stdout\n\n    if is_windows:\n        assert '|0A|' in result.stdout\n    else:\n        assert '│0A│' in result.stdout\n\n    assert 'Matrix for \"LAYOUT_ortho_1x1\"' in result.stdout\n\n\ndef test_c2json():\n    result = check_subcommand(\"c2json\", \"-kb\", \"handwired/pytest/basic\", \"-km\", \"default\", \"keyboards/handwired/pytest/basic/keymaps/default/keymap.c\")\n    check_returncode(result)\n    assert result.stdout.strip() == '{\"keyboard\": \"handwired/pytest/basic\", \"keymap\": \"default\", \"layout\": \"LAYOUT_ortho_1x1\", \"layers\": [[\"KC_A\"]]}'\n\n\ndef test_c2json_stdin():\n    result = check_subcommand_stdin(\"keyboards/handwired/pytest/basic/keymaps/default/keymap.c\", \"c2json\", \"-kb\", \"handwired/pytest/basic\", \"-km\", \"default\", \"-\")\n    check_returncode(result)\n    assert result.stdout.strip() == '{\"keyboard\": \"handwired/pytest/basic\", \"keymap\": \"default\", \"layout\": \"LAYOUT_ortho_1x1\", \"layers\": [[\"KC_A\"]]}'\n\n\ndef test_clean():\n    result = check_subcommand('clean', '-a')\n    check_returncode(result)\n    assert (result.stdout.count('done') == 2 and 'userspace' not in result.stdout) or (result.stdout.count('done') == 3 and 'userspace' in result.stdout)\n\n\ndef test_generate_api():\n    result = check_subcommand('generate-api', '--dry-run', '--filter', 'handwired/pytest')\n    check_returncode(result)\n\n\ndef test_generate_rgb_breathe_table():\n    result = check_subcommand(\"generate-rgb-breathe-table\", \"-c\", \"1.2\", \"-m\", \"127\")\n    check_returncode(result)\n    assert 'Breathing center: 1.2' in result.stdout\n    assert 'Breathing max:    127' in result.stdout\n\n\ndef test_generate_config_h():\n    result = check_subcommand('generate-config-h', '-kb', 'handwired/pytest/basic')\n    check_returncode(result)\n    assert '#    define DEVICE_VER 0x0001' in result.stdout\n    assert '#    define DIODE_DIRECTION COL2ROW' in result.stdout\n    assert '#    define MANUFACTURER \"none\"' in result.stdout\n    assert '#    define PRODUCT \"pytest\"' in result.stdout\n    assert '#    define PRODUCT_ID 0x6465' in result.stdout\n    assert '#    define VENDOR_ID 0xFEED' in result.stdout\n    assert '#    define MATRIX_COLS 1' in result.stdout\n    assert '#    define MATRIX_COL_PINS { F4 }' in result.stdout\n    assert '#    define MATRIX_ROWS 1' in result.stdout\n    assert '#    define MATRIX_ROW_PINS { F5 }' in result.stdout\n\n\ndef test_generate_rules_mk():\n    result = check_subcommand('generate-rules-mk', '-kb', 'handwired/pytest/basic')\n    check_returncode(result)\n    assert 'BOOTLOADER ?= atmel-dfu' in result.stdout\n    assert 'MCU ?= atmega32u4' in result.stdout\n\n\ndef test_generate_version_h():\n    result = check_subcommand('generate-version-h')\n    check_returncode(result)\n    assert '#define QMK_VERSION' in result.stdout\n\n\ndef test_format_json_keyboard():\n    result = check_subcommand('format-json', '--format', 'keyboard', 'lib/python/qmk/tests/minimal_info.json')\n    check_returncode(result)\n    assert result.stdout == '{\\n    \"keyboard_name\": \"tester\",\\n    \"maintainer\": \"qmk\",\\n    \"layouts\": {\\n        \"LAYOUT\": {\\n            \"layout\": [\\n                {\"label\": \"KC_A\", \"matrix\": [0, 0], \"x\": 0, \"y\": 0}\\n            ]\\n        }\\n    }\\n}\\n'\n\n\ndef test_format_json_keymap():\n    result = check_subcommand('format-json', '--format', 'keymap', 'lib/python/qmk/tests/minimal_keymap.json')\n    check_returncode(result)\n    assert result.stdout == '{\\n    \"version\": 1,\\n    \"keyboard\": \"handwired/pytest/basic\",\\n    \"keymap\": \"test\",\\n    \"layout\": \"LAYOUT_ortho_1x1\",\\n    \"layers\": [\\n                [\\n                        \"KC_A\"\\n                ]\\n    ]\\n}\\n'\n\n\ndef test_format_json_keyboard_auto():\n    result = check_subcommand('format-json', '--format', 'auto', 'lib/python/qmk/tests/minimal_info.json')\n    check_returncode(result)\n    assert result.stdout == '{\\n    \"keyboard_name\": \"tester\",\\n    \"maintainer\": \"qmk\",\\n    \"layouts\": {\\n        \"LAYOUT\": {\\n            \"layout\": [\\n                {\"label\": \"KC_A\", \"matrix\": [0, 0], \"x\": 0, \"y\": 0}\\n            ]\\n        }\\n    }\\n}\\n'\n\n\ndef test_format_json_keymap_auto():\n    result = check_subcommand('format-json', '--format', 'auto', 'lib/python/qmk/tests/minimal_keymap.json')\n    check_returncode(result)\n    assert result.stdout == '{\\n    \"keyboard\": \"handwired/pytest/basic\",\\n    \"keymap\": \"test\",\\n    \"layers\": [\\n        [\"KC_A\"]\\n    ],\\n    \"layout\": \"LAYOUT_ortho_1x1\",\\n    \"version\": 1\\n}\\n'\n\n\ndef test_find_exists():\n    result = check_subcommand('find', '-f', 'exists(rgb_matrix.split_count)', '-p', 'rgb_matrix.split_count')\n    check_returncode(result)\n    values = [s for s in result.stdout.splitlines() if 'rgb_matrix.split_count=' in s]\n    assert len(values) > 0\n    for s in values:\n        assert '=None' not in s\n        assert '=[' in s\n\n\ndef test_find_absent():\n    result = check_subcommand('find', '-f', 'absent(rgb_matrix.split_count)', '-p', 'rgb_matrix.split_count')\n    check_returncode(result)\n    values = [s for s in result.stdout.splitlines() if 'rgb_matrix.split_count=' in s]\n    assert len(values) > 0\n    for s in values:\n        assert '=None' in s\n        assert '=[' not in s\n\n\ndef test_find_length():\n    result = check_subcommand('find', '-f', 'length(matrix_pins.cols, 6)', '-p', 'matrix_pins.cols')\n    check_returncode(result)\n    values = [s for s in result.stdout.splitlines() if 'matrix_pins.cols=' in s]\n    assert len(values) > 0\n    for s in values:\n        assert s.count(',') == 5\n\n\ndef test_find_contains():\n    result = check_subcommand('find', '-f', 'contains(matrix_pins.cols, B1)', '-p', 'matrix_pins.cols')\n    check_returncode(result)\n    values = [s for s in result.stdout.splitlines() if 'matrix_pins.cols=' in s]\n    assert len(values) > 0\n    for s in values:\n        assert \"'B1'\" in s\n\n\ndef test_find_multiple_conditions():\n    # this is intended to match at least 'crkbd/rev1'\n    result = check_subcommand(\n        'find', '-f', 'exists(rgb_matrix.split_count)', '-f', 'contains(matrix_pins.cols, B1)', '-f', 'length(matrix_pins.cols, 6)', '-f', 'absent(eeprom.driver)', '-f', 'ws2812.pin == D3', '-p', 'rgb_matrix.split_count', '-p', 'matrix_pins.cols', '-p',\n        'eeprom.driver', '-p', 'ws2812.pin'\n    )\n    check_returncode(result)\n    rgb_matrix_split_count_values = [s for s in result.stdout.splitlines() if 'rgb_matrix.split_count=' in s]\n    assert len(rgb_matrix_split_count_values) > 0\n    for s in rgb_matrix_split_count_values:\n        assert '=None' not in s\n        assert '=[' in s\n    matrix_pins_cols_values = [s for s in result.stdout.splitlines() if 'matrix_pins.cols=' in s]\n    assert len(matrix_pins_cols_values) > 0\n    for s in matrix_pins_cols_values:\n        assert s.count(',') == 5\n        assert \"'B1'\" in s\n    eeprom_driver_values = [s for s in result.stdout.splitlines() if 'eeprom.driver=' in s]\n    assert len(eeprom_driver_values) > 0\n    for s in eeprom_driver_values:\n        assert '=None' in s\n    ws2812_pin_values = [s for s in result.stdout.splitlines() if 'ws2812.pin=' in s]\n    assert len(ws2812_pin_values) > 0\n    for s in ws2812_pin_values:\n        assert '=D3' in s\n"
  },
  {
    "path": "lib/python/qmk/tests/test_qmk_errors.py",
    "content": "from qmk.errors import NoSuchKeyboardError\n\n\ndef test_nosuchkeyboarderror():\n    try:\n        raise NoSuchKeyboardError(\"test message\")\n    except NoSuchKeyboardError as e:\n        assert e.message == 'test message'\n"
  },
  {
    "path": "lib/python/qmk/tests/test_qmk_keymap.py",
    "content": "import qmk.keymap\n\n\ndef test_generate_c_pytest_basic():\n    keymap_json = {\n        'keyboard': 'handwired/pytest/basic',\n        'layout': 'LAYOUT',\n        'layers': [['KC_A']],\n        'macros': None,\n    }\n    templ = qmk.keymap.generate_c(keymap_json)\n    assert templ == \"\"\"#include QMK_KEYBOARD_H\n#if __has_include(\"keymap.h\")\n#    include \"keymap.h\"\n#endif\n\n\n/* THIS FILE WAS GENERATED!\n *\n * This file was generated by qmk json2c. You may or may not want to\n * edit it directly.\n */\n\nconst uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {\n    [0] = LAYOUT(KC_A)\n};\n\n\n\n\n#ifdef OTHER_KEYMAP_C\n#    include OTHER_KEYMAP_C\n#endif // OTHER_KEYMAP_C\n\"\"\"\n\n\ndef test_generate_json_pytest_basic():\n    templ = qmk.keymap.generate_json('default', 'handwired/pytest/basic', 'LAYOUT', [['KC_A']])\n    assert templ == {\"keyboard\": \"handwired/pytest/basic\", \"keymap\": \"default\", \"layout\": \"LAYOUT\", \"layers\": [[\"KC_A\"]]}\n\n\ndef test_parse_keymap_c():\n    parsed_keymap_c = qmk.keymap.parse_keymap_c('keyboards/handwired/pytest/basic/keymaps/default/keymap.c')\n    assert parsed_keymap_c == {'layers': [{'name': '0', 'layout': 'LAYOUT_ortho_1x1', 'keycodes': ['KC_A']}]}\n\n\n# FIXME(skullydazed): Add a test for qmk.keymap.write that mocks up an FD.\n"
  },
  {
    "path": "lib/python/qmk/tests/test_qmk_path.py",
    "content": "import os\nfrom pathlib import Path\n\nimport qmk.path\n\n\ndef test_keymap_pytest_basic():\n    path = qmk.path.keymap('handwired/pytest/basic', 'default')\n    assert path.samefile('keyboards/handwired/pytest/basic/keymaps/default')\n\n\ndef test_normpath():\n    path = qmk.path.normpath('lib/python')\n    assert path.samefile(Path(os.environ['ORIG_CWD']) / 'lib/python')\n"
  },
  {
    "path": "lib/python/qmk/userspace.py",
    "content": "# Copyright 2023-2024 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\nfrom os import environ\nfrom pathlib import Path\nimport json\nimport jsonschema\n\nfrom milc import cli\n\nfrom qmk.json_schema import validate, json_load\nfrom qmk.json_encoders import UserspaceJSONEncoder\n\n\ndef qmk_userspace_paths():\n    test_dirs = []\n\n    # If we're already in a directory with a qmk.json and a keyboards or layouts directory, interpret it as userspace\n    if environ.get('ORIG_CWD') is not None:\n        current_dir = Path(environ['ORIG_CWD'])\n        while len(current_dir.parts) > 1:\n            if (current_dir / 'qmk.json').is_file():\n                test_dirs.append(current_dir)\n            current_dir = current_dir.parent\n\n    # If we have a QMK_USERSPACE environment variable, use that\n    if environ.get('QMK_USERSPACE') is not None:\n        current_dir = Path(environ['QMK_USERSPACE']).expanduser()\n        if current_dir.is_dir():\n            test_dirs.append(current_dir)\n\n    # If someone has configured a directory, use that\n    if cli.config.user.overlay_dir is not None:\n        current_dir = Path(cli.config.user.overlay_dir).expanduser().resolve()\n        if current_dir.is_dir():\n            test_dirs.append(current_dir)\n\n    # remove duplicates while maintaining the current order\n    return list(dict.fromkeys(test_dirs))\n\n\ndef qmk_userspace_validate(path):\n    # Construct a UserspaceDefs object to ensure it validates correctly\n    if (path / 'qmk.json').is_file():\n        UserspaceDefs(path / 'qmk.json')\n        return\n\n    # No qmk.json file found\n    raise FileNotFoundError('No qmk.json file found.')\n\n\ndef detect_qmk_userspace():\n    # Iterate through all the detected userspace paths and return the first one that validates correctly\n    test_dirs = qmk_userspace_paths()\n    for test_dir in test_dirs:\n        try:\n            qmk_userspace_validate(test_dir)\n            return test_dir\n        except FileNotFoundError:\n            continue\n        except UserspaceValidationError:\n            continue\n    return None\n\n\nclass UserspaceDefs:\n    def __init__(self, userspace_json: Path):\n        self.path = userspace_json\n        self.build_targets = []\n        json = json_load(userspace_json)\n\n        exception = UserspaceValidationError()\n        success = False\n\n        try:\n            validate(json, 'qmk.user_repo.v0')  # `qmk.json` must have a userspace_version at minimum\n        except jsonschema.ValidationError as err:\n            exception.add('qmk.user_repo.v0', err)\n            raise exception\n\n        # Iterate through each version of the schema, starting with the latest and decreasing to v1\n        schema_versions = [\n            ('qmk.user_repo.v1_1', self.__load_v1_1),  #\n            ('qmk.user_repo.v1', self.__load_v1)  #\n        ]\n\n        for v in schema_versions:\n            schema = v[0]\n            loader = v[1]\n            try:\n                validate(json, schema)\n                loader(json)\n                success = True\n                break\n            except jsonschema.ValidationError as err:\n                exception.add(schema, err)\n\n        if not success:\n            raise exception\n\n    def save(self):\n        target_json = {\n            \"userspace_version\": \"1.1\",  # Needs to match latest version\n            \"build_targets\": []\n        }\n\n        for e in self.build_targets:\n            if isinstance(e, dict):\n                entry = [e['keyboard'], e['keymap']]\n                if 'env' in e:\n                    entry.append(e['env'])\n                target_json['build_targets'].append(entry)\n            elif isinstance(e, Path):\n                target_json['build_targets'].append(str(e.relative_to(self.path.parent)))\n\n        try:\n            # Ensure what we're writing validates against the latest version of the schema\n            validate(target_json, 'qmk.user_repo.v1_1')\n        except jsonschema.ValidationError as err:\n            cli.log.error(f'Could not save userspace file: {err}')\n            return False\n\n        # Only actually write out data if it changed\n        old_data = json.dumps(json.loads(self.path.read_text()), cls=UserspaceJSONEncoder, sort_keys=True)\n        new_data = json.dumps(target_json, cls=UserspaceJSONEncoder, sort_keys=True)\n        if old_data != new_data:\n            self.path.write_text(new_data)\n            cli.log.info(f'Saved userspace file to {self.path}.')\n        return True\n\n    def add_target(self, keyboard=None, keymap=None, build_env=None, json_path=None, do_print=True):\n        if json_path is not None:\n            # Assume we're adding a json filename/path\n            json_path = Path(json_path)\n            if json_path not in self.build_targets:\n                self.build_targets.append(json_path)\n                if do_print:\n                    cli.log.info(f'Added {json_path} to userspace build targets.')\n            else:\n                cli.log.info(f'{json_path} is already a userspace build target.')\n\n        elif keyboard is not None and keymap is not None:\n            # Both keyboard/keymap specified\n            e = {\"keyboard\": keyboard, \"keymap\": keymap}\n            if build_env is not None:\n                e['env'] = build_env\n            if e not in self.build_targets:\n                self.build_targets.append(e)\n                if do_print:\n                    cli.log.info(f'Added {keyboard}:{keymap} to userspace build targets.')\n            else:\n                if do_print:\n                    cli.log.info(f'{keyboard}:{keymap} is already a userspace build target.')\n\n    def remove_target(self, keyboard=None, keymap=None, build_env=None, json_path=None, do_print=True):\n        if json_path is not None:\n            # Assume we're removing a json filename/path\n            json_path = Path(json_path)\n            if json_path in self.build_targets:\n                self.build_targets.remove(json_path)\n                if do_print:\n                    cli.log.info(f'Removed {json_path} from userspace build targets.')\n            else:\n                cli.log.info(f'{json_path} is not a userspace build target.')\n\n        elif keyboard is not None and keymap is not None:\n            # Both keyboard/keymap specified\n            e = {\"keyboard\": keyboard, \"keymap\": keymap}\n            if build_env is not None:\n                e['env'] = build_env\n            if e in self.build_targets:\n                self.build_targets.remove(e)\n                if do_print:\n                    cli.log.info(f'Removed {keyboard}:{keymap} from userspace build targets.')\n            else:\n                if do_print:\n                    cli.log.info(f'{keyboard}:{keymap} is not a userspace build target.')\n\n    def __load_v1(self, json):\n        for e in json['build_targets']:\n            self.__load_v1_target(e)\n\n    def __load_v1_1(self, json):\n        for e in json['build_targets']:\n            self.__load_v1_1_target(e)\n\n    def __load_v1_target(self, e):\n        if isinstance(e, list) and len(e) == 2:\n            self.add_target(keyboard=e[0], keymap=e[1], do_print=False)\n        if isinstance(e, str):\n            p = self.path.parent / e\n            if p.exists() and p.suffix == '.json':\n                self.add_target(json_path=p, do_print=False)\n\n    def __load_v1_1_target(self, e):\n        # v1.1 adds support for a third item in the build target tuple; kvp's for environment\n        if isinstance(e, list) and len(e) == 3:\n            self.add_target(keyboard=e[0], keymap=e[1], build_env=e[2], do_print=False)\n        else:\n            self.__load_v1_target(e)\n\n\nclass UserspaceValidationError(Exception):\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.__exceptions = []\n\n    def __str__(self):\n        return self.message\n\n    @property\n    def exceptions(self):\n        return self.__exceptions\n\n    def add(self, schema, exception):\n        self.__exceptions.append((schema, exception))\n        errorlist = \"\\n\\n\".join([f\"{schema}: {exception}\" for schema, exception in self.__exceptions])\n        self.message = f'Could not validate against any version of the userspace schema. Errors:\\n\\n{errorlist}'\n"
  },
  {
    "path": "lib/python/qmk/util.py",
    "content": "\"\"\"Utility functions.\n\"\"\"\nimport contextlib\nimport multiprocessing\nimport sys\nimport re\n\nfrom milc import cli\n\nTRIPLET_PATTERN = re.compile(r'^(\\d+)\\.(\\d+)\\.(\\d+)')\n\nmaybe_exit_should_exit = True\nmaybe_exit_reraise = False\n\n\n# Controls whether or not early `exit()` calls should be made\ndef maybe_exit(rc):\n    if maybe_exit_should_exit:\n        sys.exit(rc)\n    if maybe_exit_reraise:\n        e = sys.exc_info()[1]\n        if e:\n            raise e\n\n\ndef maybe_exit_config(should_exit: bool = True, should_reraise: bool = False):\n    global maybe_exit_should_exit\n    global maybe_exit_reraise\n    maybe_exit_should_exit = should_exit\n    maybe_exit_reraise = should_reraise\n\n\ndef truthy(value, value_if_unknown=False):\n    \"\"\"Returns True if the value is truthy, False otherwise.\n\n    Deals with:\n        True: 1, true, t, yes, y, on\n        False: 0, false, f, no, n, off\n    \"\"\"\n    if value in {False, True}:\n        return bool(value)\n\n    test_value = str(value).strip().lower()\n\n    if test_value in {\"1\", \"true\", \"t\", \"yes\", \"y\", \"on\"}:\n        return True\n\n    if test_value in {\"0\", \"false\", \"f\", \"no\", \"n\", \"off\"}:\n        return False\n\n    return value_if_unknown\n\n\n@contextlib.contextmanager\ndef parallelize():\n    \"\"\"Returns a function that can be used in place of a map() call.\n\n    Attempts to use `mpire`, falling back to `multiprocessing` if it's not\n    available. If parallelization is not requested, returns the original map()\n    function.\n    \"\"\"\n\n    # Work out if we've already got a config value for parallel searching\n    if cli.config.user.parallel_search is None:\n        parallel_search = True\n    else:\n        parallel_search = cli.config.user.parallel_search\n\n    # Non-parallel searches use `map()`\n    if not parallel_search:\n        yield map\n        return\n\n    # Prefer mpire's `WorkerPool` if it's available\n    with contextlib.suppress(ImportError):\n        from mpire import WorkerPool\n        from mpire.utils import make_single_arguments\n        with WorkerPool() as pool:\n\n            def _worker(func, *args):\n                # Ensure we don't unpack tuples -- mpire's `WorkerPool` tries to do so normally so we tell it not to.\n                for r in pool.imap_unordered(func, make_single_arguments(*args, generator=False), progress_bar=True):\n                    yield r\n\n            yield _worker\n        return\n\n    # Otherwise fall back to multiprocessing's `Pool`\n    with multiprocessing.Pool() as pool:\n        yield pool.imap_unordered\n\n\ndef parallel_map(*args, **kwargs):\n    \"\"\"Effectively runs `map()` but executes it in parallel if necessary.\n    \"\"\"\n    with parallelize() as map_fn:\n        # This needs to be enclosed in a `list()` as some implementations return\n        # a generator function, which means the scope of the pool is closed off\n        # before the results are returned. Returning a list ensures results are\n        # materialised before any worker pool is shut down.\n        return list(map_fn(*args, **kwargs))\n\n\ndef triplet_to_bcd(ver: str):\n    m = TRIPLET_PATTERN.match(ver)\n    if not m:\n        return '0x00000000'\n    return f'0x{int(m.group(1)):02d}{int(m.group(2)):02d}{int(m.group(3)):04d}'\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/.gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n* text=auto\n* text eol=lf\n\n# Custom for Visual Studio\n*.cs     diff=csharp\n*.sln    merge=union\n*.csproj merge=union\n*.vbproj merge=union\n*.fsproj merge=union\n*.dbproj merge=union\n\n# Standard to msysgit\n*.doc\t diff=astextplain\n*.DOC\t diff=astextplain\n*.docx diff=astextplain\n*.DOCX diff=astextplain\n*.dot  diff=astextplain\n*.DOT  diff=astextplain\n*.pdf  diff=astextplain\n*.PDF\t diff=astextplain\n*.rtf\t diff=astextplain\n*.RTF\t diff=astextplain\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/.gitignore",
    "content": "*.bak\n*.zip\n*.rar\nbuild/"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/.gitmodules",
    "content": "[submodule \"examples/testusbhostFAT/generic_storage\"]\n\tpath = examples/testusbhostFAT/generic_storage\n\turl = https://github.com/xxxajk/generic_storage\n[submodule \"examples/testusbhostFAT/xmem2\"]\n\tpath = examples/testusbhostFAT/xmem2\n\turl = https://github.com/xxxajk/xmem2\n[submodule \"examples/testusbhostFAT/Arduino_Makefile_master\"]\n\tpath = examples/testusbhostFAT/Arduino_Makefile_master\n\turl = https://github.com/xxxajk/Arduino_Makefile_master\n[submodule \"examples/testusbhostFAT/RTClib\"]\n\tpath = examples/testusbhostFAT/RTClib\n\turl = https://github.com/xxxajk/RTClib\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/BTD.cpp",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#include \"BTD.h\"\n// To enable serial debugging see \"settings.h\"\n//#define EXTRADEBUG // Uncomment to get even more debugging data\n\nconst uint8_t BTD::BTD_CONTROL_PIPE = 0;\nconst uint8_t BTD::BTD_EVENT_PIPE = 1;\nconst uint8_t BTD::BTD_DATAIN_PIPE = 2;\nconst uint8_t BTD::BTD_DATAOUT_PIPE = 3;\n\nBTD::BTD(USB *p) :\nconnectToWii(false),\npairWithWii(false),\nconnectToHIDDevice(false),\npairWithHIDDevice(false),\npUsb(p), // Pointer to USB class instance - mandatory\nbAddress(0), // Device address - mandatory\nbNumEP(1), // If config descriptor needs to be parsed\nqNextPollTime(0), // Reset NextPollTime\npollInterval(0),\nbPollEnable(false) // Don't start polling before dongle is connected\n{\n        for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++)\n                btService[i] = NULL;\n\n        Initialize(); // Set all variables, endpoint structs etc. to default values\n\n        if(pUsb) // Register in USB subsystem\n                pUsb->RegisterDeviceClass(this); // Set devConfig[] entry\n}\n\nuint8_t BTD::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {\n        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);\n        uint8_t buf[constBufSize];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n\n        Initialize(); // Set all variables, endpoint structs etc. to default values\n\n        AddressPool &addrPool = pUsb->GetAddressPool(); // Get memory address of USB device address pool\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nBTD ConfigureDevice\"), 0x80);\n#endif\n\n        if(bAddress) { // Check if address has already been assigned to an instance\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress in use\"), 0x80);\n#endif\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n        }\n\n        p = addrPool.GetUsbDevicePtr(0); // Get pointer to pseudo device with address 0 assigned\n        if(!p) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress not found\"), 0x80);\n#endif\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        if(!p->epinfo) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nepinfo is null\"), 0x80);\n#endif\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        oldep_ptr = p->epinfo; // Save old pointer to EP_RECORD of address 0\n        p->epinfo = epInfo; // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->lowspeed = lowspeed;\n        rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data\n\n        p->epinfo = oldep_ptr; // Restore p->epinfo\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        bAddress = addrPool.AllocAddress(parent, false, port); // Allocate new address according to device class\n\n        if(!bAddress) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nOut of address space\"), 0x80);\n#endif\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n        }\n\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0; // Extract Max Packet Size from device descriptor\n        epInfo[1].epAddr = udd->bNumConfigurations; // Steal and abuse from epInfo structure to save memory\n\n        VID = udd->idVendor;\n        PID = udd->idProduct;\n\n        return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;\n\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr(rcode);\n#endif\n        if(rcode != hrJERR)\n                rcode = USB_ERROR_FailGetDevDescr;\n        Release();\n        return rcode;\n};\n\nuint8_t BTD::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        uint8_t rcode;\n        uint8_t num_of_conf = epInfo[1].epAddr; // Number of configurations\n        epInfo[1].epAddr = 0;\n\n        AddressPool &addrPool = pUsb->GetAddressPool();\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nBTD Init\"), 0x80);\n#endif\n        UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record\n\n        if(!p) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress not found\"), 0x80);\n#endif\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        delay(300); // Assign new address to the device\n\n        rcode = pUsb->setAddr(0, 0, bAddress); // Assign new address to the device\n        if(rcode) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nsetAddr: \"), 0x80);\n                D_PrintHex<uint8_t > (rcode, 0x80);\n#endif\n                p->lowspeed = false;\n                goto Fail;\n        }\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nAddr: \"), 0x80);\n        D_PrintHex<uint8_t > (bAddress, 0x80);\n#endif\n\n        p->lowspeed = false;\n\n        p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record\n        if(!p) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress not found\"), 0x80);\n#endif\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        p->lowspeed = lowspeed;\n\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); // Assign epInfo to epinfo pointer - only EP0 is known\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        if(VID == PS3_VID && (PID == PS3_PID || PID == PS3NAVIGATION_PID || PID == PS3MOVE_PID)) {\n                delay(100);\n                rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, 1); // We only need the Control endpoint, so we don't have to initialize the other endpoints of device\n                if(rcode)\n                        goto FailSetConfDescr;\n\n#ifdef DEBUG_USB_HOST\n                if(PID == PS3_PID || PID == PS3NAVIGATION_PID) {\n                        if(PID == PS3_PID)\n                                Notify(PSTR(\"\\r\\nDualshock 3 Controller Connected\"), 0x80);\n                        else // It must be a navigation controller\n                                Notify(PSTR(\"\\r\\nNavigation Controller Connected\"), 0x80);\n                } else // It must be a Motion controller\n                        Notify(PSTR(\"\\r\\nMotion Controller Connected\"), 0x80);\n#endif\n\n                if(my_bdaddr[0] == 0x00 && my_bdaddr[1] == 0x00 && my_bdaddr[2] == 0x00 && my_bdaddr[3] == 0x00 && my_bdaddr[4] == 0x00 && my_bdaddr[5] == 0x00) {\n#ifdef DEBUG_USB_HOST\n                        Notify(PSTR(\"\\r\\nPlease plug in the dongle before trying to pair with the PS3 Controller\\r\\nor set the Bluetooth address in the constructor of the PS3BT class\"), 0x80);\n#endif\n                } else {\n                        if(PID == PS3_PID || PID == PS3NAVIGATION_PID)\n                                setBdaddr(my_bdaddr); // Set internal Bluetooth address\n                        else\n                                setMoveBdaddr(my_bdaddr); // Set internal Bluetooth address\n#ifdef DEBUG_USB_HOST\n                        Notify(PSTR(\"\\r\\nBluetooth Address was set to: \"), 0x80);\n                        for(int8_t i = 5; i > 0; i--) {\n                                D_PrintHex<uint8_t > (my_bdaddr[i], 0x80);\n                                Notify(PSTR(\":\"), 0x80);\n                        }\n                        D_PrintHex<uint8_t > (my_bdaddr[0], 0x80);\n#endif\n                }\n\n                pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, 0); // Reset configuration value\n                pUsb->setAddr(bAddress, 0, 0); // Reset address\n                Release(); // Release device\n                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED; // Return\n        } else {\n                // Check if attached device is a Bluetooth dongle and fill endpoint data structure\n                // First interface in the configuration must have Bluetooth assigned Class/Subclass/Protocol\n                // And 3 endpoints - interrupt-IN, bulk-IN, bulk-OUT, not necessarily in this order\n                for(uint8_t i = 0; i < num_of_conf; i++) {\n                        if(VID == IOGEAR_GBU521_VID && PID == IOGEAR_GBU521_PID) {\n                                ConfigDescParser<USB_CLASS_VENDOR_SPECIFIC, WI_SUBCLASS_RF, WI_PROTOCOL_BT, CP_MASK_COMPARE_ALL> confDescrParser(this); // Needed for the IOGEAR GBU521\n                                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);\n                        } else {\n                                ConfigDescParser<USB_CLASS_WIRELESS_CTRL, WI_SUBCLASS_RF, WI_PROTOCOL_BT, CP_MASK_COMPARE_ALL> confDescrParser(this);\n                                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);\n                        }\n                        if(rcode) // Check error code\n                                goto FailGetConfDescr;\n                        if(bNumEP >= BTD_MAX_ENDPOINTS) // All endpoints extracted\n                                break;\n                }\n\n                if(bNumEP < BTD_MAX_ENDPOINTS)\n                        goto FailUnknownDevice;\n\n                // Assign epInfo to epinfo pointer - this time all 3 endpoins\n                rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);\n                if(rcode)\n                        goto FailSetDevTblEntry;\n\n                // Set Configuration Value\n                rcode = pUsb->setConf(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bConfNum);\n                if(rcode)\n                        goto FailSetConfDescr;\n\n                hci_num_reset_loops = 100; // only loop 100 times before trying to send the hci reset command\n                hci_counter = 0;\n                hci_state = HCI_INIT_STATE;\n                watingForConnection = false;\n                bPollEnable = true;\n\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nBluetooth Dongle Initialized\"), 0x80);\n#endif\n        }\n        return 0; // Successful configuration\n\n        /* Diagnostic messages */\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailGetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetConfDescr();\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n#endif\n        goto Fail;\n\nFailUnknownDevice:\n#ifdef DEBUG_USB_HOST\n        NotifyFailUnknownDevice(VID, PID);\n#endif\n        pUsb->setAddr(bAddress, 0, 0); // Reset address\n        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\nFail:\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nBTD Init Failed, error code: \"), 0x80);\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n\nvoid BTD::Initialize() {\n        uint8_t i;\n        for(i = 0; i < BTD_MAX_ENDPOINTS; i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;\n        }\n        for(i = 0; i < BTD_NUM_SERVICES; i++) {\n                if(btService[i])\n                        btService[i]->Reset(); // Reset all Bluetooth services\n        }\n\n        connectToWii = false;\n        incomingWii = false;\n        connectToHIDDevice = false;\n        incomingHIDDevice = false;\n        incomingPS4 = false;\n        bAddress = 0; // Clear device address\n        bNumEP = 1; // Must have to be reset to 1\n        qNextPollTime = 0; // Reset next poll time\n        pollInterval = 0;\n        bPollEnable = false; // Don't start polling before dongle is connected\n}\n\n/* Extracts interrupt-IN, bulk-IN, bulk-OUT endpoint information from config descriptor */\nvoid BTD::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) {\n        //ErrorMessage<uint8_t>(PSTR(\"Conf.Val\"),conf);\n        //ErrorMessage<uint8_t>(PSTR(\"Iface Num\"),iface);\n        //ErrorMessage<uint8_t>(PSTR(\"Alt.Set\"),alt);\n\n        if(alt) // Wrong interface - by BT spec, no alt setting\n                return;\n\n        bConfNum = conf;\n        uint8_t index;\n\n        if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) { // Interrupt In endpoint found\n                index = BTD_EVENT_PIPE;\n                epInfo[index].bmNakPower = USB_NAK_NOWAIT;\n        } else {\n                if((pep->bmAttributes & 0x02) == 2) // Bulk endpoint found\n                        index = ((pep->bEndpointAddress & 0x80) == 0x80) ? BTD_DATAIN_PIPE : BTD_DATAOUT_PIPE;\n                else\n                        return;\n        }\n\n        // Fill the rest of endpoint data structure\n        epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);\n        epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;\n#ifdef EXTRADEBUG\n        PrintEndpointDescriptor(pep);\n#endif\n        if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints\n                pollInterval = pep->bInterval;\n        bNumEP++;\n}\n\nvoid BTD::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nEndpoint descriptor:\"), 0x80);\n        Notify(PSTR(\"\\r\\nLength:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);\n        Notify(PSTR(\"\\r\\nType:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);\n        Notify(PSTR(\"\\r\\nAddress:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);\n        Notify(PSTR(\"\\r\\nAttributes:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);\n        Notify(PSTR(\"\\r\\nMaxPktSize:\\t\"), 0x80);\n        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);\n        Notify(PSTR(\"\\r\\nPoll Intrv:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);\n#endif\n}\n\n/* Performs a cleanup after failed Init() attempt */\nuint8_t BTD::Release() {\n        Initialize(); // Set all variables, endpoint structs etc. to default values\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n        return 0;\n}\n\nuint8_t BTD::Poll() {\n        if(!bPollEnable)\n                return 0;\n        if((long)(millis() - qNextPollTime) >= 0L) { // Don't poll if shorter than polling interval\n                qNextPollTime = millis() + pollInterval; // Set new poll time\n                HCI_event_task(); // Poll the HCI event pipe\n                HCI_task(); // HCI state machine\n                ACL_event_task(); // Poll the ACL input pipe too\n        }\n        return 0;\n}\n\nvoid BTD::disconnect() {\n        for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++)\n                if(btService[i])\n                        btService[i]->disconnect();\n};\n\nvoid BTD::HCI_event_task() {\n        uint16_t length = BULK_MAXPKTSIZE; // Request more than 16 bytes anyway, the inTransfer routine will take care of this\n        uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_EVENT_PIPE ].epAddr, &length, hcibuf); // Input on endpoint 1\n\n        if(!rcode || rcode == hrNAK) { // Check for errors\n                switch(hcibuf[0]) { // Switch on event type\n                        case EV_COMMAND_COMPLETE:\n                                if(!hcibuf[5]) { // Check if command succeeded\n                                        hci_set_flag(HCI_FLAG_CMD_COMPLETE); // Set command complete flag\n                                        if((hcibuf[3] == 0x01) && (hcibuf[4] == 0x10)) { // Parameters from read local version information\n                                                hci_version = hcibuf[6]; // Used to check if it supports 2.0+EDR - see http://www.bluetooth.org/Technical/AssignedNumbers/hci.htm\n                                                hci_set_flag(HCI_FLAG_READ_VERSION);\n                                        } else if((hcibuf[3] == 0x09) && (hcibuf[4] == 0x10)) { // Parameters from read local bluetooth address\n                                                for(uint8_t i = 0; i < 6; i++)\n                                                        my_bdaddr[i] = hcibuf[6 + i];\n                                                hci_set_flag(HCI_FLAG_READ_BDADDR);\n                                        }\n                                }\n                                break;\n\n                        case EV_COMMAND_STATUS:\n                                if(hcibuf[2]) { // Show status on serial if not OK\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nHCI Command Failed: \"), 0x80);\n                                        D_PrintHex<uint8_t > (hcibuf[2], 0x80);\n#endif\n                                }\n                                break;\n\n                        case EV_INQUIRY_COMPLETE:\n                                if(inquiry_counter >= 5 && (pairWithWii || pairWithHIDDevice)) {\n                                        inquiry_counter = 0;\n#ifdef DEBUG_USB_HOST\n                                        if(pairWithWii)\n                                                Notify(PSTR(\"\\r\\nCouldn't find Wiimote\"), 0x80);\n                                        else\n                                                Notify(PSTR(\"\\r\\nCouldn't find HID device\"), 0x80);\n#endif\n                                        connectToWii = false;\n                                        pairWithWii = false;\n                                        connectToHIDDevice = false;\n                                        pairWithHIDDevice = false;\n                                        hci_state = HCI_SCANNING_STATE;\n                                }\n                                inquiry_counter++;\n                                break;\n\n                        case EV_INQUIRY_RESULT:\n                                if(hcibuf[2]) { // Check that there is more than zero responses\n#ifdef EXTRADEBUG\n                                        Notify(PSTR(\"\\r\\nNumber of responses: \"), 0x80);\n                                        Notify(hcibuf[2], 0x80);\n#endif\n                                        for(uint8_t i = 0; i < hcibuf[2]; i++) {\n                                                uint8_t offset = 8 * hcibuf[2] + 3 * i;\n\n                                                for(uint8_t j = 0; j < 3; j++)\n                                                        classOfDevice[j] = hcibuf[j + 4 + offset];\n\n#ifdef EXTRADEBUG\n                                                Notify(PSTR(\"\\r\\nClass of device: \"), 0x80);\n                                                D_PrintHex<uint8_t > (classOfDevice[2], 0x80);\n                                                Notify(PSTR(\" \"), 0x80);\n                                                D_PrintHex<uint8_t > (classOfDevice[1], 0x80);\n                                                Notify(PSTR(\" \"), 0x80);\n                                                D_PrintHex<uint8_t > (classOfDevice[0], 0x80);\n#endif\n\n                                                if(pairWithWii && classOfDevice[2] == 0x00 && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0x0C)) { // See http://wiibrew.org/wiki/Wiimote#SDP_information\n                                                        checkRemoteName = true; // Check remote name to distinguish between the different controllers\n\n                                                        for(uint8_t j = 0; j < 6; j++)\n                                                                disc_bdaddr[j] = hcibuf[j + 3 + 6 * i];\n\n                                                        hci_set_flag(HCI_FLAG_DEVICE_FOUND);\n                                                        break;\n                                                } else if(pairWithHIDDevice && (classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad - see: http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html\n#ifdef DEBUG_USB_HOST\n                                                        if(classOfDevice[0] & 0x80)\n                                                                Notify(PSTR(\"\\r\\nMouse found\"), 0x80);\n                                                        if(classOfDevice[0] & 0x40)\n                                                                Notify(PSTR(\"\\r\\nKeyboard found\"), 0x80);\n                                                        if(classOfDevice[0] & 0x08)\n                                                                Notify(PSTR(\"\\r\\nGamepad found\"), 0x80);\n#endif\n\n                                                        for(uint8_t j = 0; j < 6; j++)\n                                                                disc_bdaddr[j] = hcibuf[j + 3 + 6 * i];\n\n                                                        hci_set_flag(HCI_FLAG_DEVICE_FOUND);\n                                                        break;\n                                                }\n                                        }\n                                }\n                                break;\n\n                        case EV_CONNECT_COMPLETE:\n                                hci_set_flag(HCI_FLAG_CONNECT_EVENT);\n                                if(!hcibuf[2]) { // Check if connected OK\n#ifdef EXTRADEBUG\n                                        Notify(PSTR(\"\\r\\nConnection established\"), 0x80);\n#endif\n                                        hci_handle = hcibuf[3] | ((hcibuf[4] & 0x0F) << 8); // Store the handle for the ACL connection\n                                        hci_set_flag(HCI_FLAG_CONNECT_COMPLETE); // Set connection complete flag\n                                } else {\n                                        hci_state = HCI_CHECK_DEVICE_SERVICE;\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nConnection Failed: \"), 0x80);\n                                        D_PrintHex<uint8_t > (hcibuf[2], 0x80);\n#endif\n                                }\n                                break;\n\n                        case EV_DISCONNECT_COMPLETE:\n                                if(!hcibuf[2]) { // Check if disconnected OK\n                                        hci_set_flag(HCI_FLAG_DISCONNECT_COMPLETE); // Set disconnect command complete flag\n                                        hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE); // Clear connection complete flag\n                                }\n                                break;\n\n                        case EV_REMOTE_NAME_COMPLETE:\n                                if(!hcibuf[2]) { // Check if reading is OK\n                                        for(uint8_t i = 0; i < min(sizeof (remote_name), sizeof (hcibuf) - 9); i++) {\n                                                remote_name[i] = hcibuf[9 + i];\n                                                if(remote_name[i] == '\\0') // End of string\n                                                        break;\n                                        }\n                                        // TODO: Altid sæt '\\0' i remote name!\n                                        hci_set_flag(HCI_FLAG_REMOTE_NAME_COMPLETE);\n                                }\n                                break;\n\n                        case EV_INCOMING_CONNECT:\n                                for(uint8_t i = 0; i < 6; i++)\n                                        disc_bdaddr[i] = hcibuf[i + 2];\n\n                                for(uint8_t i = 0; i < 3; i++)\n                                        classOfDevice[i] = hcibuf[i + 8];\n\n                                if((classOfDevice[1] & 0x05) && (classOfDevice[0] & 0xC8)) { // Check if it is a mouse, keyboard or a gamepad\n#ifdef DEBUG_USB_HOST\n                                        if(classOfDevice[0] & 0x80)\n                                                Notify(PSTR(\"\\r\\nMouse is connecting\"), 0x80);\n                                        if(classOfDevice[0] & 0x40)\n                                                Notify(PSTR(\"\\r\\nKeyboard is connecting\"), 0x80);\n                                        if(classOfDevice[0] & 0x08)\n                                                Notify(PSTR(\"\\r\\nGamepad is connecting\"), 0x80);\n#endif\n                                        incomingHIDDevice = true;\n                                }\n\n#ifdef EXTRADEBUG\n                                Notify(PSTR(\"\\r\\nClass of device: \"), 0x80);\n                                D_PrintHex<uint8_t > (classOfDevice[2], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (classOfDevice[1], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (classOfDevice[0], 0x80);\n#endif\n                                hci_set_flag(HCI_FLAG_INCOMING_REQUEST);\n                                break;\n\n                        case EV_PIN_CODE_REQUEST:\n                                if(pairWithWii) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nPairing with Wiimote\"), 0x80);\n#endif\n                                        hci_pin_code_request_reply();\n                                } else if(btdPin != NULL) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nBluetooth pin is set too: \"), 0x80);\n                                        NotifyStr(btdPin, 0x80);\n#endif\n                                        hci_pin_code_request_reply();\n                                } else {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nNo pin was set\"), 0x80);\n#endif\n                                        hci_pin_code_negative_request_reply();\n                                }\n                                break;\n\n                        case EV_LINK_KEY_REQUEST:\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nReceived Key Request\"), 0x80);\n#endif\n                                hci_link_key_request_negative_reply();\n                                break;\n\n                        case EV_AUTHENTICATION_COMPLETE:\n                                if(pairWithWii && !connectToWii) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nPairing successful with Wiimote\"), 0x80);\n#endif\n                                        connectToWii = true; // Used to indicate to the Wii service, that it should connect to this device\n                                } else if(pairWithHIDDevice && !connectToHIDDevice) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nPairing successful with HID device\"), 0x80);\n#endif\n                                        connectToHIDDevice = true; // Used to indicate to the BTHID service, that it should connect to this device\n                                }\n                                break;\n                                /* We will just ignore the following events */\n                        case EV_NUM_COMPLETE_PKT:\n                        case EV_ROLE_CHANGED:\n                        case EV_PAGE_SCAN_REP_MODE:\n                        case EV_LOOPBACK_COMMAND:\n                        case EV_DATA_BUFFER_OVERFLOW:\n                        case EV_CHANGE_CONNECTION_LINK:\n                        case EV_MAX_SLOTS_CHANGE:\n                        case EV_QOS_SETUP_COMPLETE:\n                        case EV_LINK_KEY_NOTIFICATION:\n                        case EV_ENCRYPTION_CHANGE:\n                        case EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE:\n                                break;\n#ifdef EXTRADEBUG\n                        default:\n                                if(hcibuf[0] != 0x00) {\n                                        Notify(PSTR(\"\\r\\nUnmanaged HCI Event: \"), 0x80);\n                                        D_PrintHex<uint8_t > (hcibuf[0], 0x80);\n                                }\n                                break;\n#endif\n                } // Switch\n        }\n#ifdef EXTRADEBUG\n        else {\n                Notify(PSTR(\"\\r\\nHCI event error: \"), 0x80);\n                D_PrintHex<uint8_t > (rcode, 0x80);\n        }\n#endif\n}\n\n/* Poll Bluetooth and print result */\nvoid BTD::HCI_task() {\n        switch(hci_state) {\n                case HCI_INIT_STATE:\n                        hci_counter++;\n                        if(hci_counter > hci_num_reset_loops) { // wait until we have looped x times to clear any old events\n                                hci_reset();\n                                hci_state = HCI_RESET_STATE;\n                                hci_counter = 0;\n                        }\n                        break;\n\n                case HCI_RESET_STATE:\n                        hci_counter++;\n                        if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) {\n                                hci_counter = 0;\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHCI Reset complete\"), 0x80);\n#endif\n                                hci_state = HCI_CLASS_STATE;\n                                hci_write_class_of_device();\n                        } else if(hci_counter > hci_num_reset_loops) {\n                                hci_num_reset_loops *= 10;\n                                if(hci_num_reset_loops > 2000)\n                                        hci_num_reset_loops = 2000;\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nNo response to HCI Reset\"), 0x80);\n#endif\n                                hci_state = HCI_INIT_STATE;\n                                hci_counter = 0;\n                        }\n                        break;\n\n                case HCI_CLASS_STATE:\n                        if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nWrite class of device\"), 0x80);\n#endif\n                                hci_state = HCI_BDADDR_STATE;\n                                hci_read_bdaddr();\n                        }\n                        break;\n\n                case HCI_BDADDR_STATE:\n                        if(hci_check_flag(HCI_FLAG_READ_BDADDR)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nLocal Bluetooth Address: \"), 0x80);\n                                for(int8_t i = 5; i > 0; i--) {\n                                        D_PrintHex<uint8_t > (my_bdaddr[i], 0x80);\n                                        Notify(PSTR(\":\"), 0x80);\n                                }\n                                D_PrintHex<uint8_t > (my_bdaddr[0], 0x80);\n#endif\n                                hci_read_local_version_information();\n                                hci_state = HCI_LOCAL_VERSION_STATE;\n                        }\n                        break;\n\n                case HCI_LOCAL_VERSION_STATE: // The local version is used by the PS3BT class\n                        if(hci_check_flag(HCI_FLAG_READ_VERSION)) {\n                                if(btdName != NULL) {\n                                        hci_set_local_name(btdName);\n                                        hci_state = HCI_SET_NAME_STATE;\n                                } else\n                                        hci_state = HCI_CHECK_DEVICE_SERVICE;\n                        }\n                        break;\n\n                case HCI_SET_NAME_STATE:\n                        if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nThe name is set to: \"), 0x80);\n                                NotifyStr(btdName, 0x80);\n#endif\n                                hci_state = HCI_CHECK_DEVICE_SERVICE;\n                        }\n                        break;\n\n                case HCI_CHECK_DEVICE_SERVICE:\n                        if(pairWithHIDDevice || pairWithWii) { // Check if it should try to connect to a Wiimote\n#ifdef DEBUG_USB_HOST\n                                if(pairWithWii)\n                                        Notify(PSTR(\"\\r\\nStarting inquiry\\r\\nPress 1 & 2 on the Wiimote\\r\\nOr press the SYNC button if you are using a Wii U Pro Controller or a Wii Balance Board\"), 0x80);\n                                else\n                                        Notify(PSTR(\"\\r\\nPlease enable discovery of your device\"), 0x80);\n#endif\n                                hci_inquiry();\n                                hci_state = HCI_INQUIRY_STATE;\n                        } else\n                                hci_state = HCI_SCANNING_STATE; // Don't try to connect to a Wiimote\n                        break;\n\n                case HCI_INQUIRY_STATE:\n                        if(hci_check_flag(HCI_FLAG_DEVICE_FOUND)) {\n                                hci_inquiry_cancel(); // Stop inquiry\n#ifdef DEBUG_USB_HOST\n                                if(pairWithWii)\n                                        Notify(PSTR(\"\\r\\nWiimote found\"), 0x80);\n                                else\n                                        Notify(PSTR(\"\\r\\nHID device found\"), 0x80);\n\n                                Notify(PSTR(\"\\r\\nNow just create the instance like so:\"), 0x80);\n                                if(pairWithWii)\n                                        Notify(PSTR(\"\\r\\nWII Wii(&Btd);\"), 0x80);\n                                else\n                                        Notify(PSTR(\"\\r\\nBTHID bthid(&Btd);\"), 0x80);\n\n                                Notify(PSTR(\"\\r\\nAnd then press any button on the \"), 0x80);\n                                if(pairWithWii)\n                                        Notify(PSTR(\"Wiimote\"), 0x80);\n                                else\n                                        Notify(PSTR(\"device\"), 0x80);\n#endif\n                                if(checkRemoteName) {\n                                        hci_remote_name(); // We need to know the name to distinguish between the Wiimote, the new Wiimote with Motion Plus inside, a Wii U Pro Controller and a Wii Balance Board\n                                        hci_state = HCI_REMOTE_NAME_STATE;\n                                } else\n                                        hci_state = HCI_CONNECT_DEVICE_STATE;\n                        }\n                        break;\n\n                case HCI_CONNECT_DEVICE_STATE:\n                        if(hci_check_flag(HCI_FLAG_CMD_COMPLETE)) {\n#ifdef DEBUG_USB_HOST\n                                if(pairWithWii)\n                                        Notify(PSTR(\"\\r\\nConnecting to Wiimote\"), 0x80);\n                                else\n                                        Notify(PSTR(\"\\r\\nConnecting to HID device\"), 0x80);\n#endif\n                                checkRemoteName = false;\n                                hci_connect();\n                                hci_state = HCI_CONNECTED_DEVICE_STATE;\n                        }\n                        break;\n\n                case HCI_CONNECTED_DEVICE_STATE:\n                        if(hci_check_flag(HCI_FLAG_CONNECT_EVENT)) {\n                                if(hci_check_flag(HCI_FLAG_CONNECT_COMPLETE)) {\n#ifdef DEBUG_USB_HOST\n                                        if(pairWithWii)\n                                                Notify(PSTR(\"\\r\\nConnected to Wiimote\"), 0x80);\n                                        else\n                                                Notify(PSTR(\"\\r\\nConnected to HID device\"), 0x80);\n#endif\n                                        hci_authentication_request(); // This will start the pairing with the Wiimote\n                                        hci_state = HCI_SCANNING_STATE;\n                                } else {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nTrying to connect one more time...\"), 0x80);\n#endif\n                                        hci_connect(); // Try to connect one more time\n                                }\n                        }\n                        break;\n\n                case HCI_SCANNING_STATE:\n                        if(!connectToWii && !pairWithWii && !connectToHIDDevice && !pairWithHIDDevice) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nWait For Incoming Connection Request\"), 0x80);\n#endif\n                                hci_write_scan_enable();\n                                watingForConnection = true;\n                                hci_state = HCI_CONNECT_IN_STATE;\n                        }\n                        break;\n\n                case HCI_CONNECT_IN_STATE:\n                        if(hci_check_flag(HCI_FLAG_INCOMING_REQUEST)) {\n                                watingForConnection = false;\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nIncoming Connection Request\"), 0x80);\n#endif\n                                hci_remote_name();\n                                hci_state = HCI_REMOTE_NAME_STATE;\n                        } else if(hci_check_flag(HCI_FLAG_DISCONNECT_COMPLETE))\n                                hci_state = HCI_DISCONNECT_STATE;\n                        break;\n\n                case HCI_REMOTE_NAME_STATE:\n                        if(hci_check_flag(HCI_FLAG_REMOTE_NAME_COMPLETE)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nRemote Name: \"), 0x80);\n                                for(uint8_t i = 0; i < strlen(remote_name); i++)\n                                        Notifyc(remote_name[i], 0x80);\n#endif\n                                if(strncmp((const char*)remote_name, \"Nintendo\", 8) == 0) {\n                                        incomingWii = true;\n                                        motionPlusInside = false;\n                                        wiiUProController = false;\n                                        pairWiiUsingSync = false;\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nWiimote is connecting\"), 0x80);\n#endif\n                                        if(strncmp((const char*)remote_name, \"Nintendo RVL-CNT-01-TR\", 22) == 0) {\n#ifdef DEBUG_USB_HOST\n                                                Notify(PSTR(\" with Motion Plus Inside\"), 0x80);\n#endif\n                                                motionPlusInside = true;\n                                        } else if(strncmp((const char*)remote_name, \"Nintendo RVL-CNT-01-UC\", 22) == 0) {\n#ifdef DEBUG_USB_HOST\n                                                Notify(PSTR(\" - Wii U Pro Controller\"), 0x80);\n#endif\n                                                wiiUProController = motionPlusInside = pairWiiUsingSync = true;\n                                        } else if(strncmp((const char*)remote_name, \"Nintendo RVL-WBC-01\", 19) == 0) {\n#ifdef DEBUG_USB_HOST\n                                                Notify(PSTR(\" - Wii Balance Board\"), 0x80);\n#endif\n                                                pairWiiUsingSync = true;\n                                        }\n                                }\n                                if(classOfDevice[2] == 0 && classOfDevice[1] == 0x25 && classOfDevice[0] == 0x08 && strncmp((const char*)remote_name, \"Wireless Controller\", 19) == 0) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nPS4 controller is connecting\"), 0x80);\n#endif\n                                        incomingPS4 = true;\n                                }\n                                if(pairWithWii && checkRemoteName)\n                                        hci_state = HCI_CONNECT_DEVICE_STATE;\n                                else {\n                                        hci_accept_connection();\n                                        hci_state = HCI_CONNECTED_STATE;\n                                }\n                        }\n                        break;\n\n                case HCI_CONNECTED_STATE:\n                        if(hci_check_flag(HCI_FLAG_CONNECT_COMPLETE)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nConnected to Device: \"), 0x80);\n                                for(int8_t i = 5; i > 0; i--) {\n                                        D_PrintHex<uint8_t > (disc_bdaddr[i], 0x80);\n                                        Notify(PSTR(\":\"), 0x80);\n                                }\n                                D_PrintHex<uint8_t > (disc_bdaddr[0], 0x80);\n#endif\n                                if(incomingPS4)\n                                        connectToHIDDevice = true; // We should always connect to the PS4 controller\n\n                                // Clear these flags for a new connection\n                                l2capConnectionClaimed = false;\n                                sdpConnectionClaimed = false;\n                                rfcommConnectionClaimed = false;\n\n                                hci_event_flag = 0;\n                                hci_state = HCI_DONE_STATE;\n                        }\n                        break;\n\n                case HCI_DONE_STATE:\n                        hci_counter++;\n                        if(hci_counter > 1000) { // Wait until we have looped 1000 times to make sure that the L2CAP connection has been started\n                                hci_counter = 0;\n                                hci_state = HCI_SCANNING_STATE;\n                        }\n                        break;\n\n                case HCI_DISCONNECT_STATE:\n                        if(hci_check_flag(HCI_FLAG_DISCONNECT_COMPLETE)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHCI Disconnected from Device\"), 0x80);\n#endif\n                                hci_event_flag = 0; // Clear all flags\n\n                                // Reset all buffers\n                                memset(hcibuf, 0, BULK_MAXPKTSIZE);\n                                memset(l2capinbuf, 0, BULK_MAXPKTSIZE);\n\n                                connectToWii = incomingWii = pairWithWii = false;\n                                connectToHIDDevice = incomingHIDDevice = pairWithHIDDevice = checkRemoteName = false;\n                                incomingPS4 = false;\n\n                                hci_state = HCI_SCANNING_STATE;\n                        }\n                        break;\n                default:\n                        break;\n        }\n}\n\nvoid BTD::ACL_event_task() {\n        uint16_t length = BULK_MAXPKTSIZE;\n        uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ BTD_DATAIN_PIPE ].epAddr, &length, l2capinbuf); // Input on endpoint 2\n\n        if(!rcode) { // Check for errors\n                if(length > 0) { // Check if any data was read\n                        for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) {\n                                if(btService[i])\n                                        btService[i]->ACLData(l2capinbuf);\n                        }\n                }\n        }\n#ifdef EXTRADEBUG\n        else if(rcode != hrNAK) {\n                Notify(PSTR(\"\\r\\nACL data in error: \"), 0x80);\n                D_PrintHex<uint8_t > (rcode, 0x80);\n        }\n#endif\n        for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++)\n                if(btService[i])\n                        btService[i]->Run();\n}\n\n/************************************************************/\n/*                    HCI Commands                        */\n\n/************************************************************/\nvoid BTD::HCI_Command(uint8_t* data, uint16_t nbytes) {\n        hci_clear_flag(HCI_FLAG_CMD_COMPLETE);\n        pUsb->ctrlReq(bAddress, epInfo[ BTD_CONTROL_PIPE ].epAddr, bmREQ_HCI_OUT, 0x00, 0x00, 0x00, 0x00, nbytes, nbytes, data, NULL);\n}\n\nvoid BTD::hci_reset() {\n        hci_event_flag = 0; // Clear all the flags\n        hcibuf[0] = 0x03; // HCI OCF = 3\n        hcibuf[1] = 0x03 << 2; // HCI OGF = 3\n        hcibuf[2] = 0x00;\n\n        HCI_Command(hcibuf, 3);\n}\n\nvoid BTD::hci_write_scan_enable() {\n        hci_clear_flag(HCI_FLAG_INCOMING_REQUEST);\n        hcibuf[0] = 0x1A; // HCI OCF = 1A\n        hcibuf[1] = 0x03 << 2; // HCI OGF = 3\n        hcibuf[2] = 0x01; // parameter length = 1\n        if(btdName != NULL)\n                hcibuf[3] = 0x03; // Inquiry Scan enabled. Page Scan enabled.\n        else\n                hcibuf[3] = 0x02; // Inquiry Scan disabled. Page Scan enabled.\n\n        HCI_Command(hcibuf, 4);\n}\n\nvoid BTD::hci_write_scan_disable() {\n        hcibuf[0] = 0x1A; // HCI OCF = 1A\n        hcibuf[1] = 0x03 << 2; // HCI OGF = 3\n        hcibuf[2] = 0x01; // parameter length = 1\n        hcibuf[3] = 0x00; // Inquiry Scan disabled. Page Scan disabled.\n\n        HCI_Command(hcibuf, 4);\n}\n\nvoid BTD::hci_read_bdaddr() {\n        hci_clear_flag(HCI_FLAG_READ_BDADDR);\n        hcibuf[0] = 0x09; // HCI OCF = 9\n        hcibuf[1] = 0x04 << 2; // HCI OGF = 4\n        hcibuf[2] = 0x00;\n\n        HCI_Command(hcibuf, 3);\n}\n\nvoid BTD::hci_read_local_version_information() {\n        hci_clear_flag(HCI_FLAG_READ_VERSION);\n        hcibuf[0] = 0x01; // HCI OCF = 1\n        hcibuf[1] = 0x04 << 2; // HCI OGF = 4\n        hcibuf[2] = 0x00;\n\n        HCI_Command(hcibuf, 3);\n}\n\nvoid BTD::hci_accept_connection() {\n        hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE);\n        hcibuf[0] = 0x09; // HCI OCF = 9\n        hcibuf[1] = 0x01 << 2; // HCI OGF = 1\n        hcibuf[2] = 0x07; // parameter length 7\n        hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr\n        hcibuf[4] = disc_bdaddr[1];\n        hcibuf[5] = disc_bdaddr[2];\n        hcibuf[6] = disc_bdaddr[3];\n        hcibuf[7] = disc_bdaddr[4];\n        hcibuf[8] = disc_bdaddr[5];\n        hcibuf[9] = 0x00; // Switch role to master\n\n        HCI_Command(hcibuf, 10);\n}\n\nvoid BTD::hci_remote_name() {\n        hci_clear_flag(HCI_FLAG_REMOTE_NAME_COMPLETE);\n        hcibuf[0] = 0x19; // HCI OCF = 19\n        hcibuf[1] = 0x01 << 2; // HCI OGF = 1\n        hcibuf[2] = 0x0A; // parameter length = 10\n        hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr\n        hcibuf[4] = disc_bdaddr[1];\n        hcibuf[5] = disc_bdaddr[2];\n        hcibuf[6] = disc_bdaddr[3];\n        hcibuf[7] = disc_bdaddr[4];\n        hcibuf[8] = disc_bdaddr[5];\n        hcibuf[9] = 0x01; // Page Scan Repetition Mode\n        hcibuf[10] = 0x00; // Reserved\n        hcibuf[11] = 0x00; // Clock offset - low byte\n        hcibuf[12] = 0x00; // Clock offset - high byte\n\n        HCI_Command(hcibuf, 13);\n}\n\nvoid BTD::hci_set_local_name(const char* name) {\n        hcibuf[0] = 0x13; // HCI OCF = 13\n        hcibuf[1] = 0x03 << 2; // HCI OGF = 3\n        hcibuf[2] = strlen(name) + 1; // parameter length = the length of the string + end byte\n        uint8_t i;\n        for(i = 0; i < strlen(name); i++)\n                hcibuf[i + 3] = name[i];\n        hcibuf[i + 3] = 0x00; // End of string\n\n        HCI_Command(hcibuf, 4 + strlen(name));\n}\n\nvoid BTD::hci_inquiry() {\n        hci_clear_flag(HCI_FLAG_DEVICE_FOUND);\n        hcibuf[0] = 0x01;\n        hcibuf[1] = 0x01 << 2; // HCI OGF = 1\n        hcibuf[2] = 0x05; // Parameter Total Length = 5\n        hcibuf[3] = 0x33; // LAP: Genera/Unlimited Inquiry Access Code (GIAC = 0x9E8B33) - see https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm\n        hcibuf[4] = 0x8B;\n        hcibuf[5] = 0x9E;\n        hcibuf[6] = 0x30; // Inquiry time = 61.44 sec (maximum)\n        hcibuf[7] = 0x0A; // 10 number of responses\n\n        HCI_Command(hcibuf, 8);\n}\n\nvoid BTD::hci_inquiry_cancel() {\n        hcibuf[0] = 0x02;\n        hcibuf[1] = 0x01 << 2; // HCI OGF = 1\n        hcibuf[2] = 0x00; // Parameter Total Length = 0\n\n        HCI_Command(hcibuf, 3);\n}\n\nvoid BTD::hci_connect() {\n        hci_connect(disc_bdaddr); // Use last discovered device\n}\n\nvoid BTD::hci_connect(uint8_t *bdaddr) {\n        hci_clear_flag(HCI_FLAG_CONNECT_COMPLETE | HCI_FLAG_CONNECT_EVENT);\n        hcibuf[0] = 0x05;\n        hcibuf[1] = 0x01 << 2; // HCI OGF = 1\n        hcibuf[2] = 0x0D; // parameter Total Length = 13\n        hcibuf[3] = bdaddr[0]; // 6 octet bdaddr (LSB)\n        hcibuf[4] = bdaddr[1];\n        hcibuf[5] = bdaddr[2];\n        hcibuf[6] = bdaddr[3];\n        hcibuf[7] = bdaddr[4];\n        hcibuf[8] = bdaddr[5];\n        hcibuf[9] = 0x18; // DM1 or DH1 may be used\n        hcibuf[10] = 0xCC; // DM3, DH3, DM5, DH5 may be used\n        hcibuf[11] = 0x01; // Page repetition mode R1\n        hcibuf[12] = 0x00; // Reserved\n        hcibuf[13] = 0x00; // Clock offset\n        hcibuf[14] = 0x00; // Invalid clock offset\n        hcibuf[15] = 0x00; // Do not allow role switch\n\n        HCI_Command(hcibuf, 16);\n}\n\nvoid BTD::hci_pin_code_request_reply() {\n        hcibuf[0] = 0x0D; // HCI OCF = 0D\n        hcibuf[1] = 0x01 << 2; // HCI OGF = 1\n        hcibuf[2] = 0x17; // parameter length 23\n        hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr\n        hcibuf[4] = disc_bdaddr[1];\n        hcibuf[5] = disc_bdaddr[2];\n        hcibuf[6] = disc_bdaddr[3];\n        hcibuf[7] = disc_bdaddr[4];\n        hcibuf[8] = disc_bdaddr[5];\n        if(pairWithWii) {\n                hcibuf[9] = 6; // Pin length is the length of the Bluetooth address\n                if(pairWiiUsingSync) {\n#ifdef DEBUG_USB_HOST\n                        Notify(PSTR(\"\\r\\nParing with Wii controller via SYNC\"), 0x80);\n#endif\n                        for(uint8_t i = 0; i < 6; i++)\n                                hcibuf[10 + i] = my_bdaddr[i]; // The pin is the Bluetooth dongles Bluetooth address backwards\n                } else {\n                        for(uint8_t i = 0; i < 6; i++)\n                                hcibuf[10 + i] = disc_bdaddr[i]; // The pin is the Wiimote's Bluetooth address backwards\n                }\n                for(uint8_t i = 16; i < 26; i++)\n                        hcibuf[i] = 0x00; // The rest should be 0\n        } else {\n                hcibuf[9] = strlen(btdPin); // Length of pin\n                uint8_t i;\n                for(i = 0; i < strlen(btdPin); i++) // The maximum size of the pin is 16\n                        hcibuf[i + 10] = btdPin[i];\n                for(; i < 16; i++)\n                        hcibuf[i + 10] = 0x00; // The rest should be 0\n        }\n\n        HCI_Command(hcibuf, 26);\n}\n\nvoid BTD::hci_pin_code_negative_request_reply() {\n        hcibuf[0] = 0x0E; // HCI OCF = 0E\n        hcibuf[1] = 0x01 << 2; // HCI OGF = 1\n        hcibuf[2] = 0x06; // parameter length 6\n        hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr\n        hcibuf[4] = disc_bdaddr[1];\n        hcibuf[5] = disc_bdaddr[2];\n        hcibuf[6] = disc_bdaddr[3];\n        hcibuf[7] = disc_bdaddr[4];\n        hcibuf[8] = disc_bdaddr[5];\n\n        HCI_Command(hcibuf, 9);\n}\n\nvoid BTD::hci_link_key_request_negative_reply() {\n        hcibuf[0] = 0x0C; // HCI OCF = 0C\n        hcibuf[1] = 0x01 << 2; // HCI OGF = 1\n        hcibuf[2] = 0x06; // parameter length 6\n        hcibuf[3] = disc_bdaddr[0]; // 6 octet bdaddr\n        hcibuf[4] = disc_bdaddr[1];\n        hcibuf[5] = disc_bdaddr[2];\n        hcibuf[6] = disc_bdaddr[3];\n        hcibuf[7] = disc_bdaddr[4];\n        hcibuf[8] = disc_bdaddr[5];\n\n        HCI_Command(hcibuf, 9);\n}\n\nvoid BTD::hci_authentication_request() {\n        hcibuf[0] = 0x11; // HCI OCF = 11\n        hcibuf[1] = 0x01 << 2; // HCI OGF = 1\n        hcibuf[2] = 0x02; // parameter length = 2\n        hcibuf[3] = (uint8_t)(hci_handle & 0xFF); //connection handle - low byte\n        hcibuf[4] = (uint8_t)((hci_handle >> 8) & 0x0F); //connection handle - high byte\n\n        HCI_Command(hcibuf, 5);\n}\n\nvoid BTD::hci_disconnect(uint16_t handle) { // This is called by the different services\n        hci_clear_flag(HCI_FLAG_DISCONNECT_COMPLETE);\n        hcibuf[0] = 0x06; // HCI OCF = 6\n        hcibuf[1] = 0x01 << 2; // HCI OGF = 1\n        hcibuf[2] = 0x03; // parameter length = 3\n        hcibuf[3] = (uint8_t)(handle & 0xFF); //connection handle - low byte\n        hcibuf[4] = (uint8_t)((handle >> 8) & 0x0F); //connection handle - high byte\n        hcibuf[5] = 0x13; // reason\n\n        HCI_Command(hcibuf, 6);\n}\n\nvoid BTD::hci_write_class_of_device() { // See http://bluetooth-pentest.narod.ru/software/bluetooth_class_of_device-service_generator.html\n        hcibuf[0] = 0x24; // HCI OCF = 24\n        hcibuf[1] = 0x03 << 2; // HCI OGF = 3\n        hcibuf[2] = 0x03; // parameter length = 3\n        hcibuf[3] = 0x04; // Robot\n        hcibuf[4] = 0x08; // Toy\n        hcibuf[5] = 0x00;\n\n        HCI_Command(hcibuf, 6);\n}\n/*******************************************************************\n *                                                                 *\n *                        HCI ACL Data Packet                      *\n *                                                                 *\n *   buf[0]          buf[1]          buf[2]          buf[3]\n *   0       4       8    11 12      16              24            31 MSB\n *  .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.\n *  |      HCI Handle       |PB |BC |       Data Total Length       |   HCI ACL Data Packet\n *  .-+-+-+-+-+-+-+-|-+-+-+-|-+-|-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.\n *\n *   buf[4]          buf[5]          buf[6]          buf[7]\n *   0               8               16                            31 MSB\n *  .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.\n *  |            Length             |          Channel ID           |   Basic L2CAP header\n *  .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.\n *\n *   buf[8]          buf[9]          buf[10]         buf[11]\n *   0               8               16                            31 MSB\n *  .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.\n *  |     Code      |  Identifier   |            Length             |   Control frame (C-frame)\n *  .-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-.   (signaling packet format)\n */\n/************************************************************/\n/*                    L2CAP Commands                        */\n\n/************************************************************/\nvoid BTD::L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t channelLow, uint8_t channelHigh) {\n        uint8_t buf[8 + nbytes];\n        buf[0] = (uint8_t)(handle & 0xff); // HCI handle with PB,BC flag\n        buf[1] = (uint8_t)(((handle >> 8) & 0x0f) | 0x20);\n        buf[2] = (uint8_t)((4 + nbytes) & 0xff); // HCI ACL total data length\n        buf[3] = (uint8_t)((4 + nbytes) >> 8);\n        buf[4] = (uint8_t)(nbytes & 0xff); // L2CAP header: Length\n        buf[5] = (uint8_t)(nbytes >> 8);\n        buf[6] = channelLow;\n        buf[7] = channelHigh;\n\n        for(uint16_t i = 0; i < nbytes; i++) // L2CAP C-frame\n                buf[8 + i] = data[i];\n\n        uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ BTD_DATAOUT_PIPE ].epAddr, (8 + nbytes), buf);\n        if(rcode) {\n                delay(100); // This small delay prevents it from overflowing if it fails\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nError sending L2CAP message: 0x\"), 0x80);\n                D_PrintHex<uint8_t > (rcode, 0x80);\n                Notify(PSTR(\" - Channel ID: \"), 0x80);\n                D_PrintHex<uint8_t > (channelHigh, 0x80);\n                Notify(PSTR(\" \"), 0x80);\n                D_PrintHex<uint8_t > (channelLow, 0x80);\n#endif\n        }\n}\n\nvoid BTD::l2cap_connection_request(uint16_t handle, uint8_t rxid, uint8_t* scid, uint16_t psm) {\n        l2capoutbuf[0] = L2CAP_CMD_CONNECTION_REQUEST; // Code\n        l2capoutbuf[1] = rxid; // Identifier\n        l2capoutbuf[2] = 0x04; // Length\n        l2capoutbuf[3] = 0x00;\n        l2capoutbuf[4] = (uint8_t)(psm & 0xff); // PSM\n        l2capoutbuf[5] = (uint8_t)(psm >> 8);\n        l2capoutbuf[6] = scid[0]; // Source CID\n        l2capoutbuf[7] = scid[1];\n\n        L2CAP_Command(handle, l2capoutbuf, 8);\n}\n\nvoid BTD::l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result) {\n        l2capoutbuf[0] = L2CAP_CMD_CONNECTION_RESPONSE; // Code\n        l2capoutbuf[1] = rxid; // Identifier\n        l2capoutbuf[2] = 0x08; // Length\n        l2capoutbuf[3] = 0x00;\n        l2capoutbuf[4] = dcid[0]; // Destination CID\n        l2capoutbuf[5] = dcid[1];\n        l2capoutbuf[6] = scid[0]; // Source CID\n        l2capoutbuf[7] = scid[1];\n        l2capoutbuf[8] = result; // Result: Pending or Success\n        l2capoutbuf[9] = 0x00;\n        l2capoutbuf[10] = 0x00; // No further information\n        l2capoutbuf[11] = 0x00;\n\n        L2CAP_Command(handle, l2capoutbuf, 12);\n}\n\nvoid BTD::l2cap_config_request(uint16_t handle, uint8_t rxid, uint8_t* dcid) {\n        l2capoutbuf[0] = L2CAP_CMD_CONFIG_REQUEST; // Code\n        l2capoutbuf[1] = rxid; // Identifier\n        l2capoutbuf[2] = 0x08; // Length\n        l2capoutbuf[3] = 0x00;\n        l2capoutbuf[4] = dcid[0]; // Destination CID\n        l2capoutbuf[5] = dcid[1];\n        l2capoutbuf[6] = 0x00; // Flags\n        l2capoutbuf[7] = 0x00;\n        l2capoutbuf[8] = 0x01; // Config Opt: type = MTU (Maximum Transmission Unit) - Hint\n        l2capoutbuf[9] = 0x02; // Config Opt: length\n        l2capoutbuf[10] = 0xFF; // MTU\n        l2capoutbuf[11] = 0xFF;\n\n        L2CAP_Command(handle, l2capoutbuf, 12);\n}\n\nvoid BTD::l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid) {\n        l2capoutbuf[0] = L2CAP_CMD_CONFIG_RESPONSE; // Code\n        l2capoutbuf[1] = rxid; // Identifier\n        l2capoutbuf[2] = 0x0A; // Length\n        l2capoutbuf[3] = 0x00;\n        l2capoutbuf[4] = scid[0]; // Source CID\n        l2capoutbuf[5] = scid[1];\n        l2capoutbuf[6] = 0x00; // Flag\n        l2capoutbuf[7] = 0x00;\n        l2capoutbuf[8] = 0x00; // Result\n        l2capoutbuf[9] = 0x00;\n        l2capoutbuf[10] = 0x01; // Config\n        l2capoutbuf[11] = 0x02;\n        l2capoutbuf[12] = 0xA0;\n        l2capoutbuf[13] = 0x02;\n\n        L2CAP_Command(handle, l2capoutbuf, 14);\n}\n\nvoid BTD::l2cap_disconnection_request(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid) {\n        l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_REQUEST; // Code\n        l2capoutbuf[1] = rxid; // Identifier\n        l2capoutbuf[2] = 0x04; // Length\n        l2capoutbuf[3] = 0x00;\n        l2capoutbuf[4] = dcid[0];\n        l2capoutbuf[5] = dcid[1];\n        l2capoutbuf[6] = scid[0];\n        l2capoutbuf[7] = scid[1];\n\n        L2CAP_Command(handle, l2capoutbuf, 8);\n}\n\nvoid BTD::l2cap_disconnection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid) {\n        l2capoutbuf[0] = L2CAP_CMD_DISCONNECT_RESPONSE; // Code\n        l2capoutbuf[1] = rxid; // Identifier\n        l2capoutbuf[2] = 0x04; // Length\n        l2capoutbuf[3] = 0x00;\n        l2capoutbuf[4] = dcid[0];\n        l2capoutbuf[5] = dcid[1];\n        l2capoutbuf[6] = scid[0];\n        l2capoutbuf[7] = scid[1];\n\n        L2CAP_Command(handle, l2capoutbuf, 8);\n}\n\nvoid BTD::l2cap_information_response(uint16_t handle, uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh) {\n        l2capoutbuf[0] = L2CAP_CMD_INFORMATION_RESPONSE; // Code\n        l2capoutbuf[1] = rxid; // Identifier\n        l2capoutbuf[2] = 0x08; // Length\n        l2capoutbuf[3] = 0x00;\n        l2capoutbuf[4] = infoTypeLow;\n        l2capoutbuf[5] = infoTypeHigh;\n        l2capoutbuf[6] = 0x00; // Result = success\n        l2capoutbuf[7] = 0x00; // Result = success\n        l2capoutbuf[8] = 0x00;\n        l2capoutbuf[9] = 0x00;\n        l2capoutbuf[10] = 0x00;\n        l2capoutbuf[11] = 0x00;\n\n        L2CAP_Command(handle, l2capoutbuf, 12);\n}\n\n/* PS3 Commands - only set Bluetooth address is implemented in this library */\nvoid BTD::setBdaddr(uint8_t* bdaddr) {\n        /* Set the internal Bluetooth address */\n        uint8_t buf[8];\n        buf[0] = 0x01;\n        buf[1] = 0x00;\n\n        for(uint8_t i = 0; i < 6; i++)\n                buf[i + 2] = bdaddr[5 - i]; // Copy into buffer, has to be written reversed, so it is MSB first\n\n        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data\n        pUsb->ctrlReq(bAddress, epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL);\n}\n\nvoid BTD::setMoveBdaddr(uint8_t* bdaddr) {\n        /* Set the internal Bluetooth address */\n        uint8_t buf[11];\n        buf[0] = 0x05;\n        buf[7] = 0x10;\n        buf[8] = 0x01;\n        buf[9] = 0x02;\n        buf[10] = 0x12;\n\n        for(uint8_t i = 0; i < 6; i++)\n                buf[i + 1] = bdaddr[i];\n\n        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data\n        pUsb->ctrlReq(bAddress, epInfo[BTD_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00, 11, 11, buf, NULL);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/BTD.h",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _btd_h_\n#define _btd_h_\n\n#include \"Usb.h\"\n#include \"hid.h\"\n\n//PID and VID of the Sony PS3 devices\n#define PS3_VID                 0x054C  // Sony Corporation\n#define PS3_PID                 0x0268  // PS3 Controller DualShock 3\n#define PS3NAVIGATION_PID       0x042F  // Navigation controller\n#define PS3MOVE_PID             0x03D5  // Motion controller\n\n#define IOGEAR_GBU521_VID       0x0A5C // The IOGEAR GBU521 dongle does not presents itself correctly, so we have to check for it manually\n#define IOGEAR_GBU521_PID       0x21E8\n\n/* Bluetooth dongle data taken from descriptors */\n#define BULK_MAXPKTSIZE         64 // Max size for ACL data\n\n// Used in control endpoint header for HCI Commands\n#define bmREQ_HCI_OUT USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE\n\n/* Bluetooth HCI states for hci_task() */\n#define HCI_INIT_STATE                  0\n#define HCI_RESET_STATE                 1\n#define HCI_CLASS_STATE                 2\n#define HCI_BDADDR_STATE                3\n#define HCI_LOCAL_VERSION_STATE         4\n#define HCI_SET_NAME_STATE              5\n#define HCI_CHECK_DEVICE_SERVICE        6\n\n#define HCI_INQUIRY_STATE               7 // These three states are only used if it should pair and connect to a device\n#define HCI_CONNECT_DEVICE_STATE        8\n#define HCI_CONNECTED_DEVICE_STATE      9\n\n#define HCI_SCANNING_STATE              10\n#define HCI_CONNECT_IN_STATE            11\n#define HCI_REMOTE_NAME_STATE           12\n#define HCI_CONNECTED_STATE             13\n#define HCI_DISABLE_SCAN_STATE          14\n#define HCI_DONE_STATE                  15\n#define HCI_DISCONNECT_STATE            16\n\n/* HCI event flags*/\n#define HCI_FLAG_CMD_COMPLETE           (1UL << 0)\n#define HCI_FLAG_CONNECT_COMPLETE       (1UL << 1)\n#define HCI_FLAG_DISCONNECT_COMPLETE    (1UL << 2)\n#define HCI_FLAG_REMOTE_NAME_COMPLETE   (1UL << 3)\n#define HCI_FLAG_INCOMING_REQUEST       (1UL << 4)\n#define HCI_FLAG_READ_BDADDR            (1UL << 5)\n#define HCI_FLAG_READ_VERSION           (1UL << 6)\n#define HCI_FLAG_DEVICE_FOUND           (1UL << 7)\n#define HCI_FLAG_CONNECT_EVENT          (1UL << 8)\n\n/* Macros for HCI event flag tests */\n#define hci_check_flag(flag) (hci_event_flag & (flag))\n#define hci_set_flag(flag) (hci_event_flag |= (flag))\n#define hci_clear_flag(flag) (hci_event_flag &= ~(flag))\n\n/* HCI Events managed */\n#define EV_INQUIRY_COMPLETE                             0x01\n#define EV_INQUIRY_RESULT                               0x02\n#define EV_CONNECT_COMPLETE                             0x03\n#define EV_INCOMING_CONNECT                             0x04\n#define EV_DISCONNECT_COMPLETE                          0x05\n#define EV_AUTHENTICATION_COMPLETE                      0x06\n#define EV_REMOTE_NAME_COMPLETE                         0x07\n#define EV_ENCRYPTION_CHANGE                            0x08\n#define EV_CHANGE_CONNECTION_LINK                       0x09\n#define EV_ROLE_CHANGED                                 0x12\n#define EV_NUM_COMPLETE_PKT                             0x13\n#define EV_PIN_CODE_REQUEST                             0x16\n#define EV_LINK_KEY_REQUEST                             0x17\n#define EV_LINK_KEY_NOTIFICATION                        0x18\n#define EV_DATA_BUFFER_OVERFLOW                         0x1A\n#define EV_MAX_SLOTS_CHANGE                             0x1B\n#define EV_READ_REMOTE_VERSION_INFORMATION_COMPLETE     0x0C\n#define EV_QOS_SETUP_COMPLETE                           0x0D\n#define EV_COMMAND_COMPLETE                             0x0E\n#define EV_COMMAND_STATUS                               0x0F\n#define EV_LOOPBACK_COMMAND                             0x19\n#define EV_PAGE_SCAN_REP_MODE                           0x20\n\n/* Bluetooth states for the different Bluetooth drivers */\n#define L2CAP_WAIT                      0\n#define L2CAP_DONE                      1\n\n/* Used for HID Control channel */\n#define L2CAP_CONTROL_CONNECT_REQUEST   2\n#define L2CAP_CONTROL_CONFIG_REQUEST    3\n#define L2CAP_CONTROL_SUCCESS           4\n#define L2CAP_CONTROL_DISCONNECT        5\n\n/* Used for HID Interrupt channel */\n#define L2CAP_INTERRUPT_SETUP           6\n#define L2CAP_INTERRUPT_CONNECT_REQUEST 7\n#define L2CAP_INTERRUPT_CONFIG_REQUEST  8\n#define L2CAP_INTERRUPT_DISCONNECT      9\n\n/* Used for SDP channel */\n#define L2CAP_SDP_WAIT                  10\n#define L2CAP_SDP_SUCCESS               11\n\n/* Used for RFCOMM channel */\n#define L2CAP_RFCOMM_WAIT               12\n#define L2CAP_RFCOMM_SUCCESS            13\n\n#define L2CAP_DISCONNECT_RESPONSE       14 // Used for both SDP and RFCOMM channel\n\n/* Bluetooth states used by some drivers */\n#define TURN_ON_LED                     17\n#define PS3_ENABLE_SIXAXIS              18\n#define WII_CHECK_MOTION_PLUS_STATE     19\n#define WII_CHECK_EXTENSION_STATE       20\n#define WII_INIT_MOTION_PLUS_STATE      21\n\n/* L2CAP event flags for HID Control channel */\n#define L2CAP_FLAG_CONNECTION_CONTROL_REQUEST           (1UL << 0)\n#define L2CAP_FLAG_CONFIG_CONTROL_SUCCESS               (1UL << 1)\n#define L2CAP_FLAG_CONTROL_CONNECTED                    (1UL << 2)\n#define L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE          (1UL << 3)\n\n/* L2CAP event flags for HID Interrupt channel */\n#define L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST         (1UL << 4)\n#define L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS             (1UL << 5)\n#define L2CAP_FLAG_INTERRUPT_CONNECTED                  (1UL << 6)\n#define L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE        (1UL << 7)\n\n/* L2CAP event flags for SDP channel */\n#define L2CAP_FLAG_CONNECTION_SDP_REQUEST               (1UL << 8)\n#define L2CAP_FLAG_CONFIG_SDP_SUCCESS                   (1UL << 9)\n#define L2CAP_FLAG_DISCONNECT_SDP_REQUEST               (1UL << 10)\n\n/* L2CAP event flags for RFCOMM channel */\n#define L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST            (1UL << 11)\n#define L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS                (1UL << 12)\n#define L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST            (1UL << 13)\n\n#define L2CAP_FLAG_DISCONNECT_RESPONSE                  (1UL << 14)\n\n/* Macros for L2CAP event flag tests */\n#define l2cap_check_flag(flag) (l2cap_event_flag & (flag))\n#define l2cap_set_flag(flag) (l2cap_event_flag |= (flag))\n#define l2cap_clear_flag(flag) (l2cap_event_flag &= ~(flag))\n\n/* L2CAP signaling commands */\n#define L2CAP_CMD_COMMAND_REJECT        0x01\n#define L2CAP_CMD_CONNECTION_REQUEST    0x02\n#define L2CAP_CMD_CONNECTION_RESPONSE   0x03\n#define L2CAP_CMD_CONFIG_REQUEST        0x04\n#define L2CAP_CMD_CONFIG_RESPONSE       0x05\n#define L2CAP_CMD_DISCONNECT_REQUEST    0x06\n#define L2CAP_CMD_DISCONNECT_RESPONSE   0x07\n#define L2CAP_CMD_INFORMATION_REQUEST   0x0A\n#define L2CAP_CMD_INFORMATION_RESPONSE  0x0B\n\n// Used For Connection Response - Remember to Include High Byte\n#define PENDING     0x01\n#define SUCCESSFUL  0x00\n\n/* Bluetooth L2CAP PSM - see http://www.bluetooth.org/Technical/AssignedNumbers/logical_link.htm */\n#define SDP_PSM         0x01 // Service Discovery Protocol PSM Value\n#define RFCOMM_PSM      0x03 // RFCOMM PSM Value\n#define HID_CTRL_PSM    0x11 // HID_Control PSM Value\n#define HID_INTR_PSM    0x13 // HID_Interrupt PSM Value\n\n// Used to determine if it is a Bluetooth dongle\n#define WI_SUBCLASS_RF      0x01 // RF Controller\n#define WI_PROTOCOL_BT      0x01 // Bluetooth Programming Interface\n\n#define BTD_MAX_ENDPOINTS   4\n#define BTD_NUM_SERVICES    4 // Max number of Bluetooth services - if you need more than 4 simply increase this number\n\n#define PAIR    1\n\nclass BluetoothService;\n\n/**\n * The Bluetooth Dongle class will take care of all the USB communication\n * and then pass the data to the BluetoothService classes.\n */\nclass BTD : public USBDeviceConfig, public UsbConfigXtracter {\npublic:\n        /**\n         * Constructor for the BTD class.\n         * @param  p   Pointer to USB class instance.\n         */\n        BTD(USB *p);\n\n        /** @name USBDeviceConfig implementation */\n        /**\n         * Address assignment and basic initialization is done here.\n         * @param  parent   Hub number.\n         * @param  port     Port number on the hub.\n         * @param  lowspeed Speed of the device.\n         * @return          0 on success.\n         */\n        uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);\n        /**\n         * Initialize the Bluetooth dongle.\n         * @param  parent   Hub number.\n         * @param  port     Port number on the hub.\n         * @param  lowspeed Speed of the device.\n         * @return          0 on success.\n         */\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        /**\n         * Release the USB device.\n         * @return 0 on success.\n         */\n        uint8_t Release();\n        /**\n         * Poll the USB Input endpoints and run the state machines.\n         * @return 0 on success.\n         */\n        uint8_t Poll();\n\n        /**\n         * Get the device address.\n         * @return The device address.\n         */\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        /**\n         * Used to check if the dongle has been initialized.\n         * @return True if it's ready.\n         */\n        virtual bool isReady() {\n                return bPollEnable;\n        };\n\n        /**\n         * Used by the USB core to check what this driver support.\n         * @param  klass The device's USB class.\n         * @return       Returns true if the device's USB class matches this driver.\n         */\n        virtual bool DEVCLASSOK(uint8_t klass) {\n                return (klass == USB_CLASS_WIRELESS_CTRL);\n        };\n\n        /**\n         * Used by the USB core to check what this driver support.\n         * Used to set the Bluetooth address into the PS3 controllers.\n         * @param  vid The device's VID.\n         * @param  pid The device's PID.\n         * @return     Returns true if the device's VID and PID matches this driver.\n         */\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                if(vid == IOGEAR_GBU521_VID && pid == IOGEAR_GBU521_PID)\n                        return true;\n                if(my_bdaddr[0] != 0x00 || my_bdaddr[1] != 0x00 || my_bdaddr[2] != 0x00 || my_bdaddr[3] != 0x00 || my_bdaddr[4] != 0x00 || my_bdaddr[5] != 0x00) { // Check if Bluetooth address is set\n                        if(vid == PS3_VID && (pid == PS3_PID || pid == PS3NAVIGATION_PID || pid == PS3MOVE_PID))\n                                return true;\n                }\n                return false;\n        };\n        /**@}*/\n\n        /** @name UsbConfigXtracter implementation */\n        /**\n         * UsbConfigXtracter implementation, used to extract endpoint information.\n         * @param conf  Configuration value.\n         * @param iface Interface number.\n         * @param alt   Alternate setting.\n         * @param proto Interface Protocol.\n         * @param ep    Endpoint Descriptor.\n         */\n        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);\n        /**@}*/\n\n        /** Disconnects both the L2CAP Channel and the HCI Connection for all Bluetooth services. */\n        void disconnect();\n\n        /**\n         * Register Bluetooth dongle members/services.\n         * @param  pService Pointer to BluetoothService class instance.\n         * @return          The service ID on success or -1 on fail.\n         */\n        int8_t registerBluetoothService(BluetoothService *pService) {\n                for(uint8_t i = 0; i < BTD_NUM_SERVICES; i++) {\n                        if(!btService[i]) {\n                                btService[i] = pService;\n                                return i; // Return ID\n                        }\n                }\n                return -1; // Error registering BluetoothService\n        };\n\n        /** @name HCI Commands */\n        /**\n         * Used to send a HCI Command.\n         * @param data   Data to send.\n         * @param nbytes Number of bytes to send.\n         */\n        void HCI_Command(uint8_t* data, uint16_t nbytes);\n        /** Reset the Bluetooth dongle. */\n        void hci_reset();\n        /** Read the Bluetooth address of the dongle. */\n        void hci_read_bdaddr();\n        /** Read the HCI Version of the Bluetooth dongle. */\n        void hci_read_local_version_information();\n        /**\n         * Set the local name of the Bluetooth dongle.\n         * @param name Desired name.\n         */\n        void hci_set_local_name(const char* name);\n        /** Enable visibility to other Bluetooth devices. */\n        void hci_write_scan_enable();\n        /** Disable visibility to other Bluetooth devices. */\n        void hci_write_scan_disable();\n        /** Read the remote devices name. */\n        void hci_remote_name();\n        /** Accept the connection with the Bluetooth device. */\n        void hci_accept_connection();\n        /**\n         * Disconnect the HCI connection.\n         * @param handle The HCI Handle for the connection.\n         */\n        void hci_disconnect(uint16_t handle);\n        /**\n         * Respond with the pin for the connection.\n         * The pin is automatically set for the Wii library,\n         * but can be customized for the SPP library.\n         */\n        void hci_pin_code_request_reply();\n        /** Respons when no pin was set. */\n        void hci_pin_code_negative_request_reply();\n        /**\n         * Command is used to reply to a Link Key Request event from the BR/EDR Controller\n         * if the Host does not have a stored Link Key for the connection.\n         */\n        void hci_link_key_request_negative_reply();\n        /** Used to try to authenticate with the remote device. */\n        void hci_authentication_request();\n        /** Start a HCI inquiry. */\n        void hci_inquiry();\n        /** Cancel a HCI inquiry. */\n        void hci_inquiry_cancel();\n        /** Connect to last device communicated with. */\n        void hci_connect();\n        /**\n         * Connect to device.\n         * @param bdaddr Bluetooth address of the device.\n         */\n        void hci_connect(uint8_t *bdaddr);\n        /** Used to a set the class of the device. */\n        void hci_write_class_of_device();\n        /**@}*/\n\n        /** @name L2CAP Commands */\n        /**\n         * Used to send L2CAP Commands.\n         * @param handle      HCI Handle.\n         * @param data        Data to send.\n         * @param nbytes      Number of bytes to send.\n         * @param channelLow,channelHigh  Low and high byte of channel to send to.\n         * If argument is omitted then the Standard L2CAP header: Channel ID (0x01) for ACL-U will be used.\n         */\n        void L2CAP_Command(uint16_t handle, uint8_t* data, uint8_t nbytes, uint8_t channelLow = 0x01, uint8_t channelHigh = 0x00);\n        /**\n         * L2CAP Connection Request.\n         * @param handle HCI handle.\n         * @param rxid   Identifier.\n         * @param scid   Source Channel Identifier.\n         * @param psm    Protocol/Service Multiplexer - see: https://www.bluetooth.org/Technical/AssignedNumbers/logical_link.htm.\n         */\n        void l2cap_connection_request(uint16_t handle, uint8_t rxid, uint8_t* scid, uint16_t psm);\n        /**\n         * L2CAP Connection Response.\n         * @param handle HCI handle.\n         * @param rxid   Identifier.\n         * @param dcid   Destination Channel Identifier.\n         * @param scid   Source Channel Identifier.\n         * @param result Result - First send ::PENDING and then ::SUCCESSFUL.\n         */\n        void l2cap_connection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid, uint8_t result);\n        /**\n         * L2CAP Config Request.\n         * @param handle HCI Handle.\n         * @param rxid   Identifier.\n         * @param dcid   Destination Channel Identifier.\n         */\n        void l2cap_config_request(uint16_t handle, uint8_t rxid, uint8_t* dcid);\n        /**\n         * L2CAP Config Response.\n         * @param handle HCI Handle.\n         * @param rxid   Identifier.\n         * @param scid   Source Channel Identifier.\n         */\n        void l2cap_config_response(uint16_t handle, uint8_t rxid, uint8_t* scid);\n        /**\n         * L2CAP Disconnection Request.\n         * @param handle HCI Handle.\n         * @param rxid   Identifier.\n         * @param dcid   Device Channel Identifier.\n         * @param scid   Source Channel Identifier.\n         */\n        void l2cap_disconnection_request(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid);\n        /**\n         * L2CAP Disconnection Response.\n         * @param handle HCI Handle.\n         * @param rxid   Identifier.\n         * @param dcid   Device Channel Identifier.\n         * @param scid   Source Channel Identifier.\n         */\n        void l2cap_disconnection_response(uint16_t handle, uint8_t rxid, uint8_t* dcid, uint8_t* scid);\n        /**\n         * L2CAP Information Response.\n         * @param handle       HCI Handle.\n         * @param rxid         Identifier.\n         * @param infoTypeLow,infoTypeHigh  Infotype.\n         */\n        void l2cap_information_response(uint16_t handle, uint8_t rxid, uint8_t infoTypeLow, uint8_t infoTypeHigh);\n        /**@}*/\n\n        /** Use this to see if it is waiting for a incoming connection. */\n        bool watingForConnection;\n        /** This is used by the service to know when to store the device information. */\n        bool l2capConnectionClaimed;\n        /** This is used by the SPP library to claim the current SDP incoming request. */\n        bool sdpConnectionClaimed;\n        /** This is used by the SPP library to claim the current RFCOMM incoming request. */\n        bool rfcommConnectionClaimed;\n\n        /** The name you wish to make the dongle show up as. It is set automatically by the SPP library. */\n        const char* btdName;\n        /** The pin you wish to make the dongle use for authentication. It is set automatically by the SPP and BTHID library. */\n        const char* btdPin;\n\n        /** The bluetooth dongles Bluetooth address. */\n        uint8_t my_bdaddr[6];\n        /** HCI handle for the last connection. */\n        uint16_t hci_handle;\n        /** Last incoming devices Bluetooth address. */\n        uint8_t disc_bdaddr[6];\n        /** First 30 chars of last remote name. */\n        char remote_name[30];\n        /**\n         * The supported HCI Version read from the Bluetooth dongle.\n         * Used by the PS3BT library to check the HCI Version of the Bluetooth dongle,\n         * it should be at least 3 to work properly with the library.\n         */\n        uint8_t hci_version;\n\n        /** Call this function to pair with a Wiimote */\n        void pairWithWiimote() {\n                pairWithWii = true;\n                hci_state = HCI_CHECK_DEVICE_SERVICE;\n        };\n        /** Used to only send the ACL data to the Wiimote. */\n        bool connectToWii;\n        /** True if a Wiimote is connecting. */\n        bool incomingWii;\n        /** True when it should pair with a Wiimote. */\n        bool pairWithWii;\n        /** True if it's the new Wiimote with the Motion Plus Inside or a Wii U Pro Controller. */\n        bool motionPlusInside;\n        /** True if it's a Wii U Pro Controller. */\n        bool wiiUProController;\n\n        /** Call this function to pair with a Wiimote */\n        void pairWithHID() {\n                pairWithHIDDevice = true;\n                hci_state = HCI_CHECK_DEVICE_SERVICE;\n        };\n        /** Used to only send the ACL data to the Wiimote. */\n        bool connectToHIDDevice;\n        /** True if a Wiimote is connecting. */\n        bool incomingHIDDevice;\n        /** True when it should pair with a device like a mouse or keyboard. */\n        bool pairWithHIDDevice;\n\n        /**\n         * Read the poll interval taken from the endpoint descriptors.\n         * @return The poll interval in ms.\n         */\n        uint8_t readPollInterval() {\n                return pollInterval;\n        };\n\nprotected:\n        /** Pointer to USB class instance. */\n        USB *pUsb;\n        /** Device address. */\n        uint8_t bAddress;\n        /** Endpoint info structure. */\n        EpInfo epInfo[BTD_MAX_ENDPOINTS];\n\n        /** Configuration number. */\n        uint8_t bConfNum;\n        /** Total number of endpoints in the configuration. */\n        uint8_t bNumEP;\n        /** Next poll time based on poll interval taken from the USB descriptor. */\n        uint32_t qNextPollTime;\n\n        /** Bluetooth dongle control endpoint. */\n        static const uint8_t BTD_CONTROL_PIPE;\n        /** HCI event endpoint index. */\n        static const uint8_t BTD_EVENT_PIPE;\n        /** ACL In endpoint index. */\n        static const uint8_t BTD_DATAIN_PIPE;\n        /** ACL Out endpoint index. */\n        static const uint8_t BTD_DATAOUT_PIPE;\n\n        /**\n         * Used to print the USB Endpoint Descriptor.\n         * @param ep_ptr Pointer to USB Endpoint Descriptor.\n         */\n        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);\n\nprivate:\n        void Initialize(); // Set all variables, endpoint structs etc. to default values\n        BluetoothService *btService[BTD_NUM_SERVICES];\n\n        uint16_t PID, VID; // PID and VID of device connected\n\n        uint8_t pollInterval;\n        bool bPollEnable;\n\n        bool pairWiiUsingSync; // True if paring was done using the Wii SYNC button.\n        bool checkRemoteName; // Used to check remote device's name before connecting.\n        bool incomingPS4; // True if a PS4 controller is connecting\n        uint8_t classOfDevice[3]; // Class of device of last device\n\n        /* Variables used by high level HCI task */\n        uint8_t hci_state; // Current state of Bluetooth HCI connection\n        uint16_t hci_counter; // Counter used for Bluetooth HCI reset loops\n        uint16_t hci_num_reset_loops; // This value indicate how many times it should read before trying to reset\n        uint16_t hci_event_flag; // HCI flags of received Bluetooth events\n        uint8_t inquiry_counter;\n\n        uint8_t hcibuf[BULK_MAXPKTSIZE]; // General purpose buffer for HCI data\n        uint8_t l2capinbuf[BULK_MAXPKTSIZE]; // General purpose buffer for L2CAP in data\n        uint8_t l2capoutbuf[14]; // General purpose buffer for L2CAP out data\n\n        /* State machines */\n        void HCI_event_task(); // Poll the HCI event pipe\n        void HCI_task(); // HCI state machine\n        void ACL_event_task(); // ACL input pipe\n\n        /* Used to set the Bluetooth Address internally to the PS3 Controllers */\n        void setBdaddr(uint8_t* BDADDR);\n        void setMoveBdaddr(uint8_t* BDADDR);\n};\n\n/** All Bluetooth services should inherit this class. */\nclass BluetoothService {\npublic:\n        BluetoothService(BTD *p) : pBtd(p) {\n                if(pBtd)\n                        pBtd->registerBluetoothService(this); // Register it as a Bluetooth service\n        };\n        /**\n         * Used to pass acldata to the Bluetooth service.\n         * @param ACLData Pointer to the incoming acldata.\n         */\n        virtual void ACLData(uint8_t* ACLData) = 0;\n        /** Used to run the different state machines in the Bluetooth service. */\n        virtual void Run() = 0;\n        /** Used to reset the Bluetooth service. */\n        virtual void Reset() = 0;\n        /** Used to disconnect both the L2CAP Channel and the HCI Connection for the Bluetooth service. */\n        virtual void disconnect() = 0;\n\n        /**\n         * Used to call your own function when the device is successfully initialized.\n         * @param funcOnInit Function to call.\n         */\n        void attachOnInit(void (*funcOnInit)(void)) {\n                pFuncOnInit = funcOnInit; // TODO: This really belong in a class of it's own as it is repeated several times\n        };\n\nprotected:\n        /**\n         * Called when a device is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         */\n        virtual void onInit() = 0;\n\n        /** Used to check if the incoming L2CAP data matches the HCI Handle */\n        bool checkHciHandle(uint8_t *buf, uint16_t handle) {\n                return (buf[0] == (handle & 0xFF)) && (buf[1] == ((handle >> 8) | 0x20));\n        }\n\n        /** Pointer to function called in onInit(). */\n        void (*pFuncOnInit)(void);\n\n        /** Pointer to BTD instance. */\n        BTD *pBtd;\n\n        /** The HCI Handle for the connection. */\n        uint16_t hci_handle;\n\n        /** L2CAP flags of received Bluetooth events. */\n        uint32_t l2cap_event_flag;\n\n        /** Identifier for L2CAP commands. */\n        uint8_t identifier;\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/BTHID.cpp",
    "content": "/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#include \"BTHID.h\"\n// To enable serial debugging see \"settings.h\"\n//#define EXTRADEBUG // Uncomment to get even more debugging data\n//#define PRINTREPORT // Uncomment to print the report send by the HID device\n\nBTHID::BTHID(BTD *p, bool pair, const char *pin) :\nBluetoothService(p), // Pointer to USB class instance - mandatory\nprotocolMode(HID_BOOT_PROTOCOL) {\n        for(uint8_t i = 0; i < NUM_PARSERS; i++)\n                pRptParser[i] = NULL;\n\n        pBtd->pairWithHIDDevice = pair;\n        pBtd->btdPin = pin;\n\n        /* Set device cid for the control and intterrupt channelse - LSB */\n        control_dcid[0] = 0x70; // 0x0070\n        control_dcid[1] = 0x00;\n        interrupt_dcid[0] = 0x71; // 0x0071\n        interrupt_dcid[1] = 0x00;\n\n        Reset();\n}\n\nvoid BTHID::Reset() {\n        connected = false;\n        activeConnection = false;\n        l2cap_event_flag = 0; // Reset flags\n        l2cap_state = L2CAP_WAIT;\n        ResetBTHID();\n}\n\nvoid BTHID::disconnect() { // Use this void to disconnect the device\n        // First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection\n        pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid);\n        Reset();\n        l2cap_state = L2CAP_INTERRUPT_DISCONNECT;\n}\n\nvoid BTHID::ACLData(uint8_t* l2capinbuf) {\n        if(!pBtd->l2capConnectionClaimed && pBtd->incomingHIDDevice && !connected && !activeConnection) {\n                if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {\n                        if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {\n                                pBtd->incomingHIDDevice = false;\n                                pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service\n                                activeConnection = true;\n                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection\n                                l2cap_state = L2CAP_WAIT;\n                        }\n                }\n        }\n\n        if(checkHciHandle(l2capinbuf, hci_handle)) { // acl_handle_ok\n                if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U\n                        if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nL2CAP Command Rejected - Reason: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);\n#endif\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) {\n                                if(((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) == SUCCESSFUL)) { // Success\n                                        if(l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nHID Control Connection Complete\"), 0x80);\n                                                identifier = l2capinbuf[9];\n                                                control_scid[0] = l2capinbuf[12];\n                                                control_scid[1] = l2capinbuf[13];\n                                                l2cap_set_flag(L2CAP_FLAG_CONTROL_CONNECTED);\n                                        } else if(l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nHID Interrupt Connection Complete\"), 0x80);\n                                                identifier = l2capinbuf[9];\n                                                interrupt_scid[0] = l2capinbuf[12];\n                                                interrupt_scid[1] = l2capinbuf[13];\n                                                l2cap_set_flag(L2CAP_FLAG_INTERRUPT_CONNECTED);\n                                        }\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {\n#ifdef EXTRADEBUG\n                                Notify(PSTR(\"\\r\\nL2CAP Connection Request - PSM: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);\n                                Notify(PSTR(\" SCID: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);\n                                Notify(PSTR(\" Identifier: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);\n#endif\n                                if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {\n                                        identifier = l2capinbuf[9];\n                                        control_scid[0] = l2capinbuf[14];\n                                        control_scid[1] = l2capinbuf[15];\n                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST);\n                                } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) {\n                                        identifier = l2capinbuf[9];\n                                        interrupt_scid[0] = l2capinbuf[14];\n                                        interrupt_scid[1] = l2capinbuf[15];\n                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST);\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {\n                                if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success\n                                        if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nHID Control Configuration Complete\"), 0x80);\n                                                identifier = l2capinbuf[9];\n                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS);\n                                        } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nHID Interrupt Configuration Complete\"), 0x80);\n                                                identifier = l2capinbuf[9];\n                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS);\n                                        }\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {\n                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {\n                                        //Notify(PSTR(\"\\r\\nHID Control Configuration Request\"), 0x80);\n                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid);\n                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {\n                                        //Notify(PSTR(\"\\r\\nHID Interrupt Configuration Request\"), 0x80);\n                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid);\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {\n                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nDisconnect Request: Control Channel\"), 0x80);\n#endif\n                                        identifier = l2capinbuf[9];\n                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid);\n                                        Reset();\n                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nDisconnect Request: Interrupt Channel\"), 0x80);\n#endif\n                                        identifier = l2capinbuf[9];\n                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid);\n                                        Reset();\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {\n                                if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {\n                                        //Notify(PSTR(\"\\r\\nDisconnect Response: Control Channel\"), 0x80);\n                                        identifier = l2capinbuf[9];\n                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE);\n                                } else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) {\n                                        //Notify(PSTR(\"\\r\\nDisconnect Response: Interrupt Channel\"), 0x80);\n                                        identifier = l2capinbuf[9];\n                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE);\n                                }\n                        }\n#ifdef EXTRADEBUG\n                        else {\n                                identifier = l2capinbuf[9];\n                                Notify(PSTR(\"\\r\\nL2CAP Unknown Signaling Command: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);\n                        }\n#endif\n                } else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt\n#ifdef PRINTREPORT\n                        Notify(PSTR(\"\\r\\nL2CAP Interrupt: \"), 0x80);\n                        for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {\n                                D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                        }\n#endif\n                        if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT\n                                uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]);\n                                ParseBTHIDData((uint8_t)(length - 1), &l2capinbuf[9]);\n\n                                switch(l2capinbuf[9]) {\n                                        case 0x01: // Keyboard or Joystick events\n                                                if(pRptParser[KEYBOARD_PARSER_ID])\n                                                        pRptParser[KEYBOARD_PARSER_ID]->Parse(reinterpret_cast<HID *>(this), 0, (uint8_t)(length - 2), &l2capinbuf[10]); // Use reinterpret_cast again to extract the instance\n                                                break;\n\n                                        case 0x02: // Mouse events\n                                                if(pRptParser[MOUSE_PARSER_ID])\n                                                        pRptParser[MOUSE_PARSER_ID]->Parse(reinterpret_cast<HID *>(this), 0, (uint8_t)(length - 2), &l2capinbuf[10]); // Use reinterpret_cast again to extract the instance\n                                                break;\n#ifdef EXTRADEBUG\n                                        default:\n                                                Notify(PSTR(\"\\r\\nUnknown Report type: \"), 0x80);\n                                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);\n                                                break;\n#endif\n                                }\n                        }\n                } else if(l2capinbuf[6] == control_dcid[0] && l2capinbuf[7] == control_dcid[1]) { // l2cap_control\n#ifdef PRINTREPORT\n                        Notify(PSTR(\"\\r\\nL2CAP Control: \"), 0x80);\n                        for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {\n                                D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                        }\n#endif\n                }\n#ifdef EXTRADEBUG\n                else {\n                        Notify(PSTR(\"\\r\\nUnsupported L2CAP Data - Channel ID: \"), 0x80);\n                        D_PrintHex<uint8_t > (l2capinbuf[7], 0x80);\n                        Notify(PSTR(\" \"), 0x80);\n                        D_PrintHex<uint8_t > (l2capinbuf[6], 0x80);\n\n                        Notify(PSTR(\"\\r\\nData: \"), 0x80);\n                        Notify(PSTR(\"\\r\\n\"), 0x80);\n                        for(uint16_t i = 0; i < ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]); i++) {\n                                D_PrintHex<uint8_t > (l2capinbuf[i + 8], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                        }\n                }\n#endif\n                L2CAP_task();\n        }\n}\n\nvoid BTHID::L2CAP_task() {\n        switch(l2cap_state) {\n                        /* These states are used if the HID device is the host */\n                case L2CAP_CONTROL_SUCCESS:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Control Successfully Configured\"), 0x80);\n#endif\n                                setProtocol(); // Set protocol before establishing HID interrupt channel\n                                l2cap_state = L2CAP_INTERRUPT_SETUP;\n                        }\n                        break;\n\n                case L2CAP_INTERRUPT_SETUP:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Interrupt Incoming Connection Request\"), 0x80);\n#endif\n                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING);\n                                delay(1);\n                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL);\n                                identifier++;\n                                delay(1);\n                                pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);\n\n                                l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;\n                        }\n                        break;\n\n                        /* These states are used if the Arduino is the host */\n                case L2CAP_CONTROL_CONNECT_REQUEST:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONTROL_CONNECTED)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nSend HID Control Config Request\"), 0x80);\n#endif\n                                identifier++;\n                                pBtd->l2cap_config_request(hci_handle, identifier, control_scid);\n                                l2cap_state = L2CAP_CONTROL_CONFIG_REQUEST;\n                        }\n                        break;\n\n                case L2CAP_CONTROL_CONFIG_REQUEST:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {\n                                setProtocol(); // Set protocol before establishing HID interrupt channel\n                                delay(1); // Short delay between commands - just to be sure\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nSend HID Interrupt Connection Request\"), 0x80);\n#endif\n                                identifier++;\n                                pBtd->l2cap_connection_request(hci_handle, identifier, interrupt_dcid, HID_INTR_PSM);\n                                l2cap_state = L2CAP_INTERRUPT_CONNECT_REQUEST;\n                        }\n                        break;\n\n                case L2CAP_INTERRUPT_CONNECT_REQUEST:\n                        if(l2cap_check_flag(L2CAP_FLAG_INTERRUPT_CONNECTED)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nSend HID Interrupt Config Request\"), 0x80);\n#endif\n                                identifier++;\n                                pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);\n                                l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;\n                        }\n                        break;\n\n                case L2CAP_INTERRUPT_CONFIG_REQUEST:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)) { // Now the HID channels is established\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Channels Established\"), 0x80);\n#endif\n                                pBtd->connectToHIDDevice = false;\n                                pBtd->pairWithHIDDevice = false;\n                                connected = true;\n                                onInit();\n                                l2cap_state = L2CAP_DONE;\n                        }\n                        break;\n\n                case L2CAP_DONE:\n                        break;\n\n                case L2CAP_INTERRUPT_DISCONNECT:\n                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nDisconnected Interrupt Channel\"), 0x80);\n#endif\n                                identifier++;\n                                pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid);\n                                l2cap_state = L2CAP_CONTROL_DISCONNECT;\n                        }\n                        break;\n\n                case L2CAP_CONTROL_DISCONNECT:\n                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nDisconnected Control Channel\"), 0x80);\n#endif\n                                pBtd->hci_disconnect(hci_handle);\n                                hci_handle = -1; // Reset handle\n                                l2cap_event_flag = 0; // Reset flags\n                                l2cap_state = L2CAP_WAIT;\n                        }\n                        break;\n        }\n}\n\nvoid BTHID::Run() {\n        switch(l2cap_state) {\n                case L2CAP_WAIT:\n                        if(pBtd->connectToHIDDevice && !pBtd->l2capConnectionClaimed && !connected && !activeConnection) {\n                                pBtd->l2capConnectionClaimed = true;\n                                activeConnection = true;\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nSend HID Control Connection Request\"), 0x80);\n#endif\n                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection\n                                l2cap_event_flag = 0; // Reset flags\n                                identifier = 0;\n                                pBtd->l2cap_connection_request(hci_handle, identifier, control_dcid, HID_CTRL_PSM);\n                                l2cap_state = L2CAP_CONTROL_CONNECT_REQUEST;\n                        } else if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Control Incoming Connection Request\"), 0x80);\n#endif\n                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING);\n                                delay(1);\n                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL);\n                                identifier++;\n                                delay(1);\n                                pBtd->l2cap_config_request(hci_handle, identifier, control_scid);\n                                l2cap_state = L2CAP_CONTROL_SUCCESS;\n                        }\n                        break;\n        }\n}\n\n/************************************************************/\n/*                    HID Commands                          */\n\n/************************************************************/\nvoid BTHID::setProtocol() {\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nSet protocol mode: \"), 0x80);\n        D_PrintHex<uint8_t > (protocolMode, 0x80);\n#endif\n        if (protocolMode != HID_BOOT_PROTOCOL && protocolMode != HID_RPT_PROTOCOL) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nNot a valid protocol mode. Using Boot protocol instead.\"), 0x80);\n#endif\n                protocolMode = HID_BOOT_PROTOCOL; // Use Boot Protocol by default\n        }\n        uint8_t command = 0x70 | protocolMode; // Set Protocol, see Bluetooth HID specs page 33\n        pBtd->L2CAP_Command(hci_handle, &command, 1, control_scid[0], control_scid[1]);\n}\n\nvoid BTHID::setLeds(uint8_t data) {\n        uint8_t buf[3];\n        buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)\n        buf[1] = 0x01; // Report ID\n        buf[2] = data;\n        pBtd->L2CAP_Command(hci_handle, buf, 3, interrupt_scid[0], interrupt_scid[1]);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/BTHID.h",
    "content": "/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _bthid_h_\n#define _bthid_h_\n\n#include \"BTD.h\"\n#include \"hidboot.h\"\n\n#define KEYBOARD_PARSER_ID      0\n#define MOUSE_PARSER_ID         1\n#define NUM_PARSERS             2\n\n/** This BluetoothService class implements support for Bluetooth HID devices. */\nclass BTHID : public BluetoothService {\npublic:\n        /**\n         * Constructor for the BTHID class.\n         * @param  p   Pointer to the BTD class instance.\n         * @param  pair   Set this to true in order to pair with the device. If the argument is omitted then it will not pair with it. One can use ::PAIR to set it to true.\n         * @param  pin   Write the pin to BTD#btdPin. If argument is omitted, then \"0000\" will be used.\n         */\n        BTHID(BTD *p, bool pair = false, const char *pin = \"0000\");\n\n        /** @name BluetoothService implementation */\n        /** Used this to disconnect the devices. */\n        void disconnect();\n        /**@}*/\n\n        /**\n         * Get HIDReportParser.\n         * @param  id ID of parser.\n         * @return    Returns the corresponding HIDReportParser. Returns NULL if id is not valid.\n         */\n        HIDReportParser *GetReportParser(uint8_t id) {\n                if (id >= NUM_PARSERS)\n                        return NULL;\n                return pRptParser[id];\n        };\n\n        /**\n         * Set HIDReportParser to be used.\n         * @param  id  Id of parser.\n         * @param  prs Pointer to HIDReportParser.\n         * @return     Returns true if the HIDReportParser is set. False otherwise.\n         */\n        bool SetReportParser(uint8_t id, HIDReportParser *prs) {\n                if (id >= NUM_PARSERS)\n                        return false;\n                pRptParser[id] = prs;\n                return true;\n        };\n\n        /**\n         * Set HID protocol mode.\n         * @param mode HID protocol to use. Either HID_BOOT_PROTOCOL or HID_RPT_PROTOCOL.\n         */\n        void setProtocolMode(uint8_t mode) {\n                protocolMode = mode;\n        };\n\n        /**\n         * Used to set the leds on a keyboard.\n         * @param data See KBDLEDS in hidboot.h\n         */\n        void setLeds(uint8_t data);\n\n        /** True if a device is connected */\n        bool connected;\n\n        /** Call this to start the paring sequence with a device */\n        void pair(void) {\n                if(pBtd)\n                        pBtd->pairWithHID();\n        };\n\nprotected:\n        /** @name BluetoothService implementation */\n        /**\n         * Used to pass acldata to the services.\n         * @param ACLData Incoming acldata.\n         */\n        void ACLData(uint8_t* ACLData);\n        /** Used to run part of the state machine. */\n        void Run();\n        /** Use this to reset the service. */\n        void Reset();\n        /**\n         * Called when a device is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         */\n        void onInit() {\n                if(pFuncOnInit)\n                        pFuncOnInit(); // Call the user function\n                OnInitBTHID();\n        };\n        /**@}*/\n\n        /** @name Overridable functions */\n        /**\n         * Used to parse Bluetooth HID data to any class that inherits this class.\n         * @param len The length of the incoming data.\n         * @param buf Pointer to the data buffer.\n         */\n        virtual void ParseBTHIDData(uint8_t len, uint8_t *buf) {\n                return;\n        };\n        /** Called when a device is connected */\n        virtual void OnInitBTHID() {\n                return;\n        };\n        /** Used to reset any buffers in the class that inherits this */\n        virtual void ResetBTHID() {\n                return;\n        }\n        /**@}*/\n\n        /** L2CAP source CID for HID_Control */\n        uint8_t control_scid[2];\n\n        /** L2CAP source CID for HID_Interrupt */\n        uint8_t interrupt_scid[2];\n\nprivate:\n        HIDReportParser *pRptParser[NUM_PARSERS]; // Pointer to HIDReportParsers.\n\n        /** Set report protocol. */\n        void setProtocol();\n        uint8_t protocolMode;\n\n        void L2CAP_task(); // L2CAP state machine\n\n        bool activeConnection; // Used to indicate if it already has established a connection\n\n        /* Variables used for L2CAP communication */\n        uint8_t control_dcid[2]; // L2CAP device CID for HID_Control - Always 0x0070\n        uint8_t interrupt_dcid[2]; // L2CAP device CID for HID_Interrupt - Always 0x0071\n        uint8_t l2cap_state;\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/PS3BT.cpp",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#include \"PS3BT.h\"\n// To enable serial debugging see \"settings.h\"\n//#define EXTRADEBUG // Uncomment to get even more debugging data\n//#define PRINTREPORT // Uncomment to print the report send by the PS3 Controllers\n\nPS3BT::PS3BT(BTD *p, uint8_t btadr5, uint8_t btadr4, uint8_t btadr3, uint8_t btadr2, uint8_t btadr1, uint8_t btadr0) :\nBluetoothService(p) // Pointer to USB class instance - mandatory\n{\n        pBtd->my_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead\n        pBtd->my_bdaddr[4] = btadr4;\n        pBtd->my_bdaddr[3] = btadr3;\n        pBtd->my_bdaddr[2] = btadr2;\n        pBtd->my_bdaddr[1] = btadr1;\n        pBtd->my_bdaddr[0] = btadr0;\n\n        HIDBuffer[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02)\n        HIDBuffer[1] = 0x01; // Report ID\n\n        // Needed for PS3 Move Controller commands to work via bluetooth\n        HIDMoveBuffer[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)\n        HIDMoveBuffer[1] = 0x02; // Report ID\n\n        /* Set device cid for the control and intterrupt channelse - LSB */\n        control_dcid[0] = 0x40; // 0x0040\n        control_dcid[1] = 0x00;\n        interrupt_dcid[0] = 0x41; // 0x0041\n        interrupt_dcid[1] = 0x00;\n\n        Reset();\n}\n\nbool PS3BT::getButtonPress(ButtonEnum b) {\n        return (ButtonState & pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]));\n}\n\nbool PS3BT::getButtonClick(ButtonEnum b) {\n        uint32_t button = pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]);\n        bool click = (ButtonClickState & button);\n        ButtonClickState &= ~button; // Clear \"click\" event\n        return click;\n}\n\nuint8_t PS3BT::getAnalogButton(ButtonEnum a) {\n        return (uint8_t)(l2capinbuf[pgm_read_byte(&PS3_ANALOG_BUTTONS[(uint8_t)a])]);\n}\n\nuint8_t PS3BT::getAnalogHat(AnalogHatEnum a) {\n        return (uint8_t)(l2capinbuf[(uint8_t)a + 15]);\n}\n\nint16_t PS3BT::getSensor(SensorEnum a) {\n        if(PS3Connected) {\n                if(a == aX || a == aY || a == aZ || a == gZ)\n                        return ((l2capinbuf[(uint16_t)a] << 8) | l2capinbuf[(uint16_t)a + 1]);\n                else\n                        return 0;\n        } else if(PS3MoveConnected) {\n                if(a == mXmove || a == mYmove) // These are all 12-bits long\n                        return (((l2capinbuf[(uint16_t)a] & 0x0F) << 8) | (l2capinbuf[(uint16_t)a + 1]));\n                else if(a == mZmove || a == tempMove) // The tempearature is also 12 bits long\n                        return ((l2capinbuf[(uint16_t)a] << 4) | ((l2capinbuf[(uint16_t)a + 1] & 0xF0) >> 4));\n                else // aXmove, aYmove, aZmove, gXmove, gYmove and gZmove\n                        return (l2capinbuf[(uint16_t)a] | (l2capinbuf[(uint16_t)a + 1] << 8));\n        } else\n                return 0;\n}\n\ndouble PS3BT::getAngle(AngleEnum a) {\n        double accXval, accYval, accZval;\n\n        if(PS3Connected) {\n                // Data for the Kionix KXPC4 used in the DualShock 3\n                const double zeroG = 511.5; // 1.65/3.3*1023 (1.65V)\n                accXval = -((double)getSensor(aX) - zeroG);\n                accYval = -((double)getSensor(aY) - zeroG);\n                accZval = -((double)getSensor(aZ) - zeroG);\n        } else if(PS3MoveConnected) {\n                // It's a Kionix KXSC4 inside the Motion controller\n                const uint16_t zeroG = 0x8000;\n                accXval = -(int16_t)(getSensor(aXmove) - zeroG);\n                accYval = (int16_t)(getSensor(aYmove) - zeroG);\n                accZval = (int16_t)(getSensor(aZmove) - zeroG);\n        } else\n                return 0;\n\n        // Convert to 360 degrees resolution\n        // atan2 outputs the value of -π to π (radians)\n        // We are then converting it to 0 to 2π and then to degrees\n        if(a == Pitch)\n                return (atan2(accYval, accZval) + PI) * RAD_TO_DEG;\n        else\n                return (atan2(accXval, accZval) + PI) * RAD_TO_DEG;\n}\n\ndouble PS3BT::get9DOFValues(SensorEnum a) { // Thanks to Manfred Piendl\n        if(!PS3MoveConnected)\n                return 0;\n        int16_t value = getSensor(a);\n        if(a == mXmove || a == mYmove || a == mZmove) {\n                if(value > 2047)\n                        value -= 0x1000;\n                return (double)value / 3.2; // unit: muT = 10^(-6) Tesla\n        } else if(a == aXmove || a == aYmove || a == aZmove) {\n                if(value < 0)\n                        value += 0x8000;\n                else\n                        value -= 0x8000;\n                return (double)value / 442.0; // unit: m/(s^2)\n        } else if(a == gXmove || a == gYmove || a == gZmove) {\n                if(value < 0)\n                        value += 0x8000;\n                else\n                        value -= 0x8000;\n                if(a == gXmove)\n                        return (double)value / 11.6; // unit: deg/s\n                else if(a == gYmove)\n                        return (double)value / 11.2; // unit: deg/s\n                else // gZmove\n                        return (double)value / 9.6; // unit: deg/s\n        } else\n                return 0;\n}\n\nString PS3BT::getTemperature() {\n        if(PS3MoveConnected) {\n                int16_t input = getSensor(tempMove);\n\n                String output = String(input / 100);\n                output += \".\";\n                if(input % 100 < 10)\n                        output += \"0\";\n                output += String(input % 100);\n\n                return output;\n        } else\n                return \"Error\";\n}\n\nbool PS3BT::getStatus(StatusEnum c) {\n        return (l2capinbuf[(uint16_t)c >> 8] == ((uint8_t)c & 0xff));\n}\n\nvoid PS3BT::printStatusString() {\n        char statusOutput[100]; // Max string length plus null character\n        if(PS3Connected || PS3NavigationConnected) {\n                strcpy_P(statusOutput, PSTR(\"ConnectionStatus: \"));\n\n                if(getStatus(Plugged)) strcat_P(statusOutput, PSTR(\"Plugged\"));\n                else if(getStatus(Unplugged)) strcat_P(statusOutput, PSTR(\"Unplugged\"));\n                else strcat_P(statusOutput, PSTR(\"Error\"));\n\n                strcat_P(statusOutput, PSTR(\" - PowerRating: \"));\n\n                if(getStatus(Charging)) strcat_P(statusOutput, PSTR(\"Charging\"));\n                else if(getStatus(NotCharging)) strcat_P(statusOutput, PSTR(\"Not Charging\"));\n                else if(getStatus(Shutdown)) strcat_P(statusOutput, PSTR(\"Shutdown\"));\n                else if(getStatus(Dying)) strcat_P(statusOutput, PSTR(\"Dying\"));\n                else if(getStatus(Low)) strcat_P(statusOutput, PSTR(\"Low\"));\n                else if(getStatus(High)) strcat_P(statusOutput, PSTR(\"High\"));\n                else if(getStatus(Full)) strcat_P(statusOutput, PSTR(\"Full\"));\n                else strcat_P(statusOutput, PSTR(\"Error\"));\n\n                strcat_P(statusOutput, PSTR(\" - WirelessStatus: \"));\n\n                if(getStatus(CableRumble)) strcat_P(statusOutput, PSTR(\"Cable - Rumble is on\"));\n                else if(getStatus(Cable)) strcat_P(statusOutput, PSTR(\"Cable - Rumble is off\"));\n                else if(getStatus(BluetoothRumble)) strcat_P(statusOutput, PSTR(\"Bluetooth - Rumble is on\"));\n                else if(getStatus(Bluetooth)) strcat_P(statusOutput, PSTR(\"Bluetooth - Rumble is off\"));\n                else strcat_P(statusOutput, PSTR(\"Error\"));\n        } else if(PS3MoveConnected) {\n                strcpy_P(statusOutput, PSTR(\"PowerRating: \"));\n\n                if(getStatus(MoveCharging)) strcat_P(statusOutput, PSTR(\"Charging\"));\n                else if(getStatus(MoveNotCharging)) strcat_P(statusOutput, PSTR(\"Not Charging\"));\n                else if(getStatus(MoveShutdown)) strcat_P(statusOutput, PSTR(\"Shutdown\"));\n                else if(getStatus(MoveDying)) strcat_P(statusOutput, PSTR(\"Dying\"));\n                else if(getStatus(MoveLow)) strcat_P(statusOutput, PSTR(\"Low\"));\n                else if(getStatus(MoveHigh)) strcat_P(statusOutput, PSTR(\"High\"));\n                else if(getStatus(MoveFull)) strcat_P(statusOutput, PSTR(\"Full\"));\n                else strcat_P(statusOutput, PSTR(\"Error\"));\n        } else\n                strcpy_P(statusOutput, PSTR(\"Error\"));\n\n        USB_HOST_SERIAL.write(statusOutput);\n}\n\nvoid PS3BT::Reset() {\n        PS3Connected = false;\n        PS3MoveConnected = false;\n        PS3NavigationConnected = false;\n        activeConnection = false;\n        l2cap_event_flag = 0; // Reset flags\n        l2cap_state = L2CAP_WAIT;\n\n        // Needed for PS3 Dualshock Controller commands to work via Bluetooth\n        for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++)\n                HIDBuffer[i + 2] = pgm_read_byte(&PS3_REPORT_BUFFER[i]); // First two bytes reserved for report type and ID\n}\n\nvoid PS3BT::disconnect() { // Use this void to disconnect any of the controllers\n        // First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection\n        pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid);\n        Reset();\n        l2cap_state = L2CAP_INTERRUPT_DISCONNECT;\n}\n\nvoid PS3BT::ACLData(uint8_t* ACLData) {\n        if(!pBtd->l2capConnectionClaimed && !PS3Connected && !PS3MoveConnected && !PS3NavigationConnected && !activeConnection && !pBtd->connectToWii && !pBtd->incomingWii && !pBtd->pairWithWii) {\n                if(ACLData[8] == L2CAP_CMD_CONNECTION_REQUEST) {\n                        if((ACLData[12] | (ACLData[13] << 8)) == HID_CTRL_PSM) {\n                                pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service\n                                activeConnection = true;\n                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection\n                                l2cap_state = L2CAP_WAIT;\n                                remote_name_first = pBtd->remote_name[0]; // Store the first letter in remote name for the connection\n#ifdef DEBUG_USB_HOST\n                                if(pBtd->hci_version < 3) { // Check the HCI Version of the Bluetooth dongle\n                                        Notify(PSTR(\"\\r\\nYour dongle may not support reading the analog buttons, sensors and status\\r\\nYour HCI Version is: \"), 0x80);\n                                        Notify(pBtd->hci_version, 0x80);\n                                        Notify(PSTR(\"\\r\\nBut should be at least 3\\r\\nThis means that it doesn't support Bluetooth Version 2.0+EDR\"), 0x80);\n                                }\n#endif\n                        }\n                }\n        }\n\n        if(checkHciHandle(ACLData, hci_handle)) { // acl_handle_ok\n                memcpy(l2capinbuf, ACLData, BULK_MAXPKTSIZE);\n                if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U\n                        if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nL2CAP Command Rejected - Reason: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);\n                                Notify(PSTR(\" Data: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);\n#endif\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {\n#ifdef EXTRADEBUG\n                                Notify(PSTR(\"\\r\\nL2CAP Connection Request - PSM: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);\n                                Notify(PSTR(\" SCID: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);\n                                Notify(PSTR(\" Identifier: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);\n#endif\n                                if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {\n                                        identifier = l2capinbuf[9];\n                                        control_scid[0] = l2capinbuf[14];\n                                        control_scid[1] = l2capinbuf[15];\n                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST);\n                                } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) {\n                                        identifier = l2capinbuf[9];\n                                        interrupt_scid[0] = l2capinbuf[14];\n                                        interrupt_scid[1] = l2capinbuf[15];\n                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST);\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {\n                                if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success\n                                        if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nHID Control Configuration Complete\"), 0x80);\n                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS);\n                                        } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nHID Interrupt Configuration Complete\"), 0x80);\n                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS);\n                                        }\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {\n                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {\n                                        //Notify(PSTR(\"\\r\\nHID Control Configuration Request\"), 0x80);\n                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid);\n                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {\n                                        //Notify(PSTR(\"\\r\\nHID Interrupt Configuration Request\"), 0x80);\n                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid);\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {\n                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nDisconnect Request: Control Channel\"), 0x80);\n#endif\n                                        identifier = l2capinbuf[9];\n                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid);\n                                        Reset();\n                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nDisconnect Request: Interrupt Channel\"), 0x80);\n#endif\n                                        identifier = l2capinbuf[9];\n                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid);\n                                        Reset();\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {\n                                if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {\n                                        //Notify(PSTR(\"\\r\\nDisconnect Response: Control Channel\"), 0x80);\n                                        identifier = l2capinbuf[9];\n                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE);\n                                } else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) {\n                                        //Notify(PSTR(\"\\r\\nDisconnect Response: Interrupt Channel\"), 0x80);\n                                        identifier = l2capinbuf[9];\n                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE);\n                                }\n                        }\n#ifdef EXTRADEBUG\n                        else {\n                                Notify(PSTR(\"\\r\\nL2CAP Unknown Signaling Command: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);\n                        }\n#endif\n                } else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt\n                        //Notify(PSTR(\"\\r\\nL2CAP Interrupt\"), 0x80);\n                        if(PS3Connected || PS3MoveConnected || PS3NavigationConnected) {\n                                /* Read Report */\n                                if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT\n                                        lastMessageTime = millis(); // Store the last message time\n\n                                        if(PS3Connected || PS3NavigationConnected)\n                                                ButtonState = (uint32_t)(l2capinbuf[11] | ((uint16_t)l2capinbuf[12] << 8) | ((uint32_t)l2capinbuf[13] << 16));\n                                        else if(PS3MoveConnected)\n                                                ButtonState = (uint32_t)(l2capinbuf[10] | ((uint16_t)l2capinbuf[11] << 8) | ((uint32_t)l2capinbuf[12] << 16));\n\n                                        //Notify(PSTR(\"\\r\\nButtonState\", 0x80);\n                                        //PrintHex<uint32_t>(ButtonState, 0x80);\n\n                                        if(ButtonState != OldButtonState) {\n                                                ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable\n                                                OldButtonState = ButtonState;\n                                        }\n\n#ifdef PRINTREPORT // Uncomment \"#define PRINTREPORT\" to print the report send by the PS3 Controllers\n                                        for(uint8_t i = 10; i < 58; i++) {\n                                                D_PrintHex<uint8_t > (l2capinbuf[i], 0x80);\n                                                Notify(PSTR(\" \"), 0x80);\n                                        }\n                                        Notify(PSTR(\"\\r\\n\"), 0x80);\n#endif\n                                }\n                        }\n                }\n                L2CAP_task();\n        }\n}\n\nvoid PS3BT::L2CAP_task() {\n        switch(l2cap_state) {\n                case L2CAP_WAIT:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Control Incoming Connection Request\"), 0x80);\n#endif\n                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING);\n                                delay(1);\n                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL);\n                                identifier++;\n                                delay(1);\n                                pBtd->l2cap_config_request(hci_handle, identifier, control_scid);\n                                l2cap_state = L2CAP_CONTROL_SUCCESS;\n                        }\n                        break;\n\n                case L2CAP_CONTROL_SUCCESS:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Control Successfully Configured\"), 0x80);\n#endif\n                                l2cap_state = L2CAP_INTERRUPT_SETUP;\n                        }\n                        break;\n\n                case L2CAP_INTERRUPT_SETUP:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Interrupt Incoming Connection Request\"), 0x80);\n#endif\n                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING);\n                                delay(1);\n                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL);\n                                identifier++;\n                                delay(1);\n                                pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);\n\n                                l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;\n                        }\n                        break;\n\n                case L2CAP_INTERRUPT_CONFIG_REQUEST:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)) { // Now the HID channels is established\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Interrupt Successfully Configured\"), 0x80);\n#endif\n                                if(remote_name_first == 'M') { // First letter in Motion Controller ('M')\n                                        memset(l2capinbuf, 0, BULK_MAXPKTSIZE); // Reset l2cap in buffer as it sometimes read it as a button has been pressed\n                                        l2cap_state = TURN_ON_LED;\n                                } else\n                                        l2cap_state = PS3_ENABLE_SIXAXIS;\n                                timer = millis();\n                        }\n                        break;\n\n                        /* These states are handled in Run() */\n\n                case L2CAP_INTERRUPT_DISCONNECT:\n                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nDisconnected Interrupt Channel\"), 0x80);\n#endif\n                                identifier++;\n                                pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid);\n                                l2cap_state = L2CAP_CONTROL_DISCONNECT;\n                        }\n                        break;\n\n                case L2CAP_CONTROL_DISCONNECT:\n                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nDisconnected Control Channel\"), 0x80);\n#endif\n                                pBtd->hci_disconnect(hci_handle);\n                                hci_handle = -1; // Reset handle\n                                l2cap_event_flag = 0; // Reset flags\n                                l2cap_state = L2CAP_WAIT;\n                        }\n                        break;\n        }\n}\n\nvoid PS3BT::Run() {\n        switch(l2cap_state) {\n                case PS3_ENABLE_SIXAXIS:\n                        if(millis() - timer > 1000) { // loop 1 second before sending the command\n                                memset(l2capinbuf, 0, BULK_MAXPKTSIZE); // Reset l2cap in buffer as it sometimes read it as a button has been pressed\n                                for(uint8_t i = 15; i < 19; i++)\n                                        l2capinbuf[i] = 0x7F; // Set the analog joystick values to center position\n                                enable_sixaxis();\n                                l2cap_state = TURN_ON_LED;\n                                timer = millis();\n                        }\n                        break;\n\n                case TURN_ON_LED:\n                        if(millis() - timer > 1000) { // loop 1 second before sending the command\n                                if(remote_name_first == 'P') { // First letter in PLAYSTATION(R)3 Controller ('P')\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nDualshock 3 Controller Enabled\\r\\n\"), 0x80);\n#endif\n                                        PS3Connected = true;\n                                } else if(remote_name_first == 'N') { // First letter in Navigation Controller ('N')\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nNavigation Controller Enabled\\r\\n\"), 0x80);\n#endif\n                                        PS3NavigationConnected = true;\n                                } else if(remote_name_first == 'M') { // First letter in Motion Controller ('M')\n                                        timer = millis();\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nMotion Controller Enabled\\r\\n\"), 0x80);\n#endif\n                                        PS3MoveConnected = true;\n                                }\n                                ButtonState = 0; // Clear all values\n                                OldButtonState = 0;\n                                ButtonClickState = 0;\n\n                                onInit(); // Turn on the LED on the controller\n                                l2cap_state = L2CAP_DONE;\n                        }\n                        break;\n\n                case L2CAP_DONE:\n                        if(PS3MoveConnected) { // The Bulb and rumble values, has to be send at approximately every 5th second for it to stay on\n                                if(millis() - timer > 4000) { // Send at least every 4th second\n                                        HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE); // The Bulb and rumble values, has to be written again and again, for it to stay turned on\n                                        timer = millis();\n                                }\n                        }\n                        break;\n        }\n}\n\n/************************************************************/\n/*                    HID Commands                          */\n/************************************************************/\n\n// Playstation Sixaxis Dualshock and Navigation Controller commands\n\nvoid PS3BT::HID_Command(uint8_t* data, uint8_t nbytes) {\n        if(millis() - timerHID <= 150) // Check if is has been more than 150ms since last command\n                delay((uint32_t)(150 - (millis() - timerHID))); // There have to be a delay between commands\n        pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]); // Both the Navigation and Dualshock controller sends data via the control channel\n        timerHID = millis();\n}\n\nvoid PS3BT::setAllOff() {\n        HIDBuffer[3] = 0x00; // Rumble bytes\n        HIDBuffer[4] = 0x00;\n        HIDBuffer[5] = 0x00;\n        HIDBuffer[6] = 0x00;\n\n        HIDBuffer[11] = 0x00; // LED byte\n\n        HID_Command(HIDBuffer, HID_BUFFERSIZE);\n}\n\nvoid PS3BT::setRumbleOff() {\n        HIDBuffer[3] = 0x00;\n        HIDBuffer[4] = 0x00;\n        HIDBuffer[5] = 0x00;\n        HIDBuffer[6] = 0x00;\n\n        HID_Command(HIDBuffer, HID_BUFFERSIZE);\n}\n\nvoid PS3BT::setRumbleOn(RumbleEnum mode) {\n        uint8_t power[2] = {0xff, 0x00}; // Defaults to RumbleLow\n        if(mode == RumbleHigh) {\n                power[0] = 0x00;\n                power[1] = 0xff;\n        }\n        setRumbleOn(0xfe, power[0], 0xfe, power[1]);\n}\n\nvoid PS3BT::setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower) {\n        HIDBuffer[3] = rightDuration;\n        HIDBuffer[4] = rightPower;\n        HIDBuffer[5] = leftDuration;\n        HIDBuffer[6] = leftPower;\n        HID_Command(HIDBuffer, HID_BUFFERSIZE);\n}\n\nvoid PS3BT::setLedRaw(uint8_t value) {\n        HIDBuffer[11] = value << 1;\n        HID_Command(HIDBuffer, HID_BUFFERSIZE);\n}\n\nvoid PS3BT::setLedOff(LEDEnum a) {\n        HIDBuffer[11] &= ~((uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1));\n        HID_Command(HIDBuffer, HID_BUFFERSIZE);\n}\n\nvoid PS3BT::setLedOn(LEDEnum a) {\n        if(a == OFF)\n                setLedRaw(0);\n        else {\n                HIDBuffer[11] |= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);\n                HID_Command(HIDBuffer, HID_BUFFERSIZE);\n        }\n}\n\nvoid PS3BT::setLedToggle(LEDEnum a) {\n        HIDBuffer[11] ^= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);\n        HID_Command(HIDBuffer, HID_BUFFERSIZE);\n}\n\nvoid PS3BT::enable_sixaxis() { // Command used to enable the Dualshock 3 and Navigation controller to send data via Bluetooth\n        uint8_t cmd_buf[6];\n        cmd_buf[0] = 0x53; // HID BT Set_report (0x50) | Report Type (Feature 0x03)\n        cmd_buf[1] = 0xF4; // Report ID\n        cmd_buf[2] = 0x42; // Special PS3 Controller enable commands\n        cmd_buf[3] = 0x03;\n        cmd_buf[4] = 0x00;\n        cmd_buf[5] = 0x00;\n\n        HID_Command(cmd_buf, 6);\n}\n\n// Playstation Move Controller commands\n\nvoid PS3BT::HIDMove_Command(uint8_t* data, uint8_t nbytes) {\n        if(millis() - timerHID <= 150)// Check if is has been less than 150ms since last command\n                delay((uint32_t)(150 - (millis() - timerHID))); // There have to be a delay between commands\n        pBtd->L2CAP_Command(hci_handle, data, nbytes, interrupt_scid[0], interrupt_scid[1]); // The Move controller sends it's data via the intterrupt channel\n        timerHID = millis();\n}\n\nvoid PS3BT::moveSetBulb(uint8_t r, uint8_t g, uint8_t b) { // Use this to set the Color using RGB values\n        // Set the Bulb's values into the write buffer\n        HIDMoveBuffer[3] = r;\n        HIDMoveBuffer[4] = g;\n        HIDMoveBuffer[5] = b;\n\n        HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE);\n}\n\nvoid PS3BT::moveSetBulb(ColorsEnum color) { // Use this to set the Color using the predefined colors in enum\n        moveSetBulb((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color));\n}\n\nvoid PS3BT::moveSetRumble(uint8_t rumble) {\n#ifdef DEBUG_USB_HOST\n        if(rumble < 64 && rumble != 0) // The rumble value has to at least 64, or approximately 25% (64/255*100)\n                Notify(PSTR(\"\\r\\nThe rumble value has to at least 64, or approximately 25%\"), 0x80);\n#endif\n        // Set the rumble value into the write buffer\n        HIDMoveBuffer[7] = rumble;\n\n        HIDMove_Command(HIDMoveBuffer, HID_BUFFERSIZE);\n}\n\nvoid PS3BT::onInit() {\n        if(pFuncOnInit)\n                pFuncOnInit(); // Call the user function\n        else {\n                if(PS3MoveConnected)\n                        moveSetBulb(Red);\n                else // Dualshock 3 or Navigation controller\n                        setLedOn(LED1);\n        }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/PS3BT.h",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _ps3bt_h_\n#define _ps3bt_h_\n\n#include \"BTD.h\"\n#include \"PS3Enums.h\"\n\n#define HID_BUFFERSIZE 50 // Size of the buffer for the Playstation Motion Controller\n\n/**\n * This BluetoothService class implements support for all the official PS3 Controllers:\n * Dualshock 3, Navigation or a Motion controller via Bluetooth.\n *\n * Information about the protocol can be found at the wiki: https://github.com/felis/USB_Host_Shield_2.0/wiki/PS3-Information.\n */\nclass PS3BT : public BluetoothService {\npublic:\n        /**\n         * Constructor for the PS3BT class.\n         * @param  pBtd   Pointer to BTD class instance.\n         * @param  btadr5,btadr4,btadr3,btadr2,btadr1,btadr0\n         * Pass your dongles Bluetooth address into the constructor,\n         * This will set BTD#my_bdaddr, so you don't have to plug in the dongle before pairing with your controller.\n         */\n        PS3BT(BTD *pBtd, uint8_t btadr5 = 0, uint8_t btadr4 = 0, uint8_t btadr3 = 0, uint8_t btadr2 = 0, uint8_t btadr1 = 0, uint8_t btadr0 = 0);\n\n        /** @name BluetoothService implementation */\n        /** Used this to disconnect any of the controllers. */\n        void disconnect();\n        /**@}*/\n\n        /** @name PS3 Controller functions */\n        /**\n         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.\n         *\n         * While getButtonClick(ButtonEnum b) will only return it once.\n         *\n         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),\n         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).\n         * @param  b          ::ButtonEnum to read.\n         * @return            getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.\n         */\n        bool getButtonPress(ButtonEnum b);\n        bool getButtonClick(ButtonEnum b);\n        /**@}*/\n        /** @name PS3 Controller functions */\n        /**\n         * Used to get the analog value from button presses.\n         * @param  a The ::ButtonEnum to read.\n         * The supported buttons are:\n         * ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2,\n         * ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T.\n         * @return   Analog value in the range of 0-255.\n         */\n        uint8_t getAnalogButton(ButtonEnum a);\n        /**\n         * Used to read the analog joystick.\n         * @param  a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.\n         * @return   Return the analog value in the range of 0-255.\n         */\n        uint8_t getAnalogHat(AnalogHatEnum a);\n        /**\n         * Used to read the sensors inside the Dualshock 3 and Move controller.\n         * @param  a\n         * The Dualshock 3 has a 3-axis accelerometer and a 1-axis gyro inside.\n         * The Move controller has a 3-axis accelerometer, a 3-axis gyro, a 3-axis magnetometer\n         * and a temperature sensor inside.\n         * @return   Return the raw sensor value.\n         */\n        int16_t getSensor(SensorEnum a);\n        /**\n         * Use this to get ::Pitch and ::Roll calculated using the accelerometer.\n         * @param  a Either ::Pitch or ::Roll.\n         * @return   Return the angle in the range of 0-360.\n         */\n        double getAngle(AngleEnum a);\n        /**\n         * Read the sensors inside the Move controller.\n         * @param  a ::aXmove, ::aYmove, ::aZmove, ::gXmove, ::gYmove, ::gZmove, ::mXmove, ::mYmove, and ::mXmove.\n         * @return   The value in SI units.\n         */\n        double get9DOFValues(SensorEnum a);\n        /**\n         * Get the status from the controller.\n         * @param  c The ::StatusEnum you want to read.\n         * @return   True if correct and false if not.\n         */\n        bool getStatus(StatusEnum c);\n        /** Read all the available statuses from the controller and prints it as a nice formated string. */\n        void printStatusString();\n        /**\n         * Read the temperature from the Move controller.\n         * @return The temperature in degrees Celsius.\n         */\n        String getTemperature();\n\n        /** Used to set all LEDs and rumble off. */\n        void setAllOff();\n        /** Turn off rumble. */\n        void setRumbleOff();\n        /**\n         * Turn on rumble.\n         * @param mode Either ::RumbleHigh or ::RumbleLow.\n         */\n        void setRumbleOn(RumbleEnum mode);\n        /**\n         * Turn on rumble using custom duration and power.\n         * @param rightDuration The duration of the right/low rumble effect.\n         * @param rightPower The intensity of the right/low rumble effect.\n         * @param leftDuration The duration of the left/high rumble effect.\n         * @param leftPower The intensity of the left/high rumble effect.\n         */\n        void setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower);\n\n        /**\n         * Set LED value without using ::LEDEnum.\n         * @param value See: ::LEDEnum.\n         */\n        void setLedRaw(uint8_t value);\n\n        /** Turn all LEDs off. */\n        void setLedOff() {\n                setLedRaw(0);\n        };\n        /**\n         * Turn the specific LED off.\n         * @param a The ::LEDEnum to turn off.\n         */\n        void setLedOff(LEDEnum a);\n        /**\n         * Turn the specific LED on.\n         * @param a The ::LEDEnum to turn on.\n         */\n        void setLedOn(LEDEnum a);\n        /**\n         * Toggle the specific LED.\n         * @param a The ::LEDEnum to toggle.\n         */\n        void setLedToggle(LEDEnum a);\n\n        /**\n         * Use this to set the Color using RGB values.\n         * @param r,g,b RGB value.\n         */\n        void moveSetBulb(uint8_t r, uint8_t g, uint8_t b);\n        /**\n         * Use this to set the color using the predefined colors in ::ColorsEnum.\n         * @param color The desired color.\n         */\n        void moveSetBulb(ColorsEnum color);\n        /**\n         * Set the rumble value inside the Move controller.\n         * @param rumble The desired value in the range from 64-255.\n         */\n        void moveSetRumble(uint8_t rumble);\n\n        /** Used to get the millis() of the last message */\n        uint32_t getLastMessageTime() {\n                return lastMessageTime;\n        };\n        /**@}*/\n\n        /** Variable used to indicate if the normal Playstation controller is successfully connected. */\n        bool PS3Connected;\n        /** Variable used to indicate if the Move controller is successfully connected. */\n        bool PS3MoveConnected;\n        /** Variable used to indicate if the Navigation controller is successfully connected. */\n        bool PS3NavigationConnected;\n\nprotected:\n        /** @name BluetoothService implementation */\n        /**\n         * Used to pass acldata to the services.\n         * @param ACLData Incoming acldata.\n         */\n        void ACLData(uint8_t* ACLData);\n        /** Used to run part of the state machine. */\n        void Run();\n        /** Use this to reset the service. */\n        void Reset();\n        /**\n         * Called when the controller is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         */\n        void onInit();\n        /**@}*/\n\nprivate:\n\n        void L2CAP_task(); // L2CAP state machine\n\n        /* Variables filled from HCI event management */\n        char remote_name_first; // First letter in remote name\n        bool activeConnection; // Used to indicate if it's already has established a connection\n\n        /* Variables used by high level L2CAP task */\n        uint8_t l2cap_state;\n\n        uint32_t lastMessageTime; // Variable used to store the millis value of the last message.\n\n        uint32_t ButtonState;\n        uint32_t OldButtonState;\n        uint32_t ButtonClickState;\n\n        uint32_t timer; // Timer used to limit time between messages and also used to continuously set PS3 Move controller Bulb and rumble values\n        uint32_t timerHID; // Timer used see if there has to be a delay before a new HID command\n\n        uint8_t l2capinbuf[BULK_MAXPKTSIZE]; // General purpose buffer for L2CAP in data\n        uint8_t HIDBuffer[HID_BUFFERSIZE]; // Used to store HID commands\n        uint8_t HIDMoveBuffer[HID_BUFFERSIZE]; // Used to store HID commands for the Move controller\n\n        /* L2CAP Channels */\n        uint8_t control_scid[2]; // L2CAP source CID for HID_Control\n        uint8_t control_dcid[2]; // 0x0040\n        uint8_t interrupt_scid[2]; // L2CAP source CID for HID_Interrupt\n        uint8_t interrupt_dcid[2]; // 0x0041\n\n        /* HID Commands */\n        void HID_Command(uint8_t* data, uint8_t nbytes);\n        void HIDMove_Command(uint8_t* data, uint8_t nbytes);\n        void enable_sixaxis(); // Command used to enable the Dualshock 3 and Navigation controller to send data via Bluetooth\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/PS3Enums.h",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _ps3enums_h\n#define _ps3enums_h\n\n#include \"controllerEnums.h\"\n\n/** Size of the output report buffer for the Dualshock and Navigation controllers */\n#define PS3_REPORT_BUFFER_SIZE  48\n\n/** Report buffer for all PS3 commands */\nconst uint8_t PS3_REPORT_BUFFER[PS3_REPORT_BUFFER_SIZE] PROGMEM = {\n        0x00, 0x00, 0x00, 0x00, 0x00,\n        0x00, 0x00, 0x00, 0x00, 0x00,\n        0xff, 0x27, 0x10, 0x00, 0x32,\n        0xff, 0x27, 0x10, 0x00, 0x32,\n        0xff, 0x27, 0x10, 0x00, 0x32,\n        0xff, 0x27, 0x10, 0x00, 0x32,\n        0x00, 0x00, 0x00, 0x00, 0x00,\n        0x00, 0x00, 0x00, 0x00, 0x00,\n        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n};\n\n/** Size of the output report buffer for the Move Controller */\n#define MOVE_REPORT_BUFFER_SIZE 7\n\n/** Used to set the LEDs on the controllers */\nconst uint8_t PS3_LEDS[] PROGMEM = {\n        0x00, // OFF\n        0x01, // LED1\n        0x02, // LED2\n        0x04, // LED3\n        0x08, // LED4\n\n        0x09, // LED5\n        0x0A, // LED6\n        0x0C, // LED7\n        0x0D, // LED8\n        0x0E, // LED9\n        0x0F, // LED10\n};\n\n/**\n * Buttons on the controllers.\n * <B>Note:</B> that the location is shifted 9 when it's connected via USB.\n */\nconst uint32_t PS3_BUTTONS[] PROGMEM = {\n        0x10, // UP\n        0x20, // RIGHT\n        0x40, // DOWN\n        0x80, // LEFT\n\n        0x01, // SELECT\n        0x08, // START\n        0x02, // L3\n        0x04, // R3\n\n        0x0100, // L2\n        0x0200, // R2\n        0x0400, // L1\n        0x0800, // R1\n\n        0x1000, // TRIANGLE\n        0x2000, // CIRCLE\n        0x4000, // CROSS\n        0x8000, // SQUARE\n\n        0x010000, // PS\n        0x080000, // MOVE - covers 12 bits - we only need to read the top 8\n        0x100000, // T - covers 12 bits - we only need to read the top 8\n};\n\n/**\n * Analog buttons on the controllers.\n * <B>Note:</B> that the location is shifted 9 when it's connected via USB.\n */\nconst uint8_t PS3_ANALOG_BUTTONS[] PROGMEM = {\n        23, // UP_ANALOG\n        24, // RIGHT_ANALOG\n        25, // DOWN_ANALOG\n        26, // LEFT_ANALOG\n        0, 0, 0, 0, // Skip SELECT, L3, R3 and START\n\n        27, // L2_ANALOG\n        28, // R2_ANALOG\n        29, // L1_ANALOG\n        30, // R1_ANALOG\n        31, // TRIANGLE_ANALOG\n        32, // CIRCLE_ANALOG\n        33, // CROSS_ANALOG\n        34, // SQUARE_ANALOG\n        0, 0, // Skip PS and MOVE\n\n        // Playstation Move Controller\n        15, // T_ANALOG - Both at byte 14 (last reading) and byte 15 (current reading)\n};\n\nenum StatusEnum {\n        // Note that the location is shifted 9 when it's connected via USB\n        // Byte location | bit location\n        Plugged = (38 << 8) | 0x02,\n        Unplugged = (38 << 8) | 0x03,\n\n        Charging = (39 << 8) | 0xEE,\n        NotCharging = (39 << 8) | 0xF1,\n        Shutdown = (39 << 8) | 0x01,\n        Dying = (39 << 8) | 0x02,\n        Low = (39 << 8) | 0x03,\n        High = (39 << 8) | 0x04,\n        Full = (39 << 8) | 0x05,\n\n        MoveCharging = (21 << 8) | 0xEE,\n        MoveNotCharging = (21 << 8) | 0xF1,\n        MoveShutdown = (21 << 8) | 0x01,\n        MoveDying = (21 << 8) | 0x02,\n        MoveLow = (21 << 8) | 0x03,\n        MoveHigh = (21 << 8) | 0x04,\n        MoveFull = (21 << 8) | 0x05,\n\n        CableRumble = (40 << 8) | 0x10, // Operating by USB and rumble is turned on\n        Cable = (40 << 8) | 0x12, // Operating by USB and rumble is turned off\n        BluetoothRumble = (40 << 8) | 0x14, // Operating by Bluetooth and rumble is turned on\n        Bluetooth = (40 << 8) | 0x16, // Operating by Bluetooth and rumble is turned off\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/PS3USB.cpp",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#include \"PS3USB.h\"\n// To enable serial debugging see \"settings.h\"\n//#define EXTRADEBUG // Uncomment to get even more debugging data\n//#define PRINTREPORT // Uncomment to print the report send by the PS3 Controllers\n\nPS3USB::PS3USB(USB *p, uint8_t btadr5, uint8_t btadr4, uint8_t btadr3, uint8_t btadr2, uint8_t btadr1, uint8_t btadr0) :\npUsb(p), // pointer to USB class instance - mandatory\nbAddress(0), // device address - mandatory\nbPollEnable(false) // don't start polling before dongle is connected\n{\n        for(uint8_t i = 0; i < PS3_MAX_ENDPOINTS; i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;\n        }\n\n        if(pUsb) // register in USB subsystem\n                pUsb->RegisterDeviceClass(this); //set devConfig[] entry\n\n        my_bdaddr[5] = btadr5; // Change to your dongle's Bluetooth address instead\n        my_bdaddr[4] = btadr4;\n        my_bdaddr[3] = btadr3;\n        my_bdaddr[2] = btadr2;\n        my_bdaddr[1] = btadr1;\n        my_bdaddr[0] = btadr0;\n}\n\nuint8_t PS3USB::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        uint16_t PID;\n        uint16_t VID;\n\n        // get memory address of USB device address pool\n        AddressPool &addrPool = pUsb->GetAddressPool();\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nPS3USB Init\"), 0x80);\n#endif\n        // check if address has already been assigned to an instance\n        if(bAddress) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress in use\"), 0x80);\n#endif\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n        }\n\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress not found\"), 0x80);\n#endif\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        if(!p->epinfo) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nepinfo is null\"), 0x80);\n#endif\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        VID = udd->idVendor;\n        PID = udd->idProduct;\n\n        if(VID != PS3_VID || (PID != PS3_PID && PID != PS3NAVIGATION_PID && PID != PS3MOVE_PID))\n                goto FailUnknownDevice;\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nsetAddr: \"), 0x80);\n                D_PrintHex<uint8_t > (rcode, 0x80);\n#endif\n                return rcode;\n        }\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nAddr: \"), 0x80);\n        D_PrintHex<uint8_t > (bAddress, 0x80);\n#endif\n        //delay(300); // Spec says you should wait at least 200ms\n\n        p->lowspeed = false;\n\n        //get pointer to assigned address record\n        p = addrPool.GetUsbDevicePtr(bAddress);\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        // Assign epInfo to epinfo pointer - only EP0 is known\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n\n        /* The application will work in reduced host mode, so we can save program and data\n           memory space. After verifying the PID and VID we will use known values for the\n           configuration values for device, interface, endpoints and HID for the PS3 Controllers */\n\n        /* Initialize data structures for endpoints of device */\n        epInfo[ PS3_OUTPUT_PIPE ].epAddr = 0x02; // PS3 output endpoint\n        epInfo[ PS3_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ PS3_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ PS3_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ PS3_OUTPUT_PIPE ].bmSndToggle = 0;\n        epInfo[ PS3_OUTPUT_PIPE ].bmRcvToggle = 0;\n        epInfo[ PS3_INPUT_PIPE ].epAddr = 0x01; // PS3 report endpoint\n        epInfo[ PS3_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ PS3_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ PS3_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ PS3_INPUT_PIPE ].bmSndToggle = 0;\n        epInfo[ PS3_INPUT_PIPE ].bmRcvToggle = 0;\n\n        rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        delay(200); //Give time for address change\n\n        rcode = pUsb->setConf(bAddress, epInfo[ PS3_CONTROL_PIPE ].epAddr, 1);\n        if(rcode)\n                goto FailSetConfDescr;\n\n        if(PID == PS3_PID || PID == PS3NAVIGATION_PID) {\n                if(PID == PS3_PID) {\n#ifdef DEBUG_USB_HOST\n                        Notify(PSTR(\"\\r\\nDualshock 3 Controller Connected\"), 0x80);\n#endif\n                        PS3Connected = true;\n                } else { // must be a navigation controller\n#ifdef DEBUG_USB_HOST\n                        Notify(PSTR(\"\\r\\nNavigation Controller Connected\"), 0x80);\n#endif\n                        PS3NavigationConnected = true;\n                }\n                enable_sixaxis(); // The PS3 controller needs a special command before it starts sending data\n\n                // Needed for PS3 Dualshock and Navigation commands to work\n                for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++)\n                        writeBuf[i] = pgm_read_byte(&PS3_REPORT_BUFFER[i]);\n\n                for(uint8_t i = 6; i < 10; i++)\n                        readBuf[i] = 0x7F; // Set the analog joystick values to center position\n        } else { // must be a Motion controller\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nMotion Controller Connected\"), 0x80);\n#endif\n                PS3MoveConnected = true;\n                writeBuf[0] = 0x02; // Set report ID, this is needed for Move commands to work\n        }\n        if(my_bdaddr[0] != 0x00 || my_bdaddr[1] != 0x00 || my_bdaddr[2] != 0x00 || my_bdaddr[3] != 0x00 || my_bdaddr[4] != 0x00 || my_bdaddr[5] != 0x00) {\n                if(PS3MoveConnected)\n                        setMoveBdaddr(my_bdaddr); // Set internal Bluetooth address\n                else\n                        setBdaddr(my_bdaddr); // Set internal Bluetooth address\n\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nBluetooth Address was set to: \"), 0x80);\n                for(int8_t i = 5; i > 0; i--) {\n                        D_PrintHex<uint8_t > (my_bdaddr[i], 0x80);\n                        Notify(PSTR(\":\"), 0x80);\n                }\n                D_PrintHex<uint8_t > (my_bdaddr[0], 0x80);\n#endif\n        }\n        onInit();\n\n        bPollEnable = true;\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n        timer = millis();\n        return 0; // Successful configuration\n\n        /* Diagnostic messages */\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr();\n        goto Fail;\n#endif\n\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n#endif\n        goto Fail;\n\nFailUnknownDevice:\n#ifdef DEBUG_USB_HOST\n        NotifyFailUnknownDevice(VID, PID);\n#endif\n        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\nFail:\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nPS3 Init Failed, error code: \"), 0x80);\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n\n/* Performs a cleanup after failed Init() attempt */\nuint8_t PS3USB::Release() {\n        PS3Connected = false;\n        PS3MoveConnected = false;\n        PS3NavigationConnected = false;\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n        bAddress = 0;\n        bPollEnable = false;\n        return 0;\n}\n\nuint8_t PS3USB::Poll() {\n        if(!bPollEnable)\n                return 0;\n\n        if(PS3Connected || PS3NavigationConnected) {\n                uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;\n                pUsb->inTransfer(bAddress, epInfo[ PS3_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1\n                if(millis() - timer > 100) { // Loop 100ms before processing data\n                        readReport();\n#ifdef PRINTREPORT\n                        printReport(); // Uncomment \"#define PRINTREPORT\" to print the report send by the PS3 Controllers\n#endif\n                }\n        } else if(PS3MoveConnected) { // One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB\n                if(millis() - timer > 4000) { // Send at least every 4th second\n                        Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE); // The Bulb and rumble values, has to be written again and again, for it to stay turned on\n                        timer = millis();\n                }\n        }\n        return 0;\n}\n\nvoid PS3USB::readReport() {\n        ButtonState = (uint32_t)(readBuf[2] | ((uint16_t)readBuf[3] << 8) | ((uint32_t)readBuf[4] << 16));\n\n        //Notify(PSTR(\"\\r\\nButtonState\", 0x80);\n        //PrintHex<uint32_t>(ButtonState, 0x80);\n\n        if(ButtonState != OldButtonState) {\n                ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable\n                OldButtonState = ButtonState;\n        }\n}\n\nvoid PS3USB::printReport() { // Uncomment \"#define PRINTREPORT\" to print the report send by the PS3 Controllers\n#ifdef PRINTREPORT\n        for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++) {\n                D_PrintHex<uint8_t > (readBuf[i], 0x80);\n                Notify(PSTR(\" \"), 0x80);\n        }\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n#endif\n}\n\nbool PS3USB::getButtonPress(ButtonEnum b) {\n        return (ButtonState & pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]));\n}\n\nbool PS3USB::getButtonClick(ButtonEnum b) {\n        uint32_t button = pgm_read_dword(&PS3_BUTTONS[(uint8_t)b]);\n        bool click = (ButtonClickState & button);\n        ButtonClickState &= ~button; // Clear \"click\" event\n        return click;\n}\n\nuint8_t PS3USB::getAnalogButton(ButtonEnum a) {\n        return (uint8_t)(readBuf[(pgm_read_byte(&PS3_ANALOG_BUTTONS[(uint8_t)a])) - 9]);\n}\n\nuint8_t PS3USB::getAnalogHat(AnalogHatEnum a) {\n        return (uint8_t)(readBuf[((uint8_t)a + 6)]);\n}\n\nuint16_t PS3USB::getSensor(SensorEnum a) {\n        return ((readBuf[((uint16_t)a) - 9] << 8) | readBuf[((uint16_t)a + 1) - 9]);\n}\n\ndouble PS3USB::getAngle(AngleEnum a) {\n        if(PS3Connected) {\n                double accXval;\n                double accYval;\n                double accZval;\n\n                // Data for the Kionix KXPC4 used in the DualShock 3\n                const double zeroG = 511.5; // 1.65/3.3*1023 (1,65V)\n                accXval = -((double)getSensor(aX) - zeroG);\n                accYval = -((double)getSensor(aY) - zeroG);\n                accZval = -((double)getSensor(aZ) - zeroG);\n\n                // Convert to 360 degrees resolution\n                // atan2 outputs the value of -π to π (radians)\n                // We are then converting it to 0 to 2π and then to degrees\n                if(a == Pitch)\n                        return (atan2(accYval, accZval) + PI) * RAD_TO_DEG;\n                else\n                        return (atan2(accXval, accZval) + PI) * RAD_TO_DEG;\n        } else\n                return 0;\n}\n\nbool PS3USB::getStatus(StatusEnum c) {\n        return (readBuf[((uint16_t)c >> 8) - 9] == ((uint8_t)c & 0xff));\n}\n\nvoid PS3USB::printStatusString() {\n        char statusOutput[100]; // Max string length plus null character\n        if(PS3Connected || PS3NavigationConnected) {\n                strcpy_P(statusOutput, PSTR(\"ConnectionStatus: \"));\n\n                if(getStatus(Plugged)) strcat_P(statusOutput, PSTR(\"Plugged\"));\n                else if(getStatus(Unplugged)) strcat_P(statusOutput, PSTR(\"Unplugged\"));\n                else strcat_P(statusOutput, PSTR(\"Error\"));\n\n                strcat_P(statusOutput, PSTR(\" - PowerRating: \"));\n\n                if(getStatus(Charging)) strcat_P(statusOutput, PSTR(\"Charging\"));\n                else if(getStatus(NotCharging)) strcat_P(statusOutput, PSTR(\"Not Charging\"));\n                else if(getStatus(Shutdown)) strcat_P(statusOutput, PSTR(\"Shutdown\"));\n                else if(getStatus(Dying)) strcat_P(statusOutput, PSTR(\"Dying\"));\n                else if(getStatus(Low)) strcat_P(statusOutput, PSTR(\"Low\"));\n                else if(getStatus(High)) strcat_P(statusOutput, PSTR(\"High\"));\n                else if(getStatus(Full)) strcat_P(statusOutput, PSTR(\"Full\"));\n                else strcat_P(statusOutput, PSTR(\"Error\"));\n\n                strcat_P(statusOutput, PSTR(\" - WirelessStatus: \"));\n\n                if(getStatus(CableRumble)) strcat_P(statusOutput, PSTR(\"Cable - Rumble is on\"));\n                else if(getStatus(Cable)) strcat_P(statusOutput, PSTR(\"Cable - Rumble is off\"));\n                else if(getStatus(BluetoothRumble)) strcat_P(statusOutput, PSTR(\"Bluetooth - Rumble is on\"));\n                else if(getStatus(Bluetooth)) strcat_P(statusOutput, PSTR(\"Bluetooth - Rumble is off\"));\n                else strcat_P(statusOutput, PSTR(\"Error\"));\n        } else\n                strcpy_P(statusOutput, PSTR(\"Error\"));\n\n        USB_HOST_SERIAL.write(statusOutput);\n}\n\n/* Playstation Sixaxis Dualshock and Navigation Controller commands */\nvoid PS3USB::PS3_Command(uint8_t *data, uint16_t nbytes) {\n        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x01), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)\n        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x01, 0x02, 0x00, nbytes, nbytes, data, NULL);\n}\n\nvoid PS3USB::setAllOff() {\n        for(uint8_t i = 0; i < PS3_REPORT_BUFFER_SIZE; i++)\n                writeBuf[i] = pgm_read_byte(&PS3_REPORT_BUFFER[i]); // Reset buffer\n\n        PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);\n}\n\nvoid PS3USB::setRumbleOff() {\n        writeBuf[1] = 0x00;\n        writeBuf[2] = 0x00; // Low mode off\n        writeBuf[3] = 0x00;\n        writeBuf[4] = 0x00; // High mode off\n\n        PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);\n}\n\nvoid PS3USB::setRumbleOn(RumbleEnum mode) {\n        if((mode & 0x30) > 0x00) {\n                uint8_t power[2] = {0xff, 0x00}; // Defaults to RumbleLow\n                if(mode == RumbleHigh) {\n                        power[0] = 0x00;\n                        power[1] = 0xff;\n                }\n                setRumbleOn(0xfe, power[0], 0xfe, power[1]);\n        }\n}\n\nvoid PS3USB::setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower) {\n        writeBuf[1] = rightDuration;\n        writeBuf[2] = rightPower;\n        writeBuf[3] = leftDuration;\n        writeBuf[4] = leftPower;\n        PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);\n}\n\nvoid PS3USB::setLedRaw(uint8_t value) {\n        writeBuf[9] = value << 1;\n        PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);\n}\n\nvoid PS3USB::setLedOff(LEDEnum a) {\n        writeBuf[9] &= ~((uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1));\n        PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);\n}\n\nvoid PS3USB::setLedOn(LEDEnum a) {\n        if(a == OFF)\n                setLedRaw(0);\n        else {\n                writeBuf[9] |= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);\n                PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);\n        }\n}\n\nvoid PS3USB::setLedToggle(LEDEnum a) {\n        writeBuf[9] ^= (uint8_t)((pgm_read_byte(&PS3_LEDS[(uint8_t)a]) & 0x0f) << 1);\n        PS3_Command(writeBuf, PS3_REPORT_BUFFER_SIZE);\n}\n\nvoid PS3USB::setBdaddr(uint8_t *bdaddr) {\n        /* Set the internal Bluetooth address */\n        uint8_t buf[8];\n        buf[0] = 0x01;\n        buf[1] = 0x00;\n\n        for(uint8_t i = 0; i < 6; i++)\n                buf[i + 2] = bdaddr[5 - i]; // Copy into buffer, has to be written reversed, so it is MSB first\n\n        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data\n        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL);\n}\n\nvoid PS3USB::getBdaddr(uint8_t *bdaddr) {\n        uint8_t buf[8];\n\n        // bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0xF5), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data\n        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0xF5, 0x03, 0x00, 8, 8, buf, NULL);\n\n        for(uint8_t i = 0; i < 6; i++)\n                bdaddr[5 - i] = buf[i + 2]; // Copy into buffer reversed, so it is LSB first\n}\n\nvoid PS3USB::enable_sixaxis() { // Command used to enable the Dualshock 3 and Navigation controller to send data via USB\n        uint8_t cmd_buf[4];\n        cmd_buf[0] = 0x42; // Special PS3 Controller enable commands\n        cmd_buf[1] = 0x0c;\n        cmd_buf[2] = 0x00;\n        cmd_buf[3] = 0x00;\n\n        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0xF4), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data)\n        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0xF4, 0x03, 0x00, 4, 4, cmd_buf, NULL);\n}\n\n/* Playstation Move Controller commands */\nvoid PS3USB::Move_Command(uint8_t *data, uint16_t nbytes) {\n        pUsb->outTransfer(bAddress, epInfo[ PS3_OUTPUT_PIPE ].epAddr, nbytes, data);\n}\n\nvoid PS3USB::moveSetBulb(uint8_t r, uint8_t g, uint8_t b) { // Use this to set the Color using RGB values\n        // Set the Bulb's values into the write buffer\n        writeBuf[2] = r;\n        writeBuf[3] = g;\n        writeBuf[4] = b;\n\n        Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE);\n}\n\nvoid PS3USB::moveSetBulb(ColorsEnum color) { // Use this to set the Color using the predefined colors in \"enums.h\"\n        moveSetBulb((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color));\n}\n\nvoid PS3USB::moveSetRumble(uint8_t rumble) {\n#ifdef DEBUG_USB_HOST\n        if(rumble < 64 && rumble != 0) // The rumble value has to at least 64, or approximately 25% (64/255*100)\n                Notify(PSTR(\"\\r\\nThe rumble value has to at least 64, or approximately 25%\"), 0x80);\n#endif\n        writeBuf[6] = rumble; // Set the rumble value into the write buffer\n\n        Move_Command(writeBuf, MOVE_REPORT_BUFFER_SIZE);\n}\n\nvoid PS3USB::setMoveBdaddr(uint8_t *bdaddr) {\n        /* Set the internal Bluetooth address */\n        uint8_t buf[11];\n        buf[0] = 0x05;\n        buf[7] = 0x10;\n        buf[8] = 0x01;\n        buf[9] = 0x02;\n        buf[10] = 0x12;\n\n        for(uint8_t i = 0; i < 6; i++)\n                buf[i + 1] = bdaddr[i];\n\n        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x05), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data\n        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x05, 0x03, 0x00, 11, 11, buf, NULL);\n}\n\nvoid PS3USB::getMoveBdaddr(uint8_t *bdaddr) {\n        uint8_t buf[16];\n\n        // bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0x04), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data\n        pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0x04, 0x03, 0x00, 16, 16, buf, NULL);\n\n        for(uint8_t i = 0; i < 6; i++)\n                bdaddr[i] = buf[10 + i];\n}\n\nvoid PS3USB::getMoveCalibration(uint8_t *data) {\n        uint8_t buf[49];\n\n        for(uint8_t i = 0; i < 3; i++) {\n                // bmRequest = Device to host (0x80) | Class (0x20) | Interface (0x01) = 0xA1, bRequest = Get Report (0x01), Report ID (0x10), Report Type (Feature 0x03), interface (0x00), datalength, datalength, data\n                pUsb->ctrlReq(bAddress, epInfo[PS3_CONTROL_PIPE].epAddr, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, 0x10, 0x03, 0x00, 49, 49, buf, NULL);\n\n                for(byte j = 0; j < 49; j++)\n                        data[49 * i + j] = buf[j];\n        }\n}\n\nvoid PS3USB::onInit() {\n        if(pFuncOnInit)\n                pFuncOnInit(); // Call the user function\n        else {\n                if(PS3MoveConnected)\n                        moveSetBulb(Red);\n                else // Dualshock 3 or Navigation controller\n                        setLedOn(LED1);\n        }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/PS3USB.h",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _ps3usb_h_\n#define _ps3usb_h_\n\n#include \"Usb.h\"\n#include \"hid.h\"\n#include \"PS3Enums.h\"\n\n/* PS3 data taken from descriptors */\n#define EP_MAXPKTSIZE           64 // max size for data via USB\n\n/* Names we give to the 3 ps3 pipes - this is only used for setting the bluetooth address into the ps3 controllers */\n#define PS3_CONTROL_PIPE        0\n#define PS3_OUTPUT_PIPE         1\n#define PS3_INPUT_PIPE          2\n\n//PID and VID of the different devices\n#define PS3_VID                 0x054C  // Sony Corporation\n#define PS3_PID                 0x0268  // PS3 Controller DualShock 3\n#define PS3NAVIGATION_PID       0x042F  // Navigation controller\n#define PS3MOVE_PID             0x03D5  // Motion controller\n\n#define PS3_MAX_ENDPOINTS       3\n\n/**\n * This class implements support for all the official PS3 Controllers:\n * Dualshock 3, Navigation or a Motion controller via USB.\n *\n * One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB on the Move controller.\n *\n * Information about the protocol can be found at the wiki: https://github.com/felis/USB_Host_Shield_2.0/wiki/PS3-Information.\n */\nclass PS3USB : public USBDeviceConfig {\npublic:\n        /**\n         * Constructor for the PS3USB class.\n         * @param  pUsb   Pointer to USB class instance.\n         * @param  btadr5,btadr4,btadr3,btadr2,btadr1,btadr0\n         * Pass your dongles Bluetooth address into the constructor,\n         * so you are able to pair the controller with a Bluetooth dongle.\n         */\n        PS3USB(USB *pUsb, uint8_t btadr5 = 0, uint8_t btadr4 = 0, uint8_t btadr3 = 0, uint8_t btadr2 = 0, uint8_t btadr1 = 0, uint8_t btadr0 = 0);\n\n        /** @name USBDeviceConfig implementation */\n        /**\n         * Initialize the PS3 Controller.\n         * @param  parent   Hub number.\n         * @param  port     Port number on the hub.\n         * @param  lowspeed Speed of the device.\n         * @return          0 on success.\n         */\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        /**\n         * Release the USB device.\n         * @return 0 on success.\n         */\n        uint8_t Release();\n        /**\n         * Poll the USB Input endpoins and run the state machines.\n         * @return 0 on success.\n         */\n        uint8_t Poll();\n\n        /**\n         * Get the device address.\n         * @return The device address.\n         */\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        /**\n         * Used to check if the controller has been initialized.\n         * @return True if it's ready.\n         */\n        virtual bool isReady() {\n                return bPollEnable;\n        };\n\n        /**\n         * Used by the USB core to check what this driver support.\n         * @param  vid The device's VID.\n         * @param  pid The device's PID.\n         * @return     Returns true if the device's VID and PID matches this driver.\n         */\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                return (vid == PS3_VID && (pid == PS3_PID || pid == PS3NAVIGATION_PID || pid == PS3MOVE_PID));\n        };\n        /**@}*/\n\n        /**\n         * Used to set the Bluetooth address inside the Dualshock 3 and Navigation controller.\n         * Set using LSB first.\n         * @param bdaddr Your dongles Bluetooth address.\n         */\n        void setBdaddr(uint8_t *bdaddr);\n        /**\n         * Used to get the Bluetooth address inside the Dualshock 3 and Navigation controller.\n         * Will return LSB first.\n         * @param bdaddr Your dongles Bluetooth address.\n         */\n        void getBdaddr(uint8_t *bdaddr);\n\n        /**\n         * Used to set the Bluetooth address inside the Move controller.\n         * Set using LSB first.\n         * @param bdaddr Your dongles Bluetooth address.\n         */\n        void setMoveBdaddr(uint8_t *bdaddr);\n        /**\n         * Used to get the Bluetooth address inside the Move controller.\n         * Will return LSB first.\n         * @param bdaddr Your dongles Bluetooth address.\n         */\n        void getMoveBdaddr(uint8_t *bdaddr);\n        /**\n         * Used to get the calibration data inside the Move controller.\n         * @param data Buffer to store data in. Must be at least 147 bytes\n         */\n        void getMoveCalibration(uint8_t *data);\n\n        /** @name PS3 Controller functions */\n        /**\n         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.\n         *\n         * While getButtonClick(ButtonEnum b) will only return it once.\n         *\n         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),\n         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).\n         * @param  b          ::ButtonEnum to read.\n         * @return            getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.\n         */\n        bool getButtonPress(ButtonEnum b);\n        bool getButtonClick(ButtonEnum b);\n        /**@}*/\n        /** @name PS3 Controller functions */\n        /**\n         * Used to get the analog value from button presses.\n         * @param  a The ::ButtonEnum to read.\n         * The supported buttons are:\n         * ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2,\n         * ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T.\n         * @return   Analog value in the range of 0-255.\n         */\n        uint8_t getAnalogButton(ButtonEnum a);\n        /**\n         * Used to read the analog joystick.\n         * @param  a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.\n         * @return   Return the analog value in the range of 0-255.\n         */\n        uint8_t getAnalogHat(AnalogHatEnum a);\n        /**\n         * Used to read the sensors inside the Dualshock 3 controller.\n         * @param  a\n         * The Dualshock 3 has a 3-axis accelerometer and a 1-axis gyro inside.\n         * @return   Return the raw sensor value.\n         */\n        uint16_t getSensor(SensorEnum a);\n        /**\n         * Use this to get ::Pitch and ::Roll calculated using the accelerometer.\n         * @param  a Either ::Pitch or ::Roll.\n         * @return   Return the angle in the range of 0-360.\n         */\n        double getAngle(AngleEnum a);\n        /**\n         * Get the ::StatusEnum from the controller.\n         * @param  c The ::StatusEnum you want to read.\n         * @return   True if correct and false if not.\n         */\n        bool getStatus(StatusEnum c);\n        /** Read all the available statuses from the controller and prints it as a nice formated string. */\n        void printStatusString();\n\n        /** Used to set all LEDs and rumble off. */\n        void setAllOff();\n        /** Turn off rumble. */\n        void setRumbleOff();\n        /**\n         * Turn on rumble.\n         * @param mode Either ::RumbleHigh or ::RumbleLow.\n         */\n        void setRumbleOn(RumbleEnum mode);\n        /**\n         * Turn on rumble using custom duration and power.\n         * @param rightDuration The duration of the right/low rumble effect.\n         * @param rightPower The intensity of the right/low rumble effect.\n         * @param leftDuration The duration of the left/high rumble effect.\n         * @param leftPower The intensity of the left/high rumble effect.\n         */\n        void setRumbleOn(uint8_t rightDuration, uint8_t rightPower, uint8_t leftDuration, uint8_t leftPower);\n\n        /**\n         * Set LED value without using the ::LEDEnum.\n         * @param value See: ::LEDEnum.\n         */\n        void setLedRaw(uint8_t value);\n\n        /** Turn all LEDs off. */\n        void setLedOff() {\n                setLedRaw(0);\n        }\n        /**\n         * Turn the specific ::LEDEnum off.\n         * @param a The ::LEDEnum to turn off.\n         */\n        void setLedOff(LEDEnum a);\n        /**\n         * Turn the specific ::LEDEnum on.\n         * @param a The ::LEDEnum to turn on.\n         */\n        void setLedOn(LEDEnum a);\n        /**\n         * Toggle the specific ::LEDEnum.\n         * @param a The ::LEDEnum to toggle.\n         */\n        void setLedToggle(LEDEnum a);\n\n        /**\n         * Use this to set the Color using RGB values.\n         * @param r,g,b RGB value.\n         */\n        void moveSetBulb(uint8_t r, uint8_t g, uint8_t b);\n        /**\n         * Use this to set the color using the predefined colors in ::ColorsEnum.\n         * @param color The desired color.\n         */\n        void moveSetBulb(ColorsEnum color);\n        /**\n         * Set the rumble value inside the Move controller.\n         * @param rumble The desired value in the range from 64-255.\n         */\n        void moveSetRumble(uint8_t rumble);\n\n        /**\n         * Used to call your own function when the controller is successfully initialized.\n         * @param funcOnInit Function to call.\n         */\n        void attachOnInit(void (*funcOnInit)(void)) {\n                pFuncOnInit = funcOnInit;\n        };\n        /**@}*/\n\n        /** Variable used to indicate if the normal playstation controller is successfully connected. */\n        bool PS3Connected;\n        /** Variable used to indicate if the move controller is successfully connected. */\n        bool PS3MoveConnected;\n        /** Variable used to indicate if the navigation controller is successfully connected. */\n        bool PS3NavigationConnected;\n\nprotected:\n        /** Pointer to USB class instance. */\n        USB *pUsb;\n        /** Device address. */\n        uint8_t bAddress;\n        /** Endpoint info structure. */\n        EpInfo epInfo[PS3_MAX_ENDPOINTS];\n\nprivate:\n        /**\n         * Called when the controller is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         */\n        void onInit();\n        void (*pFuncOnInit)(void); // Pointer to function called in onInit()\n\n        bool bPollEnable;\n\n        uint32_t timer; // used to continuously set PS3 Move controller Bulb and rumble values\n\n        uint32_t ButtonState;\n        uint32_t OldButtonState;\n        uint32_t ButtonClickState;\n\n        uint8_t my_bdaddr[6]; // Change to your dongles Bluetooth address in the constructor\n        uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data\n        uint8_t writeBuf[EP_MAXPKTSIZE]; // General purpose buffer for output data\n\n        void readReport(); // read incoming data\n        void printReport(); // print incoming date - Uncomment for debugging\n\n        /* Private commands */\n        void PS3_Command(uint8_t *data, uint16_t nbytes);\n        void enable_sixaxis(); // Command used to enable the Dualshock 3 and Navigation controller to send data via USB\n        void Move_Command(uint8_t *data, uint16_t nbytes);\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/PS4BT.h",
    "content": "/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _ps4bt_h_\n#define _ps4bt_h_\n\n#include \"BTHID.h\"\n#include \"PS4Parser.h\"\n\n/**\n * This class implements support for the PS4 controller via Bluetooth.\n * It uses the BTHID class for all the Bluetooth communication.\n */\nclass PS4BT : public BTHID, public PS4Parser {\npublic:\n        /**\n         * Constructor for the PS4BT class.\n         * @param  p     Pointer to the BTD class instance.\n         * @param  pair  Set this to true in order to pair with the device. If the argument is omitted then it will not pair with it. One can use ::PAIR to set it to true.\n         * @param  pin   Write the pin to BTD#btdPin. If argument is omitted, then \"0000\" will be used.\n         */\n        PS4BT(BTD *p, bool pair = false, const char *pin = \"0000\") :\n        BTHID(p, pair, pin) {\n                PS4Parser::Reset();\n        };\n\n        /**\n         * Used to check if a PS4 controller is connected.\n         * @return Returns true if it is connected.\n         */\n        bool connected() {\n                return BTHID::connected;\n        };\n\nprotected:\n        /** @name BTHID implementation */\n        /**\n         * Used to parse Bluetooth HID data.\n         * @param len The length of the incoming data.\n         * @param buf Pointer to the data buffer.\n         */\n        virtual void ParseBTHIDData(uint8_t len, uint8_t *buf) {\n                PS4Parser::Parse(len, buf);\n        };\n\n        /**\n         * Called when a device is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         */\n        virtual void OnInitBTHID() {\n                PS4Parser::Reset();\n                enable_sixaxis(); // Make the controller send out the entire output report\n                if (pFuncOnInit)\n                        pFuncOnInit(); // Call the user function\n                else\n                        setLed(Blue);\n        };\n\n        /** Used to reset the different buffers to there default values */\n        virtual void ResetBTHID() {\n                PS4Parser::Reset();\n        };\n        /**@}*/\n\n        /** @name PS4Parser implementation */\n        virtual void sendOutputReport(PS4Output *output) { // Source: https://github.com/chrippa/ds4drv\n                uint8_t buf[79];\n                memset(buf, 0, sizeof(buf));\n\n                buf[0] = 0x52; // HID BT Set_report (0x50) | Report Type (Output 0x02)\n                buf[1] = 0x11; // Report ID\n                buf[2] = 0x80;\n                buf[4]= 0xFF;\n\n                buf[7] = output->smallRumble; // Small Rumble\n                buf[8] = output->bigRumble; // Big rumble\n\n                buf[9] = output->r; // Red\n                buf[10] = output->g; // Green\n                buf[11] = output->b; // Blue\n\n                buf[12] = output->flashOn; // Time to flash bright (255 = 2.5 seconds)\n                buf[13] = output->flashOff; // Time to flash dark (255 = 2.5 seconds)\n\n                output->reportChanged = false;\n\n                // The PS4 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed\n\n                HID_Command(buf, sizeof(buf));\n        };\n        /**@}*/\n\nprivate:\n        void enable_sixaxis() { // Command used to make the PS4 controller send out the entire output report\n                uint8_t buf[2];\n                buf[0] = 0x43; // HID BT Get_report (0x40) | Report Type (Feature 0x03)\n                buf[1] = 0x02; // Report ID\n\n                HID_Command(buf, 2);\n        };\n\n        void HID_Command(uint8_t *data, uint8_t nbytes) {\n                pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]);\n        };\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/PS4Parser.cpp",
    "content": "/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#include \"PS4Parser.h\"\n\n// To enable serial debugging see \"settings.h\"\n//#define PRINTREPORT // Uncomment to print the report send by the PS4 Controller\n\nbool PS4Parser::checkDpad(ButtonEnum b) {\n        switch (b) {\n                case UP:\n                        return ps4Data.btn.dpad == DPAD_LEFT_UP || ps4Data.btn.dpad == DPAD_UP || ps4Data.btn.dpad == DPAD_UP_RIGHT;\n                case RIGHT:\n                        return ps4Data.btn.dpad == DPAD_UP_RIGHT || ps4Data.btn.dpad == DPAD_RIGHT || ps4Data.btn.dpad == DPAD_RIGHT_DOWN;\n                case DOWN:\n                        return ps4Data.btn.dpad == DPAD_RIGHT_DOWN || ps4Data.btn.dpad == DPAD_DOWN || ps4Data.btn.dpad == DPAD_DOWN_LEFT;\n                case LEFT:\n                        return ps4Data.btn.dpad == DPAD_DOWN_LEFT || ps4Data.btn.dpad == DPAD_LEFT || ps4Data.btn.dpad == DPAD_LEFT_UP;\n                default:\n                        return false;\n        }\n}\n\nbool PS4Parser::getButtonPress(ButtonEnum b) {\n        if (b <= LEFT) // Dpad\n                return checkDpad(b);\n        else\n                return ps4Data.btn.val & (1UL << pgm_read_byte(&PS4_BUTTONS[(uint8_t)b]));\n}\n\nbool PS4Parser::getButtonClick(ButtonEnum b) {\n        uint32_t mask = 1UL << pgm_read_byte(&PS4_BUTTONS[(uint8_t)b]);\n        bool click = buttonClickState.val & mask;\n        buttonClickState.val &= ~mask; // Clear \"click\" event\n        return click;\n}\n\nuint8_t PS4Parser::getAnalogButton(ButtonEnum b) {\n        if (b == L2) // These are the only analog buttons on the controller\n                return ps4Data.trigger[0];\n        else if (b == R2)\n                return ps4Data.trigger[1];\n        return 0;\n}\n\nuint8_t PS4Parser::getAnalogHat(AnalogHatEnum a) {\n        return ps4Data.hatValue[(uint8_t)a];\n}\n\nvoid PS4Parser::Parse(uint8_t len, uint8_t *buf) {\n        if (len > 1 && buf)  {\n#ifdef PRINTREPORT\n                Notify(PSTR(\"\\r\\n\"), 0x80);\n                for (uint8_t i = 0; i < len; i++) {\n                        D_PrintHex<uint8_t > (buf[i], 0x80);\n                        Notify(PSTR(\" \"), 0x80);\n                }\n#endif\n\n                if (buf[0] == 0x01) // Check report ID\n                        memcpy(&ps4Data, buf + 1, min((uint8_t)(len - 1), sizeof(ps4Data)));\n                else if (buf[0] == 0x11) { // This report is send via Bluetooth, it has an offset of 2 compared to the USB data\n                        if (len < 4) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nReport is too short: \"), 0x80);\n                                D_PrintHex<uint8_t > (len, 0x80);\n#endif\n                                return;\n                        }\n                        memcpy(&ps4Data, buf + 3, min((uint8_t)(len - 3), sizeof(ps4Data)));\n                } else {\n#ifdef DEBUG_USB_HOST\n                        Notify(PSTR(\"\\r\\nUnknown report id: \"), 0x80);\n                        D_PrintHex<uint8_t > (buf[0], 0x80);\n#endif\n                        return;\n                }\n\n                if (ps4Data.btn.val != oldButtonState.val) { // Check if anything has changed\n                        buttonClickState.val = ps4Data.btn.val & ~oldButtonState.val; // Update click state variable\n                        oldButtonState.val = ps4Data.btn.val;\n\n                        // The DPAD buttons does not set the different bits, but set a value corresponding to the buttons pressed, we will simply set the bits ourself\n                        uint8_t newDpad = 0;\n                        if (checkDpad(UP))\n                                newDpad |= 1 << UP;\n                        if (checkDpad(RIGHT))\n                                newDpad |= 1 << RIGHT;\n                        if (checkDpad(DOWN))\n                                newDpad |= 1 << DOWN;\n                        if (checkDpad(LEFT))\n                                newDpad |= 1 << LEFT;\n                        if (newDpad != oldDpad) {\n                                buttonClickState.dpad = newDpad & ~oldDpad; // Override values\n                                oldDpad = newDpad;\n                        }\n                }\n        }\n\n        if (ps4Output.reportChanged)\n                sendOutputReport(&ps4Output); // Send output report\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/PS4Parser.h",
    "content": "/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _ps4parser_h_\n#define _ps4parser_h_\n\n#include \"Usb.h\"\n#include \"controllerEnums.h\"\n\n/** Buttons on the controller */\nconst uint8_t PS4_BUTTONS[] PROGMEM = {\n        UP, // UP\n        RIGHT, // RIGHT\n        DOWN, // DOWN\n        LEFT, // LEFT\n\n        0x0C, // SHARE\n        0x0D, // OPTIONS\n        0x0E, // L3\n        0x0F, // R3\n\n        0x0A, // L2\n        0x0B, // R2\n        0x08, // L1\n        0x09, // R1\n\n        0x07, // TRIANGLE\n        0x06, // CIRCLE\n        0x05, // CROSS\n        0x04, // SQUARE\n\n        0x10, // PS\n        0x11, // TOUCHPAD\n};\n\nunion PS4Buttons {\n        struct {\n                uint8_t dpad : 4;\n                uint8_t square : 1;\n                uint8_t cross : 1;\n                uint8_t circle : 1;\n                uint8_t triangle : 1;\n\n                uint8_t l1 : 1;\n                uint8_t r1 : 1;\n                uint8_t l2 : 1;\n                uint8_t r2 : 1;\n                uint8_t share : 1;\n                uint8_t options : 1;\n                uint8_t l3 : 1;\n                uint8_t r3 : 1;\n\n                uint8_t ps : 1;\n                uint8_t touchpad : 1;\n                uint8_t reportCounter : 6;\n        } __attribute__((packed));\n        uint32_t val : 24;\n} __attribute__((packed));\n\nstruct touchpadXY {\n        uint8_t dummy; // I can not figure out what this data is for, it seems to change randomly, maybe a timestamp?\n        struct {\n                uint8_t counter : 7; // Increments every time a finger is touching the touchpad\n                uint8_t touching : 1; // The top bit is cleared if the finger is touching the touchpad\n                uint16_t x : 12;\n                uint16_t y : 12;\n        } __attribute__((packed)) finger[2]; // 0 = first finger, 1 = second finger\n} __attribute__((packed));\n\nstruct PS4Status {\n        uint8_t battery : 4;\n        uint8_t usb : 1;\n        uint8_t audio : 1;\n        uint8_t mic : 1;\n        uint8_t unknown : 1; // Extension port?\n} __attribute__((packed));\n\nstruct PS4Data {\n        /* Button and joystick values */\n        uint8_t hatValue[4];\n        PS4Buttons btn;\n        uint8_t trigger[2];\n\n        /* Gyro and accelerometer values */\n        uint8_t dummy[3]; // First two looks random, while the third one might be some kind of status - it increments once in a while\n        int16_t gyroY, gyroZ, gyroX;\n        int16_t accX, accZ, accY;\n\n        uint8_t dummy2[5];\n        PS4Status status;\n        uint8_t dummy3[3];\n\n        /* The rest is data for the touchpad */\n        touchpadXY xy[3]; // It looks like it sends out three coordinates each time, this might be because the microcontroller inside the PS4 controller is much faster than the Bluetooth connection.\n                          // The last data is read from the last position in the array while the oldest measurement is from the first position.\n                          // The first position will also keep it's value after the finger is released, while the other two will set them to zero.\n                          // Note that if you read fast enough from the device, then only the first one will contain any data.\n\n        // The last three bytes are always: 0x00, 0x80, 0x00\n} __attribute__((packed));\n\nstruct PS4Output {\n        uint8_t bigRumble, smallRumble; // Rumble\n        uint8_t r, g, b; // RGB\n        uint8_t flashOn, flashOff; // Time to flash bright/dark (255 = 2.5 seconds)\n        bool reportChanged; // The data is send when data is received from the controller\n} __attribute__((packed));\n\nenum DPADEnum {\n        DPAD_UP = 0x0,\n        DPAD_UP_RIGHT = 0x1,\n        DPAD_RIGHT = 0x2,\n        DPAD_RIGHT_DOWN = 0x3,\n        DPAD_DOWN = 0x4,\n        DPAD_DOWN_LEFT = 0x5,\n        DPAD_LEFT = 0x6,\n        DPAD_LEFT_UP = 0x7,\n        DPAD_OFF = 0x8,\n};\n\n/** This class parses all the data sent by the PS4 controller */\nclass PS4Parser {\npublic:\n        /** Constructor for the PS4Parser class. */\n        PS4Parser() {\n                Reset();\n        };\n\n        /** @name PS4 Controller functions */\n        /**\n         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.\n         *\n         * While getButtonClick(ButtonEnum b) will only return it once.\n         *\n         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),\n         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).\n         * @param  b          ::ButtonEnum to read.\n         * @return            getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.\n         */\n        bool getButtonPress(ButtonEnum b);\n        bool getButtonClick(ButtonEnum b);\n        /**@}*/\n        /** @name PS4 Controller functions */\n        /**\n         * Used to get the analog value from button presses.\n         * @param  b The ::ButtonEnum to read.\n         * The supported buttons are:\n         * ::UP, ::RIGHT, ::DOWN, ::LEFT, ::L1, ::L2, ::R1, ::R2,\n         * ::TRIANGLE, ::CIRCLE, ::CROSS, ::SQUARE, and ::T.\n         * @return   Analog value in the range of 0-255.\n         */\n        uint8_t getAnalogButton(ButtonEnum b);\n\n        /**\n         * Used to read the analog joystick.\n         * @param  a ::LeftHatX, ::LeftHatY, ::RightHatX, and ::RightHatY.\n         * @return   Return the analog value in the range of 0-255.\n         */\n        uint8_t getAnalogHat(AnalogHatEnum a);\n\n        /**\n         * Get the x-coordinate of the touchpad. Position 0 is in the top left.\n         * @param  finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.\n         * @param  xyId   The controller sends out three packets with the same structure.\n         *                The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.\n         *                For that reason it will be set to 0 if the argument is omitted.\n         * @return        Returns the x-coordinate of the finger.\n         */\n        uint16_t getX(uint8_t finger = 0, uint8_t xyId = 0) {\n                return ps4Data.xy[xyId].finger[finger].x;\n        };\n\n        /**\n         * Get the y-coordinate of the touchpad. Position 0 is in the top left.\n         * @param  finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.\n         * @param  xyId   The controller sends out three packets with the same structure.\n         *                The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.\n         *                For that reason it will be set to 0 if the argument is omitted.\n         * @return        Returns the y-coordinate of the finger.\n         */\n        uint16_t getY(uint8_t finger = 0, uint8_t xyId = 0) {\n                return ps4Data.xy[xyId].finger[finger].y;\n        };\n\n        /**\n         * Returns whenever the user is toucing the touchpad.\n         * @param  finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.\n         * @param  xyId   The controller sends out three packets with the same structure.\n         *                The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.\n         *                For that reason it will be set to 0 if the argument is omitted.\n         * @return        Returns true if the specific finger is touching the touchpad.\n         */\n        bool isTouching(uint8_t finger = 0, uint8_t xyId = 0) {\n                return !(ps4Data.xy[xyId].finger[finger].touching); // The bit is cleared when a finger is touching the touchpad\n        };\n\n        /**\n         * This counter increments every time a finger touches the touchpad.\n         * @param  finger 0 = first finger, 1 = second finger. If omitted, then 0 will be used.\n         * @param  xyId   The controller sends out three packets with the same structure.\n         *                The third one will contain the last measure, but if you read from the controller then there is only be data in the first one.\n         *                For that reason it will be set to 0 if the argument is omitted.\n         * @return        Return the value of the counter, note that it is only a 7-bit value.\n         */\n        uint8_t getTouchCounter(uint8_t finger = 0, uint8_t xyId = 0) {\n                return ps4Data.xy[xyId].finger[finger].counter;\n        };\n\n        /**\n         * Get the angle of the controller calculated using the accelerometer.\n         * @param  a Either ::Pitch or ::Roll.\n         * @return   Return the angle in the range of 0-360.\n         */\n        double getAngle(AngleEnum a) {\n                if (a == Pitch)\n                        return (atan2(ps4Data.accY, ps4Data.accZ) + PI) * RAD_TO_DEG;\n                else\n                        return (atan2(ps4Data.accX, ps4Data.accZ) + PI) * RAD_TO_DEG;\n        };\n\n        /**\n         * Used to get the raw values from the 3-axis gyroscope and 3-axis accelerometer inside the PS4 controller.\n         * @param  s The sensor to read.\n         * @return   Returns the raw sensor reading.\n         */\n        int16_t getSensor(SensorEnum s) {\n                switch(s) {\n                        case gX:\n                                return ps4Data.gyroX;\n                        case gY:\n                                return ps4Data.gyroY;\n                        case gZ:\n                                return ps4Data.gyroZ;\n                        case aX:\n                                return ps4Data.accX;\n                        case aY:\n                                return ps4Data.accY;\n                        case aZ:\n                                return ps4Data.accZ;\n                        default:\n                                return 0;\n                }\n        };\n\n        /**\n         * Return the battery level of the PS4 controller.\n         * @return The battery level in the range 0-15.\n         */\n        uint8_t getBatteryLevel() {\n                return ps4Data.status.battery;\n        };\n\n        /**\n         * Use this to check if an USB cable is connected to the PS4 controller.\n         * @return Returns true if an USB cable is connected.\n         */\n        bool getUsbStatus() {\n                return ps4Data.status.usb;\n        };\n\n        /**\n         * Use this to check if an audio jack cable is connected to the PS4 controller.\n         * @return Returns true if an audio jack cable is connected.\n         */\n        bool getAudioStatus() {\n                return ps4Data.status.audio;\n        };\n\n        /**\n         * Use this to check if a microphone is connected to the PS4 controller.\n         * @return Returns true if a microphone is connected.\n         */\n        bool getMicStatus() {\n                return ps4Data.status.mic;\n        };\n\n        /** Turn both rumble and the LEDs off. */\n        void setAllOff() {\n                setRumbleOff();\n                setLedOff();\n        };\n\n        /** Set rumble off. */\n        void setRumbleOff() {\n                setRumbleOn(0, 0);\n        };\n\n        /**\n         * Turn on rumble.\n         * @param mode Either ::RumbleHigh or ::RumbleLow.\n         */\n        void setRumbleOn(RumbleEnum mode) {\n                if (mode == RumbleLow)\n                        setRumbleOn(0x00, 0xFF);\n                else\n                        setRumbleOn(0xFF, 0x00);\n        };\n\n        /**\n         * Turn on rumble.\n         * @param bigRumble   Value for big motor.\n         * @param smallRumble Value for small motor.\n         */\n        void setRumbleOn(uint8_t bigRumble, uint8_t smallRumble) {\n                ps4Output.bigRumble = bigRumble;\n                ps4Output.smallRumble = smallRumble;\n                ps4Output.reportChanged = true;\n        };\n\n        /** Turn all LEDs off. */\n        void setLedOff() {\n                setLed(0, 0, 0);\n        };\n\n        /**\n         * Use this to set the color using RGB values.\n         * @param r,g,b RGB value.\n         */\n        void setLed(uint8_t r, uint8_t g, uint8_t b) {\n                ps4Output.r = r;\n                ps4Output.g = g;\n                ps4Output.b = b;\n                ps4Output.reportChanged = true;\n        };\n\n        /**\n         * Use this to set the color using the predefined colors in ::ColorsEnum.\n         * @param color The desired color.\n         */\n        void setLed(ColorsEnum color) {\n                setLed((uint8_t)(color >> 16), (uint8_t)(color >> 8), (uint8_t)(color));\n        };\n\n        /**\n         * Set the LEDs flash time.\n         * @param flashOn  Time to flash bright (255 = 2.5 seconds).\n         * @param flashOff Time to flash dark (255 = 2.5 seconds).\n         */\n        void setLedFlash(uint8_t flashOn, uint8_t flashOff) {\n                ps4Output.flashOn = flashOn;\n                ps4Output.flashOff = flashOff;\n                ps4Output.reportChanged = true;\n        };\n        /**@}*/\n\nprotected:\n        /**\n         * Used to parse data sent from the PS4 controller.\n         * @param len Length of the data.\n         * @param buf Pointer to the data buffer.\n         */\n        void Parse(uint8_t len, uint8_t *buf);\n\n        /** Used to reset the different buffers to their default values */\n        void Reset() {\n                uint8_t i;\n                for (i = 0; i < sizeof(ps4Data.hatValue); i++)\n                        ps4Data.hatValue[i] = 127; // Center value\n                ps4Data.btn.val = 0;\n                oldButtonState.val = 0;\n                for (i = 0; i < sizeof(ps4Data.trigger); i++)\n                        ps4Data.trigger[i] = 0;\n                for (i = 0; i < sizeof(ps4Data.xy)/sizeof(ps4Data.xy[0]); i++) {\n                        for (uint8_t j = 0; j < sizeof(ps4Data.xy[0].finger)/sizeof(ps4Data.xy[0].finger[0]); j++)\n                                ps4Data.xy[i].finger[j].touching = 1; // The bit is cleared if the finger is touching the touchpad\n                }\n\n                ps4Data.btn.dpad = DPAD_OFF;\n                oldButtonState.dpad = DPAD_OFF;\n                buttonClickState.dpad = 0;\n                oldDpad = 0;\n\n                ps4Output.bigRumble = ps4Output.smallRumble = 0;\n                ps4Output.r = ps4Output.g = ps4Output.b = 0;\n                ps4Output.flashOn = ps4Output.flashOff = 0;\n                ps4Output.reportChanged = false;\n        };\n\n        /**\n         * Send the output to the PS4 controller. This is implemented in PS4BT.h and PS4USB.h.\n         * @param output Pointer to PS4Output buffer;\n         */\n        virtual void sendOutputReport(PS4Output *output) = 0;\n\nprivate:\n        bool checkDpad(ButtonEnum b); // Used to check PS4 DPAD buttons\n\n        PS4Data ps4Data;\n        PS4Buttons oldButtonState, buttonClickState;\n        PS4Output ps4Output;\n        uint8_t oldDpad;\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/PS4USB.h",
    "content": "/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _ps4usb_h_\n#define _ps4usb_h_\n\n#include \"hiduniversal.h\"\n#include \"PS4Parser.h\"\n\n#define PS4_VID 0x054C // Sony Corporation\n#define PS4_PID 0x05C4 // PS4 Controller\n\n/**\n * This class implements support for the PS4 controller via USB.\n * It uses the HIDUniversal class for all the USB communication.\n */\nclass PS4USB : public HIDUniversal, public PS4Parser {\npublic:\n        /**\n         * Constructor for the PS4USB class.\n         * @param  p   Pointer to the USB class instance.\n         */\n        PS4USB(USB *p) :\n        HIDUniversal(p) {\n                PS4Parser::Reset();\n        };\n\n        /**\n         * Used to check if a PS4 controller is connected.\n         * @return Returns true if it is connected.\n         */\n        bool connected() {\n                return HIDUniversal::isReady() && HIDUniversal::VID == PS4_VID && HIDUniversal::PID == PS4_PID;\n        };\n\n        /**\n         * Used to call your own function when the device is successfully initialized.\n         * @param funcOnInit Function to call.\n         */\n        void attachOnInit(void (*funcOnInit)(void)) {\n                pFuncOnInit = funcOnInit;\n        };\n\nprotected:\n        /** @name HIDUniversal implementation */\n        /**\n         * Used to parse USB HID data.\n         * @param hid       Pointer to the HID class.\n         * @param is_rpt_id Only used for Hubs.\n         * @param len       The length of the incoming data.\n         * @param buf       Pointer to the data buffer.\n         */\n        virtual void ParseHIDData(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {\n                if (HIDUniversal::VID == PS4_VID && HIDUniversal::PID == PS4_PID)\n                        PS4Parser::Parse(len, buf);\n        };\n\n        /**\n         * Called when a device is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         */\n        virtual uint8_t OnInitSuccessful() {\n                if (HIDUniversal::VID == PS4_VID && HIDUniversal::PID == PS4_PID) {\n                        PS4Parser::Reset();\n                        if (pFuncOnInit)\n                                pFuncOnInit(); // Call the user function\n                        else\n                                setLed(Blue);\n                };\n                return 0;\n        };\n        /**@}*/\n\n        /** @name PS4Parser implementation */\n        virtual void sendOutputReport(PS4Output *output) { // Source: https://github.com/chrippa/ds4drv\n                uint8_t buf[32];\n                memset(buf, 0, sizeof(buf));\n\n                buf[0] = 0x05; // Report ID\n                buf[1]= 0xFF;\n\n                buf[4] = output->smallRumble; // Small Rumble\n                buf[5] = output->bigRumble; // Big rumble\n\n                buf[6] = output->r; // Red\n                buf[7] = output->g; // Green\n                buf[8] = output->b; // Blue\n\n                buf[9] = output->flashOn; // Time to flash bright (255 = 2.5 seconds)\n                buf[10] = output->flashOff; // Time to flash dark (255 = 2.5 seconds)\n\n                output->reportChanged = false;\n\n                // The PS4 console actually set the four last bytes to a CRC32 checksum, but it seems like it is actually not needed\n\n                pUsb->outTransfer(bAddress, epInfo[ hidInterfaces[0].epIndex[epInterruptOutIndex] ].epAddr, sizeof(buf), buf);\n        };\n        /**@}*/\n\n        /** @name USBDeviceConfig implementation */\n        /**\n         * Used by the USB core to check what this driver support.\n         * @param  vid The device's VID.\n         * @param  pid The device's PID.\n         * @return     Returns true if the device's VID and PID matches this driver.\n         */\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                return (vid == PS4_VID && pid == PS4_PID);\n        };\n        /**@}*/\n\nprivate:\n        void (*pFuncOnInit)(void); // Pointer to function called in onInit()\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/PSBuzz.cpp",
    "content": "/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#include \"PSBuzz.h\"\n\n// To enable serial debugging see \"settings.h\"\n//#define PRINTREPORT // Uncomment to print the report send by the PS Buzz Controllers\n\nvoid PSBuzz::ParseHIDData(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {\n        if (HIDUniversal::VID == PSBUZZ_VID && HIDUniversal::PID == PSBUZZ_PID && len > 2 && buf) {\n#ifdef PRINTREPORT\n                Notify(PSTR(\"\\r\\n\"), 0x80);\n                for (uint8_t i = 0; i < len; i++) {\n                        D_PrintHex<uint8_t > (buf[i], 0x80);\n                        Notify(PSTR(\" \"), 0x80);\n                }\n#endif\n                memcpy(&psbuzzButtons, buf + 2, min((uint8_t)(len - 2), sizeof(psbuzzButtons)));\n\n                if (psbuzzButtons.val != oldButtonState.val) { // Check if anything has changed\n                        buttonClickState.val = psbuzzButtons.val & ~oldButtonState.val; // Update click state variable\n                        oldButtonState.val = psbuzzButtons.val;\n                }\n        }\n};\n\nuint8_t PSBuzz::OnInitSuccessful() {\n        if (HIDUniversal::VID == PSBUZZ_VID && HIDUniversal::PID == PSBUZZ_PID) {\n                Reset();\n                if (pFuncOnInit)\n                        pFuncOnInit(); // Call the user function\n                else\n                        setLedOnAll(); // Turn the LED on, on all four controllers\n        };\n        return 0;\n};\n\nbool PSBuzz::getButtonPress(ButtonEnum b, uint8_t controller) {\n        return psbuzzButtons.val & (1UL << (b + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller\n};\n\nbool PSBuzz::getButtonClick(ButtonEnum b, uint8_t controller) {\n        uint32_t mask = (1UL << (b + 5 * controller)); // Each controller uses 5 bits, so the value is shifted 5 for each controller\n        bool click = buttonClickState.val & mask;\n        buttonClickState.val &= ~mask; // Clear \"click\" event\n        return click;\n};\n\n// Source: http://www.developerfusion.com/article/84338/making-usb-c-friendly/ and https://github.com/torvalds/linux/blob/master/drivers/hid/hid-sony.c\nvoid PSBuzz::setLedRaw(bool value, uint8_t controller) {\n        ledState[controller] = value; // Save value for next time it is called\n\n        uint8_t buf[7];\n        buf[0] = 0x00;\n        buf[1] = ledState[0] ? 0xFF : 0x00;\n        buf[2] = ledState[1] ? 0xFF : 0x00;\n        buf[3] = ledState[2] ? 0xFF : 0x00;\n        buf[4] = ledState[3] ? 0xFF : 0x00;\n        buf[5] = 0x00;\n        buf[6] = 0x00;\n\n        PSBuzz_Command(buf, sizeof(buf));\n};\n\nvoid PSBuzz::PSBuzz_Command(uint8_t *data, uint16_t nbytes) {\n        // bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x00), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)\n        pUsb->ctrlReq(bAddress, epInfo[0].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);\n};\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/PSBuzz.h",
    "content": "/* Copyright (C) 2014 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _psbuzz_h_\n#define _psbuzz_h_\n\n#include \"hiduniversal.h\"\n#include \"controllerEnums.h\"\n\n#define PSBUZZ_VID 0x054C // Sony Corporation\n#define PSBUZZ_PID 0x1000 // PS Buzz Controller\n\n/** Struct used to easily read the different buttons on the controllers */\nunion PSBUZZButtons {\n        struct {\n                uint8_t red : 1;\n                uint8_t yellow : 1;\n                uint8_t green : 1;\n                uint8_t orange : 1;\n                uint8_t blue : 1;\n        } __attribute__((packed)) btn[4];\n        uint32_t val : 20;\n} __attribute__((packed));\n\n/**\n * This class implements support for the PS Buzz controllers via USB.\n * It uses the HIDUniversal class for all the USB communication.\n */\nclass PSBuzz : public HIDUniversal {\npublic:\n        /**\n         * Constructor for the PSBuzz class.\n         * @param  p   Pointer to the USB class instance.\n         */\n        PSBuzz(USB *p) :\n        HIDUniversal(p) {\n                Reset();\n        };\n\n        /**\n         * Used to check if a PS Buzz controller is connected.\n         * @return Returns true if it is connected.\n         */\n        bool connected() {\n                return HIDUniversal::isReady() && HIDUniversal::VID == PSBUZZ_VID && HIDUniversal::PID == PSBUZZ_PID;\n        };\n\n        /**\n         * Used to call your own function when the device is successfully initialized.\n         * @param funcOnInit Function to call.\n         */\n        void attachOnInit(void (*funcOnInit)(void)) {\n                pFuncOnInit = funcOnInit;\n        };\n\n        /** @name PS Buzzer Controller functions */\n        /**\n         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.\n         *\n         * While getButtonClick(ButtonEnum b) will only return it once.\n         *\n         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),\n         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).\n         * @param  b          ::ButtonEnum to read.\n         * @param  controller The controller to read from. Default to 0.\n         * @return            getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.\n         */\n        bool getButtonPress(ButtonEnum b, uint8_t controller = 0);\n        bool getButtonClick(ButtonEnum b, uint8_t controller = 0);\n        /**@}*/\n        /** @name PS Buzzer Controller functions */\n        /**\n         * Set LED value without using ::LEDEnum.\n         * @param value See: ::LEDEnum.\n         */\n        /**\n         * Set LED values directly.\n         * @param value      Used to set whenever the LED should be on or off\n         * @param controller The controller to control. Defaults to 0.\n         */\n        void setLedRaw(bool value, uint8_t controller = 0);\n\n        /** Turn all LEDs off. */\n        void setLedOffAll() {\n                for (uint8_t i = 1; i < 4; i++) // Skip first as it will be set in setLedRaw\n                        ledState[i] = false; // Just an easy way to set all four off at the same time\n                setLedRaw(false); // Turn the LED off, on all four controllers\n        };\n\n        /**\n         * Turn the LED off on a specific controller.\n         * @param controller The controller to turn off. Defaults to 0.\n         */\n        void setLedOff(uint8_t controller = 0) {\n                setLedRaw(false, controller);\n        };\n\n\n        /** Turn all LEDs on. */\n        void setLedOnAll() {\n                for (uint8_t i = 1; i < 4; i++) // Skip first as it will be set in setLedRaw\n                        ledState[i] = true; // Just an easy way to set all four off at the same time\n                setLedRaw(true); // Turn the LED on, on all four controllers\n        };\n\n        /**\n         * Turn the LED on on a specific controller.\n         * @param controller The controller to turn off. Defaults to 0.\n         */\n        void setLedOn(uint8_t controller = 0) {\n                setLedRaw(true, controller);\n        };\n\n        /**\n         * Toggle the LED on a specific controller.\n         * @param controller The controller to turn off. Defaults to 0.\n         */\n        void setLedToggle(uint8_t controller = 0) {\n                setLedRaw(!ledState[controller], controller);\n        };\n        /**@}*/\n\nprotected:\n        /** @name HIDUniversal implementation */\n        /**\n         * Used to parse USB HID data.\n         * @param hid       Pointer to the HID class.\n         * @param is_rpt_id Only used for Hubs.\n         * @param len       The length of the incoming data.\n         * @param buf       Pointer to the data buffer.\n         */\n        void ParseHIDData(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);\n\n        /**\n         * Called when a device is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         */\n        uint8_t OnInitSuccessful();\n        /**@}*/\n\n        /** Used to reset the different buffers to their default values */\n        void Reset() {\n                psbuzzButtons.val = 0;\n                oldButtonState.val = 0;\n                buttonClickState.val = 0;\n                for (uint8_t i = 0; i < sizeof(ledState); i++)\n                        ledState[i] = 0;\n        };\n\n        /** @name USBDeviceConfig implementation */\n        /**\n         * Used by the USB core to check what this driver support.\n         * @param  vid The device's VID.\n         * @param  pid The device's PID.\n         * @return     Returns true if the device's VID and PID matches this driver.\n         */\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                return (vid == PSBUZZ_VID && pid == PSBUZZ_PID);\n        };\n        /**@}*/\n\nprivate:\n        void (*pFuncOnInit)(void); // Pointer to function called in onInit()\n\n        void PSBuzz_Command(uint8_t *data, uint16_t nbytes);\n\n        PSBUZZButtons psbuzzButtons, oldButtonState, buttonClickState;\n        bool ledState[4];\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/README.md",
    "content": "# USB Host Library Rev.2.0\n\nThe code is released under the GNU General Public License.\n__________\n\n# Summary\nThis is Revision 2.0 of MAX3421E-based USB Host Shield Library for AVR's.\n\nProject main web site is: <http://www.circuitsathome.com>.\n\nSome information can also be found at: <http://blog.tkjelectronics.dk/>.\n\nThe shield can be purchased at the main site: <https://www.circuitsathome.com/arduino_usb_host_shield_projects/> or from [TKJ Electronics](http://tkjelectronics.com/): <http://shop.tkjelectronics.dk/product_info.php?products_id=43>.\n\n![USB Host Shield](http://shop.tkjelectronics.dk/images/USB_Host_Shield1.jpg)\n\nFor more information about the hardware see the [Hardware Manual](http://www.circuitsathome.com/usb-host-shield-hardware-manual).\n\n# Developed By\n\n* __Oleg Mazurov, Circuits@Home__ - <mazurov@circuitsathome.com>\n* __Alexei Glushchenko, Circuits@Home__ - <alex-gl@mail.ru>\n    * Developers of the USB Core, HID, FTDI, ADK, ACM, and PL2303 libraries\n* __Kristian Lauszus, TKJ Electronics__ - <kristianl@tkjelectronics.com>\n    * Developer of the [BTD](#bluetooth-libraries), [BTHID](#bthid-library), [SPP](#spp-library), [PS4](#ps4-library), [PS3](#ps3-library), [Wii](#wii-library), [Xbox](#xbox-library), and [PSBuzz](#ps-buzz-library) libraries\n* __Andrew Kroll__ - <xxxajk@gmail.com>\n    * Major contributor to mass storage code\n* __guruthree__\n    * [Xbox ONE](#xbox-one-library) controller support\n\n# Donate\n\nHelp yourself by helping us support you! Many thousands of hours have been spent developing the USB Host Shield library. Since you find it useful, please consider donating via the button below. Donations will allow us to support you by ensuring hardware that you have can be acquired in order to add support for your microcontroller board.\n\n<a href=\"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=donate@circuitsathome.com&amp;lc=US&amp;item_name=Donate%20to%20the%20USB%20Host%20Library%20project&amp;no_note=0&amp;currency_code=USD&amp;bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHostedGuest\"><img src=\"https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif\" alt=\"PayPal - The safer, easier way to pay online!\" /></a>\n\n# Table of Contents\n\n* [How to include the library](#how-to-include-the-library)\n    * [Arduino Library Manager](#arduino-library-manager)\n    * [Manual installation](#manual-installation)\n* [How to use the library](#how-to-use-the-library)\n    * [Documentation](#documentation)\n    * [Enable debugging](#enable-debugging)\n    * [Boards](#boards)\n    * [Bluetooth libraries](#bluetooth-libraries)\n    * [BTHID library](#bthid-library)\n    * [SPP library](#spp-library)\n    * [PS4 Library](#ps4-library)\n    * [PS3 Library](#ps3-library)\n    * [Xbox Libraries](#xbox-libraries)\n        * [Xbox library](#xbox-library)\n        * [Xbox 360 Library](#xbox-360-library)\n        * [Xbox ONE Library](#xbox-one-library)\n    * [Wii library](#wii-library)\n    * [PS Buzz Library](#ps-buzz-library)\n* [Interface modifications](#interface-modifications)\n* [FAQ](#faq)\n\n# How to include the library\n\n### Arduino Library Manager\n\nFirst install Arduino IDE version 1.6.2 or newer, then simply use the Arduino Library Manager to install the library.\n\nPlease see the following page for instructions: <http://www.arduino.cc/en/Guide/Libraries#toc3>.\n\n### Manual installation\n\nFirst download the library by clicking on the following link: <https://github.com/felis/USB_Host_Shield_2.0/archive/master.zip>.\n\nThen uncompress the zip folder and rename the directory to \"USB\\_Host\\_Shield\\_20\", as any special characters are not supported by the Arduino IDE.\n\nNow open up the Arduino IDE and open \"File>Preferences\". There you will see the location of your sketchbook. Open that directory and create a directory called \"libraries\" inside that directory.\nNow move the \"USB\\_Host\\_Shield\\_20\" directory to the \"libraries\" directory.\n\nThe final structure should look like this:\n\n* Arduino/\n    * libraries/\n        * USB\\_Host\\_Shield\\_20/\n\nNow quit the Arduino IDE and reopen it.\n\nNow you should be able to go open all the examples codes by navigating to \"File>Examples>USB\\_Host\\_Shield\\_20\" and then select the example you will like to open.\n\nFor more information visit the following sites: <http://arduino.cc/en/Guide/Libraries> and <https://learn.adafruit.com/adafruit-all-about-arduino-libraries-install-use>.\n\n# How to use the library\n\n### Documentation\n\nDocumentation for the library can be found at the following link: <http://felis.github.com/USB_Host_Shield_2.0/>.\n\n### Enable debugging\n\nBy default serial debugging is disabled. To turn it on simply change ```ENABLE_UHS_DEBUGGING``` to 1 in [settings.h](settings.h) like so:\n\n```C++\n#define ENABLE_UHS_DEBUGGING 1\n```\n\n### Boards\n\nCurrently the following boards are supported by the library:\n\n* All official Arduino AVR boards (Uno, Duemilanove, Mega, Mega 2560, Mega ADK, Leonardo etc.)\n* Arduino Due, Intel Galileo, Intel Galileo 2, and Intel Edison\n    * Note that the Intel Galileo uses pin 2 and 3 as INT and SS pin respectively by default, so some modifications to the shield are needed. See the \"Interface modifications\" section in the [hardware manual](https://www.circuitsathome.com/usb-host-shield-hardware-manual) for more information.\n* Teensy (Teensy++ 1.0, Teensy 2.0, Teensy++ 2.0, and Teensy 3.x)\n    * Note if you are using the Teensy 3.x you should download this SPI library as well: <https://github.com/xxxajk/spi4teensy3>. You should then add ```#include <spi4teensy3.h>``` to your .ino file.\n* Balanduino\n* Sanguino\n* Black Widdow\n* RedBearLab nRF51822\n* Digilent chipKIT\n    * Please see: <http://www.circuitsathome.com/mcu/usb/running-usb-host-code-on-digilent-chipkit-board>.\n\nThe following boards need to be activated manually in [settings.h](settings.h):\n\n* Arduino Mega ADK\n    * If you are using Arduino 1.5.5 or newer there is no need to activate the Arduino Mega ADK manually\n* Black Widdow\n\nSimply set the corresponding value to 1 instead of 0.\n\n### [Bluetooth libraries](BTD.cpp)\n\nThe [BTD library](BTD.cpp) is a general purpose library for an ordinary Bluetooth dongle.\nThis library make it easy to add support for different Bluetooth services like a PS3 or a Wii controller or SPP which is a virtual serial port via Bluetooth.\nSome different examples can be found in the [example directory](examples/Bluetooth).\n\nThe BTD library also makes it possible to use multiple services at once, the following example sketch is an example of this:\n[PS3SPP.ino](examples/Bluetooth/PS3SPP/PS3SPP.ino).\n\n### [BTHID library](BTHID.cpp)\n\nThe [Bluetooth HID library](BTHID.cpp) allows you to connect HID devices via Bluetooth to the USB Host Shield.\n\nCurrently HID mice and keyboards are supported.\n\nIt uses the standard Boot protocol by default, but it is also able to use the Report protocol as well. You would simply have to call ```setProtocolMode()``` and then parse ```HID_RPT_PROTOCOL``` as an argument. You will then have to modify the parser for your device. See the example: [BTHID.ino](examples/Bluetooth/BTHID/BTHID.ino) for more information.\n\nThe [PS4 library](#ps4-library) also uses this class to handle all Bluetooth communication.\n\nFor information see the following blog post: <http://blog.tkjelectronics.dk/2013/12/bluetooth-hid-devices-now-supported-by-the-usb-host-library/>.\n\n### [SPP library](SPP.cpp)\n\nSPP stands for \"Serial Port Profile\" and is a Bluetooth protocol that implements a virtual comport which allows you to send data back and forth from your computer/phone to your Arduino via Bluetooth.\nIt has been tested successfully on Windows, Mac OS X, Linux, and Android.\n\nTake a look at the [SPP.ino](examples/Bluetooth/SPP/SPP.ino) example for more information.\n\nMore information can be found at these blog posts:\n\n* <http://www.circuitsathome.com/mcu/bluetooth-rfcommspp-service-support-for-usb-host-2-0-library-released>\n* <http://blog.tkjelectronics.dk/2012/07/rfcommspp-library-for-arduino/>\n\nTo implement the SPP protocol I used a Bluetooth sniffing tool called [PacketLogger](http://www.tkjelectronics.com/uploads/PacketLogger.zip) developed by Apple.\nIt enables me to see the Bluetooth communication between my Mac and any device.\n\n### PS4 Library\n\nThe PS4BT library is split up into the [PS4BT](PS4BT.h) and the [PS4USB](PS4USB.h) library. These allow you to use the Sony PS4 controller via Bluetooth and USB.\n\nThe [PS4BT.ino](examples/Bluetooth/PS4BT/PS4BT.ino) and [PS4USB.ino](examples/PS4USB/PS4USB.ino) examples shows how to easily read the buttons, joysticks, touchpad and IMU on the controller via Bluetooth and USB respectively. It is also possible to control the rumble and light on the controller and get the battery level.\n\nBefore you can use the PS4 controller via Bluetooth you will need to pair with it.\n\nSimply create the PS4BT instance like so: ```PS4BT PS4(&Btd, PAIR);``` and then hold down the Share button and then hold down the PS without releasing the Share button. The PS4 controller will then start to blink rapidly indicating that it is in paring mode.\n\nIt should then automatically pair the dongle with your controller. This only have to be done once.\n\nFor information see the following blog post: <http://blog.tkjelectronics.dk/2014/01/ps4-controller-now-supported-by-the-usb-host-library/>.\n\nAlso check out this excellent Wiki by Frank Zhao about the PS4 controller: <http://eleccelerator.com/wiki/index.php?title=DualShock_4> and this Linux driver: <https://github.com/chrippa/ds4drv>.\n\n### PS3 Library\n\nThese libraries consist of the [PS3BT](PS3BT.cpp) and [PS3USB](PS3USB.cpp). These libraries allows you to use a Dualshock 3, Navigation or a Motion controller with the USB Host Shield both via Bluetooth and USB.\n\nIn order to use your Playstation controller via Bluetooth you have to set the Bluetooth address of the dongle internally to your PS3 Controller. This can be achieved by first plugging in the Bluetooth dongle and wait a few seconds. Now plug in the controller via USB and wait until the LEDs start to flash. The library has now written the Bluetooth address of the dongle to the PS3 controller.\n\nFinally simply plug in the Bluetooth dongle again and press PS on the PS3 controller. After a few seconds it should be connected to the dongle and ready to use.\n\n__Note:__ You will have to plug in the Bluetooth dongle before connecting the controller, as the library needs to read the address of the dongle. Alternatively you could set it in code like so: [PS3BT.ino#L20](examples/Bluetooth/PS3BT/PS3BT.ino#L20).\n\nFor more information about the PS3 protocol see the official wiki: <https://github.com/felis/USB_Host_Shield_2.0/wiki/PS3-Information>.\n\nAlso take a look at the blog posts:\n\n* <http://blog.tkjelectronics.dk/2012/01/ps3-controller-bt-library-for-arduino/>\n* <http://www.circuitsathome.com/mcu/sony-ps3-controller-support-added-to-usb-host-library>\n* <http://www.circuitsathome.com/mcu/arduino/interfacing-ps3-controllers-via-usb>\n\nA special thanks go to the following people:\n\n1. _Richard Ibbotson_ who made this excellent guide: <https://www.circuitsathome.com/mcu/ps3-and-wiimote-game-controllers-on-the-arduino-host-shield-part-1/>\n2. _Tomoyuki Tanaka_ for releasing his code for the Arduino USB Host shield connected to the wiimote: <http://www.circuitsathome.com/mcu/rc-car-controlled-by-wii-remote-on-arduino>\n\nAlso a big thanks all the people behind these sites about the Motion controller:\n\n* <http://thp.io/2010/psmove/>\n* <http://www.copenhagengamecollective.org/unimove/>\n* <https://github.com/thp/psmoveapi>\n* <http://code.google.com/p/moveonpc/>\n\n### Xbox Libraries\n\nThe library supports both the original Xbox controller via USB and the Xbox 360 controller both via USB and wirelessly.\n\n#### Xbox library\n\nThe [XBOXOLD](XBOXOLD.cpp) class implements support for the original Xbox controller via USB.\n\nAll the information are from the following sites:\n\n* <https://github.com/torvalds/linux/blob/master/Documentation/input/devices/xpad.rst>\n* <https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c>\n* <http://euc.jp/periphs/xbox-controller.ja.html>\n* <https://github.com/Grumbel/xboxdrv/blob/stable/PROTOCOL#L15>\n\n#### Xbox 360 Library\n\nThe library support one Xbox 360 via USB or up to four Xbox 360 controllers wirelessly by using a [Xbox 360 wireless receiver](http://blog.tkjelectronics.dk/wp-content/uploads/xbox360-wireless-receiver.jpg).\n\nTo use it via USB use the [XBOXUSB](XBOXUSB.cpp) library or to use it wirelessly use the [XBOXRECV](XBOXRECV.cpp) library.\n\n__Note that a Wireless controller can NOT be used via USB!__\n\nExamples code can be found in the [examples directory](examples/Xbox).\n\nAlso see the following blog posts:\n\n* <http://www.circuitsathome.com/mcu/xbox360-controller-support-added-to-usb-host-shield-2-0-library>\n* <http://blog.tkjelectronics.dk/2012/07/xbox-360-controller-support-added-to-the-usb-host-library/>\n* <http://blog.tkjelectronics.dk/2012/12/xbox-360-receiver-added-to-the-usb-host-library/>\n\nAll the information regarding the Xbox 360 controller protocol are form these sites:\n\n* <http://tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/UsbInfo>\n* <http://tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/WirelessUsbInfo>\n* <https://github.com/Grumbel/xboxdrv/blob/stable/PROTOCOL>\n\n#### Xbox ONE Library\n\nAn Xbox ONE controller is supported via USB in the [XBOXONE](XBOXONE.cpp) class. It is heavily based on the 360 library above. In addition to cross referencing the above, information on the protocol was found at:\n\n* <https://github.com/quantus/xbox-one-controller-protocol>\n* <https://github.com/torvalds/linux/blob/master/drivers/input/joystick/xpad.c>\n* <https://github.com/kylelemons/xbox/blob/master/xbox.go>\n\n### [Wii library](Wii.cpp)\n\nThe [Wii](Wii.cpp) library support the Wiimote, but also the Nunchuch and Motion Plus extensions via Bluetooth. The Wii U Pro Controller and Wii Balance Board are also supported via Bluetooth.\n\nFirst you have to pair with the controller, this is done automatically by the library if you create the instance like so:\n\n```C++\nWII Wii(&Btd, PAIR);\n```\n\nAnd then press 1 & 2 at once on the Wiimote or the SYNC buttons if you are using a Wii U Pro Controller or a Wii Balance Board.\n\nAfter that you can simply create the instance like so:\n\n```C++\nWII Wii(&Btd);\n```\n\nThen just press any button on the Wiimote and it will then connect to the dongle.\n\nTake a look at the example for more information: [Wii.ino](examples/Bluetooth/Wii/Wii.ino).\n\nAlso take a look at the blog post:\n\n* <http://blog.tkjelectronics.dk/2012/08/wiimote-added-to-usb-host-library/>\n\nThe Wii IR camera can also be used, but you will have to activate the code for it manually as it is quite large. Simply set ```ENABLE_WII_IR_CAMERA``` to 1 in [settings.h](settings.h).\n\nThe [WiiIRCamera.ino](examples/Bluetooth/WiiIRCamera/WiiIRCamera.ino) example shows how it can be used.\n\nAll the information about the Wii controllers are from these sites:\n\n* <http://wiibrew.org/wiki/Wiimote>\n* <http://wiibrew.org/wiki/Wiimote/Extension_Controllers>\n* <http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Nunchuck>\n* <http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Wii_Motion_Plus>\n* <http://wiibrew.org/wiki/Wii_Balance_Board>\n* The old library created by _Tomoyuki Tanaka_: <https://github.com/moyuchin/WiiRemote_on_Arduino> also helped a lot.\n\n### [PS Buzz Library](PSBuzz.cpp)\n\nThis library implements support for the Playstation Buzz controllers via USB.\n\nIt is essentially just a wrapper around the [HIDUniversal](hiduniversal.cpp) which takes care of the initializing and reading of the controllers. The [PSBuzz](PSBuzz.cpp) class simply inherits this and parses the data, so it is easy for users to read the buttons and turn the big red button on the controllers on and off.\n\nThe example [PSBuzz.ino](examples/PSBuzz/PSBuzz.ino) shows how one can do this with just a few lines of code.\n\nMore information about the controller can be found at the following sites:\n\n* http://www.developerfusion.com/article/84338/making-usb-c-friendly/\n* https://github.com/torvalds/linux/blob/master/drivers/hid/hid-sony.c\n\n# Interface modifications\n\nThe shield is using SPI for communicating with the MAX3421E USB host controller. It uses the SCK, MISO and MOSI pins via the ICSP on your board.\n\nNote this means that it uses pin 13, 12, 11 on an Arduino Uno, so these pins can not be used for anything else than SPI communication!\n\nFurthermore it uses one pin as SS and one INT pin. These are by default located on pin 10 and 9 respectively. They can easily be reconfigured in case you need to use them for something else by cutting the jumper on the shield and then solder a wire from the pad to the new pin.\n\nAfter that you need modify the following entry in [UsbCore.h](UsbCore.h):\n\n```C++\ntypedef MAX3421e<P10, P9> MAX3421E;\n```\n\nFor instance if you have rerouted SS to pin 7 it should read:\n\n```C++\ntypedef MAX3421e<P7, P9> MAX3421E;\n```\n\nSee the \"Interface modifications\" section in the [hardware manual](https://www.circuitsathome.com/usb-host-shield-hardware-manual) for more information.\n\n# FAQ\n\n> When I plug my device into the USB connector nothing happens?\n\n* Try to connect a external power supply to the Arduino - this solves the problem in most cases.\n* You can also use a powered hub between the device and the USB Host Shield. You should then include the USB hub library: ```#include <usbhub.h>``` and create the instance like so: ```USBHub Hub1(&Usb);```.\n\n> When I connecting my PS3 controller I get a output like this:\n\n```\nDualshock 3 Controller Enabled\n\nLeftHatX: 0 LeftHatY: 0 RightHatX: 0 RightHatY: 0\nLeftHatX: 0 LeftHatY: 0 RightHatX: 0 RightHatY: 0\nLeftHatX: 0 LeftHatY: 0 RightHatX: 0 RightHatY: 0\nLeftHatX: 0 LeftHatY: 0 RightHatX: 0 RightHatY: 0\nLeftHatX: 0 LeftHatY: 0 RightHatX: 0 RightHatY: 0\n```\n\n* This means that your dongle does not support 2.0+EDR, so you will need another dongle. Please see the following [list](https://github.com/felis/USB_Host_Shield_2.0/wiki/Bluetooth-dongles) for tested working dongles.\n\n> When compiling I am getting the following error: \"fatal error: SPI.h: No such file or directory\".\n\n* Please make sure to include the SPI library like so: ```#include <SPI.h>``` in your .ino file.\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/SPP.cpp",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#include \"SPP.h\"\n// To enable serial debugging see \"settings.h\"\n//#define EXTRADEBUG // Uncomment to get even more debugging data\n//#define PRINTREPORT // Uncomment to print the report sent to the Arduino\n\n/*\n * CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0.\n */\nconst uint8_t rfcomm_crc_table[256] PROGMEM = {/* reversed, 8-bit, poly=0x07 */\n        0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,\n        0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,\n        0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,\n        0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,\n        0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,\n        0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,\n        0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,\n        0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,\n        0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,\n        0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,\n        0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,\n        0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,\n        0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,\n        0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,\n        0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,\n        0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF\n};\n\nSPP::SPP(BTD *p, const char* name, const char* pin) :\nBluetoothService(p) // Pointer to BTD class instance - mandatory\n{\n        pBtd->btdName = name;\n        pBtd->btdPin = pin;\n\n        /* Set device cid for the SDP and RFCOMM channelse */\n        sdp_dcid[0] = 0x50; // 0x0050\n        sdp_dcid[1] = 0x00;\n        rfcomm_dcid[0] = 0x51; // 0x0051\n        rfcomm_dcid[1] = 0x00;\n\n        Reset();\n}\n\nvoid SPP::Reset() {\n        connected = false;\n        RFCOMMConnected = false;\n        SDPConnected = false;\n        waitForLastCommand = false;\n        l2cap_sdp_state = L2CAP_SDP_WAIT;\n        l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT;\n        l2cap_event_flag = 0;\n        sppIndex = 0;\n        creditSent = false;\n}\n\nvoid SPP::disconnect() {\n        connected = false;\n        // First the two L2CAP channels has to be disconnected and then the HCI connection\n        if(RFCOMMConnected)\n                pBtd->l2cap_disconnection_request(hci_handle, ++identifier, rfcomm_scid, rfcomm_dcid);\n        if(RFCOMMConnected && SDPConnected)\n                delay(1); // Add delay between commands\n        if(SDPConnected)\n                pBtd->l2cap_disconnection_request(hci_handle, ++identifier, sdp_scid, sdp_dcid);\n        l2cap_sdp_state = L2CAP_DISCONNECT_RESPONSE;\n}\n\nvoid SPP::ACLData(uint8_t* l2capinbuf) {\n        if(!connected) {\n                if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {\n                        if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM && !pBtd->sdpConnectionClaimed) {\n                                pBtd->sdpConnectionClaimed = true;\n                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection\n                                l2cap_sdp_state = L2CAP_SDP_WAIT; // Reset state\n                        } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM && !pBtd->rfcommConnectionClaimed) {\n                                pBtd->rfcommConnectionClaimed = true;\n                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection\n                                l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT; // Reset state\n                        }\n                }\n        }\n\n        if(checkHciHandle(l2capinbuf, hci_handle)) { // acl_handle_ok\n                if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U\n                        if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nL2CAP Command Rejected - Reason: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);\n                                Notify(PSTR(\" Data: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);\n#endif\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {\n#ifdef EXTRADEBUG\n                                Notify(PSTR(\"\\r\\nL2CAP Connection Request - PSM: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);\n                                Notify(PSTR(\" SCID: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);\n                                Notify(PSTR(\" Identifier: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);\n#endif\n                                if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == SDP_PSM) { // It doesn't matter if it receives another reqeust, since it waits for the channel to disconnect in the L2CAP_SDP_DONE state, and the l2cap_event_flag will be cleared if so\n                                        identifier = l2capinbuf[9];\n                                        sdp_scid[0] = l2capinbuf[14];\n                                        sdp_scid[1] = l2capinbuf[15];\n                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST);\n                                } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == RFCOMM_PSM) { // ----- || -----\n                                        identifier = l2capinbuf[9];\n                                        rfcomm_scid[0] = l2capinbuf[14];\n                                        rfcomm_scid[1] = l2capinbuf[15];\n                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST);\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {\n                                if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success\n                                        if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nSDP Configuration Complete\"), 0x80);\n                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS);\n                                        } else if(l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nRFCOMM Configuration Complete\"), 0x80);\n                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS);\n                                        }\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {\n                                if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {\n                                        //Notify(PSTR(\"\\r\\nSDP Configuration Request\"), 0x80);\n                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], sdp_scid);\n                                } else if(l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) {\n                                        //Notify(PSTR(\"\\r\\nRFCOMM Configuration Request\"), 0x80);\n                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], rfcomm_scid);\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {\n                                if(l2capinbuf[12] == sdp_dcid[0] && l2capinbuf[13] == sdp_dcid[1]) {\n                                        //Notify(PSTR(\"\\r\\nDisconnect Request: SDP Channel\"), 0x80);\n                                        identifier = l2capinbuf[9];\n                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST);\n                                } else if(l2capinbuf[12] == rfcomm_dcid[0] && l2capinbuf[13] == rfcomm_dcid[1]) {\n                                        //Notify(PSTR(\"\\r\\nDisconnect Request: RFCOMM Channel\"), 0x80);\n                                        identifier = l2capinbuf[9];\n                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST);\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {\n                                if(l2capinbuf[12] == sdp_scid[0] && l2capinbuf[13] == sdp_scid[1]) {\n                                        //Notify(PSTR(\"\\r\\nDisconnect Response: SDP Channel\"), 0x80);\n                                        identifier = l2capinbuf[9];\n                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RESPONSE);\n                                } else if(l2capinbuf[12] == rfcomm_scid[0] && l2capinbuf[13] == rfcomm_scid[1]) {\n                                        //Notify(PSTR(\"\\r\\nDisconnect Response: RFCOMM Channel\"), 0x80);\n                                        identifier = l2capinbuf[9];\n                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_RESPONSE);\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_INFORMATION_REQUEST) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nInformation request\"), 0x80);\n#endif\n                                identifier = l2capinbuf[9];\n                                pBtd->l2cap_information_response(hci_handle, identifier, l2capinbuf[12], l2capinbuf[13]);\n                        }\n#ifdef EXTRADEBUG\n                        else {\n                                Notify(PSTR(\"\\r\\nL2CAP Unknown Signaling Command: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);\n                        }\n#endif\n                } else if(l2capinbuf[6] == sdp_dcid[0] && l2capinbuf[7] == sdp_dcid[1]) { // SDP\n                        if(l2capinbuf[8] == SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU) {\n                                if(((l2capinbuf[16] << 8 | l2capinbuf[17]) == SERIALPORT_UUID) || ((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000 && (l2capinbuf[18] << 8 | l2capinbuf[19]) == SERIALPORT_UUID)) { // Check if it's sending the full UUID, see: https://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm, we will just check the first four bytes\n                                        if(firstMessage) {\n                                                serialPortResponse1(l2capinbuf[9], l2capinbuf[10]);\n                                                firstMessage = false;\n                                        } else {\n                                                serialPortResponse2(l2capinbuf[9], l2capinbuf[10]); // Serialport continuation state\n                                                firstMessage = true;\n                                        }\n                                } else if(((l2capinbuf[16] << 8 | l2capinbuf[17]) == L2CAP_UUID) || ((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000 && (l2capinbuf[18] << 8 | l2capinbuf[19]) == L2CAP_UUID)) {\n                                        if(firstMessage) {\n                                                l2capResponse1(l2capinbuf[9], l2capinbuf[10]);\n                                                firstMessage = false;\n                                        } else {\n                                                l2capResponse2(l2capinbuf[9], l2capinbuf[10]); // L2CAP continuation state\n                                                firstMessage = true;\n                                        }\n                                } else\n                                        serviceNotSupported(l2capinbuf[9], l2capinbuf[10]); // The service is not supported\n#ifdef EXTRADEBUG\n                                Notify(PSTR(\"\\r\\nUUID: \"), 0x80);\n                                uint16_t uuid;\n                                if((l2capinbuf[16] << 8 | l2capinbuf[17]) == 0x0000) // Check if it's sending the UUID as a 128-bit UUID\n                                        uuid = (l2capinbuf[18] << 8 | l2capinbuf[19]);\n                                else // Short UUID\n                                        uuid = (l2capinbuf[16] << 8 | l2capinbuf[17]);\n                                D_PrintHex<uint16_t > (uuid, 0x80);\n\n                                Notify(PSTR(\"\\r\\nLength: \"), 0x80);\n                                uint16_t length = l2capinbuf[11] << 8 | l2capinbuf[12];\n                                D_PrintHex<uint16_t > (length, 0x80);\n                                Notify(PSTR(\"\\r\\nData: \"), 0x80);\n                                for(uint8_t i = 0; i < length; i++) {\n                                        D_PrintHex<uint8_t > (l2capinbuf[13 + i], 0x80);\n                                        Notify(PSTR(\" \"), 0x80);\n                                }\n#endif\n                        }\n#ifdef EXTRADEBUG\n                        else {\n                                Notify(PSTR(\"\\r\\nUnknown PDU: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);\n                        }\n#endif\n                } else if(l2capinbuf[6] == rfcomm_dcid[0] && l2capinbuf[7] == rfcomm_dcid[1]) { // RFCOMM\n                        rfcommChannel = l2capinbuf[8] & 0xF8;\n                        rfcommDirection = l2capinbuf[8] & 0x04;\n                        rfcommCommandResponse = l2capinbuf[8] & 0x02;\n                        rfcommChannelType = l2capinbuf[9] & 0xEF;\n                        rfcommPfBit = l2capinbuf[9] & 0x10;\n\n                        if(rfcommChannel >> 3 != 0x00)\n                                rfcommChannelConnection = rfcommChannel;\n\n#ifdef EXTRADEBUG\n                        Notify(PSTR(\"\\r\\nRFCOMM Channel: \"), 0x80);\n                        D_PrintHex<uint8_t > (rfcommChannel >> 3, 0x80);\n                        Notify(PSTR(\" Direction: \"), 0x80);\n                        D_PrintHex<uint8_t > (rfcommDirection >> 2, 0x80);\n                        Notify(PSTR(\" CommandResponse: \"), 0x80);\n                        D_PrintHex<uint8_t > (rfcommCommandResponse >> 1, 0x80);\n                        Notify(PSTR(\" ChannelType: \"), 0x80);\n                        D_PrintHex<uint8_t > (rfcommChannelType, 0x80);\n                        Notify(PSTR(\" PF_BIT: \"), 0x80);\n                        D_PrintHex<uint8_t > (rfcommPfBit, 0x80);\n#endif\n                        if(rfcommChannelType == RFCOMM_DISC) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nReceived Disconnect RFCOMM Command on channel: \"), 0x80);\n                                D_PrintHex<uint8_t > (rfcommChannel >> 3, 0x80);\n#endif\n                                connected = false;\n                                sendRfcomm(rfcommChannel, rfcommDirection, rfcommCommandResponse, RFCOMM_UA, rfcommPfBit, rfcommbuf, 0x00); // UA Command\n                        }\n                        if(connected) {\n                                /* Read the incoming message */\n                                if(rfcommChannelType == RFCOMM_UIH && rfcommChannel == rfcommChannelConnection) {\n                                        uint8_t length = l2capinbuf[10] >> 1; // Get length\n                                        uint8_t offset = l2capinbuf[4] - length - 4; // Check if there is credit\n                                        if(checkFcs(&l2capinbuf[8], l2capinbuf[11 + length + offset])) {\n                                                uint8_t i = 0;\n                                                for(; i < length; i++) {\n                                                        if(rfcommAvailable + i >= sizeof (rfcommDataBuffer)) {\n#ifdef DEBUG_USB_HOST\n                                                                Notify(PSTR(\"\\r\\nWarning: Buffer is full!\"), 0x80);\n#endif\n                                                                break;\n                                                        }\n                                                        rfcommDataBuffer[rfcommAvailable + i] = l2capinbuf[11 + i + offset];\n                                                }\n                                                rfcommAvailable += i;\n#ifdef EXTRADEBUG\n                                                Notify(PSTR(\"\\r\\nRFCOMM Data Available: \"), 0x80);\n                                                Notify(rfcommAvailable, 0x80);\n                                                if(offset) {\n                                                        Notify(PSTR(\" - Credit: 0x\"), 0x80);\n                                                        D_PrintHex<uint8_t > (l2capinbuf[11], 0x80);\n                                                }\n#endif\n                                        }\n#ifdef DEBUG_USB_HOST\n                                        else\n                                                Notify(PSTR(\"\\r\\nError in FCS checksum!\"), 0x80);\n#endif\n#ifdef PRINTREPORT // Uncomment \"#define PRINTREPORT\" to print the report send to the Arduino via Bluetooth\n                                        for(uint8_t i = 0; i < length; i++)\n                                                Notifyc(l2capinbuf[i + 11 + offset], 0x80);\n#endif\n                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_RPN_CMD) { // UIH Remote Port Negotiation Command\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nReceived UIH Remote Port Negotiation Command\"), 0x80);\n#endif\n                                        rfcommbuf[0] = BT_RFCOMM_RPN_RSP; // Command\n                                        rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1\n                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1\n                                        rfcommbuf[3] = l2capinbuf[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM\n                                        rfcommbuf[4] = l2capinbuf[15]; // Priority\n                                        rfcommbuf[5] = l2capinbuf[16]; // Timer\n                                        rfcommbuf[6] = l2capinbuf[17]; // Max Fram Size LSB\n                                        rfcommbuf[7] = l2capinbuf[18]; // Max Fram Size MSB\n                                        rfcommbuf[8] = l2capinbuf[19]; // MaxRatransm.\n                                        rfcommbuf[9] = l2capinbuf[20]; // Number of Frames\n                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x0A); // UIH Remote Port Negotiation Response\n                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_CMD) { // UIH Modem Status Command\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nSend UIH Modem Status Response\"), 0x80);\n#endif\n                                        rfcommbuf[0] = BT_RFCOMM_MSC_RSP; // UIH Modem Status Response\n                                        rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1\n                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)\n                                        rfcommbuf[3] = l2capinbuf[14];\n                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x04);\n                                }\n                        } else {\n                                if(rfcommChannelType == RFCOMM_SABM) { // SABM Command - this is sent twice: once for channel 0 and then for the channel to establish\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nReceived SABM Command\"), 0x80);\n#endif\n                                        sendRfcomm(rfcommChannel, rfcommDirection, rfcommCommandResponse, RFCOMM_UA, rfcommPfBit, rfcommbuf, 0x00); // UA Command\n                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_PN_CMD) { // UIH Parameter Negotiation Command\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nReceived UIH Parameter Negotiation Command\"), 0x80);\n#endif\n                                        rfcommbuf[0] = BT_RFCOMM_PN_RSP; // UIH Parameter Negotiation Response\n                                        rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1\n                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1\n                                        rfcommbuf[3] = 0xE0; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM\n                                        rfcommbuf[4] = 0x00; // Priority\n                                        rfcommbuf[5] = 0x00; // Timer\n                                        rfcommbuf[6] = BULK_MAXPKTSIZE - 14; // Max Fram Size LSB - set to the size of received data (50)\n                                        rfcommbuf[7] = 0x00; // Max Fram Size MSB\n                                        rfcommbuf[8] = 0x00; // MaxRatransm.\n                                        rfcommbuf[9] = 0x00; // Number of Frames\n                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x0A);\n                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_CMD) { // UIH Modem Status Command\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nSend UIH Modem Status Response\"), 0x80);\n#endif\n                                        rfcommbuf[0] = BT_RFCOMM_MSC_RSP; // UIH Modem Status Response\n                                        rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1\n                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)\n                                        rfcommbuf[3] = l2capinbuf[14];\n                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x04);\n\n                                        delay(1);\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nSend UIH Modem Status Command\"), 0x80);\n#endif\n                                        rfcommbuf[0] = BT_RFCOMM_MSC_CMD; // UIH Modem Status Command\n                                        rfcommbuf[1] = 2 << 1 | 1; // Length and shiftet like so: length << 1 | 1\n                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: (1 << 0) | (1 << 1) | (0 << 2) | (channel << 3)\n                                        rfcommbuf[3] = 0x8D; // Can receive frames (YES), Ready to Communicate (YES), Ready to Receive (YES), Incomig Call (NO), Data is Value (YES)\n\n                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x04);\n                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_MSC_RSP) { // UIH Modem Status Response\n                                        if(!creditSent) {\n#ifdef DEBUG_USB_HOST\n                                                Notify(PSTR(\"\\r\\nSend UIH Command with credit\"), 0x80);\n#endif\n                                                sendRfcommCredit(rfcommChannelConnection, rfcommDirection, 0, RFCOMM_UIH, 0x10, sizeof (rfcommDataBuffer)); // Send credit\n                                                creditSent = true;\n                                                timer = millis();\n                                                waitForLastCommand = true;\n                                        }\n                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[10] == 0x01) { // UIH Command with credit\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nReceived UIH Command with credit\"), 0x80);\n#endif\n                                } else if(rfcommChannelType == RFCOMM_UIH && l2capinbuf[11] == BT_RFCOMM_RPN_CMD) { // UIH Remote Port Negotiation Command\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nReceived UIH Remote Port Negotiation Command\"), 0x80);\n#endif\n                                        rfcommbuf[0] = BT_RFCOMM_RPN_RSP; // Command\n                                        rfcommbuf[1] = l2capinbuf[12]; // Length and shiftet like so: length << 1 | 1\n                                        rfcommbuf[2] = l2capinbuf[13]; // Channel: channel << 1 | 1\n                                        rfcommbuf[3] = l2capinbuf[14]; // Pre difined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM\n                                        rfcommbuf[4] = l2capinbuf[15]; // Priority\n                                        rfcommbuf[5] = l2capinbuf[16]; // Timer\n                                        rfcommbuf[6] = l2capinbuf[17]; // Max Fram Size LSB\n                                        rfcommbuf[7] = l2capinbuf[18]; // Max Fram Size MSB\n                                        rfcommbuf[8] = l2capinbuf[19]; // MaxRatransm.\n                                        rfcommbuf[9] = l2capinbuf[20]; // Number of Frames\n                                        sendRfcomm(rfcommChannel, rfcommDirection, 0, RFCOMM_UIH, rfcommPfBit, rfcommbuf, 0x0A); // UIH Remote Port Negotiation Response\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nRFCOMM Connection is now established\\r\\n\"), 0x80);\n#endif\n                                        onInit();\n                                }\n#ifdef EXTRADEBUG\n                                else if(rfcommChannelType != RFCOMM_DISC) {\n                                        Notify(PSTR(\"\\r\\nUnsupported RFCOMM Data - ChannelType: \"), 0x80);\n                                        D_PrintHex<uint8_t > (rfcommChannelType, 0x80);\n                                        Notify(PSTR(\" Command: \"), 0x80);\n                                        D_PrintHex<uint8_t > (l2capinbuf[11], 0x80);\n                                }\n#endif\n                        }\n                }\n#ifdef EXTRADEBUG\n                else {\n                        Notify(PSTR(\"\\r\\nUnsupported L2CAP Data - Channel ID: \"), 0x80);\n                        D_PrintHex<uint8_t > (l2capinbuf[7], 0x80);\n                        Notify(PSTR(\" \"), 0x80);\n                        D_PrintHex<uint8_t > (l2capinbuf[6], 0x80);\n                }\n#endif\n                SDP_task();\n                RFCOMM_task();\n        }\n}\n\nvoid SPP::Run() {\n        if(waitForLastCommand && (millis() - timer) > 100) { // We will only wait 100ms and see if the UIH Remote Port Negotiation Command is send, as some deviced don't send it\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nRFCOMM Connection is now established - Automatic\\r\\n\"), 0x80);\n#endif\n                onInit();\n        }\n        send(); // Send all bytes currently in the buffer\n}\n\nvoid SPP::onInit() {\n        creditSent = false;\n        waitForLastCommand = false;\n        connected = true; // The RFCOMM channel is now established\n        sppIndex = 0;\n        if(pFuncOnInit)\n                pFuncOnInit(); // Call the user function\n};\n\nvoid SPP::SDP_task() {\n        switch(l2cap_sdp_state) {\n                case L2CAP_SDP_WAIT:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST)) {\n                                l2cap_clear_flag(L2CAP_FLAG_CONNECTION_SDP_REQUEST); // Clear flag\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nSDP Incoming Connection Request\"), 0x80);\n#endif\n                                pBtd->l2cap_connection_response(hci_handle, identifier, sdp_dcid, sdp_scid, PENDING);\n                                delay(1);\n                                pBtd->l2cap_connection_response(hci_handle, identifier, sdp_dcid, sdp_scid, SUCCESSFUL);\n                                identifier++;\n                                delay(1);\n                                pBtd->l2cap_config_request(hci_handle, identifier, sdp_scid);\n                                l2cap_sdp_state = L2CAP_SDP_SUCCESS;\n                        } else if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST)) {\n                                l2cap_clear_flag(L2CAP_FLAG_DISCONNECT_SDP_REQUEST); // Clear flag\n                                SDPConnected = false;\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nDisconnected SDP Channel\"), 0x80);\n#endif\n                                pBtd->l2cap_disconnection_response(hci_handle, identifier, sdp_dcid, sdp_scid);\n                        }\n                        break;\n                case L2CAP_SDP_SUCCESS:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS)) {\n                                l2cap_clear_flag(L2CAP_FLAG_CONFIG_SDP_SUCCESS); // Clear flag\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nSDP Successfully Configured\"), 0x80);\n#endif\n                                firstMessage = true; // Reset bool\n                                SDPConnected = true;\n                                l2cap_sdp_state = L2CAP_SDP_WAIT;\n                        }\n                        break;\n\n                case L2CAP_DISCONNECT_RESPONSE: // This is for both disconnection response from the RFCOMM and SDP channel if they were connected\n                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_RESPONSE)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nDisconnected L2CAP Connection\"), 0x80);\n#endif\n                                pBtd->hci_disconnect(hci_handle);\n                                hci_handle = -1; // Reset handle\n                                Reset();\n                        }\n                        break;\n        }\n}\n\nvoid SPP::RFCOMM_task() {\n        switch(l2cap_rfcomm_state) {\n                case L2CAP_RFCOMM_WAIT:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST)) {\n                                l2cap_clear_flag(L2CAP_FLAG_CONNECTION_RFCOMM_REQUEST); // Clear flag\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nRFCOMM Incoming Connection Request\"), 0x80);\n#endif\n                                pBtd->l2cap_connection_response(hci_handle, identifier, rfcomm_dcid, rfcomm_scid, PENDING);\n                                delay(1);\n                                pBtd->l2cap_connection_response(hci_handle, identifier, rfcomm_dcid, rfcomm_scid, SUCCESSFUL);\n                                identifier++;\n                                delay(1);\n                                pBtd->l2cap_config_request(hci_handle, identifier, rfcomm_scid);\n                                l2cap_rfcomm_state = L2CAP_RFCOMM_SUCCESS;\n                        } else if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST)) {\n                                l2cap_clear_flag(L2CAP_FLAG_DISCONNECT_RFCOMM_REQUEST); // Clear flag\n                                RFCOMMConnected = false;\n                                connected = false;\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nDisconnected RFCOMM Channel\"), 0x80);\n#endif\n                                pBtd->l2cap_disconnection_response(hci_handle, identifier, rfcomm_dcid, rfcomm_scid);\n                        }\n                        break;\n                case L2CAP_RFCOMM_SUCCESS:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS)) {\n                                l2cap_clear_flag(L2CAP_FLAG_CONFIG_RFCOMM_SUCCESS); // Clear flag\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nRFCOMM Successfully Configured\"), 0x80);\n#endif\n                                rfcommAvailable = 0; // Reset number of bytes available\n                                bytesRead = 0; // Reset number of bytes received\n                                RFCOMMConnected = true;\n                                l2cap_rfcomm_state = L2CAP_RFCOMM_WAIT;\n                        }\n                        break;\n        }\n}\n/************************************************************/\n/*                    SDP Commands                          */\n\n/************************************************************/\nvoid SPP::SDP_Command(uint8_t* data, uint8_t nbytes) { // See page 223 in the Bluetooth specs\n        pBtd->L2CAP_Command(hci_handle, data, nbytes, sdp_scid[0], sdp_scid[1]);\n}\n\nvoid SPP::serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow) { // See page 235 in the Bluetooth specs\n        l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU;\n        l2capoutbuf[1] = transactionIDHigh;\n        l2capoutbuf[2] = transactionIDLow;\n        l2capoutbuf[3] = 0x00; // MSB Parameter Length\n        l2capoutbuf[4] = 0x05; // LSB Parameter Length = 5\n        l2capoutbuf[5] = 0x00; // MSB AttributeListsByteCount\n        l2capoutbuf[6] = 0x02; // LSB AttributeListsByteCount = 2\n\n        /* Attribute ID/Value Sequence: */\n        l2capoutbuf[7] = 0x35; // Data element sequence - length in next byte\n        l2capoutbuf[8] = 0x00; // Length = 0\n        l2capoutbuf[9] = 0x00; // No continuation state\n\n        SDP_Command(l2capoutbuf, 10);\n}\n\nvoid SPP::serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) {\n        l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU;\n        l2capoutbuf[1] = transactionIDHigh;\n        l2capoutbuf[2] = transactionIDLow;\n        l2capoutbuf[3] = 0x00; // MSB Parameter Length\n        l2capoutbuf[4] = 0x2B; // LSB Parameter Length = 43\n        l2capoutbuf[5] = 0x00; // MSB AttributeListsByteCount\n        l2capoutbuf[6] = 0x26; // LSB AttributeListsByteCount = 38\n\n        /* Attribute ID/Value Sequence: */\n        l2capoutbuf[7] = 0x36; // Data element sequence - length in next two bytes\n        l2capoutbuf[8] = 0x00; // MSB Length\n        l2capoutbuf[9] = 0x3C; // LSB Length = 60\n\n        l2capoutbuf[10] = 0x36; // Data element sequence - length in next two bytes\n        l2capoutbuf[11] = 0x00; // MSB Length\n        l2capoutbuf[12] = 0x39; // LSB Length = 57\n\n        l2capoutbuf[13] = 0x09; // Unsigned Integer - length 2 bytes\n        l2capoutbuf[14] = 0x00; // MSB ServiceRecordHandle\n        l2capoutbuf[15] = 0x00; // LSB ServiceRecordHandle\n        l2capoutbuf[16] = 0x0A; // Unsigned int - length 4 bytes\n        l2capoutbuf[17] = 0x00; // ServiceRecordHandle value - TODO: Is this related to HCI_Handle?\n        l2capoutbuf[18] = 0x01;\n        l2capoutbuf[19] = 0x00;\n        l2capoutbuf[20] = 0x06;\n\n        l2capoutbuf[21] = 0x09; // Unsigned Integer - length 2 bytes\n        l2capoutbuf[22] = 0x00; // MSB ServiceClassIDList\n        l2capoutbuf[23] = 0x01; // LSB ServiceClassIDList\n        l2capoutbuf[24] = 0x35; // Data element sequence - length in next byte\n        l2capoutbuf[25] = 0x03; // Length = 3\n        l2capoutbuf[26] = 0x19; // UUID (universally unique identifier) - length = 2 bytes\n        l2capoutbuf[27] = 0x11; // MSB SerialPort\n        l2capoutbuf[28] = 0x01; // LSB SerialPort\n\n        l2capoutbuf[29] = 0x09; // Unsigned Integer - length 2 bytes\n        l2capoutbuf[30] = 0x00; // MSB ProtocolDescriptorList\n        l2capoutbuf[31] = 0x04; // LSB ProtocolDescriptorList\n        l2capoutbuf[32] = 0x35; // Data element sequence - length in next byte\n        l2capoutbuf[33] = 0x0C; // Length = 12\n\n        l2capoutbuf[34] = 0x35; // Data element sequence - length in next byte\n        l2capoutbuf[35] = 0x03; // Length = 3\n        l2capoutbuf[36] = 0x19; // UUID (universally unique identifier) - length = 2 bytes\n        l2capoutbuf[37] = 0x01; // MSB L2CAP\n        l2capoutbuf[38] = 0x00; // LSB L2CAP\n\n        l2capoutbuf[39] = 0x35; // Data element sequence - length in next byte\n        l2capoutbuf[40] = 0x05; // Length = 5\n        l2capoutbuf[41] = 0x19; // UUID (universally unique identifier) - length = 2 bytes\n        l2capoutbuf[42] = 0x00; // MSB RFCOMM\n        l2capoutbuf[43] = 0x03; // LSB RFCOMM\n        l2capoutbuf[44] = 0x08; // Unsigned Integer - length 1 byte\n\n        l2capoutbuf[45] = 0x02; // ContinuationState - Two more bytes\n        l2capoutbuf[46] = 0x00; // MSB length\n        l2capoutbuf[47] = 0x19; // LSB length = 25 more bytes to come\n\n        SDP_Command(l2capoutbuf, 48);\n}\n\nvoid SPP::serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) {\n        l2capoutbuf[0] = SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU;\n        l2capoutbuf[1] = transactionIDHigh;\n        l2capoutbuf[2] = transactionIDLow;\n        l2capoutbuf[3] = 0x00; // MSB Parameter Length\n        l2capoutbuf[4] = 0x1C; // LSB Parameter Length = 28\n        l2capoutbuf[5] = 0x00; // MSB AttributeListsByteCount\n        l2capoutbuf[6] = 0x19; // LSB AttributeListsByteCount = 25\n\n        /* Attribute ID/Value Sequence: */\n        l2capoutbuf[7] = 0x01; // Channel 1 - TODO: Try different values, so multiple servers can be used at once\n\n        l2capoutbuf[8] = 0x09; // Unsigned Integer - length 2 bytes\n        l2capoutbuf[9] = 0x00; // MSB LanguageBaseAttributeIDList\n        l2capoutbuf[10] = 0x06; // LSB LanguageBaseAttributeIDList\n        l2capoutbuf[11] = 0x35; // Data element sequence - length in next byte\n        l2capoutbuf[12] = 0x09; // Length = 9\n\n        // Identifier representing the natural language = en = English - see: \"ISO 639:1988\"\n        l2capoutbuf[13] = 0x09; // Unsigned Integer - length 2 bytes\n        l2capoutbuf[14] = 0x65; // 'e'\n        l2capoutbuf[15] = 0x6E; // 'n'\n\n        // \"The second element of each triplet contains an identifier that specifies a character encoding used for the language\"\n        // Encoding is set to 106 (UTF-8) - see: http://www.iana.org/assignments/character-sets/character-sets.xhtml\n        l2capoutbuf[16] = 0x09; // Unsigned Integer - length 2 bytes\n        l2capoutbuf[17] = 0x00; // MSB of character encoding\n        l2capoutbuf[18] = 0x6A; // LSB of character encoding (106)\n\n        // Attribute ID that serves as the base attribute ID for the natural language in the service record\n        // \"To facilitate the retrieval of human-readable universal attributes in a principal language, the base attribute ID value for the primary language supported by a service record shall be 0x0100\"\n        l2capoutbuf[19] = 0x09; // Unsigned Integer - length 2 bytes\n        l2capoutbuf[20] = 0x01;\n        l2capoutbuf[21] = 0x00;\n\n        l2capoutbuf[22] = 0x09; // Unsigned Integer - length 2 bytes\n        l2capoutbuf[23] = 0x01; // MSB ServiceDescription\n        l2capoutbuf[24] = 0x00; // LSB ServiceDescription\n\n        l2capoutbuf[25] = 0x25; // Text string - length in next byte\n        l2capoutbuf[26] = 0x05; // Name length\n        l2capoutbuf[27] = 'T';\n        l2capoutbuf[28] = 'K';\n        l2capoutbuf[29] = 'J';\n        l2capoutbuf[30] = 'S';\n        l2capoutbuf[31] = 'P';\n        l2capoutbuf[32] = 0x00; // No continuation state\n\n        SDP_Command(l2capoutbuf, 33);\n}\n\nvoid SPP::l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow) {\n        serialPortResponse1(transactionIDHigh, transactionIDLow); // These has to send all the supported functions, since it only supports virtual serialport it just sends the message again\n}\n\nvoid SPP::l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow) {\n        serialPortResponse2(transactionIDHigh, transactionIDLow); // Same data as serialPortResponse2\n}\n/************************************************************/\n/*                    RFCOMM Commands                       */\n\n/************************************************************/\nvoid SPP::RFCOMM_Command(uint8_t* data, uint8_t nbytes) {\n        pBtd->L2CAP_Command(hci_handle, data, nbytes, rfcomm_scid[0], rfcomm_scid[1]);\n}\n\nvoid SPP::sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t* data, uint8_t length) {\n        l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address\n        l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control\n        l2capoutbuf[2] = length << 1 | 0x01; // Length and format (always 0x01 bytes format)\n        uint8_t i = 0;\n        for(; i < length; i++)\n                l2capoutbuf[i + 3] = data[i];\n        l2capoutbuf[i + 3] = calcFcs(l2capoutbuf);\n#ifdef EXTRADEBUG\n        Notify(PSTR(\" - RFCOMM Data: \"), 0x80);\n        for(i = 0; i < length + 4; i++) {\n                D_PrintHex<uint8_t > (l2capoutbuf[i], 0x80);\n                Notify(PSTR(\" \"), 0x80);\n        }\n#endif\n        RFCOMM_Command(l2capoutbuf, length + 4);\n}\n\nvoid SPP::sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit) {\n        l2capoutbuf[0] = channel | direction | CR | extendAddress; // RFCOMM Address\n        l2capoutbuf[1] = channelType | pfBit; // RFCOMM Control\n        l2capoutbuf[2] = 0x01; // Length = 0\n        l2capoutbuf[3] = credit; // Credit\n        l2capoutbuf[4] = calcFcs(l2capoutbuf);\n#ifdef EXTRADEBUG\n        Notify(PSTR(\" - RFCOMM Credit Data: \"), 0x80);\n        for(uint8_t i = 0; i < 5; i++) {\n                D_PrintHex<uint8_t > (l2capoutbuf[i], 0x80);\n                Notify(PSTR(\" \"), 0x80);\n        }\n#endif\n        RFCOMM_Command(l2capoutbuf, 5);\n}\n\n/* CRC on 2 bytes */\nuint8_t SPP::crc(uint8_t *data) {\n        return (pgm_read_byte(&rfcomm_crc_table[pgm_read_byte(&rfcomm_crc_table[0xFF ^ data[0]]) ^ data[1]]));\n}\n\n/* Calculate FCS */\nuint8_t SPP::calcFcs(uint8_t *data) {\n        uint8_t temp = crc(data);\n        if((data[1] & 0xEF) == RFCOMM_UIH)\n                return (0xFF - temp); // FCS on 2 bytes\n        else\n                return (0xFF - pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]])); // FCS on 3 bytes\n}\n\n/* Check FCS */\nbool SPP::checkFcs(uint8_t *data, uint8_t fcs) {\n        uint8_t temp = crc(data);\n        if((data[1] & 0xEF) != RFCOMM_UIH)\n                temp = pgm_read_byte(&rfcomm_crc_table[temp ^ data[2]]); // FCS on 3 bytes\n        return (pgm_read_byte(&rfcomm_crc_table[temp ^ fcs]) == 0xCF);\n}\n\n/* Serial commands */\n#if defined(ARDUINO) && ARDUINO >=100\n\nsize_t SPP::write(uint8_t data) {\n        return write(&data, 1);\n}\n#else\n\nvoid SPP::write(uint8_t data) {\n        write(&data, 1);\n}\n#endif\n\n#if defined(ARDUINO) && ARDUINO >=100\n\nsize_t SPP::write(const uint8_t *data, size_t size) {\n#else\n\nvoid SPP::write(const uint8_t *data, size_t size) {\n#endif\n        for(uint8_t i = 0; i < size; i++) {\n                if(sppIndex >= sizeof (sppOutputBuffer) / sizeof (sppOutputBuffer[0]))\n                        send(); // Send the current data in the buffer\n                sppOutputBuffer[sppIndex++] = data[i]; // All the bytes are put into a buffer and then send using the send() function\n        }\n#if defined(ARDUINO) && ARDUINO >=100\n        return size;\n#endif\n}\n\nvoid SPP::send() {\n        if(!connected || !sppIndex)\n                return;\n        uint8_t length; // This is the length of the string we are sending\n        uint8_t offset = 0; // This is used to keep track of where we are in the string\n\n        l2capoutbuf[0] = rfcommChannelConnection | 0 | 0 | extendAddress; // RFCOMM Address\n        l2capoutbuf[1] = RFCOMM_UIH; // RFCOMM Control\n\n        while(sppIndex) { // We will run this while loop until this variable is 0\n                if(sppIndex > (sizeof (l2capoutbuf) - 4)) // Check if the string is larger than the outgoing buffer\n                        length = sizeof (l2capoutbuf) - 4;\n                else\n                        length = sppIndex;\n\n                l2capoutbuf[2] = length << 1 | 1; // Length\n                uint8_t i = 0;\n                for(; i < length; i++)\n                        l2capoutbuf[i + 3] = sppOutputBuffer[i + offset];\n                l2capoutbuf[i + 3] = calcFcs(l2capoutbuf); // Calculate checksum\n\n                RFCOMM_Command(l2capoutbuf, length + 4);\n\n                sppIndex -= length;\n                offset += length; // Increment the offset\n        }\n}\n\nint SPP::available(void) {\n        return rfcommAvailable;\n};\n\nvoid SPP::discard(void) {\n        rfcommAvailable = 0;\n}\n\nint SPP::peek(void) {\n        if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer\n                return -1;\n        return rfcommDataBuffer[0];\n}\n\nint SPP::read(void) {\n        if(rfcommAvailable == 0) // Don't read if there is nothing in the buffer\n                return -1;\n        uint8_t output = rfcommDataBuffer[0];\n        for(uint8_t i = 1; i < rfcommAvailable; i++)\n                rfcommDataBuffer[i - 1] = rfcommDataBuffer[i]; // Shift the buffer one left\n        rfcommAvailable--;\n        bytesRead++;\n        if(bytesRead > (sizeof (rfcommDataBuffer) - 5)) { // We will send the command just before it runs out of credit\n                bytesRead = 0;\n                sendRfcommCredit(rfcommChannelConnection, rfcommDirection, 0, RFCOMM_UIH, 0x10, sizeof (rfcommDataBuffer)); // Send more credit\n#ifdef EXTRADEBUG\n                Notify(PSTR(\"\\r\\nSent \"), 0x80);\n                Notify((uint8_t)sizeof (rfcommDataBuffer), 0x80);\n                Notify(PSTR(\" more credit\"), 0x80);\n#endif\n        }\n        return output;\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/SPP.h",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _spp_h_\n#define _spp_h_\n\n#include \"BTD.h\"\n\n/* Used for SDP */\n#define SDP_SERVICE_SEARCH_ATTRIBUTE_REQUEST_PDU    0x06 // See the RFCOMM specs\n#define SDP_SERVICE_SEARCH_ATTRIBUTE_RESPONSE_PDU   0x07 // See the RFCOMM specs\n#define SERIALPORT_UUID     0x1101 // See http://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm\n#define L2CAP_UUID          0x0100\n\n/* Used for RFCOMM */\n#define RFCOMM_SABM     0x2F\n#define RFCOMM_UA       0x63\n#define RFCOMM_UIH      0xEF\n//#define RFCOMM_DM       0x0F\n#define RFCOMM_DISC     0x43\n\n#define extendAddress   0x01 // Always 1\n\n// Multiplexer message types\n#define BT_RFCOMM_PN_CMD     0x83\n#define BT_RFCOMM_PN_RSP     0x81\n#define BT_RFCOMM_MSC_CMD    0xE3\n#define BT_RFCOMM_MSC_RSP    0xE1\n#define BT_RFCOMM_RPN_CMD    0x93\n#define BT_RFCOMM_RPN_RSP    0x91\n/*\n#define BT_RFCOMM_TEST_CMD   0x23\n#define BT_RFCOMM_TEST_RSP   0x21\n#define BT_RFCOMM_FCON_CMD   0xA3\n#define BT_RFCOMM_FCON_RSP   0xA1\n#define BT_RFCOMM_FCOFF_CMD  0x63\n#define BT_RFCOMM_FCOFF_RSP  0x61\n#define BT_RFCOMM_RLS_CMD    0x53\n#define BT_RFCOMM_RLS_RSP    0x51\n#define BT_RFCOMM_NSC_RSP    0x11\n */\n\n/**\n * This BluetoothService class implements the Serial Port Protocol (SPP).\n * It inherits the Arduino Stream class. This allows it to use all the standard Arduino print and stream functions.\n */\nclass SPP : public BluetoothService, public Stream {\npublic:\n        /**\n         * Constructor for the SPP class.\n         * @param  p   Pointer to BTD class instance.\n         * @param  name   Set the name to BTD#btdName. If argument is omitted, then \"Arduino\" will be used.\n         * @param  pin   Write the pin to BTD#btdPin. If argument is omitted, then \"0000\" will be used.\n         */\n        SPP(BTD *p, const char *name = \"Arduino\", const char *pin = \"0000\");\n\n        /** @name BluetoothService implementation */\n        /** Used this to disconnect the virtual serial port. */\n        void disconnect();\n        /**@}*/\n\n        /**\n         * Used to provide Boolean tests for the class.\n         * @return Return true if SPP communication is connected.\n         */\n        operator bool() {\n                return connected;\n        }\n        /** Variable used to indicate if the connection is established. */\n        bool connected;\n\n        /** @name Serial port profile (SPP) Print functions */\n        /**\n         * Get number of bytes waiting to be read.\n         * @return Return the number of bytes ready to be read.\n         */\n        int available(void);\n\n        /** Send out all bytes in the buffer. */\n        void flush(void) {\n                send();\n        };\n        /**\n         * Used to read the next value in the buffer without advancing to the next one.\n         * @return Return the byte. Will return -1 if no bytes are available.\n         */\n        int peek(void);\n        /**\n         * Used to read the buffer.\n         * @return Return the byte. Will return -1 if no bytes are available.\n         */\n        int read(void);\n\n#if defined(ARDUINO) && ARDUINO >=100\n        /**\n         * Writes the byte to send to a buffer. The message is send when either send() or after Usb.Task() is called.\n         * @param  data The byte to write.\n         * @return      Return the number of bytes written.\n         */\n        size_t write(uint8_t data);\n        /**\n         * Writes the bytes to send to a buffer. The message is send when either send() or after Usb.Task() is called.\n         * @param  data The data array to send.\n         * @param  size Size of the data.\n         * @return      Return the number of bytes written.\n         */\n        size_t write(const uint8_t* data, size_t size);\n        /** Pull in write(const char *str) from Print */\n        using Print::write;\n#else\n        /**\n         * Writes the byte to send to a buffer. The message is send when either send() or after Usb.Task() is called.\n         * @param  data The byte to write.\n         */\n        void write(uint8_t data);\n        /**\n         * Writes the bytes to send to a buffer. The message is send when either send() or after Usb.Task() is called.\n         * @param data The data array to send.\n         * @param size Size of the data.\n         */\n        void write(const uint8_t* data, size_t size);\n#endif\n\n        /** Discard all the bytes in the buffer. */\n        void discard(void);\n        /**\n         * This will send all the bytes in the buffer.\n         * This is called whenever Usb.Task() is called,\n         * but can also be called via this function.\n         */\n        void send(void);\n        /**@}*/\n\nprotected:\n        /** @name BluetoothService implementation */\n        /**\n         * Used to pass acldata to the services.\n         * @param ACLData Incoming acldata.\n         */\n        void ACLData(uint8_t* ACLData);\n        /** Used to establish the connection automatically. */\n        void Run();\n        /** Use this to reset the service. */\n        void Reset();\n        /**\n         * Called when a device is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         */\n        void onInit();\n        /**@}*/\n\nprivate:\n        /* Set true when a channel is created */\n        bool SDPConnected;\n        bool RFCOMMConnected;\n\n        /* Variables used by L2CAP state machines */\n        uint8_t l2cap_sdp_state;\n        uint8_t l2cap_rfcomm_state;\n\n        uint8_t l2capoutbuf[BULK_MAXPKTSIZE]; // General purpose buffer for l2cap out data\n        uint8_t rfcommbuf[10]; // Buffer for RFCOMM Commands\n\n        /* L2CAP Channels */\n        uint8_t sdp_scid[2]; // L2CAP source CID for SDP\n        uint8_t sdp_dcid[2]; // 0x0050\n        uint8_t rfcomm_scid[2]; // L2CAP source CID for RFCOMM\n        uint8_t rfcomm_dcid[2]; // 0x0051\n\n        /* RFCOMM Variables */\n        uint8_t rfcommChannel;\n        uint8_t rfcommChannelConnection; // This is the channel the SPP channel will be running at\n        uint8_t rfcommDirection;\n        uint8_t rfcommCommandResponse;\n        uint8_t rfcommChannelType;\n        uint8_t rfcommPfBit;\n\n        uint32_t timer;\n        bool waitForLastCommand;\n        bool creditSent;\n\n        uint8_t rfcommDataBuffer[100]; // Create a 100 sized buffer for incoming data\n        uint8_t sppOutputBuffer[100]; // Create a 100 sized buffer for outgoing SPP data\n        uint8_t sppIndex;\n        uint8_t rfcommAvailable;\n\n        bool firstMessage; // Used to see if it's the first SDP request received\n        uint8_t bytesRead; // Counter to see when it's time to send more credit\n\n        /* State machines */\n        void SDP_task(); // SDP state machine\n        void RFCOMM_task(); // RFCOMM state machine\n\n        /* SDP Commands */\n        void SDP_Command(uint8_t *data, uint8_t nbytes);\n        void serviceNotSupported(uint8_t transactionIDHigh, uint8_t transactionIDLow);\n        void serialPortResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow);\n        void serialPortResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow);\n        void l2capResponse1(uint8_t transactionIDHigh, uint8_t transactionIDLow);\n        void l2capResponse2(uint8_t transactionIDHigh, uint8_t transactionIDLow);\n\n        /* RFCOMM Commands */\n        void RFCOMM_Command(uint8_t *data, uint8_t nbytes);\n        void sendRfcomm(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t *data, uint8_t length);\n        void sendRfcommCredit(uint8_t channel, uint8_t direction, uint8_t CR, uint8_t channelType, uint8_t pfBit, uint8_t credit);\n        uint8_t calcFcs(uint8_t *data);\n        bool checkFcs(uint8_t *data, uint8_t fcs);\n        uint8_t crc(uint8_t *data);\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/Usb.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n/* USB functions */\n\n#include \"Usb.h\"\n\nstatic uint8_t usb_error = 0;\nstatic uint8_t usb_task_state;\n\n/* constructor */\nUSB::USB() : bmHubPre(0) {\n        usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE; //set up state machine\n        init();\n}\n\n/* Initialize data structures */\nvoid USB::init() {\n        //devConfigIndex = 0;\n        bmHubPre = 0;\n}\n\nuint8_t USB::getUsbTaskState(void) {\n        return ( usb_task_state);\n}\n\nvoid USB::setUsbTaskState(uint8_t state) {\n        usb_task_state = state;\n}\n\nEpInfo* USB::getEpInfoEntry(uint8_t addr, uint8_t ep) {\n        UsbDevice *p = addrPool.GetUsbDevicePtr(addr);\n\n        if(!p || !p->epinfo)\n                return NULL;\n\n        EpInfo *pep = p->epinfo;\n\n        for(uint8_t i = 0; i < p->epcount; i++) {\n                if((pep)->epAddr == ep)\n                        return pep;\n\n                pep++;\n        }\n        return NULL;\n}\n\n/* set device table entry */\n\n/* each device is different and has different number of endpoints. This function plugs endpoint record structure, defined in application, to devtable */\nuint8_t USB::setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo* eprecord_ptr) {\n        if(!eprecord_ptr)\n                return USB_ERROR_INVALID_ARGUMENT;\n\n        UsbDevice *p = addrPool.GetUsbDevicePtr(addr);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->address.devAddress = addr;\n        p->epinfo = eprecord_ptr;\n        p->epcount = epcount;\n\n        return 0;\n}\n\nuint8_t USB::SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t *nak_limit) {\n        UsbDevice *p = addrPool.GetUsbDevicePtr(addr);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        if(!p->epinfo)\n                return USB_ERROR_EPINFO_IS_NULL;\n\n        *ppep = getEpInfoEntry(addr, ep);\n\n        if(!*ppep)\n                return USB_ERROR_EP_NOT_FOUND_IN_TBL;\n\n        *nak_limit = (0x0001UL << (((*ppep)->bmNakPower > USB_NAK_MAX_POWER) ? USB_NAK_MAX_POWER : (*ppep)->bmNakPower));\n        (*nak_limit)--;\n        /*\n          USBTRACE2(\"\\r\\nAddress: \", addr);\n          USBTRACE2(\" EP: \", ep);\n          USBTRACE2(\" NAK Power: \",(*ppep)->bmNakPower);\n          USBTRACE2(\" NAK Limit: \", nak_limit);\n          USBTRACE(\"\\r\\n\");\n         */\n        regWr(rPERADDR, addr); //set peripheral address\n\n        uint8_t mode = regRd(rMODE);\n\n        //Serial.print(\"\\r\\nMode: \");\n        //Serial.println( mode, HEX);\n        //Serial.print(\"\\r\\nLS: \");\n        //Serial.println(p->lowspeed, HEX);\n\n\n\n        // Set bmLOWSPEED and bmHUBPRE in case of low-speed device, reset them otherwise\n        regWr(rMODE, (p->lowspeed) ? mode | bmLOWSPEED | bmHubPre : mode & ~(bmHUBPRE | bmLOWSPEED));\n\n        return 0;\n}\n\n/* Control transfer. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer,   */\n/* depending on request. Actual requests are defined as inlines                                                                                      */\n/* return codes:                */\n/* 00       =   success         */\n\n/* 01-0f    =   non-zero HRSLT  */\nuint8_t USB::ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi,\n        uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t* dataptr, USBReadParser *p) {\n        bool direction = false; //request direction, IN or OUT\n        uint8_t rcode;\n        SETUP_PKT setup_pkt;\n\n        EpInfo *pep = NULL;\n        uint16_t nak_limit = 0;\n\n        rcode = SetAddress(addr, ep, &pep, &nak_limit);\n\n        if(rcode)\n                return rcode;\n\n        direction = ((bmReqType & 0x80) > 0);\n\n        /* fill in setup packet */\n        setup_pkt.ReqType_u.bmRequestType = bmReqType;\n        setup_pkt.bRequest = bRequest;\n        setup_pkt.wVal_u.wValueLo = wValLo;\n        setup_pkt.wVal_u.wValueHi = wValHi;\n        setup_pkt.wIndex = wInd;\n        setup_pkt.wLength = total;\n\n        bytesWr(rSUDFIFO, 8, (uint8_t*) & setup_pkt); //transfer to setup packet FIFO\n\n        rcode = dispatchPkt(tokSETUP, ep, nak_limit); //dispatch packet\n\n        if(rcode) //return HRSLT if not zero\n                return ( rcode);\n\n        if(dataptr != NULL) //data stage, if present\n        {\n                if(direction) //IN transfer\n                {\n                        uint16_t left = total;\n\n                        pep->bmRcvToggle = 1; //bmRCVTOG1;\n\n                        while(left) {\n                                // Bytes read into buffer\n                                uint16_t read = nbytes;\n                                //uint16_t read = (left<nbytes) ? left : nbytes;\n\n                                rcode = InTransfer(pep, nak_limit, &read, dataptr);\n                                if(rcode == hrTOGERR) {\n                                        // yes, we flip it wrong here so that next time it is actually correct!\n                                        pep->bmRcvToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;\n                                        continue;\n                                }\n\n                                if(rcode)\n                                        return rcode;\n\n                                // Invoke callback function if inTransfer completed successfully and callback function pointer is specified\n                                if(!rcode && p)\n                                        ((USBReadParser*)p)->Parse(read, dataptr, total - left);\n\n                                left -= read;\n\n                                if(read < nbytes)\n                                        break;\n                        }\n                } else //OUT transfer\n                {\n                        pep->bmSndToggle = 1; //bmSNDTOG1;\n                        rcode = OutTransfer(pep, nak_limit, nbytes, dataptr);\n                }\n                if(rcode) //return error\n                        return ( rcode);\n        }\n        // Status stage\n        return dispatchPkt((direction) ? tokOUTHS : tokINHS, ep, nak_limit); //GET if direction\n}\n\n/* IN transfer to arbitrary endpoint. Assumes PERADDR is set. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */\n/* Keep sending INs and writes data to memory area pointed by 'data'                                                           */\n\n/* rcode 0 if no errors. rcode 01-0f is relayed from dispatchPkt(). Rcode f0 means RCVDAVIRQ error,\n            fe USB xfer timeout */\nuint8_t USB::inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t* data) {\n        EpInfo *pep = NULL;\n        uint16_t nak_limit = 0;\n\n        uint8_t rcode = SetAddress(addr, ep, &pep, &nak_limit);\n\n        if(rcode) {\n                USBTRACE3(\"(USB::InTransfer) SetAddress Failed \", rcode, 0x81);\n                USBTRACE3(\"(USB::InTransfer) addr requested \", addr, 0x81);\n                USBTRACE3(\"(USB::InTransfer) ep requested \", ep, 0x81);\n                return rcode;\n        }\n        return InTransfer(pep, nak_limit, nbytesptr, data);\n}\n\nuint8_t USB::InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t* data) {\n        uint8_t rcode = 0;\n        uint8_t pktsize;\n\n        uint16_t nbytes = *nbytesptr;\n        //printf(\"Requesting %i bytes \", nbytes);\n        uint8_t maxpktsize = pep->maxPktSize;\n\n        *nbytesptr = 0;\n        regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value\n\n        // use a 'break' to exit this loop\n        while(1) {\n                rcode = dispatchPkt(tokIN, pep->epAddr, nak_limit); //IN packet to EP-'endpoint'. Function takes care of NAKS.\n                if(rcode == hrTOGERR) {\n                        // yes, we flip it wrong here so that next time it is actually correct!\n                        pep->bmRcvToggle = (regRd(rHRSL) & bmRCVTOGRD) ? 0 : 1;\n                        regWr(rHCTL, (pep->bmRcvToggle) ? bmRCVTOG1 : bmRCVTOG0); //set toggle value\n                        continue;\n                }\n                if(rcode) {\n                        //printf(\">>>>>>>> Problem! dispatchPkt %2.2x\\r\\n\", rcode);\n                        break; //should be 0, indicating ACK. Else return error code.\n                }\n                /* check for RCVDAVIRQ and generate error if not present */\n                /* the only case when absence of RCVDAVIRQ makes sense is when toggle error occurred. Need to add handling for that */\n                if((regRd(rHIRQ) & bmRCVDAVIRQ) == 0) {\n                        //printf(\">>>>>>>> Problem! NO RCVDAVIRQ!\\r\\n\");\n                        rcode = 0xf0; //receive error\n                        break;\n                }\n                pktsize = regRd(rRCVBC); //number of received bytes\n                //printf(\"Got %i bytes \\r\\n\", pktsize);\n                // This would be OK, but...\n                //assert(pktsize <= nbytes);\n                if(pktsize > nbytes) {\n                        // This can happen. Use of assert on Arduino locks up the Arduino.\n                        // So I will trim the value, and hope for the best.\n                        //printf(\">>>>>>>> Problem! Wanted %i bytes but got %i.\\r\\n\", nbytes, pktsize);\n                        pktsize = nbytes;\n                }\n\n                int16_t mem_left = (int16_t)nbytes - *((int16_t*)nbytesptr);\n\n                if(mem_left < 0)\n                        mem_left = 0;\n\n                data = bytesRd(rRCVFIFO, ((pktsize > mem_left) ? mem_left : pktsize), data);\n\n                regWr(rHIRQ, bmRCVDAVIRQ); // Clear the IRQ & free the buffer\n                *nbytesptr += pktsize; // add this packet's byte count to total transfer length\n\n                /* The transfer is complete under two conditions:           */\n                /* 1. The device sent a short packet (L.T. maxPacketSize)   */\n                /* 2. 'nbytes' have been transferred.                       */\n                if((pktsize < maxpktsize) || (*nbytesptr >= nbytes)) // have we transferred 'nbytes' bytes?\n                {\n                        // Save toggle value\n                        pep->bmRcvToggle = ((regRd(rHRSL) & bmRCVTOGRD)) ? 1 : 0;\n                        //printf(\"\\r\\n\");\n                        rcode = 0;\n                        break;\n                } // if\n        } //while( 1 )\n        return ( rcode);\n}\n\n/* OUT transfer to arbitrary endpoint. Handles multiple packets if necessary. Transfers 'nbytes' bytes. */\n/* Handles NAK bug per Maxim Application Note 4000 for single buffer transfer   */\n\n/* rcode 0 if no errors. rcode 01-0f is relayed from HRSL                       */\nuint8_t USB::outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data) {\n        EpInfo *pep = NULL;\n        uint16_t nak_limit = 0;\n\n        uint8_t rcode = SetAddress(addr, ep, &pep, &nak_limit);\n\n        if(rcode)\n                return rcode;\n\n        return OutTransfer(pep, nak_limit, nbytes, data);\n}\n\nuint8_t USB::OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data) {\n        uint8_t rcode = hrSUCCESS, retry_count;\n        uint8_t *data_p = data; //local copy of the data pointer\n        uint16_t bytes_tosend, nak_count;\n        uint16_t bytes_left = nbytes;\n\n        uint8_t maxpktsize = pep->maxPktSize;\n\n        if(maxpktsize < 1 || maxpktsize > 64)\n                return USB_ERROR_INVALID_MAX_PKT_SIZE;\n\n        unsigned long timeout = millis() + USB_XFER_TIMEOUT;\n\n        regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value\n\n        while(bytes_left) {\n                retry_count = 0;\n                nak_count = 0;\n                bytes_tosend = (bytes_left >= maxpktsize) ? maxpktsize : bytes_left;\n                bytesWr(rSNDFIFO, bytes_tosend, data_p); //filling output FIFO\n                regWr(rSNDBC, bytes_tosend); //set number of bytes\n                regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet\n                while(!(regRd(rHIRQ) & bmHXFRDNIRQ)); //wait for the completion IRQ\n                regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ\n                rcode = (regRd(rHRSL) & 0x0f);\n\n                while(rcode && ((long)(millis() - timeout) < 0L)) {\n                        switch(rcode) {\n                                case hrNAK:\n                                        nak_count++;\n                                        if(nak_limit && (nak_count == nak_limit))\n                                                goto breakout;\n                                        //return ( rcode);\n                                        break;\n                                case hrTIMEOUT:\n                                        retry_count++;\n                                        if(retry_count == USB_RETRY_LIMIT)\n                                                goto breakout;\n                                        //return ( rcode);\n                                        break;\n                                case hrTOGERR:\n                                        // yes, we flip it wrong here so that next time it is actually correct!\n                                        pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 0 : 1;\n                                        regWr(rHCTL, (pep->bmSndToggle) ? bmSNDTOG1 : bmSNDTOG0); //set toggle value\n                                        break;\n                                default:\n                                        goto breakout;\n                        }//switch( rcode\n\n                        /* process NAK according to Host out NAK bug */\n                        regWr(rSNDBC, 0);\n                        regWr(rSNDFIFO, *data_p);\n                        regWr(rSNDBC, bytes_tosend);\n                        regWr(rHXFR, (tokOUT | pep->epAddr)); //dispatch packet\n                        while(!(regRd(rHIRQ) & bmHXFRDNIRQ)); //wait for the completion IRQ\n                        regWr(rHIRQ, bmHXFRDNIRQ); //clear IRQ\n                        rcode = (regRd(rHRSL) & 0x0f);\n                }//while( rcode && ....\n                bytes_left -= bytes_tosend;\n                data_p += bytes_tosend;\n        }//while( bytes_left...\nbreakout:\n\n        pep->bmSndToggle = (regRd(rHRSL) & bmSNDTOGRD) ? 1 : 0; //bmSNDTOG1 : bmSNDTOG0;  //update toggle\n        return ( rcode); //should be 0 in all cases\n}\n/* dispatch USB packet. Assumes peripheral address is set and relevant buffer is loaded/empty       */\n/* If NAK, tries to re-send up to nak_limit times                                                   */\n/* If nak_limit == 0, do not count NAKs, exit after timeout                                         */\n/* If bus timeout, re-sends up to USB_RETRY_LIMIT times                                             */\n\n/* return codes 0x00-0x0f are HRSLT( 0x00 being success ), 0xff means timeout                       */\nuint8_t USB::dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit) {\n        unsigned long timeout = millis() + USB_XFER_TIMEOUT;\n        uint8_t tmpdata;\n        uint8_t rcode = hrSUCCESS;\n        uint8_t retry_count = 0;\n        uint16_t nak_count = 0;\n\n        while((long)(millis() - timeout) < 0L) {\n                regWr(rHXFR, (token | ep)); //launch the transfer\n                rcode = USB_ERROR_TRANSFER_TIMEOUT;\n\n                while((long)(millis() - timeout) < 0L) //wait for transfer completion\n                {\n                        tmpdata = regRd(rHIRQ);\n\n                        if(tmpdata & bmHXFRDNIRQ) {\n                                regWr(rHIRQ, bmHXFRDNIRQ); //clear the interrupt\n                                rcode = 0x00;\n                                break;\n                        }//if( tmpdata & bmHXFRDNIRQ\n\n                }//while ( millis() < timeout\n\n                //if (rcode != 0x00) //exit if timeout\n                //        return ( rcode);\n\n                rcode = (regRd(rHRSL) & 0x0f); //analyze transfer result\n\n                switch(rcode) {\n                        case hrNAK:\n                                nak_count++;\n                                if(nak_limit && (nak_count == nak_limit))\n                                        return (rcode);\n                                break;\n                        case hrTIMEOUT:\n                                retry_count++;\n                                if(retry_count == USB_RETRY_LIMIT)\n                                        return (rcode);\n                                break;\n                        default:\n                                return (rcode);\n                }//switch( rcode\n\n        }//while( timeout > millis()\n        return ( rcode);\n}\n\n/* USB main task. Performs enumeration/cleanup */\nvoid USB::Task(void) //USB state machine\n{\n        uint8_t rcode;\n        uint8_t tmpdata;\n        static unsigned long delay = 0;\n        //USB_DEVICE_DESCRIPTOR buf;\n        bool lowspeed = false;\n\n        MAX3421E::Task();\n\n        tmpdata = getVbusState();\n\n        /* modify USB task state if Vbus changed */\n        switch(tmpdata) {\n                case SE1: //illegal state\n                        usb_task_state = USB_DETACHED_SUBSTATE_ILLEGAL;\n                        lowspeed = false;\n                        break;\n                case SE0: //disconnected\n                        if((usb_task_state & USB_STATE_MASK) != USB_STATE_DETACHED)\n                                usb_task_state = USB_DETACHED_SUBSTATE_INITIALIZE;\n                        lowspeed = false;\n                        break;\n                case LSHOST:\n\n                        lowspeed = true;\n                        //intentional fallthrough\n                case FSHOST: //attached\n                        if((usb_task_state & USB_STATE_MASK) == USB_STATE_DETACHED) {\n                                delay = millis() + USB_SETTLE_DELAY;\n                                usb_task_state = USB_ATTACHED_SUBSTATE_SETTLE;\n                        }\n                        break;\n        }// switch( tmpdata\n\n        for(uint8_t i = 0; i < USB_NUMDEVICES; i++)\n                if(devConfig[i])\n                        rcode = devConfig[i]->Poll();\n\n        switch(usb_task_state) {\n                case USB_DETACHED_SUBSTATE_INITIALIZE:\n                        init();\n\n                        for(uint8_t i = 0; i < USB_NUMDEVICES; i++)\n                                if(devConfig[i])\n                                        rcode = devConfig[i]->Release();\n\n                        usb_task_state = USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE;\n                        break;\n                case USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE: //just sit here\n                        break;\n                case USB_DETACHED_SUBSTATE_ILLEGAL: //just sit here\n                        break;\n                case USB_ATTACHED_SUBSTATE_SETTLE: //settle time for just attached device\n                        if((long)(millis() - delay) >= 0L)\n                                usb_task_state = USB_ATTACHED_SUBSTATE_RESET_DEVICE;\n                        else break; // don't fall through\n                case USB_ATTACHED_SUBSTATE_RESET_DEVICE:\n                        regWr(rHCTL, bmBUSRST); //issue bus reset\n                        usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE;\n                        break;\n                case USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE:\n                        if((regRd(rHCTL) & bmBUSRST) == 0) {\n                                tmpdata = regRd(rMODE) | bmSOFKAENAB; //start SOF generation\n                                regWr(rMODE, tmpdata);\n                                usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_SOF;\n                                //delay = millis() + 20; //20ms wait after reset per USB spec\n                        }\n                        break;\n                case USB_ATTACHED_SUBSTATE_WAIT_SOF: //todo: change check order\n                        if(regRd(rHIRQ) & bmFRAMEIRQ) {\n                                //when first SOF received _and_ 20ms has passed we can continue\n                                /*\n                                if (delay < millis()) //20ms passed\n                                        usb_task_state = USB_STATE_CONFIGURING;\n                                 */\n                                usb_task_state = USB_ATTACHED_SUBSTATE_WAIT_RESET;\n                                delay = millis() + 20;\n                        }\n                        break;\n                case USB_ATTACHED_SUBSTATE_WAIT_RESET:\n                        if((long)(millis() - delay) >= 0L) usb_task_state = USB_STATE_CONFIGURING;\n                        else break; // don't fall through\n                case USB_STATE_CONFIGURING:\n\n                        //Serial.print(\"\\r\\nConf.LS: \");\n                        //Serial.println(lowspeed, HEX);\n\n                        rcode = Configuring(0, 0, lowspeed);\n\n                        if(rcode) {\n                                if(rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE) {\n                                        usb_error = rcode;\n                                        usb_task_state = USB_STATE_ERROR;\n                                }\n                        } else\n                                usb_task_state = USB_STATE_RUNNING;\n                        break;\n                case USB_STATE_RUNNING:\n                        break;\n                case USB_STATE_ERROR:\n                        //MAX3421E::Init();\n                        break;\n        } // switch( usb_task_state )\n}\n\nuint8_t USB::DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed) {\n        //uint8_t                buf[12];\n        uint8_t rcode;\n        UsbDevice *p0 = NULL, *p = NULL;\n\n        // Get pointer to pseudo device with address 0 assigned\n        p0 = addrPool.GetUsbDevicePtr(0);\n\n        if(!p0)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        if(!p0->epinfo)\n                return USB_ERROR_EPINFO_IS_NULL;\n\n        p0->lowspeed = (lowspeed) ? true : false;\n\n        // Allocate new address according to device class\n        uint8_t bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        p = addrPool.GetUsbDevicePtr(bAddress);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        // Assign new address to the device\n        rcode = setAddr(0, 0, bAddress);\n\n        if(rcode) {\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n                return rcode;\n        }\n        return 0;\n};\n\nuint8_t USB::AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed) {\n        //printf(\"AttemptConfig: parent = %i, port = %i\\r\\n\", parent, port);\n        uint8_t retries = 0;\n\nagain:\n        uint8_t rcode = devConfig[driver]->ConfigureDevice(parent, port, lowspeed);\n        if(rcode == USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET) {\n                if(parent == 0) {\n                        // Send a bus reset on the root interface.\n                        regWr(rHCTL, bmBUSRST); //issue bus reset\n                        delay(102); // delay 102ms, compensate for clock inaccuracy.\n                } else {\n                        // reset parent port\n                        devConfig[parent]->ResetHubPort(port);\n                }\n        } else if(rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works\n                delay(100);\n                retries++;\n                goto again;\n        } else if(rcode)\n                return rcode;\n\n        rcode = devConfig[driver]->Init(parent, port, lowspeed);\n        if(rcode == hrJERR && retries < 3) { // Some devices returns this when plugged in - trying to initialize the device again usually works\n                delay(100);\n                retries++;\n                goto again;\n        }\n        if(rcode) {\n                // Issue a bus reset, because the device may be in a limbo state\n                if(parent == 0) {\n                        // Send a bus reset on the root interface.\n                        regWr(rHCTL, bmBUSRST); //issue bus reset\n                        delay(102); // delay 102ms, compensate for clock inaccuracy.\n                } else {\n                        // reset parent port\n                        devConfig[parent]->ResetHubPort(port);\n                }\n        }\n        return rcode;\n}\n\n/*\n * This is broken. We need to enumerate differently.\n * It causes major problems with several devices if detected in an unexpected order.\n *\n *\n * Oleg - I wouldn't do anything before the newly connected device is considered sane.\n * i.e.(delays are not indicated for brevity):\n * 1. reset\n * 2. GetDevDescr();\n * 3a. If ACK, continue with allocating address, addressing, etc.\n * 3b. Else reset again, count resets, stop at some number (5?).\n * 4. When max.number of resets is reached, toggle power/fail\n * If desired, this could be modified by performing two resets with GetDevDescr() in the middle - however, from my experience, if a device answers to GDD()\n * it doesn't need to be reset again\n * New steps proposal:\n * 1: get address pool instance. exit on fail\n * 2: pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf). exit on fail.\n * 3: bus reset, 100ms delay\n * 4: set address\n * 5: pUsb->setEpInfoEntry(bAddress, 1, epInfo), exit on fail\n * 6: while (configurations) {\n *              for(each configuration) {\n *                      for (each driver) {\n *                              6a: Ask device if it likes configuration. Returns 0 on OK.\n *                                      If successful, the driver configured device.\n *                                      The driver now owns the endpoints, and takes over managing them.\n *                                      The following will need codes:\n *                                          Everything went well, instance consumed, exit with success.\n *                                          Instance already in use, ignore it, try next driver.\n *                                          Not a supported device, ignore it, try next driver.\n *                                          Not a supported configuration for this device, ignore it, try next driver.\n *                                          Could not configure device, fatal, exit with fail.\n *                      }\n *              }\n *    }\n * 7: for(each driver) {\n *      7a: Ask device if it knows this VID/PID. Acts exactly like 6a, but using VID/PID\n * 8: if we get here, no driver likes the device plugged in, so exit failure.\n *\n */\nuint8_t USB::Configuring(uint8_t parent, uint8_t port, bool lowspeed) {\n        //uint8_t bAddress = 0;\n        //printf(\"Configuring: parent = %i, port = %i\\r\\n\", parent, port);\n        uint8_t devConfigIndex;\n        uint8_t rcode = 0;\n        uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];\n        USB_DEVICE_DESCRIPTOR *udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR *>(buf);\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        EpInfo epInfo;\n\n        epInfo.epAddr = 0;\n        epInfo.maxPktSize = 8;\n        epInfo.epAttribs = 0;\n        epInfo.bmNakPower = USB_NAK_MAX_POWER;\n\n        //delay(2000);\n        AddressPool &addrPool = GetAddressPool();\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n        if(!p) {\n                //printf(\"Configuring error: USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL\\r\\n\");\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to\n        // avoid toggle inconsistence\n\n        p->epinfo = &epInfo;\n\n        p->lowspeed = lowspeed;\n        // Get device descriptor\n        rcode = getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);\n\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(rcode) {\n                //printf(\"Configuring error: Can't get USB_DEVICE_DESCRIPTOR\\r\\n\");\n                return rcode;\n        }\n\n        // to-do?\n        // Allocate new address according to device class\n        //bAddress = addrPool.AllocAddress(parent, false, port);\n\n        uint16_t vid = udd->idVendor;\n        uint16_t pid = udd->idProduct;\n        uint8_t klass = udd->bDeviceClass;\n        uint8_t subklass = udd->bDeviceSubClass;\n        // Attempt to configure if VID/PID or device class matches with a driver\n        // Qualify with subclass too.\n        //\n        // VID/PID & class tests default to false for drivers not yet ported\n        // subclass defaults to true, so you don't have to define it if you don't have to.\n        //\n        for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) {\n                if(!devConfig[devConfigIndex]) continue; // no driver\n                if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed\n                if(devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) {\n                        rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed);\n                        if(rcode != USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED)\n                                break;\n                }\n        }\n\n        if(devConfigIndex < USB_NUMDEVICES) {\n                return rcode;\n        }\n\n\n        // blindly attempt to configure\n        for(devConfigIndex = 0; devConfigIndex < USB_NUMDEVICES; devConfigIndex++) {\n                if(!devConfig[devConfigIndex]) continue;\n                if(devConfig[devConfigIndex]->GetAddress()) continue; // consumed\n                if(devConfig[devConfigIndex]->DEVSUBCLASSOK(subklass) && (devConfig[devConfigIndex]->VIDPIDOK(vid, pid) || devConfig[devConfigIndex]->DEVCLASSOK(klass))) continue; // If this is true it means it must have returned USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED above\n                rcode = AttemptConfig(devConfigIndex, parent, port, lowspeed);\n\n                //printf(\"ERROR ENUMERATING %2.2x\\r\\n\", rcode);\n                if(!(rcode == USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED || rcode == USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE)) {\n                        // in case of an error dev_index should be reset to 0\n                        //                in order to start from the very beginning the\n                        //                next time the program gets here\n                        //if (rcode != USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE)\n                        //        devConfigIndex = 0;\n                        return rcode;\n                }\n        }\n        // if we get here that means that the device class is not supported by any of registered classes\n        rcode = DefaultAddressing(parent, port, lowspeed);\n\n        return rcode;\n}\n\nuint8_t USB::ReleaseDevice(uint8_t addr) {\n        if(!addr)\n                return 0;\n\n        for(uint8_t i = 0; i < USB_NUMDEVICES; i++) {\n                if(!devConfig[i]) continue;\n                if(devConfig[i]->GetAddress() == addr)\n                        return devConfig[i]->Release();\n        }\n        return 0;\n}\n\n#if 1 //!defined(USB_METHODS_INLINE)\n//get device descriptor\n\nuint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) {\n        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, nbytes, dataptr, NULL));\n}\n//get configuration descriptor\n\nuint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) {\n        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, nbytes, dataptr, NULL));\n}\n\n/* Requests Configuration Descriptor. Sends two Get Conf Descr requests. The first one gets the total length of all descriptors, then the second one requests this\n total length. The length of the first request can be shorter ( 4 bytes ), however, there are devices which won't work unless this length is set to 9 */\nuint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p) {\n        const uint8_t bufSize = 64;\n        uint8_t buf[bufSize];\n        USB_CONFIGURATION_DESCRIPTOR *ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR *>(buf);\n\n        uint8_t ret = getConfDescr(addr, ep, 9, conf, buf);\n\n        if(ret)\n                return ret;\n\n        uint16_t total = ucd->wTotalLength;\n\n        //USBTRACE2(\"\\r\\ntotal conf.size:\", total);\n\n        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, total, bufSize, buf, p));\n}\n\n//get string descriptor\n\nuint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t ns, uint8_t index, uint16_t langid, uint8_t* dataptr) {\n        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, ns, ns, dataptr, NULL));\n}\n//set address\n\nuint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) {\n        uint8_t rcode = ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL);\n        //delay(2); //per USB 2.0 sect.9.2.6.3\n        delay(300); // Older spec says you should wait at least 200ms\n        return rcode;\n        //return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL));\n}\n//set configuration\n\nuint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) {\n        return ( ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, 0x0000, NULL, NULL));\n}\n\n#endif // defined(USB_METHODS_INLINE)\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/Usb.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n/* USB functions */\n#ifndef _usb_h_\n#define _usb_h_\n\n// WARNING: Do not change the order of includes, or stuff will break!\n#include <inttypes.h>\n#include <stddef.h>\n#include <stdio.h>\n\n// None of these should ever be included by a driver, or a user's sketch.\n#include \"settings.h\"\n#include \"printhex.h\"\n#include \"message.h\"\n#include \"hexdump.h\"\n#include \"sink_parser.h\"\n#include \"max3421e.h\"\n#include \"address.h\"\n#include \"avrpins.h\"\n#include \"usb_ch9.h\"\n#include \"usbhost.h\"\n#include \"UsbCore.h\"\n#include \"parsetools.h\"\n#include \"confdescparser.h\"\n\n#endif //_usb_h_\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/UsbCore.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#if !defined(_usb_h_) || defined(USBCORE_H)\n#error \"Never include UsbCore.h directly; include Usb.h instead\"\n#else\n#define USBCORE_H\n\n// Not used anymore? If anyone uses this, please let us know so that this may be\n// moved to the proper place, settings.h.\n//#define USB_METHODS_INLINE\n\n/* shield pins. First parameter - SS pin, second parameter - INT pin */\n#ifdef BOARD_BLACK_WIDDOW\ntypedef MAX3421e<P6, P3> MAX3421E; // Black Widow\n#elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))\n#if EXT_RAM\ntypedef MAX3421e<P20, P7> MAX3421E; // Teensy++ 2.0 with XMEM2\n#else\ntypedef MAX3421e<P9, P8> MAX3421E; // Teensy++ 1.0 and 2.0\n#endif\n#elif defined(BOARD_MEGA_ADK)\ntypedef MAX3421e<P53, P54> MAX3421E; // Arduino Mega ADK\n#elif defined(ARDUINO_AVR_BALANDUINO)\ntypedef MAX3421e<P20, P19> MAX3421E; // Balanduino\n#elif defined(__ARDUINO_X86__) && PLATFORM_ID == 0x06\ntypedef MAX3421e<P3, P2> MAX3421E; // The Intel Galileo supports much faster read and write speed at pin 2 and 3\n#else\ntypedef MAX3421e<P10, P9> MAX3421E; // Official Arduinos (UNO, Duemilanove, Mega, 2560, Leonardo, Due etc.), Intel Edison, Intel Galileo 2 or Teensy 2.0 and 3.0\n#endif\n\n/* Common setup data constant combinations  */\n#define bmREQ_GET_DESCR     USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE     //get descriptor request type\n#define bmREQ_SET           USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE     //set request type for all but 'set feature' and 'set interface'\n#define bmREQ_CL_GET_INTF   USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE     //get interface request type\n\n// D7           data transfer direction (0 - host-to-device, 1 - device-to-host)\n// D6-5         Type (0- standard, 1 - class, 2 - vendor, 3 - reserved)\n// D4-0         Recipient (0 - device, 1 - interface, 2 - endpoint, 3 - other, 4..31 - reserved)\n\n// USB Device Classes\n#define USB_CLASS_USE_CLASS_INFO        0x00    // Use Class Info in the Interface Descriptors\n#define USB_CLASS_AUDIO                 0x01    // Audio\n#define USB_CLASS_COM_AND_CDC_CTRL      0x02    // Communications and CDC Control\n#define USB_CLASS_HID                   0x03    // HID\n#define USB_CLASS_PHYSICAL              0x05    // Physical\n#define USB_CLASS_IMAGE                 0x06    // Image\n#define USB_CLASS_PRINTER               0x07    // Printer\n#define USB_CLASS_MASS_STORAGE          0x08    // Mass Storage\n#define USB_CLASS_HUB                   0x09    // Hub\n#define USB_CLASS_CDC_DATA              0x0a    // CDC-Data\n#define USB_CLASS_SMART_CARD            0x0b    // Smart-Card\n#define USB_CLASS_CONTENT_SECURITY      0x0d    // Content Security\n#define USB_CLASS_VIDEO                 0x0e    // Video\n#define USB_CLASS_PERSONAL_HEALTH       0x0f    // Personal Healthcare\n#define USB_CLASS_DIAGNOSTIC_DEVICE     0xdc    // Diagnostic Device\n#define USB_CLASS_WIRELESS_CTRL         0xe0    // Wireless Controller\n#define USB_CLASS_MISC                  0xef    // Miscellaneous\n#define USB_CLASS_APP_SPECIFIC          0xfe    // Application Specific\n#define USB_CLASS_VENDOR_SPECIFIC       0xff    // Vendor Specific\n\n// Additional Error Codes\n#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED       0xD1\n#define USB_DEV_CONFIG_ERROR_DEVICE_INIT_INCOMPLETE     0xD2\n#define USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS       0xD3\n#define USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL          0xD4\n#define USB_ERROR_HUB_ADDRESS_OVERFLOW                  0xD5\n#define USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL             0xD6\n#define USB_ERROR_EPINFO_IS_NULL                        0xD7\n#define USB_ERROR_INVALID_ARGUMENT                      0xD8\n#define USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE         0xD9\n#define USB_ERROR_INVALID_MAX_PKT_SIZE                  0xDA\n#define USB_ERROR_EP_NOT_FOUND_IN_TBL                   0xDB\n#define USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET      0xE0\n#define USB_ERROR_FailGetDevDescr                       0xE1\n#define USB_ERROR_FailSetDevTblEntry                    0xE2\n#define USB_ERROR_FailGetConfDescr                      0xE3\n#define USB_ERROR_TRANSFER_TIMEOUT                      0xFF\n\n#define USB_XFER_TIMEOUT        5000    // (5000) USB transfer timeout in milliseconds, per section 9.2.6.1 of USB 2.0 spec\n//#define USB_NAK_LIMIT         32000   // NAK limit for a transfer. 0 means NAKs are not counted\n#define USB_RETRY_LIMIT         3       // 3 retry limit for a transfer\n#define USB_SETTLE_DELAY        200     // settle delay in milliseconds\n\n#define USB_NUMDEVICES          16      //number of USB devices\n//#define HUB_MAX_HUBS          7       // maximum number of hubs that can be attached to the host controller\n#define HUB_PORT_RESET_DELAY    20      // hub port reset delay 10 ms recomended, can be up to 20 ms\n\n/* USB state machine states */\n#define USB_STATE_MASK                                      0xf0\n\n#define USB_STATE_DETACHED                                  0x10\n#define USB_DETACHED_SUBSTATE_INITIALIZE                    0x11\n#define USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE               0x12\n#define USB_DETACHED_SUBSTATE_ILLEGAL                       0x13\n#define USB_ATTACHED_SUBSTATE_SETTLE                        0x20\n#define USB_ATTACHED_SUBSTATE_RESET_DEVICE                  0x30\n#define USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE           0x40\n#define USB_ATTACHED_SUBSTATE_WAIT_SOF                      0x50\n#define USB_ATTACHED_SUBSTATE_WAIT_RESET                    0x51\n#define USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE    0x60\n#define USB_STATE_ADDRESSING                                0x70\n#define USB_STATE_CONFIGURING                               0x80\n#define USB_STATE_RUNNING                                   0x90\n#define USB_STATE_ERROR                                     0xa0\n\nclass USBDeviceConfig {\npublic:\n\n        virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed) {\n                return 0;\n        }\n\n        virtual uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {\n                return 0;\n        }\n\n        virtual uint8_t Release() {\n                return 0;\n        }\n\n        virtual uint8_t Poll() {\n                return 0;\n        }\n\n        virtual uint8_t GetAddress() {\n                return 0;\n        }\n\n        virtual void ResetHubPort(uint8_t port) {\n                return;\n        } // Note used for hubs only!\n\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                return false;\n        }\n\n        virtual bool DEVCLASSOK(uint8_t klass) {\n                return false;\n        }\n\n        virtual bool DEVSUBCLASSOK(uint8_t subklass) {\n                return true;\n        }\n\n};\n\n/* USB Setup Packet Structure   */\ntypedef struct {\n\n        union { // offset   description\n                uint8_t bmRequestType; //   0      Bit-map of request type\n\n                struct {\n                        uint8_t recipient : 5; //          Recipient of the request\n                        uint8_t type : 2; //          Type of request\n                        uint8_t direction : 1; //          Direction of data X-fer\n                } __attribute__((packed));\n        } ReqType_u;\n        uint8_t bRequest; //   1      Request\n\n        union {\n                uint16_t wValue; //   2      Depends on bRequest\n\n                struct {\n                        uint8_t wValueLo;\n                        uint8_t wValueHi;\n                } __attribute__((packed));\n        } wVal_u;\n        uint16_t wIndex; //   4      Depends on bRequest\n        uint16_t wLength; //   6      Depends on bRequest\n} __attribute__((packed)) SETUP_PKT, *PSETUP_PKT;\n\n\n\n// Base class for incoming data parser\n\nclass USBReadParser {\npublic:\n        virtual void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset) = 0;\n};\n\nclass USB : public MAX3421E {\n        AddressPoolImpl<USB_NUMDEVICES> addrPool;\n        USBDeviceConfig* devConfig[USB_NUMDEVICES];\n        uint8_t bmHubPre;\n\npublic:\n        USB(void);\n\n        void SetHubPreMask() {\n                bmHubPre |= bmHUBPRE;\n        };\n\n        void ResetHubPreMask() {\n                bmHubPre &= (~bmHUBPRE);\n        };\n\n        AddressPool& GetAddressPool() {\n                return (AddressPool&)addrPool;\n        };\n\n        uint8_t RegisterDeviceClass(USBDeviceConfig *pdev) {\n                for(uint8_t i = 0; i < USB_NUMDEVICES; i++) {\n                        if(!devConfig[i]) {\n                                devConfig[i] = pdev;\n                                return 0;\n                        }\n                }\n                return USB_ERROR_UNABLE_TO_REGISTER_DEVICE_CLASS;\n        };\n\n        void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) {\n                addrPool.ForEachUsbDevice(pfunc);\n        };\n        uint8_t getUsbTaskState(void);\n        void setUsbTaskState(uint8_t state);\n\n        EpInfo* getEpInfoEntry(uint8_t addr, uint8_t ep);\n        uint8_t setEpInfoEntry(uint8_t addr, uint8_t epcount, EpInfo* eprecord_ptr);\n\n        /* Control requests */\n        uint8_t getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr);\n        uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr);\n\n        uint8_t getConfDescr(uint8_t addr, uint8_t ep, uint8_t conf, USBReadParser *p);\n\n        uint8_t getStrDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t index, uint16_t langid, uint8_t* dataptr);\n        uint8_t setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr);\n        uint8_t setConf(uint8_t addr, uint8_t ep, uint8_t conf_value);\n        /**/\n        uint8_t ctrlData(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr, bool direction);\n        uint8_t ctrlStatus(uint8_t ep, bool direction, uint16_t nak_limit);\n        uint8_t inTransfer(uint8_t addr, uint8_t ep, uint16_t *nbytesptr, uint8_t* data);\n        uint8_t outTransfer(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* data);\n        uint8_t dispatchPkt(uint8_t token, uint8_t ep, uint16_t nak_limit);\n\n        void Task(void);\n\n        uint8_t DefaultAddressing(uint8_t parent, uint8_t port, bool lowspeed);\n        uint8_t Configuring(uint8_t parent, uint8_t port, bool lowspeed);\n        uint8_t ReleaseDevice(uint8_t addr);\n\n        uint8_t ctrlReq(uint8_t addr, uint8_t ep, uint8_t bmReqType, uint8_t bRequest, uint8_t wValLo, uint8_t wValHi,\n                uint16_t wInd, uint16_t total, uint16_t nbytes, uint8_t* dataptr, USBReadParser *p);\n\nprivate:\n        void init();\n        uint8_t SetAddress(uint8_t addr, uint8_t ep, EpInfo **ppep, uint16_t *nak_limit);\n        uint8_t OutTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t nbytes, uint8_t *data);\n        uint8_t InTransfer(EpInfo *pep, uint16_t nak_limit, uint16_t *nbytesptr, uint8_t *data);\n        uint8_t AttemptConfig(uint8_t driver, uint8_t parent, uint8_t port, bool lowspeed);\n};\n\n#if 0 //defined(USB_METHODS_INLINE)\n//get device descriptor\n\ninline uint8_t USB::getDevDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t* dataptr) {\n        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, USB_DESCRIPTOR_DEVICE, 0x0000, nbytes, dataptr));\n}\n//get configuration descriptor\n\ninline uint8_t USB::getConfDescr(uint8_t addr, uint8_t ep, uint16_t nbytes, uint8_t conf, uint8_t* dataptr) {\n        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, conf, USB_DESCRIPTOR_CONFIGURATION, 0x0000, nbytes, dataptr));\n}\n//get string descriptor\n\ninline uint8_t USB::getStrDescr(uint8_t addr, uint8_t ep, uint16_t nuint8_ts, uint8_t index, uint16_t langid, uint8_t* dataptr) {\n        return ( ctrlReq(addr, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, index, USB_DESCRIPTOR_STRING, langid, nuint8_ts, dataptr));\n}\n//set address\n\ninline uint8_t USB::setAddr(uint8_t oldaddr, uint8_t ep, uint8_t newaddr) {\n        return ( ctrlReq(oldaddr, ep, bmREQ_SET, USB_REQUEST_SET_ADDRESS, newaddr, 0x00, 0x0000, 0x0000, NULL));\n}\n//set configuration\n\ninline uint8_t USB::setConf(uint8_t addr, uint8_t ep, uint8_t conf_value) {\n        return ( ctrlReq(addr, ep, bmREQ_SET, USB_REQUEST_SET_CONFIGURATION, conf_value, 0x00, 0x0000, 0x0000, NULL));\n}\n\n#endif // defined(USB_METHODS_INLINE)\n\n#endif /* USBCORE_H */\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/Wii.cpp",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n\n IR camera support added by Allan Glover (adglover9.81@gmail.com) and Kristian Lauszus\n */\n\n#include \"Wii.h\"\n// To enable serial debugging see \"settings.h\"\n//#define EXTRADEBUG // Uncomment to get even more debugging data\n//#define PRINTREPORT // Uncomment to print the report send by the Wii controllers\n\nconst uint8_t WII_LEDS[] PROGMEM = {\n        0x00, // OFF\n        0x10, // LED1\n        0x20, // LED2\n        0x40, // LED3\n        0x80, // LED4\n\n        0x90, // LED5\n        0xA0, // LED6\n        0xC0, // LED7\n        0xD0, // LED8\n        0xE0, // LED9\n        0xF0, // LED10\n};\n\nconst uint32_t WII_BUTTONS[] PROGMEM = {\n        0x00008, // UP\n        0x00002, // RIGHT\n        0x00004, // DOWN\n        0x00001, // LEFT\n\n        0, // Skip\n        0x00010, // PLUS\n        0x00100, // TWO\n        0x00200, // ONE\n\n        0x01000, // MINUS\n        0x08000, // HOME\n        0x10000, // Z\n        0x20000, // C\n\n        0x00400, // B\n        0x00800, // A\n};\nconst uint32_t WII_PROCONTROLLER_BUTTONS[] PROGMEM = {\n        0x00100, // UP\n        0x00080, // RIGHT\n        0x00040, // DOWN\n        0x00200, // LEFT\n\n        0, // Skip\n        0x00004, // PLUS\n        0x20000, // L3\n        0x10000, // R3\n\n        0x00010, // MINUS\n        0x00008, // HOME\n        0, 0, // Skip\n\n        0x04000, // B\n        0x01000, // A\n        0x00800, // X\n        0x02000, // Y\n\n        0x00020, // L\n        0x00002, // R\n        0x08000, // ZL\n        0x00400, // ZR\n};\n\nWII::WII(BTD *p, bool pair) :\nBluetoothService(p) // Pointer to USB class instance - mandatory\n{\n        pBtd->pairWithWii = pair;\n\n        HIDBuffer[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)\n\n        /* Set device cid for the control and intterrupt channelse - LSB */\n        control_dcid[0] = 0x60; // 0x0060\n        control_dcid[1] = 0x00;\n        interrupt_dcid[0] = 0x61; // 0x0061\n        interrupt_dcid[1] = 0x00;\n\n        Reset();\n}\n\nvoid WII::Reset() {\n        wiimoteConnected = false;\n        nunchuckConnected = false;\n        motionPlusConnected = false;\n        activateNunchuck = false;\n        motionValuesReset = false;\n        activeConnection = false;\n        motionPlusInside = false;\n        pBtd->wiiUProController = false;\n        wiiUProControllerConnected = false;\n        wiiBalanceBoardConnected = false;\n        l2cap_event_flag = 0; // Reset flags\n        l2cap_state = L2CAP_WAIT;\n}\n\nvoid WII::disconnect() { // Use this void to disconnect any of the controllers\n        if(!motionPlusInside) { // The old Wiimote needs a delay after the first command or it will automatically reconnect\n                if(motionPlusConnected) {\n#ifdef DEBUG_USB_HOST\n                        Notify(PSTR(\"\\r\\nDeactivating Motion Plus\"), 0x80);\n#endif\n                        initExtension1(); // This will disable the Motion Plus extension\n                }\n                timer = millis() + 1000; // We have to wait for the message before the rest of the channels can be deactivated\n        } else\n                timer = millis(); // Don't wait\n        // First the HID interrupt channel has to be disconnected, then the HID control channel and finally the HCI connection\n        pBtd->l2cap_disconnection_request(hci_handle, ++identifier, interrupt_scid, interrupt_dcid);\n        Reset();\n        l2cap_state = L2CAP_INTERRUPT_DISCONNECT;\n}\n\nvoid WII::ACLData(uint8_t* l2capinbuf) {\n        if(!pBtd->l2capConnectionClaimed && pBtd->incomingWii && !wiimoteConnected && !activeConnection) {\n                if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {\n                        if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {\n                                motionPlusInside = pBtd->motionPlusInside;\n                                pBtd->incomingWii = false;\n                                pBtd->l2capConnectionClaimed = true; // Claim that the incoming connection belongs to this service\n                                activeConnection = true;\n                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection\n                                l2cap_state = L2CAP_WAIT;\n                        }\n                }\n        }\n\n        if(checkHciHandle(l2capinbuf, hci_handle)) { // acl_handle_ok\n                if((l2capinbuf[6] | (l2capinbuf[7] << 8)) == 0x0001U) { // l2cap_control - Channel ID for ACL-U\n                        if(l2capinbuf[8] == L2CAP_CMD_COMMAND_REJECT) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nL2CAP Command Rejected - Reason: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[17], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[16], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);\n#endif\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_RESPONSE) {\n                                if(((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) && ((l2capinbuf[18] | (l2capinbuf[19] << 8)) == SUCCESSFUL)) { // Success\n                                        if(l2capinbuf[14] == control_dcid[0] && l2capinbuf[15] == control_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nHID Control Connection Complete\"), 0x80);\n                                                identifier = l2capinbuf[9];\n                                                control_scid[0] = l2capinbuf[12];\n                                                control_scid[1] = l2capinbuf[13];\n                                                l2cap_set_flag(L2CAP_FLAG_CONTROL_CONNECTED);\n                                        } else if(l2capinbuf[14] == interrupt_dcid[0] && l2capinbuf[15] == interrupt_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nHID Interrupt Connection Complete\"), 0x80);\n                                                identifier = l2capinbuf[9];\n                                                interrupt_scid[0] = l2capinbuf[12];\n                                                interrupt_scid[1] = l2capinbuf[13];\n                                                l2cap_set_flag(L2CAP_FLAG_INTERRUPT_CONNECTED);\n                                        }\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONNECTION_REQUEST) {\n#ifdef EXTRADEBUG\n                                Notify(PSTR(\"\\r\\nL2CAP Connection Request - PSM: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);\n                                Notify(PSTR(\" SCID: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[15], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);\n                                Notify(PSTR(\" Identifier: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);\n#endif\n                                if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_CTRL_PSM) {\n                                        identifier = l2capinbuf[9];\n                                        control_scid[0] = l2capinbuf[14];\n                                        control_scid[1] = l2capinbuf[15];\n                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST);\n                                } else if((l2capinbuf[12] | (l2capinbuf[13] << 8)) == HID_INTR_PSM) {\n                                        identifier = l2capinbuf[9];\n                                        interrupt_scid[0] = l2capinbuf[14];\n                                        interrupt_scid[1] = l2capinbuf[15];\n                                        l2cap_set_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST);\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_RESPONSE) {\n                                if((l2capinbuf[16] | (l2capinbuf[17] << 8)) == 0x0000) { // Success\n                                        if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nHID Control Configuration Complete\"), 0x80);\n                                                identifier = l2capinbuf[9];\n                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS);\n                                        } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {\n                                                //Notify(PSTR(\"\\r\\nHID Interrupt Configuration Complete\"), 0x80);\n                                                identifier = l2capinbuf[9];\n                                                l2cap_set_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS);\n                                        }\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_CONFIG_REQUEST) {\n                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {\n                                        //Notify(PSTR(\"\\r\\nHID Control Configuration Request\"), 0x80);\n                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], control_scid);\n                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {\n                                        //Notify(PSTR(\"\\r\\nHID Interrupt Configuration Request\"), 0x80);\n                                        pBtd->l2cap_config_response(hci_handle, l2capinbuf[9], interrupt_scid);\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_REQUEST) {\n                                if(l2capinbuf[12] == control_dcid[0] && l2capinbuf[13] == control_dcid[1]) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nDisconnect Request: Control Channel\"), 0x80);\n#endif\n                                        identifier = l2capinbuf[9];\n                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, control_dcid, control_scid);\n                                        Reset();\n                                } else if(l2capinbuf[12] == interrupt_dcid[0] && l2capinbuf[13] == interrupt_dcid[1]) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nDisconnect Request: Interrupt Channel\"), 0x80);\n#endif\n                                        identifier = l2capinbuf[9];\n                                        pBtd->l2cap_disconnection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid);\n                                        Reset();\n                                }\n                        } else if(l2capinbuf[8] == L2CAP_CMD_DISCONNECT_RESPONSE) {\n                                if(l2capinbuf[12] == control_scid[0] && l2capinbuf[13] == control_scid[1]) {\n                                        //Notify(PSTR(\"\\r\\nDisconnect Response: Control Channel\"), 0x80);\n                                        identifier = l2capinbuf[9];\n                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE);\n                                } else if(l2capinbuf[12] == interrupt_scid[0] && l2capinbuf[13] == interrupt_scid[1]) {\n                                        //Notify(PSTR(\"\\r\\nDisconnect Response: Interrupt Channel\"), 0x80);\n                                        identifier = l2capinbuf[9];\n                                        l2cap_set_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE);\n                                }\n                        }\n#ifdef EXTRADEBUG\n                        else {\n                                identifier = l2capinbuf[9];\n                                Notify(PSTR(\"\\r\\nL2CAP Unknown Signaling Command: \"), 0x80);\n                                D_PrintHex<uint8_t > (l2capinbuf[8], 0x80);\n                        }\n#endif\n                } else if(l2capinbuf[6] == interrupt_dcid[0] && l2capinbuf[7] == interrupt_dcid[1]) { // l2cap_interrupt\n                        //Notify(PSTR(\"\\r\\nL2CAP Interrupt\"), 0x80);\n                        if(l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT\n                                if((l2capinbuf[9] >= 0x20 && l2capinbuf[9] <= 0x22) || (l2capinbuf[9] >= 0x30 && l2capinbuf[9] <= 0x37) || l2capinbuf[9] == 0x3e || l2capinbuf[9] == 0x3f) { // These reports include the buttons\n                                        if((l2capinbuf[9] >= 0x20 && l2capinbuf[9] <= 0x22) || l2capinbuf[9] == 0x31 || l2capinbuf[9] == 0x33) // These reports have no extensions bytes\n                                                ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8));\n                                        else if(wiiUProControllerConnected)\n                                                ButtonState = (uint32_t)(((~l2capinbuf[23]) & 0xFE) | ((uint16_t)(~l2capinbuf[24]) << 8) | ((uint32_t)((~l2capinbuf[25]) & 0x03) << 16));\n                                        else if(motionPlusConnected) {\n                                                if(l2capinbuf[20] & 0x02) // Only update the Wiimote buttons, since the extension bytes are from the Motion Plus\n                                                        ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)(ButtonState & 0xFFFF0000)));\n                                                else if(nunchuckConnected) // Update if it's a report from the Nunchuck\n                                                        ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)((~l2capinbuf[20]) & 0x0C) << 14));\n                                                //else if(classicControllerConnected) // Update if it's a report from the Classic Controller\n                                        } else if(nunchuckConnected) // The Nunchuck is directly connected\n                                                ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8) | ((uint32_t)((~l2capinbuf[20]) & 0x03) << 16));\n                                                //else if(classicControllerConnected) // The Classic Controller is directly connected\n                                        else if(!unknownExtensionConnected)\n                                                ButtonState = (uint32_t)((l2capinbuf[10] & 0x1F) | ((uint16_t)(l2capinbuf[11] & 0x9F) << 8));\n#ifdef PRINTREPORT\n                                        Notify(PSTR(\"ButtonState: \"), 0x80);\n                                        D_PrintHex<uint32_t > (ButtonState, 0x80);\n                                        Notify(PSTR(\"\\r\\n\"), 0x80);\n#endif\n                                        if(ButtonState != OldButtonState) {\n                                                ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable\n                                                OldButtonState = ButtonState;\n                                        }\n                                }\n                                if(l2capinbuf[9] == 0x31 || l2capinbuf[9] == 0x33 || l2capinbuf[9] == 0x35 || l2capinbuf[9] == 0x37) { // Read the accelerometer\n                                        accXwiimote = ((l2capinbuf[12] << 2) | (l2capinbuf[10] & 0x60 >> 5)) - 500;\n                                        accYwiimote = ((l2capinbuf[13] << 2) | (l2capinbuf[11] & 0x20 >> 4)) - 500;\n                                        accZwiimote = ((l2capinbuf[14] << 2) | (l2capinbuf[11] & 0x40 >> 5)) - 500;\n                                }\n                                switch(l2capinbuf[9]) {\n                                        case 0x20: // Status Information - (a1) 20 BB BB LF 00 00 VV\n#ifdef EXTRADEBUG\n                                                Notify(PSTR(\"\\r\\nStatus report was received\"), 0x80);\n#endif\n                                                wiiState = l2capinbuf[12]; // (0x01: Battery is nearly empty), (0x02:  An Extension Controller is connected), (0x04: Speaker enabled), (0x08: IR enabled), (0x10: LED1, 0x20: LED2, 0x40: LED3, 0x80: LED4)\n                                                batteryLevel = l2capinbuf[15]; // Update battery level\n\n                                                if(!checkBatteryLevel) { // If this is true it means that the user must have called getBatteryLevel()\n                                                        if(l2capinbuf[12] & 0x02) { // Check if a extension is connected\n#ifdef DEBUG_USB_HOST\n                                                                if(!unknownExtensionConnected)\n                                                                        Notify(PSTR(\"\\r\\nExtension connected\"), 0x80);\n#endif\n                                                                unknownExtensionConnected = true;\n#ifdef WIICAMERA\n                                                                if(!isIRCameraEnabled()) // Don't activate the Motion Plus if we are trying to initialize the IR camera\n#endif\n                                                                        setReportMode(false, 0x35); // Also read the extension\n                                                        } else {\n#ifdef DEBUG_USB_HOST\n                                                                Notify(PSTR(\"\\r\\nExtension disconnected\"), 0x80);\n#endif\n                                                                if(motionPlusConnected) {\n#ifdef DEBUG_USB_HOST\n                                                                        Notify(PSTR(\" - from Motion Plus\"), 0x80);\n#endif\n                                                                        wii_clear_flag(WII_FLAG_NUNCHUCK_CONNECTED);\n                                                                        if(!activateNunchuck) // If it's already trying to initialize the Nunchuck don't set it to false\n                                                                                nunchuckConnected = false;\n                                                                        //else if(classicControllerConnected)\n                                                                } else if(nunchuckConnected) {\n#ifdef DEBUG_USB_HOST\n                                                                        Notify(PSTR(\" - Nunchuck\"), 0x80);\n#endif\n                                                                        nunchuckConnected = false; // It must be the Nunchuck controller then\n                                                                        wii_clear_flag(WII_FLAG_NUNCHUCK_CONNECTED);\n                                                                        onInit();\n                                                                        setReportMode(false, 0x31); // If there is no extension connected we will read the buttons and accelerometer\n                                                                } else\n                                                                        setReportMode(false, 0x31); // If there is no extension connected we will read the buttons and accelerometer\n                                                        }\n                                                }\n                                                else {\n#ifdef EXTRADEBUG\n                                                        Notify(PSTR(\"\\r\\nChecking battery level\"), 0x80);\n#endif\n                                                        checkBatteryLevel = false; // Check for extensions by default\n                                                }\n#ifdef DEBUG_USB_HOST\n                                                if(l2capinbuf[12] & 0x01)\n                                                        Notify(PSTR(\"\\r\\nWARNING: Battery is nearly empty\"), 0x80);\n#endif\n\n                                                break;\n                                        case 0x21: // Read Memory Data\n                                                if((l2capinbuf[12] & 0x0F) == 0) { // No error\n                                                        uint8_t reportLength = (l2capinbuf[12] >> 4) + 1; // // Bit 4-7 is the length - 1\n                                                        // See: http://wiibrew.org/wiki/Wiimote/Extension_Controllers\n                                                        if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x00 && l2capinbuf[20] == 0x00) {\n#ifdef DEBUG_USB_HOST\n                                                                Notify(PSTR(\"\\r\\nNunchuck connected\"), 0x80);\n#endif\n                                                                wii_set_flag(WII_FLAG_NUNCHUCK_CONNECTED);\n                                                        } else if(l2capinbuf[16] == 0x00 && (l2capinbuf[17] == 0xA6 || l2capinbuf[17] == 0xA4) && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x00 && l2capinbuf[20] == 0x05) {\n#ifdef DEBUG_USB_HOST\n                                                                Notify(PSTR(\"\\r\\nMotion Plus connected\"), 0x80);\n#endif\n                                                                wii_set_flag(WII_FLAG_MOTION_PLUS_CONNECTED);\n                                                        } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x04 && l2capinbuf[20] == 0x05) {\n#ifdef DEBUG_USB_HOST\n                                                                Notify(PSTR(\"\\r\\nMotion Plus activated in normal mode\"), 0x80);\n#endif\n                                                                motionPlusConnected = true;\n#ifdef WIICAMERA\n                                                                if(!isIRCameraEnabled()) // Don't activate the Motion Plus if we are trying to initialize the IR camera\n#endif\n                                                                        setReportMode(false, 0x35); // Also read the extension\n                                                        } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x05 && l2capinbuf[20] == 0x05) {\n#ifdef DEBUG_USB_HOST\n                                                                Notify(PSTR(\"\\r\\nMotion Plus activated in Nunchuck pass-through mode\"), 0x80);\n#endif\n                                                                activateNunchuck = false;\n                                                                motionPlusConnected = true;\n                                                                nunchuckConnected = true;\n#ifdef WIICAMERA\n                                                                if(!isIRCameraEnabled()) // Don't activate the Motion Plus if we are trying to initialize the IR camera\n#endif\n                                                                        setReportMode(false, 0x35); // Also read the extension\n                                                        } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA6 && l2capinbuf[18] == 0x20 && (l2capinbuf[19] == 0x00 || l2capinbuf[19] == 0x04 || l2capinbuf[19] == 0x05 || l2capinbuf[19] == 0x07) && l2capinbuf[20] == 0x05) {\n#ifdef DEBUG_USB_HOST\n                                                                Notify(PSTR(\"\\r\\nInactive Wii Motion Plus\"), 0x80);\n                                                                Notify(PSTR(\"\\r\\nPlease unplug the Motion Plus, disconnect the Wiimote and then replug the Motion Plus Extension\"), 0x80);\n#endif\n                                                                stateCounter = 300; // Skip the rest in \"WII_CHECK_MOTION_PLUS_STATE\"\n                                                        } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x01 && l2capinbuf[20] == 0x20) {\n#ifdef DEBUG_USB_HOST\n                                                                Notify(PSTR(\"\\r\\nWii U Pro Controller connected\"), 0x80);\n#endif\n                                                                wiiUProControllerConnected = true;\n                                                        } else if(l2capinbuf[16] == 0x00 && l2capinbuf[17] == 0xA4 && l2capinbuf[18] == 0x20 && l2capinbuf[19] == 0x04 && l2capinbuf[20] == 0x02) {\n#ifdef DEBUG_USB_HOST\n                                                                Notify(PSTR(\"\\r\\nWii Balance Board connected\"), 0x80);\n#endif\n                                                                setReportMode(false, 0x32); // Read the Wii Balance Board extension\n                                                                wii_set_flag(WII_FLAG_CALIBRATE_BALANCE_BOARD);\n                                                        }\n                                                        // Wii Balance Board calibration reports (24 bits in total)\n                                                        else if(l2capinbuf[13] == 0x00 && l2capinbuf[14] == 0x24 && reportLength == 16) { // First 16-bit\n                                                                for(uint8_t i = 0; i < 2; i++) {\n                                                                        for(uint8_t j = 0; j < 4; j++)\n                                                                                wiiBalanceBoardCal[i][j] = l2capinbuf[16 + 8 * i + 2 * j] | l2capinbuf[15 + 8 * i + 2 * j] << 8;\n                                                                }\n                                                        } else if(l2capinbuf[13] == 0x00 && l2capinbuf[14] == 0x34 && reportLength == 8) { // Last 8-bit\n                                                                for(uint8_t j = 0; j < 4; j++)\n                                                                        wiiBalanceBoardCal[2][j] = l2capinbuf[16 + 2 * j] | l2capinbuf[15 + 2 * j] << 8;\n#ifdef DEBUG_USB_HOST\n                                                                Notify(PSTR(\"\\r\\nWii Balance Board calibration values read successfully\"), 0x80);\n#endif\n                                                                wii_clear_flag(WII_FLAG_CALIBRATE_BALANCE_BOARD);\n                                                                wiiBalanceBoardConnected = true;\n                                                        }\n#ifdef DEBUG_USB_HOST\n                                                        else {\n                                                                Notify(PSTR(\"\\r\\nUnknown Device: \"), 0x80);\n                                                                D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);\n                                                                D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);\n                                                                Notify(PSTR(\"\\r\\nData: \"), 0x80);\n                                                                for(uint8_t i = 0; i < reportLength; i++) {\n                                                                        D_PrintHex<uint8_t > (l2capinbuf[15 + i], 0x80);\n                                                                        Notify(PSTR(\" \"), 0x80);\n                                                                }\n                                                        }\n#endif\n                                                }\n#ifdef EXTRADEBUG\n                                                else {\n                                                        Notify(PSTR(\"\\r\\nReport Error: \"), 0x80);\n                                                        D_PrintHex<uint8_t > (l2capinbuf[13], 0x80);\n                                                        D_PrintHex<uint8_t > (l2capinbuf[14], 0x80);\n                                                }\n#endif\n                                                break;\n                                        case 0x22: // Acknowledge output report, return function result\n#ifdef DEBUG_USB_HOST\n                                                if(l2capinbuf[13] != 0x00) { // Check if there is an error\n                                                        Notify(PSTR(\"\\r\\nCommand failed: \"), 0x80);\n                                                        D_PrintHex<uint8_t > (l2capinbuf[12], 0x80);\n                                                }\n#endif\n                                                break;\n                                        case 0x30: // Core buttons - (a1) 30 BB BB\n                                                break;\n                                        case 0x31: // Core Buttons and Accelerometer - (a1) 31 BB BB AA AA AA\n                                                break;\n                                        case 0x32: // Core Buttons with 8 Extension bytes - (a1) 32 BB BB EE EE EE EE EE EE EE EE\n                                                // See: http://wiibrew.org/wiki/Wii_Balance_Board#Data_Format\n                                                wiiBalanceBoardRaw[TopRight] = l2capinbuf[13] | l2capinbuf[12] << 8; // Top right\n                                                wiiBalanceBoardRaw[BotRight] = l2capinbuf[15] | l2capinbuf[14] << 8; // Bottom right\n                                                wiiBalanceBoardRaw[TopLeft] = l2capinbuf[17] | l2capinbuf[16] << 8; // Top left\n                                                wiiBalanceBoardRaw[BotLeft] = l2capinbuf[19] | l2capinbuf[18] << 8; // Bottom left\n                                                break;\n                                        case 0x33: // Core Buttons with Accelerometer and 12 IR bytes - (a1) 33 BB BB AA AA AA II II II II II II II II II II II II\n#ifdef WIICAMERA\n                                                // Read the IR data\n                                                IR_object_x1 = (l2capinbuf[15] | ((uint16_t)(l2capinbuf[17] & 0x30) << 4)); // x position\n                                                IR_object_y1 = (l2capinbuf[16] | ((uint16_t)(l2capinbuf[17] & 0xC0) << 2)); // y position\n                                                IR_object_s1 = (l2capinbuf[17] & 0x0F); // Size value, 0-15\n\n                                                IR_object_x2 = (l2capinbuf[18] | ((uint16_t)(l2capinbuf[20] & 0x30) << 4));\n                                                IR_object_y2 = (l2capinbuf[19] | ((uint16_t)(l2capinbuf[20] & 0xC0) << 2));\n                                                IR_object_s2 = (l2capinbuf[20] & 0x0F);\n\n                                                IR_object_x3 = (l2capinbuf[21] | ((uint16_t)(l2capinbuf[23] & 0x30) << 4));\n                                                IR_object_y3 = (l2capinbuf[22] | ((uint16_t)(l2capinbuf[23] & 0xC0) << 2));\n                                                IR_object_s3 = (l2capinbuf[23] & 0x0F);\n\n                                                IR_object_x4 = (l2capinbuf[24] | ((uint16_t)(l2capinbuf[26] & 0x30) << 4));\n                                                IR_object_y4 = (l2capinbuf[25] | ((uint16_t)(l2capinbuf[26] & 0xC0) << 2));\n                                                IR_object_s4 = (l2capinbuf[26] & 0x0F);\n#endif\n                                                break;\n                                        case 0x34: // Core Buttons with 19 Extension bytes - (a1) 34 BB BB EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE\n                                                break;\n                                                /* 0x3e and 0x3f both give unknown report types when report mode is 0x3e or 0x3f with mode number 0x05 */\n                                        case 0x3E: // Core Buttons with Accelerometer and 32 IR bytes\n                                                // (a1) 31 BB BB AA AA AA II II II II II II II II II II II II II II II II II II II II II II II II II II II II II II II II\n                                                // corresponds to output report mode 0x3e\n\n                                                /**** for reading in full mode: DOES NOT WORK YET ****/\n                                                /* When it works it will also have intensity and bounding box data */\n                                                /*\n                                                IR_object_x1 = (l2capinbuf[13] | ((uint16_t)(l2capinbuf[15] & 0x30) << 4));\n                                                IR_object_y1 = (l2capinbuf[14] | ((uint16_t)(l2capinbuf[15] & 0xC0) << 2));\n                                                IR_object_s1 = (l2capinbuf[15] & 0x0F);\n                                                 */\n                                                break;\n                                        case 0x3F:\n                                                /*\n                                                IR_object_x1 = (l2capinbuf[13] | ((uint16_t)(l2capinbuf[15] & 0x30) << 4));\n                                                IR_object_y1 = (l2capinbuf[14] | ((uint16_t)(l2capinbuf[15] & 0xC0) << 2));\n                                                IR_object_s1 = (l2capinbuf[15] & 0x0F);\n                                                 */\n                                                break;\n                                        case 0x35: // Core Buttons and Accelerometer with 16 Extension Bytes\n                                                // (a1) 35 BB BB AA AA AA EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE\n#if 1 // Set this to 0 if you don't want to use an extension, this reduceds the size of the library a lot!\n                                                if(motionPlusConnected) {\n                                                        if(l2capinbuf[20] & 0x02) { // Check if it's a report from the Motion controller or the extension\n                                                                if(motionValuesReset) { // We will only use the values when the gyro value has been set\n                                                                        gyroYawRaw = ((l2capinbuf[15] | ((l2capinbuf[18] & 0xFC) << 6)) - gyroYawZero);\n                                                                        gyroRollRaw = ((l2capinbuf[16] | ((l2capinbuf[19] & 0xFC) << 6)) - gyroRollZero);\n                                                                        gyroPitchRaw = ((l2capinbuf[17] | ((l2capinbuf[20] & 0xFC) << 6)) - gyroPitchZero);\n\n                                                                        yawGyroSpeed = (double)gyroYawRaw / ((double)gyroYawZero / yawGyroScale);\n                                                                        rollGyroSpeed = -(double)gyroRollRaw / ((double)gyroRollZero / rollGyroScale); // We invert these values so they will fit the acc values\n                                                                        pitchGyroSpeed = (double)gyroPitchRaw / ((double)gyroPitchZero / pitchGyroScale);\n\n                                                                        /* The onboard gyro has two ranges for slow and fast mode */\n                                                                        if(!(l2capinbuf[18] & 0x02)) // Check if fast mode is used\n                                                                                yawGyroSpeed *= 4.545;\n                                                                        if(!(l2capinbuf[18] & 0x01)) // Check if fast mode is used\n                                                                                pitchGyroSpeed *= 4.545;\n                                                                        if(!(l2capinbuf[19] & 0x02)) // Check if fast mode is used\n                                                                                rollGyroSpeed *= 4.545;\n\n                                                                        compPitch = (0.93 * (compPitch + (pitchGyroSpeed * (double)(micros() - timer) / 1000000)))+(0.07 * getWiimotePitch()); // Use a complimentary filter to calculate the angle\n                                                                        compRoll = (0.93 * (compRoll + (rollGyroSpeed * (double)(micros() - timer) / 1000000)))+(0.07 * getWiimoteRoll());\n\n                                                                        gyroYaw += (yawGyroSpeed * ((double)(micros() - timer) / 1000000));\n                                                                        gyroRoll += (rollGyroSpeed * ((double)(micros() - timer) / 1000000));\n                                                                        gyroPitch += (pitchGyroSpeed * ((double)(micros() - timer) / 1000000));\n                                                                        timer = micros();\n                                                                        /*\n                                                                        // Uncomment these lines to tune the gyro scale variabels\n                                                                        Notify(PSTR(\"\\r\\ngyroYaw: \"), 0x80);\n                                                                        Notify(gyroYaw, 0x80);\n                                                                        Notify(PSTR(\"\\tgyroRoll: \"), 0x80);\n                                                                        Notify(gyroRoll, 0x80);\n                                                                        Notify(PSTR(\"\\tgyroPitch: \"), 0x80);\n                                                                        Notify(gyroPitch, 0x80);\n                                                                         */\n                                                                        /*\n                                                                        Notify(PSTR(\"\\twiimoteRoll: \"), 0x80);\n                                                                        Notify(wiimoteRoll, 0x80);\n                                                                        Notify(PSTR(\"\\twiimotePitch: \"), 0x80);\n                                                                        Notify(wiimotePitch, 0x80);\n                                                                         */\n                                                                } else {\n                                                                        if((micros() - timer) > 1000000) { // Loop for 1 sec before resetting the values\n#ifdef DEBUG_USB_HOST\n                                                                                Notify(PSTR(\"\\r\\nThe gyro values has been reset\"), 0x80);\n#endif\n                                                                                gyroYawZero = (l2capinbuf[15] | ((l2capinbuf[18] & 0xFC) << 6));\n                                                                                gyroRollZero = (l2capinbuf[16] | ((l2capinbuf[19] & 0xFC) << 6));\n                                                                                gyroPitchZero = (l2capinbuf[17] | ((l2capinbuf[20] & 0xFC) << 6));\n\n                                                                                rollGyroScale = 500; // You might need to adjust these\n                                                                                pitchGyroScale = 400;\n                                                                                yawGyroScale = 415;\n\n                                                                                gyroYaw = 0;\n                                                                                gyroRoll = 0;\n                                                                                gyroPitch = 0;\n\n                                                                                motionValuesReset = true;\n                                                                                timer = micros();\n                                                                        }\n                                                                }\n                                                        } else {\n                                                                if(nunchuckConnected) {\n                                                                        hatValues[HatX] = l2capinbuf[15];\n                                                                        hatValues[HatY] = l2capinbuf[16];\n                                                                        accXnunchuck = ((l2capinbuf[17] << 2) | (l2capinbuf[20] & 0x10 >> 3)) - 416;\n                                                                        accYnunchuck = ((l2capinbuf[18] << 2) | (l2capinbuf[20] & 0x20 >> 4)) - 416;\n                                                                        accZnunchuck = (((l2capinbuf[19] & 0xFE) << 2) | (l2capinbuf[20] & 0xC0 >> 5)) - 416;\n                                                                }\n                                                                //else if(classicControllerConnected) { }\n                                                        }\n                                                        if(l2capinbuf[19] & 0x01) {\n                                                                if(!extensionConnected) {\n                                                                        extensionConnected = true;\n                                                                        unknownExtensionConnected = true;\n#ifdef DEBUG_USB_HOST\n                                                                        Notify(PSTR(\"\\r\\nExtension connected to Motion Plus\"), 0x80);\n#endif\n                                                                }\n                                                        } else {\n                                                                if(extensionConnected && !unknownExtensionConnected) {\n                                                                        extensionConnected = false;\n                                                                        unknownExtensionConnected = true;\n#ifdef DEBUG_USB_HOST\n                                                                        Notify(PSTR(\"\\r\\nExtension disconnected from Motion Plus\"), 0x80);\n#endif\n                                                                        nunchuckConnected = false; // There is no extension connected to the Motion Plus if this report is sent\n                                                                }\n                                                        }\n\n                                                } else if(nunchuckConnected) {\n                                                        hatValues[HatX] = l2capinbuf[15];\n                                                        hatValues[HatY] = l2capinbuf[16];\n                                                        accXnunchuck = ((l2capinbuf[17] << 2) | (l2capinbuf[20] & 0x0C >> 2)) - 416;\n                                                        accYnunchuck = ((l2capinbuf[18] << 2) | (l2capinbuf[20] & 0x30 >> 4)) - 416;\n                                                        accZnunchuck = ((l2capinbuf[19] << 2) | (l2capinbuf[20] & 0xC0 >> 6)) - 416;\n                                                } else if(wiiUProControllerConnected) {\n                                                        hatValues[LeftHatX] = (l2capinbuf[15] | l2capinbuf[16] << 8);\n                                                        hatValues[RightHatX] = (l2capinbuf[17] | l2capinbuf[18] << 8);\n                                                        hatValues[LeftHatY] = (l2capinbuf[19] | l2capinbuf[20] << 8);\n                                                        hatValues[RightHatY] = (l2capinbuf[21] | l2capinbuf[22] << 8);\n                                                }\n#endif\n                                                break;\n#ifdef DEBUG_USB_HOST\n                                        default:\n                                                Notify(PSTR(\"\\r\\nUnknown Report type: \"), 0x80);\n                                                D_PrintHex<uint8_t > (l2capinbuf[9], 0x80);\n                                                break;\n#endif\n                                }\n                        }\n                }\n                L2CAP_task();\n        }\n}\n\nvoid WII::L2CAP_task() {\n        switch(l2cap_state) {\n                        /* These states are used if the Wiimote is the host */\n                case L2CAP_CONTROL_SUCCESS:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Control Successfully Configured\"), 0x80);\n#endif\n                                l2cap_state = L2CAP_INTERRUPT_SETUP;\n                        }\n                        break;\n\n                case L2CAP_INTERRUPT_SETUP:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Interrupt Incoming Connection Request\"), 0x80);\n#endif\n                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, PENDING);\n                                delay(1);\n                                pBtd->l2cap_connection_response(hci_handle, identifier, interrupt_dcid, interrupt_scid, SUCCESSFUL);\n                                identifier++;\n                                delay(1);\n                                pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);\n\n                                l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;\n                        }\n                        break;\n\n                        /* These states are used if the Arduino is the host */\n                case L2CAP_CONTROL_CONNECT_REQUEST:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONTROL_CONNECTED)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nSend HID Control Config Request\"), 0x80);\n#endif\n                                identifier++;\n                                pBtd->l2cap_config_request(hci_handle, identifier, control_scid);\n                                l2cap_state = L2CAP_CONTROL_CONFIG_REQUEST;\n                        }\n                        break;\n\n                case L2CAP_CONTROL_CONFIG_REQUEST:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_CONTROL_SUCCESS)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nSend HID Interrupt Connection Request\"), 0x80);\n#endif\n                                identifier++;\n                                pBtd->l2cap_connection_request(hci_handle, identifier, interrupt_dcid, HID_INTR_PSM);\n                                l2cap_state = L2CAP_INTERRUPT_CONNECT_REQUEST;\n                        }\n                        break;\n\n                case L2CAP_INTERRUPT_CONNECT_REQUEST:\n                        if(l2cap_check_flag(L2CAP_FLAG_INTERRUPT_CONNECTED)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nSend HID Interrupt Config Request\"), 0x80);\n#endif\n                                identifier++;\n                                pBtd->l2cap_config_request(hci_handle, identifier, interrupt_scid);\n                                l2cap_state = L2CAP_INTERRUPT_CONFIG_REQUEST;\n                        }\n                        break;\n\n                case L2CAP_INTERRUPT_CONFIG_REQUEST:\n                        if(l2cap_check_flag(L2CAP_FLAG_CONFIG_INTERRUPT_SUCCESS)) { // Now the HID channels is established\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Channels Established\"), 0x80);\n#endif\n                                pBtd->connectToWii = false;\n                                pBtd->pairWithWii = false;\n                                stateCounter = 0;\n                                l2cap_state = WII_CHECK_MOTION_PLUS_STATE;\n                        }\n                        break;\n\n                        /* The next states are in run() */\n\n                case L2CAP_INTERRUPT_DISCONNECT:\n                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_INTERRUPT_RESPONSE) && ((long)(millis() - timer) >= 0L)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nDisconnected Interrupt Channel\"), 0x80);\n#endif\n                                identifier++;\n                                pBtd->l2cap_disconnection_request(hci_handle, identifier, control_scid, control_dcid);\n                                l2cap_state = L2CAP_CONTROL_DISCONNECT;\n                        }\n                        break;\n\n                case L2CAP_CONTROL_DISCONNECT:\n                        if(l2cap_check_flag(L2CAP_FLAG_DISCONNECT_CONTROL_RESPONSE)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nDisconnected Control Channel\"), 0x80);\n#endif\n                                pBtd->hci_disconnect(hci_handle);\n                                hci_handle = -1; // Reset handle\n                                l2cap_event_flag = 0; // Reset flags\n                                l2cap_state = L2CAP_WAIT;\n                        }\n                        break;\n        }\n}\n\nvoid WII::Run() {\n        if(l2cap_state == L2CAP_INTERRUPT_DISCONNECT && ((long)(millis() - timer) >= 0L))\n                L2CAP_task(); // Call the rest of the disconnection routine after we have waited long enough\n\n        switch(l2cap_state) {\n                case L2CAP_WAIT:\n                        if(pBtd->connectToWii && !pBtd->l2capConnectionClaimed && !wiimoteConnected && !activeConnection) {\n                                pBtd->l2capConnectionClaimed = true;\n                                activeConnection = true;\n                                motionPlusInside = pBtd->motionPlusInside;\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nSend HID Control Connection Request\"), 0x80);\n#endif\n                                hci_handle = pBtd->hci_handle; // Store the HCI Handle for the connection\n                                l2cap_event_flag = 0; // Reset flags\n                                identifier = 0;\n                                pBtd->l2cap_connection_request(hci_handle, identifier, control_dcid, HID_CTRL_PSM);\n                                l2cap_state = L2CAP_CONTROL_CONNECT_REQUEST;\n                        } else if(l2cap_check_flag(L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)) {\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nHID Control Incoming Connection Request\"), 0x80);\n#endif\n                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, PENDING);\n                                delay(1);\n                                pBtd->l2cap_connection_response(hci_handle, identifier, control_dcid, control_scid, SUCCESSFUL);\n                                identifier++;\n                                delay(1);\n                                pBtd->l2cap_config_request(hci_handle, identifier, control_scid);\n                                l2cap_state = L2CAP_CONTROL_SUCCESS;\n                        }\n                        break;\n\n                case WII_CHECK_MOTION_PLUS_STATE:\n#ifdef DEBUG_USB_HOST\n                        if(stateCounter == 0) // Only print onnce\n                                Notify(PSTR(\"\\r\\nChecking if a Motion Plus is connected\"), 0x80);\n#endif\n                        stateCounter++;\n                        if(stateCounter % 200 == 0)\n                                checkMotionPresent(); // Check if there is a motion plus connected\n                        if(wii_check_flag(WII_FLAG_MOTION_PLUS_CONNECTED)) {\n                                stateCounter = 0;\n                                l2cap_state = WII_INIT_MOTION_PLUS_STATE;\n                                timer = micros();\n\n                                if(unknownExtensionConnected) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nA extension is also connected\"), 0x80);\n#endif\n                                        activateNunchuck = true; // For we will just set this to true as this the only extension supported so far\n                                }\n\n                        } else if(stateCounter == 601) { // We will try three times to check for the motion plus\n#ifdef DEBUG_USB_HOST\n                                Notify(PSTR(\"\\r\\nNo Motion Plus was detected\"), 0x80);\n#endif\n                                stateCounter = 0;\n                                l2cap_state = WII_CHECK_EXTENSION_STATE;\n                        }\n                        break;\n\n                case WII_CHECK_EXTENSION_STATE: // This is used to check if there is anything plugged in to the extension port\n#ifdef DEBUG_USB_HOST\n                        if(stateCounter == 0) // Only print onnce\n                                Notify(PSTR(\"\\r\\nChecking if there is any extension connected\"), 0x80);\n#endif\n                        stateCounter++; // We use this counter as there has to be a short delay between the commands\n                        if(stateCounter == 1)\n                                statusRequest(); // See if a new device has connected\n                        if(stateCounter == 100) {\n                                if(unknownExtensionConnected) // Check if there is a extension is connected to the port\n                                        initExtension1();\n                                else\n                                        stateCounter = 499;\n                        } else if(stateCounter == 200)\n                                initExtension2();\n                        else if(stateCounter == 300) {\n                                readExtensionType();\n                                unknownExtensionConnected = false;\n                        } else if(stateCounter == 400) {\n                                if(wii_check_flag(WII_FLAG_CALIBRATE_BALANCE_BOARD)) {\n#ifdef DEBUG_USB_HOST\n                                        Notify(PSTR(\"\\r\\nReading Wii Balance Board calibration values\"), 0x80);\n#endif\n                                        readWiiBalanceBoardCalibration();\n                                } else\n                                        stateCounter = 499;\n                        } else if(stateCounter == 500) {\n                                stateCounter = 0;\n                                l2cap_state = TURN_ON_LED;\n                        }\n                        break;\n\n                case WII_INIT_MOTION_PLUS_STATE:\n                        stateCounter++;\n                        if(stateCounter == 1)\n                                initMotionPlus();\n                        else if(stateCounter == 100)\n                                activateMotionPlus();\n                        else if(stateCounter == 200)\n                                readExtensionType(); // Check if it has been activated\n                        else if(stateCounter == 300) {\n                                stateCounter = 0;\n                                unknownExtensionConnected = false; // The motion plus will send a status report when it's activated, we will set this to false so it doesn't reinitialize the Motion Plus\n                                l2cap_state = TURN_ON_LED;\n                        }\n                        break;\n\n                case TURN_ON_LED:\n                        if(wii_check_flag(WII_FLAG_NUNCHUCK_CONNECTED))\n                                nunchuckConnected = true;\n                        wiimoteConnected = true;\n                        onInit();\n                        l2cap_state = L2CAP_DONE;\n                        break;\n\n                case L2CAP_DONE:\n                        if(unknownExtensionConnected) {\n#ifdef DEBUG_USB_HOST\n                                if(stateCounter == 0) // Only print once\n                                        Notify(PSTR(\"\\r\\nChecking extension port\"), 0x80);\n#endif\n                                stateCounter++; // We will use this counter as there has to be a short delay between the commands\n                                if(stateCounter == 50)\n                                        statusRequest();\n                                else if(stateCounter == 100)\n                                        initExtension1();\n                                else if(stateCounter == 150)\n                                        if((extensionConnected && motionPlusConnected) || (unknownExtensionConnected && !motionPlusConnected))\n                                                initExtension2();\n                                        else\n                                                stateCounter = 299; // There is no extension connected\n                                else if(stateCounter == 200)\n                                        readExtensionType();\n                                else if(stateCounter == 250) {\n                                        if(wii_check_flag(WII_FLAG_NUNCHUCK_CONNECTED)) {\n#ifdef DEBUG_USB_HOST\n                                                Notify(PSTR(\"\\r\\nNunchuck was reconnected\"), 0x80);\n#endif\n                                                activateNunchuck = true;\n                                                nunchuckConnected = true;\n                                        }\n                                        if(!motionPlusConnected)\n                                                stateCounter = 449;\n                                } else if(stateCounter == 300) {\n                                        if(motionPlusConnected) {\n#ifdef DEBUG_USB_HOST\n                                                Notify(PSTR(\"\\r\\nReactivating the Motion Plus\"), 0x80);\n#endif\n                                                initMotionPlus();\n                                        } else\n                                                stateCounter = 449;\n                                } else if(stateCounter == 350)\n                                        activateMotionPlus();\n                                else if(stateCounter == 400)\n                                        readExtensionType(); // Check if it has been activated\n                                else if(stateCounter == 450) {\n                                        onInit();\n                                        stateCounter = 0;\n                                        unknownExtensionConnected = false;\n                                }\n                        } else\n                                stateCounter = 0;\n                        break;\n        }\n}\n\n/************************************************************/\n/*                    HID Commands                          */\n/************************************************************/\n\nvoid WII::HID_Command(uint8_t* data, uint8_t nbytes) {\n        if(motionPlusInside)\n                pBtd->L2CAP_Command(hci_handle, data, nbytes, interrupt_scid[0], interrupt_scid[1]); // It's the new Wiimote with the Motion Plus Inside or Wii U Pro controller\n        else\n                pBtd->L2CAP_Command(hci_handle, data, nbytes, control_scid[0], control_scid[1]);\n}\n\nvoid WII::setAllOff() {\n        HIDBuffer[1] = 0x11;\n        HIDBuffer[2] = 0x00;\n        HID_Command(HIDBuffer, 3);\n}\n\nvoid WII::setRumbleOff() {\n        HIDBuffer[1] = 0x11;\n        HIDBuffer[2] &= ~0x01; // Bit 0 control the rumble\n        HID_Command(HIDBuffer, 3);\n}\n\nvoid WII::setRumbleOn() {\n        HIDBuffer[1] = 0x11;\n        HIDBuffer[2] |= 0x01; // Bit 0 control the rumble\n        HID_Command(HIDBuffer, 3);\n}\n\nvoid WII::setRumbleToggle() {\n        HIDBuffer[1] = 0x11;\n        HIDBuffer[2] ^= 0x01; // Bit 0 control the rumble\n        HID_Command(HIDBuffer, 3);\n}\n\nvoid WII::setLedRaw(uint8_t value) {\n        HIDBuffer[1] = 0x11;\n        HIDBuffer[2] = value | (HIDBuffer[2] & 0x01); // Keep the rumble bit\n        HID_Command(HIDBuffer, 3);\n}\n\nvoid WII::setLedOff(LEDEnum a) {\n        HIDBuffer[1] = 0x11;\n        HIDBuffer[2] &= ~(pgm_read_byte(&WII_LEDS[(uint8_t)a]));\n        HID_Command(HIDBuffer, 3);\n}\n\nvoid WII::setLedOn(LEDEnum a) {\n        if(a == OFF)\n                setLedRaw(0);\n        else {\n                HIDBuffer[1] = 0x11;\n                HIDBuffer[2] |= pgm_read_byte(&WII_LEDS[(uint8_t)a]);\n                HID_Command(HIDBuffer, 3);\n        }\n}\n\nvoid WII::setLedToggle(LEDEnum a) {\n        HIDBuffer[1] = 0x11;\n        HIDBuffer[2] ^= pgm_read_byte(&WII_LEDS[(uint8_t)a]);\n        HID_Command(HIDBuffer, 3);\n}\n\nvoid WII::setLedStatus() {\n        HIDBuffer[1] = 0x11;\n        HIDBuffer[2] = (HIDBuffer[2] & 0x01); // Keep the rumble bit\n        if(wiimoteConnected)\n                HIDBuffer[2] |= 0x10; // If it's connected LED1 will light up\n        if(motionPlusConnected)\n                HIDBuffer[2] |= 0x20; // If it's connected LED2 will light up\n        if(nunchuckConnected)\n                HIDBuffer[2] |= 0x40; // If it's connected LED3 will light up\n\n        HID_Command(HIDBuffer, 3);\n}\n\nuint8_t WII::getBatteryLevel() {\n        checkBatteryLevel = true; // This is needed so the library knows that the status response is a response to this function\n        statusRequest(); // This will update the battery level\n        return batteryLevel;\n};\n\nvoid WII::setReportMode(bool continuous, uint8_t mode) {\n        uint8_t cmd_buf[4];\n        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)\n        cmd_buf[1] = 0x12;\n        if(continuous)\n                cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Keep the rumble bit\n        else\n                cmd_buf[2] = 0x00 | (HIDBuffer[2] & 0x01); // Keep the rumble bit\n        cmd_buf[3] = mode;\n        HID_Command(cmd_buf, 4);\n}\n\nvoid WII::statusRequest() {\n        uint8_t cmd_buf[3];\n        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)\n        cmd_buf[1] = 0x15;\n        cmd_buf[2] = (HIDBuffer[2] & 0x01); // Keep the rumble bit\n        HID_Command(cmd_buf, 3);\n}\n\n/************************************************************/\n/*                    Memmory Commands                      */\n/************************************************************/\n\nvoid WII::writeData(uint32_t offset, uint8_t size, uint8_t* data) {\n        uint8_t cmd_buf[23];\n        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)\n        cmd_buf[1] = 0x16; // Write data\n        cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Write to memory, clear bit 2 to write to EEPROM\n        cmd_buf[3] = (uint8_t)((offset & 0xFF0000) >> 16);\n        cmd_buf[4] = (uint8_t)((offset & 0xFF00) >> 8);\n        cmd_buf[5] = (uint8_t)(offset & 0xFF);\n        cmd_buf[6] = size;\n        uint8_t i = 0;\n        for(; i < size; i++)\n                cmd_buf[7 + i] = data[i];\n        for(; i < 16; i++) // Set the rest to zero\n                cmd_buf[7 + i] = 0x00;\n        HID_Command(cmd_buf, 23);\n}\n\nvoid WII::initExtension1() {\n        uint8_t buf[1];\n        buf[0] = 0x55;\n        writeData(0xA400F0, 1, buf);\n}\n\nvoid WII::initExtension2() {\n        uint8_t buf[1];\n        buf[0] = 0x00;\n        writeData(0xA400FB, 1, buf);\n}\n\nvoid WII::initMotionPlus() {\n        uint8_t buf[1];\n        buf[0] = 0x55;\n        writeData(0xA600F0, 1, buf);\n}\n\nvoid WII::activateMotionPlus() {\n        uint8_t buf[1];\n        if(pBtd->wiiUProController) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nActivating Wii U Pro Controller\"), 0x80);\n#endif\n                buf[0] = 0x00; // It seems like you can send anything but 0x04, 0x05, and 0x07\n        } else if(activateNunchuck) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nActivating Motion Plus in pass-through mode\"), 0x80);\n#endif\n                buf[0] = 0x05; // Activate nunchuck pass-through mode\n        }//else if(classicControllerConnected && extensionConnected)\n                //buf[0] = 0x07;\n        else {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nActivating Motion Plus in normal mode\"), 0x80);\n#endif\n                buf[0] = 0x04; // Don't use any extension\n        }\n        writeData(0xA600FE, 1, buf);\n}\n\nvoid WII::readData(uint32_t offset, uint16_t size, bool EEPROM) {\n        uint8_t cmd_buf[8];\n        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)\n        cmd_buf[1] = 0x17; // Read data\n        if(EEPROM)\n                cmd_buf[2] = 0x00 | (HIDBuffer[2] & 0x01); // Read from EEPROM\n        else\n                cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Read from memory\n        cmd_buf[3] = (uint8_t)((offset & 0xFF0000) >> 16);\n        cmd_buf[4] = (uint8_t)((offset & 0xFF00) >> 8);\n        cmd_buf[5] = (uint8_t)(offset & 0xFF);\n        cmd_buf[6] = (uint8_t)((size & 0xFF00) >> 8);\n        cmd_buf[7] = (uint8_t)(size & 0xFF);\n\n        HID_Command(cmd_buf, 8);\n}\n\nvoid WII::readExtensionType() {\n        readData(0xA400FA, 6, false);\n}\n\nvoid WII::readCalData() {\n        readData(0x0016, 8, true);\n}\n\nvoid WII::checkMotionPresent() {\n        readData(0xA600FA, 6, false);\n}\n\nvoid WII::readWiiBalanceBoardCalibration() {\n        readData(0xA40024, 24, false);\n}\n\n/************************************************************/\n/*                    WII Commands                          */\n/************************************************************/\n\nbool WII::getButtonPress(ButtonEnum b) { // Return true when a button is pressed\n        if(wiiUProControllerConnected)\n                return (ButtonState & pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[(uint8_t)b]));\n        else\n                return (ButtonState & pgm_read_dword(&WII_BUTTONS[(uint8_t)b]));\n}\n\nbool WII::getButtonClick(ButtonEnum b) { // Only return true when a button is clicked\n        uint32_t button;\n        if(wiiUProControllerConnected)\n                button = pgm_read_dword(&WII_PROCONTROLLER_BUTTONS[(uint8_t)b]);\n        else\n                button = pgm_read_dword(&WII_BUTTONS[(uint8_t)b]);\n        bool click = (ButtonClickState & button);\n        ButtonClickState &= ~button; // clear \"click\" event\n        return click;\n}\n\nuint8_t WII::getAnalogHat(HatEnum a) {\n        if(!nunchuckConnected)\n                return 127; // Return center position\n        else {\n                uint8_t output = hatValues[(uint8_t)a];\n                if(output == 0xFF || output == 0x00) // The joystick will only read 255 or 0 when the cable is unplugged or initializing, so we will just return the center position\n                        return 127;\n                else\n                        return output;\n        }\n}\n\nuint16_t WII::getAnalogHat(AnalogHatEnum a) {\n        if(!wiiUProControllerConnected)\n                return 2000;\n        else {\n                uint16_t output = hatValues[(uint8_t)a];\n                if(output == 0x00) // The joystick will only read 0 when it is first initializing, so we will just return the center position\n                        return 2000;\n                else\n                        return output;\n        }\n}\n\nvoid WII::onInit() {\n        if(pFuncOnInit)\n                pFuncOnInit(); // Call the user function\n        else\n                setLedStatus();\n}\n\n/************************************************************/\n/*                 Wii Balance Board Commands               */\n/************************************************************/\n\nfloat WII::getWeight(BalanceBoardEnum pos) {\n        // Use interpolating between two points - based on: https://github.com/skorokithakis/gr8w8upd8m8/blob/master/gr8w8upd8m8.py\n        // wiiBalanceBoardCal[pos][0] is calibration values for 0 kg\n        // wiiBalanceBoardCal[pos][1] is calibration values for 17 kg\n        // wiiBalanceBoardCal[pos][2] is calibration values for 34 kg\n        if(wiiBalanceBoardRaw[pos] < wiiBalanceBoardCal[0][pos])\n            return 0.0f; // Below 0 kg\n        else if(wiiBalanceBoardRaw[pos] < wiiBalanceBoardCal[1][pos]) // Between 0 and 17 kg\n            return 17.0f * (float)(wiiBalanceBoardRaw[pos] - wiiBalanceBoardCal[0][pos]) / (float)(wiiBalanceBoardCal[1][pos] - wiiBalanceBoardCal[0][pos]);\n        else // More than 17 kg\n            return 17.0f + 17.0f * (float)(wiiBalanceBoardRaw[pos] - wiiBalanceBoardCal[1][pos]) / (float)(wiiBalanceBoardCal[2][pos] - wiiBalanceBoardCal[1][pos]);\n};\n\nfloat WII::getTotalWeight() {\n        return getWeight(TopRight) + getWeight(BotRight) + getWeight(TopLeft) + getWeight(BotLeft);\n};\n\n/************************************************************/\n/*       The following functions are for the IR camera      */\n/************************************************************/\n\n#ifdef WIICAMERA\n\nvoid WII::IRinitialize() { // Turns on and initialises the IR camera\n\n        enableIRCamera1();\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nEnable IR Camera1 Complete\"), 0x80);\n#endif\n        delay(80);\n\n        enableIRCamera2();\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nEnable IR Camera2 Complete\"), 0x80);\n#endif\n        delay(80);\n\n        write0x08Value();\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nWrote hex number 0x08\"), 0x80);\n#endif\n        delay(80);\n\n        writeSensitivityBlock1();\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nWrote Sensitivity Block 1\"), 0x80);\n#endif\n        delay(80);\n\n        writeSensitivityBlock2();\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nWrote Sensitivity Block 2\"), 0x80);\n#endif\n        delay(80);\n\n        uint8_t mode_num = 0x03;\n        setWiiModeNumber(mode_num); // Change input for whatever mode you want i.e. 0x01, 0x03, or 0x05\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nSet Wii Mode Number To 0x\"), 0x80);\n        D_PrintHex<uint8_t > (mode_num, 0x80);\n#endif\n        delay(80);\n\n        write0x08Value();\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nWrote Hex Number 0x08\"), 0x80);\n#endif\n        delay(80);\n\n        setReportMode(false, 0x33);\n        //setReportMode(false, 0x3f); // For full reporting mode, doesn't work yet\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nSet Report Mode to 0x33\"), 0x80);\n#endif\n        delay(80);\n\n        statusRequest(); // Used to update wiiState - call isIRCameraEnabled() afterwards to check if it actually worked\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nIR Initialized\"), 0x80);\n#endif\n}\n\nvoid WII::enableIRCamera1() {\n        uint8_t cmd_buf[3];\n        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)\n        cmd_buf[1] = 0x13; // Output report 13\n        cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Keep the rumble bit and sets bit 2\n        HID_Command(cmd_buf, 3);\n}\n\nvoid WII::enableIRCamera2() {\n        uint8_t cmd_buf[3];\n        cmd_buf[0] = 0xA2; // HID BT DATA_request (0xA0) | Report Type (Output 0x02)\n        cmd_buf[1] = 0x1A; // Output report 1A\n        cmd_buf[2] = 0x04 | (HIDBuffer[2] & 0x01); // Keep the rumble bit and sets bit 2\n        HID_Command(cmd_buf, 3);\n}\n\nvoid WII::writeSensitivityBlock1() {\n        uint8_t buf[9];\n        buf[0] = 0x00;\n        buf[1] = 0x00;\n        buf[2] = 0x00;\n        buf[3] = 0x00;\n        buf[4] = 0x00;\n        buf[5] = 0x00;\n        buf[6] = 0x90;\n        buf[7] = 0x00;\n        buf[8] = 0x41;\n\n        writeData(0xB00000, 9, buf);\n}\n\nvoid WII::writeSensitivityBlock2() {\n        uint8_t buf[2];\n        buf[0] = 0x40;\n        buf[1] = 0x00;\n\n        writeData(0xB0001A, 2, buf);\n}\n\nvoid WII::write0x08Value() {\n        uint8_t cmd = 0x08;\n        writeData(0xb00030, 1, &cmd);\n}\n\nvoid WII::setWiiModeNumber(uint8_t mode_number) { // mode_number in hex i.e. 0x03 for extended mode\n        writeData(0xb00033, 1, &mode_number);\n}\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/Wii.h",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n\n IR camera support added by Allan Glover (adglover9.81@gmail.com) and Kristian Lauszus\n */\n\n#ifndef _wii_h_\n#define _wii_h_\n\n#include \"BTD.h\"\n#include \"controllerEnums.h\"\n\n/* Wii event flags */\n#define WII_FLAG_MOTION_PLUS_CONNECTED          (1 << 0)\n#define WII_FLAG_NUNCHUCK_CONNECTED             (1 << 1)\n#define WII_FLAG_CALIBRATE_BALANCE_BOARD        (1 << 2)\n\n#define wii_check_flag(flag)  (wii_event_flag & (flag))\n#define wii_set_flag(flag)  (wii_event_flag |= (flag))\n#define wii_clear_flag(flag)  (wii_event_flag &= ~(flag))\n\n/** Enum used to read the joystick on the Nunchuck. */\nenum HatEnum {\n        /** Read the x-axis on the Nunchuck joystick. */\n        HatX = 0,\n        /** Read the y-axis on the Nunchuck joystick. */\n        HatY = 1,\n};\n\n/** Enum used to read the weight on Wii Balance Board. */\nenum BalanceBoardEnum {\n        TopRight = 0,\n        BotRight = 1,\n        TopLeft = 2,\n        BotLeft = 3,\n};\n\n/**\n * This BluetoothService class implements support for the Wiimote including the Nunchuck and Motion Plus extension.\n *\n * It also support the Wii U Pro Controller.\n */\nclass WII : public BluetoothService {\npublic:\n        /**\n         * Constructor for the WII class.\n         * @param  p   Pointer to BTD class instance.\n         * @param  pair   Set this to true in order to pair with the Wiimote. If the argument is omitted then it won't pair with it.\n         * One can use ::PAIR to set it to true.\n         */\n        WII(BTD *p, bool pair = false);\n\n        /** @name BluetoothService implementation */\n        /** Used this to disconnect any of the controllers. */\n        void disconnect();\n        /**@}*/\n\n        /** @name Wii Controller functions */\n        /**\n         * getButtonPress(Button b) will return true as long as the button is held down.\n         *\n         * While getButtonClick(Button b) will only return it once.\n         *\n         * So you instance if you need to increase a variable once you would use getButtonClick(Button b),\n         * but if you need to drive a robot forward you would use getButtonPress(Button b).\n         * @param  b          ::ButtonEnum to read.\n         * @return            getButtonPress(ButtonEnum b) will return a true as long as a button is held down, while getButtonClick(ButtonEnum b) will return true once for each button press.\n         */\n        bool getButtonPress(ButtonEnum b);\n        bool getButtonClick(ButtonEnum b);\n        /**@}*/\n\n        /** @name Wii Controller functions */\n\n        /** Call this to start the paring sequence with a controller */\n        void pair(void) {\n                if(pBtd)\n                        pBtd->pairWithWiimote();\n        };\n        /**\n         * Used to read the joystick of the Nunchuck.\n         * @param  a Either ::HatX or ::HatY.\n         * @return   Return the analog value in the range from approximately 25-230.\n         */\n        uint8_t getAnalogHat(HatEnum a);\n        /**\n         * Used to read the joystick of the Wii U Pro Controller.\n         * @param  a Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY.\n         * @return   Return the analog value in the range from approximately 800-3200.\n         */\n        uint16_t getAnalogHat(AnalogHatEnum a);\n\n        /**\n         * Pitch calculated from the Wiimote. A complimentary filter is used if the Motion Plus is connected.\n         * @return Pitch in the range from 0-360.\n         */\n        double getPitch() {\n                if(motionPlusConnected)\n                        return compPitch;\n                return getWiimotePitch();\n        };\n\n        /**\n         * Roll calculated from the Wiimote. A complimentary filter is used if the Motion Plus is connected.\n         * @return Roll in the range from 0-360.\n         */\n        double getRoll() {\n                if(motionPlusConnected)\n                        return compRoll;\n                return getWiimoteRoll();\n        };\n\n        /**\n         * This is the yaw calculated by the gyro.\n         *\n         * <B>NOTE:</B> This angle will drift a lot and is only available if the Motion Plus extension is connected.\n         * @return The angle calculated using the gyro.\n         */\n        double getYaw() {\n                return gyroYaw;\n        };\n\n        /** Used to set all LEDs and rumble off. */\n        void setAllOff();\n        /** Turn off rumble. */\n        void setRumbleOff();\n        /** Turn on rumble. */\n        void setRumbleOn();\n        /** Toggle rumble. */\n        void setRumbleToggle();\n\n        /**\n         * Set LED value without using the ::LEDEnum.\n         * @param value See: ::LEDEnum.\n         */\n        void setLedRaw(uint8_t value);\n\n        /** Turn all LEDs off. */\n        void setLedOff() {\n                setLedRaw(0);\n        };\n        /**\n         * Turn the specific ::LEDEnum off.\n         * @param a The ::LEDEnum to turn off.\n         */\n        void setLedOff(LEDEnum a);\n        /**\n         * Turn the specific ::LEDEnum on.\n         * @param a The ::LEDEnum to turn on.\n         */\n        void setLedOn(LEDEnum a);\n        /**\n         * Toggle the specific ::LEDEnum.\n         * @param a The ::LEDEnum to toggle.\n         */\n        void setLedToggle(LEDEnum a);\n        /**\n         * This will set the LEDs, so the user can see which connections are active.\n         *\n         * The first ::LEDEnum indicate that the Wiimote is connected,\n         * the second ::LEDEnum indicate indicate that a Motion Plus is also connected\n         * the third ::LEDEnum will indicate that a Nunchuck controller is also connected.\n         */\n        void setLedStatus();\n\n        /**\n         * Return the battery level of the Wiimote.\n         * @return The battery level in the range 0-255.\n         */\n        uint8_t getBatteryLevel();\n\n        /**\n         * Return the Wiimote state.\n         * @return See: http://wiibrew.org/wiki/Wiimote#0x20:_Status.\n         */\n        uint8_t getWiiState() {\n                return wiiState;\n        };\n        /**@}*/\n\n        /**@{*/\n        /** Variable used to indicate if a Wiimote is connected. */\n        bool wiimoteConnected;\n        /** Variable used to indicate if a Nunchuck controller is connected. */\n        bool nunchuckConnected;\n        /** Variable used to indicate if a Nunchuck controller is connected. */\n        bool motionPlusConnected;\n        /** Variable used to indicate if a Wii U Pro controller is connected. */\n        bool wiiUProControllerConnected;\n        /** Variable used to indicate if a Wii Balance Board is connected. */\n        bool wiiBalanceBoardConnected;\n        /**@}*/\n\n        /* IMU Data, might be usefull if you need to do something more advanced than just calculating the angle */\n\n        /**@{*/\n\n        /** Pitch and roll calculated from the accelerometer inside the Wiimote. */\n        double getWiimotePitch() {\n                return (atan2(accYwiimote, accZwiimote) + PI) * RAD_TO_DEG;\n        };\n\n        double getWiimoteRoll() {\n                return (atan2(accXwiimote, accZwiimote) + PI) * RAD_TO_DEG;\n        };\n        /**@}*/\n\n        /**@{*/\n\n        /** Pitch and roll calculated from the accelerometer inside the Nunchuck. */\n        double getNunchuckPitch() {\n                return (atan2(accYnunchuck, accZnunchuck) + PI) * RAD_TO_DEG;\n        };\n\n        double getNunchuckRoll() {\n                return (atan2(accXnunchuck, accZnunchuck) + PI) * RAD_TO_DEG;\n        };\n        /**@}*/\n\n        /**@{*/\n        /** Accelerometer values used to calculate pitch and roll. */\n        int16_t accXwiimote, accYwiimote, accZwiimote;\n        int16_t accXnunchuck, accYnunchuck, accZnunchuck;\n        /**@}*/\n\n        /* Variables for the gyro inside the Motion Plus */\n        /** This is the pitch calculated by the gyro - use this to tune WII#pitchGyroScale. */\n        double gyroPitch;\n        /** This is the roll calculated by the gyro - use this to tune WII#rollGyroScale. */\n        double gyroRoll;\n        /** This is the yaw calculated by the gyro - use this to tune WII#yawGyroScale. */\n        double gyroYaw;\n\n        /**@{*/\n        /** The speed in deg/s from the gyro. */\n        double pitchGyroSpeed;\n        double rollGyroSpeed;\n        double yawGyroSpeed;\n        /**@}*/\n\n        /**@{*/\n        /** You might need to fine-tune these values. */\n        uint16_t pitchGyroScale;\n        uint16_t rollGyroScale;\n        uint16_t yawGyroScale;\n        /**@}*/\n\n        /**@{*/\n        /** Raw value read directly from the Motion Plus. */\n        int16_t gyroYawRaw;\n        int16_t gyroRollRaw;\n        int16_t gyroPitchRaw;\n        /**@}*/\n\n        /**@{*/\n        /** These values are set when the controller is first initialized. */\n        int16_t gyroYawZero;\n        int16_t gyroRollZero;\n        int16_t gyroPitchZero;\n        /**@}*/\n\n        /** @name Wii Balance Board functions */\n\n        /**\n         * Used to get the weight at the specific position on the Wii Balance Board.\n         * @param ::BalanceBoardEnum to read from.\n         * @return Returns the weight in kg.\n         */\n        float getWeight(BalanceBoardEnum pos);\n\n        /**\n         * Used to get total weight on the Wii Balance Board.\n         * @returnReturns the weight in kg.\n         */\n        float getTotalWeight();\n\n        /**\n         * Used to get the raw reading at the specific position on the Wii Balance Board.\n         * @param ::BalanceBoardEnum to read from.\n         * @return Returns the raw reading.\n         */\n        uint16_t getWeightRaw(BalanceBoardEnum pos) {\n                return wiiBalanceBoardRaw[pos];\n        };\n        /**@}*/\n\n#ifdef WIICAMERA\n        /** @name Wiimote IR camera functions\n         * You will have to set ::ENABLE_WII_IR_CAMERA in settings.h to 1 in order use the IR camera.\n         */\n        /** Initialises the camera as per the steps from: http://wiibrew.org/wiki/Wiimote#IR_Camera */\n        void IRinitialize();\n\n        /**\n         * IR object 1 x-position read from the Wii IR camera.\n         * @return The x-position of the object in the range 0-1023.\n         */\n        uint16_t getIRx1() {\n                return IR_object_x1;\n        };\n\n        /**\n         * IR object 1 y-position read from the Wii IR camera.\n         * @return The y-position of the object in the range 0-767.\n         */\n        uint16_t getIRy1() {\n                return IR_object_y1;\n        };\n\n        /**\n         * IR object 1 size read from the Wii IR camera.\n         * @return The size of the object in the range 0-15.\n         */\n        uint8_t getIRs1() {\n                return IR_object_s1;\n        };\n\n        /**\n         * IR object 2 x-position read from the Wii IR camera.\n         * @return The x-position of the object in the range 0-1023.\n         */\n        uint16_t getIRx2() {\n                return IR_object_x2;\n        };\n\n        /**\n         * IR object 2 y-position read from the Wii IR camera.\n         * @return The y-position of the object in the range 0-767.\n         */\n        uint16_t getIRy2() {\n                return IR_object_y2;\n        };\n\n        /**\n         * IR object 2 size read from the Wii IR camera.\n         * @return The size of the object in the range 0-15.\n         */\n        uint8_t getIRs2() {\n                return IR_object_s2;\n        };\n\n        /**\n         * IR object 3 x-position read from the Wii IR camera.\n         * @return The x-position of the object in the range 0-1023.\n         */\n        uint16_t getIRx3() {\n                return IR_object_x3;\n        };\n\n        /**\n         * IR object 3 y-position read from the Wii IR camera.\n         * @return The y-position of the object in the range 0-767.\n         */\n        uint16_t getIRy3() {\n                return IR_object_y3;\n        };\n\n        /**\n         * IR object 3 size read from the Wii IR camera.\n         * @return The size of the object in the range 0-15.\n         */\n        uint8_t getIRs3() {\n                return IR_object_s3;\n        };\n\n        /**\n         * IR object 4 x-position read from the Wii IR camera.\n         * @return The x-position of the object in the range 0-1023.\n         */\n        uint16_t getIRx4() {\n                return IR_object_x4;\n        };\n\n        /**\n         * IR object 4 y-position read from the Wii IR camera.\n         * @return The y-position of the object in the range 0-767.\n         */\n        uint16_t getIRy4() {\n                return IR_object_y4;\n        };\n\n        /**\n         * IR object 4 size read from the Wii IR camera.\n         * @return The size of the object in the range 0-15.\n         */\n        uint8_t getIRs4() {\n                return IR_object_s4;\n        };\n\n        /**\n         * Use this to check if the camera is enabled or not.\n         * If not call WII#IRinitialize to initialize the IR camera.\n         * @return     True if it's enabled, false if not.\n         */\n        bool isIRCameraEnabled() {\n                return (wiiState & 0x08);\n        };\n        /**@}*/\n#endif\n\nprotected:\n        /** @name BluetoothService implementation */\n        /**\n         * Used to pass acldata to the services.\n         * @param ACLData Incoming acldata.\n         */\n        void ACLData(uint8_t* ACLData);\n        /** Used to run part of the state machine. */\n        void Run();\n        /** Use this to reset the service. */\n        void Reset();\n        /**\n         * Called when the controller is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         */\n        void onInit();\n        /**@}*/\n\nprivate:\n\n        void L2CAP_task(); // L2CAP state machine\n\n        /* Variables filled from HCI event management */\n        bool activeConnection; // Used to indicate if it's already has established a connection\n\n        /* Variables used by high level L2CAP task */\n        uint8_t l2cap_state;\n        uint8_t wii_event_flag; // Used for Wii flags\n\n        uint32_t ButtonState;\n        uint32_t OldButtonState;\n        uint32_t ButtonClickState;\n        uint16_t hatValues[4];\n\n        uint8_t HIDBuffer[3]; // Used to store HID commands\n\n        uint16_t stateCounter;\n        bool unknownExtensionConnected;\n        bool extensionConnected;\n        bool checkBatteryLevel; // Set to true when getBatteryLevel() is called otherwise if should be false\n        bool motionPlusInside; // True if it's a new Wiimote with the Motion Plus extension build into it\n\n        /* L2CAP Channels */\n        uint8_t control_scid[2]; // L2CAP source CID for HID_Control\n        uint8_t control_dcid[2]; // 0x0060\n        uint8_t interrupt_scid[2]; // L2CAP source CID for HID_Interrupt\n        uint8_t interrupt_dcid[2]; // 0x0061\n\n        /* HID Commands */\n        void HID_Command(uint8_t* data, uint8_t nbytes);\n        void setReportMode(bool continuous, uint8_t mode);\n\n        void writeData(uint32_t offset, uint8_t size, uint8_t* data);\n        void initExtension1();\n        void initExtension2();\n\n        void statusRequest(); // Used to update the Wiimote state and battery level\n\n        void readData(uint32_t offset, uint16_t size, bool EEPROM);\n        void readExtensionType();\n        void readCalData();\n        void readWiiBalanceBoardCalibration(); // Used by the library to read the Wii Balance Board calibration values\n\n        void checkMotionPresent(); // Used to see if a Motion Plus is connected to the Wiimote\n        void initMotionPlus();\n        void activateMotionPlus();\n\n        uint16_t wiiBalanceBoardRaw[4]; // Wii Balance Board raw values\n        uint16_t wiiBalanceBoardCal[3][4]; // Wii Balance Board calibration values\n\n        double compPitch; // Fusioned angle using a complimentary filter if the Motion Plus is connected\n        double compRoll; // Fusioned angle using a complimentary filter if the Motion Plus is connected\n\n        bool activateNunchuck;\n        bool motionValuesReset; // This bool is true when the gyro values has been reset\n        uint32_t timer;\n\n        uint8_t wiiState; // Stores the value in l2capinbuf[12] - (0x01: Battery is nearly empty), (0x02:  An Extension Controller is connected), (0x04: Speaker enabled), (0x08: IR enabled), (0x10: LED1, 0x20: LED2, 0x40: LED3, 0x80: LED4)\n        uint8_t batteryLevel;\n\n#ifdef WIICAMERA\n        /* Private function and variables for the readings from the IR Camera */\n        void enableIRCamera1(); // Sets bit 2 of output report 13\n        void enableIRCamera2(); // Sets bit 2 of output report 1A\n        void writeSensitivityBlock1();\n        void writeSensitivityBlock2();\n        void write0x08Value();\n        void setWiiModeNumber(uint8_t mode_number);\n\n        uint16_t IR_object_x1; // IR x position 10 bits\n        uint16_t IR_object_y1; // IR y position 10 bits\n        uint8_t IR_object_s1; // IR size value\n        uint16_t IR_object_x2;\n        uint16_t IR_object_y2;\n        uint8_t IR_object_s2;\n        uint16_t IR_object_x3; // IR x position 10 bits\n        uint16_t IR_object_y3; // IR y position 10 bits\n        uint8_t IR_object_s3; // IR size value\n        uint16_t IR_object_x4;\n        uint16_t IR_object_y4;\n        uint8_t IR_object_s4;\n#endif\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/WiiCameraReadme.md",
    "content": "Please see <http://wiibrew.org/wiki/Wiimote#IR_Camera> for the complete capabilities of the Wii camera. The IR camera code was written based on the above website and with support from Kristian Lauszus.\n\nThis library is large, if you run into memory problems when uploading to the Arduino, disable serial debugging.\n\nTo enable the IR camera code, simply set ```ENABLE_WII_IR_CAMERA``` to 1 in [settings.h](settings.h).\n\nThis library implements the following settings:\n\n* Report sensitivity mode: 00 00 00 00 00 00 90 00 41\t 40 00\t Suggested by inio (high sensitivity)\n* Data Format: Extended mode (0x03).  Full mode is not working yet. The output reports 0x3e and 0x3f need tampering with\n\t* In this mode the camera outputs x and y coordinates and a size dimension for the 4 brightest points.\n\nAgain, read through <http://wiibrew.org/wiki/Wiimote#IR_Camera> to get an understanding of the camera and its settings.\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/XBOXOLD.cpp",
    "content": "/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#include \"XBOXOLD.h\"\n// To enable serial debugging see \"settings.h\"\n//#define EXTRADEBUG // Uncomment to get even more debugging data\n//#define PRINTREPORT // Uncomment to print the report send by the Xbox controller\n\n/** Buttons on the controllers */\nconst uint8_t XBOXOLD_BUTTONS[] PROGMEM = {\n        0x01, // UP\n        0x08, // RIGHT\n        0x02, // DOWN\n        0x04, // LEFT\n\n        0x20, // BACK\n        0x10, // START\n        0x40, // L3\n        0x80, // R3\n\n        // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons\n        4, // BLACK\n        5, // WHTIE\n        6, // L1\n        7, // R1\n\n        1, // B\n        0, // A\n        2, // X\n        3, // Y\n};\n\nXBOXOLD::XBOXOLD(USB *p) :\npUsb(p), // pointer to USB class instance - mandatory\nbAddress(0), // device address - mandatory\nbPollEnable(false) { // don't start polling before dongle is connected\n        for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;\n        }\n\n        if(pUsb) // register in USB subsystem\n                pUsb->RegisterDeviceClass(this); //set devConfig[] entry\n}\n\nuint8_t XBOXOLD::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        uint16_t PID;\n        uint16_t VID;\n\n        // get memory address of USB device address pool\n        AddressPool &addrPool = pUsb->GetAddressPool();\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nXBOXUSB Init\"), 0x80);\n#endif\n        // check if address has already been assigned to an instance\n        if(bAddress) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress in use\"), 0x80);\n#endif\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n        }\n\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress not found\"), 0x80);\n#endif\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        if(!p->epinfo) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nepinfo is null\"), 0x80);\n#endif\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        VID = udd->idVendor;\n        PID = udd->idProduct;\n\n        if((VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) || (PID != XBOX_OLD_PID1 && PID != XBOX_OLD_PID2 && PID != XBOX_OLD_PID3 && PID != XBOX_OLD_PID4)) // Check if VID and PID match\n                goto FailUnknownDevice;\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nsetAddr: \"), 0x80);\n                D_PrintHex<uint8_t > (rcode, 0x80);\n#endif\n                return rcode;\n        }\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nAddr: \"), 0x80);\n        D_PrintHex<uint8_t > (bAddress, 0x80);\n#endif\n        //delay(300); // Spec says you should wait at least 200ms\n\n        p->lowspeed = false;\n\n        //get pointer to assigned address record\n        p = addrPool.GetUsbDevicePtr(bAddress);\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        // Assign epInfo to epinfo pointer - only EP0 is known\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        /* The application will work in reduced host mode, so we can save program and data\n           memory space. After verifying the VID we will use known values for the\n           configuration values for device, interface, endpoints and HID for the XBOX controllers */\n\n        /* Initialize data structures for endpoints of device */\n        epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX report endpoint\n        epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;\n        epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX output endpoint\n        epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;\n\n        rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        delay(200); // Give time for address change\n\n        rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);\n        if(rcode)\n                goto FailSetConfDescr;\n\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nXbox Controller Connected\\r\\n\"), 0x80);\n#endif\n        if(pFuncOnInit)\n                pFuncOnInit(); // Call the user function\n        XboxConnected = true;\n        bPollEnable = true;\n        return 0; // Successful configuration\n\n        /* Diagnostic messages */\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr();\n        goto Fail;\n#endif\n\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n#endif\n        goto Fail;\n\nFailUnknownDevice:\n#ifdef DEBUG_USB_HOST\n        NotifyFailUnknownDevice(VID, PID);\n#endif\n        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\nFail:\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nXbox Init Failed, error code: \"), 0x80);\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n\n/* Performs a cleanup after failed Init() attempt */\nuint8_t XBOXOLD::Release() {\n        XboxConnected = false;\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n        bAddress = 0;\n        bPollEnable = false;\n        return 0;\n}\n\nuint8_t XBOXOLD::Poll() {\n        if(!bPollEnable)\n                return 0;\n        uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;\n        pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1\n        readReport();\n#ifdef PRINTREPORT\n        printReport(BUFFER_SIZE); // Uncomment \"#define PRINTREPORT\" to print the report send by the Xbox controller\n#endif\n        return 0;\n}\n\nvoid XBOXOLD::readReport() {\n        ButtonState = readBuf[2];\n\n        for(uint8_t i = 0; i < sizeof (buttonValues); i++)\n                buttonValues[i] = readBuf[i + 4]; // A, B, X, Y, BLACK, WHITE, L1, and R1\n\n        hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[12] << 8) | readBuf[13]);\n        hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[14] << 8) | readBuf[15]);\n        hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[16] << 8) | readBuf[17]);\n        hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[18] << 8) | readBuf[19]);\n\n        //Notify(PSTR(\"\\r\\nButtonState\"), 0x80);\n        //PrintHex<uint8_t>(ButtonState, 0x80);\n\n        if(ButtonState != OldButtonState || memcmp(buttonValues, oldButtonValues, sizeof (buttonValues)) != 0) {\n                ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable\n                OldButtonState = ButtonState;\n\n                for(uint8_t i = 0; i < sizeof (buttonValues); i++) {\n                        if(oldButtonValues[i] == 0 && buttonValues[i] != 0)\n                                buttonClicked[i] = true; // Update A, B, X, Y, BLACK, WHITE, L1, and R1 click state\n                        oldButtonValues[i] = buttonValues[i];\n                }\n        }\n}\n\nvoid XBOXOLD::printReport(uint16_t length) { //Uncomment \"#define PRINTREPORT\" to print the report send by the Xbox controller\n#ifdef PRINTREPORT\n        if(readBuf == NULL)\n                return;\n        for(uint8_t i = 0; i < length; i++) {\n                D_PrintHex<uint8_t > (readBuf[i], 0x80);\n                Notify(PSTR(\" \"), 0x80);\n        }\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n#endif\n}\n\nuint8_t XBOXOLD::getButtonPress(ButtonEnum b) {\n        uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]);\n        if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons\n                return buttonValues[button]; // Analog buttons\n        return (ButtonState & button); // Digital buttons\n}\n\nbool XBOXOLD::getButtonClick(ButtonEnum b) {\n        uint8_t button = pgm_read_byte(&XBOXOLD_BUTTONS[(uint8_t)b]);\n        if(b == A || b == B || b == X || b == Y || b == BLACK || b == WHITE || b == L1 || b == R1) { // A, B, X, Y, BLACK, WHITE, L1, and R1 are analog buttons\n                if(buttonClicked[button]) {\n                        buttonClicked[button] = false;\n                        return true;\n                }\n                return false;\n        }\n\n        bool click = (ButtonClickState & button);\n        ButtonClickState &= ~button; // clear \"click\" event\n        return click;\n}\n\nint16_t XBOXOLD::getAnalogHat(AnalogHatEnum a) {\n        return hatValue[a];\n}\n\n/* Xbox Controller commands */\nvoid XBOXOLD::XboxCommand(uint8_t* data, uint16_t nbytes) {\n        //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x00), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)\n        pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);\n}\n\nvoid XBOXOLD::setRumbleOn(uint8_t lValue, uint8_t rValue) {\n        uint8_t writeBuf[6];\n\n        writeBuf[0] = 0x00;\n        writeBuf[1] = 0x06;\n        writeBuf[2] = 0x00;\n        writeBuf[3] = rValue; // small weight\n        writeBuf[4] = 0x00;\n        writeBuf[5] = lValue; // big weight\n\n        XboxCommand(writeBuf, 6);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/XBOXOLD.h",
    "content": "/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _xboxold_h_\n#define _xboxold_h_\n\n#include \"Usb.h\"\n#include \"hid.h\"\n#include \"controllerEnums.h\"\n\n/* Data Xbox taken from descriptors */\n#define EP_MAXPKTSIZE       32 // Max size for data via USB\n\n/* Names we give to the 3 Xbox pipes */\n#define XBOX_CONTROL_PIPE    0\n#define XBOX_INPUT_PIPE      1\n#define XBOX_OUTPUT_PIPE     2\n\n// PID and VID of the different devices\n#define XBOX_VID                                0x045E // Microsoft Corporation\n#define MADCATZ_VID                             0x1BAD // For unofficial Mad Catz controllers\n#define JOYTECH_VID                             0x162E // For unofficial Joytech controllers\n\n#define XBOX_OLD_PID1                           0x0202 // Original Microsoft Xbox controller (US)\n#define XBOX_OLD_PID2                           0x0285 // Original Microsoft Xbox controller (Japan)\n#define XBOX_OLD_PID3                           0x0287 // Microsoft Microsoft Xbox Controller S\n#define XBOX_OLD_PID4                           0x0289 // Smaller Microsoft Xbox controller (US)\n\n#define XBOX_MAX_ENDPOINTS   3\n\n/** This class implements support for a the original Xbox controller via USB. */\nclass XBOXOLD : public USBDeviceConfig {\npublic:\n        /**\n         * Constructor for the XBOXOLD class.\n         * @param  pUsb   Pointer to USB class instance.\n         */\n        XBOXOLD(USB *pUsb);\n\n        /** @name USBDeviceConfig implementation */\n        /**\n         * Initialize the Xbox Controller.\n         * @param  parent   Hub number.\n         * @param  port     Port number on the hub.\n         * @param  lowspeed Speed of the device.\n         * @return          0 on success.\n         */\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        /**\n         * Release the USB device.\n         * @return 0 on success.\n         */\n        uint8_t Release();\n        /**\n         * Poll the USB Input endpoins and run the state machines.\n         * @return 0 on success.\n         */\n        uint8_t Poll();\n\n        /**\n         * Get the device address.\n         * @return The device address.\n         */\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        /**\n         * Used to check if the controller has been initialized.\n         * @return True if it's ready.\n         */\n        virtual bool isReady() {\n                return bPollEnable;\n        };\n\n        /**\n         * Used by the USB core to check what this driver support.\n         * @param  vid The device's VID.\n         * @param  pid The device's PID.\n         * @return     Returns true if the device's VID and PID matches this driver.\n         */\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                return ((vid == XBOX_VID || vid == MADCATZ_VID || vid == JOYTECH_VID) && (pid == XBOX_OLD_PID1 || pid == XBOX_OLD_PID2 || pid == XBOX_OLD_PID3 || pid == XBOX_OLD_PID4));\n        };\n        /**@}*/\n\n        /** @name Xbox Controller functions */\n        /**\n         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.\n         *\n         * While getButtonClick(ButtonEnum b) will only return it once.\n         *\n         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),\n         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).\n         * @param  b          ::ButtonEnum to read.\n         * @return            getButtonClick(ButtonEnum b) will return a bool, while getButtonPress(ButtonEnum b) will return a byte if reading ::L2 or ::R2.\n         */\n        uint8_t getButtonPress(ButtonEnum b);\n        bool getButtonClick(ButtonEnum b);\n        /**@}*/\n\n        /** @name Xbox Controller functions */\n        /**\n         * Return the analog value from the joysticks on the controller.\n         * @param  a          Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY.\n         * @return            Returns a signed 16-bit integer.\n         */\n        int16_t getAnalogHat(AnalogHatEnum a);\n\n        /** Turn rumble off the controller. */\n        void setRumbleOff() {\n                setRumbleOn(0, 0);\n        };\n        /**\n         * Turn rumble on.\n         * @param lValue     Left motor (big weight) inside the controller.\n         * @param rValue     Right motor (small weight) inside the controller.\n         */\n        void setRumbleOn(uint8_t lValue, uint8_t rValue);\n\n        /**\n         * Used to call your own function when the controller is successfully initialized.\n         * @param funcOnInit Function to call.\n         */\n        void attachOnInit(void (*funcOnInit)(void)) {\n                pFuncOnInit = funcOnInit;\n        };\n        /**@}*/\n\n        /** True if a Xbox controller is connected. */\n        bool XboxConnected;\n\nprotected:\n        /** Pointer to USB class instance. */\n        USB *pUsb;\n        /** Device address. */\n        uint8_t bAddress;\n        /** Endpoint info structure. */\n        EpInfo epInfo[XBOX_MAX_ENDPOINTS];\n\nprivate:\n        /**\n         * Called when the controller is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         */\n        void (*pFuncOnInit)(void); // Pointer to function called in onInit()\n\n        bool bPollEnable;\n\n        /* Variables to store the digital buttons */\n        uint8_t ButtonState;\n        uint8_t OldButtonState;\n        uint8_t ButtonClickState;\n\n        /* Variables to store the analog buttons */\n        uint8_t buttonValues[8]; // A, B, X, Y, BLACK, WHITE, L1, and R1\n        uint8_t oldButtonValues[8];\n        bool buttonClicked[8];\n\n        int16_t hatValue[4]; // Joystick values\n\n        uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data\n\n        void readReport(); // Read incoming data\n        void printReport(uint16_t length); // Print incoming date\n\n        /* Private commands */\n        void XboxCommand(uint8_t* data, uint16_t nbytes);\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/XBOXONE.cpp",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n   Copyright (C) 2015 guruthree\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n\n guruthree\n Web      :  https://github.com/guruthree/\n */\n\n#include \"XBOXONE.h\"\n// To enable serial debugging see \"settings.h\"\n//#define EXTRADEBUG // Uncomment to get even more debugging data\n//#define PRINTREPORT // Uncomment to print the report send by the Xbox ONE Controller\n\nXBOXONE::XBOXONE(USB *p) :\npUsb(p), // pointer to USB class instance - mandatory\nbAddress(0), // device address - mandatory\nbPollEnable(false) { // don't start polling before dongle is connected\n        for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;\n        }\n\n        if(pUsb) // register in USB subsystem\n                pUsb->RegisterDeviceClass(this); //set devConfig[] entry\n}\n\nuint8_t XBOXONE::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        uint16_t PID;\n        uint16_t VID;\n\n        // get memory address of USB device address pool\n        AddressPool &addrPool = pUsb->GetAddressPool();\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nXBOXONE Init\"), 0x80);\n#endif\n        // check if address has already been assigned to an instance\n        if(bAddress) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress in use\"), 0x80);\n#endif\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n        }\n\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress not found\"), 0x80);\n#endif\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        if(!p->epinfo) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nepinfo is null\"), 0x80);\n#endif\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        VID = udd->idVendor;\n        PID = udd->idProduct;\n\n        if(!VIDPIDOK(VID, PID)) // Check VID\n                goto FailUnknownDevice;\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nsetAddr: \"), 0x80);\n                D_PrintHex<uint8_t > (rcode, 0x80);\n#endif\n                return rcode;\n        }\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nAddr: \"), 0x80);\n        D_PrintHex<uint8_t > (bAddress, 0x80);\n#endif\n        //delay(300); // Spec says you should wait at least 200ms\n\n        p->lowspeed = false;\n\n        //get pointer to assigned address record\n        p = addrPool.GetUsbDevicePtr(bAddress);\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        // Assign epInfo to epinfo pointer - only EP0 is known\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        /* The application will work in reduced host mode, so we can save program and data\n           memory space. After verifying the VID we will use known values for the\n           configuration values for device, interface, endpoints and HID for the XBOXONE Controllers */\n\n        /* Initialize data structures for endpoints of device */\n        epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x01; // XBOX one output endpoint\n        epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;\n        epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX one input endpoint\n        epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;\n        epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;\n\n        rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        delay(200); // Give time for address change\n\n        rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);\n        if(rcode)\n                goto FailSetConfDescr;\n\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nXbox One Controller Connected\\r\\n\"), 0x80);\n#endif\n\n        delay(200); // let things settle\n\n        // initialize the controller for input\n        writeBuf[0] = 0x05;\n        writeBuf[1] = 0x20;\n        rcode = XboxCommand(writeBuf, 2);\n        if (rcode)\n                goto Fail;\n\n        onInit();\n        XboxOneConnected = true;\n        bPollEnable = true;\n        return 0; // Successful configuration\n\n        /* Diagnostic messages */\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr();\n        goto Fail;\n#endif\n\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n#endif\n        goto Fail;\n\nFailUnknownDevice:\n#ifdef DEBUG_USB_HOST\n        NotifyFailUnknownDevice(VID, PID);\n#endif\n        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\nFail:\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nXbox One Init Failed, error code: \"), 0x80);\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n\n/* Performs a cleanup after failed Init() attempt */\nuint8_t XBOXONE::Release() {\n        XboxOneConnected = false;\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n        bAddress = 0;\n        bPollEnable = false;\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nXbox One Controller Disconnected\\r\\n\"), 0x80);\n#endif\n        return 0;\n}\n\nuint8_t XBOXONE::Poll() {\n        if(!bPollEnable)\n                return 0;\n        uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;\n        uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf);\n        if (!rcode) {\n                readReport();\n#ifdef PRINTREPORT\n                printReport(); // Uncomment \"#define PRINTREPORT\" to print the report send by the Xbox ONE Controller\n#endif\n        }\n#ifdef DEBUG_USB_HOST\n                else if (rcode != 0x04) { // not a matter of no update to send\n                Notify(PSTR(\"\\r\\nXbox One Poll Failed, error code: \"), 0x80);\n                NotifyFail(rcode);\n                }\n#endif\n        return rcode;\n}\n\nvoid XBOXONE::readReport() {\n        if(readBuf == NULL)\n                return;\n        if(readBuf[0] == 0x07) {\n                // The XBOX button has a separate message\n                if(readBuf[4] == 1)\n                        ButtonState |= XBOX_BUTTONS[XBOX];\n                else\n                        ButtonState &= ~XBOX_BUTTONS[XBOX];\n        }\n        if(readBuf[0] != 0x20) { // Check if it's the correct report, otherwise return - the controller also sends different status reports\n#ifdef EXTRADEBUG\n                Notify(PSTR(\"\\r\\nXbox Poll: \"), 0x80);\n                D_PrintHex<uint8_t > (readBuf[0], 0x80); // 0x03 is a heart beat report!\n#endif\n                return;\n        }\n\n        uint16_t xbox = ButtonState & XBOX_BUTTONS[XBOX]; // Since the XBOX button is separate, save it and add it back in\n        // xbox button from before, dpad, abxy, start/back, sync, stick click, shoulder buttons\n        ButtonState = xbox | (((uint16_t)readBuf[5] & 0xF) << 8) | (readBuf[4] & 0xF0)  | (((uint16_t)readBuf[4] & 0x0C) << 10) | ((readBuf[4] & 0x01) << 3) | (((uint16_t)readBuf[5] & 0xC0) << 8) | ((readBuf[5] & 0x30) >> 4);\n\n        triggerValue[0] = (uint16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);\n        triggerValue[1] = (uint16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);\n\n        hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);\n        hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);\n        hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);\n        hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);\n\n        //Notify(PSTR(\"\\r\\nButtonState\"), 0x80);\n        //PrintHex<uint16_t>(ButtonState, 0x80);\n\n        if(ButtonState != OldButtonState) {\n                ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable\n                OldButtonState = ButtonState;\n        }\n\n        // Handle click detection for triggers\n        if(triggerValue[0] != 0 && triggerValueOld[0] == 0)\n            L2Clicked = true;\n        triggerValueOld[0] = triggerValue[0];\n        if(triggerValue[1] != 0 && triggerValueOld[1] == 0)\n            R2Clicked = true;\n        triggerValueOld[1] = triggerValue[1];\n}\n\nvoid XBOXONE::printReport() { //Uncomment \"#define PRINTREPORT\" to print the report send by the Xbox ONE Controller\n#ifdef PRINTREPORT\n        if(readBuf == NULL)\n                return;\n        for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {\n                D_PrintHex<uint8_t > (readBuf[i], 0x80);\n                Notify(PSTR(\" \"), 0x80);\n        }\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n#endif\n}\n\nuint16_t XBOXONE::getButtonPress(ButtonEnum b) {\n        if(b == L2) // These are analog buttons\n                return triggerValue[0];\n        else if(b == R2)\n                return triggerValue[1];\n        return (bool)(ButtonState & ((uint16_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b])));\n}\n\nbool XBOXONE::getButtonClick(ButtonEnum b) {\n        if(b == L2) {\n                if(L2Clicked) {\n                        L2Clicked = false;\n                        return true;\n                }\n                return false;\n        } else if(b == R2) {\n                if(R2Clicked) {\n                        R2Clicked = false;\n                        return true;\n                }\n                return false;\n        }\n        uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);\n        bool click = (ButtonClickState & button);\n        ButtonClickState &= ~button; // clear \"click\" event\n        return click;\n}\n\nint16_t XBOXONE::getAnalogHat(AnalogHatEnum a) {\n        return hatValue[a];\n}\n\n/* Xbox Controller commands */\nuint8_t XBOXONE::XboxCommand(uint8_t* data, uint16_t nbytes) {\n        uint8_t rcode = pUsb->outTransfer(bAddress, epInfo[ XBOX_OUTPUT_PIPE ].epAddr, nbytes, data);\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nXboxCommand, Return: \"), 0x80);\n        D_PrintHex<uint8_t > (rcode, 0x80);\n#endif\n        return rcode;\n}\n\nvoid XBOXONE::onInit() {\n        // a short buzz to show the controller is active\n        writeBuf[0] = 0x09;\n        writeBuf[1] = 0x08;\n        writeBuf[2] = 0x00;\n        writeBuf[3] = 0x09;\n        writeBuf[4] = 0x00;\n        writeBuf[5] = 0x0f;\n        writeBuf[6] = 0x04;\n        writeBuf[7] = 0x04;\n        writeBuf[8] = 0x20;\n        writeBuf[9] = 0x20;\n        writeBuf[10] = 0x80;\n        XboxCommand(writeBuf, 11);\n\n        if(pFuncOnInit)\n                pFuncOnInit(); // Call the user function\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/XBOXONE.h",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n   Copyright (C) 2015 guruthree\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n\n guruthree\n Web      :  https://github.com/guruthree/\n */\n\n\n#ifndef _xboxone_h_\n#define _xboxone_h_\n\n#include \"Usb.h\"\n#include \"xboxEnums.h\"\n\n/* Data Xbox ONE taken from descriptors */\n#define EP_MAXPKTSIZE       32 // max size for data via USB\n\n/* Names we give to the 3 XboxONE pipes */\n#define XBOX_CONTROL_PIPE    0\n#define XBOX_OUTPUT_PIPE     1\n#define XBOX_INPUT_PIPE      2\n\n// PID and VID of the different devices\n#define XBOX_VID                                0x045E // Microsoft Corporation\n#define XBOX_ONE_PID                            0x02D1 // Microsoft One Wired controller\n\n#define XBOX_REPORT_BUFFER_SIZE 14 // Size of the input report buffer\n\n#define XBOX_MAX_ENDPOINTS   3\n\n/** This class implements support for a Xbox ONE controller connected via USB. */\nclass XBOXONE : public USBDeviceConfig {\npublic:\n        /**\n         * Constructor for the XBOXONE class.\n         * @param  pUsb   Pointer to USB class instance.\n         */\n        XBOXONE(USB *pUsb);\n\n        /** @name USBDeviceConfig implementation */\n        /**\n         * Initialize the Xbox Controller.\n         * @param  parent   Hub number.\n         * @param  port     Port number on the hub.\n         * @param  lowspeed Speed of the device.\n         * @return          0 on success.\n         */\n        virtual uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        /**\n         * Release the USB device.\n         * @return 0 on success.\n         */\n        virtual uint8_t Release();\n        /**\n         * Poll the USB Input endpoins and run the state machines.\n         * @return 0 on success.\n         */\n        virtual uint8_t Poll();\n\n        /**\n         * Get the device address.\n         * @return The device address.\n         */\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        /**\n         * Used to check if the controller has been initialized.\n         * @return True if it's ready.\n         */\n        virtual bool isReady() {\n                return bPollEnable;\n        };\n\n        /**\n         * Used by the USB core to check what this driver support.\n         * @param  vid The device's VID.\n         * @param  pid The device's PID.\n         * @return     Returns true if the device's VID and PID matches this driver.\n         */\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                return (vid == XBOX_VID && pid == XBOX_ONE_PID);\n        };\n        /**@}*/\n\n        /** @name Xbox Controller functions */\n        /**\n         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.\n         *\n         * While getButtonClick(ButtonEnum b) will only return it once.\n         *\n         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),\n         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).\n         * @param  b          ::ButtonEnum to read.\n         * @return            getButtonClick(ButtonEnum b) will return a bool, while getButtonPress(ButtonEnum b) will return a word if reading ::L2 or ::R2.\n         */\n        uint16_t getButtonPress(ButtonEnum b);\n        bool getButtonClick(ButtonEnum b);\n\n        /**\n         * Return the analog value from the joysticks on the controller.\n         * @param  a          Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY.\n         * @return            Returns a signed 16-bit integer.\n         */\n        int16_t getAnalogHat(AnalogHatEnum a);\n\n        /**\n         * Used to call your own function when the controller is successfully initialized.\n         * @param funcOnInit Function to call.\n         */\n        void attachOnInit(void (*funcOnInit)(void)) {\n                pFuncOnInit = funcOnInit;\n        };\n        /**@}*/\n\n        /** True if a Xbox ONE controller is connected. */\n        bool XboxOneConnected;\n\nprotected:\n        /** Pointer to USB class instance. */\n        USB *pUsb;\n        /** Device address. */\n        uint8_t bAddress;\n        /** Endpoint info structure. */\n        EpInfo epInfo[XBOX_MAX_ENDPOINTS];\n\nprivate:\n        /**\n         * Called when the controller is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         */\n        void onInit();\n        void (*pFuncOnInit)(void); // Pointer to function called in onInit()\n\n        bool bPollEnable;\n\n        /* Variables to store the buttons */\n        uint16_t ButtonState;\n        uint16_t OldButtonState;\n        uint16_t ButtonClickState;\n        int16_t hatValue[4];\n        uint16_t triggerValue[2];\n        uint16_t triggerValueOld[2];\n\n        bool L2Clicked; // These buttons are analog, so we use we use these bools to check if they where clicked or not\n        bool R2Clicked;\n\n        uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data\n        uint8_t writeBuf[12]; // General purpose buffer for output data\n\n        void readReport(); // read incoming data\n        void printReport(); // print incoming date - Uncomment for debugging\n\n        /* Private commands */\n        uint8_t XboxCommand(uint8_t* data, uint16_t nbytes);\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/XBOXRECV.cpp",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n\n getBatteryLevel and checkStatus functions made by timstamp.co.uk found using BusHound from Perisoft.net\n */\n\n#include \"XBOXRECV.h\"\n// To enable serial debugging see \"settings.h\"\n//#define EXTRADEBUG // Uncomment to get even more debugging data\n//#define PRINTREPORT // Uncomment to print the report send by the Xbox 360 Controller\n\nXBOXRECV::XBOXRECV(USB *p) :\npUsb(p), // pointer to USB class instance - mandatory\nbAddress(0), // device address - mandatory\nbPollEnable(false) { // don't start polling before dongle is connected\n        for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;\n        }\n\n        if(pUsb) // register in USB subsystem\n                pUsb->RegisterDeviceClass(this); //set devConfig[] entry\n}\n\nuint8_t XBOXRECV::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {\n        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);\n        uint8_t buf[constBufSize];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        uint16_t PID, VID;\n\n        AddressPool &addrPool = pUsb->GetAddressPool(); // Get memory address of USB device address pool\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nXBOXRECV Init\"), 0x80);\n#endif\n\n        if(bAddress) { // Check if address has already been assigned to an instance\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress in use\"), 0x80);\n#endif\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n        }\n\n        p = addrPool.GetUsbDevicePtr(0); // Get pointer to pseudo device with address 0 assigned\n\n        if(!p) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress not found\"), 0x80);\n#endif\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        if(!p->epinfo) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nepinfo is null\"), 0x80);\n#endif\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        oldep_ptr = p->epinfo; // Save old pointer to EP_RECORD of address 0\n        p->epinfo = epInfo; // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->lowspeed = lowspeed;\n\n        rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data\n\n        p->epinfo = oldep_ptr; // Restore p->epinfo\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        VID = udd->idVendor;\n        PID = udd->idProduct;\n\n        if((VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID) || (PID != XBOX_WIRELESS_RECEIVER_PID && PID != XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID)) { // Check if it's a Xbox receiver using the Vendor ID and Product ID\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nYou'll need a wireless receiver for this libary to work\"), 0x80);\n#endif\n                goto FailUnknownDevice;\n        }\n\n        bAddress = addrPool.AllocAddress(parent, false, port); // Allocate new address according to device class\n\n        if(!bAddress) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nOut of address space\"), 0x80);\n#endif\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n        }\n\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0; // Extract Max Packet Size from device descriptor\n\n        delay(20); // Wait a little before resetting device\n\n        return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;\n\n        /* Diagnostic messages */\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr(rcode);\n#endif\n        if(rcode != hrJERR)\n                rcode = USB_ERROR_FailGetDevDescr;\n        goto Fail;\n\nFailUnknownDevice:\n#ifdef DEBUG_USB_HOST\n        NotifyFailUnknownDevice(VID, PID);\n#endif\n        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\nFail:\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nXbox 360 Init Failed, error code: \"), 0x80);\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n};\n\nuint8_t XBOXRECV::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        uint8_t rcode;\n\n        AddressPool &addrPool = pUsb->GetAddressPool();\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nBTD Init\"), 0x80);\n#endif\n        UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record\n\n        if(!p) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress not found\"), 0x80);\n#endif\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        delay(300); // Assign new address to the device\n\n        rcode = pUsb->setAddr(0, 0, bAddress); // Assign new address to the device\n        if(rcode) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nsetAddr: \"), 0x80);\n                D_PrintHex<uint8_t > (rcode, 0x80);\n#endif\n                p->lowspeed = false;\n                goto Fail;\n        }\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nAddr: \"), 0x80);\n        D_PrintHex<uint8_t > (bAddress, 0x80);\n#endif\n\n        p->lowspeed = false;\n\n        p = addrPool.GetUsbDevicePtr(bAddress); // Get pointer to assigned address record\n        if(!p) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress not found\"), 0x80);\n#endif\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        p->lowspeed = lowspeed;\n\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo); // Assign epInfo to epinfo pointer - only EP0 is known\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        /* The application will work in reduced host mode, so we can save program and data\n           memory space. After verifying the VID we will use known values for the\n           configuration values for device, interface, endpoints and HID for the XBOX360 Wireless receiver */\n\n        /* Initialize data structures for endpoints of device */\n        epInfo[ XBOX_INPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 report endpoint - poll interval 1ms\n        epInfo[ XBOX_INPUT_PIPE_1 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_INPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_INPUT_PIPE_1 ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_INPUT_PIPE_1 ].bmSndToggle = 0;\n        epInfo[ XBOX_INPUT_PIPE_1 ].bmRcvToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE_1 ].epAddr = 0x01; // XBOX 360 output endpoint - poll interval 8ms\n        epInfo[ XBOX_OUTPUT_PIPE_1 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_OUTPUT_PIPE_1 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_OUTPUT_PIPE_1 ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_OUTPUT_PIPE_1 ].bmSndToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE_1 ].bmRcvToggle = 0;\n\n        epInfo[ XBOX_INPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 report endpoint - poll interval 1ms\n        epInfo[ XBOX_INPUT_PIPE_2 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_INPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_INPUT_PIPE_2 ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_INPUT_PIPE_2 ].bmSndToggle = 0;\n        epInfo[ XBOX_INPUT_PIPE_2 ].bmRcvToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE_2 ].epAddr = 0x03; // XBOX 360 output endpoint - poll interval 8ms\n        epInfo[ XBOX_OUTPUT_PIPE_2 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_OUTPUT_PIPE_2 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_OUTPUT_PIPE_2 ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_OUTPUT_PIPE_2 ].bmSndToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE_2 ].bmRcvToggle = 0;\n\n        epInfo[ XBOX_INPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 report endpoint - poll interval 1ms\n        epInfo[ XBOX_INPUT_PIPE_3 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_INPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_INPUT_PIPE_3 ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_INPUT_PIPE_3 ].bmSndToggle = 0;\n        epInfo[ XBOX_INPUT_PIPE_3 ].bmRcvToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE_3 ].epAddr = 0x05; // XBOX 360 output endpoint - poll interval 8ms\n        epInfo[ XBOX_OUTPUT_PIPE_3 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_OUTPUT_PIPE_3 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_OUTPUT_PIPE_3 ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_OUTPUT_PIPE_3 ].bmSndToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE_3 ].bmRcvToggle = 0;\n\n        epInfo[ XBOX_INPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 report endpoint - poll interval 1ms\n        epInfo[ XBOX_INPUT_PIPE_4 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_INPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_INPUT_PIPE_4 ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_INPUT_PIPE_4 ].bmSndToggle = 0;\n        epInfo[ XBOX_INPUT_PIPE_4 ].bmRcvToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE_4 ].epAddr = 0x07; // XBOX 360 output endpoint - poll interval 8ms\n        epInfo[ XBOX_OUTPUT_PIPE_4 ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_OUTPUT_PIPE_4 ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_OUTPUT_PIPE_4 ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_OUTPUT_PIPE_4 ].bmSndToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE_4 ].bmRcvToggle = 0;\n\n        rcode = pUsb->setEpInfoEntry(bAddress, 9, epInfo);\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        delay(200); //Give time for address change\n\n        rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);\n        if(rcode)\n                goto FailSetConfDescr;\n\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nXbox Wireless Receiver Connected\\r\\n\"), 0x80);\n#endif\n        XboxReceiverConnected = true;\n        bPollEnable = true;\n        checkStatusTimer = 0; // Reset timer\n        return 0; // Successful configuration\n\n        /* Diagnostic messages */\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n#endif\n\nFail:\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nXbox 360 Init Failed, error code: \"), 0x80);\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n\n/* Performs a cleanup after failed Init() attempt */\nuint8_t XBOXRECV::Release() {\n        XboxReceiverConnected = false;\n        for(uint8_t i = 0; i < 4; i++)\n                Xbox360Connected[i] = 0x00;\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n        bAddress = 0;\n        bPollEnable = false;\n        return 0;\n}\n\nuint8_t XBOXRECV::Poll() {\n        if(!bPollEnable)\n                return 0;\n        if(!checkStatusTimer || ((millis() - checkStatusTimer) > 3000)) { // Run checkStatus every 3 seconds\n                checkStatusTimer = millis();\n                checkStatus();\n        }\n\n        uint8_t inputPipe;\n        uint16_t bufferSize;\n        for(uint8_t i = 0; i < 4; i++) {\n                if(i == 0)\n                        inputPipe = XBOX_INPUT_PIPE_1;\n                else if(i == 1)\n                        inputPipe = XBOX_INPUT_PIPE_2;\n                else if(i == 2)\n                        inputPipe = XBOX_INPUT_PIPE_3;\n                else\n                        inputPipe = XBOX_INPUT_PIPE_4;\n\n                bufferSize = EP_MAXPKTSIZE; // This is the maximum number of bytes we want to receive\n                pUsb->inTransfer(bAddress, epInfo[ inputPipe ].epAddr, &bufferSize, readBuf);\n                if(bufferSize > 0) { // The number of received bytes\n#ifdef EXTRADEBUG\n                        Notify(PSTR(\"Bytes Received: \"), 0x80);\n                        D_PrintHex<uint16_t > (bufferSize, 0x80);\n                        Notify(PSTR(\"\\r\\n\"), 0x80);\n#endif\n                        readReport(i);\n#ifdef PRINTREPORT\n                        printReport(i, bufferSize); // Uncomment \"#define PRINTREPORT\" to print the report send by the Xbox 360 Controller\n#endif\n                }\n        }\n        return 0;\n}\n\nvoid XBOXRECV::readReport(uint8_t controller) {\n        if(readBuf == NULL)\n                return;\n        // This report is send when a controller is connected and disconnected\n        if(readBuf[0] == 0x08 && readBuf[1] != Xbox360Connected[controller]) {\n                Xbox360Connected[controller] = readBuf[1];\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"Controller \"), 0x80);\n                Notify(controller, 0x80);\n#endif\n                if(Xbox360Connected[controller]) {\n#ifdef DEBUG_USB_HOST\n                        const char* str = 0;\n                        switch(readBuf[1]) {\n                                case 0x80: str = PSTR(\" as controller\\r\\n\");\n                                        break;\n                                case 0x40: str = PSTR(\" as headset\\r\\n\");\n                                        break;\n                                case 0xC0: str = PSTR(\" as controller+headset\\r\\n\");\n                                        break;\n                        }\n                        Notify(PSTR(\": connected\"), 0x80);\n                        Notify(str, 0x80);\n#endif\n                        onInit(controller);\n                }\n#ifdef DEBUG_USB_HOST\n                else\n                        Notify(PSTR(\": disconnected\\r\\n\"), 0x80);\n#endif\n                return;\n        }\n        // Controller status report\n        if(readBuf[1] == 0x00 && readBuf[3] & 0x13 && readBuf[4] >= 0x22) {\n                controllerStatus[controller] = ((uint16_t)readBuf[3] << 8) | readBuf[4];\n                return;\n        }\n        if(readBuf[1] != 0x01) // Check if it's the correct report - the receiver also sends different status reports\n                return;\n\n        // A controller must be connected if it's sending data\n        if(!Xbox360Connected[controller])\n                Xbox360Connected[controller] |= 0x80;\n\n        ButtonState[controller] = (uint32_t)(readBuf[9] | ((uint16_t)readBuf[8] << 8) | ((uint32_t)readBuf[7] << 16) | ((uint32_t)readBuf[6] << 24));\n\n        hatValue[controller][LeftHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);\n        hatValue[controller][LeftHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);\n        hatValue[controller][RightHatX] = (int16_t)(((uint16_t)readBuf[15] << 8) | readBuf[14]);\n        hatValue[controller][RightHatY] = (int16_t)(((uint16_t)readBuf[17] << 8) | readBuf[16]);\n\n        //Notify(PSTR(\"\\r\\nButtonState: \"), 0x80);\n        //PrintHex<uint32_t>(ButtonState[controller], 0x80);\n\n        if(ButtonState[controller] != OldButtonState[controller]) {\n                buttonStateChanged[controller] = true;\n                ButtonClickState[controller] = (ButtonState[controller] >> 16) & ((~OldButtonState[controller]) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2\n                if(((uint8_t)OldButtonState[controller]) == 0 && ((uint8_t)ButtonState[controller]) != 0) // The L2 and R2 buttons are special as they are analog buttons\n                        R2Clicked[controller] = true;\n                if((uint8_t)(OldButtonState[controller] >> 8) == 0 && (uint8_t)(ButtonState[controller] >> 8) != 0)\n                        L2Clicked[controller] = true;\n                OldButtonState[controller] = ButtonState[controller];\n        }\n}\n\nvoid XBOXRECV::printReport(uint8_t controller, uint8_t nBytes) { //Uncomment \"#define PRINTREPORT\" to print the report send by the Xbox 360 Controller\n#ifdef PRINTREPORT\n        if(readBuf == NULL)\n                return;\n        Notify(PSTR(\"Controller \"), 0x80);\n        Notify(controller, 0x80);\n        Notify(PSTR(\": \"), 0x80);\n        for(uint8_t i = 0; i < nBytes; i++) {\n                D_PrintHex<uint8_t > (readBuf[i], 0x80);\n                Notify(PSTR(\" \"), 0x80);\n        }\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n#endif\n}\n\nuint8_t XBOXRECV::getButtonPress(ButtonEnum b, uint8_t controller) {\n        if(b == L2) // These are analog buttons\n                return (uint8_t)(ButtonState[controller] >> 8);\n        else if(b == R2)\n                return (uint8_t)ButtonState[controller];\n        return (bool)(ButtonState[controller] & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16));\n}\n\nbool XBOXRECV::getButtonClick(ButtonEnum b, uint8_t controller) {\n        if(b == L2) {\n                if(L2Clicked[controller]) {\n                        L2Clicked[controller] = false;\n                        return true;\n                }\n                return false;\n        } else if(b == R2) {\n                if(R2Clicked[controller]) {\n                        R2Clicked[controller] = false;\n                        return true;\n                }\n                return false;\n        }\n        uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);\n        bool click = (ButtonClickState[controller] & button);\n        ButtonClickState[controller] &= ~button; // clear \"click\" event\n        return click;\n}\n\nint16_t XBOXRECV::getAnalogHat(AnalogHatEnum a, uint8_t controller) {\n        return hatValue[controller][a];\n}\n\nbool XBOXRECV::buttonChanged(uint8_t controller) {\n        bool state = buttonStateChanged[controller];\n        buttonStateChanged[controller] = false;\n        return state;\n}\n\n/*\nControllerStatus Breakdown\nControllerStatus[controller] & 0x0001   // 0\nControllerStatus[controller] & 0x0002   // normal batteries, no rechargeable battery pack\nControllerStatus[controller] & 0x0004   // controller starting up / settling\nControllerStatus[controller] & 0x0008   // headset adapter plugged in, but no headphones connected (mute?)\nControllerStatus[controller] & 0x0010   // 0\nControllerStatus[controller] & 0x0020   // 1\nControllerStatus[controller] & 0x0040   // battery level (high bit)\nControllerStatus[controller] & 0x0080   // battery level (low bit)\nControllerStatus[controller] & 0x0100   // 1\nControllerStatus[controller] & 0x0200   // 1\nControllerStatus[controller] & 0x0400   // headset adapter plugged in\nControllerStatus[controller] & 0x0800   // 0\nControllerStatus[controller] & 0x1000   // 1\nControllerStatus[controller] & 0x2000   // 0\nControllerStatus[controller] & 0x4000   // 0\nControllerStatus[controller] & 0x8000   // 0\n */\nuint8_t XBOXRECV::getBatteryLevel(uint8_t controller) {\n        return ((controllerStatus[controller] & 0x00C0) >> 6);\n}\n\nvoid XBOXRECV::XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes) {\n#ifdef EXTRADEBUG\n        uint8_t rcode;\n#endif\n        uint8_t outputPipe;\n        switch(controller) {\n                case 0: outputPipe = XBOX_OUTPUT_PIPE_1;\n                        break;\n                case 1: outputPipe = XBOX_OUTPUT_PIPE_2;\n                        break;\n                case 2: outputPipe = XBOX_OUTPUT_PIPE_3;\n                        break;\n                case 3: outputPipe = XBOX_OUTPUT_PIPE_4;\n                        break;\n                default:\n                        return;\n        }\n#ifdef EXTRADEBUG\n        rcode =\n#endif\n                pUsb->outTransfer(bAddress, epInfo[ outputPipe ].epAddr, nbytes, data);\n#ifdef EXTRADEBUG\n        if(rcode)\n                Notify(PSTR(\"Error sending Xbox message\\r\\n\"), 0x80);\n#endif\n}\n\nvoid XBOXRECV::disconnect(uint8_t controller) {\n        writeBuf[0] = 0x00;\n        writeBuf[1] = 0x00;\n        writeBuf[2] = 0x08;\n        writeBuf[3] = 0xC0;\n\n        XboxCommand(controller, writeBuf, 4);\n}\n\nvoid XBOXRECV::setLedRaw(uint8_t value, uint8_t controller) {\n        writeBuf[0] = 0x00;\n        writeBuf[1] = 0x00;\n        writeBuf[2] = 0x08;\n        writeBuf[3] = value | 0x40;\n\n        XboxCommand(controller, writeBuf, 4);\n}\n\nvoid XBOXRECV::setLedOn(LEDEnum led, uint8_t controller) {\n        if(led == OFF)\n                setLedRaw(0, controller);\n        else if(led != ALL) // All LEDs can't be on a the same time\n                setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4, controller);\n}\n\nvoid XBOXRECV::setLedBlink(LEDEnum led, uint8_t controller) {\n        setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]), controller);\n}\n\nvoid XBOXRECV::setLedMode(LEDModeEnum ledMode, uint8_t controller) { // This function is used to do some speciel LED stuff the controller supports\n        setLedRaw((uint8_t)ledMode, controller);\n}\n\n/* PC runs this at interval of approx 2 seconds\nThanks to BusHound from Perisoft.net for the Windows USB Analysis output\nFound by timstamp.co.uk\n */\nvoid XBOXRECV::checkStatus() {\n        if(!bPollEnable)\n                return;\n        // Get controller info\n        writeBuf[0] = 0x08;\n        writeBuf[1] = 0x00;\n        writeBuf[2] = 0x0f;\n        writeBuf[3] = 0xc0;\n        for(uint8_t i = 0; i < 4; i++) {\n                XboxCommand(i, writeBuf, 4);\n        }\n        // Get battery status\n        writeBuf[0] = 0x00;\n        writeBuf[1] = 0x00;\n        writeBuf[2] = 0x00;\n        writeBuf[3] = 0x40;\n        for(uint8_t i = 0; i < 4; i++) {\n                if(Xbox360Connected[i])\n                        XboxCommand(i, writeBuf, 4);\n        }\n}\n\nvoid XBOXRECV::setRumbleOn(uint8_t lValue, uint8_t rValue, uint8_t controller) {\n        writeBuf[0] = 0x00;\n        writeBuf[1] = 0x01;\n        writeBuf[2] = 0x0f;\n        writeBuf[3] = 0xc0;\n        writeBuf[4] = 0x00;\n        writeBuf[5] = lValue; // big weight\n        writeBuf[6] = rValue; // small weight\n\n        XboxCommand(controller, writeBuf, 7);\n}\n\nvoid XBOXRECV::onInit(uint8_t controller) {\n        if(pFuncOnInit)\n                pFuncOnInit(); // Call the user function\n        else {\n                LEDEnum led;\n                if(controller == 0)\n                        led = LED1;\n                else if(controller == 1)\n                        led = LED2;\n                else if(controller == 2)\n                        led = LED3;\n                else\n                        led = LED4;\n                setLedOn(led, controller);\n        }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/XBOXRECV.h",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n\n getBatteryLevel and checkStatus functions made by timstamp.co.uk found using BusHound from Perisoft.net\n */\n\n#ifndef _xboxrecv_h_\n#define _xboxrecv_h_\n\n#include \"Usb.h\"\n#include \"xboxEnums.h\"\n\n/* Data Xbox 360 taken from descriptors */\n#define EP_MAXPKTSIZE       32 // max size for data via USB\n\n/* Names we give to the 9 Xbox360 pipes */\n#define XBOX_CONTROL_PIPE   0\n#define XBOX_INPUT_PIPE_1   1\n#define XBOX_OUTPUT_PIPE_1  2\n#define XBOX_INPUT_PIPE_2   3\n#define XBOX_OUTPUT_PIPE_2  4\n#define XBOX_INPUT_PIPE_3   5\n#define XBOX_OUTPUT_PIPE_3  6\n#define XBOX_INPUT_PIPE_4   7\n#define XBOX_OUTPUT_PIPE_4  8\n\n// PID and VID of the different devices\n#define XBOX_VID                                0x045E  // Microsoft Corporation\n#define MADCATZ_VID                             0x1BAD  // For unofficial Mad Catz receivers\n#define JOYTECH_VID                             0x162E  // For unofficial Joytech controllers\n\n#define XBOX_WIRELESS_RECEIVER_PID              0x0719  // Microsoft Wireless Gaming Receiver\n#define XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID  0x0291  // Third party Wireless Gaming Receiver\n\n#define XBOX_MAX_ENDPOINTS   9\n\n/**\n * This class implements support for a Xbox Wireless receiver.\n *\n * Up to four controllers can connect to one receiver, if more is needed one can use a second receiver via the USBHub class.\n */\nclass XBOXRECV : public USBDeviceConfig {\npublic:\n        /**\n         * Constructor for the XBOXRECV class.\n         * @param  pUsb   Pointer to USB class instance.\n         */\n        XBOXRECV(USB *pUsb);\n\n        /** @name USBDeviceConfig implementation */\n        /**\n         * Address assignment and basic initilization is done here.\n         * @param  parent   Hub number.\n         * @param  port     Port number on the hub.\n         * @param  lowspeed Speed of the device.\n         * @return          0 on success.\n         */\n        uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);\n        /**\n         * Initialize the Xbox wireless receiver.\n         * @param  parent   Hub number.\n         * @param  port     Port number on the hub.\n         * @param  lowspeed Speed of the device.\n         * @return          0 on success.\n         */\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        /**\n         * Release the USB device.\n         * @return 0 on success.\n         */\n        uint8_t Release();\n        /**\n         * Poll the USB Input endpoins and run the state machines.\n         * @return 0 on success.\n         */\n        uint8_t Poll();\n\n        /**\n         * Get the device address.\n         * @return The device address.\n         */\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        /**\n         * Used to check if the controller has been initialized.\n         * @return True if it's ready.\n         */\n        virtual bool isReady() {\n                return bPollEnable;\n        };\n\n        /**\n         * Used by the USB core to check what this driver support.\n         * @param  vid The device's VID.\n         * @param  pid The device's PID.\n         * @return     Returns true if the device's VID and PID matches this driver.\n         */\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                return ((vid == XBOX_VID || vid == MADCATZ_VID || vid == JOYTECH_VID) && (pid == XBOX_WIRELESS_RECEIVER_PID || pid == XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID));\n        };\n        /**@}*/\n\n        /** @name Xbox Controller functions */\n        /**\n         * getButtonPress(uint8_t controller, ButtonEnum b) will return true as long as the button is held down.\n         *\n         * While getButtonClick(uint8_t controller, ButtonEnum b) will only return it once.\n         *\n         * So you instance if you need to increase a variable once you would use getButtonClick(uint8_t controller, ButtonEnum b),\n         * but if you need to drive a robot forward you would use getButtonPress(uint8_t controller, ButtonEnum b).\n         * @param  b          ::ButtonEnum to read.\n         * @param  controller The controller to read from. Default to 0.\n         * @return            getButtonClick(uint8_t controller, ButtonEnum b) will return a bool, while getButtonPress(uint8_t controller, ButtonEnum b) will return a byte if reading ::L2 or ::R2.\n         */\n        uint8_t getButtonPress(ButtonEnum b, uint8_t controller = 0);\n        bool getButtonClick(ButtonEnum b, uint8_t controller = 0);\n        /**@}*/\n\n        /** @name Xbox Controller functions */\n        /**\n         * Return the analog value from the joysticks on the controller.\n         * @param  a          Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY.\n         * @param  controller The controller to read from. Default to 0.\n         * @return            Returns a signed 16-bit integer.\n         */\n        int16_t getAnalogHat(AnalogHatEnum a, uint8_t controller = 0);\n\n        /**\n         * Used to disconnect any of the controllers.\n         * @param controller The controller to disconnect. Default to 0.\n         */\n        void disconnect(uint8_t controller = 0);\n\n        /**\n         * Turn rumble off and all the LEDs on the specific controller.\n         * @param  controller The controller to write to. Default to 0.\n         */\n        void setAllOff(uint8_t controller = 0) {\n                setRumbleOn(0, 0, controller);\n                setLedOff(controller);\n        };\n\n        /**\n         * Turn rumble off the specific controller.\n         * @param  controller The controller to write to. Default to 0.\n         */\n        void setRumbleOff(uint8_t controller = 0) {\n                setRumbleOn(0, 0, controller);\n        };\n        /**\n         * Turn rumble on.\n         * @param lValue     Left motor (big weight) inside the controller.\n         * @param rValue     Right motor (small weight) inside the controller.\n         * @param controller The controller to write to. Default to 0.\n         */\n        void setRumbleOn(uint8_t lValue, uint8_t rValue, uint8_t controller = 0);\n        /**\n         * Set LED value. Without using the ::LEDEnum or ::LEDModeEnum.\n         * @param value      See:\n         * setLedOff(uint8_t controller), setLedOn(uint8_t controller, LED l),\n         * setLedBlink(uint8_t controller, LED l), and setLedMode(uint8_t controller, LEDMode lm).\n         * @param controller The controller to write to. Default to 0.\n         */\n        void setLedRaw(uint8_t value, uint8_t controller = 0);\n\n        /**\n         * Turn all LEDs off the specific controller.\n         * @param controller The controller to write to. Default to 0.\n         */\n        void setLedOff(uint8_t controller = 0) {\n                setLedRaw(0, controller);\n        };\n        /**\n         * Turn on a LED by using ::LEDEnum.\n         * @param l          ::OFF, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller.\n         * @param controller The controller to write to. Default to 0.\n         */\n        void setLedOn(LEDEnum l, uint8_t controller = 0);\n        /**\n         * Turn on a LED by using ::LEDEnum.\n         * @param l          ::ALL, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller.\n         * @param controller The controller to write to. Default to 0.\n         */\n        void setLedBlink(LEDEnum l, uint8_t controller = 0);\n        /**\n         * Used to set special LED modes supported by the Xbox controller.\n         * @param lm         See ::LEDModeEnum.\n         * @param controller The controller to write to. Default to 0.\n         */\n        void setLedMode(LEDModeEnum lm, uint8_t controller = 0);\n        /**\n         * Used to get the battery level from the controller.\n         * @param  controller The controller to read from. Default to 0.\n         * @return            Returns the battery level as an integer in the range of 0-3.\n         */\n        uint8_t getBatteryLevel(uint8_t controller = 0);\n        /**\n         * Used to check if a button has changed.\n         * @param  controller The controller to read from. Default to 0.\n         * @return            True if a button has changed.\n         */\n        bool buttonChanged(uint8_t controller = 0);\n\n        /**\n         * Used to call your own function when the controller is successfully initialized.\n         * @param funcOnInit Function to call.\n         */\n        void attachOnInit(void (*funcOnInit)(void)) {\n                pFuncOnInit = funcOnInit;\n        };\n        /**@}*/\n\n        /** True if a wireless receiver is connected. */\n        bool XboxReceiverConnected;\n        /** Variable used to indicate if the XBOX 360 controller is successfully connected. */\n        uint8_t Xbox360Connected[4];\n\nprotected:\n        /** Pointer to USB class instance. */\n        USB *pUsb;\n        /** Device address. */\n        uint8_t bAddress;\n        /** Endpoint info structure. */\n        EpInfo epInfo[XBOX_MAX_ENDPOINTS];\n\nprivate:\n        /**\n         * Called when the controller is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         * @param controller The initialized controller.\n         */\n        void onInit(uint8_t controller);\n        void (*pFuncOnInit)(void); // Pointer to function called in onInit()\n\n        bool bPollEnable;\n\n        /* Variables to store the buttons */\n        uint32_t ButtonState[4];\n        uint32_t OldButtonState[4];\n        uint16_t ButtonClickState[4];\n        int16_t hatValue[4][4];\n        uint16_t controllerStatus[4];\n        bool buttonStateChanged[4]; // True if a button has changed\n\n        bool L2Clicked[4]; // These buttons are analog, so we use we use these bools to check if they where clicked or not\n        bool R2Clicked[4];\n\n        uint32_t checkStatusTimer; // Timing for checkStatus() signals\n\n        uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data\n        uint8_t writeBuf[7]; // General purpose buffer for output data\n\n        void readReport(uint8_t controller); // read incoming data\n        void printReport(uint8_t controller, uint8_t nBytes); // print incoming date - Uncomment for debugging\n\n        /* Private commands */\n        void XboxCommand(uint8_t controller, uint8_t* data, uint16_t nbytes);\n        void checkStatus();\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/XBOXUSB.cpp",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#include \"XBOXUSB.h\"\n// To enable serial debugging see \"settings.h\"\n//#define EXTRADEBUG // Uncomment to get even more debugging data\n//#define PRINTREPORT // Uncomment to print the report send by the Xbox 360 Controller\n\nXBOXUSB::XBOXUSB(USB *p) :\npUsb(p), // pointer to USB class instance - mandatory\nbAddress(0), // device address - mandatory\nbPollEnable(false) { // don't start polling before dongle is connected\n        for(uint8_t i = 0; i < XBOX_MAX_ENDPOINTS; i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;\n        }\n\n        if(pUsb) // register in USB subsystem\n                pUsb->RegisterDeviceClass(this); //set devConfig[] entry\n}\n\nuint8_t XBOXUSB::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        uint16_t PID;\n        uint16_t VID;\n\n        // get memory address of USB device address pool\n        AddressPool &addrPool = pUsb->GetAddressPool();\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nXBOXUSB Init\"), 0x80);\n#endif\n        // check if address has already been assigned to an instance\n        if(bAddress) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress in use\"), 0x80);\n#endif\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n        }\n\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nAddress not found\"), 0x80);\n#endif\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        if(!p->epinfo) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nepinfo is null\"), 0x80);\n#endif\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf); // Get device descriptor - addr, ep, nbytes, data\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        VID = udd->idVendor;\n        PID = udd->idProduct;\n\n        if(VID != XBOX_VID && VID != MADCATZ_VID && VID != JOYTECH_VID && VID != GAMESTOP_VID) // Check VID\n                goto FailUnknownDevice;\n        if(PID == XBOX_WIRELESS_PID) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nYou have plugged in a wireless Xbox 360 controller - it doesn't support USB communication\"), 0x80);\n#endif\n                goto FailUnknownDevice;\n        } else if(PID == XBOX_WIRELESS_RECEIVER_PID || PID == XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID) {\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nThis library only supports Xbox 360 controllers via USB\"), 0x80);\n#endif\n                goto FailUnknownDevice;\n        } else if(PID != XBOX_WIRED_PID && PID != MADCATZ_WIRED_PID && PID != GAMESTOP_WIRED_PID && PID != AFTERGLOW_WIRED_PID && PID != JOYTECH_WIRED_PID) // Check PID\n                goto FailUnknownDevice;\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n#ifdef DEBUG_USB_HOST\n                Notify(PSTR(\"\\r\\nsetAddr: \"), 0x80);\n                D_PrintHex<uint8_t > (rcode, 0x80);\n#endif\n                return rcode;\n        }\n#ifdef EXTRADEBUG\n        Notify(PSTR(\"\\r\\nAddr: \"), 0x80);\n        D_PrintHex<uint8_t > (bAddress, 0x80);\n#endif\n        //delay(300); // Spec says you should wait at least 200ms\n\n        p->lowspeed = false;\n\n        //get pointer to assigned address record\n        p = addrPool.GetUsbDevicePtr(bAddress);\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        // Assign epInfo to epinfo pointer - only EP0 is known\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        /* The application will work in reduced host mode, so we can save program and data\n           memory space. After verifying the VID we will use known values for the\n           configuration values for device, interface, endpoints and HID for the XBOX360 Controllers */\n\n        /* Initialize data structures for endpoints of device */\n        epInfo[ XBOX_INPUT_PIPE ].epAddr = 0x01; // XBOX 360 report endpoint\n        epInfo[ XBOX_INPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_INPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_INPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_INPUT_PIPE ].bmSndToggle = 0;\n        epInfo[ XBOX_INPUT_PIPE ].bmRcvToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE ].epAddr = 0x02; // XBOX 360 output endpoint\n        epInfo[ XBOX_OUTPUT_PIPE ].epAttribs = USB_TRANSFER_TYPE_INTERRUPT;\n        epInfo[ XBOX_OUTPUT_PIPE ].bmNakPower = USB_NAK_NOWAIT; // Only poll once for interrupt endpoints\n        epInfo[ XBOX_OUTPUT_PIPE ].maxPktSize = EP_MAXPKTSIZE;\n        epInfo[ XBOX_OUTPUT_PIPE ].bmSndToggle = 0;\n        epInfo[ XBOX_OUTPUT_PIPE ].bmRcvToggle = 0;\n\n        rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        delay(200); // Give time for address change\n\n        rcode = pUsb->setConf(bAddress, epInfo[ XBOX_CONTROL_PIPE ].epAddr, 1);\n        if(rcode)\n                goto FailSetConfDescr;\n\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nXbox 360 Controller Connected\\r\\n\"), 0x80);\n#endif\n        onInit();\n        Xbox360Connected = true;\n        bPollEnable = true;\n        return 0; // Successful configuration\n\n        /* Diagnostic messages */\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr();\n        goto Fail;\n#endif\n\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n#endif\n        goto Fail;\n\nFailUnknownDevice:\n#ifdef DEBUG_USB_HOST\n        NotifyFailUnknownDevice(VID, PID);\n#endif\n        rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\nFail:\n#ifdef DEBUG_USB_HOST\n        Notify(PSTR(\"\\r\\nXbox 360 Init Failed, error code: \"), 0x80);\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n\n/* Performs a cleanup after failed Init() attempt */\nuint8_t XBOXUSB::Release() {\n        Xbox360Connected = false;\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n        bAddress = 0;\n        bPollEnable = false;\n        return 0;\n}\n\nuint8_t XBOXUSB::Poll() {\n        if(!bPollEnable)\n                return 0;\n        uint16_t BUFFER_SIZE = EP_MAXPKTSIZE;\n        pUsb->inTransfer(bAddress, epInfo[ XBOX_INPUT_PIPE ].epAddr, &BUFFER_SIZE, readBuf); // input on endpoint 1\n        readReport();\n#ifdef PRINTREPORT\n        printReport(); // Uncomment \"#define PRINTREPORT\" to print the report send by the Xbox 360 Controller\n#endif\n        return 0;\n}\n\nvoid XBOXUSB::readReport() {\n        if(readBuf == NULL)\n                return;\n        if(readBuf[0] != 0x00 || readBuf[1] != 0x14) { // Check if it's the correct report - the controller also sends different status reports\n                return;\n        }\n\n        ButtonState = (uint32_t)(readBuf[5] | ((uint16_t)readBuf[4] << 8) | ((uint32_t)readBuf[3] << 16) | ((uint32_t)readBuf[2] << 24));\n\n        hatValue[LeftHatX] = (int16_t)(((uint16_t)readBuf[7] << 8) | readBuf[6]);\n        hatValue[LeftHatY] = (int16_t)(((uint16_t)readBuf[9] << 8) | readBuf[8]);\n        hatValue[RightHatX] = (int16_t)(((uint16_t)readBuf[11] << 8) | readBuf[10]);\n        hatValue[RightHatY] = (int16_t)(((uint16_t)readBuf[13] << 8) | readBuf[12]);\n\n        //Notify(PSTR(\"\\r\\nButtonState\"), 0x80);\n        //PrintHex<uint32_t>(ButtonState, 0x80);\n\n        if(ButtonState != OldButtonState) {\n                ButtonClickState = (ButtonState >> 16) & ((~OldButtonState) >> 16); // Update click state variable, but don't include the two trigger buttons L2 and R2\n                if(((uint8_t)OldButtonState) == 0 && ((uint8_t)ButtonState) != 0) // The L2 and R2 buttons are special as they are analog buttons\n                        R2Clicked = true;\n                if((uint8_t)(OldButtonState >> 8) == 0 && (uint8_t)(ButtonState >> 8) != 0)\n                        L2Clicked = true;\n                OldButtonState = ButtonState;\n        }\n}\n\nvoid XBOXUSB::printReport() { //Uncomment \"#define PRINTREPORT\" to print the report send by the Xbox 360 Controller\n#ifdef PRINTREPORT\n        if(readBuf == NULL)\n                return;\n        for(uint8_t i = 0; i < XBOX_REPORT_BUFFER_SIZE; i++) {\n                D_PrintHex<uint8_t > (readBuf[i], 0x80);\n                Notify(PSTR(\" \"), 0x80);\n        }\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n#endif\n}\n\nuint8_t XBOXUSB::getButtonPress(ButtonEnum b) {\n        if(b == L2) // These are analog buttons\n                return (uint8_t)(ButtonState >> 8);\n        else if(b == R2)\n                return (uint8_t)ButtonState;\n        return (bool)(ButtonState & ((uint32_t)pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]) << 16));\n}\n\nbool XBOXUSB::getButtonClick(ButtonEnum b) {\n        if(b == L2) {\n                if(L2Clicked) {\n                        L2Clicked = false;\n                        return true;\n                }\n                return false;\n        } else if(b == R2) {\n                if(R2Clicked) {\n                        R2Clicked = false;\n                        return true;\n                }\n                return false;\n        }\n        uint16_t button = pgm_read_word(&XBOX_BUTTONS[(uint8_t)b]);\n        bool click = (ButtonClickState & button);\n        ButtonClickState &= ~button; // clear \"click\" event\n        return click;\n}\n\nint16_t XBOXUSB::getAnalogHat(AnalogHatEnum a) {\n        return hatValue[a];\n}\n\n/* Xbox Controller commands */\nvoid XBOXUSB::XboxCommand(uint8_t* data, uint16_t nbytes) {\n        //bmRequest = Host to device (0x00) | Class (0x20) | Interface (0x01) = 0x21, bRequest = Set Report (0x09), Report ID (0x00), Report Type (Output 0x02), interface (0x00), datalength, datalength, data)\n        pUsb->ctrlReq(bAddress, epInfo[XBOX_CONTROL_PIPE].epAddr, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, 0x00, 0x02, 0x00, nbytes, nbytes, data, NULL);\n}\n\nvoid XBOXUSB::setLedRaw(uint8_t value) {\n        writeBuf[0] = 0x01;\n        writeBuf[1] = 0x03;\n        writeBuf[2] = value;\n\n        XboxCommand(writeBuf, 3);\n}\n\nvoid XBOXUSB::setLedOn(LEDEnum led) {\n        if(led == OFF)\n                setLedRaw(0);\n        else if(led != ALL) // All LEDs can't be on a the same time\n                setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]) + 4);\n}\n\nvoid XBOXUSB::setLedBlink(LEDEnum led) {\n        setLedRaw(pgm_read_byte(&XBOX_LEDS[(uint8_t)led]));\n}\n\nvoid XBOXUSB::setLedMode(LEDModeEnum ledMode) { // This function is used to do some special LED stuff the controller supports\n        setLedRaw((uint8_t)ledMode);\n}\n\nvoid XBOXUSB::setRumbleOn(uint8_t lValue, uint8_t rValue) {\n        writeBuf[0] = 0x00;\n        writeBuf[1] = 0x08;\n        writeBuf[2] = 0x00;\n        writeBuf[3] = lValue; // big weight\n        writeBuf[4] = rValue; // small weight\n        writeBuf[5] = 0x00;\n        writeBuf[6] = 0x00;\n        writeBuf[7] = 0x00;\n\n        XboxCommand(writeBuf, 8);\n}\n\nvoid XBOXUSB::onInit() {\n        if(pFuncOnInit)\n                pFuncOnInit(); // Call the user function\n        else\n                setLedOn(LED1);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/XBOXUSB.h",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _xboxusb_h_\n#define _xboxusb_h_\n\n#include \"Usb.h\"\n#include \"hid.h\"\n#include \"xboxEnums.h\"\n\n/* Data Xbox 360 taken from descriptors */\n#define EP_MAXPKTSIZE       32 // max size for data via USB\n\n/* Names we give to the 3 Xbox360 pipes */\n#define XBOX_CONTROL_PIPE    0\n#define XBOX_INPUT_PIPE      1\n#define XBOX_OUTPUT_PIPE     2\n\n// PID and VID of the different devices\n#define XBOX_VID                                0x045E // Microsoft Corporation\n#define MADCATZ_VID                             0x1BAD // For unofficial Mad Catz controllers\n#define JOYTECH_VID                             0x162E // For unofficial Joytech controllers\n#define GAMESTOP_VID                            0x0E6F // Gamestop controller\n\n#define XBOX_WIRED_PID                          0x028E // Microsoft 360 Wired controller\n#define XBOX_WIRELESS_PID                       0x028F // Wireless controller only support charging\n#define XBOX_WIRELESS_RECEIVER_PID              0x0719 // Microsoft Wireless Gaming Receiver\n#define XBOX_WIRELESS_RECEIVER_THIRD_PARTY_PID  0x0291 // Third party Wireless Gaming Receiver\n#define MADCATZ_WIRED_PID                       0xF016 // Mad Catz wired controller\n#define JOYTECH_WIRED_PID                       0xBEEF // For Joytech wired controller\n#define GAMESTOP_WIRED_PID                      0x0401 // Gamestop wired controller\n#define AFTERGLOW_WIRED_PID                     0x0213 // Afterglow wired controller - it uses the same VID as a Gamestop controller\n\n#define XBOX_REPORT_BUFFER_SIZE 14 // Size of the input report buffer\n\n#define XBOX_MAX_ENDPOINTS   3\n\n/** This class implements support for a Xbox wired controller via USB. */\nclass XBOXUSB : public USBDeviceConfig {\npublic:\n        /**\n         * Constructor for the XBOXUSB class.\n         * @param  pUsb   Pointer to USB class instance.\n         */\n        XBOXUSB(USB *pUsb);\n\n        /** @name USBDeviceConfig implementation */\n        /**\n         * Initialize the Xbox Controller.\n         * @param  parent   Hub number.\n         * @param  port     Port number on the hub.\n         * @param  lowspeed Speed of the device.\n         * @return          0 on success.\n         */\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        /**\n         * Release the USB device.\n         * @return 0 on success.\n         */\n        uint8_t Release();\n        /**\n         * Poll the USB Input endpoins and run the state machines.\n         * @return 0 on success.\n         */\n        uint8_t Poll();\n\n        /**\n         * Get the device address.\n         * @return The device address.\n         */\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        /**\n         * Used to check if the controller has been initialized.\n         * @return True if it's ready.\n         */\n        virtual bool isReady() {\n                return bPollEnable;\n        };\n\n        /**\n         * Used by the USB core to check what this driver support.\n         * @param  vid The device's VID.\n         * @param  pid The device's PID.\n         * @return     Returns true if the device's VID and PID matches this driver.\n         */\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                return ((vid == XBOX_VID || vid == MADCATZ_VID || vid == JOYTECH_VID || vid == GAMESTOP_VID) && (pid == XBOX_WIRED_PID || pid == MADCATZ_WIRED_PID || pid == GAMESTOP_WIRED_PID || pid == AFTERGLOW_WIRED_PID || pid == JOYTECH_WIRED_PID));\n        };\n        /**@}*/\n\n        /** @name Xbox Controller functions */\n        /**\n         * getButtonPress(ButtonEnum b) will return true as long as the button is held down.\n         *\n         * While getButtonClick(ButtonEnum b) will only return it once.\n         *\n         * So you instance if you need to increase a variable once you would use getButtonClick(ButtonEnum b),\n         * but if you need to drive a robot forward you would use getButtonPress(ButtonEnum b).\n         * @param  b          ::ButtonEnum to read.\n         * @return            getButtonClick(ButtonEnum b) will return a bool, while getButtonPress(ButtonEnum b) will return a byte if reading ::L2 or ::R2.\n         */\n        uint8_t getButtonPress(ButtonEnum b);\n        bool getButtonClick(ButtonEnum b);\n        /**@}*/\n\n        /** @name Xbox Controller functions */\n        /**\n         * Return the analog value from the joysticks on the controller.\n         * @param  a          Either ::LeftHatX, ::LeftHatY, ::RightHatX or ::RightHatY.\n         * @return            Returns a signed 16-bit integer.\n         */\n        int16_t getAnalogHat(AnalogHatEnum a);\n\n        /** Turn rumble off and all the LEDs on the controller. */\n        void setAllOff() {\n                setRumbleOn(0, 0);\n                setLedRaw(0);\n        };\n\n        /** Turn rumble off the controller. */\n        void setRumbleOff() {\n                setRumbleOn(0, 0);\n        };\n        /**\n         * Turn rumble on.\n         * @param lValue     Left motor (big weight) inside the controller.\n         * @param rValue     Right motor (small weight) inside the controller.\n         */\n        void setRumbleOn(uint8_t lValue, uint8_t rValue);\n        /**\n         * Set LED value. Without using the ::LEDEnum or ::LEDModeEnum.\n         * @param value      See:\n         * setLedOff(), setLedOn(LEDEnum l),\n         * setLedBlink(LEDEnum l), and setLedMode(LEDModeEnum lm).\n         */\n        void setLedRaw(uint8_t value);\n\n        /** Turn all LEDs off the controller. */\n        void setLedOff() {\n                setLedRaw(0);\n        };\n        /**\n         * Turn on a LED by using ::LEDEnum.\n         * @param l          ::OFF, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller.\n         */\n        void setLedOn(LEDEnum l);\n        /**\n         * Turn on a LED by using ::LEDEnum.\n         * @param l          ::ALL, ::LED1, ::LED2, ::LED3 and ::LED4 is supported by the Xbox controller.\n         */\n        void setLedBlink(LEDEnum l);\n        /**\n         * Used to set special LED modes supported by the Xbox controller.\n         * @param lm         See ::LEDModeEnum.\n         */\n        void setLedMode(LEDModeEnum lm);\n\n        /**\n         * Used to call your own function when the controller is successfully initialized.\n         * @param funcOnInit Function to call.\n         */\n        void attachOnInit(void (*funcOnInit)(void)) {\n                pFuncOnInit = funcOnInit;\n        };\n        /**@}*/\n\n        /** True if a Xbox 360 controller is connected. */\n        bool Xbox360Connected;\n\nprotected:\n        /** Pointer to USB class instance. */\n        USB *pUsb;\n        /** Device address. */\n        uint8_t bAddress;\n        /** Endpoint info structure. */\n        EpInfo epInfo[XBOX_MAX_ENDPOINTS];\n\nprivate:\n        /**\n         * Called when the controller is successfully initialized.\n         * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.\n         * This is useful for instance if you want to set the LEDs in a specific way.\n         */\n        void onInit();\n        void (*pFuncOnInit)(void); // Pointer to function called in onInit()\n\n        bool bPollEnable;\n\n        /* Variables to store the buttons */\n        uint32_t ButtonState;\n        uint32_t OldButtonState;\n        uint16_t ButtonClickState;\n        int16_t hatValue[4];\n        uint16_t controllerStatus;\n\n        bool L2Clicked; // These buttons are analog, so we use we use these bools to check if they where clicked or not\n        bool R2Clicked;\n\n        uint8_t readBuf[EP_MAXPKTSIZE]; // General purpose buffer for input data\n        uint8_t writeBuf[8]; // General purpose buffer for output data\n\n        void readReport(); // read incoming data\n        void printReport(); // print incoming date - Uncomment for debugging\n\n        /* Private commands */\n        void XboxCommand(uint8_t* data, uint16_t nbytes);\n};\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/address.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#if !defined(_usb_h_) || defined(__ADDRESS_H__)\n#error \"Never include address.h directly; include Usb.h instead\"\n#else\n#define __ADDRESS_H__\n\n\n\n/* NAK powers. To save space in endpoint data structure, amount of retries before giving up and returning 0x4 is stored in */\n/* bmNakPower as a power of 2. The actual nak_limit is then calculated as nak_limit = ( 2^bmNakPower - 1) */\n#define USB_NAK_MAX_POWER               15              //NAK binary order maximum value\n#define USB_NAK_DEFAULT                 14              //default 32K-1 NAKs before giving up\n#define USB_NAK_NOWAIT                  1               //Single NAK stops transfer\n#define USB_NAK_NONAK                   0               //Do not count NAKs, stop retrying after USB Timeout\n\nstruct EpInfo {\n        uint8_t epAddr; // Endpoint address\n        uint8_t maxPktSize; // Maximum packet size\n\n        union {\n                uint8_t epAttribs;\n\n                struct {\n                        uint8_t bmSndToggle : 1; // Send toggle, when zero bmSNDTOG0, bmSNDTOG1 otherwise\n                        uint8_t bmRcvToggle : 1; // Send toggle, when zero bmRCVTOG0, bmRCVTOG1 otherwise\n                        uint8_t bmNakPower : 6; // Binary order for NAK_LIMIT value\n                } __attribute__((packed));\n        };\n} __attribute__((packed));\n\n//        7   6   5   4   3   2   1   0\n//  ---------------------------------\n//  |   | H | P | P | P | A | A | A |\n//  ---------------------------------\n//\n// H - if 1 the address is a hub address\n// P - parent hub address\n// A - device address / port number in case of hub\n//\n\nstruct UsbDeviceAddress {\n\n        union {\n\n                struct {\n                        uint8_t bmAddress : 3; // device address/port number\n                        uint8_t bmParent : 3; // parent hub address\n                        uint8_t bmHub : 1; // hub flag\n                        uint8_t bmReserved : 1; // reserved, must be zero\n                } __attribute__((packed));\n                uint8_t devAddress;\n        };\n} __attribute__((packed));\n\n#define bmUSB_DEV_ADDR_ADDRESS          0x07\n#define bmUSB_DEV_ADDR_PARENT           0x38\n#define bmUSB_DEV_ADDR_HUB              0x40\n\nstruct UsbDevice {\n        EpInfo *epinfo; // endpoint info pointer\n        UsbDeviceAddress address;\n        uint8_t epcount; // number of endpoints\n        bool lowspeed; // indicates if a device is the low speed one\n        //      uint8_t devclass; // device class\n} __attribute__((packed));\n\nclass AddressPool {\npublic:\n        virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) = 0;\n        virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) = 0;\n        virtual void FreeAddress(uint8_t addr) = 0;\n};\n\ntypedef void (*UsbDeviceHandleFunc)(UsbDevice *pdev);\n\n#define ADDR_ERROR_INVALID_INDEX                0xFF\n#define ADDR_ERROR_INVALID_ADDRESS              0xFF\n\ntemplate <const uint8_t MAX_DEVICES_ALLOWED>\nclass AddressPoolImpl : public AddressPool {\n        EpInfo dev0ep; //Endpoint data structure used during enumeration for uninitialized device\n\n        uint8_t hubCounter; // hub counter is kept\n        // in order to avoid hub address duplication\n\n        UsbDevice thePool[MAX_DEVICES_ALLOWED];\n\n        // Initializes address pool entry\n\n        void InitEntry(uint8_t index) {\n                thePool[index].address.devAddress = 0;\n                thePool[index].epcount = 1;\n                thePool[index].lowspeed = 0;\n                thePool[index].epinfo = &dev0ep;\n        };\n\n        // Returns thePool index for a given address\n\n        uint8_t FindAddressIndex(uint8_t address = 0) {\n                for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++) {\n                        if(thePool[i].address.devAddress == address)\n                                return i;\n                }\n                return 0;\n        };\n\n        // Returns thePool child index for a given parent\n\n        uint8_t FindChildIndex(UsbDeviceAddress addr, uint8_t start = 1) {\n                for(uint8_t i = (start < 1 || start >= MAX_DEVICES_ALLOWED) ? 1 : start; i < MAX_DEVICES_ALLOWED; i++) {\n                        if(thePool[i].address.bmParent == addr.bmAddress)\n                                return i;\n                }\n                return 0;\n        };\n\n        // Frees address entry specified by index parameter\n\n        void FreeAddressByIndex(uint8_t index) {\n                // Zero field is reserved and should not be affected\n                if(index == 0)\n                        return;\n\n                UsbDeviceAddress uda = thePool[index].address;\n                // If a hub was switched off all port addresses should be freed\n                if(uda.bmHub == 1) {\n                        for(uint8_t i = 1; (i = FindChildIndex(uda, i));)\n                                FreeAddressByIndex(i);\n\n                        // If the hub had the last allocated address, hubCounter should be decremented\n                        if(hubCounter == uda.bmAddress)\n                                hubCounter--;\n                }\n                InitEntry(index);\n        }\n\n        // Initializes the whole address pool at once\n\n        void InitAllAddresses() {\n                for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)\n                        InitEntry(i);\n\n                hubCounter = 0;\n        };\n\npublic:\n\n        AddressPoolImpl() : hubCounter(0) {\n                // Zero address is reserved\n                InitEntry(0);\n\n                thePool[0].address.devAddress = 0;\n                thePool[0].epinfo = &dev0ep;\n                dev0ep.epAddr = 0;\n                dev0ep.maxPktSize = 8;\n                dev0ep.epAttribs = 0; //set DATA0/1 toggles to 0\n                dev0ep.bmNakPower = USB_NAK_MAX_POWER;\n\n                InitAllAddresses();\n        };\n\n        // Returns a pointer to a specified address entry\n\n        virtual UsbDevice* GetUsbDevicePtr(uint8_t addr) {\n                if(!addr)\n                        return thePool;\n\n                uint8_t index = FindAddressIndex(addr);\n\n                return (!index) ? NULL : thePool + index;\n        };\n\n        // Performs an operation specified by pfunc for each addressed device\n\n        void ForEachUsbDevice(UsbDeviceHandleFunc pfunc) {\n                if(!pfunc)\n                        return;\n\n                for(uint8_t i = 1; i < MAX_DEVICES_ALLOWED; i++)\n                        if(thePool[i].address.devAddress)\n                                pfunc(thePool + i);\n        };\n\n        // Allocates new address\n\n        virtual uint8_t AllocAddress(uint8_t parent, bool is_hub = false, uint8_t port = 0) {\n                /* if (parent != 0 && port == 0)\n                        USB_HOST_SERIAL.println(\"PRT:0\"); */\n                UsbDeviceAddress _parent;\n                _parent.devAddress = parent;\n                if(_parent.bmReserved || port > 7)\n                        //if(parent > 127 || port > 7)\n                        return 0;\n\n                if(is_hub && hubCounter == 7)\n                        return 0;\n\n                // finds first empty address entry starting from one\n                uint8_t index = FindAddressIndex(0);\n\n                if(!index) // if empty entry is not found\n                        return 0;\n\n                if(_parent.devAddress == 0) {\n                        if(is_hub) {\n                                thePool[index].address.devAddress = 0x41;\n                                hubCounter++;\n                        } else\n                                thePool[index].address.devAddress = 1;\n\n                        return thePool[index].address.devAddress;\n                }\n\n                UsbDeviceAddress addr;\n                addr.devAddress = 0; // Ensure all bits are zero\n                addr.bmParent = _parent.bmAddress;\n                if(is_hub) {\n                        addr.bmHub = 1;\n                        addr.bmAddress = ++hubCounter;\n                } else {\n                        addr.bmHub = 0;\n                        addr.bmAddress = port;\n                }\n                thePool[index].address = addr;\n                /*\n                                USB_HOST_SERIAL.print(\"Addr:\");\n                                USB_HOST_SERIAL.print(addr.bmHub, HEX);\n                                USB_HOST_SERIAL.print(\".\");\n                                USB_HOST_SERIAL.print(addr.bmParent, HEX);\n                                USB_HOST_SERIAL.print(\".\");\n                                USB_HOST_SERIAL.println(addr.bmAddress, HEX);\n                 */\n                return thePool[index].address.devAddress;\n        };\n\n        // Empties pool entry\n\n        virtual void FreeAddress(uint8_t addr) {\n                // if the root hub is disconnected all the addresses should be initialized\n                if(addr == 0x41) {\n                        InitAllAddresses();\n                        return;\n                }\n                uint8_t index = FindAddressIndex(addr);\n                FreeAddressByIndex(index);\n        };\n\n        // Returns number of hubs attached\n        // It can be rather helpfull to find out if there are hubs attached than getting the exact number of hubs.\n        //uint8_t GetNumHubs()\n        //{\n        //        return hubCounter;\n        //};\n        //uint8_t GetNumDevices()\n        //{\n        //        uint8_t counter = 0;\n\n        //        for (uint8_t i=1; i<MAX_DEVICES_ALLOWED; i++)\n        //                if (thePool[i].address != 0);\n        //                        counter ++;\n\n        //        return counter;\n        //};\n};\n\n#endif // __ADDRESS_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/adk.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n/* Google ADK interface */\n\n#include \"adk.h\"\n\nconst uint8_t ADK::epDataInIndex = 1;\nconst uint8_t ADK::epDataOutIndex = 2;\n\nADK::ADK(USB *p, const char* manufacturer,\n        const char* model,\n        const char* description,\n        const char* version,\n        const char* uri,\n        const char* serial) :\n\n/* ADK ID Strings */\nmanufacturer(manufacturer),\nmodel(model),\ndescription(description),\nversion(version),\nuri(uri),\nserial(serial),\npUsb(p), //pointer to USB class instance - mandatory\nbAddress(0), //device address - mandatory\nbConfNum(0), //configuration number\nbNumEP(1), //if config descriptor needs to be parsed\nready(false) {\n        // initialize endpoint data structures\n        for(uint8_t i = 0; i < ADK_MAX_ENDPOINTS; i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;\n        }//for(uint8_t i=0; i<ADK_MAX_ENDPOINTS; i++...\n\n        // register in USB subsystem\n        if(pUsb) {\n                pUsb->RegisterDeviceClass(this); //set devConfig[] entry\n        }\n}\n\nuint8_t ADK::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {\n        return Init(parent, port, lowspeed); // Just call Init. Yes, really!\n}\n\n/* Connection initialization of an Android phone */\nuint8_t ADK::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        uint8_t buf[sizeof (USB_DEVICE_DESCRIPTOR)];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        uint8_t num_of_conf; // number of configurations\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n\n        // get memory address of USB device address pool\n        AddressPool &addrPool = pUsb->GetAddressPool();\n\n        USBTRACE(\"\\r\\nADK Init\");\n\n        // check if address has already been assigned to an instance\n        if(bAddress) {\n                USBTRACE(\"\\r\\nAddress in use\");\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n        }\n\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p) {\n                USBTRACE(\"\\r\\nAddress not found\");\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        if(!p->epinfo) {\n                USBTRACE(\"epinfo is null\\r\\n\");\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);\n\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(rcode) {\n                goto FailGetDevDescr;\n        }\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        // Extract Max Packet Size from device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n                //USBTRACE2(\"setAddr:\",rcode);\n                return rcode;\n        }//if (rcode...\n\n        //USBTRACE2(\"\\r\\nAddr:\", bAddress);\n        // Spec says you should wait at least 200ms.\n        //delay(300);\n\n        p->lowspeed = false;\n\n        //get pointer to assigned address record\n        p = addrPool.GetUsbDevicePtr(bAddress);\n        if(!p) {\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        p->lowspeed = lowspeed;\n\n        // Assign epInfo to epinfo pointer - only EP0 is known\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);\n        if(rcode) {\n                goto FailSetDevTblEntry;\n        }\n\n        //check if ADK device is already in accessory mode; if yes, configure and exit\n        if(udd->idVendor == ADK_VID &&\n                (udd->idProduct == ADK_PID || udd->idProduct == ADB_PID)) {\n                USBTRACE(\"\\r\\nAcc.mode device detected\");\n                /* go through configurations, find first bulk-IN, bulk-OUT EP, fill epInfo and quit */\n                num_of_conf = udd->bNumConfigurations;\n\n                //USBTRACE2(\"\\r\\nNC:\",num_of_conf);\n                for(uint8_t i = 0; i < num_of_conf; i++) {\n                        ConfigDescParser < 0, 0, 0, 0 > confDescrParser(this);\n                        delay(1);\n                        rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);\n#if defined(XOOM)\n                        //added by Jaylen Scott Vanorden\n                        if(rcode) {\n                                USBTRACE2(\"\\r\\nGot 1st bad code for config: \", rcode);\n                                // Try once more\n                                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);\n                        }\n#endif\n                        if(rcode) {\n                                goto FailGetConfDescr;\n                        }\n                        if(bNumEP > 2) {\n                                break;\n                        }\n                } // for (uint8_t i=0; i<num_of_conf; i++...\n\n                if(bNumEP == 3) {\n                        // Assign epInfo to epinfo pointer - this time all 3 endpoins\n                        rcode = pUsb->setEpInfoEntry(bAddress, 3, epInfo);\n                        if(rcode) {\n                                goto FailSetDevTblEntry;\n                        }\n                }\n\n                // Set Configuration Value\n                rcode = pUsb->setConf(bAddress, 0, bConfNum);\n                if(rcode) {\n                        goto FailSetConfDescr;\n                }\n                /* print endpoint structure */\n                /*\n                USBTRACE(\"\\r\\nEndpoint Structure:\");\n                USBTRACE(\"\\r\\nEP0:\");\n                USBTRACE2(\"\\r\\nAddr: \", epInfo[0].epAddr);\n                USBTRACE2(\"\\r\\nMax.pkt.size: \", epInfo[0].maxPktSize);\n                USBTRACE2(\"\\r\\nAttr: \", epInfo[0].epAttribs);\n                USBTRACE(\"\\r\\nEpout:\");\n                USBTRACE2(\"\\r\\nAddr: \", epInfo[epDataOutIndex].epAddr);\n                USBTRACE2(\"\\r\\nMax.pkt.size: \", epInfo[epDataOutIndex].maxPktSize);\n                USBTRACE2(\"\\r\\nAttr: \", epInfo[epDataOutIndex].epAttribs);\n                USBTRACE(\"\\r\\nEpin:\");\n                USBTRACE2(\"\\r\\nAddr: \", epInfo[epDataInIndex].epAddr);\n                USBTRACE2(\"\\r\\nMax.pkt.size: \", epInfo[epDataInIndex].maxPktSize);\n                USBTRACE2(\"\\r\\nAttr: \", epInfo[epDataInIndex].epAttribs);\n                 */\n\n                USBTRACE(\"\\r\\nConfiguration successful\");\n                ready = true;\n                return 0; //successful configuration\n        }//if( buf->idVendor == ADK_VID...\n\n        //probe device - get accessory protocol revision\n        {\n                uint16_t adkproto = -1;\n                delay(1);\n                rcode = getProto((uint8_t*) & adkproto);\n#if defined(XOOM)\n                //added by Jaylen Scott Vanorden\n                if(rcode) {\n                        USBTRACE2(\"\\r\\nGot 1st bad code for proto: \", rcode);\n                        // Try once more\n                        rcode = getProto((uint8_t*) & adkproto);\n                }\n#endif\n                if(rcode) {\n                        goto FailGetProto; //init fails\n                }\n                USBTRACE2(\"\\r\\nADK protocol rev. \", adkproto);\n        }\n\n        delay(100);\n\n        //sending ID strings\n        sendStr(ACCESSORY_STRING_MANUFACTURER, manufacturer);\n        delay(10);\n        sendStr(ACCESSORY_STRING_MODEL, model);\n        delay(10);\n        sendStr(ACCESSORY_STRING_DESCRIPTION, description);\n        delay(10);\n        sendStr(ACCESSORY_STRING_VERSION, version);\n        delay(10);\n        sendStr(ACCESSORY_STRING_URI, uri);\n        delay(10);\n        sendStr(ACCESSORY_STRING_SERIAL, serial);\n\n        delay(100);\n\n        //switch to accessory mode\n        //the Android phone will reset\n        rcode = switchAcc();\n        if(rcode) {\n                goto FailSwAcc; //init fails\n        }\n        rcode = USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;\n        delay(100); // Give Android a chance to do its reset. This is a guess, and possibly could be lower.\n        goto SwAttempt; //switch to accessory mode attempted\n\n        /* diagnostic messages */\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr(rcode);\n        goto Fail;\n#endif\n\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry(rcode);\n        goto Fail;\n#endif\n\nFailGetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetConfDescr(rcode);\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr(rcode);\n        goto Fail;\n#endif\n\nFailGetProto:\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"\\r\\ngetProto:\");\n        goto Fail;\n#endif\n\nFailSwAcc:\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"\\r\\nswAcc:\");\n        goto Fail;\n#endif\n\n        //FailOnInit:\n        //        USBTRACE(\"OnInit:\");\n        //        goto Fail;\n        //\nSwAttempt:\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"\\r\\nAccessory mode switch attempt\");\nFail:\n#endif\n        //USBTRACE2(\"\\r\\nADK Init Failed, error code: \", rcode);\n        //NotifyFail(rcode);\n        Release();\n        return rcode;\n}\n\n/* Extracts bulk-IN and bulk-OUT endpoint information from config descriptor */\nvoid ADK::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) {\n        //ErrorMessage<uint8_t>(PSTR(\"Conf.Val\"), conf);\n        //ErrorMessage<uint8_t>(PSTR(\"Iface Num\"), iface);\n        //ErrorMessage<uint8_t>(PSTR(\"Alt.Set\"), alt);\n\n        //added by Yuuichi Akagawa\n        if(bNumEP == 3) {\n                return;\n        }\n\n        bConfNum = conf;\n\n        if((pep->bmAttributes & 0x02) == 2) {\n                uint8_t index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;\n                // Fill in the endpoint info structure\n                epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);\n                epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;\n\n                bNumEP++;\n\n                //PrintEndpointDescriptor(pep);\n        }\n}\n\n/* Performs a cleanup after failed Init() attempt */\nuint8_t ADK::Release() {\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n\n        bNumEP = 1; //must have to be reset to 1\n\n        bAddress = 0;\n        ready = false;\n        return 0;\n}\n\nuint8_t ADK::RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) {\n        //USBTRACE2(\"\\r\\nAddr: \", bAddress );\n        //USBTRACE2(\"\\r\\nEP: \",epInfo[epDataInIndex].epAddr);\n        return pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr);\n}\n\nuint8_t ADK::SndData(uint16_t nbytes, uint8_t *dataptr) {\n        return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, nbytes, dataptr);\n}\n\nvoid ADK::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {\n        Notify(PSTR(\"Endpoint descriptor:\"), 0x80);\n        Notify(PSTR(\"\\r\\nLength:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);\n        Notify(PSTR(\"\\r\\nType:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);\n        Notify(PSTR(\"\\r\\nAddress:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);\n        Notify(PSTR(\"\\r\\nAttributes:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);\n        Notify(PSTR(\"\\r\\nMaxPktSize:\\t\"), 0x80);\n        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);\n        Notify(PSTR(\"\\r\\nPoll Intrv:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/adk.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n/* Google ADK interface support header */\n\n#if !defined(_ADK_H_)\n#define _ADK_H_\n\n#include \"Usb.h\"\n\n#define ADK_VID   0x18D1\n#define ADK_PID   0x2D00\n#define ADB_PID   0x2D01\n\n#define XOOM  //enables repeating getProto() and getConf() attempts\n//necessary for slow devices such as Motorola XOOM\n//defined by default, can be commented out to save memory\n\n/* requests */\n\n#define ADK_GETPROTO      51  //check USB accessory protocol version\n#define ADK_SENDSTR       52  //send identifying string\n#define ADK_ACCSTART      53  //start device in accessory mode\n\n#define bmREQ_ADK_GET     USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_VENDOR|USB_SETUP_RECIPIENT_DEVICE\n#define bmREQ_ADK_SEND    USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_VENDOR|USB_SETUP_RECIPIENT_DEVICE\n\n#define ACCESSORY_STRING_MANUFACTURER   0\n#define ACCESSORY_STRING_MODEL          1\n#define ACCESSORY_STRING_DESCRIPTION    2\n#define ACCESSORY_STRING_VERSION        3\n#define ACCESSORY_STRING_URI            4\n#define ACCESSORY_STRING_SERIAL         5\n\n#define ADK_MAX_ENDPOINTS 3 //endpoint 0, bulk_IN, bulk_OUT\n\nclass ADK;\n\nclass ADK : public USBDeviceConfig, public UsbConfigXtracter {\nprivate:\n        /* ID strings */\n        const char* manufacturer;\n        const char* model;\n        const char* description;\n        const char* version;\n        const char* uri;\n        const char* serial;\n\n        /* ADK proprietary requests */\n        uint8_t getProto(uint8_t* adkproto);\n        uint8_t sendStr(uint8_t index, const char* str);\n        uint8_t switchAcc(void);\n\nprotected:\n        static const uint8_t epDataInIndex; // DataIn endpoint index\n        static const uint8_t epDataOutIndex; // DataOUT endpoint index\n\n        /* mandatory members */\n        USB *pUsb;\n        uint8_t bAddress;\n        uint8_t bConfNum; // configuration number\n\n        uint8_t bNumEP; // total number of EP in the configuration\n        bool ready;\n\n        /* Endpoint data structure */\n        EpInfo epInfo[ADK_MAX_ENDPOINTS];\n\n        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);\n\npublic:\n        ADK(USB *pUsb, const char* manufacturer,\n                const char* model,\n                const char* description,\n                const char* version,\n                const char* uri,\n                const char* serial);\n\n        // Methods for receiving and sending data\n        uint8_t RcvData(uint16_t *nbytesptr, uint8_t *dataptr);\n        uint8_t SndData(uint16_t nbytes, uint8_t *dataptr);\n\n\n        // USBDeviceConfig implementation\n        uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        uint8_t Release();\n\n        virtual uint8_t Poll() {\n                return 0;\n        };\n\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        virtual bool isReady() {\n                return ready;\n        };\n\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                return (vid == ADK_VID && (pid == ADK_PID || pid == ADB_PID));\n        };\n\n        //UsbConfigXtracter implementation\n        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);\n}; //class ADK : public USBDeviceConfig ...\n\n/* get ADK protocol version */\n\n/* returns 2 bytes in *adkproto */\ninline uint8_t ADK::getProto(uint8_t* adkproto) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_GET, ADK_GETPROTO, 0, 0, 0, 2, 2, adkproto, NULL));\n}\n\n/* send ADK string */\ninline uint8_t ADK::sendStr(uint8_t index, const char* str) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_SEND, ADK_SENDSTR, 0, 0, index, strlen(str) + 1, strlen(str) + 1, (uint8_t*)str, NULL));\n}\n\n/* switch to accessory mode */\ninline uint8_t ADK::switchAcc(void) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_ADK_SEND, ADK_ACCSTART, 0, 0, 0, 0, 0, NULL, NULL));\n}\n\n#endif // _ADK_H_\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/avrpins.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n/* derived from Konstantin Chizhov's AVR port templates */\n\n#if !defined(_usb_h_) || defined(_avrpins_h_)\n#error \"Never include avrpins.h directly; include Usb.h instead\"\n#else\n#define _avrpins_h_\n\n#if defined(__AVR__)\n\n// pointers are 16 bits on AVR\n#define pgm_read_pointer(p) pgm_read_word(p)\n\n// Support for these boards needs to be manually activated in settings.h or in a makefile\n#if !defined(BOARD_MEGA_ADK) && defined(__AVR_ATmega2560__) && (USE_UHS_MEGA_ADK || defined(ARDUINO_AVR_ADK))\n#define BOARD_MEGA_ADK\n#elif !defined(BOARD_BLACK_WIDDOW) && USE_UHS_BLACK_WIDDOW\n#define BOARD_BLACK_WIDDOW\n#endif\n\n#ifdef PORTA\n#define USE_PORTA\n#endif\n#ifdef PORTB\n#define USE_PORTB\n#endif\n#ifdef PORTC\n#define USE_PORTC\n#endif\n#ifdef PORTD\n#define USE_PORTD\n#endif\n#ifdef PORTE\n#define USE_PORTE\n#endif\n#ifdef PORTF\n#define USE_PORTF\n#endif\n#ifdef PORTG\n#define USE_PORTG\n#endif\n#ifdef PORTH\n#define USE_PORTH\n#endif\n#ifdef PORTJ\n#define USE_PORTJ\n#endif\n#ifdef PORTK\n#define USE_PORTK\n#endif\n#ifdef PORTL\n#define USE_PORTL\n#endif\n#ifdef PORTQ\n#define USE_PORTQ\n#endif\n#ifdef PORTR\n#define USE_PORTR\n#endif\n\n#ifdef TCCR0A\n#define USE_TCCR0A\n#endif\n#ifdef TCCR1A\n#define USE_TCCR1A\n#endif\n#ifdef TCCR2A\n#define USE_TCCR2A\n#endif\n\n//Port definitions for AtTiny, AtMega families.\n\n#define MAKE_PORT(portName, ddrName, pinName, className, ID) \\\n    class className{\\\n    public:\\\n      typedef uint8_t DataT;\\\n    public:\\\n      static void Write(DataT value){portName = value;}\\\n      static void ClearAndSet(DataT clearMask, DataT value){portName = (portName & ~clearMask) | value;}\\\n      static DataT Read(){return portName;}\\\n      static void DirWrite(DataT value){ddrName = value;}\\\n      static DataT DirRead(){return ddrName;}\\\n      static void Set(DataT value){portName |= value;}\\\n      static void Clear(DataT value){portName &= ~value;}\\\n      static void Toggle(DataT value){portName ^= value;}\\\n      static void DirSet(DataT value){ddrName |= value;}\\\n      static void DirClear(DataT value){ddrName &= ~value;}\\\n      static void DirToggle(DataT value){ddrName ^= value;}\\\n      static DataT PinRead(){return pinName;}\\\n      enum{Id = ID};\\\n      enum{Width=sizeof(DataT)*8};\\\n    };\n\n// TCCR registers to set/clear Arduino PWM\n#define MAKE_TCCR(TccrName, className) \\\n    class className{\\\n    public:\\\n      typedef uint8_t DataT;\\\n    public:\\\n      static void Write(DataT value){TccrName = value;}\\\n      static void ClearAndSet(DataT clearMask, DataT value){TccrName = (TccrName & ~clearMask) | value;}\\\n      static DataT Read(){return TccrName;}\\\n      static void Set(DataT value){TccrName |= value;}\\\n      static void Clear(DataT value){TccrName &= ~value;}\\\n      static void Toggle(DataT value){TccrName ^= value;}\\\n      enum{Width=sizeof(DataT)*8};\\\n    };\n\n#ifdef USE_PORTA\n\nMAKE_PORT(PORTA, DDRA, PINA, Porta, 'A')\n#endif\n#ifdef USE_PORTB\nMAKE_PORT(PORTB, DDRB, PINB, Portb, 'B')\n#endif\n#ifdef USE_PORTC\nMAKE_PORT(PORTC, DDRC, PINC, Portc, 'C')\n#endif\n#ifdef USE_PORTD\nMAKE_PORT(PORTD, DDRD, PIND, Portd, 'D')\n#endif\n#ifdef USE_PORTE\nMAKE_PORT(PORTE, DDRE, PINE, Porte, 'E')\n#endif\n#ifdef USE_PORTF\nMAKE_PORT(PORTF, DDRF, PINF, Portf, 'F')\n#endif\n#ifdef USE_PORTG\nMAKE_PORT(PORTG, DDRG, PING, Portg, 'G')\n#endif\n#ifdef USE_PORTH\nMAKE_PORT(PORTH, DDRH, PINH, Porth, 'H')\n#endif\n#ifdef USE_PORTJ\nMAKE_PORT(PORTJ, DDRJ, PINJ, Portj, 'J')\n#endif\n#ifdef USE_PORTK\nMAKE_PORT(PORTK, DDRK, PINK, Portk, 'K')\n#endif\n#ifdef USE_PORTL\nMAKE_PORT(PORTL, DDRL, PINL, Portl, 'L')\n#endif\n#ifdef USE_PORTQ\nMAKE_PORT(PORTQ, DDRQ, PINQ, Portq, 'Q')\n#endif\n#ifdef USE_PORTR\nMAKE_PORT(PORTR, DDRR, PINR, Portr, 'R')\n#endif\n\n#ifdef USE_TCCR0A\nMAKE_TCCR(TCCR0A, Tccr0a)\n#endif\n#ifdef USE_TCCR1A\nMAKE_TCCR(TCCR1A, Tccr1a)\n#endif\n#ifdef USE_TCCR2A\nMAKE_TCCR(TCCR2A, Tccr2a)\n#endif\n\n// this class represents one pin in a IO port.\n// It is fully static.\ntemplate<typename PORT, uint8_t PIN>\nclass TPin {\n        //    BOOST_STATIC_ASSERT(PIN < PORT::Width);\npublic:\n        typedef PORT Port;\n\n        enum {\n                Number = PIN\n        };\n\n        static void Set() {\n                PORT::Set(1 << PIN);\n        }\n\n        static void Set(uint8_t val) {\n                if(val)\n                        Set();\n                else Clear();\n        }\n\n        static void SetDir(uint8_t val) {\n                if(val)\n                        SetDirWrite();\n                else SetDirRead();\n        }\n\n        static void Clear() {\n                PORT::Clear(1 << PIN);\n        }\n\n        static void Toggle() {\n                PORT::Toggle(1 << PIN);\n        }\n\n        static void SetDirRead() {\n                PORT::DirClear(1 << PIN);\n        }\n\n        static void SetDirWrite() {\n                PORT::DirSet(1 << PIN);\n        }\n\n        static uint8_t IsSet() {\n                return PORT::PinRead() & (uint8_t)(1 << PIN);\n        }\n\n        static void WaiteForSet() {\n                while(IsSet() == 0) {\n                }\n        }\n\n        static void WaiteForClear() {\n                while(IsSet()) {\n                }\n        }\n}; //class TPin...\n\n// this class represents one bit in TCCR port.\n// used to set/clear TCCRx bits\n// It is fully static.\n\ntemplate<typename TCCR, uint8_t COM>\nclass TCom {\n        //    BOOST_STATIC_ASSERT(PIN < PORT::Width);\npublic:\n        typedef TCCR Tccr;\n\n        enum {\n                Com = COM\n        };\n\n        static void Set() {\n                TCCR::Set(1 << COM);\n        }\n\n        static void Clear() {\n                TCCR::Clear(1 << COM);\n        }\n\n        static void Toggle() {\n                TCCR::Toggle(1 << COM);\n        }\n}; //class TCom...\n\n//Short pin definitions\n#ifdef USE_PORTA\ntypedef TPin<Porta, 0 > Pa0;\ntypedef TPin<Porta, 1 > Pa1;\ntypedef TPin<Porta, 2 > Pa2;\ntypedef TPin<Porta, 3 > Pa3;\ntypedef TPin<Porta, 4 > Pa4;\ntypedef TPin<Porta, 5 > Pa5;\ntypedef TPin<Porta, 6 > Pa6;\ntypedef TPin<Porta, 7 > Pa7;\n#endif\n\n#ifdef USE_PORTB\ntypedef TPin<Portb, 0 > Pb0;\ntypedef TPin<Portb, 1 > Pb1;\ntypedef TPin<Portb, 2 > Pb2;\ntypedef TPin<Portb, 3 > Pb3;\ntypedef TPin<Portb, 4 > Pb4;\ntypedef TPin<Portb, 5 > Pb5;\ntypedef TPin<Portb, 6 > Pb6;\ntypedef TPin<Portb, 7 > Pb7;\n#endif\n\n#ifdef USE_PORTC\ntypedef TPin<Portc, 0 > Pc0;\ntypedef TPin<Portc, 1 > Pc1;\ntypedef TPin<Portc, 2 > Pc2;\ntypedef TPin<Portc, 3 > Pc3;\ntypedef TPin<Portc, 4 > Pc4;\ntypedef TPin<Portc, 5 > Pc5;\ntypedef TPin<Portc, 6 > Pc6;\ntypedef TPin<Portc, 7 > Pc7;\n#endif\n\n#ifdef USE_PORTD\ntypedef TPin<Portd, 0 > Pd0;\ntypedef TPin<Portd, 1 > Pd1;\ntypedef TPin<Portd, 2 > Pd2;\ntypedef TPin<Portd, 3 > Pd3;\ntypedef TPin<Portd, 4 > Pd4;\ntypedef TPin<Portd, 5 > Pd5;\ntypedef TPin<Portd, 6 > Pd6;\ntypedef TPin<Portd, 7 > Pd7;\n#endif\n\n#ifdef USE_PORTE\ntypedef TPin<Porte, 0 > Pe0;\ntypedef TPin<Porte, 1 > Pe1;\ntypedef TPin<Porte, 2 > Pe2;\ntypedef TPin<Porte, 3 > Pe3;\ntypedef TPin<Porte, 4 > Pe4;\ntypedef TPin<Porte, 5 > Pe5;\ntypedef TPin<Porte, 6 > Pe6;\ntypedef TPin<Porte, 7 > Pe7;\n#endif\n\n#ifdef USE_PORTF\ntypedef TPin<Portf, 0 > Pf0;\ntypedef TPin<Portf, 1 > Pf1;\ntypedef TPin<Portf, 2 > Pf2;\ntypedef TPin<Portf, 3 > Pf3;\ntypedef TPin<Portf, 4 > Pf4;\ntypedef TPin<Portf, 5 > Pf5;\ntypedef TPin<Portf, 6 > Pf6;\ntypedef TPin<Portf, 7 > Pf7;\n#endif\n\n#ifdef USE_PORTG\ntypedef TPin<Portg, 0 > Pg0;\ntypedef TPin<Portg, 1 > Pg1;\ntypedef TPin<Portg, 2 > Pg2;\ntypedef TPin<Portg, 3 > Pg3;\ntypedef TPin<Portg, 4 > Pg4;\ntypedef TPin<Portg, 5 > Pg5;\ntypedef TPin<Portg, 6 > Pg6;\ntypedef TPin<Portg, 7 > Pg7;\n#endif\n\n#ifdef USE_PORTH\ntypedef TPin<Porth, 0 > Ph0;\ntypedef TPin<Porth, 1 > Ph1;\ntypedef TPin<Porth, 2 > Ph2;\ntypedef TPin<Porth, 3 > Ph3;\ntypedef TPin<Porth, 4 > Ph4;\ntypedef TPin<Porth, 5 > Ph5;\ntypedef TPin<Porth, 6 > Ph6;\ntypedef TPin<Porth, 7 > Ph7;\n#endif\n\n#ifdef USE_PORTJ\ntypedef TPin<Portj, 0 > Pj0;\ntypedef TPin<Portj, 1 > Pj1;\ntypedef TPin<Portj, 2 > Pj2;\ntypedef TPin<Portj, 3 > Pj3;\ntypedef TPin<Portj, 4 > Pj4;\ntypedef TPin<Portj, 5 > Pj5;\ntypedef TPin<Portj, 6 > Pj6;\ntypedef TPin<Portj, 7 > Pj7;\n#endif\n\n#ifdef USE_PORTK\ntypedef TPin<Portk, 0 > Pk0;\ntypedef TPin<Portk, 1 > Pk1;\ntypedef TPin<Portk, 2 > Pk2;\ntypedef TPin<Portk, 3 > Pk3;\ntypedef TPin<Portk, 4 > Pk4;\ntypedef TPin<Portk, 5 > Pk5;\ntypedef TPin<Portk, 6 > Pk6;\ntypedef TPin<Portk, 7 > Pk7;\n#endif\n\n#ifdef USE_PORTL\ntypedef TPin<Portl, 0 > Pl0;\ntypedef TPin<Portl, 1 > Pl1;\ntypedef TPin<Portl, 2 > Pl2;\ntypedef TPin<Portl, 3 > Pl3;\ntypedef TPin<Portl, 4 > Pl4;\ntypedef TPin<Portl, 5 > Pl5;\ntypedef TPin<Portl, 6 > Pl6;\ntypedef TPin<Portl, 7 > Pl7;\n#endif\n\n#ifdef USE_PORTQ\ntypedef TPin<Portq, 0 > Pq0;\ntypedef TPin<Portq, 1 > Pq1;\ntypedef TPin<Portq, 2 > Pq2;\ntypedef TPin<Portq, 3 > Pq3;\ntypedef TPin<Portq, 4 > Pq4;\ntypedef TPin<Portq, 5 > Pq5;\ntypedef TPin<Portq, 6 > Pq6;\ntypedef TPin<Portq, 7 > Pq7;\n#endif\n\n#ifdef USE_PORTR\ntypedef TPin<Portr, 0 > Pr0;\ntypedef TPin<Portr, 1 > Pr1;\ntypedef TPin<Portr, 2 > Pr2;\ntypedef TPin<Portr, 3 > Pr3;\ntypedef TPin<Portr, 4 > Pr4;\ntypedef TPin<Portr, 5 > Pr5;\ntypedef TPin<Portr, 6 > Pr6;\ntypedef TPin<Portr, 7 > Pr7;\n#endif\n\n#ifdef USE_TCCR0A\ntypedef TCom<Tccr0a, COM0A1> Tc0a; //P6\ntypedef TCom<Tccr0a, COM0B1> Tc0b; //P5\n#endif\n\n#ifdef USE_TCCR1A\ntypedef TCom<Tccr1a, COM1A1> Tc1a; //P9\ntypedef TCom<Tccr1a, COM1B1> Tc1b; //P10\n#endif\n\n#ifdef USE_TCCR2A\ntypedef TCom<Tccr2a, COM2A1> Tc2a; //P11\ntypedef TCom<Tccr2a, COM2B1> Tc2b; //P3\n#endif\n\ntemplate<typename Tp_pin, typename Tc_bit>\nclass Tp_Tc {\npublic:\n\n        static void SetDir(uint8_t val) {\n                if(val)\n                        SetDirWrite();\n                else SetDirRead();\n        }\n\n        static void SetDirRead() {\n                Tp_pin::SetDirRead(); //set pin direction\n                Tc_bit::Clear(); //disconnect pin from PWM\n        }\n\n        static void SetDirWrite() {\n                Tp_pin::SetDirWrite();\n                Tc_bit::Clear();\n        }\n};\n\n/* pin definitions for cases where it's necessary to clear compare output mode bits */\n\n//typedef Tp_Tc<Pd3, Tc2b> P3;  //Arduino pin 3\n//typedef Tp_Tc<Pd5, Tc0b> P5;  //Arduino pin 5\n//typedef Tp_Tc<Pd6, Tc0a> P6;  //Arduino pin 6\n//typedef Tp_Tc<Pb1, Tc1a> P9;  //Arduino pin 9\n//typedef Tp_Tc<Pb2, Tc1b> P10;  //Arduino pin 10\n//typedef Tp_Tc<Pb3, Tc2a> P11;  //Arduino pin 11\n\n/* Arduino pin definitions  */\n#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)\n// \"Mega\" Arduino pin numbers\n\n#define P0  Pe0\n#define P1  Pe1\n#define P2  Pe4\n#define P3  Pe5\n#define P4  Pg5\n#define P5  Pe3\n#define P6  Ph3\n#define P7  Ph4\n\n#define P8  Ph5\n#define P9  Ph6\n#define P10  Pb4\n#define P11  Pb5\n#define P12  Pb6\n#define P13  Pb7\n\n#define P14  Pj1\n#define P15  Pj0\n#define P16  Ph1\n#define P17  Ph0\n#define P18  Pd3\n#define P19  Pd2\n#define P20  Pd1\n#define P21  Pd0\n\n#define P22 Pa0\n#define P23 Pa1\n#define P24 Pa2\n#define P25 Pa3\n#define P26 Pa4\n#define P27 Pa5\n#define P28 Pa6\n#define P29 Pa7\n#define P30 Pc7\n#define P31 Pc6\n#define P32 Pc5\n#define P33 Pc4\n#define P34 Pc3\n#define P35 Pc2\n#define P36 Pc1\n#define P37 Pc0\n\n#define P38 Pd7\n#define P39 Pg2\n#define P40 Pg1\n#define P41 Pg0\n#define P42 Pl7\n#define P43 Pl6\n#define P44 Pl5\n#define P45 Pl4\n#define P46 Pl3\n#define P47 Pl2\n#define P48 Pl1\n#define P49 Pl0\n#define P50 Pb3\n#define P51 Pb2\n#define P52 Pb1\n#define P53 Pb0\n\n#ifdef BOARD_MEGA_ADK // These pins are not broken out on the Arduino ADK\n#define P54 Pe6 // INT on Arduino ADK\n#define P55 Pj2 // MAX_RESET on Arduino ADK\n#endif\n\n// \"Mega\" pin numbers\n\n#elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)\n// \"Classic\" Arduino pin numbers\n\n#define P0  Pd0\n#define P1  Pd1\n#define P2  Pd2\n#define P3  Pd3\n#define P4  Pd4\n#define P5  Pd5\n#define P6  Pd6\n#define P7  Pd7\n\n#define P8  Pb0\n#define P9  Pb1\n#define P10  Pb2\n#define P11  Pb3\n#define P12  Pb4\n#define P13  Pb5\n\n#define P14  Pc0\n#define P15  Pc1\n#define P16  Pc2\n#define P17  Pc3\n#define P18  Pc4\n#define P19  Pc5\n\n// \"Classic\" Arduino pin numbers\n\n#elif defined(CORE_TEENSY) && defined(__AVR_ATmega32U4__)\n// Teensy 2.0 pin numbers\n// http://www.pjrc.com/teensy/pinout.html\n#define P0  Pb0\n#define P1  Pb1\n#define P2  Pb2\n#define P3  Pb3\n#define P4  Pb7\n#define P5  Pd0\n#define P6  Pd1\n#define P7  Pd2\n#define P8  Pd3\n#define P9  Pc6\n#define P10 Pc7\n#define P11 Pd6\n#define P12 Pd7\n#define P13 Pb4\n#define P14 Pb5\n#define P15 Pb6\n#define P16 Pf7\n#define P17 Pf6\n#define P18 Pf5\n#define P19 Pf4\n#define P20 Pf1\n#define P21 Pf0\n#define P22 Pd4\n#define P23 Pd5\n#define P24 Pe6\n// Teensy 2.0\n\n#elif defined(__AVR_ATmega32U4__)\n// Arduino Leonardo pin numbers\n\n#define P0  Pd2 // D0 - PD2\n#define P1  Pd3 // D1 - PD3\n#define P2  Pd1 // D2 - PD1\n#define P3  Pd0 // D3 - PD0\n#define P4  Pd4 // D4 - PD4\n#define P5  Pc6 // D5 - PC6\n#define P6  Pd7 // D6 - PD7\n#define P7  Pe6 // D7 - PE6\n\n#define P8  Pb4 // D8 - PB4\n#define P9  Pb5 // D9 - PB5\n#define P10 Pb6 // D10 - PB6\n#define P11 Pb7 // D11 - PB7\n#define P12 Pd6 // D12 - PD6\n#define P13 Pc7 // D13 - PC7\n\n#define P14 Pb3 // D14 - MISO - PB3\n#define P15 Pb1 // D15 - SCK - PB1\n#define P16 Pb2 // D16 - MOSI - PB2\n#define P17 Pb0 // D17 - SS - PB0\n\n#define P18 Pf7 // D18 - A0 - PF7\n#define P19 Pf6 // D19 - A1 - PF6\n#define P20 Pf5 // D20 - A2 - PF5\n#define P21 Pf4 // D21 - A3 - PF4\n#define P22 Pf1 // D22 - A4 - PF1\n#define P23 Pf0 // D23 - A5 - PF0\n\n#define P24 Pd4 // D24 / D4 - A6 - PD4\n#define P25 Pd7 // D25 / D6 - A7 - PD7\n#define P26 Pb4 // D26 / D8 - A8 - PB4\n#define P27 Pb5 // D27 / D9 - A9 - PB5\n#define P28 Pb6 // D28 / D10 - A10 - PB6\n#define P29 Pd6 // D29 / D12 - A11 - PD6\n\n// Arduino Leonardo pin numbers\n\n#elif defined(CORE_TEENSY) && (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__))\n// Teensy++ 1.0 and 2.0 pin numbers\n// http://www.pjrc.com/teensy/pinout.html\n#define P0  Pd0\n#define P1  Pd1\n#define P2  Pd2\n#define P3  Pd3\n#define P4  Pd4\n#define P5  Pd5\n#define P6  Pd6\n#define P7  Pd7\n#define P8  Pe0\n#define P9  Pe1\n#define P10 Pc0\n#define P11 Pc1\n#define P12 Pc2\n#define P13 Pc3\n#define P14 Pc4\n#define P15 Pc5\n#define P16 Pc6\n#define P17 Pc7\n#define P18 Pe6\n#define P19 Pe7\n#define P20 Pb0\n#define P21 Pb1\n#define P22 Pb2\n#define P23 Pb3\n#define P24 Pb4\n#define P25 Pb5\n#define P26 Pb6\n#define P27 Pb7\n#define P28 Pa0\n#define P29 Pa1\n#define P30 Pa2\n#define P31 Pa3\n#define P32 Pa4\n#define P33 Pa5\n#define P34 Pa6\n#define P35 Pa7\n#define P36 Pe4\n#define P37 Pe5\n#define P38 Pf0\n#define P39 Pf1\n#define P40 Pf2\n#define P41 Pf3\n#define P42 Pf4\n#define P43 Pf5\n#define P44 Pf6\n#define P45 Pf7\n// Teensy++ 1.0 and 2.0\n\n#elif defined(ARDUINO_AVR_BALANDUINO) && (defined(__AVR_ATmega644__) || defined(__AVR_ATmega1284P__))\n// Balanduino pin numbers\n// http://balanduino.net/\n#define P0  Pd0 /* 0  - PD0 */\n#define P1  Pd1 /* 1  - PD1 */\n\n#if BALANDUINO_REVISION < 13\n  #define P2  Pb2 /* 2  - PB2 */\n  #define P3  Pd6 /* 3  - PD6 */\n  #define P4  Pd7 /* 4  - PD7 */\n  #define P5  Pb3 /* 5  - PB3 */\n#else\n  #define P2  Pd2 /* 2  - PD2 */\n  #define P3  Pd3 /* 3  - PD3 */\n  #define P4  Pd6 /* 4  - PD6 */\n  #define P5  Pd7 /* 5  - PD7 */\n#endif\n\n#define P6  Pb4 /* 6  - PB4 */\n#define P7  Pa0 /* 7  - PA0 */\n#define P8  Pa1 /* 8  - PA1 */\n#define P9  Pa2 /* 9  - PA2 */\n#define P10 Pa3 /* 10 - PA3 */\n#define P11 Pa4 /* 11 - PA4 */\n#define P12 Pa5 /* 12 - PA5 */\n#define P13 Pc1 /* 13 - PC1 */\n#define P14 Pc0 /* 14 - PC0 */\n\n#if BALANDUINO_REVISION < 13\n  #define P15 Pd2 /* 15 - PD2 */\n  #define P16 Pd3 /* 16 - PD3 */\n#else\n  #define P15 Pb2 /* 15 - PB2 */\n  #define P16 Pb3 /* 16 - PB2 */\n#endif\n\n#define P17 Pd4 /* 17 - PD4 */\n#define P18 Pd5 /* 18 - PD5 */\n#define P19 Pc2 /* 19 - PC2 */\n#define P20 Pc3 /* 20 - PC3 */\n#define P21 Pc4 /* 21 - PC4 */\n#define P22 Pc5 /* 22 - PC5 */\n#define P23 Pc6 /* 23 - PC6 */\n#define P24 Pc7 /* 24 - PC7 */\n#define P25 Pb0 /* 25 - PB0 */\n#define P26 Pb1 /* 26 - PB1 */\n#define P27 Pb5 /* 27 - PB5 */\n#define P28 Pb6 /* 28 - PB6 */\n#define P29 Pb7 /* 29 - PB7 */\n#define P30 Pa6 /* 30 - PA6 */\n#define P31 Pa7 /* 31 - PA7 */\n// Balanduino\n\n#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)\n// Sanguino pin numbers\n// Homepage: http://sanguino.cc/hardware\n// Hardware add-on: https://github.com/Lauszus/Sanguino\n#define P0  Pb0\n#define P1  Pb1\n#define P2  Pb2\n#define P3  Pb3\n#define P4  Pb4\n#define P5  Pb5\n#define P6  Pb6\n#define P7  Pb7\n#define P8  Pd0\n#define P9  Pd1\n#define P10 Pd2\n#define P11 Pd3\n#define P12 Pd4\n#define P13 Pd5\n#define P14 Pd6\n#define P15 Pd7\n#define P16 Pc0\n#define P17 Pc1\n#define P18 Pc2\n#define P19 Pc3\n#define P20 Pc4\n#define P21 Pc5\n#define P22 Pc6\n#define P23 Pc7\n#define P24 Pa0\n#define P25 Pa1\n#define P26 Pa2\n#define P27 Pa3\n#define P28 Pa4\n#define P29 Pa5\n#define P30 Pa6\n#define P31 Pa7\n// Sanguino\n\n#else\n#error \"Please define board in avrpins.h\"\n\n#endif // Arduino pin definitions\n\n#elif defined(__arm__)\n\n// pointers are 32 bits on ARM\n#define pgm_read_pointer(p) pgm_read_dword(p)\n\n#if defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__))\n\n#include \"core_pins.h\"\n#include \"avr_emulation.h\"\n\n#define GPIO_BITBAND_ADDR(reg, bit) (((uint32_t)&(reg) - 0x40000000) * 32 + (bit) * 4 + 0x42000000)\n#define GPIO_BITBAND_PTR(reg, bit) ((uint8_t *)GPIO_BITBAND_ADDR((reg), (bit)))\n\n#define MAKE_PIN(className, baseReg, pinNum, configReg) \\\nclass className { \\\npublic: \\\n  static void Set() { \\\n    *GPIO_BITBAND_PTR(baseReg, pinNum) = 1; \\\n  } \\\n  static void Clear() { \\\n    *GPIO_BITBAND_PTR(baseReg, pinNum) = 0; \\\n  } \\\n  static void SetDirRead() { \\\n    configReg = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); \\\n    *(GPIO_BITBAND_PTR(baseReg, pinNum) + 640) = 0; \\\n  } \\\n  static void SetDirWrite() { \\\n    configReg = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1); \\\n    *(GPIO_BITBAND_PTR(baseReg, pinNum) + 640) = 1; \\\n  } \\\n  static uint8_t IsSet() { \\\n    return *(GPIO_BITBAND_PTR(baseReg, pinNum) + 512); \\\n  } \\\n};\n\nMAKE_PIN(P0, CORE_PIN0_PORTREG, CORE_PIN0_BIT, CORE_PIN0_CONFIG);\nMAKE_PIN(P1, CORE_PIN1_PORTREG, CORE_PIN1_BIT, CORE_PIN1_CONFIG);\nMAKE_PIN(P2, CORE_PIN2_PORTREG, CORE_PIN2_BIT, CORE_PIN2_CONFIG);\nMAKE_PIN(P3, CORE_PIN3_PORTREG, CORE_PIN3_BIT, CORE_PIN3_CONFIG);\nMAKE_PIN(P4, CORE_PIN4_PORTREG, CORE_PIN4_BIT, CORE_PIN4_CONFIG);\nMAKE_PIN(P5, CORE_PIN5_PORTREG, CORE_PIN5_BIT, CORE_PIN5_CONFIG);\nMAKE_PIN(P6, CORE_PIN6_PORTREG, CORE_PIN6_BIT, CORE_PIN6_CONFIG);\nMAKE_PIN(P7, CORE_PIN7_PORTREG, CORE_PIN7_BIT, CORE_PIN7_CONFIG);\nMAKE_PIN(P8, CORE_PIN8_PORTREG, CORE_PIN8_BIT, CORE_PIN8_CONFIG);\nMAKE_PIN(P9, CORE_PIN9_PORTREG, CORE_PIN9_BIT, CORE_PIN9_CONFIG);\nMAKE_PIN(P10, CORE_PIN10_PORTREG, CORE_PIN10_BIT, CORE_PIN10_CONFIG);\nMAKE_PIN(P11, CORE_PIN11_PORTREG, CORE_PIN11_BIT, CORE_PIN11_CONFIG);\nMAKE_PIN(P12, CORE_PIN12_PORTREG, CORE_PIN12_BIT, CORE_PIN12_CONFIG);\nMAKE_PIN(P13, CORE_PIN13_PORTREG, CORE_PIN13_BIT, CORE_PIN13_CONFIG);\nMAKE_PIN(P14, CORE_PIN14_PORTREG, CORE_PIN14_BIT, CORE_PIN14_CONFIG);\nMAKE_PIN(P15, CORE_PIN15_PORTREG, CORE_PIN15_BIT, CORE_PIN15_CONFIG);\nMAKE_PIN(P16, CORE_PIN16_PORTREG, CORE_PIN16_BIT, CORE_PIN16_CONFIG);\nMAKE_PIN(P17, CORE_PIN17_PORTREG, CORE_PIN17_BIT, CORE_PIN17_CONFIG);\nMAKE_PIN(P18, CORE_PIN18_PORTREG, CORE_PIN18_BIT, CORE_PIN18_CONFIG);\nMAKE_PIN(P19, CORE_PIN19_PORTREG, CORE_PIN19_BIT, CORE_PIN19_CONFIG);\nMAKE_PIN(P20, CORE_PIN20_PORTREG, CORE_PIN20_BIT, CORE_PIN20_CONFIG);\nMAKE_PIN(P21, CORE_PIN21_PORTREG, CORE_PIN21_BIT, CORE_PIN21_CONFIG);\nMAKE_PIN(P22, CORE_PIN22_PORTREG, CORE_PIN22_BIT, CORE_PIN22_CONFIG);\nMAKE_PIN(P23, CORE_PIN23_PORTREG, CORE_PIN23_BIT, CORE_PIN23_CONFIG);\nMAKE_PIN(P24, CORE_PIN24_PORTREG, CORE_PIN24_BIT, CORE_PIN24_CONFIG);\nMAKE_PIN(P25, CORE_PIN25_PORTREG, CORE_PIN25_BIT, CORE_PIN25_CONFIG);\nMAKE_PIN(P26, CORE_PIN26_PORTREG, CORE_PIN26_BIT, CORE_PIN26_CONFIG);\nMAKE_PIN(P27, CORE_PIN27_PORTREG, CORE_PIN27_BIT, CORE_PIN27_CONFIG);\nMAKE_PIN(P28, CORE_PIN28_PORTREG, CORE_PIN28_BIT, CORE_PIN28_CONFIG);\nMAKE_PIN(P29, CORE_PIN29_PORTREG, CORE_PIN29_BIT, CORE_PIN29_CONFIG);\nMAKE_PIN(P30, CORE_PIN30_PORTREG, CORE_PIN30_BIT, CORE_PIN30_CONFIG);\nMAKE_PIN(P31, CORE_PIN31_PORTREG, CORE_PIN31_BIT, CORE_PIN31_CONFIG);\nMAKE_PIN(P32, CORE_PIN32_PORTREG, CORE_PIN32_BIT, CORE_PIN32_CONFIG);\nMAKE_PIN(P33, CORE_PIN33_PORTREG, CORE_PIN33_BIT, CORE_PIN33_CONFIG);\n\n#undef MAKE_PIN\n\n#elif defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)\n\n// SetDirRead:\n//   Disable interrupts\n//   Disable the pull up resistor\n//   Set to INPUT\n//   Enable PIO\n\n// SetDirWrite:\n//   Disable interrupts\n//   Disable the pull up resistor\n//   Set to OUTPUT\n//   Enable PIO\n\n#define MAKE_PIN(className, pio, pinMask) \\\nclass className { \\\npublic: \\\n  static void Set() { \\\n    pio->PIO_SODR = pinMask; \\\n  } \\\n  static void Clear() { \\\n    pio->PIO_CODR = pinMask; \\\n  } \\\n  static void SetDirRead() { \\\n    pio->PIO_IDR = pinMask ; \\\n    pio->PIO_PUDR = pinMask; \\\n    pio->PIO_ODR = pinMask; \\\n    pio->PIO_PER = pinMask; \\\n  } \\\n  static void SetDirWrite() { \\\n    pio->PIO_IDR = pinMask ; \\\n    pio->PIO_PUDR = pinMask; \\\n    pio->PIO_OER = pinMask; \\\n    pio->PIO_PER = pinMask; \\\n  } \\\n  static uint8_t IsSet() { \\\n    return pio->PIO_PDSR & pinMask; \\\n  } \\\n};\n\n// See: http://arduino.cc/en/Hacking/PinMappingSAM3X and variant.cpp\n\nMAKE_PIN(P0, PIOA, PIO_PA8);\nMAKE_PIN(P1, PIOA, PIO_PA9);\nMAKE_PIN(P2, PIOB, PIO_PB25);\nMAKE_PIN(P3, PIOC, PIO_PC28);\nMAKE_PIN(P4, PIOC, PIO_PC26);\nMAKE_PIN(P5, PIOC, PIO_PC25);\nMAKE_PIN(P6, PIOC, PIO_PC24);\nMAKE_PIN(P7, PIOC, PIO_PC23);\nMAKE_PIN(P8, PIOC, PIO_PC22);\nMAKE_PIN(P9, PIOC, PIO_PC21);\nMAKE_PIN(P10, PIOC, PIO_PC29);\nMAKE_PIN(P11, PIOD, PIO_PD7);\nMAKE_PIN(P12, PIOD, PIO_PD8);\nMAKE_PIN(P13, PIOB, PIO_PB27);\nMAKE_PIN(P14, PIOD, PIO_PD4);\nMAKE_PIN(P15, PIOD, PIO_PD5);\nMAKE_PIN(P16, PIOA, PIO_PA13);\nMAKE_PIN(P17, PIOA, PIO_PA12);\nMAKE_PIN(P18, PIOA, PIO_PA11);\nMAKE_PIN(P19, PIOA, PIO_PA10);\nMAKE_PIN(P20, PIOB, PIO_PB12);\nMAKE_PIN(P21, PIOB, PIO_PB13);\nMAKE_PIN(P22, PIOB, PIO_PB26);\nMAKE_PIN(P23, PIOA, PIO_PA14);\nMAKE_PIN(P24, PIOA, PIO_PA15);\nMAKE_PIN(P25, PIOD, PIO_PD0);\nMAKE_PIN(P26, PIOD, PIO_PD1);\nMAKE_PIN(P27, PIOD, PIO_PD2);\nMAKE_PIN(P28, PIOD, PIO_PD3);\nMAKE_PIN(P29, PIOD, PIO_PD6);\nMAKE_PIN(P30, PIOD, PIO_PD9);\nMAKE_PIN(P31, PIOA, PIO_PA7);\nMAKE_PIN(P32, PIOD, PIO_PD10);\nMAKE_PIN(P33, PIOC, PIO_PC1);\nMAKE_PIN(P34, PIOC, PIO_PC2);\nMAKE_PIN(P35, PIOC, PIO_PC3);\nMAKE_PIN(P36, PIOC, PIO_PC4);\nMAKE_PIN(P37, PIOC, PIO_PC5);\nMAKE_PIN(P38, PIOC, PIO_PC6);\nMAKE_PIN(P39, PIOC, PIO_PC7);\nMAKE_PIN(P40, PIOC, PIO_PC8);\nMAKE_PIN(P41, PIOC, PIO_PC9);\nMAKE_PIN(P42, PIOA, PIO_PA19);\nMAKE_PIN(P43, PIOA, PIO_PA20);\nMAKE_PIN(P44, PIOC, PIO_PC19);\nMAKE_PIN(P45, PIOC, PIO_PC18);\nMAKE_PIN(P46, PIOC, PIO_PC17);\nMAKE_PIN(P47, PIOC, PIO_PC16);\nMAKE_PIN(P48, PIOC, PIO_PC15);\nMAKE_PIN(P49, PIOC, PIO_PC14);\nMAKE_PIN(P50, PIOC, PIO_PC13);\nMAKE_PIN(P51, PIOC, PIO_PC12);\nMAKE_PIN(P52, PIOB, PIO_PB21);\nMAKE_PIN(P53, PIOB, PIO_PB14);\nMAKE_PIN(P54, PIOA, PIO_PA16);\nMAKE_PIN(P55, PIOA, PIO_PA24);\nMAKE_PIN(P56, PIOA, PIO_PA23);\nMAKE_PIN(P57, PIOA, PIO_PA22);\nMAKE_PIN(P58, PIOA, PIO_PA6);\nMAKE_PIN(P59, PIOA, PIO_PA4);\nMAKE_PIN(P60, PIOA, PIO_PA3);\nMAKE_PIN(P61, PIOA, PIO_PA2);\nMAKE_PIN(P62, PIOB, PIO_PB17);\nMAKE_PIN(P63, PIOB, PIO_PB18);\nMAKE_PIN(P64, PIOB, PIO_PB19);\nMAKE_PIN(P65, PIOB, PIO_PB20);\nMAKE_PIN(P66, PIOB, PIO_PB15);\nMAKE_PIN(P67, PIOB, PIO_PB16);\nMAKE_PIN(P68, PIOA, PIO_PA1);\nMAKE_PIN(P69, PIOA, PIO_PA0);\nMAKE_PIN(P70, PIOA, PIO_PA17);\nMAKE_PIN(P71, PIOA, PIO_PA18);\nMAKE_PIN(P72, PIOC, PIO_PC30);\nMAKE_PIN(P73, PIOA, PIO_PA21);\nMAKE_PIN(P74, PIOA, PIO_PA25); // MISO\nMAKE_PIN(P75, PIOA, PIO_PA26); // MOSI\nMAKE_PIN(P76, PIOA, PIO_PA27); // CLK\nMAKE_PIN(P77, PIOA, PIO_PA28);\nMAKE_PIN(P78, PIOB, PIO_PB23); // Unconnected\n\n#undef MAKE_PIN\n\n#elif defined(RBL_NRF51822)\n\n#define MAKE_PIN(className, pin) \\\nclass className { \\\npublic: \\\n    static void Set() { \\\n        nrf_gpio_pin_set(pin); \\\n    } \\\n    static void Clear() { \\\n        nrf_gpio_pin_clear(pin); \\\n    } \\\n    static void SetDirRead() { \\\n        nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL); \\\n    } \\\n    static void SetDirWrite() { \\\n        nrf_gpio_cfg_output(pin); \\\n    } \\\n    static uint8_t IsSet() { \\\n        return (uint8_t)nrf_gpio_pin_read(pin); \\\n    } \\\n};\n\n// See: pin_transform.c in RBL nRF51822 SDK\nMAKE_PIN(P0, Pin_nRF51822_to_Arduino(D0));\nMAKE_PIN(P1, Pin_nRF51822_to_Arduino(D1));\nMAKE_PIN(P2, Pin_nRF51822_to_Arduino(D2));\nMAKE_PIN(P3, Pin_nRF51822_to_Arduino(D3));\nMAKE_PIN(P4, Pin_nRF51822_to_Arduino(D4));\nMAKE_PIN(P5, Pin_nRF51822_to_Arduino(D5));\nMAKE_PIN(P6, Pin_nRF51822_to_Arduino(D6));\nMAKE_PIN(P7, Pin_nRF51822_to_Arduino(D7));\nMAKE_PIN(P8, Pin_nRF51822_to_Arduino(D8));\nMAKE_PIN(P9, Pin_nRF51822_to_Arduino(D9)); // INT\nMAKE_PIN(P10, Pin_nRF51822_to_Arduino(D10)); // SS\nMAKE_PIN(P11, Pin_nRF51822_to_Arduino(D11));\nMAKE_PIN(P12, Pin_nRF51822_to_Arduino(D12));\nMAKE_PIN(P13, Pin_nRF51822_to_Arduino(D13));\nMAKE_PIN(P14, Pin_nRF51822_to_Arduino(D14));\nMAKE_PIN(P15, Pin_nRF51822_to_Arduino(D15));\nMAKE_PIN(P17, Pin_nRF51822_to_Arduino(D17)); // MISO\nMAKE_PIN(P18, Pin_nRF51822_to_Arduino(D18)); // MOSI\nMAKE_PIN(P16, Pin_nRF51822_to_Arduino(D16)); // CLK\nMAKE_PIN(P19, Pin_nRF51822_to_Arduino(D19));\nMAKE_PIN(P20, Pin_nRF51822_to_Arduino(D20));\nMAKE_PIN(P21, Pin_nRF51822_to_Arduino(D21));\nMAKE_PIN(P22, Pin_nRF51822_to_Arduino(D22));\nMAKE_PIN(P23, Pin_nRF51822_to_Arduino(D23));\nMAKE_PIN(P24, Pin_nRF51822_to_Arduino(D24));\n\n#undef MAKE_PIN\n\n#else\n#error \"Please define board in avrpins.h\"\n\n#endif\n\n#elif defined(__ARDUINO_X86__) // Intel Galileo, Intel Galileo 2 and Intel Edison\n\n#include <avr/pgmspace.h>\n\n// Pointers are 32 bits on x86\n#define pgm_read_pointer(p) pgm_read_dword(p)\n\n#if PLATFORM_ID == 0xE1 // Edison platform id\n#define pinToFastPin(pin) 1 // As far as I can tell all pins can be used as fast pins\n#endif\n\n// Pin 2 and 3 on the Intel Galileo supports a higher rate,\n// so it is recommended to use one of these as the SS pin.\n\n#define MAKE_PIN(className, pin) \\\nclass className { \\\npublic: \\\n  static void Set() { \\\n    fastDigitalWrite(pin, HIGH); \\\n  } \\\n  static void Clear() { \\\n    fastDigitalWrite(pin, LOW); \\\n  } \\\n  static void SetDirRead() { \\\n    if (pinToFastPin(pin)) \\\n      pinMode(pin, INPUT_FAST); \\\n    else \\\n      pinMode(pin, INPUT); \\\n  } \\\n  static void SetDirWrite() { \\\n    if (pinToFastPin(pin)) \\\n      pinMode(pin, OUTPUT_FAST); \\\n    else \\\n      pinMode(pin, OUTPUT); \\\n  } \\\n  static uint8_t IsSet() { \\\n    return fastDigitalRead(pin); \\\n  } \\\n};\n\nMAKE_PIN(P0, 0);\nMAKE_PIN(P1, 1);\nMAKE_PIN(P2, 2);\nMAKE_PIN(P3, 3);\nMAKE_PIN(P4, 4);\nMAKE_PIN(P5, 5);\nMAKE_PIN(P6, 6);\nMAKE_PIN(P7, 7);\nMAKE_PIN(P8, 8);\nMAKE_PIN(P9, 9);\nMAKE_PIN(P10, 10);\nMAKE_PIN(P11, 11);\nMAKE_PIN(P12, 12);\nMAKE_PIN(P13, 13);\nMAKE_PIN(P14, 14); // A0\nMAKE_PIN(P15, 15); // A1\nMAKE_PIN(P16, 16); // A2\nMAKE_PIN(P17, 17); // A3\nMAKE_PIN(P18, 18); // A4\nMAKE_PIN(P19, 19); // A5\n\n#undef MAKE_PIN\n\n#elif defined(__MIPSEL__)\n// MIPSEL (MIPS architecture using a little endian byte order)\n\n// MIPS size_t = 4\n#define pgm_read_pointer(p) pgm_read_dword(p)\n\n#define MAKE_PIN(className, pin) \\\nclass className { \\\npublic: \\\n  static void Set() { \\\n    digitalWrite(pin, HIGH);\\\n  } \\\n  static void Clear() { \\\n    digitalWrite(pin, LOW); \\\n  } \\\n  static void SetDirRead() { \\\n    pinMode(pin, INPUT); \\\n  } \\\n  static void SetDirWrite() { \\\n    pinMode(pin, OUTPUT); \\\n  } \\\n  static uint8_t IsSet() { \\\n    return digitalRead(pin); \\\n  } \\\n};\n\n// 0 .. 13 - Digital pins\nMAKE_PIN(P0, 0); // RX\nMAKE_PIN(P1, 1); // TX\nMAKE_PIN(P2, 2); //\nMAKE_PIN(P3, 3); //\nMAKE_PIN(P4, 4); //\nMAKE_PIN(P5, 5); //\nMAKE_PIN(P6, 6); //\nMAKE_PIN(P7, 7); //\nMAKE_PIN(P8, 8); //\nMAKE_PIN(P9, 9); //\nMAKE_PIN(P10, 10); //\nMAKE_PIN(P11, 11); //\nMAKE_PIN(P12, 12); //\nMAKE_PIN(P13, 13); //\n\n#undef MAKE_PIN\n\n#else\n#error \"Please define board in avrpins.h\"\n\n#endif\n\n#endif //_avrpins_h_\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/cdc_XR21B1411.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#include \"cdc_XR21B1411.h\"\n\nXR21B1411::XR21B1411(USB *p, CDCAsyncOper *pasync) :\nACM(p, pasync) {\n        // Is this needed??\n        _enhanced_status = enhanced_features(); // Set up features\n}\n\nuint8_t XR21B1411::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);\n\n        uint8_t buf[constBufSize];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        uint8_t num_of_conf; // number of configurations\n\n        AddressPool &addrPool = pUsb->GetAddressPool();\n\n        USBTRACE(\"XR Init\\r\\n\");\n\n        if(bAddress)\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        if(!p->epinfo) {\n                USBTRACE(\"epinfo\\r\\n\");\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf);\n\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from the device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n                USBTRACE2(\"setAddr:\", rcode);\n                return rcode;\n        }\n\n        USBTRACE2(\"Addr:\", bAddress);\n\n        p->lowspeed = false;\n\n        p = addrPool.GetUsbDevicePtr(bAddress);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        num_of_conf = udd->bNumConfigurations;\n\n        if((((udd->idVendor != 0x2890U) || (udd->idProduct != 0x0201U)) && ((udd->idVendor != 0x04e2U) || (udd->idProduct != 0x1411U))))\n                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);\n\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        USBTRACE2(\"NC:\", num_of_conf);\n\n        for(uint8_t i = 0; i < num_of_conf; i++) {\n                ConfigDescParser< USB_CLASS_COM_AND_CDC_CTRL,\n                        CDC_SUBCLASS_ACM,\n                        CDC_PROTOCOL_ITU_T_V_250,\n                        CP_MASK_COMPARE_CLASS |\n                        CP_MASK_COMPARE_SUBCLASS |\n                        CP_MASK_COMPARE_PROTOCOL > CdcControlParser(this);\n\n                ConfigDescParser<USB_CLASS_CDC_DATA, 0, 0,\n                        CP_MASK_COMPARE_CLASS> CdcDataParser(this);\n\n                rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcControlParser);\n\n                if(rcode)\n                        goto FailGetConfDescr;\n\n                rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcDataParser);\n\n                if(rcode)\n                        goto FailGetConfDescr;\n\n                if(bNumEP > 1)\n                        break;\n        } // for\n\n        if(bNumEP < 4)\n                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);\n\n        USBTRACE2(\"Conf:\", bConfNum);\n\n        // Set Configuration Value\n        rcode = pUsb->setConf(bAddress, 0, bConfNum);\n\n        if(rcode)\n                goto FailSetConfDescr;\n\n        // Set up features status\n        _enhanced_status = enhanced_features();\n        half_duplex(false);\n        autoflowRTS(false);\n        autoflowDSR(false);\n        autoflowXON(false);\n        wide(false); // Always false, because this is only available in custom mode.\n\n        rcode = pAsync->OnInit(this);\n\n        if(rcode)\n                goto FailOnInit;\n\n        USBTRACE(\"XR configured\\r\\n\");\n\n        ready = true;\n\n        //bPollEnable = true;\n\n        //USBTRACE(\"Poll enabled\\r\\n\");\n        return 0;\n\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr();\n        goto Fail;\n#endif\n\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailGetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetConfDescr();\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n        goto Fail;\n#endif\n\nFailOnInit:\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"OnInit:\");\n#endif\n\n#ifdef DEBUG_USB_HOST\nFail:\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/cdc_XR21B1411.h",
    "content": "/* Copyright (C) 2015 Andrew J. Kroll\n   and\n   Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(__CDC_XR21B1411_H__)\n#define __CDC_XR21B1411_H__\n\n#include \"cdcacm.h\"\n\n#define XR_REG_CUSTOM_DRIVER                    (0x020DU) // DRIVER SELECT\n#define XR_REG_CUSTOM_DRIVER_ACTIVE             (0x0001U) // 0: CDC 1: CUSTOM\n\n#define XR_REG_ACM_FLOW_CTL                     (0x0216U) // FLOW CONTROL REGISTER CDCACM MODE\n#define XR_REG_FLOW_CTL                         (0x0C06U) // FLOW CONTROL REGISTER CUSTOM MODE\n#define XR_REG_FLOW_CTL_HALF_DPLX               (0x0008U) // 0:FULL DUPLEX 1:HALF DUPLEX\n#define XR_REG_FLOW_CTL_MODE_MASK               (0x0007U) // MODE BITMASK\n#define XR_REG_FLOW_CTL_NONE                    (0x0000U) // NO FLOW CONTROL\n#define XR_REG_FLOW_CTL_HW                      (0x0001U) // HARDWARE FLOW CONTROL\n#define XR_REG_FLOW_CTL_SW                      (0x0002U) // SOFTWARE FLOW CONTROL\n#define XR_REG_FLOW_CTL_MMMRX                   (0x0003U) // MULTIDROP RX UPON ADDRESS MATCH\n#define XR_REG_FLOW_CTL_MMMRXTX                 (0x0004U) // MULTIDROP RX/TX UPON ADDRESS MATCH\n\n#define XR_REG_ACM_GPIO_MODE                    (0x0217U) // GPIO MODE REGISTER IN CDCACM MODE\n#define XR_REG_GPIO_MODE                        (0x0C0CU) // GPIO MODE REGISTER IN CUSTOM MODE\n#define XR_REG_GPIO_MODE_GPIO                   (0x0000U) // ALL GPIO PINS ACM PROGRAMMABLE\n#define XR_REG_GPIO_MODE_FC_RTSCTS              (0x0001U) // AUTO RTSCTS HW FC (GPIO 4/5)\n#define XR_REG_GPIO_MODE_FC_DTRDSR              (0x0002U) // AUTO DTRDSR HW FC (GPIO 2/3)\n#define XR_REG_GPIO_MODE_ATE                    (0x0003U) // AUTO TRANSCEIVER ENABLE DURING TX (GPIO 5)\n#define XR_REG_GPIO_MODE_ATE_ADDRESS            (0x0004U) // AUTO TRANSCEIVER ENABLE ON ADDRESS MATCH (GPIO 5)\n\n#define XR_REG_ACM_GPIO_DIR                     (0x0218U) // GPIO DIRECTION REGISTER CDCACM MODE, 0:IN 1:OUT\n#define XR_REG_GPIO_DIR                         (0x0C0DU) // GPIO DIRECTION REGISTER CUSTOM MODE, 0:IN 1:OUT\n\n#define XR_REG_ACM_GPIO_INT                     (0x0219U) // GPIO PIN CHANGE INTERRUPT ENABLE CDCACM MODE, 0: ENABLED 1: DISABLED\n#define XR_REG_GPIO_INT                         (0x0C11U) // GPIO PIN CHANGE INTERRUPT ENABLE CUSTOM MODE, 0: ENABLED 1: DISABLED\n#define XR_REG_GPIO_MASK                        (0x001FU) // GPIO REGISTERS BITMASK\n\n#define XR_REG_UART_ENABLE                      (0x0C00U) // UART I/O ENABLE REGISTER\n#define XR_REG_UART_ENABLE_RX                   (0x0002U) // 0:DISABLED 1:ENABLED\n#define XR_REG_UART_ENABLE_TX                   (0x0001U) // 0:DISABLED 1:ENABLED\n\n#define XR_REG_ERROR_STATUS                     (0x0C09U) // ERROR STATUS REGISTER\n#define XR_REG_ERROR_STATUS_MASK                (0x00F8U) // ERROR STATUS BITMASK\n#define XR_REG_ERROR_STATUS_ERROR               (0x0078U) // ERROR STATUS ERROR BITMASK\n#define XR_REG_ERROR_STATUS_BREAK               (0x0008U) // BREAK ERROR HAS BEEN DETECTED\n#define XR_REG_ERROR_STATUS_FRAME               (0x0010U) // FRAMING ERROR HAS BEEN DETECTED\n#define XR_REG_ERROR_STATUS_PARITY              (0x0020U) // PARITY ERROR HAS BEEN DETECTED\n#define XR_REG_ERROR_STATUS_OVERRUN             (0x0040U) // RX OVERRUN ERROR HAS BEEN DETECTED\n#define XR_REG_ERROR_STATUS_BREAK_STATUS        (0x0080U) // BREAK CONDITION IS CURRENTLY BEING DETECTED\n\n#define XR_REG_TX_BREAK                         (0x0C0AU) // TRANSMIT BREAK. 0X0001-0XFFE TIME IN MS, 0X0000 STOP, 0X0FFF BREAK ON\n\n#define XR_REG_XCVR_EN_DELAY                    (0x0C0BU) // TURN-ARROUND DELAY IN BIT-TIMES 0X0000-0X000F\n\n#define XR_REG_GPIO_SET                         (0x0C0EU) // 1:SET GPIO PIN\n\n#define XR_REG_GPIO_CLR                         (0x0C0FU) // 1:CLEAR GPIO PIN\n\n#define XR_REG_GPIO_STATUS                      (0x0C10U) // READ GPIO PINS\n\n#define XR_REG_CUSTOMISED_INT                   (0x0C12U) // 0:STANDARD 1:CUSTOM SEE DATA SHEET\n\n#define XR_REG_PIN_PULLUP_ENABLE                (0x0C14U) // 0:DISABLE 1:ENABLE, BITS 0-5:GPIO, 6:RX 7:TX\n\n#define XR_REG_PIN_PULLDOWN_ENABLE              (0x0C15U) // 0:DISABLE 1:ENABLE, BITS 0-5:GPIO, 6:RX 7:TX\n\n#define XR_REG_LOOPBACK                         (0x0C16U) // 0:DISABLE 1:ENABLE, SEE DATA SHEET\n\n#define XR_REG_RX_FIFO_LATENCY                  (0x0CC2U) // FIFO LATENCY REGISTER\n#define XR_REG_RX_FIFO_LATENCY_ENABLE           (0x0001U) //\n\n#define XR_REG_WIDE_MODE                        (0x0D02U)\n#define XR_REG_WIDE_MODE_ENABLE                 (0x0001U)\n\n#define XR_REG_XON_CHAR                         (0x0C07U)\n#define XR_REG_XOFF_CHAR                        (0x0C08U)\n\n#define XR_REG_TX_FIFO_RESET                    (0x0C80U) // 1: RESET, SELF-CLEARING\n#define XR_REG_TX_FIFO_COUNT                    (0x0C81U) // READ-ONLY\n#define XR_REG_RX_FIFO_RESET                    (0x0CC0U) // 1: RESET, SELF-CLEARING\n#define XR_REG_RX_FIFO_COUNT                    (0x0CC1U) // READ-ONLY\n\n#define XR_WRITE_REQUEST_TYPE                   (0x40U)\n\n#define XR_READ_REQUEST_TYPE                    (0xC0U)\n\n#define XR_MAX_ENDPOINTS                        4\n\nclass XR21B1411 : public ACM {\nprotected:\n\npublic:\n        XR21B1411(USB *pusb, CDCAsyncOper *pasync);\n\n        /**\n         * Used by the USB core to check what this driver support.\n         * @param  vid The device's VID.\n         * @param  pid The device's PID.\n         * @return     Returns true if the device's VID and PID matches this driver.\n         */\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                return (((vid == 0x2890U) && (pid == 0x0201U)) || ((vid == 0x04e2U) && (pid == 0x1411U)));\n        };\n\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n\n        virtual tty_features enhanced_features(void) {\n                tty_features rv;\n                rv.enhanced = true;\n                rv.autoflow_RTS = true;\n                rv.autoflow_DSR = true;\n                rv.autoflow_XON = true;\n                rv.half_duplex = true;\n                rv.wide = true;\n                return rv;\n        };\n\n        uint8_t read_register(uint16_t reg, uint16_t *val) {\n                return (pUsb->ctrlReq(bAddress, 0, XR_READ_REQUEST_TYPE, 1, 0, 0, reg, 2, 2, (uint8_t *)val, NULL));\n        }\n\n        uint8_t write_register(uint16_t reg, uint16_t val) {\n                return (pUsb->ctrlReq(bAddress, 0, XR_WRITE_REQUEST_TYPE, 0, BGRAB0(val), BGRAB1(val), reg, 0, 0, NULL, NULL));\n        }\n\n\n        ////////////////////////////////////////////////////////////////////////\n        // The following methods set the CDC-ACM defaults.\n        ////////////////////////////////////////////////////////////////////////\n\n        virtual void autoflowRTS(bool s) {\n                uint16_t val;\n                uint8_t rval;\n                rval = read_register(XR_REG_ACM_FLOW_CTL, &val);\n                if(!rval) {\n                        if(s) {\n                                val &= XR_REG_FLOW_CTL_HALF_DPLX;\n                                val |= XR_REG_FLOW_CTL_HW;\n                        } else {\n                                val &= XR_REG_FLOW_CTL_HALF_DPLX;\n                        }\n                        rval = write_register(XR_REG_ACM_FLOW_CTL, val);\n                        if(!rval) {\n                                rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_GPIO);\n                                if(!rval) {\n                                        // ACM commands apply the new settings.\n                                        LINE_CODING LCT;\n                                        rval = GetLineCoding(&LCT);\n                                        if(!rval) {\n                                                rval = SetLineCoding(&LCT);\n                                                if(!rval) {\n                                                        _enhanced_status.autoflow_XON = false;\n                                                        _enhanced_status.autoflow_DSR = false;\n                                                        _enhanced_status.autoflow_RTS = s;\n                                                }\n                                        }\n                                }\n                        }\n                }\n        };\n\n        virtual void autoflowDSR(bool s) {\n                uint16_t val;\n                uint8_t rval;\n                rval = read_register(XR_REG_ACM_FLOW_CTL, &val);\n                if(!rval) {\n                        if(s) {\n                                val &= XR_REG_FLOW_CTL_HALF_DPLX;\n                                val |= XR_REG_FLOW_CTL_HW;\n                        } else {\n                                val &= XR_REG_FLOW_CTL_HALF_DPLX;\n                        }\n                        rval = write_register(XR_REG_ACM_FLOW_CTL, val);\n                        if(!rval) {\n                                if(s) {\n                                        rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_FC_DTRDSR);\n                                } else {\n                                        rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_GPIO);\n                                }\n                                if(!rval) {\n                                        // ACM commands apply the new settings.\n                                        LINE_CODING LCT;\n                                        rval = GetLineCoding(&LCT);\n                                        if(!rval) {\n                                                rval = SetLineCoding(&LCT);\n                                                if(!rval) {\n                                                        _enhanced_status.autoflow_XON = false;\n                                                        _enhanced_status.autoflow_RTS = false;\n                                                        _enhanced_status.autoflow_DSR = s;\n                                                }\n                                        }\n                                }\n                        }\n                }\n        };\n\n        virtual void autoflowXON(bool s) {\n                // NOTE: hardware defaults to the normal XON/XOFF\n                uint16_t val;\n                uint8_t rval;\n                rval = read_register(XR_REG_ACM_FLOW_CTL, &val);\n                if(!rval) {\n                        if(s) {\n                                val &= XR_REG_FLOW_CTL_HALF_DPLX;\n                                val |= XR_REG_FLOW_CTL_SW;\n                        } else {\n                                val &= XR_REG_FLOW_CTL_HALF_DPLX;\n                        }\n                        rval = write_register(XR_REG_ACM_FLOW_CTL, val);\n                        if(!rval) {\n                                rval = write_register(XR_REG_ACM_GPIO_MODE, XR_REG_GPIO_MODE_GPIO);\n                                if(!rval) {\n                                        // ACM commands apply the new settings.\n                                        LINE_CODING LCT;\n                                        rval = GetLineCoding(&LCT);\n                                        if(!rval) {\n                                                rval = SetLineCoding(&LCT);\n                                                if(!rval) {\n                                                        _enhanced_status.autoflow_RTS = false;\n                                                        _enhanced_status.autoflow_DSR = false;\n                                                        _enhanced_status.autoflow_XON = s;\n                                                }\n                                        }\n                                }\n                        }\n                }\n        };\n\n        virtual void half_duplex(bool s) {\n                uint16_t val;\n                uint8_t rval;\n                rval = read_register(XR_REG_ACM_FLOW_CTL, &val);\n                if(!rval) {\n                        if(s) {\n                                val |= XR_REG_FLOW_CTL_HALF_DPLX;\n                        } else {\n                                val &= XR_REG_FLOW_CTL_MODE_MASK;\n                        }\n                        rval = write_register(XR_REG_ACM_FLOW_CTL, val);\n                        if(!rval) {\n                                // ACM commands apply the new settings.\n                                LINE_CODING LCT;\n                                rval = GetLineCoding(&LCT);\n                                if(!rval) {\n                                        rval = SetLineCoding(&LCT);\n                                        if(!rval) {\n                                                _enhanced_status.half_duplex = s;\n                                        }\n                                }\n                        }\n                }\n        };\n\n\n\n};\n\n#endif // __CDCPROLIFIC_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/cdcacm.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#include \"cdcacm.h\"\n\nconst uint8_t ACM::epDataInIndex = 1;\nconst uint8_t ACM::epDataOutIndex = 2;\nconst uint8_t ACM::epInterruptInIndex = 3;\n\nACM::ACM(USB *p, CDCAsyncOper *pasync) :\npUsb(p),\npAsync(pasync),\nbAddress(0),\nbControlIface(0),\nbDataIface(0),\nbNumEP(1),\nqNextPollTime(0),\nbPollEnable(false),\nready(false) {\n        _enhanced_status = enhanced_features(); // Set up features\n        for(uint8_t i = 0; i < ACM_MAX_ENDPOINTS; i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n                epInfo[i].bmNakPower = (i == epDataInIndex) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;\n\n        }\n        if(pUsb)\n                pUsb->RegisterDeviceClass(this);\n}\n\nuint8_t ACM::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n\n        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);\n\n        uint8_t buf[constBufSize];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        uint8_t num_of_conf; // number of configurations\n\n        AddressPool &addrPool = pUsb->GetAddressPool();\n\n        USBTRACE(\"ACM Init\\r\\n\");\n\n        if(bAddress)\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        if(!p->epinfo) {\n                USBTRACE(\"epinfo\\r\\n\");\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf);\n\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from the device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n                USBTRACE2(\"setAddr:\", rcode);\n                return rcode;\n        }\n\n        USBTRACE2(\"Addr:\", bAddress);\n\n        p->lowspeed = false;\n\n        p = addrPool.GetUsbDevicePtr(bAddress);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        num_of_conf = udd->bNumConfigurations;\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);\n\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        USBTRACE2(\"NC:\", num_of_conf);\n\n        for(uint8_t i = 0; i < num_of_conf; i++) {\n                ConfigDescParser< USB_CLASS_COM_AND_CDC_CTRL,\n                        CDC_SUBCLASS_ACM,\n                        CDC_PROTOCOL_ITU_T_V_250,\n                        CP_MASK_COMPARE_CLASS |\n                        CP_MASK_COMPARE_SUBCLASS |\n                        CP_MASK_COMPARE_PROTOCOL > CdcControlParser(this);\n\n                ConfigDescParser<USB_CLASS_CDC_DATA, 0, 0,\n                        CP_MASK_COMPARE_CLASS> CdcDataParser(this);\n\n                rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcControlParser);\n\n                if(rcode)\n                        goto FailGetConfDescr;\n\n                rcode = pUsb->getConfDescr(bAddress, 0, i, &CdcDataParser);\n\n                if(rcode)\n                        goto FailGetConfDescr;\n\n                if(bNumEP > 1)\n                        break;\n        } // for\n\n        if(bNumEP < 4)\n                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);\n\n        USBTRACE2(\"Conf:\", bConfNum);\n\n        // Set Configuration Value\n        rcode = pUsb->setConf(bAddress, 0, bConfNum);\n\n        if(rcode)\n                goto FailSetConfDescr;\n\n        // Set up features status\n        _enhanced_status = enhanced_features();\n        half_duplex(false);\n        autoflowRTS(false);\n        autoflowDSR(false);\n        autoflowXON(false);\n        wide(false); // Always false, because this is only available in custom mode.\n        rcode = pAsync->OnInit(this);\n\n        if(rcode)\n                goto FailOnInit;\n\n        USBTRACE(\"ACM configured\\r\\n\");\n\n        ready = true;\n\n        //bPollEnable = true;\n\n        //USBTRACE(\"Poll enabled\\r\\n\");\n        return 0;\n\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr();\n        goto Fail;\n#endif\n\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailGetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetConfDescr();\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n        goto Fail;\n#endif\n\nFailOnInit:\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"OnInit:\");\n#endif\n\n#ifdef DEBUG_USB_HOST\nFail:\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n\nvoid ACM::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) {\n        //ErrorMessage<uint8_t > (PSTR(\"Conf.Val\"), conf);\n        //ErrorMessage<uint8_t > (PSTR(\"Iface Num\"), iface);\n        //ErrorMessage<uint8_t > (PSTR(\"Alt.Set\"), alt);\n\n        bConfNum = conf;\n\n        uint8_t index;\n\n        if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80)\n                index = epInterruptInIndex;\n        else\n                if((pep->bmAttributes & 0x02) == 2)\n                index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;\n        else\n                return;\n\n        // Fill in the endpoint info structure\n        epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);\n        epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;\n        epInfo[index].epAttribs = 0;\n\n        bNumEP++;\n\n        PrintEndpointDescriptor(pep);\n}\n\nuint8_t ACM::Release() {\n        ready = false;\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n\n        bControlIface = 0;\n        bDataIface = 0;\n        bNumEP = 1;\n\n        bAddress = 0;\n        qNextPollTime = 0;\n        bPollEnable = false;\n        return 0;\n}\n\nuint8_t ACM::Poll() {\n        uint8_t rcode = 0;\n\n        if(!bPollEnable)\n                return 0;\n\n        return rcode;\n}\n\nuint8_t ACM::RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) {\n        return pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr);\n}\n\nuint8_t ACM::SndData(uint16_t nbytes, uint8_t *dataptr) {\n        return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, nbytes, dataptr);\n}\n\nuint8_t ACM::SetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SET_COMM_FEATURE, (fid & 0xff), (fid >> 8), bControlIface, nbytes, nbytes, dataptr, NULL));\n}\n\nuint8_t ACM::GetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCIN, CDC_GET_COMM_FEATURE, (fid & 0xff), (fid >> 8), bControlIface, nbytes, nbytes, dataptr, NULL));\n}\n\nuint8_t ACM::ClearCommFeature(uint16_t fid) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_CLEAR_COMM_FEATURE, (fid & 0xff), (fid >> 8), bControlIface, 0, 0, NULL, NULL));\n}\n\nuint8_t ACM::SetLineCoding(const LINE_CODING *dataptr) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SET_LINE_CODING, 0x00, 0x00, bControlIface, sizeof (LINE_CODING), sizeof (LINE_CODING), (uint8_t*)dataptr, NULL));\n}\n\nuint8_t ACM::GetLineCoding(LINE_CODING *dataptr) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCIN, CDC_GET_LINE_CODING, 0x00, 0x00, bControlIface, sizeof (LINE_CODING), sizeof (LINE_CODING), (uint8_t*)dataptr, NULL));\n}\n\nuint8_t ACM::SetControlLineState(uint8_t state) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SET_CONTROL_LINE_STATE, state, 0, bControlIface, 0, 0, NULL, NULL));\n}\n\nuint8_t ACM::SendBreak(uint16_t duration) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CDCOUT, CDC_SEND_BREAK, (duration & 0xff), (duration >> 8), bControlIface, 0, 0, NULL, NULL));\n}\n\nvoid ACM::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {\n        Notify(PSTR(\"Endpoint descriptor:\"), 0x80);\n        Notify(PSTR(\"\\r\\nLength:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);\n        Notify(PSTR(\"\\r\\nType:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);\n        Notify(PSTR(\"\\r\\nAddress:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);\n        Notify(PSTR(\"\\r\\nAttributes:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);\n        Notify(PSTR(\"\\r\\nMaxPktSize:\\t\"), 0x80);\n        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);\n        Notify(PSTR(\"\\r\\nPoll Intrv:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/cdcacm.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(__CDCACM_H__)\n#define __CDCACM_H__\n\n#include \"Usb.h\"\n\n#define bmREQ_CDCOUT                    USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE\n#define bmREQ_CDCIN                     USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE\n\n// CDC Subclass Constants\n#define CDC_SUBCLASS_DLCM               0x01    // Direct Line Control Model\n#define CDC_SUBCLASS_ACM                0x02    // Abstract Control Model\n#define CDC_SUBCLASS_TCM                0x03    // Telephone Control Model\n#define CDC_SUBCLASS_MCCM               0x04    // Multi Channel Control Model\n#define CDC_SUBCLASS_CAPI               0x05    // CAPI Control Model\n#define CDC_SUBCLASS_ETHERNET           0x06    // Ethernet Network Control Model\n#define CDC_SUBCLASS_ATM                0x07    // ATM Network Control Model\n#define CDC_SUBCLASS_WIRELESS_HANDSET   0x08    // Wireless Handset Control Model\n#define CDC_SUBCLASS_DEVICE_MANAGEMENT  0x09    // Device Management\n#define CDC_SUBCLASS_MOBILE_DIRECT_LINE 0x0A    // Mobile Direct Line Model\n#define CDC_SUBCLASS_OBEX               0x0B    // OBEX\n#define CDC_SUBCLASS_ETHERNET_EMU       0x0C    // Ethernet Emulation Model\n\n// Communication Interface Class Control Protocol Codes\n#define CDC_PROTOCOL_ITU_T_V_250        0x01    // AT Commands defined by ITU-T V.250\n#define CDC_PROTOCOL_PCCA_101           0x02    // AT Commands defined by PCCA-101\n#define CDC_PROTOCOL_PCCA_101_O         0x03    // AT Commands defined by PCCA-101 & Annex O\n#define CDC_PROTOCOL_GSM_7_07           0x04    // AT Commands defined by GSM 7.07\n#define CDC_PROTOCOL_3GPP_27_07         0x05    // AT Commands defined by 3GPP 27.007\n#define CDC_PROTOCOL_C_S0017_0          0x06    // AT Commands defined by TIA for CDMA\n#define CDC_PROTOCOL_USB_EEM            0x07    // Ethernet Emulation Model\n\n// CDC Commands defined by CDC 1.2\n#define CDC_SEND_ENCAPSULATED_COMMAND   0x00\n#define CDC_GET_ENCAPSULATED_RESPONSE   0x01\n\n// CDC Commands defined by PSTN 1.2\n#define CDC_SET_COMM_FEATURE            0x02\n#define CDC_GET_COMM_FEATURE            0x03\n#define CDC_CLEAR_COMM_FEATURE          0x04\n#define CDC_SET_AUX_LINE_STATE          0x10\n#define CDC_SET_HOOK_STATE              0x11\n#define CDC_PULSE_SETUP                 0x12\n#define CDC_SEND_PULSE                  0x13\n#define CDC_SET_PULSE_TIME              0x14\n#define CDC_RING_AUX_JACK               0x15\n#define CDC_SET_LINE_CODING             0x20\n#define CDC_GET_LINE_CODING             0x21\n#define CDC_SET_CONTROL_LINE_STATE      0x22\n#define CDC_SEND_BREAK                  0x23\n#define CDC_SET_RINGER_PARMS            0x30\n#define CDC_GET_RINGER_PARMS            0x31\n#define CDC_SET_OPERATION_PARMS         0x32\n#define CDC_GET_OPERATION_PARMS         0x33\n#define CDC_SET_LINE_PARMS              0x34\n#define CDC_GET_LINE_PARMS              0x35\n#define CDC_DIAL_DIGITS                 0x36\n\n//Class-Specific Notification Codes\n#define NETWORK_CONNECTION              0x00\n#define RESPONSE_AVAILABLE              0x01\n#define AUX_JACK_HOOK_STATE             0x08\n#define RING_DETECT                     0x09\n#define SERIAL_STATE                    0x20\n#define CALL_STATE_CHANGE               0x28\n#define LINE_STATE_CHANGE               0x29\n#define CONNECTION_SPEED_CHANGE         0x2a\n\n// CDC Functional Descriptor Structures\n\ntypedef struct {\n        uint8_t bFunctionLength;\n        uint8_t bDescriptorType;\n        uint8_t bDescriptorSubtype;\n        uint8_t bmCapabilities;\n        uint8_t bDataInterface;\n} CALL_MGMNT_FUNC_DESCR;\n\ntypedef struct {\n        uint8_t bFunctionLength;\n        uint8_t bDescriptorType;\n        uint8_t bDescriptorSubtype;\n        uint8_t bmCapabilities;\n} ACM_FUNC_DESCR, DLM_FUNC_DESCR, TEL_OPER_MODES_FUNC_DESCR,\nTEL_CALL_STATE_REP_CPBL_FUNC_DESCR;\n\ntypedef struct {\n        uint8_t bFunctionLength;\n        uint8_t bDescriptorType;\n        uint8_t bDescriptorSubtype;\n        uint8_t bRingerVolSteps;\n        uint8_t bNumRingerPatterns;\n} TEL_RINGER_FUNC_DESCR;\n\ntypedef struct {\n        uint32_t dwDTERate; // Data Terminal Rate in bits per second\n        uint8_t bCharFormat; // 0 - 1 stop bit, 1 - 1.5 stop bits, 2 - 2 stop bits\n        uint8_t bParityType; // 0 - None, 1 - Odd, 2 - Even, 3 - Mark, 4 - Space\n        uint8_t bDataBits; // Data bits (5, 6, 7, 8 or 16)\n} LINE_CODING;\n\ntypedef struct {\n        uint8_t bmRequestType; // 0xa1 for class-specific notifications\n        uint8_t bNotification;\n        uint16_t wValue;\n        uint16_t wIndex;\n        uint16_t wLength;\n        uint16_t bmState; //UART state bitmap for SERIAL_STATE, other notifications variable length\n} CLASS_NOTIFICATION;\n\nclass ACM;\n\nclass CDCAsyncOper {\npublic:\n\n        virtual uint8_t OnInit(ACM *pacm) {\n                return 0;\n        };\n        //virtual void OnDataRcvd(ACM *pacm, uint8_t nbytes, uint8_t *dataptr) = 0;\n        //virtual void OnDisconnected(ACM *pacm) = 0;\n};\n\n/**\n * This structure is used to report the extended capabilities of the connected device.\n * It is also used to report the current status.\n * Regular CDC-ACM reports all as false.\n */\ntypedef struct {\n\n        union {\n                uint8_t tty;\n\n                struct {\n                        bool enhanced : 1; // Do we have the ability to set/clear any features?\n                        // Status and 8th bit in data stream.\n                        // Presence only indicates feature is available, but this isn't used for CDC-ACM.\n                        bool wide : 1;\n                        bool autoflow_RTS : 1; // Has autoflow on RTS/CTS\n                        bool autoflow_DSR : 1; // Has autoflow on DTR/DSR\n                        bool autoflow_XON : 1; // Has autoflow  XON/XOFF\n                        bool half_duplex : 1;  // Has half-duplex capability.\n                } __attribute__((packed));\n        };\n} tty_features;\n\n#define ACM_MAX_ENDPOINTS               4\n\nclass ACM : public USBDeviceConfig, public UsbConfigXtracter {\nprotected:\n        static const uint8_t epDataInIndex; // DataIn endpoint index\n        static const uint8_t epDataOutIndex; // DataOUT endpoint index\n        static const uint8_t epInterruptInIndex; // InterruptIN  endpoint index\n\n        USB *pUsb;\n        CDCAsyncOper *pAsync;\n        uint8_t bAddress;\n        uint8_t bConfNum; // configuration number\n        uint8_t bControlIface; // Control interface value\n        uint8_t bDataIface; // Data interface value\n        uint8_t bNumEP; // total number of EP in the configuration\n        uint32_t qNextPollTime; // next poll time\n        volatile bool bPollEnable; // poll enable flag\n        volatile bool ready; //device ready indicator\n        tty_features _enhanced_status; // current status\n\n        EpInfo epInfo[ACM_MAX_ENDPOINTS];\n\n        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);\n\npublic:\n        ACM(USB *pusb, CDCAsyncOper *pasync);\n\n        uint8_t SetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr);\n        uint8_t GetCommFeature(uint16_t fid, uint8_t nbytes, uint8_t *dataptr);\n        uint8_t ClearCommFeature(uint16_t fid);\n        uint8_t SetLineCoding(const LINE_CODING *dataptr);\n        uint8_t GetLineCoding(LINE_CODING *dataptr);\n        uint8_t SetControlLineState(uint8_t state);\n        uint8_t SendBreak(uint16_t duration);\n        uint8_t GetNotif(uint16_t *bytes_rcvd, uint8_t *dataptr);\n\n        // Methods for receiving and sending data\n        uint8_t RcvData(uint16_t *nbytesptr, uint8_t *dataptr);\n        uint8_t SndData(uint16_t nbytes, uint8_t *dataptr);\n\n        // USBDeviceConfig implementation\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        uint8_t Release();\n        uint8_t Poll();\n\n        bool available(void) {\n                return false;\n        };\n\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        virtual bool isReady() {\n                return ready;\n        };\n\n        virtual tty_features enhanced_status(void) {\n                return _enhanced_status;\n        };\n\n        virtual tty_features enhanced_features(void) {\n                tty_features rv;\n                rv.enhanced = false;\n                rv.autoflow_RTS = false;\n                rv.autoflow_DSR = false;\n                rv.autoflow_XON = false;\n                rv.half_duplex = false;\n                rv.wide = false;\n                return rv;\n        };\n\n        virtual void autoflowRTS(bool s) {\n        };\n\n        virtual void autoflowDSR(bool s) {\n        };\n\n        virtual void autoflowXON(bool s) {\n        };\n\n        virtual void half_duplex(bool s) {\n        };\n\n        virtual void wide(bool s) {\n        };\n\n        // UsbConfigXtracter implementation\n        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);\n};\n\n#endif // __CDCACM_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/cdcftdi.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#include \"cdcftdi.h\"\n\nconst uint8_t FTDI::epDataInIndex = 1;\nconst uint8_t FTDI::epDataOutIndex = 2;\nconst uint8_t FTDI::epInterruptInIndex = 3;\n\nFTDI::FTDI(USB *p, FTDIAsyncOper *pasync) :\npAsync(pasync),\npUsb(p),\nbAddress(0),\nbNumEP(1),\nwFTDIType(0) {\n        for(uint8_t i = 0; i < FTDI_MAX_ENDPOINTS; i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n                epInfo[i].bmNakPower = (i==epDataInIndex) ? USB_NAK_NOWAIT: USB_NAK_MAX_POWER;\n        }\n        if(pUsb)\n                pUsb->RegisterDeviceClass(this);\n}\n\nuint8_t FTDI::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);\n\n        uint8_t buf[constBufSize];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n\n        uint8_t num_of_conf; // number of configurations\n\n        AddressPool &addrPool = pUsb->GetAddressPool();\n\n        USBTRACE(\"FTDI Init\\r\\n\");\n\n        if(bAddress)\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        if(!p->epinfo) {\n                USBTRACE(\"epinfo\\r\\n\");\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), buf);\n\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(rcode)\n                goto FailGetDevDescr;\n        if(udd->idVendor != FTDI_VID || udd->idProduct != FTDI_PID)\n                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\n        // Save type of FTDI chip\n        wFTDIType = udd->bcdDevice;\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from the device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n                USBTRACE2(\"setAddr:\", rcode);\n                return rcode;\n        }\n\n        USBTRACE2(\"Addr:\", bAddress);\n\n        p->lowspeed = false;\n\n        p = addrPool.GetUsbDevicePtr(bAddress);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        num_of_conf = udd->bNumConfigurations;\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);\n\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        USBTRACE2(\"NC:\", num_of_conf);\n\n        for(uint8_t i = 0; i < num_of_conf; i++) {\n                HexDumper<USBReadParser, uint16_t, uint16_t> HexDump;\n                ConfigDescParser < 0xFF, 0xFF, 0xFF, CP_MASK_COMPARE_ALL> confDescrParser(this);\n\n                rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump);\n\n                if(rcode)\n                        goto FailGetConfDescr;\n\n                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);\n\n                if(rcode)\n                        goto FailGetConfDescr;\n\n                if(bNumEP > 1)\n                        break;\n        } // for\n\n        if(bNumEP < 2)\n                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\n        USBTRACE2(\"NumEP:\", bNumEP);\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);\n\n        USBTRACE2(\"Conf:\", bConfNum);\n\n        // Set Configuration Value\n        rcode = pUsb->setConf(bAddress, 0, bConfNum);\n\n        if(rcode)\n                goto FailSetConfDescr;\n\n        rcode = pAsync->OnInit(this);\n\n        if(rcode)\n                goto FailOnInit;\n\n        USBTRACE(\"FTDI configured\\r\\n\");\n\n        bPollEnable = true;\n        return 0;\n\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr();\n        goto Fail;\n#endif\n\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailGetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetConfDescr();\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n        goto Fail;\n#endif\n\nFailOnInit:\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"OnInit:\");\n\nFail:\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n\nvoid FTDI::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) {\n        ErrorMessage<uint8_t > (PSTR(\"Conf.Val\"), conf);\n        ErrorMessage<uint8_t > (PSTR(\"Iface Num\"), iface);\n        ErrorMessage<uint8_t > (PSTR(\"Alt.Set\"), alt);\n\n        bConfNum = conf;\n\n        uint8_t index;\n\n        if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80)\n                index = epInterruptInIndex;\n        else\n                if((pep->bmAttributes & 0x02) == 2)\n                index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;\n        else\n                return;\n\n        // Fill in the endpoint info structure\n        epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);\n        epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;\n        epInfo[index].epAttribs = 0;\n\n        bNumEP++;\n\n        PrintEndpointDescriptor(pep);\n}\n\nuint8_t FTDI::Release() {\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n\n        bAddress = 0;\n        bNumEP = 1;\n        qNextPollTime = 0;\n        bPollEnable = false;\n        return pAsync->OnRelease(this);\n}\n\nuint8_t FTDI::Poll() {\n        uint8_t rcode = 0;\n\n        //if (!bPollEnable)\n        //      return 0;\n\n        //if (qNextPollTime <= millis())\n        //{\n        //      USB_HOST_SERIAL.println(bAddress, HEX);\n\n        //      qNextPollTime = millis() + 100;\n        //}\n        return rcode;\n}\n\nuint8_t FTDI::SetBaudRate(uint32_t baud) {\n        uint16_t baud_value, baud_index = 0;\n        uint32_t divisor3;\n\n        divisor3 = 48000000 / 2 / baud; // divisor shifted 3 bits to the left\n\n        if(wFTDIType == FT232AM) {\n                if((divisor3 & 0x7) == 7)\n                        divisor3++; // round x.7/8 up to x+1\n\n                baud_value = divisor3 >> 3;\n                divisor3 &= 0x7;\n\n                if(divisor3 == 1) baud_value |= 0xc000;\n                else // 0.125\n                        if(divisor3 >= 4) baud_value |= 0x4000;\n                else // 0.5\n                        if(divisor3 != 0) baud_value |= 0x8000; // 0.25\n                if(baud_value == 1) baud_value = 0; /* special case for maximum baud rate */\n        } else {\n                static const unsigned char divfrac [8] = {0, 3, 2, 0, 1, 1, 2, 3};\n                static const unsigned char divindex[8] = {0, 0, 0, 1, 0, 1, 1, 1};\n\n                baud_value = divisor3 >> 3;\n                baud_value |= divfrac [divisor3 & 0x7] << 14;\n                baud_index = divindex[divisor3 & 0x7];\n\n                /* Deal with special cases for highest baud rates. */\n                if(baud_value == 1) baud_value = 0;\n                else // 1.0\n                        if(baud_value == 0x4001) baud_value = 1; // 1.5\n        }\n        USBTRACE2(\"baud_value:\", baud_value);\n        USBTRACE2(\"baud_index:\", baud_index);\n        return pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_BAUD_RATE, baud_value & 0xff, baud_value >> 8, baud_index, 0, 0, NULL, NULL);\n}\n\nuint8_t FTDI::SetModemControl(uint16_t signal) {\n        return pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_MODEM_CTRL, signal & 0xff, signal >> 8, 0, 0, 0, NULL, NULL);\n}\n\nuint8_t FTDI::SetFlowControl(uint8_t protocol, uint8_t xon, uint8_t xoff) {\n        return pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_FLOW_CTRL, xon, xoff, protocol << 8, 0, 0, NULL, NULL);\n}\n\nuint8_t FTDI::SetData(uint16_t databm) {\n        return pUsb->ctrlReq(bAddress, 0, bmREQ_FTDI_OUT, FTDI_SIO_SET_DATA, databm & 0xff, databm >> 8, 0, 0, 0, NULL, NULL);\n}\n\nuint8_t FTDI::RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr) {\n        return pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, bytes_rcvd, dataptr);\n}\n\nuint8_t FTDI::SndData(uint16_t nbytes, uint8_t *dataptr) {\n        return pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, nbytes, dataptr);\n}\n\nvoid FTDI::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {\n        Notify(PSTR(\"Endpoint descriptor:\"), 0x80);\n        Notify(PSTR(\"\\r\\nLength:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);\n        Notify(PSTR(\"\\r\\nType:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);\n        Notify(PSTR(\"\\r\\nAddress:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);\n        Notify(PSTR(\"\\r\\nAttributes:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);\n        Notify(PSTR(\"\\r\\nMaxPktSize:\\t\"), 0x80);\n        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);\n        Notify(PSTR(\"\\r\\nPoll Intrv:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/cdcftdi.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(__CDCFTDI_H__)\n#define __CDCFTDI_H__\n\n#include \"Usb.h\"\n\n#define bmREQ_FTDI_OUT  0x40\n#define bmREQ_FTDI_IN   0xc0\n\n//#define bmREQ_FTDI_OUT                USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE\n//#define bmREQ_FTDI_IN         USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE\n\n#define FTDI_VID                        0x0403  // FTDI VID\n#define FTDI_PID                        0x6001  // FTDI PID\n\n#define FT232AM                         0x0200\n#define FT232BM                         0x0400\n#define FT2232                          0x0500\n#define FT232R                          0x0600\n\n// Commands\n#define FTDI_SIO_RESET                  0 /* Reset the port */\n#define FTDI_SIO_MODEM_CTRL             1 /* Set the modem control register */\n#define FTDI_SIO_SET_FLOW_CTRL          2 /* Set flow control register */\n#define FTDI_SIO_SET_BAUD_RATE          3 /* Set baud rate */\n#define FTDI_SIO_SET_DATA               4 /* Set the data characteristics of the port */\n#define FTDI_SIO_GET_MODEM_STATUS       5 /* Retrieve current value of modem status register */\n#define FTDI_SIO_SET_EVENT_CHAR         6 /* Set the event character */\n#define FTDI_SIO_SET_ERROR_CHAR         7 /* Set the error character */\n\n#define FTDI_SIO_RESET_SIO              0\n#define FTDI_SIO_RESET_PURGE_RX         1\n#define FTDI_SIO_RESET_PURGE_TX         2\n\n#define FTDI_SIO_SET_DATA_PARITY_NONE   (0x0 << 8 )\n#define FTDI_SIO_SET_DATA_PARITY_ODD    (0x1 << 8 )\n#define FTDI_SIO_SET_DATA_PARITY_EVEN   (0x2 << 8 )\n#define FTDI_SIO_SET_DATA_PARITY_MARK   (0x3 << 8 )\n#define FTDI_SIO_SET_DATA_PARITY_SPACE  (0x4 << 8 )\n#define FTDI_SIO_SET_DATA_STOP_BITS_1   (0x0 << 11)\n#define FTDI_SIO_SET_DATA_STOP_BITS_15  (0x1 << 11)\n#define FTDI_SIO_SET_DATA_STOP_BITS_2   (0x2 << 11)\n#define FTDI_SIO_SET_BREAK              (0x1 << 14)\n\n#define FTDI_SIO_SET_DTR_MASK           0x1\n#define FTDI_SIO_SET_DTR_HIGH           ( 1 | ( FTDI_SIO_SET_DTR_MASK  << 8))\n#define FTDI_SIO_SET_DTR_LOW            ( 0 | ( FTDI_SIO_SET_DTR_MASK  << 8))\n#define FTDI_SIO_SET_RTS_MASK           0x2\n#define FTDI_SIO_SET_RTS_HIGH           ( 2 | ( FTDI_SIO_SET_RTS_MASK << 8 ))\n#define FTDI_SIO_SET_RTS_LOW            ( 0 | ( FTDI_SIO_SET_RTS_MASK << 8 ))\n\n#define FTDI_SIO_DISABLE_FLOW_CTRL      0x0\n#define FTDI_SIO_RTS_CTS_HS             (0x1 << 8)\n#define FTDI_SIO_DTR_DSR_HS             (0x2 << 8)\n#define FTDI_SIO_XON_XOFF_HS            (0x4 << 8)\n\n#define FTDI_SIO_CTS_MASK               0x10\n#define FTDI_SIO_DSR_MASK               0x20\n#define FTDI_SIO_RI_MASK                0x40\n#define FTDI_SIO_RLSD_MASK              0x80\n\nclass FTDI;\n\nclass FTDIAsyncOper {\npublic:\n\n        virtual uint8_t OnInit(FTDI *pftdi) {\n                return 0;\n        };\n\n        virtual uint8_t OnRelease(FTDI *pftdi) {\n                return 0;\n        };\n};\n\n\n// Only single port chips are currently supported by the library,\n//              so only three endpoints are allocated.\n#define FTDI_MAX_ENDPOINTS              3\n\nclass FTDI : public USBDeviceConfig, public UsbConfigXtracter {\n        static const uint8_t epDataInIndex; // DataIn endpoint index\n        static const uint8_t epDataOutIndex; // DataOUT endpoint index\n        static const uint8_t epInterruptInIndex; // InterruptIN  endpoint index\n\n        FTDIAsyncOper *pAsync;\n        USB *pUsb;\n        uint8_t bAddress;\n        uint8_t bConfNum; // configuration number\n        uint8_t bNumIface; // number of interfaces in the configuration\n        uint8_t bNumEP; // total number of EP in the configuration\n        uint32_t qNextPollTime; // next poll time\n        bool bPollEnable; // poll enable flag\n        uint16_t wFTDIType; // Type of FTDI chip\n\n        EpInfo epInfo[FTDI_MAX_ENDPOINTS];\n\n        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);\n\npublic:\n        FTDI(USB *pusb, FTDIAsyncOper *pasync);\n\n        uint8_t SetBaudRate(uint32_t baud);\n        uint8_t SetModemControl(uint16_t control);\n        uint8_t SetFlowControl(uint8_t protocol, uint8_t xon = 0x11, uint8_t xoff = 0x13);\n        uint8_t SetData(uint16_t databm);\n\n        // Methods for recieving and sending data\n        uint8_t RcvData(uint16_t *bytes_rcvd, uint8_t *dataptr);\n        uint8_t SndData(uint16_t nbytes, uint8_t *dataptr);\n\n        // USBDeviceConfig implementation\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        uint8_t Release();\n        uint8_t Poll();\n\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        // UsbConfigXtracter implementation\n        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);\n\n        virtual bool VIDPIDOK(uint16_t vid, uint16_t pid) {\n                return (vid == FTDI_VID && pid == FTDI_PID);\n        }\n\n};\n\n#endif // __CDCFTDI_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/cdcprolific.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#include \"cdcprolific.h\"\n\nPL2303::PL2303(USB *p, CDCAsyncOper *pasync) :\nACM(p, pasync),\nwPLType(0) {\n}\n\nuint8_t PL2303::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);\n\n        uint8_t buf[constBufSize];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        uint8_t num_of_conf; // number of configurations\n#ifdef PL2303_COMPAT\n        enum pl2303_type pltype = unknown;\n#endif\n\n        AddressPool &addrPool = pUsb->GetAddressPool();\n\n        USBTRACE(\"PL Init\\r\\n\");\n\n        if(bAddress)\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        if(!p->epinfo) {\n                USBTRACE(\"epinfo\\r\\n\");\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*)buf);\n\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        if(udd->idVendor != PL_VID && CHECK_PID(udd->idProduct))\n                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\n        /* determine chip variant */\n#ifdef PL2303_COMPAT\n        if(udd->bDeviceClass == 0x02 )\n                pltype = type_0;\n        else if(udd->bMaxPacketSize0 == 0x40 )\n                pltype = rev_HX;\n        else if(udd->bDeviceClass == 0x00)\n                pltype = type_1;\n        else if(udd->bDeviceClass == 0xff)\n                pltype = type_1;\n#endif\n\n        // Save type of PL chip\n        wPLType = udd->bcdDevice;\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from the device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n                USBTRACE2(\"setAddr:\", rcode);\n                return rcode;\n        }\n\n        USBTRACE2(\"Addr:\", bAddress);\n\n        p->lowspeed = false;\n\n        p = addrPool.GetUsbDevicePtr(bAddress);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        num_of_conf = udd->bNumConfigurations;\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);\n\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        USBTRACE2(\"NC:\", num_of_conf);\n\n        for(uint8_t i = 0; i < num_of_conf; i++) {\n                HexDumper<USBReadParser, uint16_t, uint16_t> HexDump;\n                ConfigDescParser < 0xFF, 0, 0, CP_MASK_COMPARE_CLASS> confDescrParser(this);\n\n                rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump);\n\n                if(rcode)\n                        goto FailGetConfDescr;\n\n                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);\n\n                if(rcode)\n                        goto FailGetConfDescr;\n\n                if(bNumEP > 1)\n                        break;\n        } // for\n\n        if(bNumEP < 2)\n                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);\n\n        USBTRACE2(\"Conf:\", bConfNum);\n\n        // Set Configuration Value\n        rcode = pUsb->setConf(bAddress, 0, bConfNum);\n\n        if(rcode)\n                goto FailSetConfDescr;\n\n#ifdef PL2303_COMPAT\n        /* Shamanic dance - sending Prolific init data as-is */\n        vendorRead( 0x84, 0x84, 0, buf );\n        vendorWrite( 0x04, 0x04, 0 );\n        vendorRead( 0x84, 0x84, 0, buf );\n        vendorRead( 0x83, 0x83, 0, buf );\n        vendorRead( 0x84, 0x84, 0, buf );\n        vendorWrite( 0x04, 0x04, 1 );\n        vendorRead( 0x84, 0x84, 0, buf);\n        vendorRead( 0x83, 0x83, 0, buf);\n        vendorWrite( 0, 0, 1 );\n        vendorWrite( 1, 0, 0 );\n        if( pltype == rev_HX ) {\n                vendorWrite( 2, 0, 0x44 );\n                vendorWrite( 0x06, 0x06, 0 ); // From W7 init\n        }\n        else {\n                vendorWrite( 2, 0, 0x24 );\n        }\n        /* Shamanic dance end */\n#endif\n        /* Calling post-init callback */\n        rcode = pAsync->OnInit(this);\n\n        if(rcode)\n                goto FailOnInit;\n\n        USBTRACE(\"PL configured\\r\\n\");\n\n        //bPollEnable = true;\n        ready = true;\n        return 0;\n\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr();\n        goto Fail;\n#endif\n\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailGetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetConfDescr();\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n        goto Fail;\n#endif\n\nFailOnInit:\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"OnInit:\");\n#endif\n\n#ifdef DEBUG_USB_HOST\nFail:\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n\n//uint8_t PL::Poll()\n//{\n//      uint8_t rcode = 0;\n//\n//      //if (!bPollEnable)\n//      //      return 0;\n//\n//      //if (qNextPollTime <= millis())\n//      //{\n//      //      USB_HOST_SERIAL.println(bAddress, HEX);\n//\n//      //      qNextPollTime = millis() + 100;\n//      //}\n//      return rcode;\n//}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/cdcprolific.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(__CDCPROLIFIC_H__)\n#define __CDCPROLIFIC_H__\n\n#include \"cdcacm.h\"\n\n//#define PL2303_COMPAT // Uncomment it if you have compatibility problems\n\n#define PL_VID                                  0x067B\n#define CHECK_PID(pid)                          ( pid != 0x2303 && pid != 0x0609 )\n\n//#define PL_PID                                0x0609\n\n#define PROLIFIC_REV_H                          0x0202\n#define PROLIFIC_REV_X                          0x0300\n#define PROLIFIC_REV_HX_CHIP_D                  0x0400\n#define PROLIFIC_REV_1                          0x0001\n\n#define kXOnChar                                '\\x11'\n#define kXOffChar                               '\\x13'\n\n#define SPECIAL_SHIFT                           (5)\n#define SPECIAL_MASK                            ((1<<SPECIAL_SHIFT) - 1)\n#define STATE_ALL                               ( PD_RS232_S_MASK | PD_S_MASK )\n#define FLOW_RX_AUTO                            ( PD_RS232_A_RFR | PD_RS232_A_DTR | PD_RS232_A_RXO )\n#define FLOW_TX_AUTO                            ( PD_RS232_A_CTS | PD_RS232_A_DSR | PD_RS232_A_TXO | PD_RS232_A_DCD )\n#define CAN_BE_AUTO                             ( FLOW_RX_AUTO | FLOW_TX_AUTO )\n#define CAN_NOTIFY                              ( PD_RS232_N_MASK )\n#define EXTERNAL_MASK                           ( PD_S_MASK | (PD_RS232_S_MASK & ~PD_RS232_S_LOOP) )\n#define INTERNAL_DELAY                          ( PD_RS232_S_LOOP )\n#define DEFAULT_AUTO                            ( PD_RS232_A_DTR | PD_RS232_A_RFR | PD_RS232_A_CTS | PD_RS232_A_DSR )\n#define DEFAULT_NOTIFY                          0x00\n#define DEFAULT_STATE                           ( PD_S_TX_ENABLE | PD_S_RX_ENABLE | PD_RS232_A_TXO | PD_RS232_A_RXO )\n\n#define CONTINUE_SEND                           1\n#define PAUSE_SEND                              2\n\n#define kRxAutoFlow                             ((UInt32)( PD_RS232_A_RFR | PD_RS232_A_DTR | PD_RS232_A_RXO ))\n#define kTxAutoFlow                             ((UInt32)( PD_RS232_A_CTS | PD_RS232_A_DSR | PD_RS232_A_TXO | PD_RS232_A_DCD ))\n#define kControl_StateMask                      ((UInt32)( PD_RS232_S_CTS | PD_RS232_S_DSR | PD_RS232_S_CAR | PD_RS232_S_RI  ))\n#define kRxQueueState                           ((UInt32)( PD_S_RXQ_EMPTY | PD_S_RXQ_LOW_WATER | PD_S_RXQ_HIGH_WATER | PD_S_RXQ_FULL ))\n#define kTxQueueState                           ((UInt32)( PD_S_TXQ_EMPTY | PD_S_TXQ_LOW_WATER | PD_S_TXQ_HIGH_WATER | PD_S_TXQ_FULL ))\n\n#define kCONTROL_DTR                            0x01\n#define kCONTROL_RTS                            0x02\n\n#define kStateTransientMask                     0x74\n#define kBreakError                             0x04\n#define kFrameError                             0x10\n#define kParityError                            0x20\n#define kOverrunError                           0x40\n\n#define kCTS                                    0x80\n#define kDSR                                    0x02\n#define kRI                                     0x08\n#define kDCD                                    0x01\n#define kHandshakeInMask                        ((UInt32)( PD_RS232_S_CTS | PD_RS232_S_DSR | PD_RS232_S_CAR | PD_RS232_S_RI  ))\n\n#define VENDOR_WRITE_REQUEST_TYPE               0x40\n#define VENDOR_WRITE_REQUEST                    0x01\n\n#define VENDOR_READ_REQUEST_TYPE                0xc0\n#define VENDOR_READ_REQUEST                     0x01\n\n// Device Configuration Registers (DCR0, DCR1, DCR2)\n#define SET_DCR0                                0x00\n#define GET_DCR0                                0x80\n#define DCR0_INIT                               0x01\n#define DCR0_INIT_H                             0x41\n#define DCR0_INIT_X                             0x61\n\n#define SET_DCR1                                0x01\n#define GET_DCR1                                0x81\n#define DCR1_INIT_H                             0x80\n#define DCR1_INIT_X                             0x00\n\n#define SET_DCR2                                0x02\n#define GET_DCR2                                0x82\n#define DCR2_INIT_H                             0x24\n#define DCR2_INIT_X                             0x44\n\n// On-chip Data Buffers:\n#define RESET_DOWNSTREAM_DATA_PIPE              0x08\n#define RESET_UPSTREAM_DATA_PIPE                0x09\n\n\n#define PL_MAX_ENDPOINTS                        4\n\nenum tXO_State {\n        kXOnSent = -2,\n        kXOffSent = -1,\n        kXO_Idle = 0,\n        kXOffNeeded = 1,\n        kXOnNeeded = 2\n};\n\nenum pl2303_type {\n        unknown,\n        type_0, /* don't know the difference between type 0 and */\n        type_1, /* type 1, until someone from prolific tells us... */\n        rev_X,\n        rev_HX, /* HX version of the pl2303 chip */\n        rev_H\n};\n\n\nclass PL2303 : public ACM {\n        uint16_t wPLType; // Type of chip\n\npublic:\n        PL2303(USB *pusb, CDCAsyncOper *pasync);\n\n        // USBDeviceConfig implementation\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        //virtual uint8_t Release();\n        //virtual uint8_t Poll();\n        //virtual uint8_t GetAddress() { return bAddress; };\n\n        //// UsbConfigXtracter implementation\n        //virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);\n\n#ifdef PL2303_COMPAT\nprivate:\n        /* Prolific proprietary requests */\n        uint8_t vendorRead( uint8_t val_lo, uint8_t val_hi, uint16_t index, uint8_t* buf );\n        uint8_t vendorWrite( uint8_t val_lo, uint8_t val_hi, uint8_t index );\n#endif\n};\n\n#ifdef PL2303_COMPAT\n/* vendor read request */\ninline uint8_t PL2303::vendorRead( uint8_t val_lo, uint8_t val_hi, uint16_t index, uint8_t* buf )\n{\n        return( pUsb->ctrlReq(bAddress, 0, VENDOR_READ_REQUEST_TYPE, VENDOR_READ_REQUEST, val_lo, val_hi, index, 1, 1, buf, NULL ));\n}\n\n/* vendor write request */\ninline uint8_t PL2303::vendorWrite( uint8_t val_lo, uint8_t val_hi, uint8_t index )\n{\n        return( pUsb->ctrlReq(bAddress, 0, VENDOR_WRITE_REQUEST_TYPE, VENDOR_WRITE_REQUEST, val_lo, val_hi, index, 0, 0, NULL, NULL ));\n}\n#endif\n\n#endif // __CDCPROLIFIC_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/confdescparser.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(_usb_h_) || defined(__CONFDESCPARSER_H__)\n#error \"Never include confdescparser.h directly; include Usb.h instead\"\n#else\n\n#define __CONFDESCPARSER_H__\n\nclass UsbConfigXtracter {\npublic:\n        //virtual void ConfigXtract(const USB_CONFIGURATION_DESCRIPTOR *conf) = 0;\n        //virtual void InterfaceXtract(uint8_t conf, const USB_INTERFACE_DESCRIPTOR *iface) = 0;\n\n        virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep) {\n        };\n};\n\n#define CP_MASK_COMPARE_CLASS                   1\n#define CP_MASK_COMPARE_SUBCLASS                2\n#define CP_MASK_COMPARE_PROTOCOL                4\n#define CP_MASK_COMPARE_ALL                     7\n\n// Configuration Descriptor Parser Class Template\n\ntemplate <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>\nclass ConfigDescParser : public USBReadParser {\n        UsbConfigXtracter *theXtractor;\n        MultiValueBuffer theBuffer;\n        MultiByteValueParser valParser;\n        ByteSkipper theSkipper;\n        uint8_t varBuffer[16 /*sizeof(USB_CONFIGURATION_DESCRIPTOR)*/];\n\n        uint8_t stateParseDescr; // ParseDescriptor state\n\n        uint8_t dscrLen; // Descriptor length\n        uint8_t dscrType; // Descriptor type\n\n        bool isGoodInterface; // Apropriate interface flag\n        uint8_t confValue; // Configuration value\n        uint8_t protoValue; // Protocol value\n        uint8_t ifaceNumber; // Interface number\n        uint8_t ifaceAltSet; // Interface alternate settings\n\n        bool UseOr;\n        bool ParseDescriptor(uint8_t **pp, uint16_t *pcntdn);\n        void PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc);\n\npublic:\n\n        void SetOR(void) {\n                UseOr = true;\n        }\n        ConfigDescParser(UsbConfigXtracter *xtractor);\n        void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset);\n};\n\ntemplate <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>\nConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ConfigDescParser(UsbConfigXtracter *xtractor) :\ntheXtractor(xtractor),\nstateParseDescr(0),\ndscrLen(0),\ndscrType(0),\nUseOr(false) {\n        theBuffer.pValue = varBuffer;\n        valParser.Initialize(&theBuffer);\n        theSkipper.Initialize(&theBuffer);\n};\n\ntemplate <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>\nvoid ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset) {\n        uint16_t cntdn = (uint16_t)len;\n        uint8_t *p = (uint8_t*)pbuf;\n\n        while(cntdn)\n                if(!ParseDescriptor(&p, &cntdn))\n                        return;\n}\n\n/* Parser for the configuration descriptor. Takes values for class, subclass, protocol fields in interface descriptor and\n  compare masks for them. When the match is found, calls EndpointXtract passing buffer containing endpoint descriptor */\ntemplate <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>\nbool ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::ParseDescriptor(uint8_t **pp, uint16_t *pcntdn) {\n        USB_CONFIGURATION_DESCRIPTOR* ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR*>(varBuffer);\n        USB_INTERFACE_DESCRIPTOR* uid = reinterpret_cast<USB_INTERFACE_DESCRIPTOR*>(varBuffer);\n        switch(stateParseDescr) {\n                case 0:\n                        theBuffer.valueSize = 2;\n                        valParser.Initialize(&theBuffer);\n                        stateParseDescr = 1;\n                case 1:\n                        if(!valParser.Parse(pp, pcntdn))\n                                return false;\n                        dscrLen = *((uint8_t*)theBuffer.pValue);\n                        dscrType = *((uint8_t*)theBuffer.pValue + 1);\n                        stateParseDescr = 2;\n                case 2:\n                        // This is a sort of hack. Assuming that two bytes are all ready in the buffer\n                        //      the pointer is positioned two bytes ahead in order for the rest of descriptor\n                        //      to be read right after the size and the type fields.\n                        // This should be used carefully. varBuffer should be used directly to handle data\n                        //      in the buffer.\n                        theBuffer.pValue = varBuffer + 2;\n                        stateParseDescr = 3;\n                case 3:\n                        switch(dscrType) {\n                                case USB_DESCRIPTOR_INTERFACE:\n                                        isGoodInterface = false;\n                                case USB_DESCRIPTOR_CONFIGURATION:\n                                        theBuffer.valueSize = sizeof (USB_CONFIGURATION_DESCRIPTOR) - 2;\n                                        break;\n                                case USB_DESCRIPTOR_ENDPOINT:\n                                        theBuffer.valueSize = sizeof (USB_ENDPOINT_DESCRIPTOR) - 2;\n                                        break;\n                                case HID_DESCRIPTOR_HID:\n                                        theBuffer.valueSize = dscrLen - 2;\n                                        break;\n                        }\n                        valParser.Initialize(&theBuffer);\n                        stateParseDescr = 4;\n                case 4:\n                        switch(dscrType) {\n                                case USB_DESCRIPTOR_CONFIGURATION:\n                                        if(!valParser.Parse(pp, pcntdn))\n                                                return false;\n                                        confValue = ucd->bConfigurationValue;\n                                        break;\n                                case USB_DESCRIPTOR_INTERFACE:\n                                        if(!valParser.Parse(pp, pcntdn))\n                                                return false;\n                                        if((MASK & CP_MASK_COMPARE_CLASS) && uid->bInterfaceClass != CLASS_ID)\n                                                break;\n                                        if((MASK & CP_MASK_COMPARE_SUBCLASS) && uid->bInterfaceSubClass != SUBCLASS_ID)\n                                                break;\n                                        if(UseOr) {\n                                                if((!((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol)))\n                                                        break;\n                                        } else {\n                                                if((MASK & CP_MASK_COMPARE_PROTOCOL) && uid->bInterfaceProtocol != PROTOCOL_ID)\n                                                        break;\n                                        }\n                                        isGoodInterface = true;\n                                        ifaceNumber = uid->bInterfaceNumber;\n                                        ifaceAltSet = uid->bAlternateSetting;\n                                        protoValue = uid->bInterfaceProtocol;\n                                        break;\n                                case USB_DESCRIPTOR_ENDPOINT:\n                                        if(!valParser.Parse(pp, pcntdn))\n                                                return false;\n                                        if(isGoodInterface)\n                                                if(theXtractor)\n                                                        theXtractor->EndpointXtract(confValue, ifaceNumber, ifaceAltSet, protoValue, (USB_ENDPOINT_DESCRIPTOR*)varBuffer);\n                                        break;\n                                        //case HID_DESCRIPTOR_HID:\n                                        //      if (!valParser.Parse(pp, pcntdn))\n                                        //              return false;\n                                        //      PrintHidDescriptor((const USB_HID_DESCRIPTOR*)varBuffer);\n                                        //      break;\n                                default:\n                                        if(!theSkipper.Skip(pp, pcntdn, dscrLen - 2))\n                                                return false;\n                        }\n                        theBuffer.pValue = varBuffer;\n                        stateParseDescr = 0;\n        }\n        return true;\n}\n\ntemplate <const uint8_t CLASS_ID, const uint8_t SUBCLASS_ID, const uint8_t PROTOCOL_ID, const uint8_t MASK>\nvoid ConfigDescParser<CLASS_ID, SUBCLASS_ID, PROTOCOL_ID, MASK>::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc) {\n        Notify(PSTR(\"\\r\\n\\r\\nHID Descriptor:\\r\\n\"), 0x80);\n        Notify(PSTR(\"bDescLength:\\t\\t\"), 0x80);\n        PrintHex<uint8_t > (pDesc->bLength, 0x80);\n\n        Notify(PSTR(\"\\r\\nbDescriptorType:\\t\"), 0x80);\n        PrintHex<uint8_t > (pDesc->bDescriptorType, 0x80);\n\n        Notify(PSTR(\"\\r\\nbcdHID:\\t\\t\\t\"), 0x80);\n        PrintHex<uint16_t > (pDesc->bcdHID, 0x80);\n\n        Notify(PSTR(\"\\r\\nbCountryCode:\\t\\t\"), 0x80);\n        PrintHex<uint8_t > (pDesc->bCountryCode, 0x80);\n\n        Notify(PSTR(\"\\r\\nbNumDescriptors:\\t\"), 0x80);\n        PrintHex<uint8_t > (pDesc->bNumDescriptors, 0x80);\n\n        for(uint8_t i = 0; i < pDesc->bNumDescriptors; i++) {\n                HID_CLASS_DESCRIPTOR_LEN_AND_TYPE *pLT = (HID_CLASS_DESCRIPTOR_LEN_AND_TYPE*)&(pDesc->bDescrType);\n\n                Notify(PSTR(\"\\r\\nbDescrType:\\t\\t\"), 0x80);\n                PrintHex<uint8_t > (pLT[i].bDescrType, 0x80);\n\n                Notify(PSTR(\"\\r\\nwDescriptorLength:\\t\"), 0x80);\n                PrintHex<uint16_t > (pLT[i].wDescriptorLength, 0x80);\n        }\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n}\n\n\n#endif // __CONFDESCPARSER_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/controllerEnums.h",
    "content": "/* Copyright (C) 2013 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _controllerenums_h\n#define _controllerenums_h\n\n/**\n * This header file is used to store different enums for the controllers,\n * This is necessary so all the different libraries can be used at once.\n */\n\n/** Enum used to turn on the LEDs on the different controllers. */\nenum LEDEnum {\n        OFF = 0,\n        LED1 = 1,\n        LED2 = 2,\n        LED3 = 3,\n        LED4 = 4,\n\n        LED5 = 5,\n        LED6 = 6,\n        LED7 = 7,\n        LED8 = 8,\n        LED9 = 9,\n        LED10 = 10,\n        /** Used to blink all LEDs on the Xbox controller */\n        ALL = 5,\n};\n\n/** Used to set the colors of the Move and PS4 controller. */\nenum ColorsEnum {\n        /** r = 255, g = 0, b = 0 */\n        Red = 0xFF0000,\n        /** r = 0, g = 255, b = 0 */\n        Green = 0xFF00,\n        /** r = 0, g = 0, b = 255 */\n        Blue = 0xFF,\n\n        /** r = 255, g = 235, b = 4 */\n        Yellow = 0xFFEB04,\n        /** r = 0, g = 255, b = 255 */\n        Lightblue = 0xFFFF,\n        /** r = 255, g = 0, b = 255 */\n        Purble = 0xFF00FF,\n\n        /** r = 255, g = 255, b = 255 */\n        White = 0xFFFFFF,\n        /** r = 0, g = 0, b = 0 */\n        Off = 0x00,\n};\n\nenum RumbleEnum {\n        RumbleHigh = 0x10,\n        RumbleLow = 0x20,\n};\n\n/** This enum is used to read all the different buttons on the different controllers */\nenum ButtonEnum {\n        /**@{*/\n        /** These buttons are available on all the the controllers */\n        UP = 0,\n        RIGHT = 1,\n        DOWN = 2,\n        LEFT = 3,\n        /**@}*/\n\n        /**@{*/\n        /** Wii buttons */\n        PLUS = 5,\n        TWO = 6,\n        ONE = 7,\n        MINUS = 8,\n        HOME = 9,\n        Z = 10,\n        C = 11,\n        B = 12,\n        A = 13,\n        /**@}*/\n\n        /**@{*/\n        /** These are only available on the Wii U Pro Controller */\n        L = 16,\n        R = 17,\n        ZL = 18,\n        ZR = 19,\n        /**@}*/\n\n        /**@{*/\n        /** PS3 controllers buttons */\n        SELECT = 4,\n        START = 5,\n        L3 = 6,\n        R3 = 7,\n\n        L2 = 8,\n        R2 = 9,\n        L1 = 10,\n        R1 = 11,\n        TRIANGLE = 12,\n        CIRCLE = 13,\n        CROSS = 14,\n        SQUARE = 15,\n\n        PS = 16,\n\n        MOVE = 17, // Covers 12 bits - we only need to read the top 8\n        T = 18, // Covers 12 bits - we only need to read the top 8\n        /**@}*/\n\n        /** PS4 controllers buttons - SHARE and OPTIONS are present instead of SELECT and START */\n        SHARE = 4,\n        OPTIONS = 5,\n        TOUCHPAD = 17,\n        /**@}*/\n\n        /**@{*/\n        /** Xbox buttons */\n        BACK = 4,\n        X = 14,\n        Y = 15,\n        XBOX = 16,\n        SYNC = 17,\n        BLACK = 8, // Available on the original Xbox controller\n        WHITE = 9, // Available on the original Xbox controller\n        /**@}*/\n\n        /** PS Buzz controllers */\n        RED = 0,\n        YELLOW = 1,\n        GREEN = 2,\n        ORANGE = 3,\n        BLUE = 4,\n        /**@}*/\n};\n\n/** Joysticks on the PS3 and Xbox controllers. */\nenum AnalogHatEnum {\n        /** Left joystick x-axis */\n        LeftHatX = 0,\n        /** Left joystick y-axis */\n        LeftHatY = 1,\n        /** Right joystick x-axis */\n        RightHatX = 2,\n        /** Right joystick y-axis */\n        RightHatY = 3,\n};\n\n/**\n * Sensors inside the Sixaxis Dualshock 3, Move controller and PS4 controller.\n * <B>Note:</B> that the location is shifted 9 when it's connected via USB on the PS3 controller.\n */\nenum SensorEnum {\n        /** Accelerometer values */\n        aX = 50, aY = 52, aZ = 54,\n        /** Gyro z-axis */\n        gZ = 56,\n        gX, gY, // These are not available on the PS3 controller\n\n        /** Accelerometer x-axis */\n        aXmove = 28,\n        /** Accelerometer z-axis */\n        aZmove = 30,\n        /** Accelerometer y-axis */\n        aYmove = 32,\n\n        /** Gyro x-axis */\n        gXmove = 40,\n        /** Gyro z-axis */\n        gZmove = 42,\n        /** Gyro y-axis */\n        gYmove = 44,\n\n        /** Temperature sensor */\n        tempMove = 46,\n\n        /** Magnetometer x-axis */\n        mXmove = 47,\n        /** Magnetometer z-axis */\n        mZmove = 49,\n        /** Magnetometer y-axis */\n        mYmove = 50,\n};\n\n/** Used to get the angle calculated using the PS3 controller and PS4 controller. */\nenum AngleEnum {\n        Pitch = 0x01,\n        Roll = 0x02,\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/BTHID/BTHID.ino",
    "content": "/*\n Example sketch for the HID Bluetooth library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <BTHID.h>\n#include <usbhub.h>\n#include \"KeyboardParser.h\"\n#include \"MouseParser.h\"\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\n\n/* You can create the instance of the class in two ways */\n// This will start an inquiry and then pair with your device - you only have to do this once\n// If you are using a Bluetooth keyboard, then you should type in the password on the keypad and then press enter\nBTHID bthid(&Btd, PAIR, \"0000\");\n\n// After that you can simply create the instance like so and then press any button on the device\n//BTHID hid(&Btd);\n\nKbdRptParser keyboardPrs;\nMouseRptParser mousePrs;\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); // Halt\n  }\n\n  bthid.SetReportParser(KEYBOARD_PARSER_ID, (HIDReportParser*)&keyboardPrs);\n  bthid.SetReportParser(MOUSE_PARSER_ID, (HIDReportParser*)&mousePrs);\n\n  // If \"Boot Protocol Mode\" does not work, then try \"Report Protocol Mode\"\n  // If that does not work either, then uncomment PRINTREPORT in BTHID.cpp to see the raw report\n  bthid.setProtocolMode(HID_BOOT_PROTOCOL); // Boot Protocol Mode\n  //bthid.setProtocolMode(HID_RPT_PROTOCOL); // Report Protocol Mode\n\n  Serial.print(F(\"\\r\\nHID Bluetooth Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/BTHID/KeyboardParser.h",
    "content": "#ifndef __kbdrptparser_h_\n#define __kbdrptparser_h_\n\nclass KbdRptParser : public KeyboardReportParser {\n  protected:\n    virtual uint8_t HandleLockingKeys(HID *hid, uint8_t key);\n    virtual void OnControlKeysChanged(uint8_t before, uint8_t after);\n    virtual void OnKeyDown(uint8_t mod, uint8_t key);\n    virtual void OnKeyUp(uint8_t mod, uint8_t key);\n    virtual void OnKeyPressed(uint8_t key);\n\n  private:\n    void PrintKey(uint8_t mod, uint8_t key);\n};\n\nuint8_t KbdRptParser::HandleLockingKeys(HID *hid, uint8_t key) {\n  uint8_t old_keys = kbdLockingKeys.bLeds;\n\n  switch (key) {\n    case UHS_HID_BOOT_KEY_NUM_LOCK:\n      Serial.println(F(\"Num lock\"));\n      kbdLockingKeys.kbdLeds.bmNumLock = ~kbdLockingKeys.kbdLeds.bmNumLock;\n      break;\n    case UHS_HID_BOOT_KEY_CAPS_LOCK:\n      Serial.println(F(\"Caps lock\"));\n      kbdLockingKeys.kbdLeds.bmCapsLock = ~kbdLockingKeys.kbdLeds.bmCapsLock;\n      break;\n    case UHS_HID_BOOT_KEY_SCROLL_LOCK:\n      Serial.println(F(\"Scroll lock\"));\n      kbdLockingKeys.kbdLeds.bmScrollLock = ~kbdLockingKeys.kbdLeds.bmScrollLock;\n      break;\n  }\n\n  if (old_keys != kbdLockingKeys.bLeds && hid) {\n    BTHID *pBTHID = reinterpret_cast<BTHID *> (hid); // A cast the other way around is done in BTHID.cpp\n    pBTHID->setLeds(kbdLockingKeys.bLeds); // Update the LEDs on the keyboard\n  }\n\n  return 0;\n};\n\nvoid KbdRptParser::PrintKey(uint8_t m, uint8_t key) {\n  MODIFIERKEYS mod;\n  *((uint8_t*)&mod) = m;\n  Serial.print((mod.bmLeftCtrl == 1) ? F(\"C\") : F(\" \"));\n  Serial.print((mod.bmLeftShift == 1) ? F(\"S\") : F(\" \"));\n  Serial.print((mod.bmLeftAlt == 1) ? F(\"A\") : F(\" \"));\n  Serial.print((mod.bmLeftGUI == 1) ? F(\"G\") : F(\" \"));\n\n  Serial.print(F(\" >\"));\n  PrintHex<uint8_t>(key, 0x80);\n  Serial.print(F(\"< \"));\n\n  Serial.print((mod.bmRightCtrl == 1) ? F(\"C\") : F(\" \"));\n  Serial.print((mod.bmRightShift == 1) ? F(\"S\") : F(\" \"));\n  Serial.print((mod.bmRightAlt == 1) ? F(\"A\") : F(\" \"));\n  Serial.println((mod.bmRightGUI == 1) ? F(\"G\") : F(\" \"));\n};\n\nvoid KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) {\n  Serial.print(F(\"DN \"));\n  PrintKey(mod, key);\n  uint8_t c = OemToAscii(mod, key);\n\n  if (c)\n    OnKeyPressed(c);\n};\n\nvoid KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) {\n  MODIFIERKEYS beforeMod;\n  *((uint8_t*)&beforeMod) = before;\n\n  MODIFIERKEYS afterMod;\n  *((uint8_t*)&afterMod) = after;\n\n  if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl)\n    Serial.println(F(\"LeftCtrl changed\"));\n  if (beforeMod.bmLeftShift != afterMod.bmLeftShift)\n    Serial.println(F(\"LeftShift changed\"));\n  if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt)\n    Serial.println(F(\"LeftAlt changed\"));\n  if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI)\n    Serial.println(F(\"LeftGUI changed\"));\n\n  if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl)\n    Serial.println(F(\"RightCtrl changed\"));\n  if (beforeMod.bmRightShift != afterMod.bmRightShift)\n    Serial.println(F(\"RightShift changed\"));\n  if (beforeMod.bmRightAlt != afterMod.bmRightAlt)\n    Serial.println(F(\"RightAlt changed\"));\n  if (beforeMod.bmRightGUI != afterMod.bmRightGUI)\n    Serial.println(F(\"RightGUI changed\"));\n};\n\nvoid KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key) {\n  Serial.print(F(\"UP \"));\n  PrintKey(mod, key);\n};\n\nvoid KbdRptParser::OnKeyPressed(uint8_t key) {\n  Serial.print(F(\"ASCII: \"));\n  Serial.println((char)key);\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/BTHID/MouseParser.h",
    "content": "#ifndef __mouserptparser_h__\n#define __mouserptparser_h__\n\nclass MouseRptParser : public MouseReportParser {\n  protected:\n    virtual void OnMouseMove(MOUSEINFO *mi);\n    virtual void OnLeftButtonUp(MOUSEINFO *mi);\n    virtual void OnLeftButtonDown(MOUSEINFO *mi);\n    virtual void OnRightButtonUp(MOUSEINFO *mi);\n    virtual void OnRightButtonDown(MOUSEINFO *mi);\n    virtual void OnMiddleButtonUp(MOUSEINFO *mi);\n    virtual void OnMiddleButtonDown(MOUSEINFO *mi);\n};\n\nvoid MouseRptParser::OnMouseMove(MOUSEINFO *mi) {\n  Serial.print(F(\"dx=\"));\n  Serial.print(mi->dX, DEC);\n  Serial.print(F(\" dy=\"));\n  Serial.println(mi->dY, DEC);\n};\n\nvoid MouseRptParser::OnLeftButtonUp(MOUSEINFO *mi) {\n  Serial.println(F(\"L Butt Up\"));\n};\n\nvoid MouseRptParser::OnLeftButtonDown(MOUSEINFO *mi) {\n  Serial.println(F(\"L Butt Dn\"));\n};\n\nvoid MouseRptParser::OnRightButtonUp(MOUSEINFO *mi) {\n  Serial.println(F(\"R Butt Up\"));\n};\n\nvoid MouseRptParser::OnRightButtonDown(MOUSEINFO *mi) {\n  Serial.println(F(\"R Butt Dn\"));\n};\n\nvoid MouseRptParser::OnMiddleButtonUp(MOUSEINFO *mi) {\n  Serial.println(F(\"M Butt Up\"));\n};\n\nvoid MouseRptParser::OnMiddleButtonDown(MOUSEINFO *mi) {\n  Serial.println(F(\"M Butt Dn\"));\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/PS3BT/PS3BT.ino",
    "content": "/*\n Example sketch for the PS3 Bluetooth library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <PS3BT.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\n\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\n/* You can create the instance of the class in two ways */\nPS3BT PS3(&Btd); // This will just create the instance\n//PS3BT PS3(&Btd, 0x00, 0x15, 0x83, 0x3D, 0x0A, 0x57); // This will also store the bluetooth address - this can be obtained from the dongle when running the sketch\n\nbool printTemperature;\nbool printAngle;\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nPS3 Bluetooth Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n\n  if (PS3.PS3Connected || PS3.PS3NavigationConnected) {\n    if (PS3.getAnalogHat(LeftHatX) > 137 || PS3.getAnalogHat(LeftHatX) < 117 || PS3.getAnalogHat(LeftHatY) > 137 || PS3.getAnalogHat(LeftHatY) < 117 || PS3.getAnalogHat(RightHatX) > 137 || PS3.getAnalogHat(RightHatX) < 117 || PS3.getAnalogHat(RightHatY) > 137 || PS3.getAnalogHat(RightHatY) < 117) {\n      Serial.print(F(\"\\r\\nLeftHatX: \"));\n      Serial.print(PS3.getAnalogHat(LeftHatX));\n      Serial.print(F(\"\\tLeftHatY: \"));\n      Serial.print(PS3.getAnalogHat(LeftHatY));\n      if (PS3.PS3Connected) { // The Navigation controller only have one joystick\n        Serial.print(F(\"\\tRightHatX: \"));\n        Serial.print(PS3.getAnalogHat(RightHatX));\n        Serial.print(F(\"\\tRightHatY: \"));\n        Serial.print(PS3.getAnalogHat(RightHatY));\n      }\n    }\n\n    // Analog button values can be read from almost all buttons\n    if (PS3.getAnalogButton(L2) || PS3.getAnalogButton(R2)) {\n      Serial.print(F(\"\\r\\nL2: \"));\n      Serial.print(PS3.getAnalogButton(L2));\n      if (PS3.PS3Connected) {\n        Serial.print(F(\"\\tR2: \"));\n        Serial.print(PS3.getAnalogButton(R2));\n      }\n    }\n    if (PS3.getButtonClick(PS)) {\n      Serial.print(F(\"\\r\\nPS\"));\n      PS3.disconnect();\n    }\n    else {\n      if (PS3.getButtonClick(TRIANGLE))\n        Serial.print(F(\"\\r\\nTraingle\"));\n      if (PS3.getButtonClick(CIRCLE))\n        Serial.print(F(\"\\r\\nCircle\"));\n      if (PS3.getButtonClick(CROSS))\n        Serial.print(F(\"\\r\\nCross\"));\n      if (PS3.getButtonClick(SQUARE))\n        Serial.print(F(\"\\r\\nSquare\"));\n\n      if (PS3.getButtonClick(UP)) {\n        Serial.print(F(\"\\r\\nUp\"));\n        if (PS3.PS3Connected) {\n          PS3.setLedOff();\n          PS3.setLedOn(LED4);\n        }\n      }\n      if (PS3.getButtonClick(RIGHT)) {\n        Serial.print(F(\"\\r\\nRight\"));\n        if (PS3.PS3Connected) {\n          PS3.setLedOff();\n          PS3.setLedOn(LED1);\n        }\n      }\n      if (PS3.getButtonClick(DOWN)) {\n        Serial.print(F(\"\\r\\nDown\"));\n        if (PS3.PS3Connected) {\n          PS3.setLedOff();\n          PS3.setLedOn(LED2);\n        }\n      }\n      if (PS3.getButtonClick(LEFT)) {\n        Serial.print(F(\"\\r\\nLeft\"));\n        if (PS3.PS3Connected) {\n          PS3.setLedOff();\n          PS3.setLedOn(LED3);\n        }\n      }\n\n      if (PS3.getButtonClick(L1))\n        Serial.print(F(\"\\r\\nL1\"));\n      if (PS3.getButtonClick(L3))\n        Serial.print(F(\"\\r\\nL3\"));\n      if (PS3.getButtonClick(R1))\n        Serial.print(F(\"\\r\\nR1\"));\n      if (PS3.getButtonClick(R3))\n        Serial.print(F(\"\\r\\nR3\"));\n\n      if (PS3.getButtonClick(SELECT)) {\n        Serial.print(F(\"\\r\\nSelect - \"));\n        PS3.printStatusString();\n      }\n      if (PS3.getButtonClick(START)) {\n        Serial.print(F(\"\\r\\nStart\"));\n        printAngle = !printAngle;\n      }\n    }\n#if 0 // Set this to 1 in order to see the angle of the controller\n    if (printAngle) {\n      Serial.print(F(\"\\r\\nPitch: \"));\n      Serial.print(PS3.getAngle(Pitch));\n      Serial.print(F(\"\\tRoll: \"));\n      Serial.print(PS3.getAngle(Roll));\n    }\n#endif\n  }\n#if 0 // Set this to 1 in order to enable support for the Playstation Move controller\n  else if (PS3.PS3MoveConnected) {\n    if (PS3.getAnalogButton(T)) {\n      Serial.print(F(\"\\r\\nT: \"));\n      Serial.print(PS3.getAnalogButton(T));\n    }\n    if (PS3.getButtonClick(PS)) {\n      Serial.print(F(\"\\r\\nPS\"));\n      PS3.disconnect();\n    }\n    else {\n      if (PS3.getButtonClick(SELECT)) {\n        Serial.print(F(\"\\r\\nSelect\"));\n        printTemperature = !printTemperature;\n      }\n      if (PS3.getButtonClick(START)) {\n        Serial.print(F(\"\\r\\nStart\"));\n        printAngle = !printAngle;\n      }\n      if (PS3.getButtonClick(TRIANGLE)) {\n        Serial.print(F(\"\\r\\nTriangle\"));\n        PS3.moveSetBulb(Red);\n      }\n      if (PS3.getButtonClick(CIRCLE)) {\n        Serial.print(F(\"\\r\\nCircle\"));\n        PS3.moveSetBulb(Green);\n      }\n      if (PS3.getButtonClick(SQUARE)) {\n        Serial.print(F(\"\\r\\nSquare\"));\n        PS3.moveSetBulb(Blue);\n      }\n      if (PS3.getButtonClick(CROSS)) {\n        Serial.print(F(\"\\r\\nCross\"));\n        PS3.moveSetBulb(Yellow);\n      }\n      if (PS3.getButtonClick(MOVE)) {\n        PS3.moveSetBulb(Off);\n        Serial.print(F(\"\\r\\nMove\"));\n        Serial.print(F(\" - \"));\n        PS3.printStatusString();\n      }\n    }\n    if (printAngle) {\n      Serial.print(F(\"\\r\\nPitch: \"));\n      Serial.print(PS3.getAngle(Pitch));\n      Serial.print(F(\"\\tRoll: \"));\n      Serial.print(PS3.getAngle(Roll));\n    }\n    else if (printTemperature) {\n      Serial.print(F(\"\\r\\nTemperature: \"));\n      Serial.print(PS3.getTemperature());\n    }\n  }\n#endif\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/PS3Multi/PS3Multi.ino",
    "content": "/*\n Example sketch for the PS3 Bluetooth library - developed by Kristian Lauszus\n This example show how one can use multiple controllers with the library\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <PS3BT.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\n\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\nPS3BT *PS3[2]; // We will use this pointer to store the two instance, you can easily make it larger if you like, but it will use a lot of RAM!\nconst uint8_t length = sizeof(PS3) / sizeof(PS3[0]); // Get the lenght of the array\nbool printAngle[length];\nbool oldControllerState[length];\n\nvoid setup() {\n  for (uint8_t i = 0; i < length; i++) {\n    PS3[i] = new PS3BT(&Btd); // Create the instances\n    PS3[i]->attachOnInit(onInit); // onInit() is called upon a new connection - you can call the function whatever you like\n  }\n\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nPS3 Bluetooth Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n\n  for (uint8_t i = 0; i < length; i++) {\n    if (PS3[i]->PS3Connected || PS3[i]->PS3NavigationConnected) {\n      if (PS3[i]->getAnalogHat(LeftHatX) > 137 || PS3[i]->getAnalogHat(LeftHatX) < 117 || PS3[i]->getAnalogHat(LeftHatY) > 137 || PS3[i]->getAnalogHat(LeftHatY) < 117 || PS3[i]->getAnalogHat(RightHatX) > 137 || PS3[i]->getAnalogHat(RightHatX) < 117 || PS3[i]->getAnalogHat(RightHatY) > 137 || PS3[i]->getAnalogHat(RightHatY) < 117) {\n        Serial.print(F(\"\\r\\nLeftHatX: \"));\n        Serial.print(PS3[i]->getAnalogHat(LeftHatX));\n        Serial.print(F(\"\\tLeftHatY: \"));\n        Serial.print(PS3[i]->getAnalogHat(LeftHatY));\n        if (PS3[i]->PS3Connected) { // The Navigation controller only have one joystick\n          Serial.print(F(\"\\tRightHatX: \"));\n          Serial.print(PS3[i]->getAnalogHat(RightHatX));\n          Serial.print(F(\"\\tRightHatY: \"));\n          Serial.print(PS3[i]->getAnalogHat(RightHatY));\n        }\n      }\n      //Analog button values can be read from almost all buttons\n      if (PS3[i]->getAnalogButton(L2) || PS3[i]->getAnalogButton(R2)) {\n        Serial.print(F(\"\\r\\nL2: \"));\n        Serial.print(PS3[i]->getAnalogButton(L2));\n        if (PS3[i]->PS3Connected) {\n          Serial.print(F(\"\\tR2: \"));\n          Serial.print(PS3[i]->getAnalogButton(R2));\n        }\n      }\n      if (PS3[i]->getButtonClick(PS)) {\n        Serial.print(F(\"\\r\\nPS\"));\n        PS3[i]->disconnect();\n        oldControllerState[i] = false; // Reset value\n      }\n      else {\n        if (PS3[i]->getButtonClick(TRIANGLE))\n          Serial.print(F(\"\\r\\nTraingle\"));\n        if (PS3[i]->getButtonClick(CIRCLE))\n          Serial.print(F(\"\\r\\nCircle\"));\n        if (PS3[i]->getButtonClick(CROSS))\n          Serial.print(F(\"\\r\\nCross\"));\n        if (PS3[i]->getButtonClick(SQUARE))\n          Serial.print(F(\"\\r\\nSquare\"));\n\n        if (PS3[i]->getButtonClick(UP)) {\n          Serial.print(F(\"\\r\\nUp\"));\n          if (PS3[i]->PS3Connected) {\n            PS3[i]->setLedOff();\n            PS3[i]->setLedOn(LED4);\n          }\n        }\n        if (PS3[i]->getButtonClick(RIGHT)) {\n          Serial.print(F(\"\\r\\nRight\"));\n          if (PS3[i]->PS3Connected) {\n            PS3[i]->setLedOff();\n            PS3[i]->setLedOn(LED1);\n          }\n        }\n        if (PS3[i]->getButtonClick(DOWN)) {\n          Serial.print(F(\"\\r\\nDown\"));\n          if (PS3[i]->PS3Connected) {\n            PS3[i]->setLedOff();\n            PS3[i]->setLedOn(LED2);\n          }\n        }\n        if (PS3[i]->getButtonClick(LEFT)) {\n          Serial.print(F(\"\\r\\nLeft\"));\n          if (PS3[i]->PS3Connected) {\n            PS3[i]->setLedOff();\n            PS3[i]->setLedOn(LED3);\n          }\n        }\n\n        if (PS3[i]->getButtonClick(L1))\n          Serial.print(F(\"\\r\\nL1\"));\n        if (PS3[i]->getButtonClick(L3))\n          Serial.print(F(\"\\r\\nL3\"));\n        if (PS3[i]->getButtonClick(R1))\n          Serial.print(F(\"\\r\\nR1\"));\n        if (PS3[i]->getButtonClick(R3))\n          Serial.print(F(\"\\r\\nR3\"));\n\n        if (PS3[i]->getButtonClick(SELECT)) {\n          Serial.print(F(\"\\r\\nSelect - \"));\n          PS3[i]->printStatusString();\n        }\n        if (PS3[i]->getButtonClick(START)) {\n          Serial.print(F(\"\\r\\nStart\"));\n          printAngle[i] = !printAngle[i];\n        }\n      }\n      if (printAngle[i]) {\n        Serial.print(F(\"\\r\\nPitch: \"));\n        Serial.print(PS3[i]->getAngle(Pitch));\n        Serial.print(F(\"\\tRoll: \"));\n        Serial.print(PS3[i]->getAngle(Roll));\n      }\n    }\n    /* I have removed the PS3 Move code as an Uno will run out of RAM if it's included */\n    //else if(PS3[i]->PS3MoveConnected) {\n  }\n}\n\nvoid onInit() {\n  for (uint8_t i = 0; i < length; i++) {\n    if ((PS3[i]->PS3Connected || PS3[i]->PS3NavigationConnected) && !oldControllerState[i]) {\n      oldControllerState[i] = true; // Used to check which is the new controller\n      PS3[i]->setLedOn((LEDEnum)(i + 1)); // Cast directly to LEDEnum - see: \"controllerEnums.h\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/PS3SPP/PS3SPP.ino",
    "content": "/*\n Example sketch for the Bluetooth library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n\n This example show how one can combine all the difference Bluetooth services in one single code.\n Note:\n You will need a Arduino Mega 1280/2560 to run this sketch,\n as a normal Arduino (Uno, Duemilanove etc.) doesn't have enough SRAM and FLASH\n */\n\n#include <PS3BT.h>\n#include <SPP.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\n\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\n\n/* You can create the instances of the bluetooth services in two ways */\nSPP SerialBT(&Btd); // This will set the name to the defaults: \"Arduino\" and the pin to \"0000\"\n//SPP SerialBTBT(&Btd,\"Lauszus's Arduino\",\"0000\"); // You can also set the name and pin like so\nPS3BT PS3(&Btd); // This will just create the instance\n//PS3BT PS3(&Btd, 0x00, 0x15, 0x83, 0x3D, 0x0A, 0x57); // This will also store the bluetooth address - this can be obtained from the dongle when running the sketch\n\nbool firstMessage = true;\nString output = \"\"; // We will store the data in this string\n\nvoid setup() {\n  Serial.begin(115200); // This wil lprint the debugging from the libraries\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nBluetooth Library Started\"));\n  output.reserve(200); // Reserve 200 bytes for the output string\n}\nvoid loop() {\n  Usb.Task(); // The SPP data is actually not send until this is called, one could call SerialBT.send() directly as well\n\n  if (SerialBT.connected) {\n    if (firstMessage) {\n      firstMessage = false;\n      SerialBT.println(F(\"Hello from Arduino\")); // Send welcome message\n    }\n    if (Serial.available())\n      SerialBT.write(Serial.read());\n    if (SerialBT.available())\n      Serial.write(SerialBT.read());\n  }\n  else\n    firstMessage = true;\n\n  if (PS3.PS3Connected || PS3.PS3NavigationConnected) {\n    output = \"\"; // Reset output string\n    if (PS3.getAnalogHat(LeftHatX) > 137 || PS3.getAnalogHat(LeftHatX) < 117 || PS3.getAnalogHat(LeftHatY) > 137 || PS3.getAnalogHat(LeftHatY) < 117 || PS3.getAnalogHat(RightHatX) > 137 || PS3.getAnalogHat(RightHatX) < 117 || PS3.getAnalogHat(RightHatY) > 137 || PS3.getAnalogHat(RightHatY) < 117) {\n      output += \"LeftHatX: \";\n      output += PS3.getAnalogHat(LeftHatX);\n      output += \"\\tLeftHatY: \";\n      output += PS3.getAnalogHat(LeftHatY);\n      if (PS3.PS3Connected) { // The Navigation controller only have one joystick\n        output += \"\\tRightHatX: \";\n        output += PS3.getAnalogHat(RightHatX);\n        output += \"\\tRightHatY: \";\n        output += PS3.getAnalogHat(RightHatY);\n      }\n    }\n    //Analog button values can be read from almost all buttons\n    if (PS3.getAnalogButton(L2) || PS3.getAnalogButton(R2)) {\n      if (output != \"\")\n        output += \"\\r\\n\";\n      output += \"L2: \";\n      output += PS3.getAnalogButton(L2);\n      if (PS3.PS3Connected) {\n        output += \"\\tR2: \";\n        output += PS3.getAnalogButton(R2);\n      }\n    }\n    if (output != \"\") {\n      Serial.println(output);\n      if (SerialBT.connected)\n        SerialBT.println(output);\n      output = \"\"; // Reset output string\n    }\n    if (PS3.getButtonClick(PS)) {\n      output += \" - PS\";\n      PS3.disconnect();\n    }\n    else {\n      if (PS3.getButtonClick(TRIANGLE))\n        output += \" - Traingle\";\n      if (PS3.getButtonClick(CIRCLE))\n        output += \" - Circle\";\n      if (PS3.getButtonClick(CROSS))\n        output += \" - Cross\";\n      if (PS3.getButtonClick(SQUARE))\n        output += \" - Square\";\n\n      if (PS3.getButtonClick(UP)) {\n        output += \" - Up\";\n        if (PS3.PS3Connected) {\n          PS3.setLedOff();\n          PS3.setLedOn(LED4);\n        }\n      }\n      if (PS3.getButtonClick(RIGHT)) {\n        output += \" - Right\";\n        if (PS3.PS3Connected) {\n          PS3.setLedOff();\n          PS3.setLedOn(LED1);\n        }\n      }\n      if (PS3.getButtonClick(DOWN)) {\n        output += \" - Down\";\n        if (PS3.PS3Connected) {\n          PS3.setLedOff();\n          PS3.setLedOn(LED2);\n        }\n      }\n      if (PS3.getButtonClick(LEFT)) {\n        output += \" - Left\";\n        if (PS3.PS3Connected) {\n          PS3.setLedOff();\n          PS3.setLedOn(LED3);\n        }\n      }\n\n      if (PS3.getButtonClick(L1))\n        output += \" - L1\";\n      if (PS3.getButtonClick(L3))\n        output += \" - L3\";\n      if (PS3.getButtonClick(R1))\n        output += \" - R1\";\n      if (PS3.getButtonClick(R3))\n        output += \" - R3\";\n\n      if (PS3.getButtonClick(SELECT)) {\n        output += \" - Select\";\n      }\n      if (PS3.getButtonClick(START))\n        output += \" - Start\";\n\n      if (output != \"\") {\n        String string = \"PS3 Controller\" + output;\n        Serial.println(string);\n        if (SerialBT.connected)\n          SerialBT.println(string);\n      }\n    }\n    delay(10);\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/PS4BT/PS4BT.ino",
    "content": "/*\n Example sketch for the PS4 Bluetooth library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <PS4BT.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\n\n/* You can create the instance of the PS4BT class in two ways */\n// This will start an inquiry and then pair with the PS4 controller - you only have to do this once\n// You will need to hold down the PS and Share button at the same time, the PS4 controller will then start to blink rapidly indicating that it is in paring mode\nPS4BT PS4(&Btd, PAIR);\n\n// After that you can simply create the instance like so and then press the PS button on the device\n//PS4BT PS4(&Btd);\n\nbool printAngle, printTouch;\nuint8_t oldL2Value, oldR2Value;\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); // Halt\n  }\n  Serial.print(F(\"\\r\\nPS4 Bluetooth Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n\n  if (PS4.connected()) {\n    if (PS4.getAnalogHat(LeftHatX) > 137 || PS4.getAnalogHat(LeftHatX) < 117 || PS4.getAnalogHat(LeftHatY) > 137 || PS4.getAnalogHat(LeftHatY) < 117 || PS4.getAnalogHat(RightHatX) > 137 || PS4.getAnalogHat(RightHatX) < 117 || PS4.getAnalogHat(RightHatY) > 137 || PS4.getAnalogHat(RightHatY) < 117) {\n      Serial.print(F(\"\\r\\nLeftHatX: \"));\n      Serial.print(PS4.getAnalogHat(LeftHatX));\n      Serial.print(F(\"\\tLeftHatY: \"));\n      Serial.print(PS4.getAnalogHat(LeftHatY));\n      Serial.print(F(\"\\tRightHatX: \"));\n      Serial.print(PS4.getAnalogHat(RightHatX));\n      Serial.print(F(\"\\tRightHatY: \"));\n      Serial.print(PS4.getAnalogHat(RightHatY));\n    }\n\n    if (PS4.getAnalogButton(L2) || PS4.getAnalogButton(R2)) { // These are the only analog buttons on the PS4 controller\n      Serial.print(F(\"\\r\\nL2: \"));\n      Serial.print(PS4.getAnalogButton(L2));\n      Serial.print(F(\"\\tR2: \"));\n      Serial.print(PS4.getAnalogButton(R2));\n    }\n    if (PS4.getAnalogButton(L2) != oldL2Value || PS4.getAnalogButton(R2) != oldR2Value) // Only write value if it's different\n      PS4.setRumbleOn(PS4.getAnalogButton(L2), PS4.getAnalogButton(R2));\n    oldL2Value = PS4.getAnalogButton(L2);\n    oldR2Value = PS4.getAnalogButton(R2);\n\n    if (PS4.getButtonClick(PS)) {\n      Serial.print(F(\"\\r\\nPS\"));\n      PS4.disconnect();\n    }\n    else {\n      if (PS4.getButtonClick(TRIANGLE)) {\n        Serial.print(F(\"\\r\\nTraingle\"));\n        PS4.setRumbleOn(RumbleLow);\n      }\n      if (PS4.getButtonClick(CIRCLE)) {\n        Serial.print(F(\"\\r\\nCircle\"));\n        PS4.setRumbleOn(RumbleHigh);\n      }\n      if (PS4.getButtonClick(CROSS)) {\n        Serial.print(F(\"\\r\\nCross\"));\n        PS4.setLedFlash(10, 10); // Set it to blink rapidly\n      }\n      if (PS4.getButtonClick(SQUARE)) {\n        Serial.print(F(\"\\r\\nSquare\"));\n        PS4.setLedFlash(0, 0); // Turn off blinking\n      }\n\n      if (PS4.getButtonClick(UP)) {\n        Serial.print(F(\"\\r\\nUp\"));\n        PS4.setLed(Red);\n      } if (PS4.getButtonClick(RIGHT)) {\n        Serial.print(F(\"\\r\\nRight\"));\n        PS4.setLed(Blue);\n      } if (PS4.getButtonClick(DOWN)) {\n        Serial.print(F(\"\\r\\nDown\"));\n        PS4.setLed(Yellow);\n      } if (PS4.getButtonClick(LEFT)) {\n        Serial.print(F(\"\\r\\nLeft\"));\n        PS4.setLed(Green);\n      }\n\n      if (PS4.getButtonClick(L1))\n        Serial.print(F(\"\\r\\nL1\"));\n      if (PS4.getButtonClick(L3))\n        Serial.print(F(\"\\r\\nL3\"));\n      if (PS4.getButtonClick(R1))\n        Serial.print(F(\"\\r\\nR1\"));\n      if (PS4.getButtonClick(R3))\n        Serial.print(F(\"\\r\\nR3\"));\n\n      if (PS4.getButtonClick(SHARE))\n        Serial.print(F(\"\\r\\nShare\"));\n      if (PS4.getButtonClick(OPTIONS)) {\n        Serial.print(F(\"\\r\\nOptions\"));\n        printAngle = !printAngle;\n      }\n      if (PS4.getButtonClick(TOUCHPAD)) {\n        Serial.print(F(\"\\r\\nTouchpad\"));\n        printTouch = !printTouch;\n      }\n\n      if (printAngle) { // Print angle calculated using the accelerometer only\n        Serial.print(F(\"\\r\\nPitch: \"));\n        Serial.print(PS4.getAngle(Pitch));\n        Serial.print(F(\"\\tRoll: \"));\n        Serial.print(PS4.getAngle(Roll));\n      }\n\n      if (printTouch) { // Print the x, y coordinates of the touchpad\n        if (PS4.isTouching(0) || PS4.isTouching(1)) // Print newline and carriage return if any of the fingers are touching the touchpad\n          Serial.print(F(\"\\r\\n\"));\n        for (uint8_t i = 0; i < 2; i++) { // The touchpad track two fingers\n          if (PS4.isTouching(i)) { // Print the position of the finger if it is touching the touchpad\n            Serial.print(F(\"X\")); Serial.print(i + 1); Serial.print(F(\": \"));\n            Serial.print(PS4.getX(i));\n            Serial.print(F(\"\\tY\")); Serial.print(i + 1); Serial.print(F(\": \"));\n            Serial.print(PS4.getY(i));\n            Serial.print(F(\"\\t\"));\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/SPP/SPP.ino",
    "content": "/*\n Example sketch for the RFCOMM/SPP Bluetooth library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <SPP.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\n\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\n/* You can create the instance of the class in two ways */\nSPP SerialBT(&Btd); // This will set the name to the defaults: \"Arduino\" and the pin to \"0000\"\n//SPP SerialBT(&Btd, \"Lauszus's Arduino\", \"1234\"); // You can also set the name and pin like so\n\nbool firstMessage = true;\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nSPP Bluetooth Library Started\"));\n}\nvoid loop() {\n  Usb.Task(); // The SPP data is actually not send until this is called, one could call SerialBT.send() directly as well\n\n  if (SerialBT.connected) {\n    if (firstMessage) {\n      firstMessage = false;\n      SerialBT.println(F(\"Hello from Arduino\")); // Send welcome message\n    }\n    if (Serial.available())\n      SerialBT.write(Serial.read());\n    if (SerialBT.available())\n      Serial.write(SerialBT.read());\n  }\n  else\n    firstMessage = true;\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/SPPMulti/SPPMulti.ino",
    "content": "/*\n Example sketch for the RFCOMM/SPP Bluetooth library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <SPP.h>\n#include <usbhub.h>\n\n// Satisfy IDE, which only needs to see the include statment in the ino.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\n\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\n\nconst uint8_t length = 2; // Set the number of instances here\nSPP *SerialBT[length]; // We will use this pointer to store the instances, you can easily make it larger if you like, but it will use a lot of RAM!\n\nbool firstMessage[length] = { true }; // Set all to true\n\nvoid setup() {\n  for (uint8_t i = 0; i < length; i++)\n    SerialBT[i] = new SPP(&Btd); // This will set the name to the default: \"Arduino\" and the pin to \"0000\" for all connections\n\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); // Halt\n  }\n  Serial.print(F(\"\\r\\nSPP Bluetooth Library Started\"));\n}\n\nvoid loop() {\n  Usb.Task(); // The SPP data is actually not send until this is called, one could call SerialBT.send() directly as well\n\n  for (uint8_t i = 0; i < length; i++) {\n    if (SerialBT[i]->connected) {\n      if (firstMessage[i]) {\n        firstMessage[i] = false;\n        SerialBT[i]->println(F(\"Hello from Arduino\")); // Send welcome message\n      }\n      if (SerialBT[i]->available())\n        Serial.write(SerialBT[i]->read());\n    }\n    else\n      firstMessage[i] = true;\n  }\n\n  // Set the connection you want to send to using the first character\n  // For instance \"0Hello World\" would send \"Hello World\" to connection 0\n  if (Serial.available()) {\n    delay(10); // Wait for the rest of the data to arrive\n    uint8_t id = Serial.read() - '0'; // Convert from ASCII\n    if (id < length && SerialBT[id]->connected) { // Make sure that the id is valid and make sure that a device is actually connected\n      while (Serial.available()) // Check if data is available\n        SerialBT[id]->write(Serial.read()); // Send the data\n    }\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/Wii/Wii.ino",
    "content": "/*\n Example sketch for the Wiimote Bluetooth library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <Wii.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\n\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\n/* You can create the instance of the class in two ways */\nWII Wii(&Btd, PAIR); // This will start an inquiry and then pair with your Wiimote - you only have to do this once\n//WII Wii(&Btd); // After that you can simply create the instance like so and then press any button on the Wiimote\n\nbool printAngle;\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nWiimote Bluetooth Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n  if (Wii.wiimoteConnected) {\n    if (Wii.getButtonClick(HOME)) { // You can use getButtonPress to see if the button is held down\n      Serial.print(F(\"\\r\\nHOME\"));\n      Wii.disconnect();\n    }\n    else {\n      if (Wii.getButtonClick(LEFT)) {\n        Wii.setLedOff();\n        Wii.setLedOn(LED1);\n        Serial.print(F(\"\\r\\nLeft\"));\n      }\n      if (Wii.getButtonClick(RIGHT)) {\n        Wii.setLedOff();\n        Wii.setLedOn(LED3);\n        Serial.print(F(\"\\r\\nRight\"));\n      }\n      if (Wii.getButtonClick(DOWN)) {\n        Wii.setLedOff();\n        Wii.setLedOn(LED4);\n        Serial.print(F(\"\\r\\nDown\"));\n      }\n      if (Wii.getButtonClick(UP)) {\n        Wii.setLedOff();\n        Wii.setLedOn(LED2);\n        Serial.print(F(\"\\r\\nUp\"));\n      }\n\n      if (Wii.getButtonClick(PLUS))\n        Serial.print(F(\"\\r\\nPlus\"));\n      if (Wii.getButtonClick(MINUS))\n        Serial.print(F(\"\\r\\nMinus\"));\n\n      if (Wii.getButtonClick(ONE))\n        Serial.print(F(\"\\r\\nOne\"));\n      if (Wii.getButtonClick(TWO))\n        Serial.print(F(\"\\r\\nTwo\"));\n\n      if (Wii.getButtonClick(A)) {\n        printAngle = !printAngle;\n        Serial.print(F(\"\\r\\nA\"));\n      }\n      if (Wii.getButtonClick(B)) {\n        Wii.setRumbleToggle();\n        Serial.print(F(\"\\r\\nB\"));\n      }\n    }\n#if 0 // Set this to 1 in order to see the angle of the controllers\n    if (printAngle) {\n      Serial.print(F(\"\\r\\nPitch: \"));\n      Serial.print(Wii.getPitch());\n      Serial.print(F(\"\\tRoll: \"));\n      Serial.print(Wii.getRoll());\n      if (Wii.motionPlusConnected) {\n        Serial.print(F(\"\\tYaw: \"));\n        Serial.print(Wii.getYaw());\n      }\n      if (Wii.nunchuckConnected) {\n        Serial.print(F(\"\\tNunchuck Pitch: \"));\n        Serial.print(Wii.getNunchuckPitch());\n        Serial.print(F(\"\\tNunchuck Roll: \"));\n        Serial.print(Wii.getNunchuckRoll());\n      }\n    }\n#endif\n  }\n#if 0 // Set this to 1 if you are using a Nunchuck controller\n  if (Wii.nunchuckConnected) {\n    if (Wii.getButtonClick(Z))\n      Serial.print(F(\"\\r\\nZ\"));\n    if (Wii.getButtonClick(C))\n      Serial.print(F(\"\\r\\nC\"));\n    if (Wii.getAnalogHat(HatX) > 137 ||  Wii.getAnalogHat(HatX) < 117 || Wii.getAnalogHat(HatY) > 137 || Wii.getAnalogHat(HatY) < 117) {\n      Serial.print(F(\"\\r\\nHatX: \"));\n      Serial.print(Wii.getAnalogHat(HatX));\n      Serial.print(F(\"\\tHatY: \"));\n      Serial.print(Wii.getAnalogHat(HatY));\n    }\n  }\n#endif\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/WiiBalanceBoard/WiiBalanceBoard.ino",
    "content": "/*\n Example sketch for the Wii Balance Board Bluetooth library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <Wii.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\n\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\n/* You can create the instance of the class in two ways */\nWII Wii(&Btd, PAIR); // This will start an inquiry and then pair with your Wii Balance Board - you only have to do this once\n//WII Wii(&Btd); // After that you can simply create the instance like so and then press the power button on the Wii Balance Board\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nWii Balance Board Bluetooth Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n  if (Wii.wiiBalanceBoardConnected) {\n    Serial.print(F(\"\\r\\nWeight: \"));\n    for (uint8_t i = 0; i < 4; i++) {\n      Serial.print(Wii.getWeight((BalanceBoardEnum)i));\n      Serial.print(F(\"\\t\"));\n    }\n    Serial.print(F(\"Total Weight: \"));\n    Serial.print(Wii.getTotalWeight());\n    if (Wii.getButtonClick(A)) {\n      Serial.print(F(\"\\r\\nA\"));\n      //Wii.setLedToggle(LED1); // The Wii Balance Board has one LED as well\n      Wii.disconnect();\n    }\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/WiiIRCamera/WiiIRCamera.ino",
    "content": "/*\nExample sketch for the Wii libary showing the IR camera functionality. This example\nis for the Bluetooth Wii library developed for the USB shield from Circuits@Home\n\nCreated by Allan Glover and Kristian Lauszus.\nContact Kristian:  http://blog.tkjelectronics.dk/ or send an email at kristianl@tkjelectronics.com.\nContact Allan at adglover9.81@gmail.com\n\nTo test the Wiimote IR camera, you will need access to an IR source. Sunlight will work but is not ideal.\nThe simpleist solution is to use the Wii sensor bar, i.e. emitter bar, supplied by the Wii system.\nOtherwise, wire up a IR LED yourself.\n*/\n\n#include <Wii.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\n#ifndef WIICAMERA // Used to check if WIICAMERA is defined\n#error \"Please set ENABLE_WII_IR_CAMERA to 1 in settings.h\"\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\n\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\n/* You can create the instance of the class in two ways */\nWII Wii(&Btd, PAIR); // This will start an inquiry and then pair with your Wiimote - you only have to do this once\n//WII Wii(&Btd); // After the Wiimote pairs once with the line of code above, you can simply create the instance like so and re upload and then press any button on the Wiimote\n\nbool printAngle;\nuint8_t printObjects;\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nWiimote Bluetooth Library Started\"));\n}\n\nvoid loop() {\n  Usb.Task();\n  if (Wii.wiimoteConnected) {\n    if (Wii.getButtonClick(HOME)) { // You can use getButtonPress to see if the button is held down\n      Serial.print(F(\"\\r\\nHOME\"));\n      Wii.disconnect();\n    }\n    else {\n      if (Wii.getButtonClick(ONE))\n        Wii.IRinitialize(); // Run the initialisation sequence\n      if (Wii.getButtonClick(MINUS) || Wii.getButtonClick(PLUS)) {\n        if (!Wii.isIRCameraEnabled())\n          Serial.print(F(\"\\r\\nEnable IR camera first\"));\n        else {\n          if (Wii.getButtonPress(MINUS)) { // getButtonClick will only return true once\n            if (printObjects > 0)\n              printObjects--;\n          }\n          else {\n            if (printObjects < 4)\n              printObjects++;\n          }\n          Serial.print(F(\"\\r\\nTracking \"));\n          Serial.print(printObjects);\n          Serial.print(F(\" objects\"));\n        }\n      }\n      if (Wii.getButtonClick(A)) {\n        printAngle = !printAngle;\n        Serial.print(F(\"\\r\\nA\"));\n      }\n      if (Wii.getButtonClick(B)) {\n        Serial.print(F(\"\\r\\nBattery level: \"));\n        Serial.print(Wii.getBatteryLevel()); // You can get the battery level as well\n      }\n    }\n    if (printObjects > 0) {\n      if (Wii.getIRx1() != 0x3FF || Wii.getIRy1() != 0x3FF || Wii.getIRs1() != 0) { // Only print if the IR camera is actually seeing something\n        Serial.print(F(\"\\r\\nx1: \"));\n        Serial.print(Wii.getIRx1());\n        Serial.print(F(\"\\ty1: \"));\n        Serial.print(Wii.getIRy1());\n        Serial.print(F(\"\\ts1:\"));\n        Serial.print(Wii.getIRs1());\n      }\n      if (printObjects > 1) {\n        if (Wii.getIRx2() != 0x3FF || Wii.getIRy2() != 0x3FF || Wii.getIRs2() != 0) {\n          Serial.print(F(\"\\r\\nx2: \"));\n          Serial.print(Wii.getIRx2());\n          Serial.print(F(\"\\ty2: \"));\n          Serial.print(Wii.getIRy2());\n          Serial.print(F(\"\\ts2:\"));\n          Serial.print(Wii.getIRs2());\n        }\n        if (printObjects > 2) {\n          if (Wii.getIRx3() != 0x3FF || Wii.getIRy3() != 0x3FF || Wii.getIRs3() != 0) {\n            Serial.print(F(\"\\r\\nx3: \"));\n            Serial.print(Wii.getIRx3());\n            Serial.print(F(\"\\ty3: \"));\n            Serial.print(Wii.getIRy3());\n            Serial.print(F(\"\\ts3:\"));\n            Serial.print(Wii.getIRs3());\n          }\n          if (printObjects > 3) {\n            if (Wii.getIRx4() != 0x3FF || Wii.getIRy4() != 0x3FF || Wii.getIRs4() != 0) {\n              Serial.print(F(\"\\r\\nx4: \"));\n              Serial.print(Wii.getIRx4());\n              Serial.print(F(\"\\ty4: \"));\n              Serial.print(Wii.getIRy4());\n              Serial.print(F(\"\\ts4:\"));\n              Serial.print(Wii.getIRs4());\n            }\n          }\n        }\n      }\n    }\n    if (printAngle) { // There is no extension bytes available, so the MotionPlus or Nunchuck can't be read\n      Serial.print(F(\"\\r\\nPitch: \"));\n      Serial.print(Wii.getPitch());\n      Serial.print(F(\"\\tRoll: \"));\n      Serial.print(Wii.getRoll());\n    }\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/WiiMulti/WiiMulti.ino",
    "content": "/*\n Example sketch for the Wiimote Bluetooth library - developed by Kristian Lauszus\n This example show how one can use multiple controllers with the library\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <Wii.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\n\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\nWII *Wii[2]; // We will use this pointer to store the two instance, you can easily make it larger if you like, but it will use a lot of RAM!\nconst uint8_t length = sizeof(Wii) / sizeof(Wii[0]); // Get the lenght of the array\nbool printAngle[length];\nbool oldControllerState[length];\n\nvoid setup() {\n  for (uint8_t i = 0; i < length; i++) {\n    Wii[i] = new WII(&Btd); // You will have to pair each controller with the dongle before you can define the instances like so, just add PAIR as the second argument\n    Wii[i]->attachOnInit(onInit); // onInit() is called upon a new connection - you can call the function whatever you like\n  }\n\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nWiimote Bluetooth Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n\n  for (uint8_t i = 0; i < length; i++) {\n    if (Wii[i]->wiimoteConnected) {\n      if (Wii[i]->getButtonClick(HOME)) { // You can use getButtonPress to see if the button is held down\n        Serial.print(F(\"\\r\\nHOME\"));\n        Wii[i]->disconnect();\n        oldControllerState[i] = false; // Reset value\n      }\n      else {\n        if (Wii[i]->getButtonClick(LEFT)) {\n          Wii[i]->setLedOff();\n          Wii[i]->setLedOn(LED1);\n          Serial.print(F(\"\\r\\nLeft\"));\n        }\n        if (Wii[i]->getButtonClick(RIGHT)) {\n          Wii[i]->setLedOff();\n          Wii[i]->setLedOn(LED3);\n          Serial.print(F(\"\\r\\nRight\"));\n        }\n        if (Wii[i]->getButtonClick(DOWN)) {\n          Wii[i]->setLedOff();\n          Wii[i]->setLedOn(LED4);\n          Serial.print(F(\"\\r\\nDown\"));\n        }\n        if (Wii[i]->getButtonClick(UP)) {\n          Wii[i]->setLedOff();\n          Wii[i]->setLedOn(LED2);\n          Serial.print(F(\"\\r\\nUp\"));\n        }\n\n        if (Wii[i]->getButtonClick(PLUS))\n          Serial.print(F(\"\\r\\nPlus\"));\n        if (Wii[i]->getButtonClick(MINUS))\n          Serial.print(F(\"\\r\\nMinus\"));\n\n        if (Wii[i]->getButtonClick(ONE))\n          Serial.print(F(\"\\r\\nOne\"));\n        if (Wii[i]->getButtonClick(TWO))\n          Serial.print(F(\"\\r\\nTwo\"));\n\n        if (Wii[i]->getButtonClick(A)) {\n          printAngle[i] = !printAngle[i];\n          Serial.print(F(\"\\r\\nA\"));\n        }\n        if (Wii[i]->getButtonClick(B)) {\n          Wii[i]->setRumbleToggle();\n          Serial.print(F(\"\\r\\nB\"));\n        }\n      }\n      if (printAngle[i]) {\n        Serial.print(F(\"\\r\\nPitch: \"));\n        Serial.print(Wii[i]->getPitch());\n        Serial.print(F(\"\\tRoll: \"));\n        Serial.print(Wii[i]->getRoll());\n        if (Wii[i]->motionPlusConnected) {\n          Serial.print(F(\"\\tYaw: \"));\n          Serial.print(Wii[i]->getYaw());\n        }\n        if (Wii[i]->nunchuckConnected) {\n          Serial.print(F(\"\\tNunchuck Pitch: \"));\n          Serial.print(Wii[i]->getNunchuckPitch());\n          Serial.print(F(\"\\tNunchuck Roll: \"));\n          Serial.print(Wii[i]->getNunchuckRoll());\n        }\n      }\n    }\n    if (Wii[i]->nunchuckConnected) {\n      if (Wii[i]->getButtonClick(Z))\n        Serial.print(F(\"\\r\\nZ\"));\n      if (Wii[i]->getButtonClick(C))\n        Serial.print(F(\"\\r\\nC\"));\n      if (Wii[i]->getAnalogHat(HatX) > 137 ||  Wii[i]->getAnalogHat(HatX) < 117 || Wii[i]->getAnalogHat(HatY) > 137 || Wii[i]->getAnalogHat(HatY) < 117) {\n        Serial.print(F(\"\\r\\nHatX: \"));\n        Serial.print(Wii[i]->getAnalogHat(HatX));\n        Serial.print(F(\"\\tHatY: \"));\n        Serial.print(Wii[i]->getAnalogHat(HatY));\n      }\n    }\n  }\n}\n\nvoid onInit() {\n  for (uint8_t i = 0; i < length; i++) {\n    if (Wii[i]->wiimoteConnected && !oldControllerState[i]) {\n      oldControllerState[i] = true; // Used to check which is the new controller\n      Wii[i]->setLedOn((LEDEnum)(i + 1)); // Cast directly to LEDEnum - see: \"controllerEnums.h\"\n    }\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Bluetooth/WiiUProController/WiiUProController.ino",
    "content": "/*\n Example sketch for the Wiimote Bluetooth library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <Wii.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub Hub1(&Usb); // Some dongles have a hub inside\n\nBTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so\n/* You can create the instance of the class in two ways */\nWII Wii(&Btd, PAIR); // This will start an inquiry and then pair with your Wiimote - you only have to do this once\n//WII Wii(&Btd); // After that you can simply create the instance like so and then press any button on the Wiimote\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nWiimote Bluetooth Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n  if (Wii.wiiUProControllerConnected) {\n    if (Wii.getButtonClick(HOME)) { // You can use getButtonPress to see if the button is held down\n      Serial.print(F(\"\\r\\nHome\"));\n      Wii.disconnect();\n    }\n    else {\n      if (Wii.getButtonClick(LEFT)) {\n        Wii.setLedOff();\n        Wii.setLedOn(LED1);\n        Serial.print(F(\"\\r\\nLeft\"));\n      }\n      if (Wii.getButtonClick(RIGHT)) {\n        Wii.setLedOff();\n        Wii.setLedOn(LED3);\n        Serial.print(F(\"\\r\\nRight\"));\n      }\n      if (Wii.getButtonClick(DOWN)) {\n        Wii.setLedOff();\n        Wii.setLedOn(LED4);\n        Serial.print(F(\"\\r\\nDown\"));\n      }\n      if (Wii.getButtonClick(UP)) {\n        Wii.setLedOff();\n        Wii.setLedOn(LED2);\n        Serial.print(F(\"\\r\\nUp\"));\n      }\n\n      if (Wii.getButtonClick(PLUS))\n        Serial.print(F(\"\\r\\nPlus\"));\n      if (Wii.getButtonClick(MINUS))\n        Serial.print(F(\"\\r\\nMinus\"));\n\n      if (Wii.getButtonClick(A))\n        Serial.print(F(\"\\r\\nA\"));\n      if (Wii.getButtonClick(B)) {\n        Wii.setRumbleToggle();\n        Serial.print(F(\"\\r\\nB\"));\n      }\n      if (Wii.getButtonClick(X))\n        Serial.print(F(\"\\r\\nX\"));\n      if (Wii.getButtonClick(Y))\n        Serial.print(F(\"\\r\\nY\"));\n\n      if (Wii.getButtonClick(L))\n        Serial.print(F(\"\\r\\nL\"));\n      if (Wii.getButtonClick(R))\n        Serial.print(F(\"\\r\\nR\"));\n      if (Wii.getButtonClick(ZL))\n        Serial.print(F(\"\\r\\nZL\"));\n      if (Wii.getButtonClick(ZR))\n        Serial.print(F(\"\\r\\nZR\"));\n      if (Wii.getButtonClick(L3))\n        Serial.print(F(\"\\r\\nL3\"));\n      if (Wii.getButtonClick(R3))\n        Serial.print(F(\"\\r\\nR3\"));\n    }\n    if (Wii.getAnalogHat(LeftHatX) > 2200 || Wii.getAnalogHat(LeftHatX) < 1800 || Wii.getAnalogHat(LeftHatY) > 2200 || Wii.getAnalogHat(LeftHatY) < 1800 || Wii.getAnalogHat(RightHatX) > 2200 ||  Wii.getAnalogHat(RightHatX) < 1800 || Wii.getAnalogHat(RightHatY) > 2200 || Wii.getAnalogHat(RightHatY) < 1800) {\n      Serial.print(F(\"\\r\\nLeftHatX: \"));\n      Serial.print(Wii.getAnalogHat(LeftHatX));\n      Serial.print(F(\"\\tLeftHatY: \"));\n      Serial.print(Wii.getAnalogHat(LeftHatY));\n      Serial.print(F(\"\\tRightHatX: \"));\n      Serial.print(Wii.getAnalogHat(RightHatX));\n      Serial.print(F(\"\\tRightHatY: \"));\n      Serial.print(Wii.getAnalogHat(RightHatY));\n    }\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/USBHIDBootKbd/USBHIDBootKbd.ino",
    "content": "#include <hidboot.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nclass KbdRptParser : public KeyboardReportParser\n{\n    void PrintKey(uint8_t mod, uint8_t key);\n\n  protected:\n    void OnControlKeysChanged(uint8_t before, uint8_t after);\n\n    void OnKeyDown\t(uint8_t mod, uint8_t key);\n    void OnKeyUp\t(uint8_t mod, uint8_t key);\n    void OnKeyPressed(uint8_t key);\n};\n\nvoid KbdRptParser::PrintKey(uint8_t m, uint8_t key)\n{\n  MODIFIERKEYS mod;\n  *((uint8_t*)&mod) = m;\n  Serial.print((mod.bmLeftCtrl   == 1) ? \"C\" : \" \");\n  Serial.print((mod.bmLeftShift  == 1) ? \"S\" : \" \");\n  Serial.print((mod.bmLeftAlt    == 1) ? \"A\" : \" \");\n  Serial.print((mod.bmLeftGUI    == 1) ? \"G\" : \" \");\n\n  Serial.print(\" >\");\n  PrintHex<uint8_t>(key, 0x80);\n  Serial.print(\"< \");\n\n  Serial.print((mod.bmRightCtrl   == 1) ? \"C\" : \" \");\n  Serial.print((mod.bmRightShift  == 1) ? \"S\" : \" \");\n  Serial.print((mod.bmRightAlt    == 1) ? \"A\" : \" \");\n  Serial.println((mod.bmRightGUI    == 1) ? \"G\" : \" \");\n};\n\nvoid KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key)\n{\n  Serial.print(\"DN \");\n  PrintKey(mod, key);\n  uint8_t c = OemToAscii(mod, key);\n\n  if (c)\n    OnKeyPressed(c);\n}\n\nvoid KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) {\n\n  MODIFIERKEYS beforeMod;\n  *((uint8_t*)&beforeMod) = before;\n\n  MODIFIERKEYS afterMod;\n  *((uint8_t*)&afterMod) = after;\n\n  if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl) {\n    Serial.println(\"LeftCtrl changed\");\n  }\n  if (beforeMod.bmLeftShift != afterMod.bmLeftShift) {\n    Serial.println(\"LeftShift changed\");\n  }\n  if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt) {\n    Serial.println(\"LeftAlt changed\");\n  }\n  if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI) {\n    Serial.println(\"LeftGUI changed\");\n  }\n\n  if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl) {\n    Serial.println(\"RightCtrl changed\");\n  }\n  if (beforeMod.bmRightShift != afterMod.bmRightShift) {\n    Serial.println(\"RightShift changed\");\n  }\n  if (beforeMod.bmRightAlt != afterMod.bmRightAlt) {\n    Serial.println(\"RightAlt changed\");\n  }\n  if (beforeMod.bmRightGUI != afterMod.bmRightGUI) {\n    Serial.println(\"RightGUI changed\");\n  }\n\n}\n\nvoid KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key)\n{\n  Serial.print(\"UP \");\n  PrintKey(mod, key);\n}\n\nvoid KbdRptParser::OnKeyPressed(uint8_t key)\n{\n  Serial.print(\"ASCII: \");\n  Serial.println((char)key);\n};\n\nUSB     Usb;\n//USBHub     Hub(&Usb);\nHIDBoot<HID_PROTOCOL_KEYBOARD>    HidKeyboard(&Usb);\n\nuint32_t next_time;\n\nKbdRptParser Prs;\n\nvoid setup()\n{\n  Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"Start\");\n\n  if (Usb.Init() == -1)\n    Serial.println(\"OSC did not start.\");\n\n  delay( 200 );\n\n  next_time = millis() + 5000;\n\n  HidKeyboard.SetReportParser(0, (HIDReportParser*)&Prs);\n}\n\nvoid loop()\n{\n  Usb.Task();\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/USBHIDBootKbdAndMouse/USBHIDBootKbdAndMouse.ino",
    "content": "#include <hidboot.h>\n#include <usbhub.h>\n\n// Satisfy IDE, which only needs to see the include statment in the ino.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nclass MouseRptParser : public MouseReportParser\n{\n  protected:\n    void OnMouseMove(MOUSEINFO *mi);\n    void OnLeftButtonUp(MOUSEINFO *mi);\n    void OnLeftButtonDown(MOUSEINFO *mi);\n    void OnRightButtonUp(MOUSEINFO *mi);\n    void OnRightButtonDown(MOUSEINFO *mi);\n    void OnMiddleButtonUp(MOUSEINFO *mi);\n    void OnMiddleButtonDown(MOUSEINFO *mi);\n};\nvoid MouseRptParser::OnMouseMove(MOUSEINFO *mi)\n{\n  Serial.print(\"dx=\");\n  Serial.print(mi->dX, DEC);\n  Serial.print(\" dy=\");\n  Serial.println(mi->dY, DEC);\n};\nvoid MouseRptParser::OnLeftButtonUp\t(MOUSEINFO *mi)\n{\n  Serial.println(\"L Butt Up\");\n};\nvoid MouseRptParser::OnLeftButtonDown\t(MOUSEINFO *mi)\n{\n  Serial.println(\"L Butt Dn\");\n};\nvoid MouseRptParser::OnRightButtonUp\t(MOUSEINFO *mi)\n{\n  Serial.println(\"R Butt Up\");\n};\nvoid MouseRptParser::OnRightButtonDown\t(MOUSEINFO *mi)\n{\n  Serial.println(\"R Butt Dn\");\n};\nvoid MouseRptParser::OnMiddleButtonUp\t(MOUSEINFO *mi)\n{\n  Serial.println(\"M Butt Up\");\n};\nvoid MouseRptParser::OnMiddleButtonDown\t(MOUSEINFO *mi)\n{\n  Serial.println(\"M Butt Dn\");\n};\n\nclass KbdRptParser : public KeyboardReportParser\n{\n    void PrintKey(uint8_t mod, uint8_t key);\n\n  protected:\n    void OnControlKeysChanged(uint8_t before, uint8_t after);\n    void OnKeyDown\t(uint8_t mod, uint8_t key);\n    void OnKeyUp\t(uint8_t mod, uint8_t key);\n    void OnKeyPressed(uint8_t key);\n};\n\nvoid KbdRptParser::PrintKey(uint8_t m, uint8_t key)\n{\n  MODIFIERKEYS mod;\n  *((uint8_t*)&mod) = m;\n  Serial.print((mod.bmLeftCtrl   == 1) ? \"C\" : \" \");\n  Serial.print((mod.bmLeftShift  == 1) ? \"S\" : \" \");\n  Serial.print((mod.bmLeftAlt    == 1) ? \"A\" : \" \");\n  Serial.print((mod.bmLeftGUI    == 1) ? \"G\" : \" \");\n\n  Serial.print(\" >\");\n  PrintHex<uint8_t>(key, 0x80);\n  Serial.print(\"< \");\n\n  Serial.print((mod.bmRightCtrl   == 1) ? \"C\" : \" \");\n  Serial.print((mod.bmRightShift  == 1) ? \"S\" : \" \");\n  Serial.print((mod.bmRightAlt    == 1) ? \"A\" : \" \");\n  Serial.println((mod.bmRightGUI    == 1) ? \"G\" : \" \");\n};\n\nvoid KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key)\n{\n  Serial.print(\"DN \");\n  PrintKey(mod, key);\n  uint8_t c = OemToAscii(mod, key);\n\n  if (c)\n    OnKeyPressed(c);\n}\n\nvoid KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) {\n\n  MODIFIERKEYS beforeMod;\n  *((uint8_t*)&beforeMod) = before;\n\n  MODIFIERKEYS afterMod;\n  *((uint8_t*)&afterMod) = after;\n\n  if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl) {\n    Serial.println(\"LeftCtrl changed\");\n  }\n  if (beforeMod.bmLeftShift != afterMod.bmLeftShift) {\n    Serial.println(\"LeftShift changed\");\n  }\n  if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt) {\n    Serial.println(\"LeftAlt changed\");\n  }\n  if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI) {\n    Serial.println(\"LeftGUI changed\");\n  }\n\n  if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl) {\n    Serial.println(\"RightCtrl changed\");\n  }\n  if (beforeMod.bmRightShift != afterMod.bmRightShift) {\n    Serial.println(\"RightShift changed\");\n  }\n  if (beforeMod.bmRightAlt != afterMod.bmRightAlt) {\n    Serial.println(\"RightAlt changed\");\n  }\n  if (beforeMod.bmRightGUI != afterMod.bmRightGUI) {\n    Serial.println(\"RightGUI changed\");\n  }\n\n}\n\nvoid KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key)\n{\n  Serial.print(\"UP \");\n  PrintKey(mod, key);\n}\n\nvoid KbdRptParser::OnKeyPressed(uint8_t key)\n{\n  Serial.print(\"ASCII: \");\n  Serial.println((char)key);\n};\n\nUSB     Usb;\nUSBHub     Hub(&Usb);\n\nHIDBoot < HID_PROTOCOL_KEYBOARD | HID_PROTOCOL_MOUSE > HidComposite(&Usb);\nHIDBoot<HID_PROTOCOL_KEYBOARD>    HidKeyboard(&Usb);\nHIDBoot<HID_PROTOCOL_MOUSE>    HidMouse(&Usb);\n\n//uint32_t next_time;\n\nKbdRptParser KbdPrs;\nMouseRptParser MousePrs;\n\nvoid setup()\n{\n  Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"Start\");\n\n  if (Usb.Init() == -1)\n    Serial.println(\"OSC did not start.\");\n\n  delay( 200 );\n\n  //next_time = millis() + 5000;\n\n  HidComposite.SetReportParser(0, (HIDReportParser*)&KbdPrs);\n  HidComposite.SetReportParser(1, (HIDReportParser*)&MousePrs);\n  HidKeyboard.SetReportParser(0, (HIDReportParser*)&KbdPrs);\n  HidMouse.SetReportParser(0, (HIDReportParser*)&MousePrs);\n}\n\nvoid loop()\n{\n  Usb.Task();\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/USBHIDBootMouse/USBHIDBootMouse.ino",
    "content": "#include <hidboot.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nclass MouseRptParser : public MouseReportParser\n{\nprotected:\n\tvoid OnMouseMove\t(MOUSEINFO *mi);\n\tvoid OnLeftButtonUp\t(MOUSEINFO *mi);\n\tvoid OnLeftButtonDown\t(MOUSEINFO *mi);\n\tvoid OnRightButtonUp\t(MOUSEINFO *mi);\n\tvoid OnRightButtonDown\t(MOUSEINFO *mi);\n\tvoid OnMiddleButtonUp\t(MOUSEINFO *mi);\n\tvoid OnMiddleButtonDown\t(MOUSEINFO *mi);\n};\nvoid MouseRptParser::OnMouseMove(MOUSEINFO *mi)\n{\n    Serial.print(\"dx=\");\n    Serial.print(mi->dX, DEC);\n    Serial.print(\" dy=\");\n    Serial.println(mi->dY, DEC);\n};\nvoid MouseRptParser::OnLeftButtonUp\t(MOUSEINFO *mi)\n{\n    Serial.println(\"L Butt Up\");\n};\nvoid MouseRptParser::OnLeftButtonDown\t(MOUSEINFO *mi)\n{\n    Serial.println(\"L Butt Dn\");\n};\nvoid MouseRptParser::OnRightButtonUp\t(MOUSEINFO *mi)\n{\n    Serial.println(\"R Butt Up\");\n};\nvoid MouseRptParser::OnRightButtonDown\t(MOUSEINFO *mi)\n{\n    Serial.println(\"R Butt Dn\");\n};\nvoid MouseRptParser::OnMiddleButtonUp\t(MOUSEINFO *mi)\n{\n    Serial.println(\"M Butt Up\");\n};\nvoid MouseRptParser::OnMiddleButtonDown\t(MOUSEINFO *mi)\n{\n    Serial.println(\"M Butt Dn\");\n};\n\nUSB     Usb;\nUSBHub     Hub(&Usb);\nHIDBoot<HID_PROTOCOL_MOUSE>    HidMouse(&Usb);\n\nuint32_t next_time;\n\nMouseRptParser                               Prs;\n\nvoid setup()\n{\n    Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n    while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n    Serial.println(\"Start\");\n\n    if (Usb.Init() == -1)\n        Serial.println(\"OSC did not start.\");\n\n    delay( 200 );\n\n    next_time = millis() + 5000;\n\n    HidMouse.SetReportParser(0,(HIDReportParser*)&Prs);\n}\n\nvoid loop()\n{\n  Usb.Task();\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/USBHIDJoystick/USBHIDJoystick.ino",
    "content": "#include <hid.h>\n#include <hiduniversal.h>\n#include <usbhub.h>\n\n// Satisfy IDE, which only needs to see the include statment in the ino.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\n#include \"hidjoystickrptparser.h\"\n\nUSB Usb;\nUSBHub Hub(&Usb);\nHIDUniversal Hid(&Usb);\nJoystickEvents JoyEvents;\nJoystickReportParser Joy(&JoyEvents);\n\nvoid setup() {\n        Serial.begin(115200);\n#if !defined(__MIPSEL__)\n        while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n        Serial.println(\"Start\");\n\n        if (Usb.Init() == -1)\n                Serial.println(\"OSC did not start.\");\n\n        delay(200);\n\n        if (!Hid.SetReportParser(0, &Joy))\n                ErrorMessage<uint8_t > (PSTR(\"SetReportParser\"), 1);\n}\n\nvoid loop() {\n        Usb.Task();\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/USBHIDJoystick/hidjoystickrptparser.cpp",
    "content": "#include \"hidjoystickrptparser.h\"\n\nJoystickReportParser::JoystickReportParser(JoystickEvents *evt) :\njoyEvents(evt),\noldHat(0xDE),\noldButtons(0) {\n        for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++)\n                oldPad[i] = 0xD;\n}\n\nvoid JoystickReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {\n        bool match = true;\n\n        // Checking if there are changes in report since the method was last called\n        for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++)\n                if (buf[i] != oldPad[i]) {\n                        match = false;\n                        break;\n                }\n\n        // Calling Game Pad event handler\n        if (!match && joyEvents) {\n                joyEvents->OnGamePadChanged((const GamePadEventData*)buf);\n\n                for (uint8_t i = 0; i < RPT_GEMEPAD_LEN; i++) oldPad[i] = buf[i];\n        }\n\n        uint8_t hat = (buf[5] & 0xF);\n\n        // Calling Hat Switch event handler\n        if (hat != oldHat && joyEvents) {\n                joyEvents->OnHatSwitch(hat);\n                oldHat = hat;\n        }\n\n        uint16_t buttons = (0x0000 | buf[6]);\n        buttons <<= 4;\n        buttons |= (buf[5] >> 4);\n        uint16_t changes = (buttons ^ oldButtons);\n\n        // Calling Button Event Handler for every button changed\n        if (changes) {\n                for (uint8_t i = 0; i < 0x0C; i++) {\n                        uint16_t mask = (0x0001 << i);\n\n                        if (((mask & changes) > 0) && joyEvents)\n                                if ((buttons & mask) > 0)\n                                        joyEvents->OnButtonDn(i + 1);\n                                else\n                                        joyEvents->OnButtonUp(i + 1);\n                }\n                oldButtons = buttons;\n        }\n}\n\nvoid JoystickEvents::OnGamePadChanged(const GamePadEventData *evt) {\n        Serial.print(\"X1: \");\n        PrintHex<uint8_t > (evt->X, 0x80);\n        Serial.print(\"\\tY1: \");\n        PrintHex<uint8_t > (evt->Y, 0x80);\n        Serial.print(\"\\tX2: \");\n        PrintHex<uint8_t > (evt->Z1, 0x80);\n        Serial.print(\"\\tY2: \");\n        PrintHex<uint8_t > (evt->Z2, 0x80);\n        Serial.print(\"\\tRz: \");\n        PrintHex<uint8_t > (evt->Rz, 0x80);\n        Serial.println(\"\");\n}\n\nvoid JoystickEvents::OnHatSwitch(uint8_t hat) {\n        Serial.print(\"Hat Switch: \");\n        PrintHex<uint8_t > (hat, 0x80);\n        Serial.println(\"\");\n}\n\nvoid JoystickEvents::OnButtonUp(uint8_t but_id) {\n        Serial.print(\"Up: \");\n        Serial.println(but_id, DEC);\n}\n\nvoid JoystickEvents::OnButtonDn(uint8_t but_id) {\n        Serial.print(\"Dn: \");\n        Serial.println(but_id, DEC);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/USBHIDJoystick/hidjoystickrptparser.h",
    "content": "#if !defined(__HIDJOYSTICKRPTPARSER_H__)\n#define __HIDJOYSTICKRPTPARSER_H__\n\n#include <hid.h>\n\nstruct GamePadEventData {\n        uint8_t X, Y, Z1, Z2, Rz;\n};\n\nclass JoystickEvents {\npublic:\n        virtual void OnGamePadChanged(const GamePadEventData *evt);\n        virtual void OnHatSwitch(uint8_t hat);\n        virtual void OnButtonUp(uint8_t but_id);\n        virtual void OnButtonDn(uint8_t but_id);\n};\n\n#define RPT_GEMEPAD_LEN\t\t5\n\nclass JoystickReportParser : public HIDReportParser {\n        JoystickEvents *joyEvents;\n\n        uint8_t oldPad[RPT_GEMEPAD_LEN];\n        uint8_t oldHat;\n        uint16_t oldButtons;\n\npublic:\n        JoystickReportParser(JoystickEvents *evt);\n\n        virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);\n};\n\n#endif // __HIDJOYSTICKRPTPARSER_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/USBHID_desc/USBHID_desc.ino",
    "content": "#include <hid.h>\n#include <hiduniversal.h>\n#include <hidescriptorparser.h>\n#include <usbhub.h>\n#include \"pgmstrings.h\"\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nclass HIDUniversal2 : public HIDUniversal\n{\npublic:\n    HIDUniversal2(USB *usb) : HIDUniversal(usb) {};\n\nprotected:\n    uint8_t OnInitSuccessful();\n};\n\nuint8_t HIDUniversal2::OnInitSuccessful()\n{\n    uint8_t    rcode;\n\n    HexDumper<USBReadParser, uint16_t, uint16_t>    Hex;\n    ReportDescParser                                Rpt;\n\n    if ((rcode = GetReportDescr(0, &Hex)))\n        goto FailGetReportDescr1;\n\n    if ((rcode = GetReportDescr(0, &Rpt)))\n\tgoto FailGetReportDescr2;\n\n    return 0;\n\nFailGetReportDescr1:\n    USBTRACE(\"GetReportDescr1:\");\n    goto Fail;\n\nFailGetReportDescr2:\n    USBTRACE(\"GetReportDescr2:\");\n    goto Fail;\n\nFail:\n    Serial.println(rcode, HEX);\n    Release();\n    return rcode;\n}\n\nUSB Usb;\n//USBHub Hub(&Usb);\nHIDUniversal2 Hid(&Usb);\nUniversalReportParser Uni;\n\nvoid setup()\n{\n  Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"Start\");\n\n  if (Usb.Init() == -1)\n      Serial.println(\"OSC did not start.\");\n\n  delay( 200 );\n\n  if (!Hid.SetReportParser(0, &Uni))\n      ErrorMessage<uint8_t>(PSTR(\"SetReportParser\"), 1  );\n}\n\nvoid loop()\n{\n    Usb.Task();\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/USBHID_desc/pgmstrings.h",
    "content": "#if !defined(__PGMSTRINGS_H__)\n#define __PGMSTRINGS_H__\n\n#define LOBYTE(x) ((char*)(&(x)))[0]\n#define HIBYTE(x) ((char*)(&(x)))[1]\n#define BUFSIZE 256    //buffer size\n \n\n/* Print strings in Program Memory */\nconst char Gen_Error_str[] PROGMEM = \"\\r\\nRequest error. Error code:\\t\"; \nconst char Dev_Header_str[] PROGMEM =\"\\r\\nDevice descriptor: \";\nconst char Dev_Length_str[] PROGMEM =\"\\r\\nDescriptor Length:\\t\";\nconst char Dev_Type_str[] PROGMEM =\"\\r\\nDescriptor type:\\t\";\nconst char Dev_Version_str[] PROGMEM =\"\\r\\nUSB version:\\t\\t\";\nconst char Dev_Class_str[] PROGMEM =\"\\r\\nDevice class:\\t\\t\";\nconst char Dev_Subclass_str[] PROGMEM =\"\\r\\nDevice Subclass:\\t\";\nconst char Dev_Protocol_str[] PROGMEM =\"\\r\\nDevice Protocol:\\t\";\nconst char Dev_Pktsize_str[] PROGMEM =\"\\r\\nMax.packet size:\\t\";\nconst char Dev_Vendor_str[] PROGMEM =\"\\r\\nVendor  ID:\\t\\t\";\nconst char Dev_Product_str[] PROGMEM =\"\\r\\nProduct ID:\\t\\t\";\nconst char Dev_Revision_str[] PROGMEM =\"\\r\\nRevision ID:\\t\\t\";\nconst char Dev_Mfg_str[] PROGMEM =\"\\r\\nMfg.string index:\\t\";\nconst char Dev_Prod_str[] PROGMEM =\"\\r\\nProd.string index:\\t\";\nconst char Dev_Serial_str[] PROGMEM =\"\\r\\nSerial number index:\\t\";\nconst char Dev_Nconf_str[] PROGMEM =\"\\r\\nNumber of conf.:\\t\";\nconst char Conf_Trunc_str[] PROGMEM =\"Total length truncated to 256 bytes\";\nconst char Conf_Header_str[] PROGMEM =\"\\r\\nConfiguration descriptor:\";\nconst char Conf_Totlen_str[] PROGMEM =\"\\r\\nTotal length:\\t\\t\";\nconst char Conf_Nint_str[] PROGMEM =\"\\r\\nNum.intf:\\t\\t\";\nconst char Conf_Value_str[] PROGMEM =\"\\r\\nConf.value:\\t\\t\";\nconst char Conf_String_str[] PROGMEM =\"\\r\\nConf.string:\\t\\t\";\nconst char Conf_Attr_str[] PROGMEM =\"\\r\\nAttr.:\\t\\t\\t\";\nconst char Conf_Pwr_str[] PROGMEM =\"\\r\\nMax.pwr:\\t\\t\";\nconst char Int_Header_str[] PROGMEM =\"\\r\\n\\r\\nInterface descriptor:\";\nconst char Int_Number_str[] PROGMEM =\"\\r\\nIntf.number:\\t\\t\";\nconst char Int_Alt_str[] PROGMEM =\"\\r\\nAlt.:\\t\\t\\t\";\nconst char Int_Endpoints_str[] PROGMEM =\"\\r\\nEndpoints:\\t\\t\";\nconst char Int_Class_str[] PROGMEM =\"\\r\\nIntf. Class:\\t\\t\";\nconst char Int_Subclass_str[] PROGMEM =\"\\r\\nIntf. Subclass:\\t\\t\";\nconst char Int_Protocol_str[] PROGMEM =\"\\r\\nIntf. Protocol:\\t\\t\";\nconst char Int_String_str[] PROGMEM =\"\\r\\nIntf.string:\\t\\t\";\nconst char End_Header_str[] PROGMEM =\"\\r\\n\\r\\nEndpoint descriptor:\";\nconst char End_Address_str[] PROGMEM =\"\\r\\nEndpoint address:\\t\";\nconst char End_Attr_str[] PROGMEM =\"\\r\\nAttr.:\\t\\t\\t\";\nconst char End_Pktsize_str[] PROGMEM =\"\\r\\nMax.pkt size:\\t\\t\";\nconst char End_Interval_str[] PROGMEM =\"\\r\\nPolling interval:\\t\";\nconst char Unk_Header_str[] PROGMEM = \"\\r\\nUnknown descriptor:\";\nconst char Unk_Length_str[] PROGMEM =\"\\r\\nLength:\\t\\t\";\nconst char Unk_Type_str[] PROGMEM =\"\\r\\nType:\\t\\t\";\nconst char Unk_Contents_str[] PROGMEM =\"\\r\\nContents:\\t\";\n \n#endif // __PGMSTRINGS_H__"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/le3dp/le3dp.ino",
    "content": "/* Simplified Logitech Extreme 3D Pro Joystick Report Parser */\n\n#include <hid.h>\n#include <hiduniversal.h>\n#include <usbhub.h>\n\n#include \"le3dp_rptparser.h\"\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB                                             Usb;\nUSBHub                                          Hub(&Usb);\nHIDUniversal                                    Hid(&Usb);\nJoystickEvents                                  JoyEvents;\nJoystickReportParser                            Joy(&JoyEvents);\n\nvoid setup()\n{\n  Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"Start\");\n\n  if (Usb.Init() == -1)\n      Serial.println(\"OSC did not start.\");\n\n  delay( 200 );\n\n  if (!Hid.SetReportParser(0, &Joy))\n      ErrorMessage<uint8_t>(PSTR(\"SetReportParser\"), 1  );\n}\n\nvoid loop()\n{\n    Usb.Task();\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/le3dp/le3dp_rptparser.cpp",
    "content": "#include \"le3dp_rptparser.h\"\n\nJoystickReportParser::JoystickReportParser(JoystickEvents *evt) :\n\tjoyEvents(evt)\n{}\n\nvoid JoystickReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)\n{\n\tbool match = true;\n\n\t// Checking if there are changes in report since the method was last called\n\tfor (uint8_t i=0; i<RPT_GAMEPAD_LEN; i++) {\n\t\tif( buf[i] != oldPad[i] ) {\n\t\t\tmatch = false;\n\t\t\tbreak;\n\t\t}\n  }\n  \t// Calling Game Pad event handler\n\tif (!match && joyEvents) {\n\t\tjoyEvents->OnGamePadChanged((const GamePadEventData*)buf);\n\n\t\tfor (uint8_t i=0; i<RPT_GAMEPAD_LEN; i++) oldPad[i] = buf[i];\n\t}\n}\n\nvoid JoystickEvents::OnGamePadChanged(const GamePadEventData *evt)\n{\n\tSerial.print(\"X: \");\n\tPrintHex<uint16_t>(evt->x, 0x80);\n\tSerial.print(\" Y: \");\n\tPrintHex<uint16_t>(evt->y, 0x80);\n\tSerial.print(\" Hat Switch: \");\n\tPrintHex<uint8_t>(evt->hat, 0x80);\n\tSerial.print(\" Twist: \");\n\tPrintHex<uint8_t>(evt->twist, 0x80);\n\tSerial.print(\" Slider: \");\n\tPrintHex<uint8_t>(evt->slider, 0x80);\n  Serial.print(\" Buttons A: \");\n\tPrintHex<uint8_t>(evt->buttons_a, 0x80);\n\tSerial.print(\" Buttons B: \");\n\tPrintHex<uint8_t>(evt->buttons_b, 0x80);\n\tSerial.println(\"\");\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/le3dp/le3dp_rptparser.h",
    "content": "#if !defined(__HIDJOYSTICKRPTPARSER_H__)\n#define __HIDJOYSTICKRPTPARSER_H__\n\n#include <hid.h>\n\nstruct GamePadEventData\n{\n  union { //axes and hut switch\n    uint32_t axes;\n    struct {\n      uint32_t x : 10;\n      uint32_t y : 10;\n      uint32_t hat : 4;\n      uint32_t twist : 8;      \n    };\n  };\n  uint8_t buttons_a;\n  uint8_t slider;\n  uint8_t buttons_b;\n};\n\nclass JoystickEvents\n{\npublic:\n\tvirtual void OnGamePadChanged(const GamePadEventData *evt);\n};\n\n#define RPT_GAMEPAD_LEN\tsizeof(GamePadEventData)/sizeof(uint8_t)\n\nclass JoystickReportParser : public HIDReportParser\n{\n\tJoystickEvents\t\t*joyEvents;\n\n  uint8_t oldPad[RPT_GAMEPAD_LEN];\n\npublic:\n\tJoystickReportParser(JoystickEvents *evt);\n\n\tvirtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);\n};\n\n#endif // __HIDJOYSTICKRPTPARSER_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/scale/scale.ino",
    "content": "/* Digital Scale Output. Written for Stamps.com Model 510  */\n/* 5lb Digital Scale; any HID scale with Usage page 0x8d should work */\n\n#include <hid.h>\n#include <hiduniversal.h>\n#include <usbhub.h>\n\n#include \"scale_rptparser.h\"\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB                                             Usb;\nUSBHub                                          Hub(&Usb);\nHIDUniversal                                    Hid(&Usb);\nMax_LCD                                       LCD(&Usb);\nScaleEvents                                  ScaleEvents(&LCD);\nScaleReportParser                            Scale(&ScaleEvents);\n\nvoid setup()\n{\n  Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"Start\");\n\n  if (Usb.Init() == -1)\n      Serial.println(\"OSC did not start.\");\n\n    // set up the LCD's number of rows and columns:\n    LCD.begin(16, 2);\n    LCD.clear();\n    LCD.home();\n    LCD.setCursor(0,0);\n    LCD.write('R');\n\n  delay( 200 );\n\n  if (!Hid.SetReportParser(0, &Scale))\n      ErrorMessage<uint8_t>(PSTR(\"SetReportParser\"), 1  );\n}\n\nvoid loop()\n{\n    Usb.Task();\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/scale/scale_rptparser.cpp",
    "content": "/* Parser for standard HID scale (usage page 0x8d) data input report (ID 3) */ \n#include \"scale_rptparser.h\"\n\nconst char* UNITS[13] = {\n    \"units\",        // unknown unit\n    \"mg\",           // milligram\n    \"g\",            // gram\n    \"kg\",           // kilogram\n    \"cd\",           // carat\n    \"taels\",        // lian\n    \"gr\",           // grain\n    \"dwt\",          // pennyweight\n    \"tonnes\",       // metric tons\n    \"tons\",         // avoir ton\n    \"ozt\",          // troy ounce\n    \"oz\",           // ounce\n    \"lbs\"           // pound\n};\n\nScaleReportParser::ScaleReportParser(ScaleEvents *evt) :\n\tscaleEvents(evt)\n{}\n\nvoid ScaleReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)\n{\n\tbool match = true;\n\n\t// Checking if there are changes in report since the method was last called\n\tfor (uint8_t i=0; i<RPT_SCALE_LEN; i++) {\n\t\tif( buf[i] != oldScale[i] ) {\n\t\t\tmatch = false;\n\t\t\tbreak;\n\t\t}\n  }\n  \t// Calling Game Pad event handler\n\tif (!match && scaleEvents) {\n\t\tscaleEvents->OnScaleChanged((const ScaleEventData*)buf);\n\n\t\tfor (uint8_t i=0; i<RPT_SCALE_LEN; i++) oldScale[i] = buf[i];\n\t}\n}\n\nScaleEvents::ScaleEvents( Max_LCD* pLCD ) :\n\t\n\tpLcd( pLCD )\n\n{}\n\nvoid ScaleEvents::LcdPrint( const char* str )\n{\n\t\n\twhile( *str ) {\n\t\t\n\t\tpLcd->write(\t*str++ );\n\t\t\n\t}\n}\n\nvoid ScaleEvents::OnScaleChanged(const ScaleEventData *evt)\n{\n\t\n\tpLcd->clear();\n  pLcd->home();\n  pLcd->setCursor(0,0);\n\t\n\tif( evt->reportID != 3 ) {\n\t\t\n\t\tconst char inv_report[]=\"Invalid report!\";\n\t\t\n\t\tSerial.println(inv_report);\n\t\tLcdPrint(inv_report);\n\t\t\n\t\treturn;\n\t\t\n\t}//if( evt->reportID != 3...\n\t\n\tswitch( evt->status ) {\n\t\t\n\t\tcase REPORT_FAULT: \n\t\t\tSerial.println(F(\"Report fault\"));\n\t\t\tbreak;\n\t\t\t\n\t\tcase ZEROED:\n\t\t\tSerial.println(F(\"Scale zero set\"));\n\t\t\tbreak;\n\t\t\t\n\t\tcase WEIGHING: {\n\t\t\t\n\t\t\tconst char progress[] = \"Weighing...\";\n\t\t\tSerial.println(progress);\n\t\t\tLcdPrint(progress);\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tcase WEIGHT_VALID: {\n\t\t\t\n\t\t\tchar buf[10];\n      double weight = evt->weight * pow( 10, evt->exp );\n      \n      \t\n                        \n      \tSerial.print(F(\"Weight: \"));\n\t\t\t\tSerial.print( weight );\n\t\t\t\tSerial.print(F(\" \"));\n\t\t\t\tSerial.println( UNITS[ evt->unit ]);\n\t\t\t\t\n\t\t\t\tLcdPrint(\"Weight: \");\n\t\t\t\tdtostrf( weight, 4, 2, buf );\n\t\t\t\tLcdPrint( buf ); \n\t\t\t\tLcdPrint( UNITS[ evt->unit ]);\n\t\t\t\n\t\t\tbreak;\n\t\t\t\n\t\t}//case WEIGHT_VALID...\n\t\t\t\n\t\tcase WEIGHT_NEGATIVE: {\n\t\t\t\n\t\t\tconst char negweight[] = \"Negative weight\";\n\t\t\tSerial.println(negweight);\n\t\t\tLcdPrint(negweight);\n\t\t\tbreak;\n\t\t}\n\t\t\t\n\t\tcase OVERWEIGHT: {\n\t\t\n\t\t\tconst char overweight[] = \"Max.weight reached\";\n\t\t\tSerial.println(overweight);\n\t\t\tLcdPrint( overweight );\n\t\t\tbreak;\n\t\t}\n\t\t\n\t\tcase CALIBRATE_ME:\n\t\t\t\n\t\t\tSerial.println(F(\"Scale calibration required\"));\n\t\t\tbreak;\n\t\t\t\n\t\tcase ZERO_ME:\n\t\t\t\n\t\t\tSerial.println(F(\"Scale zeroing required\"));\n\t\t\tbreak;\n\t\t\t\n\t\tdefault:\n\t\t\t\n\t\t\tSerial.print(F(\"Undefined status code: \"));\n\t\t\tSerial.println( evt->status );\n\t\t\tbreak;\t\n\t\t\t\n\t}//switch( evt->status...\n\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/HID/scale/scale_rptparser.h",
    "content": "#if !defined(__SCALERPTPARSER_H__)\n#define __SCALERPTPARSER_H__\n\n#include <max_LCD.h>\n#include <hid.h>\n\n/* Scale status constants */\n#define REPORT_FAULT 0x01\n#define ZEROED 0x02\n#define WEIGHING 0x03\n#define WEIGHT_VALID 0x04\n#define WEIGHT_NEGATIVE 0x05\n#define OVERWEIGHT 0x06\n#define CALIBRATE_ME 0x07\n#define ZERO_ME 0x08\n\n/* input data report */\nstruct ScaleEventData\n{\n  uint8_t reportID;\t//must be 3\n  uint8_t status;\n  uint8_t unit;\n  int8_t exp;\t\t\t//scale factor for the weight\n  uint16_t weight;\t//\n};\n\nclass ScaleEvents\n{\n\n\tMax_LCD*\tpLcd;\n\n\tvoid LcdPrint( const char* str );\n\npublic:\n\n\tScaleEvents( Max_LCD* pLCD );\n\n\tvirtual void OnScaleChanged(const ScaleEventData *evt);\n};\n\n#define RPT_SCALE_LEN\tsizeof(ScaleEventData)/sizeof(uint8_t)\n\nclass ScaleReportParser : public HIDReportParser\n{\n\tScaleEvents\t\t*scaleEvents;\n\n  uint8_t oldScale[RPT_SCALE_LEN];\n\npublic:\n\tScaleReportParser(ScaleEvents *evt);\n\n\tvirtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);\n};\n\n#endif // __SCALERPTPARSER_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/PS3USB/PS3USB.ino",
    "content": "/*\n Example sketch for the PS3 USB library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <PS3USB.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n/* You can create the instance of the class in two ways */\nPS3USB PS3(&Usb); // This will just create the instance\n//PS3USB PS3(&Usb,0x00,0x15,0x83,0x3D,0x0A,0x57); // This will also store the bluetooth address - this can be obtained from the dongle when running the sketch\n\nbool printAngle;\nuint8_t state = 0;\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nPS3 USB Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n\n  if (PS3.PS3Connected || PS3.PS3NavigationConnected) {\n    if (PS3.getAnalogHat(LeftHatX) > 137 || PS3.getAnalogHat(LeftHatX) < 117 || PS3.getAnalogHat(LeftHatY) > 137 || PS3.getAnalogHat(LeftHatY) < 117 || PS3.getAnalogHat(RightHatX) > 137 || PS3.getAnalogHat(RightHatX) < 117 || PS3.getAnalogHat(RightHatY) > 137 || PS3.getAnalogHat(RightHatY) < 117) {\n      Serial.print(F(\"\\r\\nLeftHatX: \"));\n      Serial.print(PS3.getAnalogHat(LeftHatX));\n      Serial.print(F(\"\\tLeftHatY: \"));\n      Serial.print(PS3.getAnalogHat(LeftHatY));\n      if (PS3.PS3Connected) { // The Navigation controller only have one joystick\n        Serial.print(F(\"\\tRightHatX: \"));\n        Serial.print(PS3.getAnalogHat(RightHatX));\n        Serial.print(F(\"\\tRightHatY: \"));\n        Serial.print(PS3.getAnalogHat(RightHatY));\n      }\n    }\n    // Analog button values can be read from almost all buttons\n    if (PS3.getAnalogButton(L2) || PS3.getAnalogButton(R2)) {\n      Serial.print(F(\"\\r\\nL2: \"));\n      Serial.print(PS3.getAnalogButton(L2));\n      if (!PS3.PS3NavigationConnected) {\n        Serial.print(F(\"\\tR2: \"));\n        Serial.print(PS3.getAnalogButton(R2));\n      }\n    }\n    if (PS3.getButtonClick(PS))\n      Serial.print(F(\"\\r\\nPS\"));\n\n    if (PS3.getButtonClick(TRIANGLE))\n      Serial.print(F(\"\\r\\nTraingle\"));\n    if (PS3.getButtonClick(CIRCLE))\n      Serial.print(F(\"\\r\\nCircle\"));\n    if (PS3.getButtonClick(CROSS))\n      Serial.print(F(\"\\r\\nCross\"));\n    if (PS3.getButtonClick(SQUARE))\n      Serial.print(F(\"\\r\\nSquare\"));\n\n    if (PS3.getButtonClick(UP)) {\n      Serial.print(F(\"\\r\\nUp\"));\n      PS3.setLedOff();\n      PS3.setLedOn(LED4);\n    }\n    if (PS3.getButtonClick(RIGHT)) {\n      Serial.print(F(\"\\r\\nRight\"));\n      PS3.setLedOff();\n      PS3.setLedOn(LED1);\n    }\n    if (PS3.getButtonClick(DOWN)) {\n      Serial.print(F(\"\\r\\nDown\"));\n      PS3.setLedOff();\n      PS3.setLedOn(LED2);\n    }\n    if (PS3.getButtonClick(LEFT)) {\n      Serial.print(F(\"\\r\\nLeft\"));\n      PS3.setLedOff();\n      PS3.setLedOn(LED3);\n    }\n\n    if (PS3.getButtonClick(L1))\n      Serial.print(F(\"\\r\\nL1\"));\n    if (PS3.getButtonClick(L3))\n      Serial.print(F(\"\\r\\nL3\"));\n    if (PS3.getButtonClick(R1))\n      Serial.print(F(\"\\r\\nR1\"));\n    if (PS3.getButtonClick(R3))\n      Serial.print(F(\"\\r\\nR3\"));\n\n    if (PS3.getButtonClick(SELECT)) {\n      Serial.print(F(\"\\r\\nSelect - \"));\n      PS3.printStatusString();\n    }\n    if (PS3.getButtonClick(START)) {\n      Serial.print(F(\"\\r\\nStart\"));\n      printAngle = !printAngle;\n    }\n    if (printAngle) {\n      Serial.print(F(\"\\r\\nPitch: \"));\n      Serial.print(PS3.getAngle(Pitch));\n      Serial.print(F(\"\\tRoll: \"));\n      Serial.print(PS3.getAngle(Roll));\n    }\n  }\n  else if (PS3.PS3MoveConnected) { // One can only set the color of the bulb, set the rumble, set and get the bluetooth address and calibrate the magnetometer via USB\n    if (state == 0) {\n      PS3.moveSetRumble(0);\n      PS3.moveSetBulb(Off);\n    } else if (state == 1) {\n      PS3.moveSetRumble(75);\n      PS3.moveSetBulb(Red);\n    } else if (state == 2) {\n      PS3.moveSetRumble(125);\n      PS3.moveSetBulb(Green);\n    } else if (state == 3) {\n      PS3.moveSetRumble(150);\n      PS3.moveSetBulb(Blue);\n    } else if (state == 4) {\n      PS3.moveSetRumble(175);\n      PS3.moveSetBulb(Yellow);\n    } else if (state == 5) {\n      PS3.moveSetRumble(200);\n      PS3.moveSetBulb(Lightblue);\n    } else if (state == 6) {\n      PS3.moveSetRumble(225);\n      PS3.moveSetBulb(Purble);\n    } else if (state == 7) {\n      PS3.moveSetRumble(250);\n      PS3.moveSetBulb(White);\n    }\n\n    state++;\n    if (state > 7)\n      state = 0;\n    delay(1000);\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/PS4USB/PS4USB.ino",
    "content": "/*\n Example sketch for the PS4 USB library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <PS4USB.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\nPS4USB PS4(&Usb);\n\nbool printAngle, printTouch;\nuint8_t oldL2Value, oldR2Value;\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); // Halt\n  }\n  Serial.print(F(\"\\r\\nPS4 USB Library Started\"));\n}\n\nvoid loop() {\n  Usb.Task();\n\n  if (PS4.connected()) {\n    if (PS4.getAnalogHat(LeftHatX) > 137 || PS4.getAnalogHat(LeftHatX) < 117 || PS4.getAnalogHat(LeftHatY) > 137 || PS4.getAnalogHat(LeftHatY) < 117 || PS4.getAnalogHat(RightHatX) > 137 || PS4.getAnalogHat(RightHatX) < 117 || PS4.getAnalogHat(RightHatY) > 137 || PS4.getAnalogHat(RightHatY) < 117) {\n      Serial.print(F(\"\\r\\nLeftHatX: \"));\n      Serial.print(PS4.getAnalogHat(LeftHatX));\n      Serial.print(F(\"\\tLeftHatY: \"));\n      Serial.print(PS4.getAnalogHat(LeftHatY));\n      Serial.print(F(\"\\tRightHatX: \"));\n      Serial.print(PS4.getAnalogHat(RightHatX));\n      Serial.print(F(\"\\tRightHatY: \"));\n      Serial.print(PS4.getAnalogHat(RightHatY));\n    }\n\n    if (PS4.getAnalogButton(L2) || PS4.getAnalogButton(R2)) { // These are the only analog buttons on the PS4 controller\n      Serial.print(F(\"\\r\\nL2: \"));\n      Serial.print(PS4.getAnalogButton(L2));\n      Serial.print(F(\"\\tR2: \"));\n      Serial.print(PS4.getAnalogButton(R2));\n    }\n    if (PS4.getAnalogButton(L2) != oldL2Value || PS4.getAnalogButton(R2) != oldR2Value) // Only write value if it's different\n      PS4.setRumbleOn(PS4.getAnalogButton(L2), PS4.getAnalogButton(R2));\n    oldL2Value = PS4.getAnalogButton(L2);\n    oldR2Value = PS4.getAnalogButton(R2);\n\n    if (PS4.getButtonClick(PS))\n      Serial.print(F(\"\\r\\nPS\"));\n    if (PS4.getButtonClick(TRIANGLE)) {\n      Serial.print(F(\"\\r\\nTraingle\"));\n      PS4.setRumbleOn(RumbleLow);\n    }\n    if (PS4.getButtonClick(CIRCLE)) {\n      Serial.print(F(\"\\r\\nCircle\"));\n      PS4.setRumbleOn(RumbleHigh);\n    }\n    if (PS4.getButtonClick(CROSS)) {\n      Serial.print(F(\"\\r\\nCross\"));\n      PS4.setLedFlash(10, 10); // Set it to blink rapidly\n    }\n    if (PS4.getButtonClick(SQUARE)) {\n      Serial.print(F(\"\\r\\nSquare\"));\n      PS4.setLedFlash(0, 0); // Turn off blinking\n    }\n\n    if (PS4.getButtonClick(UP)) {\n      Serial.print(F(\"\\r\\nUp\"));\n      PS4.setLed(Red);\n    } if (PS4.getButtonClick(RIGHT)) {\n      Serial.print(F(\"\\r\\nRight\"));\n      PS4.setLed(Blue);\n    } if (PS4.getButtonClick(DOWN)) {\n      Serial.print(F(\"\\r\\nDown\"));\n      PS4.setLed(Yellow);\n    } if (PS4.getButtonClick(LEFT)) {\n      Serial.print(F(\"\\r\\nLeft\"));\n      PS4.setLed(Green);\n    }\n\n    if (PS4.getButtonClick(L1))\n      Serial.print(F(\"\\r\\nL1\"));\n    if (PS4.getButtonClick(L3))\n      Serial.print(F(\"\\r\\nL3\"));\n    if (PS4.getButtonClick(R1))\n      Serial.print(F(\"\\r\\nR1\"));\n    if (PS4.getButtonClick(R3))\n      Serial.print(F(\"\\r\\nR3\"));\n\n    if (PS4.getButtonClick(SHARE))\n      Serial.print(F(\"\\r\\nShare\"));\n    if (PS4.getButtonClick(OPTIONS)) {\n      Serial.print(F(\"\\r\\nOptions\"));\n      printAngle = !printAngle;\n    }\n    if (PS4.getButtonClick(TOUCHPAD)) {\n      Serial.print(F(\"\\r\\nTouchpad\"));\n      printTouch = !printTouch;\n    }\n\n    if (printAngle) { // Print angle calculated using the accelerometer only\n      Serial.print(F(\"\\r\\nPitch: \"));\n      Serial.print(PS4.getAngle(Pitch));\n      Serial.print(F(\"\\tRoll: \"));\n      Serial.print(PS4.getAngle(Roll));\n    }\n\n    if (printTouch) { // Print the x, y coordinates of the touchpad\n      if (PS4.isTouching(0) || PS4.isTouching(1)) // Print newline and carriage return if any of the fingers are touching the touchpad\n        Serial.print(F(\"\\r\\n\"));\n      for (uint8_t i = 0; i < 2; i++) { // The touchpad track two fingers\n        if (PS4.isTouching(i)) { // Print the position of the finger if it is touching the touchpad\n          Serial.print(F(\"X\")); Serial.print(i + 1); Serial.print(F(\": \"));\n          Serial.print(PS4.getX(i));\n          Serial.print(F(\"\\tY\")); Serial.print(i + 1); Serial.print(F(\": \"));\n          Serial.print(PS4.getY(i));\n          Serial.print(F(\"\\t\"));\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/PSBuzz/PSBuzz.ino",
    "content": "/*\n Example sketch for the Playstation Buzz library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <PSBuzz.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\nPSBuzz Buzz(&Usb);\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); // Halt\n  }\n  Serial.println(F(\"\\r\\nPS Buzz Library Started\"));\n}\n\nvoid loop() {\n  Usb.Task();\n\n  if (Buzz.connected()) {\n    for (uint8_t i = 0; i < 4; i++) {\n      if (Buzz.getButtonClick(RED, i)) {\n        Buzz.setLedToggle(i); // Toggle the LED\n        Serial.println(F(\"RED\"));\n      }\n      if (Buzz.getButtonClick(YELLOW, i))\n        Serial.println(F(\"YELLOW\"));\n      if (Buzz.getButtonClick(GREEN, i))\n        Serial.println(F(\"GREEN\"));\n      if (Buzz.getButtonClick(ORANGE, i))\n        Serial.println(F(\"ORANGE\"));\n      if (Buzz.getButtonClick(BLUE, i))\n        Serial.println(F(\"BLUE\"));\n    }\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/USB_desc/USB_desc.ino",
    "content": "#include <usbhub.h>\n\n#include \"pgmstrings.h\"\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB     Usb;\n//USBHub  Hub1(&Usb);\n//USBHub  Hub2(&Usb);\n//USBHub  Hub3(&Usb);\n//USBHub  Hub4(&Usb);\n//USBHub  Hub5(&Usb);\n//USBHub  Hub6(&Usb);\n//USBHub  Hub7(&Usb);\n\nuint32_t next_time;\n\nvoid PrintAllAddresses(UsbDevice *pdev)\n{\n  UsbDeviceAddress adr;\n  adr.devAddress = pdev->address.devAddress;\n  Serial.print(\"\\r\\nAddr:\");\n  Serial.print(adr.devAddress, HEX);\n  Serial.print(\"(\");\n  Serial.print(adr.bmHub, HEX);\n  Serial.print(\".\");\n  Serial.print(adr.bmParent, HEX);\n  Serial.print(\".\");\n  Serial.print(adr.bmAddress, HEX);\n  Serial.println(\")\");\n}\n\nvoid PrintAddress(uint8_t addr)\n{\n  UsbDeviceAddress adr;\n  adr.devAddress = addr;\n  Serial.print(\"\\r\\nADDR:\\t\");\n  Serial.println(adr.devAddress, HEX);\n  Serial.print(\"DEV:\\t\");\n  Serial.println(adr.bmAddress, HEX);\n  Serial.print(\"PRNT:\\t\");\n  Serial.println(adr.bmParent, HEX);\n  Serial.print(\"HUB:\\t\");\n  Serial.println(adr.bmHub, HEX);\n}\n\nvoid setup()\n{\n  Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"Start\");\n\n  if (Usb.Init() == -1)\n    Serial.println(\"OSC did not start.\");\n\n  delay( 200 );\n\n  next_time = millis() + 10000;\n}\n\nbyte getdevdescr( byte addr, byte &num_conf );\n\nvoid PrintDescriptors(uint8_t addr)\n{\n  uint8_t rcode = 0;\n  byte num_conf = 0;\n\n  rcode = getdevdescr( (byte)addr, num_conf );\n  if ( rcode )\n  {\n    printProgStr(Gen_Error_str);\n    print_hex( rcode, 8 );\n  }\n  Serial.print(\"\\r\\n\");\n\n  for (int i = 0; i < num_conf; i++)\n  {\n    rcode = getconfdescr( addr, i );                 // get configuration descriptor\n    if ( rcode )\n    {\n      printProgStr(Gen_Error_str);\n      print_hex(rcode, 8);\n    }\n    Serial.println(\"\\r\\n\");\n  }\n}\n\nvoid PrintAllDescriptors(UsbDevice *pdev)\n{\n  Serial.println(\"\\r\\n\");\n  print_hex(pdev->address.devAddress, 8);\n  Serial.println(\"\\r\\n--\");\n  PrintDescriptors( pdev->address.devAddress );\n}\n\nvoid loop()\n{\n  Usb.Task();\n\n  if ( Usb.getUsbTaskState() == USB_STATE_RUNNING )\n  {\n    //if (millis() >= next_time)\n    {\n      Usb.ForEachUsbDevice(&PrintAllDescriptors);\n      Usb.ForEachUsbDevice(&PrintAllAddresses);\n\n      while ( 1 );                          //stop\n    }\n  }\n}\n\nbyte getdevdescr( byte addr, byte &num_conf )\n{\n  USB_DEVICE_DESCRIPTOR buf;\n  byte rcode;\n  rcode = Usb.getDevDescr( addr, 0, 0x12, ( uint8_t *)&buf );\n  if ( rcode ) {\n    return ( rcode );\n  }\n  printProgStr(Dev_Header_str);\n  printProgStr(Dev_Length_str);\n  print_hex( buf.bLength, 8 );\n  printProgStr(Dev_Type_str);\n  print_hex( buf.bDescriptorType, 8 );\n  printProgStr(Dev_Version_str);\n  print_hex( buf.bcdUSB, 16 );\n  printProgStr(Dev_Class_str);\n  print_hex( buf.bDeviceClass, 8 );\n  printProgStr(Dev_Subclass_str);\n  print_hex( buf.bDeviceSubClass, 8 );\n  printProgStr(Dev_Protocol_str);\n  print_hex( buf.bDeviceProtocol, 8 );\n  printProgStr(Dev_Pktsize_str);\n  print_hex( buf.bMaxPacketSize0, 8 );\n  printProgStr(Dev_Vendor_str);\n  print_hex( buf.idVendor, 16 );\n  printProgStr(Dev_Product_str);\n  print_hex( buf.idProduct, 16 );\n  printProgStr(Dev_Revision_str);\n  print_hex( buf.bcdDevice, 16 );\n  printProgStr(Dev_Mfg_str);\n  print_hex( buf.iManufacturer, 8 );\n  printProgStr(Dev_Prod_str);\n  print_hex( buf.iProduct, 8 );\n  printProgStr(Dev_Serial_str);\n  print_hex( buf.iSerialNumber, 8 );\n  printProgStr(Dev_Nconf_str);\n  print_hex( buf.bNumConfigurations, 8 );\n  num_conf = buf.bNumConfigurations;\n  return ( 0 );\n}\n\nvoid printhubdescr(uint8_t *descrptr, uint8_t addr)\n{\n  HubDescriptor  *pHub = (HubDescriptor*) descrptr;\n  uint8_t        len = *((uint8_t*)descrptr);\n\n  printProgStr(PSTR(\"\\r\\n\\r\\nHub Descriptor:\\r\\n\"));\n  printProgStr(PSTR(\"bDescLength:\\t\\t\"));\n  Serial.println(pHub->bDescLength, HEX);\n\n  printProgStr(PSTR(\"bDescriptorType:\\t\"));\n  Serial.println(pHub->bDescriptorType, HEX);\n\n  printProgStr(PSTR(\"bNbrPorts:\\t\\t\"));\n  Serial.println(pHub->bNbrPorts, HEX);\n\n  printProgStr(PSTR(\"LogPwrSwitchMode:\\t\"));\n  Serial.println(pHub->LogPwrSwitchMode, BIN);\n\n  printProgStr(PSTR(\"CompoundDevice:\\t\\t\"));\n  Serial.println(pHub->CompoundDevice, BIN);\n\n  printProgStr(PSTR(\"OverCurrentProtectMode:\\t\"));\n  Serial.println(pHub->OverCurrentProtectMode, BIN);\n\n  printProgStr(PSTR(\"TTThinkTime:\\t\\t\"));\n  Serial.println(pHub->TTThinkTime, BIN);\n\n  printProgStr(PSTR(\"PortIndicatorsSupported:\"));\n  Serial.println(pHub->PortIndicatorsSupported, BIN);\n\n  printProgStr(PSTR(\"Reserved:\\t\\t\"));\n  Serial.println(pHub->Reserved, HEX);\n\n  printProgStr(PSTR(\"bPwrOn2PwrGood:\\t\\t\"));\n  Serial.println(pHub->bPwrOn2PwrGood, HEX);\n\n  printProgStr(PSTR(\"bHubContrCurrent:\\t\"));\n  Serial.println(pHub->bHubContrCurrent, HEX);\n\n  for (uint8_t i = 7; i < len; i++)\n    print_hex(descrptr[i], 8);\n\n  //for (uint8_t i=1; i<=pHub->bNbrPorts; i++)\n  //    PrintHubPortStatus(&Usb, addr, i, 1);\n}\n\nbyte getconfdescr( byte addr, byte conf )\n{\n  uint8_t buf[ BUFSIZE ];\n  uint8_t* buf_ptr = buf;\n  byte rcode;\n  byte descr_length;\n  byte descr_type;\n  unsigned int total_length;\n  rcode = Usb.getConfDescr( addr, 0, 4, conf, buf );  //get total length\n  LOBYTE( total_length ) = buf[ 2 ];\n  HIBYTE( total_length ) = buf[ 3 ];\n  if ( total_length > 256 ) {   //check if total length is larger than buffer\n    printProgStr(Conf_Trunc_str);\n    total_length = 256;\n  }\n  rcode = Usb.getConfDescr( addr, 0, total_length, conf, buf ); //get the whole descriptor\n  while ( buf_ptr < buf + total_length ) { //parsing descriptors\n    descr_length = *( buf_ptr );\n    descr_type = *( buf_ptr + 1 );\n    switch ( descr_type ) {\n      case ( USB_DESCRIPTOR_CONFIGURATION ):\n        printconfdescr( buf_ptr );\n        break;\n      case ( USB_DESCRIPTOR_INTERFACE ):\n        printintfdescr( buf_ptr );\n        break;\n      case ( USB_DESCRIPTOR_ENDPOINT ):\n        printepdescr( buf_ptr );\n        break;\n      case 0x29:\n        printhubdescr( buf_ptr, addr );\n        break;\n      default:\n        printunkdescr( buf_ptr );\n        break;\n    }//switch( descr_type\n    buf_ptr = ( buf_ptr + descr_length );    //advance buffer pointer\n  }//while( buf_ptr <=...\n  return ( rcode );\n}\n/* prints hex numbers with leading zeroes */\n// copyright, Peter H Anderson, Baltimore, MD, Nov, '07\n// source: http://www.phanderson.com/arduino/arduino_display.html\nvoid print_hex(int v, int num_places)\n{\n  int mask = 0, n, num_nibbles, digit;\n\n  for (n = 1; n <= num_places; n++) {\n    mask = (mask << 1) | 0x0001;\n  }\n  v = v & mask; // truncate v to specified number of places\n\n  num_nibbles = num_places / 4;\n  if ((num_places % 4) != 0) {\n    ++num_nibbles;\n  }\n  do {\n    digit = ((v >> (num_nibbles - 1) * 4)) & 0x0f;\n    Serial.print(digit, HEX);\n  }\n  while (--num_nibbles);\n}\n/* function to print configuration descriptor */\nvoid printconfdescr( uint8_t* descr_ptr )\n{\n  USB_CONFIGURATION_DESCRIPTOR* conf_ptr = ( USB_CONFIGURATION_DESCRIPTOR* )descr_ptr;\n  printProgStr(Conf_Header_str);\n  printProgStr(Conf_Totlen_str);\n  print_hex( conf_ptr->wTotalLength, 16 );\n  printProgStr(Conf_Nint_str);\n  print_hex( conf_ptr->bNumInterfaces, 8 );\n  printProgStr(Conf_Value_str);\n  print_hex( conf_ptr->bConfigurationValue, 8 );\n  printProgStr(Conf_String_str);\n  print_hex( conf_ptr->iConfiguration, 8 );\n  printProgStr(Conf_Attr_str);\n  print_hex( conf_ptr->bmAttributes, 8 );\n  printProgStr(Conf_Pwr_str);\n  print_hex( conf_ptr->bMaxPower, 8 );\n  return;\n}\n/* function to print interface descriptor */\nvoid printintfdescr( uint8_t* descr_ptr )\n{\n  USB_INTERFACE_DESCRIPTOR* intf_ptr = ( USB_INTERFACE_DESCRIPTOR* )descr_ptr;\n  printProgStr(Int_Header_str);\n  printProgStr(Int_Number_str);\n  print_hex( intf_ptr->bInterfaceNumber, 8 );\n  printProgStr(Int_Alt_str);\n  print_hex( intf_ptr->bAlternateSetting, 8 );\n  printProgStr(Int_Endpoints_str);\n  print_hex( intf_ptr->bNumEndpoints, 8 );\n  printProgStr(Int_Class_str);\n  print_hex( intf_ptr->bInterfaceClass, 8 );\n  printProgStr(Int_Subclass_str);\n  print_hex( intf_ptr->bInterfaceSubClass, 8 );\n  printProgStr(Int_Protocol_str);\n  print_hex( intf_ptr->bInterfaceProtocol, 8 );\n  printProgStr(Int_String_str);\n  print_hex( intf_ptr->iInterface, 8 );\n  return;\n}\n/* function to print endpoint descriptor */\nvoid printepdescr( uint8_t* descr_ptr )\n{\n  USB_ENDPOINT_DESCRIPTOR* ep_ptr = ( USB_ENDPOINT_DESCRIPTOR* )descr_ptr;\n  printProgStr(End_Header_str);\n  printProgStr(End_Address_str);\n  print_hex( ep_ptr->bEndpointAddress, 8 );\n  printProgStr(End_Attr_str);\n  print_hex( ep_ptr->bmAttributes, 8 );\n  printProgStr(End_Pktsize_str);\n  print_hex( ep_ptr->wMaxPacketSize, 16 );\n  printProgStr(End_Interval_str);\n  print_hex( ep_ptr->bInterval, 8 );\n\n  return;\n}\n/*function to print unknown descriptor */\nvoid printunkdescr( uint8_t* descr_ptr )\n{\n  byte length = *descr_ptr;\n  byte i;\n  printProgStr(Unk_Header_str);\n  printProgStr(Unk_Length_str);\n  print_hex( *descr_ptr, 8 );\n  printProgStr(Unk_Type_str);\n  print_hex( *(descr_ptr + 1 ), 8 );\n  printProgStr(Unk_Contents_str);\n  descr_ptr += 2;\n  for ( i = 0; i < length; i++ ) {\n    print_hex( *descr_ptr, 8 );\n    descr_ptr++;\n  }\n}\n\n\n/* Print a string from Program Memory directly to save RAM */\nvoid printProgStr(const char* str)\n{\n  char c;\n  if (!str) return;\n  while ((c = pgm_read_byte(str++)))\n    Serial.print(c);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/USB_desc/pgmstrings.h",
    "content": "#if !defined(__PGMSTRINGS_H__)\n#define __PGMSTRINGS_H__\n\n#define LOBYTE(x) ((char*)(&(x)))[0]\n#define HIBYTE(x) ((char*)(&(x)))[1]\n#define BUFSIZE 256    //buffer size\n \n\n/* Print strings in Program Memory */\nconst char Gen_Error_str[] PROGMEM = \"\\r\\nRequest error. Error code:\\t\"; \nconst char Dev_Header_str[] PROGMEM =\"\\r\\nDevice descriptor: \";\nconst char Dev_Length_str[] PROGMEM =\"\\r\\nDescriptor Length:\\t\";\nconst char Dev_Type_str[] PROGMEM =\"\\r\\nDescriptor type:\\t\";\nconst char Dev_Version_str[] PROGMEM =\"\\r\\nUSB version:\\t\\t\";\nconst char Dev_Class_str[] PROGMEM =\"\\r\\nDevice class:\\t\\t\";\nconst char Dev_Subclass_str[] PROGMEM =\"\\r\\nDevice Subclass:\\t\";\nconst char Dev_Protocol_str[] PROGMEM =\"\\r\\nDevice Protocol:\\t\";\nconst char Dev_Pktsize_str[] PROGMEM =\"\\r\\nMax.packet size:\\t\";\nconst char Dev_Vendor_str[] PROGMEM =\"\\r\\nVendor  ID:\\t\\t\";\nconst char Dev_Product_str[] PROGMEM =\"\\r\\nProduct ID:\\t\\t\";\nconst char Dev_Revision_str[] PROGMEM =\"\\r\\nRevision ID:\\t\\t\";\nconst char Dev_Mfg_str[] PROGMEM =\"\\r\\nMfg.string index:\\t\";\nconst char Dev_Prod_str[] PROGMEM =\"\\r\\nProd.string index:\\t\";\nconst char Dev_Serial_str[] PROGMEM =\"\\r\\nSerial number index:\\t\";\nconst char Dev_Nconf_str[] PROGMEM =\"\\r\\nNumber of conf.:\\t\";\nconst char Conf_Trunc_str[] PROGMEM =\"Total length truncated to 256 bytes\";\nconst char Conf_Header_str[] PROGMEM =\"\\r\\nConfiguration descriptor:\";\nconst char Conf_Totlen_str[] PROGMEM =\"\\r\\nTotal length:\\t\\t\";\nconst char Conf_Nint_str[] PROGMEM =\"\\r\\nNum.intf:\\t\\t\";\nconst char Conf_Value_str[] PROGMEM =\"\\r\\nConf.value:\\t\\t\";\nconst char Conf_String_str[] PROGMEM =\"\\r\\nConf.string:\\t\\t\";\nconst char Conf_Attr_str[] PROGMEM =\"\\r\\nAttr.:\\t\\t\\t\";\nconst char Conf_Pwr_str[] PROGMEM =\"\\r\\nMax.pwr:\\t\\t\";\nconst char Int_Header_str[] PROGMEM =\"\\r\\n\\r\\nInterface descriptor:\";\nconst char Int_Number_str[] PROGMEM =\"\\r\\nIntf.number:\\t\\t\";\nconst char Int_Alt_str[] PROGMEM =\"\\r\\nAlt.:\\t\\t\\t\";\nconst char Int_Endpoints_str[] PROGMEM =\"\\r\\nEndpoints:\\t\\t\";\nconst char Int_Class_str[] PROGMEM =\"\\r\\nIntf. Class:\\t\\t\";\nconst char Int_Subclass_str[] PROGMEM =\"\\r\\nIntf. Subclass:\\t\\t\";\nconst char Int_Protocol_str[] PROGMEM =\"\\r\\nIntf. Protocol:\\t\\t\";\nconst char Int_String_str[] PROGMEM =\"\\r\\nIntf.string:\\t\\t\";\nconst char End_Header_str[] PROGMEM =\"\\r\\n\\r\\nEndpoint descriptor:\";\nconst char End_Address_str[] PROGMEM =\"\\r\\nEndpoint address:\\t\";\nconst char End_Attr_str[] PROGMEM =\"\\r\\nAttr.:\\t\\t\\t\";\nconst char End_Pktsize_str[] PROGMEM =\"\\r\\nMax.pkt size:\\t\\t\";\nconst char End_Interval_str[] PROGMEM =\"\\r\\nPolling interval:\\t\";\nconst char Unk_Header_str[] PROGMEM = \"\\r\\nUnknown descriptor:\";\nconst char Unk_Length_str[] PROGMEM =\"\\r\\nLength:\\t\\t\";\nconst char Unk_Type_str[] PROGMEM =\"\\r\\nType:\\t\\t\";\nconst char Unk_Contents_str[] PROGMEM =\"\\r\\nContents:\\t\";\n \n#endif // __PGMSTRINGS_H__"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Xbox/XBOXOLD/XBOXOLD.ino",
    "content": "/*\n Example sketch for the original Xbox library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <XBOXOLD.h>\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\nUSBHub  Hub1(&Usb); // The controller has a built in hub, so this instance is needed\nXBOXOLD Xbox(&Usb);\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); // halt\n  }\n  Serial.print(F(\"\\r\\nXBOX Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n  if (Xbox.XboxConnected) {\n    if (Xbox.getButtonPress(BLACK) || Xbox.getButtonPress(WHITE)) {\n      Serial.print(\"BLACK: \");\n      Serial.print(Xbox.getButtonPress(BLACK));\n      Serial.print(\"\\tWHITE: \");\n      Serial.println(Xbox.getButtonPress(WHITE));\n      Xbox.setRumbleOn(Xbox.getButtonPress(BLACK), Xbox.getButtonPress(WHITE));\n    } else\n      Xbox.setRumbleOn(0, 0);\n\n    if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500 || Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500 || Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500 || Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) {\n      if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500) {\n        Serial.print(F(\"LeftHatX: \"));\n        Serial.print(Xbox.getAnalogHat(LeftHatX));\n        Serial.print(\"\\t\");\n      }\n      if (Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500) {\n        Serial.print(F(\"LeftHatY: \"));\n        Serial.print(Xbox.getAnalogHat(LeftHatY));\n        Serial.print(\"\\t\");\n      }\n      if (Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500) {\n        Serial.print(F(\"RightHatX: \"));\n        Serial.print(Xbox.getAnalogHat(RightHatX));\n        Serial.print(\"\\t\");\n      }\n      if (Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) {\n        Serial.print(F(\"RightHatY: \"));\n        Serial.print(Xbox.getAnalogHat(RightHatY));\n      }\n      Serial.println();\n    }\n\n    if (Xbox.getButtonClick(UP))\n      Serial.println(F(\"Up\"));\n    if (Xbox.getButtonClick(DOWN))\n      Serial.println(F(\"Down\"));\n    if (Xbox.getButtonClick(LEFT))\n      Serial.println(F(\"Left\"));\n    if (Xbox.getButtonClick(RIGHT))\n      Serial.println(F(\"Right\"));\n\n    if (Xbox.getButtonClick(START))\n      Serial.println(F(\"Start\"));\n    if (Xbox.getButtonClick(BACK))\n      Serial.println(F(\"Back\"));\n    if (Xbox.getButtonClick(L3))\n      Serial.println(F(\"L3\"));\n    if (Xbox.getButtonClick(R3))\n      Serial.println(F(\"R3\"));\n\n    if (Xbox.getButtonPress(A)) {\n      Serial.print(F(\"A: \"));\n      Serial.println(Xbox.getButtonPress(A));\n    }\n    if (Xbox.getButtonPress(B)) {\n      Serial.print(F(\"B: \"));\n      Serial.println(Xbox.getButtonPress(B));\n    }\n    if (Xbox.getButtonPress(X)) {\n      Serial.print(F(\"X: \"));\n      Serial.println(Xbox.getButtonPress(X));\n    }\n    if (Xbox.getButtonPress(Y)) {\n      Serial.print(F(\"Y: \"));\n      Serial.println(Xbox.getButtonPress(Y));\n    }\n    if (Xbox.getButtonPress(L1)) {\n      Serial.print(F(\"L1: \"));\n      Serial.println(Xbox.getButtonPress(L1));\n    }\n    if (Xbox.getButtonPress(R1)) {\n      Serial.print(F(\"R1: \"));\n      Serial.println(Xbox.getButtonPress(R1));\n    }\n  }\n  delay(1);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Xbox/XBOXONE/XBOXONE.ino",
    "content": "/*\n Example sketch for the Xbox ONE USB library - by guruthree, based on work by\n Kristian Lauszus.\n */\n\n#include <XBOXONE.h>\n// Satisfy IDE, which only needs to see the include statment in the ino.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#endif\n\nUSB Usb;\nXBOXONE Xbox(&Usb);\n\nvoid setup() {\n  Serial.begin(115200);\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nXBOX USB Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n  if (Xbox.XboxOneConnected) {\n    if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500 || Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500 || Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500 || Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) {\n      if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500) {\n        Serial.print(F(\"LeftHatX: \"));\n        Serial.print(Xbox.getAnalogHat(LeftHatX));\n        Serial.print(\"\\t\");\n      }\n      if (Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500) {\n        Serial.print(F(\"LeftHatY: \"));\n        Serial.print(Xbox.getAnalogHat(LeftHatY));\n        Serial.print(\"\\t\");\n      }\n      if (Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500) {\n        Serial.print(F(\"RightHatX: \"));\n        Serial.print(Xbox.getAnalogHat(RightHatX));\n        Serial.print(\"\\t\");\n      }\n      if (Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) {\n        Serial.print(F(\"RightHatY: \"));\n        Serial.print(Xbox.getAnalogHat(RightHatY));\n      }\n      Serial.println();\n    }\n\n    if (Xbox.getButtonPress(L2) > 0 || Xbox.getButtonPress(R2) > 0) {\n      if (Xbox.getButtonPress(L2) > 0) {\n        Serial.print(F(\"L2: \"));\n        Serial.print(Xbox.getButtonPress(L2));\n        Serial.print(\"\\t\");\n      }\n      if (Xbox.getButtonPress(R2) > 0) {\n        Serial.print(F(\"R2: \"));\n        Serial.print(Xbox.getButtonPress(R2));\n        Serial.print(\"\\t\");\n      }\n      Serial.println();\n    }\n\n    if (Xbox.getButtonClick(UP))\n      Serial.println(F(\"Up\"));\n    if (Xbox.getButtonClick(DOWN))\n      Serial.println(F(\"Down\"));\n    if (Xbox.getButtonClick(LEFT))\n      Serial.println(F(\"Left\"));\n    if (Xbox.getButtonClick(RIGHT))\n      Serial.println(F(\"Right\"));\n\n    if (Xbox.getButtonClick(START))\n      Serial.println(F(\"Start\"));\n    if (Xbox.getButtonClick(BACK))\n      Serial.println(F(\"Back\"));\n    if (Xbox.getButtonClick(XBOX))\n      Serial.println(F(\"Xbox\"));\n    if (Xbox.getButtonClick(SYNC))\n      Serial.println(F(\"Sync\"));\n\n    if (Xbox.getButtonClick(L1))\n      Serial.println(F(\"L1\"));\n    if (Xbox.getButtonClick(R1))\n      Serial.println(F(\"R1\"));\n    if (Xbox.getButtonClick(L2))\n      Serial.println(F(\"L2\"));\n    if (Xbox.getButtonClick(R2))\n      Serial.println(F(\"R2\"));\n    if (Xbox.getButtonClick(L3))\n      Serial.println(F(\"L3\"));\n    if (Xbox.getButtonClick(R3))\n      Serial.println(F(\"R3\"));\n\n\n    if (Xbox.getButtonClick(A))\n      Serial.println(F(\"A\"));\n    if (Xbox.getButtonClick(B))\n      Serial.println(F(\"B\"));\n    if (Xbox.getButtonClick(X))\n      Serial.println(F(\"X\"));\n    if (Xbox.getButtonClick(Y))\n      Serial.println(F(\"Y\"));\n  }\n  delay(1);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Xbox/XBOXRECV/XBOXRECV.ino",
    "content": "/*\n Example sketch for the Xbox Wireless Reciver library - developed by Kristian Lauszus\n It supports up to four controllers wirelessly\n For more information see the blog post: http://blog.tkjelectronics.dk/2012/12/xbox-360-receiver-added-to-the-usb-host-library/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <XBOXRECV.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\nXBOXRECV Xbox(&Usb);\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nXbox Wireless Receiver Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n  if (Xbox.XboxReceiverConnected) {\n    for (uint8_t i = 0; i < 4; i++) {\n      if (Xbox.Xbox360Connected[i]) {\n        if (Xbox.getButtonPress(L2, i) || Xbox.getButtonPress(R2, i)) {\n          Serial.print(\"L2: \");\n          Serial.print(Xbox.getButtonPress(L2, i));\n          Serial.print(\"\\tR2: \");\n          Serial.println(Xbox.getButtonPress(R2, i));\n          Xbox.setRumbleOn(Xbox.getButtonPress(L2, i), Xbox.getButtonPress(R2, i), i);\n        }\n\n        if (Xbox.getAnalogHat(LeftHatX, i) > 7500 || Xbox.getAnalogHat(LeftHatX, i) < -7500 || Xbox.getAnalogHat(LeftHatY, i) > 7500 || Xbox.getAnalogHat(LeftHatY, i) < -7500 || Xbox.getAnalogHat(RightHatX, i) > 7500 || Xbox.getAnalogHat(RightHatX, i) < -7500 || Xbox.getAnalogHat(RightHatY, i) > 7500 || Xbox.getAnalogHat(RightHatY, i) < -7500) {\n          if (Xbox.getAnalogHat(LeftHatX, i) > 7500 || Xbox.getAnalogHat(LeftHatX, i) < -7500) {\n            Serial.print(F(\"LeftHatX: \"));\n            Serial.print(Xbox.getAnalogHat(LeftHatX, i));\n            Serial.print(\"\\t\");\n          }\n          if (Xbox.getAnalogHat(LeftHatY, i) > 7500 || Xbox.getAnalogHat(LeftHatY, i) < -7500) {\n            Serial.print(F(\"LeftHatY: \"));\n            Serial.print(Xbox.getAnalogHat(LeftHatY, i));\n            Serial.print(\"\\t\");\n          }\n          if (Xbox.getAnalogHat(RightHatX, i) > 7500 || Xbox.getAnalogHat(RightHatX, i) < -7500) {\n            Serial.print(F(\"RightHatX: \"));\n            Serial.print(Xbox.getAnalogHat(RightHatX, i));\n            Serial.print(\"\\t\");\n          }\n          if (Xbox.getAnalogHat(RightHatY, i) > 7500 || Xbox.getAnalogHat(RightHatY, i) < -7500) {\n            Serial.print(F(\"RightHatY: \"));\n            Serial.print(Xbox.getAnalogHat(RightHatY, i));\n          }\n          Serial.println();\n        }\n\n        if (Xbox.getButtonClick(UP, i)) {\n          Xbox.setLedOn(LED1, i);\n          Serial.println(F(\"Up\"));\n        }\n        if (Xbox.getButtonClick(DOWN, i)) {\n          Xbox.setLedOn(LED4, i);\n          Serial.println(F(\"Down\"));\n        }\n        if (Xbox.getButtonClick(LEFT, i)) {\n          Xbox.setLedOn(LED3, i);\n          Serial.println(F(\"Left\"));\n        }\n        if (Xbox.getButtonClick(RIGHT, i)) {\n          Xbox.setLedOn(LED2, i);\n          Serial.println(F(\"Right\"));\n        }\n\n        if (Xbox.getButtonClick(START, i)) {\n          Xbox.setLedMode(ALTERNATING, i);\n          Serial.println(F(\"Start\"));\n        }\n        if (Xbox.getButtonClick(BACK, i)) {\n          Xbox.setLedBlink(ALL, i);\n          Serial.println(F(\"Back\"));\n        }\n        if (Xbox.getButtonClick(L3, i))\n          Serial.println(F(\"L3\"));\n        if (Xbox.getButtonClick(R3, i))\n          Serial.println(F(\"R3\"));\n\n        if (Xbox.getButtonClick(L1, i))\n          Serial.println(F(\"L1\"));\n        if (Xbox.getButtonClick(R1, i))\n          Serial.println(F(\"R1\"));\n        if (Xbox.getButtonClick(XBOX, i)) {\n          Xbox.setLedMode(ROTATING, i);\n          Serial.print(F(\"Xbox (Battery: \"));\n          Serial.print(Xbox.getBatteryLevel(i)); // The battery level in the range 0-3\n          Serial.println(F(\")\"));\n        }\n        if (Xbox.getButtonClick(SYNC, i)) {\n          Serial.println(F(\"Sync\"));\n          Xbox.disconnect(i);\n        }\n\n        if (Xbox.getButtonClick(A, i))\n          Serial.println(F(\"A\"));\n        if (Xbox.getButtonClick(B, i))\n          Serial.println(F(\"B\"));\n        if (Xbox.getButtonClick(X, i))\n          Serial.println(F(\"X\"));\n        if (Xbox.getButtonClick(Y, i))\n          Serial.println(F(\"Y\"));\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/Xbox/XBOXUSB/XBOXUSB.ino",
    "content": "/*\n Example sketch for the Xbox 360 USB library - developed by Kristian Lauszus\n For more information visit my blog: http://blog.tkjelectronics.dk/ or\n send me an e-mail:  kristianl@tkjelectronics.com\n */\n\n#include <XBOXUSB.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\nXBOXUSB Xbox(&Usb);\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(F(\"\\r\\nOSC did not start\"));\n    while (1); //halt\n  }\n  Serial.print(F(\"\\r\\nXBOX USB Library Started\"));\n}\nvoid loop() {\n  Usb.Task();\n  if (Xbox.Xbox360Connected) {\n    if (Xbox.getButtonPress(L2) || Xbox.getButtonPress(R2)) {\n      Serial.print(\"L2: \");\n      Serial.print(Xbox.getButtonPress(L2));\n      Serial.print(\"\\tR2: \");\n      Serial.println(Xbox.getButtonPress(R2));\n      Xbox.setRumbleOn(Xbox.getButtonPress(L2), Xbox.getButtonPress(R2));\n    } else\n      Xbox.setRumbleOn(0, 0);\n\n    if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500 || Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500 || Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500 || Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) {\n      if (Xbox.getAnalogHat(LeftHatX) > 7500 || Xbox.getAnalogHat(LeftHatX) < -7500) {\n        Serial.print(F(\"LeftHatX: \"));\n        Serial.print(Xbox.getAnalogHat(LeftHatX));\n        Serial.print(\"\\t\");\n      }\n      if (Xbox.getAnalogHat(LeftHatY) > 7500 || Xbox.getAnalogHat(LeftHatY) < -7500) {\n        Serial.print(F(\"LeftHatY: \"));\n        Serial.print(Xbox.getAnalogHat(LeftHatY));\n        Serial.print(\"\\t\");\n      }\n      if (Xbox.getAnalogHat(RightHatX) > 7500 || Xbox.getAnalogHat(RightHatX) < -7500) {\n        Serial.print(F(\"RightHatX: \"));\n        Serial.print(Xbox.getAnalogHat(RightHatX));\n        Serial.print(\"\\t\");\n      }\n      if (Xbox.getAnalogHat(RightHatY) > 7500 || Xbox.getAnalogHat(RightHatY) < -7500) {\n        Serial.print(F(\"RightHatY: \"));\n        Serial.print(Xbox.getAnalogHat(RightHatY));\n      }\n      Serial.println();\n    }\n\n    if (Xbox.getButtonClick(UP)) {\n      Xbox.setLedOn(LED1);\n      Serial.println(F(\"Up\"));\n    }\n    if (Xbox.getButtonClick(DOWN)) {\n      Xbox.setLedOn(LED4);\n      Serial.println(F(\"Down\"));\n    }\n    if (Xbox.getButtonClick(LEFT)) {\n      Xbox.setLedOn(LED3);\n      Serial.println(F(\"Left\"));\n    }\n    if (Xbox.getButtonClick(RIGHT)) {\n      Xbox.setLedOn(LED2);\n      Serial.println(F(\"Right\"));\n    }\n\n    if (Xbox.getButtonClick(START)) {\n      Xbox.setLedMode(ALTERNATING);\n      Serial.println(F(\"Start\"));\n    }\n    if (Xbox.getButtonClick(BACK)) {\n      Xbox.setLedBlink(ALL);\n      Serial.println(F(\"Back\"));\n    }\n    if (Xbox.getButtonClick(L3))\n      Serial.println(F(\"L3\"));\n    if (Xbox.getButtonClick(R3))\n      Serial.println(F(\"R3\"));\n\n    if (Xbox.getButtonClick(L1))\n      Serial.println(F(\"L1\"));\n    if (Xbox.getButtonClick(R1))\n      Serial.println(F(\"R1\"));\n    if (Xbox.getButtonClick(XBOX)) {\n      Xbox.setLedMode(ROTATING);\n      Serial.println(F(\"Xbox\"));\n    }\n\n    if (Xbox.getButtonClick(A))\n      Serial.println(F(\"A\"));\n    if (Xbox.getButtonClick(B))\n      Serial.println(F(\"B\"));\n    if (Xbox.getButtonClick(X))\n      Serial.println(F(\"X\"));\n    if (Xbox.getButtonClick(Y))\n      Serial.println(F(\"Y\"));\n  }\n  delay(1);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/acm/acm_terminal/acm_terminal.ino",
    "content": "#include <cdcacm.h>\n#include <usbhub.h>\n\n#include \"pgmstrings.h\"\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nclass ACMAsyncOper : public CDCAsyncOper\n{\npublic:\n    uint8_t OnInit(ACM *pacm);\n};\n\nuint8_t ACMAsyncOper::OnInit(ACM *pacm)\n{\n    uint8_t rcode;\n    // Set DTR = 1 RTS=1\n    rcode = pacm->SetControlLineState(3);\n\n    if (rcode)\n    {\n        ErrorMessage<uint8_t>(PSTR(\"SetControlLineState\"), rcode);\n        return rcode;\n    }\n\n    LINE_CODING\tlc;\n    lc.dwDTERate\t= 115200;\n    lc.bCharFormat\t= 0;\n    lc.bParityType\t= 0;\n    lc.bDataBits\t= 8;\n\n    rcode = pacm->SetLineCoding(&lc);\n\n    if (rcode)\n        ErrorMessage<uint8_t>(PSTR(\"SetLineCoding\"), rcode);\n\n    return rcode;\n}\n\nUSB     Usb;\n//USBHub     Hub(&Usb);\nACMAsyncOper  AsyncOper;\nACM           Acm(&Usb, &AsyncOper);\n\nvoid setup()\n{\n  Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"Start\");\n\n  if (Usb.Init() == -1)\n      Serial.println(\"OSCOKIRQ failed to assert\");\n\n  delay( 200 );\n}\n\nvoid loop()\n{\n    Usb.Task();\n\n    if( Acm.isReady()) {\n       uint8_t rcode;\n\n       /* reading the keyboard */\n       if(Serial.available()) {\n         uint8_t data= Serial.read();\n         /* sending to the phone */\n         rcode = Acm.SndData(1, &data);\n         if (rcode)\n            ErrorMessage<uint8_t>(PSTR(\"SndData\"), rcode);\n       }//if(Serial.available()...\n\n       delay(50);\n\n        /* reading the phone */\n        /* buffer size must be greater or equal to max.packet size */\n        /* it it set to 64 (largest possible max.packet size) here, can be tuned down\n        for particular endpoint */\n        uint8_t  buf[64];\n        uint16_t rcvd = 64;\n        rcode = Acm.RcvData(&rcvd, buf);\n         if (rcode && rcode != hrNAK)\n            ErrorMessage<uint8_t>(PSTR(\"Ret\"), rcode);\n\n            if( rcvd ) { //more than zero bytes received\n              for(uint16_t i=0; i < rcvd; i++ ) {\n                Serial.print((char)buf[i]); //printing on the screen\n              }\n            }\n        delay(10);\n    }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING..\n}\n\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/acm/acm_terminal/pgmstrings.h",
    "content": "#if !defined(__PGMSTRINGS_H__)\n#define __PGMSTRINGS_H__\n\n#define LOBYTE(x) ((char*)(&(x)))[0]\n#define HIBYTE(x) ((char*)(&(x)))[1]\n#define BUFSIZE 256    //buffer size\n \n\n/* Print strings in Program Memory */\nconst char Gen_Error_str[] PROGMEM = \"\\r\\nRequest error. Error code:\\t\"; \nconst char Dev_Header_str[] PROGMEM =\"\\r\\nDevice descriptor: \";\nconst char Dev_Length_str[] PROGMEM =\"\\r\\nDescriptor Length:\\t\";\nconst char Dev_Type_str[] PROGMEM =\"\\r\\nDescriptor type:\\t\";\nconst char Dev_Version_str[] PROGMEM =\"\\r\\nUSB version:\\t\\t\";\nconst char Dev_Class_str[] PROGMEM =\"\\r\\nDevice class:\\t\\t\";\nconst char Dev_Subclass_str[] PROGMEM =\"\\r\\nDevice Subclass:\\t\";\nconst char Dev_Protocol_str[] PROGMEM =\"\\r\\nDevice Protocol:\\t\";\nconst char Dev_Pktsize_str[] PROGMEM =\"\\r\\nMax.packet size:\\t\";\nconst char Dev_Vendor_str[] PROGMEM =\"\\r\\nVendor  ID:\\t\\t\";\nconst char Dev_Product_str[] PROGMEM =\"\\r\\nProduct ID:\\t\\t\";\nconst char Dev_Revision_str[] PROGMEM =\"\\r\\nRevision ID:\\t\\t\";\nconst char Dev_Mfg_str[] PROGMEM =\"\\r\\nMfg.string index:\\t\";\nconst char Dev_Prod_str[] PROGMEM =\"\\r\\nProd.string index:\\t\";\nconst char Dev_Serial_str[] PROGMEM =\"\\r\\nSerial number index:\\t\";\nconst char Dev_Nconf_str[] PROGMEM =\"\\r\\nNumber of conf.:\\t\";\nconst char Conf_Trunc_str[] PROGMEM =\"Total length truncated to 256 bytes\";\nconst char Conf_Header_str[] PROGMEM =\"\\r\\nConfiguration descriptor:\";\nconst char Conf_Totlen_str[] PROGMEM =\"\\r\\nTotal length:\\t\\t\";\nconst char Conf_Nint_str[] PROGMEM =\"\\r\\nNum.intf:\\t\\t\";\nconst char Conf_Value_str[] PROGMEM =\"\\r\\nConf.value:\\t\\t\";\nconst char Conf_String_str[] PROGMEM =\"\\r\\nConf.string:\\t\\t\";\nconst char Conf_Attr_str[] PROGMEM =\"\\r\\nAttr.:\\t\\t\\t\";\nconst char Conf_Pwr_str[] PROGMEM =\"\\r\\nMax.pwr:\\t\\t\";\nconst char Int_Header_str[] PROGMEM =\"\\r\\n\\r\\nInterface descriptor:\";\nconst char Int_Number_str[] PROGMEM =\"\\r\\nIntf.number:\\t\\t\";\nconst char Int_Alt_str[] PROGMEM =\"\\r\\nAlt.:\\t\\t\\t\";\nconst char Int_Endpoints_str[] PROGMEM =\"\\r\\nEndpoints:\\t\\t\";\nconst char Int_Class_str[] PROGMEM =\"\\r\\nIntf. Class:\\t\\t\";\nconst char Int_Subclass_str[] PROGMEM =\"\\r\\nIntf. Subclass:\\t\\t\";\nconst char Int_Protocol_str[] PROGMEM =\"\\r\\nIntf. Protocol:\\t\\t\";\nconst char Int_String_str[] PROGMEM =\"\\r\\nIntf.string:\\t\\t\";\nconst char End_Header_str[] PROGMEM =\"\\r\\n\\r\\nEndpoint descriptor:\";\nconst char End_Address_str[] PROGMEM =\"\\r\\nEndpoint address:\\t\";\nconst char End_Attr_str[] PROGMEM =\"\\r\\nAttr.:\\t\\t\\t\";\nconst char End_Pktsize_str[] PROGMEM =\"\\r\\nMax.pkt size:\\t\\t\";\nconst char End_Interval_str[] PROGMEM =\"\\r\\nPolling interval:\\t\";\nconst char Unk_Header_str[] PROGMEM = \"\\r\\nUnknown descriptor:\";\nconst char Unk_Length_str[] PROGMEM =\"\\r\\nLength:\\t\\t\";\nconst char Unk_Type_str[] PROGMEM =\"\\r\\nType:\\t\\t\";\nconst char Unk_Contents_str[] PROGMEM =\"\\r\\nContents:\\t\";\n \n#endif // __PGMSTRINGS_H__"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/adk/ArduinoBlinkLED/ArduinoBlinkLED.ino",
    "content": "// The source for the Android application can be found at the following link: https://github.com/Lauszus/ArduinoBlinkLED\n// The code for the Android application is heavily based on this guide: http://allaboutee.com/2011/12/31/arduino-adk-board-blink-an-led-with-your-phone-code-and-explanation/ by Miguel\n#include <adk.h>\n\n//\n// CAUTION! WARNING! ATTENTION! VORSICHT! ADVARSEL! ¡CUIDADO! ВНИМАНИЕ!\n//\n// Pin 13 is occupied by the SCK pin on various Arduino boards,\n// including Uno, Duemilanove, etc., so use a different pin for those boards.\n//\n// CAUTION! WARNING! ATTENTION! VORSICHT! ADVARSEL! ¡CUIDADO! ВНИМАНИЕ!\n//\n#if defined(LED_BUILTIN)\n#define LED LED_BUILTIN // Use built in LED\n#else\n#define LED 9 // Set to something here that makes sense for your board.\n#endif\n\n\n// Satisfy IDE, which only needs to see the include statment in the ino.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\nADK adk(&Usb, \"TKJElectronics\", // Manufacturer Name\n              \"ArduinoBlinkLED\", // Model Name\n              \"Example sketch for the USB Host Shield\", // Description (user-visible string)\n              \"1.0\", // Version\n              \"http://www.tkjelectronics.dk/uploads/ArduinoBlinkLED.apk\", // URL (web page to visit if no installed apps support the accessory)\n              \"123456789\"); // Serial Number (optional)\n\nuint32_t timer;\nbool connected;\n\nvoid setup() {\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  if (Usb.Init() == -1) {\n    Serial.print(\"\\r\\nOSCOKIRQ failed to assert\");\n    while (1); // halt\n  }\n  pinMode(LED, OUTPUT);\n  Serial.print(\"\\r\\nArduino Blink LED Started\");\n}\n\nvoid loop() {\n  Usb.Task();\n\n  if (adk.isReady()) {\n    if (!connected) {\n      connected = true;\n      Serial.print(F(\"\\r\\nConnected to accessory\"));\n    }\n\n    uint8_t msg[1];\n    uint16_t len = sizeof(msg);\n    uint8_t rcode = adk.RcvData(&len, msg);\n    if (rcode && rcode != hrNAK) {\n      Serial.print(F(\"\\r\\nData rcv: \"));\n      Serial.print(rcode, HEX);\n    } else if (len > 0) {\n      Serial.print(F(\"\\r\\nData Packet: \"));\n      Serial.print(msg[0]);\n      digitalWrite(LED, msg[0] ? HIGH : LOW);\n    }\n\n    if (millis() - timer >= 1000) { // Send data every 1s\n      timer = millis();\n      rcode = adk.SndData(sizeof(timer), (uint8_t*)&timer);\n      if (rcode && rcode != hrNAK) {\n        Serial.print(F(\"\\r\\nData send: \"));\n        Serial.print(rcode, HEX);\n      } else if (rcode != hrNAK) {\n        Serial.print(F(\"\\r\\nTimer: \"));\n        Serial.print(timer);\n      }\n    }\n  } else {\n    if (connected) {\n      connected = false;\n      Serial.print(F(\"\\r\\nDisconnected from accessory\"));\n      digitalWrite(LED, LOW);\n    }\n  }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/adk/adk_barcode/adk_barcode.ino",
    "content": "/**/\n/* A sketch demonstrating data exchange between two USB devices - a HID barcode scanner and ADK-compatible Android phone */\n/**/\n#include <adk.h>\n#include <hidboot.h>\n#include <usbhub.h>\n\n// Satisfy IDE, which only needs to see the include statment in the ino.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\nUSBHub Hub1(&Usb);\nUSBHub Hub2(&Usb);\nHIDBoot<HID_PROTOCOL_KEYBOARD> Keyboard(&Usb);\n\nADK adk(&Usb,\"Circuits@Home, ltd.\",\n            \"USB Host Shield\",\n            \"Arduino Terminal for Android\",\n            \"1.0\",\n            \"http://www.circuitsathome.com\",\n            \"0000000000000001\");\n\n\nclass KbdRptParser : public KeyboardReportParser\n{\n\nprotected:\n\tvoid OnKeyDown\t(uint8_t mod, uint8_t key);\n\tvoid OnKeyPressed(uint8_t key);\n};\n\nvoid KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key)\n{\n    uint8_t c = OemToAscii(mod, key);\n\n    if (c)\n        OnKeyPressed(c);\n}\n\n/* what to do when symbol arrives */\nvoid KbdRptParser::OnKeyPressed(uint8_t key)\n{\nconst char* new_line = \"\\n\";\nuint8_t rcode;\nuint8_t keylcl;\n\n if( adk.isReady() == false ) {\n   return;\n }\n\n  keylcl = key;\n\n  if( keylcl == 0x13 ) {\n    rcode = adk.SndData( strlen( new_line ), (uint8_t *)new_line );\n  }\n  else {\n    rcode = adk.SndData( 1, &keylcl );\n  }\n\n  Serial.print((char) keylcl );\n  Serial.print(\" : \");\n  Serial.println( keylcl, HEX );\n};\n\nKbdRptParser Prs;\n\nvoid setup()\n{\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"\\r\\nADK demo start\");\n\n  if (Usb.Init() == -1) {\n    Serial.println(\"OSCOKIRQ failed to assert\");\n    while(1); //halt\n  }//if (Usb.Init() == -1...\n\n  Keyboard.SetReportParser(0, (HIDReportParser*)&Prs);\n\n  delay( 200 );\n}\n\nvoid loop()\n{\n  Usb.Task();\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/adk/demokit_20/demokit_20.ino",
    "content": "#include <adk.h>\n#include <usbhub.h>\n\n// Satisfy IDE, which only needs to see the include statment in the ino.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\nUSBHub hub0(&Usb);\nUSBHub hub1(&Usb);\nADK adk(&Usb,\"Google, Inc.\",\n            \"DemoKit\",\n            \"DemoKit Arduino Board\",\n            \"1.0\",\n            \"http://www.android.com\",\n            \"0000000012345678\");\nuint8_t  b, b1;\n\n\n#define  LED1_RED       3\n#define  BUTTON1        2\n\nvoid init_buttons()\n{\n\tpinMode(BUTTON1, INPUT);\n\n\t// enable the internal pullups\n\tdigitalWrite(BUTTON1, HIGH);\n}\n\nvoid init_leds()\n{\n\tdigitalWrite(LED1_RED, 0);\n\n\tpinMode(LED1_RED, OUTPUT);\n}\n\nvoid setup()\n{\n\tSerial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n\tSerial.println(\"\\r\\nADK demo start\");\n\n        if (Usb.Init() == -1) {\n          Serial.println(\"OSCOKIRQ failed to assert\");\n        while(1); //halt\n        }//if (Usb.Init() == -1...\n\n\n\tinit_leds();\n\tinit_buttons();\n\tb1 = digitalRead(BUTTON1);\n}\n\nvoid loop()\n{\n  uint8_t rcode;\n  uint8_t msg[3] = { 0x00 };\n   Usb.Task();\n\n   if( adk.isReady() == false ) {\n     analogWrite(LED1_RED, 255);\n     return;\n   }\n   uint16_t len = sizeof(msg);\n\n   rcode = adk.RcvData(&len, msg);\n   if( rcode ) {\n     USBTRACE2(\"Data rcv. :\", rcode );\n   }\n   if(len > 0) {\n     USBTRACE(\"\\r\\nData Packet.\");\n    // assumes only one command per packet\n    if (msg[0] == 0x2) {\n      switch( msg[1] ) {\n        case 0:\n          analogWrite(LED1_RED, 255 - msg[2]);\n          break;\n      }//switch( msg[1]...\n    }//if (msg[0] == 0x2...\n   }//if( len > 0...\n\n   msg[0] = 0x1;\n\n   b = digitalRead(BUTTON1);\n   if (b != b1) {\n     USBTRACE(\"\\r\\nButton state changed\");\n     msg[1] = 0;\n     msg[2] = b ? 0 : 1;\n     rcode = adk.SndData( 3, msg );\n     if( rcode ) {\n       USBTRACE2(\"Button send: \", rcode );\n     }\n     b1 = b;\n    }//if (b != b1...\n\n\n      delay( 10 );\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/adk/term_test/term_test.ino",
    "content": "#include <adk.h>\n#include <usbhub.h>\n\n// Satisfy IDE, which only needs to see the include statment in the ino.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n//USBHub     Hub(&Usb);\n\nADK adk(&Usb,\"Circuits@Home, ltd.\",\n            \"USB Host Shield\",\n            \"Arduino Terminal for Android\",\n            \"1.0\",\n            \"http://www.circuitsathome.com\",\n            \"0000000000000001\");\n\nvoid setup()\n{\n\tSerial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n\tSerial.println(\"\\r\\nADK demo start\");\n\n        if (Usb.Init() == -1) {\n          Serial.println(\"OSCOKIRQ failed to assert\");\n        while(1); //halt\n        }//if (Usb.Init() == -1...\n}\n\nvoid loop()\n{\n  uint8_t rcode;\n  uint8_t msg[64] = { 0x00 };\n  const char* recv = \"Received: \";\n\n   Usb.Task();\n\n   if( adk.isReady() == false ) {\n     return;\n   }\n   uint16_t len = 64;\n\n   rcode = adk.RcvData(&len, msg);\n   if( rcode & ( rcode != hrNAK )) {\n     USBTRACE2(\"Data rcv. :\", rcode );\n   }\n   if(len > 0) {\n     USBTRACE(\"\\r\\nData Packet.\");\n\n    for( uint8_t i = 0; i < len; i++ ) {\n      Serial.print((char)msg[i]);\n    }\n    /* sending back what was received */\n    rcode = adk.SndData( strlen( recv ), (uint8_t *)recv );\n    rcode = adk.SndData( strlen(( char * )msg ), msg );\n\n   }//if( len > 0 )...\n\n   delay( 1000 );\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/adk/term_time/term_time.ino",
    "content": "#include <adk.h>\n#include <usbhub.h>\n\n// Satisfy IDE, which only needs to see the include statment in the ino.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\n\nADK adk(&Usb,\"Circuits@Home, ltd.\",\n            \"USB Host Shield\",\n            \"Arduino Terminal for Android\",\n            \"1.0\",\n            \"http://www.circuitsathome.com\",\n            \"0000000000000001\");\n\nvoid setup()\n{\n\tSerial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n\tSerial.println(\"\\r\\nADK demo start\");\n\n        if (Usb.Init() == -1) {\n          Serial.println(\"OSCOKIRQ failed to assert\");\n        while(1); //halt\n        }//if (Usb.Init() == -1...\n}\n\nvoid loop()\n{\n  uint8_t buf[ 12 ] = { 0 }; //buffer to convert unsigned long to ASCII\n  const char* sec_ela = \" seconds elapsed\\r\";\n  uint8_t rcode;\n\n   Usb.Task();\n   if( adk.isReady() == false ) {\n     return;\n   }\n\n    ultoa( millis()/1000, (char *)buf, 10 );\n\n    rcode = adk.SndData( strlen((char *)buf), buf );\n    rcode = adk.SndData( strlen( sec_ela), (uint8_t *)sec_ela );\n\n      delay( 1000 );\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/board_qc/board_qc.ino",
    "content": "/* USB Host Shield 2.0 board quality control routine */\n/* To see the output set your terminal speed to 115200 */\n/* for GPIO test to pass you need to connect GPIN0 to GPOUT7, GPIN1 to GPOUT6, etc. */\n/* otherwise press any key after getting GPIO error to complete the test */\n/**/\n#include <usbhub.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <../../../../hardware/pic32/libraries/SPI/SPI.h> // Hack to use the SPI library\n#include <SPI.h> // Hack to use the SPI library\n#endif\n\n/* variables */\nuint8_t rcode;\nuint8_t usbstate;\nuint8_t laststate;\n//uint8_t buf[sizeof(USB_DEVICE_DESCRIPTOR)];\nUSB_DEVICE_DESCRIPTOR buf;\n\n/* objects */\nUSB Usb;\n//USBHub hub(&Usb);\n\nvoid setup() {\n        laststate = 0;\n        Serial.begin(115200);\n#if !defined(__MIPSEL__)\n        while(!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n        E_Notify(PSTR(\"\\r\\nCircuits At Home 2011\"), 0x80);\n        E_Notify(PSTR(\"\\r\\nUSB Host Shield Quality Control Routine\"), 0x80);\n        /* SPI quick test - check revision register */\n        E_Notify(PSTR(\"\\r\\nReading REVISION register... Die revision \"), 0x80);\n        Usb.Init(); // Initializes SPI, we don't care about the return value here\n        {\n                uint8_t tmpbyte = Usb.regRd(rREVISION);\n                switch(tmpbyte) {\n                        case( 0x01): //rev.01\n                                E_Notify(PSTR(\"01\"), 0x80);\n                                break;\n                        case( 0x12): //rev.02\n                                E_Notify(PSTR(\"02\"), 0x80);\n                                break;\n                        case( 0x13): //rev.03\n                                E_Notify(PSTR(\"03\"), 0x80);\n                                break;\n                        default:\n                                E_Notify(PSTR(\"invalid. Value returned: \"), 0x80);\n                                print_hex(tmpbyte, 8);\n                                halt55();\n                                break;\n                }//switch( tmpbyte...\n        }//check revision register\n        /* SPI long test */\n        {\n                E_Notify(PSTR(\"\\r\\nSPI long test. Transfers 1MB of data. Each dot is 64K\"), 0x80);\n                uint8_t sample_wr = 0;\n                uint8_t sample_rd = 0;\n                uint8_t gpinpol_copy = Usb.regRd(rGPINPOL);\n                for(uint8_t i = 0; i < 16; i++) {\n                        for(uint16_t j = 0; j < 65535; j++) {\n                                Usb.regWr(rGPINPOL, sample_wr);\n                                sample_rd = Usb.regRd(rGPINPOL);\n                                if(sample_rd != sample_wr) {\n                                        E_Notify(PSTR(\"\\r\\nTest failed.  \"), 0x80);\n                                        E_Notify(PSTR(\"Value written: \"), 0x80);\n                                        print_hex(sample_wr, 8);\n                                        E_Notify(PSTR(\" read: \"), 0x80);\n                                        print_hex(sample_rd, 8);\n                                        halt55();\n                                }//if( sample_rd != sample_wr..\n                                sample_wr++;\n                        }//for( uint16_t j...\n                        E_Notify(PSTR(\".\"), 0x80);\n                }//for( uint8_t i...\n                Usb.regWr(rGPINPOL, gpinpol_copy);\n                E_Notify(PSTR(\" SPI long test passed\"), 0x80);\n        }//SPI long test\n        /* GPIO test */\n        /* in order to simplify board layout, GPIN pins on text fixture are connected to GPOUT */\n        /* in reverse order, i.e, GPIN0 is connected to GPOUT7, GPIN1 to GPOUT6, etc. */\n        {\n                uint8_t tmpbyte;\n                E_Notify(PSTR(\"\\r\\nGPIO test. Connect GPIN0 to GPOUT7, GPIN1 to GPOUT6, and so on\"), 0x80);\n                for(uint8_t sample_gpio = 0; sample_gpio < 255; sample_gpio++) {\n                        Usb.gpioWr(sample_gpio);\n                        tmpbyte = Usb.gpioRd();\n                        /* bit reversing code copied vetbatim from http://graphics.stanford.edu/~seander/bithacks.html#BitReverseObvious */\n                        tmpbyte = ((tmpbyte * 0x0802LU & 0x22110LU) | (tmpbyte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;\n                        if(sample_gpio != tmpbyte) {\n                                E_Notify(PSTR(\"\\r\\nTest failed. Value written: \"), 0x80);\n                                print_hex(sample_gpio, 8);\n                                E_Notify(PSTR(\" Value read: \"), 0x80);\n                                print_hex(tmpbyte, 8);\n                                E_Notify(PSTR(\" \"), 0x80);\n                                press_any_key();\n                                break;\n                        }//if( sample_gpio != tmpbyte...\n                }//for( uint8_t sample_gpio...\n                E_Notify(PSTR(\"\\r\\nGPIO test passed.\"), 0x80);\n        }//GPIO test\n        /* PLL test. Stops/starts MAX3421E oscillator several times */\n        {\n                E_Notify(PSTR(\"\\r\\nPLL test. 100 chip resets will be performed\"), 0x80);\n                /* check current state of the oscillator */\n                if(!(Usb.regRd(rUSBIRQ) & bmOSCOKIRQ)) { //wrong state - should be on\n                        E_Notify(PSTR(\"\\r\\nCurrent oscillator state unexpected.\"), 0x80);\n                        press_any_key();\n                }\n                /* Restart oscillator */\n                E_Notify(PSTR(\"\\r\\nResetting oscillator\\r\\n\"), 0x80);\n                for(uint16_t i = 0; i < 100; i++) {\n                        E_Notify(PSTR(\"\\rReset number \"), 0x80);\n                        Serial.print(i, DEC);\n                        Usb.regWr(rUSBCTL, bmCHIPRES); //reset\n                        if(Usb.regRd(rUSBIRQ) & bmOSCOKIRQ) { //wrong state - should be off\n                                E_Notify(PSTR(\"\\r\\nCurrent oscillator state unexpected.\"), 0x80);\n                                halt55();\n                        }\n                        Usb.regWr(rUSBCTL, 0x00); //release from reset\n                        uint16_t j = 0;\n                        for(j = 1; j < 65535; j++) { //tracking off to on time\n                                if(Usb.regRd(rUSBIRQ) & bmOSCOKIRQ) {\n                                        E_Notify(PSTR(\" Time to stabilize - \"), 0x80);\n                                        Serial.print(j, DEC);\n                                        E_Notify(PSTR(\" cycles\\r\\n\"), 0x80);\n                                        break;\n                                }\n                        }//for( uint16_t j = 0; j < 65535; j++\n                        if(j == 0) {\n                                E_Notify(PSTR(\"PLL failed to stabilize\"), 0x80);\n                                press_any_key();\n                        }\n                }//for( uint8_t i = 0; i < 255; i++\n\n        }//PLL test\n        /* initializing USB stack */\n        if(Usb.Init() == -1) {\n                E_Notify(PSTR(\"\\r\\nOSCOKIRQ failed to assert\"), 0x80);\n                halt55();\n        }\n        E_Notify(PSTR(\"\\r\\nChecking USB device communication.\\r\\n\"), 0x80);\n}\n\nvoid loop() {\n        delay(200);\n        Usb.Task();\n        usbstate = Usb.getUsbTaskState();\n        if(usbstate != laststate) {\n                laststate = usbstate;\n                /**/\n                switch(usbstate) {\n                        case( USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE):\n                                E_Notify(PSTR(\"\\r\\nWaiting for device...\"), 0x80);\n                                break;\n                        case( USB_ATTACHED_SUBSTATE_RESET_DEVICE):\n                                E_Notify(PSTR(\"\\r\\nDevice connected. Resetting...\"), 0x80);\n                                break;\n                        case( USB_ATTACHED_SUBSTATE_WAIT_SOF):\n                                E_Notify(PSTR(\"\\r\\nReset complete. Waiting for the first SOF...\"), 0x80);\n                                break;\n                        case( USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE):\n                                E_Notify(PSTR(\"\\r\\nSOF generation started. Enumerating device...\"), 0x80);\n                                break;\n                        case( USB_STATE_ADDRESSING):\n                                E_Notify(PSTR(\"\\r\\nSetting device address...\"), 0x80);\n                                break;\n                        case( USB_STATE_RUNNING):\n                                E_Notify(PSTR(\"\\r\\nGetting device descriptor\"), 0x80);\n                                rcode = Usb.getDevDescr(1, 0, sizeof (USB_DEVICE_DESCRIPTOR), (uint8_t*) & buf);\n\n                                if(rcode) {\n                                        E_Notify(PSTR(\"\\r\\nError reading device descriptor. Error code \"), 0x80);\n                                        print_hex(rcode, 8);\n                                } else {\n                                        /**/\n                                        E_Notify(PSTR(\"\\r\\nDescriptor Length:\\t\"), 0x80);\n                                        print_hex(buf.bLength, 8);\n                                        E_Notify(PSTR(\"\\r\\nDescriptor type:\\t\"), 0x80);\n                                        print_hex(buf.bDescriptorType, 8);\n                                        E_Notify(PSTR(\"\\r\\nUSB version:\\t\\t\"), 0x80);\n                                        print_hex(buf.bcdUSB, 16);\n                                        E_Notify(PSTR(\"\\r\\nDevice class:\\t\\t\"), 0x80);\n                                        print_hex(buf.bDeviceClass, 8);\n                                        E_Notify(PSTR(\"\\r\\nDevice Subclass:\\t\"), 0x80);\n                                        print_hex(buf.bDeviceSubClass, 8);\n                                        E_Notify(PSTR(\"\\r\\nDevice Protocol:\\t\"), 0x80);\n                                        print_hex(buf.bDeviceProtocol, 8);\n                                        E_Notify(PSTR(\"\\r\\nMax.packet size:\\t\"), 0x80);\n                                        print_hex(buf.bMaxPacketSize0, 8);\n                                        E_Notify(PSTR(\"\\r\\nVendor  ID:\\t\\t\"), 0x80);\n                                        print_hex(buf.idVendor, 16);\n                                        E_Notify(PSTR(\"\\r\\nProduct ID:\\t\\t\"), 0x80);\n                                        print_hex(buf.idProduct, 16);\n                                        E_Notify(PSTR(\"\\r\\nRevision ID:\\t\\t\"), 0x80);\n                                        print_hex(buf.bcdDevice, 16);\n                                        E_Notify(PSTR(\"\\r\\nMfg.string index:\\t\"), 0x80);\n                                        print_hex(buf.iManufacturer, 8);\n                                        E_Notify(PSTR(\"\\r\\nProd.string index:\\t\"), 0x80);\n                                        print_hex(buf.iProduct, 8);\n                                        E_Notify(PSTR(\"\\r\\nSerial number index:\\t\"), 0x80);\n                                        print_hex(buf.iSerialNumber, 8);\n                                        E_Notify(PSTR(\"\\r\\nNumber of conf.:\\t\"), 0x80);\n                                        print_hex(buf.bNumConfigurations, 8);\n                                        /**/\n                                        E_Notify(PSTR(\"\\r\\n\\nAll tests passed. Press RESET to restart test\"), 0x80);\n                                        while(1);\n                                }\n                                break;\n                        case( USB_STATE_ERROR):\n                                E_Notify(PSTR(\"\\r\\nUSB state machine reached error state\"), 0x80);\n                                break;\n\n                        default:\n                                break;\n                }//switch( usbstate...\n        }\n}//loop()...\n\n/* constantly transmits 0x55 via SPI to aid probing */\nvoid halt55() {\n\n        E_Notify(PSTR(\"\\r\\nUnrecoverable error - test halted!!\"), 0x80);\n        E_Notify(PSTR(\"\\r\\n0x55 pattern is transmitted via SPI\"), 0x80);\n        E_Notify(PSTR(\"\\r\\nPress RESET to restart test\"), 0x80);\n\n        while(1) {\n                Usb.regWr(0x55, 0x55);\n        }\n}\n\n/* prints hex numbers with leading zeroes */\nvoid print_hex(int v, int num_places) {\n        int mask = 0, n, num_nibbles, digit;\n\n        for(n = 1; n <= num_places; n++) {\n                mask = (mask << 1) | 0x0001;\n        }\n        v = v & mask; // truncate v to specified number of places\n\n        num_nibbles = num_places / 4;\n        if((num_places % 4) != 0) {\n                ++num_nibbles;\n        }\n        do {\n                digit = ((v >> (num_nibbles - 1) * 4)) & 0x0f;\n                Serial.print(digit, HEX);\n        } while(--num_nibbles);\n}\n\n/* prints \"Press any key\" and returns when key is pressed */\nvoid press_any_key() {\n        E_Notify(PSTR(\"\\r\\nPress any key to continue...\"), 0x80);\n        while(Serial.available() <= 0); //wait for input\n        Serial.read(); //empty input buffer\n        return;\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/cdc_XR21B1411/XR_terminal/XR_terminal.ino",
    "content": "#include <cdc_XR21B1411.h>\n\n// Satisfy IDE, which only needs to see the include statment in the ino.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nclass ACMAsyncOper : public CDCAsyncOper\n{\npublic:\n    uint8_t OnInit(ACM *pacm);\n};\n\nuint8_t ACMAsyncOper::OnInit(ACM *pacm)\n{\n    uint8_t rcode;\n    // Set DTR = 1 RTS=1\n    rcode = pacm->SetControlLineState(3);\n\n    if (rcode)\n    {\n        ErrorMessage<uint8_t>(PSTR(\"SetControlLineState\"), rcode);\n        return rcode;\n    }\n\n    LINE_CODING\tlc;\n    lc.dwDTERate\t= 115200;\n    lc.bCharFormat\t= 0;\n    lc.bParityType\t= 0;\n    lc.bDataBits\t= 8;\n\n    rcode = pacm->SetLineCoding(&lc);\n\n    if (rcode)\n        ErrorMessage<uint8_t>(PSTR(\"SetLineCoding\"), rcode);\n\n    return rcode;\n}\n\nUSB     Usb;\nACMAsyncOper  AsyncOper;\nXR21B1411     Acm(&Usb, &AsyncOper);\n\nvoid setup() {\n        Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n        while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n        Serial.println(\"\\r\\n\\r\\nStart\");\n\n        if (Usb.Init() == -1) Serial.println(\"OSCOKIRQ failed to assert\");\n}\n\nvoid loop() {\n        Usb.Task();\n        if( Acm.isReady()) {\n                uint8_t rcode;\n                uint8_t  buf[1];\n                uint16_t rcvd = 1;\n\n                /* read keyboard */\n                if(Serial.available()) {\n                         uint8_t data = Serial.read();\n                         /* send */\n                         rcode = Acm.SndData(1, &data);\n                         if (rcode)\n                                 ErrorMessage<uint8_t>(PSTR(\"SndData\"), rcode);\n                 }\n                \n                /* read XR serial */\n                rcode = Acm.RcvData(&rcvd, buf);\n                if (rcode && rcode != hrNAK)\n                        ErrorMessage<uint8_t>(PSTR(\"Ret\"), rcode);\n\n                if( rcvd ) { //more than zero bytes received\n                        for(uint16_t i=0; i < rcvd; i++ ) {\n                                Serial.print((char)buf[i]);\n                        }\n                }\n        }\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/ftdi/USBFTDILoopback/USBFTDILoopback.ino",
    "content": "#include <cdcftdi.h>\n#include <usbhub.h>\n\n#include \"pgmstrings.h\"\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nclass FTDIAsync : public FTDIAsyncOper\n{\npublic:\n    uint8_t OnInit(FTDI *pftdi);\n};\n\nuint8_t FTDIAsync::OnInit(FTDI *pftdi)\n{\n    uint8_t rcode = 0;\n\n    rcode = pftdi->SetBaudRate(115200);\n\n    if (rcode)\n    {\n        ErrorMessage<uint8_t>(PSTR(\"SetBaudRate\"), rcode);\n        return rcode;\n    }\n    rcode = pftdi->SetFlowControl(FTDI_SIO_DISABLE_FLOW_CTRL);\n\n    if (rcode)\n        ErrorMessage<uint8_t>(PSTR(\"SetFlowControl\"), rcode);\n\n    return rcode;\n}\n\nUSB              Usb;\n//USBHub         Hub(&Usb);\nFTDIAsync        FtdiAsync;\nFTDI             Ftdi(&Usb, &FtdiAsync);\n\nuint32_t next_time;\n\nvoid setup()\n{\n  Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"Start\");\n\n  if (Usb.Init() == -1)\n      Serial.println(\"OSC did not start.\");\n\n  delay( 200 );\n\n  next_time = millis() + 5000;\n}\n\nvoid loop()\n{\n    Usb.Task();\n\n    if( Usb.getUsbTaskState() == USB_STATE_RUNNING )\n    {\n        uint8_t  rcode;\n        char strbuf[] = \"DEADBEEF\";\n        //char strbuf[] = \"The quick brown fox jumps over the lazy dog\";\n        //char strbuf[] = \"This string contains 61 character to demonstrate FTDI buffers\"; //add one symbol to it to see some garbage\n        Serial.print(\".\");\n\n        rcode = Ftdi.SndData(strlen(strbuf), (uint8_t*)strbuf);\n\n\tif (rcode)\n            ErrorMessage<uint8_t>(PSTR(\"SndData\"), rcode);\n\n        delay(50);\n\n        uint8_t  buf[64];\n\n        for (uint8_t i=0; i<64; i++)\n            buf[i] = 0;\n\n        uint16_t rcvd = 64;\n        rcode = Ftdi.RcvData(&rcvd, buf);\n\n        if (rcode && rcode != hrNAK)\n            ErrorMessage<uint8_t>(PSTR(\"Ret\"), rcode);\n\n        // The device reserves the first two bytes of data\n        //   to contain the current values of the modem and line status registers.\n        if (rcvd > 2)\n            Serial.print((char*)(buf+2));\n\n        delay(10);\n    }\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/ftdi/USBFTDILoopback/pgmstrings.h",
    "content": "#if !defined(__PGMSTRINGS_H__)\n#define __PGMSTRINGS_H__\n\n#define LOBYTE(x) ((char*)(&(x)))[0]\n#define HIBYTE(x) ((char*)(&(x)))[1]\n#define BUFSIZE 256    //buffer size\n \n\n/* Print strings in Program Memory */\nconst char Gen_Error_str[] PROGMEM = \"\\r\\nRequest error. Error code:\\t\"; \nconst char Dev_Header_str[] PROGMEM =\"\\r\\nDevice descriptor: \";\nconst char Dev_Length_str[] PROGMEM =\"\\r\\nDescriptor Length:\\t\";\nconst char Dev_Type_str[] PROGMEM =\"\\r\\nDescriptor type:\\t\";\nconst char Dev_Version_str[] PROGMEM =\"\\r\\nUSB version:\\t\\t\";\nconst char Dev_Class_str[] PROGMEM =\"\\r\\nDevice class:\\t\\t\";\nconst char Dev_Subclass_str[] PROGMEM =\"\\r\\nDevice Subclass:\\t\";\nconst char Dev_Protocol_str[] PROGMEM =\"\\r\\nDevice Protocol:\\t\";\nconst char Dev_Pktsize_str[] PROGMEM =\"\\r\\nMax.packet size:\\t\";\nconst char Dev_Vendor_str[] PROGMEM =\"\\r\\nVendor  ID:\\t\\t\";\nconst char Dev_Product_str[] PROGMEM =\"\\r\\nProduct ID:\\t\\t\";\nconst char Dev_Revision_str[] PROGMEM =\"\\r\\nRevision ID:\\t\\t\";\nconst char Dev_Mfg_str[] PROGMEM =\"\\r\\nMfg.string index:\\t\";\nconst char Dev_Prod_str[] PROGMEM =\"\\r\\nProd.string index:\\t\";\nconst char Dev_Serial_str[] PROGMEM =\"\\r\\nSerial number index:\\t\";\nconst char Dev_Nconf_str[] PROGMEM =\"\\r\\nNumber of conf.:\\t\";\nconst char Conf_Trunc_str[] PROGMEM =\"Total length truncated to 256 bytes\";\nconst char Conf_Header_str[] PROGMEM =\"\\r\\nConfiguration descriptor:\";\nconst char Conf_Totlen_str[] PROGMEM =\"\\r\\nTotal length:\\t\\t\";\nconst char Conf_Nint_str[] PROGMEM =\"\\r\\nNum.intf:\\t\\t\";\nconst char Conf_Value_str[] PROGMEM =\"\\r\\nConf.value:\\t\\t\";\nconst char Conf_String_str[] PROGMEM =\"\\r\\nConf.string:\\t\\t\";\nconst char Conf_Attr_str[] PROGMEM =\"\\r\\nAttr.:\\t\\t\\t\";\nconst char Conf_Pwr_str[] PROGMEM =\"\\r\\nMax.pwr:\\t\\t\";\nconst char Int_Header_str[] PROGMEM =\"\\r\\n\\r\\nInterface descriptor:\";\nconst char Int_Number_str[] PROGMEM =\"\\r\\nIntf.number:\\t\\t\";\nconst char Int_Alt_str[] PROGMEM =\"\\r\\nAlt.:\\t\\t\\t\";\nconst char Int_Endpoints_str[] PROGMEM =\"\\r\\nEndpoints:\\t\\t\";\nconst char Int_Class_str[] PROGMEM =\"\\r\\nIntf. Class:\\t\\t\";\nconst char Int_Subclass_str[] PROGMEM =\"\\r\\nIntf. Subclass:\\t\\t\";\nconst char Int_Protocol_str[] PROGMEM =\"\\r\\nIntf. Protocol:\\t\\t\";\nconst char Int_String_str[] PROGMEM =\"\\r\\nIntf.string:\\t\\t\";\nconst char End_Header_str[] PROGMEM =\"\\r\\n\\r\\nEndpoint descriptor:\";\nconst char End_Address_str[] PROGMEM =\"\\r\\nEndpoint address:\\t\";\nconst char End_Attr_str[] PROGMEM =\"\\r\\nAttr.:\\t\\t\\t\";\nconst char End_Pktsize_str[] PROGMEM =\"\\r\\nMax.pkt size:\\t\\t\";\nconst char End_Interval_str[] PROGMEM =\"\\r\\nPolling interval:\\t\";\nconst char Unk_Header_str[] PROGMEM = \"\\r\\nUnknown descriptor:\";\nconst char Unk_Length_str[] PROGMEM =\"\\r\\nLength:\\t\\t\";\nconst char Unk_Type_str[] PROGMEM =\"\\r\\nType:\\t\\t\";\nconst char Unk_Contents_str[] PROGMEM =\"\\r\\nContents:\\t\";\n \n#endif // __PGMSTRINGS_H__"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/hub_demo/hub_demo.ino",
    "content": "#include <usbhub.h>\n#include \"pgmstrings.h\"\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB     Usb;\nUSBHub  Hub1(&Usb);\nUSBHub  Hub2(&Usb);\nUSBHub  Hub3(&Usb);\nUSBHub  Hub4(&Usb);\n\nuint32_t next_time;\n\nvoid PrintAllAddresses(UsbDevice *pdev)\n{\n  UsbDeviceAddress adr;\n  adr.devAddress = pdev->address.devAddress;\n  Serial.print(\"\\r\\nAddr:\");\n  Serial.print(adr.devAddress, HEX);\n  Serial.print(\"(\");\n  Serial.print(adr.bmHub, HEX);\n  Serial.print(\".\");\n  Serial.print(adr.bmParent, HEX);\n  Serial.print(\".\");\n  Serial.print(adr.bmAddress, HEX);\n  Serial.println(\")\");\n}\n\nvoid PrintAddress(uint8_t addr)\n{\n  UsbDeviceAddress adr;\n  adr.devAddress = addr;\n  Serial.print(\"\\r\\nADDR:\\t\");\n  Serial.println(adr.devAddress, HEX);\n  Serial.print(\"DEV:\\t\");\n  Serial.println(adr.bmAddress, HEX);\n  Serial.print(\"PRNT:\\t\");\n  Serial.println(adr.bmParent, HEX);\n  Serial.print(\"HUB:\\t\");\n  Serial.println(adr.bmHub, HEX);\n}\n\nvoid setup()\n{\n  Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"Start\");\n\n  if (Usb.Init() == -1)\n    Serial.println(\"OSC did not start.\");\n\n  delay( 200 );\n\n  next_time = millis() + 10000;\n}\n\nbyte getdevdescr( byte addr, byte &num_conf );\n\nvoid PrintDescriptors(uint8_t addr)\n{\n  uint8_t rcode = 0;\n  byte num_conf = 0;\n\n  rcode = getdevdescr( (byte)addr, num_conf );\n  if ( rcode )\n  {\n    printProgStr(Gen_Error_str);\n    print_hex( rcode, 8 );\n  }\n  Serial.print(\"\\r\\n\");\n\n  for (int i = 0; i < num_conf; i++)\n  {\n    rcode = getconfdescr( addr, i );                 // get configuration descriptor\n    if ( rcode )\n    {\n      printProgStr(Gen_Error_str);\n      print_hex(rcode, 8);\n    }\n    Serial.println(\"\\r\\n\");\n  }\n}\n\nvoid PrintAllDescriptors(UsbDevice *pdev)\n{\n  Serial.println(\"\\r\\n\");\n  print_hex(pdev->address.devAddress, 8);\n  Serial.println(\"\\r\\n--\");\n  PrintDescriptors( pdev->address.devAddress );\n}\n\nvoid loop()\n{\n  Usb.Task();\n\n  if ( Usb.getUsbTaskState() == USB_STATE_RUNNING )\n  {\n    if ((millis() - next_time) >= 0L)\n    {\n      Usb.ForEachUsbDevice(&PrintAllDescriptors);\n      Usb.ForEachUsbDevice(&PrintAllAddresses);\n\n      while ( 1 );                          //stop\n    }\n  }\n}\n\nbyte getdevdescr( byte addr, byte &num_conf )\n{\n  USB_DEVICE_DESCRIPTOR buf;\n  byte rcode;\n  rcode = Usb.getDevDescr( addr, 0, 0x12, ( uint8_t *)&buf );\n  if ( rcode ) {\n    return ( rcode );\n  }\n  printProgStr(Dev_Header_str);\n  printProgStr(Dev_Length_str);\n  print_hex( buf.bLength, 8 );\n  printProgStr(Dev_Type_str);\n  print_hex( buf.bDescriptorType, 8 );\n  printProgStr(Dev_Version_str);\n  print_hex( buf.bcdUSB, 16 );\n  printProgStr(Dev_Class_str);\n  print_hex( buf.bDeviceClass, 8 );\n  printProgStr(Dev_Subclass_str);\n  print_hex( buf.bDeviceSubClass, 8 );\n  printProgStr(Dev_Protocol_str);\n  print_hex( buf.bDeviceProtocol, 8 );\n  printProgStr(Dev_Pktsize_str);\n  print_hex( buf.bMaxPacketSize0, 8 );\n  printProgStr(Dev_Vendor_str);\n  print_hex( buf.idVendor, 16 );\n  printProgStr(Dev_Product_str);\n  print_hex( buf.idProduct, 16 );\n  printProgStr(Dev_Revision_str);\n  print_hex( buf.bcdDevice, 16 );\n  printProgStr(Dev_Mfg_str);\n  print_hex( buf.iManufacturer, 8 );\n  printProgStr(Dev_Prod_str);\n  print_hex( buf.iProduct, 8 );\n  printProgStr(Dev_Serial_str);\n  print_hex( buf.iSerialNumber, 8 );\n  printProgStr(Dev_Nconf_str);\n  print_hex( buf.bNumConfigurations, 8 );\n  num_conf = buf.bNumConfigurations;\n  return ( 0 );\n}\n\nvoid printhubdescr(uint8_t *descrptr, uint8_t addr)\n{\n  HubDescriptor  *pHub = (HubDescriptor*) descrptr;\n  uint8_t        len = *((uint8_t*)descrptr);\n\n  printProgStr(PSTR(\"\\r\\n\\r\\nHub Descriptor:\\r\\n\"));\n  printProgStr(PSTR(\"bDescLength:\\t\\t\"));\n  Serial.println(pHub->bDescLength, HEX);\n\n  printProgStr(PSTR(\"bDescriptorType:\\t\"));\n  Serial.println(pHub->bDescriptorType, HEX);\n\n  printProgStr(PSTR(\"bNbrPorts:\\t\\t\"));\n  Serial.println(pHub->bNbrPorts, HEX);\n\n  printProgStr(PSTR(\"LogPwrSwitchMode:\\t\"));\n  Serial.println(pHub->LogPwrSwitchMode, BIN);\n\n  printProgStr(PSTR(\"CompoundDevice:\\t\\t\"));\n  Serial.println(pHub->CompoundDevice, BIN);\n\n  printProgStr(PSTR(\"OverCurrentProtectMode:\\t\"));\n  Serial.println(pHub->OverCurrentProtectMode, BIN);\n\n  printProgStr(PSTR(\"TTThinkTime:\\t\\t\"));\n  Serial.println(pHub->TTThinkTime, BIN);\n\n  printProgStr(PSTR(\"PortIndicatorsSupported:\"));\n  Serial.println(pHub->PortIndicatorsSupported, BIN);\n\n  printProgStr(PSTR(\"Reserved:\\t\\t\"));\n  Serial.println(pHub->Reserved, HEX);\n\n  printProgStr(PSTR(\"bPwrOn2PwrGood:\\t\\t\"));\n  Serial.println(pHub->bPwrOn2PwrGood, HEX);\n\n  printProgStr(PSTR(\"bHubContrCurrent:\\t\"));\n  Serial.println(pHub->bHubContrCurrent, HEX);\n\n  for (uint8_t i = 7; i < len; i++)\n    print_hex(descrptr[i], 8);\n\n  //for (uint8_t i=1; i<=pHub->bNbrPorts; i++)\n  //    PrintHubPortStatus(&Usb, addr, i, 1);\n}\n\nbyte getconfdescr( byte addr, byte conf )\n{\n  uint8_t buf[ BUFSIZE ];\n  uint8_t* buf_ptr = buf;\n  byte rcode;\n  byte descr_length;\n  byte descr_type;\n  unsigned int total_length;\n  rcode = Usb.getConfDescr( addr, 0, 4, conf, buf );  //get total length\n  LOBYTE( total_length ) = buf[ 2 ];\n  HIBYTE( total_length ) = buf[ 3 ];\n  if ( total_length > 256 ) {   //check if total length is larger than buffer\n    printProgStr(Conf_Trunc_str);\n    total_length = 256;\n  }\n  rcode = Usb.getConfDescr( addr, 0, total_length, conf, buf ); //get the whole descriptor\n  while ( buf_ptr < buf + total_length ) { //parsing descriptors\n    descr_length = *( buf_ptr );\n    descr_type = *( buf_ptr + 1 );\n    switch ( descr_type ) {\n      case ( USB_DESCRIPTOR_CONFIGURATION ):\n        printconfdescr( buf_ptr );\n        break;\n      case ( USB_DESCRIPTOR_INTERFACE ):\n        printintfdescr( buf_ptr );\n        break;\n      case ( USB_DESCRIPTOR_ENDPOINT ):\n        printepdescr( buf_ptr );\n        break;\n      case 0x29:\n        printhubdescr( buf_ptr, addr );\n        break;\n      default:\n        printunkdescr( buf_ptr );\n        break;\n    }//switch( descr_type\n    buf_ptr = ( buf_ptr + descr_length );    //advance buffer pointer\n  }//while( buf_ptr <=...\n  return ( rcode );\n}\n/* prints hex numbers with leading zeroes */\n// copyright, Peter H Anderson, Baltimore, MD, Nov, '07\n// source: http://www.phanderson.com/arduino/arduino_display.html\nvoid print_hex(int v, int num_places)\n{\n  int mask = 0, n, num_nibbles, digit;\n\n  for (n = 1; n <= num_places; n++) {\n    mask = (mask << 1) | 0x0001;\n  }\n  v = v & mask; // truncate v to specified number of places\n\n  num_nibbles = num_places / 4;\n  if ((num_places % 4) != 0) {\n    ++num_nibbles;\n  }\n  do {\n    digit = ((v >> (num_nibbles - 1) * 4)) & 0x0f;\n    Serial.print(digit, HEX);\n  }\n  while (--num_nibbles);\n}\n/* function to print configuration descriptor */\nvoid printconfdescr( uint8_t* descr_ptr )\n{\n  USB_CONFIGURATION_DESCRIPTOR* conf_ptr = ( USB_CONFIGURATION_DESCRIPTOR* )descr_ptr;\n  printProgStr(Conf_Header_str);\n  printProgStr(Conf_Totlen_str);\n  print_hex( conf_ptr->wTotalLength, 16 );\n  printProgStr(Conf_Nint_str);\n  print_hex( conf_ptr->bNumInterfaces, 8 );\n  printProgStr(Conf_Value_str);\n  print_hex( conf_ptr->bConfigurationValue, 8 );\n  printProgStr(Conf_String_str);\n  print_hex( conf_ptr->iConfiguration, 8 );\n  printProgStr(Conf_Attr_str);\n  print_hex( conf_ptr->bmAttributes, 8 );\n  printProgStr(Conf_Pwr_str);\n  print_hex( conf_ptr->bMaxPower, 8 );\n  return;\n}\n/* function to print interface descriptor */\nvoid printintfdescr( uint8_t* descr_ptr )\n{\n  USB_INTERFACE_DESCRIPTOR* intf_ptr = ( USB_INTERFACE_DESCRIPTOR* )descr_ptr;\n  printProgStr(Int_Header_str);\n  printProgStr(Int_Number_str);\n  print_hex( intf_ptr->bInterfaceNumber, 8 );\n  printProgStr(Int_Alt_str);\n  print_hex( intf_ptr->bAlternateSetting, 8 );\n  printProgStr(Int_Endpoints_str);\n  print_hex( intf_ptr->bNumEndpoints, 8 );\n  printProgStr(Int_Class_str);\n  print_hex( intf_ptr->bInterfaceClass, 8 );\n  printProgStr(Int_Subclass_str);\n  print_hex( intf_ptr->bInterfaceSubClass, 8 );\n  printProgStr(Int_Protocol_str);\n  print_hex( intf_ptr->bInterfaceProtocol, 8 );\n  printProgStr(Int_String_str);\n  print_hex( intf_ptr->iInterface, 8 );\n  return;\n}\n/* function to print endpoint descriptor */\nvoid printepdescr( uint8_t* descr_ptr )\n{\n  USB_ENDPOINT_DESCRIPTOR* ep_ptr = ( USB_ENDPOINT_DESCRIPTOR* )descr_ptr;\n  printProgStr(End_Header_str);\n  printProgStr(End_Address_str);\n  print_hex( ep_ptr->bEndpointAddress, 8 );\n  printProgStr(End_Attr_str);\n  print_hex( ep_ptr->bmAttributes, 8 );\n  printProgStr(End_Pktsize_str);\n  print_hex( ep_ptr->wMaxPacketSize, 16 );\n  printProgStr(End_Interval_str);\n  print_hex( ep_ptr->bInterval, 8 );\n\n  return;\n}\n/*function to print unknown descriptor */\nvoid printunkdescr( uint8_t* descr_ptr )\n{\n  byte length = *descr_ptr;\n  byte i;\n  printProgStr(Unk_Header_str);\n  printProgStr(Unk_Length_str);\n  print_hex( *descr_ptr, 8 );\n  printProgStr(Unk_Type_str);\n  print_hex( *(descr_ptr + 1 ), 8 );\n  printProgStr(Unk_Contents_str);\n  descr_ptr += 2;\n  for ( i = 0; i < length; i++ ) {\n    print_hex( *descr_ptr, 8 );\n    descr_ptr++;\n  }\n}\n\n\n/* Print a string from Program Memory directly to save RAM */\nvoid printProgStr(const char* str)\n{\n  char c;\n  if (!str) return;\n  while ((c = pgm_read_byte(str++)))\n    Serial.print(c);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/hub_demo/pgmstrings.h",
    "content": "#if !defined(__PGMSTRINGS_H__)\n#define __PGMSTRINGS_H__\n\n#define LOBYTE(x) ((char*)(&(x)))[0]\n#define HIBYTE(x) ((char*)(&(x)))[1]\n#define BUFSIZE 256    //buffer size\n \n\n/* Print strings in Program Memory */\nconst char Gen_Error_str[] PROGMEM = \"\\r\\nRequest error. Error code:\\t\"; \nconst char Dev_Header_str[] PROGMEM =\"\\r\\nDevice descriptor: \";\nconst char Dev_Length_str[] PROGMEM =\"\\r\\nDescriptor Length:\\t\";\nconst char Dev_Type_str[] PROGMEM =\"\\r\\nDescriptor type:\\t\";\nconst char Dev_Version_str[] PROGMEM =\"\\r\\nUSB version:\\t\\t\";\nconst char Dev_Class_str[] PROGMEM =\"\\r\\nDevice class:\\t\\t\";\nconst char Dev_Subclass_str[] PROGMEM =\"\\r\\nDevice Subclass:\\t\";\nconst char Dev_Protocol_str[] PROGMEM =\"\\r\\nDevice Protocol:\\t\";\nconst char Dev_Pktsize_str[] PROGMEM =\"\\r\\nMax.packet size:\\t\";\nconst char Dev_Vendor_str[] PROGMEM =\"\\r\\nVendor  ID:\\t\\t\";\nconst char Dev_Product_str[] PROGMEM =\"\\r\\nProduct ID:\\t\\t\";\nconst char Dev_Revision_str[] PROGMEM =\"\\r\\nRevision ID:\\t\\t\";\nconst char Dev_Mfg_str[] PROGMEM =\"\\r\\nMfg.string index:\\t\";\nconst char Dev_Prod_str[] PROGMEM =\"\\r\\nProd.string index:\\t\";\nconst char Dev_Serial_str[] PROGMEM =\"\\r\\nSerial number index:\\t\";\nconst char Dev_Nconf_str[] PROGMEM =\"\\r\\nNumber of conf.:\\t\";\nconst char Conf_Trunc_str[] PROGMEM =\"Total length truncated to 256 bytes\";\nconst char Conf_Header_str[] PROGMEM =\"\\r\\nConfiguration descriptor:\";\nconst char Conf_Totlen_str[] PROGMEM =\"\\r\\nTotal length:\\t\\t\";\nconst char Conf_Nint_str[] PROGMEM =\"\\r\\nNum.intf:\\t\\t\";\nconst char Conf_Value_str[] PROGMEM =\"\\r\\nConf.value:\\t\\t\";\nconst char Conf_String_str[] PROGMEM =\"\\r\\nConf.string:\\t\\t\";\nconst char Conf_Attr_str[] PROGMEM =\"\\r\\nAttr.:\\t\\t\\t\";\nconst char Conf_Pwr_str[] PROGMEM =\"\\r\\nMax.pwr:\\t\\t\";\nconst char Int_Header_str[] PROGMEM =\"\\r\\n\\r\\nInterface descriptor:\";\nconst char Int_Number_str[] PROGMEM =\"\\r\\nIntf.number:\\t\\t\";\nconst char Int_Alt_str[] PROGMEM =\"\\r\\nAlt.:\\t\\t\\t\";\nconst char Int_Endpoints_str[] PROGMEM =\"\\r\\nEndpoints:\\t\\t\";\nconst char Int_Class_str[] PROGMEM =\"\\r\\nIntf. Class:\\t\\t\";\nconst char Int_Subclass_str[] PROGMEM =\"\\r\\nIntf. Subclass:\\t\\t\";\nconst char Int_Protocol_str[] PROGMEM =\"\\r\\nIntf. Protocol:\\t\\t\";\nconst char Int_String_str[] PROGMEM =\"\\r\\nIntf.string:\\t\\t\";\nconst char End_Header_str[] PROGMEM =\"\\r\\n\\r\\nEndpoint descriptor:\";\nconst char End_Address_str[] PROGMEM =\"\\r\\nEndpoint address:\\t\";\nconst char End_Attr_str[] PROGMEM =\"\\r\\nAttr.:\\t\\t\\t\";\nconst char End_Pktsize_str[] PROGMEM =\"\\r\\nMax.pkt size:\\t\\t\";\nconst char End_Interval_str[] PROGMEM =\"\\r\\nPolling interval:\\t\";\nconst char Unk_Header_str[] PROGMEM = \"\\r\\nUnknown descriptor:\";\nconst char Unk_Length_str[] PROGMEM =\"\\r\\nLength:\\t\\t\";\nconst char Unk_Type_str[] PROGMEM =\"\\r\\nType:\\t\\t\";\nconst char Unk_Contents_str[] PROGMEM =\"\\r\\nContents:\\t\";\n \n#endif // __PGMSTRINGS_H__"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/max_LCD/max_LCD.ino",
    "content": "// Just a copy of the HelloWorld example bundled with the LiquidCrystal library in the Arduino IDE\n\n// HD44780 compatible LCD display via MAX3421E GPOUT support header\n// pinout: D[4-7] -> GPOUT[4-7], RS-> GPOUT[2], E ->GPOUT[3]\n\n#include <max_LCD.h>\n\n// Satisfy IDE, which only needs to see the include statment in the ino.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nUSB Usb;\nMax_LCD lcd(&Usb);\n\nvoid setup() {\n  // Set up the LCD's number of columns and rows:\n  lcd.begin(16, 2);\n  // Print a message to the LCD.\n  lcd.print(\"Hello, World!\");\n}\n\nvoid loop() {\n  // Set the cursor to column 0, line 1 (note: line 1 is the second row, since counting begins with 0):\n  lcd.setCursor(0, 1);\n  // Print the number of seconds since reset:\n  lcd.print(millis() / 1000);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/pl2303/pl2303_gprs_terminal/pl2303_gprs_terminal.ino",
    "content": "/* Arduino terminal for PL2303 USB to serial converter and DealeXtreme GPRS modem. */\n/* USB support */\n#include <usbhub.h>\n/* CDC support */\n#include <cdcacm.h>\n#include <cdcprolific.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nclass PLAsyncOper : public CDCAsyncOper\n{\npublic:\n    uint8_t OnInit(ACM *pacm);\n};\n\nuint8_t PLAsyncOper::OnInit(ACM *pacm)\n{\n    uint8_t rcode;\n\n    // Set DTR = 1\n    rcode = pacm->SetControlLineState(1);\n\n    if (rcode)\n    {\n        ErrorMessage<uint8_t>(PSTR(\"SetControlLineState\"), rcode);\n        return rcode;\n    }\n\n    LINE_CODING\tlc;\n    //lc.dwDTERate\t= 9600;\n    lc.dwDTERate = 115200;\n    lc.bCharFormat\t= 0;\n    lc.bParityType\t= 0;\n    lc.bDataBits\t= 8;\n\n    rcode = pacm->SetLineCoding(&lc);\n\n    if (rcode)\n        ErrorMessage<uint8_t>(PSTR(\"SetLineCoding\"), rcode);\n\n    return rcode;\n}\nUSB     Usb;\n//USBHub     Hub(&Usb);\nPLAsyncOper  AsyncOper;\nPL2303       Pl(&Usb, &AsyncOper);\n\nvoid setup()\n{\n  Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"Start\");\n\n  if (Usb.Init() == -1)\n      Serial.println(\"OSCOKIRQ failed to assert\");\n\n  delay( 200 );\n}\n\nvoid loop()\n{\n    Usb.Task();\n\n    if( Usb.getUsbTaskState() == USB_STATE_RUNNING )\n    {\n       uint8_t rcode;\n\n       /* reading the keyboard */\n       if(Serial.available()) {\n         uint8_t data= Serial.read();\n\n         /* sending to the phone */\n         rcode = Pl.SndData(1, &data);\n         if (rcode)\n            ErrorMessage<uint8_t>(PSTR(\"SndData\"), rcode);\n       }//if(Serial.available()...\n\n        /* reading the converter */\n        /* buffer size must be greater or equal to max.packet size */\n        /* it it set to 64 (largest possible max.packet size) here, can be tuned down\n        for particular endpoint */\n        uint8_t  buf[64];\n        uint16_t rcvd = 64;\n        rcode = Pl.RcvData(&rcvd, buf);\n         if (rcode && rcode != hrNAK)\n           ErrorMessage<uint8_t>(PSTR(\"Ret\"), rcode);\n\n           if( rcvd ) { //more than zero bytes received\n             for(uint16_t i=0; i < rcvd; i++ ) {\n               Serial.print((char)buf[i]); //printing on the screen\n             }\n           }//if( rcvd ...\n    }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING..\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/pl2303/pl2303_gps/pl2303_gps.ino",
    "content": "/* USB Host to PL2303-based USB GPS unit interface */\n/* Navibee GM720 receiver - Sirf Star III */\n/* USB support */\n#include <usbhub.h>\n/* CDC support */\n#include <cdcacm.h>\n#include <cdcprolific.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nclass PLAsyncOper : public CDCAsyncOper {\npublic:\n        uint8_t OnInit(ACM *pacm);\n};\n\nuint8_t PLAsyncOper::OnInit(ACM *pacm) {\n        uint8_t rcode;\n\n        // Set DTR = 1\n        rcode = pacm->SetControlLineState(1);\n\n        if(rcode) {\n                ErrorMessage<uint8_t>(PSTR(\"SetControlLineState\"), rcode);\n                return rcode;\n        }\n\n        LINE_CODING lc;\n        lc.dwDTERate = 4800; //default serial speed of GPS unit\n        lc.bCharFormat = 0;\n        lc.bParityType = 0;\n        lc.bDataBits = 8;\n\n        rcode = pacm->SetLineCoding(&lc);\n\n        if(rcode)\n                ErrorMessage<uint8_t>(PSTR(\"SetLineCoding\"), rcode);\n\n        return rcode;\n}\n\nUSB Usb;\nUSBHub Hub(&Usb);\nPLAsyncOper AsyncOper;\nPL2303 Pl(&Usb, &AsyncOper);\nuint32_t read_delay;\n#define READ_DELAY 100\n\nvoid setup() {\n        Serial.begin(115200);\n#if !defined(__MIPSEL__)\n        while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n        Serial.println(\"Start\");\n\n        if(Usb.Init() == -1)\n                Serial.println(\"OSCOKIRQ failed to assert\");\n\n        delay(200);\n}\n\nvoid loop() {\n        uint8_t rcode;\n        uint8_t buf[64]; //serial buffer equals Max.packet size of bulk-IN endpoint\n        uint16_t rcvd = 64;\n\n        Usb.Task();\n\n        if(Pl.isReady()) {\n                /* reading the GPS */\n                if((long)(millis() - read_delay) >= 0L) {\n                        read_delay += READ_DELAY;\n                        rcode = Pl.RcvData(&rcvd, buf);\n                        if(rcode && rcode != hrNAK)\n                                ErrorMessage<uint8_t>(PSTR(\"Ret\"), rcode);\n                        if(rcvd) { //more than zero bytes received\n                                for(uint16_t i = 0; i < rcvd; i++) {\n                                        Serial.print((char)buf[i]); //printing on the screen\n                                }//for( uint16_t i=0; i < rcvd; i++...\n                        }//if( rcvd\n                }//if( read_delay > millis()...\n        }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING..\n}\n\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/pl2303/pl2303_tinygps/pl2303_tinygps.ino",
    "content": "/* USB Host to PL2303-based USB GPS unit interface */\n/* Navibee GM720 receiver - Sirf Star III */\n/* Mikal Hart's TinyGPS library */\n/* test_with_gps_device library example modified for PL2302 access */\n\n/* USB support */\n#include <usbhub.h>\n\n/* CDC support */\n#include <cdcacm.h>\n#include <cdcprolific.h>\n\n#include <TinyGPS.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\n/* This sample code demonstrates the normal use of a TinyGPS object.\n    Modified to be used with USB Host Shield Library r2.0\n    and USB Host Shield 2.0\n*/\n\nclass PLAsyncOper : public CDCAsyncOper\n{\npublic:\n    uint8_t OnInit(ACM *pacm);\n};\n\nuint8_t PLAsyncOper::OnInit(ACM *pacm)\n{\n    uint8_t rcode;\n\n    // Set DTR = 1\n    rcode = pacm->SetControlLineState(1);\n\n    if (rcode) {\n        ErrorMessage<uint8_t>(PSTR(\"SetControlLineState\"), rcode);\n        return rcode;\n    }\n\n    LINE_CODING lc;\n    lc.dwDTERate  = 4800;   //default serial speed of GPS unit\n    lc.bCharFormat  = 0;\n    lc.bParityType  = 0;\n    lc.bDataBits  = 8;\n\n    rcode = pacm->SetLineCoding(&lc);\n\n    if (rcode) {\n        ErrorMessage<uint8_t>(PSTR(\"SetLineCoding\"), rcode);\n    }\n\n    return rcode;\n}\n\nUSB     Usb;\n//USBHub     Hub(&Usb);\nPLAsyncOper  AsyncOper;\nPL2303       Pl(&Usb, &AsyncOper);\nTinyGPS gps;\n\nvoid gpsdump(TinyGPS &gps);\nbool feedgps();\nvoid printFloat(double f, int digits = 2);\n\nvoid setup()\n{\n\n  Serial.begin(115200);\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n\n  Serial.print(\"Testing TinyGPS library v. \"); Serial.println(TinyGPS::library_version());\n  Serial.println(\"by Mikal Hart\");\n  Serial.println();\n  Serial.print(\"Sizeof(gpsobject) = \"); Serial.println(sizeof(TinyGPS));\n  Serial.println();\n  /* USB Initialization */\n  if (Usb.Init() == -1) {\n      Serial.println(\"OSCOKIRQ failed to assert\");\n  }\n\n  delay( 200 );\n}\n\nvoid loop()\n{\n  Usb.Task();\n\n  if( Pl.isReady()) {\n\n    bool newdata = false;\n    unsigned long start = millis();\n\n    // Every 5 seconds we print an update\n    while (millis() - start < 5000) {\n      if( feedgps()) {\n        newdata = true;\n      }\n    }//while (millis()...\n\n    if (newdata) {\n      Serial.println(\"Acquired Data\");\n      Serial.println(\"-------------\");\n      gpsdump(gps);\n      Serial.println(\"-------------\");\n      Serial.println();\n    }//if( newdata...\n  }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING...\n}\n\nvoid printFloat(double number, int digits)\n{\n  // Handle negative numbers\n  if (number < 0.0)\n  {\n     Serial.print('-');\n     number = -number;\n  }\n\n  // Round correctly so that print(1.999, 2) prints as \"2.00\"\n  double rounding = 0.5;\n  for (uint8_t i=0; i<digits; ++i)\n    rounding /= 10.0;\n\n  number += rounding;\n\n  // Extract the integer part of the number and print it\n  unsigned long int_part = (unsigned long)number;\n  double remainder = number - (double)int_part;\n  Serial.print(int_part);\n\n  // Print the decimal point, but only if there are digits beyond\n  if (digits > 0)\n    Serial.print(\".\");\n\n  // Extract digits from the remainder one at a time\n  while (digits-- > 0)\n  {\n    remainder *= 10.0;\n    int toPrint = int(remainder);\n    Serial.print(toPrint);\n    remainder -= toPrint;\n  }\n}\n\nvoid gpsdump(TinyGPS &gps)\n{\n  long lat, lon;\n  float flat, flon;\n  unsigned long age, date, time, chars;\n  int year;\n  byte month, day, hour, minute, second, hundredths;\n  unsigned short sentences, failed;\n\n  gps.get_position(&lat, &lon, &age);\n  Serial.print(\"Lat/Long(10^-5 deg): \"); Serial.print(lat); Serial.print(\", \"); Serial.print(lon);\n  Serial.print(\" Fix age: \"); Serial.print(age); Serial.println(\"ms.\");\n\n  feedgps(); // If we don't feed the gps during this long routine, we may drop characters and get checksum errors\n\n  gps.f_get_position(&flat, &flon, &age);\n  Serial.print(\"Lat/Long(float): \"); printFloat(flat, 5); Serial.print(\", \"); printFloat(flon, 5);\n  Serial.print(\" Fix age: \"); Serial.print(age); Serial.println(\"ms.\");\n\n  feedgps();\n\n  gps.get_datetime(&date, &time, &age);\n  Serial.print(\"Date(ddmmyy): \"); Serial.print(date); Serial.print(\" Time(hhmmsscc): \"); Serial.print(time);\n  Serial.print(\" Fix age: \"); Serial.print(age); Serial.println(\"ms.\");\n\n  feedgps();\n\n  gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hundredths, &age);\n  Serial.print(\"Date: \"); Serial.print(static_cast<int>(month)); Serial.print(\"/\"); Serial.print(static_cast<int>(day)); Serial.print(\"/\"); Serial.print(year);\n  Serial.print(\"  Time: \"); Serial.print(static_cast<int>(hour)); Serial.print(\":\"); Serial.print(static_cast<int>(minute)); Serial.print(\":\"); Serial.print(static_cast<int>(second)); Serial.print(\".\"); Serial.print(static_cast<int>(hundredths));\n  Serial.print(\"  Fix age: \");  Serial.print(age); Serial.println(\"ms.\");\n\n  feedgps();\n\n  Serial.print(\"Alt(cm): \"); Serial.print(gps.altitude()); Serial.print(\" Course(10^-2 deg): \"); Serial.print(gps.course()); Serial.print(\" Speed(10^-2 knots): \"); Serial.println(gps.speed());\n  Serial.print(\"Alt(float): \"); printFloat(gps.f_altitude()); Serial.print(\" Course(float): \"); printFloat(gps.f_course()); Serial.println();\n  Serial.print(\"Speed(knots): \"); printFloat(gps.f_speed_knots()); Serial.print(\" (mph): \");  printFloat(gps.f_speed_mph());\n  Serial.print(\" (mps): \"); printFloat(gps.f_speed_mps()); Serial.print(\" (kmph): \"); printFloat(gps.f_speed_kmph()); Serial.println();\n\n  feedgps();\n\n  gps.stats(&chars, &sentences, &failed);\n  Serial.print(\"Stats: characters: \"); Serial.print(chars); Serial.print(\" sentences: \"); Serial.print(sentences); Serial.print(\" failed checksum: \"); Serial.println(failed);\n}\n\nbool feedgps()\n{\n  uint8_t rcode;\n  uint8_t  buf[64];    //serial buffer equals Max.packet size of bulk-IN endpoint\n  uint16_t rcvd = 64;\n    {\n        /* reading the GPS */\n        rcode = Pl.RcvData(&rcvd, buf);\n         if (rcode && rcode != hrNAK)\n            ErrorMessage<uint8_t>(PSTR(\"Ret\"), rcode);\n            rcode = false;\n            if( rcvd ) { //more than zero bytes received\n              for( uint16_t i=0; i < rcvd; i++ ) {\n                if( gps.encode((char)buf[i])) { //feed a character to gps object\n                  rcode = true;\n                }//if( gps.encode(buf[i]...\n              }//for( uint16_t i=0; i < rcvd; i++...\n            }//if( rcvd...\n    }\n  return( rcode );\n}\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/pl2303/pl2303_xbee_terminal/pl2303_xbee_terminal.ino",
    "content": "/* Arduino terminal for PL2303 USB to serial converter and XBee radio. */\n/* Inserts linefeed after carriage return in data sent to and received from Xbee */\n/* USB support */\n#include <usbhub.h>\n/* CDC support */\n#include <cdcacm.h>\n#include <cdcprolific.h>\n\n// Satisfy the IDE, which needs to see the include statment in the ino too.\n#ifdef dobogusinclude\n#include <spi4teensy3.h>\n#include <SPI.h>\n#endif\n\nclass PLAsyncOper : public CDCAsyncOper\n{\npublic:\n    uint8_t OnInit(ACM *pacm);\n};\n\nuint8_t PLAsyncOper::OnInit(ACM *pacm)\n{\n    uint8_t rcode;\n\n    // Set DTR = 1\n    rcode = pacm->SetControlLineState(1);\n\n    if (rcode)\n    {\n        ErrorMessage<uint8_t>(PSTR(\"SetControlLineState\"), rcode);\n        return rcode;\n    }\n\n    LINE_CODING\tlc;\n    lc.dwDTERate\t= 115200;\n    lc.bCharFormat\t= 0;\n    lc.bParityType\t= 0;\n    lc.bDataBits\t= 8;\n\n    rcode = pacm->SetLineCoding(&lc);\n\n    if (rcode)\n        ErrorMessage<uint8_t>(PSTR(\"SetLineCoding\"), rcode);\n\n    return rcode;\n}\nUSB     Usb;\n//USBHub     Hub(&Usb);\nPLAsyncOper  AsyncOper;\nPL2303       Pl(&Usb, &AsyncOper);\n\nvoid setup()\n{\n  Serial.begin( 115200 );\n#if !defined(__MIPSEL__)\n  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection\n#endif\n  Serial.println(\"Start\");\n\n  if (Usb.Init() == -1)\n      Serial.println(\"OSCOKIRQ failed to assert\");\n\n  delay( 200 );\n}\n\nvoid loop()\n{\n    Usb.Task();\n\n    if( Usb.getUsbTaskState() == USB_STATE_RUNNING )\n    {\n       uint8_t rcode;\n\n       /* reading the keyboard */\n       if(Serial.available()) {\n         uint8_t data= Serial.read();\n\n         if ( data == '\\r' ) {\n           Serial.print(\"\\r\\n\");  //insert linefeed\n         }\n         else {\n           Serial.print( data );  //echo back to the screen\n         }\n\n         /* sending to the phone */\n         rcode = Pl.SndData(1, &data);\n         if (rcode)\n            ErrorMessage<uint8_t>(PSTR(\"SndData\"), rcode);\n       }//if(Serial.available()...\n\n       delay(50);\n\n        /* reading the converter */\n        /* buffer size must be greater or equal to max.packet size */\n        /* it it set to 64 (largest possible max.packet size) here, can be tuned down\n        for particular endpoint */\n        uint8_t  buf[64];\n        uint16_t rcvd = 64;\n        rcode = Pl.RcvData(&rcvd, buf);\n         if (rcode && rcode != hrNAK)\n            ErrorMessage<uint8_t>(PSTR(\"Ret\"), rcode);\n\n            if( rcvd ) { //more than zero bytes received\n              for(uint16_t i=0; i < rcvd; i++ ) {\n                if( buf[i] =='\\r' ) {\n                  Serial.print(\"\\r\\n\");  //insert linefeed\n                }\n                else {\n                  Serial.print((char)buf[i]); //printing on the screen\n                }\n              }\n            }\n        delay(10);\n    }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING..\n}\n\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/testusbhostFAT/Makefile",
    "content": "#\n# These are set for a mega 1280 + quadram plus my serial patch.\n# If you lack quadram, or want to disable LFN, just change _FS_TINY=1 _USE_LFN=0\n#\n# If your board is a mega 2560 comment out the following two lines\nBOARD = mega\n\nBOARD_SUB = mega.menu.cpu.atmega1280\nPROGRAMMER = arduino\n\n# ...and then uncomment out the following two lines\n#BOARD_SUB = mega.menu.cpu.atmega2560\n#PROGRAMMER = wiring\n\n#BOARD = teensypp2\n#BOARD = teensy3\n#BOARD = teensy31\n\n# set your Arduino tty port here\nPORT = /dev/ttyUSB0\n\nEXTRA_FLAGS = -D _USE_LFN=3\n\n# change to 0 if you have quadram to take advantage of caching FAT\nEXTRA_FLAGS += -D _FS_TINY=1\n\n\nEXTRA_FLAGS += -D _MAX_SS=512\n\n\n# Don't worry if you don't have external RAM, xmem2 detects this situation.\n# You *WILL* be wanting to get some kind of external ram on your mega in order to\n# do anything that is intense.\nEXTRA_FLAGS += -D EXT_RAM_STACK=1\nEXTRA_FLAGS += -D EXT_RAM_HEAP=1\n\n\n# These are no longer needed for the demo to work.\n# In the event you need more ram, uncomment these 3 lines.\n#EXTRA_FLAGS += -D DISABLE_SERIAL1\n#EXTRA_FLAGS += -D DISABLE_SERIAL2\n#EXTRA_FLAGS += -D DISABLE_SERIAL3\n\n#\n# Advanced debug on Serial3\n#\n\n# uncomment the next two to enable debug on Serial3\nEXTRA_FLAGS += -D USB_HOST_SERIAL=Serial3\n#EXTRA_FLAGS += -D DEBUG_USB_HOST\n\n# The following are the libraries used.\nLIB_DIRS += ../../\nLIB_DIRS += ../testusbhostFAT/xmem2\nLIB_DIRS += ../testusbhostFAT/generic_storage\nLIB_DIRS += ../testusbhostFAT/RTClib\nLIB_DIRS += $(ARD_HOME)/libraries/Wire\nLIB_DIRS += $(ARD_HOME)/libraries/Wire/utility\nLIB_DIRS += $(ARD_HOME)/hardware/arduino/$(BUILD_ARCH)/libraries/Wire\nLIB_DIRS += $(ARD_HOME)/hardware/arduino/$(BUILD_ARCH)/libraries/Wire/utility\nLIB_DIRS += $(ARD_HOME)/hardware/arduino/$(BUILD_ARCH)/libraries/SPI\n\n# And finally, the part that brings everything together for you.\ninclude Arduino_Makefile_master/_Makefile.master\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/testusbhostFAT/README.md",
    "content": "This small sketch tests the USB host shield mass storage library.\n\n__Note:__ This will not run a Arduino Uno due to the limited ram available in the ATmega328p.\n\nThe Arduino Mega (ATmega1280) and the Arduino Mega 2560 (ATmega2560) are confirmed to work with this test code.\n\nTo compile this example you will need the following libraries as well:\n\n* [xmem2](https://github.com/xxxajk/xmem2)\n* [generic_storage FATfs](https://github.com/xxxajk/generic_storage)\n* [RTClib](https://github.com/xxxajk/RTClib)\n\nThe following shield is recommended for larger projects: <https://www.rugged-circuits.com/new-products/quadram>.\n\nYou may use the bundled [Makefile](Makefile) to compile the code instead of the Arduino IDE if you have problems or want a smaller binary. The master makefile is bundled as a submodule, but can also be downloaded manually at the following link: <https://github.com/xxxajk/Arduino_Makefile_master>.\n\nTo download the USB Host library and all the needed libraries for this test.\n\nRun the following command in a terminal application:\n\n```\ngit clone --recursive https://github.com/felis/USB_Host_Shield_2.0\n```\n\nIf you want to update all the submodules run:\n\n```\ngit submodule foreach --recursive git pull origin master\n```\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/examples/testusbhostFAT/testusbhostFAT.ino",
    "content": "/*\n * Mega + USB storage + optional DS1307 + optional expansion RAM + funky status LED,\n * Includes interactive debug level setting, and supports hot-plug.\n *\n * IMPORTANT! PLEASE USE Arduino 1.0.5 or better!\n * Older versions HAVE MAJOR BUGS AND WILL NOT WORK AT ALL!\n * Use of gcc-avr and lib-c that is newer than the Arduino version is even better.\n * If you experience random crashes, use make.\n * The options that the IDE use can generate bad code and cause the AVR to crash.\n *\n * This sketch requires the following libraries:\n * https://github.com/felis/USB_Host_Shield_2.0 Install as 'USB_Host_Shield_2_0'\n * https://github.com/xxxajk/xmem2 Install as 'xmem', provides memory services.\n * https://github.com/xxxajk/generic_storage provides access to FAT file system.\n * https://github.com/xxxajk/RTClib provides access to DS1307, or fake clock.\n *\n * Optional, to use the Makefile (Recommended! See above!):\n * https://github.com/xxxajk/Arduino_Makefile_master\n *\n */\n\n/////////////////////////////////////////////////////////////\n// Please Note:                                            //\n// This section is for info with the Arduino IDE ONLY.     //\n// Unfortunately due to short sightedness of the Arduino   //\n// code team, that you must set the following in the       //\n// respective libraries.                                   //\n// Changing them here will have _NO_ effect!               //\n/////////////////////////////////////////////////////////////\n\n// Uncomment to enable debugging\n//#define DEBUG_USB_HOST\n// This is where stderr/USB debugging goes to\n//#define USB_HOST_SERIAL Serial3\n\n// If you have external memory, setting this to 0 enables FAT table caches.\n// The 0 setting is recommended only if you have external memory.\n//#define _FS_TINY 1\n\n//#define _USE_LFN 3\n//#define _MAX_SS 512\n\n\n/////////////////////////////////////////////////////////////\n// End of Arduino IDE specific information                 //\n/////////////////////////////////////////////////////////////\n\n// You can set this to 0 if you are not using a USB hub.\n// It will save a little bit of flash and RAM.\n// Set to 1 if you want to use a hub.\n#define WANT_HUB_TEST 1\n\n// this is for XMEM2\n#define EXT_RAM_STACK 1\n#define EXT_RAM_HEAP 1\n#define LOAD_XMEM\n\n#if defined(CORE_TEENSY) && !defined(_AVR_)\n#include <xmem.h>\n#include <spi4teensy3.h>\n#endif\n\n#if defined(__AVR__)\n#include <xmem.h>\n#include <SPI.h>\n#elif defined(ARDUINO_ARCH_SAM)\n#include <SPI.h>\n#endif\n\n#if WANT_HUB_TEST\n#include <usbhub.h>\n#endif\n#include <Wire.h>\n#define LOAD_RTCLIB\n#include <RTClib.h>\n#include <masstorage.h>\n#include <Storage.h>\n#include <PCpartition/PCPartition.h>\n#include <avr/interrupt.h>\n#include <FAT/FAT.h>\n#include <stdio.h>\n#if defined(__AVR__)\nstatic FILE tty_stdio;\nstatic FILE tty_stderr;\nvolatile uint32_t LEDnext_time; // fade timeout\nvolatile uint32_t HEAPnext_time; // when to print out next heap report\nvolatile int brightness = 0; // how bright the LED is\nvolatile int fadeAmount = 80; // how many points to fade the LED by\n#endif\n\nUSB Usb;\n\nvolatile uint8_t current_state = 1;\nvolatile uint8_t last_state = 0;\nvolatile bool fatready = false;\nvolatile bool partsready = false;\nvolatile bool notified = false;\nvolatile bool runtest = false;\nvolatile bool usbon = false;\nvolatile uint32_t usbon_time;\nvolatile bool change = false;\nvolatile bool reportlvl = false;\nint cpart = 0;\nPCPartition *PT;\n\n#if WANT_HUB_TEST\n#define MAX_HUBS 1\nUSBHub *Hubs[MAX_HUBS];\n#endif\n\nstatic PFAT *Fats[_VOLUMES];\nstatic part_t parts[_VOLUMES];\nstatic storage_t sto[_VOLUMES];\n\n/*make sure this is a power of two. */\n#define mbxs 128\nstatic uint8_t My_Buff_x[mbxs]; /* File read buffer */\n\n#if defined(__AVR__)\n\n#define prescale1       ((1 << WGM12) | (1 << CS10))\n#define prescale8       ((1 << WGM12) | (1 << CS11))\n#define prescale64      ((1 << WGM12) | (1 << CS10) | (1 << CS11))\n#define prescale256     ((1 << WGM12) | (1 << CS12))\n#define prescale1024    ((1 << WGM12) | (1 << CS12) | (1 << CS10))\n\nextern \"C\" {\n         extern unsigned int freeHeap();\n}\nstatic int tty_stderr_putc(char c, FILE *t) {\n        USB_HOST_SERIAL.write(c);\n        return 0;\n}\n\nstatic int __attribute__((unused)) tty_stderr_flush(FILE *t) {\n        USB_HOST_SERIAL.flush();\n        return 0;\n}\n\nstatic int tty_std_putc(char c, FILE *t) {\n        Serial.write(c);\n        return 0;\n}\n\nstatic int tty_std_getc(FILE *t) {\n        while(!Serial.available());\n        return Serial.read();\n}\n\nstatic int __attribute__((unused)) tty_std_flush(FILE *t) {\n        Serial.flush();\n        return 0;\n}\n\n#else\n// Supposedly the DUE has stdio already pointing to serial...\n#if !defined(ARDUINO_ARCH_SAM)\n// But newlib needs this...\nextern \"C\" {\n        int _write(int fd, const char *ptr, int len) {\n                int j;\n                for(j = 0; j < len; j++) {\n                        if(fd == 1)\n                                Serial.write(*ptr++);\n                        else if(fd == 2)\n                                USB_HOST_SERIAL.write(*ptr++);\n                }\n                return len;\n        }\n\n        int _read(int fd, char *ptr, int len) {\n                if(len > 0 && fd == 0) {\n                        while(!Serial.available());\n                        *ptr = Serial.read();\n                        return 1;\n                }\n                return 0;\n        }\n\n#include <sys/stat.h>\n\n        int _fstat(int fd, struct stat *st) {\n                memset(st, 0, sizeof (*st));\n                st->st_mode = S_IFCHR;\n                st->st_blksize = 1024;\n                return 0;\n        }\n\n        int _isatty(int fd) {\n                return (fd < 3) ? 1 : 0;\n        }\n}\n#endif // !defined(ARDUINO_ARCH_SAM)\n#endif\n\nvoid setup() {\n        bool serr = false;\n        for(int i = 0; i < _VOLUMES; i++) {\n                Fats[i] = NULL;\n                sto[i].private_data = new pvt_t;\n                ((pvt_t *)sto[i].private_data)->B = 255; // impossible\n        }\n        // Set this to higher values to enable more debug information\n        // minimum 0x00, maximum 0xff\n        UsbDEBUGlvl = 0x81;\n\n#if !defined(CORE_TEENSY) && defined(__AVR__)\n        // make LED pin as an output:\n        pinMode(LED_BUILTIN, OUTPUT);\n        pinMode(2, OUTPUT);\n        // Ensure TX is off\n        _SFR_BYTE(UCSR0B) &= ~_BV(TXEN0);\n        // Initialize 'debug' serial port\n        USB_HOST_SERIAL.begin(115200);\n        // Do not start primary Serial port if already started.\n        if(bit_is_clear(UCSR0B, TXEN0)) {\n                Serial.begin(115200);\n                serr = true;\n        }\n\n\n        // Blink LED\n        delay(500);\n        analogWrite(LED_BUILTIN, 255);\n        delay(500);\n        analogWrite(LED_BUILTIN, 0);\n        delay(500);\n#else\n        while(!Serial);\n        Serial.begin(115200); // On the Teensy 3.x we get a delay at least!\n#endif\n#if defined(__AVR__)\n        // Set up stdio/stderr\n        tty_stdio.put = tty_std_putc;\n        tty_stdio.get = tty_std_getc;\n        tty_stdio.flags = _FDEV_SETUP_RW;\n        tty_stdio.udata = 0;\n\n        tty_stderr.put = tty_stderr_putc;\n        tty_stderr.get = NULL;\n        tty_stderr.flags = _FDEV_SETUP_WRITE;\n        tty_stderr.udata = 0;\n\n        stdout = &tty_stdio;\n        stdin = &tty_stdio;\n        stderr = &tty_stderr;\n#endif\n        printf_P(PSTR(\"\\r\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nStart\\r\\n\"));\n        printf_P(PSTR(\"Current UsbDEBUGlvl %02x\\r\\n\"), UsbDEBUGlvl);\n        printf_P(PSTR(\"'+' and '-' increase/decrease by 0x01\\r\\n\"));\n        printf_P(PSTR(\"'.' and ',' increase/decrease by 0x10\\r\\n\"));\n        printf_P(PSTR(\"'t' will run a 10MB write/read test and print out the time it took.\\r\\n\"));\n        printf_P(PSTR(\"'e' will toggle vbus off for a few moments.\\r\\n\\r\\n\"));\n        printf_P(PSTR(\"Long filename support: \"\n#if _USE_LFN\n                \"Enabled\"\n#else\n                \"Disabled\"\n#endif\n                \"\\r\\n\"));\n        if(serr) {\n                fprintf_P(stderr, PSTR(\"\\r\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n\\nStart\\r\\n\"));\n                fprintf_P(stderr, PSTR(\"Current UsbDEBUGlvl %02x\\r\\n\"), UsbDEBUGlvl);\n                fprintf_P(stderr, PSTR(\"Long filename support: \"\n#if _USE_LFN\n                        \"Enabled\"\n#else\n                        \"Disabled\"\n#endif\n                        \"\\r\\n\"));\n        }\n\n#if !defined(CORE_TEENSY) && defined(__AVR__)\n        analogWrite(LED_BUILTIN, 255);\n        delay(500);\n        analogWrite(LED_BUILTIN, 0);\n        delay(500);\n        analogWrite(LED_BUILTIN, 255);\n        delay(500);\n        analogWrite(LED_BUILTIN, 0);\n        delay(500);\n        analogWrite(LED_BUILTIN, 255);\n        delay(500);\n        analogWrite(LED_BUILTIN, 0);\n        delay(500);\n\n        LEDnext_time = millis() + 1;\n#if EXT_RAM\n        printf_P(PSTR(\"Total EXT RAM banks %i\\r\\n\"), xmem::getTotalBanks());\n#endif\n        printf_P(PSTR(\"Available heap: %u Bytes\\r\\n\"), freeHeap());\n        printf_P(PSTR(\"SP %x\\r\\n\"), (uint8_t *)(SP));\n#endif\n\n        // Even though I'm not going to actually be deleting,\n        // I want to be able to have slightly more control.\n        // Besides, it is easier to initialize stuff...\n#if WANT_HUB_TEST\n        for(int i = 0; i < MAX_HUBS; i++) {\n                Hubs[i] = new USBHub(&Usb);\n#if defined(__AVR__)\n                printf_P(PSTR(\"Available heap: %u Bytes\\r\\n\"), freeHeap());\n#endif\n        }\n#endif\n        // Initialize generic storage. This must be done before USB starts.\n        Init_Generic_Storage();\n\n        while(Usb.Init(1000) == -1) {\n                printf_P(PSTR(\"No USB HOST Shield?\\r\\n\"));\n                Notify(PSTR(\"OSC did not start.\"), 0x40);\n        }\n\n#if !defined(CORE_TEENSY) && defined(__AVR__)\n        cli();\n        TCCR3A = 0;\n        TCCR3B = 0;\n        // (0.01/(1/((16 *(10^6)) / 8))) - 1 = 19999\n        OCR3A = 19999;\n        TCCR3B |= prescale8;\n        TIMSK3 |= (1 << OCIE1A);\n        sei();\n\n        HEAPnext_time = millis() + 10000;\n#endif\n#if defined(__AVR__)\n        HEAPnext_time = millis() + 10000;\n#endif\n}\n\nvoid serialEvent() {\n        // Adjust UsbDEBUGlvl level on-the-fly.\n        // + to increase, - to decrease, * to display current level.\n        // . to increase by 16, , to decrease by 16\n        // e to flick VBUS\n        // * to report debug level\n        if(Serial.available()) {\n                int inByte = Serial.read();\n                switch(inByte) {\n                        case '+':\n                                if(UsbDEBUGlvl < 0xff) UsbDEBUGlvl++;\n                                reportlvl = true;\n                                break;\n                        case '-':\n                                if(UsbDEBUGlvl > 0x00) UsbDEBUGlvl--;\n                                reportlvl = true;\n                                break;\n                        case '.':\n                                if(UsbDEBUGlvl < 0xf0) UsbDEBUGlvl += 16;\n                                reportlvl = true;\n                                break;\n                        case ',':\n                                if(UsbDEBUGlvl > 0x0f) UsbDEBUGlvl -= 16;\n                                reportlvl = true;\n                                break;\n                        case '*':\n                                reportlvl = true;\n                                break;\n                        case 't':\n                                runtest = true;\n                                break;\n                        case 'e':\n                                change = true;\n                                usbon = false;\n                                break;\n                }\n        }\n}\n\n#if !defined(CORE_TEENSY) && defined(__AVR__)\n// ALL teensy versions LACK PWM ON LED\n\nISR(TIMER3_COMPA_vect) {\n        if((long)(millis() - LEDnext_time) >= 0L) {\n                LEDnext_time = millis() + 30;\n\n                // set the brightness of LED\n                analogWrite(LED_BUILTIN, brightness);\n\n                // change the brightness for next time through the loop:\n                brightness = brightness + fadeAmount;\n\n                // reverse the direction of the fading at the ends of the fade:\n                if(brightness <= 0) {\n                        brightness = 0;\n                        fadeAmount = -fadeAmount;\n                }\n                if(brightness >= 255) {\n                        brightness = 255;\n                        fadeAmount = -fadeAmount;\n                }\n        }\n}\n#endif\n\nbool isfat(uint8_t t) {\n        return (t == 0x01 || t == 0x04 || t == 0x06 || t == 0x0b || t == 0x0c || t == 0x0e || t == 0x1);\n}\n\nvoid die(FRESULT rc) {\n        printf_P(PSTR(\"Failed with rc=%u.\\r\\n\"), rc);\n        //for (;;);\n}\n\nvoid loop() {\n        FIL My_File_Object_x; /* File object */\n\n#if defined(__AVR__)\n        // Print a heap status report about every 10 seconds.\n        if((long)(millis() - HEAPnext_time) >= 0L) {\n                if(UsbDEBUGlvl > 0x50) {\n                        printf_P(PSTR(\"Available heap: %u Bytes\\r\\n\"), freeHeap());\n                }\n                HEAPnext_time = millis() + 10000;\n        }\n        TCCR3B = 0;\n#endif\n#if defined(CORE_TEENSY)\n        // Teensy suffers here, oh well...\n        serialEvent();\n#endif\n        // Horrid! This sort of thing really belongs in an ISR, not here!\n        // We also will be needing to test each hub port, we don't do this yet!\n        if(!change && !usbon && (long)(millis() - usbon_time) >= 0L) {\n                change = true;\n                usbon = true;\n        }\n\n        if(change) {\n                change = false;\n                if(usbon) {\n                        Usb.vbusPower(vbus_on);\n                        printf_P(PSTR(\"VBUS on\\r\\n\"));\n                } else {\n                        Usb.vbusPower(vbus_off);\n                        usbon_time = millis() + 2000;\n                }\n        }\n        Usb.Task();\n        current_state = Usb.getUsbTaskState();\n        if(current_state != last_state) {\n                if(UsbDEBUGlvl > 0x50)\n                        printf_P(PSTR(\"USB state = %x\\r\\n\"), current_state);\n#if !defined(CORE_TEENSY) && defined(__AVR__)\n                if(current_state == USB_STATE_RUNNING) {\n                        fadeAmount = 30;\n                }\n#endif\n                if(current_state == USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE) {\n#if !defined(CORE_TEENSY) && defined(__AVR__)\n                        fadeAmount = 80;\n#endif\n                        partsready = false;\n                        for(int i = 0; i < cpart; i++) {\n                                if(Fats[i] != NULL)\n                                        delete Fats[i];\n                                Fats[i] = NULL;\n                        }\n                        fatready = false;\n                        notified = false;\n                        cpart = 0;\n                }\n                last_state = current_state;\n        }\n\n        // only do any of this if usb is on\n        if(usbon) {\n                if(partsready && !fatready) {\n                        if(cpart > 0) fatready = true;\n                }\n                // This is horrible, and needs to be moved elsewhere!\n                for(int B = 0; B < MAX_USB_MS_DRIVERS; B++) {\n                        if((!partsready) && (UHS_USB_BulkOnly[B]->GetAddress())) {\n\n                                // Build a list.\n                                int ML = UHS_USB_BulkOnly[B]->GetbMaxLUN();\n                                //printf(\"MAXLUN = %i\\r\\n\", ML);\n                                ML++;\n                                for(int i = 0; i < ML; i++) {\n                                        if(UHS_USB_BulkOnly[B]->LUNIsGood(i)) {\n                                                partsready = true;\n                                                ((pvt_t *)(sto[i].private_data))->lun = i;\n                                                ((pvt_t *)(sto[i].private_data))->B = B;\n                                                sto[i].Reads = *UHS_USB_BulkOnly_Read;\n                                                sto[i].Writes = *UHS_USB_BulkOnly_Write;\n                                                sto[i].Status = *UHS_USB_BulkOnly_Status;\n                                                sto[i].Initialize = *UHS_USB_BulkOnly_Initialize;\n                                                sto[i].Commit = *UHS_USB_BulkOnly_Commit;\n                                                sto[i].TotalSectors = UHS_USB_BulkOnly[B]->GetCapacity(i);\n                                                sto[i].SectorSize = UHS_USB_BulkOnly[B]->GetSectorSize(i);\n                                                printf_P(PSTR(\"LUN:\\t\\t%u\\r\\n\"), i);\n                                                printf_P(PSTR(\"Total Sectors:\\t%08lx\\t%lu\\r\\n\"), sto[i].TotalSectors, sto[i].TotalSectors);\n                                                printf_P(PSTR(\"Sector Size:\\t%04x\\t\\t%u\\r\\n\"), sto[i].SectorSize, sto[i].SectorSize);\n                                                // get the partition data...\n                                                PT = new PCPartition;\n\n                                                if(!PT->Init(&sto[i])) {\n                                                        part_t *apart;\n                                                        for(int j = 0; j < 4; j++) {\n                                                                apart = PT->GetPart(j);\n                                                                if(apart != NULL && apart->type != 0x00) {\n                                                                        memcpy(&(parts[cpart]), apart, sizeof (part_t));\n                                                                        printf_P(PSTR(\"Partition %u type %#02x\\r\\n\"), j, parts[cpart].type);\n                                                                        // for now\n                                                                        if(isfat(parts[cpart].type)) {\n                                                                                Fats[cpart] = new PFAT(&sto[i], cpart, parts[cpart].firstSector);\n                                                                                //int r = Fats[cpart]->Good();\n                                                                                if(Fats[cpart]->MountStatus()) {\n                                                                                        delete Fats[cpart];\n                                                                                        Fats[cpart] = NULL;\n                                                                                } else cpart++;\n                                                                        }\n                                                                }\n                                                        }\n                                                } else {\n                                                        // try superblock\n                                                        Fats[cpart] = new PFAT(&sto[i], cpart, 0);\n                                                        //int r = Fats[cpart]->Good();\n                                                        if(Fats[cpart]->MountStatus()) {\n                                                                //printf_P(PSTR(\"Superblock error %x\\r\\n\"), r);\n                                                                delete Fats[cpart];\n                                                                Fats[cpart] = NULL;\n                                                        } else cpart++;\n\n                                                }\n                                                delete PT;\n                                        } else {\n                                                sto[i].Writes = NULL;\n                                                sto[i].Reads = NULL;\n                                                sto[i].Initialize = NULL;\n                                                sto[i].TotalSectors = 0UL;\n                                                sto[i].SectorSize = 0;\n                                        }\n                                }\n\n                        }\n                }\n\n                if(fatready) {\n                        if(Fats[0] != NULL) {\n                                struct Pvt * p;\n                                p = ((struct Pvt *)(Fats[0]->storage->private_data));\n                                if(!UHS_USB_BulkOnly[p->B]->LUNIsGood(p->lun)) {\n                                        // media change\n#if !defined(CORE_TEENSY) && defined(__AVR__)\n                                        fadeAmount = 80;\n#endif\n                                        partsready = false;\n                                        for(int i = 0; i < cpart; i++) {\n                                                if(Fats[i] != NULL)\n                                                        delete Fats[i];\n                                                Fats[cpart] = NULL;\n                                        }\n                                        fatready = false;\n                                        notified = false;\n                                        cpart = 0;\n                                }\n\n                        }\n                }\n                if(fatready) {\n                        FRESULT rc; /* Result code */\n                        UINT bw, br, i;\n                        if(!notified) {\n#if !defined(CORE_TEENSY) && defined(__AVR__)\n                                fadeAmount = 5;\n#endif\n                                notified = true;\n                                FATFS *fs = NULL;\n                                for(int zz = 0; zz < _VOLUMES; zz++) {\n                                        if(Fats[zz]->volmap == 0) fs = Fats[zz]->ffs;\n                                }\n                                printf_P(PSTR(\"\\r\\nOpen an existing file (message.txt).\\r\\n\"));\n                                rc = f_open(&My_File_Object_x, \"0:/MESSAGE.TXT\", FA_READ);\n                                if(rc) printf_P(PSTR(\"Error %i, message.txt not found.\\r\\n\"), rc);\n                                else {\n                                        printf_P(PSTR(\"\\r\\nType the file content.\\r\\n\"));\n                                        for(;;) {\n                                                rc = f_read(&My_File_Object_x, My_Buff_x, mbxs, &br); /* Read a chunk of file */\n                                                if(rc || !br) break; /* Error or end of file */\n                                                for(i = 0; i < br; i++) {\n                                                        /* Type the data */\n                                                        if(My_Buff_x[i] == '\\n')\n                                                                Serial.write('\\r');\n                                                        if(My_Buff_x[i] != '\\r')\n                                                                Serial.write(My_Buff_x[i]);\n                                                        Serial.flush();\n                                                }\n                                        }\n                                        if(rc) {\n                                                f_close(&My_File_Object_x);\n                                                goto out;\n                                        }\n\n                                        printf_P(PSTR(\"\\r\\nClose the file.\\r\\n\"));\n                                        rc = f_close(&My_File_Object_x);\n                                        if(rc) goto out;\n                                }\n                                printf_P(PSTR(\"\\r\\nCreate a new file (hello.txt).\\r\\n\"));\n                                rc = f_open(&My_File_Object_x, \"0:/Hello.TxT\", FA_WRITE | FA_CREATE_ALWAYS);\n                                if(rc) {\n                                        die(rc);\n                                        goto outdir;\n                                }\n                                printf_P(PSTR(\"\\r\\nWrite a text data. (Hello world!)\\r\\n\"));\n                                rc = f_write(&My_File_Object_x, \"Hello world!\\r\\n\", 14, &bw);\n                                if(rc) {\n                                        goto out;\n                                }\n                                printf_P(PSTR(\"%u bytes written.\\r\\n\"), bw);\n\n                                printf_P(PSTR(\"\\r\\nClose the file.\\r\\n\"));\n                                rc = f_close(&My_File_Object_x);\n                                if(rc) {\n                                        die(rc);\n                                        goto out;\n                                }\noutdir:{\n#if _USE_LFN\n                                        char lfn[_MAX_LFN + 1];\n                                        FILINFO My_File_Info_Object_x; /* File information object */\n                                        My_File_Info_Object_x.lfname = lfn;\n#endif\n                                        DIR My_Dir_Object_x; /* Directory object */\n                                        printf_P(PSTR(\"\\r\\nOpen root directory.\\r\\n\"));\n                                        rc = f_opendir(&My_Dir_Object_x, \"0:/\");\n                                        if(rc) {\n                                                die(rc);\n                                                goto out;\n                                        }\n\n                                        printf_P(PSTR(\"\\r\\nDirectory listing...\\r\\n\"));\n#if defined(__AVR__)\n                                        printf_P(PSTR(\"Available heap: %u Bytes\\r\\n\"), freeHeap());\n#endif\n                                        for(;;) {\n#if _USE_LFN\n                                                My_File_Info_Object_x.lfsize = _MAX_LFN;\n#endif\n\n                                                rc = f_readdir(&My_Dir_Object_x, &My_File_Info_Object_x); /* Read a directory item */\n                                                if(rc || !My_File_Info_Object_x.fname[0]) break; /* Error or end of dir */\n\n                                                if(My_File_Info_Object_x.fattrib & AM_DIR) {\n                                                        Serial.write('d');\n                                                } else {\n                                                        Serial.write('-');\n                                                }\n                                                Serial.write('r');\n\n                                                if(My_File_Info_Object_x.fattrib & AM_RDO) {\n                                                        Serial.write('-');\n                                                } else {\n                                                        Serial.write('w');\n                                                }\n                                                if(My_File_Info_Object_x.fattrib & AM_HID) {\n                                                        Serial.write('h');\n                                                } else {\n                                                        Serial.write('-');\n                                                }\n\n                                                if(My_File_Info_Object_x.fattrib & AM_SYS) {\n                                                        Serial.write('s');\n                                                } else {\n                                                        Serial.write('-');\n                                                }\n\n                                                if(My_File_Info_Object_x.fattrib & AM_ARC) {\n                                                        Serial.write('a');\n                                                } else {\n                                                        Serial.write('-');\n                                                }\n\n#if _USE_LFN\n                                                if(*My_File_Info_Object_x.lfname)\n                                                        printf_P(PSTR(\" %8lu  %s (%s)\\r\\n\"), My_File_Info_Object_x.fsize, My_File_Info_Object_x.fname, My_File_Info_Object_x.lfname);\n                                                else\n#endif\n                                                        printf_P(PSTR(\" %8lu  %s\\r\\n\"), My_File_Info_Object_x.fsize, &(My_File_Info_Object_x.fname[0]));\n                                        }\n                                }\nout:\n                                if(rc) die(rc);\n\n                                DISK_IOCTL(fs->drv, CTRL_COMMIT, 0);\n                                printf_P(PSTR(\"\\r\\nTest completed.\\r\\n\"));\n\n                        }\n\n                        if(runtest) {\n                                ULONG ii, wt, rt, start, end;\n                                FATFS *fs = NULL;\n                                for(int zz = 0; zz < _VOLUMES; zz++) {\n                                        if(Fats[zz]->volmap == 0) fs = Fats[zz]->ffs;\n                                }\n                                runtest = false;\n                                f_unlink(\"0:/10MB.bin\");\n                                printf_P(PSTR(\"\\r\\nCreate a new 10MB test file (10MB.bin).\\r\\n\"));\n                                rc = f_open(&My_File_Object_x, \"0:/10MB.bin\", FA_WRITE | FA_CREATE_ALWAYS);\n                                if(rc) goto failed;\n                                for(bw = 0; bw < mbxs; bw++) My_Buff_x[bw] = bw & 0xff;\n                                fflush(stdout);\n                                start = millis();\n                                while(start == millis());\n                                for(ii = 10485760LU / mbxs; ii > 0LU; ii--) {\n                                        rc = f_write(&My_File_Object_x, My_Buff_x, mbxs, &bw);\n                                        if(rc || !bw) goto failed;\n                                }\n                                rc = f_close(&My_File_Object_x);\n                                if(rc) goto failed;\n                                end = millis();\n                                wt = (end - start) - 1;\n                                printf_P(PSTR(\"Time to write 10485760 bytes: %lu ms (%lu sec) \\r\\n\"), wt, (500 + wt) / 1000UL);\n                                rc = f_open(&My_File_Object_x, \"0:/10MB.bin\", FA_READ);\n                                fflush(stdout);\n                                start = millis();\n                                while(start == millis());\n                                if(rc) goto failed;\n                                for(;;) {\n                                        rc = f_read(&My_File_Object_x, My_Buff_x, mbxs, &bw); /* Read a chunk of file */\n                                        if(rc || !bw) break; /* Error or end of file */\n                                }\n                                end = millis();\n                                if(rc) goto failed;\n                                rc = f_close(&My_File_Object_x);\n                                if(rc) goto failed;\n                                rt = (end - start) - 1;\n                                printf_P(PSTR(\"Time to read 10485760 bytes: %lu ms (%lu sec)\\r\\nDelete test file\\r\\n\"), rt, (500 + rt) / 1000UL);\nfailed:\n                                if(rc) die(rc);\n                                DISK_IOCTL(fs->drv, CTRL_COMMIT, 0);\n                                printf_P(PSTR(\"10MB timing test finished.\\r\\n\"));\n                        }\n                }\n        }\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/gpl2.txt",
    "content": "\t\t    GNU GENERAL PUBLIC LICENSE\n\t\t       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n\t\t\t    Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Library General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\f\n\t\t    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\f\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\f\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\f\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n\t\t\t    NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n\t\t     END OF TERMS AND CONDITIONS\n\f\n\t    How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program; if not, write to the Free Software\n    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Library General\nPublic License instead of this License.\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/hexdump.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(_usb_h_) || defined(__HEXDUMP_H__)\n#error \"Never include hexdump.h directly; include Usb.h instead\"\n#else\n#define __HEXDUMP_H__\n\nextern int UsbDEBUGlvl;\n\ntemplate <class BASE_CLASS, class LEN_TYPE, class OFFSET_TYPE>\nclass HexDumper : public BASE_CLASS {\n        uint8_t byteCount;\n        OFFSET_TYPE byteTotal;\n\npublic:\n\n        HexDumper() : byteCount(0), byteTotal(0) {\n        };\n\n        void Initialize() {\n                byteCount = 0;\n                byteTotal = 0;\n        };\n\n        void Parse(const LEN_TYPE len, const uint8_t *pbuf, const OFFSET_TYPE &offset);\n};\n\ntemplate <class BASE_CLASS, class LEN_TYPE, class OFFSET_TYPE>\nvoid HexDumper<BASE_CLASS, LEN_TYPE, OFFSET_TYPE>::Parse(const LEN_TYPE len, const uint8_t *pbuf, const OFFSET_TYPE &offset) {\n        if(UsbDEBUGlvl >= 0x80) { // Fully bypass this block of code if we do not debug.\n                for(LEN_TYPE j = 0; j < len; j++, byteCount++, byteTotal++) {\n                        if(!byteCount) {\n                                PrintHex<OFFSET_TYPE > (byteTotal, 0x80);\n                                E_Notify(PSTR(\": \"), 0x80);\n                        }\n                        PrintHex<uint8_t > (pbuf[j], 0x80);\n                        E_Notify(PSTR(\" \"), 0x80);\n\n                        if(byteCount == 15) {\n                                E_Notify(PSTR(\"\\r\\n\"), 0x80);\n                                byteCount = 0xFF;\n                        }\n                }\n        }\n}\n\n#endif // __HEXDUMP_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/hid.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#include \"hid.h\"\n\n//get HID report descriptor\n\n/* WRONG! Endpoint is _ALWAYS_ ZERO for HID! We want the _INTERFACE_ value here!\nuint8_t HID::GetReportDescr(uint8_t ep, USBReadParser *parser) {\n        const uint8_t constBufLen = 64;\n        uint8_t buf[constBufLen];\n\n        uint8_t rcode = pUsb->ctrlReq(bAddress, ep, bmREQ_HID_REPORT, USB_REQUEST_GET_DESCRIPTOR, 0x00,\n                HID_DESCRIPTOR_REPORT, 0x0000, 128, constBufLen, buf, (USBReadParser*)parser);\n\n        //return ((rcode != hrSTALL) ? rcode : 0);\n        return rcode;\n}\n */\nuint8_t HID::GetReportDescr(uint16_t wIndex, USBReadParser *parser) {\n        const uint8_t constBufLen = 64;\n        uint8_t buf[constBufLen];\n\n        uint8_t rcode = pUsb->ctrlReq(bAddress, 0x00, bmREQ_HID_REPORT, USB_REQUEST_GET_DESCRIPTOR, 0x00,\n                HID_DESCRIPTOR_REPORT, wIndex, 128, constBufLen, buf, (USBReadParser*)parser);\n\n        //return ((rcode != hrSTALL) ? rcode : 0);\n        return rcode;\n}\n\n//uint8_t HID::getHidDescr( uint8_t ep, uint16_t nbytes, uint8_t* dataptr )\n//{\n//    return( pUsb->ctrlReq( bAddress, ep, bmREQ_GET_DESCR, USB_REQUEST_GET_DESCRIPTOR, 0x00, HID_DESCRIPTOR_HID, 0x0000, nbytes, dataptr ));\n//}\n\nuint8_t HID::SetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr) {\n        return ( pUsb->ctrlReq(bAddress, ep, bmREQ_HID_OUT, HID_REQUEST_SET_REPORT, report_id, report_type, iface, nbytes, nbytes, dataptr, NULL));\n}\n\nuint8_t HID::GetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr) {\n        return ( pUsb->ctrlReq(bAddress, ep, bmREQ_HID_IN, HID_REQUEST_GET_REPORT, report_id, report_type, iface, nbytes, nbytes, dataptr, NULL));\n}\n\nuint8_t HID::GetIdle(uint8_t iface, uint8_t reportID, uint8_t* dataptr) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_IN, HID_REQUEST_GET_IDLE, reportID, 0, iface, 0x0001, 0x0001, dataptr, NULL));\n}\n\nuint8_t HID::SetIdle(uint8_t iface, uint8_t reportID, uint8_t duration) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_OUT, HID_REQUEST_SET_IDLE, reportID, duration, iface, 0x0000, 0x0000, NULL, NULL));\n}\n\nuint8_t HID::SetProtocol(uint8_t iface, uint8_t protocol) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_OUT, HID_REQUEST_SET_PROTOCOL, protocol, 0x00, iface, 0x0000, 0x0000, NULL, NULL));\n}\n\nuint8_t HID::GetProtocol(uint8_t iface, uint8_t* dataptr) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_HID_IN, HID_REQUEST_GET_PROTOCOL, 0x00, 0x00, iface, 0x0001, 0x0001, dataptr, NULL));\n}\n\nvoid HID::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr) {\n        Notify(PSTR(\"Endpoint descriptor:\"), 0x80);\n        Notify(PSTR(\"\\r\\nLength:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);\n        Notify(PSTR(\"\\r\\nType:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);\n        Notify(PSTR(\"\\r\\nAddress:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);\n        Notify(PSTR(\"\\r\\nAttributes:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);\n        Notify(PSTR(\"\\r\\nMaxPktSize:\\t\"), 0x80);\n        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);\n        Notify(PSTR(\"\\r\\nPoll Intrv:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);\n}\n\nvoid HID::PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc) {\n        Notify(PSTR(\"\\r\\n\\r\\nHID Descriptor:\\r\\n\"), 0x80);\n        Notify(PSTR(\"bDescLength:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (pDesc->bLength, 0x80);\n\n        Notify(PSTR(\"\\r\\nbDescriptorType:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (pDesc->bDescriptorType, 0x80);\n\n        Notify(PSTR(\"\\r\\nbcdHID:\\t\\t\\t\"), 0x80);\n        D_PrintHex<uint16_t > (pDesc->bcdHID, 0x80);\n\n        Notify(PSTR(\"\\r\\nbCountryCode:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (pDesc->bCountryCode, 0x80);\n\n        Notify(PSTR(\"\\r\\nbNumDescriptors:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (pDesc->bNumDescriptors, 0x80);\n\n        Notify(PSTR(\"\\r\\nbDescrType:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (pDesc->bDescrType, 0x80);\n\n        Notify(PSTR(\"\\r\\nwDescriptorLength:\\t\"), 0x80);\n        D_PrintHex<uint16_t > (pDesc->wDescriptorLength, 0x80);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/hid.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(__HID_H__)\n#define __HID_H__\n\n#include \"Usb.h\"\n#include \"hidusagestr.h\"\n\n#define MAX_REPORT_PARSERS                      2\n#define HID_MAX_HID_CLASS_DESCRIPTORS           5\n\n#define DATA_SIZE_MASK                          0x03\n#define TYPE_MASK                               0x0C\n#define TAG_MASK                                0xF0\n\n#define DATA_SIZE_0                             0x00\n#define DATA_SIZE_1                             0x01\n#define DATA_SIZE_2                             0x02\n#define DATA_SIZE_4                             0x03\n\n#define TYPE_MAIN                               0x00\n#define TYPE_GLOBAL                             0x04\n#define TYPE_LOCAL                              0x08\n\n#define TAG_MAIN_INPUT                          0x80\n#define TAG_MAIN_OUTPUT                         0x90\n#define TAG_MAIN_COLLECTION                     0xA0\n#define TAG_MAIN_FEATURE                        0xB0\n#define TAG_MAIN_ENDCOLLECTION                  0xC0\n\n#define TAG_GLOBAL_USAGEPAGE                    0x00\n#define TAG_GLOBAL_LOGICALMIN                   0x10\n#define TAG_GLOBAL_LOGICALMAX                   0x20\n#define TAG_GLOBAL_PHYSMIN                      0x30\n#define TAG_GLOBAL_PHYSMAX                      0x40\n#define TAG_GLOBAL_UNITEXP                      0x50\n#define TAG_GLOBAL_UNIT                         0x60\n#define TAG_GLOBAL_REPORTSIZE                   0x70\n#define TAG_GLOBAL_REPORTID                     0x80\n#define TAG_GLOBAL_REPORTCOUNT                  0x90\n#define TAG_GLOBAL_PUSH                         0xA0\n#define TAG_GLOBAL_POP                          0xB0\n\n#define TAG_LOCAL_USAGE                         0x00\n#define TAG_LOCAL_USAGEMIN                      0x10\n#define TAG_LOCAL_USAGEMAX                      0x20\n\n/* HID requests */\n#define bmREQ_HID_OUT                           USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE\n#define bmREQ_HID_IN                            USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE\n#define bmREQ_HID_REPORT                        USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_INTERFACE\n\n/* HID constants. Not part of chapter 9 */\n/* Class-Specific Requests */\n#define HID_REQUEST_GET_REPORT                  0x01\n#define HID_REQUEST_GET_IDLE                    0x02\n#define HID_REQUEST_GET_PROTOCOL                0x03\n#define HID_REQUEST_SET_REPORT                  0x09\n#define HID_REQUEST_SET_IDLE                    0x0A\n#define HID_REQUEST_SET_PROTOCOL                0x0B\n\n/* Class Descriptor Types */\n#define HID_DESCRIPTOR_HID                      0x21\n#define HID_DESCRIPTOR_REPORT                   0x22\n#define HID_DESRIPTOR_PHY                       0x23\n\n/* Protocol Selection */\n#define HID_BOOT_PROTOCOL                       0x00\n#define HID_RPT_PROTOCOL                        0x01\n\n/* HID Interface Class Code */\n#define HID_INTF                                0x03\n\n/* HID Interface Class SubClass Codes */\n#define HID_BOOT_INTF_SUBCLASS                  0x01\n\n/* HID Interface Class Protocol Codes */\n#define HID_PROTOCOL_NONE                       0x00\n#define HID_PROTOCOL_KEYBOARD                   0x01\n#define HID_PROTOCOL_MOUSE                      0x02\n\n#define HID_ITEM_TYPE_MAIN                      0\n#define HID_ITEM_TYPE_GLOBAL                    1\n#define HID_ITEM_TYPE_LOCAL                     2\n#define HID_ITEM_TYPE_RESERVED                  3\n\n#define HID_LONG_ITEM_PREFIX                    0xfe    // Long item prefix value\n\n#define bmHID_MAIN_ITEM_TAG                     0xfc    // Main item tag mask\n\n#define bmHID_MAIN_ITEM_INPUT                   0x80    // Main item Input tag value\n#define bmHID_MAIN_ITEM_OUTPUT                  0x90    // Main item Output tag value\n#define bmHID_MAIN_ITEM_FEATURE                 0xb0    // Main item Feature tag value\n#define bmHID_MAIN_ITEM_COLLECTION              0xa0    // Main item Collection tag value\n#define bmHID_MAIN_ITEM_END_COLLECTION          0xce    // Main item End Collection tag value\n\n#define HID_MAIN_ITEM_COLLECTION_PHYSICAL       0\n#define HID_MAIN_ITEM_COLLECTION_APPLICATION    1\n#define HID_MAIN_ITEM_COLLECTION_LOGICAL        2\n#define HID_MAIN_ITEM_COLLECTION_REPORT         3\n#define HID_MAIN_ITEM_COLLECTION_NAMED_ARRAY    4\n#define HID_MAIN_ITEM_COLLECTION_USAGE_SWITCH   5\n#define HID_MAIN_ITEM_COLLECTION_USAGE_MODIFIER 6\n\nstruct HidItemPrefix {\n        uint8_t bSize : 2;\n        uint8_t bType : 2;\n        uint8_t bTag : 4;\n};\n\nstruct MainItemIOFeature {\n        uint8_t bmIsConstantOrData : 1;\n        uint8_t bmIsArrayOrVariable : 1;\n        uint8_t bmIsRelativeOrAbsolute : 1;\n        uint8_t bmIsWrapOrNoWrap : 1;\n        uint8_t bmIsNonLonearOrLinear : 1;\n        uint8_t bmIsNoPreferedOrPrefered : 1;\n        uint8_t bmIsNullOrNoNull : 1;\n        uint8_t bmIsVolatileOrNonVolatile : 1;\n};\n\nclass HID;\n\nclass HIDReportParser {\npublic:\n        virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) = 0;\n};\n\nclass HID : public USBDeviceConfig, public UsbConfigXtracter {\nprotected:\n        USB *pUsb; // USB class instance pointer\n        uint8_t bAddress; // address\n\nprotected:\n        static const uint8_t epInterruptInIndex = 1; // InterruptIN  endpoint index\n        static const uint8_t epInterruptOutIndex = 2; // InterruptOUT endpoint index\n\n        static const uint8_t maxHidInterfaces = 3;\n        static const uint8_t maxEpPerInterface = 2;\n        static const uint8_t totalEndpoints = (maxHidInterfaces * maxEpPerInterface + 1);\n\n        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);\n        void PrintHidDescriptor(const USB_HID_DESCRIPTOR *pDesc);\n\n        virtual HIDReportParser* GetReportParser(uint8_t id) {\n                return NULL;\n        };\n\npublic:\n\n        HID(USB *pusb) : pUsb(pusb) {\n        };\n\n        const USB* GetUsb() {\n                return pUsb;\n        };\n\n        virtual bool SetReportParser(uint8_t id, HIDReportParser *prs) {\n                return false;\n        };\n\n        uint8_t SetProtocol(uint8_t iface, uint8_t protocol);\n        uint8_t GetProtocol(uint8_t iface, uint8_t* dataptr);\n        uint8_t GetIdle(uint8_t iface, uint8_t reportID, uint8_t* dataptr);\n        uint8_t SetIdle(uint8_t iface, uint8_t reportID, uint8_t duration);\n\n        uint8_t GetReportDescr(uint16_t wIndex, USBReadParser *parser = NULL);\n\n        uint8_t GetHidDescr(uint8_t ep, uint16_t nbytes, uint8_t* dataptr);\n        uint8_t GetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr);\n        uint8_t SetReport(uint8_t ep, uint8_t iface, uint8_t report_type, uint8_t report_id, uint16_t nbytes, uint8_t* dataptr);\n};\n\n#endif // __HID_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/hidboot.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#include \"hidboot.h\"\n\nvoid MouseReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {\n        MOUSEINFO *pmi = (MOUSEINFO*)buf;\n        // Future:\n        // bool event;\n\n#if 0\n        if (prevState.mouseInfo.bmLeftButton == 0 && pmi->bmLeftButton == 1)\n                OnLeftButtonDown(pmi);\n\n        if (prevState.mouseInfo.bmLeftButton == 1 && pmi->bmLeftButton == 0)\n                OnLeftButtonUp(pmi);\n\n        if (prevState.mouseInfo.bmRightButton == 0 && pmi->bmRightButton == 1)\n                OnRightButtonDown(pmi);\n\n        if (prevState.mouseInfo.bmRightButton == 1 && pmi->bmRightButton == 0)\n                OnRightButtonUp(pmi);\n\n        if (prevState.mouseInfo.bmMiddleButton == 0 && pmi->bmMiddleButton == 1)\n                OnMiddleButtonDown(pmi);\n\n        if (prevState.mouseInfo.bmMiddleButton == 1 && pmi->bmMiddleButton == 0)\n                OnMiddleButtonUp(pmi);\n\n        if (prevState.mouseInfo.dX != pmi->dX || prevState.mouseInfo.dY != pmi->dY)\n                OnMouseMove(pmi);\n\n        if (len > sizeof (MOUSEINFO))\n                for (uint8_t i = 0; i<sizeof (MOUSEINFO); i++)\n                        prevState.bInfo[i] = buf[i];\n#else\n        //\n        // Optimization idea:\n        //\n        // 1: Don't pass the structure on every event. Buttons would not need it.\n        // 2: Only pass x/y values in the movement routine.\n        //\n        // These two changes (with the ones I have made) will save extra flash.\n        // The only \"bad\" thing is that it could break old code.\n        //\n        // Future thoughts:\n        //\n        // The extra space gained can be used for a generic mouse event that can be called\n        // when there are _ANY_ changes. This one you _MAY_ want to pass everything, however the\n        // sketch could already have noted these facts to support drag/drop scroll wheel stuff, etc.\n        //\n\n        // Why do we need to pass the structure for buttons?\n        // The function call not enough of a hint for what is happening?\n        if(prevState.mouseInfo.bmLeftButton != pmi->bmLeftButton ) {\n                if(pmi->bmLeftButton) {\n                        OnLeftButtonDown(pmi);\n                } else {\n                        OnLeftButtonUp(pmi);\n                }\n                // Future:\n                // event = true;\n        }\n\n        if(prevState.mouseInfo.bmRightButton != pmi->bmRightButton) {\n                if(pmi->bmRightButton) {\n                        OnRightButtonDown(pmi);\n                } else {\n                        OnRightButtonUp(pmi);\n                }\n                // Future:\n                // event = true;\n        }\n\n        if(prevState.mouseInfo.bmMiddleButton != pmi->bmMiddleButton) {\n                if(pmi->bmMiddleButton) {\n                        OnMiddleButtonDown(pmi);\n                } else {\n                        OnMiddleButtonUp(pmi);\n                }\n                // Future:\n                // event = true;\n        }\n\n        //\n        // Scroll wheel(s), are not part of the spec, but we could support it.\n        // Logitech wireless keyboard and mouse combo reports scroll wheel in byte 4\n        // We wouldn't even need to save this information.\n        //if(len > 3) {\n        //}\n        //\n\n        // Mice only report motion when they actually move!\n        // Why not just pass the x/y values to simplify things??\n        if(pmi->dX || pmi->dY) {\n                OnMouseMove(pmi);\n                // Future:\n                // event = true;\n        }\n\n        //\n        // Future:\n        // Provide a callback that operates on the gathered events from above.\n        //\n        // if(event) OnMouse();\n        //\n\n        // Only the first byte matters (buttons). We do NOT need to save position info.\n        prevState.bInfo[0] = buf[0];\n#endif\n\n};\n\nvoid KeyboardReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {\n        // On error - return\n        if (buf[2] == 1)\n                return;\n\n        //KBDINFO       *pki = (KBDINFO*)buf;\n\n        // provide event for changed control key state\n        if (prevState.bInfo[0x00] != buf[0x00]) {\n                OnControlKeysChanged(prevState.bInfo[0x00], buf[0x00]);\n        }\n\n        for (uint8_t i = 2; i < 8; i++) {\n                bool down = false;\n                bool up = false;\n\n                for (uint8_t j = 2; j < 8; j++) {\n                        if (buf[i] == prevState.bInfo[j] && buf[i] != 1)\n                                down = true;\n                        if (buf[j] == prevState.bInfo[i] && prevState.bInfo[i] != 1)\n                                up = true;\n                }\n                if (!down) {\n                        HandleLockingKeys(hid, buf[i]);\n                        OnKeyDown(*buf, buf[i]);\n                }\n                if (!up)\n                        OnKeyUp(prevState.bInfo[0], prevState.bInfo[i]);\n        }\n        for (uint8_t i = 0; i < 8; i++)\n                prevState.bInfo[i] = buf[i];\n};\n\nconst uint8_t KeyboardReportParser::numKeys[10] PROGMEM = {'!', '@', '#', '$', '%', '^', '&', '*', '(', ')'};\nconst uint8_t KeyboardReportParser::symKeysUp[12] PROGMEM = {'_', '+', '{', '}', '|', '~', ':', '\"', '~', '<', '>', '?'};\nconst uint8_t KeyboardReportParser::symKeysLo[12] PROGMEM = {'-', '=', '[', ']', '\\\\', ' ', ';', '\\'', '`', ',', '.', '/'};\nconst uint8_t KeyboardReportParser::padKeys[5] PROGMEM = {'/', '*', '-', '+', 0x13};\n\nuint8_t KeyboardReportParser::OemToAscii(uint8_t mod, uint8_t key) {\n        uint8_t shift = (mod & 0x22);\n\n        // [a-z]\n        if (VALUE_WITHIN(key, 0x04, 0x1d)) {\n                // Upper case letters\n                if ((kbdLockingKeys.kbdLeds.bmCapsLock == 0 && shift) ||\n                        (kbdLockingKeys.kbdLeds.bmCapsLock == 1 && shift == 0))\n                        return (key - 4 + 'A');\n\n                        // Lower case letters\n                else\n                        return (key - 4 + 'a');\n        }// Numbers\n        else if (VALUE_WITHIN(key, 0x1e, 0x27)) {\n                if (shift)\n                        return ((uint8_t)pgm_read_byte(&getNumKeys()[key - 0x1e]));\n                else\n                        return ((key == UHS_HID_BOOT_KEY_ZERO) ? '0' : key - 0x1e + '1');\n        }// Keypad Numbers\n        else if(VALUE_WITHIN(key, 0x59, 0x61)) {\n                if(kbdLockingKeys.kbdLeds.bmNumLock == 1)\n                        return (key - 0x59 + '1');\n        } else if(VALUE_WITHIN(key, 0x2d, 0x38))\n                return ((shift) ? (uint8_t)pgm_read_byte(&getSymKeysUp()[key - 0x2d]) : (uint8_t)pgm_read_byte(&getSymKeysLo()[key - 0x2d]));\n        else if(VALUE_WITHIN(key, 0x54, 0x58))\n                return (uint8_t)pgm_read_byte(&getPadKeys()[key - 0x54]);\n        else {\n                switch(key) {\n                        case UHS_HID_BOOT_KEY_SPACE: return (0x20);\n                        case UHS_HID_BOOT_KEY_ENTER: return (0x13);\n                        case UHS_HID_BOOT_KEY_ZERO2: return ((kbdLockingKeys.kbdLeds.bmNumLock == 1) ? '0': 0);\n                        case UHS_HID_BOOT_KEY_PERIOD: return ((kbdLockingKeys.kbdLeds.bmNumLock == 1) ? '.': 0);\n                }\n        }\n        return ( 0);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/hidboot.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(__HIDBOOT_H__)\n#define __HIDBOOT_H__\n\n#include \"hid.h\"\n\n#define UHS_HID_BOOT_KEY_ZERO           0x27\n#define UHS_HID_BOOT_KEY_ENTER          0x28\n#define UHS_HID_BOOT_KEY_SPACE          0x2c\n#define UHS_HID_BOOT_KEY_CAPS_LOCK      0x39\n#define UHS_HID_BOOT_KEY_SCROLL_LOCK    0x47\n#define UHS_HID_BOOT_KEY_NUM_LOCK       0x53\n#define UHS_HID_BOOT_KEY_ZERO2          0x62\n#define UHS_HID_BOOT_KEY_PERIOD         0x63\n\n// Don't worry, GCC will optimize the result to a final value.\n#define bitsEndpoints(p) ((((p) & HID_PROTOCOL_KEYBOARD)? 2 : 0) | (((p) & HID_PROTOCOL_MOUSE)? 1 : 0))\n#define totalEndpoints(p) ((bitsEndpoints(p) == 3) ? 3 : 2)\n#define epMUL(p) ((((p) & HID_PROTOCOL_KEYBOARD)? 1 : 0) + (((p) & HID_PROTOCOL_MOUSE)? 1 : 0))\n\n// Already defined in hid.h\n// #define HID_MAX_HID_CLASS_DESCRIPTORS 5\n\nstruct MOUSEINFO {\n\n        struct {\n                uint8_t bmLeftButton : 1;\n                uint8_t bmRightButton : 1;\n                uint8_t bmMiddleButton : 1;\n                uint8_t bmDummy : 5;\n        };\n        int8_t dX;\n        int8_t dY;\n};\n\nclass MouseReportParser : public HIDReportParser {\n\n        union {\n                MOUSEINFO mouseInfo;\n                uint8_t bInfo[sizeof (MOUSEINFO)];\n        } prevState;\n\npublic:\n        void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);\n\nprotected:\n\n        virtual void OnMouseMove(MOUSEINFO *mi) {\n        };\n\n        virtual void OnLeftButtonUp(MOUSEINFO *mi) {\n        };\n\n        virtual void OnLeftButtonDown(MOUSEINFO *mi) {\n        };\n\n        virtual void OnRightButtonUp(MOUSEINFO *mi) {\n        };\n\n        virtual void OnRightButtonDown(MOUSEINFO *mi) {\n        };\n\n        virtual void OnMiddleButtonUp(MOUSEINFO *mi) {\n        };\n\n        virtual void OnMiddleButtonDown(MOUSEINFO *mi) {\n        };\n};\n\nstruct MODIFIERKEYS {\n        uint8_t bmLeftCtrl : 1;\n        uint8_t bmLeftShift : 1;\n        uint8_t bmLeftAlt : 1;\n        uint8_t bmLeftGUI : 1;\n        uint8_t bmRightCtrl : 1;\n        uint8_t bmRightShift : 1;\n        uint8_t bmRightAlt : 1;\n        uint8_t bmRightGUI : 1;\n};\n\nstruct KBDINFO {\n\n        struct {\n                uint8_t bmLeftCtrl : 1;\n                uint8_t bmLeftShift : 1;\n                uint8_t bmLeftAlt : 1;\n                uint8_t bmLeftGUI : 1;\n                uint8_t bmRightCtrl : 1;\n                uint8_t bmRightShift : 1;\n                uint8_t bmRightAlt : 1;\n                uint8_t bmRightGUI : 1;\n        };\n        uint8_t bReserved;\n        uint8_t Keys[6];\n};\n\nstruct KBDLEDS {\n        uint8_t bmNumLock : 1;\n        uint8_t bmCapsLock : 1;\n        uint8_t bmScrollLock : 1;\n        uint8_t bmCompose : 1;\n        uint8_t bmKana : 1;\n        uint8_t bmReserved : 3;\n};\n\nclass KeyboardReportParser : public HIDReportParser {\n        static const uint8_t numKeys[10];\n        static const uint8_t symKeysUp[12];\n        static const uint8_t symKeysLo[12];\n        static const uint8_t padKeys[5];\n\nprotected:\n\n        union {\n                KBDINFO kbdInfo;\n                uint8_t bInfo[sizeof (KBDINFO)];\n        } prevState;\n\n        union {\n                KBDLEDS kbdLeds;\n                uint8_t bLeds;\n        } kbdLockingKeys;\n\n        uint8_t OemToAscii(uint8_t mod, uint8_t key);\n\npublic:\n\n        KeyboardReportParser() {\n                kbdLockingKeys.bLeds = 0;\n        };\n\n        void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);\n\nprotected:\n\n        virtual uint8_t HandleLockingKeys(HID* hid, uint8_t key) {\n                uint8_t old_keys = kbdLockingKeys.bLeds;\n\n                switch(key) {\n                        case UHS_HID_BOOT_KEY_NUM_LOCK:\n                                kbdLockingKeys.kbdLeds.bmNumLock = ~kbdLockingKeys.kbdLeds.bmNumLock;\n                                break;\n                        case UHS_HID_BOOT_KEY_CAPS_LOCK:\n                                kbdLockingKeys.kbdLeds.bmCapsLock = ~kbdLockingKeys.kbdLeds.bmCapsLock;\n                                break;\n                        case UHS_HID_BOOT_KEY_SCROLL_LOCK:\n                                kbdLockingKeys.kbdLeds.bmScrollLock = ~kbdLockingKeys.kbdLeds.bmScrollLock;\n                                break;\n                }\n\n                if(old_keys != kbdLockingKeys.bLeds && hid)\n                        return (hid->SetReport(0, 0/*hid->GetIface()*/, 2, 0, 1, &kbdLockingKeys.bLeds));\n\n                return 0;\n        };\n\n        virtual void OnControlKeysChanged(uint8_t before, uint8_t after) {\n        };\n\n        virtual void OnKeyDown(uint8_t mod, uint8_t key) {\n        };\n\n        virtual void OnKeyUp(uint8_t mod, uint8_t key) {\n        };\n\n        virtual const uint8_t *getNumKeys() {\n                return numKeys;\n        };\n\n        virtual const uint8_t *getSymKeysUp() {\n                return symKeysUp;\n        };\n\n        virtual const uint8_t *getSymKeysLo() {\n                return symKeysLo;\n        };\n\n        virtual const uint8_t *getPadKeys() {\n                return padKeys;\n        };\n};\n\ntemplate <const uint8_t BOOT_PROTOCOL>\nclass HIDBoot : public HID //public USBDeviceConfig, public UsbConfigXtracter\n{\n        EpInfo epInfo[totalEndpoints(BOOT_PROTOCOL)];\n        HIDReportParser *pRptParser[epMUL(BOOT_PROTOCOL)];\n\n        uint8_t bConfNum; // configuration number\n        uint8_t bIfaceNum; // Interface Number\n        uint8_t bNumIface; // number of interfaces in the configuration\n        uint8_t bNumEP; // total number of EP in the configuration\n        uint32_t qNextPollTime; // next poll time\n        bool bPollEnable; // poll enable flag\n        uint8_t bInterval; // largest interval\n\n        void Initialize();\n\n        virtual HIDReportParser* GetReportParser(uint8_t id) {\n                return pRptParser[id];\n        };\n\npublic:\n        HIDBoot(USB *p);\n\n        virtual bool SetReportParser(uint8_t id, HIDReportParser *prs) {\n                pRptParser[id] = prs;\n                return true;\n        };\n\n        // USBDeviceConfig implementation\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        uint8_t Release();\n        uint8_t Poll();\n\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        virtual bool isReady() {\n                return bPollEnable;\n        };\n\n        // UsbConfigXtracter implementation\n        // Method should be defined here if virtual.\n        virtual void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);\n\n        virtual bool DEVCLASSOK(uint8_t klass) {\n                return (klass == USB_CLASS_HID);\n        }\n\n        virtual bool DEVSUBCLASSOK(uint8_t subklass) {\n                return (subklass == BOOT_PROTOCOL);\n        }\n};\n\ntemplate <const uint8_t BOOT_PROTOCOL>\nHIDBoot<BOOT_PROTOCOL>::HIDBoot(USB *p) :\nHID(p),\nqNextPollTime(0),\nbPollEnable(false) {\n        Initialize();\n\n        for(int i = 0; i < epMUL(BOOT_PROTOCOL); i++) {\n                pRptParser[i] = NULL;\n        }\n        if(pUsb)\n                pUsb->RegisterDeviceClass(this);\n}\n\ntemplate <const uint8_t BOOT_PROTOCOL>\nvoid HIDBoot<BOOT_PROTOCOL>::Initialize() {\n        for(int i = 0; i < totalEndpoints(BOOT_PROTOCOL); i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;\n        }\n        bNumEP = 1;\n        bNumIface = 0;\n        bConfNum = 0;\n}\n\ntemplate <const uint8_t BOOT_PROTOCOL>\nuint8_t HIDBoot<BOOT_PROTOCOL>::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);\n\n        uint8_t buf[constBufSize];\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        uint8_t len = 0;\n        //uint16_t cd_len = 0;\n\n        uint8_t num_of_conf; // number of configurations\n        //uint8_t num_of_intf; // number of interfaces\n\n        AddressPool &addrPool = pUsb->GetAddressPool();\n\n        USBTRACE(\"BM Init\\r\\n\");\n        //USBTRACE2(\"totalEndpoints:\", (uint8_t) (totalEndpoints(BOOT_PROTOCOL)));\n        //USBTRACE2(\"epMUL:\", epMUL(BOOT_PROTOCOL));\n\n        if(bAddress)\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n\n        bInterval = 0;\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        if(!p->epinfo) {\n                USBTRACE(\"epinfo\\r\\n\");\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf);\n\n        if(!rcode)\n                len = (buf[0] > constBufSize) ? constBufSize : buf[0];\n\n        if(rcode) {\n                // Restore p->epinfo\n                p->epinfo = oldep_ptr;\n\n                goto FailGetDevDescr;\n        }\n\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from the device descriptor\n        epInfo[0].maxPktSize = (uint8_t)((USB_DEVICE_DESCRIPTOR*)buf)->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n                USBTRACE2(\"setAddr:\", rcode);\n                return rcode;\n        }\n        //delay(2); //per USB 2.0 sect.9.2.6.3\n\n        USBTRACE2(\"Addr:\", bAddress);\n\n        p->lowspeed = false;\n\n        p = addrPool.GetUsbDevicePtr(bAddress);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        if(len)\n                rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf);\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        num_of_conf = ((USB_DEVICE_DESCRIPTOR*)buf)->bNumConfigurations;\n\n        USBTRACE2(\"NC:\", num_of_conf);\n\n        // GCC will optimize unused stuff away.\n        if((BOOT_PROTOCOL & (HID_PROTOCOL_KEYBOARD | HID_PROTOCOL_MOUSE)) == (HID_PROTOCOL_KEYBOARD | HID_PROTOCOL_MOUSE)) {\n                USBTRACE(\"HID_PROTOCOL_KEYBOARD AND MOUSE\\r\\n\");\n                ConfigDescParser<\n                        USB_CLASS_HID,\n                        HID_BOOT_INTF_SUBCLASS,\n                        HID_PROTOCOL_KEYBOARD | HID_PROTOCOL_MOUSE,\n                        CP_MASK_COMPARE_ALL > confDescrParser(this);\n                confDescrParser.SetOR(); // Use the OR variant.\n                for(uint8_t i = 0; i < num_of_conf; i++) {\n                        pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);\n                        if(bNumEP == (uint8_t)(totalEndpoints(BOOT_PROTOCOL)))\n                                break;\n                }\n        } else {\n                // GCC will optimize unused stuff away.\n                if(BOOT_PROTOCOL & HID_PROTOCOL_KEYBOARD) {\n                        USBTRACE(\"HID_PROTOCOL_KEYBOARD\\r\\n\");\n                        for(uint8_t i = 0; i < num_of_conf; i++) {\n                                ConfigDescParser<\n                                        USB_CLASS_HID,\n                                        HID_BOOT_INTF_SUBCLASS,\n                                        HID_PROTOCOL_KEYBOARD,\n                                        CP_MASK_COMPARE_ALL> confDescrParserA(this);\n\n                                pUsb->getConfDescr(bAddress, 0, i, &confDescrParserA);\n                                if(bNumEP == (uint8_t)(totalEndpoints(BOOT_PROTOCOL)))\n                                        break;\n                        }\n                }\n\n                // GCC will optimize unused stuff away.\n                if(BOOT_PROTOCOL & HID_PROTOCOL_MOUSE) {\n                        USBTRACE(\"HID_PROTOCOL_MOUSE\\r\\n\");\n                        for(uint8_t i = 0; i < num_of_conf; i++) {\n                                ConfigDescParser<\n                                        USB_CLASS_HID,\n                                        HID_BOOT_INTF_SUBCLASS,\n                                        HID_PROTOCOL_MOUSE,\n                                        CP_MASK_COMPARE_ALL> confDescrParserB(this);\n\n                                pUsb->getConfDescr(bAddress, 0, i, &confDescrParserB);\n                                if(bNumEP == ((uint8_t)(totalEndpoints(BOOT_PROTOCOL))))\n                                        break;\n\n                        }\n                }\n        }\n        USBTRACE2(\"bNumEP:\", bNumEP);\n\n        if(bNumEP != (uint8_t)(totalEndpoints(BOOT_PROTOCOL))) {\n                rcode = USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n                goto Fail;\n        }\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);\n        //USBTRACE2(\"setEpInfoEntry returned \", rcode);\n        USBTRACE2(\"Cnf:\", bConfNum);\n\n        delay(1000);\n\n        // Set Configuration Value\n        rcode = pUsb->setConf(bAddress, 0, bConfNum);\n\n        if(rcode)\n                goto FailSetConfDescr;\n\n        delay(1000);\n\n        USBTRACE2(\"bIfaceNum:\", bIfaceNum);\n        USBTRACE2(\"bNumIface:\", bNumIface);\n\n        // Yes, mouse wants SetProtocol and SetIdle too!\n        for(uint8_t i = 0; i < epMUL(BOOT_PROTOCOL); i++) {\n                USBTRACE2(\"\\r\\nInterface:\", i);\n                rcode = SetProtocol(i, HID_BOOT_PROTOCOL);\n                if(rcode) goto FailSetProtocol;\n                USBTRACE2(\"PROTOCOL SET HID_BOOT rcode:\", rcode);\n                rcode = SetIdle(i, 0, 0);\n                USBTRACE2(\"SET_IDLE rcode:\", rcode);\n                // if(rcode) goto FailSetIdle; This can fail.\n                // Get the RPIPE and just throw it away.\n                SinkParser<USBReadParser, uint16_t, uint16_t> sink;\n                rcode = GetReportDescr(i, &sink);\n                USBTRACE2(\"RPIPE rcode:\", rcode);\n        }\n\n        // Get RPIPE and throw it away.\n\n        if(BOOT_PROTOCOL & HID_PROTOCOL_KEYBOARD) {\n                // Wake keyboard interface by twinkling up to 5 LEDs that are in the spec.\n                // kana, compose, scroll, caps, num\n                rcode = 0x20; // Reuse rcode.\n                while(rcode) {\n                        rcode >>= 1;\n                        // Ignore any error returned, we don't care if LED is not supported\n                        SetReport(0, 0, 2, 0, 1, &rcode); // Eventually becomes zero (All off)\n                        delay(25);\n                }\n        }\n        USBTRACE(\"BM configured\\r\\n\");\n\n        bPollEnable = true;\n        return 0;\n\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr();\n        goto Fail;\n#endif\n\n        //FailSetDevTblEntry:\n        //#ifdef DEBUG_USB_HOST\n        //        NotifyFailSetDevTblEntry();\n        //        goto Fail;\n        //#endif\n\n        //FailGetConfDescr:\n        //#ifdef DEBUG_USB_HOST\n        //        NotifyFailGetConfDescr();\n        //        goto Fail;\n        //#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n        goto Fail;\n#endif\n\nFailSetProtocol:\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"SetProto:\");\n        goto Fail;\n#endif\n\n        //FailSetIdle:\n        //#ifdef DEBUG_USB_HOST\n        //        USBTRACE(\"SetIdle:\");\n        //#endif\n\nFail:\n#ifdef DEBUG_USB_HOST\n        NotifyFail(rcode);\n#endif\n        Release();\n\n        return rcode;\n}\n\ntemplate <const uint8_t BOOT_PROTOCOL>\nvoid HIDBoot<BOOT_PROTOCOL>::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) {\n\n        // If the first configuration satisfies, the others are not considered.\n        //if(bNumEP > 1 && conf != bConfNum)\n        if(bNumEP == totalEndpoints(BOOT_PROTOCOL))\n                return;\n\n        bConfNum = conf;\n        bIfaceNum = iface;\n\n        if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80) {\n                if(pep->bInterval > bInterval) bInterval = pep->bInterval;\n\n                // Fill in the endpoint info structure\n                epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F);\n                epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize;\n                epInfo[bNumEP].epAttribs = 0;\n                epInfo[bNumEP].bmNakPower = USB_NAK_NOWAIT;\n                bNumEP++;\n\n        }\n}\n\ntemplate <const uint8_t BOOT_PROTOCOL>\nuint8_t HIDBoot<BOOT_PROTOCOL>::Release() {\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n\n        bConfNum = 0;\n        bIfaceNum = 0;\n        bNumEP = 1;\n        bAddress = 0;\n        qNextPollTime = 0;\n        bPollEnable = false;\n\n        return 0;\n}\n\ntemplate <const uint8_t BOOT_PROTOCOL>\nuint8_t HIDBoot<BOOT_PROTOCOL>::Poll() {\n        uint8_t rcode = 0;\n\n        if(bPollEnable && ((long)(millis() - qNextPollTime) >= 0L)) {\n\n                // To-do: optimize manually, using the for loop only if needed.\n                for(int i = 0; i < epMUL(BOOT_PROTOCOL); i++) {\n                        const uint16_t const_buff_len = 16;\n                        uint8_t buf[const_buff_len];\n\n                        USBTRACE3(\"(hidboot.h) i=\", i, 0x81);\n                        USBTRACE3(\"(hidboot.h) epInfo[epInterruptInIndex + i].epAddr=\", epInfo[epInterruptInIndex + i].epAddr, 0x81);\n                        USBTRACE3(\"(hidboot.h) epInfo[epInterruptInIndex + i].maxPktSize=\", epInfo[epInterruptInIndex + i].maxPktSize, 0x81);\n                        uint16_t read = (uint16_t)epInfo[epInterruptInIndex + i].maxPktSize;\n\n                        rcode = pUsb->inTransfer(bAddress, epInfo[epInterruptInIndex + i].epAddr, &read, buf);\n                        // SOME buggy dongles report extra keys (like sleep) using a 2 byte packet on the wrong endpoint.\n                        // Since keyboard and mice must report at least 3 bytes, we ignore the extra data.\n                        if(!rcode && read > 2) {\n                                if(pRptParser[i])\n                                        pRptParser[i]->Parse((HID*)this, 0, (uint8_t)read, buf);\n#ifdef DEBUG_USB_HOST\n                                // We really don't care about errors and anomalies unless we are debugging.\n                        } else {\n                                if(rcode != hrNAK) {\n                                        USBTRACE3(\"(hidboot.h) Poll:\", rcode, 0x81);\n                                }\n                                if(!rcode && read) {\n                                        USBTRACE3(\"(hidboot.h) Strange read count: \", read, 0x80);\n                                        USBTRACE3(\"(hidboot.h) Interface:\", i, 0x80);\n                                }\n                        }\n\n                        if(!rcode && read && (UsbDEBUGlvl > 0x7f)) {\n                                for(uint8_t i = 0; i < read; i++) {\n                                        PrintHex<uint8_t > (buf[i], 0x80);\n                                        USBTRACE1(\" \", 0x80);\n                                }\n                                if(read)\n                                        USBTRACE1(\"\\r\\n\", 0x80);\n#endif\n                        }\n\n                }\n                qNextPollTime = millis() + bInterval;\n        }\n        return rcode;\n}\n\n#endif // __HIDBOOTMOUSE_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/hidescriptorparser.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#include \"hidescriptorparser.h\"\n\nconst char * const ReportDescParserBase::usagePageTitles0[] PROGMEM = {\n        pstrUsagePageGenericDesktopControls,\n        pstrUsagePageSimulationControls,\n        pstrUsagePageVRControls,\n        pstrUsagePageSportControls,\n        pstrUsagePageGameControls,\n        pstrUsagePageGenericDeviceControls,\n        pstrUsagePageKeyboardKeypad,\n        pstrUsagePageLEDs,\n        pstrUsagePageButton,\n        pstrUsagePageOrdinal,\n        pstrUsagePageTelephone,\n        pstrUsagePageConsumer,\n        pstrUsagePageDigitizer,\n        pstrUsagePagePID,\n        pstrUsagePageUnicode\n};\n\nconst char * const ReportDescParserBase::usagePageTitles1[] PROGMEM = {\n        pstrUsagePageBarCodeScanner,\n        pstrUsagePageScale,\n        pstrUsagePageMSRDevices,\n        pstrUsagePagePointOfSale,\n        pstrUsagePageCameraControl,\n        pstrUsagePageArcade\n};\nconst char * const ReportDescParserBase::genDesktopTitles0[] PROGMEM = {\n        pstrUsagePointer,\n        pstrUsageMouse,\n        pstrUsageJoystick,\n        pstrUsageGamePad,\n        pstrUsageKeyboard,\n        pstrUsageKeypad,\n        pstrUsageMultiAxisController,\n        pstrUsageTabletPCSystemControls\n\n};\nconst char * const ReportDescParserBase::genDesktopTitles1[] PROGMEM = {\n        pstrUsageX,\n        pstrUsageY,\n        pstrUsageZ,\n        pstrUsageRx,\n        pstrUsageRy,\n        pstrUsageRz,\n        pstrUsageSlider,\n        pstrUsageDial,\n        pstrUsageWheel,\n        pstrUsageHatSwitch,\n        pstrUsageCountedBuffer,\n        pstrUsageByteCount,\n        pstrUsageMotionWakeup,\n        pstrUsageStart,\n        pstrUsageSelect,\n        pstrUsagePageReserved,\n        pstrUsageVx,\n        pstrUsageVy,\n        pstrUsageVz,\n        pstrUsageVbrx,\n        pstrUsageVbry,\n        pstrUsageVbrz,\n        pstrUsageVno,\n        pstrUsageFeatureNotification,\n        pstrUsageResolutionMultiplier\n};\nconst char * const ReportDescParserBase::genDesktopTitles2[] PROGMEM = {\n        pstrUsageSystemControl,\n        pstrUsageSystemPowerDown,\n        pstrUsageSystemSleep,\n        pstrUsageSystemWakeup,\n        pstrUsageSystemContextMenu,\n        pstrUsageSystemMainMenu,\n        pstrUsageSystemAppMenu,\n        pstrUsageSystemMenuHelp,\n        pstrUsageSystemMenuExit,\n        pstrUsageSystemMenuSelect,\n        pstrUsageSystemMenuRight,\n        pstrUsageSystemMenuLeft,\n        pstrUsageSystemMenuUp,\n        pstrUsageSystemMenuDown,\n        pstrUsageSystemColdRestart,\n        pstrUsageSystemWarmRestart,\n        pstrUsageDPadUp,\n        pstrUsageDPadDown,\n        pstrUsageDPadRight,\n        pstrUsageDPadLeft\n};\nconst char * const ReportDescParserBase::genDesktopTitles3[] PROGMEM = {\n        pstrUsageSystemDock,\n        pstrUsageSystemUndock,\n        pstrUsageSystemSetup,\n        pstrUsageSystemBreak,\n        pstrUsageSystemDebuggerBreak,\n        pstrUsageApplicationBreak,\n        pstrUsageApplicationDebuggerBreak,\n        pstrUsageSystemSpeakerMute,\n        pstrUsageSystemHibernate\n};\nconst char * const ReportDescParserBase::genDesktopTitles4[] PROGMEM = {\n        pstrUsageSystemDisplayInvert,\n        pstrUsageSystemDisplayInternal,\n        pstrUsageSystemDisplayExternal,\n        pstrUsageSystemDisplayBoth,\n        pstrUsageSystemDisplayDual,\n        pstrUsageSystemDisplayToggleIntExt,\n        pstrUsageSystemDisplaySwapPriSec,\n        pstrUsageSystemDisplayLCDAutoscale\n};\nconst char * const ReportDescParserBase::simuTitles0[] PROGMEM = {\n        pstrUsageFlightSimulationDevice,\n        pstrUsageAutomobileSimulationDevice,\n        pstrUsageTankSimulationDevice,\n        pstrUsageSpaceshipSimulationDevice,\n        pstrUsageSubmarineSimulationDevice,\n        pstrUsageSailingSimulationDevice,\n        pstrUsageMotocicleSimulationDevice,\n        pstrUsageSportsSimulationDevice,\n        pstrUsageAirplaneSimulationDevice,\n        pstrUsageHelicopterSimulationDevice,\n        pstrUsageMagicCarpetSimulationDevice,\n        pstrUsageBicycleSimulationDevice\n};\nconst char * const ReportDescParserBase::simuTitles1[] PROGMEM = {\n        pstrUsageFlightControlStick,\n        pstrUsageFlightStick,\n        pstrUsageCyclicControl,\n        pstrUsageCyclicTrim,\n        pstrUsageFlightYoke,\n        pstrUsageTrackControl\n};\nconst char * const ReportDescParserBase::simuTitles2[] PROGMEM = {\n        pstrUsageAileron,\n        pstrUsageAileronTrim,\n        pstrUsageAntiTorqueControl,\n        pstrUsageAutopilotEnable,\n        pstrUsageChaffRelease,\n        pstrUsageCollectiveControl,\n        pstrUsageDiveBrake,\n        pstrUsageElectronicCountermeasures,\n        pstrUsageElevator,\n        pstrUsageElevatorTrim,\n        pstrUsageRudder,\n        pstrUsageThrottle,\n        pstrUsageFlightCommunications,\n        pstrUsageFlareRelease,\n        pstrUsageLandingGear,\n        pstrUsageToeBrake,\n        pstrUsageTrigger,\n        pstrUsageWeaponsArm,\n        pstrUsageWeaponsSelect,\n        pstrUsageWingFlaps,\n        pstrUsageAccelerator,\n        pstrUsageBrake,\n        pstrUsageClutch,\n        pstrUsageShifter,\n        pstrUsageSteering,\n        pstrUsageTurretDirection,\n        pstrUsageBarrelElevation,\n        pstrUsageDivePlane,\n        pstrUsageBallast,\n        pstrUsageBicycleCrank,\n        pstrUsageHandleBars,\n        pstrUsageFrontBrake,\n        pstrUsageRearBrake\n};\nconst char * const ReportDescParserBase::vrTitles0[] PROGMEM = {\n        pstrUsageBelt,\n        pstrUsageBodySuit,\n        pstrUsageFlexor,\n        pstrUsageGlove,\n        pstrUsageHeadTracker,\n        pstrUsageHeadMountedDisplay,\n        pstrUsageHandTracker,\n        pstrUsageOculometer,\n        pstrUsageVest,\n        pstrUsageAnimatronicDevice\n};\nconst char * const ReportDescParserBase::vrTitles1[] PROGMEM = {\n        pstrUsageStereoEnable,\n        pstrUsageDisplayEnable\n};\nconst char * const ReportDescParserBase::sportsCtrlTitles0[] PROGMEM = {\n        pstrUsageBaseballBat,\n        pstrUsageGolfClub,\n        pstrUsageRowingMachine,\n        pstrUsageTreadmill\n};\nconst char * const ReportDescParserBase::sportsCtrlTitles1[] PROGMEM = {\n        pstrUsageOar,\n        pstrUsageSlope,\n        pstrUsageRate,\n        pstrUsageStickSpeed,\n        pstrUsageStickFaceAngle,\n        pstrUsageStickHeelToe,\n        pstrUsageStickFollowThough,\n        pstrUsageStickTempo,\n        pstrUsageStickType,\n        pstrUsageStickHeight\n};\nconst char * const ReportDescParserBase::sportsCtrlTitles2[] PROGMEM = {\n        pstrUsagePutter,\n        pstrUsage1Iron,\n        pstrUsage2Iron,\n        pstrUsage3Iron,\n        pstrUsage4Iron,\n        pstrUsage5Iron,\n        pstrUsage6Iron,\n        pstrUsage7Iron,\n        pstrUsage8Iron,\n        pstrUsage9Iron,\n        pstrUsage10Iron,\n        pstrUsage11Iron,\n        pstrUsageSandWedge,\n        pstrUsageLoftWedge,\n        pstrUsagePowerWedge,\n        pstrUsage1Wood,\n        pstrUsage3Wood,\n        pstrUsage5Wood,\n        pstrUsage7Wood,\n        pstrUsage9Wood\n};\nconst char * const ReportDescParserBase::gameTitles0[] PROGMEM = {\n        pstrUsage3DGameController,\n        pstrUsagePinballDevice,\n        pstrUsageGunDevice\n};\nconst char * const ReportDescParserBase::gameTitles1[] PROGMEM = {\n        pstrUsagePointOfView,\n        pstrUsageTurnRightLeft,\n        pstrUsagePitchForwardBackward,\n        pstrUsageRollRightLeft,\n        pstrUsageMoveRightLeft,\n        pstrUsageMoveForwardBackward,\n        pstrUsageMoveUpDown,\n        pstrUsageLeanRightLeft,\n        pstrUsageLeanForwardBackward,\n        pstrUsageHeightOfPOV,\n        pstrUsageFlipper,\n        pstrUsageSecondaryFlipper,\n        pstrUsageBump,\n        pstrUsageNewGame,\n        pstrUsageShootBall,\n        pstrUsagePlayer,\n        pstrUsageGunBolt,\n        pstrUsageGunClip,\n        pstrUsageGunSelector,\n        pstrUsageGunSingleShot,\n        pstrUsageGunBurst,\n        pstrUsageGunAutomatic,\n        pstrUsageGunSafety,\n        pstrUsageGamepadFireJump,\n        pstrUsageGamepadTrigger\n};\nconst char * const ReportDescParserBase::genDevCtrlTitles[] PROGMEM = {\n        pstrUsageBatteryStrength,\n        pstrUsageWirelessChannel,\n        pstrUsageWirelessID,\n        pstrUsageDiscoverWirelessControl,\n        pstrUsageSecurityCodeCharEntered,\n        pstrUsageSecurityCodeCharErased,\n        pstrUsageSecurityCodeCleared\n};\nconst char * const ReportDescParserBase::ledTitles[] PROGMEM = {\n        pstrUsageNumLock,\n        pstrUsageCapsLock,\n        pstrUsageScrollLock,\n        pstrUsageCompose,\n        pstrUsageKana,\n        pstrUsagePower,\n        pstrUsageShift,\n        pstrUsageDoNotDisturb,\n        pstrUsageMute,\n        pstrUsageToneEnable,\n        pstrUsageHighCutFilter,\n        pstrUsageLowCutFilter,\n        pstrUsageEqualizerEnable,\n        pstrUsageSoundFieldOn,\n        pstrUsageSurroundOn,\n        pstrUsageRepeat,\n        pstrUsageStereo,\n        pstrUsageSamplingRateDetect,\n        pstrUsageSpinning,\n        pstrUsageCAV,\n        pstrUsageCLV,\n        pstrUsageRecordingFormatDetect,\n        pstrUsageOffHook,\n        pstrUsageRing,\n        pstrUsageMessageWaiting,\n        pstrUsageDataMode,\n        pstrUsageBatteryOperation,\n        pstrUsageBatteryOK,\n        pstrUsageBatteryLow,\n        pstrUsageSpeaker,\n        pstrUsageHeadSet,\n        pstrUsageHold,\n        pstrUsageMicrophone,\n        pstrUsageCoverage,\n        pstrUsageNightMode,\n        pstrUsageSendCalls,\n        pstrUsageCallPickup,\n        pstrUsageConference,\n        pstrUsageStandBy,\n        pstrUsageCameraOn,\n        pstrUsageCameraOff,\n        pstrUsageOnLine,\n        pstrUsageOffLine,\n        pstrUsageBusy,\n        pstrUsageReady,\n        pstrUsagePaperOut,\n        pstrUsagePaperJam,\n        pstrUsageRemote,\n        pstrUsageForward,\n        pstrUsageReverse,\n        pstrUsageStop,\n        pstrUsageRewind,\n        pstrUsageFastForward,\n        pstrUsagePlay,\n        pstrUsagePause,\n        pstrUsageRecord,\n        pstrUsageError,\n        pstrUsageSelectedIndicator,\n        pstrUsageInUseIndicator,\n        pstrUsageMultiModeIndicator,\n        pstrUsageIndicatorOn,\n        pstrUsageIndicatorFlash,\n        pstrUsageIndicatorSlowBlink,\n        pstrUsageIndicatorFastBlink,\n        pstrUsageIndicatorOff,\n        pstrUsageFlashOnTime,\n        pstrUsageSlowBlinkOnTime,\n        pstrUsageSlowBlinkOffTime,\n        pstrUsageFastBlinkOnTime,\n        pstrUsageFastBlinkOffTime,\n        pstrUsageIndicatorColor,\n        pstrUsageIndicatorRed,\n        pstrUsageIndicatorGreen,\n        pstrUsageIndicatorAmber,\n        pstrUsageGenericIndicator,\n        pstrUsageSystemSuspend,\n        pstrUsageExternalPowerConnected\n};\nconst char * const ReportDescParserBase::telTitles0 [] PROGMEM = {\n        pstrUsagePhone,\n        pstrUsageAnsweringMachine,\n        pstrUsageMessageControls,\n        pstrUsageHandset,\n        pstrUsageHeadset,\n        pstrUsageTelephonyKeyPad,\n        pstrUsageProgrammableButton\n};\nconst char * const ReportDescParserBase::telTitles1 [] PROGMEM = {\n        pstrUsageHookSwitch,\n        pstrUsageFlash,\n        pstrUsageFeature,\n        pstrUsageHold,\n        pstrUsageRedial,\n        pstrUsageTransfer,\n        pstrUsageDrop,\n        pstrUsagePark,\n        pstrUsageForwardCalls,\n        pstrUsageAlternateFunction,\n        pstrUsageLine,\n        pstrUsageSpeakerPhone,\n        pstrUsageConference,\n        pstrUsageRingEnable,\n        pstrUsageRingSelect,\n        pstrUsagePhoneMute,\n        pstrUsageCallerID,\n        pstrUsageSend\n};\nconst char * const ReportDescParserBase::telTitles2 [] PROGMEM = {\n        pstrUsageSpeedDial,\n        pstrUsageStoreNumber,\n        pstrUsageRecallNumber,\n        pstrUsagePhoneDirectory\n};\nconst char * const ReportDescParserBase::telTitles3 [] PROGMEM = {\n        pstrUsageVoiceMail,\n        pstrUsageScreenCalls,\n        pstrUsageDoNotDisturb,\n        pstrUsageMessage,\n        pstrUsageAnswerOnOff\n};\nconst char * const ReportDescParserBase::telTitles4 [] PROGMEM = {\n        pstrUsageInsideDialTone,\n        pstrUsageOutsideDialTone,\n        pstrUsageInsideRingTone,\n        pstrUsageOutsideRingTone,\n        pstrUsagePriorityRingTone,\n        pstrUsageInsideRingback,\n        pstrUsagePriorityRingback,\n        pstrUsageLineBusyTone,\n        pstrUsageReorderTone,\n        pstrUsageCallWaitingTone,\n        pstrUsageConfirmationTone1,\n        pstrUsageConfirmationTone2,\n        pstrUsageTonesOff,\n        pstrUsageOutsideRingback,\n        pstrUsageRinger\n};\nconst char * const ReportDescParserBase::telTitles5 [] PROGMEM = {\n        pstrUsagePhoneKey0,\n        pstrUsagePhoneKey1,\n        pstrUsagePhoneKey2,\n        pstrUsagePhoneKey3,\n        pstrUsagePhoneKey4,\n        pstrUsagePhoneKey5,\n        pstrUsagePhoneKey6,\n        pstrUsagePhoneKey7,\n        pstrUsagePhoneKey8,\n        pstrUsagePhoneKey9,\n        pstrUsagePhoneKeyStar,\n        pstrUsagePhoneKeyPound,\n        pstrUsagePhoneKeyA,\n        pstrUsagePhoneKeyB,\n        pstrUsagePhoneKeyC,\n        pstrUsagePhoneKeyD\n};\nconst char * const ReportDescParserBase::consTitles0[] PROGMEM = {\n        pstrUsageConsumerControl,\n        pstrUsageNumericKeyPad,\n        pstrUsageProgrammableButton,\n        pstrUsageMicrophone,\n        pstrUsageHeadphone,\n        pstrUsageGraphicEqualizer\n};\nconst char * const ReportDescParserBase::consTitles1[] PROGMEM = {\n        pstrUsagePlus10,\n        pstrUsagePlus100,\n        pstrUsageAMPM\n};\nconst char * const ReportDescParserBase::consTitles2[] PROGMEM = {\n        pstrUsagePower,\n        pstrUsageReset,\n        pstrUsageSleep,\n        pstrUsageSleepAfter,\n        pstrUsageSleepMode,\n        pstrUsageIllumination,\n        pstrUsageFunctionButtons\n\n};\nconst char * const ReportDescParserBase::consTitles3[] PROGMEM = {\n        pstrUsageMenu,\n        pstrUsageMenuPick,\n        pstrUsageMenuUp,\n        pstrUsageMenuDown,\n        pstrUsageMenuLeft,\n        pstrUsageMenuRight,\n        pstrUsageMenuEscape,\n        pstrUsageMenuValueIncrease,\n        pstrUsageMenuValueDecrease\n};\nconst char * const ReportDescParserBase::consTitles4[] PROGMEM = {\n        pstrUsageDataOnScreen,\n        pstrUsageClosedCaption,\n        pstrUsageClosedCaptionSelect,\n        pstrUsageVCRTV,\n        pstrUsageBroadcastMode,\n        pstrUsageSnapshot,\n        pstrUsageStill\n};\nconst char * const ReportDescParserBase::consTitles5[] PROGMEM = {\n        pstrUsageSelection,\n        pstrUsageAssignSelection,\n        pstrUsageModeStep,\n        pstrUsageRecallLast,\n        pstrUsageEnterChannel,\n        pstrUsageOrderMovie,\n        pstrUsageChannel,\n        pstrUsageMediaSelection,\n        pstrUsageMediaSelectComputer,\n        pstrUsageMediaSelectTV,\n        pstrUsageMediaSelectWWW,\n        pstrUsageMediaSelectDVD,\n        pstrUsageMediaSelectTelephone,\n        pstrUsageMediaSelectProgramGuide,\n        pstrUsageMediaSelectVideoPhone,\n        pstrUsageMediaSelectGames,\n        pstrUsageMediaSelectMessages,\n        pstrUsageMediaSelectCD,\n        pstrUsageMediaSelectVCR,\n        pstrUsageMediaSelectTuner,\n        pstrUsageQuit,\n        pstrUsageHelp,\n        pstrUsageMediaSelectTape,\n        pstrUsageMediaSelectCable,\n        pstrUsageMediaSelectSatellite,\n        pstrUsageMediaSelectSecurity,\n        pstrUsageMediaSelectHome,\n        pstrUsageMediaSelectCall,\n        pstrUsageChannelIncrement,\n        pstrUsageChannelDecrement,\n        pstrUsageMediaSelectSAP,\n        pstrUsagePageReserved,\n        pstrUsageVCRPlus,\n        pstrUsageOnce,\n        pstrUsageDaily,\n        pstrUsageWeekly,\n        pstrUsageMonthly\n};\nconst char * const ReportDescParserBase::consTitles6[] PROGMEM = {\n        pstrUsagePlay,\n        pstrUsagePause,\n        pstrUsageRecord,\n        pstrUsageFastForward,\n        pstrUsageRewind,\n        pstrUsageScanNextTrack,\n        pstrUsageScanPreviousTrack,\n        pstrUsageStop,\n        pstrUsageEject,\n        pstrUsageRandomPlay,\n        pstrUsageSelectDisk,\n        pstrUsageEnterDisk,\n        pstrUsageRepeat,\n        pstrUsageTracking,\n        pstrUsageTrackNormal,\n        pstrUsageSlowTracking,\n        pstrUsageFrameForward,\n        pstrUsageFrameBackwards,\n        pstrUsageMark,\n        pstrUsageClearMark,\n        pstrUsageRepeatFromMark,\n        pstrUsageReturnToMark,\n        pstrUsageSearchMarkForward,\n        pstrUsageSearchMarkBackwards,\n        pstrUsageCounterReset,\n        pstrUsageShowCounter,\n        pstrUsageTrackingIncrement,\n        pstrUsageTrackingDecrement,\n        pstrUsageStopEject,\n        pstrUsagePlayPause,\n        pstrUsagePlaySkip\n};\nconst char * const ReportDescParserBase::consTitles7[] PROGMEM = {\n        pstrUsageVolume,\n        pstrUsageBalance,\n        pstrUsageMute,\n        pstrUsageBass,\n        pstrUsageTreble,\n        pstrUsageBassBoost,\n        pstrUsageSurroundMode,\n        pstrUsageLoudness,\n        pstrUsageMPX,\n        pstrUsageVolumeIncrement,\n        pstrUsageVolumeDecrement\n};\nconst char * const ReportDescParserBase::consTitles8[] PROGMEM = {\n        pstrUsageSpeedSelect,\n        pstrUsagePlaybackSpeed,\n        pstrUsageStandardPlay,\n        pstrUsageLongPlay,\n        pstrUsageExtendedPlay,\n        pstrUsageSlow\n};\nconst char * const ReportDescParserBase::consTitles9[] PROGMEM = {\n        pstrUsageFanEnable,\n        pstrUsageFanSpeed,\n        pstrUsageLightEnable,\n        pstrUsageLightIlluminationLevel,\n        pstrUsageClimateControlEnable,\n        pstrUsageRoomTemperature,\n        pstrUsageSecurityEnable,\n        pstrUsageFireAlarm,\n        pstrUsagePoliceAlarm,\n        pstrUsageProximity,\n        pstrUsageMotion,\n        pstrUsageDuresAlarm,\n        pstrUsageHoldupAlarm,\n        pstrUsageMedicalAlarm\n};\nconst char * const ReportDescParserBase::consTitlesA[] PROGMEM = {\n        pstrUsageBalanceRight,\n        pstrUsageBalanceLeft,\n        pstrUsageBassIncrement,\n        pstrUsageBassDecrement,\n        pstrUsageTrebleIncrement,\n        pstrUsageTrebleDecrement\n};\nconst char * const ReportDescParserBase::consTitlesB[] PROGMEM = {\n        pstrUsageSpeakerSystem,\n        pstrUsageChannelLeft,\n        pstrUsageChannelRight,\n        pstrUsageChannelCenter,\n        pstrUsageChannelFront,\n        pstrUsageChannelCenterFront,\n        pstrUsageChannelSide,\n        pstrUsageChannelSurround,\n        pstrUsageChannelLowFreqEnhancement,\n        pstrUsageChannelTop,\n        pstrUsageChannelUnknown\n};\nconst char * const ReportDescParserBase::consTitlesC[] PROGMEM = {\n        pstrUsageSubChannel,\n        pstrUsageSubChannelIncrement,\n        pstrUsageSubChannelDecrement,\n        pstrUsageAlternateAudioIncrement,\n        pstrUsageAlternateAudioDecrement\n};\nconst char * const ReportDescParserBase::consTitlesD[] PROGMEM = {\n        pstrUsageApplicationLaunchButtons,\n        pstrUsageALLaunchButtonConfigTool,\n        pstrUsageALProgrammableButton,\n        pstrUsageALConsumerControlConfig,\n        pstrUsageALWordProcessor,\n        pstrUsageALTextEditor,\n        pstrUsageALSpreadsheet,\n        pstrUsageALGraphicsEditor,\n        pstrUsageALPresentationApp,\n        pstrUsageALDatabaseApp,\n        pstrUsageALEmailReader,\n        pstrUsageALNewsreader,\n        pstrUsageALVoicemail,\n        pstrUsageALContactsAddressBook,\n        pstrUsageALCalendarSchedule,\n        pstrUsageALTaskProjectManager,\n        pstrUsageALLogJournalTimecard,\n        pstrUsageALCheckbookFinance,\n        pstrUsageALCalculator,\n        pstrUsageALAVCapturePlayback,\n        pstrUsageALLocalMachineBrowser,\n        pstrUsageALLANWANBrow,\n        pstrUsageALInternetBrowser,\n        pstrUsageALRemoteNetISPConnect,\n        pstrUsageALNetworkConference,\n        pstrUsageALNetworkChat,\n        pstrUsageALTelephonyDialer,\n        pstrUsageALLogon,\n        pstrUsageALLogoff,\n        pstrUsageALLogonLogoff,\n        pstrUsageALTermLockScrSav,\n        pstrUsageALControlPannel,\n        pstrUsageALCommandLineProcessorRun,\n        pstrUsageALProcessTaskManager,\n        pstrUsageALSelectTaskApplication,\n        pstrUsageALNextTaskApplication,\n        pstrUsageALPreviousTaskApplication,\n        pstrUsageALPreemptiveHaltTaskApp,\n        pstrUsageALIntegratedHelpCenter,\n        pstrUsageALDocuments,\n        pstrUsageALThesaurus,\n        pstrUsageALDictionary,\n        pstrUsageALDesktop,\n        pstrUsageALSpellCheck,\n        pstrUsageALGrammarCheck,\n        pstrUsageALWirelessStatus,\n        pstrUsageALKeyboardLayout,\n        pstrUsageALVirusProtection,\n        pstrUsageALEncryption,\n        pstrUsageALScreenSaver,\n        pstrUsageALAlarms,\n        pstrUsageALClock,\n        pstrUsageALFileBrowser,\n        pstrUsageALPowerStatus,\n        pstrUsageALImageBrowser,\n        pstrUsageALAudioBrowser,\n        pstrUsageALMovieBrowser,\n        pstrUsageALDigitalRightsManager,\n        pstrUsageALDigitalWallet,\n        pstrUsagePageReserved,\n        pstrUsageALInstantMessaging,\n        pstrUsageALOEMFeaturesBrowser,\n        pstrUsageALOEMHelp,\n        pstrUsageALOnlineCommunity,\n        pstrUsageALEntertainmentContentBrow,\n        pstrUsageALOnlineShoppingBrowser,\n        pstrUsageALSmartCardInfoHelp,\n        pstrUsageALMarketMonitorFinBrowser,\n        pstrUsageALCustomCorpNewsBrowser,\n        pstrUsageALOnlineActivityBrowser,\n        pstrUsageALResearchSearchBrowser,\n        pstrUsageALAudioPlayer\n};\nconst char * const ReportDescParserBase::consTitlesE[] PROGMEM = {\n        pstrUsageGenericGUIAppControls,\n        pstrUsageACNew,\n        pstrUsageACOpen,\n        pstrUsageACClose,\n        pstrUsageACExit,\n        pstrUsageACMaximize,\n        pstrUsageACMinimize,\n        pstrUsageACSave,\n        pstrUsageACPrint,\n        pstrUsageACProperties,\n        pstrUsageACUndo,\n        pstrUsageACCopy,\n        pstrUsageACCut,\n        pstrUsageACPaste,\n        pstrUsageACSelectAll,\n        pstrUsageACFind,\n        pstrUsageACFindAndReplace,\n        pstrUsageACSearch,\n        pstrUsageACGoto,\n        pstrUsageACHome,\n        pstrUsageACBack,\n        pstrUsageACForward,\n        pstrUsageACStop,\n        pstrUsageACRefresh,\n        pstrUsageACPreviousLink,\n        pstrUsageACNextLink,\n        pstrUsageACBookmarks,\n        pstrUsageACHistory,\n        pstrUsageACSubscriptions,\n        pstrUsageACZoomIn,\n        pstrUsageACZoomOut,\n        pstrUsageACZoom,\n        pstrUsageACFullScreenView,\n        pstrUsageACNormalView,\n        pstrUsageACViewToggle,\n        pstrUsageACScrollUp,\n        pstrUsageACScrollDown,\n        pstrUsageACScroll,\n        pstrUsageACPanLeft,\n        pstrUsageACPanRight,\n        pstrUsageACPan,\n        pstrUsageACNewWindow,\n        pstrUsageACTileHoriz,\n        pstrUsageACTileVert,\n        pstrUsageACFormat,\n        pstrUsageACEdit,\n        pstrUsageACBold,\n        pstrUsageACItalics,\n        pstrUsageACUnderline,\n        pstrUsageACStrikethrough,\n        pstrUsageACSubscript,\n        pstrUsageACSuperscript,\n        pstrUsageACAllCaps,\n        pstrUsageACRotate,\n        pstrUsageACResize,\n        pstrUsageACFlipHorizontal,\n        pstrUsageACFlipVertical,\n        pstrUsageACMirrorHorizontal,\n        pstrUsageACMirrorVertical,\n        pstrUsageACFontSelect,\n        pstrUsageACFontColor,\n        pstrUsageACFontSize,\n        pstrUsageACJustifyLeft,\n        pstrUsageACJustifyCenterH,\n        pstrUsageACJustifyRight,\n        pstrUsageACJustifyBlockH,\n        pstrUsageACJustifyTop,\n        pstrUsageACJustifyCenterV,\n        pstrUsageACJustifyBottom,\n        pstrUsageACJustifyBlockV,\n        pstrUsageACIndentDecrease,\n        pstrUsageACIndentIncrease,\n        pstrUsageACNumberedList,\n        pstrUsageACRestartNumbering,\n        pstrUsageACBulletedList,\n        pstrUsageACPromote,\n        pstrUsageACDemote,\n        pstrUsageACYes,\n        pstrUsageACNo,\n        pstrUsageACCancel,\n        pstrUsageACCatalog,\n        pstrUsageACBuyChkout,\n        pstrUsageACAddToCart,\n        pstrUsageACExpand,\n        pstrUsageACExpandAll,\n        pstrUsageACCollapse,\n        pstrUsageACCollapseAll,\n        pstrUsageACPrintPreview,\n        pstrUsageACPasteSpecial,\n        pstrUsageACInsertMode,\n        pstrUsageACDelete,\n        pstrUsageACLock,\n        pstrUsageACUnlock,\n        pstrUsageACProtect,\n        pstrUsageACUnprotect,\n        pstrUsageACAttachComment,\n        pstrUsageACDeleteComment,\n        pstrUsageACViewComment,\n        pstrUsageACSelectWord,\n        pstrUsageACSelectSentence,\n        pstrUsageACSelectParagraph,\n        pstrUsageACSelectColumn,\n        pstrUsageACSelectRow,\n        pstrUsageACSelectTable,\n        pstrUsageACSelectObject,\n        pstrUsageACRedoRepeat,\n        pstrUsageACSort,\n        pstrUsageACSortAscending,\n        pstrUsageACSortDescending,\n        pstrUsageACFilter,\n        pstrUsageACSetClock,\n        pstrUsageACViewClock,\n        pstrUsageACSelectTimeZone,\n        pstrUsageACEditTimeZone,\n        pstrUsageACSetAlarm,\n        pstrUsageACClearAlarm,\n        pstrUsageACSnoozeAlarm,\n        pstrUsageACResetAlarm,\n        pstrUsageACSyncronize,\n        pstrUsageACSendReceive,\n        pstrUsageACSendTo,\n        pstrUsageACReply,\n        pstrUsageACReplyAll,\n        pstrUsageACForwardMessage,\n        pstrUsageACSend,\n        pstrUsageACAttachFile,\n        pstrUsageACUpload,\n        pstrUsageACDownload,\n        pstrUsageACSetBorders,\n        pstrUsageACInsertRow,\n        pstrUsageACInsertColumn,\n        pstrUsageACInsertFile,\n        pstrUsageACInsertPicture,\n        pstrUsageACInsertObject,\n        pstrUsageACInsertSymbol,\n        pstrUsageACSaveAndClose,\n        pstrUsageACRename,\n        pstrUsageACMerge,\n        pstrUsageACSplit,\n        pstrUsageACDistributeHorizontaly,\n        pstrUsageACDistributeVerticaly\n};\nconst char * const ReportDescParserBase::digitTitles0[] PROGMEM = {\n        pstrUsageDigitizer,\n        pstrUsagePen,\n        pstrUsageLightPen,\n        pstrUsageTouchScreen,\n        pstrUsageTouchPad,\n        pstrUsageWhiteBoard,\n        pstrUsageCoordinateMeasuringMachine,\n        pstrUsage3DDigitizer,\n        pstrUsageStereoPlotter,\n        pstrUsageArticulatedArm,\n        pstrUsageArmature,\n        pstrUsageMultiplePointDigitizer,\n        pstrUsageFreeSpaceWand\n};\nconst char * const ReportDescParserBase::digitTitles1[] PROGMEM = {\n        pstrUsageStylus,\n        pstrUsagePuck,\n        pstrUsageFinger\n\n};\nconst char * const ReportDescParserBase::digitTitles2[] PROGMEM = {\n        pstrUsageTipPressure,\n        pstrUsageBarrelPressure,\n        pstrUsageInRange,\n        pstrUsageTouch,\n        pstrUsageUntouch,\n        pstrUsageTap,\n        pstrUsageQuality,\n        pstrUsageDataValid,\n        pstrUsageTransducerIndex,\n        pstrUsageTabletFunctionKeys,\n        pstrUsageProgramChangeKeys,\n        pstrUsageBatteryStrength,\n        pstrUsageInvert,\n        pstrUsageXTilt,\n        pstrUsageYTilt,\n        pstrUsageAzimuth,\n        pstrUsageAltitude,\n        pstrUsageTwist,\n        pstrUsageTipSwitch,\n        pstrUsageSecondaryTipSwitch,\n        pstrUsageBarrelSwitch,\n        pstrUsageEraser,\n        pstrUsageTabletPick\n};\nconst char * const ReportDescParserBase::aplphanumTitles0[] PROGMEM = {\n        pstrUsageAlphanumericDisplay,\n        pstrUsageBitmappedDisplay\n};\nconst char * const ReportDescParserBase::aplphanumTitles1[] PROGMEM = {\n        pstrUsageDisplayAttributesReport,\n        pstrUsageASCIICharacterSet,\n        pstrUsageDataReadBack,\n        pstrUsageFontReadBack,\n        pstrUsageDisplayControlReport,\n        pstrUsageClearDisplay,\n        pstrUsageDisplayEnable,\n        pstrUsageScreenSaverDelay,\n        pstrUsageScreenSaverEnable,\n        pstrUsageVerticalScroll,\n        pstrUsageHorizontalScroll,\n        pstrUsageCharacterReport,\n        pstrUsageDisplayData,\n        pstrUsageDisplayStatus,\n        pstrUsageStatusNotReady,\n        pstrUsageStatusReady,\n        pstrUsageErrorNotALoadableCharacter,\n        pstrUsageErrorFotDataCanNotBeRead,\n        pstrUsageCursorPositionReport,\n        pstrUsageRow,\n        pstrUsageColumn,\n        pstrUsageRows,\n        pstrUsageColumns,\n        pstrUsageCursorPixelPosition,\n        pstrUsageCursorMode,\n        pstrUsageCursorEnable,\n        pstrUsageCursorBlink,\n        pstrUsageFontReport,\n        pstrUsageFontData,\n        pstrUsageCharacterWidth,\n        pstrUsageCharacterHeight,\n        pstrUsageCharacterSpacingHorizontal,\n        pstrUsageCharacterSpacingVertical,\n        pstrUsageUnicodeCharset,\n        pstrUsageFont7Segment,\n        pstrUsage7SegmentDirectMap,\n        pstrUsageFont14Segment,\n        pstrUsage14SegmentDirectMap,\n        pstrUsageDisplayBrightness,\n        pstrUsageDisplayContrast,\n        pstrUsageCharacterAttribute,\n        pstrUsageAttributeReadback,\n        pstrUsageAttributeData,\n        pstrUsageCharAttributeEnhance,\n        pstrUsageCharAttributeUnderline,\n        pstrUsageCharAttributeBlink\n};\nconst char * const ReportDescParserBase::aplphanumTitles2[] PROGMEM = {\n        pstrUsageBitmapSizeX,\n        pstrUsageBitmapSizeY,\n        pstrUsagePageReserved,\n        pstrUsageBitDepthFormat,\n        pstrUsageDisplayOrientation,\n        pstrUsagePaletteReport,\n        pstrUsagePaletteDataSize,\n        pstrUsagePaletteDataOffset,\n        pstrUsagePaletteData,\n        pstrUsageBlitReport,\n        pstrUsageBlitRectangleX1,\n        pstrUsageBlitRectangleY1,\n        pstrUsageBlitRectangleX2,\n        pstrUsageBlitRectangleY2,\n        pstrUsageBlitData,\n        pstrUsageSoftButton,\n        pstrUsageSoftButtonID,\n        pstrUsageSoftButtonSide,\n        pstrUsageSoftButtonOffset1,\n        pstrUsageSoftButtonOffset2,\n        pstrUsageSoftButtonReport\n};\nconst char * const ReportDescParserBase::medInstrTitles0[] PROGMEM = {\n        pstrUsageVCRAcquisition,\n        pstrUsageFreezeThaw,\n        pstrUsageClipStore,\n        pstrUsageUpdate,\n        pstrUsageNext,\n        pstrUsageSave,\n        pstrUsagePrint,\n        pstrUsageMicrophoneEnable\n};\nconst char * const ReportDescParserBase::medInstrTitles1[] PROGMEM = {\n        pstrUsageCine,\n        pstrUsageTransmitPower,\n        pstrUsageVolume,\n        pstrUsageFocus,\n        pstrUsageDepth\n};\nconst char * const ReportDescParserBase::medInstrTitles2[] PROGMEM = {\n        pstrUsageSoftStepPrimary,\n        pstrUsageSoftStepSecondary\n};\nconst char * const ReportDescParserBase::medInstrTitles3[] PROGMEM = {\n        pstrUsageZoomSelect,\n        pstrUsageZoomAdjust,\n        pstrUsageSpectralDopplerModeSelect,\n        pstrUsageSpectralDopplerModeAdjust,\n        pstrUsageColorDopplerModeSelect,\n        pstrUsageColorDopplerModeAdjust,\n        pstrUsageMotionModeSelect,\n        pstrUsageMotionModeAdjust,\n        pstrUsage2DModeSelect,\n        pstrUsage2DModeAdjust\n};\nconst char * const ReportDescParserBase::medInstrTitles4[] PROGMEM = {\n        pstrUsageSoftControlSelect,\n        pstrUsageSoftControlAdjust\n};\n\nvoid ReportDescParserBase::Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset) {\n        uint16_t cntdn = (uint16_t)len;\n        uint8_t *p = (uint8_t*)pbuf;\n\n\n        totalSize = 0;\n\n        while(cntdn) {\n                //USB_HOST_SERIAL.println(\"\");\n                //PrintHex<uint16_t>(offset + len - cntdn);\n                //USB_HOST_SERIAL.print(\":\");\n\n                ParseItem(&p, &cntdn);\n\n                //if (ParseItem(&p, &cntdn))\n                //        return;\n        }\n        //USBTRACE2(\"Total:\", totalSize);\n}\n\nvoid ReportDescParserBase::PrintValue(uint8_t *p, uint8_t len) {\n        E_Notify(PSTR(\"(\"), 0x80);\n        for(; len; p++, len--)\n                PrintHex<uint8_t > (*p, 0x80);\n        E_Notify(PSTR(\")\"), 0x80);\n}\n\nvoid ReportDescParserBase::PrintByteValue(uint8_t data) {\n        E_Notify(PSTR(\"(\"), 0x80);\n        PrintHex<uint8_t > (data, 0x80);\n        E_Notify(PSTR(\")\"), 0x80);\n}\n\nvoid ReportDescParserBase::PrintItemTitle(uint8_t prefix) {\n        switch(prefix & (TYPE_MASK | TAG_MASK)) {\n                case (TYPE_GLOBAL | TAG_GLOBAL_PUSH):\n                        E_Notify(PSTR(\"\\r\\nPush\"), 0x80);\n                        break;\n                case (TYPE_GLOBAL | TAG_GLOBAL_POP):\n                        E_Notify(PSTR(\"\\r\\nPop\"), 0x80);\n                        break;\n                case (TYPE_GLOBAL | TAG_GLOBAL_USAGEPAGE):\n                        E_Notify(PSTR(\"\\r\\nUsage Page\"), 0x80);\n                        break;\n                case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMIN):\n                        E_Notify(PSTR(\"\\r\\nLogical Min\"), 0x80);\n                        break;\n                case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMAX):\n                        E_Notify(PSTR(\"\\r\\nLogical Max\"), 0x80);\n                        break;\n                case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMIN):\n                        E_Notify(PSTR(\"\\r\\nPhysical Min\"), 0x80);\n                        break;\n                case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMAX):\n                        E_Notify(PSTR(\"\\r\\nPhysical Max\"), 0x80);\n                        break;\n                case (TYPE_GLOBAL | TAG_GLOBAL_UNITEXP):\n                        E_Notify(PSTR(\"\\r\\nUnit Exp\"), 0x80);\n                        break;\n                case (TYPE_GLOBAL | TAG_GLOBAL_UNIT):\n                        E_Notify(PSTR(\"\\r\\nUnit\"), 0x80);\n                        break;\n                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTSIZE):\n                        E_Notify(PSTR(\"\\r\\nReport Size\"), 0x80);\n                        break;\n                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTCOUNT):\n                        E_Notify(PSTR(\"\\r\\nReport Count\"), 0x80);\n                        break;\n                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTID):\n                        E_Notify(PSTR(\"\\r\\nReport Id\"), 0x80);\n                        break;\n                case (TYPE_LOCAL | TAG_LOCAL_USAGE):\n                        E_Notify(PSTR(\"\\r\\nUsage\"), 0x80);\n                        break;\n                case (TYPE_LOCAL | TAG_LOCAL_USAGEMIN):\n                        E_Notify(PSTR(\"\\r\\nUsage Min\"), 0x80);\n                        break;\n                case (TYPE_LOCAL | TAG_LOCAL_USAGEMAX):\n                        E_Notify(PSTR(\"\\r\\nUsage Max\"), 0x80);\n                        break;\n                case (TYPE_MAIN | TAG_MAIN_COLLECTION):\n                        E_Notify(PSTR(\"\\r\\nCollection\"), 0x80);\n                        break;\n                case (TYPE_MAIN | TAG_MAIN_ENDCOLLECTION):\n                        E_Notify(PSTR(\"\\r\\nEnd Collection\"), 0x80);\n                        break;\n                case (TYPE_MAIN | TAG_MAIN_INPUT):\n                        E_Notify(PSTR(\"\\r\\nInput\"), 0x80);\n                        break;\n                case (TYPE_MAIN | TAG_MAIN_OUTPUT):\n                        E_Notify(PSTR(\"\\r\\nOutput\"), 0x80);\n                        break;\n                case (TYPE_MAIN | TAG_MAIN_FEATURE):\n                        E_Notify(PSTR(\"\\r\\nFeature\"), 0x80);\n                        break;\n        } // switch (**pp & (TYPE_MASK | TAG_MASK))\n}\n\nuint8_t ReportDescParserBase::ParseItem(uint8_t **pp, uint16_t *pcntdn) {\n        //uint8_t ret = enErrorSuccess;\n        //reinterpret_cast<>(varBuffer);\n        switch(itemParseState) {\n                case 0:\n                        if(**pp == HID_LONG_ITEM_PREFIX)\n                                USBTRACE(\"\\r\\nLONG\\r\\n\");\n                        else {\n                                uint8_t size = ((**pp) & DATA_SIZE_MASK);\n\n                                itemPrefix = (**pp);\n                                itemSize = 1 + ((size == DATA_SIZE_4) ? 4 : size);\n\n                                PrintItemTitle(itemPrefix);\n                        }\n                        (*pp)++;\n                        (*pcntdn)--;\n                        itemSize--;\n                        itemParseState = 1;\n\n                        if(!itemSize)\n                                break;\n\n                        if(!pcntdn)\n                                return enErrorIncomplete;\n                case 1:\n                        //USBTRACE2(\"\\r\\niSz:\",itemSize);\n\n                        theBuffer.valueSize = itemSize;\n                        valParser.Initialize(&theBuffer);\n                        itemParseState = 2;\n                case 2:\n                        if(!valParser.Parse(pp, pcntdn))\n                                return enErrorIncomplete;\n                        itemParseState = 3;\n                case 3:\n                {\n                        uint8_t data = *((uint8_t*)varBuffer);\n\n                        switch(itemPrefix & (TYPE_MASK | TAG_MASK)) {\n                                case (TYPE_LOCAL | TAG_LOCAL_USAGE):\n                                        if(pfUsage) {\n                                                if(theBuffer.valueSize > 1) {\n                                                        uint16_t* ui16 = reinterpret_cast<uint16_t *>(varBuffer);\n                                                        pfUsage(*ui16);\n                                                } else\n                                                        pfUsage(data);\n                                        }\n                                        break;\n                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTSIZE):\n                                        rptSize = data;\n                                        PrintByteValue(data);\n                                        break;\n                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTCOUNT):\n                                        rptCount = data;\n                                        PrintByteValue(data);\n                                        break;\n                                case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMIN):\n                                case (TYPE_GLOBAL | TAG_GLOBAL_LOGICALMAX):\n                                case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMIN):\n                                case (TYPE_GLOBAL | TAG_GLOBAL_PHYSMAX):\n                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTID):\n                                case (TYPE_LOCAL | TAG_LOCAL_USAGEMIN):\n                                case (TYPE_LOCAL | TAG_LOCAL_USAGEMAX):\n                                case (TYPE_GLOBAL | TAG_GLOBAL_UNITEXP):\n                                case (TYPE_GLOBAL | TAG_GLOBAL_UNIT):\n                                        PrintValue(varBuffer, theBuffer.valueSize);\n                                        break;\n                                case (TYPE_GLOBAL | TAG_GLOBAL_PUSH):\n                                case (TYPE_GLOBAL | TAG_GLOBAL_POP):\n                                        break;\n                                case (TYPE_GLOBAL | TAG_GLOBAL_USAGEPAGE):\n                                        SetUsagePage(data);\n                                        PrintUsagePage(data);\n                                        PrintByteValue(data);\n                                        break;\n                                case (TYPE_MAIN | TAG_MAIN_COLLECTION):\n                                case (TYPE_MAIN | TAG_MAIN_ENDCOLLECTION):\n                                        switch(data) {\n                                                case 0x00:\n                                                        E_Notify(PSTR(\" Physical\"), 0x80);\n                                                        break;\n                                                case 0x01:\n                                                        E_Notify(PSTR(\" Application\"), 0x80);\n                                                        break;\n                                                case 0x02:\n                                                        E_Notify(PSTR(\" Logical\"), 0x80);\n                                                        break;\n                                                case 0x03:\n                                                        E_Notify(PSTR(\" Report\"), 0x80);\n                                                        break;\n                                                case 0x04:\n                                                        E_Notify(PSTR(\" Named Array\"), 0x80);\n                                                        break;\n                                                case 0x05:\n                                                        E_Notify(PSTR(\" Usage Switch\"), 0x80);\n                                                        break;\n                                                case 0x06:\n                                                        E_Notify(PSTR(\" Usage Modifier\"), 0x80);\n                                                        break;\n                                                default:\n                                                        E_Notify(PSTR(\" Vendor Defined(\"), 0x80);\n                                                        PrintHex<uint8_t > (data, 0x80);\n                                                        E_Notify(PSTR(\")\"), 0x80);\n                                        }\n                                        break;\n                                case (TYPE_MAIN | TAG_MAIN_INPUT):\n                                case (TYPE_MAIN | TAG_MAIN_OUTPUT):\n                                case (TYPE_MAIN | TAG_MAIN_FEATURE):\n                                        totalSize += (uint16_t)rptSize * (uint16_t)rptCount;\n                                        rptSize = 0;\n                                        rptCount = 0;\n                                        E_Notify(PSTR(\"(\"), 0x80);\n                                        PrintBin<uint8_t > (data, 0x80);\n                                        E_Notify(PSTR(\")\"), 0x80);\n                                        break;\n                        } // switch (**pp & (TYPE_MASK | TAG_MASK))\n                }\n        } // switch (itemParseState)\n        itemParseState = 0;\n        return enErrorSuccess;\n}\n\nReportDescParserBase::UsagePageFunc ReportDescParserBase::usagePageFunctions[] /*PROGMEM*/ = {\n        &ReportDescParserBase::PrintGenericDesktopPageUsage,\n        &ReportDescParserBase::PrintSimulationControlsPageUsage,\n        &ReportDescParserBase::PrintVRControlsPageUsage,\n        &ReportDescParserBase::PrintSportsControlsPageUsage,\n        &ReportDescParserBase::PrintGameControlsPageUsage,\n        &ReportDescParserBase::PrintGenericDeviceControlsPageUsage,\n        NULL, // Keyboard/Keypad\n        &ReportDescParserBase::PrintLEDPageUsage,\n        &ReportDescParserBase::PrintButtonPageUsage,\n        &ReportDescParserBase::PrintOrdinalPageUsage,\n        &ReportDescParserBase::PrintTelephonyPageUsage,\n        &ReportDescParserBase::PrintConsumerPageUsage,\n        &ReportDescParserBase::PrintDigitizerPageUsage,\n        NULL, // Reserved\n        NULL, // PID\n        NULL // Unicode\n};\n\nvoid ReportDescParserBase::SetUsagePage(uint16_t page) {\n        pfUsage = NULL;\n\n        if(VALUE_BETWEEN(page, 0x00, 0x11)) {\n                pfUsage = (usagePageFunctions[page - 1]);\n\n        } else {\n                switch(page) {\n                        case 0x14:\n                                pfUsage = &ReportDescParserBase::PrintAlphanumDisplayPageUsage;\n                                break;\n                        case 0x40:\n                                pfUsage = &ReportDescParserBase::PrintMedicalInstrumentPageUsage;\n                                break;\n                }\n        }\n}\n\nvoid ReportDescParserBase::PrintUsagePage(uint16_t page) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(page, 0x00, 0x11, w, E_Notify, usagePageTitles0, 0x80)\n        else output_if_between(page, 0x8b, 0x92, w, E_Notify, usagePageTitles1, 0x80)\n        else if(VALUE_BETWEEN(page, 0x7f, 0x84))\n                E_Notify(pstrUsagePageMonitor, 0x80);\n        else if(VALUE_BETWEEN(page, 0x83, 0x8c))\n                E_Notify(pstrUsagePagePower, 0x80);\n        else if(page > 0xfeff /* && page <= 0xffff */)\n                E_Notify(pstrUsagePageVendorDefined, 0x80);\n        else\n                switch(page) {\n                        case 0x14:\n                                E_Notify(pstrUsagePageAlphaNumericDisplay, 0x80);\n                                break;\n                        case 0x40:\n                                E_Notify(pstrUsagePageMedicalInstruments, 0x80);\n                                break;\n                        default:\n                                E_Notify(pstrUsagePageUndefined, 0x80);\n                }\n}\n\nvoid ReportDescParserBase::PrintButtonPageUsage(uint16_t usage) {\n        E_Notify(pstrSpace, 0x80);\n        E_Notify(PSTR(\"Btn\"), 0x80);\n        PrintHex<uint16_t > (usage, 0x80);\n        E_Notify(PSTR(\"\\r\\n\"), 0x80);\n        //USB_HOST_SERIAL.print(usage, HEX);\n}\n\nvoid ReportDescParserBase::PrintOrdinalPageUsage(uint16_t usage) {\n        E_Notify(pstrSpace, 0x80);\n        E_Notify(PSTR(\"Inst\"), 0x80);\n        // Sorry, HEX for now...\n        PrintHex<uint16_t > (usage, 0x80);\n        E_Notify(PSTR(\"\\r\\n\"), 0x80);\n        //USB_HOST_SERIAL.print(usage, DEC);\n}\n\nvoid ReportDescParserBase::PrintGenericDesktopPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(usage, 0x00, 0x0a, w, E_Notify, genDesktopTitles0, 0x80)\n        else output_if_between(usage, 0x2f, 0x49, w, E_Notify, genDesktopTitles1, 0x80)\n        else output_if_between(usage, 0x7f, 0x94, w, E_Notify, genDesktopTitles2, 0x80)\n        else output_if_between(usage, 0x9f, 0xa9, w, E_Notify, genDesktopTitles3, 0x80)\n        else output_if_between(usage, 0xaf, 0xb8, w, E_Notify, genDesktopTitles4, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nvoid ReportDescParserBase::PrintSimulationControlsPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(usage, 0x00, 0x0d, w, E_Notify, simuTitles0, 0x80)\n        else output_if_between(usage, 0x1f, 0x26, w, E_Notify, simuTitles1, 0x80)\n        else output_if_between(usage, 0xaf, 0xd1, w, E_Notify, simuTitles2, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nvoid ReportDescParserBase::PrintVRControlsPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(usage, 0x00, 0x0b, w, E_Notify, vrTitles0, 0x80)\n        else output_if_between(usage, 0x1f, 0x22, w, E_Notify, vrTitles1, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nvoid ReportDescParserBase::PrintSportsControlsPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(usage, 0x00, 0x05, w, E_Notify, sportsCtrlTitles0, 0x80)\n        else output_if_between(usage, 0x2f, 0x3a, w, E_Notify, sportsCtrlTitles1, 0x80)\n        else output_if_between(usage, 0x4f, 0x64, w, E_Notify, sportsCtrlTitles2, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nvoid ReportDescParserBase::PrintGameControlsPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(usage, 0x00, 0x04, w, E_Notify, gameTitles0, 0x80)\n        else output_if_between(usage, 0x1f, 0x3a, w, E_Notify, gameTitles1, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nvoid ReportDescParserBase::PrintGenericDeviceControlsPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(usage, 0x1f, 0x27, w, E_Notify, genDevCtrlTitles, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nvoid ReportDescParserBase::PrintLEDPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(usage, 0x00, 0x4e, w, E_Notify, ledTitles, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nvoid ReportDescParserBase::PrintTelephonyPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(usage, 0x00, 0x08, w, E_Notify, telTitles0, 0x80)\n        else output_if_between(usage, 0x1f, 0x32, w, E_Notify, telTitles1, 0x80)\n        else output_if_between(usage, 0x4f, 0x54, w, E_Notify, telTitles2, 0x80)\n        else output_if_between(usage, 0x6f, 0x75, w, E_Notify, telTitles3, 0x80)\n        else output_if_between(usage, 0x8f, 0x9f, w, E_Notify, telTitles4, 0x80)\n        else output_if_between(usage, 0xaf, 0xc0, w, E_Notify, telTitles5, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nvoid ReportDescParserBase::PrintConsumerPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(usage, 0x00, 0x07, w, E_Notify, consTitles0, 0x80)\n        else output_if_between(usage, 0x1f, 0x23, w, E_Notify, consTitles1, 0x80)\n        else output_if_between(usage, 0x2f, 0x37, w, E_Notify, consTitles2, 0x80)\n        else output_if_between(usage, 0x3f, 0x49, w, E_Notify, consTitles3, 0x80)\n        else output_if_between(usage, 0x5f, 0x67, w, E_Notify, consTitles4, 0x80)\n        else output_if_between(usage, 0x7f, 0xa5, w, E_Notify, consTitles5, 0x80)\n        else output_if_between(usage, 0xaf, 0xcf, w, E_Notify, consTitles6, 0x80)\n        else output_if_between(usage, 0xdf, 0xeb, w, E_Notify, consTitles7, 0x80)\n        else output_if_between(usage, 0xef, 0xf6, w, E_Notify, consTitles8, 0x80)\n        else output_if_between(usage, 0xff, 0x10e, w, E_Notify, consTitles9, 0x80)\n        else output_if_between(usage, 0x14f, 0x156, w, E_Notify, consTitlesA, 0x80)\n        else output_if_between(usage, 0x15f, 0x16b, w, E_Notify, consTitlesB, 0x80)\n        else output_if_between(usage, 0x16f, 0x175, w, E_Notify, consTitlesC, 0x80)\n        else output_if_between(usage, 0x17f, 0x1c8, w, E_Notify, consTitlesD, 0x80)\n        else output_if_between(usage, 0x1ff, 0x29d, w, E_Notify, consTitlesE, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nvoid ReportDescParserBase::PrintDigitizerPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(usage, 0x00, 0x0e, w, E_Notify, digitTitles0, 0x80)\n        else output_if_between(usage, 0x1f, 0x23, w, E_Notify, digitTitles1, 0x80)\n        else output_if_between(usage, 0x2f, 0x47, w, E_Notify, digitTitles2, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nvoid ReportDescParserBase::PrintAlphanumDisplayPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        output_if_between(usage, 0x00, 0x03, w, E_Notify, aplphanumTitles0, 0x80)\n        else output_if_between(usage, 0x1f, 0x4e, w, E_Notify, aplphanumTitles1, 0x80)\n        else output_if_between(usage, 0x7f, 0x96, w, E_Notify, digitTitles2, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nvoid ReportDescParserBase::PrintMedicalInstrumentPageUsage(uint16_t usage) {\n        const char * const * w;\n        E_Notify(pstrSpace, 0x80);\n\n        if(usage == 1) E_Notify(pstrUsageMedicalUltrasound, 0x80);\n        else if(usage == 0x70)\n                E_Notify(pstrUsageDepthGainCompensation, 0x80);\n        else output_if_between(usage, 0x1f, 0x28, w, E_Notify, medInstrTitles0, 0x80)\n        else output_if_between(usage, 0x3f, 0x45, w, E_Notify, medInstrTitles1, 0x80)\n        else output_if_between(usage, 0x5f, 0x62, w, E_Notify, medInstrTitles2, 0x80)\n        else output_if_between(usage, 0x7f, 0x8a, w, E_Notify, medInstrTitles3, 0x80)\n        else output_if_between(usage, 0x9f, 0xa2, w, E_Notify, medInstrTitles4, 0x80)\n        else E_Notify(pstrUsagePageUndefined, 0x80);\n}\n\nuint8_t ReportDescParser2::ParseItem(uint8_t **pp, uint16_t *pcntdn) {\n        //uint8_t ret = enErrorSuccess;\n\n        switch(itemParseState) {\n                case 0:\n                        if(**pp == HID_LONG_ITEM_PREFIX)\n                                USBTRACE(\"\\r\\nLONG\\r\\n\");\n                        else {\n                                uint8_t size = ((**pp) & DATA_SIZE_MASK);\n                                itemPrefix = (**pp);\n                                itemSize = 1 + ((size == DATA_SIZE_4) ? 4 : size);\n                        }\n                        (*pp)++;\n                        (*pcntdn)--;\n                        itemSize--;\n                        itemParseState = 1;\n\n                        if(!itemSize)\n                                break;\n\n                        if(!pcntdn)\n                                return enErrorIncomplete;\n                case 1:\n                        theBuffer.valueSize = itemSize;\n                        valParser.Initialize(&theBuffer);\n                        itemParseState = 2;\n                case 2:\n                        if(!valParser.Parse(pp, pcntdn))\n                                return enErrorIncomplete;\n                        itemParseState = 3;\n                case 3:\n                {\n                        uint8_t data = *((uint8_t*)varBuffer);\n\n                        switch(itemPrefix & (TYPE_MASK | TAG_MASK)) {\n                                case (TYPE_LOCAL | TAG_LOCAL_USAGE):\n                                        if(pfUsage) {\n                                                if(theBuffer.valueSize > 1) {\n                                                        uint16_t* ui16 = reinterpret_cast<uint16_t *>(varBuffer);\n                                                        pfUsage(*ui16);\n                                                } else\n                                                        pfUsage(data);\n                                        }\n                                        break;\n                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTSIZE):\n                                        rptSize = data;\n                                        break;\n                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTCOUNT):\n                                        rptCount = data;\n                                        break;\n                                case (TYPE_GLOBAL | TAG_GLOBAL_REPORTID):\n                                        rptId = data;\n                                        break;\n                                case (TYPE_LOCAL | TAG_LOCAL_USAGEMIN):\n                                        useMin = data;\n                                        break;\n                                case (TYPE_LOCAL | TAG_LOCAL_USAGEMAX):\n                                        useMax = data;\n                                        break;\n                                case (TYPE_GLOBAL | TAG_GLOBAL_USAGEPAGE):\n                                        SetUsagePage(data);\n                                        break;\n                                case (TYPE_MAIN | TAG_MAIN_OUTPUT):\n                                case (TYPE_MAIN | TAG_MAIN_FEATURE):\n                                        rptSize = 0;\n                                        rptCount = 0;\n                                        useMin = 0;\n                                        useMax = 0;\n                                        break;\n                                case (TYPE_MAIN | TAG_MAIN_INPUT):\n                                        OnInputItem(data);\n\n                                        totalSize += (uint16_t)rptSize * (uint16_t)rptCount;\n\n                                        rptSize = 0;\n                                        rptCount = 0;\n                                        useMin = 0;\n                                        useMax = 0;\n                                        break;\n                        } // switch (**pp & (TYPE_MASK | TAG_MASK))\n                }\n        } // switch (itemParseState)\n        itemParseState = 0;\n        return enErrorSuccess;\n}\n\nvoid ReportDescParser2::OnInputItem(uint8_t itm) {\n        uint8_t byte_offset = (totalSize >> 3); // calculate offset to the next unhandled byte i = (int)(totalCount / 8);\n        uint32_t tmp = (byte_offset << 3);\n        uint8_t bit_offset = totalSize - tmp; // number of bits in the current byte already handled\n        uint8_t *p = pBuf + byte_offset; // current byte pointer\n\n        if(bit_offset)\n                *p >>= bit_offset;\n\n        uint8_t usage = useMin;\n\n        bool print_usemin_usemax = ((useMin < useMax) && ((itm & 3) == 2) && pfUsage) ? true : false;\n\n        uint8_t bits_of_byte = 8;\n\n        // for each field in field array defined by rptCount\n        for(uint8_t field = 0; field < rptCount; field++, usage++) {\n\n                union {\n                        uint8_t bResult[4];\n                        uint16_t wResult[2];\n                        uint32_t dwResult;\n                } result;\n\n                result.dwResult = 0;\n                uint8_t mask = 0;\n\n                if(print_usemin_usemax)\n                        pfUsage(usage);\n\n                // bits_left            - number of bits in the field(array of fields, depending on Report Count) left to process\n                // bits_of_byte         - number of bits in current byte left to process\n                // bits_to_copy         - number of bits to copy to result buffer\n\n                // for each bit in a field\n                for(uint8_t bits_left = rptSize, bits_to_copy = 0; bits_left;\n                        bits_left -= bits_to_copy) {\n                        bits_to_copy = (bits_left > bits_of_byte) ? bits_of_byte : bits_left;\n\n                        result.dwResult <<= bits_to_copy; // Result buffer is shifted by the number of bits to be copied into it\n\n                        uint8_t val = *p;\n\n                        val >>= (8 - bits_of_byte); // Shift by the number of bits already processed\n\n                        mask = 0;\n\n                        for(uint8_t j = bits_to_copy; j; j--) {\n                                mask <<= 1;\n                                mask |= 1;\n                        }\n\n                        result.bResult[0] = (result.bResult[0] | (val & mask));\n\n                        bits_of_byte -= bits_to_copy;\n\n                        if(bits_of_byte < 1) {\n                                bits_of_byte = 8;\n                                p++;\n                        }\n                }\n                PrintByteValue(result.dwResult);\n        }\n        E_Notify(PSTR(\"\\r\\n\"), 0x80);\n}\n\nvoid UniversalReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {\n        ReportDescParser2 prs(len, buf);\n\n        uint8_t ret = hid->GetReportDescr(0, &prs);\n\n        if(ret)\n                ErrorMessage<uint8_t > (PSTR(\"GetReportDescr-2\"), ret);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/hidescriptorparser.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(__HIDDESCRIPTORPARSER_H__)\n#define __HIDDESCRIPTORPARSER_H__\n\n#include \"hid.h\"\n\nclass ReportDescParserBase : public USBReadParser {\npublic:\n        typedef void (*UsagePageFunc)(uint16_t usage);\n\n        static void PrintGenericDesktopPageUsage(uint16_t usage);\n        static void PrintSimulationControlsPageUsage(uint16_t usage);\n        static void PrintVRControlsPageUsage(uint16_t usage);\n        static void PrintSportsControlsPageUsage(uint16_t usage);\n        static void PrintGameControlsPageUsage(uint16_t usage);\n        static void PrintGenericDeviceControlsPageUsage(uint16_t usage);\n        static void PrintLEDPageUsage(uint16_t usage);\n        static void PrintButtonPageUsage(uint16_t usage);\n        static void PrintOrdinalPageUsage(uint16_t usage);\n        static void PrintTelephonyPageUsage(uint16_t usage);\n        static void PrintConsumerPageUsage(uint16_t usage);\n        static void PrintDigitizerPageUsage(uint16_t usage);\n        static void PrintAlphanumDisplayPageUsage(uint16_t usage);\n        static void PrintMedicalInstrumentPageUsage(uint16_t usage);\n\n        static void PrintValue(uint8_t *p, uint8_t len);\n        static void PrintByteValue(uint8_t data);\n\n        static void PrintItemTitle(uint8_t prefix);\n\n        static const char * const usagePageTitles0[];\n        static const char * const usagePageTitles1[];\n        static const char * const genDesktopTitles0[];\n        static const char * const genDesktopTitles1[];\n        static const char * const genDesktopTitles2[];\n        static const char * const genDesktopTitles3[];\n        static const char * const genDesktopTitles4[];\n        static const char * const simuTitles0[];\n        static const char * const simuTitles1[];\n        static const char * const simuTitles2[];\n        static const char * const vrTitles0[];\n        static const char * const vrTitles1[];\n        static const char * const sportsCtrlTitles0[];\n        static const char * const sportsCtrlTitles1[];\n        static const char * const sportsCtrlTitles2[];\n        static const char * const gameTitles0[];\n        static const char * const gameTitles1[];\n        static const char * const genDevCtrlTitles[];\n        static const char * const ledTitles[];\n        static const char * const telTitles0[];\n        static const char * const telTitles1[];\n        static const char * const telTitles2[];\n        static const char * const telTitles3[];\n        static const char * const telTitles4[];\n        static const char * const telTitles5[];\n        static const char * const consTitles0[];\n        static const char * const consTitles1[];\n        static const char * const consTitles2[];\n        static const char * const consTitles3[];\n        static const char * const consTitles4[];\n        static const char * const consTitles5[];\n        static const char * const consTitles6[];\n        static const char * const consTitles7[];\n        static const char * const consTitles8[];\n        static const char * const consTitles9[];\n        static const char * const consTitlesA[];\n        static const char * const consTitlesB[];\n        static const char * const consTitlesC[];\n        static const char * const consTitlesD[];\n        static const char * const consTitlesE[];\n        static const char * const digitTitles0[];\n        static const char * const digitTitles1[];\n        static const char * const digitTitles2[];\n        static const char * const aplphanumTitles0[];\n        static const char * const aplphanumTitles1[];\n        static const char * const aplphanumTitles2[];\n        static const char * const medInstrTitles0[];\n        static const char * const medInstrTitles1[];\n        static const char * const medInstrTitles2[];\n        static const char * const medInstrTitles3[];\n        static const char * const medInstrTitles4[];\n\nprotected:\n        static UsagePageFunc usagePageFunctions[];\n\n        MultiValueBuffer theBuffer;\n        MultiByteValueParser valParser;\n        ByteSkipper theSkipper;\n        uint8_t varBuffer[sizeof (USB_CONFIGURATION_DESCRIPTOR)];\n\n        uint8_t itemParseState; // Item parser state variable\n        uint8_t itemSize; // Item size\n        uint8_t itemPrefix; // Item prefix (first byte)\n        uint8_t rptSize; // Report Size\n        uint8_t rptCount; // Report Count\n\n        uint16_t totalSize; // Report size in bits\n\n        // Method should be defined here if virtual.\n        virtual uint8_t ParseItem(uint8_t **pp, uint16_t *pcntdn);\n\n        UsagePageFunc pfUsage;\n\n        static void PrintUsagePage(uint16_t page);\n        void SetUsagePage(uint16_t page);\n\npublic:\n\n        ReportDescParserBase() :\n        itemParseState(0),\n        itemSize(0),\n        itemPrefix(0),\n        rptSize(0),\n        rptCount(0),\n        pfUsage(NULL) {\n                theBuffer.pValue = varBuffer;\n                valParser.Initialize(&theBuffer);\n                theSkipper.Initialize(&theBuffer);\n        };\n\n        void Parse(const uint16_t len, const uint8_t *pbuf, const uint16_t &offset);\n\n        enum {\n                enErrorSuccess = 0\n                , enErrorIncomplete // value or record is partialy read in buffer\n                , enErrorBufferTooSmall\n        };\n};\n\nclass ReportDescParser : public ReportDescParserBase {\n};\n\nclass ReportDescParser2 : public ReportDescParserBase {\n        uint8_t rptId; // Report ID\n        uint8_t useMin; // Usage Minimum\n        uint8_t useMax; // Usage Maximum\n        uint8_t fieldCount; // Number of field being currently processed\n\n        void OnInputItem(uint8_t itm); // Method which is called every time Input item is found\n\n        uint8_t *pBuf; // Report buffer pointer\n        uint8_t bLen; // Report length\n\nprotected:\n        // Method should be defined here if virtual.\n        virtual uint8_t ParseItem(uint8_t **pp, uint16_t *pcntdn);\n\npublic:\n\n        ReportDescParser2(uint16_t len, uint8_t *pbuf) :\n        ReportDescParserBase(), rptId(0), useMin(0), useMax(0), fieldCount(0), pBuf(pbuf), bLen(len) {\n        };\n};\n\nclass UniversalReportParser : public HIDReportParser {\npublic:\n        // Method should be defined here if virtual.\n        virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);\n};\n\n#endif // __HIDDESCRIPTORPARSER_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/hiduniversal.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#include \"hiduniversal.h\"\n\nHIDUniversal::HIDUniversal(USB *p) :\nHID(p),\nqNextPollTime(0),\npollInterval(0),\nbPollEnable(false),\nbHasReportId(false) {\n        Initialize();\n\n        if(pUsb)\n                pUsb->RegisterDeviceClass(this);\n}\n\nuint16_t HIDUniversal::GetHidClassDescrLen(uint8_t type, uint8_t num) {\n        for(uint8_t i = 0, n = 0; i < HID_MAX_HID_CLASS_DESCRIPTORS; i++) {\n                if(descrInfo[i].bDescrType == type) {\n                        if(n == num)\n                                return descrInfo[i].wDescriptorLength;\n                        n++;\n                }\n        }\n        return 0;\n}\n\nvoid HIDUniversal::Initialize() {\n        for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) {\n                rptParsers[i].rptId = 0;\n                rptParsers[i].rptParser = NULL;\n        }\n        for(uint8_t i = 0; i < HID_MAX_HID_CLASS_DESCRIPTORS; i++) {\n                descrInfo[i].bDescrType = 0;\n                descrInfo[i].wDescriptorLength = 0;\n        }\n        for(uint8_t i = 0; i < maxHidInterfaces; i++) {\n                hidInterfaces[i].bmInterface = 0;\n                hidInterfaces[i].bmProtocol = 0;\n\n                for(uint8_t j = 0; j < maxEpPerInterface; j++)\n                        hidInterfaces[i].epIndex[j] = 0;\n        }\n        for(uint8_t i = 0; i < totalEndpoints; i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n                epInfo[i].bmNakPower = (i) ? USB_NAK_NOWAIT : USB_NAK_MAX_POWER;\n        }\n        bNumEP = 1;\n        bNumIface = 0;\n        bConfNum = 0;\n        pollInterval = 0;\n\n        ZeroMemory(constBuffLen, prevBuf);\n}\n\nbool HIDUniversal::SetReportParser(uint8_t id, HIDReportParser *prs) {\n        for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) {\n                if(rptParsers[i].rptId == 0 && rptParsers[i].rptParser == NULL) {\n                        rptParsers[i].rptId = id;\n                        rptParsers[i].rptParser = prs;\n                        return true;\n                }\n        }\n        return false;\n}\n\nHIDReportParser* HIDUniversal::GetReportParser(uint8_t id) {\n        if(!bHasReportId)\n                return ((rptParsers[0].rptParser) ? rptParsers[0].rptParser : NULL);\n\n        for(uint8_t i = 0; i < MAX_REPORT_PARSERS; i++) {\n                if(rptParsers[i].rptId == id)\n                        return rptParsers[i].rptParser;\n        }\n        return NULL;\n}\n\nuint8_t HIDUniversal::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);\n\n        uint8_t buf[constBufSize];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        uint8_t len = 0;\n\n        uint8_t num_of_conf; // number of configurations\n        //uint8_t num_of_intf; // number of interfaces\n\n        AddressPool &addrPool = pUsb->GetAddressPool();\n\n        USBTRACE(\"HU Init\\r\\n\");\n\n        if(bAddress)\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        if(!p->epinfo) {\n                USBTRACE(\"epinfo\\r\\n\");\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf);\n\n        if(!rcode)\n                len = (buf[0] > constBufSize) ? constBufSize : buf[0];\n\n        if(rcode) {\n                // Restore p->epinfo\n                p->epinfo = oldep_ptr;\n\n                goto FailGetDevDescr;\n        }\n\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from the device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n                USBTRACE2(\"setAddr:\", rcode);\n                return rcode;\n        }\n\n        //delay(2); //per USB 2.0 sect.9.2.6.3\n\n        USBTRACE2(\"Addr:\", bAddress);\n\n        p->lowspeed = false;\n\n        p = addrPool.GetUsbDevicePtr(bAddress);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        if(len)\n                rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf);\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        VID = udd->idVendor; // Can be used by classes that inherits this class to check the VID and PID of the connected device\n        PID = udd->idProduct;\n\n        num_of_conf = udd->bNumConfigurations;\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);\n\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        USBTRACE2(\"NC:\", num_of_conf);\n\n        for(uint8_t i = 0; i < num_of_conf; i++) {\n                //HexDumper<USBReadParser, uint16_t, uint16_t> HexDump;\n                ConfigDescParser<USB_CLASS_HID, 0, 0,\n                        CP_MASK_COMPARE_CLASS> confDescrParser(this);\n\n                //rcode = pUsb->getConfDescr(bAddress, 0, i, &HexDump);\n                rcode = pUsb->getConfDescr(bAddress, 0, i, &confDescrParser);\n\n                if(rcode)\n                        goto FailGetConfDescr;\n\n                if(bNumEP > 1)\n                        break;\n        } // for\n\n        if(bNumEP < 2)\n                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);\n\n        USBTRACE2(\"Cnf:\", bConfNum);\n\n        // Set Configuration Value\n        rcode = pUsb->setConf(bAddress, 0, bConfNum);\n\n        if(rcode)\n                goto FailSetConfDescr;\n\n        for(uint8_t i = 0; i < bNumIface; i++) {\n                if(hidInterfaces[i].epIndex[epInterruptInIndex] == 0)\n                        continue;\n\n                rcode = SetIdle(hidInterfaces[i].bmInterface, 0, 0);\n\n                if(rcode && rcode != hrSTALL)\n                        goto FailSetIdle;\n        }\n\n        USBTRACE(\"HU configured\\r\\n\");\n\n        OnInitSuccessful();\n\n        bPollEnable = true;\n        return 0;\n\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr();\n        goto Fail;\n#endif\n\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailGetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetConfDescr();\n        goto Fail;\n#endif\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n        goto Fail;\n#endif\n\n\nFailSetIdle:\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"SetIdle:\");\n#endif\n\n#ifdef DEBUG_USB_HOST\nFail:\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n\nHIDUniversal::HIDInterface* HIDUniversal::FindInterface(uint8_t iface, uint8_t alt, uint8_t proto) {\n        for(uint8_t i = 0; i < bNumIface && i < maxHidInterfaces; i++)\n                if(hidInterfaces[i].bmInterface == iface && hidInterfaces[i].bmAltSet == alt\n                        && hidInterfaces[i].bmProtocol == proto)\n                        return hidInterfaces + i;\n        return NULL;\n}\n\nvoid HIDUniversal::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *pep) {\n        // If the first configuration satisfies, the others are not concidered.\n        if(bNumEP > 1 && conf != bConfNum)\n                return;\n\n        //ErrorMessage<uint8_t>(PSTR(\"\\r\\nConf.Val\"), conf);\n        //ErrorMessage<uint8_t>(PSTR(\"Iface Num\"), iface);\n        //ErrorMessage<uint8_t>(PSTR(\"Alt.Set\"), alt);\n\n        bConfNum = conf;\n\n        uint8_t index = 0;\n        HIDInterface *piface = FindInterface(iface, alt, proto);\n\n        // Fill in interface structure in case of new interface\n        if(!piface) {\n                piface = hidInterfaces + bNumIface;\n                piface->bmInterface = iface;\n                piface->bmAltSet = alt;\n                piface->bmProtocol = proto;\n                bNumIface++;\n        }\n\n        if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80)\n                index = epInterruptInIndex;\n        else\n                index = epInterruptOutIndex;\n\n        if(index) {\n                // Fill in the endpoint info structure\n                epInfo[bNumEP].epAddr = (pep->bEndpointAddress & 0x0F);\n                epInfo[bNumEP].maxPktSize = (uint8_t)pep->wMaxPacketSize;\n                epInfo[bNumEP].epAttribs = 0;\n                epInfo[bNumEP].bmNakPower = USB_NAK_NOWAIT;\n\n                // Fill in the endpoint index list\n                piface->epIndex[index] = bNumEP; //(pep->bEndpointAddress & 0x0F);\n\n                if(pollInterval < pep->bInterval) // Set the polling interval as the largest polling interval obtained from endpoints\n                        pollInterval = pep->bInterval;\n\n                bNumEP++;\n        }\n        //PrintEndpointDescriptor(pep);\n}\n\nuint8_t HIDUniversal::Release() {\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n\n        bNumEP = 1;\n        bAddress = 0;\n        qNextPollTime = 0;\n        bPollEnable = false;\n        return 0;\n}\n\nbool HIDUniversal::BuffersIdentical(uint8_t len, uint8_t *buf1, uint8_t *buf2) {\n        for(uint8_t i = 0; i < len; i++)\n                if(buf1[i] != buf2[i])\n                        return false;\n        return true;\n}\n\nvoid HIDUniversal::ZeroMemory(uint8_t len, uint8_t *buf) {\n        for(uint8_t i = 0; i < len; i++)\n                buf[i] = 0;\n}\n\nvoid HIDUniversal::SaveBuffer(uint8_t len, uint8_t *src, uint8_t *dest) {\n        for(uint8_t i = 0; i < len; i++)\n                dest[i] = src[i];\n}\n\nuint8_t HIDUniversal::Poll() {\n        uint8_t rcode = 0;\n\n        if(!bPollEnable)\n                return 0;\n\n        if((long)(millis() - qNextPollTime) >= 0L) {\n                qNextPollTime = millis() + pollInterval;\n\n                uint8_t buf[constBuffLen];\n\n                for(uint8_t i = 0; i < bNumIface; i++) {\n                        uint8_t index = hidInterfaces[i].epIndex[epInterruptInIndex];\n                        uint16_t read = (uint16_t)epInfo[index].maxPktSize;\n\n                        ZeroMemory(constBuffLen, buf);\n\n                        uint8_t rcode = pUsb->inTransfer(bAddress, epInfo[index].epAddr, &read, buf);\n\n                        if(rcode) {\n                                if(rcode != hrNAK)\n                                        USBTRACE3(\"(hiduniversal.h) Poll:\", rcode, 0x81);\n                                return rcode;\n                        }\n\n                        if(read > constBuffLen)\n                                read = constBuffLen;\n\n                        bool identical = BuffersIdentical(read, buf, prevBuf);\n\n                        SaveBuffer(read, buf, prevBuf);\n\n                        if(identical)\n                                return 0;\n#if 0\n                        Notify(PSTR(\"\\r\\nBuf: \"), 0x80);\n\n                        for(uint8_t i = 0; i < read; i++) {\n                                D_PrintHex<uint8_t > (buf[i], 0x80);\n                                Notify(PSTR(\" \"), 0x80);\n                        }\n\n                        Notify(PSTR(\"\\r\\n\"), 0x80);\n#endif\n                        ParseHIDData(this, bHasReportId, (uint8_t)read, buf);\n\n                        HIDReportParser *prs = GetReportParser(((bHasReportId) ? *buf : 0));\n\n                        if(prs)\n                                prs->Parse(this, bHasReportId, (uint8_t)read, buf);\n                }\n        }\n        return rcode;\n}\n\n// Send a report to interrupt out endpoint. This is NOT SetReport() request!\nuint8_t HIDUniversal::SndRpt(uint16_t nbytes, uint8_t *dataptr) {\n        return pUsb->outTransfer(bAddress, epInfo[epInterruptOutIndex].epAddr, nbytes, dataptr);\n}"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/hiduniversal.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#if !defined(__HIDUNIVERSAL_H__)\n#define __HIDUNIVERSAL_H__\n\n#include \"hid.h\"\n//#include \"hidescriptorparser.h\"\n\nclass HIDUniversal : public HID {\n\n        struct ReportParser {\n                uint8_t rptId;\n                HIDReportParser *rptParser;\n        } rptParsers[MAX_REPORT_PARSERS];\n\n        // HID class specific descriptor type and length info obtained from HID descriptor\n        HID_CLASS_DESCRIPTOR_LEN_AND_TYPE descrInfo[HID_MAX_HID_CLASS_DESCRIPTORS];\n\n        // Returns HID class specific descriptor length by its type and order number\n        uint16_t GetHidClassDescrLen(uint8_t type, uint8_t num);\n\n        struct HIDInterface {\n                struct {\n                        uint8_t bmInterface : 3;\n                        uint8_t bmAltSet : 3;\n                        uint8_t bmProtocol : 2;\n                };\n                uint8_t epIndex[maxEpPerInterface];\n        };\n\n        uint8_t bConfNum; // configuration number\n        uint8_t bNumIface; // number of interfaces in the configuration\n        uint8_t bNumEP; // total number of EP in the configuration\n        uint32_t qNextPollTime; // next poll time\n        uint8_t pollInterval;\n        bool bPollEnable; // poll enable flag\n\n        static const uint16_t constBuffLen = 64; // event buffer length\n        uint8_t prevBuf[constBuffLen]; // previous event buffer\n\n        void Initialize();\n        HIDInterface* FindInterface(uint8_t iface, uint8_t alt, uint8_t proto);\n\n        void ZeroMemory(uint8_t len, uint8_t *buf);\n        bool BuffersIdentical(uint8_t len, uint8_t *buf1, uint8_t *buf2);\n        void SaveBuffer(uint8_t len, uint8_t *src, uint8_t *dest);\n\nprotected:\n        EpInfo epInfo[totalEndpoints];\n        HIDInterface hidInterfaces[maxHidInterfaces];\n\n        bool bHasReportId;\n\n        uint16_t PID, VID; // PID and VID of connected device\n\n        // HID implementation\n        HIDReportParser* GetReportParser(uint8_t id);\n\n        virtual uint8_t OnInitSuccessful() {\n                return 0;\n        };\n\n        virtual void ParseHIDData(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf) {\n                return;\n        };\n\npublic:\n        HIDUniversal(USB *p);\n\n        // HID implementation\n        bool SetReportParser(uint8_t id, HIDReportParser *prs);\n\n        // USBDeviceConfig implementation\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        uint8_t Release();\n        uint8_t Poll();\n\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        virtual bool isReady() {\n                return bPollEnable;\n        };\n\n        // UsbConfigXtracter implementation\n        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);\n\n        // Send report - do not mix with SetReport()!\n        uint8_t SndRpt(uint16_t nbytes, uint8_t *dataptr);\n};\n\n#endif // __HIDUNIVERSAL_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/hidusagestr.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined( __HIDUSAGESTR_H__)\n#define  __HIDUSAGESTR_H__\n\n#include \"Usb.h\"\n\nconst char pstrSpace [] PROGMEM = \" \";\nconst char pstrCRLF [] PROGMEM = \"\\r\\n\";\nconst char pstrSingleTab [] PROGMEM = \"\\t\";\nconst char pstrDoubleTab [] PROGMEM = \"\\t\\t\";\nconst char pstrTripleTab [] PROGMEM = \"\\t\\t\\t\";\n\n// Usage Page String Titles\nconst char pstrUsagePageUndefined [] PROGMEM = \"Undef\";\nconst char pstrUsagePageGenericDesktopControls [] PROGMEM = \"Gen Desktop Ctrls\";\nconst char pstrUsagePageSimulationControls [] PROGMEM = \"Simu Ctrls\";\nconst char pstrUsagePageVRControls [] PROGMEM = \"VR Ctrls\";\nconst char pstrUsagePageSportControls [] PROGMEM = \"Sport Ctrls\";\nconst char pstrUsagePageGameControls [] PROGMEM = \"Game Ctrls\";\nconst char pstrUsagePageGenericDeviceControls [] PROGMEM = \"Gen Dev Ctrls\";\nconst char pstrUsagePageKeyboardKeypad [] PROGMEM = \"Kbrd/Keypad\";\nconst char pstrUsagePageLEDs [] PROGMEM = \"LEDs\";\nconst char pstrUsagePageButton [] PROGMEM = \"Button\";\nconst char pstrUsagePageOrdinal [] PROGMEM = \"Ordinal\";\nconst char pstrUsagePageTelephone [] PROGMEM = \"Tel\";\nconst char pstrUsagePageConsumer [] PROGMEM = \"Consumer\";\nconst char pstrUsagePageDigitizer [] PROGMEM = \"Digitizer\";\nconst char pstrUsagePagePID [] PROGMEM = \"PID\";\nconst char pstrUsagePageUnicode [] PROGMEM = \"Unicode\";\nconst char pstrUsagePageAlphaNumericDisplay [] PROGMEM = \"Alpha Num Disp\";\nconst char pstrUsagePageMedicalInstruments [] PROGMEM = \"Medical Instr\";\nconst char pstrUsagePageMonitor [] PROGMEM = \"Monitor\";\nconst char pstrUsagePagePower [] PROGMEM = \"Power\";\nconst char pstrUsagePageBarCodeScanner [] PROGMEM = \"Bar Code Scan\";\nconst char pstrUsagePageScale [] PROGMEM = \"Scale\";\nconst char pstrUsagePageMSRDevices [] PROGMEM = \"Magn Stripe Read Dev\";\nconst char pstrUsagePagePointOfSale [] PROGMEM = \"POS\";\nconst char pstrUsagePageCameraControl [] PROGMEM = \"Cam Ctrl\";\nconst char pstrUsagePageArcade [] PROGMEM = \"Arcade\";\nconst char pstrUsagePageReserved [] PROGMEM = \"Reserved\";\nconst char pstrUsagePageVendorDefined [] PROGMEM = \"Vendor Def\";\n\n// Generic Desktop Controls Page\nconst char pstrUsagePointer [] PROGMEM = \"Pointer\";\nconst char pstrUsageMouse [] PROGMEM = \"Mouse\";\nconst char pstrUsageJoystick [] PROGMEM = \"Joystick\";\nconst char pstrUsageGamePad [] PROGMEM = \"Game Pad\";\nconst char pstrUsageKeyboard [] PROGMEM = \"Kbrd\";\nconst char pstrUsageKeypad [] PROGMEM = \"Keypad\";\nconst char pstrUsageMultiAxisController [] PROGMEM = \"Multi-axis Ctrl\";\nconst char pstrUsageTabletPCSystemControls [] PROGMEM = \"Tablet PC Sys Ctrls\";\nconst char pstrUsageX [] PROGMEM = \"X\";\nconst char pstrUsageY [] PROGMEM = \"Y\";\nconst char pstrUsageZ [] PROGMEM = \"Z\";\nconst char pstrUsageRx [] PROGMEM = \"Rx\";\nconst char pstrUsageRy [] PROGMEM = \"Ry\";\nconst char pstrUsageRz [] PROGMEM = \"Rz\";\nconst char pstrUsageSlider [] PROGMEM = \"Slider\";\nconst char pstrUsageDial [] PROGMEM = \"Dial\";\nconst char pstrUsageWheel [] PROGMEM = \"Wheel\";\nconst char pstrUsageHatSwitch [] PROGMEM = \"Hat Switch\";\nconst char pstrUsageCountedBuffer [] PROGMEM = \"Counted Buf\";\nconst char pstrUsageByteCount [] PROGMEM = \"Byte Count\";\nconst char pstrUsageMotionWakeup [] PROGMEM = \"Motion Wakeup\";\nconst char pstrUsageStart [] PROGMEM = \"Start\";\nconst char pstrUsageSelect [] PROGMEM = \"Sel\";\nconst char pstrUsageVx [] PROGMEM = \"Vx\";\nconst char pstrUsageVy [] PROGMEM = \"Vy\";\nconst char pstrUsageVz [] PROGMEM = \"Vz\";\nconst char pstrUsageVbrx [] PROGMEM = \"Vbrx\";\nconst char pstrUsageVbry [] PROGMEM = \"Vbry\";\nconst char pstrUsageVbrz [] PROGMEM = \"Vbrz\";\nconst char pstrUsageVno [] PROGMEM = \"Vno\";\nconst char pstrUsageFeatureNotification [] PROGMEM = \"Feature Notif\";\nconst char pstrUsageResolutionMultiplier [] PROGMEM = \"Res Mult\";\nconst char pstrUsageSystemControl [] PROGMEM = \"Sys Ctrl\";\nconst char pstrUsageSystemPowerDown [] PROGMEM = \"Sys Pwr Down\";\nconst char pstrUsageSystemSleep [] PROGMEM = \"Sys Sleep\";\nconst char pstrUsageSystemWakeup [] PROGMEM = \"Sys Wakeup\";\nconst char pstrUsageSystemContextMenu [] PROGMEM = \"Sys Context Menu\";\nconst char pstrUsageSystemMainMenu [] PROGMEM = \"Sys Main Menu\";\nconst char pstrUsageSystemAppMenu [] PROGMEM = \"Sys App Menu\";\nconst char pstrUsageSystemMenuHelp [] PROGMEM = \"Sys Menu Help\";\nconst char pstrUsageSystemMenuExit [] PROGMEM = \"Sys Menu Exit\";\nconst char pstrUsageSystemMenuSelect [] PROGMEM = \"Sys Menu Select\";\nconst char pstrUsageSystemMenuRight [] PROGMEM = \"Sys Menu Right\";\nconst char pstrUsageSystemMenuLeft [] PROGMEM = \"Sys Menu Left\";\nconst char pstrUsageSystemMenuUp [] PROGMEM = \"Sys Menu Up\";\nconst char pstrUsageSystemMenuDown [] PROGMEM = \"Sys Menu Down\";\nconst char pstrUsageSystemColdRestart [] PROGMEM = \"Sys Cold Restart\";\nconst char pstrUsageSystemWarmRestart [] PROGMEM = \"Sys Warm Restart\";\nconst char pstrUsageDPadUp [] PROGMEM = \"D-pad Up\";\nconst char pstrUsageDPadDown [] PROGMEM = \"D-pad Down\";\nconst char pstrUsageDPadRight [] PROGMEM = \"D-pad Right\";\nconst char pstrUsageDPadLeft [] PROGMEM = \"D-pad Left\";\nconst char pstrUsageSystemDock [] PROGMEM = \"Sys Dock\";\nconst char pstrUsageSystemUndock [] PROGMEM = \"Sys Undock\";\nconst char pstrUsageSystemSetup [] PROGMEM = \"Sys Setup\";\nconst char pstrUsageSystemBreak [] PROGMEM = \"Sys Break\";\nconst char pstrUsageSystemDebuggerBreak [] PROGMEM = \"Sys Dbg Brk\";\nconst char pstrUsageApplicationBreak [] PROGMEM = \"App Break\";\nconst char pstrUsageApplicationDebuggerBreak [] PROGMEM = \"App Dbg Brk\";\nconst char pstrUsageSystemSpeakerMute [] PROGMEM = \"Sys Spk Mute\";\nconst char pstrUsageSystemHibernate [] PROGMEM = \"Sys Hiber\";\nconst char pstrUsageSystemDisplayInvert [] PROGMEM = \"Sys Disp Inv\";\nconst char pstrUsageSystemDisplayInternal [] PROGMEM = \"Sys Disp Int\";\nconst char pstrUsageSystemDisplayExternal [] PROGMEM = \"Sys Disp Ext\";\nconst char pstrUsageSystemDisplayBoth [] PROGMEM = \"Sys Disp Both\";\nconst char pstrUsageSystemDisplayDual [] PROGMEM = \"Sys Disp Dual\";\nconst char pstrUsageSystemDisplayToggleIntExt [] PROGMEM = \"Sys Disp Tgl Int/Ext\";\nconst char pstrUsageSystemDisplaySwapPriSec [] PROGMEM = \"Sys Disp Swap Pri/Sec\";\nconst char pstrUsageSystemDisplayLCDAutoscale [] PROGMEM = \"Sys Disp LCD Autoscale\";\n\n// Simulation Controls Page\nconst char pstrUsageFlightSimulationDevice [] PROGMEM = \"Flight Simu Dev\";\nconst char pstrUsageAutomobileSimulationDevice [] PROGMEM = \"Auto Simu Dev\";\nconst char pstrUsageTankSimulationDevice [] PROGMEM = \"Tank Simu Dev\";\nconst char pstrUsageSpaceshipSimulationDevice [] PROGMEM = \"Space Simu Dev\";\nconst char pstrUsageSubmarineSimulationDevice [] PROGMEM = \"Subm Simu Dev\";\nconst char pstrUsageSailingSimulationDevice [] PROGMEM = \"Sail Simu Dev\";\nconst char pstrUsageMotocicleSimulationDevice [] PROGMEM = \"Moto Simu Dev\";\nconst char pstrUsageSportsSimulationDevice [] PROGMEM = \"Sport Simu Dev\";\nconst char pstrUsageAirplaneSimulationDevice [] PROGMEM = \"Airp Simu Dev\";\nconst char pstrUsageHelicopterSimulationDevice [] PROGMEM = \"Heli Simu Dev\";\nconst char pstrUsageMagicCarpetSimulationDevice [] PROGMEM = \"Magic Carpet Simu Dev\";\nconst char pstrUsageBicycleSimulationDevice [] PROGMEM = \"Bike Simu Dev\";\nconst char pstrUsageFlightControlStick [] PROGMEM = \"Flight Ctrl Stick\";\nconst char pstrUsageFlightStick [] PROGMEM = \"Flight Stick\";\nconst char pstrUsageCyclicControl [] PROGMEM = \"Cyclic Ctrl\";\nconst char pstrUsageCyclicTrim [] PROGMEM = \"Cyclic Trim\";\nconst char pstrUsageFlightYoke [] PROGMEM = \"Flight Yoke\";\nconst char pstrUsageTrackControl [] PROGMEM = \"Track Ctrl\";\nconst char pstrUsageAileron [] PROGMEM = \"Aileron\";\nconst char pstrUsageAileronTrim [] PROGMEM = \"Aileron Trim\";\nconst char pstrUsageAntiTorqueControl [] PROGMEM = \"Anti-Torque Ctrl\";\nconst char pstrUsageAutopilotEnable [] PROGMEM = \"Autopilot Enable\";\nconst char pstrUsageChaffRelease [] PROGMEM = \"Chaff Release\";\nconst char pstrUsageCollectiveControl [] PROGMEM = \"Collective Ctrl\";\nconst char pstrUsageDiveBrake [] PROGMEM = \"Dive Brake\";\nconst char pstrUsageElectronicCountermeasures [] PROGMEM = \"El Countermeasures\";\nconst char pstrUsageElevator [] PROGMEM = \"Elevator\";\nconst char pstrUsageElevatorTrim [] PROGMEM = \"Elevator Trim\";\nconst char pstrUsageRudder [] PROGMEM = \"Rudder\";\nconst char pstrUsageThrottle [] PROGMEM = \"Throttle\";\nconst char pstrUsageFlightCommunications [] PROGMEM = \"Flight Comm\";\nconst char pstrUsageFlareRelease [] PROGMEM = \"Flare Release\";\nconst char pstrUsageLandingGear [] PROGMEM = \"Landing Gear\";\nconst char pstrUsageToeBrake [] PROGMEM = \"Toe Brake\";\nconst char pstrUsageTrigger [] PROGMEM = \"Trigger\";\nconst char pstrUsageWeaponsArm [] PROGMEM = \"Weapons Arm\";\nconst char pstrUsageWeaponsSelect [] PROGMEM = \"Weapons Sel\";\nconst char pstrUsageWingFlaps [] PROGMEM = \"Wing Flaps\";\nconst char pstrUsageAccelerator [] PROGMEM = \"Accel\";\nconst char pstrUsageBrake [] PROGMEM = \"Brake\";\nconst char pstrUsageClutch [] PROGMEM = \"Clutch\";\nconst char pstrUsageShifter [] PROGMEM = \"Shifter\";\nconst char pstrUsageSteering [] PROGMEM = \"Steering\";\nconst char pstrUsageTurretDirection [] PROGMEM = \"Turret Dir\";\nconst char pstrUsageBarrelElevation [] PROGMEM = \"Barrel Ele\";\nconst char pstrUsageDivePlane [] PROGMEM = \"Dive Plane\";\nconst char pstrUsageBallast [] PROGMEM = \"Ballast\";\nconst char pstrUsageBicycleCrank [] PROGMEM = \"Bicycle Crank\";\nconst char pstrUsageHandleBars [] PROGMEM = \"Handle Bars\";\nconst char pstrUsageFrontBrake [] PROGMEM = \"Front Brake\";\nconst char pstrUsageRearBrake [] PROGMEM = \"Rear Brake\";\n\n// VR Controls Page\nconst char pstrUsageBelt [] PROGMEM = \"Belt\";\nconst char pstrUsageBodySuit [] PROGMEM = \"Body Suit\";\nconst char pstrUsageFlexor [] PROGMEM = \"Flexor\";\nconst char pstrUsageGlove [] PROGMEM = \"Glove\";\nconst char pstrUsageHeadTracker [] PROGMEM = \"Head Track\";\nconst char pstrUsageHeadMountedDisplay [] PROGMEM = \"Head Disp\";\nconst char pstrUsageHandTracker [] PROGMEM = \"Hand Track\";\nconst char pstrUsageOculometer [] PROGMEM = \"Oculometer\";\nconst char pstrUsageVest [] PROGMEM = \"Vest\";\nconst char pstrUsageAnimatronicDevice [] PROGMEM = \"Animat Dev\";\nconst char pstrUsageStereoEnable [] PROGMEM = \"Stereo Enbl\";\nconst char pstrUsageDisplayEnable [] PROGMEM = \"Display Enbl\";\n\n// Sport Controls Page\nconst char pstrUsageBaseballBat [] PROGMEM = \"Baseball Bat\";\nconst char pstrUsageGolfClub [] PROGMEM = \"Golf Club\";\nconst char pstrUsageRowingMachine [] PROGMEM = \"Rowing Mach\";\nconst char pstrUsageTreadmill [] PROGMEM = \"Treadmill\";\nconst char pstrUsageOar [] PROGMEM = \"Oar\";\nconst char pstrUsageSlope [] PROGMEM = \"Slope\";\nconst char pstrUsageRate [] PROGMEM = \"Rate\";\nconst char pstrUsageStickSpeed [] PROGMEM = \"Stick Speed\";\nconst char pstrUsageStickFaceAngle [] PROGMEM = \"Stick Face Ang\";\nconst char pstrUsageStickHeelToe [] PROGMEM = \"Stick Heel/Toe\";\nconst char pstrUsageStickFollowThough [] PROGMEM = \"Stick Flw Thru\";\nconst char pstrUsageStickTempo [] PROGMEM = \"Stick Tempo\";\nconst char pstrUsageStickType [] PROGMEM = \"Stick Type\";\nconst char pstrUsageStickHeight [] PROGMEM = \"Stick Hght\";\nconst char pstrUsagePutter [] PROGMEM = \"Putter\";\nconst char pstrUsage1Iron [] PROGMEM = \"1 Iron\";\nconst char pstrUsage2Iron [] PROGMEM = \"2 Iron\";\nconst char pstrUsage3Iron [] PROGMEM = \"3 Iron\";\nconst char pstrUsage4Iron [] PROGMEM = \"4 Iron\";\nconst char pstrUsage5Iron [] PROGMEM = \"5 Iron\";\nconst char pstrUsage6Iron [] PROGMEM = \"6 Iron\";\nconst char pstrUsage7Iron [] PROGMEM = \"7 Iron\";\nconst char pstrUsage8Iron [] PROGMEM = \"8 Iron\";\nconst char pstrUsage9Iron [] PROGMEM = \"9 Iron\";\nconst char pstrUsage10Iron [] PROGMEM = \"10 Iron\";\nconst char pstrUsage11Iron [] PROGMEM = \"11 Iron\";\nconst char pstrUsageSandWedge [] PROGMEM = \"Sand Wedge\";\nconst char pstrUsageLoftWedge [] PROGMEM = \"Loft Wedge\";\nconst char pstrUsagePowerWedge [] PROGMEM = \"Pwr Wedge\";\nconst char pstrUsage1Wood [] PROGMEM = \"1 Wood\";\nconst char pstrUsage3Wood [] PROGMEM = \"3 Wood\";\nconst char pstrUsage5Wood [] PROGMEM = \"5 Wood\";\nconst char pstrUsage7Wood [] PROGMEM = \"7 Wood\";\nconst char pstrUsage9Wood [] PROGMEM = \"9 Wood\";\n\n// Game Controls Page\nconst char pstrUsage3DGameController [] PROGMEM = \"3D Game Ctrl\";\nconst char pstrUsagePinballDevice [] PROGMEM = \"Pinball Dev\";\nconst char pstrUsageGunDevice [] PROGMEM = \"Gun Dev\";\nconst char pstrUsagePointOfView [] PROGMEM = \"POV\";\nconst char pstrUsageTurnRightLeft [] PROGMEM = \"Turn Right Left\";\nconst char pstrUsagePitchForwardBackward [] PROGMEM = \"Pitch Fwd/Back\";\nconst char pstrUsageRollRightLeft [] PROGMEM = \"Roll Right/Left\";\nconst char pstrUsageMoveRightLeft [] PROGMEM = \"Move Right/Left\";\nconst char pstrUsageMoveForwardBackward [] PROGMEM = \"Move Fwd/Back\";\nconst char pstrUsageMoveUpDown [] PROGMEM = \"Move Up/Down\";\nconst char pstrUsageLeanRightLeft [] PROGMEM = \"Lean Right/Left\";\nconst char pstrUsageLeanForwardBackward [] PROGMEM = \"Lean Fwd/Back\";\nconst char pstrUsageHeightOfPOV [] PROGMEM = \"Height of POV\";\nconst char pstrUsageFlipper [] PROGMEM = \"Flipper\";\nconst char pstrUsageSecondaryFlipper [] PROGMEM = \"Second Flipper\";\nconst char pstrUsageBump [] PROGMEM = \"Bump\";\nconst char pstrUsageNewGame [] PROGMEM = \"New Game\";\nconst char pstrUsageShootBall [] PROGMEM = \"Shoot Ball\";\nconst char pstrUsagePlayer [] PROGMEM = \"Player\";\nconst char pstrUsageGunBolt [] PROGMEM = \"Gun Bolt\";\nconst char pstrUsageGunClip [] PROGMEM = \"Gun Clip\";\nconst char pstrUsageGunSelector [] PROGMEM = \"Gun Sel\";\nconst char pstrUsageGunSingleShot [] PROGMEM = \"Gun Sngl Shot\";\nconst char pstrUsageGunBurst [] PROGMEM = \"Gun Burst\";\nconst char pstrUsageGunAutomatic [] PROGMEM = \"Gun Auto\";\nconst char pstrUsageGunSafety [] PROGMEM = \"Gun Safety\";\nconst char pstrUsageGamepadFireJump [] PROGMEM = \"Gamepad Fire/Jump\";\nconst char pstrUsageGamepadTrigger [] PROGMEM = \"Gamepad Trig\";\n\n// Generic Device Controls Page\nconst char pstrUsageBatteryStrength [] PROGMEM = \"Bat Strength\";\nconst char pstrUsageWirelessChannel [] PROGMEM = \"Wireless Ch\";\nconst char pstrUsageWirelessID [] PROGMEM = \"Wireless ID\";\nconst char pstrUsageDiscoverWirelessControl [] PROGMEM = \"Discover Wireless Ctrl\";\nconst char pstrUsageSecurityCodeCharEntered [] PROGMEM = \"Sec Code Char Entrd\";\nconst char pstrUsageSecurityCodeCharErased [] PROGMEM = \"Sec Code Char Erased\";\nconst char pstrUsageSecurityCodeCleared [] PROGMEM = \"Sec Code Cleared\";\n\n// LED Page\nconst char pstrUsageNumLock [] PROGMEM = \"Num Lock\";\nconst char pstrUsageCapsLock [] PROGMEM = \"Caps Lock\";\nconst char pstrUsageScrollLock [] PROGMEM = \"Scroll Lock\";\nconst char pstrUsageCompose [] PROGMEM = \"Compose\";\nconst char pstrUsageKana [] PROGMEM = \"Kana\";\nconst char pstrUsagePower [] PROGMEM = \"Pwr\";\nconst char pstrUsageShift [] PROGMEM = \"Shift\";\nconst char pstrUsageDoNotDisturb [] PROGMEM = \"DND\";\nconst char pstrUsageMute [] PROGMEM = \"Mute\";\nconst char pstrUsageToneEnable [] PROGMEM = \"Tone Enbl\";\nconst char pstrUsageHighCutFilter [] PROGMEM = \"High Cut Fltr\";\nconst char pstrUsageLowCutFilter [] PROGMEM = \"Low Cut Fltr\";\nconst char pstrUsageEqualizerEnable [] PROGMEM = \"Eq Enbl\";\nconst char pstrUsageSoundFieldOn [] PROGMEM = \"Sound Field On\";\nconst char pstrUsageSurroundOn [] PROGMEM = \"Surround On\";\nconst char pstrUsageRepeat [] PROGMEM = \"Repeat\";\nconst char pstrUsageStereo [] PROGMEM = \"Stereo\";\nconst char pstrUsageSamplingRateDetect [] PROGMEM = \"Smpl Rate Detect\";\nconst char pstrUsageSpinning [] PROGMEM = \"Spinning\";\nconst char pstrUsageCAV [] PROGMEM = \"CAV\";\nconst char pstrUsageCLV [] PROGMEM = \"CLV\";\nconst char pstrUsageRecordingFormatDetect [] PROGMEM = \"Rec Format Detect\";\nconst char pstrUsageOffHook [] PROGMEM = \"Off Hook\";\nconst char pstrUsageRing [] PROGMEM = \"Ring\";\nconst char pstrUsageMessageWaiting [] PROGMEM = \"Msg Wait\";\nconst char pstrUsageDataMode [] PROGMEM = \"Data Mode\";\nconst char pstrUsageBatteryOperation [] PROGMEM = \"Bat Op\";\nconst char pstrUsageBatteryOK [] PROGMEM = \"Bat OK\";\nconst char pstrUsageBatteryLow [] PROGMEM = \"Bat Low\";\nconst char pstrUsageSpeaker [] PROGMEM = \"Speaker\";\nconst char pstrUsageHeadSet [] PROGMEM = \"Head Set\";\nconst char pstrUsageHold [] PROGMEM = \"Hold\";\nconst char pstrUsageMicrophone [] PROGMEM = \"Mic\";\nconst char pstrUsageCoverage [] PROGMEM = \"Coverage\";\nconst char pstrUsageNightMode [] PROGMEM = \"Night Mode\";\nconst char pstrUsageSendCalls [] PROGMEM = \"Send Calls\";\nconst char pstrUsageCallPickup [] PROGMEM = \"Call Pickup\";\nconst char pstrUsageConference [] PROGMEM = \"Conf\";\nconst char pstrUsageStandBy [] PROGMEM = \"Stand-by\";\nconst char pstrUsageCameraOn [] PROGMEM = \"Cam On\";\nconst char pstrUsageCameraOff [] PROGMEM = \"Cam Off\";\nconst char pstrUsageOnLine [] PROGMEM = \"On-Line\";\nconst char pstrUsageOffLine [] PROGMEM = \"Off-Line\";\nconst char pstrUsageBusy [] PROGMEM = \"Busy\";\nconst char pstrUsageReady [] PROGMEM = \"Ready\";\nconst char pstrUsagePaperOut [] PROGMEM = \"Paper Out\";\nconst char pstrUsagePaperJam [] PROGMEM = \"Paper Jam\";\nconst char pstrUsageRemote [] PROGMEM = \"Remote\";\nconst char pstrUsageForward [] PROGMEM = \"Fwd\";\nconst char pstrUsageReverse [] PROGMEM = \"Rev\";\nconst char pstrUsageStop [] PROGMEM = \"Stop\";\nconst char pstrUsageRewind [] PROGMEM = \"Rewind\";\nconst char pstrUsageFastForward [] PROGMEM = \"Fast Fwd\";\nconst char pstrUsagePlay [] PROGMEM = \"Play\";\nconst char pstrUsagePause [] PROGMEM = \"Pause\";\nconst char pstrUsageRecord [] PROGMEM = \"Rec\";\nconst char pstrUsageError [] PROGMEM = \"Error\";\nconst char pstrUsageSelectedIndicator [] PROGMEM = \"Usage Sel Ind\";\nconst char pstrUsageInUseIndicator [] PROGMEM = \"Usage In Use Ind\";\nconst char pstrUsageMultiModeIndicator [] PROGMEM = \"Usage Multi Mode Ind\";\nconst char pstrUsageIndicatorOn [] PROGMEM = \"Ind On\";\nconst char pstrUsageIndicatorFlash [] PROGMEM = \"Ind Flash\";\nconst char pstrUsageIndicatorSlowBlink [] PROGMEM = \"Ind Slow Blk\";\nconst char pstrUsageIndicatorFastBlink [] PROGMEM = \"Ind Fast Blk\";\nconst char pstrUsageIndicatorOff [] PROGMEM = \"Ind Off\";\nconst char pstrUsageFlashOnTime [] PROGMEM = \"Flash On Time\";\nconst char pstrUsageSlowBlinkOnTime [] PROGMEM = \"Slow Blk On Time\";\nconst char pstrUsageSlowBlinkOffTime [] PROGMEM = \"Slow Blk Off Time\";\nconst char pstrUsageFastBlinkOnTime [] PROGMEM = \"Fast Blk On Time\";\nconst char pstrUsageFastBlinkOffTime [] PROGMEM = \"Fast Blk Off Time\";\nconst char pstrUsageIndicatorColor [] PROGMEM = \"Usage Ind Color\";\nconst char pstrUsageIndicatorRed [] PROGMEM = \"Ind Red\";\nconst char pstrUsageIndicatorGreen [] PROGMEM = \"Ind Green\";\nconst char pstrUsageIndicatorAmber [] PROGMEM = \"Ind Amber\";\nconst char pstrUsageGenericIndicator [] PROGMEM = \"Gen Ind\";\nconst char pstrUsageSystemSuspend [] PROGMEM = \"Sys Suspend\";\nconst char pstrUsageExternalPowerConnected [] PROGMEM = \"Ext Pwr Conn\";\n\n// Telephony Usage Page\nconst char pstrUsagePhone [] PROGMEM = \"Phone\";\nconst char pstrUsageAnsweringMachine [] PROGMEM = \"Answ Mach\";\nconst char pstrUsageMessageControls [] PROGMEM = \"Msg Ctrls\";\nconst char pstrUsageHandset [] PROGMEM = \"Handset\";\nconst char pstrUsageHeadset [] PROGMEM = \"Headset\";\nconst char pstrUsageTelephonyKeyPad [] PROGMEM = \"Tel Key Pad\";\nconst char pstrUsageProgrammableButton [] PROGMEM = \"Prog Button\";\nconst char pstrUsageHookSwitch [] PROGMEM = \"Hook Sw\";\nconst char pstrUsageFlash [] PROGMEM = \"Flash\";\nconst char pstrUsageFeature [] PROGMEM = \"Feature\";\n//const char pstrUsageHold [] PROGMEM = \"Hold\";\nconst char pstrUsageRedial [] PROGMEM = \"Redial\";\nconst char pstrUsageTransfer [] PROGMEM = \"Transfer\";\nconst char pstrUsageDrop [] PROGMEM = \"Drop\";\nconst char pstrUsagePark [] PROGMEM = \"Park\";\nconst char pstrUsageForwardCalls [] PROGMEM = \"Fwd Calls\";\nconst char pstrUsageAlternateFunction [] PROGMEM = \"Alt Func\";\nconst char pstrUsageLine [] PROGMEM = \"Line\";\nconst char pstrUsageSpeakerPhone [] PROGMEM = \"Spk Phone\";\n//const char pstrUsageConference [] PROGMEM = \"Conference\";\nconst char pstrUsageRingEnable [] PROGMEM = \"Ring Enbl\";\nconst char pstrUsageRingSelect [] PROGMEM = \"Ring Sel\";\nconst char pstrUsagePhoneMute [] PROGMEM = \"Phone Mute\";\nconst char pstrUsageCallerID [] PROGMEM = \"Caller ID\";\nconst char pstrUsageSend [] PROGMEM = \"Send\";\nconst char pstrUsageSpeedDial [] PROGMEM = \"Speed Dial\";\nconst char pstrUsageStoreNumber [] PROGMEM = \"Store Num\";\nconst char pstrUsageRecallNumber [] PROGMEM = \"Recall Num\";\nconst char pstrUsagePhoneDirectory [] PROGMEM = \"Phone Dir\";\nconst char pstrUsageVoiceMail [] PROGMEM = \"Voice Mail\";\nconst char pstrUsageScreenCalls [] PROGMEM = \"Screen Calls\";\n//const char pstrUsageDoNotDisturb [] PROGMEM = \"Do Not Disturb\";\nconst char pstrUsageMessage [] PROGMEM = \"Msg\";\nconst char pstrUsageAnswerOnOff [] PROGMEM = \"Answer On/Off\";\nconst char pstrUsageInsideDialTone [] PROGMEM = \"Inside Dial Tone\";\nconst char pstrUsageOutsideDialTone [] PROGMEM = \"Outside Dial Tone\";\nconst char pstrUsageInsideRingTone [] PROGMEM = \"Inside Ring Tone\";\nconst char pstrUsageOutsideRingTone [] PROGMEM = \"Outside Ring Tone\";\nconst char pstrUsagePriorityRingTone [] PROGMEM = \"Prior Ring Tone\";\nconst char pstrUsageInsideRingback [] PROGMEM = \"Inside Ringback\";\nconst char pstrUsagePriorityRingback [] PROGMEM = \"Priority Ringback\";\nconst char pstrUsageLineBusyTone [] PROGMEM = \"Ln Busy Tone\";\nconst char pstrUsageReorderTone [] PROGMEM = \"Reorder Tone\";\nconst char pstrUsageCallWaitingTone [] PROGMEM = \"Call Wait Tone\";\nconst char pstrUsageConfirmationTone1 [] PROGMEM = \"Cnfrm Tone1\";\nconst char pstrUsageConfirmationTone2 [] PROGMEM = \"Cnfrm Tone2\";\nconst char pstrUsageTonesOff [] PROGMEM = \"Tones Off\";\nconst char pstrUsageOutsideRingback [] PROGMEM = \"Outside Ringback\";\nconst char pstrUsageRinger [] PROGMEM = \"Ringer\";\nconst char pstrUsagePhoneKey0 [] PROGMEM = \"0\";\nconst char pstrUsagePhoneKey1 [] PROGMEM = \"1\";\nconst char pstrUsagePhoneKey2 [] PROGMEM = \"2\";\nconst char pstrUsagePhoneKey3 [] PROGMEM = \"3\";\nconst char pstrUsagePhoneKey4 [] PROGMEM = \"4\";\nconst char pstrUsagePhoneKey5 [] PROGMEM = \"5\";\nconst char pstrUsagePhoneKey6 [] PROGMEM = \"6\";\nconst char pstrUsagePhoneKey7 [] PROGMEM = \"7\";\nconst char pstrUsagePhoneKey8 [] PROGMEM = \"8\";\nconst char pstrUsagePhoneKey9 [] PROGMEM = \"9\";\nconst char pstrUsagePhoneKeyStar [] PROGMEM = \"*\";\nconst char pstrUsagePhoneKeyPound [] PROGMEM = \"#\";\nconst char pstrUsagePhoneKeyA [] PROGMEM = \"A\";\nconst char pstrUsagePhoneKeyB [] PROGMEM = \"B\";\nconst char pstrUsagePhoneKeyC [] PROGMEM = \"C\";\nconst char pstrUsagePhoneKeyD [] PROGMEM = \"D\";\n\n// Consumer Usage Page\nconst char pstrUsageConsumerControl [] PROGMEM = \"Consumer Ctrl\";\nconst char pstrUsageNumericKeyPad [] PROGMEM = \"Num Key Pad\";\n//const char pstrUsageProgrammableButton [] PROGMEM = \"Prog Btn\";\n//const char pstrUsageMicrophone [] PROGMEM = \"Mic\";\nconst char pstrUsageHeadphone [] PROGMEM = \"Headphone\";\nconst char pstrUsageGraphicEqualizer [] PROGMEM = \"Graph Eq\";\nconst char pstrUsagePlus10 [] PROGMEM = \"+10\";\nconst char pstrUsagePlus100 [] PROGMEM = \"+100\";\nconst char pstrUsageAMPM [] PROGMEM = \"AM/PM\";\n//const char pstrUsagePower [] PROGMEM = \"Pwr\";\nconst char pstrUsageReset [] PROGMEM = \"Reset\";\nconst char pstrUsageSleep [] PROGMEM = \"Sleep\";\nconst char pstrUsageSleepAfter [] PROGMEM = \"Sleep After\";\nconst char pstrUsageSleepMode [] PROGMEM = \"Sleep Mode\";\nconst char pstrUsageIllumination [] PROGMEM = \"Illumin\";\nconst char pstrUsageFunctionButtons [] PROGMEM = \"Func Btns\";\nconst char pstrUsageMenu [] PROGMEM = \"Menu\";\nconst char pstrUsageMenuPick [] PROGMEM = \"Menu Pick\";\nconst char pstrUsageMenuUp [] PROGMEM = \"Menu Up\";\nconst char pstrUsageMenuDown [] PROGMEM = \"Menu Down\";\nconst char pstrUsageMenuLeft [] PROGMEM = \"Menu Left\";\nconst char pstrUsageMenuRight [] PROGMEM = \"Menu Right\";\nconst char pstrUsageMenuEscape [] PROGMEM = \"Menu Esc\";\nconst char pstrUsageMenuValueIncrease [] PROGMEM = \"Menu Val Inc\";\nconst char pstrUsageMenuValueDecrease [] PROGMEM = \"Menu Val Dec\";\nconst char pstrUsageDataOnScreen [] PROGMEM = \"Data On Scr\";\nconst char pstrUsageClosedCaption [] PROGMEM = \"Closed Cptn\";\nconst char pstrUsageClosedCaptionSelect [] PROGMEM = \"Closed Cptn Sel\";\nconst char pstrUsageVCRTV [] PROGMEM = \"VCR/TV\";\nconst char pstrUsageBroadcastMode [] PROGMEM = \"Brdcast Mode\";\nconst char pstrUsageSnapshot [] PROGMEM = \"Snapshot\";\nconst char pstrUsageStill [] PROGMEM = \"Still\";\nconst char pstrUsageSelection [] PROGMEM = \"Sel\";\nconst char pstrUsageAssignSelection [] PROGMEM = \"Assign Sel\";\nconst char pstrUsageModeStep [] PROGMEM = \"Mode Step\";\nconst char pstrUsageRecallLast [] PROGMEM = \"Recall Last\";\nconst char pstrUsageEnterChannel [] PROGMEM = \"Entr Channel\";\nconst char pstrUsageOrderMovie [] PROGMEM = \"Ord Movie\";\nconst char pstrUsageChannel [] PROGMEM = \"Channel\";\nconst char pstrUsageMediaSelection [] PROGMEM = \"Med Sel\";\nconst char pstrUsageMediaSelectComputer [] PROGMEM = \"Med Sel Comp\";\nconst char pstrUsageMediaSelectTV [] PROGMEM = \"Med Sel TV\";\nconst char pstrUsageMediaSelectWWW [] PROGMEM = \"Med Sel WWW\";\nconst char pstrUsageMediaSelectDVD [] PROGMEM = \"Med Sel DVD\";\nconst char pstrUsageMediaSelectTelephone [] PROGMEM = \"Med Sel Tel\";\nconst char pstrUsageMediaSelectProgramGuide [] PROGMEM = \"Med Sel PG\";\nconst char pstrUsageMediaSelectVideoPhone [] PROGMEM = \"Med Sel Vid\";\nconst char pstrUsageMediaSelectGames [] PROGMEM = \"Med Sel Games\";\nconst char pstrUsageMediaSelectMessages [] PROGMEM = \"Med Sel Msg\";\nconst char pstrUsageMediaSelectCD [] PROGMEM = \"Med Sel CD\";\nconst char pstrUsageMediaSelectVCR [] PROGMEM = \"Med Sel VCR\";\nconst char pstrUsageMediaSelectTuner [] PROGMEM = \"Med Sel Tuner\";\nconst char pstrUsageQuit [] PROGMEM = \"Quit\";\nconst char pstrUsageHelp [] PROGMEM = \"Help\";\nconst char pstrUsageMediaSelectTape [] PROGMEM = \"Med Sel Tape\";\nconst char pstrUsageMediaSelectCable [] PROGMEM = \"Med Sel Cbl\";\nconst char pstrUsageMediaSelectSatellite [] PROGMEM = \"Med Sel Sat\";\nconst char pstrUsageMediaSelectSecurity [] PROGMEM = \"Med Sel Secur\";\nconst char pstrUsageMediaSelectHome [] PROGMEM = \"Med Sel Home\";\nconst char pstrUsageMediaSelectCall [] PROGMEM = \"Med Sel Call\";\nconst char pstrUsageChannelIncrement [] PROGMEM = \"Ch Inc\";\nconst char pstrUsageChannelDecrement [] PROGMEM = \"Ch Dec\";\nconst char pstrUsageMediaSelectSAP [] PROGMEM = \"Med Sel SAP\";\nconst char pstrUsageVCRPlus [] PROGMEM = \"VCR+\";\nconst char pstrUsageOnce [] PROGMEM = \"Once\";\nconst char pstrUsageDaily [] PROGMEM = \"Daily\";\nconst char pstrUsageWeekly [] PROGMEM = \"Weekly\";\nconst char pstrUsageMonthly [] PROGMEM = \"Monthly\";\n//const char pstrUsagePlay [] PROGMEM = \"Play\";\n//const char pstrUsagePause [] PROGMEM = \"Pause\";\n//const char pstrUsageRecord [] PROGMEM = \"Rec\";\n//const char pstrUsageFastForward [] PROGMEM = \"FF\";\n//const char pstrUsageRewind [] PROGMEM = \"Rewind\";\nconst char pstrUsageScanNextTrack [] PROGMEM = \"Next Track\";\nconst char pstrUsageScanPreviousTrack [] PROGMEM = \"Prev Track\";\n//const char pstrUsageStop [] PROGMEM = \"Stop\";\nconst char pstrUsageEject [] PROGMEM = \"Eject\";\nconst char pstrUsageRandomPlay [] PROGMEM = \"Random\";\nconst char pstrUsageSelectDisk [] PROGMEM = \"Sel Disk\";\nconst char pstrUsageEnterDisk [] PROGMEM = \"Ent Disk\";\n//const char pstrUsageRepeat [] PROGMEM = \"Repeat\";\nconst char pstrUsageTracking [] PROGMEM = \"Tracking\";\nconst char pstrUsageTrackNormal [] PROGMEM = \"Trk Norm\";\nconst char pstrUsageSlowTracking [] PROGMEM = \"Slow Trk\";\nconst char pstrUsageFrameForward [] PROGMEM = \"Frm Fwd\";\nconst char pstrUsageFrameBackwards [] PROGMEM = \"Frm Back\";\nconst char pstrUsageMark [] PROGMEM = \"Mark\";\nconst char pstrUsageClearMark [] PROGMEM = \"Clr Mark\";\nconst char pstrUsageRepeatFromMark [] PROGMEM = \"Rpt Mark\";\nconst char pstrUsageReturnToMark [] PROGMEM = \"Ret to Mark\";\nconst char pstrUsageSearchMarkForward [] PROGMEM = \"Search Mark Fwd\";\nconst char pstrUsageSearchMarkBackwards [] PROGMEM = \"Search Mark Back\";\nconst char pstrUsageCounterReset [] PROGMEM = \"Counter Reset\";\nconst char pstrUsageShowCounter [] PROGMEM = \"Show Counter\";\nconst char pstrUsageTrackingIncrement [] PROGMEM = \"Track Inc\";\nconst char pstrUsageTrackingDecrement [] PROGMEM = \"Track Dec\";\nconst char pstrUsageStopEject [] PROGMEM = \"Stop/Eject\";\nconst char pstrUsagePlayPause [] PROGMEM = \"Play/Pause\";\nconst char pstrUsagePlaySkip [] PROGMEM = \"Play/Skip\";\nconst char pstrUsageVolume [] PROGMEM = \"Vol\";\nconst char pstrUsageBalance [] PROGMEM = \"Balance\";\n//const char pstrUsageMute [] PROGMEM = \"Mute\";\nconst char pstrUsageBass [] PROGMEM = \"Bass\";\nconst char pstrUsageTreble [] PROGMEM = \"Treble\";\nconst char pstrUsageBassBoost [] PROGMEM = \"Bass Boost\";\nconst char pstrUsageSurroundMode [] PROGMEM = \"Surround\";\nconst char pstrUsageLoudness [] PROGMEM = \"Loud\";\nconst char pstrUsageMPX [] PROGMEM = \"MPX\";\nconst char pstrUsageVolumeIncrement [] PROGMEM = \"Vol Inc\";\nconst char pstrUsageVolumeDecrement [] PROGMEM = \"Vol Dec\";\nconst char pstrUsageSpeedSelect [] PROGMEM = \"Speed\";\nconst char pstrUsagePlaybackSpeed [] PROGMEM = \"Play Speed\";\nconst char pstrUsageStandardPlay [] PROGMEM = \"Std Play\";\nconst char pstrUsageLongPlay [] PROGMEM = \"Long Play\";\nconst char pstrUsageExtendedPlay [] PROGMEM = \"Ext Play\";\nconst char pstrUsageSlow [] PROGMEM = \"Slow\";\nconst char pstrUsageFanEnable [] PROGMEM = \"Fan Enbl\";\nconst char pstrUsageFanSpeed [] PROGMEM = \"Fan Speed\";\nconst char pstrUsageLightEnable [] PROGMEM = \"Light Enbl\";\nconst char pstrUsageLightIlluminationLevel [] PROGMEM = \"Light Illum Lev\";\nconst char pstrUsageClimateControlEnable [] PROGMEM = \"Climate Enbl\";\nconst char pstrUsageRoomTemperature [] PROGMEM = \"Room Temp\";\nconst char pstrUsageSecurityEnable [] PROGMEM = \"Secur Enbl\";\nconst char pstrUsageFireAlarm [] PROGMEM = \"Fire Alm\";\nconst char pstrUsagePoliceAlarm [] PROGMEM = \"Police Alm\";\nconst char pstrUsageProximity [] PROGMEM = \"Prox\";\nconst char pstrUsageMotion [] PROGMEM = \"Motion\";\nconst char pstrUsageDuresAlarm [] PROGMEM = \"Dures Alm\";\nconst char pstrUsageHoldupAlarm [] PROGMEM = \"Holdup Alm\";\nconst char pstrUsageMedicalAlarm [] PROGMEM = \"Med Alm\";\nconst char pstrUsageBalanceRight [] PROGMEM = \"Balance Right\";\nconst char pstrUsageBalanceLeft [] PROGMEM = \"Balance Left\";\nconst char pstrUsageBassIncrement [] PROGMEM = \"Bass Inc\";\nconst char pstrUsageBassDecrement [] PROGMEM = \"Bass Dec\";\nconst char pstrUsageTrebleIncrement [] PROGMEM = \"Treble Inc\";\nconst char pstrUsageTrebleDecrement [] PROGMEM = \"Treble Dec\";\nconst char pstrUsageSpeakerSystem [] PROGMEM = \"Spk Sys\";\nconst char pstrUsageChannelLeft [] PROGMEM = \"Ch Left\";\nconst char pstrUsageChannelRight [] PROGMEM = \"Ch Right\";\nconst char pstrUsageChannelCenter [] PROGMEM = \"Ch Center\";\nconst char pstrUsageChannelFront [] PROGMEM = \"Ch Front\";\nconst char pstrUsageChannelCenterFront [] PROGMEM = \"Ch Cntr Front\";\nconst char pstrUsageChannelSide [] PROGMEM = \"Ch Side\";\nconst char pstrUsageChannelSurround [] PROGMEM = \"Ch Surround\";\nconst char pstrUsageChannelLowFreqEnhancement [] PROGMEM = \"Ch Low Freq Enh\";\nconst char pstrUsageChannelTop [] PROGMEM = \"Ch Top\";\nconst char pstrUsageChannelUnknown [] PROGMEM = \"Ch Unk\";\nconst char pstrUsageSubChannel [] PROGMEM = \"Sub-ch\";\nconst char pstrUsageSubChannelIncrement [] PROGMEM = \"Sub-ch Inc\";\nconst char pstrUsageSubChannelDecrement [] PROGMEM = \"Sub-ch Dec\";\nconst char pstrUsageAlternateAudioIncrement [] PROGMEM = \"Alt Aud Inc\";\nconst char pstrUsageAlternateAudioDecrement [] PROGMEM = \"Alt Aud Dec\";\nconst char pstrUsageApplicationLaunchButtons [] PROGMEM = \"App Launch Btns\";\nconst char pstrUsageALLaunchButtonConfigTool [] PROGMEM = \"AL Launch Conf Tl\";\nconst char pstrUsageALProgrammableButton [] PROGMEM = \"AL Pgm Btn\";\nconst char pstrUsageALConsumerControlConfig [] PROGMEM = \"AL Cons Ctrl Cfg\";\nconst char pstrUsageALWordProcessor [] PROGMEM = \"AL Word Proc\";\nconst char pstrUsageALTextEditor [] PROGMEM = \"AL Txt Edtr\";\nconst char pstrUsageALSpreadsheet [] PROGMEM = \"AL Sprdsheet\";\nconst char pstrUsageALGraphicsEditor [] PROGMEM = \"AL Graph Edtr\";\nconst char pstrUsageALPresentationApp [] PROGMEM = \"AL Present App\";\nconst char pstrUsageALDatabaseApp [] PROGMEM = \"AL DB App\";\nconst char pstrUsageALEmailReader [] PROGMEM = \"AL E-mail Rdr\";\nconst char pstrUsageALNewsreader [] PROGMEM = \"AL Newsrdr\";\nconst char pstrUsageALVoicemail [] PROGMEM = \"AL Voicemail\";\nconst char pstrUsageALContactsAddressBook [] PROGMEM = \"AL Addr Book\";\nconst char pstrUsageALCalendarSchedule [] PROGMEM = \"AL Clndr/Schdlr\";\nconst char pstrUsageALTaskProjectManager [] PROGMEM = \"AL Task/Prj Mgr\";\nconst char pstrUsageALLogJournalTimecard [] PROGMEM = \"AL Log/Jrnl/Tmcrd\";\nconst char pstrUsageALCheckbookFinance [] PROGMEM = \"AL Chckbook/Fin\";\nconst char pstrUsageALCalculator [] PROGMEM = \"AL Calc\";\nconst char pstrUsageALAVCapturePlayback [] PROGMEM = \"AL A/V Capt/Play\";\nconst char pstrUsageALLocalMachineBrowser [] PROGMEM = \"AL Loc Mach Brow\";\nconst char pstrUsageALLANWANBrow [] PROGMEM = \"AL LAN/WAN Brow\";\nconst char pstrUsageALInternetBrowser [] PROGMEM = \"AL I-net Brow\";\nconst char pstrUsageALRemoteNetISPConnect [] PROGMEM = \"AL Rem Net Con\";\nconst char pstrUsageALNetworkConference [] PROGMEM = \"AL Net Conf\";\nconst char pstrUsageALNetworkChat [] PROGMEM = \"AL Net Chat\";\nconst char pstrUsageALTelephonyDialer [] PROGMEM = \"AL Tel/Dial\";\nconst char pstrUsageALLogon [] PROGMEM = \"AL Logon\";\nconst char pstrUsageALLogoff [] PROGMEM = \"AL Logoff\";\nconst char pstrUsageALLogonLogoff [] PROGMEM = \"AL Logon/Logoff\";\nconst char pstrUsageALTermLockScrSav [] PROGMEM = \"AL Term Lock/Scr Sav\";\nconst char pstrUsageALControlPannel [] PROGMEM = \"AL Ctrl Pan\";\nconst char pstrUsageALCommandLineProcessorRun [] PROGMEM = \"AL Cmd/Run\";\nconst char pstrUsageALProcessTaskManager [] PROGMEM = \"AL Task Mgr\";\nconst char pstrUsageALSelectTaskApplication [] PROGMEM = \"AL Sel App\";\nconst char pstrUsageALNextTaskApplication [] PROGMEM = \"AL Next App\";\nconst char pstrUsageALPreviousTaskApplication [] PROGMEM = \"AL Prev App\";\nconst char pstrUsageALPreemptiveHaltTaskApp [] PROGMEM = \"AL Prmpt Halt App\";\nconst char pstrUsageALIntegratedHelpCenter [] PROGMEM = \"AL Hlp Cntr\";\nconst char pstrUsageALDocuments [] PROGMEM = \"AL Docs\";\nconst char pstrUsageALThesaurus [] PROGMEM = \"AL Thsrs\";\nconst char pstrUsageALDictionary [] PROGMEM = \"AL Dict\";\nconst char pstrUsageALDesktop [] PROGMEM = \"AL Desktop\";\nconst char pstrUsageALSpellCheck [] PROGMEM = \"AL Spell Chk\";\nconst char pstrUsageALGrammarCheck [] PROGMEM = \"AL Gram Chk\";\nconst char pstrUsageALWirelessStatus [] PROGMEM = \"AL Wireless Sts\";\nconst char pstrUsageALKeyboardLayout [] PROGMEM = \"AL Kbd Layout\";\nconst char pstrUsageALVirusProtection [] PROGMEM = \"AL Vir Protect\";\nconst char pstrUsageALEncryption [] PROGMEM = \"AL Encrypt\";\nconst char pstrUsageALScreenSaver [] PROGMEM = \"AL Scr Sav\";\nconst char pstrUsageALAlarms [] PROGMEM = \"AL Alarms\";\nconst char pstrUsageALClock [] PROGMEM = \"AL Clock\";\nconst char pstrUsageALFileBrowser [] PROGMEM = \"AL File Brow\";\nconst char pstrUsageALPowerStatus [] PROGMEM = \"AL Pwr Sts\";\nconst char pstrUsageALImageBrowser [] PROGMEM = \"AL Img Brow\";\nconst char pstrUsageALAudioBrowser [] PROGMEM = \"AL Aud Brow\";\nconst char pstrUsageALMovieBrowser [] PROGMEM = \"AL Mov Brow\";\nconst char pstrUsageALDigitalRightsManager [] PROGMEM = \"AL Dig Rights Mgr\";\nconst char pstrUsageALDigitalWallet [] PROGMEM = \"AL Dig Wallet\";\nconst char pstrUsageALInstantMessaging [] PROGMEM = \"AL Inst Msg\";\nconst char pstrUsageALOEMFeaturesBrowser [] PROGMEM = \"AL OEM Tips Brow\";\nconst char pstrUsageALOEMHelp [] PROGMEM = \"AL OEM Hlp\";\nconst char pstrUsageALOnlineCommunity [] PROGMEM = \"AL Online Com\";\nconst char pstrUsageALEntertainmentContentBrow [] PROGMEM = \"AL Ent Cont Brow\";\nconst char pstrUsageALOnlineShoppingBrowser [] PROGMEM = \"AL Online Shop Brow\";\nconst char pstrUsageALSmartCardInfoHelp [] PROGMEM = \"AL SmartCard Inf\";\nconst char pstrUsageALMarketMonitorFinBrowser [] PROGMEM = \"AL Market Brow\";\nconst char pstrUsageALCustomCorpNewsBrowser [] PROGMEM = \"AL Cust Corp News Brow\";\nconst char pstrUsageALOnlineActivityBrowser [] PROGMEM = \"AL Online Act Brow\";\nconst char pstrUsageALResearchSearchBrowser [] PROGMEM = \"AL Search Brow\";\nconst char pstrUsageALAudioPlayer [] PROGMEM = \"AL Aud Player\";\nconst char pstrUsageGenericGUIAppControls [] PROGMEM = \"Gen GUI App Ctrl\";\nconst char pstrUsageACNew [] PROGMEM = \"AC New\";\nconst char pstrUsageACOpen [] PROGMEM = \"AC Open\";\nconst char pstrUsageACClose [] PROGMEM = \"AC Close\";\nconst char pstrUsageACExit [] PROGMEM = \"AC Exit\";\nconst char pstrUsageACMaximize [] PROGMEM = \"AC Max\";\nconst char pstrUsageACMinimize [] PROGMEM = \"AC Min\";\nconst char pstrUsageACSave [] PROGMEM = \"AC Save\";\nconst char pstrUsageACPrint [] PROGMEM = \"AC Print\";\nconst char pstrUsageACProperties [] PROGMEM = \"AC Prop\";\nconst char pstrUsageACUndo [] PROGMEM = \"AC Undo\";\nconst char pstrUsageACCopy [] PROGMEM = \"AC Copy\";\nconst char pstrUsageACCut [] PROGMEM = \"AC Cut\";\nconst char pstrUsageACPaste [] PROGMEM = \"AC Paste\";\nconst char pstrUsageACSelectAll [] PROGMEM = \"AC Sel All\";\nconst char pstrUsageACFind [] PROGMEM = \"AC Find\";\nconst char pstrUsageACFindAndReplace [] PROGMEM = \"AC Find/Replace\";\nconst char pstrUsageACSearch [] PROGMEM = \"AC Search\";\nconst char pstrUsageACGoto [] PROGMEM = \"AC Goto\";\nconst char pstrUsageACHome [] PROGMEM = \"AC Home\";\nconst char pstrUsageACBack [] PROGMEM = \"AC Back\";\nconst char pstrUsageACForward [] PROGMEM = \"AC Fwd\";\nconst char pstrUsageACStop [] PROGMEM = \"AC Stop\";\nconst char pstrUsageACRefresh [] PROGMEM = \"AC Refresh\";\nconst char pstrUsageACPreviousLink [] PROGMEM = \"AC Prev Link\";\nconst char pstrUsageACNextLink [] PROGMEM = \"AC Next Link\";\nconst char pstrUsageACBookmarks [] PROGMEM = \"AC Bkmarks\";\nconst char pstrUsageACHistory [] PROGMEM = \"AC Hist\";\nconst char pstrUsageACSubscriptions [] PROGMEM = \"AC Subscr\";\nconst char pstrUsageACZoomIn [] PROGMEM = \"AC Zoom In\";\nconst char pstrUsageACZoomOut [] PROGMEM = \"AC Zoom Out\";\nconst char pstrUsageACZoom [] PROGMEM = \"AC Zoom\";\nconst char pstrUsageACFullScreenView [] PROGMEM = \"AC Full Scr\";\nconst char pstrUsageACNormalView [] PROGMEM = \"AC Norm View\";\nconst char pstrUsageACViewToggle [] PROGMEM = \"AC View Tgl\";\nconst char pstrUsageACScrollUp [] PROGMEM = \"AC Scroll Up\";\nconst char pstrUsageACScrollDown [] PROGMEM = \"AC Scroll Down\";\nconst char pstrUsageACScroll [] PROGMEM = \"AC Scroll\";\nconst char pstrUsageACPanLeft [] PROGMEM = \"AC Pan Left\";\nconst char pstrUsageACPanRight [] PROGMEM = \"AC Pan Right\";\nconst char pstrUsageACPan [] PROGMEM = \"AC Pan\";\nconst char pstrUsageACNewWindow [] PROGMEM = \"AC New Wnd\";\nconst char pstrUsageACTileHoriz [] PROGMEM = \"AC Tile Horiz\";\nconst char pstrUsageACTileVert [] PROGMEM = \"AC Tile Vert\";\nconst char pstrUsageACFormat [] PROGMEM = \"AC Frmt\";\nconst char pstrUsageACEdit [] PROGMEM = \"AC Edit\";\nconst char pstrUsageACBold [] PROGMEM = \"AC Bold\";\nconst char pstrUsageACItalics [] PROGMEM = \"AC Ital\";\nconst char pstrUsageACUnderline [] PROGMEM = \"AC Under\";\nconst char pstrUsageACStrikethrough [] PROGMEM = \"AC Strike\";\nconst char pstrUsageACSubscript [] PROGMEM = \"AC Sub\";\nconst char pstrUsageACSuperscript [] PROGMEM = \"AC Super\";\nconst char pstrUsageACAllCaps [] PROGMEM = \"AC All Caps\";\nconst char pstrUsageACRotate [] PROGMEM = \"AC Rotate\";\nconst char pstrUsageACResize [] PROGMEM = \"AC Resize\";\nconst char pstrUsageACFlipHorizontal [] PROGMEM = \"AC Flp H\";\nconst char pstrUsageACFlipVertical [] PROGMEM = \"AC Flp V\";\nconst char pstrUsageACMirrorHorizontal [] PROGMEM = \"AC Mir H\";\nconst char pstrUsageACMirrorVertical [] PROGMEM = \"AC Mir V\";\nconst char pstrUsageACFontSelect [] PROGMEM = \"AC Fnt Sel\";\nconst char pstrUsageACFontColor [] PROGMEM = \"AC Fnt Clr\";\nconst char pstrUsageACFontSize [] PROGMEM = \"AC Fnt Size\";\nconst char pstrUsageACJustifyLeft [] PROGMEM = \"AC Just Left\";\nconst char pstrUsageACJustifyCenterH [] PROGMEM = \"AC Just Cent H\";\nconst char pstrUsageACJustifyRight [] PROGMEM = \"AC Just Right\";\nconst char pstrUsageACJustifyBlockH [] PROGMEM = \"AC Just Block H\";\nconst char pstrUsageACJustifyTop [] PROGMEM = \"AC Just Top\";\nconst char pstrUsageACJustifyCenterV [] PROGMEM = \"AC Just Cent V\";\nconst char pstrUsageACJustifyBottom [] PROGMEM = \"AC Just Bot\";\nconst char pstrUsageACJustifyBlockV [] PROGMEM = \"AC Just Block V\";\nconst char pstrUsageACIndentDecrease [] PROGMEM = \"AC Indent Dec\";\nconst char pstrUsageACIndentIncrease [] PROGMEM = \"AC Indent Inc\";\nconst char pstrUsageACNumberedList [] PROGMEM = \"AC Num List\";\nconst char pstrUsageACRestartNumbering [] PROGMEM = \"AC Res Num\";\nconst char pstrUsageACBulletedList [] PROGMEM = \"AC Blt List\";\nconst char pstrUsageACPromote [] PROGMEM = \"AC Promote\";\nconst char pstrUsageACDemote [] PROGMEM = \"AC Demote\";\nconst char pstrUsageACYes [] PROGMEM = \"AC Yes\";\nconst char pstrUsageACNo [] PROGMEM = \"AC No\";\nconst char pstrUsageACCancel [] PROGMEM = \"AC Cancel\";\nconst char pstrUsageACCatalog [] PROGMEM = \"AC Ctlg\";\nconst char pstrUsageACBuyChkout [] PROGMEM = \"AC Buy\";\nconst char pstrUsageACAddToCart [] PROGMEM = \"AC Add2Cart\";\nconst char pstrUsageACExpand [] PROGMEM = \"AC Xpnd\";\nconst char pstrUsageACExpandAll [] PROGMEM = \"AC Xpand All\";\nconst char pstrUsageACCollapse [] PROGMEM = \"AC Collapse\";\nconst char pstrUsageACCollapseAll [] PROGMEM = \"AC Collapse All\";\nconst char pstrUsageACPrintPreview [] PROGMEM = \"AC Prn Prevw\";\nconst char pstrUsageACPasteSpecial [] PROGMEM = \"AC Paste Spec\";\nconst char pstrUsageACInsertMode [] PROGMEM = \"AC Ins Mode\";\nconst char pstrUsageACDelete [] PROGMEM = \"AC Del\";\nconst char pstrUsageACLock [] PROGMEM = \"AC Lock\";\nconst char pstrUsageACUnlock [] PROGMEM = \"AC Unlock\";\nconst char pstrUsageACProtect [] PROGMEM = \"AC Prot\";\nconst char pstrUsageACUnprotect [] PROGMEM = \"AC Unprot\";\nconst char pstrUsageACAttachComment [] PROGMEM = \"AC Attach Cmnt\";\nconst char pstrUsageACDeleteComment [] PROGMEM = \"AC Del Cmnt\";\nconst char pstrUsageACViewComment [] PROGMEM = \"AC View Cmnt\";\nconst char pstrUsageACSelectWord [] PROGMEM = \"AC Sel Word\";\nconst char pstrUsageACSelectSentence [] PROGMEM = \"AC Sel Sntc\";\nconst char pstrUsageACSelectParagraph [] PROGMEM = \"AC Sel Para\";\nconst char pstrUsageACSelectColumn [] PROGMEM = \"AC Sel Col\";\nconst char pstrUsageACSelectRow [] PROGMEM = \"AC Sel Row\";\nconst char pstrUsageACSelectTable [] PROGMEM = \"AC Sel Tbl\";\nconst char pstrUsageACSelectObject [] PROGMEM = \"AC Sel Obj\";\nconst char pstrUsageACRedoRepeat [] PROGMEM = \"AC Redo\";\nconst char pstrUsageACSort [] PROGMEM = \"AC Sort\";\nconst char pstrUsageACSortAscending [] PROGMEM = \"AC Sort Asc\";\nconst char pstrUsageACSortDescending [] PROGMEM = \"AC Sort Desc\";\nconst char pstrUsageACFilter [] PROGMEM = \"AC Filt\";\nconst char pstrUsageACSetClock [] PROGMEM = \"AC Set Clk\";\nconst char pstrUsageACViewClock [] PROGMEM = \"AC View Clk\";\nconst char pstrUsageACSelectTimeZone [] PROGMEM = \"AC Sel Time Z\";\nconst char pstrUsageACEditTimeZone [] PROGMEM = \"AC Edt Time Z\";\nconst char pstrUsageACSetAlarm [] PROGMEM = \"AC Set Alm\";\nconst char pstrUsageACClearAlarm [] PROGMEM = \"AC Clr Alm\";\nconst char pstrUsageACSnoozeAlarm [] PROGMEM = \"AC Snz Alm\";\nconst char pstrUsageACResetAlarm [] PROGMEM = \"AC Rst Alm\";\nconst char pstrUsageACSyncronize [] PROGMEM = \"AC Sync\";\nconst char pstrUsageACSendReceive [] PROGMEM = \"AC Snd/Rcv\";\nconst char pstrUsageACSendTo [] PROGMEM = \"AC Snd To\";\nconst char pstrUsageACReply [] PROGMEM = \"AC Reply\";\nconst char pstrUsageACReplyAll [] PROGMEM = \"AC Reply All\";\nconst char pstrUsageACForwardMessage [] PROGMEM = \"AC Fwd Msg\";\nconst char pstrUsageACSend [] PROGMEM = \"AC Snd\";\nconst char pstrUsageACAttachFile [] PROGMEM = \"AC Att File\";\nconst char pstrUsageACUpload [] PROGMEM = \"AC Upld\";\nconst char pstrUsageACDownload [] PROGMEM = \"AC Dnld\";\nconst char pstrUsageACSetBorders [] PROGMEM = \"AC Set Brd\";\nconst char pstrUsageACInsertRow [] PROGMEM = \"AC Ins Row\";\nconst char pstrUsageACInsertColumn [] PROGMEM = \"AC Ins Col\";\nconst char pstrUsageACInsertFile [] PROGMEM = \"AC Ins File\";\nconst char pstrUsageACInsertPicture [] PROGMEM = \"AC Ins Pic\";\nconst char pstrUsageACInsertObject [] PROGMEM = \"AC Ins Obj\";\nconst char pstrUsageACInsertSymbol [] PROGMEM = \"AC Ins Sym\";\nconst char pstrUsageACSaveAndClose [] PROGMEM = \"AC Sav&Cls\";\nconst char pstrUsageACRename [] PROGMEM = \"AC Rename\";\nconst char pstrUsageACMerge [] PROGMEM = \"AC Merge\";\nconst char pstrUsageACSplit [] PROGMEM = \"AC Split\";\nconst char pstrUsageACDistributeHorizontaly [] PROGMEM = \"AC Dist Hor\";\nconst char pstrUsageACDistributeVerticaly [] PROGMEM = \"AC Dist Ver\";\n\n// Digitaizers\nconst char pstrUsageDigitizer [] PROGMEM = \"Digitizer\";\nconst char pstrUsagePen [] PROGMEM = \"Pen\";\nconst char pstrUsageLightPen [] PROGMEM = \"Light Pen\";\nconst char pstrUsageTouchScreen [] PROGMEM = \"Touch Scr\";\nconst char pstrUsageTouchPad [] PROGMEM = \"Touch Pad\";\nconst char pstrUsageWhiteBoard [] PROGMEM = \"White Brd\";\nconst char pstrUsageCoordinateMeasuringMachine [] PROGMEM = \"Coord Meas Mach\";\nconst char pstrUsage3DDigitizer [] PROGMEM = \"3D Dgtz\";\nconst char pstrUsageStereoPlotter [] PROGMEM = \"Stereo Plot\";\nconst char pstrUsageArticulatedArm [] PROGMEM = \"Art Arm\";\nconst char pstrUsageArmature [] PROGMEM = \"Armature\";\nconst char pstrUsageMultiplePointDigitizer [] PROGMEM = \"Multi Point Dgtz\";\nconst char pstrUsageFreeSpaceWand [] PROGMEM = \"Free Space Wand\";\nconst char pstrUsageStylus [] PROGMEM = \"Stylus\";\nconst char pstrUsagePuck [] PROGMEM = \"Puck\";\nconst char pstrUsageFinger [] PROGMEM = \"Finger\";\nconst char pstrUsageTipPressure [] PROGMEM = \"Tip Press\";\nconst char pstrUsageBarrelPressure [] PROGMEM = \"Brl Press\";\nconst char pstrUsageInRange [] PROGMEM = \"In Range\";\nconst char pstrUsageTouch [] PROGMEM = \"Touch\";\nconst char pstrUsageUntouch [] PROGMEM = \"Untouch\";\nconst char pstrUsageTap [] PROGMEM = \"Tap\";\nconst char pstrUsageQuality [] PROGMEM = \"Qlty\";\nconst char pstrUsageDataValid [] PROGMEM = \"Data Valid\";\nconst char pstrUsageTransducerIndex [] PROGMEM = \"Transducer Ind\";\nconst char pstrUsageTabletFunctionKeys [] PROGMEM = \"Tabl Func Keys\";\nconst char pstrUsageProgramChangeKeys [] PROGMEM = \"Pgm Chng Keys\";\n//const char pstrUsageBatteryStrength [] PROGMEM = \"Bat Strength\";\nconst char pstrUsageInvert [] PROGMEM = \"Invert\";\nconst char pstrUsageXTilt [] PROGMEM = \"X Tilt\";\nconst char pstrUsageYTilt [] PROGMEM = \"Y Tilt\";\nconst char pstrUsageAzimuth [] PROGMEM = \"Azimuth\";\nconst char pstrUsageAltitude [] PROGMEM = \"Altitude\";\nconst char pstrUsageTwist [] PROGMEM = \"Twist\";\nconst char pstrUsageTipSwitch [] PROGMEM = \"Tip Sw\";\nconst char pstrUsageSecondaryTipSwitch [] PROGMEM = \"Scnd Tip Sw\";\nconst char pstrUsageBarrelSwitch [] PROGMEM = \"Brl Sw\";\nconst char pstrUsageEraser [] PROGMEM = \"Eraser\";\nconst char pstrUsageTabletPick [] PROGMEM = \"Tbl Pick\";\n\n// Alphanumeric Display Page\nconst char pstrUsageAlphanumericDisplay [] PROGMEM = \"Alphanum Disp\";\nconst char pstrUsageBitmappedDisplay [] PROGMEM = \"Bmp Disp\";\nconst char pstrUsageDisplayAttributesReport [] PROGMEM = \"Disp Attr Rpt\";\nconst char pstrUsageASCIICharacterSet [] PROGMEM = \"ASCII chset\";\nconst char pstrUsageDataReadBack [] PROGMEM = \"Data Rd Back\";\nconst char pstrUsageFontReadBack [] PROGMEM = \"Fnt Rd Back\";\nconst char pstrUsageDisplayControlReport [] PROGMEM = \"Disp Ctrl Rpt\";\nconst char pstrUsageClearDisplay [] PROGMEM = \"Clr Disp\";\n//const char pstrUsageDisplayEnable [] PROGMEM = \"Disp Enbl\";\nconst char pstrUsageScreenSaverDelay [] PROGMEM = \"Scr Sav Delay\";\nconst char pstrUsageScreenSaverEnable [] PROGMEM = \"Scr Sav Enbl\";\nconst char pstrUsageVerticalScroll [] PROGMEM = \"V Scroll\";\nconst char pstrUsageHorizontalScroll [] PROGMEM = \"H Scroll\";\nconst char pstrUsageCharacterReport [] PROGMEM = \"Char Rpt\";\nconst char pstrUsageDisplayData [] PROGMEM = \"Disp Data\";\nconst char pstrUsageDisplayStatus [] PROGMEM = \"Disp Stat\";\nconst char pstrUsageStatusNotReady [] PROGMEM = \"Stat !Ready\";\nconst char pstrUsageStatusReady [] PROGMEM = \"Stat Ready\";\nconst char pstrUsageErrorNotALoadableCharacter [] PROGMEM = \"Err Not Ld Char\";\nconst char pstrUsageErrorFotDataCanNotBeRead [] PROGMEM = \"Fnt Data Rd Err\";\nconst char pstrUsageCursorPositionReport [] PROGMEM = \"Cur Pos Rpt\";\nconst char pstrUsageRow [] PROGMEM = \"Row\";\nconst char pstrUsageColumn [] PROGMEM = \"Col\";\nconst char pstrUsageRows [] PROGMEM = \"Rows\";\nconst char pstrUsageColumns [] PROGMEM = \"Cols\";\nconst char pstrUsageCursorPixelPosition [] PROGMEM = \"Cur Pix Pos\";\nconst char pstrUsageCursorMode [] PROGMEM = \"Cur Mode\";\nconst char pstrUsageCursorEnable [] PROGMEM = \"Cur Enbl\";\nconst char pstrUsageCursorBlink [] PROGMEM = \"Cur Blnk\";\nconst char pstrUsageFontReport [] PROGMEM = \"Fnt Rpt\";\nconst char pstrUsageFontData [] PROGMEM = \"Fnt Data\";\nconst char pstrUsageCharacterWidth [] PROGMEM = \"Char Wdth\";\nconst char pstrUsageCharacterHeight [] PROGMEM = \"Char Hght\";\nconst char pstrUsageCharacterSpacingHorizontal [] PROGMEM = \"Char Space H\";\nconst char pstrUsageCharacterSpacingVertical [] PROGMEM = \"Char Space V\";\nconst char pstrUsageUnicodeCharset [] PROGMEM = \"Unicode Char\";\nconst char pstrUsageFont7Segment [] PROGMEM = \"Fnt 7-seg\";\nconst char pstrUsage7SegmentDirectMap [] PROGMEM = \"7-seg map\";\nconst char pstrUsageFont14Segment [] PROGMEM = \"Fnt 14-seg\";\nconst char pstrUsage14SegmentDirectMap [] PROGMEM = \"14-seg map\";\nconst char pstrUsageDisplayBrightness [] PROGMEM = \"Disp Bright\";\nconst char pstrUsageDisplayContrast [] PROGMEM = \"Disp Cntrst\";\nconst char pstrUsageCharacterAttribute [] PROGMEM = \"Char Attr\";\nconst char pstrUsageAttributeReadback [] PROGMEM = \"Attr Readbk\";\nconst char pstrUsageAttributeData [] PROGMEM = \"Attr Data\";\nconst char pstrUsageCharAttributeEnhance [] PROGMEM = \"Char Attr Enh\";\nconst char pstrUsageCharAttributeUnderline [] PROGMEM = \"Char Attr Undl\";\nconst char pstrUsageCharAttributeBlink [] PROGMEM = \"Char Attr Blnk\";\nconst char pstrUsageBitmapSizeX [] PROGMEM = \"Bmp Size X\";\nconst char pstrUsageBitmapSizeY [] PROGMEM = \"Bmp Size Y\";\nconst char pstrUsageBitDepthFormat [] PROGMEM = \"Bit Dpth Fmt\";\nconst char pstrUsageDisplayOrientation [] PROGMEM = \"Disp Ornt\";\nconst char pstrUsagePaletteReport [] PROGMEM = \"Pal Rpt\";\nconst char pstrUsagePaletteDataSize [] PROGMEM = \"Pal Data Size\";\nconst char pstrUsagePaletteDataOffset [] PROGMEM = \"Pal Data Off\";\nconst char pstrUsagePaletteData [] PROGMEM = \"Pal Data\";\nconst char pstrUsageBlitReport [] PROGMEM = \"Blit Rpt\";\nconst char pstrUsageBlitRectangleX1 [] PROGMEM = \"Blit Rect X1\";\nconst char pstrUsageBlitRectangleY1 [] PROGMEM = \"Blit Rect Y1\";\nconst char pstrUsageBlitRectangleX2 [] PROGMEM = \"Blit Rect X2\";\nconst char pstrUsageBlitRectangleY2 [] PROGMEM = \"Blit Rect Y2\";\nconst char pstrUsageBlitData [] PROGMEM = \"Blit Data\";\nconst char pstrUsageSoftButton [] PROGMEM = \"Soft Btn\";\nconst char pstrUsageSoftButtonID [] PROGMEM = \"Soft Btn ID\";\nconst char pstrUsageSoftButtonSide [] PROGMEM = \"Soft Btn Side\";\nconst char pstrUsageSoftButtonOffset1 [] PROGMEM = \"Soft Btn Off1\";\nconst char pstrUsageSoftButtonOffset2 [] PROGMEM = \"Soft Btn Off2\";\nconst char pstrUsageSoftButtonReport [] PROGMEM = \"Soft Btn Rpt\";\n\n// Medical Instrument Page\nconst char pstrUsageMedicalUltrasound [] PROGMEM = \"Med Ultrasnd\";\nconst char pstrUsageVCRAcquisition [] PROGMEM = \"VCR/Acq\";\nconst char pstrUsageFreezeThaw [] PROGMEM = \"Freeze\";\nconst char pstrUsageClipStore [] PROGMEM = \"Clip Store\";\nconst char pstrUsageUpdate [] PROGMEM = \"Update\";\nconst char pstrUsageNext [] PROGMEM = \"Next\";\nconst char pstrUsageSave [] PROGMEM = \"Save\";\nconst char pstrUsagePrint [] PROGMEM = \"Print\";\nconst char pstrUsageMicrophoneEnable [] PROGMEM = \"Mic Enbl\";\nconst char pstrUsageCine [] PROGMEM = \"Cine\";\nconst char pstrUsageTransmitPower [] PROGMEM = \"Trans Pwr\";\n//const char pstrUsageVolume [] PROGMEM = \"Vol\";\nconst char pstrUsageFocus [] PROGMEM = \"Focus\";\nconst char pstrUsageDepth [] PROGMEM = \"Depth\";\nconst char pstrUsageSoftStepPrimary [] PROGMEM = \"Soft Stp-Pri\";\nconst char pstrUsageSoftStepSecondary [] PROGMEM = \"Soft Stp-Sec\";\nconst char pstrUsageDepthGainCompensation [] PROGMEM = \"Dpth Gain Comp\";\nconst char pstrUsageZoomSelect [] PROGMEM = \"Zoom Sel\";\nconst char pstrUsageZoomAdjust [] PROGMEM = \"Zoom Adj\";\nconst char pstrUsageSpectralDopplerModeSelect [] PROGMEM = \"Spec Dop Mode Sel\";\nconst char pstrUsageSpectralDopplerModeAdjust [] PROGMEM = \"Spec Dop Mode Adj\";\nconst char pstrUsageColorDopplerModeSelect [] PROGMEM = \"Color Dop Mode Sel\";\nconst char pstrUsageColorDopplerModeAdjust [] PROGMEM = \"Color Dop Mode Adj\";\nconst char pstrUsageMotionModeSelect [] PROGMEM = \"Motion Mode Sel\";\nconst char pstrUsageMotionModeAdjust [] PROGMEM = \"Motion Mode Adj\";\nconst char pstrUsage2DModeSelect [] PROGMEM = \"2D Mode Sel\";\nconst char pstrUsage2DModeAdjust [] PROGMEM = \"2D Mode Adj\";\nconst char pstrUsageSoftControlSelect [] PROGMEM = \"Soft Ctrl Sel\";\nconst char pstrUsageSoftControlAdjust [] PROGMEM = \"Soft Ctrl Adj\";\n\n//extern const char *usagePageTitles0[15];\n//const char *usagePageTitles1[];\n//const char *genDesktopTitles0[];\n//const char *genDesktopTitles1[];\n//const char *genDesktopTitles2[];\n//const char *genDesktopTitles3[];\n//const char *genDesktopTitles4[];\n//const char *simuTitles0[];\n//const char *simuTitles1[];\n//const char *simuTitles2[];\n//const char *vrTitles0[];\n//const char *vrTitles1[];\n//const char *sportsCtrlTitles0[];\n//const char *sportsCtrlTitles1[];\n//const char *sportsCtrlTitles2[];\n//const char *gameTitles0[];\n//const char *gameTitles1[];\n//const char *genDevCtrlTitles[];\n//const char *ledTitles[];\n//const char *telTitles0[];\n//const char *telTitles1[];\n//const char *telTitles2[];\n//const char *telTitles3[];\n//const char *telTitles4[];\n//const char *telTitles5[];\n//const char *consTitles0[];\n//const char *consTitles1[];\n//const char *consTitles2[];\n//const char *consTitles3[];\n//const char *consTitles4[];\n//const char *consTitles5[];\n//const char *consTitles6[];\n//const char *consTitles7[];\n//const char *consTitles8[];\n//const char *consTitles9[];\n//const char *consTitlesA[];\n//const char *consTitlesB[];\n//const char *consTitlesC[];\n//const char *consTitlesD[];\n//const char *consTitlesE[];\n//const char *digitTitles0[];\n//const char *digitTitles1[];\n//const char *digitTitles2[];\n//const char *aplphanumTitles0[];\n//const char *aplphanumTitles1[];\n//const char *aplphanumTitles2[];\n//const char *medInstrTitles0[];\n//const char *medInstrTitles1[];\n//const char *medInstrTitles2[];\n//const char *medInstrTitles3[];\n//const char *medInstrTitles4[];\n\n#endif //__HIDUSAGESTR_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/hidusagetitlearrays.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(__HIDUSAGETITLEARRAYS_H__)\n#define __HIDUSAGETITLEARRAYS_H__\n\n#include \"hidusagestr.h\"\n\n// This is here why?\n\n//const char *usagePageTitles0[]\tPROGMEM =\n//{\n//\tpstrUsagePageGenericDesktopControls\t,\n//\tpstrUsagePageSimulationControls\t\t,\n//\tpstrUsagePageVRControls\t\t\t\t,\n//\tpstrUsagePageSportControls\t\t\t,\n//\tpstrUsagePageGameControls\t\t\t,\n//\tpstrUsagePageGenericDeviceControls\t,\n//\tpstrUsagePageKeyboardKeypad\t\t\t,\n//\tpstrUsagePageLEDs\t\t\t\t\t,\n//\tpstrUsagePageButton\t\t\t\t\t,\n//\tpstrUsagePageOrdinal\t\t\t\t,\n//\tpstrUsagePageTelephone\t\t\t\t,\n//\tpstrUsagePageConsumer\t\t\t\t,\n//\tpstrUsagePageDigitizer\t\t\t\t,\n//\tpstrUsagePagePID\t\t\t\t\t,\n//\tpstrUsagePageUnicode\n//};\n//\n//const char *usagePageTitles1[]\tPROGMEM =\n//{\n//\tpstrUsagePageBarCodeScanner\t\t\t,\n//\tpstrUsagePageScale\t\t\t\t\t,\n//\tpstrUsagePageMSRDevices\t\t\t\t,\n//\tpstrUsagePagePointOfSale\t\t\t,\n//\tpstrUsagePageCameraControl\t\t\t,\n//\tpstrUsagePageArcade\n//};\n//const char *genDesktopTitles0[] PROGMEM =\n//{\n//\tpstrUsagePointer\t\t\t\t\t,\n//\tpstrUsageMouse\t\t\t\t\t\t,\n//\tpstrUsageJoystick\t\t\t\t\t,\n//\tpstrUsageGamePad\t\t\t\t\t,\n//\tpstrUsageKeyboard\t\t\t\t\t,\n//\tpstrUsageKeypad\t\t\t\t\t\t,\n//\tpstrUsageMultiAxisController\t\t,\n//\tpstrUsageTabletPCSystemControls\n//\n//};\n//const char *genDesktopTitles1[] PROGMEM =\n//{\n//\tpstrUsageX\t\t\t\t\t\t\t,\n//\tpstrUsageY\t\t\t\t\t\t\t,\n//\tpstrUsageZ\t\t\t\t\t\t\t,\n//\tpstrUsageRx\t\t\t\t\t\t\t,\n//\tpstrUsageRy\t\t\t\t\t\t\t,\n//\tpstrUsageRz\t\t\t\t\t\t\t,\n//\tpstrUsageSlider\t\t\t\t\t\t,\n//\tpstrUsageDial\t\t\t\t\t\t,\n//\tpstrUsageWheel\t\t\t\t\t\t,\n//\tpstrUsageHatSwitch\t\t\t\t\t,\n//\tpstrUsageCountedBuffer\t\t\t\t,\n//\tpstrUsageByteCount\t\t\t\t\t,\n//\tpstrUsageMotionWakeup\t\t\t\t,\n//\tpstrUsageStart\t\t\t\t\t\t,\n//\tpstrUsageSelect\t\t\t\t\t\t,\n//\tpstrUsagePageReserved\t\t\t\t,\n//\tpstrUsageVx\t\t\t\t\t\t\t,\n//\tpstrUsageVy\t\t\t\t\t\t\t,\n//\tpstrUsageVz\t\t\t\t\t\t\t,\n//\tpstrUsageVbrx\t\t\t\t\t\t,\n//\tpstrUsageVbry\t\t\t\t\t\t,\n//\tpstrUsageVbrz\t\t\t\t\t\t,\n//\tpstrUsageVno\t\t\t\t\t\t,\n//\tpstrUsageFeatureNotification\t\t,\n//\tpstrUsageResolutionMultiplier\n//};\n//const char *genDesktopTitles2[] PROGMEM =\n//{\n//\tpstrUsageSystemControl\t\t,\n//\tpstrUsageSystemPowerDown\t,\n//\tpstrUsageSystemSleep\t\t,\n//\tpstrUsageSystemWakeup\t\t,\n//\tpstrUsageSystemContextMenu\t,\n//\tpstrUsageSystemMainMenu\t\t,\n//\tpstrUsageSystemAppMenu\t\t,\n//\tpstrUsageSystemMenuHelp\t\t,\n//\tpstrUsageSystemMenuExit\t\t,\n//\tpstrUsageSystemMenuSelect\t,\n//\tpstrUsageSystemMenuRight\t,\n//\tpstrUsageSystemMenuLeft\t\t,\n//\tpstrUsageSystemMenuUp\t\t,\n//\tpstrUsageSystemMenuDown\t\t,\n//\tpstrUsageSystemColdRestart\t,\n//\tpstrUsageSystemWarmRestart\t,\n//\tpstrUsageDPadUp\t\t\t\t,\n//\tpstrUsageDPadDown\t\t\t,\n//\tpstrUsageDPadRight\t\t\t,\n//\tpstrUsageDPadLeft\n//};\n//const char *genDesktopTitles3[] PROGMEM =\n//{\n//\tpstrUsageSystemDock\t\t\t\t,\n//\tpstrUsageSystemUndock\t\t\t,\n//\tpstrUsageSystemSetup\t\t\t,\n//\tpstrUsageSystemBreak\t\t\t,\n//\tpstrUsageSystemDebuggerBreak\t,\n//\tpstrUsageApplicationBreak\t\t,\n//\tpstrUsageApplicationDebuggerBreak,\n//\tpstrUsageSystemSpeakerMute\t\t,\n//\tpstrUsageSystemHibernate\n//};\n//const char *genDesktopTitles4[] PROGMEM =\n//{\n//\tpstrUsageSystemDisplayInvert\t\t,\n//\tpstrUsageSystemDisplayInternal\t\t,\n//\tpstrUsageSystemDisplayExternal\t\t,\n//\tpstrUsageSystemDisplayBoth\t\t\t,\n//\tpstrUsageSystemDisplayDual\t\t\t,\n//\tpstrUsageSystemDisplayToggleIntExt\t,\n//\tpstrUsageSystemDisplaySwapPriSec\t,\n//\tpstrUsageSystemDisplayLCDAutoscale\n//};\n//const char *simuTitles0[] PROGMEM =\n//{\n//\tpstrUsageFlightSimulationDevice\t\t,\n//\tpstrUsageAutomobileSimulationDevice\t,\n//\tpstrUsageTankSimulationDevice\t\t,\n//\tpstrUsageSpaceshipSimulationDevice\t,\n//\tpstrUsageSubmarineSimulationDevice\t,\n//\tpstrUsageSailingSimulationDevice\t,\n//\tpstrUsageMotocicleSimulationDevice\t,\n//\tpstrUsageSportsSimulationDevice\t\t,\n//\tpstrUsageAirplaneSimulationDevice\t,\n//\tpstrUsageHelicopterSimulationDevice\t,\n//\tpstrUsageMagicCarpetSimulationDevice,\n//\tpstrUsageBicycleSimulationDevice\n//};\n//const char *simuTitles1[] PROGMEM =\n//{\n//\tpstrUsageFlightControlStick\t\t\t,\n//\tpstrUsageFlightStick\t\t\t\t,\n//\tpstrUsageCyclicControl\t\t\t\t,\n//\tpstrUsageCyclicTrim\t\t\t\t\t,\n//\tpstrUsageFlightYoke\t\t\t\t\t,\n//\tpstrUsageTrackControl\n//};\n//const char *simuTitles2[] PROGMEM =\n//{\n//\tpstrUsageAileron\t\t\t\t\t,\n//\tpstrUsageAileronTrim\t\t\t\t,\n//\tpstrUsageAntiTorqueControl\t\t\t,\n//\tpstrUsageAutopilotEnable\t\t\t,\n//\tpstrUsageChaffRelease\t\t\t\t,\n//\tpstrUsageCollectiveControl\t\t\t,\n//\tpstrUsageDiveBrake\t\t\t\t\t,\n//\tpstrUsageElectronicCountermeasures\t,\n//\tpstrUsageElevator\t\t\t\t\t,\n//\tpstrUsageElevatorTrim\t\t\t\t,\n//\tpstrUsageRudder\t\t\t\t\t\t,\n//\tpstrUsageThrottle\t\t\t\t\t,\n//\tpstrUsageFlightCommunications\t\t,\n//\tpstrUsageFlareRelease\t\t\t\t,\n//\tpstrUsageLandingGear\t\t\t\t,\n//\tpstrUsageToeBrake\t\t\t\t\t,\n//\tpstrUsageTrigger\t\t\t\t\t,\n//\tpstrUsageWeaponsArm\t\t\t\t\t,\n//\tpstrUsageWeaponsSelect\t\t\t\t,\n//\tpstrUsageWingFlaps\t\t\t\t\t,\n//\tpstrUsageAccelerator\t\t\t\t,\n//\tpstrUsageBrake\t\t\t\t\t\t,\n//\tpstrUsageClutch\t\t\t\t\t\t,\n//\tpstrUsageShifter\t\t\t\t\t,\n//\tpstrUsageSteering\t\t\t\t\t,\n//\tpstrUsageTurretDirection\t\t\t,\n//\tpstrUsageBarrelElevation\t\t\t,\n//\tpstrUsageDivePlane\t\t\t\t\t,\n//\tpstrUsageBallast\t\t\t\t\t,\n//\tpstrUsageBicycleCrank\t\t\t\t,\n//\tpstrUsageHandleBars\t\t\t\t\t,\n//\tpstrUsageFrontBrake\t\t\t\t\t,\n//\tpstrUsageRearBrake\n//};\n//const char *vrTitles0[]\tPROGMEM =\n//{\n//\tpstrUsageBelt\t\t\t\t,\n//\tpstrUsageBodySuit\t\t\t,\n//\tpstrUsageFlexor\t\t\t\t,\n//\tpstrUsageGlove\t\t\t\t,\n//\tpstrUsageHeadTracker\t\t,\n//\tpstrUsageHeadMountedDisplay\t,\n//\tpstrUsageHandTracker\t\t,\n//\tpstrUsageOculometer\t\t\t,\n//\tpstrUsageVest\t\t\t\t,\n//\tpstrUsageAnimatronicDevice\n//};\n//const char *vrTitles1[]\tPROGMEM =\n//{\n//\tpstrUsageStereoEnable\t,\n//\tpstrUsageDisplayEnable\n//};\n//const char *sportsCtrlTitles0[]\tPROGMEM =\n//{\n//\tpstrUsageBaseballBat\t\t\t\t,\n//\tpstrUsageGolfClub\t\t\t\t\t,\n//\tpstrUsageRowingMachine\t\t\t\t,\n//\tpstrUsageTreadmill\n//};\n//const char *sportsCtrlTitles1[]\tPROGMEM =\n//{\n//\tpstrUsageOar\t\t\t\t\t\t,\n//\tpstrUsageSlope\t\t\t\t\t\t,\n//\tpstrUsageRate\t\t\t\t\t\t,\n//\tpstrUsageStickSpeed\t\t\t\t\t,\n//\tpstrUsageStickFaceAngle\t\t\t\t,\n//\tpstrUsageStickHeelToe\t\t\t\t,\n//\tpstrUsageStickFollowThough\t\t\t,\n//\tpstrUsageStickTempo\t\t\t\t\t,\n//\tpstrUsageStickType\t\t\t\t\t,\n//\tpstrUsageStickHeight\n//};\n//const char *sportsCtrlTitles2[]\tPROGMEM =\n//{\n//\tpstrUsagePutter\t\t\t\t\t\t,\n//\tpstrUsage1Iron\t\t\t\t\t\t,\n//\tpstrUsage2Iron\t\t\t\t\t\t,\n//\tpstrUsage3Iron\t\t\t\t\t\t,\n//\tpstrUsage4Iron\t\t\t\t\t\t,\n//\tpstrUsage5Iron\t\t\t\t\t\t,\n//\tpstrUsage6Iron\t\t\t\t\t\t,\n//\tpstrUsage7Iron\t\t\t\t\t\t,\n//\tpstrUsage8Iron\t\t\t\t\t\t,\n//\tpstrUsage9Iron\t\t\t\t\t\t,\n//\tpstrUsage10Iron\t\t\t\t\t\t,\n//\tpstrUsage11Iron\t\t\t\t\t\t,\n//\tpstrUsageSandWedge\t\t\t\t\t,\n//\tpstrUsageLoftWedge\t\t\t\t\t,\n//\tpstrUsagePowerWedge\t\t\t\t\t,\n//\tpstrUsage1Wood\t\t\t\t\t\t,\n//\tpstrUsage3Wood\t\t\t\t\t\t,\n//\tpstrUsage5Wood\t\t\t\t\t\t,\n//\tpstrUsage7Wood\t\t\t\t\t\t,\n//\tpstrUsage9Wood\n//};\n//const char *gameTitles0[] PROGMEM =\n//{\n//\tpstrUsage3DGameController\t\t,\n//\tpstrUsagePinballDevice\t\t\t,\n//\tpstrUsageGunDevice\n//};\n//const char *gameTitles1[] PROGMEM =\n//{\n//\tpstrUsagePointOfView\t\t\t,\n//\tpstrUsageTurnRightLeft\t\t\t,\n//\tpstrUsagePitchForwardBackward\t,\n//\tpstrUsageRollRightLeft\t\t\t,\n//\tpstrUsageMoveRightLeft\t\t\t,\n//\tpstrUsageMoveForwardBackward\t,\n//\tpstrUsageMoveUpDown\t\t\t\t,\n//\tpstrUsageLeanRightLeft\t\t\t,\n//\tpstrUsageLeanForwardBackward\t,\n//\tpstrUsageHeightOfPOV\t\t\t,\n//\tpstrUsageFlipper\t\t\t\t,\n//\tpstrUsageSecondaryFlipper\t\t,\n//\tpstrUsageBump\t\t\t\t\t,\n//\tpstrUsageNewGame\t\t\t\t,\n//\tpstrUsageShootBall\t\t\t\t,\n//\tpstrUsagePlayer\t\t\t\t\t,\n//\tpstrUsageGunBolt\t\t\t\t,\n//\tpstrUsageGunClip\t\t\t\t,\n//\tpstrUsageGunSelector\t\t\t,\n//\tpstrUsageGunSingleShot\t\t\t,\n//\tpstrUsageGunBurst\t\t\t\t,\n//\tpstrUsageGunAutomatic\t\t\t,\n//\tpstrUsageGunSafety\t\t\t\t,\n//\tpstrUsageGamepadFireJump\t\t,\n//\tpstrUsageGamepadTrigger\n//};\n//const char *genDevCtrlTitles[] PROGMEM =\n//{\n//\tpstrUsageBatteryStrength,\n//\tpstrUsageWirelessChannel,\n//\tpstrUsageWirelessID,\n//\tpstrUsageDiscoverWirelessControl,\n//\tpstrUsageSecurityCodeCharEntered,\n//\tpstrUsageSecurityCodeCharErased,\n//\tpstrUsageSecurityCodeCleared\n//};\n//const char *ledTitles[] PROGMEM =\n//{\n//\tpstrUsageNumLock\t\t\t\t\t\t,\n//\tpstrUsageCapsLock\t\t\t\t\t,\n//\tpstrUsageScrollLock\t\t\t\t\t,\n//\tpstrUsageCompose\t\t\t\t\t,\n//\tpstrUsageKana\t\t\t\t\t\t,\n//\tpstrUsagePower\t\t\t\t\t\t,\n//\tpstrUsageShift\t\t\t\t\t\t,\n//\tpstrUsageDoNotDisturb\t\t\t\t,\n//\tpstrUsageMute\t\t\t\t\t\t,\n//\tpstrUsageToneEnable\t\t\t\t\t,\n//\tpstrUsageHighCutFilter\t\t\t\t,\n//\tpstrUsageLowCutFilter\t\t\t\t,\n//\tpstrUsageEqualizerEnable\t\t\t,\n//\tpstrUsageSoundFieldOn\t\t\t\t,\n//\tpstrUsageSurroundOn\t\t\t\t\t,\n//\tpstrUsageRepeat\t\t\t\t\t\t,\n//\tpstrUsageStereo\t\t\t\t\t\t,\n//\tpstrUsageSamplingRateDetect\t\t\t,\n//\tpstrUsageSpinning\t\t\t\t\t,\n//\tpstrUsageCAV\t\t\t\t\t\t,\n//\tpstrUsageCLV\t\t\t\t\t\t,\n//\tpstrUsageRecordingFormatDetect\t\t,\n//\tpstrUsageOffHook\t\t\t\t\t,\n//\tpstrUsageRing\t\t\t\t\t\t,\n//\tpstrUsageMessageWaiting\t\t\t\t,\n//\tpstrUsageDataMode\t\t\t\t\t,\n//\tpstrUsageBatteryOperation\t\t\t,\n//\tpstrUsageBatteryOK\t\t\t\t\t,\n//\tpstrUsageBatteryLow\t\t\t\t\t,\n//\tpstrUsageSpeaker\t\t\t\t\t,\n//\tpstrUsageHeadSet\t\t\t\t\t,\n//\tpstrUsageHold\t\t\t\t\t\t,\n//\tpstrUsageMicrophone\t\t\t\t\t,\n//\tpstrUsageCoverage\t\t\t\t\t,\n//\tpstrUsageNightMode\t\t\t\t\t,\n//\tpstrUsageSendCalls\t\t\t\t\t,\n//\tpstrUsageCallPickup\t\t\t\t\t,\n//\tpstrUsageConference\t\t\t\t\t,\n//\tpstrUsageStandBy\t\t\t\t\t,\n//\tpstrUsageCameraOn\t\t\t\t\t,\n//\tpstrUsageCameraOff\t\t\t\t\t,\n//\tpstrUsageOnLine\t\t\t\t\t\t,\n//\tpstrUsageOffLine\t\t\t\t\t,\n//\tpstrUsageBusy\t\t\t\t\t\t,\n//\tpstrUsageReady\t\t\t\t\t\t,\n//\tpstrUsagePaperOut\t\t\t\t\t,\n//\tpstrUsagePaperJam\t\t\t\t\t,\n//\tpstrUsageRemote\t\t\t\t\t\t,\n//\tpstrUsageForward\t\t\t\t\t,\n//\tpstrUsageReverse\t\t\t\t\t,\n//\tpstrUsageStop\t\t\t\t\t\t,\n//\tpstrUsageRewind\t\t\t\t\t\t,\n//\tpstrUsageFastForward\t\t\t\t,\n//\tpstrUsagePlay\t\t\t\t\t\t,\n//\tpstrUsagePause\t\t\t\t\t\t,\n//\tpstrUsageRecord\t\t\t\t\t\t,\n//\tpstrUsageError\t\t\t\t\t\t,\n//\tpstrUsageSelectedIndicator\t\t\t,\n//\tpstrUsageInUseIndicator\t\t\t\t,\n//\tpstrUsageMultiModeIndicator\t\t\t,\n//\tpstrUsageIndicatorOn\t\t\t\t,\n//\tpstrUsageIndicatorFlash\t\t\t\t,\n//\tpstrUsageIndicatorSlowBlink\t\t\t,\n//\tpstrUsageIndicatorFastBlink\t\t\t,\n//\tpstrUsageIndicatorOff\t\t\t\t,\n//\tpstrUsageFlashOnTime\t\t\t\t,\n//\tpstrUsageSlowBlinkOnTime\t\t\t,\n//\tpstrUsageSlowBlinkOffTime\t\t\t,\n//\tpstrUsageFastBlinkOnTime\t\t\t,\n//\tpstrUsageFastBlinkOffTime\t\t\t,\n//\tpstrUsageIndicatorColor\t\t\t\t,\n//\tpstrUsageIndicatorRed\t\t\t\t,\n//\tpstrUsageIndicatorGreen\t\t\t\t,\n//\tpstrUsageIndicatorAmber\t\t\t\t,\n//\tpstrUsageGenericIndicator\t\t\t,\n//\tpstrUsageSystemSuspend\t\t\t\t,\n//\tpstrUsageExternalPowerConnected\n//};\n//const char *telTitles0\t\t\t[] PROGMEM =\n//{\n//\tpstrUsagePhone\t\t\t\t,\n//\tpstrUsageAnsweringMachine\t,\n//\tpstrUsageMessageControls\t,\n//\tpstrUsageHandset\t\t\t,\n//\tpstrUsageHeadset\t\t\t,\n//\tpstrUsageTelephonyKeyPad\t,\n//\tpstrUsageProgrammableButton\n//};\n//const char *telTitles1\t\t\t[] PROGMEM =\n//{\n//\tpstrUsageHookSwitch\t\t\t\t\t,\n//\tpstrUsageFlash\t\t\t\t\t\t,\n//\tpstrUsageFeature\t\t\t\t\t,\n//\tpstrUsageHold\t\t\t\t\t\t,\n//\tpstrUsageRedial\t\t\t\t\t\t,\n//\tpstrUsageTransfer\t\t\t\t\t,\n//\tpstrUsageDrop\t\t\t\t\t\t,\n//\tpstrUsagePark\t\t\t\t\t\t,\n//\tpstrUsageForwardCalls\t\t\t\t,\n//\tpstrUsageAlternateFunction\t\t\t,\n//\tpstrUsageLine\t\t\t\t\t\t,\n//\tpstrUsageSpeakerPhone\t\t\t\t,\n//\tpstrUsageConference\t\t\t\t,\n//\tpstrUsageRingEnable\t\t\t\t,\n//\tpstrUsageRingSelect\t\t\t\t,\n//\tpstrUsagePhoneMute\t\t\t\t,\n//\tpstrUsageCallerID\t\t\t\t,\n//\tpstrUsageSend\n//};\n//const char *telTitles2\t\t\t[] PROGMEM =\n//{\n//\tpstrUsageSpeedDial\t\t,\n//\tpstrUsageStoreNumber\t,\n//\tpstrUsageRecallNumber\t,\n//\tpstrUsagePhoneDirectory\n//};\n//const char *telTitles3\t\t\t[] PROGMEM =\n//{\n//\tpstrUsageVoiceMail\t\t,\n//\tpstrUsageScreenCalls\t,\n//\tpstrUsageDoNotDisturb\t,\n//\tpstrUsageMessage\t\t,\n//\tpstrUsageAnswerOnOff\n//};\n//const char *telTitles4\t\t\t[] PROGMEM =\n//{\n//\tpstrUsageInsideDialTone\t\t\t,\n//\tpstrUsageOutsideDialTone\t\t,\n//\tpstrUsageInsideRingTone\t\t\t,\n//\tpstrUsageOutsideRingTone\t\t,\n//\tpstrUsagePriorityRingTone\t\t,\n//\tpstrUsageInsideRingback\t\t\t,\n//\tpstrUsagePriorityRingback\t\t,\n//\tpstrUsageLineBusyTone\t\t\t,\n//\tpstrUsageReorderTone\t\t\t,\n//\tpstrUsageCallWaitingTone\t\t,\n//\tpstrUsageConfirmationTone1\t\t,\n//\tpstrUsageConfirmationTone2\t\t,\n//\tpstrUsageTonesOff\t\t\t\t,\n//\tpstrUsageOutsideRingback\t\t,\n//\tpstrUsageRinger\n//};\n//const char *telTitles5\t\t\t[] PROGMEM =\n//{\n//\tpstrUsagePhoneKey0\t\t,\n//\tpstrUsagePhoneKey1\t\t,\n//\tpstrUsagePhoneKey2\t\t,\n//\tpstrUsagePhoneKey3\t\t,\n//\tpstrUsagePhoneKey4\t\t,\n//\tpstrUsagePhoneKey5\t\t,\n//\tpstrUsagePhoneKey6\t\t,\n//\tpstrUsagePhoneKey7\t\t,\n//\tpstrUsagePhoneKey8\t\t,\n//\tpstrUsagePhoneKey9\t\t,\n//\tpstrUsagePhoneKeyStar\t,\n//\tpstrUsagePhoneKeyPound\t,\n//\tpstrUsagePhoneKeyA\t\t,\n//\tpstrUsagePhoneKeyB\t\t,\n//\tpstrUsagePhoneKeyC\t\t,\n//\tpstrUsagePhoneKeyD\n//};\n//const char *consTitles0[]\tPROGMEM\t=\n//{\n//\tpstrUsageConsumerControl,\n//\tpstrUsageNumericKeyPad,\n//\tpstrUsageProgrammableButton,\n//\tpstrUsageMicrophone,\n//\tpstrUsageHeadphone,\n//\tpstrUsageGraphicEqualizer\n//};\n//const char *consTitles1[]\tPROGMEM\t=\n//{\n//\tpstrUsagePlus10\t,\n//\tpstrUsagePlus100,\n//\tpstrUsageAMPM\n//};\n//const char *consTitles2[]\tPROGMEM\t=\n//{\n//\tpstrUsagePower\t\t\t,\n//\tpstrUsageReset\t\t\t,\n//\tpstrUsageSleep\t\t\t,\n//\tpstrUsageSleepAfter\t\t,\n//\tpstrUsageSleepMode\t\t,\n//\tpstrUsageIllumination\t,\n//\tpstrUsageFunctionButtons\n//\n//};\n//const char *consTitles3[]\tPROGMEM\t=\n//{\n//\tpstrUsageMenu\t\t\t,\n//\tpstrUsageMenuPick\t\t,\n//\tpstrUsageMenuUp\t\t\t,\n//\tpstrUsageMenuDown\t\t,\n//\tpstrUsageMenuLeft\t\t,\n//\tpstrUsageMenuRight\t\t,\n//\tpstrUsageMenuEscape\t\t,\n//\tpstrUsageMenuValueIncrease,\n//\tpstrUsageMenuValueDecrease\n//};\n//const char *consTitles4[]\tPROGMEM\t=\n//{\n//\tpstrUsageDataOnScreen\t\t,\n//\tpstrUsageClosedCaption\t\t,\n//\tpstrUsageClosedCaptionSelect,\n//\tpstrUsageVCRTV\t\t\t\t,\n//\tpstrUsageBroadcastMode\t\t,\n//\tpstrUsageSnapshot\t\t\t,\n//\tpstrUsageStill\n//};\n//const char *consTitles5[]\tPROGMEM\t=\n//{\n//\tpstrUsageSelection\t\t\t\t\t,\n//\tpstrUsageAssignSelection\t\t\t,\n//\tpstrUsageModeStep\t\t\t\t\t,\n//\tpstrUsageRecallLast\t\t\t\t\t,\n//\tpstrUsageEnterChannel\t\t\t\t,\n//\tpstrUsageOrderMovie\t\t\t\t\t,\n//\tpstrUsageChannel\t\t\t\t\t,\n//\tpstrUsageMediaSelection\t\t\t\t,\n//\tpstrUsageMediaSelectComputer\t\t,\n//\tpstrUsageMediaSelectTV\t\t\t\t,\n//\tpstrUsageMediaSelectWWW\t\t\t\t,\n//\tpstrUsageMediaSelectDVD\t\t\t\t,\n//\tpstrUsageMediaSelectTelephone\t\t,\n//\tpstrUsageMediaSelectProgramGuide\t,\n//\tpstrUsageMediaSelectVideoPhone\t\t,\n//\tpstrUsageMediaSelectGames\t\t\t,\n//\tpstrUsageMediaSelectMessages\t\t,\n//\tpstrUsageMediaSelectCD\t\t\t\t,\n//\tpstrUsageMediaSelectVCR\t\t\t\t,\n//\tpstrUsageMediaSelectTuner\t\t\t,\n//\tpstrUsageQuit\t\t\t\t\t\t,\n//\tpstrUsageHelp\t\t\t\t\t\t,\n//\tpstrUsageMediaSelectTape\t\t\t,\n//\tpstrUsageMediaSelectCable\t\t\t,\n//\tpstrUsageMediaSelectSatellite\t\t,\n//\tpstrUsageMediaSelectSecurity\t\t,\n//\tpstrUsageMediaSelectHome\t\t\t,\n//\tpstrUsageMediaSelectCall\t\t\t,\n//\tpstrUsageChannelIncrement\t\t\t,\n//\tpstrUsageChannelDecrement\t\t\t,\n//\tpstrUsageMediaSelectSAP\t\t\t\t,\n//\tpstrUsagePageReserved\t\t\t\t,\n//\tpstrUsageVCRPlus\t\t\t\t\t,\n//\tpstrUsageOnce\t\t\t\t\t\t,\n//\tpstrUsageDaily\t\t\t\t\t\t,\n//\tpstrUsageWeekly\t\t\t\t\t\t,\n//\tpstrUsageMonthly\n//};\n//const char *consTitles6[]\tPROGMEM\t=\n//{\n//\tpstrUsagePlay\t\t\t\t\t,\n//\tpstrUsagePause\t\t\t\t\t,\n//\tpstrUsageRecord\t\t\t\t\t,\n//\tpstrUsageFastForward\t\t\t,\n//\tpstrUsageRewind\t\t\t\t\t,\n//\tpstrUsageScanNextTrack\t\t\t,\n//\tpstrUsageScanPreviousTrack\t\t,\n//\tpstrUsageStop\t\t\t\t\t,\n//\tpstrUsageEject\t\t\t\t\t,\n//\tpstrUsageRandomPlay\t\t\t\t,\n//\tpstrUsageSelectDisk\t\t\t\t,\n//\tpstrUsageEnterDisk\t\t\t\t,\n//\tpstrUsageRepeat\t\t\t\t\t,\n//\tpstrUsageTracking\t\t\t\t\t,\n//\tpstrUsageTrackNormal\t\t\t\t,\n//\tpstrUsageSlowTracking\t\t\t\t,\n//\tpstrUsageFrameForward\t\t\t\t,\n//\tpstrUsageFrameBackwards\t\t\t\t,\n//\tpstrUsageMark\t\t\t\t\t\t,\n//\tpstrUsageClearMark\t\t\t\t\t,\n//\tpstrUsageRepeatFromMark\t\t\t\t,\n//\tpstrUsageReturnToMark\t\t\t\t,\n//\tpstrUsageSearchMarkForward\t\t\t,\n//\tpstrUsageSearchMarkBackwards\t\t,\n//\tpstrUsageCounterReset\t\t\t\t,\n//\tpstrUsageShowCounter\t\t\t\t,\n//\tpstrUsageTrackingIncrement\t\t\t,\n//\tpstrUsageTrackingDecrement\t\t\t,\n//\tpstrUsageStopEject\t\t\t\t\t,\n//\tpstrUsagePlayPause\t\t\t\t\t,\n//\tpstrUsagePlaySkip\n//};\n//const char *consTitles7[]\tPROGMEM\t=\n//{\n//\tpstrUsageVolume\t\t\t\t\t\t,\n//\tpstrUsageBalance\t\t\t\t\t,\n//\tpstrUsageMute\t\t\t\t\t\t,\n//\tpstrUsageBass\t\t\t\t\t\t,\n//\tpstrUsageTreble\t\t\t\t\t\t,\n//\tpstrUsageBassBoost\t\t\t\t\t,\n//\tpstrUsageSurroundMode\t\t\t\t,\n//\tpstrUsageLoudness\t\t\t\t\t,\n//\tpstrUsageMPX\t\t\t\t\t\t,\n//\tpstrUsageVolumeIncrement\t\t\t,\n//\tpstrUsageVolumeDecrement\n//};\n//const char *consTitles8[]\tPROGMEM\t=\n//{\n//\tpstrUsageSpeedSelect\t\t\t\t,\n//\tpstrUsagePlaybackSpeed\t\t\t\t,\n//\tpstrUsageStandardPlay\t\t\t\t,\n//\tpstrUsageLongPlay\t\t\t\t\t,\n//\tpstrUsageExtendedPlay\t\t\t\t,\n//\tpstrUsageSlow\n//};\n//const char *consTitles9[]\tPROGMEM\t=\n//{\n//\tpstrUsageFanEnable\t\t\t\t\t,\n//\tpstrUsageFanSpeed\t\t\t\t\t,\n//\tpstrUsageLightEnable\t\t\t\t,\n//\tpstrUsageLightIlluminationLevel\t\t,\n//\tpstrUsageClimateControlEnable\t\t,\n//\tpstrUsageRoomTemperature\t\t\t,\n//\tpstrUsageSecurityEnable\t\t\t\t,\n//\tpstrUsageFireAlarm\t\t\t\t\t,\n//\tpstrUsagePoliceAlarm\t\t\t\t,\n//\tpstrUsageProximity\t\t\t\t\t,\n//\tpstrUsageMotion\t\t\t\t\t\t,\n//\tpstrUsageDuresAlarm\t\t\t\t\t,\n//\tpstrUsageHoldupAlarm\t\t\t\t\t,\n//\tpstrUsageMedicalAlarm\n//};\n//const char *consTitlesA[]\tPROGMEM\t=\n//{\n//\tpstrUsageBalanceRight\t\t\t\t,\n//\tpstrUsageBalanceLeft\t\t\t\t,\n//\tpstrUsageBassIncrement\t\t\t\t,\n//\tpstrUsageBassDecrement\t\t\t\t,\n//\tpstrUsageTrebleIncrement\t\t\t,\n//\tpstrUsageTrebleDecrement\n//};\n//const char *consTitlesB[]\tPROGMEM\t=\n//{\n//\tpstrUsageSpeakerSystem\t\t\t\t,\n//\tpstrUsageChannelLeft\t\t\t\t,\n//\tpstrUsageChannelRight\t\t\t\t,\n//\tpstrUsageChannelCenter\t\t\t\t,\n//\tpstrUsageChannelFront\t\t\t\t,\n//\tpstrUsageChannelCenterFront\t\t\t,\n//\tpstrUsageChannelSide\t\t\t\t,\n//\tpstrUsageChannelSurround\t\t\t,\n//\tpstrUsageChannelLowFreqEnhancement\t,\n//\tpstrUsageChannelTop\t\t\t\t\t,\n//\tpstrUsageChannelUnknown\n//};\n//const char *consTitlesC[]\tPROGMEM\t=\n//{\n//\tpstrUsageSubChannel\t\t\t\t\t,\n//\tpstrUsageSubChannelIncrement\t\t,\n//\tpstrUsageSubChannelDecrement\t\t,\n//\tpstrUsageAlternateAudioIncrement\t,\n//\tpstrUsageAlternateAudioDecrement\n//};\n//const char *consTitlesD[]\tPROGMEM\t=\n//{\n//\tpstrUsageApplicationLaunchButtons\t,\n//\tpstrUsageALLaunchButtonConfigTool\t,\n//\tpstrUsageALProgrammableButton\t\t,\n//\tpstrUsageALConsumerControlConfig\t,\n//\tpstrUsageALWordProcessor\t\t\t,\n//\tpstrUsageALTextEditor\t\t\t\t,\n//\tpstrUsageALSpreadsheet\t\t\t\t,\n//\tpstrUsageALGraphicsEditor\t\t\t,\n//\tpstrUsageALPresentationApp\t\t\t,\n//\tpstrUsageALDatabaseApp\t\t\t\t,\n//\tpstrUsageALEmailReader\t\t\t\t,\n//\tpstrUsageALNewsreader\t\t\t\t,\n//\tpstrUsageALVoicemail\t\t\t\t,\n//\tpstrUsageALContactsAddressBook\t\t,\n//\tpstrUsageALCalendarSchedule\t\t\t,\n//\tpstrUsageALTaskProjectManager\t\t,\n//\tpstrUsageALLogJournalTimecard\t\t,\n//\tpstrUsageALCheckbookFinance\t\t\t,\n//\tpstrUsageALCalculator\t\t\t\t,\n//\tpstrUsageALAVCapturePlayback\t\t,\n//\tpstrUsageALLocalMachineBrowser\t\t,\n//\tpstrUsageALLANWANBrow\t\t\t\t,\n//\tpstrUsageALInternetBrowser\t\t\t,\n//\tpstrUsageALRemoteNetISPConnect\t\t,\n//\tpstrUsageALNetworkConference\t\t,\n//\tpstrUsageALNetworkChat\t\t\t\t,\n//\tpstrUsageALTelephonyDialer\t\t\t,\n//\tpstrUsageALLogon\t\t\t\t\t,\n//\tpstrUsageALLogoff\t\t\t\t\t,\n//\tpstrUsageALLogonLogoff\t\t\t\t,\n//\tpstrUsageALTermLockScrSav\t\t\t,\n//\tpstrUsageALControlPannel\t\t\t,\n//\tpstrUsageALCommandLineProcessorRun\t,\n//\tpstrUsageALProcessTaskManager\t\t,\n//\tpstrUsageALSelectTaskApplication\t,\n//\tpstrUsageALNextTaskApplication\t\t,\n//\tpstrUsageALPreviousTaskApplication\t,\n//\tpstrUsageALPreemptiveHaltTaskApp\t,\n//\tpstrUsageALIntegratedHelpCenter\t\t,\n//\tpstrUsageALDocuments\t\t\t\t,\n//\tpstrUsageALThesaurus\t\t\t\t,\n//\tpstrUsageALDictionary\t\t\t\t,\n//\tpstrUsageALDesktop\t\t\t\t\t,\n//\tpstrUsageALSpellCheck\t\t\t\t,\n//\tpstrUsageALGrammarCheck\t\t\t\t,\n//\tpstrUsageALWirelessStatus\t\t\t,\n//\tpstrUsageALKeyboardLayout\t\t\t,\n//\tpstrUsageALVirusProtection\t\t\t,\n//\tpstrUsageALEncryption\t\t\t\t,\n//\tpstrUsageALScreenSaver\t\t\t\t,\n//\tpstrUsageALAlarms\t\t\t\t\t,\n//\tpstrUsageALClock\t\t\t\t\t,\n//\tpstrUsageALFileBrowser\t\t\t\t,\n//\tpstrUsageALPowerStatus\t\t\t\t,\n//\tpstrUsageALImageBrowser\t\t\t\t,\n//\tpstrUsageALAudioBrowser\t\t\t\t,\n//\tpstrUsageALMovieBrowser\t\t\t\t,\n//\tpstrUsageALDigitalRightsManager\t\t,\n//\tpstrUsageALDigitalWallet\t\t\t,\n//\tpstrUsagePageReserved\t\t\t\t,\n//\tpstrUsageALInstantMessaging\t\t\t,\n//\tpstrUsageALOEMFeaturesBrowser\t\t,\n//\tpstrUsageALOEMHelp\t\t\t\t\t,\n//\tpstrUsageALOnlineCommunity\t\t\t,\n//\tpstrUsageALEntertainmentContentBrow\t,\n//\tpstrUsageALOnlineShoppingBrowser\t,\n//\tpstrUsageALSmartCardInfoHelp\t\t,\n//\tpstrUsageALMarketMonitorFinBrowser\t,\n//\tpstrUsageALCustomCorpNewsBrowser\t\t,\n//\tpstrUsageALOnlineActivityBrowser\t\t,\n//\tpstrUsageALResearchSearchBrowser\t\t,\n//\tpstrUsageALAudioPlayer\n//};\n//const char *consTitlesE[]\tPROGMEM\t=\n//{\n//\tpstrUsageGenericGUIAppControls\t\t,\n//\tpstrUsageACNew\t\t\t\t\t\t,\n//\tpstrUsageACOpen\t\t\t\t\t\t,\n//\tpstrUsageACClose\t\t\t\t\t,\n//\tpstrUsageACExit\t\t\t\t\t\t,\n//\tpstrUsageACMaximize\t\t\t\t\t,\n//\tpstrUsageACMinimize\t\t\t\t\t,\n//\tpstrUsageACSave\t\t\t\t\t\t,\n//\tpstrUsageACPrint\t\t\t\t\t,\n//\tpstrUsageACProperties\t\t\t\t,\n//\tpstrUsageACUndo\t\t\t\t\t\t,\n//\tpstrUsageACCopy\t\t\t\t\t\t,\n//\tpstrUsageACCut\t\t\t\t\t\t,\n//\tpstrUsageACPaste\t\t\t\t\t,\n//\tpstrUsageACSelectAll\t\t\t\t,\n//\tpstrUsageACFind\t\t\t\t\t\t,\n//\tpstrUsageACFindAndReplace\t\t\t,\n//\tpstrUsageACSearch\t\t\t\t\t,\n//\tpstrUsageACGoto\t\t\t\t\t\t,\n//\tpstrUsageACHome\t\t\t\t\t\t,\n//\tpstrUsageACBack\t\t\t\t\t\t,\n//\tpstrUsageACForward\t\t\t\t\t,\n//\tpstrUsageACStop\t\t\t\t\t\t,\n//\tpstrUsageACRefresh\t\t\t\t\t,\n//\tpstrUsageACPreviousLink\t\t\t\t,\n//\tpstrUsageACNextLink\t\t\t\t\t,\n//\tpstrUsageACBookmarks\t\t\t\t,\n//\tpstrUsageACHistory\t\t\t\t\t,\n//\tpstrUsageACSubscriptions\t\t\t,\n//\tpstrUsageACZoomIn\t\t\t\t\t,\n//\tpstrUsageACZoomOut\t\t\t\t\t,\n//\tpstrUsageACZoom\t\t\t\t\t\t,\n//\tpstrUsageACFullScreenView\t\t\t,\n//\tpstrUsageACNormalView\t\t\t\t,\n//\tpstrUsageACViewToggle\t\t\t\t,\n//\tpstrUsageACScrollUp\t\t\t\t\t,\n//\tpstrUsageACScrollDown\t\t\t\t,\n//\tpstrUsageACScroll\t\t\t\t\t,\n//\tpstrUsageACPanLeft\t\t\t\t\t,\n//\tpstrUsageACPanRight\t\t\t\t\t,\n//\tpstrUsageACPan\t\t\t\t\t\t,\n//\tpstrUsageACNewWindow\t\t\t\t,\n//\tpstrUsageACTileHoriz\t\t\t\t,\n//\tpstrUsageACTileVert\t\t\t\t\t,\n//\tpstrUsageACFormat\t\t\t\t\t,\n//\tpstrUsageACEdit\t\t\t\t\t\t,\n//\tpstrUsageACBold\t\t\t\t\t\t,\n//\tpstrUsageACItalics\t\t\t\t\t,\n//\tpstrUsageACUnderline\t\t\t\t,\n//\tpstrUsageACStrikethrough\t\t\t,\n//\tpstrUsageACSubscript\t\t\t\t,\n//\tpstrUsageACSuperscript\t\t\t\t,\n//\tpstrUsageACAllCaps\t\t\t\t\t,\n//\tpstrUsageACRotate\t\t\t\t\t,\n//\tpstrUsageACResize\t\t\t\t\t,\n//\tpstrUsageACFlipHorizontal\t\t\t,\n//\tpstrUsageACFlipVertical\t\t\t\t,\n//\tpstrUsageACMirrorHorizontal\t\t\t,\n//\tpstrUsageACMirrorVertical\t\t\t,\n//\tpstrUsageACFontSelect\t\t\t\t,\n//\tpstrUsageACFontColor\t\t\t\t,\n//\tpstrUsageACFontSize\t\t\t\t\t,\n//\tpstrUsageACJustifyLeft\t\t\t\t,\n//\tpstrUsageACJustifyCenterH\t\t\t,\n//\tpstrUsageACJustifyRight\t\t\t\t,\n//\tpstrUsageACJustifyBlockH\t\t\t,\n//\tpstrUsageACJustifyTop\t\t\t\t,\n//\tpstrUsageACJustifyCenterV\t\t\t,\n//\tpstrUsageACJustifyBottom\t\t\t,\n//\tpstrUsageACJustifyBlockV\t\t\t,\n//\tpstrUsageACIndentDecrease\t\t\t,\n//\tpstrUsageACIndentIncrease\t\t\t,\n//\tpstrUsageACNumberedList\t\t\t\t,\n//\tpstrUsageACRestartNumbering\t\t\t,\n//\tpstrUsageACBulletedList\t\t\t\t,\n//\tpstrUsageACPromote\t\t\t\t\t,\n//\tpstrUsageACDemote\t\t\t\t\t,\n//\tpstrUsageACYes\t\t\t\t\t\t,\n//\tpstrUsageACNo\t\t\t\t\t\t,\n//\tpstrUsageACCancel\t\t\t\t\t,\n//\tpstrUsageACCatalog\t\t\t\t\t,\n//\tpstrUsageACBuyChkout\t\t\t\t,\n//\tpstrUsageACAddToCart\t\t\t\t,\n//\tpstrUsageACExpand\t\t\t\t\t,\n//\tpstrUsageACExpandAll\t\t\t\t,\n//\tpstrUsageACCollapse\t\t\t\t\t,\n//\tpstrUsageACCollapseAll\t\t\t\t,\n//\tpstrUsageACPrintPreview\t\t\t\t,\n//\tpstrUsageACPasteSpecial\t\t\t\t,\n//\tpstrUsageACInsertMode\t\t\t\t,\n//\tpstrUsageACDelete\t\t\t\t\t,\n//\tpstrUsageACLock\t\t\t\t\t\t,\n//\tpstrUsageACUnlock\t\t\t\t\t,\n//\tpstrUsageACProtect\t\t\t\t\t,\n//\tpstrUsageACUnprotect\t\t\t\t,\n//\tpstrUsageACAttachComment\t\t\t,\n//\tpstrUsageACDeleteComment\t\t\t,\n//\tpstrUsageACViewComment\t\t\t\t,\n//\tpstrUsageACSelectWord\t\t\t\t,\n//\tpstrUsageACSelectSentence\t\t\t,\n//\tpstrUsageACSelectParagraph\t\t\t,\n//\tpstrUsageACSelectColumn\t\t\t\t,\n//\tpstrUsageACSelectRow\t\t\t\t,\n//\tpstrUsageACSelectTable\t\t\t\t,\n//\tpstrUsageACSelectObject\t\t\t\t,\n//\tpstrUsageACRedoRepeat\t\t\t\t,\n//\tpstrUsageACSort\t\t\t\t\t\t,\n//\tpstrUsageACSortAscending\t\t\t,\n//\tpstrUsageACSortDescending\t\t\t,\n//\tpstrUsageACFilter\t\t\t\t\t,\n//\tpstrUsageACSetClock\t\t\t\t\t,\n//\tpstrUsageACViewClock\t\t\t\t,\n//\tpstrUsageACSelectTimeZone\t\t\t,\n//\tpstrUsageACEditTimeZone\t\t\t\t,\n//\tpstrUsageACSetAlarm\t\t\t\t\t,\n//\tpstrUsageACClearAlarm\t\t\t\t,\n//\tpstrUsageACSnoozeAlarm\t\t\t\t,\n//\tpstrUsageACResetAlarm\t\t\t\t,\n//\tpstrUsageACSyncronize\t\t\t\t,\n//\tpstrUsageACSendReceive\t\t\t\t,\n//\tpstrUsageACSendTo\t\t\t\t\t,\n//\tpstrUsageACReply\t\t\t\t\t,\n//\tpstrUsageACReplyAll\t\t\t\t\t,\n//\tpstrUsageACForwardMessage\t\t\t,\n//\tpstrUsageACSend\t\t\t\t\t\t,\n//\tpstrUsageACAttachFile\t\t\t\t,\n//\tpstrUsageACUpload\t\t\t\t\t,\n//\tpstrUsageACDownload\t\t\t\t\t,\n//\tpstrUsageACSetBorders\t\t\t\t,\n//\tpstrUsageACInsertRow\t\t\t\t,\n//\tpstrUsageACInsertColumn\t\t\t\t,\n//\tpstrUsageACInsertFile\t\t\t\t,\n//\tpstrUsageACInsertPicture\t\t\t,\n//\tpstrUsageACInsertObject\t\t\t\t,\n//\tpstrUsageACInsertSymbol\t\t\t\t,\n//\tpstrUsageACSaveAndClose\t\t\t\t,\n//\tpstrUsageACRename\t\t\t\t\t,\n//\tpstrUsageACMerge\t\t\t\t\t,\n//\tpstrUsageACSplit\t\t\t\t\t,\n//\tpstrUsageACDistributeHorizontaly\t,\n//\tpstrUsageACDistributeVerticaly\n//};\n//const char *digitTitles0[] PROGMEM =\n//{\n//\tpstrUsageDigitizer\t\t\t\t\t,\n//\tpstrUsagePen\t\t\t\t\t\t,\n//\tpstrUsageLightPen\t\t\t\t\t,\n//\tpstrUsageTouchScreen\t\t\t\t,\n//\tpstrUsageTouchPad\t\t\t\t\t,\n//\tpstrUsageWhiteBoard\t\t\t\t\t,\n//\tpstrUsageCoordinateMeasuringMachine\t,\n//\tpstrUsage3DDigitizer\t\t\t\t,\n//\tpstrUsageStereoPlotter\t\t\t\t,\n//\tpstrUsageArticulatedArm\t\t\t\t,\n//\tpstrUsageArmature\t\t\t\t\t,\n//\tpstrUsageMultiplePointDigitizer\t\t,\n//\tpstrUsageFreeSpaceWand\n//};\n//const char *digitTitles1[] PROGMEM =\n//{\n//\tpstrUsageStylus\t\t\t\t\t\t,\n//\tpstrUsagePuck\t\t\t\t\t\t,\n//\tpstrUsageFinger\n//\n//};\n//const char *digitTitles2[] PROGMEM =\n//{\n//\tpstrUsageTipPressure\t\t\t,\n//\tpstrUsageBarrelPressure\t\t\t,\n//\tpstrUsageInRange\t\t\t\t,\n//\tpstrUsageTouch\t\t\t\t\t,\n//\tpstrUsageUntouch\t\t\t\t,\n//\tpstrUsageTap\t\t\t\t\t,\n//\tpstrUsageQuality\t\t\t\t,\n//\tpstrUsageDataValid\t\t\t\t,\n//\tpstrUsageTransducerIndex\t\t,\n//\tpstrUsageTabletFunctionKeys\t\t,\n//\tpstrUsageProgramChangeKeys\t\t,\n//\tpstrUsageBatteryStrength\t\t,\n//\tpstrUsageInvert\t\t\t\t\t,\n//\tpstrUsageXTilt\t\t\t\t\t,\n//\tpstrUsageYTilt\t\t\t\t\t,\n//\tpstrUsageAzimuth\t\t\t\t,\n//\tpstrUsageAltitude\t\t\t\t,\n//\tpstrUsageTwist\t\t\t\t\t,\n//\tpstrUsageTipSwitch\t\t\t\t,\n//\tpstrUsageSecondaryTipSwitch\t\t,\n//\tpstrUsageBarrelSwitch\t\t\t,\n//\tpstrUsageEraser\t\t\t\t\t,\n//\tpstrUsageTabletPick\n//};\n//const char *aplphanumTitles0[]\tPROGMEM =\n//{\n//\tpstrUsageAlphanumericDisplay,\n//\tpstrUsageBitmappedDisplay\n//};\n//const char *aplphanumTitles1[]\tPROGMEM =\n//{\n//\tpstrUsageDisplayAttributesReport\t,\n//\tpstrUsageASCIICharacterSet\t\t\t,\n//\tpstrUsageDataReadBack\t\t\t\t,\n//\tpstrUsageFontReadBack\t\t\t\t,\n//\tpstrUsageDisplayControlReport\t\t,\n//\tpstrUsageClearDisplay\t\t\t\t,\n//\tpstrUsageDisplayEnable\t\t\t\t,\n//\tpstrUsageScreenSaverDelay\t\t\t,\n//\tpstrUsageScreenSaverEnable\t\t\t,\n//\tpstrUsageVerticalScroll\t\t\t\t,\n//\tpstrUsageHorizontalScroll\t\t\t,\n//\tpstrUsageCharacterReport\t\t\t,\n//\tpstrUsageDisplayData\t\t\t\t,\n//\tpstrUsageDisplayStatus\t\t\t\t,\n//\tpstrUsageStatusNotReady\t\t\t\t,\n//\tpstrUsageStatusReady\t\t\t\t,\n//\tpstrUsageErrorNotALoadableCharacter\t,\n//\tpstrUsageErrorFotDataCanNotBeRead\t,\n//\tpstrUsageCursorPositionReport\t\t,\n//\tpstrUsageRow\t\t\t\t\t\t,\n//\tpstrUsageColumn\t\t\t\t\t\t,\n//\tpstrUsageRows\t\t\t\t\t\t,\n//\tpstrUsageColumns\t\t\t\t\t,\n//\tpstrUsageCursorPixelPosition\t\t,\n//\tpstrUsageCursorMode\t\t\t\t\t,\n//\tpstrUsageCursorEnable\t\t\t\t,\n//\tpstrUsageCursorBlink\t\t\t\t,\n//\tpstrUsageFontReport\t\t\t\t\t,\n//\tpstrUsageFontData\t\t\t\t\t,\n//\tpstrUsageCharacterWidth\t\t\t\t,\n//\tpstrUsageCharacterHeight\t\t\t,\n//\tpstrUsageCharacterSpacingHorizontal\t,\n//\tpstrUsageCharacterSpacingVertical\t,\n//\tpstrUsageUnicodeCharset\t\t\t\t,\n//\tpstrUsageFont7Segment\t\t\t\t,\n//\tpstrUsage7SegmentDirectMap\t\t\t,\n//\tpstrUsageFont14Segment\t\t\t\t,\n//\tpstrUsage14SegmentDirectMap\t\t\t,\n//\tpstrUsageDisplayBrightness\t\t\t,\n//\tpstrUsageDisplayContrast\t\t\t,\n//\tpstrUsageCharacterAttribute\t\t\t,\n//\tpstrUsageAttributeReadback\t\t\t,\n//\tpstrUsageAttributeData\t\t\t\t,\n//\tpstrUsageCharAttributeEnhance\t\t,\n//\tpstrUsageCharAttributeUnderline\t\t,\n//\tpstrUsageCharAttributeBlink\n//};\n//const char *aplphanumTitles2[]\tPROGMEM =\n//{\n//\tpstrUsageBitmapSizeX\t\t\t\t,\n//\tpstrUsageBitmapSizeY\t\t\t\t,\n//\tpstrUsagePageReserved\t\t\t\t,\n//\tpstrUsageBitDepthFormat\t\t\t\t,\n//\tpstrUsageDisplayOrientation\t\t\t,\n//\tpstrUsagePaletteReport\t\t\t\t,\n//\tpstrUsagePaletteDataSize\t\t\t,\n//\tpstrUsagePaletteDataOffset\t\t\t,\n//\tpstrUsagePaletteData\t\t\t\t,\n//\tpstrUsageBlitReport\t\t\t\t\t,\n//\tpstrUsageBlitRectangleX1\t\t\t,\n//\tpstrUsageBlitRectangleY1\t\t\t,\n//\tpstrUsageBlitRectangleX2\t\t\t,\n//\tpstrUsageBlitRectangleY2\t\t\t,\n//\tpstrUsageBlitData\t\t\t\t\t,\n//\tpstrUsageSoftButton\t\t\t\t\t,\n//\tpstrUsageSoftButtonID\t\t\t\t,\n//\tpstrUsageSoftButtonSide\t\t\t\t,\n//\tpstrUsageSoftButtonOffset1\t\t\t,\n//\tpstrUsageSoftButtonOffset2\t\t\t,\n//\tpstrUsageSoftButtonReport\n//};\n//const char *medInstrTitles0[] PROGMEM =\n//{\n//\tpstrUsageVCRAcquisition\t\t\t\t,\n//\tpstrUsageFreezeThaw\t\t\t\t\t,\n//\tpstrUsageClipStore\t\t\t\t\t,\n//\tpstrUsageUpdate\t\t\t\t\t\t,\n//\tpstrUsageNext\t\t\t\t\t\t,\n//\tpstrUsageSave\t\t\t\t\t\t,\n//\tpstrUsagePrint\t\t\t\t\t\t,\n//\tpstrUsageMicrophoneEnable\n//};\n//const char *medInstrTitles1[] PROGMEM =\n//{\n//\tpstrUsageCine\t\t\t\t\t\t,\n//\tpstrUsageTransmitPower\t\t\t\t,\n//\tpstrUsageVolume\t\t\t\t\t\t,\n//\tpstrUsageFocus\t\t\t\t\t\t,\n//\tpstrUsageDepth\n//};\n//const char *medInstrTitles2[] PROGMEM =\n//{\n//\tpstrUsageSoftStepPrimary\t\t,\n//\tpstrUsageSoftStepSecondary\n//};\n//const char *medInstrTitles3[] PROGMEM =\n//{\n//\tpstrUsageZoomSelect\t\t\t\t\t,\n//\tpstrUsageZoomAdjust\t\t\t\t\t,\n//\tpstrUsageSpectralDopplerModeSelect\t,\n//\tpstrUsageSpectralDopplerModeAdjust\t,\n//\tpstrUsageColorDopplerModeSelect\t\t,\n//\tpstrUsageColorDopplerModeAdjust\t\t,\n//\tpstrUsageMotionModeSelect\t\t\t,\n//\tpstrUsageMotionModeAdjust\t\t\t,\n//\tpstrUsage2DModeSelect\t\t\t\t,\n//\tpstrUsage2DModeAdjust\n//};\n//const char *medInstrTitles4[] PROGMEM =\n//{\n//\tpstrUsageSoftControlSelect\t\t\t,\n//\tpstrUsageSoftControlAdjust\n//};\n\n#endif // __HIDUSAGETITLEARRAYS_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/keywords.txt",
    "content": "####################################################\n# Syntax Coloring Map For USB Library\n####################################################\n\n####################################################\n# Datatypes (KEYWORD1)\n####################################################\n\nUSB\tKEYWORD1\nUSBHub\tKEYWORD1\n\n####################################################\n# Syntax Coloring Map For BTD (Bluetooth) Library\n####################################################\n\n####################################################\n# Datatypes (KEYWORD1)\n####################################################\n\nBTD\tKEYWORD1\n\n####################################################\n# Methods and Functions (KEYWORD2)\n####################################################\nTask\tKEYWORD2\n\n####################################################\n# Syntax Coloring Map For PS3/PS4 Bluetooth/USB Library\n####################################################\n\n####################################################\n# Datatypes (KEYWORD1)\n####################################################\n\nPS3BT\tKEYWORD1\nPS3USB\tKEYWORD1\nPS4BT\tKEYWORD1\nPS4USB\tKEYWORD1\n\n####################################################\n# Methods and Functions (KEYWORD2)\n####################################################\nsetBdaddr\tKEYWORD2\ngetBdaddr\tKEYWORD2\nsetMoveBdaddr\tKEYWORD2\ngetMoveBdaddr\tKEYWORD2\ngetMoveCalibration\tKEYWORD2\n\ngetButtonPress\tKEYWORD2\ngetButtonClick\tKEYWORD2\ngetAnalogButton\tKEYWORD2\ngetAnalogHat\tKEYWORD2\ngetSensor\tKEYWORD2\ngetAngle\tKEYWORD2\nget9DOFValues\tKEYWORD2\ngetStatus\tKEYWORD2\nprintStatusString\tKEYWORD2\ngetTemperature\tKEYWORD2\ndisconnect\tKEYWORD2\n\nsetAllOff\tKEYWORD2\nsetRumbleOff\tKEYWORD2\nsetRumbleOn\tKEYWORD2\nsetLedOff\tKEYWORD2\nsetLedOn\tKEYWORD2\nsetLedToggle\tKEYWORD2\nsetLedFlash\tKEYWORD2\nmoveSetBulb\tKEYWORD2\nmoveSetRumble\tKEYWORD2\n\nattachOnInit\tKEYWORD2\n\nPS3Connected\tKEYWORD2\nPS3MoveConnected\tKEYWORD2\nPS3NavigationConnected\tKEYWORD2\n\nisReady\tKEYWORD2\nwatingForConnection\tKEYWORD2\n\nisTouching\tKEYWORD2\ngetX\tKEYWORD2\ngetY\tKEYWORD2\ngetTouchCounter\tKEYWORD2\n\ngetUsbStatus    KEYWORD2\ngetAudioStatus  KEYWORD2\ngetMicStatus    KEYWORD2\n\n####################################################\n# Constants and enums (LITERAL1)\n####################################################\nOFF\tLITERAL1\nLED1\tLITERAL1\nLED2\tLITERAL1\nLED3\tLITERAL1\nLED4\tLITERAL1\nLED5\tLITERAL1\nLED6\tLITERAL1\nLED7\tLITERAL1\nLED8\tLITERAL1\nLED9\tLITERAL1\nLED10\tLITERAL1\n\nRed\tLITERAL1\nGreen\tLITERAL1\nBlue\tLITERAL1\nYellow\tLITERAL1\nLightblue\tLITERAL1\nPurble\tLITERAL1\nWhite\tLITERAL1\nOff\tLITERAL1\n\nSELECT\tLITERAL1\nL3\tLITERAL1\nR3\tLITERAL1\nSTART\tLITERAL1\nUP\tLITERAL1\nRIGHT\tLITERAL1\nDOWN\tLITERAL1\nLEFT\tLITERAL1\nL2\tLITERAL1\nR2\tLITERAL1\nL1\tLITERAL1\nR1\tLITERAL1\nTRIANGLE\tLITERAL1\nCIRCLE\tLITERAL1\nCROSS\tLITERAL1\nSQUARE\tLITERAL1\nPS\tLITERAL1\nMOVE\tLITERAL1\nT\tLITERAL1\n\nSHARE\tLITERAL1\nOPTIONS\tLITERAL1\nTOUCHPAD\tLITERAL1\n\nLeftHatX\tLITERAL1\nLeftHatY\tLITERAL1\nRightHatX\tLITERAL1\nRightHatY\tLITERAL1\n\naX\tLITERAL1\naY\tLITERAL1\naZ\tLITERAL1\ngX\tLITERAL1\ngY\tLITERAL1\ngZ\tLITERAL1\naXmove\tLITERAL1\naYmove\tLITERAL1\naZmove\tLITERAL1\ngXmove\tLITERAL1\ngYmove\tLITERAL1\ngZmove\tLITERAL1\ntempMove\tLITERAL1\nmXmove\tLITERAL1\nmZmove\tLITERAL1\nmYmove\tLITERAL1\n\nPitch\tLITERAL1\nRoll\tLITERAL1\n\nPlugged\tLITERAL1\nUnplugged\tLITERAL1\nCharging\tLITERAL1\nNotCharging\tLITERAL1\nShutdown\tLITERAL1\nDying\tLITERAL1\nLow\tLITERAL1\nHigh\tLITERAL1\nFull\tLITERAL1\nMoveCharging\tLITERAL1\nMoveNotCharging\tLITERAL1\nMoveShutdown\tLITERAL1\nMoveDying\tLITERAL1\nMoveLow\tLITERAL1\nMoveHigh\tLITERAL1\nMoveFull\tLITERAL1\nCableRumble\tLITERAL1\nCable\tLITERAL1\nBluetoothRumble\tLITERAL1\nBluetooth\tLITERAL1\n\nRumbleHigh\tLITERAL1\nRumbleLow\tLITERAL1\n\n####################################################\n# Syntax Coloring Map For Xbox 360 Libraries\n####################################################\n\n####################################################\n# Datatypes (KEYWORD1)\n####################################################\n\nXBOXUSB\tKEYWORD1\nXBOXONE\tKEYWORD1\nXBOXOLD\tKEYWORD1\nXBOXRECV\tKEYWORD1\n\n####################################################\n# Methods and Functions (KEYWORD2)\n####################################################\n\nsetLedRaw\tKEYWORD2\nsetLedBlink\tKEYWORD2\nsetLedMode\tKEYWORD2\ngetBatteryLevel\tKEYWORD2\nbuttonChanged\tKEYWORD2\n\nXboxReceiverConnected\tKEYWORD2\nXbox360Connected\tKEYWORD2\nXboxOneConnected\tKEYWORD2\n\n####################################################\n# Constants and enums (LITERAL1)\n####################################################\n\nALL\tLITERAL1\n\nROTATING\tLITERAL1\nFASTBLINK\tLITERAL1\nSLOWBLINK\tLITERAL1\nALTERNATING\tLITERAL1\n\nBACK\tLITERAL1\n\nXBOX\tLITERAL1\nSYNC\tLITERAL1\n\nBLACK\tLITERAL1\nWHITE\tLITERAL1\n\nA\tLITERAL1\nB\tLITERAL1\nX\tLITERAL1\nY\tLITERAL1\n\n####################################################\n# Syntax Coloring Map For RFCOMM/SPP Library\n####################################################\n\n####################################################\n# Datatypes (KEYWORD1)\n####################################################\n\nSPP\tKEYWORD1\n\n####################################################\n# Methods and Functions (KEYWORD2)\n####################################################\n\nconnected\tKEYWORD2\ndiscard\tKEYWORD2\n\n####################################################\n# Syntax Coloring Map For Wiimote Library\n####################################################\n\n####################################################\n# Datatypes (KEYWORD1)\n####################################################\n\nWII\tKEYWORD1\n\n####################################################\n# Methods and Functions (KEYWORD2)\n####################################################\n\nwiimoteConnected\tKEYWORD2\nnunchuckConnected\tKEYWORD2\nmotionPlusConnected\tKEYWORD2\nwiiUProControllerConnected\tKEYWORD2\nwiiBalanceBoardConnected\tKEYWORD2\nsetRumbleToggle\tKEYWORD2\ngetPitch\tKEYWORD2\ngetRoll\tKEYWORD2\ngetYaw\tKEYWORD2\ngetWiimotePitch\tKEYWORD2\ngetWiimoteRoll\tKEYWORD2\ngetNunchuckPitch\tKEYWORD2\ngetNunchuckRoll\tKEYWORD2\nPAIR\tKEYWORD2\nstatusRequest\tKEYWORD2\ngetBatteryLevel\tKEYWORD2\ngetWiiState\tKEYWORD2\ngetWeight\tKEYWORD2\ngetTotalWeight\tKEYWORD2\ngetWeightRaw\tKEYWORD2\n\n####################################################\n# Constants and enums (LITERAL1)\n####################################################\n\nPLUS\tLITERAL1\nMINUS\tLITERAL1\nONE\tLITERAL1\nTWO\tLITERAL1\nHOME\tLITERAL1\nZ\tLITERAL1\nC\tLITERAL1\nL\tLITERAL1\nR\tLITERAL1\nZL\tLITERAL1\nZR\tLITERAL1\nHatX\tLITERAL1\nHatY\tLITERAL1\nTopRight\tLITERAL1\nBotRight\tLITERAL1\nTopLeft\tLITERAL1\nBotLeft\tLITERAL1\n\n####################################################\n# Methods and Functions for the IR Camera\n####################################################\n\nIRinitialize\tKEYWORD2\nisIRCameraEnabled\tKEYWORD2\ngetIRx1\tKEYWORD2\ngetIRy1\tKEYWORD2\ngetIRs1\tKEYWORD2\ngetIRx2\tKEYWORD2\ngetIRy2\tKEYWORD2\ngetIRs2\tKEYWORD2\ngetIRx3\tKEYWORD2\ngetIRy3\tKEYWORD2\ngetIRs3\tKEYWORD2\ngetIRx4\tKEYWORD2\ngetIRy4\tKEYWORD2\ngetIRs4\tKEYWORD2\n\n####################################################\n# Syntax Coloring Map For BTHID Library\n####################################################\n\n####################################################\n# Datatypes (KEYWORD1)\n####################################################\n\nBTHID\tKEYWORD1\n\n####################################################\n# Methods and Functions (KEYWORD2)\n####################################################\nSetReportParser\tKEYWORD2\nsetProtocolMode\tKEYWORD2\n\n####################################################\n# Syntax Coloring Map For PS Buzz Library\n####################################################\n\n####################################################\n# Datatypes (KEYWORD1)\n####################################################\n\nPSBuzz\tKEYWORD1\n\n####################################################\n# Methods and Functions (KEYWORD2)\n####################################################\n\nsetLedOnAll\tKEYWORD2\nsetLedOffAll\tKEYWORD2\n\n####################################################\n# Constants and enums (LITERAL1)\n####################################################\n\nRED\tLITERAL1\nYELLOW\tLITERAL1\nGREEN\tLITERAL1\nORANGE\tLITERAL1\nBLUE\tLITERAL1\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/library.json",
    "content": "{\n  \"name\": \"USB-Host-Shield-20\",\n  \"keywords\": \"usb, host, ftdi, adk, acm, pl2303, hid, bluetooth, spp, ps3, ps4, buzz, xbox, wii, mass storage\",\n  \"description\": \"Revision 2.0 of MAX3421E-based USB Host Shield Library\",\n  \"authors\":\n  [\n    {\n      \"name\": \"Oleg Mazurov\",\n      \"email\": \"mazurov@circuitsathome.com\",\n      \"url\": \"http://www.circuitsathome.com\",\n      \"maintainer\": true\n    },\n    {\n      \"name\": \"Alexei Glushchenko\",\n      \"email\": \"alex-gl@mail.ru\"\n    },\n    {\n      \"name\": \"Kristian Lauszus\",\n      \"email\": \"kristianl@tkjelectronics.com\",\n      \"url\": \"http://tkjelectronics.com\",\n      \"maintainer\": true\n    },\n    {\n      \"name\": \"Andrew Kroll\",\n      \"email\": \"xxxajk@gmail.com\",\n      \"maintainer\": true\n    }\n  ],\n  \"repository\":\n  {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/felis/USB_Host_Shield_2.0.git\"\n  },\n  \"examples\":\n  [\n  \t\"examples/*/*.ino\",\n  \t\"examples/*/*/*.ino\"\n  ],\n  \"frameworks\": \"arduino\",\n  \"platforms\":\n  [\n    \"atmelavr\",\n    \"teensy\",\n    \"atmelsam\",\n    \"nordicnrf51\"\n  ]\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/library.properties",
    "content": "name=USB Host Shield Library 2.0\nversion=1.1.0\nauthor=Oleg Mazurov (Circuits@Home) <mazurov@circuitsathome.com>, Kristian Lauszus (TKJ Electronics) <kristianl@tkjelectronics.com>, Andrew Kroll <xxxajk@gmail.com>, Alexei Glushchenko (Circuits@Home) <alex-gl@mail.ru>\nmaintainer=Oleg Mazurov (Circuits@Home) <mazurov@circuitsathome.com>, Kristian Lauszus (TKJ Electronics) <kristianl@tkjelectronics.com>, Andrew Kroll <xxxajk@gmail.com>\nsentence=Revision 2.0 of MAX3421E-based USB Host Shield Library.\nparagraph=Supports HID devices, FTDI, ADK, ACM, PL2303, Bluetooth HID devices, SPP communication and mass storage devices. Furthermore it supports PS3, PS4, PS Buzz, Wii and Xbox controllers.\ncategory=Other\nurl=https://github.com/felis/USB_Host_Shield_2.0\narchitectures=*"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/macros.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#if !defined(_usb_h_) || defined(MACROS_H)\n#error \"Never include macros.h directly; include Usb.h instead\"\n#else\n#define MACROS_H\n\n////////////////////////////////////////////////////////////////////////////////\n// HANDY MACROS\n////////////////////////////////////////////////////////////////////////////////\n\n#define VALUE_BETWEEN(v,l,h) (((v)>(l)) && ((v)<(h)))\n#define VALUE_WITHIN(v,l,h) (((v)>=(l)) && ((v)<=(h)))\n#define output_pgm_message(wa,fp,mp,el) wa = &mp, fp((char *)pgm_read_pointer(wa), el)\n#define output_if_between(v,l,h,wa,fp,mp,el) if(VALUE_BETWEEN(v,l,h)) output_pgm_message(wa,fp,mp[v-(l+1)],el);\n\n#define SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b)))\n#ifndef __BYTE_GRABBING_DEFINED__\n#define __BYTE_GRABBING_DEFINED__ 1\n#ifdef BROKEN_OPTIMIZER_LITTLE_ENDIAN\n// Note: Use this if your compiler generates horrible assembler!\n#define BGRAB0(__usi__)  (((uint8_t *)&(__usi__))[0])\n#define BGRAB1(__usi__)  (((uint8_t *)&(__usi__))[1])\n#define BGRAB2(__usi__)  (((uint8_t *)&(__usi__))[2])\n#define BGRAB3(__usi__)  (((uint8_t *)&(__usi__))[3])\n#define BGRAB4(__usi__)  (((uint8_t *)&(__usi__))[4])\n#define BGRAB5(__usi__)  (((uint8_t *)&(__usi__))[5])\n#define BGRAB6(__usi__)  (((uint8_t *)&(__usi__))[6])\n#define BGRAB7(__usi__)  (((uint8_t *)&(__usi__))[7])\n#else\n// Note: The cast alone to uint8_t is actually enough.\n// GCC throws out the \"& 0xff\", and the size is no different.\n// Some compilers need it.\n#define BGRAB0(__usi__)  ((uint8_t)((__usi__) & 0xff ))\n#define BGRAB1(__usi__)  ((uint8_t)(((__usi__) >> 8) & 0xff))\n#define BGRAB2(__usi__)  ((uint8_t)(((__usi__) >> 16) & 0xff))\n#define BGRAB3(__usi__)  ((uint8_t)(((__usi__) >> 24) & 0xff))\n#define BGRAB4(__usi__)  ((uint8_t)(((__usi__) >> 32) & 0xff))\n#define BGRAB5(__usi__)  ((uint8_t)(((__usi__) >> 40) & 0xff))\n#define BGRAB6(__usi__)  ((uint8_t)(((__usi__) >> 48) & 0xff))\n#define BGRAB7(__usi__)  ((uint8_t)(((__usi__) >> 56) & 0xff))\n#endif\n#define BOVER1(__usi__)  ((uint16_t)(__usi__) << 8)\n#define BOVER2(__usi__)  ((uint32_t)(__usi__) << 16)\n#define BOVER3(__usi__)  ((uint32_t)(__usi__) << 24)\n#define BOVER4(__usi__)  ((uint64_t)(__usi__) << 32)\n#define BOVER5(__usi__)  ((uint64_t)(__usi__) << 40)\n#define BOVER6(__usi__)  ((uint64_t)(__usi__) << 48)\n#define BOVER7(__usi__)  ((uint64_t)(__usi__) << 56)\n\n// These are the smallest and fastest ways I have found so far in pure C/C++.\n#define BMAKE16(__usc1__,__usc0__) ((uint16_t)((uint16_t)(__usc0__) | (uint16_t)BOVER1(__usc1__)))\n#define BMAKE32(__usc3__,__usc2__,__usc1__,__usc0__) ((uint32_t)((uint32_t)(__usc0__) | (uint32_t)BOVER1(__usc1__) | (uint32_t)BOVER2(__usc2__) | (uint32_t)BOVER3(__usc3__)))\n#define BMAKE64(__usc7__,__usc6__,__usc5__,__usc4__,__usc3__,__usc2__,__usc1__,__usc0__) ((uint64_t)((uint64_t)__usc0__ | (uint64_t)BOVER1(__usc1__) | (uint64_t)BOVER2(__usc2__) | (uint64_t)BOVER3(__usc3__) | (uint64_t)BOVER4(__usc4__) | (uint64_t)BOVER5(__usc5__) | (uint64_t)BOVER6(__usc6__) | (uint64_t)BOVER1(__usc7__)))\n#endif\n\n/*\n * Debug macros: Strings are stored in progmem (flash) instead of RAM.\n */\n#define USBTRACE(s) (Notify(PSTR(s), 0x80))\n#define USBTRACE1(s,l) (Notify(PSTR(s), l))\n#define USBTRACE2(s,r) (Notify(PSTR(s), 0x80), D_PrintHex((r), 0x80), Notify(PSTR(\"\\r\\n\"), 0x80))\n#define USBTRACE3(s,r,l) (Notify(PSTR(s), l), D_PrintHex((r), l), Notify(PSTR(\"\\r\\n\"), l))\n\n\n#endif /* MACROS_H */\n\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/masstorage.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#include \"masstorage.h\"\n\nconst uint8_t BulkOnly::epDataInIndex = 1;\nconst uint8_t BulkOnly::epDataOutIndex = 2;\nconst uint8_t BulkOnly::epInterruptInIndex = 3;\n\n////////////////////////////////////////////////////////////////////////////////\n\n// Interface code\n\n////////////////////////////////////////////////////////////////////////////////\n\n/**\n * Get the capacity of the media\n *\n * @param lun Logical Unit Number\n * @return media capacity\n */\nuint32_t BulkOnly::GetCapacity(uint8_t lun) {\n        if(LUNOk[lun])\n                return CurrentCapacity[lun];\n        return 0LU;\n}\n\n/**\n * Get the sector (block) size used on the media\n *\n * @param lun Logical Unit Number\n * @return media sector size\n */\nuint16_t BulkOnly::GetSectorSize(uint8_t lun) {\n        if(LUNOk[lun])\n                return CurrentSectorSize[lun];\n        return 0U;\n}\n\n/**\n * Test if LUN is ready for use\n *\n * @param lun Logical Unit Number\n * @return true if LUN is ready for use\n */\nbool BulkOnly::LUNIsGood(uint8_t lun) {\n        return LUNOk[lun];\n}\n\n/**\n * Test if LUN is write protected\n *\n * @param lun Logical Unit Number\n * @return cached status of write protect switch\n */\nbool BulkOnly::WriteProtected(uint8_t lun) {\n        return WriteOk[lun];\n}\n\n/**\n * Wrap and execute a SCSI CDB with length of 6\n *\n * @param cdb CDB to execute\n * @param buf_size Size of expected transaction\n * @param buf Buffer\n * @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT\n * @return\n */\nuint8_t BulkOnly::SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) {\n        // promote buf_size to 32bits.\n        CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir);\n        //SetCurLUN(cdb->LUN);\n        return (HandleSCSIError(Transaction(&cbw, buf_size, buf)));\n}\n\n/**\n * Wrap and execute a SCSI CDB with length of 10\n *\n * @param cdb CDB to execute\n * @param buf_size Size of expected transaction\n * @param buf Buffer\n * @param dir MASS_CMD_DIR_IN | MASS_CMD_DIR_OUT\n * @return\n */\nuint8_t BulkOnly::SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir) {\n        // promote buf_size to 32bits.\n        CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)buf_size, cdb, dir);\n        //SetCurLUN(cdb->LUN);\n        return (HandleSCSIError(Transaction(&cbw, buf_size, buf)));\n}\n\n/**\n * Lock or Unlock the tray or door on device.\n * Caution: Some devices with buggy firmware will lock up.\n *\n * @param lun Logical Unit Number\n * @param lock 1 to lock, 0 to unlock\n * @return\n */\nuint8_t BulkOnly::LockMedia(uint8_t lun, uint8_t lock) {\n        Notify(PSTR(\"\\r\\nLockMedia\\r\\n\"), 0x80);\n        Notify(PSTR(\"---------\\r\\n\"), 0x80);\n\n        CDB6_t cdb = CDB6_t(SCSI_CMD_PREVENT_REMOVAL, lun, (uint8_t)0, lock);\n        return SCSITransaction6(&cdb, (uint16_t)0, NULL, (uint8_t)MASS_CMD_DIR_IN);\n}\n\n/**\n * Media control, for spindle motor and media tray or door.\n * This includes CDROM, TAPE and anything with a media loader.\n *\n * @param lun Logical Unit Number\n * @param ctl 0x00 Stop Motor, 0x01 Start Motor, 0x02 Eject Media, 0x03 Load Media\n * @return 0 on success\n */\nuint8_t BulkOnly::MediaCTL(uint8_t lun, uint8_t ctl) {\n        Notify(PSTR(\"\\r\\nMediaCTL\\r\\n\"), 0x80);\n        Notify(PSTR(\"-----------------\\r\\n\"), 0x80);\n\n        uint8_t rcode = MASS_ERR_UNIT_NOT_READY;\n        if(bAddress) {\n                CDB6_t cdb = CDB6_t(SCSI_CMD_START_STOP_UNIT, lun, ctl & 0x03, 0);\n                rcode = SCSITransaction6(&cdb, (uint16_t)0, NULL, (uint8_t)MASS_CMD_DIR_OUT);\n        } else {\n                SetCurLUN(lun);\n        }\n        return rcode;\n}\n\n/**\n * Read data from media\n *\n * @param lun Logical Unit Number\n * @param addr LBA address on media to read\n * @param bsize size of a block (we should probably use the cached size)\n * @param blocks how many blocks to read\n * @param buf memory that is able to hold the requested data\n * @return 0 on success\n */\nuint8_t BulkOnly::Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf) {\n        if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;\n        Notify(PSTR(\"\\r\\nRead LUN:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (lun, 0x90);\n        Notify(PSTR(\"\\r\\nLBA:\\t\\t\"), 0x90);\n        D_PrintHex<uint32_t > (addr, 0x90);\n        Notify(PSTR(\"\\r\\nblocks:\\t\\t\"), 0x90);\n        D_PrintHex<uint8_t > (blocks, 0x90);\n        Notify(PSTR(\"\\r\\nblock size:\\t\"), 0x90);\n        D_PrintHex<uint16_t > (bsize, 0x90);\n        Notify(PSTR(\"\\r\\n---------\\r\\n\"), 0x80);\n        CDB10_t cdb = CDB10_t(SCSI_CMD_READ_10, lun, blocks, addr);\n\nagain:\n        uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), buf, (uint8_t)MASS_CMD_DIR_IN);\n\n        if(er == MASS_ERR_STALL) {\n                MediaCTL(lun, 1);\n                delay(150);\n                if(!TestUnitReady(lun)) goto again;\n        }\n        return er;\n}\n\n/**\n * Write data to media\n *\n * @param lun Logical Unit Number\n * @param addr LBA address on media to write\n * @param bsize size of a block (we should probably use the cached size)\n * @param blocks how many blocks to write\n * @param buf memory that contains the data to write\n * @return 0 on success\n */\nuint8_t BulkOnly::Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t * buf) {\n        if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;\n        if(!WriteOk[lun]) return MASS_ERR_WRITE_PROTECTED;\n        Notify(PSTR(\"\\r\\nWrite LUN:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (lun, 0x90);\n        Notify(PSTR(\"\\r\\nLBA:\\t\\t\"), 0x90);\n        D_PrintHex<uint32_t > (addr, 0x90);\n        Notify(PSTR(\"\\r\\nblocks:\\t\\t\"), 0x90);\n        D_PrintHex<uint8_t > (blocks, 0x90);\n        Notify(PSTR(\"\\r\\nblock size:\\t\"), 0x90);\n        D_PrintHex<uint16_t > (bsize, 0x90);\n        Notify(PSTR(\"\\r\\n---------\\r\\n\"), 0x80);\n        CDB10_t cdb = CDB10_t(SCSI_CMD_WRITE_10, lun, blocks, addr);\n\nagain:\n        uint8_t er = SCSITransaction10(&cdb, ((uint16_t)bsize * blocks), (void*)buf, (uint8_t)MASS_CMD_DIR_OUT);\n\n        if(er == MASS_ERR_WRITE_STALL) {\n                MediaCTL(lun, 1);\n                delay(150);\n                if(!TestUnitReady(lun)) goto again;\n        }\n        return er;\n}\n\n// End of user functions, the remaining code below is driver internals.\n// Only developer serviceable parts below!\n\n////////////////////////////////////////////////////////////////////////////////\n\n// Main driver code\n\n////////////////////////////////////////////////////////////////////////////////\n\nBulkOnly::BulkOnly(USB *p) :\npUsb(p),\nbAddress(0),\nbIface(0),\nbNumEP(1),\nqNextPollTime(0),\nbPollEnable(false),\n//dCBWTag(0),\nbLastUsbError(0) {\n        ClearAllEP();\n        dCBWTag = 0;\n        if(pUsb)\n                pUsb->RegisterDeviceClass(this);\n}\n\n/**\n * USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET == success\n * We need to standardize either the rcode, or change the API to return values\n * so a signal that additional actions are required can be produced.\n * Some of these codes do exist already.\n *\n * TECHNICAL: We could do most of this code elsewhere, with the exception of checking the class instance.\n * Doing so would save some program memory when using multiple drivers.\n *\n * @param parent USB address of parent\n * @param port address of port on parent\n * @param lowspeed true if device is low speed\n * @return\n */\nuint8_t BulkOnly::ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed) {\n\n        const uint8_t constBufSize = sizeof (USB_DEVICE_DESCRIPTOR);\n\n        uint8_t buf[constBufSize];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        USBTRACE(\"MS ConfigureDevice\\r\\n\");\n        ClearAllEP();\n        AddressPool &addrPool = pUsb->GetAddressPool();\n\n\n        if(bAddress)\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n\n        // <TECHNICAL>\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n        if(!p) {\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n        }\n\n        if(!p->epinfo) {\n                USBTRACE(\"epinfo\\r\\n\");\n                return USB_ERROR_EPINFO_IS_NULL;\n        }\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, constBufSize, (uint8_t*)buf);\n\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(rcode) {\n                goto FailGetDevDescr;\n        }\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from the device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n        // Steal and abuse from epInfo structure to save on memory.\n        epInfo[1].epAddr = udd->bNumConfigurations;\n        // </TECHNICAL>\n        return USB_ERROR_CONFIG_REQUIRES_ADDITIONAL_RESET;\n\nFailGetDevDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetDevDescr(rcode);\n#endif\n        rcode = USB_ERROR_FailGetDevDescr;\n\n        Release();\n        return rcode;\n};\n\n/**\n *\n * @param parent (not used)\n * @param port (not used)\n * @param lowspeed true if device is low speed\n * @return 0 for success\n */\nuint8_t BulkOnly::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        uint8_t rcode;\n        uint8_t num_of_conf = epInfo[1].epAddr; // number of configurations\n        epInfo[1].epAddr = 0;\n        USBTRACE(\"MS Init\\r\\n\");\n\n        AddressPool &addrPool = pUsb->GetAddressPool();\n        UsbDevice *p = addrPool.GetUsbDevicePtr(bAddress);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        // Assign new address to the device\n        delay(2000);\n        rcode = pUsb->setAddr(0, 0, bAddress);\n\n        if(rcode) {\n                p->lowspeed = false;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n                USBTRACE2(\"setAddr:\", rcode);\n                return rcode;\n        }\n\n        USBTRACE2(\"Addr:\", bAddress);\n\n        p->lowspeed = false;\n\n        p = addrPool.GetUsbDevicePtr(bAddress);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        p->lowspeed = lowspeed;\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, 1, epInfo);\n\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        USBTRACE2(\"NC:\", num_of_conf);\n\n        for(uint8_t i = 0; i < num_of_conf; i++) {\n                ConfigDescParser< USB_CLASS_MASS_STORAGE,\n                        MASS_SUBCLASS_SCSI,\n                        MASS_PROTO_BBB,\n                        CP_MASK_COMPARE_CLASS |\n                        CP_MASK_COMPARE_SUBCLASS |\n                        CP_MASK_COMPARE_PROTOCOL > BulkOnlyParser(this);\n\n                rcode = pUsb->getConfDescr(bAddress, 0, i, &BulkOnlyParser);\n\n                if(rcode)\n                        goto FailGetConfDescr;\n\n                if(bNumEP > 1)\n                        break;\n        }\n\n        if(bNumEP < 3)\n                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\n        // Assign epInfo to epinfo pointer\n        pUsb->setEpInfoEntry(bAddress, bNumEP, epInfo);\n\n        USBTRACE2(\"Conf:\", bConfNum);\n\n        // Set Configuration Value\n        rcode = pUsb->setConf(bAddress, 0, bConfNum);\n\n        if(rcode)\n                goto FailSetConfDescr;\n\n        //Linux does a 1sec delay after this.\n        delay(1000);\n\n        rcode = GetMaxLUN(&bMaxLUN);\n        if(rcode)\n                goto FailGetMaxLUN;\n\n        if(bMaxLUN >= MASS_MAX_SUPPORTED_LUN) bMaxLUN = MASS_MAX_SUPPORTED_LUN - 1;\n        ErrorMessage<uint8_t > (PSTR(\"MaxLUN\"), bMaxLUN);\n\n        delay(1000); // Delay a bit for slow firmware.\n\n        for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {\n                InquiryResponse response;\n                rcode = Inquiry(lun, sizeof (InquiryResponse), (uint8_t*) & response);\n                if(rcode) {\n                        ErrorMessage<uint8_t > (PSTR(\"Inquiry\"), rcode);\n                } else {\n#if 0\n                        printf(\"LUN %i `\", lun);\n                        uint8_t *buf = response.VendorID;\n                        for(int i = 0; i < 28; i++) printf(\"%c\", buf[i]);\n                        printf(\"'\\r\\nQualifier %1.1X \", response.PeripheralQualifier);\n                        printf(\"Device type %2.2X \", response.DeviceType);\n                        printf(\"RMB %1.1X \", response.Removable);\n                        printf(\"SSCS %1.1X \", response.SCCS);\n                        uint8_t sv = response.Version;\n                        printf(\"SCSI version %2.2X\\r\\nDevice conforms to \", sv);\n                        switch(sv) {\n                                case 0:\n                                        printf(\"No specific\");\n                                        break;\n                                case 1:\n                                        printf(\"ANSI X3.131-1986 (ANSI 1)\");\n                                        break;\n                                case 2:\n                                        printf(\"ANSI X3.131-1994 (ANSI 2)\");\n                                        break;\n                                case 3:\n                                        printf(\"ANSI INCITS 301-1997 (SPC)\");\n                                        break;\n                                case 4:\n                                        printf(\"ANSI INCITS 351-2001 (SPC-2)\");\n                                        break;\n                                case 5:\n                                        printf(\"ANSI INCITS 408-2005 (SPC-4)\");\n                                        break;\n                                case 6:\n                                        printf(\"T10/1731-D (SPC-4)\");\n                                        break;\n                                default:\n                                        printf(\"unknown\");\n                        }\n                        printf(\" standards.\\r\\n\");\n#endif\n                        uint8_t tries = 0xf0;\n                        while((rcode = TestUnitReady(lun))) {\n                                if(rcode == 0x08) break; // break on no media, this is OK to do.\n                                // try to lock media and spin up\n                                if(tries < 14) {\n                                        LockMedia(lun, 1);\n                                        MediaCTL(lun, 1); // I actually have a USB stick that needs this!\n                                } else delay(2 * (tries + 1));\n                                tries++;\n                                if(!tries) break;\n                        }\n                        if(!rcode) {\n                                delay(1000);\n                                LUNOk[lun] = CheckLUN(lun);\n                                if(!LUNOk[lun]) LUNOk[lun] = CheckLUN(lun);\n                        }\n                }\n        }\n\n\n        CheckMedia();\n\n        rcode = OnInit();\n\n        if(rcode)\n                goto FailOnInit;\n\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"MS configured\\r\\n\\r\\n\");\n#endif\n\n        bPollEnable = true;\n\n        //USBTRACE(\"Poll enabled\\r\\n\");\n        return 0;\n\nFailSetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetConfDescr();\n        goto Fail;\n#endif\n\nFailOnInit:\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"OnInit:\");\n        goto Fail;\n#endif\n\nFailGetMaxLUN:\n#ifdef DEBUG_USB_HOST\n        USBTRACE(\"GetMaxLUN:\");\n        goto Fail;\n#endif\n\n        //#ifdef DEBUG_USB_HOST\n        //FailInvalidSectorSize:\n        //        USBTRACE(\"Sector Size is NOT VALID: \");\n        //        goto Fail;\n        //#endif\n\nFailSetDevTblEntry:\n#ifdef DEBUG_USB_HOST\n        NotifyFailSetDevTblEntry();\n        goto Fail;\n#endif\n\nFailGetConfDescr:\n#ifdef DEBUG_USB_HOST\n        NotifyFailGetConfDescr();\n#endif\n\n#ifdef DEBUG_USB_HOST\nFail:\n        NotifyFail(rcode);\n#endif\n        Release();\n        return rcode;\n}\n\n/**\n * For driver use only.\n *\n * @param conf\n * @param iface\n * @param alt\n * @param proto\n * @param pep\n */\nvoid BulkOnly::EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR * pep) {\n        ErrorMessage<uint8_t > (PSTR(\"Conf.Val\"), conf);\n        ErrorMessage<uint8_t > (PSTR(\"Iface Num\"), iface);\n        ErrorMessage<uint8_t > (PSTR(\"Alt.Set\"), alt);\n\n        bConfNum = conf;\n\n        uint8_t index;\n\n#if 1\n        if((pep->bmAttributes & 0x02) == 2) {\n                index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;\n                // Fill in the endpoint info structure\n                epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);\n                epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;\n                epInfo[index].epAttribs = 0;\n\n                bNumEP++;\n\n                PrintEndpointDescriptor(pep);\n\n        }\n#else\n        if((pep->bmAttributes & 0x03) == 3 && (pep->bEndpointAddress & 0x80) == 0x80)\n                index = epInterruptInIndex;\n        else\n                if((pep->bmAttributes & 0x02) == 2)\n                index = ((pep->bEndpointAddress & 0x80) == 0x80) ? epDataInIndex : epDataOutIndex;\n        else\n                return;\n\n        // Fill in the endpoint info structure\n        epInfo[index].epAddr = (pep->bEndpointAddress & 0x0F);\n        epInfo[index].maxPktSize = (uint8_t)pep->wMaxPacketSize;\n        epInfo[index].epAttribs = 0;\n\n        bNumEP++;\n\n        PrintEndpointDescriptor(pep);\n#endif\n}\n\n/**\n * For driver use only.\n *\n * @return\n */\nuint8_t BulkOnly::Release() {\n        ClearAllEP();\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n        return 0;\n}\n\n/**\n * For driver use only.\n *\n * @param lun Logical Unit Number\n * @return true if LUN is ready for use.\n */\nbool BulkOnly::CheckLUN(uint8_t lun) {\n        uint8_t rcode;\n        Capacity capacity;\n        for(uint8_t i = 0; i < 8; i++) capacity.data[i] = 0;\n\n        rcode = ReadCapacity10(lun, (uint8_t*)capacity.data);\n        if(rcode) {\n                //printf(\">>>>>>>>>>>>>>>>ReadCapacity returned %i\\r\\n\", rcode);\n                return false;\n        }\n        ErrorMessage<uint8_t > (PSTR(\">>>>>>>>>>>>>>>>CAPACITY OK ON LUN\"), lun);\n        for(uint8_t i = 0; i < 8 /*sizeof (Capacity)*/; i++)\n                D_PrintHex<uint8_t > (capacity.data[i], 0x80);\n        Notify(PSTR(\"\\r\\n\\r\\n\"), 0x80);\n        // Only 512/1024/2048/4096 are valid values!\n        uint32_t c = BMAKE32(capacity.data[4], capacity.data[5], capacity.data[6], capacity.data[7]);\n        if(c != 0x0200LU && c != 0x0400LU && c != 0x0800LU && c != 0x1000LU) {\n                return false;\n        }\n        // Store capacity information.\n        CurrentSectorSize[lun] = (uint16_t)(c); // & 0xFFFF);\n\n        CurrentCapacity[lun] = BMAKE32(capacity.data[0], capacity.data[1], capacity.data[2], capacity.data[3]) + 1;\n        if(CurrentCapacity[lun] == /*0xffffffffLU */ 0x01LU || CurrentCapacity[lun] == 0x00LU) {\n                // Buggy firmware will report 0xffffffff or 0 for no media\n                if(CurrentCapacity[lun])\n                        ErrorMessage<uint8_t > (PSTR(\">>>>>>>>>>>>>>>>BUGGY FIRMWARE. CAPACITY FAIL ON LUN\"), lun);\n                return false;\n        }\n        delay(20);\n        Page3F(lun);\n        if(!TestUnitReady(lun)) return true;\n        return false;\n}\n\n/**\n * For driver use only.\n *\n * Scan for media change on all LUNs\n */\nvoid BulkOnly::CheckMedia() {\n        for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {\n                if(TestUnitReady(lun)) {\n                        LUNOk[lun] = false;\n                        continue;\n                }\n                if(!LUNOk[lun])\n                        LUNOk[lun] = CheckLUN(lun);\n        }\n#if 0\n        printf(\"}}}}}}}}}}}}}}}}STATUS \");\n        for(uint8_t lun = 0; lun <= bMaxLUN; lun++) {\n                if(LUNOk[lun])\n                        printf(\"#\");\n                else printf(\".\");\n        }\n        printf(\"\\r\\n\");\n#endif\n        qNextPollTime = millis() + 2000;\n}\n\n/**\n * For driver use only.\n *\n * @return\n */\nuint8_t BulkOnly::Poll() {\n        //uint8_t rcode = 0;\n\n        if(!bPollEnable)\n                return 0;\n\n        if((long)(millis() - qNextPollTime) >= 0L) {\n                CheckMedia();\n        }\n        //rcode = 0;\n\n        return 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////\n\n\n// SCSI code\n\n\n////////////////////////////////////////////////////////////////////////////////\n\n/**\n * For driver use only.\n *\n * @param plun\n * @return\n */\nuint8_t BulkOnly::GetMaxLUN(uint8_t *plun) {\n        uint8_t ret = pUsb->ctrlReq(bAddress, 0, bmREQ_MASSIN, MASS_REQ_GET_MAX_LUN, 0, 0, bIface, 1, 1, plun, NULL);\n\n        if(ret == hrSTALL)\n                *plun = 0;\n\n        return 0;\n}\n\n/**\n * For driver use only. Used during Driver Init\n *\n * @param lun Logical Unit Number\n * @param bsize\n * @param buf\n * @return\n */\nuint8_t BulkOnly::Inquiry(uint8_t lun, uint16_t bsize, uint8_t *buf) {\n        Notify(PSTR(\"\\r\\nInquiry\\r\\n\"), 0x80);\n        Notify(PSTR(\"---------\\r\\n\"), 0x80);\n\n        CDB6_t cdb = CDB6_t(SCSI_CMD_INQUIRY, lun, 0LU, (uint8_t)bsize, 0);\n        uint8_t rc = SCSITransaction6(&cdb, bsize, buf, (uint8_t)MASS_CMD_DIR_IN);\n\n        return rc;\n}\n\n/**\n * For driver use only.\n *\n * @param lun Logical Unit Number\n * @return\n */\nuint8_t BulkOnly::TestUnitReady(uint8_t lun) {\n        //SetCurLUN(lun);\n        if(!bAddress)\n                return MASS_ERR_UNIT_NOT_READY;\n\n        Notify(PSTR(\"\\r\\nTestUnitReady\\r\\n\"), 0x80);\n        Notify(PSTR(\"-----------------\\r\\n\"), 0x80);\n\n        CDB6_t cdb = CDB6_t(SCSI_CMD_TEST_UNIT_READY, lun, (uint8_t)0, 0);\n        return SCSITransaction6(&cdb, 0, NULL, (uint8_t)MASS_CMD_DIR_IN);\n\n}\n\n/**\n * For driver use only.\n *\n * @param lun Logical Unit Number\n * @param pc\n * @param page\n * @param subpage\n * @param len\n * @param pbuf\n * @return\n */\nuint8_t BulkOnly::ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t * pbuf) {\n        Notify(PSTR(\"\\r\\rModeSense\\r\\n\"), 0x80);\n        Notify(PSTR(\"------------\\r\\n\"), 0x80);\n\n        CDB6_t cdb = CDB6_t(SCSI_CMD_TEST_UNIT_READY, lun, (uint32_t)((((pc << 6) | page) << 8) | subpage), len, 0);\n        return SCSITransaction6(&cdb, len, pbuf, (uint8_t)MASS_CMD_DIR_IN);\n}\n\n/**\n * For driver use only.\n *\n * @param lun Logical Unit Number\n * @param bsize\n * @param buf\n * @return\n */\nuint8_t BulkOnly::ReadCapacity10(uint8_t lun, uint8_t *buf) {\n        Notify(PSTR(\"\\r\\nReadCapacity\\r\\n\"), 0x80);\n        Notify(PSTR(\"---------------\\r\\n\"), 0x80);\n\n        CDB10_t cdb = CDB10_t(SCSI_CMD_READ_CAPACITY_10, lun);\n        return SCSITransaction10(&cdb, 8, buf, (uint8_t)MASS_CMD_DIR_IN);\n}\n\n/**\n * For driver use only.\n *\n * Page 3F contains write protect status.\n *\n * @param lun Logical Unit Number to test.\n * @return Write protect switch status.\n */\nuint8_t BulkOnly::Page3F(uint8_t lun) {\n        uint8_t buf[192];\n        for(int i = 0; i < 192; i++) {\n                buf[i] = 0x00;\n        }\n        WriteOk[lun] = true;\n        uint8_t rc = ModeSense6(lun, 0, 0x3f, 0, 192, buf);\n        if(!rc) {\n                WriteOk[lun] = ((buf[2] & 0x80) == 0);\n                Notify(PSTR(\"Mode Sense: \"), 0x80);\n                for(int i = 0; i < 4; i++) {\n                        D_PrintHex<uint8_t > (buf[i], 0x80);\n                        Notify(PSTR(\" \"), 0x80);\n                }\n                Notify(PSTR(\"\\r\\n\"), 0x80);\n        }\n        return rc;\n}\n\n/**\n * For driver use only.\n *\n * @param lun Logical Unit Number\n * @param size\n * @param buf\n * @return\n */\nuint8_t BulkOnly::RequestSense(uint8_t lun, uint16_t size, uint8_t *buf) {\n        Notify(PSTR(\"\\r\\nRequestSense\\r\\n\"), 0x80);\n        Notify(PSTR(\"----------------\\r\\n\"), 0x80);\n\n        CDB6_t cdb = CDB6_t(SCSI_CMD_REQUEST_SENSE, lun, 0LU, (uint8_t)size, 0);\n        CommandBlockWrapper cbw = CommandBlockWrapper(++dCBWTag, (uint32_t)size, &cdb, (uint8_t)MASS_CMD_DIR_IN);\n        //SetCurLUN(lun);\n        return Transaction(&cbw, size, buf);\n}\n\n\n////////////////////////////////////////////////////////////////////////////////\n\n\n// USB code\n\n\n////////////////////////////////////////////////////////////////////////////////\n\n/**\n * For driver use only.\n *\n * @param index\n * @return\n */\nuint8_t BulkOnly::ClearEpHalt(uint8_t index) {\n        if(index == 0)\n                return 0;\n\n        uint8_t ret = 0;\n\n        while((ret = (pUsb->ctrlReq(bAddress, 0, USB_SETUP_HOST_TO_DEVICE | USB_SETUP_TYPE_STANDARD | USB_SETUP_RECIPIENT_ENDPOINT, USB_REQUEST_CLEAR_FEATURE, USB_FEATURE_ENDPOINT_HALT, 0, ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr), 0, 0, NULL, NULL)) == 0x01))\n                delay(6);\n\n        if(ret) {\n                ErrorMessage<uint8_t > (PSTR(\"ClearEpHalt\"), ret);\n                ErrorMessage<uint8_t > (PSTR(\"EP\"), ((index == epDataInIndex) ? (0x80 | epInfo[index].epAddr) : epInfo[index].epAddr));\n                return ret;\n        }\n        epInfo[index].bmSndToggle = 0;\n        epInfo[index].bmRcvToggle = 0;\n        // epAttribs = 0;\n        return 0;\n}\n\n/**\n * For driver use only.\n *\n */\nvoid BulkOnly::Reset() {\n        while(pUsb->ctrlReq(bAddress, 0, bmREQ_MASSOUT, MASS_REQ_BOMSR, 0, 0, bIface, 0, 0, NULL, NULL) == 0x01) delay(6);\n}\n\n/**\n * For driver use only.\n *\n * @return 0 if successful\n */\nuint8_t BulkOnly::ResetRecovery() {\n        Notify(PSTR(\"\\r\\nResetRecovery\\r\\n\"), 0x80);\n        Notify(PSTR(\"-----------------\\r\\n\"), 0x80);\n\n        delay(6);\n        Reset();\n        delay(6);\n        ClearEpHalt(epDataInIndex);\n        delay(6);\n        bLastUsbError = ClearEpHalt(epDataOutIndex);\n        delay(6);\n        return bLastUsbError;\n}\n\n/**\n * For driver use only.\n *\n * Clear all EP data and clear all LUN status\n */\nvoid BulkOnly::ClearAllEP() {\n        for(uint8_t i = 0; i < MASS_MAX_ENDPOINTS; i++) {\n                epInfo[i].epAddr = 0;\n                epInfo[i].maxPktSize = (i) ? 0 : 8;\n                epInfo[i].epAttribs = 0;\n\n                epInfo[i].bmNakPower = USB_NAK_DEFAULT;\n        }\n\n        for(uint8_t i = 0; i < MASS_MAX_SUPPORTED_LUN; i++) {\n                LUNOk[i] = false;\n                WriteOk[i] = false;\n                CurrentCapacity[i] = 0lu;\n                CurrentSectorSize[i] = 0;\n        }\n\n        bIface = 0;\n        bNumEP = 1;\n        bAddress = 0;\n        qNextPollTime = 0;\n        bPollEnable = false;\n        bLastUsbError = 0;\n        bMaxLUN = 0;\n        bTheLUN = 0;\n}\n\n/**\n * For driver use only.\n *\n * @param pcsw\n * @param pcbw\n * @return\n */\nbool BulkOnly::IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw) {\n        if(pcsw->dCSWSignature != MASS_CSW_SIGNATURE) {\n                Notify(PSTR(\"CSW:Sig error\\r\\n\"), 0x80);\n                return false;\n        }\n        if(pcsw->dCSWTag != pcbw->dCBWTag) {\n                Notify(PSTR(\"CSW:Wrong tag\\r\\n\"), 0x80);\n                return false;\n        }\n        return true;\n}\n\n/**\n * For driver use only.\n *\n * @param error\n * @param index\n * @return\n */\nuint8_t BulkOnly::HandleUsbError(uint8_t error, uint8_t index) {\n        uint8_t count = 3;\n\n        bLastUsbError = error;\n        //if (error)\n        //ClearEpHalt(index);\n        while(error && count) {\n                if(error != hrSUCCESS) {\n                        ErrorMessage<uint8_t > (PSTR(\"USB Error\"), error);\n                        ErrorMessage<uint8_t > (PSTR(\"Index\"), index);\n                }\n                switch(error) {\n                                // case hrWRONGPID:\n                        case hrSUCCESS:\n                                return MASS_ERR_SUCCESS;\n                        case hrBUSY:\n                                // SIE is busy, just hang out and try again.\n                                return MASS_ERR_UNIT_BUSY;\n                        case hrTIMEOUT:\n                        case hrJERR: return MASS_ERR_DEVICE_DISCONNECTED;\n                        case hrSTALL:\n                                if(index == 0)\n                                        return MASS_ERR_STALL;\n                                ClearEpHalt(index);\n                                if(index != epDataInIndex)\n                                        return MASS_ERR_WRITE_STALL;\n                                return MASS_ERR_STALL;\n\n                        case hrNAK:\n                                if(index == 0)\n                                        return MASS_ERR_UNIT_BUSY;\n                                return MASS_ERR_UNIT_BUSY;\n\n                        case hrTOGERR:\n                                // Handle a very super rare corner case, where toggles become de-synched.\n                                // I have only ran into one device that has this firmware bug, and this is\n                                // the only clean way to get back into sync with the buggy device firmware.\n                                //   --AJK\n                                if(bAddress && bConfNum) {\n                                        error = pUsb->setConf(bAddress, 0, bConfNum);\n\n                                        if(error)\n                                                break;\n                                }\n                                return MASS_ERR_SUCCESS;\n                        default:\n                                ErrorMessage<uint8_t > (PSTR(\"\\r\\nUSB\"), error);\n                                return MASS_ERR_GENERAL_USB_ERROR;\n                }\n                count--;\n        } // while\n\n        return ((error && !count) ? MASS_ERR_GENERAL_USB_ERROR : MASS_ERR_SUCCESS);\n}\n\n#if MS_WANT_PARSER\n\nuint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf) {\n        return Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf, 0);\n}\n#endif\n\n/**\n * For driver use only.\n *\n * @param pcbw\n * @param buf_size\n * @param buf\n * @param flags\n * @return\n */\nuint8_t BulkOnly::Transaction(CommandBlockWrapper *pcbw, uint16_t buf_size, void *buf\n#if MS_WANT_PARSER\n        , uint8_t flags\n#endif\n        ) {\n\n#if MS_WANT_PARSER\n        uint16_t bytes = (pcbw->dCBWDataTransferLength > buf_size) ? buf_size : pcbw->dCBWDataTransferLength;\n        printf(\"Transfersize %i\\r\\n\", bytes);\n        delay(1000);\n\n        bool callback = (flags & MASS_TRANS_FLG_CALLBACK) == MASS_TRANS_FLG_CALLBACK;\n#else\n        uint16_t bytes = buf_size;\n#endif\n        bool write = (pcbw->bmCBWFlags & MASS_CMD_DIR_IN) != MASS_CMD_DIR_IN;\n        uint8_t ret = 0;\n        uint8_t usberr;\n        CommandStatusWrapper csw; // up here, we allocate ahead to save cpu cycles.\n        SetCurLUN(pcbw->bmCBWLUN);\n        ErrorMessage<uint32_t > (PSTR(\"CBW.dCBWTag\"), pcbw->dCBWTag);\n\n        while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw)) == hrBUSY) delay(1);\n\n        ret = HandleUsbError(usberr, epDataOutIndex);\n        //ret = HandleUsbError(pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, sizeof (CommandBlockWrapper), (uint8_t*)pcbw), epDataOutIndex);\n        if(ret) {\n                ErrorMessage<uint8_t > (PSTR(\"============================ CBW\"), ret);\n        } else {\n                if(bytes) {\n                        if(!write) {\n#if MS_WANT_PARSER\n                                if(callback) {\n                                        uint8_t rbuf[bytes];\n                                        while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, rbuf)) == hrBUSY) delay(1);\n                                        if(usberr == hrSUCCESS) ((USBReadParser*)buf)->Parse(bytes, rbuf, 0);\n                                } else {\n#endif\n                                        while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*)buf)) == hrBUSY) delay(1);\n#if MS_WANT_PARSER\n\n                                }\n#endif\n                                ret = HandleUsbError(usberr, epDataInIndex);\n                        } else {\n                                while((usberr = pUsb->outTransfer(bAddress, epInfo[epDataOutIndex].epAddr, bytes, (uint8_t*)buf)) == hrBUSY) delay(1);\n                                ret = HandleUsbError(usberr, epDataOutIndex);\n                        }\n                        if(ret) {\n                                ErrorMessage<uint8_t > (PSTR(\"============================ DAT\"), ret);\n                        }\n                }\n        }\n\n        {\n                bytes = sizeof (CommandStatusWrapper);\n                int tries = 2;\n                while(tries--) {\n                        while((usberr = pUsb->inTransfer(bAddress, epInfo[epDataInIndex].epAddr, &bytes, (uint8_t*) & csw)) == hrBUSY) delay(1);\n                        if(!usberr) break;\n                        ClearEpHalt(epDataInIndex);\n                        if(tries) ResetRecovery();\n                }\n                if(!ret) {\n                        Notify(PSTR(\"CBW:\\t\\tOK\\r\\n\"), 0x80);\n                        Notify(PSTR(\"Data Stage:\\tOK\\r\\n\"), 0x80);\n                } else {\n                        // Throw away csw, IT IS NOT OF ANY USE.\n                        ResetRecovery();\n                        return ret;\n                }\n                ret = HandleUsbError(usberr, epDataInIndex);\n                if(ret) {\n                        ErrorMessage<uint8_t > (PSTR(\"============================ CSW\"), ret);\n                }\n                if(usberr == hrSUCCESS) {\n                        if(IsValidCSW(&csw, pcbw)) {\n                                //ErrorMessage<uint32_t > (PSTR(\"CSW.dCBWTag\"), csw.dCSWTag);\n                                //ErrorMessage<uint8_t > (PSTR(\"bCSWStatus\"), csw.bCSWStatus);\n                                //ErrorMessage<uint32_t > (PSTR(\"dCSWDataResidue\"), csw.dCSWDataResidue);\n                                Notify(PSTR(\"CSW:\\t\\tOK\\r\\n\\r\\n\"), 0x80);\n                                return csw.bCSWStatus;\n                        } else {\n                                // NOTE! Sometimes this is caused by the reported residue being wrong.\n                                // Get a different device. It isn't compliant, and should have never passed Q&A.\n                                // I own one... 05e3:0701 Genesys Logic, Inc. USB 2.0 IDE Adapter.\n                                // Other devices that exhibit this behavior exist in the wild too.\n                                // Be sure to check quirks in the Linux source code before reporting a bug. --xxxajk\n                                Notify(PSTR(\"Invalid CSW\\r\\n\"), 0x80);\n                                ResetRecovery();\n                                //return MASS_ERR_SUCCESS;\n                                return MASS_ERR_INVALID_CSW;\n                        }\n                }\n        }\n        return ret;\n}\n\n/**\n * For driver use only.\n *\n * @param lun Logical Unit Number\n * @return\n */\nuint8_t BulkOnly::SetCurLUN(uint8_t lun) {\n        if(lun > bMaxLUN)\n                return MASS_ERR_INVALID_LUN;\n        bTheLUN = lun;\n        return MASS_ERR_SUCCESS;\n};\n\n/**\n * For driver use only.\n *\n * @param status\n * @return\n */\nuint8_t BulkOnly::HandleSCSIError(uint8_t status) {\n        uint8_t ret = 0;\n\n        switch(status) {\n                case 0: return MASS_ERR_SUCCESS;\n\n                case 2:\n                        ErrorMessage<uint8_t > (PSTR(\"Phase Error\"), status);\n                        ErrorMessage<uint8_t > (PSTR(\"LUN\"), bTheLUN);\n                        ResetRecovery();\n                        return MASS_ERR_GENERAL_SCSI_ERROR;\n\n                case 1:\n                        ErrorMessage<uint8_t > (PSTR(\"SCSI Error\"), status);\n                        ErrorMessage<uint8_t > (PSTR(\"LUN\"), bTheLUN);\n                        RequestSenseResponce rsp;\n\n                        ret = RequestSense(bTheLUN, sizeof (RequestSenseResponce), (uint8_t*) & rsp);\n\n                        if(ret) {\n                                return MASS_ERR_GENERAL_SCSI_ERROR;\n                        }\n                        ErrorMessage<uint8_t > (PSTR(\"Response Code\"), rsp.bResponseCode);\n                        if(rsp.bResponseCode & 0x80) {\n                                Notify(PSTR(\"Information field: \"), 0x80);\n                                for(int i = 0; i < 4; i++) {\n                                        D_PrintHex<uint8_t > (rsp.CmdSpecificInformation[i], 0x80);\n                                        Notify(PSTR(\" \"), 0x80);\n                                }\n                                Notify(PSTR(\"\\r\\n\"), 0x80);\n                        }\n                        ErrorMessage<uint8_t > (PSTR(\"Sense Key\"), rsp.bmSenseKey);\n                        ErrorMessage<uint8_t > (PSTR(\"Add Sense Code\"), rsp.bAdditionalSenseCode);\n                        ErrorMessage<uint8_t > (PSTR(\"Add Sense Qual\"), rsp.bAdditionalSenseQualifier);\n                        // warning, this is not testing ASQ, only SK and ASC.\n                        switch(rsp.bmSenseKey) {\n                                case SCSI_S_UNIT_ATTENTION:\n                                        switch(rsp.bAdditionalSenseCode) {\n                                                case SCSI_ASC_MEDIA_CHANGED:\n                                                        return MASS_ERR_MEDIA_CHANGED;\n                                                default:\n                                                        return MASS_ERR_UNIT_NOT_READY;\n                                        }\n                                case SCSI_S_NOT_READY:\n                                        switch(rsp.bAdditionalSenseCode) {\n                                                case SCSI_ASC_MEDIUM_NOT_PRESENT:\n                                                        return MASS_ERR_NO_MEDIA;\n                                                default:\n                                                        return MASS_ERR_UNIT_NOT_READY;\n                                        }\n                                case SCSI_S_ILLEGAL_REQUEST:\n                                        switch(rsp.bAdditionalSenseCode) {\n                                                case SCSI_ASC_LBA_OUT_OF_RANGE:\n                                                        return MASS_ERR_BAD_LBA;\n                                                default:\n                                                        return MASS_ERR_CMD_NOT_SUPPORTED;\n                                        }\n                                default:\n                                        return MASS_ERR_GENERAL_SCSI_ERROR;\n                        }\n\n                        // case 4: return MASS_ERR_UNIT_BUSY; // Busy means retry later.\n                        //    case 0x05/0x14: we stalled out\n                        //    case 0x15/0x16: we naked out.\n                default:\n                        ErrorMessage<uint8_t > (PSTR(\"Gen SCSI Err\"), status);\n                        ErrorMessage<uint8_t > (PSTR(\"LUN\"), bTheLUN);\n                        return status;\n        } // switch\n}\n\n\n////////////////////////////////////////////////////////////////////////////////\n\n\n// Debugging code\n\n\n////////////////////////////////////////////////////////////////////////////////\n\n/**\n *\n * @param ep_ptr\n */\nvoid BulkOnly::PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR * ep_ptr) {\n        Notify(PSTR(\"Endpoint descriptor:\"), 0x80);\n        Notify(PSTR(\"\\r\\nLength:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bLength, 0x80);\n        Notify(PSTR(\"\\r\\nType:\\t\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bDescriptorType, 0x80);\n        Notify(PSTR(\"\\r\\nAddress:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bEndpointAddress, 0x80);\n        Notify(PSTR(\"\\r\\nAttributes:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bmAttributes, 0x80);\n        Notify(PSTR(\"\\r\\nMaxPktSize:\\t\"), 0x80);\n        D_PrintHex<uint16_t > (ep_ptr->wMaxPacketSize, 0x80);\n        Notify(PSTR(\"\\r\\nPoll Intrv:\\t\"), 0x80);\n        D_PrintHex<uint8_t > (ep_ptr->bInterval, 0x80);\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n}\n\n\n////////////////////////////////////////////////////////////////////////////////\n\n\n// misc/to kill/to-do\n\n\n////////////////////////////////////////////////////////////////////////////////\n\n/* We won't be needing this... */\nuint8_t BulkOnly::Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, USBReadParser * prs) {\n#if MS_WANT_PARSER\n        if(!LUNOk[lun]) return MASS_ERR_NO_MEDIA;\n        Notify(PSTR(\"\\r\\nRead (With parser)\\r\\n\"), 0x80);\n        Notify(PSTR(\"---------\\r\\n\"), 0x80);\n\n        CommandBlockWrapper cbw = CommandBlockWrapper();\n\n        cbw.dCBWSignature = MASS_CBW_SIGNATURE;\n        cbw.dCBWTag = ++dCBWTag;\n        cbw.dCBWDataTransferLength = ((uint32_t)bsize * blocks);\n        cbw.bmCBWFlags = MASS_CMD_DIR_IN,\n                cbw.bmCBWLUN = lun;\n        cbw.bmCBWCBLength = 10;\n\n        cbw.CBWCB[0] = SCSI_CMD_READ_10;\n        cbw.CBWCB[8] = blocks;\n        cbw.CBWCB[2] = ((addr >> 24) & 0xff);\n        cbw.CBWCB[3] = ((addr >> 16) & 0xff);\n        cbw.CBWCB[4] = ((addr >> 8) & 0xff);\n        cbw.CBWCB[5] = (addr & 0xff);\n\n        return HandleSCSIError(Transaction(&cbw, bsize, prs, 1));\n#else\n        return MASS_ERR_NOT_IMPLEMENTED;\n#endif\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/masstorage.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#if !defined(__MASSTORAGE_H__)\n#define __MASSTORAGE_H__\n\n// Cruft removal, makes driver smaller, faster.\n#ifndef MS_WANT_PARSER\n#define MS_WANT_PARSER 0\n#endif\n\n#include \"Usb.h\"\n\n#define bmREQ_MASSOUT       USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE\n#define bmREQ_MASSIN        USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE\n\n// Mass Storage Subclass Constants\n#define MASS_SUBCLASS_SCSI_NOT_REPORTED 0x00    // De facto use\n#define MASS_SUBCLASS_RBC               0x01\n#define MASS_SUBCLASS_ATAPI             0x02    // MMC-5 (ATAPI)\n#define MASS_SUBCLASS_OBSOLETE1         0x03    // Was QIC-157\n#define MASS_SUBCLASS_UFI               0x04    // Specifies how to interface Floppy Disk Drives to USB\n#define MASS_SUBCLASS_OBSOLETE2         0x05    // Was SFF-8070i\n#define MASS_SUBCLASS_SCSI              0x06    // SCSI Transparent Command Set\n#define MASS_SUBCLASS_LSDFS             0x07    // Specifies how host has to negotiate access before trying SCSI\n#define MASS_SUBCLASS_IEEE1667          0x08\n\n// Mass Storage Class Protocols\n#define MASS_PROTO_CBI                  0x00    // CBI (with command completion interrupt)\n#define MASS_PROTO_CBI_NO_INT           0x01    // CBI (without command completion interrupt)\n#define MASS_PROTO_OBSOLETE             0x02\n#define MASS_PROTO_BBB                  0x50    // Bulk Only Transport\n#define MASS_PROTO_UAS                  0x62\n\n// Request Codes\n#define MASS_REQ_ADSC                   0x00\n#define MASS_REQ_GET                    0xFC\n#define MASS_REQ_PUT                    0xFD\n#define MASS_REQ_GET_MAX_LUN            0xFE\n#define MASS_REQ_BOMSR                  0xFF    // Bulk-Only Mass Storage Reset\n\n#define MASS_CBW_SIGNATURE              0x43425355\n#define MASS_CSW_SIGNATURE              0x53425355\n\n#define MASS_CMD_DIR_OUT                0 // (0 << 7)\n#define MASS_CMD_DIR_IN                 0x80 //(1 << 7)\n\n/*\n * Reference documents from T10 (http://www.t10.org)\n * SCSI Primary Commands - 3 (SPC-3)\n * SCSI Block Commands - 2 (SBC-2)\n * Multi-Media Commands - 5 (MMC-5)\n */\n\n/* Group 1 commands (CDB's here are should all be 6-bytes) */\n#define SCSI_CMD_TEST_UNIT_READY        0x00\n#define SCSI_CMD_REQUEST_SENSE          0x03\n#define SCSI_CMD_FORMAT_UNIT            0x04\n#define SCSI_CMD_READ_6                 0x08\n#define SCSI_CMD_WRITE_6                0x0A\n#define SCSI_CMD_INQUIRY                0x12\n#define SCSI_CMD_MODE_SELECT_6          0x15\n#define SCSI_CMD_MODE_SENSE_6           0x1A\n#define SCSI_CMD_START_STOP_UNIT        0x1B\n#define SCSI_CMD_PREVENT_REMOVAL        0x1E\n/* Group 2 Commands (CDB's here are 10-bytes) */\n#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23\n#define SCSI_CMD_READ_CAPACITY_10       0x25\n#define SCSI_CMD_READ_10                0x28\n#define SCSI_CMD_WRITE_10               0x2A\n#define SCSI_CMD_SEEK_10                0x2B\n#define SCSI_CMD_ERASE_10               0x2C\n#define SCSI_CMD_WRITE_AND_VERIFY_10    0x2E\n#define SCSI_CMD_VERIFY_10              0x2F\n#define SCSI_CMD_SYNCHRONIZE_CACHE      0x35\n#define SCSI_CMD_WRITE_BUFFER           0x3B\n#define SCSI_CMD_READ_BUFFER            0x3C\n#define SCSI_CMD_READ_SUBCHANNEL        0x42\n#define SCSI_CMD_READ_TOC               0x43\n#define SCSI_CMD_READ_HEADER            0x44\n#define SCSI_CMD_PLAY_AUDIO_10          0x45\n#define SCSI_CMD_GET_CONFIGURATION      0x46\n#define SCSI_CMD_PLAY_AUDIO_MSF         0x47\n#define SCSI_CMD_PLAY_AUDIO_TI          0x48\n#define SCSI_CMD_PLAY_TRACK_REL_10      0x49\n#define SCSI_CMD_GET_EVENT_STATUS       0x4A\n#define SCSI_CMD_PAUSE_RESUME           0x4B\n#define SCSI_CMD_READ_DISC_INFORMATION  0x51\n#define SCSI_CMD_READ_TRACK_INFORMATION 0x52\n#define SCSI_CMD_RESERVE_TRACK          0x53\n#define SCSI_CMD_SEND_OPC_INFORMATION   0x54\n#define SCSI_CMD_MODE_SELECT_10         0x55\n#define SCSI_CMD_REPAIR_TRACK           0x58\n#define SCSI_CMD_MODE_SENSE_10          0x5A\n#define SCSI_CMD_CLOSE_TRACK_SESSION    0x5B\n#define SCSI_CMD_READ_BUFFER_CAPACITY   0x5C\n#define SCSI_CMD_SEND_CUE_SHEET         0x5D\n/* Group 5 Commands (CDB's here are 12-bytes) */\n#define SCSI_CMD_REPORT_LUNS            0xA0\n#define SCSI_CMD_BLANK                  0xA1\n#define SCSI_CMD_SECURITY_PROTOCOL_IN   0xA2\n#define SCSI_CMD_SEND_KEY               0xA3\n#define SCSI_CMD_REPORT_KEY             0xA4\n#define SCSI_CMD_PLAY_AUDIO_12          0xA5\n#define SCSI_CMD_LOAD_UNLOAD            0xA6\n#define SCSI_CMD_SET_READ_AHEAD         0xA7\n#define SCSI_CMD_READ_12                0xA8\n#define SCSI_CMD_PLAY_TRACK_REL_12      0xA9\n#define SCSI_CMD_WRITE_12               0xAA\n#define SCSI_CMD_READ_MEDIA_SERIAL_12   0xAB\n#define SCSI_CMD_GET_PERFORMANCE        0xAC\n#define SCSI_CMD_READ_DVD_STRUCTURE     0xAD\n#define SCSI_CMD_SECURITY_PROTOCOL_OUT  0xB5\n#define SCSI_CMD_SET_STREAMING          0xB6\n#define SCSI_CMD_READ_MSF               0xB9\n#define SCSI_CMD_SET_SPEED              0xBB\n#define SCSI_CMD_MECHANISM_STATUS       0xBD\n#define SCSI_CMD_READ_CD                0xBE\n#define SCSI_CMD_SEND_DISC_STRUCTURE    0xBF\n/* Vendor-unique Commands, included for completeness */\n#define SCSI_CMD_CD_PLAYBACK_STATUS     0xC4 /* SONY unique */\n#define SCSI_CMD_PLAYBACK_CONTROL       0xC9 /* SONY unique */\n#define SCSI_CMD_READ_CDDA              0xD8 /* Vendor unique */\n#define SCSI_CMD_READ_CDXA              0xDB /* Vendor unique */\n#define SCSI_CMD_READ_ALL_SUBCODES      0xDF /* Vendor unique */\n\n/* SCSI error codes */\n#define SCSI_S_NOT_READY                0x02\n#define SCSI_S_MEDIUM_ERROR             0x03\n#define SCSI_S_ILLEGAL_REQUEST          0x05\n#define SCSI_S_UNIT_ATTENTION           0x06\n#define SCSI_ASC_LBA_OUT_OF_RANGE       0x21\n#define SCSI_ASC_MEDIA_CHANGED          0x28\n#define SCSI_ASC_MEDIUM_NOT_PRESENT     0x3A\n\n/* USB error codes */\n#define MASS_ERR_SUCCESS                0x00\n#define MASS_ERR_PHASE_ERROR            0x02\n#define MASS_ERR_UNIT_NOT_READY         0x03\n#define MASS_ERR_UNIT_BUSY              0x04\n#define MASS_ERR_STALL                  0x05\n#define MASS_ERR_CMD_NOT_SUPPORTED      0x06\n#define MASS_ERR_INVALID_CSW            0x07\n#define MASS_ERR_NO_MEDIA               0x08\n#define MASS_ERR_BAD_LBA                0x09\n#define MASS_ERR_MEDIA_CHANGED          0x0A\n#define MASS_ERR_DEVICE_DISCONNECTED    0x11\n#define MASS_ERR_UNABLE_TO_RECOVER      0x12    // Reset recovery error\n#define MASS_ERR_INVALID_LUN            0x13\n#define MASS_ERR_WRITE_STALL            0x14\n#define MASS_ERR_READ_NAKS              0x15\n#define MASS_ERR_WRITE_NAKS             0x16\n#define MASS_ERR_WRITE_PROTECTED        0x17\n#define MASS_ERR_NOT_IMPLEMENTED        0xFD\n#define MASS_ERR_GENERAL_SCSI_ERROR     0xFE\n#define MASS_ERR_GENERAL_USB_ERROR      0xFF\n#define MASS_ERR_USER                   0xA0    // For subclasses to define their own error codes\n\n#define MASS_TRANS_FLG_CALLBACK         0x01    // Callback is involved\n#define MASS_TRANS_FLG_NO_STALL_CHECK   0x02    // STALL condition is not checked\n#define MASS_TRANS_FLG_NO_PHASE_CHECK   0x04    // PHASE_ERROR is not checked\n\n#define MASS_MAX_ENDPOINTS              3\n\nstruct Capacity {\n        uint8_t data[8];\n        //uint32_t dwBlockAddress;\n        //uint32_t dwBlockLength;\n} __attribute__((packed));\n\nstruct BASICCDB {\n        uint8_t Opcode;\n\n        unsigned unused : 5;\n        unsigned LUN : 3;\n\n        uint8_t info[12];\n} __attribute__((packed));\n\ntypedef BASICCDB BASICCDB_t;\n\nstruct CDB6 {\n        uint8_t Opcode;\n\n        unsigned LBAMSB : 5;\n        unsigned LUN : 3;\n\n        uint8_t LBAHB;\n        uint8_t LBALB;\n        uint8_t AllocationLength;\n        uint8_t Control;\n\npublic:\n\n        CDB6(uint8_t _Opcode, uint8_t _LUN, uint32_t LBA, uint8_t _AllocationLength, uint8_t _Control) :\n        Opcode(_Opcode), LBAMSB(BGRAB2(LBA) & 0x1f), LUN(_LUN), LBAHB(BGRAB1(LBA)), LBALB(BGRAB0(LBA)),\n        AllocationLength(_AllocationLength), Control(_Control) {\n        }\n\n        CDB6(uint8_t _Opcode, uint8_t _LUN, uint8_t _AllocationLength, uint8_t _Control) :\n        Opcode(_Opcode), LBAMSB(0), LUN(_LUN), LBAHB(0), LBALB(0),\n        AllocationLength(_AllocationLength), Control(_Control) {\n        }\n} __attribute__((packed));\n\ntypedef CDB6 CDB6_t;\n\nstruct CDB10 {\n        uint8_t Opcode;\n\n        unsigned Service_Action : 5;\n        unsigned LUN : 3;\n\n        uint8_t LBA_L_M_MB;\n        uint8_t LBA_L_M_LB;\n        uint8_t LBA_L_L_MB;\n        uint8_t LBA_L_L_LB;\n\n        uint8_t Misc2;\n\n        uint8_t ALC_MB;\n        uint8_t ALC_LB;\n\n        uint8_t Control;\npublic:\n\n        CDB10(uint8_t _Opcode, uint8_t _LUN) :\n        Opcode(_Opcode), Service_Action(0), LUN(_LUN),\n        LBA_L_M_MB(0), LBA_L_M_LB(0), LBA_L_L_MB(0), LBA_L_L_LB(0),\n        Misc2(0), ALC_MB(0), ALC_LB(0), Control(0) {\n        }\n\n        CDB10(uint8_t _Opcode, uint8_t _LUN, uint16_t xflen, uint32_t _LBA) :\n        Opcode(_Opcode), Service_Action(0), LUN(_LUN),\n        LBA_L_M_MB(BGRAB3(_LBA)), LBA_L_M_LB(BGRAB2(_LBA)), LBA_L_L_MB(BGRAB1(_LBA)), LBA_L_L_LB(BGRAB0(_LBA)),\n        Misc2(0), ALC_MB(BGRAB1(xflen)), ALC_LB(BGRAB0(xflen)), Control(0) {\n        }\n} __attribute__((packed));\n\ntypedef CDB10 CDB10_t;\n\nstruct CDB12 {\n        uint8_t Opcode;\n\n        unsigned Service_Action : 5;\n        unsigned Misc : 3;\n\n        uint8_t LBA_L_M_LB;\n        uint8_t LBA_L_L_MB;\n        uint8_t LBA_L_L_LB;\n\n        uint8_t ALC_M_LB;\n        uint8_t ALC_L_MB;\n        uint8_t ALC_L_LB;\n        uint8_t Control;\n} __attribute__((packed));\n\ntypedef CDB12 CDB12_t;\n\nstruct CDB_LBA32_16 {\n        uint8_t Opcode;\n\n        unsigned Service_Action : 5;\n        unsigned Misc : 3;\n\n        uint8_t LBA_L_M_MB;\n        uint8_t LBA_L_M_LB;\n        uint8_t LBA_L_L_MB;\n        uint8_t LBA_L_L_LB;\n\n        uint8_t A_M_M_MB;\n        uint8_t A_M_M_LB;\n        uint8_t A_M_L_MB;\n        uint8_t A_M_L_LB;\n\n        uint8_t ALC_M_MB;\n        uint8_t ALC_M_LB;\n        uint8_t ALC_L_MB;\n        uint8_t ALC_L_LB;\n\n        uint8_t Misc2;\n        uint8_t Control;\n} __attribute__((packed));\n\nstruct CDB_LBA64_16 {\n        uint8_t Opcode;\n        uint8_t Misc;\n\n        uint8_t LBA_M_M_MB;\n        uint8_t LBA_M_M_LB;\n        uint8_t LBA_M_L_MB;\n        uint8_t LBA_M_L_LB;\n\n        uint8_t LBA_L_M_MB;\n        uint8_t LBA_L_M_LB;\n        uint8_t LBA_L_L_MB;\n        uint8_t LBA_L_L_LB;\n\n        uint8_t ALC_M_MB;\n        uint8_t ALC_M_LB;\n        uint8_t ALC_L_MB;\n        uint8_t ALC_L_LB;\n\n        uint8_t Misc2;\n        uint8_t Control;\n} __attribute__((packed));\n\nstruct InquiryResponse {\n        uint8_t DeviceType : 5;\n        uint8_t PeripheralQualifier : 3;\n\n        unsigned Reserved : 7;\n        unsigned Removable : 1;\n\n        uint8_t Version;\n\n        unsigned ResponseDataFormat : 4;\n        unsigned HISUP : 1;\n        unsigned NormACA : 1;\n        unsigned TrmTsk : 1;\n        unsigned AERC : 1;\n\n        uint8_t AdditionalLength;\n        //uint8_t Reserved3[2];\n\n        unsigned PROTECT : 1;\n        unsigned Res : 2;\n        unsigned ThreePC : 1;\n        unsigned TPGS : 2;\n        unsigned ACC : 1;\n        unsigned SCCS : 1;\n\n        unsigned ADDR16 : 1;\n        unsigned R1 : 1;\n        unsigned R2 : 1;\n        unsigned MCHNGR : 1;\n        unsigned MULTIP : 1;\n        unsigned VS : 1;\n        unsigned ENCSERV : 1;\n        unsigned BQUE : 1;\n\n        unsigned SoftReset : 1;\n        unsigned CmdQue : 1;\n        unsigned Reserved4 : 1;\n        unsigned Linked : 1;\n        unsigned Sync : 1;\n        unsigned WideBus16Bit : 1;\n        unsigned WideBus32Bit : 1;\n        unsigned RelAddr : 1;\n\n        uint8_t VendorID[8];\n        uint8_t ProductID[16];\n        uint8_t RevisionID[4];\n} __attribute__((packed));\n\nstruct CommandBlockWrapperBase {\n        uint32_t dCBWSignature;\n        uint32_t dCBWTag;\n        uint32_t dCBWDataTransferLength;\n        uint8_t bmCBWFlags;\npublic:\n\n        CommandBlockWrapperBase() {\n        }\n\n        CommandBlockWrapperBase(uint32_t tag, uint32_t xflen, uint8_t flgs) :\n        dCBWSignature(MASS_CBW_SIGNATURE), dCBWTag(tag), dCBWDataTransferLength(xflen), bmCBWFlags(flgs) {\n        }\n} __attribute__((packed));\n\nstruct CommandBlockWrapper : public CommandBlockWrapperBase {\n\n        struct {\n                uint8_t bmCBWLUN : 4;\n                uint8_t bmReserved1 : 4;\n        };\n\n        struct {\n                uint8_t bmCBWCBLength : 4;\n                uint8_t bmReserved2 : 4;\n        };\n\n        uint8_t CBWCB[16];\n\npublic:\n        // All zeroed.\n\n        CommandBlockWrapper() :\n        CommandBlockWrapperBase(0, 0, 0), bmReserved1(0), bmReserved2(0) {\n                for(int i = 0; i < 16; i++) CBWCB[i] = 0;\n        }\n\n        // Generic Wrap, CDB zeroed.\n\n        CommandBlockWrapper(uint32_t tag, uint32_t xflen, uint8_t flgs, uint8_t lu, uint8_t cmdlen, uint8_t cmd) :\n        CommandBlockWrapperBase(tag, xflen, flgs),\n        bmCBWLUN(lu), bmReserved1(0), bmCBWCBLength(cmdlen), bmReserved2(0) {\n                for(int i = 0; i < 16; i++) CBWCB[i] = 0;\n                // Type punning can cause optimization problems and bugs.\n                // Using reinterpret_cast to a dreinterpretifferent object is the proper way to do this.\n                //(((BASICCDB_t *) CBWCB)->LUN) = cmd;\n                BASICCDB_t *x = reinterpret_cast<BASICCDB_t *>(CBWCB);\n                x->LUN = cmd;\n        }\n\n        // Wrap for CDB of 6\n\n        CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB6_t *cdb, uint8_t dir) :\n        CommandBlockWrapperBase(tag, xflen, dir),\n        bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(6), bmReserved2(0) {\n                memcpy(&CBWCB, cdb, 6);\n        }\n        // Wrap for CDB of 10\n\n        CommandBlockWrapper(uint32_t tag, uint32_t xflen, CDB10_t *cdb, uint8_t dir) :\n        CommandBlockWrapperBase(tag, xflen, dir),\n        bmCBWLUN(cdb->LUN), bmReserved1(0), bmCBWCBLength(10), bmReserved2(0) {\n                memcpy(&CBWCB, cdb, 10);\n        }\n} __attribute__((packed));\n\nstruct CommandStatusWrapper {\n        uint32_t dCSWSignature;\n        uint32_t dCSWTag;\n        uint32_t dCSWDataResidue;\n        uint8_t bCSWStatus;\n} __attribute__((packed));\n\nstruct RequestSenseResponce {\n        uint8_t bResponseCode;\n        uint8_t bSegmentNumber;\n\n        uint8_t bmSenseKey : 4;\n        uint8_t bmReserved : 1;\n        uint8_t bmILI : 1;\n        uint8_t bmEOM : 1;\n        uint8_t bmFileMark : 1;\n\n        uint8_t Information[4];\n        uint8_t bAdditionalLength;\n        uint8_t CmdSpecificInformation[4];\n        uint8_t bAdditionalSenseCode;\n        uint8_t bAdditionalSenseQualifier;\n        uint8_t bFieldReplaceableUnitCode;\n        uint8_t SenseKeySpecific[3];\n} __attribute__((packed));\n\nclass BulkOnly : public USBDeviceConfig, public UsbConfigXtracter {\nprotected:\n        static const uint8_t epDataInIndex; // DataIn endpoint index\n        static const uint8_t epDataOutIndex; // DataOUT endpoint index\n        static const uint8_t epInterruptInIndex; // InterruptIN  endpoint index\n\n        USB *pUsb;\n        uint8_t bAddress;\n        uint8_t bConfNum; // configuration number\n        uint8_t bIface; // interface value\n        uint8_t bNumEP; // total number of EP in the configuration\n        uint32_t qNextPollTime; // next poll time\n        bool bPollEnable; // poll enable flag\n\n        EpInfo epInfo[MASS_MAX_ENDPOINTS];\n\n        uint32_t dCBWTag; // Tag\n        //uint32_t dCBWDataTransferLength; // Data Transfer Length\n        uint8_t bLastUsbError; // Last USB error\n        uint8_t bMaxLUN; // Max LUN\n        uint8_t bTheLUN; // Active LUN\n        uint32_t CurrentCapacity[MASS_MAX_SUPPORTED_LUN]; // Total sectors\n        uint16_t CurrentSectorSize[MASS_MAX_SUPPORTED_LUN]; // Sector size, clipped to 16 bits\n        bool LUNOk[MASS_MAX_SUPPORTED_LUN]; // use this to check for media changes.\n        bool WriteOk[MASS_MAX_SUPPORTED_LUN];\n        void PrintEndpointDescriptor(const USB_ENDPOINT_DESCRIPTOR* ep_ptr);\n\n\n        // Additional Initialization Method for Subclasses\n\n        virtual uint8_t OnInit() {\n                return 0;\n        };\npublic:\n        BulkOnly(USB *p);\n\n        uint8_t GetLastUsbError() {\n                return bLastUsbError;\n        };\n\n        uint8_t GetbMaxLUN() {\n                return bMaxLUN; // Max LUN\n        }\n\n        uint8_t GetbTheLUN() {\n                return bTheLUN; // Active LUN\n        }\n\n        bool WriteProtected(uint8_t lun);\n        uint8_t MediaCTL(uint8_t lun, uint8_t ctl);\n        uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, uint8_t *buf);\n        uint8_t Read(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, USBReadParser *prs);\n        uint8_t Write(uint8_t lun, uint32_t addr, uint16_t bsize, uint8_t blocks, const uint8_t *buf);\n        uint8_t LockMedia(uint8_t lun, uint8_t lock);\n\n        bool LUNIsGood(uint8_t lun);\n        uint32_t GetCapacity(uint8_t lun);\n        uint16_t GetSectorSize(uint8_t lun);\n\n        // USBDeviceConfig implementation\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        uint8_t ConfigureDevice(uint8_t parent, uint8_t port, bool lowspeed);\n\n        uint8_t Release();\n        uint8_t Poll();\n\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        // UsbConfigXtracter implementation\n        void EndpointXtract(uint8_t conf, uint8_t iface, uint8_t alt, uint8_t proto, const USB_ENDPOINT_DESCRIPTOR *ep);\n\n        virtual bool DEVCLASSOK(uint8_t klass) {\n                return (klass == USB_CLASS_MASS_STORAGE);\n        }\n\n        uint8_t SCSITransaction6(CDB6_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);\n        uint8_t SCSITransaction10(CDB10_t *cdb, uint16_t buf_size, void *buf, uint8_t dir);\n\nprivate:\n        uint8_t Inquiry(uint8_t lun, uint16_t size, uint8_t *buf);\n        uint8_t TestUnitReady(uint8_t lun);\n        uint8_t RequestSense(uint8_t lun, uint16_t size, uint8_t *buf);\n        uint8_t ModeSense6(uint8_t lun, uint8_t pc, uint8_t page, uint8_t subpage, uint8_t len, uint8_t *buf);\n        uint8_t GetMaxLUN(uint8_t *max_lun);\n        uint8_t SetCurLUN(uint8_t lun);\n        void Reset();\n        uint8_t ResetRecovery();\n        uint8_t ReadCapacity10(uint8_t lun, uint8_t *buf);\n        void ClearAllEP();\n        void CheckMedia();\n        bool CheckLUN(uint8_t lun);\n        uint8_t Page3F(uint8_t lun);\n        bool IsValidCBW(uint8_t size, uint8_t *pcbw);\n        bool IsMeaningfulCBW(uint8_t size, uint8_t *pcbw);\n\n        bool IsValidCSW(CommandStatusWrapper *pcsw, CommandBlockWrapperBase *pcbw);\n\n        uint8_t ClearEpHalt(uint8_t index);\n#if MS_WANT_PARSER\n        uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf, uint8_t flags);\n#endif\n        uint8_t Transaction(CommandBlockWrapper *cbw, uint16_t bsize, void *buf);\n        uint8_t HandleUsbError(uint8_t error, uint8_t index);\n        uint8_t HandleSCSIError(uint8_t status);\n\n};\n\n#endif // __MASSTORAGE_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/max3421e.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(_usb_h_) || defined(_max3421e_h_)\n#error \"Never include max3421e.h directly; include Usb.h instead\"\n#else\n\n#define _max3421e_h_\n\n/* MAX3421E register/bit names and bitmasks */\n\n/* Arduino pin definitions */\n/* pin numbers to port numbers */\n\n#define SE0     0\n#define SE1     1\n#define FSHOST  2\n#define LSHOST  3\n\n/* MAX3421E command byte format: rrrrr0wa where 'r' is register number  */\n//\n// MAX3421E Registers in HOST mode.\n//\n#define rRCVFIFO    0x08    //1<<3\n#define rSNDFIFO    0x10    //2<<3\n#define rSUDFIFO    0x20    //4<<3\n#define rRCVBC      0x30    //6<<3\n#define rSNDBC      0x38    //7<<3\n\n#define rUSBIRQ     0x68    //13<<3\n/* USBIRQ Bits  */\n#define bmVBUSIRQ   0x40    //b6\n#define bmNOVBUSIRQ 0x20    //b5\n#define bmOSCOKIRQ  0x01    //b0\n\n#define rUSBIEN     0x70    //14<<3\n/* USBIEN Bits  */\n#define bmVBUSIE    0x40    //b6\n#define bmNOVBUSIE  0x20    //b5\n#define bmOSCOKIE   0x01    //b0\n\n#define rUSBCTL     0x78    //15<<3\n/* USBCTL Bits  */\n#define bmCHIPRES   0x20    //b5\n#define bmPWRDOWN   0x10    //b4\n\n#define rCPUCTL     0x80    //16<<3\n/* CPUCTL Bits  */\n#define bmPUSLEWID1 0x80    //b7\n#define bmPULSEWID0 0x40    //b6\n#define bmIE        0x01    //b0\n\n#define rPINCTL     0x88    //17<<3\n/* PINCTL Bits  */\n#define bmFDUPSPI   0x10    //b4\n#define bmINTLEVEL  0x08    //b3\n#define bmPOSINT    0x04    //b2\n#define bmGPXB      0x02    //b1\n#define bmGPXA      0x01    //b0\n// GPX pin selections\n#define GPX_OPERATE 0x00\n#define GPX_VBDET   0x01\n#define GPX_BUSACT  0x02\n#define GPX_SOF     0x03\n\n#define rREVISION   0x90    //18<<3\n\n#define rIOPINS1    0xa0    //20<<3\n\n/* IOPINS1 Bits */\n#define bmGPOUT0    0x01\n#define bmGPOUT1    0x02\n#define bmGPOUT2    0x04\n#define bmGPOUT3    0x08\n#define bmGPIN0     0x10\n#define bmGPIN1     0x20\n#define bmGPIN2     0x40\n#define bmGPIN3     0x80\n\n#define rIOPINS2    0xa8    //21<<3\n/* IOPINS2 Bits */\n#define bmGPOUT4    0x01\n#define bmGPOUT5    0x02\n#define bmGPOUT6    0x04\n#define bmGPOUT7    0x08\n#define bmGPIN4     0x10\n#define bmGPIN5     0x20\n#define bmGPIN6     0x40\n#define bmGPIN7     0x80\n\n#define rGPINIRQ    0xb0    //22<<3\n/* GPINIRQ Bits */\n#define bmGPINIRQ0 0x01\n#define bmGPINIRQ1 0x02\n#define bmGPINIRQ2 0x04\n#define bmGPINIRQ3 0x08\n#define bmGPINIRQ4 0x10\n#define bmGPINIRQ5 0x20\n#define bmGPINIRQ6 0x40\n#define bmGPINIRQ7 0x80\n\n#define rGPINIEN    0xb8    //23<<3\n/* GPINIEN Bits */\n#define bmGPINIEN0 0x01\n#define bmGPINIEN1 0x02\n#define bmGPINIEN2 0x04\n#define bmGPINIEN3 0x08\n#define bmGPINIEN4 0x10\n#define bmGPINIEN5 0x20\n#define bmGPINIEN6 0x40\n#define bmGPINIEN7 0x80\n\n#define rGPINPOL    0xc0    //24<<3\n/* GPINPOL Bits */\n#define bmGPINPOL0 0x01\n#define bmGPINPOL1 0x02\n#define bmGPINPOL2 0x04\n#define bmGPINPOL3 0x08\n#define bmGPINPOL4 0x10\n#define bmGPINPOL5 0x20\n#define bmGPINPOL6 0x40\n#define bmGPINPOL7 0x80\n\n#define rHIRQ       0xc8    //25<<3\n/* HIRQ Bits */\n#define bmBUSEVENTIRQ   0x01   // indicates BUS Reset Done or BUS Resume\n#define bmRWUIRQ        0x02\n#define bmRCVDAVIRQ     0x04\n#define bmSNDBAVIRQ     0x08\n#define bmSUSDNIRQ      0x10\n#define bmCONDETIRQ     0x20\n#define bmFRAMEIRQ      0x40\n#define bmHXFRDNIRQ     0x80\n\n#define rHIEN           0xd0    //26<<3\n\n/* HIEN Bits */\n#define bmBUSEVENTIE    0x01\n#define bmRWUIE         0x02\n#define bmRCVDAVIE      0x04\n#define bmSNDBAVIE      0x08\n#define bmSUSDNIE       0x10\n#define bmCONDETIE      0x20\n#define bmFRAMEIE       0x40\n#define bmHXFRDNIE      0x80\n\n#define rMODE           0xd8    //27<<3\n\n/* MODE Bits */\n#define bmHOST          0x01\n#define bmLOWSPEED      0x02\n#define bmHUBPRE        0x04\n#define bmSOFKAENAB     0x08\n#define bmSEPIRQ        0x10\n#define bmDELAYISO      0x20\n#define bmDMPULLDN      0x40\n#define bmDPPULLDN      0x80\n\n#define rPERADDR    0xe0    //28<<3\n\n#define rHCTL       0xe8    //29<<3\n/* HCTL Bits */\n#define bmBUSRST        0x01\n#define bmFRMRST        0x02\n#define bmSAMPLEBUS     0x04\n#define bmSIGRSM        0x08\n#define bmRCVTOG0       0x10\n#define bmRCVTOG1       0x20\n#define bmSNDTOG0       0x40\n#define bmSNDTOG1       0x80\n\n#define rHXFR       0xf0    //30<<3\n/* Host transfer token values for writing the HXFR register (R30)   */\n/* OR this bit field with the endpoint number in bits 3:0               */\n#define tokSETUP  0x10  // HS=0, ISO=0, OUTNIN=0, SETUP=1\n#define tokIN     0x00  // HS=0, ISO=0, OUTNIN=0, SETUP=0\n#define tokOUT    0x20  // HS=0, ISO=0, OUTNIN=1, SETUP=0\n#define tokINHS   0x80  // HS=1, ISO=0, OUTNIN=0, SETUP=0\n#define tokOUTHS  0xA0  // HS=1, ISO=0, OUTNIN=1, SETUP=0\n#define tokISOIN  0x40  // HS=0, ISO=1, OUTNIN=0, SETUP=0\n#define tokISOOUT 0x60  // HS=0, ISO=1, OUTNIN=1, SETUP=0\n\n#define rHRSL       0xf8    //31<<3\n\n/* HRSL Bits */\n#define bmRCVTOGRD  0x10\n#define bmSNDTOGRD  0x20\n#define bmKSTATUS   0x40\n#define bmJSTATUS   0x80\n#define bmSE0       0x00    //SE0 - disconnect state\n#define bmSE1       0xc0    //SE1 - illegal state\n\n/* Host error result codes, the 4 LSB's in the HRSL register */\n#define hrSUCCESS   0x00\n#define hrBUSY      0x01\n#define hrBADREQ    0x02\n#define hrUNDEF     0x03\n#define hrNAK       0x04\n#define hrSTALL     0x05\n#define hrTOGERR    0x06\n#define hrWRONGPID  0x07\n#define hrBADBC     0x08\n#define hrPIDERR    0x09\n#define hrPKTERR    0x0A\n#define hrCRCERR    0x0B\n#define hrKERR      0x0C\n#define hrJERR      0x0D\n#define hrTIMEOUT   0x0E\n#define hrBABBLE    0x0F\n\n#define MODE_FS_HOST    (bmDPPULLDN|bmDMPULLDN|bmHOST|bmSOFKAENAB)\n#define MODE_LS_HOST    (bmDPPULLDN|bmDMPULLDN|bmHOST|bmLOWSPEED|bmSOFKAENAB)\n\n\n#endif //_max3421e_h_\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/max_LCD.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#include \"max_LCD.h\"\n#include <string.h>\n\n// pin definition and set/clear\n\n#define RS  0x04    // RS pin\n#define E   0x08    // E pin\n\n#define SET_RS  lcdPins |= RS\n#define CLR_RS  lcdPins &= ~RS\n#define SET_E   lcdPins |= E\n#define CLR_E   lcdPins &= ~E\n\n#define SENDlcdPins()   pUsb->gpioWr( lcdPins )\n\n#define LCD_sendcmd(a)  {   CLR_RS;             \\\n                            sendbyte(a);    \\\n                        }\n\n#define LCD_sendchar(a) {   SET_RS;             \\\n                            sendbyte(a);    \\\n                        }\n\nstatic byte lcdPins; //copy of LCD pins\n\nMax_LCD::Max_LCD(USB *pusb) : pUsb(pusb) {\n        lcdPins = 0;\n}\n\nvoid Max_LCD::init() {\n        _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;\n\n        //   MAX3421E::gpioWr(0x55);\n\n        begin(16, 1);\n}\n\nvoid Max_LCD::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {\n        if(lines > 1) {\n                _displayfunction |= LCD_2LINE;\n        }\n        _numlines = lines;\n        _currline = 0;\n\n        // for some 1 line displays you can select a 10 pixel high font\n        if((dotsize != 0) && (lines == 1)) {\n                _displayfunction |= LCD_5x10DOTS;\n        }\n\n        // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!\n        // according to datasheet, we need at least 40ms after power rises above 2.7V\n        // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50\n        delayMicroseconds(50000);\n        lcdPins = 0x30;\n        SET_E;\n        SENDlcdPins();\n        CLR_E;\n        SENDlcdPins();\n        delayMicroseconds(10000); // wait min 4.1ms\n        //second try\n        SET_E;\n        SENDlcdPins();\n        CLR_E;\n        SENDlcdPins();\n        delayMicroseconds(10000); // wait min 4.1ms\n        // third go!\n        SET_E;\n        SENDlcdPins();\n        CLR_E;\n        SENDlcdPins();\n        delayMicroseconds(10000);\n        // finally, set to 4-bit interface\n        lcdPins = 0x20;\n        //SET_RS;\n        SET_E;\n        SENDlcdPins();\n        //CLR_RS;\n        CLR_E;\n        SENDlcdPins();\n        delayMicroseconds(10000);\n        // finally, set # lines, font size, etc.\n        command(LCD_FUNCTIONSET | _displayfunction);\n\n        // turn the display on with no cursor or blinking default\n        _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;\n        display();\n\n        // clear it off\n        clear();\n\n        // Initialize to default text direction (for romance languages)\n        _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;\n        // set the entry mode\n        command(LCD_ENTRYMODESET | _displaymode);\n}\n\n/********** high level commands, for the user! */\nvoid Max_LCD::clear() {\n        command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero\n        delayMicroseconds(2000); // this command takes a long time!\n}\n\nvoid Max_LCD::home() {\n        command(LCD_RETURNHOME); // set cursor position to zero\n        delayMicroseconds(2000); // this command takes a long time!\n}\n\nvoid Max_LCD::setCursor(uint8_t col, uint8_t row) {\n        int row_offsets[] = {0x00, 0x40, 0x14, 0x54};\n        if(row > _numlines) {\n                row = _numlines - 1; // we count rows starting w/0\n        }\n\n        command(LCD_SETDDRAMADDR | (col + row_offsets[row]));\n}\n\n// Turn the display on/off (quickly)\n\nvoid Max_LCD::noDisplay() {\n        _displaycontrol &= ~LCD_DISPLAYON;\n        command(LCD_DISPLAYCONTROL | _displaycontrol);\n}\n\nvoid Max_LCD::display() {\n        _displaycontrol |= LCD_DISPLAYON;\n        command(LCD_DISPLAYCONTROL | _displaycontrol);\n}\n\n// Turns the underline cursor on/off\n\nvoid Max_LCD::noCursor() {\n        _displaycontrol &= ~LCD_CURSORON;\n        command(LCD_DISPLAYCONTROL | _displaycontrol);\n}\n\nvoid Max_LCD::cursor() {\n        _displaycontrol |= LCD_CURSORON;\n        command(LCD_DISPLAYCONTROL | _displaycontrol);\n}\n\n\n// Turn on and off the blinking cursor\n\nvoid Max_LCD::noBlink() {\n        _displaycontrol &= ~LCD_BLINKON;\n        command(LCD_DISPLAYCONTROL | _displaycontrol);\n}\n\nvoid Max_LCD::blink() {\n        _displaycontrol |= LCD_BLINKON;\n        command(LCD_DISPLAYCONTROL | _displaycontrol);\n}\n\n// These commands scroll the display without changing the RAM\n\nvoid Max_LCD::scrollDisplayLeft(void) {\n        command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);\n}\n\nvoid Max_LCD::scrollDisplayRight(void) {\n        command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);\n}\n\n// This is for text that flows Left to Right\n\nvoid Max_LCD::leftToRight(void) {\n        _displaymode |= LCD_ENTRYLEFT;\n        command(LCD_ENTRYMODESET | _displaymode);\n}\n\n// This is for text that flows Right to Left\n\nvoid Max_LCD::rightToLeft(void) {\n        _displaymode &= ~LCD_ENTRYLEFT;\n        command(LCD_ENTRYMODESET | _displaymode);\n}\n\n// This will 'right justify' text from the cursor\n\nvoid Max_LCD::autoscroll(void) {\n        _displaymode |= LCD_ENTRYSHIFTINCREMENT;\n        command(LCD_ENTRYMODESET | _displaymode);\n}\n\n// This will 'left justify' text from the cursor\n\nvoid Max_LCD::noAutoscroll(void) {\n        _displaymode &= ~LCD_ENTRYSHIFTINCREMENT;\n        command(LCD_ENTRYMODESET | _displaymode);\n}\n\n// Allows us to fill the first 8 CGRAM locations\n// with custom characters\n\nvoid Max_LCD::createChar(uint8_t location, uint8_t charmap[]) {\n        location &= 0x7; // we only have 8 locations 0-7\n        command(LCD_SETCGRAMADDR | (location << 3));\n        for(int i = 0; i < 8; i++) {\n                write(charmap[i]);\n        }\n}\n\n/*********** mid level commands, for sending data/cmds */\n\ninline void Max_LCD::command(uint8_t value) {\n        LCD_sendcmd(value);\n        delayMicroseconds(100);\n}\n\n#if defined(ARDUINO) && ARDUINO >=100\n\ninline size_t Max_LCD::write(uint8_t value) {\n        LCD_sendchar(value);\n        return 1; // Assume success\n}\n#else\n\ninline void Max_LCD::write(uint8_t value) {\n        LCD_sendchar(value);\n}\n#endif\n\nvoid Max_LCD::sendbyte(uint8_t val) {\n        lcdPins &= 0x0f; //prepare place for the upper nibble\n        lcdPins |= (val & 0xf0); //copy upper nibble to LCD variable\n        SET_E; //send\n        SENDlcdPins();\n        delayMicroseconds(2);\n        CLR_E;\n        delayMicroseconds(2);\n        SENDlcdPins();\n        lcdPins &= 0x0f; //prepare place for the lower nibble\n        lcdPins |= (val << 4) & 0xf0; //copy lower nibble to LCD variable\n        SET_E; //send\n        SENDlcdPins();\n        CLR_E;\n        SENDlcdPins();\n        delayMicroseconds(100);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/max_LCD.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n//HD44780 compatible LCD display via MAX3421E GPOUT support header\n//pinout: D[4-7] -> GPOUT[4-7], RS-> GPOUT[2], E ->GPOUT[3]\n//\n\n#ifndef _Max_LCD_h_\n#define _Max_LCD_h_\n\n#include \"Usb.h\"\n#include \"Print.h\"\n\n// commands\n#define LCD_CLEARDISPLAY        0x01\n#define LCD_RETURNHOME          0x02\n#define LCD_ENTRYMODESET        0x04\n#define LCD_DISPLAYCONTROL      0x08\n#define LCD_CURSORSHIFT         0x10\n#define LCD_FUNCTIONSET         0x20\n#define LCD_SETCGRAMADDR        0x40\n#define LCD_SETDDRAMADDR        0x80\n\n// flags for display entry mode\n#define LCD_ENTRYRIGHT          0x00\n#define LCD_ENTRYLEFT           0x02\n#define LCD_ENTRYSHIFTINCREMENT 0x01\n#define LCD_ENTRYSHIFTDECREMENT 0x00\n\n// flags for display on/off control\n#define LCD_DISPLAYON           0x04\n#define LCD_DISPLAYOFF          0x00\n#define LCD_CURSORON            0x02\n#define LCD_CURSOROFF           0x00\n#define LCD_BLINKON             0x01\n#define LCD_BLINKOFF            0x00\n\n// flags for display/cursor shift\n#define LCD_DISPLAYMOVE         0x08\n#define LCD_CURSORMOVE          0x00\n#define LCD_MOVERIGHT           0x04\n#define LCD_MOVELEFT            0x00\n\n// flags for function set\n#define LCD_8BITMODE            0x10\n#define LCD_4BITMODE            0x00\n#define LCD_2LINE               0x08\n#define LCD_1LINE               0x00\n#define LCD_5x10DOTS            0x04\n#define LCD_5x8DOTS             0x00\n\nclass Max_LCD : public Print {\n        USB *pUsb;\n\npublic:\n        Max_LCD(USB *pusb);\n        void init();\n        void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);\n        void clear();\n        void home();\n        void noDisplay();\n        void display();\n        void noBlink();\n        void blink();\n        void noCursor();\n        void cursor();\n        void scrollDisplayLeft();\n        void scrollDisplayRight();\n        void leftToRight();\n        void rightToLeft();\n        void autoscroll();\n        void noAutoscroll();\n        void createChar(uint8_t, uint8_t[]);\n        void setCursor(uint8_t, uint8_t);\n        void command(uint8_t);\n\n#if defined(ARDUINO) && ARDUINO >=100\n        size_t write(uint8_t);\n        using Print::write;\n#else\n        void write(uint8_t);\n#endif\n\nprivate:\n        void sendbyte(uint8_t val);\n        uint8_t _displayfunction; //tokill\n        uint8_t _displaycontrol;\n        uint8_t _displaymode;\n        uint8_t _initialized;\n        uint8_t _numlines, _currline;\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/message.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#include \"Usb.h\"\n// 0x80 is the default (i.e. trace) to turn off set this global to something lower.\n// this allows for 126 other debugging levels.\n// TO-DO: Allow assignment to a different serial port by software\nint UsbDEBUGlvl = 0x80;\n\nvoid E_Notifyc(char c, int lvl) {\n        if(UsbDEBUGlvl < lvl) return;\n#if defined(ARDUINO) && ARDUINO >=100\n        USB_HOST_SERIAL.print(c);\n#else\n        USB_HOST_SERIAL.print(c, BYTE);\n#endif\n        //USB_HOST_SERIAL.flush();\n}\n\nvoid E_Notify(char const * msg, int lvl) {\n        if(UsbDEBUGlvl < lvl) return;\n        if(!msg) return;\n        char c;\n\n        while((c = pgm_read_byte(msg++))) E_Notifyc(c, lvl);\n}\n\nvoid E_NotifyStr(char const * msg, int lvl) {\n        if(UsbDEBUGlvl < lvl) return;\n        if(!msg) return;\n        char c;\n\n        while((c = *msg++)) E_Notifyc(c, lvl);\n}\n\nvoid E_Notify(uint8_t b, int lvl) {\n        if(UsbDEBUGlvl < lvl) return;\n#if defined(ARDUINO) && ARDUINO >=100\n        USB_HOST_SERIAL.print(b);\n#else\n        USB_HOST_SERIAL.print(b, DEC);\n#endif\n        //USB_HOST_SERIAL.flush();\n}\n\nvoid E_Notify(double d, int lvl) {\n        if(UsbDEBUGlvl < lvl) return;\n        USB_HOST_SERIAL.print(d);\n        //USB_HOST_SERIAL.flush();\n}\n\n#ifdef DEBUG_USB_HOST\n\nvoid NotifyFailGetDevDescr(void) {\n        Notify(PSTR(\"\\r\\ngetDevDescr \"), 0x80);\n}\n\nvoid NotifyFailSetDevTblEntry(void) {\n        Notify(PSTR(\"\\r\\nsetDevTblEn \"), 0x80);\n}\n\nvoid NotifyFailGetConfDescr(void) {\n        Notify(PSTR(\"\\r\\ngetConf \"), 0x80);\n}\n\nvoid NotifyFailSetConfDescr(void) {\n        Notify(PSTR(\"\\r\\nsetConf \"), 0x80);\n}\n\nvoid NotifyFailGetDevDescr(uint8_t reason) {\n        NotifyFailGetDevDescr();\n        NotifyFail(reason);\n}\n\nvoid NotifyFailSetDevTblEntry(uint8_t reason) {\n        NotifyFailSetDevTblEntry();\n        NotifyFail(reason);\n\n}\n\nvoid NotifyFailGetConfDescr(uint8_t reason) {\n        NotifyFailGetConfDescr();\n        NotifyFail(reason);\n}\n\nvoid NotifyFailSetConfDescr(uint8_t reason) {\n        NotifyFailSetConfDescr();\n        NotifyFail(reason);\n}\n\nvoid NotifyFailUnknownDevice(uint16_t VID, uint16_t PID) {\n        Notify(PSTR(\"\\r\\nUnknown Device Connected - VID: \"), 0x80);\n        D_PrintHex<uint16_t > (VID, 0x80);\n        Notify(PSTR(\" PID: \"), 0x80);\n        D_PrintHex<uint16_t > (PID, 0x80);\n}\n\nvoid NotifyFail(uint8_t rcode) {\n        D_PrintHex<uint8_t > (rcode, 0x80);\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n}\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/message.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(_usb_h_) || defined(__MESSAGE_H__)\n#error \"Never include message.h directly; include Usb.h instead\"\n#else\n#define __MESSAGE_H__\n\nextern int UsbDEBUGlvl;\n\nvoid E_Notify(char const * msg, int lvl);\nvoid E_Notify(uint8_t b, int lvl);\nvoid E_NotifyStr(char const * msg, int lvl);\nvoid E_Notifyc(char c, int lvl);\n\n#ifdef DEBUG_USB_HOST\n#define Notify E_Notify\n#define NotifyStr E_NotifyStr\n#define Notifyc E_Notifyc\nvoid NotifyFailGetDevDescr(uint8_t reason);\nvoid NotifyFailSetDevTblEntry(uint8_t reason);\nvoid NotifyFailGetConfDescr(uint8_t reason);\nvoid NotifyFailSetConfDescr(uint8_t reason);\nvoid NotifyFailGetDevDescr(void);\nvoid NotifyFailSetDevTblEntry(void);\nvoid NotifyFailGetConfDescr(void);\nvoid NotifyFailSetConfDescr(void);\nvoid NotifyFailUnknownDevice(uint16_t VID, uint16_t PID);\nvoid NotifyFail(uint8_t rcode);\n#else\n#define Notify(...) ((void)0)\n#define NotifyStr(...) ((void)0)\n#define Notifyc(...) ((void)0)\n#define NotifyFailGetDevDescr(...) ((void)0)\n#define NotifyFailSetDevTblEntry(...) ((void)0)\n#define NotifyFailGetConfDescr(...) ((void)0)\n#define NotifyFailGetDevDescr(...) ((void)0)\n#define NotifyFailSetDevTblEntry(...) ((void)0)\n#define NotifyFailGetConfDescr(...) ((void)0)\n#define NotifyFailSetConfDescr(...) ((void)0)\n#define NotifyFailUnknownDevice(...) ((void)0)\n#define NotifyFail(...) ((void)0)\n#endif\n\ntemplate <class ERROR_TYPE>\nvoid ErrorMessage(uint8_t level, char const * msg, ERROR_TYPE rcode = 0) {\n#ifdef DEBUG_USB_HOST\n        Notify(msg, level);\n        Notify(PSTR(\": \"), level);\n        D_PrintHex<ERROR_TYPE > (rcode, level);\n        Notify(PSTR(\"\\r\\n\"), level);\n#endif\n}\n\ntemplate <class ERROR_TYPE>\nvoid ErrorMessage(char const * msg, ERROR_TYPE rcode = 0) {\n#ifdef DEBUG_USB_HOST\n        Notify(msg, 0x80);\n        Notify(PSTR(\": \"), 0x80);\n        D_PrintHex<ERROR_TYPE > (rcode, 0x80);\n        Notify(PSTR(\"\\r\\n\"), 0x80);\n#endif\n}\n\n#endif // __MESSAGE_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/parsetools.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#include \"Usb.h\"\n\nbool MultiByteValueParser::Parse(uint8_t **pp, uint16_t *pcntdn) {\n        if(!pBuf) {\n                Notify(PSTR(\"Buffer pointer is NULL!\\r\\n\"), 0x80);\n                return false;\n        }\n        for(; countDown && (*pcntdn); countDown--, (*pcntdn)--, (*pp)++)\n                pBuf[valueSize - countDown] = (**pp);\n\n        if(countDown)\n                return false;\n\n        countDown = valueSize;\n        return true;\n}\n\nbool PTPListParser::Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, const void *me) {\n        switch(nStage) {\n                case 0:\n                        pBuf->valueSize = lenSize;\n                        theParser.Initialize(pBuf);\n                        nStage = 1;\n\n                case 1:\n                        if(!theParser.Parse(pp, pcntdn))\n                                return false;\n\n                        arLen = 0;\n                        arLen = (pBuf->valueSize >= 4) ? *((uint32_t*)pBuf->pValue) : (uint32_t)(*((uint16_t*)pBuf->pValue));\n                        arLenCntdn = arLen;\n                        nStage = 2;\n\n                case 2:\n                        pBuf->valueSize = valSize;\n                        theParser.Initialize(pBuf);\n                        nStage = 3;\n\n                case 3:\n                        for(; arLenCntdn; arLenCntdn--) {\n                                if(!theParser.Parse(pp, pcntdn))\n                                        return false;\n\n                                if(pf)\n                                        pf(pBuf, (arLen - arLenCntdn), me);\n                        }\n\n                        nStage = 0;\n        }\n        return true;\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/parsetools.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#if !defined(_usb_h_) || defined(__PARSETOOLS_H__)\n#error \"Never include parsetools.h directly; include Usb.h instead\"\n#else\n#define __PARSETOOLS_H__\n\nstruct MultiValueBuffer {\n        uint8_t valueSize;\n        void *pValue;\n} __attribute__((packed));\n\nclass MultiByteValueParser {\n        uint8_t * pBuf;\n        uint8_t countDown;\n        uint8_t valueSize;\n\npublic:\n\n        MultiByteValueParser() : pBuf(NULL), countDown(0), valueSize(0) {\n        };\n\n        const uint8_t* GetBuffer() {\n                return pBuf;\n        };\n\n        void Initialize(MultiValueBuffer * const pbuf) {\n                pBuf = (uint8_t*)pbuf->pValue;\n                countDown = valueSize = pbuf->valueSize;\n        };\n\n        bool Parse(uint8_t **pp, uint16_t *pcntdn);\n};\n\nclass ByteSkipper {\n        uint8_t *pBuf;\n        uint8_t nStage;\n        uint16_t countDown;\n\npublic:\n\n        ByteSkipper() : pBuf(NULL), nStage(0), countDown(0) {\n        };\n\n        void Initialize(MultiValueBuffer *pbuf) {\n                pBuf = (uint8_t*)pbuf->pValue;\n                countDown = 0;\n        };\n\n        bool Skip(uint8_t **pp, uint16_t *pcntdn, uint16_t bytes_to_skip) {\n                switch(nStage) {\n                        case 0:\n                                countDown = bytes_to_skip;\n                                nStage++;\n                        case 1:\n                                for(; countDown && (*pcntdn); countDown--, (*pp)++, (*pcntdn)--);\n\n                                if(!countDown)\n                                        nStage = 0;\n                };\n                return (!countDown);\n        };\n};\n\n// Pointer to a callback function triggered for each element of PTP array when used with PTPArrayParser\ntypedef void (*PTP_ARRAY_EL_FUNC)(const MultiValueBuffer * const p, uint32_t count, const void *me);\n\nclass PTPListParser {\npublic:\n\n        enum ParseMode {\n                modeArray, modeRange/*, modeEnum*/\n        };\n\nprivate:\n        uint8_t nStage;\n        uint8_t enStage;\n\n        uint32_t arLen;\n        uint32_t arLenCntdn;\n\n        uint8_t lenSize; // size of the array length field in bytes\n        uint8_t valSize; // size of the array element in bytes\n\n        MultiValueBuffer *pBuf;\n\n        // The only parser for both size and array element parsing\n        MultiByteValueParser theParser;\n\n        uint8_t /*ParseMode*/ prsMode;\n\npublic:\n\n        PTPListParser() :\n        nStage(0),\n        enStage(0),\n        arLen(0),\n        arLenCntdn(0),\n        lenSize(0),\n        valSize(0),\n        pBuf(NULL),\n        prsMode(modeArray) {\n        };\n\n        void Initialize(const uint8_t len_size, const uint8_t val_size, MultiValueBuffer * const p, const uint8_t mode = modeArray) {\n                pBuf = p;\n                lenSize = len_size;\n                valSize = val_size;\n                prsMode = mode;\n\n                if(prsMode == modeRange) {\n                        arLenCntdn = arLen = 3;\n                        nStage = 2;\n                } else {\n                        arLenCntdn = arLen = 0;\n                        nStage = 0;\n                }\n                enStage = 0;\n                theParser.Initialize(p);\n        };\n\n        bool Parse(uint8_t **pp, uint16_t *pcntdn, PTP_ARRAY_EL_FUNC pf, const void *me = NULL);\n};\n\n#endif // __PARSETOOLS_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/printhex.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#if !defined(_usb_h_) || defined(__PRINTHEX_H__)\n#error \"Never include printhex.h directly; include Usb.h instead\"\n#else\n#define __PRINTHEX_H__\n\nvoid E_Notifyc(char c, int lvl);\n\ntemplate <class T>\nvoid PrintHex(T val, int lvl) {\n        int num_nibbles = sizeof (T) * 2;\n\n        do {\n                char v = 48 + (((val >> (num_nibbles - 1) * 4)) & 0x0f);\n                if(v > 57) v += 7;\n                E_Notifyc(v, lvl);\n        } while(--num_nibbles);\n}\n\ntemplate <class T>\nvoid PrintBin(T val, int lvl) {\n        for(T mask = (((T)1) << ((sizeof (T) << 3) - 1)); mask; mask >>= 1)\n                if(val & mask)\n                        E_Notifyc('1', lvl);\n                else\n                        E_Notifyc('0', lvl);\n}\n\ntemplate <class T>\nvoid SerialPrintHex(T val) {\n        int num_nibbles = sizeof (T) * 2;\n\n        do {\n                char v = 48 + (((val >> (num_nibbles - 1) * 4)) & 0x0f);\n                if(v > 57) v += 7;\n                USB_HOST_SERIAL.print(v);\n        } while(--num_nibbles);\n}\n\ntemplate <class T>\nvoid PrintHex2(Print *prn, T val) {\n        T mask = (((T)1) << (((sizeof (T) << 1) - 1) << 2));\n\n        while(mask > 1) {\n                if(val < mask)\n                        prn->print(\"0\");\n\n                mask >>= 4;\n        }\n        prn->print((T)val, HEX);\n}\n\ntemplate <class T> void D_PrintHex(T val, int lvl) {\n#ifdef DEBUG_USB_HOST\n        PrintHex<T > (val, lvl);\n#endif\n}\n\ntemplate <class T>\nvoid D_PrintBin(T val, int lvl) {\n#ifdef DEBUG_USB_HOST\n        PrintBin<T > (val, lvl);\n#endif\n}\n\n\n\n#endif // __PRINTHEX_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/settings.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#ifndef USB_HOST_SHIELD_SETTINGS_H\n#define USB_HOST_SHIELD_SETTINGS_H\n#include \"macros.h\"\n\n////////////////////////////////////////////////////////////////////////////////\n// DEBUGGING\n////////////////////////////////////////////////////////////////////////////////\n\n/* Set this to 1 to activate serial debugging */\n#define ENABLE_UHS_DEBUGGING 0\n\n/* This can be used to select which serial port to use for debugging if\n * multiple serial ports are available.\n * For example Serial3.\n */\n#ifndef USB_HOST_SERIAL\n#define USB_HOST_SERIAL Serial\n#endif\n\n////////////////////////////////////////////////////////////////////////////////\n// Manual board activation\n////////////////////////////////////////////////////////////////////////////////\n\n/* Set this to 1 if you are using an Arduino Mega ADK board with MAX3421e built-in */\n#define USE_UHS_MEGA_ADK 0 // If you are using Arduino 1.5.5 or newer there is no need to do this manually\n\n/* Set this to 1 if you are using a Black Widdow */\n#define USE_UHS_BLACK_WIDDOW 0\n\n/* Set this to a one to use the xmem2 lock. This is needed for multitasking and threading */\n#define USE_XMEM_SPI_LOCK 0\n\n////////////////////////////////////////////////////////////////////////////////\n// Wii IR camera\n////////////////////////////////////////////////////////////////////////////////\n\n/* Set this to 1 to activate code for the Wii IR camera */\n#define ENABLE_WII_IR_CAMERA 0\n\n////////////////////////////////////////////////////////////////////////////////\n// MASS STORAGE\n////////////////////////////////////////////////////////////////////////////////\n// <<<<<<<<<<<<<<<< IMPORTANT >>>>>>>>>>>>>>>\n// Set this to 1 to support single LUN devices, and save RAM. -- I.E. thumb drives.\n// Each LUN needs ~13 bytes to be able to track the state of each unit.\n#ifndef MASS_MAX_SUPPORTED_LUN\n#define MASS_MAX_SUPPORTED_LUN 8\n#endif\n\n////////////////////////////////////////////////////////////////////////////////\n// Set to 1 to use the faster spi4teensy3 driver.\n////////////////////////////////////////////////////////////////////////////////\n#ifndef USE_SPI4TEENSY3\n#define USE_SPI4TEENSY3 1\n#endif\n\n////////////////////////////////////////////////////////////////////////////////\n// AUTOMATIC Settings\n////////////////////////////////////////////////////////////////////////////////\n\n// No user serviceable parts below this line.\n// DO NOT change anything below here unless you are a developer!\n\n#include \"version_helper.h\"\n\n#if defined(__GNUC__) && defined(__AVR__)\n#ifndef GCC_VERSION\n#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)\n#endif\n#if GCC_VERSION < 40602 // Test for GCC < 4.6.2\n#ifdef PROGMEM\n#undef PROGMEM\n#define PROGMEM __attribute__((section(\".progmem.data\"))) // Workaround for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734#c4\n#ifdef PSTR\n#undef PSTR\n#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];})) // Copied from pgmspace.h in avr-libc source\n#endif\n#endif\n#endif\n#endif\n\n#if !defined(DEBUG_USB_HOST) && ENABLE_UHS_DEBUGGING\n#define DEBUG_USB_HOST\n#endif\n\n#if !defined(WIICAMERA) && ENABLE_WII_IR_CAMERA\n#define WIICAMERA\n#endif\n\n// To use some other locking (e.g. freertos),\n// define XMEM_ACQUIRE_SPI and XMEM_RELEASE_SPI to point to your lock and unlock.\n// NOTE: NO argument is passed. You have to do this within your routine for\n// whatever you are using to lock and unlock.\n#if !defined(XMEM_ACQUIRE_SPI)\n#if USE_XMEM_SPI_LOCK || defined(USE_MULTIPLE_APP_API)\n#include <xmem.h>\n#else\n#define XMEM_ACQUIRE_SPI() (void(0))\n#define XMEM_RELEASE_SPI() (void(0))\n#endif\n#endif\n\n#if !defined(EXT_RAM) && defined(EXT_RAM_STACK) || defined(EXT_RAM_HEAP)\n#include <xmem.h>\n#else\n#define EXT_RAM 0\n#endif\n\n#if defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__))\n#define USING_SPI4TEENSY3 USE_SPI4TEENSY3\n#else\n#define USING_SPI4TEENSY3 0\n#endif\n\n#if ((defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)) || defined(RBL_NRF51822) || defined(__ARDUINO_X86__) || ARDUINO >= 10600) && !USING_SPI4TEENSY3\n#include <SPI.h> // Use the Arduino SPI library for the Arduino Due, RedBearLab nRF51822, Intel Galileo 1 & 2, Intel Edison or if the SPI library with transaction is available\n#endif\n#if defined(__PIC32MX__) || defined(__PIC32MZ__)\n#include <../../../../hardware/pic32/libraries/SPI/SPI.h> // Hack to use the SPI library\n#endif\n\n#endif /* SETTINGS_H */\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/sink_parser.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(_usb_h_) || defined(__SINK_PARSER_H__)\n#error \"Never include hexdump.h directly; include Usb.h instead\"\n#else\n#define __SINK_PARSER_H__\n\nextern int UsbDEBUGlvl;\n\n// This parser does absolutely nothing with the data, just swallows it.\n\ntemplate <class BASE_CLASS, class LEN_TYPE, class OFFSET_TYPE>\nclass SinkParser : public BASE_CLASS {\npublic:\n\n        SinkParser() {\n        };\n\n        void Initialize() {\n        };\n\n        void Parse(const LEN_TYPE len, const uint8_t *pbuf, const OFFSET_TYPE &offset) {\n        };\n};\n\n\n#endif // __HEXDUMP_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/usb_ch9.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n#if !defined(_usb_h_) || defined(_ch9_h_)\n#error \"Never include usb_ch9.h directly; include Usb.h instead\"\n#else\n\n/* USB chapter 9 structures */\n#define _ch9_h_\n\n/* Misc.USB constants */\n#define DEV_DESCR_LEN   18      //device descriptor length\n#define CONF_DESCR_LEN  9       //configuration descriptor length\n#define INTR_DESCR_LEN  9       //interface descriptor length\n#define EP_DESCR_LEN    7       //endpoint descriptor length\n\n/* Standard Device Requests */\n\n#define USB_REQUEST_GET_STATUS                  0       // Standard Device Request - GET STATUS\n#define USB_REQUEST_CLEAR_FEATURE               1       // Standard Device Request - CLEAR FEATURE\n#define USB_REQUEST_SET_FEATURE                 3       // Standard Device Request - SET FEATURE\n#define USB_REQUEST_SET_ADDRESS                 5       // Standard Device Request - SET ADDRESS\n#define USB_REQUEST_GET_DESCRIPTOR              6       // Standard Device Request - GET DESCRIPTOR\n#define USB_REQUEST_SET_DESCRIPTOR              7       // Standard Device Request - SET DESCRIPTOR\n#define USB_REQUEST_GET_CONFIGURATION           8       // Standard Device Request - GET CONFIGURATION\n#define USB_REQUEST_SET_CONFIGURATION           9       // Standard Device Request - SET CONFIGURATION\n#define USB_REQUEST_GET_INTERFACE               10      // Standard Device Request - GET INTERFACE\n#define USB_REQUEST_SET_INTERFACE               11      // Standard Device Request - SET INTERFACE\n#define USB_REQUEST_SYNCH_FRAME                 12      // Standard Device Request - SYNCH FRAME\n\n#define USB_FEATURE_ENDPOINT_HALT               0       // CLEAR/SET FEATURE - Endpoint Halt\n#define USB_FEATURE_DEVICE_REMOTE_WAKEUP        1       // CLEAR/SET FEATURE - Device remote wake-up\n#define USB_FEATURE_TEST_MODE                   2       // CLEAR/SET FEATURE - Test mode\n\n/* Setup Data Constants */\n\n#define USB_SETUP_HOST_TO_DEVICE                0x00    // Device Request bmRequestType transfer direction - host to device transfer\n#define USB_SETUP_DEVICE_TO_HOST                0x80    // Device Request bmRequestType transfer direction - device to host transfer\n#define USB_SETUP_TYPE_STANDARD                 0x00    // Device Request bmRequestType type - standard\n#define USB_SETUP_TYPE_CLASS                    0x20    // Device Request bmRequestType type - class\n#define USB_SETUP_TYPE_VENDOR                   0x40    // Device Request bmRequestType type - vendor\n#define USB_SETUP_RECIPIENT_DEVICE              0x00    // Device Request bmRequestType recipient - device\n#define USB_SETUP_RECIPIENT_INTERFACE           0x01    // Device Request bmRequestType recipient - interface\n#define USB_SETUP_RECIPIENT_ENDPOINT            0x02    // Device Request bmRequestType recipient - endpoint\n#define USB_SETUP_RECIPIENT_OTHER               0x03    // Device Request bmRequestType recipient - other\n\n/* USB descriptors  */\n\n#define USB_DESCRIPTOR_DEVICE                   0x01    // bDescriptorType for a Device Descriptor.\n#define USB_DESCRIPTOR_CONFIGURATION            0x02    // bDescriptorType for a Configuration Descriptor.\n#define USB_DESCRIPTOR_STRING                   0x03    // bDescriptorType for a String Descriptor.\n#define USB_DESCRIPTOR_INTERFACE                0x04    // bDescriptorType for an Interface Descriptor.\n#define USB_DESCRIPTOR_ENDPOINT                 0x05    // bDescriptorType for an Endpoint Descriptor.\n#define USB_DESCRIPTOR_DEVICE_QUALIFIER         0x06    // bDescriptorType for a Device Qualifier.\n#define USB_DESCRIPTOR_OTHER_SPEED              0x07    // bDescriptorType for a Other Speed Configuration.\n#define USB_DESCRIPTOR_INTERFACE_POWER          0x08    // bDescriptorType for Interface Power.\n#define USB_DESCRIPTOR_OTG                      0x09    // bDescriptorType for an OTG Descriptor.\n\n#define HID_DESCRIPTOR_HID                      0x21\n\n\n\n/* OTG SET FEATURE Constants    */\n#define OTG_FEATURE_B_HNP_ENABLE                3       // SET FEATURE OTG - Enable B device to perform HNP\n#define OTG_FEATURE_A_HNP_SUPPORT               4       // SET FEATURE OTG - A device supports HNP\n#define OTG_FEATURE_A_ALT_HNP_SUPPORT           5       // SET FEATURE OTG - Another port on the A device supports HNP\n\n/* USB Endpoint Transfer Types  */\n#define USB_TRANSFER_TYPE_CONTROL               0x00    // Endpoint is a control endpoint.\n#define USB_TRANSFER_TYPE_ISOCHRONOUS           0x01    // Endpoint is an isochronous endpoint.\n#define USB_TRANSFER_TYPE_BULK                  0x02    // Endpoint is a bulk endpoint.\n#define USB_TRANSFER_TYPE_INTERRUPT             0x03    // Endpoint is an interrupt endpoint.\n#define bmUSB_TRANSFER_TYPE                     0x03    // bit mask to separate transfer type from ISO attributes\n\n\n/* Standard Feature Selectors for CLEAR_FEATURE Requests    */\n#define USB_FEATURE_ENDPOINT_STALL              0       // Endpoint recipient\n#define USB_FEATURE_DEVICE_REMOTE_WAKEUP        1       // Device recipient\n#define USB_FEATURE_TEST_MODE                   2       // Device recipient\n\n/* descriptor data structures */\n\n/* Device descriptor structure */\ntypedef struct {\n        uint8_t bLength; // Length of this descriptor.\n        uint8_t bDescriptorType; // DEVICE descriptor type (USB_DESCRIPTOR_DEVICE).\n        uint16_t bcdUSB; // USB Spec Release Number (BCD).\n        uint8_t bDeviceClass; // Class code (assigned by the USB-IF). 0xFF-Vendor specific.\n        uint8_t bDeviceSubClass; // Subclass code (assigned by the USB-IF).\n        uint8_t bDeviceProtocol; // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific.\n        uint8_t bMaxPacketSize0; // Maximum packet size for endpoint 0.\n        uint16_t idVendor; // Vendor ID (assigned by the USB-IF).\n        uint16_t idProduct; // Product ID (assigned by the manufacturer).\n        uint16_t bcdDevice; // Device release number (BCD).\n        uint8_t iManufacturer; // Index of String Descriptor describing the manufacturer.\n        uint8_t iProduct; // Index of String Descriptor describing the product.\n        uint8_t iSerialNumber; // Index of String Descriptor with the device's serial number.\n        uint8_t bNumConfigurations; // Number of possible configurations.\n} __attribute__((packed)) USB_DEVICE_DESCRIPTOR;\n\n/* Configuration descriptor structure */\ntypedef struct {\n        uint8_t bLength; // Length of this descriptor.\n        uint8_t bDescriptorType; // CONFIGURATION descriptor type (USB_DESCRIPTOR_CONFIGURATION).\n        uint16_t wTotalLength; // Total length of all descriptors for this configuration.\n        uint8_t bNumInterfaces; // Number of interfaces in this configuration.\n        uint8_t bConfigurationValue; // Value of this configuration (1 based).\n        uint8_t iConfiguration; // Index of String Descriptor describing the configuration.\n        uint8_t bmAttributes; // Configuration characteristics.\n        uint8_t bMaxPower; // Maximum power consumed by this configuration.\n} __attribute__((packed)) USB_CONFIGURATION_DESCRIPTOR;\n\n/* Interface descriptor structure */\ntypedef struct {\n        uint8_t bLength; // Length of this descriptor.\n        uint8_t bDescriptorType; // INTERFACE descriptor type (USB_DESCRIPTOR_INTERFACE).\n        uint8_t bInterfaceNumber; // Number of this interface (0 based).\n        uint8_t bAlternateSetting; // Value of this alternate interface setting.\n        uint8_t bNumEndpoints; // Number of endpoints in this interface.\n        uint8_t bInterfaceClass; // Class code (assigned by the USB-IF).  0xFF-Vendor specific.\n        uint8_t bInterfaceSubClass; // Subclass code (assigned by the USB-IF).\n        uint8_t bInterfaceProtocol; // Protocol code (assigned by the USB-IF).  0xFF-Vendor specific.\n        uint8_t iInterface; // Index of String Descriptor describing the interface.\n} __attribute__((packed)) USB_INTERFACE_DESCRIPTOR;\n\n/* Endpoint descriptor structure */\ntypedef struct {\n        uint8_t bLength; // Length of this descriptor.\n        uint8_t bDescriptorType; // ENDPOINT descriptor type (USB_DESCRIPTOR_ENDPOINT).\n        uint8_t bEndpointAddress; // Endpoint address. Bit 7 indicates direction (0=OUT, 1=IN).\n        uint8_t bmAttributes; // Endpoint transfer type.\n        uint16_t wMaxPacketSize; // Maximum packet size.\n        uint8_t bInterval; // Polling interval in frames.\n} __attribute__((packed)) USB_ENDPOINT_DESCRIPTOR;\n\n/* HID descriptor */\ntypedef struct {\n        uint8_t bLength;\n        uint8_t bDescriptorType;\n        uint16_t bcdHID; // HID class specification release\n        uint8_t bCountryCode;\n        uint8_t bNumDescriptors; // Number of additional class specific descriptors\n        uint8_t bDescrType; // Type of class descriptor\n        uint16_t wDescriptorLength; // Total size of the Report descriptor\n} __attribute__((packed)) USB_HID_DESCRIPTOR;\n\ntypedef struct {\n        uint8_t bDescrType; // Type of class descriptor\n        uint16_t wDescriptorLength; // Total size of the Report descriptor\n} __attribute__((packed)) HID_CLASS_DESCRIPTOR_LEN_AND_TYPE;\n\n#endif // _ch9_h_\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/usbhost.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n/* MAX3421E-based USB Host Library header file */\n\n\n#if !defined(_usb_h_) || defined(_USBHOST_H_)\n#error \"Never include usbhost.h directly; include Usb.h instead\"\n#else\n#define _USBHOST_H_\n\n#if USING_SPI4TEENSY3\n#include <spi4teensy3.h>\n#include <sys/types.h>\n#endif\n\n/* SPI initialization */\ntemplate< typename SPI_CLK, typename SPI_MOSI, typename SPI_MISO, typename SPI_SS > class SPi {\npublic:\n#if USING_SPI4TEENSY3\n        static void init() {\n                // spi4teensy3 inits everything for us, except /SS\n                // CLK, MOSI and MISO are hard coded for now.\n                // spi4teensy3::init(0,0,0); // full speed, cpol 0, cpha 0\n                spi4teensy3::init(); // full speed, cpol 0, cpha 0\n                SPI_SS::SetDirWrite();\n                SPI_SS::Set();\n        }\n#elif SPI_HAS_TRANSACTION\n        static void init() {\n                SPI.begin(); // The SPI library with transaction will take care of setting up the pins - settings is set in beginTransaction()\n        }\n#elif !defined(SPDR)\n        static void init() {\n                SPI_SS::SetDirWrite();\n                SPI_SS::Set();\n                SPI.begin();\n#if defined(__MIPSEL__)\n                SPI.setClockDivider(1);\n#elif defined(__ARDUINO_X86__)\n                #ifdef SPI_CLOCK_1M // Hack used to check if setClockSpeed is available\n                    SPI.setClockSpeed(12000000); // The MAX3421E can handle up to 26MHz, but in practice this was the maximum that I could reliably use\n                #else\n                    SPI.setClockDivider(SPI_CLOCK_DIV2); // This will set the SPI frequency to 8MHz - it could be higher, but it is not supported in the old API\n                #endif\n#else\n                SPI.setClockDivider(4); // Set speed to 84MHz/4=21MHz - the MAX3421E can handle up to 26MHz\n#endif\n        }\n#elif defined(RBL_NRF51822)\n        static void init() {\n                SPI_SS::SetDirWrite();\n                SPI_SS::Set();\n                SPI.begin();\n                // SPI.setFrequency(SPI_FREQUENCY_8M);\n        }\n#else\n        static void init() {\n                //uint8_t tmp;\n                SPI_CLK::SetDirWrite();\n                SPI_MOSI::SetDirWrite();\n                SPI_MISO::SetDirRead();\n                SPI_SS::SetDirWrite();\n                /* mode 00 (CPOL=0, CPHA=0) master, fclk/2. Mode 11 (CPOL=11, CPHA=11) is also supported by MAX3421E */\n                SPCR = 0x50;\n                SPSR = 0x01; // 0x01\n                /**/\n                //tmp = SPSR;\n                //tmp = SPDR;\n        }\n#endif\n};\n\n/* SPI pin definitions. see avrpins.h   */\n#if defined(__AVR_ATmega1280__) || (__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)\ntypedef SPi< Pb1, Pb2, Pb3, Pb0 > spi;\n#elif  defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)\ntypedef SPi< Pb5, Pb3, Pb4, Pb2 > spi;\n#elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)\ntypedef SPi< Pb7, Pb5, Pb6, Pb4 > spi;\n#elif (defined(CORE_TEENSY) && (defined(__MK20DX128__) || defined(__MK20DX256__))) || defined(__ARDUINO_X86__) || defined(__MIPSEL__)\ntypedef SPi< P13, P11, P12, P10 > spi;\n#elif defined(ARDUINO_SAM_DUE) && defined(__SAM3X8E__)\ntypedef SPi< P76, P75, P74, P10 > spi;\n#elif defined(RBL_NRF51822)\ntypedef SPi< P16, P18, P17, P10 > spi;\n#else\n#error \"No SPI entry in usbhost.h\"\n#endif\n\ntypedef enum {\n        vbus_on = 0,\n        vbus_off = GPX_VBDET\n} VBUS_t;\n\ntemplate< typename SPI_SS, typename INTR > class MAX3421e /* : public spi */ {\n        static uint8_t vbusState;\n\npublic:\n        MAX3421e();\n        void regWr(uint8_t reg, uint8_t data);\n        uint8_t* bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p);\n        void gpioWr(uint8_t data);\n        uint8_t regRd(uint8_t reg);\n        uint8_t* bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p);\n        uint8_t gpioRd();\n        uint16_t reset();\n        int8_t Init();\n        int8_t Init(int mseconds);\n\n        void vbusPower(VBUS_t state) {\n                regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | state));\n        }\n\n        uint8_t getVbusState(void) {\n                return vbusState;\n        };\n        void busprobe();\n        uint8_t GpxHandler();\n        uint8_t IntHandler();\n        uint8_t Task();\n};\n\ntemplate< typename SPI_SS, typename INTR >\n        uint8_t MAX3421e< SPI_SS, INTR >::vbusState = 0;\n\n/* constructor */\ntemplate< typename SPI_SS, typename INTR >\nMAX3421e< SPI_SS, INTR >::MAX3421e() {\n        // Leaving ADK hardware setup in here, for now. This really belongs with the other parts.\n#ifdef BOARD_MEGA_ADK\n        // For Mega ADK, which has a Max3421e on-board, set MAX_RESET to output mode, and then set it to HIGH\n        P55::SetDirWrite();\n        P55::Set();\n#endif\n};\n\n/* write single byte into MAX3421 register */\ntemplate< typename SPI_SS, typename INTR >\nvoid MAX3421e< SPI_SS, INTR >::regWr(uint8_t reg, uint8_t data) {\n        XMEM_ACQUIRE_SPI();\n#if SPI_HAS_TRANSACTION\n        SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0\n#endif\n        SPI_SS::Clear();\n\n#if USING_SPI4TEENSY3\n        uint8_t c[2];\n        c[0] = reg | 0x02;\n        c[1] = data;\n        spi4teensy3::send(c, 2);\n#elif SPI_HAS_TRANSACTION\n        uint8_t c[2];\n        c[0] = reg | 0x02;\n        c[1] = data;\n        SPI.transfer(c, 2);\n#elif !defined(SPDR)\n        SPI.transfer(reg | 0x02);\n        SPI.transfer(data);\n#else\n        SPDR = (reg | 0x02);\n        while(!(SPSR & (1 << SPIF)));\n        SPDR = data;\n        while(!(SPSR & (1 << SPIF)));\n#endif\n\n        SPI_SS::Set();\n#if SPI_HAS_TRANSACTION\n        SPI.endTransaction();\n#endif\n        XMEM_RELEASE_SPI();\n        return;\n};\n/* multiple-byte write                            */\n\n/* returns a pointer to memory position after last written */\ntemplate< typename SPI_SS, typename INTR >\nuint8_t* MAX3421e< SPI_SS, INTR >::bytesWr(uint8_t reg, uint8_t nbytes, uint8_t* data_p) {\n        XMEM_ACQUIRE_SPI();\n#if SPI_HAS_TRANSACTION\n        SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0\n#endif\n        SPI_SS::Clear();\n\n#if USING_SPI4TEENSY3\n        spi4teensy3::send(reg | 0x02);\n        spi4teensy3::send(data_p, nbytes);\n        data_p += nbytes;\n#elif SPI_HAS_TRANSACTION\n        SPI.transfer(reg | 0x02);\n        SPI.transfer(data_p, nbytes);\n        data_p += nbytes;\n#elif defined(__ARDUINO_X86__)\n        SPI.transfer(reg | 0x02);\n        SPI.transferBuffer(data_p, NULL, nbytes);\n        data_p += nbytes;\n#elif !defined(SPDR)\n        SPI.transfer(reg | 0x02);\n        while(nbytes) {\n                SPI.transfer(*data_p);\n                nbytes--;\n                data_p++; // advance data pointer\n        }\n#else\n        SPDR = (reg | 0x02); //set WR bit and send register number\n        while(nbytes) {\n                while(!(SPSR & (1 << SPIF))); //check if previous byte was sent\n                SPDR = (*data_p); // send next data byte\n                nbytes--;\n                data_p++; // advance data pointer\n        }\n        while(!(SPSR & (1 << SPIF)));\n#endif\n\n        SPI_SS::Set();\n#if SPI_HAS_TRANSACTION\n        SPI.endTransaction();\n#endif\n        XMEM_RELEASE_SPI();\n        return ( data_p);\n}\n/* GPIO write                                           */\n/*GPIO byte is split between 2 registers, so two writes are needed to write one byte */\n\n/* GPOUT bits are in the low nibble. 0-3 in IOPINS1, 4-7 in IOPINS2 */\ntemplate< typename SPI_SS, typename INTR >\nvoid MAX3421e< SPI_SS, INTR >::gpioWr(uint8_t data) {\n        regWr(rIOPINS1, data);\n        data >>= 4;\n        regWr(rIOPINS2, data);\n        return;\n}\n\n/* single host register read    */\ntemplate< typename SPI_SS, typename INTR >\nuint8_t MAX3421e< SPI_SS, INTR >::regRd(uint8_t reg) {\n        XMEM_ACQUIRE_SPI();\n#if SPI_HAS_TRANSACTION\n        SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0\n#endif\n        SPI_SS::Clear();\n\n#if USING_SPI4TEENSY3\n        spi4teensy3::send(reg);\n        uint8_t rv = spi4teensy3::receive();\n        SPI_SS::Set();\n#elif !defined(SPDR) || SPI_HAS_TRANSACTION\n        SPI.transfer(reg);\n        uint8_t rv = SPI.transfer(0); // Send empty byte\n        SPI_SS::Set();\n#else\n        SPDR = reg;\n        while(!(SPSR & (1 << SPIF)));\n        SPDR = 0; // Send empty byte\n        while(!(SPSR & (1 << SPIF)));\n        SPI_SS::Set();\n        uint8_t rv = SPDR;\n#endif\n\n#if SPI_HAS_TRANSACTION\n        SPI.endTransaction();\n#endif\n        XMEM_RELEASE_SPI();\n        return (rv);\n}\n/* multiple-byte register read  */\n\n/* returns a pointer to a memory position after last read   */\ntemplate< typename SPI_SS, typename INTR >\nuint8_t* MAX3421e< SPI_SS, INTR >::bytesRd(uint8_t reg, uint8_t nbytes, uint8_t* data_p) {\n        XMEM_ACQUIRE_SPI();\n#if SPI_HAS_TRANSACTION\n        SPI.beginTransaction(SPISettings(26000000, MSBFIRST, SPI_MODE0)); // The MAX3421E can handle up to 26MHz, use MSB First and SPI mode 0\n#endif\n        SPI_SS::Clear();\n\n#if USING_SPI4TEENSY3\n        spi4teensy3::send(reg);\n        spi4teensy3::receive(data_p, nbytes);\n        data_p += nbytes;\n#elif SPI_HAS_TRANSACTION\n        SPI.transfer(reg);\n        memset(data_p, 0, nbytes); // Make sure we send out empty bytes\n        SPI.transfer(data_p, nbytes);\n        data_p += nbytes;\n#elif defined(__ARDUINO_X86__)\n        SPI.transfer(reg);\n        SPI.transferBuffer(NULL, data_p, nbytes);\n        data_p += nbytes;\n#elif !defined(SPDR)\n        SPI.transfer(reg);\n        while(nbytes) {\n            *data_p++ = SPI.transfer(0);\n            nbytes--;\n        }\n#else\n        SPDR = reg;\n        while(!(SPSR & (1 << SPIF))); //wait\n        while(nbytes) {\n                SPDR = 0; // Send empty byte\n                nbytes--;\n                while(!(SPSR & (1 << SPIF)));\n#if 0\n                {\n                        *data_p = SPDR;\n                        printf(\"%2.2x \", *data_p);\n                }\n                data_p++;\n        }\n        printf(\"\\r\\n\");\n#else\n                *data_p++ = SPDR;\n        }\n#endif\n#endif\n\n        SPI_SS::Set();\n#if SPI_HAS_TRANSACTION\n        SPI.endTransaction();\n#endif\n        XMEM_RELEASE_SPI();\n        return ( data_p);\n}\n/* GPIO read. See gpioWr for explanation */\n\n/* GPIN pins are in high nibbles of IOPINS1, IOPINS2    */\ntemplate< typename SPI_SS, typename INTR >\nuint8_t MAX3421e< SPI_SS, INTR >::gpioRd() {\n        uint8_t gpin = 0;\n        gpin = regRd(rIOPINS2); //pins 4-7\n        gpin &= 0xf0; //clean lower nibble\n        gpin |= (regRd(rIOPINS1) >> 4); //shift low bits and OR with upper from previous operation.\n        return ( gpin);\n}\n\n/* reset MAX3421E. Returns number of cycles it took for PLL to stabilize after reset\n  or zero if PLL haven't stabilized in 65535 cycles */\ntemplate< typename SPI_SS, typename INTR >\nuint16_t MAX3421e< SPI_SS, INTR >::reset() {\n        uint16_t i = 0;\n        regWr(rUSBCTL, bmCHIPRES);\n        regWr(rUSBCTL, 0x00);\n        while(++i) {\n                if((regRd(rUSBIRQ) & bmOSCOKIRQ)) {\n                        break;\n                }\n        }\n        return ( i);\n}\n\n/* initialize MAX3421E. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not */\ntemplate< typename SPI_SS, typename INTR >\nint8_t MAX3421e< SPI_SS, INTR >::Init() {\n        XMEM_ACQUIRE_SPI();\n        // Moved here.\n        // you really should not init hardware in the constructor when it involves locks.\n        // Also avoids the vbus flicker issue confusing some devices.\n        /* pin and peripheral setup */\n        SPI_SS::SetDirWrite();\n        SPI_SS::Set();\n        spi::init();\n        INTR::SetDirRead();\n        XMEM_RELEASE_SPI();\n        /* MAX3421E - full-duplex SPI, level interrupt */\n        // GPX pin on. Moved here, otherwise we flicker the vbus.\n        regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL));\n\n        if(reset() == 0) { //OSCOKIRQ hasn't asserted in time\n                return ( -1);\n        }\n\n        regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host\n\n        regWr(rHIEN, bmCONDETIE | bmFRAMEIE); //connection detection\n\n        /* check if device is connected */\n        regWr(rHCTL, bmSAMPLEBUS); // sample USB bus\n        while(!(regRd(rHCTL) & bmSAMPLEBUS)); //wait for sample operation to finish\n\n        busprobe(); //check if anything is connected\n\n        regWr(rHIRQ, bmCONDETIRQ); //clear connection detect interrupt\n        regWr(rCPUCTL, 0x01); //enable interrupt pin\n\n        return ( 0);\n}\n\n/* initialize MAX3421E. Set Host mode, pullups, and stuff. Returns 0 if success, -1 if not */\ntemplate< typename SPI_SS, typename INTR >\nint8_t MAX3421e< SPI_SS, INTR >::Init(int mseconds) {\n        XMEM_ACQUIRE_SPI();\n        // Moved here.\n        // you really should not init hardware in the constructor when it involves locks.\n        // Also avoids the vbus flicker issue confusing some devices.\n        /* pin and peripheral setup */\n        SPI_SS::SetDirWrite();\n        SPI_SS::Set();\n        spi::init();\n        INTR::SetDirRead();\n        XMEM_RELEASE_SPI();\n        /* MAX3421E - full-duplex SPI, level interrupt, vbus off */\n        regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL | GPX_VBDET));\n\n        if(reset() == 0) { //OSCOKIRQ hasn't asserted in time\n                return ( -1);\n        }\n\n        // Delay a minimum of 1 second to ensure any capacitors are drained.\n        // 1 second is required to make sure we do not smoke a Microdrive!\n        if(mseconds < 1000) mseconds = 1000;\n        delay(mseconds);\n\n        regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST); // set pull-downs, Host\n\n        regWr(rHIEN, bmCONDETIE | bmFRAMEIE); //connection detection\n\n        /* check if device is connected */\n        regWr(rHCTL, bmSAMPLEBUS); // sample USB bus\n        while(!(regRd(rHCTL) & bmSAMPLEBUS)); //wait for sample operation to finish\n\n        busprobe(); //check if anything is connected\n\n        regWr(rHIRQ, bmCONDETIRQ); //clear connection detect interrupt\n        regWr(rCPUCTL, 0x01); //enable interrupt pin\n\n        // GPX pin on. This is done here so that busprobe will fail if we have a switch connected.\n        regWr(rPINCTL, (bmFDUPSPI | bmINTLEVEL));\n\n        return ( 0);\n}\n\n/* probe bus to determine device presence and speed and switch host to this speed */\ntemplate< typename SPI_SS, typename INTR >\nvoid MAX3421e< SPI_SS, INTR >::busprobe() {\n        uint8_t bus_sample;\n        bus_sample = regRd(rHRSL); //Get J,K status\n        bus_sample &= (bmJSTATUS | bmKSTATUS); //zero the rest of the byte\n        switch(bus_sample) { //start full-speed or low-speed host\n                case( bmJSTATUS):\n                        if((regRd(rMODE) & bmLOWSPEED) == 0) {\n                                regWr(rMODE, MODE_FS_HOST); //start full-speed host\n                                vbusState = FSHOST;\n                        } else {\n                                regWr(rMODE, MODE_LS_HOST); //start low-speed host\n                                vbusState = LSHOST;\n                        }\n                        break;\n                case( bmKSTATUS):\n                        if((regRd(rMODE) & bmLOWSPEED) == 0) {\n                                regWr(rMODE, MODE_LS_HOST); //start low-speed host\n                                vbusState = LSHOST;\n                        } else {\n                                regWr(rMODE, MODE_FS_HOST); //start full-speed host\n                                vbusState = FSHOST;\n                        }\n                        break;\n                case( bmSE1): //illegal state\n                        vbusState = SE1;\n                        break;\n                case( bmSE0): //disconnected state\n                        regWr(rMODE, bmDPPULLDN | bmDMPULLDN | bmHOST | bmSEPIRQ);\n                        vbusState = SE0;\n                        break;\n        }//end switch( bus_sample )\n}\n\n/* MAX3421 state change task and interrupt handler */\ntemplate< typename SPI_SS, typename INTR >\nuint8_t MAX3421e< SPI_SS, INTR >::Task(void) {\n        uint8_t rcode = 0;\n        uint8_t pinvalue;\n        //USB_HOST_SERIAL.print(\"Vbus state: \");\n        //USB_HOST_SERIAL.println( vbusState, HEX );\n        pinvalue = INTR::IsSet(); //Read();\n        //pinvalue = digitalRead( MAX_INT );\n        if(pinvalue == 0) {\n                rcode = IntHandler();\n        }\n        //    pinvalue = digitalRead( MAX_GPX );\n        //    if( pinvalue == LOW ) {\n        //        GpxHandler();\n        //    }\n        //    usbSM();                                //USB state machine\n        return ( rcode);\n}\n\ntemplate< typename SPI_SS, typename INTR >\nuint8_t MAX3421e< SPI_SS, INTR >::IntHandler() {\n        uint8_t HIRQ;\n        uint8_t HIRQ_sendback = 0x00;\n        HIRQ = regRd(rHIRQ); //determine interrupt source\n        //if( HIRQ & bmFRAMEIRQ ) {               //->1ms SOF interrupt handler\n        //    HIRQ_sendback |= bmFRAMEIRQ;\n        //}//end FRAMEIRQ handling\n        if(HIRQ & bmCONDETIRQ) {\n                busprobe();\n                HIRQ_sendback |= bmCONDETIRQ;\n        }\n        /* End HIRQ interrupts handling, clear serviced IRQs    */\n        regWr(rHIRQ, HIRQ_sendback);\n        return ( HIRQ_sendback);\n}\n//template< typename SPI_SS, typename INTR >\n//uint8_t MAX3421e< SPI_SS, INTR >::GpxHandler()\n//{\n//    uint8_t GPINIRQ = regRd( rGPINIRQ );          //read GPIN IRQ register\n////    if( GPINIRQ & bmGPINIRQ7 ) {            //vbus overload\n////        vbusPwr( OFF );                     //attempt powercycle\n////        delay( 1000 );\n////        vbusPwr( ON );\n////        regWr( rGPINIRQ, bmGPINIRQ7 );\n////    }\n//    return( GPINIRQ );\n//}\n\n#endif // _USBHOST_H_\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/usbhub.cpp",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#include \"usbhub.h\"\n\nbool USBHub::bResetInitiated = false;\n\nUSBHub::USBHub(USB *p) :\npUsb(p),\nbAddress(0),\nbNbrPorts(0),\n//bInitState(0),\nqNextPollTime(0),\nbPollEnable(false) {\n        epInfo[0].epAddr = 0;\n        epInfo[0].maxPktSize = 8;\n        epInfo[0].epAttribs = 0;\n        epInfo[0].bmNakPower = USB_NAK_MAX_POWER;\n\n        epInfo[1].epAddr = 1;\n        epInfo[1].maxPktSize = 8; //kludge\n        epInfo[1].epAttribs = 0;\n        epInfo[1].bmNakPower = USB_NAK_NOWAIT;\n\n        if(pUsb)\n                pUsb->RegisterDeviceClass(this);\n}\n\nuint8_t USBHub::Init(uint8_t parent, uint8_t port, bool lowspeed) {\n        uint8_t buf[32];\n        USB_DEVICE_DESCRIPTOR * udd = reinterpret_cast<USB_DEVICE_DESCRIPTOR*>(buf);\n        HubDescriptor* hd = reinterpret_cast<HubDescriptor*>(buf);\n        USB_CONFIGURATION_DESCRIPTOR * ucd = reinterpret_cast<USB_CONFIGURATION_DESCRIPTOR*>(buf);\n        uint8_t rcode;\n        UsbDevice *p = NULL;\n        EpInfo *oldep_ptr = NULL;\n        uint8_t len = 0;\n        uint16_t cd_len = 0;\n\n        //USBTRACE(\"\\r\\nHub Init Start \");\n        //D_PrintHex<uint8_t > (bInitState, 0x80);\n\n        AddressPool &addrPool = pUsb->GetAddressPool();\n\n        //switch (bInitState) {\n        //        case 0:\n        if(bAddress)\n                return USB_ERROR_CLASS_INSTANCE_ALREADY_IN_USE;\n\n        // Get pointer to pseudo device with address 0 assigned\n        p = addrPool.GetUsbDevicePtr(0);\n\n        if(!p)\n                return USB_ERROR_ADDRESS_NOT_FOUND_IN_POOL;\n\n        if(!p->epinfo)\n                return USB_ERROR_EPINFO_IS_NULL;\n\n        // Save old pointer to EP_RECORD of address 0\n        oldep_ptr = p->epinfo;\n\n        // Temporary assign new pointer to epInfo to p->epinfo in order to avoid toggle inconsistence\n        p->epinfo = epInfo;\n\n        p->lowspeed = lowspeed;\n\n        // Get device descriptor\n        rcode = pUsb->getDevDescr(0, 0, 8, (uint8_t*)buf);\n\n        p->lowspeed = false;\n\n        if(!rcode)\n                len = (buf[0] > 32) ? 32 : buf[0];\n\n        if(rcode) {\n                // Restore p->epinfo\n                p->epinfo = oldep_ptr;\n                return rcode;\n        }\n\n        // Extract device class from device descriptor\n        // If device class is not a hub return\n        if(udd->bDeviceClass != 0x09)\n                return USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED;\n\n        // Allocate new address according to device class\n        bAddress = addrPool.AllocAddress(parent, (udd->bDeviceClass == 0x09) ? true : false, port);\n\n        if(!bAddress)\n                return USB_ERROR_OUT_OF_ADDRESS_SPACE_IN_POOL;\n\n        // Extract Max Packet Size from the device descriptor\n        epInfo[0].maxPktSize = udd->bMaxPacketSize0;\n\n        // Assign new address to the device\n        rcode = pUsb->setAddr(0, 0, bAddress);\n\n        if(rcode) {\n                // Restore p->epinfo\n                p->epinfo = oldep_ptr;\n                addrPool.FreeAddress(bAddress);\n                bAddress = 0;\n                return rcode;\n        }\n\n        //USBTRACE2(\"\\r\\nHub address: \", bAddress );\n\n        // Restore p->epinfo\n        p->epinfo = oldep_ptr;\n\n        if(len)\n                rcode = pUsb->getDevDescr(bAddress, 0, len, (uint8_t*)buf);\n\n        if(rcode)\n                goto FailGetDevDescr;\n\n        // Assign epInfo to epinfo pointer\n        rcode = pUsb->setEpInfoEntry(bAddress, 2, epInfo);\n\n        if(rcode)\n                goto FailSetDevTblEntry;\n\n        //                bInitState = 1;\n\n        //        case 1:\n        // Get hub descriptor\n        rcode = GetHubDescriptor(0, 8, buf);\n\n        if(rcode)\n                goto FailGetHubDescr;\n\n        // Save number of ports for future use\n        bNbrPorts = hd->bNbrPorts;\n\n        //                bInitState = 2;\n\n        //        case 2:\n        // Read configuration Descriptor in Order To Obtain Proper Configuration Value\n        rcode = pUsb->getConfDescr(bAddress, 0, 8, 0, buf);\n\n        if(!rcode) {\n                cd_len = ucd->wTotalLength;\n                rcode = pUsb->getConfDescr(bAddress, 0, cd_len, 0, buf);\n        }\n        if(rcode)\n                goto FailGetConfDescr;\n\n        // The following code is of no practical use in real life applications.\n        // It only intended for the usb protocol sniffer to properly parse hub-class requests.\n        {\n                uint8_t buf2[24];\n\n                rcode = pUsb->getConfDescr(bAddress, 0, buf[0], 0, buf2);\n\n                if(rcode)\n                        goto FailGetConfDescr;\n        }\n\n        // Set Configuration Value\n        rcode = pUsb->setConf(bAddress, 0, buf[5]);\n\n        if(rcode)\n                goto FailSetConfDescr;\n\n        //                bInitState = 3;\n\n        //        case 3:\n        // Power on all ports\n        for(uint8_t j = 1; j <= bNbrPorts; j++)\n                SetPortFeature(HUB_FEATURE_PORT_POWER, j, 0); //HubPortPowerOn(j);\n\n        pUsb->SetHubPreMask();\n        bPollEnable = true;\n        //                bInitState = 0;\n        //}\n        //bInitState = 0;\n        //USBTRACE(\"...OK\\r\\n\");\n        return 0;\n\n        // Oleg, No debugging?? -- xxxajk\nFailGetDevDescr:\n        goto Fail;\n\nFailSetDevTblEntry:\n        goto Fail;\n\nFailGetHubDescr:\n        goto Fail;\n\nFailGetConfDescr:\n        goto Fail;\n\nFailSetConfDescr:\n        goto Fail;\n\nFail:\n        USBTRACE(\"...FAIL\\r\\n\");\n        return rcode;\n}\n\nuint8_t USBHub::Release() {\n        pUsb->GetAddressPool().FreeAddress(bAddress);\n\n        if(bAddress == 0x41)\n                pUsb->SetHubPreMask();\n\n        bAddress = 0;\n        bNbrPorts = 0;\n        qNextPollTime = 0;\n        bPollEnable = false;\n        return 0;\n}\n\nuint8_t USBHub::Poll() {\n        uint8_t rcode = 0;\n\n        if(!bPollEnable)\n                return 0;\n\n        if(((long)(millis() - qNextPollTime) >= 0L)) {\n                rcode = CheckHubStatus();\n                qNextPollTime = millis() + 100;\n        }\n        return rcode;\n}\n\nuint8_t USBHub::CheckHubStatus() {\n        uint8_t rcode;\n        uint8_t buf[8];\n        uint16_t read = 1;\n\n        rcode = pUsb->inTransfer(bAddress, 1, &read, buf);\n\n        if(rcode)\n                return rcode;\n\n        //if (buf[0] & 0x01) // Hub Status Change\n        //{\n        //        pUsb->PrintHubStatus(addr);\n        //        rcode = GetHubStatus(1, 0, 1, 4, buf);\n        //        if (rcode)\n        //        {\n        //                USB_HOST_SERIAL.print(\"GetHubStatus Error\");\n        //                USB_HOST_SERIAL.println(rcode, HEX);\n        //                return rcode;\n        //        }\n        //}\n        for(uint8_t port = 1, mask = 0x02; port < 8; mask <<= 1, port++) {\n                if(buf[0] & mask) {\n                        HubEvent evt;\n                        evt.bmEvent = 0;\n\n                        rcode = GetPortStatus(port, 4, evt.evtBuff);\n\n                        if(rcode)\n                                continue;\n\n                        rcode = PortStatusChange(port, evt);\n\n                        if(rcode == HUB_ERROR_PORT_HAS_BEEN_RESET)\n                                return 0;\n\n                        if(rcode)\n                                return rcode;\n                }\n        } // for\n\n        for(uint8_t port = 1; port <= bNbrPorts; port++) {\n                HubEvent evt;\n                evt.bmEvent = 0;\n\n                rcode = GetPortStatus(port, 4, evt.evtBuff);\n\n                if(rcode)\n                        continue;\n\n                if((evt.bmStatus & bmHUB_PORT_STATE_CHECK_DISABLED) != bmHUB_PORT_STATE_DISABLED)\n                        continue;\n\n                // Emulate connection event for the port\n                evt.bmChange |= bmHUB_PORT_STATUS_C_PORT_CONNECTION;\n\n                rcode = PortStatusChange(port, evt);\n\n                if(rcode == HUB_ERROR_PORT_HAS_BEEN_RESET)\n                        return 0;\n\n                if(rcode)\n                        return rcode;\n        } // for\n        return 0;\n}\n\nvoid USBHub::ResetHubPort(uint8_t port) {\n        HubEvent evt;\n        evt.bmEvent = 0;\n        uint8_t rcode;\n\n        ClearPortFeature(HUB_FEATURE_C_PORT_ENABLE, port, 0);\n        ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0);\n        SetPortFeature(HUB_FEATURE_PORT_RESET, port, 0);\n\n\n        for(int i = 0; i < 3; i++) {\n                rcode = GetPortStatus(port, 4, evt.evtBuff);\n                if(rcode) break; // Some kind of error, bail.\n                if(evt.bmEvent == bmHUB_PORT_EVENT_RESET_COMPLETE || evt.bmEvent == bmHUB_PORT_EVENT_LS_RESET_COMPLETE) {\n                        break;\n                }\n                delay(100); // simulate polling.\n        }\n        ClearPortFeature(HUB_FEATURE_C_PORT_RESET, port, 0);\n        ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0);\n        delay(20);\n}\n\nuint8_t USBHub::PortStatusChange(uint8_t port, HubEvent &evt) {\n        switch(evt.bmEvent) {\n                        // Device connected event\n                case bmHUB_PORT_EVENT_CONNECT:\n                case bmHUB_PORT_EVENT_LS_CONNECT:\n                        if(bResetInitiated)\n                                return 0;\n\n                        ClearPortFeature(HUB_FEATURE_C_PORT_ENABLE, port, 0);\n                        ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0);\n                        SetPortFeature(HUB_FEATURE_PORT_RESET, port, 0);\n                        bResetInitiated = true;\n                        return HUB_ERROR_PORT_HAS_BEEN_RESET;\n\n                        // Device disconnected event\n                case bmHUB_PORT_EVENT_DISCONNECT:\n                        ClearPortFeature(HUB_FEATURE_C_PORT_ENABLE, port, 0);\n                        ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0);\n                        bResetInitiated = false;\n\n                        UsbDeviceAddress a;\n                        a.devAddress = 0;\n                        a.bmHub = 0;\n                        a.bmParent = bAddress;\n                        a.bmAddress = port;\n                        pUsb->ReleaseDevice(a.devAddress);\n                        return 0;\n\n                        // Reset complete event\n                case bmHUB_PORT_EVENT_RESET_COMPLETE:\n                case bmHUB_PORT_EVENT_LS_RESET_COMPLETE:\n                        ClearPortFeature(HUB_FEATURE_C_PORT_RESET, port, 0);\n                        ClearPortFeature(HUB_FEATURE_C_PORT_CONNECTION, port, 0);\n\n                        delay(20);\n\n                        a.devAddress = bAddress;\n\n                        pUsb->Configuring(a.bmAddress, port, (evt.bmStatus & bmHUB_PORT_STATUS_PORT_LOW_SPEED));\n                        bResetInitiated = false;\n                        break;\n\n        } // switch (evt.bmEvent)\n        return 0;\n}\n\nvoid PrintHubPortStatus(USBHub *hubptr, uint8_t addr, uint8_t port, bool print_changes) {\n        uint8_t rcode = 0;\n        HubEvent evt;\n\n        rcode = hubptr->GetPortStatus(port, 4, evt.evtBuff);\n\n        if(rcode) {\n                USB_HOST_SERIAL.println(\"ERROR!\");\n                return;\n        }\n        USB_HOST_SERIAL.print(\"\\r\\nPort \");\n        USB_HOST_SERIAL.println(port, DEC);\n\n        USB_HOST_SERIAL.println(\"Status\");\n        USB_HOST_SERIAL.print(\"CONNECTION:\\t\");\n        USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_CONNECTION) > 0, DEC);\n        USB_HOST_SERIAL.print(\"ENABLE:\\t\\t\");\n        USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_ENABLE) > 0, DEC);\n        USB_HOST_SERIAL.print(\"SUSPEND:\\t\");\n        USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_SUSPEND) > 0, DEC);\n        USB_HOST_SERIAL.print(\"OVER_CURRENT:\\t\");\n        USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_OVER_CURRENT) > 0, DEC);\n        USB_HOST_SERIAL.print(\"RESET:\\t\\t\");\n        USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_RESET) > 0, DEC);\n        USB_HOST_SERIAL.print(\"POWER:\\t\\t\");\n        USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_POWER) > 0, DEC);\n        USB_HOST_SERIAL.print(\"LOW_SPEED:\\t\");\n        USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_LOW_SPEED) > 0, DEC);\n        USB_HOST_SERIAL.print(\"HIGH_SPEED:\\t\");\n        USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_HIGH_SPEED) > 0, DEC);\n        USB_HOST_SERIAL.print(\"TEST:\\t\\t\");\n        USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_TEST) > 0, DEC);\n        USB_HOST_SERIAL.print(\"INDICATOR:\\t\");\n        USB_HOST_SERIAL.println((evt.bmStatus & bmHUB_PORT_STATUS_PORT_INDICATOR) > 0, DEC);\n\n        if(!print_changes)\n                return;\n\n        USB_HOST_SERIAL.println(\"\\r\\nChange\");\n        USB_HOST_SERIAL.print(\"CONNECTION:\\t\");\n        USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_CONNECTION) > 0, DEC);\n        USB_HOST_SERIAL.print(\"ENABLE:\\t\\t\");\n        USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_ENABLE) > 0, DEC);\n        USB_HOST_SERIAL.print(\"SUSPEND:\\t\");\n        USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_SUSPEND) > 0, DEC);\n        USB_HOST_SERIAL.print(\"OVER_CURRENT:\\t\");\n        USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_OVER_CURRENT) > 0, DEC);\n        USB_HOST_SERIAL.print(\"RESET:\\t\\t\");\n        USB_HOST_SERIAL.println((evt.bmChange & bmHUB_PORT_STATUS_C_PORT_RESET) > 0, DEC);\n}\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/usbhub.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n#if !defined(__USBHUB_H__)\n#define __USBHUB_H__\n\n#include \"Usb.h\"\n\n#define USB_DESCRIPTOR_HUB                      0x09 // Hub descriptor type\n\n// Hub Requests\n#define bmREQ_CLEAR_HUB_FEATURE                 USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE\n#define bmREQ_CLEAR_PORT_FEATURE                USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER\n#define bmREQ_CLEAR_TT_BUFFER                   USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER\n#define bmREQ_GET_HUB_DESCRIPTOR                USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE\n#define bmREQ_GET_HUB_STATUS                    USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE\n#define bmREQ_GET_PORT_STATUS                   USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER\n#define bmREQ_RESET_TT                          USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER\n#define bmREQ_SET_HUB_DESCRIPTOR                USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE\n#define bmREQ_SET_HUB_FEATURE                   USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_DEVICE\n#define bmREQ_SET_PORT_FEATURE                  USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER\n#define bmREQ_GET_TT_STATE                      USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER\n#define bmREQ_STOP_TT                           USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_OTHER\n\n// Hub Class Requests\n#define HUB_REQUEST_CLEAR_TT_BUFFER             8\n#define HUB_REQUEST_RESET_TT                    9\n#define HUB_REQUEST_GET_TT_STATE                10\n#define HUB_REQUEST_STOP_TT                     11\n\n// Hub Features\n#define HUB_FEATURE_C_HUB_LOCAL_POWER           0\n#define HUB_FEATURE_C_HUB_OVER_CURRENT          1\n#define HUB_FEATURE_PORT_CONNECTION             0\n#define HUB_FEATURE_PORT_ENABLE                 1\n#define HUB_FEATURE_PORT_SUSPEND                2\n#define HUB_FEATURE_PORT_OVER_CURRENT           3\n#define HUB_FEATURE_PORT_RESET                  4\n#define HUB_FEATURE_PORT_POWER                  8\n#define HUB_FEATURE_PORT_LOW_SPEED              9\n#define HUB_FEATURE_C_PORT_CONNECTION           16\n#define HUB_FEATURE_C_PORT_ENABLE               17\n#define HUB_FEATURE_C_PORT_SUSPEND              18\n#define HUB_FEATURE_C_PORT_OVER_CURRENT         19\n#define HUB_FEATURE_C_PORT_RESET                20\n#define HUB_FEATURE_PORT_TEST                   21\n#define HUB_FEATURE_PORT_INDICATOR              22\n\n// Hub Port Test Modes\n#define HUB_PORT_TEST_MODE_J                    1\n#define HUB_PORT_TEST_MODE_K                    2\n#define HUB_PORT_TEST_MODE_SE0_NAK              3\n#define HUB_PORT_TEST_MODE_PACKET               4\n#define HUB_PORT_TEST_MODE_FORCE_ENABLE         5\n\n// Hub Port Indicator Color\n#define HUB_PORT_INDICATOR_AUTO                 0\n#define HUB_PORT_INDICATOR_AMBER                1\n#define HUB_PORT_INDICATOR_GREEN                2\n#define HUB_PORT_INDICATOR_OFF                  3\n\n// Hub Port Status Bitmasks\n#define bmHUB_PORT_STATUS_PORT_CONNECTION       0x0001\n#define bmHUB_PORT_STATUS_PORT_ENABLE           0x0002\n#define bmHUB_PORT_STATUS_PORT_SUSPEND          0x0004\n#define bmHUB_PORT_STATUS_PORT_OVER_CURRENT     0x0008\n#define bmHUB_PORT_STATUS_PORT_RESET            0x0010\n#define bmHUB_PORT_STATUS_PORT_POWER            0x0100\n#define bmHUB_PORT_STATUS_PORT_LOW_SPEED        0x0200\n#define bmHUB_PORT_STATUS_PORT_HIGH_SPEED       0x0400\n#define bmHUB_PORT_STATUS_PORT_TEST             0x0800\n#define bmHUB_PORT_STATUS_PORT_INDICATOR        0x1000\n\n// Hub Port Status Change Bitmasks (used one byte instead of two)\n#define bmHUB_PORT_STATUS_C_PORT_CONNECTION     0x0001\n#define bmHUB_PORT_STATUS_C_PORT_ENABLE         0x0002\n#define bmHUB_PORT_STATUS_C_PORT_SUSPEND        0x0004\n#define bmHUB_PORT_STATUS_C_PORT_OVER_CURRENT   0x0008\n#define bmHUB_PORT_STATUS_C_PORT_RESET          0x0010\n\n// Hub Status Bitmasks (used one byte instead of two)\n#define bmHUB_STATUS_LOCAL_POWER_SOURCE         0x01\n#define bmHUB_STATUS_OVER_CURRENT               0x12\n\n// Hub Status Change Bitmasks (used one byte instead of two)\n#define bmHUB_STATUS_C_LOCAL_POWER_SOURCE       0x01\n#define bmHUB_STATUS_C_OVER_CURRENT             0x12\n\n\n// Hub Port Configuring Substates\n#define USB_STATE_HUB_PORT_CONFIGURING          0xb0\n#define USB_STATE_HUB_PORT_POWERED_OFF          0xb1\n#define USB_STATE_HUB_PORT_WAIT_FOR_POWER_GOOD  0xb2\n#define USB_STATE_HUB_PORT_DISCONNECTED         0xb3\n#define USB_STATE_HUB_PORT_DISABLED             0xb4\n#define USB_STATE_HUB_PORT_RESETTING            0xb5\n#define USB_STATE_HUB_PORT_ENABLED              0xb6\n\n// Additional Error Codes\n#define HUB_ERROR_PORT_HAS_BEEN_RESET           0xb1\n\n// The bit mask to check for all necessary state bits\n#define bmHUB_PORT_STATUS_ALL_MAIN              ((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION | bmHUB_PORT_STATUS_C_PORT_ENABLE | bmHUB_PORT_STATUS_C_PORT_SUSPEND | bmHUB_PORT_STATUS_C_PORT_RESET) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_SUSPEND)\n\n// Bit mask to check for DISABLED state in HubEvent::bmStatus field\n#define bmHUB_PORT_STATE_CHECK_DISABLED         (0x0000 | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_SUSPEND)\n\n// Hub Port States\n#define bmHUB_PORT_STATE_DISABLED               (0x0000 | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_CONNECTION)\n\n// Hub Port Events\n#define bmHUB_PORT_EVENT_CONNECT                (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_CONNECTION)\n#define bmHUB_PORT_EVENT_DISCONNECT             (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION) << 16) | bmHUB_PORT_STATUS_PORT_POWER)\n#define bmHUB_PORT_EVENT_RESET_COMPLETE         (((0UL | bmHUB_PORT_STATUS_C_PORT_RESET) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION)\n\n#define bmHUB_PORT_EVENT_LS_CONNECT             (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_LOW_SPEED)\n#define bmHUB_PORT_EVENT_LS_RESET_COMPLETE      (((0UL | bmHUB_PORT_STATUS_C_PORT_RESET) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_LOW_SPEED)\n#define bmHUB_PORT_EVENT_LS_PORT_ENABLED        (((0UL | bmHUB_PORT_STATUS_C_PORT_CONNECTION | bmHUB_PORT_STATUS_C_PORT_ENABLE) << 16) | bmHUB_PORT_STATUS_PORT_POWER | bmHUB_PORT_STATUS_PORT_ENABLE | bmHUB_PORT_STATUS_PORT_CONNECTION | bmHUB_PORT_STATUS_PORT_LOW_SPEED)\n\nstruct HubDescriptor {\n        uint8_t bDescLength; // descriptor length\n        uint8_t bDescriptorType; // descriptor type\n        uint8_t bNbrPorts; // number of ports a hub equiped with\n\n        struct {\n                uint16_t LogPwrSwitchMode : 2;\n                uint16_t CompoundDevice : 1;\n                uint16_t OverCurrentProtectMode : 2;\n                uint16_t TTThinkTime : 2;\n                uint16_t PortIndicatorsSupported : 1;\n                uint16_t Reserved : 8;\n        } __attribute__((packed));\n\n        uint8_t bPwrOn2PwrGood;\n        uint8_t bHubContrCurrent;\n} __attribute__((packed));\n\nstruct HubEvent {\n\n        union {\n\n                struct {\n                        uint16_t bmStatus; // port status bits\n                        uint16_t bmChange; // port status change bits\n                } __attribute__((packed));\n                uint32_t bmEvent;\n                uint8_t evtBuff[4];\n        };\n} __attribute__((packed));\n\nclass USBHub : USBDeviceConfig {\n        static bool bResetInitiated; // True when reset is triggered\n\n        USB *pUsb; // USB class instance pointer\n\n        EpInfo epInfo[2]; // interrupt endpoint info structure\n\n        uint8_t bAddress; // address\n        uint8_t bNbrPorts; // number of ports\n        //        uint8_t bInitState; // initialization state variable\n        uint32_t qNextPollTime; // next poll time\n        bool bPollEnable; // poll enable flag\n\n        uint8_t CheckHubStatus();\n        uint8_t PortStatusChange(uint8_t port, HubEvent &evt);\n\npublic:\n        USBHub(USB *p);\n\n        uint8_t ClearHubFeature(uint8_t fid);\n        uint8_t ClearPortFeature(uint8_t fid, uint8_t port, uint8_t sel = 0);\n        uint8_t GetHubDescriptor(uint8_t index, uint16_t nbytes, uint8_t *dataptr);\n        uint8_t GetHubStatus(uint16_t nbytes, uint8_t* dataptr);\n        uint8_t GetPortStatus(uint8_t port, uint16_t nbytes, uint8_t* dataptr);\n        uint8_t SetHubDescriptor(uint8_t port, uint16_t nbytes, uint8_t* dataptr);\n        uint8_t SetHubFeature(uint8_t fid);\n        uint8_t SetPortFeature(uint8_t fid, uint8_t port, uint8_t sel = 0);\n\n        void PrintHubStatus();\n\n        uint8_t Init(uint8_t parent, uint8_t port, bool lowspeed);\n        uint8_t Release();\n        uint8_t Poll();\n        void ResetHubPort(uint8_t port);\n\n        virtual uint8_t GetAddress() {\n                return bAddress;\n        };\n\n        virtual bool DEVCLASSOK(uint8_t klass) {\n                return (klass == 0x09);\n        }\n\n};\n\n// Clear Hub Feature\n\ninline uint8_t USBHub::ClearHubFeature(uint8_t fid) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CLEAR_HUB_FEATURE, USB_REQUEST_CLEAR_FEATURE, fid, 0, 0, 0, 0, NULL, NULL));\n}\n// Clear Port Feature\n\ninline uint8_t USBHub::ClearPortFeature(uint8_t fid, uint8_t port, uint8_t sel) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_CLEAR_PORT_FEATURE, USB_REQUEST_CLEAR_FEATURE, fid, 0, ((0x0000 | port) | (sel << 8)), 0, 0, NULL, NULL));\n}\n// Get Hub Descriptor\n\ninline uint8_t USBHub::GetHubDescriptor(uint8_t index, uint16_t nbytes, uint8_t *dataptr) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_GET_HUB_DESCRIPTOR, USB_REQUEST_GET_DESCRIPTOR, index, 0x29, 0, nbytes, nbytes, dataptr, NULL));\n}\n// Get Hub Status\n\ninline uint8_t USBHub::GetHubStatus(uint16_t nbytes, uint8_t* dataptr) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_GET_HUB_STATUS, USB_REQUEST_GET_STATUS, 0, 0, 0x0000, nbytes, nbytes, dataptr, NULL));\n}\n// Get Port Status\n\ninline uint8_t USBHub::GetPortStatus(uint8_t port, uint16_t nbytes, uint8_t* dataptr) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_GET_PORT_STATUS, USB_REQUEST_GET_STATUS, 0, 0, port, nbytes, nbytes, dataptr, NULL));\n}\n// Set Hub Descriptor\n\ninline uint8_t USBHub::SetHubDescriptor(uint8_t port, uint16_t nbytes, uint8_t* dataptr) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_SET_HUB_DESCRIPTOR, USB_REQUEST_SET_DESCRIPTOR, 0, 0, port, nbytes, nbytes, dataptr, NULL));\n}\n// Set Hub Feature\n\ninline uint8_t USBHub::SetHubFeature(uint8_t fid) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_SET_HUB_FEATURE, USB_REQUEST_SET_FEATURE, fid, 0, 0, 0, 0, NULL, NULL));\n}\n// Set Port Feature\n\ninline uint8_t USBHub::SetPortFeature(uint8_t fid, uint8_t port, uint8_t sel) {\n        return ( pUsb->ctrlReq(bAddress, 0, bmREQ_SET_PORT_FEATURE, USB_REQUEST_SET_FEATURE, fid, 0, (((0x0000 | sel) << 8) | port), 0, 0, NULL, NULL));\n}\n\nvoid PrintHubPortStatus(USB *usbptr, uint8_t addr, uint8_t port, bool print_changes = false);\n\n#endif // __USBHUB_H__\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/version_helper.h",
    "content": "/* Copyright (C) 2011 Circuits At Home, LTD. All rights reserved.\n\nThis software may be distributed and modified under the terms of the GNU\nGeneral Public License version 2 (GPL2) as published by the Free Software\nFoundation and appearing in the file GPL2.TXT included in the packaging of\nthis file. Please note that GPL2 Section 2[b] requires that all works based\non this software must also be made publicly available under the terms of\nthe GPL2 (\"Copyleft\").\n\nContact information\n-------------------\n\nCircuits At Home, LTD\nWeb      :  http://www.circuitsathome.com\ne-mail   :  support@circuitsathome.com\n */\n\n/*\n * Universal Arduino(tm) \"IDE\" fixups.\n * Includes fixes for versions as low as 0023, used by Digilent.\n */\n\n#if defined(ARDUINO) && ARDUINO >=100\n#include <Arduino.h>\n#else\n#include <WProgram.h>\n#include <pins_arduino.h>\n#ifdef __AVR__\n#include <avr/pgmspace.h>\n#include <avr/io.h>\n#else\n#endif\n#endif\n\n#ifndef __PGMSPACE_H_\n#define __PGMSPACE_H_ 1\n\n#include <inttypes.h>\n\n#ifndef PROGMEM\n#define PROGMEM\n#endif\n#ifndef PGM_P\n#define PGM_P  const char *\n#endif\n#ifndef PSTR\n#define PSTR(str) (str)\n#endif\n#ifndef F\n#define F(str) (str)\n#endif\n#ifndef _SFR_BYTE\n#define _SFR_BYTE(n) (n)\n#endif\n\n#ifndef memchr_P\n#define memchr_P(str, c, len) memchr((str), (c), (len))\n#endif\n#ifndef memcmp_P\n#define memcmp_P(a, b, n) memcmp((a), (b), (n))\n#endif\n#ifndef memcpy_P\n#define memcpy_P(dest, src, num) memcpy((dest), (src), (num))\n#endif\n#ifndef memmem_P\n#define memmem_P(a, alen, b, blen) memmem((a), (alen), (b), (blen))\n#endif\n#ifndef memrchr_P\n#define memrchr_P(str, val, len) memrchr((str), (val), (len))\n#endif\n#ifndef strcat_P\n#define strcat_P(dest, src) strcat((dest), (src))\n#endif\n#ifndef strchr_P\n#define strchr_P(str, c) strchr((str), (c))\n#endif\n#ifndef strchrnul_P\n#define strchrnul_P(str, c) strchrnul((str), (c))\n#endif\n#ifndef strcmp_P\n#define strcmp_P(a, b) strcmp((a), (b))\n#endif\n#ifndef strcpy_P\n#define strcpy_P(dest, src) strcpy((dest), (src))\n#endif\n#ifndef strcasecmp_P\n#define strcasecmp_P(a, b) strcasecmp((a), (b))\n#endif\n#ifndef strcasestr_P\n#define strcasestr_P(a, b) strcasestr((a), (b))\n#endif\n#ifndef strlcat_P\n#define strlcat_P(dest, src, len) strlcat((dest), (src), (len))\n#endif\n#ifndef strlcpy_P\n#define strlcpy_P(dest, src, len) strlcpy((dest), (src), (len))\n#endif\n#ifndef strlen_P\n#define strlen_P(s) strlen((const char *)(s))\n#endif\n#ifndef strnlen_P\n#define strnlen_P(str, len) strnlen((str), (len))\n#endif\n#ifndef strncmp_P\n#define strncmp_P(a, b, n) strncmp((a), (b), (n))\n#endif\n#ifndef strncasecmp_P\n#define strncasecmp_P(a, b, n) strncasecmp((a), (b), (n))\n#endif\n#ifndef strncat_P\n#define strncat_P(a, b, n) strncat((a), (b), (n))\n#endif\n#ifndef strncpy_P\n#define strncpy_P(a, b, n) strncmp((a), (b), (n))\n#endif\n#ifndef strpbrk_P\n#define strpbrk_P(str, chrs) strpbrk((str), (chrs))\n#endif\n#ifndef strrchr_P\n#define strrchr_P(str, c) strrchr((str), (c))\n#endif\n#ifndef strsep_P\n#define strsep_P(strp, delim) strsep((strp), (delim))\n#endif\n#ifndef strspn_P\n#define strspn_P(str, chrs) strspn((str), (chrs))\n#endif\n#ifndef strstr_P\n#define strstr_P(a, b) strstr((a), (b))\n#endif\n#ifndef sprintf_P\n#define sprintf_P(s, ...) sprintf((s), __VA_ARGS__)\n#endif\n#ifndef vfprintf_P\n#define vfprintf_P(s, ...) vfprintf((s), __VA_ARGS__)\n#endif\n#ifndef printf_P\n#define printf_P(...) printf(__VA_ARGS__)\n#endif\n#ifndef snprintf_P\n#define snprintf_P(s, n, ...) ((s), (n), __VA_ARGS__)\n#endif\n#ifndef vsprintf_P\n#define vsprintf_P(s, ...) ((s),__VA_ARGS__)\n#endif\n#ifndef vsnprintf_P\n#define vsnprintf_P(s, n, ...) ((s), (n),__VA_ARGS__)\n#endif\n#ifndef fprintf_P\n#define fprintf_P(s, ...) ((s), __VA_ARGS__)\n#endif\n\n#ifndef pgm_read_byte\n#define pgm_read_byte(addr) (*(const unsigned char *)(addr))\n#endif\n#ifndef pgm_read_word\n#define pgm_read_word(addr) (*(const unsigned short *)(addr))\n#endif\n#ifndef pgm_read_dword\n#define pgm_read_dword(addr) (*(const unsigned long *)(addr))\n#endif\n#ifndef pgm_read_float\n#define pgm_read_float(addr) (*(const float *)(addr))\n#endif\n\n#ifndef pgm_read_byte_near\n#define pgm_read_byte_near(addr) pgm_read_byte(addr)\n#endif\n#ifndef pgm_read_word_near\n#define pgm_read_word_near(addr) pgm_read_word(addr)\n#endif\n#ifndef pgm_read_dword_near\n#define pgm_read_dword_near(addr) pgm_read_dword(addr)\n#endif\n#ifndef pgm_read_float_near\n#define pgm_read_float_near(addr) pgm_read_float(addr)\n#endif\n#ifndef pgm_read_byte_far\n#define pgm_read_byte_far(addr) pgm_read_byte(addr)\n#endif\n#ifndef pgm_read_word_far\n#define pgm_read_word_far(addr) pgm_read_word(addr)\n#endif\n#ifndef pgm_read_dword_far\n#define pgm_read_dword_far(addr) pgm_read_dword(addr)\n#endif\n#ifndef pgm_read_float_far\n#define pgm_read_float_far(addr) pgm_read_float(addr)\n#endif\n\n#ifndef pgm_read_pointer\n#define pgm_read_pointer\n#endif\n#endif\n"
  },
  {
    "path": "lib/usbhost/USB_Host_Shield_2.0/xboxEnums.h",
    "content": "/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved.\n\n This software may be distributed and modified under the terms of the GNU\n General Public License version 2 (GPL2) as published by the Free Software\n Foundation and appearing in the file GPL2.TXT included in the packaging of\n this file. Please note that GPL2 Section 2[b] requires that all works based\n on this software must also be made publicly available under the terms of\n the GPL2 (\"Copyleft\").\n\n Contact information\n -------------------\n\n Kristian Lauszus, TKJ Electronics\n Web      :  http://www.tkjelectronics.com\n e-mail   :  kristianl@tkjelectronics.com\n */\n\n#ifndef _xboxenums_h\n#define _xboxenums_h\n\n#include \"controllerEnums.h\"\n\n/** Enum used to set special LED modes supported by the Xbox controller. */\nenum LEDModeEnum {\n        ROTATING = 0x0A,\n        FASTBLINK = 0x0B,\n        SLOWBLINK = 0x0C,\n        ALTERNATING = 0x0D,\n};\n\n/** Used to set the LEDs on the controllers */\nconst uint8_t XBOX_LEDS[] PROGMEM = {\n        0x00, // OFF\n        0x02, // LED1\n        0x03, // LED2\n        0x04, // LED3\n        0x05, // LED4\n        0x01, // ALL - Used to blink all LEDs\n};\n/** Buttons on the controllers */\nconst uint16_t XBOX_BUTTONS[] PROGMEM = {\n        0x0100, // UP\n        0x0800, // RIGHT\n        0x0200, // DOWN\n        0x0400, // LEFT\n\n        0x2000, // BACK\n        0x1000, // START\n        0x4000, // L3\n        0x8000, // R3\n\n        0, 0, // Skip L2 and R2 as these are analog buttons\n        0x0001, // L1\n        0x0002, // R1\n\n        0x0020, // B\n        0x0010, // A\n        0x0040, // X\n        0x0080, // Y\n\n        0x0004, // XBOX\n        0x0008, // SYNC\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/Arduino.h",
    "content": "#ifndef Arduino_h\n#define Arduino_h\n\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n\n#include <avr/pgmspace.h>\n#include <avr/io.h>\n#include <avr/interrupt.h>\n\n#include \"binary.h\"\n\n#ifdef __cplusplus\nextern \"C\"{\n#endif\n\n#define HIGH 0x1\n#define LOW  0x0\n\n#define INPUT 0x0\n#define OUTPUT 0x1\n#define INPUT_PULLUP 0x2\n\n#define true 0x1\n#define false 0x0\n\n#define PI 3.1415926535897932384626433832795\n#define HALF_PI 1.5707963267948966192313216916398\n#define TWO_PI 6.283185307179586476925286766559\n#define DEG_TO_RAD 0.017453292519943295769236907684886\n#define RAD_TO_DEG 57.295779513082320876798154814105\n\n#define SERIAL  0x0\n#define DISPLAY 0x1\n\n#define LSBFIRST 0\n#define MSBFIRST 1\n\n#define CHANGE 1\n#define FALLING 2\n#define RISING 3\n\n#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)\n#define DEFAULT 0\n#define EXTERNAL 1\n#define INTERNAL 2\n#else  \n#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1284P__) \n#define INTERNAL1V1 2\n#define INTERNAL2V56 3\n#else\n#define INTERNAL 3\n#endif\n#define DEFAULT 1\n#define EXTERNAL 0\n#endif\n\n// undefine stdlib's abs if encountered\n#ifdef abs\n#undef abs\n#endif\n\n#define min(a,b) ((a)<(b)?(a):(b))\n#define max(a,b) ((a)>(b)?(a):(b))\n#define abs(x) ((x)>0?(x):-(x))\n#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))\n#define round(x)     ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))\n#define radians(deg) ((deg)*DEG_TO_RAD)\n#define degrees(rad) ((rad)*RAD_TO_DEG)\n#define sq(x) ((x)*(x))\n\n#define interrupts() sei()\n#define noInterrupts() cli()\n\n#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )\n#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )\n#define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() )\n\n#define lowByte(w) ((uint8_t) ((w) & 0xff))\n#define highByte(w) ((uint8_t) ((w) >> 8))\n\n#define bitRead(value, bit) (((value) >> (bit)) & 0x01)\n#define bitSet(value, bit) ((value) |= (1UL << (bit)))\n#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))\n#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))\n\n\ntypedef unsigned int word;\n\n#define bit(b) (1UL << (b))\n\ntypedef uint8_t boolean;\ntypedef uint8_t byte;\n\nvoid init(void);\n\nvoid pinMode(uint8_t, uint8_t);\nvoid digitalWrite(uint8_t, uint8_t);\nint digitalRead(uint8_t);\nint analogRead(uint8_t);\nvoid analogReference(uint8_t mode);\nvoid analogWrite(uint8_t, int);\n\nunsigned long millis(void);\nunsigned long micros(void);\nvoid delay(unsigned long);\nvoid delayMicroseconds(unsigned int us);\nunsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout);\n\nvoid shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val);\nuint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder);\n\nvoid attachInterrupt(uint8_t, void (*)(void), int mode);\nvoid detachInterrupt(uint8_t);\n\nvoid setup(void);\nvoid loop(void);\n\n// Get the bit location within the hardware port of the given virtual pin.\n// This comes from the pins_*.c file for the active board configuration.\n\n#define analogInPinToBit(P) (P)\n\n// On the ATmega1280, the addresses of some of the port registers are\n// greater than 255, so we can't store them in uint8_t's.\nextern const uint16_t PROGMEM port_to_mode_PGM[];\nextern const uint16_t PROGMEM port_to_input_PGM[];\nextern const uint16_t PROGMEM port_to_output_PGM[];\n\nextern const uint8_t PROGMEM digital_pin_to_port_PGM[];\n// extern const uint8_t PROGMEM digital_pin_to_bit_PGM[];\nextern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[];\nextern const uint8_t PROGMEM digital_pin_to_timer_PGM[];\n\n// Get the bit location within the hardware port of the given virtual pin.\n// This comes from the pins_*.c file for the active board configuration.\n// \n// These perform slightly better as macros compared to inline functions\n//\n#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )\n#define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )\n#define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) )\n#define analogInPinToBit(P) (P)\n#define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) )\n#define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) )\n#define portModeRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_mode_PGM + (P))) )\n\n#define NOT_A_PIN 0\n#define NOT_A_PORT 0\n\n#ifdef ARDUINO_MAIN\n#define PA 1\n#define PB 2\n#define PC 3\n#define PD 4\n#define PE 5\n#define PF 6\n#define PG 7\n#define PH 8\n#define PJ 10\n#define PK 11\n#define PL 12\n#endif\n\n#define NOT_ON_TIMER 0\n#define TIMER0A 1\n#define TIMER0B 2\n#define TIMER1A 3\n#define TIMER1B 4\n#define TIMER2  5\n#define TIMER2A 6\n#define TIMER2B 7\n\n#define TIMER3A 8\n#define TIMER3B 9\n#define TIMER3C 10\n#define TIMER4A 11\n#define TIMER4B 12\n#define TIMER4C 13\n#define TIMER4D 14\t\n#define TIMER5A 15\n#define TIMER5B 16\n#define TIMER5C 17\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\n#ifdef __cplusplus\n#include \"WCharacter.h\"\n#include \"WString.h\"\n#include \"HardwareSerial.h\"\n\nuint16_t makeWord(uint16_t w);\nuint16_t makeWord(byte h, byte l);\n\n#define word(...) makeWord(__VA_ARGS__)\n\nunsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);\n\nvoid tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0);\nvoid noTone(uint8_t _pin);\n\n// WMath prototypes\nlong random(long);\nlong random(long, long);\nvoid randomSeed(unsigned int);\nlong map(long, long, long, long, long);\n\n#endif\n\n#include \"pins_arduino.h\"\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/CDC.cpp",
    "content": "\n\n/* Copyright (c) 2011, Peter Barrett  \n**  \n** Permission to use, copy, modify, and/or distribute this software for  \n** any purpose with or without fee is hereby granted, provided that the  \n** above copyright notice and this permission notice appear in all copies.  \n** \n** THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL  \n** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED  \n** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR  \n** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES  \n** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,  \n** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  \n** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS  \n** SOFTWARE.  \n*/\n\n#include \"Platform.h\"\n#include \"USBAPI.h\"\n#include <avr/wdt.h>\n\n#if defined(USBCON)\n#ifdef CDC_ENABLED\n\n#if (RAMEND < 1000)\n#define SERIAL_BUFFER_SIZE 16\n#else\n#define SERIAL_BUFFER_SIZE 64\n#endif\n\nstruct ring_buffer\n{\n\tunsigned char buffer[SERIAL_BUFFER_SIZE];\n\tvolatile int head;\n\tvolatile int tail;\n};\n\nring_buffer cdc_rx_buffer = { { 0 }, 0, 0};\n\ntypedef struct\n{\n\tu32\tdwDTERate;\n\tu8\tbCharFormat;\n\tu8 \tbParityType;\n\tu8 \tbDataBits;\n\tu8\tlineState;\n} LineInfo;\n\nstatic volatile LineInfo _usbLineInfo = { 57600, 0x00, 0x00, 0x00, 0x00 };\n\n#define WEAK __attribute__ ((weak))\n\nextern const CDCDescriptor _cdcInterface PROGMEM;\nconst CDCDescriptor _cdcInterface =\n{\n\tD_IAD(0,2,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,1),\n\n\t//\tCDC communication interface\n\tD_INTERFACE(CDC_ACM_INTERFACE,1,CDC_COMMUNICATION_INTERFACE_CLASS,CDC_ABSTRACT_CONTROL_MODEL,0),\n\tD_CDCCS(CDC_HEADER,0x10,0x01),\t\t\t\t\t\t\t\t// Header (1.10 bcd)\n\tD_CDCCS(CDC_CALL_MANAGEMENT,1,1),\t\t\t\t\t\t\t// Device handles call management (not)\n\tD_CDCCS4(CDC_ABSTRACT_CONTROL_MANAGEMENT,6),\t\t\t\t// SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE supported\n\tD_CDCCS(CDC_UNION,CDC_ACM_INTERFACE,CDC_DATA_INTERFACE),\t// Communication interface is master, data interface is slave 0\n\tD_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_ACM),USB_ENDPOINT_TYPE_INTERRUPT,0x10,0x40),\n\n\t//\tCDC data interface\n\tD_INTERFACE(CDC_DATA_INTERFACE,2,CDC_DATA_INTERFACE_CLASS,0,0),\n\tD_ENDPOINT(USB_ENDPOINT_OUT(CDC_ENDPOINT_OUT),USB_ENDPOINT_TYPE_BULK,0x40,0),\n\tD_ENDPOINT(USB_ENDPOINT_IN (CDC_ENDPOINT_IN ),USB_ENDPOINT_TYPE_BULK,0x40,0)\n};\n\nint WEAK CDC_GetInterface(u8* interfaceNum)\n{\n\tinterfaceNum[0] += 2;\t// uses 2\n\treturn USB_SendControl(TRANSFER_PGM,&_cdcInterface,sizeof(_cdcInterface));\n}\n\nbool WEAK CDC_Setup(Setup& setup)\n{\n\tu8 r = setup.bRequest;\n\tu8 requestType = setup.bmRequestType;\n\n\tif (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType)\n\t{\n\t\tif (CDC_GET_LINE_CODING == r)\n\t\t{\n\t\t\tUSB_SendControl(0,(void*)&_usbLineInfo,7);\n\t\t\treturn true;\n\t\t}\n\t}\n\n\tif (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType)\n\t{\n\t\tif (CDC_SET_LINE_CODING == r)\n\t\t{\n\t\t\tUSB_RecvControl((void*)&_usbLineInfo,7);\n\t\t\treturn true;\n\t\t}\n\n\t\tif (CDC_SET_CONTROL_LINE_STATE == r)\n\t\t{\n\t\t\t_usbLineInfo.lineState = setup.wValueL;\n\n\t\t\t// auto-reset into the bootloader is triggered when the port, already \n\t\t\t// open at 1200 bps, is closed.  this is the signal to start the watchdog\n\t\t\t// with a relatively long period so it can finish housekeeping tasks\n\t\t\t// like servicing endpoints before the sketch ends\n\t\t\tif (1200 == _usbLineInfo.dwDTERate) {\n\t\t\t\t// We check DTR state to determine if host port is open (bit 0 of lineState).\n\t\t\t\tif ((_usbLineInfo.lineState & 0x01) == 0) {\n\t\t\t\t\t*(uint16_t *)0x0800 = 0x7777;\n\t\t\t\t\twdt_enable(WDTO_120MS);\n\t\t\t\t} else {\n\t\t\t\t\t// Most OSs do some intermediate steps when configuring ports and DTR can\n\t\t\t\t\t// twiggle more than once before stabilizing.\n\t\t\t\t\t// To avoid spurious resets we set the watchdog to 250ms and eventually\n\t\t\t\t\t// cancel if DTR goes back high.\n\t\n\t\t\t\t\twdt_disable();\n\t\t\t\t\twdt_reset();\n\t\t\t\t\t*(uint16_t *)0x0800 = 0x0;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n\nint _serialPeek = -1;\nvoid Serial_::begin(uint16_t baud_count)\n{\n}\n\nvoid Serial_::end(void)\n{\n}\n\nvoid Serial_::accept(void) \n{\n\tring_buffer *buffer = &cdc_rx_buffer;\n\tint c = USB_Recv(CDC_RX); \n\tint i = (unsigned int)(buffer->head+1) % SERIAL_BUFFER_SIZE;\n\t\n\t// if we should be storing the received character into the location\n\t// just before the tail (meaning that the head would advance to the\n\t// current location of the tail), we're about to overflow the buffer\n\t// and so we don't write the character or advance the head.\n\tif (i != buffer->tail) {\n\t\tbuffer->buffer[buffer->head] = c;\n\t\tbuffer->head = i;\n\t}\n}\n\nint Serial_::available(void)\n{\n\tring_buffer *buffer = &cdc_rx_buffer;\n\treturn (unsigned int)(SERIAL_BUFFER_SIZE + buffer->head - buffer->tail) % SERIAL_BUFFER_SIZE;\n}\n\nint Serial_::peek(void)\n{\n\tring_buffer *buffer = &cdc_rx_buffer;\n\tif (buffer->head == buffer->tail) {\n\t\treturn -1;\n\t} else {\n\t\treturn buffer->buffer[buffer->tail];\n\t}\n}\n\nint Serial_::read(void)\n{\n\tring_buffer *buffer = &cdc_rx_buffer;\n\t// if the head isn't ahead of the tail, we don't have any characters\n\tif (buffer->head == buffer->tail) {\n\t\treturn -1;\n\t} else {\n\t\tunsigned char c = buffer->buffer[buffer->tail];\n\t\tbuffer->tail = (unsigned int)(buffer->tail + 1) % SERIAL_BUFFER_SIZE;\n\t\treturn c;\n\t}\t\n}\n\nvoid Serial_::flush(void)\n{\n\tUSB_Flush(CDC_TX);\n}\n\nsize_t Serial_::write(uint8_t c)\n{\n\t/* only try to send bytes if the high-level CDC connection itself \n\t is open (not just the pipe) - the OS should set lineState when the port\n\t is opened and clear lineState when the port is closed.\n\t bytes sent before the user opens the connection or after\n\t the connection is closed are lost - just like with a UART. */\n\t\n\t// TODO - ZE - check behavior on different OSes and test what happens if an\n\t// open connection isn't broken cleanly (cable is yanked out, host dies\n\t// or locks up, or host virtual serial port hangs)\n\tif (_usbLineInfo.lineState > 0)\t{\n\t\tint r = USB_Send(CDC_TX,&c,1);\n\t\tif (r > 0) {\n\t\t\treturn r;\n\t\t} else {\n\t\t\tsetWriteError();\n\t\t\treturn 0;\n\t\t}\n\t}\n\tsetWriteError();\n\treturn 0;\n}\n\n// This operator is a convenient way for a sketch to check whether the\n// port has actually been configured and opened by the host (as opposed\n// to just being connected to the host).  It can be used, for example, in \n// setup() before printing to ensure that an application on the host is\n// actually ready to receive and display the data.\n// We add a short delay before returning to fix a bug observed by Federico\n// where the port is configured (lineState != 0) but not quite opened.\nSerial_::operator bool() {\n\tbool result = false;\n\tif (_usbLineInfo.lineState > 0) \n\t\tresult = true;\n\tdelay(10);\n\treturn result;\n}\n\nSerial_ Serial;\n\n#endif\n#endif /* if defined(USBCON) */\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/Client.h",
    "content": "#ifndef client_h\n#define client_h\n#include \"Print.h\"\n#include \"Stream.h\"\n#include \"IPAddress.h\"\n\nclass Client : public Stream {\n\npublic:\n  virtual int connect(IPAddress ip, uint16_t port) =0;\n  virtual int connect(const char *host, uint16_t port) =0;\n  virtual size_t write(uint8_t) =0;\n  virtual size_t write(const uint8_t *buf, size_t size) =0;\n  virtual int available() = 0;\n  virtual int read() = 0;\n  virtual int read(uint8_t *buf, size_t size) = 0;\n  virtual int peek() = 0;\n  virtual void flush() = 0;\n  virtual void stop() = 0;\n  virtual uint8_t connected() = 0;\n  virtual operator bool() = 0;\nprotected:\n  uint8_t* rawIPAddress(IPAddress& addr) { return addr.raw_address(); };\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/HID.cpp",
    "content": "\n\n/* Copyright (c) 2011, Peter Barrett  \n**  \n** Permission to use, copy, modify, and/or distribute this software for  \n** any purpose with or without fee is hereby granted, provided that the  \n** above copyright notice and this permission notice appear in all copies.  \n** \n** THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL  \n** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED  \n** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR  \n** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES  \n** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,  \n** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  \n** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS  \n** SOFTWARE.  \n*/\n\n#include \"Platform.h\"\n#include \"USBAPI.h\"\n#include \"USBDesc.h\"\n\n#if defined(USBCON)\n#ifdef HID_ENABLED\n\n//#define RAWHID_ENABLED\n\n//\tSingletons for mouse and keyboard\n\nMouse_ Mouse;\nKeyboard_ Keyboard;\n\n//================================================================================\n//================================================================================\n\n//\tHID report descriptor\n\n#define LSB(_x) ((_x) & 0xFF)\n#define MSB(_x) ((_x) >> 8)\n\n#define RAWHID_USAGE_PAGE\t0xFFC0\n#define RAWHID_USAGE\t\t0x0C00\n#define RAWHID_TX_SIZE 64\n#define RAWHID_RX_SIZE 64\n\nextern const u8 _hidReportDescriptor[] PROGMEM;\nconst u8 _hidReportDescriptor[] = {\n\t\n\t//\tMouse\n    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)\t// 54\n    0x09, 0x02,                    // USAGE (Mouse)\n    0xa1, 0x01,                    // COLLECTION (Application)\n    0x09, 0x01,                    //   USAGE (Pointer)\n    0xa1, 0x00,                    //   COLLECTION (Physical)\n    0x85, 0x01,                    //     REPORT_ID (1)\n    0x05, 0x09,                    //     USAGE_PAGE (Button)\n    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)\n    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)\n    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)\n    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)\n    0x95, 0x03,                    //     REPORT_COUNT (3)\n    0x75, 0x01,                    //     REPORT_SIZE (1)\n    0x81, 0x02,                    //     INPUT (Data,Var,Abs)\n    0x95, 0x01,                    //     REPORT_COUNT (1)\n    0x75, 0x05,                    //     REPORT_SIZE (5)\n    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)\n    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)\n    0x09, 0x30,                    //     USAGE (X)\n    0x09, 0x31,                    //     USAGE (Y)\n    0x09, 0x38,                    //     USAGE (Wheel)\n    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)\n    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)\n    0x75, 0x08,                    //     REPORT_SIZE (8)\n    0x95, 0x03,                    //     REPORT_COUNT (3)\n    0x81, 0x06,                    //     INPUT (Data,Var,Rel)\n    0xc0,                          //   END_COLLECTION\n    0xc0,                          // END_COLLECTION\n\n\t//\tKeyboard\n    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)\t// 47\n    0x09, 0x06,                    // USAGE (Keyboard)\n    0xa1, 0x01,                    // COLLECTION (Application)\n    0x85, 0x02,                    //   REPORT_ID (2)\n    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)\n   \n\t0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)\n    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)\n    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)\n    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)\n    0x75, 0x01,                    //   REPORT_SIZE (1)\n    \n\t0x95, 0x08,                    //   REPORT_COUNT (8)\n    0x81, 0x02,                    //   INPUT (Data,Var,Abs)\n    0x95, 0x01,                    //   REPORT_COUNT (1)\n    0x75, 0x08,                    //   REPORT_SIZE (8)\n    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)\n    \n\t0x95, 0x06,                    //   REPORT_COUNT (6)\n    0x75, 0x08,                    //   REPORT_SIZE (8)\n    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)\n    0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)\n    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)\n    \n\t0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))\n    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)\n    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)\n    0xc0,                          // END_COLLECTION\n\n#if RAWHID_ENABLED\n\t//\tRAW HID\n\t0x06, LSB(RAWHID_USAGE_PAGE), MSB(RAWHID_USAGE_PAGE),\t// 30\n\t0x0A, LSB(RAWHID_USAGE), MSB(RAWHID_USAGE),\n\n\t0xA1, 0x01,\t\t\t\t// Collection 0x01\n    0x85, 0x03,             // REPORT_ID (3)\n\t0x75, 0x08,\t\t\t\t// report size = 8 bits\n\t0x15, 0x00,\t\t\t\t// logical minimum = 0\n\t0x26, 0xFF, 0x00,\t\t// logical maximum = 255\n\n\t0x95, 64,\t\t\t\t// report count TX\n\t0x09, 0x01,\t\t\t\t// usage\n\t0x81, 0x02,\t\t\t\t// Input (array)\n\n\t0x95, 64,\t\t\t\t// report count RX\n\t0x09, 0x02,\t\t\t\t// usage\n\t0x91, 0x02,\t\t\t\t// Output (array)\n\t0xC0\t\t\t\t\t// end collection\n#endif\n};\n\nextern const HIDDescriptor _hidInterface PROGMEM;\nconst HIDDescriptor _hidInterface =\n{\n\tD_INTERFACE(HID_INTERFACE,1,3,0,0),\n\tD_HIDREPORT(sizeof(_hidReportDescriptor)),\n\tD_ENDPOINT(USB_ENDPOINT_IN (HID_ENDPOINT_INT),USB_ENDPOINT_TYPE_INTERRUPT,0x40,0x01)\n};\n\n//================================================================================\n//================================================================================\n//\tDriver\n\nu8 _hid_protocol = 1;\nu8 _hid_idle = 1;\n\n#define WEAK __attribute__ ((weak))\n\nint WEAK HID_GetInterface(u8* interfaceNum)\n{\n\tinterfaceNum[0] += 1;\t// uses 1\n\treturn USB_SendControl(TRANSFER_PGM,&_hidInterface,sizeof(_hidInterface));\n}\n\nint WEAK HID_GetDescriptor(int i)\n{\n\treturn USB_SendControl(TRANSFER_PGM,_hidReportDescriptor,sizeof(_hidReportDescriptor));\n}\n\nvoid WEAK HID_SendReport(u8 id, const void* data, int len)\n{\n\tUSB_Send(HID_TX, &id, 1);\n\tUSB_Send(HID_TX | TRANSFER_RELEASE,data,len);\n}\n\nbool WEAK HID_Setup(Setup& setup)\n{\n\tu8 r = setup.bRequest;\n\tu8 requestType = setup.bmRequestType;\n\tif (REQUEST_DEVICETOHOST_CLASS_INTERFACE == requestType)\n\t{\n\t\tif (HID_GET_REPORT == r)\n\t\t{\n\t\t\t//HID_GetReport();\n\t\t\treturn true;\n\t\t}\n\t\tif (HID_GET_PROTOCOL == r)\n\t\t{\n\t\t\t//Send8(_hid_protocol);\t// TODO\n\t\t\treturn true;\n\t\t}\n\t}\n\t\n\tif (REQUEST_HOSTTODEVICE_CLASS_INTERFACE == requestType)\n\t{\n\t\tif (HID_SET_PROTOCOL == r)\n\t\t{\n\t\t\t_hid_protocol = setup.wValueL;\n\t\t\treturn true;\n\t\t}\n\n\t\tif (HID_SET_IDLE == r)\n\t\t{\n\t\t\t_hid_idle = setup.wValueL;\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n}\n\n//================================================================================\n//================================================================================\n//\tMouse\n\nMouse_::Mouse_(void) : _buttons(0)\n{\n}\n\nvoid Mouse_::begin(void) \n{\n}\n\nvoid Mouse_::end(void) \n{\n}\n\nvoid Mouse_::click(uint8_t b)\n{\n\t_buttons = b;\n\tmove(0,0,0);\n\t_buttons = 0;\n\tmove(0,0,0);\n}\n\nvoid Mouse_::move(signed char x, signed char y, signed char wheel)\n{\n\tu8 m[4];\n\tm[0] = _buttons;\n\tm[1] = x;\n\tm[2] = y;\n\tm[3] = wheel;\n\tHID_SendReport(1,m,4);\n}\n\nvoid Mouse_::buttons(uint8_t b)\n{\n\tif (b != _buttons)\n\t{\n\t\t_buttons = b;\n\t\tmove(0,0,0);\n\t}\n}\n\nvoid Mouse_::press(uint8_t b) \n{\n\tbuttons(_buttons | b);\n}\n\nvoid Mouse_::release(uint8_t b)\n{\n\tbuttons(_buttons & ~b);\n}\n\nbool Mouse_::isPressed(uint8_t b)\n{\n\tif ((b & _buttons) > 0) \n\t\treturn true;\n\treturn false;\n}\n\n//================================================================================\n//================================================================================\n//\tKeyboard\n\nKeyboard_::Keyboard_(void) \n{\n}\n\nvoid Keyboard_::begin(void) \n{\n}\n\nvoid Keyboard_::end(void) \n{\n}\n\nvoid Keyboard_::sendReport(KeyReport* keys)\n{\n\tHID_SendReport(2,keys,sizeof(KeyReport));\n}\n\nextern\nconst uint8_t _asciimap[128] PROGMEM;\n\n#define SHIFT 0x80\nconst uint8_t _asciimap[128] =\n{\n\t0x00,             // NUL\n\t0x00,             // SOH\n\t0x00,             // STX\n\t0x00,             // ETX\n\t0x00,             // EOT\n\t0x00,             // ENQ\n\t0x00,             // ACK  \n\t0x00,             // BEL\n\t0x2a,\t\t\t// BS\tBackspace\n\t0x2b,\t\t\t// TAB\tTab\n\t0x28,\t\t\t// LF\tEnter\n\t0x00,             // VT \n\t0x00,             // FF \n\t0x00,             // CR \n\t0x00,             // SO \n\t0x00,             // SI \n\t0x00,             // DEL\n\t0x00,             // DC1\n\t0x00,             // DC2\n\t0x00,             // DC3\n\t0x00,             // DC4\n\t0x00,             // NAK\n\t0x00,             // SYN\n\t0x00,             // ETB\n\t0x00,             // CAN\n\t0x00,             // EM \n\t0x00,             // SUB\n\t0x00,             // ESC\n\t0x00,             // FS \n\t0x00,             // GS \n\t0x00,             // RS \n\t0x00,             // US \n\n\t0x2c,\t\t   //  ' '\n\t0x1e|SHIFT,\t   // !\n\t0x34|SHIFT,\t   // \"\n\t0x20|SHIFT,    // #\n\t0x21|SHIFT,    // $\n\t0x22|SHIFT,    // %\n\t0x24|SHIFT,    // &\n\t0x34,          // '\n\t0x26|SHIFT,    // (\n\t0x27|SHIFT,    // )\n\t0x25|SHIFT,    // *\n\t0x2e|SHIFT,    // +\n\t0x36,          // ,\n\t0x2d,          // -\n\t0x37,          // .\n\t0x38,          // /\n\t0x27,          // 0\n\t0x1e,          // 1\n\t0x1f,          // 2\n\t0x20,          // 3\n\t0x21,          // 4\n\t0x22,          // 5\n\t0x23,          // 6\n\t0x24,          // 7\n\t0x25,          // 8\n\t0x26,          // 9\n\t0x33|SHIFT,      // :\n\t0x33,          // ;\n\t0x36|SHIFT,      // <\n\t0x2e,          // =\n\t0x37|SHIFT,      // >\n\t0x38|SHIFT,      // ?\n\t0x1f|SHIFT,      // @\n\t0x04|SHIFT,      // A\n\t0x05|SHIFT,      // B\n\t0x06|SHIFT,      // C\n\t0x07|SHIFT,      // D\n\t0x08|SHIFT,      // E\n\t0x09|SHIFT,      // F\n\t0x0a|SHIFT,      // G\n\t0x0b|SHIFT,      // H\n\t0x0c|SHIFT,      // I\n\t0x0d|SHIFT,      // J\n\t0x0e|SHIFT,      // K\n\t0x0f|SHIFT,      // L\n\t0x10|SHIFT,      // M\n\t0x11|SHIFT,      // N\n\t0x12|SHIFT,      // O\n\t0x13|SHIFT,      // P\n\t0x14|SHIFT,      // Q\n\t0x15|SHIFT,      // R\n\t0x16|SHIFT,      // S\n\t0x17|SHIFT,      // T\n\t0x18|SHIFT,      // U\n\t0x19|SHIFT,      // V\n\t0x1a|SHIFT,      // W\n\t0x1b|SHIFT,      // X\n\t0x1c|SHIFT,      // Y\n\t0x1d|SHIFT,      // Z\n\t0x2f,          // [\n\t0x31,          // bslash\n\t0x30,          // ]\n\t0x23|SHIFT,    // ^\n\t0x2d|SHIFT,    // _\n\t0x35,          // `\n\t0x04,          // a\n\t0x05,          // b\n\t0x06,          // c\n\t0x07,          // d\n\t0x08,          // e\n\t0x09,          // f\n\t0x0a,          // g\n\t0x0b,          // h\n\t0x0c,          // i\n\t0x0d,          // j\n\t0x0e,          // k\n\t0x0f,          // l\n\t0x10,          // m\n\t0x11,          // n\n\t0x12,          // o\n\t0x13,          // p\n\t0x14,          // q\n\t0x15,          // r\n\t0x16,          // s\n\t0x17,          // t\n\t0x18,          // u\n\t0x19,          // v\n\t0x1a,          // w\n\t0x1b,          // x\n\t0x1c,          // y\n\t0x1d,          // z\n\t0x2f|SHIFT,    // \n\t0x31|SHIFT,    // |\n\t0x30|SHIFT,    // }\n\t0x35|SHIFT,    // ~\n\t0\t\t\t\t// DEL\n};\n\nuint8_t USBPutChar(uint8_t c);\n\n// press() adds the specified key (printing, non-printing, or modifier)\n// to the persistent key report and sends the report.  Because of the way \n// USB HID works, the host acts like the key remains pressed until we \n// call release(), releaseAll(), or otherwise clear the report and resend.\nsize_t Keyboard_::press(uint8_t k) \n{\n\tuint8_t i;\n\tif (k >= 136) {\t\t\t// it's a non-printing key (not a modifier)\n\t\tk = k - 136;\n\t} else if (k >= 128) {\t// it's a modifier key\n\t\t_keyReport.modifiers |= (1<<(k-128));\n\t\tk = 0;\n\t} else {\t\t\t\t// it's a printing key\n\t\tk = pgm_read_byte(_asciimap + k);\n\t\tif (!k) {\n\t\t\tsetWriteError();\n\t\t\treturn 0;\n\t\t}\n\t\tif (k & 0x80) {\t\t\t\t\t\t// it's a capital letter or other character reached with shift\n\t\t\t_keyReport.modifiers |= 0x02;\t// the left shift modifier\n\t\t\tk &= 0x7F;\n\t\t}\n\t}\n\t\n\t// Add k to the key report only if it's not already present\n\t// and if there is an empty slot.\n\tif (_keyReport.keys[0] != k && _keyReport.keys[1] != k && \n\t\t_keyReport.keys[2] != k && _keyReport.keys[3] != k &&\n\t\t_keyReport.keys[4] != k && _keyReport.keys[5] != k) {\n\t\t\n\t\tfor (i=0; i<6; i++) {\n\t\t\tif (_keyReport.keys[i] == 0x00) {\n\t\t\t\t_keyReport.keys[i] = k;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (i == 6) {\n\t\t\tsetWriteError();\n\t\t\treturn 0;\n\t\t}\t\n\t}\n\tsendReport(&_keyReport);\n\treturn 1;\n}\n\n// release() takes the specified key out of the persistent key report and\n// sends the report.  This tells the OS the key is no longer pressed and that\n// it shouldn't be repeated any more.\nsize_t Keyboard_::release(uint8_t k) \n{\n\tuint8_t i;\n\tif (k >= 136) {\t\t\t// it's a non-printing key (not a modifier)\n\t\tk = k - 136;\n\t} else if (k >= 128) {\t// it's a modifier key\n\t\t_keyReport.modifiers &= ~(1<<(k-128));\n\t\tk = 0;\n\t} else {\t\t\t\t// it's a printing key\n\t\tk = pgm_read_byte(_asciimap + k);\n\t\tif (!k) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (k & 0x80) {\t\t\t\t\t\t\t// it's a capital letter or other character reached with shift\n\t\t\t_keyReport.modifiers &= ~(0x02);\t// the left shift modifier\n\t\t\tk &= 0x7F;\n\t\t}\n\t}\n\t\n\t// Test the key report to see if k is present.  Clear it if it exists.\n\t// Check all positions in case the key is present more than once (which it shouldn't be)\n\tfor (i=0; i<6; i++) {\n\t\tif (0 != k && _keyReport.keys[i] == k) {\n\t\t\t_keyReport.keys[i] = 0x00;\n\t\t}\n\t}\n\n\tsendReport(&_keyReport);\n\treturn 1;\n}\n\nvoid Keyboard_::releaseAll(void)\n{\n\t_keyReport.keys[0] = 0;\n\t_keyReport.keys[1] = 0;\t\n\t_keyReport.keys[2] = 0;\n\t_keyReport.keys[3] = 0;\t\n\t_keyReport.keys[4] = 0;\n\t_keyReport.keys[5] = 0;\t\n\t_keyReport.modifiers = 0;\n\tsendReport(&_keyReport);\n}\n\nsize_t Keyboard_::write(uint8_t c)\n{\t\n\tuint8_t p = press(c);\t\t// Keydown\n\tuint8_t r = release(c);\t\t// Keyup\n\treturn (p);\t\t\t\t\t// just return the result of press() since release() almost always returns 1\n}\n\n#endif\n\n#endif /* if defined(USBCON) */"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/HardwareSerial.cpp",
    "content": "/*\n  HardwareSerial.cpp - Hardware serial library for Wiring\n  Copyright (c) 2006 Nicholas Zambetti.  All right reserved.\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General Public\n  License along with this library; if not, write to the Free Software\n  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n  \n  Modified 23 November 2006 by David A. Mellis\n  Modified 28 September 2010 by Mark Sproul\n*/\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <inttypes.h>\n#include \"Arduino.h\"\n#include \"wiring_private.h\"\n\n// this next line disables the entire HardwareSerial.cpp, \n// this is so I can support Attiny series and any other chip without a uart\n#if defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H)\n\n#include \"HardwareSerial.h\"\n\n// Define constants and variables for buffering incoming serial data.  We're\n// using a ring buffer (I think), in which head is the index of the location\n// to which to write the next incoming character and tail is the index of the\n// location from which to read.\n#if (RAMEND < 1000)\n  #define SERIAL_BUFFER_SIZE 16\n#else\n  #define SERIAL_BUFFER_SIZE 64\n#endif\n\nstruct ring_buffer\n{\n  unsigned char buffer[SERIAL_BUFFER_SIZE];\n  volatile unsigned int head;\n  volatile unsigned int tail;\n};\n\n#if defined(USBCON)\n  ring_buffer rx_buffer = { { 0 }, 0, 0};\n  ring_buffer tx_buffer = { { 0 }, 0, 0};\n#endif\n#if defined(UBRRH) || defined(UBRR0H)\n  ring_buffer rx_buffer  =  { { 0 }, 0, 0 };\n  ring_buffer tx_buffer  =  { { 0 }, 0, 0 };\n#endif\n#if defined(UBRR1H)\n  ring_buffer rx_buffer1  =  { { 0 }, 0, 0 };\n  ring_buffer tx_buffer1  =  { { 0 }, 0, 0 };\n#endif\n#if defined(UBRR2H)\n  ring_buffer rx_buffer2  =  { { 0 }, 0, 0 };\n  ring_buffer tx_buffer2  =  { { 0 }, 0, 0 };\n#endif\n#if defined(UBRR3H)\n  ring_buffer rx_buffer3  =  { { 0 }, 0, 0 };\n  ring_buffer tx_buffer3  =  { { 0 }, 0, 0 };\n#endif\n\ninline void store_char(unsigned char c, ring_buffer *buffer)\n{\n  int i = (unsigned int)(buffer->head + 1) % SERIAL_BUFFER_SIZE;\n\n  // if we should be storing the received character into the location\n  // just before the tail (meaning that the head would advance to the\n  // current location of the tail), we're about to overflow the buffer\n  // and so we don't write the character or advance the head.\n  if (i != buffer->tail) {\n    buffer->buffer[buffer->head] = c;\n    buffer->head = i;\n  }\n}\n\n#if !defined(USART0_RX_vect) && defined(USART1_RX_vect)\n// do nothing - on the 32u4 the first USART is USART1\n#else\n#if !defined(USART_RX_vect) && !defined(SIG_USART0_RECV) && \\\n    !defined(SIG_UART0_RECV) && !defined(USART0_RX_vect) && \\\n\t!defined(SIG_UART_RECV)\n  #error \"Don't know what the Data Received vector is called for the first UART\"\n#else\n  void serialEvent() __attribute__((weak));\n  void serialEvent() {}\n  #define serialEvent_implemented\n#if defined(USART_RX_vect)\n  SIGNAL(USART_RX_vect)\n#elif defined(SIG_USART0_RECV)\n  SIGNAL(SIG_USART0_RECV)\n#elif defined(SIG_UART0_RECV)\n  SIGNAL(SIG_UART0_RECV)\n#elif defined(USART0_RX_vect)\n  SIGNAL(USART0_RX_vect)\n#elif defined(SIG_UART_RECV)\n  SIGNAL(SIG_UART_RECV)\n#endif\n  {\n  #if defined(UDR0)\n    unsigned char c  =  UDR0;\n  #elif defined(UDR)\n    unsigned char c  =  UDR;\n  #else\n    #error UDR not defined\n  #endif\n    store_char(c, &rx_buffer);\n  }\n#endif\n#endif\n\n#if defined(USART1_RX_vect)\n  void serialEvent1() __attribute__((weak));\n  void serialEvent1() {}\n  #define serialEvent1_implemented\n  SIGNAL(USART1_RX_vect)\n  {\n    unsigned char c = UDR1;\n    store_char(c, &rx_buffer1);\n  }\n#elif defined(SIG_USART1_RECV)\n  #error SIG_USART1_RECV\n#endif\n\n#if defined(USART2_RX_vect) && defined(UDR2)\n  void serialEvent2() __attribute__((weak));\n  void serialEvent2() {}\n  #define serialEvent2_implemented\n  SIGNAL(USART2_RX_vect)\n  {\n    unsigned char c = UDR2;\n    store_char(c, &rx_buffer2);\n  }\n#elif defined(SIG_USART2_RECV)\n  #error SIG_USART2_RECV\n#endif\n\n#if defined(USART3_RX_vect) && defined(UDR3)\n  void serialEvent3() __attribute__((weak));\n  void serialEvent3() {}\n  #define serialEvent3_implemented\n  SIGNAL(USART3_RX_vect)\n  {\n    unsigned char c = UDR3;\n    store_char(c, &rx_buffer3);\n  }\n#elif defined(SIG_USART3_RECV)\n  #error SIG_USART3_RECV\n#endif\n\nvoid serialEventRun(void)\n{\n#ifdef serialEvent_implemented\n  if (Serial.available()) serialEvent();\n#endif\n#ifdef serialEvent1_implemented\n  if (Serial1.available()) serialEvent1();\n#endif\n#ifdef serialEvent2_implemented\n  if (Serial2.available()) serialEvent2();\n#endif\n#ifdef serialEvent3_implemented\n  if (Serial3.available()) serialEvent3();\n#endif\n}\n\n\n#if !defined(USART0_UDRE_vect) && defined(USART1_UDRE_vect)\n// do nothing - on the 32u4 the first USART is USART1\n#else\n#if !defined(UART0_UDRE_vect) && !defined(UART_UDRE_vect) && !defined(USART0_UDRE_vect) && !defined(USART_UDRE_vect)\n  #error \"Don't know what the Data Register Empty vector is called for the first UART\"\n#else\n#if defined(UART0_UDRE_vect)\nISR(UART0_UDRE_vect)\n#elif defined(UART_UDRE_vect)\nISR(UART_UDRE_vect)\n#elif defined(USART0_UDRE_vect)\nISR(USART0_UDRE_vect)\n#elif defined(USART_UDRE_vect)\nISR(USART_UDRE_vect)\n#endif\n{\n  if (tx_buffer.head == tx_buffer.tail) {\n\t// Buffer empty, so disable interrupts\n#if defined(UCSR0B)\n    cbi(UCSR0B, UDRIE0);\n#else\n    cbi(UCSRB, UDRIE);\n#endif\n  }\n  else {\n    // There is more data in the output buffer. Send the next byte\n    unsigned char c = tx_buffer.buffer[tx_buffer.tail];\n    tx_buffer.tail = (tx_buffer.tail + 1) % SERIAL_BUFFER_SIZE;\n\t\n  #if defined(UDR0)\n    UDR0 = c;\n  #elif defined(UDR)\n    UDR = c;\n  #else\n    #error UDR not defined\n  #endif\n  }\n}\n#endif\n#endif\n\n#ifdef USART1_UDRE_vect\nISR(USART1_UDRE_vect)\n{\n  if (tx_buffer1.head == tx_buffer1.tail) {\n\t// Buffer empty, so disable interrupts\n    cbi(UCSR1B, UDRIE1);\n  }\n  else {\n    // There is more data in the output buffer. Send the next byte\n    unsigned char c = tx_buffer1.buffer[tx_buffer1.tail];\n    tx_buffer1.tail = (tx_buffer1.tail + 1) % SERIAL_BUFFER_SIZE;\n\t\n    UDR1 = c;\n  }\n}\n#endif\n\n#ifdef USART2_UDRE_vect\nISR(USART2_UDRE_vect)\n{\n  if (tx_buffer2.head == tx_buffer2.tail) {\n\t// Buffer empty, so disable interrupts\n    cbi(UCSR2B, UDRIE2);\n  }\n  else {\n    // There is more data in the output buffer. Send the next byte\n    unsigned char c = tx_buffer2.buffer[tx_buffer2.tail];\n    tx_buffer2.tail = (tx_buffer2.tail + 1) % SERIAL_BUFFER_SIZE;\n\t\n    UDR2 = c;\n  }\n}\n#endif\n\n#ifdef USART3_UDRE_vect\nISR(USART3_UDRE_vect)\n{\n  if (tx_buffer3.head == tx_buffer3.tail) {\n\t// Buffer empty, so disable interrupts\n    cbi(UCSR3B, UDRIE3);\n  }\n  else {\n    // There is more data in the output buffer. Send the next byte\n    unsigned char c = tx_buffer3.buffer[tx_buffer3.tail];\n    tx_buffer3.tail = (tx_buffer3.tail + 1) % SERIAL_BUFFER_SIZE;\n\t\n    UDR3 = c;\n  }\n}\n#endif\n\n\n// Constructors ////////////////////////////////////////////////////////////////\n\nHardwareSerial::HardwareSerial(ring_buffer *rx_buffer, ring_buffer *tx_buffer,\n  volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,\n  volatile uint8_t *ucsra, volatile uint8_t *ucsrb,\n  volatile uint8_t *udr,\n  uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x)\n{\n  _rx_buffer = rx_buffer;\n  _tx_buffer = tx_buffer;\n  _ubrrh = ubrrh;\n  _ubrrl = ubrrl;\n  _ucsra = ucsra;\n  _ucsrb = ucsrb;\n  _udr = udr;\n  _rxen = rxen;\n  _txen = txen;\n  _rxcie = rxcie;\n  _udrie = udrie;\n  _u2x = u2x;\n}\n\n// Public Methods //////////////////////////////////////////////////////////////\n\nvoid HardwareSerial::begin(unsigned long baud)\n{\n  uint16_t baud_setting;\n  bool use_u2x = true;\n\n#if F_CPU == 16000000UL\n  // hardcoded exception for compatibility with the bootloader shipped\n  // with the Duemilanove and previous boards and the firmware on the 8U2\n  // on the Uno and Mega 2560.\n  if (baud == 57600) {\n    use_u2x = false;\n  }\n#endif\n\ntry_again:\n  \n  if (use_u2x) {\n    *_ucsra = 1 << _u2x;\n    baud_setting = (F_CPU / 4 / baud - 1) / 2;\n  } else {\n    *_ucsra = 0;\n    baud_setting = (F_CPU / 8 / baud - 1) / 2;\n  }\n  \n  if ((baud_setting > 4095) && use_u2x)\n  {\n    use_u2x = false;\n    goto try_again;\n  }\n\n  // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)\n  *_ubrrh = baud_setting >> 8;\n  *_ubrrl = baud_setting;\n\n  sbi(*_ucsrb, _rxen);\n  sbi(*_ucsrb, _txen);\n  sbi(*_ucsrb, _rxcie);\n  cbi(*_ucsrb, _udrie);\n}\n\nvoid HardwareSerial::end()\n{\n  // wait for transmission of outgoing data\n  while (_tx_buffer->head != _tx_buffer->tail)\n    ;\n\n  cbi(*_ucsrb, _rxen);\n  cbi(*_ucsrb, _txen);\n  cbi(*_ucsrb, _rxcie);  \n  cbi(*_ucsrb, _udrie);\n  \n  // clear any received data\n  _rx_buffer->head = _rx_buffer->tail;\n}\n\nint HardwareSerial::available(void)\n{\n  return (unsigned int)(SERIAL_BUFFER_SIZE + _rx_buffer->head - _rx_buffer->tail) % SERIAL_BUFFER_SIZE;\n}\n\nint HardwareSerial::peek(void)\n{\n  if (_rx_buffer->head == _rx_buffer->tail) {\n    return -1;\n  } else {\n    return _rx_buffer->buffer[_rx_buffer->tail];\n  }\n}\n\nint HardwareSerial::read(void)\n{\n  // if the head isn't ahead of the tail, we don't have any characters\n  if (_rx_buffer->head == _rx_buffer->tail) {\n    return -1;\n  } else {\n    unsigned char c = _rx_buffer->buffer[_rx_buffer->tail];\n    _rx_buffer->tail = (unsigned int)(_rx_buffer->tail + 1) % SERIAL_BUFFER_SIZE;\n    return c;\n  }\n}\n\nvoid HardwareSerial::flush()\n{\n  while (_tx_buffer->head != _tx_buffer->tail)\n    ;\n}\n\nsize_t HardwareSerial::write(uint8_t c)\n{\n  int i = (_tx_buffer->head + 1) % SERIAL_BUFFER_SIZE;\n\t\n  // If the output buffer is full, there's nothing for it other than to \n  // wait for the interrupt handler to empty it a bit\n  // ???: return 0 here instead?\n  while (i == _tx_buffer->tail)\n    ;\n\t\n  _tx_buffer->buffer[_tx_buffer->head] = c;\n  _tx_buffer->head = i;\n\t\n  sbi(*_ucsrb, _udrie);\n  \n  return 1;\n}\n\nHardwareSerial::operator bool() {\n\treturn true;\n}\n\n// Preinstantiate Objects //////////////////////////////////////////////////////\n\n#if defined(UBRRH) && defined(UBRRL)\n  HardwareSerial Serial(&rx_buffer, &tx_buffer, &UBRRH, &UBRRL, &UCSRA, &UCSRB, &UDR, RXEN, TXEN, RXCIE, UDRIE, U2X);\n#elif defined(UBRR0H) && defined(UBRR0L)\n  HardwareSerial Serial(&rx_buffer, &tx_buffer, &UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UDR0, RXEN0, TXEN0, RXCIE0, UDRIE0, U2X0);\n#elif defined(USBCON)\n  // do nothing - Serial object and buffers are initialized in CDC code\n#else\n  #error no serial port defined  (port 0)\n#endif\n\n#if defined(UBRR1H)\n  HardwareSerial Serial1(&rx_buffer1, &tx_buffer1, &UBRR1H, &UBRR1L, &UCSR1A, &UCSR1B, &UDR1, RXEN1, TXEN1, RXCIE1, UDRIE1, U2X1);\n#endif\n#if defined(UBRR2H)\n  HardwareSerial Serial2(&rx_buffer2, &tx_buffer2, &UBRR2H, &UBRR2L, &UCSR2A, &UCSR2B, &UDR2, RXEN2, TXEN2, RXCIE2, UDRIE2, U2X2);\n#endif\n#if defined(UBRR3H)\n  HardwareSerial Serial3(&rx_buffer3, &tx_buffer3, &UBRR3H, &UBRR3L, &UCSR3A, &UCSR3B, &UDR3, RXEN3, TXEN3, RXCIE3, UDRIE3, U2X3);\n#endif\n\n#endif // whole file\n\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/HardwareSerial.h",
    "content": "/*\n  HardwareSerial.h - Hardware serial library for Wiring\n  Copyright (c) 2006 Nicholas Zambetti.  All right reserved.\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General Public\n  License along with this library; if not, write to the Free Software\n  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n\n  Modified 28 September 2010 by Mark Sproul\n*/\n\n#ifndef HardwareSerial_h\n#define HardwareSerial_h\n\n#include <inttypes.h>\n\n#include \"Stream.h\"\n\nstruct ring_buffer;\n\nclass HardwareSerial : public Stream\n{\n  private:\n    ring_buffer *_rx_buffer;\n    ring_buffer *_tx_buffer;\n    volatile uint8_t *_ubrrh;\n    volatile uint8_t *_ubrrl;\n    volatile uint8_t *_ucsra;\n    volatile uint8_t *_ucsrb;\n    volatile uint8_t *_udr;\n    uint8_t _rxen;\n    uint8_t _txen;\n    uint8_t _rxcie;\n    uint8_t _udrie;\n    uint8_t _u2x;\n  public:\n    HardwareSerial(ring_buffer *rx_buffer, ring_buffer *tx_buffer,\n      volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,\n      volatile uint8_t *ucsra, volatile uint8_t *ucsrb,\n      volatile uint8_t *udr,\n      uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x);\n    void begin(unsigned long);\n    void end();\n    virtual int available(void);\n    virtual int peek(void);\n    virtual int read(void);\n    virtual void flush(void);\n    virtual size_t write(uint8_t);\n    using Print::write; // pull in write(str) and write(buf, size) from Print\n    operator bool();\n};\n\n#if defined(UBRRH) || defined(UBRR0H)\n  extern HardwareSerial Serial;\n#elif defined(USBCON)\n  #include \"USBAPI.h\"\n//  extern HardwareSerial Serial_;  \n#endif\n#if defined(UBRR1H)\n  extern HardwareSerial Serial1;\n#endif\n#if defined(UBRR2H)\n  extern HardwareSerial Serial2;\n#endif\n#if defined(UBRR3H)\n  extern HardwareSerial Serial3;\n#endif\n\nextern void serialEventRun(void) __attribute__((weak));\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/IPAddress.cpp",
    "content": "\n#include <Arduino.h>\n#include <IPAddress.h>\n\nIPAddress::IPAddress()\n{\n    memset(_address, 0, sizeof(_address));\n}\n\nIPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)\n{\n    _address[0] = first_octet;\n    _address[1] = second_octet;\n    _address[2] = third_octet;\n    _address[3] = fourth_octet;\n}\n\nIPAddress::IPAddress(uint32_t address)\n{\n    memcpy(_address, &address, sizeof(_address));\n}\n\nIPAddress::IPAddress(const uint8_t *address)\n{\n    memcpy(_address, address, sizeof(_address));\n}\n\nIPAddress& IPAddress::operator=(const uint8_t *address)\n{\n    memcpy(_address, address, sizeof(_address));\n    return *this;\n}\n\nIPAddress& IPAddress::operator=(uint32_t address)\n{\n    memcpy(_address, (const uint8_t *)&address, sizeof(_address));\n    return *this;\n}\n\nbool IPAddress::operator==(const uint8_t* addr)\n{\n    return memcmp(addr, _address, sizeof(_address)) == 0;\n}\n\nsize_t IPAddress::printTo(Print& p) const\n{\n    size_t n = 0;\n    for (int i =0; i < 3; i++)\n    {\n        n += p.print(_address[i], DEC);\n        n += p.print('.');\n    }\n    n += p.print(_address[3], DEC);\n    return n;\n}\n\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/IPAddress.h",
    "content": "/*\n *\n * MIT License:\n * Copyright (c) 2011 Adrian McEwen\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * adrianm@mcqn.com 1/1/2011\n */\n\n#ifndef IPAddress_h\n#define IPAddress_h\n\n#include <Printable.h>\n\n// A class to make it easier to handle and pass around IP addresses\n\nclass IPAddress : public Printable {\nprivate:\n    uint8_t _address[4];  // IPv4 address\n    // Access the raw byte array containing the address.  Because this returns a pointer\n    // to the internal structure rather than a copy of the address this function should only\n    // be used when you know that the usage of the returned uint8_t* will be transient and not\n    // stored.\n    uint8_t* raw_address() { return _address; };\n\npublic:\n    // Constructors\n    IPAddress();\n    IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);\n    IPAddress(uint32_t address);\n    IPAddress(const uint8_t *address);\n\n    // Overloaded cast operator to allow IPAddress objects to be used where a pointer\n    // to a four-byte uint8_t array is expected\n    operator uint32_t() { return *((uint32_t*)_address); };\n    bool operator==(const IPAddress& addr) { return (*((uint32_t*)_address)) == (*((uint32_t*)addr._address)); };\n    bool operator==(const uint8_t* addr);\n\n    // Overloaded index operator to allow getting and setting individual octets of the address\n    uint8_t operator[](int index) const { return _address[index]; };\n    uint8_t& operator[](int index) { return _address[index]; };\n\n    // Overloaded copy operators to allow initialisation of IPAddress objects from other types\n    IPAddress& operator=(const uint8_t *address);\n    IPAddress& operator=(uint32_t address);\n\n    virtual size_t printTo(Print& p) const;\n\n    friend class EthernetClass;\n    friend class UDP;\n    friend class Client;\n    friend class Server;\n    friend class DhcpClass;\n    friend class DNSClient;\n};\n\nconst IPAddress INADDR_NONE(0,0,0,0);\n\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/Platform.h",
    "content": "\n#ifndef __PLATFORM_H__\n#define __PLATFORM_H__\n\n#include <inttypes.h>\n#include <avr/pgmspace.h>\n#include <avr/eeprom.h>\n#include <avr/interrupt.h>\n#include <util/delay.h>\n\ntypedef unsigned char u8;\ntypedef unsigned short u16;\ntypedef unsigned long u32;\n\n#include \"Arduino.h\"\n\n#if defined(USBCON)\n\t#include \"USBDesc.h\"\n\t#include \"USBCore.h\"\n\t#include \"USBAPI.h\"\n#endif /* if defined(USBCON) */\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/Print.cpp",
    "content": "/*\n Print.cpp - Base class that provides print() and println()\n Copyright (c) 2008 David A. Mellis.  All right reserved.\n \n This library is free software; you can redistribute it and/or\n modify it under the terms of the GNU Lesser General Public\n License as published by the Free Software Foundation; either\n version 2.1 of the License, or (at your option) any later version.\n \n This library is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n Lesser General Public License for more details.\n \n You should have received a copy of the GNU Lesser General Public\n License along with this library; if not, write to the Free Software\n Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n \n Modified 23 November 2006 by David A. Mellis\n */\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <math.h>\n#include \"Arduino.h\"\n\n#include \"Print.h\"\n\n// Public Methods //////////////////////////////////////////////////////////////\n\n/* default implementation: may be overridden */\nsize_t Print::write(const uint8_t *buffer, size_t size)\n{\n  size_t n = 0;\n  while (size--) {\n    n += write(*buffer++);\n  }\n  return n;\n}\n\nsize_t Print::print(const __FlashStringHelper *ifsh)\n{\n  const char PROGMEM *p = (const char PROGMEM *)ifsh;\n  size_t n = 0;\n  while (1) {\n    unsigned char c = pgm_read_byte(p++);\n    if (c == 0) break;\n    n += write(c);\n  }\n  return n;\n}\n\nsize_t Print::print(const String &s)\n{\n  size_t n = 0;\n  for (uint16_t i = 0; i < s.length(); i++) {\n    n += write(s[i]);\n  }\n  return n;\n}\n\nsize_t Print::print(const char str[])\n{\n  return write(str);\n}\n\nsize_t Print::print(char c)\n{\n  return write(c);\n}\n\nsize_t Print::print(unsigned char b, int base)\n{\n  return print((unsigned long) b, base);\n}\n\nsize_t Print::print(int n, int base)\n{\n  return print((long) n, base);\n}\n\nsize_t Print::print(unsigned int n, int base)\n{\n  return print((unsigned long) n, base);\n}\n\nsize_t Print::print(long n, int base)\n{\n  if (base == 0) {\n    return write(n);\n  } else if (base == 10) {\n    if (n < 0) {\n      int t = print('-');\n      n = -n;\n      return printNumber(n, 10) + t;\n    }\n    return printNumber(n, 10);\n  } else {\n    return printNumber(n, base);\n  }\n}\n\nsize_t Print::print(unsigned long n, int base)\n{\n  if (base == 0) return write(n);\n  else return printNumber(n, base);\n}\n\nsize_t Print::print(double n, int digits)\n{\n  return printFloat(n, digits);\n}\n\nsize_t Print::println(const __FlashStringHelper *ifsh)\n{\n  size_t n = print(ifsh);\n  n += println();\n  return n;\n}\n\nsize_t Print::print(const Printable& x)\n{\n  return x.printTo(*this);\n}\n\nsize_t Print::println(void)\n{\n  size_t n = print('\\r');\n  n += print('\\n');\n  return n;\n}\n\nsize_t Print::println(const String &s)\n{\n  size_t n = print(s);\n  n += println();\n  return n;\n}\n\nsize_t Print::println(const char c[])\n{\n  size_t n = print(c);\n  n += println();\n  return n;\n}\n\nsize_t Print::println(char c)\n{\n  size_t n = print(c);\n  n += println();\n  return n;\n}\n\nsize_t Print::println(unsigned char b, int base)\n{\n  size_t n = print(b, base);\n  n += println();\n  return n;\n}\n\nsize_t Print::println(int num, int base)\n{\n  size_t n = print(num, base);\n  n += println();\n  return n;\n}\n\nsize_t Print::println(unsigned int num, int base)\n{\n  size_t n = print(num, base);\n  n += println();\n  return n;\n}\n\nsize_t Print::println(long num, int base)\n{\n  size_t n = print(num, base);\n  n += println();\n  return n;\n}\n\nsize_t Print::println(unsigned long num, int base)\n{\n  size_t n = print(num, base);\n  n += println();\n  return n;\n}\n\nsize_t Print::println(double num, int digits)\n{\n  size_t n = print(num, digits);\n  n += println();\n  return n;\n}\n\nsize_t Print::println(const Printable& x)\n{\n  size_t n = print(x);\n  n += println();\n  return n;\n}\n\n// Private Methods /////////////////////////////////////////////////////////////\n\nsize_t Print::printNumber(unsigned long n, uint8_t base) {\n  char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.\n  char *str = &buf[sizeof(buf) - 1];\n\n  *str = '\\0';\n\n  // prevent crash if called with base == 1\n  if (base < 2) base = 10;\n\n  do {\n    unsigned long m = n;\n    n /= base;\n    char c = m - base * n;\n    *--str = c < 10 ? c + '0' : c + 'A' - 10;\n  } while(n);\n\n  return write(str);\n}\n\nsize_t Print::printFloat(double number, uint8_t digits) \n{ \n  size_t n = 0;\n  \n  // Handle negative numbers\n  if (number < 0.0)\n  {\n     n += print('-');\n     number = -number;\n  }\n\n  // Round correctly so that print(1.999, 2) prints as \"2.00\"\n  double rounding = 0.5;\n  for (uint8_t i=0; i<digits; ++i)\n    rounding /= 10.0;\n  \n  number += rounding;\n\n  // Extract the integer part of the number and print it\n  unsigned long int_part = (unsigned long)number;\n  double remainder = number - (double)int_part;\n  n += print(int_part);\n\n  // Print the decimal point, but only if there are digits beyond\n  if (digits > 0) {\n    n += print(\".\"); \n  }\n\n  // Extract digits from the remainder one at a time\n  while (digits-- > 0)\n  {\n    remainder *= 10.0;\n    int toPrint = int(remainder);\n    n += print(toPrint);\n    remainder -= toPrint; \n  } \n  \n  return n;\n}\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/Print.h",
    "content": "/*\n  Print.h - Base class that provides print() and println()\n  Copyright (c) 2008 David A. Mellis.  All right reserved.\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General Public\n  License along with this library; if not, write to the Free Software\n  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n*/\n\n#ifndef Print_h\n#define Print_h\n\n#include <inttypes.h>\n#include <stdio.h> // for size_t\n\n#include \"WString.h\"\n#include \"Printable.h\"\n\n#define DEC 10\n#define HEX 16\n#define OCT 8\n#define BIN 2\n\nclass Print\n{\n  private:\n    int write_error;\n    size_t printNumber(unsigned long, uint8_t);\n    size_t printFloat(double, uint8_t);\n  protected:\n    void setWriteError(int err = 1) { write_error = err; }\n  public:\n    Print() : write_error(0) {}\n  \n    int getWriteError() { return write_error; }\n    void clearWriteError() { setWriteError(0); }\n  \n    virtual size_t write(uint8_t) = 0;\n    size_t write(const char *str) { return write((const uint8_t *)str, strlen(str)); }\n    virtual size_t write(const uint8_t *buffer, size_t size);\n    \n    size_t print(const __FlashStringHelper *);\n    size_t print(const String &);\n    size_t print(const char[]);\n    size_t print(char);\n    size_t print(unsigned char, int = DEC);\n    size_t print(int, int = DEC);\n    size_t print(unsigned int, int = DEC);\n    size_t print(long, int = DEC);\n    size_t print(unsigned long, int = DEC);\n    size_t print(double, int = 2);\n    size_t print(const Printable&);\n\n    size_t println(const __FlashStringHelper *);\n    size_t println(const String &s);\n    size_t println(const char[]);\n    size_t println(char);\n    size_t println(unsigned char, int = DEC);\n    size_t println(int, int = DEC);\n    size_t println(unsigned int, int = DEC);\n    size_t println(long, int = DEC);\n    size_t println(unsigned long, int = DEC);\n    size_t println(double, int = 2);\n    size_t println(const Printable&);\n    size_t println(void);\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/Printable.h",
    "content": "/*\n  Printable.h - Interface class that allows printing of complex types\n  Copyright (c) 2011 Adrian McEwen.  All right reserved.\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General Public\n  License along with this library; if not, write to the Free Software\n  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n*/\n\n#ifndef Printable_h\n#define Printable_h\n\n#include <new.h>\n\nclass Print;\n\n/** The Printable class provides a way for new classes to allow themselves to be printed.\n    By deriving from Printable and implementing the printTo method, it will then be possible\n    for users to print out instances of this class by passing them into the usual\n    Print::print and Print::println methods.\n*/\n\nclass Printable\n{\n  public:\n    virtual size_t printTo(Print& p) const = 0;\n};\n\n#endif\n\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/Server.h",
    "content": "#ifndef server_h\n#define server_h\n\nclass Server : public Print {\npublic:\n  virtual void begin() =0;\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/Stream.cpp",
    "content": "/*\n Stream.cpp - adds parsing methods to Stream class\n Copyright (c) 2008 David A. Mellis.  All right reserved.\n\n This library is free software; you can redistribute it and/or\n modify it under the terms of the GNU Lesser General Public\n License as published by the Free Software Foundation; either\n version 2.1 of the License, or (at your option) any later version.\n\n This library is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n Lesser General Public License for more details.\n\n You should have received a copy of the GNU Lesser General Public\n License along with this library; if not, write to the Free Software\n Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n\n Created July 2011\n parsing functions based on TextFinder library by Michael Margolis\n */\n\n#include \"Arduino.h\"\n#include \"Stream.h\"\n\n#define PARSE_TIMEOUT 1000  // default number of milli-seconds to wait\n#define NO_SKIP_CHAR  1  // a magic char not found in a valid ASCII numeric field\n\n// private method to read stream with timeout\nint Stream::timedRead()\n{\n  int c;\n  _startMillis = millis();\n  do {\n    c = read();\n    if (c >= 0) return c;\n  } while(millis() - _startMillis < _timeout);\n  return -1;     // -1 indicates timeout\n}\n\n// private method to peek stream with timeout\nint Stream::timedPeek()\n{\n  int c;\n  _startMillis = millis();\n  do {\n    c = peek();\n    if (c >= 0) return c;\n  } while(millis() - _startMillis < _timeout);\n  return -1;     // -1 indicates timeout\n}\n\n// returns peek of the next digit in the stream or -1 if timeout\n// discards non-numeric characters\nint Stream::peekNextDigit()\n{\n  int c;\n  while (1) {\n    c = timedPeek();\n    if (c < 0) return c;  // timeout\n    if (c == '-') return c;\n    if (c >= '0' && c <= '9') return c;\n    read();  // discard non-numeric\n  }\n}\n\n// Public Methods\n//////////////////////////////////////////////////////////////\n\nvoid Stream::setTimeout(unsigned long timeout)  // sets the maximum number of milliseconds to wait\n{\n  _timeout = timeout;\n}\n\n // find returns true if the target string is found\nbool  Stream::find(char *target)\n{\n  return findUntil(target, NULL);\n}\n\n// reads data from the stream until the target string of given length is found\n// returns true if target string is found, false if timed out\nbool Stream::find(char *target, size_t length)\n{\n  return findUntil(target, length, NULL, 0);\n}\n\n// as find but search ends if the terminator string is found\nbool  Stream::findUntil(char *target, char *terminator)\n{\n  return findUntil(target, strlen(target), terminator, strlen(terminator));\n}\n\n// reads data from the stream until the target string of the given length is found\n// search terminated if the terminator string is found\n// returns true if target string is found, false if terminated or timed out\nbool Stream::findUntil(char *target, size_t targetLen, char *terminator, size_t termLen)\n{\n  size_t index = 0;  // maximum target string length is 64k bytes!\n  size_t termIndex = 0;\n  int c;\n  \n  if( *target == 0)\n    return true;   // return true if target is a null string\n  while( (c = timedRead()) > 0){\n    \n    if(c != target[index])\n      index = 0; // reset index if any char does not match\n    \n    if( c == target[index]){\n      //////Serial.print(\"found \"); Serial.write(c); Serial.print(\"index now\"); Serial.println(index+1);\n      if(++index >= targetLen){ // return true if all chars in the target match\n        return true;\n      }\n    }\n    \n    if(termLen > 0 && c == terminator[termIndex]){\n      if(++termIndex >= termLen)\n        return false;       // return false if terminate string found before target string\n    }\n    else\n      termIndex = 0;\n  }\n  return false;\n}\n\n\n// returns the first valid (long) integer value from the current position.\n// initial characters that are not digits (or the minus sign) are skipped\n// function is terminated by the first character that is not a digit.\nlong Stream::parseInt()\n{\n  return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)\n}\n\n// as above but a given skipChar is ignored\n// this allows format characters (typically commas) in values to be ignored\nlong Stream::parseInt(char skipChar)\n{\n  boolean isNegative = false;\n  long value = 0;\n  int c;\n\n  c = peekNextDigit();\n  // ignore non numeric leading characters\n  if(c < 0)\n    return 0; // zero returned if timeout\n\n  do{\n    if(c == skipChar)\n      ; // ignore this charactor\n    else if(c == '-')\n      isNegative = true;\n    else if(c >= '0' && c <= '9')        // is c a digit?\n      value = value * 10 + c - '0';\n    read();  // consume the character we got with peek\n    c = timedPeek();\n  }\n  while( (c >= '0' && c <= '9') || c == skipChar );\n\n  if(isNegative)\n    value = -value;\n  return value;\n}\n\n\n// as parseInt but returns a floating point value\nfloat Stream::parseFloat()\n{\n  return parseFloat(NO_SKIP_CHAR);\n}\n\n// as above but the given skipChar is ignored\n// this allows format characters (typically commas) in values to be ignored\nfloat Stream::parseFloat(char skipChar){\n  boolean isNegative = false;\n  boolean isFraction = false;\n  long value = 0;\n  char c;\n  float fraction = 1.0;\n\n  c = peekNextDigit();\n    // ignore non numeric leading characters\n  if(c < 0)\n    return 0; // zero returned if timeout\n\n  do{\n    if(c == skipChar)\n      ; // ignore\n    else if(c == '-')\n      isNegative = true;\n    else if (c == '.')\n      isFraction = true;\n    else if(c >= '0' && c <= '9')  {      // is c a digit?\n      value = value * 10 + c - '0';\n      if(isFraction)\n         fraction *= 0.1;\n    }\n    read();  // consume the character we got with peek\n    c = timedPeek();\n  }\n  while( (c >= '0' && c <= '9')  || c == '.' || c == skipChar );\n\n  if(isNegative)\n    value = -value;\n  if(isFraction)\n    return value * fraction;\n  else\n    return value;\n}\n\n// read characters from stream into buffer\n// terminates if length characters have been read, or timeout (see setTimeout)\n// returns the number of characters placed in the buffer\n// the buffer is NOT null terminated.\n//\nsize_t Stream::readBytes(char *buffer, size_t length)\n{\n  size_t count = 0;\n  while (count < length) {\n    int c = timedRead();\n    if (c < 0) break;\n    *buffer++ = (char)c;\n    count++;\n  }\n  return count;\n}\n\n\n// as readBytes with terminator character\n// terminates if length characters have been read, timeout, or if the terminator character  detected\n// returns the number of characters placed in the buffer (0 means no valid data found)\n\nsize_t Stream::readBytesUntil(char terminator, char *buffer, size_t length)\n{\n  if (length < 1) return 0;\n  size_t index = 0;\n  while (index < length) {\n    int c = timedRead();\n    if (c < 0 || c == terminator) break;\n    *buffer++ = (char)c;\n    index++;\n  }\n  return index; // return number of characters, not including null terminator\n}\n\nString Stream::readString()\n{\n  String ret;\n  int c = timedRead();\n  while (c >= 0)\n  {\n    ret += (char)c;\n    c = timedRead();\n  }\n  return ret;\n}\n\nString Stream::readStringUntil(char terminator)\n{\n  String ret;\n  int c = timedRead();\n  while (c >= 0 && c != terminator)\n  {\n    ret += (char)c;\n    c = timedRead();\n  }\n  return ret;\n}\n\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/Stream.h",
    "content": "/*\n  Stream.h - base class for character-based streams.\n  Copyright (c) 2010 David A. Mellis.  All right reserved.\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General Public\n  License along with this library; if not, write to the Free Software\n  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n\n  parsing functions based on TextFinder library by Michael Margolis\n*/\n\n#ifndef Stream_h\n#define Stream_h\n\n#include <inttypes.h>\n#include \"Print.h\"\n\n// compatability macros for testing\n/*\n#define   getInt()            parseInt()\n#define   getInt(skipChar)    parseInt(skipchar)\n#define   getFloat()          parseFloat()\n#define   getFloat(skipChar)  parseFloat(skipChar)\n#define   getString( pre_string, post_string, buffer, length)\nreadBytesBetween( pre_string, terminator, buffer, length)\n*/\n\nclass Stream : public Print\n{\n  private:\n    unsigned long _timeout;      // number of milliseconds to wait for the next char before aborting timed read\n    unsigned long _startMillis;  // used for timeout measurement\n    int timedRead();    // private method to read stream with timeout\n    int timedPeek();    // private method to peek stream with timeout\n    int peekNextDigit(); // returns the next numeric digit in the stream or -1 if timeout\n\n  public:\n    virtual int available() = 0;\n    virtual int read() = 0;\n    virtual int peek() = 0;\n    virtual void flush() = 0;\n\n    Stream() {_timeout=1000;}\n\n// parsing methods\n\n  void setTimeout(unsigned long timeout);  // sets maximum milliseconds to wait for stream data, default is 1 second\n\n  bool find(char *target);   // reads data from the stream until the target string is found\n  // returns true if target string is found, false if timed out (see setTimeout)\n\n  bool find(char *target, size_t length);   // reads data from the stream until the target string of given length is found\n  // returns true if target string is found, false if timed out\n\n  bool findUntil(char *target, char *terminator);   // as find but search ends if the terminator string is found\n\n  bool findUntil(char *target, size_t targetLen, char *terminate, size_t termLen);   // as above but search ends if the terminate string is found\n\n\n  long parseInt(); // returns the first valid (long) integer value from the current position.\n  // initial characters that are not digits (or the minus sign) are skipped\n  // integer is terminated by the first character that is not a digit.\n\n  float parseFloat();               // float version of parseInt\n\n  size_t readBytes( char *buffer, size_t length); // read chars from stream into buffer\n  // terminates if length characters have been read or timeout (see setTimeout)\n  // returns the number of characters placed in the buffer (0 means no valid data found)\n\n  size_t readBytesUntil( char terminator, char *buffer, size_t length); // as readBytes with terminator character\n  // terminates if length characters have been read, timeout, or if the terminator character  detected\n  // returns the number of characters placed in the buffer (0 means no valid data found)\n\n  // Arduino String functions to be added here\n  String readString();\n  String readStringUntil(char terminator);\n\n  protected:\n  long parseInt(char skipChar); // as above but the given skipChar is ignored\n  // as above but the given skipChar is ignored\n  // this allows format characters (typically commas) in values to be ignored\n\n  float parseFloat(char skipChar);  // as above but the given skipChar is ignored\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/Tone.cpp",
    "content": "/* Tone.cpp\n\n  A Tone Generator Library\n\n  Written by Brett Hagman\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General Public\n  License along with this library; if not, write to the Free Software\n  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n\nVersion Modified By Date     Comments\n------- ----------- -------- --------\n0001    B Hagman    09/08/02 Initial coding\n0002    B Hagman    09/08/18 Multiple pins\n0003    B Hagman    09/08/18 Moved initialization from constructor to begin()\n0004    B Hagman    09/09/26 Fixed problems with ATmega8\n0005    B Hagman    09/11/23 Scanned prescalars for best fit on 8 bit timers\n                    09/11/25 Changed pin toggle method to XOR\n                    09/11/25 Fixed timer0 from being excluded\n0006    D Mellis    09/12/29 Replaced objects with functions\n0007    M Sproul    10/08/29 Changed #ifdefs from cpu to register\n*************************************************/\n\n#include <avr/interrupt.h>\n#include <avr/pgmspace.h>\n#include \"Arduino.h\"\n#include \"pins_arduino.h\"\n\n#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__)\n#define TCCR2A TCCR2\n#define TCCR2B TCCR2\n#define COM2A1 COM21\n#define COM2A0 COM20\n#define OCR2A OCR2\n#define TIMSK2 TIMSK\n#define OCIE2A OCIE2\n#define TIMER2_COMPA_vect TIMER2_COMP_vect\n#define TIMSK1 TIMSK\n#endif\n\n// timerx_toggle_count:\n//  > 0 - duration specified\n//  = 0 - stopped\n//  < 0 - infinitely (until stop() method called, or new play() called)\n\n#if !defined(__AVR_ATmega8__)\nvolatile long timer0_toggle_count;\nvolatile uint8_t *timer0_pin_port;\nvolatile uint8_t timer0_pin_mask;\n#endif\n\nvolatile long timer1_toggle_count;\nvolatile uint8_t *timer1_pin_port;\nvolatile uint8_t timer1_pin_mask;\nvolatile long timer2_toggle_count;\nvolatile uint8_t *timer2_pin_port;\nvolatile uint8_t timer2_pin_mask;\n\n#if defined(TIMSK3)\nvolatile long timer3_toggle_count;\nvolatile uint8_t *timer3_pin_port;\nvolatile uint8_t timer3_pin_mask;\n#endif\n\n#if defined(TIMSK4)\nvolatile long timer4_toggle_count;\nvolatile uint8_t *timer4_pin_port;\nvolatile uint8_t timer4_pin_mask;\n#endif\n\n#if defined(TIMSK5)\nvolatile long timer5_toggle_count;\nvolatile uint8_t *timer5_pin_port;\nvolatile uint8_t timer5_pin_mask;\n#endif\n\n\n// MLS: This does not make sense, the 3 options are the same\n#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)\n\n#define AVAILABLE_TONE_PINS 1\n\nconst uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2 /*, 3, 4, 5, 1, 0 */ };\nstatic uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255 /*, 255, 255, 255, 255, 255 */ };\n\n#elif defined(__AVR_ATmega8__)\n\n#define AVAILABLE_TONE_PINS 1\n\nconst uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2 /*, 1 */ };\nstatic uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255 /*, 255 */ };\n\n#else\n\n#define AVAILABLE_TONE_PINS 1\n\n// Leave timer 0 to last.\nconst uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2 /*, 1, 0 */ };\nstatic uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255 /*, 255, 255 */ };\n\n#endif\n\n\n\nstatic int8_t toneBegin(uint8_t _pin)\n{\n  int8_t _timer = -1;\n\n  // if we're already using the pin, the timer should be configured.  \n  for (int i = 0; i < AVAILABLE_TONE_PINS; i++) {\n    if (tone_pins[i] == _pin) {\n      return pgm_read_byte(tone_pin_to_timer_PGM + i);\n    }\n  }\n  \n  // search for an unused timer.\n  for (int i = 0; i < AVAILABLE_TONE_PINS; i++) {\n    if (tone_pins[i] == 255) {\n      tone_pins[i] = _pin;\n      _timer = pgm_read_byte(tone_pin_to_timer_PGM + i);\n      break;\n    }\n  }\n  \n  if (_timer != -1)\n  {\n    // Set timer specific stuff\n    // All timers in CTC mode\n    // 8 bit timers will require changing prescalar values,\n    // whereas 16 bit timers are set to either ck/1 or ck/64 prescalar\n    switch (_timer)\n    {\n      #if defined(TCCR0A) && defined(TCCR0B)\n      case 0:\n        // 8 bit timer\n        TCCR0A = 0;\n        TCCR0B = 0;\n        bitWrite(TCCR0A, WGM01, 1);\n        bitWrite(TCCR0B, CS00, 1);\n        timer0_pin_port = portOutputRegister(digitalPinToPort(_pin));\n        timer0_pin_mask = digitalPinToBitMask(_pin);\n        break;\n      #endif\n\n      #if defined(TCCR1A) && defined(TCCR1B) && defined(WGM12)\n      case 1:\n        // 16 bit timer\n        TCCR1A = 0;\n        TCCR1B = 0;\n        bitWrite(TCCR1B, WGM12, 1);\n        bitWrite(TCCR1B, CS10, 1);\n        timer1_pin_port = portOutputRegister(digitalPinToPort(_pin));\n        timer1_pin_mask = digitalPinToBitMask(_pin);\n        break;\n      #endif\n\n      #if defined(TCCR2A) && defined(TCCR2B)\n      case 2:\n        // 8 bit timer\n        TCCR2A = 0;\n        TCCR2B = 0;\n        bitWrite(TCCR2A, WGM21, 1);\n        bitWrite(TCCR2B, CS20, 1);\n        timer2_pin_port = portOutputRegister(digitalPinToPort(_pin));\n        timer2_pin_mask = digitalPinToBitMask(_pin);\n        break;\n      #endif\n\n      #if defined(TCCR3A) && defined(TCCR3B) &&  defined(TIMSK3)\n      case 3:\n        // 16 bit timer\n        TCCR3A = 0;\n        TCCR3B = 0;\n        bitWrite(TCCR3B, WGM32, 1);\n        bitWrite(TCCR3B, CS30, 1);\n        timer3_pin_port = portOutputRegister(digitalPinToPort(_pin));\n        timer3_pin_mask = digitalPinToBitMask(_pin);\n        break;\n      #endif\n\n      #if defined(TCCR4A) && defined(TCCR4B) &&  defined(TIMSK4)\n      case 4:\n        // 16 bit timer\n        TCCR4A = 0;\n        TCCR4B = 0;\n        #if defined(WGM42)\n          bitWrite(TCCR4B, WGM42, 1);\n        #elif defined(CS43)\n          #warning this may not be correct\n          // atmega32u4\n          bitWrite(TCCR4B, CS43, 1);\n        #endif\n        bitWrite(TCCR4B, CS40, 1);\n        timer4_pin_port = portOutputRegister(digitalPinToPort(_pin));\n        timer4_pin_mask = digitalPinToBitMask(_pin);\n        break;\n      #endif\n\n      #if defined(TCCR5A) && defined(TCCR5B) &&  defined(TIMSK5)\n      case 5:\n        // 16 bit timer\n        TCCR5A = 0;\n        TCCR5B = 0;\n        bitWrite(TCCR5B, WGM52, 1);\n        bitWrite(TCCR5B, CS50, 1);\n        timer5_pin_port = portOutputRegister(digitalPinToPort(_pin));\n        timer5_pin_mask = digitalPinToBitMask(_pin);\n        break;\n      #endif\n    }\n  }\n\n  return _timer;\n}\n\n\n\n// frequency (in hertz) and duration (in milliseconds).\n\nvoid tone(uint8_t _pin, unsigned int frequency, unsigned long duration)\n{\n  uint8_t prescalarbits = 0b001;\n  long toggle_count = 0;\n  uint32_t ocr = 0;\n  int8_t _timer;\n\n  _timer = toneBegin(_pin);\n\n  if (_timer >= 0)\n  {\n    // Set the pinMode as OUTPUT\n    pinMode(_pin, OUTPUT);\n    \n    // if we are using an 8 bit timer, scan through prescalars to find the best fit\n    if (_timer == 0 || _timer == 2)\n    {\n      ocr = F_CPU / frequency / 2 - 1;\n      prescalarbits = 0b001;  // ck/1: same for both timers\n      if (ocr > 255)\n      {\n        ocr = F_CPU / frequency / 2 / 8 - 1;\n        prescalarbits = 0b010;  // ck/8: same for both timers\n\n        if (_timer == 2 && ocr > 255)\n        {\n          ocr = F_CPU / frequency / 2 / 32 - 1;\n          prescalarbits = 0b011;\n        }\n\n        if (ocr > 255)\n        {\n          ocr = F_CPU / frequency / 2 / 64 - 1;\n          prescalarbits = _timer == 0 ? 0b011 : 0b100;\n\n          if (_timer == 2 && ocr > 255)\n          {\n            ocr = F_CPU / frequency / 2 / 128 - 1;\n            prescalarbits = 0b101;\n          }\n\n          if (ocr > 255)\n          {\n            ocr = F_CPU / frequency / 2 / 256 - 1;\n            prescalarbits = _timer == 0 ? 0b100 : 0b110;\n            if (ocr > 255)\n            {\n              // can't do any better than /1024\n              ocr = F_CPU / frequency / 2 / 1024 - 1;\n              prescalarbits = _timer == 0 ? 0b101 : 0b111;\n            }\n          }\n        }\n      }\n\n#if defined(TCCR0B)\n      if (_timer == 0)\n      {\n        TCCR0B = prescalarbits;\n      }\n      else\n#endif\n#if defined(TCCR2B)\n      {\n        TCCR2B = prescalarbits;\n      }\n#else\n      {\n        // dummy place holder to make the above ifdefs work\n      }\n#endif\n    }\n    else\n    {\n      // two choices for the 16 bit timers: ck/1 or ck/64\n      ocr = F_CPU / frequency / 2 - 1;\n\n      prescalarbits = 0b001;\n      if (ocr > 0xffff)\n      {\n        ocr = F_CPU / frequency / 2 / 64 - 1;\n        prescalarbits = 0b011;\n      }\n\n      if (_timer == 1)\n      {\n#if defined(TCCR1B)\n        TCCR1B = (TCCR1B & 0b11111000) | prescalarbits;\n#endif\n      }\n#if defined(TCCR3B)\n      else if (_timer == 3)\n        TCCR3B = (TCCR3B & 0b11111000) | prescalarbits;\n#endif\n#if defined(TCCR4B)\n      else if (_timer == 4)\n        TCCR4B = (TCCR4B & 0b11111000) | prescalarbits;\n#endif\n#if defined(TCCR5B)\n      else if (_timer == 5)\n        TCCR5B = (TCCR5B & 0b11111000) | prescalarbits;\n#endif\n\n    }\n    \n\n    // Calculate the toggle count\n    if (duration > 0)\n    {\n      toggle_count = 2 * frequency * duration / 1000;\n    }\n    else\n    {\n      toggle_count = -1;\n    }\n\n    // Set the OCR for the given timer,\n    // set the toggle count,\n    // then turn on the interrupts\n    switch (_timer)\n    {\n\n#if defined(OCR0A) && defined(TIMSK0) && defined(OCIE0A)\n      case 0:\n        OCR0A = ocr;\n        timer0_toggle_count = toggle_count;\n        bitWrite(TIMSK0, OCIE0A, 1);\n        break;\n#endif\n\n      case 1:\n#if defined(OCR1A) && defined(TIMSK1) && defined(OCIE1A)\n        OCR1A = ocr;\n        timer1_toggle_count = toggle_count;\n        bitWrite(TIMSK1, OCIE1A, 1);\n#elif defined(OCR1A) && defined(TIMSK) && defined(OCIE1A)\n        // this combination is for at least the ATmega32\n        OCR1A = ocr;\n        timer1_toggle_count = toggle_count;\n        bitWrite(TIMSK, OCIE1A, 1);\n#endif\n        break;\n\n#if defined(OCR2A) && defined(TIMSK2) && defined(OCIE2A)\n      case 2:\n        OCR2A = ocr;\n        timer2_toggle_count = toggle_count;\n        bitWrite(TIMSK2, OCIE2A, 1);\n        break;\n#endif\n\n#if defined(TIMSK3)\n      case 3:\n        OCR3A = ocr;\n        timer3_toggle_count = toggle_count;\n        bitWrite(TIMSK3, OCIE3A, 1);\n        break;\n#endif\n\n#if defined(TIMSK4)\n      case 4:\n        OCR4A = ocr;\n        timer4_toggle_count = toggle_count;\n        bitWrite(TIMSK4, OCIE4A, 1);\n        break;\n#endif\n\n#if defined(OCR5A) && defined(TIMSK5) && defined(OCIE5A)\n      case 5:\n        OCR5A = ocr;\n        timer5_toggle_count = toggle_count;\n        bitWrite(TIMSK5, OCIE5A, 1);\n        break;\n#endif\n\n    }\n  }\n}\n\n\n// XXX: this function only works properly for timer 2 (the only one we use\n// currently).  for the others, it should end the tone, but won't restore\n// proper PWM functionality for the timer.\nvoid disableTimer(uint8_t _timer)\n{\n  switch (_timer)\n  {\n    case 0:\n      #if defined(TIMSK0)\n        TIMSK0 = 0;\n      #elif defined(TIMSK)\n        TIMSK = 0; // atmega32\n      #endif\n      break;\n\n#if defined(TIMSK1) && defined(OCIE1A)\n    case 1:\n      bitWrite(TIMSK1, OCIE1A, 0);\n      break;\n#endif\n\n    case 2:\n      #if defined(TIMSK2) && defined(OCIE2A)\n        bitWrite(TIMSK2, OCIE2A, 0); // disable interrupt\n      #endif\n      #if defined(TCCR2A) && defined(WGM20)\n        TCCR2A = (1 << WGM20);\n      #endif\n      #if defined(TCCR2B) && defined(CS22)\n        TCCR2B = (TCCR2B & 0b11111000) | (1 << CS22);\n      #endif\n      #if defined(OCR2A)\n        OCR2A = 0;\n      #endif\n      break;\n\n#if defined(TIMSK3)\n    case 3:\n      TIMSK3 = 0;\n      break;\n#endif\n\n#if defined(TIMSK4)\n    case 4:\n      TIMSK4 = 0;\n      break;\n#endif\n\n#if defined(TIMSK5)\n    case 5:\n      TIMSK5 = 0;\n      break;\n#endif\n  }\n}\n\n\nvoid noTone(uint8_t _pin)\n{\n  int8_t _timer = -1;\n  \n  for (int i = 0; i < AVAILABLE_TONE_PINS; i++) {\n    if (tone_pins[i] == _pin) {\n      _timer = pgm_read_byte(tone_pin_to_timer_PGM + i);\n      tone_pins[i] = 255;\n    }\n  }\n  \n  disableTimer(_timer);\n\n  digitalWrite(_pin, 0);\n}\n\n#if 0\n#if !defined(__AVR_ATmega8__)\nISR(TIMER0_COMPA_vect)\n{\n  if (timer0_toggle_count != 0)\n  {\n    // toggle the pin\n    *timer0_pin_port ^= timer0_pin_mask;\n\n    if (timer0_toggle_count > 0)\n      timer0_toggle_count--;\n  }\n  else\n  {\n    disableTimer(0);\n    *timer0_pin_port &= ~(timer0_pin_mask);  // keep pin low after stop\n  }\n}\n#endif\n\n\nISR(TIMER1_COMPA_vect)\n{\n  if (timer1_toggle_count != 0)\n  {\n    // toggle the pin\n    *timer1_pin_port ^= timer1_pin_mask;\n\n    if (timer1_toggle_count > 0)\n      timer1_toggle_count--;\n  }\n  else\n  {\n    disableTimer(1);\n    *timer1_pin_port &= ~(timer1_pin_mask);  // keep pin low after stop\n  }\n}\n#endif\n\n\nISR(TIMER2_COMPA_vect)\n{\n\n  if (timer2_toggle_count != 0)\n  {\n    // toggle the pin\n    *timer2_pin_port ^= timer2_pin_mask;\n\n    if (timer2_toggle_count > 0)\n      timer2_toggle_count--;\n  }\n  else\n  {\n    // need to call noTone() so that the tone_pins[] entry is reset, so the\n    // timer gets initialized next time we call tone().\n    // XXX: this assumes timer 2 is always the first one used.\n    noTone(tone_pins[0]);\n//    disableTimer(2);\n//    *timer2_pin_port &= ~(timer2_pin_mask);  // keep pin low after stop\n  }\n}\n\n\n\n//#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)\n#if 0\n\nISR(TIMER3_COMPA_vect)\n{\n  if (timer3_toggle_count != 0)\n  {\n    // toggle the pin\n    *timer3_pin_port ^= timer3_pin_mask;\n\n    if (timer3_toggle_count > 0)\n      timer3_toggle_count--;\n  }\n  else\n  {\n    disableTimer(3);\n    *timer3_pin_port &= ~(timer3_pin_mask);  // keep pin low after stop\n  }\n}\n\nISR(TIMER4_COMPA_vect)\n{\n  if (timer4_toggle_count != 0)\n  {\n    // toggle the pin\n    *timer4_pin_port ^= timer4_pin_mask;\n\n    if (timer4_toggle_count > 0)\n      timer4_toggle_count--;\n  }\n  else\n  {\n    disableTimer(4);\n    *timer4_pin_port &= ~(timer4_pin_mask);  // keep pin low after stop\n  }\n}\n\nISR(TIMER5_COMPA_vect)\n{\n  if (timer5_toggle_count != 0)\n  {\n    // toggle the pin\n    *timer5_pin_port ^= timer5_pin_mask;\n\n    if (timer5_toggle_count > 0)\n      timer5_toggle_count--;\n  }\n  else\n  {\n    disableTimer(5);\n    *timer5_pin_port &= ~(timer5_pin_mask);  // keep pin low after stop\n  }\n}\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/USBAPI.h",
    "content": "\n\n#ifndef __USBAPI__\n#define __USBAPI__\n\n#if defined(USBCON)\n\n//================================================================================\n//================================================================================\n//\tUSB\n\nclass USBDevice_\n{\npublic:\n\tUSBDevice_();\n\tbool configured();\n\n\tvoid attach();\n\tvoid detach();\t// Serial port goes down too...\n\tvoid poll();\n};\nextern USBDevice_ USBDevice;\n\n//================================================================================\n//================================================================================\n//\tSerial over CDC (Serial1 is the physical port)\n\nclass Serial_ : public Stream\n{\nprivate:\n\tring_buffer *_cdc_rx_buffer;\npublic:\n\tvoid begin(uint16_t baud_count);\n\tvoid end(void);\n\n\tvirtual int available(void);\n\tvirtual void accept(void);\n\tvirtual int peek(void);\n\tvirtual int read(void);\n\tvirtual void flush(void);\n\tvirtual size_t write(uint8_t);\n\toperator bool();\n};\nextern Serial_ Serial;\n\n//================================================================================\n//================================================================================\n//\tMouse\n\n#define MOUSE_LEFT 1\n#define MOUSE_RIGHT 2\n#define MOUSE_MIDDLE 4\n#define MOUSE_ALL (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)\n\nclass Mouse_\n{\nprivate:\n\tuint8_t _buttons;\n\tvoid buttons(uint8_t b);\npublic:\n\tMouse_(void);\n\tvoid begin(void);\n\tvoid end(void);\n\tvoid click(uint8_t b = MOUSE_LEFT);\n\tvoid move(signed char x, signed char y, signed char wheel = 0);\t\n\tvoid press(uint8_t b = MOUSE_LEFT);\t\t// press LEFT by default\n\tvoid release(uint8_t b = MOUSE_LEFT);\t// release LEFT by default\n\tbool isPressed(uint8_t b = MOUSE_LEFT);\t// check LEFT by default\n};\nextern Mouse_ Mouse;\n\n//================================================================================\n//================================================================================\n//\tKeyboard\n\n#define KEY_LEFT_CTRL\t\t0x80\n#define KEY_LEFT_SHIFT\t\t0x81\n#define KEY_LEFT_ALT\t\t0x82\n#define KEY_LEFT_GUI\t\t0x83\n#define KEY_RIGHT_CTRL\t\t0x84\n#define KEY_RIGHT_SHIFT\t\t0x85\n#define KEY_RIGHT_ALT\t\t0x86\n#define KEY_RIGHT_GUI\t\t0x87\n\n#define KEY_UP_ARROW\t\t0xDA\n#define KEY_DOWN_ARROW\t\t0xD9\n#define KEY_LEFT_ARROW\t\t0xD8\n#define KEY_RIGHT_ARROW\t\t0xD7\n#define KEY_BACKSPACE\t\t0xB2\n#define KEY_TAB\t\t\t\t0xB3\n#define KEY_RETURN\t\t\t0xB0\n#define KEY_ESC\t\t\t\t0xB1\n#define KEY_INSERT\t\t\t0xD1\n#define KEY_DELETE\t\t\t0xD4\n#define KEY_PAGE_UP\t\t\t0xD3\n#define KEY_PAGE_DOWN\t\t0xD6\n#define KEY_HOME\t\t\t0xD2\n#define KEY_END\t\t\t\t0xD5\n#define KEY_CAPS_LOCK\t\t0xC1\n#define KEY_F1\t\t\t\t0xC2\n#define KEY_F2\t\t\t\t0xC3\n#define KEY_F3\t\t\t\t0xC4\n#define KEY_F4\t\t\t\t0xC5\n#define KEY_F5\t\t\t\t0xC6\n#define KEY_F6\t\t\t\t0xC7\n#define KEY_F7\t\t\t\t0xC8\n#define KEY_F8\t\t\t\t0xC9\n#define KEY_F9\t\t\t\t0xCA\n#define KEY_F10\t\t\t\t0xCB\n#define KEY_F11\t\t\t\t0xCC\n#define KEY_F12\t\t\t\t0xCD\n\n//\tLow level key report: up to 6 keys and shift, ctrl etc at once\ntypedef struct\n{\n\tuint8_t modifiers;\n\tuint8_t reserved;\n\tuint8_t keys[6];\n} KeyReport;\n\nclass Keyboard_ : public Print\n{\nprivate:\n\tKeyReport _keyReport;\n\tvoid sendReport(KeyReport* keys);\npublic:\n\tKeyboard_(void);\n\tvoid begin(void);\n\tvoid end(void);\n\tvirtual size_t write(uint8_t k);\n\tvirtual size_t press(uint8_t k);\n\tvirtual size_t release(uint8_t k);\n\tvirtual void releaseAll(void);\n};\nextern Keyboard_ Keyboard;\n\n//================================================================================\n//================================================================================\n//\tLow level API\n\ntypedef struct\n{\n\tuint8_t bmRequestType;\n\tuint8_t bRequest;\n\tuint8_t wValueL;\n\tuint8_t wValueH;\n\tuint16_t wIndex;\n\tuint16_t wLength;\n} Setup;\n\n//================================================================================\n//================================================================================\n//\tHID 'Driver'\n\nint\t\tHID_GetInterface(uint8_t* interfaceNum);\nint\t\tHID_GetDescriptor(int i);\nbool\tHID_Setup(Setup& setup);\nvoid\tHID_SendReport(uint8_t id, const void* data, int len);\n\n//================================================================================\n//================================================================================\n//\tMSC 'Driver'\n\nint\t\tMSC_GetInterface(uint8_t* interfaceNum);\nint\t\tMSC_GetDescriptor(int i);\nbool\tMSC_Setup(Setup& setup);\nbool\tMSC_Data(uint8_t rx,uint8_t tx);\n\n//================================================================================\n//================================================================================\n//\tCSC 'Driver'\n\nint\t\tCDC_GetInterface(uint8_t* interfaceNum);\nint\t\tCDC_GetDescriptor(int i);\nbool\tCDC_Setup(Setup& setup);\n\n//================================================================================\n//================================================================================\n\n#define TRANSFER_PGM\t\t0x80\n#define TRANSFER_RELEASE\t0x40\n#define TRANSFER_ZERO\t\t0x20\n\nint USB_SendControl(uint8_t flags, const void* d, int len);\nint USB_RecvControl(void* d, int len);\n\nuint8_t\tUSB_Available(uint8_t ep);\nint USB_Send(uint8_t ep, const void* data, int len);\t// blocking\nint USB_Recv(uint8_t ep, void* data, int len);\t\t// non-blocking\nint USB_Recv(uint8_t ep);\t\t\t\t\t\t\t// non-blocking\nvoid USB_Flush(uint8_t ep);\n\n#endif\n\n#endif /* if defined(USBCON) */"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/USBCore.cpp",
    "content": "\n\n/* Copyright (c) 2010, Peter Barrett  \n**  \n** Permission to use, copy, modify, and/or distribute this software for  \n** any purpose with or without fee is hereby granted, provided that the  \n** above copyright notice and this permission notice appear in all copies.  \n** \n** THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL  \n** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED  \n** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR  \n** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES  \n** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,  \n** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  \n** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS  \n** SOFTWARE.  \n*/\n\n#include \"Platform.h\"\n#include \"USBAPI.h\"\n#include \"USBDesc.h\"\n\n#if defined(USBCON)\n\n#define EP_TYPE_CONTROL\t\t\t\t0x00\n#define EP_TYPE_BULK_IN\t\t\t\t0x81\n#define EP_TYPE_BULK_OUT\t\t\t0x80\n#define EP_TYPE_INTERRUPT_IN\t\t0xC1\n#define EP_TYPE_INTERRUPT_OUT\t\t0xC0\n#define EP_TYPE_ISOCHRONOUS_IN\t\t0x41\n#define EP_TYPE_ISOCHRONOUS_OUT\t\t0x40\n\n/** Pulse generation counters to keep track of the number of milliseconds remaining for each pulse type */\n#define TX_RX_LED_PULSE_MS 100\nvolatile u8 TxLEDPulse; /**< Milliseconds remaining for data Tx LED pulse */\nvolatile u8 RxLEDPulse; /**< Milliseconds remaining for data Rx LED pulse */\n\n//==================================================================\n//==================================================================\n\nextern const u16 STRING_LANGUAGE[] PROGMEM;\nextern const u16 STRING_IPRODUCT[] PROGMEM;\nextern const u16 STRING_IMANUFACTURER[] PROGMEM;\nextern const DeviceDescriptor USB_DeviceDescriptor PROGMEM;\nextern const DeviceDescriptor USB_DeviceDescriptorA PROGMEM;\n\nconst u16 STRING_LANGUAGE[2] = {\n\t(3<<8) | (2+2),\n\t0x0409\t// English\n};\n\nconst u16 STRING_IPRODUCT[17] = {\n\t(3<<8) | (2+2*16),\n#if USB_PID == 0x8036\t\n\t'A','r','d','u','i','n','o',' ','L','e','o','n','a','r','d','o'\n#else\n\t'U','S','B',' ','I','O',' ','B','o','a','r','d',' ',' ',' ',' '\n#endif\n};\n\nconst u16 STRING_IMANUFACTURER[12] = {\n\t(3<<8) | (2+2*11),\n#if USB_VID == 0x2341\n\t'A','r','d','u','i','n','o',' ','L','L','C'\n#else\n\t'U','n','k','n','o','w','n',' ',' ',' ',' '\n#endif\n};\n\n#ifdef CDC_ENABLED\n#define DEVICE_CLASS 0x02\n#else\n#define DEVICE_CLASS 0x00\n#endif\n\n//\tDEVICE DESCRIPTOR\nconst DeviceDescriptor USB_DeviceDescriptor =\n\tD_DEVICE(0x00,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);\n\nconst DeviceDescriptor USB_DeviceDescriptorA =\n\tD_DEVICE(DEVICE_CLASS,0x00,0x00,64,USB_VID,USB_PID,0x100,IMANUFACTURER,IPRODUCT,0,1);\n\n//==================================================================\n//==================================================================\n\nvolatile u8 _usbConfiguration = 0;\n\nstatic inline void WaitIN(void)\n{\n\twhile (!(UEINTX & (1<<TXINI)));\n}\n\nstatic inline void ClearIN(void)\n{\n\tUEINTX = ~(1<<TXINI);\n}\n\nstatic inline void WaitOUT(void)\n{\n\twhile (!(UEINTX & (1<<RXOUTI)))\n\t\t;\n}\n\nstatic inline u8 WaitForINOrOUT()\n{\n\twhile (!(UEINTX & ((1<<TXINI)|(1<<RXOUTI))))\n\t\t;\n\treturn (UEINTX & (1<<RXOUTI)) == 0;\n}\n\nstatic inline void ClearOUT(void)\n{\n\tUEINTX = ~(1<<RXOUTI);\n}\n\nvoid Recv(volatile u8* data, u8 count)\n{\n\twhile (count--)\n\t\t*data++ = UEDATX;\n\t\n\tRXLED1;\t\t\t\t\t// light the RX LED\n\tRxLEDPulse = TX_RX_LED_PULSE_MS;\t\n}\n\nstatic inline u8 Recv8()\n{\n\tRXLED1;\t\t\t\t\t// light the RX LED\n\tRxLEDPulse = TX_RX_LED_PULSE_MS;\n\n\treturn UEDATX;\t\n}\n\nstatic inline void Send8(u8 d)\n{\n\tUEDATX = d;\n}\n\nstatic inline void SetEP(u8 ep)\n{\n\tUENUM = ep;\n}\n\nstatic inline u8 FifoByteCount()\n{\n\treturn UEBCLX;\n}\n\nstatic inline u8 ReceivedSetupInt()\n{\n\treturn UEINTX & (1<<RXSTPI);\n}\n\nstatic inline void ClearSetupInt()\n{\n\tUEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI));\n}\n\nstatic inline void Stall()\n{\n\tUECONX = (1<<STALLRQ) | (1<<EPEN);\n}\n\nstatic inline u8 ReadWriteAllowed()\n{\n\treturn UEINTX & (1<<RWAL);\n}\n\nstatic inline u8 Stalled()\n{\n\treturn UEINTX & (1<<STALLEDI);\n}\n\nstatic inline u8 FifoFree()\n{\n\treturn UEINTX & (1<<FIFOCON);\n}\n\nstatic inline void ReleaseRX()\n{\n\tUEINTX = 0x6B;\t// FIFOCON=0 NAKINI=1 RWAL=1 NAKOUTI=0 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=1\n}\n\nstatic inline void ReleaseTX()\n{\n\tUEINTX = 0x3A;\t// FIFOCON=0 NAKINI=0 RWAL=1 NAKOUTI=1 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=0\n}\n\nstatic inline u8 FrameNumber()\n{\n\treturn UDFNUML;\n}\n\n//==================================================================\n//==================================================================\n\nu8 USBGetConfiguration(void)\n{\n\treturn _usbConfiguration;\n}\n\n#define USB_RECV_TIMEOUT\nclass LockEP\n{\n\tu8 _sreg;\npublic:\n\tLockEP(u8 ep) : _sreg(SREG)\n\t{\n\t\tcli();\n\t\tSetEP(ep & 7);\n\t}\n\t~LockEP()\n\t{\n\t\tSREG = _sreg;\n\t}\n};\n\n//\tNumber of bytes, assumes a rx endpoint\nu8 USB_Available(u8 ep)\n{\n\tLockEP lock(ep);\n\treturn FifoByteCount();\n}\n\n//\tNon Blocking receive\n//\tReturn number of bytes read\nint USB_Recv(u8 ep, void* d, int len)\n{\n\tif (!_usbConfiguration || len < 0)\n\t\treturn -1;\n\t\n\tLockEP lock(ep);\n\tu8 n = FifoByteCount();\n\tlen = min(n,len);\n\tn = len;\n\tu8* dst = (u8*)d;\n\twhile (n--)\n\t\t*dst++ = Recv8();\n\tif (len && !FifoByteCount())\t// release empty buffer\n\t\tReleaseRX();\n\t\n\treturn len;\n}\n\n//\tRecv 1 byte if ready\nint USB_Recv(u8 ep)\n{\n\tu8 c;\n\tif (USB_Recv(ep,&c,1) != 1)\n\t\treturn -1;\n\treturn c;\n}\n\n//\tSpace in send EP\nu8 USB_SendSpace(u8 ep)\n{\n\tLockEP lock(ep);\n\tif (!ReadWriteAllowed())\n\t\treturn 0;\n\treturn 64 - FifoByteCount();\n}\n\n//\tBlocking Send of data to an endpoint\nint USB_Send(u8 ep, const void* d, int len)\n{\n\tif (!_usbConfiguration)\n\t\treturn -1;\n\n\tint r = len;\n\tconst u8* data = (const u8*)d;\n\tu8 zero = ep & TRANSFER_ZERO;\n\tu8 timeout = 250;\t\t// 250ms timeout on send? TODO\n\twhile (len)\n\t{\n\t\tu8 n = USB_SendSpace(ep);\n\t\tif (n == 0)\n\t\t{\n\t\t\tif (!(--timeout))\n\t\t\t\treturn -1;\n\t\t\tdelay(1);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (n > len)\n\t\t\tn = len;\n\t\tlen -= n;\n\t\t{\n\t\t\tLockEP lock(ep);\n\t\t\tif (ep & TRANSFER_ZERO)\n\t\t\t{\n\t\t\t\twhile (n--)\n\t\t\t\t\tSend8(0);\n\t\t\t}\n\t\t\telse if (ep & TRANSFER_PGM)\n\t\t\t{\n\t\t\t\twhile (n--)\n\t\t\t\t\tSend8(pgm_read_byte(data++));\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\twhile (n--)\n\t\t\t\t\tSend8(*data++);\n\t\t\t}\n\t\t\tif (!ReadWriteAllowed() || ((len == 0) && (ep & TRANSFER_RELEASE)))\t// Release full buffer\n\t\t\t\tReleaseTX();\n\t\t}\n\t}\n\tTXLED1;\t\t\t\t\t// light the TX LED\n\tTxLEDPulse = TX_RX_LED_PULSE_MS;\n\treturn r;\n}\n\nextern const u8 _initEndpoints[] PROGMEM;\nconst u8 _initEndpoints[] = \n{\n\t0,\n\t\n#ifdef CDC_ENABLED\n\tEP_TYPE_INTERRUPT_IN,\t\t// CDC_ENDPOINT_ACM\n\tEP_TYPE_BULK_OUT,\t\t\t// CDC_ENDPOINT_OUT\n\tEP_TYPE_BULK_IN,\t\t\t// CDC_ENDPOINT_IN\n#endif\n\n#ifdef HID_ENABLED\n\tEP_TYPE_INTERRUPT_IN\t\t// HID_ENDPOINT_INT\n#endif\n};\n\n#define EP_SINGLE_64 0x32\t// EP0\n#define EP_DOUBLE_64 0x36\t// Other endpoints\n\nstatic\nvoid InitEP(u8 index, u8 type, u8 size)\n{\n\tUENUM = index;\n\tUECONX = 1;\n\tUECFG0X = type;\n\tUECFG1X = size;\n}\n\nstatic\nvoid InitEndpoints()\n{\n\tfor (u8 i = 1; i < sizeof(_initEndpoints); i++)\n\t{\n\t\tUENUM = i;\n\t\tUECONX = 1;\n\t\tUECFG0X = pgm_read_byte(_initEndpoints+i);\n\t\tUECFG1X = EP_DOUBLE_64;\n\t}\n\tUERST = 0x7E;\t// And reset them\n\tUERST = 0;\n}\n\n//\tHandle CLASS_INTERFACE requests\nstatic\nbool ClassInterfaceRequest(Setup& setup)\n{\n\tu8 i = setup.wIndex;\n\n#ifdef CDC_ENABLED\n\tif (CDC_ACM_INTERFACE == i)\n\t\treturn CDC_Setup(setup);\n#endif\n\n#ifdef HID_ENABLED\n\tif (HID_INTERFACE == i)\n\t\treturn HID_Setup(setup);\n#endif\n\treturn false;\n}\n\nint _cmark;\nint _cend;\nvoid InitControl(int end)\n{\n\tSetEP(0);\n\t_cmark = 0;\n\t_cend = end;\n}\n\nstatic\nbool SendControl(u8 d)\n{\n\tif (_cmark < _cend)\n\t{\n\t\tif (!WaitForINOrOUT())\n\t\t\treturn false;\n\t\tSend8(d);\n\t\tif (!((_cmark + 1) & 0x3F))\n\t\t\tClearIN();\t// Fifo is full, release this packet\n\t}\n\t_cmark++;\n\treturn true;\n};\n\n//\tClipped by _cmark/_cend\nint USB_SendControl(u8 flags, const void* d, int len)\n{\n\tint sent = len;\n\tconst u8* data = (const u8*)d;\n\tbool pgm = flags & TRANSFER_PGM;\n\twhile (len--)\n\t{\n\t\tu8 c = pgm ? pgm_read_byte(data++) : *data++;\n\t\tif (!SendControl(c))\n\t\t\treturn -1;\n\t}\n\treturn sent;\n}\n\n//\tDoes not timeout or cross fifo boundaries\n//\tWill only work for transfers <= 64 bytes\n//\tTODO\nint USB_RecvControl(void* d, int len)\n{\n\tWaitOUT();\n\tRecv((u8*)d,len);\n\tClearOUT();\n\treturn len;\n}\n\nint SendInterfaces()\n{\n\tint total = 0;\n\tu8 interfaces = 0;\n\n#ifdef CDC_ENABLED\n\ttotal = CDC_GetInterface(&interfaces);\n#endif\n\n#ifdef HID_ENABLED\n\ttotal += HID_GetInterface(&interfaces);\n#endif\n\n\treturn interfaces;\n}\n\n//\tConstruct a dynamic configuration descriptor\n//\tThis really needs dynamic endpoint allocation etc\n//\tTODO\nstatic\nbool SendConfiguration(int maxlen)\n{\n\t//\tCount and measure interfaces\n\tInitControl(0);\t\n\tint interfaces = SendInterfaces();\n\tConfigDescriptor config = D_CONFIG(_cmark + sizeof(ConfigDescriptor),interfaces);\n\n\t//\tNow send them\n\tInitControl(maxlen);\n\tUSB_SendControl(0,&config,sizeof(ConfigDescriptor));\n\tSendInterfaces();\n\treturn true;\n}\n\nu8 _cdcComposite = 0;\n\nstatic\nbool SendDescriptor(Setup& setup)\n{\n\tu8 t = setup.wValueH;\n\tif (USB_CONFIGURATION_DESCRIPTOR_TYPE == t)\n\t\treturn SendConfiguration(setup.wLength);\n\n\tInitControl(setup.wLength);\n#ifdef HID_ENABLED\n\tif (HID_REPORT_DESCRIPTOR_TYPE == t)\n\t\treturn HID_GetDescriptor(t);\n#endif\n\n\tu8 desc_length = 0;\n\tconst u8* desc_addr = 0;\n\tif (USB_DEVICE_DESCRIPTOR_TYPE == t)\n\t{\n\t\tif (setup.wLength == 8)\n\t\t\t_cdcComposite = 1;\n\t\tdesc_addr = _cdcComposite ?  (const u8*)&USB_DeviceDescriptorA : (const u8*)&USB_DeviceDescriptor;\n\t}\n\telse if (USB_STRING_DESCRIPTOR_TYPE == t)\n\t{\n\t\tif (setup.wValueL == 0)\n\t\t\tdesc_addr = (const u8*)&STRING_LANGUAGE;\n\t\telse if (setup.wValueL == IPRODUCT) \n\t\t\tdesc_addr = (const u8*)&STRING_IPRODUCT;\n\t\telse if (setup.wValueL == IMANUFACTURER)\n\t\t\tdesc_addr = (const u8*)&STRING_IMANUFACTURER;\n\t\telse\n\t\t\treturn false;\n\t}\n\n\tif (desc_addr == 0)\n\t\treturn false;\n\tif (desc_length == 0)\n\t\tdesc_length = pgm_read_byte(desc_addr);\n\n\tUSB_SendControl(TRANSFER_PGM,desc_addr,desc_length);\n\treturn true;\n}\n\n//\tEndpoint 0 interrupt\nISR(USB_COM_vect)\n{\n    SetEP(0);\n\tif (!ReceivedSetupInt())\n\t\treturn;\n\n\tSetup setup;\n\tRecv((u8*)&setup,8);\n\tClearSetupInt();\n\n\tu8 requestType = setup.bmRequestType;\n\tif (requestType & REQUEST_DEVICETOHOST)\n\t\tWaitIN();\n\telse\n\t\tClearIN();\n\n    bool ok = true;\n\tif (REQUEST_STANDARD == (requestType & REQUEST_TYPE))\n\t{\n\t\t//\tStandard Requests\n\t\tu8 r = setup.bRequest;\n\t\tif (GET_STATUS == r)\n\t\t{\n\t\t\tSend8(0);\t\t// TODO\n\t\t\tSend8(0);\n\t\t}\n\t\telse if (CLEAR_FEATURE == r)\n\t\t{\n\t\t}\n\t\telse if (SET_FEATURE == r)\n\t\t{\n\t\t}\n\t\telse if (SET_ADDRESS == r)\n\t\t{\n\t\t\tWaitIN();\n\t\t\tUDADDR = setup.wValueL | (1<<ADDEN);\n\t\t}\n\t\telse if (GET_DESCRIPTOR == r)\n\t\t{\n\t\t\tok = SendDescriptor(setup);\n\t\t}\n\t\telse if (SET_DESCRIPTOR == r)\n\t\t{\n\t\t\tok = false;\n\t\t}\n\t\telse if (GET_CONFIGURATION == r)\n\t\t{\n\t\t\tSend8(1);\n\t\t}\n\t\telse if (SET_CONFIGURATION == r)\n\t\t{\n\t\t\tif (REQUEST_DEVICE == (requestType & REQUEST_RECIPIENT))\n\t\t\t{\n\t\t\t\tInitEndpoints();\n\t\t\t\t_usbConfiguration = setup.wValueL;\n\t\t\t} else\n\t\t\t\tok = false;\n\t\t}\n\t\telse if (GET_INTERFACE == r)\n\t\t{\n\t\t}\n\t\telse if (SET_INTERFACE == r)\n\t\t{\n\t\t}\n\t}\n\telse\n\t{\n\t\tInitControl(setup.wLength);\t\t//\tMax length of transfer\n\t\tok = ClassInterfaceRequest(setup);\n\t}\n\n\tif (ok)\n\t\tClearIN();\n\telse\n\t{\n\t\tStall();\n\t}\n}\n\nvoid USB_Flush(u8 ep)\n{\n\tSetEP(ep);\n\tif (FifoByteCount())\n\t\tReleaseTX();\n}\n\n//\tGeneral interrupt\nISR(USB_GEN_vect)\n{\n\tu8 udint = UDINT;\n\tUDINT = 0;\n\n\t//\tEnd of Reset\n\tif (udint & (1<<EORSTI))\n\t{\n\t\tInitEP(0,EP_TYPE_CONTROL,EP_SINGLE_64);\t// init ep0\n\t\t_usbConfiguration = 0;\t\t\t// not configured yet\n\t\tUEIENX = 1 << RXSTPE;\t\t\t// Enable interrupts for ep0\n\t}\n\n\t//\tStart of Frame - happens every millisecond so we use it for TX and RX LED one-shot timing, too\n\tif (udint & (1<<SOFI))\n\t{\n#ifdef CDC_ENABLED\n\t\tUSB_Flush(CDC_TX);\t\t\t\t// Send a tx frame if found\n\t\twhile (USB_Available(CDC_RX))\t// Handle received bytes (if any)\n\t\t\tSerial.accept();\n#endif\n\t\t\n\t\t// check whether the one-shot period has elapsed.  if so, turn off the LED\n\t\tif (TxLEDPulse && !(--TxLEDPulse))\n\t\t\tTXLED0;\n\t\tif (RxLEDPulse && !(--RxLEDPulse))\n\t\t\tRXLED0;\n\t}\n}\n\n//\tVBUS or counting frames\n//\tAny frame counting?\nu8 USBConnected()\n{\n\tu8 f = UDFNUML;\n\tdelay(3);\n\treturn f != UDFNUML;\n}\n\n//=======================================================================\n//=======================================================================\n\nUSBDevice_ USBDevice;\n\nUSBDevice_::USBDevice_()\n{\n}\n\nvoid USBDevice_::attach()\n{\n\t_usbConfiguration = 0;\n\tUHWCON = 0x01;\t\t\t\t\t\t// power internal reg\n\tUSBCON = (1<<USBE)|(1<<FRZCLK);\t\t// clock frozen, usb enabled\n\tPLLCSR = 0x12;\t\t\t\t\t\t// Need 16 MHz xtal\n\twhile (!(PLLCSR & (1<<PLOCK)))\t\t// wait for lock pll\n\t\t;\n\n\t// Some tests on specific versions of macosx (10.7.3), reported some\n\t// strange behaviuors when the board is reset using the serial\n\t// port touch at 1200 bps. This delay fixes this behaviour.\n\tdelay(1);\n\n\tUSBCON = ((1<<USBE)|(1<<OTGPADE));\t// start USB clock\n\tUDIEN = (1<<EORSTE)|(1<<SOFE);\t\t// Enable interrupts for EOR (End of Reset) and SOF (start of frame)\n\tUDCON = 0;\t\t\t\t\t\t\t// enable attach resistor\n\t\n\tTX_RX_LED_INIT;\n}\n\nvoid USBDevice_::detach()\n{\n}\n\n//\tCheck for interrupts\n//\tTODO: VBUS detection\nbool USBDevice_::configured()\n{\n\treturn _usbConfiguration;\n}\n\nvoid USBDevice_::poll()\n{\n}\n\n#endif /* if defined(USBCON) */\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/USBCore.h",
    "content": "\n// Copyright (c) 2010, Peter Barrett \n/*\n** Permission to use, copy, modify, and/or distribute this software for  \n** any purpose with or without fee is hereby granted, provided that the  \n** above copyright notice and this permission notice appear in all copies.  \n**  \n** THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL  \n** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED  \n** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR  \n** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES  \n** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,  \n** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  \n** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS  \n** SOFTWARE.  \n*/\n\n#ifndef __USBCORE_H__\n#define __USBCORE_H__\n\n//\tStandard requests\n#define GET_STATUS\t\t\t0\n#define CLEAR_FEATURE\t\t1\n#define SET_FEATURE\t\t\t3\n#define SET_ADDRESS\t\t\t5\n#define GET_DESCRIPTOR\t\t6\n#define SET_DESCRIPTOR\t\t7\n#define GET_CONFIGURATION\t8\n#define SET_CONFIGURATION\t9\n#define GET_INTERFACE\t\t10\n#define SET_INTERFACE\t\t11\n\n\n// bmRequestType\n#define REQUEST_HOSTTODEVICE\t0x00\n#define REQUEST_DEVICETOHOST\t0x80\n#define REQUEST_DIRECTION\t\t0x80\n\n#define REQUEST_STANDARD\t\t0x00\n#define REQUEST_CLASS\t\t\t0x20\n#define REQUEST_VENDOR\t\t\t0x40\n#define REQUEST_TYPE\t\t\t0x60\n\n#define REQUEST_DEVICE\t\t\t0x00\n#define REQUEST_INTERFACE\t\t0x01\n#define REQUEST_ENDPOINT\t\t0x02\n#define REQUEST_OTHER\t\t\t0x03\n#define REQUEST_RECIPIENT\t\t0x03\n\n#define REQUEST_DEVICETOHOST_CLASS_INTERFACE  (REQUEST_DEVICETOHOST + REQUEST_CLASS + REQUEST_INTERFACE)\n#define REQUEST_HOSTTODEVICE_CLASS_INTERFACE  (REQUEST_HOSTTODEVICE + REQUEST_CLASS + REQUEST_INTERFACE)\n\n//\tClass requests\n\n#define CDC_SET_LINE_CODING\t\t\t0x20\n#define CDC_GET_LINE_CODING\t\t\t0x21\n#define CDC_SET_CONTROL_LINE_STATE\t0x22\n\n#define MSC_RESET\t\t\t\t\t0xFF\n#define MSC_GET_MAX_LUN\t\t\t\t0xFE\n\n#define HID_GET_REPORT\t\t\t\t0x01\n#define HID_GET_IDLE\t\t\t\t0x02\n#define HID_GET_PROTOCOL\t\t\t0x03\n#define HID_SET_REPORT\t\t\t\t0x09\n#define HID_SET_IDLE\t\t\t\t0x0A\n#define HID_SET_PROTOCOL\t\t\t0x0B\n\n//\tDescriptors\n\n#define USB_DEVICE_DESC_SIZE 18\n#define USB_CONFIGUARTION_DESC_SIZE 9\n#define USB_INTERFACE_DESC_SIZE 9\n#define USB_ENDPOINT_DESC_SIZE 7\n\n#define USB_DEVICE_DESCRIPTOR_TYPE             1\n#define USB_CONFIGURATION_DESCRIPTOR_TYPE      2\n#define USB_STRING_DESCRIPTOR_TYPE             3\n#define USB_INTERFACE_DESCRIPTOR_TYPE          4\n#define USB_ENDPOINT_DESCRIPTOR_TYPE           5\n\n#define USB_DEVICE_CLASS_COMMUNICATIONS        0x02\n#define USB_DEVICE_CLASS_HUMAN_INTERFACE       0x03\n#define USB_DEVICE_CLASS_STORAGE               0x08\n#define USB_DEVICE_CLASS_VENDOR_SPECIFIC       0xFF\n\n#define USB_CONFIG_POWERED_MASK                0x40\n#define USB_CONFIG_BUS_POWERED                 0x80\n#define USB_CONFIG_SELF_POWERED                0xC0\n#define USB_CONFIG_REMOTE_WAKEUP               0x20\n\n// bMaxPower in Configuration Descriptor\n#define USB_CONFIG_POWER_MA(mA)                ((mA)/2)\n\n// bEndpointAddress in Endpoint Descriptor\n#define USB_ENDPOINT_DIRECTION_MASK            0x80\n#define USB_ENDPOINT_OUT(addr)                 ((addr) | 0x00)\n#define USB_ENDPOINT_IN(addr)                  ((addr) | 0x80)\n\n#define USB_ENDPOINT_TYPE_MASK                 0x03\n#define USB_ENDPOINT_TYPE_CONTROL              0x00\n#define USB_ENDPOINT_TYPE_ISOCHRONOUS          0x01\n#define USB_ENDPOINT_TYPE_BULK                 0x02\n#define USB_ENDPOINT_TYPE_INTERRUPT            0x03\n\n#define TOBYTES(x) ((x) & 0xFF),(((x) >> 8) & 0xFF)\n\n#define CDC_V1_10                               0x0110\n#define CDC_COMMUNICATION_INTERFACE_CLASS       0x02\n\n#define CDC_CALL_MANAGEMENT                     0x01\n#define CDC_ABSTRACT_CONTROL_MODEL              0x02\n#define CDC_HEADER                              0x00\n#define CDC_ABSTRACT_CONTROL_MANAGEMENT         0x02\n#define CDC_UNION                               0x06\n#define CDC_CS_INTERFACE                        0x24\n#define CDC_CS_ENDPOINT                         0x25\n#define CDC_DATA_INTERFACE_CLASS                0x0A\n\n#define MSC_SUBCLASS_SCSI\t\t\t\t\t\t0x06 \n#define MSC_PROTOCOL_BULK_ONLY\t\t\t\t\t0x50 \n\n#define HID_HID_DESCRIPTOR_TYPE\t\t\t\t\t0x21\n#define HID_REPORT_DESCRIPTOR_TYPE\t\t\t\t0x22\n#define HID_PHYSICAL_DESCRIPTOR_TYPE\t\t\t0x23\n\n\n//\tDevice\ntypedef struct {\n\tu8 len;\t\t\t\t// 18\n\tu8 dtype;\t\t\t// 1 USB_DEVICE_DESCRIPTOR_TYPE\n\tu16 usbVersion;\t\t// 0x200\n\tu8\tdeviceClass;\n\tu8\tdeviceSubClass;\n\tu8\tdeviceProtocol;\n\tu8\tpacketSize0;\t// Packet 0\n\tu16\tidVendor;\n\tu16\tidProduct;\n\tu16\tdeviceVersion;\t// 0x100\n\tu8\tiManufacturer;\n\tu8\tiProduct;\n\tu8\tiSerialNumber;\n\tu8\tbNumConfigurations;\n} DeviceDescriptor;\n\n//\tConfig\ntypedef struct {\n\tu8\tlen;\t\t\t// 9\n\tu8\tdtype;\t\t\t// 2\n\tu16 clen;\t\t\t// total length\n\tu8\tnumInterfaces;\n\tu8\tconfig;\n\tu8\ticonfig;\n\tu8\tattributes;\n\tu8\tmaxPower;\n} ConfigDescriptor;\n\n//\tString\n\n//\tInterface\ntypedef struct\n{\n\tu8 len;\t\t// 9\n\tu8 dtype;\t// 4\n\tu8 number;\n\tu8 alternate;\n\tu8 numEndpoints;\n\tu8 interfaceClass;\n\tu8 interfaceSubClass;\n\tu8 protocol;\n\tu8 iInterface;\n} InterfaceDescriptor;\n\n//\tEndpoint\ntypedef struct\n{\n\tu8 len;\t\t// 7\n\tu8 dtype;\t// 5\n\tu8 addr;\n\tu8 attr;\n\tu16 packetSize;\n\tu8 interval;\n} EndpointDescriptor;\n\n// Interface Association Descriptor\n// Used to bind 2 interfaces together in CDC compostite device\ntypedef struct\n{\n\tu8 len;\t\t\t\t// 8\n\tu8 dtype;\t\t\t// 11\n\tu8 firstInterface;\n\tu8 interfaceCount;\n\tu8 functionClass;\n\tu8 funtionSubClass;\n\tu8 functionProtocol;\n\tu8 iInterface;\n} IADDescriptor;\n\n//\tCDC CS interface descriptor\ntypedef struct\n{\n\tu8 len;\t\t// 5\n\tu8 dtype;\t// 0x24\n\tu8 subtype;\n\tu8 d0;\n\tu8 d1;\n} CDCCSInterfaceDescriptor;\n\ntypedef struct\n{\n\tu8 len;\t\t// 4\n\tu8 dtype;\t// 0x24\n\tu8 subtype;\n\tu8 d0;\n} CDCCSInterfaceDescriptor4;\n\ntypedef struct \n{\n    u8\tlen;\n    u8 \tdtype;\t\t// 0x24\n    u8 \tsubtype;\t// 1\n    u8 \tbmCapabilities;\n    u8 \tbDataInterface;\n} CMFunctionalDescriptor;\n\t\ntypedef struct \n{\n    u8\tlen;\n    u8 \tdtype;\t\t// 0x24\n    u8 \tsubtype;\t// 1\n    u8 \tbmCapabilities;\n} ACMFunctionalDescriptor;\n\ntypedef struct \n{\n\t//\tIAD\n\tIADDescriptor\t\t\t\tiad;\t// Only needed on compound device\n\n\t//\tControl\n\tInterfaceDescriptor\t\t\tcif;\t// \n\tCDCCSInterfaceDescriptor\theader;\n\tCMFunctionalDescriptor\t\tcallManagement;\t\t\t// Call Management\n\tACMFunctionalDescriptor\t\tcontrolManagement;\t\t// ACM\n\tCDCCSInterfaceDescriptor\tfunctionalDescriptor;\t// CDC_UNION\n\tEndpointDescriptor\t\t\tcifin;\n\n\t//\tData\n\tInterfaceDescriptor\t\t\tdif;\n\tEndpointDescriptor\t\t\tin;\n\tEndpointDescriptor\t\t\tout;\n} CDCDescriptor;\n\ntypedef struct \n{\n\tInterfaceDescriptor\t\t\tmsc;\n\tEndpointDescriptor\t\t\tin;\n\tEndpointDescriptor\t\t\tout;\n} MSCDescriptor;\n\ntypedef struct\n{\n\tu8 len;\t\t\t// 9\n\tu8 dtype;\t\t// 0x21\n\tu8 addr;\n\tu8\tversionL;\t// 0x101\n\tu8\tversionH;\t// 0x101\n\tu8\tcountry;\n\tu8\tdesctype;\t// 0x22 report\n\tu8\tdescLenL;\n\tu8\tdescLenH;\n} HIDDescDescriptor;\n\ntypedef struct \n{\n\tInterfaceDescriptor\t\t\thid;\n\tHIDDescDescriptor\t\t\tdesc;\n\tEndpointDescriptor\t\t\tin;\n} HIDDescriptor;\n\n\n#define D_DEVICE(_class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs) \\\n\t{ 18, 1, 0x200, _class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_configs }\n\n#define D_CONFIG(_totalLength,_interfaces) \\\n\t{ 9, 2, _totalLength,_interfaces, 1, 0, USB_CONFIG_BUS_POWERED, USB_CONFIG_POWER_MA(500) }\n\n#define D_INTERFACE(_n,_numEndpoints,_class,_subClass,_protocol) \\\n\t{ 9, 4, _n, 0, _numEndpoints, _class,_subClass, _protocol, 0 }\n\n#define D_ENDPOINT(_addr,_attr,_packetSize, _interval) \\\n\t{ 7, 5, _addr,_attr,_packetSize, _interval }\n\n#define D_IAD(_firstInterface, _count, _class, _subClass, _protocol) \\\n\t{ 8, 11, _firstInterface, _count, _class, _subClass, _protocol, 0 }\n\n#define D_HIDREPORT(_descriptorLength) \\\n\t{ 9, 0x21, 0x1, 0x1, 0, 1, 0x22, _descriptorLength, 0 }\n\n#define D_CDCCS(_subtype,_d0,_d1)\t{ 5, 0x24, _subtype, _d0, _d1 }\n#define D_CDCCS4(_subtype,_d0)\t\t{ 4, 0x24, _subtype, _d0 }\n\n\n#endif"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/USBDesc.h",
    "content": "\n\n/* Copyright (c) 2011, Peter Barrett  \n**  \n** Permission to use, copy, modify, and/or distribute this software for  \n** any purpose with or without fee is hereby granted, provided that the  \n** above copyright notice and this permission notice appear in all copies.  \n** \n** THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL  \n** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED  \n** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR  \n** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES  \n** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,  \n** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  \n** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS  \n** SOFTWARE.  \n*/\n\n#define CDC_ENABLED\n#define HID_ENABLED\n\n\n#ifdef CDC_ENABLED\n#define CDC_INTERFACE_COUNT\t2\n#define CDC_ENPOINT_COUNT\t3\n#else\n#define CDC_INTERFACE_COUNT\t0\n#define CDC_ENPOINT_COUNT\t0\n#endif\n\n#ifdef HID_ENABLED\n#define HID_INTERFACE_COUNT\t1\n#define HID_ENPOINT_COUNT\t1\n#else\n#define HID_INTERFACE_COUNT\t0\n#define HID_ENPOINT_COUNT\t0\n#endif\n\n#define CDC_ACM_INTERFACE\t0\t// CDC ACM\n#define CDC_DATA_INTERFACE\t1\t// CDC Data\n#define CDC_FIRST_ENDPOINT\t1\n#define CDC_ENDPOINT_ACM\t(CDC_FIRST_ENDPOINT)\t\t\t\t\t\t\t// CDC First\n#define CDC_ENDPOINT_OUT\t(CDC_FIRST_ENDPOINT+1)\n#define CDC_ENDPOINT_IN\t\t(CDC_FIRST_ENDPOINT+2)\n\n#define HID_INTERFACE\t\t(CDC_ACM_INTERFACE + CDC_INTERFACE_COUNT)\t\t// HID Interface\n#define HID_FIRST_ENDPOINT\t(CDC_FIRST_ENDPOINT + CDC_ENPOINT_COUNT)\n#define HID_ENDPOINT_INT\t(HID_FIRST_ENDPOINT)\n\n#define INTERFACE_COUNT\t\t(MSC_INTERFACE + MSC_INTERFACE_COUNT)\n\n#ifdef CDC_ENABLED\n#define CDC_RX CDC_ENDPOINT_OUT\n#define CDC_TX CDC_ENDPOINT_IN\n#endif\n\n#ifdef HID_ENABLED\n#define HID_TX HID_ENDPOINT_INT\n#endif\n\n#define IMANUFACTURER\t1\n#define IPRODUCT\t\t2\n\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/Udp.h",
    "content": "/*\n *  Udp.cpp: Library to send/receive UDP packets.\n *\n * NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these)\n * 1) UDP does not guarantee the order in which assembled UDP packets are received. This\n * might not happen often in practice, but in larger network topologies, a UDP\n * packet can be received out of sequence. \n * 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being\n * aware of it. Again, this may not be a concern in practice on small local networks.\n * For more information, see http://www.cafeaulait.org/course/week12/35.html\n *\n * MIT License:\n * Copyright (c) 2008 Bjoern Hartmann\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n * bjoern@cs.stanford.edu 12/30/2008\n */\n\n#ifndef udp_h\n#define udp_h\n\n#include <Stream.h>\n#include <IPAddress.h>\n\nclass UDP : public Stream {\n\npublic:\n  virtual uint8_t begin(uint16_t) =0;\t// initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use\n  virtual void stop() =0;  // Finish with the UDP socket\n\n  // Sending UDP packets\n  \n  // Start building up a packet to send to the remote host specific in ip and port\n  // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port\n  virtual int beginPacket(IPAddress ip, uint16_t port) =0;\n  // Start building up a packet to send to the remote host specific in host and port\n  // Returns 1 if successful, 0 if there was a problem resolving the hostname or port\n  virtual int beginPacket(const char *host, uint16_t port) =0;\n  // Finish off this packet and send it\n  // Returns 1 if the packet was sent successfully, 0 if there was an error\n  virtual int endPacket() =0;\n  // Write a single byte into the packet\n  virtual size_t write(uint8_t) =0;\n  // Write size bytes from buffer into the packet\n  virtual size_t write(const uint8_t *buffer, size_t size) =0;\n\n  // Start processing the next available incoming packet\n  // Returns the size of the packet in bytes, or 0 if no packets are available\n  virtual int parsePacket() =0;\n  // Number of bytes remaining in the current packet\n  virtual int available() =0;\n  // Read a single byte from the current packet\n  virtual int read() =0;\n  // Read up to len bytes from the current packet and place them into buffer\n  // Returns the number of bytes read, or 0 if none are available\n  virtual int read(unsigned char* buffer, size_t len) =0;\n  // Read up to len characters from the current packet and place them into buffer\n  // Returns the number of characters read, or 0 if none are available\n  virtual int read(char* buffer, size_t len) =0;\n  // Return the next byte from the current packet without moving on to the next byte\n  virtual int peek() =0;\n  virtual void flush() =0;\t// Finish reading the current packet\n\n  // Return the IP address of the host who sent the current incoming packet\n  virtual IPAddress remoteIP() =0;\n  // Return the port of the host who sent the current incoming packet\n  virtual uint16_t remotePort() =0;\nprotected:\n  uint8_t* rawIPAddress(IPAddress& addr) { return addr.raw_address(); };\n};\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/WCharacter.h",
    "content": "/*\n WCharacter.h - Character utility functions for Wiring & Arduino\n Copyright (c) 2010 Hernando Barragan.  All right reserved.\n \n This library is free software; you can redistribute it and/or\n modify it under the terms of the GNU Lesser General Public\n License as published by the Free Software Foundation; either\n version 2.1 of the License, or (at your option) any later version.\n \n This library is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n Lesser General Public License for more details.\n \n You should have received a copy of the GNU Lesser General Public\n License along with this library; if not, write to the Free Software\n Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n */\n\n#ifndef Character_h\n#define Character_h\n\n#include <ctype.h>\n\n// WCharacter.h prototypes\ninline boolean isAlphaNumeric(int c) __attribute__((always_inline));\ninline boolean isAlpha(int c) __attribute__((always_inline));\ninline boolean isAscii(int c) __attribute__((always_inline));\ninline boolean isWhitespace(int c) __attribute__((always_inline));\ninline boolean isControl(int c) __attribute__((always_inline));\ninline boolean isDigit(int c) __attribute__((always_inline));\ninline boolean isGraph(int c) __attribute__((always_inline));\ninline boolean isLowerCase(int c) __attribute__((always_inline));\ninline boolean isPrintable(int c) __attribute__((always_inline));\ninline boolean isPunct(int c) __attribute__((always_inline));\ninline boolean isSpace(int c) __attribute__((always_inline));\ninline boolean isUpperCase(int c) __attribute__((always_inline));\ninline boolean isHexadecimalDigit(int c) __attribute__((always_inline));\ninline int toAscii(int c) __attribute__((always_inline));\ninline int toLowerCase(int c) __attribute__((always_inline));\ninline int toUpperCase(int c)__attribute__((always_inline));\n\n\n// Checks for an alphanumeric character. \n// It is equivalent to (isalpha(c) || isdigit(c)).\ninline boolean isAlphaNumeric(int c) \n{\n  return ( isalnum(c) == 0 ? false : true);\n}\n\n\n// Checks for an alphabetic character. \n// It is equivalent to (isupper(c) || islower(c)).\ninline boolean isAlpha(int c)\n{\n  return ( isalpha(c) == 0 ? false : true);\n}\n\n\n// Checks whether c is a 7-bit unsigned char value \n// that fits into the ASCII character set.\ninline boolean isAscii(int c)\n{\n  return ( isascii (c) == 0 ? false : true);\n}\n\n\n// Checks for a blank character, that is, a space or a tab.\ninline boolean isWhitespace(int c)\n{\n  return ( isblank (c) == 0 ? false : true);\n}\n\n\n// Checks for a control character.\ninline boolean isControl(int c)\n{\n  return ( iscntrl (c) == 0 ? false : true);\n}\n\n\n// Checks for a digit (0 through 9).\ninline boolean isDigit(int c)\n{\n  return ( isdigit (c) == 0 ? false : true);\n}\n\n\n// Checks for any printable character except space.\ninline boolean isGraph(int c)\n{\n  return ( isgraph (c) == 0 ? false : true);\n}\n\n\n// Checks for a lower-case character.\ninline boolean isLowerCase(int c)\n{\n  return (islower (c) == 0 ? false : true);\n}\n\n\n// Checks for any printable character including space.\ninline boolean isPrintable(int c)\n{\n  return ( isprint (c) == 0 ? false : true);\n}\n\n\n// Checks for any printable character which is not a space \n// or an alphanumeric character.\ninline boolean isPunct(int c)\n{\n  return ( ispunct (c) == 0 ? false : true);\n}\n\n\n// Checks for white-space characters. For the avr-libc library, \n// these are: space, formfeed ('\\f'), newline ('\\n'), carriage \n// return ('\\r'), horizontal tab ('\\t'), and vertical tab ('\\v').\ninline boolean isSpace(int c)\n{\n  return ( isspace (c) == 0 ? false : true);\n}\n\n\n// Checks for an uppercase letter.\ninline boolean isUpperCase(int c)\n{\n  return ( isupper (c) == 0 ? false : true);\n}\n\n\n// Checks for a hexadecimal digits, i.e. one of 0 1 2 3 4 5 6 7 \n// 8 9 a b c d e f A B C D E F.\ninline boolean isHexadecimalDigit(int c)\n{\n  return ( isxdigit (c) == 0 ? false : true);\n}\n\n\n// Converts c to a 7-bit unsigned char value that fits into the \n// ASCII character set, by clearing the high-order bits.\ninline int toAscii(int c)\n{\n  return toascii (c);\n}\n\n\n// Warning:\n// Many people will be unhappy if you use this function. \n// This function will convert accented letters into random \n// characters.\n\n// Converts the letter c to lower case, if possible.\ninline int toLowerCase(int c)\n{\n  return tolower (c);\n}\n\n\n// Converts the letter c to upper case, if possible.\ninline int toUpperCase(int c)\n{\n  return toupper (c);\n}\n\n#endif"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/WInterrupts.c",
    "content": "/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */\n\n/*\n  Part of the Wiring project - http://wiring.uniandes.edu.co\n\n  Copyright (c) 2004-05 Hernando Barragan\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n  \n  Modified 24 November 2006 by David A. Mellis\n  Modified 1 August 2010 by Mark Sproul\n*/\n\n#include <inttypes.h>\n#include <avr/io.h>\n#include <avr/interrupt.h>\n#include <avr/pgmspace.h>\n#include <stdio.h>\n\n#include \"wiring_private.h\"\n\nstatic volatile voidFuncPtr intFunc[EXTERNAL_NUM_INTERRUPTS];\n// volatile static voidFuncPtr twiIntFunc;\n\nvoid attachInterrupt(uint8_t interruptNum, void (*userFunc)(void), int mode) {\n  if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {\n    intFunc[interruptNum] = userFunc;\n    \n    // Configure the interrupt mode (trigger on low input, any change, rising\n    // edge, or falling edge).  The mode constants were chosen to correspond\n    // to the configuration bits in the hardware register, so we simply shift\n    // the mode into place.\n      \n    // Enable the interrupt.\n      \n    switch (interruptNum) {\n#if defined(__AVR_ATmega32U4__)\n\t// I hate doing this, but the register assignment differs between the 1280/2560\n\t// and the 32U4.  Since avrlib defines registers PCMSK1 and PCMSK2 that aren't \n\t// even present on the 32U4 this is the only way to distinguish between them.\n\tcase 0:\n\t\tEICRA = (EICRA & ~((1<<ISC00) | (1<<ISC01))) | (mode << ISC00);\n\t\tEIMSK |= (1<<INT0);\n\t\tbreak;\n\tcase 1:\n\t\tEICRA = (EICRA & ~((1<<ISC10) | (1<<ISC11))) | (mode << ISC10);\n\t\tEIMSK |= (1<<INT1);\n\t\tbreak;\t\n#elif defined(EICRA) && defined(EICRB) && defined(EIMSK)\n    case 2:\n      EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);\n      EIMSK |= (1 << INT0);\n      break;\n    case 3:\n      EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);\n      EIMSK |= (1 << INT1);\n      break;\n    case 4:\n      EICRA = (EICRA & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);\n      EIMSK |= (1 << INT2);\n      break;\n    case 5:\n      EICRA = (EICRA & ~((1 << ISC30) | (1 << ISC31))) | (mode << ISC30);\n      EIMSK |= (1 << INT3);\n      break;\n    case 0:\n      EICRB = (EICRB & ~((1 << ISC40) | (1 << ISC41))) | (mode << ISC40);\n      EIMSK |= (1 << INT4);\n      break;\n    case 1:\n      EICRB = (EICRB & ~((1 << ISC50) | (1 << ISC51))) | (mode << ISC50);\n      EIMSK |= (1 << INT5);\n      break;\n    case 6:\n      EICRB = (EICRB & ~((1 << ISC60) | (1 << ISC61))) | (mode << ISC60);\n      EIMSK |= (1 << INT6);\n      break;\n    case 7:\n      EICRB = (EICRB & ~((1 << ISC70) | (1 << ISC71))) | (mode << ISC70);\n      EIMSK |= (1 << INT7);\n      break;\n#else\t\t\n    case 0:\n    #if defined(EICRA) && defined(ISC00) && defined(EIMSK)\n      EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);\n      EIMSK |= (1 << INT0);\n    #elif defined(MCUCR) && defined(ISC00) && defined(GICR)\n      MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);\n      GICR |= (1 << INT0);\n    #elif defined(MCUCR) && defined(ISC00) && defined(GIMSK)\n      MCUCR = (MCUCR & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);\n      GIMSK |= (1 << INT0);\n    #else\n      #error attachInterrupt not finished for this CPU (case 0)\n    #endif\n      break;\n\n    case 1:\n    #if defined(EICRA) && defined(ISC10) && defined(ISC11) && defined(EIMSK)\n      EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);\n      EIMSK |= (1 << INT1);\n    #elif defined(MCUCR) && defined(ISC10) && defined(ISC11) && defined(GICR)\n      MCUCR = (MCUCR & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);\n      GICR |= (1 << INT1);\n    #elif defined(MCUCR) && defined(ISC10) && defined(GIMSK) && defined(GIMSK)\n      MCUCR = (MCUCR & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);\n      GIMSK |= (1 << INT1);\n    #else\n      #warning attachInterrupt may need some more work for this cpu (case 1)\n    #endif\n      break;\n    \n    case 2:\n    #if defined(EICRA) && defined(ISC20) && defined(ISC21) && defined(EIMSK)\n      EICRA = (EICRA & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);\n      EIMSK |= (1 << INT2);\n    #elif defined(MCUCR) && defined(ISC20) && defined(ISC21) && defined(GICR)\n      MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);\n      GICR |= (1 << INT2);\n    #elif defined(MCUCR) && defined(ISC20) && defined(GIMSK) && defined(GIMSK)\n      MCUCR = (MCUCR & ~((1 << ISC20) | (1 << ISC21))) | (mode << ISC20);\n      GIMSK |= (1 << INT2);\n    #endif\n      break;\n#endif\n    }\n  }\n}\n\nvoid detachInterrupt(uint8_t interruptNum) {\n  if(interruptNum < EXTERNAL_NUM_INTERRUPTS) {\n    // Disable the interrupt.  (We can't assume that interruptNum is equal\n    // to the number of the EIMSK bit to clear, as this isn't true on the \n    // ATmega8.  There, INT0 is 6 and INT1 is 7.)\n    switch (interruptNum) {\n#if defined(__AVR_ATmega32U4__)\n\tcase 0:\n\t\tEIMSK &= ~(1<<INT0);\n\t\tbreak;\n\tcase 1:\n\t\tEIMSK &= ~(1<<INT1);\n\t\tbreak;\t\t\n#elif defined(EICRA) && defined(EICRB) && defined(EIMSK)\n    case 2:\n      EIMSK &= ~(1 << INT0);\n      break;\n    case 3:\n      EIMSK &= ~(1 << INT1);\n      break;\n    case 4:\n      EIMSK &= ~(1 << INT2);\n      break;\n    case 5:\n      EIMSK &= ~(1 << INT3);\n      break;\n    case 0:\n      EIMSK &= ~(1 << INT4);\n      break;\n    case 1:\n      EIMSK &= ~(1 << INT5);\n      break;\n    case 6:\n      EIMSK &= ~(1 << INT6);\n      break;\n    case 7:\n      EIMSK &= ~(1 << INT7);\n      break;\n#else\n    case 0:\n    #if defined(EIMSK) && defined(INT0)\n      EIMSK &= ~(1 << INT0);\n    #elif defined(GICR) && defined(ISC00)\n      GICR &= ~(1 << INT0); // atmega32\n    #elif defined(GIMSK) && defined(INT0)\n      GIMSK &= ~(1 << INT0);\n    #else\n      #error detachInterrupt not finished for this cpu\n    #endif\n      break;\n\n    case 1:\n    #if defined(EIMSK) && defined(INT1)\n      EIMSK &= ~(1 << INT1);\n    #elif defined(GICR) && defined(INT1)\n      GICR &= ~(1 << INT1); // atmega32\n    #elif defined(GIMSK) && defined(INT1)\n      GIMSK &= ~(1 << INT1);\n    #else\n      #warning detachInterrupt may need some more work for this cpu (case 1)\n    #endif\n      break;\n#endif\n    }\n      \n    intFunc[interruptNum] = 0;\n  }\n}\n\n/*\nvoid attachInterruptTwi(void (*userFunc)(void) ) {\n  twiIntFunc = userFunc;\n}\n*/\n\n#if defined(__AVR_ATmega32U4__)\nSIGNAL(INT0_vect) {\n\tif(intFunc[EXTERNAL_INT_0])\n\t\tintFunc[EXTERNAL_INT_0]();\n}\n\nSIGNAL(INT1_vect) {\n\tif(intFunc[EXTERNAL_INT_1])\n\t\tintFunc[EXTERNAL_INT_1]();\n}\n\n#elif defined(EICRA) && defined(EICRB)\n\nSIGNAL(INT0_vect) {\n  if(intFunc[EXTERNAL_INT_2])\n    intFunc[EXTERNAL_INT_2]();\n}\n\nSIGNAL(INT1_vect) {\n  if(intFunc[EXTERNAL_INT_3])\n    intFunc[EXTERNAL_INT_3]();\n}\n\nSIGNAL(INT2_vect) {\n  if(intFunc[EXTERNAL_INT_4])\n    intFunc[EXTERNAL_INT_4]();\n}\n\nSIGNAL(INT3_vect) {\n  if(intFunc[EXTERNAL_INT_5])\n    intFunc[EXTERNAL_INT_5]();\n}\n\nSIGNAL(INT4_vect) {\n  if(intFunc[EXTERNAL_INT_0])\n    intFunc[EXTERNAL_INT_0]();\n}\n\nSIGNAL(INT5_vect) {\n  if(intFunc[EXTERNAL_INT_1])\n    intFunc[EXTERNAL_INT_1]();\n}\n\nSIGNAL(INT6_vect) {\n  if(intFunc[EXTERNAL_INT_6])\n    intFunc[EXTERNAL_INT_6]();\n}\n\nSIGNAL(INT7_vect) {\n  if(intFunc[EXTERNAL_INT_7])\n    intFunc[EXTERNAL_INT_7]();\n}\n\n#else\n\nSIGNAL(INT0_vect) {\n  if(intFunc[EXTERNAL_INT_0])\n    intFunc[EXTERNAL_INT_0]();\n}\n\nSIGNAL(INT1_vect) {\n  if(intFunc[EXTERNAL_INT_1])\n    intFunc[EXTERNAL_INT_1]();\n}\n\n#if defined(EICRA) && defined(ISC20)\nSIGNAL(INT2_vect) {\n  if(intFunc[EXTERNAL_INT_2])\n    intFunc[EXTERNAL_INT_2]();\n}\n#endif\n\n#endif\n\n/*\nSIGNAL(SIG_2WIRE_SERIAL) {\n  if(twiIntFunc)\n    twiIntFunc();\n}\n*/\n\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/WMath.cpp",
    "content": "/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */\n\n/*\n  Part of the Wiring project - http://wiring.org.co\n  Copyright (c) 2004-06 Hernando Barragan\n  Modified 13 August 2006, David A. Mellis for Arduino - http://www.arduino.cc/\n  \n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n  \n  $Id$\n*/\n\nextern \"C\" {\n  #include \"stdlib.h\"\n}\n\nvoid randomSeed(unsigned int seed)\n{\n  if (seed != 0) {\n    srandom(seed);\n  }\n}\n\nlong random(long howbig)\n{\n  if (howbig == 0) {\n    return 0;\n  }\n  return random() % howbig;\n}\n\nlong random(long howsmall, long howbig)\n{\n  if (howsmall >= howbig) {\n    return howsmall;\n  }\n  long diff = howbig - howsmall;\n  return random(diff) + howsmall;\n}\n\nlong map(long x, long in_min, long in_max, long out_min, long out_max)\n{\n  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;\n}\n\nunsigned int makeWord(unsigned int w) { return w; }\nunsigned int makeWord(unsigned char h, unsigned char l) { return (h << 8) | l; }"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/WString.cpp",
    "content": "/*\n  WString.cpp - String library for Wiring & Arduino\n  ...mostly rewritten by Paul Stoffregen...\n  Copyright (c) 2009-10 Hernando Barragan.  All rights reserved.\n  Copyright 2011, Paul Stoffregen, paul@pjrc.com\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General Public\n  License along with this library; if not, write to the Free Software\n  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n*/\n\n#include \"WString.h\"\n\n\n/*********************************************/\n/*  Constructors                             */\n/*********************************************/\n\nString::String(const char *cstr)\n{\n\tinit();\n\tif (cstr) copy(cstr, strlen(cstr));\n}\n\nString::String(const String &value)\n{\n\tinit();\n\t*this = value;\n}\n\n#ifdef __GXX_EXPERIMENTAL_CXX0X__\nString::String(String &&rval)\n{\n\tinit();\n\tmove(rval);\n}\nString::String(StringSumHelper &&rval)\n{\n\tinit();\n\tmove(rval);\n}\n#endif\n\nString::String(char c)\n{\n\tinit();\n\tchar buf[2];\n\tbuf[0] = c;\n\tbuf[1] = 0;\n\t*this = buf;\n}\n\nString::String(unsigned char value, unsigned char base)\n{\n\tinit();\n\tchar buf[9];\n\tutoa(value, buf, base);\n\t*this = buf;\n}\n\nString::String(int value, unsigned char base)\n{\n\tinit();\n\tchar buf[18];\n\titoa(value, buf, base);\n\t*this = buf;\n}\n\nString::String(unsigned int value, unsigned char base)\n{\n\tinit();\n\tchar buf[17];\n\tutoa(value, buf, base);\n\t*this = buf;\n}\n\nString::String(long value, unsigned char base)\n{\n\tinit();\n\tchar buf[34];\n\tltoa(value, buf, base);\n\t*this = buf;\n}\n\nString::String(unsigned long value, unsigned char base)\n{\n\tinit();\n\tchar buf[33];\n\tultoa(value, buf, base);\n\t*this = buf;\n}\n\nString::~String()\n{\n\tfree(buffer);\n}\n\n/*********************************************/\n/*  Memory Management                        */\n/*********************************************/\n\ninline void String::init(void)\n{\n\tbuffer = NULL;\n\tcapacity = 0;\n\tlen = 0;\n\tflags = 0;\n}\n\nvoid String::invalidate(void)\n{\n\tif (buffer) free(buffer);\n\tbuffer = NULL;\n\tcapacity = len = 0;\n}\n\nunsigned char String::reserve(unsigned int size)\n{\n\tif (buffer && capacity >= size) return 1;\n\tif (changeBuffer(size)) {\n\t\tif (len == 0) buffer[0] = 0;\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\nunsigned char String::changeBuffer(unsigned int maxStrLen)\n{\n\tchar *newbuffer = (char *)realloc(buffer, maxStrLen + 1);\n\tif (newbuffer) {\n\t\tbuffer = newbuffer;\n\t\tcapacity = maxStrLen;\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n\n/*********************************************/\n/*  Copy and Move                            */\n/*********************************************/\n\nString & String::copy(const char *cstr, unsigned int length)\n{\n\tif (!reserve(length)) {\n\t\tinvalidate();\n\t\treturn *this;\n\t}\n\tlen = length;\n\tstrcpy(buffer, cstr);\n\treturn *this;\n}\n\n#ifdef __GXX_EXPERIMENTAL_CXX0X__\nvoid String::move(String &rhs)\n{\n\tif (buffer) {\n\t\tif (capacity >= rhs.len) {\n\t\t\tstrcpy(buffer, rhs.buffer);\n\t\t\tlen = rhs.len;\n\t\t\trhs.len = 0;\n\t\t\treturn;\n\t\t} else {\n\t\t\tfree(buffer);\n\t\t}\n\t}\n\tbuffer = rhs.buffer;\n\tcapacity = rhs.capacity;\n\tlen = rhs.len;\n\trhs.buffer = NULL;\n\trhs.capacity = 0;\n\trhs.len = 0;\n}\n#endif\n\nString & String::operator = (const String &rhs)\n{\n\tif (this == &rhs) return *this;\n\t\n\tif (rhs.buffer) copy(rhs.buffer, rhs.len);\n\telse invalidate();\n\t\n\treturn *this;\n}\n\n#ifdef __GXX_EXPERIMENTAL_CXX0X__\nString & String::operator = (String &&rval)\n{\n\tif (this != &rval) move(rval);\n\treturn *this;\n}\n\nString & String::operator = (StringSumHelper &&rval)\n{\n\tif (this != &rval) move(rval);\n\treturn *this;\n}\n#endif\n\nString & String::operator = (const char *cstr)\n{\n\tif (cstr) copy(cstr, strlen(cstr));\n\telse invalidate();\n\t\n\treturn *this;\n}\n\n/*********************************************/\n/*  concat                                   */\n/*********************************************/\n\nunsigned char String::concat(const String &s)\n{\n\treturn concat(s.buffer, s.len);\n}\n\nunsigned char String::concat(const char *cstr, unsigned int length)\n{\n\tunsigned int newlen = len + length;\n\tif (!cstr) return 0;\n\tif (length == 0) return 1;\n\tif (!reserve(newlen)) return 0;\n\tstrcpy(buffer + len, cstr);\n\tlen = newlen;\n\treturn 1;\n}\n\nunsigned char String::concat(const char *cstr)\n{\n\tif (!cstr) return 0;\n\treturn concat(cstr, strlen(cstr));\n}\n\nunsigned char String::concat(char c)\n{\n\tchar buf[2];\n\tbuf[0] = c;\n\tbuf[1] = 0;\n\treturn concat(buf, 1);\n}\n\nunsigned char String::concat(unsigned char num)\n{\n\tchar buf[4];\n\titoa(num, buf, 10);\n\treturn concat(buf, strlen(buf));\n}\n\nunsigned char String::concat(int num)\n{\n\tchar buf[7];\n\titoa(num, buf, 10);\n\treturn concat(buf, strlen(buf));\n}\n\nunsigned char String::concat(unsigned int num)\n{\n\tchar buf[6];\n\tutoa(num, buf, 10);\n\treturn concat(buf, strlen(buf));\n}\n\nunsigned char String::concat(long num)\n{\n\tchar buf[12];\n\tltoa(num, buf, 10);\n\treturn concat(buf, strlen(buf));\n}\n\nunsigned char String::concat(unsigned long num)\n{\n\tchar buf[11];\n\tultoa(num, buf, 10);\n\treturn concat(buf, strlen(buf));\n}\n\n/*********************************************/\n/*  Concatenate                              */\n/*********************************************/\n\nStringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs)\n{\n\tStringSumHelper &a = const_cast<StringSumHelper&>(lhs);\n\tif (!a.concat(rhs.buffer, rhs.len)) a.invalidate();\n\treturn a;\n}\n\nStringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr)\n{\n\tStringSumHelper &a = const_cast<StringSumHelper&>(lhs);\n\tif (!cstr || !a.concat(cstr, strlen(cstr))) a.invalidate();\n\treturn a;\n}\n\nStringSumHelper & operator + (const StringSumHelper &lhs, char c)\n{\n\tStringSumHelper &a = const_cast<StringSumHelper&>(lhs);\n\tif (!a.concat(c)) a.invalidate();\n\treturn a;\n}\n\nStringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num)\n{\n\tStringSumHelper &a = const_cast<StringSumHelper&>(lhs);\n\tif (!a.concat(num)) a.invalidate();\n\treturn a;\n}\n\nStringSumHelper & operator + (const StringSumHelper &lhs, int num)\n{\n\tStringSumHelper &a = const_cast<StringSumHelper&>(lhs);\n\tif (!a.concat(num)) a.invalidate();\n\treturn a;\n}\n\nStringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num)\n{\n\tStringSumHelper &a = const_cast<StringSumHelper&>(lhs);\n\tif (!a.concat(num)) a.invalidate();\n\treturn a;\n}\n\nStringSumHelper & operator + (const StringSumHelper &lhs, long num)\n{\n\tStringSumHelper &a = const_cast<StringSumHelper&>(lhs);\n\tif (!a.concat(num)) a.invalidate();\n\treturn a;\n}\n\nStringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num)\n{\n\tStringSumHelper &a = const_cast<StringSumHelper&>(lhs);\n\tif (!a.concat(num)) a.invalidate();\n\treturn a;\n}\n\n/*********************************************/\n/*  Comparison                               */\n/*********************************************/\n\nint String::compareTo(const String &s) const\n{\n\tif (!buffer || !s.buffer) {\n\t\tif (s.buffer && s.len > 0) return 0 - *(unsigned char *)s.buffer;\n\t\tif (buffer && len > 0) return *(unsigned char *)buffer;\n\t\treturn 0;\n\t}\n\treturn strcmp(buffer, s.buffer);\n}\n\nunsigned char String::equals(const String &s2) const\n{\n\treturn (len == s2.len && compareTo(s2) == 0);\n}\n\nunsigned char String::equals(const char *cstr) const\n{\n\tif (len == 0) return (cstr == NULL || *cstr == 0);\n\tif (cstr == NULL) return buffer[0] == 0;\n\treturn strcmp(buffer, cstr) == 0;\n}\n\nunsigned char String::operator<(const String &rhs) const\n{\n\treturn compareTo(rhs) < 0;\n}\n\nunsigned char String::operator>(const String &rhs) const\n{\n\treturn compareTo(rhs) > 0;\n}\n\nunsigned char String::operator<=(const String &rhs) const\n{\n\treturn compareTo(rhs) <= 0;\n}\n\nunsigned char String::operator>=(const String &rhs) const\n{\n\treturn compareTo(rhs) >= 0;\n}\n\nunsigned char String::equalsIgnoreCase( const String &s2 ) const\n{\n\tif (this == &s2) return 1;\n\tif (len != s2.len) return 0;\n\tif (len == 0) return 1;\n\tconst char *p1 = buffer;\n\tconst char *p2 = s2.buffer;\n\twhile (*p1) {\n\t\tif (tolower(*p1++) != tolower(*p2++)) return 0;\n\t} \n\treturn 1;\n}\n\nunsigned char String::startsWith( const String &s2 ) const\n{\n\tif (len < s2.len) return 0;\n\treturn startsWith(s2, 0);\n}\n\nunsigned char String::startsWith( const String &s2, unsigned int offset ) const\n{\n\tif (offset > len - s2.len || !buffer || !s2.buffer) return 0;\n\treturn strncmp( &buffer[offset], s2.buffer, s2.len ) == 0;\n}\n\nunsigned char String::endsWith( const String &s2 ) const\n{\n\tif ( len < s2.len || !buffer || !s2.buffer) return 0;\n\treturn strcmp(&buffer[len - s2.len], s2.buffer) == 0;\n}\n\n/*********************************************/\n/*  Character Access                         */\n/*********************************************/\n\nchar String::charAt(unsigned int loc) const\n{\n\treturn operator[](loc);\n}\n\nvoid String::setCharAt(unsigned int loc, char c) \n{\n\tif (loc < len) buffer[loc] = c;\n}\n\nchar & String::operator[](unsigned int index)\n{\n\tstatic char dummy_writable_char;\n\tif (index >= len || !buffer) {\n\t\tdummy_writable_char = 0;\n\t\treturn dummy_writable_char;\n\t}\n\treturn buffer[index];\n}\n\nchar String::operator[]( unsigned int index ) const\n{\n\tif (index >= len || !buffer) return 0;\n\treturn buffer[index];\n}\n\nvoid String::getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index) const\n{\n\tif (!bufsize || !buf) return;\n\tif (index >= len) {\n\t\tbuf[0] = 0;\n\t\treturn;\n\t}\n\tunsigned int n = bufsize - 1;\n\tif (n > len - index) n = len - index;\n\tstrncpy((char *)buf, buffer + index, n);\n\tbuf[n] = 0;\n}\n\n/*********************************************/\n/*  Search                                   */\n/*********************************************/\n\nint String::indexOf(char c) const\n{\n\treturn indexOf(c, 0);\n}\n\nint String::indexOf( char ch, unsigned int fromIndex ) const\n{\n\tif (fromIndex >= len) return -1;\n\tconst char* temp = strchr(buffer + fromIndex, ch);\n\tif (temp == NULL) return -1;\n\treturn temp - buffer;\n}\n\nint String::indexOf(const String &s2) const\n{\n\treturn indexOf(s2, 0);\n}\n\nint String::indexOf(const String &s2, unsigned int fromIndex) const\n{\n\tif (fromIndex >= len) return -1;\n\tconst char *found = strstr(buffer + fromIndex, s2.buffer);\n\tif (found == NULL) return -1;\n\treturn found - buffer;\n}\n\nint String::lastIndexOf( char theChar ) const\n{\n\treturn lastIndexOf(theChar, len - 1);\n}\n\nint String::lastIndexOf(char ch, unsigned int fromIndex) const\n{\n\tif (fromIndex >= len) return -1;\n\tchar tempchar = buffer[fromIndex + 1];\n\tbuffer[fromIndex + 1] = '\\0';\n\tchar* temp = strrchr( buffer, ch );\n\tbuffer[fromIndex + 1] = tempchar;\n\tif (temp == NULL) return -1;\n\treturn temp - buffer;\n}\n\nint String::lastIndexOf(const String &s2) const\n{\n\treturn lastIndexOf(s2, len - s2.len);\n}\n\nint String::lastIndexOf(const String &s2, unsigned int fromIndex) const\n{\n  \tif (s2.len == 0 || len == 0 || s2.len > len) return -1;\n\tif (fromIndex >= len) fromIndex = len - 1;\n\tint found = -1;\n\tfor (char *p = buffer; p <= buffer + fromIndex; p++) {\n\t\tp = strstr(p, s2.buffer);\n\t\tif (!p) break;\n\t\tif ((unsigned int)(p - buffer) <= fromIndex) found = p - buffer;\n\t}\n\treturn found;\n}\n\nString String::substring( unsigned int left ) const\n{\n\treturn substring(left, len);\n}\n\nString String::substring(unsigned int left, unsigned int right) const\n{\n\tif (left > right) {\n\t\tunsigned int temp = right;\n\t\tright = left;\n\t\tleft = temp;\n\t}\n\tString out;\n\tif (left > len) return out;\n\tif (right > len) right = len;\n\tchar temp = buffer[right];  // save the replaced character\n\tbuffer[right] = '\\0';\t\n\tout = buffer + left;  // pointer arithmetic\n\tbuffer[right] = temp;  //restore character\n\treturn out;\n}\n\n/*********************************************/\n/*  Modification                             */\n/*********************************************/\n\nvoid String::replace(char find, char replace)\n{\n\tif (!buffer) return;\n\tfor (char *p = buffer; *p; p++) {\n\t\tif (*p == find) *p = replace;\n\t}\n}\n\nvoid String::replace(const String& find, const String& replace)\n{\n\tif (len == 0 || find.len == 0) return;\n\tint diff = replace.len - find.len;\n\tchar *readFrom = buffer;\n\tchar *foundAt;\n\tif (diff == 0) {\n\t\twhile ((foundAt = strstr(readFrom, find.buffer)) != NULL) {\n\t\t\tmemcpy(foundAt, replace.buffer, replace.len);\n\t\t\treadFrom = foundAt + replace.len;\n\t\t}\n\t} else if (diff < 0) {\n\t\tchar *writeTo = buffer;\n\t\twhile ((foundAt = strstr(readFrom, find.buffer)) != NULL) {\n\t\t\tunsigned int n = foundAt - readFrom;\n\t\t\tmemcpy(writeTo, readFrom, n);\n\t\t\twriteTo += n;\n\t\t\tmemcpy(writeTo, replace.buffer, replace.len);\n\t\t\twriteTo += replace.len;\n\t\t\treadFrom = foundAt + find.len;\n\t\t\tlen += diff;\n\t\t}\n\t\tstrcpy(writeTo, readFrom);\n\t} else {\n\t\tunsigned int size = len; // compute size needed for result\n\t\twhile ((foundAt = strstr(readFrom, find.buffer)) != NULL) {\n\t\t\treadFrom = foundAt + find.len;\n\t\t\tsize += diff;\n\t\t}\n\t\tif (size == len) return;\n\t\tif (size > capacity && !changeBuffer(size)) return; // XXX: tell user!\n\t\tint index = len - 1;\n\t\twhile (index >= 0 && (index = lastIndexOf(find, index)) >= 0) {\n\t\t\treadFrom = buffer + index + find.len;\n\t\t\tmemmove(readFrom + diff, readFrom, len - (readFrom - buffer));\n\t\t\tlen += diff;\n\t\t\tbuffer[len] = 0;\n\t\t\tmemcpy(buffer + index, replace.buffer, replace.len);\n\t\t\tindex--;\n\t\t}\n\t}\n}\n\nvoid String::toLowerCase(void)\n{\n\tif (!buffer) return;\n\tfor (char *p = buffer; *p; p++) {\n\t\t*p = tolower(*p);\n\t}\n}\n\nvoid String::toUpperCase(void)\n{\n\tif (!buffer) return;\n\tfor (char *p = buffer; *p; p++) {\n\t\t*p = toupper(*p);\n\t}\n}\n\nvoid String::trim(void)\n{\n\tif (!buffer || len == 0) return;\n\tchar *begin = buffer;\n\twhile (isspace(*begin)) begin++;\n\tchar *end = buffer + len - 1;\n\twhile (isspace(*end) && end >= begin) end--;\n\tlen = end + 1 - begin;\n\tif (begin > buffer) memcpy(buffer, begin, len);\n\tbuffer[len] = 0;\n}\n\n/*********************************************/\n/*  Parsing / Conversion                     */\n/*********************************************/\n\nlong String::toInt(void) const\n{\n\tif (buffer) return atol(buffer);\n\treturn 0;\n}\n\n\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/WString.h",
    "content": "/*\n  WString.h - String library for Wiring & Arduino\n  ...mostly rewritten by Paul Stoffregen...\n  Copyright (c) 2009-10 Hernando Barragan.  All right reserved.\n  Copyright 2011, Paul Stoffregen, paul@pjrc.com\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General Public\n  License along with this library; if not, write to the Free Software\n  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n*/\n\n#ifndef String_class_h\n#define String_class_h\n#ifdef __cplusplus\n\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n#include <avr/pgmspace.h>\n\n// When compiling programs with this class, the following gcc parameters\n// dramatically increase performance and memory (RAM) efficiency, typically\n// with little or no increase in code size.\n//     -felide-constructors\n//     -std=c++0x\n\nclass __FlashStringHelper;\n#define F(string_literal) (reinterpret_cast<const __FlashStringHelper *>(PSTR(string_literal)))\n\n// An inherited class for holding the result of a concatenation.  These\n// result objects are assumed to be writable by subsequent concatenations.\nclass StringSumHelper;\n\n// The string class\nclass String\n{\n\t// use a function pointer to allow for \"if (s)\" without the\n\t// complications of an operator bool(). for more information, see:\n\t// http://www.artima.com/cppsource/safebool.html\n\ttypedef void (String::*StringIfHelperType)() const;\n\tvoid StringIfHelper() const {}\n\npublic:\n\t// constructors\n\t// creates a copy of the initial value.\n\t// if the initial value is null or invalid, or if memory allocation\n\t// fails, the string will be marked as invalid (i.e. \"if (s)\" will\n\t// be false).\n\tString(const char *cstr = \"\");\n\tString(const String &str);\n\t#ifdef __GXX_EXPERIMENTAL_CXX0X__\n\tString(String &&rval);\n\tString(StringSumHelper &&rval);\n\t#endif\n\texplicit String(char c);\n\texplicit String(unsigned char, unsigned char base=10);\n\texplicit String(int, unsigned char base=10);\n\texplicit String(unsigned int, unsigned char base=10);\n\texplicit String(long, unsigned char base=10);\n\texplicit String(unsigned long, unsigned char base=10);\n\t~String(void);\n\n\t// memory management\n\t// return true on success, false on failure (in which case, the string\n\t// is left unchanged).  reserve(0), if successful, will validate an\n\t// invalid string (i.e., \"if (s)\" will be true afterwards)\n\tunsigned char reserve(unsigned int size);\n\tinline unsigned int length(void) const {return len;}\n\n\t// creates a copy of the assigned value.  if the value is null or\n\t// invalid, or if the memory allocation fails, the string will be \n\t// marked as invalid (\"if (s)\" will be false).\n\tString & operator = (const String &rhs);\n\tString & operator = (const char *cstr);\n\t#ifdef __GXX_EXPERIMENTAL_CXX0X__\n\tString & operator = (String &&rval);\n\tString & operator = (StringSumHelper &&rval);\n\t#endif\n\n\t// concatenate (works w/ built-in types)\n\t\n\t// returns true on success, false on failure (in which case, the string\n\t// is left unchanged).  if the argument is null or invalid, the \n\t// concatenation is considered unsucessful.  \n\tunsigned char concat(const String &str);\n\tunsigned char concat(const char *cstr);\n\tunsigned char concat(char c);\n\tunsigned char concat(unsigned char c);\n\tunsigned char concat(int num);\n\tunsigned char concat(unsigned int num);\n\tunsigned char concat(long num);\n\tunsigned char concat(unsigned long num);\n\t\n\t// if there's not enough memory for the concatenated value, the string\n\t// will be left unchanged (but this isn't signalled in any way)\n\tString & operator += (const String &rhs)\t{concat(rhs); return (*this);}\n\tString & operator += (const char *cstr)\t\t{concat(cstr); return (*this);}\n\tString & operator += (char c)\t\t\t{concat(c); return (*this);}\n\tString & operator += (unsigned char num)\t\t{concat(num); return (*this);}\n\tString & operator += (int num)\t\t\t{concat(num); return (*this);}\n\tString & operator += (unsigned int num)\t\t{concat(num); return (*this);}\n\tString & operator += (long num)\t\t\t{concat(num); return (*this);}\n\tString & operator += (unsigned long num)\t{concat(num); return (*this);}\n\n\tfriend StringSumHelper & operator + (const StringSumHelper &lhs, const String &rhs);\n\tfriend StringSumHelper & operator + (const StringSumHelper &lhs, const char *cstr);\n\tfriend StringSumHelper & operator + (const StringSumHelper &lhs, char c);\n\tfriend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned char num);\n\tfriend StringSumHelper & operator + (const StringSumHelper &lhs, int num);\n\tfriend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned int num);\n\tfriend StringSumHelper & operator + (const StringSumHelper &lhs, long num);\n\tfriend StringSumHelper & operator + (const StringSumHelper &lhs, unsigned long num);\n\n\t// comparison (only works w/ Strings and \"strings\")\n\toperator StringIfHelperType() const { return buffer ? &String::StringIfHelper : 0; }\n\tint compareTo(const String &s) const;\n\tunsigned char equals(const String &s) const;\n\tunsigned char equals(const char *cstr) const;\n\tunsigned char operator == (const String &rhs) const {return equals(rhs);}\n\tunsigned char operator == (const char *cstr) const {return equals(cstr);}\n\tunsigned char operator != (const String &rhs) const {return !equals(rhs);}\n\tunsigned char operator != (const char *cstr) const {return !equals(cstr);}\n\tunsigned char operator <  (const String &rhs) const;\n\tunsigned char operator >  (const String &rhs) const;\n\tunsigned char operator <= (const String &rhs) const;\n\tunsigned char operator >= (const String &rhs) const;\n\tunsigned char equalsIgnoreCase(const String &s) const;\n\tunsigned char startsWith( const String &prefix) const;\n\tunsigned char startsWith(const String &prefix, unsigned int offset) const;\n\tunsigned char endsWith(const String &suffix) const;\n\n\t// character acccess\n\tchar charAt(unsigned int index) const;\n\tvoid setCharAt(unsigned int index, char c);\n\tchar operator [] (unsigned int index) const;\n\tchar& operator [] (unsigned int index);\n\tvoid getBytes(unsigned char *buf, unsigned int bufsize, unsigned int index=0) const;\n\tvoid toCharArray(char *buf, unsigned int bufsize, unsigned int index=0) const\n\t\t{getBytes((unsigned char *)buf, bufsize, index);}\n\n\t// search\n\tint indexOf( char ch ) const;\n\tint indexOf( char ch, unsigned int fromIndex ) const;\n\tint indexOf( const String &str ) const;\n\tint indexOf( const String &str, unsigned int fromIndex ) const;\n\tint lastIndexOf( char ch ) const;\n\tint lastIndexOf( char ch, unsigned int fromIndex ) const;\n\tint lastIndexOf( const String &str ) const;\n\tint lastIndexOf( const String &str, unsigned int fromIndex ) const;\n\tString substring( unsigned int beginIndex ) const;\n\tString substring( unsigned int beginIndex, unsigned int endIndex ) const;\n\n\t// modification\n\tvoid replace(char find, char replace);\n\tvoid replace(const String& find, const String& replace);\n\tvoid toLowerCase(void);\n\tvoid toUpperCase(void);\n\tvoid trim(void);\n\n\t// parsing/conversion\n\tlong toInt(void) const;\n\nprotected:\n\tchar *buffer;\t        // the actual char array\n\tunsigned int capacity;  // the array length minus one (for the '\\0')\n\tunsigned int len;       // the String length (not counting the '\\0')\n\tunsigned char flags;    // unused, for future features\nprotected:\n\tvoid init(void);\n\tvoid invalidate(void);\n\tunsigned char changeBuffer(unsigned int maxStrLen);\n\tunsigned char concat(const char *cstr, unsigned int length);\n\n\t// copy and move\n\tString & copy(const char *cstr, unsigned int length);\n\t#ifdef __GXX_EXPERIMENTAL_CXX0X__\n\tvoid move(String &rhs);\n\t#endif\n};\n\nclass StringSumHelper : public String\n{\npublic:\n\tStringSumHelper(const String &s) : String(s) {}\n\tStringSumHelper(const char *p) : String(p) {}\n\tStringSumHelper(char c) : String(c) {}\n\tStringSumHelper(unsigned char num) : String(num) {}\n\tStringSumHelper(int num) : String(num) {}\n\tStringSumHelper(unsigned int num) : String(num) {}\n\tStringSumHelper(long num) : String(num) {}\n\tStringSumHelper(unsigned long num) : String(num) {}\n};\n\n#endif  // __cplusplus\n#endif  // String_class_h\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/binary.h",
    "content": "#ifndef Binary_h\n#define Binary_h\n\n#define B0 0\n#define B00 0\n#define B000 0\n#define B0000 0\n#define B00000 0\n#define B000000 0\n#define B0000000 0\n#define B00000000 0\n#define B1 1\n#define B01 1\n#define B001 1\n#define B0001 1\n#define B00001 1\n#define B000001 1\n#define B0000001 1\n#define B00000001 1\n#define B10 2\n#define B010 2\n#define B0010 2\n#define B00010 2\n#define B000010 2\n#define B0000010 2\n#define B00000010 2\n#define B11 3\n#define B011 3\n#define B0011 3\n#define B00011 3\n#define B000011 3\n#define B0000011 3\n#define B00000011 3\n#define B100 4\n#define B0100 4\n#define B00100 4\n#define B000100 4\n#define B0000100 4\n#define B00000100 4\n#define B101 5\n#define B0101 5\n#define B00101 5\n#define B000101 5\n#define B0000101 5\n#define B00000101 5\n#define B110 6\n#define B0110 6\n#define B00110 6\n#define B000110 6\n#define B0000110 6\n#define B00000110 6\n#define B111 7\n#define B0111 7\n#define B00111 7\n#define B000111 7\n#define B0000111 7\n#define B00000111 7\n#define B1000 8\n#define B01000 8\n#define B001000 8\n#define B0001000 8\n#define B00001000 8\n#define B1001 9\n#define B01001 9\n#define B001001 9\n#define B0001001 9\n#define B00001001 9\n#define B1010 10\n#define B01010 10\n#define B001010 10\n#define B0001010 10\n#define B00001010 10\n#define B1011 11\n#define B01011 11\n#define B001011 11\n#define B0001011 11\n#define B00001011 11\n#define B1100 12\n#define B01100 12\n#define B001100 12\n#define B0001100 12\n#define B00001100 12\n#define B1101 13\n#define B01101 13\n#define B001101 13\n#define B0001101 13\n#define B00001101 13\n#define B1110 14\n#define B01110 14\n#define B001110 14\n#define B0001110 14\n#define B00001110 14\n#define B1111 15\n#define B01111 15\n#define B001111 15\n#define B0001111 15\n#define B00001111 15\n#define B10000 16\n#define B010000 16\n#define B0010000 16\n#define B00010000 16\n#define B10001 17\n#define B010001 17\n#define B0010001 17\n#define B00010001 17\n#define B10010 18\n#define B010010 18\n#define B0010010 18\n#define B00010010 18\n#define B10011 19\n#define B010011 19\n#define B0010011 19\n#define B00010011 19\n#define B10100 20\n#define B010100 20\n#define B0010100 20\n#define B00010100 20\n#define B10101 21\n#define B010101 21\n#define B0010101 21\n#define B00010101 21\n#define B10110 22\n#define B010110 22\n#define B0010110 22\n#define B00010110 22\n#define B10111 23\n#define B010111 23\n#define B0010111 23\n#define B00010111 23\n#define B11000 24\n#define B011000 24\n#define B0011000 24\n#define B00011000 24\n#define B11001 25\n#define B011001 25\n#define B0011001 25\n#define B00011001 25\n#define B11010 26\n#define B011010 26\n#define B0011010 26\n#define B00011010 26\n#define B11011 27\n#define B011011 27\n#define B0011011 27\n#define B00011011 27\n#define B11100 28\n#define B011100 28\n#define B0011100 28\n#define B00011100 28\n#define B11101 29\n#define B011101 29\n#define B0011101 29\n#define B00011101 29\n#define B11110 30\n#define B011110 30\n#define B0011110 30\n#define B00011110 30\n#define B11111 31\n#define B011111 31\n#define B0011111 31\n#define B00011111 31\n#define B100000 32\n#define B0100000 32\n#define B00100000 32\n#define B100001 33\n#define B0100001 33\n#define B00100001 33\n#define B100010 34\n#define B0100010 34\n#define B00100010 34\n#define B100011 35\n#define B0100011 35\n#define B00100011 35\n#define B100100 36\n#define B0100100 36\n#define B00100100 36\n#define B100101 37\n#define B0100101 37\n#define B00100101 37\n#define B100110 38\n#define B0100110 38\n#define B00100110 38\n#define B100111 39\n#define B0100111 39\n#define B00100111 39\n#define B101000 40\n#define B0101000 40\n#define B00101000 40\n#define B101001 41\n#define B0101001 41\n#define B00101001 41\n#define B101010 42\n#define B0101010 42\n#define B00101010 42\n#define B101011 43\n#define B0101011 43\n#define B00101011 43\n#define B101100 44\n#define B0101100 44\n#define B00101100 44\n#define B101101 45\n#define B0101101 45\n#define B00101101 45\n#define B101110 46\n#define B0101110 46\n#define B00101110 46\n#define B101111 47\n#define B0101111 47\n#define B00101111 47\n#define B110000 48\n#define B0110000 48\n#define B00110000 48\n#define B110001 49\n#define B0110001 49\n#define B00110001 49\n#define B110010 50\n#define B0110010 50\n#define B00110010 50\n#define B110011 51\n#define B0110011 51\n#define B00110011 51\n#define B110100 52\n#define B0110100 52\n#define B00110100 52\n#define B110101 53\n#define B0110101 53\n#define B00110101 53\n#define B110110 54\n#define B0110110 54\n#define B00110110 54\n#define B110111 55\n#define B0110111 55\n#define B00110111 55\n#define B111000 56\n#define B0111000 56\n#define B00111000 56\n#define B111001 57\n#define B0111001 57\n#define B00111001 57\n#define B111010 58\n#define B0111010 58\n#define B00111010 58\n#define B111011 59\n#define B0111011 59\n#define B00111011 59\n#define B111100 60\n#define B0111100 60\n#define B00111100 60\n#define B111101 61\n#define B0111101 61\n#define B00111101 61\n#define B111110 62\n#define B0111110 62\n#define B00111110 62\n#define B111111 63\n#define B0111111 63\n#define B00111111 63\n#define B1000000 64\n#define B01000000 64\n#define B1000001 65\n#define B01000001 65\n#define B1000010 66\n#define B01000010 66\n#define B1000011 67\n#define B01000011 67\n#define B1000100 68\n#define B01000100 68\n#define B1000101 69\n#define B01000101 69\n#define B1000110 70\n#define B01000110 70\n#define B1000111 71\n#define B01000111 71\n#define B1001000 72\n#define B01001000 72\n#define B1001001 73\n#define B01001001 73\n#define B1001010 74\n#define B01001010 74\n#define B1001011 75\n#define B01001011 75\n#define B1001100 76\n#define B01001100 76\n#define B1001101 77\n#define B01001101 77\n#define B1001110 78\n#define B01001110 78\n#define B1001111 79\n#define B01001111 79\n#define B1010000 80\n#define B01010000 80\n#define B1010001 81\n#define B01010001 81\n#define B1010010 82\n#define B01010010 82\n#define B1010011 83\n#define B01010011 83\n#define B1010100 84\n#define B01010100 84\n#define B1010101 85\n#define B01010101 85\n#define B1010110 86\n#define B01010110 86\n#define B1010111 87\n#define B01010111 87\n#define B1011000 88\n#define B01011000 88\n#define B1011001 89\n#define B01011001 89\n#define B1011010 90\n#define B01011010 90\n#define B1011011 91\n#define B01011011 91\n#define B1011100 92\n#define B01011100 92\n#define B1011101 93\n#define B01011101 93\n#define B1011110 94\n#define B01011110 94\n#define B1011111 95\n#define B01011111 95\n#define B1100000 96\n#define B01100000 96\n#define B1100001 97\n#define B01100001 97\n#define B1100010 98\n#define B01100010 98\n#define B1100011 99\n#define B01100011 99\n#define B1100100 100\n#define B01100100 100\n#define B1100101 101\n#define B01100101 101\n#define B1100110 102\n#define B01100110 102\n#define B1100111 103\n#define B01100111 103\n#define B1101000 104\n#define B01101000 104\n#define B1101001 105\n#define B01101001 105\n#define B1101010 106\n#define B01101010 106\n#define B1101011 107\n#define B01101011 107\n#define B1101100 108\n#define B01101100 108\n#define B1101101 109\n#define B01101101 109\n#define B1101110 110\n#define B01101110 110\n#define B1101111 111\n#define B01101111 111\n#define B1110000 112\n#define B01110000 112\n#define B1110001 113\n#define B01110001 113\n#define B1110010 114\n#define B01110010 114\n#define B1110011 115\n#define B01110011 115\n#define B1110100 116\n#define B01110100 116\n#define B1110101 117\n#define B01110101 117\n#define B1110110 118\n#define B01110110 118\n#define B1110111 119\n#define B01110111 119\n#define B1111000 120\n#define B01111000 120\n#define B1111001 121\n#define B01111001 121\n#define B1111010 122\n#define B01111010 122\n#define B1111011 123\n#define B01111011 123\n#define B1111100 124\n#define B01111100 124\n#define B1111101 125\n#define B01111101 125\n#define B1111110 126\n#define B01111110 126\n#define B1111111 127\n#define B01111111 127\n#define B10000000 128\n#define B10000001 129\n#define B10000010 130\n#define B10000011 131\n#define B10000100 132\n#define B10000101 133\n#define B10000110 134\n#define B10000111 135\n#define B10001000 136\n#define B10001001 137\n#define B10001010 138\n#define B10001011 139\n#define B10001100 140\n#define B10001101 141\n#define B10001110 142\n#define B10001111 143\n#define B10010000 144\n#define B10010001 145\n#define B10010010 146\n#define B10010011 147\n#define B10010100 148\n#define B10010101 149\n#define B10010110 150\n#define B10010111 151\n#define B10011000 152\n#define B10011001 153\n#define B10011010 154\n#define B10011011 155\n#define B10011100 156\n#define B10011101 157\n#define B10011110 158\n#define B10011111 159\n#define B10100000 160\n#define B10100001 161\n#define B10100010 162\n#define B10100011 163\n#define B10100100 164\n#define B10100101 165\n#define B10100110 166\n#define B10100111 167\n#define B10101000 168\n#define B10101001 169\n#define B10101010 170\n#define B10101011 171\n#define B10101100 172\n#define B10101101 173\n#define B10101110 174\n#define B10101111 175\n#define B10110000 176\n#define B10110001 177\n#define B10110010 178\n#define B10110011 179\n#define B10110100 180\n#define B10110101 181\n#define B10110110 182\n#define B10110111 183\n#define B10111000 184\n#define B10111001 185\n#define B10111010 186\n#define B10111011 187\n#define B10111100 188\n#define B10111101 189\n#define B10111110 190\n#define B10111111 191\n#define B11000000 192\n#define B11000001 193\n#define B11000010 194\n#define B11000011 195\n#define B11000100 196\n#define B11000101 197\n#define B11000110 198\n#define B11000111 199\n#define B11001000 200\n#define B11001001 201\n#define B11001010 202\n#define B11001011 203\n#define B11001100 204\n#define B11001101 205\n#define B11001110 206\n#define B11001111 207\n#define B11010000 208\n#define B11010001 209\n#define B11010010 210\n#define B11010011 211\n#define B11010100 212\n#define B11010101 213\n#define B11010110 214\n#define B11010111 215\n#define B11011000 216\n#define B11011001 217\n#define B11011010 218\n#define B11011011 219\n#define B11011100 220\n#define B11011101 221\n#define B11011110 222\n#define B11011111 223\n#define B11100000 224\n#define B11100001 225\n#define B11100010 226\n#define B11100011 227\n#define B11100100 228\n#define B11100101 229\n#define B11100110 230\n#define B11100111 231\n#define B11101000 232\n#define B11101001 233\n#define B11101010 234\n#define B11101011 235\n#define B11101100 236\n#define B11101101 237\n#define B11101110 238\n#define B11101111 239\n#define B11110000 240\n#define B11110001 241\n#define B11110010 242\n#define B11110011 243\n#define B11110100 244\n#define B11110101 245\n#define B11110110 246\n#define B11110111 247\n#define B11111000 248\n#define B11111001 249\n#define B11111010 250\n#define B11111011 251\n#define B11111100 252\n#define B11111101 253\n#define B11111110 254\n#define B11111111 255\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/main.cpp",
    "content": "#include <Arduino.h>\n\nint main(void)\n{\n\tinit();\n\n#if defined(USBCON)\n\tUSBDevice.attach();\n#endif\n\t\n\tsetup();\n    \n\tfor (;;) {\n\t\tloop();\n\t\tif (serialEventRun) serialEventRun();\n\t}\n        \n\treturn 0;\n}\n\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/new.cpp",
    "content": "#include <new.h>\n\nvoid * operator new(size_t size)\n{\n  return malloc(size);\n}\n\nvoid operator delete(void * ptr)\n{\n  free(ptr);\n} \n\nint __cxa_guard_acquire(__guard *g) {return !*(char *)(g);};\nvoid __cxa_guard_release (__guard *g) {*(char *)g = 1;};\nvoid __cxa_guard_abort (__guard *) {}; \n\nvoid __cxa_pure_virtual(void) {};\n\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/new.h",
    "content": "/* Header to define new/delete operators as they aren't provided by avr-gcc by default\n   Taken from http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=59453 \n */\n\n#ifndef NEW_H\n#define NEW_H\n\n#include <stdlib.h>\n\nvoid * operator new(size_t size);\nvoid operator delete(void * ptr); \n\n__extension__ typedef int __guard __attribute__((mode (__DI__)));\n\nextern \"C\" int __cxa_guard_acquire(__guard *);\nextern \"C\" void __cxa_guard_release (__guard *);\nextern \"C\" void __cxa_guard_abort (__guard *); \n\nextern \"C\" void __cxa_pure_virtual(void);\n\n#endif\n\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/wiring.c",
    "content": "/*\n  wiring.c - Partial implementation of the Wiring API for the ATmega8.\n  Part of Arduino - http://www.arduino.cc/\n\n  Copyright (c) 2005-2006 David A. Mellis\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n\n  $Id$\n*/\n\n#include \"wiring_private.h\"\n\n// the prescaler is set so that timer0 ticks every 64 clock cycles, and the\n// the overflow handler is called every 256 ticks.\n#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))\n\n// the whole number of milliseconds per timer0 overflow\n#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)\n\n// the fractional number of milliseconds per timer0 overflow. we shift right\n// by three to fit these numbers into a byte. (for the clock speeds we care\n// about - 8 and 16 MHz - this doesn't lose precision.)\n#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)\n#define FRACT_MAX (1000 >> 3)\n\nvolatile unsigned long timer0_overflow_count = 0;\nvolatile unsigned long timer0_millis = 0;\nstatic unsigned char timer0_fract = 0;\n\n#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)\nSIGNAL(TIM0_OVF_vect)\n#else\nSIGNAL(TIMER0_OVF_vect)\n#endif\n{\n\t// copy these to local variables so they can be stored in registers\n\t// (volatile variables must be read from memory on every access)\n\tunsigned long m = timer0_millis;\n\tunsigned char f = timer0_fract;\n\n\tm += MILLIS_INC;\n\tf += FRACT_INC;\n\tif (f >= FRACT_MAX) {\n\t\tf -= FRACT_MAX;\n\t\tm += 1;\n\t}\n\n\ttimer0_fract = f;\n\ttimer0_millis = m;\n\ttimer0_overflow_count++;\n}\n\nunsigned long millis()\n{\n\tunsigned long m;\n\tuint8_t oldSREG = SREG;\n\n\t// disable interrupts while we read timer0_millis or we might get an\n\t// inconsistent value (e.g. in the middle of a write to timer0_millis)\n\tcli();\n\tm = timer0_millis;\n\tSREG = oldSREG;\n\n\treturn m;\n}\n\nunsigned long micros() {\n\tunsigned long m;\n\tuint8_t oldSREG = SREG, t;\n\t\n\tcli();\n\tm = timer0_overflow_count;\n#if defined(TCNT0)\n\tt = TCNT0;\n#elif defined(TCNT0L)\n\tt = TCNT0L;\n#else\n\t#error TIMER 0 not defined\n#endif\n\n  \n#ifdef TIFR0\n\tif ((TIFR0 & _BV(TOV0)) && (t < 255))\n\t\tm++;\n#else\n\tif ((TIFR & _BV(TOV0)) && (t < 255))\n\t\tm++;\n#endif\n\n\tSREG = oldSREG;\n\t\n\treturn ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());\n}\n\nvoid delay(unsigned long ms)\n{\n\tuint16_t start = (uint16_t)micros();\n\n\twhile (ms > 0) {\n\t\tif (((uint16_t)micros() - start) >= 1000) {\n\t\t\tms--;\n\t\t\tstart += 1000;\n\t\t}\n\t}\n}\n\n/* Delay for the given number of microseconds.  Assumes a 8 or 16 MHz clock. */\nvoid delayMicroseconds(unsigned int us)\n{\n\t// calling avrlib's delay_us() function with low values (e.g. 1 or\n\t// 2 microseconds) gives delays longer than desired.\n\t//delay_us(us);\n#if F_CPU >= 20000000L\n\t// for the 20 MHz clock on rare Arduino boards\n\n\t// for a one-microsecond delay, simply wait 2 cycle and return. The overhead\n\t// of the function call yields a delay of exactly a one microsecond.\n\t__asm__ __volatile__ (\n\t\t\"nop\" \"\\n\\t\"\n\t\t\"nop\"); //just waiting 2 cycle\n\tif (--us == 0)\n\t\treturn;\n\n\t// the following loop takes a 1/5 of a microsecond (4 cycles)\n\t// per iteration, so execute it five times for each microsecond of\n\t// delay requested.\n\tus = (us<<2) + us; // x5 us\n\n\t// account for the time taken in the preceeding commands.\n\tus -= 2;\n\n#elif F_CPU >= 16000000L\n\t// for the 16 MHz clock on most Arduino boards\n\n\t// for a one-microsecond delay, simply return.  the overhead\n\t// of the function call yields a delay of approximately 1 1/8 us.\n\tif (--us == 0)\n\t\treturn;\n\n\t// the following loop takes a quarter of a microsecond (4 cycles)\n\t// per iteration, so execute it four times for each microsecond of\n\t// delay requested.\n\tus <<= 2;\n\n\t// account for the time taken in the preceeding commands.\n\tus -= 2;\n#else\n\t// for the 8 MHz internal clock on the ATmega168\n\n\t// for a one- or two-microsecond delay, simply return.  the overhead of\n\t// the function calls takes more than two microseconds.  can't just\n\t// subtract two, since us is unsigned; we'd overflow.\n\tif (--us == 0)\n\t\treturn;\n\tif (--us == 0)\n\t\treturn;\n\n\t// the following loop takes half of a microsecond (4 cycles)\n\t// per iteration, so execute it twice for each microsecond of\n\t// delay requested.\n\tus <<= 1;\n    \n\t// partially compensate for the time taken by the preceeding commands.\n\t// we can't subtract any more than this or we'd overflow w/ small delays.\n\tus--;\n#endif\n\n\t// busy wait\n\t__asm__ __volatile__ (\n\t\t\"1: sbiw %0,1\" \"\\n\\t\" // 2 cycles\n\t\t\"brne 1b\" : \"=w\" (us) : \"0\" (us) // 2 cycles\n\t);\n}\n\nvoid init()\n{\n\t// this needs to be called before setup() or some functions won't\n\t// work there\n\tsei();\n\t\n\t// on the ATmega168, timer 0 is also used for fast hardware pwm\n\t// (using phase-correct PWM would mean that timer 0 overflowed half as often\n\t// resulting in different millis() behavior on the ATmega8 and ATmega168)\n#if defined(TCCR0A) && defined(WGM01)\n\tsbi(TCCR0A, WGM01);\n\tsbi(TCCR0A, WGM00);\n#endif  \n\n\t// set timer 0 prescale factor to 64\n#if defined(__AVR_ATmega128__)\n\t// CPU specific: different values for the ATmega128\n\tsbi(TCCR0, CS02);\n#elif defined(TCCR0) && defined(CS01) && defined(CS00)\n\t// this combination is for the standard atmega8\n\tsbi(TCCR0, CS01);\n\tsbi(TCCR0, CS00);\n#elif defined(TCCR0B) && defined(CS01) && defined(CS00)\n\t// this combination is for the standard 168/328/1280/2560\n\tsbi(TCCR0B, CS01);\n\tsbi(TCCR0B, CS00);\n#elif defined(TCCR0A) && defined(CS01) && defined(CS00)\n\t// this combination is for the __AVR_ATmega645__ series\n\tsbi(TCCR0A, CS01);\n\tsbi(TCCR0A, CS00);\n#else\n\t#error Timer 0 prescale factor 64 not set correctly\n#endif\n\n\t// enable timer 0 overflow interrupt\n#if defined(TIMSK) && defined(TOIE0)\n\tsbi(TIMSK, TOIE0);\n#elif defined(TIMSK0) && defined(TOIE0)\n\tsbi(TIMSK0, TOIE0);\n#else\n\t#error\tTimer 0 overflow interrupt not set correctly\n#endif\n\n\t// timers 1 and 2 are used for phase-correct hardware pwm\n\t// this is better for motors as it ensures an even waveform\n\t// note, however, that fast pwm mode can achieve a frequency of up\n\t// 8 MHz (with a 16 MHz clock) at 50% duty cycle\n\n#if defined(TCCR1B) && defined(CS11) && defined(CS10)\n\tTCCR1B = 0;\n\n\t// set timer 1 prescale factor to 64\n\tsbi(TCCR1B, CS11);\n#if F_CPU >= 8000000L\n\tsbi(TCCR1B, CS10);\n#endif\n#elif defined(TCCR1) && defined(CS11) && defined(CS10)\n\tsbi(TCCR1, CS11);\n#if F_CPU >= 8000000L\n\tsbi(TCCR1, CS10);\n#endif\n#endif\n\t// put timer 1 in 8-bit phase correct pwm mode\n#if defined(TCCR1A) && defined(WGM10)\n\tsbi(TCCR1A, WGM10);\n#elif defined(TCCR1)\n\t#warning this needs to be finished\n#endif\n\n\t// set timer 2 prescale factor to 64\n#if defined(TCCR2) && defined(CS22)\n\tsbi(TCCR2, CS22);\n#elif defined(TCCR2B) && defined(CS22)\n\tsbi(TCCR2B, CS22);\n#else\n\t#warning Timer 2 not finished (may not be present on this CPU)\n#endif\n\n\t// configure timer 2 for phase correct pwm (8-bit)\n#if defined(TCCR2) && defined(WGM20)\n\tsbi(TCCR2, WGM20);\n#elif defined(TCCR2A) && defined(WGM20)\n\tsbi(TCCR2A, WGM20);\n#else\n\t#warning Timer 2 not finished (may not be present on this CPU)\n#endif\n\n#if defined(TCCR3B) && defined(CS31) && defined(WGM30)\n\tsbi(TCCR3B, CS31);\t\t// set timer 3 prescale factor to 64\n\tsbi(TCCR3B, CS30);\n\tsbi(TCCR3A, WGM30);\t\t// put timer 3 in 8-bit phase correct pwm mode\n#endif\n\n#if defined(TCCR4A) && defined(TCCR4B) && defined(TCCR4D) /* beginning of timer4 block for 32U4 and similar */\n\tsbi(TCCR4B, CS42);\t\t// set timer4 prescale factor to 64\n\tsbi(TCCR4B, CS41);\n\tsbi(TCCR4B, CS40);\n\tsbi(TCCR4D, WGM40);\t\t// put timer 4 in phase- and frequency-correct PWM mode\t\n\tsbi(TCCR4A, PWM4A);\t\t// enable PWM mode for comparator OCR4A\n\tsbi(TCCR4C, PWM4D);\t\t// enable PWM mode for comparator OCR4D\n#else /* beginning of timer4 block for ATMEGA1280 and ATMEGA2560 */\n#if defined(TCCR4B) && defined(CS41) && defined(WGM40)\n\tsbi(TCCR4B, CS41);\t\t// set timer 4 prescale factor to 64\n\tsbi(TCCR4B, CS40);\n\tsbi(TCCR4A, WGM40);\t\t// put timer 4 in 8-bit phase correct pwm mode\n#endif\n#endif /* end timer4 block for ATMEGA1280/2560 and similar */\t\n\n#if defined(TCCR5B) && defined(CS51) && defined(WGM50)\n\tsbi(TCCR5B, CS51);\t\t// set timer 5 prescale factor to 64\n\tsbi(TCCR5B, CS50);\n\tsbi(TCCR5A, WGM50);\t\t// put timer 5 in 8-bit phase correct pwm mode\n#endif\n\n#if defined(ADCSRA)\n\t// set a2d prescale factor to 128\n\t// 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.\n\t// XXX: this will not work properly for other clock speeds, and\n\t// this code should use F_CPU to determine the prescale factor.\n\tsbi(ADCSRA, ADPS2);\n\tsbi(ADCSRA, ADPS1);\n\tsbi(ADCSRA, ADPS0);\n\n\t// enable a2d conversions\n\tsbi(ADCSRA, ADEN);\n#endif\n\n\t// the bootloader connects pins 0 and 1 to the USART; disconnect them\n\t// here so they can be used as normal digital i/o; they will be\n\t// reconnected in Serial.begin()\n#if defined(UCSRB)\n\tUCSRB = 0;\n#elif defined(UCSR0B)\n\tUCSR0B = 0;\n#endif\n}\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/wiring_analog.c",
    "content": "/*\n  wiring_analog.c - analog input and output\n  Part of Arduino - http://www.arduino.cc/\n\n  Copyright (c) 2005-2006 David A. Mellis\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n\n  Modified 28 September 2010 by Mark Sproul\n\n  $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $\n*/\n\n#include \"wiring_private.h\"\n#include \"pins_arduino.h\"\n\nuint8_t analog_reference = DEFAULT;\n\nvoid analogReference(uint8_t mode)\n{\n\t// can't actually set the register here because the default setting\n\t// will connect AVCC and the AREF pin, which would cause a short if\n\t// there's something connected to AREF.\n\tanalog_reference = mode;\n}\n\nint analogRead(uint8_t pin)\n{\n\tuint8_t low, high;\n\n#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)\n\tif (pin >= 54) pin -= 54; // allow for channel or pin numbers\n#elif defined(__AVR_ATmega32U4__)\n\tif (pin >= 18) pin -= 18; // allow for channel or pin numbers\n#elif defined(__AVR_ATmega1284__)\n\tif (pin >= 24) pin -= 24; // allow for channel or pin numbers\n#else\n\tif (pin >= 14) pin -= 14; // allow for channel or pin numbers\n#endif\n\t\n#if defined(__AVR_ATmega32U4__)\n\tpin = analogPinToChannel(pin);\n\tADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);\n#elif defined(ADCSRB) && defined(MUX5)\n\t// the MUX5 bit of ADCSRB selects whether we're reading from channels\n\t// 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).\n\tADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);\n#endif\n  \n\t// set the analog reference (high two bits of ADMUX) and select the\n\t// channel (low 4 bits).  this also sets ADLAR (left-adjust result)\n\t// to 0 (the default).\n#if defined(ADMUX)\n\tADMUX = (analog_reference << 6) | (pin & 0x07);\n#endif\n\n\t// without a delay, we seem to read from the wrong channel\n\t//delay(1);\n\n#if defined(ADCSRA) && defined(ADCL)\n\t// start the conversion\n\tsbi(ADCSRA, ADSC);\n\n\t// ADSC is cleared when the conversion finishes\n\twhile (bit_is_set(ADCSRA, ADSC));\n\n\t// we have to read ADCL first; doing so locks both ADCL\n\t// and ADCH until ADCH is read.  reading ADCL second would\n\t// cause the results of each conversion to be discarded,\n\t// as ADCL and ADCH would be locked when it completed.\n\tlow  = ADCL;\n\thigh = ADCH;\n#else\n\t// we dont have an ADC, return 0\n\tlow  = 0;\n\thigh = 0;\n#endif\n\n\t// combine the two bytes\n\treturn (high << 8) | low;\n}\n\n// Right now, PWM output only works on the pins with\n// hardware support.  These are defined in the appropriate\n// pins_*.c file.  For the rest of the pins, we default\n// to digital output.\nvoid analogWrite(uint8_t pin, int val)\n{\n\t// We need to make sure the PWM output is enabled for those pins\n\t// that support it, as we turn it off when digitally reading or\n\t// writing with them.  Also, make sure the pin is in output mode\n\t// for consistenty with Wiring, which doesn't require a pinMode\n\t// call for the analog output pins.\n\tpinMode(pin, OUTPUT);\n\tif (val == 0)\n\t{\n\t\tdigitalWrite(pin, LOW);\n\t}\n\telse if (val == 255)\n\t{\n\t\tdigitalWrite(pin, HIGH);\n\t}\n\telse\n\t{\n\t\tswitch(digitalPinToTimer(pin))\n\t\t{\n\t\t\t// XXX fix needed for atmega8\n\t\t\t#if defined(TCCR0) && defined(COM00) && !defined(__AVR_ATmega8__)\n\t\t\tcase TIMER0A:\n\t\t\t\t// connect pwm to pin on timer 0\n\t\t\t\tsbi(TCCR0, COM00);\n\t\t\t\tOCR0 = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR0A) && defined(COM0A1)\n\t\t\tcase TIMER0A:\n\t\t\t\t// connect pwm to pin on timer 0, channel A\n\t\t\t\tsbi(TCCR0A, COM0A1);\n\t\t\t\tOCR0A = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR0A) && defined(COM0B1)\n\t\t\tcase TIMER0B:\n\t\t\t\t// connect pwm to pin on timer 0, channel B\n\t\t\t\tsbi(TCCR0A, COM0B1);\n\t\t\t\tOCR0B = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR1A) && defined(COM1A1)\n\t\t\tcase TIMER1A:\n\t\t\t\t// connect pwm to pin on timer 1, channel A\n\t\t\t\tsbi(TCCR1A, COM1A1);\n\t\t\t\tOCR1A = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR1A) && defined(COM1B1)\n\t\t\tcase TIMER1B:\n\t\t\t\t// connect pwm to pin on timer 1, channel B\n\t\t\t\tsbi(TCCR1A, COM1B1);\n\t\t\t\tOCR1B = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR2) && defined(COM21)\n\t\t\tcase TIMER2:\n\t\t\t\t// connect pwm to pin on timer 2\n\t\t\t\tsbi(TCCR2, COM21);\n\t\t\t\tOCR2 = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR2A) && defined(COM2A1)\n\t\t\tcase TIMER2A:\n\t\t\t\t// connect pwm to pin on timer 2, channel A\n\t\t\t\tsbi(TCCR2A, COM2A1);\n\t\t\t\tOCR2A = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR2A) && defined(COM2B1)\n\t\t\tcase TIMER2B:\n\t\t\t\t// connect pwm to pin on timer 2, channel B\n\t\t\t\tsbi(TCCR2A, COM2B1);\n\t\t\t\tOCR2B = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR3A) && defined(COM3A1)\n\t\t\tcase TIMER3A:\n\t\t\t\t// connect pwm to pin on timer 3, channel A\n\t\t\t\tsbi(TCCR3A, COM3A1);\n\t\t\t\tOCR3A = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR3A) && defined(COM3B1)\n\t\t\tcase TIMER3B:\n\t\t\t\t// connect pwm to pin on timer 3, channel B\n\t\t\t\tsbi(TCCR3A, COM3B1);\n\t\t\t\tOCR3B = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR3A) && defined(COM3C1)\n\t\t\tcase TIMER3C:\n\t\t\t\t// connect pwm to pin on timer 3, channel C\n\t\t\t\tsbi(TCCR3A, COM3C1);\n\t\t\t\tOCR3C = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR4A)\n\t\t\tcase TIMER4A:\n\t\t\t\t//connect pwm to pin on timer 4, channel A\n\t\t\t\tsbi(TCCR4A, COM4A1);\n\t\t\t\t#if defined(COM4A0)\t\t// only used on 32U4\n\t\t\t\tcbi(TCCR4A, COM4A0);\n\t\t\t\t#endif\n\t\t\t\tOCR4A = val;\t// set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\t\t\t\n\t\t\t#if defined(TCCR4A) && defined(COM4B1)\n\t\t\tcase TIMER4B:\n\t\t\t\t// connect pwm to pin on timer 4, channel B\n\t\t\t\tsbi(TCCR4A, COM4B1);\n\t\t\t\tOCR4B = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR4A) && defined(COM4C1)\n\t\t\tcase TIMER4C:\n\t\t\t\t// connect pwm to pin on timer 4, channel C\n\t\t\t\tsbi(TCCR4A, COM4C1);\n\t\t\t\tOCR4C = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\t\t\t\t\n\t\t\t#if defined(TCCR4C) && defined(COM4D1)\n\t\t\tcase TIMER4D:\t\t\t\t\n\t\t\t\t// connect pwm to pin on timer 4, channel D\n\t\t\t\tsbi(TCCR4C, COM4D1);\n\t\t\t\t#if defined(COM4D0)\t\t// only used on 32U4\n\t\t\t\tcbi(TCCR4C, COM4D0);\n\t\t\t\t#endif\n\t\t\t\tOCR4D = val;\t// set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t\t\t\t\t\n\t\t\t#if defined(TCCR5A) && defined(COM5A1)\n\t\t\tcase TIMER5A:\n\t\t\t\t// connect pwm to pin on timer 5, channel A\n\t\t\t\tsbi(TCCR5A, COM5A1);\n\t\t\t\tOCR5A = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR5A) && defined(COM5B1)\n\t\t\tcase TIMER5B:\n\t\t\t\t// connect pwm to pin on timer 5, channel B\n\t\t\t\tsbi(TCCR5A, COM5B1);\n\t\t\t\tOCR5B = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\t#if defined(TCCR5A) && defined(COM5C1)\n\t\t\tcase TIMER5C:\n\t\t\t\t// connect pwm to pin on timer 5, channel C\n\t\t\t\tsbi(TCCR5A, COM5C1);\n\t\t\t\tOCR5C = val; // set pwm duty\n\t\t\t\tbreak;\n\t\t\t#endif\n\n\t\t\tcase NOT_ON_TIMER:\n\t\t\tdefault:\n\t\t\t\tif (val < 128) {\n\t\t\t\t\tdigitalWrite(pin, LOW);\n\t\t\t\t} else {\n\t\t\t\t\tdigitalWrite(pin, HIGH);\n\t\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/wiring_digital.c",
    "content": "/*\n  wiring_digital.c - digital input and output functions\n  Part of Arduino - http://www.arduino.cc/\n\n  Copyright (c) 2005-2006 David A. Mellis\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n\n  Modified 28 September 2010 by Mark Sproul\n\n  $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $\n*/\n\n#define ARDUINO_MAIN\n#include \"wiring_private.h\"\n#include \"pins_arduino.h\"\n\nvoid pinMode(uint8_t pin, uint8_t mode)\n{\n\tuint8_t bit = digitalPinToBitMask(pin);\n\tuint8_t port = digitalPinToPort(pin);\n\tvolatile uint8_t *reg, *out;\n\n\tif (port == NOT_A_PIN) return;\n\n\t// JWS: can I let the optimizer do this?\n\treg = portModeRegister(port);\n\tout = portOutputRegister(port);\n\n\tif (mode == INPUT) { \n\t\tuint8_t oldSREG = SREG;\n                cli();\n\t\t*reg &= ~bit;\n\t\t*out &= ~bit;\n\t\tSREG = oldSREG;\n\t} else if (mode == INPUT_PULLUP) {\n\t\tuint8_t oldSREG = SREG;\n                cli();\n\t\t*reg &= ~bit;\n\t\t*out |= bit;\n\t\tSREG = oldSREG;\n\t} else {\n\t\tuint8_t oldSREG = SREG;\n                cli();\n\t\t*reg |= bit;\n\t\tSREG = oldSREG;\n\t}\n}\n\n// Forcing this inline keeps the callers from having to push their own stuff\n// on the stack. It is a good performance win and only takes 1 more byte per\n// user than calling. (It will take more bytes on the 168.)\n//\n// But shouldn't this be moved into pinMode? Seems silly to check and do on\n// each digitalread or write.\n//\n// Mark Sproul:\n// - Removed inline. Save 170 bytes on atmega1280\n// - changed to a switch statment; added 32 bytes but much easier to read and maintain.\n// - Added more #ifdefs, now compiles for atmega645\n//\n//static inline void turnOffPWM(uint8_t timer) __attribute__ ((always_inline));\n//static inline void turnOffPWM(uint8_t timer)\nstatic void turnOffPWM(uint8_t timer)\n{\n\tswitch (timer)\n\t{\n\t\t#if defined(TCCR1A) && defined(COM1A1)\n\t\tcase TIMER1A:   cbi(TCCR1A, COM1A1);    break;\n\t\t#endif\n\t\t#if defined(TCCR1A) && defined(COM1B1)\n\t\tcase TIMER1B:   cbi(TCCR1A, COM1B1);    break;\n\t\t#endif\n\t\t\n\t\t#if defined(TCCR2) && defined(COM21)\n\t\tcase  TIMER2:   cbi(TCCR2, COM21);      break;\n\t\t#endif\n\t\t\n\t\t#if defined(TCCR0A) && defined(COM0A1)\n\t\tcase  TIMER0A:  cbi(TCCR0A, COM0A1);    break;\n\t\t#endif\n\t\t\n\t\t#if defined(TIMER0B) && defined(COM0B1)\n\t\tcase  TIMER0B:  cbi(TCCR0A, COM0B1);    break;\n\t\t#endif\n\t\t#if defined(TCCR2A) && defined(COM2A1)\n\t\tcase  TIMER2A:  cbi(TCCR2A, COM2A1);    break;\n\t\t#endif\n\t\t#if defined(TCCR2A) && defined(COM2B1)\n\t\tcase  TIMER2B:  cbi(TCCR2A, COM2B1);    break;\n\t\t#endif\n\t\t\n\t\t#if defined(TCCR3A) && defined(COM3A1)\n\t\tcase  TIMER3A:  cbi(TCCR3A, COM3A1);    break;\n\t\t#endif\n\t\t#if defined(TCCR3A) && defined(COM3B1)\n\t\tcase  TIMER3B:  cbi(TCCR3A, COM3B1);    break;\n\t\t#endif\n\t\t#if defined(TCCR3A) && defined(COM3C1)\n\t\tcase  TIMER3C:  cbi(TCCR3A, COM3C1);    break;\n\t\t#endif\n\n\t\t#if defined(TCCR4A) && defined(COM4A1)\n\t\tcase  TIMER4A:  cbi(TCCR4A, COM4A1);    break;\n\t\t#endif\t\t\t\t\t\n\t\t#if defined(TCCR4A) && defined(COM4B1)\n\t\tcase  TIMER4B:  cbi(TCCR4A, COM4B1);    break;\n\t\t#endif\n\t\t#if defined(TCCR4A) && defined(COM4C1)\n\t\tcase  TIMER4C:  cbi(TCCR4A, COM4C1);    break;\n\t\t#endif\t\t\t\n\t\t#if defined(TCCR4C) && defined(COM4D1)\n\t\tcase TIMER4D:\tcbi(TCCR4C, COM4D1);\tbreak;\n\t\t#endif\t\t\t\n\t\t\t\n\t\t#if defined(TCCR5A)\n\t\tcase  TIMER5A:  cbi(TCCR5A, COM5A1);    break;\n\t\tcase  TIMER5B:  cbi(TCCR5A, COM5B1);    break;\n\t\tcase  TIMER5C:  cbi(TCCR5A, COM5C1);    break;\n\t\t#endif\n\t}\n}\n\nvoid digitalWrite(uint8_t pin, uint8_t val)\n{\n\tuint8_t timer = digitalPinToTimer(pin);\n\tuint8_t bit = digitalPinToBitMask(pin);\n\tuint8_t port = digitalPinToPort(pin);\n\tvolatile uint8_t *out;\n\n\tif (port == NOT_A_PIN) return;\n\n\t// If the pin that support PWM output, we need to turn it off\n\t// before doing a digital write.\n\tif (timer != NOT_ON_TIMER) turnOffPWM(timer);\n\n\tout = portOutputRegister(port);\n\n\tuint8_t oldSREG = SREG;\n\tcli();\n\n\tif (val == LOW) {\n\t\t*out &= ~bit;\n\t} else {\n\t\t*out |= bit;\n\t}\n\n\tSREG = oldSREG;\n}\n\nint digitalRead(uint8_t pin)\n{\n\tuint8_t timer = digitalPinToTimer(pin);\n\tuint8_t bit = digitalPinToBitMask(pin);\n\tuint8_t port = digitalPinToPort(pin);\n\n\tif (port == NOT_A_PIN) return LOW;\n\n\t// If the pin that support PWM output, we need to turn it off\n\t// before getting a digital reading.\n\tif (timer != NOT_ON_TIMER) turnOffPWM(timer);\n\n\tif (*portInputRegister(port) & bit) return HIGH;\n\treturn LOW;\n}\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/wiring_private.h",
    "content": "/*\n  wiring_private.h - Internal header file.\n  Part of Arduino - http://www.arduino.cc/\n\n  Copyright (c) 2005-2006 David A. Mellis\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n\n  $Id: wiring.h 239 2007-01-12 17:58:39Z mellis $\n*/\n\n#ifndef WiringPrivate_h\n#define WiringPrivate_h\n\n#include <avr/io.h>\n#include <avr/interrupt.h>\n#include <stdio.h>\n#include <stdarg.h>\n\n#include \"Arduino.h\"\n\n#ifdef __cplusplus\nextern \"C\"{\n#endif\n\n#ifndef cbi\n#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))\n#endif\n#ifndef sbi\n#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))\n#endif\n\n#define EXTERNAL_INT_0 0\n#define EXTERNAL_INT_1 1\n#define EXTERNAL_INT_2 2\n#define EXTERNAL_INT_3 3\n#define EXTERNAL_INT_4 4\n#define EXTERNAL_INT_5 5\n#define EXTERNAL_INT_6 6\n#define EXTERNAL_INT_7 7\n\n#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)\n#define EXTERNAL_NUM_INTERRUPTS 8\n#elif defined(__AVR_ATmega1284P__) \n#define EXTERNAL_NUM_INTERRUPTS 3\n#else\n#define EXTERNAL_NUM_INTERRUPTS 2\n#endif\n\ntypedef void (*voidFuncPtr)(void);\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\n#endif\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/wiring_pulse.c",
    "content": "/*\n  wiring_pulse.c - pulseIn() function\n  Part of Arduino - http://www.arduino.cc/\n\n  Copyright (c) 2005-2006 David A. Mellis\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n\n  $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $\n*/\n\n#include \"wiring_private.h\"\n#include \"pins_arduino.h\"\n\n/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH\n * or LOW, the type of pulse to measure.  Works on pulses from 2-3 microseconds\n * to 3 minutes in length, but must be called at least a few dozen microseconds\n * before the start of the pulse. */\nunsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)\n{\n\t// cache the port and bit of the pin in order to speed up the\n\t// pulse width measuring loop and achieve finer resolution.  calling\n\t// digitalRead() instead yields much coarser resolution.\n\tuint8_t bit = digitalPinToBitMask(pin);\n\tuint8_t port = digitalPinToPort(pin);\n\tuint8_t stateMask = (state ? bit : 0);\n\tunsigned long width = 0; // keep initialization out of time critical area\n\t\n\t// convert the timeout from microseconds to a number of times through\n\t// the initial loop; it takes 16 clock cycles per iteration.\n\tunsigned long numloops = 0;\n\tunsigned long maxloops = microsecondsToClockCycles(timeout) / 16;\n\t\n\t// wait for any previous pulse to end\n\twhile ((*portInputRegister(port) & bit) == stateMask)\n\t\tif (numloops++ == maxloops)\n\t\t\treturn 0;\n\t\n\t// wait for the pulse to start\n\twhile ((*portInputRegister(port) & bit) != stateMask)\n\t\tif (numloops++ == maxloops)\n\t\t\treturn 0;\n\t\n\t// wait for the pulse to stop\n\twhile ((*portInputRegister(port) & bit) == stateMask) {\n\t\tif (numloops++ == maxloops)\n\t\t\treturn 0;\n\t\twidth++;\n\t}\n\n\t// convert the reading to microseconds. The loop has been determined\n\t// to be 20 clock cycles long and have about 16 clocks between the edge\n\t// and the start of the loop. There will be some error introduced by\n\t// the interrupt handlers.\n\treturn clockCyclesToMicroseconds(width * 21 + 16); \n}\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/cores/arduino/wiring_shift.c",
    "content": "/*\n  wiring_shift.c - shiftOut() function\n  Part of Arduino - http://www.arduino.cc/\n\n  Copyright (c) 2005-2006 David A. Mellis\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n\n  $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $\n*/\n\n#include \"wiring_private.h\"\n\nuint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {\n\tuint8_t value = 0;\n\tuint8_t i;\n\n\tfor (i = 0; i < 8; ++i) {\n\t\tdigitalWrite(clockPin, HIGH);\n\t\tif (bitOrder == LSBFIRST)\n\t\t\tvalue |= digitalRead(dataPin) << i;\n\t\telse\n\t\t\tvalue |= digitalRead(dataPin) << (7 - i);\n\t\tdigitalWrite(clockPin, LOW);\n\t}\n\treturn value;\n}\n\nvoid shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val)\n{\n\tuint8_t i;\n\n\tfor (i = 0; i < 8; i++)  {\n\t\tif (bitOrder == LSBFIRST)\n\t\t\tdigitalWrite(dataPin, !!(val & (1 << i)));\n\t\telse\t\n\t\t\tdigitalWrite(dataPin, !!(val & (1 << (7 - i))));\n\t\t\t\n\t\tdigitalWrite(clockPin, HIGH);\n\t\tdigitalWrite(clockPin, LOW);\t\t\n\t}\n}\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/variants/eightanaloginputs/pins_arduino.h",
    "content": "/*\n  pins_arduino.h - Pin definition functions for Arduino\n  Part of Arduino - http://www.arduino.cc/\n\n  Copyright (c) 2007 David A. Mellis\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n\n  $Id: wiring.h 249 2007-02-03 16:52:51Z mellis $\n*/\n\n#include \"../standard/pins_arduino.h\"\n#undef NUM_ANALOG_INPUTS\n#define NUM_ANALOG_INPUTS           8\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/variants/leonardo/pins_arduino.h",
    "content": "/*\n  pins_arduino.h - Pin definition functions for Arduino\n  Part of Arduino - http://www.arduino.cc/\n\n  Copyright (c) 2007 David A. Mellis\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n\n  $Id: wiring.h 249 2007-02-03 16:52:51Z mellis $\n*/\n\n#ifndef Pins_Arduino_h\n#define Pins_Arduino_h\n\n#include <avr/pgmspace.h>\n\n#define TX_RX_LED_INIT\tDDRD |= (1<<5), DDRB |= (1<<0)\n#define TXLED0\t\t\tPORTD |= (1<<5)\n#define TXLED1\t\t\tPORTD &= ~(1<<5)\n#define RXLED0\t\t\tPORTB |= (1<<0)\n#define RXLED1\t\t\tPORTB &= ~(1<<0)\n\nstatic const uint8_t SDA = 2;\nstatic const uint8_t SCL = 3;\n\n// Map SPI port to 'new' pins D14..D17\nstatic const uint8_t SS   = 17;\nstatic const uint8_t MOSI = 16;\nstatic const uint8_t MISO = 14;\nstatic const uint8_t SCK  = 15;\n\n// Mapping of analog pins as digital I/O\n// A6-A11 share with digital pins\nstatic const uint8_t A0 = 18;\nstatic const uint8_t A1 = 19;\nstatic const uint8_t A2 = 20;\nstatic const uint8_t A3 = 21;\nstatic const uint8_t A4 = 22;\nstatic const uint8_t A5 = 23;\nstatic const uint8_t A6 = 24;\t// D4\nstatic const uint8_t A7 = 25;\t// D6\nstatic const uint8_t A8 = 26;\t// D8\nstatic const uint8_t A9 = 27;\t// D9\nstatic const uint8_t A10 = 28;\t// D10\nstatic const uint8_t A11 = 29;\t// D12\n\n#define digitalPinToPCICR(p)    ((((p) >= 8 && (p) <= 11) || ((p) >= 14 && (p) <= 17) || ((p) >= A8 && (p) <= A10)) ? (&PCICR) : ((uint8_t *)0))\n#define digitalPinToPCICRbit(p) 0\n#define digitalPinToPCMSK(p)    ((((p) >= 8 && (p) <= 11) || ((p) >= 14 && (p) <= 17) || ((p) >= A8 && (p) <= A10)) ? (&PCMSK0) : ((uint8_t *)0))\n#define digitalPinToPCMSKbit(p) ( ((p) >= 8 && (p) <= 11) ? (p) - 4 : ((p) == 14 ? 3 : ((p) == 15 ? 1 : ((p) == 16 ? 2 : ((p) == 17 ? 0 : (p - A8 + 4))))))\n\n//\t__AVR_ATmega32U4__ has an unusual mapping of pins to channels\nextern const uint8_t PROGMEM analog_pin_to_channel_PGM[];\n#define analogPinToChannel(P)  ( pgm_read_byte( analog_pin_to_channel_PGM + (P) ) )\n\n#ifdef ARDUINO_MAIN\n\n// On the Arduino board, digital pins are also used\n// for the analog output (software PWM).  Analog input\n// pins are a separate set.\n\n// ATMEL ATMEGA32U4 / ARDUINO LEONARDO\n//\n// D0\t\t\t\tPD2\t\t\t\t\tRXD1/INT2\n// D1\t\t\t\tPD3\t\t\t\t\tTXD1/INT3\n// D2\t\t\t\tPD1\t\tSDA\t\t\tSDA/INT1\n// D3#\t\t\t\tPD0\t\tPWM8/SCL\tOC0B/SCL/INT0\n// D4\t\tA6\t\tPD4\t\t\t\t\tADC8\n// D5#\t\t\t\tPC6\t\t???\t\t\tOC3A/#OC4A\n// D6#\t\tA7\t\tPD7\t\tFastPWM\t\t#OC4D/ADC10\n// D7\t\t\t\tPE6\t\t\t\t\tINT6/AIN0\n//\n// D8\t\tA8\t\tPB4\t\t\t\t\tADC11/PCINT4\n// D9#\t\tA9\t\tPB5\t\tPWM16\t\tOC1A/#OC4B/ADC12/PCINT5\n// D10#\t\tA10\t\tPB6\t\tPWM16\t\tOC1B/0c4B/ADC13/PCINT6\n// D11#\t\t\t\tPB7\t\tPWM8/16\t\t0C0A/OC1C/#RTS/PCINT7\n// D12\t\tA11\t\tPD6\t\t\t\t\tT1/#OC4D/ADC9\n// D13#\t\t\t\tPC7\t\tPWM10\t\tCLK0/OC4A\n//\n// A0\t\tD18\t\tPF7\t\t\t\t\tADC7\n// A1\t\tD19\t\tPF6\t\t\t\t\tADC6\n// A2\t\tD20 \tPF5\t\t\t\t\tADC5\n// A3\t\tD21 \tPF4\t\t\t\t\tADC4\n// A4\t\tD22\t\tPF1\t\t\t\t\tADC1\n// A5\t\tD23 \tPF0\t\t\t\t\tADC0\n//\n// New pins D14..D17 to map SPI port to digital pins\n//\n// MISO\t\tD14\t\tPB3\t\t\t\t\tMISO,PCINT3\n// SCK\t\tD15\t\tPB1\t\t\t\t\tSCK,PCINT1\n// MOSI\t\tD16\t\tPB2\t\t\t\t\tMOSI,PCINT2\n// SS\t\tD17\t\tPB0\t\t\t\t\tRXLED,SS/PCINT0\n//\n// TXLED\t\t\tPD5\n// RXLED\t\t    PB0\n// HWB\t\t\t\tPE2\t\t\t\t\tHWB\n\n// these arrays map port names (e.g. port B) to the\n// appropriate addresses for various functions (e.g. reading\n// and writing)\nconst uint16_t PROGMEM port_to_mode_PGM[] = {\n\tNOT_A_PORT,\n\tNOT_A_PORT,\n\t(uint16_t) &DDRB,\n\t(uint16_t) &DDRC,\n\t(uint16_t) &DDRD,\n\t(uint16_t) &DDRE,\n\t(uint16_t) &DDRF,\n};\n\nconst uint16_t PROGMEM port_to_output_PGM[] = {\n\tNOT_A_PORT,\n\tNOT_A_PORT,\n\t(uint16_t) &PORTB,\n\t(uint16_t) &PORTC,\n\t(uint16_t) &PORTD,\n\t(uint16_t) &PORTE,\n\t(uint16_t) &PORTF,\n};\n\nconst uint16_t PROGMEM port_to_input_PGM[] = {\n\tNOT_A_PORT,\n\tNOT_A_PORT,\n\t(uint16_t) &PINB,\n\t(uint16_t) &PINC,\n\t(uint16_t) &PIND,\n\t(uint16_t) &PINE,\n\t(uint16_t) &PINF,\n};\n\nconst uint8_t PROGMEM digital_pin_to_port_PGM[30] = {\n\tPD, // D0 - PD2\n\tPD,\t// D1 - PD3\n\tPD, // D2 - PD1\n\tPD,\t// D3 - PD0\n\tPD,\t// D4 - PD4\n\tPC, // D5 - PC6\n\tPD, // D6 - PD7\n\tPE, // D7 - PE6\n\t\n\tPB, // D8 - PB4\n\tPB,\t// D9 - PB5\n\tPB, // D10 - PB6\n\tPB,\t// D11 - PB7\n\tPD, // D12 - PD6\n\tPC, // D13 - PC7\n\t\n\tPB,\t// D14 - MISO - PB3\n\tPB,\t// D15 - SCK - PB1\n\tPB,\t// D16 - MOSI - PB2\n\tPB,\t// D17 - SS - PB0\n\t\n\tPF,\t// D18 - A0 - PF7\n\tPF, // D19 - A1 - PF6\n\tPF, // D20 - A2 - PF5\n\tPF, // D21 - A3 - PF4\n\tPF, // D22 - A4 - PF1\n\tPF, // D23 - A5 - PF0\n\t\n\tPD, // D24 / D4 - A6 - PD4\n\tPD, // D25 / D6 - A7 - PD7\n\tPB, // D26 / D8 - A8 - PB4\n\tPB, // D27 / D9 - A9 - PB5\n\tPB, // D28 / D10 - A10 - PB6\n\tPD, // D29 / D12 - A11 - PD6\n};\n\nconst uint8_t PROGMEM digital_pin_to_bit_mask_PGM[30] = {\n\t_BV(2), // D0 - PD2\n\t_BV(3),\t// D1 - PD3\n\t_BV(1), // D2 - PD1\n\t_BV(0),\t// D3 - PD0\n\t_BV(4),\t// D4 - PD4\n\t_BV(6), // D5 - PC6\n\t_BV(7), // D6 - PD7\n\t_BV(6), // D7 - PE6\n\t\n\t_BV(4), // D8 - PB4\n\t_BV(5),\t// D9 - PB5\n\t_BV(6), // D10 - PB6\n\t_BV(7),\t// D11 - PB7\n\t_BV(6), // D12 - PD6\n\t_BV(7), // D13 - PC7\n\t\n\t_BV(3),\t// D14 - MISO - PB3\n\t_BV(1),\t// D15 - SCK - PB1\n\t_BV(2),\t// D16 - MOSI - PB2\n\t_BV(0),\t// D17 - SS - PB0\n\t\n\t_BV(7),\t// D18 - A0 - PF7\n\t_BV(6), // D19 - A1 - PF6\n\t_BV(5), // D20 - A2 - PF5\n\t_BV(4), // D21 - A3 - PF4\n\t_BV(1), // D22 - A4 - PF1\n\t_BV(0), // D23 - A5 - PF0\n\t\n\t_BV(4), // D24 / D4 - A6 - PD4\n\t_BV(7), // D25 / D6 - A7 - PD7\n\t_BV(4), // D26 / D8 - A8 - PB4\n\t_BV(5), // D27 / D9 - A9 - PB5\n\t_BV(6), // D28 / D10 - A10 - PB6\n\t_BV(6), // D29 / D12 - A11 - PD6\n};\n\nconst uint8_t PROGMEM digital_pin_to_timer_PGM[16] = {\n\tNOT_ON_TIMER,\t\n\tNOT_ON_TIMER,\n\tNOT_ON_TIMER,\n\tTIMER0B,\t\t/* 3 */\n\tNOT_ON_TIMER,\n\tTIMER3A,\t\t/* 5 */\n\tTIMER4D,\t\t/* 6 */\n\tNOT_ON_TIMER,\t\n\t\n\tNOT_ON_TIMER,\t\n\tTIMER1A,\t\t/* 9 */\n\tTIMER1B,\t\t/* 10 */\n\tTIMER0A,\t\t/* 11 */\n\t\n\tNOT_ON_TIMER,\t\n\tTIMER4A,\t\t/* 13 */\n\t\n\tNOT_ON_TIMER,\t\n\tNOT_ON_TIMER,\n};\n\nconst uint8_t PROGMEM analog_pin_to_channel_PGM[12] = {\n\t7,\t// A0\t\t\t\tPF7\t\t\t\t\tADC7\n\t6,\t// A1\t\t\t\tPF6\t\t\t\t\tADC6\t\n\t5,\t// A2\t\t\t\tPF5\t\t\t\t\tADC5\t\n\t4,\t// A3\t\t\t\tPF4\t\t\t\t\tADC4\n\t1,\t// A4\t\t\t\tPF1\t\t\t\t\tADC1\t\n\t0,\t// A5\t\t\t\tPF0\t\t\t\t\tADC0\t\n\t8,\t// A6\t\tD4\t\tPD4\t\t\t\t\tADC8\n\t10,\t// A7\t\tD6\t\tPD7\t\t\t\t\tADC10\n\t11,\t// A8\t\tD8\t\tPB4\t\t\t\t\tADC11\n\t12,\t// A9\t\tD9\t\tPB5\t\t\t\t\tADC12\n\t13,\t// A10\t\tD10\t\tPB6\t\t\t\t\tADC13\n\t9\t// A11\t\tD12\t\tPD6\t\t\t\t\tADC9\n};\n\n#endif /* ARDUINO_MAIN */\n#endif /* Pins_Arduino_h */\n"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/variants/mega/pins_arduino.h",
    "content": "/*\n  pins_arduino.h - Pin definition functions for Arduino\n  Part of Arduino - http://www.arduino.cc/\n\n  Copyright (c) 2007 David A. Mellis\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n\n  $Id: wiring.h 249 2007-02-03 16:52:51Z mellis $\n*/\n\n#ifndef Pins_Arduino_h\n#define Pins_Arduino_h\n\n#include <avr/pgmspace.h>\n\n#define NUM_DIGITAL_PINS            70\n#define NUM_ANALOG_INPUTS           16\n#define analogInputToDigitalPin(p)  ((p < 16) ? (p) + 54 : -1)\n#define digitalPinHasPWM(p)         (((p) >= 2 && (p) <= 13) || ((p) >= 44 && (p)<= 46))\n\nstatic const uint8_t SS   = 53;\nstatic const uint8_t MOSI = 51;\nstatic const uint8_t MISO = 50;\nstatic const uint8_t SCK  = 52;\n\nstatic const uint8_t SDA = 20;\nstatic const uint8_t SCL = 21;\nstatic const uint8_t LED_BUILTIN = 13;\n\nstatic const uint8_t A0 = 54;\nstatic const uint8_t A1 = 55;\nstatic const uint8_t A2 = 56;\nstatic const uint8_t A3 = 57;\nstatic const uint8_t A4 = 58;\nstatic const uint8_t A5 = 59;\nstatic const uint8_t A6 = 60;\nstatic const uint8_t A7 = 61;\nstatic const uint8_t A8 = 62;\nstatic const uint8_t A9 = 63;\nstatic const uint8_t A10 = 64;\nstatic const uint8_t A11 = 65;\nstatic const uint8_t A12 = 66;\nstatic const uint8_t A13 = 67;\nstatic const uint8_t A14 = 68;\nstatic const uint8_t A15 = 69;\n\n// A majority of the pins are NOT PCINTs, SO BE WARNED (i.e. you cannot use them as receive pins)\n// Only pins available for RECEIVE (TRANSMIT can be on any pin):\n// (I've deliberately left out pin mapping to the Hardware USARTs - seems senseless to me)\n// Pins: 10, 11, 12, 13,  50, 51, 52, 53,  62, 63, 64, 65, 66, 67, 68, 69\n\n#define digitalPinToPCICR(p)    ( (((p) >= 10) && ((p) <= 13)) || \\\n                                  (((p) >= 50) && ((p) <= 53)) || \\\n                                  (((p) >= 62) && ((p) <= 69)) ? (&PCICR) : ((uint8_t *)0) )\n\n#define digitalPinToPCICRbit(p) ( (((p) >= 10) && ((p) <= 13)) || (((p) >= 50) && ((p) <= 53)) ? 0 : \\\n                                ( (((p) >= 62) && ((p) <= 69)) ? 2 : \\\n                                0 ) )\n\n#define digitalPinToPCMSK(p)    ( (((p) >= 10) && ((p) <= 13)) || (((p) >= 50) && ((p) <= 53)) ? (&PCMSK0) : \\\n                                ( (((p) >= 62) && ((p) <= 69)) ? (&PCMSK2) : \\\n                                ((uint8_t *)0) ) )\n\n#define digitalPinToPCMSKbit(p) ( (((p) >= 10) && ((p) <= 13)) ? ((p) - 6) : \\\n                                ( ((p) == 50) ? 3 : \\\n                                ( ((p) == 51) ? 2 : \\\n                                ( ((p) == 52) ? 1 : \\\n                                ( ((p) == 53) ? 0 : \\\n                                ( (((p) >= 62) && ((p) <= 69)) ? ((p) - 62) : \\\n                                0 ) ) ) ) ) )\n\n#ifdef ARDUINO_MAIN\n\nconst uint16_t PROGMEM port_to_mode_PGM[] = {\n\tNOT_A_PORT,\n\t(uint16_t) &DDRA,\n\t(uint16_t) &DDRB,\n\t(uint16_t) &DDRC,\n\t(uint16_t) &DDRD,\n\t(uint16_t) &DDRE,\n\t(uint16_t) &DDRF,\n\t(uint16_t) &DDRG,\n\t(uint16_t) &DDRH,\n\tNOT_A_PORT,\n\t(uint16_t) &DDRJ,\n\t(uint16_t) &DDRK,\n\t(uint16_t) &DDRL,\n};\n\nconst uint16_t PROGMEM port_to_output_PGM[] = {\n\tNOT_A_PORT,\n\t(uint16_t) &PORTA,\n\t(uint16_t) &PORTB,\n\t(uint16_t) &PORTC,\n\t(uint16_t) &PORTD,\n\t(uint16_t) &PORTE,\n\t(uint16_t) &PORTF,\n\t(uint16_t) &PORTG,\n\t(uint16_t) &PORTH,\n\tNOT_A_PORT,\n\t(uint16_t) &PORTJ,\n\t(uint16_t) &PORTK,\n\t(uint16_t) &PORTL,\n};\n\nconst uint16_t PROGMEM port_to_input_PGM[] = {\n\tNOT_A_PIN,\n\t(uint16_t) &PINA,\n\t(uint16_t) &PINB,\n\t(uint16_t) &PINC,\n\t(uint16_t) &PIND,\n\t(uint16_t) &PINE,\n\t(uint16_t) &PINF,\n\t(uint16_t) &PING,\n\t(uint16_t) &PINH,\n\tNOT_A_PIN,\n\t(uint16_t) &PINJ,\n\t(uint16_t) &PINK,\n\t(uint16_t) &PINL,\n};\n\nconst uint8_t PROGMEM digital_pin_to_port_PGM[] = {\n\t// PORTLIST\t\t\n\t// -------------------------------------------\t\t\n\tPE\t, // PE 0 ** 0 ** USART0_RX\t\n\tPE\t, // PE 1 ** 1 ** USART0_TX\t\n\tPE\t, // PE 4 ** 2 ** PWM2\t\n\tPE\t, // PE 5 ** 3 ** PWM3\t\n\tPG\t, // PG 5 ** 4 ** PWM4\t\n\tPE\t, // PE 3 ** 5 ** PWM5\t\n\tPH\t, // PH 3 ** 6 ** PWM6\t\n\tPH\t, // PH 4 ** 7 ** PWM7\t\n\tPH\t, // PH 5 ** 8 ** PWM8\t\n\tPH\t, // PH 6 ** 9 ** PWM9\t\n\tPB\t, // PB 4 ** 10 ** PWM10\t\n\tPB\t, // PB 5 ** 11 ** PWM11\t\n\tPB\t, // PB 6 ** 12 ** PWM12\t\n\tPB\t, // PB 7 ** 13 ** PWM13\t\n\tPJ\t, // PJ 1 ** 14 ** USART3_TX\t\n\tPJ\t, // PJ 0 ** 15 ** USART3_RX\t\n\tPH\t, // PH 1 ** 16 ** USART2_TX\t\n\tPH\t, // PH 0 ** 17 ** USART2_RX\t\n\tPD\t, // PD 3 ** 18 ** USART1_TX\t\n\tPD\t, // PD 2 ** 19 ** USART1_RX\t\n\tPD\t, // PD 1 ** 20 ** I2C_SDA\t\n\tPD\t, // PD 0 ** 21 ** I2C_SCL\t\n\tPA\t, // PA 0 ** 22 ** D22\t\n\tPA\t, // PA 1 ** 23 ** D23\t\n\tPA\t, // PA 2 ** 24 ** D24\t\n\tPA\t, // PA 3 ** 25 ** D25\t\n\tPA\t, // PA 4 ** 26 ** D26\t\n\tPA\t, // PA 5 ** 27 ** D27\t\n\tPA\t, // PA 6 ** 28 ** D28\t\n\tPA\t, // PA 7 ** 29 ** D29\t\n\tPC\t, // PC 7 ** 30 ** D30\t\n\tPC\t, // PC 6 ** 31 ** D31\t\n\tPC\t, // PC 5 ** 32 ** D32\t\n\tPC\t, // PC 4 ** 33 ** D33\t\n\tPC\t, // PC 3 ** 34 ** D34\t\n\tPC\t, // PC 2 ** 35 ** D35\t\n\tPC\t, // PC 1 ** 36 ** D36\t\n\tPC\t, // PC 0 ** 37 ** D37\t\n\tPD\t, // PD 7 ** 38 ** D38\t\n\tPG\t, // PG 2 ** 39 ** D39\t\n\tPG\t, // PG 1 ** 40 ** D40\t\n\tPG\t, // PG 0 ** 41 ** D41\t\n\tPL\t, // PL 7 ** 42 ** D42\t\n\tPL\t, // PL 6 ** 43 ** D43\t\n\tPL\t, // PL 5 ** 44 ** D44\t\n\tPL\t, // PL 4 ** 45 ** D45\t\n\tPL\t, // PL 3 ** 46 ** D46\t\n\tPL\t, // PL 2 ** 47 ** D47\t\n\tPL\t, // PL 1 ** 48 ** D48\t\n\tPL\t, // PL 0 ** 49 ** D49\t\n\tPB\t, // PB 3 ** 50 ** SPI_MISO\t\n\tPB\t, // PB 2 ** 51 ** SPI_MOSI\t\n\tPB\t, // PB 1 ** 52 ** SPI_SCK\t\n\tPB\t, // PB 0 ** 53 ** SPI_SS\t\n\tPF\t, // PF 0 ** 54 ** A0\t\n\tPF\t, // PF 1 ** 55 ** A1\t\n\tPF\t, // PF 2 ** 56 ** A2\t\n\tPF\t, // PF 3 ** 57 ** A3\t\n\tPF\t, // PF 4 ** 58 ** A4\t\n\tPF\t, // PF 5 ** 59 ** A5\t\n\tPF\t, // PF 6 ** 60 ** A6\t\n\tPF\t, // PF 7 ** 61 ** A7\t\n\tPK\t, // PK 0 ** 62 ** A8\t\n\tPK\t, // PK 1 ** 63 ** A9\t\n\tPK\t, // PK 2 ** 64 ** A10\t\n\tPK\t, // PK 3 ** 65 ** A11\t\n\tPK\t, // PK 4 ** 66 ** A12\t\n\tPK\t, // PK 5 ** 67 ** A13\t\n\tPK\t, // PK 6 ** 68 ** A14\t\n\tPK\t, // PK 7 ** 69 ** A15\t\n};\n\nconst uint8_t PROGMEM digital_pin_to_bit_mask_PGM[] = {\n\t// PIN IN PORT\t\t\n\t// -------------------------------------------\t\t\n\t_BV( 0 )\t, // PE 0 ** 0 ** USART0_RX\t\n\t_BV( 1 )\t, // PE 1 ** 1 ** USART0_TX\t\n\t_BV( 4 )\t, // PE 4 ** 2 ** PWM2\t\n\t_BV( 5 )\t, // PE 5 ** 3 ** PWM3\t\n\t_BV( 5 )\t, // PG 5 ** 4 ** PWM4\t\n\t_BV( 3 )\t, // PE 3 ** 5 ** PWM5\t\n\t_BV( 3 )\t, // PH 3 ** 6 ** PWM6\t\n\t_BV( 4 )\t, // PH 4 ** 7 ** PWM7\t\n\t_BV( 5 )\t, // PH 5 ** 8 ** PWM8\t\n\t_BV( 6 )\t, // PH 6 ** 9 ** PWM9\t\n\t_BV( 4 )\t, // PB 4 ** 10 ** PWM10\t\n\t_BV( 5 )\t, // PB 5 ** 11 ** PWM11\t\n\t_BV( 6 )\t, // PB 6 ** 12 ** PWM12\t\n\t_BV( 7 )\t, // PB 7 ** 13 ** PWM13\t\n\t_BV( 1 )\t, // PJ 1 ** 14 ** USART3_TX\t\n\t_BV( 0 )\t, // PJ 0 ** 15 ** USART3_RX\t\n\t_BV( 1 )\t, // PH 1 ** 16 ** USART2_TX\t\n\t_BV( 0 )\t, // PH 0 ** 17 ** USART2_RX\t\n\t_BV( 3 )\t, // PD 3 ** 18 ** USART1_TX\t\n\t_BV( 2 )\t, // PD 2 ** 19 ** USART1_RX\t\n\t_BV( 1 )\t, // PD 1 ** 20 ** I2C_SDA\t\n\t_BV( 0 )\t, // PD 0 ** 21 ** I2C_SCL\t\n\t_BV( 0 )\t, // PA 0 ** 22 ** D22\t\n\t_BV( 1 )\t, // PA 1 ** 23 ** D23\t\n\t_BV( 2 )\t, // PA 2 ** 24 ** D24\t\n\t_BV( 3 )\t, // PA 3 ** 25 ** D25\t\n\t_BV( 4 )\t, // PA 4 ** 26 ** D26\t\n\t_BV( 5 )\t, // PA 5 ** 27 ** D27\t\n\t_BV( 6 )\t, // PA 6 ** 28 ** D28\t\n\t_BV( 7 )\t, // PA 7 ** 29 ** D29\t\n\t_BV( 7 )\t, // PC 7 ** 30 ** D30\t\n\t_BV( 6 )\t, // PC 6 ** 31 ** D31\t\n\t_BV( 5 )\t, // PC 5 ** 32 ** D32\t\n\t_BV( 4 )\t, // PC 4 ** 33 ** D33\t\n\t_BV( 3 )\t, // PC 3 ** 34 ** D34\t\n\t_BV( 2 )\t, // PC 2 ** 35 ** D35\t\n\t_BV( 1 )\t, // PC 1 ** 36 ** D36\t\n\t_BV( 0 )\t, // PC 0 ** 37 ** D37\t\n\t_BV( 7 )\t, // PD 7 ** 38 ** D38\t\n\t_BV( 2 )\t, // PG 2 ** 39 ** D39\t\n\t_BV( 1 )\t, // PG 1 ** 40 ** D40\t\n\t_BV( 0 )\t, // PG 0 ** 41 ** D41\t\n\t_BV( 7 )\t, // PL 7 ** 42 ** D42\t\n\t_BV( 6 )\t, // PL 6 ** 43 ** D43\t\n\t_BV( 5 )\t, // PL 5 ** 44 ** D44\t\n\t_BV( 4 )\t, // PL 4 ** 45 ** D45\t\n\t_BV( 3 )\t, // PL 3 ** 46 ** D46\t\n\t_BV( 2 )\t, // PL 2 ** 47 ** D47\t\n\t_BV( 1 )\t, // PL 1 ** 48 ** D48\t\n\t_BV( 0 )\t, // PL 0 ** 49 ** D49\t\n\t_BV( 3 )\t, // PB 3 ** 50 ** SPI_MISO\t\n\t_BV( 2 )\t, // PB 2 ** 51 ** SPI_MOSI\t\n\t_BV( 1 )\t, // PB 1 ** 52 ** SPI_SCK\t\n\t_BV( 0 )\t, // PB 0 ** 53 ** SPI_SS\t\n\t_BV( 0 )\t, // PF 0 ** 54 ** A0\t\n\t_BV( 1 )\t, // PF 1 ** 55 ** A1\t\n\t_BV( 2 )\t, // PF 2 ** 56 ** A2\t\n\t_BV( 3 )\t, // PF 3 ** 57 ** A3\t\n\t_BV( 4 )\t, // PF 4 ** 58 ** A4\t\n\t_BV( 5 )\t, // PF 5 ** 59 ** A5\t\n\t_BV( 6 )\t, // PF 6 ** 60 ** A6\t\n\t_BV( 7 )\t, // PF 7 ** 61 ** A7\t\n\t_BV( 0 )\t, // PK 0 ** 62 ** A8\t\n\t_BV( 1 )\t, // PK 1 ** 63 ** A9\t\n\t_BV( 2 )\t, // PK 2 ** 64 ** A10\t\n\t_BV( 3 )\t, // PK 3 ** 65 ** A11\t\n\t_BV( 4 )\t, // PK 4 ** 66 ** A12\t\n\t_BV( 5 )\t, // PK 5 ** 67 ** A13\t\n\t_BV( 6 )\t, // PK 6 ** 68 ** A14\t\n\t_BV( 7 )\t, // PK 7 ** 69 ** A15\t\n};\n\nconst uint8_t PROGMEM digital_pin_to_timer_PGM[] = {\n\t// TIMERS\t\t\n\t// -------------------------------------------\t\t\n\tNOT_ON_TIMER\t, // PE 0 ** 0 ** USART0_RX\t\n\tNOT_ON_TIMER\t, // PE 1 ** 1 ** USART0_TX\t\n\tTIMER3B\t, // PE 4 ** 2 ** PWM2\t\n\tTIMER3C\t, // PE 5 ** 3 ** PWM3\t\n\tTIMER0B\t, // PG 5 ** 4 ** PWM4\t\n\tTIMER3A\t, // PE 3 ** 5 ** PWM5\t\n\tTIMER4A\t, // PH 3 ** 6 ** PWM6\t\n\tTIMER4B\t, // PH 4 ** 7 ** PWM7\t\n\tTIMER4C\t, // PH 5 ** 8 ** PWM8\t\n\tTIMER2B\t, // PH 6 ** 9 ** PWM9\t\n\tTIMER2A\t, // PB 4 ** 10 ** PWM10\t\n\tTIMER1A\t, // PB 5 ** 11 ** PWM11\t\n\tTIMER1B\t, // PB 6 ** 12 ** PWM12\t\n\tTIMER0A\t, // PB 7 ** 13 ** PWM13\t\n\tNOT_ON_TIMER\t, // PJ 1 ** 14 ** USART3_TX\t\n\tNOT_ON_TIMER\t, // PJ 0 ** 15 ** USART3_RX\t\n\tNOT_ON_TIMER\t, // PH 1 ** 16 ** USART2_TX\t\n\tNOT_ON_TIMER\t, // PH 0 ** 17 ** USART2_RX\t\n\tNOT_ON_TIMER\t, // PD 3 ** 18 ** USART1_TX\t\n\tNOT_ON_TIMER\t, // PD 2 ** 19 ** USART1_RX\t\n\tNOT_ON_TIMER\t, // PD 1 ** 20 ** I2C_SDA\t\n\tNOT_ON_TIMER\t, // PD 0 ** 21 ** I2C_SCL\t\n\tNOT_ON_TIMER\t, // PA 0 ** 22 ** D22\t\n\tNOT_ON_TIMER\t, // PA 1 ** 23 ** D23\t\n\tNOT_ON_TIMER\t, // PA 2 ** 24 ** D24\t\n\tNOT_ON_TIMER\t, // PA 3 ** 25 ** D25\t\n\tNOT_ON_TIMER\t, // PA 4 ** 26 ** D26\t\n\tNOT_ON_TIMER\t, // PA 5 ** 27 ** D27\t\n\tNOT_ON_TIMER\t, // PA 6 ** 28 ** D28\t\n\tNOT_ON_TIMER\t, // PA 7 ** 29 ** D29\t\n\tNOT_ON_TIMER\t, // PC 7 ** 30 ** D30\t\n\tNOT_ON_TIMER\t, // PC 6 ** 31 ** D31\t\n\tNOT_ON_TIMER\t, // PC 5 ** 32 ** D32\t\n\tNOT_ON_TIMER\t, // PC 4 ** 33 ** D33\t\n\tNOT_ON_TIMER\t, // PC 3 ** 34 ** D34\t\n\tNOT_ON_TIMER\t, // PC 2 ** 35 ** D35\t\n\tNOT_ON_TIMER\t, // PC 1 ** 36 ** D36\t\n\tNOT_ON_TIMER\t, // PC 0 ** 37 ** D37\t\n\tNOT_ON_TIMER\t, // PD 7 ** 38 ** D38\t\n\tNOT_ON_TIMER\t, // PG 2 ** 39 ** D39\t\n\tNOT_ON_TIMER\t, // PG 1 ** 40 ** D40\t\n\tNOT_ON_TIMER\t, // PG 0 ** 41 ** D41\t\n\tNOT_ON_TIMER\t, // PL 7 ** 42 ** D42\t\n\tNOT_ON_TIMER\t, // PL 6 ** 43 ** D43\t\n\tTIMER5C\t, // PL 5 ** 44 ** D44\t\n\tTIMER5B\t, // PL 4 ** 45 ** D45\t\n\tTIMER5A\t, // PL 3 ** 46 ** D46\t\n\tNOT_ON_TIMER\t, // PL 2 ** 47 ** D47\t\n\tNOT_ON_TIMER\t, // PL 1 ** 48 ** D48\t\n\tNOT_ON_TIMER\t, // PL 0 ** 49 ** D49\t\n\tNOT_ON_TIMER\t, // PB 3 ** 50 ** SPI_MISO\t\n\tNOT_ON_TIMER\t, // PB 2 ** 51 ** SPI_MOSI\t\n\tNOT_ON_TIMER\t, // PB 1 ** 52 ** SPI_SCK\t\n\tNOT_ON_TIMER\t, // PB 0 ** 53 ** SPI_SS\t\n\tNOT_ON_TIMER\t, // PF 0 ** 54 ** A0\t\n\tNOT_ON_TIMER\t, // PF 1 ** 55 ** A1\t\n\tNOT_ON_TIMER\t, // PF 2 ** 56 ** A2\t\n\tNOT_ON_TIMER\t, // PF 3 ** 57 ** A3\t\n\tNOT_ON_TIMER\t, // PF 4 ** 58 ** A4\t\n\tNOT_ON_TIMER\t, // PF 5 ** 59 ** A5\t\n\tNOT_ON_TIMER\t, // PF 6 ** 60 ** A6\t\n\tNOT_ON_TIMER\t, // PF 7 ** 61 ** A7\t\n\tNOT_ON_TIMER\t, // PK 0 ** 62 ** A8\t\n\tNOT_ON_TIMER\t, // PK 1 ** 63 ** A9\t\n\tNOT_ON_TIMER\t, // PK 2 ** 64 ** A10\t\n\tNOT_ON_TIMER\t, // PK 3 ** 65 ** A11\t\n\tNOT_ON_TIMER\t, // PK 4 ** 66 ** A12\t\n\tNOT_ON_TIMER\t, // PK 5 ** 67 ** A13\t\n\tNOT_ON_TIMER\t, // PK 6 ** 68 ** A14\t\n\tNOT_ON_TIMER\t, // PK 7 ** 69 ** A15\t\n};\n\n#endif\n\n#endif"
  },
  {
    "path": "lib/usbhost/arduino-1.0.1/variants/standard/pins_arduino.h",
    "content": "/*\n  pins_arduino.h - Pin definition functions for Arduino\n  Part of Arduino - http://www.arduino.cc/\n\n  Copyright (c) 2007 David A. Mellis\n\n  This library is free software; you can redistribute it and/or\n  modify it under the terms of the GNU Lesser General Public\n  License as published by the Free Software Foundation; either\n  version 2.1 of the License, or (at your option) any later version.\n\n  This library is distributed in the hope that it will be useful,\n  but WITHOUT ANY WARRANTY; without even the implied warranty of\n  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n  Lesser General Public License for more details.\n\n  You should have received a copy of the GNU Lesser General\n  Public License along with this library; if not, write to the\n  Free Software Foundation, Inc., 59 Temple Place, Suite 330,\n  Boston, MA  02111-1307  USA\n\n  $Id: wiring.h 249 2007-02-03 16:52:51Z mellis $\n*/\n\n#ifndef Pins_Arduino_h\n#define Pins_Arduino_h\n\n#include <avr/pgmspace.h>\n\n#define NUM_DIGITAL_PINS            20\n#define NUM_ANALOG_INPUTS           6\n#define analogInputToDigitalPin(p)  ((p < 6) ? (p) + 14 : -1)\n\n#if defined(__AVR_ATmega8__)\n#define digitalPinHasPWM(p)         ((p) == 9 || (p) == 10 || (p) == 11)\n#else\n#define digitalPinHasPWM(p)         ((p) == 3 || (p) == 5 || (p) == 6 || (p) == 9 || (p) == 10 || (p) == 11)\n#endif\n\nstatic const uint8_t SS   = 10;\nstatic const uint8_t MOSI = 11;\nstatic const uint8_t MISO = 12;\nstatic const uint8_t SCK  = 13;\n\nstatic const uint8_t SDA = 18;\nstatic const uint8_t SCL = 19;\nstatic const uint8_t LED_BUILTIN = 13;\n\nstatic const uint8_t A0 = 14;\nstatic const uint8_t A1 = 15;\nstatic const uint8_t A2 = 16;\nstatic const uint8_t A3 = 17;\nstatic const uint8_t A4 = 18;\nstatic const uint8_t A5 = 19;\nstatic const uint8_t A6 = 20;\nstatic const uint8_t A7 = 21;\n\n#define digitalPinToPCICR(p)    (((p) >= 0 && (p) <= 21) ? (&PCICR) : ((uint8_t *)0))\n#define digitalPinToPCICRbit(p) (((p) <= 7) ? 2 : (((p) <= 13) ? 0 : 1))\n#define digitalPinToPCMSK(p)    (((p) <= 7) ? (&PCMSK2) : (((p) <= 13) ? (&PCMSK0) : (((p) <= 21) ? (&PCMSK1) : ((uint8_t *)0))))\n#define digitalPinToPCMSKbit(p) (((p) <= 7) ? (p) : (((p) <= 13) ? ((p) - 8) : ((p) - 14)))\n\n#ifdef ARDUINO_MAIN\n\n// On the Arduino board, digital pins are also used\n// for the analog output (software PWM).  Analog input\n// pins are a separate set.\n\n// ATMEL ATMEGA8 & 168 / ARDUINO\n//\n//                  +-\\/-+\n//            PC6  1|    |28  PC5 (AI 5)\n//      (D 0) PD0  2|    |27  PC4 (AI 4)\n//      (D 1) PD1  3|    |26  PC3 (AI 3)\n//      (D 2) PD2  4|    |25  PC2 (AI 2)\n// PWM+ (D 3) PD3  5|    |24  PC1 (AI 1)\n//      (D 4) PD4  6|    |23  PC0 (AI 0)\n//            VCC  7|    |22  GND\n//            GND  8|    |21  AREF\n//            PB6  9|    |20  AVCC\n//            PB7 10|    |19  PB5 (D 13)\n// PWM+ (D 5) PD5 11|    |18  PB4 (D 12)\n// PWM+ (D 6) PD6 12|    |17  PB3 (D 11) PWM\n//      (D 7) PD7 13|    |16  PB2 (D 10) PWM\n//      (D 8) PB0 14|    |15  PB1 (D 9) PWM\n//                  +----+\n//\n// (PWM+ indicates the additional PWM pins on the ATmega168.)\n\n// ATMEL ATMEGA1280 / ARDUINO\n//\n// 0-7 PE0-PE7   works\n// 8-13 PB0-PB5  works\n// 14-21 PA0-PA7 works \n// 22-29 PH0-PH7 works\n// 30-35 PG5-PG0 works\n// 36-43 PC7-PC0 works\n// 44-51 PJ7-PJ0 works\n// 52-59 PL7-PL0 works\n// 60-67 PD7-PD0 works\n// A0-A7 PF0-PF7\n// A8-A15 PK0-PK7\n\n\n// these arrays map port names (e.g. port B) to the\n// appropriate addresses for various functions (e.g. reading\n// and writing)\nconst uint16_t PROGMEM port_to_mode_PGM[] = {\n\tNOT_A_PORT,\n\tNOT_A_PORT,\n\t(uint16_t) &DDRB,\n\t(uint16_t) &DDRC,\n\t(uint16_t) &DDRD,\n};\n\nconst uint16_t PROGMEM port_to_output_PGM[] = {\n\tNOT_A_PORT,\n\tNOT_A_PORT,\n\t(uint16_t) &PORTB,\n\t(uint16_t) &PORTC,\n\t(uint16_t) &PORTD,\n};\n\nconst uint16_t PROGMEM port_to_input_PGM[] = {\n\tNOT_A_PORT,\n\tNOT_A_PORT,\n\t(uint16_t) &PINB,\n\t(uint16_t) &PINC,\n\t(uint16_t) &PIND,\n};\n\nconst uint8_t PROGMEM digital_pin_to_port_PGM[] = {\n\tPD, /* 0 */\n\tPD,\n\tPD,\n\tPD,\n\tPD,\n\tPD,\n\tPD,\n\tPD,\n\tPB, /* 8 */\n\tPB,\n\tPB,\n\tPB,\n\tPB,\n\tPB,\n\tPC, /* 14 */\n\tPC,\n\tPC,\n\tPC,\n\tPC,\n\tPC,\n};\n\nconst uint8_t PROGMEM digital_pin_to_bit_mask_PGM[] = {\n\t_BV(0), /* 0, port D */\n\t_BV(1),\n\t_BV(2),\n\t_BV(3),\n\t_BV(4),\n\t_BV(5),\n\t_BV(6),\n\t_BV(7),\n\t_BV(0), /* 8, port B */\n\t_BV(1),\n\t_BV(2),\n\t_BV(3),\n\t_BV(4),\n\t_BV(5),\n\t_BV(0), /* 14, port C */\n\t_BV(1),\n\t_BV(2),\n\t_BV(3),\n\t_BV(4),\n\t_BV(5),\n};\n\nconst uint8_t PROGMEM digital_pin_to_timer_PGM[] = {\n\tNOT_ON_TIMER, /* 0 - port D */\n\tNOT_ON_TIMER,\n\tNOT_ON_TIMER,\n\t// on the ATmega168, digital pin 3 has hardware pwm\n#if defined(__AVR_ATmega8__)\n\tNOT_ON_TIMER,\n#else\n\tTIMER2B,\n#endif\n\tNOT_ON_TIMER,\n\t// on the ATmega168, digital pins 5 and 6 have hardware pwm\n#if defined(__AVR_ATmega8__)\n\tNOT_ON_TIMER,\n\tNOT_ON_TIMER,\n#else\n\tTIMER0B,\n\tTIMER0A,\n#endif\n\tNOT_ON_TIMER,\n\tNOT_ON_TIMER, /* 8 - port B */\n\tTIMER1A,\n\tTIMER1B,\n#if defined(__AVR_ATmega8__)\n\tTIMER2,\n#else\n\tTIMER2A,\n#endif\n\tNOT_ON_TIMER,\n\tNOT_ON_TIMER,\n\tNOT_ON_TIMER,\n\tNOT_ON_TIMER, /* 14 - port C */\n\tNOT_ON_TIMER,\n\tNOT_ON_TIMER,\n\tNOT_ON_TIMER,\n\tNOT_ON_TIMER,\n};\n\n#endif\n\n#endif\n"
  },
  {
    "path": "license_GPLv2.md",
    "content": "The GNU General Public License, Version 2, June 1991 (GPLv2)\n============================================================\n\n> Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n> 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\n\nEveryone is permitted to copy and distribute verbatim copies of this license\ndocument, but changing it is not allowed.\n\n\nPreamble\n--------\n\nThe licenses for most software are designed to take away your freedom to share\nand change it. By contrast, the GNU General Public License is intended to\nguarantee your freedom to share and change free software--to make sure the\nsoftware is free for all its users. This General Public License applies to most\nof the Free Software Foundation's software and to any other program whose\nauthors commit to using it. (Some other Free Software Foundation software is\ncovered by the GNU Lesser General Public License instead.) You can apply it to\nyour programs, too.\n\nWhen we speak of free software, we are referring to freedom, not price. Our\nGeneral Public Licenses are designed to make sure that you have the freedom to\ndistribute copies of free software (and charge for this service if you wish),\nthat you receive source code or can get it if you want it, that you can change\nthe software or use pieces of it in new free programs; and that you know you can\ndo these things.\n\nTo protect your rights, we need to make restrictions that forbid anyone to deny\nyou these rights or to ask you to surrender the rights. These restrictions\ntranslate to certain responsibilities for you if you distribute copies of the\nsoftware, or if you modify it.\n\nFor example, if you distribute copies of such a program, whether gratis or for a\nfee, you must give the recipients all the rights that you have. You must make\nsure that they, too, receive or can get the source code. And you must show them\nthese terms so they know their rights.\n\nWe protect your rights with two steps: (1) copyright the software, and (2) offer\nyou this license which gives you legal permission to copy, distribute and/or\nmodify the software.\n\nAlso, for each author's protection and ours, we want to make certain that\neveryone understands that there is no warranty for this free software. If the\nsoftware is modified by someone else and passed on, we want its recipients to\nknow that what they have is not the original, so that any problems introduced by\nothers will not reflect on the original authors' reputations.\n\nFinally, any free program is threatened constantly by software patents. We wish\nto avoid the danger that redistributors of a free program will individually\nobtain patent licenses, in effect making the program proprietary. To prevent\nthis, we have made it clear that any patent must be licensed for everyone's free\nuse or not licensed at all.\n\nThe precise terms and conditions for copying, distribution and modification\nfollow.\n\n\nTerms And Conditions For Copying, Distribution And Modification\n---------------------------------------------------------------\n\n**0.** This License applies to any program or other work which contains a notice\nplaced by the copyright holder saying it may be distributed under the terms of\nthis General Public License. The \"Program\", below, refers to any such program or\nwork, and a \"work based on the Program\" means either the Program or any\nderivative work under copyright law: that is to say, a work containing the\nProgram or a portion of it, either verbatim or with modifications and/or\ntranslated into another language. (Hereinafter, translation is included without\nlimitation in the term \"modification\".) Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not covered by\nthis License; they are outside its scope. The act of running the Program is not\nrestricted, and the output from the Program is covered only if its contents\nconstitute a work based on the Program (independent of having been made by\nrunning the Program). Whether that is true depends on what the Program does.\n\n**1.** You may copy and distribute verbatim copies of the Program's source code\nas you receive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice and\ndisclaimer of warranty; keep intact all the notices that refer to this License\nand to the absence of any warranty; and give any other recipients of the Program\na copy of this License along with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and you may at\nyour option offer warranty protection in exchange for a fee.\n\n**2.** You may modify your copy or copies of the Program or any portion of it,\nthus forming a work based on the Program, and copy and distribute such\nmodifications or work under the terms of Section 1 above, provided that you also\nmeet all of these conditions:\n\n*   **a)** You must cause the modified files to carry prominent notices stating\n    that you changed the files and the date of any change.\n\n*   **b)** You must cause any work that you distribute or publish, that in whole\n    or in part contains or is derived from the Program or any part thereof, to\n    be licensed as a whole at no charge to all third parties under the terms of\n    this License.\n\n*   **c)** If the modified program normally reads commands interactively when\n    run, you must cause it, when started running for such interactive use in the\n    most ordinary way, to print or display an announcement including an\n    appropriate copyright notice and a notice that there is no warranty (or\n    else, saying that you provide a warranty) and that users may redistribute\n    the program under these conditions, and telling the user how to view a copy\n    of this License. (Exception: if the Program itself is interactive but does\n    not normally print such an announcement, your work based on the Program is\n    not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole. If identifiable\nsections of that work are not derived from the Program, and can be reasonably\nconsidered independent and separate works in themselves, then this License, and\nits terms, do not apply to those sections when you distribute them as separate\nworks. But when you distribute the same sections as part of a whole which is a\nwork based on the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the entire whole,\nand thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest your\nrights to work written entirely by you; rather, the intent is to exercise the\nright to control the distribution of derivative or collective works based on the\nProgram.\n\nIn addition, mere aggregation of another work not based on the Program with the\nProgram (or with a work based on the Program) on a volume of a storage or\ndistribution medium does not bring the other work under the scope of this\nLicense.\n\n**3.** You may copy and distribute the Program (or a work based on it, under\nSection 2) in object code or executable form under the terms of Sections 1 and 2\nabove provided that you also do one of the following:\n\n*   **a)** Accompany it with the complete corresponding machine-readable source\n    code, which must be distributed under the terms of Sections 1 and 2 above on\n    a medium customarily used for software interchange; or,\n\n*   **b)** Accompany it with a written offer, valid for at least three years, to\n    give any third party, for a charge no more than your cost of physically\n    performing source distribution, a complete machine-readable copy of the\n    corresponding source code, to be distributed under the terms of Sections 1\n    and 2 above on a medium customarily used for software interchange; or,\n\n*   **c)** Accompany it with the information you received as to the offer to\n    distribute corresponding source code. (This alternative is allowed only for\n    noncommercial distribution and only if you received the program in object\n    code or executable form with such an offer, in accord with Subsection b\n    above.)\n\nThe source code for a work means the preferred form of the work for making\nmodifications to it. For an executable work, complete source code means all the\nsource code for all modules it contains, plus any associated interface\ndefinition files, plus the scripts used to control compilation and installation\nof the executable. However, as a special exception, the source code distributed\nneed not include anything that is normally distributed (in either source or\nbinary form) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component itself\naccompanies the executable.\n\nIf distribution of executable or object code is made by offering access to copy\nfrom a designated place, then offering equivalent access to copy the source code\nfrom the same place counts as distribution of the source code, even though third\nparties are not compelled to copy the source along with the object code.\n\n**4.** You may not copy, modify, sublicense, or distribute the Program except as\nexpressly provided under this License. Any attempt otherwise to copy, modify,\nsublicense or distribute the Program is void, and will automatically terminate\nyour rights under this License. However, parties who have received copies, or\nrights, from you under this License will not have their licenses terminated so\nlong as such parties remain in full compliance.\n\n**5.** You are not required to accept this License, since you have not signed\nit. However, nothing else grants you permission to modify or distribute the\nProgram or its derivative works. These actions are prohibited by law if you do\nnot accept this License. Therefore, by modifying or distributing the Program (or\nany work based on the Program), you indicate your acceptance of this License to\ndo so, and all its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n**6.** Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the original\nlicensor to copy, distribute or modify the Program subject to these terms and\nconditions. You may not impose any further restrictions on the recipients'\nexercise of the rights granted herein. You are not responsible for enforcing\ncompliance by third parties to this License.\n\n**7.** If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues), conditions\nare imposed on you (whether by court order, agreement or otherwise) that\ncontradict the conditions of this License, they do not excuse you from the\nconditions of this License. If you cannot distribute so as to satisfy\nsimultaneously your obligations under this License and any other pertinent\nobligations, then as a consequence you may not distribute the Program at all.\nFor example, if a patent license would not permit royalty-free redistribution of\nthe Program by all those who receive copies directly or indirectly through you,\nthen the only way you could satisfy both it and this License would be to refrain\nentirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under any\nparticular circumstance, the balance of the section is intended to apply and the\nsection as a whole is intended to apply in other circumstances.\n\nIt is not the purpose of this section to induce you to infringe any patents or\nother property right claims or to contest validity of any such claims; this\nsection has the sole purpose of protecting the integrity of the free software\ndistribution system, which is implemented by public license practices. Many\npeople have made generous contributions to the wide range of software\ndistributed through that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing to\ndistribute software through any other system and a licensee cannot impose that\nchoice.\n\nThis section is intended to make thoroughly clear what is believed to be a\nconsequence of the rest of this License.\n\n**8.** If the distribution and/or use of the Program is restricted in certain\ncountries either by patents or by copyrighted interfaces, the original copyright\nholder who places the Program under this License may add an explicit\ngeographical distribution limitation excluding those countries, so that\ndistribution is permitted only in or among countries not thus excluded. In such\ncase, this License incorporates the limitation as if written in the body of this\nLicense.\n\n**9.** The Free Software Foundation may publish revised and/or new versions of\nthe General Public License from time to time. Such new versions will be similar\nin spirit to the present version, but may differ in detail to address new\nproblems or concerns.\n\nEach version is given a distinguishing version number. If the Program specifies\na version number of this License which applies to it and \"any later version\",\nyou have the option of following the terms and conditions either of that version\nor of any later version published by the Free Software Foundation. If the\nProgram does not specify a version number of this License, you may choose any\nversion ever published by the Free Software Foundation.\n\n**10.** If you wish to incorporate parts of the Program into other free programs\nwhose distribution conditions are different, write to the author to ask for\npermission. For software which is copyrighted by the Free Software Foundation,\nwrite to the Free Software Foundation; we sometimes make exceptions for this.\nOur decision will be guided by the two goals of preserving the free status of\nall derivatives of our free software and of promoting the sharing and reuse of\nsoftware generally.\n\n\nNo Warranty\n-----------\n\n**11.** BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR\nTHE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE\nSTATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM\n\"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,\nBUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE\nPROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n**12.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR\nINABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA\nBEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A\nFAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER\nOR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n"
  },
  {
    "path": "license_GPLv3.md",
    "content": "The GNU General Public License, Version 3, 29 June 2007 (GPLv3)\n===============================================================\n\n> Copyright &copy; 2007\n> Free Software Foundation, Inc.\n> <<http://fsf.org/>>\n\nEveryone is permitted to copy and distribute verbatim copies of this license\ndocument, but changing it is not allowed.\n\n\nPreamble\n--------\n\nThe GNU General Public License is a free, copyleft license for software and\nother kinds of works.\n\nThe licenses for most software and other practical works are designed to take\naway your freedom to share and change the works. By contrast, the GNU General\nPublic License is intended to guarantee your freedom to share and change all\nversions of a program--to make sure it remains free software for all its users.\nWe, the Free Software Foundation, use the GNU General Public License for most of\nour software; it applies also to any other work released this way by its\nauthors. You can apply it to your programs, too.\n\nWhen we speak of free software, we are referring to freedom, not price. Our\nGeneral Public Licenses are designed to make sure that you have the freedom to\ndistribute copies of free software (and charge for them if you wish), that you\nreceive source code or can get it if you want it, that you can change the\nsoftware or use pieces of it in new free programs, and that you know you can do\nthese things.\n\nTo protect your rights, we need to prevent others from denying you these rights\nor asking you to surrender the rights. Therefore, you have certain\nresponsibilities if you distribute copies of the software, or if you modify it:\nresponsibilities to respect the freedom of others.\n\nFor example, if you distribute copies of such a program, whether gratis or for a\nfee, you must pass on to the recipients the same freedoms that you received. You\nmust make sure that they, too, receive or can get the source code. And you must\nshow them these terms so they know their rights.\n\nDevelopers that use the GNU GPL protect your rights with two steps: (1) assert\ncopyright on the software, and (2) offer you this License giving you legal\npermission to copy, distribute and/or modify it.\n\nFor the developers' and authors' protection, the GPL clearly explains that there\nis no warranty for this free software. For both users' and authors' sake, the\nGPL requires that modified versions be marked as changed, so that their problems\nwill not be attributed erroneously to authors of previous versions.\n\nSome devices are designed to deny users access to install or run modified\nversions of the software inside them, although the manufacturer can do so. This\nis fundamentally incompatible with the aim of protecting users' freedom to\nchange the software. The systematic pattern of such abuse occurs in the area of\nproducts for individuals to use, which is precisely where it is most\nunacceptable. Therefore, we have designed this version of the GPL to prohibit\nthe practice for those products. If such problems arise substantially in other\ndomains, we stand ready to extend this provision to those domains in future\nversions of the GPL, as needed to protect the freedom of users.\n\nFinally, every program is threatened constantly by software patents. States\nshould not allow patents to restrict development and use of software on\ngeneral-purpose computers, but in those that do, we wish to avoid the special\ndanger that patents applied to a free program could make it effectively\nproprietary. To prevent this, the GPL assures that patents cannot be used to\nrender the program non-free.\n\nThe precise terms and conditions for copying, distribution and modification\nfollow.\n\n\nTERMS AND CONDITIONS\n--------------------\n\n\n### 0. Definitions.\n\n\"This License refers to version 3 of the GNU General Public License.\n\n\"Copyright\" also means copyright-like laws that apply to other kinds of works,\nsuch as semiconductor masks.\n\n\"The Program\" refers to any copyrightable work licensed under this License. Each\nlicensee is addressed as \"you\". \"Licensees\" and \"recipients\" may be individuals\nor organizations.\n\nTo \"modify\" a work means to copy from or adapt all or part of the work in a\nfashion requiring copyright permission, other than the making of an exact copy.\nThe resulting work is called a \"modified version\" of the earlier work or a work\n\"based on\" the earlier work.\n\nA \"covered work\" means either the unmodified Program or a work based on the\nProgram.\n\nTo \"propagate\" a work means to do anything with it that, without permission,\nwould make you directly or secondarily liable for infringement under applicable\ncopyright law, except executing it on a computer or modifying a private copy.\nPropagation includes copying, distribution (with or without modification),\nmaking available to the public, and in some countries other activities as well.\n\nTo \"convey\" a work means any kind of propagation that enables other parties to\nmake or receive copies. Mere interaction with a user through a computer network,\nwith no transfer of a copy, is not conveying.\n\nAn interactive user interface displays \"Appropriate Legal Notices\" to the extent\nthat it includes a convenient and prominently visible feature that (1) displays\nan appropriate copyright notice, and (2) tells the user that there is no\nwarranty for the work (except to the extent that warranties are provided), that\nlicensees may convey the work under this License, and how to view a copy of this\nLicense. If the interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n\n### 1. Source Code.\n\nThe \"source code\" for a work means the preferred form of the work for making\nmodifications to it. \"Object code\" means any non-source form of a work.\n\nA \"Standard Interface\" means an interface that either is an official standard\ndefined by a recognized standards body, or, in the case of interfaces specified\nfor a particular programming language, one that is widely used among developers\nworking in that language.\n\nThe \"System Libraries\" of an executable work include anything, other than the\nwork as a whole, that (a) is included in the normal form of packaging a Major\nComponent, but which is not part of that Major Component, and (b) serves only to\nenable use of the work with that Major Component, or to implement a Standard\nInterface for which an implementation is available to the public in source code\nform. A \"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system (if any) on\nwhich the executable work runs, or a compiler used to produce the work, or an\nobject code interpreter used to run it.\n\nThe \"Corresponding Source\" for a work in object code form means all the source\ncode needed to generate, install, and (for an executable work) run the object\ncode and to modify the work, including scripts to control those activities.\nHowever, it does not include the work's System Libraries, or general-purpose\ntools or generally available free programs which are used unmodified in\nperforming those activities but which are not part of the work. For example,\nCorresponding Source includes interface definition files associated with source\nfiles for the work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require, such as by\nintimate data communication or control flow between those subprograms and other\nparts of the work.\n\nThe Corresponding Source need not include anything that users can regenerate\nautomatically from other parts of the Corresponding Source.\n\nThe Corresponding Source for a work in source code form is that same work.\n\n\n### 2. Basic Permissions.\n\nAll rights granted under this License are granted for the term of copyright on\nthe Program, and are irrevocable provided the stated conditions are met. This\nLicense explicitly affirms your unlimited permission to run the unmodified\nProgram. The output from running a covered work is covered by this License only\nif the output, given its content, constitutes a covered work. This License\nacknowledges your rights of fair use or other equivalent, as provided by\ncopyright law.\n\nYou may make, run and propagate covered works that you do not convey, without\nconditions so long as your license otherwise remains in force. You may convey\ncovered works to others for the sole purpose of having them make modifications\nexclusively for you, or provide you with facilities for running those works,\nprovided that you comply with the terms of this License in conveying all\nmaterial for which you do not control copyright. Those thus making or running\nthe covered works for you must do so exclusively on your behalf, under your\ndirection and control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\nConveying under any other circumstances is permitted solely under the conditions\nstated below. Sublicensing is not allowed; section 10 makes it unnecessary.\n\n\n### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\nNo covered work shall be deemed part of an effective technological measure under\nany applicable law fulfilling obligations under article 11 of the WIPO copyright\ntreaty adopted on 20 December 1996, or similar laws prohibiting or restricting\ncircumvention of such measures.\n\nWhen you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention is\neffected by exercising rights under this License with respect to the covered\nwork, and you disclaim any intention to limit operation or modification of the\nwork as a means of enforcing, against the work's users, your or third parties'\nlegal rights to forbid circumvention of technological measures.\n\n\n### 4. Conveying Verbatim Copies.\n\nYou may convey verbatim copies of the Program's source code as you receive it,\nin any medium, provided that you conspicuously and appropriately publish on each\ncopy an appropriate copyright notice; keep intact all notices stating that this\nLicense and any non-permissive terms added in accord with section 7 apply to the\ncode; keep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\nYou may charge any price or no price for each copy that you convey, and you may\noffer support or warranty protection for a fee.\n\n\n### 5. Conveying Modified Source Versions.\n\nYou may convey a work based on the Program, or the modifications to produce it\nfrom the Program, in the form of source code under the terms of section 4,\nprovided that you also meet all of these conditions:\n\n*   **a)** The work must carry prominent notices stating that you modified it,\n    and giving a relevant date.\n\n*   **b)** The work must carry prominent notices stating that it is released\n    under this License and any conditions added under section 7. This\n    requirement modifies the requirement in section 4 to \"keep intact all\n    notices\".\n\n*   **c)** You must license the entire work, as a whole, under this License to\n    anyone who comes into possession of a copy. This License will therefore\n    apply, along with any applicable section 7 additional terms, to the whole of\n    the work, and all its parts, regardless of how they are packaged. This\n    License gives no permission to license the work in any other way, but it\n    does not invalidate such permission if you have separately received it.\n\n*   **d)** If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your work need not\n    make them do so.\n\nA compilation of a covered work with other separate and independent works,\nwhich are not by their nature extensions of the covered work, and which are\nnot combined with it such as to form a larger program, in or on a volume of\na storage or distribution medium, is called an \"aggregate\" if the\ncompilation and its resulting copyright are not used to limit the access or\nlegal rights of the compilation's users beyond what the individual works\npermit. Inclusion of a covered work in an aggregate does not cause this\nLicense to apply to the other parts of the aggregate.\n\n\n### 6. Conveying Non-Source Forms.\n\nYou may convey a covered work in object code form under the terms of sections 4\nand 5, provided that you also convey the machine-readable Corresponding Source\nunder the terms of this License, in one of these ways:\n\n*   **a)** Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the Corresponding\n    Source fixed on a durable physical medium customarily used for software\n    interchange.\n\n*   **b)** Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a written offer,\n    valid for at least three years and valid for as long as you offer spare\n    parts or customer support for that product model, to give anyone who\n    possesses the object code either (1) a copy of the Corresponding Source for\n    all the software in the product that is covered by this License, on a\n    durable physical medium customarily used for software interchange, for a\n    price no more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the Corresponding Source from a\n    network server at no charge.\n\n*   **c)** Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source. This alternative is\n    allowed only occasionally and noncommercially, and only if you received the\n    object code with such an offer, in accord with subsection 6b.\n\n*   **d)** Convey the object code by offering access from a designated place\n    (gratis or for a charge), and offer equivalent access to the Corresponding\n    Source in the same way through the same place at no further charge. You need\n    not require recipients to copy the Corresponding Source along with the\n    object code. If the place to copy the object code is a network server, the\n    Corresponding Source may be on a different server (operated by you or a\n    third party) that supports equivalent copying facilities, provided you\n    maintain clear directions next to the object code saying where to find the\n    Corresponding Source. Regardless of what server hosts the Corresponding\n    Source, you remain obligated to ensure that it is available for as long as\n    needed to satisfy these requirements.\n\n*   **e)** Convey the object code using peer-to-peer transmission, provided you\n    inform other peers where the object code and Corresponding Source of the\n    work are being offered to the general public at no charge under subsection\n    6d.\n\nA separable portion of the object code, whose source code is excluded from\nthe Corresponding Source as a System Library, need not be included in\nconveying the object code work.\n\nA \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family, or\nhousehold purposes, or (2) anything designed or sold for incorporation into\na dwelling. In determining whether a product is a consumer product, doubtful\ncases shall be resolved in favor of coverage. For a particular product\nreceived by a particular user, \"normally used\" refers to a typical or common\nuse of that class of product, regardless of the status of the particular\nuser or of the way in which the particular user actually uses, or expects or\nis expected to use, the product. A product is a consumer product regardless\nof whether the product has substantial commercial, industrial or non-\nconsumer uses, unless such uses represent the only significant mode of use\nof the product.\n\n\"Installation Information\" for a User Product means any methods, procedures,\nauthorization keys, or other information required to install and execute\nmodified versions of a covered work in that User Product from a modified\nversion of its Corresponding Source. The information must suffice to ensure\nthat the continued functioning of the modified object code is in no case\nprevented or interfered with solely because modification has been made.\n\nIf you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as part of\na transaction in which the right of possession and use of the User Product\nis transferred to the recipient in perpetuity or for a fixed term\n(regardless of how the transaction is characterized), the Corresponding\nSource conveyed under this section must be accompanied by the Installation\nInformation. But this requirement does not apply if neither you nor any\nthird party retains the ability to install modified object code on the User\nProduct (for example, the work has been installed in ROM).\n\nThe requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates for\na work that has been modified or installed by the recipient, or for the User\nProduct in which it has been modified or installed. Access to a network may\nbe denied when the modification itself materially and adversely affects the\noperation of the network or violates the rules and protocols for\ncommunication across the network.\n\nCorresponding Source conveyed, and Installation Information provided, in\naccord with this section must be in a format that is publicly documented\n(and with an implementation available to the public in source code form),\nand must require no special password or key for unpacking, reading or\ncopying.\n\n\n### 7. Additional Terms.\n\n\"Additional permissions\" are terms that supplement the terms of this License by\nmaking exceptions from one or more of its conditions. Additional permissions\nthat are applicable to the entire Program shall be treated as though they were\nincluded in this License, to the extent that they are valid under applicable\nlaw. If additional permissions apply only to part of the Program, that part may\nbe used separately under those permissions, but the entire Program remains\ngoverned by this License without regard to the additional permissions.\n\nWhen you convey a copy of a covered work, you may at your option remove any\nadditional permissions from that copy, or from any part of it. (Additional\npermissions may be written to require their own removal in certain cases when\nyou modify the work.) You may place additional permissions on material, added by\nyou to a covered work, for which you have or can give appropriate copyright\npermission.\n\nNotwithstanding any other provision of this License, for material you add to a\ncovered work, you may (if authorized by the copyright holders of that material)\nsupplement the terms of this License with terms:\n\n*   **a)** Disclaiming warranty or limiting liability differently from the terms\n    of sections 15 and 16 of this License; or\n\n*   **b)** Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal Notices\n    displayed by works containing it; or\n\n*   **c)** Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in reasonable\n    ways as different from the original version; or\n\n*   **d)** Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n*   **e)** Declining to grant rights under trademark law for use of some trade\n    names, trademarks, or service marks; or\n\n*   **f)** Requiring indemnification of licensors and authors of that material\n    by anyone who conveys the material (or modified versions of it) with\n    contractual assumptions of liability to the recipient, for any liability\n    that these contractual assumptions directly impose on those licensors and\n    authors.\n\nAll other non-permissive additional terms are considered \"further restrictions\"\nwithin the meaning of section 10. If the Program as you received it, or any part\nof it, contains a notice stating that it is governed by this License along with\na term that is a further restriction, you may remove that term. If a license\ndocument contains a further restriction but permits relicensing or conveying\nunder this License, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does not survive\nsuch relicensing or conveying.\n\nIf you add terms to a covered work in accord with this section, you must place,\nin the relevant source files, a statement of the additional terms that apply to\nthose files, or a notice indicating where to find the applicable terms.\n\nAdditional terms, permissive or non-permissive, may be stated in the form of a\nseparately written license, or stated as exceptions; the above requirements\napply either way.\n\n\n### 8. Termination.\n\nYou may not propagate or modify a covered work except as expressly provided\nunder this License. Any attempt otherwise to propagate or modify it is void, and\nwill automatically terminate your rights under this License (including any\npatent licenses granted under the third paragraph of section 11).\n\nHowever, if you cease all violation of this License, then your license from a\nparticular copyright holder is reinstated (a) provisionally, unless and until\nthe copyright holder explicitly and finally terminates your license, and (b)\npermanently, if the copyright holder fails to notify you of the violation by\nsome reasonable means prior to 60 days after the cessation.\n\nMoreover, your license from a particular copyright holder is reinstated\npermanently if the copyright holder notifies you of the violation by some\nreasonable means, this is the first time you have received notice of violation\nof this License (for any work) from that copyright holder, and you cure the\nviolation prior to 30 days after your receipt of the notice.\n\nTermination of your rights under this section does not terminate the licenses of\nparties who have received copies or rights from you under this License. If your\nrights have been terminated and not permanently reinstated, you do not qualify\nto receive new licenses for the same material under section 10.\n\n\n### 9. Acceptance Not Required for Having Copies.\n\nYou are not required to accept this License in order to receive or run a copy of\nthe Program. Ancillary propagation of a covered work occurring solely as a\nconsequence of using peer-to-peer transmission to receive a copy likewise does\nnot require acceptance. However, nothing other than this License grants you\npermission to propagate or modify any covered work. These actions infringe\ncopyright if you do not accept this License. Therefore, by modifying or\npropagating a covered work, you indicate your acceptance of this License to do\nso.\n\n\n### 10. Automatic Licensing of Downstream Recipients.\n\nEach time you convey a covered work, the recipient automatically receives a\nlicense from the original licensors, to run, modify and propagate that work,\nsubject to this License. You are not responsible for enforcing compliance by\nthird parties with this License.\n\nAn \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations. If propagation of a covered work results\nfrom an entity transaction, each party to that transaction who receives a copy\nof the work also receives whatever licenses to the work the party's predecessor\nin interest had or could give under the previous paragraph, plus a right to\npossession of the Corresponding Source of the work from the predecessor in\ninterest, if the predecessor has it or can get it with reasonable efforts.\n\nYou may not impose any further restrictions on the exercise of the rights\ngranted or affirmed under this License. For example, you may not impose a\nlicense fee, royalty, or other charge for exercise of rights granted under this\nLicense, and you may not initiate litigation (including a cross-claim or\ncounterclaim in a lawsuit) alleging that any patent claim is infringed by\nmaking, using, selling, offering for sale, or importing the Program or any\nportion of it.\n\n\n### 11. Patents.\n\nA \"contributor\" is a copyright holder who authorizes use under this License of\nthe Program or a work on which the Program is based. The work thus licensed is\ncalled the contributor's \"contributor version\".\n\nA contributor's \"essential patent claims\" are all patent claims owned or\ncontrolled by the contributor, whether already acquired or hereafter acquired,\nthat would be infringed by some manner, permitted by this License, of making,\nusing, or selling its contributor version, but do not include claims that would\nbe infringed only as a consequence of further modification of the contributor\nversion. For purposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of this License.\n\nEach contributor grants you a non-exclusive, worldwide, royalty-free patent\nlicense under the contributor's essential patent claims, to make, use, sell,\noffer for sale, import and otherwise run, modify and propagate the contents of\nits contributor version.\n\nIn the following three paragraphs, a \"patent license\" is any express agreement\nor commitment, however denominated, not to enforce a patent (such as an express\npermission to practice a patent or covenant not to sue for patent infringement).\nTo \"grant\" such a patent license to a party means to make such an agreement or\ncommitment not to enforce a patent against the party.\n\nIf you convey a covered work, knowingly relying on a patent license, and the\nCorresponding Source of the work is not available for anyone to copy, free of\ncharge and under the terms of this License, through a publicly available network\nserver or other readily accessible means, then you must either (1) cause the\nCorresponding Source to be so available, or (2) arrange to deprive yourself of\nthe benefit of the patent license for this particular work, or (3) arrange, in a\nmanner consistent with the requirements of this License, to extend the patent\nlicense to downstream recipients. \"Knowingly relying\" means you have actual\nknowledge that, but for the patent license, your conveying the covered work in a\ncountry, or your recipient's use of the covered work in a country, would\ninfringe one or more identifiable patents in that country that you have reason\nto believe are valid.\n\nIf, pursuant to or in connection with a single transaction or arrangement, you\nconvey, or propagate by procuring conveyance of, a covered work, and grant a\npatent license to some of the parties receiving the covered work authorizing\nthem to use, propagate, modify or convey a specific copy of the covered work,\nthen the patent license you grant is automatically extended to all recipients of\nthe covered work and works based on it.\n\nA patent license is \"discriminatory\" if it does not include within the scope of\nits coverage, prohibits the exercise of, or is conditioned on the non- exercise\nof one or more of the rights that are specifically granted under this License.\nYou may not convey a covered work if you are a party to an arrangement with a\nthird party that is in the business of distributing software, under which you\nmake payment to the third party based on the extent of your activity of\nconveying the work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory patent\nlicense (a) in connection with copies of the covered work conveyed by you (or\ncopies made from those copies), or (b) primarily for and in connection with\nspecific products or compilations that contain the covered work, unless you\nentered into that arrangement, or that patent license was granted, prior to 28\nMarch 2007.\n\nNothing in this License shall be construed as excluding or limiting any implied\nlicense or other defenses to infringement that may otherwise be available to you\nunder applicable patent law.\n\n\n### 12. No Surrender of Others' Freedom.\n\nIf conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not excuse\nyou from the conditions of this License. If you cannot convey a covered work so\nas to satisfy simultaneously your obligations under this License and any other\npertinent obligations, then as a consequence you may not convey it at all. For\nexample, if you agree to terms that obligate you to collect a royalty for\nfurther conveying from those to whom you convey the Program, the only way you\ncould satisfy both those terms and this License would be to refrain entirely\nfrom conveying the Program.\n\n\n### 13. Use with the GNU Affero General Public License.\n\nNotwithstanding any other provision of this License, you have permission to link\nor combine any covered work with a work licensed under version 3 of the GNU\nAffero General Public License into a single combined work, and to convey the\nresulting work. The terms of this License will continue to apply to the part\nwhich is the covered work, but the special requirements of the GNU Affero\nGeneral Public License, section 13, concerning interaction through a network\nwill apply to the combination as such.\n\n\n### 14. Revised Versions of this License.\n\nThe Free Software Foundation may publish revised and/or new versions of the GNU\nGeneral Public License from time to time. Such new versions will be similar in\nspirit to the present version, but may differ in detail to address new problems\nor concerns.\n\nEach version is given a distinguishing version number. If the Program specifies\nthat a certain numbered version of the GNU General Public License \"or any later\nversion\" applies to it, you have the option of following the terms and\nconditions either of that numbered version or of any later version published by\nthe Free Software Foundation. If the Program does not specify a version number\nof the GNU General Public License, you may choose any version ever published by\nthe Free Software Foundation.\n\nIf the Program specifies that a proxy can decide which future versions of the\nGNU General Public License can be used, that proxy's public statement of\nacceptance of a version permanently authorizes you to choose that version for\nthe Program.\n\nLater license versions may give you additional or different permissions.\nHowever, no additional obligations are imposed on any author or copyright holder\nas a result of your choosing to follow a later version.\n\n\n### 15. Disclaimer of Warranty.\n\nTHERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.\nEXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER\nPARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER\nEXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE\nQUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE\nDEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n\n### 16. Limitation of Liability.\n\nIN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY\nCOPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS\nPERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,\nINCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE\nTHE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED\nINACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE\nPROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY\nHAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n### 17. Interpretation of Sections 15 and 16.\n\nIf the disclaimer of warranty and limitation of liability provided above cannot\nbe given local legal effect according to their terms, reviewing courts shall\napply local law that most closely approximates an absolute waiver of all civil\nliability in connection with the Program, unless a warranty or assumption of\nliability accompanies a copy of the Program in return for a fee.\n\nEND OF TERMS AND CONDITIONS\n\n\nHow to Apply These Terms to Your New Programs\n---------------------------------------------\n\nIf you develop a new program, and you want it to be of the greatest possible use\nto the public, the best way to achieve this is to make it free software which\neveryone can redistribute and change under these terms.\n\nTo do so, attach the following notices to the program. It is safest to attach\nthem to the start of each source file to most effectively state the exclusion of\nwarranty; and each file should have at least the \"copyright\" line and a pointer\nto where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify it\n    under the terms of the GNU General Public License as published by the Free\n    Software Foundation, either version 3 of the License, or (at your option)\n    any later version.\n\n    This program is distributed in the hope that it will be useful, but WITHOUT\n    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\n    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for\n    more details.\n\n    You should have received a copy of the GNU General Public License along with\n    this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program does terminal interaction, make it output a short notice like\nthis when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.\n    This is free software, and you are welcome to redistribute it under certain\n    conditions; type 'show c' for details.\n\nThe hypothetical commands 'show w' and 'show c' should show the appropriate\nparts of the General Public License. Of course, your program's commands might be\ndifferent; for a GUI interface, you would use an \"about box\".\n\nYou should also get your employer (if you work as a programmer) or school, if\nany, to sign a \"copyright disclaimer\" for the program, if necessary. For more\ninformation on this, and how to apply and follow the GNU GPL, see\n<<http://www.gnu.org/licenses/>>.\n\nThe GNU General Public License does not permit incorporating your program into\nproprietary programs. If your program is a subroutine library, you may consider\nit more useful to permit linking proprietary applications with the library. If\nthis is what you want to do, use the GNU Lesser General Public License instead\nof this License. But first, please read\n<<http://www.gnu.org/philosophy/why-not-lgpl.html>>.\n"
  },
  {
    "path": "license_Modified_BSD.md",
    "content": "This software is licensed with a Modified BSD License.\n\nAll of this is supposed to be Free Software, Open Source, DFSG-free,\nGPL-compatible, and OK to use in both free and proprietary applications.\nAdditions and corrections to this file are welcome.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright\n  notice, this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright\n  notice, this list of conditions and the following disclaimer in\n  the documentation and/or other materials provided with the\n  distribution.\n\n* Neither the name of the copyright holders nor the names of\n  contributors may be used to endorse or promote products derived\n  from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "modules/qmk/flow_led_matrix_effect/led_matrix_module.inc",
    "content": "// Copyright 2024-2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nLED_MATRIX_EFFECT(FLOW)\n\n#ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\n// \"Flow\" animated effect. Draws moving wave patterns mimicking the appearance\n// of flowing liquid. For interesting variety of patterns, space coordinates are\n// slowly rotated and a function of several sine waves is evaluated.\nstatic bool FLOW(effect_params_t* params) {\n    LED_MATRIX_USE_LIMITS(led_min, led_max);\n\n    static uint16_t wrap_correction = 0;\n    static uint8_t  last_high_byte  = 0;\n    const uint8_t   time_scale      = 1 + led_matrix_eeconfig.speed / 8;\n    const uint8_t   high_byte       = (uint8_t)(g_led_timer >> 16);\n    if (last_high_byte != high_byte) {\n        last_high_byte = high_byte;\n        wrap_correction += ((uint16_t)time_scale) << 8;\n    }\n    const uint16_t time = scale16by8(g_led_timer, time_scale) + wrap_correction;\n\n    // Compute rotation coefficients with 7 fractional bits.\n    const int8_t  rot_c = cos8(time / 4) - 128;\n    const int8_t  rot_s = sin8(time / 4) - 128;\n    const uint8_t omega = 32 + sin8(time) / 4;\n\n    for (uint8_t i = led_min; i < led_max; ++i) {\n        LED_MATRIX_TEST_LED_FLAGS();\n        const uint8_t x = g_led_config.point[i].x;\n        const uint8_t y = g_led_config.point[i].y;\n\n        // Rotate (x, y) by the 2x2 rotation matrix described by rot_c, rot_s.\n        const uint8_t x1 = (uint8_t)((((int16_t)rot_c) * ((int16_t)x)) / 128) - (uint8_t)((((int16_t)rot_s) * ((int16_t)y)) / 128);\n        const uint8_t y1 = (uint8_t)((((int16_t)rot_s) * ((int16_t)x)) / 128) + (uint8_t)((((int16_t)rot_c) * ((int16_t)y)) / 128);\n\n        uint8_t value = scale8(sin8(x1 - 2 * time), omega) + y1 + time / 4;\n        value         = (value <= 127) ? value : (255 - value);\n\n        led_matrix_set_value(i, scale8(led_matrix_eeconfig.val, value));\n    }\n\n    return led_matrix_check_finished_leds(led_max);\n}\n\n#endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n"
  },
  {
    "path": "modules/qmk/flow_led_matrix_effect/qmk_module.json",
    "content": "{\n    \"module_name\": \"Flow LED matrix effect\",\n    \"maintainer\": \"QMK Maintainers\",\n    \"license\": \"Apache-2.0\",\n    \"features\": {\n        \"led_matrix\": true\n    }\n}\n"
  },
  {
    "path": "modules/qmk/flow_rgb_matrix_effect/qmk_module.json",
    "content": "{\n    \"module_name\": \"Flow RGB matrix effect\",\n    \"maintainer\": \"QMK Maintainers\",\n    \"license\": \"Apache-2.0\",\n    \"features\": {\n        \"rgb_matrix\": true\n    }\n}\n"
  },
  {
    "path": "modules/qmk/flow_rgb_matrix_effect/rgb_matrix_module.inc",
    "content": "// Copyright 2024-2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nRGB_MATRIX_EFFECT(FLOW)\n\n#ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\n// \"Flow\" animated effect. Draws moving wave patterns mimicking the appearance\n// of flowing liquid. For interesting variety of patterns, space coordinates are\n// slowly rotated and a function of several sine waves is evaluated.\nstatic bool FLOW(effect_params_t* params) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    static uint16_t wrap_correction = 0;\n    static uint8_t  last_high_byte  = 0;\n    const uint8_t   time_scale      = 1 + rgb_matrix_config.speed / 8;\n    const uint8_t   high_byte       = (uint8_t)(g_rgb_timer >> 16);\n    if (last_high_byte != high_byte) {\n        last_high_byte = high_byte;\n        wrap_correction += ((uint16_t)time_scale) << 8;\n    }\n    const uint16_t time = scale16by8(g_rgb_timer, time_scale) + wrap_correction;\n\n    // Compute rotation coefficients with 7 fractional bits.\n    const int8_t  rot_c = cos8(time / 4) - 128;\n    const int8_t  rot_s = sin8(time / 4) - 128;\n    const uint8_t omega = 32 + sin8(time) / 4;\n\n    for (uint8_t i = led_min; i < led_max; ++i) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        const uint8_t x = g_led_config.point[i].x;\n        const uint8_t y = g_led_config.point[i].y;\n\n        // Rotate (x, y) by the 2x2 rotation matrix described by rot_c, rot_s.\n        const uint8_t x1 = (uint8_t)((((int16_t)rot_c) * ((int16_t)x)) / 128) - (uint8_t)((((int16_t)rot_s) * ((int16_t)y)) / 128);\n        const uint8_t y1 = (uint8_t)((((int16_t)rot_s) * ((int16_t)x)) / 128) + (uint8_t)((((int16_t)rot_c) * ((int16_t)y)) / 128);\n\n        uint8_t value = scale8(sin8(x1 - 2 * time), omega) + y1 + time / 4;\n        value         = (value <= 127) ? value : (255 - value);\n\n        hsv_t hsv = rgb_matrix_config.hsv;\n        hsv.h -= value / 4;\n        hsv.s     = scale8(hsv.s, (value < 74) ? 255 : (549 - 4 * value));\n        hsv.v     = scale8(hsv.v, (value < 95) ? (64 + 2 * value) : 255);\n\n        rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);\n        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n    }\n\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n"
  },
  {
    "path": "modules/qmk/hello_world/hello_world.c",
    "content": "// Copyright 2025 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include QMK_KEYBOARD_H\n\n#include \"introspection.h\"\n\nASSERT_COMMUNITY_MODULES_MIN_API_VERSION(1, 0, 0);\n\nuint32_t delayed_hello_world(uint32_t trigger_time, void *cb_arg) {\n    dprintf(\"Hello, world! I'm a QMK based keyboard! The keymap array size is %d bytes.\\n\", (int)hello_world_introspection().total_size);\n    return 0;\n}\n\nvoid keyboard_post_init_hello_world(void) {\n    keyboard_post_init_hello_world_kb();\n    defer_exec(10000, delayed_hello_world, NULL);\n}\n\nbool process_record_hello_world(uint16_t keycode, keyrecord_t *record) {\n    if (!process_record_hello_world_kb(keycode, record)) {\n        return false;\n    }\n\n    switch (keycode) {\n        case COMMUNITY_MODULE_HELLO:\n            if (record->event.pressed) {\n                SEND_STRING(\"Hello there.\");\n                break;\n            }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "modules/qmk/hello_world/introspection.c",
    "content": "// Copyright 2025 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\nhello_world_introspection_t hello_world_introspection(void) {\n    hello_world_introspection_t introspection = {\n        .total_size  = sizeof(keymaps),\n        .layer_count = sizeof(keymaps) / sizeof(keymaps[0]),\n    };\n    return introspection;\n}\n"
  },
  {
    "path": "modules/qmk/hello_world/introspection.h",
    "content": "// Copyright 2025 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include QMK_KEYBOARD_H\n\ntypedef struct hello_world_introspection_t {\n    int16_t total_size;\n    int16_t layer_count;\n} hello_world_introspection_t;\n\nhello_world_introspection_t hello_world_introspection(void);\n"
  },
  {
    "path": "modules/qmk/hello_world/qmk_module.json",
    "content": "{\n    \"module_name\": \"Hello World\",\n    \"maintainer\": \"QMK Maintainers\",\n    \"license\": \"GPL-2.0-or-later\",\n    \"features\": {\n        \"console\": true,\n        \"deferred_exec\": true\n    },\n    \"keycodes\": [\n        {\n            \"key\": \"COMMUNITY_MODULE_HELLO\",\n            \"aliases\": [\"CM_HELO\"]\n        }\n    ]\n}\n"
  },
  {
    "path": "modules/qmk/hello_world/rules.mk",
    "content": "# Just a simple rules.mk which tests that they work from a community module.\n$(shell $(QMK_BIN) hello -n \"from QMK's hello world community module\")\n"
  },
  {
    "path": "modules/qmk/super_alt_tab/qmk_module.json",
    "content": "{\n    \"module_name\": \"Super Alt Tab\",\n    \"maintainer\": \"QMK Maintainers\",\n    \"license\": \"GPL-2.0-or-later\",\n    \"keycodes\": [\n        {\n            \"key\": \"COMMUNITY_MODULE_SUPER_ALT_TAB\",\n            \"aliases\": [\"CM_S_AT\"]\n        }\n    ]\n}\n"
  },
  {
    "path": "modules/qmk/super_alt_tab/super_alt_tab.c",
    "content": "// Copyright 2025 Christopher Courtney, aka Drashna Jael're  (@drashna)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include QMK_KEYBOARD_H\n\nASSERT_COMMUNITY_MODULES_MIN_API_VERSION(1, 0, 0);\n\nstatic bool     is_alt_tab_active = false;\nstatic uint16_t alt_tab_timer     = 0;\n\n#ifndef COMMUNITY_MODULE_SUPER_ALT_TAB_TIMEOUT\n#    define COMMUNITY_MODULE_SUPER_ALT_TAB_TIMEOUT 1000\n#endif // COMMUNITY_MODULE_SUPER_ALT_TAB_TIMEOUT\n#ifndef COMMUNITY_MODULE_SUPER_ALT_TAB_MODIFIER\n#    define COMMUNITY_MODULE_SUPER_ALT_TAB_MODIFIER MOD_LALT\n#endif // COMMUNITY_MODULE_SUPER_ALT_TAB_MODIFIER\n#ifndef COMMUNITY_MODULE_SUPER_ALT_TAB_KEY\n#    define COMMUNITY_MODULE_SUPER_ALT_TAB_KEY KC_TAB\n#endif // COMMUNITY_MODULE_SUPER_ALT_TAB_KEY\n\nbool process_record_super_alt_tab(uint16_t keycode, keyrecord_t *record) {\n    if (!process_record_super_alt_tab_kb(keycode, record)) {\n        return false;\n    }\n\n    switch (keycode) { // This will do most of the grunt work with the keycodes.\n        case COMMUNITY_MODULE_SUPER_ALT_TAB:\n            if (record->event.pressed) {\n                if (!is_alt_tab_active) {\n                    is_alt_tab_active = true;\n                    register_mods(COMMUNITY_MODULE_SUPER_ALT_TAB_MODIFIER);\n                }\n                alt_tab_timer = timer_read();\n                register_code(COMMUNITY_MODULE_SUPER_ALT_TAB_KEY);\n            } else {\n                unregister_code(COMMUNITY_MODULE_SUPER_ALT_TAB_KEY);\n            }\n            break;\n    }\n    return true;\n}\n\nvoid housekeeping_task_super_alt_tab(void) {\n    if (is_alt_tab_active) {\n        if (timer_elapsed(alt_tab_timer) > COMMUNITY_MODULE_SUPER_ALT_TAB_TIMEOUT) {\n            unregister_mods(COMMUNITY_MODULE_SUPER_ALT_TAB_MODIFIER);\n            is_alt_tab_active = false;\n        }\n    }\n}\n"
  },
  {
    "path": "nose2.cfg",
    "content": "[unittest]\nstart-dir = lib/python/qmk/tests\n"
  },
  {
    "path": "paths.mk",
    "content": "# Directory common source files exist\nTOP_DIR = .\nTMK_DIR = tmk_core\nTMK_PATH = $(TMK_DIR)\n\nLIB_DIR = lib\nLIB_PATH = $(LIB_DIR)\n\nQUANTUM_DIR = quantum\nQUANTUM_PATH = $(QUANTUM_DIR)\n\nDRIVER_DIR = drivers\nDRIVER_PATH = $(DRIVER_DIR)\n\nPLATFORM_DIR = platforms\nPLATFORM_PATH = $(PLATFORM_DIR)\n\nPROTOCOL_DIR = protocol\nPROTOCOL_PATH = $(TMK_DIR)/$(PROTOCOL_DIR)\n\nBUILDDEFS_DIR = builddefs\nBUILDDEFS_PATH = $(BUILDDEFS_DIR)\n\nBUILD_DIR := .build\n\nCOMMON_VPATH := $(TOP_DIR)\nCOMMON_VPATH += $(TMK_PATH)\nCOMMON_VPATH += $(QUANTUM_PATH)\nCOMMON_VPATH += $(QUANTUM_PATH)/keymap_extras\nCOMMON_VPATH += $(QUANTUM_PATH)/process_keycode\nCOMMON_VPATH += $(QUANTUM_PATH)/sequencer\nCOMMON_VPATH += $(DRIVER_PATH)\n"
  },
  {
    "path": "platforms/atomic_util.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include \"compiler_support.h\"\n\n// Macro to help make GPIO and other controls atomic.\n\n#ifndef IGNORE_ATOMIC_BLOCK\n#    if __has_include_next(\"atomic_util.h\")\n#        include_next \"atomic_util.h\" /* Include the platforms atomic.h */\n#    else\n#        define ATOMIC_BLOCK STATIC_ASSERT(0, \"ATOMIC_BLOCK not implemented\")\n#        define ATOMIC_BLOCK_RESTORESTATE STATIC_ASSERT(0, \"ATOMIC_BLOCK_RESTORESTATE not implemented\")\n#        define ATOMIC_BLOCK_FORCEON STATIC_ASSERT(0, \"ATOMIC_BLOCK_FORCEON not implemented\")\n#        define ATOMIC_FORCEON STATIC_ASSERT(0, \"ATOMIC_FORCEON not implemented\")\n#        define ATOMIC_RESTORESTATE STATIC_ASSERT(0, \"ATOMIC_RESTORESTATE not implemented\")\n#    endif\n#else /* do nothing atomic macro */\n#    define ATOMIC_BLOCK(t) for (uint8_t __ToDo = 1; __ToDo; __ToDo = 0)\n#    define ATOMIC_FORCEON\n#    define ATOMIC_RESTORESTATE\n#    define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK(ATOMIC_RESTORESTATE)\n#    define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON)\n#endif\n"
  },
  {
    "path": "platforms/avr/_pin_defs.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <avr/io.h>\n\n#define PORT_SHIFTER 4 // this may be 4 for all AVR chips\n\n// If you want to add more to this list, reference the PINx definitions in these header\n// files: https://github.com/vancegroup-mirrors/avr-libc/tree/master/avr-libc/include/avr\n\n#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)\n#    define ADDRESS_BASE 0x00\n#    define PINB_ADDRESS 0x3\n#    define PINC_ADDRESS 0x6\n#    define PIND_ADDRESS 0x9\n#    define PINE_ADDRESS 0xC\n#    define PINF_ADDRESS 0xF\n#elif defined(__AVR_AT90USB162__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)\n#    define ADDRESS_BASE 0x00\n#    define PINB_ADDRESS 0x3\n#    define PINC_ADDRESS 0x6\n#    define PIND_ADDRESS 0x9\n#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)\n#    define ADDRESS_BASE 0x00\n#    define PINA_ADDRESS 0x0\n#    define PINB_ADDRESS 0x3\n#    define PINC_ADDRESS 0x6\n#    define PIND_ADDRESS 0x9\n#    define PINE_ADDRESS 0xC\n#    define PINF_ADDRESS 0xF\n#elif defined(__AVR_ATmega32A__)\n#    define ADDRESS_BASE 0x10\n#    define PIND_ADDRESS 0x0\n#    define PINC_ADDRESS 0x3\n#    define PINB_ADDRESS 0x6\n#    define PINA_ADDRESS 0x9\n#elif defined(__AVR_ATtiny85__)\n#    define ADDRESS_BASE 0x10\n#    define PINB_ADDRESS 0x6\n#else\n#    error \"Pins are not defined\"\n#endif\n\n#define PINDEF(port, pin) ((PIN##port##_ADDRESS << PORT_SHIFTER) | pin)\n\n#define _PIN_ADDRESS(p, offset) _SFR_IO8(ADDRESS_BASE + ((p) >> PORT_SHIFTER) + (offset))\n// Port X Input Pins Address\n#define PINx_ADDRESS(p) _PIN_ADDRESS(p, 0)\n// Port X Data Direction Register,  0:input 1:output\n#define DDRx_ADDRESS(p) _PIN_ADDRESS(p, 1)\n// Port X Data Register\n#define PORTx_ADDRESS(p) _PIN_ADDRESS(p, 2)\n\n/* I/O pins */\n#ifdef PORTA\n#    define A0 PINDEF(A, 0)\n#    define A1 PINDEF(A, 1)\n#    define A2 PINDEF(A, 2)\n#    define A3 PINDEF(A, 3)\n#    define A4 PINDEF(A, 4)\n#    define A5 PINDEF(A, 5)\n#    define A6 PINDEF(A, 6)\n#    define A7 PINDEF(A, 7)\n#endif\n#ifdef PORTB\n#    define B0 PINDEF(B, 0)\n#    define B1 PINDEF(B, 1)\n#    define B2 PINDEF(B, 2)\n#    define B3 PINDEF(B, 3)\n#    define B4 PINDEF(B, 4)\n#    define B5 PINDEF(B, 5)\n#    define B6 PINDEF(B, 6)\n#    define B7 PINDEF(B, 7)\n#endif\n#ifdef PORTC\n#    define C0 PINDEF(C, 0)\n#    define C1 PINDEF(C, 1)\n#    define C2 PINDEF(C, 2)\n#    define C3 PINDEF(C, 3)\n#    define C4 PINDEF(C, 4)\n#    define C5 PINDEF(C, 5)\n#    define C6 PINDEF(C, 6)\n#    define C7 PINDEF(C, 7)\n#endif\n#ifdef PORTD\n#    define D0 PINDEF(D, 0)\n#    define D1 PINDEF(D, 1)\n#    define D2 PINDEF(D, 2)\n#    define D3 PINDEF(D, 3)\n#    define D4 PINDEF(D, 4)\n#    define D5 PINDEF(D, 5)\n#    define D6 PINDEF(D, 6)\n#    define D7 PINDEF(D, 7)\n#endif\n#ifdef PORTE\n#    define E0 PINDEF(E, 0)\n#    define E1 PINDEF(E, 1)\n#    define E2 PINDEF(E, 2)\n#    define E3 PINDEF(E, 3)\n#    define E4 PINDEF(E, 4)\n#    define E5 PINDEF(E, 5)\n#    define E6 PINDEF(E, 6)\n#    define E7 PINDEF(E, 7)\n#endif\n#ifdef PORTF\n#    define F0 PINDEF(F, 0)\n#    define F1 PINDEF(F, 1)\n#    define F2 PINDEF(F, 2)\n#    define F3 PINDEF(F, 3)\n#    define F4 PINDEF(F, 4)\n#    define F5 PINDEF(F, 5)\n#    define F6 PINDEF(F, 6)\n#    define F7 PINDEF(F, 7)\n#endif\n"
  },
  {
    "path": "platforms/avr/_print.h",
    "content": "/* Copyright 2012 Jun Wako <wakojun@gmail.com> */\n/* Very basic print functions, intended to be used with usb_debug_only.c\n * http://www.pjrc.com/teensy/\n * Copyright (c) 2008 PJRC.COM, LLC\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n#pragma once\n\n#include \"avr/xprintf.h\"\n"
  },
  {
    "path": "platforms/avr/_timer.h",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n// The platform is 8-bit, so prefer 16-bit timers to reduce code size\n#define FAST_TIMER_T_SIZE 16\n"
  },
  {
    "path": "platforms/avr/_util.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n// AVR can't actually run anything from RAM, so just no-op the define.\n#define RESIDENT_IN_RAM(funcname) funcname\n\n#if __has_include_next(\"_util.h\")\n#    include_next \"_util.h\"\n#endif\n"
  },
  {
    "path": "platforms/avr/_wait.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n// Need to disable GCC's \"maybe-uninitialized\" warning for this file, as it causes issues when running `KEEP_INTERMEDIATES=yes`.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wmaybe-uninitialized\"\n#include <util/delay.h>\n#pragma GCC diagnostic pop\n\n// http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf\n// page 22: Table 4-2. Arithmetic and Logic Instructions\n/*\n    for (uint16_t i = times; i > 0; i--) {\n        __builtin_avr_delay_cycles(1);\n    }\n\n    .L3:  sbiw r24,0  // loop step 1\n          brne .L4    // loop step 2\n          ret\n    .L4:  nop         // __builtin_avr_delay_cycles(1);\n          sbiw r24,1  // loop step 3\n          rjmp .L3    // loop step 4\n*/\n\n#define AVR_sbiw_clocks 2\n#define AVR_rjmp_clocks 2\n#define AVR_brne_clocks 2\n#define AVR_WAIT_LOOP_OVERHEAD (AVR_sbiw_clocks + AVR_brne_clocks + AVR_sbiw_clocks + AVR_rjmp_clocks)\n\n#define wait_ms(ms)                             \\\n    do {                                        \\\n        if (__builtin_constant_p(ms)) {         \\\n            _delay_ms(ms);                      \\\n        } else {                                \\\n            for (uint16_t i = ms; i > 0; i--) { \\\n                _delay_ms(1);                   \\\n            }                                   \\\n        }                                       \\\n    } while (0)\n#define wait_us(us)                                                                     \\\n    do {                                                                                \\\n        if (__builtin_constant_p(us)) {                                                 \\\n            _delay_us(us);                                                              \\\n        } else {                                                                        \\\n            for (uint16_t i = us; i > 0; i--) {                                         \\\n                __builtin_avr_delay_cycles((F_CPU / 1000000) - AVR_WAIT_LOOP_OVERHEAD); \\\n            }                                                                           \\\n        }                                                                               \\\n    } while (0)\n#define wait_cpuclock(n) __builtin_avr_delay_cycles(n)\n#define CPU_CLOCK F_CPU\n\n/* The AVR series GPIOs have a one clock read delay for changes in the digital input signal.\n * But here's more margin to make it two clocks. */\n#ifndef GPIO_INPUT_PIN_DELAY\n#    define GPIO_INPUT_PIN_DELAY 2\n#endif\n\n#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)\n"
  },
  {
    "path": "platforms/avr/atomic_util.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n/* atomic macro for AVR */\n#include <util/atomic.h>\n\n#define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK(ATOMIC_RESTORESTATE)\n#define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON)\n"
  },
  {
    "path": "platforms/avr/bootloader.mk",
    "content": "# Copyright 2017 Jack Humbert\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# If it's possible that multiple bootloaders can be used for one project,\n# you can leave this unset, and the correct size will be selected\n# automatically.\n#\n# Sets the bootloader defined in the keyboard's/keymap's rules.mk\n#\n# Current options for AVR:\n#     halfkay      PJRC Teensy\n#     caterina     Pro Micro (Sparkfun/generic)\n#     atmel-dfu    Atmel factory DFU\n#     lufa-dfu     LUFA DFU\n#     qmk-dfu      QMK DFU (LUFA + blinkenlight)\n#     qmk-hid      QMK HID (LUFA + blinkenlight)\n#     bootloadhid  HIDBootFlash compatible (ATmega32A)\n#     usbasploader USBaspLoader (ATmega328P)\n#\n# If you need to provide your own implementation, you can set inside `rules.mk`\n# `BOOTLOADER = custom` -- you'll need to provide your own implementations. See\n# the respective file under `platforms/<PLATFORM>/bootloaders/custom.c` to see\n# which functions may be overridden.\n#\n# BOOTLOADER_SIZE can still be defined manually, but it's recommended\n# you add any possible configuration to this list\n\nFIRMWARE_FORMAT?=hex\n\nifeq ($(strip $(BOOTLOADER)), custom)\n    OPT_DEFS += -DBOOTLOADER_CUSTOM\n    BOOTLOADER_TYPE = custom\nendif\n\nifeq ($(strip $(BOOTLOADER)), atmel-dfu)\n    OPT_DEFS += -DBOOTLOADER_ATMEL_DFU\n    OPT_DEFS += -DBOOTLOADER_DFU\n    BOOTLOADER_TYPE = dfu\n\n    ifneq (,$(filter $(MCU), at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647))\n        BOOTLOADER_SIZE = 4096\n    endif\n    ifneq (,$(filter $(MCU), at90usb1286 at90usb1287))\n        BOOTLOADER_SIZE = 8192\n    endif\nendif\nifeq ($(strip $(BOOTLOADER)), lufa-dfu)\n    OPT_DEFS += -DBOOTLOADER_LUFA_DFU\n    OPT_DEFS += -DBOOTLOADER_DFU\n    BOOTLOADER_TYPE = dfu\n\n    ifneq (,$(filter $(MCU), at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647))\n        BOOTLOADER_SIZE ?= 4096\n    endif\n    ifneq (,$(filter $(MCU), at90usb1286 at90usb1287))\n        BOOTLOADER_SIZE ?= 8192\n    endif\nendif\nifeq ($(strip $(BOOTLOADER)), qmk-dfu)\n    OPT_DEFS += -DBOOTLOADER_QMK_DFU\n    OPT_DEFS += -DBOOTLOADER_DFU\n    BOOTLOADER_TYPE = dfu\n\n    ifneq (,$(filter $(MCU), at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647))\n        BOOTLOADER_SIZE ?= 4096\n    endif\n    ifneq (,$(filter $(MCU), at90usb1286 at90usb1287))\n        BOOTLOADER_SIZE ?= 8192\n    endif\nendif\nifeq ($(strip $(BOOTLOADER)), qmk-hid)\n    OPT_DEFS += -DBOOTLOADER_QMK_HID\n    OPT_DEFS += -DBOOTLOADER_HID\n    BOOTLOADER_TYPE = dfu\n\n    BOOTLOADER_SIZE ?= 4096\nendif\nifeq ($(strip $(BOOTLOADER)), halfkay)\n    OPT_DEFS += -DBOOTLOADER_HALFKAY\n    BOOTLOADER_TYPE = halfkay\n\n    # Teensy 2.0\n    ifeq ($(strip $(MCU)), atmega32u4)\n        BOOTLOADER_SIZE = 512\n    endif\n    # Teensy 2.0++\n    ifeq ($(strip $(MCU)), at90usb1286)\n        BOOTLOADER_SIZE = 1024\n    endif\nendif\nifeq ($(strip $(BOOTLOADER)), caterina)\n    OPT_DEFS += -DBOOTLOADER_CATERINA\n    BOOTLOADER_TYPE = caterina\n\n    BOOTLOADER_SIZE = 4096\nendif\nifeq ($(strip $(BOOTLOADER)), bootloadhid)\n    OPT_DEFS += -DBOOTLOADER_BOOTLOADHID\n    BOOTLOADER_TYPE = bootloadhid\n\n    BOOTLOADER_SIZE = 4096\nendif\nifeq ($(strip $(BOOTLOADER)), usbasploader)\n    OPT_DEFS += -DBOOTLOADER_USBASP\n    BOOTLOADER_TYPE = usbasploader\n\n    BOOTLOADER_SIZE = 4096\nendif\nifeq ($(strip $(BOOTLOADER)), lufa-ms)\n    OPT_DEFS += -DBOOTLOADER_MS\n    BOOTLOADER_TYPE = dfu\n\n    BOOTLOADER_SIZE ?= 8192\n    FIRMWARE_FORMAT = bin\ncpfirmware: lufa_warning\n.INTERMEDIATE: lufa_warning\nlufa_warning: $(FIRMWARE_FORMAT)\n\t$(info @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@)\n\t$(info LUFA MASS STORAGE Bootloader selected)\n\t$(info DO NOT USE THIS BOOTLOADER IN NEW PROJECTS!)\n\t$(info It is extremely prone to bricking, and is only included to support existing boards.)\n\t$(info @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@)\nendif\nifdef BOOTLOADER_SIZE\n    OPT_DEFS += -DBOOTLOADER_SIZE=$(strip $(BOOTLOADER_SIZE))\nendif\n\nifeq ($(strip $(BOOTLOADER_TYPE)),)\n    ifneq ($(strip $(BOOTLOADER)),)\n        $(call CATASTROPHIC_ERROR,Invalid BOOTLOADER,Invalid bootloader specified. Please set an appropriate bootloader in your rules.mk or info.json.)\n    else\n        $(call CATASTROPHIC_ERROR,Invalid BOOTLOADER,No bootloader specified. Please set an appropriate bootloader in your rules.mk or info.json.)\n    endif\nendif\n"
  },
  {
    "path": "platforms/avr/bootloader_size.c",
    "content": "// Copyright 2017 Jack Humbert\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include <avr/io.h>\n#include <avr/boot.h>\n\n// clang-format off\n// this is not valid C - it's for computing the size available on the chip\nAVR_SIZE: FLASHEND + 1 - BOOTLOADER_SIZE\n"
  },
  {
    "path": "platforms/avr/bootloaders/bootloadhid.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n#include <avr/eeprom.h>\n#include <avr/wdt.h>\n\n__attribute__((weak)) void bootloader_jump(void) {\n    // force bootloadHID to stay in bootloader mode, so that it waits\n    // for a new firmware to be flashed\n    // NOTE: this byte is part of QMK's \"magic number\" - changing it causes the EEPROM to be re-initialized\n    // thus every time the device is flashed the EEPROM will be wiped\n    eeprom_write_byte((uint8_t *)1, 0x00);\n\n    // watchdog reset\n    wdt_enable(WDTO_250MS);\n    for (;;)\n        ;\n}\n\n__attribute__((weak)) void mcu_reset(void) {\n    // watchdog reset\n    wdt_enable(WDTO_250MS);\n    for (;;)\n        ;\n}\n"
  },
  {
    "path": "platforms/avr/bootloaders/caterina.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n#include <avr/wdt.h>\n\n__attribute__((weak)) void bootloader_jump(void) {\n    // this block may be optional\n    // TODO: figure it out\n\n    uint16_t *const bootKeyPtr = (uint16_t *)0x0800;\n\n    // Value used by Caterina bootloader use to determine whether to run the\n    // sketch or the bootloader programmer.\n    uint16_t bootKey = 0x7777;\n\n    *bootKeyPtr = bootKey;\n\n    // setup watchdog timeout\n    wdt_enable(WDTO_60MS);\n\n    // wait for watchdog timer to trigger\n    while (1) {\n    }\n}\n\n__attribute__((weak)) void mcu_reset(void) {\n    // setup watchdog timeout\n    wdt_enable(WDTO_60MS);\n\n    // wait for watchdog timer to trigger\n    while (1) {\n    }\n}\n"
  },
  {
    "path": "platforms/avr/bootloaders/custom.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n#include <avr/wdt.h>\n\n__attribute__((weak)) void bootloader_jump(void) {}\n__attribute__((weak)) void mcu_reset(void) {\n    // setup watchdog timeout\n    wdt_enable(WDTO_60MS);\n\n    // wait for watchdog timer to trigger\n    while (1) {\n    }\n}\n"
  },
  {
    "path": "platforms/avr/bootloaders/dfu.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n#include <avr/wdt.h>\n#include <util/delay.h>\n\n#define FLASH_SIZE (FLASHEND + 1L)\n\n/** \\brief Entering the Bootloader via Software\n *\n * http://www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__software_bootloader_start.html\n */\n#define BOOTLOADER_RESET_KEY 0xB007B007\nuint32_t reset_key __attribute__((section(\".noinit,\\\"aw\\\",@nobits;\")));\n\n__attribute__((weak)) void bootloader_jump(void) {\n    UDCON  = 1;\n    USBCON = (1 << FRZCLK); // disable USB\n    UCSR1B = 0;\n    _delay_ms(5); // 5 seems to work fine\n\n    reset_key = BOOTLOADER_RESET_KEY;\n    // watchdog reset\n    wdt_enable(WDTO_250MS);\n    for (;;)\n        ;\n}\n\n__attribute__((weak)) void mcu_reset(void) {\n    // watchdog reset\n    wdt_enable(WDTO_250MS);\n    for (;;)\n        ;\n}\n\n/* this runs before main() */\nvoid bootloader_jump_after_watchdog_reset(void) __attribute__((used, naked, section(\".init3\")));\nvoid bootloader_jump_after_watchdog_reset(void) {\n    if ((MCUSR & (1 << WDRF)) && reset_key == BOOTLOADER_RESET_KEY) {\n        reset_key = 0;\n\n        ((void (*)(void))((FLASH_SIZE - BOOTLOADER_SIZE) >> 1))();\n    }\n}\n"
  },
  {
    "path": "platforms/avr/bootloaders/halfkay.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n#include <avr/interrupt.h>\n#include <avr/wdt.h>\n#include <util/delay.h>\n\n__attribute__((weak)) void bootloader_jump(void) {\n    // http://www.pjrc.com/teensy/jump_to_bootloader.html\n\n    cli();\n    // disable watchdog, if enabled (it's not)\n    // disable all peripherals\n    // a shutdown call might make sense here\n    UDCON  = 1;\n    USBCON = (1 << FRZCLK); // disable USB\n    UCSR1B = 0;\n    _delay_ms(5);\n\n#if defined(__AVR_AT90USB162__) // Teensy 1.0\n    EIMSK  = 0;\n    PCICR  = 0;\n    SPCR   = 0;\n    ACSR   = 0;\n    EECR   = 0;\n    TIMSK0 = 0;\n    TIMSK1 = 0;\n    UCSR1B = 0;\n    DDRB   = 0;\n    DDRC   = 0;\n    DDRD   = 0;\n    PORTB  = 0;\n    PORTC  = 0;\n    PORTD  = 0;\n    asm volatile(\"jmp 0x3E00\");\n#elif defined(__AVR_ATmega32U4__)  // Teensy 2.0\n    EIMSK  = 0;\n    PCICR  = 0;\n    SPCR   = 0;\n    ACSR   = 0;\n    EECR   = 0;\n    ADCSRA = 0;\n    TIMSK0 = 0;\n    TIMSK1 = 0;\n    TIMSK3 = 0;\n    TIMSK4 = 0;\n    UCSR1B = 0;\n    TWCR   = 0;\n    DDRB   = 0;\n    DDRC   = 0;\n    DDRD   = 0;\n    DDRE   = 0;\n    DDRF   = 0;\n    TWCR   = 0;\n    PORTB  = 0;\n    PORTC  = 0;\n    PORTD  = 0;\n    PORTE  = 0;\n    PORTF  = 0;\n    asm volatile(\"jmp 0x7E00\");\n#elif defined(__AVR_AT90USB646__)  // Teensy++ 1.0\n    EIMSK  = 0;\n    PCICR  = 0;\n    SPCR   = 0;\n    ACSR   = 0;\n    EECR   = 0;\n    ADCSRA = 0;\n    TIMSK0 = 0;\n    TIMSK1 = 0;\n    TIMSK2 = 0;\n    TIMSK3 = 0;\n    UCSR1B = 0;\n    TWCR   = 0;\n    DDRA   = 0;\n    DDRB   = 0;\n    DDRC   = 0;\n    DDRD   = 0;\n    DDRE   = 0;\n    DDRF   = 0;\n    PORTA  = 0;\n    PORTB  = 0;\n    PORTC  = 0;\n    PORTD  = 0;\n    PORTE  = 0;\n    PORTF  = 0;\n    asm volatile(\"jmp 0xFC00\");\n#elif defined(__AVR_AT90USB1286__) // Teensy++ 2.0\n    EIMSK  = 0;\n    PCICR  = 0;\n    SPCR   = 0;\n    ACSR   = 0;\n    EECR   = 0;\n    ADCSRA = 0;\n    TIMSK0 = 0;\n    TIMSK1 = 0;\n    TIMSK2 = 0;\n    TIMSK3 = 0;\n    UCSR1B = 0;\n    TWCR   = 0;\n    DDRA   = 0;\n    DDRB   = 0;\n    DDRC   = 0;\n    DDRD   = 0;\n    DDRE   = 0;\n    DDRF   = 0;\n    PORTA  = 0;\n    PORTB  = 0;\n    PORTC  = 0;\n    PORTD  = 0;\n    PORTE  = 0;\n    PORTF  = 0;\n    asm volatile(\"jmp 0x1FC00\");\n#endif\n}\n\n__attribute__((weak)) void mcu_reset(void) {\n    // setup watchdog timeout\n    wdt_enable(WDTO_60MS);\n\n    // wait for watchdog timer to trigger\n    while (1) {\n    }\n}\n"
  },
  {
    "path": "platforms/avr/bootloaders/usbasploader.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n#include <avr/wdt.h>\n\n#define FLASH_SIZE (FLASHEND + 1L)\n\n#if !defined(MCUCSR)\n#    if defined(MCUSR)\n#        define MCUCSR MCUSR\n#    endif\n#endif\n\n__attribute__((weak)) void bootloader_jump(void) {\n    // Taken with permission of Stephan Baerwolf from https://github.com/tinyusbboard/API/blob/master/apipage.c\n\n    wdt_enable(WDTO_15MS);\n    wdt_reset();\n    asm volatile(\"cli                    \\n\\t\"\n                 \"ldi    r29 ,       %[ramendhi] \\n\\t\"\n                 \"ldi    r28 ,       %[ramendlo] \\n\\t\"\n#if (FLASHEND > 131071)\n                 \"ldi    r18 ,       %[bootaddrhi]   \\n\\t\"\n                 \"st     Y+,         r18     \\n\\t\"\n#endif\n                 \"ldi    r18 ,       %[bootaddrme]   \\n\\t\"\n                 \"st     Y+,     r18     \\n\\t\"\n                 \"ldi    r18 ,       %[bootaddrlo]   \\n\\t\"\n                 \"st     Y+,     r18     \\n\\t\"\n                 \"out    %[mcucsrio],    __zero_reg__    \\n\\t\"\n                 \"bootloader_startup_loop%=:         \\n\\t\"\n                 \"rjmp bootloader_startup_loop%=     \\n\\t\"\n                 :\n                 : [mcucsrio] \"I\"(_SFR_IO_ADDR(MCUCSR)),\n#if (FLASHEND > 131071)\n                   [ramendhi] \"M\"(((RAMEND - 2) >> 8) & 0xff), [ramendlo] \"M\"(((RAMEND - 2) >> 0) & 0xff), [bootaddrhi] \"M\"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 16) & 0xff),\n#else\n                   [ramendhi] \"M\"(((RAMEND - 1) >> 8) & 0xff), [ramendlo] \"M\"(((RAMEND - 1) >> 0) & 0xff),\n#endif\n                   [bootaddrme] \"M\"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 8) & 0xff), [bootaddrlo] \"M\"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 0) & 0xff));\n}\n\n__attribute__((weak)) void mcu_reset(void) {\n    // setup watchdog timeout\n    wdt_enable(WDTO_15MS);\n\n    // wait for watchdog timer to trigger\n    while (1) {\n    }\n}\n"
  },
  {
    "path": "platforms/avr/drivers/analog.c",
    "content": "/* Copyright 2015 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"analog.h\"\n\nstatic uint8_t aref = ADC_REF_POWER;\n\nvoid analogReference(uint8_t mode) {\n    aref = mode & (_BV(REFS1) | _BV(REFS0));\n}\n\nint16_t analogReadPin(pin_t pin) {\n    return adc_read(pinToMux(pin));\n}\n\nuint8_t pinToMux(pin_t pin) {\n    switch (pin) {\n        // clang-format off\n#if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)\n        case F0: return 0;  // ADC0\n        case F1: return _BV(MUX0);  // ADC1\n        case F2: return _BV(MUX1);  // ADC2\n        case F3: return _BV(MUX1) | _BV(MUX0);  // ADC3\n        case F4: return _BV(MUX2);  // ADC4\n        case F5: return _BV(MUX2) | _BV(MUX0);  // ADC5\n        case F6: return _BV(MUX2) | _BV(MUX1);  // ADC6\n        case F7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0);  // ADC7\n        default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0);  // 0V\n#elif defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)\n        case F0: return 0;  // ADC0\n        case F1: return _BV(MUX0);  // ADC1\n        case F4: return _BV(MUX2);  // ADC4\n        case F5: return _BV(MUX2) | _BV(MUX0);  // ADC5\n        case F6: return _BV(MUX2) | _BV(MUX1);  // ADC6\n        case F7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0);  // ADC7\n        case D4: return _BV(MUX5);  // ADC8\n        case D6: return _BV(MUX5) | _BV(MUX0);  // ADC9\n        case D7: return _BV(MUX5) | _BV(MUX1);  // ADC10\n        case B4: return _BV(MUX5) | _BV(MUX1) | _BV(MUX0);  // ADC11\n        case B5: return _BV(MUX5) | _BV(MUX2);  // ADC12\n        case B6: return _BV(MUX5) | _BV(MUX2) | _BV(MUX0);  // ADC13\n        default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0);  // 0V\n#elif defined(__AVR_ATmega32A__)\n        case A0: return 0;  // ADC0\n        case A1: return _BV(MUX0);  // ADC1\n        case A2: return _BV(MUX1);  // ADC2\n        case A3: return _BV(MUX1) | _BV(MUX0);  // ADC3\n        case A4: return _BV(MUX2);  // ADC4\n        case A5: return _BV(MUX2) | _BV(MUX0);  // ADC5\n        case A6: return _BV(MUX2) | _BV(MUX1);  // ADC6\n        case A7: return _BV(MUX2) | _BV(MUX1) | _BV(MUX0);  // ADC7\n        default: return _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0);  // 0V\n#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)\n        case C0: return 0;  // ADC0\n        case C1: return _BV(MUX0);  // ADC1\n        case C2: return _BV(MUX1);  // ADC2\n        case C3: return _BV(MUX1) | _BV(MUX0);  // ADC3\n        case C4: return _BV(MUX2);  // ADC4\n        case C5: return _BV(MUX2) | _BV(MUX0);  // ADC5\n        // ADC7:6 not present in DIP package and not shared by GPIO pins\n        default: return _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0);  // 0V\n#endif\n            // clang-format on\n    }\n    return 0;\n}\n\nint16_t adc_read(uint8_t mux) {\n    uint16_t low;\n\n    // Enable ADC and configure prescaler\n    ADCSRA = _BV(ADEN) | ADC_PRESCALER;\n\n#if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)\n    // High speed mode and ADC8-13\n    ADCSRB = _BV(ADHSM) | (mux & _BV(MUX5));\n#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)\n    // High speed mode only\n    ADCSRB = _BV(ADHSM);\n#endif\n\n    // Configure mux input\n#if defined(MUX4)\n    ADMUX = aref | (mux & (_BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0)));\n#else\n    ADMUX  = aref | (mux & (_BV(MUX3) | _BV(MUX2) | _BV(MUX1) | _BV(MUX0)));\n#endif\n\n    // Start the conversion\n    ADCSRA |= _BV(ADSC);\n    // Wait for result\n    while (ADCSRA & _BV(ADSC))\n        ;\n    // Must read LSB first\n    low = ADCL;\n    // Must read MSB only once!\n    low |= (ADCH << 8);\n\n    // turn off the ADC\n    ADCSRA &= ~(1 << ADEN);\n\n    return low;\n}\n"
  },
  {
    "path": "platforms/avr/drivers/analog.h",
    "content": "/* Copyright 2015 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include \"gpio.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nvoid analogReference(uint8_t mode);\n\nint16_t analogReadPin(pin_t pin);\nuint8_t pinToMux(pin_t pin);\n\nint16_t adc_read(uint8_t mux);\n#ifdef __cplusplus\n}\n#endif\n\n#define ADC_REF_EXTERNAL 0                         // AREF, Internal Vref turned off\n#define ADC_REF_POWER _BV(REFS0)                   // AVCC with external capacitor on AREF pin\n#define ADC_REF_INTERNAL (_BV(REFS1) | _BV(REFS0)) // Internal 2.56V Voltage Reference with external capacitor on AREF pin (1.1V for 328P)\n\n// These prescaler values are for high speed mode, ADHSM = 1\n#if F_CPU == 16000000L || F_CPU == 12000000L\n#    define ADC_PRESCALER (_BV(ADPS2) | _BV(ADPS1)) // /64\n#elif F_CPU == 8000000L\n#    define ADC_PRESCALER (_BV(ADPS2) | _BV(ADPS0)) // /32\n#elif F_CPU == 4000000L\n#    define ADC_PRESCALER (_BV(ADPS2)) // /16\n#elif F_CPU == 2000000L\n#    define ADC_PRESCALER (_BV(ADPS1) | _BV(ADPS0)) // /8\n#elif F_CPU == 1000000L\n#    define ADC_PRESCALER _BV(ADPS1) // /4\n#else\n#    define ADC_PRESCALER _BV(ADPS0) // /2\n#endif\n"
  },
  {
    "path": "platforms/avr/drivers/audio_pwm.h",
    "content": "/* Copyright 2020 Jack Humbert\n * Copyright 2020 JohSchneider\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n"
  },
  {
    "path": "platforms/avr/drivers/audio_pwm_hardware.c",
    "content": "/* Copyright 2016 Jack Humbert\n * Copyright 2020 JohSchneider\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"audio.h\"\n#include \"gpio.h\"\n#include <avr/interrupt.h>\n\nextern bool    playing_note;\nextern bool    playing_melody;\nextern uint8_t note_timbre;\n\n#define CPU_PRESCALER 8\n\n/*\n  Audio Driver: PWM\n\n  drive up to two speakers through the AVR PWM hardware-peripheral, using timer1 and/or timer3 on Atmega32U4.\n\n  the primary channel_1 can be connected to either pin PC4 PC5 or PC6 (the later being used by most AVR based keyboards) with a PMW signal generated by timer3\n  and an optional secondary channel_2 on either pin PB5, PB6 or PB7, with a PWM signal from timer1\n\n  alternatively, the PWM pins on PORTB can be used as only/primary speaker\n*/\n\n#if defined(AUDIO_PIN) && (AUDIO_PIN != C4) && (AUDIO_PIN != C5) && (AUDIO_PIN != C6) && (AUDIO_PIN != B5) && (AUDIO_PIN != B6) && (AUDIO_PIN != B7) && (AUDIO_PIN != D5)\n#    error \"Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under the AVR settings for available options.\"\n#endif\n\n#if (AUDIO_PIN == C4) || (AUDIO_PIN == C5) || (AUDIO_PIN == C6)\n#    define AUDIO1_PIN_SET\n#    define AUDIO1_TIMSKx TIMSK3\n#    define AUDIO1_TCCRxA TCCR3A\n#    define AUDIO1_TCCRxB TCCR3B\n#    define AUDIO1_ICRx ICR3\n#    define AUDIO1_WGMx0 WGM30\n#    define AUDIO1_WGMx1 WGM31\n#    define AUDIO1_WGMx2 WGM32\n#    define AUDIO1_WGMx3 WGM33\n#    define AUDIO1_CSx0 CS30\n#    define AUDIO1_CSx1 CS31\n#    define AUDIO1_CSx2 CS32\n\n#    if (AUDIO_PIN == C6)\n#        define AUDIO1_COMxy0 COM3A0\n#        define AUDIO1_COMxy1 COM3A1\n#        define AUDIO1_OCIExy OCIE3A\n#        define AUDIO1_OCRxy OCR3A\n#        define AUDIO1_PIN C6\n#        define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPA_vect\n#    elif (AUDIO_PIN == C5)\n#        define AUDIO1_COMxy0 COM3B0\n#        define AUDIO1_COMxy1 COM3B1\n#        define AUDIO1_OCIExy OCIE3B\n#        define AUDIO1_OCRxy OCR3B\n#        define AUDIO1_PIN C5\n#        define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPB_vect\n#    elif (AUDIO_PIN == C4)\n#        define AUDIO1_COMxy0 COM3C0\n#        define AUDIO1_COMxy1 COM3C1\n#        define AUDIO1_OCIExy OCIE3C\n#        define AUDIO1_OCRxy OCR3C\n#        define AUDIO1_PIN C4\n#        define AUDIO1_TIMERx_COMPy_vect TIMER3_COMPC_vect\n#    endif\n#endif\n\n#if defined(AUDIO_PIN) && defined(AUDIO_PIN_ALT) && (AUDIO_PIN == AUDIO_PIN_ALT)\n#    error \"Audio feature: AUDIO_PIN and AUDIO_PIN_ALT on the same pin makes no sense.\"\n#endif\n\n#if ((AUDIO_PIN == B5) && ((AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B6) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B7))) || ((AUDIO_PIN == B7) && ((AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6)))\n#    error \"Audio feature: PORTB as AUDIO_PIN and AUDIO_PIN_ALT at the same time is not supported.\"\n#endif\n\n#if defined(AUDIO_PIN_ALT) && (AUDIO_PIN_ALT != B5) && (AUDIO_PIN_ALT != B6) && (AUDIO_PIN_ALT != B7)\n#    error \"Audio feature: the pin selected as AUDIO_PIN_ALT is not supported.\"\n#endif\n\n#if (AUDIO_PIN == B5) || (AUDIO_PIN == B6) || (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B5) || (AUDIO_PIN_ALT == B6) || (AUDIO_PIN_ALT == B7) || (AUDIO_PIN == D5)\n#    define AUDIO2_PIN_SET\n#    define AUDIO2_TIMSKx TIMSK1\n#    define AUDIO2_TCCRxA TCCR1A\n#    define AUDIO2_TCCRxB TCCR1B\n#    define AUDIO2_ICRx ICR1\n#    define AUDIO2_WGMx0 WGM10\n#    define AUDIO2_WGMx1 WGM11\n#    define AUDIO2_WGMx2 WGM12\n#    define AUDIO2_WGMx3 WGM13\n#    define AUDIO2_CSx0 CS10\n#    define AUDIO2_CSx1 CS11\n#    define AUDIO2_CSx2 CS12\n\n#    if (AUDIO_PIN == B5) || (AUDIO_PIN_ALT == B5)\n#        define AUDIO2_COMxy0 COM1A0\n#        define AUDIO2_COMxy1 COM1A1\n#        define AUDIO2_OCIExy OCIE1A\n#        define AUDIO2_OCRxy OCR1A\n#        define AUDIO2_PIN B5\n#        define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect\n#    elif (AUDIO_PIN == B6) || (AUDIO_PIN_ALT == B6)\n#        define AUDIO2_COMxy0 COM1B0\n#        define AUDIO2_COMxy1 COM1B1\n#        define AUDIO2_OCIExy OCIE1B\n#        define AUDIO2_OCRxy OCR1B\n#        define AUDIO2_PIN B6\n#        define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPB_vect\n#    elif (AUDIO_PIN == B7) || (AUDIO_PIN_ALT == B7)\n#        define AUDIO2_COMxy0 COM1C0\n#        define AUDIO2_COMxy1 COM1C1\n#        define AUDIO2_OCIExy OCIE1C\n#        define AUDIO2_OCRxy OCR1C\n#        define AUDIO2_PIN B7\n#        define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPC_vect\n#    elif (AUDIO_PIN == D5) && defined(__AVR_ATmega32A__)\n#        pragma message \"Audio support for ATmega32A is experimental and can cause crashes.\"\n#        undef AUDIO2_TIMSKx\n#        define AUDIO2_TIMSKx TIMSK\n#        define AUDIO2_COMxy0 COM1A0\n#        define AUDIO2_COMxy1 COM1A1\n#        define AUDIO2_OCIExy OCIE1A\n#        define AUDIO2_OCRxy OCR1A\n#        define AUDIO2_PIN D5\n#        define AUDIO2_TIMERx_COMPy_vect TIMER1_COMPA_vect\n#    endif\n#endif\n\n// C6 seems to be the assumed default by many existing keyboard - but sill warn the user\n#if !defined(AUDIO1_PIN_SET) && !defined(AUDIO2_PIN_SET)\n#    pragma message \"Audio feature enabled, but no suitable pin selected - see docs/feature_audio under the AVR settings for available options. Don't expect to hear anything... :-)\"\n// TODO: make this an error - go through the breaking-change-process and change all keyboards to the new define\n#endif\n// -----------------------------------------------------------------------------\n\n#ifdef AUDIO1_PIN_SET\nstatic float channel_1_frequency = 0.0f;\nvoid         channel_1_set_frequency(float freq) {\n    if (freq == 0.0f) // a pause/rest is a valid \"note\" with freq=0\n    {\n        // disable the output, but keep the pwm-ISR going (with the previous\n        // frequency) so the audio-state keeps getting updated\n        // Note: setting the duty-cycle 0 is not possible on non-inverting PWM mode - see the AVR data-sheet\n        AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));\n        return;\n    } else {\n        AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1); // enable output, PWM mode\n    }\n\n    channel_1_frequency = freq;\n\n    // set pwm period\n    AUDIO1_ICRx = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));\n    // and duty cycle\n    AUDIO1_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);\n}\n\nvoid channel_1_start(void) {\n    // enable timer-counter ISR\n    AUDIO1_TIMSKx |= _BV(AUDIO1_OCIExy);\n    // enable timer-counter output\n    AUDIO1_TCCRxA |= _BV(AUDIO1_COMxy1);\n}\n\nvoid channel_1_stop(void) {\n    // disable timer-counter ISR\n    AUDIO1_TIMSKx &= ~_BV(AUDIO1_OCIExy);\n    // disable timer-counter output\n    AUDIO1_TCCRxA &= ~(_BV(AUDIO1_COMxy1) | _BV(AUDIO1_COMxy0));\n}\n#endif\n\n#ifdef AUDIO2_PIN_SET\nstatic float channel_2_frequency = 0.0f;\nvoid         channel_2_set_frequency(float freq) {\n    if (freq == 0.0f) {\n        AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));\n        return;\n    } else {\n        AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);\n    }\n\n    channel_2_frequency = freq;\n\n    AUDIO2_ICRx  = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER));\n    AUDIO2_OCRxy = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre / 100);\n}\n\nfloat channel_2_get_frequency(void) {\n    return channel_2_frequency;\n}\n\nvoid channel_2_start(void) {\n    AUDIO2_TIMSKx |= _BV(AUDIO2_OCIExy);\n    AUDIO2_TCCRxA |= _BV(AUDIO2_COMxy1);\n}\n\nvoid channel_2_stop(void) {\n    AUDIO2_TIMSKx &= ~_BV(AUDIO2_OCIExy);\n    AUDIO2_TCCRxA &= ~(_BV(AUDIO2_COMxy1) | _BV(AUDIO2_COMxy0));\n}\n#endif\n\nvoid audio_driver_initialize_impl(void) {\n#ifdef AUDIO1_PIN_SET\n    channel_1_stop();\n    gpio_set_pin_output(AUDIO1_PIN);\n#endif\n\n#ifdef AUDIO2_PIN_SET\n    channel_2_stop();\n    gpio_set_pin_output(AUDIO2_PIN);\n#endif\n\n    // TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B\n    // Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation\n    //   OC3A -- PC6\n    //   OC3B -- PC5\n    //   OC3C -- PC4\n    //   OC1A -- PB5\n    //   OC1B -- PB6\n    //   OC1C -- PB7\n\n    // Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A)\n    //   OCR3A - PC6\n    //   OCR3B - PC5\n    //   OCR3C - PC4\n    //   OCR1A - PB5\n    //   OCR1B - PB6\n    //   OCR1C - PB7\n\n    // Clock Select (CS3n) = 0b010 = Clock / 8\n#ifdef AUDIO1_PIN_SET\n    // initialize timer-counter\n    AUDIO1_TCCRxA = (0 << AUDIO1_COMxy1) | (0 << AUDIO1_COMxy0) | (1 << AUDIO1_WGMx1) | (0 << AUDIO1_WGMx0);\n    AUDIO1_TCCRxB = (1 << AUDIO1_WGMx3) | (1 << AUDIO1_WGMx2) | (0 << AUDIO1_CSx2) | (1 << AUDIO1_CSx1) | (0 << AUDIO1_CSx0);\n#endif\n\n#ifdef AUDIO2_PIN_SET\n    AUDIO2_TCCRxA = (0 << AUDIO2_COMxy1) | (0 << AUDIO2_COMxy0) | (1 << AUDIO2_WGMx1) | (0 << AUDIO2_WGMx0);\n    AUDIO2_TCCRxB = (1 << AUDIO2_WGMx3) | (1 << AUDIO2_WGMx2) | (0 << AUDIO2_CSx2) | (1 << AUDIO2_CSx1) | (0 << AUDIO2_CSx0);\n#endif\n}\n\nvoid audio_driver_stop_impl(void) {\n#ifdef AUDIO1_PIN_SET\n    channel_1_stop();\n#endif\n\n#ifdef AUDIO2_PIN_SET\n    channel_2_stop();\n#endif\n}\n\nvoid audio_driver_start_impl(void) {\n#ifdef AUDIO1_PIN_SET\n    channel_1_start();\n    if (playing_note) {\n        channel_1_set_frequency(audio_get_processed_frequency(0));\n    }\n#endif\n\n#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)\n    channel_2_start();\n    if (playing_note) {\n        channel_2_set_frequency(audio_get_processed_frequency(0));\n    }\n#endif\n}\n\nstatic volatile uint32_t isr_counter = 0;\n#ifdef AUDIO1_PIN_SET\nISR(AUDIO1_TIMERx_COMPy_vect) {\n    isr_counter++;\n    if (isr_counter < channel_1_frequency / (CPU_PRESCALER * 8)) return;\n\n    isr_counter        = 0;\n    bool state_changed = audio_update_state();\n\n    if (!playing_note && !playing_melody) {\n        channel_1_stop();\n#    ifdef AUDIO2_PIN_SET\n        channel_2_stop();\n#    endif\n        return;\n    }\n\n    if (state_changed) {\n        channel_1_set_frequency(audio_get_processed_frequency(0));\n#    ifdef AUDIO2_PIN_SET\n        if (audio_get_number_of_active_tones() > 1) {\n            channel_2_set_frequency(audio_get_processed_frequency(1));\n        } else {\n            channel_2_stop();\n        }\n#    endif\n    }\n}\n#endif\n\n#if !defined(AUDIO1_PIN_SET) && defined(AUDIO2_PIN_SET)\nISR(AUDIO2_TIMERx_COMPy_vect) {\n    isr_counter++;\n    if (isr_counter < channel_2_frequency / (CPU_PRESCALER * 8)) return;\n\n    isr_counter        = 0;\n    bool state_changed = audio_update_state();\n\n    if (!playing_note && !playing_melody) {\n        channel_2_stop();\n        return;\n    }\n\n    if (state_changed) {\n        channel_2_set_frequency(audio_get_processed_frequency(0));\n    }\n}\n#endif\n"
  },
  {
    "path": "platforms/avr/drivers/backlight_pwm.c",
    "content": "#include \"backlight.h\"\n#include \"gpio.h\"\n#include \"progmem.h\"\n#include <avr/io.h>\n#include <avr/interrupt.h>\n\n// Maximum duty cycle limit\n#ifndef BACKLIGHT_LIMIT_VAL\n#    define BACKLIGHT_LIMIT_VAL 255\n#endif\n\n#if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) && (BACKLIGHT_PIN == B5 || BACKLIGHT_PIN == B6 || BACKLIGHT_PIN == B7)\n#    define ICRx ICR1\n#    define TCCRxA TCCR1A\n#    define TCCRxB TCCR1B\n#    define TIMERx_OVF_vect TIMER1_OVF_vect\n#    define TIMSKx TIMSK1\n#    define TOIEx TOIE1\n\n#    if BACKLIGHT_PIN == B5\n#        define COMxx0 COM1A0\n#        define COMxx1 COM1A1\n#        define OCRxx OCR1A\n#    elif BACKLIGHT_PIN == B6\n#        define COMxx0 COM1B0\n#        define COMxx1 COM1B1\n#        define OCRxx OCR1B\n#    elif BACKLIGHT_PIN == B7\n#        define COMxx0 COM1C0\n#        define COMxx1 COM1C1\n#        define OCRxx OCR1C\n#    endif\n#elif (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) && (BACKLIGHT_PIN == C4 || BACKLIGHT_PIN == C5 || BACKLIGHT_PIN == C6)\n#    define ICRx ICR3\n#    define TCCRxA TCCR3A\n#    define TCCRxB TCCR3B\n#    define TIMERx_OVF_vect TIMER3_OVF_vect\n#    define TIMSKx TIMSK3\n#    define TOIEx TOIE3\n\n#    if BACKLIGHT_PIN == C4\n#        if (defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))\n#            error This MCU has no C4 pin!\n#        else\n#            define COMxx0 COM3C0\n#            define COMxx1 COM3C1\n#            define OCRxx OCR3C\n#        endif\n#    elif BACKLIGHT_PIN == C5\n#        if (defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))\n#            error This MCU has no C5 pin!\n#        else\n#            define COMxx0 COM3B0\n#            define COMxx1 COM3B1\n#            define OCRxx OCR3B\n#        endif\n#    elif BACKLIGHT_PIN == C6\n#        define COMxx0 COM3A0\n#        define COMxx1 COM3A1\n#        define OCRxx OCR3A\n#    endif\n#elif (defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__)) && (BACKLIGHT_PIN == B7 || BACKLIGHT_PIN == C5 || BACKLIGHT_PIN == C6)\n#    define ICRx ICR1\n#    define TCCRxA TCCR1A\n#    define TCCRxB TCCR1B\n#    define TIMERx_OVF_vect TIMER1_OVF_vect\n#    define TIMSKx TIMSK1\n#    define TOIEx TOIE1\n\n#    if BACKLIGHT_PIN == B7\n#        define COMxx0 COM1C0\n#        define COMxx1 COM1C1\n#        define OCRxx OCR1C\n#    elif BACKLIGHT_PIN == C5\n#        define COMxx0 COM1B0\n#        define COMxx1 COM1B1\n#        define OCRxx OCR1B\n#    elif BACKLIGHT_PIN == C6\n#        define COMxx0 COM1A0\n#        define COMxx1 COM1A1\n#        define OCRxx OCR1A\n#    endif\n#elif defined(__AVR_ATmega32A__) && (BACKLIGHT_PIN == D4 || BACKLIGHT_PIN == D5)\n#    define ICRx ICR1\n#    define TCCRxA TCCR1A\n#    define TCCRxB TCCR1B\n#    define TIMERx_OVF_vect TIMER1_OVF_vect\n#    define TIMSKx TIMSK\n#    define TOIEx TOIE1\n\n#    if BACKLIGHT_PIN == D4\n#        define COMxx0 COM1B0\n#        define COMxx1 COM1B1\n#        define OCRxx OCR1B\n#    elif BACKLIGHT_PIN == D5\n#        define COMxx0 COM1A0\n#        define COMxx1 COM1A1\n#        define OCRxx OCR1A\n#    endif\n#elif (defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)) && (BACKLIGHT_PIN == B1 || BACKLIGHT_PIN == B2)\n#    define ICRx ICR1\n#    define TCCRxA TCCR1A\n#    define TCCRxB TCCR1B\n#    define TIMERx_OVF_vect TIMER1_OVF_vect\n#    define TIMSKx TIMSK1\n#    define TOIEx TOIE1\n\n#    if BACKLIGHT_PIN == B1\n#        define COMxx0 COM1A0\n#        define COMxx1 COM1A1\n#        define OCRxx OCR1A\n#    elif BACKLIGHT_PIN == B2\n#        define COMxx0 COM1B0\n#        define COMxx1 COM1B1\n#        define OCRxx OCR1B\n#    endif\n#endif\n\n#ifndef BACKLIGHT_RESOLUTION\n#    define BACKLIGHT_RESOLUTION 0xFFFFU\n#endif\n\n#if (BACKLIGHT_RESOLUTION > 0xFFFF || BACKLIGHT_RESOLUTION < 0x00FF)\n#    error \"Backlight resolution must be between 0x00FF and 0xFFFF\"\n#endif\n\n#define BREATHING_SCALE_FACTOR F_CPU / BACKLIGHT_RESOLUTION / 120\n\nstatic inline void enable_pwm(void) {\n#if BACKLIGHT_ON_STATE == 1\n    TCCRxA |= _BV(COMxx1);\n#else\n    TCCRxA |= _BV(COMxx1) | _BV(COMxx0);\n#endif\n}\n\nstatic inline void disable_pwm(void) {\n#if BACKLIGHT_ON_STATE == 1\n    TCCRxA &= ~(_BV(COMxx1));\n#else\n    TCCRxA &= ~(_BV(COMxx1) | _BV(COMxx0));\n#endif\n}\n\n// See http://jared.geek.nz/2013/feb/linear-led-pwm\nstatic uint16_t cie_lightness(uint16_t v) {\n    if (v <= (uint32_t)ICRx / 12) // If the value is less than or equal to ~8% of max\n    {\n        return v / 9; // Same as dividing by 900%\n    } else {\n        // In the next two lines values are bit-shifted. This is to avoid loosing decimals in integer math.\n        uint32_t y   = (((uint32_t)v + (uint32_t)ICRx / 6) << 5) / ((uint32_t)ICRx / 6 + ICRx); // If above 8%, add ~16% of max, and normalize with (max + ~16% max)\n        uint32_t out = (y * y * y * ICRx) >> 15;                                                // Cube it and undo the bit-shifting. (which is now three times as much due to the cubing)\n\n        if (out > ICRx) // Avoid overflows\n        {\n            out = ICRx;\n        }\n        return (uint16_t)out;\n    }\n}\n\n// rescale the supplied backlight value to be in terms of the value limit\t// range for val is [0..ICRx]. PWM pin is high while the timer count is below val.\nstatic uint32_t rescale_limit_val(uint32_t val) {\n    return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256;\n}\n\n// range for val is [0..ICRx]. PWM pin is high while the timer count is below val.\nstatic inline void set_pwm(uint16_t val) {\n    OCRxx = val;\n}\n\nvoid backlight_set(uint8_t level) {\n    if (level > BACKLIGHT_LEVELS) level = BACKLIGHT_LEVELS;\n\n    if (level == 0) {\n        // Turn off PWM control on backlight pin\n        disable_pwm();\n    } else {\n        // Turn on PWM control of backlight pin\n        enable_pwm();\n    }\n    // Set the brightness\n    set_pwm(cie_lightness(rescale_limit_val(ICRx * (uint32_t)level / BACKLIGHT_LEVELS)));\n}\n\nvoid backlight_task(void) {}\n\n#ifdef BACKLIGHT_BREATHING\n#    define BREATHING_NO_HALT 0\n#    define BREATHING_HALT_OFF 1\n#    define BREATHING_HALT_ON 2\n#    define BREATHING_STEPS 128\n\nstatic uint8_t  breathing_halt    = BREATHING_NO_HALT;\nstatic uint16_t breathing_counter = 0;\n\nstatic uint8_t breath_scale_counter = 1;\n/* Run the breathing loop at ~120Hz*/\nconst uint8_t breathing_ISR_frequency = 120;\n\nbool is_breathing(void) {\n    return !!(TIMSKx & _BV(TOIEx));\n}\n\n#    define breathing_interrupt_enable() \\\n        do {                             \\\n            TIMSKx |= _BV(TOIEx);        \\\n        } while (0)\n#    define breathing_interrupt_disable() \\\n        do {                              \\\n            TIMSKx &= ~_BV(TOIEx);        \\\n        } while (0)\n\n#    define breathing_min()        \\\n        do {                       \\\n            breathing_counter = 0; \\\n        } while (0)\n#    define breathing_max()                                                           \\\n        do {                                                                          \\\n            breathing_counter = get_breathing_period() * breathing_ISR_frequency / 2; \\\n        } while (0)\n\nvoid breathing_enable(void) {\n    breathing_counter = 0;\n    breathing_halt    = BREATHING_NO_HALT;\n    breathing_interrupt_enable();\n}\n\nvoid breathing_pulse(void) {\n    if (get_backlight_level() == 0)\n        breathing_min();\n    else\n        breathing_max();\n    breathing_halt = BREATHING_HALT_ON;\n    breathing_interrupt_enable();\n}\n\nvoid breathing_disable(void) {\n    breathing_interrupt_disable();\n    // Restore backlight level\n    backlight_set(get_backlight_level());\n}\n\nvoid breathing_self_disable(void) {\n    if (get_backlight_level() == 0)\n        breathing_halt = BREATHING_HALT_OFF;\n    else\n        breathing_halt = BREATHING_HALT_ON;\n}\n\n/* To generate breathing curve in python:\n * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]\n */\nstatic const uint8_t breathing_table[BREATHING_STEPS] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\n\n// Use this before the cie_lightness function.\nstatic inline uint16_t scale_backlight(uint16_t v) {\n    return v / BACKLIGHT_LEVELS * get_backlight_level();\n}\n\n/* Assuming a 16MHz CPU clock and a timer that resets at 64k (ICR1), the following interrupt handler will run\n * about 244 times per second.\n *\n * The following ISR runs at F_CPU/ISRx. With a 16MHz clock and default pwm resolution, that means 244Hz\n */\nISR(TIMERx_OVF_vect) {\n    // Only run this ISR at ~120 Hz\n    if (breath_scale_counter++ == BREATHING_SCALE_FACTOR) {\n        breath_scale_counter = 1;\n    } else {\n        return;\n    }\n    uint16_t interval = (uint16_t)get_breathing_period() * breathing_ISR_frequency / BREATHING_STEPS;\n    // resetting after one period to prevent ugly reset at overflow.\n    breathing_counter = (breathing_counter + 1) % (get_breathing_period() * breathing_ISR_frequency);\n    uint8_t index     = breathing_counter / interval;\n    // limit index to max step value\n    if (index >= BREATHING_STEPS) {\n        index = BREATHING_STEPS - 1;\n    }\n\n    if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) || ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1))) {\n        breathing_interrupt_disable();\n    }\n\n    // Set PWM to a brightnessvalue scaled to the configured resolution\n    set_pwm(cie_lightness(rescale_limit_val(scale_backlight((uint32_t)pgm_read_byte(&breathing_table[index]) * ICRx / 255))));\n}\n\n#endif // BACKLIGHT_BREATHING\n\nvoid backlight_init_ports(void) {\n    gpio_set_pin_output(BACKLIGHT_PIN);\n#if BACKLIGHT_ON_STATE == 1\n    gpio_write_pin_low(BACKLIGHT_PIN);\n#else\n    gpio_write_pin_high(BACKLIGHT_PIN);\n#endif\n\n    // I could write a wall of text here to explain... but TL;DW\n    // Go read the ATmega32u4 datasheet.\n    // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on\n\n    // Pin PB7 = OCR1C (Timer 1, Channel C)\n    // Compare Output Mode = Clear on compare match, Channel C = COM1C1=1 COM1C0=0\n    // (i.e. start high, go low when counter matches.)\n    // WGM Mode 14 (Fast PWM) = WGM13=1 WGM12=1 WGM11=1 WGM10=0\n    // Clock Select = clk/1 (no prescaling) = CS12=0 CS11=0 CS10=1\n\n    /*\n    14.8.3:\n    \"In fast PWM mode, the compare units allow generation of PWM waveforms on the OCnx pins. Setting the COMnx1:0 bits to two will produce a non-inverted PWM [..].\"\n    \"In fast PWM mode the counter is incremented until the counter value matches either one of the fixed values 0x00FF, 0x01FF, or 0x03FF (WGMn3:0 = 5, 6, or 7), the value in ICRn (WGMn3:0 = 14), or the value in OCRnA (WGMn3:0 = 15).\"\n    */\n    TCCRxA = _BV(COMxx1) | _BV(WGM11);            // = 0b00001010;\n    TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;\n    ICRx   = BACKLIGHT_RESOLUTION;\n\n    backlight_init();\n\n#ifdef BACKLIGHT_BREATHING\n    if (is_backlight_breathing()) {\n        breathing_enable();\n    }\n#endif\n}\n"
  },
  {
    "path": "platforms/avr/drivers/backlight_timer.c",
    "content": "#include \"backlight.h\"\n#include \"backlight_driver_common.h\"\n#include \"progmem.h\"\n#include <avr/io.h>\n#include <avr/interrupt.h>\n\n// Maximum duty cycle limit\n#ifndef BACKLIGHT_LIMIT_VAL\n#    define BACKLIGHT_LIMIT_VAL 255\n#endif\n\n#ifndef BACKLIGHT_PWM_TIMER\n#    define BACKLIGHT_PWM_TIMER 1\n#endif\n\n#if BACKLIGHT_PWM_TIMER == 1\n#    define ICRx ICR1\n#    define TCCRxA TCCR1A\n#    define TCCRxB TCCR1B\n#    define TIMERx_COMPA_vect TIMER1_COMPA_vect\n#    define TIMERx_OVF_vect TIMER1_OVF_vect\n#    if defined(__AVR_ATmega32A__) // This MCU has only one TIMSK register\n#        define TIMSKx TIMSK\n#    else\n#        define TIMSKx TIMSK1\n#    endif\n#    define TOIEx TOIE1\n\n#    define OCIExA OCIE1A\n#    define OCRxx OCR1A\n#elif BACKLIGHT_PWM_TIMER == 3\n#    define ICRx ICR1\n#    define TCCRxA TCCR3A\n#    define TCCRxB TCCR3B\n#    define TIMERx_COMPA_vect TIMER3_COMPA_vect\n#    define TIMERx_OVF_vect TIMER3_OVF_vect\n#    define TIMSKx TIMSK3\n#    define TOIEx TOIE3\n\n#    define OCIExA OCIE3A\n#    define OCRxx OCR3A\n#else\n#    error Invalid backlight PWM timer!\n#endif\n\n#ifndef BACKLIGHT_RESOLUTION\n#    define BACKLIGHT_RESOLUTION 0xFFFFU\n#endif\n\n#if (BACKLIGHT_RESOLUTION > 0xFFFF || BACKLIGHT_RESOLUTION < 0x00FF)\n#    error \"Backlight resolution must be between 0x00FF and 0xFFFF\"\n#endif\n\n#define BREATHING_SCALE_FACTOR F_CPU / BACKLIGHT_RESOLUTION / 120\n\n// The idea of software PWM assisted by hardware timers is the following\n// we use the hardware timer in fast PWM mode like for hardware PWM, but\n// instead of letting the Output Match Comparator control the led pin\n// (which is not possible since the backlight is not wired to PWM pins on the\n// CPU), we do the LED on/off by oursleves.\n// The timer is setup to count up to 0xFFFF, and we set the Output Compare\n// register to the current 16bits backlight level (after CIE correction).\n// This means the CPU will trigger a compare match interrupt when the counter\n// reaches the backlight level, where we turn off the LEDs,\n// but also an overflow interrupt when the counter rolls back to 0,\n// in which we're going to turn on the LEDs.\n// The LED will then be on for OCRxx/0xFFFF time, adjusted every 244Hz,\n// or F_CPU/BACKLIGHT_RESOLUTION if used.\n\n// Triggered when the counter reaches the OCRx value\nISR(TIMERx_COMPA_vect) {\n    backlight_pins_off();\n}\n\n// Triggered when the counter reaches the TOP value\n// this one triggers at F_CPU/ICRx = 16MHz/65536 =~ 244 Hz\nISR(TIMERx_OVF_vect) {\n#ifdef BACKLIGHT_BREATHING\n    if (is_breathing()) {\n        breathing_task();\n    }\n#endif\n    // for very small values of OCRxx (or backlight level)\n    // we can't guarantee this whole code won't execute\n    // at the same time as the compare match interrupt\n    // which means that we might turn on the leds while\n    // trying to turn them off, leading to flickering\n    // artifacts (especially while breathing, because breathing_task\n    // takes many computation cycles).\n    // so better not turn them on while the counter TOP is very low.\n    if (OCRxx > ICRx / 250 + 5) {\n        backlight_pins_on();\n    }\n}\n\n// See http://jared.geek.nz/2013/feb/linear-led-pwm\nstatic uint16_t cie_lightness(uint16_t v) {\n    if (v <= (uint32_t)ICRx / 12) // If the value is less than or equal to ~8% of max\n    {\n        return v / 9; // Same as dividing by 900%\n    } else {\n        // In the next two lines values are bit-shifted. This is to avoid loosing decimals in integer math.\n        uint32_t y   = (((uint32_t)v + (uint32_t)ICRx / 6) << 5) / ((uint32_t)ICRx / 6 + ICRx); // If above 8%, add ~16% of max, and normalize with (max + ~16% max)\n        uint32_t out = (y * y * y * ICRx) >> 15;                                                // Cube it and undo the bit-shifting. (which is now three times as much due to the cubing)\n\n        if (out > ICRx) // Avoid overflows\n        {\n            out = ICRx;\n        }\n        return (uint16_t)out;\n    }\n}\n\n// rescale the supplied backlight value to be in terms of the value limit\t// range for val is [0..ICRx]. PWM pin is high while the timer count is below val.\nstatic uint32_t rescale_limit_val(uint32_t val) {\n    return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256;\n}\n\n// range for val is [0..ICRx]. PWM pin is high while the timer count is below val.\nstatic inline void set_pwm(uint16_t val) {\n    OCRxx = val;\n}\n\nvoid backlight_set(uint8_t level) {\n    if (level > BACKLIGHT_LEVELS) level = BACKLIGHT_LEVELS;\n\n    if (level == 0) {\n        if (OCRxx) {\n            TIMSKx &= ~(_BV(OCIExA));\n            TIMSKx &= ~(_BV(TOIEx));\n        }\n        backlight_pins_off();\n    } else {\n        if (!OCRxx) {\n            TIMSKx |= _BV(OCIExA);\n            TIMSKx |= _BV(TOIEx);\n        }\n    }\n    // Set the brightness\n    set_pwm(cie_lightness(rescale_limit_val(ICRx * (uint32_t)level / BACKLIGHT_LEVELS)));\n}\n\nvoid backlight_task(void) {}\n\n#ifdef BACKLIGHT_BREATHING\n#    define BREATHING_NO_HALT 0\n#    define BREATHING_HALT_OFF 1\n#    define BREATHING_HALT_ON 2\n#    define BREATHING_STEPS 128\n\nstatic uint8_t  breathing_halt    = BREATHING_NO_HALT;\nstatic uint16_t breathing_counter = 0;\n\nstatic uint8_t breath_scale_counter = 1;\n/* Run the breathing loop at ~120Hz*/\nconst uint8_t breathing_ISR_frequency = 120;\n\nstatic bool breathing = false;\n\nbool is_breathing(void) {\n    return breathing;\n}\n\n#    define breathing_interrupt_enable() \\\n        do {                             \\\n            breathing = true;            \\\n        } while (0)\n#    define breathing_interrupt_disable() \\\n        do {                              \\\n            breathing = false;            \\\n        } while (0)\n\n#    define breathing_min()        \\\n        do {                       \\\n            breathing_counter = 0; \\\n        } while (0)\n#    define breathing_max()                                                           \\\n        do {                                                                          \\\n            breathing_counter = get_breathing_period() * breathing_ISR_frequency / 2; \\\n        } while (0)\n\nvoid breathing_enable(void) {\n    breathing_counter = 0;\n    breathing_halt    = BREATHING_NO_HALT;\n    breathing_interrupt_enable();\n}\n\nvoid breathing_pulse(void) {\n    if (get_backlight_level() == 0)\n        breathing_min();\n    else\n        breathing_max();\n    breathing_halt = BREATHING_HALT_ON;\n    breathing_interrupt_enable();\n}\n\nvoid breathing_disable(void) {\n    breathing_interrupt_disable();\n    // Restore backlight level\n    backlight_set(get_backlight_level());\n}\n\nvoid breathing_self_disable(void) {\n    if (get_backlight_level() == 0)\n        breathing_halt = BREATHING_HALT_OFF;\n    else\n        breathing_halt = BREATHING_HALT_ON;\n}\n\n/* To generate breathing curve in python:\n * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]\n */\nstatic const uint8_t breathing_table[BREATHING_STEPS] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\n\n// Use this before the cie_lightness function.\nstatic inline uint16_t scale_backlight(uint16_t v) {\n    return v / BACKLIGHT_LEVELS * get_backlight_level();\n}\n\nvoid breathing_task(void) {\n    // Only run this ISR at ~120 Hz\n    if (breath_scale_counter++ == BREATHING_SCALE_FACTOR) {\n        breath_scale_counter = 1;\n    } else {\n        return;\n    }\n    uint16_t interval = (uint16_t)get_breathing_period() * breathing_ISR_frequency / BREATHING_STEPS;\n    // resetting after one period to prevent ugly reset at overflow.\n    breathing_counter = (breathing_counter + 1) % (get_breathing_period() * breathing_ISR_frequency);\n    uint8_t index     = breathing_counter / interval;\n    // limit index to max step value\n    if (index >= BREATHING_STEPS) {\n        index = BREATHING_STEPS - 1;\n    }\n\n    if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) || ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1))) {\n        breathing_interrupt_disable();\n    }\n\n    // Set PWM to a brightnessvalue scaled to the configured resolution\n    set_pwm(cie_lightness(rescale_limit_val(scale_backlight((uint32_t)pgm_read_byte(&breathing_table[index]) * ICRx / 255))));\n}\n\n#endif // BACKLIGHT_BREATHING\n\nvoid backlight_init_ports(void) {\n    // Setup backlight pin as output and output to on state.\n    backlight_pins_init();\n\n    // I could write a wall of text here to explain... but TL;DW\n    // Go read the ATmega32u4 datasheet.\n    // And this: http://blog.saikoled.com/post/43165849837/secret-konami-cheat-code-to-high-resolution-pwm-on\n\n    // TimerX setup, Fast PWM mode count to TOP set in ICRx\n    TCCRxA = _BV(WGM11); // = 0b00000010;\n    // clock select clk/1\n    TCCRxB = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // = 0b00011001;\n    ICRx   = BACKLIGHT_RESOLUTION;\n\n    backlight_init();\n\n#ifdef BACKLIGHT_BREATHING\n    if (is_backlight_breathing()) {\n        breathing_enable();\n    }\n#endif\n}\n"
  },
  {
    "path": "platforms/avr/drivers/glcdfont.c",
    "content": "// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0.\n// See gfxfont.h for newer custom bitmap font info.\n\n#include \"progmem.h\"\n\n// Standard ASCII 5x7 font\n\nstatic const unsigned char font[] PROGMEM = {\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x5B, 0x4F, 0x5B, 0x3E, 0x3E, 0x6B, 0x4F, 0x6B, 0x3E, 0x1C, 0x3E, 0x7C, 0x3E, 0x1C, 0x18, 0x3C, 0x7E, 0x3C, 0x18, 0x1C, 0x57, 0x7D, 0x57, 0x1C, 0x1C, 0x5E, 0x7F, 0x5E, 0x1C, 0x00, 0x18, 0x3C, 0x18, 0x00, 0xFF, 0xE7, 0xC3, 0xE7, 0xFF, 0x00, 0x18, 0x24, 0x18, 0x00, 0xFF, 0xE7, 0xDB, 0xE7, 0xFF, 0x30, 0x48, 0x3A, 0x06, 0x0E, 0x26, 0x29, 0x79, 0x29, 0x26, 0x40, 0x7F, 0x05, 0x05, 0x07, 0x40, 0x7F, 0x05, 0x25, 0x3F, 0x5A, 0x3C, 0xE7, 0x3C, 0x5A, 0x7F, 0x3E, 0x1C, 0x1C, 0x08, 0x08, 0x1C, 0x1C, 0x3E, 0x7F, 0x14, 0x22, 0x7F, 0x22, 0x14, 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, 0x66, 0x89, 0x95, 0x6A, 0x60, 0x60, 0x60, 0x60, 0x60, 0x94, 0xA2, 0xFF, 0xA2, 0x94, 0x08, 0x04, 0x7E, 0x04, 0x08, 0x10, 0x20, 0x7E, 0x20, 0x10, 0x08, 0x08, 0x2A, 0x1C, 0x08, 0x08, 0x1C, 0x2A, 0x08, 0x08, 0x1E, 0x10, 0x10, 0x10, 0x10, 0x0C, 0x1E, 0x0C, 0x1E, 0x0C,\n    0x30, 0x38, 0x3E, 0x38, 0x30, 0x06, 0x0E, 0x3E, 0x0E, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x23, 0x13, 0x08, 0x64, 0x62, 0x36, 0x49, 0x56, 0x20, 0x50, 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, 0x41, 0x22, 0x1C, 0x00, 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x80, 0x70, 0x30, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x60, 0x60, 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x72, 0x49, 0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4D, 0x33, 0x18, 0x14, 0x12, 0x7F, 0x10, 0x27, 0x45, 0x45, 0x45, 0x39, 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x41, 0x21, 0x11, 0x09, 0x07, 0x36, 0x49, 0x49, 0x49, 0x36, 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x40, 0x34, 0x00, 0x00,\n    0x00, 0x08, 0x14, 0x22, 0x41, 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, 0x41, 0x22, 0x14, 0x08, 0x02, 0x01, 0x59, 0x09, 0x06, 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E, 0x41, 0x41, 0x41, 0x22, 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01, 0x3E, 0x41, 0x41, 0x51, 0x73, 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, 0x41, 0x7F, 0x41, 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, 0x7F, 0x08, 0x14, 0x22, 0x41, 0x7F, 0x40, 0x40, 0x40, 0x40, 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x7F, 0x09, 0x09, 0x09, 0x06, 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x7F, 0x09, 0x19, 0x29, 0x46, 0x26, 0x49, 0x49, 0x49, 0x32, 0x03, 0x01, 0x7F, 0x01, 0x03, 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x63, 0x14, 0x08, 0x14, 0x63, 0x03, 0x04, 0x78, 0x04, 0x03,\n    0x61, 0x59, 0x49, 0x4D, 0x43, 0x00, 0x7F, 0x41, 0x41, 0x41, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x41, 0x41, 0x41, 0x7F, 0x04, 0x02, 0x01, 0x02, 0x04, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x03, 0x07, 0x08, 0x00, 0x20, 0x54, 0x54, 0x78, 0x40, 0x7F, 0x28, 0x44, 0x44, 0x38, 0x38, 0x44, 0x44, 0x44, 0x28, 0x38, 0x44, 0x44, 0x28, 0x7F, 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, 0x08, 0x7E, 0x09, 0x02, 0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, 0x44, 0x7D, 0x40, 0x00, 0x20, 0x40, 0x40, 0x3D, 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, 0x7C, 0x04, 0x78, 0x04, 0x78, 0x7C, 0x08, 0x04, 0x04, 0x78, 0x38, 0x44, 0x44, 0x44, 0x38, 0xFC, 0x18, 0x24, 0x24, 0x18, 0x18, 0x24, 0x24, 0x18, 0xFC, 0x7C, 0x08, 0x04, 0x04, 0x08, 0x48, 0x54, 0x54, 0x54, 0x24, 0x04, 0x04, 0x3F, 0x44, 0x24, 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x3C, 0x40, 0x30, 0x40, 0x3C,\n    0x44, 0x28, 0x10, 0x28, 0x44, 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x41, 0x36, 0x08, 0x00, 0x02, 0x01, 0x02, 0x04, 0x02, 0x3C, 0x26, 0x23, 0x26, 0x3C, 0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x3A, 0x40, 0x40, 0x20, 0x7A, 0x38, 0x54, 0x54, 0x55, 0x59, 0x21, 0x55, 0x55, 0x79, 0x41, 0x22, 0x54, 0x54, 0x78, 0x42,                                                                                                                                                                                                                                                                                                             // a-umlaut\n    0x21, 0x55, 0x54, 0x78, 0x40, 0x20, 0x54, 0x55, 0x79, 0x40, 0x0C, 0x1E, 0x52, 0x72, 0x12, 0x39, 0x55, 0x55, 0x55, 0x59, 0x39, 0x54, 0x54, 0x54, 0x59, 0x39, 0x55, 0x54, 0x54, 0x58, 0x00, 0x00, 0x45, 0x7C, 0x41, 0x00, 0x02, 0x45, 0x7D, 0x42, 0x00, 0x01, 0x45, 0x7C, 0x40, 0x7D, 0x12, 0x11, 0x12, 0x7D,                                                                                                                                                                                                                                                                                                                                                                                                       // A-umlaut\n    0xF0, 0x28, 0x25, 0x28, 0xF0, 0x7C, 0x54, 0x55, 0x45, 0x00, 0x20, 0x54, 0x54, 0x7C, 0x54, 0x7C, 0x0A, 0x09, 0x7F, 0x49, 0x32, 0x49, 0x49, 0x49, 0x32, 0x3A, 0x44, 0x44, 0x44, 0x3A,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               // o-umlaut\n    0x32, 0x4A, 0x48, 0x48, 0x30, 0x3A, 0x41, 0x41, 0x21, 0x7A, 0x3A, 0x42, 0x40, 0x20, 0x78, 0x00, 0x9D, 0xA0, 0xA0, 0x7D, 0x3D, 0x42, 0x42, 0x42, 0x3D,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             // O-umlaut\n    0x3D, 0x40, 0x40, 0x40, 0x3D, 0x3C, 0x24, 0xFF, 0x24, 0x24, 0x48, 0x7E, 0x49, 0x43, 0x66, 0x2B, 0x2F, 0xFC, 0x2F, 0x2B, 0xFF, 0x09, 0x29, 0xF6, 0x20, 0xC0, 0x88, 0x7E, 0x09, 0x03, 0x20, 0x54, 0x54, 0x79, 0x41, 0x00, 0x00, 0x44, 0x7D, 0x41, 0x30, 0x48, 0x48, 0x4A, 0x32, 0x38, 0x40, 0x40, 0x22, 0x7A, 0x00, 0x7A, 0x0A, 0x0A, 0x72, 0x7D, 0x0D, 0x19, 0x31, 0x7D, 0x26, 0x29, 0x29, 0x2F, 0x28, 0x26, 0x29, 0x29, 0x29, 0x26, 0x30, 0x48, 0x4D, 0x40, 0x20, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x2F, 0x10, 0xC8, 0xAC, 0xBA, 0x2F, 0x10, 0x28, 0x34, 0xFA, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x08, 0x14, 0x2A, 0x14, 0x22, 0x22, 0x14, 0x2A, 0x14, 0x08, 0x55, 0x00, 0x55, 0x00, 0x55, // #176 (25% block) missing in old code\n    0xAA, 0x55, 0xAA, 0x55, 0xAA,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     // 50% block\n    0xFF, 0x55, 0xFF, 0x55, 0xFF,                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     // 75% block\n    0x00, 0x00, 0x00, 0xFF, 0x00, 0x10, 0x10, 0x10, 0xFF, 0x00, 0x14, 0x14, 0x14, 0xFF, 0x00, 0x10, 0x10, 0xFF, 0x00, 0xFF, 0x10, 0x10, 0xF0, 0x10, 0xF0, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x14, 0x14, 0x17, 0x10, 0x1F, 0x10, 0x10, 0x1F, 0x10, 0x1F, 0x14, 0x14, 0x14, 0x1F, 0x00, 0x10, 0x10, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, 0x1F, 0x10, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x00, 0x00, 0x00, 0xFF, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0xFF, 0x10, 0x00, 0x00, 0x00, 0xFF, 0x14, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x1F, 0x10, 0x17, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14, 0x14, 0x17, 0x14, 0x10, 0x10, 0x1F, 0x10, 0x1F,\n    0x14, 0x14, 0x14, 0xF4, 0x14, 0x10, 0x10, 0xF0, 0x10, 0xF0, 0x00, 0x00, 0x1F, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x1F, 0x14, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x00, 0x00, 0xF0, 0x10, 0xF0, 0x10, 0x10, 0xFF, 0x10, 0xFF, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x10, 0x10, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x10, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x38, 0x44, 0x44, 0x38, 0x44, 0xFC, 0x4A, 0x4A, 0x4A, 0x34,                                                                                                                                                                                                                                                                                                                                                                                                      // sharp-s or beta\n    0x7E, 0x02, 0x02, 0x06, 0x06, 0x02, 0x7E, 0x02, 0x7E, 0x02, 0x63, 0x55, 0x49, 0x41, 0x63, 0x38, 0x44, 0x44, 0x3C, 0x04, 0x40, 0x7E, 0x20, 0x1E, 0x20, 0x06, 0x02, 0x7E, 0x02, 0x02, 0x99, 0xA5, 0xE7, 0xA5, 0x99, 0x1C, 0x2A, 0x49, 0x2A, 0x1C, 0x4C, 0x72, 0x01, 0x72, 0x4C, 0x30, 0x4A, 0x4D, 0x4D, 0x30, 0x30, 0x48, 0x78, 0x48, 0x30, 0xBC, 0x62, 0x5A, 0x46, 0x3D, 0x3E, 0x49, 0x49, 0x49, 0x00, 0x7E, 0x01, 0x01, 0x01, 0x7E, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x44, 0x44, 0x5F, 0x44, 0x44, 0x40, 0x51, 0x4A, 0x44, 0x40, 0x40, 0x44, 0x4A, 0x51, 0x40, 0x00, 0x00, 0xFF, 0x01, 0x03, 0xE0, 0x80, 0xFF, 0x00, 0x00, 0x08, 0x08, 0x6B, 0x6B, 0x08, 0x36, 0x12, 0x36, 0x24, 0x36, 0x06, 0x0F, 0x09, 0x0F, 0x06, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x30, 0x40, 0xFF, 0x01, 0x01, 0x00, 0x1F, 0x01, 0x01, 0x1E, 0x00, 0x19, 0x1D, 0x17, 0x12, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00 // #255 NBSP\n};\n"
  },
  {
    "path": "platforms/avr/drivers/i2c_master.c",
    "content": "/*  Copyright (C) 2019 Elia Ritterbusch\n +\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n/* Library made by: g4lvanix\n * GitHub repository: https://github.com/g4lvanix/I2C-master-lib\n */\n\n#include <avr/io.h>\n#include <util/twi.h>\n\n#include \"i2c_master.h\"\n#include \"timer.h\"\n#include \"wait.h\"\n#include \"util.h\"\n#include \"progmem.h\"\n\n#ifndef F_SCL\n#    define F_SCL 400000UL // SCL frequency\n#endif\n\n#ifndef I2C_START_RETRY_COUNT\n#    define I2C_START_RETRY_COUNT 20\n#endif // I2C_START_RETRY_COUNT\n\n#define I2C_ACTION_READ 0x01\n#define I2C_ACTION_WRITE 0x00\n\n#define TWBR_val (((F_CPU / F_SCL) - 16) / 2)\n\n__attribute__((weak)) void i2c_init(void) {\n    TWSR = 0; /* no prescaler */\n    TWBR = (uint8_t)TWBR_val;\n\n#ifdef __AVR_ATmega32A__\n    // set pull-up resistors on I2C bus pins\n    PORTC |= 0b11;\n\n    // enable TWI (two-wire interface)\n    TWCR |= (1 << TWEN);\n\n    // enable TWI interrupt and slave address ACK\n    TWCR |= (1 << TWIE);\n    TWCR |= (1 << TWEA);\n#endif\n}\n\nstatic i2c_status_t i2c_start_impl(uint8_t address, uint16_t timeout) {\n    // reset TWI control register\n    TWCR = 0;\n    // transmit START condition\n    TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);\n\n    uint16_t timeout_timer = timer_read();\n    while (!(TWCR & (1 << TWINT))) {\n        if ((timeout != I2C_TIMEOUT_INFINITE) && (timer_elapsed(timeout_timer) > timeout)) {\n            return I2C_STATUS_TIMEOUT;\n        }\n    }\n\n    // check if the start condition was successfully transmitted\n    if (((TW_STATUS & 0xF8) != TW_START) && ((TW_STATUS & 0xF8) != TW_REP_START)) {\n        return I2C_STATUS_ERROR;\n    }\n\n    // load slave address into data register\n    TWDR = address;\n    // start transmission of address\n    TWCR = (1 << TWINT) | (1 << TWEN);\n\n    timeout_timer = timer_read();\n    while (!(TWCR & (1 << TWINT))) {\n        if ((timeout != I2C_TIMEOUT_INFINITE) && (timer_elapsed(timeout_timer) > timeout)) {\n            return I2C_STATUS_TIMEOUT;\n        }\n    }\n\n    // check if the device has acknowledged the READ / WRITE mode\n    uint8_t twst = TW_STATUS & 0xF8;\n    if ((twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK)) {\n        return I2C_STATUS_ERROR;\n    }\n\n    return I2C_STATUS_SUCCESS;\n}\n\n__attribute__((always_inline)) static inline i2c_status_t i2c_start(uint8_t address, uint16_t timeout) {\n    // Retry i2c_start_impl a bunch times in case the remote side has interrupts disabled.\n    uint16_t     timeout_timer = timer_read();\n    uint16_t     time_slice    = MAX(1, (timeout == (I2C_TIMEOUT_INFINITE)) ? 5 : (timeout / (I2C_START_RETRY_COUNT))); // if it's infinite, wait 1ms between attempts, otherwise split up the entire timeout into the number of retries\n    i2c_status_t status;\n    do {\n        status = i2c_start_impl(address, time_slice);\n    } while ((status < 0) && ((timeout == I2C_TIMEOUT_INFINITE) || (timer_elapsed(timeout_timer) <= timeout)));\n    return status;\n}\n\n__attribute__((always_inline)) static inline void i2c_stop(void) {\n    // transmit STOP condition\n    TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);\n}\n\ni2c_status_t i2c_write(uint8_t data, uint16_t timeout) {\n    // load data into data register\n    TWDR = data;\n    // start transmission of data\n    TWCR = (1 << TWINT) | (1 << TWEN);\n\n    uint16_t timeout_timer = timer_read();\n    while (!(TWCR & (1 << TWINT))) {\n        if ((timeout != I2C_TIMEOUT_INFINITE) && (timer_elapsed(timeout_timer) > timeout)) {\n            return I2C_STATUS_TIMEOUT;\n        }\n    }\n\n    if ((TW_STATUS & 0xF8) != TW_MT_DATA_ACK) {\n        return I2C_STATUS_ERROR;\n    }\n\n    return I2C_STATUS_SUCCESS;\n}\n\nint16_t i2c_read_ack(uint16_t timeout) {\n    // start TWI module and acknowledge data after reception\n    TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);\n\n    uint16_t timeout_timer = timer_read();\n    while (!(TWCR & (1 << TWINT))) {\n        if ((timeout != I2C_TIMEOUT_INFINITE) && (timer_elapsed(timeout_timer) > timeout)) {\n            return I2C_STATUS_TIMEOUT;\n        }\n    }\n\n    // return received data from TWDR\n    return TWDR;\n}\n\nint16_t i2c_read_nack(uint16_t timeout) {\n    // start receiving without acknowledging reception\n    TWCR = (1 << TWINT) | (1 << TWEN);\n\n    uint16_t timeout_timer = timer_read();\n    while (!(TWCR & (1 << TWINT))) {\n        if ((timeout != I2C_TIMEOUT_INFINITE) && (timer_elapsed(timeout_timer) > timeout)) {\n            return I2C_STATUS_TIMEOUT;\n        }\n    }\n\n    // return received data from TWDR\n    return TWDR;\n}\n\ni2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2c_status_t status = i2c_start(address | I2C_ACTION_WRITE, timeout);\n\n    for (uint16_t i = 0; i < length && status >= 0; i++) {\n        status = i2c_write(data[i], timeout);\n    }\n\n    i2c_stop();\n\n    return status;\n}\n\ni2c_status_t i2c_transmit_P(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2c_status_t status = i2c_start(address | I2C_ACTION_WRITE, timeout);\n\n    for (uint16_t i = 0; i < length && status >= 0; i++) {\n        status = i2c_write(pgm_read_byte((const char*)data++), timeout);\n    }\n\n    i2c_stop();\n\n    return status;\n}\n\ni2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2c_status_t status = i2c_start(address | I2C_ACTION_READ, timeout);\n\n    for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {\n        status = i2c_read_ack(timeout);\n        if (status >= 0) {\n            data[i] = status;\n        }\n    }\n\n    if (status >= 0) {\n        status = i2c_read_nack(timeout);\n        if (status >= 0) {\n            data[(length - 1)] = status;\n        }\n    }\n\n    i2c_stop();\n\n    return (status < 0) ? status : I2C_STATUS_SUCCESS;\n}\n\ni2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2c_status_t status = i2c_start(devaddr | 0x00, timeout);\n    if (status >= 0) {\n        status = i2c_write(regaddr, timeout);\n\n        for (uint16_t i = 0; i < length && status >= 0; i++) {\n            status = i2c_write(data[i], timeout);\n        }\n    }\n\n    i2c_stop();\n\n    return status;\n}\n\ni2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2c_status_t status = i2c_start(devaddr | 0x00, timeout);\n    if (status >= 0) {\n        status = i2c_write(regaddr >> 8, timeout);\n\n        if (status >= 0) {\n            status = i2c_write(regaddr & 0xFF, timeout);\n\n            for (uint16_t i = 0; i < length && status >= 0; i++) {\n                status = i2c_write(data[i], timeout);\n            }\n        }\n    }\n\n    i2c_stop();\n\n    return status;\n}\n\ni2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2c_status_t status = i2c_start(devaddr, timeout);\n    if (status < 0) {\n        goto error;\n    }\n\n    status = i2c_write(regaddr, timeout);\n    if (status < 0) {\n        goto error;\n    }\n\n    status = i2c_start(devaddr | 0x01, timeout);\n\n    for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {\n        status = i2c_read_ack(timeout);\n        if (status >= 0) {\n            data[i] = status;\n        }\n    }\n\n    if (status >= 0) {\n        status = i2c_read_nack(timeout);\n        if (status >= 0) {\n            data[(length - 1)] = status;\n        }\n    }\n\nerror:\n    i2c_stop();\n\n    return (status < 0) ? status : I2C_STATUS_SUCCESS;\n}\n\ni2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2c_status_t status = i2c_start(devaddr, timeout);\n    if (status < 0) {\n        goto error;\n    }\n\n    status = i2c_write(regaddr >> 8, timeout);\n    if (status < 0) {\n        goto error;\n    }\n    status = i2c_write(regaddr & 0xFF, timeout);\n    if (status < 0) {\n        goto error;\n    }\n\n    status = i2c_start(devaddr | 0x01, timeout);\n\n    for (uint16_t i = 0; i < (length - 1) && status >= 0; i++) {\n        status = i2c_read_ack(timeout);\n        if (status >= 0) {\n            data[i] = status;\n        }\n    }\n\n    if (status >= 0) {\n        status = i2c_read_nack(timeout);\n        if (status >= 0) {\n            data[(length - 1)] = status;\n        }\n    }\n\nerror:\n    i2c_stop();\n\n    return (status < 0) ? status : I2C_STATUS_SUCCESS;\n}\n\n__attribute__((weak)) i2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout) {\n    i2c_status_t status = i2c_start(address, timeout);\n    i2c_stop();\n    return status;\n}\n"
  },
  {
    "path": "platforms/avr/drivers/i2c_slave.c",
    "content": "/*  Copyright (C) 2019 Elia Ritterbusch\n +\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n/* Library made by: g4lvanix\n * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib\n */\n\n#include <stddef.h>\n#include <avr/io.h>\n#include <util/twi.h>\n#include <avr/interrupt.h>\n#include <stdbool.h>\n\n#include \"i2c_slave.h\"\n\n#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)\n#    include \"transactions.h\"\n\nstatic volatile bool is_callback_executor = false;\n#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)\n\nvolatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];\n\nstatic volatile uint8_t buffer_address;\nstatic volatile bool    slave_has_register_set = false;\n\nvoid i2c_slave_init(uint8_t address) {\n    // load address into TWI address register\n    TWAR = address;\n    // set the TWCR to enable address matching and enable TWI, clear TWINT, enable TWI interrupt\n    TWCR = (1 << TWIE) | (1 << TWEA) | (1 << TWINT) | (1 << TWEN);\n}\n\nvoid i2c_slave_stop(void) {\n    // clear acknowledge and enable bits\n    TWCR &= ~((1 << TWEA) | (1 << TWEN));\n}\n\nISR(TWI_vect) {\n    uint8_t ack = 1;\n\n    switch (TW_STATUS) {\n        case TW_SR_SLA_ACK:\n            // The device is now a slave receiver\n            slave_has_register_set = false;\n#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)\n            is_callback_executor = false;\n#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)\n            break;\n\n        case TW_SR_DATA_ACK:\n            // This device is a slave receiver and has received data\n            // First byte is the location then the bytes will be writen in buffer with auto-increment\n            if (!slave_has_register_set) {\n                buffer_address = TWDR;\n\n                if (buffer_address >= I2C_SLAVE_REG_COUNT) { // address out of bounds dont ack\n                    ack            = 0;\n                    buffer_address = 0;\n                }\n                slave_has_register_set = true; // address has been received now fill in buffer\n\n#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)\n                // Work out if we're attempting to execute a callback\n                is_callback_executor = buffer_address == split_transaction_table[I2C_EXECUTE_CALLBACK].initiator2target_offset;\n#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)\n            } else {\n                i2c_slave_reg[buffer_address] = TWDR;\n                buffer_address++;\n\n#if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)\n                // If we're intending to execute a transaction callback, do so, as we've just received the transaction ID\n                if (is_callback_executor) {\n                    split_transaction_desc_t *trans = &split_transaction_table[split_shmem->transaction_id];\n                    if (trans->slave_callback) {\n                        trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));\n                    }\n                }\n#endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)\n            }\n            break;\n\n        case TW_ST_SLA_ACK:\n        case TW_ST_DATA_ACK:\n            // This device is a slave transmitter and master has requested data\n            TWDR = i2c_slave_reg[buffer_address];\n            buffer_address++;\n            break;\n\n        case TW_BUS_ERROR:\n            // We got an error, reset i2c\n            TWCR = 0;\n        default:\n            break;\n    }\n\n    // Reset i2c state machine to be ready for next interrupt\n    TWCR |= (1 << TWIE) | (1 << TWINT) | (ack << TWEA) | (1 << TWEN);\n}\n"
  },
  {
    "path": "platforms/avr/drivers/i2c_slave.h",
    "content": "/*  Copyright (C) 2019 Elia Ritterbusch\n +\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n/* Library made by: g4lvanix\n * GitHub repository: https://github.com/g4lvanix/I2C-slave-lib\n\n Info: Inititate the library by giving the required address.\n       Read or write to the necessary buffer according to the opperation.\n */\n\n#pragma once\n\n#include \"compiler_support.h\"\n\n#ifndef I2C_SLAVE_REG_COUNT\n\n#    if defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)\n#        include \"transport.h\"\n#        define I2C_SLAVE_REG_COUNT sizeof(split_shared_memory_t)\n#    else // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)\n#        define I2C_SLAVE_REG_COUNT 30\n#    endif // defined(USE_I2C) && defined(SPLIT_COMMON_TRANSACTIONS)\n\n#endif // I2C_SLAVE_REG_COUNT\n\nSTATIC_ASSERT(I2C_SLAVE_REG_COUNT < 256, \"I2C target registers must be single byte\");\n\nextern volatile uint8_t i2c_slave_reg[I2C_SLAVE_REG_COUNT];\n\nvoid i2c_slave_init(uint8_t address);\nvoid i2c_slave_stop(void);\n"
  },
  {
    "path": "platforms/avr/drivers/ps2/ps2_io.c",
    "content": "#include <stdbool.h>\n#include \"ps2_io.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n/* Check port settings for clock and data line */\n#if !(defined(PS2_CLOCK_PIN))\n#    error \"PS/2 clock setting is required in config.h\"\n#endif\n\n#if !(defined(PS2_DATA_PIN))\n#    error \"PS/2 data setting is required in config.h\"\n#endif\n\n/*\n * Clock\n */\nvoid clock_init(void) {}\n\nvoid clock_lo(void) {\n    // Transition from input with pull-up to output low via Hi-Z instead of output high\n    gpio_write_pin_low(PS2_CLOCK_PIN);\n    gpio_set_pin_output(PS2_CLOCK_PIN);\n}\n\nvoid clock_hi(void) {\n    gpio_set_pin_input_high(PS2_CLOCK_PIN);\n}\n\nbool clock_in(void) {\n    gpio_set_pin_input_high(PS2_CLOCK_PIN);\n    wait_us(1);\n    return gpio_read_pin(PS2_CLOCK_PIN);\n}\n\n/*\n * Data\n */\nvoid data_init(void) {}\n\nvoid data_lo(void) {\n    // Transition from input with pull-up to output low via Hi-Z instead of output high\n    gpio_write_pin_low(PS2_DATA_PIN);\n    gpio_set_pin_output(PS2_DATA_PIN);\n}\n\nvoid data_hi(void) {\n    gpio_set_pin_input_high(PS2_DATA_PIN);\n}\n\nbool data_in(void) {\n    gpio_set_pin_input_high(PS2_DATA_PIN);\n    wait_us(1);\n    return gpio_read_pin(PS2_DATA_PIN);\n}\n"
  },
  {
    "path": "platforms/avr/drivers/ps2/ps2_usart.c",
    "content": "/*\nCopyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>\n\nThis software is licensed with a Modified BSD License.\nAll of this is supposed to be Free Software, Open Source, DFSG-free,\nGPL-compatible, and OK to use in both free and proprietary applications.\nAdditions and corrections to this file are welcome.\n\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright\n  notice, this list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright\n  notice, this list of conditions and the following disclaimer in\n  the documentation and/or other materials provided with the\n  distribution.\n\n* Neither the name of the copyright holders nor the names of\n  contributors may be used to endorse or promote products derived\n  from this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.\n*/\n\n/*\n * PS/2 protocol USART version\n */\n\n#include <stdbool.h>\n#include <avr/interrupt.h>\n#include <util/delay.h>\n#include \"gpio.h\"\n#include \"ps2.h\"\n#include \"ps2_io.h\"\n#include \"print.h\"\n\n#ifndef PS2_CLOCK_DDR\n#    define PS2_CLOCK_DDR PORTx_ADDRESS(PS2_CLOCK_PIN)\n#endif\n#ifndef PS2_CLOCK_BIT\n#    define PS2_CLOCK_BIT (PS2_CLOCK_PIN & 0xF)\n#endif\n#ifndef PS2_DATA_DDR\n#    define PS2_DATA_DDR PORTx_ADDRESS(PS2_DATA_PIN)\n#endif\n#ifndef PS2_DATA_BIT\n#    define PS2_DATA_BIT (PS2_DATA_PIN & 0xF)\n#endif\n\n#define WAIT(stat, us, err)     \\\n    do {                        \\\n        if (!wait_##stat(us)) { \\\n            ps2_error = err;    \\\n            goto ERROR;         \\\n        }                       \\\n    } while (0)\n\nuint8_t ps2_error = PS2_ERR_NONE;\n\nstatic inline uint8_t pbuf_dequeue(void);\nstatic inline void    pbuf_enqueue(uint8_t data);\nstatic inline void    pbuf_clear(void);\nbool                  pbuf_has_data(void);\n\nvoid ps2_host_init(void) {\n    idle(); // without this many USART errors occur when cable is disconnected\n    PS2_USART_INIT();\n    PS2_USART_RX_INT_ON();\n    // POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)\n    //_delay_ms(2500);\n}\n\nuint8_t ps2_host_send(uint8_t data) {\n    bool parity = true;\n    ps2_error   = PS2_ERR_NONE;\n\n    PS2_USART_OFF();\n\n    /* terminate a transmission if we have */\n    inhibit();\n    _delay_us(100); // [4]p.13\n\n    /* 'Request to Send' and Start bit */\n    data_lo();\n    clock_hi();\n    WAIT(clock_lo, 10000, 10); // 10ms [5]p.50\n\n    /* Data bit[2-9] */\n    for (uint8_t i = 0; i < 8; i++) {\n        _delay_us(15);\n        if (data & (1 << i)) {\n            parity = !parity;\n            data_hi();\n        } else {\n            data_lo();\n        }\n        WAIT(clock_hi, 50, 2);\n        WAIT(clock_lo, 50, 3);\n    }\n\n    /* Parity bit */\n    _delay_us(15);\n    if (parity) {\n        data_hi();\n    } else {\n        data_lo();\n    }\n    WAIT(clock_hi, 50, 4);\n    WAIT(clock_lo, 50, 5);\n\n    /* Stop bit */\n    _delay_us(15);\n    data_hi();\n\n    /* Ack */\n    WAIT(data_lo, 50, 6);\n    WAIT(clock_lo, 50, 7);\n\n    /* wait for idle state */\n    WAIT(clock_hi, 50, 8);\n    WAIT(data_hi, 50, 9);\n\n    idle();\n    PS2_USART_INIT();\n    PS2_USART_RX_INT_ON();\n    return ps2_host_recv_response();\nERROR:\n    idle();\n    PS2_USART_INIT();\n    PS2_USART_RX_INT_ON();\n    return 0;\n}\n\nuint8_t ps2_host_recv_response(void) {\n    // Command may take 25ms/20ms at most([5]p.46, [3]p.21)\n    uint8_t retry = 25;\n    while (retry-- && !pbuf_has_data()) {\n        _delay_ms(1);\n    }\n    return pbuf_dequeue();\n}\n\nuint8_t ps2_host_recv(void) {\n    if (pbuf_has_data()) {\n        ps2_error = PS2_ERR_NONE;\n        return pbuf_dequeue();\n    } else {\n        ps2_error = PS2_ERR_NODATA;\n        return 0;\n    }\n}\n\nISR(PS2_USART_RX_VECT) {\n    // TODO: request RESEND when error occurs?\n    uint8_t error = PS2_USART_ERROR; // USART error should be read before data\n    uint8_t data  = PS2_USART_RX_DATA;\n    if (!error) {\n        pbuf_enqueue(data);\n    } else {\n        xprintf(\"PS2 USART error: %02X data: %02X\\n\", error, data);\n    }\n}\n\n/* send LED state to keyboard */\nvoid ps2_host_set_led(uint8_t led) {\n    ps2_host_send(0xED);\n    ps2_host_send(led);\n}\n\n/*--------------------------------------------------------------------\n * Ring buffer to store scan codes from keyboard\n *------------------------------------------------------------------*/\n#define PBUF_SIZE 32\nstatic uint8_t     pbuf[PBUF_SIZE];\nstatic uint8_t     pbuf_head = 0;\nstatic uint8_t     pbuf_tail = 0;\nstatic inline void pbuf_enqueue(uint8_t data) {\n    uint8_t sreg = SREG;\n    cli();\n    uint8_t next = (pbuf_head + 1) % PBUF_SIZE;\n    if (next != pbuf_tail) {\n        pbuf[pbuf_head] = data;\n        pbuf_head       = next;\n    } else {\n        print(\"pbuf: full\\n\");\n    }\n    SREG = sreg;\n}\nstatic inline uint8_t pbuf_dequeue(void) {\n    uint8_t val = 0;\n\n    uint8_t sreg = SREG;\n    cli();\n    if (pbuf_head != pbuf_tail) {\n        val       = pbuf[pbuf_tail];\n        pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE;\n    }\n    SREG = sreg;\n\n    return val;\n}\nbool pbuf_has_data(void) {\n    uint8_t sreg = SREG;\n    cli();\n    bool has_data = (pbuf_head != pbuf_tail);\n    SREG          = sreg;\n    return has_data;\n}\nstatic inline void pbuf_clear(void) {\n    uint8_t sreg = SREG;\n    cli();\n    pbuf_head = pbuf_tail = 0;\n    SREG                  = sreg;\n}\n"
  },
  {
    "path": "platforms/avr/drivers/serial.c",
    "content": "/*\n * WARNING: be careful changing this code, it is very timing dependent\n *\n * 2018-10-28 checked\n *  avr-gcc 4.9.2\n *  avr-gcc 5.4.0\n *  avr-gcc 7.3.0\n */\n\n#ifndef F_CPU\n#    define F_CPU 16000000\n#endif\n\n#include <avr/io.h>\n#include <avr/interrupt.h>\n#include <util/delay.h>\n#include <stddef.h>\n#include <stdbool.h>\n#include \"gpio.h\"\n#include \"serial.h\"\n\n#ifdef SOFT_SERIAL_PIN\n\n#    if !(defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))\n#        error serial.c is not supported for the currently selected MCU\n#    endif\n// if using ATmega32U4/2, AT90USBxxx I2C, can not use PD0 and PD1 in soft serial.\n#    if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)\n#        if defined(USE_AVR_I2C) && (SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1)\n#            error Using I2C, so can not use PD0, PD1\n#        endif\n#    endif\n// PD0..PD3, common config\n#    if SOFT_SERIAL_PIN == D0\n#        define EIMSK_BIT _BV(INT0)\n#        define EICRx_BIT (~(_BV(ISC00) | _BV(ISC01)))\n#        define SERIAL_PIN_INTERRUPT INT0_vect\n#        define EICRx EICRA\n#    elif SOFT_SERIAL_PIN == D1\n#        define EIMSK_BIT _BV(INT1)\n#        define EICRx_BIT (~(_BV(ISC10) | _BV(ISC11)))\n#        define SERIAL_PIN_INTERRUPT INT1_vect\n#        define EICRx EICRA\n#    elif SOFT_SERIAL_PIN == D2\n#        define EIMSK_BIT _BV(INT2)\n#        define EICRx_BIT (~(_BV(ISC20) | _BV(ISC21)))\n#        define SERIAL_PIN_INTERRUPT INT2_vect\n#        define EICRx EICRA\n#    elif SOFT_SERIAL_PIN == D3\n#        define EIMSK_BIT _BV(INT3)\n#        define EICRx_BIT (~(_BV(ISC30) | _BV(ISC31)))\n#        define SERIAL_PIN_INTERRUPT INT3_vect\n#        define EICRx EICRA\n#    endif\n\n// ATmegaxxU2/AT90USB162 specific config\n#    if defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_AT90USB162__)\n// PD4(INT5), PD6(INT6), PD7(INT7), PC7(INT4)\n#        if SOFT_SERIAL_PIN == D4\n#            define EIMSK_BIT _BV(INT5)\n#            define EICRx_BIT (~(_BV(ISC50) | _BV(ISC51)))\n#            define SERIAL_PIN_INTERRUPT INT5_vect\n#            define EICRx EICRB\n#        elif SOFT_SERIAL_PIN == D6\n#            define EIMSK_BIT _BV(INT6)\n#            define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))\n#            define SERIAL_PIN_INTERRUPT INT6_vect\n#            define EICRx EICRB\n#        elif SOFT_SERIAL_PIN == D7\n#            define EIMSK_BIT _BV(INT7)\n#            define EICRx_BIT (~(_BV(ISC70) | _BV(ISC71)))\n#            define SERIAL_PIN_INTERRUPT INT7_vect\n#            define EICRx EICRB\n#        elif SOFT_SERIAL_PIN == C7\n#            define EIMSK_BIT _BV(INT4)\n#            define EICRx_BIT (~(_BV(ISC40) | _BV(ISC41)))\n#            define SERIAL_PIN_INTERRUPT INT4_vect\n#            define EICRx EICRB\n#        endif\n#    endif\n\n// ATmegaxxU4 specific config\n#    if defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)\n// PE6(INT6)\n#        if SOFT_SERIAL_PIN == E6\n#            define EIMSK_BIT _BV(INT6)\n#            define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))\n#            define SERIAL_PIN_INTERRUPT INT6_vect\n#            define EICRx EICRB\n#        endif\n#    endif\n\n// AT90USBxxx specific config\n#    if defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)\n// PE4..PE7(INT4..INT7)\n#        if SOFT_SERIAL_PIN == E4\n#            define EIMSK_BIT _BV(INT4)\n#            define EICRx_BIT (~(_BV(ISC40) | _BV(ISC41)))\n#            define SERIAL_PIN_INTERRUPT INT4_vect\n#            define EICRx EICRB\n#        elif SOFT_SERIAL_PIN == E5\n#            define EIMSK_BIT _BV(INT5)\n#            define EICRx_BIT (~(_BV(ISC50) | _BV(ISC51)))\n#            define SERIAL_PIN_INTERRUPT INT5_vect\n#            define EICRx EICRB\n#        elif SOFT_SERIAL_PIN == E6\n#            define EIMSK_BIT _BV(INT6)\n#            define EICRx_BIT (~(_BV(ISC60) | _BV(ISC61)))\n#            define SERIAL_PIN_INTERRUPT INT6_vect\n#            define EICRx EICRB\n#        elif SOFT_SERIAL_PIN == E7\n#            define EIMSK_BIT _BV(INT7)\n#            define EICRx_BIT (~(_BV(ISC70) | _BV(ISC71)))\n#            define SERIAL_PIN_INTERRUPT INT7_vect\n#            define EICRx EICRB\n#        endif\n#    endif\n\n#    ifndef SERIAL_PIN_INTERRUPT\n#        error invalid SOFT_SERIAL_PIN value\n#    endif\n\n#    define ALWAYS_INLINE __attribute__((always_inline))\n#    define NO_INLINE __attribute__((noinline))\n#    define _delay_sub_us(x) __builtin_avr_delay_cycles(x)\n\n// parity check\n#    define ODD_PARITY 1\n#    define EVEN_PARITY 0\n#    define PARITY EVEN_PARITY\n\n#    ifdef SERIAL_DELAY\n// custom setup in config.h\n// #define TID_SEND_ADJUST 2\n// #define SERIAL_DELAY 6             // micro sec\n// #define READ_WRITE_START_ADJUST 30 // cycles\n// #define READ_WRITE_WIDTH_ADJUST 8 // cycles\n#    else\n// ============ Standard setups ============\n\n#        ifndef SELECT_SOFT_SERIAL_SPEED\n#            define SELECT_SOFT_SERIAL_SPEED 1\n//  0: about 189kbps (Experimental only)\n//  1: about 137kbps (default)\n//  2: about 75kbps\n//  3: about 39kbps\n//  4: about 26kbps\n//  5: about 20kbps\n#        endif\n\n#        if __GNUC__ < 6\n#            define TID_SEND_ADJUST 14\n#        else\n#            define TID_SEND_ADJUST 2\n#        endif\n\n#        if SELECT_SOFT_SERIAL_SPEED == 0\n// Very High speed\n#            define SERIAL_DELAY 4 // micro sec\n#            if __GNUC__ < 6\n#                define READ_WRITE_START_ADJUST 33 // cycles\n#                define READ_WRITE_WIDTH_ADJUST 3  // cycles\n#            else\n#                define READ_WRITE_START_ADJUST 34 // cycles\n#                define READ_WRITE_WIDTH_ADJUST 7  // cycles\n#            endif\n#        elif SELECT_SOFT_SERIAL_SPEED == 1\n// High speed\n#            define SERIAL_DELAY 6 // micro sec\n#            if __GNUC__ < 6\n#                define READ_WRITE_START_ADJUST 30 // cycles\n#                define READ_WRITE_WIDTH_ADJUST 3  // cycles\n#            else\n#                define READ_WRITE_START_ADJUST 33 // cycles\n#                define READ_WRITE_WIDTH_ADJUST 7  // cycles\n#            endif\n#        elif SELECT_SOFT_SERIAL_SPEED == 2\n// Middle speed\n#            define SERIAL_DELAY 12            // micro sec\n#            define READ_WRITE_START_ADJUST 30 // cycles\n#            if __GNUC__ < 6\n#                define READ_WRITE_WIDTH_ADJUST 3 // cycles\n#            else\n#                define READ_WRITE_WIDTH_ADJUST 7 // cycles\n#            endif\n#        elif SELECT_SOFT_SERIAL_SPEED == 3\n// Low speed\n#            define SERIAL_DELAY 24            // micro sec\n#            define READ_WRITE_START_ADJUST 30 // cycles\n#            if __GNUC__ < 6\n#                define READ_WRITE_WIDTH_ADJUST 3 // cycles\n#            else\n#                define READ_WRITE_WIDTH_ADJUST 7 // cycles\n#            endif\n#        elif SELECT_SOFT_SERIAL_SPEED == 4\n// Very Low speed\n#            define SERIAL_DELAY 36            // micro sec\n#            define READ_WRITE_START_ADJUST 30 // cycles\n#            if __GNUC__ < 6\n#                define READ_WRITE_WIDTH_ADJUST 3 // cycles\n#            else\n#                define READ_WRITE_WIDTH_ADJUST 7 // cycles\n#            endif\n#        elif SELECT_SOFT_SERIAL_SPEED == 5\n// Ultra Low speed\n#            define SERIAL_DELAY 48            // micro sec\n#            define READ_WRITE_START_ADJUST 30 // cycles\n#            if __GNUC__ < 6\n#                define READ_WRITE_WIDTH_ADJUST 3 // cycles\n#            else\n#                define READ_WRITE_WIDTH_ADJUST 7 // cycles\n#            endif\n#        else\n#            error invalid SELECT_SOFT_SERIAL_SPEED value\n#        endif /* SELECT_SOFT_SERIAL_SPEED */\n#    endif     /* SERIAL_DELAY */\n\n#    define SERIAL_DELAY_HALF1 (SERIAL_DELAY / 2)\n#    define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY / 2)\n\n#    define SLAVE_INT_WIDTH_US 1\n#    define SLAVE_INT_ACK_WIDTH_UNIT 2\n#    define SLAVE_INT_ACK_WIDTH 4\n\ninline static void serial_delay(void) ALWAYS_INLINE;\ninline static void serial_delay(void) {\n    _delay_us(SERIAL_DELAY);\n}\n\ninline static void serial_delay_half1(void) ALWAYS_INLINE;\ninline static void serial_delay_half1(void) {\n    _delay_us(SERIAL_DELAY_HALF1);\n}\n\ninline static void serial_delay_half2(void) ALWAYS_INLINE;\ninline static void serial_delay_half2(void) {\n    _delay_us(SERIAL_DELAY_HALF2);\n}\n\ninline static void serial_output(void) ALWAYS_INLINE;\ninline static void serial_output(void) {\n    gpio_set_pin_output(SOFT_SERIAL_PIN);\n}\n\n// make the serial pin an input with pull-up resistor\ninline static void serial_input_with_pullup(void) ALWAYS_INLINE;\ninline static void serial_input_with_pullup(void) {\n    gpio_set_pin_input_high(SOFT_SERIAL_PIN);\n}\n\ninline static uint8_t serial_read_pin(void) ALWAYS_INLINE;\ninline static uint8_t serial_read_pin(void) {\n    return !!gpio_read_pin(SOFT_SERIAL_PIN);\n}\n\ninline static void serial_low(void) ALWAYS_INLINE;\ninline static void serial_low(void) {\n    gpio_write_pin_low(SOFT_SERIAL_PIN);\n}\n\ninline static void serial_high(void) ALWAYS_INLINE;\ninline static void serial_high(void) {\n    gpio_write_pin_high(SOFT_SERIAL_PIN);\n}\n\nvoid soft_serial_initiator_init(void) {\n    serial_output();\n    serial_high();\n}\n\nvoid soft_serial_target_init(void) {\n    serial_input_with_pullup();\n\n    // Enable INT0-INT7\n    EIMSK |= EIMSK_BIT;\n    EICRx &= EICRx_BIT;\n}\n\n// Used by the sender to synchronize timing with the reciver.\nstatic void sync_recv(void) NO_INLINE;\nstatic void sync_recv(void) {\n    for (uint8_t i = 0; i < SERIAL_DELAY * 5 && serial_read_pin(); i++) {\n    }\n    // This shouldn't hang if the target disconnects because the\n    // serial line will float to high if the target does disconnect.\n    while (!serial_read_pin())\n        ;\n}\n\n// Used by the reciver to send a synchronization signal to the sender.\nstatic void sync_send(void) NO_INLINE;\nstatic void sync_send(void) {\n    serial_low();\n    serial_delay();\n    serial_high();\n}\n\n// Reads a byte from the serial line\nstatic uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE;\nstatic uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) {\n    uint8_t byte, i, p, pb;\n\n    _delay_sub_us(READ_WRITE_START_ADJUST);\n    for (i = 0, byte = 0, p = PARITY; i < bit; i++) {\n        serial_delay_half1(); // read the middle of pulses\n        if (serial_read_pin()) {\n            byte = (byte << 1) | 1;\n            p ^= 1;\n        } else {\n            byte = (byte << 1) | 0;\n            p ^= 0;\n        }\n        _delay_sub_us(READ_WRITE_WIDTH_ADJUST);\n        serial_delay_half2();\n    }\n    /* recive parity bit */\n    serial_delay_half1(); // read the middle of pulses\n    pb = serial_read_pin();\n    _delay_sub_us(READ_WRITE_WIDTH_ADJUST);\n    serial_delay_half2();\n\n    *pterrcount += (p != pb) ? 1 : 0;\n\n    return byte;\n}\n\n// Sends a byte with MSB ordering\nvoid serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE;\nvoid serial_write_chunk(uint8_t data, uint8_t bit) {\n    uint8_t b, p;\n    for (p = PARITY, b = 1 << (bit - 1); b; b >>= 1) {\n        if (data & b) {\n            serial_high();\n            p ^= 1;\n        } else {\n            serial_low();\n            p ^= 0;\n        }\n        serial_delay();\n    }\n    /* send parity bit */\n    if (p & 1) {\n        serial_high();\n    } else {\n        serial_low();\n    }\n    serial_delay();\n\n    serial_low(); // sync_send() / senc_recv() need raise edge\n}\n\nstatic void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE;\nstatic void serial_send_packet(uint8_t *buffer, uint8_t size) {\n    for (uint8_t i = 0; i < size; ++i) {\n        uint8_t data;\n        data = buffer[i];\n        sync_send();\n        serial_write_chunk(data, 8);\n    }\n}\n\nstatic uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE;\nstatic uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) {\n    uint8_t pecount = 0;\n    for (uint8_t i = 0; i < size; ++i) {\n        uint8_t data;\n        sync_recv();\n        data      = serial_read_chunk(&pecount, 8);\n        buffer[i] = data;\n    }\n    return pecount == 0;\n}\n\ninline static void change_sender2reciver(void) {\n    sync_send();                // 0\n    serial_delay_half1();       // 1\n    serial_low();               // 2\n    serial_input_with_pullup(); // 2\n    serial_delay_half1();       // 3\n}\n\ninline static void change_reciver2sender(void) {\n    sync_recv();          // 0\n    serial_delay();       // 1\n    serial_low();         // 3\n    serial_output();      // 3\n    serial_delay_half1(); // 4\n}\n\nstatic inline uint8_t nibble_bits_count(uint8_t bits) {\n    bits = (bits & 0x5) + (bits >> 1 & 0x5);\n    bits = (bits & 0x3) + (bits >> 2 & 0x3);\n    return bits;\n}\n\n// interrupt handle to be used by the target device\nISR(SERIAL_PIN_INTERRUPT) {\n    // recive transaction table index\n    uint8_t tid, bits;\n    uint8_t pecount = 0;\n    sync_recv();\n    bits = serial_read_chunk(&pecount, 8);\n    tid  = bits >> 3;\n    bits = (bits & 7) != (nibble_bits_count(tid) & 7);\n    if (bits || pecount > 0 || tid > NUM_TOTAL_TRANSACTIONS) {\n        return;\n    }\n    serial_delay_half1();\n\n    serial_high(); // response step1 low->high\n    serial_output();\n    _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT * SLAVE_INT_ACK_WIDTH);\n    split_transaction_desc_t *trans = &split_transaction_table[tid];\n    serial_low(); // response step2 ack high->low\n\n    // If the transaction has a callback, we can execute it now\n    if (trans->slave_callback) {\n        trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));\n    }\n\n    // target send phase\n    if (trans->target2initiator_buffer_size > 0) serial_send_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size);\n    // target switch to input\n    change_sender2reciver();\n\n    // target recive phase\n    if (trans->initiator2target_buffer_size > 0) {\n        serial_recive_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);\n    }\n\n    sync_recv(); // weit initiator output to high\n}\n\n/////////\n//  start transaction by initiator\n//\n// bool  soft_serial_transaction(int sstd_index)\n//\n// this code is very time dependent, so we need to disable interrupts\nbool soft_serial_transaction(int sstd_index) {\n    if (sstd_index > NUM_TOTAL_TRANSACTIONS) return false;\n    split_transaction_desc_t *trans = &split_transaction_table[sstd_index];\n\n    cli();\n\n    // signal to the target that we want to start a transaction\n    serial_output();\n    serial_low();\n    _delay_us(SLAVE_INT_WIDTH_US);\n\n    // send transaction table index\n    int tid = (sstd_index << 3) | (7 & nibble_bits_count(sstd_index));\n    sync_send();\n    _delay_sub_us(TID_SEND_ADJUST);\n    serial_write_chunk(tid, 8);\n    serial_delay_half1();\n\n    // wait for the target response (step1 low->high)\n    serial_input_with_pullup();\n    while (!serial_read_pin()) {\n        _delay_sub_us(2);\n    }\n\n    // check if the target is present (step2 high->low)\n    for (int i = 0; serial_read_pin(); i++) {\n        if (i > SLAVE_INT_ACK_WIDTH + 1) {\n            // slave failed to pull the line low, assume not present\n            serial_output();\n            serial_high();\n            sei();\n            return false;\n        }\n        _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);\n    }\n\n    // initiator recive phase\n    // if the target is present syncronize with it\n    if (trans->target2initiator_buffer_size > 0) {\n        if (!serial_recive_packet((uint8_t *)split_trans_target2initiator_buffer(trans), trans->target2initiator_buffer_size)) {\n            serial_output();\n            serial_high();\n            sei();\n            return false;\n        }\n    }\n\n    // initiator switch to output\n    change_reciver2sender();\n\n    // initiator send phase\n    if (trans->initiator2target_buffer_size > 0) {\n        serial_send_packet((uint8_t *)split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size);\n    }\n\n    // always, release the line when not in use\n    sync_send();\n\n    sei();\n    return true;\n}\n#else\n#    ifndef USE_I2C\n#        error SOFT_SERIAL_PIN or USE_I2C is required but has not been defined.\n#    endif\n#endif\n\n// Helix serial.c history\n//   2018-1-29 fork from let's split and add PD2, modify sync_recv() (#2308, bceffdefc)\n//   2018-6-28 bug fix master to slave comm and speed up (#3255, 1038bbef4)\n//             (adjusted with avr-gcc 4.9.2)\n//   2018-7-13 remove USE_SERIAL_PD2 macro (#3374, f30d6dd78)\n//             (adjusted with avr-gcc 4.9.2)\n//   2018-8-11 add support multi-type transaction (#3608, feb5e4aae)\n//             (adjusted with avr-gcc 4.9.2)\n//   2018-10-21 fix serial and RGB animation conflict (#4191, 4665e4fff)\n//             (adjusted with avr-gcc 7.3.0)\n//   2018-10-28 re-adjust compiler depend value of delay (#4269, 8517f8a66)\n//             (adjusted with avr-gcc 5.4.0, 7.3.0)\n//   2018-12-17 copy to TOP/quantum/split_common/ and remove backward compatibility code (#4669)\n"
  },
  {
    "path": "platforms/avr/drivers/spi_master.c",
    "content": "/*  Copyright 2020\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"spi_master.h\"\n\n#include \"timer.h\"\n\n#if defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)\n#    define SPI_SCK_PIN B1\n#    define SPI_MOSI_PIN B2\n#    define SPI_MISO_PIN B3\n#elif defined(__AVR_ATmega32A__)\n#    define SPI_SCK_PIN B7\n#    define SPI_MOSI_PIN B5\n#    define SPI_MISO_PIN B6\n#elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)\n#    define SPI_SCK_PIN B5\n#    define SPI_MOSI_PIN B3\n#    define SPI_MISO_PIN B4\n#endif\n\n#ifndef SPI_TIMEOUT\n#    define SPI_TIMEOUT 100\n#endif\n\nstatic pin_t   current_slave_pin     = NO_PIN;\nstatic bool    current_cs_active_low = true;\nstatic uint8_t current_slave_config  = 0;\nstatic bool    current_slave_2x      = false;\n\nstatic inline void spi_select(void) {\n    gpio_write_pin(current_slave_pin, current_cs_active_low ? 0 : 1);\n}\n\nstatic inline void spi_unselect(void) {\n    gpio_write_pin(current_slave_pin, current_cs_active_low ? 1 : 0);\n}\n\nvoid spi_init(void) {\n    gpio_write_pin_high(SPI_SS_PIN);\n    gpio_set_pin_output(SPI_SCK_PIN);\n    gpio_set_pin_output(SPI_MOSI_PIN);\n    gpio_set_pin_input(SPI_MISO_PIN);\n\n    SPCR = (_BV(SPE) | _BV(MSTR));\n}\n\nbool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {\n    spi_start_config_t start_config = {0};\n    start_config.slave_pin          = slavePin;\n    start_config.lsb_first          = lsbFirst;\n    start_config.mode               = mode;\n    start_config.divisor            = divisor;\n    start_config.cs_active_low      = true;\n    return spi_start_extended(&start_config);\n}\n\nbool spi_start_extended(spi_start_config_t *start_config) {\n    if (current_slave_pin != NO_PIN || start_config->slave_pin == NO_PIN) {\n        return false;\n    }\n\n    current_slave_config = 0;\n\n    if (start_config->lsb_first) {\n        current_slave_config |= _BV(DORD);\n    }\n\n    switch (start_config->mode) {\n        case 1:\n            current_slave_config |= _BV(CPHA);\n            break;\n        case 2:\n            current_slave_config |= _BV(CPOL);\n            break;\n        case 3:\n            current_slave_config |= (_BV(CPOL) | _BV(CPHA));\n            break;\n    }\n\n    uint16_t roundedDivisor = 1;\n    while (roundedDivisor < start_config->divisor) {\n        roundedDivisor <<= 1;\n    }\n\n    switch (roundedDivisor) {\n        case 16:\n            current_slave_config |= _BV(SPR0);\n            break;\n        case 64:\n            current_slave_config |= _BV(SPR1);\n            break;\n        case 128:\n            current_slave_config |= (_BV(SPR1) | _BV(SPR0));\n            break;\n        case 2:\n            current_slave_2x = true;\n            break;\n        case 8:\n            current_slave_2x = true;\n            current_slave_config |= _BV(SPR0);\n            break;\n        case 32:\n            current_slave_2x = true;\n            current_slave_config |= _BV(SPR1);\n            break;\n    }\n\n    SPCR |= current_slave_config;\n    if (current_slave_2x) {\n        SPSR |= _BV(SPI2X);\n    }\n    current_slave_pin     = start_config->slave_pin;\n    current_cs_active_low = start_config->cs_active_low;\n    gpio_set_pin_output(current_slave_pin);\n    spi_select();\n\n    return true;\n}\n\nspi_status_t spi_write(uint8_t data) {\n    SPDR = data;\n\n    uint16_t timeout_timer = timer_read();\n    while (!(SPSR & _BV(SPIF))) {\n        if ((timer_read() - timeout_timer) >= SPI_TIMEOUT) {\n            return SPI_STATUS_TIMEOUT;\n        }\n    }\n\n    return SPDR;\n}\n\nspi_status_t spi_read() {\n    SPDR = 0x00; // Dummy\n\n    uint16_t timeout_timer = timer_read();\n    while (!(SPSR & _BV(SPIF))) {\n        if ((timer_read() - timeout_timer) >= SPI_TIMEOUT) {\n            return SPI_STATUS_TIMEOUT;\n        }\n    }\n\n    return SPDR;\n}\n\nspi_status_t spi_transmit(const uint8_t *data, uint16_t length) {\n    spi_status_t status;\n\n    for (uint16_t i = 0; i < length; i++) {\n        status = spi_write(data[i]);\n\n        if (status < 0) {\n            return status;\n        }\n    }\n\n    return SPI_STATUS_SUCCESS;\n}\n\nspi_status_t spi_receive(uint8_t *data, uint16_t length) {\n    spi_status_t status;\n\n    for (uint16_t i = 0; i < length; i++) {\n        status = spi_read();\n\n        if (status >= 0) {\n            data[i] = status;\n        } else {\n            return status;\n        }\n    }\n\n    return SPI_STATUS_SUCCESS;\n}\n\nvoid spi_stop(void) {\n    if (current_slave_pin != NO_PIN) {\n        gpio_set_pin_output(current_slave_pin);\n        spi_unselect();\n        current_slave_pin = NO_PIN;\n        SPSR &= ~(_BV(SPI2X));\n        SPCR &= ~(current_slave_config);\n        current_slave_config = 0;\n        current_slave_2x     = false;\n    }\n}\n"
  },
  {
    "path": "platforms/avr/drivers/uart.c",
    "content": "/* UART Example for Teensy USB Development Board\n * http://www.pjrc.com/teensy/\n * Copyright (c) 2009 PJRC.COM, LLC\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n// Version 1.0: Initial Release\n// Version 1.1: Add support for Teensy 2.0, minor optimizations\n\n#include <avr/io.h>\n#include <avr/interrupt.h>\n\n#include \"uart.h\"\n\n#if defined(__AVR_AT90USB162__) || defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)\n#    define UDRn UDR1\n#    define UBRRnL UBRR1L\n#    define UCSRnA UCSR1A\n#    define UCSRnB UCSR1B\n#    define UCSRnC UCSR1C\n#    define U2Xn U2X1\n#    define RXENn RXEN1\n#    define TXENn TXEN1\n#    define RXCIEn RXCIE1\n#    define UCSZn1 UCSZ11\n#    define UCSZn0 UCSZ10\n#    define UDRIEn UDRIE1\n#    define USARTn_UDRE_vect USART1_UDRE_vect\n#    define USARTn_RX_vect USART1_RX_vect\n#elif defined(__AVR_ATmega32A__)\n#    define UDRn UDR\n#    define UBRRnL UBRRL\n#    define UCSRnA UCSRA\n#    define UCSRnB UCSRB\n#    define UCSRnC UCSRC\n#    define U2Xn U2X\n#    define RXENn RXEN\n#    define TXENn TXEN\n#    define RXCIEn RXCIE\n#    define UCSZn1 UCSZ1\n#    define UCSZn0 UCSZ0\n#    define UDRIEn UDRIE\n#    define USARTn_UDRE_vect USART_UDRE_vect\n#    define USARTn_RX_vect USART_RX_vect\n#elif defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__)\n#    define UDRn UDR0\n#    define UBRRnL UBRR0L\n#    define UCSRnA UCSR0A\n#    define UCSRnB UCSR0B\n#    define UCSRnC UCSR0C\n#    define U2Xn U2X0\n#    define RXENn RXEN0\n#    define TXENn TXEN0\n#    define RXCIEn RXCIE0\n#    define UCSZn1 UCSZ01\n#    define UCSZn0 UCSZ00\n#    define UDRIEn UDRIE0\n#    define USARTn_UDRE_vect USART_UDRE_vect\n#    define USARTn_RX_vect USART_RX_vect\n#endif\n\n// These buffers may be any size from 2 to 256 bytes.\n#define RX_BUFFER_SIZE 64\n#define TX_BUFFER_SIZE 256\n\nstatic volatile uint8_t tx_buffer[TX_BUFFER_SIZE];\nstatic volatile uint8_t tx_buffer_head;\nstatic volatile uint8_t tx_buffer_tail;\nstatic volatile uint8_t rx_buffer[RX_BUFFER_SIZE];\nstatic volatile uint8_t rx_buffer_head;\nstatic volatile uint8_t rx_buffer_tail;\n\n// Initialize the UART\nvoid uart_init(uint32_t baud) {\n    cli();\n    UBRRnL         = (F_CPU / 4 / baud - 1) / 2;\n    UCSRnA         = (1 << U2Xn);\n    UCSRnB         = (1 << RXENn) | (1 << TXENn) | (1 << RXCIEn);\n    UCSRnC         = (1 << UCSZn1) | (1 << UCSZn0);\n    tx_buffer_head = tx_buffer_tail = 0;\n    rx_buffer_head = rx_buffer_tail = 0;\n    sei();\n}\n\n// Transmit a byte\nvoid uart_write(uint8_t data) {\n    uint8_t i;\n\n    i = tx_buffer_head + 1;\n    if (i >= TX_BUFFER_SIZE) i = 0;\n    // return immediately to avoid deadlock when interrupt is disabled(called from ISR)\n    if (tx_buffer_tail == i && (SREG & (1 << SREG_I)) == 0) return;\n    while (tx_buffer_tail == i)\n        ; // wait until space in buffer\n    // cli();\n    tx_buffer[i]   = data;\n    tx_buffer_head = i;\n    UCSRnB         = (1 << RXENn) | (1 << TXENn) | (1 << RXCIEn) | (1 << UDRIEn);\n    // sei();\n}\n\n// Receive a byte\nuint8_t uart_read(void) {\n    uint8_t data, i;\n\n    while (rx_buffer_head == rx_buffer_tail)\n        ; // wait for character\n    i = rx_buffer_tail + 1;\n    if (i >= RX_BUFFER_SIZE) i = 0;\n    data           = rx_buffer[i];\n    rx_buffer_tail = i;\n    return data;\n}\n\nvoid uart_transmit(const uint8_t *data, uint16_t length) {\n    for (uint16_t i = 0; i < length; i++) {\n        uart_write(data[i]);\n    }\n}\n\nvoid uart_receive(uint8_t *data, uint16_t length) {\n    for (uint16_t i = 0; i < length; i++) {\n        data[i] = uart_read();\n    }\n}\n\n// Return whether the number of bytes waiting in the receive buffer is nonzero.\n// Call this before uart_read() to check if it will need\n// to wait for a byte to arrive.\nbool uart_available(void) {\n    uint8_t head, tail;\n\n    head = rx_buffer_head;\n    tail = rx_buffer_tail;\n    if (head >= tail) return (head - tail) > 0;\n    return (RX_BUFFER_SIZE + head - tail) > 0;\n}\n\n// Transmit Interrupt\nISR(USARTn_UDRE_vect) {\n    uint8_t i;\n\n    if (tx_buffer_head == tx_buffer_tail) {\n        // buffer is empty, disable transmit interrupt\n        UCSRnB = (1 << RXENn) | (1 << TXENn) | (1 << RXCIEn);\n    } else {\n        i = tx_buffer_tail + 1;\n        if (i >= TX_BUFFER_SIZE) i = 0;\n        UDRn           = tx_buffer[i];\n        tx_buffer_tail = i;\n    }\n}\n\n// Receive Interrupt\nISR(USARTn_RX_vect) {\n    uint8_t c, i;\n\n    c = UDRn;\n    i = rx_buffer_head + 1;\n    if (i >= RX_BUFFER_SIZE) i = 0;\n    if (i != rx_buffer_tail) {\n        rx_buffer[i]   = c;\n        rx_buffer_head = i;\n    }\n}\n"
  },
  {
    "path": "platforms/avr/drivers/ws2812_bitbang.c",
    "content": "/*\n * light weight WS2812 lib V2.0b\n *\n * Controls WS2811/WS2812/WS2812B RGB-LEDs\n * Author: Tim (cpldcpu@gmail.com)\n *\n * Jan 18th, 2014  v2.0b Initial Version\n * Nov 29th, 2015  v2.3  Added SK6812RGBW support\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include <avr/interrupt.h>\n#include <avr/io.h>\n#include <util/delay.h>\n#include \"ws2812.h\"\n#include \"pin_defs.h\"\n\n#define pinmask(pin) (_BV((pin)&0xF))\n\n/*\n  This routine writes an array of bytes with RGB values to the Dataout pin\n  using the fast 800kHz clockless WS2811/2812 protocol.\n*/\n\n// Fixed cycles used by the inner loop\n#define w_fixedlow 2\n#define w_fixedhigh 4\n#define w_fixedtotal 8\n\n// Insert NOPs to match the timing, if possible\n#define w_zerocycles (((F_CPU / 1000) * WS2812_T0H) / 1000000)\n#define w_onecycles (((F_CPU / 1000) * WS2812_T1H + 500000) / 1000000)\n#define w_totalcycles (((F_CPU / 1000) * WS2812_TIMING + 500000) / 1000000)\n\n// w1_nops - nops between rising edge and falling edge - low\n#if w_zerocycles >= w_fixedlow\n#    define w1_nops (w_zerocycles - w_fixedlow)\n#else\n#    define w1_nops 0\n#endif\n\n// w2_nops - nops between fe low and fe high\n#if w_onecycles >= (w_fixedhigh + w1_nops)\n#    define w2_nops (w_onecycles - w_fixedhigh - w1_nops)\n#else\n#    define w2_nops 0\n#endif\n\n// w3_nops - nops to complete loop\n#if w_totalcycles >= (w_fixedtotal + w1_nops + w2_nops)\n#    define w3_nops (w_totalcycles - w_fixedtotal - w1_nops - w2_nops)\n#else\n#    define w3_nops 0\n#endif\n\n// The only critical timing parameter is the minimum pulse length of the \"0\"\n// Warn or throw error if this timing can not be met with current F_CPU settings.\n#define w_lowtime ((w1_nops + w_fixedlow) * 1000000) / (F_CPU / 1000)\n#if w_lowtime > 550\n#    error \"Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?\"\n#elif w_lowtime > 450\n#    warning \"Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S).\"\n#    warning \"Please consider a higher clockspeed, if possible\"\n#endif\n\n#define w_nop1 \"nop      \\n\\t\"\n#define w_nop2 \"rjmp .+0 \\n\\t\"\n#define w_nop4 w_nop2 w_nop2\n#define w_nop8 w_nop4 w_nop4\n#define w_nop16 w_nop8 w_nop8\n\nstatic inline void ws2812_sendarray_mask(uint8_t *data, uint16_t datlen, uint8_t masklo, uint8_t maskhi) {\n    uint8_t curbyte, ctr, sreg_prev;\n\n    sreg_prev = SREG;\n    cli();\n\n    while (datlen--) {\n        curbyte = (*data++);\n\n        asm volatile(\"       ldi   %0,8  \\n\\t\"\n                     \"loop%=:            \\n\\t\"\n                     \"       out   %2,%3 \\n\\t\" //  '1' [01] '0' [01] - re\n#if (w1_nops & 1)\n                     w_nop1\n#endif\n#if (w1_nops & 2)\n                         w_nop2\n#endif\n#if (w1_nops & 4)\n                             w_nop4\n#endif\n#if (w1_nops & 8)\n                                 w_nop8\n#endif\n#if (w1_nops & 16)\n                                     w_nop16\n#endif\n                     \"       sbrs  %1,7  \\n\\t\" //  '1' [03] '0' [02]\n                     \"       out   %2,%4 \\n\\t\" //  '1' [--] '0' [03] - fe-low\n                     \"       lsl   %1    \\n\\t\" //  '1' [04] '0' [04]\n#if (w2_nops & 1)\n                     w_nop1\n#endif\n#if (w2_nops & 2)\n                         w_nop2\n#endif\n#if (w2_nops & 4)\n                             w_nop4\n#endif\n#if (w2_nops & 8)\n                                 w_nop8\n#endif\n#if (w2_nops & 16)\n                                     w_nop16\n#endif\n                     \"       out   %2,%4 \\n\\t\" //  '1' [+1] '0' [+1] - fe-high\n#if (w3_nops & 1)\n                     w_nop1\n#endif\n#if (w3_nops & 2)\n                         w_nop2\n#endif\n#if (w3_nops & 4)\n                             w_nop4\n#endif\n#if (w3_nops & 8)\n                                 w_nop8\n#endif\n#if (w3_nops & 16)\n                                     w_nop16\n#endif\n\n                     \"       dec   %0    \\n\\t\" //  '1' [+2] '0' [+2]\n                     \"       brne  loop%=\\n\\t\" //  '1' [+3] '0' [+4]\n                     : \"=&d\"(ctr)\n                     : \"r\"(curbyte), \"I\"(_SFR_IO_ADDR(PORTx_ADDRESS(WS2812_DI_PIN))), \"r\"(maskhi), \"r\"(masklo));\n    }\n\n    SREG = sreg_prev;\n}\n\nws2812_led_t ws2812_leds[WS2812_LED_COUNT];\n\nvoid ws2812_init(void) {\n    DDRx_ADDRESS(WS2812_DI_PIN) |= pinmask(WS2812_DI_PIN);\n}\n\nvoid ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    ws2812_leds[index].r = red;\n    ws2812_leds[index].g = green;\n    ws2812_leds[index].b = blue;\n#if defined(WS2812_RGBW)\n    ws2812_rgb_to_rgbw(&ws2812_leds[index]);\n#endif\n}\n\nvoid ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < WS2812_LED_COUNT; i++) {\n        ws2812_set_color(i, red, green, blue);\n    }\n}\n\nvoid ws2812_flush(void) {\n    uint8_t masklo = ~(pinmask(WS2812_DI_PIN)) & PORTx_ADDRESS(WS2812_DI_PIN);\n    uint8_t maskhi = pinmask(WS2812_DI_PIN) | PORTx_ADDRESS(WS2812_DI_PIN);\n\n    ws2812_sendarray_mask((uint8_t *)ws2812_leds, WS2812_LED_COUNT * sizeof(ws2812_led_t), masklo, maskhi);\n\n    _delay_us(WS2812_TRST_US);\n}\n"
  },
  {
    "path": "platforms/avr/drivers/ws2812_i2c.c",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"ws2812.h\"\n#include \"i2c_master.h\"\n\n#ifdef WS2812_RGBW\n#    error \"RGBW not supported\"\n#endif\n\n#ifndef WS2812_I2C_ADDRESS\n#    define WS2812_I2C_ADDRESS 0xB0\n#endif\n\n#ifndef WS2812_I2C_TIMEOUT\n#    define WS2812_I2C_TIMEOUT 100\n#endif\n\nws2812_led_t ws2812_leds[WS2812_LED_COUNT];\n\nvoid ws2812_init(void) {\n    i2c_init();\n}\n\nvoid ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    ws2812_leds[index].r = red;\n    ws2812_leds[index].g = green;\n    ws2812_leds[index].b = blue;\n}\n\nvoid ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < WS2812_LED_COUNT; i++) {\n        ws2812_set_color(i, red, green, blue);\n    }\n}\n\nvoid ws2812_flush(void) {\n    i2c_transmit(WS2812_I2C_ADDRESS, (uint8_t *)ws2812_leds, WS2812_LED_COUNT * sizeof(ws2812_led_t), WS2812_I2C_TIMEOUT);\n}\n"
  },
  {
    "path": "platforms/avr/flash.mk",
    "content": "# Hey Emacs, this is a -*- makefile -*-\n##############################################################################\n# Architecture or project specific options\n#\n\n# Autodetect teensy loader\nifndef TEENSY_LOADER_CLI\n\tifneq (, $(shell which teensy-loader-cli 2>/dev/null))\n\t\tTEENSY_LOADER_CLI ?= teensy-loader-cli\n\telse\n\t\tTEENSY_LOADER_CLI ?= teensy_loader_cli\n\tendif\nendif\n\ndefine EXEC_TEENSY\n\t$(TEENSY_LOADER_CLI) -mmcu=$(MCU) -w -v $(BUILD_DIR)/$(TARGET).hex\nendef\n\nteensy: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware\n\t$(call EXEC_TEENSY)\n\nDFU_PROGRAMMER ?= dfu-programmer\nGREP ?= grep\n\ndefine EXEC_DFU\n\tif [ \"$(1)\" ]; then \\\n\t\techo \"Flashing '$(1)' for EE_HANDS split keyboard support.\" ;\\\n\tfi; \\\n\tif ! $(DFU_PROGRAMMER) $(MCU) get bootloader-version >/dev/null 2>/dev/null; then\\\n\t\tprintf \"$(MSG_BOOTLOADER_NOT_FOUND_QUICK_RETRY)\" ;\\\n\t\tsleep $(BOOTLOADER_RETRY_TIME) ;\\\n\t\twhile ! $(DFU_PROGRAMMER) $(MCU) get bootloader-version >/dev/null 2>/dev/null; do\\\n\t\t\tprintf \".\" ;\\\n\t\t\tsleep $(BOOTLOADER_RETRY_TIME) ;\\\n\t\tdone ;\\\n\t\tprintf \"\\n\" ;\\\n\tfi; \\\n\t$(DFU_PROGRAMMER) $(MCU) get bootloader-version ;\\\n\tif $(DFU_PROGRAMMER) --version 2>&1 | $(GREP) -q 0.7 ; then\\\n\t\t$(DFU_PROGRAMMER) $(MCU) erase --force; \\\n\t\tif [ \"$(1)\" ]; then \\\n\t\t\t$(DFU_PROGRAMMER) $(MCU) flash --force --eeprom $(QUANTUM_PATH)/split_common/$(1);\\\n\t\tfi; \\\n\t\t$(DFU_PROGRAMMER) $(MCU) flash --force $(BUILD_DIR)/$(TARGET).hex;\\\n\telse \\\n\t\t$(DFU_PROGRAMMER) $(MCU) erase; \\\n\t\tif [ \"$(1)\" ]; then \\\n\t\t\t$(DFU_PROGRAMMER) $(MCU) flash-eeprom $(QUANTUM_PATH)/split_common/$(1);\\\n\t\tfi; \\\n\t\t$(DFU_PROGRAMMER) $(MCU) flash $(BUILD_DIR)/$(TARGET).hex;\\\n\tfi; \\\n\t$(DFU_PROGRAMMER) $(MCU) reset\nendef\n\ndfu: $(BUILD_DIR)/$(TARGET).hex cpfirmware check-size\n\t$(call EXEC_DFU)\n\ndfu-start:\n\t$(DFU_PROGRAMMER) $(MCU) reset\n\t$(DFU_PROGRAMMER) $(MCU) start\n\ndfu-ee: $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).eep\n\tif $(DFU_PROGRAMMER) --version 2>&1 | $(GREP) -q 0.7 ; then\\\n\t\t$(DFU_PROGRAMMER) $(MCU) flash --force --eeprom $(BUILD_DIR)/$(TARGET).eep;\\\n\telse\\\n\t\t$(DFU_PROGRAMMER) $(MCU) flash-eeprom $(BUILD_DIR)/$(TARGET).eep;\\\n\tfi\n\t$(DFU_PROGRAMMER) $(MCU) reset\n\ndfu-split-left: $(BUILD_DIR)/$(TARGET).hex cpfirmware check-size\n\t$(call EXEC_DFU,eeprom-lefthand.eep)\n\ndfu-split-right: $(BUILD_DIR)/$(TARGET).hex cpfirmware check-size\n\t$(call EXEC_DFU,eeprom-righthand.eep)\n\nAVRDUDE_PROGRAMMER ?= avrdude\n\ndefine EXEC_AVRDUDE\n\tlist_devices() { \\\n\t\tif $(GREP) -q -s icrosoft /proc/version; then \\\n\t\t\tpowershell.exe 'Get-CimInstance -Class Win32_SerialPort | Select -ExpandProperty \"DeviceID\"' 2>/dev/null | sed -e \"s/\\r//g\" | LANG=C perl -pne 's/COM(\\d+)/COM.($$1-1)/e' | sed 's!COM!/dev/ttyS!' | sort; \\\n\t\telif [ \"`uname`\" = \"FreeBSD\" ]; then \\\n\t\t\tls /dev/tty* | grep -v '\\.lock$$' | grep -v '\\.init$$'; \\\n\t\telse \\\n\t\t\tls /dev/tty*; \\\n\t\tfi; \\\n\t}; \\\n\tUSB= ;\\\n\tprintf \"Waiting for USB serial port - reset your controller now (Ctrl+C to cancel)\"; \\\n\tTMP1=`mktemp`; \\\n\tTMP2=`mktemp`; \\\n\tlist_devices > $$TMP1; \\\n\twhile [ -z \"$$USB\" ]; do \\\n\t\tsleep $(BOOTLOADER_RETRY_TIME); \\\n\t\tprintf \".\"; \\\n\t\tlist_devices > $$TMP2; \\\n\t\tUSB=`comm -13 $$TMP1 $$TMP2 | $(GREP) -o '/dev/tty.*'`; \\\n\t\tmv $$TMP2 $$TMP1; \\\n\tdone; \\\n\trm $$TMP1; \\\n\techo \"\"; \\\n\techo \"Device $$USB has appeared; assuming it is the controller.\"; \\\n\tif $(GREP) -q -s 'MINGW\\|MSYS\\|icrosoft' /proc/version; then \\\n\t\tUSB=`echo \"$$USB\" | LANG=C perl -pne 's/\\/dev\\/ttyS(\\d+)/COM.($$1+1)/e'`; \\\n\t\techo \"Remapped USB port to $$USB\"; \\\n\t\tsleep 1; \\\n\telse \\\n\t\tprintf \"Waiting for $$USB to become writable.\"; \\\n\t\twhile [ ! -w \"$$USB\" ]; do sleep $(BOOTLOADER_RETRY_TIME); printf \".\"; done; echo \"\"; \\\n\tfi; \\\n\tif [ -z \"$(1)\" ]; then \\\n\t\t$(AVRDUDE_PROGRAMMER) -p $(MCU) -c avr109 -P $$USB -U flash:w:$(BUILD_DIR)/$(TARGET).hex; \\\n\telse \\\n\t\t$(AVRDUDE_PROGRAMMER) -p $(MCU) -c avr109 -P $$USB -U flash:w:$(BUILD_DIR)/$(TARGET).hex -U eeprom:w:$(QUANTUM_PATH)/split_common/$(1); \\\n\tfi\nendef\n\navrdude: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware\n\t$(call EXEC_AVRDUDE)\n\navrdude-loop: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware\n\twhile true; do \\\n\t\t$(call EXEC_AVRDUDE) ; \\\n\tdone\n\navrdude-split-left: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware\n\t$(call EXEC_AVRDUDE,eeprom-lefthand.eep)\n\navrdude-split-right: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware\n\t$(call EXEC_AVRDUDE,eeprom-righthand.eep)\n\ndefine EXEC_USBASP\n\tif $(AVRDUDE_PROGRAMMER) -p $(AVRDUDE_MCU) -c usbasp 2>&1 | grep -q \"\\(could not\\|cannot\\) find USB device with\"; then \\\n\t\tprintf \"$(MSG_BOOTLOADER_NOT_FOUND_QUICK_RETRY)\" ;\\\n\t\tsleep $(BOOTLOADER_RETRY_TIME) ;\\\n\t\tuntil $(AVRDUDE_PROGRAMMER) -p $(AVRDUDE_MCU) -c usbasp 2>&1 | (! grep -q \"\\(could not\\|cannot\\) find USB device with\"); do\\\n\t\t\tprintf \".\" ;\\\n\t\t\tsleep $(BOOTLOADER_RETRY_TIME) ;\\\n\t\tdone ;\\\n\t\tprintf \"\\n\" ;\\\n\tfi\n\t$(AVRDUDE_PROGRAMMER) -p $(AVRDUDE_MCU) -c usbasp -U flash:w:$(BUILD_DIR)/$(TARGET).hex\nendef\n\nusbasp: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware\n\t$(call EXEC_USBASP)\n\nBOOTLOADHID_PROGRAMMER ?= bootloadHID\n\n# bootloadHid executable has no cross platform detect methods\n# so keep running bootloadHid if the output contains \"The specified device was not found\"\ndefine EXEC_BOOTLOADHID\n\tuntil $(BOOTLOADHID_PROGRAMMER) -r $(BUILD_DIR)/$(TARGET).hex 2>&1 | tee /dev/stderr | grep -v \"device was not found\"; do\\\n\t\tprintf \"$(MSG_BOOTLOADER_NOT_FOUND)\" ;\\\n\t\tsleep 5 ;\\\n\tdone\nendef\n\nbootloadhid: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware\n\t$(call EXEC_BOOTLOADHID)\n\nHID_BOOTLOADER_CLI ?= hid_bootloader_cli\n\ndefine EXEC_HID_LUFA\n\t$(HID_BOOTLOADER_CLI) -mmcu=$(MCU) -w -v $(BUILD_DIR)/$(TARGET).hex\nendef\n\nhid_bootloader: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware\n\t$(call EXEC_HID_LUFA)\n\nflash: $(BUILD_DIR)/$(TARGET).hex check-size cpfirmware\n\t$(SILENT) || printf \"Flashing for bootloader: $(BLUE)$(BOOTLOADER)$(NO_COLOR)\\n\"\nifneq ($(strip $(PROGRAM_CMD)),)\n\t$(UNSYNC_OUTPUT_CMD) && $(PROGRAM_CMD)\nelse ifeq ($(strip $(BOOTLOADER)), caterina)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_AVRDUDE)\nelse ifeq ($(strip $(BOOTLOADER)), halfkay)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_TEENSY)\nelse ifeq (dfu,$(findstring dfu,$(BOOTLOADER)))\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_DFU)\nelse ifeq ($(strip $(BOOTLOADER)), usbasploader)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_USBASP)\nelse ifeq ($(strip $(BOOTLOADER)), bootloadhid)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_BOOTLOADHID)\nelse ifeq ($(strip $(BOOTLOADER)), qmk-hid)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_HID_LUFA)\nelse\n\t$(PRINT_OK); $(SILENT) || printf \"$(MSG_FLASH_BOOTLOADER)\"\nendif\n"
  },
  {
    "path": "platforms/avr/gpio.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <avr/io.h>\n\n#include \"compiler_support.h\"\n#include \"pin_defs.h\"\n\ntypedef uint8_t pin_t;\n\n/* Operation of GPIO by pin. */\n\n#define gpio_set_pin_input(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))\n#define gpio_set_pin_input_high(pin) (DDRx_ADDRESS(pin) &= ~_BV((pin)&0xF), PORTx_ADDRESS(pin) |= _BV((pin)&0xF))\n#define gpio_set_pin_input_low(pin) STATIC_ASSERT(0, \"GPIO pulldowns in input mode are not available on AVR\")\n#define gpio_set_pin_output_push_pull(pin) (DDRx_ADDRESS(pin) |= _BV((pin)&0xF))\n#define gpio_set_pin_output_open_drain(pin) STATIC_ASSERT(0, \"Open-drain outputs are not available on AVR\")\n#define gpio_set_pin_output(pin) gpio_set_pin_output_push_pull(pin)\n\n#define gpio_write_pin_high(pin) (PORTx_ADDRESS(pin) |= _BV((pin)&0xF))\n#define gpio_write_pin_low(pin) (PORTx_ADDRESS(pin) &= ~_BV((pin)&0xF))\n#define gpio_write_pin(pin, level) ((level) ? gpio_write_pin_high(pin) : gpio_write_pin_low(pin))\n\n#define gpio_read_pin(pin) ((bool)(PINx_ADDRESS(pin) & _BV((pin)&0xF)))\n\n#define gpio_toggle_pin(pin) (PORTx_ADDRESS(pin) ^= _BV((pin)&0xF))\n"
  },
  {
    "path": "platforms/avr/hardware_id.c",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n// For some reason this bit is undocumented for some AVR parts and not defined in their avr-libc IO headers\n// See https://stackoverflow.com/questions/12350914/how-to-read-atmega-32-signature-row\n#ifndef SIGRD\n#    define SIGRD 5\n#endif // SIGRD\n\n#include <avr/boot.h>\n#include \"hardware_id.h\"\n\n__attribute__((weak)) hardware_id_t get_hardware_id(void) {\n    hardware_id_t id = {0};\n    for (uint8_t i = 0; i < 10; i += 1) {\n        ((uint8_t*)&id)[i] = boot_signature_byte_get(i + 0x0E);\n    }\n    return id;\n}\n"
  },
  {
    "path": "platforms/avr/mcu_selection.mk",
    "content": "ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2 atmega16u4 atmega32u4 at90usb646 at90usb647 at90usb1286 at90usb1287))\n  PROTOCOL = LUFA\n\n  # Processor frequency.\n  #     This will define a symbol, F_CPU, in all source code files equal to the\n  #     processor frequency in Hz. You can then use this symbol in your source code to\n  #     calculate timings. Do NOT tack on a 'UL' at the end, this will be done\n  #     automatically to create a 32-bit value in your source code.\n  #\n  #     This will be an integer division of F_USB below, as it is sourced by\n  #     F_USB after it has run through any CPU prescalers. Note that this value\n  #     does not *change* the processor frequency - it should merely be updated to\n  #     reflect the processor speed set externally so that the code can use accurate\n  #     software delays.\n  F_CPU ?= 16000000\n\n  # LUFA specific\n  #\n  # Target architecture (see library \"Board Types\" documentation).\n  ARCH = AVR8\n\n  # Input clock frequency.\n  #     This will define a symbol, F_USB, in all source code files equal to the\n  #     input clock frequency (before any prescaling is performed) in Hz. This value may\n  #     differ from F_CPU if prescaling is used on the latter, and is required as the\n  #     raw input clock is fed directly to the PLL sections of the AVR for high speed\n  #     clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL'\n  #     at the end, this will be done automatically to create a 32-bit value in your\n  #     source code.\n  #\n  #     If no clock division is performed on the input clock inside the AVR (via the\n  #     CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU.\n  F_USB ?= $(F_CPU)\n\n  # Interrupt driven control endpoint task\n  ifeq (,$(filter $(NO_INTERRUPT_CONTROL_ENDPOINT),yes))\n    OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT\n  endif\n  ifneq (,$(filter $(MCU),at90usb162 atmega16u2 atmega32u2))\n    NO_I2C = yes\n  endif\nendif\n\nifneq (,$(filter $(MCU),atmega32a))\n  # MCU name for avrdude\n  AVRDUDE_MCU = m32\n\n  PROTOCOL = VUSB\n\n  # Processor frequency.\n  #     This will define a symbol, F_CPU, in all source code files equal to the\n  #     processor frequency in Hz. You can then use this symbol in your source code to\n  #     calculate timings. Do NOT tack on a 'UL' at the end, this will be done\n  #     automatically to create a 32-bit value in your source code.\n  F_CPU ?= 12000000\nendif\n\nifneq (,$(filter $(MCU),atmega328p))\n  # MCU name for avrdude\n  AVRDUDE_MCU = m328p\n\n  PROTOCOL = VUSB\n\n  # Processor frequency.\n  #     This will define a symbol, F_CPU, in all source code files equal to the\n  #     processor frequency in Hz. You can then use this symbol in your source code to\n  #     calculate timings. Do NOT tack on a 'UL' at the end, this will be done\n  #     automatically to create a 32-bit value in your source code.\n  F_CPU ?= 16000000\nendif\n\nifneq (,$(filter $(MCU),atmega328))\n  # MCU name for avrdude\n  AVRDUDE_MCU = m328\n\n  PROTOCOL = VUSB\n\n  # Processor frequency.\n  #     This will define a symbol, F_CPU, in all source code files equal to the\n  #     processor frequency in Hz. You can then use this symbol in your source code to\n  #     calculate timings. Do NOT tack on a 'UL' at the end, this will be done\n  #     automatically to create a 32-bit value in your source code.\n  F_CPU ?= 16000000\nendif\n\nifneq (,$(filter $(MCU),attiny85))\n  PROTOCOL = VUSB\n\n  # Processor frequency.\n  #     This will define a symbol, F_CPU, in all source code files equal to the\n  #     processor frequency in Hz. You can then use this symbol in your source code to\n  #     calculate timings. Do NOT tack on a 'UL' at the end, this will be done\n  #     automatically to create a 32-bit value in your source code.\n  F_CPU ?= 16500000\nendif\n"
  },
  {
    "path": "platforms/avr/platform.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"platform_deps.h\"\n\nstatic void disable_jtag(void) {\n// To use PF4-7 (PC2-5 on ATmega32A), disable JTAG by writing JTD bit twice within four cycles.\n#if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__))\n    MCUCR |= _BV(JTD);\n    MCUCR |= _BV(JTD);\n#elif defined(__AVR_ATmega32A__)\n    MCUCSR |= _BV(JTD);\n    MCUCSR |= _BV(JTD);\n#endif\n}\n\nvoid platform_setup(void) {\n    disable_jtag();\n}\n"
  },
  {
    "path": "platforms/avr/platform.mk",
    "content": "# Hey Emacs, this is a -*- makefile -*-\n##############################################################################\n# Compiler settings\n#\nCC = $(CC_PREFIX) avr-gcc\nOBJCOPY = avr-objcopy\nOBJDUMP = avr-objdump\nSIZE = avr-size\nAR = avr-ar\nNM = avr-nm\nHEX = $(OBJCOPY) -O $(FORMAT) -R .eeprom -R .fuse -R .lock -R .signature\nEEP = $(OBJCOPY) -j .eeprom --set-section-flags=.eeprom=\"alloc,load\" --change-section-lma .eeprom=0 --no-change-warnings -O $(FORMAT)\nBIN =\n\nCOMPILEFLAGS += $(call cc-option,--param=min-pagesize=0)\n\n# Fix ICE's: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116389\nCOMPILEFLAGS += $(call cc-option,-mlra)\n\nCOMPILEFLAGS += -funsigned-char\nCOMPILEFLAGS += -funsigned-bitfields\nCOMPILEFLAGS += -ffunction-sections\nCOMPILEFLAGS += -fdata-sections\nCOMPILEFLAGS += -fpack-struct\nCOMPILEFLAGS += -fshort-enums\nCOMPILEFLAGS += -mcall-prologues\nCOMPILEFLAGS += -fno-builtin-printf\n\n# On older compilers, linker relaxation is only possible if link time optimizations are not enabled.\nifeq ($(strip $(LTO_ENABLE)), no)\n\tCOMPILEFLAGS += -mrelax\nelse\n\t# Newer compilers may support both, so quickly check before adding `-mrelax`.\n\tCOMPILEFLAGS += $(call cc-option,-mrelax,,-flto=auto)\nendif\n\nASFLAGS += $(AVR_ASFLAGS)\n\nCFLAGS += $(COMPILEFLAGS) $(AVR_CFLAGS)\nCFLAGS += -fno-inline-small-functions\nCFLAGS += -fno-strict-aliasing\n\nCXXFLAGS += $(COMPILEFLAGS)\nCXXFLAGS += -fno-exceptions $(CXXSTANDARD)\n\nLDFLAGS += -Wl,--gc-sections\n\n# Use AVR's libc minimal printf implementation which has less features\n# and thus can shave ~400 bytes. Usually we use the xprintf\n# implementation but keyboards that use s(n)printf automatically\n# pull in the AVR libc implementation, which is ~900 bytes heavy.\nAVR_USE_MINIMAL_PRINTF ?= no\nifeq ($(strip $(AVR_USE_MINIMAL_PRINTF)), yes)\n\tLDFLAGS += -Wl,--whole-archive -lprintf_min -Wl,--no-whole-archive\nendif\n\nOPT_DEFS += -DF_CPU=$(F_CPU)UL\n\nMCUFLAGS = -mmcu=$(MCU)\n\n# List any extra directories to look for libraries here.\n#     Each directory must be seperated by a space.\n#     Use forward slashes for directory separators.\n#     For a directory that has spaces, enclose it in quotes.\nEXTRALIBDIRS =\n\n\n#---------------- External Memory Options ----------------\n\n# 64 KB of external RAM, starting after internal RAM (ATmega128!),\n# used for variables (.data/.bss) and heap (malloc()).\n#EXTMEMOPTS = -Wl,-Tdata=0x801100,--defsym=__heap_end=0x80ffff\n\n# 64 KB of external RAM, starting after internal RAM (ATmega128!),\n# only used for heap (malloc()).\n#EXTMEMOPTS = -Wl,--section-start,.data=0x801100,--defsym=__heap_end=0x80ffff\n\nEXTMEMOPTS =\n\n#---------------- Debugging Options ----------------\n\n# Debugging format.\n#     Native formats for AVR-GCC's -g are dwarf-2 [default] or stabs.\n#     AVR Studio 4.10 requires dwarf-2.\n#     AVR [Extended] COFF format requires stabs, plus an avr-objcopy run.\nDEBUG = dwarf-2\n\n# For simulavr only - target MCU frequency.\nDEBUG_MFREQ = $(F_CPU)\n\n# Set the DEBUG_UI to either gdb or insight.\n# DEBUG_UI = gdb\nDEBUG_UI = insight\n\n# Set the debugging back-end to either avarice, simulavr.\nDEBUG_BACKEND = avarice\n#DEBUG_BACKEND = simulavr\n\n# GDB Init Filename.\nGDBINIT_FILE = __avr_gdbinit\n\n# When using avarice settings for the JTAG\nJTAG_DEV = /dev/com1\n\n# Debugging port used to communicate between GDB / avarice / simulavr.\nDEBUG_PORT = 4242\n\n# Debugging host used to communicate between GDB / avarice / simulavr, normally\n#     just set to localhost unless doing some sort of crazy debugging when\n#     avarice is running on a different computer.\nDEBUG_HOST = localhost\n\n#============================================================================\n\nSIZE_MARGIN = 1024\n\ncheck-size:\n\t$(eval MAX_SIZE=$(shell n=`$(CC) -E -mmcu=$(MCU) -D__ASSEMBLER__ $(CFLAGS) $(OPT_DEFS) platforms/avr/bootloader_size.c 2> /dev/null | $(SED) -ne 's/\\r//;/^#/n;/^AVR_SIZE:/,$${s/^AVR_SIZE: //;p;}'` && echo $$(($$n)) || echo 0))\n\t$(eval CURRENT_SIZE=$(shell if [ -f $(BUILD_DIR)/$(TARGET).hex ]; then $(SIZE) --target=$(FORMAT) $(BUILD_DIR)/$(TARGET).hex | $(AWK) 'NR==2 {print $$4}'; else printf 0; fi))\n\t$(eval FREE_SIZE=$(shell expr $(MAX_SIZE) - $(CURRENT_SIZE)))\n\t$(eval OVER_SIZE=$(shell expr $(CURRENT_SIZE) - $(MAX_SIZE)))\n\t$(eval PERCENT_SIZE=$(shell expr $(CURRENT_SIZE) \\* 100 / $(MAX_SIZE)))\n\tif [ $(MAX_SIZE) -gt 0 ] && [ $(CURRENT_SIZE) -gt 0 ]; then \\\n\t\t$(SILENT) || printf \"$(MSG_CHECK_FILESIZE)\" | $(AWK_CMD); \\\n\t\tif [ $(CURRENT_SIZE) -gt $(MAX_SIZE) ]; then \\\n\t\t\t$(REMOVE) $(TARGET).$(FIRMWARE_FORMAT); \\\n\t\t\t$(REMOVE) $(BUILD_DIR)/$(TARGET).{hex,bin,uf2}; \\\n\t\t    printf \"\\n * $(MSG_FILE_TOO_BIG)\"; $(PRINT_ERROR_PLAIN); \\\n\t\telse \\\n\t\t    if [ $(FREE_SIZE) -lt $(SIZE_MARGIN) ]; then \\\n\t\t\t$(PRINT_WARNING_PLAIN); printf \" * $(MSG_FILE_NEAR_LIMIT)\"; \\\n\t\t    else \\\n\t\t\t$(PRINT_OK); $(SILENT) || printf \" * $(MSG_FILE_JUST_RIGHT)\"; \\\n\t\t    fi ; \\\n\t\tfi ; \\\n\tfi\n\t$(eval END_POINTER=$(shell printf \"%d\" $$(( 0xffff & 0x$$( if [ -f $(BUILD_DIR)/$(TARGET).elf ]; then avr-objdump -t $(BUILD_DIR)/$(TARGET).elf | grep -e '\\b_end\\b' | cut -c -8; else printf 0; fi ) )) ))\n\t$(eval STACK_POINTER=$(shell printf \"%d\" $$(( 0xffff & 0x$$( if [ -f $(BUILD_DIR)/$(TARGET).elf ]; then avr-objdump -t $(BUILD_DIR)/$(TARGET).elf | grep -e '\\b__stack\\b' | cut -c -8; else printf 0; fi ) )) ))\n\t$(eval STACK_SIZE=$(shell expr $(STACK_POINTER) + 1 - $(END_POINTER)))\n\t$(eval RAM_OVERFLOW_AMOUNT=$(shell expr 0 - $(STACK_SIZE)))\n\tif [ $(STACK_POINTER) -gt 0 ] && [ $(END_POINTER) -gt 0 ]; then \\\n\t\t$(SILENT) || printf \"$(MSG_CHECK_STACKSIZE)\" | $(AWK_CMD); \\\n\t\tif [ $(STACK_SIZE) -lt 0 ] ; then \\\n\t\t\tprintf \"\\n * $(MSG_MEMORY_OVERFLOW)\"; $(PRINT_ERROR_PLAIN); \\\n\t\telse \\\n\t\t\t$(SILENT) || printf \"\\n * $(MSG_STACK_SIZE)\"; \\\n\t\tfi ; \\\n\tfi\n\n# Convert hex to bin.\nbin: $(BUILD_DIR)/$(TARGET).hex\nifeq ($(BOOTLOADER),lufa-ms)\n\t$(eval BIN_PADDING=$(shell n=`expr 32768 - $(BOOTLOADER_SIZE)` && echo $$(($$n)) || echo 0))\n\t$(OBJCOPY) -Iihex -Obinary $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin --pad-to $(BIN_PADDING)\nelse\n\t$(OBJCOPY) -Iihex -Obinary $(BUILD_DIR)/$(TARGET).hex $(BUILD_DIR)/$(TARGET).bin\nendif\n\t$(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin;\n\n# copy bin to FLASH.bin\nflashbin: bin\n\t$(COPY) $(BUILD_DIR)/$(TARGET).bin FLASH.bin;\n\n# Generate avr-gdb config/init file which does the following:\n#     define the reset signal, load the target file, connect to target, and set\n#     a breakpoint at main().\ngdb-config:\n\t@$(REMOVE) $(GDBINIT_FILE)\n\t@echo define reset >> $(GDBINIT_FILE)\n\t@echo SIGNAL SIGHUP >> $(GDBINIT_FILE)\n\t@echo end >> $(GDBINIT_FILE)\n\t@echo file $(BUILD_DIR)/$(TARGET).elf >> $(GDBINIT_FILE)\n\t@echo target remote $(DEBUG_HOST):$(DEBUG_PORT)  >> $(GDBINIT_FILE)\nifeq ($(DEBUG_BACKEND),simulavr)\n\t@echo load  >> $(GDBINIT_FILE)\nendif\n\t@echo break main >> $(GDBINIT_FILE)\n\ndebug: gdb-config $(BUILD_DIR)/$(TARGET).elf\nifeq ($(DEBUG_BACKEND), avarice)\n\t@echo Starting AVaRICE - Press enter when \"waiting to connect\" message displays.\n\t@$(WINSHELL) /c start avarice --jtag $(JTAG_DEV) --erase --program --file \\\n\t$(BUILD_DIR)/$(TARGET).elf $(DEBUG_HOST):$(DEBUG_PORT)\n\t@$(WINSHELL) /c pause\n\nelse\n\t@$(WINSHELL) /c start simulavr --gdbserver --device $(MCU) --clock-freq \\\n\t$(DEBUG_MFREQ) --port $(DEBUG_PORT)\nendif\n\t@$(WINSHELL) /c start avr-$(DEBUG_UI) --command=$(GDBINIT_FILE)\n\n\n\n\n# Convert ELF to COFF for use in debugging / simulating in AVR Studio or VMLAB.\nCOFFCONVERT = $(OBJCOPY) --debugging\nCOFFCONVERT += --change-section-address .data-0x800000\nCOFFCONVERT += --change-section-address .bss-0x800000\nCOFFCONVERT += --change-section-address .noinit-0x800000\nCOFFCONVERT += --change-section-address .eeprom-0x810000\n\n\n\ncoff: $(BUILD_DIR)/$(TARGET).elf\n\t@$(SECHO) $(MSG_COFF) $(BUILD_DIR)/$(TARGET).cof\n\t$(COFFCONVERT) -O coff-avr $< $(BUILD_DIR)/$(TARGET).cof\n\n\nextcoff: $(BUILD_DIR)/$(TARGET).elf\n\t@$(SECHO) $(MSG_EXTENDED_COFF) $(BUILD_DIR)/$(TARGET).cof\n\t$(COFFCONVERT) -O coff-ext-avr $< $(BUILD_DIR)/$(TARGET).cof\n\nifeq ($(strip $(BOOTLOADER)), qmk-dfu)\nQMK_BOOTLOADER_TYPE = DFU\nelse ifeq ($(strip $(BOOTLOADER)), qmk-hid)\nQMK_BOOTLOADER_TYPE = HID\nendif\n\nbootloader: cpfirmware\nifeq ($(strip $(QMK_BOOTLOADER_TYPE)),)\n\t$(call CATASTROPHIC_ERROR,Invalid BOOTLOADER,Please set BOOTLOADER to \"qmk-dfu\" or \"qmk-hid\" first!)\nelse\n\tmake -C lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/ clean TARGET=Bootloader$(QMK_BOOTLOADER_TYPE)\n\t$(QMK_BIN) generate-dfu-header --quiet --keyboard $(KEYBOARD) --output lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/Keyboard.h\n\t$(eval MAX_SIZE=$(shell n=`$(CC) -E -mmcu=$(MCU) -D__ASSEMBLER__ $(CFLAGS) $(OPT_DEFS) platforms/avr/bootloader_size.c 2> /dev/null | sed -ne 's/\\r//;/^#/n;/^AVR_SIZE:/,$${s/^AVR_SIZE: //;p;}'` && echo $$(($$n)) || echo 0))\n\t$(eval PROGRAM_SIZE_KB=$(shell n=`expr $(MAX_SIZE) / 1024` && echo $$(($$n)) || echo 0))\n\t$(eval BOOT_SECTION_SIZE_KB=$(shell n=`expr  $(BOOTLOADER_SIZE) / 1024` && echo $$(($$n)) || echo 0))\n\t$(eval FLASH_SIZE_KB=$(shell n=`expr $(PROGRAM_SIZE_KB) + $(BOOT_SECTION_SIZE_KB)` && echo $$(($$n)) || echo 0))\n\tmake -C lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/ MCU=$(MCU) ARCH=$(ARCH) F_CPU=$(F_CPU) FLASH_SIZE_KB=$(FLASH_SIZE_KB) BOOT_SECTION_SIZE_KB=$(BOOT_SECTION_SIZE_KB) TARGET=Bootloader$(QMK_BOOTLOADER_TYPE)\n\tprintf \"Bootloader$(QMK_BOOTLOADER_TYPE).hex copied to $(TARGET)_bootloader.hex\\n\"\n\tcp lib/lufa/Bootloaders/$(QMK_BOOTLOADER_TYPE)/Bootloader$(QMK_BOOTLOADER_TYPE).hex $(TARGET)_bootloader.hex\nendif\n\nproduction: $(BUILD_DIR)/$(TARGET).hex bootloader cpfirmware\n\t@cat $(BUILD_DIR)/$(TARGET).hex | awk '/^:00000001FF/ == 0' > $(TARGET)_production.hex\n\t@cat $(TARGET)_bootloader.hex >> $(TARGET)_production.hex\n\techo \"File sizes:\"\n\t$(SIZE) $(TARGET).hex $(TARGET)_bootloader.hex $(TARGET)_production.hex\n"
  },
  {
    "path": "platforms/avr/platform_deps.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <avr/pgmspace.h>\n#include <avr/io.h>\n#include <avr/interrupt.h>\n"
  },
  {
    "path": "platforms/avr/printf.c",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n#include \"xprintf.h\"\n#include \"sendchar.h\"\n\nvoid print_set_sendchar(sendchar_func_t func) {\n    xdev_out(func);\n}\n"
  },
  {
    "path": "platforms/avr/printf.mk",
    "content": "SRC += $(PLATFORM_COMMON_DIR)/xprintf.S\nSRC += $(PLATFORM_COMMON_DIR)/printf.c\n"
  },
  {
    "path": "platforms/avr/sleep_led.c",
    "content": "#include <stdint.h>\n#include <avr/io.h>\n#include <avr/interrupt.h>\n#include <avr/pgmspace.h>\n#include \"led.h\"\n#include \"sleep_led.h\"\n\n#ifndef SLEEP_LED_TIMER\n#    define SLEEP_LED_TIMER 1\n#endif\n\n#if SLEEP_LED_TIMER == 1\n#    define TCCRxB TCCR1B\n#    define TIMERx_COMPA_vect TIMER1_COMPA_vect\n#    if defined(__AVR_ATmega32A__) // This MCU has only one TIMSK register\n#        define TIMSKx TIMSK\n#    else\n#        define TIMSKx TIMSK1\n#    endif\n#    define OCIExA OCIE1A\n#    define OCRxx OCR1A\n#elif SLEEP_LED_TIMER == 3\n#    define TCCRxB TCCR3B\n#    define TIMERx_COMPA_vect TIMER3_COMPA_vect\n#    define TIMSKx TIMSK3\n#    define OCIExA OCIE3A\n#    define OCRxx OCR3A\n#else\nerror(\"Invalid SLEEP_LED_TIMER config\")\n#endif\n\n/* Software PWM\n *  ______           ______           __\n * |  ON  |___OFF___|  ON  |___OFF___|   ....\n * |<-------------->|<-------------->|<- ....\n *     PWM period       PWM period\n *\n * 256              interrupts/period[resolution]\n * 64               periods/second[frequency]\n * 256*64           interrupts/second\n * F_CPU/(256*64)   clocks/interrupt\n */\n#define SLEEP_LED_TIMER_TOP F_CPU / (256 * 64)\n\n/** \\brief Sleep LED initialization\n *\n * FIXME: needs doc\n */\nvoid sleep_led_init(void) {\n    /* Timer1 setup */\n    /* CTC mode */\n    TCCRxB |= _BV(WGM12);\n    /* Clock selelct: clk/1 */\n    TCCRxB |= _BV(CS10);\n    /* Set TOP value */\n    uint8_t sreg = SREG;\n    cli();\n    OCRxx = SLEEP_LED_TIMER_TOP;\n    SREG  = sreg;\n}\n\n/** \\brief Sleep LED enable\n *\n * FIXME: needs doc\n */\nvoid sleep_led_enable(void) {\n    /* Enable Compare Match Interrupt */\n    TIMSKx |= _BV(OCIExA);\n}\n\n/** \\brief Sleep LED disable\n *\n * FIXME: needs doc\n */\nvoid sleep_led_disable(void) {\n    /* Disable Compare Match Interrupt */\n    TIMSKx &= ~_BV(OCIExA);\n}\n\n/** \\brief Sleep LED toggle\n *\n * FIXME: needs doc\n */\nvoid sleep_led_toggle(void) {\n    /* Disable Compare Match Interrupt */\n    TIMSKx ^= _BV(OCIExA);\n}\n\n/** \\brief Breathing Sleep LED brighness(PWM On period) table\n *\n * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle\n *\n * https://www.wolframalpha.com/input/?i=sin%28x%2F64*pi%29**8+*+255%2C+x%3D0+to+63\n * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i }\n */\nstatic const uint8_t breathing_table[64] PROGMEM = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10, 15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252, 255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23, 15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\n\nISR(TIMERx_COMPA_vect) {\n    /* Software PWM\n     * timer:1111 1111 1111 1111\n     *       \\_____/\\/ \\_______/____  count(0-255)\n     *          \\    \\______________  duration of step(4)\n     *           \\__________________  index of step table(0-63)\n     */\n    static union {\n        uint16_t row;\n        struct {\n            uint8_t count : 8;\n            uint8_t duration : 2;\n            uint8_t index : 6;\n        } pwm;\n    } timer                = {.row = 0};\n    static led_t led_state = {0};\n\n    timer.row++;\n\n    // LED on\n    if (timer.pwm.count == 0) {\n        led_state.caps_lock = true;\n        led_set(led_state.raw);\n    }\n    // LED off\n    if (timer.pwm.count == pgm_read_byte(&breathing_table[timer.pwm.index])) {\n        led_state.caps_lock = false;\n        led_set(led_state.raw);\n    }\n}\n"
  },
  {
    "path": "platforms/avr/suspend.c",
    "content": "#include <stdbool.h>\n#include <avr/sleep.h>\n#include <avr/wdt.h>\n#include <avr/interrupt.h>\n#include \"suspend.h\"\n#include \"action.h\"\n#include \"timer.h\"\n\n#ifdef PROTOCOL_LUFA\n#    include \"lufa.h\"\n#endif\n#ifdef PROTOCOL_VUSB\n#    include \"vusb.h\"\n#endif\n\n// TODO: This needs some cleanup\n\n#if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)\n\n// clang-format off\n#define wdt_intr_enable(value) \\\n__asm__ __volatile__ ( \\\n    \"in __tmp_reg__,__SREG__\" \"\\n\\t\" \\\n    \"cli\" \"\\n\\t\" \\\n    \"wdr\" \"\\n\\t\" \\\n    \"sts %0,%1\" \"\\n\\t\" \\\n    \"out __SREG__,__tmp_reg__\" \"\\n\\t\" \\\n    \"sts %0,%2\" \"\\n\\t\" \\\n    : /* no outputs */ \\\n    : \"M\" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \\\n    \"r\" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \\\n    \"r\" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | _BV(WDIE) | (value & 0x07))) \\\n    : \"r0\" \\\n)\n// clang-format on\n\n/** \\brief Power down MCU with watchdog timer\n *\n * wdto: watchdog timer timeout defined in <avr/wdt.h>\n *          WDTO_15MS\n *          WDTO_30MS\n *          WDTO_60MS\n *          WDTO_120MS\n *          WDTO_250MS\n *          WDTO_500MS\n *          WDTO_1S\n *          WDTO_2S\n *          WDTO_4S\n *          WDTO_8S\n */\nstatic uint8_t wdt_timeout = 0;\n\n/** \\brief Power down\n *\n * FIXME: needs doc\n */\nstatic void power_down(uint8_t wdto) {\n    wdt_timeout = wdto;\n\n    // Watchdog Interrupt Mode\n    wdt_intr_enable(wdto);\n\n    // TODO: more power saving\n    // See PicoPower application note\n    // - I/O port input with pullup\n    // - prescale clock\n    // - BOD disable\n    // - Power Reduction Register PRR\n    set_sleep_mode(SLEEP_MODE_PWR_DOWN);\n    sleep_enable();\n    sei();\n    sleep_cpu();\n    sleep_disable();\n\n    // Disable watchdog after sleep\n    wdt_disable();\n}\n\n/* watchdog timeout */\nISR(WDT_vect) {\n    // compensate timer for sleep\n    switch (wdt_timeout) {\n        case WDTO_15MS:\n            timer_count += 15 + 2; // WDTO_15MS + 2(from observation)\n            break;\n        default:;\n    }\n}\n\n#endif\n\n/** \\brief Suspend power down\n *\n * FIXME: needs doc\n */\nvoid suspend_power_down(void) {\n#ifdef PROTOCOL_LUFA\n    if (USB_DeviceState == DEVICE_STATE_Configured) return;\n#endif\n#ifdef PROTOCOL_VUSB\n    if (!vusb_suspended) return;\n#endif\n\n    suspend_power_down_quantum();\n\n#ifndef NO_SUSPEND_POWER_DOWN\n    // Enter sleep state if possible (ie, the MCU has a watchdog timeout interrupt)\n#    if defined(WDT_vect)\n    power_down(WDTO_15MS);\n#    endif\n#endif\n}\n\n/** \\brief run immediately after wakeup\n *\n * FIXME: needs doc\n */\nvoid suspend_wakeup_init(void) {\n    // clear keyboard state\n    clear_keyboard();\n\n    suspend_wakeup_init_quantum();\n}\n"
  },
  {
    "path": "platforms/avr/timer.c",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include <avr/io.h>\n#include <avr/interrupt.h>\n#include <util/atomic.h>\n#include <stdint.h>\n#include \"timer_avr.h\"\n#include \"timer.h\"\n\n// counter resolution 1ms\n// NOTE: union { uint32_t timer32; struct { uint16_t dummy; uint16_t timer16; }}\nvolatile uint32_t timer_count;\nstatic uint32_t   saved_ms;\n\n/** \\brief timer initialization\n *\n * FIXME: needs doc\n */\nvoid timer_init(void) {\n#if TIMER_PRESCALER == 1\n    uint8_t prescaler = _BV(CS00);\n#elif TIMER_PRESCALER == 8\n    uint8_t prescaler = _BV(CS01);\n#elif TIMER_PRESCALER == 64\n    uint8_t prescaler = _BV(CS00) | _BV(CS01);\n#elif TIMER_PRESCALER == 256\n    uint8_t prescaler = _BV(CS02);\n#elif TIMER_PRESCALER == 1024\n    uint8_t prescaler = _BV(CS00) | _BV(CS02);\n#else\n#    error \"Timer prescaler value is not valid\"\n#endif\n\n#if defined(__AVR_ATmega32A__)\n    // Timer0 CTC mode\n    TCCR0 = _BV(WGM01) | prescaler;\n\n    OCR0  = TIMER_RAW_TOP;\n    TIMSK = _BV(OCIE0);\n#elif defined(__AVR_ATtiny85__)\n    // Timer0 CTC mode\n    TCCR0A = _BV(WGM01);\n    TCCR0B = prescaler;\n\n    OCR0A = TIMER_RAW_TOP;\n    TIMSK = _BV(OCIE0A);\n#else\n    // Timer0 CTC mode\n    TCCR0A = _BV(WGM01);\n    TCCR0B = prescaler;\n\n    OCR0A  = TIMER_RAW_TOP;\n    TIMSK0 = _BV(OCIE0A);\n#endif\n}\n\n/** \\brief timer clear\n *\n * FIXME: needs doc\n */\ninline void timer_clear(void) {\n    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {\n        timer_count = 0;\n    }\n}\n\n/** \\brief timer save\n *\n * Set saved_ms to current time.\n */\nvoid timer_save(void) {\n    saved_ms = timer_read32();\n}\n\n/** \\brief timer restore\n *\n * Set timer_count to saved_ms\n */\nvoid timer_restore(void) {\n    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {\n        timer_count = saved_ms;\n    }\n}\n\n/** \\brief timer read\n *\n * FIXME: needs doc\n */\ninline uint16_t timer_read(void) {\n    uint32_t t;\n\n    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {\n        t = timer_count;\n    }\n\n    return (t & 0xFFFF);\n}\n\n/** \\brief timer read32\n *\n * FIXME: needs doc\n */\ninline uint32_t timer_read32(void) {\n    uint32_t t;\n\n    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {\n        t = timer_count;\n    }\n\n    return t;\n}\n\n// excecuted once per 1ms.(excess for just timer count?)\n#ifndef __AVR_ATmega32A__\n#    define TIMER_INTERRUPT_VECTOR TIMER0_COMPA_vect\n#else\n#    define TIMER_INTERRUPT_VECTOR TIMER0_COMP_vect\n#endif\nISR(TIMER_INTERRUPT_VECTOR, ISR_NOBLOCK) {\n    timer_count++;\n}\n"
  },
  {
    "path": "platforms/avr/timer_avr.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n\n#ifndef TIMER_PRESCALER\n#    if F_CPU > 16000000\n#        define TIMER_PRESCALER 256\n#    elif F_CPU > 2000000\n#        define TIMER_PRESCALER 64\n#    elif F_CPU > 250000\n#        define TIMER_PRESCALER 8\n#    else\n#        define TIMER_PRESCALER 1\n#    endif\n#endif\n#define TIMER_RAW_FREQ (F_CPU / TIMER_PRESCALER)\n#define TIMER_RAW TCNT0\n#define TIMER_RAW_TOP (TIMER_RAW_FREQ / 1000)\n\n#if (TIMER_RAW_TOP > 255)\n#    error \"Timer0 can't count 1ms at this clock freq. Use larger prescaler.\"\n#endif\n"
  },
  {
    "path": "platforms/avr/xprintf.S",
    "content": ";---------------------------------------------------------------------------;\n; Extended itoa, puts, printf and atoi                     (C)ChaN, 2011\n;---------------------------------------------------------------------------;\n\n\t\t\t\t// Base size is 152 bytes\n#define\tCR_CRLF\t\t0\t// Convert \\n to \\r\\n (+10 bytes)\n#define USE_XPRINTF\t1\t// Enable xprintf function (+194 bytes)\n#define USE_XSPRINTF\t0\t// Add xsprintf function (+78 bytes)\n#define USE_XFPRINTF\t0\t// Add xfprintf function (+54 bytes)\n#define USE_XATOI\t0\t// Enable xatoi function (+182 bytes)\n\n\n#if FLASHEND > 0x1FFFF\n#error xitoa module does not support 256K devices\n#endif\n\n.nolist\n#include <avr/io.h>\t// Include device specific definitions.\n.list\n\n#ifdef SPM_PAGESIZE\t// Recent devices have \"lpm Rd,Z+\" and \"movw\".\n.macro\t_LPMI\treg\n\tlpm\t\\reg, Z+\n.endm\n.macro\t_MOVW\tdh,dl, sh,sl\n\tmovw\t\\dl, \\sl\n.endm\n#else\t\t\t// Earlier devices do not have \"lpm Rd,Z+\" nor \"movw\".\n.macro\t_LPMI\treg\n\tlpm\n\tmov\t\\reg, r0\n\tadiw\tZL, 1\n.endm\n.macro\t_MOVW\tdh,dl, sh,sl\n\tmov\t\\dl, \\sl\n\tmov\t\\dh, \\sh\n.endm\n#endif\n\n\n\n;---------------------------------------------------------------------------\n; Stub function to forward to user output function\n;\n;Prototype: void xputc (char chr\t// a character to be output\n;\t\t\t);\n;Size: 12/12 words\n\n.section .bss\n.global xfunc_out\t; xfunc_out must be initialized before using this module.\nxfunc_out:\t.ds.w\t1\n.section .text\n\n\n.func xputc\n.global xputc\nxputc:\n#if CR_CRLF\n\tcpi\tr24, 10\t\t;LF --> CRLF\n\tbrne\t1f\t\t;\n\tldi\tr24, 13\t\t;\n\trcall\t1f\t\t;\n\tldi\tr24, 10\t\t;/\n1:\n#endif\n\tpush\tZH\n\tpush\tZL\n\tlds\tZL, xfunc_out+0\t;Pointer to the registered output function.\n\tlds\tZH, xfunc_out+1\t;/\n\tsbiw\tZL, 0\t\t;Skip if null\n\tbreq\t2f\t\t;/\n\ticall\n2:\tpop\tZL\n\tpop\tZH\n\tret\n.endfunc\n\n\n\n;---------------------------------------------------------------------------\n; Direct ROM string output\n;\n;Prototype: void xputs (const char *str_p // rom string to be output\n;\t\t\t);\n\n.func xputs\n.global xputs\nxputs:\n\t_MOVW\tZH,ZL, r25,r24\t; Z = pointer to rom string\n1:\t_LPMI\tr24\n\tcpi\tr24, 0\n\tbreq\t2f\n\trcall\txputc\n\trjmp\t1b\n2:\tret\n.endfunc\n\n\n;---------------------------------------------------------------------------\n; Extended direct numeral string output (32bit version)\n;\n;Prototype: void xitoa (long value,\t// value to be output\n;                       char radix,\t// radix\n;                       char width);\t// minimum width\n;\n\n.func xitoa\n.global xitoa\nxitoa:\n\t\t\t\t;r25:r22 = value, r20 = base, r18 = digits\n\tclr\tr31\t\t;r31 = stack level\n\tldi\tr30, ' '\t;r30 = sign\n\tldi\tr19, ' '\t;r19 = filler\n\tsbrs\tr20, 7\t\t;When base indicates signd format and the value\n\trjmp\t0f\t\t;is minus, add a '-'.\n\tneg\tr20\t\t;\n\tsbrs\tr25, 7\t\t;\n\trjmp\t0f\t\t;\n\tldi\tr30, '-'\t;\n\tcom\tr22\t\t;\n\tcom\tr23\t\t;\n\tcom\tr24\t\t;\n\tcom\tr25\t\t;\n\tadc\tr22, r1\t\t;\n\tadc\tr23, r1\t\t;\n\tadc\tr24, r1\t\t;\n\tadc\tr25, r1\t\t;/\n0:\tsbrs\tr18, 7\t\t;When digits indicates zero filled,\n\trjmp\t1f\t\t;filler is '0'.\n\tneg\tr18\t\t;\n\tldi\tr19, '0'\t;/\n\t\t\t\t;----- string conversion loop\n1:\tldi\tr21, 32\t\t;r26 = r25:r22 % r20\n\tclr\tr26\t\t;r25:r22 /= r20\n2:\tlsl\tr22\t\t;\n\trol\tr23\t\t;\n\trol\tr24\t\t;\n\trol\tr25\t\t;\n\trol\tr26\t\t;\n\tcp\tr26, r20\t;\n\tbrcs\t3f\t\t;\n\tsub\tr26, r20\t;\n\tinc\tr22\t\t;\n3:\tdec\tr21\t\t;\n\tbrne\t2b\t\t;/\n\tcpi\tr26, 10\t\t;r26 is a numeral digit '0'-'F'\n\tbrcs\t4f\t\t;\n\tsubi\tr26, -7\t\t;\n4:\tsubi\tr26, -'0'\t;/\n\tpush\tr26\t\t;Stack it\n\tinc\tr31\t\t;/\n\tcp\tr22, r1\t\t;Repeat until r25:r22 gets zero\n\tcpc\tr23, r1\t\t;\n\tcpc\tr24, r1\t\t;\n\tcpc\tr25, r1\t\t;\n\tbrne\t1b\t\t;/\n\n\tcpi\tr30, '-'\t;Minus sign if needed\n\tbrne\t5f\t\t;\n\tpush\tr30\t\t;\n\tinc\tr31\t\t;/\n5:\tcp\tr31, r18\t;Filler\n\tbrcc\t6f\t\t;\n\tpush\tr19\t\t;\n\tinc\tr31\t\t;\n\trjmp\t5b\t\t;/\n\n6:\tpop\tr24\t\t;Flush stacked digits and exit\n\trcall\txputc\t\t;\n\tdec\tr31\t\t;\n\tbrne\t6b\t\t;/\n\n\tret\n.endfunc\n\n\n\n;---------------------------------------------------------------------------;\n; Formatted string output (16/32bit version)\n;\n;Prototype:\n; void __xprintf (const char *format_p, ...);\n; void __xsprintf(char*, const char *format_p, ...);\n; void __xfprintf(void(*func)(char), const char *format_p, ...);\n;\n\n#if USE_XPRINTF\n\n.func xvprintf\nxvprintf:\n\tld\tZL, Y+\t\t;Z = pointer to format string\n\tld\tZH, Y+\t\t;/\n\n0:\t_LPMI\tr24\t\t;Get a format char\n\tcpi\tr24, 0\t\t;End of format string?\n\tbreq\t90f\t\t;/\n\tcpi\tr24, '%'\t;Is format?\n\tbreq\t20f\t\t;/\n1:\trcall\txputc\t\t;Put a normal character\n\trjmp\t0b\t\t;/\n90:\tret\n\n20:\tldi\tr18, 0\t\t;r18: digits\n\tclt\t\t\t;T: filler\n\t_LPMI\tr21\t\t;Get flags\n\tcpi\tr21, '%'\t;Is a %?\n\tbreq\t1b\t\t;/\n\tcpi\tr21, '0'\t;Zero filled?\n\tbrne\t23f\t\t;\n\tset\t\t\t;/\n22:\t_LPMI\tr21\t\t;Get width\n23:\tcpi\tr21, '9'+1\t;\n\tbrcc\t24f\t\t;\n\tsubi\tr21, '0'\t;\n\tbrcs\t90b\t\t;\n\tlsl\tr18\t\t;\n\tmov\tr0, r18\t\t;\n\tlsl\tr18\t\t;\n\tlsl\tr18\t\t;\n\tadd\tr18, r0\t\t;\n\tadd\tr18, r21\t;\n\trjmp\t22b\t\t;/\n\n24:\tbrtc\t25f\t\t;get value (low word)\n\tneg\tr18\t\t;\n25:\tld\tr24, Y+\t\t;\n\tld\tr25, Y+\t\t;/\n\tcpi\tr21, 'c'\t;Is type character?\n\tbreq\t1b\t\t;/\n\tcpi\tr21, 's'\t;Is type RAM string?\n\tbreq\t50f\t\t;/\n\tcpi\tr21, 'S'\t;Is type ROM string?\n\tbreq\t60f\t\t;/\n\t_MOVW\tr23,r22,r25,r24\t;r25:r22 = value\n\tclr\tr24\t\t;\n\tclr\tr25\t\t;\n\tclt\t\t\t;/\n\tcpi\tr21, 'l'\t;Is long int?\n\tbrne\t26f\t\t;\n\tld\tr24, Y+\t\t;get value (high word)\n\tld\tr25, Y+\t\t;\n\tset\t\t\t;\n\t_LPMI\tr21\t\t;/\n26:\tcpi\tr21, 'd'\t;Is type signed decimal?\n\tbrne\t27f\t\t;/\n\tldi\tr20, -10\t;\n\tbrts\t40f\t\t;\n\tsbrs\tr23, 7\t\t;\n\trjmp\t40f\t\t;\n\tldi\tr24, -1\t\t;\n\tldi\tr25, -1\t\t;\n\trjmp\t40f\t\t;/\n27:\tcpi\tr21, 'u'\t;Is type unsigned decimal?\n\tldi\tr20, 10\t\t;\n\tbreq\t40f\t\t;/\n\tcpi\tr21, 'X'\t;Is type hexdecimal?\n\tldi\tr20, 16\t\t;\n\tbreq\t40f\t\t;/\n\tcpi\tr21, 'b'\t;Is type binary?\n\tldi\tr20, 2\t\t;\n\tbreq\t40f\t\t;/\n\tret\t\t\t;abort\n40:\tpush\tZH\t\t;Output the value\n\tpush\tZL\t\t;\n\trcall\txitoa\t\t;\n42:\tpop\tZL\t\t;\n\tpop\tZH\t\t;\n\trjmp\t0b\t\t;/\n\n50:\tpush\tZH\t\t;Put a string on the RAM\n\tpush\tZL\n\t_MOVW\tZH,ZL, r25,r24\n51:\tld\tr24, Z+\n\tcpi\tr24, 0\n\tbreq\t42b\n\trcall\txputc\n\trjmp\t51b\n\n60:\tpush\tZH\t\t;Put a string on the ROM\n\tpush\tZL\n\trcall\txputs\n\trjmp\t42b\n.endfunc\n\n\n.func __xprintf\n.global __xprintf\n__xprintf:\n\tpush\tYH\n\tpush\tYL\n\tin\tYL, _SFR_IO_ADDR(SPL)\n#ifdef SPH\n\tin\tYH, _SFR_IO_ADDR(SPH)\n#else\n\tclr\tYH\n#endif\n\tadiw\tYL, 5\t\t;Y = pointer to arguments\n\trcall\txvprintf\n\tpop\tYL\n\tpop\tYH\n\tret\n.endfunc\n\n\n#if USE_XSPRINTF\n\n.func __xsprintf\nputram:\n\t_MOVW\tZH,ZL, r15,r14\n\tst\tZ+, r24\n\t_MOVW\tr15,r14, ZH,ZL\n\tret\n.global __xsprintf\n__xsprintf:\n\tpush\tYH\n\tpush\tYL\n\tin\tYL, _SFR_IO_ADDR(SPL)\n#ifdef SPH\n\tin\tYH, _SFR_IO_ADDR(SPH)\n#else\n\tclr\tYH\n#endif\n\tadiw\tYL, 5\t\t;Y = pointer to arguments\n\tlds\tZL, xfunc_out+0\t;Save registered output function\n\tlds\tZH, xfunc_out+1\t;\n\tpush\tZL\t\t;\n\tpush\tZH\t\t;/\n\tldi\tZL, lo8(pm(putram));Set local output function\n\tldi\tZH, hi8(pm(putram));\n\tsts\txfunc_out+0, ZL\t;\n\tsts\txfunc_out+1, ZH\t;/\n\tpush\tr15\t\t;Initialize pointer to string buffer\n\tpush\tr14\t\t;\n\tld\tr14, Y+\t\t;\n\tld\tr15, Y+\t\t;/\n\trcall\txvprintf\n\t_MOVW\tZH,ZL, r15,r14\t;Terminate string\n\tst\tZ, r1\t\t;\n\tpop\tr14\t\t;\n\tpop\tr15\t\t;/\n\tpop\tZH\t\t;Restore registered output function\n\tpop\tZL\t\t;\n\tsts\txfunc_out+0, ZL\t;\n\tsts\txfunc_out+1, ZH\t;/\n\tpop\tYL\n\tpop\tYH\n\tret\n.endfunc\n#endif\n\n\n#if USE_XFPRINTF\n.func __xfprintf\n.global __xfprintf\n__xfprintf:\n\tpush\tYH\n\tpush\tYL\n\tin\tYL, _SFR_IO_ADDR(SPL)\n#ifdef SPH\n\tin\tYH, _SFR_IO_ADDR(SPH)\n#else\n\tclr\tYH\n#endif\n\tadiw\tYL, 5\t\t;Y = pointer to arguments\n\tlds\tZL, xfunc_out+0\t;Save registered output function\n\tlds\tZH, xfunc_out+1\t;\n\tpush\tZL\t\t;\n\tpush\tZH\t\t;/\n\tld\tZL, Y+\t\t;Set output function\n\tld\tZH, Y+\t\t;\n\tsts\txfunc_out+0, ZL\t;\n\tsts\txfunc_out+1, ZH\t;/\n\trcall\txvprintf\n\tpop\tZH\t\t;Restore registered output function\n\tpop\tZL\t\t;\n\tsts\txfunc_out+0, ZL\t;\n\tsts\txfunc_out+1, ZH\t;/\n\tpop\tYL\n\tpop\tYH\n\tret\n.endfunc\n#endif\n\n#endif\n\n\n\n;---------------------------------------------------------------------------\n; Extended numeral string input\n;\n;Prototype:\n; char xatoi (           /* 1: Successful, 0: Failed */\n;      const char **str, /* pointer to pointer to source string */\n;      long *res         /* result */\n; );\n;\n\n\n#if USE_XATOI\n.func xatoi\n.global xatoi\nxatoi:\n\t_MOVW\tr1, r0, r23, r22\n\t_MOVW\tXH, XL, r25, r24\n\tld\tZL, X+\n\tld\tZH, X+\n\tclr\tr18\t\t;r21:r18 = 0;\n\tclr\tr19\t\t;\n\tclr\tr20\t\t;\n\tclr\tr21\t\t;/\n\tclt\t\t\t;T = 0;\n\n\tldi\tr25, 10\t\t;r25 = 10;\n\trjmp\t41f\t\t;/\n40:\tadiw\tZL, 1\t\t;Z++;\n41:\tld\tr22, Z\t\t;r22 = *Z;\n\tcpi\tr22, ' '\t;if(r22 == ' ') continue\n\tbreq\t40b\t\t;/\n\tbrcs\t70f\t\t;if(r22 < ' ') error;\n\tcpi\tr22, '-'\t;if(r22 == '-') {\n\tbrne\t42f\t\t; T = 1;\n\tset\t\t\t; continue;\n\trjmp\t40b\t\t;}\n42:\tcpi\tr22, '9'+1\t;if(r22 > '9') error;\n\tbrcc\t70f\t\t;/\n\tcpi\tr22, '0'\t;if(r22 < '0') error;\n\tbrcs\t70f\t\t;/\n\tbrne\t51f\t\t;if(r22 > '0') cv_start;\n\tldi\tr25, 8\t\t;r25 = 8;\n\tadiw\tZL, 1\t\t;r22 = *(++Z);\n\tld\tr22, Z\t\t;/\n\tcpi\tr22, ' '+1\t;if(r22 <= ' ') exit;\n\tbrcs\t80f\t\t;/\n\tcpi\tr22, 'b'\t;if(r22 == 'b') {\n\tbrne\t43f\t\t; r25 = 2;\n\tldi\tr25, 2\t\t; cv_start;\n\trjmp\t50f\t\t;}\n43:\tcpi\tr22, 'x'\t;if(r22 != 'x') error;\n\tbrne\t51f\t\t;/\n\tldi\tr25, 16\t\t;r25 = 16;\n\n50:\tadiw\tZL, 1\t\t;Z++;\n\tld\tr22, Z\t\t;r22 = *Z;\n51:\tcpi\tr22, ' '+1\t;if(r22 <= ' ') break;\n\tbrcs\t80f\t\t;/\n\tcpi\tr22, 'a'\t;if(r22 >= 'a') r22 =- 0x20;\n\tbrcs\t52f\t\t;\n\tsubi\tr22, 0x20\t;/\n52:\tsubi\tr22, '0'\t;if((r22 -= '0') < 0) error;\n\tbrcs\t70f\t\t;/\n\tcpi\tr22, 10\t\t;if(r22 >= 10) {\n\tbrcs\t53f\t\t; r22 -= 7;\n\tsubi\tr22, 7\t\t; if(r22 < 10)\n\tcpi\tr22, 10\t\t;\n\tbrcs\t70f\t\t;}\n53:\tcp\tr22, r25\t;if(r22 >= r25) error;\n\tbrcc\t70f\t\t;/\n60:\tldi\tr24, 33\t\t;r21:r18 *= r25;\n\tsub\tr23, r23\t;\n61:\tbrcc\t62f\t\t;\n\tadd\tr23, r25\t;\n62:\tlsr\tr23\t\t;\n\tror\tr21\t\t;\n\tror\tr20\t\t;\n\tror\tr19\t\t;\n\tror\tr18\t\t;\n\tdec\tr24\t\t;\n\tbrne\t61b\t\t;/\n\tadd\tr18, r22\t;r21:r18 += r22;\n\tadc\tr19, r24\t;\n\tadc\tr20, r24\t;\n\tadc\tr21, r24\t;/\n\trjmp\t50b\t\t;repeat\n\n70:\tldi\tr24, 0\n\trjmp\t81f\n80:\tldi\tr24, 1\n81:\tbrtc\t82f\n\tclr\tr22\n\tcom\tr18\n\tcom\tr19\n\tcom\tr20\n\tcom\tr21\n\tadc\tr18, r22\n\tadc\tr19, r22\n\tadc\tr20, r22\n\tadc\tr21, r22\n82:\tst\t-X, ZH\n\tst\t-X, ZL\n\t_MOVW\tXH, XL, r1, r0\n\tst\tX+, r18\n\tst\tX+, r19\n\tst\tX+, r20\n\tst\tX+, r21\n\tclr\tr1\n\tret\n.endfunc\n#endif\n"
  },
  {
    "path": "platforms/avr/xprintf.h",
    "content": "/*---------------------------------------------------------------------------\n   Extended itoa, puts and printf                    (C)ChaN, 2011\n-----------------------------------------------------------------------------*/\n\n#pragma once\n\n#include <inttypes.h>\n#include <avr/pgmspace.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern void (*xfunc_out)(uint8_t);\n#define xdev_out(func) xfunc_out = (void (*)(uint8_t))(func)\n\n/* This is a pointer to user defined output function. It must be initialized\n   before using this modle.\n*/\n\nvoid xputc(char chr);\n\n/* This is a stub function to forward outputs to user defined output function.\n   All outputs from this module are output via this function.\n*/\n\n/*-----------------------------------------------------------------------------*/\nvoid xputs(const char *string_p);\n\n/*  The string placed in the ROM is forwarded to xputc() directly.\n */\n\n/*-----------------------------------------------------------------------------*/\nvoid xitoa(long value, char radix, char width);\n\n/* Extended itoa().\n\n      value  radix  width   output\n        100     10      6   \"   100\"\n        100     10     -6   \"000100\"\n        100     10      0   \"100\"\n 4294967295     10      0   \"4294967295\"\n 4294967295    -10      0   \"-1\"\n     655360     16     -8   \"000A0000\"\n       1024     16      0   \"400\"\n       0x55      2     -8   \"01010101\"\n*/\n\n/*-----------------------------------------------------------------------------*/\n#define xprintf(format, ...) __xprintf(PSTR(format), ##__VA_ARGS__)\n#define xsprintf(str, format, ...) __xsprintf(str, PSTR(format), ##__VA_ARGS__)\n#define xfprintf(func, format, ...) __xfprintf(func, PSTR(format), ##__VA_ARGS__)\n\nvoid __xprintf(const char *format_p, ...); /* Send formatted string to the registered device */\n// void __xsprintf(char*, const char *format_p, ...);\t/* Put formatted string to the memory */\n// void __xfprintf(void(*func)(uint8_t), const char *format_p, ...); /* Send formatted string to the specified device */\n\n/* Format string is placed in the ROM. The format flags is similar to printf().\n\n   %[flag][width][size]type\n\n   flag\n     A '0' means filled with '0' when output is shorter than width.\n     ' ' is used in default. This is effective only numeral type.\n   width\n     Minimum width in decimal number. This is effective only numeral type.\n     Default width is zero.\n   size\n     A 'l' means the argument is long(32bit). Default is short(16bit).\n     This is effective only numeral type.\n   type\n     'c' : Character, argument is the value\n     's' : String placed on the RAM, argument is the pointer\n     'S' : String placed on the ROM, argument is the pointer\n     'd' : Signed decimal, argument is the value\n     'u' : Unsigned decimal, argument is the value\n     'X' : Hexdecimal, argument is the value\n     'b' : Binary, argument is the value\n     '%' : '%'\n\n*/\n\n/*-----------------------------------------------------------------------------*/\nchar xatoi(char **str, long *ret);\n\n/* Get value of the numeral string.\n\n  str\n    Pointer to pointer to source string\n\n    \"0b11001010\" binary\n    \"0377\" octal\n    \"0xff800\" hexdecimal\n    \"1250000\" decimal\n    \"-25000\" decimal\n\n  ret\n    Pointer to return value\n*/\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "platforms/bootloader.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n/* give code for your bootloader to come up if needed */\nvoid bootloader_jump(void);\nvoid mcu_reset(void);\n"
  },
  {
    "path": "platforms/chibios/_pin_defs.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#if defined(MCU_KINETIS)\n// TODO: including this avoids \"error: expected identifier before '(' token\" errors\n//       here just to please KINETIS builds...\n#    include <hal.h>\n#endif\n\n/* Include the vendor specific pin defs */\n#if __has_include_next(\"_pin_defs.h\")\n#    include_next \"_pin_defs.h\"\n#else\n#    define A0 PAL_LINE(GPIOA, 0)\n#    define A1 PAL_LINE(GPIOA, 1)\n#    define A2 PAL_LINE(GPIOA, 2)\n#    define A3 PAL_LINE(GPIOA, 3)\n#    define A4 PAL_LINE(GPIOA, 4)\n#    define A5 PAL_LINE(GPIOA, 5)\n#    define A6 PAL_LINE(GPIOA, 6)\n#    define A7 PAL_LINE(GPIOA, 7)\n#    define A8 PAL_LINE(GPIOA, 8)\n#    define A9 PAL_LINE(GPIOA, 9)\n#    define A10 PAL_LINE(GPIOA, 10)\n#    define A11 PAL_LINE(GPIOA, 11)\n#    define A12 PAL_LINE(GPIOA, 12)\n#    define A13 PAL_LINE(GPIOA, 13)\n#    define A14 PAL_LINE(GPIOA, 14)\n#    define A15 PAL_LINE(GPIOA, 15)\n#    define A16 PAL_LINE(GPIOA, 16)\n#    define A17 PAL_LINE(GPIOA, 17)\n#    define A18 PAL_LINE(GPIOA, 18)\n#    define A19 PAL_LINE(GPIOA, 19)\n#    define A20 PAL_LINE(GPIOA, 20)\n#    define A21 PAL_LINE(GPIOA, 21)\n#    define A22 PAL_LINE(GPIOA, 22)\n#    define A23 PAL_LINE(GPIOA, 23)\n#    define A24 PAL_LINE(GPIOA, 24)\n#    define A25 PAL_LINE(GPIOA, 25)\n#    define A26 PAL_LINE(GPIOA, 26)\n#    define A27 PAL_LINE(GPIOA, 27)\n#    define A28 PAL_LINE(GPIOA, 28)\n#    define A29 PAL_LINE(GPIOA, 29)\n#    define A30 PAL_LINE(GPIOA, 30)\n#    define A31 PAL_LINE(GPIOA, 31)\n#    define A32 PAL_LINE(GPIOA, 32)\n#    define B0 PAL_LINE(GPIOB, 0)\n#    define B1 PAL_LINE(GPIOB, 1)\n#    define B2 PAL_LINE(GPIOB, 2)\n#    define B3 PAL_LINE(GPIOB, 3)\n#    define B4 PAL_LINE(GPIOB, 4)\n#    define B5 PAL_LINE(GPIOB, 5)\n#    define B6 PAL_LINE(GPIOB, 6)\n#    define B7 PAL_LINE(GPIOB, 7)\n#    define B8 PAL_LINE(GPIOB, 8)\n#    define B9 PAL_LINE(GPIOB, 9)\n#    define B10 PAL_LINE(GPIOB, 10)\n#    define B11 PAL_LINE(GPIOB, 11)\n#    define B12 PAL_LINE(GPIOB, 12)\n#    define B13 PAL_LINE(GPIOB, 13)\n#    define B14 PAL_LINE(GPIOB, 14)\n#    define B15 PAL_LINE(GPIOB, 15)\n#    define B16 PAL_LINE(GPIOB, 16)\n#    define B17 PAL_LINE(GPIOB, 17)\n#    define B18 PAL_LINE(GPIOB, 18)\n#    define B19 PAL_LINE(GPIOB, 19)\n#    define B20 PAL_LINE(GPIOB, 20)\n#    define B21 PAL_LINE(GPIOB, 21)\n#    define B22 PAL_LINE(GPIOB, 22)\n#    define B23 PAL_LINE(GPIOB, 23)\n#    define B24 PAL_LINE(GPIOB, 24)\n#    define B25 PAL_LINE(GPIOB, 25)\n#    define B26 PAL_LINE(GPIOB, 26)\n#    define B27 PAL_LINE(GPIOB, 27)\n#    define B28 PAL_LINE(GPIOB, 28)\n#    define B29 PAL_LINE(GPIOB, 29)\n#    define B30 PAL_LINE(GPIOB, 30)\n#    define B31 PAL_LINE(GPIOB, 31)\n#    define B32 PAL_LINE(GPIOB, 32)\n#    define C0 PAL_LINE(GPIOC, 0)\n#    define C1 PAL_LINE(GPIOC, 1)\n#    define C2 PAL_LINE(GPIOC, 2)\n#    define C3 PAL_LINE(GPIOC, 3)\n#    define C4 PAL_LINE(GPIOC, 4)\n#    define C5 PAL_LINE(GPIOC, 5)\n#    define C6 PAL_LINE(GPIOC, 6)\n#    define C7 PAL_LINE(GPIOC, 7)\n#    define C8 PAL_LINE(GPIOC, 8)\n#    define C9 PAL_LINE(GPIOC, 9)\n#    define C10 PAL_LINE(GPIOC, 10)\n#    define C11 PAL_LINE(GPIOC, 11)\n#    define C12 PAL_LINE(GPIOC, 12)\n#    define C13 PAL_LINE(GPIOC, 13)\n#    define C14 PAL_LINE(GPIOC, 14)\n#    define C15 PAL_LINE(GPIOC, 15)\n#    define C16 PAL_LINE(GPIOC, 16)\n#    define C17 PAL_LINE(GPIOC, 17)\n#    define C18 PAL_LINE(GPIOC, 18)\n#    define C19 PAL_LINE(GPIOC, 19)\n#    define C20 PAL_LINE(GPIOC, 20)\n#    define C21 PAL_LINE(GPIOC, 21)\n#    define C22 PAL_LINE(GPIOC, 22)\n#    define C23 PAL_LINE(GPIOC, 23)\n#    define C24 PAL_LINE(GPIOC, 24)\n#    define C25 PAL_LINE(GPIOC, 25)\n#    define C26 PAL_LINE(GPIOC, 26)\n#    define C27 PAL_LINE(GPIOC, 27)\n#    define C28 PAL_LINE(GPIOC, 28)\n#    define C29 PAL_LINE(GPIOC, 29)\n#    define C30 PAL_LINE(GPIOC, 30)\n#    define C31 PAL_LINE(GPIOC, 31)\n#    define C32 PAL_LINE(GPIOC, 32)\n#    define D0 PAL_LINE(GPIOD, 0)\n#    define D1 PAL_LINE(GPIOD, 1)\n#    define D2 PAL_LINE(GPIOD, 2)\n#    define D3 PAL_LINE(GPIOD, 3)\n#    define D4 PAL_LINE(GPIOD, 4)\n#    define D5 PAL_LINE(GPIOD, 5)\n#    define D6 PAL_LINE(GPIOD, 6)\n#    define D7 PAL_LINE(GPIOD, 7)\n#    define D8 PAL_LINE(GPIOD, 8)\n#    define D9 PAL_LINE(GPIOD, 9)\n#    define D10 PAL_LINE(GPIOD, 10)\n#    define D11 PAL_LINE(GPIOD, 11)\n#    define D12 PAL_LINE(GPIOD, 12)\n#    define D13 PAL_LINE(GPIOD, 13)\n#    define D14 PAL_LINE(GPIOD, 14)\n#    define D15 PAL_LINE(GPIOD, 15)\n#    define D16 PAL_LINE(GPIOD, 16)\n#    define D17 PAL_LINE(GPIOD, 17)\n#    define D18 PAL_LINE(GPIOD, 18)\n#    define D19 PAL_LINE(GPIOD, 19)\n#    define D20 PAL_LINE(GPIOD, 20)\n#    define D21 PAL_LINE(GPIOD, 21)\n#    define D22 PAL_LINE(GPIOD, 22)\n#    define D23 PAL_LINE(GPIOD, 23)\n#    define D24 PAL_LINE(GPIOD, 24)\n#    define D25 PAL_LINE(GPIOD, 25)\n#    define D26 PAL_LINE(GPIOD, 26)\n#    define D27 PAL_LINE(GPIOD, 27)\n#    define D28 PAL_LINE(GPIOD, 28)\n#    define D29 PAL_LINE(GPIOD, 29)\n#    define D30 PAL_LINE(GPIOD, 30)\n#    define D31 PAL_LINE(GPIOD, 31)\n#    define D32 PAL_LINE(GPIOD, 32)\n#    define E0 PAL_LINE(GPIOE, 0)\n#    define E1 PAL_LINE(GPIOE, 1)\n#    define E2 PAL_LINE(GPIOE, 2)\n#    define E3 PAL_LINE(GPIOE, 3)\n#    define E4 PAL_LINE(GPIOE, 4)\n#    define E5 PAL_LINE(GPIOE, 5)\n#    define E6 PAL_LINE(GPIOE, 6)\n#    define E7 PAL_LINE(GPIOE, 7)\n#    define E8 PAL_LINE(GPIOE, 8)\n#    define E9 PAL_LINE(GPIOE, 9)\n#    define E10 PAL_LINE(GPIOE, 10)\n#    define E11 PAL_LINE(GPIOE, 11)\n#    define E12 PAL_LINE(GPIOE, 12)\n#    define E13 PAL_LINE(GPIOE, 13)\n#    define E14 PAL_LINE(GPIOE, 14)\n#    define E15 PAL_LINE(GPIOE, 15)\n#    define E16 PAL_LINE(GPIOE, 16)\n#    define E17 PAL_LINE(GPIOE, 17)\n#    define E18 PAL_LINE(GPIOE, 18)\n#    define E19 PAL_LINE(GPIOE, 19)\n#    define E20 PAL_LINE(GPIOE, 20)\n#    define E21 PAL_LINE(GPIOE, 21)\n#    define E22 PAL_LINE(GPIOE, 22)\n#    define E23 PAL_LINE(GPIOE, 23)\n#    define E24 PAL_LINE(GPIOE, 24)\n#    define E25 PAL_LINE(GPIOE, 25)\n#    define E26 PAL_LINE(GPIOE, 26)\n#    define E27 PAL_LINE(GPIOE, 27)\n#    define E28 PAL_LINE(GPIOE, 28)\n#    define E29 PAL_LINE(GPIOE, 29)\n#    define E30 PAL_LINE(GPIOE, 30)\n#    define E31 PAL_LINE(GPIOE, 31)\n#    define E32 PAL_LINE(GPIOE, 32)\n#    define F0 PAL_LINE(GPIOF, 0)\n#    define F1 PAL_LINE(GPIOF, 1)\n#    define F2 PAL_LINE(GPIOF, 2)\n#    define F3 PAL_LINE(GPIOF, 3)\n#    define F4 PAL_LINE(GPIOF, 4)\n#    define F5 PAL_LINE(GPIOF, 5)\n#    define F6 PAL_LINE(GPIOF, 6)\n#    define F7 PAL_LINE(GPIOF, 7)\n#    define F8 PAL_LINE(GPIOF, 8)\n#    define F9 PAL_LINE(GPIOF, 9)\n#    define F10 PAL_LINE(GPIOF, 10)\n#    define F11 PAL_LINE(GPIOF, 11)\n#    define F12 PAL_LINE(GPIOF, 12)\n#    define F13 PAL_LINE(GPIOF, 13)\n#    define F14 PAL_LINE(GPIOF, 14)\n#    define F15 PAL_LINE(GPIOF, 15)\n#    define G0 PAL_LINE(GPIOG, 0)\n#    define G1 PAL_LINE(GPIOG, 1)\n#    define G2 PAL_LINE(GPIOG, 2)\n#    define G3 PAL_LINE(GPIOG, 3)\n#    define G4 PAL_LINE(GPIOG, 4)\n#    define G5 PAL_LINE(GPIOG, 5)\n#    define G6 PAL_LINE(GPIOG, 6)\n#    define G7 PAL_LINE(GPIOG, 7)\n#    define G8 PAL_LINE(GPIOG, 8)\n#    define G9 PAL_LINE(GPIOG, 9)\n#    define G10 PAL_LINE(GPIOG, 10)\n#    define G11 PAL_LINE(GPIOG, 11)\n#    define G12 PAL_LINE(GPIOG, 12)\n#    define G13 PAL_LINE(GPIOG, 13)\n#    define G14 PAL_LINE(GPIOG, 14)\n#    define G15 PAL_LINE(GPIOG, 15)\n#    define H0 PAL_LINE(GPIOH, 0)\n#    define H1 PAL_LINE(GPIOH, 1)\n#    define H2 PAL_LINE(GPIOH, 2)\n#    define H3 PAL_LINE(GPIOH, 3)\n#    define H4 PAL_LINE(GPIOH, 4)\n#    define H5 PAL_LINE(GPIOH, 5)\n#    define H6 PAL_LINE(GPIOH, 6)\n#    define H7 PAL_LINE(GPIOH, 7)\n#    define H8 PAL_LINE(GPIOH, 8)\n#    define H9 PAL_LINE(GPIOH, 9)\n#    define H10 PAL_LINE(GPIOH, 10)\n#    define H11 PAL_LINE(GPIOH, 11)\n#    define H12 PAL_LINE(GPIOH, 12)\n#    define H13 PAL_LINE(GPIOH, 13)\n#    define H14 PAL_LINE(GPIOH, 14)\n#    define H15 PAL_LINE(GPIOH, 15)\n#    define I0 PAL_LINE(GPIOI, 0)\n#    define I1 PAL_LINE(GPIOI, 1)\n#    define I2 PAL_LINE(GPIOI, 2)\n#    define I3 PAL_LINE(GPIOI, 3)\n#    define I4 PAL_LINE(GPIOI, 4)\n#    define I5 PAL_LINE(GPIOI, 5)\n#    define I6 PAL_LINE(GPIOI, 6)\n#    define I7 PAL_LINE(GPIOI, 7)\n#    define I8 PAL_LINE(GPIOI, 8)\n#    define I9 PAL_LINE(GPIOI, 9)\n#    define I10 PAL_LINE(GPIOI, 10)\n#    define I11 PAL_LINE(GPIOI, 11)\n#    define I12 PAL_LINE(GPIOI, 12)\n#    define I13 PAL_LINE(GPIOI, 13)\n#    define I14 PAL_LINE(GPIOI, 14)\n#    define I15 PAL_LINE(GPIOI, 15)\n#    define J0 PAL_LINE(GPIOJ, 0)\n#    define J1 PAL_LINE(GPIOJ, 1)\n#    define J2 PAL_LINE(GPIOJ, 2)\n#    define J3 PAL_LINE(GPIOJ, 3)\n#    define J4 PAL_LINE(GPIOJ, 4)\n#    define J5 PAL_LINE(GPIOJ, 5)\n#    define J6 PAL_LINE(GPIOJ, 6)\n#    define J7 PAL_LINE(GPIOJ, 7)\n#    define J8 PAL_LINE(GPIOJ, 8)\n#    define J9 PAL_LINE(GPIOJ, 9)\n#    define J10 PAL_LINE(GPIOJ, 10)\n#    define J11 PAL_LINE(GPIOJ, 11)\n#    define J12 PAL_LINE(GPIOJ, 12)\n#    define J13 PAL_LINE(GPIOJ, 13)\n#    define J14 PAL_LINE(GPIOJ, 14)\n#    define J15 PAL_LINE(GPIOJ, 15)\n\n// Keyboards can `#define KEYBOARD_REQUIRES_GPIOK` if they need to access GPIO-K pins. These conflict with a whole\n// bunch of layout definitions, so it's intentionally left out unless absolutely required -- in that case, the\n// keyboard designer should use a different symbol when defining their layout macros.\n#    ifdef KEYBOARD_REQUIRES_GPIOK\n#        define K0 PAL_LINE(GPIOK, 0)\n#        define K1 PAL_LINE(GPIOK, 1)\n#        define K2 PAL_LINE(GPIOK, 2)\n#        define K3 PAL_LINE(GPIOK, 3)\n#        define K4 PAL_LINE(GPIOK, 4)\n#        define K5 PAL_LINE(GPIOK, 5)\n#        define K6 PAL_LINE(GPIOK, 6)\n#        define K7 PAL_LINE(GPIOK, 7)\n#        define K8 PAL_LINE(GPIOK, 8)\n#        define K9 PAL_LINE(GPIOK, 9)\n#        define K10 PAL_LINE(GPIOK, 10)\n#        define K11 PAL_LINE(GPIOK, 11)\n#        define K12 PAL_LINE(GPIOK, 12)\n#        define K13 PAL_LINE(GPIOK, 13)\n#        define K14 PAL_LINE(GPIOK, 14)\n#        define K15 PAL_LINE(GPIOK, 15)\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/_timer.h",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n// The platform is 32-bit, so prefer 32-bit timers to avoid overflow\n#define FAST_TIMER_T_SIZE 32\n"
  },
  {
    "path": "platforms/chibios/_util.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#define RESIDENT_IN_RAM(funcname) __attribute__((section(\".ram0_init.\" #funcname), noinline)) funcname\n\n#if __has_include_next(\"_util.h\")\n#    include_next \"_util.h\"\n#endif\n"
  },
  {
    "path": "platforms/chibios/_wait.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef __OPTIMIZE__\n#    pragma message \"Compiler optimizations disabled; wait_cpuclock() won't work as designed\"\n#endif\n\n#define CLOCK_DELAY_NOP8 \"nop\\n\\t nop\\n\\t nop\\n\\t nop\\n\\t   nop\\n\\t nop\\n\\t nop\\n\\t nop\\n\\t\"\n\n__attribute__((always_inline)) static inline void wait_cpuclock(unsigned int n) { /* n: 1..135 */\n    /* The argument n must be a constant expression.\n     * That way, compiler optimization will remove unnecessary code. */\n    if (n < 1) {\n        return;\n    }\n    if (n > 8) {\n        unsigned int n8 = n / 8;\n        n               = n - n8 * 8;\n        switch (n8) {\n            case 16:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 15:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 14:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 13:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 12:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 11:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 10:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 9:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 8:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 7:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 6:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 5:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 4:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 3:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 2:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 1:\n                asm volatile(CLOCK_DELAY_NOP8::: \"memory\");\n            case 0:\n                break;\n        }\n    }\n    switch (n) {\n        case 8:\n            asm volatile(\"nop\" ::: \"memory\");\n        case 7:\n            asm volatile(\"nop\" ::: \"memory\");\n        case 6:\n            asm volatile(\"nop\" ::: \"memory\");\n        case 5:\n            asm volatile(\"nop\" ::: \"memory\");\n        case 4:\n            asm volatile(\"nop\" ::: \"memory\");\n        case 3:\n            asm volatile(\"nop\" ::: \"memory\");\n        case 2:\n            asm volatile(\"nop\" ::: \"memory\");\n        case 1:\n            asm volatile(\"nop\" ::: \"memory\");\n        case 0:\n            break;\n    }\n}\n"
  },
  {
    "path": "platforms/chibios/_wait.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <ch.h>\n#include <hal.h>\n#include \"chibios_config.h\"\n\n/* chThdSleepX of zero maps to infinite - so we map to a tiny delay to still yield */\n#define wait_ms(ms)                     \\\n    do {                                \\\n        if (ms != 0) {                  \\\n            chThdSleepMilliseconds(ms); \\\n        } else {                        \\\n            chThdSleepMicroseconds(1);  \\\n        }                               \\\n    } while (0)\n\n#ifdef WAIT_US_TIMER\nvoid wait_us(uint16_t duration);\n#elif PORT_SUPPORTS_RT == TRUE\n#    define wait_us(us)                                            \\\n        do {                                                       \\\n            chSysPolledDelayX(US2RTC(REALTIME_COUNTER_CLOCK, us)); \\\n        } while (0)\n#else\n#    define wait_us(us)                     \\\n        do {                                \\\n            if (us != 0) {                  \\\n                chThdSleepMicroseconds(us); \\\n            } else {                        \\\n                chThdSleepMicroseconds(1);  \\\n            }                               \\\n        } while (0)\n#endif\n\n#include \"_wait.c\"\n\n/* For GPIOs on ARM-based MCUs, the input pins are sampled by the clock of the bus\n * to which the GPIO is connected.\n * The connected buses differ depending on the various series of MCUs.\n * And since the instruction execution clock of the CPU and the bus clock of GPIO are different,\n * there is a delay of several clocks to read the change of the input signal.\n *\n * Define this delay with the GPIO_INPUT_PIN_DELAY macro.\n * If the GPIO_INPUT_PIN_DELAY macro is not defined, the following default values will be used.\n * (A fairly large value of 0.25 microseconds is set.)\n */\n#ifndef GPIO_INPUT_PIN_DELAY\n#    define GPIO_INPUT_PIN_DELAY (CPU_CLOCK / 1000000L / 4)\n#endif\n\n#define waitInputPinDelay() wait_cpuclock(GPIO_INPUT_PIN_DELAY)\n"
  },
  {
    "path": "platforms/chibios/atomic_util.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <ch.h>\n\nstatic __inline__ uint8_t __interrupt_disable__(void) {\n    chSysLock();\n\n    return 1;\n}\n\nstatic __inline__ void __interrupt_enable__(const uint8_t *__s) {\n    chSysUnlock();\n\n    __asm__ volatile(\"\" ::: \"memory\");\n    (void)__s;\n}\n\nstatic __inline__ syssts_t __interrupt_lock__(void) {\n    return chSysGetStatusAndLockX();\n}\n\nstatic __inline__ void __interrupt_unlock__(const syssts_t *__s) {\n    chSysRestoreStatusX(*__s);\n\n    __asm__ volatile(\"\" ::: \"memory\");\n}\n\n#define ATOMIC_BLOCK(type) for (type, __ToDo = 1; __ToDo; __ToDo = 0)\n#define ATOMIC_FORCEON uint8_t status_save __attribute__((__cleanup__(__interrupt_enable__))) = __interrupt_disable__()\n#define ATOMIC_RESTORESTATE syssts_t status_save __attribute__((__cleanup__(__interrupt_unlock__))) = __interrupt_lock__()\n\n#define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK(ATOMIC_RESTORESTATE)\n#define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON)\n"
  },
  {
    "path": "platforms/chibios/boards/BLACKPILL_STM32_F401/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_STM32F401C_DISCOVERY/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_STM32F401C_DISCOVERY\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/BLACKPILL_STM32_F401/configs/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <board.h>\n\n// Force B9 as input to align with qmk defaults\n#undef VAL_GPIOB_MODER\n#define VAL_GPIOB_MODER             (PIN_MODE_INPUT(GPIOB_PIN0) |           \\\n                                     PIN_MODE_INPUT(GPIOB_PIN1) |           \\\n                                     PIN_MODE_INPUT(GPIOB_PIN2) |           \\\n                                     PIN_MODE_ALTERNATE(GPIOB_SWO) |        \\\n                                     PIN_MODE_INPUT(GPIOB_PIN4) |           \\\n                                     PIN_MODE_INPUT(GPIOB_PIN5) |           \\\n                                     PIN_MODE_INPUT(GPIOB_LSM303DLHC_SCL) | \\\n                                     PIN_MODE_INPUT(GPIOB_PIN7) |           \\\n                                     PIN_MODE_INPUT(GPIOB_PIN8) |           \\\n                                     PIN_MODE_INPUT(GPIOB_LSM303DLHC_SDA) | \\\n                                     PIN_MODE_ALTERNATE(GPIOB_MP45DT02_CLK_IN) |\\\n                                     PIN_MODE_INPUT(GPIOB_PIN11) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN12) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN13) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN14) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN15))\n\n#undef VAL_GPIOB_PUPDR\n#define VAL_GPIOB_PUPDR             (PIN_PUPDR_PULLUP(GPIOB_PIN0) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN1) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN2) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_SWO) |          \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN4) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN5) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_LSM303DLHC_SCL) |\\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN7) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN8) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_LSM303DLHC_SDA) |\\\n                                     PIN_PUPDR_FLOATING(GPIOB_MP45DT02_CLK_IN) |\\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN11) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN12) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN13) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN14) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN15))\n\n#undef VAL_GPIOB_AFRL\n#define VAL_GPIOB_AFRL              (PIN_AFIO_AF(GPIOB_PIN0, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_PIN1, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_PIN2, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_SWO, 0U) |           \\\n                                     PIN_AFIO_AF(GPIOB_PIN4, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_PIN5, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_LSM303DLHC_SCL, 0) | \\\n                                     PIN_AFIO_AF(GPIOB_PIN7, 0U))\n\n#undef VAL_GPIOB_AFRH\n#define VAL_GPIOB_AFRH              (PIN_AFIO_AF(GPIOB_PIN8, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_LSM303DLHC_SDA, 0) | \\\n                                     PIN_AFIO_AF(GPIOB_MP45DT02_CLK_IN, 5U) |\\\n                                     PIN_AFIO_AF(GPIOB_PIN11, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN12, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN13, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN14, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN15, 0U))\n\n#undef STM32_HSE_BYPASS\n"
  },
  {
    "path": "platforms/chibios/boards/BLACKPILL_STM32_F401/configs/config.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#define BOARD_OTG_NOVBUSSENS 1\n\n#ifndef STM32_LSECLK\n#    define STM32_LSECLK 32768U\n#endif // STM32_LSECLK\n\n#ifndef STM32_HSECLK\n#    define STM32_HSECLK 25000000U\n#endif // STM32_HSECLK\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n#ifdef WEAR_LEVELING_EMBEDDED_FLASH\n#    ifndef WEAR_LEVELING_EFL_FIRST_SECTOR\n#        ifdef BOOTLOADER_TINYUF2\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 3\n#        else\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 1\n#        endif\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/BLACKPILL_STM32_F401/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32F4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F4xx_MCUCONF\n#define STM32F401_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_BKPRAM_ENABLE                 FALSE\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_CLOCK48_REQUIRED              TRUE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PLLM_VALUE                    25\n#define STM32_PLLN_VALUE                    336\n#define STM32_PLLP_VALUE                    4\n#define STM32_PLLQ_VALUE                    7\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV4\n#define STM32_PPRE2                         STM32_PPRE2_DIV2\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n#define STM32_RTCPRE_VALUE                  8\n#define STM32_MCO1SEL                       STM32_MCO1SEL_HSI\n#define STM32_MCO1PRE                       STM32_MCO1PRE_DIV1\n#define STM32_MCO2SEL                       STM32_MCO2SEL_SYSCLK\n#define STM32_MCO2PRE                       STM32_MCO2PRE_DIV5\n#define STM32_I2SSRC                        STM32_I2SSRC_CKIN\n#define STM32_PLLI2SN_VALUE                 192\n#define STM32_PLLI2SR_VALUE                 5\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           15\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_PRIORITY           15\n#define STM32_IRQ_EXTI22_PRIORITY           15\n\n#define STM32_IRQ_TIM1_BRK_TIM9_PRIORITY    7\n#define STM32_IRQ_TIM1_UP_TIM10_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM11_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM5_PRIORITY             7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART6_PRIORITY           12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_ADCPRE                    ADC_CCR_ADCPRE_DIV4\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(2, 4)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_IRQ_PRIORITY              6\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     6\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM9                  FALSE\n#define STM32_GPT_USE_TIM10                 FALSE\n#define STM32_GPT_USE_TIM11                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * I2S driver system settings.\n */\n#define STM32_I2S_USE_SPI2                  FALSE\n#define STM32_I2S_USE_SPI3                  FALSE\n#define STM32_I2S_SPI2_IRQ_PRIORITY         10\n#define STM32_I2S_SPI3_IRQ_PRIORITY         10\n#define STM32_I2S_SPI2_DMA_PRIORITY         1\n#define STM32_I2S_SPI3_DMA_PRIORITY         1\n#define STM32_I2S_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2S_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2S_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2S_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2S_DMA_ERROR_HOOK(i2sp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM9                  FALSE\n#define STM32_ICU_USE_TIM10                 FALSE\n#define STM32_ICU_USE_TIM11                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM9                  FALSE\n#define STM32_PWM_USE_TIM10                 FALSE\n#define STM32_PWM_USE_TIM11                 FALSE\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART6             FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 0)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART6               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 5)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 5)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 2)\n#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART6_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_OTG1                  TRUE\n#define STM32_USB_OTG1_IRQ_PRIORITY         14\n#define STM32_USB_OTG1_RX_FIFO_SIZE         512\n#define STM32_USB_HOST_WAKEUP_DURATION      2\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/BLACKPILL_STM32_F411/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F411RE/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F411RE\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/BLACKPILL_STM32_F411/configs/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <board.h>\n\n#undef STM32_HSE_BYPASS\n"
  },
  {
    "path": "platforms/chibios/boards/BLACKPILL_STM32_F411/configs/config.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#define BOARD_OTG_NOVBUSSENS 1\n\n#ifndef STM32_LSECLK\n#    define STM32_LSECLK 32768U\n#endif // STM32_LSECLK\n\n#ifndef STM32_HSECLK\n#    define STM32_HSECLK 25000000U\n#endif // STM32_HSECLK\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n#ifdef WEAR_LEVELING_EMBEDDED_FLASH\n#    ifndef WEAR_LEVELING_EFL_FIRST_SECTOR\n#        ifdef BOOTLOADER_TINYUF2\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 3\n#        else\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 1\n#        endif\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/BLACKPILL_STM32_F411/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32F4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F4xx_MCUCONF\n#define STM32F411_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_BKPRAM_ENABLE                 FALSE\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_CLOCK48_REQUIRED              TRUE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PLLM_VALUE                    25\n#define STM32_PLLN_VALUE                    384\n#define STM32_PLLP_VALUE                    4\n#define STM32_PLLQ_VALUE                    8\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV4\n#define STM32_PPRE2                         STM32_PPRE2_DIV2\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n#define STM32_RTCPRE_VALUE                  8\n#define STM32_MCO1SEL                       STM32_MCO1SEL_HSI\n#define STM32_MCO1PRE                       STM32_MCO1PRE_DIV1\n#define STM32_MCO2SEL                       STM32_MCO2SEL_SYSCLK\n#define STM32_MCO2PRE                       STM32_MCO2PRE_DIV5\n#define STM32_I2SSRC                        STM32_I2SSRC_CKIN\n#define STM32_PLLI2SN_VALUE                 192\n#define STM32_PLLI2SR_VALUE                 5\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           15\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_PRIORITY           15\n#define STM32_IRQ_EXTI22_PRIORITY           15\n\n#define STM32_IRQ_TIM1_BRK_TIM9_PRIORITY    7\n#define STM32_IRQ_TIM1_UP_TIM10_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM11_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM5_PRIORITY             7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART6_PRIORITY           12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_ADCPRE                    ADC_CCR_ADCPRE_DIV4\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(2, 4)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_IRQ_PRIORITY              6\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     6\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM9                  FALSE\n#define STM32_GPT_USE_TIM10                 FALSE\n#define STM32_GPT_USE_TIM11                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * I2S driver system settings.\n */\n#define STM32_I2S_USE_SPI2                  FALSE\n#define STM32_I2S_USE_SPI3                  FALSE\n#define STM32_I2S_SPI2_IRQ_PRIORITY         10\n#define STM32_I2S_SPI3_IRQ_PRIORITY         10\n#define STM32_I2S_SPI2_DMA_PRIORITY         1\n#define STM32_I2S_SPI3_DMA_PRIORITY         1\n#define STM32_I2S_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2S_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2S_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2S_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2S_DMA_ERROR_HOOK(i2sp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM9                  FALSE\n#define STM32_ICU_USE_TIM10                 FALSE\n#define STM32_ICU_USE_TIM11                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM9                  FALSE\n#define STM32_PWM_USE_TIM10                 FALSE\n#define STM32_PWM_USE_TIM11                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART6             FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 0)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART6               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 5)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 5)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 2)\n#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART6_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_OTG1                  TRUE\n#define STM32_USB_OTG1_IRQ_PRIORITY         14\n#define STM32_USB_OTG1_RX_FIFO_SIZE         512\n#define STM32_USB_HOST_WAKEUP_DURATION      2\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/BONSAI_C4/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F411RE/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F411RE\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/BONSAI_C4/configs/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <board.h>\n\n#undef STM32_HSE_BYPASS\n"
  },
  {
    "path": "platforms/chibios/boards/BONSAI_C4/configs/config.h",
    "content": "/* Copyright 2022 David Hoelscher, customMK\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n// Bonsai C4 includes Vbus sensing; derived designs that use PA9 for other purposes\n// may disable Vbus sensing with #define BOARD_OTG_NOVBUSSENS 1\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n// FRAM configuration\n#ifndef EXTERNAL_EEPROM_SPI_SLAVE_SELECT_PIN\n#    define EEPROM_SPI_MB85RS64V\n#    define EXTERNAL_EEPROM_SPI_SLAVE_SELECT_PIN PAL_LINE(GPIOA, 0)\n#    define EXTERNAL_EEPROM_SPI_CLOCK_DIVISOR 8 // 96MHz / 8 = 12MHz; max supported by MB85R64 is 20MHz\n#endif\n\n// External flash configuration\n#ifndef EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN\n#    define EXTERNAL_FLASH_SPI_SLAVE_SELECT_PIN PAL_LINE(GPIOB, 12)\n#    define EXTERNAL_FLASH_SPI_CLOCK_DIVISOR 2  // 48MHz; max supported by W25Q128JV is 133MHz\n#    define EXTERNAL_FLASH_BYTE_COUNT (16 * 1024 * 1024)  //128Mbit or 16MByte\n#    define EXTERNAL_FLASH_PAGE_SIZE 256\n#    define EXTERNAL_FLASH_SPI_TIMEOUT 200000 //datasheet max is 200 seconds for flash chip erase\n#endif\n\n// SPI Configuration (needed for FRAM and FLASH)\n#ifndef SPI_DRIVER\n#    define SPI_DRIVER SPID1\n#endif\n#ifndef SPI_SCK_PIN\n#    define SPI_SCK_PIN PAL_LINE(GPIOB, 3)\n#endif\n#ifndef SPI_MOSI_PIN\n#    define SPI_MOSI_PIN PAL_LINE(GPIOB, 5)\n#endif\n#ifndef SPI_MISO_PIN\n#    define SPI_MISO_PIN PAL_LINE(GPIOB, 4)\n#endif\n\n\n// I2C Configuration\n#ifdef CONVERT_TO_BONSAI_C4\n#    ifndef I2C1_SCL_PIN\n#        define I2C1_SCL_PIN PAL_LINE(GPIOB, 6)\n#    endif\n#    ifndef I2C1_SDA_PIN\n#        define I2C1_SDA_PIN PAL_LINE(GPIOB, 9)\n#    endif\n#endif\n\n// WS2812-style LED control on pin A10\n#ifdef WS2812_PWM\n#    ifndef WS2812_DI_PIN\n#        define WS2812_DI_PIN PAL_LINE(GPIOA, 10)\n#    endif\n#    ifndef WS2812_PWM_DRIVER\n#        define WS2812_PWM_DRIVER PWMD1\n#    endif\n#    ifndef WS2812_PWM_CHANNEL\n#        define WS2812_PWM_CHANNEL 3\n#    endif\n#    ifndef WS2812_PWM_PAL_MODE\n#        define WS2812_PWM_PAL_MODE 1\n#    endif\n#    ifndef WS2812_PWM_DMA_STREAM\n#        define WS2812_PWM_DMA_STREAM STM32_DMA2_STREAM5\n#    endif\n#    ifndef WS2812_PWM_DMA_CHANNEL\n#        define WS2812_PWM_DMA_CHANNEL 6\n#    endif\n#endif\n\n#ifndef USB_VBUS_PIN\n#    define USB_VBUS_PIN PAL_LINE(GPIOA, 9)\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/BONSAI_C4/configs/halconf.h",
    "content": "/* Copyright 2022 David Hoelscher, customMK\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#ifndef HAL_USE_SPI\n#    define HAL_USE_SPI TRUE\n#endif\n\n#ifndef HAL_USE_I2C\n#    define HAL_USE_I2C TRUE\n#endif\n\n#ifdef SPLIT_KEYBOARD\n#    ifndef HAL_USE_SERIAL\n#        define HAL_USE_SERIAL TRUE\n#    endif\n#    ifndef SERIAL_BUFFERS_SIZE\n#        define SERIAL_BUFFERS_SIZE 256\n#    endif\n#endif\n\n#ifdef WS2812_PWM\n#    ifndef HAL_USE_PWM\n#        define HAL_USE_PWM TRUE\n#    endif\n#endif\n\n#ifndef SPI_SELECT_MODE\n#    define SPI_SELECT_MODE SPI_SELECT_MODE_PAD\n#endif\n\n#ifndef SPI_USE_WAIT\n#    define SPI_USE_WAIT TRUE\n#endif\n\n#include_next <halconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/BONSAI_C4/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32F4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F4xx_MCUCONF\n#define STM32F411_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_BKPRAM_ENABLE                 FALSE\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_CLOCK48_REQUIRED              TRUE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PLLM_VALUE                    4\n#define STM32_PLLN_VALUE                    96\n#define STM32_PLLP_VALUE                    2\n#define STM32_PLLQ_VALUE                    4\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV2\n#define STM32_PPRE2                         STM32_PPRE2_DIV1\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n#define STM32_RTCPRE_VALUE                  8\n#define STM32_MCO1SEL                       STM32_MCO1SEL_HSI\n#define STM32_MCO1PRE                       STM32_MCO1PRE_DIV1\n#define STM32_MCO2SEL                       STM32_MCO2SEL_SYSCLK\n#define STM32_MCO2PRE                       STM32_MCO2PRE_DIV5\n#define STM32_I2SSRC                        STM32_I2SSRC_CKIN\n#define STM32_PLLI2SN_VALUE                 192\n#define STM32_PLLI2SR_VALUE                 5\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           15\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_PRIORITY           15\n#define STM32_IRQ_EXTI22_PRIORITY           15\n\n#define STM32_IRQ_TIM1_BRK_TIM9_PRIORITY    7\n#define STM32_IRQ_TIM1_UP_TIM10_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM11_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM5_PRIORITY             7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART6_PRIORITY           12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_ADCPRE                    ADC_CCR_ADCPRE_DIV4\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(2, 4)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_IRQ_PRIORITY              6\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     6\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM9                  FALSE\n#define STM32_GPT_USE_TIM10                 FALSE\n#define STM32_GPT_USE_TIM11                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  TRUE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * I2S driver system settings.\n */\n#define STM32_I2S_USE_SPI2                  FALSE\n#define STM32_I2S_USE_SPI3                  FALSE\n#define STM32_I2S_SPI2_IRQ_PRIORITY         10\n#define STM32_I2S_SPI3_IRQ_PRIORITY         10\n#define STM32_I2S_SPI2_DMA_PRIORITY         1\n#define STM32_I2S_SPI3_DMA_PRIORITY         1\n#define STM32_I2S_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2S_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2S_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2S_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2S_DMA_ERROR_HOOK(i2sp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM9                  FALSE\n#define STM32_ICU_USE_TIM10                 FALSE\n#define STM32_ICU_USE_TIM11                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  TRUE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  TRUE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM9                  FALSE\n#define STM32_PWM_USE_TIM10                 FALSE\n#define STM32_PWM_USE_TIM11                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             TRUE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART6             FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  TRUE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 0)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART6               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 5)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 5)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 2)\n#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART6_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_OTG1                  TRUE\n#define STM32_USB_OTG1_IRQ_PRIORITY         14\n#define STM32_USB_OTG1_RX_FIFO_SIZE         512\n#define STM32_USB_HOST_WAKEUP_DURATION      2\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_AT32_F415XX/board/board.c",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n    ChibiOS - Copyright (C) 2023..2025 HorrorTroll\n    ChibiOS - Copyright (C) 2023..2025 Zhaqian\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#include \"hal.h\"\n\n/*===========================================================================*/\n/* Driver local definitions.                                                 */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver exported variables.                                                */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver local variables and types.                                         */\n/*===========================================================================*/\n\n/**\n * @brief   PAL setup.\n * @details Digital I/O ports static configuration as defined in @p board.h.\n *          This variable is used by the HAL when initializing the PAL driver.\n */\n#if HAL_USE_PAL || defined(__DOXYGEN__)\nconst PALConfig pal_default_config =\n{\n  {VAL_GPIOAODT, VAL_GPIOACFGLR, VAL_GPIOACFGHR},\n  {VAL_GPIOBODT, VAL_GPIOBCFGLR, VAL_GPIOBCFGHR},\n#if AT32_HAS_GPIOC\n  {VAL_GPIOCODT, VAL_GPIOCCFGLR, VAL_GPIOCCFGHR},\n#endif\n  {VAL_GPIODODT, VAL_GPIODCFGLR, VAL_GPIODCFGHR},\n#if AT32_HAS_GPIOF\n  {VAL_GPIOFODT, VAL_GPIOFCFGLR, VAL_GPIOFCFGHR},\n#endif\n};\n#endif\n\n/*===========================================================================*/\n/* Driver local functions.                                                   */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver interrupt handlers.                                                */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver exported functions.                                                */\n/*===========================================================================*/\n\n/**\n * @brief   Early initialization code.\n * @details System clocks are initialized before everything else.\n */\nvoid __early_init(void) {\n  at32_clock_init();\n}\n\n#if HAL_USE_SDC || defined(__DOXYGEN__)\n/**\n * @brief   SDC card detection.\n */\nbool sdc_lld_is_card_inserted(SDCDriver *sdcp) {\n  static bool last_status = false;\n\n  if (blkIsTransferring(sdcp))\n    return last_status;\n  return last_status = (bool)palReadPad(GPIOC, GPIOC_PIN11);\n}\n\n/**\n * @brief   SDC card write protection detection.\n */\nbool sdc_lld_is_write_protected(SDCDriver *sdcp) {\n\n  (void)sdcp;\n  return false;\n}\n#endif /* HAL_USE_SDC */\n\n/**\n * @brief   Board-specific initialization code.\n * @note    You can add your board-specific code here.\n */\nvoid boardInit(void) {\n  IOMUX->REMAP |= IOMUX_REMAP_SWJTAG_MUX_JTAGDIS;\n}\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_AT32_F415XX/board/board.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n    ChibiOS - Copyright (C) 2023..2025 HorrorTroll\n    ChibiOS - Copyright (C) 2023..2025 Zhaqian\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef _BOARD_H_\n#define _BOARD_H_\n\n/*===========================================================================*/\n/* Driver constants.                                                         */\n/*===========================================================================*/\n\n/*\n * Setup for a Generic AT32F415 board.\n */\n\n/*\n * Board identifier.\n */\n#define BOARD_GENERIC_AT32_F415XX\n#define BOARD_NAME                  \"GENERIC AT32F415 board\"\n\n/*\n * Board oscillators-related settings.\n */\n#if !defined(AT32_LEXTCLK)\n#define AT32_LEXTCLK                32768\n#endif\n\n#if !defined(AT32_HEXTCLK)\n#define AT32_HEXTCLK                8000000\n#endif\n\n/*\n * MCU type, supported types are defined in ./os/hal/platforms/hal_lld.h.\n */\n#define AT32F415KB\n\n/*\n * GPIO settings, allow unused GPIO for smaller chip packages.\n */\n#if defined(AT32F415KB) || defined(AT32F415KC)\n#define AT32_HAS_GPIOC              TRUE\n#define AT32_HAS_GPIOF              TRUE\n#endif\n\n/*\n * IO pins assignments.\n */\n#define GPIOA_PIN0                  0U\n#define GPIOA_PIN1                  1U\n#define GPIOA_PIN2                  2U\n#define GPIOA_PIN3                  3U\n#define GPIOA_PIN4                  4U\n#define GPIOA_PIN5                  5U\n#define GPIOA_PIN6                  6U\n#define GPIOA_PIN7                  7U\n#define GPIOA_PIN8                  8U\n#define GPIOA_PIN9                  9U\n#define GPIOA_PIN10                 10U\n#define GPIOA_PIN11                 11U\n#define GPIOA_PIN12                 12U\n#define GPIOA_SWDIO                 13U\n#define GPIOA_SWCLK                 14U\n#define GPIOA_PIN15                 15U\n\n#define GPIOB_PIN0                  0U\n#define GPIOB_PIN1                  1U\n#define GPIOB_PIN2                  2U\n#define GPIOB_PIN3                  3U\n#define GPIOB_PIN4                  4U\n#define GPIOB_PIN5                  5U\n#define GPIOB_PIN6                  6U\n#define GPIOB_PIN7                  7U\n#define GPIOB_PIN8                  8U\n#define GPIOB_PIN9                  9U\n#define GPIOB_PIN10                 10U\n#define GPIOB_PIN11                 11U\n#define GPIOB_PIN12                 12U\n#define GPIOB_PIN13                 13U\n#define GPIOB_PIN14                 14U\n#define GPIOB_PIN15                 15U\n\n#define GPIOC_PIN0                  0U\n#define GPIOC_PIN1                  1U\n#define GPIOC_PIN2                  2U\n#define GPIOC_PIN3                  3U\n#define GPIOC_PIN4                  4U\n#define GPIOC_PIN5                  5U\n#define GPIOC_PIN6                  6U\n#define GPIOC_PIN7                  7U\n#define GPIOC_PIN8                  8U\n#define GPIOC_PIN9                  9U\n#define GPIOC_PIN10                 10U\n#define GPIOC_PIN11                 11U\n#define GPIOC_PIN12                 12U\n#define GPIOC_PIN13                 13U\n#define GPIOC_PIN14                 14U\n#define GPIOC_PIN15                 15U\n\n#define GPIOD_HEXT_IN               0U\n#define GPIOD_HEXT_OUT              1U\n#define GPIOD_PIN2                  2U\n\n#define GPIOF_PIN4                  4U\n#define GPIOF_PIN5                  5U\n#define GPIOF_PIN6                  6U\n#define GPIOF_PIN7                  7U\n\n/*===========================================================================*/\n/* Driver pre-compile time settings.                                         */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Derived constants and error checks.                                       */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver data structures and types.                                         */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver macros.                                                            */\n/*===========================================================================*/\n\n/*\n * I/O ports initial setup, this configuration is established soon after reset\n * in the initialization code.\n *\n * The digits have the following meaning:\n *   0 - Analog input.\n *   1 - Push Pull output 10MHz.\n *   2 - Push Pull output 2MHz.\n *   3 - Push Pull output 50MHz.\n *   4 - Digital input.\n *   5 - Open Drain output 10MHz.\n *   6 - Open Drain output 2MHz.\n *   7 - Open Drain output 50MHz.\n *   8 - Digital input with Pull-Up or Pull-Down resistor depending on ODT.\n *   9 - Multiplexing Push Pull output 10MHz.\n *   A - Multiplexing Push Pull output 2MHz.\n *   B - Multiplexing Push Pull output 50MHz.\n *   C - Reserved.\n *   D - Multiplexing Open Drain output 10MHz.\n *   E - Multiplexing Open Drain output 2MHz.\n *   F - Multiplexing Open Drain output 50MHz.\n * Please refer to the AT32 Reference Manual for details.\n */\n\n/*\n * Port A setup.\n */\n#define VAL_GPIOACFGLR          0x88888888      /*  PA7...PA0 */\n#define VAL_GPIOACFGHR          0x88888888      /* PA15...PA8 */\n#define VAL_GPIOAODT            0xFFFFFFFF\n\n/*\n * Port B setup.\n */\n#define VAL_GPIOBCFGLR          0x88888888      /*  PB7...PB0 */\n#define VAL_GPIOBCFGHR          0x88888888      /* PB15...PB8 */\n#define VAL_GPIOBODT            0xFFFFFFFF\n\n/*\n * Port C setup.\n */\n#define VAL_GPIOCCFGLR          0x88888888      /*  PC7...PC0 */\n#define VAL_GPIOCCFGHR          0x88888888      /* PC15...PC8 */\n#define VAL_GPIOCODT            0xFFFFFFFF\n\n/*\n * Port D setup.\n * Everything input with pull-up except:\n * PD0  - Normal input              (GPIOD_HEXT_IN).\n * PD1  - Normal input              (GPIOD_HEXT_OUT).\n */\n#define VAL_GPIODCFGLR          0x88888844      /*  PD7...PD0 */\n#define VAL_GPIODCFGHR          0x88888888      /* PD15...PD8 */\n#define VAL_GPIODODT            0xFFFFFFFF\n\n/*\n * Port F setup.\n */\n#define VAL_GPIOFCFGLR          0x88888888      /*  PF7...PF0 */\n#define VAL_GPIOFCFGHR          0x88888888      /* PF15...PF8 */\n#define VAL_GPIOFODT            0xFFFFFFFF\n\n/*===========================================================================*/\n/* External declarations.                                                    */\n/*===========================================================================*/\n\n#if !defined(_FROM_ASM_)\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n  void boardInit(void);\n#ifdef __cplusplus\n}\n#endif\n#endif /* _FROM_ASM_ */\n\n#endif /* _BOARD_H_ */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_AT32_F415XX/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(BOARD_PATH)/board/board.c\n\n# Required include directories\nBOARDINC = $(BOARD_PATH)/board\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_AT32_F415XX/configs/config.h",
    "content": "// Copyright 2023-2025 HorrorTroll <https://github.com/HorrorTroll>\n// Copyright 2023-2025 Zhaqian <https://github.com/zhaqian12>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#define BOARD_OTG_VBUSIG\n\n#define USB_ENDPOINTS_ARE_REORDERABLE\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_AT32_F415XX/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n    ChibiOS - Copyright (C) 2023..2025 HorrorTroll\n    ChibiOS - Copyright (C) 2023..2025 Zhaqian\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * AT32F415 drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define AT32F415_MCUCONF\n\n/*\n * General settings.\n */\n#define AT32_NO_INIT                        FALSE\n\n/*\n * HAL driver system settings.\n */\n#define AT32_HICK_ENABLED                   TRUE\n#define AT32_LICK_ENABLED                   FALSE\n#define AT32_HEXT_ENABLED                   TRUE\n#define AT32_LEXT_ENABLED                   FALSE\n#define AT32_SCLKSEL                        AT32_SCLKSEL_PLL\n#define AT32_PLLRCS                         AT32_PLLRCS_HEXT\n#define AT32_PLLHEXTDIV                     AT32_PLLHEXTDIV_DIV1\n#define AT32_PLLCFGEN                       AT32_PLLCFGEN_SOLID\n#define AT32_PLLMULT_VALUE                  18\n#define AT32_PLL_FR_VALUE                   4\n#define AT32_PLL_MS_VALUE                   1\n#define AT32_PLL_NS_VALUE                   72\n#define AT32_AHBDIV                         AT32_AHBDIV_DIV1\n#define AT32_APB1DIV                        AT32_APB1DIV_DIV2\n#define AT32_APB2DIV                        AT32_APB2DIV_DIV2\n#define AT32_ADCDIV                         AT32_ADCDIV_DIV4\n#define AT32_USB_CLOCK_REQUIRED             TRUE\n#define AT32_USBDIV                         AT32_USBDIV_DIV3\n#define AT32_CLKOUT_SEL                     AT32_CLKOUT_SEL_NOCLOCK\n#define AT32_CLKOUTDIV                      AT32_CLKOUTDIV_DIV1\n#define AT32_ERTCSEL                        AT32_ERTCSEL_NOCLOCK\n#define AT32_PVM_ENABLE                     FALSE\n#define AT32_PVMSEL                         AT32_PVMSEL_LEV1\n\n/*\n * IRQ system settings.\n */\n#define AT32_IRQ_EXINT0_PRIORITY            6\n#define AT32_IRQ_EXINT1_PRIORITY            6\n#define AT32_IRQ_EXINT2_PRIORITY            6\n#define AT32_IRQ_EXINT3_PRIORITY            6\n#define AT32_IRQ_EXINT4_PRIORITY            6\n#define AT32_IRQ_EXINT5_9_PRIORITY          6\n#define AT32_IRQ_EXINT10_15_PRIORITY        6\n#define AT32_IRQ_EXINT16_PRIORITY           6\n#define AT32_IRQ_EXINT17_PRIORITY           15\n#define AT32_IRQ_EXINT18_PRIORITY           6\n#define AT32_IRQ_EXINT19_PRIORITY           6\n#define AT32_IRQ_EXINT20_PRIORITY           6\n#define AT32_IRQ_EXINT21_PRIORITY           15\n#define AT32_IRQ_EXINT22_PRIORITY           15\n\n#define AT32_IRQ_TMR1_BRK_TMR9_PRIORITY     7\n#define AT32_IRQ_TMR1_OVF_TMR10_PRIORITY    7\n#define AT32_IRQ_TMR1_HALL_TMR11_PRIORITY   7\n#define AT32_IRQ_TMR1_CH_PRIORITY           7\n#define AT32_IRQ_TMR2_PRIORITY              7\n#define AT32_IRQ_TMR3_PRIORITY              7\n#define AT32_IRQ_TMR4_PRIORITY              7\n#define AT32_IRQ_TMR5_PRIORITY              7\n\n#define AT32_IRQ_USART1_PRIORITY            12\n#define AT32_IRQ_USART2_PRIORITY            12\n#define AT32_IRQ_USART3_PRIORITY            12\n#define AT32_IRQ_UART4_PRIORITY             12\n#define AT32_IRQ_UART5_PRIORITY             12\n\n/*\n * ADC driver system settings.\n */\n#define AT32_ADC_USE_ADC1                   FALSE\n#define AT32_ADC_ADC1_DMA_PRIORITY          2\n#define AT32_ADC_ADC1_IRQ_PRIORITY          6\n\n/*\n * CAN driver system settings.\n */\n#define AT32_CAN_USE_CAN1                   FALSE\n#define AT32_CAN_CAN1_IRQ_PRIORITY          11\n\n/*\n * DMA driver system settings.\n */\n#define AT32_DMA_USE_DMAMUX                 TRUE\n\n/*\n * GPT driver system settings.\n */\n#define AT32_GPT_USE_TMR1                   FALSE\n#define AT32_GPT_USE_TMR2                   FALSE\n#define AT32_GPT_USE_TMR3                   FALSE\n#define AT32_GPT_USE_TMR4                   FALSE\n#define AT32_GPT_USE_TMR5                   FALSE\n#define AT32_GPT_USE_TMR9                   FALSE\n#define AT32_GPT_USE_TMR10                  FALSE\n#define AT32_GPT_USE_TMR11                  FALSE\n\n/*\n * I2C driver system settings.\n */\n#define AT32_I2C_USE_I2C1                   FALSE\n#define AT32_I2C_USE_I2C2                   FALSE\n#define AT32_I2C_BUSY_TIMEOUT               50\n#define AT32_I2C_I2C1_DMA_PRIORITY          3\n#define AT32_I2C_I2C2_DMA_PRIORITY          3\n#define AT32_I2C_I2C1_IRQ_PRIORITY          5\n#define AT32_I2C_I2C2_IRQ_PRIORITY          5\n#define AT32_I2C_DMA_ERROR_HOOK(i2cp)       osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define AT32_ICU_USE_TMR1                   FALSE\n#define AT32_ICU_USE_TMR2                   FALSE\n#define AT32_ICU_USE_TMR3                   FALSE\n#define AT32_ICU_USE_TMR4                   FALSE\n#define AT32_ICU_USE_TMR5                   FALSE\n#define AT32_ICU_USE_TMR9                   FALSE\n\n/*\n * PWM driver system settings.\n */\n#define AT32_PWM_USE_TMR1                   FALSE\n#define AT32_PWM_USE_TMR2                   FALSE\n#define AT32_PWM_USE_TMR3                   FALSE\n#define AT32_PWM_USE_TMR4                   FALSE\n#define AT32_PWM_USE_TMR5                   FALSE\n#define AT32_PWM_USE_TMR9                   FALSE\n#define AT32_PWM_USE_TMR10                  FALSE\n#define AT32_PWM_USE_TMR11                  FALSE\n\n/*\n * RTC driver system settings.\n */\n#define AT32_ERTC_DIVA_VALUE                32\n#define AT32_ERTC_DIVB_VALUE                1024\n#define AT32_ERTC_CTRL_INIT                 0\n#define AT32_ERTC_TAMP_INIT                 0\n\n/*\n * SDC driver system settings.\n */\n#define AT32_SDC_SDIO_DMA_PRIORITY          3\n#define AT32_SDC_SDIO_IRQ_PRIORITY          9\n#define AT32_SDC_WRITE_TIMEOUT_MS           1000\n#define AT32_SDC_READ_TIMEOUT_MS            1000\n#define AT32_SDC_CLOCK_ACTIVATION_DELAY     10\n#define AT32_SDC_SDIO_UNALIGNED_SUPPORT     TRUE\n\n/*\n * SERIAL driver system settings.\n */\n#define AT32_SERIAL_USE_USART1              FALSE\n#define AT32_SERIAL_USE_USART2              FALSE\n#define AT32_SERIAL_USE_USART3              FALSE\n#define AT32_SERIAL_USE_UART4               FALSE\n#define AT32_SERIAL_USE_UART5               FALSE\n\n/*\n * SPI driver system settings.\n */\n#define AT32_SPI_USE_SPI1                   FALSE\n#define AT32_SPI_USE_SPI2                   FALSE\n#define AT32_SPI_SPI1_DMA_PRIORITY          1\n#define AT32_SPI_SPI2_DMA_PRIORITY          1\n#define AT32_SPI_SPI1_IRQ_PRIORITY          10\n#define AT32_SPI_SPI2_IRQ_PRIORITY          10\n#define AT32_SPI_DMA_ERROR_HOOK(spip)       osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define AT32_ST_IRQ_PRIORITY                8\n#define AT32_ST_USE_TIMER                   2\n\n/*\n * UART driver system settings.\n */\n#define AT32_UART_USE_USART1                FALSE\n#define AT32_UART_USE_USART2                FALSE\n#define AT32_UART_USE_USART3                FALSE\n#define AT32_UART_USE_UART4                 FALSE\n#define AT32_UART_USE_UART5                 FALSE\n#define AT32_UART_USART1_DMA_PRIORITY       0\n#define AT32_UART_USART2_DMA_PRIORITY       0\n#define AT32_UART_USART3_DMA_PRIORITY       0\n#define AT32_UART_UART4_DMA_PRIORITY        0\n#define AT32_UART_UART5_DMA_PRIORITY        0\n#define AT32_UART_DMA_ERROR_HOOK(uartp)     osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define AT32_USB_USE_OTG1                   TRUE\n#define AT32_USB_OTG1_IRQ_PRIORITY          14\n#define AT32_USB_OTG1_RX_FIFO_SIZE          512\n\n/*\n * WDG driver system settings.\n */\n#define AT32_WDG_USE_WDT                    FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_PROMICRO_RP2040/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040\n\n# Shared variables\nALLCSRC += $(BOARDSRC) \nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/board.h",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include_next <board.h>\n\n#undef BOARD_RP_PICO_RP2040\n#define BOARD_GENERIC_PROMICRO_RP2040\n\n#undef BOARD_NAME\n#define BOARD_NAME \"Pro Micro RP2040\"\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/chconf.h",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#define CH_CFG_SMP_MODE                     TRUE\n#define CH_CFG_ST_RESOLUTION                32\n#define CH_CFG_ST_FREQUENCY                 1000000\n#define CH_CFG_INTERVALS_SIZE               32\n#define CH_CFG_TIME_TYPES_SIZE              32\n#define CH_CFG_ST_TIMEDELTA                 20\n\n#include_next <chconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/config.h",
    "content": "// Copyright 2024 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n/**======================\n **    I2C Driver\n *========================**/\n\n#if !defined(I2C_DRIVER)\n#    define I2C_DRIVER I2CD1\n#endif\n\n#if !defined(I2C1_SDA_PIN)\n#    define I2C1_SDA_PIN GP2\n#endif\n\n#if !defined(I2C1_SCL_PIN)\n#    define I2C1_SCL_PIN GP3\n#endif\n\n/**======================\n **    SPI Driver\n *========================**/\n\n#if !defined(SPI_DRIVER)\n#    define SPI_DRIVER SPID0\n#endif\n\n#if !defined(SPI_SCK_PIN)\n#    define SPI_SCK_PIN GP18\n#endif\n\n#if !defined(SPI_MISO_PIN)\n#    define SPI_MISO_PIN GP20\n#endif\n\n#if !defined(SPI_MOSI_PIN)\n#    define SPI_MOSI_PIN GP19\n#endif\n\n/**======================\n **      SERIAL Driver\n *========================**/\n\n#if !defined(SERIAL_USART_DRIVER)\n#    define SERIAL_USART_DRIVER SIOD0\n#endif\n\n#if !defined(SERIAL_USART_TX_PIN) && !defined(SOFT_SERIAL_PIN)\n#    define SERIAL_USART_TX_PIN GP0\n#endif\n\n#if !defined(SERIAL_USART_RX_PIN)\n#    define SERIAL_USART_RX_PIN GP1\n#endif\n\n/**======================\n **      UART Driver\n *========================**/\n\n#if !defined(UART_DRIVER)\n#    define UART_DRIVER SIOD0\n#endif\n\n#if !defined(UART_TX_PIN)\n#    define UART_TX_PIN GP0\n#endif\n\n#if !defined(UART_RX_PIN)\n#    define UART_RX_PIN GP1\n#endif\n\n#if !defined(UART_CTS_PIN)\n#    define UART_CTS_PIN GP2\n#endif\n\n#if !defined(UART_RTS_PIN)\n#    define UART_RTS_PIN GP3\n#endif\n\n/**======================\n **    Double-tap\n *========================**/\n\n#define RP2040_BOOTLOADER_DOUBLE_TAP_RESET\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_PROMICRO_RP2040/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2021 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * RP2040_MCUCONF drivers configuration.\n *\n * IRQ priorities:\n * 3...0        Lowest...Highest.\n *\n * DMA priorities:\n * 0...1        Lowest...Highest.\n */\n\n#define RP2040_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define RP_NO_INIT                          FALSE\n#define RP_CORE1_START                      FALSE\n#define RP_CORE1_VECTORS_TABLE              _vectors\n#define RP_CORE1_ENTRY_POINT                _crt0_c1_entry\n#define RP_CORE1_STACK_END                  __c1_main_stack_end__\n\n/*\n * IRQ system settings.\n */\n#define RP_IRQ_SYSTICK_PRIORITY             2\n#define RP_IRQ_TIMER_ALARM0_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM1_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM2_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM3_PRIORITY        2\n#define RP_IRQ_ADC1_PRIORITY                3\n#define RP_IRQ_UART0_PRIORITY               3\n#define RP_IRQ_UART1_PRIORITY               3\n#define RP_IRQ_SPI0_PRIORITY                2\n#define RP_IRQ_SPI1_PRIORITY                2\n#define RP_IRQ_USB0_PRIORITY                3\n#define RP_IRQ_I2C0_PRIORITY                2\n#define RP_IRQ_I2C1_PRIORITY                2\n#define RP_IRQ_RTC_PRIORITY                 3\n\n/*\n * ADC driver system settings.\n */\n#define RP_ADC_USE_ADC1                     TRUE\n\n/*\n * SIO driver system settings.\n */\n#define RP_SIO_USE_UART0                    TRUE\n#define RP_SIO_USE_UART1                    FALSE\n\n/*\n * SPI driver system settings.\n */\n#define RP_SPI_USE_SPI0                     TRUE\n#define RP_SPI_USE_SPI1                     FALSE\n#define RP_SPI_SPI0_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI0_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI1_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI1_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI0_DMA_PRIORITY            1\n#define RP_SPI_SPI1_DMA_PRIORITY            1\n#define RP_SPI_DMA_ERROR_HOOK(spip)\n\n/*\n * PWM driver system settings.\n */\n#define RP_PWM_USE_PWM0                     FALSE\n#define RP_PWM_USE_PWM1                     FALSE\n#define RP_PWM_USE_PWM2                     FALSE\n#define RP_PWM_USE_PWM3                     FALSE\n#define RP_PWM_USE_PWM4                     FALSE\n#define RP_PWM_USE_PWM5                     FALSE\n#define RP_PWM_USE_PWM6                     FALSE\n#define RP_PWM_USE_PWM7                     FALSE\n#define RP_PWM_IRQ_WRAP_NUMBER_PRIORITY     3\n\n/*\n * I2C driver system settings.\n */\n#define RP_I2C_USE_I2C0                     FALSE\n#define RP_I2C_USE_I2C1                     TRUE\n#define RP_I2C_BUSY_TIMEOUT                 50\n#define RP_I2C_ADDRESS_MODE_10BIT           FALSE\n\n/*\n * USB driver system settings.\n */\n#define RP_USB_USE_USBD0                    TRUE\n#define RP_USB_FORCE_VBUS_DETECT            TRUE\n#define RP_USE_EXTERNAL_VBUS_DETECT         FALSE\n#define RP_USB_USE_ERROR_DATA_SEQ_INTR      FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_RP_RP2040/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040\n\n# Shared variables\nALLCSRC += $(BOARDSRC) \nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_RP_RP2040/configs/board.h",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include_next <board.h>\n\n#undef BOARD_RP_PICO_RP2040\n#define BOARD_GENERIC_RP2040\n\n#undef BOARD_NAME\n#define BOARD_NAME \"Generic Raspberry Pi RP2040\"\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_RP_RP2040/configs/chconf.h",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#define CH_CFG_SMP_MODE                     TRUE\n#define CH_CFG_ST_RESOLUTION                32\n#define CH_CFG_ST_FREQUENCY                 1000000\n#define CH_CFG_INTERVALS_SIZE               32\n#define CH_CFG_TIME_TYPES_SIZE              32\n#define CH_CFG_ST_TIMEDELTA                 20\n\n#include_next <chconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_RP_RP2040/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2021 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * RP2040_MCUCONF drivers configuration.\n *\n * IRQ priorities:\n * 3...0        Lowest...Highest.\n *\n * DMA priorities:\n * 0...1        Lowest...Highest.\n */\n\n#define RP2040_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define RP_NO_INIT                          FALSE\n#define RP_CORE1_START                      FALSE\n#define RP_CORE1_VECTORS_TABLE              _vectors\n#define RP_CORE1_ENTRY_POINT                _crt0_c1_entry\n#define RP_CORE1_STACK_END                  __c1_main_stack_end__\n\n/*\n * IRQ system settings.\n */\n#define RP_IRQ_SYSTICK_PRIORITY             2\n#define RP_IRQ_TIMER_ALARM0_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM1_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM2_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM3_PRIORITY        2\n#define RP_IRQ_ADC1_PRIORITY                3\n#define RP_IRQ_UART0_PRIORITY               3\n#define RP_IRQ_UART1_PRIORITY               3\n#define RP_IRQ_SPI0_PRIORITY                2\n#define RP_IRQ_SPI1_PRIORITY                2\n#define RP_IRQ_USB0_PRIORITY                3\n#define RP_IRQ_I2C0_PRIORITY                2\n#define RP_IRQ_I2C1_PRIORITY                2\n#define RP_IRQ_RTC_PRIORITY                 3\n\n/*\n * ADC driver system settings.\n */\n#define RP_ADC_USE_ADC1                     FALSE\n\n/*\n * SIO driver system settings.\n */\n#define RP_SIO_USE_UART0                    FALSE\n#define RP_SIO_USE_UART1                    FALSE\n\n/*\n * SPI driver system settings.\n */\n#define RP_SPI_USE_SPI0                     FALSE\n#define RP_SPI_USE_SPI1                     FALSE\n#define RP_SPI_SPI0_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI0_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI1_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI1_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI0_DMA_PRIORITY            1\n#define RP_SPI_SPI1_DMA_PRIORITY            1\n#define RP_SPI_DMA_ERROR_HOOK(spip)\n\n/*\n * PWM driver system settings.\n */\n#define RP_PWM_USE_PWM0                     FALSE\n#define RP_PWM_USE_PWM1                     FALSE\n#define RP_PWM_USE_PWM2                     FALSE\n#define RP_PWM_USE_PWM3                     FALSE\n#define RP_PWM_USE_PWM4                     FALSE\n#define RP_PWM_USE_PWM5                     FALSE\n#define RP_PWM_USE_PWM6                     FALSE\n#define RP_PWM_USE_PWM7                     FALSE\n#define RP_PWM_IRQ_WRAP_NUMBER_PRIORITY     3\n\n/*\n * I2C driver system settings.\n */\n#define RP_I2C_USE_I2C0                     FALSE\n#define RP_I2C_USE_I2C1                     FALSE\n#define RP_I2C_BUSY_TIMEOUT                 50\n#define RP_I2C_ADDRESS_MODE_10BIT           FALSE\n\n/*\n * USB driver system settings.\n */\n#define RP_USB_USE_USBD0                    TRUE\n#define RP_USB_FORCE_VBUS_DETECT            TRUE\n#define RP_USE_EXTERNAL_VBUS_DETECT         FALSE\n#define RP_USB_USE_ERROR_DATA_SEQ_INTR      FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F042X6/board/board.c",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * This file has been automatically generated using ChibiStudio board\n * generator plugin. Do not edit manually.\n */\n\n#include <hal.h>\n#include <stm32_gpio.h>\n\n/*===========================================================================*/\n/* Driver local definitions.                                                 */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver exported variables.                                                */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver local variables and types.                                         */\n/*===========================================================================*/\n\n/**\n * @brief   Type of STM32 GPIO port setup.\n */\ntypedef struct {\n  uint32_t              moder;\n  uint32_t              otyper;\n  uint32_t              ospeedr;\n  uint32_t              pupdr;\n  uint32_t              odr;\n  uint32_t              afrl;\n  uint32_t              afrh;\n} gpio_setup_t;\n\n/**\n * @brief   Type of STM32 GPIO initialization data.\n */\ntypedef struct {\n#if STM32_HAS_GPIOA || defined(__DOXYGEN__)\n  gpio_setup_t          PAData;\n#endif\n#if STM32_HAS_GPIOB || defined(__DOXYGEN__)\n  gpio_setup_t          PBData;\n#endif\n#if STM32_HAS_GPIOC || defined(__DOXYGEN__)\n  gpio_setup_t          PCData;\n#endif\n#if STM32_HAS_GPIOD || defined(__DOXYGEN__)\n  gpio_setup_t          PDData;\n#endif\n#if STM32_HAS_GPIOE || defined(__DOXYGEN__)\n  gpio_setup_t          PEData;\n#endif\n#if STM32_HAS_GPIOF || defined(__DOXYGEN__)\n  gpio_setup_t          PFData;\n#endif\n#if STM32_HAS_GPIOG || defined(__DOXYGEN__)\n  gpio_setup_t          PGData;\n#endif\n#if STM32_HAS_GPIOH || defined(__DOXYGEN__)\n  gpio_setup_t          PHData;\n#endif\n#if STM32_HAS_GPIOI || defined(__DOXYGEN__)\n  gpio_setup_t          PIData;\n#endif\n#if STM32_HAS_GPIOJ || defined(__DOXYGEN__)\n  gpio_setup_t          PJData;\n#endif\n#if STM32_HAS_GPIOK || defined(__DOXYGEN__)\n  gpio_setup_t          PKData;\n#endif\n} gpio_config_t;\n\n/**\n * @brief   STM32 GPIO static initialization data.\n */\nstatic const gpio_config_t gpio_default_config = {\n#if STM32_HAS_GPIOA\n  {VAL_GPIOA_MODER, VAL_GPIOA_OTYPER, VAL_GPIOA_OSPEEDR, VAL_GPIOA_PUPDR,\n   VAL_GPIOA_ODR,   VAL_GPIOA_AFRL,   VAL_GPIOA_AFRH},\n#endif\n#if STM32_HAS_GPIOB\n  {VAL_GPIOB_MODER, VAL_GPIOB_OTYPER, VAL_GPIOB_OSPEEDR, VAL_GPIOB_PUPDR,\n   VAL_GPIOB_ODR,   VAL_GPIOB_AFRL,   VAL_GPIOB_AFRH},\n#endif\n#if STM32_HAS_GPIOC\n  {VAL_GPIOC_MODER, VAL_GPIOC_OTYPER, VAL_GPIOC_OSPEEDR, VAL_GPIOC_PUPDR,\n   VAL_GPIOC_ODR,   VAL_GPIOC_AFRL,   VAL_GPIOC_AFRH},\n#endif\n#if STM32_HAS_GPIOD\n  {VAL_GPIOD_MODER, VAL_GPIOD_OTYPER, VAL_GPIOD_OSPEEDR, VAL_GPIOD_PUPDR,\n   VAL_GPIOD_ODR,   VAL_GPIOD_AFRL,   VAL_GPIOD_AFRH},\n#endif\n#if STM32_HAS_GPIOE\n  {VAL_GPIOE_MODER, VAL_GPIOE_OTYPER, VAL_GPIOE_OSPEEDR, VAL_GPIOE_PUPDR,\n   VAL_GPIOE_ODR,   VAL_GPIOE_AFRL,   VAL_GPIOE_AFRH},\n#endif\n#if STM32_HAS_GPIOF\n  {VAL_GPIOF_MODER, VAL_GPIOF_OTYPER, VAL_GPIOF_OSPEEDR, VAL_GPIOF_PUPDR,\n   VAL_GPIOF_ODR,   VAL_GPIOF_AFRL,   VAL_GPIOF_AFRH},\n#endif\n#if STM32_HAS_GPIOG\n  {VAL_GPIOG_MODER, VAL_GPIOG_OTYPER, VAL_GPIOG_OSPEEDR, VAL_GPIOG_PUPDR,\n   VAL_GPIOG_ODR,   VAL_GPIOG_AFRL,   VAL_GPIOG_AFRH},\n#endif\n#if STM32_HAS_GPIOH\n  {VAL_GPIOH_MODER, VAL_GPIOH_OTYPER, VAL_GPIOH_OSPEEDR, VAL_GPIOH_PUPDR,\n   VAL_GPIOH_ODR,   VAL_GPIOH_AFRL,   VAL_GPIOH_AFRH},\n#endif\n#if STM32_HAS_GPIOI\n  {VAL_GPIOI_MODER, VAL_GPIOI_OTYPER, VAL_GPIOI_OSPEEDR, VAL_GPIOI_PUPDR,\n   VAL_GPIOI_ODR,   VAL_GPIOI_AFRL,   VAL_GPIOI_AFRH},\n#endif\n#if STM32_HAS_GPIOJ\n  {VAL_GPIOJ_MODER, VAL_GPIOJ_OTYPER, VAL_GPIOJ_OSPEEDR, VAL_GPIOJ_PUPDR,\n   VAL_GPIOJ_ODR,   VAL_GPIOJ_AFRL,   VAL_GPIOJ_AFRH},\n#endif\n#if STM32_HAS_GPIOK\n  {VAL_GPIOK_MODER, VAL_GPIOK_OTYPER, VAL_GPIOK_OSPEEDR, VAL_GPIOK_PUPDR,\n   VAL_GPIOK_ODR,   VAL_GPIOK_AFRL,   VAL_GPIOK_AFRH}\n#endif\n};\n\n/*===========================================================================*/\n/* Driver local functions.                                                   */\n/*===========================================================================*/\n\nstatic void gpio_init(stm32_gpio_t *gpiop, const gpio_setup_t *config) {\n\n  gpiop->OTYPER  = config->otyper;\n  gpiop->OSPEEDR = config->ospeedr;\n  gpiop->PUPDR   = config->pupdr;\n  gpiop->ODR     = config->odr;\n  gpiop->AFRL    = config->afrl;\n  gpiop->AFRH    = config->afrh;\n  gpiop->MODER   = config->moder;\n}\n\nstatic void stm32_gpio_init(void) {\n\n  /* Enabling GPIO-related clocks, the mask comes from the\n     registry header file.*/\n  rccResetAHB(STM32_GPIO_EN_MASK);\n  rccEnableAHB(STM32_GPIO_EN_MASK, true);\n\n  /* Initializing all the defined GPIO ports.*/\n#if STM32_HAS_GPIOA\n  gpio_init(GPIOA, &gpio_default_config.PAData);\n#endif\n#if STM32_HAS_GPIOB\n  gpio_init(GPIOB, &gpio_default_config.PBData);\n#endif\n#if STM32_HAS_GPIOC\n  gpio_init(GPIOC, &gpio_default_config.PCData);\n#endif\n#if STM32_HAS_GPIOD\n  gpio_init(GPIOD, &gpio_default_config.PDData);\n#endif\n#if STM32_HAS_GPIOE\n  gpio_init(GPIOE, &gpio_default_config.PEData);\n#endif\n#if STM32_HAS_GPIOF\n  gpio_init(GPIOF, &gpio_default_config.PFData);\n#endif\n#if STM32_HAS_GPIOG\n  gpio_init(GPIOG, &gpio_default_config.PGData);\n#endif\n#if STM32_HAS_GPIOH\n  gpio_init(GPIOH, &gpio_default_config.PHData);\n#endif\n#if STM32_HAS_GPIOI\n  gpio_init(GPIOI, &gpio_default_config.PIData);\n#endif\n#if STM32_HAS_GPIOJ\n  gpio_init(GPIOJ, &gpio_default_config.PJData);\n#endif\n#if STM32_HAS_GPIOK\n  gpio_init(GPIOK, &gpio_default_config.PKData);\n#endif\n}\n\n/*===========================================================================*/\n/* Driver interrupt handlers.                                                */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver exported functions.                                                */\n/*===========================================================================*/\n\n/**\n * @brief   Early initialization code.\n * @details GPIO ports and system clocks are initialized before everything\n *          else.\n */\nvoid __early_init(void) {\n  stm32_gpio_init();\n  stm32_clock_init();\n}\n\n#if HAL_USE_SDC || defined(__DOXYGEN__)\n/**\n * @brief   SDC card detection.\n */\nbool sdc_lld_is_card_inserted(SDCDriver *sdcp) {\n\n  (void)sdcp;\n  /* TODO: Fill the implementation.*/\n  return true;\n}\n\n/**\n * @brief   SDC card write protection detection.\n */\nbool sdc_lld_is_write_protected(SDCDriver *sdcp) {\n\n  (void)sdcp;\n  /* TODO: Fill the implementation.*/\n  return false;\n}\n#endif /* HAL_USE_SDC */\n\n#if HAL_USE_MMC_SPI || defined(__DOXYGEN__)\n/**\n * @brief   MMC_SPI card detection.\n */\nbool mmc_lld_is_card_inserted(MMCDriver *mmcp) {\n\n  (void)mmcp;\n  /* TODO: Fill the implementation.*/\n  return true;\n}\n\n/**\n * @brief   MMC_SPI card write protection detection.\n */\nbool mmc_lld_is_write_protected(MMCDriver *mmcp) {\n\n  (void)mmcp;\n  /* TODO: Fill the implementation.*/\n  return false;\n}\n#endif\n\n/**\n * @brief   Board-specific initialization code.\n * @todo    Add your board-specific code, if any.\n */\nvoid boardInit(void) {\n\n}\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F042X6/board/board.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n#ifndef _BOARD_H\n#define _BOARD_H\n\n/*\n * Setup for STMicroelectronics STM32 Nucleo32-F042K6 board.\n */\n\n/*\n * Board identifier.\n */\n#define BOARD_GENERIC_STM32_F042X6\n#define BOARD_NAME                  \"Generic STM32F042 PCB\"\n\n/*\n * Board oscillators-related settings.\n * NOTE: LSE not fitted.\n * NOTE: HSE not fitted.\n */\n#if !defined(STM32_LSECLK)\n#define STM32_LSECLK                0U\n#endif\n\n#define STM32_LSEDRV                (3U << 3U)\n\n#if !defined(STM32_HSECLK)\n#define STM32_HSECLK                0U\n#endif\n\n/*\n * MCU type as defined in the ST header.\n */\n#define STM32F042x6\n\n/*\n * IO pins assignments.\n */\n#define GPIOA_PIN0                  0U\n#define GPIOA_PIN1                  1U\n#define GPIOA_PIN2                  2U\n#define GPIOA_PIN3                  3U\n#define GPIOA_PIN4                  4U\n#define GPIOA_PIN5                  5U\n#define GPIOA_PIN6                  6U\n#define GPIOA_PIN7                  7U\n#define GPIOA_PIN8                  8U\n#define GPIOA_PIN9                  9U\n#define GPIOA_PIN10                 10U\n#define GPIOA_PIN11                11U\n#define GPIOA_PIN12                12U\n#define GPIOA_PIN13                 13U\n#define GPIOA_PIN14                 14U\n#define GPIOA_PIN15                 15U\n\n#define GPIOB_PIN0                  0U\n#define GPIOB_PIN1                  1U\n#define GPIOB_PIN2                  2U\n#define GPIOB_PIN3                  3U\n#define GPIOB_PIN4                  4U\n#define GPIOB_PIN5                  5U\n#define GPIOB_PIN6                  6U\n#define GPIOB_PIN7                  7U\n#define GPIOB_PIN8                  8U\n#define GPIOB_PIN9                  9U\n#define GPIOB_PIN10                 10U\n#define GPIOB_PIN11                 11U\n#define GPIOB_PIN12                 12U\n#define GPIOB_PIN13                 13U\n#define GPIOB_PIN14                 14U\n#define GPIOB_PIN15                 15U\n\n#define GPIOC_PIN0                  0U\n#define GPIOC_PIN1                  1U\n#define GPIOC_PIN2                  2U\n#define GPIOC_PIN3                  3U\n#define GPIOC_PIN4                  4U\n#define GPIOC_PIN5                  5U\n#define GPIOC_PIN6                  6U\n#define GPIOC_PIN7                  7U\n#define GPIOC_PIN8                  8U\n#define GPIOC_PIN9                  9U\n#define GPIOC_PIN10                 10U\n#define GPIOC_PIN11                 11U\n#define GPIOC_PIN12                 12U\n#define GPIOC_PIN13                 13U\n#define GPIOC_PIN14                 14U\n#define GPIOC_PIN15                 15U\n\n#define GPIOD_PIN0                  0U\n#define GPIOD_PIN1                  1U\n#define GPIOD_PIN2                  2U\n#define GPIOD_PIN3                  3U\n#define GPIOD_PIN4                  4U\n#define GPIOD_PIN5                  5U\n#define GPIOD_PIN6                  6U\n#define GPIOD_PIN7                  7U\n#define GPIOD_PIN8                  8U\n#define GPIOD_PIN9                  9U\n#define GPIOD_PIN10                 10U\n#define GPIOD_PIN11                 11U\n#define GPIOD_PIN12                 12U\n#define GPIOD_PIN13                 13U\n#define GPIOD_PIN14                 14U\n#define GPIOD_PIN15                 15U\n\n#define GPIOE_PIN0                  0U\n#define GPIOE_PIN1                  1U\n#define GPIOE_PIN2                  2U\n#define GPIOE_PIN3                  3U\n#define GPIOE_PIN4                  4U\n#define GPIOE_PIN5                  5U\n#define GPIOE_PIN6                  6U\n#define GPIOE_PIN7                  7U\n#define GPIOE_PIN8                  8U\n#define GPIOE_PIN9                  9U\n#define GPIOE_PIN10                 10U\n#define GPIOE_PIN11                 11U\n#define GPIOE_PIN12                 12U\n#define GPIOE_PIN13                 13U\n#define GPIOE_PIN14                 14U\n#define GPIOE_PIN15                 15U\n\n#define GPIOF_PIN0                  0U\n#define GPIOF_PIN1                  1U\n#define GPIOF_PIN2                  2U\n#define GPIOF_PIN3                  3U\n#define GPIOF_PIN4                  4U\n#define GPIOF_PIN5                  5U\n#define GPIOF_PIN6                  6U\n#define GPIOF_PIN7                  7U\n#define GPIOF_PIN8                  8U\n#define GPIOF_PIN9                  9U\n#define GPIOF_PIN10                 10U\n#define GPIOF_PIN11                 11U\n#define GPIOF_PIN12                 12U\n#define GPIOF_PIN13                 13U\n#define GPIOF_PIN14                 14U\n#define GPIOF_PIN15                 15U\n\n/*\n * IO lines assignments.\n */\n\n#define LINE_BOOT0                  PAL_LINE(GPIOB, 8U)\n#define LINE_SWCLK                  PAL_LINE(GPIOA, 14U)\n#define LINE_SWDIO                  PAL_LINE(GPIOA, 13U)\n\n/*\n * I/O ports initial setup, this configuration is established soon after reset\n * in the initialization code.\n * Please refer to the STM32 Reference Manual for details.\n */\n#define PIN_MODE_INPUT(n)           (0U << ((n) * 2U))\n#define PIN_MODE_OUTPUT(n)          (1U << ((n) * 2U))\n#define PIN_MODE_ALTERNATE(n)       (2U << ((n) * 2U))\n#define PIN_MODE_ANALOG(n)          (3U << ((n) * 2U))\n#define PIN_ODR_LOW(n)              (0U << (n))\n#define PIN_ODR_HIGH(n)             (1U << (n))\n#define PIN_OTYPE_PUSHPULL(n)       (0U << (n))\n#define PIN_OTYPE_OPENDRAIN(n)      (1U << (n))\n#define PIN_OSPEED_VERYLOW(n)       (0U << ((n) * 2U))\n#define PIN_OSPEED_LOW(n)           (1U << ((n) * 2U))\n#define PIN_OSPEED_MEDIUM(n)        (2U << ((n) * 2U))\n#define PIN_OSPEED_HIGH(n)          (3U << ((n) * 2U))\n#define PIN_PUPDR_FLOATING(n)       (0U << ((n) * 2U))\n#define PIN_PUPDR_PULLUP(n)         (1U << ((n) * 2U))\n#define PIN_PUPDR_PULLDOWN(n)       (2U << ((n) * 2U))\n#define PIN_AFIO_AF(n, v)           ((v) << (((n) % 8U) * 4U))\n\n/*\n * GPIOA setup:\n *\n * PA0  - COL5\n * PA1  - COL4\n * PA2  - COL3\n * PA3  - COL2\n * PA4  - COL1\n * PA5  - COL0\n * PA6  - ROW4\n * PA7  - ROW3\n * PA8  - NC\n * PA9  - ROW1\n * PA10 - ROW0\n * PA11 - USB_DM\n * PA12 - USB_DP\n * PA13 - COL15/SWDIO (for now, COL15)\n * PA14 - COL14/SWCLK (for now, COL14)\n * PA15 - COL13\n */\n#define VAL_GPIOA_MODER             (PIN_MODE_INPUT(GPIOA_PIN0) |           \\\n                                     PIN_MODE_INPUT(GPIOA_PIN1) |           \\\n                                     PIN_MODE_INPUT(GPIOA_PIN2) |     \\\n                                     PIN_MODE_INPUT(GPIOA_PIN3) |         \\\n                                     PIN_MODE_INPUT(GPIOA_PIN4) |         \\\n                                     PIN_MODE_INPUT(GPIOA_PIN5) |         \\\n                                     PIN_MODE_INPUT(GPIOA_PIN6) |         \\\n                                     PIN_MODE_INPUT(GPIOA_PIN7) |         \\\n                                     PIN_MODE_INPUT(GPIOA_PIN8) |         \\\n                                     PIN_MODE_INPUT(GPIOA_PIN9) |         \\\n                                     PIN_MODE_INPUT(GPIOA_PIN10) |         \\\n                                     PIN_MODE_INPUT(GPIOA_PIN11) |         \\\n                                     PIN_MODE_INPUT(GPIOA_PIN12) |         \\\n                                     PIN_MODE_INPUT(GPIOA_PIN13) |      \\\n                                     PIN_MODE_INPUT(GPIOA_PIN14) |      \\\n                                     PIN_MODE_INPUT(GPIOA_PIN15))\n#define VAL_GPIOA_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOA_PIN0) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN1) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN2) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN3) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN4) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN5) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN6) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN7) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN8) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN9) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN10) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN11) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN12) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN13) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN14) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOA_PIN15))\n#define VAL_GPIOA_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOA_PIN0) |          \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN1) |          \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN2) |         \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN3) |         \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN4) |        \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN5) |         \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN6) |        \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN7) |        \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN8) |        \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN9) |        \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN10) |        \\\n                                     PIN_OSPEED_HIGH(GPIOA_PIN11) |        \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN12) |        \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN13) |         \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN14) |         \\\n                                     PIN_OSPEED_VERYLOW(GPIOA_PIN15))\n#define VAL_GPIOA_PUPDR             (PIN_PUPDR_PULLUP(GPIOA_PIN0) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN1) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN2) |     \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN3) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN4) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN5) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN6) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN7) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN8) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN9) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN10) |       \\\n                                     PIN_PUPDR_FLOATING(GPIOA_PIN11) |       \\\n                                     PIN_PUPDR_FLOATING(GPIOA_PIN12) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN13) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN14) |      \\\n                                     PIN_PUPDR_PULLUP(GPIOA_PIN15))\n#define VAL_GPIOA_ODR               (PIN_ODR_HIGH(GPIOA_PIN0) |             \\\n                                     PIN_ODR_HIGH(GPIOA_PIN1) |             \\\n                                     PIN_ODR_HIGH(GPIOA_PIN2) |           \\\n                                     PIN_ODR_HIGH(GPIOA_PIN3) |           \\\n                                     PIN_ODR_HIGH(GPIOA_PIN4) |           \\\n                                     PIN_ODR_HIGH(GPIOA_PIN5) |            \\\n                                     PIN_ODR_HIGH(GPIOA_PIN6) |           \\\n                                     PIN_ODR_HIGH(GPIOA_PIN7) |           \\\n                                     PIN_ODR_HIGH(GPIOA_PIN8) |           \\\n                                     PIN_ODR_HIGH(GPIOA_PIN9) |           \\\n                                     PIN_ODR_HIGH(GPIOA_PIN10) |           \\\n                                     PIN_ODR_HIGH(GPIOA_PIN11) |           \\\n                                     PIN_ODR_HIGH(GPIOA_PIN12) |           \\\n                                     PIN_ODR_HIGH(GPIOA_PIN13) |            \\\n                                     PIN_ODR_HIGH(GPIOA_PIN14) |            \\\n                                     PIN_ODR_HIGH(GPIOA_PIN15))\n#define VAL_GPIOA_AFRL              (PIN_AFIO_AF(GPIOA_PIN0, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOA_PIN1, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOA_PIN2, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOA_PIN3, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOA_PIN4, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOA_PIN5, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOA_PIN6, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOA_PIN7, 0U))\n#define VAL_GPIOA_AFRH              (PIN_AFIO_AF(GPIOA_PIN8, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOA_PIN9, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOA_PIN10, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOA_PIN11, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOA_PIN12, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOA_PIN13, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOA_PIN14, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOA_PIN15, 0U))\n\n/*\n * GPIOB setup:\n *\n * PB0  - ROW2\n * PB1  - RGB_D\n * PB2  - PIN2                      (input pullup).\n * PB3  - COL12\n * PB4  - COL11\n * PB5  - COL10\n * PB6  - COL9\n * PB7  - COL8\n * PB8  - BOOT0 (set as output for STM32F042)\n * PB9  - PIN9                      (input pullup).\n * PB10 - PIN10                     (input pullup).\n * PB11 - PIN11                     (input pullup).\n * PB12 - PIN12                     (input pullup).\n * PB13 - PIN13                     (input pullup).\n * PB14 - PIN14                     (input pullup).\n * PB15 - PIN15                     (input pullup).\n */\n#define VAL_GPIOB_MODER             (PIN_MODE_INPUT(GPIOB_PIN0) |         \\\n                                     PIN_MODE_OUTPUT(GPIOB_PIN1) |         \\\n                                     PIN_MODE_INPUT(GPIOB_PIN2) |           \\\n                                     PIN_MODE_INPUT(GPIOB_PIN3) |       \\\n                                     PIN_MODE_INPUT(GPIOB_PIN4) |        \\\n                                     PIN_MODE_INPUT(GPIOB_PIN5) |        \\\n                                     PIN_MODE_INPUT(GPIOB_PIN6) |         \\\n                                     PIN_MODE_INPUT(GPIOB_PIN7) |         \\\n                                     PIN_MODE_OUTPUT(GPIOB_PIN8) |           \\\n                                     PIN_MODE_INPUT(GPIOB_PIN9) |           \\\n                                     PIN_MODE_INPUT(GPIOB_PIN10) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN11) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN12) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN13) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN14) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN15))\n#define VAL_GPIOB_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOB_PIN0) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN1) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN2) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN3) |    \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN4) |    \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN5) |    \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN6) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN7) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN8) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN9) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN10) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN11) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN12) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN13) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN14) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOB_PIN15))\n#define VAL_GPIOB_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOB_PIN0) |        \\\n                                     PIN_OSPEED_HIGH(GPIOB_PIN1) |        \\\n                                     PIN_OSPEED_HIGH(GPIOB_PIN2) |          \\\n                                     PIN_OSPEED_VERYLOW(GPIOB_PIN3) |       \\\n                                     PIN_OSPEED_VERYLOW(GPIOB_PIN4) |       \\\n                                     PIN_OSPEED_VERYLOW(GPIOB_PIN5) |       \\\n                                     PIN_OSPEED_VERYLOW(GPIOB_PIN6) |        \\\n                                     PIN_OSPEED_VERYLOW(GPIOB_PIN7) |        \\\n                                     PIN_OSPEED_VERYLOW(GPIOB_PIN8) |          \\\n                                     PIN_OSPEED_HIGH(GPIOB_PIN9) |          \\\n                                     PIN_OSPEED_HIGH(GPIOB_PIN10) |         \\\n                                     PIN_OSPEED_HIGH(GPIOB_PIN11) |         \\\n                                     PIN_OSPEED_HIGH(GPIOB_PIN12) |         \\\n                                     PIN_OSPEED_HIGH(GPIOB_PIN13) |         \\\n                                     PIN_OSPEED_HIGH(GPIOB_PIN14) |         \\\n                                     PIN_OSPEED_HIGH(GPIOB_PIN15))\n#define VAL_GPIOB_PUPDR             (PIN_PUPDR_PULLUP(GPIOB_PIN0) |       \\\n                                     PIN_PUPDR_FLOATING(GPIOB_PIN1) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN2) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN3) |    \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN4) |      \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN5) |      \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN6) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN7) |       \\\n                                     PIN_PUPDR_PULLDOWN(GPIOB_PIN8) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN9) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN10) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN11) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN12) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN13) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN14) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN15))\n#define VAL_GPIOB_ODR               (PIN_ODR_HIGH(GPIOB_PIN0) |           \\\n                                     PIN_ODR_HIGH(GPIOB_PIN1) |           \\\n                                     PIN_ODR_HIGH(GPIOB_PIN2) |             \\\n                                     PIN_ODR_HIGH(GPIOB_PIN3) |           \\\n                                     PIN_ODR_HIGH(GPIOB_PIN4) |          \\\n                                     PIN_ODR_HIGH(GPIOB_PIN5) |          \\\n                                     PIN_ODR_HIGH(GPIOB_PIN6) |           \\\n                                     PIN_ODR_HIGH(GPIOB_PIN7) |           \\\n                                     PIN_ODR_HIGH(GPIOB_PIN8) |             \\\n                                     PIN_ODR_HIGH(GPIOB_PIN9) |             \\\n                                     PIN_ODR_HIGH(GPIOB_PIN10) |            \\\n                                     PIN_ODR_HIGH(GPIOB_PIN11) |            \\\n                                     PIN_ODR_HIGH(GPIOB_PIN12) |            \\\n                                     PIN_ODR_HIGH(GPIOB_PIN13) |            \\\n                                     PIN_ODR_HIGH(GPIOB_PIN14) |            \\\n                                     PIN_ODR_HIGH(GPIOB_PIN15))\n#define VAL_GPIOB_AFRL              (PIN_AFIO_AF(GPIOB_PIN0, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOB_PIN1, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOB_PIN2, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_PIN3, 0U) |       \\\n                                     PIN_AFIO_AF(GPIOB_PIN4, 0U) |       \\\n                                     PIN_AFIO_AF(GPIOB_PIN5, 0U) |       \\\n                                     PIN_AFIO_AF(GPIOB_PIN6, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOB_PIN7, 0U))\n#define VAL_GPIOB_AFRH              (PIN_AFIO_AF(GPIOB_PIN8, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_PIN9, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_PIN10, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN11, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN12, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN13, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN14, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN15, 0U))\n\n/*\n * GPIOC setup:\n *\n * PC0  - PIN0                      (input pullup).\n * PC1  - PIN1                      (input pullup).\n * PC2  - PIN2                      (input pullup).\n * PC3  - PIN3                      (input pullup).\n * PC4  - PIN4                      (input pullup).\n * PC5  - PIN5                      (input pullup).\n * PC6  - PIN6                      (input pullup).\n * PC7  - PIN7                      (input pullup).\n * PC8  - PIN8                      (input pullup).\n * PC9  - PIN9                      (input pullup).\n * PC10 - PIN10                     (input pullup).\n * PC11 - PIN11                     (input pullup).\n * PC12 - PIN12                     (input pullup).\n * PC13 - PIN13                     (input pullup).\n * PC14 - PIN14                     (input pullup).\n * PC15 - PIN15                     (input pullup).\n */\n#define VAL_GPIOC_MODER             (PIN_MODE_INPUT(GPIOC_PIN0) |           \\\n                                     PIN_MODE_INPUT(GPIOC_PIN1) |           \\\n                                     PIN_MODE_INPUT(GPIOC_PIN2) |           \\\n                                     PIN_MODE_INPUT(GPIOC_PIN3) |           \\\n                                     PIN_MODE_INPUT(GPIOC_PIN4) |           \\\n                                     PIN_MODE_INPUT(GPIOC_PIN5) |           \\\n                                     PIN_MODE_INPUT(GPIOC_PIN6) |           \\\n                                     PIN_MODE_INPUT(GPIOC_PIN7) |           \\\n                                     PIN_MODE_INPUT(GPIOC_PIN8) |           \\\n                                     PIN_MODE_INPUT(GPIOC_PIN9) |           \\\n                                     PIN_MODE_INPUT(GPIOC_PIN10) |          \\\n                                     PIN_MODE_INPUT(GPIOC_PIN11) |          \\\n                                     PIN_MODE_INPUT(GPIOC_PIN12) |          \\\n                                     PIN_MODE_INPUT(GPIOC_PIN13) |          \\\n                                     PIN_MODE_INPUT(GPIOC_PIN14) |          \\\n                                     PIN_MODE_INPUT(GPIOC_PIN15))\n#define VAL_GPIOC_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOC_PIN0) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN1) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN2) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN3) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN4) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN5) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN6) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN7) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN8) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN9) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN10) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN11) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN12) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN13) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN14) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOC_PIN15))\n#define VAL_GPIOC_OSPEEDR           (PIN_OSPEED_HIGH(GPIOC_PIN0) |          \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN1) |          \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN2) |          \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN3) |          \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN4) |          \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN5) |          \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN6) |          \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN7) |          \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN8) |          \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN9) |          \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN10) |         \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN11) |         \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN12) |         \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN13) |         \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN14) |         \\\n                                     PIN_OSPEED_HIGH(GPIOC_PIN15))\n#define VAL_GPIOC_PUPDR             (PIN_PUPDR_PULLUP(GPIOC_PIN0) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN1) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN2) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN3) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN4) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN5) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN6) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN7) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN8) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN9) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN10) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN11) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN12) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN13) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN14) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOC_PIN15))\n#define VAL_GPIOC_ODR               (PIN_ODR_HIGH(GPIOC_PIN0) |             \\\n                                     PIN_ODR_HIGH(GPIOC_PIN1) |             \\\n                                     PIN_ODR_HIGH(GPIOC_PIN2) |             \\\n                                     PIN_ODR_HIGH(GPIOC_PIN3) |             \\\n                                     PIN_ODR_HIGH(GPIOC_PIN4) |             \\\n                                     PIN_ODR_HIGH(GPIOC_PIN5) |             \\\n                                     PIN_ODR_HIGH(GPIOC_PIN6) |             \\\n                                     PIN_ODR_HIGH(GPIOC_PIN7) |             \\\n                                     PIN_ODR_HIGH(GPIOC_PIN8) |             \\\n                                     PIN_ODR_HIGH(GPIOC_PIN9) |             \\\n                                     PIN_ODR_HIGH(GPIOC_PIN10) |            \\\n                                     PIN_ODR_HIGH(GPIOC_PIN11) |            \\\n                                     PIN_ODR_HIGH(GPIOC_PIN12) |            \\\n                                     PIN_ODR_HIGH(GPIOC_PIN13) |            \\\n                                     PIN_ODR_HIGH(GPIOC_PIN14) |            \\\n                                     PIN_ODR_HIGH(GPIOC_PIN15))\n#define VAL_GPIOC_AFRL              (PIN_AFIO_AF(GPIOC_PIN0, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOC_PIN1, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOC_PIN2, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOC_PIN3, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOC_PIN4, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOC_PIN5, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOC_PIN6, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOC_PIN7, 0U))\n#define VAL_GPIOC_AFRH              (PIN_AFIO_AF(GPIOC_PIN8, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOC_PIN9, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOC_PIN10, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOC_PIN11, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOC_PIN12, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOC_PIN13, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOC_PIN14, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOC_PIN15, 0U))\n\n/*\n * GPIOD setup:\n *\n * PD0  - PIN0                      (input pullup).\n * PD1  - PIN1                      (input pullup).\n * PD2  - PIN2                      (input pullup).\n * PD3  - PIN3                      (input pullup).\n * PD4  - PIN4                      (input pullup).\n * PD5  - PIN5                      (input pullup).\n * PD6  - PIN6                      (input pullup).\n * PD7  - PIN7                      (input pullup).\n * PD8  - PIN8                      (input pullup).\n * PD9  - PIN9                      (input pullup).\n * PD10 - PIN10                     (input pullup).\n * PD11 - PIN11                     (input pullup).\n * PD12 - PIN12                     (input pullup).\n * PD13 - PIN13                     (input pullup).\n * PD14 - PIN14                     (input pullup).\n * PD15 - PIN15                     (input pullup).\n */\n#define VAL_GPIOD_MODER             (PIN_MODE_INPUT(GPIOD_PIN0) |           \\\n                                     PIN_MODE_INPUT(GPIOD_PIN1) |           \\\n                                     PIN_MODE_INPUT(GPIOD_PIN2) |           \\\n                                     PIN_MODE_INPUT(GPIOD_PIN3) |           \\\n                                     PIN_MODE_INPUT(GPIOD_PIN4) |           \\\n                                     PIN_MODE_INPUT(GPIOD_PIN5) |           \\\n                                     PIN_MODE_INPUT(GPIOD_PIN6) |           \\\n                                     PIN_MODE_INPUT(GPIOD_PIN7) |           \\\n                                     PIN_MODE_INPUT(GPIOD_PIN8) |           \\\n                                     PIN_MODE_INPUT(GPIOD_PIN9) |           \\\n                                     PIN_MODE_INPUT(GPIOD_PIN10) |          \\\n                                     PIN_MODE_INPUT(GPIOD_PIN11) |          \\\n                                     PIN_MODE_INPUT(GPIOD_PIN12) |          \\\n                                     PIN_MODE_INPUT(GPIOD_PIN13) |          \\\n                                     PIN_MODE_INPUT(GPIOD_PIN14) |          \\\n                                     PIN_MODE_INPUT(GPIOD_PIN15))\n#define VAL_GPIOD_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOD_PIN0) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN1) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN2) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN3) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN4) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN5) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN6) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN7) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN8) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN9) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN10) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN11) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN12) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN13) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN14) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOD_PIN15))\n#define VAL_GPIOD_OSPEEDR           (PIN_OSPEED_HIGH(GPIOD_PIN0) |          \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN1) |          \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN2) |          \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN3) |          \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN4) |          \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN5) |          \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN6) |          \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN7) |          \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN8) |          \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN9) |          \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN10) |         \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN11) |         \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN12) |         \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN13) |         \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN14) |         \\\n                                     PIN_OSPEED_HIGH(GPIOD_PIN15))\n#define VAL_GPIOD_PUPDR             (PIN_PUPDR_PULLUP(GPIOD_PIN0) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN1) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN2) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN3) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN4) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN5) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN6) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN7) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN8) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN9) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN10) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN11) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN12) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN13) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN14) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOD_PIN15))\n#define VAL_GPIOD_ODR               (PIN_ODR_HIGH(GPIOD_PIN0) |             \\\n                                     PIN_ODR_HIGH(GPIOD_PIN1) |             \\\n                                     PIN_ODR_HIGH(GPIOD_PIN2) |             \\\n                                     PIN_ODR_HIGH(GPIOD_PIN3) |             \\\n                                     PIN_ODR_HIGH(GPIOD_PIN4) |             \\\n                                     PIN_ODR_HIGH(GPIOD_PIN5) |             \\\n                                     PIN_ODR_HIGH(GPIOD_PIN6) |             \\\n                                     PIN_ODR_HIGH(GPIOD_PIN7) |             \\\n                                     PIN_ODR_HIGH(GPIOD_PIN8) |             \\\n                                     PIN_ODR_HIGH(GPIOD_PIN9) |             \\\n                                     PIN_ODR_HIGH(GPIOD_PIN10) |            \\\n                                     PIN_ODR_HIGH(GPIOD_PIN11) |            \\\n                                     PIN_ODR_HIGH(GPIOD_PIN12) |            \\\n                                     PIN_ODR_HIGH(GPIOD_PIN13) |            \\\n                                     PIN_ODR_HIGH(GPIOD_PIN14) |            \\\n                                     PIN_ODR_HIGH(GPIOD_PIN15))\n#define VAL_GPIOD_AFRL              (PIN_AFIO_AF(GPIOD_PIN0, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOD_PIN1, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOD_PIN2, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOD_PIN3, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOD_PIN4, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOD_PIN5, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOD_PIN6, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOD_PIN7, 0U))\n#define VAL_GPIOD_AFRH              (PIN_AFIO_AF(GPIOD_PIN8, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOD_PIN9, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOD_PIN10, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOD_PIN11, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOD_PIN12, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOD_PIN13, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOD_PIN14, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOD_PIN15, 0U))\n\n/*\n * GPIOE setup:\n *\n * PE0  - PIN0                      (input pullup).\n * PE1  - PIN1                      (input pullup).\n * PE2  - PIN2                      (input pullup).\n * PE3  - PIN3                      (input pullup).\n * PE4  - PIN4                      (input pullup).\n * PE5  - PIN5                      (input pullup).\n * PE6  - PIN6                      (input pullup).\n * PE7  - PIN7                      (input pullup).\n * PE8  - PIN8                      (input pullup).\n * PE9  - PIN9                      (input pullup).\n * PE10 - PIN10                     (input pullup).\n * PE11 - PIN11                     (input pullup).\n * PE12 - PIN12                     (input pullup).\n * PE13 - PIN13                     (input pullup).\n * PE14 - PIN14                     (input pullup).\n * PE15 - PIN15                     (input pullup).\n */\n#define VAL_GPIOE_MODER             (PIN_MODE_INPUT(GPIOE_PIN0) |           \\\n                                     PIN_MODE_INPUT(GPIOE_PIN1) |           \\\n                                     PIN_MODE_INPUT(GPIOE_PIN2) |           \\\n                                     PIN_MODE_INPUT(GPIOE_PIN3) |           \\\n                                     PIN_MODE_INPUT(GPIOE_PIN4) |           \\\n                                     PIN_MODE_INPUT(GPIOE_PIN5) |           \\\n                                     PIN_MODE_INPUT(GPIOE_PIN6) |           \\\n                                     PIN_MODE_INPUT(GPIOE_PIN7) |           \\\n                                     PIN_MODE_INPUT(GPIOE_PIN8) |           \\\n                                     PIN_MODE_INPUT(GPIOE_PIN9) |           \\\n                                     PIN_MODE_INPUT(GPIOE_PIN10) |          \\\n                                     PIN_MODE_INPUT(GPIOE_PIN11) |          \\\n                                     PIN_MODE_INPUT(GPIOE_PIN12) |          \\\n                                     PIN_MODE_INPUT(GPIOE_PIN13) |          \\\n                                     PIN_MODE_INPUT(GPIOE_PIN14) |          \\\n                                     PIN_MODE_INPUT(GPIOE_PIN15))\n#define VAL_GPIOE_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOE_PIN0) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN1) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN2) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN3) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN4) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN5) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN6) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN7) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN8) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN9) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN10) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN11) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN12) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN13) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN14) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOE_PIN15))\n#define VAL_GPIOE_OSPEEDR           (PIN_OSPEED_HIGH(GPIOE_PIN0) |          \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN1) |          \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN2) |          \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN3) |          \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN4) |          \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN5) |          \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN6) |          \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN7) |          \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN8) |          \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN9) |          \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN10) |         \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN11) |         \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN12) |         \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN13) |         \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN14) |         \\\n                                     PIN_OSPEED_HIGH(GPIOE_PIN15))\n#define VAL_GPIOE_PUPDR             (PIN_PUPDR_PULLUP(GPIOE_PIN0) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN1) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN2) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN3) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN4) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN5) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN6) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN7) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN8) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN9) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN10) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN11) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN12) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN13) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN14) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOE_PIN15))\n#define VAL_GPIOE_ODR               (PIN_ODR_HIGH(GPIOE_PIN0) |             \\\n                                     PIN_ODR_HIGH(GPIOE_PIN1) |             \\\n                                     PIN_ODR_HIGH(GPIOE_PIN2) |             \\\n                                     PIN_ODR_HIGH(GPIOE_PIN3) |             \\\n                                     PIN_ODR_HIGH(GPIOE_PIN4) |             \\\n                                     PIN_ODR_HIGH(GPIOE_PIN5) |             \\\n                                     PIN_ODR_HIGH(GPIOE_PIN6) |             \\\n                                     PIN_ODR_HIGH(GPIOE_PIN7) |             \\\n                                     PIN_ODR_HIGH(GPIOE_PIN8) |             \\\n                                     PIN_ODR_HIGH(GPIOE_PIN9) |             \\\n                                     PIN_ODR_HIGH(GPIOE_PIN10) |            \\\n                                     PIN_ODR_HIGH(GPIOE_PIN11) |            \\\n                                     PIN_ODR_HIGH(GPIOE_PIN12) |            \\\n                                     PIN_ODR_HIGH(GPIOE_PIN13) |            \\\n                                     PIN_ODR_HIGH(GPIOE_PIN14) |            \\\n                                     PIN_ODR_HIGH(GPIOE_PIN15))\n#define VAL_GPIOE_AFRL              (PIN_AFIO_AF(GPIOE_PIN0, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOE_PIN1, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOE_PIN2, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOE_PIN3, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOE_PIN4, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOE_PIN5, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOE_PIN6, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOE_PIN7, 0U))\n#define VAL_GPIOE_AFRH              (PIN_AFIO_AF(GPIOE_PIN8, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOE_PIN9, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOE_PIN10, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOE_PIN11, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOE_PIN12, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOE_PIN13, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOE_PIN14, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOE_PIN15, 0U))\n\n/*\n * GPIOF setup:\n *\n * PF0  - COL7\n * PF1  - COL6\n * PF2  - PIN2                      (input pullup).\n * PF3  - PIN3                      (input pullup).\n * PF4  - PIN4                      (input pullup).\n * PF5  - PIN5                      (input pullup).\n * PF6  - PIN6                      (input pullup).\n * PF7  - PIN7                      (input pullup).\n * PF8  - PIN8                      (input pullup).\n * PF9  - PIN9                      (input pullup).\n * PF10 - PIN10                     (input pullup).\n * PF11 - PIN11                     (input pullup).\n * PF12 - PIN12                     (input pullup).\n * PF13 - PIN13                     (input pullup).\n * PF14 - PIN14                     (input pullup).\n * PF15 - PIN15                     (input pullup).\n */\n#define VAL_GPIOF_MODER             (PIN_MODE_INPUT(GPIOF_PIN0) |         \\\n                                     PIN_MODE_INPUT(GPIOF_PIN1) |         \\\n                                     PIN_MODE_INPUT(GPIOF_PIN2) |           \\\n                                     PIN_MODE_INPUT(GPIOF_PIN3) |           \\\n                                     PIN_MODE_INPUT(GPIOF_PIN4) |           \\\n                                     PIN_MODE_INPUT(GPIOF_PIN5) |           \\\n                                     PIN_MODE_INPUT(GPIOF_PIN6) |           \\\n                                     PIN_MODE_INPUT(GPIOF_PIN7) |           \\\n                                     PIN_MODE_INPUT(GPIOF_PIN8) |           \\\n                                     PIN_MODE_INPUT(GPIOF_PIN9) |           \\\n                                     PIN_MODE_INPUT(GPIOF_PIN10) |          \\\n                                     PIN_MODE_INPUT(GPIOF_PIN11) |          \\\n                                     PIN_MODE_INPUT(GPIOF_PIN12) |          \\\n                                     PIN_MODE_INPUT(GPIOF_PIN13) |          \\\n                                     PIN_MODE_INPUT(GPIOF_PIN14) |          \\\n                                     PIN_MODE_INPUT(GPIOF_PIN15))\n#define VAL_GPIOF_OTYPER            (PIN_OTYPE_PUSHPULL(GPIOF_PIN0) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN1) |     \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN2) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN3) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN4) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN5) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN6) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN7) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN8) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN9) |       \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN10) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN11) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN12) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN13) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN14) |      \\\n                                     PIN_OTYPE_PUSHPULL(GPIOF_PIN15))\n#define VAL_GPIOF_OSPEEDR           (PIN_OSPEED_VERYLOW(GPIOF_PIN0) |        \\\n                                     PIN_OSPEED_VERYLOW(GPIOF_PIN1) |        \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN2) |          \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN3) |          \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN4) |          \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN5) |          \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN6) |          \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN7) |          \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN8) |          \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN9) |          \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN10) |         \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN11) |         \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN12) |         \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN13) |         \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN14) |         \\\n                                     PIN_OSPEED_HIGH(GPIOF_PIN15))\n#define VAL_GPIOF_PUPDR             (PIN_PUPDR_PULLUP(GPIOF_PIN0) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN1) |       \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN2) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN3) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN4) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN5) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN6) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN7) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN8) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN9) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN10) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN11) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN12) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN13) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN14) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOF_PIN15))\n#define VAL_GPIOF_ODR               (PIN_ODR_HIGH(GPIOF_PIN0) |           \\\n                                     PIN_ODR_HIGH(GPIOF_PIN1) |           \\\n                                     PIN_ODR_HIGH(GPIOF_PIN2) |             \\\n                                     PIN_ODR_HIGH(GPIOF_PIN3) |             \\\n                                     PIN_ODR_HIGH(GPIOF_PIN4) |             \\\n                                     PIN_ODR_HIGH(GPIOF_PIN5) |             \\\n                                     PIN_ODR_HIGH(GPIOF_PIN6) |             \\\n                                     PIN_ODR_HIGH(GPIOF_PIN7) |             \\\n                                     PIN_ODR_HIGH(GPIOF_PIN8) |             \\\n                                     PIN_ODR_HIGH(GPIOF_PIN9) |             \\\n                                     PIN_ODR_HIGH(GPIOF_PIN10) |            \\\n                                     PIN_ODR_HIGH(GPIOF_PIN11) |            \\\n                                     PIN_ODR_HIGH(GPIOF_PIN12) |            \\\n                                     PIN_ODR_HIGH(GPIOF_PIN13) |            \\\n                                     PIN_ODR_HIGH(GPIOF_PIN14) |            \\\n                                     PIN_ODR_HIGH(GPIOF_PIN15))\n#define VAL_GPIOF_AFRL              (PIN_AFIO_AF(GPIOF_PIN0, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOF_PIN1, 0U) |        \\\n                                     PIN_AFIO_AF(GPIOF_PIN2, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOF_PIN3, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOF_PIN4, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOF_PIN5, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOF_PIN6, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOF_PIN7, 0U))\n#define VAL_GPIOF_AFRH              (PIN_AFIO_AF(GPIOF_PIN8, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOF_PIN9, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOF_PIN10, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOF_PIN11, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOF_PIN12, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOF_PIN13, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOF_PIN14, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOF_PIN15, 0U))\n\n#if !defined(_FROM_ASM_)\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n  void boardInit(void);\n#ifdef __cplusplus\n}\n#endif\n#endif /* _FROM_ASM_ */\n\n#endif /* _BOARD_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F042X6/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(BOARD_PATH)/board/board.c\n\n# Required include directories\nBOARDINC = $(BOARD_PATH)/board\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F042X6/configs/config.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F042X6/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n        http://www.apache.org/licenses/LICENSE-2.0\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef _MCUCONF_H_\n#define _MCUCONF_H_\n\n/*\n * STM32F0xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 3...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F0xx_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_HSI14_ENABLED                 TRUE\n#define STM32_HSI48_ENABLED                 FALSE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   FALSE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSI_DIV2\n#define STM32_PREDIV_VALUE                  1\n#define STM32_PLLMUL_VALUE                  12\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE                          STM32_PPRE_DIV1\n#define STM32_ADCSW                         STM32_ADCSW_HSI14\n#define STM32_ADCPRE                        STM32_ADCPRE_DIV4\n#define STM32_MCOSEL                        STM32_MCOSEL_NOCLOCK\n#define STM32_ADCPRE                        STM32_ADCPRE_DIV4\n#define STM32_ADCSW                         STM32_ADCSW_HSI14\n#define STM32_USBSW                         STM32_USBSW_HSI48\n#define STM32_CECSW                         STM32_CECSW_HSI\n#define STM32_I2C1SW                        STM32_I2C1SW_HSI\n#define STM32_USART1SW                      STM32_USART1SW_PCLK\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_IRQ_PRIORITY              2\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     2\n\n/*\n * EXT driver system settings.\n */\n#define STM32_EXT_EXTI0_1_IRQ_PRIORITY      3\n#define STM32_EXT_EXTI2_3_IRQ_PRIORITY      3\n#define STM32_EXT_EXTI4_15_IRQ_PRIORITY     3\n#define STM32_EXT_EXTI16_IRQ_PRIORITY       3\n#define STM32_EXT_EXTI17_IRQ_PRIORITY       3\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM14                 FALSE\n#define STM32_GPT_TIM1_IRQ_PRIORITY         2\n#define STM32_GPT_TIM2_IRQ_PRIORITY         2\n#define STM32_GPT_TIM3_IRQ_PRIORITY         2\n#define STM32_GPT_TIM14_IRQ_PRIORITY        2\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_IRQ_PRIORITY         3\n#define STM32_I2C_I2C2_IRQ_PRIORITY         3\n#define STM32_I2C_USE_DMA                   TRUE\n#define STM32_I2C_I2C1_DMA_PRIORITY         1\n#define STM32_I2C_I2C2_DMA_PRIORITY         1\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_TIM1_IRQ_PRIORITY         3\n#define STM32_ICU_TIM2_IRQ_PRIORITY         3\n#define STM32_ICU_TIM3_IRQ_PRIORITY         3\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_ADVANCED              FALSE\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_TIM1_IRQ_PRIORITY         3\n#define STM32_PWM_TIM2_IRQ_PRIORITY         3\n#define STM32_PWM_TIM3_IRQ_PRIORITY         3\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USART1_PRIORITY        3\n#define STM32_SERIAL_USART2_PRIORITY        3\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         2\n#define STM32_SPI_SPI2_IRQ_PRIORITY         2\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               2\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USART1_IRQ_PRIORITY      3\n#define STM32_UART_USART2_IRQ_PRIORITY      3\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_USB1                  TRUE\n#define STM32_USB_LOW_POWER_ON_SUSPEND      FALSE\n#define STM32_USB_USB1_LP_IRQ_PRIORITY      3\n\n#endif /* _MCUCONF_H_ */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F072XB/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F072RB/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F072RB\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F072XB/configs/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <board.h>\n\n#undef STM32_HSE_BYPASS\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F072XB/configs/config.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F072XB/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef _MCUCONF_H_\n#define _MCUCONF_H_\n\n/*\n * STM32F0xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 3...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F0xx_MCUCONF\n// #define STM32F070xB\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT FALSE\n#define STM32_PVD_ENABLE FALSE\n#define STM32_PLS STM32_PLS_LEV0\n#define STM32_HSI_ENABLED TRUE\n#define STM32_HSI14_ENABLED TRUE\n#define STM32_HSI48_ENABLED FALSE\n#define STM32_LSI_ENABLED TRUE\n#define STM32_HSE_ENABLED FALSE\n#define STM32_LSE_ENABLED FALSE\n#define STM32_SW STM32_SW_PLL\n#define STM32_PLLSRC STM32_PLLSRC_HSI_DIV2\n#define STM32_PREDIV_VALUE 1\n#define STM32_PLLMUL_VALUE 12\n#define STM32_HPRE STM32_HPRE_DIV1\n#define STM32_PPRE STM32_PPRE_DIV1\n#define STM32_ADCSW STM32_ADCSW_HSI14\n#define STM32_ADCPRE STM32_ADCPRE_DIV4\n#define STM32_MCOSEL STM32_MCOSEL_NOCLOCK\n#define STM32_ADCPRE STM32_ADCPRE_DIV4\n#define STM32_ADCSW STM32_ADCSW_HSI14\n#define STM32_USBSW STM32_USBSW_HSI48\n#define STM32_CECSW STM32_CECSW_HSI\n#define STM32_I2C1SW STM32_I2C1SW_HSI\n#define STM32_USART1SW STM32_USART1SW_PCLK\n#define STM32_RTCSEL STM32_RTCSEL_LSI\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_1_IRQ_PRIORITY 3\n#define STM32_IRQ_EXTI2_3_IRQ_PRIORITY 3\n#define STM32_IRQ_EXTI4_15_IRQ_PRIORITY 3\n#define STM32_IRQ_EXTI16_IRQ_PRIORITY 3\n#define STM32_IRQ_EXTI17_20_IRQ_PRIORITY 3\n#define STM32_IRQ_EXTI21_22_IRQ_PRIORITY 3\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_USE_ADC1 FALSE\n#define STM32_ADC_ADC1_DMA_PRIORITY 2\n#define STM32_ADC_IRQ_PRIORITY 2\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 2\n#define STM32_ADC_ADC1_DMA_STREAM STM32_DMA_STREAM_ID(1, 1)\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1 FALSE\n#define STM32_GPT_USE_TIM2 FALSE\n#define STM32_GPT_USE_TIM3 FALSE\n#define STM32_GPT_USE_TIM14 FALSE\n#define STM32_GPT_TIM1_IRQ_PRIORITY 2\n#define STM32_GPT_TIM2_IRQ_PRIORITY 2\n#define STM32_GPT_TIM3_IRQ_PRIORITY 2\n#define STM32_GPT_TIM14_IRQ_PRIORITY 2\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1 FALSE\n#define STM32_I2C_USE_I2C2 FALSE\n#define STM32_I2C_BUSY_TIMEOUT 50\n#define STM32_I2C_I2C1_IRQ_PRIORITY 3\n#define STM32_I2C_I2C2_IRQ_PRIORITY 3\n#define STM32_I2C_USE_DMA TRUE\n#define STM32_I2C_I2C1_DMA_PRIORITY 1\n#define STM32_I2C_I2C2_DMA_PRIORITY 1\n#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1 FALSE\n#define STM32_ICU_USE_TIM2 FALSE\n#define STM32_ICU_USE_TIM3 FALSE\n#define STM32_ICU_TIM1_IRQ_PRIORITY 3\n#define STM32_ICU_TIM2_IRQ_PRIORITY 3\n#define STM32_ICU_TIM3_IRQ_PRIORITY 3\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_ADVANCED FALSE\n#define STM32_PWM_USE_TIM1 FALSE\n#define STM32_PWM_USE_TIM2 FALSE\n#define STM32_PWM_USE_TIM3 FALSE\n#define STM32_PWM_TIM1_IRQ_PRIORITY 3\n#define STM32_PWM_TIM2_IRQ_PRIORITY 3\n#define STM32_PWM_TIM3_IRQ_PRIORITY 3\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1 FALSE\n#define STM32_SERIAL_USE_USART2 FALSE\n#define STM32_SERIAL_USART1_PRIORITY 3\n#define STM32_SERIAL_USART2_PRIORITY 3\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1 FALSE\n#define STM32_SPI_USE_SPI2 FALSE\n#define STM32_SPI_SPI1_DMA_PRIORITY 1\n#define STM32_SPI_SPI2_DMA_PRIORITY 1\n#define STM32_SPI_SPI1_IRQ_PRIORITY 2\n#define STM32_SPI_SPI2_IRQ_PRIORITY 2\n#define STM32_SPI_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)\n#define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)\n#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)\n#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)\n#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY 2\n#define STM32_ST_USE_TIMER 2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1 FALSE\n#define STM32_UART_USE_USART2 FALSE\n#define STM32_UART_USART1_IRQ_PRIORITY 3\n#define STM32_UART_USART2_IRQ_PRIORITY 3\n#define STM32_UART_USART1_DMA_PRIORITY 0\n#define STM32_UART_USART2_DMA_PRIORITY 0\n#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_USB1 TRUE\n#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE\n#define STM32_USB_USB1_LP_IRQ_PRIORITY 3\n\n#endif /* _MCUCONF_H_ */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F303XC/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_STM32F3_DISCOVERY/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_STM32F3_DISCOVERY\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F303XC/configs/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <board.h>\n\n#undef STM32_HSE_BYPASS\n\n/*\n * USB bus activation macro, required by the USB driver.\n */\n#define usb_lld_connect_bus(usbp)                                   \\\n    do {                                                            \\\n        palSetPadMode(GPIOA, GPIOA_USB_DP, PAL_MODE_ALTERNATE(14)); \\\n    } while (0)\n\n/*\n * USB bus de-activation macro, required by the USB driver.\n */\n#define usb_lld_disconnect_bus(usbp)                                  \\\n    do {                                                              \\\n        palSetPadMode(GPIOA, GPIOA_USB_DP, PAL_MODE_OUTPUT_PUSHPULL); \\\n        palClearPad(GPIOA, GPIOA_USB_DP);                             \\\n    } while (0)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F303XC/configs/config.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F303XC/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32F3xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F3xx_MCUCONF\n#define STM32F303_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PREDIV_VALUE                  1\n#define STM32_PLLMUL_VALUE                  9\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV2\n#define STM32_PPRE2                         STM32_PPRE2_DIV2\n#define STM32_MCOSEL                        STM32_MCOSEL_NOCLOCK\n#define STM32_ADC12PRES                     STM32_ADC12PRES_DIV1\n#define STM32_ADC34PRES                     STM32_ADC34PRES_DIV1\n#define STM32_USART1SW                      STM32_USART1SW_PCLK\n#define STM32_USART2SW                      STM32_USART2SW_PCLK\n#define STM32_USART3SW                      STM32_USART3SW_PCLK\n#define STM32_UART4SW                       STM32_UART4SW_PCLK\n#define STM32_UART5SW                       STM32_UART5SW_PCLK\n#define STM32_I2C1SW                        STM32_I2C1SW_SYSCLK\n#define STM32_I2C2SW                        STM32_I2C2SW_SYSCLK\n#define STM32_TIM1SW                        STM32_TIM1SW_PCLK2\n#define STM32_TIM8SW                        STM32_TIM8SW_PCLK2\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n#define STM32_USB_CLOCK_REQUIRED            TRUE\n#define STM32_USBPRE                        STM32_USBPRE_DIV1P5\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           15\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           15\n#define STM32_IRQ_EXTI20_PRIORITY           15\n#define STM32_IRQ_EXTI21_22_29_PRIORITY     6\n#define STM32_IRQ_EXTI30_32_PRIORITY        6\n#define STM32_IRQ_EXTI33_PRIORITY           6\n#define STM32_IRQ_TIM1_BRK_TIM15_PRIORITY   7\n#define STM32_IRQ_TIM1_UP_TIM16_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM17_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_DUAL_MODE                 FALSE\n#define STM32_ADC_COMPACT_SAMPLES           FALSE\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_USE_ADC2                  FALSE\n#define STM32_ADC_USE_ADC3                  FALSE\n#define STM32_ADC_USE_ADC4                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(1, 1)\n#define STM32_ADC_ADC2_DMA_STREAM           STM32_DMA_STREAM_ID(2, 1)\n#define STM32_ADC_ADC3_DMA_STREAM           STM32_DMA_STREAM_ID(2, 5)\n#define STM32_ADC_ADC4_DMA_STREAM           STM32_DMA_STREAM_ID(2, 2)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_ADC2_DMA_PRIORITY         2\n#define STM32_ADC_ADC3_DMA_PRIORITY         2\n#define STM32_ADC_ADC4_DMA_PRIORITY         2\n#define STM32_ADC_ADC12_IRQ_PRIORITY        5\n#define STM32_ADC_ADC3_IRQ_PRIORITY         5\n#define STM32_ADC_ADC4_IRQ_PRIORITY         5\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC4_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC12_CLOCK_MODE          ADC_CCR_CKMODE_AHB_DIV1\n#define STM32_ADC_ADC34_CLOCK_MODE          ADC_CCR_CKMODE_AHB_DIV1\n\n/*\n * CAN driver system settings.\n */\n#define STM32_CAN_USE_CAN1                  FALSE\n#define STM32_CAN_CAN1_IRQ_PRIORITY         11\n\n/*\n * DAC driver system settings.\n */\n#define STM32_DAC_DUAL_MODE                 FALSE\n#define STM32_DAC_USE_DAC1_CH1              FALSE\n#define STM32_DAC_USE_DAC1_CH2              FALSE\n#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM6                  FALSE\n#define STM32_GPT_USE_TIM7                  FALSE\n#define STM32_GPT_USE_TIM8                  FALSE\n#define STM32_GPT_USE_TIM15                 FALSE\n#define STM32_GPT_USE_TIM16                 FALSE\n#define STM32_GPT_USE_TIM17                 FALSE\n#define STM32_GPT_TIM1_IRQ_PRIORITY         7\n#define STM32_GPT_TIM2_IRQ_PRIORITY         7\n#define STM32_GPT_TIM3_IRQ_PRIORITY         7\n#define STM32_GPT_TIM4_IRQ_PRIORITY         7\n#define STM32_GPT_TIM6_IRQ_PRIORITY         7\n#define STM32_GPT_TIM7_IRQ_PRIORITY         7\n#define STM32_GPT_TIM8_IRQ_PRIORITY         7\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_IRQ_PRIORITY         10\n#define STM32_I2C_I2C2_IRQ_PRIORITY         10\n#define STM32_I2C_USE_DMA                   TRUE\n#define STM32_I2C_I2C1_DMA_PRIORITY         1\n#define STM32_I2C_I2C2_DMA_PRIORITY         1\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM8                  FALSE\n#define STM32_ICU_USE_TIM15                 FALSE\n#define STM32_ICU_TIM1_IRQ_PRIORITY         7\n#define STM32_ICU_TIM2_IRQ_PRIORITY         7\n#define STM32_ICU_TIM3_IRQ_PRIORITY         7\n#define STM32_ICU_TIM4_IRQ_PRIORITY         7\n#define STM32_ICU_TIM8_IRQ_PRIORITY         7\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM8                  FALSE\n#define STM32_PWM_USE_TIM15                 FALSE\n#define STM32_PWM_USE_TIM16                 FALSE\n#define STM32_PWM_USE_TIM17                 FALSE\n#define STM32_PWM_TIM1_IRQ_PRIORITY         7\n#define STM32_PWM_TIM2_IRQ_PRIORITY         7\n#define STM32_PWM_TIM3_IRQ_PRIORITY         7\n#define STM32_PWM_TIM4_IRQ_PRIORITY         7\n#define STM32_PWM_TIM8_IRQ_PRIORITY         7\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART3             FALSE\n#define STM32_SERIAL_USE_UART4              FALSE\n#define STM32_SERIAL_USE_UART5              FALSE\n#define STM32_SERIAL_USART1_PRIORITY        12\n#define STM32_SERIAL_USART2_PRIORITY        12\n#define STM32_SERIAL_USART3_PRIORITY        12\n#define STM32_SERIAL_UART4_PRIORITY         12\n#define STM32_SERIAL_UART5_PRIORITY         12\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART3               FALSE\n#define STM32_UART_USART1_IRQ_PRIORITY      12\n#define STM32_UART_USART2_IRQ_PRIORITY      12\n#define STM32_UART_USART3_IRQ_PRIORITY      12\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART3_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_USB1                  TRUE\n#define STM32_USB_LOW_POWER_ON_SUSPEND      FALSE\n#define STM32_USB_USB1_HP_IRQ_PRIORITY      13\n#define STM32_USB_USB1_LP_IRQ_PRIORITY      14\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F401XC/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_STM32F401C_DISCOVERY/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_STM32F401C_DISCOVERY\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F401XC/configs/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <board.h>\n\n// Force B9 as input to align with qmk defaults\n#undef VAL_GPIOB_MODER\n#define VAL_GPIOB_MODER             (PIN_MODE_INPUT(GPIOB_PIN0) |           \\\n                                     PIN_MODE_INPUT(GPIOB_PIN1) |           \\\n                                     PIN_MODE_INPUT(GPIOB_PIN2) |           \\\n                                     PIN_MODE_ALTERNATE(GPIOB_SWO) |        \\\n                                     PIN_MODE_INPUT(GPIOB_PIN4) |           \\\n                                     PIN_MODE_INPUT(GPIOB_PIN5) |           \\\n                                     PIN_MODE_INPUT(GPIOB_LSM303DLHC_SCL) | \\\n                                     PIN_MODE_INPUT(GPIOB_PIN7) |           \\\n                                     PIN_MODE_INPUT(GPIOB_PIN8) |           \\\n                                     PIN_MODE_INPUT(GPIOB_LSM303DLHC_SDA) | \\\n                                     PIN_MODE_ALTERNATE(GPIOB_MP45DT02_CLK_IN) |\\\n                                     PIN_MODE_INPUT(GPIOB_PIN11) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN12) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN13) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN14) |          \\\n                                     PIN_MODE_INPUT(GPIOB_PIN15))\n\n#undef VAL_GPIOB_PUPDR\n#define VAL_GPIOB_PUPDR             (PIN_PUPDR_PULLUP(GPIOB_PIN0) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN1) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN2) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_SWO) |          \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN4) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN5) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_LSM303DLHC_SCL) |\\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN7) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN8) |         \\\n                                     PIN_PUPDR_PULLUP(GPIOB_LSM303DLHC_SDA) |\\\n                                     PIN_PUPDR_FLOATING(GPIOB_MP45DT02_CLK_IN) |\\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN11) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN12) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN13) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN14) |        \\\n                                     PIN_PUPDR_PULLUP(GPIOB_PIN15))\n\n#undef VAL_GPIOB_AFRL\n#define VAL_GPIOB_AFRL              (PIN_AFIO_AF(GPIOB_PIN0, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_PIN1, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_PIN2, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_SWO, 0U) |           \\\n                                     PIN_AFIO_AF(GPIOB_PIN4, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_PIN5, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_LSM303DLHC_SCL, 0) | \\\n                                     PIN_AFIO_AF(GPIOB_PIN7, 0U))\n\n#undef VAL_GPIOB_AFRH\n#define VAL_GPIOB_AFRH              (PIN_AFIO_AF(GPIOB_PIN8, 0U) |          \\\n                                     PIN_AFIO_AF(GPIOB_LSM303DLHC_SDA, 0) | \\\n                                     PIN_AFIO_AF(GPIOB_MP45DT02_CLK_IN, 5U) |\\\n                                     PIN_AFIO_AF(GPIOB_PIN11, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN12, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN13, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN14, 0U) |         \\\n                                     PIN_AFIO_AF(GPIOB_PIN15, 0U))\n\n#undef STM32_HSE_BYPASS\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F401XC/configs/config.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#define BOARD_OTG_NOVBUSSENS 1\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n#ifdef WEAR_LEVELING_EMBEDDED_FLASH\n#    ifndef WEAR_LEVELING_EFL_FIRST_SECTOR\n#        ifdef BOOTLOADER_TINYUF2\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 3\n#        else\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 1\n#        endif\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F401XC/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32F4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F4xx_MCUCONF\n#define STM32F401_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_BKPRAM_ENABLE                 FALSE\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_CLOCK48_REQUIRED              TRUE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PLLM_VALUE                    4\n#define STM32_PLLN_VALUE                    168\n#define STM32_PLLP_VALUE                    4\n#define STM32_PLLQ_VALUE                    7\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV2\n#define STM32_PPRE2                         STM32_PPRE2_DIV1\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n#define STM32_RTCPRE_VALUE                  8\n#define STM32_MCO1SEL                       STM32_MCO1SEL_HSI\n#define STM32_MCO1PRE                       STM32_MCO1PRE_DIV1\n#define STM32_MCO2SEL                       STM32_MCO2SEL_SYSCLK\n#define STM32_MCO2PRE                       STM32_MCO2PRE_DIV5\n#define STM32_I2SSRC                        STM32_I2SSRC_CKIN\n#define STM32_PLLI2SN_VALUE                 192\n#define STM32_PLLI2SR_VALUE                 5\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           15\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_PRIORITY           15\n#define STM32_IRQ_EXTI22_PRIORITY           15\n\n#define STM32_IRQ_TIM1_BRK_TIM9_PRIORITY    7\n#define STM32_IRQ_TIM1_UP_TIM10_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM11_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM5_PRIORITY             7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART6_PRIORITY           12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_ADCPRE                    ADC_CCR_ADCPRE_DIV4\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(2, 4)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_IRQ_PRIORITY              6\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     6\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM9                  FALSE\n#define STM32_GPT_USE_TIM10                 FALSE\n#define STM32_GPT_USE_TIM11                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * I2S driver system settings.\n */\n#define STM32_I2S_USE_SPI2                  FALSE\n#define STM32_I2S_USE_SPI3                  FALSE\n#define STM32_I2S_SPI2_IRQ_PRIORITY         10\n#define STM32_I2S_SPI3_IRQ_PRIORITY         10\n#define STM32_I2S_SPI2_DMA_PRIORITY         1\n#define STM32_I2S_SPI3_DMA_PRIORITY         1\n#define STM32_I2S_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2S_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2S_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2S_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2S_DMA_ERROR_HOOK(i2sp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM9                  FALSE\n#define STM32_ICU_USE_TIM10                 FALSE\n#define STM32_ICU_USE_TIM11                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM9                  FALSE\n#define STM32_PWM_USE_TIM10                 FALSE\n#define STM32_PWM_USE_TIM11                 FALSE\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART6             FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 0)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART6               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 5)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 5)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 2)\n#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART6_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_OTG1                  TRUE\n#define STM32_USB_OTG1_IRQ_PRIORITY         14\n#define STM32_USB_OTG1_RX_FIFO_SIZE         512\n#define STM32_USB_HOST_WAKEUP_DURATION      2\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F405XG/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_STM32F4_DISCOVERY/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_STM32F4_DISCOVERY\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F405XG/configs/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#define STM32_HSECLK 12000000\n// The following is required to disable the pull-down on PA9, when PA9 is used for the keyboard matrix:\n#define BOARD_OTG_NOVBUSSENS\n\n#include_next <board.h>\n\n#undef STM32_HSE_BYPASS\n\n#undef STM32F407xx\n#define STM32F405xG\n#define STM32F405xx\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F405XG/configs/config.h",
    "content": "/* Copyright 2021 Andrei Purdea\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* Address for jumping to bootloader on STM32 chips. */\n/* It is chip dependent, the correct number can be looked up by checking against ST's application note AN2606.\n */\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n#ifdef WEAR_LEVELING_EMBEDDED_FLASH\n#    ifndef WEAR_LEVELING_EFL_FIRST_SECTOR\n#        ifdef BOOTLOADER_TINYUF2\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 3\n#        else\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 1\n#        endif\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F405XG/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32F4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F4xx_MCUCONF\n#define STM32F405_MCUCONF\n#define STM32F415_MCUCONF\n#define STM32F407_MCUCONF\n#define STM32F417_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_BKPRAM_ENABLE                 FALSE\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_CLOCK48_REQUIRED              TRUE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PLLM_VALUE                    12\n#define STM32_PLLN_VALUE                    336\n#define STM32_PLLP_VALUE                    2\n#define STM32_PLLQ_VALUE                    7\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV4\n#define STM32_PPRE2                         STM32_PPRE2_DIV2\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n#define STM32_RTCPRE_VALUE                  8\n#define STM32_MCO1SEL                       STM32_MCO1SEL_HSI\n#define STM32_MCO1PRE                       STM32_MCO1PRE_DIV1\n#define STM32_MCO2SEL                       STM32_MCO2SEL_SYSCLK\n#define STM32_MCO2PRE                       STM32_MCO2PRE_DIV5\n#define STM32_I2SSRC                        STM32_I2SSRC_CKIN\n#define STM32_PLLI2SN_VALUE                 192\n#define STM32_PLLI2SR_VALUE                 5\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           15\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_PRIORITY           15\n#define STM32_IRQ_EXTI22_PRIORITY           15\n\n#define STM32_IRQ_TIM1_BRK_TIM9_PRIORITY    7\n#define STM32_IRQ_TIM1_UP_TIM10_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM11_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM5_PRIORITY             7\n#define STM32_IRQ_TIM6_PRIORITY             7\n#define STM32_IRQ_TIM7_PRIORITY             7\n#define STM32_IRQ_TIM8_BRK_TIM12_PRIORITY   7\n#define STM32_IRQ_TIM8_UP_TIM13_PRIORITY    7\n#define STM32_IRQ_TIM8_TRGCO_TIM14_PRIORITY 7\n#define STM32_IRQ_TIM8_CC_PRIORITY          7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART3_PRIORITY           12\n#define STM32_IRQ_UART4_PRIORITY            12\n#define STM32_IRQ_UART5_PRIORITY            12\n#define STM32_IRQ_USART6_PRIORITY           12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_ADCPRE                    ADC_CCR_ADCPRE_DIV4\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_USE_ADC2                  FALSE\n#define STM32_ADC_USE_ADC3                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(2, 4)\n#define STM32_ADC_ADC2_DMA_STREAM           STM32_DMA_STREAM_ID(2, 2)\n#define STM32_ADC_ADC3_DMA_STREAM           STM32_DMA_STREAM_ID(2, 1)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_ADC2_DMA_PRIORITY         2\n#define STM32_ADC_ADC3_DMA_PRIORITY         2\n#define STM32_ADC_IRQ_PRIORITY              6\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     6\n#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY     6\n#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY     6\n\n/*\n * CAN driver system settings.\n */\n#define STM32_CAN_USE_CAN1                  FALSE\n#define STM32_CAN_USE_CAN2                  FALSE\n#define STM32_CAN_CAN1_IRQ_PRIORITY         11\n#define STM32_CAN_CAN2_IRQ_PRIORITY         11\n\n/*\n * DAC driver system settings.\n */\n#define STM32_DAC_DUAL_MODE                 FALSE\n#define STM32_DAC_USE_DAC1_CH1              FALSE\n#define STM32_DAC_USE_DAC1_CH2              FALSE\n#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH1_DMA_STREAM       STM32_DMA_STREAM_ID(1, 5)\n#define STM32_DAC_DAC1_CH2_DMA_STREAM       STM32_DMA_STREAM_ID(1, 6)\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM6                  FALSE\n#define STM32_GPT_USE_TIM7                  FALSE\n#define STM32_GPT_USE_TIM8                  FALSE\n#define STM32_GPT_USE_TIM9                  FALSE\n#define STM32_GPT_USE_TIM10                 FALSE\n#define STM32_GPT_USE_TIM11                 FALSE\n#define STM32_GPT_USE_TIM12                 FALSE\n#define STM32_GPT_USE_TIM13                 FALSE\n#define STM32_GPT_USE_TIM14                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * I2S driver system settings.\n */\n#define STM32_I2S_USE_SPI2                  FALSE\n#define STM32_I2S_USE_SPI3                  FALSE\n#define STM32_I2S_SPI2_IRQ_PRIORITY         10\n#define STM32_I2S_SPI3_IRQ_PRIORITY         10\n#define STM32_I2S_SPI2_DMA_PRIORITY         1\n#define STM32_I2S_SPI3_DMA_PRIORITY         1\n#define STM32_I2S_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2S_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2S_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2S_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2S_DMA_ERROR_HOOK(i2sp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM8                  FALSE\n#define STM32_ICU_USE_TIM9                  FALSE\n#define STM32_ICU_USE_TIM10                 FALSE\n#define STM32_ICU_USE_TIM11                 FALSE\n#define STM32_ICU_USE_TIM12                 FALSE\n#define STM32_ICU_USE_TIM13                 FALSE\n#define STM32_ICU_USE_TIM14                 FALSE\n\n/*\n * MAC driver system settings.\n */\n#define STM32_MAC_TRANSMIT_BUFFERS          2\n#define STM32_MAC_RECEIVE_BUFFERS           4\n#define STM32_MAC_BUFFERS_SIZE              1522\n#define STM32_MAC_PHY_TIMEOUT               100\n#define STM32_MAC_ETH1_CHANGE_PHY_STATE     TRUE\n#define STM32_MAC_ETH1_IRQ_PRIORITY         13\n#define STM32_MAC_IP_CHECKSUM_OFFLOAD       0\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM8                  FALSE\n#define STM32_PWM_USE_TIM9                  FALSE\n#define STM32_PWM_USE_TIM10                 FALSE\n#define STM32_PWM_USE_TIM11                 FALSE\n#define STM32_PWM_USE_TIM12                 FALSE\n#define STM32_PWM_USE_TIM13                 FALSE\n#define STM32_PWM_USE_TIM14                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SDC driver system settings.\n */\n#define STM32_SDC_SDIO_DMA_PRIORITY         3\n#define STM32_SDC_SDIO_IRQ_PRIORITY         9\n#define STM32_SDC_WRITE_TIMEOUT_MS          1000\n#define STM32_SDC_READ_TIMEOUT_MS           1000\n#define STM32_SDC_CLOCK_ACTIVATION_DELAY    10\n#define STM32_SDC_SDIO_UNALIGNED_SUPPORT    TRUE\n#define STM32_SDC_SDIO_DMA_STREAM           STM32_DMA_STREAM_ID(2, 3)\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART3             FALSE\n#define STM32_SERIAL_USE_UART4              FALSE\n#define STM32_SERIAL_USE_UART5              FALSE\n#define STM32_SERIAL_USE_USART6             FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 0)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART3               FALSE\n#define STM32_UART_USE_UART4                FALSE\n#define STM32_UART_USE_UART5                FALSE\n#define STM32_UART_USE_USART6               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 5)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 5)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART3_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 1)\n#define STM32_UART_USART3_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 3)\n#define STM32_UART_UART4_RX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 2)\n#define STM32_UART_UART4_TX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 4)\n#define STM32_UART_UART5_RX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 0)\n#define STM32_UART_UART5_TX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 7)\n#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 2)\n#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART3_DMA_PRIORITY      0\n#define STM32_UART_UART4_DMA_PRIORITY       0\n#define STM32_UART_UART5_DMA_PRIORITY       0\n#define STM32_UART_USART6_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_OTG1                  TRUE\n#define STM32_USB_USE_OTG2                  FALSE\n#define STM32_USB_OTG1_IRQ_PRIORITY         14\n#define STM32_USB_OTG2_IRQ_PRIORITY         14\n#define STM32_USB_OTG1_RX_FIFO_SIZE         512\n#define STM32_USB_OTG2_RX_FIFO_SIZE         1024\n#define STM32_USB_HOST_WAKEUP_DURATION      2\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F407XE/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_STM32F4_DISCOVERY/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_STM32F4_DISCOVERY\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F407XE/configs/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#define STM32_HSECLK 8000000\n// The following is required to disable the pull-down on PA9, when PA9 is used for the keyboard matrix:\n#define BOARD_OTG_NOVBUSSENS\n\n#include_next <board.h>\n\n#undef STM32_HSE_BYPASS\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F407XE/configs/config.h",
    "content": "/* Copyright 2021 Andrei Purdea\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* Address for jumping to bootloader on STM32 chips. */\n/* It is chip dependent, the correct number can be looked up by checking against ST's application note AN2606.\n */\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n#ifdef WEAR_LEVELING_EMBEDDED_FLASH\n#    ifndef WEAR_LEVELING_EFL_FIRST_SECTOR\n#        ifdef BOOTLOADER_TINYUF2\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 3\n#        else\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 1\n#        endif\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F407XE/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32F4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F4xx_MCUCONF\n#define STM32F405_MCUCONF\n#define STM32F415_MCUCONF\n#define STM32F407_MCUCONF\n#define STM32F417_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_BKPRAM_ENABLE                 FALSE\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_CLOCK48_REQUIRED              TRUE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PLLM_VALUE                    8\n#define STM32_PLLN_VALUE                    336\n#define STM32_PLLP_VALUE                    2\n#define STM32_PLLQ_VALUE                    7\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV4\n#define STM32_PPRE2                         STM32_PPRE2_DIV2\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n#define STM32_RTCPRE_VALUE                  8\n#define STM32_MCO1SEL                       STM32_MCO1SEL_HSI\n#define STM32_MCO1PRE                       STM32_MCO1PRE_DIV1\n#define STM32_MCO2SEL                       STM32_MCO2SEL_SYSCLK\n#define STM32_MCO2PRE                       STM32_MCO2PRE_DIV5\n#define STM32_I2SSRC                        STM32_I2SSRC_CKIN\n#define STM32_PLLI2SN_VALUE                 192\n#define STM32_PLLI2SR_VALUE                 5\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           15\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_PRIORITY           15\n#define STM32_IRQ_EXTI22_PRIORITY           15\n\n#define STM32_IRQ_TIM1_BRK_TIM9_PRIORITY    7\n#define STM32_IRQ_TIM1_UP_TIM10_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM11_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM5_PRIORITY             7\n#define STM32_IRQ_TIM6_PRIORITY             7\n#define STM32_IRQ_TIM7_PRIORITY             7\n#define STM32_IRQ_TIM8_BRK_TIM12_PRIORITY   7\n#define STM32_IRQ_TIM8_UP_TIM13_PRIORITY    7\n#define STM32_IRQ_TIM8_TRGCO_TIM14_PRIORITY 7\n#define STM32_IRQ_TIM8_CC_PRIORITY          7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART3_PRIORITY           12\n#define STM32_IRQ_UART4_PRIORITY            12\n#define STM32_IRQ_UART5_PRIORITY            12\n#define STM32_IRQ_USART6_PRIORITY           12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_ADCPRE                    ADC_CCR_ADCPRE_DIV4\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_USE_ADC2                  FALSE\n#define STM32_ADC_USE_ADC3                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(2, 4)\n#define STM32_ADC_ADC2_DMA_STREAM           STM32_DMA_STREAM_ID(2, 2)\n#define STM32_ADC_ADC3_DMA_STREAM           STM32_DMA_STREAM_ID(2, 1)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_ADC2_DMA_PRIORITY         2\n#define STM32_ADC_ADC3_DMA_PRIORITY         2\n#define STM32_ADC_IRQ_PRIORITY              6\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     6\n#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY     6\n#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY     6\n\n/*\n * CAN driver system settings.\n */\n#define STM32_CAN_USE_CAN1                  FALSE\n#define STM32_CAN_USE_CAN2                  FALSE\n#define STM32_CAN_CAN1_IRQ_PRIORITY         11\n#define STM32_CAN_CAN2_IRQ_PRIORITY         11\n\n/*\n * DAC driver system settings.\n */\n#define STM32_DAC_DUAL_MODE                 FALSE\n#define STM32_DAC_USE_DAC1_CH1              FALSE\n#define STM32_DAC_USE_DAC1_CH2              FALSE\n#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH1_DMA_STREAM       STM32_DMA_STREAM_ID(1, 5)\n#define STM32_DAC_DAC1_CH2_DMA_STREAM       STM32_DMA_STREAM_ID(1, 6)\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM6                  FALSE\n#define STM32_GPT_USE_TIM7                  FALSE\n#define STM32_GPT_USE_TIM8                  FALSE\n#define STM32_GPT_USE_TIM9                  FALSE\n#define STM32_GPT_USE_TIM10                 FALSE\n#define STM32_GPT_USE_TIM11                 FALSE\n#define STM32_GPT_USE_TIM12                 FALSE\n#define STM32_GPT_USE_TIM13                 FALSE\n#define STM32_GPT_USE_TIM14                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * I2S driver system settings.\n */\n#define STM32_I2S_USE_SPI2                  FALSE\n#define STM32_I2S_USE_SPI3                  FALSE\n#define STM32_I2S_SPI2_IRQ_PRIORITY         10\n#define STM32_I2S_SPI3_IRQ_PRIORITY         10\n#define STM32_I2S_SPI2_DMA_PRIORITY         1\n#define STM32_I2S_SPI3_DMA_PRIORITY         1\n#define STM32_I2S_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2S_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2S_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2S_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2S_DMA_ERROR_HOOK(i2sp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM8                  FALSE\n#define STM32_ICU_USE_TIM9                  FALSE\n#define STM32_ICU_USE_TIM10                 FALSE\n#define STM32_ICU_USE_TIM11                 FALSE\n#define STM32_ICU_USE_TIM12                 FALSE\n#define STM32_ICU_USE_TIM13                 FALSE\n#define STM32_ICU_USE_TIM14                 FALSE\n\n/*\n * MAC driver system settings.\n */\n#define STM32_MAC_TRANSMIT_BUFFERS          2\n#define STM32_MAC_RECEIVE_BUFFERS           4\n#define STM32_MAC_BUFFERS_SIZE              1522\n#define STM32_MAC_PHY_TIMEOUT               100\n#define STM32_MAC_ETH1_CHANGE_PHY_STATE     TRUE\n#define STM32_MAC_ETH1_IRQ_PRIORITY         13\n#define STM32_MAC_IP_CHECKSUM_OFFLOAD       0\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM8                  FALSE\n#define STM32_PWM_USE_TIM9                  FALSE\n#define STM32_PWM_USE_TIM10                 FALSE\n#define STM32_PWM_USE_TIM11                 FALSE\n#define STM32_PWM_USE_TIM12                 FALSE\n#define STM32_PWM_USE_TIM13                 FALSE\n#define STM32_PWM_USE_TIM14                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SDC driver system settings.\n */\n#define STM32_SDC_SDIO_DMA_PRIORITY         3\n#define STM32_SDC_SDIO_IRQ_PRIORITY         9\n#define STM32_SDC_WRITE_TIMEOUT_MS          1000\n#define STM32_SDC_READ_TIMEOUT_MS           1000\n#define STM32_SDC_CLOCK_ACTIVATION_DELAY    10\n#define STM32_SDC_SDIO_UNALIGNED_SUPPORT    TRUE\n#define STM32_SDC_SDIO_DMA_STREAM           STM32_DMA_STREAM_ID(2, 3)\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART3             FALSE\n#define STM32_SERIAL_USE_UART4              FALSE\n#define STM32_SERIAL_USE_UART5              FALSE\n#define STM32_SERIAL_USE_USART6             FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 0)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART3               FALSE\n#define STM32_UART_USE_UART4                FALSE\n#define STM32_UART_USE_UART5                FALSE\n#define STM32_UART_USE_USART6               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 5)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 5)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART3_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 1)\n#define STM32_UART_USART3_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 3)\n#define STM32_UART_UART4_RX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 2)\n#define STM32_UART_UART4_TX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 4)\n#define STM32_UART_UART5_RX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 0)\n#define STM32_UART_UART5_TX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 7)\n#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 2)\n#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART3_DMA_PRIORITY      0\n#define STM32_UART_UART4_DMA_PRIORITY       0\n#define STM32_UART_UART5_DMA_PRIORITY       0\n#define STM32_UART_USART6_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_OTG1                  TRUE\n#define STM32_USB_USE_OTG2                  FALSE\n#define STM32_USB_OTG1_IRQ_PRIORITY         14\n#define STM32_USB_OTG2_IRQ_PRIORITY         14\n#define STM32_USB_OTG1_RX_FIFO_SIZE         512\n#define STM32_USB_OTG2_RX_FIFO_SIZE         1024\n#define STM32_USB_HOST_WAKEUP_DURATION      2\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F411XE/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F411RE/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F411RE\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F411XE/configs/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <board.h>\n\n#undef STM32_HSE_BYPASS\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F411XE/configs/config.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#define BOARD_OTG_NOVBUSSENS 1\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n#ifdef WEAR_LEVELING_EMBEDDED_FLASH\n#    ifndef WEAR_LEVELING_EFL_FIRST_SECTOR\n#        ifdef BOOTLOADER_TINYUF2\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 3\n#        else\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 1\n#        endif\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F411XE/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32F4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F4xx_MCUCONF\n#define STM32F411_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_BKPRAM_ENABLE                 FALSE\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_CLOCK48_REQUIRED              TRUE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PLLM_VALUE                    4\n#define STM32_PLLN_VALUE                    96\n#define STM32_PLLP_VALUE                    2\n#define STM32_PLLQ_VALUE                    4\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV2\n#define STM32_PPRE2                         STM32_PPRE2_DIV1\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n#define STM32_RTCPRE_VALUE                  8\n#define STM32_MCO1SEL                       STM32_MCO1SEL_HSI\n#define STM32_MCO1PRE                       STM32_MCO1PRE_DIV1\n#define STM32_MCO2SEL                       STM32_MCO2SEL_SYSCLK\n#define STM32_MCO2PRE                       STM32_MCO2PRE_DIV5\n#define STM32_I2SSRC                        STM32_I2SSRC_CKIN\n#define STM32_PLLI2SN_VALUE                 192\n#define STM32_PLLI2SR_VALUE                 5\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           15\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_PRIORITY           15\n#define STM32_IRQ_EXTI22_PRIORITY           15\n\n#define STM32_IRQ_TIM1_BRK_TIM9_PRIORITY    7\n#define STM32_IRQ_TIM1_UP_TIM10_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM11_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM5_PRIORITY             7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART6_PRIORITY           12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_ADCPRE                    ADC_CCR_ADCPRE_DIV4\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(2, 4)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_IRQ_PRIORITY              6\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     6\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM9                  FALSE\n#define STM32_GPT_USE_TIM10                 FALSE\n#define STM32_GPT_USE_TIM11                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * I2S driver system settings.\n */\n#define STM32_I2S_USE_SPI2                  FALSE\n#define STM32_I2S_USE_SPI3                  FALSE\n#define STM32_I2S_SPI2_IRQ_PRIORITY         10\n#define STM32_I2S_SPI3_IRQ_PRIORITY         10\n#define STM32_I2S_SPI2_DMA_PRIORITY         1\n#define STM32_I2S_SPI3_DMA_PRIORITY         1\n#define STM32_I2S_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2S_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2S_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2S_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2S_DMA_ERROR_HOOK(i2sp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM9                  FALSE\n#define STM32_ICU_USE_TIM10                 FALSE\n#define STM32_ICU_USE_TIM11                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM9                  FALSE\n#define STM32_PWM_USE_TIM10                 FALSE\n#define STM32_PWM_USE_TIM11                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART6             FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 0)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART6               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 5)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 5)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 2)\n#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART6_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_OTG1                  TRUE\n#define STM32_USB_OTG1_IRQ_PRIORITY         14\n#define STM32_USB_OTG1_RX_FIFO_SIZE         512\n#define STM32_USB_HOST_WAKEUP_DURATION      2\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F446XE/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F446RE/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F446RE\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F446XE/configs/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#define STM32_HSECLK 16000000\n// The following is required to disable the pull-down on PA9, when PA9 is used for the keyboard matrix:\n#define BOARD_OTG_NOVBUSSENS\n\n#include_next <board.h>\n\n#undef STM32_HSE_BYPASS\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F446XE/configs/config.h",
    "content": "/* Copyright 2021 Andrei Purdea\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n#ifdef WEAR_LEVELING_EMBEDDED_FLASH\n#    ifndef WEAR_LEVELING_EFL_FIRST_SECTOR\n#        ifdef BOOTLOADER_TINYUF2\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 3\n#        else\n#            define WEAR_LEVELING_EFL_FIRST_SECTOR 1\n#        endif\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_F446XE/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32F4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F4xx_MCUCONF\n#define STM32F446_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_BKPRAM_ENABLE                 FALSE\n#define STM32_HSI_ENABLED                   FALSE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_CLOCK48_REQUIRED              TRUE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PLLM_VALUE                    8\n#define STM32_PLLN_VALUE                    180\n#define STM32_PLLP_VALUE                    2\n#define STM32_PLLQ_VALUE                    7\n#define STM32_PLLI2SN_VALUE                 192\n#define STM32_PLLI2SM_VALUE                 8\n#define STM32_PLLI2SR_VALUE                 4\n#define STM32_PLLI2SP_VALUE                 4\n#define STM32_PLLI2SQ_VALUE                 4\n#define STM32_PLLSAIN_VALUE                 192\n#define STM32_PLLSAIM_VALUE                 8\n#define STM32_PLLSAIP_VALUE                 8\n#define STM32_PLLSAIQ_VALUE                 4\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV4\n#define STM32_PPRE2                         STM32_PPRE2_DIV2\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n#define STM32_RTCPRE_VALUE                  8\n#define STM32_MCO1SEL                       STM32_MCO1SEL_HSE\n#define STM32_MCO1PRE                       STM32_MCO1PRE_DIV1\n#define STM32_MCO2SEL                       STM32_MCO2SEL_PLLI2S\n#define STM32_MCO2PRE                       STM32_MCO2PRE_DIV1\n#define STM32_I2SSRC                        STM32_I2SSRC_PLLI2S\n#define STM32_SAI1SEL                       STM32_SAI2SEL_PLLR\n#define STM32_SAI2SEL                       STM32_SAI2SEL_PLLR\n#define STM32_CK48MSEL                      STM32_CK48MSEL_PLLALT\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           15\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_PRIORITY           15\n#define STM32_IRQ_EXTI22_PRIORITY           15\n\n#define STM32_IRQ_TIM1_BRK_TIM9_PRIORITY    7\n#define STM32_IRQ_TIM1_UP_TIM10_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM11_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM5_PRIORITY             7\n#define STM32_IRQ_TIM6_PRIORITY             7\n#define STM32_IRQ_TIM7_PRIORITY             7\n#define STM32_IRQ_TIM8_BRK_TIM12_PRIORITY   7\n#define STM32_IRQ_TIM8_UP_TIM13_PRIORITY    7\n#define STM32_IRQ_TIM8_TRGCO_TIM14_PRIORITY 7\n#define STM32_IRQ_TIM8_CC_PRIORITY          7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART3_PRIORITY           12\n#define STM32_IRQ_UART4_PRIORITY            12\n#define STM32_IRQ_UART5_PRIORITY            12\n#define STM32_IRQ_USART6_PRIORITY           12\n#define STM32_IRQ_UART7_PRIORITY            12\n#define STM32_IRQ_UART8_PRIORITY            12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_ADCPRE                    ADC_CCR_ADCPRE_DIV4\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_USE_ADC2                  FALSE\n#define STM32_ADC_USE_ADC3                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(2, 4)\n#define STM32_ADC_ADC2_DMA_STREAM           STM32_DMA_STREAM_ID(2, 2)\n#define STM32_ADC_ADC3_DMA_STREAM           STM32_DMA_STREAM_ID(2, 1)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_ADC2_DMA_PRIORITY         2\n#define STM32_ADC_ADC3_DMA_PRIORITY         2\n#define STM32_ADC_IRQ_PRIORITY              6\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     6\n#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY     6\n#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY     6\n\n/*\n * CAN driver system settings.\n */\n#define STM32_CAN_USE_CAN1                  FALSE\n#define STM32_CAN_USE_CAN2                  FALSE\n#define STM32_CAN_CAN1_IRQ_PRIORITY         11\n#define STM32_CAN_CAN2_IRQ_PRIORITY         11\n\n/*\n * DAC driver system settings.\n */\n#define STM32_DAC_DUAL_MODE                 FALSE\n#define STM32_DAC_USE_DAC1_CH1              FALSE\n#define STM32_DAC_USE_DAC1_CH2              FALSE\n#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH1_DMA_STREAM       STM32_DMA_STREAM_ID(1, 5)\n#define STM32_DAC_DAC1_CH2_DMA_STREAM       STM32_DMA_STREAM_ID(1, 6)\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM6                  FALSE\n#define STM32_GPT_USE_TIM7                  FALSE\n#define STM32_GPT_USE_TIM8                  FALSE\n#define STM32_GPT_USE_TIM9                  FALSE\n#define STM32_GPT_USE_TIM10                 FALSE\n#define STM32_GPT_USE_TIM11                 FALSE\n#define STM32_GPT_USE_TIM12                 FALSE\n#define STM32_GPT_USE_TIM13                 FALSE\n#define STM32_GPT_USE_TIM14                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * I2S driver system settings.\n */\n#define STM32_I2S_USE_SPI2                  FALSE\n#define STM32_I2S_USE_SPI3                  FALSE\n#define STM32_I2S_SPI2_IRQ_PRIORITY         10\n#define STM32_I2S_SPI3_IRQ_PRIORITY         10\n#define STM32_I2S_SPI2_DMA_PRIORITY         1\n#define STM32_I2S_SPI3_DMA_PRIORITY         1\n#define STM32_I2S_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2S_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2S_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2S_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2S_DMA_ERROR_HOOK(i2sp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM8                  FALSE\n#define STM32_ICU_USE_TIM9                  FALSE\n#define STM32_ICU_USE_TIM10                 FALSE\n#define STM32_ICU_USE_TIM11                 FALSE\n#define STM32_ICU_USE_TIM12                 FALSE\n#define STM32_ICU_USE_TIM13                 FALSE\n#define STM32_ICU_USE_TIM14                 FALSE\n\n/*\n * MAC driver system settings.\n */\n#define STM32_MAC_TRANSMIT_BUFFERS          2\n#define STM32_MAC_RECEIVE_BUFFERS           4\n#define STM32_MAC_BUFFERS_SIZE              1522\n#define STM32_MAC_PHY_TIMEOUT               100\n#define STM32_MAC_ETH1_CHANGE_PHY_STATE     TRUE\n#define STM32_MAC_ETH1_IRQ_PRIORITY         13\n#define STM32_MAC_IP_CHECKSUM_OFFLOAD       0\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM8                  FALSE\n#define STM32_PWM_USE_TIM9                  FALSE\n#define STM32_PWM_USE_TIM10                 FALSE\n#define STM32_PWM_USE_TIM11                 FALSE\n#define STM32_PWM_USE_TIM12                 FALSE\n#define STM32_PWM_USE_TIM13                 FALSE\n#define STM32_PWM_USE_TIM14                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SDC driver system settings.\n */\n#define STM32_SDC_SDIO_DMA_PRIORITY         3\n#define STM32_SDC_SDIO_IRQ_PRIORITY         9\n#define STM32_SDC_WRITE_TIMEOUT_MS          1000\n#define STM32_SDC_READ_TIMEOUT_MS           1000\n#define STM32_SDC_CLOCK_ACTIVATION_DELAY    10\n#define STM32_SDC_SDIO_UNALIGNED_SUPPORT    TRUE\n#define STM32_SDC_SDIO_DMA_STREAM           STM32_DMA_STREAM_ID(2, 3)\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART3             FALSE\n#define STM32_SERIAL_USE_UART4              FALSE\n#define STM32_SERIAL_USE_UART5              FALSE\n#define STM32_SERIAL_USE_USART6             FALSE\n#define STM32_SERIAL_USE_UART7              FALSE\n#define STM32_SERIAL_USE_UART8              FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_USE_SPI4                  FALSE\n#define STM32_SPI_USE_SPI5                  FALSE\n#define STM32_SPI_USE_SPI6                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 0)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_SPI_SPI4_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 0)\n#define STM32_SPI_SPI4_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 1)\n#define STM32_SPI_SPI5_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI5_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 4)\n#define STM32_SPI_SPI6_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 6)\n#define STM32_SPI_SPI6_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 5)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI4_DMA_PRIORITY         1\n#define STM32_SPI_SPI5_DMA_PRIORITY         1\n#define STM32_SPI_SPI6_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_SPI4_IRQ_PRIORITY         10\n#define STM32_SPI_SPI5_IRQ_PRIORITY         10\n#define STM32_SPI_SPI6_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART3               FALSE\n#define STM32_UART_USE_UART4                FALSE\n#define STM32_UART_USE_UART5                FALSE\n#define STM32_UART_USE_USART6               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 5)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 5)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART3_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 1)\n#define STM32_UART_USART3_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 3)\n#define STM32_UART_UART4_RX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 2)\n#define STM32_UART_UART4_TX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 4)\n#define STM32_UART_UART5_RX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 0)\n#define STM32_UART_UART5_TX_DMA_STREAM      STM32_DMA_STREAM_ID(1, 7)\n#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 2)\n#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART3_DMA_PRIORITY      0\n#define STM32_UART_UART4_DMA_PRIORITY       0\n#define STM32_UART_UART5_DMA_PRIORITY       0\n#define STM32_UART_USART6_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_OTG1                  TRUE\n#define STM32_USB_USE_OTG2                  FALSE\n#define STM32_USB_OTG1_IRQ_PRIORITY         14\n#define STM32_USB_OTG2_IRQ_PRIORITY         14\n#define STM32_USB_OTG1_RX_FIFO_SIZE         512\n#define STM32_USB_OTG2_RX_FIFO_SIZE         1024\n#define STM32_USB_HOST_WAKEUP_DURATION      2\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G0B1XB/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_G0B1RE/board.c\n\n# Extra files\nBOARDSRC += $(BOARD_PATH)/board/extra.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_G0B1RE\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G0B1XB/board/extra.c",
    "content": "// Copyright 2025 Stefan Kerkmann (@karlk90)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <hal.h>\n\n#define FLASH_KEY1 0x45670123U\n#define FLASH_KEY2 0xCDEF89ABU\n#define FLASH_OPTKEY1 0x08192A3BU\n#define FLASH_OPTKEY2 0x4C5D6E7FU\n#define FLASH_OPTR_CLR_MASK (FLASH_OPTR_nBOOT_SEL)\n#define FLASH_OPTR_SET_MASK (FLASH_OPTR_NRST_MODE_Msk)\n\nstatic void wait_for_flash(void) {\n    while (READ_BIT(FLASH->SR, FLASH_SR_BSY1)) {\n    }\n}\n\nvoid __attribute__((constructor)) enable_boot0_and_nrst_pin(void) {\n    // Only apply on STM32G0x1 devices, see RM0444 Rev 6, Table 265: \"DEV_ID\n    // and REV_ID field values.\"\n    switch (READ_BIT(DBG->IDCODE, DBG_IDCODE_DEV_ID)) {\n        case 0x467: // STM32G0B1xx and STM32G0C1xx\n        case 0x460: // STM32G071xx and STM32G081xx\n        case 0x456: // STM32G051xx and STM32G061xx\n        case 0x466: // STM32G041xx and STM32G031xx\n            break;\n        default:\n            return;\n    }\n\n    uint32_t optr = FLASH->OPTR;\n\n    // Make sure that:\n    // 1. legacy boot0 pin handling is enabled.\n    //   OPTR[24] = 0\n    // 2. legacy nRST pin handling is enabled.\n    //   OPTR[28:27] = 0b11\n    // To match the default behavior found in older (F0/F1/F3/F4) STM32 devices.\n    if (READ_BIT(optr, FLASH_OPTR_CLR_MASK) || (READ_BIT(optr, FLASH_OPTR_SET_MASK) != FLASH_OPTR_SET_MASK)) {\n        if (READ_BIT(FLASH->CR, FLASH_CR_LOCK)) {\n            WRITE_REG(FLASH->KEYR, FLASH_KEY1);\n            WRITE_REG(FLASH->KEYR, FLASH_KEY2);\n            while (READ_BIT(FLASH->CR, FLASH_CR_LOCK)) {\n            }\n            wait_for_flash();\n        }\n        if (READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK)) {\n            WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY1);\n            WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY2);\n            while (READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK)) {\n            }\n            wait_for_flash();\n        }\n\n        MODIFY_REG(FLASH->OPTR, FLASH_OPTR_CLR_MASK, FLASH_OPTR_SET_MASK);\n        wait_for_flash();\n\n        SET_BIT(FLASH->CR, FLASH_CR_OPTSTRT);\n        wait_for_flash();\n\n        CLEAR_BIT(FLASH->CR, FLASH_CR_OPTSTRT);\n        wait_for_flash();\n\n        // Launch the option byte (re)loading, which resets the device. This\n        // should not return.\n        SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH);\n    }\n}\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G0B1XB/configs/config.h",
    "content": "// Copyright 2024 Stefan Kerkmann (@karlk90)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G0B1XB/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32G0xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 3...0        Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n#define STM32G0xx_MCUCONF\n#define STM32G0B1_MCUCONF\n#define STM32G0C1_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_CLOCK_DYNAMIC                 TRUE\n#define STM32_VOS                           STM32_VOS_RANGE1\n#define STM32_PWR_CR2                       (STM32_PVDRT_LEV0 | STM32_PVDFT_LEV0 | STM32_PVDE_DISABLED)\n#define STM32_PWR_CR3                       (PWR_CR3_EIWUL)\n#define STM32_PWR_CR4                       (0U)\n#define STM32_PWR_PUCRA                     (0U)\n#define STM32_PWR_PDCRA                     (0U)\n#define STM32_PWR_PUCRB                     (0U)\n#define STM32_PWR_PDCRB                     (0U)\n#define STM32_PWR_PUCRC                     (0U)\n#define STM32_PWR_PDCRC                     (0U)\n#define STM32_PWR_PUCRD                     (0U)\n#define STM32_PWR_PDCRD                     (0U)\n#define STM32_PWR_PUCRE                     (0U)\n#define STM32_PWR_PDCRE                     (0U)\n#define STM32_PWR_PUCRF                     (0U)\n#define STM32_PWR_PDCRF                     (0U)\n#define STM32_HSIDIV_VALUE                  1\n#define STM32_HSI16_ENABLED                 TRUE\n#define STM32_HSI48_ENABLED                 TRUE\n#define STM32_HSE_ENABLED                   FALSE\n#define STM32_LSI_ENABLED                   FALSE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_SW                            STM32_SW_PLLRCLK\n#define STM32_PLLSRC                        STM32_PLLSRC_HSI16\n#define STM32_PLLM_VALUE                    2\n#define STM32_PLLN_VALUE                    16\n#define STM32_PLLP_VALUE                    2\n#define STM32_PLLQ_VALUE                    4\n#define STM32_PLLR_VALUE                    2\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE                          STM32_PPRE_DIV1\n#define STM32_MCOSEL                        STM32_MCOSEL_NOCLOCK\n#define STM32_MCOPRE                        STM32_MCOPRE_DIV1\n#define STM32_LSCOSEL                       STM32_LSCOSEL_NOCLOCK\n\n/*\n * Peripherals clocks and sources.\n */\n#define STM32_FDCANSEL                      STM32_USBSEL_HSI48\n#define STM32_USBSEL                        STM32_USBSEL_HSI48\n#define STM32_USART1SEL                     STM32_USART1SEL_SYSCLK\n#define STM32_USART2SEL                     STM32_USART2SEL_SYSCLK\n#define STM32_USART3SEL                     STM32_USART3SEL_SYSCLK\n#define STM32_LPUART1SEL                    STM32_LPUART1SEL_SYSCLK\n#define STM32_LPUART2SEL                    STM32_LPUART2SEL_SYSCLK\n#define STM32_CECSEL                        STM32_CECSEL_HSI16DIV\n#define STM32_I2C1SEL                       STM32_I2C1SEL_PCLK\n#define STM32_I2C2SEL                       STM32_I2C1SEL_PCLK\n#define STM32_I2S1SEL                       STM32_I2S1SEL_SYSCLK\n#define STM32_I2S2SEL                       STM32_I2S2SEL_SYSCLK\n#define STM32_LPTIM1SEL                     STM32_LPTIM1SEL_PCLK\n#define STM32_LPTIM2SEL                     STM32_LPTIM2SEL_PCLK\n#define STM32_TIM1SEL                       STM32_TIM1SEL_TIMPCLK\n#define STM32_TIM15SEL                      STM32_TIM15SEL_TIMPCLK\n#define STM32_RNGSEL                        STM32_RNGSEL_HSI16\n#define STM32_RNGDIV_VALUE                  1\n#define STM32_ADCSEL                        STM32_ADCSEL_PLLPCLK\n#define STM32_RTCSEL                        STM32_RTCSEL_NOCLOCK\n\n/*\n * Shared IRQ settings.\n */\n#define STM32_IRQ_EXTI0_1_PRIORITY          3\n#define STM32_IRQ_EXTI2_3_PRIORITY          3\n#define STM32_IRQ_EXTI4_15_PRIORITY         3\n#define STM32_IRQ_EXTI1921_PRIORITY         3\n\n#define STM32_IRQ_USART1_PRIORITY           2\n#define STM32_IRQ_USART2_LP2_PRIORITY       2\n#define STM32_IRQ_USART3_4_5_6_LP1_PRIORITY 2\n\n#define STM32_IRQ_TIM1_UP_PRIORITY          1\n#define STM32_IRQ_TIM1_CC_PRIORITY          1\n#define STM32_IRQ_TIM2_PRIORITY             1\n#define STM32_IRQ_TIM3_4_PRIORITY           1\n#define STM32_IRQ_TIM6_PRIORITY             1\n#define STM32_IRQ_TIM7_PRIORITY             1\n#define STM32_IRQ_TIM14_PRIORITY            1\n#define STM32_IRQ_TIM15_PRIORITY            1\n#define STM32_IRQ_TIM16_PRIORITY            1\n#define STM32_IRQ_TIM17_PRIORITY            1\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_CFGR2                ADC_CFGR2_CKMODE_ADCCLK\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     2\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID_ANY\n#define STM32_ADC_PRESCALER_VALUE           2\n\n/*\n * DAC driver system settings.\n */\n#define STM32_DAC_DUAL_MODE                 FALSE\n#define STM32_DAC_USE_DAC1_CH1              FALSE\n#define STM32_DAC_USE_DAC1_CH2              FALSE\n#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     3\n#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     3\n#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH1_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n#define STM32_DAC_DAC1_CH2_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM6                  FALSE\n#define STM32_GPT_USE_TIM7                  FALSE\n#define STM32_GPT_USE_TIM14                 FALSE\n#define STM32_GPT_USE_TIM15                 FALSE\n#define STM32_GPT_USE_TIM16                 FALSE\n#define STM32_GPT_USE_TIM17                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C1_IRQ_PRIORITY         3\n#define STM32_I2C_I2C2_IRQ_PRIORITY         3\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM15                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM14                 FALSE\n#define STM32_PWM_USE_TIM15                 FALSE\n#define STM32_PWM_USE_TIM16                 FALSE\n#define STM32_PWM_USE_TIM17                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART3             FALSE\n#define STM32_SERIAL_USE_UART4              FALSE\n#define STM32_SERIAL_USE_UART5              FALSE\n#define STM32_SERIAL_USE_USART6             FALSE\n#define STM32_SERIAL_USE_LPUART1            FALSE\n#define STM32_SERIAL_USE_LPUART2            FALSE\n\n/*\n * SIO driver system settings.\n */\n#define STM32_SIO_USE_USART1                FALSE\n#define STM32_SIO_USE_USART2                FALSE\n#define STM32_SIO_USE_USART3                FALSE\n#define STM32_SIO_USE_UART4                 FALSE\n#define STM32_SIO_USE_UART5                 FALSE\n#define STM32_SIO_USE_USART6                FALSE\n#define STM32_SIO_USE_LPUART1               FALSE\n#define STM32_SIO_USE_LPUART2               FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         2\n#define STM32_SPI_SPI2_IRQ_PRIORITY         2\n#define STM32_SPI_SPI3_IRQ_PRIORITY         2\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               2\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * TRNG driver system settings.\n * NOTE: STM32G0C1 only.\n */\n#define STM32_TRNG_USE_RNG1                 FALSE\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART3               FALSE\n#define STM32_UART_USE_UART4                FALSE\n#define STM32_UART_USE_UART5                FALSE\n#define STM32_UART_USE_USART6               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART3_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART3_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART4_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART4_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART5_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART5_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART3_DMA_PRIORITY      0\n#define STM32_UART_UART4_DMA_PRIORITY       0\n#define STM32_UART_UART5_DMA_PRIORITY       0\n#define STM32_UART_USART6_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_USB1                  TRUE\n#define STM32_USB_USB1_LP_IRQ_PRIORITY      3\n#define STM32_USB_USE_ISOCHRONOUS           FALSE\n#define STM32_USB_USE_FAST_COPY             TRUE\n#define STM32_USB_HOST_WAKEUP_DURATION      2\n#define STM32_USB_48MHZ_DELTA               0\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G431XB/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_G431RB/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_G431RB\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G431XB/configs/config.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G431XB/configs/hal_lld.h",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n// TODO: Remove when upstream no longer requires patching\n#include <stm32_registry.h>\n\n#include_next <hal_lld.h>\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G431XB/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32G4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n#define STM32G4xx_MCUCONF\n#define STM32G431_MCUCONF\n#define STM32G441_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_CLOCK_DYNAMIC                 FALSE\n#define STM32_VOS                           STM32_VOS_RANGE1\n#define STM32_PWR_BOOST                     TRUE\n#define STM32_PWR_CR2                       (PWR_CR2_PLS_LEV0)\n#define STM32_PWR_CR3                       (PWR_CR3_EIWF)\n#define STM32_PWR_CR4                       (0U)\n#define STM32_PWR_PUCRA                     (0U)\n#define STM32_PWR_PDCRA                     (0U)\n#define STM32_PWR_PUCRB                     (0U)\n#define STM32_PWR_PDCRB                     (0U)\n#define STM32_PWR_PUCRC                     (0U)\n#define STM32_PWR_PDCRC                     (0U)\n#define STM32_PWR_PUCRD                     (0U)\n#define STM32_PWR_PDCRD                     (0U)\n#define STM32_PWR_PUCRE                     (0U)\n#define STM32_PWR_PDCRE                     (0U)\n#define STM32_PWR_PUCRF                     (0U)\n#define STM32_PWR_PDCRF                     (0U)\n#define STM32_PWR_PUCRG                     (0U)\n#define STM32_PWR_PDCRG                     (0U)\n#define STM32_HSI16_ENABLED                 TRUE\n#define STM32_HSI48_ENABLED                 TRUE\n#define STM32_HSE_ENABLED                   FALSE\n#define STM32_LSI_ENABLED                   FALSE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_SW                            STM32_SW_PLLRCLK\n#define STM32_PLLSRC                        STM32_PLLSRC_HSI16\n#define STM32_PLLM_VALUE                    4\n#define STM32_PLLN_VALUE                    80\n#define STM32_PLLPDIV_VALUE                 0\n#define STM32_PLLP_VALUE                    7\n#define STM32_PLLQ_VALUE                    8\n#define STM32_PLLR_VALUE                    2\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV1\n#define STM32_PPRE2                         STM32_PPRE2_DIV1\n#define STM32_MCOSEL                        STM32_MCOSEL_NOCLOCK\n#define STM32_MCOPRE                        STM32_MCOPRE_DIV1\n#define STM32_LSCOSEL                       STM32_LSCOSEL_NOCLOCK\n\n/*\n * Peripherals clock sources.\n */\n#define STM32_USART1SEL                     STM32_USART1SEL_SYSCLK\n#define STM32_USART2SEL                     STM32_USART2SEL_SYSCLK\n#define STM32_USART3SEL                     STM32_USART3SEL_SYSCLK\n#define STM32_UART4SEL                      STM32_UART4SEL_SYSCLK\n#define STM32_LPUART1SEL                    STM32_LPUART1SEL_PCLK1\n#define STM32_I2C1SEL                       STM32_I2C1SEL_PCLK1\n#define STM32_I2C2SEL                       STM32_I2C2SEL_PCLK1\n#define STM32_I2C3SEL                       STM32_I2C3SEL_PCLK1\n#define STM32_LPTIM1SEL                     STM32_LPTIM1SEL_PCLK1\n#define STM32_SAI1SEL                       STM32_SAI1SEL_SYSCLK\n#define STM32_I2S23SEL                      STM32_I2S23SEL_SYSCLK\n#define STM32_FDCANSEL                      STM32_FDCANSEL_PCLK1\n#define STM32_CLK48SEL                      STM32_CLK48SEL_HSI48\n#define STM32_ADC12SEL                      STM32_ADC12SEL_PLLPCLK\n#define STM32_RTCSEL                        STM32_RTCSEL_NOCLOCK\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI164041_PRIORITY       6\n#define STM32_IRQ_EXTI17_PRIORITY           6\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI212229_PRIORITY       6\n#define STM32_IRQ_EXTI30_32_PRIORITY        6\n#define STM32_IRQ_EXTI33_PRIORITY           6\n\n#define STM32_IRQ_FDCAN1_PRIORITY           10\n\n#define STM32_IRQ_TIM1_BRK_TIM15_PRIORITY   7\n#define STM32_IRQ_TIM1_UP_TIM16_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM17_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM6_PRIORITY             7\n#define STM32_IRQ_TIM7_PRIORITY             7\n#define STM32_IRQ_TIM8_UP_PRIORITY          7\n#define STM32_IRQ_TIM8_CC_PRIORITY          7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART3_PRIORITY           12\n#define STM32_IRQ_UART4_PRIORITY            12\n#define STM32_IRQ_LPUART1_PRIORITY          12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_DUAL_MODE                 FALSE\n#define STM32_ADC_COMPACT_SAMPLES           FALSE\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_USE_ADC2                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID_ANY\n#define STM32_ADC_ADC2_DMA_STREAM           STM32_DMA_STREAM_ID_ANY\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_ADC2_DMA_PRIORITY         2\n#define STM32_ADC_ADC12_IRQ_PRIORITY        5\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC12_CLOCK_MODE          ADC_CCR_CKMODE_AHB_DIV4\n#define STM32_ADC_ADC12_PRESC               ADC_CCR_PRESC_DIV2\n\n/*\n * CAN driver system settings.\n */\n#define STM32_CAN_USE_FDCAN1                FALSE\n\n/*\n * DAC driver system settings.\n */\n#define STM32_DAC_DUAL_MODE                 FALSE\n#define STM32_DAC_USE_DAC1_CH1              FALSE\n#define STM32_DAC_USE_DAC1_CH2              FALSE\n#define STM32_DAC_USE_DAC3_CH1              FALSE\n#define STM32_DAC_USE_DAC3_CH2              FALSE\n#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC3_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC3_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC3_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC3_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH1_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n#define STM32_DAC_DAC1_CH2_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n#define STM32_DAC_DAC3_CH1_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n#define STM32_DAC_DAC3_CH2_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM6                  FALSE\n#define STM32_GPT_USE_TIM7                  FALSE\n#define STM32_GPT_USE_TIM8                  FALSE\n#define STM32_GPT_USE_TIM15                 FALSE\n#define STM32_GPT_USE_TIM16                 FALSE\n#define STM32_GPT_USE_TIM17                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM8                  FALSE\n#define STM32_ICU_USE_TIM15                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM8                  FALSE\n#define STM32_PWM_USE_TIM15                 FALSE\n#define STM32_PWM_USE_TIM16                 FALSE\n#define STM32_PWM_USE_TIM17                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_TAMP_CR1_INIT                 0\n#define STM32_TAMP_CR2_INIT                 0\n#define STM32_TAMP_FLTCR_INIT               0\n#define STM32_TAMP_IER_INIT                 0\n\n/*\n * SDC driver system settings.\n */\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART3             FALSE\n#define STM32_SERIAL_USE_UART4              FALSE\n#define STM32_SERIAL_USE_LPUART1            FALSE\n\n/*\n * SIO driver system settings.\n */\n#define STM32_SIO_USE_USART1                FALSE\n#define STM32_SIO_USE_USART2                FALSE\n#define STM32_SIO_USE_USART3                FALSE\n#define STM32_SIO_USE_UART4                 FALSE\n#define STM32_SIO_USE_LPUART1               FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * TRNG driver system settings.\n */\n#define STM32_TRNG_USE_RNG1                 FALSE\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART3               FALSE\n#define STM32_UART_USE_UART4                FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART3_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART3_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART4_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART4_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART3_DMA_PRIORITY      0\n#define STM32_UART_UART4_DMA_PRIORITY       0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_USB1                  TRUE\n#define STM32_USB_LOW_POWER_ON_SUSPEND      FALSE\n#define STM32_USB_USB1_HP_IRQ_PRIORITY      5\n#define STM32_USB_USB1_LP_IRQ_PRIORITY      6\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G431XB/configs/stm32_registry.h",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n// TODO: Remove when STM32_FLASH_SECTORS_PER_BANK fixed upstream\n#ifndef STM32_FLASH_SECTORS_PER_BANK\n#    define STM32_FLASH_SECTORS_PER_BANK 64\n#endif\n\n#include_next <stm32_registry.h>\n\n// TODO: Remove when STM32_FLASH_NUMBER_OF_BANKS fixed upstream\n#undef STM32_FLASH_NUMBER_OF_BANKS\n#define STM32_FLASH_NUMBER_OF_BANKS 1\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G474XE/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_G474RE/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_G474RE\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G474XE/configs/config.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#ifndef STM32_BOOTLOADER_DUAL_BANK\n#    define STM32_BOOTLOADER_DUAL_BANK FALSE\n#endif\n\n// To Enter bootloader from `RESET` keycode, you'll need to dedicate a GPIO to\n// charge an RC network on the BOOT0 pin.\n// See the QMK Discord's #hardware channel pins for an example circuit.\n// Insert these two lines into your keyboard's `config.h` file.\n// In the case below, PB7 is selected to charge.\n#if 0\n#define STM32_BOOTLOADER_DUAL_BANK TRUE\n#define STM32_BOOTLOADER_DUAL_BANK_GPIO B7\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_G474XE/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32G4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n#define STM32G4xx_MCUCONF\n#define STM32G473_MCUCONF\n#define STM32G483_MCUCONF\n#define STM32G474_MCUCONF\n#define STM32G484_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_CLOCK_DYNAMIC                 FALSE\n#define STM32_VOS                           STM32_VOS_RANGE1\n#define STM32_PWR_BOOST                     TRUE\n#define STM32_PWR_CR2                       (PWR_CR2_PLS_LEV0)\n#define STM32_PWR_CR3                       (PWR_CR3_EIWF)\n#define STM32_PWR_CR4                       (0U)\n#define STM32_PWR_PUCRA                     (0U)\n#define STM32_PWR_PDCRA                     (0U)\n#define STM32_PWR_PUCRB                     (0U)\n#define STM32_PWR_PDCRB                     (0U)\n#define STM32_PWR_PUCRC                     (0U)\n#define STM32_PWR_PDCRC                     (0U)\n#define STM32_PWR_PUCRD                     (0U)\n#define STM32_PWR_PDCRD                     (0U)\n#define STM32_PWR_PUCRE                     (0U)\n#define STM32_PWR_PDCRE                     (0U)\n#define STM32_PWR_PUCRF                     (0U)\n#define STM32_PWR_PDCRF                     (0U)\n#define STM32_PWR_PUCRG                     (0U)\n#define STM32_PWR_PDCRG                     (0U)\n#define STM32_HSI16_ENABLED                 TRUE\n#define STM32_HSI48_ENABLED                 TRUE\n#define STM32_HSE_ENABLED                   FALSE\n#define STM32_LSI_ENABLED                   FALSE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_SW                            STM32_SW_PLLRCLK\n#define STM32_PLLSRC                        STM32_PLLSRC_HSI16\n#define STM32_PLLM_VALUE                    2\n#define STM32_PLLN_VALUE                    40\n#define STM32_PLLPDIV_VALUE                 0\n#define STM32_PLLP_VALUE                    7\n#define STM32_PLLQ_VALUE                    2\n#define STM32_PLLR_VALUE                    2\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV1\n#define STM32_PPRE2                         STM32_PPRE2_DIV1\n#define STM32_MCOSEL                        STM32_MCOSEL_NOCLOCK\n#define STM32_MCOPRE                        STM32_MCOPRE_DIV1\n#define STM32_LSCOSEL                       STM32_LSCOSEL_NOCLOCK\n\n/*\n * Peripherals clock sources.\n */\n#define STM32_USART1SEL                     STM32_USART1SEL_SYSCLK\n#define STM32_USART2SEL                     STM32_USART2SEL_SYSCLK\n#define STM32_USART3SEL                     STM32_USART3SEL_SYSCLK\n#define STM32_UART4SEL                      STM32_UART4SEL_SYSCLK\n#define STM32_UART5SEL                      STM32_UART5SEL_SYSCLK\n#define STM32_LPUART1SEL                    STM32_LPUART1SEL_PCLK1\n#define STM32_I2C1SEL                       STM32_I2C1SEL_PCLK1\n#define STM32_I2C2SEL                       STM32_I2C2SEL_PCLK1\n#define STM32_I2C3SEL                       STM32_I2C3SEL_PCLK1\n#define STM32_I2C4SEL                       STM32_I2C4SEL_PCLK1\n#define STM32_LPTIM1SEL                     STM32_LPTIM1SEL_PCLK1\n#define STM32_SAI1SEL                       STM32_SAI1SEL_SYSCLK\n#define STM32_I2S23SEL                      STM32_I2S23SEL_SYSCLK\n#define STM32_FDCANSEL                      STM32_FDCANSEL_HSE\n#define STM32_CLK48SEL                      STM32_CLK48SEL_HSI48\n#define STM32_ADC12SEL                      STM32_ADC12SEL_PLLPCLK\n#define STM32_ADC345SEL                     STM32_ADC345SEL_PLLPCLK\n#define STM32_QSPISEL                       STM32_QSPISEL_SYSCLK\n#define STM32_RTCSEL                        STM32_RTCSEL_NOCLOCK\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI164041_PRIORITY       6\n#define STM32_IRQ_EXTI17_PRIORITY           6\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI212229_PRIORITY       6\n#define STM32_IRQ_EXTI30_32_PRIORITY        6\n#define STM32_IRQ_EXTI33_PRIORITY           6\n\n#define STM32_IRQ_FDCAN1_PRIORITY           10\n#define STM32_IRQ_FDCAN2_PRIORITY           10\n#define STM32_IRQ_FDCAN3_PRIORITY           10\n\n#define STM32_IRQ_TIM1_BRK_TIM15_PRIORITY   7\n#define STM32_IRQ_TIM1_UP_TIM16_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM17_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM5_PRIORITY             7\n#define STM32_IRQ_TIM6_PRIORITY             7\n#define STM32_IRQ_TIM7_PRIORITY             7\n#define STM32_IRQ_TIM8_UP_PRIORITY          7\n#define STM32_IRQ_TIM8_CC_PRIORITY          7\n#define STM32_IRQ_TIM20_UP_PRIORITY         7\n#define STM32_IRQ_TIM20_CC_PRIORITY         7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART3_PRIORITY           12\n#define STM32_IRQ_UART4_PRIORITY            12\n#define STM32_IRQ_UART5_PRIORITY            12\n#define STM32_IRQ_LPUART1_PRIORITY          12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_DUAL_MODE                 FALSE\n#define STM32_ADC_COMPACT_SAMPLES           FALSE\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_USE_ADC2                  FALSE\n#define STM32_ADC_USE_ADC3                  FALSE\n#define STM32_ADC_USE_ADC4                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID_ANY\n#define STM32_ADC_ADC2_DMA_STREAM           STM32_DMA_STREAM_ID_ANY\n#define STM32_ADC_ADC3_DMA_STREAM           STM32_DMA_STREAM_ID_ANY\n#define STM32_ADC_ADC4_DMA_STREAM           STM32_DMA_STREAM_ID_ANY\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_ADC2_DMA_PRIORITY         2\n#define STM32_ADC_ADC3_DMA_PRIORITY         2\n#define STM32_ADC_ADC4_DMA_PRIORITY         2\n#define STM32_ADC_ADC12_IRQ_PRIORITY        5\n#define STM32_ADC_ADC3_IRQ_PRIORITY         5\n#define STM32_ADC_ADC4_IRQ_PRIORITY         5\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC4_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC12_CLOCK_MODE          ADC_CCR_CKMODE_AHB_DIV4\n#define STM32_ADC_ADC345_CLOCK_MODE         ADC_CCR_CKMODE_AHB_DIV4\n#define STM32_ADC_ADC12_PRESC               ADC_CCR_PRESC_DIV2\n#define STM32_ADC_ADC345_PRESC              ADC_CCR_PRESC_DIV2\n\n/*\n * CAN driver system settings.\n */\n#define STM32_CAN_USE_FDCAN1                FALSE\n#define STM32_CAN_USE_FDCAN2                FALSE\n#define STM32_CAN_USE_FDCAN3                FALSE\n\n/*\n * DAC driver system settings.\n */\n#define STM32_DAC_DUAL_MODE                 FALSE\n#define STM32_DAC_USE_DAC1_CH1              FALSE\n#define STM32_DAC_USE_DAC1_CH2              FALSE\n#define STM32_DAC_USE_DAC2_CH1              FALSE\n#define STM32_DAC_USE_DAC3_CH1              FALSE\n#define STM32_DAC_USE_DAC3_CH2              FALSE\n#define STM32_DAC_USE_DAC4_CH1              FALSE\n#define STM32_DAC_USE_DAC4_CH2              FALSE\n#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC2_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC3_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC3_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC4_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC4_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC2_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC3_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC3_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC4_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC4_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH1_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n#define STM32_DAC_DAC1_CH2_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n#define STM32_DAC_DAC2_CH1_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n#define STM32_DAC_DAC3_CH1_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n#define STM32_DAC_DAC3_CH2_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n#define STM32_DAC_DAC4_CH1_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n#define STM32_DAC_DAC4_CH2_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM6                  FALSE\n#define STM32_GPT_USE_TIM7                  FALSE\n#define STM32_GPT_USE_TIM8                  FALSE\n#define STM32_GPT_USE_TIM15                 FALSE\n#define STM32_GPT_USE_TIM16                 FALSE\n#define STM32_GPT_USE_TIM17                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_USE_I2C4                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C4_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C4_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C4_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_I2C4_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM8                  FALSE\n#define STM32_ICU_USE_TIM15                 FALSE\n#define STM32_ICU_USE_TIM16                 FALSE\n#define STM32_ICU_USE_TIM17                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM8                  FALSE\n#define STM32_PWM_USE_TIM15                 FALSE\n#define STM32_PWM_USE_TIM16                 FALSE\n#define STM32_PWM_USE_TIM17                 FALSE\n#define STM32_PWM_USE_TIM20                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_TAMP_CR1_INIT                 0\n#define STM32_TAMP_CR2_INIT                 0\n#define STM32_TAMP_FLTCR_INIT               0\n#define STM32_TAMP_IER_INIT                 0\n\n/*\n * SDC driver system settings.\n */\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART3             FALSE\n#define STM32_SERIAL_USE_UART4              FALSE\n#define STM32_SERIAL_USE_UART5              FALSE\n#define STM32_SERIAL_USE_LPUART1            FALSE\n\n/*\n * SIO driver system settings.\n */\n#define STM32_SIO_USE_USART1                FALSE\n#define STM32_SIO_USE_USART2                FALSE\n#define STM32_SIO_USE_USART3                FALSE\n#define STM32_SIO_USE_UART4                 FALSE\n#define STM32_SIO_USE_UART5                 FALSE\n#define STM32_SIO_USE_LPUART1               FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_USE_SPI4                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI4_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI4_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI4_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_SPI4_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * TRNG driver system settings.\n */\n#define STM32_TRNG_USE_RNG1                 FALSE\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART3               FALSE\n#define STM32_UART_USE_UART4                FALSE\n#define STM32_UART_USE_UART5                FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART3_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART3_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART4_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART4_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART5_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART5_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART3_DMA_PRIORITY      0\n#define STM32_UART_UART4_DMA_PRIORITY       0\n#define STM32_UART_UART5_DMA_PRIORITY       0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_USB1                  TRUE\n#define STM32_USB_LOW_POWER_ON_SUSPEND      FALSE\n#define STM32_USB_USB1_HP_IRQ_PRIORITY      5\n#define STM32_USB_USB1_LP_IRQ_PRIORITY      5\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n/*\n * WSPI driver system settings.\n */\n#define STM32_WSPI_USE_QUADSPI1             FALSE\n#define STM32_WSPI_QUADSPI1_DMA_STREAM      STM32_DMA_STREAM_ID(2, 7)\n#define STM32_WSPI_QUADSPI1_PRESCALER_VALUE 1\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_H723XG/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO144_H723ZG/board.c\n\n# Extra files\nBOARDSRC += $(BOARD_PATH)/board/extra.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO144_H723ZG\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_H723XG/board/extra.c",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <hal.h>\n#define BOOTLOADER_MAGIC 0xDEADBEEF\n\n////////////////////////////////////////////////////////////////////////////////\n// Different signalling for bootloader entry\n// - RAM is cleared on reset, so we can't use the usual __ram0_end__ symbol.\n// - Use backup registers in the RTC peripheral to store the magic value instead.\n\nstatic inline void enable_backup_register_access(void) {\n    PWR->CR1 |= PWR_CR1_DBP;\n}\n\nstatic inline void disable_backup_register_access(void) {\n    PWR->CR1 &= ~PWR_CR1_DBP;\n}\n\nvoid bootloader_marker_enable(void) {\n    enable_backup_register_access();\n    RTC->BKP0R = BOOTLOADER_MAGIC;\n    disable_backup_register_access();\n}\n\nbool bootloader_marker_active(void) {\n    enable_backup_register_access();\n    bool ret = RTC->BKP0R == BOOTLOADER_MAGIC;\n    disable_backup_register_access();\n    return ret;\n}\n\nvoid bootloader_marker_disable(void) {\n    enable_backup_register_access();\n    RTC->BKP0R = 0;\n    disable_backup_register_access();\n}\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_H723XG/configs/config.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#define USB_DRIVER USBD2\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_H723XG/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32H723/33/25/35 drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32H7xx_MCUCONF\n#define STM32H723_MCUCONF\n#define STM32H733_MCUCONF\n#define STM32H725_MCUCONF\n#define STM32H735_MCUCONF\n\n/*\n * General settings.\n */\n#define STM32_NO_INIT                       FALSE\n\n/*\n * Memory attributes settings.\n */\n#define STM32_NOCACHE_ENABLE                FALSE\n#define STM32_NOCACHE_MPU_REGION            MPU_REGION_6\n#define STM32_NOCACHE_RBAR                  0x24000000U\n#define STM32_NOCACHE_RASR                  MPU_RASR_SIZE_16K\n\n/*\n * PWR system settings.\n * Reading STM32 Reference Manual is required, settings in PWR_CR3 are\n * very critical.\n * Register constants are taken from the ST header.\n */\n#define STM32_VOS                           STM32_VOS_SCALE0\n#define STM32_PWR_CR1                       (PWR_CR1_SVOS_1 | PWR_CR1_SVOS_0)\n#define STM32_PWR_CR2                       (PWR_CR2_BREN)\n#define STM32_PWR_CR3                       (PWR_CR3_LDOEN | PWR_CR3_USB33DEN)\n#define STM32_PWR_CPUCR                     0\n\n/*\n * Clock tree static settings.\n * Reading STM32 Reference Manual is required.\n */\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   FALSE\n#define STM32_CSI_ENABLED                   FALSE\n#define STM32_HSI48_ENABLED                 TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_HSIDIV                        STM32_HSIDIV_DIV1\n\n/*\n * PLLs static settings.\n * Reading STM32 Reference Manual is required.\n */\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE_CK\n#define STM32_PLLCFGR_MASK                  ~0\n#define STM32_PLL1_ENABLED                  TRUE\n#define STM32_PLL1_P_ENABLED                TRUE\n#define STM32_PLL1_Q_ENABLED                TRUE\n#define STM32_PLL1_R_ENABLED                TRUE\n#define STM32_PLL1_DIVM_VALUE               4\n#define STM32_PLL1_DIVN_VALUE               275\n#define STM32_PLL1_FRACN_VALUE              0\n#define STM32_PLL1_DIVP_VALUE               1\n#define STM32_PLL1_DIVQ_VALUE               10\n#define STM32_PLL1_DIVR_VALUE               4\n#define STM32_PLL2_ENABLED                  TRUE\n#define STM32_PLL2_P_ENABLED                TRUE\n#define STM32_PLL2_Q_ENABLED                TRUE\n#define STM32_PLL2_R_ENABLED                TRUE\n#define STM32_PLL2_DIVM_VALUE               4\n#define STM32_PLL2_DIVN_VALUE               400\n#define STM32_PLL2_FRACN_VALUE              0\n#define STM32_PLL2_DIVP_VALUE               40\n#define STM32_PLL2_DIVQ_VALUE               8\n#define STM32_PLL2_DIVR_VALUE               8\n#define STM32_PLL3_ENABLED                  TRUE\n#define STM32_PLL3_P_ENABLED                TRUE\n#define STM32_PLL3_Q_ENABLED                TRUE\n#define STM32_PLL3_R_ENABLED                TRUE\n#define STM32_PLL3_DIVM_VALUE               4\n#define STM32_PLL3_DIVN_VALUE               240\n#define STM32_PLL3_FRACN_VALUE              0\n#define STM32_PLL3_DIVP_VALUE               10\n#define STM32_PLL3_DIVQ_VALUE               10\n#define STM32_PLL3_DIVR_VALUE               10\n\n/*\n * Core clocks dynamic settings (can be changed at runtime).\n * Reading STM32 Reference Manual is required.\n */\n#define STM32_SW                            STM32_SW_PLL1_P_CK\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI_CK\n#define STM32_D1CPRE                        STM32_D1CPRE_DIV1\n#define STM32_D1HPRE                        STM32_D1HPRE_DIV2\n#define STM32_D1PPRE3                       STM32_D1PPRE3_DIV2\n#define STM32_D2PPRE1                       STM32_D2PPRE1_DIV2\n#define STM32_D2PPRE2                       STM32_D2PPRE2_DIV2\n#define STM32_D3PPRE4                       STM32_D3PPRE4_DIV2\n\n/*\n * Peripherals clocks static settings.\n * Reading STM32 Reference Manual is required.\n */\n#define STM32_MCO1SEL                       STM32_MCO1SEL_HSI_CK\n#define STM32_MCO1PRE_VALUE                 4\n#define STM32_MCO2SEL                       STM32_MCO2SEL_SYS_CK\n#define STM32_MCO2PRE_VALUE                 4\n#define STM32_TIMPRE_ENABLE                 TRUE\n#define STM32_HRTIMSEL                      0\n#define STM32_STOPKERWUCK                   0\n#define STM32_STOPWUCK                      0\n#define STM32_RTCPRE_VALUE                  8\n#define STM32_CKPERSEL                      STM32_CKPERSEL_HSE_CK\n#define STM32_SDMMCSEL                      STM32_SDMMCSEL_PLL1_Q_CK\n#define STM32_OCTOSPISEL                    STM32_OCTOSPISEL_HCLK\n#define STM32_FMCSEL                        STM32_FMCSEL_HCLK\n#define STM32_SWPSEL                        STM32_SWPSEL_PCLK1\n#define STM32_FDCANSEL                      STM32_FDCANSEL_HSE_CK\n#define STM32_DFSDM1SEL                     STM32_DFSDM1SEL_PCLK2\n#define STM32_SPDIFSEL                      STM32_SPDIFSEL_PLL1_Q_CK\n#define STM32_SPI45SEL                      STM32_SPI45SEL_PCLK2\n#define STM32_SPI123SEL                     STM32_SPI123SEL_PLL1_Q_CK\n#define STM32_SAI1SEL                       STM32_SAI1SEL_PLL1_Q_CK\n#define STM32_LPTIM1SEL                     STM32_LPTIM1SEL_PCLK1\n#define STM32_CECSEL                        STM32_CECSEL_LSE_CK\n#define STM32_USBSEL                        STM32_USBSEL_PLL3_Q_CK\n#define STM32_I2C1235SEL                    STM32_I2C1235SEL_PCLK1\n#define STM32_RNGSEL                        STM32_RNGSEL_PLL1_Q_CK\n#define STM32_USART16910SEL                 STM32_USART16910SEL_PCLK2\n#define STM32_USART234578SEL                STM32_USART234578SEL_PCLK1\n#define STM32_SPI6SEL                       STM32_SPI6SEL_PCLK4\n#define STM32_SAI4BSEL                      STM32_SAI4BSEL_PLL1_Q_CK\n#define STM32_SAI4ASEL                      STM32_SAI4ASEL_PLL1_Q_CK\n#define STM32_ADCSEL                        STM32_ADCSEL_PLL2_P_CK\n#define STM32_LPTIM345SEL                   STM32_LPTIM345SEL_PCLK4\n#define STM32_LPTIM2SEL                     STM32_LPTIM2SEL_PCLK4\n#define STM32_I2C4SEL                       STM32_I2C4SEL_PCLK4\n#define STM32_LPUART1SEL                    STM32_LPUART1SEL_PCLK4\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           6\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_21_PRIORITY        6\n\n#define STM32_IRQ_FDCAN1_PRIORITY           10\n#define STM32_IRQ_FDCAN2_PRIORITY           10\n#define STM32_IRQ_FDCAN3_PRIORITY           10\n\n#define STM32_IRQ_MDMA_PRIORITY             9\n\n#define STM32_IRQ_OCTOSPI1_PRIORITY         10\n#define STM32_IRQ_OCTOSPI2_PRIORITY         10\n\n#define STM32_IRQ_SDMMC1_PRIORITY           9\n#define STM32_IRQ_SDMMC2_PRIORITY           9\n\n#define STM32_IRQ_TIM1_UP_PRIORITY          7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM5_PRIORITY             7\n#define STM32_IRQ_TIM6_PRIORITY             7\n#define STM32_IRQ_TIM7_PRIORITY             7\n#define STM32_IRQ_TIM8_BRK_TIM12_PRIORITY   7\n#define STM32_IRQ_TIM8_UP_TIM13_PRIORITY    7\n#define STM32_IRQ_TIM8_TRGCO_TIM14_PRIORITY 7\n#define STM32_IRQ_TIM8_CC_PRIORITY          7\n#define STM32_IRQ_TIM15_PRIORITY            7\n#define STM32_IRQ_TIM16_PRIORITY            7\n#define STM32_IRQ_TIM17_PRIORITY            7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART3_PRIORITY           12\n#define STM32_IRQ_UART4_PRIORITY            12\n#define STM32_IRQ_UART5_PRIORITY            12\n#define STM32_IRQ_USART6_PRIORITY           12\n#define STM32_IRQ_UART7_PRIORITY            12\n#define STM32_IRQ_UART8_PRIORITY            12\n#define STM32_IRQ_UART9_PRIORITY            12\n#define STM32_IRQ_USART10_PRIORITY          12\n#define STM32_IRQ_LPUART1_PRIORITY          12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_DUAL_MODE                 FALSE\n#define STM32_ADC_SAMPLES_SIZE              16\n#define STM32_ADC_USE_ADC12                 FALSE\n#define STM32_ADC_ADC12_DMA_STREAM          STM32_DMA_STREAM_ID_ANY\n#define STM32_ADC_ADC12_DMA_PRIORITY        2\n#define STM32_ADC_ADC12_IRQ_PRIORITY        5\n#define STM32_ADC_ADC12_CLOCK_MODE          ADC_CCR_CKMODE_AHB_DIV4\n\n/*\n * CAN driver system settings.\n */\n#define STM32_CAN_USE_FDCAN1                FALSE\n#define STM32_CAN_USE_FDCAN2                FALSE\n#define STM32_CAN_USE_FDCAN3                FALSE\n\n/*\n * DAC driver system settings.\n */\n#define STM32_DAC_DUAL_MODE                 FALSE\n#define STM32_DAC_USE_DAC1_CH1              FALSE\n#define STM32_DAC_USE_DAC1_CH2              FALSE\n#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH1_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n#define STM32_DAC_DAC1_CH2_DMA_STREAM       STM32_DMA_STREAM_ID_ANY\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM6                  FALSE\n#define STM32_GPT_USE_TIM7                  FALSE\n#define STM32_GPT_USE_TIM8                  FALSE\n#define STM32_GPT_USE_TIM12                 FALSE\n#define STM32_GPT_USE_TIM13                 FALSE\n#define STM32_GPT_USE_TIM14                 FALSE\n#define STM32_GPT_USE_TIM15                 FALSE\n#define STM32_GPT_USE_TIM16                 FALSE\n#define STM32_GPT_USE_TIM17                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_USE_I2C4                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_I2C_I2C4_RX_BDMA_STREAM       STM32_BDMA_STREAM_ID_ANY\n#define STM32_I2C_I2C4_TX_BDMA_STREAM       STM32_BDMA_STREAM_ID_ANY\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C4_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_I2C4_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM8                  FALSE\n#define STM32_ICU_USE_TIM12                 FALSE\n#define STM32_ICU_USE_TIM13                 FALSE\n#define STM32_ICU_USE_TIM14                 FALSE\n#define STM32_ICU_USE_TIM15                 FALSE\n#define STM32_ICU_USE_TIM16                 FALSE\n#define STM32_ICU_USE_TIM17                 FALSE\n\n/*\n * MAC driver system settings.\n */\n#define STM32_MAC_TRANSMIT_BUFFERS          2\n#define STM32_MAC_RECEIVE_BUFFERS           4\n#define STM32_MAC_BUFFERS_SIZE              1522\n#define STM32_MAC_PHY_TIMEOUT               100\n#define STM32_MAC_ETH1_CHANGE_PHY_STATE     TRUE\n#define STM32_MAC_ETH1_IRQ_PRIORITY         13\n#define STM32_MAC_IP_CHECKSUM_OFFLOAD       0\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM8                  FALSE\n#define STM32_PWM_USE_TIM12                 FALSE\n#define STM32_PWM_USE_TIM13                 FALSE\n#define STM32_PWM_USE_TIM14                 FALSE\n#define STM32_PWM_USE_TIM15                 FALSE\n#define STM32_PWM_USE_TIM16                 FALSE\n#define STM32_PWM_USE_TIM17                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SDC driver system settings.\n */\n#define STM32_SDC_USE_SDMMC1                FALSE\n#define STM32_SDC_USE_SDMMC2                FALSE\n#define STM32_SDC_SDMMC_UNALIGNED_SUPPORT   TRUE\n#define STM32_SDC_SDMMC_WRITE_TIMEOUT       10000\n#define STM32_SDC_SDMMC_READ_TIMEOUT        10000\n#define STM32_SDC_SDMMC_CLOCK_DELAY         10\n#define STM32_SDC_SDMMC_PWRSAV              TRUE\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART3             FALSE\n#define STM32_SERIAL_USE_UART4              FALSE\n#define STM32_SERIAL_USE_UART5              FALSE\n#define STM32_SERIAL_USE_USART6             FALSE\n#define STM32_SERIAL_USE_UART7              FALSE\n#define STM32_SERIAL_USE_UART8              FALSE\n#define STM32_SERIAL_USE_UART9              FALSE\n#define STM32_SERIAL_USE_USART10            FALSE\n#define STM32_SERIAL_USE_LPUART1            FALSE\n\n/*\n * SIO driver system settings.\n */\n#define STM32_SIO_USE_USART1                FALSE\n#define STM32_SIO_USE_USART2                FALSE\n#define STM32_SIO_USE_USART3                FALSE\n#define STM32_SIO_USE_UART4                 FALSE\n#define STM32_SIO_USE_UART5                 FALSE\n#define STM32_SIO_USE_USART6                FALSE\n#define STM32_SIO_USE_UART7                 FALSE\n#define STM32_SIO_USE_UART8                 FALSE\n#define STM32_SIO_USE_UART9                 FALSE\n#define STM32_SIO_USE_USART10               FALSE\n#define STM32_SIO_USE_LPUART1               FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_USE_SPI4                  FALSE\n#define STM32_SPI_USE_SPI5                  FALSE\n#define STM32_SPI_USE_SPI6                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI4_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI4_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI5_RX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI5_TX_DMA_STREAM        STM32_DMA_STREAM_ID_ANY\n#define STM32_SPI_SPI6_RX_BDMA_STREAM       STM32_BDMA_STREAM_ID_ANY\n#define STM32_SPI_SPI6_TX_BDMA_STREAM       STM32_BDMA_STREAM_ID_ANY\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI4_DMA_PRIORITY         1\n#define STM32_SPI_SPI5_DMA_PRIORITY         1\n#define STM32_SPI_SPI6_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_SPI4_IRQ_PRIORITY         10\n#define STM32_SPI_SPI5_IRQ_PRIORITY         10\n#define STM32_SPI_SPI6_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * TRNG driver system settings.\n */\n#define STM32_TRNG_USE_RNG1                 FALSE\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART3               FALSE\n#define STM32_UART_USE_UART4                FALSE\n#define STM32_UART_USE_UART5                FALSE\n#define STM32_UART_USE_USART6               FALSE\n#define STM32_UART_USE_UART7                FALSE\n#define STM32_UART_USE_UART8                FALSE\n#define STM32_UART_USE_UART9                FALSE\n#define STM32_UART_USE_USART10              FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART3_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART3_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART4_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART4_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART5_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART5_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART7_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART7_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART8_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART8_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART9_RX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_UART9_TX_DMA_STREAM      STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART10_RX_DMA_STREAM    STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART10_TX_DMA_STREAM    STM32_DMA_STREAM_ID_ANY\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART3_DMA_PRIORITY      0\n#define STM32_UART_UART4_DMA_PRIORITY       0\n#define STM32_UART_UART5_DMA_PRIORITY       0\n#define STM32_UART_USART6_DMA_PRIORITY      0\n#define STM32_UART_UART7_DMA_PRIORITY       0\n#define STM32_UART_UART8_DMA_PRIORITY       0\n#define STM32_UART_UART9_DMA_PRIORITY       0\n#define STM32_UART_USART10_DMA_PRIORITY     0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_OTG2                  TRUE\n#define STM32_USB_OTG2_IRQ_PRIORITY         14\n#define STM32_USB_OTG2_RX_FIFO_SIZE         1024\n#define STM32_USB_HOST_WAKEUP_DURATION      2\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n/*\n * WSPI driver system settings.\n */\n#define STM32_WSPI_USE_OCTOSPI1             FALSE\n#define STM32_WSPI_USE_OCTOSPI2             FALSE\n#define STM32_WSPI_OCTOSPI1_PRESCALER_VALUE 1\n#define STM32_WSPI_OCTOSPI2_PRESCALER_VALUE 1\n#define STM32_WSPI_OCTOSPI1_SSHIFT          FALSE\n#define STM32_WSPI_OCTOSPI2_SSHIFT          FALSE\n#define STM32_WSPI_OCTOSPI1_DHQC            FALSE\n#define STM32_WSPI_OCTOSPI2_DHQC            FALSE\n#define STM32_WSPI_OCTOSPI1_MDMA_CHANNEL    STM32_MDMA_CHANNEL_ID_ANY\n#define STM32_WSPI_OCTOSPI2_MDMA_CHANNEL    STM32_MDMA_CHANNEL_ID_ANY\n#define STM32_WSPI_OCTOSPI1_MDMA_PRIORITY   1\n#define STM32_WSPI_OCTOSPI2_MDMA_PRIORITY   1\n#define STM32_WSPI_OCTOSPI1_MDMA_IRQ_PRIORITY 10\n#define STM32_WSPI_OCTOSPI2_MDMA_IRQ_PRIORITY 10\n#define STM32_WSPI_DMA_ERROR_HOOK(wspip)    osalSysHalt(\"MDMA failure\")\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_L412XB/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO32_L432KC/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO32_L432KC\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_L412XB/configs/board.h",
    "content": "/* Copyright 2018-2021 Harrison Chan (@Xelus)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <board.h>\n\n#undef STM32L432xx\n#define STM32L422xx\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_L412XB/configs/config.h",
    "content": "/* Copyright 2018-2021 Harrison Chan (@Xelus)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* Address for jumping to bootloader on STM32 chips. */\n/* It is chip dependent, the correct number can be looked up by checking against ST's application note AN2606.\n */\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_L412XB/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32L4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n#define STM32L4xx_MCUCONF\n#define STM32L422_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_VOS                           STM32_VOS_RANGE1\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_HSI16_ENABLED                 TRUE\n#define STM32_HSI48_ENABLED                 TRUE\n#define STM32_LSI_ENABLED                   FALSE\n#define STM32_HSE_ENABLED                   FALSE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_MSIPLL_ENABLED                FALSE\n#define STM32_MSIRANGE                      STM32_MSIRANGE_4M\n#define STM32_MSISRANGE                     STM32_MSISRANGE_4M\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSI16\n#define STM32_PLLM_VALUE                    4\n#define STM32_PLLN_VALUE                    80\n#define STM32_PLLPDIV_VALUE                 0\n#define STM32_PLLP_VALUE                    7\n#define STM32_PLLQ_VALUE                    4\n#define STM32_PLLR_VALUE                    4\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV1\n#define STM32_PPRE2                         STM32_PPRE2_DIV1\n#define STM32_STOPWUCK                      STM32_STOPWUCK_MSI\n#define STM32_MCOSEL                        STM32_MCOSEL_NOCLOCK\n#define STM32_MCOPRE                        STM32_MCOPRE_DIV1\n#define STM32_LSCOSEL                       STM32_LSCOSEL_NOCLOCK\n#define STM32_PLLSAI1N_VALUE                72\n#define STM32_PLLSAI1PDIV_VALUE             6\n#define STM32_PLLSAI1P_VALUE                7\n#define STM32_PLLSAI1Q_VALUE                6\n#define STM32_PLLSAI1R_VALUE                6\n\n/*\n * Peripherals clock sources.\n */\n#define STM32_USART1SEL                     STM32_USART1SEL_SYSCLK\n#define STM32_USART2SEL                     STM32_USART2SEL_SYSCLK\n#define STM32_LPUART1SEL                    STM32_LPUART1SEL_SYSCLK\n#define STM32_I2C1SEL                       STM32_I2C1SEL_SYSCLK\n#define STM32_I2C3SEL                       STM32_I2C3SEL_SYSCLK\n#define STM32_LPTIM1SEL                     STM32_LPTIM1SEL_PCLK1\n#define STM32_LPTIM2SEL                     STM32_LPTIM2SEL_PCLK1\n#define STM32_SAI1SEL                       STM32_SAI1SEL_OFF\n#define STM32_CLK48SEL                      STM32_CLK48SEL_HSI48\n#define STM32_ADCSEL                        STM32_ADCSEL_SYSCLK\n#define STM32_SWPMI1SEL                     STM32_SWPMI1SEL_PCLK1\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI1635_38_PRIORITY      6\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_22_PRIORITY        15\n\n#define STM32_IRQ_TIM1_BRK_TIM15_PRIORITY   7\n#define STM32_IRQ_TIM1_UP_TIM16_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM17_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM6_PRIORITY             7\n#define STM32_IRQ_TIM7_PRIORITY             7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_LPUART1_PRIORITY          12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_COMPACT_SAMPLES           FALSE\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(1, 1)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_USE_ADC2                  FALSE\n#define STM32_ADC_ADC2_DMA_STREAM           STM32_DMA_STREAM_ID(1, 2)\n#define STM32_ADC_ADC2_DMA_PRIORITY         2\n#define STM32_ADC_ADC12_IRQ_PRIORITY        5\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC123_CLOCK_MODE         ADC_CCR_CKMODE_AHB_DIV1\n#define STM32_ADC_ADC123_PRESC              ADC_CCR_PRESC_DIV2\n\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM6                  FALSE\n#define STM32_GPT_USE_TIM7                  FALSE\n#define STM32_GPT_USE_TIM15                 FALSE\n#define STM32_GPT_USE_TIM16                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM15                 FALSE\n#define STM32_ICU_USE_TIM16                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM15                 FALSE\n#define STM32_PWM_USE_TIM16                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_LPUART1            FALSE\n\n/*\n * SIO driver system settings.\n */\n#define STM32_SIO_USE_USART1                FALSE\n#define STM32_SIO_USE_USART2                FALSE\n#define STM32_SIO_USE_LPUART1               FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 4)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * TRNG driver system settings.\n */\n#define STM32_TRNG_USE_RNG1                 FALSE\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 6)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 7)\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_USB1                  TRUE\n#define STM32_USB_LOW_POWER_ON_SUSPEND      FALSE\n#define STM32_USB_USB1_HP_IRQ_PRIORITY      13\n#define STM32_USB_USB1_LP_IRQ_PRIORITY      14\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n/*\n * WSPI driver system settings.\n */\n#define STM32_WSPI_USE_QUADSPI1             FALSE\n#define STM32_WSPI_QUADSPI1_DMA_STREAM      STM32_DMA_STREAM_ID(2, 7)\n#define STM32_WSPI_QUADSPI1_PRESCALER_VALUE 1\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_L432XC/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO32_L432KC/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO32_L432KC\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_L432XC/configs/config.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_L432XC/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32L4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n#define STM32L4xx_MCUCONF\n#define STM32L432_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_VOS                           STM32_VOS_RANGE1\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_HSI16_ENABLED                 TRUE\n#define STM32_HSI48_ENABLED                 TRUE\n#define STM32_LSI_ENABLED                   FALSE\n#define STM32_HSE_ENABLED                   FALSE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_MSIPLL_ENABLED                FALSE\n#define STM32_MSIRANGE                      STM32_MSIRANGE_4M\n#define STM32_MSISRANGE                     STM32_MSISRANGE_4M\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSI16\n#define STM32_PLLM_VALUE                    1\n#define STM32_PLLN_VALUE                    10\n#define STM32_PLLPDIV_VALUE                 0\n#define STM32_PLLP_VALUE                    7\n#define STM32_PLLQ_VALUE                    2\n#define STM32_PLLR_VALUE                    2\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV1\n#define STM32_PPRE2                         STM32_PPRE2_DIV1\n#define STM32_STOPWUCK                      STM32_STOPWUCK_MSI\n#define STM32_MCOSEL                        STM32_MCOSEL_NOCLOCK\n#define STM32_MCOPRE                        STM32_MCOPRE_DIV1\n#define STM32_LSCOSEL                       STM32_LSCOSEL_NOCLOCK\n#define STM32_PLLSAI1N_VALUE                24\n#define STM32_PLLSAI1PDIV_VALUE             0\n#define STM32_PLLSAI1P_VALUE                7\n#define STM32_PLLSAI1Q_VALUE                2\n#define STM32_PLLSAI1R_VALUE                2\n\n/*\n * Peripherals clock sources.\n */\n#define STM32_USART1SEL                     STM32_USART1SEL_SYSCLK\n#define STM32_USART2SEL                     STM32_USART2SEL_SYSCLK\n#define STM32_LPUART1SEL                    STM32_LPUART1SEL_SYSCLK\n#define STM32_I2C1SEL                       STM32_I2C1SEL_SYSCLK\n#define STM32_I2C3SEL                       STM32_I2C3SEL_SYSCLK\n#define STM32_LPTIM1SEL                     STM32_LPTIM1SEL_PCLK1\n#define STM32_LPTIM2SEL                     STM32_LPTIM2SEL_PCLK1\n#define STM32_SAI1SEL                       STM32_SAI1SEL_OFF\n#define STM32_CLK48SEL                      STM32_CLK48SEL_HSI48\n#define STM32_ADCSEL                        STM32_ADCSEL_SYSCLK\n#define STM32_SWPMI1SEL                     STM32_SWPMI1SEL_PCLK1\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI1635_38_PRIORITY      6\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_22_PRIORITY        15\n\n#define STM32_IRQ_TIM1_BRK_TIM15_PRIORITY   7\n#define STM32_IRQ_TIM1_UP_TIM16_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM17_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM6_PRIORITY             7\n#define STM32_IRQ_TIM7_PRIORITY             7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_LPUART1_PRIORITY          12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_COMPACT_SAMPLES           FALSE\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(1, 1)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_ADC12_IRQ_PRIORITY        5\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC123_CLOCK_MODE         ADC_CCR_CKMODE_AHB_DIV1\n#define STM32_ADC_ADC123_PRESC              ADC_CCR_PRESC_DIV2\n\n/*\n * CAN driver system settings.\n */\n#define STM32_CAN_USE_CAN1                  FALSE\n#define STM32_CAN_CAN1_IRQ_PRIORITY         11\n\n/*\n * DAC driver system settings.\n */\n#define STM32_DAC_DUAL_MODE                 FALSE\n#define STM32_DAC_USE_DAC1_CH1              FALSE\n#define STM32_DAC_USE_DAC1_CH2              FALSE\n#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH1_DMA_STREAM       STM32_DMA_STREAM_ID(2, 4)\n#define STM32_DAC_DAC1_CH2_DMA_STREAM       STM32_DMA_STREAM_ID(1, 4)\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM6                  FALSE\n#define STM32_GPT_USE_TIM7                  FALSE\n#define STM32_GPT_USE_TIM15                 FALSE\n#define STM32_GPT_USE_TIM16                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM15                 FALSE\n#define STM32_ICU_USE_TIM16                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM15                 FALSE\n#define STM32_PWM_USE_TIM16                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_LPUART1            FALSE\n\n/*\n * SIO driver system settings.\n */\n#define STM32_SIO_USE_USART1                FALSE\n#define STM32_SIO_USE_USART2                FALSE\n#define STM32_SIO_USE_LPUART1               FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 4)\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 1)\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 2)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * TRNG driver system settings.\n */\n#define STM32_TRNG_USE_RNG1                 FALSE\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 6)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 7)\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_USB1                  TRUE\n#define STM32_USB_LOW_POWER_ON_SUSPEND      FALSE\n#define STM32_USB_USB1_HP_IRQ_PRIORITY      13\n#define STM32_USB_USB1_LP_IRQ_PRIORITY      14\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n/*\n * WSPI driver system settings.\n */\n#define STM32_WSPI_USE_QUADSPI1             FALSE\n#define STM32_WSPI_QUADSPI1_DMA_STREAM      STM32_DMA_STREAM_ID(2, 7)\n#define STM32_WSPI_QUADSPI1_PRESCALER_VALUE 1\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_L433XC/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO32_L432KC/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO32_L432KC\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_L433XC/configs/board.h",
    "content": "/* Copyright 2018-2021 Harrison Chan (@Xelus)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <board.h>\n\n#undef STM32L432xx\n\n// Pretend that we're an L443xx as the ChibiOS definitions for L4x2/L4x3 mistakenly don't enable GPIOH, I2C2, or SPI2.\n// Until ChibiOS upstream is fixed, this should be kept at L443, as nothing in QMK currently utilises the crypto peripheral on the L443.\n#define STM32L443xx\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_L433XC/configs/config.h",
    "content": "/* Copyright 2018-2021 Harrison Chan (@Xelus)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* Address for jumping to bootloader on STM32 chips. */\n/* It is chip dependent, the correct number can be looked up by checking against ST's application note AN2606.\n */\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_STM32_L433XC/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n        http://www.apache.org/licenses/LICENSE-2.0\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32L4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n#define STM32L4xx_MCUCONF\n#define STM32L443_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_VOS                           STM32_VOS_RANGE1\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_HSI16_ENABLED                 TRUE\n#define STM32_HSI48_ENABLED                 TRUE\n#define STM32_LSI_ENABLED                   FALSE\n#define STM32_HSE_ENABLED                   FALSE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_MSIPLL_ENABLED                FALSE\n#define STM32_MSIRANGE                      STM32_MSIRANGE_4M\n#define STM32_MSISRANGE                     STM32_MSISRANGE_4M\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSI16\n#define STM32_PLLM_VALUE                    1\n#define STM32_PLLN_VALUE                    10\n#define STM32_PLLPDIV_VALUE                 0\n#define STM32_PLLP_VALUE                    7\n#define STM32_PLLQ_VALUE                    2\n#define STM32_PLLR_VALUE                    2\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV1\n#define STM32_PPRE2                         STM32_PPRE2_DIV1\n#define STM32_STOPWUCK                      STM32_STOPWUCK_MSI\n#define STM32_MCOSEL                        STM32_MCOSEL_NOCLOCK\n#define STM32_MCOPRE                        STM32_MCOPRE_DIV1\n#define STM32_LSCOSEL                       STM32_LSCOSEL_NOCLOCK\n#define STM32_PLLSAI1N_VALUE                24\n#define STM32_PLLSAI1PDIV_VALUE             0\n#define STM32_PLLSAI1P_VALUE                7\n#define STM32_PLLSAI1Q_VALUE                2\n#define STM32_PLLSAI1R_VALUE                2\n\n/*\n * Peripherals clock sources.\n */\n#define STM32_USART1SEL                     STM32_USART1SEL_SYSCLK\n#define STM32_USART2SEL                     STM32_USART2SEL_SYSCLK\n#define STM32_USART3SEL                     STM32_USART3SEL_SYSCLK\n#define STM32_LPUART1SEL                    STM32_LPUART1SEL_SYSCLK\n#define STM32_I2C1SEL                       STM32_I2C1SEL_SYSCLK\n#define STM32_I2C2SEL                       STM32_I2C2SEL_SYSCLK\n#define STM32_I2C3SEL                       STM32_I2C3SEL_SYSCLK\n#define STM32_LPTIM1SEL                     STM32_LPTIM1SEL_PCLK1\n#define STM32_LPTIM2SEL                     STM32_LPTIM2SEL_PCLK1\n#define STM32_SAI1SEL                       STM32_SAI1SEL_OFF\n#define STM32_CLK48SEL                      STM32_CLK48SEL_HSI48\n#define STM32_ADCSEL                        STM32_ADCSEL_SYSCLK\n#define STM32_SWPMI1SEL                     STM32_SWPMI1SEL_PCLK1\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI1635_38_PRIORITY      6\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_22_PRIORITY        15\n\n#define STM32_IRQ_TIM1_BRK_TIM15_PRIORITY   7\n#define STM32_IRQ_TIM1_UP_TIM16_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM17_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM6_PRIORITY             7\n#define STM32_IRQ_TIM7_PRIORITY             7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART3_PRIORITY           12\n#define STM32_IRQ_LPUART1_PRIORITY          12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_COMPACT_SAMPLES           FALSE\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(1, 1)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_ADC12_IRQ_PRIORITY        5\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC123_CLOCK_MODE         ADC_CCR_CKMODE_AHB_DIV1\n#define STM32_ADC_ADC123_PRESC              ADC_CCR_PRESC_DIV2\n\n/*\n * CAN driver system settings.\n */\n#define STM32_CAN_USE_CAN1                  FALSE\n#define STM32_CAN_CAN1_IRQ_PRIORITY         11\n\n/*\n * DAC driver system settings.\n */\n#define STM32_DAC_DUAL_MODE                 FALSE\n#define STM32_DAC_USE_DAC1_CH1              FALSE\n#define STM32_DAC_USE_DAC1_CH2              FALSE\n#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH1_DMA_STREAM       STM32_DMA_STREAM_ID(2, 4)\n#define STM32_DAC_DAC1_CH2_DMA_STREAM       STM32_DMA_STREAM_ID(1, 4)\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM6                  FALSE\n#define STM32_GPT_USE_TIM7                  FALSE\n#define STM32_GPT_USE_TIM15                 FALSE\n#define STM32_GPT_USE_TIM16                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 5)\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM15                 FALSE\n#define STM32_ICU_USE_TIM16                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_ADVANCED              FALSE\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM15                 FALSE\n#define STM32_PWM_USE_TIM16                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SDMMC drive system settings.\n */\n#define STM32_SDC_USE_SDMMC1                FALSE\n#define STM32_SDC_SDMMC_UNALIGNED_SUPPORT   TRUE\n#define STM32_SDC_SDMMC_WRITE_TIMEOUT       1000\n#define STM32_SDC_SDMMC_READ_TIMEOUT        1000\n#define STM32_SDC_SDMMC_CLOCK_DELAY         10\n#define STM32_SDC_SDMMC1_DMA_PRIORITY       3\n#define STM32_SDC_SDMMC1_IRQ_PRIORITY       9\n#define STM32_SDC_SDMMC1_DMA_STREAM         STM32_DMA_STREAM_ID(2, 4)\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART3             FALSE\n#define STM32_SERIAL_USE_LPUART1            FALSE\n#define STM32_SERIAL_USART1_PRIORITY        12\n#define STM32_SERIAL_USART2_PRIORITY        12\n#define STM32_SERIAL_USART3_PRIORITY        12\n#define STM32_SERIAL_LPUART1_PRIORITY       12\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 4)\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 5)\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 1)\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 2)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * TRNG driver system settings.\n */\n#define STM32_TRNG_USE_RNG1                 FALSE\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART3               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 6)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 7)\n#define STM32_UART_USART3_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 3)\n#define STM32_UART_USART3_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 2)\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_USB1                  TRUE\n#define STM32_USB_LOW_POWER_ON_SUSPEND      FALSE\n#define STM32_USB_USB1_HP_IRQ_PRIORITY      13\n#define STM32_USB_USB1_LP_IRQ_PRIORITY      14\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n/*\n * WSPI driver system settings.\n */\n#define STM32_WSPI_USE_QUADSPI1             FALSE\n#define STM32_WSPI_QUADSPI1_DMA_STREAM      STM32_DMA_STREAM_ID(2, 7)\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_F3G71XX/board/board.c",
    "content": "/*\n    Copyright (C) 2021 Westberry Technology (ChangZhou) Corp., Ltd\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * This file has been automatically generated using ChibiStudio board\n * generator plugin. Do not edit manually.\n */\n\n#include \"hal.h\"\n\n/*===========================================================================*/\n/* Driver local definitions.                                                 */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver exported variables.                                                */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver local variables and types.                                         */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver local functions.                                                   */\n/*===========================================================================*/\n\nstatic void wb32_gpio_init(void) {\n\n#if WB32_HAS_GPIOA\n  rccEnableAPB1(RCC_APB1ENR_GPIOAEN);\n#endif\n\n#if WB32_HAS_GPIOB\n  rccEnableAPB1(RCC_APB1ENR_GPIOBEN);\n#endif\n\n#if WB32_HAS_GPIOC\n  rccEnableAPB1(RCC_APB1ENR_GPIOCEN);\n#endif\n\n#if WB32_HAS_GPIOD\n  rccEnableAPB1(RCC_APB1ENR_GPIODEN);\n#endif\n}\n\n/*===========================================================================*/\n/* Driver interrupt handlers.                                                */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver exported functions.                                                */\n/*===========================================================================*/\n/*\n * Early initialization code.\n * This initialization must be performed just after stack setup and before\n * any other initialization.\n */\nvoid __early_init(void) {\n\n  wb32_clock_init();\n  wb32_gpio_init();\n}\n\n/**\n * @brief   Board-specific initialization code.\n * @note    You can add your board-specific code here.\n */\nvoid boardInit(void) {\n\n}\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_F3G71XX/board/board.h",
    "content": "#pragma once\n/*\n    Copyright (C) 2021 Westberry Technology (ChangZhou) Corp., Ltd\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * This file has been automatically generated using ChibiStudio board\n * generator plugin. Do not edit manually.\n */\n\n#ifndef BOARD_H\n#define BOARD_H\n\n/*===========================================================================*/\n/* Driver constants.                                                         */\n/*===========================================================================*/\n\n/*\n * Setup board.\n */\n\n/*\n * Board identifier.\n */\n#if !(defined(WB32F3G71x9) || defined(WB32F3G71xB) || defined(WB32F3G71xC))\n  #define WB32F3G71x9\n#endif\n\n#if !defined(WB32F3G71xx)\n  #define WB32F3G71xx\n#endif\n\n/*===========================================================================*/\n/* External declarations.                                                    */\n/*===========================================================================*/\n\n#if !defined(_FROM_ASM_)\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n  void boardInit(void);\n#ifdef __cplusplus\n}\n#endif\n#endif /* _FROM_ASM_ */\n\n#endif /* BOARD_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_F3G71XX/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(BOARD_PATH)/board/board.c\n\n# Required include directories\nBOARDINC = $(BOARD_PATH)/board\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_F3G71XX/configs/chconf.h",
    "content": "/* Copyright 2020 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * This file was auto-generated by:\n *    `qmk chibios-confmigrate -i platforms/chibios/boards/GENERIC_WB32_F3G71XX/configs/chconf.h -r platforms/chibios/boards/common/configs/chconf.h`\n */\n\n#pragma once\n\n#define CH_CFG_ST_TIMEDELTA 0\n\n#include_next <chconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_F3G71XX/configs/config.h",
    "content": "/*  Copyright (C) 2021 Westberry Technology (ChangZhou) Corp., Ltd\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n#define USB_ENDPOINTS_ARE_REORDERABLE\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_F3G71XX/configs/mcuconf.h",
    "content": "/*\n    Copyright (C) 2021 Westberry Technology (ChangZhou) Corp., Ltd\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n#define WB32F3G71xx_MCUCONF  TRUE\n\n/*\n * WB32F3G71 drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n */\n\n/**\n * @name    Internal clock sources\n * @{\n */\n#define WB32_HSECLK                        12000000\n#define WB32_LSECLK                        32768\n\n/*\n * HAL driver system settings.\n */\n#define WB32_NO_INIT                       FALSE\n#define WB32_MHSI_ENABLED                  TRUE\n#define WB32_FHSI_ENABLED                  FALSE\n#define WB32_LSI_ENABLED                   FALSE\n#define WB32_HSE_ENABLED                   TRUE\n#define WB32_LSE_ENABLED                   FALSE\n#define WB32_PLL_ENABLED                   TRUE\n#define WB32_MAINCLKSRC                    WB32_MAINCLKSRC_PLL\n#define WB32_PLLSRC                        WB32_PLLSRC_HSE\n#define WB32_PLLDIV_VALUE                  2\n#define WB32_PLLMUL_VALUE                  12     //The allowed range is 12,16,20,24.\n#define WB32_HPRE                          1\n#define WB32_PPRE1                         1\n#define WB32_PPRE2                         1\n#define WB32_USBPRE                        WB32_USBPRE_DIV1P5\n\n/*\n * EXTI driver system settings.\n */\n#define WB32_IRQ_EXTI0_PRIORITY            6\n#define WB32_IRQ_EXTI1_PRIORITY            6\n#define WB32_IRQ_EXTI2_PRIORITY            6\n#define WB32_IRQ_EXTI3_PRIORITY            6\n#define WB32_IRQ_EXTI4_PRIORITY            6\n#define WB32_IRQ_EXTI5_9_PRIORITY          6\n#define WB32_IRQ_EXTI10_15_PRIORITY        6\n#define WB32_IRQ_EXTI16_PRIORITY           6\n#define WB32_IRQ_EXTI17_PRIORITY           6\n#define WB32_IRQ_EXTI18_PRIORITY           6\n#define WB32_IRQ_EXTI19_PRIORITY           6\n\n/*\n * GPT driver system settings.\n */\n#define WB32_TIM_MAX_CHANNELS              4\n#define WB32_GPT_USE_TIM1                  FALSE\n#define WB32_GPT_USE_TIM2                  FALSE\n#define WB32_GPT_USE_TIM3                  FALSE\n#define WB32_GPT_USE_TIM4                  FALSE\n#define WB32_GPT_TIM1_IRQ_PRIORITY         7\n#define WB32_GPT_TIM2_IRQ_PRIORITY         7\n#define WB32_GPT_TIM3_IRQ_PRIORITY         7\n#define WB32_GPT_TIM4_IRQ_PRIORITY         7\n\n/*\n * ICU driver system settings.\n */\n#define WB32_ICU_USE_TIM1                  FALSE\n#define WB32_ICU_USE_TIM2                  FALSE\n#define WB32_ICU_USE_TIM3                  FALSE\n#define WB32_ICU_USE_TIM4                  FALSE\n#define WB32_ICU_TIM1_IRQ_PRIORITY         7\n#define WB32_ICU_TIM2_IRQ_PRIORITY         7\n#define WB32_ICU_TIM3_IRQ_PRIORITY         7\n#define WB32_ICU_TIM4_IRQ_PRIORITY         7\n\n/*\n * PWM driver system settings.\n */\n#define WB32_PWM_USE_ADVANCED              FALSE\n#define WB32_PWM_USE_TIM1                  FALSE\n#define WB32_PWM_USE_TIM2                  FALSE\n#define WB32_PWM_USE_TIM3                  FALSE\n#define WB32_PWM_USE_TIM4                  FALSE\n#define WB32_PWM_TIM1_IRQ_PRIORITY         7\n#define WB32_PWM_TIM2_IRQ_PRIORITY         7\n#define WB32_PWM_TIM3_IRQ_PRIORITY         7\n#define WB32_PWM_TIM4_IRQ_PRIORITY         7\n\n/*\n * I2C driver system settings.\n */\n#define WB32_I2C_USE_I2C1                  FALSE\n#define WB32_I2C_USE_I2C2                  FALSE\n#define WB32_I2C_BUSY_TIMEOUT              50\n#define WB32_I2C_I2C1_IRQ_PRIORITY         5\n#define WB32_I2C_I2C2_IRQ_PRIORITY         5\n\n/*\n * SERIAL driver system settings.\n */\n#define WB32_SERIAL_USE_UART1             FALSE\n#define WB32_SERIAL_USE_UART2             FALSE\n#define WB32_SERIAL_USE_UART3             FALSE\n#define WB32_SERIAL_USART1_PRIORITY        12\n#define WB32_SERIAL_USART2_PRIORITY        12\n#define WB32_SERIAL_USART3_PRIORITY        12\n\n/*\n * SPI driver system settings.\n */\n#define WB32_SPI_USE_QSPI                   FALSE\n#define WB32_SPI_USE_SPIM2                  FALSE\n#define WB32_SPI_USE_SPIS1                  FALSE\n#define WB32_SPI_USE_SPIS2                  FALSE\n#define WB32_SPI_QSPI_IRQ_PRIORITY          10\n#define WB32_SPI_SPIM2_IRQ_PRIORITY         10\n#define WB32_SPI_SPIS1_IRQ_PRIORITY         10\n#define WB32_SPI_SPIS2_IRQ_PRIORITY         10\n\n/*\n * ST driver system settings.\n */\n#define WB32_ST_IRQ_PRIORITY                8\n#define WB32_ST_USE_TIMER                   2\n\n/*\n * UART driver system settings.\n */\n#define WB32_UART_USE_UART1                 FALSE\n#define WB32_UART_USE_UART2                 FALSE\n#define WB32_UART_USE_UART3                 FALSE\n#define WB32_UART_UART1_IRQ_PRIORITY        12\n#define WB32_UART_UART2_IRQ_PRIORITY        12\n#define WB32_UART_UART3_IRQ_PRIORITY        12\n\n/*\n * USB driver system settings.\n */\n#define WB32_USB_USE_USB1                   TRUE\n#define WB32_USB_USB1_IRQ_PRIORITY          13\n#define WB32_USB_HOST_WAKEUP_DURATION       10\n\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_FQ95XX/board/board.c",
    "content": "/*\n    Copyright (C) 2022 Westberry Technology (ChangZhou) Corp., Ltd\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * This file has been automatically generated using ChibiStudio board\n * generator plugin. Do not edit manually.\n */\n\n#include \"hal.h\"\n\n/*===========================================================================*/\n/* Driver local definitions.                                                 */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver exported variables.                                                */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver local variables and types.                                         */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver local functions.                                                   */\n/*===========================================================================*/\n\nstatic void wb32_gpio_init(void) {\n\n#if WB32_HAS_GPIOA\n  rccEnableAPB1(RCC_APB1ENR_GPIOAEN);\n#endif\n\n#if WB32_HAS_GPIOB\n  rccEnableAPB1(RCC_APB1ENR_GPIOBEN);\n#endif\n\n#if WB32_HAS_GPIOC\n  rccEnableAPB1(RCC_APB1ENR_GPIOCEN);\n#endif\n\n#if WB32_HAS_GPIOD\n  rccEnableAPB1(RCC_APB1ENR_GPIODEN);\n#endif\n}\n\n/*===========================================================================*/\n/* Driver interrupt handlers.                                                */\n/*===========================================================================*/\n\n/*===========================================================================*/\n/* Driver exported functions.                                                */\n/*===========================================================================*/\n/*\n * Early initialization code.\n * This initialization must be performed just after stack setup and before\n * any other initialization.\n */\nvoid __early_init(void) {\n\n  wb32_clock_init();\n  wb32_gpio_init();\n}\n\n/**\n * @brief   Board-specific initialization code.\n * @note    You can add your board-specific code here.\n */\nvoid boardInit(void) {\n\n}\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_FQ95XX/board/board.h",
    "content": "#pragma once\n/*\n    Copyright (C) 2022 Westberry Technology (ChangZhou) Corp., Ltd\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * This file has been automatically generated using ChibiStudio board\n * generator plugin. Do not edit manually.\n */\n\n#ifndef BOARD_H\n#define BOARD_H\n\n/*===========================================================================*/\n/* Driver constants.                                                         */\n/*===========================================================================*/\n\n/*\n * Setup board.\n */\n\n/*\n * Board identifier.\n */\n#if !(defined(WB32FQ95x9) || defined(WB32FQ95xB) || defined(WB32FQ95xC))\n  #define WB32FQ95xB\n#endif\n\n#if !defined(WB32FQ95xx)\n  #define WB32FQ95xx\n#endif\n\n/*===========================================================================*/\n/* External declarations.                                                    */\n/*===========================================================================*/\n\n#if !defined(_FROM_ASM_)\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n  void boardInit(void);\n#ifdef __cplusplus\n}\n#endif\n#endif /* _FROM_ASM_ */\n\n#endif /* BOARD_H */\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_FQ95XX/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(BOARD_PATH)/board/board.c\n\n# Required include directories\nBOARDINC = $(BOARD_PATH)/board\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_FQ95XX/configs/chconf.h",
    "content": "/* Copyright 2020 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\n * This file was auto-generated by:\n *    `qmk chibios-confmigrate -i platforms/chibios/boards/GENERIC_WB32_F3G71XX/configs/chconf.h -r platforms/chibios/boards/common/configs/chconf.h`\n */\n\n#pragma once\n\n#define CH_CFG_ST_TIMEDELTA 0\n\n#include_next <chconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_FQ95XX/configs/config.h",
    "content": "/*  Copyright (C) 2022 Westberry Technology (ChangZhou) Corp., Ltd\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n#define USB_ENDPOINTS_ARE_REORDERABLE\n"
  },
  {
    "path": "platforms/chibios/boards/GENERIC_WB32_FQ95XX/configs/mcuconf.h",
    "content": "/*\n    Copyright (C) 2022 Westberry Technology (ChangZhou) Corp., Ltd\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n#define WB32FQ95xx_MCUCONF  TRUE\n\n/*\n * WB32FQ95 drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n */\n\n/**\n * @name    Internal clock sources\n * @{\n */\n#define WB32_HSECLK                        12000000\n#define WB32_LSECLK                        32768\n\n/*\n * HAL driver system settings.\n */\n#define WB32_NO_INIT                       FALSE\n#define WB32_MHSI_ENABLED                  TRUE\n#define WB32_FHSI_ENABLED                  FALSE\n#define WB32_LSI_ENABLED                   FALSE\n#define WB32_HSE_ENABLED                   TRUE\n#define WB32_LSE_ENABLED                   FALSE\n#define WB32_PLL_ENABLED                   TRUE\n#define WB32_MAINCLKSRC                    WB32_MAINCLKSRC_PLL\n#define WB32_PLLSRC                        WB32_PLLSRC_HSE\n#define WB32_PLLDIV_VALUE                  2\n#define WB32_PLLMUL_VALUE                  12     //The allowed range is 12,16,20,24.\n#define WB32_HPRE                          1\n#define WB32_PPRE1                         1\n#define WB32_PPRE2                         1\n#define WB32_USBPRE                        WB32_USBPRE_DIV1P5\n\n/*\n * EXTI driver system settings.\n */\n#define WB32_IRQ_EXTI0_PRIORITY            6\n#define WB32_IRQ_EXTI1_PRIORITY            6\n#define WB32_IRQ_EXTI2_PRIORITY            6\n#define WB32_IRQ_EXTI3_PRIORITY            6\n#define WB32_IRQ_EXTI4_PRIORITY            6\n#define WB32_IRQ_EXTI5_9_PRIORITY          6\n#define WB32_IRQ_EXTI10_15_PRIORITY        6\n#define WB32_IRQ_EXTI16_PRIORITY           6\n#define WB32_IRQ_EXTI17_PRIORITY           6\n#define WB32_IRQ_EXTI18_PRIORITY           6\n#define WB32_IRQ_EXTI19_PRIORITY           6\n\n/*\n * GPT driver system settings.\n */\n#define WB32_TIM_MAX_CHANNELS              4\n#define WB32_GPT_USE_TIM1                  FALSE\n#define WB32_GPT_USE_TIM2                  FALSE\n#define WB32_GPT_USE_TIM3                  FALSE\n#define WB32_GPT_USE_TIM4                  FALSE\n#define WB32_GPT_TIM1_IRQ_PRIORITY         7\n#define WB32_GPT_TIM2_IRQ_PRIORITY         7\n#define WB32_GPT_TIM3_IRQ_PRIORITY         7\n#define WB32_GPT_TIM4_IRQ_PRIORITY         7\n\n/*\n * ICU driver system settings.\n */\n#define WB32_ICU_USE_TIM1                  FALSE\n#define WB32_ICU_USE_TIM2                  FALSE\n#define WB32_ICU_USE_TIM3                  FALSE\n#define WB32_ICU_USE_TIM4                  FALSE\n#define WB32_ICU_TIM1_IRQ_PRIORITY         7\n#define WB32_ICU_TIM2_IRQ_PRIORITY         7\n#define WB32_ICU_TIM3_IRQ_PRIORITY         7\n#define WB32_ICU_TIM4_IRQ_PRIORITY         7\n\n/*\n * PWM driver system settings.\n */\n#define WB32_PWM_USE_ADVANCED              FALSE\n#define WB32_PWM_USE_TIM1                  FALSE\n#define WB32_PWM_USE_TIM2                  FALSE\n#define WB32_PWM_USE_TIM3                  FALSE\n#define WB32_PWM_USE_TIM4                  FALSE\n#define WB32_PWM_TIM1_IRQ_PRIORITY         7\n#define WB32_PWM_TIM2_IRQ_PRIORITY         7\n#define WB32_PWM_TIM3_IRQ_PRIORITY         7\n#define WB32_PWM_TIM4_IRQ_PRIORITY         7\n\n/*\n * I2C driver system settings.\n */\n#define WB32_I2C_USE_I2C1                  FALSE\n#define WB32_I2C_USE_I2C2                  FALSE\n#define WB32_I2C_BUSY_TIMEOUT              50\n#define WB32_I2C_I2C1_IRQ_PRIORITY         5\n#define WB32_I2C_I2C2_IRQ_PRIORITY         5\n\n/*\n * SERIAL driver system settings.\n */\n#define WB32_SERIAL_USE_UART1             FALSE\n#define WB32_SERIAL_USE_UART2             FALSE\n#define WB32_SERIAL_USE_UART3             FALSE\n#define WB32_SERIAL_USART1_PRIORITY        12\n#define WB32_SERIAL_USART2_PRIORITY        12\n#define WB32_SERIAL_USART3_PRIORITY        12\n\n/*\n * SPI driver system settings.\n */\n#define WB32_SPI_USE_QSPI                   FALSE\n#define WB32_SPI_USE_SPIM2                  FALSE\n#define WB32_SPI_USE_SPIS1                  FALSE\n#define WB32_SPI_USE_SPIS2                  FALSE\n#define WB32_SPI_QSPI_IRQ_PRIORITY          10\n#define WB32_SPI_SPIM2_IRQ_PRIORITY         10\n#define WB32_SPI_SPIS1_IRQ_PRIORITY         10\n#define WB32_SPI_SPIS2_IRQ_PRIORITY         10\n\n/*\n * ST driver system settings.\n */\n#define WB32_ST_IRQ_PRIORITY                8\n#define WB32_ST_USE_TIMER                   2\n\n/*\n * UART driver system settings.\n */\n#define WB32_UART_USE_UART1                 FALSE\n#define WB32_UART_USE_UART2                 FALSE\n#define WB32_UART_USE_UART3                 FALSE\n#define WB32_UART_UART1_IRQ_PRIORITY        12\n#define WB32_UART_UART2_IRQ_PRIORITY        12\n#define WB32_UART_UART3_IRQ_PRIORITY        12\n\n/*\n * USB driver system settings.\n */\n#define WB32_USB_USE_USB1                   TRUE\n#define WB32_USB_USB1_IRQ_PRIORITY          13\n#define WB32_USB_HOST_WAKEUP_DURATION       10\n\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/IC_TEENSY_3_1/board/board.c",
    "content": "/*\n    ChibiOS - Copyright (C) 2015 RedoX https://github.com/RedoXyde\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n#include <hal.h>\n\n#if HAL_USE_PAL || defined(__DOXYGEN__)\n/**\n * @brief   PAL setup.\n * @details Digital I/O ports static configuration as defined in @p board.h.\n *          This variable is used by the HAL when initializing the PAL driver.\n */\nconst PALConfig pal_default_config = {\n    .ports =\n        {\n            {\n                /*\n                 * PORTA setup.\n                 *\n                 * PTA4  - PIN33\n                 * PTA5  - PIN24\n                 * PTA12 - PIN3\n                 * PTA13 - PIN4\n                 *\n                 * PTA18/19 crystal\n                 * PTA0/3 SWD\n                 */\n                .port = IOPORT1,\n                .pads =\n                    {\n                        PAL_MODE_ALTERNATIVE_7, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_ALTERNATIVE_7, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_INPUT_ANALOG, PAL_MODE_INPUT_ANALOG, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED,\n                    },\n            },\n            {\n                /*\n                 * PORTB setup.\n                 *\n                 * PTB0  - PIN16\n                 * PTB1  - PIN17\n                 * PTB2  - PIN19\n                 * PTB3  - PIN18\n                 * PTB16 - PIN0 - UART0_TX\n                 * PTB17 - PIN1 - UART0_RX\n                 * PTB18 - PIN32\n                 * PTB19 - PIN25\n                 */\n                .port = IOPORT2,\n                .pads =\n                    {\n                        PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_ALTERNATIVE_3, PAL_MODE_ALTERNATIVE_3, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED,\n                    },\n            },\n            {\n                /*\n                 * PORTC setup.\n                 *\n                 * PTC0  - PIN15\n                 * PTC1  - PIN22\n                 * PTC2  - PIN23\n                 * PTC3  - PIN9\n                 * PTC4  - PIN10\n                 * PTC5  - PIN13\n                 * PTC6  - PIN11\n                 * PTC7  - PIN12\n                 * PTC8  - PIN28\n                 * PTC9  - PIN27\n                 * PTC10 - PIN29\n                 * PTC11 - PIN30\n                 */\n                .port = IOPORT3,\n                .pads =\n                    {\n                        PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED,\n                    },\n            },\n            {\n                /*\n                 * PORTD setup.\n                 *\n                 * PTD0  - PIN2\n                 * PTD1  - PIN14\n                 * PTD2  - PIN7\n                 * PTD3  - PIN8\n                 * PTD4  - PIN6\n                 * PTD5  - PIN20\n                 * PTD6  - PIN21\n                 * PTD7  - PIN5\n                 */\n                .port = IOPORT4,\n                .pads =\n                    {\n                        PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED,\n                    },\n            },\n            {\n                /*\n                 * PORTE setup.\n                 *\n                 * PTE0  - PIN31\n                 * PTE1  - PIN26\n                 */\n                .port = IOPORT5,\n                .pads =\n                    {\n                        PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_OUTPUT_PUSHPULL, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED, PAL_MODE_UNCONNECTED,\n                    },\n            },\n        },\n};\n#endif\n\n// NOTE: This value comes from kiibohd/controller and is the location of a value\n// which needs to be checked before disabling the watchdog (which happens in\n// k20x_clock_init)\n#define WDOG_TMROUTL *(volatile uint16_t *)0x40052012\n\n/**\n * @brief   Early initialization code.\n * @details This initialization must be performed just after stack setup\n *          and before any other initialization.\n */\nvoid __early_init(void) {\n    // This is a dirty hack and should only be used as a temporary fix until this\n    // is upstreamed.\n    while (WDOG_TMROUTL < 2)\n        ;  // Must wait for WDOG timer if already running, before jumping\n\n    k20x_clock_init();\n}\n\n/**\n * @brief   Board-specific initialization code.\n * @todo    Add your board-specific code, if any.\n */\nvoid boardInit(void) {}\n\n\nvoid restart_usb_driver(USBDriver *usbp) {\n  // Do nothing. Restarting the USB driver on these boards breaks it.\n}\n"
  },
  {
    "path": "platforms/chibios/boards/IC_TEENSY_3_1/board/board.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2015 RedoX https://github.com/RedoXyde\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef _BOARD_H_\n#define _BOARD_H_\n\n/*\n * Setup for the PJRC Teensy 3.1 board.\n */\n\n/*\n * Board identifier.\n */\n#define BOARD_PJRC_TEENSY_3_1\n#define BOARD_NAME \"PJRC Teensy 3.1\"\n\n/* External 16 MHz crystal */\n#define KINETIS_XTAL_FREQUENCY 16000000UL\n\n/* Use internal capacitors for the crystal */\n#define KINETIS_BOARD_OSCILLATOR_SETTING OSC_CR_SC8P | OSC_CR_SC2P\n\n/*\n * MCU type\n */\n#define K20x7\n\n/*\n * IO pins assignments.\n */\n#define PORTA_PIN0 0\n#define PORTA_PIN1 1\n#define PORTA_PIN2 2\n#define PORTA_PIN3 3\n#define TEENSY_PIN33 4\n#define TEENSY_PIN24 5\n#define PORTA_PIN6 6\n#define PORTA_PIN7 7\n#define PORTA_PIN8 8\n#define PORTA_PIN9 9\n#define PORTA_PIN10 10\n#define PORTA_PIN11 11\n#define TEENSY_PIN3 12\n#define TEENSY_PIN4 13\n#define PORTA_PIN14 14\n#define PORTA_PIN15 15\n#define PORTA_PIN16 16\n#define PORTA_PIN17 17\n#define PORTA_PIN18 18\n#define PORTA_PIN19 19\n#define PORTA_PIN20 20\n#define PORTA_PIN21 21\n#define PORTA_PIN22 22\n#define PORTA_PIN23 23\n#define PORTA_PIN24 24\n#define PORTA_PIN25 25\n#define PORTA_PIN26 26\n#define PORTA_PIN27 27\n#define PORTA_PIN28 28\n#define PORTA_PIN29 29\n#define PORTA_PIN30 30\n#define PORTA_PIN31 31\n\n#define TEENSY_PIN3_IOPORT IOPORT1\n#define TEENSY_PIN4_IOPORT IOPORT1\n#define TEENSY_PIN24_IOPORT IOPORT1\n#define TEENSY_PIN33_IOPORT IOPORT1\n\n#define TEENSY_PIN16 0\n#define TEENSY_PIN17 1\n#define TEENSY_PIN19 2\n#define TEENSY_PIN18 3\n#define PORTB_PIN4 4\n#define PORTB_PIN5 5\n#define PORTB_PIN6 6\n#define PORTB_PIN7 7\n#define PORTB_PIN8 8\n#define PORTB_PIN9 9\n#define PORTB_PIN10 10\n#define PORTB_PIN11 11\n#define PORTB_PIN12 12\n#define PORTB_PIN13 13\n#define PORTB_PIN14 14\n#define PORTB_PIN15 15\n#define TEENSY_PIN0 16\n#define TEENSY_PIN1 17\n#define TEENSY_PIN32 18\n#define TEENSY_PIN25 19\n#define PORTB_PIN20 20\n#define PORTB_PIN21 21\n#define PORTB_PIN22 22\n#define PORTB_PIN23 23\n#define PORTB_PIN24 24\n#define PORTB_PIN25 25\n#define PORTB_PIN26 26\n#define PORTB_PIN27 27\n#define PORTB_PIN28 28\n#define PORTB_PIN29 29\n#define PORTB_PIN30 30\n#define PORTB_PIN31 31\n\n#define TEENSY_PIN0_IOPORT IOPORT2\n#define TEENSY_PIN1_IOPORT IOPORT2\n#define TEENSY_PIN16_IOPORT IOPORT2\n#define TEENSY_PIN17_IOPORT IOPORT2\n#define TEENSY_PIN18_IOPORT IOPORT2\n#define TEENSY_PIN19_IOPORT IOPORT2\n#define TEENSY_PIN25_IOPORT IOPORT2\n#define TEENSY_PIN32_IOPORT IOPORT2\n\n#define TEENSY_PIN15 0\n#define TEENSY_PIN22 1\n#define TEENSY_PIN23 2\n#define TEENSY_PIN9 3\n#define TEENSY_PIN10 4\n#define TEENSY_PIN13 5\n#define TEENSY_PIN11 6\n#define TEENSY_PIN12 7\n#define TEENSY_PIN28 8\n#define TEENSY_PIN27 9\n#define TEENSY_PIN29 10\n#define TEENSY_PIN30 11\n#define PORTC_PIN12 12\n#define PORTC_PIN13 13\n#define PORTC_PIN14 14\n#define PORTC_PIN15 15\n#define PORTC_PIN16 16\n#define PORTC_PIN17 17\n#define PORTC_PIN18 18\n#define PORTC_PIN19 19\n#define PORTC_PIN20 20\n#define PORTC_PIN21 21\n#define PORTC_PIN22 22\n#define PORTC_PIN23 23\n#define PORTC_PIN24 24\n#define PORTC_PIN25 25\n#define PORTC_PIN26 26\n#define PORTC_PIN27 27\n#define PORTC_PIN28 28\n#define PORTC_PIN29 29\n#define PORTC_PIN30 30\n#define PORTC_PIN31 31\n\n#define TEENSY_PIN9_IOPORT IOPORT3\n#define TEENSY_PIN10_IOPORT IOPORT3\n#define TEENSY_PIN11_IOPORT IOPORT3\n#define TEENSY_PIN12_IOPORT IOPORT3\n#define TEENSY_PIN13_IOPORT IOPORT3\n#define TEENSY_PIN15_IOPORT IOPORT3\n#define TEENSY_PIN22_IOPORT IOPORT3\n#define TEENSY_PIN23_IOPORT IOPORT3\n#define TEENSY_PIN27_IOPORT IOPORT3\n#define TEENSY_PIN28_IOPORT IOPORT3\n#define TEENSY_PIN29_IOPORT IOPORT3\n#define TEENSY_PIN30_IOPORT IOPORT3\n\n#define TEENSY_PIN2 0\n#define TEENSY_PIN14 1\n#define TEENSY_PIN7 2\n#define TEENSY_PIN8 3\n#define TEENSY_PIN6 4\n#define TEENSY_PIN20 5\n#define TEENSY_PIN21 6\n#define TEENSY_PIN5 7\n#define PORTD_PIN8 8\n#define PORTD_PIN9 9\n#define PORTD_PIN10 10\n#define PORTD_PIN11 11\n#define PORTD_PIN12 12\n#define PORTD_PIN13 13\n#define PORTD_PIN14 14\n#define PORTD_PIN15 15\n#define PORTD_PIN16 16\n#define PORTD_PIN17 17\n#define PORTD_PIN18 18\n#define PORTD_PIN19 19\n#define PORTD_PIN20 20\n#define PORTD_PIN21 21\n#define PORTD_PIN22 22\n#define PORTD_PIN23 23\n#define PORTD_PIN24 24\n#define PORTD_PIN25 25\n#define PORTD_PIN26 26\n#define PORTD_PIN27 27\n#define PORTD_PIN28 28\n#define PORTD_PIN29 29\n#define PORTD_PIN30 30\n#define PORTD_PIN31 31\n\n#define TEENSY_PIN2_IOPORT IOPORT4\n#define TEENSY_PIN5_IOPORT IOPORT4\n#define TEENSY_PIN6_IOPORT IOPORT4\n#define TEENSY_PIN7_IOPORT IOPORT4\n#define TEENSY_PIN8_IOPORT IOPORT4\n#define TEENSY_PIN14_IOPORT IOPORT4\n#define TEENSY_PIN20_IOPORT IOPORT4\n#define TEENSY_PIN21_IOPORT IOPORT4\n\n#define TEENSY_PIN31 0\n#define TEENSY_PIN26 1\n#define PORTE_PIN2 2\n#define PORTE_PIN3 3\n#define PORTE_PIN4 4\n#define PORTE_PIN5 5\n#define PORTE_PIN6 6\n#define PORTE_PIN7 7\n#define PORTE_PIN8 8\n#define PORTE_PIN9 9\n#define PORTE_PIN10 10\n#define PORTE_PIN11 11\n#define PORTE_PIN12 12\n#define PORTE_PIN13 13\n#define PORTE_PIN14 14\n#define PORTE_PIN15 15\n#define PORTE_PIN16 16\n#define PORTE_PIN17 17\n#define PORTE_PIN18 18\n#define PORTE_PIN19 19\n#define PORTE_PIN20 20\n#define PORTE_PIN21 21\n#define PORTE_PIN22 22\n#define PORTE_PIN23 23\n#define PORTE_PIN24 24\n#define PORTE_PIN25 25\n#define PORTE_PIN26 26\n#define PORTE_PIN27 27\n#define PORTE_PIN28 28\n#define PORTE_PIN29 29\n#define PORTE_PIN30 30\n#define PORTE_PIN31 31\n\n#define TEENSY_PIN26_IOPORT IOPORT5\n#define TEENSY_PIN31_IOPORT IOPORT5\n\n#define LINE_PIN1 PAL_LINE(TEENSY_PIN1_IOPORT, TEENSY_PIN1)\n#define LINE_PIN2 PAL_LINE(TEENSY_PIN2_IOPORT, TEENSY_PIN2)\n#define LINE_PIN3 PAL_LINE(TEENSY_PIN3_IOPORT, TEENSY_PIN3)\n#define LINE_PIN4 PAL_LINE(TEENSY_PIN4_IOPORT, TEENSY_PIN4)\n#define LINE_PIN5 PAL_LINE(TEENSY_PIN5_IOPORT, TEENSY_PIN5)\n#define LINE_PIN6 PAL_LINE(TEENSY_PIN6_IOPORT, TEENSY_PIN6)\n#define LINE_PIN7 PAL_LINE(TEENSY_PIN7_IOPORT, TEENSY_PIN7)\n#define LINE_PIN8 PAL_LINE(TEENSY_PIN8_IOPORT, TEENSY_PIN8)\n#define LINE_PIN9 PAL_LINE(TEENSY_PIN9_IOPORT, TEENSY_PIN9)\n#define LINE_PIN10 PAL_LINE(TEENSY_PIN10_IOPORT, TEENSY_PIN10)\n#define LINE_PIN11 PAL_LINE(TEENSY_PIN11_IOPORT, TEENSY_PIN11)\n#define LINE_PIN12 PAL_LINE(TEENSY_PIN12_IOPORT, TEENSY_PIN12)\n#define LINE_PIN13 PAL_LINE(TEENSY_PIN13_IOPORT, TEENSY_PIN13)\n#define LINE_PIN14 PAL_LINE(TEENSY_PIN14_IOPORT, TEENSY_PIN14)\n#define LINE_PIN15 PAL_LINE(TEENSY_PIN15_IOPORT, TEENSY_PIN15)\n#define LINE_PIN16 PAL_LINE(TEENSY_PIN16_IOPORT, TEENSY_PIN16)\n#define LINE_PIN17 PAL_LINE(TEENSY_PIN17_IOPORT, TEENSY_PIN17)\n#define LINE_PIN18 PAL_LINE(TEENSY_PIN18_IOPORT, TEENSY_PIN18)\n#define LINE_PIN19 PAL_LINE(TEENSY_PIN19_IOPORT, TEENSY_PIN19)\n#define LINE_PIN20 PAL_LINE(TEENSY_PIN20_IOPORT, TEENSY_PIN20)\n#define LINE_PIN21 PAL_LINE(TEENSY_PIN21_IOPORT, TEENSY_PIN21)\n#define LINE_PIN22 PAL_LINE(TEENSY_PIN22_IOPORT, TEENSY_PIN22)\n#define LINE_PIN23 PAL_LINE(TEENSY_PIN23_IOPORT, TEENSY_PIN23)\n#define LINE_PIN24 PAL_LINE(TEENSY_PIN24_IOPORT, TEENSY_PIN24)\n#define LINE_PIN25 PAL_LINE(TEENSY_PIN25_IOPORT, TEENSY_PIN25)\n#define LINE_PIN25 PAL_LINE(TEENSY_PIN25_IOPORT, TEENSY_PIN25)\n#define LINE_PIN26 PAL_LINE(TEENSY_PIN26_IOPORT, TEENSY_PIN26)\n#define LINE_PIN27 PAL_LINE(TEENSY_PIN27_IOPORT, TEENSY_PIN27)\n#define LINE_PIN28 PAL_LINE(TEENSY_PIN28_IOPORT, TEENSY_PIN28)\n#define LINE_PIN29 PAL_LINE(TEENSY_PIN29_IOPORT, TEENSY_PIN29)\n#define LINE_PIN30 PAL_LINE(TEENSY_PIN30_IOPORT, TEENSY_PIN30)\n#define LINE_PIN31 PAL_LINE(TEENSY_PIN31_IOPORT, TEENSY_PIN31)\n#define LINE_PIN32 PAL_LINE(TEENSY_PIN32_IOPORT, TEENSY_PIN32)\n#define LINE_PIN33 PAL_LINE(TEENSY_PIN33_IOPORT, TEENSY_PIN33)\n\n#define LINE_LED LINE_PIN13\n\n#if !defined(_FROM_ASM_)\n#    ifdef __cplusplus\nextern \"C\" {\n#    endif\nvoid boardInit(void);\n#    ifdef __cplusplus\n}\n#    endif\n#endif /* _FROM_ASM_ */\n\n#endif /* _BOARD_H_ */\n"
  },
  {
    "path": "platforms/chibios/boards/IC_TEENSY_3_1/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(BOARD_PATH)/board/board.c\n\n# Required include directories\nBOARDINC = $(BOARD_PATH)/board\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/IC_TEENSY_4_1/board/board.mk",
    "content": "include $(CHIBIOS_CONTRIB)/os/hal/boards/PJRC_TEENSY_4_1/board.mk\n"
  },
  {
    "path": "platforms/chibios/boards/IC_TEENSY_4_1/rules.mk",
    "content": "TEENSY_LOADER_CLI_MCU = imxrt1062\n"
  },
  {
    "path": "platforms/chibios/boards/PJRC_TEENSY_3_5/board/board.mk",
    "content": "include $(CHIBIOS_CONTRIB)/os/hal/boards/PJRC_TEENSY_3_5/board.mk\n\n# List of all the board related files.\nBOARDSRC += $(BOARD_PATH)/board/extra.c\n\n# Required include directories\nBOARDINC += $(BOARD_PATH)/board\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/PJRC_TEENSY_3_5/board/extra.c",
    "content": "#include <hal.h>\n\nvoid restart_usb_driver(USBDriver *usbp) {\n    // Do nothing. Restarting the USB driver on the Teensy 3.6 breaks it,\n    // resulting in a keyboard which can wake up a PC from Suspend-to-RAM, but\n    // does not actually produce any keypresses until you un-plug and re-plug.\n}\n"
  },
  {
    "path": "platforms/chibios/boards/PJRC_TEENSY_3_6/board/board.mk",
    "content": "include $(CHIBIOS_CONTRIB)/os/hal/boards/PJRC_TEENSY_3_6/board.mk\n\n# List of all the board related files.\nBOARDSRC += $(BOARD_PATH)/board/extra.c\n\n# Required include directories\nBOARDINC += $(BOARD_PATH)/board\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/PJRC_TEENSY_3_6/board/extra.c",
    "content": "#include <hal.h>\n\nvoid restart_usb_driver(USBDriver *usbp) {\n    // Do nothing. Restarting the USB driver on the Teensy 3.6 breaks it,\n    // resulting in a keyboard which can wake up a PC from Suspend-to-RAM, but\n    // does not actually produce any keypresses until you un-plug and re-plug.\n}\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_BLOK/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040\n\n# Shared variables\nALLCSRC += $(BOARDSRC) \nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_BLOK/configs/board.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include_next <board.h>\n\n#undef BOARD_RP_PICO_RP2040\n#define BOARD_PM2040\n\n#undef BOARD_NAME\n#define BOARD_NAME \"Blok\"\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_BLOK/configs/chconf.h",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#define CH_CFG_SMP_MODE                     TRUE\n#define CH_CFG_ST_RESOLUTION                32\n#define CH_CFG_ST_FREQUENCY                 1000000\n#define CH_CFG_INTERVALS_SIZE               32\n#define CH_CFG_TIME_TYPES_SIZE              32\n#define CH_CFG_ST_TIMEDELTA                 20\n\n#include_next <chconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_BLOK/configs/config.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n/**======================\n **    I2C Driver\n *========================**/\n\n#ifndef I2C_DRIVER\n#    define I2C_DRIVER I2CD0\n#endif\n#ifndef I2C1_SDA_PIN\n#    define I2C1_SDA_PIN D1\n#endif\n#ifndef I2C1_SCL_PIN\n#    define I2C1_SCL_PIN D0\n#endif\n\n/**======================\n **      UART Driver\n *========================**/\n\n#ifndef UART_DRIVER\n#    define UART_DRIVER SIOD0\n#endif\n\n#ifndef UART_TX_PIN\n#    define UART_TX_PIN D3\n#endif\n\n#ifndef UART_RX_PIN\n#    define UART_RX_PIN D2\n#endif\n\n/**======================\n **    Double-tap\n *========================**/\n\n#ifndef RP2040_BOOTLOADER_DOUBLE_TAP_RESET\n#    define RP2040_BOOTLOADER_DOUBLE_TAP_RESET\n#endif\n#ifndef RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT\n#    define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 500U\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_BLOK/configs/halconf.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#define HAL_USE_ADC TRUE\n#define HAL_USE_I2C TRUE\n#define HAL_USE_SPI TRUE\n\n#include_next <halconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_BLOK/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2021 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * RP2040_MCUCONF drivers configuration.\n *\n * IRQ priorities:\n * 3...0        Lowest...Highest.\n *\n * DMA priorities:\n * 0...1        Lowest...Highest.\n */\n\n#define RP2040_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define RP_NO_INIT                          FALSE\n#define RP_CORE1_START                      FALSE\n#define RP_CORE1_VECTORS_TABLE              _vectors\n#define RP_CORE1_ENTRY_POINT                _crt0_c1_entry\n#define RP_CORE1_STACK_END                  __c1_main_stack_end__\n\n/*\n * IRQ system settings.\n */\n#define RP_IRQ_SYSTICK_PRIORITY             2\n#define RP_IRQ_TIMER_ALARM0_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM1_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM2_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM3_PRIORITY        2\n#define RP_IRQ_ADC1_PRIORITY                3\n#define RP_IRQ_UART0_PRIORITY               3\n#define RP_IRQ_UART1_PRIORITY               3\n#define RP_IRQ_SPI0_PRIORITY                2\n#define RP_IRQ_SPI1_PRIORITY                2\n#define RP_IRQ_USB0_PRIORITY                3\n#define RP_IRQ_I2C0_PRIORITY                2\n#define RP_IRQ_I2C1_PRIORITY                2\n#define RP_IRQ_RTC_PRIORITY                 3\n\n/*\n * ADC driver system settings.\n */\n#define RP_ADC_USE_ADC1                     TRUE\n\n/*\n * SIO driver system settings.\n */\n#define RP_SIO_USE_UART0                    FALSE\n#define RP_SIO_USE_UART1                    FALSE\n\n/*\n * SPI driver system settings.\n */\n#define RP_SPI_USE_SPI0                     TRUE\n#define RP_SPI_USE_SPI1                     FALSE\n#define RP_SPI_SPI0_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI0_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI1_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI1_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI0_DMA_PRIORITY            1\n#define RP_SPI_SPI1_DMA_PRIORITY            1\n#define RP_SPI_DMA_ERROR_HOOK(spip)\n\n/*\n * PWM driver system settings.\n */\n#define RP_PWM_USE_PWM0                     FALSE\n#define RP_PWM_USE_PWM1                     FALSE\n#define RP_PWM_USE_PWM2                     FALSE\n#define RP_PWM_USE_PWM3                     FALSE\n#define RP_PWM_USE_PWM4                     FALSE\n#define RP_PWM_USE_PWM5                     FALSE\n#define RP_PWM_USE_PWM6                     FALSE\n#define RP_PWM_USE_PWM7                     FALSE\n#define RP_PWM_IRQ_WRAP_NUMBER_PRIORITY     3\n\n/*\n * I2C driver system settings.\n */\n#define RP_I2C_USE_I2C0                     TRUE\n#define RP_I2C_USE_I2C1                     FALSE\n#define RP_I2C_BUSY_TIMEOUT                 50\n#define RP_I2C_ADDRESS_MODE_10BIT           FALSE\n\n/*\n * USB driver system settings.\n */\n#define RP_USB_USE_USBD0                    TRUE\n#define RP_USB_FORCE_VBUS_DETECT            TRUE\n#define RP_USE_EXTERNAL_VBUS_DETECT         FALSE\n#define RP_USB_USE_ERROR_DATA_SEQ_INTR      FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PM2040/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/RP_PICO_RP2040\n\n# Shared variables\nALLCSRC += $(BOARDSRC) \nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PM2040/configs/board.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include_next <board.h>\n\n#undef BOARD_RP_PICO_RP2040\n#define BOARD_PM2040\n\n#undef BOARD_NAME\n#define BOARD_NAME \"Pro Micro RP2040\"\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PM2040/configs/chconf.h",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#define CH_CFG_SMP_MODE                     TRUE\n#define CH_CFG_ST_RESOLUTION                32\n#define CH_CFG_ST_FREQUENCY                 1000000\n#define CH_CFG_INTERVALS_SIZE               32\n#define CH_CFG_TIME_TYPES_SIZE              32\n#define CH_CFG_ST_TIMEDELTA                 20\n\n#include_next <chconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PM2040/configs/config.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n/**======================\n **    I2C Driver\n *========================**/\n\n#ifndef I2C_DRIVER\n#    define I2C_DRIVER I2CD1\n#endif\n#ifndef I2C1_SDA_PIN\n#    define I2C1_SDA_PIN D1\n#endif\n#ifndef I2C1_SCL_PIN\n#    define I2C1_SCL_PIN D0\n#endif\n\n/**======================\n **      UART Driver\n *========================**/\n\n#ifndef UART_DRIVER\n#    define UART_DRIVER SIOD0\n#endif\n\n#ifndef UART_TX_PIN\n#    define UART_TX_PIN D3\n#endif\n\n#ifndef UART_RX_PIN\n#    define UART_RX_PIN D2\n#endif\n\n/**======================\n **    Double-tap\n *========================**/\n\n#ifndef RP2040_BOOTLOADER_DOUBLE_TAP_RESET\n#    define RP2040_BOOTLOADER_DOUBLE_TAP_RESET\n#endif\n#ifndef RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT\n#    define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 500U\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PM2040/configs/halconf.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#define HAL_USE_ADC TRUE\n#define HAL_USE_I2C TRUE\n#define HAL_USE_SPI TRUE\n\n#include_next <halconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PM2040/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2021 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * RP2040_MCUCONF drivers configuration.\n *\n * IRQ priorities:\n * 3...0        Lowest...Highest.\n *\n * DMA priorities:\n * 0...1        Lowest...Highest.\n */\n\n#define RP2040_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define RP_NO_INIT                          FALSE\n#define RP_CORE1_START                      FALSE\n#define RP_CORE1_VECTORS_TABLE              _vectors\n#define RP_CORE1_ENTRY_POINT                _crt0_c1_entry\n#define RP_CORE1_STACK_END                  __c1_main_stack_end__\n\n/*\n * IRQ system settings.\n */\n#define RP_IRQ_SYSTICK_PRIORITY             2\n#define RP_IRQ_TIMER_ALARM0_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM1_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM2_PRIORITY        2\n#define RP_IRQ_TIMER_ALARM3_PRIORITY        2\n#define RP_IRQ_ADC1_PRIORITY                3\n#define RP_IRQ_UART0_PRIORITY               3\n#define RP_IRQ_UART1_PRIORITY               3\n#define RP_IRQ_SPI0_PRIORITY                2\n#define RP_IRQ_SPI1_PRIORITY                2\n#define RP_IRQ_USB0_PRIORITY                3\n#define RP_IRQ_I2C0_PRIORITY                2\n#define RP_IRQ_I2C1_PRIORITY                2\n#define RP_IRQ_RTC_PRIORITY                 3\n\n/*\n * ADC driver system settings.\n */\n#define RP_ADC_USE_ADC1                     TRUE\n\n/*\n * SIO driver system settings.\n */\n#define RP_SIO_USE_UART0                    FALSE\n#define RP_SIO_USE_UART1                    FALSE\n\n/*\n * SPI driver system settings.\n */\n#define RP_SPI_USE_SPI0                     TRUE\n#define RP_SPI_USE_SPI1                     FALSE\n#define RP_SPI_SPI0_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI0_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI1_RX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI1_TX_DMA_CHANNEL          RP_DMA_CHANNEL_ID_ANY\n#define RP_SPI_SPI0_DMA_PRIORITY            1\n#define RP_SPI_SPI1_DMA_PRIORITY            1\n#define RP_SPI_DMA_ERROR_HOOK(spip)\n\n/*\n * PWM driver system settings.\n */\n#define RP_PWM_USE_PWM0                     FALSE\n#define RP_PWM_USE_PWM1                     FALSE\n#define RP_PWM_USE_PWM2                     FALSE\n#define RP_PWM_USE_PWM3                     FALSE\n#define RP_PWM_USE_PWM4                     FALSE\n#define RP_PWM_USE_PWM5                     FALSE\n#define RP_PWM_USE_PWM6                     FALSE\n#define RP_PWM_USE_PWM7                     FALSE\n#define RP_PWM_IRQ_WRAP_NUMBER_PRIORITY     3\n\n/*\n * I2C driver system settings.\n */\n#define RP_I2C_USE_I2C0                     FALSE\n#define RP_I2C_USE_I2C1                     TRUE\n#define RP_I2C_BUSY_TIMEOUT                 50\n#define RP_I2C_ADDRESS_MODE_10BIT           FALSE\n\n/*\n * USB driver system settings.\n */\n#define RP_USB_USE_USBD0                    TRUE\n#define RP_USB_FORCE_VBUS_DETECT            TRUE\n#define RP_USE_EXTERNAL_VBUS_DETECT         FALSE\n#define RP_USB_USE_ERROR_DATA_SEQ_INTR      FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PROTON_C/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_STM32F3_DISCOVERY/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_STM32F3_DISCOVERY\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PROTON_C/configs/board.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include_next <board.h>\n\n#undef STM32_HSE_BYPASS\n\n/*\n * USB bus activation macro, required by the USB driver.\n */\n#define usb_lld_connect_bus(usbp)                                   \\\n    do {                                                            \\\n        palSetPadMode(GPIOA, GPIOA_USB_DP, PAL_MODE_ALTERNATE(14)); \\\n    } while (0)\n\n/*\n * USB bus de-activation macro, required by the USB driver.\n */\n#define usb_lld_disconnect_bus(usbp)                                  \\\n    do {                                                              \\\n        palSetPadMode(GPIOA, GPIOA_USB_DP, PAL_MODE_OUTPUT_PUSHPULL); \\\n        palClearPad(GPIOA, GPIOA_USB_DP);                             \\\n    } while (0)\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PROTON_C/configs/chconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/**\n * @file    rt/templates/chconf.h\n * @brief   Configuration file template.\n * @details A copy of this file must be placed in each project directory, it\n *          contains the application specific kernel settings.\n *\n * @addtogroup config\n * @details Kernel related settings and hooks.\n * @{\n */\n\n#ifndef CHCONF_H\n#define CHCONF_H\n\n#define _CHIBIOS_RT_CONF_\n#define _CHIBIOS_RT_CONF_VER_7_0_\n\n/*===========================================================================*/\n/**\n * @name System settings\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Handling of instances.\n * @note    If enabled then threads assigned to various instances can\n *          interact each other using the same synchronization objects.\n *          If disabled then each OS instance is a separate world, no\n *          direct interactions are handled by the OS.\n */\n#if !defined(CH_CFG_SMP_MODE)\n#define CH_CFG_SMP_MODE                     FALSE\n#endif\n\n/**\n * @brief   Kernel hardening level.\n * @details This option is the level of functional-safety checks enabled\n *          in the kerkel. The meaning is:\n *          - 0: No checks, maximum performance.\n *          - 1: Reasonable checks.\n *          - 2: All checks.\n *          .\n */\n#if !defined(CH_CFG_HARDENING_LEVEL)\n#define CH_CFG_HARDENING_LEVEL              0\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name System timers settings\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   System time counter resolution.\n * @note    Allowed values are 16, 32 or 64 bits.\n */\n#if !defined(CH_CFG_ST_RESOLUTION)\n#define CH_CFG_ST_RESOLUTION                32\n#endif\n\n/**\n * @brief   System tick frequency.\n * @details Frequency of the system timer that drives the system ticks. This\n *          setting also defines the system tick time unit.\n */\n#if !defined(CH_CFG_ST_FREQUENCY)\n#define CH_CFG_ST_FREQUENCY                 100000\n#endif\n\n/**\n * @brief   Time intervals data size.\n * @note    Allowed values are 16, 32 or 64 bits.\n */\n#if !defined(CH_CFG_INTERVALS_SIZE)\n#define CH_CFG_INTERVALS_SIZE               32\n#endif\n\n/**\n * @brief   Time types data size.\n * @note    Allowed values are 16 or 32 bits.\n */\n#if !defined(CH_CFG_TIME_TYPES_SIZE)\n#define CH_CFG_TIME_TYPES_SIZE              32\n#endif\n\n/**\n * @brief   Time delta constant for the tick-less mode.\n * @note    If this value is zero then the system uses the classic\n *          periodic tick. This value represents the minimum number\n *          of ticks that is safe to specify in a timeout directive.\n *          The value one is not valid, timeouts are rounded up to\n *          this value.\n */\n#if !defined(CH_CFG_ST_TIMEDELTA)\n#define CH_CFG_ST_TIMEDELTA                 2\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Kernel parameters and options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Round robin interval.\n * @details This constant is the number of system ticks allowed for the\n *          threads before preemption occurs. Setting this value to zero\n *          disables the preemption for threads with equal priority and the\n *          round robin becomes cooperative. Note that higher priority\n *          threads can still preempt, the kernel is always preemptive.\n * @note    Disabling the round robin preemption makes the kernel more compact\n *          and generally faster.\n * @note    The round robin preemption is not supported in tickless mode and\n *          must be set to zero in that case.\n */\n#if !defined(CH_CFG_TIME_QUANTUM)\n#define CH_CFG_TIME_QUANTUM                 0\n#endif\n\n/**\n * @brief   Idle thread automatic spawn suppression.\n * @details When this option is activated the function @p chSysInit()\n *          does not spawn the idle thread. The application @p main()\n *          function becomes the idle thread and must implement an\n *          infinite loop.\n */\n#if !defined(CH_CFG_NO_IDLE_THREAD)\n#define CH_CFG_NO_IDLE_THREAD               FALSE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Performance options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   OS optimization.\n * @details If enabled then time efficient rather than space efficient code\n *          is used when two possible implementations exist.\n *\n * @note    This is not related to the compiler optimization options.\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_OPTIMIZE_SPEED)\n#define CH_CFG_OPTIMIZE_SPEED               TRUE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Subsystem options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Time Measurement APIs.\n * @details If enabled then the time measurement APIs are included in\n *          the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_TM)\n#define CH_CFG_USE_TM                       TRUE\n#endif\n\n/**\n * @brief   Time Stamps APIs.\n * @details If enabled then the time stamps APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_TIMESTAMP)\n#define CH_CFG_USE_TIMESTAMP                TRUE\n#endif\n\n/**\n * @brief   Threads registry APIs.\n * @details If enabled then the registry APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_REGISTRY)\n#define CH_CFG_USE_REGISTRY                 TRUE\n#endif\n\n/**\n * @brief   Threads synchronization APIs.\n * @details If enabled then the @p chThdWait() function is included in\n *          the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_WAITEXIT)\n#define CH_CFG_USE_WAITEXIT                 TRUE\n#endif\n\n/**\n * @brief   Semaphores APIs.\n * @details If enabled then the Semaphores APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_SEMAPHORES)\n#define CH_CFG_USE_SEMAPHORES               TRUE\n#endif\n\n/**\n * @brief   Semaphores queuing mode.\n * @details If enabled then the threads are enqueued on semaphores by\n *          priority rather than in FIFO order.\n *\n * @note    The default is @p FALSE. Enable this if you have special\n *          requirements.\n * @note    Requires @p CH_CFG_USE_SEMAPHORES.\n */\n#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)\n#define CH_CFG_USE_SEMAPHORES_PRIORITY      FALSE\n#endif\n\n/**\n * @brief   Mutexes APIs.\n * @details If enabled then the mutexes APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_MUTEXES)\n#define CH_CFG_USE_MUTEXES                  TRUE\n#endif\n\n/**\n * @brief   Enables recursive behavior on mutexes.\n * @note    Recursive mutexes are heavier and have an increased\n *          memory footprint.\n *\n * @note    The default is @p FALSE.\n * @note    Requires @p CH_CFG_USE_MUTEXES.\n */\n#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)\n#define CH_CFG_USE_MUTEXES_RECURSIVE        FALSE\n#endif\n\n/**\n * @brief   Conditional Variables APIs.\n * @details If enabled then the conditional variables APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_MUTEXES.\n */\n#if !defined(CH_CFG_USE_CONDVARS)\n#define CH_CFG_USE_CONDVARS                 TRUE\n#endif\n\n/**\n * @brief   Conditional Variables APIs with timeout.\n * @details If enabled then the conditional variables APIs with timeout\n *          specification are included in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_CONDVARS.\n */\n#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)\n#define CH_CFG_USE_CONDVARS_TIMEOUT         TRUE\n#endif\n\n/**\n * @brief   Events Flags APIs.\n * @details If enabled then the event flags APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_EVENTS)\n#define CH_CFG_USE_EVENTS                   TRUE\n#endif\n\n/**\n * @brief   Events Flags APIs with timeout.\n * @details If enabled then the events APIs with timeout specification\n *          are included in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_EVENTS.\n */\n#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)\n#define CH_CFG_USE_EVENTS_TIMEOUT           TRUE\n#endif\n\n/**\n * @brief   Synchronous Messages APIs.\n * @details If enabled then the synchronous messages APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_MESSAGES)\n#define CH_CFG_USE_MESSAGES                 TRUE\n#endif\n\n/**\n * @brief   Synchronous Messages queuing mode.\n * @details If enabled then messages are served by priority rather than in\n *          FIFO order.\n *\n * @note    The default is @p FALSE. Enable this if you have special\n *          requirements.\n * @note    Requires @p CH_CFG_USE_MESSAGES.\n */\n#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)\n#define CH_CFG_USE_MESSAGES_PRIORITY        TRUE\n#endif\n\n/**\n * @brief   Dynamic Threads APIs.\n * @details If enabled then the dynamic threads creation APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_WAITEXIT.\n * @note    Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.\n */\n#if !defined(CH_CFG_USE_DYNAMIC)\n#define CH_CFG_USE_DYNAMIC                  TRUE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name OSLIB options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Mailboxes APIs.\n * @details If enabled then the asynchronous messages (mailboxes) APIs are\n *          included in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_SEMAPHORES.\n */\n#if !defined(CH_CFG_USE_MAILBOXES)\n#define CH_CFG_USE_MAILBOXES                TRUE\n#endif\n\n/**\n * @brief   Memory checks APIs.\n * @details If enabled then the memory checks APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_MEMCHECKS)\n#define CH_CFG_USE_MEMCHECKS                TRUE\n#endif\n\n/**\n * @brief   Core Memory Manager APIs.\n * @details If enabled then the core memory manager APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_MEMCORE)\n#define CH_CFG_USE_MEMCORE                  TRUE\n#endif\n\n/**\n * @brief   Managed RAM size.\n * @details Size of the RAM area to be managed by the OS. If set to zero\n *          then the whole available RAM is used. The core memory is made\n *          available to the heap allocator and/or can be used directly through\n *          the simplified core memory allocator.\n *\n * @note    In order to let the OS manage the whole RAM the linker script must\n *          provide the @p __heap_base__ and @p __heap_end__ symbols.\n * @note    Requires @p CH_CFG_USE_MEMCORE.\n */\n#if !defined(CH_CFG_MEMCORE_SIZE)\n#define CH_CFG_MEMCORE_SIZE                 0\n#endif\n\n/**\n * @brief   Heap Allocator APIs.\n * @details If enabled then the memory heap allocator APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or\n *          @p CH_CFG_USE_SEMAPHORES.\n * @note    Mutexes are recommended.\n */\n#if !defined(CH_CFG_USE_HEAP)\n#define CH_CFG_USE_HEAP                     TRUE\n#endif\n\n/**\n * @brief   Memory Pools Allocator APIs.\n * @details If enabled then the memory pools allocator APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_MEMPOOLS)\n#define CH_CFG_USE_MEMPOOLS                 TRUE\n#endif\n\n/**\n * @brief   Objects FIFOs APIs.\n * @details If enabled then the objects FIFOs APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_OBJ_FIFOS)\n#define CH_CFG_USE_OBJ_FIFOS                TRUE\n#endif\n\n/**\n * @brief   Pipes APIs.\n * @details If enabled then the pipes APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_PIPES)\n#define CH_CFG_USE_PIPES                    TRUE\n#endif\n\n/**\n * @brief   Objects Caches APIs.\n * @details If enabled then the objects caches APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_OBJ_CACHES)\n#define CH_CFG_USE_OBJ_CACHES               FALSE\n#endif\n\n/**\n * @brief   Delegate threads APIs.\n * @details If enabled then the delegate threads APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_DELEGATES)\n#define CH_CFG_USE_DELEGATES                FALSE\n#endif\n\n/**\n * @brief   Jobs Queues APIs.\n * @details If enabled then the jobs queues APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_JOBS)\n#define CH_CFG_USE_JOBS                     FALSE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Objects factory options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Objects Factory APIs.\n * @details If enabled then the objects factory APIs are included in the\n *          kernel.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_CFG_USE_FACTORY)\n#define CH_CFG_USE_FACTORY                  FALSE\n#endif\n\n/**\n * @brief   Maximum length for object names.\n * @details If the specified length is zero then the name is stored by\n *          pointer but this could have unintended side effects.\n */\n#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)\n#define CH_CFG_FACTORY_MAX_NAMES_LENGTH     8\n#endif\n\n/**\n * @brief   Enables the registry of generic objects.\n */\n#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)\n#define CH_CFG_FACTORY_OBJECTS_REGISTRY     TRUE\n#endif\n\n/**\n * @brief   Enables factory for generic buffers.\n */\n#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)\n#define CH_CFG_FACTORY_GENERIC_BUFFERS      TRUE\n#endif\n\n/**\n * @brief   Enables factory for semaphores.\n */\n#if !defined(CH_CFG_FACTORY_SEMAPHORES)\n#define CH_CFG_FACTORY_SEMAPHORES           TRUE\n#endif\n\n/**\n * @brief   Enables factory for mailboxes.\n */\n#if !defined(CH_CFG_FACTORY_MAILBOXES)\n#define CH_CFG_FACTORY_MAILBOXES            TRUE\n#endif\n\n/**\n * @brief   Enables factory for objects FIFOs.\n */\n#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)\n#define CH_CFG_FACTORY_OBJ_FIFOS            TRUE\n#endif\n\n/**\n * @brief   Enables factory for Pipes.\n */\n#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)\n#define CH_CFG_FACTORY_PIPES                TRUE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Debug options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Debug option, kernel statistics.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_DBG_STATISTICS)\n#define CH_DBG_STATISTICS                   FALSE\n#endif\n\n/**\n * @brief   Debug option, system state check.\n * @details If enabled the correct call protocol for system APIs is checked\n *          at runtime.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_DBG_SYSTEM_STATE_CHECK)\n#define CH_DBG_SYSTEM_STATE_CHECK           FALSE\n#endif\n\n/**\n * @brief   Debug option, parameters checks.\n * @details If enabled then the checks on the API functions input\n *          parameters are activated.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_DBG_ENABLE_CHECKS)\n#define CH_DBG_ENABLE_CHECKS                FALSE\n#endif\n\n/**\n * @brief   Debug option, consistency checks.\n * @details If enabled then all the assertions in the kernel code are\n *          activated. This includes consistency checks inside the kernel,\n *          runtime anomalies and port-defined checks.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_DBG_ENABLE_ASSERTS)\n#define CH_DBG_ENABLE_ASSERTS               FALSE\n#endif\n\n/**\n * @brief   Debug option, trace buffer.\n * @details If enabled then the trace buffer is activated.\n *\n * @note    The default is @p CH_DBG_TRACE_MASK_DISABLED.\n */\n#if !defined(CH_DBG_TRACE_MASK)\n#define CH_DBG_TRACE_MASK                   CH_DBG_TRACE_MASK_DISABLED\n#endif\n\n/**\n * @brief   Trace buffer entries.\n * @note    The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is\n *          different from @p CH_DBG_TRACE_MASK_DISABLED.\n */\n#if !defined(CH_DBG_TRACE_BUFFER_SIZE)\n#define CH_DBG_TRACE_BUFFER_SIZE            128\n#endif\n\n/**\n * @brief   Debug option, stack checks.\n * @details If enabled then a runtime stack check is performed.\n *\n * @note    The default is @p FALSE.\n * @note    The stack check is performed in a architecture/port dependent way.\n *          It may not be implemented or some ports.\n * @note    The default failure mode is to halt the system with the global\n *          @p panic_msg variable set to @p NULL.\n */\n#if !defined(CH_DBG_ENABLE_STACK_CHECK)\n#define CH_DBG_ENABLE_STACK_CHECK           TRUE\n#endif\n\n/**\n * @brief   Debug option, stacks initialization.\n * @details If enabled then the threads working area is filled with a byte\n *          value when a thread is created. This can be useful for the\n *          runtime measurement of the used stack.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_DBG_FILL_THREADS)\n#define CH_DBG_FILL_THREADS                 FALSE\n#endif\n\n/**\n * @brief   Debug option, threads profiling.\n * @details If enabled then a field is added to the @p thread_t structure that\n *          counts the system ticks occurred while executing the thread.\n *\n * @note    The default is @p FALSE.\n * @note    This debug option is not currently compatible with the\n *          tickless mode.\n */\n#if !defined(CH_DBG_THREADS_PROFILING)\n#define CH_DBG_THREADS_PROFILING            FALSE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Kernel hooks\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   System structure extension.\n * @details User fields added to the end of the @p ch_system_t structure.\n */\n#define CH_CFG_SYSTEM_EXTRA_FIELDS                                          \\\n  /* Add system custom fields here.*/\n\n/**\n * @brief   System initialization hook.\n * @details User initialization code added to the @p chSysInit() function\n *          just before interrupts are enabled globally.\n */\n#define CH_CFG_SYSTEM_INIT_HOOK() {                                         \\\n  /* Add system initialization code here.*/                                 \\\n}\n\n/**\n * @brief   OS instance structure extension.\n * @details User fields added to the end of the @p os_instance_t structure.\n */\n#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS                                     \\\n  /* Add OS instance custom fields here.*/\n\n/**\n * @brief   OS instance initialization hook.\n *\n * @param[in] oip       pointer to the @p os_instance_t structure\n */\n#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) {                                 \\\n  /* Add OS instance initialization code here.*/                            \\\n}\n\n/**\n * @brief   Threads descriptor structure extension.\n * @details User fields added to the end of the @p thread_t structure.\n */\n#define CH_CFG_THREAD_EXTRA_FIELDS                                          \\\n  /* Add threads custom fields here.*/\n\n/**\n * @brief   Threads initialization hook.\n * @details User initialization code added to the @p _thread_init() function.\n *\n * @note    It is invoked from within @p _thread_init() and implicitly from all\n *          the threads creation APIs.\n *\n * @param[in] tp        pointer to the @p thread_t structure\n */\n#define CH_CFG_THREAD_INIT_HOOK(tp) {                                       \\\n  /* Add threads initialization code here.*/                                \\\n}\n\n/**\n * @brief   Threads finalization hook.\n * @details User finalization code added to the @p chThdExit() API.\n *\n * @param[in] tp        pointer to the @p thread_t structure\n */\n#define CH_CFG_THREAD_EXIT_HOOK(tp) {                                       \\\n  /* Add threads finalization code here.*/                                  \\\n}\n\n/**\n * @brief   Context switch hook.\n * @details This hook is invoked just before switching between threads.\n *\n * @param[in] ntp       thread being switched in\n * @param[in] otp       thread being switched out\n */\n#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) {                              \\\n  /* Context switch code here.*/                                            \\\n}\n\n/**\n * @brief   ISR enter hook.\n */\n#define CH_CFG_IRQ_PROLOGUE_HOOK() {                                        \\\n  /* IRQ prologue code here.*/                                              \\\n}\n\n/**\n * @brief   ISR exit hook.\n */\n#define CH_CFG_IRQ_EPILOGUE_HOOK() {                                        \\\n  /* IRQ epilogue code here.*/                                              \\\n}\n\n/**\n * @brief   Idle thread enter hook.\n * @note    This hook is invoked within a critical zone, no OS functions\n *          should be invoked from here.\n * @note    This macro can be used to activate a power saving mode.\n */\n#define CH_CFG_IDLE_ENTER_HOOK() {                                          \\\n  /* Idle-enter code here.*/                                                \\\n}\n\n/**\n * @brief   Idle thread leave hook.\n * @note    This hook is invoked within a critical zone, no OS functions\n *          should be invoked from here.\n * @note    This macro can be used to deactivate a power saving mode.\n */\n#define CH_CFG_IDLE_LEAVE_HOOK() {                                          \\\n  /* Idle-leave code here.*/                                                \\\n}\n\n/**\n * @brief   Idle Loop hook.\n * @details This hook is continuously invoked by the idle thread loop.\n */\n#define CH_CFG_IDLE_LOOP_HOOK() {                                           \\\n  /* Idle loop code here.*/                                                 \\\n}\n\n/**\n * @brief   System tick event hook.\n * @details This hook is invoked in the system tick handler immediately\n *          after processing the virtual timers queue.\n */\n#define CH_CFG_SYSTEM_TICK_HOOK() {                                         \\\n  /* System tick event code here.*/                                         \\\n}\n\n/**\n * @brief   System halt hook.\n * @details This hook is invoked in case to a system halting error before\n *          the system is halted.\n */\n#define CH_CFG_SYSTEM_HALT_HOOK(reason) {                                   \\\n  /* System halt code here.*/                                               \\\n}\n\n/**\n * @brief   Trace hook.\n * @details This hook is invoked each time a new record is written in the\n *          trace buffer.\n */\n#define CH_CFG_TRACE_HOOK(tep) {                                            \\\n  /* Trace code here.*/                                                     \\\n}\n\n/**\n * @brief   Runtime Faults Collection Unit hook.\n * @details This hook is invoked each time new faults are collected and stored.\n */\n#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) {                                  \\\n  /* Faults handling code here.*/                                           \\\n}\n\n/** @} */\n\n/*===========================================================================*/\n/* Port-specific settings (override port settings defaulted in chcore.h).    */\n/*===========================================================================*/\n\n#endif  /* CHCONF_H */\n\n/** @} */\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PROTON_C/configs/config.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n#ifdef CONVERT_TO_PROTON_C\n#    ifndef I2C1_SDA_PIN\n#        define I2C1_SDA_PIN D1\n#    endif\n#    ifndef I2C1_SCL_PIN\n#        define I2C1_SCL_PIN D0\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PROTON_C/configs/halconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/**\n * @file    templates/halconf.h\n * @brief   HAL configuration header.\n * @details HAL configuration file, this file allows to enable or disable the\n *          various device drivers from your application. You may also use\n *          this file in order to override the device drivers default settings.\n *\n * @addtogroup HAL_CONF\n * @{\n */\n\n#ifndef HALCONF_H\n#define HALCONF_H\n\n#define _CHIBIOS_HAL_CONF_\n#define _CHIBIOS_HAL_CONF_VER_8_4_\n\n#include <mcuconf.h>\n\n/**\n * @brief   Enables the PAL subsystem.\n */\n#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)\n#define HAL_USE_PAL                         TRUE\n#endif\n\n/**\n * @brief   Enables the ADC subsystem.\n */\n#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)\n#define HAL_USE_ADC                         FALSE\n#endif\n\n/**\n * @brief   Enables the CAN subsystem.\n */\n#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)\n#define HAL_USE_CAN                         FALSE\n#endif\n\n/**\n * @brief   Enables the cryptographic subsystem.\n */\n#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)\n#define HAL_USE_CRY                         FALSE\n#endif\n\n/**\n * @brief   Enables the DAC subsystem.\n */\n#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)\n#define HAL_USE_DAC                         TRUE\n#endif\n\n/**\n * @brief   Enables the EFlash subsystem.\n */\n#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)\n#define HAL_USE_EFL                         FALSE\n#endif\n\n/**\n * @brief   Enables the GPT subsystem.\n */\n#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)\n#define HAL_USE_GPT                         TRUE\n#endif\n\n/**\n * @brief   Enables the I2C subsystem.\n */\n#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)\n#define HAL_USE_I2C                         TRUE\n#endif\n\n/**\n * @brief   Enables the I2S subsystem.\n */\n#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)\n#define HAL_USE_I2S                         FALSE\n#endif\n\n/**\n * @brief   Enables the ICU subsystem.\n */\n#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)\n#define HAL_USE_ICU                         FALSE\n#endif\n\n/**\n * @brief   Enables the MAC subsystem.\n */\n#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)\n#define HAL_USE_MAC                         FALSE\n#endif\n\n/**\n * @brief   Enables the MMC_SPI subsystem.\n */\n#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)\n#define HAL_USE_MMC_SPI                     FALSE\n#endif\n\n/**\n * @brief   Enables the PWM subsystem.\n */\n#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)\n#define HAL_USE_PWM                         TRUE\n#endif\n\n/**\n * @brief   Enables the RTC subsystem.\n */\n#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)\n#define HAL_USE_RTC                         FALSE\n#endif\n\n/**\n * @brief   Enables the SDC subsystem.\n */\n#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)\n#define HAL_USE_SDC                         FALSE\n#endif\n\n/**\n * @brief   Enables the SERIAL subsystem.\n */\n#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)\n#define HAL_USE_SERIAL                      FALSE\n#endif\n\n/**\n * @brief   Enables the SERIAL over USB subsystem.\n */\n#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)\n#define HAL_USE_SERIAL_USB                  TRUE\n#endif\n\n/**\n * @brief   Enables the SIO subsystem.\n */\n#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)\n#define HAL_USE_SIO                         FALSE\n#endif\n\n/**\n * @brief   Enables the SPI subsystem.\n */\n#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)\n#define HAL_USE_SPI                         TRUE\n#endif\n\n/**\n * @brief   Enables the TRNG subsystem.\n */\n#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)\n#define HAL_USE_TRNG                        FALSE\n#endif\n\n/**\n * @brief   Enables the UART subsystem.\n */\n#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)\n#define HAL_USE_UART                        FALSE\n#endif\n\n/**\n * @brief   Enables the USB subsystem.\n */\n#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)\n#define HAL_USE_USB                         TRUE\n#endif\n\n/**\n * @brief   Enables the WDG subsystem.\n */\n#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)\n#define HAL_USE_WDG                         FALSE\n#endif\n\n/**\n * @brief   Enables the WSPI subsystem.\n */\n#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)\n#define HAL_USE_WSPI                        FALSE\n#endif\n\n/*===========================================================================*/\n/* PAL driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)\n#define PAL_USE_CALLBACKS                   TRUE\n#endif\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)\n#define PAL_USE_WAIT                        TRUE\n#endif\n\n/*===========================================================================*/\n/* ADC driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)\n#define ADC_USE_WAIT                        TRUE\n#endif\n\n/**\n * @brief   Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define ADC_USE_MUTUAL_EXCLUSION            TRUE\n#endif\n\n/*===========================================================================*/\n/* CAN driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Sleep mode related APIs inclusion switch.\n */\n#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)\n#define CAN_USE_SLEEP_MODE                  TRUE\n#endif\n\n/**\n * @brief   Enforces the driver to use direct callbacks rather than OSAL events.\n */\n#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)\n#define CAN_ENFORCE_USE_CALLBACKS           FALSE\n#endif\n\n/*===========================================================================*/\n/* CRY driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables the SW fall-back of the cryptographic driver.\n * @details When enabled, this option, activates a fall-back software\n *          implementation for algorithms not supported by the underlying\n *          hardware.\n * @note    Fall-back implementations may not be present for all algorithms.\n */\n#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)\n#define HAL_CRY_USE_FALLBACK                FALSE\n#endif\n\n/**\n * @brief   Makes the driver forcibly use the fall-back implementations.\n */\n#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)\n#define HAL_CRY_ENFORCE_FALLBACK            FALSE\n#endif\n\n/*===========================================================================*/\n/* DAC driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)\n#define DAC_USE_WAIT                        TRUE\n#endif\n\n/**\n * @brief   Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define DAC_USE_MUTUAL_EXCLUSION            TRUE\n#endif\n\n/*===========================================================================*/\n/* I2C driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables the mutual exclusion APIs on the I2C bus.\n */\n#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define I2C_USE_MUTUAL_EXCLUSION            TRUE\n#endif\n\n/*===========================================================================*/\n/* MAC driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables the zero-copy API.\n */\n#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)\n#define MAC_USE_ZERO_COPY                   FALSE\n#endif\n\n/**\n * @brief   Enables an event sources for incoming packets.\n */\n#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)\n#define MAC_USE_EVENTS                      TRUE\n#endif\n\n/*===========================================================================*/\n/* MMC_SPI driver related settings.                                          */\n/*===========================================================================*/\n\n/**\n * @brief   Timeout before assuming a failure while waiting for card idle.\n * @note    Time is in milliseconds.\n */\n#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)\n#define MMC_IDLE_TIMEOUT_MS                 1000\n#endif\n\n/**\n * @brief   Mutual exclusion on the SPI bus.\n */\n#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define MMC_USE_MUTUAL_EXCLUSION            TRUE\n#endif\n\n/*===========================================================================*/\n/* SDC driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Number of initialization attempts before rejecting the card.\n * @note    Attempts are performed at 10mS intervals.\n */\n#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)\n#define SDC_INIT_RETRY                      100\n#endif\n\n/**\n * @brief   Include support for MMC cards.\n * @note    MMC support is not yet implemented so this option must be kept\n *          at @p FALSE.\n */\n#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)\n#define SDC_MMC_SUPPORT                     FALSE\n#endif\n\n/**\n * @brief   Delays insertions.\n * @details If enabled this options inserts delays into the MMC waiting\n *          routines releasing some extra CPU time for the threads with\n *          lower priority, this may slow down the driver a bit however.\n */\n#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)\n#define SDC_NICE_WAITING                    TRUE\n#endif\n\n/**\n * @brief   OCR initialization constant for V20 cards.\n */\n#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)\n#define SDC_INIT_OCR_V20                    0x50FF8000U\n#endif\n\n/**\n * @brief   OCR initialization constant for non-V20 cards.\n */\n#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)\n#define SDC_INIT_OCR                        0x80100000U\n#endif\n\n/*===========================================================================*/\n/* SERIAL driver related settings.                                           */\n/*===========================================================================*/\n\n/**\n * @brief   Default bit rate.\n * @details Configuration parameter, this is the baud rate selected for the\n *          default configuration.\n */\n#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)\n#define SERIAL_DEFAULT_BITRATE              38400\n#endif\n\n/**\n * @brief   Serial buffers size.\n * @details Configuration parameter, you can change the depth of the queue\n *          buffers depending on the requirements of your application.\n * @note    The default is 16 bytes for both the transmission and receive\n *          buffers.\n */\n#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)\n#define SERIAL_BUFFERS_SIZE                 128\n#endif\n\n/*===========================================================================*/\n/* SIO driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Default bit rate.\n * @details Configuration parameter, this is the baud rate selected for the\n *          default configuration.\n */\n#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)\n#define SIO_DEFAULT_BITRATE                 38400\n#endif\n\n/**\n * @brief   Support for thread synchronization API.\n */\n#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)\n#define SIO_USE_SYNCHRONIZATION             TRUE\n#endif\n\n/*===========================================================================*/\n/* SERIAL_USB driver related setting.                                        */\n/*===========================================================================*/\n\n/**\n * @brief   Serial over USB buffers size.\n * @details Configuration parameter, the buffer size must be a multiple of\n *          the USB data endpoint maximum packet size.\n * @note    The default is 256 bytes for both the transmission and receive\n *          buffers.\n */\n#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)\n#define SERIAL_USB_BUFFERS_SIZE             1\n#endif\n\n/**\n * @brief   Serial over USB number of buffers.\n * @note    The default is 2 buffers.\n */\n#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)\n#define SERIAL_USB_BUFFERS_NUMBER           2\n#endif\n\n/*===========================================================================*/\n/* SPI driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)\n#define SPI_USE_WAIT                        TRUE\n#endif\n\n/**\n * @brief   Inserts an assertion on function errors before returning.\n */\n#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)\n#define SPI_USE_ASSERT_ON_ERROR             TRUE\n#endif\n\n/**\n * @brief   Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define SPI_USE_MUTUAL_EXCLUSION            TRUE\n#endif\n\n/**\n * @brief   Handling method for SPI CS line.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)\n#define SPI_SELECT_MODE                     SPI_SELECT_MODE_PAD\n#endif\n\n/*===========================================================================*/\n/* UART driver related settings.                                             */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)\n#define UART_USE_WAIT                       FALSE\n#endif\n\n/**\n * @brief   Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define UART_USE_MUTUAL_EXCLUSION           FALSE\n#endif\n\n/*===========================================================================*/\n/* USB driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)\n#define USB_USE_WAIT                        TRUE\n#endif\n\n/*===========================================================================*/\n/* WSPI driver related settings.                                             */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)\n#define WSPI_USE_WAIT                       TRUE\n#endif\n\n/**\n * @brief   Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define WSPI_USE_MUTUAL_EXCLUSION           TRUE\n#endif\n\n#endif /* HALCONF_H */\n\n/** @} */\n"
  },
  {
    "path": "platforms/chibios/boards/QMK_PROTON_C/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32F3xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F3xx_MCUCONF\n#define STM32F303_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PREDIV_VALUE                  1\n#define STM32_PLLMUL_VALUE                  9\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV2\n#define STM32_PPRE2                         STM32_PPRE2_DIV2\n#define STM32_MCOSEL                        STM32_MCOSEL_NOCLOCK\n#define STM32_ADC12PRES                     STM32_ADC12PRES_DIV1\n#define STM32_ADC34PRES                     STM32_ADC34PRES_DIV1\n#define STM32_USART1SW                      STM32_USART1SW_PCLK\n#define STM32_USART2SW                      STM32_USART2SW_PCLK\n#define STM32_USART3SW                      STM32_USART3SW_PCLK\n#define STM32_UART4SW                       STM32_UART4SW_PCLK\n#define STM32_UART5SW                       STM32_UART5SW_PCLK\n#define STM32_I2C1SW                        STM32_I2C1SW_SYSCLK\n#define STM32_I2C2SW                        STM32_I2C2SW_SYSCLK\n#define STM32_TIM1SW                        STM32_TIM1SW_PCLK2\n#define STM32_TIM8SW                        STM32_TIM8SW_PCLK2\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n#define STM32_USB_CLOCK_REQUIRED            TRUE\n#define STM32_USBPRE                        STM32_USBPRE_DIV1P5\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           15\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           15\n#define STM32_IRQ_EXTI20_PRIORITY           15\n#define STM32_IRQ_EXTI21_22_29_PRIORITY     6\n#define STM32_IRQ_EXTI30_32_PRIORITY        6\n#define STM32_IRQ_EXTI33_PRIORITY           6\n#define STM32_IRQ_TIM1_BRK_TIM15_PRIORITY   7\n#define STM32_IRQ_TIM1_UP_TIM16_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM17_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_DUAL_MODE                 FALSE\n#define STM32_ADC_COMPACT_SAMPLES           FALSE\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_USE_ADC2                  FALSE\n#define STM32_ADC_USE_ADC3                  FALSE\n#define STM32_ADC_USE_ADC4                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(1, 1)\n#define STM32_ADC_ADC2_DMA_STREAM           STM32_DMA_STREAM_ID(2, 1)\n#define STM32_ADC_ADC3_DMA_STREAM           STM32_DMA_STREAM_ID(2, 5)\n#define STM32_ADC_ADC4_DMA_STREAM           STM32_DMA_STREAM_ID(2, 2)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_ADC2_DMA_PRIORITY         2\n#define STM32_ADC_ADC3_DMA_PRIORITY         2\n#define STM32_ADC_ADC4_DMA_PRIORITY         2\n#define STM32_ADC_ADC12_IRQ_PRIORITY        5\n#define STM32_ADC_ADC3_IRQ_PRIORITY         5\n#define STM32_ADC_ADC4_IRQ_PRIORITY         5\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC2_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC3_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC4_DMA_IRQ_PRIORITY     5\n#define STM32_ADC_ADC12_CLOCK_MODE          ADC_CCR_CKMODE_AHB_DIV1\n#define STM32_ADC_ADC34_CLOCK_MODE          ADC_CCR_CKMODE_AHB_DIV1\n\n/*\n * CAN driver system settings.\n */\n#define STM32_CAN_USE_CAN1                  FALSE\n#define STM32_CAN_CAN1_IRQ_PRIORITY         11\n\n/*\n * DAC driver system settings.\n */\n#define STM32_DAC_DUAL_MODE                 FALSE\n#define STM32_DAC_USE_DAC1_CH1              TRUE\n#define STM32_DAC_USE_DAC1_CH2              TRUE\n#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY     10\n#define STM32_DAC_DAC1_CH1_DMA_PRIORITY     2\n#define STM32_DAC_DAC1_CH2_DMA_PRIORITY     2\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM6                  TRUE\n#define STM32_GPT_USE_TIM7                  TRUE\n#define STM32_GPT_USE_TIM8                  TRUE\n#define STM32_GPT_USE_TIM15                 TRUE\n#define STM32_GPT_USE_TIM16                 FALSE\n#define STM32_GPT_USE_TIM17                 FALSE\n#define STM32_GPT_TIM1_IRQ_PRIORITY         7\n#define STM32_GPT_TIM2_IRQ_PRIORITY         7\n#define STM32_GPT_TIM3_IRQ_PRIORITY         7\n#define STM32_GPT_TIM4_IRQ_PRIORITY         7\n#define STM32_GPT_TIM6_IRQ_PRIORITY         7\n#define STM32_GPT_TIM7_IRQ_PRIORITY         7\n#define STM32_GPT_TIM8_IRQ_PRIORITY         7\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  TRUE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_IRQ_PRIORITY         10\n#define STM32_I2C_I2C2_IRQ_PRIORITY         10\n#define STM32_I2C_USE_DMA                   TRUE\n#define STM32_I2C_I2C1_DMA_PRIORITY         1\n#define STM32_I2C_I2C2_DMA_PRIORITY         1\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM8                  FALSE\n#define STM32_ICU_USE_TIM15                 FALSE\n#define STM32_ICU_TIM1_IRQ_PRIORITY         7\n#define STM32_ICU_TIM2_IRQ_PRIORITY         7\n#define STM32_ICU_TIM3_IRQ_PRIORITY         7\n#define STM32_ICU_TIM4_IRQ_PRIORITY         7\n#define STM32_ICU_TIM8_IRQ_PRIORITY         7\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  TRUE\n#define STM32_PWM_USE_TIM4                  TRUE\n#define STM32_PWM_USE_TIM8                  FALSE\n#define STM32_PWM_USE_TIM15                 FALSE\n#define STM32_PWM_USE_TIM16                 FALSE\n#define STM32_PWM_USE_TIM17                 FALSE\n#define STM32_PWM_TIM1_IRQ_PRIORITY         7\n#define STM32_PWM_TIM2_IRQ_PRIORITY         7\n#define STM32_PWM_TIM3_IRQ_PRIORITY         7\n#define STM32_PWM_TIM4_IRQ_PRIORITY         7\n#define STM32_PWM_TIM8_IRQ_PRIORITY         7\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             TRUE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART3             FALSE\n#define STM32_SERIAL_USE_UART4              FALSE\n#define STM32_SERIAL_USE_UART5              FALSE\n#define STM32_SERIAL_USART1_PRIORITY        12\n#define STM32_SERIAL_USART2_PRIORITY        12\n#define STM32_SERIAL_USART3_PRIORITY        12\n#define STM32_SERIAL_UART4_PRIORITY         12\n#define STM32_SERIAL_UART5_PRIORITY         12\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  TRUE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART3               FALSE\n#define STM32_UART_USART1_IRQ_PRIORITY      12\n#define STM32_UART_USART2_IRQ_PRIORITY      12\n#define STM32_UART_USART3_IRQ_PRIORITY      12\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART3_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_USB1                  TRUE\n#define STM32_USB_LOW_POWER_ON_SUSPEND      FALSE\n#define STM32_USB_USB1_HP_IRQ_PRIORITY      13\n#define STM32_USB_USB1_LP_IRQ_PRIORITY      14\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/SIPEED_LONGAN_NANO/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = ${CHIBIOS_CONTRIB}/os/hal/boards/SIPEED_LONGAN_NANO/board.c\n\n# Required include directories\nBOARDINC = ${CHIBIOS_CONTRIB}/os/hal/boards/SIPEED_LONGAN_NANO\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/SIPEED_LONGAN_NANO/configs/chconf.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* To compile the ChibiOS syscall stubs with picolibc\n * the _reent struct has to be defined. */\n#if !defined(_FROM_ASM_) && defined(USE_PICOLIBC)\nstruct _reent;\n#endif\n\n#include_next <chconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/SIPEED_LONGAN_NANO/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n    ChibiOS - Copyright (C) 2021 Stefan Kerkmann\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#pragma once\n\n#define GD32VF103_MCUCONF\n#define GD32VF103CB\n\n/*\n * GD32VF103 drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 0...15       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n/*\n * HAL driver system settings.\n*/\n\n#if defined(OVERCLOCK_120MHZ)\n/* (8MHz / 2) * 30 = 120MHz Sysclock */\n#define GD32_ALLOW_120MHZ_SYSCLK\n#define GD32_PLLMF_VALUE                     30\n#define GD32_USBFSPSC                        GD32_USBFSPSC_DIV2P5\n#else\n/* (8MHz / 2) * 24 = 96MHz Sysclock */\n#define GD32_PLLMF_VALUE                     24\n#define GD32_USBFSPSC                        GD32_USBFSPSC_DIV2\n#endif\n\n#define GD32_NO_INIT                         FALSE\n#define GD32_IRC8M_ENABLED                   TRUE\n#define GD32_IRC40K_ENABLED                  FALSE\n#define GD32_HXTAL_ENABLED                   TRUE\n#define GD32_LXTAL_ENABLED                   FALSE\n#define GD32_SCS                             GD32_SCS_PLL\n#define GD32_PLLSEL                          GD32_PLLSEL_PREDV0\n#define GD32_PREDV0SEL                       GD32_PREDV0SEL_HXTAL\n#define GD32_PREDV0_VALUE                    2\n#define GD32_PREDV1_VALUE                    2\n#define GD32_PLL1MF_VALUE                    14\n#define GD32_PLL2MF_VALUE                    13\n#define GD32_AHBPSC                          GD32_AHBPSC_DIV1\n#define GD32_APB1PSC                         GD32_APB1PSC_DIV2\n#define GD32_APB2PSC                         GD32_APB2PSC_DIV1\n#define GD32_ADCPSC                          GD32_ADCPSC_DIV16\n#define GD32_USB_CLOCK_REQUIRED              TRUE\n#define GD32_I2S_CLOCK_REQUIRED              FALSE\n#define GD32_CKOUT0SEL                       GD32_CKOUT0SEL_NOCLOCK\n#define GD32_RTCSRC                          GD32_RTCSRC_NOCLOCK\n#define GD32_PVD_ENABLE                      FALSE\n#define GD32_LVDT                            GD32_LVDT_LEV0\n\n/*\n * ECLIC system settings.\n */\n#define ECLIC_TRIGGER_DEFAULT                ECLIC_POSTIVE_EDGE_TRIGGER\n#define ECLIC_DMA_TRIGGER                    ECLIC_TRIGGER_DEFAULT\n\n/*\n * IRQ system settings.\n */\n#define GD32_IRQ_EXTI0_PRIORITY              6\n#define GD32_IRQ_EXTI1_PRIORITY              6\n#define GD32_IRQ_EXTI2_PRIORITY              6\n#define GD32_IRQ_EXTI3_PRIORITY              6\n#define GD32_IRQ_EXTI4_PRIORITY              6\n#define GD32_IRQ_EXTI5_9_PRIORITY            6\n#define GD32_IRQ_EXTI10_15_PRIORITY          6\n#define GD32_IRQ_EXTI0_TRIGGER               ECLIC_TRIGGER_DEFAULT\n#define GD32_IRQ_EXTI1_TRIGGER               ECLIC_TRIGGER_DEFAULT\n#define GD32_IRQ_EXTI2_TRIGGER               ECLIC_TRIGGER_DEFAULT\n#define GD32_IRQ_EXTI3_TRIGGER               ECLIC_TRIGGER_DEFAULT\n#define GD32_IRQ_EXTI4_TRIGGER               ECLIC_TRIGGER_DEFAULT\n#define GD32_IRQ_EXTI5_9_TRIGGER             ECLIC_TRIGGER_DEFAULT\n#define GD32_IRQ_EXTI10_15_TRIGGER           ECLIC_TRIGGER_DEFAULT\n\n/*\n * ADC driver system settings.\n */\n#define GD32_ADC_USE_ADC0                    FALSE\n#define GD32_ADC_ADC0_DMA_PRIORITY           2\n#define GD32_ADC_ADC0_IRQ_PRIORITY           6\n\n/*\n * CAN driver system settings.\n */\n#define GD32_CAN_USE_CAN0                    FALSE\n#define GD32_CAN_CAN0_IRQ_PRIORITY           11\n#define GD32_CAN_USE_CAN1                    FALSE\n#define GD32_CAN_CAN1_IRQ_PRIORITY           11\n#define GD32_CAN_CAN0_IRQ_TRIGGER            ECLIC_TRIGGER_DEFAULT\n#define GD32_CAN_CAN1_IRQ_TRIGGER            ECLIC_TRIGGER_DEFAULT\n\n/*\n * CRC driver system settings.\n */\n#define GD32_CRC_USE_CRC0                   FALSE\n#define GD32_CRC_CRC0_DMA_IRQ_PRIORITY      14\n#define GD32_CRC_CRC0_DMA_PRIORITY          2\n#define GD32_CRC_CRC0_DMA_STREAM            GD32_DMA_STREAM_ID(0, 0)\n#define CRC_USE_DMA                         FALSE\n#define CRCSW_USE_CRC1                      FALSE\n#define CRCSW_CRC32_TABLE                   FALSE\n#define CRCSW_CRC16_TABLE                   FALSE\n#define CRCSW_PROGRAMMABLE                  FALSE\n\n/*\n * DAC driver system settings.\n */\n#define GD32_DAC_USE_DAC_CH1                FALSE\n#define GD32_DAC_USE_DAC_CH2                FALSE\n\n/*\n * GPT driver system settings.\n */\n#define GD32_GPT_USE_TIM0                   FALSE\n#define GD32_GPT_USE_TIM1                   FALSE\n#define GD32_GPT_USE_TIM2                   FALSE\n#define GD32_GPT_USE_TIM3                   FALSE\n#define GD32_GPT_USE_TIM4                   FALSE\n#define GD32_GPT_TIM0_IRQ_PRIORITY          7\n#define GD32_GPT_TIM1_IRQ_PRIORITY          7\n#define GD32_GPT_TIM2_IRQ_PRIORITY          7\n#define GD32_GPT_TIM3_IRQ_PRIORITY          7\n#define GD32_GPT_TIM4_IRQ_PRIORITY          7\n#define GD32_GPT_TIM0_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_GPT_TIM1_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_GPT_TIM2_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_GPT_TIM3_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_GPT_TIM4_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_GPT_TIM5_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_GPT_TIM6_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n\n/*\n * I2S driver system settings.\n */\n#define GD32_I2S_USE_SPI1                   FALSE\n#define GD32_I2S_USE_SPI2                   FALSE\n#define GD32_I2S_SPI1_IRQ_PRIORITY          10\n#define GD32_I2S_SPI2_IRQ_PRIORITY          10\n#define GD32_I2S_SPI1_DMA_PRIORITY          1\n#define GD32_I2S_SPI2_DMA_PRIORITY          1\n#define GD32_I2S_DMA_ERROR_HOOK(i2sp)       osalSysHalt(\"DMA failure\")\n\n/*\n * I2C driver system settings.\n */\n#define GD32_I2C_USE_I2C0                   FALSE\n#define GD32_I2C_USE_I2C1                   FALSE\n#define GD32_I2C_BUSY_TIMEOUT               50\n#define GD32_I2C_I2C0_IRQ_PRIORITY          10\n#define GD32_I2C_I2C1_IRQ_PRIORITY          5\n#define GD32_I2C_I2C0_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_I2C_I2C1_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_I2C_I2C0_DMA_PRIORITY          2\n#define GD32_I2C_I2C1_DMA_PRIORITY          2\n#define GD32_I2C_DMA_ERROR_HOOK(i2cp)       osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define GD32_ICU_USE_TIM0                   FALSE\n#define GD32_ICU_USE_TIM1                   FALSE\n#define GD32_ICU_USE_TIM2                   FALSE\n#define GD32_ICU_USE_TIM3                   FALSE\n#define GD32_ICU_USE_TIM4                   FALSE\n#define GD32_ICU_TIM0_IRQ_PRIORITY          7\n#define GD32_ICU_TIM1_IRQ_PRIORITY          7\n#define GD32_ICU_TIM2_IRQ_PRIORITY          7\n#define GD32_ICU_TIM3_IRQ_PRIORITY          7\n#define GD32_ICU_TIM4_IRQ_PRIORITY          7\n#define GD32_ICU_TIM0_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_ICU_TIM1_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_ICU_TIM2_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_ICU_TIM3_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_ICU_TIM4_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n\n/*\n * PWM driver system settings.\n */\n#define GD32_PWM_USE_ADVANCED               FALSE\n#define GD32_PWM_USE_TIM0                   FALSE\n#define GD32_PWM_USE_TIM1                   FALSE\n#define GD32_PWM_USE_TIM2                   FALSE\n#define GD32_PWM_USE_TIM3                   FALSE\n#define GD32_PWM_USE_TIM4                   FALSE\n#define GD32_PWM_TIM0_IRQ_PRIORITY          10\n#define GD32_PWM_TIM1_IRQ_PRIORITY          10\n#define GD32_PWM_TIM2_IRQ_PRIORITY          10\n#define GD32_PWM_TIM3_IRQ_PRIORITY          10\n#define GD32_PWM_TIM4_IRQ_PRIORITY          10\n#define GD32_PWM_TIM0_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_PWM_TIM1_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_PWM_TIM2_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_PWM_TIM3_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_PWM_TIM4_IRQ_TRIGGER           ECLIC_TRIGGER_DEFAULT\n\n/*\n * RTC driver system settings.\n */\n#define GD32_RTC_IRQ_PRIORITY               15\n#define GD32_RTC_IRQ_TRIGGER                ECLIC_TRIGGER_DEFAULT\n\n/*\n * SERIAL driver system settings.\n */\n#define GD32_SERIAL_USE_USART0              FALSE\n#define GD32_SERIAL_USE_USART1              FALSE\n#define GD32_SERIAL_USE_USART2              FALSE\n#define GD32_SERIAL_USE_UART3               FALSE\n#define GD32_SERIAL_USE_UART4               FALSE\n#define GD32_SERIAL_USART0_PRIORITY         10\n#define GD32_SERIAL_USART1_PRIORITY         10\n#define GD32_SERIAL_USART2_PRIORITY         10\n#define GD32_SERIAL_UART3_PRIORITY          10\n#define GD32_SERIAL_UART4_PRIORITY          10\n#define GD32_SERIAL_USART0_TRIGGER          ECLIC_TRIGGER_DEFAULT\n#define GD32_SERIAL_USART1_TRIGGER          ECLIC_TRIGGER_DEFAULT\n#define GD32_SERIAL_USART2_TRIGGER          ECLIC_TRIGGER_DEFAULT\n#define GD32_SERIAL_UART3_TRIGGER           ECLIC_TRIGGER_DEFAULT\n#define GD32_SERIAL_UART4_TRIGGER           ECLIC_TRIGGER_DEFAULT\n\n/*\n * SPI driver system settings.\n */\n#define GD32_SPI_USE_SPI0                   FALSE\n#define GD32_SPI_USE_SPI1                   FALSE\n#define GD32_SPI_USE_SPI2                   FALSE\n#define GD32_SPI_SPI0_DMA_PRIORITY          1\n#define GD32_SPI_SPI1_DMA_PRIORITY          1\n#define GD32_SPI_SPI2_DMA_PRIORITY          1\n#define GD32_SPI_SPI0_IRQ_PRIORITY          10\n#define GD32_SPI_SPI1_IRQ_PRIORITY          10\n#define GD32_SPI_SPI2_IRQ_PRIORITY          10\n#define GD32_SPI_DMA_ERROR_HOOK(spip)       osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define GD32_ST_IRQ_PRIORITY                10\n#define GD32_ST_IRQ_TRIGGER                 ECLIC_TRIGGER_DEFAULT\n#define GD32_ST_USE_TIMER                   1\n\n/*\n * UART driver system settings.\n */\n#define GD32_UART_USE_USART0                FALSE\n#define GD32_UART_USE_USART1                FALSE\n#define GD32_UART_USE_USART2                FALSE\n#define GD32_UART_USE_UART3                 FALSE\n#define GD32_UART_USE_UART4                 FALSE\n#define GD32_UART_USART0_IRQ_PRIORITY       10\n#define GD32_UART_USART1_IRQ_PRIORITY       10\n#define GD32_UART_USART2_IRQ_PRIORITY       10\n#define GD32_UART_UART3_IRQ_PRIORITY        10\n#define GD32_UART_UART4_IRQ_PRIORITY        10\n#define GD32_UART_USART0_IRQ_TRIGGER        ECLIC_TRIGGER_DEFAULT\n#define GD32_UART_USART1_IRQ_TRIGGER        ECLIC_TRIGGER_DEFAULT\n#define GD32_UART_USART2_IRQ_TRIGGER        ECLIC_TRIGGER_DEFAULT\n#define GD32_UART_UART3_IRQ_TRIGGER         ECLIC_TRIGGER_DEFAULT\n#define GD32_UART_UART4_IRQ_TRIGGER         ECLIC_TRIGGER_DEFAULT\n#define GD32_UART_USART0_DMA_PRIORITY       3\n#define GD32_UART_USART1_DMA_PRIORITY       3\n#define GD32_UART_USART2_DMA_PRIORITY       3\n#define GD32_UART_UART3_DMA_PRIORITY        3\n#define GD32_UART_UART4_DMA_PRIORITY        3\n#define GD32_UART_DMA_ERROR_HOOK(uartp)     osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define GD32_USB_USE_USBFS                  TRUE\n#define GD32_USB_USBFS_IRQ_PRIORITY         10\n#define GD32_USB_USBFS_IRQ_TRIGGER          ECLIC_TRIGGER_DEFAULT\n#define GD32_USB_USBFS_RX_FIFO_SIZE         256\n\n/*\n * WDG driver system settings.\n */\n#define GD32_WDG_USE_FWDGT                  FALSE\n"
  },
  {
    "path": "platforms/chibios/boards/STEMCELL/board/board.mk",
    "content": "# Copyright 2022 Mega Mind (@megamind4089)\n# SPDX-License-Identifier: GPL-2.0-or-later\n\n# Default pin config of nucleo64_411re has most pins in input pull up mode\n\n# List of all the board related files.\nBOARDSRC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F411RE/board.c\n\n# Required include directories\nBOARDINC = $(CHIBIOS)/os/hal/boards/ST_NUCLEO64_F411RE\n\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/STEMCELL/configs/board.h",
    "content": "// Copyright 2022 Mega Mind (@megamind4089)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include_next <board.h>\n\n#undef STM32_HSE_BYPASS\n"
  },
  {
    "path": "platforms/chibios/boards/STEMCELL/configs/chconf.h",
    "content": "// Copyright 2022 Mega Mind (@megamind4089)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#define CH_CFG_ST_RESOLUTION 16\n#define CH_CFG_ST_FREQUENCY 10000\n\n#include_next <chconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/STEMCELL/configs/config.h",
    "content": "// Copyright 2022 Mega Mind(@megamind4089)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE\n#endif\n\n/**======================\n **    I2C Driver\n *========================**/\n\n#if !defined(I2C1_SDA_PIN)\n#    define I2C1_SDA_PIN D0\n#endif\n\n#if !defined(I2C1_SCL_PIN)\n#    define I2C1_SCL_PIN D1\n#endif\n\n/**======================\n **      SERIAL Driver\n *========================**/\n\n#if !defined(SERIAL_USART_DRIVER)\n#    define SERIAL_USART_DRIVER SD2\n#endif\n\n"
  },
  {
    "path": "platforms/chibios/boards/STEMCELL/configs/halconf.h",
    "content": "// Copyright 2022 Mega Mind (@megamind4089)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#define PAL_USE_WAIT TRUE\n#define PAL_USE_CALLBACKS TRUE\n#define HAL_USE_I2C TRUE\n#define HAL_USE_SERIAL TRUE\n\n#include_next <halconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/STEMCELL/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef MCUCONF_H\n#define MCUCONF_H\n\n/*\n * STM32F4xx drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n#define STM32F4xx_MCUCONF\n#define STM32F411_MCUCONF\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n#define STM32_BKPRAM_ENABLE                 FALSE\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   TRUE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_CLOCK48_REQUIRED              TRUE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PLLM_VALUE                    8\n#define STM32_PLLN_VALUE                    336\n#define STM32_PLLP_VALUE                    4\n#define STM32_PLLQ_VALUE                    7\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV2\n#define STM32_PPRE2                         STM32_PPRE2_DIV1\n#define STM32_RTCSEL                        STM32_RTCSEL_LSI\n#define STM32_RTCPRE_VALUE                  8\n#define STM32_MCO1SEL                       STM32_MCO1SEL_HSI\n#define STM32_MCO1PRE                       STM32_MCO1PRE_DIV1\n#define STM32_MCO2SEL                       STM32_MCO2SEL_SYSCLK\n#define STM32_MCO2PRE                       STM32_MCO2PRE_DIV5\n#define STM32_I2SSRC                        STM32_I2SSRC_CKIN\n#define STM32_PLLI2SN_VALUE                 192\n#define STM32_PLLI2SR_VALUE                 5\n\n/*\n * IRQ system settings.\n */\n#define STM32_IRQ_EXTI0_PRIORITY            6\n#define STM32_IRQ_EXTI1_PRIORITY            6\n#define STM32_IRQ_EXTI2_PRIORITY            6\n#define STM32_IRQ_EXTI3_PRIORITY            6\n#define STM32_IRQ_EXTI4_PRIORITY            6\n#define STM32_IRQ_EXTI5_9_PRIORITY          6\n#define STM32_IRQ_EXTI10_15_PRIORITY        6\n#define STM32_IRQ_EXTI16_PRIORITY           6\n#define STM32_IRQ_EXTI17_PRIORITY           15\n#define STM32_IRQ_EXTI18_PRIORITY           6\n#define STM32_IRQ_EXTI19_PRIORITY           6\n#define STM32_IRQ_EXTI20_PRIORITY           6\n#define STM32_IRQ_EXTI21_PRIORITY           15\n#define STM32_IRQ_EXTI22_PRIORITY           15\n\n#define STM32_IRQ_TIM1_BRK_TIM9_PRIORITY    7\n#define STM32_IRQ_TIM1_UP_TIM10_PRIORITY    7\n#define STM32_IRQ_TIM1_TRGCO_TIM11_PRIORITY 7\n#define STM32_IRQ_TIM1_CC_PRIORITY          7\n#define STM32_IRQ_TIM2_PRIORITY             7\n#define STM32_IRQ_TIM3_PRIORITY             7\n#define STM32_IRQ_TIM4_PRIORITY             7\n#define STM32_IRQ_TIM5_PRIORITY             7\n\n#define STM32_IRQ_USART1_PRIORITY           12\n#define STM32_IRQ_USART2_PRIORITY           12\n#define STM32_IRQ_USART6_PRIORITY           12\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_ADCPRE                    ADC_CCR_ADCPRE_DIV4\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_DMA_STREAM           STM32_DMA_STREAM_ID(2, 4)\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_IRQ_PRIORITY              6\n#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY     6\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM9                  FALSE\n#define STM32_GPT_USE_TIM10                 FALSE\n#define STM32_GPT_USE_TIM11                 FALSE\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  TRUE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_USE_I2C3                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2C_I2C1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 6)\n#define STM32_I2C_I2C2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2C_I2C3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 2)\n#define STM32_I2C_I2C3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C3_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_I2C3_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * I2S driver system settings.\n */\n#define STM32_I2S_USE_SPI2                  FALSE\n#define STM32_I2S_USE_SPI3                  FALSE\n#define STM32_I2S_SPI2_IRQ_PRIORITY         10\n#define STM32_I2S_SPI3_IRQ_PRIORITY         10\n#define STM32_I2S_SPI2_DMA_PRIORITY         1\n#define STM32_I2S_SPI3_DMA_PRIORITY         1\n#define STM32_I2S_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_I2S_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_I2S_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_I2S_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_I2S_DMA_ERROR_HOOK(i2sp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM9                  FALSE\n#define STM32_ICU_USE_TIM10                 FALSE\n#define STM32_ICU_USE_TIM11                 FALSE\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM9                  FALSE\n#define STM32_PWM_USE_TIM10                 FALSE\n#define STM32_PWM_USE_TIM11                 FALSE\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_PRESA_VALUE               32\n#define STM32_RTC_PRESS_VALUE               1024\n#define STM32_RTC_CR_INIT                   0\n#define STM32_RTC_TAMPCR_INIT               0\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             TRUE\n#define STM32_SERIAL_USE_USART2             TRUE\n#define STM32_SERIAL_USE_USART6             FALSE\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  FALSE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_RX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 0)\n#define STM32_SPI_SPI1_TX_DMA_STREAM        STM32_DMA_STREAM_ID(2, 3)\n#define STM32_SPI_SPI2_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 3)\n#define STM32_SPI_SPI2_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 4)\n#define STM32_SPI_SPI3_RX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 0)\n#define STM32_SPI_SPI3_TX_DMA_STREAM        STM32_DMA_STREAM_ID(1, 7)\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART6               FALSE\n#define STM32_UART_USART1_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 5)\n#define STM32_UART_USART1_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART2_RX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 5)\n#define STM32_UART_USART2_TX_DMA_STREAM     STM32_DMA_STREAM_ID(1, 6)\n#define STM32_UART_USART6_RX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 2)\n#define STM32_UART_USART6_TX_DMA_STREAM     STM32_DMA_STREAM_ID(2, 7)\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART6_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_OTG1                  TRUE\n#define STM32_USB_OTG1_IRQ_PRIORITY         14\n#define STM32_USB_OTG1_RX_FIFO_SIZE         512\n#define STM32_USB_HOST_WAKEUP_DURATION      2\n\n/*\n * WDG driver system settings.\n */\n#define STM32_WDG_USE_IWDG                  FALSE\n\n#endif /* MCUCONF_H */\n"
  },
  {
    "path": "platforms/chibios/boards/STM32_F103_STM32DUINO/board/board.c",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#include <hal.h>\n\n/**\n * @brief   PAL setup.\n * @details Digital I/O ports static configuration as defined in @p board.h.\n *          This variable is used by the HAL when initializing the PAL driver.\n */\n#if HAL_USE_PAL || defined(__DOXYGEN__)\nconst PALConfig pal_default_config =\n{\n  {VAL_GPIOAODR, VAL_GPIOACRL, VAL_GPIOACRH},\n  {VAL_GPIOBODR, VAL_GPIOBCRL, VAL_GPIOBCRH},\n  {VAL_GPIOCODR, VAL_GPIOCCRL, VAL_GPIOCCRH},\n  {VAL_GPIODODR, VAL_GPIODCRL, VAL_GPIODCRH},\n#    if STM32_HAS_GPIOE\n  {VAL_GPIOEODR, VAL_GPIOECRL, VAL_GPIOECRH},\n#    endif\n};\n#endif\n\n__attribute__((weak)) void enter_bootloader_mode_if_requested(void) {}\n\n/*\n * Early initialization code.\n * This initialization must be performed just after stack setup and before\n * any other initialization.\n */\nvoid __early_init(void) {\n  enter_bootloader_mode_if_requested();\n\n  stm32_clock_init();\n}\n\n/*\n * Board-specific initialization code.\n */\nvoid boardInit(void) {\n   //JTAG-DP Disabled and SW-DP Enabled\n   AFIO->MAPR |= AFIO_MAPR_SWJ_CFG_JTAGDISABLE;\n   //Set backup register DR10 to enter bootloader on reset\n   BKP->DR10 = RTC_BOOTLOADER_FLAG;\n}\n"
  },
  {
    "path": "platforms/chibios/boards/STM32_F103_STM32DUINO/board/board.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef _BOARD_H_\n#define _BOARD_H_\n\n/*\n * Setup for a Generic STM32F103 board.\n */\n\n/*\n * Board identifier.\n */\n#define BOARD_STM32_F103_STM32DUINO\n#define BOARD_NAME              \"GENERIC STM32F103C8T6 board - stm32duino bootloader\"\n\n/*\n * Board frequencies.\n */\n#define STM32_LSECLK            32768\n#define STM32_HSECLK            8000000\n\n/*\n * MCU type, supported types are defined in ./os/hal/platforms/hal_lld.h.\n */\n#define STM32F103xB\n\n/*\n * IO pins assignments\n */\n\n/* on-board */\n\n#define GPIOA_LED               8\n#define GPIOD_OSC_IN            0\n#define GPIOD_OSC_OUT           1\n\n/* In case your board has a \"USB enable\" hardware\n   controlled by a pin, define it here. (It could be just\n   a 1.5k resistor connected to D+ line.)\n*/\n/*\n#define GPIOB_USB_DISC          10\n*/\n\n/*\n * I/O ports initial setup, this configuration is established soon after reset\n * in the initialization code.\n *\n * The digits have the following meaning:\n *   0 - Analog input.\n *   1 - Push Pull output 10MHz.\n *   2 - Push Pull output 2MHz.\n *   3 - Push Pull output 50MHz.\n *   4 - Digital input.\n *   5 - Open Drain output 10MHz.\n *   6 - Open Drain output 2MHz.\n *   7 - Open Drain output 50MHz.\n *   8 - Digital input with PullUp or PullDown resistor depending on ODR.\n *   9 - Alternate Push Pull output 10MHz.\n *   A - Alternate Push Pull output 2MHz.\n *   B - Alternate Push Pull output 50MHz.\n *   C - Reserved.\n *   D - Alternate Open Drain output 10MHz.\n *   E - Alternate Open Drain output 2MHz.\n *   F - Alternate Open Drain output 50MHz.\n * Please refer to the STM32 Reference Manual for details.\n */\n\n/*\n * Port A setup.\n * Everything input with pull-up except:\n * PA2  - Alternate output  (USART2 TX).\n * PA3  - Normal input      (USART2 RX).\n * PA9  - Alternate output  (USART1 TX).\n * PA10 - Normal input      (USART1 RX).\n */\n#define VAL_GPIOACRL            0x88884B88      /*  PA7...PA0 */\n#define VAL_GPIOACRH            0x888884B8      /* PA15...PA8 */\n#define VAL_GPIOAODR            0xFFFFFFFF\n\n/*\n * Port B setup.\n * Everything input with pull-up except:\n * PB10    - Push Pull output  (USB switch).\n */\n#define VAL_GPIOBCRL            0x88888888      /*  PB7...PB0 */\n#define VAL_GPIOBCRH            0x88888388      /* PB15...PB8 */\n#define VAL_GPIOBODR            0xFFFFFFFF\n\n/*\n * Port C setup.\n * Everything input with pull-up except:\n * PC13    - Push Pull output  (LED).\n */\n#define VAL_GPIOCCRL            0x88888888      /*  PC7...PC0 */\n#define VAL_GPIOCCRH            0x88388888      /* PC15...PC8 */\n#define VAL_GPIOCODR            0xFFFFFFFF\n\n/*\n * Port D setup.\n * Everything input with pull-up except:\n * PD0  - Normal input (XTAL).\n * PD1  - Normal input (XTAL).\n */\n#define VAL_GPIODCRL            0x88888844      /*  PD7...PD0 */\n#define VAL_GPIODCRH            0x88888888      /* PD15...PD8 */\n#define VAL_GPIODODR            0xFFFFFFFF\n\n/*\n * Port E setup.\n * Everything input with pull-up except:\n */\n#define VAL_GPIOECRL            0x88888888      /*  PE7...PE0 */\n#define VAL_GPIOECRH            0x88888888      /* PE15...PE8 */\n#define VAL_GPIOEODR            0xFFFFFFFF\n\n/*\n * USB bus activation macro, required by the USB driver.\n */\n/* The point is that most of the generic STM32F103* boards\n   have a 1.5k resistor connected on one end to the D+ line\n   and on the other end to some pin. Or even a slightly more\n   complicated \"USB enable\" circuit, controlled by a pin.\n   That should go here.\n\n   However on some boards (e.g. one that I have), there's no\n   such hardware. In which case it's better to not do anything.\n*/\n/*\n#define usb_lld_connect_bus(usbp) palClearPad(GPIOB, GPIOB_USB_DISC)\n*/\n#define usb_lld_connect_bus(usbp) palSetPadMode(GPIOA, 12, PAL_MODE_INPUT);\n\n/*\n * USB bus de-activation macro, required by the USB driver.\n */\n/*\n#define usb_lld_disconnect_bus(usbp) palSetPad(GPIOB, GPIOB_USB_DISC)\n*/\n#define usb_lld_disconnect_bus(usbp) palSetPadMode(GPIOA, 12, PAL_MODE_OUTPUT_PUSHPULL); palClearPad(GPIOA, 12);\n\n#if !defined(_FROM_ASM_)\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n  void boardInit(void);\n#ifdef __cplusplus\n}\n#endif\n#endif /* _FROM_ASM_ */\n\n#endif /* _BOARD_H_ */\n"
  },
  {
    "path": "platforms/chibios/boards/STM32_F103_STM32DUINO/board/board.mk",
    "content": "# List of all the board related files.\nBOARDSRC = $(BOARD_PATH)/board/board.c\n\n# Required include directories\nBOARDINC = $(BOARD_PATH)/board\n\n# Shared variables\nALLCSRC += $(BOARDSRC)\nALLINC  += $(BOARDINC)\n"
  },
  {
    "path": "platforms/chibios/boards/STM32_F103_STM32DUINO/configs/chconf.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#define CH_CFG_ST_TIMEDELTA 0\n\n#include_next <chconf.h>\n"
  },
  {
    "path": "platforms/chibios/boards/STM32_F103_STM32DUINO/configs/config.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n// Value to place in RTC backup register 10 for persistent bootloader mode\n#define RTC_BOOTLOADER_FLAG 0x424C\n\n// Value to place in RTC backup register 10 for instant reboot mode\n#define RTC_BOOTLOADER_JUST_UPLOADED 0x424D\n"
  },
  {
    "path": "platforms/chibios/boards/STM32_F103_STM32DUINO/configs/mcuconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n#ifndef _MCUCONF_H_\n#define _MCUCONF_H_\n\n#define STM32F103_MCUCONF\n\n/*\n * STM32F103 drivers configuration.\n * The following settings override the default settings present in\n * the various device driver implementation headers.\n * Note that the settings for each driver only have effect if the whole\n * driver is enabled in halconf.h.\n *\n * IRQ priorities:\n * 15...0       Lowest...Highest.\n *\n * DMA priorities:\n * 0...3        Lowest...Highest.\n */\n\n/*\n * HAL driver system settings.\n */\n#define STM32_NO_INIT                       FALSE\n#define STM32_HSI_ENABLED                   TRUE\n#define STM32_LSI_ENABLED                   FALSE\n#define STM32_HSE_ENABLED                   TRUE\n#define STM32_LSE_ENABLED                   FALSE\n#define STM32_SW                            STM32_SW_PLL\n#define STM32_PLLSRC                        STM32_PLLSRC_HSE\n#define STM32_PLLXTPRE                      STM32_PLLXTPRE_DIV1\n#define STM32_PLLMUL_VALUE                  9\n#define STM32_HPRE                          STM32_HPRE_DIV1\n#define STM32_PPRE1                         STM32_PPRE1_DIV2\n#define STM32_PPRE2                         STM32_PPRE2_DIV2\n#define STM32_ADCPRE                        STM32_ADCPRE_DIV4\n#define STM32_USB_CLOCK_REQUIRED            TRUE\n#define STM32_USBPRE                        STM32_USBPRE_DIV1P5\n#define STM32_MCOSEL                        STM32_MCOSEL_NOCLOCK\n#define STM32_RTCSEL                        STM32_RTCSEL_HSEDIV\n#define STM32_PVD_ENABLE                    FALSE\n#define STM32_PLS                           STM32_PLS_LEV0\n\n/*\n * ADC driver system settings.\n */\n#define STM32_ADC_USE_ADC1                  FALSE\n#define STM32_ADC_ADC1_DMA_PRIORITY         2\n#define STM32_ADC_ADC1_IRQ_PRIORITY         6\n\n/*\n * CAN driver system settings.\n */\n#define STM32_CAN_USE_CAN1                  FALSE\n#define STM32_CAN_CAN1_IRQ_PRIORITY         11\n\n/*\n * EXT driver system settings.\n */\n#define STM32_EXT_EXTI0_IRQ_PRIORITY        6\n#define STM32_EXT_EXTI1_IRQ_PRIORITY        6\n#define STM32_EXT_EXTI2_IRQ_PRIORITY        6\n#define STM32_EXT_EXTI3_IRQ_PRIORITY        6\n#define STM32_EXT_EXTI4_IRQ_PRIORITY        6\n#define STM32_EXT_EXTI5_9_IRQ_PRIORITY      6\n#define STM32_EXT_EXTI10_15_IRQ_PRIORITY    6\n#define STM32_EXT_EXTI16_IRQ_PRIORITY       6\n#define STM32_EXT_EXTI17_IRQ_PRIORITY       6\n#define STM32_EXT_EXTI18_IRQ_PRIORITY       6\n#define STM32_EXT_EXTI19_IRQ_PRIORITY       6\n\n/*\n * GPT driver system settings.\n */\n#define STM32_GPT_USE_TIM1                  FALSE\n#define STM32_GPT_USE_TIM2                  FALSE\n#define STM32_GPT_USE_TIM3                  FALSE\n#define STM32_GPT_USE_TIM4                  FALSE\n#define STM32_GPT_USE_TIM5                  FALSE\n#define STM32_GPT_USE_TIM8                  FALSE\n#define STM32_GPT_TIM1_IRQ_PRIORITY         7\n#define STM32_GPT_TIM2_IRQ_PRIORITY         7\n#define STM32_GPT_TIM3_IRQ_PRIORITY         7\n#define STM32_GPT_TIM4_IRQ_PRIORITY         7\n#define STM32_GPT_TIM5_IRQ_PRIORITY         7\n#define STM32_GPT_TIM8_IRQ_PRIORITY         7\n\n/*\n * I2C driver system settings.\n */\n#define STM32_I2C_USE_I2C1                  FALSE\n#define STM32_I2C_USE_I2C2                  FALSE\n#define STM32_I2C_BUSY_TIMEOUT              50\n#define STM32_I2C_I2C1_IRQ_PRIORITY         5\n#define STM32_I2C_I2C2_IRQ_PRIORITY         5\n#define STM32_I2C_I2C1_DMA_PRIORITY         3\n#define STM32_I2C_I2C2_DMA_PRIORITY         3\n#define STM32_I2C_DMA_ERROR_HOOK(i2cp)      osalSysHalt(\"DMA failure\")\n\n/*\n * ICU driver system settings.\n */\n#define STM32_ICU_USE_TIM1                  FALSE\n#define STM32_ICU_USE_TIM2                  FALSE\n#define STM32_ICU_USE_TIM3                  FALSE\n#define STM32_ICU_USE_TIM4                  FALSE\n#define STM32_ICU_USE_TIM5                  FALSE\n#define STM32_ICU_USE_TIM8                  FALSE\n#define STM32_ICU_TIM1_IRQ_PRIORITY         7\n#define STM32_ICU_TIM2_IRQ_PRIORITY         7\n#define STM32_ICU_TIM3_IRQ_PRIORITY         7\n#define STM32_ICU_TIM4_IRQ_PRIORITY         7\n#define STM32_ICU_TIM5_IRQ_PRIORITY         7\n#define STM32_ICU_TIM8_IRQ_PRIORITY         7\n\n/*\n * PWM driver system settings.\n */\n#define STM32_PWM_USE_ADVANCED              FALSE\n#define STM32_PWM_USE_TIM1                  FALSE\n#define STM32_PWM_USE_TIM2                  FALSE\n#define STM32_PWM_USE_TIM3                  FALSE\n#define STM32_PWM_USE_TIM4                  FALSE\n#define STM32_PWM_USE_TIM5                  FALSE\n#define STM32_PWM_USE_TIM8                  FALSE\n#define STM32_PWM_TIM1_IRQ_PRIORITY         7\n#define STM32_PWM_TIM2_IRQ_PRIORITY         7\n#define STM32_PWM_TIM3_IRQ_PRIORITY         7\n#define STM32_PWM_TIM4_IRQ_PRIORITY         7\n#define STM32_PWM_TIM5_IRQ_PRIORITY         7\n#define STM32_PWM_TIM8_IRQ_PRIORITY         7\n\n/*\n * RTC driver system settings.\n */\n#define STM32_RTC_IRQ_PRIORITY              15\n\n/*\n * SERIAL driver system settings.\n */\n#define STM32_SERIAL_USE_USART1             FALSE\n#define STM32_SERIAL_USE_USART2             FALSE\n#define STM32_SERIAL_USE_USART3             FALSE\n#define STM32_SERIAL_USE_UART4              FALSE\n#define STM32_SERIAL_USE_UART5              FALSE\n#define STM32_SERIAL_USART1_PRIORITY        12\n#define STM32_SERIAL_USART2_PRIORITY        12\n#define STM32_SERIAL_USART3_PRIORITY        12\n#define STM32_SERIAL_UART4_PRIORITY         12\n#define STM32_SERIAL_UART5_PRIORITY         12\n\n/*\n * SPI driver system settings.\n */\n#define STM32_SPI_USE_SPI1                  FALSE\n#define STM32_SPI_USE_SPI2                  TRUE\n#define STM32_SPI_USE_SPI3                  FALSE\n#define STM32_SPI_SPI1_DMA_PRIORITY         1\n#define STM32_SPI_SPI2_DMA_PRIORITY         1\n#define STM32_SPI_SPI3_DMA_PRIORITY         1\n#define STM32_SPI_SPI1_IRQ_PRIORITY         10\n#define STM32_SPI_SPI2_IRQ_PRIORITY         10\n#define STM32_SPI_SPI3_IRQ_PRIORITY         10\n#define STM32_SPI_DMA_ERROR_HOOK(spip)      osalSysHalt(\"DMA failure\")\n\n/*\n * ST driver system settings.\n */\n#define STM32_ST_IRQ_PRIORITY               8\n#define STM32_ST_USE_TIMER                  2\n\n/*\n * UART driver system settings.\n */\n#define STM32_UART_USE_USART1               FALSE\n#define STM32_UART_USE_USART2               FALSE\n#define STM32_UART_USE_USART3               FALSE\n#define STM32_UART_USART1_IRQ_PRIORITY      12\n#define STM32_UART_USART2_IRQ_PRIORITY      12\n#define STM32_UART_USART3_IRQ_PRIORITY      12\n#define STM32_UART_USART1_DMA_PRIORITY      0\n#define STM32_UART_USART2_DMA_PRIORITY      0\n#define STM32_UART_USART3_DMA_PRIORITY      0\n#define STM32_UART_DMA_ERROR_HOOK(uartp)    osalSysHalt(\"DMA failure\")\n\n/*\n * USB driver system settings.\n */\n#define STM32_USB_USE_USB1                  TRUE\n#define STM32_USB_LOW_POWER_ON_SUSPEND      FALSE\n#define STM32_USB_USB1_HP_IRQ_PRIORITY      13\n#define STM32_USB_USB1_LP_IRQ_PRIORITY      14\n\n#endif /* _MCUCONF_H_ */\n"
  },
  {
    "path": "platforms/chibios/boards/STM32_F103_STM32DUINO/ld/STM32F103x6_stm32duino.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32F103x6 memory setup for use with the STM32Duino bootloader.\n */\nf103_flash_size = 32k;\nf103_ram_size = 10k;\n\nINCLUDE stm32duino_bootloader_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/STM32_F103_STM32DUINO/ld/STM32F103x8_stm32duino.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32F103x8 memory setup for use with the STM32Duino bootloader.\n */\nf103_flash_size = 64k;\nf103_ram_size = 20k;\n\nINCLUDE stm32duino_bootloader_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/STM32_F103_STM32DUINO/ld/STM32F103xB_stm32duino.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32F103xB memory setup for use with the STM32Duino bootloader.\n */\nf103_flash_size = 128k;\nf103_ram_size = 20k;\n\nINCLUDE stm32duino_bootloader_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/STM32_F103_STM32DUINO/ld/stm32duino_bootloader_common.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32Duino bootloader common memory setup.\n */\nMEMORY\n{\n    flash0  : org = 0x08002000, len = f103_flash_size - 0x2000\n    flash1  : org = 0x00000000, len = 0\n    flash2  : org = 0x00000000, len = 0\n    flash3  : org = 0x00000000, len = 0\n    flash4  : org = 0x00000000, len = 0\n    flash5  : org = 0x00000000, len = 0\n    flash6  : org = 0x00000000, len = 0\n    flash7  : org = 0x00000000, len = 0\n    ram0    : org = 0x20000000, len = f103_ram_size\n    ram1    : org = 0x00000000, len = 0\n    ram2    : org = 0x00000000, len = 0\n    ram3    : org = 0x00000000, len = 0\n    ram4    : org = 0x00000000, len = 0\n    ram5    : org = 0x00000000, len = 0\n    ram6    : org = 0x00000000, len = 0\n    ram7    : org = 0x00000000, len = 0\n}\n\n/* For each data/text section two region are defined, a virtual region\n   and a load region (_LMA suffix).*/\n\n/* Flash region to be used for exception vectors.*/\nREGION_ALIAS(\"VECTORS_FLASH\", flash0);\nREGION_ALIAS(\"VECTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for constructors and destructors.*/\nREGION_ALIAS(\"XTORS_FLASH\", flash0);\nREGION_ALIAS(\"XTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for code text.*/\nREGION_ALIAS(\"TEXT_FLASH\", flash0);\nREGION_ALIAS(\"TEXT_FLASH_LMA\", flash0);\n\n/* Flash region to be used for read only data.*/\nREGION_ALIAS(\"RODATA_FLASH\", flash0);\nREGION_ALIAS(\"RODATA_FLASH_LMA\", flash0);\n\n/* Flash region to be used for various.*/\nREGION_ALIAS(\"VARIOUS_FLASH\", flash0);\nREGION_ALIAS(\"VARIOUS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for RAM(n) initialization data.*/\nREGION_ALIAS(\"RAM_INIT_FLASH_LMA\", flash0);\n\n/* RAM region to be used for Main stack. This stack accommodates the processing\n   of all exceptions and interrupts.*/\nREGION_ALIAS(\"MAIN_STACK_RAM\", ram0);\n\n/* RAM region to be used for the process stack. This is the stack used by\n   the main() function.*/\nREGION_ALIAS(\"PROCESS_STACK_RAM\", ram0);\n\n/* RAM region to be used for data segment.*/\nREGION_ALIAS(\"DATA_RAM\", ram0);\nREGION_ALIAS(\"DATA_RAM_LMA\", flash0);\n\n/* RAM region to be used for BSS segment.*/\nREGION_ALIAS(\"BSS_RAM\", ram0);\n\n/* RAM region to be used for the default heap.*/\nREGION_ALIAS(\"HEAP_RAM\", ram0);\n\n/* Generic rules inclusion.*/\nINCLUDE rules.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/configs/chconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/**\n * @file    rt/templates/chconf.h\n * @brief   Configuration file template.\n * @details A copy of this file must be placed in each project directory, it\n *          contains the application specific kernel settings.\n *\n * @addtogroup config\n * @details Kernel related settings and hooks.\n * @{\n */\n\n#ifndef CHCONF_H\n#define CHCONF_H\n\n#define _CHIBIOS_RT_CONF_\n#define _CHIBIOS_RT_CONF_VER_7_0_\n\n/*===========================================================================*/\n/**\n * @name System settings\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Handling of instances.\n * @note    If enabled then threads assigned to various instances can\n *          interact each other using the same synchronization objects.\n *          If disabled then each OS instance is a separate world, no\n *          direct interactions are handled by the OS.\n */\n#if !defined(CH_CFG_SMP_MODE)\n#define CH_CFG_SMP_MODE                     FALSE\n#endif\n\n/**\n * @brief   Kernel hardening level.\n * @details This option is the level of functional-safety checks enabled\n *          in the kerkel. The meaning is:\n *          - 0: No checks, maximum performance.\n *          - 1: Reasonable checks.\n *          - 2: All checks.\n *          .\n */\n#if !defined(CH_CFG_HARDENING_LEVEL)\n#define CH_CFG_HARDENING_LEVEL              0\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name System timers settings\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   System time counter resolution.\n * @note    Allowed values are 16, 32 or 64 bits.\n */\n#if !defined(CH_CFG_ST_RESOLUTION)\n#define CH_CFG_ST_RESOLUTION                32\n#endif\n\n/**\n * @brief   System tick frequency.\n * @details Frequency of the system timer that drives the system ticks. This\n *          setting also defines the system tick time unit.\n */\n#if !defined(CH_CFG_ST_FREQUENCY)\n#define CH_CFG_ST_FREQUENCY                 100000\n#endif\n\n/**\n * @brief   Time intervals data size.\n * @note    Allowed values are 16, 32 or 64 bits.\n */\n#if !defined(CH_CFG_INTERVALS_SIZE)\n#define CH_CFG_INTERVALS_SIZE               32\n#endif\n\n/**\n * @brief   Time types data size.\n * @note    Allowed values are 16 or 32 bits.\n */\n#if !defined(CH_CFG_TIME_TYPES_SIZE)\n#define CH_CFG_TIME_TYPES_SIZE              32\n#endif\n\n/**\n * @brief   Time delta constant for the tick-less mode.\n * @note    If this value is zero then the system uses the classic\n *          periodic tick. This value represents the minimum number\n *          of ticks that is safe to specify in a timeout directive.\n *          The value one is not valid, timeouts are rounded up to\n *          this value.\n */\n#if !defined(CH_CFG_ST_TIMEDELTA)\n#define CH_CFG_ST_TIMEDELTA                 2\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Kernel parameters and options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Round robin interval.\n * @details This constant is the number of system ticks allowed for the\n *          threads before preemption occurs. Setting this value to zero\n *          disables the preemption for threads with equal priority and the\n *          round robin becomes cooperative. Note that higher priority\n *          threads can still preempt, the kernel is always preemptive.\n * @note    Disabling the round robin preemption makes the kernel more compact\n *          and generally faster.\n * @note    The round robin preemption is not supported in tickless mode and\n *          must be set to zero in that case.\n */\n#if !defined(CH_CFG_TIME_QUANTUM)\n#define CH_CFG_TIME_QUANTUM                 0\n#endif\n\n/**\n * @brief   Idle thread automatic spawn suppression.\n * @details When this option is activated the function @p chSysInit()\n *          does not spawn the idle thread. The application @p main()\n *          function becomes the idle thread and must implement an\n *          infinite loop.\n */\n#if !defined(CH_CFG_NO_IDLE_THREAD)\n#define CH_CFG_NO_IDLE_THREAD               FALSE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Performance options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   OS optimization.\n * @details If enabled then time efficient rather than space efficient code\n *          is used when two possible implementations exist.\n *\n * @note    This is not related to the compiler optimization options.\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_OPTIMIZE_SPEED)\n#define CH_CFG_OPTIMIZE_SPEED               TRUE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Subsystem options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Time Measurement APIs.\n * @details If enabled then the time measurement APIs are included in\n *          the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_TM)\n#define CH_CFG_USE_TM                       FALSE\n#endif\n\n/**\n * @brief   Time Stamps APIs.\n * @details If enabled then the time stamps APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_TIMESTAMP)\n#define CH_CFG_USE_TIMESTAMP                TRUE\n#endif\n\n/**\n * @brief   Threads registry APIs.\n * @details If enabled then the registry APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_REGISTRY)\n#define CH_CFG_USE_REGISTRY                 FALSE\n#endif\n\n/**\n * @brief   Threads synchronization APIs.\n * @details If enabled then the @p chThdWait() function is included in\n *          the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_WAITEXIT)\n#define CH_CFG_USE_WAITEXIT                 FALSE\n#endif\n\n/**\n * @brief   Semaphores APIs.\n * @details If enabled then the Semaphores APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_SEMAPHORES)\n#define CH_CFG_USE_SEMAPHORES               TRUE\n#endif\n\n/**\n * @brief   Semaphores queuing mode.\n * @details If enabled then the threads are enqueued on semaphores by\n *          priority rather than in FIFO order.\n *\n * @note    The default is @p FALSE. Enable this if you have special\n *          requirements.\n * @note    Requires @p CH_CFG_USE_SEMAPHORES.\n */\n#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)\n#define CH_CFG_USE_SEMAPHORES_PRIORITY      FALSE\n#endif\n\n/**\n * @brief   Mutexes APIs.\n * @details If enabled then the mutexes APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_MUTEXES)\n#define CH_CFG_USE_MUTEXES                  TRUE\n#endif\n\n/**\n * @brief   Enables recursive behavior on mutexes.\n * @note    Recursive mutexes are heavier and have an increased\n *          memory footprint.\n *\n * @note    The default is @p FALSE.\n * @note    Requires @p CH_CFG_USE_MUTEXES.\n */\n#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)\n#define CH_CFG_USE_MUTEXES_RECURSIVE        FALSE\n#endif\n\n/**\n * @brief   Conditional Variables APIs.\n * @details If enabled then the conditional variables APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_MUTEXES.\n */\n#if !defined(CH_CFG_USE_CONDVARS)\n#define CH_CFG_USE_CONDVARS                 FALSE\n#endif\n\n/**\n * @brief   Conditional Variables APIs with timeout.\n * @details If enabled then the conditional variables APIs with timeout\n *          specification are included in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_CONDVARS.\n */\n#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)\n#define CH_CFG_USE_CONDVARS_TIMEOUT         TRUE\n#endif\n\n/**\n * @brief   Events Flags APIs.\n * @details If enabled then the event flags APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_EVENTS)\n#define CH_CFG_USE_EVENTS                   TRUE\n#endif\n\n/**\n * @brief   Events Flags APIs with timeout.\n * @details If enabled then the events APIs with timeout specification\n *          are included in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_EVENTS.\n */\n#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)\n#define CH_CFG_USE_EVENTS_TIMEOUT           TRUE\n#endif\n\n/**\n * @brief   Synchronous Messages APIs.\n * @details If enabled then the synchronous messages APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_MESSAGES)\n#define CH_CFG_USE_MESSAGES                 FALSE\n#endif\n\n/**\n * @brief   Synchronous Messages queuing mode.\n * @details If enabled then messages are served by priority rather than in\n *          FIFO order.\n *\n * @note    The default is @p FALSE. Enable this if you have special\n *          requirements.\n * @note    Requires @p CH_CFG_USE_MESSAGES.\n */\n#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)\n#define CH_CFG_USE_MESSAGES_PRIORITY        FALSE\n#endif\n\n/**\n * @brief   Dynamic Threads APIs.\n * @details If enabled then the dynamic threads creation APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_WAITEXIT.\n * @note    Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.\n */\n#if !defined(CH_CFG_USE_DYNAMIC)\n#define CH_CFG_USE_DYNAMIC                  FALSE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name OSLIB options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Mailboxes APIs.\n * @details If enabled then the asynchronous messages (mailboxes) APIs are\n *          included in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_SEMAPHORES.\n */\n#if !defined(CH_CFG_USE_MAILBOXES)\n#define CH_CFG_USE_MAILBOXES                FALSE\n#endif\n\n/**\n * @brief   Memory checks APIs.\n * @details If enabled then the memory checks APIs are included in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_MEMCHECKS)\n#define CH_CFG_USE_MEMCHECKS                TRUE\n#endif\n\n/**\n * @brief   Core Memory Manager APIs.\n * @details If enabled then the core memory manager APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_MEMCORE)\n#define CH_CFG_USE_MEMCORE                  TRUE\n#endif\n\n/**\n * @brief   Managed RAM size.\n * @details Size of the RAM area to be managed by the OS. If set to zero\n *          then the whole available RAM is used. The core memory is made\n *          available to the heap allocator and/or can be used directly through\n *          the simplified core memory allocator.\n *\n * @note    In order to let the OS manage the whole RAM the linker script must\n *          provide the @p __heap_base__ and @p __heap_end__ symbols.\n * @note    Requires @p CH_CFG_USE_MEMCORE.\n */\n#if !defined(CH_CFG_MEMCORE_SIZE)\n#define CH_CFG_MEMCORE_SIZE                 0\n#endif\n\n/**\n * @brief   Heap Allocator APIs.\n * @details If enabled then the memory heap allocator APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n * @note    Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or\n *          @p CH_CFG_USE_SEMAPHORES.\n * @note    Mutexes are recommended.\n */\n#if !defined(CH_CFG_USE_HEAP)\n#define CH_CFG_USE_HEAP                     FALSE\n#endif\n\n/**\n * @brief   Memory Pools Allocator APIs.\n * @details If enabled then the memory pools allocator APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_MEMPOOLS)\n#define CH_CFG_USE_MEMPOOLS                 FALSE\n#endif\n\n/**\n * @brief   Objects FIFOs APIs.\n * @details If enabled then the objects FIFOs APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_OBJ_FIFOS)\n#define CH_CFG_USE_OBJ_FIFOS                FALSE\n#endif\n\n/**\n * @brief   Pipes APIs.\n * @details If enabled then the pipes APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_PIPES)\n#define CH_CFG_USE_PIPES                    FALSE\n#endif\n\n/**\n * @brief   Objects Caches APIs.\n * @details If enabled then the objects caches APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_OBJ_CACHES)\n#define CH_CFG_USE_OBJ_CACHES               FALSE\n#endif\n\n/**\n * @brief   Delegate threads APIs.\n * @details If enabled then the delegate threads APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_DELEGATES)\n#define CH_CFG_USE_DELEGATES                FALSE\n#endif\n\n/**\n * @brief   Jobs Queues APIs.\n * @details If enabled then the jobs queues APIs are included\n *          in the kernel.\n *\n * @note    The default is @p TRUE.\n */\n#if !defined(CH_CFG_USE_JOBS)\n#define CH_CFG_USE_JOBS                     FALSE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Objects factory options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Objects Factory APIs.\n * @details If enabled then the objects factory APIs are included in the\n *          kernel.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_CFG_USE_FACTORY)\n#define CH_CFG_USE_FACTORY                  FALSE\n#endif\n\n/**\n * @brief   Maximum length for object names.\n * @details If the specified length is zero then the name is stored by\n *          pointer but this could have unintended side effects.\n */\n#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)\n#define CH_CFG_FACTORY_MAX_NAMES_LENGTH     8\n#endif\n\n/**\n * @brief   Enables the registry of generic objects.\n */\n#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)\n#define CH_CFG_FACTORY_OBJECTS_REGISTRY     FALSE\n#endif\n\n/**\n * @brief   Enables factory for generic buffers.\n */\n#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)\n#define CH_CFG_FACTORY_GENERIC_BUFFERS      FALSE\n#endif\n\n/**\n * @brief   Enables factory for semaphores.\n */\n#if !defined(CH_CFG_FACTORY_SEMAPHORES)\n#define CH_CFG_FACTORY_SEMAPHORES           FALSE\n#endif\n\n/**\n * @brief   Enables factory for mailboxes.\n */\n#if !defined(CH_CFG_FACTORY_MAILBOXES)\n#define CH_CFG_FACTORY_MAILBOXES            FALSE\n#endif\n\n/**\n * @brief   Enables factory for objects FIFOs.\n */\n#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)\n#define CH_CFG_FACTORY_OBJ_FIFOS            FALSE\n#endif\n\n/**\n * @brief   Enables factory for Pipes.\n */\n#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)\n#define CH_CFG_FACTORY_PIPES                FALSE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Debug options\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   Debug option, kernel statistics.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_DBG_STATISTICS)\n#define CH_DBG_STATISTICS                   FALSE\n#endif\n\n/**\n * @brief   Debug option, system state check.\n * @details If enabled the correct call protocol for system APIs is checked\n *          at runtime.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_DBG_SYSTEM_STATE_CHECK)\n#define CH_DBG_SYSTEM_STATE_CHECK           FALSE\n#endif\n\n/**\n * @brief   Debug option, parameters checks.\n * @details If enabled then the checks on the API functions input\n *          parameters are activated.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_DBG_ENABLE_CHECKS)\n#define CH_DBG_ENABLE_CHECKS                FALSE\n#endif\n\n/**\n * @brief   Debug option, consistency checks.\n * @details If enabled then all the assertions in the kernel code are\n *          activated. This includes consistency checks inside the kernel,\n *          runtime anomalies and port-defined checks.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_DBG_ENABLE_ASSERTS)\n#define CH_DBG_ENABLE_ASSERTS               FALSE\n#endif\n\n/**\n * @brief   Debug option, trace buffer.\n * @details If enabled then the trace buffer is activated.\n *\n * @note    The default is @p CH_DBG_TRACE_MASK_DISABLED.\n */\n#if !defined(CH_DBG_TRACE_MASK)\n#define CH_DBG_TRACE_MASK                   CH_DBG_TRACE_MASK_DISABLED\n#endif\n\n/**\n * @brief   Trace buffer entries.\n * @note    The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is\n *          different from @p CH_DBG_TRACE_MASK_DISABLED.\n */\n#if !defined(CH_DBG_TRACE_BUFFER_SIZE)\n#define CH_DBG_TRACE_BUFFER_SIZE            128\n#endif\n\n/**\n * @brief   Debug option, stack checks.\n * @details If enabled then a runtime stack check is performed.\n *\n * @note    The default is @p FALSE.\n * @note    The stack check is performed in a architecture/port dependent way.\n *          It may not be implemented or some ports.\n * @note    The default failure mode is to halt the system with the global\n *          @p panic_msg variable set to @p NULL.\n */\n#if !defined(CH_DBG_ENABLE_STACK_CHECK)\n#define CH_DBG_ENABLE_STACK_CHECK           FALSE\n#endif\n\n/**\n * @brief   Debug option, stacks initialization.\n * @details If enabled then the threads working area is filled with a byte\n *          value when a thread is created. This can be useful for the\n *          runtime measurement of the used stack.\n *\n * @note    The default is @p FALSE.\n */\n#if !defined(CH_DBG_FILL_THREADS)\n#define CH_DBG_FILL_THREADS                 FALSE\n#endif\n\n/**\n * @brief   Debug option, threads profiling.\n * @details If enabled then a field is added to the @p thread_t structure that\n *          counts the system ticks occurred while executing the thread.\n *\n * @note    The default is @p FALSE.\n * @note    This debug option is not currently compatible with the\n *          tickless mode.\n */\n#if !defined(CH_DBG_THREADS_PROFILING)\n#define CH_DBG_THREADS_PROFILING            FALSE\n#endif\n\n/** @} */\n\n/*===========================================================================*/\n/**\n * @name Kernel hooks\n * @{\n */\n/*===========================================================================*/\n\n/**\n * @brief   System structure extension.\n * @details User fields added to the end of the @p ch_system_t structure.\n */\n#define CH_CFG_SYSTEM_EXTRA_FIELDS                                          \\\n  /* Add system custom fields here.*/\n\n/**\n * @brief   System initialization hook.\n * @details User initialization code added to the @p chSysInit() function\n *          just before interrupts are enabled globally.\n */\n#define CH_CFG_SYSTEM_INIT_HOOK() {                                         \\\n  /* Add system initialization code here.*/                                 \\\n}\n\n/**\n * @brief   OS instance structure extension.\n * @details User fields added to the end of the @p os_instance_t structure.\n */\n#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS                                     \\\n  /* Add OS instance custom fields here.*/\n\n/**\n * @brief   OS instance initialization hook.\n *\n * @param[in] oip       pointer to the @p os_instance_t structure\n */\n#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) {                                 \\\n  /* Add OS instance initialization code here.*/                            \\\n}\n\n/**\n * @brief   Threads descriptor structure extension.\n * @details User fields added to the end of the @p thread_t structure.\n */\n#define CH_CFG_THREAD_EXTRA_FIELDS                                          \\\n  /* Add threads custom fields here.*/\n\n/**\n * @brief   Threads initialization hook.\n * @details User initialization code added to the @p _thread_init() function.\n *\n * @note    It is invoked from within @p _thread_init() and implicitly from all\n *          the threads creation APIs.\n *\n * @param[in] tp        pointer to the @p thread_t structure\n */\n#define CH_CFG_THREAD_INIT_HOOK(tp) {                                       \\\n  /* Add threads initialization code here.*/                                \\\n}\n\n/**\n * @brief   Threads finalization hook.\n * @details User finalization code added to the @p chThdExit() API.\n *\n * @param[in] tp        pointer to the @p thread_t structure\n */\n#define CH_CFG_THREAD_EXIT_HOOK(tp) {                                       \\\n  /* Add threads finalization code here.*/                                  \\\n}\n\n/**\n * @brief   Context switch hook.\n * @details This hook is invoked just before switching between threads.\n *\n * @param[in] ntp       thread being switched in\n * @param[in] otp       thread being switched out\n */\n#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) {                              \\\n  /* Context switch code here.*/                                            \\\n}\n\n/**\n * @brief   ISR enter hook.\n */\n#define CH_CFG_IRQ_PROLOGUE_HOOK() {                                        \\\n  /* IRQ prologue code here.*/                                              \\\n}\n\n/**\n * @brief   ISR exit hook.\n */\n#define CH_CFG_IRQ_EPILOGUE_HOOK() {                                        \\\n  /* IRQ epilogue code here.*/                                              \\\n}\n\n/**\n * @brief   Idle thread enter hook.\n * @note    This hook is invoked within a critical zone, no OS functions\n *          should be invoked from here.\n * @note    This macro can be used to activate a power saving mode.\n */\n#define CH_CFG_IDLE_ENTER_HOOK() {                                          \\\n  /* Idle-enter code here.*/                                                \\\n}\n\n/**\n * @brief   Idle thread leave hook.\n * @note    This hook is invoked within a critical zone, no OS functions\n *          should be invoked from here.\n * @note    This macro can be used to deactivate a power saving mode.\n */\n#define CH_CFG_IDLE_LEAVE_HOOK() {                                          \\\n  /* Idle-leave code here.*/                                                \\\n}\n\n/**\n * @brief   Idle Loop hook.\n * @details This hook is continuously invoked by the idle thread loop.\n */\n#define CH_CFG_IDLE_LOOP_HOOK() {                                           \\\n  /* Idle loop code here.*/                                                 \\\n}\n\n/**\n * @brief   System tick event hook.\n * @details This hook is invoked in the system tick handler immediately\n *          after processing the virtual timers queue.\n */\n#define CH_CFG_SYSTEM_TICK_HOOK() {                                         \\\n  /* System tick event code here.*/                                         \\\n}\n\n/**\n * @brief   System halt hook.\n * @details This hook is invoked in case to a system halting error before\n *          the system is halted.\n */\n#define CH_CFG_SYSTEM_HALT_HOOK(reason) {                                   \\\n  /* System halt code here.*/                                               \\\n}\n\n/**\n * @brief   Trace hook.\n * @details This hook is invoked each time a new record is written in the\n *          trace buffer.\n */\n#define CH_CFG_TRACE_HOOK(tep) {                                            \\\n  /* Trace code here.*/                                                     \\\n}\n\n/**\n * @brief   Runtime Faults Collection Unit hook.\n * @details This hook is invoked each time new faults are collected and stored.\n */\n#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) {                                  \\\n  /* Faults handling code here.*/                                           \\\n}\n\n/** @} */\n\n/*===========================================================================*/\n/* Port-specific settings (override port settings defaulted in chcore.h).    */\n/*===========================================================================*/\n\n#endif  /* CHCONF_H */\n\n/** @} */\n"
  },
  {
    "path": "platforms/chibios/boards/common/configs/halconf.h",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2020 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/**\n * @file    templates/halconf.h\n * @brief   HAL configuration header.\n * @details HAL configuration file, this file allows to enable or disable the\n *          various device drivers from your application. You may also use\n *          this file in order to override the device drivers default settings.\n *\n * @addtogroup HAL_CONF\n * @{\n */\n\n#ifndef HALCONF_H\n#define HALCONF_H\n\n#define _CHIBIOS_HAL_CONF_\n#define _CHIBIOS_HAL_CONF_VER_8_4_\n\n#include <mcuconf.h>\n\n/**\n * @brief   Enables the PAL subsystem.\n */\n#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)\n#define HAL_USE_PAL                         TRUE\n#endif\n\n/**\n * @brief   Enables the ADC subsystem.\n */\n#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)\n#define HAL_USE_ADC                         FALSE\n#endif\n\n/**\n * @brief   Enables the CAN subsystem.\n */\n#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)\n#define HAL_USE_CAN                         FALSE\n#endif\n\n/**\n * @brief   Enables the cryptographic subsystem.\n */\n#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)\n#define HAL_USE_CRY                         FALSE\n#endif\n\n/**\n * @brief   Enables the DAC subsystem.\n */\n#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)\n#define HAL_USE_DAC                         FALSE\n#endif\n\n/**\n * @brief   Enables the EFlash subsystem.\n */\n#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)\n#define HAL_USE_EFL                         FALSE\n#endif\n\n/**\n * @brief   Enables the GPT subsystem.\n */\n#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)\n#define HAL_USE_GPT                         FALSE\n#endif\n\n/**\n * @brief   Enables the I2C subsystem.\n */\n#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)\n#define HAL_USE_I2C                         FALSE\n#endif\n\n/**\n * @brief   Enables the I2S subsystem.\n */\n#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)\n#define HAL_USE_I2S                         FALSE\n#endif\n\n/**\n * @brief   Enables the ICU subsystem.\n */\n#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)\n#define HAL_USE_ICU                         FALSE\n#endif\n\n/**\n * @brief   Enables the MAC subsystem.\n */\n#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)\n#define HAL_USE_MAC                         FALSE\n#endif\n\n/**\n * @brief   Enables the MMC_SPI subsystem.\n */\n#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)\n#define HAL_USE_MMC_SPI                     FALSE\n#endif\n\n/**\n * @brief   Enables the PWM subsystem.\n */\n#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)\n#define HAL_USE_PWM                         FALSE\n#endif\n\n/**\n * @brief   Enables the RTC subsystem.\n */\n#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)\n#define HAL_USE_RTC                         FALSE\n#endif\n\n/**\n * @brief   Enables the SDC subsystem.\n */\n#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)\n#define HAL_USE_SDC                         FALSE\n#endif\n\n/**\n * @brief   Enables the SERIAL subsystem.\n */\n#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)\n#define HAL_USE_SERIAL                      FALSE\n#endif\n\n/**\n * @brief   Enables the SERIAL over USB subsystem.\n */\n#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)\n#define HAL_USE_SERIAL_USB                  FALSE\n#endif\n\n/**\n * @brief   Enables the SIO subsystem.\n */\n#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)\n#define HAL_USE_SIO                         FALSE\n#endif\n\n/**\n * @brief   Enables the SPI subsystem.\n */\n#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)\n#define HAL_USE_SPI                         FALSE\n#endif\n\n/**\n * @brief   Enables the TRNG subsystem.\n */\n#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)\n#define HAL_USE_TRNG                        FALSE\n#endif\n\n/**\n * @brief   Enables the UART subsystem.\n */\n#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)\n#define HAL_USE_UART                        FALSE\n#endif\n\n/**\n * @brief   Enables the USB subsystem.\n */\n#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)\n#define HAL_USE_USB                         TRUE\n#endif\n\n/**\n * @brief   Enables the WDG subsystem.\n */\n#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)\n#define HAL_USE_WDG                         FALSE\n#endif\n\n/**\n * @brief   Enables the WSPI subsystem.\n */\n#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)\n#define HAL_USE_WSPI                        FALSE\n#endif\n\n/*===========================================================================*/\n/* PAL driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)\n#define PAL_USE_CALLBACKS                   FALSE\n#endif\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)\n#define PAL_USE_WAIT                        FALSE\n#endif\n\n/*===========================================================================*/\n/* ADC driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)\n#define ADC_USE_WAIT                        TRUE\n#endif\n\n/**\n * @brief   Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define ADC_USE_MUTUAL_EXCLUSION            TRUE\n#endif\n\n/*===========================================================================*/\n/* CAN driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Sleep mode related APIs inclusion switch.\n */\n#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)\n#define CAN_USE_SLEEP_MODE                  TRUE\n#endif\n\n/**\n * @brief   Enforces the driver to use direct callbacks rather than OSAL events.\n */\n#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)\n#define CAN_ENFORCE_USE_CALLBACKS           FALSE\n#endif\n\n/*===========================================================================*/\n/* CRY driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables the SW fall-back of the cryptographic driver.\n * @details When enabled, this option, activates a fall-back software\n *          implementation for algorithms not supported by the underlying\n *          hardware.\n * @note    Fall-back implementations may not be present for all algorithms.\n */\n#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)\n#define HAL_CRY_USE_FALLBACK                FALSE\n#endif\n\n/**\n * @brief   Makes the driver forcibly use the fall-back implementations.\n */\n#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)\n#define HAL_CRY_ENFORCE_FALLBACK            FALSE\n#endif\n\n/*===========================================================================*/\n/* DAC driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)\n#define DAC_USE_WAIT                        TRUE\n#endif\n\n/**\n * @brief   Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define DAC_USE_MUTUAL_EXCLUSION            TRUE\n#endif\n\n/*===========================================================================*/\n/* I2C driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables the mutual exclusion APIs on the I2C bus.\n */\n#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define I2C_USE_MUTUAL_EXCLUSION            TRUE\n#endif\n\n/*===========================================================================*/\n/* MAC driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables the zero-copy API.\n */\n#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)\n#define MAC_USE_ZERO_COPY                   FALSE\n#endif\n\n/**\n * @brief   Enables an event sources for incoming packets.\n */\n#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)\n#define MAC_USE_EVENTS                      TRUE\n#endif\n\n/*===========================================================================*/\n/* MMC_SPI driver related settings.                                          */\n/*===========================================================================*/\n\n/**\n * @brief   Timeout before assuming a failure while waiting for card idle.\n * @note    Time is in milliseconds.\n */\n#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)\n#define MMC_IDLE_TIMEOUT_MS                 1000\n#endif\n\n/**\n * @brief   Mutual exclusion on the SPI bus.\n */\n#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define MMC_USE_MUTUAL_EXCLUSION            TRUE\n#endif\n\n/*===========================================================================*/\n/* SDC driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Number of initialization attempts before rejecting the card.\n * @note    Attempts are performed at 10mS intervals.\n */\n#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)\n#define SDC_INIT_RETRY                      100\n#endif\n\n/**\n * @brief   Include support for MMC cards.\n * @note    MMC support is not yet implemented so this option must be kept\n *          at @p FALSE.\n */\n#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)\n#define SDC_MMC_SUPPORT                     FALSE\n#endif\n\n/**\n * @brief   Delays insertions.\n * @details If enabled this options inserts delays into the MMC waiting\n *          routines releasing some extra CPU time for the threads with\n *          lower priority, this may slow down the driver a bit however.\n */\n#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)\n#define SDC_NICE_WAITING                    TRUE\n#endif\n\n/**\n * @brief   OCR initialization constant for V20 cards.\n */\n#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)\n#define SDC_INIT_OCR_V20                    0x50FF8000U\n#endif\n\n/**\n * @brief   OCR initialization constant for non-V20 cards.\n */\n#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)\n#define SDC_INIT_OCR                        0x80100000U\n#endif\n\n/*===========================================================================*/\n/* SERIAL driver related settings.                                           */\n/*===========================================================================*/\n\n/**\n * @brief   Default bit rate.\n * @details Configuration parameter, this is the baud rate selected for the\n *          default configuration.\n */\n#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)\n#define SERIAL_DEFAULT_BITRATE              38400\n#endif\n\n/**\n * @brief   Serial buffers size.\n * @details Configuration parameter, you can change the depth of the queue\n *          buffers depending on the requirements of your application.\n * @note    The default is 16 bytes for both the transmission and receive\n *          buffers.\n */\n#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)\n#define SERIAL_BUFFERS_SIZE                 128\n#endif\n\n/*===========================================================================*/\n/* SIO driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Default bit rate.\n * @details Configuration parameter, this is the baud rate selected for the\n *          default configuration.\n */\n#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)\n#define SIO_DEFAULT_BITRATE                 38400\n#endif\n\n/**\n * @brief   Support for thread synchronization API.\n */\n#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)\n#define SIO_USE_SYNCHRONIZATION             TRUE\n#endif\n\n/*===========================================================================*/\n/* SERIAL_USB driver related setting.                                        */\n/*===========================================================================*/\n\n/**\n * @brief   Serial over USB buffers size.\n * @details Configuration parameter, the buffer size must be a multiple of\n *          the USB data endpoint maximum packet size.\n * @note    The default is 256 bytes for both the transmission and receive\n *          buffers.\n */\n#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)\n#define SERIAL_USB_BUFFERS_SIZE             1\n#endif\n\n/**\n * @brief   Serial over USB number of buffers.\n * @note    The default is 2 buffers.\n */\n#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)\n#define SERIAL_USB_BUFFERS_NUMBER           2\n#endif\n\n/*===========================================================================*/\n/* SPI driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)\n#define SPI_USE_WAIT                        TRUE\n#endif\n\n/**\n * @brief   Inserts an assertion on function errors before returning.\n */\n#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)\n#define SPI_USE_ASSERT_ON_ERROR             TRUE\n#endif\n\n/**\n * @brief   Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define SPI_USE_MUTUAL_EXCLUSION            TRUE\n#endif\n\n/**\n * @brief   Handling method for SPI CS line.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)\n#define SPI_SELECT_MODE                     SPI_SELECT_MODE_PAD\n#endif\n\n/*===========================================================================*/\n/* UART driver related settings.                                             */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)\n#define UART_USE_WAIT                       FALSE\n#endif\n\n/**\n * @brief   Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define UART_USE_MUTUAL_EXCLUSION           FALSE\n#endif\n\n/*===========================================================================*/\n/* USB driver related settings.                                              */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)\n#define USB_USE_WAIT                        TRUE\n#endif\n\n/*===========================================================================*/\n/* WSPI driver related settings.                                             */\n/*===========================================================================*/\n\n/**\n * @brief   Enables synchronous APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)\n#define WSPI_USE_WAIT                       TRUE\n#endif\n\n/**\n * @brief   Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.\n * @note    Disabling this option saves both code and data space.\n */\n#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)\n#define WSPI_USE_MUTUAL_EXCLUSION           TRUE\n#endif\n\n#endif /* HALCONF_H */\n\n/** @} */\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/MKL26Z64.ld",
    "content": "/*\n * Copyright (C) 2013-2016 Fabio Utzig, http://fabioutzig.com\n *           (C) 2016 flabbergast <s3+flabbergast@sdfeu.org>\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the \"Software\"),\n * to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense,\n * and/or sell copies of the Software, and to permit persons to whom the\n * Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n/*\n * KL26Z64 memory setup.\n */\nMEMORY\n{\n  flash0   : org = 0x00000000, len = 0x100\n  flash1   : org = 0x00000400, len = 0x10\n  flash2   : org = 0x00000410, len = 62k - 0x410\n  flash3   : org = 0x0000F800, len = 2k\n  flash4   : org = 0x00000000, len = 0\n  flash5   : org = 0x00000000, len = 0\n  flash6   : org = 0x00000000, len = 0\n  flash7   : org = 0x00000000, len = 0\n  ram0     : org = 0x1FFFF800, len = 8k\n  ram1     : org = 0x00000000, len = 0\n  ram2     : org = 0x00000000, len = 0\n  ram3     : org = 0x00000000, len = 0\n  ram4     : org = 0x00000000, len = 0\n  ram5     : org = 0x00000000, len = 0\n  ram6     : org = 0x00000000, len = 0\n  ram7     : org = 0x00000000, len = 0\n}\n\n/* Flash region for the configuration bytes.*/\nSECTIONS\n{\n  .cfmprotect : ALIGN(4) SUBALIGN(4)\n  {\n    KEEP(*(.cfmconfig))\n  } > flash1\n}\n\n/* For each data/text section two region are defined, a virtual region\n   and a load region (_LMA suffix).*/\n\n/* Flash region to be used for exception vectors.*/\nREGION_ALIAS(\"VECTORS_FLASH\", flash0);\nREGION_ALIAS(\"VECTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for constructors and destructors.*/\nREGION_ALIAS(\"XTORS_FLASH\", flash2);\nREGION_ALIAS(\"XTORS_FLASH_LMA\", flash2);\n\n/* Flash region to be used for code text.*/\nREGION_ALIAS(\"TEXT_FLASH\", flash2);\nREGION_ALIAS(\"TEXT_FLASH_LMA\", flash2);\n\n/* Flash region to be used for read only data.*/\nREGION_ALIAS(\"RODATA_FLASH\", flash2);\nREGION_ALIAS(\"RODATA_FLASH_LMA\", flash2);\n\n/* Flash region to be used for various.*/\nREGION_ALIAS(\"VARIOUS_FLASH\", flash2);\nREGION_ALIAS(\"VARIOUS_FLASH_LMA\", flash2);\n\n/* Flash region to be used for RAM(n) initialization data.*/\nREGION_ALIAS(\"RAM_INIT_FLASH_LMA\", flash2);\n\n/* RAM region to be used for Main stack. This stack accommodates the processing\n   of all exceptions and interrupts.*/\nREGION_ALIAS(\"MAIN_STACK_RAM\", ram0);\n\n/* RAM region to be used for the process stack. This is the stack used by\n   the main() function.*/\nREGION_ALIAS(\"PROCESS_STACK_RAM\", ram0);\n\n/* RAM region to be used for data segment.*/\nREGION_ALIAS(\"DATA_RAM\", ram0);\nREGION_ALIAS(\"DATA_RAM_LMA\", flash2);\n\n/* RAM region to be used for BSS segment.*/\nREGION_ALIAS(\"BSS_RAM\", ram0);\n\n/* RAM region to be used for the default heap.*/\nREGION_ALIAS(\"HEAP_RAM\", ram0);\n\n__eeprom_workarea_start__ = ORIGIN(flash3);\n__eeprom_workarea_size__  = LENGTH(flash3);\n__eeprom_workarea_end__   = __eeprom_workarea_start__ + __eeprom_workarea_size__;\n\n/* Generic rules inclusion.*/\nINCLUDE rules.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/RP2040_FLASH_TIMECRIT.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2021 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * RP2040 memory setup.\n */\nMEMORY\n{\n    flash0 (rx) : org = 0x00000000, len = 16k   /* ROM                  */\n    flash1 (rx) : org = 0x10000000, len = DEFINED(FLASH_LEN) ? FLASH_LEN : 2048k /* XIP */\n    flash2 (rx) : org = 0x00000000, len = 0\n    flash3 (rx) : org = 0x00000000, len = 0\n    flash4 (rx) : org = 0x00000000, len = 0\n    flash5 (rx) : org = 0x00000000, len = 0\n    flash6 (rx) : org = 0x00000000, len = 0\n    flash7 (rx) : org = 0x00000000, len = 0\n    ram0   (wx) : org = 0x20000000, len = 256k  /* SRAM0 striped        */\n    ram1   (wx) : org = 0x00000000, len = 256k  /* SRAM0 non striped    */\n    ram2   (wx) : org = 0x00000000, len = 0\n    ram3   (wx) : org = 0x00000000, len = 0\n    ram4   (wx) : org = 0x20040000, len = 4k    /* SRAM4                */\n    ram5   (wx) : org = 0x20041000, len = 4k    /* SRAM5                */\n    ram6   (wx) : org = 0x00000000, len = 0\n    ram7   (wx) : org = 0x20041f00, len = 256   /* SRAM5 boot           */\n}\n\n/* For each data/text section two region are defined, a virtual region\n   and a load region (_LMA suffix).*/\n\n/* Flash region to be used for exception vectors.*/\nREGION_ALIAS(\"VECTORS_FLASH\", flash1);\nREGION_ALIAS(\"VECTORS_FLASH_LMA\", flash1);\n\n/* Flash region to be used for constructors and destructors.*/\nREGION_ALIAS(\"XTORS_FLASH\", flash1);\nREGION_ALIAS(\"XTORS_FLASH_LMA\", flash1);\n\n/* Flash region to be used for code text.*/\nREGION_ALIAS(\"TEXT_FLASH\", flash1);\nREGION_ALIAS(\"TEXT_FLASH_LMA\", flash1);\n\n/* Flash region to be used for read only data.*/\nREGION_ALIAS(\"RODATA_FLASH\", flash1);\nREGION_ALIAS(\"RODATA_FLASH_LMA\", flash1);\n\n/* Flash region to be used for various.*/\nREGION_ALIAS(\"VARIOUS_FLASH\", flash1);\nREGION_ALIAS(\"VARIOUS_FLASH_LMA\", flash1);\n\n/* Flash region to be used for RAM(n) initialization data.*/\nREGION_ALIAS(\"RAM_INIT_FLASH_LMA\", flash1);\n\n/* RAM region to be used for Main stack. This stack accommodates the processing\n   of all exceptions and interrupts.*/\nREGION_ALIAS(\"MAIN_STACK_RAM\", ram4);\n\n/* RAM region to be used for the process stack. This is the stack used by\n   the main() function.*/\nREGION_ALIAS(\"PROCESS_STACK_RAM\", ram4);\n\n/* RAM region to be used for Main stack. This stack accommodates the processing\n   of all exceptions and interrupts.*/\nREGION_ALIAS(\"C1_MAIN_STACK_RAM\", ram5);\n\n/* RAM region to be used for the process stack. This is the stack used by\n   the main() function.*/\nREGION_ALIAS(\"C1_PROCESS_STACK_RAM\", ram5);\n\n/* RAM region to be used for data segment.*/\nREGION_ALIAS(\"DATA_RAM\", ram0);\nREGION_ALIAS(\"DATA_RAM_LMA\", flash1);\n\n/* RAM region to be used for BSS segment.*/\nREGION_ALIAS(\"BSS_RAM\", ram0);\n\n/* RAM region to be used for the default heap.*/\nREGION_ALIAS(\"HEAP_RAM\", ram0);\n\nSECTIONS\n{\n   .flash_begin : {\n      __flash_binary_start = .;\n   } > flash1\n\n   .boot2 : {\n      __boot2_start__ = .;\n      KEEP (*(.boot2))\n      __boot2_end__ = .;\n   } > flash1\n}\n\n/* Generic rules inclusion.*/\nINCLUDE rules_stacks.ld\nINCLUDE rules_stacks_c1.ld\nINCLUDE RP2040_rules_code_with_boot2.ld\nINCLUDE RP2040_rules_data_with_timecrit.ld\nINCLUDE rules_memory.ld\n\nSECTIONS\n{\n   .flash_end : {\n      __flash_binary_end = .;\n   } > flash1\n}\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/RP2040_rules_data_with_timecrit.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\nSECTIONS\n{\n    .data : ALIGN(4)\n    {\n        PROVIDE(_textdata = LOADADDR(.data));\n        PROVIDE(_data = .);\n        __textdata_base__ = LOADADDR(.data);\n        __data_base__ = .;\n        *(vtable)\n        *(.time_critical*)\n        . = ALIGN(4);\n        *(.data)\n        *(.data.*)\n        *(.ramtext)\n        . = ALIGN(4);\n        PROVIDE(_edata = .);\n        __data_end__ = .;\n    } > DATA_RAM AT > DATA_RAM_LMA\n\n    .bss (NOLOAD) : ALIGN(4)\n    {\n        __bss_base__ = .;\n        *(.bss)\n        *(.bss.*)\n        *(COMMON)\n        . = ALIGN(4);\n        __bss_end__ = .;\n        PROVIDE(end = .);\n    } > BSS_RAM\n}\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F103x8_uf2boot.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * ST32F103x8 memory setup.\n */\nMEMORY\n{\n    flash0 (rx) : org = 0x08000000 + 16K, len = 64k - 16K\n    flash1 (rx) : org = 0x00000000, len = 0\n    flash2 (rx) : org = 0x00000000, len = 0\n    flash3 (rx) : org = 0x00000000, len = 0\n    flash4 (rx) : org = 0x00000000, len = 0\n    flash5 (rx) : org = 0x00000000, len = 0\n    flash6 (rx) : org = 0x00000000, len = 0\n    flash7 (rx) : org = 0x00000000, len = 0\n    ram0   (wx) : org = 0x20000000, len = 20k\n    ram1   (wx) : org = 0x00000000, len = 0\n    ram2   (wx) : org = 0x00000000, len = 0\n    ram3   (wx) : org = 0x00000000, len = 0\n    ram4   (wx) : org = 0x00000000, len = 0\n    ram5   (wx) : org = 0x00000000, len = 0\n    ram6   (wx) : org = 0x00000000, len = 0\n    ram7   (wx) : org = 0x00000000, len = 0\n}\n\n/* For each data/text section two region are defined, a virtual region\n   and a load region (_LMA suffix).*/\n\n/* Flash region to be used for exception vectors.*/\nREGION_ALIAS(\"VECTORS_FLASH\", flash0);\nREGION_ALIAS(\"VECTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for constructors and destructors.*/\nREGION_ALIAS(\"XTORS_FLASH\", flash0);\nREGION_ALIAS(\"XTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for code text.*/\nREGION_ALIAS(\"TEXT_FLASH\", flash0);\nREGION_ALIAS(\"TEXT_FLASH_LMA\", flash0);\n\n/* Flash region to be used for read only data.*/\nREGION_ALIAS(\"RODATA_FLASH\", flash0);\nREGION_ALIAS(\"RODATA_FLASH_LMA\", flash0);\n\n/* Flash region to be used for various.*/\nREGION_ALIAS(\"VARIOUS_FLASH\", flash0);\nREGION_ALIAS(\"VARIOUS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for RAM(n) initialization data.*/\nREGION_ALIAS(\"RAM_INIT_FLASH_LMA\", flash0);\n\n/* RAM region to be used for Main stack. This stack accommodates the processing\n   of all exceptions and interrupts.*/\nREGION_ALIAS(\"MAIN_STACK_RAM\", ram0);\n\n/* RAM region to be used for the process stack. This is the stack used by\n   the main() function.*/\nREGION_ALIAS(\"PROCESS_STACK_RAM\", ram0);\n\n/* RAM region to be used for data segment.*/\nREGION_ALIAS(\"DATA_RAM\", ram0);\nREGION_ALIAS(\"DATA_RAM_LMA\", flash0);\n\n/* RAM region to be used for BSS segment.*/\nREGION_ALIAS(\"BSS_RAM\", ram0);\n\n/* RAM region to be used for the default heap.*/\nREGION_ALIAS(\"HEAP_RAM\", ram0);\n\n/* Generic rules inclusion.*/\nINCLUDE rules.ld\n\n/* Bootloader reset support */\n_board_magic_reg = ORIGIN(ram0) + 16k; /* this is based off the code within backup.c */\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F103xB_uf2boot.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * ST32F103xB memory setup.\n */\nMEMORY\n{\n    flash0 (rx) : org = 0x08000000 + 16K, len = 128k - 16K\n    flash1 (rx) : org = 0x00000000, len = 0\n    flash2 (rx) : org = 0x00000000, len = 0\n    flash3 (rx) : org = 0x00000000, len = 0\n    flash4 (rx) : org = 0x00000000, len = 0\n    flash5 (rx) : org = 0x00000000, len = 0\n    flash6 (rx) : org = 0x00000000, len = 0\n    flash7 (rx) : org = 0x00000000, len = 0\n    ram0   (wx) : org = 0x20000000, len = 20k\n    ram1   (wx) : org = 0x00000000, len = 0\n    ram2   (wx) : org = 0x00000000, len = 0\n    ram3   (wx) : org = 0x00000000, len = 0\n    ram4   (wx) : org = 0x00000000, len = 0\n    ram5   (wx) : org = 0x00000000, len = 0\n    ram6   (wx) : org = 0x00000000, len = 0\n    ram7   (wx) : org = 0x00000000, len = 0\n}\n\n/* For each data/text section two region are defined, a virtual region\n   and a load region (_LMA suffix).*/\n\n/* Flash region to be used for exception vectors.*/\nREGION_ALIAS(\"VECTORS_FLASH\", flash0);\nREGION_ALIAS(\"VECTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for constructors and destructors.*/\nREGION_ALIAS(\"XTORS_FLASH\", flash0);\nREGION_ALIAS(\"XTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for code text.*/\nREGION_ALIAS(\"TEXT_FLASH\", flash0);\nREGION_ALIAS(\"TEXT_FLASH_LMA\", flash0);\n\n/* Flash region to be used for read only data.*/\nREGION_ALIAS(\"RODATA_FLASH\", flash0);\nREGION_ALIAS(\"RODATA_FLASH_LMA\", flash0);\n\n/* Flash region to be used for various.*/\nREGION_ALIAS(\"VARIOUS_FLASH\", flash0);\nREGION_ALIAS(\"VARIOUS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for RAM(n) initialization data.*/\nREGION_ALIAS(\"RAM_INIT_FLASH_LMA\", flash0);\n\n/* RAM region to be used for Main stack. This stack accommodates the processing\n   of all exceptions and interrupts.*/\nREGION_ALIAS(\"MAIN_STACK_RAM\", ram0);\n\n/* RAM region to be used for the process stack. This is the stack used by\n   the main() function.*/\nREGION_ALIAS(\"PROCESS_STACK_RAM\", ram0);\n\n/* RAM region to be used for data segment.*/\nREGION_ALIAS(\"DATA_RAM\", ram0);\nREGION_ALIAS(\"DATA_RAM_LMA\", flash0);\n\n/* RAM region to be used for BSS segment.*/\nREGION_ALIAS(\"BSS_RAM\", ram0);\n\n/* RAM region to be used for the default heap.*/\nREGION_ALIAS(\"HEAP_RAM\", ram0);\n\n/* Generic rules inclusion.*/\nINCLUDE rules.ld\n\n/* Bootloader reset support */\n_board_magic_reg = ORIGIN(ram0) + 16k; /* this is based off the code within backup.c */\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F303xC_tinyuf2.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32F303xC memory setup.\n */\nMEMORY\n{\n    flash0 (rx) : org = 0x08000000 + 16k, len = 256k - 16k\n    flash1 (rx) : org = 0x00000000, len = 0\n    flash2 (rx) : org = 0x00000000, len = 0\n    flash3 (rx) : org = 0x00000000, len = 0\n    flash4 (rx) : org = 0x00000000, len = 0\n    flash5 (rx) : org = 0x00000000, len = 0\n    flash6 (rx) : org = 0x00000000, len = 0\n    flash7 (rx) : org = 0x00000000, len = 0\n    ram0   (wx) : org = 0x20000000, len = 40k\n    ram1   (wx) : org = 0x00000000, len = 0\n    ram2   (wx) : org = 0x00000000, len = 0\n    ram3   (wx) : org = 0x00000000, len = 0\n    ram4   (wx) : org = 0x10000000, len = 8k\n    ram5   (wx) : org = 0x00000000, len = 0\n    ram6   (wx) : org = 0x00000000, len = 0\n    ram7   (wx) : org = 0x00000000, len = 0\n}\n\n/* For each data/text section two region are defined, a virtual region\n   and a load region (_LMA suffix).*/\n\n/* Flash region to be used for exception vectors.*/\nREGION_ALIAS(\"VECTORS_FLASH\", flash0);\nREGION_ALIAS(\"VECTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for constructors and destructors.*/\nREGION_ALIAS(\"XTORS_FLASH\", flash0);\nREGION_ALIAS(\"XTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for code text.*/\nREGION_ALIAS(\"TEXT_FLASH\", flash0);\nREGION_ALIAS(\"TEXT_FLASH_LMA\", flash0);\n\n/* Flash region to be used for read only data.*/\nREGION_ALIAS(\"RODATA_FLASH\", flash0);\nREGION_ALIAS(\"RODATA_FLASH_LMA\", flash0);\n\n/* Flash region to be used for various.*/\nREGION_ALIAS(\"VARIOUS_FLASH\", flash0);\nREGION_ALIAS(\"VARIOUS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for RAM(n) initialization data.*/\nREGION_ALIAS(\"RAM_INIT_FLASH_LMA\", flash0);\n\n/* RAM region to be used for Main stack. This stack accommodates the processing\n   of all exceptions and interrupts.*/\nREGION_ALIAS(\"MAIN_STACK_RAM\", ram0);\n\n/* RAM region to be used for the process stack. This is the stack used by\n   the main() function.*/\nREGION_ALIAS(\"PROCESS_STACK_RAM\", ram0);\n\n/* RAM region to be used for data segment.*/\nREGION_ALIAS(\"DATA_RAM\", ram0);\nREGION_ALIAS(\"DATA_RAM_LMA\", flash0);\n\n/* RAM region to be used for BSS segment.*/\nREGION_ALIAS(\"BSS_RAM\", ram0);\n\n/* RAM region to be used for the default heap.*/\nREGION_ALIAS(\"HEAP_RAM\", ram0);\n\n/* Generic rules inclusion.*/\nINCLUDE rules.ld\n\n/* TinyUF2 bootloader reset support */\n_board_dfu_dbl_tap = ORIGIN(ram0) + 40k - 4; /* this is based off the linker file for tinyuf2 */\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F401xC.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 256k;\nf4xx_ram1_size = 64k;\nf4xx_ram2_size = 0;\nf4xx_ram4_size = 0;\nf4xx_ram5_size = 0;\n\nINCLUDE stm32f4xx_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F401xC_tinyuf2.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 256k;\nf4xx_ram1_size = 64k;\nf4xx_ram2_size = 0;\nf4xx_ram4_size = 0;\nf4xx_ram5_size = 0;\n\nINCLUDE stm32f4xx_tinyuf2_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F401xE.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 512k;\nf4xx_ram1_size = 96k;\nf4xx_ram2_size = 0;\nf4xx_ram4_size = 0;\nf4xx_ram5_size = 0;\n\nINCLUDE stm32f4xx_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F401xE_tinyuf2.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 512k;\nf4xx_ram1_size = 96k;\nf4xx_ram2_size = 0;\nf4xx_ram4_size = 0;\nf4xx_ram5_size = 0;\n\nINCLUDE stm32f4xx_tinyuf2_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F405xG.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 1M;\nf4xx_ram1_size = 112k;\nf4xx_ram2_size = 16k;\nf4xx_ram4_size = 64k;\nf4xx_ram5_size = 4k;\n\nINCLUDE stm32f4xx_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F405xG_tinyuf2.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 1M;\nf4xx_ram1_size = 112k;\nf4xx_ram2_size = 16k;\nf4xx_ram4_size = 64k;\nf4xx_ram5_size = 4k;\n\nINCLUDE stm32f4xx_tinyuf2_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F407xE.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 512k;\nf4xx_ram1_size = 112k;\nf4xx_ram2_size = 16k;\nf4xx_ram4_size = 64k;\nf4xx_ram5_size = 4k;\n\nINCLUDE stm32f4xx_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F407xE_tinyuf2.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 512k;\nf4xx_ram1_size = 112k;\nf4xx_ram2_size = 16k;\nf4xx_ram4_size = 64k;\nf4xx_ram5_size = 4k;\n\nINCLUDE stm32f4xx_tinyuf2_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F411xC.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 256k;\nf4xx_ram1_size = 128k;\nf4xx_ram2_size = 0;\nf4xx_ram4_size = 0;\nf4xx_ram5_size = 0;\n\nINCLUDE stm32f4xx_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F411xC_tinyuf2.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 256k;\nf4xx_ram1_size = 128k;\nf4xx_ram2_size = 0;\nf4xx_ram4_size = 0;\nf4xx_ram5_size = 0;\n\nINCLUDE stm32f4xx_tinyuf2_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F411xE.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 512k;\nf4xx_ram1_size = 128k;\nf4xx_ram2_size = 0;\nf4xx_ram4_size = 0;\nf4xx_ram5_size = 0;\n\nINCLUDE stm32f4xx_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F411xE_tinyuf2.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 512k;\nf4xx_ram1_size = 128k;\nf4xx_ram2_size = 0;\nf4xx_ram4_size = 0;\nf4xx_ram5_size = 0;\n\nINCLUDE stm32f4xx_tinyuf2_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F446xE.ld",
    "content": "/*\n * Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 512k;\nf4xx_ram1_size = 112k;\nf4xx_ram2_size = 16k;\nf4xx_ram4_size = 0;\nf4xx_ram5_size = 4k;\n\nINCLUDE stm32f4xx_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32F446xE_tinyuf2.ld",
    "content": "/*\n* Copyright 2006..2018 Giovanni Di Sirio\n * Copyright 2022 QMK contributors\n * SPDX-License-Identifier: GPL-2.0-or-later\n */\n\nf4xx_flash_size = 512k;\nf4xx_ram1_size = 112k;\nf4xx_ram2_size = 16k;\nf4xx_ram4_size = 0;\nf4xx_ram5_size = 4k;\n\nINCLUDE stm32f4xx_tinyuf2_common.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32G0B1xB.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32G0B1xB memory setup.\n */\nMEMORY\n{\n    flash0 (rx) : org = 0x08000000, len = 128k\n    flash1 (rx) : org = 0x00000000, len = 0\n    flash2 (rx) : org = 0x00000000, len = 0\n    flash3 (rx) : org = 0x00000000, len = 0\n    flash4 (rx) : org = 0x00000000, len = 0\n    flash5 (rx) : org = 0x00000000, len = 0\n    flash6 (rx) : org = 0x00000000, len = 0\n    flash7 (rx) : org = 0x00000000, len = 0\n    ram0   (wx) : org = 0x20000000, len = 144k\n    ram1   (wx) : org = 0x00000000, len = 0\n    ram2   (wx) : org = 0x00000000, len = 0\n    ram3   (wx) : org = 0x00000000, len = 0\n    ram4   (wx) : org = 0x00000000, len = 0\n    ram5   (wx) : org = 0x00000000, len = 0\n    ram6   (wx) : org = 0x00000000, len = 0\n    ram7   (wx) : org = 0x00000000, len = 0\n}\n\n/* For each data/text section two region are defined, a virtual region\n   and a load region (_LMA suffix).*/\n\n/* Flash region to be used for exception vectors.*/\nREGION_ALIAS(\"VECTORS_FLASH\", flash0);\nREGION_ALIAS(\"VECTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for constructors and destructors.*/\nREGION_ALIAS(\"XTORS_FLASH\", flash0);\nREGION_ALIAS(\"XTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for code text.*/\nREGION_ALIAS(\"TEXT_FLASH\", flash0);\nREGION_ALIAS(\"TEXT_FLASH_LMA\", flash0);\n\n/* Flash region to be used for read only data.*/\nREGION_ALIAS(\"RODATA_FLASH\", flash0);\nREGION_ALIAS(\"RODATA_FLASH_LMA\", flash0);\n\n/* Flash region to be used for various.*/\nREGION_ALIAS(\"VARIOUS_FLASH\", flash0);\nREGION_ALIAS(\"VARIOUS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for RAM(n) initialization data.*/\nREGION_ALIAS(\"RAM_INIT_FLASH_LMA\", flash0);\n\n/* RAM region to be used for Main stack. This stack accommodates the processing\n   of all exceptions and interrupts.*/\nREGION_ALIAS(\"MAIN_STACK_RAM\", ram0);\n\n/* RAM region to be used for the process stack. This is the stack used by\n   the main() function.*/\nREGION_ALIAS(\"PROCESS_STACK_RAM\", ram0);\n\n/* RAM region to be used for data segment.*/\nREGION_ALIAS(\"DATA_RAM\", ram0);\nREGION_ALIAS(\"DATA_RAM_LMA\", flash0);\n\n/* RAM region to be used for BSS segment.*/\nREGION_ALIAS(\"BSS_RAM\", ram0);\n\n/* RAM region to be used for the default heap.*/\nREGION_ALIAS(\"HEAP_RAM\", ram0);\n\n/* Generic rules inclusion.*/\nINCLUDE rules.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/STM32L412xB.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2016 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32L412xB memory setup.\n */\nMEMORY\n{\n    flash0  : org = 0x08000000, len = 128k\n    flash1  : org = 0x00000000, len = 0\n    flash2  : org = 0x00000000, len = 0\n    flash3  : org = 0x00000000, len = 0\n    flash4  : org = 0x00000000, len = 0\n    flash5  : org = 0x00000000, len = 0\n    flash6  : org = 0x00000000, len = 0\n    flash7  : org = 0x00000000, len = 0\n    ram0    : org = 0x20000000, len = 32k\n    ram1    : org = 0x00000000, len = 0\n    ram2    : org = 0x00000000, len = 0\n    ram3    : org = 0x00000000, len = 0\n    ram4    : org = 0x00000000, len = 0\n    ram5    : org = 0x00000000, len = 0\n    ram6    : org = 0x00000000, len = 0\n    ram7    : org = 0x00000000, len = 0\n}\n\n/* For each data/text section two region are defined, a virtual region\n   and a load region (_LMA suffix).*/\n\n/* Flash region to be used for exception vectors.*/\nREGION_ALIAS(\"VECTORS_FLASH\", flash0);\nREGION_ALIAS(\"VECTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for constructors and destructors.*/\nREGION_ALIAS(\"XTORS_FLASH\", flash0);\nREGION_ALIAS(\"XTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for code text.*/\nREGION_ALIAS(\"TEXT_FLASH\", flash0);\nREGION_ALIAS(\"TEXT_FLASH_LMA\", flash0);\n\n/* Flash region to be used for read only data.*/\nREGION_ALIAS(\"RODATA_FLASH\", flash0);\nREGION_ALIAS(\"RODATA_FLASH_LMA\", flash0);\n\n/* Flash region to be used for various.*/\nREGION_ALIAS(\"VARIOUS_FLASH\", flash0);\nREGION_ALIAS(\"VARIOUS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for RAM(n) initialization data.*/\nREGION_ALIAS(\"RAM_INIT_FLASH_LMA\", flash0);\n\n/* RAM region to be used for Main stack. This stack accommodates the processing\n   of all exceptions and interrupts.*/\nREGION_ALIAS(\"MAIN_STACK_RAM\", ram0);\n\n/* RAM region to be used for the process stack. This is the stack used by\n   the main() function.*/\nREGION_ALIAS(\"PROCESS_STACK_RAM\", ram0);\n\n/* RAM region to be used for data segment.*/\nREGION_ALIAS(\"DATA_RAM\", ram0);\nREGION_ALIAS(\"DATA_RAM_LMA\", flash0);\n\n/* RAM region to be used for BSS segment.*/\nREGION_ALIAS(\"BSS_RAM\", ram0);\n\n/* RAM region to be used for the default heap.*/\nREGION_ALIAS(\"HEAP_RAM\", ram0);\n\n/* Generic rules inclusion.*/\nINCLUDE rules.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/stm32f4xx_common.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\nMEMORY\n{\n    flash0 (rx) : org = 0x08000000, len = 16k        /* Sector 0    - Init code as ROM bootloader assumes application starts here */\n    flash1 (rx) : org = 0x08004000, len = 16k        /* Sector 1    - Emulated eeprom */\n    flash2 (rx) : org = 0x08008000, len = f4xx_flash_size - 32k /* Sector 2..6 - Rest of firmware */\n    flash3 (rx) : org = 0x00000000, len = 0\n    flash4 (rx) : org = 0x00000000, len = 0\n    flash5 (rx) : org = 0x00000000, len = 0\n    flash6 (rx) : org = 0x00000000, len = 0\n    flash7 (rx) : org = 0x00000000, len = 0\n    ram0   (wx) : org = 0x20000000, len = f4xx_ram1_size + f4xx_ram2_size /* SRAM1 + SRAM2 */\n    ram1   (wx) : org = 0x20000000, len = f4xx_ram1_size                  /* SRAM1 */\n    ram2   (wx) : org = 0x20000000 + f4xx_ram1_size, len = f4xx_ram2_size /* SRAM2 */\n    ram3   (wx) : org = 0x00000000, len = 0\n    ram4   (wx) : org = 0x10000000, len = f4xx_ram4_size                  /* CCM SRAM */\n    ram5   (wx) : org = 0x40024000, len = f4xx_ram5_size                  /* BCKP SRAM */\n    ram6   (wx) : org = 0x00000000, len = 0\n    ram7   (wx) : org = 0x00000000, len = 0\n}\n\n/* For each data/text section two region are defined, a virtual region\n   and a load region (_LMA suffix).*/\n\n/* Flash region to be used for exception vectors.*/\nREGION_ALIAS(\"VECTORS_FLASH\", flash0);\nREGION_ALIAS(\"VECTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for constructors and destructors.*/\nREGION_ALIAS(\"XTORS_FLASH\", flash2);\nREGION_ALIAS(\"XTORS_FLASH_LMA\", flash2);\n\n/* Flash region to be used for code text.*/\nREGION_ALIAS(\"TEXT_FLASH\", flash2);\nREGION_ALIAS(\"TEXT_FLASH_LMA\", flash2);\n\n/* Flash region to be used for read only data.*/\nREGION_ALIAS(\"RODATA_FLASH\", flash2);\nREGION_ALIAS(\"RODATA_FLASH_LMA\", flash2);\n\n/* Flash region to be used for various.*/\nREGION_ALIAS(\"VARIOUS_FLASH\", flash2);\nREGION_ALIAS(\"VARIOUS_FLASH_LMA\", flash2);\n\n/* Flash region to be used for RAM(n) initialization data.*/\nREGION_ALIAS(\"RAM_INIT_FLASH_LMA\", flash2);\n\n/* RAM region to be used for Main stack. This stack accommodates the processing\n   of all exceptions and interrupts.*/\nREGION_ALIAS(\"MAIN_STACK_RAM\", ram0);\n\n/* RAM region to be used for the process stack. This is the stack used by\n   the main() function.*/\nREGION_ALIAS(\"PROCESS_STACK_RAM\", ram0);\n\n/* RAM region to be used for data segment.*/\nREGION_ALIAS(\"DATA_RAM\", ram0);\nREGION_ALIAS(\"DATA_RAM_LMA\", flash2);\n\n/* RAM region to be used for BSS segment.*/\nREGION_ALIAS(\"BSS_RAM\", ram0);\n\n/* RAM region to be used for the default heap.*/\nREGION_ALIAS(\"HEAP_RAM\", ram0);\n\n/* Generic rules inclusion.*/\nINCLUDE rules.ld\n"
  },
  {
    "path": "platforms/chibios/boards/common/ld/stm32f4xx_tinyuf2_common.ld",
    "content": "/*\n    ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n*/\n\n/*\n * STM32F411xE memory setup.\n */\nMEMORY\n{\n    flash0 (rx) : org = 0x08000000 + 64k, len = f4xx_flash_size - 64k /* tinyuf2 bootloader requires app to be located at 64k offset for this MCU */\n    flash1 (rx) : org = 0x00000000, len = 0\n    flash2 (rx) : org = 0x00000000, len = 0\n    flash3 (rx) : org = 0x00000000, len = 0\n    flash4 (rx) : org = 0x00000000, len = 0\n    flash5 (rx) : org = 0x00000000, len = 0\n    flash6 (rx) : org = 0x00000000, len = 0\n    flash7 (rx) : org = 0x00000000, len = 0\n    ram0   (wx) : org = 0x20000000, len = f4xx_ram1_size + f4xx_ram2_size /* SRAM1 + SRAM2 */\n    ram1   (wx) : org = 0x20000000, len = f4xx_ram1_size                  /* SRAM1 */\n    ram2   (wx) : org = 0x20000000 + f4xx_ram1_size, len = f4xx_ram2_size /* SRAM2 */\n    ram3   (wx) : org = 0x00000000, len = 0\n    ram4   (wx) : org = 0x10000000, len = f4xx_ram4_size                  /* CCM SRAM */\n    ram5   (wx) : org = 0x40024000, len = f4xx_ram5_size                  /* BCKP SRAM */\n    ram6   (wx) : org = 0x00000000, len = 0\n    ram7   (wx) : org = 0x00000000, len = 0\n}\n\n/* For each data/text section two region are defined, a virtual region\n   and a load region (_LMA suffix).*/\n\n/* Flash region to be used for exception vectors.*/\nREGION_ALIAS(\"VECTORS_FLASH\", flash0);\nREGION_ALIAS(\"VECTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for constructors and destructors.*/\nREGION_ALIAS(\"XTORS_FLASH\", flash0);\nREGION_ALIAS(\"XTORS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for code text.*/\nREGION_ALIAS(\"TEXT_FLASH\", flash0);\nREGION_ALIAS(\"TEXT_FLASH_LMA\", flash0);\n\n/* Flash region to be used for read only data.*/\nREGION_ALIAS(\"RODATA_FLASH\", flash0);\nREGION_ALIAS(\"RODATA_FLASH_LMA\", flash0);\n\n/* Flash region to be used for various.*/\nREGION_ALIAS(\"VARIOUS_FLASH\", flash0);\nREGION_ALIAS(\"VARIOUS_FLASH_LMA\", flash0);\n\n/* Flash region to be used for RAM(n) initialization data.*/\nREGION_ALIAS(\"RAM_INIT_FLASH_LMA\", flash0);\n\n/* RAM region to be used for Main stack. This stack accommodates the processing\n   of all exceptions and interrupts.*/\nREGION_ALIAS(\"MAIN_STACK_RAM\", ram0);\n\n/* RAM region to be used for the process stack. This is the stack used by\n   the main() function.*/\nREGION_ALIAS(\"PROCESS_STACK_RAM\", ram0);\n\n/* RAM region to be used for data segment.*/\nREGION_ALIAS(\"DATA_RAM\", ram0);\nREGION_ALIAS(\"DATA_RAM_LMA\", flash0);\n\n/* RAM region to be used for BSS segment.*/\nREGION_ALIAS(\"BSS_RAM\", ram0);\n\n/* RAM region to be used for the default heap.*/\nREGION_ALIAS(\"HEAP_RAM\", ram0);\n\n/* Generic rules inclusion.*/\nINCLUDE rules.ld\n\n/* TinyUF2 bootloader reset support */\n_board_dfu_dbl_tap = ORIGIN(ram0) + 64k - 4; /* this is based off the linker file for tinyuf2 */\n"
  },
  {
    "path": "platforms/chibios/bootloader.mk",
    "content": "# Copyright 2017 Jack Humbert\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# If it's possible that multiple bootloaders can be used for one project,\n# you can leave this unset, and the correct size will be selected\n# automatically.\n#\n# Sets the bootloader defined in the keyboard's/keymap's rules.mk\n#\n# Current options for ARM:\n#     halfkay      PJRC Teensy\n#     kiibohd      Input:Club Kiibohd bootloader (only used on their boards)\n#     stm32duino   STM32Duino (STM32F103x8)\n#     stm32-dfu    STM32 USB DFU in ROM\n#     apm32-dfu    APM32 USB DFU in ROM\n#     wb32-dfu     WB32 USB DFU in ROM\n#     at32-dfu     AT32 USB DFU in ROM\n#     tinyuf2      TinyUF2\n#     rp2040       Raspberry Pi RP2040\n# Current options for RISC-V:\n#     gd32v-dfu    GD32V USB DFU in ROM\n#\n# If you need to provide your own implementation, you can set inside `rules.mk`\n# `BOOTLOADER = custom` -- you'll need to provide your own implementations. See\n# the respective file under `platforms/<PLATFORM>/bootloaders/custom.c` to see\n# which functions may be overridden.\n\nFIRMWARE_FORMAT?=bin\n\nifeq ($(strip $(BOOTLOADER)), custom)\n    OPT_DEFS += -DBOOTLOADER_CUSTOM\n    BOOTLOADER_TYPE = custom\nendif\n\nifeq ($(strip $(BOOTLOADER)), halfkay)\n    OPT_DEFS += -DBOOTLOADER_HALFKAY\n    BOOTLOADER_TYPE = halfkay\n\n    # Teensy LC, 3.0, 3.1/2, 3.5, 3.6\n    ifneq (,$(filter $(MCU_ORIG), MKL26Z64 MK20DX128 MK20DX256 MK64FX512 MK66FX1M0))\n        FIRMWARE_FORMAT = hex\n    endif\nendif\nifeq ($(strip $(BOOTLOADER)), stm32-dfu)\n    OPT_DEFS += -DBOOTLOADER_STM32_DFU\n    BOOTLOADER_TYPE = stm32_dfu\n\n    # Options to pass to dfu-util when flashing\n    DFU_ARGS ?= -d 0483:DF11 -a 0 -s 0x08000000:leave\n    DFU_SUFFIX_ARGS ?= -v 0483 -p DF11\nendif\nifeq ($(strip $(BOOTLOADER)), apm32-dfu)\n    OPT_DEFS += -DBOOTLOADER_APM32_DFU\n    BOOTLOADER_TYPE = stm32_dfu\n\n    # Options to pass to dfu-util when flashing\n    DFU_ARGS ?= -d 314B:0106 -a 0 -s 0x08000000:leave\n    DFU_SUFFIX_ARGS ?= -v 314B -p 0106\nendif\nifeq ($(strip $(BOOTLOADER)), gd32v-dfu)\n    OPT_DEFS += -DBOOTLOADER_GD32V_DFU\n    BOOTLOADER_TYPE = gd32v_dfu\n\n    # Options to pass to dfu-util when flashing\n    DFU_ARGS ?= -d 28E9:0189 -a 0 -s 0x08000000:leave\n    DFU_SUFFIX_ARGS ?= -v 28E9 -p 0189\nendif\nifeq ($(strip $(BOOTLOADER)), kiibohd)\n    OPT_DEFS += -DBOOTLOADER_KIIBOHD\n    BOOTLOADER_TYPE = kiibohd\n\n    ifeq ($(strip $(MCU_ORIG)), MK20DX128)\n        MCU_LDSCRIPT = MK20DX128BLDR4\n    endif\n    ifeq ($(strip $(MCU_ORIG)), MK20DX256)\n        MCU_LDSCRIPT = MK20DX256BLDR8\n    endif\n\n    # Options to pass to dfu-util when flashing\n    DFU_ARGS = -d 1C11:B007\n    DFU_SUFFIX_ARGS = -v 1C11 -p B007\nendif\nifeq ($(strip $(BOOTLOADER)), stm32duino)\n    OPT_DEFS += -DBOOTLOADER_STM32DUINO\n    BOARD = STM32_F103_STM32DUINO\n    BOOTLOADER_TYPE = stm32duino\n\n    # Options to pass to dfu-util when flashing\n    DFU_ARGS = -d 1EAF:0003 -a 2 -R\n    DFU_SUFFIX_ARGS = -v 1EAF -p 0003\nendif\nifeq ($(strip $(BOOTLOADER)), tinyuf2)\n    OPT_DEFS += -DBOOTLOADER_TINYUF2\n    BOOTLOADER_TYPE = tinyuf2\n    FIRMWARE_FORMAT = uf2\nendif\nifeq ($(strip $(BOOTLOADER)), uf2boot)\n    OPT_DEFS += -DBOOTLOADER_UF2BOOT\n    BOARD = STM32_F103_STM32DUINO\n    BOOTLOADER_TYPE = uf2boot\n    FIRMWARE_FORMAT = uf2\nendif\nifeq ($(strip $(BOOTLOADER)), rp2040)\n    OPT_DEFS += -DBOOTLOADER_RP2040\n    BOOTLOADER_TYPE = rp2040\nendif\nifeq ($(strip $(BOOTLOADER)), wb32-dfu)\n    OPT_DEFS += -DBOOTLOADER_WB32_DFU\n    BOOTLOADER_TYPE = wb32_dfu\nendif\nifeq ($(strip $(BOOTLOADER)), at32-dfu)\n    OPT_DEFS += -DBOOTLOADER_AT32_DFU\n    BOOTLOADER_TYPE = at32_dfu\n\n    # Options to pass to dfu-util when flashing\n    DFU_ARGS ?= -d 2E3C:DF11 -a 0 -s 0x08000000:leave\n    DFU_SUFFIX_ARGS ?= -v 2E3C -p DF11\nendif\n\nifeq ($(strip $(BOOTLOADER_TYPE)),)\n    ifneq ($(strip $(BOOTLOADER)),)\n        $(call CATASTROPHIC_ERROR,Invalid BOOTLOADER,Invalid bootloader specified. Please set an appropriate bootloader in your rules.mk or info.json.)\n    else\n        $(call CATASTROPHIC_ERROR,Invalid BOOTLOADER,No bootloader specified. Please set an appropriate bootloader in your rules.mk or info.json.)\n    endif\nendif\n"
  },
  {
    "path": "platforms/chibios/bootloaders/at32_dfu.c",
    "content": "// Copyright 2021-2023 QMK\n// Copyright 2023-2024 HorrorTroll <https://github.com/HorrorTroll>\n// Copyright 2023-2024 Zhaqian <https://github.com/zhaqian12>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"bootloader.h\"\n#include \"util.h\"\n\n#include <ch.h>\n#include <hal.h>\n#include \"wait.h\"\n\n#ifndef AT32_BOOTLOADER_RAM_SYMBOL\n#    define AT32_BOOTLOADER_RAM_SYMBOL __ram0_end__\n#endif\n\nextern uint32_t AT32_BOOTLOADER_RAM_SYMBOL;\n\n/* This code should be checked whether it runs correctly on platforms */\n#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0))\n#define BOOTLOADER_MAGIC 0xDEADBEEF\n#define MAGIC_ADDR (unsigned long *)(SYMVAL(AT32_BOOTLOADER_RAM_SYMBOL) - 4)\n\n__attribute__((weak)) void bootloader_marker_enable(void) {\n    uint32_t *marker = (uint32_t *)MAGIC_ADDR;\n    *marker          = BOOTLOADER_MAGIC; // set magic flag => reset handler will jump into boot loader\n}\n\n__attribute__((weak)) bool bootloader_marker_active(void) {\n    const uint32_t *marker = (const uint32_t *)MAGIC_ADDR;\n    return (*marker == BOOTLOADER_MAGIC) ? true : false;\n}\n\n__attribute__((weak)) void bootloader_marker_disable(void) {\n    uint32_t *marker = (uint32_t *)MAGIC_ADDR;\n    *marker          = 0;\n}\n\n__attribute__((weak)) void bootloader_jump(void) {\n    bootloader_marker_enable();\n    NVIC_SystemReset();\n}\n\n__attribute__((weak)) void mcu_reset(void) {\n    NVIC_SystemReset();\n}\n\nvoid enter_bootloader_mode_if_requested(void) {\n    if (bootloader_marker_active()) {\n        bootloader_marker_disable();\n\n        struct system_memory_vector_t {\n            uint32_t stack_top;\n            void (*entrypoint)(void);\n        };\n        const struct system_memory_vector_t *bootloader = (const struct system_memory_vector_t *)(AT32_BOOTLOADER_ADDRESS);\n\n        __disable_irq();\n\n#if defined(__MPU_PRESENT) && (__MPU_PRESENT == 1U)\n        ARM_MPU_Disable();\n#endif\n\n        SysTick->CTRL = 0;\n        SysTick->VAL  = 0;\n        SysTick->LOAD = 0;\n\n        // Clear interrupt enable and interrupt pending registers\n        for (int i = 0; i < ARRAY_SIZE(NVIC->ICER); i++) {\n            NVIC->ICER[i] = 0xFFFFFFFF;\n            NVIC->ICPR[i] = 0xFFFFFFFF;\n        }\n\n        __set_CONTROL(0);\n        __set_MSP(bootloader->stack_top);\n        __enable_irq();\n\n        // Jump to bootloader\n        bootloader->entrypoint();\n        while (true) {\n        }\n    }\n}\n"
  },
  {
    "path": "platforms/chibios/bootloaders/custom.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n__attribute__((weak)) void bootloader_jump(void) {}\n__attribute__((weak)) void mcu_reset(void) {}\n\n__attribute__((weak)) void enter_bootloader_mode_if_requested(void) {}\n"
  },
  {
    "path": "platforms/chibios/bootloaders/gd32v_dfu.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n#include <ch.h>\n#include <hal.h>\n\n#define DBGMCU_KEY_UNLOCK 0x4B5A6978\n#define DBGMCU_CMD_RESET 0x1\n\n__IO uint32_t *DBGMCU_KEY = (uint32_t *)DBGMCU_BASE + 0x0CU;\n__IO uint32_t *DBGMCU_CMD = (uint32_t *)DBGMCU_BASE + 0x08U;\n\n__attribute__((weak)) void bootloader_jump(void) {\n    /* The MTIMER unit of the GD32VF103 doesn't have the MSFRST\n     * register to generate a software reset request.\n     * BUT instead two undocumented registers in the debug peripheral\n     * that allow issueing a software reset. WHO would need the MSFRST\n     * register anyway? Source:\n     * https://github.com/esmil/gd32vf103inator/blob/master/include/gd32vf103/dbg.h */\n    *DBGMCU_KEY = DBGMCU_KEY_UNLOCK;\n    *DBGMCU_CMD = DBGMCU_CMD_RESET;\n}\n\n__attribute__((weak)) void mcu_reset(void) {\n    // Confirmed by karlk90, there is no actual reset to bootloader.\n    // This just resets the controller.\n    *DBGMCU_KEY = DBGMCU_KEY_UNLOCK;\n    *DBGMCU_CMD = DBGMCU_CMD_RESET;\n}\n\n/* Jumping to bootloader is not possible from user code. */\nvoid enter_bootloader_mode_if_requested(void) {}\n"
  },
  {
    "path": "platforms/chibios/bootloaders/halfkay.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n#include <ch.h>\n#include \"wait.h\"\n\n__attribute__((weak)) void bootloader_jump(void) {\n    wait_ms(100);\n    __BKPT(0);\n}\n\n__attribute__((weak)) void mcu_reset(void) {}\n"
  },
  {
    "path": "platforms/chibios/bootloaders/kiibohd.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n#include <ch.h>\n\n/* Kiibohd Bootloader (MCHCK and Infinity KB) */\n#define SCB_AIRCR_VECTKEY_WRITEMAGIC 0x05FA0000\n\nconst uint8_t sys_reset_to_loader_magic[] = \"\\xff\\x00\\x7fRESET TO LOADER\\x7f\\x00\\xff\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\";\n\n__attribute__((weak)) void bootloader_jump(void) {\n    void *volatile vbat = (void *)VBAT;\n    __builtin_memcpy(vbat, (const void *)sys_reset_to_loader_magic, sizeof(sys_reset_to_loader_magic));\n\n    // request reset\n    SCB->AIRCR = SCB_AIRCR_VECTKEY_WRITEMAGIC | SCB_AIRCR_SYSRESETREQ_Msk;\n}\n__attribute__((weak)) void mcu_reset(void) {}\n"
  },
  {
    "path": "platforms/chibios/bootloaders/rp2040.c",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"hal.h\"\n#include \"bootloader.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n#include \"pico/bootrom.h\"\n\n#if !defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED)\n#    define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK 0U\n#else\n#    define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK (1U << RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED)\n#endif\n\n__attribute__((weak)) void mcu_reset(void) {\n    NVIC_SystemReset();\n}\nvoid bootloader_jump(void) {\n    reset_usb_boot(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK, 0U);\n}\n\nvoid enter_bootloader_mode_if_requested(void) {}\n\n#if defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET)\n#    if !defined(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT)\n#        define RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT 200U\n#    endif\n\n// Needs to be located in a RAM section that is never initialized on boot to\n// preserve its value on reset\nstatic volatile uint32_t __attribute__((section(\".ram0.bootloader_magic\"))) magic_location;\nconst uint32_t magic_token = 0xCAFEB0BA;\n\n// We can not use the __early_init / enter_bootloader_mode_if_requested hook as\n// we depend on an already initialized system with usable memory regions and\n// populated function pointer tables to the optimized math functions in the\n// bootrom. This function is called just prior to main.\nvoid __late_init(void) {\n    // All clocks have to be enabled before jumping to the bootloader function,\n    // otherwise the bootrom will be stuck infinitely.\n    clocks_init();\n\n    if (magic_location != magic_token) {\n        magic_location = magic_token;\n        // ChibiOS is not initialized at this point, so sleeping is only\n        // possible via busy waiting.\n        wait_us(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_TIMEOUT * 1000U);\n        magic_location = 0;\n        return;\n    }\n\n    magic_location = 0;\n    reset_usb_boot(RP2040_BOOTLOADER_DOUBLE_TAP_RESET_LED_MASK, 0U);\n}\n\n#endif\n"
  },
  {
    "path": "platforms/chibios/bootloaders/stm32_dfu.c",
    "content": "/* Copyright 2021-2023 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n#include \"util.h\"\n\n#include <ch.h>\n#include <hal.h>\n#include \"wait.h\"\n\n#ifndef STM32_BOOTLOADER_RAM_SYMBOL\n#    define STM32_BOOTLOADER_RAM_SYMBOL __ram0_end__\n#endif\n\nextern uint32_t STM32_BOOTLOADER_RAM_SYMBOL;\n\n#ifndef STM32_BOOTLOADER_DUAL_BANK\n#    define STM32_BOOTLOADER_DUAL_BANK FALSE\n#endif\n\n#if STM32_BOOTLOADER_DUAL_BANK\n#    include \"gpio.h\"\n\n#    ifndef STM32_BOOTLOADER_DUAL_BANK_GPIO\n#        error \"No STM32_BOOTLOADER_DUAL_BANK_GPIO defined, don't know which pin to toggle\"\n#    endif\n\n#    ifndef STM32_BOOTLOADER_DUAL_BANK_POLARITY\n#        define STM32_BOOTLOADER_DUAL_BANK_POLARITY 0\n#    endif\n\n#    ifndef STM32_BOOTLOADER_DUAL_BANK_DELAY\n#        define STM32_BOOTLOADER_DUAL_BANK_DELAY 100\n#    endif\n\n__attribute__((weak)) void bootloader_jump(void) {\n    // For STM32 MCUs with dual-bank flash, and we're incapable of jumping to the bootloader. The first valid flash\n    // bank is executed unconditionally after a reset, so it doesn't enter DFU unless BOOT0 is high. Instead, we do\n    // it with hardware...in this case, we pull a GPIO high/low depending on the configuration, connects 3.3V to\n    // BOOT0's RC charging circuit, lets it charge the capacitor, and issue a system reset. See the QMK discord\n    // #hardware channel pins for an example circuit.\n    palSetPadMode(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_MODE_OUTPUT_PUSHPULL);\n#    if STM32_BOOTLOADER_DUAL_BANK_POLARITY\n    palSetPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO));\n#    else\n    palClearPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO));\n#    endif\n\n    // Wait for a while for the capacitor to charge\n    wait_ms(STM32_BOOTLOADER_DUAL_BANK_DELAY);\n\n    // Issue a system reset to get the ROM bootloader to execute, with BOOT0 high\n    NVIC_SystemReset();\n}\n\n__attribute__((weak)) void mcu_reset(void) {\n    NVIC_SystemReset();\n}\n// not needed at all, but if anybody attempts to invoke it....\nvoid enter_bootloader_mode_if_requested(void) {}\n\n#else\n\n/* This code should be checked whether it runs correctly on platforms */\n#    define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0))\n#    define BOOTLOADER_MAGIC 0xDEADBEEF\n#    define MAGIC_ADDR (unsigned long *)(SYMVAL(STM32_BOOTLOADER_RAM_SYMBOL) - 4)\n\n__attribute__((weak)) void bootloader_marker_enable(void) {\n    uint32_t *marker = (uint32_t *)MAGIC_ADDR;\n    *marker          = BOOTLOADER_MAGIC; // set magic flag => reset handler will jump into boot loader\n}\n\n__attribute__((weak)) bool bootloader_marker_active(void) {\n    const uint32_t *marker = (const uint32_t *)MAGIC_ADDR;\n    return (*marker == BOOTLOADER_MAGIC) ? true : false;\n}\n\n__attribute__((weak)) void bootloader_marker_disable(void) {\n    uint32_t *marker = (uint32_t *)MAGIC_ADDR;\n    *marker          = 0;\n}\n\n__attribute__((weak)) void bootloader_jump(void) {\n    bootloader_marker_enable();\n    NVIC_SystemReset();\n}\n\n__attribute__((weak)) void mcu_reset(void) {\n    NVIC_SystemReset();\n}\n\nvoid enter_bootloader_mode_if_requested(void) {\n    if (bootloader_marker_active()) {\n        bootloader_marker_disable();\n\n        struct system_memory_vector_t {\n            uint32_t stack_top;\n            void (*entrypoint)(void);\n        };\n        const struct system_memory_vector_t *bootloader = (const struct system_memory_vector_t *)(STM32_BOOTLOADER_ADDRESS);\n\n        __disable_irq();\n\n#    if defined(QMK_MCU_ARCH_CORTEX_M7)\n        SCB_DisableDCache();\n        SCB_DisableICache();\n#    endif\n\n#    if defined(__MPU_PRESENT) && (__MPU_PRESENT == 1U)\n        ARM_MPU_Disable();\n#    endif\n\n        SysTick->CTRL = 0;\n        SysTick->VAL  = 0;\n        SysTick->LOAD = 0;\n\n        // Clear interrupt enable and interrupt pending registers\n        for (int i = 0; i < ARRAY_SIZE(NVIC->ICER); i++) {\n            NVIC->ICER[i] = 0xFFFFFFFF;\n            NVIC->ICPR[i] = 0xFFFFFFFF;\n        }\n\n        __set_CONTROL(0);\n        __set_MSP(bootloader->stack_top);\n        __enable_irq();\n\n        // Jump to bootloader\n        bootloader->entrypoint();\n        while (true) {\n        }\n    }\n}\n#endif\n"
  },
  {
    "path": "platforms/chibios/bootloaders/stm32duino.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n#include <ch.h>\n\n__attribute__((weak)) void bootloader_jump(void) {\n    NVIC_SystemReset();\n}\n\n__attribute__((weak)) void mcu_reset(void) {\n    BKP->DR10 = RTC_BOOTLOADER_JUST_UPLOADED;\n    NVIC_SystemReset();\n}\n"
  },
  {
    "path": "platforms/chibios/bootloaders/tinyuf2.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n#include <ch.h>\n\n// From tinyuf2's board_api.h\n#define DBL_TAP_MAGIC 0xF01669EF\n\n// defined by linker script\nextern uint32_t _board_dfu_dbl_tap[];\n#define DBL_TAP_REG _board_dfu_dbl_tap[0]\n\n__attribute__((weak)) void mcu_reset(void) {\n    NVIC_SystemReset();\n}\n\n__attribute__((weak)) void bootloader_jump(void) {\n    DBL_TAP_REG = DBL_TAP_MAGIC;\n    NVIC_SystemReset();\n}\n\n/* not needed, no two-stage reset */\nvoid enter_bootloader_mode_if_requested(void) {}\n"
  },
  {
    "path": "platforms/chibios/bootloaders/uf2boot.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <stdint.h>\n#include <hal.h>\n#include \"bootloader.h\"\n\n// From mmoskal/uf2-stm32f103's backup.c\n#define MAGIC_BOOT 0x544F4F42UL\n\n// defined by linker script\nextern uint32_t _board_magic_reg[];\n#define MAGIC_REG _board_magic_reg[0]\n\nvoid bootloader_jump(void) {\n    MAGIC_REG = MAGIC_BOOT;\n    NVIC_SystemReset();\n}\n\nvoid mcu_reset(void) {\n    NVIC_SystemReset();\n}\n\n/* not needed, no two-stage reset */\nvoid enter_bootloader_mode_if_requested(void) {}\n"
  },
  {
    "path": "platforms/chibios/bootloaders/wb32_dfu.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\n#include <ch.h>\n#include <hal.h>\n#include \"wait.h\"\n\nextern uint32_t __ram0_end__;\n\n/* This code should be checked whether it runs correctly on platforms */\n#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0))\n#define BOOTLOADER_MAGIC 0xDEADBEEF\n#define MAGIC_ADDR (unsigned long *)(SYMVAL(__ram0_end__) - 4)\n\n__attribute__((weak)) void bootloader_jump(void) {\n    *MAGIC_ADDR = BOOTLOADER_MAGIC; // set magic flag => reset handler will jump into boot loader\n    NVIC_SystemReset();\n}\n\nvoid enter_bootloader_mode_if_requested(void) {\n    unsigned long *check = MAGIC_ADDR;\n    if (*check == BOOTLOADER_MAGIC) {\n        *check = 0;\n        __set_CONTROL(0);\n        __set_MSP(*(__IO uint32_t *)WB32_BOOTLOADER_ADDRESS);\n        __enable_irq();\n\n        typedef void (*BootJump_t)(void);\n        BootJump_t boot_jump = *(BootJump_t *)(WB32_BOOTLOADER_ADDRESS + 4);\n        boot_jump();\n        while (1)\n            ;\n    }\n}\n\n__attribute__((weak)) void mcu_reset(void) {\n    NVIC_SystemReset();\n}\n"
  },
  {
    "path": "platforms/chibios/chibios_config.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include \"compiler_support.h\"\n\n#ifndef USB_VBUS_PIN\n#    define SPLIT_USB_DETECT // Force this on when dedicated pin is not used\n#endif\n\n#if defined(MCU_RP)\n#    define CPU_CLOCK RP_CORE_CLK\n// ChibiOS uses the RP2040 timer peripheral as its real time counter, this timer\n// is monotonic and running at 1MHz.\n#    define REALTIME_COUNTER_CLOCK 1000000\n\n#    define USE_GPIOV1\n#    define PAL_OUTPUT_TYPE_OPENDRAIN STATIC_ASSERT(0, \"RP2040 has no Open Drain GPIO configuration, setting this is not possible\");\n\n/* Aliases for GPIO PWM channels - every pin has at least one PWM channel\n * assigned */\n#    define RP2040_PWM_CHANNEL_A 1U\n#    define RP2040_PWM_CHANNEL_B 2U\n\n#    ifndef BACKLIGHT_PAL_MODE\n#        define BACKLIGHT_PAL_MODE (PAL_MODE_ALTERNATE_PWM | PAL_RP_PAD_DRIVE12 | PAL_RP_GPIO_OE)\n#    endif\n#    define BACKLIGHT_PWM_COUNTER_FREQUENCY 1000000\n#    ifndef BACKLIGHT_PWM_PERIOD\n#        define BACKLIGHT_PWM_PERIOD BACKLIGHT_PWM_COUNTER_FREQUENCY / 2048\n#    endif\n\n#    ifndef AUDIO_PWM_PAL_MODE\n#        define AUDIO_PWM_PAL_MODE (PAL_MODE_ALTERNATE_PWM | PAL_RP_PAD_DRIVE12 | PAL_RP_GPIO_OE)\n#    endif\n#    define AUDIO_PWM_COUNTER_FREQUENCY 500000\n\n#    define usb_lld_endpoint_fields\n\n#    ifndef I2C1_SCL_PAL_MODE\n#        define I2C1_SCL_PAL_MODE (PAL_MODE_ALTERNATE_I2C | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_PUE | PAL_RP_PAD_DRIVE4)\n#    endif\n#    ifndef I2C1_SDA_PAL_MODE\n#        define I2C1_SDA_PAL_MODE (PAL_MODE_ALTERNATE_I2C | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_PUE | PAL_RP_PAD_DRIVE4)\n#    endif\n\n#    define USE_I2CV1_CONTRIB\n#    if !defined(I2C1_CLOCK_SPEED)\n#        define I2C1_CLOCK_SPEED 400000\n#    endif\n\n#    ifndef SPI_SCK_PAL_MODE\n#        define SPI_SCK_PAL_MODE (PAL_MODE_ALTERNATE_SPI | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_DRIVE4)\n#    endif\n#    ifndef SPI_MOSI_PAL_MODE\n#        define SPI_MOSI_PAL_MODE (PAL_MODE_ALTERNATE_SPI | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_DRIVE4)\n#    endif\n#    ifndef SPI_MISO_PAL_MODE\n#        define SPI_MISO_PAL_MODE (PAL_MODE_ALTERNATE_SPI | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_DRIVE4)\n#    endif\n\n#    ifndef UART_TX_PAL_MODE\n#        define UART_TX_PAL_MODE PAL_MODE_ALTERNATE_UART\n#    endif\n#    ifndef UART_RX_PAL_MODE\n#        define UART_RX_PAL_MODE PAL_MODE_ALTERNATE_UART\n#    endif\n#    ifndef UART_CTS_PAL_MODE\n#        define UART_CTS_PAL_MODE PAL_MODE_ALTERNATE_UART\n#    endif\n#    ifndef UART_RTS_PAL_MODE\n#        define UART_RTS_PAL_MODE PAL_MODE_ALTERNATE_UART\n#    endif\n\n#endif\n\n// STM32 compatibility\n#if defined(MCU_STM32)\n#    if defined(STM32_CORE_CK)\n#        define CPU_CLOCK STM32_CORE_CK\n#    else\n#        define CPU_CLOCK STM32_SYSCLK\n#    endif\n\n#    if defined(STM32F1XX)\n#        define USE_GPIOV1\n#        define PAL_MODE_ALTERNATE_OPENDRAIN PAL_MODE_STM32_ALTERNATE_OPENDRAIN\n#        define PAL_MODE_ALTERNATE_PUSHPULL PAL_MODE_STM32_ALTERNATE_PUSHPULL\n#        define AUDIO_PWM_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    else\n#        define PAL_OUTPUT_TYPE_OPENDRAIN PAL_STM32_OTYPE_OPENDRAIN\n#        define PAL_OUTPUT_TYPE_PUSHPULL PAL_STM32_OTYPE_PUSHPULL\n#        define PAL_OUTPUT_SPEED_HIGHEST PAL_STM32_OSPEED_HIGHEST\n#        define PAL_PUPDR_FLOATING PAL_STM32_PUPDR_FLOATING\n#    endif\n\n#    if defined(STM32F1XX) || defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32L1XX)\n#        define USE_I2CV1\n#    endif\n\n#    if defined(STM32G0XX) || defined(STM32G4XX) || defined(STM32L5XX) || defined(STM32H7XX)\n#        define USE_USARTV3\n#    endif\n\n#endif\n\n// GD32 compatibility\n#if defined(MCU_GD32V)\n#    define CPU_CLOCK GD32_SYSCLK\n\n#    if defined(GD32VF103)\n#        define USE_GPIOV1\n#        define USE_I2CV1\n#        define PAL_MODE_ALTERNATE_OPENDRAIN PAL_MODE_GD32_ALTERNATE_OPENDRAIN\n#        define PAL_MODE_ALTERNATE_PUSHPULL PAL_MODE_GD32_ALTERNATE_PUSHPULL\n#        define AUDIO_PWM_PAL_MODE PAL_MODE_GD32_ALTERNATE_PUSHPULL\n#    endif\n#endif\n\n// WB32 compatibility\n#if defined(MCU_WB32)\n#    define CPU_CLOCK WB32_MAINCLK\n\n#    if defined(WB32F3G71xx) || defined(WB32FQ95xx)\n#        define PAL_OUTPUT_TYPE_OPENDRAIN PAL_WB32_OTYPE_OPENDRAIN\n#        define PAL_OUTPUT_TYPE_PUSHPULL PAL_WB32_OTYPE_PUSHPULL\n#        define PAL_OUTPUT_SPEED_HIGHEST PAL_WB32_OSPEED_HIGH\n#        define PAL_PUPDR_FLOATING PAL_WB32_PUPDR_FLOATING\n\n#        define SPI_SCK_FLAGS PAL_MODE_ALTERNATE(SPI_SCK_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST | PAL_WB32_CURRENT_LEVEL3\n#    endif\n#endif\n\n// AT32 compatibility\n#if defined(MCU_AT32)\n#    define CPU_CLOCK AT32_SYSCLK\n\n#    if defined(AT32F415)\n#        define USE_GPIOV1\n#        define USE_I2CV1\n#        define PAL_MODE_ALTERNATE_OPENDRAIN PAL_MODE_AT32_MUX_OPENDRAIN\n#        define PAL_MODE_ALTERNATE_PUSHPULL PAL_MODE_AT32_MUX_PUSHPULL\n#        define AUDIO_PWM_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    endif\n#endif\n\n#if defined(GD32VF103)\n/* This chip has the same API as STM32F103, but uses different names for literally the same thing.\n * As of 4.7.2021 QMK is tailored to use STM32 defines/names, for compatibility sake\n * we just redefine the GD32 names. */\n#    include \"gd32v_compatibility.h\"\n#endif\n\n// teensy compatibility\n#if defined(MCU_KINETIS)\n#    define CPU_CLOCK KINETIS_SYSCLK_FREQUENCY\n\n#    if defined(K20x) || defined(K60x) || defined(KL2x)\n#        define USE_I2CV1\n#        define USE_I2CV1_CONTRIB // for some reason a bunch of ChibiOS-Contrib boards only have clock_speed\n#        define USE_GPIOV1\n#    endif\n#endif\n\n#if defined(MCU_MIMXRT1062)\n#    include \"clock_config.h\"\n#    define CPU_CLOCK BOARD_BOOTCLOCKRUN_CORE_CLOCK\n#endif\n\n#if defined(HT32)\n#    define CPU_CLOCK HT32_CK_SYS_FREQUENCY\n#    define PAL_MODE_ALTERNATE PAL_HT32_MODE_AF\n#    define PAL_OUTPUT_TYPE_OPENDRAIN (PAL_HT32_MODE_OD | PAL_HT32_MODE_DIR)\n#    define PAL_OUTPUT_TYPE_PUSHPULL PAL_HT32_MODE_DIR\n#    define PAL_OUTPUT_SPEED_HIGHEST 0\n#endif\n\n#if !defined(REALTIME_COUNTER_CLOCK)\n#    define REALTIME_COUNTER_CLOCK CPU_CLOCK\n#endif\n\n// SPI Fallbacks\n#ifndef SPI_SCK_FLAGS\n#    define SPI_SCK_FLAGS PAL_MODE_ALTERNATE(SPI_SCK_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST\n#endif\n\n#ifndef SPI_MOSI_FLAGS\n#    define SPI_MOSI_FLAGS PAL_MODE_ALTERNATE(SPI_MOSI_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST\n#endif\n\n#ifndef SPI_MISO_FLAGS\n#    define SPI_MISO_FLAGS PAL_MODE_ALTERNATE(SPI_MISO_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST\n#endif\n"
  },
  {
    "path": "platforms/chibios/config.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifndef CORTEX_ENABLE_WFI_IDLE\n#    define CORTEX_ENABLE_WFI_IDLE TRUE\n#endif // CORTEX_ENABLE_WFI_IDLE\n\n#ifndef SERIAL_NUMBER_USE_HARDWARE_ID\n#    define SERIAL_NUMBER_USE_HARDWARE_ID TRUE\n#endif // SERIAL_NUMBER_USE_HARDWARE_ID\n"
  },
  {
    "path": "platforms/chibios/converters/elite_c_to_elite_pi/pre_converter.mk",
    "content": "CONVERTER:=platforms/chibios/converters/elite_c_to_rp2040_ce\nACTIVE_CONVERTER:=rp2040_ce\n"
  },
  {
    "path": "platforms/chibios/converters/elite_c_to_helios/pre_converter.mk",
    "content": "CONVERTER:=platforms/chibios/converters/elite_c_to_rp2040_ce\nACTIVE_CONVERTER:=rp2040_ce\n"
  },
  {
    "path": "platforms/chibios/converters/elite_c_to_liatris/pre_converter.mk",
    "content": "CONVERTER:=platforms/chibios/converters/elite_c_to_rp2040_ce\nACTIVE_CONVERTER:=rp2040_ce\n"
  },
  {
    "path": "platforms/chibios/converters/elite_c_to_rp2040_ce/_pin_defs.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Left side (front)\n#define D3 0U\n#define D2 1U\n//      GND\n//      GND\n#define D1 2U\n#define D0 3U\n#define D4 4U\n#define C6 5U\n#define D7 6U\n#define E6 7U\n#define B4 8U\n#define B5 9U\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 29U\n#define F5 28U\n#define F6 27U\n#define F7 26U\n#define B1 22U\n#define B3 20U\n#define B2 23U\n#define B6 21U\n\n// Bottom row\n#define B7 12U\n#define D5 13U\n#define C7 14U\n#define F1 15U\n#define F0 16U\n"
  },
  {
    "path": "platforms/chibios/converters/elite_c_to_rp2040_ce/converter.mk",
    "content": "# rp2040_ce MCU settings for converting AVR projects\nMCU := RP2040\nBOARD := QMK_PM2040\nBOOTLOADER := rp2040\n\n# These are defaults based on what has been implemented for RP2040 boards\nSERIAL_DRIVER ?= vendor\nWS2812_DRIVER ?= vendor\nBACKLIGHT_DRIVER ?= software\nOPT_DEFS += -DUSB_VBUS_PIN=19U\n"
  },
  {
    "path": "platforms/chibios/converters/elite_c_to_stemcell/_pin_defs.h",
    "content": "// Copyright 2022 Mega Mind (@megamind4089)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Pindefs for v2.0.0\n// https://megamind4089.github.io/STeMCell/pinout/\n\n// Left side (front)\n#ifdef STEMCELL_UART_SWAP\n#    define D3 PAL_LINE(GPIOA, 3)\n#    define D2 PAL_LINE(GPIOA, 2)\n#else\n#    define D3 PAL_LINE(GPIOA, 2)\n#    define D2 PAL_LINE(GPIOA, 3)\n#endif\n//      GND\n//      GND\n#ifdef STEMCELL_I2C_SWAP\n#    define D1 PAL_LINE(GPIOB, 6)\n#    define D0 PAL_LINE(GPIOB, 7)\n#else\n#    define D1 PAL_LINE(GPIOB, 7)\n#    define D0 PAL_LINE(GPIOB, 6)\n#endif\n\n#define D4 PAL_LINE(GPIOA, 15)\n#define C6 PAL_LINE(GPIOB, 3)\n#define D7 PAL_LINE(GPIOB, 4)\n#define E6 PAL_LINE(GPIOB, 5)\n#define B4 PAL_LINE(GPIOB, 8)\n#define B5 PAL_LINE(GPIOB, 9)\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 PAL_LINE(GPIOB, 10)\n#define F5 PAL_LINE(GPIOB, 2)\n#define F6 PAL_LINE(GPIOB, 1)\n#define F7 PAL_LINE(GPIOB, 0)\n\n#define B1 PAL_LINE(GPIOA, 5)\n#define B3 PAL_LINE(GPIOA, 6)\n#define B2 PAL_LINE(GPIOA, 7)\n#define B6 PAL_LINE(GPIOA, 4)\n\n// Bottom row\n#define B7 PAL_LINE(GPIOC, 13)\n#define D5 PAL_LINE(GPIOC, 14)\n#define C7 PAL_LINE(GPIOC, 15)\n#define F1 PAL_LINE(GPIOA, 0)\n#define F0 PAL_LINE(GPIOA, 1)\n"
  },
  {
    "path": "platforms/chibios/converters/elite_c_to_stemcell/converter.mk",
    "content": "# Copyright 2022 Mega Mind (@megamind4089)\n# SPDX-License-Identifier: GPL-2.0-or-later\n\nMCU := STM32F411\nBOARD := STEMCELL\nBOOTLOADER := tinyuf2\n\nSERIAL_DRIVER ?= usart\nWS2812_DRIVER ?= bitbang\n\nifeq ($(strip $(STMC_US)), yes)\n  OPT_DEFS += -DSTEMCELL_UART_SWAP\nendif\n\nifeq ($(strip $(STMC_IS)), yes)\n  OPT_DEFS += -DSTEMCELL_I2C_SWAP\nendif\n\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_bit_c_pro/_pin_defs.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Left side (front)\n#define D3 0U\n#define D2 1U\n//      GND\n//      GND\n#define D1 2U\n#define D0 3U\n#define D4 4U\n#define C6 5U\n#define D7 6U\n#define E6 7U\n#define B4 8U\n#define B5 9U\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 29U\n#define F5 28U\n#define F6 27U\n#define F7 26U\n#define B1 22U\n#define B3 20U\n#define B2 23U\n#define B6 21U\n\n// LEDs (Mapped to R and G channel of the Bit-C PRO's RGB led)\n#define D5 16U\n#define B0 17U\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_bit_c_pro/converter.mk",
    "content": "# nullbits Bit-C PRO MCU settings for converting AVR projects\nMCU := RP2040\nBOARD := QMK_PM2040\nBOOTLOADER := rp2040\n\n# These are defaults based on what has been implemented for RP2040 boards\nSERIAL_DRIVER ?= vendor\nWS2812_DRIVER ?= vendor\nBACKLIGHT_DRIVER ?= software\n\n# Tell QMK to use the correct 2nd stage bootloader\nOPT_DEFS += -DRP2040_FLASH_W25X10CL\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_blok/_pin_defs.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Left side (front)\n#define D3 0U\n#define D2 1U\n//      GND\n//      GND\n#define D1 16U\n#define D0 17U\n#define D4 4U\n#define C6 5U\n#define D7 6U\n#define E6 7U\n#define B4 8U\n#define B5 9U\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 29U\n#define F5 28U\n#define F6 27U\n#define F7 26U\n#define B1 22U\n#define B3 20U\n#define B2 23U\n#define B6 21U\n\n// LEDs (Mapped to unused pins to avoid collisions)\n#define D5 12U\n#define B0 13U\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_blok/converter.mk",
    "content": "# Boardsource Blok MCU settings for converting AVR projects\nMCU := RP2040\nBOARD := QMK_BLOK\nBOOTLOADER := rp2040\n\n# These are defaults based on what has been implemented for RP2040 boards\nSERIAL_DRIVER ?= vendor\nWS2812_DRIVER ?= vendor\nBACKLIGHT_DRIVER ?= software\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_bonsai_c3/pre_converter.mk",
    "content": "CONVERTER:=platforms/chibios/converters/promicro_to_proton_c\nACTIVE_CONVERTER:=proton_c\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_bonsai_c4/_pin_defs.h",
    "content": "// Copyright 2022 customMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Left side (front)\n#define D3 PAL_LINE(GPIOB, 7)\n#define D2 PAL_LINE(GPIOA, 15)\n//      GND\n//      GND\n#define D1 PAL_LINE(GPIOB, 9)\n#define D0 PAL_LINE(GPIOB, 6)\n#define D4 PAL_LINE(GPIOA, 4)\n#define C6 PAL_LINE(GPIOB, 8)\n#define D7 PAL_LINE(GPIOA, 3)\n#define E6 PAL_LINE(GPIOB, 10)\n#define B4 PAL_LINE(GPIOA, 8)\n#define B5 PAL_LINE(GPIOB, 0)\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 PAL_LINE(GPIOA, 7)\n#define F5 PAL_LINE(GPIOA, 6)\n#define F6 PAL_LINE(GPIOA, 5)\n#define F7 PAL_LINE(GPIOA, 1)\n#define B1 PAL_LINE(GPIOB, 13)\n#define B3 PAL_LINE(GPIOB, 14)\n#define B2 PAL_LINE(GPIOB, 15)\n#define B6 PAL_LINE(GPIOB, 1)\n\n// LEDs (only D5/B2 uses an actual LED)\n// Setting both RX and TX LEDs to the same pin as there\n// is only one LED availble\n// If this is undesirable, either B0 or B5 can be redefined by\n// using #undef and #define to change its assignment\n#define B0 PAL_LINE(GPIOB, 2)\n#define D5 PAL_LINE(GPIOB, 2)\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_bonsai_c4/converter.mk",
    "content": "# Proton C MCU settings for converting AVR projects\nMCU := STM32F411\nBOARD := BONSAI_C4\nBOOTLOADER := stm32-dfu\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_bonsai_c4/post_converter.mk",
    "content": "BACKLIGHT_DRIVER ?= pwm\nWS2812_DRIVER ?= pwm\nSERIAL_DRIVER ?= usart\nFLASH_DRIVER ?= spi\nEEPROM_DRIVER ?= spi\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_elite_pi/pre_converter.mk",
    "content": "CONVERTER:=platforms/chibios/converters/promicro_to_rp2040_ce\nACTIVE_CONVERTER:=rp2040_ce\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_helios/pre_converter.mk",
    "content": "CONVERTER:=platforms/chibios/converters/promicro_to_rp2040_ce\nACTIVE_CONVERTER:=rp2040_ce\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_imera/_pin_defs.h",
    "content": "// Copyright 2023 splitkb.com\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Left side (front)\n#define D3 0U\n#define D2 1U\n//      GND\n//      GND\n#define D1 2U\n#define D0 3U\n#define D4 4U\n#define C6 5U\n#define D7 6U\n#define E6 7U\n#define B4 8U\n#define B5 9U\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 29U\n#define F5 28U\n#define F6 17U\n#define F7 16U\n#define B1 22U\n#define B3 20U\n#define B2 23U\n#define B6 21U\n\n// LEDs\n#define D5 24U // Power LED, default-on\n#define B0 18U // Unconnected\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_imera/converter.mk",
    "content": "# rp2040_ce MCU settings for converting AVR projects\nMCU := RP2040\nBOARD := QMK_PM2040\nBOOTLOADER := rp2040\n\n# These are defaults based on what has been implemented for RP2040 boards\nSERIAL_DRIVER ?= vendor\nWS2812_DRIVER ?= vendor\nBACKLIGHT_DRIVER ?= software\nOPT_DEFS += -DUSB_VBUS_PIN=19U\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_kb2040/_pin_defs.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Left side (front)\n#define D3 0U\n#define D2 1U\n//      GND\n//      GND\n#define D1 2U\n#define D0 3U\n#define D4 4U\n#define C6 5U\n#define D7 6U\n#define E6 7U\n#define B4 8U\n#define B5 9U\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 29U\n#define F5 28U\n#define F6 27U\n#define F7 26U\n#define B1 18U\n#define B3 20U\n#define B2 19U\n#define B6 10U\n\n// LEDs (Mapped to QT connector to avoid collisions with button/neopixel)\n#define D5 12U\n#define B0 13U\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_kb2040/converter.mk",
    "content": "# Adafruit KB2040 MCU settings for converting AVR projects\nMCU := RP2040\nBOARD := QMK_PM2040\nBOOTLOADER := rp2040\n\n# These are defaults based on what has been implemented for RP2040 boards\nSERIAL_DRIVER ?= vendor\nWS2812_DRIVER ?= vendor\nBACKLIGHT_DRIVER ?= software\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_liatris/pre_converter.mk",
    "content": "CONVERTER:=platforms/chibios/converters/promicro_to_rp2040_ce\nACTIVE_CONVERTER:=rp2040_ce\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_michi/_pin_defs.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Left side (front)\n#define D3 0U\n#define D2 1U\n//      GND\n//      GND\n#define D1 2U\n#define D0 3U\n#define D4 13U\n#define C6 4U\n#define D7 9U\n#define E6 10U\n#define B4 11U\n#define B5 12U\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 29U\n#define F5 28U\n#define F6 27U\n#define F7 26U\n#define B1 20U\n#define B3 19U\n#define B2 18U\n#define B6 17U\n\n// LEDs (Mapped to unused pins to avoid collisions)\n#define D5 14U\n#define B0 15U\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_michi/converter.mk",
    "content": "# Michi MCU settings for converting AVR projects\nMCU := RP2040\nBOARD := QMK_PM2040\nBOOTLOADER := rp2040\n\n# These are defaults based on what has been implemented for RP2040 boards\nSERIAL_DRIVER ?= vendor\nWS2812_DRIVER ?= vendor\nBACKLIGHT_DRIVER ?= software\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_promicro_rp2040/pre_converter.mk",
    "content": "CONVERTER:=platforms/chibios/converters/promicro_to_sparkfun_pm2040\nACTIVE_CONVERTER:=sparkfun_pm2040\n\n$(info @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@)\n$(info The 'CONVERT_TO=promicro_rp2040' option is now deprecated.)\n$(info Depending on hardware either 'CONVERT_TO=sparkfun_pm2040' or 'CONVERT_TO=rp2040_ce' should be used instead.)\n$(info See https://docs.qmk.fm/feature_converters#pro-micro documentation for more information.)\n$(info @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@)\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_proton_c/_pin_defs.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Left side (front)\n#define D3 PAL_LINE(GPIOA, 9)\n#define D2 PAL_LINE(GPIOA, 10)\n//      GND\n//      GND\n#define D1 PAL_LINE(GPIOB, 7)\n#define D0 PAL_LINE(GPIOB, 6)\n#define D4 PAL_LINE(GPIOB, 5)\n#define C6 PAL_LINE(GPIOB, 4)\n#define D7 PAL_LINE(GPIOB, 3)\n#define E6 PAL_LINE(GPIOB, 2)\n#define B4 PAL_LINE(GPIOB, 1)\n#define B5 PAL_LINE(GPIOB, 0)\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 PAL_LINE(GPIOA, 2)\n#define F5 PAL_LINE(GPIOA, 1)\n#define F6 PAL_LINE(GPIOA, 0)\n#define F7 PAL_LINE(GPIOB, 8)\n#define B1 PAL_LINE(GPIOB, 13)\n#define B3 PAL_LINE(GPIOB, 14)\n#define B2 PAL_LINE(GPIOB, 15)\n#define B6 PAL_LINE(GPIOB, 9)\n\n// LEDs (only D5/C13 uses an actual LED)\n#ifdef CONVERT_TO_PROTON_C_RXLED\n#    define D5 PAL_LINE(GPIOC, 14)\n#    define B0 PAL_LINE(GPIOC, 13)\n#else\n#    define D5 PAL_LINE(GPIOC, 13)\n#    define B0 PAL_LINE(GPIOC, 14)\n#endif\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_proton_c/converter.mk",
    "content": "# Proton C MCU settings for converting AVR projects\nMCU := STM32F303\nBOARD := QMK_PROTON_C\nBOOTLOADER := stm32-dfu\n\n# These are defaults based on what has been implemented for ARM boards\nAUDIO_ENABLE ?= yes\nWS2812_DRIVER ?= bitbang\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_proton_c/post_converter.mk",
    "content": "BACKLIGHT_DRIVER ?= software\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_rp2040_ce/_pin_defs.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Left side (front)\n#define D3 0U\n#define D2 1U\n//      GND\n//      GND\n#define D1 2U\n#define D0 3U\n#define D4 4U\n#define C6 5U\n#define D7 6U\n#define E6 7U\n#define B4 8U\n#define B5 9U\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 29U\n#define F5 28U\n#define F6 27U\n#define F7 26U\n#define B1 22U\n#define B3 20U\n#define B2 23U\n#define B6 21U\n\n// LEDs\n#define D5 12U\n#define B0 13U\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_rp2040_ce/converter.mk",
    "content": "# rp2040_ce MCU settings for converting AVR projects\nMCU := RP2040\nBOARD := QMK_PM2040\nBOOTLOADER := rp2040\n\n# These are defaults based on what has been implemented for RP2040 boards\nSERIAL_DRIVER ?= vendor\nWS2812_DRIVER ?= vendor\nBACKLIGHT_DRIVER ?= software\nOPT_DEFS += -DUSB_VBUS_PIN=19U\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_sparkfun_pm2040/_pin_defs.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Left side (front)\n#define D3 0U\n#define D2 1U\n//      GND\n//      GND\n#define D1 2U\n#define D0 3U\n#define D4 4U\n#define C6 5U\n#define D7 6U\n#define E6 7U\n#define B4 8U\n#define B5 9U\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 29U\n#define F5 28U\n#define F6 27U\n#define F7 26U\n#define B1 22U\n#define B3 20U\n#define B2 23U\n#define B6 21U\n\n// LEDs (Mapped to QT connector to avoid collisions with button/neopixel)\n#define D5 17U\n#define B0 16U\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_sparkfun_pm2040/converter.mk",
    "content": "# Sparkfun Pro Micro RP2040 MCU settings for converting AVR projects\nMCU := RP2040\nBOARD := QMK_PM2040\nBOOTLOADER := rp2040\n\n# These are defaults based on what has been implemented for RP2040 boards\nSERIAL_DRIVER ?= vendor\nWS2812_DRIVER ?= vendor\nBACKLIGHT_DRIVER ?= software\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_stemcell/_pin_defs.h",
    "content": "// Copyright 2022 Mega Mind (@megamind4089)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Pindefs for v2.0.0\n// https://megamind4089.github.io/STeMCell/pinout/\n\n// Left side (front)\n#ifdef STEMCELL_UART_SWAP\n#    define D3 PAL_LINE(GPIOA, 3)\n#    define D2 PAL_LINE(GPIOA, 2)\n#else\n#    define D3 PAL_LINE(GPIOA, 2)\n#    define D2 PAL_LINE(GPIOA, 3)\n#endif\n//      GND\n//      GND\n#ifdef STEMCELL_I2C_SWAP\n#    define D1 PAL_LINE(GPIOB, 6)\n#    define D0 PAL_LINE(GPIOB, 7)\n#else\n#    define D1 PAL_LINE(GPIOB, 7)\n#    define D0 PAL_LINE(GPIOB, 6)\n#endif\n\n#define D4 PAL_LINE(GPIOA, 15)\n#define C6 PAL_LINE(GPIOB, 3)\n#define D7 PAL_LINE(GPIOB, 4)\n#define E6 PAL_LINE(GPIOB, 5)\n#define B4 PAL_LINE(GPIOB, 8)\n#define B5 PAL_LINE(GPIOB, 9)\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 PAL_LINE(GPIOB, 10)\n#define F5 PAL_LINE(GPIOB, 2)\n#define F6 PAL_LINE(GPIOB, 1)\n#define F7 PAL_LINE(GPIOB, 0)\n\n#define B1 PAL_LINE(GPIOA, 5)\n#define B3 PAL_LINE(GPIOA, 6)\n#define B2 PAL_LINE(GPIOA, 7)\n#define B6 PAL_LINE(GPIOA, 4)\n\n// LEDs\n#define D5 PAL_LINE(GPIOA, 8) // User LED\n#define B0 PAL_LINE(GPIOA, 9) // unconnected pin\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_stemcell/converter.mk",
    "content": "# Copyright 2022 Mega Mind (@megamind4089)\n# SPDX-License-Identifier: GPL-2.0-or-later\n\nMCU := STM32F411\nBOARD := STEMCELL\nBOOTLOADER := tinyuf2\n\nSERIAL_DRIVER ?= usart\nWS2812_DRIVER ?= bitbang\n\nifeq ($(strip $(STMC_US)), yes)\n  OPT_DEFS += -DSTEMCELL_UART_SWAP\nendif\n\nifeq ($(strip $(STMC_IS)), yes)\n  OPT_DEFS += -DSTEMCELL_I2C_SWAP\nendif\n\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_svlinky/_pin_defs.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Left side (front)\n#define D3 0U\n#define D2 1U\n//      GND\n//      GND\n#define D1 2U\n#define D0 3U\n#define D4 4U\n#define C6 5U\n#define D7 6U\n#define E6 7U\n#define B4 8U\n#define B5 9U\n\n// Right side (front)\n//      RAW\n//      GND\n//      RESET\n//      VCC\n#define F4 29U\n#define F5 28U\n#define F6 18U\n#define F7 24U\n#define B1 22U\n#define B3 20U\n#define B2 23U\n#define B6 21U\n\n// LEDs\n#define D5 17U\n#define B0 25U\n"
  },
  {
    "path": "platforms/chibios/converters/promicro_to_svlinky/converter.mk",
    "content": "# rp2040_ce MCU settings for converting AVR projects\nMCU := RP2040\nBOARD := QMK_PM2040\nBOOTLOADER := rp2040\n\n# These are defaults based on what has been implemented for RP2040 boards\nSERIAL_DRIVER ?= vendor\nWS2812_DRIVER ?= vendor\nBACKLIGHT_DRIVER ?= software\nOPT_DEFS += -DUSB_VBUS_PIN=19U\n"
  },
  {
    "path": "platforms/chibios/drivers/analog.c",
    "content": "/* Copyright 2019 Drew Mills\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"analog.h\"\n#include <ch.h>\n#include <hal.h>\n\n#if !HAL_USE_ADC\n#    error \"You need to set HAL_USE_ADC to TRUE in your halconf.h to use the ADC.\"\n#endif\n\n#if !RP_ADC_USE_ADC1 && !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4 && !WB32_ADC_USE_ADC1 && !AT32_ADC_USE_ADC1\n#    error \"You need to set one of the 'xxx_ADC_USE_ADCx' settings to TRUE in your mcuconf.h to use the ADC.\"\n#endif\n\n#if STM32_ADC_DUAL_MODE\n#    error \"STM32 ADC Dual Mode is not supported at this time.\"\n#endif\n\n#if STM32_ADCV3_OVERSAMPLING\n// Apparently all ADCV3 chips that support oversampling (STM32L4xx, STM32L4xx+,\n// STM32G4xx, STM32WB[35]x) have errata like “Wrong ADC result if conversion\n// done late after calibration or previous conversion”; the workaround is to\n// perform a dummy conversion and discard its result.  STM32G4xx chips also\n// have the “ADC channel 0 converted instead of the required ADC channel”\n// errata, one workaround for which is also to perform a dummy conversion.\n#    define ADC_DUMMY_CONVERSIONS_AT_START 1\n#else\n#    define ADC_DUMMY_CONVERSIONS_AT_START 0\n#endif\n\n// Otherwise assume V3\n#if defined(STM32F0XX) || defined(STM32L0XX) || defined(STM32G0XX)\n#    define USE_ADCV1\n#elif defined(STM32F1XX) || defined(STM32F2XX) || defined(STM32F4XX) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx) || defined(AT32F415)\n#    define USE_ADCV2\n#endif\n\n// BODGE to make v2 look like v1,3 and 4\n#if defined(USE_ADCV2) || defined(RP2040)\n#    if !defined(ADC_SMPR_SMP_1P5) && defined(ADC_SAMPLE_3)\n#        define ADC_SMPR_SMP_1P5 ADC_SAMPLE_3\n#        define ADC_SMPR_SMP_7P5 ADC_SAMPLE_15\n#        define ADC_SMPR_SMP_13P5 ADC_SAMPLE_28\n#        define ADC_SMPR_SMP_28P5 ADC_SAMPLE_56\n#        define ADC_SMPR_SMP_41P5 ADC_SAMPLE_84\n#        define ADC_SMPR_SMP_55P5 ADC_SAMPLE_112\n#        define ADC_SMPR_SMP_71P5 ADC_SAMPLE_144\n#        define ADC_SMPR_SMP_239P5 ADC_SAMPLE_480\n#    endif\n\n#    if !defined(ADC_SMPR_SMP_1P5) && defined(ADC_SAMPLE_1P5)\n#        define ADC_SMPR_SMP_1P5 ADC_SAMPLE_1P5\n#        define ADC_SMPR_SMP_7P5 ADC_SAMPLE_7P5\n#        define ADC_SMPR_SMP_13P5 ADC_SAMPLE_13P5\n#        define ADC_SMPR_SMP_28P5 ADC_SAMPLE_28P5\n#        define ADC_SMPR_SMP_41P5 ADC_SAMPLE_41P5\n#        define ADC_SMPR_SMP_55P5 ADC_SAMPLE_55P5\n#        define ADC_SMPR_SMP_71P5 ADC_SAMPLE_71P5\n#        define ADC_SMPR_SMP_239P5 ADC_SAMPLE_239P5\n#    endif\n\n// we still sample at 12bit, but scale down to the requested bit range\n#    define ADC_CFGR1_RES_12BIT 12\n#    define ADC_CFGR1_RES_10BIT 10\n#    define ADC_CFGR1_RES_8BIT 8\n#    define ADC_CFGR1_RES_6BIT 6\n#endif\n\n/* User configurable ADC options */\n#ifndef ADC_COUNT\n#    if defined(RP2040) || defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F4XX) || defined(STM32G0XX) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx) || defined(AT32F415)\n#        define ADC_COUNT 1\n#    elif defined(STM32F3XX) || defined(STM32G4XX)\n#        define ADC_COUNT 4\n#    elif defined(STM32L4XX)\n#        define ADC_COUNT 3\n#    else\n#        error \"ADC_COUNT has not been set for this ARM microcontroller.\"\n#    endif\n#endif\n\n#ifndef ADC_NUM_CHANNELS\n#    define ADC_NUM_CHANNELS 1\n#elif ADC_NUM_CHANNELS != 1\n#    error \"The ARM ADC implementation currently only supports reading one channel at a time.\"\n#endif\n\n// Add dummy conversions as extra channels (this would work only on chips that\n// have multiple channel index fields instead of a channel mask, but all chips\n// that need that workaround are like that).\n#define ADC_TOTAL_CHANNELS (ADC_DUMMY_CONVERSIONS_AT_START + ADC_NUM_CHANNELS)\n\n#ifndef ADC_BUFFER_DEPTH\n#    define ADC_BUFFER_DEPTH 1\n#endif\n\n// For more sampling rate options, look at hal_adc_lld.h in ChibiOS\n#if !defined(ADC_SAMPLING_RATE) && !defined(RP2040)\n#    if defined(ADC_SMPR_SMP_1P5)\n#        define ADC_SAMPLING_RATE ADC_SMPR_SMP_1P5\n#    elif defined(ADC_SMPR_SMP_2P5) // STM32L4XX, STM32L4XXP, STM32G4XX, STM32WBXX\n#        define ADC_SAMPLING_RATE ADC_SMPR_SMP_2P5\n#    elif defined(ADC_SMPR_SMP1_1P5) // STM32G0XX\n#        define ADC_SAMPLING_RATE ADC_SMPR_SMP1_1P5\n#    else\n#        error \"Cannot determine the default ADC_SAMPLING_RATE for this MCU.\"\n#    endif\n#endif\n\n// Options are 12, 10, 8, and 6 bit.\n#ifndef ADC_RESOLUTION\n#    ifdef ADC_CFGR_RES_10BITS // ADCv3, ADCv4\n#        define ADC_RESOLUTION ADC_CFGR_RES_10BITS\n#    else // ADCv1, ADCv5, or the bodge for ADCv2 above\n#        define ADC_RESOLUTION ADC_CFGR1_RES_10BIT\n#    endif\n#endif\n\nstatic ADCConfig   adcCfg = {};\nstatic adcsample_t sampleBuffer[ADC_TOTAL_CHANNELS * ADC_BUFFER_DEPTH];\n\n// Initialize to max number of ADCs, set to empty object to initialize all to false.\nstatic bool adcInitialized[ADC_COUNT] = {};\n\n// TODO: add back TR handling???\nstatic ADCConversionGroup adcConversionGroup = {\n    .circular     = FALSE,\n    .num_channels = (uint16_t)(ADC_TOTAL_CHANNELS),\n#if defined(USE_ADCV1)\n    .cfgr1 = ADC_CFGR1_CONT | ADC_RESOLUTION,\n    .smpr  = ADC_SAMPLING_RATE,\n#elif defined(USE_ADCV2)\n#    if !defined(STM32F1XX) && !defined(GD32VF103) && !defined(WB32F3G71xx) && !defined(WB32FQ95xx) && !defined(AT32F415)\n    .cr2  = ADC_CR2_SWSTART, // F103 seem very unhappy with, F401 seems very unhappy without...\n#    endif\n#    if defined(AT32F415)\n    .spt2 = ADC_SPT2_CSPT_AN0(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN1(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN2(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN3(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN4(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN5(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN6(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN7(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN8(ADC_SAMPLING_RATE) | ADC_SPT2_CSPT_AN9(ADC_SAMPLING_RATE),\n    .spt1 = ADC_SPT1_CSPT_AN10(ADC_SAMPLING_RATE) | ADC_SPT1_CSPT_AN11(ADC_SAMPLING_RATE) | ADC_SPT1_CSPT_AN12(ADC_SAMPLING_RATE) | ADC_SPT1_CSPT_AN13(ADC_SAMPLING_RATE) | ADC_SPT1_CSPT_AN14(ADC_SAMPLING_RATE) | ADC_SPT1_CSPT_AN15(ADC_SAMPLING_RATE),\n#    else\n    .smpr2 = ADC_SMPR2_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN9(ADC_SAMPLING_RATE),\n    .smpr1 = ADC_SMPR1_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN15(ADC_SAMPLING_RATE),\n#    endif\n#elif defined(RP2040)\n// RP2040 does not have any extra config here\n#else\n    .cfgr = ADC_CFGR_CONT | ADC_RESOLUTION,\n    .smpr = {ADC_SMPR1_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN9(ADC_SAMPLING_RATE), ADC_SMPR2_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN15(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN16(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN17(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN18(ADC_SAMPLING_RATE)},\n#endif\n};\n\n// clang-format off\n__attribute__((weak)) adc_mux pinToMux(pin_t pin) {\n    switch (pin) {\n#if defined(STM32F0XX)\n        case A0:  return TO_MUX( 0,  0 );\n        case A1:  return TO_MUX( 1,  0 );\n        case A2:  return TO_MUX( 2,  0 );\n        case A3:  return TO_MUX( 3,  0 );\n        case A4:  return TO_MUX( 4,  0 );\n        case A5:  return TO_MUX( 5,  0 );\n        case A6:  return TO_MUX( 6,  0 );\n        case A7:  return TO_MUX( 7,  0 );\n        case B0:  return TO_MUX( 8,  0 );\n        case B1:  return TO_MUX( 9,  0 );\n        case C0:  return TO_MUX( 10, 0 );\n        case C1:  return TO_MUX( 11, 0 );\n        case C2:  return TO_MUX( 12, 0 );\n        case C3:  return TO_MUX( 13, 0 );\n        case C4:  return TO_MUX( 14, 0 );\n        case C5:  return TO_MUX( 15, 0 );\n#elif defined(STM32F3XX)\n        case A0:  return TO_MUX( ADC_CHANNEL_IN1,  0 );\n        case A1:  return TO_MUX( ADC_CHANNEL_IN2,  0 );\n        case A2:  return TO_MUX( ADC_CHANNEL_IN3,  0 );\n        case A3:  return TO_MUX( ADC_CHANNEL_IN4,  0 );\n        case A4:  return TO_MUX( ADC_CHANNEL_IN1,  1 );\n        case A5:  return TO_MUX( ADC_CHANNEL_IN2,  1 );\n        case A6:  return TO_MUX( ADC_CHANNEL_IN3,  1 );\n        case A7:  return TO_MUX( ADC_CHANNEL_IN4,  1 );\n        case B0:  return TO_MUX( ADC_CHANNEL_IN12, 2 );\n        case B1:  return TO_MUX( ADC_CHANNEL_IN1,  2 );\n        case B2:  return TO_MUX( ADC_CHANNEL_IN12, 1 );\n        case B12: return TO_MUX( ADC_CHANNEL_IN3,  3 );\n        case B13: return TO_MUX( ADC_CHANNEL_IN5,  2 );\n        case B14: return TO_MUX( ADC_CHANNEL_IN4,  3 );\n        case B15: return TO_MUX( ADC_CHANNEL_IN5,  3 );\n        case C0:  return TO_MUX( ADC_CHANNEL_IN6,  0 ); // Can also be ADC2\n        case C1:  return TO_MUX( ADC_CHANNEL_IN7,  0 ); // Can also be ADC2\n        case C2:  return TO_MUX( ADC_CHANNEL_IN8,  0 ); // Can also be ADC2\n        case C3:  return TO_MUX( ADC_CHANNEL_IN9,  0 ); // Can also be ADC2\n        case C4:  return TO_MUX( ADC_CHANNEL_IN5,  1 );\n        case C5:  return TO_MUX( ADC_CHANNEL_IN11, 1 );\n        case D8:  return TO_MUX( ADC_CHANNEL_IN12, 3 );\n        case D9:  return TO_MUX( ADC_CHANNEL_IN13, 3 );\n        case D10: return TO_MUX( ADC_CHANNEL_IN7,  2 ); // Can also be ADC4\n        case D11: return TO_MUX( ADC_CHANNEL_IN8,  2 ); // Can also be ADC4\n        case D12: return TO_MUX( ADC_CHANNEL_IN9,  2 ); // Can also be ADC4\n        case D13: return TO_MUX( ADC_CHANNEL_IN10, 2 ); // Can also be ADC4\n        case D14: return TO_MUX( ADC_CHANNEL_IN11, 2 ); // Can also be ADC4\n        case E7:  return TO_MUX( ADC_CHANNEL_IN13, 2 );\n        case E8:  return TO_MUX( ADC_CHANNEL_IN6,  2 ); // Can also be ADC4\n        case E9:  return TO_MUX( ADC_CHANNEL_IN2,  2 );\n        case E10: return TO_MUX( ADC_CHANNEL_IN14, 2 );\n        case E11: return TO_MUX( ADC_CHANNEL_IN15, 2 );\n        case E12: return TO_MUX( ADC_CHANNEL_IN16, 2 );\n        case E13: return TO_MUX( ADC_CHANNEL_IN3,  2 );\n        case E14: return TO_MUX( ADC_CHANNEL_IN1,  3 );\n        case E15: return TO_MUX( ADC_CHANNEL_IN2,  3 );\n        case F2:  return TO_MUX( ADC_CHANNEL_IN10, 0 ); // Can also be ADC2\n        case F4:  return TO_MUX( ADC_CHANNEL_IN5,  0 );\n#elif defined(STM32F4XX)\n        case A0:  return TO_MUX( ADC_CHANNEL_IN0,  0 );\n        case A1:  return TO_MUX( ADC_CHANNEL_IN1,  0 );\n        case A2:  return TO_MUX( ADC_CHANNEL_IN2,  0 );\n        case A3:  return TO_MUX( ADC_CHANNEL_IN3,  0 );\n        case A4:  return TO_MUX( ADC_CHANNEL_IN4,  0 );\n        case A5:  return TO_MUX( ADC_CHANNEL_IN5,  0 );\n        case A6:  return TO_MUX( ADC_CHANNEL_IN6,  0 );\n        case A7:  return TO_MUX( ADC_CHANNEL_IN7,  0 );\n        case B0:  return TO_MUX( ADC_CHANNEL_IN8,  0 );\n        case B1:  return TO_MUX( ADC_CHANNEL_IN9,  0 );\n        case C0:  return TO_MUX( ADC_CHANNEL_IN10, 0 );\n        case C1:  return TO_MUX( ADC_CHANNEL_IN11, 0 );\n        case C2:  return TO_MUX( ADC_CHANNEL_IN12, 0 );\n        case C3:  return TO_MUX( ADC_CHANNEL_IN13, 0 );\n        case C4:  return TO_MUX( ADC_CHANNEL_IN14, 0 );\n        case C5:  return TO_MUX( ADC_CHANNEL_IN15, 0 );\n#    if STM32_ADC_USE_ADC3\n        case F3:  return TO_MUX( ADC_CHANNEL_IN9,  2 );\n        case F4:  return TO_MUX( ADC_CHANNEL_IN14, 2 );\n        case F5:  return TO_MUX( ADC_CHANNEL_IN15, 2 );\n        case F6:  return TO_MUX( ADC_CHANNEL_IN4,  2 );\n        case F7:  return TO_MUX( ADC_CHANNEL_IN5,  2 );\n        case F8:  return TO_MUX( ADC_CHANNEL_IN6,  2 );\n        case F9:  return TO_MUX( ADC_CHANNEL_IN7,  2 );\n        case F10: return TO_MUX( ADC_CHANNEL_IN8,  2 );\n#    endif\n#elif defined(STM32F1XX) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx) || defined(AT32F415)\n        case A0:  return TO_MUX( ADC_CHANNEL_IN0,  0 );\n        case A1:  return TO_MUX( ADC_CHANNEL_IN1,  0 );\n        case A2:  return TO_MUX( ADC_CHANNEL_IN2,  0 );\n        case A3:  return TO_MUX( ADC_CHANNEL_IN3,  0 );\n        case A4:  return TO_MUX( ADC_CHANNEL_IN4,  0 );\n        case A5:  return TO_MUX( ADC_CHANNEL_IN5,  0 );\n        case A6:  return TO_MUX( ADC_CHANNEL_IN6,  0 );\n        case A7:  return TO_MUX( ADC_CHANNEL_IN7,  0 );\n        case B0:  return TO_MUX( ADC_CHANNEL_IN8,  0 );\n        case B1:  return TO_MUX( ADC_CHANNEL_IN9,  0 );\n        case C0:  return TO_MUX( ADC_CHANNEL_IN10, 0 );\n        case C1:  return TO_MUX( ADC_CHANNEL_IN11, 0 );\n        case C2:  return TO_MUX( ADC_CHANNEL_IN12, 0 );\n        case C3:  return TO_MUX( ADC_CHANNEL_IN13, 0 );\n        case C4:  return TO_MUX( ADC_CHANNEL_IN14, 0 );\n        case C5:  return TO_MUX( ADC_CHANNEL_IN15, 0 );\n        // STM32F103x[C-G] in 144-pin packages also have analog inputs on F6...F10, but they are on ADC3, and the\n        // ChibiOS ADC driver for STM32F1xx currently supports only ADC1, therefore these pins are not usable.\n#elif defined(STM32L4XX)\n        case A0: return TO_MUX( ADC_CHANNEL_IN5,  0 ); // Can also be ADC2 in some cases\n        case A1: return TO_MUX( ADC_CHANNEL_IN6,  0 ); // Can also be ADC2 in some cases\n        case A2: return TO_MUX( ADC_CHANNEL_IN7,  0 ); // Can also be ADC2\n        case A3: return TO_MUX( ADC_CHANNEL_IN8,  0 ); // Can also be ADC2\n        case A4: return TO_MUX( ADC_CHANNEL_IN9,  0 ); // Can also be ADC2\n        case A5: return TO_MUX( ADC_CHANNEL_IN10, 0 ); // Can also be ADC2\n        case A6: return TO_MUX( ADC_CHANNEL_IN11, 0 ); // Can also be ADC2\n        case A7: return TO_MUX( ADC_CHANNEL_IN12, 0 ); // Can also be ADC2\n        case B0: return TO_MUX( ADC_CHANNEL_IN15, 0 ); // Can also be ADC2\n        case B1: return TO_MUX( ADC_CHANNEL_IN16, 0 ); // Can also be ADC2\n        case C0: return TO_MUX( ADC_CHANNEL_IN1,  0 ); // Can also be ADC2 or ADC3\n        case C1: return TO_MUX( ADC_CHANNEL_IN2,  0 ); // Can also be ADC2 or ADC3\n        case C2: return TO_MUX( ADC_CHANNEL_IN3,  0 ); // Can also be ADC2 or ADC3\n        case C3: return TO_MUX( ADC_CHANNEL_IN4,  0 ); // Can also be ADC2 or ADC3\n        case C4: return TO_MUX( ADC_CHANNEL_IN13, 0 ); // Can also be ADC2\n        case C5: return TO_MUX( ADC_CHANNEL_IN14, 0 ); // Can also be ADC2\n#    if STM32_HAS_GPIOF && STM32_ADC_USE_ADC3\n        case F3:  return TO_MUX( ADC_CHANNEL_IN6,  2 );\n        case F4:  return TO_MUX( ADC_CHANNEL_IN7,  2 );\n        case F5:  return TO_MUX( ADC_CHANNEL_IN8,  2 );\n        case F6:  return TO_MUX( ADC_CHANNEL_IN9,  2 );\n        case F7:  return TO_MUX( ADC_CHANNEL_IN10, 2 );\n        case F8:  return TO_MUX( ADC_CHANNEL_IN11, 2 );\n        case F9:  return TO_MUX( ADC_CHANNEL_IN12, 2 );\n        case F10: return TO_MUX( ADC_CHANNEL_IN13, 2 );\n#    endif\n#elif defined(STM32G0XX)\n        case A0:  return TO_MUX( 0,  0 );\n        case A1:  return TO_MUX( 1,  0 );\n        case A2:  return TO_MUX( 2,  0 );\n        case A3:  return TO_MUX( 3,  0 );\n        case A4:  return TO_MUX( 4,  0 );\n        case A5:  return TO_MUX( 5,  0 );\n        case A6:  return TO_MUX( 6,  0 );\n        case A7:  return TO_MUX( 7,  0 );\n        case B0:  return TO_MUX( 8,  0 );\n        case B1:  return TO_MUX( 9,  0 );\n        case B2:  return TO_MUX( 10, 0 );\n        case B10: return TO_MUX( 11, 0 );\n        case B11: return TO_MUX( 15, 0 );\n        case B12: return TO_MUX( 16, 0 );\n        case C4:  return TO_MUX( 17, 0 );\n        case C5:  return TO_MUX( 18, 0 );\n#elif defined(STM32G4XX)\n        case A0:  return TO_MUX( ADC_CHANNEL_IN1,  0 ); // Can also be ADC2\n        case A1:  return TO_MUX( ADC_CHANNEL_IN2,  0 ); // Can also be ADC2\n        case A2:  return TO_MUX( ADC_CHANNEL_IN3,  0 );\n        case A3:  return TO_MUX( ADC_CHANNEL_IN4,  0 );\n        case A4:  return TO_MUX( ADC_CHANNEL_IN17, 1 );\n        case A5:  return TO_MUX( ADC_CHANNEL_IN13, 1 );\n        case A6:  return TO_MUX( ADC_CHANNEL_IN3,  1 );\n        case A7:  return TO_MUX( ADC_CHANNEL_IN4,  1 );\n        case B0:  return TO_MUX( ADC_CHANNEL_IN15, 0 ); // Can also be ADC3\n        case B1:  return TO_MUX( ADC_CHANNEL_IN12, 0 ); // Can also be ADC3\n        case B2:  return TO_MUX( ADC_CHANNEL_IN12, 1 );\n        case B11: return TO_MUX( ADC_CHANNEL_IN14, 0 ); // Can also be ADC2\n        case B12: return TO_MUX( ADC_CHANNEL_IN11, 0 ); // Can also be ADC4\n        case B13: return TO_MUX( ADC_CHANNEL_IN5,  2 );\n        case B14: return TO_MUX( ADC_CHANNEL_IN5,  0 ); // Can also be ADC4\n        case B15: return TO_MUX( ADC_CHANNEL_IN15, 1 ); // Can also be ADC4\n        case C0:  return TO_MUX( ADC_CHANNEL_IN6,  0 ); // Can also be ADC2\n        case C1:  return TO_MUX( ADC_CHANNEL_IN7,  0 ); // Can also be ADC2\n        case C2:  return TO_MUX( ADC_CHANNEL_IN8,  0 ); // Can also be ADC2\n        case C3:  return TO_MUX( ADC_CHANNEL_IN9,  0 ); // Can also be ADC2\n        case C4:  return TO_MUX( ADC_CHANNEL_IN5,  1 );\n        case C5:  return TO_MUX( ADC_CHANNEL_IN11, 1 );\n        case D8:  return TO_MUX( ADC_CHANNEL_IN12, 3 );\n        case D9:  return TO_MUX( ADC_CHANNEL_IN13, 3 );\n        case D10: return TO_MUX( ADC_CHANNEL_IN7,  2 ); // Can also be ADC4\n        case D11: return TO_MUX( ADC_CHANNEL_IN8,  2 ); // Can also be ADC4\n        case D12: return TO_MUX( ADC_CHANNEL_IN9,  2 ); // Can also be ADC4\n        case D13: return TO_MUX( ADC_CHANNEL_IN10, 2 ); // Can also be ADC4\n        case D14: return TO_MUX( ADC_CHANNEL_IN11, 2 ); // Can also be ADC4\n        case E5:  return TO_MUX( ADC_CHANNEL_IN2,  3 );\n        case E7:  return TO_MUX( ADC_CHANNEL_IN4,  2 );\n        case E8:  return TO_MUX( ADC_CHANNEL_IN6,  2 ); // Can also be ADC4\n        case E9:  return TO_MUX( ADC_CHANNEL_IN2,  2 );\n        case E10: return TO_MUX( ADC_CHANNEL_IN14, 2 ); // Can also be ADC4\n        case E11: return TO_MUX( ADC_CHANNEL_IN15, 2 ); // Can also be ADC4\n        case E12: return TO_MUX( ADC_CHANNEL_IN16, 2 ); // Can also be ADC4\n        case E13: return TO_MUX( ADC_CHANNEL_IN3,  2 );\n        case E14: return TO_MUX( ADC_CHANNEL_IN1,  3 );\n        case F0:  return TO_MUX( ADC_CHANNEL_IN10, 0 );\n        case F1:  return TO_MUX( ADC_CHANNEL_IN10, 1 );\n#elif defined(RP2040)\n        case 26U: return TO_MUX(0, 0);\n        case 27U: return TO_MUX(1, 0);\n        case 28U: return TO_MUX(2, 0);\n        case 29U: return TO_MUX(3, 0);\n#endif\n    }\n\n    // return an adc that would never be used so intToADCDriver will bail out\n    return TO_MUX(0, 0xFF);\n}\n// clang-format on\n\nstatic inline ADCDriver* intToADCDriver(uint8_t adcInt) {\n    switch (adcInt) {\n#if RP_ADC_USE_ADC1 || STM32_ADC_USE_ADC1 || WB32_ADC_USE_ADC1 || AT32_ADC_USE_ADC1\n        case 0:\n            return &ADCD1;\n#endif\n#if STM32_ADC_USE_ADC2\n        case 1:\n            return &ADCD2;\n#endif\n#if STM32_ADC_USE_ADC3\n        case 2:\n            return &ADCD3;\n#endif\n#if STM32_ADC_USE_ADC4\n        case 3:\n            return &ADCD4;\n#endif\n    }\n\n    return NULL;\n}\n\nstatic inline void manageAdcInitializationDriver(uint8_t adc, ADCDriver* adcDriver) {\n    if (!adcInitialized[adc]) {\n        adcStart(adcDriver, &adcCfg);\n        adcInitialized[adc] = true;\n    }\n}\n\nint16_t analogReadPin(pin_t pin) {\n    palSetLineMode(pin, PAL_MODE_INPUT_ANALOG);\n\n    return adc_read(pinToMux(pin));\n}\n\nint16_t analogReadPinAdc(pin_t pin, uint8_t adc) {\n    palSetLineMode(pin, PAL_MODE_INPUT_ANALOG);\n\n    adc_mux target = pinToMux(pin);\n    target.adc     = adc;\n    return adc_read(target);\n}\n\nint16_t adc_read(adc_mux mux) {\n#if defined(USE_ADCV1)\n    // TODO: fix previous assumption of only 1 input...\n    adcConversionGroup.chselr = 1 << mux.input; /*no macro to convert N to ADC_CHSELR_CHSEL1*/\n#elif defined(USE_ADCV2)\n#    if defined(AT32F415)\n    adcConversionGroup.osq3 = ADC_OSQ3_OSN1_N(mux.input);\n#    else\n    adcConversionGroup.sqr3 = ADC_SQR3_SQ1_N(mux.input);\n#    endif\n#elif defined(RP2040)\n    adcConversionGroup.channel_mask = 1 << mux.input;\n#else\n    adcConversionGroup.sqr[0] = ADC_SQR1_SQ1_N(mux.input)\n#    if ADC_DUMMY_CONVERSIONS_AT_START >= 1\n                                | ADC_SQR1_SQ2_N(mux.input)\n#    endif\n        ;\n#endif\n\n    ADCDriver* targetDriver = intToADCDriver(mux.adc);\n    if (!targetDriver) {\n        return 0;\n    }\n\n    manageAdcInitializationDriver(mux.adc, targetDriver);\n    if (adcConvert(targetDriver, &adcConversionGroup, &sampleBuffer[0], ADC_BUFFER_DEPTH) != MSG_OK) {\n        return 0;\n    }\n\n#if defined(USE_ADCV2) || defined(RP2040)\n    // fake 12-bit -> N-bit scale\n    return (sampleBuffer[ADC_DUMMY_CONVERSIONS_AT_START]) >> (12 - ADC_RESOLUTION);\n#else\n    // already handled as part of adcConvert\n    return sampleBuffer[ADC_DUMMY_CONVERSIONS_AT_START];\n#endif\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/analog.h",
    "content": "/* Copyright 2019 Drew Mills\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include \"gpio.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct {\n    uint16_t input;\n    uint8_t  adc;\n} adc_mux;\n#define TO_MUX(i, a) \\\n    (adc_mux) {      \\\n        i, a         \\\n    }\n\nint16_t analogReadPin(pin_t pin);\nint16_t analogReadPinAdc(pin_t pin, uint8_t adc);\nadc_mux pinToMux(pin_t pin);\n\nint16_t adc_read(adc_mux mux);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/audio_dac.h",
    "content": "/* Copyright 2019 Jack Humbert\n * Copyright 2020 JohSchneider\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#ifndef A4\n#    define A4 PAL_LINE(GPIOA, 4)\n#endif\n#ifndef A5\n#    define A5 PAL_LINE(GPIOA, 5)\n#endif\n\n/**\n * Highest value allowed sample value.\n\n * since the DAC is limited to 12 bit, the absolute max is 0xfff = 4095U;\n * lower values adjust the peak-voltage aka volume down.\n * adjusting this value has only an effect on a sample-buffer whose values are\n * are NOT pregenerated - see square-wave\n */\n#ifndef AUDIO_DAC_SAMPLE_MAX\n#    define AUDIO_DAC_SAMPLE_MAX 4095U\n#endif\n\n#if !defined(AUDIO_DAC_SAMPLE_RATE) && !defined(AUDIO_MAX_SIMULTANEOUS_TONES) && !defined(AUDIO_DAC_QUALITY_VERY_LOW) && !defined(AUDIO_DAC_QUALITY_LOW) && !defined(AUDIO_DAC_QUALITY_HIGH) && !defined(AUDIO_DAC_QUALITY_VERY_HIGH)\n#    define AUDIO_DAC_QUALITY_SANE_MINIMUM\n#endif\n\n/**\n * These presets allow you to quickly switch between quality settings for\n * the DAC. The sample rate and maximum number of simultaneous tones roughly\n * has an inverse relationship - slightly higher sample rates may be possible.\n *\n * NOTE: a high sample-rate results in a higher cpu-load, which might lead to\n *       (audible) discontinuities and/or starve other processes of cpu-time\n *       (like RGB-led back-lighting, ...)\n */\n#ifdef AUDIO_DAC_QUALITY_VERY_LOW\n#    define AUDIO_DAC_SAMPLE_RATE 11025U\n#    define AUDIO_MAX_SIMULTANEOUS_TONES 8\n#endif\n\n#ifdef AUDIO_DAC_QUALITY_LOW\n#    define AUDIO_DAC_SAMPLE_RATE 22050U\n#    define AUDIO_MAX_SIMULTANEOUS_TONES 4\n#endif\n\n#ifdef AUDIO_DAC_QUALITY_HIGH\n#    define AUDIO_DAC_SAMPLE_RATE 44100U\n#    define AUDIO_MAX_SIMULTANEOUS_TONES 2\n#endif\n\n#ifdef AUDIO_DAC_QUALITY_VERY_HIGH\n#    define AUDIO_DAC_SAMPLE_RATE 88200U\n#    define AUDIO_MAX_SIMULTANEOUS_TONES 1\n#endif\n\n#ifdef AUDIO_DAC_QUALITY_SANE_MINIMUM\n/* a sane-minimum config: with a trade-off between cpu-load and tone-range\n *\n * the (currently) highest defined note is NOTE_B8 with 7902Hz; if we now\n * aim for an even even multiple of the buffer-size, we end up with:\n * ( roundUptoPow2(highest note / AUDIO_DAC_BUFFER_SIZE) * nyquist-rate * AUDIO_DAC_BUFFER_SIZE)\n *                              7902/256 = 30.867        *       2      * 256 ~= 16384\n * which works out (but the 'scope shows some sampling artifacts with lower harmonics :-P)\n */\n#    define AUDIO_DAC_SAMPLE_RATE 16384U\n#    define AUDIO_MAX_SIMULTANEOUS_TONES 8\n#endif\n\n/**\n * Effective bit-rate of the DAC. 44.1khz is the standard for most audio - any\n * lower will sacrifice perceptible audio quality. Any higher will limit the\n * number of simultaneous tones. In most situations, a tenth (1/10) of the\n * sample rate is where notes become unbearable.\n */\n#ifndef AUDIO_DAC_SAMPLE_RATE\n#    define AUDIO_DAC_SAMPLE_RATE 44100U\n#endif\n\n/**\n * Size of the dac_buffer array. This controls the length of the runtime buffer\n * which accumulates the data to be sent to the DAC every few milliseconds, and\n * it does not need to correspond to the length of the wavetable for the chosen\n * waveform defined by AUDIO_DAC_SAMPLE_WAVEFORM_* in the additive DAC driver.\n * By default, this is set to be as close to 3.3 ms as possible, giving 300 DAC\n * interrupts per second. Any smaller and the interrupt load gets too heavy and\n * this results in crackling due to buffer underrun in the additive DAC driver;\n * too large and the RAM (additive driver) or flash (basic driver) usage may be\n * too high, causing build failures, and matrix scanning is liable to have long\n * periodic pauses that delay key presses or releases or fully lose short taps.\n * Large buffers also cause notes to take longer to stop after they should from\n * music mode or MIDI input.\n * This should be a power of 2 for maximum compatibility.\n */\n#ifndef AUDIO_DAC_BUFFER_SIZE\n#    if AUDIO_DAC_SAMPLE_RATE < 5100U\n#        define AUDIO_DAC_BUFFER_SIZE 16U\n#    elif AUDIO_DAC_SAMPLE_RATE < 9900U\n#        define AUDIO_DAC_BUFFER_SIZE 32U\n#    elif AUDIO_DAC_SAMPLE_RATE < 19500U\n#        define AUDIO_DAC_BUFFER_SIZE 64U\n#    elif AUDIO_DAC_SAMPLE_RATE < 38700U\n#        define AUDIO_DAC_BUFFER_SIZE 128U\n#    else\n#        define AUDIO_DAC_BUFFER_SIZE 256U\n#    endif\n#endif\n\n/**\n * The number of tones that can be played simultaneously. If too high a value\n * is used here, the keyboard will freeze and glitch-out when that many tones\n * are being played.\n */\n#ifndef AUDIO_MAX_SIMULTANEOUS_TONES\n#    define AUDIO_MAX_SIMULTANEOUS_TONES 2\n#endif\n\n/**\n * The default value of the DAC when not playing anything. Certain hardware\n * setups may require a high (AUDIO_DAC_SAMPLE_MAX) or low (0) value here.\n * Since multiple added sine waves tend to oscillate around the midpoint,\n * and possibly never/rarely reach either 0 of MAX, 1/2 MAX can be a\n * reasonable default value.\n */\n#ifndef AUDIO_DAC_OFF_VALUE\n#    define AUDIO_DAC_OFF_VALUE AUDIO_DAC_SAMPLE_MAX / 2\n#endif\n\n#if AUDIO_DAC_OFF_VALUE > AUDIO_DAC_SAMPLE_MAX\n#    error \"AUDIO_DAC: OFF_VALUE may not be larger than SAMPLE_MAX\"\n#endif\n\n/**\n *user overridable sample generation/processing\n */\nuint16_t dac_value_generate(void);\n"
  },
  {
    "path": "platforms/chibios/drivers/audio_dac_additive.c",
    "content": "/* Copyright 2016-2019 Jack Humbert\n * Copyright 2020 JohSchneider\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"audio.h\"\n#include \"gpio.h\"\n#include <math.h>\n#include \"util.h\"\n\n// Need to disable GCC's \"tautological-compare\" warning for this file, as it causes issues when running `KEEP_INTERMEDIATES=yes`. Corresponding pop at the end of the file.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wtautological-compare\"\n\n/*\n  Audio Driver: DAC\n\n  which utilizes the dac unit many STM32 are equipped with, to output a modulated waveform from samples stored in the dac_buffer_* array who are passed to the hardware through DMA\n\n  it is also possible to have a custom sample-LUT by implementing/overriding 'dac_value_generate'\n\n  this driver allows for multiple simultaneous tones to be played through one single channel by doing additive wave-synthesis\n*/\n\n#if !defined(AUDIO_PIN)\n#    error \"Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC additive)' for available options.\"\n#endif\n#if defined(AUDIO_PIN_ALT) && !defined(AUDIO_PIN_ALT_AS_NEGATIVE)\n#    pragma message \"Audio feature: AUDIO_PIN_ALT set, but not AUDIO_PIN_ALT_AS_NEGATIVE - pin will be left unused; audio might still work though.\"\n#endif\n\n#if !defined(AUDIO_PIN_ALT)\n// no ALT pin defined is valid, but the c-ifs below need some value set\n#    define AUDIO_PIN_ALT PAL_NOLINE\n#endif\n\n#if !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)\n#    define AUDIO_DAC_SAMPLE_WAVEFORM_SINE\n#endif\n\n#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SINE\n/* one full sine wave over [0,2*pi], but shifted up one amplitude and left pi/4; for the samples to start at 0\n */\nstatic const dacsample_t dac_buffer_sine[] = {\n    // 256 values, max 4095\n    0x0,   0x1,   0x2,   0x6,   0xa,   0xf,   0x16,  0x1e,  0x27,  0x32,  0x3d,  0x4a,  0x58,  0x67,  0x78,  0x89,  0x9c,  0xb0,  0xc5,  0xdb,  0xf2,  0x10a, 0x123, 0x13e, 0x159, 0x175, 0x193, 0x1b1, 0x1d1, 0x1f1, 0x212, 0x235, 0x258, 0x27c, 0x2a0, 0x2c6, 0x2ed, 0x314, 0x33c, 0x365, 0x38e, 0x3b8, 0x3e3, 0x40e, 0x43a, 0x467, 0x494, 0x4c2, 0x4f0, 0x51f, 0x54e, 0x57d, 0x5ad, 0x5dd, 0x60e, 0x63f, 0x670, 0x6a1, 0x6d3, 0x705, 0x737, 0x769, 0x79b, 0x7cd, 0x800, 0x832, 0x864, 0x896, 0x8c8, 0x8fa, 0x92c, 0x95e, 0x98f, 0x9c0, 0x9f1, 0xa22, 0xa52, 0xa82, 0xab1, 0xae0, 0xb0f, 0xb3d, 0xb6b, 0xb98, 0xbc5, 0xbf1, 0xc1c, 0xc47, 0xc71, 0xc9a, 0xcc3, 0xceb, 0xd12, 0xd39, 0xd5f, 0xd83, 0xda7, 0xdca, 0xded, 0xe0e, 0xe2e, 0xe4e, 0xe6c, 0xe8a, 0xea6, 0xec1, 0xedc, 0xef5, 0xf0d, 0xf24, 0xf3a, 0xf4f, 0xf63, 0xf76, 0xf87, 0xf98, 0xfa7, 0xfb5, 0xfc2, 0xfcd, 0xfd8, 0xfe1, 0xfe9, 0xff0, 0xff5, 0xff9, 0xffd, 0xffe,\n    0xfff, 0xffe, 0xffd, 0xff9, 0xff5, 0xff0, 0xfe9, 0xfe1, 0xfd8, 0xfcd, 0xfc2, 0xfb5, 0xfa7, 0xf98, 0xf87, 0xf76, 0xf63, 0xf4f, 0xf3a, 0xf24, 0xf0d, 0xef5, 0xedc, 0xec1, 0xea6, 0xe8a, 0xe6c, 0xe4e, 0xe2e, 0xe0e, 0xded, 0xdca, 0xda7, 0xd83, 0xd5f, 0xd39, 0xd12, 0xceb, 0xcc3, 0xc9a, 0xc71, 0xc47, 0xc1c, 0xbf1, 0xbc5, 0xb98, 0xb6b, 0xb3d, 0xb0f, 0xae0, 0xab1, 0xa82, 0xa52, 0xa22, 0x9f1, 0x9c0, 0x98f, 0x95e, 0x92c, 0x8fa, 0x8c8, 0x896, 0x864, 0x832, 0x800, 0x7cd, 0x79b, 0x769, 0x737, 0x705, 0x6d3, 0x6a1, 0x670, 0x63f, 0x60e, 0x5dd, 0x5ad, 0x57d, 0x54e, 0x51f, 0x4f0, 0x4c2, 0x494, 0x467, 0x43a, 0x40e, 0x3e3, 0x3b8, 0x38e, 0x365, 0x33c, 0x314, 0x2ed, 0x2c6, 0x2a0, 0x27c, 0x258, 0x235, 0x212, 0x1f1, 0x1d1, 0x1b1, 0x193, 0x175, 0x159, 0x13e, 0x123, 0x10a, 0xf2,  0xdb,  0xc5,  0xb0,  0x9c,  0x89,  0x78,  0x67,  0x58,  0x4a,  0x3d,  0x32,  0x27,  0x1e,  0x16,  0xf,   0xa,   0x6,   0x2,   0x1,\n};\n#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SINE\n#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE\nstatic const dacsample_t dac_buffer_triangle[] = {\n    // 256 values, max 4095\n    0x0,   0x20,  0x40,  0x60,  0x80,  0xa0,  0xc0,  0xe0,  0x100, 0x120, 0x140, 0x160, 0x180, 0x1a0, 0x1c0, 0x1e0, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0x400, 0x420, 0x440, 0x460, 0x480, 0x4a0, 0x4c0, 0x4e0, 0x500, 0x520, 0x540, 0x560, 0x580, 0x5a0, 0x5c0, 0x5e0, 0x600, 0x620, 0x640, 0x660, 0x680, 0x6a0, 0x6c0, 0x6e0, 0x700, 0x720, 0x740, 0x760, 0x780, 0x7a0, 0x7c0, 0x7e0, 0x800, 0x81f, 0x83f, 0x85f, 0x87f, 0x89f, 0x8bf, 0x8df, 0x8ff, 0x91f, 0x93f, 0x95f, 0x97f, 0x99f, 0x9bf, 0x9df, 0x9ff, 0xa1f, 0xa3f, 0xa5f, 0xa7f, 0xa9f, 0xabf, 0xadf, 0xaff, 0xb1f, 0xb3f, 0xb5f, 0xb7f, 0xb9f, 0xbbf, 0xbdf, 0xbff, 0xc1f, 0xc3f, 0xc5f, 0xc7f, 0xc9f, 0xcbf, 0xcdf, 0xcff, 0xd1f, 0xd3f, 0xd5f, 0xd7f, 0xd9f, 0xdbf, 0xddf, 0xdff, 0xe1f, 0xe3f, 0xe5f, 0xe7f, 0xe9f, 0xebf, 0xedf, 0xeff, 0xf1f, 0xf3f, 0xf5f, 0xf7f, 0xf9f, 0xfbf, 0xfdf,\n    0xfff, 0xfdf, 0xfbf, 0xf9f, 0xf7f, 0xf5f, 0xf3f, 0xf1f, 0xeff, 0xedf, 0xebf, 0xe9f, 0xe7f, 0xe5f, 0xe3f, 0xe1f, 0xdff, 0xddf, 0xdbf, 0xd9f, 0xd7f, 0xd5f, 0xd3f, 0xd1f, 0xcff, 0xcdf, 0xcbf, 0xc9f, 0xc7f, 0xc5f, 0xc3f, 0xc1f, 0xbff, 0xbdf, 0xbbf, 0xb9f, 0xb7f, 0xb5f, 0xb3f, 0xb1f, 0xaff, 0xadf, 0xabf, 0xa9f, 0xa7f, 0xa5f, 0xa3f, 0xa1f, 0x9ff, 0x9df, 0x9bf, 0x99f, 0x97f, 0x95f, 0x93f, 0x91f, 0x8ff, 0x8df, 0x8bf, 0x89f, 0x87f, 0x85f, 0x83f, 0x81f, 0x800, 0x7e0, 0x7c0, 0x7a0, 0x780, 0x760, 0x740, 0x720, 0x700, 0x6e0, 0x6c0, 0x6a0, 0x680, 0x660, 0x640, 0x620, 0x600, 0x5e0, 0x5c0, 0x5a0, 0x580, 0x560, 0x540, 0x520, 0x500, 0x4e0, 0x4c0, 0x4a0, 0x480, 0x460, 0x440, 0x420, 0x400, 0x3e0, 0x3c0, 0x3a0, 0x380, 0x360, 0x340, 0x320, 0x300, 0x2e0, 0x2c0, 0x2a0, 0x280, 0x260, 0x240, 0x220, 0x200, 0x1e0, 0x1c0, 0x1a0, 0x180, 0x160, 0x140, 0x120, 0x100, 0xe0,  0xc0,  0xa0,  0x80,  0x60,  0x40,  0x20,\n};\n#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE\n#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE\nstatic const dacsample_t dac_buffer_square[] = {\n    AUDIO_DAC_OFF_VALUE,  // first and\n    AUDIO_DAC_SAMPLE_MAX, // second steps\n};\n#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE\n/*\n// four steps: 0, 1/3, 2/3 and 1\nstatic const dacsample_t dac_buffer_staircase[] = {\n    0,\n    AUDIO_DAC_SAMPLE_MAX / 3,\n    2 * AUDIO_DAC_SAMPLE_MAX / 3,\n    AUDIO_DAC_SAMPLE_MAX,\n}\n*/\n#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID\nstatic const dacsample_t dac_buffer_trapezoid[] = {\n    0x0,   0x1f,  0x7f,  0xdf,  0x13f, 0x19f, 0x1ff, 0x25f, 0x2bf, 0x31f, 0x37f, 0x3df, 0x43f, 0x49f, 0x4ff, 0x55f, 0x5bf, 0x61f, 0x67f, 0x6df, 0x73f, 0x79f, 0x7ff, 0x85f, 0x8bf, 0x91f, 0x97f, 0x9df, 0xa3f, 0xa9f, 0xaff, 0xb5f, 0xbbf, 0xc1f, 0xc7f, 0xcdf, 0xd3f, 0xd9f, 0xdff, 0xe5f, 0xebf, 0xf1f, 0xf7f, 0xfdf, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff,\n    0xfff, 0xfdf, 0xf7f, 0xf1f, 0xebf, 0xe5f, 0xdff, 0xd9f, 0xd3f, 0xcdf, 0xc7f, 0xc1f, 0xbbf, 0xb5f, 0xaff, 0xa9f, 0xa3f, 0x9df, 0x97f, 0x91f, 0x8bf, 0x85f, 0x7ff, 0x79f, 0x73f, 0x6df, 0x67f, 0x61f, 0x5bf, 0x55f, 0x4ff, 0x49f, 0x43f, 0x3df, 0x37f, 0x31f, 0x2bf, 0x25f, 0x1ff, 0x19f, 0x13f, 0xdf,  0x7f,  0x1f,  0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,   0x0,\n};\n#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID\n\nstatic dacsample_t dac_buffer[AUDIO_DAC_BUFFER_SIZE];\n\n/* keep track of the sample position for for each frequency */\nstatic float dac_if[AUDIO_MAX_SIMULTANEOUS_TONES] = {0.0};\n\nstatic float   active_tones_snapshot[AUDIO_MAX_SIMULTANEOUS_TONES] = {0};\nstatic uint8_t active_tones_snapshot_length                        = 0;\n\ntypedef enum {\n    OUTPUT_SHOULD_START,\n    OUTPUT_RUN_NORMALLY,\n    // path 1: wait for zero, then change/update active tones\n    OUTPUT_TONES_CHANGED,\n    OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE,\n    // path 2: hardware should stop, wait for zero then turn output off = stop the timer\n    OUTPUT_SHOULD_STOP,\n    OUTPUT_REACHED_ZERO_BEFORE_OFF,\n    OUTPUT_OFF,\n    OUTPUT_OFF_1,\n    OUTPUT_OFF_2, // trailing off: giving the DAC two more conversion cycles until the AUDIO_DAC_OFF_VALUE reaches the output, then turn the timer off, which leaves the output at that level\n    number_of_output_states\n} output_states_t;\noutput_states_t state = OUTPUT_OFF_2;\n\n/**\n * Generation of the waveform being passed to the callback. Declared weak so users\n * can override it with their own wave-forms/noises.\n */\n__attribute__((weak)) uint16_t dac_value_generate(void) {\n    // DAC is running/asking for values but snapshot length is zero -> must be playing a pause\n    if (active_tones_snapshot_length == 0) {\n        return AUDIO_DAC_OFF_VALUE;\n    }\n\n    /* doing additive wave synthesis over all currently playing tones = adding up\n     * sine-wave-samples for each frequency, scaled by the number of active tones\n     */\n    uint_fast16_t value     = 0;\n    float         frequency = 0.0f;\n\n#if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE)\n    const size_t wavetable_length = ARRAY_SIZE(dac_buffer_sine);\n#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE)\n    const size_t wavetable_length = ARRAY_SIZE(dac_buffer_triangle);\n#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)\n    const size_t wavetable_length = ARRAY_SIZE(dac_buffer_trapezoid);\n#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE)\n    const size_t wavetable_length = ARRAY_SIZE(dac_buffer_square);\n#endif\n\n    for (size_t i = 0; i < active_tones_snapshot_length; i++) {\n        /* Note: a user implementation does not have to rely on the active_tones_snapshot, but\n         * could directly query the active frequencies through audio_get_processed_frequency */\n        frequency = active_tones_snapshot[i];\n\n        float new_dac_if = dac_if[i];\n        new_dac_if += frequency * ((float)wavetable_length / AUDIO_DAC_SAMPLE_RATE * 2.0f / 3.0f);\n        /*Note: the 2/3 are necessary to get the correct frequencies on the\n         *      DAC output (as measured with an oscilloscope), since the gpt\n         *      timer runs with 3*AUDIO_DAC_SAMPLE_RATE; and the DAC callback\n         *      is called twice per conversion.*/\n\n        while (new_dac_if >= wavetable_length)\n            new_dac_if -= wavetable_length;\n        dac_if[i] = new_dac_if;\n\n        // Wavetable generation/lookup\n        size_t dac_i = (size_t)new_dac_if;\n\n#if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE)\n        value += dac_buffer_sine[dac_i] / active_tones_snapshot_length;\n#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE)\n        value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length;\n#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID)\n        value += dac_buffer_trapezoid[dac_i] / active_tones_snapshot_length;\n#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE)\n        value += dac_buffer_square[dac_i] / active_tones_snapshot_length;\n#endif\n        /*\n        // SINE\n        value += dac_buffer_sine[dac_i] / active_tones_snapshot_length / 3;\n        // TRIANGLE\n        value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length / 3;\n        // SQUARE\n        value += dac_buffer_square[dac_i] / active_tones_snapshot_length / 3;\n        //NOTE: combination of these three wave-forms is more exemplary - and doesn't sound particularly good :-P\n        */\n\n        // STAIRS (mostly usefully as test-pattern)\n        // value_avg = dac_buffer_staircase[dac_i] / active_tones_snapshot_length;\n    }\n\n    return value;\n}\n\n/**\n * DAC streaming callback. Does all of the main computing for playing songs.\n *\n * Note: chibios calls this CB twice: during the 'half buffer event', and the 'full buffer event'.\n */\nstatic void dac_end(DACDriver *dacp) {\n    dacsample_t *sample_p = (dacp)->samples;\n\n    // work on the other half of the buffer\n    if (dacIsBufferComplete(dacp)) {\n        sample_p += AUDIO_DAC_BUFFER_SIZE / 2; // 'half_index'\n    }\n\n    for (uint8_t s = 0; s < AUDIO_DAC_BUFFER_SIZE / 2; s++) {\n        if (OUTPUT_OFF <= state) {\n            sample_p[s] = AUDIO_DAC_OFF_VALUE;\n            continue;\n        } else {\n            sample_p[s] = dac_value_generate();\n        }\n\n        /* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX)\n         * ============================*=*========================== AUDIO_DAC_SAMPLE_MAX\n         *                          *       *\n         *                        *           *\n         * ---------------------------------------------------------\n         *                     *                 *                  } AUDIO_DAC_SAMPLE_MAX/100\n         * --------------------------------------------------------- AUDIO_DAC_OFF_VALUE\n         *                  *                       *               } AUDIO_DAC_SAMPLE_MAX/100\n         * ---------------------------------------------------------\n         *               *\n         * *           *\n         *   *       *\n         * =====*=*================================================= 0x0\n         */\n        if (((sample_p[s] + (AUDIO_DAC_SAMPLE_MAX / 100)) > AUDIO_DAC_OFF_VALUE) && // value approaches from below\n            (sample_p[s] < (AUDIO_DAC_OFF_VALUE + (AUDIO_DAC_SAMPLE_MAX / 100)))    // or above\n        ) {\n            if ((OUTPUT_SHOULD_START == state) && (active_tones_snapshot_length > 0)) {\n                state = OUTPUT_RUN_NORMALLY;\n            } else if (OUTPUT_TONES_CHANGED == state) {\n                state = OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE;\n            } else if (OUTPUT_SHOULD_STOP == state) {\n                state = OUTPUT_REACHED_ZERO_BEFORE_OFF;\n            }\n        }\n\n        // still 'ramping up', reset the output to OFF_VALUE until the generated values reach that value, to do a smooth handover\n        if (OUTPUT_SHOULD_START == state) {\n            sample_p[s] = AUDIO_DAC_OFF_VALUE;\n        }\n\n        if ((OUTPUT_SHOULD_START == state) || (OUTPUT_REACHED_ZERO_BEFORE_OFF == state) || (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state)) {\n            uint8_t active_tones         = MIN(AUDIO_MAX_SIMULTANEOUS_TONES, audio_get_number_of_active_tones());\n            active_tones_snapshot_length = 0;\n            // update the snapshot - once, and only on occasion that something changed;\n            // -> saves cpu cycles (?)\n            for (uint8_t i = 0; i < active_tones; i++) {\n                float freq = audio_get_processed_frequency(i);\n                if (freq > 0) { // disregard 'rest' notes, with valid frequency 0.0f; which would only lower the resulting waveform volume during the additive synthesis step\n                    active_tones_snapshot[active_tones_snapshot_length++] = freq;\n                }\n            }\n\n            if ((0 == active_tones_snapshot_length) && (OUTPUT_REACHED_ZERO_BEFORE_OFF == state)) {\n                state = OUTPUT_OFF;\n            }\n            if (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state) {\n                state = OUTPUT_RUN_NORMALLY;\n            }\n        }\n    }\n\n    // update audio internal state (note position, current_note, ...)\n    if (audio_update_state()) {\n        if (OUTPUT_SHOULD_STOP != state) {\n            state = OUTPUT_TONES_CHANGED;\n        }\n    }\n\n    if (OUTPUT_OFF <= state) {\n        if (OUTPUT_OFF_2 == state) {\n            // stopping timer6 = stopping the DAC at whatever value it is currently pushing to the output = AUDIO_DAC_OFF_VALUE\n            gptStopTimer(&GPTD6);\n        } else {\n            state++;\n        }\n    }\n}\n\nstatic void dac_error(DACDriver *dacp, dacerror_t err) {\n    (void)dacp;\n    (void)err;\n\n    chSysHalt(\"DAC failure. halp\");\n}\n\nstatic const GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE * 3,\n                                   .callback  = NULL,\n                                   .cr2       = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event.  */\n                                   .dier      = 0U};\n\nstatic const DACConfig dac_conf = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};\n\n/**\n * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered\n * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency\n * to be a third of what we expect.\n *\n * Here are all the values for DAC_TRG (TSEL in the ref manual)\n * TIM15_TRGO 0b011\n * TIM2_TRGO  0b100\n * TIM3_TRGO  0b001\n * TIM6_TRGO  0b000\n * TIM7_TRGO  0b010\n * EXTI9      0b110\n * SWTRIG     0b111\n */\nstatic const DACConversionGroup dac_conv_cfg = {.num_channels = 1U, .end_cb = dac_end, .error_cb = dac_error, .trigger = DAC_TRG(0b000)};\n\nvoid audio_driver_initialize_impl(void) {\n    if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {\n        palSetLineMode(A4, PAL_MODE_INPUT_ANALOG);\n        dacStart(&DACD1, &dac_conf);\n    }\n    if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {\n        palSetLineMode(A5, PAL_MODE_INPUT_ANALOG);\n        dacStart(&DACD2, &dac_conf);\n    }\n\n    /* enable the output buffer, to directly drive external loads with no additional circuitry\n     *\n     * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers\n     * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer\n     * Note: enabling the output buffer imparts an additional dc-offset of a couple mV\n     *\n     * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet\n     * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'\n     */\n    DACD1.params->dac->CR &= ~DAC_CR_BOFF1;\n    DACD2.params->dac->CR &= ~DAC_CR_BOFF2;\n\n    /* Start the DAC output with all off values. This buffer will then get fed\n     * with samples from dac_end, which will play notes.\n     */\n    for (size_t i = 0; i < AUDIO_DAC_BUFFER_SIZE; i++) {\n        dac_buffer[i] = AUDIO_DAC_OFF_VALUE;\n    }\n\n    if (AUDIO_PIN == A4) {\n        dacStartConversion(&DACD1, &dac_conv_cfg, dac_buffer, AUDIO_DAC_BUFFER_SIZE);\n    } else if (AUDIO_PIN == A5) {\n        dacStartConversion(&DACD2, &dac_conv_cfg, dac_buffer, AUDIO_DAC_BUFFER_SIZE);\n    }\n\n    // no inverted/out-of-phase waveform (yet?), only pulling AUDIO_PIN_ALT to AUDIO_DAC_OFF_VALUE\n#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)\n    if (AUDIO_PIN_ALT == A4) {\n        dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);\n    } else if (AUDIO_PIN_ALT == A5) {\n        dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);\n    }\n#endif\n\n    gptStart(&GPTD6, &gpt6cfg1);\n}\n\nvoid audio_driver_stop_impl(void) {\n    state = OUTPUT_SHOULD_STOP;\n}\n\nvoid audio_driver_start_impl(void) {\n    gptStartContinuous(&GPTD6, 2U);\n\n    for (uint8_t i = 0; i < AUDIO_MAX_SIMULTANEOUS_TONES; i++) {\n        dac_if[i]                = 0.0f;\n        active_tones_snapshot[i] = 0.0f;\n    }\n    active_tones_snapshot_length = 0;\n    state                        = OUTPUT_SHOULD_START;\n}\n\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "platforms/chibios/drivers/audio_dac_basic.c",
    "content": "/* Copyright 2016-2020 Jack Humbert\n * Copyright 2020 JohSchneider\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"audio.h\"\n#include \"gpio.h\"\n\n// Need to disable GCC's \"tautological-compare\" warning for this file, as it causes issues when running `KEEP_INTERMEDIATES=yes`. Corresponding pop at the end of the file.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wtautological-compare\"\n\n/*\n  Audio Driver: DAC\n\n  which utilizes both channels of the DAC unit many STM32 are equipped with to output a modulated square-wave, from precomputed samples stored in a buffer, which is passed to the hardware through DMA\n\n  this driver can either be used to drive to separate speakers, wired to A4+Gnd and A5+Gnd, which allows two tones to be played simultaneously\n  OR\n  one speaker wired to A4+A5 with the AUDIO_PIN_ALT_AS_NEGATIVE define set - see docs/feature_audio\n\n*/\n\n#if !defined(AUDIO_PIN)\n#    pragma message \"Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC basic)' for available options.\"\n// TODO: make this an 'error' instead; go through a breaking change, and add AUDIO_PIN A5 to all keyboards currently using AUDIO on STM32 based boards? - for now: set the define here\n#    define AUDIO_PIN A5\n#endif\n// check configuration for ONE speaker, connected to both DAC pins\n#if defined(AUDIO_PIN_ALT_AS_NEGATIVE) && !defined(AUDIO_PIN_ALT)\n#    error \"Audio feature: AUDIO_PIN_ALT_AS_NEGATIVE set, but no pin configured as AUDIO_PIN_ALT\"\n#endif\n\n#ifndef AUDIO_PIN_ALT\n// no ALT pin defined is valid, but the c-ifs below need some value set\n#    define AUDIO_PIN_ALT -1\n#endif\n\n#if !defined(AUDIO_STATE_TIMER)\n#    define AUDIO_STATE_TIMER GPTD8\n#endif\n\n// square-wave\nstatic const dacsample_t dac_buffer_1[AUDIO_DAC_BUFFER_SIZE] = {\n    // First half is max, second half is 0\n    [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1]                     = AUDIO_DAC_SAMPLE_MAX,\n    [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = 0,\n};\n\n// square-wave\nstatic const dacsample_t dac_buffer_2[AUDIO_DAC_BUFFER_SIZE] = {\n    // opposite of dac_buffer above\n    [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1]                     = 0,\n    [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX,\n};\n\nGPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,\n                      .callback  = NULL,\n                      .cr2       = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event.    */\n                      .dier      = 0U};\nGPTConfig gpt7cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE,\n                      .callback  = NULL,\n                      .cr2       = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event.    */\n                      .dier      = 0U};\n\nstatic void gpt_audio_state_cb(GPTDriver *gptp);\nGPTConfig   gptStateUpdateCfg = {.frequency = 10,\n                               .callback  = gpt_audio_state_cb,\n                               .cr2       = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event.    */\n                               .dier      = 0U};\n\nstatic const DACConfig dac_conf_ch1 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};\nstatic const DACConfig dac_conf_ch2 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT};\n\n/**\n * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered\n * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency\n * to be a third of what we expect.\n *\n * Here are all the values for DAC_TRG (TSEL in the ref manual)\n * TIM15_TRGO 0b011\n * TIM2_TRGO  0b100\n * TIM3_TRGO  0b001\n * TIM6_TRGO  0b000\n * TIM7_TRGO  0b010\n * EXTI9      0b110\n * SWTRIG     0b111\n */\nstatic const DACConversionGroup dac_conv_grp_ch1 = {.num_channels = 1U, .trigger = DAC_TRG(0b000)};\nstatic const DACConversionGroup dac_conv_grp_ch2 = {.num_channels = 1U, .trigger = DAC_TRG(0b010)};\n\nvoid channel_1_start(void) {\n    gptStart(&GPTD6, &gpt6cfg1);\n    gptStartContinuous(&GPTD6, 2U);\n    palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);\n}\n\nvoid channel_1_stop(void) {\n    gptStopTimer(&GPTD6);\n    palSetPadMode(GPIOA, 4, PAL_MODE_OUTPUT_PUSHPULL);\n    palSetPad(GPIOA, 4);\n}\n\nstatic float channel_1_frequency = 0.0f;\nvoid         channel_1_set_frequency(float freq) {\n    channel_1_frequency = freq;\n\n    channel_1_stop();\n    if (freq <= 0.0) // a pause/rest has freq=0\n        return;\n\n    gpt6cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;\n    channel_1_start();\n}\nfloat channel_1_get_frequency(void) {\n    return channel_1_frequency;\n}\n\nvoid channel_2_start(void) {\n    gptStart(&GPTD7, &gpt7cfg1);\n    gptStartContinuous(&GPTD7, 2U);\n    palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);\n}\n\nvoid channel_2_stop(void) {\n    gptStopTimer(&GPTD7);\n    palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL);\n    palSetPad(GPIOA, 5);\n}\n\nstatic float channel_2_frequency = 0.0f;\nvoid         channel_2_set_frequency(float freq) {\n    channel_2_frequency = freq;\n\n    channel_2_stop();\n    if (freq <= 0.0) // a pause/rest has freq=0\n        return;\n\n    gpt7cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE;\n    channel_2_start();\n}\nfloat channel_2_get_frequency(void) {\n    return channel_2_frequency;\n}\n\nstatic void gpt_audio_state_cb(GPTDriver *gptp) {\n    if (audio_update_state()) {\n#if defined(AUDIO_PIN_ALT_AS_NEGATIVE)\n        // one piezo/speaker connected to both audio pins, the generated square-waves are inverted\n        channel_1_set_frequency(audio_get_processed_frequency(0));\n        channel_2_set_frequency(audio_get_processed_frequency(0));\n\n#else // two separate audio outputs/speakers\n      // primary speaker on A4, optional secondary on A5\n        if (AUDIO_PIN == A4) {\n            channel_1_set_frequency(audio_get_processed_frequency(0));\n            if (AUDIO_PIN_ALT == A5) {\n                if (audio_get_number_of_active_tones() > 1) {\n                    channel_2_set_frequency(audio_get_processed_frequency(1));\n                } else {\n                    channel_2_stop();\n                }\n            }\n        }\n\n        // primary speaker on A5, optional secondary on A4\n        if (AUDIO_PIN == A5) {\n            channel_2_set_frequency(audio_get_processed_frequency(0));\n            if (AUDIO_PIN_ALT == A4) {\n                if (audio_get_number_of_active_tones() > 1) {\n                    channel_1_set_frequency(audio_get_processed_frequency(1));\n                } else {\n                    channel_1_stop();\n                }\n            }\n        }\n#endif\n    }\n}\n\nvoid audio_driver_initialize_impl(void) {\n    if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {\n        palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG);\n        dacStart(&DACD1, &dac_conf_ch1);\n\n        // initial setup of the dac-triggering timer is still required, even\n        // though it gets reconfigured and restarted later on\n        gptStart(&GPTD6, &gpt6cfg1);\n    }\n\n    if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {\n        palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG);\n        dacStart(&DACD2, &dac_conf_ch2);\n\n        gptStart(&GPTD7, &gpt7cfg1);\n    }\n\n    /* enable the output buffer, to directly drive external loads with no additional circuitry\n     *\n     * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers\n     * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer\n     * Note: enabling the output buffer imparts an additional dc-offset of a couple mV\n     *\n     * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet\n     * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.'\n     */\n    DACD1.params->dac->CR &= ~DAC_CR_BOFF1;\n    DACD2.params->dac->CR &= ~DAC_CR_BOFF2;\n\n    // start state-updater\n    gptStart(&AUDIO_STATE_TIMER, &gptStateUpdateCfg);\n}\n\nvoid audio_driver_stop_impl(void) {\n    if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {\n        gptStopTimer(&GPTD6);\n\n        // stop the ongoing conversion and put the output in a known state\n        dacStopConversion(&DACD1);\n        dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE);\n    }\n\n    if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {\n        gptStopTimer(&GPTD7);\n\n        dacStopConversion(&DACD2);\n        dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE);\n    }\n    gptStopTimer(&AUDIO_STATE_TIMER);\n}\n\nvoid audio_driver_start_impl(void) {\n    if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) {\n        dacStartConversion(&DACD1, &dac_conv_grp_ch1, (dacsample_t *)dac_buffer_1, AUDIO_DAC_BUFFER_SIZE);\n    }\n    if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) {\n        dacStartConversion(&DACD2, &dac_conv_grp_ch2, (dacsample_t *)dac_buffer_2, AUDIO_DAC_BUFFER_SIZE);\n    }\n    gptStartContinuous(&AUDIO_STATE_TIMER, 2U);\n}\n\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "platforms/chibios/drivers/audio_pwm.h",
    "content": "/* Copyright 2020 Jack Humbert\n * Copyright 2020 JohSchneider\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#if !defined(AUDIO_PWM_DRIVER)\n// NOTE: Timer2 seems to be used otherwise in QMK, otherwise we could default to A5 (= TIM2_CH1, with PWMD2 and alternate-function(1))\n#    define AUDIO_PWM_DRIVER PWMD1\n#endif\n\n#if !defined(AUDIO_PWM_CHANNEL)\n// NOTE: sticking to the STM data-sheet numbering: TIMxCH1 to TIMxCH4\n// default: STM32F303CC PA8+TIM1_CH1 -> 1\n#    define AUDIO_PWM_CHANNEL 1\n#endif\n\n#if !defined(AUDIO_PWM_PAL_MODE)\n// pin-alternate function: see the data-sheet for which pin needs what AF to connect to TIMx_CHy\n// default: STM32F303CC PA8+TIM1_CH1 -> 6\n#    define AUDIO_PWM_PAL_MODE 6\n#endif\n\n#if !defined(AUDIO_STATE_TIMER)\n// timer used to trigger updates in the audio-system, configured/enabled in chibios mcuconf.\n// Tim6 is the default for \"larger\" STMs, smaller ones might not have this one (enabled) and need to switch to a different one (e.g.: STM32F103 has only Tim1-Tim4)\n#    define AUDIO_STATE_TIMER GPTD6\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/audio_pwm_hardware.c",
    "content": "// Copyright 2022 Stefan Kerkmann\n// Copyright 2020 Jack Humbert\n// Copyright 2020 JohSchneider\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n// Audio Driver: PWM the duty-cycle is always kept at 50%, and the pwm-period is\n// adjusted to match the frequency of a note to be played back. This driver uses\n// the chibios-PWM system to produce a square-wave on specific output pins that\n// are connected to the PWM hardware. The hardware directly toggles the pin via\n// its alternate function. see your MCUs data-sheet for which pin can be driven\n// by what timer - looking for TIMx_CHy and the corresponding alternate\n// function.\n\n#include \"audio.h\"\n#include \"gpio.h\"\n\n#if !defined(AUDIO_PIN)\n#    error \"Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings\"\n#endif\n\n#if !defined(AUDIO_PWM_COUNTER_FREQUENCY)\n#    define AUDIO_PWM_COUNTER_FREQUENCY 100000\n#endif\n\n#ifndef AUDIO_PWM_COMPLEMENTARY_OUTPUT\n#    define AUDIO_PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_HIGH\n#else\n#    define AUDIO_PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH\n#endif\n\nextern bool    playing_note;\nextern bool    playing_melody;\nextern uint8_t note_timbre;\n\nstatic PWMConfig pwmCFG = {.frequency = AUDIO_PWM_COUNTER_FREQUENCY, /* PWM clock frequency  */\n                           .period    = 2,\n                           .callback  = NULL,\n                           .channels  = {[(AUDIO_PWM_CHANNEL - 1)] = {.mode = AUDIO_PWM_OUTPUT_MODE, .callback = NULL}}};\n\nstatic float channel_1_frequency = 0.0f;\n\nvoid channel_1_set_frequency(float freq) {\n    channel_1_frequency = freq;\n    pwmcnt_t period;\n    pwmcnt_t width;\n\n    if (freq <= 0.0) {\n        period = 2;\n        width  = 0;\n    } else {\n        period = (pwmCFG.frequency / freq);\n        width  = (pwmcnt_t)(((period) * (pwmcnt_t)((100 - note_timbre) * 100)) / (pwmcnt_t)(10000));\n    }\n    chSysLockFromISR();\n    pwmChangePeriodI(&AUDIO_PWM_DRIVER, period);\n    pwmEnableChannelI(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1, width);\n    chSysUnlockFromISR();\n}\n\nfloat channel_1_get_frequency(void) {\n    return channel_1_frequency;\n}\n\nvoid channel_1_start(void) {\n    pwmStop(&AUDIO_PWM_DRIVER);\n    pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);\n}\n\nvoid channel_1_stop(void) {\n    pwmStop(&AUDIO_PWM_DRIVER);\n    pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);\n    pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1, 0);\n    pwmStop(&AUDIO_PWM_DRIVER);\n}\n\nstatic virtual_timer_t audio_vt;\nstatic void            audio_callback(virtual_timer_t *vtp, void *p);\n\n// a regular timer task, that checks the note to be currently played and updates\n// the pwm to output that frequency.\nstatic void audio_callback(virtual_timer_t *vtp, void *p) {\n    float freq; // TODO: freq_alt\n\n    if (audio_update_state()) {\n        freq = audio_get_processed_frequency(0); // freq_alt would be index=1\n        channel_1_set_frequency(freq);\n    }\n\n    chSysLockFromISR();\n    chVTSetI(&audio_vt, TIME_MS2I(16), audio_callback, NULL);\n    chSysUnlockFromISR();\n}\n\nvoid audio_driver_initialize_impl(void) {\n    pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);\n\n    // connect the AUDIO_PIN to the PWM hardware\n#if defined(USE_GPIOV1) // STM32F103C8, RP2040\n    palSetLineMode(AUDIO_PIN, AUDIO_PWM_PAL_MODE);\n#else // GPIOv2 (or GPIOv3 for f4xx, which is the same/compatible at this command)\n    palSetLineMode(AUDIO_PIN, PAL_MODE_ALTERNATE(AUDIO_PWM_PAL_MODE));\n#endif\n\n    chVTObjectInit(&audio_vt);\n}\n\nvoid audio_driver_start_impl(void) {\n    channel_1_stop();\n    channel_1_start();\n\n    if ((playing_note || playing_melody) && !chVTIsArmed(&audio_vt)) {\n        // a whole note is one beat, which is - per definition in\n        // musical_notes.h - set to 64 the longest note is\n        // BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4 the tempo (which\n        // might vary!) is in bpm (beats per minute) therefore: if the timer\n        // ticks away at 64Hz (~16.6ms) audio_update_state is called just often\n        // enough to not miss any notes\n        chVTSet(&audio_vt, TIME_MS2I(16), audio_callback, NULL);\n    }\n}\n\nvoid audio_driver_stop_impl(void) {\n    channel_1_stop();\n    chVTReset(&audio_vt);\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/audio_pwm_software.c",
    "content": "/* Copyright 2020 Jack Humbert\n * Copyright 2020 JohSchneider\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\nAudio Driver: PWM\n\nthe duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back.\n\nthis driver uses the chibios-PWM system to produce a square-wave on any given output pin in software\n- a pwm callback is used to set/clear the configured pin.\n\n */\n#include \"audio.h\"\n#include \"gpio.h\"\n\n#if !defined(AUDIO_PIN)\n#    error \"Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings\"\n#endif\nextern bool    playing_note;\nextern bool    playing_melody;\nextern uint8_t note_timbre;\n\nstatic void pwm_audio_period_callback(PWMDriver *pwmp);\nstatic void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp);\n\nstatic PWMConfig pwmCFG = {\n    .frequency = 100000, /* PWM clock frequency  */\n    // CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime\n    .period   = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */\n    .callback = pwm_audio_period_callback,\n    .channels =\n        {\n            // software-PWM just needs another callback on any channel\n            {PWM_OUTPUT_ACTIVE_HIGH, pwm_audio_channel_interrupt_callback}, /* channel 0 -> TIMx_CH1 */\n            {PWM_OUTPUT_DISABLED, NULL},                                    /* channel 1 -> TIMx_CH2 */\n            {PWM_OUTPUT_DISABLED, NULL},                                    /* channel 2 -> TIMx_CH3 */\n            {PWM_OUTPUT_DISABLED, NULL}                                     /* channel 3 -> TIMx_CH4 */\n        },\n};\n\nstatic float channel_1_frequency = 0.0f;\nvoid         channel_1_set_frequency(float freq) {\n    channel_1_frequency = freq;\n\n    if (freq <= 0.0) // a pause/rest has freq=0\n        return;\n\n    pwmcnt_t period = (pwmCFG.frequency / freq);\n    pwmChangePeriod(&AUDIO_PWM_DRIVER, period);\n\n    pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,\n                     // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH\n                     PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));\n}\n\nfloat channel_1_get_frequency(void) {\n    return channel_1_frequency;\n}\n\nvoid channel_1_start(void) {\n    pwmStop(&AUDIO_PWM_DRIVER);\n    pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);\n\n    pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER);\n    pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);\n}\n\nvoid channel_1_stop(void) {\n    pwmStop(&AUDIO_PWM_DRIVER);\n\n    palClearLine(AUDIO_PIN); // leave the line low, after last note was played\n\n#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)\n    palClearLine(AUDIO_PIN_ALT); // leave the line low, after last note was played\n#endif\n}\n\n// generate a PWM signal on any pin, not necessarily the one connected to the timer\nstatic void pwm_audio_period_callback(PWMDriver *pwmp) {\n    (void)pwmp;\n    palClearLine(AUDIO_PIN);\n\n#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)\n    palSetLine(AUDIO_PIN_ALT);\n#endif\n}\nstatic void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp) {\n    (void)pwmp;\n    if (channel_1_frequency > 0) {\n        palSetLine(AUDIO_PIN); // generate a PWM signal on any pin, not necessarily the one connected to the timer\n#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)\n        palClearLine(AUDIO_PIN_ALT);\n#endif\n    }\n}\n\nstatic void gpt_callback(GPTDriver *gptp);\nGPTConfig   gptCFG = {\n    /* a whole note is one beat, which is - per definition in musical_notes.h - set to 64\n       the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4\n       the tempo (which might vary!) is in bpm (beats per minute)\n       therefore: if the timer ticks away at .frequency = (60*64)Hz,\n       and the .interval counts from 64 downwards - audio_update_state is\n       called just often enough to not miss anything\n    */\n    .frequency = 60 * 64,\n    .callback  = gpt_callback,\n};\n\nvoid audio_driver_initialize_impl(void) {\n    pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);\n\n    palSetLineMode(AUDIO_PIN, PAL_MODE_OUTPUT_PUSHPULL);\n    palClearLine(AUDIO_PIN);\n\n#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE)\n    palSetLineMode(AUDIO_PIN_ALT, PAL_MODE_OUTPUT_PUSHPULL);\n    palClearLine(AUDIO_PIN_ALT);\n#endif\n\n    pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); // enable pwm callbacks\n    pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1);\n\n    gptStart(&AUDIO_STATE_TIMER, &gptCFG);\n}\n\nvoid audio_driver_start_impl(void) {\n    channel_1_stop();\n    channel_1_start();\n\n    if (playing_note || playing_melody) {\n        gptStartContinuous(&AUDIO_STATE_TIMER, 64);\n    }\n}\n\nvoid audio_driver_stop_impl(void) {\n    channel_1_stop();\n    gptStopTimer(&AUDIO_STATE_TIMER);\n}\n\n/* a regular timer task, that checks the note to be currently played\n * and updates the pwm to output that frequency\n */\nstatic void gpt_callback(GPTDriver *gptp) {\n    float freq; // TODO: freq_alt\n\n    if (audio_update_state()) {\n        freq = audio_get_processed_frequency(0); // freq_alt would be index=1\n        channel_1_set_frequency(freq);\n    }\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/backlight_pwm.c",
    "content": "#include \"backlight.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n#include <hal.h>\n\n// Maximum duty cycle limit\n#ifndef BACKLIGHT_LIMIT_VAL\n#    define BACKLIGHT_LIMIT_VAL 255\n#endif\n\n#ifndef BACKLIGHT_PAL_MODE\n#    if defined(USE_GPIOV1)\n#        define BACKLIGHT_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    else\n// GPIOV2 && GPIOV3\n#        define BACKLIGHT_PAL_MODE 2\n#    endif\n#endif\n\n// GENERIC\n#ifndef BACKLIGHT_PWM_DRIVER\n#    define BACKLIGHT_PWM_DRIVER PWMD4\n#endif\n#ifndef BACKLIGHT_PWM_CHANNEL\n#    define BACKLIGHT_PWM_CHANNEL 3\n#endif\n\n// Support for pins which are on TIM1_CH1N\n#ifdef BACKLIGHT_PWM_COMPLEMENTARY_OUTPUT\n#    if BACKLIGHT_ON_STATE == 1\n#        define PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH;\n#    else\n#        define PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW;\n#    endif\n#else\n#    if BACKLIGHT_ON_STATE == 1\n#        define PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_HIGH;\n#    else\n#        define PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_LOW;\n#    endif\n#endif\n\n#ifndef BACKLIGHT_PWM_COUNTER_FREQUENCY\n#    define BACKLIGHT_PWM_COUNTER_FREQUENCY 0xFFFF\n#endif\n\n#ifndef BACKLIGHT_PWM_PERIOD\n#    define BACKLIGHT_PWM_PERIOD 256\n#endif\n\nstatic PWMConfig pwmCFG = {\n    .frequency = BACKLIGHT_PWM_COUNTER_FREQUENCY, /* PWM clock frequency  */\n    .period    = BACKLIGHT_PWM_PERIOD,            /* PWM period in counter ticks. e.g. clock frequency is 10KHz, period is 256 ticks then t_period is 25.6ms */\n};\n\n#ifdef BACKLIGHT_BREATHING\nstatic virtual_timer_t breathing_vt;\n#endif\n\n// See http://jared.geek.nz/2013/feb/linear-led-pwm\nstatic uint16_t cie_lightness(uint16_t v) {\n    if (v <= 5243)    // if below 8% of max\n        return v / 9; // same as dividing by 900%\n    else {\n        uint32_t y = (((uint32_t)v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare\n        // to get a useful result with integer division, we shift left in the expression above\n        // and revert what we've done again after squaring.\n        y = y * y * y >> 8;\n        if (y > 0xFFFFUL) { // prevent overflow\n            return 0xFFFFU;\n        } else {\n            return (uint16_t)y;\n        }\n    }\n}\n\nstatic uint32_t rescale_limit_val(uint32_t val) {\n    // rescale the supplied backlight value to be in terms of the value limit\n    return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256;\n}\n\nvoid backlight_init_ports(void) {\n#ifdef USE_GPIOV1\n    palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), BACKLIGHT_PAL_MODE);\n#else\n    palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_ALTERNATE(BACKLIGHT_PAL_MODE));\n#endif\n\n    pwmCFG.channels[BACKLIGHT_PWM_CHANNEL - 1].mode = PWM_OUTPUT_MODE;\n    pwmStart(&BACKLIGHT_PWM_DRIVER, &pwmCFG);\n\n    backlight_set(get_backlight_level());\n\n#ifdef BACKLIGHT_BREATHING\n    chVTObjectInit(&breathing_vt);\n    if (is_backlight_breathing()) {\n        breathing_enable();\n    }\n#endif\n}\n\nvoid backlight_set(uint8_t level) {\n    if (level > BACKLIGHT_LEVELS) {\n        level = BACKLIGHT_LEVELS;\n    }\n\n    if (level == 0) {\n        // Turn backlight off\n        pwmDisableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1);\n    } else {\n        // Turn backlight on\n        uint32_t duty = (uint32_t)(cie_lightness(rescale_limit_val(0xFFFF * (uint32_t)level / BACKLIGHT_LEVELS)));\n        pwmEnableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty));\n    }\n}\n\nvoid backlight_task(void) {}\n\n#ifdef BACKLIGHT_BREATHING\n\n#    define BREATHING_STEPS 128\n\n/* To generate breathing curve in python:\n * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]\n */\nstatic const uint8_t breathing_table[BREATHING_STEPS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\n\nstatic void breathing_callback(virtual_timer_t *vtp, void *p);\n\nbool is_breathing(void) {\n    return chVTIsArmed(&breathing_vt);\n}\n\nvoid breathing_enable(void) {\n    /* Update frequency is 256Hz -> 3906us intervals */\n    chVTSetContinuous(&breathing_vt, TIME_US2I(3906), breathing_callback, NULL);\n}\n\nvoid breathing_disable(void) {\n    chVTReset(&breathing_vt);\n\n    // Restore backlight level\n    backlight_set(get_backlight_level());\n}\n\n// Use this before the cie_lightness function.\nstatic inline uint16_t scale_backlight(uint16_t v) {\n    return v / BACKLIGHT_LEVELS * get_backlight_level();\n}\n\nstatic void breathing_callback(virtual_timer_t *vtp, void *p) {\n    uint8_t  breathing_period = get_breathing_period();\n    uint16_t interval         = (uint16_t)breathing_period * 256 / BREATHING_STEPS;\n\n    // resetting after one period to prevent ugly reset at overflow.\n    static uint16_t breathing_counter = 0;\n    breathing_counter                 = (breathing_counter + 1) % (breathing_period * 256);\n    uint8_t  index                    = breathing_counter / interval % BREATHING_STEPS;\n    uint32_t duty                     = cie_lightness(rescale_limit_val(scale_backlight(breathing_table[index] * 256)));\n\n    chSysLockFromISR();\n    pwmEnableChannelI(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty));\n    chSysUnlockFromISR();\n}\n\n// TODO: integrate generic pulse solution\nvoid breathing_pulse(void) {\n    backlight_set(is_backlight_enabled() ? 0 : BACKLIGHT_LEVELS);\n    wait_ms(10);\n    backlight_set(is_backlight_enabled() ? get_backlight_level() : 0);\n}\n\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/backlight_timer.c",
    "content": "#include \"backlight.h\"\n#include \"backlight_driver_common.h\"\n#include \"wait.h\"\n\n#ifndef BACKLIGHT_GPT_DRIVER\n#    define BACKLIGHT_GPT_DRIVER GPTD15\n#endif\n\n// Platform specific implementations\nstatic void     backlight_timer_configure(bool enable);\nstatic void     backlight_timer_set_duty(uint16_t duty);\nstatic uint16_t backlight_timer_get_duty(void);\n\n// See http://jared.geek.nz/2013/feb/linear-led-pwm\nstatic uint16_t cie_lightness(uint16_t v) {\n    if (v <= 5243)    // if below 8% of max\n        return v / 9; // same as dividing by 900%\n    else {\n        uint32_t y = (((uint32_t)v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare\n        // to get a useful result with integer division, we shift left in the expression above\n        // and revert what we've done again after squaring.\n        y = y * y * y >> 8;\n        if (y > 0xFFFFUL) // prevent overflow\n            return 0xFFFFU;\n        else\n            return (uint16_t)y;\n    }\n}\n\nvoid backlight_init_ports(void) {\n    backlight_pins_init();\n\n    backlight_set(get_backlight_level());\n\n#ifdef BACKLIGHT_BREATHING\n    if (is_backlight_breathing()) {\n        breathing_enable();\n    }\n#endif\n}\n\nvoid backlight_set(uint8_t level) {\n    if (level > BACKLIGHT_LEVELS) level = BACKLIGHT_LEVELS;\n\n    backlight_pins_off();\n\n    backlight_timer_set_duty(cie_lightness(0xFFFFU / BACKLIGHT_LEVELS * level));\n    backlight_timer_configure(level != 0);\n}\n\nstatic void backlight_timer_top(void) {\n#ifdef BACKLIGHT_BREATHING\n    if (is_breathing()) {\n        breathing_task();\n    }\n#endif\n\n    if (backlight_timer_get_duty() > 256) {\n        backlight_pins_on();\n    }\n}\n\nstatic void backlight_timer_cmp(void) {\n    backlight_pins_off();\n}\n\nvoid backlight_task(void) {}\n\n#ifdef BACKLIGHT_BREATHING\n#    define BREATHING_STEPS 128\n\nstatic bool     breathing         = false;\nstatic uint16_t breathing_counter = 0;\n\n/* To generate breathing curve in python:\n * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)]\n */\nstatic const uint8_t breathing_table[BREATHING_STEPS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\n\n// Use this before the cie_lightness function.\nstatic inline uint16_t scale_backlight(uint16_t v) {\n    return v / BACKLIGHT_LEVELS * get_backlight_level();\n}\n\nvoid breathing_task(void) {\n    uint8_t  breathing_period = get_breathing_period();\n    uint16_t interval         = (uint16_t)breathing_period * 256 / BREATHING_STEPS;\n    // resetting after one period to prevent ugly reset at overflow.\n    breathing_counter = (breathing_counter + 1) % (breathing_period * 256);\n    uint8_t index     = breathing_counter / interval % BREATHING_STEPS;\n\n    // printf(\"index:%u\\n\", index);\n\n    backlight_timer_set_duty(cie_lightness(scale_backlight((uint16_t)breathing_table[index] * 256)));\n}\n\nbool is_breathing(void) {\n    return breathing;\n}\n\nvoid breathing_enable(void) {\n    breathing_counter = 0;\n    breathing         = true;\n}\nvoid breathing_disable(void) {\n    breathing = false;\n}\n\nvoid breathing_pulse(void) {\n    backlight_set(is_backlight_enabled() ? 0 : BACKLIGHT_LEVELS);\n    wait_ms(10);\n    backlight_set(is_backlight_enabled() ? get_backlight_level() : 0);\n}\n#endif\n\n#ifdef PROTOCOL_CHIBIOS\n// On Platforms where timers fire every tick and have no capture/top events\n//   - fake event in the normal timer callback\nuint16_t s_duty = 0;\n\nstatic void timerCallback(void) {\n    /* Software PWM\n     * timer:1111 1111 1111 1111\n     *       \\______/| \\_______/____  count(0-255)\n     *          \\    \\______________  unused(1)\n     *           \\__________________  index of step table(0-127)\n     */\n\n    // this works for cca 65536 irqs/sec\n    static union {\n        uint16_t raw;\n        struct {\n            uint16_t count : 8;\n            uint8_t  dummy : 1;\n            uint8_t  index : 7;\n        } pwm;\n    } timer = {.raw = 0};\n\n    timer.raw++;\n\n    if (timer.pwm.count == 0) {\n        // LED on\n        backlight_timer_top();\n    } else if (timer.pwm.count == (s_duty / 256)) {\n        // LED off\n        backlight_timer_cmp();\n    }\n}\n\nstatic void backlight_timer_set_duty(uint16_t duty) {\n    s_duty = duty;\n}\nstatic uint16_t backlight_timer_get_duty(void) {\n    return s_duty;\n}\n\n// ChibiOS - Map GPT timer onto Software PWM\nstatic void gptTimerCallback(GPTDriver *gptp) {\n    (void)gptp;\n    timerCallback();\n}\n\nstatic void backlight_timer_configure(bool enable) {\n    static const GPTConfig gptcfg = {1000000, gptTimerCallback, 0, 0};\n\n    static bool s_init = false;\n    if (!s_init) {\n        gptStart(&BACKLIGHT_GPT_DRIVER, &gptcfg);\n        s_init = true;\n    }\n\n    if (enable) {\n        gptStartContinuous(&BACKLIGHT_GPT_DRIVER, gptcfg.frequency / 0xFFFF);\n    } else {\n        gptStopTimer(&BACKLIGHT_GPT_DRIVER);\n    }\n}\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/eeprom/eeprom_kinetis_flexram.c",
    "content": "#include <ch.h>\n#include <hal.h>\n\n#include \"eeprom_kinetis_flexram.h\"\n#include \"eeconfig.h\"\n\n/*************************************/\n/*          Hardware backend         */\n/*                                   */\n/*    Code from PJRC/Teensyduino     */\n/*************************************/\n\n/* Teensyduino Core Library\n * http://www.pjrc.com/teensy/\n * Copyright (c) 2013 PJRC.COM, LLC.\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * 1. The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * 2. If the Software is incorporated into a build system that allows\n * selection among a list of target devices, then similar target\n * devices manufactured by PJRC.COM must be included in the list of\n * target devices and selectable in the same manner.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#if defined(K20x) /* chip selection */\n/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */\n\n/*\n    ^^^ Here be dragons:\n        NXP AppNote AN4282 section 3.1 states that partitioning must only be done once.\n        Once EEPROM partitioning is done, the size is locked to this initial configuration.\n        Attempts to modify the EEPROM_SIZE setting may brick your board.\n*/\n\n// Writing unaligned 16 or 32 bit data is handled automatically when\n// this is defined, but at a cost of extra code size.  Without this,\n// any unaligned write will cause a hard fault exception!  If you're\n// absolutely sure all 16 and 32 bit writes will be aligned, you can\n// remove the extra unnecessary code.\n//\n#    define HANDLE_UNALIGNED_WRITES\n\n// Minimum EEPROM Endurance\n// ------------------------\n#    if (EEPROM_SIZE == 2048) // 35000 writes/byte or 70000 writes/word\n#        define EEESIZE 0x33\n#    elif (EEPROM_SIZE == 1024) // 75000 writes/byte or 150000 writes/word\n#        define EEESIZE 0x34\n#    elif (EEPROM_SIZE == 512) // 155000 writes/byte or 310000 writes/word\n#        define EEESIZE 0x35\n#    elif (EEPROM_SIZE == 256) // 315000 writes/byte or 630000 writes/word\n#        define EEESIZE 0x36\n#    elif (EEPROM_SIZE == 128) // 635000 writes/byte or 1270000 writes/word\n#        define EEESIZE 0x37\n#    elif (EEPROM_SIZE == 64) // 1275000 writes/byte or 2550000 writes/word\n#        define EEESIZE 0x38\n#    elif (EEPROM_SIZE == 32) // 2555000 writes/byte or 5110000 writes/word\n#        define EEESIZE 0x39\n#    endif\n\n/** \\brief eeprom initialization\n *\n * FIXME: needs doc\n */\nvoid eeprom_initialize(void) {\n    uint32_t count          = 0;\n    uint16_t do_flash_cmd[] = {0xf06f, 0x037f, 0x7003, 0x7803, 0xf013, 0x0f80, 0xd0fb, 0x4770};\n    uint8_t  status;\n\n    if (FTFL->FCNFG & FTFL_FCNFG_RAMRDY) {\n        // FlexRAM is configured as traditional RAM\n        // We need to reconfigure for EEPROM usage\n        FTFL->FCCOB0 = 0x80;    // PGMPART = Program Partition Command\n        FTFL->FCCOB4 = EEESIZE; // EEPROM Size\n        FTFL->FCCOB5 = 0x03;    // 0K for Dataflash, 32K for EEPROM backup\n        __disable_irq();\n        // do_flash_cmd() must execute from RAM.  Luckily the C syntax is simple...\n        (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFL->FSTAT));\n        __enable_irq();\n        status = FTFL->FSTAT;\n        if (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL)) {\n            FTFL->FSTAT = (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL));\n            return; // error\n        }\n    }\n    // wait for eeprom to become ready (is this really necessary?)\n    while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) {\n        if (++count > 20000) break;\n    }\n}\n\n#    define FlexRAM ((uint8_t *)0x14000000)\n\n/** \\brief eeprom read byte\n *\n * FIXME: needs doc\n */\nuint8_t eeprom_read_byte(const uint8_t *addr) {\n    uint32_t offset = (uint32_t)addr;\n    if (offset >= EEPROM_SIZE) return 0;\n    if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();\n    return FlexRAM[offset];\n}\n\n/** \\brief eeprom read word\n *\n * FIXME: needs doc\n */\nuint16_t eeprom_read_word(const uint16_t *addr) {\n    uint32_t offset = (uint32_t)addr;\n    if (offset >= EEPROM_SIZE - 1) return 0;\n    if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();\n    return *(uint16_t *)(&FlexRAM[offset]);\n}\n\n/** \\brief eeprom read dword\n *\n * FIXME: needs doc\n */\nuint32_t eeprom_read_dword(const uint32_t *addr) {\n    uint32_t offset = (uint32_t)addr;\n    if (offset >= EEPROM_SIZE - 3) return 0;\n    if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();\n    return *(uint32_t *)(&FlexRAM[offset]);\n}\n\n/** \\brief eeprom read block\n *\n * FIXME: needs doc\n */\nvoid eeprom_read_block(void *buf, const void *addr, size_t len) {\n    uint32_t offset = (uint32_t)addr;\n    uint8_t *dest   = (uint8_t *)buf;\n    uint32_t end    = offset + len;\n\n    if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();\n    if (end > EEPROM_SIZE) end = EEPROM_SIZE;\n    while (offset < end) {\n        *dest++ = FlexRAM[offset++];\n    }\n}\n\n/** \\brief eeprom is ready\n *\n * FIXME: needs doc\n */\nint eeprom_is_ready(void) {\n    return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0;\n}\n\n/** \\brief flexram wait\n *\n * FIXME: needs doc\n */\nstatic void flexram_wait(void) {\n    while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) {\n        // TODO: timeout\n    }\n}\n\n/** \\brief eeprom_write_byte\n *\n * FIXME: needs doc\n */\nvoid eeprom_write_byte(uint8_t *addr, uint8_t value) {\n    uint32_t offset = (uint32_t)addr;\n\n    if (offset >= EEPROM_SIZE) return;\n    if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();\n    if (FlexRAM[offset] != value) {\n        FlexRAM[offset] = value;\n        flexram_wait();\n    }\n}\n\n/** \\brief eeprom write word\n *\n * FIXME: needs doc\n */\nvoid eeprom_write_word(uint16_t *addr, uint16_t value) {\n    uint32_t offset = (uint32_t)addr;\n\n    if (offset >= EEPROM_SIZE - 1) return;\n    if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();\n#    ifdef HANDLE_UNALIGNED_WRITES\n    if ((offset & 1) == 0) {\n#    endif\n        if (*(uint16_t *)(&FlexRAM[offset]) != value) {\n            *(uint16_t *)(&FlexRAM[offset]) = value;\n            flexram_wait();\n        }\n#    ifdef HANDLE_UNALIGNED_WRITES\n    } else {\n        if (FlexRAM[offset] != value) {\n            FlexRAM[offset] = value;\n            flexram_wait();\n        }\n        if (FlexRAM[offset + 1] != (value >> 8)) {\n            FlexRAM[offset + 1] = value >> 8;\n            flexram_wait();\n        }\n    }\n#    endif\n}\n\n/** \\brief eeprom write dword\n *\n * FIXME: needs doc\n */\nvoid eeprom_write_dword(uint32_t *addr, uint32_t value) {\n    uint32_t offset = (uint32_t)addr;\n\n    if (offset >= EEPROM_SIZE - 3) return;\n    if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();\n#    ifdef HANDLE_UNALIGNED_WRITES\n    switch (offset & 3) {\n        case 0:\n#    endif\n            if (*(uint32_t *)(&FlexRAM[offset]) != value) {\n                *(uint32_t *)(&FlexRAM[offset]) = value;\n                flexram_wait();\n            }\n            return;\n#    ifdef HANDLE_UNALIGNED_WRITES\n        case 2:\n            if (*(uint16_t *)(&FlexRAM[offset]) != value) {\n                *(uint16_t *)(&FlexRAM[offset]) = value;\n                flexram_wait();\n            }\n            if (*(uint16_t *)(&FlexRAM[offset + 2]) != (value >> 16)) {\n                *(uint16_t *)(&FlexRAM[offset + 2]) = value >> 16;\n                flexram_wait();\n            }\n            return;\n        default:\n            if (FlexRAM[offset] != value) {\n                FlexRAM[offset] = value;\n                flexram_wait();\n            }\n            if (*(uint16_t *)(&FlexRAM[offset + 1]) != (value >> 8)) {\n                *(uint16_t *)(&FlexRAM[offset + 1]) = value >> 8;\n                flexram_wait();\n            }\n            if (FlexRAM[offset + 3] != (value >> 24)) {\n                FlexRAM[offset + 3] = value >> 24;\n                flexram_wait();\n            }\n    }\n#    endif\n}\n\n/** \\brief eeprom write block\n *\n * FIXME: needs doc\n */\nvoid eeprom_write_block(const void *buf, void *addr, size_t len) {\n    uint32_t       offset = (uint32_t)addr;\n    const uint8_t *src    = (const uint8_t *)buf;\n\n    if (offset >= EEPROM_SIZE) return;\n    if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize();\n    if (len >= EEPROM_SIZE) len = EEPROM_SIZE;\n    if (offset + len >= EEPROM_SIZE) len = EEPROM_SIZE - offset;\n    while (len > 0) {\n        uint32_t lsb = offset & 3;\n        if (lsb == 0 && len >= 4) {\n            // write aligned 32 bits\n            uint32_t val32;\n            val32 = *src++;\n            val32 |= (*src++ << 8);\n            val32 |= (*src++ << 16);\n            val32 |= (*src++ << 24);\n            if (*(uint32_t *)(&FlexRAM[offset]) != val32) {\n                *(uint32_t *)(&FlexRAM[offset]) = val32;\n                flexram_wait();\n            }\n            offset += 4;\n            len -= 4;\n        } else if ((lsb == 0 || lsb == 2) && len >= 2) {\n            // write aligned 16 bits\n            uint16_t val16;\n            val16 = *src++;\n            val16 |= (*src++ << 8);\n            if (*(uint16_t *)(&FlexRAM[offset]) != val16) {\n                *(uint16_t *)(&FlexRAM[offset]) = val16;\n                flexram_wait();\n            }\n            offset += 2;\n            len -= 2;\n        } else {\n            // write 8 bits\n            uint8_t val8 = *src++;\n            if (FlexRAM[offset] != val8) {\n                FlexRAM[offset] = val8;\n                flexram_wait();\n            }\n            offset++;\n            len--;\n        }\n    }\n}\n\n/*\nvoid do_flash_cmd(volatile uint8_t *fstat)\n{\n    *fstat = 0x80;\n    while ((*fstat & 0x80) == 0) ; // wait\n}\n00000000 <do_flash_cmd>:\n   0:\tf06f 037f \tmvn.w\tr3, #127\t; 0x7f\n   4:\t7003      \tstrb\tr3, [r0, #0]\n   6:\t7803      \tldrb\tr3, [r0, #0]\n   8:\tf013 0f80 \ttst.w\tr3, #128\t; 0x80\n   c:\td0fb      \tbeq.n\t6 <do_flash_cmd+0x6>\n   e:\t4770      \tbx\tlr\n*/\n\n#elif defined(KL2x) /* chip selection */\n/* Teensy LC (emulated) */\n\n#    define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0))\n\nextern uint32_t __eeprom_workarea_start__;\nextern uint32_t __eeprom_workarea_end__;\n\nstatic uint32_t flashend = 0;\n\nvoid eeprom_initialize(void) {\n    const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__);\n\n    do {\n        if (*p++ == 0xFFFF) {\n            flashend = (uint32_t)(p - 2);\n            return;\n        }\n    } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__));\n    flashend = (uint32_t)(p - 1);\n}\n\nuint8_t eeprom_read_byte(const uint8_t *addr) {\n    uint32_t        offset = (uint32_t)addr;\n    const uint16_t *p      = (uint16_t *)SYMVAL(__eeprom_workarea_start__);\n    const uint16_t *end    = (const uint16_t *)((uint32_t)flashend);\n    uint16_t        val;\n    uint8_t         data = 0xFF;\n\n    if (!end) {\n        eeprom_initialize();\n        end = (const uint16_t *)((uint32_t)flashend);\n    }\n    if (offset < EEPROM_SIZE) {\n        while (p <= end) {\n            val = *p++;\n            if ((val & 255) == offset) data = val >> 8;\n        }\n    }\n    return data;\n}\n\nstatic void flash_write(const uint16_t *code, uint32_t addr, uint32_t data) {\n    // with great power comes great responsibility....\n    uint32_t stat;\n    *(uint32_t *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC);\n    *(uint32_t *)&(FTFA->FCCOB7) = data;\n    __disable_irq();\n    (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT));\n    __enable_irq();\n    stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL);\n    if (stat) {\n        FTFA->FSTAT = stat;\n    }\n    MCM->PLACR |= MCM_PLACR_CFCC;\n}\n\nvoid eeprom_write_byte(uint8_t *addr, uint8_t data) {\n    uint32_t        offset = (uint32_t)addr;\n    const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend);\n    uint32_t        i, val, flashaddr;\n    uint16_t        do_flash_cmd[] = {0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770};\n    uint8_t         buf[EEPROM_SIZE];\n\n    if (offset >= EEPROM_SIZE) return;\n    if (!end) {\n        eeprom_initialize();\n        end = (const uint16_t *)((uint32_t)flashend);\n    }\n    if (++end < (uint16_t *)SYMVAL(__eeprom_workarea_end__)) {\n        val       = (data << 8) | offset;\n        flashaddr = (uint32_t)end;\n        flashend  = flashaddr;\n        if ((flashaddr & 2) == 0) {\n            val |= 0xFFFF0000;\n        } else {\n            val <<= 16;\n            val |= 0x0000FFFF;\n        }\n        flash_write(do_flash_cmd, flashaddr, val);\n    } else {\n        for (i = 0; i < EEPROM_SIZE; i++) {\n            buf[i] = 0xFF;\n        }\n        val = 0;\n        for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) {\n            val = *p;\n            if ((val & 255) < EEPROM_SIZE) {\n                buf[val & 255] = val >> 8;\n            }\n        }\n        buf[offset] = data;\n        for (flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) {\n            *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr;\n            __disable_irq();\n            (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT));\n            __enable_irq();\n            val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL);\n            ;\n            if (val) FTFA->FSTAT = val;\n            MCM->PLACR |= MCM_PLACR_CFCC;\n        }\n        flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__);\n        for (i = 0; i < EEPROM_SIZE; i++) {\n            if (buf[i] == 0xFF) continue;\n            if ((flashaddr & 2) == 0) {\n                val = (buf[i] << 8) | i;\n            } else {\n                val = val | (buf[i] << 24) | (i << 16);\n                flash_write(do_flash_cmd, flashaddr, val);\n            }\n            flashaddr += 2;\n        }\n        flashend = flashaddr;\n        if ((flashaddr & 2)) {\n            val |= 0xFFFF0000;\n            flash_write(do_flash_cmd, flashaddr, val);\n        }\n    }\n}\n\n/*\nvoid do_flash_cmd(volatile uint8_t *fstat)\n{\n        *fstat = 0x80;\n        while ((*fstat & 0x80) == 0) ; // wait\n}\n00000000 <do_flash_cmd>:\n   0:\t2380      \tmovs\tr3, #128\t; 0x80\n   2:\t7003      \tstrb\tr3, [r0, #0]\n   4:\t7803      \tldrb\tr3, [r0, #0]\n   6:\tb25b      \tsxtb\tr3, r3\n   8:\t2b00      \tcmp\tr3, #0\n   a:\tdafb      \tbge.n\t4 <do_flash_cmd+0x4>\n   c:\t4770      \tbx\tlr\n*/\n\nuint16_t eeprom_read_word(const uint16_t *addr) {\n    const uint8_t *p = (const uint8_t *)addr;\n    return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8);\n}\n\nuint32_t eeprom_read_dword(const uint32_t *addr) {\n    const uint8_t *p = (const uint8_t *)addr;\n    return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24);\n}\n\nvoid eeprom_read_block(void *buf, const void *addr, size_t len) {\n    const uint8_t *p    = (const uint8_t *)addr;\n    uint8_t *      dest = (uint8_t *)buf;\n    while (len--) {\n        *dest++ = eeprom_read_byte(p++);\n    }\n}\n\nint eeprom_is_ready(void) {\n    return 1;\n}\n\nvoid eeprom_write_word(uint16_t *addr, uint16_t value) {\n    uint8_t *p = (uint8_t *)addr;\n    eeprom_write_byte(p++, value);\n    eeprom_write_byte(p, value >> 8);\n}\n\nvoid eeprom_write_dword(uint32_t *addr, uint32_t value) {\n    uint8_t *p = (uint8_t *)addr;\n    eeprom_write_byte(p++, value);\n    eeprom_write_byte(p++, value >> 8);\n    eeprom_write_byte(p++, value >> 16);\n    eeprom_write_byte(p, value >> 24);\n}\n\nvoid eeprom_write_block(const void *buf, void *addr, size_t len) {\n    uint8_t *      p   = (uint8_t *)addr;\n    const uint8_t *src = (const uint8_t *)buf;\n    while (len--) {\n        eeprom_write_byte(p++, *src++);\n    }\n}\n\n#else\n#    error Unsupported Teensy EEPROM.\n#endif /* chip selection */\n// The update functions just calls write for now, but could probably be optimized\n\nvoid eeprom_update_byte(uint8_t *addr, uint8_t value) {\n    eeprom_write_byte(addr, value);\n}\n\nvoid eeprom_update_word(uint16_t *addr, uint16_t value) {\n    uint8_t *p = (uint8_t *)addr;\n    eeprom_write_byte(p++, value);\n    eeprom_write_byte(p, value >> 8);\n}\n\nvoid eeprom_update_dword(uint32_t *addr, uint32_t value) {\n    uint8_t *p = (uint8_t *)addr;\n    eeprom_write_byte(p++, value);\n    eeprom_write_byte(p++, value >> 8);\n    eeprom_write_byte(p++, value >> 16);\n    eeprom_write_byte(p, value >> 24);\n}\n\nvoid eeprom_update_block(const void *buf, void *addr, size_t len) {\n    uint8_t *      p   = (uint8_t *)addr;\n    const uint8_t *src = (const uint8_t *)buf;\n    while (len--) {\n        eeprom_write_byte(p++, *src++);\n    }\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/eeprom/eeprom_kinetis_flexram.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include <ch.h>\n#include <hal.h>\n\n#if defined(K20x)\n/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */\n// The EEPROM is really RAM with a hardware-based backup system to\n// flash memory.  Selecting a smaller size EEPROM allows more wear\n// leveling, for higher write endurance.  If you edit this file,\n// set this to the smallest size your application can use.  Also,\n// due to Freescale's implementation, writing 16 or 32 bit words\n// (aligned to 2 or 4 byte boundaries) has twice the endurance\n// compared to writing 8 bit bytes.\n//\n#    ifndef EEPROM_SIZE\n#        define EEPROM_SIZE 32\n#    endif\n#elif defined(KL2x) /* Teensy LC (emulated) */\n#    define EEPROM_SIZE 128\n#else\n#    error Unsupported Teensy EEPROM.\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash.c",
    "content": "/*\n * This software is experimental and a work in progress.\n * Under no circumstances should these files be used in relation to any critical system(s).\n * Use of these files is at your own risk.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE.\n *\n * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by\n * Artur F.\n *\n * Modifications for QMK and STM32F303 by Yiancar\n * Modifications to add flash wear leveling by Ilya Zhuravlev\n * Modifications to increase flash density by Don Kjer\n */\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"util.h\"\n#include \"debug.h\"\n#include \"eeprom_legacy_emulated_flash.h\"\n#include \"legacy_flash_ops.h\"\n#include \"eeprom_driver.h\"\n\n/*\n * We emulate eeprom by writing a snapshot compacted view of eeprom contents,\n * followed by a write log of any change since that snapshot:\n *\n * === SIMULATED EEPROM CONTENTS ===\n *\n * ┌─ Compacted ┬ Write Log ─┐\n * │............│[BYTE][BYTE]│\n * │FFFF....FFFF│[WRD0][WRD1]│\n * │FFFFFFFFFFFF│[WORD][NEXT]│\n * │....FFFFFFFF│[BYTE][WRD0]│\n * ├────────────┼────────────┤\n * └──PAGE_BASE │            │\n *    PAGE_LAST─┴─WRITE_BASE │\n *                WRITE_LAST ┘\n *\n * Compacted contents are the 1's complement of the actual EEPROM contents.\n * e.g. An 'FFFF' represents a '0000' value.\n *\n * The size of the 'compacted' area is equal to the size of the 'emulated' eeprom.\n * The size of the compacted-area and write log are configurable, and the combined\n * size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent.\n * Simulated Eeprom contents are located at the end of available flash space.\n *\n * The following configuration defines can be set:\n *\n * FEE_PAGE_COUNT   # Total number of pages to use for eeprom simulation (Compact + Write log)\n * FEE_DENSITY_BYTES   # Size of simulated eeprom. (Defaults to half the space allocated by FEE_PAGE_COUNT)\n * NOTE: The current implementation does not include page swapping,\n * and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents.\n *\n * The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals\n * FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES.\n * The larger the write log, the less frequently the compacted area needs to be rewritten.\n *\n *\n * *** General Algorithm ***\n *\n * During initialization:\n * The contents of the Compacted-flash area are loaded and the 1's complement value\n * is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache).\n * Write log entries are processed until a 0xFFFF is reached.\n * Each log entry updates a byte or word in the cache.\n *\n * During reads:\n * EEPROM contents are given back directly from the cache in memory.\n *\n * During writes:\n * The contents of the cache is updated first.\n * If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash\n * Otherwise:\n * If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area.\n * Otherwise a Write log entry is constructed and appended to the next free position in the Write log.\n *\n *\n * *** Write Log Structure ***\n *\n * Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned.\n *\n * === WRITE LOG ENTRY FORMATS ===\n *\n * ╔═══ Byte-Entry ══╗\n * ║0XXXXXXX║YYYYYYYY║\n * ║ └──┬──┘║└──┬───┘║\n * ║ Address║ Value  ║\n * ╚════════╩════════╝\n * 0 <= Address < 0x80 (128)\n *\n * ╔ Word-Encoded 0 ╗\n * ║100XXXXXXXXXXXXX║\n * ║  │└─────┬─────┘║\n * ║  │Address >> 1 ║\n * ║  └── Value: 0  ║\n * ╚════════════════╝\n * 0 <= Address <= 0x3FFE (16382)\n *\n * ╔ Word-Encoded 1 ╗\n * ║101XXXXXXXXXXXXX║\n * ║  │└─────┬─────┘║\n * ║  │Address >> 1 ║\n * ║  └── Value: 1  ║\n * ╚════════════════╝\n * 0 <= Address <= 0x3FFE (16382)\n *\n * ╔═══ Reserved ═══╗\n * ║110XXXXXXXXXXXXX║\n * ╚════════════════╝\n *\n * ╔═══════════ Word-Next ═══════════╗\n * ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║\n * ║   └─────┬─────┘║└───────┬──────┘║\n * ║(Address-128)>>1║     ~Value     ║\n * ╚════════════════╩════════════════╝\n * (  0 <= Address <  0x0080 (128): Reserved)\n * 0x80 <= Address <= 0x3FFE (16382)\n *\n * Write Log entry ranges:\n * 0x0000 ... 0x7FFF - Byte-Entry;     address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF)\n * 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0\n * 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1\n * 0xC000 ... 0xDFFF - Reserved\n * 0xE000 ... 0xFFBF - Word-Next;      address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry)\n * 0xFFC0 ... 0xFFFE - Reserved\n * 0xFFFF            - Unprogrammed\n *\n */\n\n#include \"eeprom_legacy_emulated_flash_defs.h\"\n/* These bits are used for optimizing encoding of bytes, 0 and 1 */\n#define FEE_WORD_ENCODING 0x8000\n#define FEE_VALUE_NEXT 0x6000\n#define FEE_VALUE_RESERVED 0x4000\n#define FEE_VALUE_ENCODED 0x2000\n#define FEE_BYTE_RANGE 0x80\n\n/* Flash word value after erase */\n#define FEE_EMPTY_WORD ((uint16_t)0xFFFF)\n\n#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS)\n#    error \"not implemented.\"\n#endif\n\n/* In-memory contents of emulated eeprom for faster access */\n/* *TODO: Implement page swapping */\nstatic uint16_t WordBuf[FEE_DENSITY_BYTES / 2];\nstatic uint8_t *DataBuf = (uint8_t *)WordBuf;\n\n/* Pointer to the first available slot within the write log */\nstatic uint16_t *empty_slot;\n\n// #define DEBUG_EEPROM_OUTPUT\n\n/*\n * Debug print utils\n */\n\n#if defined(DEBUG_EEPROM_OUTPUT)\n\n#    define debug_eeprom debug_enable\n#    define eeprom_println(s) println(s)\n#    define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__);\n\n#else /* NO_DEBUG */\n\n#    define debug_eeprom false\n#    define eeprom_println(s)\n#    define eeprom_printf(fmt, ...)\n\n#endif /* NO_DEBUG */\n\nvoid print_eeprom(void) {\n#ifndef NO_DEBUG\n    int empty_rows = 0;\n    for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) {\n        if (i % 16 == 0) {\n            if (i >= FEE_DENSITY_BYTES - 16) {\n                /* Make sure we display the last row */\n                empty_rows = 0;\n            }\n            /* Check if this row is uninitialized */\n            ++empty_rows;\n            for (uint16_t j = 0; j < 16; j++) {\n                if (DataBuf[i + j]) {\n                    empty_rows = 0;\n                    break;\n                }\n            }\n            if (empty_rows > 1) {\n                /* Repeat empty row */\n                if (empty_rows == 2) {\n                    /* Only display the first repeat empty row */\n                    println(\"*\");\n                }\n                i += 15;\n                continue;\n            }\n            xprintf(\"%04x\", i);\n        }\n        if (i % 8 == 0) print(\" \");\n\n        xprintf(\" %02x\", DataBuf[i]);\n        if ((i + 1) % 16 == 0) {\n            println(\"\");\n        }\n    }\n#endif\n}\n\nuint16_t EEPROM_Init(void) {\n    /* Load emulated eeprom contents from compacted flash into memory */\n    uint16_t *src  = (uint16_t *)FEE_COMPACTED_BASE_ADDRESS;\n    uint16_t *dest = (uint16_t *)DataBuf;\n    for (; src < (uint16_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) {\n        *dest = ~*src;\n    }\n\n    if (debug_eeprom) {\n        println(\"EEPROM_Init Compacted Pages:\");\n        print_eeprom();\n        println(\"EEPROM_Init Write Log:\");\n    }\n\n    /* Replay write log */\n    uint16_t *log_addr;\n    for (log_addr = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) {\n        uint16_t address = *log_addr;\n        if (address == FEE_EMPTY_WORD) {\n            break;\n        }\n        /* Check for lowest 128-bytes optimization */\n        if (!(address & FEE_WORD_ENCODING)) {\n            uint8_t bvalue = (uint8_t)address;\n            address >>= 8;\n            DataBuf[address] = bvalue;\n            eeprom_printf(\"DataBuf[0x%02x] = 0x%02x;\\n\", address, bvalue);\n        } else {\n            uint16_t wvalue;\n            /* Check if value is in next word */\n            if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) {\n                /* Read value from next word */\n                if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {\n                    break;\n                }\n                wvalue = ~*log_addr;\n                if (!wvalue) {\n                    eeprom_printf(\"Incomplete write at log_addr: 0x%04lx;\\n\", (uint32_t)log_addr);\n                    /* Possibly incomplete write.  Ignore and continue */\n                    continue;\n                }\n                address &= 0x1FFF;\n                address <<= 1;\n                /* Writes to addresses less than 128 are byte log entries */\n                address += FEE_BYTE_RANGE;\n            } else {\n                /* Reserved for future use */\n                if (address & FEE_VALUE_RESERVED) {\n                    eeprom_printf(\"Reserved encoded value at log_addr: 0x%04lx;\\n\", (uint32_t)log_addr);\n                    continue;\n                }\n                /* Optimization for 0 or 1 values. */\n                wvalue = (address & FEE_VALUE_ENCODED) >> 13;\n                address &= 0x1FFF;\n                address <<= 1;\n            }\n            if (address < FEE_DENSITY_BYTES) {\n                eeprom_printf(\"DataBuf[0x%04x] = 0x%04x;\\n\", address, wvalue);\n                *(uint16_t *)(&DataBuf[address]) = wvalue;\n            } else {\n                eeprom_printf(\"DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\\n\", address, wvalue);\n            }\n        }\n    }\n\n    empty_slot = log_addr;\n\n    if (debug_eeprom) {\n        println(\"EEPROM_Init Final DataBuf:\");\n        print_eeprom();\n    }\n\n    return FEE_DENSITY_BYTES;\n}\n\n/* Clear flash contents (doesn't touch in-memory DataBuf) */\nstatic void eeprom_clear(void) {\n    FLASH_Unlock();\n\n    for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) {\n        eeprom_printf(\"FLASH_ErasePage(0x%04lx)\\n\", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)));\n        FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE));\n    }\n\n    FLASH_Lock();\n\n    empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS;\n    eeprom_printf(\"eeprom_clear empty_slot: 0x%08lx\\n\", (uint32_t)empty_slot);\n}\n\n/* Erase emulated eeprom */\nvoid EEPROM_Erase(void) {\n    eeprom_println(\"EEPROM_Erase\");\n    /* Erase compacted pages and write log */\n    eeprom_clear();\n    /* re-initialize to reset DataBuf */\n    EEPROM_Init();\n}\n\n/* Compact write log */\nstatic uint8_t eeprom_compact(void) {\n    /* Erase compacted pages and write log */\n    eeprom_clear();\n\n    FLASH_Unlock();\n\n    FLASH_Status final_status = FLASH_COMPLETE;\n\n    /* Write emulated eeprom contents from memory to compacted flash */\n    uint16_t *src  = (uint16_t *)DataBuf;\n    uintptr_t dest = FEE_COMPACTED_BASE_ADDRESS;\n    uint16_t  value;\n    for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 2) {\n        value = *src;\n        if (value) {\n            eeprom_printf(\"FLASH_ProgramHalfWord(0x%04lx, 0x%04x)\\n\", (uint32_t)dest, ~value);\n            FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value);\n            if (status != FLASH_COMPLETE) final_status = status;\n        }\n    }\n\n    FLASH_Lock();\n\n    if (debug_eeprom) {\n        println(\"eeprom_compacted:\");\n        print_eeprom();\n    }\n\n    return final_status;\n}\n\nstatic uint8_t eeprom_write_direct_entry(uint16_t Address) {\n    /* Check if we can just write this directly to the compacted flash area */\n    uintptr_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFFE);\n    if (*(uint16_t *)directAddress == FEE_EMPTY_WORD) {\n        /* Write the value directly to the compacted area without a log entry */\n        uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]);\n        /* Early exit if a write isn't needed */\n        if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE;\n\n        FLASH_Unlock();\n\n        eeprom_printf(\"FLASH_ProgramHalfWord(0x%08lx, 0x%04x) [DIRECT]\\n\", (uint32_t)directAddress, value);\n        FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value);\n\n        FLASH_Lock();\n        return status;\n    }\n    return 0;\n}\n\nstatic uint8_t eeprom_write_log_word_entry(uint16_t Address) {\n    FLASH_Status final_status = FLASH_COMPLETE;\n\n    uint16_t value = *(uint16_t *)(&DataBuf[Address]);\n    eeprom_printf(\"eeprom_write_log_word_entry(0x%04x): 0x%04x\\n\", Address, value);\n\n    /* MSB signifies the lowest 128-byte optimization is not in effect */\n    uint16_t encoding = FEE_WORD_ENCODING;\n    uint8_t  entry_size;\n    if (value <= 1) {\n        encoding |= value << 13;\n        entry_size = 2;\n    } else {\n        encoding |= FEE_VALUE_NEXT;\n        entry_size = 4;\n        /* Writes to addresses less than 128 are byte log entries */\n        Address -= FEE_BYTE_RANGE;\n    }\n\n    /* if we can't find an empty spot, we must compact emulated eeprom */\n    if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) {\n        /* compact the write log into the compacted flash area */\n        return eeprom_compact();\n    }\n\n    /* Word log writes should be word-aligned.  Take back a bit */\n    Address >>= 1;\n    Address |= encoding;\n\n    /* ok we found a place let's write our data */\n    FLASH_Unlock();\n\n    /* address */\n    eeprom_printf(\"FLASH_ProgramHalfWord(0x%08lx, 0x%04x)\\n\", (uint32_t)empty_slot, Address);\n    final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address);\n\n    /* value */\n    if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) {\n        eeprom_printf(\"FLASH_ProgramHalfWord(0x%08lx, 0x%04x)\\n\", (uint32_t)empty_slot, ~value);\n        FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value);\n        if (status != FLASH_COMPLETE) final_status = status;\n    }\n\n    FLASH_Lock();\n\n    return final_status;\n}\n\nstatic uint8_t eeprom_write_log_byte_entry(uint16_t Address) {\n    eeprom_printf(\"eeprom_write_log_byte_entry(0x%04x): 0x%02x\\n\", Address, DataBuf[Address]);\n\n    /* if couldn't find an empty spot, we must compact emulated eeprom */\n    if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {\n        /* compact the write log into the compacted flash area */\n        return eeprom_compact();\n    }\n\n    /* ok we found a place let's write our data */\n    FLASH_Unlock();\n\n    /* Pack address and value into the same word */\n    uint16_t value = (Address << 8) | DataBuf[Address];\n\n    /* write to flash */\n    eeprom_printf(\"FLASH_ProgramHalfWord(0x%08lx, 0x%04x)\\n\", (uint32_t)empty_slot, value);\n    FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value);\n\n    FLASH_Lock();\n\n    return status;\n}\n\nuint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {\n    /* if the address is out-of-bounds, do nothing */\n    if (Address >= FEE_DENSITY_BYTES) {\n        eeprom_printf(\"EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\\n\", Address, DataByte);\n        return FLASH_BAD_ADDRESS;\n    }\n\n    /* if the value is the same, don't bother writing it */\n    if (DataBuf[Address] == DataByte) {\n        eeprom_printf(\"EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\\n\", Address, DataByte);\n        return 0;\n    }\n\n    /* keep DataBuf cache in sync */\n    DataBuf[Address] = DataByte;\n    eeprom_printf(\"EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\\n\", Address, DataBuf[Address]);\n\n    /* perform the write into flash memory */\n    /* First, attempt to write directly into the compacted flash area */\n    FLASH_Status status = eeprom_write_direct_entry(Address);\n    if (!status) {\n        /* Otherwise append to the write log */\n        if (Address < FEE_BYTE_RANGE) {\n            status = eeprom_write_log_byte_entry(Address);\n        } else {\n            status = eeprom_write_log_word_entry(Address & 0xFFFE);\n        }\n    }\n    if (status != 0 && status != FLASH_COMPLETE) {\n        eeprom_printf(\"EEPROM_WriteDataByte [STATUS == %d]\\n\", status);\n    }\n    return status;\n}\n\nuint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) {\n    /* if the address is out-of-bounds, do nothing */\n    if (Address >= FEE_DENSITY_BYTES) {\n        eeprom_printf(\"EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\\n\", Address, DataWord);\n        return FLASH_BAD_ADDRESS;\n    }\n\n    /* Check for word alignment */\n    FLASH_Status final_status = FLASH_COMPLETE;\n    if (Address % 2) {\n        final_status        = EEPROM_WriteDataByte(Address, DataWord);\n        FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8);\n        if (status != FLASH_COMPLETE) final_status = status;\n        if (final_status != 0 && final_status != FLASH_COMPLETE) {\n            eeprom_printf(\"EEPROM_WriteDataWord [STATUS == %d]\\n\", final_status);\n        }\n        return final_status;\n    }\n\n    /* if the value is the same, don't bother writing it */\n    uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]);\n    if (oldValue == DataWord) {\n        eeprom_printf(\"EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\\n\", Address, DataWord);\n        return 0;\n    }\n\n    /* keep DataBuf cache in sync */\n    *(uint16_t *)(&DataBuf[Address]) = DataWord;\n    eeprom_printf(\"EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\\n\", Address, *(uint16_t *)(&DataBuf[Address]));\n\n    /* perform the write into flash memory */\n    /* First, attempt to write directly into the compacted flash area */\n    final_status = eeprom_write_direct_entry(Address);\n    if (!final_status) {\n        /* Otherwise append to the write log */\n        /* Check if we need to fall back to byte write */\n        if (Address < FEE_BYTE_RANGE) {\n            final_status = FLASH_COMPLETE;\n            /* Only write a byte if it has changed */\n            if ((uint8_t)oldValue != (uint8_t)DataWord) {\n                final_status = eeprom_write_log_byte_entry(Address);\n            }\n            FLASH_Status status = FLASH_COMPLETE;\n            /* Only write a byte if it has changed */\n            if ((oldValue >> 8) != (DataWord >> 8)) {\n                status = eeprom_write_log_byte_entry(Address + 1);\n            }\n            if (status != FLASH_COMPLETE) final_status = status;\n        } else {\n            final_status = eeprom_write_log_word_entry(Address);\n        }\n    }\n    if (final_status != 0 && final_status != FLASH_COMPLETE) {\n        eeprom_printf(\"EEPROM_WriteDataWord [STATUS == %d]\\n\", final_status);\n    }\n    return final_status;\n}\n\nuint8_t EEPROM_ReadDataByte(uint16_t Address) {\n    uint8_t DataByte = 0xFF;\n\n    if (Address < FEE_DENSITY_BYTES) {\n        DataByte = DataBuf[Address];\n    }\n\n    eeprom_printf(\"EEPROM_ReadDataByte(0x%04x): 0x%02x\\n\", Address, DataByte);\n\n    return DataByte;\n}\n\nuint16_t EEPROM_ReadDataWord(uint16_t Address) {\n    uint16_t DataWord = 0xFFFF;\n\n    if (Address < FEE_DENSITY_BYTES - 1) {\n        /* Check word alignment */\n        if (Address % 2) {\n            DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8);\n        } else {\n            DataWord = *(uint16_t *)(&DataBuf[Address]);\n        }\n    }\n\n    eeprom_printf(\"EEPROM_ReadDataWord(0x%04x): 0x%04x\\n\", Address, DataWord);\n\n    return DataWord;\n}\n\n/*****************************************************************************\n *  Bind to eeprom_driver.c\n *******************************************************************************/\nvoid eeprom_driver_init(void) {\n    EEPROM_Init();\n}\n\nvoid eeprom_driver_format(bool erase) {\n    /* emulated eepron requires the write log data structures to be erased before use. */\n    (void)erase;\n    eeprom_driver_erase();\n}\n\nvoid eeprom_driver_erase(void) {\n    EEPROM_Erase();\n}\n\nvoid eeprom_read_block(void *buf, const void *addr, size_t len) {\n    const uint8_t *src  = (const uint8_t *)addr;\n    uint8_t *      dest = (uint8_t *)buf;\n\n    /* Check word alignment */\n    if (len && (uintptr_t)src % 2) {\n        /* Read the unaligned first byte */\n        *dest++ = EEPROM_ReadDataByte((const uintptr_t)src++);\n        --len;\n    }\n\n    uint16_t value;\n    bool     aligned = ((uintptr_t)dest % 2 == 0);\n    while (len > 1) {\n        value = EEPROM_ReadDataWord((const uintptr_t)((uint16_t *)src));\n        if (aligned) {\n            *(uint16_t *)dest = value;\n            dest += 2;\n        } else {\n            *dest++ = value;\n            *dest++ = value >> 8;\n        }\n        src += 2;\n        len -= 2;\n    }\n    if (len) {\n        *dest = EEPROM_ReadDataByte((const uintptr_t)src);\n    }\n}\n\nvoid eeprom_write_block(const void *buf, void *addr, size_t len) {\n    uint8_t *      dest = (uint8_t *)addr;\n    const uint8_t *src  = (const uint8_t *)buf;\n\n    /* Check word alignment */\n    if (len && (uintptr_t)dest % 2) {\n        /* Write the unaligned first byte */\n        EEPROM_WriteDataByte((uintptr_t)dest++, *src++);\n        --len;\n    }\n\n    uint16_t value;\n    bool     aligned = ((uintptr_t)src % 2 == 0);\n    while (len > 1) {\n        if (aligned) {\n            value = *(uint16_t *)src;\n        } else {\n            value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8);\n        }\n        EEPROM_WriteDataWord((uintptr_t)((uint16_t *)dest), value);\n        dest += 2;\n        src += 2;\n        len -= 2;\n    }\n\n    if (len) {\n        EEPROM_WriteDataByte((uintptr_t)dest, *src);\n    }\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash.h",
    "content": "/*\n * This software is experimental and a work in progress.\n * Under no circumstances should these files be used in relation to any critical system(s).\n * Use of these files is at your own risk.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE.\n *\n * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by\n * Artur F.\n *\n * Modifications for QMK and STM32F303 by Yiancar\n *\n * This library assumes 8-bit data locations. To add a new MCU, please provide the flash\n * page size and the total flash size in Kb. The number of available pages must be a multiple\n * of 2. Only half of the pages account for the total EEPROM size.\n * This library also assumes that the pages are not used by the firmware.\n */\n\n#pragma once\n\nuint16_t EEPROM_Init(void);\nvoid     EEPROM_Erase(void);\nuint8_t  EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte);\nuint8_t  EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord);\nuint8_t  EEPROM_ReadDataByte(uint16_t Address);\nuint16_t EEPROM_ReadDataWord(uint16_t Address);\n\nvoid print_eeprom(void);\n"
  },
  {
    "path": "platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash_defs.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <hal.h>\n\n#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT)\n#    if defined(STM32F103xB) || defined(STM32F042x6) || defined(GD32VF103C8) || defined(GD32VF103CB)\n#        ifndef FEE_PAGE_SIZE\n#            define FEE_PAGE_SIZE 0x400 // Page size = 1KByte\n#        endif\n#        ifndef FEE_PAGE_COUNT\n#            define FEE_PAGE_COUNT 2 // How many pages are used\n#        endif\n#    elif defined(STM32F103xE) || defined(STM32F303xC) || defined(STM32F303xE) || defined(STM32F072xB) || defined(STM32F070xB)\n#        ifndef FEE_PAGE_SIZE\n#            define FEE_PAGE_SIZE 0x800 // Page size = 2KByte\n#        endif\n#        ifndef FEE_PAGE_COUNT\n#            define FEE_PAGE_COUNT 4 // How many pages are used\n#        endif\n#    elif defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F405xG) || defined(STM32F411xE)\n#        ifndef FEE_PAGE_SIZE\n#            define FEE_PAGE_SIZE 0x4000 // Page size = 16KByte\n#        endif\n#        ifndef FEE_PAGE_COUNT\n#            define FEE_PAGE_COUNT 1 // How many pages are used\n#        endif\n#    endif\n#endif\n\n#if !defined(FEE_MCU_FLASH_SIZE)\n#    if defined(STM32F042x6)\n#        define FEE_MCU_FLASH_SIZE 32 // Size in Kb\n#    elif defined(GD32VF103C8)\n#        define FEE_MCU_FLASH_SIZE 64 // Size in Kb\n#    elif defined(STM32F103xB) || defined(STM32F072xB) || defined(STM32F070xB) || defined(GD32VF103CB)\n#        define FEE_MCU_FLASH_SIZE 128 // Size in Kb\n#    elif defined(STM32F303xC) || defined(STM32F401xC)\n#        define FEE_MCU_FLASH_SIZE 256 // Size in Kb\n#    elif defined(STM32F103xE) || defined(STM32F303xE) || defined(STM32F401xE) || defined(STM32F411xE)\n#        define FEE_MCU_FLASH_SIZE 512 // Size in Kb\n#    elif defined(STM32F405xG)\n#        define FEE_MCU_FLASH_SIZE 1024 // Size in Kb\n#    endif\n#endif\n\n/* Start of the emulated eeprom */\n#if !defined(FEE_PAGE_BASE_ADDRESS)\n#    if defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F405xG) || defined(STM32F411xE)\n#        ifndef FEE_PAGE_BASE_ADDRESS\n#            define FEE_PAGE_BASE_ADDRESS 0x08004000 // bodge to force 2nd 16k page\n#        endif\n#    else\n#        ifndef FEE_FLASH_BASE\n#            define FEE_FLASH_BASE 0x8000000\n#        endif\n/* Default to end of flash */\n#        define FEE_PAGE_BASE_ADDRESS ((uintptr_t)(FEE_FLASH_BASE) + FEE_MCU_FLASH_SIZE * 1024 - (FEE_PAGE_COUNT * FEE_PAGE_SIZE))\n#    endif\n#endif\n\n/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */\n#define FEE_ADDRESS_MAX_SIZE 0x4000\n\n/* Size of combined compacted eeprom and write log pages */\n#define FEE_DENSITY_MAX_SIZE (FEE_PAGE_COUNT * FEE_PAGE_SIZE)\n\n#ifndef FEE_MCU_FLASH_SIZE_IGNORE_CHECK /* *TODO: Get rid of this check */\n#    if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024)\n#        pragma message STR(FEE_DENSITY_MAX_SIZE) \" > \" STR(FEE_MCU_FLASH_SIZE * 1024)\n#        error emulated eeprom: FEE_DENSITY_MAX_SIZE is greater than available flash size\n#    endif\n#endif\n\n/* Size of emulated eeprom */\n#ifdef FEE_DENSITY_BYTES\n#    if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE)\n#        pragma message STR(FEE_DENSITY_BYTES) \" > \" STR(FEE_DENSITY_MAX_SIZE)\n#        error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE\n#    endif\n#    if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE)\n#        pragma message STR(FEE_DENSITY_BYTES) \" == \" STR(FEE_DENSITY_MAX_SIZE)\n#        warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log.  This will greatly increase the flash wear rate!\n#    endif\n#    if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE\n#        pragma message STR(FEE_DENSITY_BYTES) \" > \" STR(FEE_ADDRESS_MAX_SIZE)\n#        error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows\n#    endif\n#    if ((FEE_DENSITY_BYTES) % 2) == 1\n#        error emulated eeprom: FEE_DENSITY_BYTES must be even\n#    endif\n#else\n/* Default to half of allocated space used for emulated eeprom, half for write log */\n#    define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2)\n#endif\n\n/* Size of write log */\n#ifdef FEE_WRITE_LOG_BYTES\n#    if ((FEE_DENSITY_BYTES + FEE_WRITE_LOG_BYTES) > FEE_DENSITY_MAX_SIZE)\n#        pragma message STR(FEE_DENSITY_BYTES) \" + \" STR(FEE_WRITE_LOG_BYTES) \" > \" STR(FEE_DENSITY_MAX_SIZE)\n#        error emulated eeprom: FEE_WRITE_LOG_BYTES exceeds remaining FEE_DENSITY_MAX_SIZE\n#    endif\n#    if ((FEE_WRITE_LOG_BYTES) % 2) == 1\n#        error emulated eeprom: FEE_WRITE_LOG_BYTES must be even\n#    endif\n#else\n/* Default to use all remaining space */\n#    define FEE_WRITE_LOG_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES)\n#endif\n\n/* Start of the emulated eeprom compacted flash area */\n#define FEE_COMPACTED_BASE_ADDRESS FEE_PAGE_BASE_ADDRESS\n/* End of the emulated eeprom compacted flash area */\n#define FEE_COMPACTED_LAST_ADDRESS (FEE_COMPACTED_BASE_ADDRESS + FEE_DENSITY_BYTES)\n/* Start of the emulated eeprom write log */\n#define FEE_WRITE_LOG_BASE_ADDRESS FEE_COMPACTED_LAST_ADDRESS\n/* End of the emulated eeprom write log */\n#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES)\n\n#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES)\n#    error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/eeprom/eeprom_stm32_L0_L1.c",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <stdint.h>\n#include <string.h>\n\n#include <hal.h>\n#include \"eeprom_driver.h\"\n#include \"eeprom_stm32_L0_L1.h\"\n\n#define EEPROM_BASE_ADDR 0x08080000\n#define EEPROM_ADDR(offset) (EEPROM_BASE_ADDR + (offset))\n#define EEPROM_PTR(offset) ((__IO uint8_t *)EEPROM_ADDR(offset))\n#define EEPROM_BYTE(location, offset) (*(EEPROM_PTR(((uint32_t)location) + ((uint32_t)offset))))\n#define EEPROM_WORD(location) (*(__IO uint32_t *)EEPROM_PTR(location))\n\n#define BUFFER_BYTE(buffer, offset) (*(((uint8_t *)buffer) + offset))\n\n#define FLASH_PEKEY1 0x89ABCDEF\n#define FLASH_PEKEY2 0x02030405\n\nstatic inline void STM32_L0_L1_EEPROM_WaitNotBusy(void) {\n    while (FLASH->SR & FLASH_SR_BSY) {\n        __WFI();\n    }\n}\n\nstatic inline void STM32_L0_L1_EEPROM_Unlock(void) {\n    STM32_L0_L1_EEPROM_WaitNotBusy();\n    if (FLASH->PECR & FLASH_PECR_PELOCK) {\n        FLASH->PEKEYR = FLASH_PEKEY1;\n        FLASH->PEKEYR = FLASH_PEKEY2;\n    }\n}\n\nstatic inline void STM32_L0_L1_EEPROM_Lock(void) {\n    STM32_L0_L1_EEPROM_WaitNotBusy();\n    FLASH->PECR |= FLASH_PECR_PELOCK;\n}\n\nvoid eeprom_driver_init(void) {}\n\nvoid eeprom_driver_format(bool erase) {\n    if (erase) {\n        eeprom_driver_erase();\n    }\n}\n\nvoid eeprom_driver_erase(void) {\n    STM32_L0_L1_EEPROM_Unlock();\n\n    for (size_t offset = 0; offset < STM32_ONBOARD_EEPROM_SIZE; offset += sizeof(uint32_t)) {\n#ifdef QMK_MCU_SERIES_STM32L0XX\n        FLASH->PECR |= FLASH_PECR_ERASE | FLASH_PECR_DATA;\n#endif\n\n        EEPROM_WORD(offset) = (uint32_t)0;\n\n        STM32_L0_L1_EEPROM_WaitNotBusy();\n#ifdef QMK_MCU_SERIES_STM32L0XX\n        FLASH->PECR &= ~(FLASH_PECR_ERASE | FLASH_PECR_DATA);\n#endif\n    }\n\n    STM32_L0_L1_EEPROM_Lock();\n}\n\nvoid eeprom_read_block(void *buf, const void *addr, size_t len) {\n    for (size_t offset = 0; offset < len; ++offset) {\n        // Drop out if we've hit the limit of the EEPROM\n        if ((((uint32_t)addr) + offset) >= STM32_ONBOARD_EEPROM_SIZE) {\n            break;\n        }\n\n        STM32_L0_L1_EEPROM_WaitNotBusy();\n        BUFFER_BYTE(buf, offset) = EEPROM_BYTE(addr, offset);\n    }\n}\n\nvoid eeprom_write_block(const void *buf, void *addr, size_t len) {\n    // use word-aligned write to overcome issues with writing null bytes\n    uint32_t start_addr = (uint32_t)addr;\n    if (start_addr >= (STM32_ONBOARD_EEPROM_SIZE)) {\n        return;\n    }\n    uint32_t max_len = (STM32_ONBOARD_EEPROM_SIZE)-start_addr;\n    if (len > max_len) {\n        len = max_len;\n    }\n    uint32_t end_addr = start_addr + len;\n\n    uint32_t aligned_start = start_addr & ~0x3;\n    uint32_t aligned_end   = (end_addr + 3) & ~0x3;\n\n    STM32_L0_L1_EEPROM_Unlock();\n    for (uint32_t word_addr = aligned_start; word_addr < aligned_end; word_addr += 4) {\n        uint32_t existing_word = EEPROM_WORD(word_addr);\n        uint32_t new_word      = existing_word;\n\n        // Update the relevant bytes in the word\n        for (int i = 0; i < 4; i++) {\n            uint32_t byte_addr = word_addr + i;\n            if (byte_addr >= start_addr && byte_addr < end_addr) {\n                uint8_t new_byte = BUFFER_BYTE(buf, byte_addr - start_addr);\n                new_word         = (new_word & ~(0xFFU << (i * 8))) | ((uint32_t)new_byte << (i * 8));\n            }\n        }\n\n        // Only write if the word has changed\n        if (new_word != existing_word) {\n            STM32_L0_L1_EEPROM_WaitNotBusy();\n            EEPROM_WORD(word_addr) = new_word;\n        }\n    }\n    STM32_L0_L1_EEPROM_Lock();\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/eeprom/eeprom_stm32_L0_L1.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n/*\n    The size used by the STM32 L0/L1 EEPROM driver.\n*/\n#ifndef STM32_ONBOARD_EEPROM_SIZE\n#    ifdef DYNAMIC_KEYMAP_ENABLE\n#        define STM32_ONBOARD_EEPROM_SIZE 1024\n#    else\n#        include \"eeconfig.h\"\n#        define STM32_ONBOARD_EEPROM_SIZE (((EECONFIG_SIZE + 3) / 4) * 4) // based off eeconfig's current usage, aligned to 4-byte sizes, to deal with LTO and EEPROM page sizing\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/flash/legacy_flash_ops.c",
    "content": "/*\n * This software is experimental and a work in progress.\n * Under no circumstances should these files be used in relation to any critical system(s).\n * Use of these files is at your own risk.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE.\n *\n * This files are free to use from https://github.com/rogerclarkmelbourne/Arduino_STM32 and\n * https://github.com/leaflabs/libmaple\n *\n * Modifications for QMK and STM32F303 by Yiancar\n */\n\n#include <hal.h>\n#include \"legacy_flash_ops.h\"\n\n#if defined(STM32F1XX)\n#    define FLASH_SR_WRPERR FLASH_SR_WRPRTERR\n#endif\n\n#if defined(MCU_GD32V)\n/* GigaDevice GD32VF103 is a STM32F103 clone at heart. */\n#    include \"gd32v_compatibility.h\"\n#endif\n\n#if defined(STM32F4XX)\n#    define FLASH_SR_PGERR (FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR)\n\n#    define FLASH_KEY1 0x45670123U\n#    define FLASH_KEY2 0xCDEF89ABU\n\nstatic uint8_t ADDR2PAGE(uint32_t Page_Address) {\n    switch (Page_Address) {\n        case 0x08000000 ... 0x08003FFF:\n            return 0;\n        case 0x08004000 ... 0x08007FFF:\n            return 1;\n        case 0x08008000 ... 0x0800BFFF:\n            return 2;\n        case 0x0800C000 ... 0x0800FFFF:\n            return 3;\n    }\n\n    // TODO: bad times...\n    return 7;\n}\n#endif\n\n/* Delay definition */\n#define EraseTimeout ((uint32_t)0x00000FFF)\n#define ProgramTimeout ((uint32_t)0x0000001F)\n\n#define ASSERT(exp) (void)((0))\n\n/**\n * @brief  Inserts a time delay.\n * @param  None\n * @retval None\n */\nstatic void delay(void) {\n    __IO uint32_t i = 0;\n    for (i = 0xFF; i != 0; i--) {\n    }\n}\n\n/**\n * @brief  Returns the FLASH Status.\n * @param  None\n * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG,\n *   FLASH_ERROR_WRP or FLASH_COMPLETE\n */\nFLASH_Status FLASH_GetStatus(void) {\n    if ((FLASH->SR & FLASH_SR_BSY) == FLASH_SR_BSY) return FLASH_BUSY;\n\n    if ((FLASH->SR & FLASH_SR_PGERR) != 0) return FLASH_ERROR_PG;\n\n    if ((FLASH->SR & FLASH_SR_WRPERR) != 0) return FLASH_ERROR_WRP;\n\n#if defined(FLASH_OBR_OPTERR)\n    if ((FLASH->SR & FLASH_OBR_OPTERR) != 0) return FLASH_ERROR_OPT;\n#endif\n\n    return FLASH_COMPLETE;\n}\n\n/**\n * @brief  Waits for a Flash operation to complete or a TIMEOUT to occur.\n * @param  Timeout: FLASH progamming Timeout\n * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,\n *   FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.\n */\nFLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) {\n    FLASH_Status status;\n\n    /* Check for the Flash Status */\n    status = FLASH_GetStatus();\n    /* Wait for a Flash operation to complete or a TIMEOUT to occur */\n    while ((status == FLASH_BUSY) && (Timeout != 0x00)) {\n        delay();\n        status = FLASH_GetStatus();\n        Timeout--;\n    }\n    if (Timeout == 0) status = FLASH_TIMEOUT;\n    /* Return the operation status */\n    return status;\n}\n\n/**\n * @brief  Erases a specified FLASH page.\n * @param  Page_Address: The page address to be erased.\n * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG,\n *   FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.\n */\nFLASH_Status FLASH_ErasePage(uint32_t Page_Address) {\n    FLASH_Status status = FLASH_COMPLETE;\n    /* Check the parameters */\n    ASSERT(IS_FLASH_ADDRESS(Page_Address));\n    /* Wait for last operation to be completed */\n    status = FLASH_WaitForLastOperation(EraseTimeout);\n\n    if (status == FLASH_COMPLETE) {\n        /* if the previous operation is completed, proceed to erase the page */\n#if defined(FLASH_CR_SNB)\n        FLASH->CR &= ~FLASH_CR_SNB;\n        FLASH->CR |= FLASH_CR_SER | (ADDR2PAGE(Page_Address) << FLASH_CR_SNB_Pos);\n#else\n        FLASH->CR |= FLASH_CR_PER;\n        FLASH->AR = Page_Address;\n#endif\n        FLASH->CR |= FLASH_CR_STRT;\n\n        /* Wait for last operation to be completed */\n        status = FLASH_WaitForLastOperation(EraseTimeout);\n        if (status != FLASH_TIMEOUT) {\n            /* if the erase operation is completed, disable the configured Bits */\n#if defined(FLASH_CR_SNB)\n            FLASH->CR &= ~(FLASH_CR_SER | FLASH_CR_SNB);\n#else\n            FLASH->CR &= ~FLASH_CR_PER;\n#endif\n        }\n        FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR);\n    }\n    /* Return the Erase Status */\n    return status;\n}\n\n/**\n * @brief  Programs a half word at a specified address.\n * @param  Address: specifies the address to be programmed.\n * @param  Data: specifies the data to be programmed.\n * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,\n *   FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.\n */\nFLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {\n    FLASH_Status status = FLASH_BAD_ADDRESS;\n\n    if (IS_FLASH_ADDRESS(Address)) {\n        /* Wait for last operation to be completed */\n        status = FLASH_WaitForLastOperation(ProgramTimeout);\n        if (status == FLASH_COMPLETE) {\n            /* if the previous operation is completed, proceed to program the new data */\n\n#if defined(FLASH_CR_PSIZE)\n            FLASH->CR &= ~FLASH_CR_PSIZE;\n            FLASH->CR |= FLASH_CR_PSIZE_0;\n#endif\n            FLASH->CR |= FLASH_CR_PG;\n            *(__IO uint16_t*)Address = Data;\n            /* Wait for last operation to be completed */\n            status = FLASH_WaitForLastOperation(ProgramTimeout);\n            if (status != FLASH_TIMEOUT) {\n                /* if the program operation is completed, disable the PG Bit */\n                FLASH->CR &= ~FLASH_CR_PG;\n            }\n            FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR);\n        }\n    }\n    return status;\n}\n\n/**\n * @brief  Unlocks the FLASH Program Erase Controller.\n * @param  None\n * @retval None\n */\nvoid FLASH_Unlock(void) {\n    if (FLASH->CR & FLASH_CR_LOCK) {\n        /* Authorize the FPEC Access */\n        FLASH->KEYR = FLASH_KEY1;\n        FLASH->KEYR = FLASH_KEY2;\n    }\n}\n\n/**\n * @brief  Locks the FLASH Program Erase Controller.\n * @param  None\n * @retval None\n */\nvoid FLASH_Lock(void) {\n    /* Set the Lock Bit to lock the FPEC and the FCR */\n    FLASH->CR |= FLASH_CR_LOCK;\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/flash/legacy_flash_ops.h",
    "content": "/*\n * This software is experimental and a work in progress.\n * Under no circumstances should these files be used in relation to any critical system(s).\n * Use of these files is at your own risk.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR\n * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\n * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE.\n *\n * This files are free to use from https://github.com/rogerclarkmelbourne/Arduino_STM32 and\n * https://github.com/leaflabs/libmaple\n *\n * Modifications for QMK and STM32F303 by Yiancar\n */\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#ifdef LEGACY_FLASH_OPS_MOCKED\nextern uint8_t FlashBuf[MOCK_FLASH_SIZE];\n#endif\n\ntypedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status;\n\n#define IS_FLASH_ADDRESS(ADDRESS) (((ADDRESS) >= 0x08000000) && ((ADDRESS) < 0x0807FFFF))\n\nFLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout);\nFLASH_Status FLASH_ErasePage(uint32_t Page_Address);\nFLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);\n\nvoid FLASH_Unlock(void);\nvoid FLASH_Lock(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/i2c_master.c",
    "content": "/* Copyright 2018 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2023 customMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* This library is only valid for STM32 processors.\n * This library follows the convention of the AVR i2c_master library.\n * As a result addresses are expected to be already shifted (addr << 1).\n * I2CD1 is the default driver which corresponds to pins B6 and B7. This\n * can be changed.\n * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that\n * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. Pins B6 and B7 are used\n * but using any other I2C pins should be trivial.\n */\n\n#include \"i2c_master.h\"\n#include \"gpio.h\"\n#include \"chibios_config.h\"\n#include <ch.h>\n#include <hal.h>\n\n#ifndef I2C_DRIVER\n#    define I2C_DRIVER I2CD1\n#endif\n\n#ifndef I2C1_SCL_PIN\n#    define I2C1_SCL_PIN B6\n#endif\n\n#ifndef I2C1_SCL_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define I2C1_SCL_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN\n#    else\n#        define I2C1_SCL_PAL_MODE 4\n#    endif\n#endif\n\n#ifndef I2C1_SDA_PIN\n#    define I2C1_SDA_PIN B7\n#endif\n\n#ifndef I2C1_SDA_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define I2C1_SDA_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN\n#    else\n#        define I2C1_SDA_PAL_MODE 4\n#    endif\n#endif\n\n#ifdef USE_I2CV1\n#    ifndef I2C1_OPMODE\n#        define I2C1_OPMODE OPMODE_I2C\n#    endif\n#    ifndef I2C1_CLOCK_SPEED\n#        define I2C1_CLOCK_SPEED 100000 /* 400000 */\n#    endif\n#    ifndef I2C1_DUTY_CYCLE\n#        define I2C1_DUTY_CYCLE STD_DUTY_CYCLE /* FAST_DUTY_CYCLE_2 */\n#    endif\n#else\n// The default timing values below configures the I2C clock to 400khz assuming a 72Mhz clock\n// For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html\n#    ifndef I2C1_TIMINGR_PRESC\n#        define I2C1_TIMINGR_PRESC 0U\n#    endif\n#    ifndef I2C1_TIMINGR_SCLDEL\n#        define I2C1_TIMINGR_SCLDEL 7U\n#    endif\n#    ifndef I2C1_TIMINGR_SDADEL\n#        define I2C1_TIMINGR_SDADEL 0U\n#    endif\n#    ifndef I2C1_TIMINGR_SCLH\n#        define I2C1_TIMINGR_SCLH 38U\n#    endif\n#    ifndef I2C1_TIMINGR_SCLL\n#        define I2C1_TIMINGR_SCLL 129U\n#    endif\n#endif\n\nstatic const I2CConfig i2cconfig = {\n#if defined(USE_I2CV1_CONTRIB)\n    I2C1_CLOCK_SPEED,\n#elif defined(USE_I2CV1)\n    I2C1_OPMODE,\n    I2C1_CLOCK_SPEED,\n    I2C1_DUTY_CYCLE,\n#elif defined(WB32F3G71xx) || defined(WB32FQ95xx)\n    I2C1_OPMODE,\n    I2C1_CLOCK_SPEED,\n#else\n    // This configures the I2C clock to 400khz assuming a 72Mhz clock\n    // For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html\n    STM32_TIMINGR_PRESC(I2C1_TIMINGR_PRESC) | STM32_TIMINGR_SCLDEL(I2C1_TIMINGR_SCLDEL) | STM32_TIMINGR_SDADEL(I2C1_TIMINGR_SDADEL) | STM32_TIMINGR_SCLH(I2C1_TIMINGR_SCLH) | STM32_TIMINGR_SCLL(I2C1_TIMINGR_SCLL), 0, 0\n#endif\n};\n\n/**\n * @brief Handles any I2C error condition by stopping the I2C peripheral and\n * aborting any ongoing transactions. Furthermore ChibiOS status codes are\n * converted into QMK codes.\n *\n * @param status ChibiOS specific I2C status code\n * @return i2c_status_t QMK specific I2C status code\n */\nstatic i2c_status_t i2c_epilogue(const msg_t status) {\n    if (status == MSG_OK) {\n        return I2C_STATUS_SUCCESS;\n    }\n\n    // From ChibiOS HAL: \"After a timeout the driver must be stopped and\n    // restarted because the bus is in an uncertain state.\" We also issue that\n    // hard stop in case of any error.\n    i2cStop(&I2C_DRIVER);\n\n    return status == MSG_TIMEOUT ? I2C_STATUS_TIMEOUT : I2C_STATUS_ERROR;\n}\n\n__attribute__((weak)) void i2c_init(void) {\n    static bool is_initialised = false;\n    if (!is_initialised) {\n        is_initialised = true;\n\n        // Try releasing special pins for a short time\n        palSetLineMode(I2C1_SCL_PIN, PAL_MODE_INPUT);\n        palSetLineMode(I2C1_SDA_PIN, PAL_MODE_INPUT);\n\n        chThdSleepMilliseconds(10);\n#if defined(USE_GPIOV1)\n        palSetLineMode(I2C1_SCL_PIN, I2C1_SCL_PAL_MODE);\n        palSetLineMode(I2C1_SDA_PIN, I2C1_SDA_PAL_MODE);\n#else\n        palSetLineMode(I2C1_SCL_PIN, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN);\n        palSetLineMode(I2C1_SDA_PIN, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN);\n#endif\n    }\n}\n\ni2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2cStart(&I2C_DRIVER, &i2cconfig);\n    msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (address >> 1), data, length, 0, 0, TIME_MS2I(timeout));\n    return i2c_epilogue(status);\n}\n\ni2c_status_t i2c_transmit_and_receive(uint8_t address, const uint8_t* tx_data, uint16_t tx_length, uint8_t* rx_data, uint16_t rx_length, uint16_t timeout) {\n    i2cStart(&I2C_DRIVER, &i2cconfig);\n    msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (address >> 1), tx_data, tx_length, rx_data, rx_length, TIME_MS2I(timeout));\n    return i2c_epilogue(status);\n}\n\ni2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2cStart(&I2C_DRIVER, &i2cconfig);\n    msg_t status = i2cMasterReceiveTimeout(&I2C_DRIVER, (address >> 1), data, length, TIME_MS2I(timeout));\n    return i2c_epilogue(status);\n}\n\ni2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2cStart(&I2C_DRIVER, &i2cconfig);\n\n    uint8_t complete_packet[length + 1];\n    for (uint16_t i = 0; i < length; i++) {\n        complete_packet[i + 1] = data[i];\n    }\n    complete_packet[0] = regaddr;\n\n    msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (devaddr >> 1), complete_packet, length + 1, 0, 0, TIME_MS2I(timeout));\n    return i2c_epilogue(status);\n}\n\ni2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2cStart(&I2C_DRIVER, &i2cconfig);\n\n    uint8_t complete_packet[length + 2];\n    for (uint16_t i = 0; i < length; i++) {\n        complete_packet[i + 2] = data[i];\n    }\n    complete_packet[0] = regaddr >> 8;\n    complete_packet[1] = regaddr & 0xFF;\n\n    msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (devaddr >> 1), complete_packet, length + 2, 0, 0, TIME_MS2I(timeout));\n    return i2c_epilogue(status);\n}\n\ni2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2cStart(&I2C_DRIVER, &i2cconfig);\n    msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (devaddr >> 1), &regaddr, 1, data, length, TIME_MS2I(timeout));\n    return i2c_epilogue(status);\n}\n\ni2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) {\n    i2cStart(&I2C_DRIVER, &i2cconfig);\n    uint8_t register_packet[2] = {regaddr >> 8, regaddr & 0xFF};\n    msg_t   status             = i2cMasterTransmitTimeout(&I2C_DRIVER, (devaddr >> 1), register_packet, 2, data, length, TIME_MS2I(timeout));\n    return i2c_epilogue(status);\n}\n\n__attribute__((weak)) i2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout) {\n    // ChibiOS does not provide low level enough control to check for an ack.\n    // Best effort instead tries reading register 0 which will either succeed or timeout.\n    // This approach may produce false negative results for I2C devices that do not respond to a register 0 read request.\n    uint8_t data = 0;\n    return i2c_read_register(address, 0, &data, sizeof(data), timeout);\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/ps2/ps2_io.c",
    "content": "#include <stdbool.h>\n#include \"ps2_io.h\"\n\n// chibiOS headers\n#include \"ch.h\"\n#include \"hal.h\"\n#include \"gpio.h\"\n\n/* Check port settings for clock and data line */\n#if !(defined(PS2_CLOCK_PIN))\n#    error \"PS/2 clock setting is required in config.h\"\n#endif\n\n#if !(defined(PS2_DATA_PIN))\n#    error \"PS/2 data setting is required in config.h\"\n#endif\n\n/*\n * Clock\n */\nvoid clock_init(void) {}\n\nvoid clock_lo(void) {\n    palSetLineMode(PS2_CLOCK_PIN, PAL_MODE_OUTPUT_OPENDRAIN);\n    palWriteLine(PS2_CLOCK_PIN, PAL_LOW);\n}\n\nvoid clock_hi(void) {\n    palSetLineMode(PS2_CLOCK_PIN, PAL_MODE_OUTPUT_OPENDRAIN);\n    palWriteLine(PS2_CLOCK_PIN, PAL_HIGH);\n}\n\nbool clock_in(void) {\n    palSetLineMode(PS2_CLOCK_PIN, PAL_MODE_INPUT);\n    return palReadLine(PS2_CLOCK_PIN);\n}\n\n/*\n * Data\n */\nvoid data_init(void) {}\n\nvoid data_lo(void) {\n    palSetLineMode(PS2_DATA_PIN, PAL_MODE_OUTPUT_OPENDRAIN);\n    palWriteLine(PS2_DATA_PIN, PAL_LOW);\n}\n\nvoid data_hi(void) {\n    palSetLineMode(PS2_DATA_PIN, PAL_MODE_OUTPUT_OPENDRAIN);\n    palWriteLine(PS2_DATA_PIN, PAL_HIGH);\n}\n\nbool data_in(void) {\n    palSetLineMode(PS2_DATA_PIN, PAL_MODE_INPUT);\n    return palReadLine(PS2_DATA_PIN);\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/serial.c",
    "content": "/*\n * WARNING: be careful changing this code, it is very timing dependent\n */\n\n#include \"serial.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n#include \"synchronization_util.h\"\n\n#include <hal.h>\n\n// TODO: resolve/remove build warnings\n#if defined(RGBLIGHT_ENABLE) && defined(RGBLED_SPLIT) && defined(PROTOCOL_CHIBIOS) && defined(WS2812_BITBANG)\n#    warning \"RGBLED_SPLIT not supported with bitbang WS2812 driver\"\n#endif\n\n// default wait implementation cannot be called within interrupt\n//   this method seems to be more accurate than GPT timers\n#if PORT_SUPPORTS_RT == FALSE\n#    error \"chSysPolledDelayX method not supported on this platform\"\n#else\n#    undef wait_us\n// Force usage of polled waiting - in case WAIT_US_TIMER is activated\n#    define wait_us(us) chSysPolledDelayX(US2RTC(REALTIME_COUNTER_CLOCK, us))\n#endif\n\n#ifndef SELECT_SOFT_SERIAL_SPEED\n#    define SELECT_SOFT_SERIAL_SPEED 1\n// TODO: correct speeds...\n//  0: about 189kbps (Experimental only)\n//  1: about 137kbps (default)\n//  2: about 75kbps\n//  3: about 39kbps\n//  4: about 26kbps\n//  5: about 20kbps\n#endif\n\n// Serial pulse period in microseconds. At the moment, going lower than 12 causes communication failure\n#if SELECT_SOFT_SERIAL_SPEED == 0\n#    define SERIAL_DELAY 12\n#elif SELECT_SOFT_SERIAL_SPEED == 1\n#    define SERIAL_DELAY 16\n#elif SELECT_SOFT_SERIAL_SPEED == 2\n#    define SERIAL_DELAY 24\n#elif SELECT_SOFT_SERIAL_SPEED == 3\n#    define SERIAL_DELAY 32\n#elif SELECT_SOFT_SERIAL_SPEED == 4\n#    define SERIAL_DELAY 48\n#elif SELECT_SOFT_SERIAL_SPEED == 5\n#    define SERIAL_DELAY 64\n#else\n#    error invalid SELECT_SOFT_SERIAL_SPEED value\n#endif\n\ninline static void serial_delay(void) {\n    wait_us(SERIAL_DELAY);\n}\ninline static void serial_delay_half(void) {\n    wait_us(SERIAL_DELAY / 2);\n}\ninline static void serial_delay_blip(void) {\n    wait_us(1);\n}\ninline static void serial_output(void) {\n    gpio_set_pin_output(SOFT_SERIAL_PIN);\n}\ninline static void serial_input(void) {\n    gpio_set_pin_input_high(SOFT_SERIAL_PIN);\n}\ninline static bool serial_read_pin(void) {\n    return !!gpio_read_pin(SOFT_SERIAL_PIN);\n}\ninline static void serial_low(void) {\n    gpio_write_pin_low(SOFT_SERIAL_PIN);\n}\ninline static void serial_high(void) {\n    gpio_write_pin_high(SOFT_SERIAL_PIN);\n}\n\nvoid interrupt_handler(void *arg);\n\n// Use thread + palWaitLineTimeout instead of palSetLineCallback\n//  - Methods like gpio_set_pin_output and palEnableLineEvent/palDisableLineEvent\n//    cause the interrupt to lock up, which would limit to only receiving data...\nstatic THD_WORKING_AREA(waThread1, 128);\nstatic THD_FUNCTION(Thread1, arg) {\n    (void)arg;\n    chRegSetThreadName(\"blinker\");\n    while (true) {\n        palWaitLineTimeout(SOFT_SERIAL_PIN, TIME_INFINITE);\n        interrupt_handler(NULL);\n    }\n}\n\nvoid soft_serial_initiator_init(void) {\n    serial_output();\n    serial_high();\n}\n\nvoid soft_serial_target_init(void) {\n    serial_input();\n\n    palEnablePadEvent(PAL_PORT(SOFT_SERIAL_PIN), PAL_PAD(SOFT_SERIAL_PIN), PAL_EVENT_MODE_FALLING_EDGE);\n    chThdCreateStatic(waThread1, sizeof(waThread1), HIGHPRIO, Thread1, NULL);\n}\n\n// Used by the master to synchronize timing with the slave.\nstatic void __attribute__((noinline)) sync_recv(void) {\n    serial_input();\n    // This shouldn't hang if the slave disconnects because the\n    // serial line will float to high if the slave does disconnect.\n    while (!serial_read_pin()) {\n    }\n\n    serial_delay();\n}\n\n// Used by the slave to send a synchronization signal to the master.\nstatic void __attribute__((noinline)) sync_send(void) {\n    serial_output();\n\n    serial_low();\n    serial_delay();\n\n    serial_high();\n}\n\n// Reads a byte from the serial line\nstatic uint8_t __attribute__((noinline)) serial_read_byte(void) {\n    uint8_t byte = 0;\n    serial_input();\n    for (uint8_t i = 0; i < 8; ++i) {\n        byte = (byte << 1) | serial_read_pin();\n        serial_delay();\n    }\n\n    return byte;\n}\n\n// Sends a byte with MSB ordering\nstatic void __attribute__((noinline)) serial_write_byte(uint8_t data) {\n    uint8_t b = 8;\n    serial_output();\n    while (b--) {\n        if (data & (1 << b)) {\n            serial_high();\n        } else {\n            serial_low();\n        }\n        serial_delay();\n    }\n}\n\n// interrupt handle to be used by the slave device\nvoid interrupt_handler(void *arg) {\n    split_shared_memory_lock_autounlock();\n    chSysLockFromISR();\n\n    sync_send();\n\n    // read mid pulses\n    serial_delay_blip();\n\n    uint8_t checksum_computed = 0;\n    int     sstd_index        = 0;\n\n    sstd_index = serial_read_byte();\n    sync_send();\n\n    split_transaction_desc_t *trans = &split_transaction_table[sstd_index];\n    for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {\n        split_trans_initiator2target_buffer(trans)[i] = serial_read_byte();\n        sync_send();\n        checksum_computed += split_trans_initiator2target_buffer(trans)[i];\n    }\n    checksum_computed ^= 7;\n\n    serial_read_byte();\n    sync_send();\n\n    // wait for the sync to finish sending\n    serial_delay();\n\n    // Allow any slave processing to occur\n    if (trans->slave_callback) {\n        trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans));\n    }\n\n    uint8_t checksum = 0;\n    for (int i = 0; i < trans->target2initiator_buffer_size; ++i) {\n        serial_write_byte(split_trans_target2initiator_buffer(trans)[i]);\n        sync_send();\n        serial_delay_half();\n        checksum += split_trans_target2initiator_buffer(trans)[i];\n    }\n    serial_write_byte(checksum ^ 7);\n    sync_send();\n\n    // wait for the sync to finish sending\n    serial_delay();\n\n    // end transaction\n    serial_input();\n\n    // TODO: remove extra delay between transactions\n    serial_delay();\n\n    chSysUnlockFromISR();\n}\n\nstatic inline bool initiate_transaction(uint8_t sstd_index) {\n    if (sstd_index > NUM_TOTAL_TRANSACTIONS) return false;\n\n    split_shared_memory_lock_autounlock();\n\n    split_transaction_desc_t *trans = &split_transaction_table[sstd_index];\n\n    // TODO: remove extra delay between transactions\n    serial_delay();\n\n    // this code is very time dependent, so we need to disable interrupts\n    chSysLock();\n\n    // signal to the slave that we want to start a transaction\n    serial_output();\n    serial_low();\n    serial_delay_blip();\n\n    // wait for the slaves response\n    serial_input();\n    serial_high();\n    serial_delay();\n\n    // check if the slave is present\n    if (serial_read_pin()) {\n        // slave failed to pull the line low, assume not present\n        serial_dprintf(\"serial::NO_RESPONSE\\n\");\n        chSysUnlock();\n        return false;\n    }\n\n    // if the slave is present synchronize with it\n    uint8_t checksum = 0;\n    // send data to the slave\n    serial_write_byte(sstd_index); // first chunk is transaction id\n    sync_recv();\n\n    for (int i = 0; i < trans->initiator2target_buffer_size; ++i) {\n        serial_write_byte(split_trans_initiator2target_buffer(trans)[i]);\n        sync_recv();\n        checksum += split_trans_initiator2target_buffer(trans)[i];\n    }\n    serial_write_byte(checksum ^ 7);\n    sync_recv();\n\n    serial_delay();\n    serial_delay(); // read mid pulses\n\n    // receive data from the slave\n    uint8_t checksum_computed = 0;\n    for (int i = 0; i < trans->target2initiator_buffer_size; ++i) {\n        split_trans_target2initiator_buffer(trans)[i] = serial_read_byte();\n        sync_recv();\n        checksum_computed += split_trans_target2initiator_buffer(trans)[i];\n    }\n    checksum_computed ^= 7;\n    uint8_t checksum_received = serial_read_byte();\n\n    sync_recv();\n    serial_delay();\n\n    if ((checksum_computed) != (checksum_received)) {\n        serial_dprintf(\"serial::FAIL[%u,%u,%u]\\n\", checksum_computed, checksum_received, sstd_index);\n        serial_output();\n        serial_high();\n\n        chSysUnlock();\n        return false;\n    }\n\n    // always, release the line when not in use\n    serial_high();\n    serial_output();\n\n    chSysUnlock();\n    return true;\n}\n\n/////////\n//  start transaction by initiator\n//\n// bool  soft_serial_transaction(int sstd_index)\n//\n// this code is very time dependent, so we need to disable interrupts\nbool soft_serial_transaction(int sstd_index) {\n    return initiate_transaction((uint8_t)sstd_index);\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/serial_protocol.c",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <ch.h>\n\n#include \"serial.h\"\n#include \"serial_protocol.h\"\n#include \"synchronization_util.h\"\n\nstatic inline bool initiate_transaction(uint8_t transaction_id);\nstatic inline bool react_to_transaction(void);\n\n/**\n * @brief This thread runs on the slave and responds to transactions initiated\n * by the master.\n */\nstatic THD_WORKING_AREA(waSlaveThread, 1024);\nstatic THD_FUNCTION(SlaveThread, arg) {\n    (void)arg;\n    chRegSetThreadName(\"split_protocol_tx_rx\");\n\n    while (true) {\n        if (unlikely(!react_to_transaction())) {\n            /* Clear the receive queue, to start with a clean slate.\n             * Parts of failed transactions or spurious bytes could still be in it. */\n            serial_transport_driver_clear();\n        }\n    }\n}\n\n/**\n * @brief Slave specific initializations.\n */\nvoid soft_serial_target_init(void) {\n    serial_transport_driver_slave_init();\n\n    /* Start transport thread. */\n    chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);\n}\n\n/**\n * @brief Master specific initializations.\n */\nvoid soft_serial_initiator_init(void) {\n    serial_transport_driver_master_init();\n}\n\n/**\n * @brief React to transactions started by the master.\n */\nstatic inline bool react_to_transaction(void) {\n    uint8_t transaction_id = 0;\n    /* Wait until there is a transaction for us. */\n    if (unlikely(!serial_transport_receive_blocking(&transaction_id, sizeof(transaction_id)))) {\n        return false;\n    }\n\n    /* Sanity check that we are actually responding to a valid transaction. */\n    if (unlikely(transaction_id >= NUM_TOTAL_TRANSACTIONS)) {\n        return false;\n    }\n\n    split_shared_memory_lock_autounlock();\n\n    split_transaction_desc_t* transaction = &split_transaction_table[transaction_id];\n\n    /* Send back the handshake which is XORed as a simple checksum,\n     to signal that the slave is ready to receive possible transaction buffers  */\n    transaction_id ^= NUM_TOTAL_TRANSACTIONS;\n    if (unlikely(!serial_transport_send(&transaction_id, sizeof(transaction_id)))) {\n        return false;\n    }\n\n    /* Receive transaction buffer from the master. If this transaction requires it.*/\n    if (transaction->initiator2target_buffer_size) {\n        if (unlikely(!serial_transport_receive(split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size))) {\n            return false;\n        }\n    }\n\n    /* Allow any slave processing to occur. */\n    if (transaction->slave_callback) {\n        transaction->slave_callback(transaction->initiator2target_buffer_size, split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size, split_trans_target2initiator_buffer(transaction));\n    }\n\n    /* Send transaction buffer to the master. If this transaction requires it. */\n    if (transaction->target2initiator_buffer_size) {\n        if (unlikely(!serial_transport_send(split_trans_target2initiator_buffer(transaction), transaction->target2initiator_buffer_size))) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\n/**\n * @brief Start transaction from the master half to the slave half.\n *\n * @param index Transaction Table index of the transaction to start.\n * @return bool Indicates success of transaction.\n */\nbool soft_serial_transaction(int index) {\n    /* Clear the receive queue, to start with a clean slate.\n     * Parts of failed transactions or spurious bytes could still be in it. */\n    serial_transport_driver_clear();\n\n    return initiate_transaction((uint8_t)index);\n}\n\n/**\n * @brief Initiate transaction to slave half.\n */\nstatic inline bool initiate_transaction(uint8_t transaction_id) {\n    /* Sanity check that we are actually starting a valid transaction. */\n    if (unlikely(transaction_id >= NUM_TOTAL_TRANSACTIONS)) {\n        serial_dprintf(\"SPLIT: illegal transaction id\\n\");\n        return false;\n    }\n\n    split_shared_memory_lock_autounlock();\n\n    split_transaction_desc_t* transaction = &split_transaction_table[transaction_id];\n\n    /* Send transaction table index to the slave, which doubles as basic handshake token. */\n    if (unlikely(!serial_transport_send(&transaction_id, sizeof(transaction_id)))) {\n        serial_dprintf(\"SPLIT: sending handshake failed\\n\");\n        return false;\n    }\n\n    uint8_t transaction_id_shake = 0xFF;\n\n    /* Which we always read back first so that we can error out correctly.\n     *   - due to the half duplex limitations on return codes, we always have to read *something*.\n     *   - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready.\n     */\n    if (unlikely(!serial_transport_receive(&transaction_id_shake, sizeof(transaction_id_shake)) || (transaction_id_shake != (transaction_id ^ NUM_TOTAL_TRANSACTIONS)))) {\n        serial_dprintf(\"SPLIT: receiving handshake failed\\n\");\n        return false;\n    }\n\n    /* Send transaction buffer to the slave. If this transaction requires it. */\n    if (transaction->initiator2target_buffer_size) {\n        if (unlikely(!serial_transport_send(split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size))) {\n            serial_dprintf(\"SPLIT: sending buffer failed\\n\");\n            return false;\n        }\n    }\n\n    /* Receive transaction buffer from the slave. If this transaction requires it. */\n    if (transaction->target2initiator_buffer_size) {\n        if (unlikely(!serial_transport_receive(split_trans_target2initiator_buffer(transaction), transaction->target2initiator_buffer_size))) {\n            serial_dprintf(\"SPLIT: receiving buffer failed\\n\");\n            return false;\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/serial_protocol.h",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n#pragma once\n\n/**\n * @brief Clears any intermediate sending or receiving state of the driver to a known good\n * state. This happens after errors in the middle of transactions, to start with\n * a clean slate.\n */\nvoid serial_transport_driver_clear(void);\n\n/**\n * @brief Driver specific initialization on the slave half.\n */\nvoid serial_transport_driver_slave_init(void);\n\n/**\n * @brief Driver specific specific initialization on the master half.\n */\nvoid serial_transport_driver_master_init(void);\n\n/**\n * @brief  Blocking receive of size * bytes.\n *\n * @return true Receive success.\n * @return false Receive failed, e.g. by bit errors.\n */\nbool __attribute__((nonnull, hot)) serial_transport_receive(uint8_t* destination, const size_t size);\n\n/**\n * @brief Blocking receive of size * bytes with an implicitly defined timeout.\n *\n * @return true Receive success.\n * @return false Receive failed, e.g. by timeout or bit errors.\n */\nbool __attribute__((nonnull, hot)) serial_transport_receive_blocking(uint8_t* destination, const size_t size);\n\n/**\n * @brief Blocking send of buffer with timeout.\n *\n * @return true Send success.\n * @return false Send failed, e.g. by timeout or bit errors.\n */\nbool __attribute__((nonnull, hot)) serial_transport_send(const uint8_t* source, const size_t size);\n"
  },
  {
    "path": "platforms/chibios/drivers/serial_usart.c",
    "content": "// Copyright 2021 QMK\n// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"serial_usart.h\"\n#include \"serial_protocol.h\"\n#include \"synchronization_util.h\"\n#include \"chibios_config.h\"\n\n#if defined(SERIAL_USART_CONFIG)\nstatic QMKSerialConfig serial_config = SERIAL_USART_CONFIG;\n#elif defined(MCU_AT32) /* AT32 MCUs */\nstatic QMKSerialConfig serial_config = {\n    .speed = (SERIAL_USART_SPEED),\n    .ctrl1 = (SERIAL_USART_CTRL1),\n    .ctrl2 = (SERIAL_USART_CTRL2),\n#    if !defined(SERIAL_USART_FULL_DUPLEX)\n    .ctrl3 = ((SERIAL_USART_CTRL3) | USART_CTRL3_SLBEN) /* activate half-duplex mode */\n#    else\n    .ctrl3 = (SERIAL_USART_CTRL3)\n#    endif\n};\n#elif defined(MCU_STM32) /* STM32 MCUs */\nstatic QMKSerialConfig serial_config = {\n#    if HAL_USE_SERIAL\n    .speed = (SERIAL_USART_SPEED),\n#    else\n    .baud = (SERIAL_USART_SPEED),\n#    endif\n    .cr1   = (SERIAL_USART_CR1),\n    .cr2   = (SERIAL_USART_CR2),\n#    if !defined(SERIAL_USART_FULL_DUPLEX)\n    .cr3   = ((SERIAL_USART_CR3) | USART_CR3_HDSEL) /* activate half-duplex mode */\n#    else\n    .cr3  = (SERIAL_USART_CR3)\n#    endif\n};\n#elif defined(MCU_RP) /* Raspberry Pi MCUs */\n/* USART in 8E2 config with RX and TX FIFOs enabled. */\n// clang-format off\nstatic QMKSerialConfig serial_config = {\n    .baud = (SERIAL_USART_SPEED),\n    .UARTLCR_H = UART_UARTLCR_H_WLEN_8BITS | UART_UARTLCR_H_PEN | UART_UARTLCR_H_STP2 | UART_UARTLCR_H_FEN,\n    .UARTCR = 0U,\n    .UARTIFLS = UART_UARTIFLS_RXIFLSEL_1_8F | UART_UARTIFLS_TXIFLSEL_1_8E,\n    .UARTDMACR = 0U\n};\n// clang-format on\n#else\n#    error MCU Familiy not supported by default, supply your own serial_config by defining SERIAL_USART_CONFIG in your keyboard files.\n#endif\n\nstatic QMKSerialDriver* serial_driver = (QMKSerialDriver*)&SERIAL_USART_DRIVER;\n\n#if HAL_USE_SERIAL\n\n/**\n * @brief SERIAL Driver startup routine.\n */\nstatic inline void usart_driver_start(void) {\n    sdStart(serial_driver, &serial_config);\n}\n\ninline void serial_transport_driver_clear(void) {\n    osalSysLock();\n    bool volatile queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue);\n    osalSysUnlock();\n\n    while (queue_not_empty) {\n        osalSysLock();\n        /* Hard reset the input queue. */\n        iqResetI(&serial_driver->iqueue);\n        osalSysUnlock();\n        /* Allow pending interrupts to preempt.\n         * Do not merge the lock/unlock blocks into one\n         * or the code will not work properly.\n         * The empty read adds a tiny amount of delay. */\n        (void)queue_not_empty;\n        osalSysLock();\n        queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue);\n        osalSysUnlock();\n    }\n}\n\n#elif HAL_USE_SIO\n\n/**\n * @brief SIO Driver startup routine.\n */\nstatic inline void usart_driver_start(void) {\n    sioStart(serial_driver, &serial_config);\n}\n\ninline void serial_transport_driver_clear(void) {\n    if (sioHasRXErrorsX(serial_driver)) {\n        sioGetAndClearErrors(serial_driver);\n    }\n    osalSysLock();\n    while (!sioIsRXEmptyX(serial_driver)) {\n        (void)sioGetX(serial_driver);\n    }\n    osalSysUnlock();\n}\n\n#else\n\n#    error Either the SERIAL or SIO driver has to be activated to use the usart driver for split keyboards.\n\n#endif\n\ninline bool serial_transport_send(const uint8_t* source, const size_t size) {\n    bool success = (size_t)chnWriteTimeout(serial_driver, source, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;\n\n#if !defined(SERIAL_USART_FULL_DUPLEX)\n    /* Half duplex fills the input queue with the data we wrote - just throw it away. */\n    if (likely(success)) {\n        size_t bytes_left = size;\n#    if HAL_USE_SERIAL\n        /* The SERIAL driver uses large soft FIFOs that are filled from an IRQ\n         * context, so there is a delay between receiving the data and it\n         * becoming actually available, therefore we have to apply a timeout\n         * mechanism. Under the right circumstances (e.g. bad cables paired with\n         * high baud rates) less bytes can be present in the input queue as\n         * well. */\n        uint8_t dump[64];\n\n        while (unlikely(bytes_left >= 64)) {\n            if (unlikely(!serial_transport_receive(dump, 64))) {\n                return false;\n            }\n            bytes_left -= 64;\n        }\n\n        return serial_transport_receive(dump, bytes_left);\n#    else\n        /* The SIO driver directly accesses the hardware FIFOs of the USART\n         * peripheral. As these are limited in depth, the RX FIFO might have\n         * been overflowed by a large transaction that we just send. Therefore\n         * we attempt to read back all the data we send or until the FIFO runs\n         * empty in case it overflowed and data was truncated. */\n        if (unlikely(sioSynchronizeTXEnd(serial_driver, TIME_MS2I(SERIAL_USART_TIMEOUT)) < MSG_OK)) {\n            return false;\n        }\n\n        osalSysLock();\n        while (bytes_left > 0 && !sioIsRXEmptyX(serial_driver)) {\n            (void)sioGetX(serial_driver);\n            bytes_left--;\n        }\n        osalSysUnlock();\n#    endif\n    }\n#endif\n\n    return success;\n}\n\ninline bool serial_transport_receive(uint8_t* destination, const size_t size) {\n    bool success = (size_t)chnReadTimeout(serial_driver, destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size;\n    return success;\n}\n\ninline bool serial_transport_receive_blocking(uint8_t* destination, const size_t size) {\n    bool success = (size_t)chnRead(serial_driver, destination, size) == size;\n    return success;\n}\n\n#if !defined(SERIAL_USART_FULL_DUPLEX)\n\n/**\n * @brief Initiate pins for USART peripheral. Half-duplex configuration.\n */\n__attribute__((weak)) void usart_init(void) {\n#    if defined(MCU_STM32) || defined(MCU_AT32) /* STM32 and AT32 MCUs */\n#        if defined(USE_GPIOV1)\n    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_OPENDRAIN);\n#        else\n    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN);\n#        endif\n\n#        if defined(USART_REMAP)\n    USART_REMAP;\n#        endif\n#    elif defined(MCU_RP) /* Raspberry Pi MCUs */\n#        error Half-duplex with the SIO driver is not supported due to hardware limitations on the RP2040, switch to the PIO driver which has half-duplex support.\n#    else\n#        pragma message \"usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files.\"\n#    endif\n}\n\n#else\n\n/**\n * @brief Initiate pins for USART peripheral. Full-duplex configuration.\n */\n__attribute__((weak)) void usart_init(void) {\n#    if defined(MCU_STM32) || defined(MCU_AT32) /* STM32 and AT32 MCUs */\n#        if defined(USE_GPIOV1)\n    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_PUSHPULL);\n    palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);\n#        else\n    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);\n    palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);\n#        endif\n\n#        if defined(USART_REMAP)\n    USART_REMAP;\n#        endif\n#    elif defined(MCU_RP) /* Raspberry Pi MCUs */\n    palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_UART);\n    palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE_UART);\n#    else\n#        pragma message \"usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files.\"\n#    endif\n}\n\n#endif\n\n/**\n * @brief Overridable master specific initializations.\n */\n__attribute__((weak, nonnull)) void usart_master_init(QMKSerialDriver** driver) {\n    (void)driver;\n    usart_init();\n}\n\n/**\n * @brief Overridable slave specific initializations.\n */\n__attribute__((weak, nonnull)) void usart_slave_init(QMKSerialDriver** driver) {\n    (void)driver;\n    usart_init();\n}\n\nvoid serial_transport_driver_slave_init(void) {\n    usart_slave_init(&serial_driver);\n    usart_driver_start();\n}\n\nvoid serial_transport_driver_master_init(void) {\n    usart_master_init(&serial_driver);\n\n#if defined(MCU_STM32) && defined(SERIAL_USART_PIN_SWAP)\n    serial_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins\n#endif\n\n    usart_driver_start();\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/serial_usart.h",
    "content": "// Copyright 2021 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"serial.h\"\n#include <hal.h>\n\n#if defined(SOFT_SERIAL_PIN)\n#    define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN\n#endif\n\n#if !defined(SERIAL_USART_TX_PIN)\n#    define SERIAL_USART_TX_PIN A9\n#endif\n\n#if !defined(SERIAL_USART_RX_PIN)\n#    define SERIAL_USART_RX_PIN A10\n#endif\n\n#if !defined(SELECT_SOFT_SERIAL_SPEED)\n#    define SELECT_SOFT_SERIAL_SPEED 1\n#endif\n\n#if defined(SERIAL_USART_SPEED)\n// Allow advanced users to directly set SERIAL_USART_SPEED\n#elif SELECT_SOFT_SERIAL_SPEED == 0\n#    define SERIAL_USART_SPEED 460800\n#elif SELECT_SOFT_SERIAL_SPEED == 1\n#    define SERIAL_USART_SPEED 230400\n#elif SELECT_SOFT_SERIAL_SPEED == 2\n#    define SERIAL_USART_SPEED 115200\n#elif SELECT_SOFT_SERIAL_SPEED == 3\n#    define SERIAL_USART_SPEED 57600\n#elif SELECT_SOFT_SERIAL_SPEED == 4\n#    define SERIAL_USART_SPEED 38400\n#elif SELECT_SOFT_SERIAL_SPEED == 5\n#    define SERIAL_USART_SPEED 19200\n#else\n#    error invalid SELECT_SOFT_SERIAL_SPEED value\n#endif\n\n#if !defined(SERIAL_USART_TIMEOUT)\n#    define SERIAL_USART_TIMEOUT 20\n#endif\n\n#if HAL_USE_SERIAL\n\ntypedef SerialDriver QMKSerialDriver;\ntypedef SerialConfig QMKSerialConfig;\n\n#    if !defined(SERIAL_USART_DRIVER)\n#        define SERIAL_USART_DRIVER SD1\n#    endif\n\n#elif HAL_USE_SIO\n\ntypedef SIODriver QMKSerialDriver;\ntypedef SIOConfig QMKSerialConfig;\n\n#    if !defined(SERIAL_USART_DRIVER)\n#        define SERIAL_USART_DRIVER SIOD1\n#    endif\n\n#endif\n\n#if !defined(USE_GPIOV1)\n/* The default PAL alternate modes are used to signal that the pins are used for USART. */\n#    if !defined(SERIAL_USART_TX_PAL_MODE)\n#        define SERIAL_USART_TX_PAL_MODE 7\n#    endif\n#    if !defined(SERIAL_USART_RX_PAL_MODE)\n#        define SERIAL_USART_RX_PAL_MODE 7\n#    endif\n#endif\n\n#if defined(MCU_STM32) /* STM32 MCUs */\n#    if !defined(USART_CR1_M0)\n#        define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so\n#    endif\n\n#    if !defined(SERIAL_USART_CR1)\n#        define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length\n#    endif\n\n#    if !defined(SERIAL_USART_CR2)\n#        define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits\n#    endif\n\n#    if !defined(SERIAL_USART_CR3)\n#        define SERIAL_USART_CR3 0\n#    endif\n\n#    if defined(USART1_REMAP)\n#        define USART_REMAP                             \\\n            do {                                        \\\n                (AFIO->MAPR |= AFIO_MAPR_USART1_REMAP); \\\n            } while (0)\n#    elif defined(USART2_REMAP)\n#        define USART_REMAP                             \\\n            do {                                        \\\n                (AFIO->MAPR |= AFIO_MAPR_USART2_REMAP); \\\n            } while (0)\n#    elif defined(USART3_PARTIALREMAP)\n#        define USART_REMAP                                          \\\n            do {                                                     \\\n                (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_PARTIALREMAP); \\\n            } while (0)\n#    elif defined(USART3_FULLREMAP)\n#        define USART_REMAP                                       \\\n            do {                                                  \\\n                (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); \\\n            } while (0)\n#    endif\n#elif defined(MCU_AT32) /* AT32 MCUs */\n#    if !defined(USART_CTRL1_DBN0)\n#        define USART_CTRL1_DBN0 USART_CTRL1_DBN\n#    endif\n\n#    if !defined(SERIAL_USART_CTRL1)\n#        define SERIAL_USART_CTRL1 (USART_CTRL1_PEN | USART_CTRL1_PSEL | USART_CTRL1_DBN0) // parity enable, odd parity, 9 bit length\n#    endif\n\n#    if !defined(SERIAL_USART_CTRL2)\n#        define SERIAL_USART_CTRL2 (USART_CTRL2_STOPBN_1) // 2 stop bits\n#    endif\n\n#    if !defined(SERIAL_USART_CTRL3)\n#        define SERIAL_USART_CTRL3 0\n#    endif\n\n#    if defined(USART1_REMAP)\n#        define USART_REMAP                               \\\n            do {                                          \\\n                (IOMUX->REMAP |= IOMUX_REMAP_USART1_MUX); \\\n            } while (0)\n#    elif defined(USART3_PARTIALREMAP)\n#        define USART_REMAP                                    \\\n            do {                                               \\\n                (IOMUX->REMAP |= IOMUX_REMAP_USART3_MUX_MUX1); \\\n            } while (0)\n#    elif defined(USART3_FULLREMAP)\n#        define USART_REMAP                                    \\\n            do {                                               \\\n                (IOMUX->REMAP |= IOMUX_REMAP_USART3_MUX_MUX2); \\\n            } while (0)\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/spi_master.c",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n *  This program is free software: you can redistribute it and/or modify\n *  it under the terms of the GNU General Public License as published by\n *  the Free Software Foundation, either version 3 of the License, or\n *  (at your option) any later version.\n *\n *  This program is distributed in the hope that it will be useful,\n *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n *  GNU General Public License for more details.\n *\n *  You should have received a copy of the GNU General Public License\n *  along with this program.  If not, see <https://www.gnu.org/licenses/>.\n */\n\n#include \"spi_master.h\"\n#include \"chibios_config.h\"\n#include <ch.h>\n#include <hal.h>\n\n#ifndef SPI_DRIVER\n#    define SPI_DRIVER SPID2\n#endif\n\n#ifndef SPI_SCK_PIN\n#    define SPI_SCK_PIN B13\n#endif\n\n#ifndef SPI_SCK_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define SPI_SCK_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    else\n#        define SPI_SCK_PAL_MODE 5\n#    endif\n#endif\n\n#ifndef SPI_MOSI_PIN\n#    define SPI_MOSI_PIN B15\n#endif\n\n#ifndef SPI_MOSI_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define SPI_MOSI_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    else\n#        define SPI_MOSI_PAL_MODE 5\n#    endif\n#endif\n\n#ifndef SPI_MISO_PIN\n#    define SPI_MISO_PIN B14\n#endif\n\n#ifndef SPI_MISO_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define SPI_MISO_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    else\n#        define SPI_MISO_PAL_MODE 5\n#    endif\n#endif\n\nstatic bool spiStarted = false;\n#if SPI_SELECT_MODE == SPI_SELECT_MODE_NONE\nstatic pin_t current_slave_pin     = NO_PIN;\nstatic bool  current_cs_active_low = true;\n#endif\n\nstatic SPIConfig spiConfig;\n\nstatic inline void spi_select(void) {\n    spiSelect(&SPI_DRIVER);\n\n#if SPI_SELECT_MODE == SPI_SELECT_MODE_NONE\n    if (current_slave_pin != NO_PIN) {\n        gpio_write_pin(current_slave_pin, current_cs_active_low ? 0 : 1);\n    }\n#endif\n}\n\nstatic inline void spi_unselect(void) {\n#if SPI_SELECT_MODE == SPI_SELECT_MODE_NONE\n    if (current_slave_pin != NO_PIN) {\n        gpio_write_pin(current_slave_pin, current_cs_active_low ? 1 : 0);\n    }\n#endif\n\n    spiUnselect(&SPI_DRIVER);\n}\n\n__attribute__((weak)) void spi_init(void) {\n    static bool is_initialised = false;\n    if (!is_initialised) {\n        is_initialised = true;\n\n        // Try releasing special pins for a short time\n        gpio_set_pin_input(SPI_SCK_PIN);\n        if (SPI_MOSI_PIN != NO_PIN) {\n            gpio_set_pin_input(SPI_MOSI_PIN);\n        }\n        if (SPI_MISO_PIN != NO_PIN) {\n            gpio_set_pin_input(SPI_MISO_PIN);\n        }\n\n        chThdSleepMilliseconds(10);\n#if defined(USE_GPIOV1)\n        palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), SPI_SCK_PAL_MODE);\n        if (SPI_MOSI_PIN != NO_PIN) {\n            palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), SPI_MOSI_PAL_MODE);\n        }\n        if (SPI_MISO_PIN != NO_PIN) {\n            palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), SPI_MISO_PAL_MODE);\n        }\n#else\n        palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), SPI_SCK_FLAGS);\n        if (SPI_MOSI_PIN != NO_PIN) {\n            palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), SPI_MOSI_FLAGS);\n        }\n        if (SPI_MISO_PIN != NO_PIN) {\n            palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), SPI_MISO_FLAGS);\n        }\n#endif\n        spiStop(&SPI_DRIVER);\n        spiStarted = false;\n    }\n}\n\nbool spi_start_extended(spi_start_config_t *start_config) {\n#if (SPI_USE_MUTUAL_EXCLUSION == TRUE)\n    spiAcquireBus(&SPI_DRIVER);\n#endif // (SPI_USE_MUTUAL_EXCLUSION == TRUE)\n\n    if (spiStarted) {\n        return false;\n    }\n#if SPI_SELECT_MODE != SPI_SELECT_MODE_NONE\n    if (start_config->slave_pin == NO_PIN) {\n        return false;\n    }\n#endif\n\n#if !(defined(WB32F3G71xx) || defined(WB32FQ95xx))\n    uint16_t roundedDivisor = 2;\n    while (roundedDivisor < start_config->divisor) {\n        roundedDivisor <<= 1;\n    }\n\n#    if defined(AT32F415)\n    if (roundedDivisor < 2 || roundedDivisor > 1024) {\n        return false;\n    }\n#    else\n    if (roundedDivisor < 2 || roundedDivisor > 256) {\n        return false;\n    }\n#    endif\n#endif\n\n#if defined(K20x) || defined(KL2x)\n    spiConfig.tar0 = SPIx_CTARn_FMSZ(7) | SPIx_CTARn_ASC(1);\n\n    if (start_config->lsb_first) {\n        spiConfig.tar0 |= SPIx_CTARn_LSBFE;\n    }\n\n    switch (start_config->mode) {\n        case 0:\n            break;\n        case 1:\n            spiConfig.tar0 |= SPIx_CTARn_CPHA;\n            break;\n        case 2:\n            spiConfig.tar0 |= SPIx_CTARn_CPOL;\n            break;\n        case 3:\n            spiConfig.tar0 |= SPIx_CTARn_CPHA | SPIx_CTARn_CPOL;\n            break;\n    }\n\n    switch (roundedDivisor) {\n        case 2:\n            spiConfig.tar0 |= SPIx_CTARn_BR(0);\n            break;\n        case 4:\n            spiConfig.tar0 |= SPIx_CTARn_BR(1);\n            break;\n        case 8:\n            spiConfig.tar0 |= SPIx_CTARn_BR(3);\n            break;\n        case 16:\n            spiConfig.tar0 |= SPIx_CTARn_BR(4);\n            break;\n        case 32:\n            spiConfig.tar0 |= SPIx_CTARn_BR(5);\n            break;\n        case 64:\n            spiConfig.tar0 |= SPIx_CTARn_BR(6);\n            break;\n        case 128:\n            spiConfig.tar0 |= SPIx_CTARn_BR(7);\n            break;\n        case 256:\n            spiConfig.tar0 |= SPIx_CTARn_BR(8);\n            break;\n    }\n\n#elif defined(HT32)\n    spiConfig.cr0 = SPI_CR0_SELOEN;\n    spiConfig.cr1 = SPI_CR1_MODE | 8; // 8 bits and in master mode\n\n    if (start_config->lsb_first) {\n        spiConfig.cr1 |= SPI_CR1_FIRSTBIT;\n    }\n\n    switch (start_config->mode) {\n        case 0:\n            spiConfig.cr1 |= SPI_CR1_FORMAT_MODE0;\n            break;\n        case 1:\n            spiConfig.cr1 |= SPI_CR1_FORMAT_MODE1;\n            break;\n        case 2:\n            spiConfig.cr1 |= SPI_CR1_FORMAT_MODE2;\n            break;\n        case 3:\n            spiConfig.cr1 |= SPI_CR1_FORMAT_MODE3;\n            break;\n    }\n\n    spiConfig.cpr = (roundedDivisor - 1) >> 1;\n\n#elif defined(WB32F3G71xx) || defined(WB32FQ95xx)\n    if (!start_config->lsb_first) {\n        osalDbgAssert(start_config->lsb_first != FALSE, \"unsupported lsb_first\");\n    }\n\n    if (start_config->divisor < 1) {\n        return false;\n    }\n\n    spiConfig.SPI_BaudRatePrescaler = (start_config->divisor << 2);\n\n    switch (start_config->mode) {\n        case 0:\n            spiConfig.SPI_CPHA = SPI_CPHA_1Edge;\n            spiConfig.SPI_CPOL = SPI_CPOL_Low;\n            break;\n        case 1:\n            spiConfig.SPI_CPHA = SPI_CPHA_2Edge;\n            spiConfig.SPI_CPOL = SPI_CPOL_Low;\n            break;\n        case 2:\n            spiConfig.SPI_CPHA = SPI_CPHA_1Edge;\n            spiConfig.SPI_CPOL = SPI_CPOL_High;\n            break;\n        case 3:\n            spiConfig.SPI_CPHA = SPI_CPHA_2Edge;\n            spiConfig.SPI_CPOL = SPI_CPOL_High;\n            break;\n    }\n#elif defined(MCU_RP)\n    if (start_config->lsb_first) {\n        osalDbgAssert(start_config->lsb_first == false, \"RP2040s PrimeCell SPI implementation does not support sending LSB first.\");\n    }\n\n    // Motorola frame format and 8bit transfer data size.\n    spiConfig.SSPCR0 = SPI_SSPCR0_FRF_MOTOROLA | SPI_SSPCR0_DSS_8BIT;\n    // Serial output clock = (ck_sys or ck_peri) / (SSPCPSR->CPSDVSR * (1 +\n    // SSPCR0->SCR)). SCR is always set to zero, as QMK SPI API expects the\n    // passed divisor to be the only value to divide the input clock by.\n    spiConfig.SSPCPSR = roundedDivisor; // Even number from 2 to 254\n\n    switch (start_config->mode) {\n        case 0:\n            spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPO; // Clock polarity: low\n            spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPH; // Clock phase: sample on first edge\n            break;\n        case 1:\n            spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPO; // Clock polarity: low\n            spiConfig.SSPCR0 |= SPI_SSPCR0_SPH;  // Clock phase: sample on second edge transition\n            break;\n        case 2:\n            spiConfig.SSPCR0 |= SPI_SSPCR0_SPO;  // Clock polarity: high\n            spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPH; // Clock phase: sample on first edge\n            break;\n        case 3:\n            spiConfig.SSPCR0 |= SPI_SSPCR0_SPO; // Clock polarity: high\n            spiConfig.SSPCR0 |= SPI_SSPCR0_SPH; // Clock phase: sample on second edge transition\n            break;\n    }\n#elif defined(AT32F415)\n    spiConfig.ctrl1 = 0;\n\n    if (start_config->lsb_first) {\n        spiConfig.ctrl1 |= SPI_CTRL1_LTF;\n    }\n\n    switch (start_config->mode) {\n        case 0:\n            break;\n        case 1:\n            spiConfig.ctrl1 |= SPI_CTRL1_CLKPHA;\n            break;\n        case 2:\n            spiConfig.ctrl1 |= SPI_CTRL1_CLKPOL;\n            break;\n        case 3:\n            spiConfig.ctrl1 |= SPI_CTRL1_CLKPHA | SPI_CTRL1_CLKPOL;\n            break;\n    }\n\n    switch (roundedDivisor) {\n        case 2:\n            break;\n        case 4:\n            spiConfig.ctrl1 |= SPI_CTRL1_MDIV_0;\n            break;\n        case 8:\n            spiConfig.ctrl1 |= SPI_CTRL1_MDIV_1;\n            break;\n        case 16:\n            spiConfig.ctrl1 |= SPI_CTRL1_MDIV_1 | SPI_CTRL1_MDIV_0;\n            break;\n        case 32:\n            spiConfig.ctrl1 |= SPI_CTRL1_MDIV_2;\n            break;\n        case 64:\n            spiConfig.ctrl1 |= SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_0;\n            break;\n        case 128:\n            spiConfig.ctrl1 |= SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_1;\n            break;\n        case 256:\n            spiConfig.ctrl1 |= SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_1 | SPI_CTRL1_MDIV_0;\n            break;\n        case 512:\n            spiConfig.ctrl2 |= SPI_CTRL1_MDIV_3;\n            break;\n        case 1024:\n            spiConfig.ctrl2 |= SPI_CTRL1_MDIV_3;\n            spiConfig.ctrl1 |= SPI_CTRL1_MDIV_0;\n            break;\n    }\n#else\n    spiConfig.cr1 = 0;\n\n    if (start_config->lsb_first) {\n        spiConfig.cr1 |= SPI_CR1_LSBFIRST;\n    }\n\n    switch (start_config->mode) {\n        case 0:\n            break;\n        case 1:\n            spiConfig.cr1 |= SPI_CR1_CPHA;\n            break;\n        case 2:\n            spiConfig.cr1 |= SPI_CR1_CPOL;\n            break;\n        case 3:\n            spiConfig.cr1 |= SPI_CR1_CPHA | SPI_CR1_CPOL;\n            break;\n    }\n\n    switch (roundedDivisor) {\n        case 2:\n            break;\n        case 4:\n            spiConfig.cr1 |= SPI_CR1_BR_0;\n            break;\n        case 8:\n            spiConfig.cr1 |= SPI_CR1_BR_1;\n            break;\n        case 16:\n            spiConfig.cr1 |= SPI_CR1_BR_1 | SPI_CR1_BR_0;\n            break;\n        case 32:\n            spiConfig.cr1 |= SPI_CR1_BR_2;\n            break;\n        case 64:\n            spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_0;\n            break;\n        case 128:\n            spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1;\n            break;\n        case 256:\n            spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0;\n            break;\n    }\n#endif\n\n    spiStarted = true;\n#if SPI_SELECT_MODE == SPI_SELECT_MODE_NONE\n    current_slave_pin     = start_config->slave_pin;\n    current_cs_active_low = start_config->cs_active_low;\n#endif\n#if SPI_SELECT_MODE == SPI_SELECT_MODE_PAD\n    spiConfig.ssport = PAL_PORT(start_config->slave_pin);\n    spiConfig.sspad  = PAL_PAD(start_config->slave_pin);\n    gpio_set_pin_output(start_config->slave_pin);\n#elif SPI_SELECT_MODE == SPI_SELECT_MODE_NONE\n    if (start_config->slave_pin != NO_PIN) {\n        gpio_set_pin_output(start_config->slave_pin);\n    }\n#else\n#    error \"Unsupported SPI_SELECT_MODE\"\n#endif\n\n    spiStart(&SPI_DRIVER, &spiConfig);\n    spi_select();\n\n    return true;\n}\n\nbool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) {\n    spi_start_config_t start_config = {0};\n    start_config.slave_pin          = slavePin;\n    start_config.lsb_first          = lsbFirst;\n    start_config.mode               = mode;\n    start_config.divisor            = divisor;\n    start_config.cs_active_low      = true;\n    return spi_start_extended(&start_config);\n}\n\nspi_status_t spi_write(uint8_t data) {\n    uint8_t rxData;\n    spiExchange(&SPI_DRIVER, 1, &data, &rxData);\n\n    return rxData;\n}\n\nspi_status_t spi_read(void) {\n    uint8_t data = 0;\n    spiReceive(&SPI_DRIVER, 1, &data);\n\n    return data;\n}\n\nspi_status_t spi_transmit(const uint8_t *data, uint16_t length) {\n    spiSend(&SPI_DRIVER, length, data);\n    return SPI_STATUS_SUCCESS;\n}\n\nspi_status_t spi_receive(uint8_t *data, uint16_t length) {\n    spiReceive(&SPI_DRIVER, length, data);\n    return SPI_STATUS_SUCCESS;\n}\n\nvoid spi_stop(void) {\n    if (spiStarted) {\n        spi_unselect();\n        spiStop(&SPI_DRIVER);\n        spiStarted = false;\n    }\n\n#if (SPI_USE_MUTUAL_EXCLUSION == TRUE)\n    spiReleaseBus(&SPI_DRIVER);\n#endif // (SPI_USE_MUTUAL_EXCLUSION == TRUE)\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/uart_serial.c",
    "content": "// Copyright 2024 Stefan Kerkmann (@KarlK90)\n// Copyright 2021 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"uart.h\"\n#include \"gpio.h\"\n#include \"chibios_config.h\"\n#include <hal.h>\n\n#ifndef UART_DRIVER\n#    define UART_DRIVER SD1\n#endif\n\n#ifndef UART_TX_PIN\n#    define UART_TX_PIN A9\n#endif\n\n#ifndef UART_TX_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define UART_TX_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    else\n#        define UART_TX_PAL_MODE 7\n#    endif\n#endif\n\n#ifndef UART_RX_PIN\n#    define UART_RX_PIN A10\n#endif\n\n#ifndef UART_RX_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define UART_RX_PAL_MODE PAL_MODE_INPUT\n#    else\n#        define UART_RX_PAL_MODE 7\n#    endif\n#endif\n\n#ifndef UART_CTS_PIN\n#    define UART_CTS_PIN A11\n#endif\n\n#ifndef UART_CTS_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define UART_CTS_PAL_MODE PAL_MODE_INPUT\n#    else\n#        define UART_CTS_PAL_MODE 7\n#    endif\n#endif\n\n#ifndef UART_RTS_PIN\n#    define UART_RTS_PIN A12\n#endif\n\n#ifndef UART_RTS_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define UART_RTS_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    else\n#        define UART_RTS_PAL_MODE 7\n#    endif\n#endif\n\n#ifndef UART_CR1\n#    define UART_CR1 0\n#endif\n\n#ifndef UART_CR2\n#    define UART_CR2 0\n#endif\n\n#ifndef UART_CR3\n#    define UART_CR3 0\n#endif\n\n#ifndef UART_WRDLEN\n#    define UART_WRDLEN 3\n#endif\n\n#ifndef UART_STPBIT\n#    define UART_STPBIT 0\n#endif\n\n#ifndef UART_PARITY\n#    define UART_PARITY 0\n#endif\n\n#ifndef UART_ATFLCT\n#    define UART_ATFLCT 0\n#endif\n\n#if defined(MCU_KINETIS)\nstatic SerialConfig serialConfig = {SERIAL_DEFAULT_BITRATE};\n#elif defined(WB32F3G71xx) || defined(WB32FQ95xx)\nstatic SerialConfig serialConfig = {\n    SERIAL_DEFAULT_BITRATE, UART_WRDLEN, UART_STPBIT, UART_PARITY, UART_ATFLCT,\n};\n#else\nstatic SerialConfig serialConfig = {\n    SERIAL_DEFAULT_BITRATE,\n    UART_CR1,\n    UART_CR2,\n    UART_CR3,\n};\n#endif\n\nvoid uart_init(uint32_t baud) {\n    static bool is_initialised = false;\n\n    if (is_initialised) {\n        return;\n    }\n    is_initialised = true;\n\n#if defined(MCU_KINETIS)\n    serialConfig.sc_speed = baud;\n#else\n    serialConfig.speed = baud;\n#endif\n\n#if defined(USE_GPIOV1)\n    palSetLineMode(UART_TX_PIN, UART_TX_PAL_MODE);\n    palSetLineMode(UART_RX_PIN, UART_RX_PAL_MODE);\n#else\n    palSetLineMode(UART_TX_PIN, PAL_MODE_ALTERNATE(UART_TX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);\n    palSetLineMode(UART_RX_PIN, PAL_MODE_ALTERNATE(UART_RX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);\n#endif\n\n    sdStart(&UART_DRIVER, &serialConfig);\n}\n\nvoid uart_write(uint8_t data) {\n    sdPut(&UART_DRIVER, data);\n}\n\nuint8_t uart_read(void) {\n    return (uint8_t)sdGet(&UART_DRIVER);\n}\n\nvoid uart_transmit(const uint8_t *data, uint16_t length) {\n    sdWrite(&UART_DRIVER, data, length);\n}\n\nvoid uart_receive(uint8_t *data, uint16_t length) {\n    sdRead(&UART_DRIVER, data, length);\n}\n\nbool uart_available(void) {\n    return !sdGetWouldBlock(&UART_DRIVER);\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/uart_sio.c",
    "content": "// Copyright 2024 Stefan Kerkmann (@KarlK90)\n// Copyright 2021 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"uart.h\"\n#include \"gpio.h\"\n#include \"chibios_config.h\"\n#include <hal.h>\n\n#ifndef UART_DRIVER\n#    define UART_DRIVER SIOD1\n#endif\n\n#ifndef UART_TX_PIN\n#    define UART_TX_PIN A9\n#endif\n\n#ifndef UART_TX_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define UART_TX_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    else\n#        define UART_TX_PAL_MODE 7\n#    endif\n#endif\n\n#ifndef UART_RX_PIN\n#    define UART_RX_PIN A10\n#endif\n\n#ifndef UART_RX_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define UART_RX_PAL_MODE PAL_MODE_INPUT\n#    else\n#        define UART_RX_PAL_MODE 7\n#    endif\n#endif\n\n#ifndef UART_CTS_PIN\n#    define UART_CTS_PIN A11\n#endif\n\n#ifndef UART_CTS_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define UART_CTS_PAL_MODE PAL_MODE_INPUT\n#    else\n#        define UART_CTS_PAL_MODE 7\n#    endif\n#endif\n\n#ifndef UART_RTS_PIN\n#    define UART_RTS_PIN A12\n#endif\n\n#ifndef UART_RTS_PAL_MODE\n#    ifdef USE_GPIOV1\n#        define UART_RTS_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    else\n#        define UART_RTS_PAL_MODE 7\n#    endif\n#endif\n\n#ifndef UART_CR1\n#    define UART_CR1 0\n#endif\n\n#ifndef UART_CR2\n#    define UART_CR2 0\n#endif\n\n#ifndef UART_CR3\n#    define UART_CR3 0\n#endif\n\n#if defined(MCU_RP)\n// 38400 baud, 8 data bits, 1 stop bit, no parity, no flow control\nstatic SIOConfig sioConfig = {\n    .baud      = SIO_DEFAULT_BITRATE,\n    .UARTLCR_H = (UART_UARTLCR_H_WLEN_8BITS | UART_UARTLCR_H_FEN),\n    .UARTCR    = 0U,\n    .UARTIFLS  = (UART_UARTIFLS_RXIFLSEL_1_8F | UART_UARTIFLS_TXIFLSEL_1_8E),\n    .UARTDMACR = 0U,\n};\n#else\nstatic SIOConfig sioConfig = {\n    .baud  = SIO_DEFAULT_BITRATE,\n#    if defined(MCU_STM32) && defined(USE_USARTV3)\n    .presc = USART_PRESC1,\n#    endif\n    .cr1   = UART_CR1,\n    .cr2   = UART_CR2,\n    .cr3   = UART_CR3,\n};\n#endif\n\nvoid uart_init(uint32_t baud) {\n    static bool is_initialised = false;\n\n    if (is_initialised) {\n        return;\n    }\n    is_initialised = true;\n\n    sioConfig.baud = baud;\n\n#if defined(USE_GPIOV1)\n    palSetLineMode(UART_TX_PIN, UART_TX_PAL_MODE);\n    palSetLineMode(UART_RX_PIN, UART_RX_PAL_MODE);\n#else\n    palSetLineMode(UART_TX_PIN, PAL_MODE_ALTERNATE(UART_TX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);\n    palSetLineMode(UART_RX_PIN, PAL_MODE_ALTERNATE(UART_RX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST);\n#endif\n\n    sioStart(&UART_DRIVER, &sioConfig);\n}\n\nvoid uart_write(uint8_t data) {\n    chnPutTimeout(&UART_DRIVER, data, TIME_INFINITE);\n}\n\nuint8_t uart_read(void) {\n    msg_t result = chnGetTimeout(&UART_DRIVER, TIME_INFINITE);\n\n    if (sioHasRXErrorsX(&UART_DRIVER)) {\n        sioGetAndClearErrors(&UART_DRIVER);\n    }\n\n    return (uint8_t)result;\n}\n\nvoid uart_transmit(const uint8_t *data, uint16_t length) {\n    chnWrite(&UART_DRIVER, data, length);\n}\n\nvoid uart_receive(uint8_t *data, uint16_t length) {\n    chnRead(&UART_DRIVER, data, length);\n\n    if (sioHasRXErrorsX(&UART_DRIVER)) {\n        sioGetAndClearErrors(&UART_DRIVER);\n    }\n}\n\nbool uart_available() {\n    return !sioIsRXEmptyX(&UART_DRIVER);\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/usbpd_stm32g4.c",
    "content": "/* Copyright 2021 Nick Brassel (@tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <quantum.h>\n\n#ifndef USBPD_UCPD1_CFG1\n#    define USBPD_UCPD1_CFG1 (UCPD_CFG1_PSC_UCPDCLK_0 | UCPD_CFG1_TRANSWIN_3 | UCPD_CFG1_IFRGAP_4 | UCPD_CFG1_HBITCLKDIV_4)\n#endif // USBPD_UCPD1_CFG1\n\n// Initialises the USBPD subsystem\n__attribute__((weak)) void usbpd_init(void) {\n    // Enable the clock for the UCPD1 peripheral\n    RCC->APB1ENR2 |= RCC_APB1ENR2_UCPD1EN;\n\n    // Copy the existing value\n    uint32_t CFG1 = UCPD1->CFG1;\n    // Force-disable UCPD1 before configuring\n    CFG1 &= ~UCPD_CFG1_UCPDEN;\n    // Configure UCPD1\n    CFG1 = USBPD_UCPD1_CFG1;\n    // Apply the changes\n    UCPD1->CFG1 = CFG1;\n    // Enable UCPD1\n    UCPD1->CFG1 |= UCPD_CFG1_UCPDEN;\n\n    // Copy the existing value\n    uint32_t CR = UCPD1->CR;\n    // Clear out ANASUBMODE (irrelevant as a sink device)\n    CR &= ~UCPD_CR_ANASUBMODE_Msk;\n    // Advertise our capabilities as a sink, with both CC lines enabled\n    CR |= UCPD_CR_ANAMODE | UCPD_CR_CCENABLE_Msk;\n    // Apply the changes\n    UCPD1->CR = CR;\n\n    // Disable dead-battery signals only after UCPD1 is configured to ensure\n    // that the transition does not go through any intermediate state without\n    // any pull-down resistance.\n    PWR->CR3 |= PWR_CR3_UCPD_DBDIS;\n}\n\n// Gets the current state of the USBPD allowance\n__attribute__((weak)) usbpd_allowance_t usbpd_get_allowance(void) {\n    uint32_t CR = UCPD1->CR;\n\n    int ucpd_enabled = (UCPD1->CFG1 & UCPD_CFG1_UCPDEN_Msk) >> UCPD_CFG1_UCPDEN_Pos;\n    int anamode      = (CR & UCPD_CR_ANAMODE_Msk) >> UCPD_CR_ANAMODE_Pos;\n    int cc_enabled   = (CR & UCPD_CR_CCENABLE_Msk) >> UCPD_CR_CCENABLE_Pos;\n\n    if (ucpd_enabled && anamode && cc_enabled) {\n        uint32_t SR         = UCPD1->SR;\n        int      vstate_cc1 = (SR & UCPD_SR_TYPEC_VSTATE_CC1_Msk) >> UCPD_SR_TYPEC_VSTATE_CC1_Pos;\n        int      vstate_cc2 = (SR & UCPD_SR_TYPEC_VSTATE_CC2_Msk) >> UCPD_SR_TYPEC_VSTATE_CC2_Pos;\n        int      vstate_max = vstate_cc1 > vstate_cc2 ? vstate_cc1 : vstate_cc2;\n        switch (vstate_max) {\n            case 0:\n            case 1:\n                return USBPD_500MA; // Note that this is 500mA (i.e. max USB 2.0), not 900mA, as we're not using USB 3.1 as a sink device.\n            case 2:\n                return USBPD_1500MA;\n            case 3:\n                return USBPD_3000MA;\n        }\n    }\n\n    return USBPD_500MA;\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c",
    "content": "// Copyright 2022 Marek Kraus (@gamelaster)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"gpio.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/clocks.h\"\n#include \"ps2.h\"\n#include \"debug.h\"\n\n#if !defined(MCU_RP)\n#    error PIO Driver is only available for Raspberry Pi 2040 MCUs!\n#endif\n\n#if defined(PS2_ENABLE)\n#    if defined(PS2_MOUSE_ENABLE)\n#        if !defined(PS2_MOUSE_USE_REMOTE_MODE)\n#            define BUFFERED_MODE_ENABLE\n#        endif\n#    else // PS2 Keyboard\n#        define BUFFERED_MODE_ENABLE\n#    endif\n#endif\n\n#if PS2_DATA_PIN + 1 != PS2_CLOCK_PIN\n#    error PS/2 clock pin must be data pin + 1!\n#endif\n\nstatic inline void pio_serve_interrupt(void);\n\n#if defined(PS2_PIO_USE_PIO1)\nstatic const PIO pio = pio1;\n\nOSAL_IRQ_HANDLER(RP_PIO1_IRQ_0_HANDLER) {\n    OSAL_IRQ_PROLOGUE();\n    pio_serve_interrupt();\n    OSAL_IRQ_EPILOGUE();\n}\n#else\nstatic const PIO pio = pio0;\n\nOSAL_IRQ_HANDLER(RP_PIO0_IRQ_0_HANDLER) {\n    OSAL_IRQ_PROLOGUE();\n    pio_serve_interrupt();\n    OSAL_IRQ_EPILOGUE();\n}\n#endif\n\n#define PS2_WRAP_TARGET 0\n#define PS2_WRAP 20\n\n// clang-format off\nstatic const uint16_t ps2_program_instructions[] = {\n            //     .wrap_target\n    0x00c7, //  0: jmp    pin, 7\n    0xe02a, //  1: set    x, 10\n    0x2021, //  2: wait   0 pin, 1\n    0x4001, //  3: in     pins, 1\n    0x20a1, //  4: wait   1 pin, 1\n    0x0042, //  5: jmp    x--, 2\n    0x0000, //  6: jmp    0\n    0x00e9, //  7: jmp    !osre, 9\n    0x0000, //  8: jmp    0\n    0xff81, //  9: set    pindirs, 1             [31]\n    0xe280, // 10: set    pindirs, 0             [2]\n    0xe082, // 11: set    pindirs, 2\n    0x2021, // 12: wait   0 pin, 1\n    0xe029, // 13: set    x, 9\n    0x6081, // 14: out    pindirs, 1\n    0x20a1, // 15: wait   1 pin, 1\n    0x2021, // 16: wait   0 pin, 1\n    0x004e, // 17: jmp    x--, 14\n    0xe083, // 18: set    pindirs, 3\n    0x2021, // 19: wait   0 pin, 1\n    0x20a1, // 20: wait   1 pin, 1\n            //     .wrap\n};\n// clang-format on\n\nstatic const struct pio_program ps2_program = {\n    .instructions = ps2_program_instructions,\n    .length       = 21,\n    .origin       = -1,\n};\n\nstatic int                state_machine = -1;\nstatic thread_reference_t tx_thread     = NULL;\n\n#define BUFFER_SIZE 32\nstatic input_buffers_queue_t               pio_rx_queue;\nstatic __attribute__((aligned(4))) uint8_t pio_rx_buffer[BQ_BUFFER_SIZE(BUFFER_SIZE, sizeof(uint32_t))];\n\nuint8_t ps2_error = PS2_ERR_NONE;\n\nvoid pio_serve_interrupt(void) {\n    uint32_t irqs = pio->ints0;\n\n    if (irqs & (PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS << state_machine)) {\n        osalSysLockFromISR();\n        uint32_t* frame_buffer = (uint32_t*)ibqGetEmptyBufferI(&pio_rx_queue);\n        if (frame_buffer == NULL) {\n            osalSysUnlockFromISR();\n            return;\n        }\n        *frame_buffer = pio_sm_get(pio, state_machine);\n        ibqPostFullBufferI(&pio_rx_queue, sizeof(uint32_t));\n        osalSysUnlockFromISR();\n    }\n\n    if (irqs & (PIO_IRQ0_INTF_SM0_TXNFULL_BITS << state_machine)) {\n        pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + state_machine, false);\n        osalSysLockFromISR();\n        osalThreadResumeI(&tx_thread, MSG_OK);\n        osalSysUnlockFromISR();\n    }\n}\n\nvoid ps2_host_init(void) {\n    ibqObjectInit(&pio_rx_queue, false, pio_rx_buffer, sizeof(uint32_t), BUFFER_SIZE, NULL, NULL);\n    uint pio_idx = pio_get_index(pio);\n\n    hal_lld_peripheral_unreset(pio_idx == 0 ? RESETS_ALLREG_PIO0 : RESETS_ALLREG_PIO1);\n\n    state_machine = pio_claim_unused_sm(pio, true);\n    if (state_machine < 0) {\n        dprintln(\"ERROR: Failed to acquire state machine for PS/2!\");\n        ps2_error = PS2_ERR_NODATA;\n        return;\n    }\n\n    uint offset = pio_add_program(pio, &ps2_program);\n\n    pio_sm_config c = pio_get_default_sm_config();\n    sm_config_set_wrap(&c, offset + PS2_WRAP_TARGET, offset + PS2_WRAP);\n\n    // Set pindirs to input (output enable is inverted below)\n    pio_sm_set_consecutive_pindirs(pio, state_machine, PS2_DATA_PIN, 2, true);\n    sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / (200.0f * KHZ));\n    sm_config_set_set_pins(&c, PS2_DATA_PIN, 2);\n    sm_config_set_out_pins(&c, PS2_DATA_PIN, 1);\n    sm_config_set_out_shift(&c, true, true, 10);\n    sm_config_set_in_shift(&c, true, true, 11);\n    sm_config_set_jmp_pin(&c, PS2_CLOCK_PIN);\n    sm_config_set_in_pins(&c, PS2_DATA_PIN);\n\n    // clang-format off\n    iomode_t pin_mode = PAL_RP_PAD_IE |\n                        PAL_RP_GPIO_OE |\n                        PAL_RP_PAD_SLEWFAST |\n                        PAL_RP_PAD_DRIVE12 |\n                        // Invert output enable so that pindirs=1 means input\n                        // and indirs=0 means output. This way, out pindirs\n                        // works correctly with the open-drain PS/2 interface.\n                        // Setting pindirs=1 effectively pulls the line high,\n                        // due to the pull-up resistor, while pindirs=0 pulls\n                        // the line low.\n                        PAL_RP_IOCTRL_OEOVER_DRVINVPERI |\n                        (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);\n    // clang-format on\n\n    palSetLineMode(PS2_DATA_PIN, pin_mode);\n    palSetLineMode(PS2_CLOCK_PIN, pin_mode);\n\n    pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + state_machine, true);\n    pio_sm_init(pio, state_machine, offset, &c);\n\n#if defined(PS2_PIO_USE_PIO1)\n    nvicEnableVector(RP_PIO1_IRQ_0_NUMBER, CORTEX_MAX_KERNEL_PRIORITY);\n#else\n    nvicEnableVector(RP_PIO0_IRQ_0_NUMBER, CORTEX_MAX_KERNEL_PRIORITY);\n#endif\n\n    pio_sm_set_enabled(pio, state_machine, true);\n}\n\nstatic int bit_parity(int x) {\n    return !__builtin_parity(x);\n}\n\nuint8_t ps2_host_send(uint8_t data) {\n    uint32_t frame = 0b1000000000;\n    frame          = frame | data;\n\n    if (bit_parity(data)) {\n        frame = frame | (1 << 8);\n    }\n\n    pio_sm_put(pio, state_machine, frame);\n\n    msg_t msg = MSG_OK;\n    osalSysLock();\n    while (pio_sm_is_tx_fifo_full(pio, state_machine)) {\n        pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + state_machine, true);\n        msg = osalThreadSuspendTimeoutS(&tx_thread, TIME_MS2I(100));\n        if (msg < MSG_OK) {\n            pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + state_machine, false);\n            ps2_error = PS2_ERR_NODATA;\n            osalSysUnlock();\n            return 0;\n        }\n    }\n    osalSysUnlock();\n\n    return ps2_host_recv_response();\n}\n\nstatic uint8_t ps2_get_data_from_frame(uint32_t frame) {\n    uint8_t  data       = (frame >> 22) & 0xFF;\n    uint32_t start_bit  = (frame & 0b00000000001000000000000000000000) ? 1 : 0;\n    uint32_t parity_bit = (frame & 0b01000000000000000000000000000000) ? 1 : 0;\n    uint32_t stop_bit   = (frame & 0b10000000001000000000000000000000) ? 1 : 0;\n\n    if (start_bit != 0) {\n        ps2_error = PS2_ERR_STARTBIT1;\n        return 0;\n    }\n\n    if (parity_bit != bit_parity(data)) {\n        ps2_error = PS2_ERR_PARITY;\n        return 0;\n    }\n\n    if (stop_bit != 1) {\n        ps2_error = PS2_ERR_STARTBIT2;\n        return 0;\n    }\n\n    return data;\n}\n\nuint8_t ps2_host_recv_response(void) {\n    uint32_t frame = 0;\n    msg_t    msg   = MSG_OK;\n\n    msg = ibqReadTimeout(&pio_rx_queue, (uint8_t*)&frame, sizeof(uint32_t), TIME_MS2I(100));\n    if (msg < MSG_OK) {\n        ps2_error = PS2_ERR_NODATA;\n        return 0;\n    }\n\n    return ps2_get_data_from_frame(frame);\n}\n\n#ifdef BUFFERED_MODE_ENABLE\n\nbool pbuf_has_data(void) {\n    osalSysLock();\n    bool has_data = !ibqIsEmptyI(&pio_rx_queue);\n    osalSysUnlock();\n    return has_data;\n}\n\nuint8_t ps2_host_recv(void) {\n    uint32_t frame = 0;\n    msg_t    msg   = MSG_OK;\n\n    uint8_t has_data = pbuf_has_data();\n    if (has_data) {\n        msg = ibqReadTimeout(&pio_rx_queue, (uint8_t*)&frame, sizeof(uint32_t), TIME_MS2I(100));\n        if (msg < MSG_OK) {\n            ps2_error = PS2_ERR_NODATA;\n            return 0;\n        }\n    } else {\n        ps2_error = PS2_ERR_NODATA;\n    }\n\n    return frame != 0 ? ps2_get_data_from_frame(frame) : 0;\n}\n\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"serial_usart.h\"\n#include \"serial_protocol.h\"\n#include \"hardware/pio.h\"\n#include \"hardware/clocks.h\"\n#include \"wait.h\"\n#include \"debug.h\"\n\n#if !defined(MCU_RP)\n#    error PIO Driver is only available for Raspberry Pi 2040 MCUs!\n#endif\n\nstatic inline bool receive_impl(uint8_t* destination, const size_t size, sysinterval_t timeout);\nstatic inline bool send_impl(const uint8_t* source, const size_t size);\nstatic inline void pio_serve_interrupt(void);\n\n#define MSG_PIO_ERROR ((msg_t)(-3))\n\n#if defined(SERIAL_PIO_USE_PIO1)\nstatic const PIO pio = pio1;\n\nOSAL_IRQ_HANDLER(RP_PIO1_IRQ_0_HANDLER) {\n    OSAL_IRQ_PROLOGUE();\n    pio_serve_interrupt();\n    OSAL_IRQ_EPILOGUE();\n}\n#else\nstatic const PIO pio = pio0;\n\nOSAL_IRQ_HANDLER(RP_PIO0_IRQ_0_HANDLER) {\n    OSAL_IRQ_PROLOGUE();\n    pio_serve_interrupt();\n    OSAL_IRQ_EPILOGUE();\n}\n#endif\n\n#define UART_TX_WRAP_TARGET 0\n#define UART_TX_WRAP 3\n\n// clang-format off\n#if defined(SERIAL_USART_FULL_DUPLEX)\nstatic const uint16_t uart_tx_program_instructions[] = {\n            //     .wrap_target\n    0x9fa0, //  0: pull   block           side 1 [7]\n    0xf727, //  1: set    x, 7            side 0 [7]\n    0x6001, //  2: out    pins, 1\n    0x0642, //  3: jmp    x--, 2                 [6]\n            //     .wrap\n};\n#else\nstatic const uint16_t uart_tx_program_instructions[] = {\n            //     .wrap_target\n    0x9fa0, //  0: pull   block           side 1 [7]\n    0xf727, //  1: set    x, 7            side 0 [7]\n    0x6081, //  2: out    pindirs, 1\n    0x0642, //  3: jmp    x--, 2                 [6]\n            //     .wrap\n};\n#endif\n// clang-format on\n\nstatic const pio_program_t uart_tx_program = {\n    .instructions = uart_tx_program_instructions,\n    .length       = 4,\n    .origin       = -1,\n};\n\n#define UART_RX_WRAP_TARGET 0\n#define UART_RX_WRAP 8\n\n// clang-format off\nstatic const uint16_t uart_rx_program_instructions[] = {\n            //     .wrap_target\n    0x2020, //  0: wait   0 pin, 0\n    0xea27, //  1: set    x, 7                   [10]\n    0x4001, //  2: in     pins, 1\n    0x0642, //  3: jmp    x--, 2                 [6]\n    0x00c8, //  4: jmp    pin, 8\n    0xc020, //  5: irq    wait 0\n    0x20a0, //  6: wait   1 pin, 0\n    0x0000, //  7: jmp    0\n    0x8020, //  8: push   block\n            //     .wrap\n};\n// clang-format on\n\nstatic const pio_program_t uart_rx_program = {\n    .instructions = uart_rx_program_instructions,\n    .length       = 9,\n    .origin       = -1,\n};\n\nthread_reference_t rx_thread        = NULL;\nstatic int         rx_state_machine = -1;\n\nthread_reference_t tx_thread        = NULL;\nstatic int         tx_state_machine = -1;\n\nvoid pio_serve_interrupt(void) {\n    uint32_t irqs = pio->ints0;\n\n    // The RX FIFO is not empty any more, therefore wake any sleeping rx thread\n    if (irqs & (PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS << rx_state_machine)) {\n        // Disable rx not empty interrupt\n        pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, false);\n\n        osalSysLockFromISR();\n        osalThreadResumeI(&rx_thread, MSG_OK);\n        osalSysUnlockFromISR();\n    }\n\n    // The TX FIFO is not full any more, therefore wake any sleeping tx thread\n    if (irqs & (PIO_IRQ0_INTF_SM0_TXNFULL_BITS << tx_state_machine)) {\n        // Disable tx not full interrupt\n        pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, false);\n        osalSysLockFromISR();\n        osalThreadResumeI(&tx_thread, MSG_OK);\n        osalSysUnlockFromISR();\n    }\n\n    // IRQ 0 is set on framing or break errors by the rx state machine\n    if (pio_interrupt_get(pio, 0UL)) {\n        pio_interrupt_clear(pio, 0UL);\n\n        osalSysLockFromISR();\n        osalThreadResumeI(&rx_thread, MSG_PIO_ERROR);\n        osalSysUnlockFromISR();\n    }\n}\n\n#if !defined(SERIAL_USART_FULL_DUPLEX)\n// The internal pull-ups of the RP2040 are rather weakish with a range of 50k to\n// 80k, which in turn do not provide enough current to guarantee fast signal rise\n// times with a parasitic capacitance of greater than 100pf. In real world\n// applications, like split keyboards which might have vias in the signal path\n// or long PCB traces, this prevents a successful communication. The solution\n// is to temporarily augment the weak pull ups from the receiving side by\n// driving the tx pin high. On the receiving side the lowest possible drive\n// strength is chosen because the transmitting side must still be able to drive\n// the signal low. With this configuration the rise times are fast enough and\n// the generated low level with 360mV will generate a logical zero.\nstatic void __no_inline_not_in_flash_func(enter_rx_state)(void) {\n    osalSysLock();\n    // Wait for the transmitting state machines FIFO to run empty. At this point\n    // the last byte has been pulled from the transmitting state machines FIFO\n    // into the output shift register. We have to wait a tiny bit more until\n    // this byte is transmitted, before we can turn on the receiving state\n    // machine again.\n    while (!pio_sm_is_tx_fifo_empty(pio, tx_state_machine)) {\n    }\n    // Wait for ~11 bits, 1 start bit + 8 data bits + 1 stop bit + 1 bit\n    // headroom.\n    wait_us(1000000U * 11U / SERIAL_USART_SPEED);\n    // Disable tx state machine to not interfere with our tx pin manipulation\n    pio_sm_set_enabled(pio, tx_state_machine, false);\n    gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_2MA);\n    pio_sm_set_pins_with_mask(pio, tx_state_machine, 1U << SERIAL_USART_TX_PIN, 1U << SERIAL_USART_TX_PIN);\n    pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, false);\n    pio_sm_set_enabled(pio, rx_state_machine, true);\n    osalSysUnlock();\n}\n\nstatic void __no_inline_not_in_flash_func(leave_rx_state)(void) {\n    osalSysLock();\n    // In Half-duplex operation the tx pin dual-functions as sender and\n    // receiver. To not receive the data we will send, we disable the receiving\n    // state machine.\n    pio_sm_set_enabled(pio, rx_state_machine, false);\n    pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, true);\n    pio_sm_set_pins_with_mask(pio, tx_state_machine, 0U, 1U << SERIAL_USART_TX_PIN);\n    gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_12MA);\n    pio_sm_restart(pio, tx_state_machine);\n    pio_sm_set_enabled(pio, tx_state_machine, true);\n    osalSysUnlock();\n}\n#else\n// All this trickery is gladly not necessary for full-duplex.\nstatic inline void enter_rx_state(void) {}\nstatic inline void leave_rx_state(void) {}\n#endif\n\n/**\n * @brief Clear the FIFO of the RX state machine.\n */\ninline void serial_transport_driver_clear(void) {\n    osalSysLock();\n    while (!pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) {\n        pio_sm_clear_fifos(pio, rx_state_machine);\n    }\n    osalSysUnlock();\n}\n\nstatic inline msg_t sync_tx(sysinterval_t timeout) {\n    msg_t msg = MSG_OK;\n    osalSysLock();\n    while (pio_sm_is_tx_fifo_full(pio, tx_state_machine)) {\n        pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, true);\n        msg = osalThreadSuspendTimeoutS(&tx_thread, timeout);\n        if (msg < MSG_OK) {\n            pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, false);\n            break;\n        }\n    }\n    osalSysUnlock();\n    return msg;\n}\n\nstatic inline bool send_impl(const uint8_t* source, const size_t size) {\n    size_t send = 0;\n    msg_t  msg;\n    while (send < size) {\n        msg = sync_tx(TIME_MS2I(SERIAL_USART_TIMEOUT));\n        if (msg < MSG_OK) {\n            return false;\n        }\n\n        osalSysLock();\n        while (send < size) {\n            if (pio_sm_is_tx_fifo_full(pio, tx_state_machine)) {\n                break;\n            }\n            if (send >= size) {\n                break;\n            }\n            pio_sm_put(pio, tx_state_machine, (uint32_t)(*source));\n            source++;\n            send++;\n        }\n        osalSysUnlock();\n    }\n\n    return send == size;\n}\n\n/**\n * @brief Blocking send of buffer with timeout.\n *\n * @return true Send success.\n * @return false Send failed.\n */\ninline bool serial_transport_send(const uint8_t* source, const size_t size) {\n    leave_rx_state();\n    bool result = send_impl(source, size);\n    enter_rx_state();\n\n    return result;\n}\n\nstatic inline msg_t sync_rx(sysinterval_t timeout) {\n    msg_t msg = MSG_OK;\n    osalSysLock();\n    while (pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) {\n        pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, true);\n        msg = osalThreadSuspendTimeoutS(&rx_thread, timeout);\n        if (msg < MSG_OK) {\n            pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, false);\n            break;\n        }\n    }\n    osalSysUnlock();\n    return msg;\n}\n\nstatic inline bool receive_impl(uint8_t* destination, const size_t size, sysinterval_t timeout) {\n    size_t read = 0U;\n\n    while (read < size) {\n        msg_t msg = sync_rx(timeout);\n        if (msg < MSG_OK) {\n            return false;\n        }\n        osalSysLock();\n        while (true) {\n            if (pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) {\n                break;\n            }\n            if (read >= size) {\n                break;\n            }\n            *destination++ = *((uint8_t*)&pio->rxf[rx_state_machine] + 3U);\n            read++;\n        }\n        osalSysUnlock();\n    }\n\n    return read == size;\n}\n\n/**\n * @brief  Blocking receive of size * bytes with timeout.\n *\n * @return true Receive success.\n * @return false Receive failed, e.g. by timeout.\n */\ninline bool serial_transport_receive(uint8_t* destination, const size_t size) {\n    return receive_impl(destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT));\n}\n\n/**\n * @brief  Blocking receive of size * bytes.\n *\n * @return true Receive success.\n * @return false Receive failed.\n */\ninline bool serial_transport_receive_blocking(uint8_t* destination, const size_t size) {\n    return receive_impl(destination, size, TIME_INFINITE);\n}\n\nstatic inline void pio_tx_init(pin_t tx_pin) {\n    uint pio_idx = pio_get_index(pio);\n    uint offset  = pio_add_program(pio, &uart_tx_program);\n\n#if defined(SERIAL_USART_FULL_DUPLEX)\n    // clang-format off\n    iomode_t tx_pin_mode = PAL_RP_GPIO_OE |\n                           PAL_RP_PAD_SLEWFAST |\n                           PAL_RP_PAD_DRIVE4 |\n                           (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);\n    // clang-format on\n    pio_sm_set_pins_with_mask(pio, tx_state_machine, 1U << tx_pin, 1U << tx_pin);\n    pio_sm_set_consecutive_pindirs(pio, tx_state_machine, tx_pin, 1U, true);\n#else\n    // clang-format off\n    iomode_t tx_pin_mode = PAL_RP_PAD_IE |\n                           PAL_RP_GPIO_OE |\n                           PAL_RP_PAD_SCHMITT |\n                           PAL_RP_PAD_PUE |\n                           PAL_RP_PAD_SLEWFAST |\n                           PAL_RP_PAD_DRIVE12 |\n                           PAL_RP_IOCTRL_OEOVER_DRVINVPERI |\n                           (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);\n    // clang-format on\n    pio_sm_set_pins_with_mask(pio, tx_state_machine, 0U << tx_pin, 1U << tx_pin);\n    pio_sm_set_consecutive_pindirs(pio, tx_state_machine, tx_pin, 1U, true);\n#endif\n\n    palSetLineMode(tx_pin, tx_pin_mode);\n\n    pio_sm_config config = pio_get_default_sm_config();\n    sm_config_set_wrap(&config, offset + UART_TX_WRAP_TARGET, offset + UART_TX_WRAP);\n#if defined(SERIAL_USART_FULL_DUPLEX)\n    sm_config_set_sideset(&config, 2, true, false);\n#else\n    sm_config_set_sideset(&config, 2, true, true);\n#endif\n    // OUT shifts to right, no autopull\n    sm_config_set_out_shift(&config, true, false, 32);\n    // We are mapping both OUT and side-set to the same pin, because sometimes\n    // we need to assert user data onto the pin (with OUT) and sometimes\n    // assert constant values (start/stop bit)\n    sm_config_set_out_pins(&config, tx_pin, 1);\n    sm_config_set_sideset_pins(&config, tx_pin);\n    // We only need TX, so get an 8-deep FIFO!\n    sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX);\n    // SM transmits 1 bit per 8 execution cycles.\n    float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_USART_SPEED);\n    sm_config_set_clkdiv(&config, div);\n    pio_sm_init(pio, tx_state_machine, offset, &config);\n    pio_sm_set_enabled(pio, tx_state_machine, true);\n}\n\nstatic inline void pio_rx_init(pin_t rx_pin) {\n    uint offset = pio_add_program(pio, &uart_rx_program);\n\n#if defined(SERIAL_USART_FULL_DUPLEX)\n    uint pio_idx = pio_get_index(pio);\n    pio_sm_set_consecutive_pindirs(pio, rx_state_machine, rx_pin, 1, false);\n    // clang-format off\n    iomode_t rx_pin_mode = PAL_RP_PAD_IE |\n                           PAL_RP_PAD_SCHMITT |\n                           PAL_RP_PAD_PUE |\n                           (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);\n    // clang-format on\n    palSetLineMode(rx_pin, rx_pin_mode);\n#endif\n\n    pio_sm_config config = pio_get_default_sm_config();\n    sm_config_set_wrap(&config, offset + UART_RX_WRAP_TARGET, offset + UART_RX_WRAP);\n    sm_config_set_in_pins(&config, rx_pin); // for WAIT, IN\n    sm_config_set_jmp_pin(&config, rx_pin); // for JMP\n    // Shift to right, autopush disabled\n    sm_config_set_in_shift(&config, true, false, 32);\n    // Deeper FIFO as we're not doing any TX\n    sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_RX);\n    // SM transmits 1 bit per 8 execution cycles.\n    float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_USART_SPEED);\n    sm_config_set_clkdiv(&config, div);\n    pio_sm_init(pio, rx_state_machine, offset, &config);\n    pio_sm_set_enabled(pio, rx_state_machine, true);\n}\n\nstatic inline void pio_init(pin_t tx_pin, pin_t rx_pin) {\n    uint pio_idx = pio_get_index(pio);\n\n    /* Get PIOx peripheral out of reset state. */\n    hal_lld_peripheral_unreset(pio_idx == 0 ? RESETS_ALLREG_PIO0 : RESETS_ALLREG_PIO1);\n\n    tx_state_machine = pio_claim_unused_sm(pio, true);\n    if (tx_state_machine < 0) {\n        dprintln(\"ERROR: Failed to acquire state machine for serial transmission!\");\n        return;\n    }\n    pio_tx_init(tx_pin);\n\n    rx_state_machine = pio_claim_unused_sm(pio, true);\n    if (rx_state_machine < 0) {\n        dprintln(\"ERROR: Failed to acquire state machine for serial reception!\");\n        return;\n    }\n    pio_rx_init(rx_pin);\n\n    // Enable error flag IRQ source for rx state machine\n    pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, true);\n    pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, true);\n    pio_set_irq0_source_enabled(pio, pis_interrupt0, true);\n\n    // Enable PIO specific interrupt vector, as the pio implementation is timing\n    // critical we use the highest possible priority.\n#if defined(SERIAL_PIO_USE_PIO1)\n    nvicEnableVector(RP_PIO1_IRQ_0_NUMBER, CORTEX_MAX_KERNEL_PRIORITY);\n#else\n    nvicEnableVector(RP_PIO0_IRQ_0_NUMBER, CORTEX_MAX_KERNEL_PRIORITY);\n#endif\n\n    enter_rx_state();\n}\n\n/**\n * @brief PIO driver specific initialization function for the master side.\n */\nvoid serial_transport_driver_master_init(void) {\n#if defined(SERIAL_USART_FULL_DUPLEX)\n    pin_t tx_pin = SERIAL_USART_TX_PIN;\n    pin_t rx_pin = SERIAL_USART_RX_PIN;\n#else\n    pin_t tx_pin = SERIAL_USART_TX_PIN;\n    pin_t rx_pin = SERIAL_USART_TX_PIN;\n#endif\n\n#if defined(SERIAL_USART_PIN_SWAP)\n    pio_init(rx_pin, tx_pin);\n#else\n    pio_init(tx_pin, rx_pin);\n#endif\n}\n\n/**\n * @brief PIO driver specific initialization function for the slave side.\n */\nvoid serial_transport_driver_slave_init(void) {\n#if defined(SERIAL_USART_FULL_DUPLEX)\n    pin_t tx_pin = SERIAL_USART_TX_PIN;\n    pin_t rx_pin = SERIAL_USART_RX_PIN;\n#else\n    pin_t tx_pin = SERIAL_USART_TX_PIN;\n    pin_t rx_pin = SERIAL_USART_TX_PIN;\n#endif\n\n    pio_init(tx_pin, rx_pin);\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c",
    "content": "// Copyright 2022 Stefan Kerkmann (@KarlK90)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"ws2812.h\"\n\n// Keep this exact include order otherwise we run into naming conflicts between\n// pico-sdk and rp2040.h which we don't control.\n#include \"hardware/timer.h\"\n#include \"hardware/clocks.h\"\n#include <hal.h>\n#include \"hardware/pio.h\"\n\n#include \"gpio.h\"\n#include \"debug.h\"\n#include \"wait.h\"\n#include \"util.h\"\n\n#if !defined(MCU_RP)\n#    error PIO Driver is only available for Raspberry Pi 2040 MCUs!\n#endif\n\n#if defined(WS2812_PIO_USE_PIO1)\nstatic const PIO pio = pio1;\n#else\nstatic const PIO pio = pio0;\n#endif\n\n#if !defined(RP_DMA_PRIORITY_WS2812)\n#    define RP_DMA_PRIORITY_WS2812 3\n#endif\n\n#if defined(WS2812_EXTERNAL_PULLUP)\n#    pragma message \"The GPIOs of the RP2040 are NOT 5V tolerant! Make sure to NOT apply any voltage over 3.3V to the RGB data pin.\"\n#endif\n\n/*================== WS2812 PIO TIMINGS =================*/\n\n// WS2812_T1L rounded to 50ns intervals and split into two wait timings\n#define PIO_T1L (WS2812_T1L / 50)\n#define PIO_T1L_A (MAX(CEILING(PIO_T1L, 2) - 1, 0))\n#define PIO_T1L_B (MAX(PIO_T1L / 2 - 1, 0))\n\n// WS2812_T0L rounded to 50ns intervals\n#define PIO_T0L (MAX(WS2812_T0L / 50 - PIO_T1L, 0))\n#define PIO_T0L_A (MAX(PIO_T0L - 1, 0))\n\n// WS2812_T0H rounded to 50ns intervals\n#define PIO_T0H (WS2812_T0H / 50)\n#define PIO_T0H_A MAX(PIO_T0H - 1, 0)\n\n// WS2812_T1H rounded to 50ns intervals and split into two wait timings\n#define PIO_T1H (MAX(WS2812_T1H / 50 - PIO_T0H, 0))\n#define PIO_T1H_A (MAX((CEILING(PIO_T1H, 2) - 1), 0))\n#define PIO_T1H_B (MAX((PIO_T1H / 2) - 1, 0))\n\n#if (WS2812_T0L % 50) != 0\n#    pragma message \"WS2812_T0L is not given in an 50ns interval, it will be rounded to the next 50ns\"\n#endif\n\n#if (WS2812_T0H % 50) != 0\n#    pragma message \"WS2812_T0H is not given in an 50ns interval, it will be rounded to the next 50ns\"\n#endif\n\n#if (WS2812_T1L % 50) != 0\n#    pragma message \"WS2812_T0L is not given in an 50ns interval, it will be rounded to the next 50ns\"\n#endif\n\n#if (WS2812_T1H % 50) != 0\n#    pragma message \"WS2812_T0H is not given in an 50ns interval, it will be rounded to the next 50ns\"\n#endif\n\n#if WS2812_T0L < WS2812_T1L\n#    error WS2812_T0L is shorter than WS2812_T1L, this is impossible to express in the RP2040 PIO driver. Please correct your timings.\n#endif\n\n#if WS2812_T1H < WS2812_T0H\n#    error WS2812_T1H is shorter than WS2812_T0H, this is impossible to express in the RP2040 PIO driver. Please correct your timings.\n#endif\n\n#if WS2812_T0L > (850 + WS2812_T1L)\n#    error WS2812_T0L is longer than 850ns + WS2812_T1L, this is impossible to express in the RP2040 PIO driver. Please correct your timings.\n#endif\n\n#if WS2812_T0H > 850\n#    error WS2812_T0H is longer than 850ns, this is impossible to express in the RP2040 PIO driver. Please correct your timings.\n#endif\n\n#if WS2812_T1H > (1700 + WS2812_T0H)\n#    error WS2812_T1H is longer than 1700ns + WS2812_T0H, this is impossible to express in the RP2040 PIO driver. Please correct your timings.\n#endif\n\n#if WS2812_T1L > 1700\n#    error WS2812_T1L is longer than 1700ns, this is impossible to express in the RP2040 PIO driver. Please correct your timings.\n#endif\n\n#if WS2812_T0L < (50 + WS2812_T1L)\n#    error WS2812_T0L is shorter than 50ns + WS2812_T1L, this is impossible to express in the RP2040 PIO driver. Please correct your timings.\n#endif\n\n#if WS2812_T0H < 50\n#    error WS2812_T0H is shorter than 50ns, this is impossible to express in the RP2040 PIO driver. Please correct your timings.\n#endif\n\n#if WS2812_T1H < (100 + WS2812_T0H)\n#    error WS2812_T1H is longer than 100ns + WS2812_T0H, this is impossible to express in the RP2040 PIO driver. Please correct your timings.\n#endif\n\n#if WS2812_T1L < 100\n#    error WS2812_T1L is longer than 1700ns, this is impossible to express in the RP2040 PIO driver. Please correct your timings.\n#endif\n\n/**\n * @brief Helper macro to binary patch the delay part of an per-compiled PIO\n * opcode.\n */\n#define PIO_DELAY(delay, opcode) (((delay & 0xF) << 8U) | opcode)\n\n#define WS2812_WRAP_TARGET 0\n#define WS2812_WRAP 5\n\nstatic const uint16_t ws2812_program_instructions[] = {\n    //     .wrap_target\n    PIO_DELAY(PIO_T1L_A, 0x6021), //  0: out    x, 1            side 0  // T1L (max. 1700ns)\n    PIO_DELAY(PIO_T1L_B, 0xa042), //  1: nop                    side 0  // T1L\n    PIO_DELAY(PIO_T0H_A, 0x1025), //  2: jmp    !x, 5           side 1  // T0H (max. 850ns)\n    PIO_DELAY(PIO_T1H_A, 0xb042), //  3: nop                    side 1  // T1H (max. 1700ns + T0H)\n    PIO_DELAY(PIO_T1H_B, 0x1000), //  4: jmp    0               side 1  // T1H\n    PIO_DELAY(PIO_T0L_A, 0xa042), //  5: nop                    side 0  // T0L (max. 850ns + T1L)\n    //     .wrap\n};\n\nstatic const pio_program_t ws2812_program = {\n    .instructions = ws2812_program_instructions,\n    .length       = ARRAY_SIZE(ws2812_program_instructions),\n    .origin       = -1,\n};\n\nstatic uint32_t                WS2812_BUFFER[WS2812_LED_COUNT];\nstatic const rp_dma_channel_t* dma_channel;\nstatic uint32_t                RP_DMA_MODE_WS2812;\nstatic int                     STATE_MACHINE = -1;\n\nstatic SEMAPHORE_DECL(TRANSFER_COUNTER, 1);\nstatic absolute_time_t LAST_TRANSFER;\n\n/**\n * @brief Convert RGBW value into WS2812 compatible 32-bit data word.\n */\n__always_inline static uint32_t rgbw8888_to_u32(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) {\n#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)\n    return ((uint32_t)green << 24) | ((uint32_t)red << 16) | ((uint32_t)blue << 8) | ((uint32_t)white);\n#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)\n    return ((uint32_t)red << 24) | ((uint32_t)green << 16) | ((uint32_t)blue << 8) | ((uint32_t)white);\n#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)\n    return ((uint32_t)blue << 24) | ((uint32_t)green << 16) | ((uint32_t)red << 8) | ((uint32_t)white);\n#endif\n}\n\nstatic void ws2812_dma_callback(void* p, uint32_t ct) {\n    // We assume that there is at least one frame left in the OSR even if the TX\n    // FIFO is already empty.\n    rtcnt_t time_to_completion = (pio_sm_get_tx_fifo_level(pio, STATE_MACHINE) + 1) * MAX(WS2812_T1H + WS2812_T1L, WS2812_T0H + WS2812_T0L);\n\n#if defined(WS2812_RGBW)\n    time_to_completion *= 32;\n#else\n    time_to_completion *= 24;\n#endif\n\n    // Convert from ns to us\n    time_to_completion /= 1000;\n\n    update_us_since_boot(&LAST_TRANSFER, time_us_64() + time_to_completion + WS2812_TRST_US);\n\n    osalSysLockFromISR();\n    chSemSignalI(&TRANSFER_COUNTER);\n    osalSysUnlockFromISR();\n}\n\nvoid ws2812_init(void) {\n    uint pio_idx = pio_get_index(pio);\n    /* Get PIOx peripheral out of reset state. */\n    hal_lld_peripheral_unreset(pio_idx == 0 ? RESETS_ALLREG_PIO0 : RESETS_ALLREG_PIO1);\n\n    // clang-format off\n    iomode_t rgb_pin_mode = PAL_RP_PAD_SLEWFAST |\n                            PAL_RP_GPIO_OE |\n#if defined(WS2812_EXTERNAL_PULLUP)\n                            PAL_RP_IOCTRL_OEOVER_DRVINVPERI |\n#endif\n                            (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1);\n    // clang-format on\n\n    palSetLineMode(WS2812_DI_PIN, rgb_pin_mode);\n\n    STATE_MACHINE = pio_claim_unused_sm(pio, true);\n    if (STATE_MACHINE < 0) {\n        dprintln(\"ERROR: Failed to acquire state machine for WS2812 output!\");\n        return;\n    }\n\n    uint offset = pio_add_program(pio, &ws2812_program);\n\n    pio_sm_set_consecutive_pindirs(pio, STATE_MACHINE, WS2812_DI_PIN, 1, true);\n\n    pio_sm_config config = pio_get_default_sm_config();\n    sm_config_set_wrap(&config, offset + WS2812_WRAP_TARGET, offset + WS2812_WRAP);\n    sm_config_set_sideset_pins(&config, WS2812_DI_PIN);\n    sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX);\n\n#if defined(WS2812_EXTERNAL_PULLUP)\n    /* Instruct side-set to change the pin-directions instead of outputting\n     * a logic level. We generate our levels the following way:\n     *\n     * 1: Set RGB data pin to high impedance input and let the pull-up drive the\n     * signal high.\n     *\n     * 0: Set RGB data pin to low impedance output and drive the pin low.\n     */\n    sm_config_set_sideset(&config, 1, false, true);\n#else\n    sm_config_set_sideset(&config, 1, false, false);\n#endif\n\n#if defined(WS2812_RGBW)\n    sm_config_set_out_shift(&config, false, true, 32);\n#else\n    sm_config_set_out_shift(&config, false, true, 24);\n#endif\n\n    // Every instruction takes 50ns to execute with a clock speed of 20 MHz,\n    // giving the WS2812 PIO driver its time resolution\n    float div = clock_get_hz(clk_sys) / (20.0f * MHZ);\n    sm_config_set_clkdiv(&config, div);\n\n    pio_sm_init(pio, STATE_MACHINE, offset, &config);\n    pio_sm_set_enabled(pio, STATE_MACHINE, true);\n\n    dma_channel = dmaChannelAlloc(RP_DMA_CHANNEL_ID_ANY, RP_DMA_PRIORITY_WS2812, (rp_dmaisr_t)ws2812_dma_callback, NULL);\n    dmaChannelEnableInterruptX(dma_channel);\n    dmaChannelSetDestinationX(dma_channel, (uint32_t)&pio->txf[STATE_MACHINE]);\n\n    // clang-format off\n    RP_DMA_MODE_WS2812 = DMA_CTRL_TRIG_INCR_READ |\n                         DMA_CTRL_TRIG_DATA_SIZE_WORD |\n                         DMA_CTRL_TRIG_TREQ_SEL(pio == pio0 ? STATE_MACHINE : STATE_MACHINE + 8) |\n                         DMA_CTRL_TRIG_PRIORITY(RP_DMA_PRIORITY_WS2812);\n    // clang-format on\n}\n\nstatic inline void sync_ws2812_transfer(void) {\n    if (chSemWaitTimeout(&TRANSFER_COUNTER, TIME_MS2I(WS2812_LED_COUNT)) == MSG_TIMEOUT) {\n        // Abort the synchronization if we have to wait longer than the total\n        // count of LEDs in milliseconds. This is safely much longer than it\n        // would take to push all the data out.\n        dprintln(\"ERROR: WS2812 DMA transfer has stalled, aborting!\");\n        dmaChannelDisableX(dma_channel);\n        pio_sm_clear_fifos(pio, STATE_MACHINE);\n        pio_sm_restart(pio, STATE_MACHINE);\n        chSemReset(&TRANSFER_COUNTER, 0);\n        wait_us(WS2812_TRST_US);\n        return;\n    }\n\n    // Busy wait until last transfer has finished\n    busy_wait_until(LAST_TRANSFER);\n}\n\nws2812_led_t ws2812_leds[WS2812_LED_COUNT];\n\nvoid ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    ws2812_leds[index].r = red;\n    ws2812_leds[index].g = green;\n    ws2812_leds[index].b = blue;\n#if defined(WS2812_RGBW)\n    ws2812_rgb_to_rgbw(&ws2812_leds[index]);\n#endif\n}\n\nvoid ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < WS2812_LED_COUNT; i++) {\n        ws2812_set_color(i, red, green, blue);\n    }\n}\n\nvoid ws2812_flush(void) {\n    sync_ws2812_transfer();\n\n    for (int i = 0; i < WS2812_LED_COUNT; i++) {\n#if defined(WS2812_RGBW)\n        WS2812_BUFFER[i] = rgbw8888_to_u32(ws2812_leds[i].r, ws2812_leds[i].g, ws2812_leds[i].b, ws2812_leds[i].w);\n#else\n        WS2812_BUFFER[i] = rgbw8888_to_u32(ws2812_leds[i].r, ws2812_leds[i].g, ws2812_leds[i].b, 0);\n#endif\n    }\n\n    dmaChannelSetSourceX(dma_channel, (uint32_t)WS2812_BUFFER);\n    dmaChannelSetCounterX(dma_channel, WS2812_LED_COUNT);\n    dmaChannelSetModeX(dma_channel, RP_DMA_MODE_WS2812);\n    dmaChannelEnableX(dma_channel);\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <stdbool.h>\n#include <hal.h>\n#include \"timer.h\"\n#include \"wear_leveling.h\"\n#include \"wear_leveling_efl_config.h\"\n#include \"wear_leveling_internal.h\"\n\nstatic flash_offset_t base_offset = UINT32_MAX;\n\n#if defined(WEAR_LEVELING_EFL_FIRST_SECTOR)\nstatic flash_sector_t first_sector = WEAR_LEVELING_EFL_FIRST_SECTOR;\n#else  // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)\nstatic flash_sector_t first_sector = UINT16_MAX;\n#endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)\n\n#if !defined(WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT)\n#    define WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT 0\n#endif // WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT\n\nstatic flash_sector_t sector_count = UINT16_MAX;\nstatic BaseFlash *    flash;\nstatic bool           flash_erased_is_one;\nstatic volatile bool  is_issuing_read    = false;\nstatic volatile bool  ecc_error_occurred = false;\n\n// \"Automatic\" detection of the flash size -- ideally ChibiOS would have this already, but alas, it doesn't.\nstatic inline uint32_t detect_flash_size(void) {\n#if defined(WEAR_LEVELING_EFL_FLASH_SIZE)\n    return WEAR_LEVELING_EFL_FLASH_SIZE;\n#elif defined(FLASH_BANK_SIZE)\n    return FLASH_BANK_SIZE;\n#elif defined(FLASH_SIZE)\n    return FLASH_SIZE;\n#elif defined(FLASHSIZE_BASE)\n#    if defined(QMK_MCU_SERIES_STM32F0XX) || defined(QMK_MCU_SERIES_STM32F1XX) || defined(QMK_MCU_SERIES_STM32F3XX) || defined(QMK_MCU_SERIES_STM32F4XX) || defined(QMK_MCU_SERIES_STM32G4XX) || defined(QMK_MCU_SERIES_STM32L0XX) || defined(QMK_MCU_SERIES_STM32L4XX) || defined(QMK_MCU_SERIES_AT32F415) || defined(QMK_MCU_SERIES_GD32VF103)\n    return ((*(uint32_t *)FLASHSIZE_BASE) & 0xFFFFU) << 10U; // this register has the flash size in kB, so we convert it to bytes\n#    elif defined(QMK_MCU_SERIES_STM32L1XX)\n#        error This MCU family has an uncommon flash size register definition and has not been implemented. Perhaps try using the true EEPROM on the MCU instead?\n#    endif\n#else\n#    error Unknown flash size definition.\n    return 0;\n#endif\n}\n\nbool backing_store_init(void) {\n    bs_dprintf(\"Init\\n\");\n    flash = (BaseFlash *)&EFLD1;\n\n    // Need to re-lock the EFL, as if we've just had the bootloader executing it'll already be unlocked.\n    backing_store_lock();\n\n    const flash_descriptor_t *desc       = flashGetDescriptor(flash);\n    uint32_t                  counter    = 0;\n    uint32_t                  flash_size = detect_flash_size();\n\n    // Check if the hardware erase is logic 1\n    flash_erased_is_one = (desc->attributes & FLASH_ATTR_ERASED_IS_ONE) ? true : false;\n\n    if (WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT >= desc->sectors_count) {\n        // Last sector defined is greater than available number of sectors. Can't do anything here. Fault.\n        chSysHalt(\"Last sector intended to be used with wear_leveling is beyond available flash descriptor range\");\n    }\n\n#if defined(WEAR_LEVELING_EFL_FIRST_SECTOR)\n\n    // Work out how many sectors we want to use, working forwards from the first sector specified\n    flash_sector_t last_sector = desc->sectors_count - WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT;\n    for (flash_sector_t i = 0; i < last_sector - first_sector; ++i) {\n        counter += flashGetSectorSize(flash, first_sector + i);\n        if (counter >= (WEAR_LEVELING_BACKING_SIZE)) {\n            sector_count = i + 1;\n            base_offset  = flashGetSectorOffset(flash, first_sector);\n            break;\n        }\n    }\n    if (sector_count == UINT16_MAX || base_offset >= flash_size) {\n        // We didn't get the required number of sectors. Can't do anything here. Fault.\n        chSysHalt(\"Invalid sector count intended to be used with wear_leveling\");\n    }\n\n#else // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)\n\n    // Work out how many sectors we want to use, working backwards from the end of the flash\n    flash_sector_t last_sector = desc->sectors_count - WEAR_LEVELING_EFL_OMIT_LAST_SECTOR_COUNT;\n    for (flash_sector_t i = 0; i < last_sector; ++i) {\n        first_sector = last_sector - i - 1;\n        if (flashGetSectorOffset(flash, first_sector) >= flash_size) {\n            last_sector = first_sector;\n            continue;\n        }\n        counter += flashGetSectorSize(flash, first_sector);\n        if (counter >= (WEAR_LEVELING_BACKING_SIZE)) {\n            sector_count = last_sector - first_sector;\n            base_offset  = flashGetSectorOffset(flash, first_sector);\n            break;\n        }\n    }\n\n#endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR)\n\n    return true;\n}\n\nbool backing_store_unlock(void) {\n    bs_dprintf(\"Unlock\\n\");\n    return eflStart(&EFLD1, NULL) == HAL_RET_SUCCESS;\n}\n\nbool backing_store_erase(void) {\n#ifdef WEAR_LEVELING_DEBUG_OUTPUT\n    uint32_t start = timer_read32();\n#endif\n\n    bool          ret = true;\n    flash_error_t status;\n    for (int i = 0; i < sector_count; ++i) {\n        // Kick off the sector erase\n        status = flashStartEraseSector(flash, first_sector + i);\n        if (status != FLASH_NO_ERROR && status != FLASH_BUSY_ERASING) {\n            ret = false;\n        }\n\n        // Wait for the erase to complete\n        status = flashWaitErase(flash);\n        if (status != FLASH_NO_ERROR && status != FLASH_BUSY_ERASING) {\n            ret = false;\n        }\n    }\n\n    bs_dprintf(\"Backing store erase took %ldms to complete\\n\", ((long)(timer_read32() - start)));\n    return ret;\n}\n\nbool backing_store_write(uint32_t address, backing_store_int_t value) {\n    uint32_t offset = (base_offset + address);\n    bs_dprintf(\"Write \");\n    wl_dump(offset, &value, sizeof(value));\n    if (flash_erased_is_one) {\n        value = ~value;\n    }\n    return flashProgram(flash, offset, sizeof(value), (const uint8_t *)&value) == FLASH_NO_ERROR;\n}\n\nbool backing_store_lock(void) {\n    bs_dprintf(\"Lock  \\n\");\n    eflStop(&EFLD1);\n    return true;\n}\n\nstatic backing_store_int_t backing_store_safe_read_from_location(backing_store_int_t *loc) {\n    backing_store_int_t value;\n    is_issuing_read    = true;\n    ecc_error_occurred = false;\n    value              = flash_erased_is_one ? ~(*loc) : (*loc);\n    is_issuing_read    = false;\n    return value;\n}\n\nbool backing_store_read(uint32_t address, backing_store_int_t *value) {\n    uint32_t             offset = (base_offset + address);\n    backing_store_int_t *loc    = (backing_store_int_t *)flashGetOffsetAddress(flash, offset);\n    backing_store_int_t  tmp    = backing_store_safe_read_from_location(loc);\n\n    if (ecc_error_occurred) {\n        bs_dprintf(\"Failed to read from backing store, ECC error detected\\n\");\n        ecc_error_occurred = false;\n        *value             = 0;\n        return false;\n    }\n\n    *value = tmp;\n\n    bs_dprintf(\"Read  \");\n    wl_dump(offset, value, sizeof(backing_store_int_t));\n    return true;\n}\n\nbool backing_store_allow_ecc_errors(void) {\n    return is_issuing_read;\n}\n\nvoid backing_store_signal_ecc_error(void) {\n    ecc_error_occurred = true;\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/wear_leveling/wear_leveling_efl_config.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifndef __ASSEMBLER__\n#    include <hal.h>\n#endif\n\n// Work out how many bytes per write to internal flash\n#ifndef BACKING_STORE_WRITE_SIZE\n// These need to match EFL's XXXXXX_FLASH_LINE_SIZE, see associated code in `lib/chibios/os/hal/ports/**/hal_efl_lld.c`,\n// or associated `stm32_registry.h` for the MCU in question (or equivalent for the family).\n#    if defined(QMK_MCU_SERIES_GD32VF103)\n#        define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c\n#    elif defined(QMK_MCU_FAMILY_NUC123)\n#        define BACKING_STORE_WRITE_SIZE 4 // from hal_efl_lld.c\n#    elif defined(QMK_MCU_FAMILY_WB32)\n#        define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c\n#    elif defined(QMK_MCU_FAMILY_AT32)\n#        define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c\n#    elif defined(QMK_MCU_FAMILY_STM32)\n#        if defined(STM32_FLASH_LINE_SIZE) // from some family's stm32_registry.h file\n#            define BACKING_STORE_WRITE_SIZE (STM32_FLASH_LINE_SIZE)\n#        else\n#            if defined(QMK_MCU_SERIES_STM32F0XX)\n#                define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c\n#            elif defined(QMK_MCU_SERIES_STM32F1XX)\n#                define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c\n#            elif defined(QMK_MCU_SERIES_STM32F3XX)\n#                define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c\n#            elif defined(QMK_MCU_SERIES_STM32F4XX)\n#                define BACKING_STORE_WRITE_SIZE (1 << STM32_FLASH_PSIZE) // from hal_efl_lld.c\n#            elif defined(QMK_MCU_SERIES_STM32L4XX)\n#                define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c\n#            elif defined(QMK_MCU_SERIES_STM32G0XX)\n#                define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c\n#            elif defined(QMK_MCU_SERIES_STM32G4XX)\n#                define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c\n#            else\n#                error \"ChibiOS hasn't defined STM32_FLASH_LINE_SIZE, and could not automatically determine BACKING_STORE_WRITE_SIZE\" // normally defined in stm32_registry.h, should be set by STM32_FLASH_LINE_SIZE\n#            endif\n#        endif\n#    else\n#        error \"Could not automatically determine BACKING_STORE_WRITE_SIZE\"\n#    endif\n#endif\n\n// 2kB backing space allocated\n#ifndef WEAR_LEVELING_BACKING_SIZE\n#    define WEAR_LEVELING_BACKING_SIZE 2048\n#endif // WEAR_LEVELING_BACKING_SIZE\n\n// 1kB logical EEPROM\n#ifndef WEAR_LEVELING_LOGICAL_SIZE\n#    define WEAR_LEVELING_LOGICAL_SIZE ((WEAR_LEVELING_BACKING_SIZE) / 2)\n#endif // WEAR_LEVELING_LOGICAL_SIZE\n"
  },
  {
    "path": "platforms/chibios/drivers/wear_leveling/wear_leveling_legacy.c",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <stdbool.h>\n#include <hal.h>\n#include \"timer.h\"\n#include \"wear_leveling.h\"\n#include \"wear_leveling_legacy_config.h\"\n#include \"wear_leveling_internal.h\"\n#include \"legacy_flash_ops.h\"\n\nbool backing_store_init(void) {\n    bs_dprintf(\"Init\\n\");\n    return true;\n}\n\nbool backing_store_unlock(void) {\n    bs_dprintf(\"Unlock\\n\");\n    FLASH_Unlock();\n    return true;\n}\n\nbool backing_store_erase(void) {\n#ifdef WEAR_LEVELING_DEBUG_OUTPUT\n    uint32_t start = timer_read32();\n#endif\n\n    bool         ret = true;\n    FLASH_Status status;\n    for (int i = 0; i < (WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT); ++i) {\n        status = FLASH_ErasePage(WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS + (i * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE)));\n        if (status != FLASH_COMPLETE) {\n            ret = false;\n        }\n    }\n\n    bs_dprintf(\"Backing store erase took %ldms to complete\\n\", ((long)(timer_read32() - start)));\n    return ret;\n}\n\nbool backing_store_write(uint32_t address, backing_store_int_t value) {\n    uint32_t offset = ((WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS) + address);\n    bs_dprintf(\"Write \");\n    wl_dump(offset, &value, sizeof(backing_store_int_t));\n    return FLASH_ProgramHalfWord(offset, ~value) == FLASH_COMPLETE;\n}\n\nbool backing_store_lock(void) {\n    bs_dprintf(\"Lock  \\n\");\n    FLASH_Lock();\n    return true;\n}\n\nbool backing_store_read(uint32_t address, backing_store_int_t* value) {\n    uint32_t             offset = ((WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS) + address);\n    backing_store_int_t* loc    = (backing_store_int_t*)offset;\n    *value                      = ~(*loc);\n    bs_dprintf(\"Read  \");\n    wl_dump(offset, loc, sizeof(backing_store_int_t));\n    return true;\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/wear_leveling/wear_leveling_legacy_config.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n// Work out the page size to use\n#ifndef WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE\n#    if defined(QMK_MCU_STM32F042)\n#        define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 1024\n#    elif defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072)\n#        define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 2048\n#    elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411)\n#        define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 16384\n#    endif\n#endif\n\n// Work out how much flash space we have\n#ifndef WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE\n#    define WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE ((*(uint32_t *)FLASHSIZE_BASE) & 0xFFFFU) // in kB\n#endif\n\n// The base location of program memory\n#ifndef WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE\n#    define WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE 0x08000000\n#endif\n\n// The number of pages to use\n#ifndef WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT\n#    if defined(QMK_MCU_STM32F042)\n#        define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 2\n#    elif defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072)\n#        define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 1\n#    elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411)\n#        define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 1\n#    endif\n#endif\n\n// The origin of the emulated eeprom\n#ifndef WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS\n#    if defined(QMK_MCU_STM32F042) || defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072)\n#        define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS ((uintptr_t)(WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE) + WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE * 1024 - (WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT * WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))\n#    elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411)\n#        if defined(BOOTLOADER_STM32_DFU)\n#            define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS (WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE + (1 * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))) // +16k\n#        elif defined(BOOTLOADER_TINYUF2)\n#            define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS (WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE + (3 * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))) // +48k\n#        endif\n#    endif\n#endif\n\n// 2-byte writes\n#ifndef BACKING_STORE_WRITE_SIZE\n#    define BACKING_STORE_WRITE_SIZE 2\n#endif\n\n// The amount of space to use for the entire set of emulation\n#ifndef WEAR_LEVELING_BACKING_SIZE\n#    if defined(QMK_MCU_STM32F042) || defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072)\n#        define WEAR_LEVELING_BACKING_SIZE 2048\n#    elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411)\n#        define WEAR_LEVELING_BACKING_SIZE 16384\n#    endif\n#endif\n\n// The logical amount of eeprom available\n#ifndef WEAR_LEVELING_LOGICAL_SIZE\n#    define WEAR_LEVELING_LOGICAL_SIZE 1024\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c",
    "content": "/**\n * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.\n * Copyright (c) 2022 Nick Brassel (@tzarc)\n * Copyright (c) 2022 Stefan Kerkmann (@KarlK90)\n *\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n#include <stdbool.h>\n\n#include \"pico/bootrom.h\"\n#include \"hardware/flash.h\"\n#include \"hardware/sync.h\"\n#include \"hardware/structs/ssi.h\"\n#include \"hardware/structs/ioqspi.h\"\n\n#include \"compiler_support.h\"\n#include \"timer.h\"\n#include \"wear_leveling.h\"\n#include \"wear_leveling_rp2040_flash_config.h\"\n#include \"wear_leveling_internal.h\"\n\n#ifndef WEAR_LEVELING_RP2040_FLASH_BULK_COUNT\n#    define WEAR_LEVELING_RP2040_FLASH_BULK_COUNT 64\n#endif // WEAR_LEVELING_RP2040_FLASH_BULK_COUNT\n\n#define FLASHCMD_PAGE_PROGRAM 0x02\n#define FLASHCMD_READ_STATUS 0x05\n#define FLASHCMD_WRITE_ENABLE 0x06\n\nextern const uint8_t BOOT2_ROM[256];\nstatic uint32_t      BOOT2_ROM_RAM[64];\n\nstatic ssi_hw_t *const ssi = (ssi_hw_t *)XIP_SSI_BASE;\n\n// Sanity check\ncheck_hw_layout(ssi_hw_t, ssienr, SSI_SSIENR_OFFSET);\ncheck_hw_layout(ssi_hw_t, spi_ctrlr0, SSI_SPI_CTRLR0_OFFSET);\n\nstatic void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) {\n    ((void (*)(void))BOOT2_ROM_RAM + 1)();\n}\n\n// Bitbanging the chip select using IO overrides, in case RAM-resident IRQs\n// are still running, and the FIFO bottoms out. (the bootrom does the same)\nstatic void __no_inline_not_in_flash_func(flash_cs_force)(bool high) {\n    uint32_t field_val = high ? IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH : IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW;\n    hw_write_masked(&ioqspi_hw->io[1].ctrl, field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB, IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS);\n}\n\n// Also allow any unbounded loops to check whether the above abort condition\n// was asserted, and terminate early\nstatic int __no_inline_not_in_flash_func(flash_was_aborted)(void) {\n    return *(io_rw_32 *)(IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SD1_CTRL_OFFSET) & IO_QSPI_GPIO_QSPI_SD1_CTRL_INOVER_BITS;\n}\n\n// Put bytes from one buffer, and get bytes into another buffer.\n// These can be the same buffer.\n// If tx is NULL then send zeroes.\n// If rx is NULL then all read data will be dropped.\n//\n// If rx_skip is nonzero, this many bytes will first be consumed from the FIFO,\n// before reading a further count bytes into *rx.\n// E.g. if you have written a command+address just before calling this function.\nstatic void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx, uint8_t *rx, size_t count, size_t rx_skip) {\n    // Make sure there is never more data in flight than the depth of the RX\n    // FIFO. Otherwise, when we are interrupted for long periods, hardware\n    // will overflow the RX FIFO.\n    const uint max_in_flight = 16 - 2; // account for data internal to SSI\n    size_t     tx_count      = count;\n    size_t     rx_count      = count;\n    while (tx_count || rx_skip || rx_count) {\n        // NB order of reads, for pessimism rather than optimism\n        uint32_t tx_level      = ssi_hw->txflr;\n        uint32_t rx_level      = ssi_hw->rxflr;\n        bool     did_something = false; // Expect this to be folded into control flow, not register\n        if (tx_count && tx_level + rx_level < max_in_flight) {\n            ssi->dr0 = (uint32_t)(tx ? *tx++ : 0);\n            --tx_count;\n            did_something = true;\n        }\n        if (rx_level) {\n            uint8_t rxbyte = ssi->dr0;\n            did_something  = true;\n            if (rx_skip) {\n                --rx_skip;\n            } else {\n                if (rx) *rx++ = rxbyte;\n                --rx_count;\n            }\n        }\n        // APB load costs 4 cycles, so only do it on idle loops (our budget is\n        // 48 cyc/byte)\n        if (!did_something && __builtin_expect(flash_was_aborted(), 0)) break;\n    }\n    flash_cs_force(1);\n}\n\n// Convenience wrapper for above\n// (And it's hard for the debug host to get the tight timing between\n// cmd DR0 write and the remaining data)\nstatic void __no_inline_not_in_flash_func(_flash_do_cmd)(uint8_t cmd, const uint8_t *tx, uint8_t *rx, size_t count) {\n    flash_cs_force(0);\n    ssi->dr0 = cmd;\n    flash_put_get(tx, rx, count, 1);\n}\n\n// Timing of this one is critical, so do not expose the symbol to debugger etc\nstatic void __no_inline_not_in_flash_func(flash_put_cmd_addr)(uint8_t cmd, uint32_t addr) {\n    flash_cs_force(0);\n    addr |= cmd << 24;\n    for (int i = 0; i < 4; ++i) {\n        ssi->dr0 = addr >> 24;\n        addr <<= 8;\n    }\n}\n\n// Poll the flash status register until the busy bit (LSB) clears\nstatic void __no_inline_not_in_flash_func(flash_wait_ready)(void) {\n    uint8_t stat;\n    do {\n        _flash_do_cmd(FLASHCMD_READ_STATUS, NULL, &stat, 1);\n    } while (stat & 0x1 && !flash_was_aborted());\n}\n\n// Set the WEL bit (needed before any program/erase operation)\nstatic void __no_inline_not_in_flash_func(flash_enable_write)(void) {\n    _flash_do_cmd(FLASHCMD_WRITE_ENABLE, NULL, NULL, 0);\n}\n\nstatic void __no_inline_not_in_flash_func(pico_program_bulk)(uint32_t flash_address, backing_store_int_t *values, size_t item_count) {\n    rom_connect_internal_flash_fn connect_internal_flash = (rom_connect_internal_flash_fn)rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH);\n    rom_flash_exit_xip_fn         flash_exit_xip         = (rom_flash_exit_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP);\n    rom_flash_flush_cache_fn      flash_flush_cache      = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE);\n    assert(connect_internal_flash && flash_exit_xip && flash_flush_cache);\n\n    static backing_store_int_t bulk_write_buffer[WEAR_LEVELING_RP2040_FLASH_BULK_COUNT];\n\n    while (item_count) {\n        size_t batch_size = MIN(item_count, WEAR_LEVELING_RP2040_FLASH_BULK_COUNT);\n        for (size_t i = 0; i < batch_size; i++, values++, item_count--) {\n            bulk_write_buffer[i] = ~(*values);\n        }\n        __compiler_memory_barrier();\n\n        connect_internal_flash();\n        flash_exit_xip();\n        flash_enable_write();\n\n        flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, flash_address);\n        flash_put_get((uint8_t *)bulk_write_buffer, NULL, batch_size * sizeof(backing_store_int_t), 4);\n        flash_wait_ready();\n        flash_address += batch_size * sizeof(backing_store_int_t);\n\n        flash_flush_cache();\n        flash_enable_xip_via_boot2();\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// QMK Wear-Leveling Backing Store implementation\n\nstatic int interrupts;\n\nbool backing_store_init(void) {\n    bs_dprintf(\"Init\\n\");\n    memcpy(BOOT2_ROM_RAM, BOOT2_ROM, sizeof(BOOT2_ROM));\n    __compiler_memory_barrier();\n    return true;\n}\n\nbool backing_store_unlock(void) {\n    bs_dprintf(\"Unlock\\n\");\n    return true;\n}\n\nbool backing_store_erase(void) {\n#ifdef WEAR_LEVELING_DEBUG_OUTPUT\n    uint32_t start = timer_read32();\n#endif\n\n    // Ensure the backing size can be cleanly subtracted from the flash size without alignment issues.\n    STATIC_ASSERT((WEAR_LEVELING_BACKING_SIZE) % (FLASH_SECTOR_SIZE) == 0, \"Backing size must be a multiple of FLASH_SECTOR_SIZE\");\n\n    interrupts = save_and_disable_interrupts();\n    flash_range_erase((WEAR_LEVELING_RP2040_FLASH_BASE), (WEAR_LEVELING_BACKING_SIZE));\n    restore_interrupts(interrupts);\n\n    bs_dprintf(\"Backing store erase took %ldms to complete\\n\", ((long)(timer_read32() - start)));\n    return true;\n}\n\nbool backing_store_write(uint32_t address, backing_store_int_t value) {\n    return backing_store_write_bulk(address, &value, 1);\n}\n\nbool backing_store_write_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {\n    uint32_t offset = (WEAR_LEVELING_RP2040_FLASH_BASE) + address;\n    bs_dprintf(\"Write \");\n    wl_dump(offset, values, sizeof(backing_store_int_t) * item_count);\n    interrupts = save_and_disable_interrupts();\n    pico_program_bulk(offset, values, item_count);\n    restore_interrupts(interrupts);\n    return true;\n}\n\nbool backing_store_lock(void) {\n    return true;\n}\n\nbool backing_store_read(uint32_t address, backing_store_int_t *value) {\n    return backing_store_read_bulk(address, value, 1);\n}\n\nbool backing_store_read_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {\n    uint32_t             offset = (WEAR_LEVELING_RP2040_FLASH_BASE) + address;\n    backing_store_int_t *loc    = (backing_store_int_t *)((XIP_BASE) + offset);\n    for (size_t i = 0; i < item_count; ++i) {\n        values[i] = ~loc[i];\n    }\n    bs_dprintf(\"Read  \");\n    wl_dump(offset, values, item_count * sizeof(backing_store_int_t));\n    return true;\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash_config.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#ifndef __ASSEMBLER__\n#    include \"hardware/flash.h\"\n#endif\n\n// 2-byte writes\n#ifndef BACKING_STORE_WRITE_SIZE\n#    define BACKING_STORE_WRITE_SIZE 2\n#endif\n\n// 64kB backing space allocated\n#ifndef WEAR_LEVELING_BACKING_SIZE\n#    define WEAR_LEVELING_BACKING_SIZE 8192\n#endif // WEAR_LEVELING_BACKING_SIZE\n\n// 32kB logical EEPROM\n#ifndef WEAR_LEVELING_LOGICAL_SIZE\n#    define WEAR_LEVELING_LOGICAL_SIZE ((WEAR_LEVELING_BACKING_SIZE) / 2)\n#endif // WEAR_LEVELING_LOGICAL_SIZE\n\n// Define how much flash space we have (defaults to lib/pico-sdk/src/boards/include/boards/***)\n#ifndef WEAR_LEVELING_RP2040_FLASH_SIZE\n#    define WEAR_LEVELING_RP2040_FLASH_SIZE (PICO_FLASH_SIZE_BYTES)\n#endif\n\n// Define the location of emulated EEPROM\n#ifndef WEAR_LEVELING_RP2040_FLASH_BASE\n#    define WEAR_LEVELING_RP2040_FLASH_BASE ((WEAR_LEVELING_RP2040_FLASH_SIZE) - (WEAR_LEVELING_BACKING_SIZE))\n#endif\n"
  },
  {
    "path": "platforms/chibios/drivers/ws2812_bitbang.c",
    "content": "#include \"ws2812.h\"\n\n#include \"gpio.h\"\n#include \"chibios_config.h\"\n\n// DEPRECATED - DO NOT USE\n#if defined(NOP_FUDGE)\n#    define WS2812_BITBANG_NOP_FUDGE NOP_FUDGE\n#endif\n\n/* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */\n\n#ifndef WS2812_BITBANG_NOP_FUDGE\n#    if defined(STM32F0XX) || defined(STM32F1XX) || defined(GD32VF103) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) || defined(WB32F3G71xx) || defined(WB32FQ95xx) || defined(AT32F415)\n#        define WS2812_BITBANG_NOP_FUDGE 0.4\n#    else\n#        if defined(RP2040)\n#            error \"Please use `vendor` WS2812 driver for RP2040\"\n#        else\n#            error \"WS2812_BITBANG_NOP_FUDGE configuration required\"\n#        endif\n#        define WS2812_BITBANG_NOP_FUDGE 1 // this just pleases the compile so the above error is easier to spot\n#    endif\n#endif\n\n// Push Pull or Open Drain Configuration\n// Default Push Pull\n#ifndef WS2812_EXTERNAL_PULLUP\n#    define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_PUSHPULL\n#else\n#    define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_OPENDRAIN\n#endif\n\n// The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased\n// to values like 600000 ns. If it is too small, the pixels will show nothing most of the time.\n#ifndef WS2812_RES\n#    define WS2812_RES (1000 * WS2812_TRST_US) // Width of the low gap between bits to cause a frame to latch\n#endif\n\n#define NUMBER_NOPS 6\n#define CYCLES_PER_SEC (CPU_CLOCK / NUMBER_NOPS * WS2812_BITBANG_NOP_FUDGE)\n#define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives\n#define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC)\n#define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE)\n\n#define wait_ns(x)                                  \\\n    do {                                            \\\n        for (int i = 0; i < NS_TO_CYCLES(x); i++) { \\\n            __asm__ volatile(\"nop\\n\\t\"              \\\n                             \"nop\\n\\t\"              \\\n                             \"nop\\n\\t\"              \\\n                             \"nop\\n\\t\"              \\\n                             \"nop\\n\\t\"              \\\n                             \"nop\\n\\t\");            \\\n        }                                           \\\n    } while (0)\n\nvoid sendByte(uint8_t byte) {\n    // WS2812 protocol wants most significant bits first\n    for (unsigned char bit = 0; bit < 8; bit++) {\n        bool is_one = byte & (1 << (7 - bit));\n        // using something like wait_ns(is_one ? T1L : T0L) here throws off timings\n        if (is_one) {\n            // 1\n            gpio_write_pin_high(WS2812_DI_PIN);\n            wait_ns(WS2812_T1H);\n            gpio_write_pin_low(WS2812_DI_PIN);\n            wait_ns(WS2812_T1L);\n        } else {\n            // 0\n            gpio_write_pin_high(WS2812_DI_PIN);\n            wait_ns(WS2812_T0H);\n            gpio_write_pin_low(WS2812_DI_PIN);\n            wait_ns(WS2812_T0L);\n        }\n    }\n}\n\nws2812_led_t ws2812_leds[WS2812_LED_COUNT];\n\nvoid ws2812_init(void) {\n    palSetLineMode(WS2812_DI_PIN, WS2812_OUTPUT_MODE);\n}\n\nvoid ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    ws2812_leds[index].r = red;\n    ws2812_leds[index].g = green;\n    ws2812_leds[index].b = blue;\n#if defined(WS2812_RGBW)\n    ws2812_rgb_to_rgbw(&ws2812_leds[index]);\n#endif\n}\n\nvoid ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < WS2812_LED_COUNT; i++) {\n        ws2812_set_color(i, red, green, blue);\n    }\n}\n\nvoid ws2812_flush(void) {\n    // this code is very time dependent, so we need to disable interrupts\n    chSysLock();\n\n    for (int i = 0; i < WS2812_LED_COUNT; i++) {\n        // WS2812 protocol dictates grb order\n#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)\n        sendByte(ws2812_leds[i].g);\n        sendByte(ws2812_leds[i].r);\n        sendByte(ws2812_leds[i].b);\n#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)\n        sendByte(ws2812_leds[i].r);\n        sendByte(ws2812_leds[i].g);\n        sendByte(ws2812_leds[i].b);\n#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)\n        sendByte(ws2812_leds[i].b);\n        sendByte(ws2812_leds[i].g);\n        sendByte(ws2812_leds[i].r);\n#endif\n\n#ifdef WS2812_RGBW\n        sendByte(ws2812_leds[i].w);\n#endif\n    }\n\n    wait_ns(WS2812_RES);\n\n    chSysUnlock();\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/ws2812_pwm.c",
    "content": "#include \"ws2812.h\"\n#include \"gpio.h\"\n#include \"chibios_config.h\"\n\n// ======== DEPRECATED DEFINES - DO NOT USE ========\n#ifdef WS2812_DMA_STREAM\n#    define WS2812_PWM_DMA_STREAM WS2812_DMA_STREAM\n#endif\n#ifdef WS2812_DMA_CHANNEL\n#    define WS2812_PWM_DMA_CHANNEL WS2812_DMA_CHANNEL\n#endif\n#ifdef WS2812_DMAMUX_ID\n#    define WS2812_PWM_DMAMUX_ID WS2812_DMAMUX_ID\n#endif\n// ========\n\n/* Adapted from https://github.com/joewa/WS2812-LED-Driver_ChibiOS/ */\n\n#ifdef WS2812_RGBW\n#    define WS2812_CHANNELS 4\n#else\n#    define WS2812_CHANNELS 3\n#endif\n\n#ifndef WS2812_PWM_DRIVER\n#    define WS2812_PWM_DRIVER PWMD2 // TIMx\n#endif\n#ifndef WS2812_PWM_CHANNEL\n#    define WS2812_PWM_CHANNEL 2 // Channel\n#endif\n#ifndef WS2812_PWM_PAL_MODE\n#    define WS2812_PWM_PAL_MODE 2 // DI Pin's alternate function value\n#endif\n#ifndef WS2812_PWM_DMA_STREAM\n#    define WS2812_PWM_DMA_STREAM STM32_DMA1_STREAM2 // DMA Stream for TIMx_UP\n#endif\n#ifndef WS2812_PWM_DMA_CHANNEL\n#    define WS2812_PWM_DMA_CHANNEL 2 // DMA Channel for TIMx_UP\n#endif\n#if (STM32_DMA_SUPPORTS_DMAMUX == TRUE) && !defined(WS2812_PWM_DMAMUX_ID)\n#    error \"please consult your MCU's datasheet and specify in your config.h: #define WS2812_PWM_DMAMUX_ID STM32_DMAMUX1_TIM?_UP\"\n#endif\n#if (AT32_DMA_SUPPORTS_DMAMUX == TRUE) && !defined(WS2812_PWM_DMAMUX_CHANNEL) && !defined(WS2812_PWM_DMAMUX_ID)\n#    error \"please consult your MCU's datasheet and specify in your config.h: #define WS2812_PWM_DMAMUX_CHANNEL 1, #define WS2812_PWM_DMAMUX_ID AT32_DMAMUX_TMR?_OVERFLOW\"\n#endif\n\n/* Summarize https://www.st.com/resource/en/application_note/an4013-stm32-crossseries-timer-overview-stmicroelectronics.pdf to\n * figure out if we are using a 32bit timer. This is needed to setup the DMA controller correctly.\n * Ignore STM32H7XX and STM32U5XX as they are not supported by ChibiOS.\n */\n#if !defined(STM32F1XX) && !defined(STM32L0XX) && !defined(STM32L1XX)\n#    define WS2812_PWM_TIMER_32BIT_PWMD2 1\n#endif\n#if !defined(STM32F1XX)\n#    define WS2812_PWM_TIMER_32BIT_PWMD5 1\n#endif\n#define WS2812_CONCAT1(a, b) a##b\n#define WS2812_CONCAT(a, b) WS2812_CONCAT1(a, b)\n#if WS2812_CONCAT(WS2812_PWM_TIMER_32BIT_, WS2812_PWM_DRIVER)\n#    define WS2812_PWM_TIMER_32BIT\n#endif\n\n#ifndef WS2812_PWM_COMPLEMENTARY_OUTPUT\n#    define WS2812_PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_HIGH\n#else\n#    define WS2812_PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH\n#endif\n\n// Push Pull or Open Drain Configuration\n// Default Push Pull\n#ifndef WS2812_EXTERNAL_PULLUP\n#    if defined(USE_GPIOV1)\n#        define WS2812_OUTPUT_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    else\n#        define WS2812_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_PWM_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST | PAL_PUPDR_FLOATING\n#    endif\n#else\n#    if defined(USE_GPIOV1)\n#        define WS2812_OUTPUT_MODE PAL_MODE_ALTERNATE_OPENDRAIN\n#    else\n#        define WS2812_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_PWM_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN | PAL_OUTPUT_SPEED_HIGHEST | PAL_PUPDR_FLOATING\n#    endif\n#endif\n\n// Default is 800000Hz, which has a period of 1.25us\n#ifndef WS2812_PWM_FREQUENCY\n#    define WS2812_PWM_FREQUENCY (1000000000 / WS2812_TIMING)\n#endif\n\n/* --- PRIVATE CONSTANTS ---------------------------------------------------- */\n\n#define WS2812_PWM_TICK_FREQUENCY (CPU_CLOCK / 2)                            /**< Clock frequency of PWM ticks, must be valid with respect to system clock! */\n#define WS2812_PWM_PERIOD (WS2812_PWM_TICK_FREQUENCY / WS2812_PWM_FREQUENCY) /**< Clock period in PWM ticks. */\n\n/**\n * @brief   Number of bit-periods to hold the data line low at the end of a frame\n *\n * The reset period for each frame is defined in WS2812_TRST_US.\n * Calculate the number of zeroes to add at the end assuming 1.25 uS/bit:\n */\n#define WS2812_COLOR_BITS (WS2812_CHANNELS * 8)\n#define WS2812_RESET_BIT_N (1000 * WS2812_TRST_US / WS2812_TIMING)\n#define WS2812_COLOR_BIT_N (WS2812_LED_COUNT * WS2812_COLOR_BITS) /**< Number of data bits */\n#define WS2812_BIT_N (WS2812_COLOR_BIT_N + WS2812_RESET_BIT_N)    /**< Total number of bits in a frame */\n\n/**\n * @brief   High period for a zero, in ticks\n */\n#define WS2812_DUTYCYCLE_0 (WS2812_PWM_TICK_FREQUENCY / (1000000000 / WS2812_T0H))\n#if (WS2812_DUTYCYCLE_0 > 255)\n#    error WS2812 PWM driver: High period for a 0 is more than a byte\n#endif\n\n/**\n * @brief   High period for a one, in ticks\n */\n#define WS2812_DUTYCYCLE_1 (WS2812_PWM_TICK_FREQUENCY / (1000000000 / WS2812_T1H))\n#if (WS2812_DUTYCYCLE_1 > 255)\n#    error WS2812 PWM driver: High period for a 1 is more than a byte\n#endif\n\n/* --- PRIVATE MACROS ------------------------------------------------------- */\n\n/**\n * @brief   Determine the index in @ref ws2812_frame_buffer \"the frame buffer\" of a given bit\n *\n * @param[in] led:                  The led index [0, @ref WS2812_LED_COUNT)\n * @param[in] byte:                 The byte number [0, 2]\n * @param[in] bit:                  The bit number [0, 7]\n *\n * @return                          The bit index\n */\n#define WS2812_BIT(led, byte, bit) (WS2812_COLOR_BITS * (led) + 8 * (byte) + (7 - (bit)))\n\n#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)\n/**\n * @brief   Determine the index in @ref ws2812_frame_buffer \"the frame buffer\" of a given red bit\n *\n * @note    The red byte is the middle byte in the color packet\n *\n * @param[in] led:                  The led index [0, @ref WS2812_LED_COUNT)\n * @param[in] bit:                  The bit number [0, 7]\n *\n * @return                          The bit index\n */\n#    define WS2812_RED_BIT(led, bit) WS2812_BIT((led), 1, (bit))\n\n/**\n * @brief   Determine the index in @ref ws2812_frame_buffer \"the frame buffer\" of a given green bit\n *\n * @note    The red byte is the first byte in the color packet\n *\n * @param[in] led:                  The led index [0, @ref WS2812_LED_COUNT)\n * @param[in] bit:                  The bit number [0, 7]\n *\n * @return                          The bit index\n */\n#    define WS2812_GREEN_BIT(led, bit) WS2812_BIT((led), 0, (bit))\n\n/**\n * @brief   Determine the index in @ref ws2812_frame_buffer \"the frame buffer\" of a given blue bit\n *\n * @note    The red byte is the last byte in the color packet\n *\n * @param[in] led:                  The led index [0, @ref WS2812_LED_COUNT)\n * @param[in] bit:                  The bit index [0, 7]\n *\n * @return                          The bit index\n */\n#    define WS2812_BLUE_BIT(led, bit) WS2812_BIT((led), 2, (bit))\n\n#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)\n/**\n * @brief   Determine the index in @ref ws2812_frame_buffer \"the frame buffer\" of a given red bit\n *\n * @note    The red byte is the middle byte in the color packet\n *\n * @param[in] led:                  The led index [0, @ref WS2812_LED_COUNT)\n * @param[in] bit:                  The bit number [0, 7]\n *\n * @return                          The bit index\n */\n#    define WS2812_RED_BIT(led, bit) WS2812_BIT((led), 0, (bit))\n\n/**\n * @brief   Determine the index in @ref ws2812_frame_buffer \"the frame buffer\" of a given green bit\n *\n * @note    The red byte is the first byte in the color packet\n *\n * @param[in] led:                  The led index [0, @ref WS2812_LED_COUNT)\n * @param[in] bit:                  The bit number [0, 7]\n *\n * @return                          The bit index\n */\n#    define WS2812_GREEN_BIT(led, bit) WS2812_BIT((led), 1, (bit))\n\n/**\n * @brief   Determine the index in @ref ws2812_frame_buffer \"the frame buffer\" of a given blue bit\n *\n * @note    The red byte is the last byte in the color packet\n *\n * @param[in] led:                  The led index [0, @ref WS2812_LED_COUNT)\n * @param[in] bit:                  The bit index [0, 7]\n *\n * @return                          The bit index\n */\n#    define WS2812_BLUE_BIT(led, bit) WS2812_BIT((led), 2, (bit))\n\n#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)\n/**\n * @brief   Determine the index in @ref ws2812_frame_buffer \"the frame buffer\" of a given red bit\n *\n * @note    The red byte is the middle byte in the color packet\n *\n * @param[in] led:                  The led index [0, @ref WS2812_LED_COUNT)\n * @param[in] bit:                  The bit number [0, 7]\n *\n * @return                          The bit index\n */\n#    define WS2812_RED_BIT(led, bit) WS2812_BIT((led), 2, (bit))\n\n/**\n * @brief   Determine the index in @ref ws2812_frame_buffer \"the frame buffer\" of a given green bit\n *\n * @note    The red byte is the first byte in the color packet\n *\n * @param[in] led:                  The led index [0, @ref WS2812_LED_COUNT)\n * @param[in] bit:                  The bit number [0, 7]\n *\n * @return                          The bit index\n */\n#    define WS2812_GREEN_BIT(led, bit) WS2812_BIT((led), 1, (bit))\n\n/**\n * @brief   Determine the index in @ref ws2812_frame_buffer \"the frame buffer\" of a given blue bit\n *\n * @note    The red byte is the last byte in the color packet\n *\n * @param[in] led:                  The led index [0, @ref WS2812_LED_COUNT)\n * @param[in] bit:                  The bit index [0, 7]\n *\n * @return                          The bit index\n */\n#    define WS2812_BLUE_BIT(led, bit) WS2812_BIT((led), 0, (bit))\n#endif\n\n#ifdef WS2812_RGBW\n/**\n * @brief   Determine the index in @ref ws2812_frame_buffer \"the frame buffer\" of a given white bit\n *\n * @note    The white byte is the last byte in the color packet\n *\n * @param[in] led:                  The led index [0, @ref WS2812_LED_N)\n * @param[in] bit:                  The bit index [0, 7]\n *\n * @return                          The bit index\n */\n#    define WS2812_WHITE_BIT(led, bit) WS2812_BIT((led), 3, (bit))\n#endif\n\n/* --- PRIVATE VARIABLES ---------------------------------------------------- */\n\n// STM32F2XX, STM32F4XX and STM32F7XX do NOT zero pad DMA transfers of unequal data width. Buffer width must match TIMx CCR.\n// For all other STM32 DMA transfer will automatically zero pad. We only need to set the right peripheral width.\n#if defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX)\n#    if defined(WS2812_PWM_TIMER_32BIT)\n#        define WS2812_PWM_DMA_MEMORY_WIDTH STM32_DMA_CR_MSIZE_WORD\n#        define WS2812_PWM_DMA_PERIPHERAL_WIDTH STM32_DMA_CR_PSIZE_WORD\ntypedef uint32_t ws2812_buffer_t;\n#    else\n#        define WS2812_PWM_DMA_MEMORY_WIDTH STM32_DMA_CR_MSIZE_HWORD\n#        define WS2812_PWM_DMA_PERIPHERAL_WIDTH STM32_DMA_CR_PSIZE_HWORD\ntypedef uint16_t ws2812_buffer_t;\n#    endif\n#elif defined(AT32F415)\n#    define WS2812_PWM_DMA_MEMORY_WIDTH AT32_DMA_CCTRL_MWIDTH_BYTE\n#    if defined(WS2812_PWM_TIMER_32BIT)\n#        define WS2812_PWM_DMA_PERIPHERAL_WIDTH AT32_DMA_CCTRL_PWIDTH_WORD\n#    else\n#        define WS2812_PWM_DMA_PERIPHERAL_WIDTH AT32_DMA_CCTRL_PWIDTH_HWORD\n#    endif\ntypedef uint8_t ws2812_buffer_t;\n#else\n#    define WS2812_PWM_DMA_MEMORY_WIDTH STM32_DMA_CR_MSIZE_BYTE\n#    if defined(WS2812_PWM_TIMER_32BIT)\n#        define WS2812_PWM_DMA_PERIPHERAL_WIDTH STM32_DMA_CR_PSIZE_WORD\n#    else\n#        define WS2812_PWM_DMA_PERIPHERAL_WIDTH STM32_DMA_CR_PSIZE_HWORD\n#    endif\ntypedef uint8_t ws2812_buffer_t;\n#endif\n\nstatic ws2812_buffer_t ws2812_frame_buffer[WS2812_BIT_N + 1]; /**< Buffer for a frame */\n\n/* --- PUBLIC FUNCTIONS ----------------------------------------------------- */\n/*\n * Gedanke: Double-buffer type transactions: double buffer transfers using two memory pointers for\n * the memory (while the DMA is reading/writing from/to a buffer, the application can\n * write/read to/from the other buffer).\n */\n\nvoid ws2812_init(void) {\n    // Initialize led frame buffer\n    uint32_t i;\n    for (i = 0; i < WS2812_COLOR_BIT_N; i++)\n        ws2812_frame_buffer[i] = WS2812_DUTYCYCLE_0; // All color bits are zero duty cycle\n    for (i = 0; i < WS2812_RESET_BIT_N; i++)\n        ws2812_frame_buffer[i + WS2812_COLOR_BIT_N] = 0; // All reset bits are zero\n\n    palSetLineMode(WS2812_DI_PIN, WS2812_OUTPUT_MODE);\n\n    // PWM Configuration\n    //#pragma GCC diagnostic ignored \"-Woverride-init\"  // Turn off override-init warning for this struct. We use the overriding ability to set a \"default\" channel config\n    static const PWMConfig ws2812_pwm_config = {\n        .frequency = WS2812_PWM_TICK_FREQUENCY,\n        .period    = WS2812_PWM_PERIOD, // Mit dieser Periode wird UDE-Event erzeugt und ein neuer Wert (Länge WS2812_BIT_N) vom DMA ins CCR geschrieben\n        .callback  = NULL,\n        .channels =\n            {\n                [0 ... 3]                = {.mode = PWM_OUTPUT_DISABLED, .callback = NULL},    // Channels default to disabled\n                [WS2812_PWM_CHANNEL - 1] = {.mode = WS2812_PWM_OUTPUT_MODE, .callback = NULL}, // Turn on the channel we care about\n            },\n#if defined(AT32F415)\n        .ctrl2 = 0,\n        .iden  = AT32_TMR_IDEN_OVFDEN, // DMA on update event for next period\n#else\n        .cr2  = 0,\n        .dier = TIM_DIER_UDE, // DMA on update event for next period\n#endif\n    };\n    //#pragma GCC diagnostic pop  // Restore command-line warning options\n\n    // Configure DMA\n    // dmaInit(); // Joe added this\n#if defined(WB32F3G71xx) || defined(WB32FQ95xx)\n    dmaStreamAlloc(WS2812_PWM_DMA_STREAM - WB32_DMA_STREAM(0), 10, NULL, NULL);\n    dmaStreamSetSource(WS2812_PWM_DMA_STREAM, ws2812_frame_buffer);\n    dmaStreamSetDestination(WS2812_PWM_DMA_STREAM, &(WS2812_PWM_DRIVER.tim->CCR[WS2812_PWM_CHANNEL - 1])); // Ziel ist der An-Zeit im Cap-Comp-Register\n    dmaStreamSetMode(WS2812_PWM_DMA_STREAM, WB32_DMA_CHCFG_HWHIF(WS2812_PWM_DMA_CHANNEL) | WB32_DMA_CHCFG_DIR_M2P | WB32_DMA_CHCFG_PSIZE_WORD | WB32_DMA_CHCFG_MSIZE_WORD | WB32_DMA_CHCFG_MINC | WB32_DMA_CHCFG_CIRC | WB32_DMA_CHCFG_TCIE | WB32_DMA_CHCFG_PL(3));\n#elif defined(AT32F415)\n    dmaStreamAlloc(WS2812_PWM_DMA_STREAM - AT32_DMA_STREAM(0), 10, NULL, NULL);\n    dmaStreamSetPeripheral(WS2812_PWM_DMA_STREAM, &(WS2812_PWM_DRIVER.tmr->CDT[WS2812_PWM_CHANNEL - 1])); // Ziel ist der An-Zeit im Cap-Comp-Register\n    dmaStreamSetMemory0(WS2812_PWM_DMA_STREAM, ws2812_frame_buffer);\n    dmaStreamSetMode(WS2812_PWM_DMA_STREAM, AT32_DMA_CCTRL_DTD_M2P | WS2812_PWM_DMA_PERIPHERAL_WIDTH | WS2812_PWM_DMA_MEMORY_WIDTH | AT32_DMA_CCTRL_MINCM | AT32_DMA_CCTRL_LM | AT32_DMA_CCTRL_CHPL(3));\n#else\n    dmaStreamAlloc(WS2812_PWM_DMA_STREAM - STM32_DMA_STREAM(0), 10, NULL, NULL);\n    dmaStreamSetPeripheral(WS2812_PWM_DMA_STREAM, &(WS2812_PWM_DRIVER.tim->CCR[WS2812_PWM_CHANNEL - 1])); // Ziel ist der An-Zeit im Cap-Comp-Register\n    dmaStreamSetMemory0(WS2812_PWM_DMA_STREAM, ws2812_frame_buffer);\n    dmaStreamSetMode(WS2812_PWM_DMA_STREAM, STM32_DMA_CR_CHSEL(WS2812_PWM_DMA_CHANNEL) | STM32_DMA_CR_DIR_M2P | WS2812_PWM_DMA_PERIPHERAL_WIDTH | WS2812_PWM_DMA_MEMORY_WIDTH | STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3));\n#endif\n    dmaStreamSetTransactionSize(WS2812_PWM_DMA_STREAM, WS2812_BIT_N);\n    // M2P: Memory 2 Periph; PL: Priority Level\n\n#if (STM32_DMA_SUPPORTS_DMAMUX == TRUE)\n    // If the MCU has a DMAMUX we need to assign the correct resource\n    dmaSetRequestSource(WS2812_PWM_DMA_STREAM, WS2812_PWM_DMAMUX_ID);\n#endif\n\n#if (AT32_DMA_SUPPORTS_DMAMUX == TRUE)\n    // If the MCU has a DMAMUX we need to assign the correct resource\n    dmaSetRequestSource(WS2812_PWM_DMA_STREAM, WS2812_PWM_DMAMUX_CHANNEL, WS2812_PWM_DMAMUX_ID);\n#endif\n\n    // Start DMA\n    dmaStreamEnable(WS2812_PWM_DMA_STREAM);\n\n    // Configure PWM\n    // NOTE: It's required that preload be enabled on the timer channel CCR register. This is currently enabled in the\n    // ChibiOS driver code, so we don't have to do anything special to the timer. If we did, we'd have to start the timer,\n    // disable counting, enable the channel, and then make whatever configuration changes we need.\n    pwmStart(&WS2812_PWM_DRIVER, &ws2812_pwm_config);\n    pwmEnableChannel(&WS2812_PWM_DRIVER, WS2812_PWM_CHANNEL - 1, 0); // Initial period is 0; output will be low until first duty cycle is DMA'd in\n}\n\nvoid ws2812_write_led(uint16_t led_number, uint8_t r, uint8_t g, uint8_t b) {\n    // Write color to frame buffer\n    for (uint8_t bit = 0; bit < 8; bit++) {\n        ws2812_frame_buffer[WS2812_RED_BIT(led_number, bit)]   = ((r >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;\n        ws2812_frame_buffer[WS2812_GREEN_BIT(led_number, bit)] = ((g >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;\n        ws2812_frame_buffer[WS2812_BLUE_BIT(led_number, bit)]  = ((b >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;\n    }\n}\nvoid ws2812_write_led_rgbw(uint16_t led_number, uint8_t r, uint8_t g, uint8_t b, uint8_t w) {\n    // Write color to frame buffer\n    for (uint8_t bit = 0; bit < 8; bit++) {\n        ws2812_frame_buffer[WS2812_RED_BIT(led_number, bit)]   = ((r >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;\n        ws2812_frame_buffer[WS2812_GREEN_BIT(led_number, bit)] = ((g >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;\n        ws2812_frame_buffer[WS2812_BLUE_BIT(led_number, bit)]  = ((b >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;\n#ifdef WS2812_RGBW\n        ws2812_frame_buffer[WS2812_WHITE_BIT(led_number, bit)] = ((w >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0;\n#endif\n    }\n}\n\nws2812_led_t ws2812_leds[WS2812_LED_COUNT];\n\nvoid ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    ws2812_leds[index].r = red;\n    ws2812_leds[index].g = green;\n    ws2812_leds[index].b = blue;\n#if defined(WS2812_RGBW)\n    ws2812_rgb_to_rgbw(&ws2812_leds[index]);\n#endif\n}\n\nvoid ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < WS2812_LED_COUNT; i++) {\n        ws2812_set_color(i, red, green, blue);\n    }\n}\n\nvoid ws2812_flush(void) {\n    for (int i = 0; i < WS2812_LED_COUNT; i++) {\n#if defined(WS2812_RGBW)\n        ws2812_write_led_rgbw(i, ws2812_leds[i].r, ws2812_leds[i].g, ws2812_leds[i].b, ws2812_leds[i].w);\n#else\n        ws2812_write_led(i, ws2812_leds[i].r, ws2812_leds[i].g, ws2812_leds[i].b);\n#endif\n    }\n}\n"
  },
  {
    "path": "platforms/chibios/drivers/ws2812_spi.c",
    "content": "#include \"ws2812.h\"\n#include \"gpio.h\"\n#include \"util.h\"\n#include \"chibios_config.h\"\n\n/* Adapted from https://github.com/gamazeps/ws2812b-chibios-SPIDMA/ */\n\n// Define the spi your LEDs are plugged to here\n#ifndef WS2812_SPI_DRIVER\n#    define WS2812_SPI_DRIVER SPID1\n#endif\n\n#ifndef WS2812_SPI_MOSI_PAL_MODE\n#    define WS2812_SPI_MOSI_PAL_MODE 5\n#endif\n\n#ifndef WS2812_SPI_SCK_PAL_MODE\n#    define WS2812_SPI_SCK_PAL_MODE 5\n#endif\n\n#ifndef WS2812_SPI_DIVISOR\n#    define WS2812_SPI_DIVISOR 16\n#endif\n\n// Push Pull or Open Drain Configuration\n// Default Push Pull\n#ifndef WS2812_EXTERNAL_PULLUP\n#    if defined(USE_GPIOV1)\n#        define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#    else\n#        define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL\n#    endif\n#else\n#    if defined(USE_GPIOV1)\n#        define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE_OPENDRAIN\n#    else\n#        define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN\n#    endif\n#endif\n\n// Define SPI config speed\n// baudrate should target 3.2MHz\n#if defined(AT32F415)\n#    if WS2812_SPI_DIVISOR == 2\n#        define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (0)\n#    elif WS2812_SPI_DIVISOR == 4\n#        define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_0)\n#    elif WS2812_SPI_DIVISOR == 8\n#        define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_1)\n#    elif WS2812_SPI_DIVISOR == 16 // default\n#        define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_1 | SPI_CTRL1_MDIV_0)\n#    elif WS2812_SPI_DIVISOR == 32\n#        define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2)\n#    elif WS2812_SPI_DIVISOR == 64\n#        define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_0)\n#    elif WS2812_SPI_DIVISOR == 128\n#        define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_1)\n#    elif WS2812_SPI_DIVISOR == 256\n#        define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_2 | SPI_CTRL1_MDIV_1 | SPI_CTRL1_MDIV_0)\n#    elif WS2812_SPI_DIVISOR == 512\n#        define WS2812_SPI_DIVISOR_CTRL2_MDIV_X (SPI_CTRL1_MDIV_3)\n#    elif WS2812_SPI_DIVISOR == 1024\n#        define WS2812_SPI_DIVISOR_CTRL2_MDIV_X (SPI_CTRL1_MDIV_3)\n#        define WS2812_SPI_DIVISOR_CTRL1_MDIV_X (SPI_CTRL1_MDIV_0)\n#    else\n#        error \"Configured WS2812_SPI_DIVISOR value is not supported at this time.\"\n#    endif\n#else\n// F072 fpclk = 48MHz\n// 48/16 = 3Mhz\n#    if WS2812_SPI_DIVISOR == 2\n#        define WS2812_SPI_DIVISOR_CR1_BR_X (0)\n#    elif WS2812_SPI_DIVISOR == 4\n#        define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_0)\n#    elif WS2812_SPI_DIVISOR == 8\n#        define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1)\n#    elif WS2812_SPI_DIVISOR == 16 // default\n#        define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1 | SPI_CR1_BR_0)\n#    elif WS2812_SPI_DIVISOR == 32\n#        define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2)\n#    elif WS2812_SPI_DIVISOR == 64\n#        define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_0)\n#    elif WS2812_SPI_DIVISOR == 128\n#        define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1)\n#    elif WS2812_SPI_DIVISOR == 256\n#        define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0)\n#    else\n#        error \"Configured WS2812_SPI_DIVISOR value is not supported at this time.\"\n#    endif\n#endif\n\n// Use SPI circular buffer\n#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER\n#    define WS2812_SPI_BUFFER_MODE 1 // circular buffer\n#else\n#    define WS2812_SPI_BUFFER_MODE 0 // normal buffer\n#endif\n\n#if defined(USE_GPIOV1)\n#    define WS2812_SCK_OUTPUT_MODE PAL_MODE_ALTERNATE_PUSHPULL\n#else\n#    define WS2812_SCK_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_SCK_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL\n#endif\n\n#define BYTES_FOR_LED_BYTE 4\n#ifdef WS2812_RGBW\n#    define WS2812_CHANNELS 4\n#else\n#    define WS2812_CHANNELS 3\n#endif\n#define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * WS2812_CHANNELS)\n#define DATA_SIZE (BYTES_FOR_LED * WS2812_LED_COUNT)\n#define RESET_SIZE (1000 * WS2812_TRST_US / (2 * WS2812_TIMING))\n#define PREAMBLE_SIZE 4\n\nstatic uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE] = {0};\n\n/*\n * As the trick here is to use the SPI to send a huge pattern of 0 and 1 to\n * the ws2812b protocol, we use this helper function to translate bytes into\n * 0s and 1s for the LED (with the appropriate timing).\n */\nstatic uint8_t get_protocol_eq(uint8_t data, int pos) {\n    uint8_t eq = 0;\n    if (data & (1 << (2 * (3 - pos))))\n        eq = 0b1110;\n    else\n        eq = 0b1000;\n    if (data & (2 << (2 * (3 - pos))))\n        eq += 0b11100000;\n    else\n        eq += 0b10000000;\n    return eq;\n}\n\nstatic void set_led_color_rgb(ws2812_led_t color, int pos) {\n    uint8_t* tx_start = &txbuf[PREAMBLE_SIZE];\n\n#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB)\n    for (int j = 0; j < 4; j++)\n        tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.g, j);\n    for (int j = 0; j < 4; j++)\n        tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.r, j);\n    for (int j = 0; j < 4; j++)\n        tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.b, j);\n#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB)\n    for (int j = 0; j < 4; j++)\n        tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.r, j);\n    for (int j = 0; j < 4; j++)\n        tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.g, j);\n    for (int j = 0; j < 4; j++)\n        tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.b, j);\n#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR)\n    for (int j = 0; j < 4; j++)\n        tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.b, j);\n    for (int j = 0; j < 4; j++)\n        tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.g, j);\n    for (int j = 0; j < 4; j++)\n        tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.r, j);\n#endif\n#ifdef WS2812_RGBW\n    for (int j = 0; j < 4; j++)\n        tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 3 + j] = get_protocol_eq(color.w, j);\n#endif\n}\n\nws2812_led_t ws2812_leds[WS2812_LED_COUNT];\n\nvoid ws2812_init(void) {\n    palSetLineMode(WS2812_DI_PIN, WS2812_MOSI_OUTPUT_MODE);\n\n#ifdef WS2812_SPI_SCK_PIN\n    palSetLineMode(WS2812_SPI_SCK_PIN, WS2812_SCK_OUTPUT_MODE);\n#endif // WS2812_SPI_SCK_PIN\n\n    // TODO: more dynamic baudrate\n    static const SPIConfig spicfg = {\n#ifndef HAL_LLD_SELECT_SPI_V2\n// HAL_SPI_V1\n#    if SPI_SUPPORTS_CIRCULAR == TRUE\n        WS2812_SPI_BUFFER_MODE,\n#    endif\n        NULL, // end_cb\n        PAL_PORT(WS2812_DI_PIN),\n        PAL_PAD(WS2812_DI_PIN),\n#    if defined(WB32F3G71xx) || defined(WB32FQ95xx)\n        0,\n        0,\n        WS2812_SPI_DIVISOR\n#    else\n        WS2812_SPI_DIVISOR_CR1_BR_X,\n        0\n#    endif\n#else\n    // HAL_SPI_V2\n#    if SPI_SUPPORTS_CIRCULAR == TRUE\n        WS2812_SPI_BUFFER_MODE,\n#    endif\n#    if SPI_SUPPORTS_SLAVE_MODE == TRUE\n        false,\n#    endif\n        NULL, // data_cb\n        NULL, // error_cb\n        PAL_PORT(WS2812_DI_PIN),\n        PAL_PAD(WS2812_DI_PIN),\n#    if defined(AT32F415)\n        WS2812_SPI_DIVISOR_CTRL1_MDIV_X,\n#        if (WS2812_SPI_DIVISOR == 512 || WS2812_SPI_DIVISOR == 1024)\n        WS2812_SPI_DIVISOR_CTRL2_MDIV_X,\n#        endif\n        0\n#    else\n        WS2812_SPI_DIVISOR_CR1_BR_X,\n        0\n#    endif\n#endif\n    };\n\n    spiAcquireBus(&WS2812_SPI_DRIVER);     /* Acquire ownership of the bus.    */\n    spiStart(&WS2812_SPI_DRIVER, &spicfg); /* Setup transfer parameters.       */\n    spiSelect(&WS2812_SPI_DRIVER);         /* Slave Select assertion.          */\n#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER\n    spiStartSend(&WS2812_SPI_DRIVER, ARRAY_SIZE(txbuf), txbuf);\n#endif\n}\n\nvoid ws2812_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    ws2812_leds[index].r = red;\n    ws2812_leds[index].g = green;\n    ws2812_leds[index].b = blue;\n#if defined(WS2812_RGBW)\n    ws2812_rgb_to_rgbw(&ws2812_leds[index]);\n#endif\n}\n\nvoid ws2812_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n    for (int i = 0; i < WS2812_LED_COUNT; i++) {\n        ws2812_set_color(i, red, green, blue);\n    }\n}\n\nvoid ws2812_flush(void) {\n    for (int i = 0; i < WS2812_LED_COUNT; i++) {\n        set_led_color_rgb(ws2812_leds[i], i);\n    }\n\n    // Send async - each led takes ~0.03ms, 50 leds ~1.5ms, animations flushing faster than send will cause issues.\n    // Instead spiSend can be used to send synchronously (or the thread logic can be added back).\n#ifndef WS2812_SPI_USE_CIRCULAR_BUFFER\n#    ifdef WS2812_SPI_SYNC\n    spiSend(&WS2812_SPI_DRIVER, ARRAY_SIZE(txbuf), txbuf);\n#    else\n    spiStartSend(&WS2812_SPI_DRIVER, ARRAY_SIZE(txbuf), txbuf);\n#    endif\n#endif\n}\n"
  },
  {
    "path": "platforms/chibios/errno.h",
    "content": "// Copyright 2025 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n#include_next <errno.h>\n\n// Newer versions of picolibc don't seem to provide `__errno_r(r)` in the header file, but is used by ChibiOS.\n#ifndef __errno_r\n#    ifdef __REENT_ERRNO\n#        define __errno_r(r) _REENT_ERRNO(r)\n#    else\n#        define __errno_r(r) (errno)\n#    endif\n#endif\n"
  },
  {
    "path": "platforms/chibios/flash.mk",
    "content": "# Hey Emacs, this is a -*- makefile -*-\n##############################################################################\n# Architecture or project specific options\n#\n\nDFU_ARGS ?=\nifneq (\"$(SERIAL)\",\"\")\n\tDFU_ARGS += -S $(SERIAL)\nendif\n\nDFU_UTIL ?= dfu-util\n\ndefine EXEC_DFU_UTIL\n\tif ! $(DFU_UTIL) -l | grep -q \"Found DFU\"; then \\\n\t\tprintf \"$(MSG_BOOTLOADER_NOT_FOUND_QUICK_RETRY)\" ;\\\n\t\tsleep $(BOOTLOADER_RETRY_TIME) ;\\\n\t\twhile ! $(DFU_UTIL) -l | grep -q \"Found DFU\"; do \\\n\t\t\tprintf \".\" ;\\\n\t\t\tsleep $(BOOTLOADER_RETRY_TIME) ;\\\n\t\tdone ;\\\n\t\tprintf \"\\n\" ;\\\n\tfi\n\t$(DFU_UTIL) $(DFU_ARGS) -D $(BUILD_DIR)/$(TARGET).bin\nendef\n\nWB32_DFU_UPDATER ?= wb32-dfu-updater_cli\n\ndefine EXEC_WB32_DFU_UPDATER\n\tif ! wb32-dfu-updater_cli -l | grep -q \"Found DFU\"; then \\\n\t\tprintf \"$(MSG_BOOTLOADER_NOT_FOUND_QUICK_RETRY)\" ;\\\n\t\tsleep $(BOOTLOADER_RETRY_TIME) ;\\\n\t\twhile ! wb32-dfu-updater_cli -l | grep -q \"Found DFU\"; do \\\n\t\t\tprintf \".\" ;\\\n\t\t\tsleep $(BOOTLOADER_RETRY_TIME) ;\\\n\t\tdone ;\\\n\t\tprintf \"\\n\" ;\\\n\tfi\n\t$(WB32_DFU_UPDATER) -D $(BUILD_DIR)/$(TARGET).bin && $(WB32_DFU_UPDATER) -R\nendef\n\ndfu-util: $(BUILD_DIR)/$(TARGET).bin cpfirmware sizeafter\n\t$(call EXEC_DFU_UTIL)\n\ndefine EXEC_UF2_UTIL_DEPLOY\n\t$(UF2CONV) --wait --deploy $(BUILD_DIR)/$(TARGET).uf2\nendef\n\n# TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS\n#       within the emulated eeprom via dfu-util or another tool\nifneq (,$(filter $(MAKECMDGOALS), dfu-util-split-left uf2-split-left))\n    OPT_DEFS += -DINIT_EE_HANDS_LEFT\nendif\n\nifneq (,$(filter $(MAKECMDGOALS), dfu-util-split-right uf2-split-right))\n    OPT_DEFS += -DINIT_EE_HANDS_RIGHT\nendif\n\ndfu-util-split-left: dfu-util\n\ndfu-util-split-right: dfu-util\n\nuf2-split-left: flash\n\nuf2-split-right: flash\n\nST_LINK_CLI ?= st-link_cli\nST_LINK_ARGS ?=\n\nst-link-cli: $(BUILD_DIR)/$(TARGET).hex sizeafter\n\t$(ST_LINK_CLI) $(ST_LINK_ARGS) -q -c SWD -p $(BUILD_DIR)/$(TARGET).hex -Rst\n\nST_FLASH ?= st-flash\nST_FLASH_ARGS ?=\n\nst-flash: $(BUILD_DIR)/$(TARGET).hex sizeafter\n\t$(ST_FLASH) $(ST_FLASH_ARGS) --reset --format ihex write $(BUILD_DIR)/$(TARGET).hex\n\n# Autodetect teensy loader\nifndef TEENSY_LOADER_CLI\n    ifneq (, $(shell which teensy-loader-cli 2>/dev/null))\n        TEENSY_LOADER_CLI ?= teensy-loader-cli\n    else\n        TEENSY_LOADER_CLI ?= teensy_loader_cli\n    endif\nendif\n\nTEENSY_LOADER_CLI_MCU ?= $(MCU_LDSCRIPT)\n\ndefine EXEC_TEENSY\n\t$(TEENSY_LOADER_CLI) -mmcu=$(TEENSY_LOADER_CLI_MCU) -w -v $(BUILD_DIR)/$(TARGET).hex\nendef\n\nteensy: $(BUILD_DIR)/$(TARGET).hex cpfirmware sizeafter\n\t$(call EXEC_TEENSY)\n\nflash: $(BUILD_DIR)/$(TARGET).bin cpfirmware sizeafter\n\t$(SILENT) || printf \"Flashing for bootloader: $(BLUE)$(BOOTLOADER)$(NO_COLOR)\\n\"\nifneq ($(strip $(PROGRAM_CMD)),)\n\t$(UNSYNC_OUTPUT_CMD) && $(PROGRAM_CMD)\nelse ifeq ($(strip $(BOOTLOADER)),kiibohd)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_DFU_UTIL)\nelse ifeq ($(strip $(BOOTLOADER)),tinyuf2)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_UF2_UTIL_DEPLOY)\nelse ifeq ($(strip $(BOOTLOADER)),uf2boot)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_UF2_UTIL_DEPLOY)\nelse ifeq ($(strip $(BOOTLOADER)),rp2040)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_UF2_UTIL_DEPLOY)\nelse ifeq ($(strip $(MCU_FAMILY)),KINETIS)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_TEENSY)\nelse ifeq ($(strip $(MCU_FAMILY)),MIMXRT1062)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_TEENSY)\nelse ifeq ($(strip $(MCU_FAMILY)),STM32)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_DFU_UTIL)\nelse ifeq ($(strip $(MCU_FAMILY)),WB32)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_WB32_DFU_UPDATER)\nelse ifeq ($(strip $(MCU_FAMILY)),AT32)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_DFU_UTIL)\nelse ifeq ($(strip $(MCU_FAMILY)),GD32V)\n\t$(UNSYNC_OUTPUT_CMD) && $(call EXEC_DFU_UTIL)\nelse\n\t$(PRINT_OK); $(SILENT) || printf \"$(MSG_FLASH_BOOTLOADER)\"\nendif\n"
  },
  {
    "path": "platforms/chibios/gd32v_compatibility.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n/* GD32VF103 has the same API as STM32F103, but uses different names for literally the same thing.\n * As of 23.7.2021 QMK is tailored to use STM32 defines/names, for compatibility sake\n * we just redefine the GD32 names. */\n\n/* Close your eyes kids. */\n#define MCU_STM32\n\n/* AFIO redefines */\n#define MAPR PCF0\n#define AFIO_MAPR_USART1_REMAP AFIO_PCF0_USART0_REMAP\n#define AFIO_MAPR_USART2_REMAP AFIO_PCF0_USART1_REMAP\n#define AFIO_MAPR_USART3_REMAP_PARTIALREMAP AFIO_PCF0_USART2_REMAP_PARTIALREMAP\n#define AFIO_MAPR_USART3_REMAP_FULLREMAP AFIO_PCF0_USART2_REMAP_FULLREMAP\n\n/* DMA redefines. */\n#define STM32_DMA_STREAM(stream) GD32_DMA_STREAM(stream)\n#define STM32_DMA_STREAM_ID(peripheral, channel) GD32_DMA_STREAM_ID(peripheral - 1, channel - 1)\n#define STM32_DMA_CR_DIR_M2P GD32_DMA_CTL_DIR_M2P\n#define STM32_DMA_CR_PSIZE_WORD GD32_DMA_CTL_PWIDTH_WORD\n#define STM32_DMA_CR_PSIZE_HWORD GD32_DMA_CTL_PWIDTH_HWORD\n#define STM32_DMA_CR_MSIZE_WORD GD32_DMA_CTL_MWIDTH_WORD\n#define STM32_DMA_CR_MSIZE_BYTE GD32_DMA_CTL_MWIDTH_BYTE\n#define STM32_DMA_CR_MINC GD32_DMA_CTL_MNAGA\n#define STM32_DMA_CR_CIRC GD32_DMA_CTL_CMEN\n#define STM32_DMA_CR_PL GD32_DMA_CTL_PRIO\n#define STM32_DMA_CR_CHSEL GD32_DMA_CTL_CHSEL\n#define cr1 ctl0\n#define cr2 ctl1\n#define cr3 ctl2\n#define dier dmainten\n\n/* ADC redefines */\n#if HAL_USE_ADC\n#    define STM32_ADC_USE_ADC1 GD32_ADC_USE_ADC0\n\n#    define smpr1 sampt0\n#    define smpr2 sampt1\n#    define sqr1 rsq0\n#    define sqr2 rsq1\n#    define sqr3 rsq2\n\n#    define ADC_SMPR2_SMP_AN0 ADC_SAMPT1_SMP_SPT0\n#    define ADC_SMPR2_SMP_AN1 ADC_SAMPT1_SMP_SPT1\n#    define ADC_SMPR2_SMP_AN2 ADC_SAMPT1_SMP_SPT2\n#    define ADC_SMPR2_SMP_AN3 ADC_SAMPT1_SMP_SPT3\n#    define ADC_SMPR2_SMP_AN4 ADC_SAMPT1_SMP_SPT4\n#    define ADC_SMPR2_SMP_AN5 ADC_SAMPT1_SMP_SPT5\n#    define ADC_SMPR2_SMP_AN6 ADC_SAMPT1_SMP_SPT6\n#    define ADC_SMPR2_SMP_AN7 ADC_SAMPT1_SMP_SPT7\n#    define ADC_SMPR2_SMP_AN8 ADC_SAMPT1_SMP_SPT8\n#    define ADC_SMPR2_SMP_AN9 ADC_SAMPT1_SMP_SPT9\n\n#    define ADC_SMPR1_SMP_AN10 ADC_SAMPT0_SMP_SPT10\n#    define ADC_SMPR1_SMP_AN11 ADC_SAMPT0_SMP_SPT11\n#    define ADC_SMPR1_SMP_AN12 ADC_SAMPT0_SMP_SPT12\n#    define ADC_SMPR1_SMP_AN13 ADC_SAMPT0_SMP_SPT13\n#    define ADC_SMPR1_SMP_AN14 ADC_SAMPT0_SMP_SPT14\n#    define ADC_SMPR1_SMP_AN15 ADC_SAMPT0_SMP_SPT15\n\n#    define ADC_SQR3_SQ1_N ADC_RSQ2_RSQ1_N\n#endif\n\n/* FLASH redefines */\n#if defined(EEPROM_ENABLE)\n#    define SR STAT\n#    define FLASH_SR_BSY FLASH_STAT_BUSY\n#    define FLASH_SR_PGERR FLASH_STAT_PGERR\n#    define FLASH_SR_EOP FLASH_STAT_ENDF\n#    define FLASH_SR_WRPRTERR FLASH_STAT_WPERR\n#    define FLASH_SR_WRPERR FLASH_SR_WRPRTERR\n#    define FLASH_OBR_OPTERR FLASH_OBSTAT_OBERR\n#    define AR ADDR\n#    define CR CTL\n#    define FLASH_CR_PER FLASH_CTL_PER\n#    define FLASH_CR_STRT FLASH_CTL_START\n#    define FLASH_CR_LOCK FLASH_CTL_LK\n#    define FLASH_CR_PG FLASH_CTL_PG\n#    define KEYR KEY\n#endif\n\n/* Serial USART redefines. */\n#if HAL_USE_SERIAL\n#    if !defined(SERIAL_USART_CR1)\n#        define SERIAL_USART_CR1 (USART_CTL0_PCEN | USART_CTL0_PM | USART_CTL0_WL) // parity enable, odd parity, 9 bit length\n#    endif\n#    if !defined(SERIAL_USART_CR2)\n#        define SERIAL_USART_CR2 (USART_CTL1_STB_1) // 2 stop bits\n#    endif\n#    if !defined(SERIAL_USART_CR3)\n#        define SERIAL_USART_CR3 0x0\n#    endif\n#    define USART_CR3_HDSEL USART_CTL2_HDEN\n#    define CCR CHCV\n#endif\n\n/* SPI redefines. */\n#if HAL_USE_SPI\n#    define SPI_CR1_LSBFIRST SPI_CTL0_LF\n#    define SPI_CR1_CPHA SPI_CTL0_CKPH\n#    define SPI_CR1_CPOL SPI_CTL0_CKPL\n#    define SPI_CR1_BR_0 SPI_CTL0_PSC_0\n#    define SPI_CR1_BR_1 SPI_CTL0_PSC_1\n#    define SPI_CR1_BR_2 SPI_CTL0_PSC_2\n#endif\n"
  },
  {
    "path": "platforms/chibios/gpio.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <hal.h>\n#include \"pin_defs.h\"\n\ntypedef ioline_t pin_t;\n\n/* Operation of GPIO by pin. */\n\n#define gpio_set_pin_input(pin) palSetLineMode((pin), PAL_MODE_INPUT)\n#define gpio_set_pin_input_high(pin) palSetLineMode((pin), PAL_MODE_INPUT_PULLUP)\n#define gpio_set_pin_input_low(pin) palSetLineMode((pin), PAL_MODE_INPUT_PULLDOWN)\n#define gpio_set_pin_output_push_pull(pin) palSetLineMode((pin), PAL_MODE_OUTPUT_PUSHPULL)\n#define gpio_set_pin_output_open_drain(pin) palSetLineMode((pin), PAL_MODE_OUTPUT_OPENDRAIN)\n#define gpio_set_pin_output(pin) gpio_set_pin_output_push_pull(pin)\n\n#define gpio_write_pin_high(pin) palSetLine(pin)\n#define gpio_write_pin_low(pin) palClearLine(pin)\n#define gpio_write_pin(pin, level)    \\\n    do {                              \\\n        if (level) {                  \\\n            gpio_write_pin_high(pin); \\\n        } else {                      \\\n            gpio_write_pin_low(pin);  \\\n        }                             \\\n    } while (0)\n\n#define gpio_read_pin(pin) palReadLine(pin)\n\n#define gpio_toggle_pin(pin) palToggleLine(pin)\n"
  },
  {
    "path": "platforms/chibios/hardware_id.c",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <ch.h>\n#include \"hardware_id.h\"\n\n__attribute__((weak)) hardware_id_t get_hardware_id(void) {\n    hardware_id_t id = {0};\n#if defined(RP2040)\n    // Forward declare as including \"hardware/flash.h\" here causes more issues...\n    void flash_get_unique_id(uint8_t *);\n\n    flash_get_unique_id((uint8_t *)&id);\n#elif defined(UID_BASE)\n    id.data[0] = (uint32_t)(*((uint32_t *)UID_BASE));\n    id.data[1] = (uint32_t)(*((uint32_t *)(UID_BASE + 4)));\n    id.data[2] = (uint32_t)(*((uint32_t *)(UID_BASE + 8)));\n#endif\n    return id;\n}\n"
  },
  {
    "path": "platforms/chibios/interrupt_handlers.c",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n///////////////////////////////////////////////////////////////////////////////\n// BEGIN: STM32 EFL Wear-leveling ECC fault handling\n//\n// Some STM32s have ECC checks for all flash memory access. Whenever there's an\n// ECC failure, the MCU raises the NMI interrupt. Whenever we receive such an\n// interrupt whilst reading the wear-leveling EEPROM area, we gracefully cater\n// for it, signalling the wear-leveling code that a failure has occurred.\n///////////////////////////////////////////////////////////////////////////////\n\n#include <ch.h>\n#include <chcore.h>\n\n#ifdef WEAR_LEVELING_EMBEDDED_FLASH\n#    ifdef QMK_MCU_SERIES_STM32L4XX\n#        define ECC_ERRORS_TRIGGER_NMI_INTERRUPT\n#        define ECC_CHECK_REGISTER FLASH->ECCR\n#        define ECC_CHECK_FLAG FLASH_ECCR_ECCD\n#    endif // QMK_MCU_SERIES_STM32L4XX\n#endif     // WEAR_LEVELING_EMBEDDED_FLASH\n\n#ifdef ECC_ERRORS_TRIGGER_NMI_INTERRUPT\n\nextern bool backing_store_allow_ecc_errors(void);\nextern void backing_store_signal_ecc_error(void);\n\nvoid NMI_Handler(void) {\n    if ((ECC_CHECK_REGISTER) & (ECC_CHECK_FLAG)) {\n        if (backing_store_allow_ecc_errors()) {\n            (ECC_CHECK_REGISTER) = (ECC_CHECK_FLAG);\n            backing_store_signal_ecc_error();\n            return;\n        }\n    }\n\n    chSysHalt(\"NMI\");\n}\n\n#endif // ECC_ERRORS_TRIGGER_NMI_INTERRUPT\n\n///////////////////////////////////////////////////////////////////////////////\n// END: STM32 EFL Wear-leveling ECC fault handling\n///////////////////////////////////////////////////////////////////////////////\n"
  },
  {
    "path": "platforms/chibios/mcu_selection.mk",
    "content": "ifneq ($(findstring MKL26Z64, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m0plus\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 6\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = KINETIS\n  MCU_SERIES = KL2x\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= MKL26Z64\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/ports/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= kl2x\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= PJRC_TEENSY_LC\nendif\n\nifneq ($(findstring MK20DX128, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = KINETIS\n  MCU_SERIES = K20x\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= MK20DX128\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/ports/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= k20x5\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= PJRC_TEENSY_3\nendif\n\nifneq ($(findstring MK20DX256, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = KINETIS\n  MCU_SERIES = K20x\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= MK20DX256\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/ports/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= k20x7\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= PJRC_TEENSY_3_1\nendif\n\nifneq ($(findstring MK64FX512, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = KINETIS\n  MCU_SERIES = K60x\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= MK64FX512\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= k60x\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= PJRC_TEENSY_3_5\nendif\n\nifneq ($(findstring MK66FX1M0, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = KINETIS\n  MCU_SERIES = MK66F18\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= MK66FX1M0\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= MK66F18\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= PJRC_TEENSY_3_6\nendif\n\nifneq ($(findstring RP2040, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m0plus\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  CHIBIOS_PORT = ARMv6-M-RP2\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = RP\n  MCU_SERIES = RP2040\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  STARTUPLD_CONTRIB = $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/ld\n  MCU_LDSCRIPT ?= RP2040_FLASH_TIMECRIT\n  LDFLAGS += -L $(STARTUPLD_CONTRIB)\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= rp2040\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_PROMICRO_RP2040\n\n  # Default UF2 Bootloader settings\n  UF2_FAMILY ?= RP2040\n  FIRMWARE_FORMAT ?= uf2\nendif\n\nifneq ($(findstring STM32F042, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m0\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 6\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32F0xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32F042x6\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32f0xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_F042X6\n\n  USE_FPU ?= no\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32F0\n\n  # Stack sizes: Since this chip has limited RAM capacity, the stack area needs to be reduced.\n  # This ensures that the EEPROM page buffer fits into RAM\n  USE_PROCESS_STACKSIZE = 0x600\n  USE_EXCEPTIONS_STACKSIZE = 0x300\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFFC400\nendif\n\nifneq ($(findstring STM32F072, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m0\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 6\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32F0xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32F072xB\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32f0xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_F072XB\n\n  USE_FPU ?= no\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32F0\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFFC800\nendif\n\nifneq ($(findstring STM32F103, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m3\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32F1xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32F103x8\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32f1xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_F103\n\n  USE_FPU ?= no\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32F1\nendif\n\nifneq ($(findstring STM32F303, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32F3xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32F303xC\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32f3xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_F303XC\n\n  USE_FPU ?= yes\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32F3\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFFD800\nendif\n\nifneq ($(findstring STM32F401, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32F4xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32F401xC\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32f4xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_F401XC\n\n  USE_FPU ?= yes\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32F4\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFF0000\nendif\n\nifneq ($(findstring STM32F405, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32F4xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32F405xG\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32f4xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_F405XG\n\n  USE_FPU ?= yes\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32F4\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFF0000\nendif\n\nifneq ($(findstring STM32F407, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32F4xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32F407xE\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32f4xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_F407XE\n\n  USE_FPU ?= yes\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32F4\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFF0000\nendif\n\nifneq ($(findstring STM32F411, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32F4xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32F411xE\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32f4xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_F411XE\n\n  USE_FPU ?= yes\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32F4\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFF0000\nendif\n\nifneq ($(findstring STM32F446, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32F4xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32F446xE\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32f4xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_F446XE\n\n  USE_FPU ?= yes\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFF0000\n\n  # Default as no chibios efl config\n  EEPROM_DRIVER ?= transient\nendif\n\nifneq ($(findstring STM32G0B1, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m0plus\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 6\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32G0xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32G0B1xB\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32g0xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_G0B1XB\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32G0\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFF0000\nendif\n\nifneq ($(findstring STM32G431, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32G4xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32G431xB\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32g4xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_G431XB\n\n  USE_FPU ?= yes\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32G4\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFF0000\nendif\n\nifneq ($(findstring STM32G474, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32G4xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32G474xE\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32g4xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_G474XE\n\n  USE_FPU ?= yes\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32G4\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFF0000\nendif\n\nifneq (,$(filter $(MCU),STM32L432 STM32L442))\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32L4xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32L432xC\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32l4xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_L432XC\n\n  PLATFORM_NAME ?= platform_l432\n\n  USE_FPU ?= yes\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32L4\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFF0000\nendif\n\nifneq (,$(filter $(MCU),STM32L433 STM32L443))\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32L4xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32L432xC\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32l4xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_L433XC\n\n  PLATFORM_NAME ?= platform_l432\n\n  USE_FPU ?= yes\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32L4\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFF0000\nendif\n\nifneq (,$(filter $(MCU),STM32L412 STM32L422))\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32L4xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32L412xB\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32l4xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_L412XB\n\n  PLATFORM_NAME ?= platform_l412_l422\n\n  USE_FPU ?= yes\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32L4\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FFF0000\nendif\n\nifneq (,$(filter $(MCU),STM32H723 STM32H733))\n  # Cortex version\n  MCU = cortex-m7\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = STM32\n  MCU_SERIES = STM32H7xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= STM32H723xG_ITCM64k\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= stm32h7xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_STM32_H723XG\n\n  PLATFORM_NAME ?= platform_type2\n\n  USE_FPU ?= yes\n\n  # UF2 settings\n  UF2_FAMILY ?= STM32H7\n\n  # Bootloader address for STM32 DFU\n  STM32_BOOTLOADER_ADDRESS ?= 0x1FF09800\nendif\n\nifneq ($(findstring WB32F3G71, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m3\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = WB32\n  MCU_SERIES = WB32F3G71xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= WB32F3G71x9\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= wb32f3g71xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_WB32_F3G71XX\n\n  USE_FPU ?= no\n\n  # Bootloader address for WB32 DFU\n  WB32_BOOTLOADER_ADDRESS ?= 0x1FFFE000\nendif\n\nifneq ($(findstring WB32FQ95, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m3\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = WB32\n  MCU_SERIES = WB32FQ95xx\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/ports/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= WB32FQ95xB\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= wb32fq95xx\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_WB32_FQ95XX\n\n  USE_FPU ?= no\n\n  # Bootloader address for WB32 DFU\n  WB32_BOOTLOADER_ADDRESS ?= 0x1FFFE000\nendif\n\nifneq ($(findstring AT32F415, $(MCU)),)\n  # Cortex version\n  MCU = cortex-m4\n\n  # ARM version, CORTEX-M0/M1 are 6, CORTEX-M3/M4/M7 are 7\n  ARMV = 7\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_FAMILY = AT32\n  MCU_SERIES = AT32F415\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/ARMCMx/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= AT32F415xB\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/ARMCMx/compilers/GCC/mk/\n  MCU_STARTUP ?= at32f415\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= GENERIC_AT32_F415XX\n\n  USE_FPU ?= no\n\n  # Bootloader address for AT32 DFU\n  AT32_BOOTLOADER_ADDRESS ?= 0x1FFFAC00\nendif\n\nifneq ($(findstring GD32VF103, $(MCU)),)\n  # RISC-V\n  MCU = risc-v\n\n  # RISC-V extensions and abi configuration\n  MCU_ARCH = rv32imac\n  MCU_ABI = ilp32\n  MCU_CMODEL = medlow\n\n  ## chip/board settings\n  # - the next two should match the directories in\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)\n  #   OR\n  #   <chibios[-contrib]>/os/hal/ports/$(MCU_FAMILY)/$(MCU_SERIES)\n  MCU_PORT_NAME = GD\n  MCU_FAMILY = GD32V\n  MCU_SERIES = GD32VF103\n\n  # Linker script to use\n  # - it should exist either in <chibios>/os/common/startup/RISCV-ECLIC/compilers/GCC/ld/\n  #   or <keyboard_dir>/ld/\n  MCU_LDSCRIPT ?= GD32VF103xB\n\n  # Startup code to use\n  #  - it should exist in <chibios>/os/common/startup/RISCV-ECLIC/compilers/GCC/mk/\n  MCU_STARTUP ?= gd32vf103\n\n  # Board: it should exist either in <chibios>/os/hal/boards/,\n  # <keyboard_dir>/boards/, or drivers/boards/\n  BOARD ?= SIPEED_LONGAN_NANO\n\n  USE_FPU ?= no\nendif\n"
  },
  {
    "path": "platforms/chibios/platform.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"platform_deps.h\"\n\nvoid platform_setup(void) {\n    halInit();\n    chSysInit();\n}\n"
  },
  {
    "path": "platforms/chibios/platform.mk",
    "content": "# Hey Emacs, this is a -*- makefile -*-\n##############################################################################\n# Architecture or project specific options\n#\n\n# Stack size to be allocated to the Cortex-M process stack. This stack is\n# the stack used by the main() thread.\nifeq ($(USE_PROCESS_STACKSIZE),)\n  USE_PROCESS_STACKSIZE = 0x800\nendif\n\n# Stack size to the allocated to the Cortex-M main/exceptions stack. This\n# stack is used for processing interrupts and exceptions.\nifeq ($(USE_EXCEPTIONS_STACKSIZE),)\n  USE_EXCEPTIONS_STACKSIZE = 0x400\nendif\n\n#\n# Architecture or project specific options\n##############################################################################\n\n##############################################################################\n# Project, sources and paths\n#\n\n# Imported source files and paths\nOPT_OS = chibios\nCHIBIOS = $(TOP_DIR)/lib/chibios\nCHIBIOS_CONTRIB = $(TOP_DIR)/lib/chibios-contrib\n\n#\n# Startup, Port and Platform support selection\n##############################################################################\n\nifeq ($(strip $(MCU)), risc-v)\n    # RISC-V Support\n    # As of 7.4.2021 there is only one supported RISC-V platform in Chibios-Contrib,\n    # therefore all required settings are hard-coded\n    USE_CHIBIOS_CONTRIB = yes\n    STARTUP_MK = $(CHIBIOS_CONTRIB)/os/common/startup/RISCV-ECLIC/compilers/GCC/mk/startup_$(MCU_STARTUP).mk\n    PORT_V = $(CHIBIOS_CONTRIB)/os/common/ports/RISCV-ECLIC/compilers/GCC/mk/port.mk\n    RULESPATH = $(CHIBIOS_CONTRIB)/os/common/startup/RISCV-ECLIC/compilers/GCC\nelse\n    # ARM Support\n    CHIBIOS_PORT ?=\n    ifeq (\"$(CHIBIOS_PORT)\",\"\")\n        CHIBIOS_PORT = ARMv$(ARMV)-M\n    endif\n\n    # Startup files. Try a few different locations, for compability with old versions and\n    # for things hardware in the contrib repository\n    STARTUP_MK = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/startup_$(MCU_STARTUP).mk\n    ifeq (\"$(wildcard $(STARTUP_MK))\",\"\")\n        STARTUP_MK = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_$(MCU_STARTUP).mk\n        ifeq (\"$(wildcard $(STARTUP_MK))\",\"\")\n            STARTUP_MK = $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_$(MCU_STARTUP).mk\n        endif\n    endif\n\n    # Port files. Try a few different locations, for compability with old versions and\n    # for things hardware in the contrib repository\n    PORT_V = $(CHIBIOS)/os/common/ports/$(CHIBIOS_PORT)/compilers/GCC/mk/port.mk\n    ifeq (\"$(wildcard $(PORT_V))\",\"\")\n        PORT_V = $(CHIBIOS)/os/rt/ports/ARMCMx/compilers/GCC/mk/port_v$(ARMV)m.mk\n        ifeq (\"$(wildcard $(PORT_V))\",\"\")\n            PORT_V = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC/mk/port_v$(ARMV)m.mk\n        endif\n    endif\n\n    # Rules location. Try a few different locations, for compability with old versions and\n    # for things hardware in the contrib repository\n    RULESPATH = $(CHIBIOS)/os/common/ports/$(CHIBIOS_PORT)/compilers/GCC\n    ifeq (\"$(wildcard $(RULESPATH)/rules.mk)\",\"\")\n        RULESPATH = $(CHIBIOS)/os/common/ports/ARMCMx/compilers/GCC\n        ifeq (\"$(wildcard $(RULESPATH)/rules.mk)\",\"\")\n            RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC\n        endif\n    endif\nendif\n\nifeq (\"$(PLATFORM_NAME)\",\"\")\n    PLATFORM_NAME = platform\nendif\n\n# If no MCU port name was specified, use the family instead\nifeq (\"$(MCU_PORT_NAME)\",\"\")\n    MCU_PORT_NAME = $(MCU_FAMILY)\nendif\n\nifeq (\"$(wildcard $(PLATFORM_MK))\",\"\")\n    PLATFORM_MK = $(CHIBIOS_CONTRIB)/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)/$(PLATFORM_NAME).mk\n    ifeq (\"$(wildcard $(PLATFORM_MK))\",\"\")\n        PLATFORM_MK = $(CHIBIOS)/os/hal/ports/$(MCU_PORT_NAME)/$(MCU_SERIES)/$(PLATFORM_NAME).mk\n    endif\nendif\n\n# If no MCU architecture specified, use the MCU instead (allows for mcu_selection.mk to swap to cortex-m0 etc.)\nifeq (\"$(MCU_ARCH)\",\"\")\n    MCU_ARCH = $(MCU)\nendif\n\ninclude $(STARTUP_MK)\ninclude $(PORT_V)\ninclude $(PLATFORM_MK)\n\n#\n# Board support selection.\n##############################################################################\n\nBOARD_MK :=\n\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/boards/$(BOARD)/board.mk)\",\"\")\n    BOARD_PATH = $(KEYBOARD_PATH_5)\n    BOARD_MK += $(KEYBOARD_PATH_5)/boards/$(BOARD)/board.mk\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_4)/boards/$(BOARD)/board.mk)\",\"\")\n    BOARD_PATH = $(KEYBOARD_PATH_4)\n    BOARD_MK += $(KEYBOARD_PATH_4)/boards/$(BOARD)/board.mk\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_3)/boards/$(BOARD)/board.mk)\",\"\")\n    BOARD_PATH = $(KEYBOARD_PATH_3)\n    BOARD_MK += $(KEYBOARD_PATH_3)/boards/$(BOARD)/board.mk\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_2)/boards/$(BOARD)/board.mk)\",\"\")\n    BOARD_PATH = $(KEYBOARD_PATH_2)\n    BOARD_MK += $(KEYBOARD_PATH_2)/boards/$(BOARD)/board.mk\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_1)/boards/$(BOARD)/board.mk)\",\"\")\n    BOARD_PATH = $(KEYBOARD_PATH_1)\n    BOARD_MK += $(KEYBOARD_PATH_1)/boards/$(BOARD)/board.mk\nelse ifneq (\"$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/board/board.mk)\",\"\")\n    BOARD_PATH = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)\n    BOARD_MK += $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/board/board.mk\n    KEYBOARD_PATHS += $(BOARD_PATH)/configs\n    ifneq (\"$(wildcard $(BOARD_PATH)/rules.mk)\",\"\")\n        include $(BOARD_PATH)/rules.mk\n    endif\nendif\n\nifeq (\"$(wildcard $(BOARD_MK))\",\"\")\n    BOARD_MK = $(CHIBIOS)/os/hal/boards/$(BOARD)/board.mk\n    ifeq (\"$(wildcard $(BOARD_MK))\",\"\")\n        BOARD_MK = $(CHIBIOS_CONTRIB)/os/hal/boards/$(BOARD)/board.mk\n    endif\nendif\n\ninclude $(BOARD_MK)\n\n#\n# Bootloader selection.\n##############################################################################\n\n# Set bootloader address if supplied.\nifdef STM32_BOOTLOADER_ADDRESS\n    OPT_DEFS += -DSTM32_BOOTLOADER_ADDRESS=$(STM32_BOOTLOADER_ADDRESS)\nendif\n\nifdef WB32_BOOTLOADER_ADDRESS\n    OPT_DEFS += -DWB32_BOOTLOADER_ADDRESS=$(WB32_BOOTLOADER_ADDRESS)\nendif\n\nifdef AT32_BOOTLOADER_ADDRESS\n    OPT_DEFS += -DAT32_BOOTLOADER_ADDRESS=$(AT32_BOOTLOADER_ADDRESS)\nendif\n\n# Work out if we need to set up the include for the bootloader definitions\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/bootloader_defs.h)\",\"\")\n    OPT_DEFS += -include $(KEYBOARD_PATH_5)/bootloader_defs.h\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_5)/boards/$(BOARD)/bootloader_defs.h)\",\"\")\n    OPT_DEFS += -include $(KEYBOARD_PATH_5)/boards/$(BOARD)/bootloader_defs.h\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_4)/bootloader_defs.h)\",\"\")\n    OPT_DEFS += -include $(KEYBOARD_PATH_4)/bootloader_defs.h\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_4)/boards/$(BOARD)/bootloader_defs.h)\",\"\")\n    OPT_DEFS += -include $(KEYBOARD_PATH_4)/boards/$(BOARD)/bootloader_defs.h\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_3)/bootloader_defs.h)\",\"\")\n    OPT_DEFS += -include $(KEYBOARD_PATH_3)/bootloader_defs.h\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_3)/boards/$(BOARD)/bootloader_defs.h)\",\"\")\n    OPT_DEFS += -include $(KEYBOARD_PATH_3)/boards/$(BOARD)/bootloader_defs.h\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_2)/bootloader_defs.h)\",\"\")\n    OPT_DEFS += -include $(KEYBOARD_PATH_2)/bootloader_defs.h\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_2)/boards/$(BOARD)/bootloader_defs.h)\",\"\")\n    OPT_DEFS += -include $(KEYBOARD_PATH_2)/boards/$(BOARD)/bootloader_defs.h\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_1)/bootloader_defs.h)\",\"\")\n    OPT_DEFS += -include $(KEYBOARD_PATH_1)/bootloader_defs.h\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_1)/boards/$(BOARD)/bootloader_defs.h)\",\"\")\n    OPT_DEFS += -include $(KEYBOARD_PATH_1)/boards/$(BOARD)/bootloader_defs.h\nelse ifneq (\"$(wildcard $(BOARD_PATH)/configs/bootloader_defs.h)\",\"\")\n    OPT_DEFS += -include $(BOARD_PATH)/configs/bootloader_defs.h\nendif\n\n#\n# ChibiOS config selection.\n##############################################################################\n\n# Work out the config file directories\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/chconf.h)\",\"\")\n    CHCONFDIR = $(KEYBOARD_PATH_5)\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_4)/chconf.h)\",\"\")\n    CHCONFDIR = $(KEYBOARD_PATH_4)\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_3)/chconf.h)\",\"\")\n    CHCONFDIR = $(KEYBOARD_PATH_3)\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_2)/chconf.h)\",\"\")\n    CHCONFDIR = $(KEYBOARD_PATH_2)\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_1)/chconf.h)\",\"\")\n    CHCONFDIR = $(KEYBOARD_PATH_1)\nelse ifneq (\"$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/chconf.h)\",\"\")\n    CHCONFDIR = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs\nelse ifneq (\"$(wildcard $(TOP_DIR)/platforms/boards/chibios/common/configs/chconf.h)\",\"\")\n    CHCONFDIR = $(TOP_DIR)/platforms/chibios/boards/common/configs\nendif\n\n#\n# HAL config selection.\n##############################################################################\n\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/halconf.h)\",\"\")\n    HALCONFDIR = $(KEYBOARD_PATH_5)\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_4)/halconf.h)\",\"\")\n    HALCONFDIR = $(KEYBOARD_PATH_4)\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_3)/halconf.h)\",\"\")\n    HALCONFDIR = $(KEYBOARD_PATH_3)\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_2)/halconf.h)\",\"\")\n    HALCONFDIR = $(KEYBOARD_PATH_2)\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_1)/halconf.h)\",\"\")\n    HALCONFDIR = $(KEYBOARD_PATH_1)\nelse ifneq (\"$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/halconf.h)\",\"\")\n    HALCONFDIR = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs\nelse ifneq (\"$(wildcard $(TOP_DIR)/platforms/chibios/boards/common/configs/halconf.h)\",\"\")\n    HALCONFDIR = $(TOP_DIR)/platforms/chibios/boards/common/configs\nendif\n\n#\n# Linker script selection.\n##############################################################################\n\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/ld/$(MCU_LDSCRIPT).ld)\",\"\")\n    LDSCRIPT = $(KEYBOARD_PATH_5)/ld/$(MCU_LDSCRIPT).ld\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_4)/ld/$(MCU_LDSCRIPT).ld)\",\"\")\n    LDSCRIPT = $(KEYBOARD_PATH_4)/ld/$(MCU_LDSCRIPT).ld\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_3)/ld/$(MCU_LDSCRIPT).ld)\",\"\")\n    LDSCRIPT = $(KEYBOARD_PATH_3)/ld/$(MCU_LDSCRIPT).ld\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld)\",\"\")\n    LDSCRIPT = $(KEYBOARD_PATH_2)/ld/$(MCU_LDSCRIPT).ld\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld)\",\"\")\n    LDSCRIPT = $(KEYBOARD_PATH_1)/ld/$(MCU_LDSCRIPT).ld\nelse ifneq (\"$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld/$(MCU_LDSCRIPT)_$(BOOTLOADER).ld)\",\"\")\n    LDFLAGS += -L$(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld\n    LDSCRIPT = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld/$(MCU_LDSCRIPT)_$(BOOTLOADER).ld\nelse ifneq (\"$(wildcard $(TOP_DIR)/platforms/chibios/boards/common/ld/$(MCU_LDSCRIPT)_$(BOOTLOADER).ld)\",\"\")\n    LDSCRIPT = $(TOP_DIR)/platforms/chibios/boards/common/ld/$(MCU_LDSCRIPT)_$(BOOTLOADER).ld\nelse ifneq (\"$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld/$(MCU_LDSCRIPT).ld)\",\"\")\n    LDFLAGS += -L$(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld\n    LDSCRIPT = $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/ld/$(MCU_LDSCRIPT).ld\nelse ifneq (\"$(wildcard $(TOP_DIR)/platforms/chibios/boards/common/ld/$(MCU_LDSCRIPT).ld)\",\"\")\n    LDSCRIPT = $(TOP_DIR)/platforms/chibios/boards/common/ld/$(MCU_LDSCRIPT).ld\nelse ifneq (\"$(wildcard $(STARTUPLD_CONTRIB)/$(MCU_LDSCRIPT).ld)\",\"\")\n    LDSCRIPT = $(STARTUPLD_CONTRIB)/$(MCU_LDSCRIPT).ld\n    USE_CHIBIOS_CONTRIB = yes\nelse\n    LDSCRIPT = $(STARTUPLD)/$(MCU_LDSCRIPT).ld\nendif\n\n#\n# Include ChibiOS makefiles.\n##############################################################################\n\n# HAL-OSAL files (optional).\ninclude $(CHIBIOS)/os/hal/hal.mk\ninclude $(CHIBIOS)/os/oslib/oslib.mk\ninclude $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk\n# RTOS files (optional).\ninclude $(CHIBIOS)/os/rt/rt.mk\n# Other files (optional).\ninclude $(CHIBIOS)/os/hal/lib/streams/streams.mk\n\nPLATFORM_SRC = \\\n        $(STARTUPSRC) \\\n        $(KERNSRC) \\\n        $(PORTSRC) \\\n        $(OSALSRC) \\\n        $(OSLIBSRC) \\\n        $(HALSRC) \\\n        $(PLATFORMSRC) \\\n        $(BOARDSRC) \\\n        $(STREAMSSRC) \\\n        $(CHIBIOS)/os/various/syscalls.c \\\n        $(PLATFORM_COMMON_DIR)/syscall-fallbacks.c \\\n        $(PLATFORM_COMMON_DIR)/wait.c \\\n        $(PLATFORM_COMMON_DIR)/synchronization_util.c \\\n        $(PLATFORM_COMMON_DIR)/interrupt_handlers.c\n\n# Ensure the ASM files are not subjected to LTO -- it'll strip out interrupt handlers otherwise.\nQUANTUM_LIB_SRC += $(STARTUPASM) $(PORTASM) $(OSALASM) $(PLATFORMASM)\n\nPLATFORM_SRC := $(patsubst $(TOP_DIR)/%,%,$(PLATFORM_SRC))\n\nEXTRAINCDIRS += $(CHIBIOS)/os/license \\\n         $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs \\\n         $(TOP_DIR)/platforms/chibios/boards/common/configs \\\n         $(HALCONFDIR) $(CHCONFDIR) \\\n         $(STARTUPINC) $(KERNINC) $(PORTINC) $(OSALINC) $(OSLIBINC) \\\n         $(HALINC) $(PLATFORMINC) $(BOARDINC) $(TESTINC) \\\n         $(STREAMSINC) $(CHIBIOS)/os/various $(COMMON_VPATH)\n\n#\n# QMK specific MCU family support selection.\n##############################################################################\nifneq (\"$(wildcard $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_SERIES).mk)\",\"\")\n    # Either by MCU series e.g. STM32/STM32F1xx.mk or...\n    include $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_SERIES).mk\nelse ifneq (\"$(wildcard $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_FAMILY).mk)\",\"\")\n    # By MCU family e.g. STM32/STM32.mk\n    include $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)/$(MCU_FAMILY).mk\nendif\n\n#\n# ChibiOS-Contrib\n##############################################################################\n\n# Work out if we're using ChibiOS-Contrib by checking if halconf_community.h exists\nifneq (\"$(wildcard $(KEYBOARD_PATH_5)/halconf_community.h)\",\"\")\n    USE_CHIBIOS_CONTRIB = yes\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_4)/halconf_community.h)\",\"\")\n    USE_CHIBIOS_CONTRIB = yes\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_3)/halconf_community.h)\",\"\")\n    USE_CHIBIOS_CONTRIB = yes\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_2)/halconf_community.h)\",\"\")\n    USE_CHIBIOS_CONTRIB = yes\nelse ifneq (\"$(wildcard $(KEYBOARD_PATH_1)/halconf_community.h)\",\"\")\n    USE_CHIBIOS_CONTRIB = yes\nelse ifneq (\"$(wildcard $(TOP_DIR)/platforms/chibios/boards/$(BOARD)/configs/halconf_community.h)\",\"\")\n    USE_CHIBIOS_CONTRIB = yes\nendif\n\nifeq ($(strip $(USE_CHIBIOS_CONTRIB)),yes)\n    include $(CHIBIOS_CONTRIB)/os/hal/hal.mk\n    PLATFORM_SRC += $(PLATFORMSRC_CONTRIB) $(HALSRC_CONTRIB)\n    EXTRAINCDIRS += $(PLATFORMINC_CONTRIB) $(HALINC_CONTRIB) $(CHIBIOS_CONTRIB)/os/various\nendif\n\n#\n# Project, sources and paths\n##############################################################################\n\n##############################################################################\n# Injected configs\n#\nifneq (\"$(wildcard $(BOARD_PATH)/configs/config.h)\",\"\")\n    CONFIG_H += $(BOARD_PATH)/configs/config.h\nendif\nifneq (\"$(wildcard $(BOARD_PATH)/configs/post_config.h)\",\"\")\n    POST_CONFIG_H += $(BOARD_PATH)/configs/post_config.h\nendif\n\n##############################################################################\n# Compiler and Linker configuration\n#\n\n# Use defined stack sizes of the main thread in linker scripts\nSHARED_LDSYMBOLS = -Wl,--defsym=__process_stack_size__=$(USE_PROCESS_STACKSIZE),--defsym=__main_stack_size__=$(USE_EXCEPTIONS_STACKSIZE)\n\n# Shared Compiler flags for all toolchains\nSHARED_CFLAGS = -fomit-frame-pointer \\\n                -ffunction-sections \\\n                -fdata-sections \\\n                -fno-common \\\n                -fshort-wchar \\\n                -fno-builtin-printf\n\nLDSCRIPT_PATH := $(shell dirname \"$(LDSCRIPT)\")\n\n# Shared Linker flags for all toolchains\nSHARED_LDFLAGS = -T $(LDSCRIPT) \\\n                 -L $(LDSCRIPT_PATH) \\\n                 -Wl,--gc-sections \\\n                 -nostartfiles\n\nifeq ($(strip $(MCU)), risc-v)\n    # RISC-V toolchain specific configuration\n    # Find suitable GCC compiler\n    ifeq ($(strip $(TOOLCHAIN)),)\n        ifneq ($(shell which riscv32-unknown-elf-gcc 2>/dev/null),)\n            TOOLCHAIN = riscv32-unknown-elf-\n        else\n            ifneq ($(shell which riscv64-unknown-elf-gcc 2>/dev/null),)\n                TOOLCHAIN = riscv64-unknown-elf-\n            else\n                $(call CATASTROPHIC_ERROR,Missing toolchain,No RISC-V toolchain found. Can't find riscv32-unknown-elf-gcc or riscv64-unknown-elf-gcc found in your systems PATH variable. Please install a valid toolchain and make it accessible!)\n            endif\n        endif\n    endif\n\n    # Default to compiling with picolibc for RISC-V targets if available, which\n    # is available by default on distributions based on Debian 11+.\n    ifeq ($(shell $(TOOLCHAIN)gcc --specs=picolibc.specs -E - 2>/dev/null >/dev/null </dev/null ; echo $$?),0)\n        # Toolchain specific Compiler flags Note that we still link with our own\n        # linker script by providing it via the -T flag in SHARED_LDFLAGS.\n        TOOLCHAIN_CFLAGS = --specs=picolibc.specs\n\n        # picolibc internally uses __heap_start and __heap_end instead of the\n        # defacto chibios linker script standard __heap_base__ and __heap_end__\n        # therefore we introduce these symbols as an alias.\n        TOOLCHAIN_LDSYMBOLS = -Wl,--defsym=__heap_start=__heap_base__,--defsym=__heap_end=__heap_end__\n\n        # Tell QMK that we are compiling with picolibc.\n        OPT_DEFS += -DUSE_PICOLIBC\n    endif\n\n    # MCU architecture flags\n    MCUFLAGS = -march=$(MCU_ARCH) \\\n               -mabi=$(MCU_ABI) \\\n               -mcmodel=$(MCU_CMODEL) \\\n               -mstrict-align\n\n    # Deal with different arch revisions and gcc renaming them\n    ifneq ($(shell echo 'int main() { asm(\"csrc 0x300,8\"); return 0; }' | $(TOOLCHAIN)gcc $(MCUFLAGS) $(TOOLCHAIN_CFLAGS) -x c -o /dev/null - 2>/dev/null >/dev/null; echo $$?),0)\n        MCUFLAGS = -march=$(MCU_ARCH)_zicsr \\\n                   -mabi=$(MCU_ABI) \\\n                   -mcmodel=$(MCU_CMODEL) \\\n                   -mstrict-align\n        ifneq ($(shell echo 'int main() { asm(\"csrc 0x300,8\"); return 0; }' | $(TOOLCHAIN)gcc $(MCUFLAGS) $(TOOLCHAIN_CFLAGS) -x c -o /dev/null - 2>/dev/null >/dev/null; echo $$?),0)\n            $(call CATASTROPHIC_ERROR,Incompatible toolchain,No compatible RISC-V toolchain found. Can't work out correct architecture.)\n        endif\n    endif\nelse\n    # ARM toolchain specific configuration\n    TOOLCHAIN ?= arm-none-eabi-\n\n    # Toolchain specific Linker flags\n    TOOLCHAIN_LDFLAGS = -Wl,--no-wchar-size-warning \\\n                        --specs=nano.specs\n\n    # MCU architecture flags\n    MCUFLAGS = -mcpu=$(MCU) \\\n               -mthumb -DTHUMB_PRESENT \\\n               -mno-thumb-interwork -DTHUMB_NO_INTERWORKING \\\n               -mno-unaligned-access\n\n    # Some ARM cores like the M4 and M7 have floating point units which can be enabled\n    USE_FPU ?= no\n\n    ifneq ($(USE_FPU),no)\n        OPT_DEFS += -DCORTEX_USE_FPU=TRUE\n\n        # Default is single precision floats\n        USE_FPU_OPT ?= -mfloat-abi=hard \\\n                       -mfpu=fpv4-sp-d16 \\\n                       -fsingle-precision-constant\n\n        MCUFLAGS += $(USE_FPU_OPT)\n    else\n        OPT_DEFS += -DCORTEX_USE_FPU=FALSE\n    endif\nendif\n\n# Extra config.h files for the platform\nifneq (\"$(wildcard $(PLATFORM_COMMON_DIR)/vendors/$(MCU_FAMILY)/$(MCU_SERIES)/config.h)\",\"\")\n    CONFIG_H += $(PLATFORM_COMMON_DIR)/vendors/$(MCU_FAMILY)/$(MCU_SERIES)/config.h\nendif\nifneq (\"$(wildcard $(PLATFORM_COMMON_DIR)/vendors/$(MCU_FAMILY)/config.h)\",\"\")\n    CONFIG_H += $(PLATFORM_COMMON_DIR)/vendors/$(MCU_FAMILY)/config.h\nendif\nCONFIG_H += $(PLATFORM_COMMON_DIR)/config.h\n\n# Assembler flags\nASFLAGS  += $(SHARED_ASFLAGS) $(TOOLCHAIN_ASFLAGS)\n\n# C Compiler flags\nCFLAGS   += $(SHARED_CFLAGS) $(TOOLCHAIN_CFLAGS)\n\n# C++ Compiler flags\nCXXFLAGS += $(CFLAGS) $(SHARED_CXXFLAGS) $(TOOLCHAIN_CXXFLAGS) -fno-rtti\n\n# Linker flags\nLDFLAGS  += $(SHARED_LDFLAGS) $(SHARED_LDSYMBOLS) $(TOOLCHAIN_LDFLAGS) $(TOOLCHAIN_LDSYMBOLS) $(MCUFLAGS)\n\n# Tell QMK that we are hosting it on ChibiOS.\nOPT_DEFS += -DPROTOCOL_CHIBIOS\n\n# And what flavor of MCU\nOPT_DEFS += -DMCU_$(MCU_FAMILY)\n\n# ChibiOS supports synchronization primitives like a Mutex\nOPT_DEFS += -DPLATFORM_SUPPORTS_SYNCHRONIZATION\n\n# Workaround to stop ChibiOS from complaining about new GCC -- it's been fixed for 7/8/9 already\nOPT_DEFS += -DPORT_IGNORE_GCC_VERSION_CHECK=1\n\n# Construct GCC toolchain\nCC      = $(CC_PREFIX) $(TOOLCHAIN)gcc\nOBJCOPY = $(TOOLCHAIN)objcopy\nOBJDUMP = $(TOOLCHAIN)objdump\nSIZE    = $(TOOLCHAIN)size\nAR      = $(TOOLCHAIN)ar\nNM      = $(TOOLCHAIN)nm\nHEX     = $(OBJCOPY) -O $(FORMAT)\nEEP     =\nBIN     = $(OBJCOPY) -O binary\n\n# disable warning about RWX triggered by ChibiOS linker scripts\nifeq (\"$(shell echo \"int main(){}\" | $(CC) -shared -Wl,--no-warn-rwx-segments -x c - -o /dev/null 2>&1)\", \"\")\n\tSHARED_LDFLAGS += -Wl,--no-warn-rwx-segments\nendif\n\n##############################################################################\n# Make targets\n#\n\nDEBUG = gdb\n\n# List any extra directories to look for libraries here.\nEXTRALIBDIRS = $(RULESPATH)/ld\n\nbin: $(BUILD_DIR)/$(TARGET).bin sizeafter\n\t$(COPY) $(BUILD_DIR)/$(TARGET).bin $(TARGET).bin;\n"
  },
  {
    "path": "platforms/chibios/platform_deps.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <hal.h>\n#include \"chibios_config.h\"\n"
  },
  {
    "path": "platforms/chibios/sleep_led.c",
    "content": "#include <ch.h>\n#include <hal.h>\n\n#include \"led.h\"\n#include \"sleep_led.h\"\n\n/* All right, we go the \"software\" way: timer, toggle LED in interrupt.\n * Based on hasu's code for AVRs.\n * Use LP timer on Kinetises, TIM14 on STM32F0.\n */\n\n#ifndef SLEEP_LED_GPT_DRIVER\n#    if defined(STM32F0XX)\n#        define SLEEP_LED_GPT_DRIVER GPTD14\n#    endif\n#endif\n\n#if defined(KL2x) || defined(K20x) || defined(SLEEP_LED_GPT_DRIVER) /* common parts for timers/interrupts */\n\n/* Breathing Sleep LED brighness(PWM On period) table\n * (64[steps] * 4[duration]) / 64[PWM periods/s] = 4 second breath cycle\n *\n * http://www.wolframalpha.com/input/?i=%28sin%28+x%2F64*pi%29**8+*+255%2C+x%3D0+to+63\n * (0..63).each {|x| p ((sin(x/64.0*PI)**8)*255).to_i }\n */\nstatic const uint8_t breathing_table[64] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 10, 15, 23, 32, 44, 58, 74, 93, 113, 135, 157, 179, 199, 218, 233, 245, 252, 255, 252, 245, 233, 218, 199, 179, 157, 135, 113, 93, 74, 58, 44, 32, 23, 15, 10, 6, 4, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\n\nvoid sleep_led_timer_callback(void) {\n    /* Software PWM\n     * timer:1111 1111 1111 1111\n     *       \\_____/\\/ \\_______/____  count(0-255)\n     *          \\    \\______________  duration of step(4)\n     *           \\__________________  index of step table(0-63)\n     */\n\n    // this works for cca 65536 irqs/sec\n    static union {\n        uint16_t row;\n        struct {\n            uint8_t count : 8;\n            uint8_t duration : 2;\n            uint8_t index : 6;\n        } pwm;\n    } timer                = {.row = 0};\n    static led_t led_state = {0};\n\n    timer.row++;\n\n    // LED on\n    if (timer.pwm.count == 0) {\n        led_state.caps_lock = true;\n        led_set(led_state.raw);\n    }\n    // LED off\n    if (timer.pwm.count == breathing_table[timer.pwm.index]) {\n        led_state.caps_lock = false;\n        led_set(led_state.raw);\n    }\n}\n\n#endif /* common parts for known platforms */\n\n#if defined(KL2x) || defined(K20x) /* platform selection: familiar Kinetis chips */\n\n/* Use Low Power Timer (LPTMR) */\n#    define TIMER_INTERRUPT_VECTOR KINETIS_LPTMR0_IRQ_VECTOR\n#    define RESET_COUNTER LPTMR0->CSR |= LPTMRx_CSR_TCF\n\n/* LPTMR clock options */\n#    define LPTMR_CLOCK_MCGIRCLK 0 /* 4MHz clock */\n#    define LPTMR_CLOCK_LPO 1      /* 1kHz clock */\n#    define LPTMR_CLOCK_ERCLK32K 2 /* external 32kHz crystal */\n#    define LPTMR_CLOCK_OSCERCLK 3 /* output from OSC */\n\n/* Work around inconsistencies in Freescale naming */\n#    if !defined(SIM_SCGC5_LPTMR)\n#        define SIM_SCGC5_LPTMR SIM_SCGC5_LPTIMER\n#    endif\n\n/* interrupt handler */\nOSAL_IRQ_HANDLER(TIMER_INTERRUPT_VECTOR) {\n    OSAL_IRQ_PROLOGUE();\n\n    sleep_led_timer_callback();\n\n    /* Reset the counter */\n    RESET_COUNTER;\n\n    OSAL_IRQ_EPILOGUE();\n}\n\n/* Initialise the timer */\nvoid sleep_led_init(void) {\n    /* Make sure the clock to the LPTMR is enabled */\n    SIM->SCGC5 |= SIM_SCGC5_LPTMR;\n    /* Reset LPTMR settings */\n    LPTMR0->CSR = 0;\n    /* Set the compare value */\n    LPTMR0->CMR = 0; // trigger on counter value (i.e. every time)\n\n/* Set up clock source and prescaler */\n/* Software PWM\n *  ______           ______           __\n * |  ON  |___OFF___|  ON  |___OFF___|   ....\n * |<-------------->|<-------------->|<- ....\n *     PWM period       PWM period\n *\n * R                interrupts/period[resolution]\n * F                periods/second[frequency]\n * R * F            interrupts/second\n */\n\n/* === OPTION 1 === */\n#    if 0\n    //  1kHz LPO\n    //  No prescaler => 1024 irqs/sec\n    //  Note: this is too slow for a smooth breathe\n    LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_LPO)|LPTMRx_PSR_PBYP;\n#    endif /* OPTION 1 */\n\n/* === OPTION 2 === */\n#    if 1\n    //  nMHz IRC (n=4 on KL25Z, KL26Z and K20x; n=2 or 8 on KL27Z)\n    MCG->C2 |= MCG_C2_IRCS; // fast (4MHz) internal ref clock\n#        if defined(KL27)   // divide the 8MHz IRC by 2, to have the same MCGIRCLK speed as others\n    MCG->MC |= MCG_MC_LIRC_DIV2_DIV2;\n#        endif                 /* KL27 */\n    MCG->C1 |= MCG_C1_IRCLKEN; // enable internal ref clock\n    //  to work in stop mode, also MCG_C1_IREFSTEN\n    //  Divide 4MHz by 2^N (N=6) => 62500 irqs/sec =>\n    //  => approx F=61, R=256, duration = 4\n    LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_MCGIRCLK) | LPTMRx_PSR_PRESCALE(6);\n#    endif /* OPTION 2 */\n\n/* === OPTION 3 === */\n#    if 0\n    //  OSC output (external crystal), usually 8MHz or 16MHz\n    OSC0->CR |= OSC_CR_ERCLKEN; // enable ext ref clock\n    //  to work in stop mode, also OSC_CR_EREFSTEN\n    //  Divide by 2^N\n    LPTMR0->PSR = LPTMRx_PSR_PCS(LPTMR_CLOCK_OSCERCLK)|LPTMRx_PSR_PRESCALE(7);\n#    endif /* OPTION 3 */\n    /* === END OPTIONS === */\n\n    /* Interrupt on TCF set (compare flag) */\n    nvicEnableVector(LPTMR0_IRQn, 2); // vector, priority\n    LPTMR0->CSR |= LPTMRx_CSR_TIE;\n}\n\nvoid sleep_led_enable(void) {\n    /* Enable the timer */\n    LPTMR0->CSR |= LPTMRx_CSR_TEN;\n}\n\nvoid sleep_led_disable(void) {\n    /* Disable the timer */\n    LPTMR0->CSR &= ~LPTMRx_CSR_TEN;\n}\n\nvoid sleep_led_toggle(void) {\n    /* Toggle the timer */\n    LPTMR0->CSR ^= LPTMRx_CSR_TEN;\n}\n\n#elif defined(SLEEP_LED_GPT_DRIVER)\n\nstatic void gptTimerCallback(GPTDriver *gptp) {\n    (void)gptp;\n    sleep_led_timer_callback();\n}\n\nstatic const GPTConfig gptcfg = {1000000, gptTimerCallback, 0, 0};\n\n/* Initialise the timer */\nvoid sleep_led_init(void) {\n    gptStart(&SLEEP_LED_GPT_DRIVER, &gptcfg);\n}\n\nvoid sleep_led_enable(void) {\n    gptStartContinuous(&SLEEP_LED_GPT_DRIVER, gptcfg.frequency / 0xFFFF);\n}\n\nvoid sleep_led_disable(void) {\n    gptStopTimer(&SLEEP_LED_GPT_DRIVER);\n}\n\nvoid sleep_led_toggle(void) {\n    (SLEEP_LED_GPT_DRIVER.state == GPT_READY) ? sleep_led_enable() : sleep_led_disable();\n}\n\n#else /* platform selection: not on familiar chips */\n\nvoid sleep_led_init(void) {}\n\nvoid sleep_led_enable(void) {\n    led_set(2); // Caps Lock\n}\n\nvoid sleep_led_disable(void) {\n    led_set(0);\n}\n\nvoid sleep_led_toggle(void) {\n    // not implemented\n}\n\n#endif /* platform selection */\n"
  },
  {
    "path": "platforms/chibios/suspend.c",
    "content": "/* TODO */\n\n#include <ch.h>\n#include <hal.h>\n\n#include \"matrix.h\"\n#include \"action.h\"\n#include \"action_util.h\"\n#include \"mousekey.h\"\n#include \"programmable_button.h\"\n#include \"host.h\"\n#include \"suspend.h\"\n#include \"led.h\"\n#include \"wait.h\"\n\n/** \\brief suspend power down\n *\n * FIXME: needs doc\n */\nvoid suspend_power_down(void) {\n    suspend_power_down_quantum();\n    // on AVR, this enables the watchdog for 15ms (max), and goes to\n    // SLEEP_MODE_PWR_DOWN\n\n    wait_ms(17);\n}\n\n/** \\brief suspend wakeup condition\n *\n * run immediately after wakeup\n * FIXME: needs doc\n */\nvoid suspend_wakeup_init(void) {\n    // clear keyboard state\n    // need to do it manually, because we're running from ISR\n    //  and clear_keyboard() calls print\n    // so only clear the variables in memory\n    // the reports will be sent from main.c afterwards\n    // or if the PC asks for GET_REPORT\n    clear_mods();\n    clear_weak_mods();\n    clear_keys();\n#ifdef MOUSEKEY_ENABLE\n    mousekey_clear();\n#endif /* MOUSEKEY_ENABLE */\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\n    programmable_button_clear();\n#endif /* PROGRAMMABLE_BUTTON_ENABLE */\n#ifdef EXTRAKEY_ENABLE\n    host_system_send(0);\n    host_consumer_send(0);\n#endif /* EXTRAKEY_ENABLE */\n\n    suspend_wakeup_init_quantum();\n}\n"
  },
  {
    "path": "platforms/chibios/synchronization_util.c",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"synchronization_util.h\"\n#include \"ch.h\"\n\n#if defined(SPLIT_KEYBOARD)\nstatic MUTEX_DECL(SPLIT_SHARED_MEMORY_MUTEX);\n\n/**\n * @brief Acquire exclusive access to the split keyboard shared memory, by\n * locking the mutex guarding it. If the mutex is already held, the calling\n * thread will be suspended until the mutex currently owning thread releases the\n * mutex again.\n */\nvoid split_shared_memory_lock(void) {\n    chMtxLock(&SPLIT_SHARED_MEMORY_MUTEX);\n}\n\n/**\n * @brief Release the split shared memory mutex that has been acquired before.\n */\nvoid split_shared_memory_unlock(void) {\n    chMtxUnlock(&SPLIT_SHARED_MEMORY_MUTEX);\n}\n#endif\n"
  },
  {
    "path": "platforms/chibios/syscall-fallbacks.c",
    "content": "/* Copyright 2021 Nick Brassel, QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <errno.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n/* To compile the ChibiOS syscall stubs with picolibc\n * the _reent struct has to be defined. */\n#if defined(USE_PICOLIBC)\nstruct _reent;\nstruct timeval;\n#endif\n\n#pragma GCC diagnostic ignored \"-Wmissing-prototypes\"\n\n__attribute__((weak, used)) int _open_r(struct _reent *r, const char *path, int flag, int m) {\n    __errno_r(r) = ENOENT;\n    return -1;\n}\n\n__attribute__((weak, used)) int _lseek_r(struct _reent *r, int file, int ptr, int dir) {\n    __errno_r(r) = EBADF;\n    return -1;\n}\n\n__attribute__((weak, used)) int _read_r(struct _reent *r, int file, char *ptr, int len) {\n    __errno_r(r) = EBADF;\n    return -1;\n}\n\n__attribute__((weak, used)) int _write_r(struct _reent *r, int file, char *ptr, int len) {\n    __errno_r(r) = EBADF;\n    return -1;\n}\n\n__attribute__((weak, used)) int _close_r(struct _reent *r, int file) {\n    __errno_r(r) = EBADF;\n    return -1;\n}\n\n__attribute__((weak, used)) int _link_r(struct _reent *r, const char *oldpath, const char *newpath) {\n    __errno_r(r) = EPERM;\n    return -1;\n}\n\n__attribute__((weak, used)) int _unlink_r(struct _reent *r, const char *path) {\n    __errno_r(r) = EPERM;\n    return -1;\n}\n\n__attribute__((weak, used)) clock_t _times_r(struct _reent *r, void *t) {\n    __errno_r(r) = EFAULT;\n    return -1;\n}\n\n__attribute__((weak, used)) int _fstat_r(struct _reent *r, int file, struct stat *st) {\n    __errno_r(r) = EBADF;\n    return -1;\n}\n\n__attribute__((weak, used)) int _isatty_r(struct _reent *r, int fd) {\n    __errno_r(r) = EBADF;\n    return 0;\n}\n\n__attribute__((weak, used)) caddr_t _sbrk_r(struct _reent *r, int incr) {\n    __errno_r(r) = ENOMEM;\n    return (caddr_t)-1;\n}\n\n__attribute__((weak, used)) int _kill(int pid, int sig) {\n    errno = EPERM;\n    return -1;\n}\n\n__attribute__((weak, used)) pid_t _getpid(void) {\n    return 1;\n}\n\n__attribute__((weak, used)) void _fini(void) {\n    return;\n}\n\n__attribute__((weak, used, noreturn)) void _exit(int i) {\n    while (1)\n        ;\n}\n\n__attribute__((weak, used)) int _gettimeofday_r(struct _reent *r, struct timeval *t, void *tzp) {\n    __errno_r(r) = EPERM;\n    return -1;\n}\n\n__attribute__((weak, used)) void *__dso_handle;\n\n__attribute__((weak, used)) void __cxa_pure_virtual(void) {\n    while (1)\n        ;\n}\n\n#pragma GCC diagnostic pop\n"
  },
  {
    "path": "platforms/chibios/timer.c",
    "content": "#include <ch.h>\n\n#include \"timer.h\"\n\nstatic uint32_t ticks_offset = 0;\nstatic uint32_t last_ticks   = 0;\nstatic uint32_t ms_offset    = 0;\nstatic uint32_t saved_ms     = 0;\n#if CH_CFG_ST_RESOLUTION < 32\nstatic uint32_t last_systime = 0;\nstatic uint32_t overflow     = 0;\n#endif\n\n// Get the current system time in ticks as a 32-bit number.\n// This function must be called from within a system lock zone (so that it can safely use and update the static data).\nstatic inline uint32_t get_system_time_ticks(void) {\n    uint32_t systime = (uint32_t)chVTGetSystemTimeX();\n\n#if CH_CFG_ST_RESOLUTION < 32\n    // If the real system timer resolution is less than 32 bits, provide the missing bits by checking for the counter\n    // overflow.  For this to work, this function must be called at least once for every overflow of the system timer.\n    // In the 16-bit case, the corresponding times are:\n    //    - CH_CFG_ST_FREQUENCY = 100000, overflow will occur every ~0.65 seconds\n    //    - CH_CFG_ST_FREQUENCY = 10000, overflow will occur every ~6.5 seconds\n    //    - CH_CFG_ST_FREQUENCY = 1000, overflow will occur every ~65 seconds\n    if (systime < last_systime) {\n        overflow += ((uint32_t)1) << CH_CFG_ST_RESOLUTION;\n    }\n    last_systime = systime;\n    systime += overflow;\n#endif\n\n    return systime;\n}\n\n#if CH_CFG_ST_RESOLUTION < 32\nstatic virtual_timer_t update_timer;\n\n// Update the system tick counter every half of the timer overflow period; this should keep the tick counter correct\n// even if something blocks timer interrupts for 1/2 of the timer overflow period.\n#    define UPDATE_INTERVAL (((sysinterval_t)1) << (CH_CFG_ST_RESOLUTION - 1))\n\n// VT callback function to keep the overflow bits of the system tick counter updated.\nstatic void update_fn(struct ch_virtual_timer *timer, void *arg) {\n    (void)arg;\n    chSysLockFromISR();\n    get_system_time_ticks();\n    chVTSetI(&update_timer, UPDATE_INTERVAL, update_fn, NULL);\n    chSysUnlockFromISR();\n}\n#endif\n\n// The highest multiple of CH_CFG_ST_FREQUENCY that fits into uint32_t.  This number of ticks will necessarily\n// correspond to some integer number of seconds.\n#define OVERFLOW_ADJUST_TICKS ((uint32_t)((UINT32_MAX / CH_CFG_ST_FREQUENCY) * CH_CFG_ST_FREQUENCY))\n\n// The time in milliseconds which corresponds to OVERFLOW_ADJUST_TICKS ticks (this is a precise conversion, because\n// OVERFLOW_ADJUST_TICKS corresponds to an integer number of seconds).\n#define OVERFLOW_ADJUST_MS (TIME_I2MS(OVERFLOW_ADJUST_TICKS))\n\nvoid timer_init(void) {\n    timer_clear();\n#if CH_CFG_ST_RESOLUTION < 32\n    chVTObjectInit(&update_timer);\n    chVTSet(&update_timer, UPDATE_INTERVAL, update_fn, NULL);\n#endif\n}\n\nvoid timer_clear(void) {\n    chSysLock();\n    ticks_offset = get_system_time_ticks();\n    last_ticks   = 0;\n    ms_offset    = 0;\n    chSysUnlock();\n}\n\n__attribute__((weak)) void platform_timer_save_value(uint32_t value) {\n    saved_ms = value;\n}\n\n__attribute__((weak)) uint32_t platform_timer_restore_value(void) {\n    return saved_ms;\n}\n\nvoid timer_restore(void) {\n    chSysLock();\n    ticks_offset = get_system_time_ticks();\n    last_ticks   = 0;\n    ms_offset    = platform_timer_restore_value();\n    chSysUnlock();\n}\n\nvoid timer_save(void) {\n    platform_timer_save_value(timer_read32());\n}\n\nuint16_t timer_read(void) {\n    return (uint16_t)timer_read32();\n}\n\nuint32_t timer_read32(void) {\n    syssts_t sts   = chSysGetStatusAndLockX();\n    uint32_t ticks = get_system_time_ticks() - ticks_offset;\n    if (ticks < last_ticks) {\n        // The 32-bit tick counter overflowed and wrapped around.  We cannot just extend the counter to 64 bits here,\n        // because TIME_I2MS() may encounter overflows when handling a 64-bit argument; therefore the solution here is\n        // to subtract a reasonably large number of ticks from the tick counter to bring its value below the 32-bit\n        // limit again, and then add the equivalent number of milliseconds to the converted value.  (Adjusting just the\n        // converted value to account for 2**32 ticks is not possible in general, because 2**32 ticks may not correspond\n        // to an integer number of milliseconds).\n        ticks -= OVERFLOW_ADJUST_TICKS;\n        ticks_offset += OVERFLOW_ADJUST_TICKS;\n        ms_offset += OVERFLOW_ADJUST_MS;\n    }\n    last_ticks              = ticks;\n    uint32_t ms_offset_copy = ms_offset; // read while still holding the lock to ensure a consistent value\n    chSysRestoreStatusX(sts);\n\n    return (uint32_t)TIME_I2MS(ticks) + ms_offset_copy;\n}\n"
  },
  {
    "path": "platforms/chibios/vendors/RP/RP2040.mk",
    "content": "#\n# Raspberry Pi RP2040 specific drivers\n##############################################################################\nCOMMON_VPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)/vendor/$(MCU_FAMILY)/$(MCU_SERIES)\n\nifeq ($(strip $(WS2812_DRIVER)), vendor)\n    OPT_DEFS += -DRP_DMA_REQUIRED=TRUE\nendif\n\n#\n# Raspberry Pi Pico SDK Support\n##############################################################################\nADEFS  += -DCRT0_VTOR_INIT=1 \\\n\t\t  -DCRT0_EXTRA_CORES_NUMBER=0 \\\n          -DCRT0_INIT_VECTORS=1\n\nCFLAGS += -DPICO_NO_FPGA_CHECK \\\n          -DNDEBUG\n\n#\n# Pico SDK source and header files needed by QMK and ChibiOS\n##############################################################################\nPICOSDKROOT   := $(TOP_DIR)/lib/pico-sdk\n\nPICOSDKSRC     = $(PICOSDKROOT)/src/rp2_common/hardware_clocks/clocks.c \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_pll/pll.c \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_pio/pio.c \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_timer/timer.c \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_flash/flash.c \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_gpio/gpio.c \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_claim/claim.c \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_watchdog/watchdog.c \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_xosc/xosc.c \\\n                 $(PICOSDKROOT)/src/rp2_common/pico_bootrom/bootrom.c\n\nPICOSDKINC     = $(CHIBIOS)//os/various/pico_bindings/dumb/include \\\n                 $(PICOSDKROOT)/src/common/pico_base/include \\\n                 $(PICOSDKROOT)/src/rp2_common/pico_platform/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_base/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_clocks/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_claim/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_flash/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_gpio/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_irq/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_pll/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_pio/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_sync/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_timer/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_resets/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_watchdog/include \\\n                 $(PICOSDKROOT)/src/rp2_common/hardware_xosc/include \\\n                 $(PICOSDKROOT)/src/rp2040/hardware_regs/include \\\n                 $(PICOSDKROOT)/src/rp2040/hardware_structs/include \\\n                 $(PICOSDKROOT)/src/boards/include \\\n                 $(PICOSDKROOT)/src/rp2_common/pico_bootrom/include\n\nPLATFORM_SRC += $(PICOSDKSRC)\nEXTRAINCDIRS += $(PICOSDKINC)\n\nPLATFORM_RP2040_PATH := $(PLATFORM_PATH)/$(PLATFORM_KEY)/vendors/$(MCU_FAMILY)\n\nPLATFORM_SRC +=\t$(PLATFORM_RP2040_PATH)/stage2_bootloaders.c \\\n\t\t\t\t$(PLATFORM_RP2040_PATH)/pico_sdk_shims.c\n\nEXTRAINCDIRS += $(PLATFORM_RP2040_PATH)\n\n#\n# RP2040 optimized compiler intrinsics\n##############################################################################\n\n# The RP2040 sdk provides optimized compiler intrinsics which override the GCC\n# built-ins. Some of these functions are located in the bootrom of the RP2040.\n# Execution of these functions is realized via a vtable that is populated on\n# bootup. This mechanism needs startup code and linker script support from\n# ChibiOS, which is currently not implemented thus these functions are disabled\n# ATM.\nPICOSDKINTRINSICSSRC =  $(PICOSDKROOT)/src/rp2_common/pico_divider/divider.S \\\n                        $(PICOSDKROOT)/src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S\n\nPICOSDKINTRINSICSINC =  $(PICOSDKROOT)/src/common/pico_base/include \\\n                        $(PICOSDKROOT)/src/rp2_common/pico_platform/include \\\n                        $(PICOSDKROOT)/src/rp2_common/hardware_divider/include\n\n# integer division intrinsics utilizing the RP2040 hardware divider\nOPT_DEFS += -DPICO_DIVIDER_IN_RAM=1\nOPT_DEFS += -DPICO_DIVIDER_DISABLE_INTERRUPTS=1\n\nCFLAGS += -Wl,--wrap=__aeabi_idiv\nCFLAGS += -Wl,--wrap=__aeabi_idivmod\nCFLAGS += -Wl,--wrap=__aeabi_ldivmod\nCFLAGS += -Wl,--wrap=__aeabi_uidiv\nCFLAGS += -Wl,--wrap=__aeabi_uidivmod\nCFLAGS += -Wl,--wrap=__aeabi_uldivmod\n\n# 64bit integer intrinsics\nOPT_DEFS += -DPICO_INT64_OPS_IN_RAM=1\n\nCFLAGS += -Wl,--wrap=__aeabi_lmul\n\nPLATFORM_SRC += $(PICOSDKINTRINSICSSRC)\nEXTRAINCDIRS += $(PICOSDKINTRINSICSINC)\n"
  },
  {
    "path": "platforms/chibios/vendors/RP/_pin_defs.h",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n/* RP2040 GPIO Numbering */\n#define GP0 0U\n#define GP1 1U\n#define GP2 2U\n#define GP3 3U\n#define GP4 4U\n#define GP5 5U\n#define GP6 6U\n#define GP7 7U\n#define GP8 8U\n#define GP9 9U\n#define GP10 10U\n#define GP11 11U\n#define GP12 12U\n#define GP13 13U\n#define GP14 14U\n#define GP15 15U\n#define GP16 16U\n#define GP17 17U\n#define GP18 18U\n#define GP19 19U\n#define GP20 20U\n#define GP21 21U\n#define GP22 22U\n#define GP23 23U\n#define GP24 24U\n#define GP25 25U\n#define GP26 26U\n#define GP27 27U\n#define GP28 28U\n#define GP29 29U\n#define GP30 30U\n"
  },
  {
    "path": "platforms/chibios/vendors/RP/pico_sdk_shims.c",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <stdbool.h>\n\nextern void chSysHalt(const char *reason) __attribute__((noreturn));\n\nvoid panic(const char *fmt, ...) {\n    chSysHalt(fmt);\n}\n\nvoid hard_assertion_failure(void) {\n    panic(\"hard assert\");\n}\n"
  },
  {
    "path": "platforms/chibios/vendors/RP/stage2_bootloaders.c",
    "content": "// ----------------------------------------------------------------------------\n// Pre-compiled second stage boot code for RP2040.\n//\n// Copyright (c) 2019-2021 Raspberry Pi (Trading) Ltd.\n// SPDX-License-Identifier: BSD-3-Clause\n// ----------------------------------------------------------------------------\n\n#include <stdint.h>\n\n#define BOOTLOADER_SECTION __attribute__((used, section(\".boot2\")))\n\n// clang-format off\n\n#if defined(RP2040_FLASH_AT25SF128A)\n\nconst uint8_t BOOTLOADER_SECTION BOOT2_ROM[256] = {\n  0x00, 0xb5, 0x31, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21,\n  0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2d, 0x4b,\n  0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22,\n  0x99, 0x50, 0x2a, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20,\n  0x00, 0xf0, 0x42, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x12, 0xd0, 0x06, 0x21,\n  0x19, 0x66, 0x00, 0xf0, 0x32, 0xf8, 0x19, 0x6e, 0x31, 0x21, 0x19, 0x66,\n  0x1a, 0x66, 0x00, 0xf0, 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e,\n  0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21, 0x08, 0x42, 0xf9, 0xd1,\n  0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60,\n  0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21,\n  0x19, 0x66, 0x20, 0x21, 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21,\n  0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60,\n  0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49,\n  0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5,\n  0x99, 0x6a, 0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42,\n  0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66, 0x18, 0x66, 0xff, 0xf7,\n  0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40,\n  0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00,\n  0x21, 0x22, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0x20,\n  0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0xc0, 0xdd, 0xc0, 0xb5\n};\n\n#elif defined(RP2040_FLASH_GD25Q64CS)\n\nconst uint8_t BOOTLOADER_SECTION BOOT2_ROM[256] = {\n  0x00, 0xb5, 0x31, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21,\n  0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2d, 0x4b,\n  0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22,\n  0x99, 0x50, 0x2a, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20,\n  0x00, 0xf0, 0x42, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x12, 0xd0, 0x06, 0x21,\n  0x19, 0x66, 0x00, 0xf0, 0x32, 0xf8, 0x19, 0x6e, 0x31, 0x21, 0x19, 0x66,\n  0x1a, 0x66, 0x00, 0xf0, 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e,\n  0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21, 0x08, 0x42, 0xf9, 0xd1,\n  0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60,\n  0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xe7, 0x21,\n  0x19, 0x66, 0xa0, 0x21, 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21,\n  0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60,\n  0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49,\n  0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5,\n  0x99, 0x6a, 0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42,\n  0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66, 0x18, 0x66, 0xff, 0xf7,\n  0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40,\n  0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00,\n  0x21, 0x12, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18, 0x22, 0x10, 0x00, 0xa0,\n  0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0xe2, 0xd9, 0xa2, 0xb5\n};\n\n#elif defined(RP2040_FLASH_W25X10CL)\n\nconst uint8_t BOOTLOADER_SECTION BOOT2_ROM[256] = {\n  0x00, 0xb5, 0x14, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61,\n  0x12, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60, 0x11, 0x49, 0x12, 0x48,\n  0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xbb, 0x21, 0x19, 0x66, 0x02, 0x21,\n  0x19, 0x66, 0x08, 0x21, 0x98, 0x6a, 0x08, 0x42, 0xfc, 0xd0, 0x00, 0x21,\n  0x99, 0x60, 0x0c, 0x49, 0x0a, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60,\n  0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x08, 0x48, 0x09, 0x49,\n  0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x3f, 0x00, 0x1d, 0x12, 0x00, 0x00,\n  0xf4, 0x00, 0x00, 0x18, 0x1e, 0x10, 0x00, 0x20, 0x00, 0x01, 0x00, 0x10,\n  0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x7c, 0x81, 0x53, 0x9a\n};\n\n#elif defined(RP2040_FLASH_IS25LP080)\n\nconst uint8_t BOOTLOADER_SECTION BOOT2_ROM[256] = {\n  0x00, 0xb5, 0x2b, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61,\n  0x29, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x28, 0x48, 0x00, 0xf0,\n  0x42, 0xf8, 0x28, 0x4a, 0x90, 0x42, 0x12, 0xd0, 0x06, 0x21, 0x19, 0x66,\n  0x00, 0xf0, 0x32, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20,\n  0x1a, 0x66, 0x00, 0xf0, 0x2b, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x1f, 0x48,\n  0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21, 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21,\n  0x99, 0x60, 0x1d, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60, 0x1c, 0x49,\n  0x1c, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66,\n  0xa0, 0x21, 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60,\n  0x17, 0x49, 0x16, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc,\n  0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x14, 0x48, 0x14, 0x49, 0x08, 0x60,\n  0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a,\n  0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1,\n  0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66, 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff,\n  0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,\n  0x00, 0x00, 0x07, 0x00, 0x05, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,\n  0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18,\n  0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x28, 0x33, 0x43, 0xb2\n};\n\n#elif defined(RP2040_FLASH_GENERIC_03H)\n\nconst uint8_t BOOTLOADER_SECTION BOOT2_ROM[256] = {\n  0x00, 0xb5, 0x0c, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61,\n  0x0a, 0x49, 0x19, 0x60, 0x0a, 0x49, 0x0b, 0x48, 0x01, 0x60, 0x00, 0x21,\n  0x59, 0x60, 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0,\n  0x00, 0x47, 0x07, 0x48, 0x07, 0x49, 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3,\n  0x08, 0x88, 0x08, 0x47, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, 0x1f, 0x00,\n  0x18, 0x02, 0x00, 0x03, 0xf4, 0x00, 0x00, 0x18, 0x00, 0x01, 0x00, 0x10,\n  0x08, 0xed, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x2c, 0xec, 0x21, 0x0d\n};\n\n#else\n\nconst uint8_t BOOTLOADER_SECTION BOOT2_ROM[256] = {\n  0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21,\n  0x88, 0x43, 0x98, 0x60, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b,\n  0x00, 0x21, 0x99, 0x60, 0x04, 0x21, 0x59, 0x61, 0x01, 0x21, 0xf0, 0x22,\n  0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20,\n  0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21,\n  0x19, 0x66, 0x00, 0xf0, 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66,\n  0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0, 0x2c, 0xf8, 0x19, 0x6e,\n  0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21,\n  0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60,\n  0x00, 0x21, 0x59, 0x60, 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21,\n  0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21, 0x19, 0x66, 0x00, 0xf0,\n  0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60,\n  0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47,\n  0x12, 0x48, 0x13, 0x49, 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88,\n  0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20, 0x01, 0x42, 0xfb, 0xd0,\n  0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66,\n  0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd,\n  0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00,\n  0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x18,\n  0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0,\n  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n  0x07, 0x0b, 0x8f, 0xd5\n};\n\n#endif\n\n// clang-format on\n"
  },
  {
    "path": "platforms/chibios/wait.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <ch.h>\n#include <hal.h>\n\n#include \"_wait.h\"\n\n#ifdef WAIT_US_TIMER\nvoid wait_us(uint16_t duration) {\n    static const GPTConfig gpt_cfg = {.frequency = 1000000}; /* 1MHz timer, no callback */\n\n    if (duration == 0) {\n        duration = 1;\n    }\n\n    /*\n     * Only use this timer on the main thread;\n     * other threads need to use their own timer.\n     */\n    if (chThdGetSelfX() == &(currcore->mainthread) && duration < (1ULL << (sizeof(gptcnt_t) * 8))) {\n        gptStart(&WAIT_US_TIMER, &gpt_cfg);\n        gptPolledDelay(&WAIT_US_TIMER, duration);\n    } else {\n        chThdSleepMicroseconds(duration);\n    }\n}\n#endif\n"
  },
  {
    "path": "platforms/common.mk",
    "content": "PLATFORM_COMMON_DIR = $(PLATFORM_PATH)/$(PLATFORM_KEY)\n\nSRC +=\t\\\n\t$(PLATFORM_PATH)/suspend.c \\\n\t$(PLATFORM_PATH)/synchronization_util.c \\\n\t$(PLATFORM_PATH)/timer.c \\\n\t$(PLATFORM_COMMON_DIR)/hardware_id.c \\\n\t$(PLATFORM_COMMON_DIR)/platform.c \\\n\t$(PLATFORM_COMMON_DIR)/suspend.c \\\n\t$(PLATFORM_COMMON_DIR)/timer.c \\\n\t$(PLATFORM_COMMON_DIR)/bootloaders/$(BOOTLOADER_TYPE).c\n\n# Search Path\nVPATH += $(PLATFORM_PATH)\nVPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)\nVPATH += $(PLATFORM_PATH)/$(PLATFORM_KEY)/$(DRIVER_DIR)\n"
  },
  {
    "path": "platforms/eeprom.h",
    "content": "// Copyright 2018-2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#if defined(__AVR__) && !defined(EEPROM_DRIVER)\n#    include <avr/eeprom.h>\n#else\n#    include <stdint.h>\n#    include <stdlib.h>\n\nuint8_t  eeprom_read_byte(const uint8_t *__p);\nuint16_t eeprom_read_word(const uint16_t *__p);\nuint32_t eeprom_read_dword(const uint32_t *__p);\nvoid     eeprom_read_block(void *__dst, const void *__src, size_t __n);\nvoid     eeprom_write_byte(uint8_t *__p, uint8_t __value);\nvoid     eeprom_write_word(uint16_t *__p, uint16_t __value);\nvoid     eeprom_write_dword(uint32_t *__p, uint32_t __value);\nvoid     eeprom_write_block(const void *__src, void *__dst, size_t __n);\nvoid     eeprom_update_byte(uint8_t *__p, uint8_t __value);\nvoid     eeprom_update_word(uint16_t *__p, uint16_t __value);\nvoid     eeprom_update_dword(uint32_t *__p, uint32_t __value);\nvoid     eeprom_update_block(const void *__src, void *__dst, size_t __n);\n#endif\n\n// While newer avr-libc versions may have an implementation\n//   use preprocessor as to not cause conflicts\n#undef eeprom_write_qword\n#define eeprom_write_qword(__p, __value)                  \\\n    do {                                                  \\\n        uint64_t tmp = __value;                           \\\n        eeprom_update_block(&tmp, __p, sizeof(uint64_t)); \\\n    } while (0)\n\n#if defined(EEPROM_CUSTOM)\n#    ifndef EEPROM_SIZE\n#        error EEPROM_SIZE has not been defined for custom driver.\n#    endif\n#    define TOTAL_EEPROM_BYTE_COUNT (EEPROM_SIZE)\n#elif defined(EEPROM_WEAR_LEVELING)\n#    include \"wear_leveling_drivers.h\"\n#    define TOTAL_EEPROM_BYTE_COUNT (WEAR_LEVELING_LOGICAL_SIZE)\n#elif defined(EEPROM_TRANSIENT)\n#    include \"eeprom_transient.h\"\n#    define TOTAL_EEPROM_BYTE_COUNT (TRANSIENT_EEPROM_SIZE)\n#elif defined(EEPROM_I2C)\n#    include \"eeprom_i2c.h\"\n#    define TOTAL_EEPROM_BYTE_COUNT (EXTERNAL_EEPROM_BYTE_COUNT)\n#elif defined(EEPROM_SPI)\n#    include \"eeprom_spi.h\"\n#    define TOTAL_EEPROM_BYTE_COUNT (EXTERNAL_EEPROM_BYTE_COUNT)\n#elif defined(EEPROM_STM32_L0_L1)\n#    include \"eeprom_stm32_L0_L1.h\"\n#    define TOTAL_EEPROM_BYTE_COUNT (STM32_ONBOARD_EEPROM_SIZE)\n#elif defined(EEPROM_KINETIS_FLEXRAM)\n#    include \"eeprom_kinetis_flexram.h\"\n#    define TOTAL_EEPROM_BYTE_COUNT (EEPROM_SIZE)\n#elif defined(EEPROM_LEGACY_EMULATED_FLASH)\n#    include \"eeprom_legacy_emulated_flash_defs.h\"\n#    define TOTAL_EEPROM_BYTE_COUNT (FEE_DENSITY_BYTES)\n#elif defined(EEPROM_SAMD)\n#    include \"eeprom_samd.h\"\n#    define TOTAL_EEPROM_BYTE_COUNT (EEPROM_SIZE)\n#elif defined(__AVR_ATmega16U2__) || defined(__AVR_ATmega16U4__) || defined(__AVR_AT90USB162__) || defined(__AVR_ATtiny85__)\n#    define TOTAL_EEPROM_BYTE_COUNT 512\n#elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega32A__)\n#    define TOTAL_EEPROM_BYTE_COUNT 1024\n#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__)\n#    define TOTAL_EEPROM_BYTE_COUNT 2048\n#elif defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)\n#    define TOTAL_EEPROM_BYTE_COUNT 4096\n#elif defined(EEPROM_TEST_HARNESS)\n#    ifndef LEGACY_FLASH_OPS_MOCKED\n// Normal tests\n#        define TOTAL_EEPROM_BYTE_COUNT 32\n#    else\n// Flash wear-leveling testing\n#        include \"eeprom_legacy_emulated_flash_tests.h\"\n#        define TOTAL_EEPROM_BYTE_COUNT (EEPROM_SIZE)\n#    endif\n#else\n#    error Unknown EEPROM driver.\n#endif\n"
  },
  {
    "path": "platforms/gpio.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include \"pin_defs.h\"\n\n#if __has_include_next(\"gpio.h\")\n#    include_next \"gpio.h\" /* Include the platforms gpio.h */\n#endif\n\n// ======== DEPRECATED DEFINES - DO NOT USE ========\n\n#define setPinInput(pin) gpio_set_pin_input(pin)\n#define setPinInputHigh(pin) gpio_set_pin_input_high(pin)\n#define setPinInputLow(pin) gpio_set_pin_input_low(pin)\n#define setPinOutputPushPull(pin) gpio_set_pin_output_push_pull(pin)\n#define setPinOutputOpenDrain(pin) gpio_set_pin_output_open_drain(pin)\n#define setPinOutput(pin) gpio_set_pin_output_push_pull(pin)\n\n#define writePinHigh(pin) gpio_write_pin_high(pin)\n#define writePinLow(pin) gpio_write_pin_low(pin)\n#define writePin(pin, level) gpio_write_pin(pin, level)\n\n#define readPin(pin) gpio_read_pin(pin)\n\n#define togglePin(pin) gpio_toggle_pin(pin)\n"
  },
  {
    "path": "platforms/hardware_id.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n\n/** \\brief Storage for a hardware ID\n *\n * Ensure this is sized to cover all hardware scenarios\n */\ntypedef struct hardware_id_t {\n    uint32_t data[4];\n} hardware_id_t;\n\n/** \\brief Query the devices \"unique\" ID\n */\nhardware_id_t get_hardware_id(void);\n"
  },
  {
    "path": "platforms/lv_conf.h",
    "content": "/**\n * @file lv_conf.h\n * Configuration file for v8.2.0\n */\n\n/*\n * Copy this file as `lv_conf.h`\n * 1. simply next to the `lvgl` folder\n * 2. or any other places and\n *    - define `LV_CONF_INCLUDE_SIMPLE`\n *    - add the path as include path\n */\n\n/* clang-format off */\n#if 1 /*Set it to \"1\" to enable content*/\n\n#ifndef LV_CONF_H\n#define LV_CONF_H\n\n#include <stdint.h>\n\n/*====================\n   COLOR SETTINGS\n *====================*/\n\n/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/\n#define LV_COLOR_DEPTH 16\n\n/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/\n#define LV_COLOR_16_SWAP 1\n\n/*Enable more complex drawing routines to manage screens transparency.\n *Can be used if the UI is above another layer, e.g. an OSD menu or video player.\n *Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/\n#define LV_COLOR_SCREEN_TRANSP 0\n\n/* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently.\n * 0: round down, 64: round up from x.75, 128: round up from half, 192: round up from x.25, 254: round up */\n#define LV_COLOR_MIX_ROUND_OFS (LV_COLOR_DEPTH == 32 ? 0: 128)\n\n/*Images pixels with this color will not be drawn if they are chroma keyed)*/\n#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00)         /*pure green*/\n\n/*=========================\n   MEMORY SETTINGS\n *=========================*/\n\n/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/\n#ifndef LV_MEM_CUSTOM\n#define LV_MEM_CUSTOM 1\n#endif // LV_MEM_CUSTOM\n#if LV_MEM_CUSTOM == 0\n    /*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/\n    #define LV_MEM_SIZE (48U * 1024U)          /*[bytes]*/\n\n    /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/\n    #define LV_MEM_ADR 0     /*0: unused*/\n    /*Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc*/\n    #if LV_MEM_ADR == 0\n        //#define LV_MEM_POOL_INCLUDE your_alloc_library  /* Uncomment if using an external allocator*/\n        //#define LV_MEM_POOL_ALLOC   your_alloc          /* Uncomment if using an external allocator*/\n    #endif\n\n#else       /*LV_MEM_CUSTOM*/\n    #define LV_MEM_CUSTOM_INCLUDE <stdlib.h>   /*Header for the dynamic memory function*/\n    #define LV_MEM_CUSTOM_ALLOC   malloc\n    #define LV_MEM_CUSTOM_FREE    free\n    #define LV_MEM_CUSTOM_REALLOC realloc\n#endif     /*LV_MEM_CUSTOM*/\n\n/*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms.\n *You will see an error log message if there wasn't enough buffers. */\n#define LV_MEM_BUF_MAX_NUM 16\n\n/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/\n#define LV_MEMCPY_MEMSET_STD 0\n\n/*====================\n   HAL SETTINGS\n *====================*/\n\n/*Default display refresh period. LVG will redraw changed areas with this period time*/\n#define LV_DISP_DEF_REFR_PERIOD 30      /*[ms]*/\n\n/*Input device read period in milliseconds*/\n#define LV_INDEV_DEF_READ_PERIOD 30     /*[ms]*/\n\n/*Use a custom tick source that tells the elapsed time in milliseconds.\n *It removes the need to manually update the tick with `lv_tick_inc()`)*/\n#ifndef LV_TICK_CUSTOM\n#define LV_TICK_CUSTOM 0\n#endif // LV_TICK_CUSTOM\n#if LV_TICK_CUSTOM\n    #define LV_TICK_CUSTOM_INCLUDE \"Arduino.h\"         /*Header for the system time function*/\n    #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis())    /*Expression evaluating to current system time in ms*/\n#endif   /*LV_TICK_CUSTOM*/\n\n/*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings.\n *(Not so important, you can adjust it to modify default sizes and spaces)*/\n#define LV_DPI_DEF 130     /*[px/inch]*/\n\n/*=======================\n * FEATURE CONFIGURATION\n *=======================*/\n\n/*-------------\n * Drawing\n *-----------*/\n\n/*Enable complex draw engine.\n *Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/\n#ifndef LV_DRAW_COMPLEX\n#define LV_DRAW_COMPLEX 1\n#endif // LV_DRAW_COMPLEX\n#if LV_DRAW_COMPLEX != 0\n\n    /*Allow buffering some shadow calculation.\n    *LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius`\n    *Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/\n    #define LV_SHADOW_CACHE_SIZE 0\n\n    /* Set number of maximally cached circle data.\n    * The circumference of 1/4 circle are saved for anti-aliasing\n    * radius * 4 bytes are used per circle (the most often used radiuses are saved)\n    * 0: to disable caching */\n    #define LV_CIRCLE_CACHE_SIZE 4\n#endif /*LV_DRAW_COMPLEX*/\n\n/*Default image cache size. Image caching keeps the images opened.\n *If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added)\n *With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.\n *However the opened images might consume additional RAM.\n *0: to disable caching*/\n#define LV_IMG_CACHE_DEF_SIZE   0\n\n/*Number of stops allowed per gradient. Increase this to allow more stops.\n *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/\n#define LV_GRADIENT_MAX_STOPS       2\n\n/*Default gradient buffer size.\n *When LVGL calculates the gradient \"maps\" it can save them into a cache to avoid calculating them again.\n *LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes.\n *If the cache is too small the map will be allocated only while it's required for the drawing.\n *0 mean no caching.*/\n#define LV_GRAD_CACHE_DEF_SIZE      0\n\n/*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display)\n *LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface\n *The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */\n#ifndef LV_DITHER_GRADIENT\n#define LV_DITHER_GRADIENT      0\n#endif\n#if LV_DITHER_GRADIENT\n    /*Add support for error diffusion dithering.\n     *Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing.\n     *The increase in memory consumption is (24 bits * object's width)*/\n    #define LV_DITHER_ERROR_DIFFUSION   0\n#endif\n\n/*Maximum buffer size to allocate for rotation.\n *Only used if software rotation is enabled in the display driver.*/\n#define LV_DISP_ROT_MAX_BUF (10*1024)\n\n/*-------------\n * GPU\n *-----------*/\n\n/*Use STM32's DMA2D (aka Chrom Art) GPU*/\n#ifndef LV_USE_GPU_STM32_DMA2D\n#define LV_USE_GPU_STM32_DMA2D 0\n#endif // LV_USE_GPU_STM32_DMA2D\n#if LV_USE_GPU_STM32_DMA2D\n    /*Must be defined to include path of CMSIS header of target processor\n    e.g. \"stm32f769xx.h\" or \"stm32f429xx.h\"*/\n    #define LV_GPU_DMA2D_CMSIS_INCLUDE\n#endif\n\n/*Use NXP's PXP GPU iMX RTxxx platforms*/\n#ifndef LV_USE_GPU_NXP_PXP\n#define LV_USE_GPU_NXP_PXP 0\n#endif // LV_USE_GPU_NXP_PXP\n#if LV_USE_GPU_NXP_PXP\n    /*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c)\n    *   and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol SDK_OS_FREE_RTOS\n    *   has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected.\n    *0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init()\n    */\n    #define LV_USE_GPU_NXP_PXP_AUTO_INIT 0\n#endif\n\n/*Use NXP's VG-Lite GPU iMX RTxxx platforms*/\n#define LV_USE_GPU_NXP_VG_LITE 0\n\n/*Use SDL renderer API*/\n#ifndef LV_USE_GPU_SDL\n#define LV_USE_GPU_SDL 0\n#endif // LV_USE_GPU_SDL\n#if LV_USE_GPU_SDL\n    #define LV_GPU_SDL_INCLUDE_PATH <SDL2/SDL.h>\n    /*Texture cache size, 8MB by default*/\n    #define LV_GPU_SDL_LRU_SIZE (1024 * 1024 * 8)\n    /*Custom blend mode for mask drawing, disable if you need to link with older SDL2 lib*/\n    #define LV_GPU_SDL_CUSTOM_BLEND_MODE (SDL_VERSION_ATLEAST(2, 0, 6))\n#endif\n\n/*-------------\n * Logging\n *-----------*/\n\n/*Enable the log module*/\n#ifndef LV_USE_LOG\n#define LV_USE_LOG 0\n#endif // LV_USE_LOG\n#if LV_USE_LOG\n\n    /*How important log should be added:\n    *LV_LOG_LEVEL_TRACE       A lot of logs to give detailed information\n    *LV_LOG_LEVEL_INFO        Log important events\n    *LV_LOG_LEVEL_WARN        Log if something unwanted happened but didn't cause a problem\n    *LV_LOG_LEVEL_ERROR       Only critical issue, when the system may fail\n    *LV_LOG_LEVEL_USER        Only logs added by the user\n    *LV_LOG_LEVEL_NONE        Do not log anything*/\n    #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN\n\n    /*1: Print the log with 'printf';\n    *0: User need to register a callback with `lv_log_register_print_cb()`*/\n    #define LV_LOG_PRINTF 0\n\n    /*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/\n    #define LV_LOG_TRACE_MEM        1\n    #define LV_LOG_TRACE_TIMER      1\n    #define LV_LOG_TRACE_INDEV      1\n    #define LV_LOG_TRACE_DISP_REFR  1\n    #define LV_LOG_TRACE_EVENT      1\n    #define LV_LOG_TRACE_OBJ_CREATE 1\n    #define LV_LOG_TRACE_LAYOUT     1\n    #define LV_LOG_TRACE_ANIM       1\n\n#endif  /*LV_USE_LOG*/\n\n/*-------------\n * Asserts\n *-----------*/\n\n/*Enable asserts if an operation is failed or an invalid data is found.\n *If LV_USE_LOG is enabled an error message will be printed on failure*/\n#define LV_USE_ASSERT_NULL          1   /*Check if the parameter is NULL. (Very fast, recommended)*/\n#define LV_USE_ASSERT_MALLOC        1   /*Checks is the memory is successfully allocated or no. (Very fast, recommended)*/\n#define LV_USE_ASSERT_STYLE         0   /*Check if the styles are properly initialized. (Very fast, recommended)*/\n#define LV_USE_ASSERT_MEM_INTEGRITY 0   /*Check the integrity of `lv_mem` after critical operations. (Slow)*/\n#define LV_USE_ASSERT_OBJ           0   /*Check the object's type and existence (e.g. not deleted). (Slow)*/\n\n/*Add a custom handler when assert happens e.g. to restart the MCU*/\n#define LV_ASSERT_HANDLER_INCLUDE <stdint.h>\n#define LV_ASSERT_HANDLER while(1);   /*Halt by default*/\n\n/*-------------\n * Others\n *-----------*/\n\n/*1: Show CPU usage and FPS count*/\n#ifndef LV_USE_PERF_MONITOR\n#define LV_USE_PERF_MONITOR 0\n#endif // LV_USE_PERF_MONITOR\n#if LV_USE_PERF_MONITOR\n    #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT\n#endif\n\n/*1: Show the used memory and the memory fragmentation\n * Requires LV_MEM_CUSTOM = 0*/\n#ifndef LV_USE_MEM_MONITOR\n#define LV_USE_MEM_MONITOR 0\n#endif // LV_USE_MEM_MONITOR\n#if LV_USE_MEM_MONITOR\n    #define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT\n#endif\n\n/*1: Draw random colored rectangles over the redrawn areas*/\n#define LV_USE_REFR_DEBUG 0\n\n/*Change the built in (v)snprintf functions*/\n#ifndef LV_SPRINTF_CUSTOM\n#define LV_SPRINTF_CUSTOM 1\n#endif // LV_SPRINTF_CUSTOM\n#if LV_SPRINTF_CUSTOM\n    #define LV_SPRINTF_INCLUDE <stdio.h>\n    #define lv_snprintf  snprintf\n    #define lv_vsnprintf vsnprintf\n#else   /*LV_SPRINTF_CUSTOM*/\n    #define LV_SPRINTF_USE_FLOAT 0\n#endif  /*LV_SPRINTF_CUSTOM*/\n\n#define LV_USE_USER_DATA 1\n\n/*Garbage Collector settings\n *Used if lvgl is bound to higher level language and the memory is managed by that language*/\n#ifndef LV_ENABLE_GC\n#define LV_ENABLE_GC 0\n#endif // LV_ENABLE_GC\n#if LV_ENABLE_GC != 0\n    #define LV_GC_INCLUDE \"gc.h\"                           /*Include Garbage Collector related things*/\n#endif /*LV_ENABLE_GC*/\n\n/*=====================\n *  COMPILER SETTINGS\n *====================*/\n\n/*For big endian systems set to 1*/\n#define LV_BIG_ENDIAN_SYSTEM 0\n\n/*Define a custom attribute to `lv_tick_inc` function*/\n#define LV_ATTRIBUTE_TICK_INC\n\n/*Define a custom attribute to `lv_timer_handler` function*/\n#define LV_ATTRIBUTE_TIMER_HANDLER\n\n/*Define a custom attribute to `lv_disp_flush_ready` function*/\n#define LV_ATTRIBUTE_FLUSH_READY\n\n/*Required alignment size for buffers*/\n#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1\n\n/*Will be added where memories needs to be aligned (with -Os data might not be aligned to boundary by default).\n * E.g. __attribute__((aligned(4)))*/\n#define LV_ATTRIBUTE_MEM_ALIGN\n\n/*Attribute to mark large constant arrays for example font's bitmaps*/\n#define LV_ATTRIBUTE_LARGE_CONST\n\n/*Compiler prefix for a big array declaration in RAM*/\n#define LV_ATTRIBUTE_LARGE_RAM_ARRAY\n\n/*Place performance critical functions into a faster memory (e.g RAM)*/\n#define LV_ATTRIBUTE_FAST_MEM\n\n/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/\n#define LV_ATTRIBUTE_DMA\n\n/*Export integer constant to binding. This macro is used with constants in the form of LV_<CONST> that\n *should also appear on LVGL binding API such as Micropython.*/\n#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /*The default value just prevents GCC warning*/\n\n/*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/\n#define LV_USE_LARGE_COORD 0\n\n/*==================\n *   FONT USAGE\n *===================*/\n\n/*Montserrat fonts with ASCII range and some symbols using bpp = 4\n *https://fonts.google.com/specimen/Montserrat*/\n#define LV_FONT_MONTSERRAT_8  0\n#define LV_FONT_MONTSERRAT_10 0\n#define LV_FONT_MONTSERRAT_12 0\n#define LV_FONT_MONTSERRAT_14 1\n#define LV_FONT_MONTSERRAT_16 0\n#define LV_FONT_MONTSERRAT_18 0\n#define LV_FONT_MONTSERRAT_20 0\n#define LV_FONT_MONTSERRAT_22 0\n#define LV_FONT_MONTSERRAT_24 0\n#define LV_FONT_MONTSERRAT_26 0\n#define LV_FONT_MONTSERRAT_28 0\n#define LV_FONT_MONTSERRAT_30 0\n#define LV_FONT_MONTSERRAT_32 0\n#define LV_FONT_MONTSERRAT_34 0\n#define LV_FONT_MONTSERRAT_36 0\n#define LV_FONT_MONTSERRAT_38 0\n#define LV_FONT_MONTSERRAT_40 0\n#define LV_FONT_MONTSERRAT_42 0\n#define LV_FONT_MONTSERRAT_44 0\n#define LV_FONT_MONTSERRAT_46 0\n#define LV_FONT_MONTSERRAT_48 0\n\n/*Demonstrate special features*/\n#define LV_FONT_MONTSERRAT_12_SUBPX      0\n#define LV_FONT_MONTSERRAT_28_COMPRESSED 0  /*bpp = 3*/\n#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0  /*Hebrew, Arabic, Persian letters and all their forms*/\n#define LV_FONT_SIMSUN_16_CJK            0  /*1000 most common CJK radicals*/\n\n/*Pixel perfect monospace fonts*/\n#define LV_FONT_UNSCII_8  0\n#define LV_FONT_UNSCII_16 0\n\n/*Optionally declare custom fonts here.\n *You can use these fonts as default font too and they will be available globally.\n *E.g. #define LV_FONT_CUSTOM_DECLARE   LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/\n#define LV_FONT_CUSTOM_DECLARE\n\n/*Always set a default font*/\n#define LV_FONT_DEFAULT &lv_font_montserrat_14\n\n/*Enable handling large font and/or fonts with a lot of characters.\n *The limit depends on the font size, font face and bpp.\n *Compiler error will be triggered if a font needs it.*/\n#define LV_FONT_FMT_TXT_LARGE 0\n\n/*Enables/disables support for compressed fonts.*/\n#define LV_USE_FONT_COMPRESSED 0\n\n/*Enable subpixel rendering*/\n#ifndef LV_USE_FONT_SUBPX\n#define LV_USE_FONT_SUBPX 0\n#endif // LV_USE_FONT_SUBPX\n#if LV_USE_FONT_SUBPX\n    /*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with \"normal\" fonts.*/\n    #define LV_FONT_SUBPX_BGR 0  /*0: RGB; 1:BGR order*/\n#endif\n\n/*=================\n *  TEXT SETTINGS\n *=================*/\n\n/**\n * Select a character encoding for strings.\n * Your IDE or editor should have the same character encoding\n * - LV_TXT_ENC_UTF8\n * - LV_TXT_ENC_ASCII\n */\n#define LV_TXT_ENC LV_TXT_ENC_UTF8\n\n/*Can break (wrap) texts on these chars*/\n#define LV_TXT_BREAK_CHARS \" ,.;:-_\"\n\n/*If a word is at least this long, will break wherever \"prettiest\"\n *To disable, set to a value <= 0*/\n#define LV_TXT_LINE_BREAK_LONG_LEN 0\n\n/*Minimum number of characters in a long word to put on a line before a break.\n *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/\n#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3\n\n/*Minimum number of characters in a long word to put on a line after a break.\n *Depends on LV_TXT_LINE_BREAK_LONG_LEN.*/\n#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3\n\n/*The control character to use for signalling text recoloring.*/\n#define LV_TXT_COLOR_CMD \"#\"\n\n/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts.\n *The direction will be processed according to the Unicode Bidirectional Algorithm:\n *https://www.w3.org/International/articles/inline-bidi-markup/uba-basics*/\n#ifndef LV_USE_BIDI\n#define LV_USE_BIDI 0\n#endif // LV_USE_BIDI\n#if LV_USE_BIDI\n    /*Set the default direction. Supported values:\n    *`LV_BASE_DIR_LTR` Left-to-Right\n    *`LV_BASE_DIR_RTL` Right-to-Left\n    *`LV_BASE_DIR_AUTO` detect texts base direction*/\n    #define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO\n#endif\n\n/*Enable Arabic/Persian processing\n *In these languages characters should be replaced with an other form based on their position in the text*/\n#define LV_USE_ARABIC_PERSIAN_CHARS 0\n\n/*==================\n *  WIDGET USAGE\n *================*/\n\n/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/\n\n#define LV_USE_ARC        1\n\n#define LV_USE_ANIMIMG    1\n\n#define LV_USE_BAR        1\n\n#define LV_USE_BTN        1\n\n#define LV_USE_BTNMATRIX  1\n\n#define LV_USE_CANVAS     1\n\n#define LV_USE_CHECKBOX   1\n\n#define LV_USE_DROPDOWN   1   /*Requires: lv_label*/\n\n#define LV_USE_IMG        1   /*Requires: lv_label*/\n\n#ifndef LV_USE_LABEL\n#define LV_USE_LABEL      1\n#endif // LV_USE_LABEL\n#if LV_USE_LABEL\n    #define LV_LABEL_TEXT_SELECTION 1 /*Enable selecting text of the label*/\n    #define LV_LABEL_LONG_TXT_HINT 1  /*Store some extra info in labels to speed up drawing of very long texts*/\n#endif\n\n#define LV_USE_LINE       1\n\n#ifndef LV_USE_ROLLER\n#define LV_USE_ROLLER     1   /*Requires: lv_label*/\n#endif // LV_USE_ROLLER\n#if LV_USE_ROLLER\n    #define LV_ROLLER_INF_PAGES 7 /*Number of extra \"pages\" when the roller is infinite*/\n#endif\n\n#define LV_USE_SLIDER     1   /*Requires: lv_bar*/\n\n#define LV_USE_SWITCH     1\n\n#ifndef LV_USE_TEXTAREA\n#define LV_USE_TEXTAREA   1   /*Requires: lv_label*/\n#endif // LV_USE_TEXTAREA\n#if LV_USE_TEXTAREA != 0\n    #define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500    /*ms*/\n#endif\n\n#define LV_USE_TABLE      1\n\n/*==================\n * EXTRA COMPONENTS\n *==================*/\n\n/*-----------\n * Widgets\n *----------*/\n#ifndef LV_USE_CALENDAR\n#define LV_USE_CALENDAR   1\n#endif // LV_USE_CALENDAR\n#if LV_USE_CALENDAR\n    #define LV_CALENDAR_WEEK_STARTS_MONDAY 0\n    #if LV_CALENDAR_WEEK_STARTS_MONDAY\n        #define LV_CALENDAR_DEFAULT_DAY_NAMES {\"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\", \"Su\"}\n    #else\n        #define LV_CALENDAR_DEFAULT_DAY_NAMES {\"Su\", \"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\"}\n    #endif\n\n    #define LV_CALENDAR_DEFAULT_MONTH_NAMES {\"January\", \"February\", \"March\",  \"April\", \"May\",  \"June\", \"July\", \"August\", \"September\", \"October\", \"November\", \"December\"}\n    #define LV_USE_CALENDAR_HEADER_ARROW 1\n    #define LV_USE_CALENDAR_HEADER_DROPDOWN 1\n#endif  /*LV_USE_CALENDAR*/\n\n#define LV_USE_CHART      1\n\n#define LV_USE_COLORWHEEL 1\n\n#define LV_USE_IMGBTN     1\n\n#define LV_USE_KEYBOARD   1\n\n#define LV_USE_LED        1\n\n#define LV_USE_LIST       1\n\n#define LV_USE_MENU       1\n\n#define LV_USE_METER      1\n\n#define LV_USE_MSGBOX     1\n\n#define LV_USE_SPINBOX    1\n\n#define LV_USE_SPINNER    1\n\n#define LV_USE_TABVIEW    1\n\n#define LV_USE_TILEVIEW   1\n\n#define LV_USE_WIN        1\n\n#ifndef LV_USE_SPAN\n#define LV_USE_SPAN       1\n#endif // LV_USE_SPAN\n#if LV_USE_SPAN\n    /*A line text can contain maximum num of span descriptor */\n    #define LV_SPAN_SNIPPET_STACK_SIZE 64\n#endif\n\n/*-----------\n * Themes\n *----------*/\n\n/*A simple, impressive and very complete theme*/\n#ifndef LV_USE_THEME_DEFAULT\n#define LV_USE_THEME_DEFAULT 1\n#endif // LV_USE_THEME_DEFAULT\n#if LV_USE_THEME_DEFAULT\n\n    /*0: Light mode; 1: Dark mode*/\n    #define LV_THEME_DEFAULT_DARK 0\n\n    /*1: Enable grow on press*/\n    #define LV_THEME_DEFAULT_GROW 1\n\n    /*Default transition time in [ms]*/\n    #define LV_THEME_DEFAULT_TRANSITION_TIME 80\n#endif /*LV_USE_THEME_DEFAULT*/\n\n/*A very simple theme that is a good starting point for a custom theme*/\n#define LV_USE_THEME_BASIC 1\n\n/*A theme designed for monochrome displays*/\n#define LV_USE_THEME_MONO 1\n\n/*-----------\n * Layouts\n *----------*/\n\n/*A layout similar to Flexbox in CSS.*/\n#define LV_USE_FLEX 1\n\n/*A layout similar to Grid in CSS.*/\n#define LV_USE_GRID 1\n\n/*---------------------\n * 3rd party libraries\n *--------------------*/\n\n/*File system interfaces for common APIs */\n\n/*API for fopen, fread, etc*/\n#ifndef LV_USE_FS_STDIO\n#define LV_USE_FS_STDIO 0\n#endif // LV_USE_FS_STDIO\n#if LV_USE_FS_STDIO\n    #define LV_FS_STDIO_LETTER '\\0'     /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/\n    #define LV_FS_STDIO_PATH \"\"         /*Set the working directory. File/directory paths will be appended to it.*/\n    #define LV_FS_STDIO_CACHE_SIZE  0   /*>0 to cache this number of bytes in lv_fs_read()*/\n#endif\n\n/*API for open, read, etc*/\n#ifndef LV_USE_FS_POSIX\n#define LV_USE_FS_POSIX 0\n#endif // LV_USE_FS_POSIX\n#if LV_USE_FS_POSIX\n    #define LV_FS_POSIX_LETTER '\\0'     /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/\n    #define LV_FS_POSIX_PATH \"\"         /*Set the working directory. File/directory paths will be appended to it.*/\n    #define LV_FS_POSIX_CACHE_SIZE  0   /*>0 to cache this number of bytes in lv_fs_read()*/\n#endif\n\n/*API for CreateFile, ReadFile, etc*/\n#ifndef LV_USE_FS_WIN32\n#define LV_USE_FS_WIN32 0\n#endif // LV_USE_FS_WIN32\n#if LV_USE_FS_WIN32\n    #define LV_FS_WIN32_LETTER  '\\0'    /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/\n    #define LV_FS_WIN32_PATH \"\"         /*Set the working directory. File/directory paths will be appended to it.*/\n    #define LV_FS_WIN32_CACHE_SIZE 0    /*>0 to cache this number of bytes in lv_fs_read()*/\n#endif\n\n/*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/\n#ifndef LV_USE_FS_FATFS\n#define LV_USE_FS_FATFS  0\n#endif // LV_USE_FS_FATFS\n#if LV_USE_FS_FATFS\n    #define LV_FS_FATFS_LETTER '\\0'     /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/\n    #define LV_FS_FATFS_CACHE_SIZE 0    /*>0 to cache this number of bytes in lv_fs_read()*/\n#endif\n\n/*PNG decoder library*/\n#define LV_USE_PNG 0\n\n/*BMP decoder library*/\n#define LV_USE_BMP 0\n\n/* JPG + split JPG decoder library.\n * Split JPG is a custom format optimized for embedded systems. */\n#define LV_USE_SJPG 0\n\n/*GIF decoder library*/\n#define LV_USE_GIF 0\n\n/*QR code library*/\n#define LV_USE_QRCODE 0\n\n/*FreeType library*/\n#ifndef LV_USE_FREETYPE\n#define LV_USE_FREETYPE 0\n#endif // LV_USE_FREETYPE\n#if LV_USE_FREETYPE\n    /*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/\n    #define LV_FREETYPE_CACHE_SIZE (16 * 1024)\n    #if LV_FREETYPE_CACHE_SIZE >= 0\n        /* 1: bitmap cache use the sbit cache, 0:bitmap cache use the image cache. */\n        /* sbit cache:it is much more memory efficient for small bitmaps(font size < 256) */\n        /* if font size >= 256, must be configured as image cache */\n        #define LV_FREETYPE_SBIT_CACHE 0\n        /* Maximum number of opened FT_Face/FT_Size objects managed by this cache instance. */\n        /* (0:use system defaults) */\n        #define LV_FREETYPE_CACHE_FT_FACES 0\n        #define LV_FREETYPE_CACHE_FT_SIZES 0\n    #endif\n#endif\n\n/*Rlottie library*/\n#define LV_USE_RLOTTIE 0\n\n/*FFmpeg library for image decoding and playing videos\n *Supports all major image formats so do not enable other image decoder with it*/\n#ifndef LV_USE_FFMPEG\n#define LV_USE_FFMPEG  0\n#endif // LV_USE_FFMPEG\n#if LV_USE_FFMPEG\n    /*Dump input information to stderr*/\n    #define LV_FFMPEG_AV_DUMP_FORMAT 0\n#endif\n\n/*-----------\n * Others\n *----------*/\n\n/*1: Enable API to take snapshot for object*/\n#define LV_USE_SNAPSHOT 0\n\n/*1: Enable Monkey test*/\n#define LV_USE_MONKEY   0\n\n/*1: Enable grid navigation*/\n#define LV_USE_GRIDNAV  0\n\n/*==================\n* EXAMPLES\n*==================*/\n\n/*Enable the examples to be built with the library*/\n#define LV_BUILD_EXAMPLES 1\n\n/*===================\n * DEMO USAGE\n ====================*/\n\n/*Show some widget. It might be required to increase `LV_MEM_SIZE` */\n#ifndef LV_USE_DEMO_WIDGETS\n#define LV_USE_DEMO_WIDGETS        0\n#endif // LV_USE_DEMO_WIDGETS\n#if LV_USE_DEMO_WIDGETS\n#define LV_DEMO_WIDGETS_SLIDESHOW  0\n#endif\n\n/*Demonstrate the usage of encoder and keyboard*/\n#define LV_USE_DEMO_KEYPAD_AND_ENCODER     0\n\n/*Benchmark your system*/\n#define LV_USE_DEMO_BENCHMARK   0\n\n/*Stress test for LVGL*/\n#define LV_USE_DEMO_STRESS      0\n\n/*Music player demo*/\n#ifndef LV_USE_DEMO_MUSIC\n#define LV_USE_DEMO_MUSIC       0\n#endif // LV_USE_DEMO_MUSIC\n#if LV_USE_DEMO_MUSIC\n# define LV_DEMO_MUSIC_SQUARE       0\n# define LV_DEMO_MUSIC_LANDSCAPE    0\n# define LV_DEMO_MUSIC_ROUND        0\n# define LV_DEMO_MUSIC_LARGE        0\n# define LV_DEMO_MUSIC_AUTO_PLAY    0\n#endif\n\n/*--END OF LV_CONF_H--*/\n\n#endif /*LV_CONF_H*/\n\n#endif /*End of \"Content enable\"*/\n"
  },
  {
    "path": "platforms/pin_defs.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n// useful for direct pin mapping\n#define NO_PIN (pin_t)(~0)\n\n#if __has_include(\"_pin_defs.h\")\n#    include \"_pin_defs.h\" /* Include the platforms pin defs */\n#endif\n"
  },
  {
    "path": "platforms/progmem.h",
    "content": "#pragma once\n\n#if defined(__AVR__)\n#    include <avr/pgmspace.h>\n#else\n#    include <string.h>\n#    define PROGMEM\n#    define PSTR(x) x\n#    define PGM_P const char*\n#    define memcpy_P(dest, src, n) memcpy(dest, src, n)\n#    define pgm_read_byte(address_short) *((uint8_t*)(address_short))\n#    define pgm_read_word(address_short) *((uint16_t*)(address_short))\n#    define pgm_read_dword(address_short) *((uint32_t*)(address_short))\n#    define pgm_read_ptr(address_short) *((void**)(address_short))\n#    define strcmp_P(s1, s2) strcmp(s1, s2)\n#    define strcpy_P(dest, src) strcpy(dest, src)\n#    define strlen_P(src) strlen(src)\n#endif\n"
  },
  {
    "path": "platforms/sleep_led.h",
    "content": "#pragma once\n\n#ifdef SLEEP_LED_ENABLE\n\nvoid sleep_led_init(void);\nvoid sleep_led_enable(void);\nvoid sleep_led_disable(void);\nvoid sleep_led_toggle(void);\n\n#else\n\n#    define sleep_led_init()\n#    define sleep_led_enable()\n#    define sleep_led_disable()\n#    define sleep_led_toggle()\n\n#endif\n"
  },
  {
    "path": "platforms/suspend.c",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"suspend.h\"\n#include \"matrix.h\"\n\n// TODO: Move to more correct location\n__attribute__((weak)) void matrix_power_up(void) {}\n__attribute__((weak)) void matrix_power_down(void) {}\n\n/** \\brief Run user level Power down\n *\n * FIXME: needs doc\n */\n__attribute__((weak)) void suspend_power_down_user(void) {}\n\n/** \\brief Run keyboard level Power down\n *\n * FIXME: needs doc\n */\n__attribute__((weak)) void suspend_power_down_kb(void) {\n    suspend_power_down_user();\n}\n\n/** \\brief run user level code immediately after wakeup\n *\n * FIXME: needs doc\n */\n__attribute__((weak)) void suspend_wakeup_init_user(void) {}\n\n/** \\brief run keyboard level code immediately after wakeup\n *\n * FIXME: needs doc\n */\n__attribute__((weak)) void suspend_wakeup_init_kb(void) {\n    suspend_wakeup_init_user();\n}\n\n/** \\brief suspend wakeup condition\n *\n * FIXME: needs doc\n */\nbool suspend_wakeup_condition(void) {\n    matrix_power_up();\n    matrix_scan();\n    matrix_power_down();\n    for (uint8_t r = 0; r < MATRIX_ROWS; r++) {\n        if (matrix_get_row(r)) return true;\n    }\n    return false;\n}\n"
  },
  {
    "path": "platforms/suspend.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\nvoid suspend_power_down(void);\nbool suspend_wakeup_condition(void);\nvoid suspend_wakeup_init(void);\n\nvoid suspend_wakeup_init_user(void);\nvoid suspend_wakeup_init_kb(void);\nvoid suspend_wakeup_init_quantum(void);\nvoid suspend_power_down_user(void);\nvoid suspend_power_down_kb(void);\nvoid suspend_power_down_quantum(void);\n\n#ifndef USB_SUSPEND_WAKEUP_DELAY\n#    define USB_SUSPEND_WAKEUP_DELAY 0\n#endif\n"
  },
  {
    "path": "platforms/synchronization_util.c",
    "content": "// Copyright 2023 Sergey Vlasov (@sigprof)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"synchronization_util.h\"\n\n// Generate out-of-line copies for inline functions defined in synchronization_util.h.\n\n#if !defined(PLATFORM_SUPPORTS_SYNCHRONIZATION)\n#    if defined(SPLIT_KEYBOARD)\nextern inline void split_shared_memory_lock(void);\nextern inline void split_shared_memory_unlock(void);\n#    endif\n#endif\n\n#if defined(SPLIT_KEYBOARD)\nQMK_IMPLEMENT_AUTOUNLOCK_HELPERS(split_shared_memory)\n#endif\n"
  },
  {
    "path": "platforms/synchronization_util.h",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#if defined(PLATFORM_SUPPORTS_SYNCHRONIZATION)\n#    if defined(SPLIT_KEYBOARD)\nvoid split_shared_memory_lock(void);\nvoid split_shared_memory_unlock(void);\n#    endif\n#else\n#    if defined(SPLIT_KEYBOARD)\ninline void split_shared_memory_lock(void){};\ninline void split_shared_memory_unlock(void){};\n#    endif\n#endif\n\n/* GCCs cleanup attribute expects a function with one parameter, which is a\n * pointer to a type compatible with the variable. As we don't want to expose\n * the platforms internal mutex type this workaround with auto generated adapter\n * function is defined */\n#define QMK_DECLARE_AUTOUNLOCK_HELPERS(prefix)                              \\\n    inline unsigned prefix##_autounlock_lock_helper(void) {                 \\\n        prefix##_lock();                                                    \\\n        return 0;                                                           \\\n    }                                                                       \\\n                                                                            \\\n    inline void prefix##_autounlock_unlock_helper(unsigned* unused_guard) { \\\n        prefix##_unlock();                                                  \\\n    }\n\n/* Generate an out-of-line implementation in case the inline functions defined\n * by the above macro don't actually get inlined. */\n#define QMK_IMPLEMENT_AUTOUNLOCK_HELPERS(prefix)                  \\\n    extern inline unsigned prefix##_autounlock_lock_helper(void); \\\n    extern inline void     prefix##_autounlock_unlock_helper(unsigned* unused_guard);\n\n/* Convinience macro the automatically generate the correct RAII-style\n * lock_autounlock function macro */\n#define QMK_DECLARE_AUTOUNLOCK_CALL(prefix) unsigned prefix##_guard __attribute__((unused, cleanup(prefix##_autounlock_unlock_helper))) = prefix##_autounlock_lock_helper\n\n#if defined(SPLIT_KEYBOARD)\nQMK_DECLARE_AUTOUNLOCK_HELPERS(split_shared_memory)\n\n/**\n * @brief Acquire exclusive access to the split keyboard shared memory, by\n * calling the platforms `split_shared_memory_lock()` function. The lock is\n * automatically released by calling the platforms `split_shared_memory_unlock()`\n * function. This happens when the block where\n * `split_shared_memory_lock_autounlock()` is called in goes out of scope i.e.\n * when the enclosing function returns.\n */\n#    define split_shared_memory_lock_autounlock QMK_DECLARE_AUTOUNLOCK_CALL(split_shared_memory)\n#endif\n"
  },
  {
    "path": "platforms/test/_wait.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <inttypes.h>\n\nvoid wait_ms(uint32_t ms);\n#define wait_us(us) wait_ms(us / 1000)\n#define waitInputPinDelay()\n"
  },
  {
    "path": "platforms/test/bootloaders/none.c",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"bootloader.h\"\n\nvoid bootloader_jump(void) {}\nvoid mcu_reset(void) {}\n"
  },
  {
    "path": "platforms/test/drivers/audio_pwm.h",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n"
  },
  {
    "path": "platforms/test/drivers/audio_pwm_hardware.c",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"audio.h\"\n\nvoid audio_driver_initialize_impl(void) {}\nvoid audio_driver_start_impl() {}\nvoid audio_driver_stop_impl() {}\n"
  },
  {
    "path": "platforms/test/eeprom.c",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"eeprom.h\"\n\nstatic uint8_t buffer[TOTAL_EEPROM_BYTE_COUNT];\n\nuint8_t eeprom_read_byte(const uint8_t *addr) {\n    uintptr_t offset = (uintptr_t)addr;\n    return buffer[offset];\n}\n\nvoid eeprom_write_byte(uint8_t *addr, uint8_t value) {\n    uintptr_t offset = (uintptr_t)addr;\n    buffer[offset]   = value;\n}\n\nuint16_t eeprom_read_word(const uint16_t *addr) {\n    const uint8_t *p = (const uint8_t *)addr;\n    return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8);\n}\n\nuint32_t eeprom_read_dword(const uint32_t *addr) {\n    const uint8_t *p = (const uint8_t *)addr;\n    return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24);\n}\n\nvoid eeprom_read_block(void *buf, const void *addr, size_t len) {\n    const uint8_t *p    = (const uint8_t *)addr;\n    uint8_t *      dest = (uint8_t *)buf;\n    while (len--) {\n        *dest++ = eeprom_read_byte(p++);\n    }\n}\n\nvoid eeprom_write_word(uint16_t *addr, uint16_t value) {\n    uint8_t *p = (uint8_t *)addr;\n    eeprom_write_byte(p++, value);\n    eeprom_write_byte(p, value >> 8);\n}\n\nvoid eeprom_write_dword(uint32_t *addr, uint32_t value) {\n    uint8_t *p = (uint8_t *)addr;\n    eeprom_write_byte(p++, value);\n    eeprom_write_byte(p++, value >> 8);\n    eeprom_write_byte(p++, value >> 16);\n    eeprom_write_byte(p, value >> 24);\n}\n\nvoid eeprom_write_block(const void *buf, void *addr, size_t len) {\n    uint8_t *      p   = (uint8_t *)addr;\n    const uint8_t *src = (const uint8_t *)buf;\n    while (len--) {\n        eeprom_write_byte(p++, *src++);\n    }\n}\n\nvoid eeprom_update_byte(uint8_t *addr, uint8_t value) {\n    eeprom_write_byte(addr, value);\n}\n\nvoid eeprom_update_word(uint16_t *addr, uint16_t value) {\n    uint8_t *p = (uint8_t *)addr;\n    eeprom_write_byte(p++, value);\n    eeprom_write_byte(p, value >> 8);\n}\n\nvoid eeprom_update_dword(uint32_t *addr, uint32_t value) {\n    uint8_t *p = (uint8_t *)addr;\n    eeprom_write_byte(p++, value);\n    eeprom_write_byte(p++, value >> 8);\n    eeprom_write_byte(p++, value >> 16);\n    eeprom_write_byte(p, value >> 24);\n}\n\nvoid eeprom_update_block(const void *buf, void *addr, size_t len) {\n    uint8_t *      p   = (uint8_t *)addr;\n    const uint8_t *src = (const uint8_t *)buf;\n    while (len--) {\n        eeprom_write_byte(p++, *src++);\n    }\n}\n"
  },
  {
    "path": "platforms/test/eeprom_legacy_emulated_flash_tests.cpp",
    "content": "/* Copyright 2021 by Don Kjer\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\nextern \"C\" {\n#include \"eeprom.h\"\n}\n\n/* Mock Flash Parameters:\n *\n * === Large Layout ===\n * flash size: 65536\n * page size: 2048\n * density pages: 16\n * Simulated EEPROM size: 16384\n *\n * FlashBuf Layout:\n * [Unused | Compact |  Write Log  ]\n * [0......|32768......|49152......65535]\n *\n * === Tiny Layout ===\n * flash size: 1024\n * page size: 512\n * density pages: 1\n * Simulated EEPROM size: 256\n *\n * FlashBuf Layout:\n * [Unused | Compact |  Write Log  ]\n * [0......|512......|768......1023]\n *\n */\n\n#define LOG_SIZE EEPROM_SIZE\n#define LOG_BASE (MOCK_FLASH_SIZE - LOG_SIZE)\n#define EEPROM_BASE (LOG_BASE - EEPROM_SIZE)\n\n/* Log encoding helpers */\n#define BYTE_VALUE(addr, value) (((addr) << 8) | (value))\n#define WORD_ZERO(addr) (0x8000 | ((addr) >> 1))\n#define WORD_ONE(addr) (0xA000 | ((addr) >> 1))\n#define WORD_NEXT(addr) (0xE000 | (((addr)-0x80) >> 1))\n\nclass EepromStm32Test : public testing::Test {\n   public:\n    EepromStm32Test() {}\n    ~EepromStm32Test() {}\n\n   protected:\n    void SetUp() override {\n        EEPROM_Erase();\n    }\n\n    void TearDown() override {\n#ifdef EEPROM_DEBUG\n        dumpEepromDataBuf();\n#endif\n    }\n};\n\nTEST_F(EepromStm32Test, TestErase) {\n    EEPROM_WriteDataByte(0, 0x42);\n    EEPROM_Erase();\n    EXPECT_EQ(EEPROM_ReadDataByte(0), 0);\n    EXPECT_EQ(EEPROM_ReadDataByte(1), 0);\n}\n\nTEST_F(EepromStm32Test, TestReadGarbage) {\n    uint8_t garbage = 0x3c;\n    for (int i = 0; i < MOCK_FLASH_SIZE; ++i) {\n        garbage ^= 0xa3;\n        garbage += i;\n        FlashBuf[i] = garbage;\n    }\n    EEPROM_Init(); // Just verify we don't crash\n}\n\nTEST_F(EepromStm32Test, TestWriteBadAddress) {\n    EXPECT_EQ(EEPROM_WriteDataByte(EEPROM_SIZE, 0x42), FLASH_BAD_ADDRESS);\n    EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE - 1, 0xbeef), FLASH_BAD_ADDRESS);\n    EXPECT_EQ(EEPROM_WriteDataWord(EEPROM_SIZE, 0xbeef), FLASH_BAD_ADDRESS);\n}\n\nTEST_F(EepromStm32Test, TestReadBadAddress) {\n    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE), 0xFF);\n    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 1), 0xFFFF);\n    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE), 0xFFFF);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 3)), 0xFF000000);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)EEPROM_SIZE), 0xFFFFFFFF);\n}\n\nTEST_F(EepromStm32Test, TestReadByte) {\n    /* Direct compacted-area baseline: Address < 0x80 */\n    FlashBuf[EEPROM_BASE + 2] = ~0xef;\n    FlashBuf[EEPROM_BASE + 3] = ~0xbe;\n    /* Direct compacted-area baseline: Address >= 0x80 */\n    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78;\n    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56;\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef);\n    EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe);\n    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78);\n    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56);\n    /* Write Log byte value */\n    FlashBuf[LOG_BASE]     = 0x65;\n    FlashBuf[LOG_BASE + 1] = 3;\n    /* Write Log word value */\n    *(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_NEXT(EEPROM_SIZE - 2);\n    *(uint16_t*)&FlashBuf[LOG_BASE + 4] = ~0x9abc;\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef);\n    EXPECT_EQ(EEPROM_ReadDataByte(3), 0x65);\n    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0xbc);\n    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x9a);\n}\n\nTEST_F(EepromStm32Test, TestWriteByte) {\n    /* Direct compacted-area baseline: Address < 0x80 */\n    EEPROM_WriteDataByte(2, 0xef);\n    EEPROM_WriteDataByte(3, 0xbe);\n    /* Direct compacted-area baseline: Address >= 0x80 */\n    EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78);\n    EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56);\n    /* Check values */\n    /* First write in each aligned word should have been direct */\n    EXPECT_EQ(FlashBuf[EEPROM_BASE + 2], (uint8_t)~0xef);\n    EXPECT_EQ(FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint8_t)~0x78);\n\n    /* Second write per aligned word requires a log entry */\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(3, 0xbe));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(EEPROM_SIZE - 1));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0x5678);\n}\n\nTEST_F(EepromStm32Test, TestByteRoundTrip) {\n    /* Direct compacted-area: Address < 0x80 */\n    EEPROM_WriteDataWord(0, 0xdead);\n    EEPROM_WriteDataByte(2, 0xef);\n    EEPROM_WriteDataByte(3, 0xbe);\n    /* Direct compacted-area: Address >= 0x80 */\n    EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x78);\n    EEPROM_WriteDataByte(EEPROM_SIZE - 1, 0x56);\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataByte(0), 0xad);\n    EXPECT_EQ(EEPROM_ReadDataByte(1), 0xde);\n    EXPECT_EQ(EEPROM_ReadDataByte(2), 0xef);\n    EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe);\n    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x78);\n    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56);\n    /* Write log entries */\n    EEPROM_WriteDataByte(2, 0x80);\n    EEPROM_WriteDataByte(EEPROM_SIZE - 2, 0x3c);\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataByte(2), 0x80);\n    EXPECT_EQ(EEPROM_ReadDataByte(3), 0xbe);\n    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 2), 0x3c);\n    EXPECT_EQ(EEPROM_ReadDataByte(EEPROM_SIZE - 1), 0x56);\n}\n\nTEST_F(EepromStm32Test, TestReadWord) {\n    /* Direct compacted-area baseline: Address < 0x80 */\n    FlashBuf[EEPROM_BASE + 0] = ~0xad;\n    FlashBuf[EEPROM_BASE + 1] = ~0xde;\n    /* Direct compacted-area baseline: Address >= 0x80 */\n    FlashBuf[EEPROM_BASE + 200]             = ~0xcd;\n    FlashBuf[EEPROM_BASE + 201]             = ~0xab;\n    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4] = ~0x34;\n    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 3] = ~0x12;\n    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2] = ~0x78;\n    FlashBuf[EEPROM_BASE + EEPROM_SIZE - 1] = ~0x56;\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead);\n    EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd);\n    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234);\n    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678);\n    /* Write Log word zero-encoded */\n    *(uint16_t*)&FlashBuf[LOG_BASE] = WORD_ZERO(200);\n    /* Write Log word one-encoded */\n    *(uint16_t*)&FlashBuf[LOG_BASE + 2] = WORD_ONE(EEPROM_SIZE - 4);\n    /* Write Log word value */\n    *(uint16_t*)&FlashBuf[LOG_BASE + 4] = WORD_NEXT(EEPROM_SIZE - 2);\n    *(uint16_t*)&FlashBuf[LOG_BASE + 6] = ~0x9abc;\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataWord(200), 0);\n    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 1);\n    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x9abc);\n}\n\nTEST_F(EepromStm32Test, TestWriteWord) {\n    /* Direct compacted-area: Address < 0x80 */\n    EEPROM_WriteDataWord(0, 0xdead); // Aligned\n    EEPROM_WriteDataWord(3, 0xbeef); // Unaligned\n    /* Direct compacted-area: Address >= 0x80 */\n    EEPROM_WriteDataWord(200, 0xabcd); // Aligned\n    EEPROM_WriteDataWord(203, 0x9876); // Unaligned\n    EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234);\n    EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678);\n    /* Write Log word zero-encoded */\n    EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0);\n    /* Write Log word one-encoded */\n    EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1);\n    /* Write Log word value aligned */\n    EEPROM_WriteDataWord(200, 0x4321); // Aligned\n    /* Write Log word value unaligned */\n    EEPROM_WriteDataByte(202, 0x3c);   // Set neighboring byte\n    EEPROM_WriteDataWord(203, 0xcdef); // Unaligned\n    /* Check values */\n    /* Direct compacted-area */\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE], (uint16_t)~0xdead);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 3], (uint16_t)~0xbeef);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + 200], (uint16_t)~0xabcd);\n    EXPECT_EQ(FlashBuf[EEPROM_BASE + 203], (uint8_t)~0x76);\n    EXPECT_EQ(FlashBuf[EEPROM_BASE + 204], (uint8_t)~0x98);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 4], (uint16_t)~0x1234);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[EEPROM_BASE + EEPROM_SIZE - 2], (uint16_t)~0x5678);\n    /* Write Log word zero-encoded */\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], WORD_ZERO(EEPROM_SIZE - 4));\n    /* Write Log word one-encoded */\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_ONE(EEPROM_SIZE - 2));\n    /* Write Log word value aligned */\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], WORD_NEXT(200));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], (uint16_t)~0x4321);\n    /* Write Log word value unaligned */\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], WORD_NEXT(202));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], (uint16_t)~0x763c);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(202));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xef3c);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(204));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0x00cd);\n}\n\nTEST_F(EepromStm32Test, TestWordRoundTrip) {\n    /* Direct compacted-area: Address < 0x80 */\n    EEPROM_WriteDataWord(0, 0xdead); // Aligned\n    EEPROM_WriteDataWord(3, 0xbeef); // Unaligned\n    /* Direct compacted-area: Address >= 0x80 */\n    EEPROM_WriteDataWord(200, 0xabcd); // Aligned\n    EEPROM_WriteDataWord(203, 0x9876); // Unaligned\n    EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0x1234);\n    EEPROM_WriteDataWord(EEPROM_SIZE - 2, 0x5678);\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataWord(0), 0xdead);\n    EXPECT_EQ(EEPROM_ReadDataWord(3), 0xbeef);\n    EXPECT_EQ(EEPROM_ReadDataWord(200), 0xabcd);\n    EXPECT_EQ(EEPROM_ReadDataWord(203), 0x9876);\n    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0x1234);\n    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 0x5678);\n\n    /* Write Log word zero-encoded */\n    EEPROM_WriteDataWord(EEPROM_SIZE - 4, 0);\n    /* Write Log word one-encoded */\n    EEPROM_WriteDataWord(EEPROM_SIZE - 2, 1);\n    /* Write Log word value aligned */\n    EEPROM_WriteDataWord(200, 0x4321); // Aligned\n    /* Write Log word value unaligned */\n    EEPROM_WriteDataByte(202, 0x3c);   // Set neighboring byte\n    EEPROM_WriteDataWord(203, 0xcdef); // Unaligned\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataWord(200), 0x4321);\n    EXPECT_EQ(EEPROM_ReadDataByte(202), 0x3c);\n    EXPECT_EQ(EEPROM_ReadDataWord(203), 0xcdef);\n    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 4), 0);\n    EXPECT_EQ(EEPROM_ReadDataWord(EEPROM_SIZE - 2), 1);\n}\n\nTEST_F(EepromStm32Test, TestByteWordBoundary) {\n    /* Direct compacted-area write */\n    EEPROM_WriteDataWord(0x7e, 0xdead);\n    EEPROM_WriteDataWord(0x80, 0xbeef);\n    /* Byte log entry */\n    EEPROM_WriteDataByte(0x7f, 0x3c);\n    /* Word log entry */\n    EEPROM_WriteDataByte(0x80, 0x18);\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0x3cad);\n    EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xbe18);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], BYTE_VALUE(0x7f, 0x3c));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 2], WORD_NEXT(0x80));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 4], (uint16_t)~0xbe18);\n    /* Byte log entries */\n    EEPROM_WriteDataWord(0x7e, 0xcafe);\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataWord(0x7e), 0xcafe);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 6], BYTE_VALUE(0x7e, 0xfe));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 8], BYTE_VALUE(0x7f, 0xca));\n    /* Byte and Word log entries */\n    EEPROM_WriteDataWord(0x7f, 0xba5e);\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataWord(0x7f), 0xba5e);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 10], BYTE_VALUE(0x7f, 0x5e));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 12], WORD_NEXT(0x80));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 14], (uint16_t)~0xbeba);\n    /* Word log entry */\n    EEPROM_WriteDataWord(0x80, 0xf00d);\n    /* Check values */\n    EEPROM_Init();\n    EXPECT_EQ(EEPROM_ReadDataWord(0x80), 0xf00d);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 16], WORD_NEXT(0x80));\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + 18], (uint16_t)~0xf00d);\n}\n\nTEST_F(EepromStm32Test, TestDWordRoundTrip) {\n    /* Direct compacted-area: Address < 0x80 */\n    eeprom_write_dword((uint32_t*)0, 0xdeadbeef); // Aligned\n    eeprom_write_dword((uint32_t*)9, 0x12345678); // Unaligned\n    /* Direct compacted-area: Address >= 0x80 */\n    eeprom_write_dword((uint32_t*)200, 0xfacef00d);\n    eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xba5eba11); // Aligned\n    eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0xcafed00d); // Unaligned\n    /* Check direct values */\n    EEPROM_Init();\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x12345678);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 0xfacef00d);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xba5eba11); // Aligned\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0xcafed00d); // Unaligned\n    /* Write Log byte encoded */\n    eeprom_write_dword((uint32_t*)0, 0xdecafbad);\n    eeprom_write_dword((uint32_t*)9, 0x87654321);\n    /* Write Log word encoded */\n    eeprom_write_dword((uint32_t*)200, 1);\n    /* Write Log word value aligned */\n    eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 4), 0xdeadc0de); // Aligned\n    eeprom_write_dword((uint32_t*)(EEPROM_SIZE - 9), 0x6789abcd); // Unaligned\n    /* Check log values */\n    EEPROM_Init();\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdecafbad);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)9), 0x87654321);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)200), 1);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 4)), 0xdeadc0de); // Aligned\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)(EEPROM_SIZE - 9)), 0x6789abcd); // Unaligned\n}\n\nTEST_F(EepromStm32Test, TestBlockRoundTrip) {\n    char  src0[] = \"0123456789abcdef\";\n    void* src1   = (void*)&src0[1];\n    /* Various alignments of src & dst, Address < 0x80 */\n    eeprom_write_block(src0, (void*)0, sizeof(src0));\n    eeprom_write_block(src0, (void*)21, sizeof(src0));\n    eeprom_write_block(src1, (void*)40, sizeof(src0) - 1);\n    eeprom_write_block(src1, (void*)61, sizeof(src0) - 1);\n    /* Various alignments of src & dst, Address >= 0x80 */\n    eeprom_write_block(src0, (void*)140, sizeof(src0));\n    eeprom_write_block(src0, (void*)161, sizeof(src0));\n    eeprom_write_block(src1, (void*)180, sizeof(src0) - 1);\n    eeprom_write_block(src1, (void*)201, sizeof(src0) - 1);\n\n    /* Check values */\n    EEPROM_Init();\n\n    char  dstBuf[256] = {0};\n    char* dst0a       = (char*)dstBuf;\n    char* dst0b       = (char*)&dstBuf[20];\n    char* dst1a       = (char*)&dstBuf[41];\n    char* dst1b       = (char*)&dstBuf[61];\n    char* dst0c       = (char*)&dstBuf[80];\n    char* dst0d       = (char*)&dstBuf[100];\n    char* dst1c       = (char*)&dstBuf[121];\n    char* dst1d       = (char*)&dstBuf[141];\n    eeprom_read_block((void*)dst0a, (void*)0, sizeof(src0));\n    eeprom_read_block((void*)dst0b, (void*)21, sizeof(src0));\n    eeprom_read_block((void*)dst1a, (void*)40, sizeof(src0) - 1);\n    eeprom_read_block((void*)dst1b, (void*)61, sizeof(src0) - 1);\n    eeprom_read_block((void*)dst0c, (void*)140, sizeof(src0));\n    eeprom_read_block((void*)dst0d, (void*)161, sizeof(src0));\n    eeprom_read_block((void*)dst1c, (void*)180, sizeof(src0) - 1);\n    eeprom_read_block((void*)dst1d, (void*)201, sizeof(src0) - 1);\n    EXPECT_EQ(strcmp((char*)src0, dst0a), 0);\n    EXPECT_EQ(strcmp((char*)src0, dst0b), 0);\n    EXPECT_EQ(strcmp((char*)src0, dst0c), 0);\n    EXPECT_EQ(strcmp((char*)src0, dst0d), 0);\n    EXPECT_EQ(strcmp((char*)src1, dst1a), 0);\n    EXPECT_EQ(strcmp((char*)src1, dst1b), 0);\n    EXPECT_EQ(strcmp((char*)src1, dst1c), 0);\n    EXPECT_EQ(strcmp((char*)src1, dst1d), 0);\n}\n\nTEST_F(EepromStm32Test, TestCompaction) {\n    /* Direct writes */\n    eeprom_write_dword((uint32_t*)0, 0xdeadbeef);\n    eeprom_write_byte((uint8_t*)4, 0x3c);\n    eeprom_write_word((uint16_t*)6, 0xd00d);\n    eeprom_write_dword((uint32_t*)150, 0xcafef00d);\n    eeprom_write_dword((uint32_t*)200, 0x12345678);\n    /* Fill write log entries */\n    uint32_t i;\n    uint32_t val = 0xd8453c6b;\n    for (i = 0; i < (LOG_SIZE / (sizeof(uint32_t) * 2)); i++) {\n        val ^= 0x593ca5b3;\n        val += i;\n        eeprom_write_dword((uint32_t*)200, val);\n    }\n    /* Check values pre-compaction */\n    EEPROM_Init();\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef);\n    EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x3c);\n    EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val);\n    EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF);\n    EXPECT_NE(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF);\n    /* Run compaction */\n    eeprom_write_byte((uint8_t*)4, 0x1f);\n    EEPROM_Init();\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)0), 0xdeadbeef);\n    EXPECT_EQ(eeprom_read_byte((uint8_t*)4), 0x1f);\n    EXPECT_EQ(eeprom_read_word((uint16_t*)6), 0xd00d);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)150), 0xcafef00d);\n    EXPECT_EQ(eeprom_read_dword((uint32_t*)200), val);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE], 0xFFFF);\n    EXPECT_EQ(*(uint16_t*)&FlashBuf[LOG_BASE + LOG_SIZE - 2], 0xFFFF);\n}\n"
  },
  {
    "path": "platforms/test/eeprom_legacy_emulated_flash_tests.h",
    "content": "// Copyright 2018-2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"legacy_flash_ops.h\"\n#include \"eeprom_legacy_emulated_flash.h\"\n\n#define EEPROM_SIZE (FEE_PAGE_SIZE * FEE_PAGE_COUNT / 2)\n"
  },
  {
    "path": "platforms/test/hal.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n// Just here to please eeprom tests\n"
  },
  {
    "path": "platforms/test/hardware_id.c",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"hardware_id.h\"\n\nhardware_id_t get_hardware_id(void) {\n    hardware_id_t id = {0};\n    return id;\n}\n"
  },
  {
    "path": "platforms/test/legacy_flash_ops_mock.c",
    "content": "/* Copyright 2021 by Don Kjer\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <string.h>\n#include <stdbool.h>\n#include \"legacy_flash_ops.h\"\n\nuint8_t FlashBuf[MOCK_FLASH_SIZE] = {0};\n\nstatic bool flash_locked = true;\n\nFLASH_Status FLASH_ErasePage(uint32_t Page_Address) {\n    if (flash_locked) return FLASH_ERROR_WRP;\n    Page_Address -= (uintptr_t)FlashBuf;\n    Page_Address -= (Page_Address % FEE_PAGE_SIZE);\n    if (Page_Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS;\n    memset(&FlashBuf[Page_Address], '\\xff', FEE_PAGE_SIZE);\n    return FLASH_COMPLETE;\n}\n\nFLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {\n    if (flash_locked) return FLASH_ERROR_WRP;\n    Address -= (uintptr_t)FlashBuf;\n    if (Address >= MOCK_FLASH_SIZE) return FLASH_BAD_ADDRESS;\n    uint16_t oldData = *(uint16_t*)&FlashBuf[Address];\n    if (oldData == 0xFFFF || Data == 0) {\n        *(uint16_t*)&FlashBuf[Address] = Data;\n        return FLASH_COMPLETE;\n    } else {\n        return FLASH_ERROR_PG;\n    }\n}\n\nFLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) {\n    return FLASH_COMPLETE;\n}\nvoid FLASH_Unlock(void) {\n    flash_locked = false;\n}\nvoid FLASH_Lock(void) {\n    flash_locked = true;\n}\n"
  },
  {
    "path": "platforms/test/platform.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"platform_deps.h\"\n\nvoid platform_setup(void) {\n    // do nothing\n}\n"
  },
  {
    "path": "platforms/test/platform.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n// here just to please the build\n"
  },
  {
    "path": "platforms/test/platform.mk",
    "content": "SYSTEM_TYPE := $(shell gcc -dumpmachine)\nGCC_VERSION := $(shell gcc --version 2>/dev/null)\n\nCC = $(CC_PREFIX) gcc\nOBJCOPY =\nOBJDUMP =\nSIZE =\nAR =\nNM =\nHEX =\nEEP =\nBIN =\n\n\nCOMPILEFLAGS += -funsigned-char\nifeq ($(findstring clang, ${GCC_VERSION}),)\nCOMPILEFLAGS += -funsigned-bitfields\nendif\nCOMPILEFLAGS += -ffunction-sections\nCOMPILEFLAGS += -fdata-sections\nCOMPILEFLAGS += -fshort-enums\nifneq ($(findstring mingw, ${SYSTEM_TYPE}),)\nCOMPILEFLAGS += -mno-ms-bitfields\nendif\n\nCFLAGS += $(COMPILEFLAGS)\nifeq ($(findstring clang, ${GCC_VERSION}),)\nCFLAGS += -fno-inline-small-functions\nendif\nCFLAGS += -fno-strict-aliasing\n\nCXXFLAGS += $(COMPILEFLAGS)\nCXXFLAGS += -fno-exceptions\nCXXFLAGS += $(CXXSTANDARD)\n"
  },
  {
    "path": "platforms/test/platform_deps.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n// here just to please the build\n"
  },
  {
    "path": "platforms/test/rules.mk",
    "content": "eeprom_legacy_emulated_flash_DEFS  := -DEEPROM_TEST_HARNESS -DLEGACY_FLASH_OPS_MOCKED -DNO_PRINT -DFEE_FLASH_BASE=FlashBuf\neeprom_legacy_emulated_flash_tiny_DEFS := $(eeprom_legacy_emulated_flash_DEFS) \\\n\t-DFEE_MCU_FLASH_SIZE=1 \\\n\t-DMOCK_FLASH_SIZE=1024 \\\n\t-DFEE_PAGE_SIZE=512 \\\n\t-DFEE_PAGE_COUNT=1\neeprom_legacy_emulated_flash_large_DEFS := $(eeprom_legacy_emulated_flash_DEFS) \\\n\t-DFEE_MCU_FLASH_SIZE=64 \\\n\t-DMOCK_FLASH_SIZE=65536 \\\n\t-DFEE_PAGE_SIZE=2048 \\\n\t-DFEE_PAGE_COUNT=16\n\neeprom_legacy_emulated_flash_INC := \\\n\t$(PLATFORM_PATH)/chibios/drivers/eeprom/ \\\n\t$(PLATFORM_PATH)/chibios/drivers/flash/\neeprom_legacy_emulated_flash_tiny_INC := $(eeprom_legacy_emulated_flash_INC)\neeprom_legacy_emulated_flash_large_INC := $(eeprom_legacy_emulated_flash_INC)\n\neeprom_legacy_emulated_flash_SRC := \\\n\t$(TOP_DIR)/drivers/eeprom/eeprom_driver.c \\\n\t$(PLATFORM_PATH)/$(PLATFORM_KEY)/eeprom_legacy_emulated_flash_tests.cpp \\\n\t$(PLATFORM_PATH)/$(PLATFORM_KEY)/legacy_flash_ops_mock.c \\\n\t$(PLATFORM_PATH)/chibios/drivers/eeprom/eeprom_legacy_emulated_flash.c\neeprom_legacy_emulated_flash_tiny_SRC := $(eeprom_legacy_emulated_flash_SRC)\neeprom_legacy_emulated_flash_large_SRC := $(eeprom_legacy_emulated_flash_SRC)\n"
  },
  {
    "path": "platforms/test/suspend.c",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n"
  },
  {
    "path": "platforms/test/testlist.mk",
    "content": "TEST_LIST += eeprom_legacy_emulated_flash_tiny eeprom_legacy_emulated_flash_large\n"
  },
  {
    "path": "platforms/test/timer.c",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"timer.h\"\n#include <stdatomic.h>\n\nstatic atomic_uint_least32_t current_time      = 0;\nstatic atomic_uint_least32_t async_tick_amount = 0;\nstatic atomic_uint_least32_t access_counter    = 0;\n\nvoid simulate_async_tick(uint32_t t) {\n    async_tick_amount = t;\n}\n\nuint32_t timer_read_internal(void) {\n    return current_time;\n}\n\nuint32_t current_access_counter(void) {\n    return access_counter;\n}\n\nvoid reset_access_counter(void) {\n    access_counter = 0;\n}\n\nvoid timer_init(void) {\n    current_time      = 0;\n    async_tick_amount = 0;\n    access_counter    = 0;\n}\n\nvoid timer_clear(void) {\n    current_time      = 0;\n    async_tick_amount = 0;\n    access_counter    = 0;\n}\n\nuint16_t timer_read(void) {\n    return (uint16_t)timer_read32();\n}\n\nuint32_t timer_read32(void) {\n    if (access_counter++ > 0) {\n        current_time += async_tick_amount;\n    }\n    return current_time;\n}\n\nvoid set_time(uint32_t t) {\n    current_time   = t;\n    access_counter = 0;\n}\n\nvoid advance_time(uint32_t ms) {\n    current_time += ms;\n    access_counter = 0;\n}\n\nvoid wait_ms(uint32_t ms) {\n    advance_time(ms);\n}\n"
  },
  {
    "path": "platforms/timer.c",
    "content": "// Copyright 2023 Sergey Vlasov (@sigprof)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"timer.h\"\n\n// Generate out-of-line copies for inline functions defined in timer.h.\nextern inline fast_timer_t timer_read_fast(void);\nextern inline fast_timer_t timer_elapsed_fast(fast_timer_t last);\n\nuint16_t timer_elapsed(uint16_t last) {\n    return TIMER_DIFF_16(timer_read(), last);\n}\n\nuint32_t timer_elapsed32(uint32_t last) {\n    return TIMER_DIFF_32(timer_read32(), last);\n}\n"
  },
  {
    "path": "platforms/timer.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\nCopyright 2021 Simon Arlott\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#if __has_include_next(\"_timer.h\")\n#    include_next \"_timer.h\" /* Include the platform's _timer.h */\n#endif\n\n#include <stdint.h>\n\n#define TIMER_DIFF_8(a, b) (uint8_t)((a) - (b))\n#define TIMER_DIFF_16(a, b) (uint16_t)((a) - (b))\n#define TIMER_DIFF_32(a, b) (uint32_t)((a) - (b))\n#define TIMER_DIFF_RAW(a, b) TIMER_DIFF_8(a, b)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern volatile uint32_t timer_count;\n\nvoid     timer_init(void);\nvoid     timer_clear(void);\nvoid     timer_save(void);\nvoid     timer_restore(void);\nuint16_t timer_read(void);\nuint32_t timer_read32(void);\nuint16_t timer_elapsed(uint16_t last);\nuint32_t timer_elapsed32(uint32_t last);\n\n// Utility functions to check if a future time has expired & autmatically handle time wrapping if checked / reset frequently (half of max value)\n#define timer_expired(current, future) ((uint16_t)(current - future) < UINT16_MAX / 2)\n#define timer_expired32(current, future) ((uint32_t)(current - future) < UINT32_MAX / 2)\n\n// Use an appropriate timer integer size based on architecture (16-bit will overflow sooner)\n#if FAST_TIMER_T_SIZE < 32\n#    define TIMER_DIFF_FAST(a, b) TIMER_DIFF_16(a, b)\n#    define timer_expired_fast(current, future) timer_expired(current, future)\ntypedef uint16_t fast_timer_t;\nfast_timer_t inline timer_read_fast(void) {\n    return timer_read();\n}\nfast_timer_t inline timer_elapsed_fast(fast_timer_t last) {\n    return timer_elapsed(last);\n}\n#else\n#    define TIMER_DIFF_FAST(a, b) TIMER_DIFF_32(a, b)\n#    define timer_expired_fast(current, future) timer_expired32(current, future)\ntypedef uint32_t fast_timer_t;\nfast_timer_t inline timer_read_fast(void) {\n    return timer_read32();\n}\nfast_timer_t inline timer_elapsed_fast(fast_timer_t last) {\n    return timer_elapsed32(last);\n}\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "platforms/wait.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <inttypes.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#if __has_include_next(\"_wait.h\")\n#    include_next \"_wait.h\" /* Include the platforms _wait.h */\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/action.c",
    "content": "/*\nCopyright 2012,2013 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n#include <limits.h>\n\n#include \"host.h\"\n#include \"keycode.h\"\n#include \"keyboard.h\"\n#include \"mousekey.h\"\n#include \"programmable_button.h\"\n#include \"command.h\"\n#include \"led.h\"\n#include \"action_layer.h\"\n#include \"action_tapping.h\"\n#include \"action_util.h\"\n#include \"action.h\"\n#include \"wait.h\"\n#include \"keycode_config.h\"\n#include \"debug.h\"\n#include \"quantum.h\"\n\n#ifdef BACKLIGHT_ENABLE\n#    include \"backlight.h\"\n#endif\n\n#ifdef POINTING_DEVICE_ENABLE\n#    include \"pointing_device.h\"\n#endif\n\n#if defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE) && defined(SWAP_HANDS_ENABLE)\n#    include \"encoder.h\"\n#endif\n\nint tp_buttons;\n\n#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))\nbool     retro_tap_primed   = false;\nuint16_t retro_tap_curr_key = 0;\n#    if !(defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))\nuint8_t retro_tap_curr_mods = 0;\nuint8_t retro_tap_next_mods = 0;\n#    endif\n#endif\n\n#if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)\n#    include \"process_auto_shift.h\"\n#endif\n\n#ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY\n__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) {\n    return false;\n}\n#endif\n\n#ifdef RETRO_TAPPING_PER_KEY\n__attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) {\n    return false;\n}\n#endif\n\n/** \\brief Called to execute an action.\n *\n * FIXME: Needs documentation.\n */\nvoid action_exec(keyevent_t event) {\n    if (IS_EVENT(event)) {\n        ac_dprintf(\"\\n---- action_exec: start -----\\n\");\n        ac_dprintf(\"EVENT: \");\n        debug_event(event);\n        ac_dprintf(\"\\n\");\n#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))\n        uint16_t event_keycode = get_event_keycode(event, false);\n        if (event.pressed) {\n            retro_tap_primed   = false;\n            retro_tap_curr_key = event_keycode;\n        } else if (retro_tap_curr_key == event_keycode) {\n            retro_tap_primed = true;\n        }\n#endif\n    }\n\n    if (event.pressed) {\n        // clear the potential weak mods left by previously pressed keys\n        clear_weak_mods();\n    }\n\n#ifdef SWAP_HANDS_ENABLE\n    // Swap hands handles both keys and encoders, if ENCODER_MAP_ENABLE is defined.\n    if (IS_EVENT(event)) {\n        process_hand_swap(&event);\n    }\n#endif\n\n    keyrecord_t record = {.event = event};\n\n#ifndef NO_ACTION_ONESHOT\n    if (keymap_config.oneshot_enable) {\n#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\n        if (has_oneshot_layer_timed_out()) {\n            clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);\n        }\n        if (has_oneshot_mods_timed_out()) {\n            clear_oneshot_mods();\n        }\n#        ifdef SWAP_HANDS_ENABLE\n        if (has_oneshot_swaphands_timed_out()) {\n            clear_oneshot_swaphands();\n        }\n#        endif\n#    endif\n    }\n#endif\n\n#ifndef NO_ACTION_TAPPING\n#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)\n    if (event.pressed) {\n        retroshift_poll_time(&event);\n    }\n#    endif\n    if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) {\n        action_tapping_process(record);\n    }\n#else\n    if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) {\n        process_record(&record);\n    }\n    if (IS_EVENT(record.event)) {\n        ac_dprintf(\"processed: \");\n        debug_record(record);\n        dprintln();\n    }\n#endif\n}\n\n#ifdef SWAP_HANDS_ENABLE\nextern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS];\n#    ifdef ENCODER_MAP_ENABLE\nextern const uint8_t PROGMEM encoder_hand_swap_config[NUM_ENCODERS];\n#    endif // ENCODER_MAP_ENABLE\n\nbool swap_hands = false;\nbool swap_held  = false;\n\nbool should_swap_hands(size_t index, uint8_t *swap_state, bool pressed) {\n    size_t  array_index = index / (CHAR_BIT);\n    size_t  bit_index   = index % (CHAR_BIT);\n    uint8_t bit_val     = 1 << bit_index;\n    bool    do_swap     = pressed ? swap_hands : swap_state[array_index] & bit_val;\n    return do_swap;\n}\n\nvoid set_swap_hands_state(size_t index, uint8_t *swap_state, bool on) {\n    size_t  array_index = index / (CHAR_BIT);\n    size_t  bit_index   = index % (CHAR_BIT);\n    uint8_t bit_val     = 1 << bit_index;\n    if (on) {\n        swap_state[array_index] |= bit_val;\n    } else {\n        swap_state[array_index] &= ~bit_val;\n    }\n}\n\nvoid swap_hands_on(void) {\n    swap_hands = true;\n}\n\nvoid swap_hands_off(void) {\n    swap_hands = false;\n}\n\nvoid swap_hands_toggle(void) {\n    swap_hands = !swap_hands;\n}\n\nbool is_swap_hands_on(void) {\n    return swap_hands;\n}\n\n/** \\brief Process Hand Swap\n *\n * FIXME: Needs documentation.\n */\nvoid process_hand_swap(keyevent_t *event) {\n    keypos_t pos = event->key;\n    if (IS_KEYEVENT(*event) && pos.row < MATRIX_ROWS && pos.col < MATRIX_COLS) {\n        static uint8_t matrix_swap_state[((MATRIX_ROWS * MATRIX_COLS) + (CHAR_BIT)-1) / (CHAR_BIT)];\n        size_t         index   = (size_t)(pos.row * MATRIX_COLS) + pos.col;\n        bool           do_swap = should_swap_hands(index, matrix_swap_state, event->pressed);\n        if (do_swap) {\n            event->key.row = pgm_read_byte(&hand_swap_config[pos.row][pos.col].row);\n            event->key.col = pgm_read_byte(&hand_swap_config[pos.row][pos.col].col);\n            set_swap_hands_state(index, matrix_swap_state, true);\n        } else {\n            set_swap_hands_state(index, matrix_swap_state, false);\n        }\n    }\n#    ifdef ENCODER_MAP_ENABLE\n    else if (IS_ENCODEREVENT(*event) && (pos.row == KEYLOC_ENCODER_CW || pos.row == KEYLOC_ENCODER_CCW)) {\n        static uint8_t encoder_swap_state[((NUM_ENCODERS) + (CHAR_BIT)-1) / (CHAR_BIT)];\n        size_t         index   = pos.col;\n        bool           do_swap = should_swap_hands(index, encoder_swap_state, event->pressed);\n        if (do_swap) {\n            event->key.row = pos.row;\n            event->key.col = pgm_read_byte(&encoder_hand_swap_config[pos.col]);\n            set_swap_hands_state(index, encoder_swap_state, true);\n        } else {\n            set_swap_hands_state(index, encoder_swap_state, false);\n        }\n    }\n#    endif // ENCODER_MAP_ENABLE\n}\n#endif\n\n#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)\nbool disable_action_cache = false;\n\nvoid process_record_nocache(keyrecord_t *record) {\n    disable_action_cache = true;\n    process_record(record);\n    disable_action_cache = false;\n}\n#else\nvoid process_record_nocache(keyrecord_t *record) {\n    process_record(record);\n}\n#endif\n\n__attribute__((weak)) bool process_record_quantum(keyrecord_t *record) {\n    return true;\n}\n\n__attribute__((weak)) void post_process_record_quantum(keyrecord_t *record) {}\n\n#ifndef NO_ACTION_TAPPING\n/** \\brief Allows for handling tap-hold actions immediately instead of waiting for TAPPING_TERM or another keypress.\n *\n * FIXME: Needs documentation.\n */\nvoid process_record_tap_hint(keyrecord_t *record) {\n    if (!IS_KEYEVENT(record->event)) {\n        return;\n    }\n\n    action_t action = layer_switch_get_action(record->event.key);\n\n    switch (action.kind.id) {\n#    ifdef SWAP_HANDS_ENABLE\n        case ACT_SWAP_HANDS:\n            switch (action.swap.code) {\n                case OP_SH_ONESHOT:\n                    break;\n                case OP_SH_TAP_TOGGLE:\n                default:\n                    swap_hands = !swap_hands;\n                    swap_held  = true;\n            }\n            break;\n#    endif\n    }\n}\n#endif\n\n/** \\brief Take a key event (key press or key release) and processes it.\n *\n * FIXME: Needs documentation.\n */\nvoid process_record(keyrecord_t *record) {\n    if (IS_NOEVENT(record->event)) {\n        return;\n    }\n#ifdef FLOW_TAP_TERM\n    flow_tap_update_last_event(record);\n#endif // FLOW_TAP_TERM\n\n    if (!process_record_quantum(record)) {\n#ifndef NO_ACTION_ONESHOT\n        if (is_oneshot_layer_active() && record->event.pressed && keymap_config.oneshot_enable) {\n            clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);\n        }\n#endif\n        return;\n    }\n\n    process_record_handler(record);\n    post_process_record_quantum(record);\n}\n\nvoid process_record_handler(keyrecord_t *record) {\n#if defined(COMBO_ENABLE) || defined(REPEAT_KEY_ENABLE)\n    action_t action;\n    if (record->keycode) {\n        action = action_for_keycode(record->keycode);\n    } else {\n        action = store_or_get_action(record->event.pressed, record->event.key);\n    }\n#else\n    action_t action = store_or_get_action(record->event.pressed, record->event.key);\n#endif\n    ac_dprintf(\"ACTION: \");\n    debug_action(action);\n#ifndef NO_ACTION_LAYER\n    ac_dprintf(\" layer_state: \");\n    layer_debug();\n    ac_dprintf(\" default_layer_state: \");\n    default_layer_debug();\n#endif\n    ac_dprintf(\"\\n\");\n\n    process_action(record, action);\n}\n\n/**\n * @brief handles all the messy mouse stuff\n *\n * Handles all the edgecases and special stuff that is needed for coexistense\n * of the multiple mouse subsystems.\n *\n * @param mouse_keycode[in] uint8_t mouse keycode\n * @param pressed[in] bool\n */\n\nvoid register_mouse(uint8_t mouse_keycode, bool pressed) {\n#ifdef MOUSEKEY_ENABLE\n    // if mousekeys is enabled, let it do the brunt of the work\n    if (pressed) {\n        mousekey_on(mouse_keycode);\n    } else {\n        mousekey_off(mouse_keycode);\n    }\n    // should mousekeys send report, or does something else handle this?\n    switch (mouse_keycode) {\n#    if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE)\n        case QK_MOUSE_BUTTON_1 ... QK_MOUSE_BUTTON_8:\n            // let pointing device handle the buttons\n            // expand if/when it handles more of the code\n#        if defined(POINTING_DEVICE_ENABLE)\n            pointing_device_keycode_handler(mouse_keycode, pressed);\n#        endif\n            break;\n#    endif\n        default:\n            mousekey_send();\n            break;\n    }\n#elif defined(POINTING_DEVICE_ENABLE)\n    // if mousekeys isn't enabled, and pointing device is enabled, then\n    // let pointing device do all the heavy lifting, then\n    if (IS_MOUSE_KEYCODE(mouse_keycode)) {\n        pointing_device_keycode_handler(mouse_keycode, pressed);\n    }\n#endif\n\n#ifdef PS2_MOUSE_ENABLE\n    // make sure that ps2 mouse has button report synced\n    if (QK_MOUSE_BUTTON_1 <= mouse_keycode && mouse_keycode <= QK_MOUSE_BUTTON_3) {\n        uint8_t tmp_button_msk = MOUSE_BTN_MASK(mouse_keycode - QK_MOUSE_BUTTON_1);\n        tp_buttons             = pressed ? tp_buttons | tmp_button_msk : tp_buttons & ~tmp_button_msk;\n    }\n#endif\n}\n\n/** \\brief Take an action and processes it.\n *\n * FIXME: Needs documentation.\n */\nvoid process_action(keyrecord_t *record, action_t action) {\n    keyevent_t event = record->event;\n#ifndef NO_ACTION_TAPPING\n    uint8_t tap_count = record->tap.count;\n#endif\n\n#ifndef NO_ACTION_ONESHOT\n    bool do_release_oneshot = false;\n    // notice we only clear the one shot layer if the pressed key is not a modifier.\n    if (is_oneshot_layer_active() && event.pressed &&\n        (action.kind.id == ACT_USAGE || !(IS_MODIFIER_KEYCODE(action.key.code)\n#    ifndef NO_ACTION_TAPPING\n                                          || ((action.kind.id == ACT_LMODS_TAP || action.kind.id == ACT_RMODS_TAP) && (action.layer_tap.code <= MODS_TAP_TOGGLE || tap_count == 0))\n#    endif\n                                              ))\n#    ifdef SWAP_HANDS_ENABLE\n        && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)\n#    endif\n        && keymap_config.oneshot_enable) {\n        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);\n        do_release_oneshot = !is_oneshot_layer_active();\n    }\n#endif\n\n    switch (action.kind.id) {\n        /* Key and Mods */\n        case ACT_LMODS:\n        case ACT_RMODS: {\n            uint8_t mods = (action.kind.id == ACT_LMODS) ? action.key.mods : action.key.mods << 4;\n            if (event.pressed) {\n                if (mods) {\n                    if (IS_MODIFIER_KEYCODE(action.key.code) || action.key.code == KC_NO) {\n                        // e.g. LSFT(KC_LEFT_GUI): we don't want the LSFT to be weak as it would make it useless.\n                        // This also makes LSFT(KC_LEFT_GUI) behave exactly the same as LGUI(KC_LEFT_SHIFT).\n                        // Same applies for some keys like KC_MEH which are declared as MEH(KC_NO).\n                        add_mods(mods);\n                    } else {\n                        add_weak_mods(mods);\n                    }\n                    send_keyboard_report();\n                }\n                register_code(action.key.code);\n            } else {\n                unregister_code(action.key.code);\n                if (mods) {\n                    if (IS_MODIFIER_KEYCODE(action.key.code) || action.key.code == KC_NO) {\n                        del_mods(mods);\n                    } else {\n                        del_weak_mods(mods);\n                    }\n                    send_keyboard_report();\n                }\n            }\n        } break;\n        case ACT_LMODS_TAP:\n        case ACT_RMODS_TAP: {\n#ifndef NO_ACTION_TAPPING\n            uint8_t mods = (action.kind.id == ACT_LMODS_TAP) ? action.key.mods : action.key.mods << 4;\n            switch (action.layer_tap.code) {\n#    ifndef NO_ACTION_ONESHOT\n                case MODS_ONESHOT:\n                    // Oneshot modifier\n                    if (!keymap_config.oneshot_enable) {\n                        if (event.pressed) {\n                            if (mods) {\n                                if (IS_MODIFIER_KEYCODE(action.key.code) || action.key.code == KC_NO) {\n                                    // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless.\n                                    // This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT).\n                                    // Same applies for some keys like KC_MEH which are declared as MEH(KC_NO).\n                                    add_mods(mods);\n                                } else {\n                                    add_weak_mods(mods);\n                                }\n                                send_keyboard_report();\n                            }\n                            register_code(action.key.code);\n                        } else {\n                            unregister_code(action.key.code);\n                            if (mods) {\n                                if (IS_MODIFIER_KEYCODE(action.key.code) || action.key.code == KC_NO) {\n                                    del_mods(mods);\n                                } else {\n                                    del_weak_mods(mods);\n                                }\n                                send_keyboard_report();\n                            }\n                        }\n                    } else {\n                        if (event.pressed) {\n                            if (tap_count == 0) {\n                                // Not a tap, but a hold: register the held mod\n                                ac_dprintf(\"MODS_TAP: Oneshot: 0\\n\");\n                                register_mods(mods);\n                            } else if (tap_count == 1) {\n                                ac_dprintf(\"MODS_TAP: Oneshot: start\\n\");\n                                add_oneshot_mods(mods);\n#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1\n                            } else if (tap_count == ONESHOT_TAP_TOGGLE) {\n                                ac_dprintf(\"MODS_TAP: Toggling oneshot\");\n                                register_mods(mods);\n                                del_oneshot_mods(mods);\n                                add_oneshot_locked_mods(mods);\n#        endif\n                            }\n                        } else {\n                            if (tap_count == 0) {\n                                // Release hold: unregister the held mod and its variants\n                                unregister_mods(mods);\n                                del_oneshot_mods(mods);\n                                del_oneshot_locked_mods(mods);\n#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1\n                            } else if (tap_count == 1 && (mods & get_mods())) {\n                                unregister_mods(mods);\n                                del_oneshot_mods(mods);\n                                del_oneshot_locked_mods(mods);\n#        endif\n                            }\n                        }\n                    }\n                    break;\n#    endif\n                case MODS_TAP_TOGGLE:\n                    if (event.pressed) {\n                        if (tap_count <= TAPPING_TOGGLE) {\n                            register_mods(mods);\n                        }\n                    } else {\n                        if (tap_count < TAPPING_TOGGLE) {\n                            unregister_mods(mods);\n                        }\n                    }\n                    break;\n                default:\n                    if (event.pressed) {\n                        if (tap_count > 0) {\n#    ifdef HOLD_ON_OTHER_KEY_PRESS\n                            if (\n#        ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY\n                                get_hold_on_other_key_press(get_event_keycode(record->event, false), record) &&\n#        endif\n                                record->tap.interrupted) {\n                                ac_dprintf(\"mods_tap: tap: cancel: add_mods\\n\");\n                                // ad hoc: set 0 to cancel tap\n                                record->tap.count = 0;\n                                register_mods(mods);\n                            } else\n#    endif\n                            {\n                                ac_dprintf(\"MODS_TAP: Tap: register_code\\n\");\n                                register_code(action.key.code);\n                            }\n                        } else {\n                            ac_dprintf(\"MODS_TAP: No tap: add_mods\\n\");\n                            register_mods(mods);\n                        }\n                    } else {\n                        if (tap_count > 0) {\n                            ac_dprintf(\"MODS_TAP: Tap: unregister_code\\n\");\n                            if (action.layer_tap.code == KC_CAPS_LOCK) {\n                                wait_ms(TAP_HOLD_CAPS_DELAY);\n                            } else {\n                                wait_ms(TAP_CODE_DELAY);\n                            }\n                            unregister_code(action.key.code);\n                        } else {\n                            ac_dprintf(\"MODS_TAP: No tap: add_mods\\n\");\n#    if defined(RETRO_TAPPING) && defined(DUMMY_MOD_NEUTRALIZER_KEYCODE)\n                            // Send a dummy keycode to neutralize flashing modifiers\n                            // if the key was held and then released with no interruptions.\n                            uint16_t ev_kc = get_event_keycode(event, false);\n                            if (retro_tap_primed && retro_tap_curr_key == ev_kc) {\n                                neutralize_flashing_modifiers(get_mods());\n                            }\n#    endif\n                            unregister_mods(mods);\n                        }\n                    }\n                    break;\n            }\n#endif // NO_ACTION_TAPPING\n        } break;\n#ifdef EXTRAKEY_ENABLE\n        /* other HID usage */\n        case ACT_USAGE:\n            switch (action.usage.page) {\n                case PAGE_SYSTEM:\n                    host_system_send(event.pressed ? action.usage.code : 0);\n                    break;\n                case PAGE_CONSUMER:\n                    host_consumer_send(event.pressed ? action.usage.code : 0);\n                    break;\n            }\n            break;\n#endif // EXTRAKEY_ENABLE\n        /* Mouse key */\n        case ACT_MOUSEKEY:\n            register_mouse(action.key.code, event.pressed);\n            break;\n#ifndef NO_ACTION_LAYER\n        case ACT_LAYER:\n            if (action.layer_bitop.on == 0) {\n                /* Default Layer Bitwise Operation */\n                if (!event.pressed) {\n                    uint8_t       shift = action.layer_bitop.part * 4;\n                    layer_state_t bits  = ((layer_state_t)action.layer_bitop.bits) << shift;\n                    layer_state_t mask  = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0;\n                    switch (action.layer_bitop.op) {\n                        case OP_BIT_AND:\n                            default_layer_and(bits | mask);\n                            break;\n                        case OP_BIT_OR:\n                            default_layer_or(bits | mask);\n                            break;\n                        case OP_BIT_XOR:\n                            default_layer_xor(bits | mask);\n                            break;\n                        case OP_BIT_SET:\n                            default_layer_set(bits | mask);\n                            break;\n                    }\n                }\n            } else {\n                /* Layer Bitwise Operation */\n                if (event.pressed ? (action.layer_bitop.on & ON_PRESS) : (action.layer_bitop.on & ON_RELEASE)) {\n                    uint8_t       shift = action.layer_bitop.part * 4;\n                    layer_state_t bits  = ((layer_state_t)action.layer_bitop.bits) << shift;\n                    layer_state_t mask  = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0;\n                    switch (action.layer_bitop.op) {\n                        case OP_BIT_AND:\n                            layer_and(bits | mask);\n                            break;\n                        case OP_BIT_OR:\n                            layer_or(bits | mask);\n                            break;\n                        case OP_BIT_XOR:\n                            layer_xor(bits | mask);\n                            break;\n                        case OP_BIT_SET:\n                            layer_state_set(bits | mask);\n                            break;\n                    }\n                }\n            }\n            break;\n        case ACT_LAYER_MODS:\n            if (event.pressed) {\n                layer_on(action.layer_mods.layer);\n                register_mods(action.layer_mods.mods);\n            } else {\n                unregister_mods(action.layer_mods.mods);\n                layer_off(action.layer_mods.layer);\n            }\n            break;\n        case ACT_LAYER_TAP:\n        case ACT_LAYER_TAP_EXT:\n            switch (action.layer_tap.code) {\n#    ifndef NO_ACTION_TAPPING\n                case OP_TAP_TOGGLE:\n                    /* tap toggle */\n                    if (event.pressed) {\n                        if (tap_count < TAPPING_TOGGLE) {\n                            layer_invert(action.layer_tap.val);\n                        }\n                    } else {\n                        if (tap_count <= TAPPING_TOGGLE) {\n                            layer_invert(action.layer_tap.val);\n                        }\n                    }\n                    break;\n#    endif\n                case OP_ON_OFF:\n                    event.pressed ? layer_on(action.layer_tap.val) : layer_off(action.layer_tap.val);\n                    break;\n                case OP_OFF_ON:\n                    event.pressed ? layer_off(action.layer_tap.val) : layer_on(action.layer_tap.val);\n                    break;\n                case OP_SET_CLEAR:\n                    event.pressed ? layer_move(action.layer_tap.val) : layer_clear();\n                    break;\n#    if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)\n                case OP_ONESHOT:\n                    // Oneshot modifier\n                    if (!keymap_config.oneshot_enable) {\n                        if (event.pressed) {\n                            layer_on(action.layer_tap.val);\n                        } else {\n                            layer_off(action.layer_tap.val);\n                        }\n                    } else {\n#        if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1\n                        do_release_oneshot = false;\n                        if (event.pressed) {\n                            if (get_oneshot_layer_state() == ONESHOT_TOGGLED) {\n                                reset_oneshot_layer();\n                                layer_off(action.layer_tap.val);\n                                break;\n                            } else if (tap_count < ONESHOT_TAP_TOGGLE) {\n                                set_oneshot_layer(action.layer_tap.val, ONESHOT_START);\n                            }\n                        } else {\n                            if (tap_count >= ONESHOT_TAP_TOGGLE) {\n                                reset_oneshot_layer();\n                                set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED);\n                            } else {\n                                clear_oneshot_layer_state(ONESHOT_PRESSED);\n                            }\n                        }\n#        else\n                        if (event.pressed) {\n                            set_oneshot_layer(action.layer_tap.val, ONESHOT_START);\n                        } else {\n                            clear_oneshot_layer_state(ONESHOT_PRESSED);\n                            if (tap_count > 1) {\n                                clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);\n                            }\n                        }\n#        endif\n                    }\n#    else  // NO_ACTION_ONESHOT && NO_ACTION_TAPPING\n                    if (event.pressed) {\n                        layer_on(action.layer_tap.val);\n                    } else {\n                        layer_off(action.layer_tap.val);\n                    }\n#    endif // !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)\n                    break;\n                default:\n#    ifndef NO_ACTION_TAPPING /* tap key */\n                    if (event.pressed) {\n                        if (tap_count > 0) {\n                            ac_dprintf(\"KEYMAP_TAP_KEY: Tap: register_code\\n\");\n                            register_code(action.layer_tap.code);\n                        } else {\n                            ac_dprintf(\"KEYMAP_TAP_KEY: No tap: On on press\\n\");\n                            layer_on(action.layer_tap.val);\n                        }\n                    } else {\n                        if (tap_count > 0) {\n                            ac_dprintf(\"KEYMAP_TAP_KEY: Tap: unregister_code\\n\");\n                            if (action.layer_tap.code == KC_CAPS_LOCK) {\n                                wait_ms(TAP_HOLD_CAPS_DELAY);\n                            } else {\n                                wait_ms(TAP_CODE_DELAY);\n                            }\n                            unregister_code(action.layer_tap.code);\n                        } else {\n                            ac_dprintf(\"KEYMAP_TAP_KEY: No tap: Off on release\\n\");\n                            layer_off(action.layer_tap.val);\n                        }\n                    }\n#    else\n                    if (event.pressed) {\n                        ac_dprintf(\"KEYMAP_TAP_KEY: Tap: register_code\\n\");\n                        register_code(action.layer_tap.code);\n                    } else {\n                        ac_dprintf(\"KEYMAP_TAP_KEY: Tap: unregister_code\\n\");\n                        if (action.layer_tap.code == KC_CAPS) {\n                            wait_ms(TAP_HOLD_CAPS_DELAY);\n                        } else {\n                            wait_ms(TAP_CODE_DELAY);\n                        }\n                        unregister_code(action.layer_tap.code);\n                    }\n#    endif\n                    break;\n            }\n            break;\n#endif // NO_ACTION_LAYER\n\n#ifdef SWAP_HANDS_ENABLE\n        case ACT_SWAP_HANDS:\n            switch (action.swap.code) {\n                case OP_SH_TOGGLE:\n                    if (event.pressed) {\n                        swap_hands = !swap_hands;\n                    }\n                    break;\n                case OP_SH_ON_OFF:\n                    swap_hands = event.pressed;\n                    break;\n                case OP_SH_OFF_ON:\n                    swap_hands = !event.pressed;\n                    break;\n                case OP_SH_ON:\n                    if (!event.pressed) {\n                        swap_hands = true;\n                    }\n                    break;\n                case OP_SH_OFF:\n                    if (!event.pressed) {\n                        swap_hands = false;\n                    }\n                    break;\n#    ifndef NO_ACTION_ONESHOT\n                case OP_SH_ONESHOT:\n                    if (event.pressed) {\n                        set_oneshot_swaphands();\n                    } else {\n                        release_oneshot_swaphands();\n                    }\n                    break;\n#    endif\n\n#    ifndef NO_ACTION_TAPPING\n                case OP_SH_TAP_TOGGLE:\n                    /* tap toggle */\n\n                    if (event.pressed) {\n                        if (swap_held) {\n                            swap_held = false;\n                        } else {\n                            swap_hands = !swap_hands;\n                        }\n                    } else {\n                        if (tap_count < TAPPING_TOGGLE) {\n                            swap_hands = !swap_hands;\n                        }\n                    }\n                    break;\n                default:\n                    /* tap key */\n                    if (tap_count > 0) {\n                        if (swap_held) {\n                            swap_hands = !swap_hands; // undo hold set up in _tap_hint\n                            swap_held  = false;\n                        }\n                        if (event.pressed) {\n                            register_code(action.swap.code);\n                        } else {\n                            wait_ms(TAP_CODE_DELAY);\n                            unregister_code(action.swap.code);\n                            *record = (keyrecord_t){}; // hack: reset tap mode\n                        }\n                    } else {\n                        if (swap_held && !event.pressed) {\n                            swap_hands = !swap_hands; // undo hold set up in _tap_hint\n                            swap_held  = false;\n                        }\n                    }\n#    endif\n            }\n#endif\n        default:\n            break;\n    }\n\n#ifndef NO_ACTION_LAYER\n    // if this event is a layer action, update the leds\n    switch (action.kind.id) {\n        case ACT_LAYER:\n        case ACT_LAYER_MODS:\n#    ifndef NO_ACTION_TAPPING\n        case ACT_LAYER_TAP:\n        case ACT_LAYER_TAP_EXT:\n#    endif\n            led_set(host_keyboard_leds());\n#    ifndef NO_ACTION_ONESHOT\n            // don't release the key\n            do_release_oneshot = false;\n#    endif\n            break;\n        default:\n            break;\n    }\n#endif\n\n#ifndef NO_ACTION_TAPPING\n#    if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) || (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))\n    if (is_tap_action(action)) {\n        if (event.pressed) {\n            if (tap_count > 0) {\n                retro_tap_primed = false;\n            } else {\n#        if !(defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))\n                retro_tap_curr_mods = retro_tap_next_mods;\n                retro_tap_next_mods = get_mods();\n#        endif\n            }\n        } else {\n            uint16_t event_keycode = get_event_keycode(event, false);\n#        if !(defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))\n            uint8_t curr_mods = get_mods();\n#        endif\n            if (tap_count > 0) {\n                retro_tap_primed = false;\n            } else if (retro_tap_curr_key == event_keycode) {\n                if (\n#        ifdef RETRO_TAPPING_PER_KEY\n                    get_retro_tapping(event_keycode, record) &&\n#        endif\n                    retro_tap_primed) {\n#        if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)\n                    process_auto_shift(action.layer_tap.code, record);\n#        else\n                    register_mods(retro_tap_curr_mods);\n                    wait_ms(TAP_CODE_DELAY);\n                    tap_code(action.layer_tap.code);\n                    wait_ms(TAP_CODE_DELAY);\n                    unregister_mods(retro_tap_curr_mods);\n#        endif\n                }\n                retro_tap_primed = false;\n            }\n#        if !(defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))\n            retro_tap_next_mods = curr_mods;\n#        endif\n        }\n    }\n#    endif\n#endif\n\n#ifdef SWAP_HANDS_ENABLE\n#    ifndef NO_ACTION_ONESHOT\n    if (event.pressed && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)) {\n        use_oneshot_swaphands();\n    }\n#    endif\n#endif\n\n#ifndef NO_ACTION_ONESHOT\n    /* Because we switch layers after a oneshot event, we need to release the\n     * key before we leave the layer or no key up event will be generated.\n     */\n    if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED)) {\n        record->event.pressed = false;\n        layer_on(get_oneshot_layer());\n        process_record(record);\n        layer_off(get_oneshot_layer());\n    }\n#endif\n}\n\n/** \\brief Utilities for actions. (FIXME: Needs better description)\n *\n * FIXME: Needs documentation.\n */\n__attribute__((weak)) void register_code(uint8_t code) {\n    if (code == KC_NO) {\n        return;\n\n#ifdef LOCKING_SUPPORT_ENABLE\n    } else if (KC_LOCKING_CAPS_LOCK == code) {\n#    ifdef LOCKING_RESYNC_ENABLE\n        // Resync: ignore if caps lock already is on\n        if (host_keyboard_led_state().caps_lock) return;\n#    endif\n        add_key(KC_CAPS_LOCK);\n        send_keyboard_report();\n        wait_ms(TAP_HOLD_CAPS_DELAY);\n        del_key(KC_CAPS_LOCK);\n        send_keyboard_report();\n\n    } else if (KC_LOCKING_NUM_LOCK == code) {\n#    ifdef LOCKING_RESYNC_ENABLE\n        if (host_keyboard_led_state().num_lock) return;\n#    endif\n        add_key(KC_NUM_LOCK);\n        send_keyboard_report();\n        wait_ms(100);\n        del_key(KC_NUM_LOCK);\n        send_keyboard_report();\n\n    } else if (KC_LOCKING_SCROLL_LOCK == code) {\n#    ifdef LOCKING_RESYNC_ENABLE\n        if (host_keyboard_led_state().scroll_lock) return;\n#    endif\n        add_key(KC_SCROLL_LOCK);\n        send_keyboard_report();\n        wait_ms(100);\n        del_key(KC_SCROLL_LOCK);\n        send_keyboard_report();\n#endif\n\n    } else if (IS_BASIC_KEYCODE(code)) {\n        // TODO: should push command_proc out of this block?\n        if (command_proc(code)) return;\n\n        // Force a new key press if the key is already pressed\n        // without this, keys with the same keycode, but different\n        // modifiers will be reported incorrectly, see issue #1708\n        if (is_key_pressed(code)) {\n            del_key(code);\n            send_keyboard_report();\n        }\n        add_key(code);\n        send_keyboard_report();\n    } else if (IS_MODIFIER_KEYCODE(code)) {\n        add_mods(MOD_BIT(code));\n        send_keyboard_report();\n\n#ifdef EXTRAKEY_ENABLE\n    } else if (IS_SYSTEM_KEYCODE(code)) {\n        host_system_send(KEYCODE2SYSTEM(code));\n    } else if (IS_CONSUMER_KEYCODE(code)) {\n        host_consumer_send(KEYCODE2CONSUMER(code));\n#endif\n\n    } else if (IS_MOUSE_KEYCODE(code)) {\n        register_mouse(code, true);\n    }\n}\n\n/** \\brief Utilities for actions. (FIXME: Needs better description)\n *\n * FIXME: Needs documentation.\n */\n__attribute__((weak)) void unregister_code(uint8_t code) {\n    if (code == KC_NO) {\n        return;\n\n#ifdef LOCKING_SUPPORT_ENABLE\n    } else if (KC_LOCKING_CAPS_LOCK == code) {\n#    ifdef LOCKING_RESYNC_ENABLE\n        // Resync: ignore if caps lock already is off\n        if (!host_keyboard_led_state().caps_lock) return;\n#    endif\n        add_key(KC_CAPS_LOCK);\n        send_keyboard_report();\n        del_key(KC_CAPS_LOCK);\n        send_keyboard_report();\n\n    } else if (KC_LOCKING_NUM_LOCK == code) {\n#    ifdef LOCKING_RESYNC_ENABLE\n        if (!host_keyboard_led_state().num_lock) return;\n#    endif\n        add_key(KC_NUM_LOCK);\n        send_keyboard_report();\n        del_key(KC_NUM_LOCK);\n        send_keyboard_report();\n\n    } else if (KC_LOCKING_SCROLL_LOCK == code) {\n#    ifdef LOCKING_RESYNC_ENABLE\n        if (!host_keyboard_led_state().scroll_lock) return;\n#    endif\n        add_key(KC_SCROLL_LOCK);\n        send_keyboard_report();\n        del_key(KC_SCROLL_LOCK);\n        send_keyboard_report();\n#endif\n\n    } else if (IS_BASIC_KEYCODE(code)) {\n        del_key(code);\n        send_keyboard_report();\n    } else if (IS_MODIFIER_KEYCODE(code)) {\n        del_mods(MOD_BIT(code));\n        send_keyboard_report();\n\n#ifdef EXTRAKEY_ENABLE\n    } else if (IS_SYSTEM_KEYCODE(code)) {\n        host_system_send(0);\n    } else if (IS_CONSUMER_KEYCODE(code)) {\n        host_consumer_send(0);\n#endif\n\n    } else if (IS_MOUSE_KEYCODE(code)) {\n        register_mouse(code, false);\n    }\n}\n\n/** \\brief Tap a keycode with a delay.\n *\n * \\param code The basic keycode to tap.\n * \\param delay The amount of time in milliseconds to leave the keycode registered, before unregistering it.\n */\n__attribute__((weak)) void tap_code_delay(uint8_t code, uint16_t delay) {\n    register_code(code);\n    wait_ms(delay);\n    unregister_code(code);\n}\n\n/** \\brief Tap a keycode with the default delay.\n *\n * \\param code The basic keycode to tap. If `code` is `KC_CAPS_LOCK`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined.\n */\n__attribute__((weak)) void tap_code(uint8_t code) {\n    tap_code_delay(code, code == KC_CAPS_LOCK ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY);\n}\n\n/** \\brief Adds the given physically pressed modifiers and sends a keyboard report immediately.\n *\n * \\param mods A bitfield of modifiers to register.\n */\n__attribute__((weak)) void register_mods(uint8_t mods) {\n    if (mods) {\n        add_mods(mods);\n        send_keyboard_report();\n    }\n}\n\n/** \\brief Removes the given physically pressed modifiers and sends a keyboard report immediately.\n *\n * \\param mods A bitfield of modifiers to unregister.\n */\n__attribute__((weak)) void unregister_mods(uint8_t mods) {\n    if (mods) {\n        del_mods(mods);\n        send_keyboard_report();\n    }\n}\n\n/** \\brief Adds the given weak modifiers and sends a keyboard report immediately.\n *\n * \\param mods A bitfield of modifiers to register.\n */\n__attribute__((weak)) void register_weak_mods(uint8_t mods) {\n    if (mods) {\n        add_weak_mods(mods);\n        send_keyboard_report();\n    }\n}\n\n/** \\brief Removes the given weak modifiers and sends a keyboard report immediately.\n *\n * \\param mods A bitfield of modifiers to unregister.\n */\n__attribute__((weak)) void unregister_weak_mods(uint8_t mods) {\n    if (mods) {\n        del_weak_mods(mods);\n        send_keyboard_report();\n    }\n}\n\n/** \\brief Utilities for actions. (FIXME: Needs better description)\n *\n * FIXME: Needs documentation.\n */\nvoid clear_keyboard(void) {\n    clear_mods();\n    clear_keyboard_but_mods();\n}\n\n/** \\brief Utilities for actions. (FIXME: Needs better description)\n *\n * FIXME: Needs documentation.\n */\nvoid clear_keyboard_but_mods(void) {\n    clear_keys();\n    clear_keyboard_but_mods_and_keys();\n}\n\n/** \\brief Utilities for actions. (FIXME: Needs better description)\n *\n * FIXME: Needs documentation.\n */\nvoid clear_keyboard_but_mods_and_keys(void) {\n#ifdef EXTRAKEY_ENABLE\n    host_system_send(0);\n    host_consumer_send(0);\n#endif\n    clear_weak_mods();\n    send_keyboard_report();\n#ifdef MOUSEKEY_ENABLE\n    mousekey_clear();\n    mousekey_send();\n#endif\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\n    programmable_button_clear();\n#endif\n}\n\n/** \\brief Utilities for actions. (FIXME: Needs better description)\n *\n * FIXME: Needs documentation.\n */\nbool is_tap_record(keyrecord_t *record) {\n    if (IS_NOEVENT(record->event)) {\n        return false;\n    }\n\n#if defined(COMBO_ENABLE) || defined(REPEAT_KEY_ENABLE)\n    action_t action;\n    if (record->keycode) {\n        action = action_for_keycode(record->keycode);\n    } else {\n        action = layer_switch_get_action(record->event.key);\n    }\n#else\n    action_t action = layer_switch_get_action(record->event.key);\n#endif\n    return is_tap_action(action);\n}\n\n/** \\brief Utilities for actions. (FIXME: Needs better description)\n *\n * FIXME: Needs documentation.\n */\nbool is_tap_action(action_t action) {\n    switch (action.kind.id) {\n        case ACT_LMODS_TAP:\n        case ACT_RMODS_TAP:\n        case ACT_LAYER_TAP:\n        case ACT_LAYER_TAP_EXT:\n            switch (action.layer_tap.code) {\n                case KC_NO ... KC_RIGHT_GUI:\n                case OP_TAP_TOGGLE:\n                case OP_ONESHOT:\n                    return true;\n            }\n            return false;\n        case ACT_SWAP_HANDS:\n            switch (action.swap.code) {\n                case KC_NO ... KC_RIGHT_GUI:\n                case OP_SH_TAP_TOGGLE:\n                    return true;\n            }\n            return false;\n    }\n    return false;\n}\n\nuint16_t get_tap_keycode(uint16_t keycode) {\n    switch (keycode) {\n        case QK_MOD_TAP ... QK_MOD_TAP_MAX:\n            return QK_MOD_TAP_GET_TAP_KEYCODE(keycode);\n        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:\n            return QK_LAYER_TAP_GET_TAP_KEYCODE(keycode);\n        case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:\n            // IS_SWAP_HANDS_KEYCODE() tests for the special action keycodes\n            // like SH_TOGG, SH_TT, ..., which overlap the SH_T(kc) range.\n            if (!IS_SWAP_HANDS_KEYCODE(keycode)) {\n                return QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode);\n            }\n            break;\n    }\n    return keycode;\n}\n\n/** \\brief Debug print (FIXME: Needs better description)\n *\n * FIXME: Needs documentation.\n */\nvoid debug_event(keyevent_t event) {\n    ac_dprintf(\"%04X%c(%u)\", (event.key.row << 8 | event.key.col), (event.pressed ? 'd' : 'u'), event.time);\n}\n/** \\brief Debug print (FIXME: Needs better description)\n *\n * FIXME: Needs documentation.\n */\nvoid debug_record(keyrecord_t record) {\n    debug_event(record.event);\n#ifndef NO_ACTION_TAPPING\n    ac_dprintf(\":%u%c\", record.tap.count, (record.tap.interrupted ? '-' : ' '));\n#endif\n}\n\n/** \\brief Debug print (FIXME: Needs better description)\n *\n * FIXME: Needs documentation.\n */\nvoid debug_action(action_t action) {\n    switch (action.kind.id) {\n        case ACT_LMODS:\n            ac_dprintf(\"ACT_LMODS\");\n            break;\n        case ACT_RMODS:\n            ac_dprintf(\"ACT_RMODS\");\n            break;\n        case ACT_LMODS_TAP:\n            ac_dprintf(\"ACT_LMODS_TAP\");\n            break;\n        case ACT_RMODS_TAP:\n            ac_dprintf(\"ACT_RMODS_TAP\");\n            break;\n        case ACT_USAGE:\n            ac_dprintf(\"ACT_USAGE\");\n            break;\n        case ACT_MOUSEKEY:\n            ac_dprintf(\"ACT_MOUSEKEY\");\n            break;\n        case ACT_LAYER:\n            ac_dprintf(\"ACT_LAYER\");\n            break;\n        case ACT_LAYER_MODS:\n            ac_dprintf(\"ACT_LAYER_MODS\");\n            break;\n        case ACT_LAYER_TAP:\n            ac_dprintf(\"ACT_LAYER_TAP\");\n            break;\n        case ACT_LAYER_TAP_EXT:\n            ac_dprintf(\"ACT_LAYER_TAP_EXT\");\n            break;\n        case ACT_SWAP_HANDS:\n            ac_dprintf(\"ACT_SWAP_HANDS\");\n            break;\n        default:\n            ac_dprintf(\"UNKNOWN\");\n            break;\n    }\n    ac_dprintf(\"[%X:%02X]\", action.kind.param >> 8, action.kind.param & 0xff);\n}\n"
  },
  {
    "path": "quantum/action.h",
    "content": "/*\nCopyright 2012,2013 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"progmem.h\"\n#include \"keyboard.h\"\n#include \"keycode.h\"\n#include \"action_code.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifndef TAP_CODE_DELAY\n#    define TAP_CODE_DELAY 0\n#endif\n#ifndef TAP_HOLD_CAPS_DELAY\n#    define TAP_HOLD_CAPS_DELAY 80\n#endif\n\n/* tapping count and state */\ntypedef struct {\n    bool    interrupted : 1;\n    bool    reserved2 : 1;\n    bool    reserved1 : 1;\n    bool    reserved0 : 1;\n    uint8_t count : 4;\n} tap_t;\n\n/* Key event container for recording */\ntypedef struct keyrecord_t {\n    keyevent_t event;\n#ifndef NO_ACTION_TAPPING\n    tap_t tap;\n#endif\n#if defined(COMBO_ENABLE) || defined(REPEAT_KEY_ENABLE)\n    uint16_t keycode;\n#endif\n} keyrecord_t;\n\n/* Execute action per keyevent */\nvoid action_exec(keyevent_t event);\n\n/* action for key */\naction_t action_for_key(uint8_t layer, keypos_t key);\naction_t action_for_keycode(uint16_t keycode);\n\n/* keyboard-specific key event (pre)processing */\nbool process_record_quantum(keyrecord_t *record);\n\n/* Utilities for actions.  */\n#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)\nextern bool disable_action_cache;\n#endif\n\n/* Code for handling one-handed key modifiers. */\n#ifdef SWAP_HANDS_ENABLE\nextern bool                   swap_hands;\nextern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS];\n#    if (MATRIX_COLS <= 8)\ntypedef uint8_t swap_state_row_t;\n#    elif (MATRIX_COLS <= 16)\ntypedef uint16_t swap_state_row_t;\n#    elif (MATRIX_COLS <= 32)\ntypedef uint32_t swap_state_row_t;\n#    else\n#        error \"MATRIX_COLS: invalid value\"\n#    endif\n\n/**\n * @brief Enable swap hands\n */\nvoid swap_hands_on(void);\n/**\n * @brief Disable swap hands\n */\nvoid swap_hands_off(void);\n/**\n * @brief Toggle swap hands enable state\n */\nvoid swap_hands_toggle(void);\n/**\n * @brief Get the swap hands enable state\n *\n * @return true\n * @return false\n */\nbool is_swap_hands_on(void);\n\nvoid process_hand_swap(keyevent_t *record);\n#endif\n\nvoid process_record_nocache(keyrecord_t *record);\nvoid process_record(keyrecord_t *record);\nvoid process_record_handler(keyrecord_t *record);\nvoid post_process_record_quantum(keyrecord_t *record);\nvoid process_action(keyrecord_t *record, action_t action);\nvoid register_code(uint8_t code);\nvoid unregister_code(uint8_t code);\nvoid tap_code(uint8_t code);\nvoid tap_code_delay(uint8_t code, uint16_t delay);\nvoid register_mods(uint8_t mods);\nvoid unregister_mods(uint8_t mods);\nvoid register_weak_mods(uint8_t mods);\nvoid unregister_weak_mods(uint8_t mods);\n// void set_mods(uint8_t mods);\nvoid clear_keyboard(void);\nvoid clear_keyboard_but_mods(void);\nvoid clear_keyboard_but_mods_and_keys(void);\nvoid layer_switch(uint8_t new_layer);\nbool is_tap_record(keyrecord_t *record);\nbool is_tap_action(action_t action);\n\n/**\n * Given an MT or LT keycode, returns the tap keycode. Otherwise returns the\n * original keycode unchanged.\n */\nuint16_t get_tap_keycode(uint16_t keycode);\n\n#ifndef NO_ACTION_TAPPING\nvoid process_record_tap_hint(keyrecord_t *record);\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Helpers\n\n#ifdef ACTION_DEBUG\n#    include \"debug.h\"\n#    include \"print.h\"\n#    define ac_dprintf(...) dprintf(__VA_ARGS__)\n#else\n#    define ac_dprintf(...) \\\n        do {                \\\n        } while (0)\n#endif\n\nvoid debug_event(keyevent_t event);\nvoid debug_record(keyrecord_t record);\nvoid debug_action(action_t action);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/action_code.h",
    "content": "/*\nCopyright 2013 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include \"modifiers.h\"\n\n/** \\brief Action codes\n *\n * 16bit code: action_kind(4bit) + action_parameter(12bit)\n *\n * Key Actions(00xx)\n * -----------------\n * ACT_MODS(000r):\n * 000r|0000|0000 0000    No action code\n * 000r|0000|0000 0001    Transparent code\n * 000r|0000| keycode     Key\n * 000r|mods|0000 0000    Modifiers\n * 000r|mods| keycode     Modifiers+Key(Modified key)\n *   r: Left/Right flag(Left:0, Right:1)\n *\n * ACT_MODS_TAP(001r):\n * 001r|mods|0000 0000    Modifiers with OneShot\n * 001r|mods|0000 0001    Modifiers with tap toggle\n * 001r|mods|0000 00xx    (reserved)\n * 001r|mods| keycode     Modifiers with Tap Key(Dual role)\n *\n * Other Keys(01xx)\n * ----------------\n * ACT_USAGE(0100): TODO: Not needed?\n * 0100|00| usage(10)     System control(0x80) - General Desktop page(0x01)\n * 0100|01| usage(10)     Consumer control(0x01) - Consumer page(0x0C)\n * 0100|10| usage(10)     (reserved)\n * 0100|11| usage(10)     (reserved)\n *\n * ACT_MOUSEKEY(0101): TODO: Merge these two actions to conserve space?\n * 0101|xxxx| keycode     Mouse key\n *\n * ACT_SWAP_HANDS(0110):\n * 0110|xxxx| keycode     Swap hands (keycode on tap, or options)\n *\n * 0111|xxxx xxxx xxxx    (reserved)\n *\n * Layer Actions(10xx)\n * -------------------\n * ACT_LAYER(1000):\n * 1000|oo00|pppE BBBB   Default Layer Bitwise operation\n *   oo:    operation(00:AND, 01:OR, 10:XOR, 11:SET)\n *   ppp:   4-bit chunk part(0-7)\n *   EBBBB: bits and extra bit\n * 1000|ooee|pppE BBBB   Layer Bitwise Operation\n *   oo:    operation(00:AND, 01:OR, 10:XOR, 11:SET)\n *   ppp:   4-bit chunk part(0-7)\n *   EBBBB: bits and extra bit\n *   ee:    on event(01:press, 10:release, 11:both)\n *\n * ACT_LAYER_MODS(1001):\n * 1001|LLLL| mods       Layer with modifiers held\n *\n * ACT_LAYER_TAP(101x):\n * 101E|LLLL| keycode    On/Off with tap key    (0x00-DF)[TAP]\n * 101E|LLLL|1110 mods   On/Off with modifiers  (0xE0-EF)[NOT TAP]\n * 101E|LLLL|1111 0000   Invert with tap toggle (0xF0)   [TAP]\n * 101E|LLLL|1111 0001   On/Off                 (0xF1)   [NOT TAP]\n * 101E|LLLL|1111 0010   Off/On                 (0xF2)   [NOT TAP]\n * 101E|LLLL|1111 0011   Set/Clear              (0xF3)   [NOT TAP]\n * 101E|LLLL|1111 0100   One Shot Layer         (0xF4)   [TAP]\n * 101E|LLLL|1111 xxxx   Reserved               (0xF5-FF)\n *   ELLLL: layer 0-31(E: extra bit for layer 16-31)\n */\nenum action_kind_id {\n    /* Key Actions */\n    ACT_MODS      = 0b0000,\n    ACT_LMODS     = 0b0000,\n    ACT_RMODS     = 0b0001,\n    ACT_MODS_TAP  = 0b0010,\n    ACT_LMODS_TAP = 0b0010,\n    ACT_RMODS_TAP = 0b0011,\n    /* Other Keys */\n    ACT_USAGE    = 0b0100,\n    ACT_MOUSEKEY = 0b0101,\n    /* One-hand Support */\n    ACT_SWAP_HANDS = 0b0110,\n    /* Layer Actions */\n    ACT_LAYER         = 0b1000,\n    ACT_LAYER_MODS    = 0b1001,\n    ACT_LAYER_TAP     = 0b1010, /* Layer  0-15 */\n    ACT_LAYER_TAP_EXT = 0b1011, /* Layer 16-31 */\n};\n\n/** \\brief Action Code Struct\n *\n * NOTE:\n * In avr-gcc bit field seems to be assigned from LSB(bit0) to MSB(bit15).\n * AVR looks like a little endian in avr-gcc.\n * Not portable across compiler/endianness?\n *\n * Byte order and bit order of 0x1234:\n *   Big endian:                Little endian:\n *   --------------------       --------------------\n *   FEDC BA98  7654 3210       0123 4567  89AB CDEF\n *   0001 0010  0011 0100       0010 1100  0100 1000\n *     0x12       0x34            0x34       0x12\n */\ntypedef union {\n    uint16_t code;\n    struct action_kind {\n        uint16_t param : 12;\n        uint8_t  id : 4;\n    } kind;\n    struct action_key {\n        uint8_t code : 8;\n        uint8_t mods : 4;\n        uint8_t kind : 4;\n    } key;\n    struct action_layer_bitop {\n        uint8_t bits : 4;\n        uint8_t xbit : 1;\n        uint8_t part : 3;\n        uint8_t on : 2;\n        uint8_t op : 2;\n        uint8_t kind : 4;\n    } layer_bitop;\n    struct action_layer_mods {\n        uint8_t mods : 8;\n        uint8_t layer : 4;\n        uint8_t kind : 4;\n    } layer_mods;\n    struct action_layer_tap {\n        uint8_t code : 8;\n        uint8_t val : 5;\n        uint8_t kind : 3;\n    } layer_tap;\n    struct action_usage {\n        uint16_t code : 10;\n        uint8_t  page : 2;\n        uint8_t  kind : 4;\n    } usage;\n    struct action_swap {\n        uint8_t code : 8;\n        uint8_t opt : 4;\n        uint8_t kind : 4;\n    } swap;\n} action_t;\n\n/* action utility */\n#define ACTION_NO 0\n#define ACTION_TRANSPARENT 1\n#define ACTION(kind, param) ((kind) << 12 | (param))\n\nenum mods_codes {\n    MODS_ONESHOT    = 0x00,\n    MODS_TAP_TOGGLE = 0x01,\n};\n#define ACTION_KEY(key) ACTION(ACT_MODS, (key))\n#define ACTION_MODS(mods) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | 0)\n#define ACTION_MODS_KEY(mods, key) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | (key))\n#define ACTION_MODS_TAP_KEY(mods, key) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | (key))\n#define ACTION_MODS_ONESHOT(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_ONESHOT)\n#define ACTION_MODS_TAP_TOGGLE(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_TAP_TOGGLE)\n\n/** \\brief Other Keys\n */\nenum usage_pages {\n    PAGE_SYSTEM,\n    PAGE_CONSUMER,\n};\n\n#define ACTION_USAGE_SYSTEM(id) ACTION(ACT_USAGE, PAGE_SYSTEM << 10 | (id))\n#define ACTION_USAGE_CONSUMER(id) ACTION(ACT_USAGE, PAGE_CONSUMER << 10 | (id))\n#define ACTION_MOUSEKEY(key) ACTION(ACT_MOUSEKEY, key)\n\n/** \\brief Layer Actions\n */\nenum layer_param_on {\n    ON_PRESS   = 1,\n    ON_RELEASE = 2,\n    ON_BOTH    = 3,\n};\n\n/** \\brief Layer Actions\n */\nenum layer_param_bit_op {\n    OP_BIT_AND = 0,\n    OP_BIT_OR  = 1,\n    OP_BIT_XOR = 2,\n    OP_BIT_SET = 3,\n};\n\n/** \\brief Layer Actions\n */\nenum layer_param_tap_op {\n    OP_TAP_TOGGLE = 0xF0,\n    OP_ON_OFF,\n    OP_OFF_ON,\n    OP_SET_CLEAR,\n    OP_ONESHOT,\n};\n#define ACTION_LAYER_BITOP(op, part, bits, on) ACTION(ACT_LAYER, (op) << 10 | (on) << 8 | (part) << 5 | ((bits)&0x1f))\n#define ACTION_LAYER_TAP(layer, key) ACTION(ACT_LAYER_TAP, (layer) << 8 | (key))\n/* Default Layer */\n#define ACTION_DEFAULT_LAYER_SET(layer) ACTION_DEFAULT_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4))\n/* Layer Operation */\n#define ACTION_LAYER_CLEAR(on) ACTION_LAYER_BIT_AND(0, 0, (on))\n#define ACTION_LAYER_MOMENTARY(layer) ACTION_LAYER_ON_OFF(layer)\n#define ACTION_LAYER_TOGGLE(layer) ACTION_LAYER_INVERT(layer, ON_RELEASE)\n#define ACTION_LAYER_INVERT(layer, on) ACTION_LAYER_BIT_XOR((layer) / 4, 1 << ((layer) % 4), (on))\n#define ACTION_LAYER_ON(layer, on) ACTION_LAYER_BIT_OR((layer) / 4, 1 << ((layer) % 4), (on))\n#define ACTION_LAYER_OFF(layer, on) ACTION_LAYER_BIT_AND((layer) / 4, ~(1 << ((layer) % 4)), (on))\n#define ACTION_LAYER_GOTO(layer) ACTION_LAYER_SET(layer, ON_PRESS)\n#define ACTION_LAYER_SET(layer, on) ACTION_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4), (on))\n#define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF)\n#define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON)\n#define ACTION_LAYER_SET_CLEAR(layer) ACTION_LAYER_TAP((layer), OP_SET_CLEAR)\n#define ACTION_LAYER_ONESHOT(layer) ACTION_LAYER_TAP((layer), OP_ONESHOT)\n#define ACTION_LAYER_MODS(layer, mods) ACTION(ACT_LAYER_MODS, (layer) << 8 | (mods))\n/* With Tapping */\n#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key))\n#define ACTION_LAYER_TAP_TOGGLE(layer) ACTION_LAYER_TAP((layer), OP_TAP_TOGGLE)\n/* Bitwise Operation */\n#define ACTION_LAYER_BIT_AND(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), (on))\n#define ACTION_LAYER_BIT_OR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), (on))\n#define ACTION_LAYER_BIT_XOR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), (on))\n#define ACTION_LAYER_BIT_SET(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), (on))\n/* Default Layer Bitwise Operation */\n#define ACTION_DEFAULT_LAYER_BIT_AND(part, bits) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), 0)\n#define ACTION_DEFAULT_LAYER_BIT_OR(part, bits) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), 0)\n#define ACTION_DEFAULT_LAYER_BIT_XOR(part, bits) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), 0)\n#define ACTION_DEFAULT_LAYER_BIT_SET(part, bits) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), 0)\n\n/* OneHand Support */\nenum swap_hands_param_tap_op {\n    OP_SH_TOGGLE = 0xF0,\n    OP_SH_TAP_TOGGLE,\n    OP_SH_ON_OFF,\n    OP_SH_OFF_ON,\n    OP_SH_OFF,\n    OP_SH_ON,\n    OP_SH_ONESHOT,\n};\n\n#define ACTION_SWAP_HANDS() ACTION_SWAP_HANDS_ON_OFF()\n#define ACTION_SWAP_HANDS_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TOGGLE)\n#define ACTION_SWAP_HANDS_TAP_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TAP_TOGGLE)\n#define ACTION_SWAP_HANDS_ONESHOT() ACTION(ACT_SWAP_HANDS, OP_SH_ONESHOT)\n#define ACTION_SWAP_HANDS_TAP_KEY(key) ACTION(ACT_SWAP_HANDS, key)\n#define ACTION_SWAP_HANDS_ON_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_ON_OFF)\n#define ACTION_SWAP_HANDS_OFF_ON() ACTION(ACT_SWAP_HANDS, OP_SH_OFF_ON)\n#define ACTION_SWAP_HANDS_ON() ACTION(ACT_SWAP_HANDS, OP_SH_ON)\n#define ACTION_SWAP_HANDS_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_OFF)\n"
  },
  {
    "path": "quantum/action_layer.c",
    "content": "#include <limits.h>\n#include <stdint.h>\n\n#include \"keyboard.h\"\n#include \"action.h\"\n#include \"encoder.h\"\n#include \"util.h\"\n#include \"action_layer.h\"\n\n/** \\brief Default Layer State\n */\nlayer_state_t default_layer_state = 0;\n\n/** \\brief Default Layer State Set At user Level\n *\n * Run user code on default layer state change\n */\n__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state) {\n    return state;\n}\n\n/** \\brief Default Layer State Set At Keyboard Level\n *\n *  Run keyboard code on default layer state change\n */\n__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state) {\n    return default_layer_state_set_user(state);\n}\n\n/** \\brief Default Layer State Set At Module Level\n *\n * Run module code on default layer state change\n */\n__attribute__((weak)) layer_state_t default_layer_state_set_modules(layer_state_t state) {\n    return state;\n}\n\n/** \\brief Default Layer State Set\n *\n * Static function to set the default layer state, prints debug info and clears keys\n */\nstatic void default_layer_state_set(layer_state_t state) {\n    state = default_layer_state_set_modules(state);\n    state = default_layer_state_set_kb(state);\n    ac_dprintf(\"default_layer_state: \");\n    default_layer_debug();\n    ac_dprintf(\" to \");\n    default_layer_state = state;\n    default_layer_debug();\n    ac_dprintf(\"\\n\");\n#if defined(STRICT_LAYER_RELEASE)\n    clear_keyboard_but_mods(); // To avoid stuck keys\n#elif defined(SEMI_STRICT_LAYER_RELEASE)\n    clear_keyboard_but_mods_and_keys(); // Don't reset held keys\n#endif\n}\n\n/** \\brief Default Layer Print\n *\n * Print out the hex value of the 32-bit default layer state, as well as the value of the highest bit.\n */\nvoid default_layer_debug(void) {\n    ac_dprintf(\"%08hX(%u)\", default_layer_state, get_highest_layer(default_layer_state));\n}\n\n/** \\brief Default Layer Set\n *\n * Sets the default layer state.\n */\nvoid default_layer_set(layer_state_t state) {\n    default_layer_state_set(state);\n}\n\n#ifndef NO_ACTION_LAYER\n/** \\brief Default Layer Or\n *\n * Turns on the default layer based on matching bits between specified layer and existing layer state\n */\nvoid default_layer_or(layer_state_t state) {\n    default_layer_state_set(default_layer_state | state);\n}\n/** \\brief Default Layer And\n *\n * Turns on default layer based on matching enabled bits between specified layer and existing layer state\n */\nvoid default_layer_and(layer_state_t state) {\n    default_layer_state_set(default_layer_state & state);\n}\n/** \\brief Default Layer Xor\n *\n * Turns on default layer based on non-matching bits between specified layer and existing layer state\n */\nvoid default_layer_xor(layer_state_t state) {\n    default_layer_state_set(default_layer_state ^ state);\n}\n#endif\n\n#ifndef NO_ACTION_LAYER\n/** \\brief Keymap Layer State\n */\nlayer_state_t layer_state = 0;\n\n/** \\brief Layer state set user\n *\n * Runs user code on layer state change\n */\n__attribute__((weak)) layer_state_t layer_state_set_user(layer_state_t state) {\n    return state;\n}\n\n/** \\brief Layer state set keyboard\n *\n * Runs keyboard code on layer state change\n */\n__attribute__((weak)) layer_state_t layer_state_set_kb(layer_state_t state) {\n    return layer_state_set_user(state);\n}\n\n/** \\brief Layer state set modules\n *\n * Runs module code on layer state change\n */\n\n__attribute__((weak)) layer_state_t layer_state_set_modules(layer_state_t state) {\n    return state;\n}\n\n/** \\brief Layer state set\n *\n * Sets the layer to match the specified state (a bitmask)\n */\nvoid layer_state_set(layer_state_t state) {\n    state = layer_state_set_modules(state);\n    state = layer_state_set_kb(state);\n    ac_dprintf(\"layer_state: \");\n    layer_debug();\n    ac_dprintf(\" to \");\n    layer_state = state;\n    layer_debug();\n    ac_dprintf(\"\\n\");\n#    if defined(STRICT_LAYER_RELEASE)\n    clear_keyboard_but_mods(); // To avoid stuck keys\n#    elif defined(SEMI_STRICT_LAYER_RELEASE)\n    clear_keyboard_but_mods_and_keys(); // Don't reset held keys\n#    endif\n}\n\n/** \\brief Layer clear\n *\n * Turn off all layers\n */\nvoid layer_clear(void) {\n    layer_state_set(0);\n}\n\n/** \\brief Layer state is\n *\n * Return whether the given state is on (it might still be shadowed by a higher state, though)\n */\nbool layer_state_is(uint8_t layer) {\n    return layer_state_cmp(layer_state, layer);\n}\n\n/** \\brief Layer state compare\n *\n * Used for comparing layers {mostly used for unit testing}\n */\nbool layer_state_cmp(layer_state_t cmp_layer_state, uint8_t layer) {\n    if (!cmp_layer_state) {\n        return layer == 0;\n    }\n    return (cmp_layer_state & ((layer_state_t)1 << layer)) != 0;\n}\n\n/** \\brief Layer move\n *\n * Turns on the given layer and turn off all other layers\n */\nvoid layer_move(uint8_t layer) {\n    layer_state_set((layer_state_t)1 << layer);\n}\n\n/** \\brief Layer on\n *\n * Turns on given layer\n */\nvoid layer_on(uint8_t layer) {\n    layer_state_set(layer_state | ((layer_state_t)1 << layer));\n}\n\n/** \\brief Layer off\n *\n * Turns off given layer\n */\nvoid layer_off(uint8_t layer) {\n    layer_state_set(layer_state & ~((layer_state_t)1 << layer));\n}\n\n/** \\brief Layer invert\n *\n * Toggle the given layer (set it if it's unset, or unset it if it's set)\n */\nvoid layer_invert(uint8_t layer) {\n    layer_state_set(layer_state ^ ((layer_state_t)1 << layer));\n}\n\n/** \\brief Layer or\n *\n * Turns on layers based on matching bits between specified layer and existing layer state\n */\nvoid layer_or(layer_state_t state) {\n    layer_state_set(layer_state | state);\n}\n/** \\brief Layer and\n *\n * Turns on layers based on matching enabled bits between specified layer and existing layer state\n */\nvoid layer_and(layer_state_t state) {\n    layer_state_set(layer_state & state);\n}\n/** \\brief Layer xor\n *\n * Turns on layers based on non-matching bits between specified layer and existing layer state\n */\nvoid layer_xor(layer_state_t state) {\n    layer_state_set(layer_state ^ state);\n}\n\n/** \\brief Layer debug printing\n *\n * Print out the hex value of the 32-bit layer state, as well as the value of the highest bit.\n */\nvoid layer_debug(void) {\n    ac_dprintf(\"%08hX(%u)\", layer_state, get_highest_layer(layer_state));\n}\n#endif\n\n#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)\n/** \\brief source layer cache\n */\n\nuint8_t source_layers_cache[((MATRIX_ROWS * MATRIX_COLS) + (CHAR_BIT)-1) / (CHAR_BIT)][MAX_LAYER_BITS] = {{0}};\n#    ifdef ENCODER_MAP_ENABLE\nuint8_t encoder_source_layers_cache[(NUM_ENCODERS + (CHAR_BIT)-1) / (CHAR_BIT)][MAX_LAYER_BITS] = {{0}};\n#    endif // ENCODER_MAP_ENABLE\n\n/** \\brief update source layers cache impl\n *\n * Updates the supplied cache when changing layers\n */\nvoid update_source_layers_cache_impl(uint8_t layer, uint16_t entry_number, uint8_t cache[][MAX_LAYER_BITS]) {\n    const uint16_t storage_idx = entry_number / (CHAR_BIT);\n    const uint8_t  storage_bit = entry_number % (CHAR_BIT);\n    for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {\n        cache[storage_idx][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ cache[storage_idx][bit_number]) & (1U << storage_bit);\n    }\n}\n\n/** \\brief read source layers cache\n *\n * reads the cached keys stored when the layer was changed\n */\nuint8_t read_source_layers_cache_impl(uint16_t entry_number, uint8_t cache[][MAX_LAYER_BITS]) {\n    const uint16_t storage_idx = entry_number / (CHAR_BIT);\n    const uint8_t  storage_bit = entry_number % (CHAR_BIT);\n    uint8_t        layer       = 0;\n\n    for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) {\n        layer |= ((cache[storage_idx][bit_number] & (1U << storage_bit)) != 0) << bit_number;\n    }\n\n    return layer;\n}\n\n/** \\brief update encoder source layers cache\n *\n * Updates the cached encoders when changing layers\n */\nvoid update_source_layers_cache(keypos_t key, uint8_t layer) {\n    if (key.row < MATRIX_ROWS && key.col < MATRIX_COLS) {\n        const uint16_t entry_number = (uint16_t)(key.row * MATRIX_COLS) + key.col;\n        update_source_layers_cache_impl(layer, entry_number, source_layers_cache);\n    }\n#    ifdef ENCODER_MAP_ENABLE\n    else if (key.row == KEYLOC_ENCODER_CW || key.row == KEYLOC_ENCODER_CCW) {\n        const uint16_t entry_number = key.col;\n        update_source_layers_cache_impl(layer, entry_number, encoder_source_layers_cache);\n    }\n#    endif // ENCODER_MAP_ENABLE\n}\n\n/** \\brief read source layers cache\n *\n * reads the cached keys stored when the layer was changed\n */\nuint8_t read_source_layers_cache(keypos_t key) {\n    if (key.row < MATRIX_ROWS && key.col < MATRIX_COLS) {\n        const uint16_t entry_number = (uint16_t)(key.row * MATRIX_COLS) + key.col;\n        return read_source_layers_cache_impl(entry_number, source_layers_cache);\n    }\n#    ifdef ENCODER_MAP_ENABLE\n    else if (key.row == KEYLOC_ENCODER_CW || key.row == KEYLOC_ENCODER_CCW) {\n        const uint16_t entry_number = key.col;\n        return read_source_layers_cache_impl(entry_number, encoder_source_layers_cache);\n    }\n#    endif // ENCODER_MAP_ENABLE\n    return 0;\n}\n#endif\n\n/** \\brief Store or get action (FIXME: Needs better summary)\n *\n * Make sure the action triggered when the key is released is the same\n * one as the one triggered on press. It's important for the mod keys\n * when the layer is switched after the down event but before the up\n * event as they may get stuck otherwise.\n */\naction_t store_or_get_action(bool pressed, keypos_t key) {\n#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)\n    if (disable_action_cache) {\n        return layer_switch_get_action(key);\n    }\n\n    uint8_t layer;\n\n    if (pressed) {\n        layer = layer_switch_get_layer(key);\n        update_source_layers_cache(key, layer);\n    } else {\n        layer = read_source_layers_cache(key);\n    }\n    return action_for_key(layer, key);\n#else\n    return layer_switch_get_action(key);\n#endif\n}\n\n/** \\brief Layer switch get layer\n *\n * Gets the layer based on key info\n */\nuint8_t layer_switch_get_layer(keypos_t key) {\n#ifndef NO_ACTION_LAYER\n    action_t action;\n    action.code = ACTION_TRANSPARENT;\n\n    layer_state_t layers = layer_state | default_layer_state;\n    /* check top layer first */\n    for (int8_t i = MAX_LAYER - 1; i >= 0; i--) {\n        if (layers & ((layer_state_t)1 << i)) {\n            action = action_for_key(i, key);\n            if (action.code != ACTION_TRANSPARENT) {\n                return i;\n            }\n        }\n    }\n    /* fall back to layer 0 */\n    return 0;\n#else\n    return get_highest_layer(default_layer_state);\n#endif\n}\n\n/** \\brief Layer switch get layer\n *\n * Gets action code based on key position\n */\naction_t layer_switch_get_action(keypos_t key) {\n    return action_for_key(layer_switch_get_layer(key), key);\n}\n\n#ifndef NO_ACTION_LAYER\nlayer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3) {\n    layer_state_t mask12 = ((layer_state_t)1 << layer1) | ((layer_state_t)1 << layer2);\n    layer_state_t mask3  = (layer_state_t)1 << layer3;\n    return (state & mask12) == mask12 ? (state | mask3) : (state & ~mask3);\n}\n\nvoid update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3) {\n    layer_state_set(update_tri_layer_state(layer_state, layer1, layer2, layer3));\n}\n#endif\n"
  },
  {
    "path": "quantum/action_layer.h",
    "content": "/*\nCopyright 2013 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include \"keyboard.h\"\n#include \"action.h\"\n#include \"bitwise.h\"\n\n#ifdef DYNAMIC_KEYMAP_ENABLE\n#    ifndef DYNAMIC_KEYMAP_LAYER_COUNT\n#        define DYNAMIC_KEYMAP_LAYER_COUNT 4\n#    endif\n#    define MAX_LAYER DYNAMIC_KEYMAP_LAYER_COUNT\n#    if DYNAMIC_KEYMAP_LAYER_COUNT <= 8\n#        ifndef LAYER_STATE_8BIT\n#            define LAYER_STATE_8BIT\n#        endif\n#    elif DYNAMIC_KEYMAP_LAYER_COUNT <= 16\n#        ifndef LAYER_STATE_16BIT\n#            define LAYER_STATE_16BIT\n#        endif\n#    else\n#        ifndef LAYER_STATE_32BIT\n#            define LAYER_STATE_32BIT\n#        endif\n#    endif\n#endif\n\n#if !defined(LAYER_STATE_8BIT) && !defined(LAYER_STATE_16BIT) && !defined(LAYER_STATE_32BIT)\n#    define LAYER_STATE_16BIT\n#endif\n\n#if defined(LAYER_STATE_8BIT)\ntypedef uint8_t layer_state_t;\n#    define MAX_LAYER_BITS 3\n#    ifndef MAX_LAYER\n#        define MAX_LAYER 8\n#    endif\n#    define get_highest_layer(state) biton(state)\n#elif defined(LAYER_STATE_16BIT)\ntypedef uint16_t layer_state_t;\n#    define MAX_LAYER_BITS 4\n#    ifndef MAX_LAYER\n#        define MAX_LAYER 16\n#    endif\n#    define get_highest_layer(state) biton16(state)\n#elif defined(LAYER_STATE_32BIT)\ntypedef uint32_t layer_state_t;\n#    define MAX_LAYER_BITS 5\n#    ifndef MAX_LAYER\n#        define MAX_LAYER 32\n#    endif\n#    define get_highest_layer(state) biton32(state)\n#else\n#    error Layer Mask size not specified.  HOW?!\n#endif\n\n/*\n * Default Layer\n */\nextern layer_state_t default_layer_state;\nvoid                 default_layer_debug(void);\nvoid                 default_layer_set(layer_state_t state);\n\n__attribute__((weak)) layer_state_t default_layer_state_set_modules(layer_state_t state);\n__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state);\n__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state);\n\n#ifndef NO_ACTION_LAYER\n/* bitwise operation */\nvoid default_layer_or(layer_state_t state);\nvoid default_layer_and(layer_state_t state);\nvoid default_layer_xor(layer_state_t state);\n#else\n#    define default_layer_or(state)\n#    define default_layer_and(state)\n#    define default_layer_xor(state)\n#endif\n\n/*\n * Keymap Layer\n */\n#ifndef NO_ACTION_LAYER\nextern layer_state_t layer_state;\n\nvoid layer_state_set(layer_state_t state);\nbool layer_state_is(uint8_t layer);\nbool layer_state_cmp(layer_state_t layer1, uint8_t layer2);\n\nvoid layer_debug(void);\nvoid layer_clear(void);\nvoid layer_move(uint8_t layer);\nvoid layer_on(uint8_t layer);\nvoid layer_off(uint8_t layer);\nvoid layer_invert(uint8_t layer);\n/* bitwise operation */\nvoid          layer_or(layer_state_t state);\nvoid          layer_and(layer_state_t state);\nvoid          layer_xor(layer_state_t state);\nlayer_state_t layer_state_set_user(layer_state_t state);\nlayer_state_t layer_state_set_kb(layer_state_t state);\nlayer_state_t layer_state_set_modules(layer_state_t state);\n\n/**\n * @brief Applies the tri layer to global layer state. Not be used in layer_state_set_(kb|user) functions.\n *\n * @param layer1 First layer to check for tri layer\n * @param layer2 Second layer to check for tri layer\n * @param layer3 Layer to activate if both other layers are enabled\n */\nvoid update_tri_layer(uint8_t layer1, uint8_t layer2, uint8_t layer3);\n/**\n * @brief Applies the tri layer behavior to supplied layer bitmask, without using layer functions.\n *\n * @param state Original layer bitmask to check and modify\n * @param layer1 First layer to check for tri layer\n * @param layer2 Second layer to check for tri layer\n * @param layer3 Layer to activate if both other layers are enabled\n * @return layer_state_t returns a modified layer bitmask with tri layer modifications applied\n */\nlayer_state_t update_tri_layer_state(layer_state_t state, uint8_t layer1, uint8_t layer2, uint8_t layer3);\n#else\n#    define layer_state 0\n\n#    define layer_state_set(layer)\n#    define layer_state_is(layer) (layer == 0)\n#    define layer_state_cmp(state, layer) (state == 0 ? layer == 0 : (state & (layer_state_t)1 << layer) != 0)\n\n#    define layer_debug()\n#    define layer_clear()\n#    define layer_move(layer) (void)layer\n#    define layer_on(layer) (void)layer\n#    define layer_off(layer) (void)layer\n#    define layer_invert(layer) (void)layer\n#    define layer_or(state) (void)state\n#    define layer_and(state) (void)state\n#    define layer_xor(state) (void)state\n#    define layer_state_set_modules(state) (void)state\n#    define layer_state_set_kb(state) (void)state\n#    define layer_state_set_user(state) (void)state\n#    define update_tri_layer(layer1, layer2, layer3)\n#    define update_tri_layer_state(state, layer1, layer2, layer3) (void)state\n#endif\n\n/* pressed actions cache */\n#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)\n\nvoid    update_source_layers_cache(keypos_t key, uint8_t layer);\nuint8_t read_source_layers_cache(keypos_t key);\n#endif\naction_t store_or_get_action(bool pressed, keypos_t key);\n\n/* return the topmost non-transparent layer currently associated with key */\nuint8_t layer_switch_get_layer(keypos_t key);\n\n/* return action depending on current layer status */\naction_t layer_switch_get_action(keypos_t key);\n"
  },
  {
    "path": "quantum/action_tapping.c",
    "content": "#include <stdint.h>\n#include <stdbool.h>\n\n#include \"action.h\"\n#include \"action_layer.h\"\n#include \"action_tapping.h\"\n#include \"action_util.h\"\n#include \"keycode.h\"\n#include \"quantum_keycodes.h\"\n#include \"timer.h\"\n\n#ifndef NO_ACTION_TAPPING\n\n#    if defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY)\n#        error \"IGNORE_MOD_TAP_INTERRUPT_PER_KEY has been removed; the code needs to be ported to use HOLD_ON_OTHER_KEY_PRESS_PER_KEY instead.\"\n#    elif defined(IGNORE_MOD_TAP_INTERRUPT)\n#        error \"IGNORE_MOD_TAP_INTERRUPT is no longer necessary as it is now the default behavior of mod-tap keys. Please remove it from your config.\"\n#    endif\n\n#    ifndef COMBO_ENABLE\n#        define IS_TAPPING_RECORD(r) (KEYEQ(tapping_key.event.key, (r->event.key)))\n#    else\n#        define IS_TAPPING_RECORD(r) (KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode)\n#    endif\n#    define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < GET_TAPPING_TERM(get_record_keycode(&tapping_key, false), &tapping_key))\n#    define WITHIN_QUICK_TAP_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < GET_QUICK_TAP_TERM(get_record_keycode(&tapping_key, false), &tapping_key))\n\n#    ifdef DYNAMIC_TAPPING_TERM_ENABLE\nuint16_t g_tapping_term = TAPPING_TERM;\n#    endif\n\n#    ifdef TAPPING_TERM_PER_KEY\n__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) {\n#        ifdef DYNAMIC_TAPPING_TERM_ENABLE\n    return g_tapping_term;\n#        else\n    return TAPPING_TERM;\n#        endif\n}\n#    endif\n\n#    ifdef QUICK_TAP_TERM_PER_KEY\n__attribute__((weak)) uint16_t get_quick_tap_term(uint16_t keycode, keyrecord_t *record) {\n    return QUICK_TAP_TERM;\n}\n#    endif\n\n#    ifdef PERMISSIVE_HOLD_PER_KEY\n__attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) {\n    return false;\n}\n#    endif\n\n#    if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)\n#        define REGISTERED_TAPS_SIZE 8\n// Array of tap-hold keys that have been settled as tapped but not yet released.\nstatic keypos_t registered_taps[REGISTERED_TAPS_SIZE] = {};\nstatic uint8_t  num_registered_taps                   = 0;\n\n/** Adds `key` to the registered_taps array. */\nstatic void registered_taps_add(keypos_t key);\n/** Returns the index of `key` in registered_taps, or -1 if not found. */\nstatic int8_t registered_tap_find(keypos_t key);\n/** Removes index `i` from the registered_taps array. */\nstatic void registered_taps_del_index(uint8_t i);\n/** Logs the registered_taps array for debugging. */\nstatic void debug_registered_taps(void);\n\nstatic bool is_mt_or_lt(uint16_t keycode) {\n    return IS_QK_MOD_TAP(keycode) || IS_QK_LAYER_TAP(keycode);\n}\n#    endif // defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)\n\n#    if defined(CHORDAL_HOLD)\nextern const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM;\n\n/** \\brief Finds which queued events should be held according to Chordal Hold.\n *\n * In a situation with multiple unsettled tap-hold key presses, scan the queue\n * up until the first release, non-tap-hold, or one-shot event and find the\n * latest event in the queue that settles as held according to\n * get_chordal_hold().\n *\n * \\return Index of the first tap, or equivalently, one past the latest hold.\n */\nstatic uint8_t waiting_buffer_find_chordal_hold_tap(void);\n\n/** Processes queued events up to and including `key` as tapped. */\nstatic void waiting_buffer_chordal_hold_taps_until(keypos_t key);\n\n/** \\brief Processes and pops buffered events until the first tap-hold event. */\nstatic void waiting_buffer_process_regular(void);\n#    endif // CHORDAL_HOLD\n\n#    ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY\n__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) {\n    return false;\n}\n#    endif\n\n#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)\n#        include \"process_auto_shift.h\"\n#    endif\n\n#    if defined(FLOW_TAP_TERM)\nstatic uint16_t flow_tap_prev_keycode = KC_NO;\nstatic uint16_t flow_tap_prev_time    = 0;\nstatic bool     flow_tap_expired      = true;\n\nstatic bool flow_tap_key_if_within_term(keyrecord_t *record, uint16_t prev_time);\n#    endif // defined(FLOW_TAP_TERM)\n\nstatic keyrecord_t tapping_key                         = {};\nstatic keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {};\nstatic uint8_t     waiting_buffer_head                 = 0;\nstatic uint8_t     waiting_buffer_tail                 = 0;\n\nstatic bool process_tapping(keyrecord_t *record);\nstatic bool waiting_buffer_enq(keyrecord_t record);\nstatic void waiting_buffer_clear(void);\nstatic bool waiting_buffer_typed(keyevent_t event);\nstatic bool waiting_buffer_has_anykey_pressed(void);\nstatic void waiting_buffer_scan_tap(void);\nstatic void debug_tapping_key(void);\nstatic void debug_waiting_buffer(void);\n\n/** \\brief Action Tapping Process\n *\n * FIXME: Needs doc\n */\nvoid action_tapping_process(keyrecord_t record) {\n    if (process_tapping(&record)) {\n        if (IS_EVENT(record.event)) {\n            ac_dprintf(\"processed: \");\n            debug_record(record);\n            ac_dprintf(\"\\n\");\n        }\n    } else {\n        if (!waiting_buffer_enq(record)) {\n            // clear all in case of overflow.\n            ac_dprintf(\"OVERFLOW: CLEAR ALL STATES\\n\");\n            clear_keyboard();\n            waiting_buffer_clear();\n            tapping_key = (keyrecord_t){0};\n        }\n    }\n\n    // process waiting_buffer\n    if (IS_EVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) {\n        ac_dprintf(\"---- action_exec: process waiting_buffer -----\\n\");\n    }\n    for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {\n        if (process_tapping(&waiting_buffer[waiting_buffer_tail])) {\n            ac_dprintf(\"processed: waiting_buffer[%u] =\", waiting_buffer_tail);\n            debug_record(waiting_buffer[waiting_buffer_tail]);\n            ac_dprintf(\"\\n\\n\");\n        } else {\n            break;\n        }\n    }\n    if (IS_EVENT(record.event)) {\n        ac_dprintf(\"\\n\");\n    } else {\n#    ifdef FLOW_TAP_TERM\n        if (!flow_tap_expired && TIMER_DIFF_16(record.event.time, flow_tap_prev_time) >= INT16_MAX / 2) {\n            flow_tap_expired = true;\n        }\n#    endif // FLOW_TAP_TERM\n    }\n}\n\n/* Some conditionally defined helper macros to keep process_tapping more\n * readable. The conditional definition of tapping_keycode and all the\n * conditional uses of it are hidden inside macros named TAP_...\n */\n#    define TAP_DEFINE_KEYCODE const uint16_t tapping_keycode = get_record_keycode(&tapping_key, false)\n\n#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)\n#        ifdef RETRO_TAPPING_PER_KEY\n#            define TAP_GET_RETRO_TAPPING(keyp) get_auto_shifted_key(tapping_keycode, keyp) && get_retro_tapping(tapping_keycode, &tapping_key)\n#        else\n#            define TAP_GET_RETRO_TAPPING(keyp) get_auto_shifted_key(tapping_keycode, keyp)\n#        endif\n/* Used to extend TAPPING_TERM:\n *     indefinitely if RETRO_SHIFT does not have a value\n *     to RETRO_SHIFT if RETRO_SHIFT is set\n * for possibly retro shifted keys.\n */\n#        define MAYBE_RETRO_SHIFTING(ev, keyp) (get_auto_shifted_key(tapping_keycode, keyp) && TAP_GET_RETRO_TAPPING(keyp) && ((RETRO_SHIFT + 0) == 0 || TIMER_DIFF_16((ev).time, tapping_key.event.time) < (RETRO_SHIFT + 0)))\n#        define TAP_IS_LT IS_QK_LAYER_TAP(tapping_keycode)\n#        define TAP_IS_MT IS_QK_MOD_TAP(tapping_keycode)\n#        define TAP_IS_RETRO IS_RETRO(tapping_keycode)\n#    else\n#        define TAP_GET_RETRO_TAPPING(keyp) false\n#        define MAYBE_RETRO_SHIFTING(ev, kp) false\n#        define TAP_IS_LT false\n#        define TAP_IS_MT false\n#        define TAP_IS_RETRO false\n#    endif\n\n#    ifdef PERMISSIVE_HOLD_PER_KEY\n#        define TAP_GET_PERMISSIVE_HOLD get_permissive_hold(tapping_keycode, &tapping_key)\n#    elif defined(PERMISSIVE_HOLD)\n#        define TAP_GET_PERMISSIVE_HOLD true\n#    else\n#        define TAP_GET_PERMISSIVE_HOLD false\n#    endif\n\n#    ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY\n#        define TAP_GET_HOLD_ON_OTHER_KEY_PRESS get_hold_on_other_key_press(tapping_keycode, &tapping_key)\n#    elif defined(HOLD_ON_OTHER_KEY_PRESS)\n#        define TAP_GET_HOLD_ON_OTHER_KEY_PRESS true\n#    else\n#        define TAP_GET_HOLD_ON_OTHER_KEY_PRESS false\n#    endif\n\n/** \\brief Tapping\n *\n * Rule: Tap key is typed(pressed and released) within TAPPING_TERM.\n *       (without interfering by typing other key)\n */\n/* return true when key event is processed or consumed. */\nbool process_tapping(keyrecord_t *keyp) {\n    const keyevent_t event = keyp->event;\n\n#    if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)\n    if (!event.pressed) {\n        const int8_t i = registered_tap_find(event.key);\n        if (i != -1) {\n            // If a tap-hold key was previously settled as tapped, set its\n            // tap.count correspondingly on release.\n            keyp->tap.count = 1;\n            registered_taps_del_index(i);\n            ac_dprintf(\"Found tap release for [%d]\\n\", i);\n            debug_registered_taps();\n        }\n    }\n#    endif // defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)\n\n    // state machine is in the \"reset\" state, no tapping key is to be\n    // processed\n    if (IS_NOEVENT(tapping_key.event)) {\n        if (!IS_EVENT(event)) {\n            // early return for tick events\n        } else if (event.pressed && is_tap_record(keyp)) {\n            // the currently pressed key is a tapping key, therefore transition\n            // into the \"pressed\" tapping key state\n\n#    if defined(FLOW_TAP_TERM)\n            if (flow_tap_key_if_within_term(keyp, flow_tap_prev_time)) {\n                return true;\n            }\n#    endif // defined(FLOW_TAP_TERM)\n\n            ac_dprintf(\"Tapping: Start(Press tap key).\\n\");\n            tapping_key = *keyp;\n            process_record_tap_hint(&tapping_key);\n            waiting_buffer_scan_tap();\n            debug_tapping_key();\n        } else {\n            // the current key is just a regular key, pass it on for regular\n            // processing\n            process_record(keyp);\n        }\n\n        return true;\n    }\n\n#    if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)) || defined(PERMISSIVE_HOLD_PER_KEY) || defined(CHORDAL_HOLD) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY)\n    TAP_DEFINE_KEYCODE;\n#    endif\n\n    // process \"pressed\" tapping key state\n    if (tapping_key.event.pressed) {\n        if (WITHIN_TAPPING_TERM(event) || MAYBE_RETRO_SHIFTING(event, keyp)) {\n            if (IS_NOEVENT(event)) {\n                // early return for tick events\n                return true;\n            }\n\n            if (tapping_key.tap.count == 0) {\n                if (IS_TAPPING_RECORD(keyp) && !event.pressed) {\n                    // first tap!\n                    ac_dprintf(\"Tapping: First tap(0->1).\\n\");\n                    tapping_key.tap.count = 1;\n                    debug_tapping_key();\n                    process_record(&tapping_key);\n\n                    // copy tapping state\n                    keyp->tap = tapping_key.tap;\n\n#    if defined(FLOW_TAP_TERM)\n                    // Now that tapping_key has settled as tapped, check whether\n                    // Flow Tap applies to following yet-unsettled keys.\n                    uint16_t prev_time = tapping_key.event.time;\n                    for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {\n                        keyrecord_t *record = &waiting_buffer[waiting_buffer_tail];\n                        if (!record->event.pressed) {\n                            break;\n                        }\n                        const int16_t next_time = record->event.time;\n                        if (!is_tap_record(record)) {\n                            process_record(record);\n                        } else if (!flow_tap_key_if_within_term(record, prev_time)) {\n                            break;\n                        }\n                        prev_time = next_time;\n                    }\n                    debug_waiting_buffer();\n#    endif // defined(FLOW_TAP_TERM)\n\n                    // enqueue\n                    return false;\n                }\n#    if defined(CHORDAL_HOLD)\n                else if (is_mt_or_lt(tapping_keycode) && !event.pressed && waiting_buffer_typed(event) && !get_chordal_hold(tapping_keycode, &tapping_key, get_record_keycode(keyp, false), keyp)) {\n                    // Key release that is not a chord with the tapping key.\n                    // Settle the tapping key and any other pending tap-hold\n                    // keys preceding the press of this key as tapped.\n\n                    ac_dprintf(\"Tapping: End. Chord considered a tap\\n\");\n                    tapping_key.tap.count = 1;\n                    registered_taps_add(tapping_key.event.key);\n                    process_record(&tapping_key);\n                    tapping_key = (keyrecord_t){0};\n\n                    waiting_buffer_chordal_hold_taps_until(event.key);\n                    debug_registered_taps();\n                    debug_waiting_buffer();\n                    // enqueue\n                    return false;\n                }\n#    endif      // CHORDAL_HOLD\n                /* Process a key typed within TAPPING_TERM\n                 * This can register the key before settlement of tapping,\n                 * useful for long TAPPING_TERM but may prevent fast typing.\n                 */\n                // clang-format off\n                else if (\n                    !event.pressed && waiting_buffer_typed(event) &&\n                    (\n                        TAP_GET_PERMISSIVE_HOLD ||\n                        // Causes nested taps to not wait past TAPPING_TERM/RETRO_SHIFT\n                        // unnecessarily and fixes them for Layer Taps.\n                        TAP_GET_RETRO_TAPPING(keyp)\n                    )\n                ) {\n                    // clang-format on\n                    ac_dprintf(\"Tapping: End. No tap. Interfered by typing key\\n\");\n                    process_record(&tapping_key);\n\n#    if defined(CHORDAL_HOLD)\n                    uint8_t first_tap = waiting_buffer_find_chordal_hold_tap();\n                    ac_dprintf(\"first_tap = %u\\n\", first_tap);\n                    if (first_tap < WAITING_BUFFER_SIZE) {\n                        for (; waiting_buffer_tail != first_tap; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {\n                            ac_dprintf(\"Processing [%u]\\n\", waiting_buffer_tail);\n                            process_record(&waiting_buffer[waiting_buffer_tail]);\n                        }\n                    }\n\n                    waiting_buffer_chordal_hold_taps_until(event.key);\n                    debug_registered_taps();\n                    debug_waiting_buffer();\n#    endif // CHORDAL_HOLD\n\n                    tapping_key = (keyrecord_t){0};\n                    debug_tapping_key();\n                    // enqueue\n                    return false;\n                }\n                /* Process release event of a key pressed before tapping starts\n                 * Without this unexpected repeating will occur with having fast repeating setting\n                 * https://github.com/tmk/tmk_keyboard/issues/60\n                 *\n                 * NOTE: This workaround causes events to process out of order,\n                 * e.g. in a rolled press of three tap-hold keys like\n                 *\n                 *   \"A down, B down, C down, A up, B up, C up\"\n                 *\n                 * events are processed as\n                 *\n                 *   \"A down, B down, A up, B up, C down, C up\"\n                 *\n                 * It seems incorrect to process keyp before the tapping key.\n                 * This workaround is old, from 2013. This might no longer\n                 * be needed for the original problem it was meant to address.\n                 */\n                else if (!event.pressed && !waiting_buffer_typed(event)) {\n                    // Modifier/Layer should be retained till end of this tapping.\n                    action_t action = layer_switch_get_action(event.key);\n                    switch (action.kind.id) {\n                        case ACT_LMODS:\n                        case ACT_RMODS:\n                            if (action.key.mods && !action.key.code) return false;\n                            if (IS_MODIFIER_KEYCODE(action.key.code)) return false;\n                            break;\n                        case ACT_LMODS_TAP:\n                        case ACT_RMODS_TAP:\n                            if (action.key.mods && keyp->tap.count == 0) return false;\n                            if (IS_MODIFIER_KEYCODE(action.key.code)) return false;\n                            break;\n                        case ACT_LAYER_TAP:\n                        case ACT_LAYER_TAP_EXT:\n                            switch (action.layer_tap.code) {\n                                case 0 ...(OP_TAP_TOGGLE - 1):\n                                case OP_ON_OFF:\n                                case OP_OFF_ON:\n                                case OP_SET_CLEAR:\n                                    return false;\n                            }\n                            break;\n                    }\n                    // Release of key should be process immediately.\n                    ac_dprintf(\"Tapping: release event of a key pressed before tapping\\n\");\n                    process_record(keyp);\n                    return true;\n                } else {\n                    // set interrupted flag when other key pressed during tapping\n                    if (event.pressed) {\n                        tapping_key.tap.interrupted = true;\n\n#    if defined(CHORDAL_HOLD)\n                        if (is_mt_or_lt(tapping_keycode) && !get_chordal_hold(tapping_keycode, &tapping_key, get_record_keycode(keyp, false), keyp)) {\n                            // In process_action(), HOLD_ON_OTHER_KEY_PRESS\n                            // will revert interrupted events to holds, so\n                            // this needs to be set false.\n                            tapping_key.tap.interrupted = false;\n\n                            if (!is_tap_record(keyp)) {\n                                ac_dprintf(\"Tapping: End. Chord considered a tap\\n\");\n                                tapping_key.tap.count = 1;\n                                registered_taps_add(tapping_key.event.key);\n                                debug_registered_taps();\n                                process_record(&tapping_key);\n                                tapping_key = (keyrecord_t){0};\n                            }\n                        } else\n#    endif // CHORDAL_HOLD\n                            if (TAP_GET_HOLD_ON_OTHER_KEY_PRESS\n#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)\n                                // Auto Shift cannot evaluate this early\n                                // Retro Shift uses the hold action for all nested taps even without HOLD_ON_OTHER_KEY_PRESS, so this is fine to skip\n                                && !(MAYBE_RETRO_SHIFTING(event, keyp) && get_auto_shifted_key(get_record_keycode(keyp, false), keyp))\n#    endif\n                            ) {\n                            // Settle the tapping key as *held*, since\n                            // HOLD_ON_OTHER_KEY_PRESS is enabled for this key.\n                            ac_dprintf(\"Tapping: End. No tap. Interfered by pressed key\\n\");\n                            process_record(&tapping_key);\n\n#    if defined(CHORDAL_HOLD)\n                            if (waiting_buffer_tail != waiting_buffer_head && is_tap_record(&waiting_buffer[waiting_buffer_tail])) {\n                                tapping_key = waiting_buffer[waiting_buffer_tail];\n                                // Pop tail from the queue.\n                                waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE;\n                                debug_waiting_buffer();\n                            } else\n#    endif // CHORDAL_HOLD\n                            {\n                                tapping_key = (keyrecord_t){0};\n                            }\n                            debug_tapping_key();\n\n#    if defined(CHORDAL_HOLD)\n                            waiting_buffer_process_regular();\n#    endif // CHORDAL_HOLD\n                        }\n                    }\n                    // enqueue\n                    return false;\n                }\n            }\n            // tap_count > 0\n            else {\n                if (IS_TAPPING_RECORD(keyp) && !event.pressed) {\n                    ac_dprintf(\"Tapping: Tap release(%u)\\n\", tapping_key.tap.count);\n                    keyp->tap = tapping_key.tap;\n                    process_record(keyp);\n                    tapping_key = *keyp;\n                    debug_tapping_key();\n                    return true;\n                } else if (is_tap_record(keyp) && event.pressed) {\n                    if (tapping_key.tap.count > 1) {\n                        ac_dprintf(\"Tapping: Start new tap with releasing last tap(>1).\\n\");\n                        // unregister key\n                        process_record(&(keyrecord_t){\n                            .tap           = tapping_key.tap,\n                            .event.key     = tapping_key.event.key,\n                            .event.time    = event.time,\n                            .event.pressed = false,\n                            .event.type    = tapping_key.event.type,\n#    ifdef COMBO_ENABLE\n                            .keycode = tapping_key.keycode,\n#    endif\n                        });\n                    } else {\n                        ac_dprintf(\"Tapping: Start while last tap(1).\\n\");\n                    }\n                    tapping_key = *keyp;\n                    waiting_buffer_scan_tap();\n                    debug_tapping_key();\n                    return true;\n                } else {\n                    ac_dprintf(\"Tapping: key event while last tap(>0).\\n\");\n#    if defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT)\n                    retroshift_swap_times();\n#    endif\n                    process_record(keyp);\n                    return true;\n                }\n            }\n        }\n        // after TAPPING_TERM\n        else {\n            if (tapping_key.tap.count == 0) {\n                ac_dprintf(\"Tapping: End. Timeout. Not tap(0): \");\n                debug_event(event);\n                ac_dprintf(\"\\n\");\n                process_record(&tapping_key);\n                tapping_key = (keyrecord_t){0};\n                debug_tapping_key();\n                return false;\n            } else {\n                if (IS_NOEVENT(event)) {\n                    return true;\n                }\n                if (IS_TAPPING_RECORD(keyp) && !event.pressed) {\n                    ac_dprintf(\"Tapping: End. last timeout tap release(>0).\");\n                    keyp->tap = tapping_key.tap;\n                    process_record(keyp);\n                    tapping_key = (keyrecord_t){0};\n                    return true;\n                } else if (is_tap_record(keyp) && event.pressed) {\n                    if (tapping_key.tap.count > 1) {\n                        ac_dprintf(\"Tapping: Start new tap with releasing last timeout tap(>1).\\n\");\n                        // unregister key\n                        process_record(&(keyrecord_t){\n                            .tap           = tapping_key.tap,\n                            .event.key     = tapping_key.event.key,\n                            .event.time    = event.time,\n                            .event.pressed = false,\n                            .event.type    = tapping_key.event.type,\n#    ifdef COMBO_ENABLE\n                            .keycode = tapping_key.keycode,\n#    endif\n                        });\n                    } else {\n                        ac_dprintf(\"Tapping: Start while last timeout tap(1).\\n\");\n                    }\n                    tapping_key = *keyp;\n                    waiting_buffer_scan_tap();\n                    debug_tapping_key();\n                    return true;\n                } else {\n                    ac_dprintf(\"Tapping: key event while last timeout tap(>0).\\n\");\n                    process_record(keyp);\n                    return true;\n                }\n            }\n        }\n    }\n    // process \"released\" tapping key state\n    else {\n        if (WITHIN_TAPPING_TERM(event) || MAYBE_RETRO_SHIFTING(event, keyp)) {\n            if (IS_NOEVENT(event)) {\n                // early return for tick events\n                return true;\n            }\n            if (event.pressed) {\n                if (IS_TAPPING_RECORD(keyp)) {\n                    if (WITHIN_QUICK_TAP_TERM(event) && !tapping_key.tap.interrupted && tapping_key.tap.count > 0) {\n                        // sequential tap.\n                        keyp->tap = tapping_key.tap;\n                        if (keyp->tap.count < 15) keyp->tap.count += 1;\n                        ac_dprintf(\"Tapping: Tap press(%u)\\n\", keyp->tap.count);\n                        process_record(keyp);\n                        tapping_key = *keyp;\n                        debug_tapping_key();\n                        return true;\n                    }\n                    // FIX: start new tap again\n                    tapping_key = *keyp;\n                    return true;\n                } else if (is_tap_record(keyp)) {\n                    // Sequential tap can be interfered with other tap key.\n#    if defined(FLOW_TAP_TERM)\n                    if (flow_tap_key_if_within_term(keyp, flow_tap_prev_time)) {\n                        tapping_key = (keyrecord_t){0};\n                        debug_tapping_key();\n                        return true;\n                    }\n#    endif // defined(FLOW_TAP_TERM)\n                    ac_dprintf(\"Tapping: Start with interfering other tap.\\n\");\n                    tapping_key = *keyp;\n                    waiting_buffer_scan_tap();\n                    debug_tapping_key();\n                    return true;\n                } else {\n                    // should none in buffer\n                    // FIX: interrupted when other key is pressed\n                    tapping_key.tap.interrupted = true;\n                    process_record(keyp);\n                    return true;\n                }\n            } else {\n                ac_dprintf(\"Tapping: other key just after tap.\\n\");\n                process_record(keyp);\n                return true;\n            }\n        } else {\n            // Timeout - reset state machine.\n            ac_dprintf(\"Tapping: End(Timeout after releasing last tap): \");\n            debug_event(event);\n            ac_dprintf(\"\\n\");\n            tapping_key = (keyrecord_t){0};\n            debug_tapping_key();\n            return false;\n        }\n    }\n}\n\n/** \\brief Waiting buffer enq\n *\n * FIXME: Needs docs\n */\nbool waiting_buffer_enq(keyrecord_t record) {\n    if (IS_NOEVENT(record.event)) {\n        return true;\n    }\n\n    if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) {\n        ac_dprintf(\"waiting_buffer_enq: Over flow.\\n\");\n        return false;\n    }\n\n    waiting_buffer[waiting_buffer_head] = record;\n    waiting_buffer_head                 = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE;\n\n    ac_dprintf(\"waiting_buffer_enq: \");\n    debug_waiting_buffer();\n    return true;\n}\n\n/** \\brief Waiting buffer clear\n *\n * FIXME: Needs docs\n */\nvoid waiting_buffer_clear(void) {\n    waiting_buffer_head = 0;\n    waiting_buffer_tail = 0;\n}\n\n/** \\brief Waiting buffer typed\n *\n * FIXME: Needs docs\n */\nbool waiting_buffer_typed(keyevent_t event) {\n    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {\n        if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) {\n            return true;\n        }\n    }\n    return false;\n}\n\n/** \\brief Waiting buffer has anykey pressed\n *\n * FIXME: Needs docs\n */\n__attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) {\n    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {\n        if (waiting_buffer[i].event.pressed) return true;\n    }\n    return false;\n}\n\n/** \\brief Scan buffer for tapping\n *\n * FIXME: Needs docs\n */\nvoid waiting_buffer_scan_tap(void) {\n    // early return if:\n    // - tapping already is settled\n    // - invalid state: tapping_key released && tap.count == 0\n    if ((tapping_key.tap.count > 0) || !tapping_key.event.pressed) {\n        return;\n    }\n\n#    if (defined(AUTO_SHIFT_ENABLE) && defined(RETRO_SHIFT))\n    TAP_DEFINE_KEYCODE;\n#    endif\n    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {\n        keyrecord_t *candidate = &waiting_buffer[i];\n        // clang-format off\n        if (IS_EVENT(candidate->event) && KEYEQ(candidate->event.key, tapping_key.event.key) && !candidate->event.pressed && (\n            WITHIN_TAPPING_TERM(waiting_buffer[i].event) || MAYBE_RETRO_SHIFTING(waiting_buffer[i].event, &tapping_key)\n        )) {\n            // clang-format on\n            tapping_key.tap.count = 1;\n            candidate->tap.count  = 1;\n            process_record(&tapping_key);\n\n            ac_dprintf(\"waiting_buffer_scan_tap: found at [%u]\\n\", i);\n            debug_waiting_buffer();\n            return;\n        }\n    }\n}\n\n#    if defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)\nstatic void registered_taps_add(keypos_t key) {\n    if (num_registered_taps >= REGISTERED_TAPS_SIZE) {\n        ac_dprintf(\"TAPS OVERFLOW: CLEAR ALL STATES\\n\");\n        clear_keyboard();\n        num_registered_taps = 0;\n    }\n\n    registered_taps[num_registered_taps] = key;\n    ++num_registered_taps;\n}\n\nstatic int8_t registered_tap_find(keypos_t key) {\n    for (int8_t i = 0; i < num_registered_taps; ++i) {\n        if (KEYEQ(registered_taps[i], key)) {\n            return i;\n        }\n    }\n    return -1;\n}\n\nstatic void registered_taps_del_index(uint8_t i) {\n    if (i < num_registered_taps) {\n        --num_registered_taps;\n        if (i < num_registered_taps) {\n            registered_taps[i] = registered_taps[num_registered_taps];\n        }\n    }\n}\n\nstatic void debug_registered_taps(void) {\n    ac_dprintf(\"registered_taps = { \");\n    for (int8_t i = 0; i < num_registered_taps; ++i) {\n        ac_dprintf(\"%02X%02X \", registered_taps[i].row, registered_taps[i].col);\n    }\n    ac_dprintf(\"}\\n\");\n}\n\n#    endif // defined(CHORDAL_HOLD) || defined(FLOW_TAP_TERM)\n\n#    ifdef CHORDAL_HOLD\n__attribute__((weak)) bool get_chordal_hold(uint16_t tap_hold_keycode, keyrecord_t *tap_hold_record, uint16_t other_keycode, keyrecord_t *other_record) {\n    return get_chordal_hold_default(tap_hold_record, other_record);\n}\n\nbool get_chordal_hold_default(keyrecord_t *tap_hold_record, keyrecord_t *other_record) {\n    if (tap_hold_record->event.type != KEY_EVENT || other_record->event.type != KEY_EVENT) {\n        return true; // Return true on combos or other non-key events.\n    }\n\n    char tap_hold_hand = chordal_hold_handedness(tap_hold_record->event.key);\n    if (tap_hold_hand == '*') {\n        return true;\n    }\n    char other_hand = chordal_hold_handedness(other_record->event.key);\n    return other_hand == '*' || tap_hold_hand != other_hand;\n}\n\n__attribute__((weak)) char chordal_hold_handedness(keypos_t key) {\n    return (char)pgm_read_byte(&chordal_hold_layout[key.row][key.col]);\n}\n\nstatic uint8_t waiting_buffer_find_chordal_hold_tap(void) {\n    keyrecord_t *prev         = &tapping_key;\n    uint16_t     prev_keycode = get_record_keycode(&tapping_key, false);\n    uint8_t      first_tap    = WAITING_BUFFER_SIZE;\n    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {\n        keyrecord_t *  cur         = &waiting_buffer[i];\n        const uint16_t cur_keycode = get_record_keycode(cur, false);\n        if (!cur->event.pressed || !is_mt_or_lt(prev_keycode)) {\n            break;\n        } else if (get_chordal_hold(prev_keycode, prev, cur_keycode, cur)) {\n            first_tap = i; // Track one index past the latest hold.\n        }\n        prev         = cur;\n        prev_keycode = cur_keycode;\n    }\n    return first_tap;\n}\n\nstatic void waiting_buffer_chordal_hold_taps_until(keypos_t key) {\n    while (waiting_buffer_tail != waiting_buffer_head) {\n        keyrecord_t *record = &waiting_buffer[waiting_buffer_tail];\n        ac_dprintf(\"waiting_buffer_chordal_hold_taps_until: processing [%u]\\n\", waiting_buffer_tail);\n        if (record->event.pressed && is_tap_record(record)) {\n            record->tap.count = 1;\n            registered_taps_add(record->event.key);\n        }\n        process_record(record);\n        waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE;\n\n        if (KEYEQ(key, record->event.key) && record->event.pressed) {\n            break;\n        }\n    }\n}\n\nstatic void waiting_buffer_process_regular(void) {\n    for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) {\n        if (is_tap_record(&waiting_buffer[waiting_buffer_tail])) {\n            break; // Stop once a tap-hold key event is reached.\n        }\n        ac_dprintf(\"waiting_buffer_process_regular: processing [%u]\\n\", waiting_buffer_tail);\n        process_record(&waiting_buffer[waiting_buffer_tail]);\n    }\n    debug_waiting_buffer();\n}\n#    endif // CHORDAL_HOLD\n\n#    ifdef FLOW_TAP_TERM\nvoid flow_tap_update_last_event(keyrecord_t *record) {\n    const uint16_t keycode = get_record_keycode(record, false);\n    // Don't update while a tap-hold key is unsettled.\n    if (record->tap.count == 0 && (waiting_buffer_tail != waiting_buffer_head || (tapping_key.event.pressed && tapping_key.tap.count == 0))) {\n        return;\n    }\n    // Ignore releases of modifiers and held layer switches.\n    if (!record->event.pressed) {\n        switch (keycode) {\n            case MODIFIER_KEYCODE_RANGE:\n            case QK_MOMENTARY ... QK_MOMENTARY_MAX:\n            case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:\n#        ifndef NO_ACTION_ONESHOT // Ignore one-shot keys.\n            case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:\n            case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:\n#        endif                  // NO_ACTION_ONESHOT\n#        ifdef TRI_LAYER_ENABLE // Ignore Tri Layer keys.\n            case QK_TRI_LAYER_LOWER:\n            case QK_TRI_LAYER_UPPER:\n#        endif // TRI_LAYER_ENABLE\n                return;\n            case QK_MODS ... QK_MODS_MAX:\n                if (QK_MODS_GET_BASIC_KEYCODE(keycode) == KC_NO) {\n                    return;\n                }\n                break;\n            case QK_MOD_TAP ... QK_MOD_TAP_MAX:\n            case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:\n                if (record->tap.count == 0) {\n                    return;\n                }\n                break;\n        }\n    }\n\n    flow_tap_prev_keycode = keycode;\n    flow_tap_prev_time    = record->event.time;\n    flow_tap_expired      = false;\n}\n\nstatic bool flow_tap_key_if_within_term(keyrecord_t *record, uint16_t prev_time) {\n    const uint16_t idle_time = TIMER_DIFF_16(record->event.time, prev_time);\n    if (flow_tap_expired || idle_time >= 500) {\n        return false;\n    }\n\n    const uint16_t keycode = get_record_keycode(record, false);\n    if (is_mt_or_lt(keycode)) {\n        uint16_t term = get_flow_tap_term(keycode, record, flow_tap_prev_keycode);\n        if (term > 500) {\n            term = 500;\n        }\n        if (idle_time < term) {\n            debug_event(record->event);\n            ac_dprintf(\" within flow tap term (%u < %u) considered a tap\\n\", idle_time, term);\n            record->tap.count = 1;\n            registered_taps_add(record->event.key);\n            debug_registered_taps();\n            process_record(record);\n            return true;\n        }\n    }\n    return false;\n}\n\n// By default, enable Flow Tap for the keys in the main alphas area and Space.\n// This should work reasonably even if the layout is remapped on the host to an\n// alt layout or international layout (e.g. Dvorak or AZERTY), where these same\n// key positions are mostly used for typing letters.\n__attribute__((weak)) bool is_flow_tap_key(uint16_t keycode) {\n    if ((get_mods() & (MOD_MASK_CG | MOD_BIT_LALT)) != 0) {\n        return false; // Disable Flow Tap on hotkeys.\n    }\n    switch (get_tap_keycode(keycode)) {\n        case KC_SPC:\n        case KC_A ... KC_Z:\n        case KC_DOT:\n        case KC_COMM:\n        case KC_SCLN:\n        case KC_SLSH:\n            return true;\n    }\n    return false;\n}\n\n__attribute__((weak)) uint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t *record, uint16_t prev_keycode) {\n    if (is_flow_tap_key(keycode) && is_flow_tap_key(prev_keycode)) {\n        return FLOW_TAP_TERM;\n    }\n    return 0;\n}\n#    endif // FLOW_TAP_TERM\n\n/** \\brief Logs tapping key if ACTION_DEBUG is enabled. */\nstatic void debug_tapping_key(void) {\n    ac_dprintf(\"TAPPING_KEY=\");\n    debug_record(tapping_key);\n    ac_dprintf(\"\\n\");\n}\n\n/** \\brief Logs waiting buffer if ACTION_DEBUG is enabled. */\nstatic void debug_waiting_buffer(void) {\n    ac_dprintf(\"{\");\n    for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) {\n        ac_dprintf(\" [%u]=\", i);\n        debug_record(waiting_buffer[i]);\n    }\n    ac_dprintf(\"}\\n\");\n}\n\n#endif\n"
  },
  {
    "path": "quantum/action_tapping.h",
    "content": "/*\nCopyright 2013 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n/* period of tapping(ms) */\n#ifndef TAPPING_TERM\n#    define TAPPING_TERM 200\n#endif\n\n/* period of quick tap(ms) */\n#if !defined(QUICK_TAP_TERM) || QUICK_TAP_TERM > TAPPING_TERM\n#    define QUICK_TAP_TERM TAPPING_TERM\n#endif\n\n/* tap count needed for toggling a feature */\n#ifndef TAPPING_TOGGLE\n#    define TAPPING_TOGGLE 5\n#endif\n\n#define WAITING_BUFFER_SIZE 8\n\n#ifndef NO_ACTION_TAPPING\nuint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache);\nuint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);\nvoid     action_tapping_process(keyrecord_t record);\n#endif\n\nuint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record);\nuint16_t get_quick_tap_term(uint16_t keycode, keyrecord_t *record);\nbool     get_permissive_hold(uint16_t keycode, keyrecord_t *record);\nbool     get_retro_tapping(uint16_t keycode, keyrecord_t *record);\nbool     get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record);\n\n#ifdef CHORDAL_HOLD\n/**\n * Callback to say when a key chord before the tapping term may be held.\n *\n * In keymap.c, define the callback\n *\n *     bool get_chordal_hold(uint16_t tap_hold_keycode,\n *                           keyrecord_t* tap_hold_record,\n *                           uint16_t other_keycode,\n *                           keyrecord_t* other_record) {\n *        // Conditions...\n *     }\n *\n * This callback is called when:\n *\n * 1. `tap_hold_keycode` is pressed.\n * 2. `other_keycode` is pressed while `tap_hold_keycode` is still held,\n *     provided `other_keycode` is *not* also a tap-hold key and it is pressed\n *     before the tapping term.\n *\n * If false is returned, this has the effect of immediately settling the\n * tap-hold key as tapped. If true is returned, the tap-hold key is still\n * unsettled, and may be settled as held depending on configuration and\n * subsequent events.\n *\n * @param tap_hold_keycode   Keycode of the tap-hold key.\n * @param tap_hold_record    Record from the tap-hold press event.\n * @param other_keycode      Keycode of the other key.\n * @param other_record       Record from the other key's press event.\n * @return True if the tap-hold key may be considered held; false if tapped.\n */\nbool get_chordal_hold(uint16_t tap_hold_keycode, keyrecord_t *tap_hold_record, uint16_t other_keycode, keyrecord_t *other_record);\n\n/**\n * Default \"opposite hands rule\" for whether a key chord may settle as held.\n *\n * This function returns true when the tap-hold key and other key are on\n * \"opposite hands.\" In detail, handedness of the two keys are compared. If\n * handedness values differ, or if either handedness is '*', the function\n * returns true, indicating that it may be held. Otherwise, it returns false,\n * in which case the tap-hold key is immediately settled at tapped.\n *\n * @param tap_hold_record  Record of the active tap-hold key press.\n * @param other_record     Record of the other, interrupting key press.\n * @return True if the tap-hold key may be considered held; false if tapped.\n */\nbool get_chordal_hold_default(keyrecord_t *tap_hold_record, keyrecord_t *other_record);\n\n/**\n * Gets the handedness of a key.\n *\n * This function returns:\n *   'L' for keys pressed by the left hand,\n *   'R' for keys on the right hand,\n *   '*' for keys exempt from the \"opposite hands rule.\" This could be used\n *       perhaps on thumb keys or keys that might be pressed by either hand.\n *\n * @param key   A key matrix position.\n * @return Handedness value.\n */\nchar chordal_hold_handedness(keypos_t key);\n\nextern const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM;\n#endif\n\n#ifdef FLOW_TAP_TERM\n/**\n * Callback to specify the keys where Flow Tap is enabled.\n *\n * Flow Tap is constrained to certain keys by the following rule: this callback\n * is called for both the tap-hold key *and* the key press immediately preceding\n * it. If the callback returns true for both keycodes, Flow Tap is enabled.\n *\n * The default implementation of this callback corresponds to\n *\n *     bool is_flow_tap_key(uint16_t keycode) {\n *       switch (get_tap_keycode(keycode)) {\n *         case KC_SPC:\n *         case KC_A ... KC_Z:\n *         case KC_DOT:\n *         case KC_COMM:\n *         case KC_SCLN:\n *         case KC_SLSH:\n *           return true;\n *       }\n *       return false;\n *     }\n *\n * @param keycode Keycode of the key.\n * @return Whether to enable Flow Tap for this key.\n */\nbool is_flow_tap_key(uint16_t keycode);\n\n/**\n * Callback to customize Flow Tap filtering.\n *\n * Flow Tap acts only when key events are closer together than this time.\n *\n * Return a time of 0 to disable filtering. In this way, Flow Tap may be\n * disabled for certain tap-hold keys, or when following certain previous keys.\n *\n * The default implementation of this callback is\n *\n *     uint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t* record,\n *                                uint16_t prev_keycode) {\n *       if (is_flow_tap_key(keycode) && is_flow_tap_key(prev_keycode)) {\n *         return g_flow_tap_term;\n *       }\n *       return 0;\n *     }\n *\n * NOTE: If both `is_flow_tap_key()` and `get_flow_tap_term()` are defined, then\n * `get_flow_tap_term()` takes precedence.\n *\n * @param keycode Keycode of the tap-hold key.\n * @param record keyrecord_t of the tap-hold event.\n * @param prev_keycode Keycode of the previously pressed key.\n * @return Time in milliseconds.\n */\nuint16_t get_flow_tap_term(uint16_t keycode, keyrecord_t *record, uint16_t prev_keycode);\n\n/** Updates the Flow Tap last key and timer. */\nvoid flow_tap_update_last_event(keyrecord_t *record);\n#endif // FLOW_TAP_TERM\n\n#ifdef DYNAMIC_TAPPING_TERM_ENABLE\nextern uint16_t g_tapping_term;\n#endif\n\n#if defined(TAPPING_TERM_PER_KEY) && !defined(NO_ACTION_TAPPING)\n#    define GET_TAPPING_TERM(keycode, record) get_tapping_term(keycode, record)\n#elif defined(DYNAMIC_TAPPING_TERM_ENABLE) && !defined(NO_ACTION_TAPPING)\n#    define GET_TAPPING_TERM(keycode, record) g_tapping_term\n#else\n#    define GET_TAPPING_TERM(keycode, record) (TAPPING_TERM)\n#endif\n\n#ifdef QUICK_TAP_TERM_PER_KEY\n#    define GET_QUICK_TAP_TERM(keycode, record) get_quick_tap_term(keycode, record)\n#else\n#    define GET_QUICK_TAP_TERM(keycode, record) (QUICK_TAP_TERM)\n#endif\n"
  },
  {
    "path": "quantum/action_util.c",
    "content": "/*\nCopyright 2013 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n#include \"host.h\"\n#include \"report.h\"\n#include \"debug.h\"\n#include \"action_util.h\"\n#include \"action_layer.h\"\n#include \"timer.h\"\n#include \"keycode_config.h\"\n#include <string.h>\n\nextern keymap_config_t keymap_config;\n\nstatic uint8_t real_mods = 0;\nstatic uint8_t weak_mods = 0;\n#ifdef KEY_OVERRIDE_ENABLE\nstatic uint8_t weak_override_mods = 0;\nstatic uint8_t suppressed_mods    = 0;\n#endif\n\n// TODO: pointer variable is not needed\n// report_keyboard_t keyboard_report = {};\nreport_keyboard_t *keyboard_report = &(report_keyboard_t){};\n#ifdef NKRO_ENABLE\nreport_nkro_t *nkro_report = &(report_nkro_t){};\n#endif\n\nextern inline void add_key(uint8_t key);\nextern inline void del_key(uint8_t key);\nextern inline void clear_keys(void);\n\n#ifndef NO_ACTION_ONESHOT\nstatic uint8_t oneshot_mods        = 0;\nstatic uint8_t oneshot_locked_mods = 0;\nuint8_t        get_oneshot_locked_mods(void) {\n    return oneshot_locked_mods;\n}\nvoid add_oneshot_locked_mods(uint8_t mods) {\n    if ((oneshot_locked_mods & mods) != mods) {\n        oneshot_locked_mods |= mods;\n        oneshot_locked_mods_changed_kb(oneshot_locked_mods);\n    }\n}\nvoid set_oneshot_locked_mods(uint8_t mods) {\n    if (mods != oneshot_locked_mods) {\n        oneshot_locked_mods = mods;\n        oneshot_locked_mods_changed_kb(oneshot_locked_mods);\n    }\n}\nvoid clear_oneshot_locked_mods(void) {\n    if (oneshot_locked_mods) {\n        oneshot_locked_mods = 0;\n        oneshot_locked_mods_changed_kb(oneshot_locked_mods);\n    }\n}\nvoid del_oneshot_locked_mods(uint8_t mods) {\n    if (oneshot_locked_mods & mods) {\n        oneshot_locked_mods &= ~mods;\n        oneshot_locked_mods_changed_kb(oneshot_locked_mods);\n    }\n}\n#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\nstatic uint16_t oneshot_time = 0;\nbool            has_oneshot_mods_timed_out(void) {\n    return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT;\n}\n#    else\nbool has_oneshot_mods_timed_out(void) {\n    return false;\n}\n#    endif\n#endif\n\n/* oneshot layer */\n#ifndef NO_ACTION_ONESHOT\n/** \\brief oneshot_layer_data bits\n * LLLL LSSS\n * where:\n *   L => are layer bits\n *   S => oneshot state bits\n */\nstatic uint8_t oneshot_layer_data = 0;\n\ninline uint8_t get_oneshot_layer(void) {\n    return oneshot_layer_data >> 3;\n}\ninline uint8_t get_oneshot_layer_state(void) {\n    return oneshot_layer_data & 0b111;\n}\n\n#    ifdef SWAP_HANDS_ENABLE\nenum {\n    SHO_OFF,\n    SHO_ACTIVE,  // Swap hands button was pressed, and we didn't send any swapped keys yet\n    SHO_PRESSED, // Swap hands button is currently pressed\n    SHO_USED,    // Swap hands button is still pressed, and we already sent swapped keys\n} swap_hands_oneshot = SHO_OFF;\n#    endif\n\n#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\nstatic uint16_t oneshot_layer_time = 0;\ninline bool     has_oneshot_layer_timed_out(void) {\n    return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT && !(get_oneshot_layer_state() & ONESHOT_TOGGLED);\n}\n#        ifdef SWAP_HANDS_ENABLE\nstatic uint16_t oneshot_swaphands_time = 0;\ninline bool     has_oneshot_swaphands_timed_out(void) {\n    return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= ONESHOT_TIMEOUT && (swap_hands_oneshot == SHO_ACTIVE);\n}\n#        endif\n#    endif\n\n#    ifdef SWAP_HANDS_ENABLE\n\nvoid set_oneshot_swaphands(void) {\n    swap_hands_oneshot = SHO_PRESSED;\n    swap_hands         = true;\n#        if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\n    oneshot_swaphands_time = timer_read();\n    if (oneshot_layer_time != 0) {\n        oneshot_layer_time = oneshot_swaphands_time;\n    }\n#        endif\n}\n\nvoid release_oneshot_swaphands(void) {\n    if (swap_hands_oneshot == SHO_PRESSED) {\n        swap_hands_oneshot = SHO_ACTIVE;\n    }\n    if (swap_hands_oneshot == SHO_USED) {\n        clear_oneshot_swaphands();\n    }\n}\n\nvoid use_oneshot_swaphands(void) {\n    if (swap_hands_oneshot == SHO_PRESSED) {\n        swap_hands_oneshot = SHO_USED;\n    }\n    if (swap_hands_oneshot == SHO_ACTIVE) {\n        clear_oneshot_swaphands();\n    }\n}\n\nvoid clear_oneshot_swaphands(void) {\n    swap_hands_oneshot = SHO_OFF;\n    swap_hands         = false;\n#        if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\n    oneshot_swaphands_time = 0;\n#        endif\n}\n\n#    endif\n\n/** \\brief Set oneshot layer\n *\n * FIXME: needs doc\n */\nvoid set_oneshot_layer(uint8_t layer, uint8_t state) {\n    if (keymap_config.oneshot_enable) {\n        oneshot_layer_data = layer << 3 | state;\n        layer_on(layer);\n#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\n        oneshot_layer_time = timer_read();\n#    endif\n        oneshot_layer_changed_kb(get_oneshot_layer());\n    } else {\n        layer_on(layer);\n    }\n}\n/** \\brief Reset oneshot layer\n *\n * FIXME: needs doc\n */\nvoid reset_oneshot_layer(void) {\n    oneshot_layer_data = 0;\n#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\n    oneshot_layer_time = 0;\n#    endif\n    oneshot_layer_changed_kb(get_oneshot_layer());\n}\n/** \\brief Clear oneshot layer\n *\n * FIXME: needs doc\n */\nvoid clear_oneshot_layer_state(oneshot_fullfillment_t state) {\n    uint8_t start_state = oneshot_layer_data;\n    oneshot_layer_data &= ~state;\n    if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) && keymap_config.oneshot_enable) {\n        layer_off(get_oneshot_layer());\n        reset_oneshot_layer();\n    }\n}\n/** \\brief Is oneshot layer active\n *\n * FIXME: needs doc\n */\nbool is_oneshot_layer_active(void) {\n    return get_oneshot_layer_state();\n}\n\n/** \\brief set oneshot\n *\n * FIXME: needs doc\n */\nvoid oneshot_set(bool active) {\n    if (keymap_config.oneshot_enable != active) {\n        keymap_config.oneshot_enable = active;\n        eeconfig_update_keymap(&keymap_config);\n        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);\n        dprintf(\"Oneshot: active: %d\\n\", active);\n    }\n}\n\n/** \\brief toggle oneshot\n *\n * FIXME: needs doc\n */\nvoid oneshot_toggle(void) {\n    oneshot_set(!keymap_config.oneshot_enable);\n}\n\n/** \\brief enable oneshot\n *\n * FIXME: needs doc\n */\nvoid oneshot_enable(void) {\n    oneshot_set(true);\n}\n\n/** \\brief disable oneshot\n *\n * FIXME: needs doc\n */\nvoid oneshot_disable(void) {\n    oneshot_set(false);\n}\n\nbool is_oneshot_enabled(void) {\n    return keymap_config.oneshot_enable;\n}\n\n#endif\n\nstatic uint8_t get_mods_for_report(void) {\n    uint8_t mods = real_mods | weak_mods;\n\n#ifndef NO_ACTION_ONESHOT\n    if (oneshot_mods) {\n#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\n        if (has_oneshot_mods_timed_out()) {\n            dprintf(\"Oneshot: timeout\\n\");\n            clear_oneshot_mods();\n        }\n#    endif\n        mods |= oneshot_mods;\n        if (has_anykey()) {\n            clear_oneshot_mods();\n        }\n    }\n#endif\n\n#ifdef KEY_OVERRIDE_ENABLE\n    // These need to be last to be able to properly control key overrides\n    mods &= ~suppressed_mods;\n    mods |= weak_override_mods;\n#endif\n\n    return mods;\n}\n\nvoid send_6kro_report(void) {\n    keyboard_report->mods = get_mods_for_report();\n\n#ifdef PROTOCOL_VUSB\n    host_keyboard_send(keyboard_report);\n#else\n    static report_keyboard_t last_report;\n\n    /* Only send the report if there are changes to propagate to the host. */\n    if (memcmp(keyboard_report, &last_report, sizeof(report_keyboard_t)) != 0) {\n        memcpy(&last_report, keyboard_report, sizeof(report_keyboard_t));\n        host_keyboard_send(keyboard_report);\n    }\n#endif\n}\n\n#ifdef NKRO_ENABLE\nvoid send_nkro_report(void) {\n    nkro_report->mods = get_mods_for_report();\n\n    static report_nkro_t last_report;\n\n    /* Only send the report if there are changes to propagate to the host. */\n    if (memcmp(nkro_report, &last_report, sizeof(report_nkro_t)) != 0) {\n        memcpy(&last_report, nkro_report, sizeof(report_nkro_t));\n        host_nkro_send(nkro_report);\n    }\n}\n#endif\n\n/** \\brief Send keyboard report\n *\n * FIXME: needs doc\n */\nvoid send_keyboard_report(void) {\n#ifdef NKRO_ENABLE\n    if (host_can_send_nkro() && keymap_config.nkro) {\n        send_nkro_report();\n        return;\n    }\n#endif\n    send_6kro_report();\n}\n\n/** \\brief Get mods\n *\n * FIXME: needs doc\n */\nuint8_t get_mods(void) {\n    return real_mods;\n}\n/** \\brief add mods\n *\n * FIXME: needs doc\n */\nvoid add_mods(uint8_t mods) {\n    real_mods |= mods;\n}\n/** \\brief del mods\n *\n * FIXME: needs doc\n */\nvoid del_mods(uint8_t mods) {\n    real_mods &= ~mods;\n}\n/** \\brief set mods\n *\n * FIXME: needs doc\n */\nvoid set_mods(uint8_t mods) {\n    real_mods = mods;\n}\n/** \\brief clear mods\n *\n * FIXME: needs doc\n */\nvoid clear_mods(void) {\n    real_mods = 0;\n}\n\n/** \\brief get weak mods\n *\n * FIXME: needs doc\n */\nuint8_t get_weak_mods(void) {\n    return weak_mods;\n}\n/** \\brief add weak mods\n *\n * FIXME: needs doc\n */\nvoid add_weak_mods(uint8_t mods) {\n    weak_mods |= mods;\n}\n/** \\brief del weak mods\n *\n * FIXME: needs doc\n */\nvoid del_weak_mods(uint8_t mods) {\n    weak_mods &= ~mods;\n}\n/** \\brief set weak mods\n *\n * FIXME: needs doc\n */\nvoid set_weak_mods(uint8_t mods) {\n    weak_mods = mods;\n}\n/** \\brief clear weak mods\n *\n * FIXME: needs doc\n */\nvoid clear_weak_mods(void) {\n    weak_mods = 0;\n}\n\n#ifdef KEY_OVERRIDE_ENABLE\n/** \\brief set weak mods used by key overrides. DO not call this manually\n */\nvoid set_weak_override_mods(uint8_t mods) {\n    weak_override_mods = mods;\n}\n/** \\brief clear weak mods used by key overrides. DO not call this manually\n */\nvoid clear_weak_override_mods(void) {\n    weak_override_mods = 0;\n}\n\n/** \\brief set suppressed mods used by key overrides. DO not call this manually\n */\nvoid set_suppressed_override_mods(uint8_t mods) {\n    suppressed_mods = mods;\n}\n/** \\brief clear suppressed mods used by key overrides. DO not call this manually\n */\nvoid clear_suppressed_override_mods(void) {\n    suppressed_mods = 0;\n}\n#endif\n\n#ifndef NO_ACTION_ONESHOT\n/** \\brief get oneshot mods\n *\n * FIXME: needs doc\n */\nuint8_t get_oneshot_mods(void) {\n    return oneshot_mods;\n}\n\nvoid add_oneshot_mods(uint8_t mods) {\n    if ((oneshot_mods & mods) != mods) {\n#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\n        oneshot_time = timer_read();\n#    endif\n        oneshot_mods |= mods;\n        oneshot_mods_changed_kb(mods);\n    }\n}\n\nvoid del_oneshot_mods(uint8_t mods) {\n    if (oneshot_mods & mods) {\n        oneshot_mods &= ~mods;\n#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\n        oneshot_time = oneshot_mods ? timer_read() : 0;\n#    endif\n        oneshot_mods_changed_kb(oneshot_mods);\n    }\n}\n\n/** \\brief set oneshot mods\n *\n * FIXME: needs doc\n */\nvoid set_oneshot_mods(uint8_t mods) {\n    if (keymap_config.oneshot_enable) {\n        if (oneshot_mods != mods) {\n#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\n            oneshot_time = timer_read();\n#    endif\n            oneshot_mods = mods;\n            oneshot_mods_changed_kb(mods);\n        }\n    }\n}\n\n/** \\brief clear oneshot mods\n *\n * FIXME: needs doc\n */\nvoid clear_oneshot_mods(void) {\n    if (oneshot_mods) {\n        oneshot_mods = 0;\n#    if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0))\n        oneshot_time = 0;\n#    endif\n        oneshot_mods_changed_kb(oneshot_mods);\n    }\n}\n#endif\n\n/** \\brief Called when the one shot modifiers have been changed.\n *\n * \\param mods Contains the active modifiers active after the change.\n */\n__attribute__((weak)) void oneshot_locked_mods_changed_user(uint8_t mods) {}\n\n/** \\brief Called when the locked one shot modifiers have been changed.\n *\n * \\param mods Contains the active modifiers active after the change.\n */\n__attribute__((weak)) void oneshot_locked_mods_changed_kb(uint8_t mods) {\n    oneshot_locked_mods_changed_user(mods);\n}\n\n/** \\brief Called when the one shot modifiers have been changed.\n *\n * \\param mods Contains the active modifiers active after the change.\n */\n__attribute__((weak)) void oneshot_mods_changed_user(uint8_t mods) {}\n\n/** \\brief Called when the one shot modifiers have been changed.\n *\n * \\param mods Contains the active modifiers active after the change.\n */\n__attribute__((weak)) void oneshot_mods_changed_kb(uint8_t mods) {\n    oneshot_mods_changed_user(mods);\n}\n\n/** \\brief Called when the one shot layers have been changed.\n *\n * \\param layer Contains the layer that is toggled on, or zero when toggled off.\n */\n__attribute__((weak)) void oneshot_layer_changed_user(uint8_t layer) {}\n\n/** \\brief Called when the one shot layers have been changed.\n *\n * \\param layer Contains the layer that is toggled on, or zero when toggled off.\n */\n__attribute__((weak)) void oneshot_layer_changed_kb(uint8_t layer) {\n    oneshot_layer_changed_user(layer);\n}\n\n/** \\brief inspect keyboard state\n *\n * FIXME: needs doc\n */\nuint8_t has_anymod(void) {\n    return bitpop(real_mods);\n}\n\n#ifdef DUMMY_MOD_NEUTRALIZER_KEYCODE\n/** \\brief Send a dummy keycode in between the register and unregister event of a modifier key, to neutralize the \"flashing modifiers\" phenomenon.\n *\n * \\param active_mods 8-bit packed bit-array describing the currently active modifiers (in the format GASCGASC).\n *\n * Certain QMK features like  key overrides or retro tap must unregister a previously\n * registered modifier before sending another keycode but this can trigger undesired\n * keyboard shortcuts if the clean tap of a single modifier key is bound to an action\n * on the host OS, as is for example the case for the left GUI key on Windows, which\n * opens the Start Menu when tapped.\n */\nvoid neutralize_flashing_modifiers(uint8_t active_mods) {\n    // In most scenarios, the flashing modifiers phenomenon is a problem\n    // only for a subset of modifier masks.\n    const static uint8_t mods_to_neutralize[] = MODS_TO_NEUTRALIZE;\n    const static uint8_t n_mods               = ARRAY_SIZE(mods_to_neutralize);\n    for (uint8_t i = 0; i < n_mods; ++i) {\n        if (active_mods == mods_to_neutralize[i]) {\n            tap_code(DUMMY_MOD_NEUTRALIZER_KEYCODE);\n            break;\n        }\n    }\n}\n#endif\n"
  },
  {
    "path": "quantum/action_util.h",
    "content": "/*\nCopyright 2013 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include \"report.h\"\n#include \"modifiers.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern report_keyboard_t *keyboard_report;\n#ifdef NKRO_ENABLE\nextern report_nkro_t *nkro_report;\n#endif\n\nvoid send_keyboard_report(void);\n\n/* key */\ninline void add_key(uint8_t key) {\n    add_key_to_report(key);\n}\n\ninline void del_key(uint8_t key) {\n    del_key_from_report(key);\n}\n\ninline void clear_keys(void) {\n    clear_keys_from_report();\n}\n\n/* modifier */\nuint8_t get_mods(void);\nvoid    add_mods(uint8_t mods);\nvoid    del_mods(uint8_t mods);\nvoid    set_mods(uint8_t mods);\nvoid    clear_mods(void);\n\n/* weak modifier */\nuint8_t get_weak_mods(void);\nvoid    add_weak_mods(uint8_t mods);\nvoid    del_weak_mods(uint8_t mods);\nvoid    set_weak_mods(uint8_t mods);\nvoid    clear_weak_mods(void);\n\n/* oneshot modifier */\nuint8_t get_oneshot_mods(void);\nvoid    add_oneshot_mods(uint8_t mods);\nvoid    del_oneshot_mods(uint8_t mods);\nvoid    set_oneshot_mods(uint8_t mods);\nvoid    clear_oneshot_mods(void);\nbool    has_oneshot_mods_timed_out(void);\n\nuint8_t get_oneshot_locked_mods(void);\nvoid    add_oneshot_locked_mods(uint8_t mods);\nvoid    set_oneshot_locked_mods(uint8_t mods);\nvoid    clear_oneshot_locked_mods(void);\nvoid    del_oneshot_locked_mods(uint8_t mods);\n\ntypedef enum { ONESHOT_PRESSED = 0b01, ONESHOT_OTHER_KEY_PRESSED = 0b10, ONESHOT_START = 0b11, ONESHOT_TOGGLED = 0b100 } oneshot_fullfillment_t;\nvoid    set_oneshot_layer(uint8_t layer, uint8_t state);\nuint8_t get_oneshot_layer(void);\nvoid    clear_oneshot_layer_state(oneshot_fullfillment_t state);\nvoid    reset_oneshot_layer(void);\nbool    is_oneshot_layer_active(void);\nuint8_t get_oneshot_layer_state(void);\nbool    has_oneshot_layer_timed_out(void);\nbool    has_oneshot_swaphands_timed_out(void);\n\nvoid oneshot_locked_mods_changed_user(uint8_t mods);\nvoid oneshot_locked_mods_changed_kb(uint8_t mods);\nvoid oneshot_mods_changed_user(uint8_t mods);\nvoid oneshot_mods_changed_kb(uint8_t mods);\nvoid oneshot_layer_changed_user(uint8_t layer);\nvoid oneshot_layer_changed_kb(uint8_t layer);\n\nvoid oneshot_toggle(void);\nvoid oneshot_enable(void);\nvoid oneshot_disable(void);\nbool is_oneshot_enabled(void);\n\n/* inspect */\nuint8_t has_anymod(void);\n\n#ifdef SWAP_HANDS_ENABLE\nvoid set_oneshot_swaphands(void);\nvoid release_oneshot_swaphands(void);\nvoid use_oneshot_swaphands(void);\nvoid clear_oneshot_swaphands(void);\n#endif\n\n#ifdef DUMMY_MOD_NEUTRALIZER_KEYCODE\n// KC_A is used as the lowerbound instead of QK_BASIC because the range QK_BASIC...KC_A includes\n// internal keycodes like KC_NO and KC_TRANSPARENT which are unsuitable for use with `tap_code(kc)`.\n#    if !(KC_A <= DUMMY_MOD_NEUTRALIZER_KEYCODE && DUMMY_MOD_NEUTRALIZER_KEYCODE <= QK_BASIC_MAX)\n#        error \"DUMMY_MOD_NEUTRALIZER_KEYCODE must be a basic, unmodified, HID keycode!\"\n#    endif\nvoid neutralize_flashing_modifiers(uint8_t active_mods);\n#endif\n#ifndef MODS_TO_NEUTRALIZE\n#    define MODS_TO_NEUTRALIZE \\\n        { MOD_BIT(KC_LEFT_ALT), MOD_BIT(KC_LEFT_GUI) }\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/audio/audio.c",
    "content": "/* Copyright 2016-2020 Jack Humbert\n * Copyright 2020 JohSchneider\n\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"audio.h\"\n#include \"eeconfig.h\"\n#include \"timer.h\"\n#include \"debug.h\"\n#include \"wait.h\"\n#include \"util.h\"\n#include \"gpio.h\"\n\n/* audio system:\n *\n * audio.[ch] takes care of all overall state, tracking the actively playing\n *            notes/tones; the notes a SONG consists of;\n *            ...\n *            = everything audio-related that is platform agnostic\n *\n * driver_[avr|chibios]_[dac|pwm] take care of the lower hardware dependent parts,\n *            specific to each platform and the used subsystem/driver to drive\n *            the output pins/channels with the calculated frequencies for each\n *            active tone\n *            as part of this, the driver has to trigger regular state updates by\n *            calling 'audio_update_state' through some sort of timer - be it a\n *            dedicated one or piggybacking on for example the timer used to\n *            generate a pwm signal/clock.\n *\n *\n * A Note on terminology:\n * tone, pitch and frequency are used somewhat interchangeably, in a strict Wikipedia-sense:\n *    \"(Musical) tone, a sound characterized by its duration, pitch (=frequency),\n *    intensity (=volume), and timbre\"\n * - intensity/volume is currently not handled at all, although the 'dac_additive' driver could do so\n * - timbre is handled globally (TODO: only used with the pwm drivers at the moment)\n *\n * in musical_note.h a 'note' is the combination of a pitch and a duration\n * these are used to create SONG arrays; during playback their frequencies\n * are handled as single successive tones, while the durations are\n * kept track of in 'audio_update_state'\n *\n * 'voice' as it is used here, equates to a sort of instrument with its own\n * characteristics sound and effects\n * the audio system as-is deals only with (possibly multiple) tones of one\n * instrument/voice at a time (think: chords). since the number of tones that\n * can be reproduced depends on the hardware/driver in use: pwm can only\n * reproduce one tone per output/speaker; DACs can reproduce/mix multiple\n * when doing additive synthesis.\n *\n * 'duration' can either be in the beats-per-minute related unit found in\n * musical_notes.h, OR in ms; keyboards create SONGs with the former, while\n * the internal state of the audio system does its calculations with the later - ms\n */\n\n#ifndef AUDIO_DEFAULT_ON\n#    define AUDIO_DEFAULT_ON true\n#endif\n#ifndef AUDIO_DEFAULT_CLICKY_ON\n#    define AUDIO_DEFAULT_CLICKY_ON true\n#endif\n\n#ifndef AUDIO_TONE_STACKSIZE\n#    define AUDIO_TONE_STACKSIZE 8\n#endif\nuint8_t        active_tones = 0;            // number of tones pushed onto the stack by audio_play_tone - might be more than the hardware is able to reproduce at any single time\nmusical_tone_t tones[AUDIO_TONE_STACKSIZE]; // stack of currently active tones\n\nbool playing_melody = false; // playing a SONG?\nbool playing_note   = false; // or (possibly multiple simultaneous) tones\nbool state_changed  = false; // global flag, which is set if anything changes with the active_tones\n\n// melody/SONG related state variables\nfloat (*notes_pointer)[][2];                           // SONG, an array of MUSICAL_NOTEs\nuint16_t notes_count;                                  // length of the notes_pointer array\nbool     notes_repeat;                                 // PLAY_SONG or PLAY_LOOP?\nuint16_t melody_current_note_duration = 0;             // duration of the currently playing note from the active melody, in ms\nuint8_t  note_tempo                   = TEMPO_DEFAULT; // beats-per-minute\nuint16_t current_note                 = 0;             // index into the array at notes_pointer\nbool     note_resting                 = false;         // if a short pause was introduced between two notes with the same frequency while playing a melody\nuint16_t last_timestamp               = 0;\n\n#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING\n#    ifndef AUDIO_MAX_SIMULTANEOUS_TONES\n#        define AUDIO_MAX_SIMULTANEOUS_TONES 3\n#    endif\nuint16_t tone_multiplexing_rate        = AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT;\nuint8_t  tone_multiplexing_index_shift = 0; // offset used on active-tone array access\n#endif\n\n// provided and used by voices.c\nextern uint8_t  note_timbre;\nextern bool     glissando;\nextern bool     vibrato;\nextern uint16_t voices_timer;\n\n#ifndef STARTUP_SONG\n#    define STARTUP_SONG SONG(STARTUP_SOUND)\n#endif\n#ifndef AUDIO_ON_SONG\n#    define AUDIO_ON_SONG SONG(AUDIO_ON_SOUND)\n#endif\n#ifndef AUDIO_OFF_SONG\n#    define AUDIO_OFF_SONG SONG(AUDIO_OFF_SOUND)\n#endif\nfloat startup_song[][2]   = STARTUP_SONG;\nfloat audio_on_song[][2]  = AUDIO_ON_SONG;\nfloat audio_off_song[][2] = AUDIO_OFF_SONG;\n\nstatic bool    audio_initialized    = false;\nstatic bool    audio_driver_stopped = true;\naudio_config_t audio_config;\n\n#ifndef AUDIO_POWER_CONTROL_PIN_ON_STATE\n#    define AUDIO_POWER_CONTROL_PIN_ON_STATE 1\n#endif\n\nvoid audio_driver_initialize(void) {\n#ifdef AUDIO_POWER_CONTROL_PIN\n    gpio_set_pin_output_push_pull(AUDIO_POWER_CONTROL_PIN);\n    gpio_write_pin(AUDIO_POWER_CONTROL_PIN, !AUDIO_POWER_CONTROL_PIN_ON_STATE);\n#endif\n    audio_driver_initialize_impl();\n}\n\nvoid audio_driver_stop(void) {\n    audio_driver_stop_impl();\n#ifdef AUDIO_POWER_CONTROL_PIN\n    gpio_write_pin(AUDIO_POWER_CONTROL_PIN, !AUDIO_POWER_CONTROL_PIN_ON_STATE);\n#endif\n}\n\nvoid audio_driver_start(void) {\n#ifdef AUDIO_POWER_CONTROL_PIN\n    gpio_write_pin(AUDIO_POWER_CONTROL_PIN, AUDIO_POWER_CONTROL_PIN_ON_STATE);\n#endif\n    audio_driver_start_impl();\n}\n\nvoid eeconfig_update_audio_current(void) {\n    eeconfig_update_audio(&audio_config);\n}\n\nvoid eeconfig_update_audio_default(void) {\n    audio_config.valid         = true;\n    audio_config.enable        = AUDIO_DEFAULT_ON;\n    audio_config.clicky_enable = AUDIO_DEFAULT_CLICKY_ON;\n    eeconfig_update_audio(&audio_config);\n}\n\nvoid audio_init(void) {\n    if (audio_initialized) {\n        return;\n    }\n\n    eeconfig_read_audio(&audio_config);\n    if (!audio_config.valid) {\n        dprintf(\"audio_init audio_config.valid = 0. Write default values to EEPROM.\\n\");\n        eeconfig_update_audio_default();\n    }\n\n    for (uint8_t i = 0; i < AUDIO_TONE_STACKSIZE; i++) {\n        tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};\n    }\n\n    audio_driver_initialize();\n    audio_initialized = true;\n\n    stop_all_notes();\n#ifndef AUDIO_INIT_DELAY\n    audio_startup();\n#endif\n}\n\nvoid audio_startup(void) {\n    if (audio_config.enable) {\n        PLAY_SONG(startup_song);\n    }\n\n    last_timestamp = timer_read();\n}\n\nvoid audio_toggle(void) {\n    if (audio_config.enable) {\n        stop_all_notes();\n    }\n    audio_config.enable ^= 1;\n    eeconfig_update_audio(&audio_config);\n    if (audio_config.enable) {\n        audio_on_user();\n    } else {\n        audio_off_user();\n    }\n}\n\nvoid audio_on(void) {\n    audio_config.enable = 1;\n    eeconfig_update_audio(&audio_config);\n    audio_on_user();\n    PLAY_SONG(audio_on_song);\n}\n\nvoid audio_off(void) {\n    PLAY_SONG(audio_off_song);\n    audio_off_user();\n    wait_ms(100);\n    audio_stop_all();\n    audio_config.enable = 0;\n    eeconfig_update_audio(&audio_config);\n}\n\nbool audio_is_on(void) {\n    return (audio_config.enable != 0);\n}\n\nvoid audio_stop_all(void) {\n    if (audio_driver_stopped) {\n        return;\n    }\n\n    active_tones = 0;\n\n    audio_driver_stop();\n\n    playing_melody = false;\n    playing_note   = false;\n\n    melody_current_note_duration = 0;\n\n    for (uint8_t i = 0; i < AUDIO_TONE_STACKSIZE; i++) {\n        tones[i] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};\n    }\n\n    audio_driver_stopped = true;\n}\n\nvoid audio_stop_tone(float pitch) {\n    if (pitch < 0.0f) {\n        pitch = -1 * pitch;\n    }\n\n    if (playing_note) {\n        if (!audio_initialized) {\n            audio_init();\n        }\n        bool found = false;\n        for (int i = AUDIO_TONE_STACKSIZE - 1; i >= 0; i--) {\n            found = (tones[i].pitch == pitch);\n            if (found) {\n                for (int j = i; (j < AUDIO_TONE_STACKSIZE - 1); j++) {\n                    tones[j] = tones[j + 1];\n                }\n                tones[AUDIO_TONE_STACKSIZE - 1] = (musical_tone_t){.time_started = 0, .pitch = -1.0f, .duration = 0};\n                break;\n            }\n        }\n        if (!found) {\n            return;\n        }\n\n        state_changed = true;\n        active_tones--;\n        if (active_tones < 0) active_tones = 0;\n#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING\n        if (tone_multiplexing_index_shift >= active_tones) {\n            tone_multiplexing_index_shift = 0;\n        }\n#endif\n        if (active_tones == 0) {\n            audio_driver_stop();\n            audio_driver_stopped = true;\n            playing_note         = false;\n        }\n    }\n}\n\nvoid audio_play_note(float pitch, uint16_t duration) {\n    if (!audio_config.enable) {\n        return;\n    }\n\n    if (!audio_initialized) {\n        audio_init();\n    }\n\n    if (pitch < 0.0f) {\n        pitch = -1 * pitch;\n    }\n\n    // round-robin: shifting out old tones, keeping only unique ones\n    // if the new frequency is already amongst the active tones, shift it to the top of the stack\n    bool found = false;\n    for (int i = active_tones - 1; i >= 0; i--) {\n        found = (tones[i].pitch == pitch);\n        if (found) {\n            for (int j = i; (j < active_tones - 1); j++) {\n                tones[j]     = tones[j + 1];\n                tones[j + 1] = (musical_tone_t){.time_started = timer_read(), .pitch = pitch, .duration = duration};\n            }\n            return; // since this frequency played already, the hardware was already started\n        }\n    }\n\n    // frequency/tone is actually new, so we put it on the top of the stack\n    active_tones++;\n    if (active_tones > AUDIO_TONE_STACKSIZE) {\n        active_tones = AUDIO_TONE_STACKSIZE;\n        // shift out the oldest tone to make room\n        for (int i = 0; i < active_tones - 1; i++) {\n            tones[i] = tones[i + 1];\n        }\n    }\n    state_changed           = true;\n    playing_note            = true;\n    tones[active_tones - 1] = (musical_tone_t){.time_started = timer_read(), .pitch = pitch, .duration = duration};\n\n    // TODO: needs to be handled per note/tone -> use its timestamp instead?\n    voices_timer = timer_read(); // reset to zero, for the effects added by voices.c\n\n    if (audio_driver_stopped) {\n        audio_driver_start();\n        audio_driver_stopped = false;\n    }\n}\n\nvoid audio_play_tone(float pitch) {\n    audio_play_note(pitch, 0xffff);\n}\n\nvoid audio_play_melody(float (*np)[][2], uint16_t n_count, bool n_repeat) {\n    if (!audio_config.enable) {\n        audio_stop_all();\n        return;\n    }\n\n    if (n_count == 0) {\n        return;\n    }\n\n    if (!audio_initialized) {\n        audio_init();\n    }\n\n    // Cancel note if a note is playing\n    if (playing_note) audio_stop_all();\n\n    playing_melody = true;\n    note_resting   = false;\n\n    notes_pointer = np;\n    notes_count   = n_count;\n    notes_repeat  = n_repeat;\n\n    current_note = 0; // note in the melody-array/list at note_pointer\n\n    // start first note manually, which also starts the audio_driver\n    // all following/remaining notes are played by 'audio_update_state'\n    audio_play_note((*notes_pointer)[current_note][0], audio_duration_to_ms((*notes_pointer)[current_note][1]));\n    last_timestamp               = timer_read();\n    melody_current_note_duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);\n}\n\nfloat click[2][2];\nvoid  audio_play_click(uint16_t delay, float pitch, uint16_t duration) {\n    uint16_t duration_tone  = audio_ms_to_duration(duration);\n    uint16_t duration_delay = audio_ms_to_duration(delay);\n\n    if (delay <= 0.0f) {\n        click[0][0] = pitch;\n        click[0][1] = duration_tone;\n        click[1][0] = 0.0f;\n        click[1][1] = 0.0f;\n        audio_play_melody(&click, 1, false);\n    } else {\n        // first note is a rest/pause\n        click[0][0] = 0.0f;\n        click[0][1] = duration_delay;\n        // second note is the actual click\n        click[1][0] = pitch;\n        click[1][1] = duration_tone;\n        audio_play_melody(&click, 2, false);\n    }\n}\n\nbool audio_is_playing_note(void) {\n    return playing_note;\n}\n\nbool audio_is_playing_melody(void) {\n    return playing_melody;\n}\n\nuint8_t audio_get_number_of_active_tones(void) {\n    return active_tones;\n}\n\nfloat audio_get_frequency(uint8_t tone_index) {\n    if (tone_index >= active_tones) {\n        return 0.0f;\n    }\n    return tones[active_tones - tone_index - 1].pitch;\n}\n\nfloat audio_get_processed_frequency(uint8_t tone_index) {\n    if (tone_index >= active_tones) {\n        return 0.0f;\n    }\n\n    int8_t index = active_tones - tone_index - 1;\n    // new tones are stacked on top (= appended at the end), so the most recent/current is MAX-1\n\n#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING\n    index = index - tone_multiplexing_index_shift;\n    if (index < 0) // wrap around\n        index += active_tones;\n#endif\n\n    if (tones[index].pitch <= 0.0f) {\n        return 0.0f;\n    }\n\n    return voice_envelope(tones[index].pitch);\n}\n\nbool audio_update_state(void) {\n    if (!playing_note && !playing_melody) {\n        return false;\n    }\n\n    bool     goto_next_note = false;\n    uint16_t current_time   = timer_read();\n\n    if (playing_melody) {\n        goto_next_note = timer_elapsed(last_timestamp) >= melody_current_note_duration;\n        if (goto_next_note) {\n            uint16_t delta         = timer_elapsed(last_timestamp) - melody_current_note_duration;\n            last_timestamp         = current_time;\n            uint16_t previous_note = current_note;\n            current_note++;\n            voices_timer = timer_read(); // reset to zero, for the effects added by voices.c\n\n            if (current_note >= notes_count) {\n                if (notes_repeat) {\n                    current_note = 0;\n                } else {\n                    audio_stop_all();\n                    return false;\n                }\n            }\n\n            if (!note_resting && (*notes_pointer)[previous_note][0] == (*notes_pointer)[current_note][0]) {\n                note_resting = true;\n\n                // special handling for successive notes of the same frequency:\n                // insert a short pause to separate them audibly\n                audio_play_note(0.0f, audio_duration_to_ms(2));\n                current_note                 = previous_note;\n                melody_current_note_duration = audio_duration_to_ms(2);\n\n            } else {\n                note_resting = false;\n\n                // TODO: handle glissando here (or remember previous and current tone)\n                /* there would need to be a freq(here we are) -> freq(next note)\n                 * and do slide/glissando in between problem here is to know which\n                 * frequency on the stack relates to what other? e.g. a melody starts\n                 * tones in a sequence, and stops expiring one, so the most recently\n                 * stopped is the starting point for a glissando to the most recently started?\n                 * how to detect and preserve this relation?\n                 * and what about user input, chords, ...?\n                 */\n\n                // '- delta': Skip forward in the next note's length if we've over shot\n                //            the last, so the overall length of the song is the same\n                uint16_t duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);\n\n                // Skip forward past any completely missed notes\n                while (delta > duration && current_note < notes_count - 1) {\n                    delta -= duration;\n                    current_note++;\n                    duration = audio_duration_to_ms((*notes_pointer)[current_note][1]);\n                }\n\n                if (delta < duration) {\n                    duration -= delta;\n                } else {\n                    // Only way to get here is if it is the last note and\n                    // we have completely missed it. Play it for 1ms...\n                    duration = 1;\n                }\n\n                audio_play_note((*notes_pointer)[current_note][0], duration);\n                melody_current_note_duration = duration;\n            }\n        }\n    }\n\n    if (playing_note) {\n#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING\n        tone_multiplexing_index_shift = (int)(current_time / tone_multiplexing_rate) % MIN(AUDIO_MAX_SIMULTANEOUS_TONES, active_tones);\n        goto_next_note                = true;\n#endif\n        if (vibrato || glissando) {\n            // force update on each cycle, since vibrato shifts the frequency slightly\n            goto_next_note = true;\n        }\n\n        // housekeeping: stop notes that have no playtime left\n        for (int i = 0; i < active_tones; i++) {\n            if ((tones[i].duration != 0xffff) // indefinitely playing notes, started by 'audio_play_tone'\n                && (tones[i].duration != 0)   // 'uninitialized'\n            ) {\n                if (timer_elapsed(tones[i].time_started) >= tones[i].duration) {\n                    audio_stop_tone(tones[i].pitch); // also sets 'state_changed=true'\n                }\n            }\n        }\n    }\n\n    // state-changes have a higher priority, always triggering the hardware to update\n    if (state_changed) {\n        state_changed = false;\n        return true;\n    }\n\n    return goto_next_note;\n}\n\n// Tone-multiplexing functions\n#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING\nvoid audio_set_tone_multiplexing_rate(uint16_t rate) {\n    tone_multiplexing_rate = rate;\n}\nvoid audio_enable_tone_multiplexing(void) {\n    tone_multiplexing_rate = AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT;\n}\nvoid audio_disable_tone_multiplexing(void) {\n    tone_multiplexing_rate = 0;\n}\nvoid audio_increase_tone_multiplexing_rate(uint16_t change) {\n    if ((0xffff - change) > tone_multiplexing_rate) {\n        tone_multiplexing_rate += change;\n    }\n}\nvoid audio_decrease_tone_multiplexing_rate(uint16_t change) {\n    if (change <= tone_multiplexing_rate) {\n        tone_multiplexing_rate -= change;\n    }\n}\n#endif\n\n// Tempo functions\n\nvoid audio_set_tempo(uint8_t tempo) {\n    if (tempo < 10) note_tempo = 10;\n    //  else if (tempo > 250)\n    //      note_tempo = 250;\n    else\n        note_tempo = tempo;\n}\n\nvoid audio_increase_tempo(uint8_t tempo_change) {\n    if (tempo_change > 255 - note_tempo)\n        note_tempo = 255;\n    else\n        note_tempo += tempo_change;\n}\n\nvoid audio_decrease_tempo(uint8_t tempo_change) {\n    if (tempo_change >= note_tempo - 10)\n        note_tempo = 10;\n    else\n        note_tempo -= tempo_change;\n}\n\n/**\n * Converts from units of 1/64ths of a beat to milliseconds.\n *\n * Round-off error is at most 1 millisecond.\n *\n * Conversion will never overflow for duration_bpm <= 699, provided that\n * note_tempo is at least 10. This is quite a long duration, over ten beats.\n *\n * Beware that for duration_bpm > 699, the result may overflow uint16_t range\n * when duration_bpm is large compared to note_tempo:\n *\n *    duration_bpm * 60 * 1000 / (64 * note_tempo) > UINT16_MAX\n *\n *    duration_bpm > (2 * 65535 / 1875) * note_tempo\n *                 = 69.904 * note_tempo.\n */\nuint16_t audio_duration_to_ms(uint16_t duration_bpm) {\n    return ((uint32_t)duration_bpm * 1875) / ((uint_fast16_t)note_tempo * 2);\n}\n\n/**\n * Converts from units of milliseconds to 1/64ths of a beat.\n *\n * Round-off error is at most 1/64th of a beat.\n *\n * This conversion never overflows: since duration_ms <= UINT16_MAX = 65535\n * and note_tempo <= 255, the result is always in uint16_t range:\n *\n *     duration_ms * 64 * note_tempo / 60 / 1000\n *     <= 65535 * 2 * 255 / 1875\n *      = 17825.52\n *     <= UINT16_MAX.\n */\nuint16_t audio_ms_to_duration(uint16_t duration_ms) {\n    return ((uint32_t)duration_ms * 2 * note_tempo) / 1875;\n}\n\n__attribute__((weak)) void audio_on_user(void) {}\n__attribute__((weak)) void audio_off_user(void) {}\n"
  },
  {
    "path": "quantum/audio/audio.h",
    "content": "/* Copyright 2016-2020 Jack Humbert\n * Copyright 2020 JohSchneider\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"compiler_support.h\"\n#include \"musical_notes.h\"\n#include \"song_list.h\"\n#include \"voices.h\"\n\n#if defined(AUDIO_DRIVER_PWM)\n#    include \"audio_pwm.h\"\n#elif defined(AUDIO_DRIVER_DAC)\n#    include \"audio_dac.h\"\n#endif\n\ntypedef union audio_config_t {\n    uint8_t raw;\n    struct {\n        bool    enable : 1;\n        bool    clicky_enable : 1;\n        bool    valid : 1;\n        uint8_t reserved : 5;\n    };\n} audio_config_t;\n\nSTATIC_ASSERT(sizeof(audio_config_t) == sizeof(uint8_t), \"Audio EECONFIG out of spec.\");\n\n/*\n * a 'musical note' is represented by pitch and duration; a 'musical tone' adds intensity and timbre\n * https://en.wikipedia.org/wiki/Musical_tone\n * \"A musical tone is characterized by its duration, pitch, intensity (or loudness), and timbre (or quality)\"\n */\ntypedef struct {\n    uint16_t time_started; // timestamp the tone/note was started, system time runs with 1ms resolution -> 16bit timer overflows every ~64 seconds, long enough under normal circumstances; but might be too soon for long-duration notes when the note_tempo is set to a very low value\n    float    pitch;        // aka frequency, in Hz\n    uint16_t duration;     // in ms, converted from the musical_notes.h unit which has 64parts to a beat, factoring in the current tempo in beats-per-minute\n    // float intensity;    // aka volume [0,1] TODO: not used at the moment; pwm drivers can't handle it\n    // uint8_t timbre;     // range: [0,100] TODO: this currently kept track of globally, should we do this per tone instead?\n} musical_tone_t;\n\n// public interface\n\n/**\n * @brief Save the current choices to the eeprom\n */\nvoid eeconfig_update_audio_current(void);\n\n/**\n * @brief one-time initialization called by quantum/quantum.c\n * @details usually done lazy, when some tones are to be played\n *\n * @post audio system (and hardware) initialized and ready to play tones\n */\nvoid audio_init(void);\nvoid audio_startup(void);\n\n/**\n * @brief en-/disable audio output, save this choice to the eeprom\n */\nvoid audio_toggle(void);\n/**\n * @brief enable audio output, save this choice to the eeprom\n */\nvoid audio_on(void);\n/**\n * @brief disable audio output, save this choice to the eeprom\n */\nvoid audio_off(void);\n/**\n * @brief query the if audio output is enabled\n */\nbool audio_is_on(void);\n\n/**\n * @brief start playback of a tone with the given frequency and duration\n *\n * @details starts the playback of a given note, which is automatically stopped\n *          at the the end of its duration = fire&forget\n *\n * @param[in] pitch frequency of the tone be played\n * @param[in] duration in milliseconds, use 'audio_duration_to_ms' to convert\n *                     from the musical_notes.h unit to ms\n */\nvoid audio_play_note(float pitch, uint16_t duration);\n// TODO: audio_play_note(float pitch, uint16_t duration, float intensity, float timbre);\n// audio_play_note_with_instrument ifdef AUDIO_ENABLE_VOICES\n\n/**\n * @brief start playback of a tone with the given frequency\n *\n * @details the 'frequency' is put on-top the internal stack of active tones,\n *          as a new tone with indefinite duration. this tone is played by\n *          the hardware until a call to 'audio_stop_tone'.\n *          should a tone with that frequency already be active, its entry\n *          is put on the top of said internal stack - so no duplicate\n *          entries are kept.\n *          'hardware_start' is called upon the first note.\n *\n * @param[in] pitch frequency of the tone be played\n */\nvoid audio_play_tone(float pitch);\n\n/**\n * @brief stop a given tone/frequency\n *\n * @details removes a tone matching the given frequency from the internal\n *          playback stack\n *          the hardware is stopped in case this was the last/only frequency\n *          being played.\n *\n * @param[in] pitch tone/frequency to be stopped\n */\nvoid audio_stop_tone(float pitch);\n\n/**\n * @brief play a melody\n *\n * @details starts playback of a melody passed in from a SONG definition - an\n *          array of {pitch, duration} float-tuples\n *\n * @param[in] np note-pointer to the SONG array\n * @param[in] n_count number of MUSICAL_NOTES of the SONG\n * @param[in] n_repeat false for onetime, true for looped playback\n */\nvoid audio_play_melody(float (*np)[][2], uint16_t n_count, bool n_repeat);\n\n/**\n * @brief play a short tone of a specific frequency to emulate a 'click'\n *\n * @details constructs a two-note melody (one pause plus a note) and plays it through\n *          audio_play_melody. very short durations might not quite work due to\n *          hardware limitations (DAC: added pulses from zero-crossing feature;...)\n *\n * @param[in] delay in milliseconds, length for the pause before the pulses, can be zero\n * @param[in] pitch\n * @param[in] duration in milliseconds, length of the 'click'\n */\nvoid audio_play_click(uint16_t delay, float pitch, uint16_t duration);\n\n/**\n * @brief stops all playback\n *\n * @details stops playback of both a melody as well as single tones, resetting\n *          the internal state\n */\nvoid audio_stop_all(void);\n\n/**\n * @brief query if one/multiple tones are playing\n */\nbool audio_is_playing_note(void);\n\n/**\n * @brief query if a melody/SONG is playing\n */\nbool audio_is_playing_melody(void);\n\n// These macros are used to allow audio_play_melody to play an array of indeterminate\n// length. This works around the limitation of C's sizeof operation on pointers.\n// The global float array for the song must be used here.\n#define NOTE_ARRAY_SIZE(x) ((int16_t)(sizeof(x) / (sizeof(x[0]))))\n\n/**\n * @brief convenience macro, to play a melody/SONG once\n */\n#define PLAY_SONG(note_array) audio_play_melody(&note_array, NOTE_ARRAY_SIZE((note_array)), false)\n// TODO: a 'song' is a melody plus singing/vocals -> PLAY_MELODY\n/**\n * @brief convenience macro, to play a melody/SONG in a loop, until stopped by 'audio_stop_all'\n */\n#define PLAY_LOOP(note_array) audio_play_melody(&note_array, NOTE_ARRAY_SIZE((note_array)), true)\n\n// Tone-Multiplexing functions\n// this feature only makes sense for hardware setups which can't do proper\n// audio-wave synthesis = have no DAC and need to use PWM for tone generation\n#ifdef AUDIO_ENABLE_TONE_MULTIPLEXING\n#    ifndef AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT\n#        define AUDIO_TONE_MULTIPLEXING_RATE_DEFAULT 0\n//       0=off, good starting value is 4; the lower the value the higher the cpu-load\n#    endif\nvoid audio_set_tone_multiplexing_rate(uint16_t rate);\nvoid audio_enable_tone_multiplexing(void);\nvoid audio_disable_tone_multiplexing(void);\nvoid audio_increase_tone_multiplexing_rate(uint16_t change);\nvoid audio_decrease_tone_multiplexing_rate(uint16_t change);\n#endif\n\n// Tempo functions\n\nvoid audio_set_tempo(uint8_t tempo);\nvoid audio_increase_tempo(uint8_t tempo_change);\nvoid audio_decrease_tempo(uint8_t tempo_change);\n\n// conversion macros, from 64parts-to-a-beat to milliseconds and back\nuint16_t audio_duration_to_ms(uint16_t duration_bpm);\nuint16_t audio_ms_to_duration(uint16_t duration_ms);\n\nvoid audio_startup(void);\n\n// hardware interface\n\n// implementation in the driver_avr/arm_* respective parts\nvoid audio_driver_initialize_impl(void);\nvoid audio_driver_start_impl(void);\nvoid audio_driver_stop_impl(void);\n\n/**\n * @brief get the number of currently active tones\n * @return number, 0=none active\n */\nuint8_t audio_get_number_of_active_tones(void);\n\n/**\n * @brief access to the raw/unprocessed frequency for a specific tone\n * @details each active tone has a frequency associated with it, which\n *          the internal state keeps track of, and is usually influenced\n *          by various effects\n * @param[in] tone_index, ranging from 0 to number_of_active_tones-1, with the\n *            first being the most recent and each increment yielding the next\n *            older one\n * @return a positive frequency, in Hz; or zero if the tone is a pause\n */\nfloat audio_get_frequency(uint8_t tone_index);\n\n/**\n * @brief calculate and return the frequency for the requested tone\n * @details effects like glissando, vibrato, ... are post-processed onto the\n *          each active tones 'base'-frequency; this function returns the\n *          post-processed result.\n * @param[in] tone_index, ranging from 0 to number_of_active_tones-1, with the\n *            first being the most recent and each increment yielding the next\n *            older one\n * @return a positive frequency, in Hz; or zero if the tone is a pause\n */\nfloat audio_get_processed_frequency(uint8_t tone_index);\n\n/**\n * @brief   update audio internal state: currently playing and active tones,...\n * @details This function is intended to be called by the audio-hardware\n *          specific implementation on a somewhat regular basis while a SONG\n *          or notes (pitch+duration) are playing to 'advance' the internal\n *          state (current playing notes, position in the melody, ...)\n *\n * @return true if something changed in the currently active tones, which the\n *         hardware might need to react to\n */\nbool audio_update_state(void);\n\n// legacy and back-warts compatibility stuff\n\n#define is_audio_on() audio_is_on()\n#define is_playing_notes() audio_is_playing_melody()\n#define is_playing_note() audio_is_playing_note()\n#define stop_all_notes() audio_stop_all()\n#define stop_note(f) audio_stop_tone(f)\n#define play_note(f, v) audio_play_tone(f)\n\n#define set_timbre(t) voice_set_timbre(t)\n#define set_tempo(t) audio_set_tempo(t)\n#define increase_tempo(t) audio_increase_tempo(t)\n#define decrease_tempo(t) audio_decrease_tempo(t)\n// vibrato functions are not used in any keyboards\n\nvoid audio_on_user(void);\nvoid audio_off_user(void);\n"
  },
  {
    "path": "quantum/audio/luts.c",
    "content": "/* Copyright 2016 IBNobody\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"luts.h\"\n\nconst float vibrato_lut[VIBRATO_LUT_LENGTH] = {\n    1.0022336811487, 1.0042529943610, 1.0058584256028, 1.0068905285205, 1.0072464122237, 1.0068905285205, 1.0058584256028, 1.0042529943610, 1.0022336811487, 1.0000000000000, 0.9977712970630, 0.9957650169978, 0.9941756956510, 0.9931566259436, 0.9928057204913, 0.9931566259436, 0.9941756956510, 0.9957650169978, 0.9977712970630, 1.0000000000000,\n};\n\nconst uint16_t frequency_lut[FREQUENCY_LUT_LENGTH] = {\n    0x8E0B, 0x8C02, 0x8A00, 0x8805, 0x8612, 0x8426, 0x8241, 0x8063, 0x7E8C, 0x7CBB, 0x7AF2, 0x792E, 0x7772, 0x75BB, 0x740B, 0x7261, 0x70BD, 0x6F20, 0x6D88, 0x6BF6, 0x6A69, 0x68E3, 0x6762, 0x65E6, 0x6470, 0x6300, 0x6194, 0x602E, 0x5ECD, 0x5D71, 0x5C1A, 0x5AC8, 0x597B, 0x5833, 0x56EF, 0x55B0, 0x5475, 0x533F, 0x520E, 0x50E1, 0x4FB8, 0x4E93, 0x4D73, 0x4C57, 0x4B3E, 0x4A2A, 0x491A, 0x480E, 0x4705, 0x4601, 0x4500, 0x4402, 0x4309, 0x4213, 0x4120, 0x4031, 0x3F46, 0x3E5D, 0x3D79, 0x3C97, 0x3BB9, 0x3ADD, 0x3A05, 0x3930, 0x385E, 0x3790, 0x36C4, 0x35FB, 0x3534, 0x3471, 0x33B1, 0x32F3, 0x3238, 0x3180, 0x30CA, 0x3017, 0x2F66, 0x2EB8, 0x2E0D, 0x2D64, 0x2CBD, 0x2C19, 0x2B77, 0x2AD8, 0x2A3A, 0x299F, 0x2907, 0x2870, 0x27DC, 0x2749, 0x26B9, 0x262B, 0x259F, 0x2515, 0x248D, 0x2407, 0x2382, 0x2300, 0x2280, 0x2201, 0x2184, 0x2109, 0x2090, 0x2018, 0x1FA3, 0x1F2E, 0x1EBC, 0x1E4B, 0x1DDC, 0x1D6E, 0x1D02, 0x1C98, 0x1C2F, 0x1BC8, 0x1B62, 0x1AFD, 0x1A9A,\n    0x1A38, 0x19D8, 0x1979, 0x191C, 0x18C0, 0x1865, 0x180B, 0x17B3, 0x175C, 0x1706, 0x16B2, 0x165E, 0x160C, 0x15BB, 0x156C, 0x151D, 0x14CF, 0x1483, 0x1438, 0x13EE, 0x13A4, 0x135C, 0x1315, 0x12CF, 0x128A, 0x1246, 0x1203, 0x11C1, 0x1180, 0x1140, 0x1100, 0x10C2, 0x1084, 0x1048, 0x100C, 0xFD1,  0xF97,  0xF5E,  0xF25,  0xEEE,  0xEB7,  0xE81,  0xE4C,  0xE17,  0xDE4,  0xDB1,  0xD7E,  0xD4D,  0xD1C,  0xCEC,  0xCBC,  0xC8E,  0xC60,  0xC32,  0xC05,  0xBD9,  0xBAE,  0xB83,  0xB59,  0xB2F,  0xB06,  0xADD,  0xAB6,  0xA8E,  0xA67,  0xA41,  0xA1C,  0x9F7,  0x9D2,  0x9AE,  0x98A,  0x967,  0x945,  0x923,  0x901,  0x8E0,  0x8C0,  0x8A0,  0x880,  0x861,  0x842,  0x824,  0x806,  0x7E8,  0x7CB,  0x7AF,  0x792,  0x777,  0x75B,  0x740,  0x726,  0x70B,  0x6F2,  0x6D8,  0x6BF,  0x6A6,  0x68E,  0x676,  0x65E,  0x647,  0x630,  0x619,  0x602,  0x5EC,  0x5D7,  0x5C1,  0x5AC,  0x597,  0x583,  0x56E,  0x55B,  0x547,  0x533,  0x520,  0x50E,  0x4FB,  0x4E9,\n    0x4D7,  0x4C5,  0x4B3,  0x4A2,  0x491,  0x480,  0x470,  0x460,  0x450,  0x440,  0x430,  0x421,  0x412,  0x403,  0x3F4,  0x3E5,  0x3D7,  0x3C9,  0x3BB,  0x3AD,  0x3A0,  0x393,  0x385,  0x379,  0x36C,  0x35F,  0x353,  0x347,  0x33B,  0x32F,  0x323,  0x318,  0x30C,  0x301,  0x2F6,  0x2EB,  0x2E0,  0x2D6,  0x2CB,  0x2C1,  0x2B7,  0x2AD,  0x2A3,  0x299,  0x290,  0x287,  0x27D,  0x274,  0x26B,  0x262,  0x259,  0x251,  0x248,  0x240,  0x238,  0x230,  0x228,  0x220,  0x218,  0x210,  0x209,  0x201,  0x1FA,  0x1F2,  0x1EB,  0x1E4,  0x1DD,  0x1D6,  0x1D0,  0x1C9,  0x1C2,  0x1BC,  0x1B6,  0x1AF,  0x1A9,  0x1A3,  0x19D,  0x197,  0x191,  0x18C,  0x186,  0x180,  0x17B,  0x175,  0x170,  0x16B,  0x165,  0x160,  0x15B,  0x156,  0x151,  0x14C,  0x148,  0x143,  0x13E,  0x13A,  0x135,  0x131,  0x12C,  0x128,  0x124,  0x120,  0x11C,  0x118,  0x114,  0x110,  0x10C,  0x108,  0x104,  0x100,  0xFD,   0xF9,   0xF5,   0xF2,   0xEE,\n};\n"
  },
  {
    "path": "quantum/audio/luts.h",
    "content": "/* Copyright 2016 IBNobody\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <float.h>\n#include <stdint.h>\n\n#define VIBRATO_LUT_LENGTH 20\n\n#define FREQUENCY_LUT_LENGTH 349\n\nextern const float    vibrato_lut[VIBRATO_LUT_LENGTH];\nextern const uint16_t frequency_lut[FREQUENCY_LUT_LENGTH];\n"
  },
  {
    "path": "quantum/audio/muse.c",
    "content": "#include \"muse.h\"\n\n#include <stdbool.h>\n\nenum { MUSE_OFF, MUSE_ON, MUSE_C_1_2, MUSE_C1, MUSE_C2, MUSE_C4, MUSE_C8, MUSE_C3, MUSE_C6, MUSE_B1, MUSE_B2, MUSE_B3, MUSE_B4, MUSE_B5, MUSE_B6, MUSE_B7, MUSE_B8, MUSE_B9, MUSE_B10, MUSE_B11, MUSE_B12, MUSE_B13, MUSE_B14, MUSE_B15, MUSE_B16, MUSE_B17, MUSE_B18, MUSE_B19, MUSE_B20, MUSE_B21, MUSE_B22, MUSE_B23, MUSE_B24, MUSE_B25, MUSE_B26, MUSE_B27, MUSE_B28, MUSE_B29, MUSE_B30, MUSE_B31 };\n\nbool number_of_ones_to_bool[16] = {1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1};\n\nuint8_t muse_interval[4] = {MUSE_B7, MUSE_B19, MUSE_B3, MUSE_B28};\nuint8_t muse_theme[4]    = {MUSE_B8, MUSE_B23, MUSE_B18, MUSE_B17};\n\nbool     muse_timer_1bit         = 0;\nuint8_t  muse_timer_2bit         = 0;\nuint8_t  muse_timer_2bit_counter = 0;\nuint8_t  muse_timer_4bit         = 0;\nuint32_t muse_timer_31bit        = 0;\n\nbool bit_for_value(uint8_t value) {\n    switch (value) {\n        case MUSE_OFF:\n            return 0;\n        case MUSE_ON:\n            return 1;\n        case MUSE_C_1_2:\n            return muse_timer_1bit;\n        case MUSE_C1:\n            return (muse_timer_4bit & 1);\n        case MUSE_C2:\n            return (muse_timer_4bit & 2);\n        case MUSE_C4:\n            return (muse_timer_4bit & 4);\n        case MUSE_C8:\n            return (muse_timer_4bit & 8);\n        case MUSE_C3:\n            return (muse_timer_2bit & 1);\n        case MUSE_C6:\n            return (muse_timer_2bit & 2);\n        default:\n            return muse_timer_31bit & (1UL << (value - MUSE_B1));\n    }\n}\n\nuint8_t muse_clock_pulse(void) {\n    bool top = number_of_ones_to_bool[bit_for_value(muse_theme[0]) + (bit_for_value(muse_theme[1]) << 1) + (bit_for_value(muse_theme[2]) << 2) + (bit_for_value(muse_theme[3]) << 3)];\n\n    if (muse_timer_1bit == 0) {\n        if (muse_timer_2bit_counter == 0) {\n            muse_timer_2bit = (muse_timer_2bit + 1) % 4;\n        }\n        muse_timer_2bit_counter = (muse_timer_2bit_counter + 1) % 3;\n        muse_timer_4bit         = (muse_timer_4bit + 1) % 16;\n        muse_timer_31bit        = (muse_timer_31bit << 1) + top;\n    }\n\n    muse_timer_1bit = (muse_timer_1bit + 1) % 2;\n\n    return bit_for_value(muse_interval[0]) + (bit_for_value(muse_interval[1]) << 1) + (bit_for_value(muse_interval[2]) << 2) + (bit_for_value(muse_interval[3]) << 3);\n}\n"
  },
  {
    "path": "quantum/audio/muse.h",
    "content": "#pragma once\n\n#include <stdint.h>\n\nuint8_t muse_clock_pulse(void);\n"
  },
  {
    "path": "quantum/audio/musical_notes.h",
    "content": "/* Copyright 2016 Jack Humbert\n * Copyright 2020 JohSchneider\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#ifndef TEMPO_DEFAULT\n#    define TEMPO_DEFAULT 120\n// in beats-per-minute\n#endif\n\n#define SONG(notes...) \\\n    { notes }\n\n// Note Types\n#define MUSICAL_NOTE(note, duration) \\\n    { (NOTE##note), duration }\n\n#define BREVE_NOTE(note) MUSICAL_NOTE(note, 128)\n#define WHOLE_NOTE(note) MUSICAL_NOTE(note, 64)\n#define HALF_NOTE(note) MUSICAL_NOTE(note, 32)\n#define QUARTER_NOTE(note) MUSICAL_NOTE(note, 16)\n#define EIGHTH_NOTE(note) MUSICAL_NOTE(note, 8)\n#define SIXTEENTH_NOTE(note) MUSICAL_NOTE(note, 4)\n#define THIRTYSECOND_NOTE(note) MUSICAL_NOTE(note, 2)\n\n#define BREVE_DOT_NOTE(note) MUSICAL_NOTE(note, 128 + 64)\n#define WHOLE_DOT_NOTE(note) MUSICAL_NOTE(note, 64 + 32)\n#define HALF_DOT_NOTE(note) MUSICAL_NOTE(note, 32 + 16)\n#define QUARTER_DOT_NOTE(note) MUSICAL_NOTE(note, 16 + 8)\n#define EIGHTH_DOT_NOTE(note) MUSICAL_NOTE(note, 8 + 4)\n#define SIXTEENTH_DOT_NOTE(note) MUSICAL_NOTE(note, 4 + 2)\n#define THIRTYSECOND_DOT_NOTE(note) MUSICAL_NOTE(note, 2 + 1)\n// duration of 64 units == one beat == one whole note\n// with a tempo of 60bpm this comes to a length of one second\n\n// Note Type Shortcuts\n#define M__NOTE(note, duration) MUSICAL_NOTE(note, duration)\n#define B__NOTE(n) BREVE_NOTE(n)\n#define W__NOTE(n) WHOLE_NOTE(n)\n#define H__NOTE(n) HALF_NOTE(n)\n#define Q__NOTE(n) QUARTER_NOTE(n)\n#define E__NOTE(n) EIGHTH_NOTE(n)\n#define S__NOTE(n) SIXTEENTH_NOTE(n)\n#define T__NOTE(n) THIRTYSECOND_NOTE(n)\n#define BD_NOTE(n) BREVE_DOT_NOTE(n)\n#define WD_NOTE(n) WHOLE_DOT_NOTE(n)\n#define HD_NOTE(n) HALF_DOT_NOTE(n)\n#define QD_NOTE(n) QUARTER_DOT_NOTE(n)\n#define ED_NOTE(n) EIGHTH_DOT_NOTE(n)\n#define SD_NOTE(n) SIXTEENTH_DOT_NOTE(n)\n#define TD_NOTE(n) THIRTYSECOND_DOT_NOTE(n)\n\n// Note Timbre\n// Changes how the notes sound\n#define TIMBRE_12 12\n#define TIMBRE_25 25\n#define TIMBRE_50 50\n#define TIMBRE_75 75\n#ifndef TIMBRE_DEFAULT\n#    define TIMBRE_DEFAULT TIMBRE_50\n#endif\n\n// Notes - # = Octave\n\n#define NOTE_REST 0.00f\n\n#define NOTE_C0 16.35f\n#define NOTE_CS0 17.32f\n#define NOTE_D0 18.35f\n#define NOTE_DS0 19.45f\n#define NOTE_E0 20.60f\n#define NOTE_F0 21.83f\n#define NOTE_FS0 23.12f\n#define NOTE_G0 24.50f\n#define NOTE_GS0 25.96f\n#define NOTE_A0 27.50f\n#define NOTE_AS0 29.14f\n#define NOTE_B0 30.87f\n#define NOTE_C1 32.70f\n#define NOTE_CS1 34.65f\n#define NOTE_D1 36.71f\n#define NOTE_DS1 38.89f\n#define NOTE_E1 41.20f\n#define NOTE_F1 43.65f\n#define NOTE_FS1 46.25f\n#define NOTE_G1 49.00f\n#define NOTE_GS1 51.91f\n#define NOTE_A1 55.00f\n#define NOTE_AS1 58.27f\n#define NOTE_B1 61.74f\n#define NOTE_C2 65.41f\n#define NOTE_CS2 69.30f\n#define NOTE_D2 73.42f\n#define NOTE_DS2 77.78f\n#define NOTE_E2 82.41f\n#define NOTE_F2 87.31f\n#define NOTE_FS2 92.50f\n#define NOTE_G2 98.00f\n#define NOTE_GS2 103.83f\n#define NOTE_A2 110.00f\n#define NOTE_AS2 116.54f\n#define NOTE_B2 123.47f\n#define NOTE_C3 130.81f\n#define NOTE_CS3 138.59f\n#define NOTE_D3 146.83f\n#define NOTE_DS3 155.56f\n#define NOTE_E3 164.81f\n#define NOTE_F3 174.61f\n#define NOTE_FS3 185.00f\n#define NOTE_G3 196.00f\n#define NOTE_GS3 207.65f\n#define NOTE_A3 220.00f\n#define NOTE_AS3 233.08f\n#define NOTE_B3 246.94f\n#define NOTE_C4 261.63f\n#define NOTE_CS4 277.18f\n#define NOTE_D4 293.66f\n#define NOTE_DS4 311.13f\n#define NOTE_E4 329.63f\n#define NOTE_F4 349.23f\n#define NOTE_FS4 369.99f\n#define NOTE_G4 392.00f\n#define NOTE_GS4 415.30f\n#define NOTE_A4 440.00f\n#define NOTE_AS4 466.16f\n#define NOTE_B4 493.88f\n#define NOTE_C5 523.25f\n#define NOTE_CS5 554.37f\n#define NOTE_D5 587.33f\n#define NOTE_DS5 622.25f\n#define NOTE_E5 659.26f\n#define NOTE_F5 698.46f\n#define NOTE_FS5 739.99f\n#define NOTE_G5 783.99f\n#define NOTE_GS5 830.61f\n#define NOTE_A5 880.00f\n#define NOTE_AS5 932.33f\n#define NOTE_B5 987.77f\n#define NOTE_C6 1046.50f\n#define NOTE_CS6 1108.73f\n#define NOTE_D6 1174.66f\n#define NOTE_DS6 1244.51f\n#define NOTE_E6 1318.51f\n#define NOTE_F6 1396.91f\n#define NOTE_FS6 1479.98f\n#define NOTE_G6 1567.98f\n#define NOTE_GS6 1661.22f\n#define NOTE_A6 1760.00f\n#define NOTE_AS6 1864.66f\n#define NOTE_B6 1975.53f\n#define NOTE_C7 2093.00f\n#define NOTE_CS7 2217.46f\n#define NOTE_D7 2349.32f\n#define NOTE_DS7 2489.02f\n#define NOTE_E7 2637.02f\n#define NOTE_F7 2793.83f\n#define NOTE_FS7 2959.96f\n#define NOTE_G7 3135.96f\n#define NOTE_GS7 3322.44f\n#define NOTE_A7 3520.00f\n#define NOTE_AS7 3729.31f\n#define NOTE_B7 3951.07f\n#define NOTE_C8 4186.01f\n#define NOTE_CS8 4434.92f\n#define NOTE_D8 4698.64f\n#define NOTE_DS8 4978.03f\n#define NOTE_E8 5274.04f\n#define NOTE_F8 5587.65f\n#define NOTE_FS8 5919.91f\n#define NOTE_G8 6271.93f\n#define NOTE_GS8 6644.88f\n#define NOTE_A8 7040.00f\n#define NOTE_AS8 7458.62f\n#define NOTE_B8 7902.13f\n\n// Flat Aliases\n#define NOTE_DF0 NOTE_CS0\n#define NOTE_EF0 NOTE_DS0\n#define NOTE_GF0 NOTE_FS0\n#define NOTE_AF0 NOTE_GS0\n#define NOTE_BF0 NOTE_AS0\n#define NOTE_DF1 NOTE_CS1\n#define NOTE_EF1 NOTE_DS1\n#define NOTE_GF1 NOTE_FS1\n#define NOTE_AF1 NOTE_GS1\n#define NOTE_BF1 NOTE_AS1\n#define NOTE_DF2 NOTE_CS2\n#define NOTE_EF2 NOTE_DS2\n#define NOTE_GF2 NOTE_FS2\n#define NOTE_AF2 NOTE_GS2\n#define NOTE_BF2 NOTE_AS2\n#define NOTE_DF3 NOTE_CS3\n#define NOTE_EF3 NOTE_DS3\n#define NOTE_GF3 NOTE_FS3\n#define NOTE_AF3 NOTE_GS3\n#define NOTE_BF3 NOTE_AS3\n#define NOTE_DF4 NOTE_CS4\n#define NOTE_EF4 NOTE_DS4\n#define NOTE_GF4 NOTE_FS4\n#define NOTE_AF4 NOTE_GS4\n#define NOTE_BF4 NOTE_AS4\n#define NOTE_DF5 NOTE_CS5\n#define NOTE_EF5 NOTE_DS5\n#define NOTE_GF5 NOTE_FS5\n#define NOTE_AF5 NOTE_GS5\n#define NOTE_BF5 NOTE_AS5\n#define NOTE_DF6 NOTE_CS6\n#define NOTE_EF6 NOTE_DS6\n#define NOTE_GF6 NOTE_FS6\n#define NOTE_AF6 NOTE_GS6\n#define NOTE_BF6 NOTE_AS6\n#define NOTE_DF7 NOTE_CS7\n#define NOTE_EF7 NOTE_DS7\n#define NOTE_GF7 NOTE_FS7\n#define NOTE_AF7 NOTE_GS7\n#define NOTE_BF7 NOTE_AS7\n#define NOTE_DF8 NOTE_CS8\n#define NOTE_EF8 NOTE_DS8\n#define NOTE_GF8 NOTE_FS8\n#define NOTE_AF8 NOTE_GS8\n#define NOTE_BF8 NOTE_AS8\n"
  },
  {
    "path": "quantum/audio/song_list.h",
    "content": "/* Any song or sound without a license explicitly stated is:\n *\n * Copyright 2016 Jack Humbert\n * Copyright 2017 Zach White\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include \"musical_notes.h\"\n\n#if __has_include(\"user_song_list.h\")\n#    include \"user_song_list.h\"\n#endif // if file exists\n\n#define NO_SOUND\n\n/* Ode to Joy\n * Author: Friedrich Schiller\n + License: Public Domain\n */\n#define ODE_TO_JOY Q__NOTE(_E4), Q__NOTE(_E4), Q__NOTE(_F4), Q__NOTE(_G4), Q__NOTE(_G4), Q__NOTE(_F4), Q__NOTE(_E4), Q__NOTE(_D4), Q__NOTE(_C4), Q__NOTE(_C4), Q__NOTE(_D4), Q__NOTE(_E4), QD_NOTE(_E4), E__NOTE(_D4), H__NOTE(_D4),\n\n/* Rock-a-bye Baby\n * Author: Unknown\n + License: Public Domain\n */\n#define ROCK_A_BYE_BABY QD_NOTE(_B4), E__NOTE(_D4), Q__NOTE(_B5), H__NOTE(_A5), Q__NOTE(_G5), QD_NOTE(_B4), E__NOTE(_D5), Q__NOTE(_G5), H__NOTE(_FS5),\n\n#define CLUEBOARD_SOUND HD_NOTE(_C3), HD_NOTE(_D3), HD_NOTE(_E3), HD_NOTE(_F3), HD_NOTE(_G3), HD_NOTE(_A4), HD_NOTE(_B4), HD_NOTE(_C4)\n/*\n    HD_NOTE(_G3), HD_NOTE(_E3), HD_NOTE(_C3), \\\n    Q__NOTE(_E3), Q__NOTE(_C3), Q__NOTE(_G3), \\\n    Q__NOTE(_E3)\n*/\n/*\n    HD_NOTE(_C3), HD_NOTE(_G3), HD_NOTE(_E3), \\\n    Q__NOTE(_G3), Q__NOTE(_E3), Q__NOTE(_G3), \\\n    Q__NOTE(_F3)\n*/\n\n#define STARTUP_SOUND E__NOTE(_E6), E__NOTE(_A6), ED_NOTE(_E7),\n\n#define GOODBYE_SOUND E__NOTE(_E7), E__NOTE(_A6), ED_NOTE(_E6),\n\n#define PLANCK_SOUND ED_NOTE(_E7), E__NOTE(_CS7), E__NOTE(_E6), E__NOTE(_A6), M__NOTE(_CS7, 20),\n\n#define PREONIC_SOUND M__NOTE(_B5, 20), E__NOTE(_B6), M__NOTE(_DS6, 20), E__NOTE(_B6),\n\n#define QWERTY_SOUND E__NOTE(_GS6), E__NOTE(_A6), S__NOTE(_REST), Q__NOTE(_E7),\n\n#define COLEMAK_SOUND E__NOTE(_GS6), E__NOTE(_A6), S__NOTE(_REST), ED_NOTE(_E7), S__NOTE(_REST), ED_NOTE(_GS7),\n\n#define DVORAK_SOUND E__NOTE(_GS6), E__NOTE(_A6), S__NOTE(_REST), E__NOTE(_E7), S__NOTE(_REST), E__NOTE(_FS7), S__NOTE(_REST), E__NOTE(_E7),\n\n#define WORKMAN_SOUND E__NOTE(_GS6), E__NOTE(_A6), S__NOTE(_REST), E__NOTE(_GS6), E__NOTE(_A6), S__NOTE(_REST), ED_NOTE(_FS7), S__NOTE(_REST), ED_NOTE(_A7),\n\n#define PLOVER_SOUND E__NOTE(_GS6), E__NOTE(_A6), S__NOTE(_REST), ED_NOTE(_E7), S__NOTE(_REST), ED_NOTE(_A7),\n\n#define PLOVER_GOODBYE_SOUND E__NOTE(_GS6), E__NOTE(_A6), S__NOTE(_REST), ED_NOTE(_A7), S__NOTE(_REST), ED_NOTE(_E7),\n\n#define MUSIC_ON_SOUND E__NOTE(_A5), E__NOTE(_B5), E__NOTE(_CS6), E__NOTE(_D6), E__NOTE(_E6), E__NOTE(_FS6), E__NOTE(_GS6), E__NOTE(_A6),\n\n#define AUDIO_ON_SOUND E__NOTE(_A5), E__NOTE(_A6),\n\n#define AUDIO_OFF_SOUND E__NOTE(_A6), E__NOTE(_A5),\n\n#define MUSIC_SCALE_SOUND MUSIC_ON_SOUND\n\n#define MUSIC_OFF_SOUND E__NOTE(_A6), E__NOTE(_GS6), E__NOTE(_FS6), E__NOTE(_E6), E__NOTE(_D6), E__NOTE(_CS6), E__NOTE(_B5), E__NOTE(_A5),\n\n#define VOICE_CHANGE_SOUND Q__NOTE(_A5), Q__NOTE(_CS6), Q__NOTE(_E6), Q__NOTE(_A6),\n\n#define CHROMATIC_SOUND Q__NOTE(_A5), Q__NOTE(_AS5), Q__NOTE(_B5), Q__NOTE(_C6), Q__NOTE(_CS6),\n\n#define MAJOR_SOUND Q__NOTE(_A5), Q__NOTE(_B5), Q__NOTE(_CS6), Q__NOTE(_D6), Q__NOTE(_E6),\n\n#define MINOR_SOUND Q__NOTE(_A5), Q__NOTE(_B5), Q__NOTE(_C6), Q__NOTE(_D6), Q__NOTE(_E6),\n\n#define GUITAR_SOUND Q__NOTE(_E5), Q__NOTE(_A5), Q__NOTE(_D6), Q__NOTE(_G6),\n\n#define VIOLIN_SOUND Q__NOTE(_G5), Q__NOTE(_D6), Q__NOTE(_A6), Q__NOTE(_E7),\n\n#define CAPS_LOCK_ON_SOUND E__NOTE(_A3), E__NOTE(_B3),\n\n#define CAPS_LOCK_OFF_SOUND E__NOTE(_B3), E__NOTE(_A3),\n\n#define SCROLL_LOCK_ON_SOUND E__NOTE(_D4), E__NOTE(_E4),\n\n#define SCROLL_LOCK_OFF_SOUND E__NOTE(_E4), E__NOTE(_D4),\n\n#define NUM_LOCK_ON_SOUND E__NOTE(_D5), E__NOTE(_E5),\n\n#define NUM_LOCK_OFF_SOUND E__NOTE(_E5), E__NOTE(_D5),\n\n#define AG_NORM_SOUND E__NOTE(_A5), E__NOTE(_A5),\n\n#define AG_SWAP_SOUND SD_NOTE(_B5), SD_NOTE(_A5), SD_NOTE(_B5), SD_NOTE(_A5),\n\n#define UNICODE_WINDOWS E__NOTE(_B5), S__NOTE(_E6),\n\n#define UNICODE_LINUX E__NOTE(_E6), S__NOTE(_B5),\n\n#define TERMINAL_SOUND E__NOTE(_C5)\n\n/* Title:            La Campanella\n * Author/Composer:  Frank Lizst\n * License:          Public Domain\n */\n#define CAMPANELLA                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 \\\n    Q__NOTE(_DS4), E__NOTE(_DS4), E__NOTE(_DS5), Q__NOTE(_DS5), E__NOTE(_DS5), E__NOTE(_DS6), Q__NOTE(_DS5), E__NOTE(_DS5), E__NOTE(_DS6), Q__NOTE(_CS5), E__NOTE(_CS5), E__NOTE(_DS6), Q__NOTE(_B4), E__NOTE(_B4), E__NOTE(_DS6), Q__NOTE(_B4), E__NOTE(_B4), E__NOTE(_DS6), Q__NOTE(_AS4), E__NOTE(_AS4), E__NOTE(_DS6), Q__NOTE(_GS4), E__NOTE(_GS4), E__NOTE(_DS6), Q__NOTE(_G4), E__NOTE(_G4), E__NOTE(_DS6), Q__NOTE(_GS4), E__NOTE(_GS4), E__NOTE(_DS6), Q__NOTE(_AS4), E__NOTE(_AS4), E__NOTE(_DS6), Q__NOTE(_DS4), E__NOTE(_DS4), E__NOTE(_DS6), Q__NOTE(_DS5), E__NOTE(_DS5), E__NOTE(_DS6), Q__NOTE(_E5), E__NOTE(_E5), E__NOTE(_DS6), Q__NOTE(_DS5), E__NOTE(_DS5), E__NOTE(_DS6), Q__NOTE(_CS5), E__NOTE(_CS5), E__NOTE(_DS6), Q__NOTE(_B4), E__NOTE(_B4), E__NOTE(_DS6), Q__NOTE(_B4), E__NOTE(_B4), E__NOTE(_DS6), Q__NOTE(_AS4), E__NOTE(_AS4), E__NOTE(_DS6), Q__NOTE(_GS4), E__NOTE(_GS4), E__NOTE(_DS6), Q__NOTE(_G4), E__NOTE(_G4), E__NOTE(_DS6), Q__NOTE(_GS4), E__NOTE(_GS4), E__NOTE(_DS6), Q__NOTE(_AS4), \\\n        E__NOTE(_AS4), E__NOTE(_DS6), Q__NOTE(_DS4), E__NOTE(_DS4), E__NOTE(_DS5), Q__NOTE(_DS5), E__NOTE(_DS5), E__NOTE(_DS6), Q__NOTE(_DS6), E__NOTE(_DS6), E__NOTE(_DS7), Q__NOTE(_DS6), E__NOTE(_DS6), E__NOTE(_DS7), Q__NOTE(_CS6), E__NOTE(_CS6), E__NOTE(_DS7), Q__NOTE(_B5), E__NOTE(_B5), E__NOTE(_DS7), Q__NOTE(_B5), E__NOTE(_B5), E__NOTE(_DS7), Q__NOTE(_AS5), E__NOTE(_AS5), E__NOTE(_DS7), Q__NOTE(_GS5), E__NOTE(_GS5), E__NOTE(_DS7), Q__NOTE(_G5), E__NOTE(_G5), E__NOTE(_DS7), Q__NOTE(_GS5), E__NOTE(_GS5), E__NOTE(_DS7), Q__NOTE(_AS5), E__NOTE(_AS5), E__NOTE(_DS7), Q__NOTE(_DS5), E__NOTE(_DS5), E__NOTE(_DS7), W__NOTE(_DS6), W__NOTE(_GS5),\n\n/* Title:            Fantaisie-Impromptu\n * Author/Composer:  Chopin\n * License:          Public Domain\n */\n#define FANTASIE_IMPROMPTU                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   \\\n    E__NOTE(_GS4), E__NOTE(_A4), E__NOTE(_GS4), E__NOTE(_REST), E__NOTE(_GS4), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_C5), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_GS5), E__NOTE(_GS4), E__NOTE(_A4), E__NOTE(_GS4), E__NOTE(_REST), E__NOTE(_GS4), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_C5), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_GS5), E__NOTE(_A4), E__NOTE(_CS5), E__NOTE(_DS5), E__NOTE(_FS5), E__NOTE(_A5), E__NOTE(_CS6), E__NOTE(_DS6), E__NOTE(_B6), E__NOTE(_A6), E__NOTE(_GS6), E__NOTE(_FS6), E__NOTE(_E6), E__NOTE(_DS6), E__NOTE(_FS6), E__NOTE(_CS6), E__NOTE(_C5), E__NOTE(_DS6), E__NOTE(_A5), E__NOTE(_GS5), E__NOTE(_FS5), E__NOTE(_A5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_FS5), E__NOTE(_CS5), E__NOTE(_C5), E__NOTE(_DS5), E__NOTE(_A4), E__NOTE(_GS4), E__NOTE(_B4), E__NOTE(_A4), E__NOTE(_A4), E__NOTE(_GS4), E__NOTE(_A4), E__NOTE(_GS4), E__NOTE(_REST), E__NOTE(_GS4), \\\n        E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_C5), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_GS5), E__NOTE(_GS4), E__NOTE(_AS4), E__NOTE(_GS4), E__NOTE(_REST), E__NOTE(_GS4), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_DS5), E__NOTE(_CS5), E__NOTE(_C5), E__NOTE(_CS5), E__NOTE(_E5), E__NOTE(_GS5), E__NOTE(_DS5), E__NOTE(_E5), E__NOTE(_DS5), E__NOTE(_REST), E__NOTE(_DS5), E__NOTE(_B5), E__NOTE(_AS5), E__NOTE(_GS5), E__NOTE(_REST), E__NOTE(_E6), E__NOTE(_DS6), E__NOTE(_CS6), E__NOTE(_B5), E__NOTE(_AS5), E__NOTE(_GS5), E__NOTE(_REST), E__NOTE(_AS5), WD_NOTE(_GS5),\n\n/* Title:            Nocturne Op. 9 No. 1 in B flat minor\n * Author/Composer:  Chopin\n * License:          Public Domain\n */\n#define NOCTURNE_OP_9_NO_1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \\\n    H__NOTE(_BF5), H__NOTE(_C6), H__NOTE(_DF6), H__NOTE(_A5), H__NOTE(_BF5), H__NOTE(_GF5), W__NOTE(_F5), W__NOTE(_F5), W__NOTE(_F5), W__NOTE(_F5), H__NOTE(_GF5), H__NOTE(_F5), H__NOTE(_EF5), H__NOTE(_C5), B__NOTE(_DF5), W__NOTE(_BF4), Q__NOTE(_BF5), Q__NOTE(_C6), Q__NOTE(_DF6), Q__NOTE(_A5), Q__NOTE(_BF5), Q__NOTE(_A5), Q__NOTE(_GS5), Q__NOTE(_A5), Q__NOTE(_C6), Q__NOTE(_BF5), Q__NOTE(_GF5), Q__NOTE(_F5), Q__NOTE(_GF5), Q__NOTE(_E5), Q__NOTE(_F5), Q__NOTE(_BF5), Q__NOTE(_A5), Q__NOTE(_AF5), Q__NOTE(_G5), Q__NOTE(_GF5), Q__NOTE(_F5), Q__NOTE(_E5), Q__NOTE(_EF5), Q__NOTE(_D5), Q__NOTE(_DF5), Q__NOTE(_C5), Q__NOTE(_DF5), Q__NOTE(_C5), Q__NOTE(_B4), Q__NOTE(_C5), Q__NOTE(_F5), Q__NOTE(_E5), Q__NOTE(_EF5), B__NOTE(_DF5), W__NOTE(_BF4), W__NOTE(_BF5), W__NOTE(_BF5), W__NOTE(_BF5), BD_NOTE(_AF5), W__NOTE(_DF5), H__NOTE(_BF4), H__NOTE(_C5), H__NOTE(_DF5), H__NOTE(_GF5), H__NOTE(_GF5), BD_NOTE(_F5), W__NOTE(_EF5), H__NOTE(_F5), H__NOTE(_EF5), H__NOTE(_DF5), H__NOTE(_A4), B__NOTE(_AF4), \\\n        W__NOTE(_DF5), W__NOTE(_EF5), H__NOTE(_F5), H__NOTE(_EF5), H__NOTE(_DF5), H__NOTE(_EF5), BD_NOTE(_F5),\n\n/* Title:            State Anthem of the Soviet Union\n * Author/Composer:  Alexander Alexandrov\n * License:          Public Domain\n */\n#define USSR_ANTHEM B__NOTE(_G6), B__NOTE(_C7), W__NOTE(_G6), H__NOTE(_A6), B__NOTE(_B6), W__NOTE(_E6), W__NOTE(_E6), B__NOTE(_A6), W__NOTE(_G6), H__NOTE(_F6), B__NOTE(_G6), W__NOTE(_C6), W__NOTE(_C6), B__NOTE(_D6), W__NOTE(_D6), W__NOTE(_E6), B__NOTE(_D6), W__NOTE(_D6), W__NOTE(_G6), B__NOTE(_F6), W__NOTE(_G6), W__NOTE(_A6), B__NOTE(_B6),\n\n/* Title:            Hymn Risen\n * Author/Composer:  Terrance Andrew Davis\n * License:          Public Domain\n */\n#define TOS_HYMN_RISEN H__NOTE(_D5), H__NOTE(_E5), HD_NOTE(_F5), HD_NOTE(_F5), H__NOTE(_F5), HD_NOTE(_D5), E__NOTE(_E5), E__NOTE(_E5), H__NOTE(_C5), Q__NOTE(_D5), Q__NOTE(_D5), H__NOTE(_E5), H__NOTE(_C5), Q__NOTE(_G5), Q__NOTE(_F5), H__NOTE(_D5), H__NOTE(_E5), HD_NOTE(_F5), HD_NOTE(_F5), H__NOTE(_F5), HD_NOTE(_D5), E__NOTE(_E5), E__NOTE(_E5), H__NOTE(_C5), Q__NOTE(_D5), Q__NOTE(_D5), H__NOTE(_E5), H__NOTE(_C5), Q__NOTE(_G5), Q__NOTE(_F5), H__NOTE(_D5), H__NOTE(_C5), W__NOTE(_D5), W__NOTE(_E5), Q__NOTE(_A4), H__NOTE(_A4), Q__NOTE(_E5), Q__NOTE(_E5), Q__NOTE(_F5), Q__NOTE(_E5), Q__NOTE(_D5), Q__NOTE(_G5), Q__NOTE(_B4), Q__NOTE(_D5), Q__NOTE(_C5), M__NOTE(_F5, 80), H__NOTE(_D5), H__NOTE(_C5), W__NOTE(_D5), W__NOTE(_E5), Q__NOTE(_A4), H__NOTE(_A4), Q__NOTE(_E5), Q__NOTE(_E5), Q__NOTE(_F5), Q__NOTE(_E5), Q__NOTE(_D5), Q__NOTE(_G5), Q__NOTE(_B4), Q__NOTE(_D5), Q__NOTE(_C5), M__NOTE(_F5, 80)\n\n/* Removed sounds\n +   This list is here solely for compatibility, so that removed songs don't just break things\n *   If you think that any of these songs were wrongfully removed, let us know and provide\n *   proof of permission to use them, or public domain status.\n */\n\n#ifndef CLOSE_ENCOUNTERS_5_NOTE\n#    define CLOSE_ENCOUNTERS_5_NOTE\n#endif\n#ifndef DOE_A_DEER\n#    define DOE_A_DEER\n#endif\n#ifndef IN_LIKE_FLINT\n#    define IN_LIKE_FLINT\n#endif\n#ifndef IMPERIAL_MARCH\n#    define IMPERIAL_MARCH\n#endif\n#ifndef BASKET_CASE\n#    define BASKET_CASE\n#endif\n#ifndef COIN_SOUND\n#    define COIN_SOUND\n#endif\n#ifndef ONE_UP_SOUND\n#    define ONE_UP_SOUND\n#endif\n#ifndef SONIC_RING\n#    define SONIC_RING\n#endif\n#ifndef ZELDA_PUZZLE\n#    define ZELDA_PUZZLE\n#endif\n#ifndef ZELDA_TREASURE\n#    define ZELDA_TREASURE\n#endif\n#ifndef OVERWATCH_THEME\n#    define OVERWATCH_THEME\n#endif\n#ifndef MARIO_THEME\n#    define MARIO_THEME\n#endif\n#ifndef MARIO_GAMEOVER\n#    define MARIO_GAMEOVER\n#endif\n#ifndef MARIO_MUSHROOM\n#    define MARIO_MUSHROOM\n#endif\n#ifndef E1M1_DOOM\n#    define E1M1_DOOM\n#endif\n#ifndef DISNEY_SONG\n#    define DISNEY_SONG\n#endif\n#ifndef NUMBER_ONE\n#    define NUMBER_ONE\n#endif\n#ifndef CABBAGE_SONG\n#    define CABBAGE_SONG\n#endif\n#ifndef OLD_SPICE\n#    define OLD_SPICE\n#endif\n#ifndef VICTORY_FANFARE_SHORT\n#    define VICTORY_FANFARE_SHORT\n#endif\n#ifndef ALL_STAR\n#    define ALL_STAR\n#endif\n#ifndef RICK_ROLL\n#    define RICK_ROLL\n#endif\n#ifndef FF_PRELUDE\n#    define FF_PRELUDE\n#endif\n#ifndef TO_BOLDLY_GO\n#    define TO_BOLDLY_GO\n#endif\n#ifndef KATAWARE_DOKI\n#    define KATAWARE_DOKI\n#endif\n#ifndef MEGALOVANIA\n#    define MEGALOVANIA\n#endif\n#ifndef MICHISHIRUBE\n#    define MICHISHIRUBE\n#endif\n#ifndef LIEBESLEID\n#    define LIEBESLEID\n#endif\n#ifndef MELODIES_OF_LIFE\n#    define MELODIES_OF_LIFE\n#endif\n#ifndef EYES_ON_ME\n#    define EYES_ON_ME\n#endif\n#ifndef SONG_OF_THE_ANCIENTS\n#    define SONG_OF_THE_ANCIENTS\n#endif\n#ifndef NIER_AMUSEMENT_PARK\n#    define NIER_AMUSEMENT_PARK\n#endif\n#ifndef COPIED_CITY\n#    define COPIED_CITY\n#endif\n#ifndef VAGUE_HOPE_COLD_RAIN\n#    define VAGUE_HOPE_COLD_RAIN\n#endif\n#ifndef KAINE_SALVATION\n#    define KAINE_SALVATION\n#endif\n#ifndef WEIGHT_OF_THE_WORLD\n#    define WEIGHT_OF_THE_WORLD\n#endif\n#ifndef ISABELLAS_LULLABY\n#    define ISABELLAS_LULLABY\n#endif\n#ifndef TERRAS_THEME\n#    define TERRAS_THEME\n#endif\n#ifndef RENAI_CIRCULATION\n#    define RENAI_CIRCULATION\n#endif\n#ifndef PLATINUM_DISCO\n#    define PLATINUM_DISCO\n#endif\n#ifndef LP_NUMB\n#    define LP_NUMB\n#endif\n"
  },
  {
    "path": "quantum/audio/voices.c",
    "content": "/* Copyright 2016 Jack Humbert\n * Copyright 2020 JohSchneider\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"voices.h\"\n#include \"audio.h\"\n#include \"timer.h\"\n#include <stdlib.h>\n#include <math.h>\n\nuint8_t note_timbre      = TIMBRE_DEFAULT;\nbool    glissando        = false;\nbool    vibrato          = false;\nfloat   vibrato_strength = 0.5;\nfloat   vibrato_rate     = 0.125;\n\nuint16_t voices_timer = 0;\n\n#ifdef AUDIO_VOICE_DEFAULT\nvoice_type voice = AUDIO_VOICE_DEFAULT;\n#else\nvoice_type voice = default_voice;\n#endif\n\nvoid set_voice(voice_type v) {\n    voice = v;\n}\n\nvoid voice_iterate(void) {\n    voice = (voice + 1) % number_of_voices;\n}\n\nvoid voice_deiterate(void) {\n    voice = (voice - 1 + number_of_voices) % number_of_voices;\n}\n\n#ifdef AUDIO_VOICES\nfloat mod(float a, int b) {\n    float r = fmod(a, b);\n    return r < 0 ? r + b : r;\n}\n\n// Effect: 'vibrate' a given target frequency slightly above/below its initial value\nfloat voice_add_vibrato(float average_freq) {\n    float vibrato_counter = mod(timer_read() / (100 * vibrato_rate), VIBRATO_LUT_LENGTH);\n\n    return average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength);\n}\n\n// Effect: 'slides' the 'frequency' from the starting-point, to the target frequency\nfloat voice_add_glissando(float from_freq, float to_freq) {\n    if (to_freq != 0 && from_freq < to_freq && from_freq < to_freq * pow(2, -440 / to_freq / 12 / 2)) {\n        return from_freq * pow(2, 440 / from_freq / 12 / 2);\n    } else if (to_freq != 0 && from_freq > to_freq && from_freq > to_freq * pow(2, 440 / to_freq / 12 / 2)) {\n        return from_freq * pow(2, -440 / from_freq / 12 / 2);\n    } else {\n        return to_freq;\n    }\n}\n#endif\n\nfloat voice_envelope(float frequency) {\n    // envelope_index ranges from 0 to 0xFFFF, which is preserved at 880.0 Hz\n//    __attribute__((unused)) uint16_t compensated_index = (uint16_t)((float)envelope_index * (880.0 / frequency));\n#ifdef AUDIO_VOICES\n    uint16_t envelope_index    = timer_elapsed(voices_timer); // TODO: multiply in some factor?\n    uint16_t compensated_index = envelope_index / 100;        // TODO: correct factor would be?\n#endif\n\n    switch (voice) {\n        case default_voice:\n            glissando = false;\n            // note_timbre    = TIMBRE_50; //Note: leave the user the possibility to adjust the timbre with 'audio_set_timbre'\n            break;\n\n#ifdef AUDIO_VOICES\n\n        case vibrating:\n            glissando = false;\n            vibrato   = true;\n            break;\n\n        case something:\n            glissando = false;\n            switch (compensated_index) {\n                case 0 ... 9:\n                    note_timbre = TIMBRE_12;\n                    break;\n\n                case 10 ... 19:\n                    note_timbre = TIMBRE_25;\n                    break;\n\n                case 20 ... 200:\n                    note_timbre = 12 + 12;\n                    break;\n\n                default:\n                    note_timbre = 12;\n                    break;\n            }\n            break;\n\n        case drums:\n            glissando = false;\n            // switch (compensated_index) {\n            //     case 0 ... 10:\n            //         note_timbre = 50;\n            //         break;\n            //     case 11 ... 20:\n            //         note_timbre = 50 * (21 - compensated_index) / 10;\n            //         break;\n            //     default:\n            //         note_timbre = 0;\n            //         break;\n            // }\n            // frequency = (rand() % (int)(frequency * 1.2 - frequency)) + (frequency * 0.8);\n\n            if (frequency < 80.0) {\n            } else if (frequency < 160.0) {\n                // Bass drum: 60 - 100 Hz\n                frequency = (rand() % (int)(40)) + 60;\n                switch (envelope_index) {\n                    case 0 ... 10:\n                        note_timbre = 50;\n                        break;\n                    case 11 ... 20:\n                        note_timbre = 50 * (21 - envelope_index) / 10;\n                        break;\n                    default:\n                        note_timbre = 0;\n                        break;\n                }\n\n            } else if (frequency < 320.0) {\n                // Snare drum: 1 - 2 KHz\n                frequency = (rand() % (int)(1000)) + 1000;\n                switch (envelope_index) {\n                    case 0 ... 5:\n                        note_timbre = 50;\n                        break;\n                    case 6 ... 20:\n                        note_timbre = 50 * (21 - envelope_index) / 15;\n                        break;\n                    default:\n                        note_timbre = 0;\n                        break;\n                }\n\n            } else if (frequency < 640.0) {\n                // Closed Hi-hat: 3 - 5 KHz\n                frequency = (rand() % (int)(2000)) + 3000;\n                switch (envelope_index) {\n                    case 0 ... 15:\n                        note_timbre = 50;\n                        break;\n                    case 16 ... 20:\n                        note_timbre = 50 * (21 - envelope_index) / 5;\n                        break;\n                    default:\n                        note_timbre = 0;\n                        break;\n                }\n\n            } else if (frequency < 1280.0) {\n                // Open Hi-hat: 3 - 5 KHz\n                frequency = (rand() % (int)(2000)) + 3000;\n                switch (envelope_index) {\n                    case 0 ... 35:\n                        note_timbre = 50;\n                        break;\n                    case 36 ... 50:\n                        note_timbre = 50 * (51 - envelope_index) / 15;\n                        break;\n                    default:\n                        note_timbre = 0;\n                        break;\n                }\n            }\n            break;\n        case butts_fader:\n            glissando = true;\n            switch (compensated_index) {\n                case 0 ... 9:\n                    frequency   = frequency / 4;\n                    note_timbre = TIMBRE_12;\n                    break;\n\n                case 10 ... 19:\n                    frequency   = frequency / 2;\n                    note_timbre = TIMBRE_12;\n                    break;\n\n                case 20 ... 200:\n                    note_timbre = 12 - (uint8_t)(pow(((float)compensated_index - 20) / (200 - 20), 2) * 12.5);\n                    break;\n\n                default:\n                    note_timbre = 0;\n                    break;\n            }\n            break;\n\n            // case octave_crunch:\n            //     switch (compensated_index) {\n            //         case 0 ... 9:\n            //         case 20 ... 24:\n            //         case 30 ... 32:\n            //             frequency = frequency / 2;\n            //             note_timbre = TIMBRE_12;\n            //         break;\n\n            //         case 10 ... 19:\n            //         case 25 ... 29:\n            //         case 33 ... 35:\n            //             frequency = frequency * 2;\n            //             note_timbre = TIMBRE_12;\n            //          break;\n\n            //         default:\n            //             note_timbre = TIMBRE_12;\n            //          break;\n            //     }\n            //  break;\n\n        case duty_osc:\n            // This slows the loop down a substantial amount, so higher notes may freeze\n            glissando = true;\n            switch (compensated_index) {\n                default:\n#    define OCS_SPEED 10\n#    define OCS_AMP .25\n                    // sine wave is slow\n                    // note_timbre = (sin((float)compensated_index/10000*OCS_SPEED) * OCS_AMP / 2) + .5;\n                    // triangle wave is a bit faster\n                    note_timbre = (uint8_t)abs((compensated_index * OCS_SPEED % 3000) - 1500) * (OCS_AMP / 1500) + (1 - OCS_AMP) / 2;\n                    break;\n            }\n            break;\n\n        case duty_octave_down:\n            glissando   = true;\n            note_timbre = (uint8_t)(100 * (envelope_index % 2) * .125 + .375 * 2);\n            if ((envelope_index % 4) == 0) note_timbre = 50;\n            if ((envelope_index % 8) == 0) note_timbre = 0;\n            break;\n        case delayed_vibrato:\n            glissando   = true;\n            note_timbre = TIMBRE_50;\n#    define VOICE_VIBRATO_DELAY 150\n#    define VOICE_VIBRATO_SPEED 50\n            switch (compensated_index) {\n                case 0 ... VOICE_VIBRATO_DELAY:\n                    break;\n                default:\n                    // TODO: merge/replace with voice_add_vibrato above\n                    frequency = frequency * vibrato_lut[(int)fmod((((float)compensated_index - (VOICE_VIBRATO_DELAY + 1)) / 1000 * VOICE_VIBRATO_SPEED), VIBRATO_LUT_LENGTH)];\n                    break;\n            }\n            break;\n            // case delayed_vibrato_octave:\n            //     if ((envelope_index % 2) == 1) {\n            //         note_timbre = 55;\n            //     } else {\n            //         note_timbre = 45;\n            //     }\n            //     #define VOICE_VIBRATO_DELAY 150\n            //     #define VOICE_VIBRATO_SPEED 50\n            //     switch (compensated_index) {\n            //         case 0 ... VOICE_VIBRATO_DELAY:\n            //             break;\n            //         default:\n            //             frequency = frequency * VIBRATO_LUT[(int)fmod((((float)compensated_index - (VOICE_VIBRATO_DELAY + 1))/1000*VOICE_VIBRATO_SPEED), VIBRATO_LUT_LENGTH)];\n            //             break;\n            //     }\n            //     break;\n            // case duty_fifth_down:\n            //     note_timbre = TIMBRE_50;\n            //     if ((envelope_index % 3) == 0)\n            //         note_timbre = TIMBRE_75;\n            //     break;\n            // case duty_fourth_down:\n            //     note_timbre = 0;\n            //     if ((envelope_index % 12) == 0)\n            //         note_timbre = TIMBRE_75;\n            //     if (((envelope_index % 12) % 4) != 1)\n            //         note_timbre = TIMBRE_75;\n            //     break;\n            // case duty_third_down:\n            //     note_timbre = TIMBRE_50;\n            //     if ((envelope_index % 5) == 0)\n            //         note_timbre = TIMBRE_75;\n            //     break;\n            // case duty_fifth_third_down:\n            //     note_timbre = TIMBRE_50;\n            //     if ((envelope_index % 5) == 0)\n            //         note_timbre = TIMBRE_75;\n            //     if ((envelope_index % 3) == 0)\n            //         note_timbre = TIMBRE_25;\n            //     break;\n\n#endif // AUDIO_VOICES\n\n        default:\n            break;\n    }\n\n#ifdef AUDIO_VOICES\n    if (vibrato && (vibrato_strength > 0)) {\n        frequency = voice_add_vibrato(frequency);\n    }\n\n    if (glissando) {\n        // TODO: where to keep track of the start-frequency?\n        // frequency = voice_add_glissando(??, frequency);\n    }\n#endif // AUDIO_VOICES\n\n    return frequency;\n}\n\n// Vibrato functions\n\nvoid voice_set_vibrato_rate(float rate) {\n    vibrato_rate = rate;\n}\nvoid voice_increase_vibrato_rate(float change) {\n    vibrato_rate *= change;\n}\nvoid voice_decrease_vibrato_rate(float change) {\n    vibrato_rate /= change;\n}\nvoid voice_set_vibrato_strength(float strength) {\n    vibrato_strength = strength;\n}\nvoid voice_increase_vibrato_strength(float change) {\n    vibrato_strength *= change;\n}\nvoid voice_decrease_vibrato_strength(float change) {\n    vibrato_strength /= change;\n}\n\n// Timbre functions\n\nvoid voice_set_timbre(uint8_t timbre) {\n    if ((timbre > 0) && (timbre < 100)) {\n        note_timbre = timbre;\n    }\n}\nuint8_t voice_get_timbre(void) {\n    return note_timbre;\n}\n"
  },
  {
    "path": "quantum/audio/voices.h",
    "content": "/* Copyright 2016 Jack Humbert\n * Copyright 2020 JohSchneider\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"wait.h\"\n#include \"luts.h\"\n\nfloat voice_envelope(float frequency);\n\ntypedef enum {\n    default_voice,\n#ifdef AUDIO_VOICES\n    vibrating,\n    something,\n    drums,\n    butts_fader,\n    octave_crunch,\n    duty_osc,\n    duty_octave_down,\n    delayed_vibrato,\n// delayed_vibrato_octave,\n// duty_fifth_down,\n// duty_fourth_down,\n// duty_third_down,\n// duty_fifth_third_down,\n#endif\n    number_of_voices // important that this is last\n} voice_type;\n\nvoid set_voice(voice_type v);\nvoid voice_iterate(void);\nvoid voice_deiterate(void);\n\n// Vibrato functions\nvoid voice_set_vibrato_rate(float rate);\nvoid voice_increase_vibrato_rate(float change);\nvoid voice_decrease_vibrato_rate(float change);\nvoid voice_set_vibrato_strength(float strength);\nvoid voice_increase_vibrato_strength(float change);\nvoid voice_decrease_vibrato_strength(float change);\n\n// Timbre functions\n/**\n * @brief set the global timbre for tones to be played\n * @note: only applies to pwm implementations - where it adjusts the duty-cycle\n * @note: using any instrument from voices.[ch] other than 'default' may override the set value\n * @param[in]: timbre: valid range is (0,100)\n */\nvoid    voice_set_timbre(uint8_t timbre);\nuint8_t voice_get_timbre(void);\n"
  },
  {
    "path": "quantum/backlight/backlight.c",
    "content": "/*\nCopyright 2013 Mathias Andersson <wraul@dbox.se>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"backlight.h\"\n#include \"eeconfig.h\"\n#include \"debug.h\"\n\nbacklight_config_t backlight_config;\n\n#ifndef BACKLIGHT_DEFAULT_ON\n#    define BACKLIGHT_DEFAULT_ON true\n#endif\n\n#ifndef BACKLIGHT_DEFAULT_LEVEL\n#    define BACKLIGHT_DEFAULT_LEVEL BACKLIGHT_LEVELS\n#endif\n\n#ifndef BACKLIGHT_DEFAULT_BREATHING\n#    define BACKLIGHT_DEFAULT_BREATHING false\n#else\n#    undef BACKLIGHT_DEFAULT_BREATHING\n#    define BACKLIGHT_DEFAULT_BREATHING true\n#endif\n\n#ifdef BACKLIGHT_BREATHING\n// TODO: migrate to backlight_config_t\nstatic uint8_t breathing_period = BREATHING_PERIOD;\n#endif\n\nstatic void backlight_check_config(void) {\n    /* Add some out of bound checks for backlight config */\n\n    if (backlight_config.level > BACKLIGHT_LEVELS) {\n        backlight_config.level = BACKLIGHT_LEVELS;\n    }\n}\n\n/** \\brief Backlight initialization\n *\n * FIXME: needs doc\n */\nvoid backlight_init(void) {\n    eeconfig_read_backlight(&backlight_config);\n    if (!backlight_config.valid) {\n        dprintf(\"backlight_init backlight_config.valid = 0. Write default values to EEPROM.\\n\");\n        eeconfig_update_backlight_default();\n    }\n    backlight_check_config();\n\n    backlight_set(backlight_config.enable ? backlight_config.level : 0);\n}\n\n/** \\brief Backlight increase\n *\n * FIXME: needs doc\n */\nvoid backlight_increase(void) {\n    if (backlight_config.level < BACKLIGHT_LEVELS) {\n        backlight_config.level++;\n    }\n    backlight_config.enable = 1;\n    eeconfig_update_backlight(&backlight_config);\n    dprintf(\"backlight increase: %u\\n\", backlight_config.level);\n    backlight_set(backlight_config.level);\n}\n\n/** \\brief Backlight decrease\n *\n * FIXME: needs doc\n */\nvoid backlight_decrease(void) {\n    if (backlight_config.level > 0) {\n        backlight_config.level--;\n        backlight_config.enable = !!backlight_config.level;\n        eeconfig_update_backlight(&backlight_config);\n    }\n    dprintf(\"backlight decrease: %u\\n\", backlight_config.level);\n    backlight_set(backlight_config.level);\n}\n\n/** \\brief Backlight toggle\n *\n * FIXME: needs doc\n */\nvoid backlight_toggle(void) {\n    bool enabled = backlight_config.enable;\n    dprintf(\"backlight toggle: %u\\n\", enabled);\n    if (enabled)\n        backlight_disable();\n    else\n        backlight_enable();\n}\n\n/** \\brief Enable backlight\n *\n * FIXME: needs doc\n */\nvoid backlight_enable(void) {\n    if (backlight_config.enable) return; // do nothing if backlight is already on\n\n    backlight_config.enable = true;\n    if (backlight_config.raw == 1) // enabled but level == 0\n        backlight_config.level = 1;\n    eeconfig_update_backlight(&backlight_config);\n    dprintf(\"backlight enable\\n\");\n    backlight_set(backlight_config.level);\n}\n\n/** \\brief Disable backlight\n *\n * FIXME: needs doc\n */\nvoid backlight_disable(void) {\n    if (!backlight_config.enable) return; // do nothing if backlight is already off\n\n    backlight_config.enable = false;\n    eeconfig_update_backlight(&backlight_config);\n    dprintf(\"backlight disable\\n\");\n    backlight_set(0);\n}\n\n/** /brief Get the backlight status\n *\n * FIXME: needs doc\n */\nbool is_backlight_enabled(void) {\n    return backlight_config.enable;\n}\n\n/** \\brief Backlight step through levels\n *\n * FIXME: needs doc\n */\nvoid backlight_step(void) {\n    backlight_config.level++;\n    if (backlight_config.level > BACKLIGHT_LEVELS) {\n        backlight_config.level = 0;\n    }\n    backlight_config.enable = !!backlight_config.level;\n    eeconfig_update_backlight(&backlight_config);\n    dprintf(\"backlight step: %u\\n\", backlight_config.level);\n    backlight_set(backlight_config.level);\n}\n\n/** \\brief Backlight set level without EEPROM update\n *\n */\nvoid backlight_level_noeeprom(uint8_t level) {\n    if (level > BACKLIGHT_LEVELS) level = BACKLIGHT_LEVELS;\n    backlight_config.level  = level;\n    backlight_config.enable = !!backlight_config.level;\n    backlight_set(backlight_config.level);\n}\n\n/** \\brief Backlight set level\n *\n * FIXME: needs doc\n */\nvoid backlight_level(uint8_t level) {\n    backlight_level_noeeprom(level);\n    eeconfig_update_backlight(&backlight_config);\n}\n\nvoid eeconfig_update_backlight_current(void) {\n    eeconfig_update_backlight(&backlight_config);\n}\n\nvoid eeconfig_update_backlight_default(void) {\n    backlight_config.valid     = true;\n    backlight_config.enable    = BACKLIGHT_DEFAULT_ON;\n    backlight_config.breathing = BACKLIGHT_DEFAULT_BREATHING;\n    backlight_config.level     = BACKLIGHT_DEFAULT_LEVEL;\n    eeconfig_update_backlight(&backlight_config);\n}\n\n/** \\brief Get backlight level\n *\n * FIXME: needs doc\n */\nuint8_t get_backlight_level(void) {\n    return backlight_config.level;\n}\n\n#ifdef BACKLIGHT_BREATHING\n/** \\brief Backlight breathing toggle\n *\n * FIXME: needs doc\n */\nvoid backlight_toggle_breathing(void) {\n    bool breathing = backlight_config.breathing;\n    dprintf(\"backlight breathing toggle: %u\\n\", breathing);\n    if (breathing)\n        backlight_disable_breathing();\n    else\n        backlight_enable_breathing();\n}\n\n/** \\brief Enable backlight breathing\n *\n * FIXME: needs doc\n */\nvoid backlight_enable_breathing(void) {\n    if (backlight_config.breathing) return; // do nothing if breathing is already on\n\n    backlight_config.breathing = true;\n    eeconfig_update_backlight(&backlight_config);\n    dprintf(\"backlight breathing enable\\n\");\n    breathing_enable();\n}\n\n/** \\brief Disable backlight breathing\n *\n * FIXME: needs doc\n */\nvoid backlight_disable_breathing(void) {\n    if (!backlight_config.breathing) return; // do nothing if breathing is already off\n\n    backlight_config.breathing = false;\n    eeconfig_update_backlight(&backlight_config);\n    dprintf(\"backlight breathing disable\\n\");\n    breathing_disable();\n}\n\n/** \\brief Get the backlight breathing status\n *\n * FIXME: needs doc\n */\nbool is_backlight_breathing(void) {\n    return backlight_config.breathing;\n}\n\n// following are marked as weak purely for backwards compatibility\n__attribute__((weak)) void breathing_period_set(uint8_t value) {\n    breathing_period = value ? value : 1;\n}\n\n__attribute__((weak)) uint8_t get_breathing_period(void) {\n    return breathing_period;\n}\n\n__attribute__((weak)) void breathing_period_default(void) {\n    breathing_period_set(BREATHING_PERIOD);\n}\n\n__attribute__((weak)) void breathing_period_inc(void) {\n    breathing_period_set(breathing_period + 1);\n}\n\n__attribute__((weak)) void breathing_period_dec(void) {\n    breathing_period_set(breathing_period - 1);\n}\n\n__attribute__((weak)) void breathing_toggle(void) {\n    if (is_breathing())\n        breathing_disable();\n    else\n        breathing_enable();\n}\n\n#endif\n\n// defaults for backlight api\n__attribute__((weak)) void backlight_init_ports(void) {}\n\n__attribute__((weak)) void backlight_set(uint8_t level) {}\n\n__attribute__((weak)) void backlight_task(void) {}\n"
  },
  {
    "path": "quantum/backlight/backlight.h",
    "content": "/*\nCopyright 2013 Mathias Andersson <wraul@dbox.se>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"compiler_support.h\"\n\n#ifndef BACKLIGHT_LEVELS\n#    define BACKLIGHT_LEVELS 3\n#elif BACKLIGHT_LEVELS > 31\n#    error \"Maximum value of BACKLIGHT_LEVELS is 31\"\n#endif\n\n#ifndef BACKLIGHT_ON_STATE\n#    define BACKLIGHT_ON_STATE 1\n#endif\n\n#ifndef BREATHING_PERIOD\n#    define BREATHING_PERIOD 6\n#endif\n\ntypedef union backlight_config_t {\n    uint8_t raw;\n    struct {\n        bool    enable : 1;\n        bool    breathing : 1;\n        bool    valid : 1;\n        uint8_t level : 5;\n    };\n} backlight_config_t;\n\nSTATIC_ASSERT(sizeof(backlight_config_t) == sizeof(uint8_t), \"Backlight EECONFIG out of spec.\");\n\nvoid    backlight_init(void);\nvoid    backlight_toggle(void);\nvoid    backlight_enable(void);\nvoid    backlight_disable(void);\nbool    is_backlight_enabled(void);\nvoid    backlight_step(void);\nvoid    backlight_increase(void);\nvoid    backlight_decrease(void);\nvoid    backlight_level_noeeprom(uint8_t level);\nvoid    backlight_level(uint8_t level);\nuint8_t get_backlight_level(void);\n\nvoid eeconfig_update_backlight_current(void);\nvoid eeconfig_update_backlight_default(void);\n\n// implementation specific\nvoid backlight_init_ports(void);\nvoid backlight_set(uint8_t level);\nvoid backlight_task(void);\n\n#ifdef BACKLIGHT_BREATHING\n\nvoid backlight_toggle_breathing(void);\nvoid backlight_enable_breathing(void);\nvoid backlight_disable_breathing(void);\nbool is_backlight_breathing(void);\n\nvoid    breathing_period_set(uint8_t value);\nuint8_t get_breathing_period(void);\nvoid    breathing_period_default(void);\nvoid    breathing_period_inc(void);\nvoid    breathing_period_dec(void);\n\nvoid breathing_toggle(void);\n\n// implementation specific\nvoid breathing_enable(void);\nvoid breathing_disable(void);\nbool is_breathing(void);\nvoid breathing_pulse(void);\n#endif\n"
  },
  {
    "path": "quantum/backlight/backlight_driver_common.c",
    "content": "#include \"backlight.h\"\n#include \"backlight_driver_common.h\"\n#include \"gpio.h\"\n#include \"util.h\"\n\n#if !defined(BACKLIGHT_PIN) && !defined(BACKLIGHT_PINS)\n#    error \"Backlight pin/pins not defined. Please configure.\"\n#endif\n\n#if defined(BACKLIGHT_PINS)\nstatic const pin_t backlight_pins[] = BACKLIGHT_PINS;\n#    ifndef BACKLIGHT_LED_COUNT\n#        define BACKLIGHT_LED_COUNT ARRAY_SIZE(backlight_pins)\n#    endif\n\n#    define FOR_EACH_LED(x)                                 \\\n        for (uint8_t i = 0; i < BACKLIGHT_LED_COUNT; i++) { \\\n            pin_t backlight_pin = backlight_pins[i];        \\\n            { x }                                           \\\n        }\n#else\n// we support only one backlight pin\nstatic const pin_t backlight_pin = BACKLIGHT_PIN;\n#    define FOR_EACH_LED(x) x\n#endif\n\nstatic inline void backlight_on(pin_t backlight_pin) {\n#if BACKLIGHT_ON_STATE == 0\n    gpio_write_pin_low(backlight_pin);\n#else\n    gpio_write_pin_high(backlight_pin);\n#endif\n}\n\nstatic inline void backlight_off(pin_t backlight_pin) {\n#if BACKLIGHT_ON_STATE == 0\n    gpio_write_pin_high(backlight_pin);\n#else\n    gpio_write_pin_low(backlight_pin);\n#endif\n}\n\nvoid backlight_pins_init(void) {\n    // Setup backlight pin as output and output to off state.\n    FOR_EACH_LED(gpio_set_pin_output(backlight_pin); backlight_off(backlight_pin);)\n}\n\nvoid backlight_pins_on(void) {\n    FOR_EACH_LED(backlight_on(backlight_pin);)\n}\n\nvoid backlight_pins_off(void) {\n    FOR_EACH_LED(backlight_off(backlight_pin);)\n}\n"
  },
  {
    "path": "quantum/backlight/backlight_driver_common.h",
    "content": "#pragma once\n\nvoid backlight_pins_init(void);\nvoid backlight_pins_on(void);\nvoid backlight_pins_off(void);\n\nvoid breathing_task(void);\n"
  },
  {
    "path": "quantum/basic_profiling.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n/*\n    This API allows for basic profiling information to be printed out over console.\n\n    Usage example:\n\n        #include \"basic_profiling.h\"\n\n        // Original code:\n        matrix_task();\n\n        // Delete the original, replace with the following (variant 1, automatic naming):\n        PROFILE_CALL(1000, matrix_task());\n\n        // Delete the original, replace with the following (variant 2, explicit naming):\n        PROFILE_CALL_NAMED(1000, \"matrix_task\", {\n            matrix_task();\n        });\n*/\n\n#if defined(PROTOCOL_LUFA) || defined(PROTOCOL_VUSB)\n#    define TIMESTAMP_GETTER TCNT0\n#elif defined(PROTOCOL_CHIBIOS)\n#    define TIMESTAMP_GETTER chSysGetRealtimeCounterX()\n#else\n#    error Unknown protocol in use\n#endif\n\n#ifndef CONSOLE_ENABLE\n// Can't do anything if we don't have console output enabled.\n#    define PROFILE_CALL_NAMED(count, name, call) \\\n        do {                                      \\\n        } while (0)\n#else\n#    define PROFILE_CALL_NAMED(count, name, call)                                                                         \\\n        do {                                                                                                              \\\n            static uint64_t inner_sum = 0;                                                                                \\\n            static uint64_t outer_sum = 0;                                                                                \\\n            uint32_t        start_ts;                                                                                     \\\n            static uint32_t end_ts;                                                                                       \\\n            static uint32_t write_location = 0;                                                                           \\\n            start_ts                       = TIMESTAMP_GETTER;                                                            \\\n            if (write_location > 0) {                                                                                     \\\n                outer_sum += start_ts - end_ts;                                                                           \\\n            }                                                                                                             \\\n            do {                                                                                                          \\\n                call;                                                                                                     \\\n            } while (0);                                                                                                  \\\n            end_ts = TIMESTAMP_GETTER;                                                                                    \\\n            inner_sum += end_ts - start_ts;                                                                               \\\n            ++write_location;                                                                                             \\\n            if (write_location >= ((uint32_t)count)) {                                                                    \\\n                uint32_t inner_avg = inner_sum / (((uint32_t)count) - 1);                                                 \\\n                uint32_t outer_avg = outer_sum / (((uint32_t)count) - 1);                                                 \\\n                dprintf(\"%s -- Percentage time spent: %d%%\\n\", (name), (int)(inner_avg * 100 / (inner_avg + outer_avg))); \\\n                inner_sum      = 0;                                                                                       \\\n                outer_sum      = 0;                                                                                       \\\n                write_location = 0;                                                                                       \\\n            }                                                                                                             \\\n        } while (0)\n\n#endif // CONSOLE_ENABLE\n\n#define PROFILE_CALL(count, call) PROFILE_CALL_NAMED(count, #call, call)\n"
  },
  {
    "path": "quantum/bits.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 */\n\n#pragma once\n\n#include <stdint.h>\n\n/* Remove these once we transitioned to C23 across all platforms */\n#define UINT32_WIDTH 32\n#define UINT64_WIDTH 64\n\n/**\n *  @brief Mask for the little endian nth bit (0-31) in a 32-bit integer.\n */\n#define BIT32(n) (UINT32_C(1) << (n))\n\n/**\n * @brief Mask for the little endian nth bit (0-63) in a 64-bit integer.\n */\n#define BIT64(n) (UINT64_C(1) << (n))\n\n/**\n * @brief Create a contiguous 32-bit wide bitmask starting at bit position @l\n * and ending at position @h. The range is inclusive, meaning GENMASK32(20, 10)\n * gives us the 32-bit mask 0x001ffc00.\n */\n#define GENMASK32(h, l) (((~UINT32_C(0)) - (UINT32_C(1) << (l)) + 1) & (~UINT32_C(0) >> (UINT32_WIDTH - 1 - (h))))\n\n/**\n * @brief Create a contiguous 64-bit wide bitmask starting at bit position @l\n * and ending at position @h. The range is inclusive, meaning GENMASK64(39, 21)\n * gives us the 64-bit mask 0x000000ffffe00000.\n */\n#define GENMASK64(h, l) (((~UINT64_C(0)) - (UINT64_C(1) << (l)) + 1) & (~UINT64_C(0) >> (UINT64_WIDTH - 1 - (h))))\n"
  },
  {
    "path": "quantum/bitwise.c",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"util.h\"\n\n// bit population - return number of on-bit\n__attribute__((noinline)) uint8_t bitpop(uint8_t bits) {\n    uint8_t c;\n    for (c = 0; bits; c++)\n        bits &= bits - 1;\n    return c;\n    /*\n        const uint8_t bit_count[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };\n        return bit_count[bits>>4] + bit_count[bits&0x0F]\n    */\n}\n\nuint8_t bitpop16(uint16_t bits) {\n    uint8_t c;\n    for (c = 0; bits; c++)\n        bits &= bits - 1;\n    return c;\n}\n\nuint8_t bitpop32(uint32_t bits) {\n    uint8_t c;\n    for (c = 0; bits; c++)\n        bits &= bits - 1;\n    return c;\n}\n\n// most significant on-bit - return highest location of on-bit\n// NOTE: return 0 when bit0 is on or all bits are off\n__attribute__((noinline)) uint8_t biton(uint8_t bits) {\n    uint8_t n = 0;\n    if (bits >> 4) {\n        bits >>= 4;\n        n += 4;\n    }\n    if (bits >> 2) {\n        bits >>= 2;\n        n += 2;\n    }\n    if (bits >> 1) {\n        bits >>= 1;\n        n += 1;\n    }\n    return n;\n}\n\nuint8_t biton16(uint16_t bits) {\n    uint8_t n = 0;\n    if (bits >> 8) {\n        bits >>= 8;\n        n += 8;\n    }\n    if (bits >> 4) {\n        bits >>= 4;\n        n += 4;\n    }\n    if (bits >> 2) {\n        bits >>= 2;\n        n += 2;\n    }\n    if (bits >> 1) {\n        bits >>= 1;\n        n += 1;\n    }\n    return n;\n}\n\nuint8_t biton32(uint32_t bits) {\n    uint8_t n = 0;\n    if (bits >> 16) {\n        bits >>= 16;\n        n += 16;\n    }\n    if (bits >> 8) {\n        bits >>= 8;\n        n += 8;\n    }\n    if (bits >> 4) {\n        bits >>= 4;\n        n += 4;\n    }\n    if (bits >> 2) {\n        bits >>= 2;\n        n += 2;\n    }\n    if (bits >> 1) {\n        bits >>= 1;\n        n += 1;\n    }\n    return n;\n}\n\n__attribute__((noinline)) uint8_t bitrev(uint8_t bits) {\n    bits = (bits & 0x0f) << 4 | (bits & 0xf0) >> 4;\n    bits = (bits & 0b00110011) << 2 | (bits & 0b11001100) >> 2;\n    bits = (bits & 0b01010101) << 1 | (bits & 0b10101010) >> 1;\n    return bits;\n}\n\nuint16_t bitrev16(uint16_t bits) {\n    bits = bitrev(bits & 0x00ff) << 8 | bitrev((bits & 0xff00) >> 8);\n    return bits;\n}\n\nuint32_t bitrev32(uint32_t bits) {\n    bits = (uint32_t)bitrev16(bits & 0x0000ffff) << 16 | bitrev16((bits & 0xffff0000) >> 16);\n    return bits;\n}\n"
  },
  {
    "path": "quantum/bitwise.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nuint8_t bitpop(uint8_t bits);\nuint8_t bitpop16(uint16_t bits);\nuint8_t bitpop32(uint32_t bits);\n\nuint8_t biton(uint8_t bits);\nuint8_t biton16(uint16_t bits);\nuint8_t biton32(uint32_t bits);\n\nuint8_t  bitrev(uint8_t bits);\nuint16_t bitrev16(uint16_t bits);\nuint32_t bitrev32(uint32_t bits);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/bootmagic/bootmagic.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"bootmagic.h\"\n#include \"matrix.h\"\n#include \"keyboard.h\"\n#include \"wait.h\"\n#include \"eeconfig.h\"\n#include \"bootloader.h\"\n\n#ifndef BOOTMAGIC_DEBOUNCE\n#    if defined(DEBOUNCE) && DEBOUNCE > 0\n#        define BOOTMAGIC_DEBOUNCE (DEBOUNCE * 2)\n#    else\n#        define BOOTMAGIC_DEBOUNCE 30\n#    endif\n#endif\n\n/** \\brief Reset eeprom\n *\n * ...just incase someone wants to only change the eeprom behaviour\n */\n__attribute__((weak)) void bootmagic_reset_eeprom(void) {\n    eeconfig_disable();\n}\n\n/** \\brief Decide reboot based on current matrix state\n */\n__attribute__((weak)) bool bootmagic_should_reset(void) {\n    // If the configured key (commonly Esc) is held down on power up,\n    // reset the EEPROM valid state and jump to bootloader.\n    // This isn't very generalized, but we need something that doesn't\n    // rely on user's keymaps in firmware or EEPROM.\n    uint8_t row = BOOTMAGIC_ROW;\n    uint8_t col = BOOTMAGIC_COLUMN;\n\n#if defined(SPLIT_KEYBOARD) && defined(BOOTMAGIC_ROW_RIGHT) && defined(BOOTMAGIC_COLUMN_RIGHT)\n    if (!is_keyboard_left()) {\n        row = BOOTMAGIC_ROW_RIGHT;\n        col = BOOTMAGIC_COLUMN_RIGHT;\n    }\n#endif\n\n    return matrix_get_row(row) & (1 << col);\n}\n\n/** \\brief The abridged version of TMK's bootmagic based on Wilba.\n *\n *  100% less potential for accidentally making the keyboard do stupid things.\n */\n__attribute__((weak)) void bootmagic_scan(void) {\n    // We need multiple scans because debouncing can't be turned off.\n    matrix_scan();\n    wait_ms(BOOTMAGIC_DEBOUNCE);\n    matrix_scan();\n\n    if (bootmagic_should_reset()) {\n        bootmagic_reset_eeprom();\n\n        // Jump to bootloader.\n        bootloader_jump();\n    }\n}\n\nvoid bootmagic(void) {\n    bootmagic_scan();\n}\n"
  },
  {
    "path": "quantum/bootmagic/bootmagic.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n// ======== DEPRECATED DEFINES - DO NOT USE ========\n#ifdef BOOTMAGIC_LITE_ROW\n#    define BOOTMAGIC_ROW BOOTMAGIC_LITE_ROW\n#endif\n#ifdef BOOTMAGIC_LITE_COLUMN\n#    define BOOTMAGIC_COLUMN BOOTMAGIC_LITE_COLUMN\n#endif\n#ifdef BOOTMAGIC_LITE_ROW_RIGHT\n#    define BOOTMAGIC_ROW_RIGHT BOOTMAGIC_LITE_ROW_RIGHT\n#endif\n#ifdef BOOTMAGIC_LITE_COLUMN_RIGHT\n#    define BOOTMAGIC_COLUMN_RIGHT BOOTMAGIC_LITE_COLUMN_RIGHT\n#endif\n// ========\n\n#ifndef BOOTMAGIC_COLUMN\n#    define BOOTMAGIC_COLUMN 0\n#endif\n#ifndef BOOTMAGIC_ROW\n#    define BOOTMAGIC_ROW 0\n#endif\n\nvoid bootmagic(void);\n"
  },
  {
    "path": "quantum/caps_word.c",
    "content": "// Copyright 2021-2022 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <stdint.h>\n#include \"caps_word.h\"\n#include \"timer.h\"\n#include \"action.h\"\n#include \"action_util.h\"\n\n/** @brief True when Caps Word is active. */\nstatic bool caps_word_active = false;\n\n#if CAPS_WORD_IDLE_TIMEOUT > 0\n// Constrain timeout to a sensible range. With 16-bit timers, the longest\n// timeout possible is 32768 ms, rounded here to 30000 ms = half a minute.\n#    if CAPS_WORD_IDLE_TIMEOUT < 100 || CAPS_WORD_IDLE_TIMEOUT > 30000\n#        error \"CAPS_WORD_IDLE_TIMEOUT must be between 100 and 30000 ms\"\n#    endif\n\n/** @brief Deadline for idle timeout. */\nstatic uint16_t idle_timer = 0;\n\nvoid caps_word_task(void) {\n    if (caps_word_active && timer_expired(timer_read(), idle_timer)) {\n        caps_word_off();\n    }\n}\n\nvoid caps_word_reset_idle_timer(void) {\n    idle_timer = timer_read() + CAPS_WORD_IDLE_TIMEOUT;\n}\n#else\nvoid caps_word_task(void) {}\n#endif // CAPS_WORD_IDLE_TIMEOUT > 0\n\nvoid caps_word_on(void) {\n    if (caps_word_active) {\n        return;\n    }\n\n    clear_mods();\n#ifndef NO_ACTION_ONESHOT\n    clear_oneshot_mods();\n#endif // NO_ACTION_ONESHOT\n#if CAPS_WORD_IDLE_TIMEOUT > 0\n    caps_word_reset_idle_timer();\n#endif // CAPS_WORD_IDLE_TIMEOUT > 0\n\n    caps_word_active = true;\n    caps_word_set_user(true);\n}\n\nvoid caps_word_off(void) {\n    if (!caps_word_active) {\n        return;\n    }\n\n    unregister_weak_mods(MOD_MASK_SHIFT); // Make sure weak shift is off.\n    caps_word_active = false;\n    caps_word_set_user(false);\n}\n\nvoid caps_word_toggle(void) {\n    if (caps_word_active) {\n        caps_word_off();\n    } else {\n        caps_word_on();\n    }\n}\n\nbool is_caps_word_on(void) {\n    return caps_word_active;\n}\n\n__attribute__((weak)) void caps_word_set_user(bool active) {}\n"
  },
  {
    "path": "quantum/caps_word.h",
    "content": "// Copyright 2021-2022 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdbool.h>\n\n#ifndef CAPS_WORD_IDLE_TIMEOUT\n#    define CAPS_WORD_IDLE_TIMEOUT 5000 // Default timeout of 5 seconds.\n#endif\n\n/** @brief Matrix scan task for Caps Word feature */\nvoid caps_word_task(void);\n\n#if CAPS_WORD_IDLE_TIMEOUT > 0\n/** @brief Resets timer for Caps Word idle timeout. */\nvoid caps_word_reset_idle_timer(void);\n#endif\n\n/** @brief Activates Caps Word. */\nvoid caps_word_on(void);\n\n/** @brief Deactivates Caps Word. */\nvoid caps_word_off(void);\n\n/** @brief Toggles Caps Word. */\nvoid caps_word_toggle(void);\n\n/** @brief Gets whether currently active. */\nbool is_caps_word_on(void);\n\n/**\n * @brief Caps Word set callback.\n *\n * @param active True if Caps Word is active, false otherwise\n */\nvoid caps_word_set_user(bool active);\n"
  },
  {
    "path": "quantum/color.c",
    "content": "/* Copyright 2017 Jason Williams\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"color.h\"\n#include \"led_tables.h\"\n#include \"progmem.h\"\n#include \"util.h\"\n\nrgb_t hsv_to_rgb_impl(hsv_t hsv, bool use_cie) {\n    rgb_t    rgb;\n    uint8_t  region, remainder, p, q, t;\n    uint16_t h, s, v;\n\n    if (hsv.s == 0) {\n#ifdef USE_CIE1931_CURVE\n        if (use_cie) {\n            rgb.r = rgb.g = rgb.b = pgm_read_byte(&CIE1931_CURVE[hsv.v]);\n        } else {\n            rgb.r = hsv.v;\n            rgb.g = hsv.v;\n            rgb.b = hsv.v;\n        }\n#else\n        rgb.r = hsv.v;\n        rgb.g = hsv.v;\n        rgb.b = hsv.v;\n#endif\n        return rgb;\n    }\n\n    h = hsv.h;\n    s = hsv.s;\n#ifdef USE_CIE1931_CURVE\n    if (use_cie) {\n        v = pgm_read_byte(&CIE1931_CURVE[hsv.v]);\n    } else {\n        v = hsv.v;\n    }\n#else\n    v = hsv.v;\n#endif\n\n    region    = h * 6 / 255;\n    remainder = (h * 2 - region * 85) * 3;\n\n    p = (v * (255 - s)) >> 8;\n    q = (v * (255 - ((s * remainder) >> 8))) >> 8;\n    t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8;\n\n    switch (region) {\n        case 6:\n        case 0:\n            rgb.r = v;\n            rgb.g = t;\n            rgb.b = p;\n            break;\n        case 1:\n            rgb.r = q;\n            rgb.g = v;\n            rgb.b = p;\n            break;\n        case 2:\n            rgb.r = p;\n            rgb.g = v;\n            rgb.b = t;\n            break;\n        case 3:\n            rgb.r = p;\n            rgb.g = q;\n            rgb.b = v;\n            break;\n        case 4:\n            rgb.r = t;\n            rgb.g = p;\n            rgb.b = v;\n            break;\n        default:\n            rgb.r = v;\n            rgb.g = p;\n            rgb.b = q;\n            break;\n    }\n\n    return rgb;\n}\n\nrgb_t hsv_to_rgb(hsv_t hsv) {\n#ifdef USE_CIE1931_CURVE\n    return hsv_to_rgb_impl(hsv, true);\n#else\n    return hsv_to_rgb_impl(hsv, false);\n#endif\n}\n\nrgb_t hsv_to_rgb_nocie(hsv_t hsv) {\n    return hsv_to_rgb_impl(hsv, false);\n}\n"
  },
  {
    "path": "quantum/color.h",
    "content": "/* Copyright 2017 Jason Williams\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"util.h\"\n\n// clang-format off\n\n/*\n * RGB Colors\n */\n#define RGB_AZURE       0x99, 0xF5, 0xFF\n#define RGB_BLACK       0x00, 0x00, 0x00\n#define RGB_BLUE        0x00, 0x00, 0xFF\n#define RGB_CHARTREUSE  0x80, 0xFF, 0x00\n#define RGB_CORAL       0xFF, 0x7C, 0x4D\n#define RGB_CYAN        0x00, 0xFF, 0xFF\n#define RGB_GOLD        0xFF, 0xD9, 0x00\n#define RGB_GOLDENROD   0xD9, 0xA5, 0x21\n#define RGB_GREEN       0x00, 0xFF, 0x00\n#define RGB_MAGENTA     0xFF, 0x00, 0xFF\n#define RGB_ORANGE      0xFF, 0x80, 0x00\n#define RGB_PINK        0xFF, 0x80, 0xBF\n#define RGB_PURPLE      0x7A, 0x00, 0xFF\n#define RGB_RED         0xFF, 0x00, 0x00\n#define RGB_SPRINGGREEN 0x00, 0xFF, 0x80\n#define RGB_TEAL        0x00, 0x80, 0x80\n#define RGB_TURQUOISE   0x47, 0x6E, 0x6A\n#define RGB_WHITE       0xFF, 0xFF, 0xFF\n#define RGB_YELLOW      0xFF, 0xFF, 0x00\n#define RGB_OFF         RGB_BLACK\n\n/*\n * HSV Colors\n *\n * All values (including hue) are scaled to 0-255\n */\n#define HSV_AZURE       132, 102, 255\n#define HSV_BLACK         0,   0,   0\n#define HSV_BLUE        170, 255, 255\n#define HSV_CHARTREUSE   64, 255, 255\n#define HSV_CORAL        11, 176, 255\n#define HSV_CYAN        128, 255, 255\n#define HSV_GOLD         36, 255, 255\n#define HSV_GOLDENROD    30, 218, 218\n#define HSV_GREEN        85, 255, 255\n#define HSV_MAGENTA     213, 255, 255\n#define HSV_ORANGE       21, 255, 255\n#define HSV_PINK        234, 128, 255\n#define HSV_PURPLE      191, 255, 255\n#define HSV_RED           0, 255, 255\n#define HSV_SPRINGGREEN 106, 255, 255\n#define HSV_TEAL        128, 255, 128\n#define HSV_TURQUOISE   123,  90, 112\n#define HSV_WHITE         0,   0, 255\n#define HSV_YELLOW       43, 255, 255\n#define HSV_OFF         HSV_BLACK\n\n// clang-format on\n\ntypedef struct PACKED rgb_t {\n    uint8_t r;\n    uint8_t g;\n    uint8_t b;\n} rgb_t;\n\n// DEPRECATED\ntypedef rgb_t RGB;\ntypedef rgb_t rgb_led_t;\n\ntypedef struct PACKED hsv_t {\n    uint8_t h;\n    uint8_t s;\n    uint8_t v;\n} hsv_t;\n\n// DEPRECATED\ntypedef hsv_t HSV;\n\nrgb_t hsv_to_rgb(hsv_t hsv);\nrgb_t hsv_to_rgb_nocie(hsv_t hsv);\n"
  },
  {
    "path": "quantum/command.c",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n#include <stdint.h>\n#include <stdbool.h>\n#include \"wait.h\"\n#include \"keycode.h\"\n#include \"host.h\"\n#include \"print.h\"\n#include \"debug.h\"\n#include \"util.h\"\n#include \"timer.h\"\n#include \"keyboard.h\"\n#include \"bootloader.h\"\n#include \"action_layer.h\"\n#include \"action_util.h\"\n#include \"eeconfig.h\"\n#include \"sleep_led.h\"\n#include \"led.h\"\n#include \"command.h\"\n#include \"quantum.h\"\n#include \"usb_device_state.h\"\n#include \"version.h\"\n\n#ifdef BACKLIGHT_ENABLE\n#    include \"backlight.h\"\n#endif\n\n#if defined(MOUSEKEY_ENABLE)\n#    include \"mousekey.h\"\n#endif\n\n#ifdef AUDIO_ENABLE\n#    include \"audio.h\"\n#endif /* AUDIO_ENABLE */\n\nstatic bool command_common(uint8_t code);\nstatic void command_common_help(void);\nstatic void print_version(void);\nstatic void print_status(void);\nstatic bool command_console(uint8_t code);\nstatic void command_console_help(void);\n#if defined(MOUSEKEY_ENABLE)\nstatic bool mousekey_console(uint8_t code);\n#endif\n\nstatic void switch_default_layer(uint8_t layer);\n\ncommand_state_t command_state = ONESHOT;\n\nbool command_proc(uint8_t code) {\n    switch (command_state) {\n        case ONESHOT:\n            if (!IS_COMMAND()) return false;\n            return (command_extra(code) || command_common(code));\n            break;\n        case CONSOLE:\n            if (IS_COMMAND())\n                return (command_extra(code) || command_common(code));\n            else\n                return (command_console_extra(code) || command_console(code));\n            break;\n#if defined(MOUSEKEY_ENABLE)\n        case MOUSEKEY:\n            mousekey_console(code);\n            break;\n#endif\n        default:\n            command_state = ONESHOT;\n            return false;\n    }\n    return true;\n}\n\n/* TODO: Refactoring is needed. */\n/* This allows to define extra commands. return false when not processed. */\nbool command_extra(uint8_t code) __attribute__((weak));\nbool command_extra(uint8_t code) {\n    (void)code;\n    return false;\n}\n\nbool command_console_extra(uint8_t code) __attribute__((weak));\nbool command_console_extra(uint8_t code) {\n    (void)code;\n    return false;\n}\n\n/***********************************************************\n * Command common\n ***********************************************************/\n\nstatic void command_common_help(void) {\n    print(/* clang-format off */\n        \"\\n\\t- Magic -\\n\"\n\n        STR(MAGIC_KEY_DEBUG) \":\tDebug Message Toggle\\n\"\n        STR(MAGIC_KEY_DEBUG_MATRIX) \":\tMatrix Debug Mode Toggle\"\n            \" - Show keypresses in matrix grid\\n\"\n        STR(MAGIC_KEY_DEBUG_KBD) \":\tKeyboard Debug Toggle\"\n            \" - Show keypress report\\n\"\n        STR(MAGIC_KEY_DEBUG_MOUSE) \":\tDebug Mouse Toggle\\n\"\n        STR(MAGIC_KEY_VERSION) \":\tVersion\\n\"\n        STR(MAGIC_KEY_STATUS) \":\tStatus\\n\"\n        STR(MAGIC_KEY_CONSOLE) \":\tActivate Console Mode\\n\"\n\n#if MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM\n        STR(MAGIC_KEY_LAYER0) \":\tSwitch to Layer 0\\n\"\n        STR(MAGIC_KEY_LAYER1) \":\tSwitch to Layer 1\\n\"\n        STR(MAGIC_KEY_LAYER2) \":\tSwitch to Layer 2\\n\"\n        STR(MAGIC_KEY_LAYER3) \":\tSwitch to Layer 3\\n\"\n        STR(MAGIC_KEY_LAYER4) \":\tSwitch to Layer 4\\n\"\n        STR(MAGIC_KEY_LAYER5) \":\tSwitch to Layer 5\\n\"\n        STR(MAGIC_KEY_LAYER6) \":\tSwitch to Layer 6\\n\"\n        STR(MAGIC_KEY_LAYER7) \":\tSwitch to Layer 7\\n\"\n        STR(MAGIC_KEY_LAYER8) \":\tSwitch to Layer 8\\n\"\n        STR(MAGIC_KEY_LAYER9) \":\tSwitch to Layer 9\\n\"\n#endif\n\n#if MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS\n        \"F1-F10:\tSwitch to Layer 0-9 (F10 = L0)\\n\"\n#endif\n\n#if MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS\n        \"0-9:\tSwitch to Layer 0-9\\n\"\n#endif\n\n        STR(MAGIC_KEY_LAYER0_ALT) \":\tSwitch to Layer 0 (alternate)\\n\"\n\n        STR(MAGIC_KEY_BOOTLOADER) \":\tJump to Bootloader\\n\"\n        STR(MAGIC_KEY_BOOTLOADER_ALT) \":\tJump to Bootloader (alternate)\\n\"\n\n#ifdef KEYBOARD_LOCK_ENABLE\n        STR(MAGIC_KEY_LOCK) \":\tLock Keyboard\\n\"\n#endif\n\n        STR(MAGIC_KEY_EEPROM) \":\tPrint EEPROM Settings\\n\"\n        STR(MAGIC_KEY_EEPROM_CLEAR) \":\tClear EEPROM\\n\"\n\n#ifdef NKRO_ENABLE\n        STR(MAGIC_KEY_NKRO) \":\tNKRO Toggle\\n\"\n#endif\n\n#ifdef SLEEP_LED_ENABLE\n        STR(MAGIC_KEY_SLEEP_LED) \":\tSleep LED Test\\n\"\n#endif\n    ); /* clang-format on */\n}\n\nstatic void print_version(void) {\n    xprintf(\"%s\", /* clang-format off */\n        \"\\n\\t- Version -\\n\"\n        \"VID: \" STR(VENDOR_ID) \"(\" STR(MANUFACTURER) \") \"\n        \"PID: \" STR(PRODUCT_ID) \"(\" STR(PRODUCT) \") \"\n        \"VER: \" STR(DEVICE_VER) \"\\n\"\n        \"BUILD:  (\" __DATE__ \")\\n\"\n#ifndef SKIP_VERSION\n#    ifdef PROTOCOL_CHIBIOS\n        \"CHIBIOS: \" STR(CHIBIOS_VERSION)\n            \", CONTRIB: \" STR(CHIBIOS_CONTRIB_VERSION) \"\\n\"\n#    endif\n#endif\n\n    /* build options */\n        \"OPTIONS:\"\n\n#ifdef PROTOCOL_LUFA\n        \" LUFA\"\n#endif\n#ifdef PROTOCOL_VUSB\n        \" VUSB\"\n#endif\n#ifdef BOOTMAGIC_ENABLE\n        \" BOOTMAGIC\"\n#endif\n#ifdef MOUSEKEY_ENABLE\n        \" MOUSEKEY\"\n#endif\n#ifdef EXTRAKEY_ENABLE\n        \" EXTRAKEY\"\n#endif\n#ifdef CONSOLE_ENABLE\n        \" CONSOLE\"\n#endif\n#ifdef COMMAND_ENABLE\n        \" COMMAND\"\n#endif\n#ifdef NKRO_ENABLE\n        \" NKRO\"\n#endif\n#ifdef LTO_ENABLE\n        \" LTO\"\n#endif\n\n        \" \" STR(BOOTLOADER_SIZE) \"\\n\"\n\n        \"GCC: \" STR(__GNUC__)\n            \".\" STR(__GNUC_MINOR__)\n            \".\" STR(__GNUC_PATCHLEVEL__)\n#if defined(__AVR__)\n        \" AVR-LIBC: \" __AVR_LIBC_VERSION_STRING__\n        \" AVR_ARCH: avr\" STR(__AVR_ARCH__)\n#endif\n        \"\\n\"\n    ); /* clang-format on */\n}\n\nstatic void print_status(void) {\n    xprintf(/* clang-format off */\n        \"\\n\\t- Status -\\n\"\n\n        \"host_keyboard_leds(): %02X\\n\"\n        \"keyboard_protocol: %02X\\n\"\n        \"keyboard_idle: %02X\\n\"\n#ifdef NKRO_ENABLE\n        \"keymap_config.nkro: %02X\\n\"\n#endif\n        \"timer_read32(): %08lX\\n\"\n\n        , host_keyboard_leds()\n        , usb_device_state_get_protocol()\n        , usb_device_state_get_idle_rate()\n#ifdef NKRO_ENABLE\n        , keymap_config.nkro\n#endif\n        , timer_read32()\n\n    ); /* clang-format on */\n}\n\n#if !defined(NO_PRINT) && !defined(USER_PRINT)\nstatic void print_eeconfig(void) {\n    xprintf(\"eeconfig:\\ndefault_layer: %\" PRIu32 \"\\n\", (uint32_t)eeconfig_read_default_layer());\n\n    debug_config_t dc;\n    eeconfig_read_debug(&dc);\n    xprintf(/* clang-format off */\n\n        \"debug_config.raw: %02X\\n\"\n        \".enable: %u\\n\"\n        \".matrix: %u\\n\"\n        \".keyboard: %u\\n\"\n        \".mouse: %u\\n\"\n\n        , dc.raw\n        , dc.enable\n        , dc.matrix\n        , dc.keyboard\n        , dc.mouse\n    ); /* clang-format on */\n\n    keymap_config_t kc;\n    eeconfig_read_keymap(&kc);\n    xprintf(/* clang-format off */\n\n        \"keymap_config.raw: %02X\\n\"\n        \".swap_control_capslock: %u\\n\"\n        \".capslock_to_control: %u\\n\"\n        \".swap_lctl_lgui: %u\\n\"\n        \".swap_rctl_rgui: %u\\n\"\n        \".swap_lalt_lgui: %u\\n\"\n        \".swap_ralt_rgui: %u\\n\"\n        \".no_gui: %u\\n\"\n        \".swap_grave_esc: %u\\n\"\n        \".swap_backslash_backspace: %u\\n\"\n        \".nkro: %u\\n\"\n        \".swap_escape_capslock: %u\\n\"\n\n        , kc.raw\n        , kc.swap_control_capslock\n        , kc.capslock_to_control\n        , kc.swap_lctl_lgui\n        , kc.swap_rctl_rgui\n        , kc.swap_lalt_lgui\n        , kc.swap_ralt_rgui\n        , kc.no_gui\n        , kc.swap_grave_esc\n        , kc.swap_backslash_backspace\n        , kc.nkro\n        , kc.swap_escape_capslock\n    ); /* clang-format on */\n\n#    ifdef BACKLIGHT_ENABLE\n\n    backlight_config_t bc;\n    eeconfig_read_backlight(&bc);\n    xprintf(/* clang-format off */\n        \"backlight_config\"\n\n        \".raw: %02X\\n\"\n        \".enable: %u\\n\"\n        \".level: %u\\n\"\n\n        , bc.raw\n        , bc.enable\n        , bc.level\n\n    ); /* clang-format on */\n\n#    endif /* BACKLIGHT_ENABLE */\n}\n#endif /* !NO_PRINT && !USER_PRINT */\n\nstatic bool command_common(uint8_t code) {\n#ifdef KEYBOARD_LOCK_ENABLE\n    static host_driver_t *host_driver = 0;\n#endif\n\n    switch (code) {\n#ifdef SLEEP_LED_ENABLE\n\n        // test breathing sleep LED\n        case MAGIC_KC(MAGIC_KEY_SLEEP_LED):\n            print(\"Sleep LED Test\\n\");\n            sleep_led_toggle();\n            led_set(host_keyboard_leds());\n            break;\n#endif\n\n        // print stored eeprom config\n        case MAGIC_KC(MAGIC_KEY_EEPROM):\n#if !defined(NO_PRINT) && !defined(USER_PRINT)\n            print_eeconfig();\n#endif /* !NO_PRINT && !USER_PRINT */\n            break;\n\n        // clear eeprom\n        case MAGIC_KC(MAGIC_KEY_EEPROM_CLEAR):\n            print(\"Clearing EEPROM\\n\");\n            eeconfig_init();\n            break;\n\n#ifdef KEYBOARD_LOCK_ENABLE\n\n        // lock/unlock keyboard\n        case MAGIC_KC(MAGIC_KEY_LOCK):\n            if (host_get_driver()) {\n                host_driver = host_get_driver();\n                clear_keyboard();\n                host_set_driver(0);\n                print(\"Locked.\\n\");\n            } else {\n                host_set_driver(host_driver);\n                print(\"Unlocked.\\n\");\n            }\n            break;\n#endif\n\n        // print help\n        case MAGIC_KC(MAGIC_KEY_HELP):\n        case MAGIC_KC(MAGIC_KEY_HELP_ALT):\n            command_common_help();\n            break;\n\n        // activate console\n        case MAGIC_KC(MAGIC_KEY_CONSOLE):\n            debug_matrix   = false;\n            debug_keyboard = false;\n            debug_mouse    = false;\n            debug_enable   = false;\n            command_console_help();\n            print(\"C> \");\n            command_state = CONSOLE;\n            break;\n\n        // jump to bootloader\n        case MAGIC_KC(MAGIC_KEY_BOOTLOADER):\n        case MAGIC_KC(MAGIC_KEY_BOOTLOADER_ALT):\n            print(\"\\n\\nJumping to bootloader... \");\n            reset_keyboard();\n            break;\n\n        // debug toggle\n        case MAGIC_KC(MAGIC_KEY_DEBUG):\n            debug_enable = !debug_enable;\n            if (debug_enable) {\n                print(\"\\ndebug: on\\n\");\n            } else {\n                print(\"\\ndebug: off\\n\");\n                debug_matrix   = false;\n                debug_keyboard = false;\n                debug_mouse    = false;\n            }\n            break;\n\n        // debug matrix toggle\n        case MAGIC_KC(MAGIC_KEY_DEBUG_MATRIX):\n            debug_matrix = !debug_matrix;\n            if (debug_matrix) {\n                print(\"\\nmatrix: on\\n\");\n                debug_enable = true;\n            } else {\n                print(\"\\nmatrix: off\\n\");\n            }\n            break;\n\n        // debug keyboard toggle\n        case MAGIC_KC(MAGIC_KEY_DEBUG_KBD):\n            debug_keyboard = !debug_keyboard;\n            if (debug_keyboard) {\n                print(\"\\nkeyboard: on\\n\");\n                debug_enable = true;\n            } else {\n                print(\"\\nkeyboard: off\\n\");\n            }\n            break;\n\n        // debug mouse toggle\n        case MAGIC_KC(MAGIC_KEY_DEBUG_MOUSE):\n            debug_mouse = !debug_mouse;\n            if (debug_mouse) {\n                print(\"\\nmouse: on\\n\");\n                debug_enable = true;\n            } else {\n                print(\"\\nmouse: off\\n\");\n            }\n            break;\n\n        // print version\n        case MAGIC_KC(MAGIC_KEY_VERSION):\n            print_version();\n            break;\n\n        // print status\n        case MAGIC_KC(MAGIC_KEY_STATUS):\n            print_status();\n            break;\n\n#ifdef NKRO_ENABLE\n\n        // NKRO toggle\n        case MAGIC_KC(MAGIC_KEY_NKRO):\n            clear_keyboard(); // clear to prevent stuck keys\n            keymap_config.nkro = !keymap_config.nkro;\n            if (keymap_config.nkro) {\n                print(\"NKRO: on\\n\");\n            } else {\n                print(\"NKRO: off\\n\");\n            }\n            break;\n#endif\n\n            // switch layers\n\n        case MAGIC_KC(MAGIC_KEY_LAYER0_ALT):\n            switch_default_layer(0);\n            break;\n\n#if MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM\n\n        case MAGIC_KC(MAGIC_KEY_LAYER0):\n            switch_default_layer(0);\n            break;\n\n        case MAGIC_KC(MAGIC_KEY_LAYER1):\n            switch_default_layer(1);\n            break;\n\n        case MAGIC_KC(MAGIC_KEY_LAYER2):\n            switch_default_layer(2);\n            break;\n\n        case MAGIC_KC(MAGIC_KEY_LAYER3):\n            switch_default_layer(3);\n            break;\n\n        case MAGIC_KC(MAGIC_KEY_LAYER4):\n            switch_default_layer(4);\n            break;\n\n        case MAGIC_KC(MAGIC_KEY_LAYER5):\n            switch_default_layer(5);\n            break;\n\n        case MAGIC_KC(MAGIC_KEY_LAYER6):\n            switch_default_layer(6);\n            break;\n\n        case MAGIC_KC(MAGIC_KEY_LAYER7):\n            switch_default_layer(7);\n            break;\n\n        case MAGIC_KC(MAGIC_KEY_LAYER8):\n            switch_default_layer(8);\n            break;\n\n        case MAGIC_KC(MAGIC_KEY_LAYER9):\n            switch_default_layer(9);\n            break;\n#endif\n\n#if MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS\n\n        case KC_F1 ... KC_F9:\n            switch_default_layer((code - KC_F1) + 1);\n            break;\n        case KC_F10:\n            switch_default_layer(0);\n            break;\n#endif\n\n#if MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS\n\n        case KC_1 ... KC_9:\n            switch_default_layer((code - KC_1) + 1);\n            break;\n        case KC_0:\n            switch_default_layer(0);\n            break;\n#endif\n\n        default:\n            print(\"?\");\n            return false;\n    }\n    return true;\n}\n\n/***********************************************************\n * Command console\n ***********************************************************/\nstatic void command_console_help(void) {\n    print(\"\\n\\t- Console -\\n\"\n          \"ESC/q:\tquit\\n\"\n#ifdef MOUSEKEY_ENABLE\n          \"m:\tmousekey\\n\"\n#endif\n    );\n}\n\nstatic bool command_console(uint8_t code) {\n    switch (code) {\n        case KC_H:\n        case KC_SLASH: /* ? */\n            command_console_help();\n            print(\"C> \");\n            return true;\n        case KC_Q:\n        case KC_ESC:\n            command_state = ONESHOT;\n            return false;\n#if defined(MOUSEKEY_ENABLE)\n        case KC_M:\n            command_state = MOUSEKEY;\n            mousekey_console(KC_SLASH /* ? */);\n            return true;\n#endif\n        default:\n            print(\"?\");\n            return false;\n    }\n}\n\n/***********************************************************\n * Mousekey console\n ***********************************************************/\n\n#if defined(MOUSEKEY_ENABLE)\n\n#    if !defined(NO_PRINT) && !defined(USER_PRINT)\nstatic void mousekey_param_print(void) {\n    xprintf(/* clang-format off */\n\n#ifndef MK_3_SPEED\n        \"1:\tdelay(*10ms): %u\\n\"\n        \"2:\tinterval(ms): %u\\n\"\n        \"3:\tmax_speed: %u\\n\"\n        \"4:\ttime_to_max: %u\\n\"\n        \"5:\twheel_max_speed: %u\\n\"\n        \"6:\twheel_time_to_max: %u\\n\"\n\n        , mk_delay\n        , mk_interval\n        , mk_max_speed\n        , mk_time_to_max\n        , mk_wheel_max_speed\n        , mk_wheel_time_to_max\n#else\n        \"no knobs sorry\\n\"\n#endif\n\n    ); /* clang-format on */\n}\n#    endif /* !NO_PRINT && !USER_PRINT */\n\n#    if !defined(NO_PRINT) && !defined(USER_PRINT)\nstatic void mousekey_console_help(void) {\n    mousekey_param_print();\n    xprintf(/* clang-format off */\n        \"p:\tprint values\\n\"\n        \"d:\tset defaults\\n\"\n        \"up:\t+1\\n\"\n        \"dn:\t-1\\n\"\n        \"lt:\t+10\\n\"\n        \"rt:\t-10\\n\"\n        \"ESC/q:\tquit\\n\"\n\n#ifndef MK_3_SPEED\n        \"\\n\"\n        \"speed = delta * max_speed * (repeat / time_to_max)\\n\"\n        \"where delta: cursor=%d, wheel=%d\\n\"\n        \"See http://en.wikipedia.org/wiki/Mouse_keys\\n\"\n        , MOUSEKEY_MOVE_DELTA, MOUSEKEY_WHEEL_DELTA\n#endif\n\n    ); /* clang-format on */\n}\n#    endif /* !NO_PRINT && !USER_PRINT */\n\n/* Only used by `quantum/command.c` / `command_proc()`. To avoid\n * any doubt: we return `false` to return to the main console,\n * which differs from the `bool` that `command_proc()` returns. */\nbool mousekey_console(uint8_t code) {\n    static uint8_t  param = 0;\n    static uint8_t *pp    = NULL;\n    static char *   desc  = NULL;\n\n#    if defined(NO_PRINT) || defined(USER_PRINT) /* -Wunused-parameter */\n    (void)desc;\n#    endif\n\n    int8_t change = 0;\n\n    switch (code) {\n        case KC_H:\n        case KC_SLASH: /* ? */\n#    if !defined(NO_PRINT) && !defined(USER_PRINT)\n            print(\"\\n\\t- Mousekey -\\n\");\n            mousekey_console_help();\n#    endif\n            break;\n\n        case KC_Q:\n        case KC_ESC:\n            print(\"q\\n\");\n            if (!param) return false;\n            param = 0;\n            pp    = NULL;\n            desc  = NULL;\n            break;\n\n        case KC_P:\n#    if !defined(NO_PRINT) && !defined(USER_PRINT)\n            print(\"\\n\\t- Values -\\n\");\n            mousekey_param_print();\n#    endif\n            break;\n\n        case KC_1 ... KC_0: /* KC_0 gives param = 10 */\n            param = 1 + code - KC_1;\n            switch (param) { /* clang-format off */\n#               define PARAM(n, v) case n: pp = &(v); desc = #v; break\n\n#ifndef MK_3_SPEED\n                PARAM(1, mk_delay);\n                PARAM(2, mk_interval);\n                PARAM(3, mk_max_speed);\n                PARAM(4, mk_time_to_max);\n                PARAM(5, mk_wheel_max_speed);\n                PARAM(6, mk_wheel_time_to_max);\n#endif /* MK_3_SPEED */\n\n#               undef PARAM\n                default:\n                    param = 0;\n                    print(\"?\\n\");\n                    break;\n            } /* clang-format on */\n            if (param) xprintf(\"%u\\n\", param);\n            break;\n\n            /* clang-format off */\n        case KC_UP:    change =  +1; break;\n        case KC_DOWN:  change =  -1; break;\n        case KC_LEFT:  change = -10; break;\n        case KC_RIGHT: change = +10; break;\n            /* clang-format on */\n\n        case KC_D:\n\n#    ifndef MK_3_SPEED\n            mk_delay             = MOUSEKEY_DELAY / 10;\n            mk_interval          = MOUSEKEY_INTERVAL;\n            mk_max_speed         = MOUSEKEY_MAX_SPEED;\n            mk_time_to_max       = MOUSEKEY_TIME_TO_MAX;\n            mk_wheel_max_speed   = MOUSEKEY_WHEEL_MAX_SPEED;\n            mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;\n#    endif /* MK_3_SPEED */\n\n            print(\"defaults\\n\");\n            break;\n\n        default:\n            print(\"?\\n\");\n            break;\n    }\n\n    if (change) {\n        if (pp) {\n            int16_t val = *pp + change;\n            if (val > (int16_t)UINT8_MAX)\n                *pp = UINT8_MAX;\n            else if (val < 0)\n                *pp = 0;\n            else\n                *pp = (uint8_t)val;\n            xprintf(\"= %u\\n\", *pp);\n        } else {\n            print(\"?\\n\");\n        }\n    }\n\n    if (param) {\n        xprintf(\"M%u:%s> \", param, desc ? desc : \"???\");\n    } else {\n        print(\"M> \");\n    }\n    return true;\n}\n\n#endif /* MOUSEKEY_ENABLE */\n\n/***********************************************************\n * Utilities\n ***********************************************************/\n\nstatic void switch_default_layer(uint8_t layer) {\n    xprintf(\"L%d\\n\", layer);\n    default_layer_set((layer_state_t)1 << layer);\n    clear_keyboard();\n}\n"
  },
  {
    "path": "quantum/command.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n/* FIXME: Add doxygen comments for the behavioral defines in here. */\n\n/* TODO: Refactoring */\ntypedef enum { ONESHOT, CONSOLE, MOUSEKEY } command_state_t;\nextern command_state_t command_state;\n\n/* This allows to extend commands. Return false when command is not processed. */\nbool command_extra(uint8_t code);\nbool command_console_extra(uint8_t code);\n\n#ifdef COMMAND_ENABLE\nbool command_proc(uint8_t code);\n#else\n#    define command_proc(code) false\n#endif\n\n#ifndef IS_COMMAND\n#    define IS_COMMAND() (get_mods() == MOD_MASK_SHIFT)\n#endif\n\n#ifndef MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS\n#    define MAGIC_KEY_SWITCH_LAYER_WITH_FKEYS true\n#endif\n\n#ifndef MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS\n#    define MAGIC_KEY_SWITCH_LAYER_WITH_NKEYS true\n#endif\n\n#ifndef MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM\n#    define MAGIC_KEY_SWITCH_LAYER_WITH_CUSTOM false\n#endif\n\n#ifndef MAGIC_KEY_HELP\n#    define MAGIC_KEY_HELP H\n#endif\n\n#ifndef MAGIC_KEY_HELP_ALT\n#    define MAGIC_KEY_HELP_ALT SLASH\n#endif\n\n#ifndef MAGIC_KEY_DEBUG\n#    define MAGIC_KEY_DEBUG D\n#endif\n\n#ifndef MAGIC_KEY_DEBUG_MATRIX\n#    define MAGIC_KEY_DEBUG_MATRIX X\n#endif\n\n#ifndef MAGIC_KEY_DEBUG_KBD\n#    define MAGIC_KEY_DEBUG_KBD K\n#endif\n\n#ifndef MAGIC_KEY_DEBUG_MOUSE\n#    define MAGIC_KEY_DEBUG_MOUSE M\n#endif\n\n#ifndef MAGIC_KEY_VERSION\n#    define MAGIC_KEY_VERSION V\n#endif\n\n#ifndef MAGIC_KEY_STATUS\n#    define MAGIC_KEY_STATUS S\n#endif\n\n#ifndef MAGIC_KEY_CONSOLE\n#    define MAGIC_KEY_CONSOLE C\n#endif\n\n#ifndef MAGIC_KEY_LAYER0\n#    define MAGIC_KEY_LAYER0 0\n#endif\n\n#ifndef MAGIC_KEY_LAYER0_ALT\n#    define MAGIC_KEY_LAYER0_ALT GRAVE\n#endif\n\n#ifndef MAGIC_KEY_LAYER1\n#    define MAGIC_KEY_LAYER1 1\n#endif\n\n#ifndef MAGIC_KEY_LAYER2\n#    define MAGIC_KEY_LAYER2 2\n#endif\n\n#ifndef MAGIC_KEY_LAYER3\n#    define MAGIC_KEY_LAYER3 3\n#endif\n\n#ifndef MAGIC_KEY_LAYER4\n#    define MAGIC_KEY_LAYER4 4\n#endif\n\n#ifndef MAGIC_KEY_LAYER5\n#    define MAGIC_KEY_LAYER5 5\n#endif\n\n#ifndef MAGIC_KEY_LAYER6\n#    define MAGIC_KEY_LAYER6 6\n#endif\n\n#ifndef MAGIC_KEY_LAYER7\n#    define MAGIC_KEY_LAYER7 7\n#endif\n\n#ifndef MAGIC_KEY_LAYER8\n#    define MAGIC_KEY_LAYER8 8\n#endif\n\n#ifndef MAGIC_KEY_LAYER9\n#    define MAGIC_KEY_LAYER9 9\n#endif\n\n#ifndef MAGIC_KEY_BOOTLOADER\n#    define MAGIC_KEY_BOOTLOADER B\n#endif\n\n#ifndef MAGIC_KEY_BOOTLOADER_ALT\n#    define MAGIC_KEY_BOOTLOADER_ALT ESC\n#endif\n\n#ifndef MAGIC_KEY_LOCK\n#    define MAGIC_KEY_LOCK CAPS\n#endif\n\n#ifndef MAGIC_KEY_EEPROM\n#    define MAGIC_KEY_EEPROM E\n#endif\n\n#ifndef MAGIC_KEY_EEPROM_CLEAR\n#    define MAGIC_KEY_EEPROM_CLEAR BACKSPACE\n#endif\n\n#ifndef MAGIC_KEY_NKRO\n#    define MAGIC_KEY_NKRO N\n#endif\n\n#ifndef MAGIC_KEY_SLEEP_LED\n#    define MAGIC_KEY_SLEEP_LED Z\n\n#endif\n\n#define XMAGIC_KC(key) KC_##key\n#define MAGIC_KC(key) XMAGIC_KC(key)\n"
  },
  {
    "path": "quantum/compiler_support.h",
    "content": "// Copyright 2025 QMK Contributors\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/**\n * @brief Perfom an assertion at compile time.\n *\n * `_Static_assert` is C<23, while `static_assert` is C++/C23.\n */\n#if !defined(STATIC_ASSERT)\n#    ifdef __cplusplus\n#        define STATIC_ASSERT static_assert\n#    else\n#        define STATIC_ASSERT _Static_assert\n#    endif\n#endif\n"
  },
  {
    "path": "quantum/connection/connection.c",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include \"connection.h\"\n#include \"eeconfig.h\"\n#include \"usb_util.h\"\n#include \"util.h\"\n\n#ifdef BLUETOOTH_ENABLE\n#    include \"bluetooth.h\"\n#endif\n\n// ======== DEPRECATED DEFINES - DO NOT USE ========\n#ifdef OUTPUT_DEFAULT\n#    undef CONNECTION_HOST_DEFAULT\n#    define CONNECTION_HOST_DEFAULT OUTPUT_DEFAULT\n#endif\n\n__attribute__((weak)) void set_output_user(uint8_t output) {}\n// ========\n\n#define CONNECTION_HOST_INVALID 0xFF\n\n#ifndef CONNECTION_HOST_DEFAULT\n#    define CONNECTION_HOST_DEFAULT CONNECTION_HOST_AUTO\n#endif\n\nstatic const connection_host_t host_candidates[] = {\n    CONNECTION_HOST_AUTO,\n    CONNECTION_HOST_USB,\n#ifdef BLUETOOTH_ENABLE\n    CONNECTION_HOST_BLUETOOTH,\n#endif\n#if 0\n    CONNECTION_HOST_2P4GHZ,\n#endif\n};\n\n#define HOST_CANDIDATES_COUNT ARRAY_SIZE(host_candidates)\n\nstatic connection_config_t config = {.desired_host = CONNECTION_HOST_INVALID};\n\nvoid eeconfig_update_connection_default(void) {\n    config.desired_host = CONNECTION_HOST_DEFAULT;\n\n    eeconfig_update_connection(&config);\n}\n\nvoid connection_init(void) {\n    eeconfig_read_connection(&config);\n    if (config.desired_host == CONNECTION_HOST_INVALID) {\n        eeconfig_update_connection_default();\n    }\n}\n\n__attribute__((weak)) void connection_host_changed_user(connection_host_t host) {}\n__attribute__((weak)) void connection_host_changed_kb(connection_host_t host) {}\n\nstatic void handle_host_changed(void) {\n    connection_host_changed_user(config.desired_host);\n    connection_host_changed_kb(config.desired_host);\n\n    // TODO: Remove deprecated callback\n    set_output_user(config.desired_host);\n}\n\nvoid connection_set_host_noeeprom(connection_host_t host) {\n    if (config.desired_host == host) {\n        return;\n    }\n\n    config.desired_host = host;\n\n    handle_host_changed();\n}\n\nvoid connection_set_host(connection_host_t host) {\n    connection_set_host_noeeprom(host);\n\n    eeconfig_update_connection(&config);\n}\n\nvoid connection_next_host_noeeprom(void) {\n    uint8_t next = 0;\n    for (uint8_t i = 0; i < HOST_CANDIDATES_COUNT; i++) {\n        if (host_candidates[i] == config.desired_host) {\n            next = i == HOST_CANDIDATES_COUNT - 1 ? 0 : i + 1;\n            break;\n        }\n    }\n\n    connection_set_host_noeeprom(host_candidates[next]);\n}\n\nvoid connection_next_host(void) {\n    connection_next_host_noeeprom();\n\n    eeconfig_update_connection(&config);\n}\n\nvoid connection_prev_host_noeeprom(void) {\n    uint8_t next = 0;\n    for (uint8_t i = 0; i < HOST_CANDIDATES_COUNT; i++) {\n        if (host_candidates[i] == config.desired_host) {\n            next = i == 0 ? HOST_CANDIDATES_COUNT - 1 : i - 1;\n            break;\n        }\n    }\n\n    connection_set_host_noeeprom(host_candidates[next]);\n}\n\nvoid connection_prev_host(void) {\n    connection_prev_host_noeeprom();\n\n    eeconfig_update_connection(&config);\n}\n\nconnection_host_t connection_get_host_raw(void) {\n    return config.desired_host;\n}\n\nconnection_host_t connection_auto_detect_host(void) {\n    if (usb_connected_state()) {\n        return CONNECTION_HOST_USB;\n    }\n\n#ifdef BLUETOOTH_ENABLE\n    if (bluetooth_is_connected()) {\n        return CONNECTION_HOST_BLUETOOTH;\n    }\n#endif\n\n    return CONNECTION_HOST_NONE;\n}\n\nconnection_host_t connection_get_host(void) {\n    if (config.desired_host == CONNECTION_HOST_AUTO) {\n        return connection_auto_detect_host();\n    }\n    return config.desired_host;\n}\n"
  },
  {
    "path": "quantum/connection/connection.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include <stdint.h>\n\n#include \"compiler_support.h\"\n#include \"util.h\"\n\n/**\n * \\enum connection_host_t\n *\n * An enumeration of the possible hosts.\n */\ntypedef enum connection_host_t {\n    CONNECTION_HOST_AUTO,\n\n    CONNECTION_HOST_NONE,\n    CONNECTION_HOST_USB,\n    CONNECTION_HOST_BLUETOOTH,\n    CONNECTION_HOST_2P4GHZ\n} connection_host_t;\n\n/**\n * \\union connection_config_t\n *\n * Configuration structure for the connection subsystem.\n */\ntypedef union connection_config_t {\n    uint8_t           raw;\n    connection_host_t desired_host : 8;\n} PACKED connection_config_t;\n\nSTATIC_ASSERT(sizeof(connection_config_t) == sizeof(uint8_t), \"Connection EECONFIG out of spec.\");\n\n/**\n * \\brief Initialize the subsystem.\n *\n * This function must be called only once, before any of the below functions can be called.\n */\nvoid connection_init(void);\n\n/**\n * \\brief Get currently configured host. Does not resolve 'CONNECTION_HOST_AUTO'.\n *\n * \\return 'connection_host_t' of the configured host.\n */\nconnection_host_t connection_get_host_raw(void);\n\n/**\n * \\brief Get current active host.\n *\n * \\return 'connection_host_t' of the configured host.\n */\nconnection_host_t connection_auto_detect_host(void);\n\n/**\n * \\brief Get currently configured host. Resolves 'CONNECTION_HOST_AUTO' using 'connection_auto_detect_host()'.\n *\n * \\return 'connection_host_t' of the configured host.\n */\nconnection_host_t connection_get_host(void);\n\n/**\n * \\brief Get current host. New state is not written to EEPROM.\n *\n * \\param host The host to configure.\n */\nvoid connection_set_host_noeeprom(connection_host_t host);\n\n/**\n * \\brief Get current host.\n *\n * \\param host The host to configure.\n */\nvoid connection_set_host(connection_host_t host);\n\n/**\n * \\brief Move to the next potential host. New state is not written to EEPROM.\n *\n */\nvoid connection_next_host_noeeprom(void);\n\n/**\n * \\brief Move to the next potential host.\n *\n */\nvoid connection_next_host(void);\n\n/**\n * \\brief Move to the previous potential host. New state is not written to EEPROM.\n *\n */\nvoid connection_prev_host_noeeprom(void);\n\n/**\n * \\brief Move to the previous potential host.\n *\n */\nvoid connection_prev_host(void);\n\n/**\n * \\brief user hook called when changing configured host\n *\n */\nvoid connection_host_changed_user(connection_host_t host);\n\n/**\n * \\brief keyboard hook called when changing configured host\n *\n */\nvoid connection_host_changed_kb(connection_host_t host);\n"
  },
  {
    "path": "quantum/crc.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"crc.h\"\n\n__attribute__((weak)) void crc_init(void) {\n    // Software implementation nothing todo here.\n}\n\n#if defined(CRC8_USE_TABLE)\n/**\n * Static table used for the table_driven implementation.\n */\nstatic const crc_t crc_table[256] = {\n    0x00, 0x07, 0x0e, 0x09, 0x1c, 0x1b, 0x12, 0x15, 0x38, 0x3f, 0x36, 0x31, 0x24, 0x23, 0x2a, 0x2d, //\n    0x70, 0x77, 0x7e, 0x79, 0x6c, 0x6b, 0x62, 0x65, 0x48, 0x4f, 0x46, 0x41, 0x54, 0x53, 0x5a, 0x5d, //\n    0xe0, 0xe7, 0xee, 0xe9, 0xfc, 0xfb, 0xf2, 0xf5, 0xd8, 0xdf, 0xd6, 0xd1, 0xc4, 0xc3, 0xca, 0xcd, //\n    0x90, 0x97, 0x9e, 0x99, 0x8c, 0x8b, 0x82, 0x85, 0xa8, 0xaf, 0xa6, 0xa1, 0xb4, 0xb3, 0xba, 0xbd, //\n    0xc7, 0xc0, 0xc9, 0xce, 0xdb, 0xdc, 0xd5, 0xd2, 0xff, 0xf8, 0xf1, 0xf6, 0xe3, 0xe4, 0xed, 0xea, //\n    0xb7, 0xb0, 0xb9, 0xbe, 0xab, 0xac, 0xa5, 0xa2, 0x8f, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9d, 0x9a, //\n    0x27, 0x20, 0x29, 0x2e, 0x3b, 0x3c, 0x35, 0x32, 0x1f, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0d, 0x0a, //\n    0x57, 0x50, 0x59, 0x5e, 0x4b, 0x4c, 0x45, 0x42, 0x6f, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7d, 0x7a, //\n    0x89, 0x8e, 0x87, 0x80, 0x95, 0x92, 0x9b, 0x9c, 0xb1, 0xb6, 0xbf, 0xb8, 0xad, 0xaa, 0xa3, 0xa4, //\n    0xf9, 0xfe, 0xf7, 0xf0, 0xe5, 0xe2, 0xeb, 0xec, 0xc1, 0xc6, 0xcf, 0xc8, 0xdd, 0xda, 0xd3, 0xd4, //\n    0x69, 0x6e, 0x67, 0x60, 0x75, 0x72, 0x7b, 0x7c, 0x51, 0x56, 0x5f, 0x58, 0x4d, 0x4a, 0x43, 0x44, //\n    0x19, 0x1e, 0x17, 0x10, 0x05, 0x02, 0x0b, 0x0c, 0x21, 0x26, 0x2f, 0x28, 0x3d, 0x3a, 0x33, 0x34, //\n    0x4e, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5c, 0x5b, 0x76, 0x71, 0x78, 0x7f, 0x6a, 0x6d, 0x64, 0x63, //\n    0x3e, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2c, 0x2b, 0x06, 0x01, 0x08, 0x0f, 0x1a, 0x1d, 0x14, 0x13, //\n    0xae, 0xa9, 0xa0, 0xa7, 0xb2, 0xb5, 0xbc, 0xbb, 0x96, 0x91, 0x98, 0x9f, 0x8a, 0x8d, 0x84, 0x83, //\n    0xde, 0xd9, 0xd0, 0xd7, 0xc2, 0xc5, 0xcc, 0xcb, 0xe6, 0xe1, 0xe8, 0xef, 0xfa, 0xfd, 0xf4, 0xf3  //\n};\n\n__attribute__((weak)) uint8_t crc8(const void *data, size_t data_len) {\n    const uint8_t *d   = (const uint8_t *)data;\n    crc_t          crc = 0xff;\n    size_t         tbl_idx;\n\n    while (data_len--) {\n        tbl_idx = crc ^ *d;\n        crc     = crc_table[tbl_idx] & 0xff;\n        d++;\n    }\n    return crc & 0xff;\n}\n#else\n__attribute__((weak)) uint8_t crc8(const void *data, size_t data_len) {\n    const uint8_t *d   = (const uint8_t *)data;\n    crc_t          crc = 0xff;\n    size_t         i, j;\n\n    for (i = 0; i < data_len; i++) {\n        crc ^= d[i];\n        for (j = 0; j < 8; j++) {\n            if ((crc & 0x80) != 0)\n                crc = (crc_t)((crc << 1) ^ 0x31);\n            else\n                crc <<= 1;\n        }\n    }\n    return crc;\n}\n#endif\n"
  },
  {
    "path": "quantum/crc.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n\n/**\n * The type of the CRC values.\n *\n * This type must be big enough to contain at least 8 bits.\n */\n#if defined(CRC8_OPTIMIZE_SPEED)\ntypedef uint_fast8_t crc_t;\n#else\ntypedef uint_least8_t crc_t;\n#endif\n\n/**\n * Initialize crc subsystem.\n */\n__attribute__((weak)) void crc_init(void);\n\n/**\n * Generate CRC8 value from given data.\n *\n * \\param[in] data     Pointer to a buffer of \\a data_len bytes.\n * \\param[in] data_len Number of bytes in the \\a data buffer.\n * \\return             The calculated crc value.\n */\n__attribute__((weak)) uint8_t crc8(const void *data, size_t data_len);\n"
  },
  {
    "path": "quantum/debounce/asym_eager_defer_pk.c",
    "content": "/*\n * Copyright 2017 Alex Ong <the.onga@gmail.com>\n * Copyright 2020 Andrei Purdea <andrei@purdea.ro>\n * Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/*\nAsymetric per-key algorithm. After pressing a key, it immediately changes state,\nwith no further inputs accepted until DEBOUNCE milliseconds have occurred. After\nreleasing a key, that state is pushed after no changes occur for DEBOUNCE milliseconds.\n*/\n\n#include \"debounce.h\"\n#include \"timer.h\"\n#include <stdlib.h>\n\n#ifdef PROTOCOL_CHIBIOS\n#    if CH_CFG_USE_MEMCORE == FALSE\n#        error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.\n#    endif\n#endif\n\n#ifndef DEBOUNCE\n#    define DEBOUNCE 5\n#endif\n\n// Maximum debounce: 127ms\n#if DEBOUNCE > 127\n#    undef DEBOUNCE\n#    define DEBOUNCE 127\n#endif\n\n#define ROW_SHIFTER ((matrix_row_t)1)\n\ntypedef struct {\n    bool    pressed : 1;\n    uint8_t time : 7;\n} debounce_counter_t;\n\n#if DEBOUNCE > 0\nstatic debounce_counter_t *debounce_counters;\nstatic fast_timer_t        last_time;\nstatic bool                counters_need_update;\nstatic bool                matrix_need_update;\nstatic bool                cooked_changed;\n\n#    define DEBOUNCE_ELAPSED 0\n\nstatic void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);\nstatic void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);\n\n// we use num_rows rather than MATRIX_ROWS to support split keyboards\nvoid debounce_init(uint8_t num_rows) {\n    debounce_counters = malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));\n    int i             = 0;\n    for (uint8_t r = 0; r < num_rows; r++) {\n        for (uint8_t c = 0; c < MATRIX_COLS; c++) {\n            debounce_counters[i++].time = DEBOUNCE_ELAPSED;\n        }\n    }\n}\n\nvoid debounce_free(void) {\n    free(debounce_counters);\n    debounce_counters = NULL;\n}\n\nbool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {\n    bool updated_last = false;\n    cooked_changed    = false;\n\n    if (counters_need_update) {\n        fast_timer_t now          = timer_read_fast();\n        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);\n\n        last_time    = now;\n        updated_last = true;\n        if (elapsed_time > UINT8_MAX) {\n            elapsed_time = UINT8_MAX;\n        }\n\n        if (elapsed_time > 0) {\n            update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time);\n        }\n    }\n\n    if (changed || matrix_need_update) {\n        if (!updated_last) {\n            last_time = timer_read_fast();\n        }\n\n        transfer_matrix_values(raw, cooked, num_rows);\n    }\n\n    return cooked_changed;\n}\n\nstatic void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) {\n    debounce_counter_t *debounce_pointer = debounce_counters;\n\n    counters_need_update = false;\n    matrix_need_update   = false;\n\n    for (uint8_t row = 0; row < num_rows; row++) {\n        for (uint8_t col = 0; col < MATRIX_COLS; col++) {\n            matrix_row_t col_mask = (ROW_SHIFTER << col);\n\n            if (debounce_pointer->time != DEBOUNCE_ELAPSED) {\n                if (debounce_pointer->time <= elapsed_time) {\n                    debounce_pointer->time = DEBOUNCE_ELAPSED;\n\n                    if (debounce_pointer->pressed) {\n                        // key-down: eager\n                        matrix_need_update = true;\n                    } else {\n                        // key-up: defer\n                        matrix_row_t cooked_next = (cooked[row] & ~col_mask) | (raw[row] & col_mask);\n                        cooked_changed |= cooked_next ^ cooked[row];\n                        cooked[row] = cooked_next;\n                    }\n                } else {\n                    debounce_pointer->time -= elapsed_time;\n                    counters_need_update = true;\n                }\n            }\n            debounce_pointer++;\n        }\n    }\n}\n\nstatic void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {\n    debounce_counter_t *debounce_pointer = debounce_counters;\n\n    matrix_need_update = false;\n\n    for (uint8_t row = 0; row < num_rows; row++) {\n        matrix_row_t delta = raw[row] ^ cooked[row];\n        for (uint8_t col = 0; col < MATRIX_COLS; col++) {\n            matrix_row_t col_mask = (ROW_SHIFTER << col);\n\n            if (delta & col_mask) {\n                if (debounce_pointer->time == DEBOUNCE_ELAPSED) {\n                    debounce_pointer->pressed = (raw[row] & col_mask);\n                    debounce_pointer->time    = DEBOUNCE;\n                    counters_need_update      = true;\n\n                    if (debounce_pointer->pressed) {\n                        // key-down: eager\n                        cooked[row] ^= col_mask;\n                        cooked_changed = true;\n                    }\n                }\n            } else if (debounce_pointer->time != DEBOUNCE_ELAPSED) {\n                if (!debounce_pointer->pressed) {\n                    // key-up: defer\n                    debounce_pointer->time = DEBOUNCE_ELAPSED;\n                }\n            }\n            debounce_pointer++;\n        }\n    }\n}\n\n#else\n#    include \"none.c\"\n#endif\n"
  },
  {
    "path": "quantum/debounce/none.c",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"debounce.h\"\n#include <string.h>\n\nvoid debounce_init(uint8_t num_rows) {}\n\nbool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {\n    bool cooked_changed = false;\n\n    if (changed) {\n        size_t matrix_size = num_rows * sizeof(matrix_row_t);\n        if (memcmp(cooked, raw, matrix_size) != 0) {\n            memcpy(cooked, raw, matrix_size);\n            cooked_changed = true;\n        }\n    }\n\n    return cooked_changed;\n}\n\nvoid debounce_free(void) {}\n"
  },
  {
    "path": "quantum/debounce/sym_defer_g.c",
    "content": "/*\nCopyright 2017 Alex Ong<the.onga@gmail.com>\nCopyright 2021 Simon Arlott\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n/*\nBasic global debounce algorithm. Used in 99% of keyboards at time of implementation\nWhen no state changes have occured for DEBOUNCE milliseconds, we push the state.\n*/\n#include \"debounce.h\"\n#include \"timer.h\"\n#include <string.h>\n#ifndef DEBOUNCE\n#    define DEBOUNCE 5\n#endif\n\n// Maximum debounce: 255ms\n#if DEBOUNCE > UINT8_MAX\n#    undef DEBOUNCE\n#    define DEBOUNCE UINT8_MAX\n#endif\n\n#if DEBOUNCE > 0\nstatic bool         debouncing = false;\nstatic fast_timer_t debouncing_time;\n\nvoid debounce_init(uint8_t num_rows) {}\n\nbool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {\n    bool cooked_changed = false;\n\n    if (changed) {\n        debouncing      = true;\n        debouncing_time = timer_read_fast();\n    } else if (debouncing && timer_elapsed_fast(debouncing_time) >= DEBOUNCE) {\n        size_t matrix_size = num_rows * sizeof(matrix_row_t);\n        if (memcmp(cooked, raw, matrix_size) != 0) {\n            memcpy(cooked, raw, matrix_size);\n            cooked_changed = true;\n        }\n        debouncing = false;\n    }\n\n    return cooked_changed;\n}\n\nvoid debounce_free(void) {}\n#else // no debouncing.\n#    include \"none.c\"\n#endif\n"
  },
  {
    "path": "quantum/debounce/sym_defer_pk.c",
    "content": "/*\nCopyright 2017 Alex Ong<the.onga@gmail.com>\nCopyright 2020 Andrei Purdea<andrei@purdea.ro>\nCopyright 2021 Simon Arlott\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n/*\nBasic symmetric per-key algorithm. Uses an 8-bit counter per key.\nWhen no state changes have occured for DEBOUNCE milliseconds, we push the state.\n*/\n\n#include \"debounce.h\"\n#include \"timer.h\"\n#include <stdlib.h>\n\n#ifdef PROTOCOL_CHIBIOS\n#    if CH_CFG_USE_MEMCORE == FALSE\n#        error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.\n#    endif\n#endif\n\n#ifndef DEBOUNCE\n#    define DEBOUNCE 5\n#endif\n\n// Maximum debounce: 255ms\n#if DEBOUNCE > UINT8_MAX\n#    undef DEBOUNCE\n#    define DEBOUNCE UINT8_MAX\n#endif\n\n#define ROW_SHIFTER ((matrix_row_t)1)\n\ntypedef uint8_t debounce_counter_t;\n\n#if DEBOUNCE > 0\nstatic debounce_counter_t *debounce_counters;\nstatic fast_timer_t        last_time;\nstatic bool                counters_need_update;\nstatic bool                cooked_changed;\n\n#    define DEBOUNCE_ELAPSED 0\n\nstatic void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time);\nstatic void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);\n\n// we use num_rows rather than MATRIX_ROWS to support split keyboards\nvoid debounce_init(uint8_t num_rows) {\n    debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));\n    int i             = 0;\n    for (uint8_t r = 0; r < num_rows; r++) {\n        for (uint8_t c = 0; c < MATRIX_COLS; c++) {\n            debounce_counters[i++] = DEBOUNCE_ELAPSED;\n        }\n    }\n}\n\nvoid debounce_free(void) {\n    free(debounce_counters);\n    debounce_counters = NULL;\n}\n\nbool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {\n    bool updated_last = false;\n    cooked_changed    = false;\n\n    if (counters_need_update) {\n        fast_timer_t now          = timer_read_fast();\n        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);\n\n        last_time    = now;\n        updated_last = true;\n        if (elapsed_time > UINT8_MAX) {\n            elapsed_time = UINT8_MAX;\n        }\n\n        if (elapsed_time > 0) {\n            update_debounce_counters_and_transfer_if_expired(raw, cooked, num_rows, elapsed_time);\n        }\n    }\n\n    if (changed) {\n        if (!updated_last) {\n            last_time = timer_read_fast();\n        }\n\n        start_debounce_counters(raw, cooked, num_rows);\n    }\n\n    return cooked_changed;\n}\n\nstatic void update_debounce_counters_and_transfer_if_expired(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, uint8_t elapsed_time) {\n    counters_need_update                 = false;\n    debounce_counter_t *debounce_pointer = debounce_counters;\n    for (uint8_t row = 0; row < num_rows; row++) {\n        for (uint8_t col = 0; col < MATRIX_COLS; col++) {\n            if (*debounce_pointer != DEBOUNCE_ELAPSED) {\n                if (*debounce_pointer <= elapsed_time) {\n                    *debounce_pointer        = DEBOUNCE_ELAPSED;\n                    matrix_row_t cooked_next = (cooked[row] & ~(ROW_SHIFTER << col)) | (raw[row] & (ROW_SHIFTER << col));\n                    cooked_changed |= cooked[row] ^ cooked_next;\n                    cooked[row] = cooked_next;\n                } else {\n                    *debounce_pointer -= elapsed_time;\n                    counters_need_update = true;\n                }\n            }\n            debounce_pointer++;\n        }\n    }\n}\n\nstatic void start_debounce_counters(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {\n    debounce_counter_t *debounce_pointer = debounce_counters;\n    for (uint8_t row = 0; row < num_rows; row++) {\n        matrix_row_t delta = raw[row] ^ cooked[row];\n        for (uint8_t col = 0; col < MATRIX_COLS; col++) {\n            if (delta & (ROW_SHIFTER << col)) {\n                if (*debounce_pointer == DEBOUNCE_ELAPSED) {\n                    *debounce_pointer    = DEBOUNCE;\n                    counters_need_update = true;\n                }\n            } else {\n                *debounce_pointer = DEBOUNCE_ELAPSED;\n            }\n            debounce_pointer++;\n        }\n    }\n}\n\n#else\n#    include \"none.c\"\n#endif\n"
  },
  {
    "path": "quantum/debounce/sym_defer_pr.c",
    "content": "/*\nCopyright 2021 Chad Austin <chad@chadaustin.me>\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n/*\nSymmetric per-row debounce algorithm. Changes only apply when\nDEBOUNCE milliseconds have elapsed since the last change.\n*/\n\n#include \"debounce.h\"\n#include \"timer.h\"\n#include <stdlib.h>\n\n#ifndef DEBOUNCE\n#    define DEBOUNCE 5\n#endif\n\nstatic uint16_t last_time;\n// [row] milliseconds until key's state is considered debounced.\nstatic uint8_t* countdowns;\n// [row]\nstatic matrix_row_t* last_raw;\n\nvoid debounce_init(uint8_t num_rows) {\n    countdowns = (uint8_t*)calloc(num_rows, sizeof(uint8_t));\n    last_raw   = (matrix_row_t*)calloc(num_rows, sizeof(matrix_row_t));\n\n    last_time = timer_read();\n}\n\nvoid debounce_free(void) {\n    free(countdowns);\n    countdowns = NULL;\n    free(last_raw);\n    last_raw = NULL;\n}\n\nbool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {\n    uint16_t now           = timer_read();\n    uint16_t elapsed16     = TIMER_DIFF_16(now, last_time);\n    last_time              = now;\n    uint8_t elapsed        = (elapsed16 > 255) ? 255 : elapsed16;\n    bool    cooked_changed = false;\n\n    uint8_t* countdown = countdowns;\n\n    for (uint8_t row = 0; row < num_rows; ++row, ++countdown) {\n        matrix_row_t raw_row = raw[row];\n\n        if (raw_row != last_raw[row]) {\n            *countdown    = DEBOUNCE;\n            last_raw[row] = raw_row;\n        } else if (*countdown > elapsed) {\n            *countdown -= elapsed;\n        } else if (*countdown) {\n            cooked_changed |= cooked[row] ^ raw_row;\n            cooked[row] = raw_row;\n            *countdown  = 0;\n        }\n    }\n\n    return cooked_changed;\n}\n\nbool debounce_active(void) {\n    return true;\n}\n"
  },
  {
    "path": "quantum/debounce/sym_eager_pk.c",
    "content": "/*\nCopyright 2017 Alex Ong<the.onga@gmail.com>\nCopyright 2021 Simon Arlott\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n/*\nBasic per-key algorithm. Uses an 8-bit counter per key.\nAfter pressing a key, it immediately changes state, and sets a counter.\nNo further inputs are accepted until DEBOUNCE milliseconds have occurred.\n*/\n\n#include \"debounce.h\"\n#include \"timer.h\"\n#include <stdlib.h>\n\n#ifdef PROTOCOL_CHIBIOS\n#    if CH_CFG_USE_MEMCORE == FALSE\n#        error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.\n#    endif\n#endif\n\n#ifndef DEBOUNCE\n#    define DEBOUNCE 5\n#endif\n\n// Maximum debounce: 255ms\n#if DEBOUNCE > UINT8_MAX\n#    undef DEBOUNCE\n#    define DEBOUNCE UINT8_MAX\n#endif\n\n#define ROW_SHIFTER ((matrix_row_t)1)\n\ntypedef uint8_t debounce_counter_t;\n\n#if DEBOUNCE > 0\nstatic debounce_counter_t *debounce_counters;\nstatic fast_timer_t        last_time;\nstatic bool                counters_need_update;\nstatic bool                matrix_need_update;\nstatic bool                cooked_changed;\n\n#    define DEBOUNCE_ELAPSED 0\n\nstatic void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);\nstatic void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);\n\n// we use num_rows rather than MATRIX_ROWS to support split keyboards\nvoid debounce_init(uint8_t num_rows) {\n    debounce_counters = (debounce_counter_t *)malloc(num_rows * MATRIX_COLS * sizeof(debounce_counter_t));\n    int i             = 0;\n    for (uint8_t r = 0; r < num_rows; r++) {\n        for (uint8_t c = 0; c < MATRIX_COLS; c++) {\n            debounce_counters[i++] = DEBOUNCE_ELAPSED;\n        }\n    }\n}\n\nvoid debounce_free(void) {\n    free(debounce_counters);\n    debounce_counters = NULL;\n}\n\nbool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {\n    bool updated_last = false;\n    cooked_changed    = false;\n\n    if (counters_need_update) {\n        fast_timer_t now          = timer_read_fast();\n        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);\n\n        last_time    = now;\n        updated_last = true;\n        if (elapsed_time > UINT8_MAX) {\n            elapsed_time = UINT8_MAX;\n        }\n\n        if (elapsed_time > 0) {\n            update_debounce_counters(num_rows, elapsed_time);\n        }\n    }\n\n    if (changed || matrix_need_update) {\n        if (!updated_last) {\n            last_time = timer_read_fast();\n        }\n\n        transfer_matrix_values(raw, cooked, num_rows);\n    }\n\n    return cooked_changed;\n}\n\n// If the current time is > debounce counter, set the counter to enable input.\nstatic void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {\n    counters_need_update                 = false;\n    matrix_need_update                   = false;\n    debounce_counter_t *debounce_pointer = debounce_counters;\n    for (uint8_t row = 0; row < num_rows; row++) {\n        for (uint8_t col = 0; col < MATRIX_COLS; col++) {\n            if (*debounce_pointer != DEBOUNCE_ELAPSED) {\n                if (*debounce_pointer <= elapsed_time) {\n                    *debounce_pointer  = DEBOUNCE_ELAPSED;\n                    matrix_need_update = true;\n                } else {\n                    *debounce_pointer -= elapsed_time;\n                    counters_need_update = true;\n                }\n            }\n            debounce_pointer++;\n        }\n    }\n}\n\n// upload from raw_matrix to final matrix;\nstatic void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {\n    matrix_need_update                   = false;\n    debounce_counter_t *debounce_pointer = debounce_counters;\n    for (uint8_t row = 0; row < num_rows; row++) {\n        matrix_row_t delta        = raw[row] ^ cooked[row];\n        matrix_row_t existing_row = cooked[row];\n        for (uint8_t col = 0; col < MATRIX_COLS; col++) {\n            matrix_row_t col_mask = (ROW_SHIFTER << col);\n            if (delta & col_mask) {\n                if (*debounce_pointer == DEBOUNCE_ELAPSED) {\n                    *debounce_pointer    = DEBOUNCE;\n                    counters_need_update = true;\n                    existing_row ^= col_mask; // flip the bit.\n                    cooked_changed = true;\n                }\n            }\n            debounce_pointer++;\n        }\n        cooked[row] = existing_row;\n    }\n}\n\n#else\n#    include \"none.c\"\n#endif\n"
  },
  {
    "path": "quantum/debounce/sym_eager_pr.c",
    "content": "/*\nCopyright 2019 Alex Ong<the.onga@gmail.com>\nCopyright 2021 Simon Arlott\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n/*\nBasic per-row algorithm. Uses an 8-bit counter per row.\nAfter pressing a key, it immediately changes state, and sets a counter.\nNo further inputs are accepted until DEBOUNCE milliseconds have occurred.\n*/\n\n#include \"debounce.h\"\n#include \"timer.h\"\n#include <stdlib.h>\n\n#ifdef PROTOCOL_CHIBIOS\n#    if CH_CFG_USE_MEMCORE == FALSE\n#        error ChibiOS is configured without a memory allocator. Your keyboard may have set `#define CH_CFG_USE_MEMCORE FALSE`, which is incompatible with this debounce algorithm.\n#    endif\n#endif\n\n#ifndef DEBOUNCE\n#    define DEBOUNCE 5\n#endif\n\n// Maximum debounce: 255ms\n#if DEBOUNCE > UINT8_MAX\n#    undef DEBOUNCE\n#    define DEBOUNCE UINT8_MAX\n#endif\n\ntypedef uint8_t debounce_counter_t;\n\n#if DEBOUNCE > 0\nstatic bool matrix_need_update;\n\nstatic debounce_counter_t *debounce_counters;\nstatic fast_timer_t        last_time;\nstatic bool                counters_need_update;\nstatic bool                cooked_changed;\n\n#    define DEBOUNCE_ELAPSED 0\n\nstatic void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time);\nstatic void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows);\n\n// we use num_rows rather than MATRIX_ROWS to support split keyboards\nvoid debounce_init(uint8_t num_rows) {\n    debounce_counters = (debounce_counter_t *)malloc(num_rows * sizeof(debounce_counter_t));\n    for (uint8_t r = 0; r < num_rows; r++) {\n        debounce_counters[r] = DEBOUNCE_ELAPSED;\n    }\n}\n\nvoid debounce_free(void) {\n    free(debounce_counters);\n    debounce_counters = NULL;\n}\n\nbool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed) {\n    bool updated_last = false;\n    cooked_changed    = false;\n\n    if (counters_need_update) {\n        fast_timer_t now          = timer_read_fast();\n        fast_timer_t elapsed_time = TIMER_DIFF_FAST(now, last_time);\n\n        last_time    = now;\n        updated_last = true;\n        if (elapsed_time > UINT8_MAX) {\n            elapsed_time = UINT8_MAX;\n        }\n\n        if (elapsed_time > 0) {\n            update_debounce_counters(num_rows, elapsed_time);\n        }\n    }\n\n    if (changed || matrix_need_update) {\n        if (!updated_last) {\n            last_time = timer_read_fast();\n        }\n\n        transfer_matrix_values(raw, cooked, num_rows);\n    }\n\n    return cooked_changed;\n}\n\n// If the current time is > debounce counter, set the counter to enable input.\nstatic void update_debounce_counters(uint8_t num_rows, uint8_t elapsed_time) {\n    counters_need_update                 = false;\n    matrix_need_update                   = false;\n    debounce_counter_t *debounce_pointer = debounce_counters;\n    for (uint8_t row = 0; row < num_rows; row++) {\n        if (*debounce_pointer != DEBOUNCE_ELAPSED) {\n            if (*debounce_pointer <= elapsed_time) {\n                *debounce_pointer  = DEBOUNCE_ELAPSED;\n                matrix_need_update = true;\n            } else {\n                *debounce_pointer -= elapsed_time;\n                counters_need_update = true;\n            }\n        }\n        debounce_pointer++;\n    }\n}\n\n// upload from raw_matrix to final matrix;\nstatic void transfer_matrix_values(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows) {\n    matrix_need_update                   = false;\n    debounce_counter_t *debounce_pointer = debounce_counters;\n    for (uint8_t row = 0; row < num_rows; row++) {\n        matrix_row_t existing_row = cooked[row];\n        matrix_row_t raw_row      = raw[row];\n\n        // determine new value basd on debounce pointer + raw value\n        if (existing_row != raw_row) {\n            if (*debounce_pointer == DEBOUNCE_ELAPSED) {\n                *debounce_pointer = DEBOUNCE;\n                cooked_changed |= cooked[row] ^ raw_row;\n                cooked[row]          = raw_row;\n                counters_need_update = true;\n            }\n        }\n        debounce_pointer++;\n    }\n}\n\n#else\n#    include \"none.c\"\n#endif\n"
  },
  {
    "path": "quantum/debounce/tests/asym_eager_defer_pk_tests.cpp",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\n#include \"debounce_test_common.h\"\n\nTEST_F(DebounceTest, OneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        /* Release key after 1ms delay */\n        {1, {{0, 1, UP}}, {}},\n\n        /*\n         * Until the eager timer on DOWN is observed to finish, the defer timer\n         * on UP can't start. There's no workaround for this because it's not\n         * possible to debounce an event that isn't being tracked.\n         *\n         * sym_defer_pk has the same problem but the test has to track that the\n         * key changed state so the DOWN timer is always allowed to finish\n         * before starting the UP timer.\n         */\n        {5, {}, {}},\n\n        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */\n        /* Press key again after 1ms delay */\n        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        /* Release key after 2ms delay */\n        {2, {{0, 1, UP}}, {}},\n\n        {5, {}, {}}, /* See OneKeyShort1 */\n\n        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */\n        /* Press key again after 1ms delay */\n        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        /* Release key after 3ms delay */\n        {3, {{0, 1, UP}}, {}},\n\n        {5, {}, {}}, /* See OneKeyShort1 */\n\n        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */\n        /* Press key again after 1ms delay */\n        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort4) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        /* Release key after 4ms delay */\n        {4, {{0, 1, UP}}, {}},\n\n        {5, {}, {}}, /* See OneKeyShort1 */\n\n        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */\n        /* Press key again after 1ms delay */\n        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort5) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Release key after 5ms delay */\n        {5, {{0, 1, UP}}, {}},\n\n        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */\n        /* Press key again after 1ms delay */\n        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort6) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Release key after 6ms delay */\n        {6, {{0, 1, UP}}, {}},\n\n        {11, {}, {{0, 1, UP}}}, /* 5ms after UP at time 6 */\n        /* Press key again after 1ms delay */\n        {12, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort7) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Release key after 7ms delay */\n        {7, {{0, 1, UP}}, {}},\n\n        {12, {}, {{0, 1, UP}}}, /* 5ms after UP at time 7 */\n        /* Press key again after 1ms delay */\n        {13, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort8) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        /* Release key after 1ms delay */\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {}}, /* See OneKeyShort1 */\n\n        {10, {}, {{0, 1, UP}}}, /* 5ms after UP at time 7 */\n        /* Press key again after 0ms delay (scan 2) */\n        {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort9) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        /* Release key after 1ms delay */\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {}}, /* See OneKeyShort1 */\n\n        /* Press key again after 0ms delay (same scan) before debounce finishes */\n        {10, {{0, 1, DOWN}}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n        {2, {{0, 1, DOWN}}, {}},\n        {3, {{0, 1, UP}}, {}},\n        {4, {{0, 1, DOWN}}, {}},\n        {5, {{0, 1, UP}}, {}},\n        {6, {{0, 1, DOWN}}, {}},\n        {7, {{0, 1, UP}}, {}},\n        {8, {{0, 1, DOWN}}, {}},\n        {9, {{0, 1, UP}}, {}},\n        {10, {{0, 1, DOWN}}, {}},\n        {11, {{0, 1, UP}}, {}},\n        {12, {{0, 1, DOWN}}, {}},\n        {13, {{0, 1, UP}}, {}},\n        {14, {{0, 1, DOWN}}, {}},\n        {15, {{0, 1, UP}}, {}},\n\n        {20, {}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay */\n        {21, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        /* Change twice in the same time period */\n        {1, {{0, 1, UP}}, {}},\n        {1, {{0, 1, DOWN}}, {}},\n        /* Change three times in the same time period */\n        {2, {{0, 1, UP}}, {}},\n        {2, {{0, 1, DOWN}}, {}},\n        {2, {{0, 1, UP}}, {}},\n        /* Change twice in the same time period */\n        {6, {{0, 1, DOWN}}, {}},\n        {6, {{0, 1, UP}}, {}},\n        /* Change three times in the same time period */\n        {7, {{0, 1, DOWN}}, {}},\n        {7, {{0, 1, UP}}, {}},\n        {7, {{0, 1, DOWN}}, {}},\n        /* Change twice in the same time period */\n        {8, {{0, 1, UP}}, {}},\n        {8, {{0, 1, DOWN}}, {}},\n        /* Change three times in the same time period */\n        {9, {{0, 1, UP}}, {}},\n        {9, {{0, 1, DOWN}}, {}},\n        {9, {{0, 1, UP}}, {}},\n\n        {14, {}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay */\n        {15, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyLong) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {25, {{0, 1, UP}}, {}},\n\n        {30, {}, {{0, 1, UP}}},\n\n        {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {75, {{0, 1, UP}}, {}},\n\n        {80, {}, {{0, 1, UP}}},\n\n        {100, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysShort) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 2, DOWN}}, {{0, 2, DOWN}}},\n        /* Release key after 2ms delay */\n        {2, {{0, 1, UP}}, {}},\n        {3, {{0, 2, UP}}, {}},\n\n        {5, {}, {}}, /* See OneKeyShort1 */\n        {6, {}, {}}, /* See OneKeyShort1 */\n\n        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */\n        /* Press key again after 1ms delay */\n        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}, {0, 2, UP}}}, /* 5ms+5ms after DOWN at time 0 */\n        {12, {{0, 2, DOWN}}, {{0, 2, DOWN}}},             /* 5ms+5ms after DOWN at time 0 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late, immediately release key */\n        {300, {{0, 1, UP}}, {}},\n\n        {305, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late, immediately release key */\n        {300, {{0, 1, UP}}, {}},\n\n        /* Processing is very late again */\n        {600, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late */\n        {300, {}, {}},\n        /* Release key after 1ms */\n        {301, {{0, 1, UP}}, {}},\n\n        {306, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan4) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late */\n        {300, {}, {}},\n        /* Release key after 1ms */\n        {301, {{0, 1, UP}}, {}},\n\n        /* Processing is very late again */\n        {600, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan5) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {5, {{0, 1, UP}}, {}},\n\n        /* Processing is very late */\n        {300, {}, {{0, 1, UP}}},\n        /* Immediately press key again */\n        {300, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan6) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {5, {{0, 1, UP}}, {}},\n\n        /* Processing is very late */\n        {300, {}, {{0, 1, UP}}},\n\n        /* Press key again after 1ms */\n        {301, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan7) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {5, {{0, 1, UP}}, {}},\n\n        /* Press key again before debounce expires */\n        {300, {{0, 1, DOWN}}, {}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan8) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is a bit late */\n        {50, {}, {}},\n        /* Release key after 1ms */\n        {51, {{0, 1, UP}}, {}},\n\n        /* Processing is a bit late again */\n        {100, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, AsyncTickOneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        /* Release key after 1ms delay */\n        {1, {{0, 1, UP}}, {}},\n\n        /*\n         * Until the eager timer on DOWN is observed to finish, the defer timer\n         * on UP can't start. There's no workaround for this because it's not\n         * possible to debounce an event that isn't being tracked.\n         *\n         * sym_defer_pk has the same problem but the test has to track that the\n         * key changed state so the DOWN timer is always allowed to finish\n         * before starting the UP timer.\n         */\n        {5, {}, {}},\n\n        {10, {}, {{0, 1, UP}}}, /* 5ms+5ms after DOWN at time 0 */\n        /* Press key again after 1ms delay */\n        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    /*\n     * Debounce implementations should never read the timer more than once per invocation\n     */\n    async_time_jumps_ = DEBOUNCE;\n    runEvents();\n}\n"
  },
  {
    "path": "quantum/debounce/tests/debounce_test_common.cpp",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\n#include \"debounce_test_common.h\"\n\n#include <algorithm>\n#include <iomanip>\n#include <sstream>\n\nextern \"C\" {\n#include \"debounce.h\"\n#include \"timer.h\"\n\nvoid     simulate_async_tick(uint32_t t);\nvoid     reset_access_counter(void);\nuint32_t current_access_counter(void);\nuint32_t timer_read_internal(void);\nvoid     set_time(uint32_t t);\nvoid     advance_time(uint32_t ms);\n}\n\nvoid DebounceTest::addEvents(std::initializer_list<DebounceTestEvent> events) {\n    events_.insert(events_.end(), events.begin(), events.end());\n}\n\nvoid DebounceTest::runEvents() {\n    /* Run the test multiple times, from 1kHz to 10kHz scan rate */\n    for (extra_iterations_ = 0; extra_iterations_ < 10; extra_iterations_++) {\n        if (time_jumps_) {\n            /* Don't advance time smoothly, jump to the next event (some tests require this) */\n            auto_advance_time_ = false;\n            runEventsInternal();\n        } else {\n            /* Run the test with both smooth and irregular time; it must produce the same result */\n            auto_advance_time_ = true;\n            runEventsInternal();\n            auto_advance_time_ = false;\n            runEventsInternal();\n        }\n    }\n}\n\nvoid DebounceTest::runEventsInternal() {\n    fast_timer_t previous = 0;\n    bool         first    = true;\n\n    /* Initialise keyboard with start time (offset to avoid testing at 0) and all keys UP */\n    debounce_init(MATRIX_ROWS);\n    set_time(time_offset_);\n    simulate_async_tick(async_time_jumps_);\n    std::fill(std::begin(input_matrix_), std::end(input_matrix_), 0);\n    std::fill(std::begin(output_matrix_), std::end(output_matrix_), 0);\n\n    for (auto &event : events_) {\n        if (!auto_advance_time_) {\n            /* Jump to the next event */\n            set_time(time_offset_ + event.time_);\n        } else if (!first && event.time_ == previous + 1) {\n            /* This event immediately follows the previous one, don't make extra debounce() calls */\n            advance_time(1);\n        } else {\n            /* Fast forward to the time for this event, calling debounce() with no changes */\n            ASSERT_LT((time_offset_ + event.time_) - timer_read_internal(), 60000) << \"Test tries to advance more than 1 minute of time\";\n\n            while (timer_read_internal() != time_offset_ + event.time_) {\n                runDebounce(false);\n                checkCookedMatrix(false, \"debounce() modified cooked matrix\");\n                advance_time(1);\n            }\n        }\n\n        first    = false;\n        previous = event.time_;\n\n        /* Prepare input matrix */\n        for (auto &input : event.inputs_) {\n            matrixUpdate(input_matrix_, \"input\", input);\n        }\n\n        /* Call debounce */\n        runDebounce(!event.inputs_.empty());\n\n        /* Prepare output matrix */\n        for (auto &output : event.outputs_) {\n            matrixUpdate(output_matrix_, \"output\", output);\n        }\n\n        /* Check output matrix has expected change events */\n        for (auto &output : event.outputs_) {\n            EXPECT_EQ(!!(cooked_matrix_[output.row_] & (1U << output.col_)), directionValue(output.direction_)) << \"Missing event at \" << strTime() << \" expected key \" << output.row_ << \",\" << output.col_ << \" \" << directionLabel(output.direction_) << \"\\ninput_matrix: changed=\" << !event.inputs_.empty() << \"\\n\" << strMatrix(input_matrix_) << \"\\nexpected_matrix:\\n\" << strMatrix(output_matrix_) << \"\\nactual_matrix:\\n\" << strMatrix(cooked_matrix_);\n        }\n\n        /* Check output matrix has no other changes */\n        checkCookedMatrix(!event.inputs_.empty(), \"debounce() cooked matrix does not match expected output matrix\");\n\n        /* Perform some extra iterations of the matrix scan with no changes */\n        for (int i = 0; i < extra_iterations_; i++) {\n            runDebounce(false);\n            checkCookedMatrix(false, \"debounce() modified cooked matrix\");\n        }\n    }\n\n    /* Check that no further changes happen for 1 minute */\n    for (int i = 0; i < 60000; i++) {\n        runDebounce(false);\n        checkCookedMatrix(false, \"debounce() modified cooked matrix\");\n        advance_time(1);\n    }\n\n    debounce_free();\n}\n\nvoid DebounceTest::runDebounce(bool changed) {\n    std::copy(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_));\n    std::copy(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_));\n\n    reset_access_counter();\n\n    bool cooked_changed = debounce(raw_matrix_, cooked_matrix_, MATRIX_ROWS, changed);\n\n    if (!std::equal(std::begin(input_matrix_), std::end(input_matrix_), std::begin(raw_matrix_))) {\n        FAIL() << \"Fatal error: debounce() modified raw matrix at \" << strTime() << \"\\ninput_matrix: changed=\" << changed << \"\\n\" << strMatrix(input_matrix_) << \"\\nraw_matrix:\\n\" << strMatrix(raw_matrix_);\n    }\n\n    if (std::equal(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_)) == cooked_changed) {\n        FAIL() << \"Fatal error: debounce() reported a wrong cooked matrix change result at \" << strTime() << \"\\noutput_matrix: cooked_changed=\" << cooked_changed << \"\\n\" << strMatrix(output_matrix_) << \"\\ncooked_matrix:\\n\" << strMatrix(cooked_matrix_);\n    }\n\n    if (current_access_counter() > 1) {\n        FAIL() << \"Fatal error: debounce() read the timer multiple times, which is not allowed, at \" << strTime() << \"\\ntimer: access_count=\" << current_access_counter() << \"\\noutput_matrix: cooked_changed=\" << cooked_changed << \"\\n\" << strMatrix(output_matrix_) << \"\\ncooked_matrix:\\n\" << strMatrix(cooked_matrix_);\n    }\n}\n\nvoid DebounceTest::checkCookedMatrix(bool changed, const std::string &error_message) {\n    if (!std::equal(std::begin(output_matrix_), std::end(output_matrix_), std::begin(cooked_matrix_))) {\n        FAIL() << \"Unexpected event: \" << error_message << \" at \" << strTime() << \"\\ninput_matrix: changed=\" << changed << \"\\n\" << strMatrix(input_matrix_) << \"\\nexpected_matrix:\\n\" << strMatrix(output_matrix_) << \"\\nactual_matrix:\\n\" << strMatrix(cooked_matrix_);\n    }\n}\n\nstd::string DebounceTest::strTime() {\n    std::stringstream text;\n\n    text << \"time \" << (timer_read_internal() - time_offset_) << \" (extra_iterations=\" << extra_iterations_ << \", auto_advance_time=\" << auto_advance_time_ << \")\";\n\n    return text.str();\n}\n\nstd::string DebounceTest::strMatrix(matrix_row_t matrix[]) {\n    std::stringstream text;\n\n    text << \"\\t\" << std::setw(3) << \"\";\n    for (int col = 0; col < MATRIX_COLS; col++) {\n        text << \" \" << std::setw(2) << col;\n    }\n    text << \"\\n\";\n\n    for (int row = 0; row < MATRIX_ROWS; row++) {\n        text << \"\\t\" << std::setw(2) << row << \":\";\n        for (int col = 0; col < MATRIX_COLS; col++) {\n            text << ((matrix[row] & (1U << col)) ? \" XX\" : \" __\");\n        }\n\n        text << \"\\n\";\n    }\n\n    return text.str();\n}\n\nbool DebounceTest::directionValue(Direction direction) {\n    switch (direction) {\n        case DOWN:\n            return true;\n\n        case UP:\n            return false;\n    }\n}\n\nstd::string DebounceTest::directionLabel(Direction direction) {\n    switch (direction) {\n        case DOWN:\n            return \"DOWN\";\n\n        case UP:\n            return \"UP\";\n    }\n}\n\n/* Modify a matrix and verify that events always specify a change */\nvoid DebounceTest::matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event) {\n    ASSERT_NE(!!(matrix[event.row_] & (1U << event.col_)), directionValue(event.direction_)) << \"Test \" << name << \" at \" << strTime() << \" sets key \" << event.row_ << \",\" << event.col_ << \" \" << directionLabel(event.direction_) << \" but it is already \" << directionLabel(event.direction_) << \"\\n\" << name << \"_matrix:\\n\" << strMatrix(matrix);\n\n    switch (event.direction_) {\n        case DOWN:\n            matrix[event.row_] |= (1U << event.col_);\n            break;\n\n        case UP:\n            matrix[event.row_] &= ~(1U << event.col_);\n            break;\n    }\n}\n\nDebounceTestEvent::DebounceTestEvent(fast_timer_t time, std::initializer_list<MatrixTestEvent> inputs, std::initializer_list<MatrixTestEvent> outputs) : time_(time), inputs_(inputs), outputs_(outputs) {}\n\nMatrixTestEvent::MatrixTestEvent(int row, int col, Direction direction) : row_(row), col_(col), direction_(direction) {}\n"
  },
  {
    "path": "quantum/debounce/tests/debounce_test_common.h",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\n#include <initializer_list>\n#include <list>\n#include <string>\n\nextern \"C\" {\n#include \"matrix.h\"\n#include \"timer.h\"\n}\n\nenum Direction {\n    DOWN,\n    UP,\n};\n\nclass MatrixTestEvent {\n   public:\n    MatrixTestEvent(int row, int col, Direction direction);\n\n    const int       row_;\n    const int       col_;\n    const Direction direction_;\n};\n\nclass DebounceTestEvent {\n   public:\n    // 0, {{0, 1, DOWN}}, {{0, 1, DOWN}})\n    DebounceTestEvent(fast_timer_t time, std::initializer_list<MatrixTestEvent> inputs, std::initializer_list<MatrixTestEvent> outputs);\n\n    const fast_timer_t               time_;\n    const std::list<MatrixTestEvent> inputs_;\n    const std::list<MatrixTestEvent> outputs_;\n};\n\nclass DebounceTest : public ::testing::Test {\n   protected:\n    void addEvents(std::initializer_list<DebounceTestEvent> events);\n    void runEvents();\n\n    fast_timer_t time_offset_      = 7777;\n    bool         time_jumps_       = false;\n    fast_timer_t async_time_jumps_ = 0;\n\n   private:\n    static bool        directionValue(Direction direction);\n    static std::string directionLabel(Direction direction);\n\n    void runEventsInternal();\n    void runDebounce(bool changed);\n    void checkCookedMatrix(bool changed, const std::string &error_message);\n    void matrixUpdate(matrix_row_t matrix[], const std::string &name, const MatrixTestEvent &event);\n\n    std::string strTime();\n    std::string strMatrix(matrix_row_t matrix[]);\n\n    std::list<DebounceTestEvent> events_;\n\n    matrix_row_t input_matrix_[MATRIX_ROWS];\n    matrix_row_t raw_matrix_[MATRIX_ROWS];\n    matrix_row_t cooked_matrix_[MATRIX_ROWS];\n    matrix_row_t output_matrix_[MATRIX_ROWS];\n\n    int  extra_iterations_;\n    bool auto_advance_time_;\n};\n"
  },
  {
    "path": "quantum/debounce/tests/none_tests.cpp",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\n#include \"debounce_test_common.h\"\n\nTEST_F(DebounceTest, OneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {5, {}, {}},\n        /* 0ms delay (fast scan rate) */\n        {5, {{0, 1, UP}}, {{0, 1, UP}}},\n\n        {10, {}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {5, {}, {}},\n        /* 1ms delay */\n        {6, {{0, 1, UP}}, {{0, 1, UP}}},\n\n        {11, {}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {5, {}, {}},\n        /* 2ms delay */\n        {7, {{0, 1, UP}}, {{0, 1, UP}}},\n\n        {12, {}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyTooQuick1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        /* Release key exactly on the debounce time */\n        {5, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyTooQuick2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {5, {}, {}},\n        {6, {{0, 1, UP}}, {{0, 1, UP}}},\n\n        /* Press key exactly on the debounce time */\n        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {{0, 1, UP}}},\n        {2, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {3, {{0, 1, UP}}, {{0, 1, UP}}},\n        {4, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {5, {{0, 1, UP}}, {{0, 1, UP}}},\n        {6, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {11, {{0, 1, UP}}, {{0, 1, UP}}}, /* 5ms after DOWN at time 7 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {5, {}, {}},\n        {6, {{0, 1, UP}}, {{0, 1, UP}}},\n        {7, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {8, {{0, 1, UP}}, {{0, 1, UP}}},\n        {9, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {10, {{0, 1, UP}}, {{0, 1, UP}}},\n        {15, {}, {}}, /* 5ms after UP at time 10 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyLong) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {5, {}, {}},\n\n        {25, {{0, 1, UP}}, {{0, 1, UP}}},\n\n        {30, {}, {}},\n\n        {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {55, {}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysShort) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 2, DOWN}}, {{0, 2, DOWN}}},\n\n        {6, {}, {}},\n\n        {7, {{0, 1, UP}}, {{0, 1, UP}}},\n        {8, {{0, 2, UP}}, {{0, 2, UP}}},\n\n        {13, {}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysSimultaneous1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}},\n\n        {5, {}, {}},\n        {6, {{0, 1, UP}, {0, 2, UP}}, {{0, 1, UP}, {0, 2, UP}}},\n\n        {11, {}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysSimultaneous2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 2, DOWN}}, {{0, 2, DOWN}}},\n\n        {5, {}, {}},\n        {6, {}, {}},\n        {7, {{0, 1, UP}}, {{0, 1, UP}}},\n        {8, {{0, 2, UP}}, {{0, 2, UP}}},\n\n        {13, {}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late */\n        {300, {}, {}},\n        /* Immediately release key */\n        {300, {{0, 1, UP}}, {{0, 1, UP}}},\n\n        {305, {}, {}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late */\n        {300, {}, {}},\n        /* Release key after 1ms */\n        {301, {{0, 1, UP}}, {{0, 1, UP}}},\n\n        {306, {}, {}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Release key before debounce expires */\n        {300, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan4) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is a bit late */\n        {50, {}, {}},\n        /* Release key after 1ms */\n        {51, {{0, 1, UP}}, {{0, 1, UP}}},\n\n        {56, {}, {}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, AsyncTickOneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {5, {}, {}},\n        /* 0ms delay (fast scan rate) */\n        {5, {{0, 1, UP}}, {{0, 1, UP}}},\n\n        {10, {}, {}},\n    });\n    /*\n     * Debounce implementations should never read the timer more than once per invocation\n     */\n    async_time_jumps_ = DEBOUNCE;\n    runEvents();\n}\n"
  },
  {
    "path": "quantum/debounce/tests/rules.mk",
    "content": "# Copyright 2021 Simon Arlott\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nDEBOUNCE_COMMON_DEFS := -DMATRIX_ROWS=4 -DMATRIX_COLS=10 -DDEBOUNCE=5\n\nDEBOUNCE_COMMON_SRC := $(QUANTUM_PATH)/debounce/tests/debounce_test_common.cpp \\\n\t$(PLATFORM_PATH)/timer.c \\\n\t$(PLATFORM_PATH)/$(PLATFORM_KEY)/timer.c\n\ndebounce_none_DEFS := $(DEBOUNCE_COMMON_DEFS)\ndebounce_none_SRC := $(DEBOUNCE_COMMON_SRC) \\\n\t$(QUANTUM_PATH)/debounce/none.c \\\n\t$(QUANTUM_PATH)/debounce/tests/none_tests.cpp\n\ndebounce_sym_defer_g_DEFS := $(DEBOUNCE_COMMON_DEFS)\ndebounce_sym_defer_g_SRC := $(DEBOUNCE_COMMON_SRC) \\\n\t$(QUANTUM_PATH)/debounce/sym_defer_g.c \\\n\t$(QUANTUM_PATH)/debounce/tests/sym_defer_g_tests.cpp\n\ndebounce_sym_defer_pk_DEFS := $(DEBOUNCE_COMMON_DEFS)\ndebounce_sym_defer_pk_SRC := $(DEBOUNCE_COMMON_SRC) \\\n\t$(QUANTUM_PATH)/debounce/sym_defer_pk.c \\\n\t$(QUANTUM_PATH)/debounce/tests/sym_defer_pk_tests.cpp\n\ndebounce_sym_defer_pr_DEFS := $(DEBOUNCE_COMMON_DEFS)\ndebounce_sym_defer_pr_SRC := $(DEBOUNCE_COMMON_SRC) \\\n\t$(QUANTUM_PATH)/debounce/sym_defer_pr.c \\\n\t$(QUANTUM_PATH)/debounce/tests/sym_defer_pr_tests.cpp\n\ndebounce_sym_eager_pk_DEFS := $(DEBOUNCE_COMMON_DEFS)\ndebounce_sym_eager_pk_SRC := $(DEBOUNCE_COMMON_SRC) \\\n\t$(QUANTUM_PATH)/debounce/sym_eager_pk.c \\\n\t$(QUANTUM_PATH)/debounce/tests/sym_eager_pk_tests.cpp\n\ndebounce_sym_eager_pr_DEFS := $(DEBOUNCE_COMMON_DEFS)\ndebounce_sym_eager_pr_SRC := $(DEBOUNCE_COMMON_SRC) \\\n\t$(QUANTUM_PATH)/debounce/sym_eager_pr.c \\\n\t$(QUANTUM_PATH)/debounce/tests/sym_eager_pr_tests.cpp\n\ndebounce_asym_eager_defer_pk_DEFS := $(DEBOUNCE_COMMON_DEFS)\ndebounce_asym_eager_defer_pk_SRC := $(DEBOUNCE_COMMON_SRC) \\\n\t$(QUANTUM_PATH)/debounce/asym_eager_defer_pk.c \\\n\t$(QUANTUM_PATH)/debounce/tests/asym_eager_defer_pk_tests.cpp\n"
  },
  {
    "path": "quantum/debounce/tests/sym_defer_g_tests.cpp",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\n#include \"debounce_test_common.h\"\n\nTEST_F(DebounceTest, OneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 0ms delay (fast scan rate) */\n        {5, {{0, 1, UP}}, {}},\n\n        {10, {}, {{0, 1, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 1ms delay */\n        {6, {{0, 1, UP}}, {}},\n\n        {11, {}, {{0, 1, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 2ms delay */\n        {7, {{0, 1, UP}}, {}},\n\n        {12, {}, {{0, 1, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyTooQuick1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        /* Release key exactly on the debounce time */\n        {5, {{0, 1, UP}}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyTooQuick2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        {6, {{0, 1, UP}}, {}},\n\n        /* Press key exactly on the debounce time */\n        {11, {{0, 1, DOWN}}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {1, {{0, 1, UP}}, {}},\n        {2, {{0, 1, DOWN}}, {}},\n        {3, {{0, 1, UP}}, {}},\n        {4, {{0, 1, DOWN}}, {}},\n        {5, {{0, 1, UP}}, {}},\n        {6, {{0, 1, DOWN}}, {}},\n        {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {5, {}, {{0, 1, DOWN}}},\n        {6, {{0, 1, UP}}, {}},\n        {7, {{0, 1, DOWN}}, {}},\n        {8, {{0, 1, UP}}, {}},\n        {9, {{0, 1, DOWN}}, {}},\n        {10, {{0, 1, UP}}, {}},\n        {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyLong) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n\n        {25, {{0, 1, UP}}, {}},\n\n        {30, {}, {{0, 1, UP}}},\n\n        {50, {{0, 1, DOWN}}, {}},\n\n        {55, {}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysShort) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {1, {{0, 2, DOWN}}, {}},\n\n        {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},\n\n        {7, {{0, 1, UP}}, {}},\n        {8, {{0, 2, UP}}, {}},\n\n        {13, {}, {{0, 1, UP}, {0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysSimultaneous1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},\n        {6, {{0, 1, UP}, {0, 2, UP}}, {}},\n\n        {11, {}, {{0, 1, UP}, {0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysSimultaneous2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {1, {{0, 2, DOWN}}, {}},\n\n        {5, {}, {}},\n        {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},\n        {7, {{0, 1, UP}}, {}},\n        {8, {{0, 2, UP}}, {}},\n\n        {13, {}, {{0, 1, UP}, {0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Processing is very late */\n        {300, {}, {{0, 1, DOWN}}},\n        /* Immediately release key */\n        {300, {{0, 1, UP}}, {}},\n\n        {305, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Processing is very late */\n        {300, {}, {{0, 1, DOWN}}},\n        /* Release key after 1ms */\n        {301, {{0, 1, UP}}, {}},\n\n        {306, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Release key before debounce expires */\n        {300, {{0, 1, UP}}, {}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan4) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Processing is a bit late */\n        {50, {}, {{0, 1, DOWN}}},\n        /* Release key after 1ms */\n        {51, {{0, 1, UP}}, {}},\n\n        {56, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, AsyncTickOneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 0ms delay (fast scan rate) */\n        {5, {{0, 1, UP}}, {}},\n\n        {10, {}, {{0, 1, UP}}},\n    });\n    /*\n     * Debounce implementations should never read the timer more than once per invocation\n     */\n    async_time_jumps_ = DEBOUNCE;\n    runEvents();\n}\n"
  },
  {
    "path": "quantum/debounce/tests/sym_defer_pk_tests.cpp",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\n#include \"debounce_test_common.h\"\n\nTEST_F(DebounceTest, OneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 0ms delay (fast scan rate) */\n        {5, {{0, 1, UP}}, {}},\n\n        {10, {}, {{0, 1, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 1ms delay */\n        {6, {{0, 1, UP}}, {}},\n\n        {11, {}, {{0, 1, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 2ms delay */\n        {7, {{0, 1, UP}}, {}},\n\n        {12, {}, {{0, 1, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyTooQuick1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        /* Release key exactly on the debounce time */\n        {5, {{0, 1, UP}}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyTooQuick2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        {6, {{0, 1, UP}}, {}},\n\n        /* Press key exactly on the debounce time */\n        {11, {{0, 1, DOWN}}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {1, {{0, 1, UP}}, {}},\n        {2, {{0, 1, DOWN}}, {}},\n        {3, {{0, 1, UP}}, {}},\n        {4, {{0, 1, DOWN}}, {}},\n        {5, {{0, 1, UP}}, {}},\n        {6, {{0, 1, DOWN}}, {}},\n        {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {5, {}, {{0, 1, DOWN}}},\n        {6, {{0, 1, UP}}, {}},\n        {7, {{0, 1, DOWN}}, {}},\n        {8, {{0, 1, UP}}, {}},\n        {9, {{0, 1, DOWN}}, {}},\n        {10, {{0, 1, UP}}, {}},\n        {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyLong) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n\n        {25, {{0, 1, UP}}, {}},\n\n        {30, {}, {{0, 1, UP}}},\n\n        {50, {{0, 1, DOWN}}, {}},\n\n        {55, {}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysShort) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {1, {{0, 2, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        {6, {}, {{0, 2, DOWN}}},\n\n        {7, {{0, 1, UP}}, {}},\n        {8, {{0, 2, UP}}, {}},\n\n        {12, {}, {{0, 1, UP}}},\n        {13, {}, {{0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysSimultaneous1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},\n        {6, {{0, 1, UP}, {0, 2, UP}}, {}},\n\n        {11, {}, {{0, 1, UP}, {0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysSimultaneous2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {1, {{0, 2, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        {6, {{0, 1, UP}}, {{0, 2, DOWN}}},\n        {7, {{0, 2, UP}}, {}},\n\n        {11, {}, {{0, 1, UP}}},\n        {12, {}, {{0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Processing is very late */\n        {300, {}, {{0, 1, DOWN}}},\n        /* Immediately release key */\n        {300, {{0, 1, UP}}, {}},\n\n        {305, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Processing is very late */\n        {300, {}, {{0, 1, DOWN}}},\n        /* Release key after 1ms */\n        {301, {{0, 1, UP}}, {}},\n\n        {306, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Release key before debounce expires */\n        {300, {{0, 1, UP}}, {}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan4) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Processing is a bit late */\n        {50, {}, {{0, 1, DOWN}}},\n        /* Release key after 1ms */\n        {51, {{0, 1, UP}}, {}},\n\n        {56, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, AsyncTickOneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 0ms delay (fast scan rate) */\n        {5, {{0, 1, UP}}, {}},\n\n        {10, {}, {{0, 1, UP}}},\n    });\n    /*\n     * Debounce implementations should never read the timer more than once per invocation\n     */\n    async_time_jumps_ = DEBOUNCE;\n    runEvents();\n}\n"
  },
  {
    "path": "quantum/debounce/tests/sym_defer_pr_tests.cpp",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\n#include \"debounce_test_common.h\"\n\nTEST_F(DebounceTest, OneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 0ms delay (fast scan rate) */\n        {5, {{0, 1, UP}}, {}},\n\n        {10, {}, {{0, 1, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 1ms delay */\n        {6, {{0, 1, UP}}, {}},\n\n        {11, {}, {{0, 1, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 2ms delay */\n        {7, {{0, 1, UP}}, {}},\n\n        {12, {}, {{0, 1, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyTooQuick1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        /* Release key exactly on the debounce time */\n        {5, {{0, 1, UP}}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyTooQuick2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        {6, {{0, 1, UP}}, {}},\n\n        /* Press key exactly on the debounce time */\n        {11, {{0, 1, DOWN}}, {}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {1, {{0, 1, UP}}, {}},\n        {2, {{0, 1, DOWN}}, {}},\n        {3, {{0, 1, UP}}, {}},\n        {4, {{0, 1, DOWN}}, {}},\n        {5, {{0, 1, UP}}, {}},\n        {6, {{0, 1, DOWN}}, {}},\n        {11, {}, {{0, 1, DOWN}}}, /* 5ms after DOWN at time 7 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {5, {}, {{0, 1, DOWN}}},\n        {6, {{0, 1, UP}}, {}},\n        {7, {{0, 1, DOWN}}, {}},\n        {8, {{0, 1, UP}}, {}},\n        {9, {{0, 1, DOWN}}, {}},\n        {10, {{0, 1, UP}}, {}},\n        {15, {}, {{0, 1, UP}}}, /* 5ms after UP at time 10 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyLong) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n\n        {25, {{0, 1, UP}}, {}},\n\n        {30, {}, {{0, 1, UP}}},\n\n        {50, {{0, 1, DOWN}}, {}},\n\n        {55, {}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysShort) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {1, {{0, 2, DOWN}}, {}},\n\n        {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},\n\n        {7, {{0, 1, UP}}, {}},\n        {8, {{0, 2, UP}}, {}},\n\n        {13, {}, {{0, 1, UP}, {0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysSimultaneous1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},\n        {6, {{0, 1, UP}, {0, 2, UP}}, {}},\n\n        {11, {}, {{0, 1, UP}, {0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysSimultaneous2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n        {1, {{0, 2, DOWN}}, {}},\n\n        {6, {}, {{0, 1, DOWN}, {0, 2, DOWN}}},\n        {7, {{0, 2, UP}}, {}},\n        {9, {{0, 1, UP}}, {}},\n\n        // Debouncing loses the specific ordering -- both events report simultaneously.\n        {14, {}, {{0, 1, UP}, {0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Processing is very late */\n        {300, {}, {{0, 1, DOWN}}},\n        /* Immediately release key */\n        {300, {{0, 1, UP}}, {}},\n\n        {305, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Processing is very late */\n        {300, {}, {{0, 1, DOWN}}},\n        /* Release key after 1ms */\n        {301, {{0, 1, UP}}, {}},\n\n        {306, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Release key before debounce expires */\n        {300, {{0, 1, UP}}, {}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan4) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        /* Processing is a bit late */\n        {50, {}, {{0, 1, DOWN}}},\n        /* Release key after 1ms */\n        {51, {{0, 1, UP}}, {}},\n\n        {56, {}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, AsyncTickOneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {}},\n\n        {5, {}, {{0, 1, DOWN}}},\n        /* 0ms delay (fast scan rate) */\n        {5, {{0, 1, UP}}, {}},\n\n        {10, {}, {{0, 1, UP}}},\n    });\n    /*\n     * Debounce implementations should never read the timer more than once per invocation\n     */\n    async_time_jumps_ = DEBOUNCE;\n    runEvents();\n}\n"
  },
  {
    "path": "quantum/debounce/tests/sym_eager_pk_tests.cpp",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\n#include \"debounce_test_common.h\"\n\nTEST_F(DebounceTest, OneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {6, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 2ms delay (debounce has not yet finished) */\n        {7, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 3ms delay (debounce has not yet finished) */\n        {8, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort4) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 4ms delay (debounce has not yet finished) */\n        {9, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort5) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 5ms delay (debounce has finished) */\n        {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort6) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key after after 6ms delay (debounce has finished) */\n        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n        {2, {{0, 1, DOWN}}, {}},\n        {3, {{0, 1, UP}}, {}},\n        {4, {{0, 1, DOWN}}, {}},\n        {5, {{0, 1, UP}}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {6, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        /* Change twice in the same time period */\n        {1, {{0, 1, UP}}, {}},\n        {1, {{0, 1, DOWN}}, {}},\n        /* Change three times in the same time period */\n        {2, {{0, 1, UP}}, {}},\n        {2, {{0, 1, DOWN}}, {}},\n        {2, {{0, 1, UP}}, {}},\n        /* Change three times in the same time period */\n        {3, {{0, 1, DOWN}}, {}},\n        {3, {{0, 1, UP}}, {}},\n        {3, {{0, 1, DOWN}}, {}},\n        /* Change twice in the same time period */\n        {4, {{0, 1, UP}}, {}},\n        {4, {{0, 1, DOWN}}, {}},\n        {5, {{0, 1, UP}}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {6, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyLong) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {25, {{0, 1, UP}}, {{0, 1, UP}}},\n\n        {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysShort) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n        {2, {{0, 2, DOWN}}, {{0, 2, DOWN}}},\n        {3, {{0, 2, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {6, {{0, 1, DOWN}}, {}},\n        {7, {}, {{0, 2, UP}}},\n\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {9, {{0, 2, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n\n        {12, {}, {{0, 2, DOWN}}}, /* 5ms after UP at time 7 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late but the change will now be accepted */\n        {300, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late but the change will now be accepted even with a 1 scan delay */\n        {300, {}, {}},\n        {300, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late but the change will now be accepted even with a 1ms delay */\n        {300, {}, {}},\n        {301, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan4) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is a bit late but the change will now be accepted */\n        {50, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan5) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late but the change will now be accepted even with a 1 scan delay */\n        {50, {}, {}},\n        {50, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan6) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late but the change will now be accepted even with a 1ms delay */\n        {50, {}, {}},\n        {51, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, AsyncTickOneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {6, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    /*\n     * Debounce implementations should never read the timer more than once per invocation\n     */\n    async_time_jumps_ = DEBOUNCE;\n    runEvents();\n}\n"
  },
  {
    "path": "quantum/debounce/tests/sym_eager_pr_tests.cpp",
    "content": "/* Copyright 2021 Simon Arlott\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\n#include \"debounce_test_common.h\"\n\nTEST_F(DebounceTest, OneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {6, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 2ms delay (debounce has not yet finished) */\n        {7, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 3ms delay (debounce has not yet finished) */\n        {8, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort4) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 4ms delay (debounce has not yet finished) */\n        {9, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort5) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 5ms delay (debounce has finished) */\n        {10, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyShort6) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key after after 6ms delay (debounce has finished) */\n        {11, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n        {2, {{0, 1, DOWN}}, {}},\n        {3, {{0, 1, UP}}, {}},\n        {4, {{0, 1, DOWN}}, {}},\n        {5, {{0, 1, UP}}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {6, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyBouncing2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        /* Change twice in the same time period */\n        {1, {{0, 1, UP}}, {}},\n        {1, {{0, 1, DOWN}}, {}},\n        /* Change three times in the same time period */\n        {2, {{0, 1, UP}}, {}},\n        {2, {{0, 1, DOWN}}, {}},\n        {2, {{0, 1, UP}}, {}},\n        /* Change three times in the same time period */\n        {3, {{0, 1, DOWN}}, {}},\n        {3, {{0, 1, UP}}, {}},\n        {3, {{0, 1, DOWN}}, {}},\n        /* Change twice in the same time period */\n        {4, {{0, 1, UP}}, {}},\n        {4, {{0, 1, DOWN}}, {}},\n        {5, {{0, 1, UP}}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {6, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyLong) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        {25, {{0, 1, UP}}, {{0, 1, UP}}},\n\n        {50, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoRowsShort) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n        {2, {{2, 0, DOWN}}, {{2, 0, DOWN}}},\n        {3, {{2, 0, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {6, {{0, 1, DOWN}}, {}},\n        {7, {}, {{2, 0, UP}}},\n\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {9, {{2, 0, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n\n        {12, {}, {{2, 0, DOWN}}}, /* 5ms after UP at time 7 */\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysOverlap) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n        /* Press a second key during the first debounce */\n        {2, {{0, 2, DOWN}}, {}},\n\n        /* Key registers as soon as debounce finishes, 5ms after time 0 */\n        {5, {}, {{0, 1, UP}, {0, 2, DOWN}}},\n        {6, {{0, 1, DOWN}}, {}},\n\n        /* Key registers as soon as debounce finishes, 5ms after time 5 */\n        {10, {}, {{0, 1, DOWN}}},\n        /* Release both keys */\n        {11, {{0, 1, UP}}, {}},\n        {12, {{0, 2, UP}}, {}},\n\n        /* Keys register as soon as debounce finishes, 5ms after time 10 */\n        {15, {}, {{0, 1, UP}, {0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysSimultaneous1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}},\n        {20, {{0, 1, UP}}, {{0, 1, UP}}},\n        {21, {{0, 2, UP}}, {}},\n\n        /* Key registers as soon as debounce finishes, 5ms after time 20 */\n        {25, {}, {{0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, TwoKeysSimultaneous2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}, {0, 2, DOWN}}, {{0, 1, DOWN}, {0, 2, DOWN}}},\n        {20, {{0, 1, UP}, {0, 2, UP}}, {{0, 1, UP}, {0, 2, UP}}},\n    });\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late but the change will now be accepted */\n        {300, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan2) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late but the change will now be accepted even with a 1 scan delay */\n        {300, {}, {}},\n        {300, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan3) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late but the change will now be accepted even with a 1ms delay */\n        {300, {}, {}},\n        {301, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan4) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is a bit late but the change will now be accepted */\n        {50, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan5) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late but the change will now be accepted even with a 1 scan delay */\n        {50, {}, {}},\n        {50, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, OneKeyDelayedScan6) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n\n        /* Processing is very late but the change will now be accepted even with a 1ms delay */\n        {50, {}, {}},\n        {51, {{0, 1, UP}}, {{0, 1, UP}}},\n    });\n    time_jumps_ = true;\n    runEvents();\n}\n\nTEST_F(DebounceTest, AsyncTickOneKeyShort1) {\n    addEvents({\n        /* Time, Inputs, Outputs */\n        {0, {{0, 1, DOWN}}, {{0, 1, DOWN}}},\n        {1, {{0, 1, UP}}, {}},\n\n        {5, {}, {{0, 1, UP}}},\n        /* Press key again after 1ms delay (debounce has not yet finished) */\n        {6, {{0, 1, DOWN}}, {}},\n        {10, {}, {{0, 1, DOWN}}}, /* 5ms after UP at time 5 */\n    });\n    /*\n     * Debounce implementations should never read the timer more than once per invocation\n     */\n    async_time_jumps_ = DEBOUNCE;\n    runEvents();\n}\n"
  },
  {
    "path": "quantum/debounce/tests/testlist.mk",
    "content": "TEST_LIST += \\\n\tdebounce_none \\\n\tdebounce_sym_defer_g \\\n\tdebounce_sym_defer_pk \\\n\tdebounce_sym_defer_pr \\\n\tdebounce_sym_eager_pk \\\n\tdebounce_sym_eager_pr \\\n\tdebounce_asym_eager_defer_pk\n"
  },
  {
    "path": "quantum/debounce.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"matrix.h\"\n\n/**\n * @brief Debounce raw matrix events according to the choosen debounce algorithm.\n *\n * @param raw The current key state\n * @param cooked The debounced key state\n * @param num_rows Number of rows to debounce\n * @param changed True if raw has changed since the last call\n * @return true Cooked has new keychanges after debouncing\n * @return false Cooked is the same as before\n */\nbool debounce(matrix_row_t raw[], matrix_row_t cooked[], uint8_t num_rows, bool changed);\n\nvoid debounce_init(uint8_t num_rows);\n\nvoid debounce_free(void);\n"
  },
  {
    "path": "quantum/deferred_exec.c",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <stddef.h>\n#include <timer.h>\n#include <deferred_exec.h>\n\n#ifndef MAX_DEFERRED_EXECUTORS\n#    define MAX_DEFERRED_EXECUTORS 8\n#endif\n\n//------------------------------------\n// Helpers\n//\n\nstatic deferred_token current_token = 0;\n\nstatic inline bool token_can_be_used(deferred_executor_t *table, size_t table_count, deferred_token token) {\n    if (token == INVALID_DEFERRED_TOKEN) {\n        return false;\n    }\n    for (int i = 0; i < table_count; ++i) {\n        if (table[i].token == token) {\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic inline deferred_token allocate_token(deferred_executor_t *table, size_t table_count) {\n    deferred_token first = ++current_token;\n    while (!token_can_be_used(table, table_count, current_token)) {\n        ++current_token;\n        if (current_token == first) {\n            // If we've looped back around to the first, everything is already allocated (yikes!). Need to exit with a failure.\n            return INVALID_DEFERRED_TOKEN;\n        }\n    }\n    return current_token;\n}\n\n//------------------------------------\n// Advanced API: used when a custom-allocated table is used, primarily for core code.\n//\n\ndeferred_token defer_exec_advanced(deferred_executor_t *table, size_t table_count, uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg) {\n    // Ignore queueing if the table isn't valid, it's a zero-time delay, or the token is not valid\n    if (!table || table_count == 0 || delay_ms == 0 || !callback) {\n        return INVALID_DEFERRED_TOKEN;\n    }\n\n    // Find an unused slot and claim it\n    for (int i = 0; i < table_count; ++i) {\n        deferred_executor_t *entry = &table[i];\n        if (entry->token == INVALID_DEFERRED_TOKEN) {\n            // Work out the new token value, dropping out if none were available\n            deferred_token token = allocate_token(table, table_count);\n            if (token == INVALID_DEFERRED_TOKEN) {\n                return false;\n            }\n\n            // Set up the executor table entry\n            entry->token        = current_token;\n            entry->trigger_time = timer_read32() + delay_ms;\n            entry->callback     = callback;\n            entry->cb_arg       = cb_arg;\n            return current_token;\n        }\n    }\n\n    // None available\n    return INVALID_DEFERRED_TOKEN;\n}\n\nbool extend_deferred_exec_advanced(deferred_executor_t *table, size_t table_count, deferred_token token, uint32_t delay_ms) {\n    // Ignore queueing if the table isn't valid, it's a zero-time delay, or the token is not valid\n    if (!table || table_count == 0 || delay_ms == 0 || token == INVALID_DEFERRED_TOKEN) {\n        return false;\n    }\n\n    // Find the entry corresponding to the token\n    for (int i = 0; i < table_count; ++i) {\n        deferred_executor_t *entry = &table[i];\n        if (entry->token == token) {\n            // Found it, extend the delay\n            entry->trigger_time = timer_read32() + delay_ms;\n            return true;\n        }\n    }\n\n    // Not found\n    return false;\n}\n\nbool cancel_deferred_exec_advanced(deferred_executor_t *table, size_t table_count, deferred_token token) {\n    // Ignore request if the table/token are not valid\n    if (!table || table_count == 0 || token == INVALID_DEFERRED_TOKEN) {\n        return false;\n    }\n\n    // Find the entry corresponding to the token\n    for (int i = 0; i < table_count; ++i) {\n        deferred_executor_t *entry = &table[i];\n        if (entry->token == token) {\n            // Found it, cancel and clear the table entry\n            entry->token        = INVALID_DEFERRED_TOKEN;\n            entry->trigger_time = 0;\n            entry->callback     = NULL;\n            entry->cb_arg       = NULL;\n            return true;\n        }\n    }\n\n    // Not found\n    return false;\n}\n\nvoid deferred_exec_advanced_task(deferred_executor_t *table, size_t table_count, uint32_t *last_execution_time) {\n    uint32_t now = timer_read32();\n\n    // Throttle only once per millisecond\n    if (((int32_t)TIMER_DIFF_32(now, (*last_execution_time))) > 0) {\n        *last_execution_time = now;\n\n        // Run through each of the executors\n        for (int i = 0; i < table_count; ++i) {\n            deferred_executor_t *entry      = &table[i];\n            deferred_token       curr_token = entry->token;\n\n            // Check if we're supposed to execute this entry\n            if (curr_token != INVALID_DEFERRED_TOKEN && ((int32_t)TIMER_DIFF_32(entry->trigger_time, now)) <= 0) {\n                // Invoke the callback and work work out if we should be requeued\n                uint32_t delay_ms = entry->callback(entry->trigger_time, entry->cb_arg);\n\n                // If the token has changed, then the callback has canceled and re-queued. Skip further processing.\n                if (entry->token != curr_token) {\n                    continue;\n                }\n\n                // Update the trigger time if we have to repeat, otherwise clear it out\n                if (delay_ms > 0) {\n                    // Intentionally add just the delay to the existing trigger time -- this ensures the next\n                    // invocation is with respect to the previous trigger, rather than when it got to execution. Under\n                    // normal circumstances this won't cause issue, but if another executor is invoked that takes a\n                    // considerable length of time, then this ensures best-effort timing between invocations.\n                    entry->trigger_time += delay_ms;\n                } else {\n                    // If it was zero, then the callback is cancelling repeated execution. Free up the slot.\n                    entry->token        = INVALID_DEFERRED_TOKEN;\n                    entry->trigger_time = 0;\n                    entry->callback     = NULL;\n                    entry->cb_arg       = NULL;\n                }\n            }\n        }\n    }\n}\n\n//------------------------------------\n// Basic API: used by user-mode code, guaranteed to not collide with core deferred execution\n//\n\nstatic uint32_t            last_deferred_exec_check                = 0;\nstatic deferred_executor_t basic_executors[MAX_DEFERRED_EXECUTORS] = {0};\n\ndeferred_token defer_exec(uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg) {\n    return defer_exec_advanced(basic_executors, MAX_DEFERRED_EXECUTORS, delay_ms, callback, cb_arg);\n}\nbool extend_deferred_exec(deferred_token token, uint32_t delay_ms) {\n    return extend_deferred_exec_advanced(basic_executors, MAX_DEFERRED_EXECUTORS, token, delay_ms);\n}\nbool cancel_deferred_exec(deferred_token token) {\n    return cancel_deferred_exec_advanced(basic_executors, MAX_DEFERRED_EXECUTORS, token);\n}\nvoid deferred_exec_task(void) {\n    deferred_exec_advanced_task(basic_executors, MAX_DEFERRED_EXECUTORS, &last_deferred_exec_check);\n}\n"
  },
  {
    "path": "quantum/deferred_exec.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdlib.h>\n\n//------------------------------------\n// Common\n//------------------------------------\n\n/**\n * @typedef A token that can be used to cancel or extend an existing deferred execution.\n */\ntypedef uint8_t deferred_token;\n\n/**\n * @def The constant used to denote an invalid deferred execution token.\n */\n#define INVALID_DEFERRED_TOKEN 0\n\n/**\n * @typedef Callback to execute.\n * @param trigger_time[in] the intended trigger time to execute the callback -- equivalent time-space as timer_read32()\n * @param cb_arg[in] the callback argument specified when enqueueing the deferred executor\n * @return non-zero re-queues the callback to execute after the returned number of milliseconds. Zero cancels repeated execution.\n */\ntypedef uint32_t (*deferred_exec_callback)(uint32_t trigger_time, void *cb_arg);\n\n//------------------------------------\n// Basic API: used by user-mode code, guaranteed to not collide with core deferred execution\n//------------------------------------\n\n/**\n * Configures the supplied deferred executor to be executed after the required number of milliseconds.\n *\n * @param delay_ms[in] the number of milliseconds before executing the callback\n * @param callback[in] the executor to invoke\n * @param cb_arg[in] the argument to pass to the executor, may be NULL if unused by the executor\n * @return a token usable for extension/cancellation, or INVALID_DEFERRED_TOKEN if an error occurred\n */\ndeferred_token defer_exec(uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg);\n\n/**\n * Allows for extending the timeframe before an existing deferred execution is invoked.\n *\n * @param token[in] the returned value from defer_exec for the deferred execution you wish to extend\n * @param delay_ms[in] the number of milliseconds before executing the callback\n * @return true if the token was extended successfully, otherwise false\n */\nbool extend_deferred_exec(deferred_token token, uint32_t delay_ms);\n\n/**\n * Allows for cancellation of an existing deferred execution.\n *\n * @param token[in] the returned value from defer_exec for the deferred execution you wish to cancel\n * @return true if the token was cancelled successfully, otherwise false\n */\nbool cancel_deferred_exec(deferred_token token);\n\n/**\n * Forward declaration for the main loop in order to execute any deferred executors. Should not be invoked by keyboard/user code.\n */\nvoid deferred_exec_task(void);\n\n//------------------------------------\n// Advanced API: used when a custom-allocated table is used, primarily for core code.\n//------------------------------------\n\n/**\n * @struct Structure for containing self-hosted deferred executor tables.\n * @brief Core-side code can use this to create their own tables without impacting on the use of users' ability to add deferred execution.\n *        Code outside deferred_exec.c should not worry about internals of this struct, and should just allocate the required number in an array.\n */\ntypedef struct deferred_executor_t {\n    deferred_token         token;\n    uint32_t               trigger_time;\n    deferred_exec_callback callback;\n    void *                 cb_arg;\n} deferred_executor_t;\n\n/**\n * Configures the supplied deferred executor to be executed after the required number of milliseconds.\n *\n * @param table[in] the custom table used for storage\n * @param table_count[in] the number of available items in the table\n * @param delay_ms[in] the number of milliseconds before executing the callback\n * @param callback[in] the executor to invoke\n * @param cb_arg[in] the argument to pass to the executor, may be NULL if unused by the executor\n * @return a token usable for extension/cancellation, or INVALID_DEFERRED_TOKEN if an error occurred\n */\ndeferred_token defer_exec_advanced(deferred_executor_t *table, size_t table_count, uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg);\n\n/**\n * Allows for extending the timeframe before an existing deferred execution is invoked.\n *\n * @param token[in] the returned value from defer_exec for the deferred execution you wish to extend\n * @param delay_ms[in] the number of milliseconds before executing the callback\n * @return true if the token was extended successfully, otherwise false\n */\nbool extend_deferred_exec_advanced(deferred_executor_t *table, size_t table_count, deferred_token token, uint32_t delay_ms);\n\n/**\n * Allows for cancellation of an existing deferred execution.\n *\n * @param token[in] the returned value from defer_exec for the deferred execution you wish to cancel\n * @return true if the token was cancelled successfully, otherwise false\n */\nbool cancel_deferred_exec_advanced(deferred_executor_t *table, size_t table_count, deferred_token token);\n\n/**\n * Forward declaration for the main loop in order to execute any custom table deferred executors. Should not be invoked by keyboard/user code.\n * Needed for any custom-allocated deferred execution tables. Any core tasks should add appropriate invocation to quantum/main.c.\n *\n * @param table[in] the custom table used for storage\n * @param table_count[in] the number of available items in the table\n * @param last_execution_time[in,out] the last execution time -- this will be checked first to determine if execution is needed, and updated if execution occurred\n */\nvoid deferred_exec_advanced_task(deferred_executor_t *table, size_t table_count, uint32_t *last_execution_time);\n"
  },
  {
    "path": "quantum/digitizer.c",
    "content": "/* Copyright 2021\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"digitizer.h\"\n\ndigitizer_t digitizer_state = {\n    .in_range = false,\n    .tip      = false,\n    .barrel   = false,\n    .x        = 0,\n    .y        = 0,\n    .dirty    = false,\n};\n\nvoid digitizer_flush(void) {\n    if (digitizer_state.dirty) {\n        host_digitizer_send(&digitizer_state);\n        digitizer_state.dirty = false;\n    }\n}\n\nvoid digitizer_in_range_on(void) {\n    digitizer_state.in_range = true;\n    digitizer_state.dirty    = true;\n    digitizer_flush();\n}\n\nvoid digitizer_in_range_off(void) {\n    digitizer_state.in_range = false;\n    digitizer_state.dirty    = true;\n    digitizer_flush();\n}\n\nvoid digitizer_tip_switch_on(void) {\n    digitizer_state.tip   = true;\n    digitizer_state.dirty = true;\n    digitizer_flush();\n}\n\nvoid digitizer_tip_switch_off(void) {\n    digitizer_state.tip   = false;\n    digitizer_state.dirty = true;\n    digitizer_flush();\n}\n\nvoid digitizer_barrel_switch_on(void) {\n    digitizer_state.barrel = true;\n    digitizer_state.dirty  = true;\n    digitizer_flush();\n}\n\nvoid digitizer_barrel_switch_off(void) {\n    digitizer_state.barrel = false;\n    digitizer_state.dirty  = true;\n    digitizer_flush();\n}\n\nvoid digitizer_set_position(float x, float y) {\n    digitizer_state.x     = x;\n    digitizer_state.y     = y;\n    digitizer_state.dirty = true;\n    digitizer_flush();\n}\n"
  },
  {
    "path": "quantum/digitizer.h",
    "content": "/* Copyright 2021\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n\n/**\n * \\file\n *\n * defgroup digitizer HID Digitizer\n * \\{\n */\n\ntypedef struct {\n    bool  in_range : 1;\n    bool  tip : 1;\n    bool  barrel : 1;\n    float x;\n    float y;\n    bool  dirty;\n} digitizer_t;\n\nextern digitizer_t digitizer_state;\n\n/**\n * \\brief Send the digitizer report to the host if it is marked as dirty.\n */\nvoid digitizer_flush(void);\n\n/**\n * \\brief Assert the \"in range\" indicator, and flush the report.\n */\nvoid digitizer_in_range_on(void);\n\n/**\n * \\brief Deassert the \"in range\" indicator, and flush the report.\n */\nvoid digitizer_in_range_off(void);\n\n/**\n * \\brief Assert the tip switch, and flush the report.\n */\nvoid digitizer_tip_switch_on(void);\n\n/**\n * \\brief Deassert the tip switch, and flush the report.\n */\nvoid digitizer_tip_switch_off(void);\n\n/**\n * \\brief Assert the barrel switch, and flush the report.\n */\nvoid digitizer_barrel_switch_on(void);\n\n/**\n * \\brief Deassert the barrel switch, and flush the report.\n */\nvoid digitizer_barrel_switch_off(void);\n\n/**\n * \\brief Set the absolute X and Y position of the digitizer contact, and flush the report.\n *\n * \\param x The X value of the contact position, from 0 to 1.\n * \\param y The Y value of the contact position, from 0 to 1.\n */\nvoid digitizer_set_position(float x, float y);\n\nvoid host_digitizer_send(digitizer_t *digitizer);\n\n/** \\} */\n"
  },
  {
    "path": "quantum/dip_switch.c",
    "content": "/*\n * Copyright 2018 Jack Humbert <jack.humb@gmail.com>\n * Copyright 2019 Drashna Jaelre (Christopher Courtney) <drashna@live.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <string.h> // for memcpy\n\n#include \"dip_switch.h\"\n\n#ifdef SPLIT_KEYBOARD\n#    include \"split_common/split_util.h\"\n#endif\n\n#if !defined(DIP_SWITCH_PINS) && !defined(DIP_SWITCH_MATRIX_GRID)\n#    error \"Either DIP_SWITCH_PINS or DIP_SWITCH_MATRIX_GRID must be defined.\"\n#endif\n\n#if defined(DIP_SWITCH_PINS) && defined(DIP_SWITCH_MATRIX_GRID)\n#    error \"Both DIP_SWITCH_PINS and DIP_SWITCH_MATRIX_GRID are defined.\"\n#endif\n\n#ifdef DIP_SWITCH_PINS\nstatic pin_t dip_switch_pad[] = DIP_SWITCH_PINS;\n#endif\n\n#ifdef DIP_SWITCH_MATRIX_GRID\nstatic matrix_intersection_t dip_switch_pad[] = DIP_SWITCH_MATRIX_GRID;\nextern bool                  peek_matrix(uint8_t row_index, uint8_t col_index, bool read_raw);\nstatic uint16_t              scan_count;\n#endif /* DIP_SWITCH_MATRIX_GRID */\n\nstatic bool dip_switch_state[NUM_DIP_SWITCHES]      = {0};\nstatic bool last_dip_switch_state[NUM_DIP_SWITCHES] = {0};\n\n__attribute__((weak)) bool dip_switch_update_user(uint8_t index, bool active) {\n    return true;\n}\n\n__attribute__((weak)) bool dip_switch_update_kb(uint8_t index, bool active) {\n    return dip_switch_update_user(index, active);\n}\n\n__attribute__((weak)) bool dip_switch_update_mask_user(uint32_t state) {\n    return true;\n}\n\n__attribute__((weak)) bool dip_switch_update_mask_kb(uint32_t state) {\n    return dip_switch_update_mask_user(state);\n}\n\n#ifdef DIP_SWITCH_MAP_ENABLE\n#    include \"keymap_introspection.h\"\n#    include \"action.h\"\n#    include \"wait.h\"\n\n#    ifndef DIP_SWITCH_MAP_KEY_DELAY\n#        define DIP_SWITCH_MAP_KEY_DELAY TAP_CODE_DELAY\n#    endif\n\nstatic void dip_switch_exec_mapping(uint8_t index, bool on) {\n    // The delays below cater for Windows and its wonderful requirements.\n    action_exec(on ? MAKE_DIPSWITCH_ON_EVENT(index, true) : MAKE_DIPSWITCH_OFF_EVENT(index, true));\n#    if DIP_SWITCH_MAP_KEY_DELAY > 0\n    wait_ms(DIP_SWITCH_MAP_KEY_DELAY);\n#    endif // DIP_SWITCH_MAP_KEY_DELAY > 0\n\n    action_exec(on ? MAKE_DIPSWITCH_ON_EVENT(index, false) : MAKE_DIPSWITCH_OFF_EVENT(index, false));\n#    if DIP_SWITCH_MAP_KEY_DELAY > 0\n    wait_ms(DIP_SWITCH_MAP_KEY_DELAY);\n#    endif // DIP_SWITCH_MAP_KEY_DELAY > 0\n}\n#endif // DIP_SWITCH_MAP_ENABLE\n\nvoid dip_switch_init(void) {\n#ifdef DIP_SWITCH_PINS\n#    if defined(SPLIT_KEYBOARD) && defined(DIP_SWITCH_PINS_RIGHT)\n    if (!isLeftHand) {\n        const pin_t dip_switch_pad_right[] = DIP_SWITCH_PINS_RIGHT;\n        for (uint8_t i = 0; i < NUM_DIP_SWITCHES; i++) {\n            dip_switch_pad[i] = dip_switch_pad_right[i];\n        }\n    }\n#    endif\n    for (uint8_t i = 0; i < NUM_DIP_SWITCHES; i++) {\n        gpio_set_pin_input_high(dip_switch_pad[i]);\n    }\n    dip_switch_read(true);\n#endif\n#ifdef DIP_SWITCH_MATRIX_GRID\n    scan_count = 0;\n#endif\n}\n\nvoid dip_switch_read(bool forced) {\n    bool     has_dip_state_changed = false;\n    uint32_t dip_switch_mask       = 0;\n\n#ifdef DIP_SWITCH_MATRIX_GRID\n    bool read_raw = false;\n\n    if (scan_count < 500) {\n        scan_count++;\n        if (scan_count == 10) {\n            read_raw = true;\n            forced   = true; /* First reading of the dip switch */\n        } else {\n            return;\n        }\n    }\n#endif\n\n    for (uint8_t i = 0; i < NUM_DIP_SWITCHES; i++) {\n#ifdef DIP_SWITCH_PINS\n        dip_switch_state[i] = !gpio_read_pin(dip_switch_pad[i]);\n#endif\n#ifdef DIP_SWITCH_MATRIX_GRID\n        dip_switch_state[i] = peek_matrix(dip_switch_pad[i].row, dip_switch_pad[i].col, read_raw);\n#endif\n        dip_switch_mask |= dip_switch_state[i] << i;\n        if (last_dip_switch_state[i] != dip_switch_state[i] || forced) {\n            has_dip_state_changed = true;\n#ifndef DIP_SWITCH_MAP_ENABLE\n            dip_switch_update_kb(i, dip_switch_state[i]);\n#else\n            dip_switch_exec_mapping(i, dip_switch_state[i]);\n#endif\n        }\n    }\n    if (has_dip_state_changed) {\n#ifndef DIP_SWITCH_MAP_ENABLE\n        dip_switch_update_mask_kb(dip_switch_mask);\n#endif\n        memcpy(last_dip_switch_state, dip_switch_state, sizeof(dip_switch_state));\n    }\n}\n\nvoid dip_switch_task(void) {\n    dip_switch_read(false);\n}\n"
  },
  {
    "path": "quantum/dip_switch.h",
    "content": "/*\n * Copyright 2018 Jack Humbert <jack.humb@gmail.com>\n * Copyright 2018 Drashna Jaelre (Christopher Courtney) <drashna@live.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"gpio.h\"\n#include \"util.h\"\n\n#if defined(DIP_SWITCH_PINS)\n#    define NUM_DIP_SWITCHES ARRAY_SIZE(((pin_t[])DIP_SWITCH_PINS))\n#elif defined(DIP_SWITCH_MATRIX_GRID)\ntypedef struct matrix_intersection_t {\n    uint8_t row;\n    uint8_t col;\n} matrix_intersection_t;\n#    define NUM_DIP_SWITCHES ARRAY_SIZE(((matrix_intersection_t[])DIP_SWITCH_MATRIX_GRID))\n#endif\n\n#ifndef NUM_DIP_SWITCHES\n#    define NUM_DIP_SWITCHES 0\n#endif\n\nbool dip_switch_update_kb(uint8_t index, bool active);\nbool dip_switch_update_user(uint8_t index, bool active);\nbool dip_switch_update_mask_user(uint32_t state);\nbool dip_switch_update_mask_kb(uint32_t state);\n\nvoid dip_switch_read(bool forced);\n\nvoid dip_switch_init(void);\nvoid dip_switch_task(void);\n\n#ifdef DIP_SWITCH_MAP_ENABLE\n#    define NUM_DIP_STATES 2\n#    define DIP_SWITCH_OFF_ON(off, on) \\\n        { (off), (on) }\nextern const uint16_t dip_switch_map[NUM_DIP_SWITCHES][NUM_DIP_STATES];\n#endif // DIP_SWITCH_MAP_ENABLE\n"
  },
  {
    "path": "quantum/dynamic_keymap.c",
    "content": "/* Copyright 2017 Jason Williams (Wilba)\n * Copyright 2024-2025 Nick Brassel (@tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"dynamic_keymap.h\"\n#include \"keymap_introspection.h\"\n#include \"action.h\"\n#include \"send_string.h\"\n#include \"keycodes.h\"\n#include \"nvm_dynamic_keymap.h\"\n\n#ifdef ENCODER_ENABLE\n#    include \"encoder.h\"\n#else\n#    define NUM_ENCODERS 0\n#endif\n\n#ifndef DYNAMIC_KEYMAP_MACRO_DELAY\n#    define DYNAMIC_KEYMAP_MACRO_DELAY TAP_CODE_DELAY\n#endif\n\nuint8_t dynamic_keymap_get_layer_count(void) {\n    return DYNAMIC_KEYMAP_LAYER_COUNT;\n}\n\nuint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column) {\n    return nvm_dynamic_keymap_read_keycode(layer, row, column);\n}\n\nvoid dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode) {\n    nvm_dynamic_keymap_update_keycode(layer, row, column, keycode);\n}\n\n#ifdef ENCODER_MAP_ENABLE\nuint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise) {\n    return nvm_dynamic_keymap_read_encoder(layer, encoder_id, clockwise);\n}\n\nvoid dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode) {\n    nvm_dynamic_keymap_update_encoder(layer, encoder_id, clockwise, keycode);\n}\n#endif // ENCODER_MAP_ENABLE\n\nvoid dynamic_keymap_reset(void) {\n    // Erase the keymaps, if necessary.\n    nvm_dynamic_keymap_erase();\n\n    // Reset the keymaps in EEPROM to what is in flash.\n    for (int layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++) {\n        for (int row = 0; row < MATRIX_ROWS; row++) {\n            for (int column = 0; column < MATRIX_COLS; column++) {\n                dynamic_keymap_set_keycode(layer, row, column, keycode_at_keymap_location_raw(layer, row, column));\n            }\n        }\n#ifdef ENCODER_MAP_ENABLE\n        for (int encoder = 0; encoder < NUM_ENCODERS; encoder++) {\n            dynamic_keymap_set_encoder(layer, encoder, true, keycode_at_encodermap_location_raw(layer, encoder, true));\n            dynamic_keymap_set_encoder(layer, encoder, false, keycode_at_encodermap_location_raw(layer, encoder, false));\n        }\n#endif // ENCODER_MAP_ENABLE\n    }\n}\n\nvoid dynamic_keymap_get_buffer(uint16_t offset, uint16_t size, uint8_t *data) {\n    nvm_dynamic_keymap_read_buffer(offset, size, data);\n}\n\nvoid dynamic_keymap_set_buffer(uint16_t offset, uint16_t size, uint8_t *data) {\n    nvm_dynamic_keymap_update_buffer(offset, size, data);\n}\n\nuint16_t keycode_at_keymap_location(uint8_t layer_num, uint8_t row, uint8_t column) {\n    if (layer_num < DYNAMIC_KEYMAP_LAYER_COUNT && row < MATRIX_ROWS && column < MATRIX_COLS) {\n        return dynamic_keymap_get_keycode(layer_num, row, column);\n    }\n    return KC_NO;\n}\n\n#ifdef ENCODER_MAP_ENABLE\nuint16_t keycode_at_encodermap_location(uint8_t layer_num, uint8_t encoder_idx, bool clockwise) {\n    if (layer_num < DYNAMIC_KEYMAP_LAYER_COUNT && encoder_idx < NUM_ENCODERS) {\n        return dynamic_keymap_get_encoder(layer_num, encoder_idx, clockwise);\n    }\n    return KC_NO;\n}\n#endif // ENCODER_MAP_ENABLE\n\nuint8_t dynamic_keymap_macro_get_count(void) {\n    return DYNAMIC_KEYMAP_MACRO_COUNT;\n}\n\nuint16_t dynamic_keymap_macro_get_buffer_size(void) {\n    return (uint16_t)nvm_dynamic_keymap_macro_size();\n}\n\nvoid dynamic_keymap_macro_get_buffer(uint16_t offset, uint16_t size, uint8_t *data) {\n    nvm_dynamic_keymap_macro_read_buffer(offset, size, data);\n}\n\nvoid dynamic_keymap_macro_set_buffer(uint16_t offset, uint16_t size, uint8_t *data) {\n    nvm_dynamic_keymap_macro_update_buffer(offset, size, data);\n}\n\nstatic uint8_t dynamic_keymap_read_byte(uint32_t offset) {\n    uint8_t d;\n    nvm_dynamic_keymap_macro_read_buffer(offset, 1, &d);\n    return d;\n}\n\ntypedef struct send_string_nvm_state_t {\n    uint32_t offset;\n} send_string_nvm_state_t;\n\nchar send_string_get_next_nvm(void *arg) {\n    send_string_nvm_state_t *state = (send_string_nvm_state_t *)arg;\n    char                     ret   = dynamic_keymap_read_byte(state->offset);\n    state->offset++;\n    return ret;\n}\n\nvoid dynamic_keymap_macro_reset(void) {\n    // Erase the macros, if necessary.\n    nvm_dynamic_keymap_macro_erase();\n    nvm_dynamic_keymap_macro_reset();\n}\n\nvoid dynamic_keymap_macro_send(uint8_t id) {\n    if (id >= DYNAMIC_KEYMAP_MACRO_COUNT) {\n        return;\n    }\n\n    // Check the last byte of the buffer.\n    // If it's not zero, then we are in the middle\n    // of buffer writing, possibly an aborted buffer\n    // write. So do nothing.\n    if (dynamic_keymap_read_byte(nvm_dynamic_keymap_macro_size() - 1) != 0) {\n        return;\n    }\n\n    // Skip N null characters\n    // p will then point to the Nth macro\n    uint32_t offset = 0;\n    uint32_t end    = nvm_dynamic_keymap_macro_size();\n    while (id > 0) {\n        // If we are past the end of the buffer, then there is\n        // no Nth macro in the buffer.\n        if (offset == end) {\n            return;\n        }\n        if (dynamic_keymap_read_byte(offset) == 0) {\n            --id;\n        }\n        ++offset;\n    }\n\n    send_string_nvm_state_t state = {.offset = 0};\n    send_string_with_delay_impl(send_string_get_next_nvm, &state, DYNAMIC_KEYMAP_MACRO_DELAY);\n}\n"
  },
  {
    "path": "quantum/dynamic_keymap.h",
    "content": "/* Copyright 2017 Jason Williams (Wilba)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#ifndef DYNAMIC_KEYMAP_LAYER_COUNT\n#    define DYNAMIC_KEYMAP_LAYER_COUNT 4\n#endif\n\n#ifndef DYNAMIC_KEYMAP_MACRO_COUNT\n#    define DYNAMIC_KEYMAP_MACRO_COUNT 16\n#endif\n\nuint8_t  dynamic_keymap_get_layer_count(void);\nuint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column);\nvoid     dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode);\n#ifdef ENCODER_MAP_ENABLE\nuint16_t dynamic_keymap_get_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise);\nvoid     dynamic_keymap_set_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode);\n#endif // ENCODER_MAP_ENABLE\nvoid dynamic_keymap_reset(void);\n// These get/set the keycodes as stored in the EEPROM buffer\n// Data is big-endian 16-bit values (the keycodes)\n// Order is by layer/row/column\n// Thus offset 0 = 0,0,0, offset MATRIX_COLS*2 = 0,1,0, offset MATRIX_ROWS*MATRIX_COLS*2 = 1,0,0\n// Note the *2, because offset is in bytes and keycodes are two bytes\n// This is only really useful for host applications that want to get a whole keymap fast,\n// by reading 14 keycodes (28 bytes) at a time, reducing the number of raw HID transfers by\n// a factor of 14.\nvoid dynamic_keymap_get_buffer(uint16_t offset, uint16_t size, uint8_t *data);\nvoid dynamic_keymap_set_buffer(uint16_t offset, uint16_t size, uint8_t *data);\n\n// This overrides the one in quantum/keymap_common.c\n// uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key);\n\n// Note regarding dynamic_keymap_macro_set_buffer():\n// The last byte of the buffer is used as a valid flag,\n// so macro sending is disabled during writing a new buffer,\n// should it happen during, or after an interrupted transfer.\n//\n// Users writing to the buffer must first set the last byte of the buffer\n// to non-zero (i.e. 0xFF). After (or during) the final write, set the\n// last byte of the buffer to zero.\n//\n// Since the contents of the buffer must be a list of null terminated\n// strings, the last byte must be a null when at maximum capacity,\n// and it not being null means the buffer can be considered in an\n// invalid state.\n//\n// The buffer *may* contain less macro strings than the maximum.\n// This allows a higher maximum number of macros without requiring that\n// number of nulls to be in the buffer.\n// Note: dynamic_keymap_macro_get_count() returns the maximum that *can* be\n// stored, not the current count of macros in the buffer.\n\nuint8_t  dynamic_keymap_macro_get_count(void);\nuint16_t dynamic_keymap_macro_get_buffer_size(void);\nvoid     dynamic_keymap_macro_get_buffer(uint16_t offset, uint16_t size, uint8_t *data);\nvoid     dynamic_keymap_macro_set_buffer(uint16_t offset, uint16_t size, uint8_t *data);\nvoid     dynamic_keymap_macro_reset(void);\n\nvoid dynamic_keymap_macro_send(uint8_t id);\n"
  },
  {
    "path": "quantum/eeconfig.c",
    "content": "#include <string.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include \"debug.h\"\n#include \"eeconfig.h\"\n#include \"action_layer.h\"\n#include \"nvm_eeconfig.h\"\n#include \"keycode_config.h\"\n\n#ifdef BACKLIGHT_ENABLE\n#    include \"backlight.h\"\n#endif // BACKLIGHT_ENABLE\n\n#ifdef AUDIO_ENABLE\n#    include \"audio.h\"\n#endif // AUDIO_ENABLE\n\n#ifdef RGBLIGHT_ENABLE\n#    include \"rgblight.h\"\n#endif // RGBLIGHT_ENABLE\n\n#ifdef RGB_MATRIX_ENABLE\n#    include \"rgb_matrix_types.h\"\n#endif // RGB_MATRIX_ENABLE\n\n#ifdef LED_MATRIX_ENABLE\n#    include \"led_matrix_types.h\"\n#endif // LED_MATRIX_ENABLE\n\n#ifdef UNICODE_COMMON_ENABLE\n#    include \"unicode.h\"\n#endif // UNICODE_COMMON_ENABLE\n\n#ifdef HAPTIC_ENABLE\n#    include \"haptic.h\"\n#endif // HAPTIC_ENABLE\n\n#ifdef CONNECTION_ENABLE\n#    include \"connection.h\"\n#endif // CONNECTION_ENABLE\n\n#ifdef VIA_ENABLE\nbool via_eeprom_is_valid(void);\nvoid via_eeprom_set_valid(bool valid);\nvoid eeconfig_init_via(void);\n#else\nvoid dynamic_keymap_reset(void);\n#endif // VIA_ENABLE\n\n#ifndef NKRO_DEFAULT_ON\n#    define NKRO_DEFAULT_ON false\n#endif\n\n__attribute__((weak)) void eeconfig_init_user(void) {\n#if (EECONFIG_USER_DATA_SIZE) == 0\n    // Reset user EEPROM value to blank, rather than to a set value\n    eeconfig_update_user(0);\n#endif // (EECONFIG_USER_DATA_SIZE) == 0\n}\n\n__attribute__((weak)) void eeconfig_init_kb(void) {\n#if (EECONFIG_KB_DATA_SIZE) == 0\n    // Reset Keyboard EEPROM value to blank, rather than to a set value\n    eeconfig_update_kb(0);\n#endif // (EECONFIG_KB_DATA_SIZE) == 0\n\n    eeconfig_init_user();\n}\n\nvoid eeconfig_init_quantum(void) {\n    nvm_eeconfig_erase();\n\n    eeconfig_enable();\n\n    debug_config_t debug_config = {0};\n    eeconfig_update_debug(&debug_config);\n\n    default_layer_state = (layer_state_t)1 << 0;\n    eeconfig_update_default_layer(default_layer_state);\n\n    keymap_config_t keymap_config = {\n        .swap_control_capslock    = false,\n        .capslock_to_control      = false,\n        .swap_lalt_lgui           = false,\n        .swap_ralt_rgui           = false,\n        .no_gui                   = false,\n        .swap_grave_esc           = false,\n        .swap_backslash_backspace = false,\n        .nkro                     = NKRO_DEFAULT_ON,\n        .swap_lctl_lgui           = false,\n        .swap_rctl_rgui           = false,\n        .oneshot_enable           = true, // Enable oneshot by default\n        .swap_escape_capslock     = false,\n        .autocorrect_enable       = true, // Enable autocorrect by default\n    };\n    eeconfig_update_keymap(&keymap_config);\n\n#ifdef BACKLIGHT_ENABLE\n    backlight_config_t backlight_config = {0};\n    eeconfig_update_backlight(&backlight_config);\n#endif // BACKLIGHT_ENABLE\n\n#ifdef AUDIO_ENABLE\n    audio_config_t audio_config = {0};\n    eeconfig_update_audio(&audio_config);\n#endif // AUDIO_ENABLE\n\n#ifdef RGBLIGHT_ENABLE\n    extern void eeconfig_update_rgblight_default(void);\n    eeconfig_update_rgblight_default();\n#endif // RGBLIGHT_ENABLE\n\n#ifdef UNICODE_COMMON_ENABLE\n    unicode_config_t unicode_config = {0};\n    eeconfig_update_unicode_mode(&unicode_config);\n#endif // UNICODE_COMMON_ENABLE\n\n#ifdef STENO_ENABLE\n    eeconfig_update_steno_mode(0);\n#endif // STENO_ENABLE\n\n#ifdef RGB_MATRIX_ENABLE\n    extern void eeconfig_update_rgb_matrix_default(void);\n    eeconfig_update_rgb_matrix_default();\n#endif\n\n#ifdef LED_MATRIX_ENABLE\n    extern void eeconfig_update_led_matrix_default(void);\n    eeconfig_update_led_matrix_default();\n#endif // LED_MATRIX_ENABLE\n\n#ifdef HAPTIC_ENABLE\n    haptic_config_t haptic_config = {0};\n    eeconfig_update_haptic(&haptic_config);\n    haptic_reset();\n#endif // HAPTIC_ENABLE\n\n#ifdef CONNECTION_ENABLE\n    extern void eeconfig_update_connection_default(void);\n    eeconfig_update_connection_default();\n#endif // CONNECTION_ENABLE\n\n#if (EECONFIG_KB_DATA_SIZE) > 0\n    eeconfig_init_kb_datablock();\n#endif // (EECONFIG_KB_DATA_SIZE) > 0\n\n#if (EECONFIG_USER_DATA_SIZE) > 0\n    eeconfig_init_user_datablock();\n#endif // (EECONFIG_USER_DATA_SIZE) > 0\n\n#if defined(VIA_ENABLE)\n    // Invalidate VIA eeprom config, and then reset.\n    // Just in case if power is lost mid init, this makes sure that it gets\n    // properly re-initialized.\n    eeconfig_init_via();\n#elif defined(DYNAMIC_KEYMAP_ENABLE)\n    dynamic_keymap_reset();\n#endif\n\n    eeconfig_init_kb();\n\n#ifdef RGB_MATRIX_ENABLE\n    extern void eeconfig_force_flush_rgb_matrix(void);\n    eeconfig_force_flush_rgb_matrix();\n#endif // RGB_MATRIX_ENABLE\n#ifdef LED_MATRIX_ENABLE\n    extern void eeconfig_force_flush_led_matrix(void);\n    eeconfig_force_flush_led_matrix();\n#endif // LED_MATRIX_ENABLE\n}\n\nvoid eeconfig_init(void) {\n    eeconfig_init_quantum();\n}\n\nvoid eeconfig_enable(void) {\n    nvm_eeconfig_enable();\n}\n\nvoid eeconfig_disable(void) {\n    nvm_eeconfig_disable();\n}\n\nbool eeconfig_is_enabled(void) {\n    bool is_eeprom_enabled = nvm_eeconfig_is_enabled();\n#ifdef VIA_ENABLE\n    if (is_eeprom_enabled) {\n        is_eeprom_enabled = via_eeprom_is_valid();\n    }\n#endif // VIA_ENABLE\n    return is_eeprom_enabled;\n}\n\nbool eeconfig_is_disabled(void) {\n    bool is_eeprom_disabled = nvm_eeconfig_is_disabled();\n#ifdef VIA_ENABLE\n    if (!is_eeprom_disabled) {\n        is_eeprom_disabled = !via_eeprom_is_valid();\n    }\n#endif // VIA_ENABLE\n    return is_eeprom_disabled;\n}\n\nvoid eeconfig_read_debug(debug_config_t *debug_config) {\n    nvm_eeconfig_read_debug(debug_config);\n}\nvoid eeconfig_update_debug(const debug_config_t *debug_config) {\n    nvm_eeconfig_update_debug(debug_config);\n}\n\nlayer_state_t eeconfig_read_default_layer(void) {\n    return nvm_eeconfig_read_default_layer();\n}\nvoid eeconfig_update_default_layer(layer_state_t state) {\n    nvm_eeconfig_update_default_layer(state);\n}\n\nvoid eeconfig_read_keymap(keymap_config_t *keymap_config) {\n    nvm_eeconfig_read_keymap(keymap_config);\n}\nvoid eeconfig_update_keymap(const keymap_config_t *keymap_config) {\n    nvm_eeconfig_update_keymap(keymap_config);\n}\n\n#ifdef AUDIO_ENABLE\nvoid eeconfig_read_audio(audio_config_t *audio_config) {\n    nvm_eeconfig_read_audio(audio_config);\n}\nvoid eeconfig_update_audio(const audio_config_t *audio_config) {\n    nvm_eeconfig_update_audio(audio_config);\n}\n#endif // AUDIO_ENABLE\n\n#ifdef UNICODE_COMMON_ENABLE\nvoid eeconfig_read_unicode_mode(unicode_config_t *unicode_config) {\n    return nvm_eeconfig_read_unicode_mode(unicode_config);\n}\nvoid eeconfig_update_unicode_mode(const unicode_config_t *unicode_config) {\n    nvm_eeconfig_update_unicode_mode(unicode_config);\n}\n#endif // UNICODE_COMMON_ENABLE\n\n#ifdef BACKLIGHT_ENABLE\nvoid eeconfig_read_backlight(backlight_config_t *backlight_config) {\n    nvm_eeconfig_read_backlight(backlight_config);\n}\nvoid eeconfig_update_backlight(const backlight_config_t *backlight_config) {\n    nvm_eeconfig_update_backlight(backlight_config);\n}\n#endif // BACKLIGHT_ENABLE\n\n#ifdef STENO_ENABLE\nuint8_t eeconfig_read_steno_mode(void) {\n    return nvm_eeconfig_read_steno_mode();\n}\nvoid eeconfig_update_steno_mode(uint8_t val) {\n    nvm_eeconfig_update_steno_mode(val);\n}\n#endif // STENO_ENABLE\n\n#ifdef RGB_MATRIX_ENABLE\nvoid eeconfig_read_rgb_matrix(rgb_config_t *rgb_matrix_config) {\n    nvm_eeconfig_read_rgb_matrix(rgb_matrix_config);\n}\nvoid eeconfig_update_rgb_matrix(const rgb_config_t *rgb_matrix_config) {\n    nvm_eeconfig_update_rgb_matrix(rgb_matrix_config);\n}\n#endif // RGB_MATRIX_ENABLE\n\n#ifdef LED_MATRIX_ENABLE\nvoid eeconfig_read_led_matrix(led_eeconfig_t *led_matrix_config) {\n    nvm_eeconfig_read_led_matrix(led_matrix_config);\n}\nvoid eeconfig_update_led_matrix(const led_eeconfig_t *led_matrix_config) {\n    nvm_eeconfig_update_led_matrix(led_matrix_config);\n}\n#endif // LED_MATRIX_ENABLE\n\n#ifdef RGBLIGHT_ENABLE\nvoid eeconfig_read_rgblight(rgblight_config_t *rgblight_config) {\n    nvm_eeconfig_read_rgblight(rgblight_config);\n}\nvoid eeconfig_update_rgblight(const rgblight_config_t *rgblight_config) {\n    nvm_eeconfig_update_rgblight(rgblight_config);\n}\n#endif // RGBLIGHT_ENABLE\n\n#if (EECONFIG_KB_DATA_SIZE) == 0\nuint32_t eeconfig_read_kb(void) {\n    return nvm_eeconfig_read_kb();\n}\nvoid eeconfig_update_kb(uint32_t val) {\n    nvm_eeconfig_update_kb(val);\n}\n#endif // (EECONFIG_KB_DATA_SIZE) == 0\n\n#if (EECONFIG_USER_DATA_SIZE) == 0\nuint32_t eeconfig_read_user(void) {\n    return nvm_eeconfig_read_user();\n}\nvoid eeconfig_update_user(uint32_t val) {\n    nvm_eeconfig_update_user(val);\n}\n#endif // (EECONFIG_USER_DATA_SIZE) == 0\n\n#ifdef HAPTIC_ENABLE\nvoid eeconfig_read_haptic(haptic_config_t *haptic_config) {\n    nvm_eeconfig_read_haptic(haptic_config);\n}\nvoid eeconfig_update_haptic(const haptic_config_t *haptic_config) {\n    nvm_eeconfig_update_haptic(haptic_config);\n}\n#endif // HAPTIC_ENABLE\n\n#ifdef CONNECTION_ENABLE\nvoid eeconfig_read_connection(connection_config_t *config) {\n    nvm_eeconfig_read_connection(config);\n}\nvoid eeconfig_update_connection(const connection_config_t *config) {\n    nvm_eeconfig_update_connection(config);\n}\n#endif // CONNECTION_ENABLE\n\nbool eeconfig_read_handedness(void) {\n    return nvm_eeconfig_read_handedness();\n}\nvoid eeconfig_update_handedness(bool val) {\n    nvm_eeconfig_update_handedness(val);\n}\n\n#if (EECONFIG_KB_DATA_SIZE) > 0\nbool eeconfig_is_kb_datablock_valid(void) {\n    return nvm_eeconfig_is_kb_datablock_valid();\n}\nuint32_t eeconfig_read_kb_datablock(void *data, uint32_t offset, uint32_t length) {\n    return nvm_eeconfig_read_kb_datablock(data, offset, length);\n}\nuint32_t eeconfig_update_kb_datablock(const void *data, uint32_t offset, uint32_t length) {\n    return nvm_eeconfig_update_kb_datablock(data, offset, length);\n}\n__attribute__((weak)) void eeconfig_init_kb_datablock(void) {\n    nvm_eeconfig_init_kb_datablock();\n}\n#endif // (EECONFIG_KB_DATA_SIZE) > 0\n\n#if (EECONFIG_USER_DATA_SIZE) > 0\nbool eeconfig_is_user_datablock_valid(void) {\n    return nvm_eeconfig_is_user_datablock_valid();\n}\nuint32_t eeconfig_read_user_datablock(void *data, uint32_t offset, uint32_t length) {\n    return nvm_eeconfig_read_user_datablock(data, offset, length);\n}\nuint32_t eeconfig_update_user_datablock(const void *data, uint32_t offset, uint32_t length) {\n    return nvm_eeconfig_update_user_datablock(data, offset, length);\n}\n__attribute__((weak)) void eeconfig_init_user_datablock(void) {\n    nvm_eeconfig_init_user_datablock();\n}\n#endif // (EECONFIG_USER_DATA_SIZE) > 0\n"
  },
  {
    "path": "quantum/eeconfig.h",
    "content": "/*\nCopyright 2013 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include <stddef.h>       // offsetof\n#include \"action_layer.h\" // layer_state_t\n\n// Size of EEPROM dedicated to keyboard- and user-specific data\n#ifndef EECONFIG_KB_DATA_SIZE\n#    define EECONFIG_KB_DATA_SIZE 0\n#endif\n#ifndef EECONFIG_KB_DATA_VERSION\n#    define EECONFIG_KB_DATA_VERSION (EECONFIG_KB_DATA_SIZE)\n#endif\n#ifndef EECONFIG_USER_DATA_SIZE\n#    define EECONFIG_USER_DATA_SIZE 0\n#endif\n#ifndef EECONFIG_USER_DATA_VERSION\n#    define EECONFIG_USER_DATA_VERSION (EECONFIG_USER_DATA_SIZE)\n#endif\n\n/* debug bit */\n#define EECONFIG_DEBUG_ENABLE (1 << 0)\n#define EECONFIG_DEBUG_MATRIX (1 << 1)\n#define EECONFIG_DEBUG_KEYBOARD (1 << 2)\n#define EECONFIG_DEBUG_MOUSE (1 << 3)\n\n/* keyconf bit */\n#define EECONFIG_KEYMAP_SWAP_CONTROL_CAPSLOCK (1 << 0)\n#define EECONFIG_KEYMAP_CAPSLOCK_TO_CONTROL (1 << 1)\n#define EECONFIG_KEYMAP_SWAP_LALT_LGUI (1 << 2)\n#define EECONFIG_KEYMAP_SWAP_RALT_RGUI (1 << 3)\n#define EECONFIG_KEYMAP_NO_GUI (1 << 4)\n#define EECONFIG_KEYMAP_SWAP_GRAVE_ESC (1 << 5)\n#define EECONFIG_KEYMAP_SWAP_BACKSLASH_BACKSPACE (1 << 6)\n#define EECONFIG_KEYMAP_NKRO (1 << 7)\n\nbool eeconfig_is_enabled(void);\nbool eeconfig_is_disabled(void);\n\nvoid eeconfig_init(void);\nvoid eeconfig_init_quantum(void);\nvoid eeconfig_init_kb(void);\nvoid eeconfig_init_user(void);\n\nvoid eeconfig_enable(void);\nvoid eeconfig_disable(void);\n\ntypedef union debug_config_t debug_config_t;\nvoid                         eeconfig_read_debug(debug_config_t *debug_config) __attribute__((nonnull));\nvoid                         eeconfig_update_debug(const debug_config_t *debug_config) __attribute__((nonnull));\n\nlayer_state_t eeconfig_read_default_layer(void);\nvoid          eeconfig_update_default_layer(layer_state_t state);\n\ntypedef union keymap_config_t keymap_config_t;\nvoid                          eeconfig_read_keymap(keymap_config_t *keymap_config) __attribute__((nonnull));\nvoid                          eeconfig_update_keymap(const keymap_config_t *keymap_config) __attribute__((nonnull));\n\n#ifdef AUDIO_ENABLE\ntypedef union audio_config_t audio_config_t;\nvoid                         eeconfig_read_audio(audio_config_t *audio_config) __attribute__((nonnull));\nvoid                         eeconfig_update_audio(const audio_config_t *audio_config) __attribute__((nonnull));\n#endif // AUDIO_ENABLE\n\n#ifdef UNICODE_COMMON_ENABLE\ntypedef union unicode_config_t unicode_config_t;\nvoid                           eeconfig_read_unicode_mode(unicode_config_t *unicode_config) __attribute__((nonnull));\nvoid                           eeconfig_update_unicode_mode(const unicode_config_t *unicode_config) __attribute__((nonnull));\n#endif // UNICODE_COMMON_ENABLE\n\n#ifdef BACKLIGHT_ENABLE\ntypedef union backlight_config_t backlight_config_t;\nvoid                             eeconfig_read_backlight(backlight_config_t *backlight_config) __attribute__((nonnull));\nvoid                             eeconfig_update_backlight(const backlight_config_t *backlight_config) __attribute__((nonnull));\n#endif // BACKLIGHT_ENABLE\n\n#ifdef STENO_ENABLE\nuint8_t eeconfig_read_steno_mode(void);\nvoid    eeconfig_update_steno_mode(uint8_t val);\n#endif // STENO_ENABLE\n\n#ifdef RGB_MATRIX_ENABLE\ntypedef union rgb_config_t rgb_config_t;\nvoid                       eeconfig_read_rgb_matrix(rgb_config_t *rgb_matrix_config) __attribute__((nonnull));\nvoid                       eeconfig_update_rgb_matrix(const rgb_config_t *rgb_matrix_config) __attribute__((nonnull));\n#endif // RGB_MATRIX_ENABLE\n\n#ifdef LED_MATRIX_ENABLE\ntypedef union led_eeconfig_t led_eeconfig_t;\nvoid                         eeconfig_read_led_matrix(led_eeconfig_t *led_matrix_config) __attribute__((nonnull));\nvoid                         eeconfig_update_led_matrix(const led_eeconfig_t *led_matrix_config) __attribute__((nonnull));\n#endif // LED_MATRIX_ENABLE\n\n#ifdef RGBLIGHT_ENABLE\ntypedef union rgblight_config_t rgblight_config_t;\nvoid                            eeconfig_read_rgblight(rgblight_config_t *rgblight_config) __attribute__((nonnull));\nvoid                            eeconfig_update_rgblight(const rgblight_config_t *rgblight_config) __attribute__((nonnull));\n#endif // RGBLIGHT_ENABLE\n\n#if (EECONFIG_KB_DATA_SIZE) == 0\nuint32_t eeconfig_read_kb(void);\nvoid     eeconfig_update_kb(uint32_t val);\n#endif // (EECONFIG_KB_DATA_SIZE) == 0\n\n#if (EECONFIG_USER_DATA_SIZE) == 0\nuint32_t eeconfig_read_user(void);\nvoid     eeconfig_update_user(uint32_t val);\n#endif // (EECONFIG_USER_DATA_SIZE) == 0\n\n#ifdef HAPTIC_ENABLE\ntypedef union haptic_config_t haptic_config_t;\nvoid                          eeconfig_read_haptic(haptic_config_t *haptic_config) __attribute__((nonnull));\nvoid                          eeconfig_update_haptic(const haptic_config_t *haptic_config) __attribute__((nonnull));\n#endif\n\n#ifdef CONNECTION_ENABLE\ntypedef union connection_config_t connection_config_t;\nvoid                              eeconfig_read_connection(connection_config_t *config);\nvoid                              eeconfig_update_connection(const connection_config_t *config);\n#endif\n\nbool eeconfig_read_handedness(void);\nvoid eeconfig_update_handedness(bool val);\n\n#if (EECONFIG_KB_DATA_SIZE) > 0\nbool     eeconfig_is_kb_datablock_valid(void);\nuint32_t eeconfig_read_kb_datablock(void *data, uint32_t offset, uint32_t length) __attribute__((nonnull));\nuint32_t eeconfig_update_kb_datablock(const void *data, uint32_t offset, uint32_t length) __attribute__((nonnull));\nvoid     eeconfig_init_kb_datablock(void);\n#    define eeconfig_read_kb_datablock_field(__object, __field) eeconfig_read_kb_datablock(&(__object.__field), offsetof(typeof(__object), __field), sizeof(__object.__field))\n#    define eeconfig_update_kb_datablock_field(__object, __field) eeconfig_update_kb_datablock(&(__object.__field), offsetof(typeof(__object), __field), sizeof(__object.__field))\n#endif // (EECONFIG_KB_DATA_SIZE) > 0\n\n#if (EECONFIG_USER_DATA_SIZE) > 0\nbool     eeconfig_is_user_datablock_valid(void);\nuint32_t eeconfig_read_user_datablock(void *data, uint32_t offset, uint32_t length) __attribute__((nonnull));\nuint32_t eeconfig_update_user_datablock(const void *data, uint32_t offset, uint32_t length) __attribute__((nonnull));\nvoid     eeconfig_init_user_datablock(void);\n#    define eeconfig_read_user_datablock_field(__object, __field) eeconfig_read_user_datablock(&(__object.__field), offsetof(__object, __field), sizeof(__object.__field))\n#    define eeconfig_update_user_datablock_field(__object, __field) eeconfig_update_user_datablock(&(__object.__field), offsetof(__object, __field), sizeof(__object.__field))\n#endif // (EECONFIG_USER_DATA_SIZE) > 0\n\n// Any \"checked\" debounce variant used requires implementation of:\n//    -- bool eeconfig_check_valid_##name(void)\n//    -- void eeconfig_post_flush_##name(void)\n#define EECONFIG_DEBOUNCE_HELPER_CHECKED(name, config)                  \\\n    static uint8_t dirty_##name = false;                                \\\n                                                                        \\\n    bool eeconfig_check_valid_##name(void);                             \\\n    void eeconfig_post_flush_##name(void);                              \\\n                                                                        \\\n    static inline void eeconfig_init_##name(void) {                     \\\n        dirty_##name = true;                                            \\\n        if (eeconfig_check_valid_##name()) {                            \\\n            eeconfig_read_##name(&config);                              \\\n            dirty_##name = false;                                       \\\n        }                                                               \\\n    }                                                                   \\\n    static inline void eeconfig_flush_##name(bool force) {              \\\n        if (force || dirty_##name) {                                    \\\n            eeconfig_update_##name(&config);                            \\\n            eeconfig_post_flush_##name();                               \\\n            dirty_##name = false;                                       \\\n        }                                                               \\\n    }                                                                   \\\n    static inline void eeconfig_flush_##name##_task(uint16_t timeout) { \\\n        static uint16_t flush_timer = 0;                                \\\n        if (timer_elapsed(flush_timer) > timeout) {                     \\\n            eeconfig_flush_##name(false);                               \\\n            flush_timer = timer_read();                                 \\\n        }                                                               \\\n    }                                                                   \\\n    static inline void eeconfig_flag_##name(bool v) {                   \\\n        dirty_##name |= v;                                              \\\n    }                                                                   \\\n    static inline void eeconfig_write_##name(typeof(config) *conf) {    \\\n        if (memcmp(&config, conf, sizeof(config)) != 0) {               \\\n            memcpy(&config, conf, sizeof(config));                      \\\n            eeconfig_flag_##name(true);                                 \\\n        }                                                               \\\n    }\n\n#define EECONFIG_DEBOUNCE_HELPER(name, config)     \\\n    EECONFIG_DEBOUNCE_HELPER_CHECKED(name, config) \\\n                                                   \\\n    bool eeconfig_check_valid_##name(void) {       \\\n        return true;                               \\\n    }                                              \\\n    void eeconfig_post_flush_##name(void) {}\n"
  },
  {
    "path": "quantum/encoder/tests/config_encoder_common.h",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n// Override the one in quantum/util because it doesn't like working on x64 builds.\n#define ARRAY_SIZE(array) (sizeof((array)) / sizeof((array)[0]))\n"
  },
  {
    "path": "quantum/encoder/tests/config_mock.h",
    "content": "// Copyright 2022-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n#include \"config_encoder_common.h\"\n\n#define MATRIX_ROWS 1\n#define MATRIX_COLS 1\n\n/* Here, \"pins\" from 0 to 31 are allowed. */\n#define ENCODER_A_PINS \\\n    { 0 }\n#define ENCODER_B_PINS \\\n    { 1 }\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"mock.h\"\n\n#ifdef __cplusplus\n};\n#endif\n"
  },
  {
    "path": "quantum/encoder/tests/config_mock_split_left_eq_right.h",
    "content": "// Copyright 2022-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n#include \"config_encoder_common.h\"\n\n#define MATRIX_ROWS 1\n#define MATRIX_COLS 1\n\n/* Here, \"pins\" from 0 to 31 are allowed. */\n#define ENCODER_A_PINS \\\n    { 0, 2 }\n#define ENCODER_B_PINS \\\n    { 1, 3 }\n#define ENCODER_A_PINS_RIGHT \\\n    { 4, 6 }\n#define ENCODER_B_PINS_RIGHT \\\n    { 5, 7 }\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"mock_split.h\"\n\n#ifdef __cplusplus\n};\n#endif\n"
  },
  {
    "path": "quantum/encoder/tests/config_mock_split_left_gt_right.h",
    "content": "// Copyright 2022-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n#include \"config_encoder_common.h\"\n\n#define MATRIX_ROWS 1\n#define MATRIX_COLS 1\n\n/* Here, \"pins\" from 0 to 31 are allowed. */\n#define ENCODER_A_PINS \\\n    { 0, 2, 4 }\n#define ENCODER_B_PINS \\\n    { 1, 3, 5 }\n#define ENCODER_A_PINS_RIGHT \\\n    { 6, 8 }\n#define ENCODER_B_PINS_RIGHT \\\n    { 7, 9 }\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"mock_split.h\"\n\n#ifdef __cplusplus\n};\n#endif\n"
  },
  {
    "path": "quantum/encoder/tests/config_mock_split_left_lt_right.h",
    "content": "// Copyright 2022-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n#include \"config_encoder_common.h\"\n\n#define MATRIX_ROWS 1\n#define MATRIX_COLS 1\n\n/* Here, \"pins\" from 0 to 31 are allowed. */\n#define ENCODER_A_PINS \\\n    { 0, 2 }\n#define ENCODER_B_PINS \\\n    { 1, 3 }\n#define ENCODER_A_PINS_RIGHT \\\n    { 4, 6, 8 }\n#define ENCODER_B_PINS_RIGHT \\\n    { 5, 7, 9 }\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"mock_split.h\"\n\n#ifdef __cplusplus\n};\n#endif\n"
  },
  {
    "path": "quantum/encoder/tests/config_mock_split_no_left.h",
    "content": "// Copyright 2022-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n#include \"config_encoder_common.h\"\n\n#define MATRIX_ROWS 1\n#define MATRIX_COLS 1\n\n/* Here, \"pins\" from 0 to 31 are allowed. */\n#define ENCODER_A_PINS \\\n    {}\n#define ENCODER_B_PINS \\\n    {}\n#define ENCODER_A_PINS_RIGHT \\\n    { 0, 2 }\n#define ENCODER_B_PINS_RIGHT \\\n    { 1, 3 }\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"mock_split.h\"\n\n#ifdef __cplusplus\n};\n#endif\n"
  },
  {
    "path": "quantum/encoder/tests/config_mock_split_no_right.h",
    "content": "// Copyright 2022-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n#include \"config_encoder_common.h\"\n\n#define MATRIX_ROWS 1\n#define MATRIX_COLS 1\n\n/* Here, \"pins\" from 0 to 31 are allowed. */\n#define ENCODER_A_PINS \\\n    { 0, 2 }\n#define ENCODER_B_PINS \\\n    { 1, 3 }\n#define ENCODER_A_PINS_RIGHT \\\n    {}\n#define ENCODER_B_PINS_RIGHT \\\n    {}\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"mock_split.h\"\n\n#ifdef __cplusplus\n};\n#endif\n"
  },
  {
    "path": "quantum/encoder/tests/config_mock_split_role.h",
    "content": "// Copyright 2022-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n#include \"config_encoder_common.h\"\n\n#define MATRIX_ROWS 1\n#define MATRIX_COLS 1\n\n/* Here, \"pins\" from 0 to 31 are allowed. */\n#define ENCODER_A_PINS \\\n    { 0, 2 }\n#define ENCODER_B_PINS \\\n    { 1, 3 }\n#define ENCODER_A_PINS_RIGHT \\\n    { 4, 6 }\n#define ENCODER_B_PINS_RIGHT \\\n    { 5, 7 }\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"mock_split.h\"\n\n#ifdef __cplusplus\n};\n#endif\n"
  },
  {
    "path": "quantum/encoder/tests/encoder_tests.cpp",
    "content": "/* Copyright 2021 Balz Guenat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include <vector>\n#include <algorithm>\n#include <stdio.h>\n\nextern \"C\" {\n#include \"encoder.h\"\n#include \"encoder/tests/mock.h\"\n}\n\nstruct update {\n    int8_t index;\n    bool   clockwise;\n};\n\nuint8_t updates_array_idx = 0;\nupdate  updates[32];\n\nbool encoder_update_kb(uint8_t index, bool clockwise) {\n    updates[updates_array_idx % 32] = {index, clockwise};\n    updates_array_idx++;\n    return true;\n}\n\nbool setAndRead(pin_t pin, bool val) {\n    setPin(pin, val);\n    return encoder_task();\n}\n\nclass EncoderTest : public ::testing::Test {};\n\nTEST_F(EncoderTest, TestInit) {\n    updates_array_idx = 0;\n    encoder_init();\n    EXPECT_EQ(pinIsInputHigh[0], true);\n    EXPECT_EQ(pinIsInputHigh[1], true);\n    EXPECT_EQ(updates_array_idx, 0);\n}\n\nTEST_F(EncoderTest, TestOneClockwise) {\n    updates_array_idx = 0;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n\n    EXPECT_EQ(updates_array_idx, 1);\n    EXPECT_EQ(updates[0].index, 0);\n    EXPECT_EQ(updates[0].clockwise, true);\n}\n\nTEST_F(EncoderTest, TestOneCounterClockwise) {\n    updates_array_idx = 0;\n    encoder_init();\n    setAndRead(1, false);\n    setAndRead(0, false);\n    setAndRead(1, true);\n    setAndRead(0, true);\n\n    EXPECT_EQ(updates_array_idx, 1);\n    EXPECT_EQ(updates[0].index, 0);\n    EXPECT_EQ(updates[0].clockwise, false);\n}\n\nTEST_F(EncoderTest, TestTwoClockwiseOneCC) {\n    updates_array_idx = 0;\n    encoder_init();\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n    setAndRead(1, false);\n    setAndRead(0, false);\n    setAndRead(1, true);\n    setAndRead(0, true);\n\n    EXPECT_EQ(updates_array_idx, 3);\n    EXPECT_EQ(updates[0].index, 0);\n    EXPECT_EQ(updates[0].clockwise, true);\n    EXPECT_EQ(updates[1].index, 0);\n    EXPECT_EQ(updates[1].clockwise, true);\n    EXPECT_EQ(updates[2].index, 0);\n    EXPECT_EQ(updates[2].clockwise, false);\n}\n\nTEST_F(EncoderTest, TestNoEarly) {\n    updates_array_idx = 0;\n    encoder_init();\n    // send 3 pulses. with resolution 4, that's not enough for a step.\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    EXPECT_EQ(updates_array_idx, 0);\n    // now send last pulse\n    setAndRead(1, true);\n    EXPECT_EQ(updates_array_idx, 1);\n    EXPECT_EQ(updates[0].index, 0);\n    EXPECT_EQ(updates[0].clockwise, true);\n}\n\nTEST_F(EncoderTest, TestHalfway) {\n    updates_array_idx = 0;\n    encoder_init();\n    // go halfway\n    setAndRead(0, false);\n    setAndRead(1, false);\n    EXPECT_EQ(updates_array_idx, 0);\n    // back off\n    setAndRead(1, true);\n    setAndRead(0, true);\n    EXPECT_EQ(updates_array_idx, 0);\n    // go all the way\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n    // should result in 1 update\n    EXPECT_EQ(updates_array_idx, 1);\n    EXPECT_EQ(updates[0].index, 0);\n    EXPECT_EQ(updates[0].clockwise, true);\n}\n"
  },
  {
    "path": "quantum/encoder/tests/encoder_tests_split_left_eq_right.cpp",
    "content": "/* Copyright 2021 Balz Guenat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include <vector>\n#include <algorithm>\n#include <stdio.h>\n\nextern \"C\" {\n#include \"encoder.h\"\n#include \"encoder/tests/mock_split.h\"\n}\n\nstruct update {\n    int8_t index;\n    bool   clockwise;\n};\n\nuint8_t updates_array_idx = 0;\nupdate  updates[32];\n\nbool isMaster;\nbool isLeftHand;\n\nextern \"C\" {\nbool is_keyboard_master(void) {\n    return isMaster;\n}\n\nbool encoder_update_kb(uint8_t index, bool clockwise) {\n    if (!is_keyboard_master()) {\n        // this method has no effect on slave half\n        printf(\"ignoring update on slave (%d,%s)\\n\", index, clockwise ? \"CW\" : \"CC\");\n        return true;\n    }\n    updates[updates_array_idx % 32] = {index, clockwise};\n    updates_array_idx++;\n    return true;\n}\n};\n\nbool setAndRead(pin_t pin, bool val) {\n    setPin(pin, val);\n    return encoder_task();\n}\n\nclass EncoderSplitTestLeftEqRight : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        updates_array_idx = 0;\n        for (int i = 0; i < 32; i++) {\n            pinIsInputHigh[i] = 0;\n            pins[i]           = 0;\n        }\n    }\n};\n\nTEST_F(EncoderSplitTestLeftEqRight, TestInitLeft) {\n    isMaster   = true;\n    isLeftHand = true;\n    encoder_init();\n    EXPECT_EQ(pinIsInputHigh[0], true);\n    EXPECT_EQ(pinIsInputHigh[1], true);\n    EXPECT_EQ(pinIsInputHigh[2], true);\n    EXPECT_EQ(pinIsInputHigh[3], true);\n    EXPECT_EQ(pinIsInputHigh[4], false);\n    EXPECT_EQ(pinIsInputHigh[5], false);\n    EXPECT_EQ(pinIsInputHigh[6], false);\n    EXPECT_EQ(pinIsInputHigh[7], false);\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n}\n\nTEST_F(EncoderSplitTestLeftEqRight, TestInitRight) {\n    isMaster   = true;\n    isLeftHand = false;\n    encoder_init();\n    EXPECT_EQ(pinIsInputHigh[0], false);\n    EXPECT_EQ(pinIsInputHigh[1], false);\n    EXPECT_EQ(pinIsInputHigh[2], false);\n    EXPECT_EQ(pinIsInputHigh[3], false);\n    EXPECT_EQ(pinIsInputHigh[4], true);\n    EXPECT_EQ(pinIsInputHigh[5], true);\n    EXPECT_EQ(pinIsInputHigh[6], true);\n    EXPECT_EQ(pinIsInputHigh[7], true);\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n}\n\nTEST_F(EncoderSplitTestLeftEqRight, TestOneClockwiseLeftMaster) {\n    isMaster   = true;\n    isLeftHand = true;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n\n    EXPECT_EQ(updates_array_idx, 1); // one update received\n    EXPECT_EQ(updates[0].index, 0);\n    EXPECT_EQ(updates[0].clockwise, true);\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 0); // No events should be queued on master\n}\n\nTEST_F(EncoderSplitTestLeftEqRight, TestOneClockwiseRightMaster) {\n    isMaster   = true;\n    isLeftHand = false;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(6, false);\n    setAndRead(7, false);\n    setAndRead(6, true);\n    setAndRead(7, true);\n\n    EXPECT_EQ(updates_array_idx, 1); // one update received\n    EXPECT_EQ(updates[0].index, 3);\n    EXPECT_EQ(updates[0].clockwise, true);\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 0); // No events should be queued on master\n}\n\nTEST_F(EncoderSplitTestLeftEqRight, TestOneClockwiseLeftSlave) {\n    isMaster   = false;\n    isLeftHand = true;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 1); // One event should be queued on slave\n}\n\nTEST_F(EncoderSplitTestLeftEqRight, TestOneClockwiseRightSlave) {\n    isMaster   = false;\n    isLeftHand = false;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(6, false);\n    setAndRead(7, false);\n    setAndRead(6, true);\n    setAndRead(7, true);\n\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 1); // One event should be queued on slave\n}\n"
  },
  {
    "path": "quantum/encoder/tests/encoder_tests_split_left_gt_right.cpp",
    "content": "/* Copyright 2021 Balz Guenat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include <vector>\n#include <algorithm>\n#include <stdio.h>\n\nextern \"C\" {\n#include \"encoder.h\"\n#include \"encoder/tests/mock_split.h\"\n}\n\nstruct update {\n    int8_t index;\n    bool   clockwise;\n};\n\nuint8_t updates_array_idx = 0;\nupdate  updates[32];\n\nbool isMaster;\nbool isLeftHand;\n\nextern \"C\" {\nbool is_keyboard_master(void) {\n    return isMaster;\n}\n\nbool encoder_update_kb(uint8_t index, bool clockwise) {\n    if (!is_keyboard_master()) {\n        // this method has no effect on slave half\n        printf(\"ignoring update on slave (%d,%s)\\n\", index, clockwise ? \"CW\" : \"CC\");\n        return true;\n    }\n    updates[updates_array_idx % 32] = {index, clockwise};\n    updates_array_idx++;\n    return true;\n}\n};\n\nbool setAndRead(pin_t pin, bool val) {\n    setPin(pin, val);\n    return encoder_task();\n}\n\nclass EncoderSplitTestLeftGreaterThanRight : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        updates_array_idx = 0;\n        for (int i = 0; i < 32; i++) {\n            pinIsInputHigh[i] = 0;\n            pins[i]           = 0;\n        }\n    }\n};\n\nTEST_F(EncoderSplitTestLeftGreaterThanRight, TestInitLeft) {\n    isLeftHand = true;\n    encoder_init();\n    EXPECT_EQ(pinIsInputHigh[0], true);\n    EXPECT_EQ(pinIsInputHigh[1], true);\n    EXPECT_EQ(pinIsInputHigh[2], true);\n    EXPECT_EQ(pinIsInputHigh[3], true);\n    EXPECT_EQ(pinIsInputHigh[4], true);\n    EXPECT_EQ(pinIsInputHigh[5], true);\n    EXPECT_EQ(pinIsInputHigh[6], false);\n    EXPECT_EQ(pinIsInputHigh[7], false);\n    EXPECT_EQ(pinIsInputHigh[8], false);\n    EXPECT_EQ(pinIsInputHigh[9], false);\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n}\n\nTEST_F(EncoderSplitTestLeftGreaterThanRight, TestInitRight) {\n    isLeftHand = false;\n    encoder_init();\n    EXPECT_EQ(pinIsInputHigh[0], false);\n    EXPECT_EQ(pinIsInputHigh[1], false);\n    EXPECT_EQ(pinIsInputHigh[2], false);\n    EXPECT_EQ(pinIsInputHigh[3], false);\n    EXPECT_EQ(pinIsInputHigh[4], false);\n    EXPECT_EQ(pinIsInputHigh[5], false);\n    EXPECT_EQ(pinIsInputHigh[6], true);\n    EXPECT_EQ(pinIsInputHigh[7], true);\n    EXPECT_EQ(pinIsInputHigh[8], true);\n    EXPECT_EQ(pinIsInputHigh[9], true);\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n}\n\nTEST_F(EncoderSplitTestLeftGreaterThanRight, TestOneClockwiseLeftMaster) {\n    isMaster   = true;\n    isLeftHand = true;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n\n    EXPECT_EQ(updates_array_idx, 1); // one update received\n    EXPECT_EQ(updates[0].index, 0);\n    EXPECT_EQ(updates[0].clockwise, true);\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 0); // No events should be queued on master\n}\n\nTEST_F(EncoderSplitTestLeftGreaterThanRight, TestOneClockwiseRightMaster) {\n    isMaster   = true;\n    isLeftHand = false;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(6, false);\n    setAndRead(7, false);\n    setAndRead(6, true);\n    setAndRead(7, true);\n\n    EXPECT_EQ(updates_array_idx, 1); // one update received\n    EXPECT_EQ(updates[0].index, 3);\n    EXPECT_EQ(updates[0].clockwise, true);\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 0); // No events should be queued on master\n}\n\nTEST_F(EncoderSplitTestLeftGreaterThanRight, TestOneClockwiseLeftSlave) {\n    isMaster   = false;\n    isLeftHand = true;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 1); // One event should be queued on slave\n}\n\nTEST_F(EncoderSplitTestLeftGreaterThanRight, TestOneClockwiseRightSlave) {\n    isMaster   = false;\n    isLeftHand = false;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(6, false);\n    setAndRead(7, false);\n    setAndRead(6, true);\n    setAndRead(7, true);\n\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 1); // One event should be queued on slave\n}\n"
  },
  {
    "path": "quantum/encoder/tests/encoder_tests_split_left_lt_right.cpp",
    "content": "/* Copyright 2021 Balz Guenat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include <vector>\n#include <algorithm>\n#include <stdio.h>\n\nextern \"C\" {\n#include \"encoder.h\"\n#include \"encoder/tests/mock_split.h\"\n}\n\nstruct update {\n    int8_t index;\n    bool   clockwise;\n};\n\nuint8_t updates_array_idx = 0;\nupdate  updates[32];\n\nbool isMaster;\nbool isLeftHand;\n\nextern \"C\" {\nbool is_keyboard_master(void) {\n    return isMaster;\n}\n\nbool encoder_update_kb(uint8_t index, bool clockwise) {\n    if (!is_keyboard_master()) {\n        // this method has no effect on slave half\n        printf(\"ignoring update on slave (%d,%s)\\n\", index, clockwise ? \"CW\" : \"CC\");\n        return true;\n    }\n    updates[updates_array_idx % 32] = {index, clockwise};\n    updates_array_idx++;\n    return true;\n}\n};\n\nbool setAndRead(pin_t pin, bool val) {\n    setPin(pin, val);\n    return encoder_task();\n}\n\nclass EncoderSplitTestLeftLessThanRight : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        updates_array_idx = 0;\n        for (int i = 0; i < 32; i++) {\n            pinIsInputHigh[i] = 0;\n            pins[i]           = 0;\n        }\n    }\n};\n\nTEST_F(EncoderSplitTestLeftLessThanRight, TestInitLeft) {\n    isLeftHand = true;\n    encoder_init();\n    EXPECT_EQ(pinIsInputHigh[0], true);\n    EXPECT_EQ(pinIsInputHigh[1], true);\n    EXPECT_EQ(pinIsInputHigh[2], true);\n    EXPECT_EQ(pinIsInputHigh[3], true);\n    EXPECT_EQ(pinIsInputHigh[4], false);\n    EXPECT_EQ(pinIsInputHigh[5], false);\n    EXPECT_EQ(pinIsInputHigh[6], false);\n    EXPECT_EQ(pinIsInputHigh[7], false);\n    EXPECT_EQ(pinIsInputHigh[8], false);\n    EXPECT_EQ(pinIsInputHigh[9], false);\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n}\n\nTEST_F(EncoderSplitTestLeftLessThanRight, TestInitRight) {\n    isLeftHand = false;\n    encoder_init();\n    EXPECT_EQ(pinIsInputHigh[0], false);\n    EXPECT_EQ(pinIsInputHigh[1], false);\n    EXPECT_EQ(pinIsInputHigh[2], false);\n    EXPECT_EQ(pinIsInputHigh[3], false);\n    EXPECT_EQ(pinIsInputHigh[4], true);\n    EXPECT_EQ(pinIsInputHigh[5], true);\n    EXPECT_EQ(pinIsInputHigh[6], true);\n    EXPECT_EQ(pinIsInputHigh[7], true);\n    EXPECT_EQ(pinIsInputHigh[8], true);\n    EXPECT_EQ(pinIsInputHigh[9], true);\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n}\n\nTEST_F(EncoderSplitTestLeftLessThanRight, TestOneClockwiseLeftMaster) {\n    isMaster   = true;\n    isLeftHand = true;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n\n    EXPECT_EQ(updates_array_idx, 1); // one update received\n    EXPECT_EQ(updates[0].index, 0);\n    EXPECT_EQ(updates[0].clockwise, true);\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 0); // No events should be queued on master\n}\n\nTEST_F(EncoderSplitTestLeftLessThanRight, TestOneClockwiseRightMaster) {\n    isMaster   = true;\n    isLeftHand = false;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(6, false);\n    setAndRead(7, false);\n    setAndRead(6, true);\n    setAndRead(7, true);\n\n    EXPECT_EQ(updates_array_idx, 1); // one update received\n    EXPECT_EQ(updates[0].index, 3);\n    EXPECT_EQ(updates[0].clockwise, true);\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 0); // No events should be queued on master\n}\n\nTEST_F(EncoderSplitTestLeftLessThanRight, TestOneClockwiseLeftSlave) {\n    isMaster   = false;\n    isLeftHand = true;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 1); // One event should be queued on slave\n}\n\nTEST_F(EncoderSplitTestLeftLessThanRight, TestOneClockwiseRightSlave) {\n    isMaster   = false;\n    isLeftHand = false;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(6, false);\n    setAndRead(7, false);\n    setAndRead(6, true);\n    setAndRead(7, true);\n\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 1); // One event should be queued on slave\n}\n"
  },
  {
    "path": "quantum/encoder/tests/encoder_tests_split_no_left.cpp",
    "content": "/* Copyright 2021 Balz Guenat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include <vector>\n#include <algorithm>\n#include <stdio.h>\n\nextern \"C\" {\n#include \"encoder.h\"\n#include \"encoder/tests/mock_split.h\"\n}\n\nstruct update {\n    int8_t index;\n    bool   clockwise;\n};\n\nuint8_t updates_array_idx = 0;\nupdate  updates[32];\n\nbool isMaster;\nbool isLeftHand;\n\nextern \"C\" {\nbool is_keyboard_master(void) {\n    return isMaster;\n}\n\nbool encoder_update_kb(uint8_t index, bool clockwise) {\n    if (!is_keyboard_master()) {\n        // this method has no effect on slave half\n        printf(\"ignoring update on slave (%d,%s)\\n\", index, clockwise ? \"CW\" : \"CC\");\n        return true;\n    }\n    updates[updates_array_idx % 32] = {index, clockwise};\n    updates_array_idx++;\n    return true;\n}\n};\n\nbool setAndRead(pin_t pin, bool val) {\n    setPin(pin, val);\n    return encoder_task();\n}\n\nclass EncoderSplitTestNoLeft : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        updates_array_idx = 0;\n        for (int i = 0; i < 32; i++) {\n            pinIsInputHigh[i] = 0;\n            pins[i]           = 0;\n        }\n    }\n};\n\nTEST_F(EncoderSplitTestNoLeft, TestInitLeft) {\n    isLeftHand = true;\n    encoder_init();\n    EXPECT_EQ(pinIsInputHigh[0], false);\n    EXPECT_EQ(pinIsInputHigh[1], false);\n    EXPECT_EQ(pinIsInputHigh[2], false);\n    EXPECT_EQ(pinIsInputHigh[3], false);\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n}\n\nTEST_F(EncoderSplitTestNoLeft, TestInitRight) {\n    isLeftHand = false;\n    encoder_init();\n    EXPECT_EQ(pinIsInputHigh[0], true);\n    EXPECT_EQ(pinIsInputHigh[1], true);\n    EXPECT_EQ(pinIsInputHigh[2], true);\n    EXPECT_EQ(pinIsInputHigh[3], true);\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n}\n\nTEST_F(EncoderSplitTestNoLeft, TestOneClockwiseLeftMaster) {\n    isMaster   = true;\n    isLeftHand = false;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(2, false);\n    setAndRead(3, false);\n    setAndRead(2, true);\n    setAndRead(3, true);\n\n    EXPECT_EQ(updates_array_idx, 1); // one update received\n    EXPECT_EQ(updates[0].index, 1);\n    EXPECT_EQ(updates[0].clockwise, true);\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 0); // No events should be queued on master\n}\n\nTEST_F(EncoderSplitTestNoLeft, TestOneClockwiseRightSlave) {\n    isMaster   = false;\n    isLeftHand = false;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(2, false);\n    setAndRead(3, false);\n    setAndRead(2, true);\n    setAndRead(3, true);\n\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 1); // One event should be queued on slave\n}\n"
  },
  {
    "path": "quantum/encoder/tests/encoder_tests_split_no_right.cpp",
    "content": "/* Copyright 2021 Balz Guenat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include <vector>\n#include <algorithm>\n#include <stdio.h>\n\nextern \"C\" {\n#include \"encoder.h\"\n#include \"encoder/tests/mock_split.h\"\n}\n\nstruct update {\n    int8_t index;\n    bool   clockwise;\n};\n\nuint8_t updates_array_idx = 0;\nupdate  updates[32];\n\nbool isMaster;\nbool isLeftHand;\n\nextern \"C\" {\nbool is_keyboard_master(void) {\n    return isMaster;\n}\n\nbool encoder_update_kb(uint8_t index, bool clockwise) {\n    if (!is_keyboard_master()) {\n        // this method has no effect on slave half\n        printf(\"ignoring update on slave (%d,%s)\\n\", index, clockwise ? \"CW\" : \"CC\");\n        return true;\n    }\n    updates[updates_array_idx % 32] = {index, clockwise};\n    updates_array_idx++;\n    return true;\n}\n};\n\nbool setAndRead(pin_t pin, bool val) {\n    setPin(pin, val);\n    return encoder_task();\n}\n\nclass EncoderSplitTestNoRight : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        updates_array_idx = 0;\n        for (int i = 0; i < 32; i++) {\n            pinIsInputHigh[i] = 0;\n            pins[i]           = 0;\n        }\n    }\n};\n\nTEST_F(EncoderSplitTestNoRight, TestInitLeft) {\n    isLeftHand = true;\n    encoder_init();\n    EXPECT_EQ(pinIsInputHigh[0], true);\n    EXPECT_EQ(pinIsInputHigh[1], true);\n    EXPECT_EQ(pinIsInputHigh[2], true);\n    EXPECT_EQ(pinIsInputHigh[3], true);\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n}\n\nTEST_F(EncoderSplitTestNoRight, TestInitRight) {\n    isLeftHand = false;\n    encoder_init();\n    EXPECT_EQ(pinIsInputHigh[0], false);\n    EXPECT_EQ(pinIsInputHigh[1], false);\n    EXPECT_EQ(pinIsInputHigh[2], false);\n    EXPECT_EQ(pinIsInputHigh[3], false);\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n}\n\nTEST_F(EncoderSplitTestNoRight, TestOneClockwiseLeftMaster) {\n    isMaster   = true;\n    isLeftHand = true;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(2, false);\n    setAndRead(3, false);\n    setAndRead(2, true);\n    setAndRead(3, true);\n\n    EXPECT_EQ(updates_array_idx, 1); // one update received\n    EXPECT_EQ(updates[0].index, 1);\n    EXPECT_EQ(updates[0].clockwise, true);\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 0); // No events should be queued on master\n}\n\nTEST_F(EncoderSplitTestNoRight, TestOneClockwiseRightSlave) {\n    isMaster   = false;\n    isLeftHand = true;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(2, false);\n    setAndRead(3, false);\n    setAndRead(2, true);\n    setAndRead(3, true);\n\n    EXPECT_EQ(updates_array_idx, 0); // no updates received\n\n    int              events_queued = 0;\n    encoder_events_t events;\n    encoder_retrieve_events(&events);\n    while (events.tail != events.head) {\n        events.tail = (events.tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n        ++events_queued;\n    }\n    EXPECT_EQ(events_queued, 1); // One event should be queued on slave\n}\n"
  },
  {
    "path": "quantum/encoder/tests/encoder_tests_split_role.cpp",
    "content": "/* Copyright 2021 Balz Guenat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include <vector>\n#include <algorithm>\n#include <stdio.h>\n\nextern \"C\" {\n#include \"encoder.h\"\n#include \"keyboard.h\"\n#include \"encoder/tests/mock_split.h\"\n}\n\nstruct update {\n    int8_t index;\n    bool   clockwise;\n};\n\nuint8_t num_updates = 0;\n\nbool isMaster;\nbool isLeftHand;\n\nbool is_keyboard_master(void) {\n    return isMaster;\n}\n\nbool encoder_update_kb(uint8_t index, bool clockwise) {\n    if (!isMaster) {\n        ADD_FAILURE() << \"We shouldn't get here.\";\n    }\n    num_updates++;\n    return true;\n}\n\nbool setAndRead(pin_t pin, bool val) {\n    setPin(pin, val);\n    return encoder_task();\n}\n\nclass EncoderSplitTestRole : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        num_updates = 0;\n        for (int i = 0; i < 32; i++) {\n            pinIsInputHigh[i] = 0;\n            pins[i]           = 0;\n        }\n    }\n};\n\nTEST_F(EncoderSplitTestRole, TestPrimaryLeft) {\n    isMaster   = true;\n    isLeftHand = true;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n\n    EXPECT_EQ(num_updates, 1); // one update received\n}\n\nTEST_F(EncoderSplitTestRole, TestPrimaryRight) {\n    isMaster   = true;\n    isLeftHand = false;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(6, false);\n    setAndRead(7, false);\n    setAndRead(6, true);\n    setAndRead(7, true);\n\n    EXPECT_EQ(num_updates, 1); // one update received\n}\n\nTEST_F(EncoderSplitTestRole, TestNotPrimaryLeft) {\n    isMaster   = false;\n    isLeftHand = true;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(0, false);\n    setAndRead(1, false);\n    setAndRead(0, true);\n    setAndRead(1, true);\n\n    EXPECT_EQ(num_updates, 0); // zero updates received\n}\n\nTEST_F(EncoderSplitTestRole, TestNotPrimaryRight) {\n    isMaster   = false;\n    isLeftHand = false;\n    encoder_init();\n    // send 4 pulses. with resolution 4, that's one step and we should get 1 update.\n    setAndRead(6, false);\n    setAndRead(7, false);\n    setAndRead(6, true);\n    setAndRead(7, true);\n\n    EXPECT_EQ(num_updates, 0); // zero updates received\n}\n"
  },
  {
    "path": "quantum/encoder/tests/mock.c",
    "content": "/* Copyright 2021 Balz Guenat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"mock.h\"\n\nbool pins[32]           = {0};\nbool pinIsInputHigh[32] = {0};\n\nuint8_t mock_set_pin_input_high(pin_t pin) {\n    // dprintf(\"Setting pin %d input high.\", pin);\n    pins[pin]           = true;\n    pinIsInputHigh[pin] = true;\n    return 0;\n}\n\nbool mock_read_pin(pin_t pin) {\n    return pins[pin];\n}\n\nbool setPin(pin_t pin, bool val) {\n    pins[pin] = val;\n    return val;\n}\n\n__attribute__((weak)) bool is_keyboard_master(void) {\n    return true;\n}\n"
  },
  {
    "path": "quantum/encoder/tests/mock.h",
    "content": "/* Copyright 2021 Balz Guenat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\ntypedef uint8_t pin_t;\n\nextern bool pins[];\nextern bool pinIsInputHigh[];\n\n#define gpio_set_pin_input_high(pin) (mock_set_pin_input_high(pin))\n#define gpio_read_pin(pin) (mock_read_pin(pin))\n\nuint8_t mock_set_pin_input_high(pin_t pin);\n\nbool mock_read_pin(pin_t pin);\n\nbool setPin(pin_t pin, bool val);\n"
  },
  {
    "path": "quantum/encoder/tests/mock_split.c",
    "content": "/* Copyright 2021 Balz Guenat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"mock_split.h\"\n\nbool pins[32]           = {0};\nbool pinIsInputHigh[32] = {0};\n\nuint8_t mock_set_pin_input_high(pin_t pin) {\n    // dprintf(\"Setting pin %d input high.\", pin);\n    pins[pin]           = true;\n    pinIsInputHigh[pin] = true;\n    return 0;\n}\n\nbool mock_read_pin(pin_t pin) {\n    return pins[pin];\n}\n\nbool setPin(pin_t pin, bool val) {\n    pins[pin] = val;\n    return val;\n}\n\nvoid last_encoder_activity_trigger(void) {}\n"
  },
  {
    "path": "quantum/encoder/tests/mock_split.h",
    "content": "/* Copyright 2021 Balz Guenat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#define SPLIT_KEYBOARD\ntypedef uint8_t pin_t;\n\nextern bool pins[];\nextern bool pinIsInputHigh[];\n\n#define gpio_set_pin_input_high(pin) (mock_set_pin_input_high(pin))\n#define gpio_read_pin(pin) (mock_read_pin(pin))\n\nuint8_t mock_set_pin_input_high(pin_t pin);\n\nbool mock_read_pin(pin_t pin);\n\nbool setPin(pin_t pin, bool val);\n"
  },
  {
    "path": "quantum/encoder/tests/rules.mk",
    "content": "encoder_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SINGLE\nencoder_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock.h\n\nencoder_SRC := \\\n\tplatforms/test/timer.c \\\n\tdrivers/encoder/encoder_quadrature.c \\\n\t$(QUANTUM_PATH)/encoder/tests/mock.c \\\n\t$(QUANTUM_PATH)/encoder/tests/encoder_tests.cpp \\\n\t$(QUANTUM_PATH)/encoder.c\n\nencoder_split_left_eq_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT\nencoder_split_left_eq_right_INC := $(QUANTUM_PATH)/split_common\nencoder_split_left_eq_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_left_eq_right.h\n\nencoder_split_left_eq_right_SRC := \\\n\tplatforms/test/timer.c \\\n\tdrivers/encoder/encoder_quadrature.c \\\n\t$(QUANTUM_PATH)/encoder/tests/mock_split.c \\\n\t$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_left_eq_right.cpp \\\n\t$(QUANTUM_PATH)/encoder.c\n\nencoder_split_left_gt_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT\nencoder_split_left_gt_right_INC := $(QUANTUM_PATH)/split_common\nencoder_split_left_gt_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_left_gt_right.h\n\nencoder_split_left_gt_right_SRC := \\\n\tplatforms/test/timer.c \\\n\tdrivers/encoder/encoder_quadrature.c \\\n\t$(QUANTUM_PATH)/encoder/tests/mock_split.c \\\n\t$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_left_gt_right.cpp \\\n\t$(QUANTUM_PATH)/encoder.c\n\nencoder_split_left_lt_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT\nencoder_split_left_lt_right_INC := $(QUANTUM_PATH)/split_common\nencoder_split_left_lt_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_left_lt_right.h\n\nencoder_split_left_lt_right_SRC := \\\n\tplatforms/test/timer.c \\\n\tdrivers/encoder/encoder_quadrature.c \\\n\t$(QUANTUM_PATH)/encoder/tests/mock_split.c \\\n\t$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_left_lt_right.cpp \\\n\t$(QUANTUM_PATH)/encoder.c\n\nencoder_split_no_left_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT\nencoder_split_no_left_INC := $(QUANTUM_PATH)/split_common\nencoder_split_no_left_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_no_left.h\n\nencoder_split_no_left_SRC := \\\n\tplatforms/test/timer.c \\\n\tdrivers/encoder/encoder_quadrature.c \\\n\t$(QUANTUM_PATH)/encoder/tests/mock_split.c \\\n\t$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_no_left.cpp \\\n\t$(QUANTUM_PATH)/encoder.c\n\nencoder_split_no_right_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT\nencoder_split_no_right_INC := $(QUANTUM_PATH)/split_common\nencoder_split_no_right_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_no_right.h\n\nencoder_split_no_right_SRC := \\\n\tplatforms/test/timer.c \\\n\tdrivers/encoder/encoder_quadrature.c \\\n\t$(QUANTUM_PATH)/encoder/tests/mock_split.c \\\n\t$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_no_right.cpp \\\n\t$(QUANTUM_PATH)/encoder.c\n\nencoder_split_role_DEFS := -DENCODER_TESTS -DENCODER_ENABLE -DENCODER_MOCK_SPLIT\nencoder_split_role_INC := $(QUANTUM_PATH)/split_common\nencoder_split_role_CONFIG := $(QUANTUM_PATH)/encoder/tests/config_mock_split_role.h\n\nencoder_split_role_SRC := \\\n\tplatforms/test/timer.c \\\n\tdrivers/encoder/encoder_quadrature.c \\\n\t$(QUANTUM_PATH)/encoder/tests/mock_split.c \\\n\t$(QUANTUM_PATH)/encoder/tests/encoder_tests_split_role.cpp \\\n\t$(QUANTUM_PATH)/encoder.c\n"
  },
  {
    "path": "quantum/encoder/tests/testlist.mk",
    "content": "TEST_LIST += \\\n\tencoder \\\n\tencoder_split_left_eq_right \\\n\tencoder_split_left_gt_right \\\n\tencoder_split_left_lt_right \\\n\tencoder_split_no_left \\\n\tencoder_split_no_right \\\n\tencoder_split_role \\\n"
  },
  {
    "path": "quantum/encoder.c",
    "content": "// Copyright 2022-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <string.h>\n#include \"action.h\"\n#include \"encoder.h\"\n#include \"wait.h\"\n\n#ifndef ENCODER_MAP_KEY_DELAY\n#    define ENCODER_MAP_KEY_DELAY TAP_CODE_DELAY\n#endif\n\n__attribute__((weak)) bool should_process_encoder(void) {\n    return is_keyboard_master();\n}\n\nstatic encoder_events_t encoder_events;\nstatic bool             signal_queue_drain = false;\n\nvoid encoder_init(void) {\n    memset(&encoder_events, 0, sizeof(encoder_events));\n    encoder_driver_init();\n}\n\nstatic void encoder_queue_drain(void) {\n    encoder_events.tail     = encoder_events.head;\n    encoder_events.dequeued = encoder_events.enqueued;\n}\n\nstatic bool encoder_handle_queue(void) {\n    bool    changed = false;\n    uint8_t index;\n    bool    clockwise;\n    while (encoder_dequeue_event(&index, &clockwise)) {\n#ifdef ENCODER_MAP_ENABLE\n\n        // The delays below cater for Windows and its wonderful requirements.\n        action_exec(clockwise ? MAKE_ENCODER_CW_EVENT(index, true) : MAKE_ENCODER_CCW_EVENT(index, true));\n#    if ENCODER_MAP_KEY_DELAY > 0\n        wait_ms(ENCODER_MAP_KEY_DELAY);\n#    endif // ENCODER_MAP_KEY_DELAY > 0\n\n        action_exec(clockwise ? MAKE_ENCODER_CW_EVENT(index, false) : MAKE_ENCODER_CCW_EVENT(index, false));\n#    if ENCODER_MAP_KEY_DELAY > 0\n        wait_ms(ENCODER_MAP_KEY_DELAY);\n#    endif // ENCODER_MAP_KEY_DELAY > 0\n\n#else // ENCODER_MAP_ENABLE\n\n        encoder_update_kb(index, clockwise);\n\n#endif // ENCODER_MAP_ENABLE\n\n        changed = true;\n    }\n    return changed;\n}\n\nbool encoder_task(void) {\n    bool changed = false;\n\n#ifdef SPLIT_KEYBOARD\n    // Attempt to process existing encoder events in case split handling has already enqueued events\n    if (should_process_encoder()) {\n        changed |= encoder_handle_queue();\n    }\n#endif // SPLIT_KEYBOARD\n\n    if (signal_queue_drain) {\n        signal_queue_drain = false;\n        encoder_queue_drain();\n    }\n\n    // Let the encoder driver produce events\n    encoder_driver_task();\n\n    // Process any events that were enqueued\n    if (should_process_encoder()) {\n        changed |= encoder_handle_queue();\n    }\n\n    return changed;\n}\n\nbool encoder_queue_full_advanced(encoder_events_t *events) {\n    return events->tail == (events->head + 1) % MAX_QUEUED_ENCODER_EVENTS;\n}\n\nbool encoder_queue_full(void) {\n    return encoder_queue_full_advanced(&encoder_events);\n}\n\nbool encoder_queue_empty_advanced(encoder_events_t *events) {\n    return events->head == events->tail;\n}\n\nbool encoder_queue_empty(void) {\n    return encoder_queue_empty_advanced(&encoder_events);\n}\n\nbool encoder_queue_event_advanced(encoder_events_t *events, uint8_t index, bool clockwise) {\n    // Drop out if we're full\n    if (encoder_queue_full_advanced(events)) {\n        return false;\n    }\n\n    // Append the event\n    encoder_event_t new_event   = {.index = index, .clockwise = clockwise ? 1 : 0};\n    events->queue[events->head] = new_event;\n\n    // Increment the head index\n    events->head = (events->head + 1) % MAX_QUEUED_ENCODER_EVENTS;\n    events->enqueued++;\n\n    return true;\n}\n\nbool encoder_dequeue_event_advanced(encoder_events_t *events, uint8_t *index, bool *clockwise) {\n    if (encoder_queue_empty_advanced(events)) {\n        return false;\n    }\n\n    // Retrieve the event\n    encoder_event_t event = events->queue[events->tail];\n    *index                = event.index;\n    *clockwise            = event.clockwise;\n\n    // Increment the tail index\n    events->tail = (events->tail + 1) % MAX_QUEUED_ENCODER_EVENTS;\n    events->dequeued++;\n\n    return true;\n}\n\nbool encoder_queue_event(uint8_t index, bool clockwise) {\n    return encoder_queue_event_advanced(&encoder_events, index, clockwise);\n}\n\nbool encoder_dequeue_event(uint8_t *index, bool *clockwise) {\n    return encoder_dequeue_event_advanced(&encoder_events, index, clockwise);\n}\n\nvoid encoder_retrieve_events(encoder_events_t *events) {\n    memcpy(events, &encoder_events, sizeof(encoder_events));\n}\n\nvoid encoder_signal_queue_drain(void) {\n    signal_queue_drain = true;\n}\n\n__attribute__((weak)) bool encoder_update_user(uint8_t index, bool clockwise) {\n    return true;\n}\n\n__attribute__((weak)) bool encoder_update_kb(uint8_t index, bool clockwise) {\n    bool res = encoder_update_user(index, clockwise);\n#if !defined(ENCODER_TESTS)\n    if (res) {\n        if (clockwise) {\n#    if defined(EXTRAKEY_ENABLE)\n            tap_code_delay(KC_VOLU, 10);\n#    elif defined(MOUSEKEY_ENABLE)\n            tap_code_delay(QK_MOUSE_WHEEL_UP, 10);\n#    else\n            tap_code_delay(KC_PGDN, 10);\n#    endif\n        } else {\n#    if defined(EXTRAKEY_ENABLE)\n            tap_code_delay(KC_VOLD, 10);\n#    elif defined(MOUSEKEY_ENABLE)\n            tap_code_delay(QK_MOUSE_WHEEL_DOWN, 10);\n#    else\n            tap_code_delay(KC_PGUP, 10);\n#    endif\n        }\n    }\n#endif // ENCODER_TESTS\n    return res;\n}\n"
  },
  {
    "path": "quantum/encoder.h",
    "content": "/*\n * Copyright 2018 Jack Humbert <jack.humb@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"gpio.h\"\n#include \"util.h\"\n\n// ======== DEPRECATED DEFINES - DO NOT USE ========\n#ifdef ENCODERS_PAD_A\n#    define ENCODER_A_PINS ENCODERS_PAD_A\n#endif\n#ifdef ENCODERS_PAD_B\n#    define ENCODER_B_PINS ENCODERS_PAD_B\n#endif\n#ifdef ENCODERS_PAD_A_RIGHT\n#    define ENCODER_A_PINS_RIGHT ENCODERS_PAD_A_RIGHT\n#endif\n#ifdef ENCODERS_PAD_B_RIGHT\n#    define ENCODER_B_PINS_RIGHT ENCODERS_PAD_B_RIGHT\n#endif\n// ========\n\n#ifdef ENCODER_ENABLE\n\n__attribute__((weak)) bool should_process_encoder(void);\n\nvoid encoder_init(void);\nbool encoder_task(void);\nbool encoder_queue_event(uint8_t index, bool clockwise);\nbool encoder_dequeue_event(uint8_t *index, bool *clockwise);\n\nbool encoder_update_kb(uint8_t index, bool clockwise);\nbool encoder_update_user(uint8_t index, bool clockwise);\n\n#    ifdef SPLIT_KEYBOARD\n\n#        if defined(ENCODER_A_PINS_RIGHT)\n#            ifndef NUM_ENCODERS_LEFT\n#                define NUM_ENCODERS_LEFT ARRAY_SIZE(((pin_t[])ENCODER_A_PINS))\n#            endif\n#            ifndef NUM_ENCODERS_RIGHT\n#                define NUM_ENCODERS_RIGHT ARRAY_SIZE(((pin_t[])ENCODER_A_PINS_RIGHT))\n#            endif\n#        else\n#            ifndef NUM_ENCODERS_LEFT\n#                define NUM_ENCODERS_LEFT ARRAY_SIZE(((pin_t[])ENCODER_A_PINS))\n#            endif\n#            ifndef NUM_ENCODERS_RIGHT\n#                define NUM_ENCODERS_RIGHT NUM_ENCODERS_LEFT\n#            endif\n#        endif\n#        ifndef NUM_ENCODERS\n#            define NUM_ENCODERS (NUM_ENCODERS_LEFT + NUM_ENCODERS_RIGHT)\n#        endif\n\n#    else // SPLIT_KEYBOARD\n\n#        ifndef NUM_ENCODERS\n#            define NUM_ENCODERS ARRAY_SIZE(((pin_t[])ENCODER_A_PINS))\n#        endif\n#        define NUM_ENCODERS_LEFT NUM_ENCODERS\n#        define NUM_ENCODERS_RIGHT 0\n\n#    endif // SPLIT_KEYBOARD\n\n#    define NUM_ENCODERS_MAX_PER_SIDE MAX(NUM_ENCODERS_LEFT, NUM_ENCODERS_RIGHT)\n\n#    ifndef MAX_QUEUED_ENCODER_EVENTS\n#        define MAX_QUEUED_ENCODER_EVENTS MAX(4, ((NUM_ENCODERS_MAX_PER_SIDE) + 1))\n#    endif // MAX_QUEUED_ENCODER_EVENTS\n\ntypedef struct encoder_event_t {\n    uint8_t index : 7;\n    uint8_t clockwise : 1;\n} encoder_event_t;\n\ntypedef struct encoder_events_t {\n    uint8_t         enqueued;\n    uint8_t         dequeued;\n    uint8_t         head;\n    uint8_t         tail;\n    encoder_event_t queue[MAX_QUEUED_ENCODER_EVENTS];\n} encoder_events_t;\n\n// Get the current queued events\nvoid encoder_retrieve_events(encoder_events_t *events);\n\n// Encoder event queue management\nbool encoder_queue_event_advanced(encoder_events_t *events, uint8_t index, bool clockwise);\nbool encoder_dequeue_event_advanced(encoder_events_t *events, uint8_t *index, bool *clockwise);\n\n// Reset the queue to be empty\nvoid encoder_signal_queue_drain(void);\n\n#    ifdef ENCODER_MAP_ENABLE\n#        define NUM_DIRECTIONS 2\n#        define ENCODER_CCW_CW(ccw, cw) \\\n            { (cw), (ccw) }\nextern const uint16_t encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS];\n#    endif // ENCODER_MAP_ENABLE\n\n// \"Custom encoder lite\" support\nvoid encoder_driver_init(void);\nvoid encoder_driver_task(void);\n\n#endif // ENCODER_ENABLE\n"
  },
  {
    "path": "quantum/haptic.c",
    "content": "/* Copyright 2019 ishtob\n * Driver for haptic feedback written for QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"haptic.h\"\n#include \"eeconfig.h\"\n#include \"debug.h\"\n#include \"usb_device_state.h\"\n#include \"gpio.h\"\n#include \"keyboard.h\"\n\n#ifdef HAPTIC_DRV2605L\n#    include \"drv2605l.h\"\n#endif\n#ifdef HAPTIC_SOLENOID\n#    include \"solenoid.h\"\n#endif\n\n#if defined(SPLIT_KEYBOARD) && defined(SPLIT_HAPTIC_ENABLE)\nextern uint8_t split_haptic_play;\n#endif\n\nhaptic_config_t haptic_config;\n\nstatic void update_haptic_enable_gpios(void) {\n    if (haptic_config.enable && ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state_get_configure_state() == USB_DEVICE_STATE_CONFIGURED))) {\n#if defined(HAPTIC_ENABLE_PIN)\n        HAPTIC_ENABLE_PIN_WRITE_ACTIVE();\n#endif\n#if defined(HAPTIC_ENABLE_STATUS_LED)\n        HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE();\n#endif\n    } else {\n#if defined(HAPTIC_ENABLE_PIN)\n        HAPTIC_ENABLE_PIN_WRITE_INACTIVE();\n#endif\n#if defined(HAPTIC_ENABLE_STATUS_LED)\n        HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE();\n#endif\n    }\n}\n\nstatic void set_haptic_config_enable(bool enabled) {\n    haptic_config.enable = enabled;\n    update_haptic_enable_gpios();\n}\n\nvoid haptic_init(void) {\n// only initialize on secondary boards if the user desires\n#if defined(SPLIT_KEYBOARD) && !defined(SPLIT_HAPTIC_ENABLE)\n    if (!is_keyboard_master()) return;\n#endif\n\n    if (!eeconfig_is_enabled()) {\n        eeconfig_init();\n    }\n    eeconfig_read_haptic(&haptic_config);\n#ifdef HAPTIC_SOLENOID\n    solenoid_set_dwell(haptic_config.dwell);\n#endif\n    if ((haptic_config.raw == 0)\n#ifdef HAPTIC_SOLENOID\n        || (haptic_config.dwell == 0)\n#endif\n    ) {\n        // this will be called, if the eeprom is not corrupt,\n        // but the previous firmware didn't have haptic enabled,\n        // or the previous firmware didn't have solenoid enabled,\n        // and the current one has solenoid enabled.\n        haptic_reset();\n    } else {\n        // Haptic configuration has been loaded through the \"raw\" union item.\n        // This is to execute any side effects of the configuration.\n        set_haptic_config_enable(haptic_config.enable);\n    }\n#ifdef HAPTIC_SOLENOID\n    solenoid_setup();\n    dprintf(\"Solenoid driver initialized\\n\");\n#endif\n#ifdef HAPTIC_DRV2605L\n    drv2605l_init();\n    dprintf(\"DRV2605 driver initialized\\n\");\n#endif\n    eeconfig_debug_haptic();\n#ifdef HAPTIC_ENABLE_PIN\n    gpio_set_pin_output(HAPTIC_ENABLE_PIN);\n#endif\n#ifdef HAPTIC_ENABLE_STATUS_LED\n    gpio_set_pin_output(HAPTIC_ENABLE_STATUS_LED);\n#endif\n}\n\nvoid haptic_task(void) {\n#ifdef HAPTIC_SOLENOID\n// Only run task on seconary boards if the user desires\n#    if defined(SPLIT_KEYBOARD) && !defined(SPLIT_HAPTIC_ENABLE)\n    if (!is_keyboard_master()) return;\n#    endif\n    solenoid_check();\n#endif // HAPTIC_SOLENOID\n}\n\nvoid eeconfig_debug_haptic(void) {\n    dprintf(\"haptic_config eeprom\\n\");\n    dprintf(\"haptic_config.enable = %d\\n\", haptic_config.enable);\n    dprintf(\"haptic_config.mode = %d\\n\", haptic_config.mode);\n}\n\nvoid haptic_enable(void) {\n    set_haptic_config_enable(true);\n    dprintf(\"haptic_config.enable = %u\\n\", haptic_config.enable);\n    eeconfig_update_haptic(&haptic_config);\n}\n\nvoid haptic_disable(void) {\n    set_haptic_config_enable(false);\n    dprintf(\"haptic_config.enable = %u\\n\", haptic_config.enable);\n    eeconfig_update_haptic(&haptic_config);\n}\n\nvoid haptic_toggle(void) {\n    if (haptic_config.enable) {\n        haptic_disable();\n    } else {\n        haptic_enable();\n    }\n    eeconfig_update_haptic(&haptic_config);\n}\n\nvoid haptic_feedback_toggle(void) {\n    haptic_config.feedback++;\n    if (haptic_config.feedback >= HAPTIC_FEEDBACK_MAX) haptic_config.feedback = KEY_PRESS;\n    dprintf(\"haptic_config.feedback = %u\\n\", !haptic_config.feedback);\n    eeconfig_update_haptic(&haptic_config);\n}\n\nvoid haptic_buzz_toggle(void) {\n    bool buzz_stat     = !haptic_config.buzz;\n    haptic_config.buzz = buzz_stat;\n    haptic_set_buzz(buzz_stat);\n}\n\nvoid haptic_mode_increase(void) {\n    uint8_t mode = haptic_config.mode + 1;\n#ifdef HAPTIC_DRV2605L\n    if (haptic_config.mode >= DRV2605L_EFFECT_COUNT) {\n        mode = 1;\n    }\n#endif\n    haptic_set_mode(mode);\n}\n\nvoid haptic_mode_decrease(void) {\n    uint8_t mode = haptic_config.mode - 1;\n#ifdef HAPTIC_DRV2605L\n    if (haptic_config.mode < 1) {\n        mode = (DRV2605L_EFFECT_COUNT - 1);\n    }\n#endif\n    haptic_set_mode(mode);\n}\n\nvoid haptic_dwell_increase(void) {\n#ifdef HAPTIC_SOLENOID\n    int16_t next_dwell = ((int16_t)haptic_config.dwell) + SOLENOID_DWELL_STEP_SIZE;\n    if (haptic_config.dwell >= SOLENOID_MAX_DWELL) {\n        // if it's already at max, we wrap back to min\n        next_dwell = SOLENOID_MIN_DWELL;\n    } else if (next_dwell > SOLENOID_MAX_DWELL) {\n        // if we overshoot the max, then cap at max\n        next_dwell = SOLENOID_MAX_DWELL;\n    }\n    solenoid_set_dwell(next_dwell);\n#else\n    int16_t next_dwell = ((int16_t)haptic_config.dwell) + 1;\n#endif\n    haptic_set_dwell(next_dwell);\n}\n\nvoid haptic_dwell_decrease(void) {\n#ifdef HAPTIC_SOLENOID\n    int16_t next_dwell = ((int16_t)haptic_config.dwell) - SOLENOID_DWELL_STEP_SIZE;\n    if (haptic_config.dwell <= SOLENOID_MIN_DWELL) {\n        // if it's already at min, we wrap to max\n        next_dwell = SOLENOID_MAX_DWELL;\n    } else if (next_dwell < SOLENOID_MIN_DWELL) {\n        // if we go below min, then we cap to min\n        next_dwell = SOLENOID_MIN_DWELL;\n    }\n    solenoid_set_dwell(next_dwell);\n#else\n    int16_t next_dwell = ((int16_t)haptic_config.dwell) - 1;\n#endif\n    haptic_set_dwell(next_dwell);\n}\n\nvoid haptic_reset(void) {\n    set_haptic_config_enable(true);\n    uint8_t feedback       = HAPTIC_DEFAULT_FEEDBACK;\n    haptic_config.feedback = feedback;\n#ifdef HAPTIC_DRV2605L\n    uint8_t mode       = HAPTIC_DEFAULT_MODE;\n    haptic_config.mode = mode;\n#endif\n#ifdef HAPTIC_SOLENOID\n    uint8_t dwell       = SOLENOID_DEFAULT_DWELL;\n    haptic_config.dwell = dwell;\n    haptic_config.buzz  = SOLENOID_DEFAULT_BUZZ;\n    solenoid_set_dwell(dwell);\n#else\n    // This is to trigger haptic_reset again, if solenoid is enabled in the future.\n    haptic_config.dwell = 0;\n    haptic_config.buzz  = 0;\n#endif\n    eeconfig_update_haptic(&haptic_config);\n    dprintf(\"haptic_config.feedback = %u\\n\", haptic_config.feedback);\n    dprintf(\"haptic_config.mode = %u\\n\", haptic_config.mode);\n}\n\nvoid haptic_set_feedback(uint8_t feedback) {\n    haptic_config.feedback = feedback;\n    eeconfig_update_haptic(&haptic_config);\n    dprintf(\"haptic_config.feedback = %u\\n\", haptic_config.feedback);\n}\n\nvoid haptic_set_mode(uint8_t mode) {\n    haptic_config.mode = mode;\n    eeconfig_update_haptic(&haptic_config);\n    dprintf(\"haptic_config.mode = %u\\n\", haptic_config.mode);\n}\n\nvoid haptic_set_amplitude(uint8_t amp) {\n    haptic_config.amplitude = amp;\n    eeconfig_update_haptic(&haptic_config);\n    dprintf(\"haptic_config.amplitude = %u\\n\", haptic_config.amplitude);\n#ifdef HAPTIC_DRV2605L\n    drv2605l_amplitude(amp);\n#endif\n}\n\nvoid haptic_set_buzz(uint8_t buzz) {\n    haptic_config.buzz = buzz;\n    eeconfig_update_haptic(&haptic_config);\n    dprintf(\"haptic_config.buzz = %u\\n\", haptic_config.buzz);\n}\n\nvoid haptic_set_dwell(uint8_t dwell) {\n    haptic_config.dwell = dwell;\n    eeconfig_update_haptic(&haptic_config);\n    dprintf(\"haptic_config.dwell = %u\\n\", haptic_config.dwell);\n}\n\nuint8_t haptic_get_enable(void) {\n    return haptic_config.enable;\n}\n\nuint8_t haptic_get_mode(void) {\n    if (!haptic_config.enable) {\n        return false;\n    }\n    return haptic_config.mode;\n}\n\nuint8_t haptic_get_feedback(void) {\n    if (!haptic_config.enable) {\n        return false;\n    }\n    return haptic_config.feedback;\n}\n\nuint8_t haptic_get_dwell(void) {\n    if (!haptic_config.enable) {\n        return false;\n    }\n    return haptic_config.dwell;\n}\n\nvoid haptic_enable_continuous(void) {\n    haptic_config.cont = 1;\n    dprintf(\"haptic_config.cont = %u\\n\", haptic_config.cont);\n    eeconfig_update_haptic(&haptic_config);\n#ifdef HAPTIC_DRV2605L\n    drv2605l_rtp_init();\n#endif\n}\n\nvoid haptic_disable_continuous(void) {\n    haptic_config.cont = 0;\n    dprintf(\"haptic_config.cont = %u\\n\", haptic_config.cont);\n    eeconfig_update_haptic(&haptic_config);\n#ifdef HAPTIC_DRV2605L\n    drv2605l_write(DRV2605L_REG_MODE, 0x00);\n#endif\n}\n\nvoid haptic_toggle_continuous(void) {\n    if (haptic_config.cont) {\n        haptic_disable_continuous();\n    } else {\n        haptic_enable_continuous();\n    }\n}\n\nvoid haptic_cont_increase(void) {\n    uint8_t amp = haptic_config.amplitude + 10;\n    if (haptic_config.amplitude >= 120) {\n        amp = 120;\n    }\n    haptic_set_amplitude(amp);\n}\n\nvoid haptic_cont_decrease(void) {\n    uint8_t amp = haptic_config.amplitude - 10;\n    if (haptic_config.amplitude < 20) {\n        amp = 20;\n    }\n    haptic_set_amplitude(amp);\n}\n\nvoid haptic_play(void) {\n#ifdef HAPTIC_DRV2605L\n    uint8_t play_eff = 0;\n    play_eff         = haptic_config.mode;\n    drv2605l_pulse(play_eff);\n#    if defined(SPLIT_KEYBOARD) && defined(SPLIT_HAPTIC_ENABLE)\n    split_haptic_play = haptic_config.mode;\n#    endif\n#endif\n#ifdef HAPTIC_SOLENOID\n    solenoid_fire_handler();\n#    if defined(SPLIT_KEYBOARD) && defined(SPLIT_HAPTIC_ENABLE)\n    split_haptic_play = 1;\n#    endif\n#endif\n}\n\nvoid haptic_shutdown(void) {\n#ifdef HAPTIC_SOLENOID\n    solenoid_shutdown();\n#endif\n}\n\nvoid haptic_notify_usb_device_state_change(void) {\n    update_haptic_enable_gpios();\n#if defined(HAPTIC_ENABLE_PIN)\n    gpio_set_pin_output(HAPTIC_ENABLE_PIN);\n#endif\n#if defined(HAPTIC_ENABLE_STATUS_LED)\n    gpio_set_pin_output(HAPTIC_ENABLE_STATUS_LED);\n#endif\n}\n"
  },
  {
    "path": "quantum/haptic.h",
    "content": "/* Copyright 2019 ishtob\n * Driver for haptic feedback written for QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"compiler_support.h\"\n\n#ifndef HAPTIC_DEFAULT_FEEDBACK\n#    define HAPTIC_DEFAULT_FEEDBACK 0\n#endif\n#ifndef HAPTIC_DEFAULT_MODE\n#    define HAPTIC_DEFAULT_MODE DRV2605L_DEFAULT_MODE\n#endif\n\n/* EEPROM config settings */\ntypedef union haptic_config_t {\n    uint32_t raw;\n    struct {\n        bool    enable : 1;\n        uint8_t mode : 7;\n        bool    buzz : 1;\n        uint8_t dwell : 7;\n        uint8_t amplitude : 8;\n        uint8_t feedback : 2;\n        bool    cont : 1;\n        uint8_t reserved : 5;\n    };\n} haptic_config_t;\n\nSTATIC_ASSERT(sizeof(haptic_config_t) == sizeof(uint32_t), \"Haptic EECONFIG out of spec.\");\n\ntypedef enum HAPTIC_FEEDBACK {\n    KEY_PRESS,\n    KEY_PRESS_RELEASE,\n    KEY_RELEASE,\n    HAPTIC_FEEDBACK_MAX,\n} HAPTIC_FEEDBACK;\n\nvoid    haptic_init(void);\nvoid    haptic_task(void);\nvoid    eeconfig_debug_haptic(void);\nvoid    haptic_enable(void);\nvoid    haptic_disable(void);\nvoid    haptic_toggle(void);\nvoid    haptic_feedback_toggle(void);\nvoid    haptic_mode_increase(void);\nvoid    haptic_mode_decrease(void);\nvoid    haptic_mode(uint8_t mode);\nvoid    haptic_reset(void);\nvoid    haptic_set_feedback(uint8_t feedback);\nvoid    haptic_set_mode(uint8_t mode);\nvoid    haptic_set_dwell(uint8_t dwell);\nvoid    haptic_set_buzz(uint8_t buzz);\nvoid    haptic_buzz_toggle(void);\nuint8_t haptic_get_enable(void);\nuint8_t haptic_get_mode(void);\nuint8_t haptic_get_feedback(void);\nvoid    haptic_dwell_increase(void);\nvoid    haptic_dwell_decrease(void);\nvoid    haptic_toggle_continuous(void);\nvoid    haptic_cont_increase(void);\nvoid    haptic_cont_decrease(void);\n\nvoid haptic_play(void);\nvoid haptic_shutdown(void);\nvoid haptic_notify_usb_device_state_change(void);\n\n#ifdef HAPTIC_ENABLE_PIN_ACTIVE_LOW\n#    ifndef HAPTIC_ENABLE_PIN\n#        error HAPTIC_ENABLE_PIN not defined\n#    endif\n#    define HAPTIC_ENABLE_PIN_WRITE_ACTIVE() gpio_write_pin_low(HAPTIC_ENABLE_PIN)\n#    define HAPTIC_ENABLE_PIN_WRITE_INACTIVE() gpio_write_pin_high(HAPTIC_ENABLE_PIN)\n#else\n#    define HAPTIC_ENABLE_PIN_WRITE_ACTIVE() gpio_write_pin_high(HAPTIC_ENABLE_PIN)\n#    define HAPTIC_ENABLE_PIN_WRITE_INACTIVE() gpio_write_pin_low(HAPTIC_ENABLE_PIN)\n#endif\n\n#ifdef HAPTIC_ENABLE_STATUS_LED_ACTIVE_LOW\n#    ifndef HAPTIC_ENABLE_STATUS_LED\n#        error HAPTIC_ENABLE_STATUS_LED not defined\n#    endif\n#    define HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE() gpio_write_pin_low(HAPTIC_ENABLE_STATUS_LED)\n#    define HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE() gpio_write_pin_high(HAPTIC_ENABLE_STATUS_LED)\n#else\n#    define HAPTIC_ENABLE_STATUS_LED_WRITE_ACTIVE() gpio_write_pin_high(HAPTIC_ENABLE_STATUS_LED)\n#    define HAPTIC_ENABLE_STATUS_LED_WRITE_INACTIVE() gpio_write_pin_low(HAPTIC_ENABLE_STATUS_LED)\n#endif\n\n#ifndef HAPTIC_OFF_IN_LOW_POWER\n#    define HAPTIC_OFF_IN_LOW_POWER 0\n#endif\n"
  },
  {
    "path": "quantum/joystick.c",
    "content": "/* Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"joystick.h\"\n#include \"wait.h\"\n\n#if defined(JOYSTICK_ANALOG)\n#    include \"analog.h\"\n#endif\n\njoystick_t joystick_state = {\n    .buttons = {0},\n    .axes =\n        {\n#if JOYSTICK_AXIS_COUNT > 0\n            0\n#endif\n        },\n#ifdef JOYSTICK_HAS_HAT\n    .hat = -1,\n#endif\n    .dirty = false,\n};\n\n// array defining the reading of analog values for each axis\n__attribute__((weak)) joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = {\n#if JOYSTICK_AXIS_COUNT > 0\n    [0 ...(JOYSTICK_AXIS_COUNT - 1)] = JOYSTICK_AXIS_VIRTUAL\n#endif\n};\n\n__attribute__((weak)) void joystick_axis_init(uint8_t axis) {\n    if (axis >= JOYSTICK_AXIS_COUNT) return;\n\n#if defined(JOYSTICK_ANALOG)\n    gpio_set_pin_input(joystick_axes[axis].input_pin);\n#endif\n}\n\n__attribute__((weak)) uint16_t joystick_axis_sample(uint8_t axis) {\n    if (axis >= JOYSTICK_AXIS_COUNT) return 0;\n\n#if defined(JOYSTICK_ANALOG)\n    return analogReadPin(joystick_axes[axis].input_pin);\n#else\n    // default to resting position\n    return joystick_axes[axis].mid_digit;\n#endif\n}\n\nstatic inline bool is_virtual_axis(uint8_t axis) {\n    return joystick_axes[axis].input_pin == NO_PIN;\n}\n\nvoid joystick_flush(void) {\n    if (!joystick_state.dirty) return;\n\n    // TODO: host.h?\n    void host_joystick_send(joystick_t * joystick);\n    host_joystick_send(&joystick_state);\n    joystick_state.dirty = false;\n}\n\nvoid register_joystick_button(uint8_t button) {\n    if (button >= JOYSTICK_BUTTON_COUNT) return;\n\n    joystick_state.buttons[button / 8] |= 1 << (button % 8);\n    joystick_state.dirty = true;\n    joystick_flush();\n}\n\nvoid unregister_joystick_button(uint8_t button) {\n    if (button >= JOYSTICK_BUTTON_COUNT) return;\n\n    joystick_state.buttons[button / 8] &= ~(1 << (button % 8));\n    joystick_state.dirty = true;\n    joystick_flush();\n}\n\nint16_t joystick_read_axis(uint8_t axis) {\n    if (axis >= JOYSTICK_AXIS_COUNT) return 0;\n\n    int16_t axis_val = joystick_axis_sample(axis);\n\n    // test the converted value against the lower range\n    int32_t ref        = joystick_axes[axis].mid_digit;\n    int32_t range      = joystick_axes[axis].min_digit;\n    int32_t ranged_val = ((axis_val - ref) * -JOYSTICK_MAX_VALUE) / (range - ref);\n\n    if (ranged_val > 0) {\n        // the value is in the higher range\n        range      = joystick_axes[axis].max_digit;\n        ranged_val = ((axis_val - ref) * JOYSTICK_MAX_VALUE) / (range - ref);\n    }\n\n    // clamp the result in the valid range\n    ranged_val = ranged_val < -JOYSTICK_MAX_VALUE ? -JOYSTICK_MAX_VALUE : ranged_val;\n    ranged_val = ranged_val > JOYSTICK_MAX_VALUE ? JOYSTICK_MAX_VALUE : ranged_val;\n\n    return ranged_val;\n}\n\nvoid joystick_init_axes(void) {\n#if JOYSTICK_AXIS_COUNT > 0\n    for (int i = 0; i < JOYSTICK_AXIS_COUNT; ++i) {\n        if (is_virtual_axis(i)) {\n            continue;\n        }\n\n        joystick_axis_init(i);\n    }\n#endif\n}\n\nvoid joystick_read_axes(void) {\n#if JOYSTICK_AXIS_COUNT > 0\n    for (int i = 0; i < JOYSTICK_AXIS_COUNT; ++i) {\n        if (is_virtual_axis(i)) {\n            continue;\n        }\n\n        joystick_set_axis(i, joystick_read_axis(i));\n    }\n\n    joystick_flush();\n#endif\n}\n\nvoid joystick_set_axis(uint8_t axis, int16_t value) {\n    if (axis >= JOYSTICK_AXIS_COUNT) return;\n\n    if (value != joystick_state.axes[axis]) {\n        joystick_state.axes[axis] = value;\n        joystick_state.dirty      = true;\n    }\n}\n\n#ifdef JOYSTICK_HAS_HAT\nvoid joystick_set_hat(int8_t value) {\n    joystick_state.hat   = value;\n    joystick_state.dirty = true;\n}\n#endif\n\nvoid joystick_init(void) {\n    joystick_init_axes();\n}\n\nvoid joystick_task(void) {\n    joystick_read_axes();\n}\n"
  },
  {
    "path": "quantum/joystick.h",
    "content": "/* Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#include \"gpio.h\"\n\n/**\n * \\file\n *\n * \\defgroup joystick HID Joystick\n * \\{\n */\n\n#ifndef JOYSTICK_BUTTON_COUNT\n#    define JOYSTICK_BUTTON_COUNT 8\n#elif JOYSTICK_BUTTON_COUNT > 32\n#    error Joystick feature only supports up to 32 buttons\n#endif\n\n#ifndef JOYSTICK_AXIS_COUNT\n#    define JOYSTICK_AXIS_COUNT 2\n#elif JOYSTICK_AXIS_COUNT > 6\n#    error Joystick feature only supports up to 6 axes\n#endif\n\n#if JOYSTICK_AXIS_COUNT == 0 && JOYSTICK_BUTTON_COUNT == 0\n#    error Joystick feature requires at least one axis or button\n#endif\n\n#ifndef JOYSTICK_AXIS_RESOLUTION\n#    define JOYSTICK_AXIS_RESOLUTION 8\n#elif JOYSTICK_AXIS_RESOLUTION < 8 || JOYSTICK_AXIS_RESOLUTION > 16\n#    error JOYSTICK_AXIS_RESOLUTION must be between 8 and 16\n#endif\n\n#define JOYSTICK_MAX_VALUE ((1L << (JOYSTICK_AXIS_RESOLUTION - 1)) - 1)\n\n#define JOYSTICK_HAT_CENTER -1\n#define JOYSTICK_HAT_NORTH 0\n#define JOYSTICK_HAT_NORTHEAST 1\n#define JOYSTICK_HAT_EAST 2\n#define JOYSTICK_HAT_SOUTHEAST 3\n#define JOYSTICK_HAT_SOUTH 4\n#define JOYSTICK_HAT_SOUTHWEST 5\n#define JOYSTICK_HAT_WEST 6\n#define JOYSTICK_HAT_NORTHWEST 7\n\n// configure on input_pin of the joystick_axes array entry to NO_PIN\n// to prevent it from being read from the ADC. This allows outputting forged axis value.\n#define JOYSTICK_AXIS_VIRTUAL \\\n    { NO_PIN, 0, JOYSTICK_MAX_VALUE / 2, JOYSTICK_MAX_VALUE }\n#define JOYSTICK_AXIS_IN(INPUT_PIN, LOW, REST, HIGH) \\\n    { INPUT_PIN, LOW, REST, HIGH }\n\ntypedef struct {\n    pin_t input_pin;\n\n    // the AVR ADC offers 10 bit precision, with significant bits on the higher part\n    uint16_t min_digit;\n    uint16_t mid_digit;\n    uint16_t max_digit;\n} joystick_config_t;\n\nextern joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT];\n\ntypedef struct {\n    uint8_t buttons[(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1];\n    int16_t axes[JOYSTICK_AXIS_COUNT];\n#ifdef JOYSTICK_HAS_HAT\n    int8_t hat;\n#endif\n    bool dirty;\n} joystick_t;\n\nextern joystick_t joystick_state;\n\n/**\n * \\brief Handle the initialization of the subsystem.\n */\nvoid joystick_init(void);\n\n/**\n * \\brief Handle various subsystem background tasks.\n */\nvoid joystick_task(void);\n\n/**\n * \\brief Send the joystick report to the host, if it has been marked as dirty.\n */\nvoid joystick_flush(void);\n\n/**\n * \\brief Set the state of a button, and flush the report.\n *\n * \\param button The index of the button to press, from 0 to 31.\n */\nvoid register_joystick_button(uint8_t button);\n\n/**\n * \\brief Reset the state of a button, and flush the report.\n *\n * \\param button The index of the button to release, from 0 to 31.\n */\nvoid unregister_joystick_button(uint8_t button);\n\n/**\n * \\brief Sample and process the analog value of the given axis.\n *\n * \\param axis The axis to read.\n *\n * \\return A signed 16-bit integer, where 0 is the resting or mid point.\n */\nint16_t joystick_read_axis(uint8_t axis);\n\n/**\n * \\brief Sample and process the all axis.\n */\nvoid joystick_read_axes(void);\n\n/**\n * \\brief Set the value of the given axis.\n *\n * \\param axis The axis to set the value of.\n * \\param value The value to set.\n */\nvoid joystick_set_axis(uint8_t axis, int16_t value);\n\n/**\n * \\brief Set the position of the hat switch.\n *\n * \\param value The hat switch position to set.\n */\nvoid joystick_set_hat(int8_t value);\n\n/** \\} */\n"
  },
  {
    "path": "quantum/keyboard.c",
    "content": "/*\nCopyright 2011, 2012, 2013 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include <stdint.h>\n#include \"keyboard.h\"\n#include \"keycode_config.h\"\n#include \"matrix.h\"\n#include \"keymap_introspection.h\"\n#include \"host.h\"\n#include \"led.h\"\n#include \"keycode.h\"\n#include \"timer.h\"\n#include \"sync_timer.h\"\n#include \"print.h\"\n#include \"debug.h\"\n#include \"command.h\"\n#include \"util.h\"\n#include \"sendchar.h\"\n#include \"eeconfig.h\"\n#include \"action_layer.h\"\n#ifdef BOOTMAGIC_ENABLE\n#    include \"bootmagic.h\"\n#endif\n#ifdef AUDIO_ENABLE\n#    include \"audio.h\"\n#endif\n#if defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))\n#    include \"process_music.h\"\n#endif\n#ifdef BACKLIGHT_ENABLE\n#    include \"backlight.h\"\n#endif\n#ifdef MOUSEKEY_ENABLE\n#    include \"mousekey.h\"\n#endif\n#ifdef PS2_MOUSE_ENABLE\n#    include \"ps2_mouse.h\"\n#endif\n#ifdef RGBLIGHT_ENABLE\n#    include \"rgblight.h\"\n#endif\n#ifdef LED_MATRIX_ENABLE\n#    include \"led_matrix.h\"\n#endif\n#ifdef RGB_MATRIX_ENABLE\n#    include \"rgb_matrix.h\"\n#endif\n#ifdef ENCODER_ENABLE\n#    include \"encoder.h\"\n#endif\n#ifdef HAPTIC_ENABLE\n#    include \"haptic.h\"\n#endif\n#ifdef AUTO_SHIFT_ENABLE\n#    include \"process_auto_shift.h\"\n#endif\n#ifdef COMBO_ENABLE\n#    include \"process_combo.h\"\n#endif\n#ifdef TAP_DANCE_ENABLE\n#    include \"process_tap_dance.h\"\n#endif\n#ifdef STENO_ENABLE\n#    include \"process_steno.h\"\n#endif\n#ifdef KEY_OVERRIDE_ENABLE\n#    include \"process_key_override.h\"\n#endif\n#ifdef SECURE_ENABLE\n#    include \"secure.h\"\n#endif\n#ifdef POINTING_DEVICE_ENABLE\n#    include \"pointing_device.h\"\n#endif\n#ifdef MIDI_ENABLE\n#    include \"process_midi.h\"\n#endif\n#ifdef JOYSTICK_ENABLE\n#    include \"joystick.h\"\n#endif\n#ifdef HD44780_ENABLE\n#    include \"hd44780.h\"\n#endif\n#ifdef OLED_ENABLE\n#    include \"oled_driver.h\"\n#endif\n#ifdef ST7565_ENABLE\n#    include \"st7565.h\"\n#endif\n#ifdef VIA_ENABLE\n#    include \"via.h\"\n#endif\n#ifdef DIP_SWITCH_ENABLE\n#    include \"dip_switch.h\"\n#endif\n#ifdef EEPROM_DRIVER\n#    include \"eeprom_driver.h\"\n#endif\n#if defined(CRC_ENABLE)\n#    include \"crc.h\"\n#endif\n#ifdef VIRTSER_ENABLE\n#    include \"virtser.h\"\n#endif\n#ifdef SLEEP_LED_ENABLE\n#    include \"sleep_led.h\"\n#endif\n#ifdef SPLIT_KEYBOARD\n#    include \"split_util.h\"\n#endif\n#ifdef BATTERY_DRIVER\n#    include \"battery.h\"\n#endif\n#ifdef BLUETOOTH_ENABLE\n#    include \"bluetooth.h\"\n#endif\n#ifdef CAPS_WORD_ENABLE\n#    include \"caps_word.h\"\n#endif\n#ifdef LEADER_ENABLE\n#    include \"leader.h\"\n#endif\n#ifdef UNICODE_COMMON_ENABLE\n#    include \"unicode.h\"\n#endif\n#ifdef WPM_ENABLE\n#    include \"wpm.h\"\n#endif\n#ifdef OS_DETECTION_ENABLE\n#    include \"os_detection.h\"\n#endif\n#ifdef LAYER_LOCK_ENABLE\n#    include \"layer_lock.h\"\n#endif\n#ifdef CONNECTION_ENABLE\n#    include \"connection.h\"\n#endif\n\nstatic uint32_t last_input_modification_time = 0;\nuint32_t        last_input_activity_time(void) {\n    return last_input_modification_time;\n}\nuint32_t last_input_activity_elapsed(void) {\n    return sync_timer_elapsed32(last_input_modification_time);\n}\n\nstatic uint32_t last_matrix_modification_time = 0;\nuint32_t        last_matrix_activity_time(void) {\n    return last_matrix_modification_time;\n}\nuint32_t last_matrix_activity_elapsed(void) {\n    return sync_timer_elapsed32(last_matrix_modification_time);\n}\nvoid last_matrix_activity_trigger(void) {\n    last_matrix_modification_time = last_input_modification_time = sync_timer_read32();\n}\n\nstatic uint32_t last_encoder_modification_time = 0;\nuint32_t        last_encoder_activity_time(void) {\n    return last_encoder_modification_time;\n}\nuint32_t last_encoder_activity_elapsed(void) {\n    return sync_timer_elapsed32(last_encoder_modification_time);\n}\nvoid last_encoder_activity_trigger(void) {\n    last_encoder_modification_time = last_input_modification_time = sync_timer_read32();\n}\n\nstatic uint32_t last_pointing_device_modification_time = 0;\nuint32_t        last_pointing_device_activity_time(void) {\n    return last_pointing_device_modification_time;\n}\nuint32_t last_pointing_device_activity_elapsed(void) {\n    return sync_timer_elapsed32(last_pointing_device_modification_time);\n}\nvoid last_pointing_device_activity_trigger(void) {\n    last_pointing_device_modification_time = last_input_modification_time = sync_timer_read32();\n}\n\nvoid set_activity_timestamps(uint32_t matrix_timestamp, uint32_t encoder_timestamp, uint32_t pointing_device_timestamp) {\n    last_matrix_modification_time          = matrix_timestamp;\n    last_encoder_modification_time         = encoder_timestamp;\n    last_pointing_device_modification_time = pointing_device_timestamp;\n    last_input_modification_time           = MAX(matrix_timestamp, MAX(encoder_timestamp, pointing_device_timestamp));\n}\n\n// Only enable this if console is enabled to print to\n#if defined(DEBUG_MATRIX_SCAN_RATE)\nstatic uint32_t matrix_timer           = 0;\nstatic uint32_t matrix_scan_count      = 0;\nstatic uint32_t last_matrix_scan_count = 0;\n\nvoid matrix_scan_perf_task(void) {\n    matrix_scan_count++;\n\n    uint32_t timer_now = timer_read32();\n    if (TIMER_DIFF_32(timer_now, matrix_timer) >= 1000) {\n#    if defined(CONSOLE_ENABLE)\n        dprintf(\"matrix scan frequency: %lu\\n\", matrix_scan_count);\n#    endif\n        last_matrix_scan_count = matrix_scan_count;\n        matrix_timer           = timer_now;\n        matrix_scan_count      = 0;\n    }\n}\n\nuint32_t get_matrix_scan_rate(void) {\n    return last_matrix_scan_count;\n}\n#else\n#    define matrix_scan_perf_task()\n#endif\n\n#ifdef MATRIX_HAS_GHOST\nstatic matrix_row_t get_real_keys(uint8_t row, matrix_row_t rowdata) {\n    matrix_row_t out = 0;\n    for (uint8_t col = 0; col < MATRIX_COLS; col++) {\n        // read each key in the row data and check if the keymap defines it as a real key\n        if (keycode_at_keymap_location(0, row, col) && (rowdata & (((matrix_row_t)1) << col))) {\n            // this creates new row data, if a key is defined in the keymap, it will be set here\n            out |= ((matrix_row_t)1) << col;\n        }\n    }\n    return out;\n}\n\nstatic inline bool popcount_more_than_one(matrix_row_t rowdata) {\n    rowdata &= rowdata - 1; // if there are less than two bits (keys) set, rowdata will become zero\n    return rowdata;\n}\n\nstatic inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) {\n    /* No ghost exists when less than 2 keys are down on the row.\n    If there are \"active\" blanks in the matrix, the key can't be pressed by the user,\n    there is no doubt as to which keys are really being pressed.\n    The ghosts will be ignored, they are KC_NO.   */\n    rowdata = get_real_keys(row, rowdata);\n    if ((popcount_more_than_one(rowdata)) == 0) {\n        return false;\n    }\n    /* Ghost occurs when the row shares a column line with other row,\n    and two columns are read on each row. Blanks in the matrix don't matter,\n    so they are filtered out.\n    If there are two or more real keys pressed and they match columns with\n    at least two of another row's real keys, the row will be ignored. Keep in mind,\n    we are checking one row at a time, not all of them at once.\n    */\n    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {\n        if (i != row && popcount_more_than_one(get_real_keys(i, matrix_get_row(i)) & rowdata)) {\n            return true;\n        }\n    }\n    return false;\n}\n\n#else\n\nstatic inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) {\n    return false;\n}\n\n#endif\n\n/** \\brief matrix_setup\n *\n * FIXME: needs doc\n */\n__attribute__((weak)) void matrix_setup(void) {}\n\n/** \\brief keyboard_pre_init_user\n *\n * FIXME: needs doc\n */\n__attribute__((weak)) void keyboard_pre_init_user(void) {}\n\n/** \\brief keyboard_pre_init_kb\n *\n * FIXME: needs doc\n */\n__attribute__((weak)) void keyboard_pre_init_kb(void) {\n    keyboard_pre_init_user();\n}\n\n/** \\brief keyboard_pre_init_modules\n *\n * FIXME: needs doc\n */\n__attribute__((weak)) void keyboard_pre_init_modules(void) {}\n\n/** \\brief keyboard_pre_init_quantum\n *\n * FIXME: needs doc\n */\nvoid keyboard_pre_init_quantum(void) {\n    keyboard_pre_init_modules();\n    keyboard_pre_init_kb();\n}\n\n/** \\brief keyboard_post_init_user\n *\n * FIXME: needs doc\n */\n\n__attribute__((weak)) void keyboard_post_init_user(void) {}\n\n/** \\brief keyboard_post_init_kb\n *\n * FIXME: needs doc\n */\n\n__attribute__((weak)) void keyboard_post_init_kb(void) {\n    keyboard_post_init_user();\n}\n\n/** \\brief keyboard_post_init_modules\n *\n * FIXME: needs doc\n */\n\n__attribute__((weak)) void keyboard_post_init_modules(void) {}\n\n/** \\brief keyboard_post_init_quantum\n *\n * FIXME: needs doc\n */\n\nvoid keyboard_post_init_quantum(void) {\n    keyboard_post_init_modules();\n    keyboard_post_init_kb();\n}\n\n/** \\brief matrix_can_read\n *\n * Allows overriding when matrix scanning operations should be executed.\n */\n__attribute__((weak)) bool matrix_can_read(void) {\n    return true;\n}\n\n/** \\brief keyboard_setup\n *\n * FIXME: needs doc\n */\nvoid keyboard_setup(void) {\n    print_set_sendchar(sendchar);\n#ifdef EEPROM_DRIVER\n    eeprom_driver_init();\n#endif\n    matrix_setup();\n    keyboard_pre_init_quantum();\n}\n\n#ifndef SPLIT_KEYBOARD\n\n/** \\brief is_keyboard_master\n *\n * FIXME: needs doc\n */\n__attribute__((weak)) bool is_keyboard_master(void) {\n    return true;\n}\n\n/** \\brief is_keyboard_left\n *\n * FIXME: needs doc\n */\n__attribute__((weak)) bool is_keyboard_left(void) {\n    return true;\n}\n\n#endif\n\n/** \\brief should_process_keypress\n *\n * Override this function if you have a condition where keypresses processing should change:\n *   - splits where the slave side needs to process for rgb/oled functionality\n */\n__attribute__((weak)) bool should_process_keypress(void) {\n    return is_keyboard_master();\n}\n\n/** \\brief housekeeping_task_modules\n *\n * Codegen will override this if community modules are enabled.\n * This is specific to keyboard-level functionality.\n */\n__attribute__((weak)) void housekeeping_task_modules(void) {}\n\n/** \\brief housekeeping_task_kb\n *\n * Override this function if you have a need to execute code for every keyboard main loop iteration.\n * This is specific to keyboard-level functionality.\n */\n__attribute__((weak)) void housekeeping_task_kb(void) {}\n\n/** \\brief housekeeping_task_user\n *\n * Override this function if you have a need to execute code for every keyboard main loop iteration.\n * This is specific to user/keymap-level functionality.\n */\n__attribute__((weak)) void housekeeping_task_user(void) {}\n\n/** \\brief housekeeping_task\n *\n * Invokes hooks for executing code after QMK is done after each loop iteration.\n */\nvoid housekeeping_task(void) {\n    housekeeping_task_modules();\n    housekeeping_task_kb();\n    housekeeping_task_user();\n}\n\n/** \\brief quantum_init\n *\n * Init global state\n */\nvoid quantum_init(void) {\n    /* check signature */\n    if (!eeconfig_is_enabled()) {\n        eeconfig_init();\n    }\n\n    /* init globals */\n    eeconfig_read_debug(&debug_config);\n    eeconfig_read_keymap(&keymap_config);\n\n#ifdef BOOTMAGIC_ENABLE\n    bootmagic();\n#endif\n\n    /* read here just incase bootmagic process changed its value */\n    layer_state_t default_layer = (layer_state_t)eeconfig_read_default_layer();\n    default_layer_set(default_layer);\n\n    /* Also initialize layer state to trigger callback functions for layer_state */\n    layer_state_set_kb((layer_state_t)layer_state);\n}\n\n/** \\brief keyboard_init\n *\n * FIXME: needs doc\n */\nvoid keyboard_init(void) {\n    timer_init();\n    sync_timer_init();\n#ifdef VIA_ENABLE\n    via_init();\n#endif\n#ifdef SPLIT_KEYBOARD\n    split_pre_init();\n#endif\n#ifdef ENCODER_ENABLE\n    encoder_init();\n#endif\n    matrix_init();\n    quantum_init();\n#ifdef CONNECTION_ENABLE\n    connection_init();\n#endif\n    led_init_ports();\n#ifdef BACKLIGHT_ENABLE\n    backlight_init_ports();\n#endif\n#ifdef AUDIO_ENABLE\n    audio_init();\n#endif\n#ifdef LED_MATRIX_ENABLE\n    led_matrix_init();\n#endif\n#ifdef RGB_MATRIX_ENABLE\n    rgb_matrix_init();\n#endif\n#if defined(UNICODE_COMMON_ENABLE)\n    unicode_input_mode_init();\n#endif\n#if defined(CRC_ENABLE)\n    crc_init();\n#endif\n#ifdef OLED_ENABLE\n    oled_init(OLED_ROTATION_0);\n#endif\n#ifdef ST7565_ENABLE\n    st7565_init(DISPLAY_ROTATION_0);\n#endif\n#ifdef PS2_MOUSE_ENABLE\n    ps2_mouse_init();\n#endif\n#ifdef BACKLIGHT_ENABLE\n    backlight_init();\n#endif\n#ifdef RGBLIGHT_ENABLE\n    rgblight_init();\n#endif\n#ifdef STENO_ENABLE_ALL\n    steno_init();\n#endif\n#if defined(NKRO_ENABLE) && defined(FORCE_NKRO)\n#    pragma message \"FORCE_NKRO option is now deprecated - Please migrate to NKRO_DEFAULT_ON instead.\"\n    keymap_config.nkro = 1;\n    eeconfig_update_keymap(&keymap_config);\n#endif\n#ifdef DIP_SWITCH_ENABLE\n    dip_switch_init();\n#endif\n#ifdef JOYSTICK_ENABLE\n    joystick_init();\n#endif\n#ifdef SLEEP_LED_ENABLE\n    sleep_led_init();\n#endif\n#ifdef VIRTSER_ENABLE\n    virtser_init();\n#endif\n#ifdef SPLIT_KEYBOARD\n    split_post_init();\n#endif\n#ifdef POINTING_DEVICE_ENABLE\n    // init after split init\n    pointing_device_init();\n#endif\n#ifdef BATTERY_DRIVER\n    battery_init();\n#endif\n#ifdef BLUETOOTH_ENABLE\n    bluetooth_init();\n#endif\n#ifdef HAPTIC_ENABLE\n    haptic_init();\n#endif\n\n#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE)\n    debug_enable = true;\n#endif\n\n    keyboard_post_init_quantum(); /* Always keep this last */\n}\n\n/** \\brief key_event_task\n *\n * This function is responsible for calling into other systems when they need to respond to electrical switch press events.\n * This is differnet than keycode events as no layer processing, or filtering occurs.\n */\nvoid switch_events(uint8_t row, uint8_t col, bool pressed) {\n#if defined(LED_MATRIX_ENABLE)\n    led_matrix_handle_key_event(row, col, pressed);\n#endif\n#if defined(RGB_MATRIX_ENABLE)\n    rgb_matrix_handle_key_event(row, col, pressed);\n#endif\n}\n\n/**\n * @brief Generates a tick event at a maximum rate of 1KHz that drives the\n * internal QMK state machine.\n */\nstatic inline void generate_tick_event(void) {\n    static uint16_t last_tick = 0;\n    const uint16_t  now       = timer_read();\n    if (TIMER_DIFF_16(now, last_tick) != 0) {\n        action_exec(MAKE_TICK_EVENT);\n        last_tick = now;\n    }\n}\n\n/**\n * @brief This task scans the keyboards matrix and processes any key presses\n * that occur.\n *\n * @return true Matrix did change\n * @return false Matrix didn't change\n */\nstatic bool matrix_task(void) {\n    if (!matrix_can_read()) {\n        generate_tick_event();\n        return false;\n    }\n\n    static matrix_row_t matrix_previous[MATRIX_ROWS];\n\n    matrix_scan();\n    bool matrix_changed = false;\n    for (uint8_t row = 0; row < MATRIX_ROWS && !matrix_changed; row++) {\n        matrix_changed |= matrix_previous[row] ^ matrix_get_row(row);\n    }\n\n    matrix_scan_perf_task();\n\n    // Short-circuit the complete matrix processing if it is not necessary\n    if (!matrix_changed) {\n        generate_tick_event();\n        return matrix_changed;\n    }\n\n    if (debug_config.matrix) {\n        matrix_print();\n    }\n\n    const bool process_keypress = should_process_keypress();\n\n    for (uint8_t row = 0; row < MATRIX_ROWS; row++) {\n        const matrix_row_t current_row = matrix_get_row(row);\n        const matrix_row_t row_changes = current_row ^ matrix_previous[row];\n\n        if (!row_changes || has_ghost_in_row(row, current_row)) {\n            continue;\n        }\n\n        matrix_row_t col_mask = 1;\n        for (uint8_t col = 0; col < MATRIX_COLS; col++, col_mask <<= 1) {\n            if (row_changes & col_mask) {\n                const bool key_pressed = current_row & col_mask;\n\n                if (process_keypress) {\n                    action_exec(MAKE_KEYEVENT(row, col, key_pressed));\n                }\n\n                switch_events(row, col, key_pressed);\n            }\n        }\n\n        matrix_previous[row] = current_row;\n    }\n\n    return matrix_changed;\n}\n\n/** \\brief Tasks previously located in matrix_scan_quantum\n *\n * TODO: rationalise against keyboard_task and current split role\n */\nvoid quantum_task(void) {\n#ifdef SPLIT_KEYBOARD\n    // some tasks should only run on master\n    if (!is_keyboard_master()) return;\n#endif\n\n#if defined(AUDIO_ENABLE) && defined(AUDIO_INIT_DELAY)\n    // There are some tasks that need to be run a little bit\n    // after keyboard startup, or else they will not work correctly\n    // because of interaction with the USB device state, which\n    // may still be in flux...\n    //\n    // At the moment the only feature that needs this is the\n    // startup song.\n    static bool     delayed_tasks_run  = false;\n    static uint16_t delayed_task_timer = 0;\n    if (!delayed_tasks_run) {\n        if (!delayed_task_timer) {\n            delayed_task_timer = timer_read();\n        } else if (timer_elapsed(delayed_task_timer) > 300) {\n            audio_startup();\n            delayed_tasks_run = true;\n        }\n    }\n#endif\n\n#if defined(AUDIO_ENABLE) && !defined(NO_MUSIC_MODE)\n    music_task();\n#endif\n\n#ifdef KEY_OVERRIDE_ENABLE\n    key_override_task();\n#endif\n\n#ifdef SEQUENCER_ENABLE\n    sequencer_task();\n#endif\n\n#ifdef TAP_DANCE_ENABLE\n    tap_dance_task();\n#endif\n\n#ifdef COMBO_ENABLE\n    combo_task();\n#endif\n\n#ifdef LEADER_ENABLE\n    leader_task();\n#endif\n\n#ifdef WPM_ENABLE\n    decay_wpm();\n#endif\n\n#ifdef DIP_SWITCH_ENABLE\n    dip_switch_task();\n#endif\n\n#ifdef AUTO_SHIFT_ENABLE\n    autoshift_matrix_scan();\n#endif\n\n#ifdef CAPS_WORD_ENABLE\n    caps_word_task();\n#endif\n\n#ifdef SECURE_ENABLE\n    secure_task();\n#endif\n\n#ifdef LAYER_LOCK_ENABLE\n    layer_lock_task();\n#endif\n}\n\n/** \\brief Main task that is repeatedly called as fast as possible. */\nvoid keyboard_task(void) {\n    __attribute__((unused)) bool activity_has_occurred = false;\n    if (matrix_task()) {\n        last_matrix_activity_trigger();\n        activity_has_occurred = true;\n    }\n\n    quantum_task();\n\n#if defined(SPLIT_WATCHDOG_ENABLE)\n    split_watchdog_task();\n#endif\n\n#if defined(RGBLIGHT_ENABLE)\n    rgblight_task();\n#endif\n\n#ifdef LED_MATRIX_ENABLE\n    led_matrix_task();\n#endif\n#ifdef RGB_MATRIX_ENABLE\n    rgb_matrix_task();\n#endif\n\n#if defined(BACKLIGHT_ENABLE)\n#    if defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS)\n    backlight_task();\n#    endif\n#endif\n\n#ifdef ENCODER_ENABLE\n    if (encoder_task()) {\n        last_encoder_activity_trigger();\n        activity_has_occurred = true;\n    }\n#endif\n\n#ifdef POINTING_DEVICE_ENABLE\n    if (pointing_device_task()) {\n        last_pointing_device_activity_trigger();\n        activity_has_occurred = true;\n    }\n#endif\n\n#ifdef OLED_ENABLE\n    oled_task();\n#    if OLED_TIMEOUT > 0\n    // Wake up oled if user is using those fabulous keys or spinning those encoders!\n    if (activity_has_occurred) oled_on();\n#    endif\n#endif\n\n#ifdef ST7565_ENABLE\n    st7565_task();\n#    if ST7565_TIMEOUT > 0\n    // Wake up display if user is using those fabulous keys or spinning those encoders!\n    if (activity_has_occurred) st7565_on();\n#    endif\n#endif\n\n#ifdef MOUSEKEY_ENABLE\n    // mousekey repeat & acceleration\n    mousekey_task();\n#endif\n\n#ifdef PS2_MOUSE_ENABLE\n    ps2_mouse_task();\n#endif\n\n#ifdef MIDI_ENABLE\n    midi_task();\n#endif\n\n#ifdef JOYSTICK_ENABLE\n    joystick_task();\n#endif\n\n#ifdef BATTERY_DRIVER\n    battery_task();\n#endif\n\n#ifdef BLUETOOTH_ENABLE\n    bluetooth_task();\n#endif\n\n#ifdef HAPTIC_ENABLE\n    haptic_task();\n#endif\n\n    led_task();\n\n#ifdef OS_DETECTION_ENABLE\n    os_detection_task();\n#endif\n}\n"
  },
  {
    "path": "quantum/keyboard.h",
    "content": "/*\nCopyright 2011,2012,2013 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#include \"timer.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* key matrix position */\ntypedef struct {\n    uint8_t col;\n    uint8_t row;\n} keypos_t;\n\ntypedef enum keyevent_type_t { TICK_EVENT = 0, KEY_EVENT = 1, ENCODER_CW_EVENT = 2, ENCODER_CCW_EVENT = 3, COMBO_EVENT = 4, DIP_SWITCH_ON_EVENT = 5, DIP_SWITCH_OFF_EVENT = 6 } keyevent_type_t;\n\n/* key event */\ntypedef struct {\n    keypos_t        key;\n    uint16_t        time;\n    keyevent_type_t type;\n    bool            pressed;\n} keyevent_t;\n\n/* equivalent test of keypos_t */\n#define KEYEQ(keya, keyb) ((keya).row == (keyb).row && (keya).col == (keyb).col)\n\n/* special keypos_t entries */\n#define KEYLOC_ENCODER_CW 253\n#define KEYLOC_ENCODER_CCW 252\n#define KEYLOC_DIP_SWITCH_ON 251\n#define KEYLOC_DIP_SWITCH_OFF 250\n\nstatic inline bool IS_NOEVENT(const keyevent_t event) {\n    return event.type == TICK_EVENT;\n}\nstatic inline bool IS_EVENT(const keyevent_t event) {\n    return event.type != TICK_EVENT;\n}\nstatic inline bool IS_KEYEVENT(const keyevent_t event) {\n    return event.type == KEY_EVENT;\n}\nstatic inline bool IS_COMBOEVENT(const keyevent_t event) {\n    return event.type == COMBO_EVENT;\n}\nstatic inline bool IS_ENCODEREVENT(const keyevent_t event) {\n    return event.type == ENCODER_CW_EVENT || event.type == ENCODER_CCW_EVENT;\n}\nstatic inline bool IS_DIPSWITCHEVENT(const keyevent_t event) {\n    return event.type == DIP_SWITCH_ON_EVENT || event.type == DIP_SWITCH_OFF_EVENT;\n}\n\n/* Common keypos_t object factory */\n#define MAKE_KEYPOS(row_num, col_num) ((keypos_t){.row = (row_num), .col = (col_num)})\n\n/* Common keyevent_t object factory */\n#define MAKE_EVENT(row_num, col_num, press, event_type) ((keyevent_t){.key = MAKE_KEYPOS((row_num), (col_num)), .pressed = (press), .time = timer_read(), .type = (event_type)})\n\n/**\n * @brief Constructs a key event for a pressed or released key.\n */\n#define MAKE_KEYEVENT(row_num, col_num, press) MAKE_EVENT((row_num), (col_num), (press), KEY_EVENT)\n\n/**\n * @brief Constructs a combo event.\n */\n#define MAKE_COMBOEVENT(press) MAKE_EVENT(0, 0, (press), COMBO_EVENT)\n\n/**\n * @brief Constructs a internal tick event that is used to drive the internal QMK state machine.\n */\n#define MAKE_TICK_EVENT MAKE_EVENT(0, 0, false, TICK_EVENT)\n\n#ifdef ENCODER_MAP_ENABLE\n/* Encoder events */\n#    define MAKE_ENCODER_CW_EVENT(enc_id, press) MAKE_EVENT(KEYLOC_ENCODER_CW, (enc_id), (press), ENCODER_CW_EVENT)\n#    define MAKE_ENCODER_CCW_EVENT(enc_id, press) MAKE_EVENT(KEYLOC_ENCODER_CCW, (enc_id), (press), ENCODER_CCW_EVENT)\n#endif // ENCODER_MAP_ENABLE\n\n#ifdef DIP_SWITCH_MAP_ENABLE\n/* Dip Switch events */\n#    define MAKE_DIPSWITCH_ON_EVENT(switch_id, press) MAKE_EVENT(KEYLOC_DIP_SWITCH_ON, (switch_id), (press), DIP_SWITCH_ON_EVENT)\n#    define MAKE_DIPSWITCH_OFF_EVENT(switch_id, press) MAKE_EVENT(KEYLOC_DIP_SWITCH_OFF, (switch_id), (press), DIP_SWITCH_OFF_EVENT)\n#endif // DIP_SWITCH_MAP_ENABLE\n\n/* it runs once at early stage of startup before keyboard_init. */\nvoid keyboard_setup(void);\n/* it runs once after initializing host side protocol, debug and MCU peripherals. */\nvoid keyboard_init(void);\n/* it runs repeatedly in main loop */\nvoid keyboard_task(void);\n/* it runs whenever code has to behave differently on a slave */\nbool is_keyboard_master(void);\n/* it runs whenever code has to behave differently on left vs right split */\nbool is_keyboard_left(void);\n\nvoid keyboard_pre_init_kb(void);\nvoid keyboard_pre_init_user(void);\nvoid keyboard_post_init_kb(void);\nvoid keyboard_post_init_user(void);\n\nvoid housekeeping_task(void);      // To be executed by the main loop in each backend TMK protocol\nvoid housekeeping_task_kb(void);   // To be overridden by keyboard-level code\nvoid housekeeping_task_user(void); // To be overridden by user/keymap-level code\n\nuint32_t last_input_activity_time(void);    // Timestamp of the last matrix or encoder or pointing device activity\nuint32_t last_input_activity_elapsed(void); // Number of milliseconds since the last matrix or encoder or pointing device activity\n\nuint32_t last_matrix_activity_time(void);    // Timestamp of the last matrix activity\nuint32_t last_matrix_activity_elapsed(void); // Number of milliseconds since the last matrix activity\n\nuint32_t last_encoder_activity_time(void);    // Timestamp of the last encoder activity\nuint32_t last_encoder_activity_elapsed(void); // Number of milliseconds since the last encoder activity\n\nuint32_t last_pointing_device_activity_time(void);    // Timestamp of the last pointing device activity\nuint32_t last_pointing_device_activity_elapsed(void); // Number of milliseconds since the last  pointing device activity\n\nvoid set_activity_timestamps(uint32_t matrix_timestamp, uint32_t encoder_timestamp, uint32_t pointing_device_timestamp); // Set the timestamps of the last matrix and encoder activity\n\nuint32_t get_matrix_scan_rate(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/keycode.h",
    "content": "/*\nCopyright 2011,2012 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n/*\n * Keycodes based on HID Keyboard/Keypad Usage Page (0x07) plus media keys from Generic Desktop Page (0x01) and Consumer Page (0x0C)\n *\n * See https://web.archive.org/web/20060218214400/http://www.usb.org/developers/devclass_docs/Hut1_12.pdf\n * or http://www.usb.org/developers/hidpage/Hut1_12v2.pdf (older)\n */\n\n#pragma once\n\n/* FIXME: Add doxygen comments here */\n\n#define IS_ANY(code) (KC_A <= (code) && (code) <= 0xFF)\n\n#define IS_MOUSEKEY(code) IS_MOUSE_KEYCODE(code)\n#define IS_MOUSEKEY_MOVE(code) (QK_MOUSE_CURSOR_UP <= (code) && (code) <= QK_MOUSE_CURSOR_RIGHT)\n#define IS_MOUSEKEY_BUTTON(code) (QK_MOUSE_BUTTON_1 <= (code) && (code) <= QK_MOUSE_BUTTON_8)\n#define IS_MOUSEKEY_WHEEL(code) (QK_MOUSE_WHEEL_UP <= (code) && (code) <= QK_MOUSE_WHEEL_RIGHT)\n#define IS_MOUSEKEY_ACCEL(code) (QK_MOUSE_ACCELERATION_0 <= (code) && (code) <= QK_MOUSE_ACCELERATION_2)\n\n#define MOD_BIT(code) (1 << ((code)&0x07))\n\n// clang-format off\n\n// TODO: dd keycodes\n#include \"keycodes.h\"\n#include \"modifiers.h\"\n"
  },
  {
    "path": "quantum/keycode_config.c",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keycode_config.h\"\n\nkeymap_config_t keymap_config;\n\n/** \\brief keycode_config\n *\n * This function is used to check a specific keycode against the bootmagic config,\n * and will return the corrected keycode, when appropriate.\n */\n__attribute__((weak)) uint16_t keycode_config(uint16_t keycode) {\n    switch (keycode) {\n        case KC_CAPS_LOCK:\n        case KC_LOCKING_CAPS_LOCK:\n            if (keymap_config.swap_control_capslock || keymap_config.capslock_to_control) {\n                return KC_LEFT_CTRL;\n            } else if (keymap_config.swap_escape_capslock) {\n                return KC_ESCAPE;\n            }\n            return keycode;\n        case KC_LEFT_CTRL:\n            if (keymap_config.swap_control_capslock) {\n                return KC_CAPS_LOCK;\n            }\n            if (keymap_config.swap_lctl_lgui) {\n                if (keymap_config.no_gui) {\n                    return KC_NO;\n                }\n                return KC_LEFT_GUI;\n            }\n            return KC_LEFT_CTRL;\n        case KC_LEFT_ALT:\n            if (keymap_config.swap_lalt_lgui) {\n                if (keymap_config.no_gui) {\n                    return KC_NO;\n                }\n                return KC_LEFT_GUI;\n            }\n            return KC_LEFT_ALT;\n        case KC_LEFT_GUI:\n            if (keymap_config.swap_lalt_lgui) {\n                return KC_LEFT_ALT;\n            }\n            if (keymap_config.swap_lctl_lgui) {\n                return KC_LEFT_CTRL;\n            }\n            if (keymap_config.no_gui) {\n                return KC_NO;\n            }\n            return KC_LEFT_GUI;\n        case KC_RIGHT_CTRL:\n            if (keymap_config.swap_rctl_rgui) {\n                if (keymap_config.no_gui) {\n                    return KC_NO;\n                }\n                return KC_RIGHT_GUI;\n            }\n            return KC_RIGHT_CTRL;\n        case KC_RIGHT_ALT:\n            if (keymap_config.swap_ralt_rgui) {\n                if (keymap_config.no_gui) {\n                    return KC_NO;\n                }\n                return KC_RIGHT_GUI;\n            }\n            return KC_RIGHT_ALT;\n        case KC_RIGHT_GUI:\n            if (keymap_config.swap_ralt_rgui) {\n                return KC_RIGHT_ALT;\n            }\n            if (keymap_config.swap_rctl_rgui) {\n                return KC_RIGHT_CTRL;\n            }\n            if (keymap_config.no_gui) {\n                return KC_NO;\n            }\n            return KC_RIGHT_GUI;\n        case KC_GRAVE:\n            if (keymap_config.swap_grave_esc) {\n                return KC_ESCAPE;\n            }\n            return KC_GRAVE;\n        case KC_ESCAPE:\n            if (keymap_config.swap_grave_esc) {\n                return KC_GRAVE;\n            } else if (keymap_config.swap_escape_capslock) {\n                return KC_CAPS_LOCK;\n            }\n            return KC_ESCAPE;\n        case KC_BACKSLASH:\n            if (keymap_config.swap_backslash_backspace) {\n                return KC_BACKSPACE;\n            }\n            return KC_BACKSLASH;\n        case KC_BACKSPACE:\n            if (keymap_config.swap_backslash_backspace) {\n                return KC_BACKSLASH;\n            }\n            return KC_BACKSPACE;\n        default:\n            return keycode;\n    }\n}\n\n/** \\brief mod_config\n *\n *  This function checks the mods passed to it against the bootmagic config,\n *  and will remove or replace mods, based on that.\n */\n\n__attribute__((weak)) uint8_t mod_config(uint8_t mod) {\n    /**\n     * Note: This function is for the 5-bit packed mods, NOT the full 8-bit mods.\n     * More info about the mods can be seen in modifiers.h.\n     */\n    if (keymap_config.swap_lalt_lgui) {\n        /** If both modifiers pressed or neither pressed, do nothing\n         * Otherwise swap the values\n         * Note: The left mods are ANDed with the right-hand values to check\n         * if they were pressed with the right hand bit set\n         */\n        if (((mod & MOD_RALT) == MOD_LALT) ^ ((mod & MOD_RGUI) == MOD_LGUI)) {\n            mod ^= (MOD_LALT | MOD_LGUI);\n        }\n    }\n    if (keymap_config.swap_ralt_rgui) {\n        if (((mod & MOD_RALT) == MOD_RALT) ^ ((mod & MOD_RGUI) == MOD_RGUI)) {\n            /* lefthand values to preserve the right hand bit */\n            mod ^= (MOD_LALT | MOD_LGUI);\n        }\n    }\n    if (keymap_config.swap_lctl_lgui) {\n        /* left mods ANDed with right-hand values to check for right hand bit */\n        if (((mod & MOD_RCTL) == MOD_LCTL) ^ ((mod & MOD_RGUI) == MOD_LGUI)) {\n            mod ^= (MOD_LCTL | MOD_LGUI);\n        }\n    }\n    if (keymap_config.swap_rctl_rgui) {\n        if (((mod & MOD_RCTL) == MOD_RCTL) ^ ((mod & MOD_RGUI) == MOD_RGUI)) {\n            /* lefthand values to preserve the right hand bit */\n            mod ^= (MOD_LCTL | MOD_LGUI);\n        }\n    }\n    if (keymap_config.no_gui) {\n        mod &= ~MOD_LGUI;\n        mod &= ~MOD_RGUI;\n    }\n\n    return mod;\n}\n"
  },
  {
    "path": "quantum/keycode_config.h",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"compiler_support.h\"\n\n#include \"eeconfig.h\"\n#include \"keycode.h\"\n#include \"action_code.h\"\n\nuint16_t keycode_config(uint16_t keycode);\nuint8_t  mod_config(uint8_t mod);\n\n/* NOTE: Not portable. Bit field order depends on implementation */\ntypedef union keymap_config_t {\n    uint16_t raw;\n    struct {\n        bool swap_control_capslock : 1;\n        bool capslock_to_control : 1;\n        bool swap_lalt_lgui : 1;\n        bool swap_ralt_rgui : 1;\n        bool no_gui : 1;\n        bool swap_grave_esc : 1;\n        bool swap_backslash_backspace : 1;\n        bool nkro : 1;\n        bool swap_lctl_lgui : 1;\n        bool swap_rctl_rgui : 1;\n        bool oneshot_enable : 1;\n        bool swap_escape_capslock : 1;\n        bool autocorrect_enable : 1;\n    };\n} keymap_config_t;\n\nSTATIC_ASSERT(sizeof(keymap_config_t) == sizeof(uint16_t), \"Keycode (magic) EECONFIG out of spec.\");\n\nextern keymap_config_t keymap_config;\n"
  },
  {
    "path": "quantum/keycode_string.c",
    "content": "// Copyright 2024-2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"keycode_string.h\"\n\n#include <string.h>\n#include \"bitwise.h\"\n#include \"keycode.h\"\n#include \"progmem.h\"\n#include \"quantum_keycodes.h\"\n#include \"util.h\"\n\ntypedef int_fast8_t index_t;\n\n// clang-format off\n/** Packs a 7-char keycode name, ignoring the third char, as 3 words. */\n#define KEYCODE_NAME7(c0, c1, unused_c2, c3, c4, c5, c6) \\\n    ((uint16_t)c0) | (((uint16_t)c1) << 8),              \\\n    ((uint16_t)c3) | (((uint16_t)c4) << 8),              \\\n    ((uint16_t)c5) | (((uint16_t)c6) << 8)\n\n/**\n * @brief Names of some common keycodes.\n *\n * Each (keycode, name) entry is stored flat in 8 bytes in PROGMEM. Names in\n * this table must be at most 7 chars long and have an underscore '_' for the\n * third char. This underscore is assumed and not actually stored.\n *\n * To save memory, feature-specific key entries are ifdef'd to include them only\n * when their feature is enabled.\n */\nstatic const uint16_t common_names[] PROGMEM = {\n    KC_TRNS, KEYCODE_NAME7('K', 'C', '_', 'T', 'R', 'N', 'S'),\n    KC_ENT , KEYCODE_NAME7('K', 'C', '_', 'E', 'N', 'T',  0 ),\n    KC_ESC , KEYCODE_NAME7('K', 'C', '_', 'E', 'S', 'C',  0 ),\n    KC_BSPC, KEYCODE_NAME7('K', 'C', '_', 'B', 'S', 'P', 'C'),\n    KC_TAB , KEYCODE_NAME7('K', 'C', '_', 'T', 'A', 'B',  0 ),\n    KC_SPC , KEYCODE_NAME7('K', 'C', '_', 'S', 'P', 'C',  0 ),\n    KC_MINS, KEYCODE_NAME7('K', 'C', '_', 'M', 'I', 'N', 'S'),\n    KC_EQL , KEYCODE_NAME7('K', 'C', '_', 'E', 'Q', 'L',  0 ),\n    KC_LBRC, KEYCODE_NAME7('K', 'C', '_', 'L', 'B', 'R', 'C'),\n    KC_RBRC, KEYCODE_NAME7('K', 'C', '_', 'R', 'B', 'R', 'C'),\n    KC_BSLS, KEYCODE_NAME7('K', 'C', '_', 'B', 'S', 'L', 'S'),\n    KC_NUHS, KEYCODE_NAME7('K', 'C', '_', 'N', 'U', 'H', 'S'),\n    KC_SCLN, KEYCODE_NAME7('K', 'C', '_', 'S', 'C', 'L', 'N'),\n    KC_QUOT, KEYCODE_NAME7('K', 'C', '_', 'Q', 'U', 'O', 'T'),\n    KC_GRV , KEYCODE_NAME7('K', 'C', '_', 'G', 'R', 'V',  0 ),\n    KC_COMM, KEYCODE_NAME7('K', 'C', '_', 'C', 'O', 'M', 'M'),\n    KC_DOT , KEYCODE_NAME7('K', 'C', '_', 'D', 'O', 'T',  0 ),\n    KC_SLSH, KEYCODE_NAME7('K', 'C', '_', 'S', 'L', 'S', 'H'),\n    KC_CAPS, KEYCODE_NAME7('K', 'C', '_', 'C', 'A', 'P', 'S'),\n    KC_PSCR, KEYCODE_NAME7('K', 'C', '_', 'P', 'S', 'C', 'R'),\n    KC_PAUS, KEYCODE_NAME7('K', 'C', '_', 'P', 'A', 'U', 'S'),\n    KC_INS , KEYCODE_NAME7('K', 'C', '_', 'I', 'N', 'S',  0 ),\n    KC_HOME, KEYCODE_NAME7('K', 'C', '_', 'H', 'O', 'M', 'E'),\n    KC_PGUP, KEYCODE_NAME7('K', 'C', '_', 'P', 'G', 'U', 'P'),\n    KC_DEL , KEYCODE_NAME7('K', 'C', '_', 'D', 'E', 'L',  0 ),\n    KC_END , KEYCODE_NAME7('K', 'C', '_', 'E', 'N', 'D',  0 ),\n    KC_PGDN, KEYCODE_NAME7('K', 'C', '_', 'P', 'G', 'D', 'N'),\n    KC_RGHT, KEYCODE_NAME7('K', 'C', '_', 'R', 'G', 'H', 'T'),\n    KC_LEFT, KEYCODE_NAME7('K', 'C', '_', 'L', 'E', 'F', 'T'),\n    KC_DOWN, KEYCODE_NAME7('K', 'C', '_', 'D', 'O', 'W', 'N'),\n    KC_UP  , KEYCODE_NAME7('K', 'C', '_', 'U', 'P',  0 ,  0 ),\n    KC_NUBS, KEYCODE_NAME7('K', 'C', '_', 'N', 'U', 'B', 'S'),\n    KC_HYPR, KEYCODE_NAME7('K', 'C', '_', 'H', 'Y', 'P', 'R'),\n    KC_MEH , KEYCODE_NAME7('K', 'C', '_', 'M', 'E', 'H',  0 ),\n#ifdef EXTRAKEY_ENABLE\n    KC_WHOM, KEYCODE_NAME7('K', 'C', '_', 'W', 'H', 'O', 'M'),\n    KC_WBAK, KEYCODE_NAME7('K', 'C', '_', 'W', 'B', 'A', 'K'),\n    KC_WFWD, KEYCODE_NAME7('K', 'C', '_', 'W', 'F', 'W', 'D'),\n    KC_WSTP, KEYCODE_NAME7('K', 'C', '_', 'W', 'S', 'T', 'P'),\n    KC_WREF, KEYCODE_NAME7('K', 'C', '_', 'W', 'R', 'E', 'F'),\n    KC_MNXT, KEYCODE_NAME7('K', 'C', '_', 'M', 'N', 'X', 'T'),\n    KC_MPRV, KEYCODE_NAME7('K', 'C', '_', 'M', 'P', 'R', 'V'),\n    KC_MPLY, KEYCODE_NAME7('K', 'C', '_', 'M', 'P', 'L', 'Y'),\n    KC_MUTE, KEYCODE_NAME7('K', 'C', '_', 'M', 'U', 'T', 'E'),\n    KC_VOLU, KEYCODE_NAME7('K', 'C', '_', 'V', 'O', 'L', 'U'),\n    KC_VOLD, KEYCODE_NAME7('K', 'C', '_', 'V', 'O', 'L', 'D'),\n#endif // EXTRAKEY_ENABLE\n#ifdef MOUSEKEY_ENABLE\n    MS_LEFT, KEYCODE_NAME7('M', 'S', '_', 'L', 'E', 'F', 'T'),\n    MS_RGHT, KEYCODE_NAME7('M', 'S', '_', 'R', 'G', 'H', 'T'),\n    MS_UP  , KEYCODE_NAME7('M', 'S', '_', 'U', 'P',  0 ,  0 ),\n    MS_DOWN, KEYCODE_NAME7('M', 'S', '_', 'D', 'O', 'W', 'N'),\n    MS_WHLL, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'L'),\n    MS_WHLR, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'R'),\n    MS_WHLU, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'U'),\n    MS_WHLD, KEYCODE_NAME7('M', 'S', '_', 'W', 'H', 'L', 'D'),\n#endif // MOUSEKEY_ENABLE\n#ifdef SWAP_HANDS_ENABLE\n    SH_ON  , KEYCODE_NAME7('S', 'H', '_', 'O', 'N',  0 ,  0 ),\n    SH_OFF , KEYCODE_NAME7('S', 'H', '_', 'O', 'F', 'F',  0 ),\n    SH_MON , KEYCODE_NAME7('S', 'H', '_', 'M', 'O', 'N',  0 ),\n    SH_MOFF, KEYCODE_NAME7('S', 'H', '_', 'M', 'O', 'F', 'F'),\n    SH_TOGG, KEYCODE_NAME7('S', 'H', '_', 'T', 'O', 'G', 'G'),\n    SH_TT  , KEYCODE_NAME7('S', 'H', '_', 'T', 'T',  0 ,  0 ),\n#    if !defined(NO_ACTION_ONESHOT)\n    SH_OS  , KEYCODE_NAME7('S', 'H', '_', 'O', 'S',  0 ,  0 ),\n#    endif // !defined(NO_ACTION_ONESHOT)\n#endif // SWAP_HANDS_ENABLE\n#ifdef LEADER_ENABLE\n    QK_LEAD, KEYCODE_NAME7('Q', 'K', '_', 'L', 'E', 'A', 'D'),\n#endif // LEADER_ENABLE\n#ifdef KEY_LOCK_ENABLE\n    QK_LOCK, KEYCODE_NAME7('Q', 'K', '_', 'L', 'O', 'C', 'K'),\n#endif // KEY_LOCK_ENABLE\n#ifdef TRI_LAYER_ENABLE\n    TL_LOWR, KEYCODE_NAME7('T', 'L', '_', 'L', 'O', 'W', 'R'),\n    TL_UPPR, KEYCODE_NAME7('T', 'L', '_', 'U', 'P', 'P', 'R'),\n#endif // TRI_LAYER_ENABLE\n#ifdef GRAVE_ESC_ENABLE\n    QK_GESC, KEYCODE_NAME7('Q', 'K', '_', 'G', 'E', 'S', 'C'),\n#endif // GRAVE_ESC_ENABLE\n#ifdef CAPS_WORD_ENABLE\n    CW_TOGG, KEYCODE_NAME7('C', 'W', '_', 'T', 'O', 'G', 'G'),\n#endif // CAPS_WORD_ENABLE\n#ifdef SECURE_ENABLE\n    SE_LOCK, KEYCODE_NAME7('S', 'E', '_', 'L', 'O', 'C', 'K'),\n    SE_UNLK, KEYCODE_NAME7('S', 'E', '_', 'U', 'N', 'L', 'K'),\n    SE_TOGG, KEYCODE_NAME7('S', 'E', '_', 'T', 'O', 'G', 'G'),\n    SE_REQ , KEYCODE_NAME7('S', 'E', '_', 'R', 'E', 'Q',  0 ),\n#endif // SECURE_ENABLE\n#ifdef LAYER_LOCK_ENABLE\n    QK_LLCK, KEYCODE_NAME7('Q', 'K', '_', 'L', 'L', 'C', 'K'),\n#endif // LAYER_LOCK_ENABLE\n    EE_CLR , KEYCODE_NAME7('E', 'E', '_', 'C', 'L', 'R',  0 ),\n    QK_BOOT, KEYCODE_NAME7('Q', 'K', '_', 'B', 'O', 'O', 'T'),\n    DB_TOGG, KEYCODE_NAME7('D', 'B', '_', 'T', 'O', 'G', 'G'),\n};\n// clang-format on\n\n/** Users can override this to define names of additional keycodes. */\n__attribute__((weak)) const keycode_string_name_t* keycode_string_names_data_user = NULL;\n__attribute__((weak)) uint16_t                     keycode_string_names_size_user = 0;\n/** Keyboard vendors can override this to define names of additional keycodes. */\n__attribute__((weak)) const keycode_string_name_t* keycode_string_names_data_kb = NULL;\n__attribute__((weak)) uint16_t                     keycode_string_names_size_kb = 0;\n/** Names of the 4 mods on each hand. */\nstatic const char mod_names[] PROGMEM = \"CTL\\0SFT\\0ALT\\0GUI\";\n/** Internal buffer for holding a stringified keycode. */\nstatic char buffer[32];\n#define BUFFER_MAX_LEN (sizeof(buffer) - 1)\nstatic index_t buffer_len;\n\n/** Finds the name of a keycode in `common_names` or returns NULL. */\nstatic const char* search_common_names(uint16_t keycode) {\n    static uint8_t buffer[8];\n\n    for (int_fast16_t offset = 0; offset < ARRAY_SIZE(common_names); offset += 4) {\n        if (keycode == pgm_read_word(common_names + offset)) {\n            const uint16_t w0 = pgm_read_word(common_names + offset + 1);\n            const uint16_t w1 = pgm_read_word(common_names + offset + 2);\n            const uint16_t w2 = pgm_read_word(common_names + offset + 3);\n            buffer[0]         = (uint8_t)w0;\n            buffer[1]         = (uint8_t)(w0 >> 8);\n            buffer[2]         = '_';\n            buffer[3]         = (uint8_t)w1;\n            buffer[4]         = (uint8_t)(w1 >> 8);\n            buffer[5]         = (uint8_t)w2;\n            buffer[6]         = (uint8_t)(w2 >> 8);\n            buffer[7]         = 0;\n            return (const char*)buffer;\n        }\n    }\n\n    return NULL;\n}\n\n/**\n * @brief Finds the name of a keycode in table or returns NULL.\n *\n * @param data   Pointer to table to be searched.\n * @param size   Numer of entries in the table.\n * @return Name string for the keycode, or NULL if not found.\n */\nstatic const char* search_table(const keycode_string_name_t* data, uint16_t size, uint16_t keycode) {\n    if (data != NULL) {\n        for (uint16_t i = 0; i < size; ++i) {\n            if (data[i].keycode == keycode) {\n                return data[i].name;\n            }\n        }\n    }\n    return NULL;\n}\n\n/** Formats `number` in `base`, either 10 or 16. */\nstatic char* number_string(uint16_t number, int8_t base) {\n    static char result[7];\n    result[sizeof(result) - 1] = '\\0';\n    index_t i                  = sizeof(result) - 1;\n    do {\n        const uint8_t digit = number % base;\n        number /= base;\n        result[--i] = (digit < 10) ? (char)(digit + UINT8_C('0')) : (char)(digit + (UINT8_C('A') - 10));\n    } while (number > 0 && i > 0);\n\n    if (base == 16 && i >= 2) {\n        result[--i] = 'x';\n        result[--i] = '0';\n    }\n    return result + i;\n}\n\n/** Appends `str` to `buffer`, truncating if the result would overflow. */\nstatic void append(const char* str) {\n    char*   dest = buffer + buffer_len;\n    index_t i;\n    for (i = 0; buffer_len + i < BUFFER_MAX_LEN && str[i]; ++i) {\n        dest[i] = str[i];\n    }\n    buffer_len += i;\n    buffer[buffer_len] = '\\0';\n}\n\n/** Same as append(), but where `str` is a PROGMEM string. */\nstatic void append_P(const char* str) {\n    char*   dest = buffer + buffer_len;\n    index_t i;\n    for (i = 0; buffer_len + i < BUFFER_MAX_LEN; ++i) {\n        const char c = pgm_read_byte(&str[i]);\n        if (c == '\\0') {\n            break;\n        }\n        dest[i] = c;\n    }\n    buffer_len += i;\n    buffer[buffer_len] = '\\0';\n}\n\n/** Appends a single char to `buffer` if there is space. */\nstatic void append_char(char c) {\n    if (buffer_len < BUFFER_MAX_LEN) {\n        buffer[buffer_len]   = c;\n        buffer[++buffer_len] = '\\0';\n    }\n}\n\n/** Formats `number` in `base`, either 10 or 16, and appends it to `buffer`. */\nstatic void append_number(uint16_t number, int8_t base) {\n    append(number_string(number, base));\n}\n\n/** Stringifies 5-bit mods and appends it to `buffer`. */\nstatic void append_5_bit_mods(uint8_t mods) {\n    const bool    is_rhs = mods > 15;\n    const uint8_t csag   = mods & 15;\n    if (csag != 0 && (csag & (csag - 1)) == 0) { // One mod is set.\n        append_P(PSTR(\"MOD_\"));\n        append_char(is_rhs ? 'R' : 'L');\n        append_P(&mod_names[4 * biton(csag)]);\n    } else { // Fallback: write the mod as a hex value.\n        append_number(mods, 16);\n    }\n}\n\n/**\n * @brief Writes a keycode of the format `name` + \"(\" + `param` + \")\".\n * @note `name` is a PROGMEM string, `param` is not.\n */\nstatic void append_unary_keycode(const char* name, const char* param) {\n    append_P(name);\n    append_char('(');\n    append(param);\n    append_char(')');\n}\n\n/**\n * @brief Writes a keycode of the format `name` + `number`.\n * @note `name` is a PROGMEM string.\n */\nstatic void append_numbered_keycode(const char* name, uint16_t number) {\n    append_P(name);\n    append_number(number, 10);\n}\n\n/** Stringifies `keycode` and appends it to `buffer`. */\nstatic void append_keycode(uint16_t keycode) {\n    // In case there is overlap among tables, search `keycode_string_names_user`\n    // first so that it takes precedence.\n    const char* keycode_name = search_table(keycode_string_names_data_user, keycode_string_names_size_user, keycode);\n    if (keycode_name) {\n        append(keycode_name);\n        return;\n    }\n    keycode_name = search_table(keycode_string_names_data_kb, keycode_string_names_size_kb, keycode);\n    if (keycode_name) {\n        append(keycode_name);\n        return;\n    }\n    keycode_name = search_common_names(keycode);\n    if (keycode_name) {\n        append(keycode_name);\n        return;\n    }\n\n    if (keycode <= 255) { // Basic keycodes.\n        switch (keycode) {\n            // Modifiers KC_LSFT, KC_RCTL, etc.\n            case MODIFIER_KEYCODE_RANGE: {\n                const uint8_t i      = keycode - KC_LCTL;\n                const bool    is_rhs = i > 3;\n                append_P(PSTR(\"KC_\"));\n                append_char(is_rhs ? 'R' : 'L');\n                append_P(&mod_names[4 * (i & 3)]);\n            }\n                return;\n\n            // Letters A-Z.\n            case KC_A ... KC_Z:\n                append_P(PSTR(\"KC_\"));\n                append_char((char)(keycode + (UINT8_C('A') - KC_A)));\n                return;\n\n            // Digits 0-9 (NOTE: Unlike the ASCII order, KC_0 comes *after* KC_9.)\n            case KC_1 ... KC_0:\n                append_numbered_keycode(PSTR(\"KC_\"), (keycode - (KC_1 - 1)) % 10);\n                return;\n\n            // Keypad digits.\n            case KC_KP_1 ... KC_KP_0:\n                append_numbered_keycode(PSTR(\"KC_KP_\"), (keycode - (KC_KP_1 - 1)) % 10);\n                return;\n\n            // Function keys. F1-F12 and F13-F24 are coded in separate ranges.\n            case KC_F1 ... KC_F12:\n                append_numbered_keycode(PSTR(\"KC_F\"), keycode - (KC_F1 - 1));\n                return;\n\n            case KC_F13 ... KC_F24:\n                append_numbered_keycode(PSTR(\"KC_F\"), keycode - (KC_F13 - 13));\n                return;\n        }\n    }\n\n    // clang-format off\n    switch (keycode) {\n        // A modified keycode, like S(KC_1) for Shift + 1 = !. This implementation\n        // only covers modified keycodes where one modifier is applied, e.g. a\n        // Ctrl + Shift + kc or Hyper + kc keycode is not formatted.\n        case QK_MODS ... QK_MODS_MAX: {\n            uint8_t mods = QK_MODS_GET_MODS(keycode);\n            const bool is_rhs = mods > 15;\n            mods &= 15;\n            if (mods != 0 && (mods & (mods - 1)) == 0) { // One mod is set.\n                const char* name = &mod_names[4 * biton(mods)];\n                if (is_rhs) {\n                    append_char('R');\n                    append_P(name);\n                } else {\n                    append_char(pgm_read_byte(&name[0]));\n                }\n                append_char('(');\n                append_keycode(QK_MODS_GET_BASIC_KEYCODE(keycode));\n                append_char(')');\n                return;\n            }\n        } break;\n\n#if !defined(NO_ACTION_ONESHOT)\n        case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: // One-shot mod OSM(mod) key.\n            append_P(PSTR(\"OSM(\"));\n            append_5_bit_mods(QK_ONE_SHOT_MOD_GET_MODS(keycode));\n            append_char(')');\n            return;\n#endif // !defined(NO_ACTION_ONESHOT)\n\n        // Various layer switch keys.\n        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: // Layer-tap LT(layer,kc) key.\n            append_P(PSTR(\"LT(\"));\n            append_number(QK_LAYER_TAP_GET_LAYER(keycode), 10);\n            append_char(',');\n            append_keycode(QK_LAYER_TAP_GET_TAP_KEYCODE(keycode));\n            append_char(')');\n            return;\n\n        case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: // LM(layer,mod) key.\n            append_P(PSTR(\"LM(\"));\n            append_number(QK_LAYER_MOD_GET_LAYER(keycode), 10);\n            append_char(',');\n            append_5_bit_mods(QK_LAYER_MOD_GET_MODS(keycode));\n            append_char(')');\n            return;\n\n        case QK_TO ... QK_TO_MAX: // TO(layer) key.\n            append_unary_keycode(PSTR(\"TO\"), number_string(QK_TO_GET_LAYER(keycode), 10));\n            return;\n\n        case QK_MOMENTARY ... QK_MOMENTARY_MAX: // MO(layer) key.\n            append_unary_keycode(PSTR(\"MO\"), number_string(QK_MOMENTARY_GET_LAYER(keycode), 10));\n            return;\n\n        case QK_DEF_LAYER ... QK_DEF_LAYER_MAX: // DF(layer) key.\n            append_unary_keycode(PSTR(\"DF\"), number_string(QK_DEF_LAYER_GET_LAYER(keycode), 10));\n            return;\n\n        case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX: // TG(layer) key.\n            append_unary_keycode(PSTR(\"TG\"), number_string(QK_TOGGLE_LAYER_GET_LAYER(keycode), 10));\n            return;\n\n#if !defined(NO_ACTION_ONESHOT)\n        case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX: // OSL(layer) key.\n            append_unary_keycode(PSTR(\"OSL\"), number_string(QK_ONE_SHOT_LAYER_GET_LAYER(keycode), 10));\n            return;\n#endif // !defined(NO_ACTION_ONESHOT)\n\n        case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: // TT(layer) key.\n            append_unary_keycode(PSTR(\"TT\"), number_string(QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode), 10));\n            return;\n\n        case QK_PERSISTENT_DEF_LAYER ... QK_PERSISTENT_DEF_LAYER_MAX: // PDF(layer) key.\n            append_unary_keycode(PSTR(\"PDF\"), number_string(QK_PERSISTENT_DEF_LAYER_GET_LAYER(keycode), 10));\n            return;\n\n        // Mod-tap MT(mod,kc) key. This implementation formats the MT keys where\n        // one modifier is applied. For MT keys with multiple modifiers, the mod\n        // arg is written numerically as a hex code.\n        case QK_MOD_TAP ... QK_MOD_TAP_MAX: {\n            uint8_t mods = QK_MOD_TAP_GET_MODS(keycode);\n            const bool is_rhs = mods > 15;\n            const uint8_t csag = mods & 15;\n            if (csag != 0 && (csag & (csag - 1)) == 0) { // One mod is set.\n                append_char(is_rhs ? 'R' : 'L');\n                append_P(&mod_names[4 * biton(csag)]);\n                append_P(PSTR(\"_T(\"));\n            } else if (mods == MOD_HYPR) {\n                append_P(PSTR(\"HYPR_T(\"));\n            } else if (mods == MOD_MEH) {\n                append_P(PSTR(\"MEH_T(\"));\n            } else {\n                append_P(PSTR(\"MT(\"));\n                append_number(mods, 16);\n                append_char(',');\n            }\n            append_keycode(QK_MOD_TAP_GET_TAP_KEYCODE(keycode));\n            append_char(')');\n        }   return;\n\n        case QK_TAP_DANCE ... QK_TAP_DANCE_MAX: // Tap dance TD(i) key.\n            append_unary_keycode(PSTR(\"TD\"), number_string(QK_TAP_DANCE_GET_INDEX(keycode), 10));\n            return;\n\n#ifdef UNICODE_ENABLE\n        case QK_UNICODE ... QK_UNICODE_MAX: // Unicode UC(codepoint) key.\n            append_unary_keycode(PSTR(\"UC\"), number_string(QK_UNICODE_GET_CODE_POINT(keycode), 16));\n            return;\n#elif defined(UNICODEMAP_ENABLE)\n        case QK_UNICODEMAP ... QK_UNICODEMAP_MAX: // Unicode Map UM(i) key.\n            append_unary_keycode(PSTR(\"UM\"), number_string(QK_UNICODEMAP_GET_INDEX(keycode), 10));\n            return;\n\n        case QK_UNICODEMAP_PAIR ... QK_UNICODEMAP_PAIR_MAX: { // UP(i,j) key.\n            const uint8_t i = QK_UNICODEMAP_PAIR_GET_UNSHIFTED_INDEX(keycode);\n            const uint8_t j = QK_UNICODEMAP_PAIR_GET_SHIFTED_INDEX(keycode);\n            append_P(PSTR(\"UP(\"));\n            append_number(i, 10);\n            append_char(',');\n            append_number(j, 10);\n            append_char(')');\n        }   return;\n#endif\n#ifdef MOUSEKEY_ENABLE\n        case MS_BTN1 ... MS_BTN8: // Mouse button keycode.\n            append_numbered_keycode(PSTR(\"MS_BTN\"), keycode - (MS_BTN1 - 1));\n            return;\n#endif // MOUSEKEY_ENABLE\n#ifdef SWAP_HANDS_ENABLE\n        case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: // Swap Hands SH_T(kc) key.\n            if (!IS_SWAP_HANDS_KEYCODE(keycode)) {\n                append_P(PSTR(\"SH_T(\"));\n                append_keycode(QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode));\n                append_char(')');\n                return;\n            }\n            break;\n#endif // SWAP_HANDS_ENABLE\n#ifdef JOYSTICK_ENABLE\n        case JOYSTICK_KEYCODE_RANGE: // Joystick JS_ key.\n            append_numbered_keycode(PSTR(\"JS_\"), keycode - JS_0);\n            return;\n#endif // JOYSTICK_ENABLE\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\n        case PROGRAMMABLE_BUTTON_KEYCODE_RANGE: // Programmable button PB_ key.\n            append_numbered_keycode(PSTR(\"PB_\"), keycode - (PB_1 - 1));\n            return;\n#endif // PROGRAMMABLE_BUTTON_ENABLE\n\n        case MACRO_KEYCODE_RANGE: // Macro range MC_ keycode.\n            append_numbered_keycode(PSTR(\"MC_\"), keycode - MC_0);\n            return;\n\n        case KB_KEYCODE_RANGE: // Keyboard range keycode.\n            append_numbered_keycode(PSTR(\"QK_KB_\"), keycode - QK_KB_0);\n            return;\n\n        case USER_KEYCODE_RANGE: // User range keycode.\n            append_numbered_keycode(PSTR(\"QK_USER_\"), keycode - QK_USER_0);\n            return;\n\n        // It would take a nontrivial amount of string data to cover some\n        // feature-specific keycodes, such as those for MIDI and lighting. As a\n        // fallback while still providing some information, we stringify\n        // remaining keys in known code ranges as \"QK_<feature>+<number>\".\n#ifdef MAGIC_ENABLE\n        case MAGIC_KEYCODE_RANGE:\n            append_numbered_keycode(PSTR(\"QK_MAGIC+\"), keycode - QK_MAGIC);\n            return;\n#endif // MAGIC_ENABLE\n#ifdef MIDI_ENABLE\n        case MIDI_KEYCODE_RANGE:\n            append_numbered_keycode(PSTR(\"QK_MIDI+\"), keycode - QK_MIDI);\n            return;\n#endif // MIDI_ENABLE\n#ifdef SEQUENCER_ENABLE\n        case SEQUENCER_KEYCODE_RANGE:\n            append_numbered_keycode(PSTR(\"QK_SEQUENCER+\"), keycode - QK_SEQUENCER);\n            return;\n#endif // SEQUENCER_ENABLE\n#ifdef AUDIO_ENABLE\n        case AUDIO_KEYCODE_RANGE:\n            append_numbered_keycode(PSTR(\"QK_AUDIO+\"), keycode - QK_AUDIO);\n            return;\n#endif // AUDIO_ENABLE\n#if defined(BACKLIGHT_ENABLE) || defined(LED_MATRIX_ENABLE) || defined(RGBLIGHT_ENABLED) || defined(RGB_MATRIX_ENABLE) // Lighting-related features.\n        case QK_LIGHTING ... QK_LIGHTING_MAX:\n            append_numbered_keycode(PSTR(\"QK_LIGHTING+\"), keycode - QK_LIGHTING);\n            return;\n#endif // defined(BACKLIGHT_ENABLE) || defined(LED_MATRIX_ENABLE) || defined(RGBLIGHT_ENABLED) || defined(RGB_MATRIX_ENABLE)\n#ifdef STENO_ENABLE\n        case STENO_KEYCODE_RANGE:\n            append_numbered_keycode(PSTR(\"QK_STENO+\"), keycode - QK_STENO);\n            return;\n#endif // AUDIO_ENABLE\n#ifdef BLUETOOTH_ENABLE\n        case CONNECTION_KEYCODE_RANGE:\n            append_numbered_keycode(PSTR(\"QK_CONNECTION+\"), keycode - QK_CONNECTION);\n            return;\n#endif // BLUETOOTH_ENABLE\n        case QUANTUM_KEYCODE_RANGE:\n            append_numbered_keycode(PSTR(\"QK_QUANTUM+\"), keycode - QK_QUANTUM);\n            return;\n    }\n    // clang-format on\n\n    append_number(keycode, 16); // Fallback: write keycode as hex value.\n}\n\nconst char* get_keycode_string(uint16_t keycode) {\n    buffer_len = 0;\n    buffer[0]  = '\\0';\n    append_keycode(keycode);\n    return buffer;\n}\n"
  },
  {
    "path": "quantum/keycode_string.h",
    "content": "// Copyright 2024-2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n\n#if KEYCODE_STRING_ENABLE\n\n/**\n * @brief Formats a QMK keycode as a human-readable string.\n *\n * Given a keycode, like `KC_A`, this function returns a formatted string, like\n * \"KC_A\". This is useful for debugging and diagnostics so that keys are more\n * easily identified than they would be by raw numerical codes.\n *\n * @note The returned char* string should be used right away. The string memory\n * is reused and will be overwritten by the next call to `keycode_string()`.\n *\n * Many common QMK keycodes are understood by this function, but not all.\n * Recognized keycodes include:\n *\n *  - Most basic keycodes, including letters `KC_A` - `KC_Z`, digits `KC_0` -\n *    `KC_9`, function keys `KC_F1` - `KC_F24`, and modifiers like `KC_LSFT`.\n *\n *  - Modified basic keycodes, like `S(KC_1)` (Shift + 1 = !).\n *\n *  - `MO`, `TO`, `TG`, `OSL`, `LM(layer,mod)`, `LT(layer,kc)` layer switches.\n *\n *  - One-shot mod `OSM(mod)` keycodes.\n *\n *  - Mod-tap `MT(mod, kc)` keycodes.\n *\n *  - Tap dance keycodes `TD(i)`.\n *\n *  - Swap hands keycodes `SH_T(kc)`, `SH_TOGG`, etc.\n *\n *  - Joystick keycodes `JS_n`.\n *\n *  - Programmable button keycodes `PB_n`.\n *\n *  - Unicode `UC(codepoint)` and Unicode Map `UM(i)` and `UP(i,j)` keycodes.\n *\n *  - Keyboard range keycodes `QK_KB_*`.\n *\n *  - User range (SAFE_RANGE) keycodes `QK_USER_*`.\n *\n * Keycodes involving mods like `OSM`, `LM`, `MT` are fully supported only where\n * a single mod is applied.\n *\n * Unrecognized keycodes are printed numerically as hex values like `0x1ABC`.\n *\n * Optionally, use `keycode_string_names_user` or `keycode_string_names_kb` to\n * define names for additional keycodes or override how any of the above are\n * formatted.\n *\n * @param keycode  QMK keycode.\n * @return         Stringified keycode.\n */\nconst char* get_keycode_string(uint16_t keycode);\n\n/** Defines a human-readable name for a keycode. */\ntypedef struct {\n    uint16_t    keycode;\n    const char* name;\n} keycode_string_name_t;\n\n// clang-format off\n/**\n * @brief Defines names for additional keycodes for `get_keycode_string()`.\n *\n * Define `KEYCODE_STRING_NAMES_USER` in your keymap.c to add names for\n * additional keycodes to `keycode_string()`. This table may also be used to\n * override how `keycode_string()` formats a keycode. For example, supposing\n * keymap.c defines `MYMACRO1` and `MYMACRO2` as custom keycodes:\n *\n *     KEYCODE_STRING_NAMES_USER(\n *         KEYCODE_STRING_NAME(MYMACRO1),\n *         KEYCODE_STRING_NAME(MYMACRO2),\n *         KEYCODE_STRING_NAME(KC_EXLM),\n *     );\n *\n * The above defines names for `MYMACRO1` and `MYMACRO2`, and overrides\n * `KC_EXLM` to format as \"KC_EXLM\" instead of the default \"S(KC_1)\".\n */\n#    define KEYCODE_STRING_NAMES_USER(...)                                          \\\n    static const keycode_string_name_t keycode_string_names_user[] = {__VA_ARGS__}; \\\n    uint16_t keycode_string_names_size_user =                                       \\\n        sizeof(keycode_string_names_user) / sizeof(keycode_string_name_t);          \\\n    const keycode_string_name_t* keycode_string_names_data_user =                   \\\n        keycode_string_names_user\n\n/** Same as above, but defines keycode string names at the keyboard level. */\n#    define KEYCODE_STRING_NAMES_KB(...)                                            \\\n    static const keycode_string_name_t keycode_string_names_kb[] = {__VA_ARGS__};   \\\n    uint16_t keycode_string_names_size_kb =                                         \\\n        sizeof(keycode_string_names_kb) / sizeof(keycode_string_name_t);            \\\n    const keycode_string_name_t* keycode_string_names_data_kb =                     \\\n        keycode_string_names_kb\n\n/** Helper to define a keycode_string_name_t. */\n#    define KEYCODE_STRING_NAME(kc) \\\n        { (kc), #kc }\n// clang-format on\n\nextern const keycode_string_name_t* keycode_string_names_data_user;\nextern uint16_t                     keycode_string_names_size_user;\nextern const keycode_string_name_t* keycode_string_names_data_kb;\nextern uint16_t                     keycode_string_names_size_kb;\n\n#else\n\n// When keycode_string is disabled, fall back to printing keycodes numerically\n// as decimal values, using get_u16_str() from quantum.c.\n#    define get_keycode_string(kc) get_u16_str(kc, ' ')\n\nconst char* get_u16_str(uint16_t curr_num, char curr_pad);\n\n#    define KEYCODE_STRING_NAMES_USER(...)\n#    define KEYCODE_STRING_NAMES_KB(...)\n#    define KEYCODE_STRING_NAME(kc)\n\n#endif // KEYCODE_STRING_ENABLE\n"
  },
  {
    "path": "quantum/keycodes.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n// clang-format off\n\n#define QMK_KEYCODES_VERSION \"0.0.7\"\n#define QMK_KEYCODES_VERSION_BCD 0x00000007\n#define QMK_KEYCODES_VERSION_MAJOR 0\n#define QMK_KEYCODES_VERSION_MINOR 0\n#define QMK_KEYCODES_VERSION_PATCH 7\n\nenum qk_keycode_ranges {\n// Ranges\n    QK_BASIC                       = 0x0000,\n    QK_BASIC_MAX                   = 0x00FF,\n    QK_MODS                        = 0x0100,\n    QK_MODS_MAX                    = 0x1FFF,\n    QK_MOD_TAP                     = 0x2000,\n    QK_MOD_TAP_MAX                 = 0x3FFF,\n    QK_LAYER_TAP                   = 0x4000,\n    QK_LAYER_TAP_MAX               = 0x4FFF,\n    QK_LAYER_MOD                   = 0x5000,\n    QK_LAYER_MOD_MAX               = 0x51FF,\n    QK_TO                          = 0x5200,\n    QK_TO_MAX                      = 0x521F,\n    QK_MOMENTARY                   = 0x5220,\n    QK_MOMENTARY_MAX               = 0x523F,\n    QK_DEF_LAYER                   = 0x5240,\n    QK_DEF_LAYER_MAX               = 0x525F,\n    QK_TOGGLE_LAYER                = 0x5260,\n    QK_TOGGLE_LAYER_MAX            = 0x527F,\n    QK_ONE_SHOT_LAYER              = 0x5280,\n    QK_ONE_SHOT_LAYER_MAX          = 0x529F,\n    QK_ONE_SHOT_MOD                = 0x52A0,\n    QK_ONE_SHOT_MOD_MAX            = 0x52BF,\n    QK_LAYER_TAP_TOGGLE            = 0x52C0,\n    QK_LAYER_TAP_TOGGLE_MAX        = 0x52DF,\n    QK_PERSISTENT_DEF_LAYER        = 0x52E0,\n    QK_PERSISTENT_DEF_LAYER_MAX    = 0x52FF,\n    QK_SWAP_HANDS                  = 0x5600,\n    QK_SWAP_HANDS_MAX              = 0x56FF,\n    QK_TAP_DANCE                   = 0x5700,\n    QK_TAP_DANCE_MAX               = 0x57FF,\n    QK_MAGIC                       = 0x7000,\n    QK_MAGIC_MAX                   = 0x70FF,\n    QK_MIDI                        = 0x7100,\n    QK_MIDI_MAX                    = 0x71FF,\n    QK_SEQUENCER                   = 0x7200,\n    QK_SEQUENCER_MAX               = 0x73FF,\n    QK_JOYSTICK                    = 0x7400,\n    QK_JOYSTICK_MAX                = 0x743F,\n    QK_PROGRAMMABLE_BUTTON         = 0x7440,\n    QK_PROGRAMMABLE_BUTTON_MAX     = 0x747F,\n    QK_AUDIO                       = 0x7480,\n    QK_AUDIO_MAX                   = 0x74BF,\n    QK_STENO                       = 0x74C0,\n    QK_STENO_MAX                   = 0x74FF,\n    QK_MACRO                       = 0x7700,\n    QK_MACRO_MAX                   = 0x777F,\n    QK_CONNECTION                  = 0x7780,\n    QK_CONNECTION_MAX              = 0x77BF,\n    QK_COMMUNITY_MODULE            = 0x77C0,\n    QK_COMMUNITY_MODULE_MAX        = 0x77FF,\n    QK_LIGHTING                    = 0x7800,\n    QK_LIGHTING_MAX                = 0x78FF,\n    QK_QUANTUM                     = 0x7C00,\n    QK_QUANTUM_MAX                 = 0x7DFF,\n    QK_KB                          = 0x7E00,\n    QK_KB_MAX                      = 0x7E3F,\n    QK_USER                        = 0x7E40,\n    QK_USER_MAX                    = 0x7FFF,\n    QK_UNICODEMAP                  = 0x8000,\n    QK_UNICODEMAP_MAX              = 0xBFFF,\n    QK_UNICODE                     = 0x8000,\n    QK_UNICODE_MAX                 = 0xFFFF,\n    QK_UNICODEMAP_PAIR             = 0xC000,\n    QK_UNICODEMAP_PAIR_MAX         = 0xFFFF,\n};\n\nenum qk_keycode_defines {\n// Keycodes\n    KC_NO = 0x0000,\n    KC_TRANSPARENT = 0x0001,\n    KC_A = 0x0004,\n    KC_B = 0x0005,\n    KC_C = 0x0006,\n    KC_D = 0x0007,\n    KC_E = 0x0008,\n    KC_F = 0x0009,\n    KC_G = 0x000A,\n    KC_H = 0x000B,\n    KC_I = 0x000C,\n    KC_J = 0x000D,\n    KC_K = 0x000E,\n    KC_L = 0x000F,\n    KC_M = 0x0010,\n    KC_N = 0x0011,\n    KC_O = 0x0012,\n    KC_P = 0x0013,\n    KC_Q = 0x0014,\n    KC_R = 0x0015,\n    KC_S = 0x0016,\n    KC_T = 0x0017,\n    KC_U = 0x0018,\n    KC_V = 0x0019,\n    KC_W = 0x001A,\n    KC_X = 0x001B,\n    KC_Y = 0x001C,\n    KC_Z = 0x001D,\n    KC_1 = 0x001E,\n    KC_2 = 0x001F,\n    KC_3 = 0x0020,\n    KC_4 = 0x0021,\n    KC_5 = 0x0022,\n    KC_6 = 0x0023,\n    KC_7 = 0x0024,\n    KC_8 = 0x0025,\n    KC_9 = 0x0026,\n    KC_0 = 0x0027,\n    KC_ENTER = 0x0028,\n    KC_ESCAPE = 0x0029,\n    KC_BACKSPACE = 0x002A,\n    KC_TAB = 0x002B,\n    KC_SPACE = 0x002C,\n    KC_MINUS = 0x002D,\n    KC_EQUAL = 0x002E,\n    KC_LEFT_BRACKET = 0x002F,\n    KC_RIGHT_BRACKET = 0x0030,\n    KC_BACKSLASH = 0x0031,\n    KC_NONUS_HASH = 0x0032,\n    KC_SEMICOLON = 0x0033,\n    KC_QUOTE = 0x0034,\n    KC_GRAVE = 0x0035,\n    KC_COMMA = 0x0036,\n    KC_DOT = 0x0037,\n    KC_SLASH = 0x0038,\n    KC_CAPS_LOCK = 0x0039,\n    KC_F1 = 0x003A,\n    KC_F2 = 0x003B,\n    KC_F3 = 0x003C,\n    KC_F4 = 0x003D,\n    KC_F5 = 0x003E,\n    KC_F6 = 0x003F,\n    KC_F7 = 0x0040,\n    KC_F8 = 0x0041,\n    KC_F9 = 0x0042,\n    KC_F10 = 0x0043,\n    KC_F11 = 0x0044,\n    KC_F12 = 0x0045,\n    KC_PRINT_SCREEN = 0x0046,\n    KC_SCROLL_LOCK = 0x0047,\n    KC_PAUSE = 0x0048,\n    KC_INSERT = 0x0049,\n    KC_HOME = 0x004A,\n    KC_PAGE_UP = 0x004B,\n    KC_DELETE = 0x004C,\n    KC_END = 0x004D,\n    KC_PAGE_DOWN = 0x004E,\n    KC_RIGHT = 0x004F,\n    KC_LEFT = 0x0050,\n    KC_DOWN = 0x0051,\n    KC_UP = 0x0052,\n    KC_NUM_LOCK = 0x0053,\n    KC_KP_SLASH = 0x0054,\n    KC_KP_ASTERISK = 0x0055,\n    KC_KP_MINUS = 0x0056,\n    KC_KP_PLUS = 0x0057,\n    KC_KP_ENTER = 0x0058,\n    KC_KP_1 = 0x0059,\n    KC_KP_2 = 0x005A,\n    KC_KP_3 = 0x005B,\n    KC_KP_4 = 0x005C,\n    KC_KP_5 = 0x005D,\n    KC_KP_6 = 0x005E,\n    KC_KP_7 = 0x005F,\n    KC_KP_8 = 0x0060,\n    KC_KP_9 = 0x0061,\n    KC_KP_0 = 0x0062,\n    KC_KP_DOT = 0x0063,\n    KC_NONUS_BACKSLASH = 0x0064,\n    KC_APPLICATION = 0x0065,\n    KC_KB_POWER = 0x0066,\n    KC_KP_EQUAL = 0x0067,\n    KC_F13 = 0x0068,\n    KC_F14 = 0x0069,\n    KC_F15 = 0x006A,\n    KC_F16 = 0x006B,\n    KC_F17 = 0x006C,\n    KC_F18 = 0x006D,\n    KC_F19 = 0x006E,\n    KC_F20 = 0x006F,\n    KC_F21 = 0x0070,\n    KC_F22 = 0x0071,\n    KC_F23 = 0x0072,\n    KC_F24 = 0x0073,\n    KC_EXECUTE = 0x0074,\n    KC_HELP = 0x0075,\n    KC_MENU = 0x0076,\n    KC_SELECT = 0x0077,\n    KC_STOP = 0x0078,\n    KC_AGAIN = 0x0079,\n    KC_UNDO = 0x007A,\n    KC_CUT = 0x007B,\n    KC_COPY = 0x007C,\n    KC_PASTE = 0x007D,\n    KC_FIND = 0x007E,\n    KC_KB_MUTE = 0x007F,\n    KC_KB_VOLUME_UP = 0x0080,\n    KC_KB_VOLUME_DOWN = 0x0081,\n    KC_LOCKING_CAPS_LOCK = 0x0082,\n    KC_LOCKING_NUM_LOCK = 0x0083,\n    KC_LOCKING_SCROLL_LOCK = 0x0084,\n    KC_KP_COMMA = 0x0085,\n    KC_KP_EQUAL_AS400 = 0x0086,\n    KC_INTERNATIONAL_1 = 0x0087,\n    KC_INTERNATIONAL_2 = 0x0088,\n    KC_INTERNATIONAL_3 = 0x0089,\n    KC_INTERNATIONAL_4 = 0x008A,\n    KC_INTERNATIONAL_5 = 0x008B,\n    KC_INTERNATIONAL_6 = 0x008C,\n    KC_INTERNATIONAL_7 = 0x008D,\n    KC_INTERNATIONAL_8 = 0x008E,\n    KC_INTERNATIONAL_9 = 0x008F,\n    KC_LANGUAGE_1 = 0x0090,\n    KC_LANGUAGE_2 = 0x0091,\n    KC_LANGUAGE_3 = 0x0092,\n    KC_LANGUAGE_4 = 0x0093,\n    KC_LANGUAGE_5 = 0x0094,\n    KC_LANGUAGE_6 = 0x0095,\n    KC_LANGUAGE_7 = 0x0096,\n    KC_LANGUAGE_8 = 0x0097,\n    KC_LANGUAGE_9 = 0x0098,\n    KC_ALTERNATE_ERASE = 0x0099,\n    KC_SYSTEM_REQUEST = 0x009A,\n    KC_CANCEL = 0x009B,\n    KC_CLEAR = 0x009C,\n    KC_PRIOR = 0x009D,\n    KC_RETURN = 0x009E,\n    KC_SEPARATOR = 0x009F,\n    KC_OUT = 0x00A0,\n    KC_OPER = 0x00A1,\n    KC_CLEAR_AGAIN = 0x00A2,\n    KC_CRSEL = 0x00A3,\n    KC_EXSEL = 0x00A4,\n    KC_SYSTEM_POWER = 0x00A5,\n    KC_SYSTEM_SLEEP = 0x00A6,\n    KC_SYSTEM_WAKE = 0x00A7,\n    KC_AUDIO_MUTE = 0x00A8,\n    KC_AUDIO_VOL_UP = 0x00A9,\n    KC_AUDIO_VOL_DOWN = 0x00AA,\n    KC_MEDIA_NEXT_TRACK = 0x00AB,\n    KC_MEDIA_PREV_TRACK = 0x00AC,\n    KC_MEDIA_STOP = 0x00AD,\n    KC_MEDIA_PLAY_PAUSE = 0x00AE,\n    KC_MEDIA_SELECT = 0x00AF,\n    KC_MEDIA_EJECT = 0x00B0,\n    KC_MAIL = 0x00B1,\n    KC_CALCULATOR = 0x00B2,\n    KC_MY_COMPUTER = 0x00B3,\n    KC_WWW_SEARCH = 0x00B4,\n    KC_WWW_HOME = 0x00B5,\n    KC_WWW_BACK = 0x00B6,\n    KC_WWW_FORWARD = 0x00B7,\n    KC_WWW_STOP = 0x00B8,\n    KC_WWW_REFRESH = 0x00B9,\n    KC_WWW_FAVORITES = 0x00BA,\n    KC_MEDIA_FAST_FORWARD = 0x00BB,\n    KC_MEDIA_REWIND = 0x00BC,\n    KC_BRIGHTNESS_UP = 0x00BD,\n    KC_BRIGHTNESS_DOWN = 0x00BE,\n    KC_CONTROL_PANEL = 0x00BF,\n    KC_ASSISTANT = 0x00C0,\n    KC_MISSION_CONTROL = 0x00C1,\n    KC_LAUNCHPAD = 0x00C2,\n    QK_MOUSE_CURSOR_UP = 0x00CD,\n    QK_MOUSE_CURSOR_DOWN = 0x00CE,\n    QK_MOUSE_CURSOR_LEFT = 0x00CF,\n    QK_MOUSE_CURSOR_RIGHT = 0x00D0,\n    QK_MOUSE_BUTTON_1 = 0x00D1,\n    QK_MOUSE_BUTTON_2 = 0x00D2,\n    QK_MOUSE_BUTTON_3 = 0x00D3,\n    QK_MOUSE_BUTTON_4 = 0x00D4,\n    QK_MOUSE_BUTTON_5 = 0x00D5,\n    QK_MOUSE_BUTTON_6 = 0x00D6,\n    QK_MOUSE_BUTTON_7 = 0x00D7,\n    QK_MOUSE_BUTTON_8 = 0x00D8,\n    QK_MOUSE_WHEEL_UP = 0x00D9,\n    QK_MOUSE_WHEEL_DOWN = 0x00DA,\n    QK_MOUSE_WHEEL_LEFT = 0x00DB,\n    QK_MOUSE_WHEEL_RIGHT = 0x00DC,\n    QK_MOUSE_ACCELERATION_0 = 0x00DD,\n    QK_MOUSE_ACCELERATION_1 = 0x00DE,\n    QK_MOUSE_ACCELERATION_2 = 0x00DF,\n    KC_LEFT_CTRL = 0x00E0,\n    KC_LEFT_SHIFT = 0x00E1,\n    KC_LEFT_ALT = 0x00E2,\n    KC_LEFT_GUI = 0x00E3,\n    KC_RIGHT_CTRL = 0x00E4,\n    KC_RIGHT_SHIFT = 0x00E5,\n    KC_RIGHT_ALT = 0x00E6,\n    KC_RIGHT_GUI = 0x00E7,\n    QK_SWAP_HANDS_TOGGLE = 0x56F0,\n    QK_SWAP_HANDS_TAP_TOGGLE = 0x56F1,\n    QK_SWAP_HANDS_MOMENTARY_ON = 0x56F2,\n    QK_SWAP_HANDS_MOMENTARY_OFF = 0x56F3,\n    QK_SWAP_HANDS_OFF = 0x56F4,\n    QK_SWAP_HANDS_ON = 0x56F5,\n    QK_SWAP_HANDS_ONE_SHOT = 0x56F6,\n    QK_MAGIC_SWAP_CONTROL_CAPS_LOCK = 0x7000,\n    QK_MAGIC_UNSWAP_CONTROL_CAPS_LOCK = 0x7001,\n    QK_MAGIC_TOGGLE_CONTROL_CAPS_LOCK = 0x7002,\n    QK_MAGIC_CAPS_LOCK_AS_CONTROL_OFF = 0x7003,\n    QK_MAGIC_CAPS_LOCK_AS_CONTROL_ON = 0x7004,\n    QK_MAGIC_SWAP_LALT_LGUI = 0x7005,\n    QK_MAGIC_UNSWAP_LALT_LGUI = 0x7006,\n    QK_MAGIC_SWAP_RALT_RGUI = 0x7007,\n    QK_MAGIC_UNSWAP_RALT_RGUI = 0x7008,\n    QK_MAGIC_GUI_ON = 0x7009,\n    QK_MAGIC_GUI_OFF = 0x700A,\n    QK_MAGIC_TOGGLE_GUI = 0x700B,\n    QK_MAGIC_SWAP_GRAVE_ESC = 0x700C,\n    QK_MAGIC_UNSWAP_GRAVE_ESC = 0x700D,\n    QK_MAGIC_SWAP_BACKSLASH_BACKSPACE = 0x700E,\n    QK_MAGIC_UNSWAP_BACKSLASH_BACKSPACE = 0x700F,\n    QK_MAGIC_TOGGLE_BACKSLASH_BACKSPACE = 0x7010,\n    QK_MAGIC_NKRO_ON = 0x7011,\n    QK_MAGIC_NKRO_OFF = 0x7012,\n    QK_MAGIC_TOGGLE_NKRO = 0x7013,\n    QK_MAGIC_SWAP_ALT_GUI = 0x7014,\n    QK_MAGIC_UNSWAP_ALT_GUI = 0x7015,\n    QK_MAGIC_TOGGLE_ALT_GUI = 0x7016,\n    QK_MAGIC_SWAP_LCTL_LGUI = 0x7017,\n    QK_MAGIC_UNSWAP_LCTL_LGUI = 0x7018,\n    QK_MAGIC_SWAP_RCTL_RGUI = 0x7019,\n    QK_MAGIC_UNSWAP_RCTL_RGUI = 0x701A,\n    QK_MAGIC_SWAP_CTL_GUI = 0x701B,\n    QK_MAGIC_UNSWAP_CTL_GUI = 0x701C,\n    QK_MAGIC_TOGGLE_CTL_GUI = 0x701D,\n    QK_MAGIC_EE_HANDS_LEFT = 0x701E,\n    QK_MAGIC_EE_HANDS_RIGHT = 0x701F,\n    QK_MAGIC_SWAP_ESCAPE_CAPS_LOCK = 0x7020,\n    QK_MAGIC_UNSWAP_ESCAPE_CAPS_LOCK = 0x7021,\n    QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK = 0x7022,\n    QK_MIDI_ON = 0x7100,\n    QK_MIDI_OFF = 0x7101,\n    QK_MIDI_TOGGLE = 0x7102,\n    QK_MIDI_NOTE_C_0 = 0x7103,\n    QK_MIDI_NOTE_C_SHARP_0 = 0x7104,\n    QK_MIDI_NOTE_D_0 = 0x7105,\n    QK_MIDI_NOTE_D_SHARP_0 = 0x7106,\n    QK_MIDI_NOTE_E_0 = 0x7107,\n    QK_MIDI_NOTE_F_0 = 0x7108,\n    QK_MIDI_NOTE_F_SHARP_0 = 0x7109,\n    QK_MIDI_NOTE_G_0 = 0x710A,\n    QK_MIDI_NOTE_G_SHARP_0 = 0x710B,\n    QK_MIDI_NOTE_A_0 = 0x710C,\n    QK_MIDI_NOTE_A_SHARP_0 = 0x710D,\n    QK_MIDI_NOTE_B_0 = 0x710E,\n    QK_MIDI_NOTE_C_1 = 0x710F,\n    QK_MIDI_NOTE_C_SHARP_1 = 0x7110,\n    QK_MIDI_NOTE_D_1 = 0x7111,\n    QK_MIDI_NOTE_D_SHARP_1 = 0x7112,\n    QK_MIDI_NOTE_E_1 = 0x7113,\n    QK_MIDI_NOTE_F_1 = 0x7114,\n    QK_MIDI_NOTE_F_SHARP_1 = 0x7115,\n    QK_MIDI_NOTE_G_1 = 0x7116,\n    QK_MIDI_NOTE_G_SHARP_1 = 0x7117,\n    QK_MIDI_NOTE_A_1 = 0x7118,\n    QK_MIDI_NOTE_A_SHARP_1 = 0x7119,\n    QK_MIDI_NOTE_B_1 = 0x711A,\n    QK_MIDI_NOTE_C_2 = 0x711B,\n    QK_MIDI_NOTE_C_SHARP_2 = 0x711C,\n    QK_MIDI_NOTE_D_2 = 0x711D,\n    QK_MIDI_NOTE_D_SHARP_2 = 0x711E,\n    QK_MIDI_NOTE_E_2 = 0x711F,\n    QK_MIDI_NOTE_F_2 = 0x7120,\n    QK_MIDI_NOTE_F_SHARP_2 = 0x7121,\n    QK_MIDI_NOTE_G_2 = 0x7122,\n    QK_MIDI_NOTE_G_SHARP_2 = 0x7123,\n    QK_MIDI_NOTE_A_2 = 0x7124,\n    QK_MIDI_NOTE_A_SHARP_2 = 0x7125,\n    QK_MIDI_NOTE_B_2 = 0x7126,\n    QK_MIDI_NOTE_C_3 = 0x7127,\n    QK_MIDI_NOTE_C_SHARP_3 = 0x7128,\n    QK_MIDI_NOTE_D_3 = 0x7129,\n    QK_MIDI_NOTE_D_SHARP_3 = 0x712A,\n    QK_MIDI_NOTE_E_3 = 0x712B,\n    QK_MIDI_NOTE_F_3 = 0x712C,\n    QK_MIDI_NOTE_F_SHARP_3 = 0x712D,\n    QK_MIDI_NOTE_G_3 = 0x712E,\n    QK_MIDI_NOTE_G_SHARP_3 = 0x712F,\n    QK_MIDI_NOTE_A_3 = 0x7130,\n    QK_MIDI_NOTE_A_SHARP_3 = 0x7131,\n    QK_MIDI_NOTE_B_3 = 0x7132,\n    QK_MIDI_NOTE_C_4 = 0x7133,\n    QK_MIDI_NOTE_C_SHARP_4 = 0x7134,\n    QK_MIDI_NOTE_D_4 = 0x7135,\n    QK_MIDI_NOTE_D_SHARP_4 = 0x7136,\n    QK_MIDI_NOTE_E_4 = 0x7137,\n    QK_MIDI_NOTE_F_4 = 0x7138,\n    QK_MIDI_NOTE_F_SHARP_4 = 0x7139,\n    QK_MIDI_NOTE_G_4 = 0x713A,\n    QK_MIDI_NOTE_G_SHARP_4 = 0x713B,\n    QK_MIDI_NOTE_A_4 = 0x713C,\n    QK_MIDI_NOTE_A_SHARP_4 = 0x713D,\n    QK_MIDI_NOTE_B_4 = 0x713E,\n    QK_MIDI_NOTE_C_5 = 0x713F,\n    QK_MIDI_NOTE_C_SHARP_5 = 0x7140,\n    QK_MIDI_NOTE_D_5 = 0x7141,\n    QK_MIDI_NOTE_D_SHARP_5 = 0x7142,\n    QK_MIDI_NOTE_E_5 = 0x7143,\n    QK_MIDI_NOTE_F_5 = 0x7144,\n    QK_MIDI_NOTE_F_SHARP_5 = 0x7145,\n    QK_MIDI_NOTE_G_5 = 0x7146,\n    QK_MIDI_NOTE_G_SHARP_5 = 0x7147,\n    QK_MIDI_NOTE_A_5 = 0x7148,\n    QK_MIDI_NOTE_A_SHARP_5 = 0x7149,\n    QK_MIDI_NOTE_B_5 = 0x714A,\n    QK_MIDI_OCTAVE_N2 = 0x714B,\n    QK_MIDI_OCTAVE_N1 = 0x714C,\n    QK_MIDI_OCTAVE_0 = 0x714D,\n    QK_MIDI_OCTAVE_1 = 0x714E,\n    QK_MIDI_OCTAVE_2 = 0x714F,\n    QK_MIDI_OCTAVE_3 = 0x7150,\n    QK_MIDI_OCTAVE_4 = 0x7151,\n    QK_MIDI_OCTAVE_5 = 0x7152,\n    QK_MIDI_OCTAVE_6 = 0x7153,\n    QK_MIDI_OCTAVE_7 = 0x7154,\n    QK_MIDI_OCTAVE_DOWN = 0x7155,\n    QK_MIDI_OCTAVE_UP = 0x7156,\n    QK_MIDI_TRANSPOSE_N6 = 0x7157,\n    QK_MIDI_TRANSPOSE_N5 = 0x7158,\n    QK_MIDI_TRANSPOSE_N4 = 0x7159,\n    QK_MIDI_TRANSPOSE_N3 = 0x715A,\n    QK_MIDI_TRANSPOSE_N2 = 0x715B,\n    QK_MIDI_TRANSPOSE_N1 = 0x715C,\n    QK_MIDI_TRANSPOSE_0 = 0x715D,\n    QK_MIDI_TRANSPOSE_1 = 0x715E,\n    QK_MIDI_TRANSPOSE_2 = 0x715F,\n    QK_MIDI_TRANSPOSE_3 = 0x7160,\n    QK_MIDI_TRANSPOSE_4 = 0x7161,\n    QK_MIDI_TRANSPOSE_5 = 0x7162,\n    QK_MIDI_TRANSPOSE_6 = 0x7163,\n    QK_MIDI_TRANSPOSE_DOWN = 0x7164,\n    QK_MIDI_TRANSPOSE_UP = 0x7165,\n    QK_MIDI_VELOCITY_0 = 0x7166,\n    QK_MIDI_VELOCITY_1 = 0x7167,\n    QK_MIDI_VELOCITY_2 = 0x7168,\n    QK_MIDI_VELOCITY_3 = 0x7169,\n    QK_MIDI_VELOCITY_4 = 0x716A,\n    QK_MIDI_VELOCITY_5 = 0x716B,\n    QK_MIDI_VELOCITY_6 = 0x716C,\n    QK_MIDI_VELOCITY_7 = 0x716D,\n    QK_MIDI_VELOCITY_8 = 0x716E,\n    QK_MIDI_VELOCITY_9 = 0x716F,\n    QK_MIDI_VELOCITY_10 = 0x7170,\n    QK_MIDI_VELOCITY_DOWN = 0x7171,\n    QK_MIDI_VELOCITY_UP = 0x7172,\n    QK_MIDI_CHANNEL_1 = 0x7173,\n    QK_MIDI_CHANNEL_2 = 0x7174,\n    QK_MIDI_CHANNEL_3 = 0x7175,\n    QK_MIDI_CHANNEL_4 = 0x7176,\n    QK_MIDI_CHANNEL_5 = 0x7177,\n    QK_MIDI_CHANNEL_6 = 0x7178,\n    QK_MIDI_CHANNEL_7 = 0x7179,\n    QK_MIDI_CHANNEL_8 = 0x717A,\n    QK_MIDI_CHANNEL_9 = 0x717B,\n    QK_MIDI_CHANNEL_10 = 0x717C,\n    QK_MIDI_CHANNEL_11 = 0x717D,\n    QK_MIDI_CHANNEL_12 = 0x717E,\n    QK_MIDI_CHANNEL_13 = 0x717F,\n    QK_MIDI_CHANNEL_14 = 0x7180,\n    QK_MIDI_CHANNEL_15 = 0x7181,\n    QK_MIDI_CHANNEL_16 = 0x7182,\n    QK_MIDI_CHANNEL_DOWN = 0x7183,\n    QK_MIDI_CHANNEL_UP = 0x7184,\n    QK_MIDI_ALL_NOTES_OFF = 0x7185,\n    QK_MIDI_SUSTAIN = 0x7186,\n    QK_MIDI_PORTAMENTO = 0x7187,\n    QK_MIDI_SOSTENUTO = 0x7188,\n    QK_MIDI_SOFT = 0x7189,\n    QK_MIDI_LEGATO = 0x718A,\n    QK_MIDI_MODULATION = 0x718B,\n    QK_MIDI_MODULATION_SPEED_DOWN = 0x718C,\n    QK_MIDI_MODULATION_SPEED_UP = 0x718D,\n    QK_MIDI_PITCH_BEND_DOWN = 0x718E,\n    QK_MIDI_PITCH_BEND_UP = 0x718F,\n    QK_SEQUENCER_ON = 0x7200,\n    QK_SEQUENCER_OFF = 0x7201,\n    QK_SEQUENCER_TOGGLE = 0x7202,\n    QK_SEQUENCER_TEMPO_DOWN = 0x7203,\n    QK_SEQUENCER_TEMPO_UP = 0x7204,\n    QK_SEQUENCER_RESOLUTION_DOWN = 0x7205,\n    QK_SEQUENCER_RESOLUTION_UP = 0x7206,\n    QK_SEQUENCER_STEPS_ALL = 0x7207,\n    QK_SEQUENCER_STEPS_CLEAR = 0x7208,\n    QK_JOYSTICK_BUTTON_0 = 0x7400,\n    QK_JOYSTICK_BUTTON_1 = 0x7401,\n    QK_JOYSTICK_BUTTON_2 = 0x7402,\n    QK_JOYSTICK_BUTTON_3 = 0x7403,\n    QK_JOYSTICK_BUTTON_4 = 0x7404,\n    QK_JOYSTICK_BUTTON_5 = 0x7405,\n    QK_JOYSTICK_BUTTON_6 = 0x7406,\n    QK_JOYSTICK_BUTTON_7 = 0x7407,\n    QK_JOYSTICK_BUTTON_8 = 0x7408,\n    QK_JOYSTICK_BUTTON_9 = 0x7409,\n    QK_JOYSTICK_BUTTON_10 = 0x740A,\n    QK_JOYSTICK_BUTTON_11 = 0x740B,\n    QK_JOYSTICK_BUTTON_12 = 0x740C,\n    QK_JOYSTICK_BUTTON_13 = 0x740D,\n    QK_JOYSTICK_BUTTON_14 = 0x740E,\n    QK_JOYSTICK_BUTTON_15 = 0x740F,\n    QK_JOYSTICK_BUTTON_16 = 0x7410,\n    QK_JOYSTICK_BUTTON_17 = 0x7411,\n    QK_JOYSTICK_BUTTON_18 = 0x7412,\n    QK_JOYSTICK_BUTTON_19 = 0x7413,\n    QK_JOYSTICK_BUTTON_20 = 0x7414,\n    QK_JOYSTICK_BUTTON_21 = 0x7415,\n    QK_JOYSTICK_BUTTON_22 = 0x7416,\n    QK_JOYSTICK_BUTTON_23 = 0x7417,\n    QK_JOYSTICK_BUTTON_24 = 0x7418,\n    QK_JOYSTICK_BUTTON_25 = 0x7419,\n    QK_JOYSTICK_BUTTON_26 = 0x741A,\n    QK_JOYSTICK_BUTTON_27 = 0x741B,\n    QK_JOYSTICK_BUTTON_28 = 0x741C,\n    QK_JOYSTICK_BUTTON_29 = 0x741D,\n    QK_JOYSTICK_BUTTON_30 = 0x741E,\n    QK_JOYSTICK_BUTTON_31 = 0x741F,\n    QK_PROGRAMMABLE_BUTTON_1 = 0x7440,\n    QK_PROGRAMMABLE_BUTTON_2 = 0x7441,\n    QK_PROGRAMMABLE_BUTTON_3 = 0x7442,\n    QK_PROGRAMMABLE_BUTTON_4 = 0x7443,\n    QK_PROGRAMMABLE_BUTTON_5 = 0x7444,\n    QK_PROGRAMMABLE_BUTTON_6 = 0x7445,\n    QK_PROGRAMMABLE_BUTTON_7 = 0x7446,\n    QK_PROGRAMMABLE_BUTTON_8 = 0x7447,\n    QK_PROGRAMMABLE_BUTTON_9 = 0x7448,\n    QK_PROGRAMMABLE_BUTTON_10 = 0x7449,\n    QK_PROGRAMMABLE_BUTTON_11 = 0x744A,\n    QK_PROGRAMMABLE_BUTTON_12 = 0x744B,\n    QK_PROGRAMMABLE_BUTTON_13 = 0x744C,\n    QK_PROGRAMMABLE_BUTTON_14 = 0x744D,\n    QK_PROGRAMMABLE_BUTTON_15 = 0x744E,\n    QK_PROGRAMMABLE_BUTTON_16 = 0x744F,\n    QK_PROGRAMMABLE_BUTTON_17 = 0x7450,\n    QK_PROGRAMMABLE_BUTTON_18 = 0x7451,\n    QK_PROGRAMMABLE_BUTTON_19 = 0x7452,\n    QK_PROGRAMMABLE_BUTTON_20 = 0x7453,\n    QK_PROGRAMMABLE_BUTTON_21 = 0x7454,\n    QK_PROGRAMMABLE_BUTTON_22 = 0x7455,\n    QK_PROGRAMMABLE_BUTTON_23 = 0x7456,\n    QK_PROGRAMMABLE_BUTTON_24 = 0x7457,\n    QK_PROGRAMMABLE_BUTTON_25 = 0x7458,\n    QK_PROGRAMMABLE_BUTTON_26 = 0x7459,\n    QK_PROGRAMMABLE_BUTTON_27 = 0x745A,\n    QK_PROGRAMMABLE_BUTTON_28 = 0x745B,\n    QK_PROGRAMMABLE_BUTTON_29 = 0x745C,\n    QK_PROGRAMMABLE_BUTTON_30 = 0x745D,\n    QK_PROGRAMMABLE_BUTTON_31 = 0x745E,\n    QK_PROGRAMMABLE_BUTTON_32 = 0x745F,\n    QK_AUDIO_ON = 0x7480,\n    QK_AUDIO_OFF = 0x7481,\n    QK_AUDIO_TOGGLE = 0x7482,\n    QK_AUDIO_CLICKY_TOGGLE = 0x748A,\n    QK_AUDIO_CLICKY_ON = 0x748B,\n    QK_AUDIO_CLICKY_OFF = 0x748C,\n    QK_AUDIO_CLICKY_UP = 0x748D,\n    QK_AUDIO_CLICKY_DOWN = 0x748E,\n    QK_AUDIO_CLICKY_RESET = 0x748F,\n    QK_MUSIC_ON = 0x7490,\n    QK_MUSIC_OFF = 0x7491,\n    QK_MUSIC_TOGGLE = 0x7492,\n    QK_MUSIC_MODE_NEXT = 0x7493,\n    QK_AUDIO_VOICE_NEXT = 0x7494,\n    QK_AUDIO_VOICE_PREVIOUS = 0x7495,\n    QK_STENO_BOLT = 0x74F0,\n    QK_STENO_GEMINI = 0x74F1,\n    QK_STENO_COMB = 0x74F2,\n    QK_STENO_COMB_MAX = 0x74FC,\n    QK_MACRO_0 = 0x7700,\n    QK_MACRO_1 = 0x7701,\n    QK_MACRO_2 = 0x7702,\n    QK_MACRO_3 = 0x7703,\n    QK_MACRO_4 = 0x7704,\n    QK_MACRO_5 = 0x7705,\n    QK_MACRO_6 = 0x7706,\n    QK_MACRO_7 = 0x7707,\n    QK_MACRO_8 = 0x7708,\n    QK_MACRO_9 = 0x7709,\n    QK_MACRO_10 = 0x770A,\n    QK_MACRO_11 = 0x770B,\n    QK_MACRO_12 = 0x770C,\n    QK_MACRO_13 = 0x770D,\n    QK_MACRO_14 = 0x770E,\n    QK_MACRO_15 = 0x770F,\n    QK_MACRO_16 = 0x7710,\n    QK_MACRO_17 = 0x7711,\n    QK_MACRO_18 = 0x7712,\n    QK_MACRO_19 = 0x7713,\n    QK_MACRO_20 = 0x7714,\n    QK_MACRO_21 = 0x7715,\n    QK_MACRO_22 = 0x7716,\n    QK_MACRO_23 = 0x7717,\n    QK_MACRO_24 = 0x7718,\n    QK_MACRO_25 = 0x7719,\n    QK_MACRO_26 = 0x771A,\n    QK_MACRO_27 = 0x771B,\n    QK_MACRO_28 = 0x771C,\n    QK_MACRO_29 = 0x771D,\n    QK_MACRO_30 = 0x771E,\n    QK_MACRO_31 = 0x771F,\n    QK_OUTPUT_AUTO = 0x7780,\n    QK_OUTPUT_NEXT = 0x7781,\n    QK_OUTPUT_PREV = 0x7782,\n    QK_OUTPUT_NONE = 0x7783,\n    QK_OUTPUT_USB = 0x7784,\n    QK_OUTPUT_2P4GHZ = 0x7785,\n    QK_OUTPUT_BLUETOOTH = 0x7786,\n    QK_BLUETOOTH_PROFILE_NEXT = 0x7790,\n    QK_BLUETOOTH_PROFILE_PREV = 0x7791,\n    QK_BLUETOOTH_UNPAIR = 0x7792,\n    QK_BLUETOOTH_PROFILE1 = 0x7793,\n    QK_BLUETOOTH_PROFILE2 = 0x7794,\n    QK_BLUETOOTH_PROFILE3 = 0x7795,\n    QK_BLUETOOTH_PROFILE4 = 0x7796,\n    QK_BLUETOOTH_PROFILE5 = 0x7797,\n    QK_BACKLIGHT_ON = 0x7800,\n    QK_BACKLIGHT_OFF = 0x7801,\n    QK_BACKLIGHT_TOGGLE = 0x7802,\n    QK_BACKLIGHT_DOWN = 0x7803,\n    QK_BACKLIGHT_UP = 0x7804,\n    QK_BACKLIGHT_STEP = 0x7805,\n    QK_BACKLIGHT_TOGGLE_BREATHING = 0x7806,\n    QK_LED_MATRIX_ON = 0x7810,\n    QK_LED_MATRIX_OFF = 0x7811,\n    QK_LED_MATRIX_TOGGLE = 0x7812,\n    QK_LED_MATRIX_MODE_NEXT = 0x7813,\n    QK_LED_MATRIX_MODE_PREVIOUS = 0x7814,\n    QK_LED_MATRIX_BRIGHTNESS_UP = 0x7815,\n    QK_LED_MATRIX_BRIGHTNESS_DOWN = 0x7816,\n    QK_LED_MATRIX_SPEED_UP = 0x7817,\n    QK_LED_MATRIX_SPEED_DOWN = 0x7818,\n    QK_UNDERGLOW_TOGGLE = 0x7820,\n    QK_UNDERGLOW_MODE_NEXT = 0x7821,\n    QK_UNDERGLOW_MODE_PREVIOUS = 0x7822,\n    QK_UNDERGLOW_HUE_UP = 0x7823,\n    QK_UNDERGLOW_HUE_DOWN = 0x7824,\n    QK_UNDERGLOW_SATURATION_UP = 0x7825,\n    QK_UNDERGLOW_SATURATION_DOWN = 0x7826,\n    QK_UNDERGLOW_VALUE_UP = 0x7827,\n    QK_UNDERGLOW_VALUE_DOWN = 0x7828,\n    QK_UNDERGLOW_SPEED_UP = 0x7829,\n    QK_UNDERGLOW_SPEED_DOWN = 0x782A,\n    RGB_MODE_PLAIN = 0x782B,\n    RGB_MODE_BREATHE = 0x782C,\n    RGB_MODE_RAINBOW = 0x782D,\n    RGB_MODE_SWIRL = 0x782E,\n    RGB_MODE_SNAKE = 0x782F,\n    RGB_MODE_KNIGHT = 0x7830,\n    RGB_MODE_XMAS = 0x7831,\n    RGB_MODE_GRADIENT = 0x7832,\n    RGB_MODE_RGBTEST = 0x7833,\n    RGB_MODE_TWINKLE = 0x7834,\n    QK_RGB_MATRIX_ON = 0x7840,\n    QK_RGB_MATRIX_OFF = 0x7841,\n    QK_RGB_MATRIX_TOGGLE = 0x7842,\n    QK_RGB_MATRIX_MODE_NEXT = 0x7843,\n    QK_RGB_MATRIX_MODE_PREVIOUS = 0x7844,\n    QK_RGB_MATRIX_HUE_UP = 0x7845,\n    QK_RGB_MATRIX_HUE_DOWN = 0x7846,\n    QK_RGB_MATRIX_SATURATION_UP = 0x7847,\n    QK_RGB_MATRIX_SATURATION_DOWN = 0x7848,\n    QK_RGB_MATRIX_VALUE_UP = 0x7849,\n    QK_RGB_MATRIX_VALUE_DOWN = 0x784A,\n    QK_RGB_MATRIX_SPEED_UP = 0x784B,\n    QK_RGB_MATRIX_SPEED_DOWN = 0x784C,\n    QK_BOOTLOADER = 0x7C00,\n    QK_REBOOT = 0x7C01,\n    QK_DEBUG_TOGGLE = 0x7C02,\n    QK_CLEAR_EEPROM = 0x7C03,\n    QK_MAKE = 0x7C04,\n    QK_AUTO_SHIFT_DOWN = 0x7C10,\n    QK_AUTO_SHIFT_UP = 0x7C11,\n    QK_AUTO_SHIFT_REPORT = 0x7C12,\n    QK_AUTO_SHIFT_ON = 0x7C13,\n    QK_AUTO_SHIFT_OFF = 0x7C14,\n    QK_AUTO_SHIFT_TOGGLE = 0x7C15,\n    QK_GRAVE_ESCAPE = 0x7C16,\n    QK_VELOCIKEY_TOGGLE = 0x7C17,\n    QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN = 0x7C18,\n    QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE = 0x7C19,\n    QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN = 0x7C1A,\n    QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE = 0x7C1B,\n    QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN = 0x7C1C,\n    QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE = 0x7C1D,\n    QK_SPACE_CADET_RIGHT_SHIFT_ENTER = 0x7C1E,\n    QK_UNICODE_MODE_NEXT = 0x7C30,\n    QK_UNICODE_MODE_PREVIOUS = 0x7C31,\n    QK_UNICODE_MODE_MACOS = 0x7C32,\n    QK_UNICODE_MODE_LINUX = 0x7C33,\n    QK_UNICODE_MODE_WINDOWS = 0x7C34,\n    QK_UNICODE_MODE_BSD = 0x7C35,\n    QK_UNICODE_MODE_WINCOMPOSE = 0x7C36,\n    QK_UNICODE_MODE_EMACS = 0x7C37,\n    QK_HAPTIC_ON = 0x7C40,\n    QK_HAPTIC_OFF = 0x7C41,\n    QK_HAPTIC_TOGGLE = 0x7C42,\n    QK_HAPTIC_RESET = 0x7C43,\n    QK_HAPTIC_FEEDBACK_TOGGLE = 0x7C44,\n    QK_HAPTIC_BUZZ_TOGGLE = 0x7C45,\n    QK_HAPTIC_MODE_NEXT = 0x7C46,\n    QK_HAPTIC_MODE_PREVIOUS = 0x7C47,\n    QK_HAPTIC_CONTINUOUS_TOGGLE = 0x7C48,\n    QK_HAPTIC_CONTINUOUS_UP = 0x7C49,\n    QK_HAPTIC_CONTINUOUS_DOWN = 0x7C4A,\n    QK_HAPTIC_DWELL_UP = 0x7C4B,\n    QK_HAPTIC_DWELL_DOWN = 0x7C4C,\n    QK_COMBO_ON = 0x7C50,\n    QK_COMBO_OFF = 0x7C51,\n    QK_COMBO_TOGGLE = 0x7C52,\n    QK_DYNAMIC_MACRO_RECORD_START_1 = 0x7C53,\n    QK_DYNAMIC_MACRO_RECORD_START_2 = 0x7C54,\n    QK_DYNAMIC_MACRO_RECORD_STOP = 0x7C55,\n    QK_DYNAMIC_MACRO_PLAY_1 = 0x7C56,\n    QK_DYNAMIC_MACRO_PLAY_2 = 0x7C57,\n    QK_LEADER = 0x7C58,\n    QK_LOCK = 0x7C59,\n    QK_ONE_SHOT_ON = 0x7C5A,\n    QK_ONE_SHOT_OFF = 0x7C5B,\n    QK_ONE_SHOT_TOGGLE = 0x7C5C,\n    QK_KEY_OVERRIDE_TOGGLE = 0x7C5D,\n    QK_KEY_OVERRIDE_ON = 0x7C5E,\n    QK_KEY_OVERRIDE_OFF = 0x7C5F,\n    QK_SECURE_LOCK = 0x7C60,\n    QK_SECURE_UNLOCK = 0x7C61,\n    QK_SECURE_TOGGLE = 0x7C62,\n    QK_SECURE_REQUEST = 0x7C63,\n    QK_DYNAMIC_TAPPING_TERM_PRINT = 0x7C70,\n    QK_DYNAMIC_TAPPING_TERM_UP = 0x7C71,\n    QK_DYNAMIC_TAPPING_TERM_DOWN = 0x7C72,\n    QK_CAPS_WORD_TOGGLE = 0x7C73,\n    QK_AUTOCORRECT_ON = 0x7C74,\n    QK_AUTOCORRECT_OFF = 0x7C75,\n    QK_AUTOCORRECT_TOGGLE = 0x7C76,\n    QK_TRI_LAYER_LOWER = 0x7C77,\n    QK_TRI_LAYER_UPPER = 0x7C78,\n    QK_REPEAT_KEY = 0x7C79,\n    QK_ALT_REPEAT_KEY = 0x7C7A,\n    QK_LAYER_LOCK = 0x7C7B,\n    QK_KB_0 = 0x7E00,\n    QK_KB_1 = 0x7E01,\n    QK_KB_2 = 0x7E02,\n    QK_KB_3 = 0x7E03,\n    QK_KB_4 = 0x7E04,\n    QK_KB_5 = 0x7E05,\n    QK_KB_6 = 0x7E06,\n    QK_KB_7 = 0x7E07,\n    QK_KB_8 = 0x7E08,\n    QK_KB_9 = 0x7E09,\n    QK_KB_10 = 0x7E0A,\n    QK_KB_11 = 0x7E0B,\n    QK_KB_12 = 0x7E0C,\n    QK_KB_13 = 0x7E0D,\n    QK_KB_14 = 0x7E0E,\n    QK_KB_15 = 0x7E0F,\n    QK_KB_16 = 0x7E10,\n    QK_KB_17 = 0x7E11,\n    QK_KB_18 = 0x7E12,\n    QK_KB_19 = 0x7E13,\n    QK_KB_20 = 0x7E14,\n    QK_KB_21 = 0x7E15,\n    QK_KB_22 = 0x7E16,\n    QK_KB_23 = 0x7E17,\n    QK_KB_24 = 0x7E18,\n    QK_KB_25 = 0x7E19,\n    QK_KB_26 = 0x7E1A,\n    QK_KB_27 = 0x7E1B,\n    QK_KB_28 = 0x7E1C,\n    QK_KB_29 = 0x7E1D,\n    QK_KB_30 = 0x7E1E,\n    QK_KB_31 = 0x7E1F,\n    QK_USER_0 = 0x7E40,\n    QK_USER_1 = 0x7E41,\n    QK_USER_2 = 0x7E42,\n    QK_USER_3 = 0x7E43,\n    QK_USER_4 = 0x7E44,\n    QK_USER_5 = 0x7E45,\n    QK_USER_6 = 0x7E46,\n    QK_USER_7 = 0x7E47,\n    QK_USER_8 = 0x7E48,\n    QK_USER_9 = 0x7E49,\n    QK_USER_10 = 0x7E4A,\n    QK_USER_11 = 0x7E4B,\n    QK_USER_12 = 0x7E4C,\n    QK_USER_13 = 0x7E4D,\n    QK_USER_14 = 0x7E4E,\n    QK_USER_15 = 0x7E4F,\n    QK_USER_16 = 0x7E50,\n    QK_USER_17 = 0x7E51,\n    QK_USER_18 = 0x7E52,\n    QK_USER_19 = 0x7E53,\n    QK_USER_20 = 0x7E54,\n    QK_USER_21 = 0x7E55,\n    QK_USER_22 = 0x7E56,\n    QK_USER_23 = 0x7E57,\n    QK_USER_24 = 0x7E58,\n    QK_USER_25 = 0x7E59,\n    QK_USER_26 = 0x7E5A,\n    QK_USER_27 = 0x7E5B,\n    QK_USER_28 = 0x7E5C,\n    QK_USER_29 = 0x7E5D,\n    QK_USER_30 = 0x7E5E,\n    QK_USER_31 = 0x7E5F,\n\n// Alias\n    XXXXXXX    = KC_NO,\n    _______    = KC_TRANSPARENT,\n    KC_TRNS    = KC_TRANSPARENT,\n    KC_ENT     = KC_ENTER,\n    KC_ESC     = KC_ESCAPE,\n    KC_BSPC    = KC_BACKSPACE,\n    KC_SPC     = KC_SPACE,\n    KC_MINS    = KC_MINUS,\n    KC_EQL     = KC_EQUAL,\n    KC_LBRC    = KC_LEFT_BRACKET,\n    KC_RBRC    = KC_RIGHT_BRACKET,\n    KC_BSLS    = KC_BACKSLASH,\n    KC_NUHS    = KC_NONUS_HASH,\n    KC_SCLN    = KC_SEMICOLON,\n    KC_QUOT    = KC_QUOTE,\n    KC_GRV     = KC_GRAVE,\n    KC_COMM    = KC_COMMA,\n    KC_SLSH    = KC_SLASH,\n    KC_CAPS    = KC_CAPS_LOCK,\n    KC_PSCR    = KC_PRINT_SCREEN,\n    KC_SCRL    = KC_SCROLL_LOCK,\n    KC_BRMD    = KC_SCROLL_LOCK,\n    KC_PAUS    = KC_PAUSE,\n    KC_BRK     = KC_PAUSE,\n    KC_BRMU    = KC_PAUSE,\n    KC_INS     = KC_INSERT,\n    KC_PGUP    = KC_PAGE_UP,\n    KC_DEL     = KC_DELETE,\n    KC_PGDN    = KC_PAGE_DOWN,\n    KC_RGHT    = KC_RIGHT,\n    KC_NUM     = KC_NUM_LOCK,\n    KC_PSLS    = KC_KP_SLASH,\n    KC_PAST    = KC_KP_ASTERISK,\n    KC_PMNS    = KC_KP_MINUS,\n    KC_PPLS    = KC_KP_PLUS,\n    KC_PENT    = KC_KP_ENTER,\n    KC_P1      = KC_KP_1,\n    KC_P2      = KC_KP_2,\n    KC_P3      = KC_KP_3,\n    KC_P4      = KC_KP_4,\n    KC_P5      = KC_KP_5,\n    KC_P6      = KC_KP_6,\n    KC_P7      = KC_KP_7,\n    KC_P8      = KC_KP_8,\n    KC_P9      = KC_KP_9,\n    KC_P0      = KC_KP_0,\n    KC_PDOT    = KC_KP_DOT,\n    KC_NUBS    = KC_NONUS_BACKSLASH,\n    KC_APP     = KC_APPLICATION,\n    KC_PEQL    = KC_KP_EQUAL,\n    KC_EXEC    = KC_EXECUTE,\n    KC_SLCT    = KC_SELECT,\n    KC_AGIN    = KC_AGAIN,\n    KC_PSTE    = KC_PASTE,\n    KC_LCAP    = KC_LOCKING_CAPS_LOCK,\n    KC_LNUM    = KC_LOCKING_NUM_LOCK,\n    KC_LSCR    = KC_LOCKING_SCROLL_LOCK,\n    KC_PCMM    = KC_KP_COMMA,\n    KC_INT1    = KC_INTERNATIONAL_1,\n    KC_INT2    = KC_INTERNATIONAL_2,\n    KC_INT3    = KC_INTERNATIONAL_3,\n    KC_INT4    = KC_INTERNATIONAL_4,\n    KC_INT5    = KC_INTERNATIONAL_5,\n    KC_INT6    = KC_INTERNATIONAL_6,\n    KC_INT7    = KC_INTERNATIONAL_7,\n    KC_INT8    = KC_INTERNATIONAL_8,\n    KC_INT9    = KC_INTERNATIONAL_9,\n    KC_LNG1    = KC_LANGUAGE_1,\n    KC_LNG2    = KC_LANGUAGE_2,\n    KC_LNG3    = KC_LANGUAGE_3,\n    KC_LNG4    = KC_LANGUAGE_4,\n    KC_LNG5    = KC_LANGUAGE_5,\n    KC_LNG6    = KC_LANGUAGE_6,\n    KC_LNG7    = KC_LANGUAGE_7,\n    KC_LNG8    = KC_LANGUAGE_8,\n    KC_LNG9    = KC_LANGUAGE_9,\n    KC_ERAS    = KC_ALTERNATE_ERASE,\n    KC_SYRQ    = KC_SYSTEM_REQUEST,\n    KC_CNCL    = KC_CANCEL,\n    KC_CLR     = KC_CLEAR,\n    KC_PRIR    = KC_PRIOR,\n    KC_RETN    = KC_RETURN,\n    KC_SEPR    = KC_SEPARATOR,\n    KC_CLAG    = KC_CLEAR_AGAIN,\n    KC_CRSL    = KC_CRSEL,\n    KC_EXSL    = KC_EXSEL,\n    KC_PWR     = KC_SYSTEM_POWER,\n    KC_SLEP    = KC_SYSTEM_SLEEP,\n    KC_WAKE    = KC_SYSTEM_WAKE,\n    KC_MUTE    = KC_AUDIO_MUTE,\n    KC_VOLU    = KC_AUDIO_VOL_UP,\n    KC_VOLD    = KC_AUDIO_VOL_DOWN,\n    KC_MNXT    = KC_MEDIA_NEXT_TRACK,\n    KC_MPRV    = KC_MEDIA_PREV_TRACK,\n    KC_MSTP    = KC_MEDIA_STOP,\n    KC_MPLY    = KC_MEDIA_PLAY_PAUSE,\n    KC_MSEL    = KC_MEDIA_SELECT,\n    KC_EJCT    = KC_MEDIA_EJECT,\n    KC_CALC    = KC_CALCULATOR,\n    KC_MYCM    = KC_MY_COMPUTER,\n    KC_WSCH    = KC_WWW_SEARCH,\n    KC_WHOM    = KC_WWW_HOME,\n    KC_WBAK    = KC_WWW_BACK,\n    KC_WFWD    = KC_WWW_FORWARD,\n    KC_WSTP    = KC_WWW_STOP,\n    KC_WREF    = KC_WWW_REFRESH,\n    KC_WFAV    = KC_WWW_FAVORITES,\n    KC_MFFD    = KC_MEDIA_FAST_FORWARD,\n    KC_MRWD    = KC_MEDIA_REWIND,\n    KC_BRIU    = KC_BRIGHTNESS_UP,\n    KC_BRID    = KC_BRIGHTNESS_DOWN,\n    KC_CPNL    = KC_CONTROL_PANEL,\n    KC_ASST    = KC_ASSISTANT,\n    KC_MCTL    = KC_MISSION_CONTROL,\n    KC_LPAD    = KC_LAUNCHPAD,\n    MS_UP      = QK_MOUSE_CURSOR_UP,\n    MS_DOWN    = QK_MOUSE_CURSOR_DOWN,\n    MS_LEFT    = QK_MOUSE_CURSOR_LEFT,\n    MS_RGHT    = QK_MOUSE_CURSOR_RIGHT,\n    MS_BTN1    = QK_MOUSE_BUTTON_1,\n    MS_BTN2    = QK_MOUSE_BUTTON_2,\n    MS_BTN3    = QK_MOUSE_BUTTON_3,\n    MS_BTN4    = QK_MOUSE_BUTTON_4,\n    MS_BTN5    = QK_MOUSE_BUTTON_5,\n    MS_BTN6    = QK_MOUSE_BUTTON_6,\n    MS_BTN7    = QK_MOUSE_BUTTON_7,\n    MS_BTN8    = QK_MOUSE_BUTTON_8,\n    MS_WHLU    = QK_MOUSE_WHEEL_UP,\n    MS_WHLD    = QK_MOUSE_WHEEL_DOWN,\n    MS_WHLL    = QK_MOUSE_WHEEL_LEFT,\n    MS_WHLR    = QK_MOUSE_WHEEL_RIGHT,\n    MS_ACL0    = QK_MOUSE_ACCELERATION_0,\n    MS_ACL1    = QK_MOUSE_ACCELERATION_1,\n    MS_ACL2    = QK_MOUSE_ACCELERATION_2,\n    KC_LCTL    = KC_LEFT_CTRL,\n    KC_LSFT    = KC_LEFT_SHIFT,\n    KC_LALT    = KC_LEFT_ALT,\n    KC_LOPT    = KC_LEFT_ALT,\n    KC_LGUI    = KC_LEFT_GUI,\n    KC_LCMD    = KC_LEFT_GUI,\n    KC_LWIN    = KC_LEFT_GUI,\n    KC_RCTL    = KC_RIGHT_CTRL,\n    KC_RSFT    = KC_RIGHT_SHIFT,\n    KC_RALT    = KC_RIGHT_ALT,\n    KC_ROPT    = KC_RIGHT_ALT,\n    KC_ALGR    = KC_RIGHT_ALT,\n    KC_RGUI    = KC_RIGHT_GUI,\n    KC_RCMD    = KC_RIGHT_GUI,\n    KC_RWIN    = KC_RIGHT_GUI,\n    SH_TOGG    = QK_SWAP_HANDS_TOGGLE,\n    SH_TT      = QK_SWAP_HANDS_TAP_TOGGLE,\n    SH_MON     = QK_SWAP_HANDS_MOMENTARY_ON,\n    SH_MOFF    = QK_SWAP_HANDS_MOMENTARY_OFF,\n    SH_OFF     = QK_SWAP_HANDS_OFF,\n    SH_ON      = QK_SWAP_HANDS_ON,\n    SH_OS      = QK_SWAP_HANDS_ONE_SHOT,\n    CL_SWAP    = QK_MAGIC_SWAP_CONTROL_CAPS_LOCK,\n    CL_NORM    = QK_MAGIC_UNSWAP_CONTROL_CAPS_LOCK,\n    CL_TOGG    = QK_MAGIC_TOGGLE_CONTROL_CAPS_LOCK,\n    CL_CAPS    = QK_MAGIC_CAPS_LOCK_AS_CONTROL_OFF,\n    CL_CTRL    = QK_MAGIC_CAPS_LOCK_AS_CONTROL_ON,\n    AG_LSWP    = QK_MAGIC_SWAP_LALT_LGUI,\n    AG_LNRM    = QK_MAGIC_UNSWAP_LALT_LGUI,\n    AG_RSWP    = QK_MAGIC_SWAP_RALT_RGUI,\n    AG_RNRM    = QK_MAGIC_UNSWAP_RALT_RGUI,\n    GU_ON      = QK_MAGIC_GUI_ON,\n    GU_OFF     = QK_MAGIC_GUI_OFF,\n    GU_TOGG    = QK_MAGIC_TOGGLE_GUI,\n    GE_SWAP    = QK_MAGIC_SWAP_GRAVE_ESC,\n    GE_NORM    = QK_MAGIC_UNSWAP_GRAVE_ESC,\n    BS_SWAP    = QK_MAGIC_SWAP_BACKSLASH_BACKSPACE,\n    BS_NORM    = QK_MAGIC_UNSWAP_BACKSLASH_BACKSPACE,\n    BS_TOGG    = QK_MAGIC_TOGGLE_BACKSLASH_BACKSPACE,\n    NK_ON      = QK_MAGIC_NKRO_ON,\n    NK_OFF     = QK_MAGIC_NKRO_OFF,\n    NK_TOGG    = QK_MAGIC_TOGGLE_NKRO,\n    AG_SWAP    = QK_MAGIC_SWAP_ALT_GUI,\n    AG_NORM    = QK_MAGIC_UNSWAP_ALT_GUI,\n    AG_TOGG    = QK_MAGIC_TOGGLE_ALT_GUI,\n    CG_LSWP    = QK_MAGIC_SWAP_LCTL_LGUI,\n    CG_LNRM    = QK_MAGIC_UNSWAP_LCTL_LGUI,\n    CG_RSWP    = QK_MAGIC_SWAP_RCTL_RGUI,\n    CG_RNRM    = QK_MAGIC_UNSWAP_RCTL_RGUI,\n    CG_SWAP    = QK_MAGIC_SWAP_CTL_GUI,\n    CG_NORM    = QK_MAGIC_UNSWAP_CTL_GUI,\n    CG_TOGG    = QK_MAGIC_TOGGLE_CTL_GUI,\n    EH_LEFT    = QK_MAGIC_EE_HANDS_LEFT,\n    EH_RGHT    = QK_MAGIC_EE_HANDS_RIGHT,\n    EC_SWAP    = QK_MAGIC_SWAP_ESCAPE_CAPS_LOCK,\n    EC_NORM    = QK_MAGIC_UNSWAP_ESCAPE_CAPS_LOCK,\n    EC_TOGG    = QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK,\n    MI_ON      = QK_MIDI_ON,\n    MI_OFF     = QK_MIDI_OFF,\n    MI_TOGG    = QK_MIDI_TOGGLE,\n    MI_C       = QK_MIDI_NOTE_C_0,\n    MI_Cs      = QK_MIDI_NOTE_C_SHARP_0,\n    MI_Db      = QK_MIDI_NOTE_C_SHARP_0,\n    MI_D       = QK_MIDI_NOTE_D_0,\n    MI_Ds      = QK_MIDI_NOTE_D_SHARP_0,\n    MI_Eb      = QK_MIDI_NOTE_D_SHARP_0,\n    MI_E       = QK_MIDI_NOTE_E_0,\n    MI_F       = QK_MIDI_NOTE_F_0,\n    MI_Fs      = QK_MIDI_NOTE_F_SHARP_0,\n    MI_Gb      = QK_MIDI_NOTE_F_SHARP_0,\n    MI_G       = QK_MIDI_NOTE_G_0,\n    MI_Gs      = QK_MIDI_NOTE_G_SHARP_0,\n    MI_Ab      = QK_MIDI_NOTE_G_SHARP_0,\n    MI_A       = QK_MIDI_NOTE_A_0,\n    MI_As      = QK_MIDI_NOTE_A_SHARP_0,\n    MI_Bb      = QK_MIDI_NOTE_A_SHARP_0,\n    MI_B       = QK_MIDI_NOTE_B_0,\n    MI_C1      = QK_MIDI_NOTE_C_1,\n    MI_Cs1     = QK_MIDI_NOTE_C_SHARP_1,\n    MI_Db1     = QK_MIDI_NOTE_C_SHARP_1,\n    MI_D1      = QK_MIDI_NOTE_D_1,\n    MI_Ds1     = QK_MIDI_NOTE_D_SHARP_1,\n    MI_Eb1     = QK_MIDI_NOTE_D_SHARP_1,\n    MI_E1      = QK_MIDI_NOTE_E_1,\n    MI_F1      = QK_MIDI_NOTE_F_1,\n    MI_Fs1     = QK_MIDI_NOTE_F_SHARP_1,\n    MI_Gb1     = QK_MIDI_NOTE_F_SHARP_1,\n    MI_G1      = QK_MIDI_NOTE_G_1,\n    MI_Gs1     = QK_MIDI_NOTE_G_SHARP_1,\n    MI_Ab1     = QK_MIDI_NOTE_G_SHARP_1,\n    MI_A1      = QK_MIDI_NOTE_A_1,\n    MI_As1     = QK_MIDI_NOTE_A_SHARP_1,\n    MI_Bb1     = QK_MIDI_NOTE_A_SHARP_1,\n    MI_B1      = QK_MIDI_NOTE_B_1,\n    MI_C2      = QK_MIDI_NOTE_C_2,\n    MI_Cs2     = QK_MIDI_NOTE_C_SHARP_2,\n    MI_Db2     = QK_MIDI_NOTE_C_SHARP_2,\n    MI_D2      = QK_MIDI_NOTE_D_2,\n    MI_Ds2     = QK_MIDI_NOTE_D_SHARP_2,\n    MI_Eb2     = QK_MIDI_NOTE_D_SHARP_2,\n    MI_E2      = QK_MIDI_NOTE_E_2,\n    MI_F2      = QK_MIDI_NOTE_F_2,\n    MI_Fs2     = QK_MIDI_NOTE_F_SHARP_2,\n    MI_Gb2     = QK_MIDI_NOTE_F_SHARP_2,\n    MI_G2      = QK_MIDI_NOTE_G_2,\n    MI_Gs2     = QK_MIDI_NOTE_G_SHARP_2,\n    MI_Ab2     = QK_MIDI_NOTE_G_SHARP_2,\n    MI_A2      = QK_MIDI_NOTE_A_2,\n    MI_As2     = QK_MIDI_NOTE_A_SHARP_2,\n    MI_Bb2     = QK_MIDI_NOTE_A_SHARP_2,\n    MI_B2      = QK_MIDI_NOTE_B_2,\n    MI_C3      = QK_MIDI_NOTE_C_3,\n    MI_Cs3     = QK_MIDI_NOTE_C_SHARP_3,\n    MI_Db3     = QK_MIDI_NOTE_C_SHARP_3,\n    MI_D3      = QK_MIDI_NOTE_D_3,\n    MI_Ds3     = QK_MIDI_NOTE_D_SHARP_3,\n    MI_Eb3     = QK_MIDI_NOTE_D_SHARP_3,\n    MI_E3      = QK_MIDI_NOTE_E_3,\n    MI_F3      = QK_MIDI_NOTE_F_3,\n    MI_Fs3     = QK_MIDI_NOTE_F_SHARP_3,\n    MI_Gb3     = QK_MIDI_NOTE_F_SHARP_3,\n    MI_G3      = QK_MIDI_NOTE_G_3,\n    MI_Gs3     = QK_MIDI_NOTE_G_SHARP_3,\n    MI_Ab3     = QK_MIDI_NOTE_G_SHARP_3,\n    MI_A3      = QK_MIDI_NOTE_A_3,\n    MI_As3     = QK_MIDI_NOTE_A_SHARP_3,\n    MI_Bb3     = QK_MIDI_NOTE_A_SHARP_3,\n    MI_B3      = QK_MIDI_NOTE_B_3,\n    MI_C4      = QK_MIDI_NOTE_C_4,\n    MI_Cs4     = QK_MIDI_NOTE_C_SHARP_4,\n    MI_Db4     = QK_MIDI_NOTE_C_SHARP_4,\n    MI_D4      = QK_MIDI_NOTE_D_4,\n    MI_Ds4     = QK_MIDI_NOTE_D_SHARP_4,\n    MI_Eb4     = QK_MIDI_NOTE_D_SHARP_4,\n    MI_E4      = QK_MIDI_NOTE_E_4,\n    MI_F4      = QK_MIDI_NOTE_F_4,\n    MI_Fs4     = QK_MIDI_NOTE_F_SHARP_4,\n    MI_Gb4     = QK_MIDI_NOTE_F_SHARP_4,\n    MI_G4      = QK_MIDI_NOTE_G_4,\n    MI_Gs4     = QK_MIDI_NOTE_G_SHARP_4,\n    MI_Ab4     = QK_MIDI_NOTE_G_SHARP_4,\n    MI_A4      = QK_MIDI_NOTE_A_4,\n    MI_As4     = QK_MIDI_NOTE_A_SHARP_4,\n    MI_Bb4     = QK_MIDI_NOTE_A_SHARP_4,\n    MI_B4      = QK_MIDI_NOTE_B_4,\n    MI_C5      = QK_MIDI_NOTE_C_5,\n    MI_Cs5     = QK_MIDI_NOTE_C_SHARP_5,\n    MI_Db5     = QK_MIDI_NOTE_C_SHARP_5,\n    MI_D5      = QK_MIDI_NOTE_D_5,\n    MI_Ds5     = QK_MIDI_NOTE_D_SHARP_5,\n    MI_Eb5     = QK_MIDI_NOTE_D_SHARP_5,\n    MI_E5      = QK_MIDI_NOTE_E_5,\n    MI_F5      = QK_MIDI_NOTE_F_5,\n    MI_Fs5     = QK_MIDI_NOTE_F_SHARP_5,\n    MI_Gb5     = QK_MIDI_NOTE_F_SHARP_5,\n    MI_G5      = QK_MIDI_NOTE_G_5,\n    MI_Gs5     = QK_MIDI_NOTE_G_SHARP_5,\n    MI_Ab5     = QK_MIDI_NOTE_G_SHARP_5,\n    MI_A5      = QK_MIDI_NOTE_A_5,\n    MI_As5     = QK_MIDI_NOTE_A_SHARP_5,\n    MI_Bb5     = QK_MIDI_NOTE_A_SHARP_5,\n    MI_B5      = QK_MIDI_NOTE_B_5,\n    MI_OCN2    = QK_MIDI_OCTAVE_N2,\n    MI_OCN1    = QK_MIDI_OCTAVE_N1,\n    MI_OC0     = QK_MIDI_OCTAVE_0,\n    MI_OC1     = QK_MIDI_OCTAVE_1,\n    MI_OC2     = QK_MIDI_OCTAVE_2,\n    MI_OC3     = QK_MIDI_OCTAVE_3,\n    MI_OC4     = QK_MIDI_OCTAVE_4,\n    MI_OC5     = QK_MIDI_OCTAVE_5,\n    MI_OC6     = QK_MIDI_OCTAVE_6,\n    MI_OC7     = QK_MIDI_OCTAVE_7,\n    MI_OCTD    = QK_MIDI_OCTAVE_DOWN,\n    MI_OCTU    = QK_MIDI_OCTAVE_UP,\n    MI_TRN6    = QK_MIDI_TRANSPOSE_N6,\n    MI_TRN5    = QK_MIDI_TRANSPOSE_N5,\n    MI_TRN4    = QK_MIDI_TRANSPOSE_N4,\n    MI_TRN3    = QK_MIDI_TRANSPOSE_N3,\n    MI_TRN2    = QK_MIDI_TRANSPOSE_N2,\n    MI_TRN1    = QK_MIDI_TRANSPOSE_N1,\n    MI_TR0     = QK_MIDI_TRANSPOSE_0,\n    MI_TR1     = QK_MIDI_TRANSPOSE_1,\n    MI_TR2     = QK_MIDI_TRANSPOSE_2,\n    MI_TR3     = QK_MIDI_TRANSPOSE_3,\n    MI_TR4     = QK_MIDI_TRANSPOSE_4,\n    MI_TR5     = QK_MIDI_TRANSPOSE_5,\n    MI_TR6     = QK_MIDI_TRANSPOSE_6,\n    MI_TRSD    = QK_MIDI_TRANSPOSE_DOWN,\n    MI_TRSU    = QK_MIDI_TRANSPOSE_UP,\n    MI_VL0     = QK_MIDI_VELOCITY_0,\n    MI_VL1     = QK_MIDI_VELOCITY_1,\n    MI_VL2     = QK_MIDI_VELOCITY_2,\n    MI_VL3     = QK_MIDI_VELOCITY_3,\n    MI_VL4     = QK_MIDI_VELOCITY_4,\n    MI_VL5     = QK_MIDI_VELOCITY_5,\n    MI_VL6     = QK_MIDI_VELOCITY_6,\n    MI_VL7     = QK_MIDI_VELOCITY_7,\n    MI_VL8     = QK_MIDI_VELOCITY_8,\n    MI_VL9     = QK_MIDI_VELOCITY_9,\n    MI_VL10    = QK_MIDI_VELOCITY_10,\n    MI_VELD    = QK_MIDI_VELOCITY_DOWN,\n    MI_VELU    = QK_MIDI_VELOCITY_UP,\n    MI_CH1     = QK_MIDI_CHANNEL_1,\n    MI_CH2     = QK_MIDI_CHANNEL_2,\n    MI_CH3     = QK_MIDI_CHANNEL_3,\n    MI_CH4     = QK_MIDI_CHANNEL_4,\n    MI_CH5     = QK_MIDI_CHANNEL_5,\n    MI_CH6     = QK_MIDI_CHANNEL_6,\n    MI_CH7     = QK_MIDI_CHANNEL_7,\n    MI_CH8     = QK_MIDI_CHANNEL_8,\n    MI_CH9     = QK_MIDI_CHANNEL_9,\n    MI_CH10    = QK_MIDI_CHANNEL_10,\n    MI_CH11    = QK_MIDI_CHANNEL_11,\n    MI_CH12    = QK_MIDI_CHANNEL_12,\n    MI_CH13    = QK_MIDI_CHANNEL_13,\n    MI_CH14    = QK_MIDI_CHANNEL_14,\n    MI_CH15    = QK_MIDI_CHANNEL_15,\n    MI_CH16    = QK_MIDI_CHANNEL_16,\n    MI_CHND    = QK_MIDI_CHANNEL_DOWN,\n    MI_CHNU    = QK_MIDI_CHANNEL_UP,\n    MI_AOFF    = QK_MIDI_ALL_NOTES_OFF,\n    MI_SUST    = QK_MIDI_SUSTAIN,\n    MI_PORT    = QK_MIDI_PORTAMENTO,\n    MI_SOST    = QK_MIDI_SOSTENUTO,\n    MI_SOFT    = QK_MIDI_SOFT,\n    MI_LEG     = QK_MIDI_LEGATO,\n    MI_MOD     = QK_MIDI_MODULATION,\n    MI_MODD    = QK_MIDI_MODULATION_SPEED_DOWN,\n    MI_MODU    = QK_MIDI_MODULATION_SPEED_UP,\n    MI_BNDD    = QK_MIDI_PITCH_BEND_DOWN,\n    MI_BNDU    = QK_MIDI_PITCH_BEND_UP,\n    SQ_ON      = QK_SEQUENCER_ON,\n    SQ_OFF     = QK_SEQUENCER_OFF,\n    SQ_TOGG    = QK_SEQUENCER_TOGGLE,\n    SQ_TMPD    = QK_SEQUENCER_TEMPO_DOWN,\n    SQ_TMPU    = QK_SEQUENCER_TEMPO_UP,\n    SQ_RESD    = QK_SEQUENCER_RESOLUTION_DOWN,\n    SQ_RESU    = QK_SEQUENCER_RESOLUTION_UP,\n    SQ_SALL    = QK_SEQUENCER_STEPS_ALL,\n    SQ_SCLR    = QK_SEQUENCER_STEPS_CLEAR,\n    JS_0       = QK_JOYSTICK_BUTTON_0,\n    JS_1       = QK_JOYSTICK_BUTTON_1,\n    JS_2       = QK_JOYSTICK_BUTTON_2,\n    JS_3       = QK_JOYSTICK_BUTTON_3,\n    JS_4       = QK_JOYSTICK_BUTTON_4,\n    JS_5       = QK_JOYSTICK_BUTTON_5,\n    JS_6       = QK_JOYSTICK_BUTTON_6,\n    JS_7       = QK_JOYSTICK_BUTTON_7,\n    JS_8       = QK_JOYSTICK_BUTTON_8,\n    JS_9       = QK_JOYSTICK_BUTTON_9,\n    JS_10      = QK_JOYSTICK_BUTTON_10,\n    JS_11      = QK_JOYSTICK_BUTTON_11,\n    JS_12      = QK_JOYSTICK_BUTTON_12,\n    JS_13      = QK_JOYSTICK_BUTTON_13,\n    JS_14      = QK_JOYSTICK_BUTTON_14,\n    JS_15      = QK_JOYSTICK_BUTTON_15,\n    JS_16      = QK_JOYSTICK_BUTTON_16,\n    JS_17      = QK_JOYSTICK_BUTTON_17,\n    JS_18      = QK_JOYSTICK_BUTTON_18,\n    JS_19      = QK_JOYSTICK_BUTTON_19,\n    JS_20      = QK_JOYSTICK_BUTTON_20,\n    JS_21      = QK_JOYSTICK_BUTTON_21,\n    JS_22      = QK_JOYSTICK_BUTTON_22,\n    JS_23      = QK_JOYSTICK_BUTTON_23,\n    JS_24      = QK_JOYSTICK_BUTTON_24,\n    JS_25      = QK_JOYSTICK_BUTTON_25,\n    JS_26      = QK_JOYSTICK_BUTTON_26,\n    JS_27      = QK_JOYSTICK_BUTTON_27,\n    JS_28      = QK_JOYSTICK_BUTTON_28,\n    JS_29      = QK_JOYSTICK_BUTTON_29,\n    JS_30      = QK_JOYSTICK_BUTTON_30,\n    JS_31      = QK_JOYSTICK_BUTTON_31,\n    PB_1       = QK_PROGRAMMABLE_BUTTON_1,\n    PB_2       = QK_PROGRAMMABLE_BUTTON_2,\n    PB_3       = QK_PROGRAMMABLE_BUTTON_3,\n    PB_4       = QK_PROGRAMMABLE_BUTTON_4,\n    PB_5       = QK_PROGRAMMABLE_BUTTON_5,\n    PB_6       = QK_PROGRAMMABLE_BUTTON_6,\n    PB_7       = QK_PROGRAMMABLE_BUTTON_7,\n    PB_8       = QK_PROGRAMMABLE_BUTTON_8,\n    PB_9       = QK_PROGRAMMABLE_BUTTON_9,\n    PB_10      = QK_PROGRAMMABLE_BUTTON_10,\n    PB_11      = QK_PROGRAMMABLE_BUTTON_11,\n    PB_12      = QK_PROGRAMMABLE_BUTTON_12,\n    PB_13      = QK_PROGRAMMABLE_BUTTON_13,\n    PB_14      = QK_PROGRAMMABLE_BUTTON_14,\n    PB_15      = QK_PROGRAMMABLE_BUTTON_15,\n    PB_16      = QK_PROGRAMMABLE_BUTTON_16,\n    PB_17      = QK_PROGRAMMABLE_BUTTON_17,\n    PB_18      = QK_PROGRAMMABLE_BUTTON_18,\n    PB_19      = QK_PROGRAMMABLE_BUTTON_19,\n    PB_20      = QK_PROGRAMMABLE_BUTTON_20,\n    PB_21      = QK_PROGRAMMABLE_BUTTON_21,\n    PB_22      = QK_PROGRAMMABLE_BUTTON_22,\n    PB_23      = QK_PROGRAMMABLE_BUTTON_23,\n    PB_24      = QK_PROGRAMMABLE_BUTTON_24,\n    PB_25      = QK_PROGRAMMABLE_BUTTON_25,\n    PB_26      = QK_PROGRAMMABLE_BUTTON_26,\n    PB_27      = QK_PROGRAMMABLE_BUTTON_27,\n    PB_28      = QK_PROGRAMMABLE_BUTTON_28,\n    PB_29      = QK_PROGRAMMABLE_BUTTON_29,\n    PB_30      = QK_PROGRAMMABLE_BUTTON_30,\n    PB_31      = QK_PROGRAMMABLE_BUTTON_31,\n    PB_32      = QK_PROGRAMMABLE_BUTTON_32,\n    AU_ON      = QK_AUDIO_ON,\n    AU_OFF     = QK_AUDIO_OFF,\n    AU_TOGG    = QK_AUDIO_TOGGLE,\n    CK_TOGG    = QK_AUDIO_CLICKY_TOGGLE,\n    CK_ON      = QK_AUDIO_CLICKY_ON,\n    CK_OFF     = QK_AUDIO_CLICKY_OFF,\n    CK_UP      = QK_AUDIO_CLICKY_UP,\n    CK_DOWN    = QK_AUDIO_CLICKY_DOWN,\n    CK_RST     = QK_AUDIO_CLICKY_RESET,\n    MU_ON      = QK_MUSIC_ON,\n    MU_OFF     = QK_MUSIC_OFF,\n    MU_TOGG    = QK_MUSIC_TOGGLE,\n    MU_NEXT    = QK_MUSIC_MODE_NEXT,\n    AU_NEXT    = QK_AUDIO_VOICE_NEXT,\n    AU_PREV    = QK_AUDIO_VOICE_PREVIOUS,\n    MC_0       = QK_MACRO_0,\n    MC_1       = QK_MACRO_1,\n    MC_2       = QK_MACRO_2,\n    MC_3       = QK_MACRO_3,\n    MC_4       = QK_MACRO_4,\n    MC_5       = QK_MACRO_5,\n    MC_6       = QK_MACRO_6,\n    MC_7       = QK_MACRO_7,\n    MC_8       = QK_MACRO_8,\n    MC_9       = QK_MACRO_9,\n    MC_10      = QK_MACRO_10,\n    MC_11      = QK_MACRO_11,\n    MC_12      = QK_MACRO_12,\n    MC_13      = QK_MACRO_13,\n    MC_14      = QK_MACRO_14,\n    MC_15      = QK_MACRO_15,\n    MC_16      = QK_MACRO_16,\n    MC_17      = QK_MACRO_17,\n    MC_18      = QK_MACRO_18,\n    MC_19      = QK_MACRO_19,\n    MC_20      = QK_MACRO_20,\n    MC_21      = QK_MACRO_21,\n    MC_22      = QK_MACRO_22,\n    MC_23      = QK_MACRO_23,\n    MC_24      = QK_MACRO_24,\n    MC_25      = QK_MACRO_25,\n    MC_26      = QK_MACRO_26,\n    MC_27      = QK_MACRO_27,\n    MC_28      = QK_MACRO_28,\n    MC_29      = QK_MACRO_29,\n    MC_30      = QK_MACRO_30,\n    MC_31      = QK_MACRO_31,\n    OU_AUTO    = QK_OUTPUT_AUTO,\n    OU_NEXT    = QK_OUTPUT_NEXT,\n    OU_PREV    = QK_OUTPUT_PREV,\n    OU_NONE    = QK_OUTPUT_NONE,\n    OU_USB     = QK_OUTPUT_USB,\n    OU_2P4G    = QK_OUTPUT_2P4GHZ,\n    OU_BT      = QK_OUTPUT_BLUETOOTH,\n    BT_NEXT    = QK_BLUETOOTH_PROFILE_NEXT,\n    BT_PREV    = QK_BLUETOOTH_PROFILE_PREV,\n    BT_UNPR    = QK_BLUETOOTH_UNPAIR,\n    BT_PRF1    = QK_BLUETOOTH_PROFILE1,\n    BT_PRF2    = QK_BLUETOOTH_PROFILE2,\n    BT_PRF3    = QK_BLUETOOTH_PROFILE3,\n    BT_PRF4    = QK_BLUETOOTH_PROFILE4,\n    BT_PRF5    = QK_BLUETOOTH_PROFILE5,\n    BL_ON      = QK_BACKLIGHT_ON,\n    BL_OFF     = QK_BACKLIGHT_OFF,\n    BL_TOGG    = QK_BACKLIGHT_TOGGLE,\n    BL_DOWN    = QK_BACKLIGHT_DOWN,\n    BL_UP      = QK_BACKLIGHT_UP,\n    BL_STEP    = QK_BACKLIGHT_STEP,\n    BL_BRTG    = QK_BACKLIGHT_TOGGLE_BREATHING,\n    LM_ON      = QK_LED_MATRIX_ON,\n    LM_OFF     = QK_LED_MATRIX_OFF,\n    LM_TOGG    = QK_LED_MATRIX_TOGGLE,\n    LM_NEXT    = QK_LED_MATRIX_MODE_NEXT,\n    LM_PREV    = QK_LED_MATRIX_MODE_PREVIOUS,\n    LM_BRIU    = QK_LED_MATRIX_BRIGHTNESS_UP,\n    LM_BRID    = QK_LED_MATRIX_BRIGHTNESS_DOWN,\n    LM_SPDU    = QK_LED_MATRIX_SPEED_UP,\n    LM_SPDD    = QK_LED_MATRIX_SPEED_DOWN,\n    UG_TOGG    = QK_UNDERGLOW_TOGGLE,\n    UG_NEXT    = QK_UNDERGLOW_MODE_NEXT,\n    UG_PREV    = QK_UNDERGLOW_MODE_PREVIOUS,\n    UG_HUEU    = QK_UNDERGLOW_HUE_UP,\n    UG_HUED    = QK_UNDERGLOW_HUE_DOWN,\n    UG_SATU    = QK_UNDERGLOW_SATURATION_UP,\n    UG_SATD    = QK_UNDERGLOW_SATURATION_DOWN,\n    UG_VALU    = QK_UNDERGLOW_VALUE_UP,\n    UG_VALD    = QK_UNDERGLOW_VALUE_DOWN,\n    UG_SPDU    = QK_UNDERGLOW_SPEED_UP,\n    UG_SPDD    = QK_UNDERGLOW_SPEED_DOWN,\n    RGB_M_P    = RGB_MODE_PLAIN,\n    RGB_M_B    = RGB_MODE_BREATHE,\n    RGB_M_R    = RGB_MODE_RAINBOW,\n    RGB_M_SW   = RGB_MODE_SWIRL,\n    RGB_M_SN   = RGB_MODE_SNAKE,\n    RGB_M_K    = RGB_MODE_KNIGHT,\n    RGB_M_X    = RGB_MODE_XMAS,\n    RGB_M_G    = RGB_MODE_GRADIENT,\n    RGB_M_T    = RGB_MODE_RGBTEST,\n    RGB_M_TW   = RGB_MODE_TWINKLE,\n    RM_ON      = QK_RGB_MATRIX_ON,\n    RM_OFF     = QK_RGB_MATRIX_OFF,\n    RM_TOGG    = QK_RGB_MATRIX_TOGGLE,\n    RM_NEXT    = QK_RGB_MATRIX_MODE_NEXT,\n    RM_PREV    = QK_RGB_MATRIX_MODE_PREVIOUS,\n    RM_HUEU    = QK_RGB_MATRIX_HUE_UP,\n    RM_HUED    = QK_RGB_MATRIX_HUE_DOWN,\n    RM_SATU    = QK_RGB_MATRIX_SATURATION_UP,\n    RM_SATD    = QK_RGB_MATRIX_SATURATION_DOWN,\n    RM_VALU    = QK_RGB_MATRIX_VALUE_UP,\n    RM_VALD    = QK_RGB_MATRIX_VALUE_DOWN,\n    RM_SPDU    = QK_RGB_MATRIX_SPEED_UP,\n    RM_SPDD    = QK_RGB_MATRIX_SPEED_DOWN,\n    QK_BOOT    = QK_BOOTLOADER,\n    QK_RBT     = QK_REBOOT,\n    DB_TOGG    = QK_DEBUG_TOGGLE,\n    EE_CLR     = QK_CLEAR_EEPROM,\n    AS_DOWN    = QK_AUTO_SHIFT_DOWN,\n    AS_UP      = QK_AUTO_SHIFT_UP,\n    AS_RPT     = QK_AUTO_SHIFT_REPORT,\n    AS_ON      = QK_AUTO_SHIFT_ON,\n    AS_OFF     = QK_AUTO_SHIFT_OFF,\n    AS_TOGG    = QK_AUTO_SHIFT_TOGGLE,\n    QK_GESC    = QK_GRAVE_ESCAPE,\n    VK_TOGG    = QK_VELOCIKEY_TOGGLE,\n    SC_LCPO    = QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN,\n    SC_RCPC    = QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE,\n    SC_LSPO    = QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN,\n    SC_RSPC    = QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE,\n    SC_LAPO    = QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN,\n    SC_RAPC    = QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE,\n    SC_SENT    = QK_SPACE_CADET_RIGHT_SHIFT_ENTER,\n    UC_NEXT    = QK_UNICODE_MODE_NEXT,\n    UC_PREV    = QK_UNICODE_MODE_PREVIOUS,\n    UC_MAC     = QK_UNICODE_MODE_MACOS,\n    UC_LINX    = QK_UNICODE_MODE_LINUX,\n    UC_WIN     = QK_UNICODE_MODE_WINDOWS,\n    UC_BSD     = QK_UNICODE_MODE_BSD,\n    UC_WINC    = QK_UNICODE_MODE_WINCOMPOSE,\n    UC_EMAC    = QK_UNICODE_MODE_EMACS,\n    HF_ON      = QK_HAPTIC_ON,\n    HF_OFF     = QK_HAPTIC_OFF,\n    HF_TOGG    = QK_HAPTIC_TOGGLE,\n    HF_RST     = QK_HAPTIC_RESET,\n    HF_FDBK    = QK_HAPTIC_FEEDBACK_TOGGLE,\n    HF_BUZZ    = QK_HAPTIC_BUZZ_TOGGLE,\n    HF_NEXT    = QK_HAPTIC_MODE_NEXT,\n    HF_PREV    = QK_HAPTIC_MODE_PREVIOUS,\n    HF_CONT    = QK_HAPTIC_CONTINUOUS_TOGGLE,\n    HF_CONU    = QK_HAPTIC_CONTINUOUS_UP,\n    HF_COND    = QK_HAPTIC_CONTINUOUS_DOWN,\n    HF_DWLU    = QK_HAPTIC_DWELL_UP,\n    HF_DWLD    = QK_HAPTIC_DWELL_DOWN,\n    CM_ON      = QK_COMBO_ON,\n    CM_OFF     = QK_COMBO_OFF,\n    CM_TOGG    = QK_COMBO_TOGGLE,\n    DM_REC1    = QK_DYNAMIC_MACRO_RECORD_START_1,\n    DM_REC2    = QK_DYNAMIC_MACRO_RECORD_START_2,\n    DM_RSTP    = QK_DYNAMIC_MACRO_RECORD_STOP,\n    DM_PLY1    = QK_DYNAMIC_MACRO_PLAY_1,\n    DM_PLY2    = QK_DYNAMIC_MACRO_PLAY_2,\n    QK_LEAD    = QK_LEADER,\n    OS_ON      = QK_ONE_SHOT_ON,\n    OS_OFF     = QK_ONE_SHOT_OFF,\n    OS_TOGG    = QK_ONE_SHOT_TOGGLE,\n    KO_TOGG    = QK_KEY_OVERRIDE_TOGGLE,\n    KO_ON      = QK_KEY_OVERRIDE_ON,\n    KO_OFF     = QK_KEY_OVERRIDE_OFF,\n    SE_LOCK    = QK_SECURE_LOCK,\n    SE_UNLK    = QK_SECURE_UNLOCK,\n    SE_TOGG    = QK_SECURE_TOGGLE,\n    SE_REQ     = QK_SECURE_REQUEST,\n    DT_PRNT    = QK_DYNAMIC_TAPPING_TERM_PRINT,\n    DT_UP      = QK_DYNAMIC_TAPPING_TERM_UP,\n    DT_DOWN    = QK_DYNAMIC_TAPPING_TERM_DOWN,\n    CW_TOGG    = QK_CAPS_WORD_TOGGLE,\n    AC_ON      = QK_AUTOCORRECT_ON,\n    AC_OFF     = QK_AUTOCORRECT_OFF,\n    AC_TOGG    = QK_AUTOCORRECT_TOGGLE,\n    TL_LOWR    = QK_TRI_LAYER_LOWER,\n    TL_UPPR    = QK_TRI_LAYER_UPPER,\n    QK_REP     = QK_REPEAT_KEY,\n    QK_AREP    = QK_ALT_REPEAT_KEY,\n    QK_LLCK    = QK_LAYER_LOCK,\n};\n\n// Range Helpers\n#define IS_QK_BASIC(code) ((code) >= QK_BASIC && (code) <= QK_BASIC_MAX)\n#define IS_QK_MODS(code) ((code) >= QK_MODS && (code) <= QK_MODS_MAX)\n#define IS_QK_MOD_TAP(code) ((code) >= QK_MOD_TAP && (code) <= QK_MOD_TAP_MAX)\n#define IS_QK_LAYER_TAP(code) ((code) >= QK_LAYER_TAP && (code) <= QK_LAYER_TAP_MAX)\n#define IS_QK_LAYER_MOD(code) ((code) >= QK_LAYER_MOD && (code) <= QK_LAYER_MOD_MAX)\n#define IS_QK_TO(code) ((code) >= QK_TO && (code) <= QK_TO_MAX)\n#define IS_QK_MOMENTARY(code) ((code) >= QK_MOMENTARY && (code) <= QK_MOMENTARY_MAX)\n#define IS_QK_DEF_LAYER(code) ((code) >= QK_DEF_LAYER && (code) <= QK_DEF_LAYER_MAX)\n#define IS_QK_TOGGLE_LAYER(code) ((code) >= QK_TOGGLE_LAYER && (code) <= QK_TOGGLE_LAYER_MAX)\n#define IS_QK_ONE_SHOT_LAYER(code) ((code) >= QK_ONE_SHOT_LAYER && (code) <= QK_ONE_SHOT_LAYER_MAX)\n#define IS_QK_ONE_SHOT_MOD(code) ((code) >= QK_ONE_SHOT_MOD && (code) <= QK_ONE_SHOT_MOD_MAX)\n#define IS_QK_LAYER_TAP_TOGGLE(code) ((code) >= QK_LAYER_TAP_TOGGLE && (code) <= QK_LAYER_TAP_TOGGLE_MAX)\n#define IS_QK_PERSISTENT_DEF_LAYER(code) ((code) >= QK_PERSISTENT_DEF_LAYER && (code) <= QK_PERSISTENT_DEF_LAYER_MAX)\n#define IS_QK_SWAP_HANDS(code) ((code) >= QK_SWAP_HANDS && (code) <= QK_SWAP_HANDS_MAX)\n#define IS_QK_TAP_DANCE(code) ((code) >= QK_TAP_DANCE && (code) <= QK_TAP_DANCE_MAX)\n#define IS_QK_MAGIC(code) ((code) >= QK_MAGIC && (code) <= QK_MAGIC_MAX)\n#define IS_QK_MIDI(code) ((code) >= QK_MIDI && (code) <= QK_MIDI_MAX)\n#define IS_QK_SEQUENCER(code) ((code) >= QK_SEQUENCER && (code) <= QK_SEQUENCER_MAX)\n#define IS_QK_JOYSTICK(code) ((code) >= QK_JOYSTICK && (code) <= QK_JOYSTICK_MAX)\n#define IS_QK_PROGRAMMABLE_BUTTON(code) ((code) >= QK_PROGRAMMABLE_BUTTON && (code) <= QK_PROGRAMMABLE_BUTTON_MAX)\n#define IS_QK_AUDIO(code) ((code) >= QK_AUDIO && (code) <= QK_AUDIO_MAX)\n#define IS_QK_STENO(code) ((code) >= QK_STENO && (code) <= QK_STENO_MAX)\n#define IS_QK_MACRO(code) ((code) >= QK_MACRO && (code) <= QK_MACRO_MAX)\n#define IS_QK_CONNECTION(code) ((code) >= QK_CONNECTION && (code) <= QK_CONNECTION_MAX)\n#define IS_QK_COMMUNITY_MODULE(code) ((code) >= QK_COMMUNITY_MODULE && (code) <= QK_COMMUNITY_MODULE_MAX)\n#define IS_QK_LIGHTING(code) ((code) >= QK_LIGHTING && (code) <= QK_LIGHTING_MAX)\n#define IS_QK_QUANTUM(code) ((code) >= QK_QUANTUM && (code) <= QK_QUANTUM_MAX)\n#define IS_QK_KB(code) ((code) >= QK_KB && (code) <= QK_KB_MAX)\n#define IS_QK_USER(code) ((code) >= QK_USER && (code) <= QK_USER_MAX)\n#define IS_QK_UNICODEMAP(code) ((code) >= QK_UNICODEMAP && (code) <= QK_UNICODEMAP_MAX)\n#define IS_QK_UNICODE(code) ((code) >= QK_UNICODE && (code) <= QK_UNICODE_MAX)\n#define IS_QK_UNICODEMAP_PAIR(code) ((code) >= QK_UNICODEMAP_PAIR && (code) <= QK_UNICODEMAP_PAIR_MAX)\n\n// Group Helpers\n#define IS_INTERNAL_KEYCODE(code) ((code) >= KC_NO && (code) <= KC_TRANSPARENT)\n#define IS_BASIC_KEYCODE(code) ((code) >= KC_A && (code) <= KC_EXSEL)\n#define IS_SYSTEM_KEYCODE(code) ((code) >= KC_SYSTEM_POWER && (code) <= KC_SYSTEM_WAKE)\n#define IS_CONSUMER_KEYCODE(code) ((code) >= KC_AUDIO_MUTE && (code) <= KC_LAUNCHPAD)\n#define IS_MOUSE_KEYCODE(code) ((code) >= QK_MOUSE_CURSOR_UP && (code) <= QK_MOUSE_ACCELERATION_2)\n#define IS_MODIFIER_KEYCODE(code) ((code) >= KC_LEFT_CTRL && (code) <= KC_RIGHT_GUI)\n#define IS_SWAP_HANDS_KEYCODE(code) ((code) >= QK_SWAP_HANDS_TOGGLE && (code) <= QK_SWAP_HANDS_ONE_SHOT)\n#define IS_MAGIC_KEYCODE(code) ((code) >= QK_MAGIC_SWAP_CONTROL_CAPS_LOCK && (code) <= QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK)\n#define IS_MIDI_KEYCODE(code) ((code) >= QK_MIDI_ON && (code) <= QK_MIDI_PITCH_BEND_UP)\n#define IS_SEQUENCER_KEYCODE(code) ((code) >= QK_SEQUENCER_ON && (code) <= QK_SEQUENCER_STEPS_CLEAR)\n#define IS_JOYSTICK_KEYCODE(code) ((code) >= QK_JOYSTICK_BUTTON_0 && (code) <= QK_JOYSTICK_BUTTON_31)\n#define IS_PROGRAMMABLE_BUTTON_KEYCODE(code) ((code) >= QK_PROGRAMMABLE_BUTTON_1 && (code) <= QK_PROGRAMMABLE_BUTTON_32)\n#define IS_AUDIO_KEYCODE(code) ((code) >= QK_AUDIO_ON && (code) <= QK_AUDIO_VOICE_PREVIOUS)\n#define IS_STENO_KEYCODE(code) ((code) >= QK_STENO_BOLT && (code) <= QK_STENO_COMB_MAX)\n#define IS_MACRO_KEYCODE(code) ((code) >= QK_MACRO_0 && (code) <= QK_MACRO_31)\n#define IS_CONNECTION_KEYCODE(code) ((code) >= QK_OUTPUT_AUTO && (code) <= QK_BLUETOOTH_PROFILE5)\n#define IS_BACKLIGHT_KEYCODE(code) ((code) >= QK_BACKLIGHT_ON && (code) <= QK_BACKLIGHT_TOGGLE_BREATHING)\n#define IS_LED_MATRIX_KEYCODE(code) ((code) >= QK_LED_MATRIX_ON && (code) <= QK_LED_MATRIX_SPEED_DOWN)\n#define IS_UNDERGLOW_KEYCODE(code) ((code) >= QK_UNDERGLOW_TOGGLE && (code) <= QK_UNDERGLOW_SPEED_DOWN)\n#define IS_RGB_KEYCODE(code) ((code) >= RGB_MODE_PLAIN && (code) <= RGB_MODE_TWINKLE)\n#define IS_RGB_MATRIX_KEYCODE(code) ((code) >= QK_RGB_MATRIX_ON && (code) <= QK_RGB_MATRIX_SPEED_DOWN)\n#define IS_QUANTUM_KEYCODE(code) ((code) >= QK_BOOTLOADER && (code) <= QK_LAYER_LOCK)\n#define IS_KB_KEYCODE(code) ((code) >= QK_KB_0 && (code) <= QK_KB_31)\n#define IS_USER_KEYCODE(code) ((code) >= QK_USER_0 && (code) <= QK_USER_31)\n\n// Switch statement Helpers\n#define INTERNAL_KEYCODE_RANGE              KC_NO ... KC_TRANSPARENT\n#define BASIC_KEYCODE_RANGE                 KC_A ... KC_EXSEL\n#define SYSTEM_KEYCODE_RANGE                KC_SYSTEM_POWER ... KC_SYSTEM_WAKE\n#define CONSUMER_KEYCODE_RANGE              KC_AUDIO_MUTE ... KC_LAUNCHPAD\n#define MOUSE_KEYCODE_RANGE                 QK_MOUSE_CURSOR_UP ... QK_MOUSE_ACCELERATION_2\n#define MODIFIER_KEYCODE_RANGE              KC_LEFT_CTRL ... KC_RIGHT_GUI\n#define SWAP_HANDS_KEYCODE_RANGE            QK_SWAP_HANDS_TOGGLE ... QK_SWAP_HANDS_ONE_SHOT\n#define MAGIC_KEYCODE_RANGE                 QK_MAGIC_SWAP_CONTROL_CAPS_LOCK ... QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK\n#define MIDI_KEYCODE_RANGE                  QK_MIDI_ON ... QK_MIDI_PITCH_BEND_UP\n#define SEQUENCER_KEYCODE_RANGE             QK_SEQUENCER_ON ... QK_SEQUENCER_STEPS_CLEAR\n#define JOYSTICK_KEYCODE_RANGE              QK_JOYSTICK_BUTTON_0 ... QK_JOYSTICK_BUTTON_31\n#define PROGRAMMABLE_BUTTON_KEYCODE_RANGE   QK_PROGRAMMABLE_BUTTON_1 ... QK_PROGRAMMABLE_BUTTON_32\n#define AUDIO_KEYCODE_RANGE                 QK_AUDIO_ON ... QK_AUDIO_VOICE_PREVIOUS\n#define STENO_KEYCODE_RANGE                 QK_STENO_BOLT ... QK_STENO_COMB_MAX\n#define MACRO_KEYCODE_RANGE                 QK_MACRO_0 ... QK_MACRO_31\n#define CONNECTION_KEYCODE_RANGE            QK_OUTPUT_AUTO ... QK_BLUETOOTH_PROFILE5\n#define BACKLIGHT_KEYCODE_RANGE             QK_BACKLIGHT_ON ... QK_BACKLIGHT_TOGGLE_BREATHING\n#define LED_MATRIX_KEYCODE_RANGE            QK_LED_MATRIX_ON ... QK_LED_MATRIX_SPEED_DOWN\n#define UNDERGLOW_KEYCODE_RANGE             QK_UNDERGLOW_TOGGLE ... QK_UNDERGLOW_SPEED_DOWN\n#define RGB_KEYCODE_RANGE                   RGB_MODE_PLAIN ... RGB_MODE_TWINKLE\n#define RGB_MATRIX_KEYCODE_RANGE            QK_RGB_MATRIX_ON ... QK_RGB_MATRIX_SPEED_DOWN\n#define QUANTUM_KEYCODE_RANGE               QK_BOOTLOADER ... QK_LAYER_LOCK\n#define KB_KEYCODE_RANGE                    QK_KB_0 ... QK_KB_31\n#define USER_KEYCODE_RANGE                  QK_USER_0 ... QK_USER_31\n"
  },
  {
    "path": "quantum/keymap_common.c",
    "content": "/*\nCopyright 2012-2017 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"keymap_common.h\"\n#include \"keymap_introspection.h\"\n#include \"report.h\"\n#include \"keycode.h\"\n#include \"action_layer.h\"\n#include \"action.h\"\n#include \"debug.h\"\n#include \"keycode_config.h\"\n#include \"quantum_keycodes.h\"\n\n#ifdef ENCODER_MAP_ENABLE\n#    include \"encoder.h\"\n#endif\n\n#ifdef DIP_SWITCH_MAP_ENABLE\n#    include \"dip_switch.h\"\n#endif\n\n#ifdef BACKLIGHT_ENABLE\n#    include \"backlight.h\"\n#endif\n\n#ifdef MIDI_ENABLE\n#    include \"process_midi.h\"\n#endif\n\nextern keymap_config_t keymap_config;\n\n#include <inttypes.h>\n\n/* converts key to action */\naction_t action_for_key(uint8_t layer, keypos_t key) {\n    // 16bit keycodes - important\n    uint16_t keycode = keymap_key_to_keycode(layer, key);\n    return action_for_keycode(keycode);\n};\n\naction_t action_for_keycode(uint16_t keycode) {\n    // keycode remapping\n    keycode = keycode_config(keycode);\n\n    action_t action = {};\n    uint8_t  action_layer, mod;\n\n    (void)action_layer;\n    (void)mod;\n\n    switch (keycode) {\n        case BASIC_KEYCODE_RANGE:\n        case MODIFIER_KEYCODE_RANGE:\n            action.code = ACTION_KEY(keycode);\n            break;\n#ifdef EXTRAKEY_ENABLE\n        case SYSTEM_KEYCODE_RANGE:\n            action.code = ACTION_USAGE_SYSTEM(KEYCODE2SYSTEM(keycode));\n            break;\n        case CONSUMER_KEYCODE_RANGE:\n            action.code = ACTION_USAGE_CONSUMER(KEYCODE2CONSUMER(keycode));\n            break;\n#endif\n        case MOUSE_KEYCODE_RANGE:\n            action.code = ACTION_MOUSEKEY(keycode);\n            break;\n        case KC_TRANSPARENT:\n            action.code = ACTION_TRANSPARENT;\n            break;\n        case QK_MODS ... QK_MODS_MAX:;\n        // Has a modifier\n        // Split it up\n#ifdef LEGACY_MAGIC_HANDLING\n            action.code = ACTION_MODS_KEY(QK_MODS_GET_MODS(keycode), QK_MODS_GET_BASIC_KEYCODE(keycode)); // adds modifier to key\n#else                                                                                                     // LEGACY_MAGIC_HANDLING\n            action.code = ACTION_MODS_KEY(mod_config(QK_MODS_GET_MODS(keycode)), keycode_config(QK_MODS_GET_BASIC_KEYCODE(keycode))); // adds modifier to key\n#endif                                                                                                    // LEGACY_MAGIC_HANDLING\n            break;\n        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:\n#if !defined(NO_ACTION_LAYER) && !defined(NO_ACTION_TAPPING)\n#    ifdef LEGACY_MAGIC_HANDLING\n            action.code = ACTION_LAYER_TAP_KEY(QK_LAYER_TAP_GET_LAYER(keycode), QK_LAYER_TAP_GET_TAP_KEYCODE(keycode));\n#    else  // LEGACY_MAGIC_HANDLING\n            action.code = ACTION_LAYER_TAP_KEY(QK_LAYER_TAP_GET_LAYER(keycode), keycode_config(QK_LAYER_TAP_GET_TAP_KEYCODE(keycode)));\n#    endif // LEGACY_MAGIC_HANDLING\n#else\n            // pass through keycode_config again, since it previously missed it\n            // and then only send as ACTION_KEY to bypass most of action.c handling\n            action.code = ACTION_KEY(keycode_config(QK_LAYER_TAP_GET_TAP_KEYCODE(keycode)));\n#endif\n            break;\n#ifndef NO_ACTION_LAYER\n        case QK_TO ... QK_TO_MAX:;\n            // Layer set \"GOTO\"\n            action_layer = QK_TO_GET_LAYER(keycode);\n            action.code  = ACTION_LAYER_GOTO(action_layer);\n            break;\n        case QK_MOMENTARY ... QK_MOMENTARY_MAX:;\n            // Momentary action_layer\n            action_layer = QK_MOMENTARY_GET_LAYER(keycode);\n            action.code  = ACTION_LAYER_MOMENTARY(action_layer);\n            break;\n        case QK_DEF_LAYER ... QK_DEF_LAYER_MAX:;\n            // Set default action_layer\n            action_layer = QK_DEF_LAYER_GET_LAYER(keycode);\n            action.code  = ACTION_DEFAULT_LAYER_SET(action_layer);\n            break;\n        case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:;\n            // Set toggle\n            action_layer = QK_TOGGLE_LAYER_GET_LAYER(keycode);\n            action.code  = ACTION_LAYER_TOGGLE(action_layer);\n            break;\n#endif\n#ifndef NO_ACTION_ONESHOT\n        case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:;\n            // OSL(action_layer) - One-shot action_layer\n            action_layer = QK_ONE_SHOT_LAYER_GET_LAYER(keycode);\n            action.code  = ACTION_LAYER_ONESHOT(action_layer);\n            break;\n#endif // NO_ACTION_ONESHOT\n        case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:;\n            // OSM(mod) - One-shot mod\n            mod = mod_config(QK_ONE_SHOT_MOD_GET_MODS(keycode));\n#if defined(NO_ACTION_TAPPING) || defined(NO_ACTION_ONESHOT)\n            action.code = ACTION_MODS(mod);\n#else  // defined(NO_ACTION_TAPPING) || defined(NO_ACTION_ONESHOT)\n            action.code = ACTION_MODS_ONESHOT(mod);\n#endif // defined(NO_ACTION_TAPPING) || defined(NO_ACTION_ONESHOT)\n            break;\n#ifndef NO_ACTION_LAYER\n        case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:\n#    ifndef NO_ACTION_TAPPING\n            action.code = ACTION_LAYER_TAP_TOGGLE(QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode));\n#    else // NO_ACTION_TAPPING\n#        ifdef NO_ACTION_TAPPING_TAP_TOGGLE_MO\n            action.code = ACTION_LAYER_MOMENTARY(QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode));\n#        else  // NO_ACTION_TAPPING_TAP_TOGGLE_MO\n            action.code = ACTION_LAYER_TOGGLE(QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode));\n#        endif // NO_ACTION_TAPPING_TAP_TOGGLE_MO\n#    endif     // NO_ACTION_TAPPING\n            break;\n        case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:\n            mod          = mod_config(QK_LAYER_MOD_GET_MODS(keycode));\n            action_layer = QK_LAYER_MOD_GET_LAYER(keycode);\n            action.code  = ACTION_LAYER_MODS(action_layer, (mod & 0x10) ? mod << 4 : mod);\n            break;\n#endif // NO_ACTION_LAYER\n        case QK_MOD_TAP ... QK_MOD_TAP_MAX:\n#ifndef NO_ACTION_TAPPING\n            mod = mod_config(QK_MOD_TAP_GET_MODS(keycode));\n#    ifdef LEGACY_MAGIC_HANDLING\n            action.code = ACTION_MODS_TAP_KEY(mod, QK_MOD_TAP_GET_TAP_KEYCODE(keycode));\n#    else  // LEGACY_MAGIC_HANDLING\n            action.code = ACTION_MODS_TAP_KEY(mod, keycode_config(QK_MOD_TAP_GET_TAP_KEYCODE(keycode)));\n#    endif // LEGACY_MAGIC_HANDLING\n#else      // NO_ACTION_TAPPING\n#    ifdef NO_ACTION_TAPPING_MODTAP_MODS\n            // pass through mod_config again, since it previously missed it\n            // and then only send as ACTION_KEY to bypass most of action.c handling\n            action.code = ACTION_MODS(mod_config(QK_MOD_TAP_GET_MODS(keycode)));\n#    else  // NO_ACTION_TAPPING_MODTAP_MODS\n           // pass through keycode_config again, since it previously missed it\n           // and then only send as ACTION_KEY to bypass most of action.c handling\n            action.code = ACTION_KEY(keycode_config(QK_MOD_TAP_GET_TAP_KEYCODE(keycode)));\n#    endif // NO_ACTION_TAPPING_MODTAP_MODS\n#endif     // NO_ACTION_TAPPING\n            break;\n#ifdef SWAP_HANDS_ENABLE\n        case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:\n#    ifdef LEGACY_MAGIC_HANDLING\n            action.code = ACTION(ACT_SWAP_HANDS, QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode));\n#    else  // LEGACY_MAGIC_HANDLING\n            action.code = ACTION(ACT_SWAP_HANDS, keycode_config(QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode)));\n#    endif // LEGACY_MAGIC_HANDLING\n            break;\n#endif\n\n        default:\n            action.code = ACTION_NO;\n            break;\n    }\n    return action;\n}\n\n// translates key to keycode\n__attribute__((weak)) uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) {\n    if (key.row < MATRIX_ROWS && key.col < MATRIX_COLS) {\n        return keycode_at_keymap_location(layer, key.row, key.col);\n    }\n#ifdef ENCODER_MAP_ENABLE\n    else if (key.row == KEYLOC_ENCODER_CW && key.col < NUM_ENCODERS) {\n        return keycode_at_encodermap_location(layer, key.col, true);\n    } else if (key.row == KEYLOC_ENCODER_CCW && key.col < NUM_ENCODERS) {\n        return keycode_at_encodermap_location(layer, key.col, false);\n    }\n#endif // ENCODER_MAP_ENABLE\n#ifdef DIP_SWITCH_MAP_ENABLE\n    else if (key.row == KEYLOC_DIP_SWITCH_ON && key.col < NUM_DIP_SWITCHES) {\n        return keycode_at_dip_switch_map_location(key.col, true);\n    } else if (key.row == KEYLOC_DIP_SWITCH_OFF && key.col < NUM_DIP_SWITCHES) {\n        return keycode_at_dip_switch_map_location(key.col, false);\n    }\n#endif // DIP_SWITCH_MAP_ENABLE\n\n    return KC_NO;\n}\n"
  },
  {
    "path": "quantum/keymap_common.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include \"keyboard.h\"\n\n// translates key to keycode\nuint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key);\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_belgian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_BELGIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_BELGIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_BELGIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_BELGIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_BELGIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define BE_SUP2 KC_GRV  // ²\n#define BE_AMPR KC_1    // &\n#define BE_EACU KC_2    // é\n#define BE_DQUO KC_3    // \"\n#define BE_QUOT KC_4    // '\n#define BE_LPRN KC_5    // (\n#define BE_SECT KC_6    // §\n#define BE_EGRV KC_7    // è\n#define BE_EXLM KC_8    // !\n#define BE_CCED KC_9    // ç\n#define BE_AGRV KC_0    // à\n#define BE_RPRN KC_MINS // )\n#define BE_MINS KC_EQL  // -\n#define BE_A    KC_Q    // A\n#define BE_Z    KC_W    // Z\n#define BE_E    KC_E    // E\n#define BE_R    KC_R    // R\n#define BE_T    KC_T    // T\n#define BE_Y    KC_Y    // Y\n#define BE_U    KC_U    // U\n#define BE_I    KC_I    // I\n#define BE_O    KC_O    // O\n#define BE_P    KC_P    // P\n#define BE_DCIR KC_LBRC // ^ (dead)\n#define BE_DLR  KC_RBRC // $\n#define BE_Q    KC_A    // Q\n#define BE_S    KC_S    // S\n#define BE_D    KC_D    // D\n#define BE_F    KC_F    // F\n#define BE_G    KC_G    // G\n#define BE_H    KC_H    // H\n#define BE_J    KC_J    // J\n#define BE_K    KC_K    // K\n#define BE_L    KC_L    // L\n#define BE_M    KC_SCLN // M\n#define BE_UGRV KC_QUOT // ù\n#define BE_MICR KC_NUHS // µ\n#define BE_LABK KC_NUBS // <\n#define BE_W    KC_Z    // W\n#define BE_X    KC_X    // X\n#define BE_C    KC_C    // C\n#define BE_V    KC_V    // V\n#define BE_B    KC_B    // B\n#define BE_N    KC_N    // N\n#define BE_COMM KC_M    // ,\n#define BE_SCLN KC_COMM // ;\n#define BE_COLN KC_DOT  // :\n#define BE_EQL  KC_SLSH // =\n#define BE_SUP3 S(BE_SUP2) // ³\n#define BE_1    S(BE_AMPR) // 1\n#define BE_2    S(BE_EACU) // 2\n#define BE_3    S(BE_DQUO) // 3\n#define BE_4    S(BE_QUOT) // 4\n#define BE_5    S(BE_LPRN) // 5\n#define BE_6    S(BE_SECT) // 6\n#define BE_7    S(BE_EGRV) // 7\n#define BE_8    S(BE_EXLM) // 8\n#define BE_9    S(BE_CCED) // 9\n#define BE_0    S(BE_AGRV) // 0\n#define BE_DEG  S(BE_RPRN) // °\n#define BE_UNDS S(BE_MINS) // _\n#define BE_DIAE S(BE_DCIR) // ¨ (dead)\n#define BE_ASTR S(BE_DLR)  // *\n#define BE_PERC S(BE_UGRV) // %\n#define BE_PND  S(BE_MICR) // £\n#define BE_RABK S(BE_LABK) // >\n#define BE_QUES S(BE_COMM) // ?\n#define BE_DOT  S(BE_SCLN) // .\n#define BE_SLSH S(BE_COLN) // /\n#define BE_PLUS S(BE_EQL)  // +\n#define BE_PIPE ALGR(BE_AMPR) // |\n#define BE_AT   ALGR(BE_EACU) // @\n#define BE_HASH ALGR(BE_DQUO) // #\n#define BE_CIRC ALGR(BE_SECT) // ^\n#define BE_LCBR ALGR(BE_CCED) // {\n#define BE_RCBR ALGR(BE_AGRV) // }\n#define BE_EURO ALGR(BE_E)    // €\n#define BE_LBRC ALGR(BE_DCIR) // [\n#define BE_RBRC ALGR(BE_DLR)  // ]\n#define BE_ACUT ALGR(BE_UGRV) // ´ (dead)\n#define BE_GRV  ALGR(BE_MICR) // ` (dead)\n#define BE_BSLS ALGR(BE_LABK) // (backslash)\n#define BE_TILD ALGR(BE_EQL)  // ~\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_bepo.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_BEPO_KEYCODES_VERSION \"0.0.1\"\n#define QMK_BEPO_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_BEPO_KEYCODES_VERSION_MAJOR 0\n#define QMK_BEPO_KEYCODES_VERSION_MINOR 0\n#define QMK_BEPO_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define BP_DLR  KC_GRV  // $\n#define BP_DQUO KC_1    // \"\n#define BP_LDAQ KC_2    // «\n#define BP_RDAQ KC_3    // »\n#define BP_LPRN KC_4    // (\n#define BP_RPRN KC_5    // )\n#define BP_AT   KC_6    // @\n#define BP_PLUS KC_7    // +\n#define BP_MINS KC_8    // -\n#define BP_SLSH KC_9    // /\n#define BP_ASTR KC_0    // *\n#define BP_EQL  KC_MINS // =\n#define BP_PERC KC_EQL  // %\n#define BP_B    KC_Q    // B\n#define BP_EACU KC_W    // É\n#define BP_P    KC_E    // P\n#define BP_O    KC_R    // O\n#define BP_EGRV KC_T    // È\n#define BP_DCIR KC_Y    // ^ (dead)\n#define BP_V    KC_U    // V\n#define BP_D    KC_I    // D\n#define BP_L    KC_O    // L\n#define BP_J    KC_P    // J\n#define BP_Z    KC_LBRC // Z\n#define BP_W    KC_RBRC // W\n#define BP_A    KC_A    // A\n#define BP_U    KC_S    // U\n#define BP_I    KC_D    // I\n#define BP_E    KC_F    // E\n#define BP_COMM KC_G    // ,\n#define BP_C    KC_H    // C\n#define BP_T    KC_J    // T\n#define BP_S    KC_K    // S\n#define BP_R    KC_L    // R\n#define BP_N    KC_SCLN // N\n#define BP_M    KC_QUOT // M\n#define BP_CCED KC_BSLS // Ç\n#define BP_ECIR KC_NUBS // Ê\n#define BP_AGRV KC_Z    // À\n#define BP_Y    KC_X    // Y\n#define BP_X    KC_C    // X\n#define BP_DOT  KC_V    // .\n#define BP_K    KC_B    // K\n#define BP_QUOT KC_N    // '\n#define BP_Q    KC_M    // Q\n#define BP_G    KC_COMM // G\n#define BP_H    KC_DOT  // H\n#define BP_F    KC_SLSH // F\n#define BP_HASH S(BP_DLR)  // #\n#define BP_1    S(BP_DQUO) // 1\n#define BP_2    S(BP_LDAQ) // 2\n#define BP_3    S(BP_RDAQ) // 3\n#define BP_4    S(BP_LPRN) // 4\n#define BP_5    S(BP_RPRN) // 5\n#define BP_6    S(BP_AT)   // 6\n#define BP_7    S(BP_PLUS) // 7\n#define BP_8    S(BP_MINS) // 8\n#define BP_9    S(BP_SLSH) // 9\n#define BP_0    S(BP_ASTR) // 0\n#define BP_DEG  S(BP_EQL)  // °\n#define BP_GRV  S(BP_PERC) // `\n#define BP_EXLM S(BP_DCIR) // !\n#define BP_SCLN S(BP_COMM) // ;\n#define BP_COLN S(BP_DOT)  // :\n#define BP_QUES S(BP_QUOT) // ?\n#define BP_NBSP S(KC_SPC)  // (non-breaking space)\n#define BP_NDSH ALGR(BP_DLR)  // –\n#define BP_MDSH ALGR(BP_DQUO) // —\n#define BP_LABK ALGR(BP_LDAQ) // <\n#define BP_RABK ALGR(BP_RDAQ) // >\n#define BP_LBRC ALGR(BP_LPRN) // [\n#define BP_RBRC ALGR(BP_RPRN) // ]\n#define BP_CIRC ALGR(BP_AT)   // ^\n#define BP_PLMN ALGR(BP_PLUS) // ±\n#define BP_MMNS ALGR(BP_MINS) // −\n#define BP_DIV  ALGR(BP_SLSH) // ÷\n#define BP_MUL  ALGR(BP_ASTR) // ×\n#define BP_NEQL ALGR(BP_EQL)  // ≠\n#define BP_PERM ALGR(BP_PERC) // ‰\n#define BP_PIPE ALGR(BP_B)    // |\n#define BP_ACUT ALGR(BP_EACU) // ´ (dead)\n#define BP_AMPR ALGR(BP_P)    // &\n#define BP_OE   ALGR(BP_O)    // Œ\n#define BP_DGRV ALGR(BP_EGRV) // ` (dead)\n#define BP_IEXL ALGR(BP_DCIR) // ¡\n#define BP_CARN ALGR(BP_V)    // ˇ (dead)\n#define BP_ETH  ALGR(BP_D)    // Ð\n#define BP_DSLS ALGR(BP_L)    // / (dead)\n#define BP_IJ   ALGR(BP_J)    // Ĳ\n#define BP_SCHW ALGR(BP_Z)    // Ə\n#define BP_BREV ALGR(BP_W)    // ˘ (dead)\n#define BP_AE   ALGR(BP_A)    // Æ\n#define BP_UGRV ALGR(BP_U)    // Ù\n#define BP_DIAE ALGR(BP_I)    // ¨ (dead)\n#define BP_EURO ALGR(BP_E)    // €\n#define BP_COPY ALGR(BP_C)    // ©\n#define BP_THRN ALGR(BP_T)    // Þ\n#define BP_SS   ALGR(BP_S)    // ẞ\n#define BP_REGD ALGR(BP_R)    // ®\n#define BP_DTIL ALGR(BP_N)    // ~ (dead)\n#define BP_MACR ALGR(BP_M)    // ¯ (dead)\n#define BP_CEDL ALGR(BP_CCED) // ¸ (dead)\n#define BP_BSLS ALGR(BP_AGRV) // (backslash)\n#define BP_LCBR ALGR(BP_Y)    // {\n#define BP_RCBR ALGR(BP_X)    // }\n#define BP_ELLP ALGR(BP_DOT)  // …\n#define BP_TILD ALGR(BP_K)    // ~\n#define BP_IQUE ALGR(BP_QUES) // ¿\n#define BP_RNGA ALGR(BP_Q)    // ° (dead)\n#define BP_DGRK ALGR(BP_G)    // µ (dead Greek key)\n#define BP_DAGG ALGR(BP_H)    // †\n#define BP_OGON ALGR(BP_F)    // ˛ (dead)\n#define BP_UNDS ALGR(KC_SPC)  // _\n#define BP_PARA S(ALGR(BP_DLR))  // ¶\n#define BP_DLQU S(ALGR(BP_DQUO)) // „\n#define BP_LDQU S(ALGR(BP_LDAQ)) // “\n#define BP_RDQU S(ALGR(BP_RDAQ)) // ”\n#define BP_LEQL S(ALGR(BP_LPRN)) // ≤\n#define BP_GEQL S(ALGR(BP_RPRN)) // ≥\n#define BP_NOT  S(ALGR(BP_PLUS)) // ¬\n#define BP_QRTR S(ALGR(BP_MINS)) // ¼\n#define BP_HALF S(ALGR(BP_SLSH)) // ½\n#define BP_TQTR S(ALGR(BP_ASTR)) // ¾\n#define BP_PRIM S(ALGR(BP_EQL))  // ′\n#define BP_DPRM S(ALGR(BP_PERC)) // ″\n#define BP_BRKP S(ALGR(BP_B))    // ¦\n#define BP_DACU S(ALGR(BP_EACU)) // ˝ (dead)\n#define BP_SECT S(ALGR(BP_P))    // §\n#define BP_DOTA S(ALGR(BP_I))    // ˙ (dead)\n#define BP_CURR S(ALGR(BP_E))    // ¤ (dead)\n#define BP_HORN S(ALGR(BP_COMM)) // ̛ (dead)\n#define BP_LNGS S(ALGR(BP_C))    // ſ\n#define BP_TM   S(ALGR(BP_R))    // ™\n#define BP_MORD S(ALGR(BP_M))    // º\n#define BP_DCMM S(ALGR(BP_CCED)) // , (dead)\n#define BP_LSQU S(ALGR(BP_Y))    // ‘\n#define BP_RSQU S(ALGR(BP_X))    // ’\n#define BP_MDDT S(ALGR(BP_DOT))  // ·\n#define BP_KEYB S(ALGR(BP_K))    // ⌨\n#define BP_HOKA S(ALGR(BP_QUOT)) // ̉ (dead)\n#define BP_DOTB S(ALGR(BP_Q))    // ̣ (dead)\n#define BP_DDAG S(ALGR(BP_H))    // ‡\n#define BP_FORD S(ALGR(BP_F))    // ª\n#define BP_NNBS S(ALGR(KC_SPC))  // (narrow non-breaking space)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_brazilian_abnt2.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_BRAZILIAN_ABNT2_KEYCODES_VERSION \"0.0.1\"\n#define QMK_BRAZILIAN_ABNT2_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_BRAZILIAN_ABNT2_KEYCODES_VERSION_MAJOR 0\n#define QMK_BRAZILIAN_ABNT2_KEYCODES_VERSION_MINOR 0\n#define QMK_BRAZILIAN_ABNT2_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define BR_QUOT KC_GRV  // '\n#define BR_1    KC_1    // 1\n#define BR_2    KC_2    // 2\n#define BR_3    KC_3    // 3\n#define BR_4    KC_4    // 4\n#define BR_5    KC_5    // 5\n#define BR_6    KC_6    // 6\n#define BR_7    KC_7    // 7\n#define BR_8    KC_8    // 8\n#define BR_9    KC_9    // 9\n#define BR_0    KC_0    // 0\n#define BR_MINS KC_MINS // -\n#define BR_EQL  KC_EQL  // =\n#define BR_Q    KC_Q    // Q\n#define BR_W    KC_W    // W\n#define BR_E    KC_E    // E\n#define BR_R    KC_R    // R\n#define BR_T    KC_T    // T\n#define BR_Y    KC_Y    // Y\n#define BR_U    KC_U    // U\n#define BR_I    KC_I    // I\n#define BR_O    KC_O    // O\n#define BR_P    KC_P    // P\n#define BR_ACUT KC_LBRC // ´ (dead)\n#define BR_LBRC KC_RBRC // [\n#define BR_A    KC_A    // A\n#define BR_S    KC_S    // S\n#define BR_D    KC_D    // D\n#define BR_F    KC_F    // F\n#define BR_G    KC_G    // G\n#define BR_H    KC_H    // H\n#define BR_J    KC_J    // J\n#define BR_K    KC_K    // K\n#define BR_L    KC_L    // L\n#define BR_CCED KC_SCLN // Ç\n#define BR_TILD KC_QUOT // ~ (dead)\n#define BR_RBRC KC_BSLS // ]\n#define BR_BSLS KC_NUBS // (backslash)\n#define BR_Z    KC_Z    // Z\n#define BR_X    KC_X    // X\n#define BR_C    KC_C    // C\n#define BR_V    KC_V    // V\n#define BR_B    KC_B    // B\n#define BR_N    KC_N    // N\n#define BR_M    KC_M    // M\n#define BR_COMM KC_COMM // ,\n#define BR_DOT  KC_DOT  // .\n#define BR_SCLN KC_SLSH // ;\n#define BR_SLSH KC_INT1 // /\n#define BR_PDOT KC_PCMM // .\n#define BR_PCMM KC_PDOT // ,\n#define BR_DQUO S(BR_QUOT) // \"\n#define BR_EXLM S(BR_1)    // !\n#define BR_AT   S(BR_2)    // @\n#define BR_HASH S(BR_3)    // #\n#define BR_DLR  S(BR_4)    // $\n#define BR_PERC S(BR_5)    // %\n#define BR_DIAE S(BR_6)    // ¨ (dead)\n#define BR_AMPR S(BR_7)    // &\n#define BR_ASTR S(BR_8)    // *\n#define BR_LPRN S(BR_9)    // (\n#define BR_RPRN S(BR_0)    // )\n#define BR_UNDS S(BR_MINS) // _\n#define BR_PLUS S(BR_EQL)  // +\n#define BR_GRV  S(BR_ACUT) // ` (dead)\n#define BR_LCBR S(BR_LBRC) // {\n#define BR_CIRC S(BR_TILD) // ^ (dead)\n#define BR_RCBR S(BR_RBRC) // }\n#define BR_PIPE S(BR_BSLS) // |\n#define BR_LABK S(BR_COMM) // <\n#define BR_RABK S(BR_DOT)  // >\n#define BR_COLN S(BR_SCLN) // :\n#define BR_QUES S(BR_SLSH) // ?\n#define BR_SUP1 ALGR(BR_1)    // ¹\n#define BR_SUP2 ALGR(BR_2)    // ²\n#define BR_SUP3 ALGR(BR_3)    // ³\n#define BR_PND  ALGR(BR_4)    // £\n#define BR_CENT ALGR(BR_5)    // ¢\n#define BR_NOT  ALGR(BR_6)    // ¬\n#define BR_SECT ALGR(BR_EQL)  // §\n#define BR_DEG  ALGR(BR_E)    // °\n#define BR_FORD ALGR(BR_LBRC) // ª\n#define BR_MORD ALGR(BR_RBRC) // º\n#define BR_CRUZ ALGR(BR_C)    // ₢\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_canadian_french.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_CANADIAN_FRENCH_KEYCODES_VERSION \"0.0.1\"\n#define QMK_CANADIAN_FRENCH_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_CANADIAN_FRENCH_KEYCODES_VERSION_MAJOR 0\n#define QMK_CANADIAN_FRENCH_KEYCODES_VERSION_MINOR 0\n#define QMK_CANADIAN_FRENCH_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define FR_HASH KC_GRV  // #\n#define FR_1    KC_1    // 1\n#define FR_2    KC_2    // 2\n#define FR_3    KC_3    // 3\n#define FR_4    KC_4    // 4\n#define FR_5    KC_5    // 5\n#define FR_6    KC_6    // 6\n#define FR_7    KC_7    // 7\n#define FR_8    KC_8    // 8\n#define FR_9    KC_9    // 9\n#define FR_0    KC_0    // 0\n#define FR_MINS KC_MINS // -\n#define FR_EQL  KC_EQL  // =\n#define FR_Q    KC_Q    // Q\n#define FR_W    KC_W    // W\n#define FR_E    KC_E    // E\n#define FR_R    KC_R    // R\n#define FR_T    KC_T    // T\n#define FR_Y    KC_Y    // Y\n#define FR_U    KC_U    // U\n#define FR_I    KC_I    // I\n#define FR_O    KC_O    // O\n#define FR_P    KC_P    // P\n#define FR_DCIR KC_LBRC // ^ (dead)\n#define FR_CEDL KC_RBRC // ¸ (dead)\n#define FR_A    KC_A    // A\n#define FR_S    KC_S    // S\n#define FR_D    KC_D    // D\n#define FR_F    KC_F    // F\n#define FR_G    KC_G    // G\n#define FR_H    KC_H    // H\n#define FR_J    KC_J    // J\n#define FR_K    KC_K    // K\n#define FR_L    KC_L    // L\n#define FR_SCLN KC_SCLN // ;\n#define FR_DGRV KC_QUOT // ` (dead)\n#define FR_LABK KC_NUHS // <\n#define FR_LDAQ KC_NUBS // «\n#define FR_Z    KC_Z    // Z\n#define FR_X    KC_X    // X\n#define FR_C    KC_C    // C\n#define FR_V    KC_V    // V\n#define FR_B    KC_B    // B\n#define FR_N    KC_N    // N\n#define FR_M    KC_M    // M\n#define FR_COMM KC_COMM // ,\n#define FR_DOT  KC_DOT  // .\n#define FR_EACU KC_SLSH // É\n#define FR_PIPE S(FR_HASH) // |\n#define FR_EXLM S(FR_1)    // !\n#define FR_DQUO S(FR_2)    // \"\n#define FR_SLSH S(FR_3)    // /\n#define FR_DLR  S(FR_4)    // $\n#define FR_PERC S(FR_5)    // %\n#define FR_QUES S(FR_6)    // ?\n#define FR_AMPR S(FR_7)    // &\n#define FR_ASTR S(FR_8)    // *\n#define FR_LPRN S(FR_9)    // (\n#define FR_RPRN S(FR_0)    // )\n#define FR_UNDS S(FR_MINS) // _\n#define FR_PLUS S(FR_EQL)  // +\n#define FR_DIAE S(FR_CEDL) // ¨ (dead)\n#define FR_COLN S(FR_SCLN) // :\n#define FR_RABK S(FR_LABK) // >\n#define FR_RDAQ S(FR_LDAQ) // »\n#define FR_QUOT S(FR_COMM) // '\n#define FR_BSLS ALGR(FR_HASH) // (backslash)\n#define FR_PLMN ALGR(FR_1)    // ±\n#define FR_AT   ALGR(FR_2)    // @\n#define FR_PND  ALGR(FR_3)    // £\n#define FR_CENT ALGR(FR_4)    // ¢\n#define FR_CURR ALGR(FR_5)    // ¤\n#define FR_NOT  ALGR(FR_6)    // ¬\n#define FR_BRKP ALGR(FR_7)    // ¦\n#define FR_SUP2 ALGR(FR_8)    // ²\n#define FR_SUP3 ALGR(FR_9)    // ³\n#define FR_QRTR ALGR(FR_0)    // ¼\n#define FR_HALF ALGR(FR_MINS) // ½\n#define FR_TQTR ALGR(FR_EQL)  // ¾\n#define FR_SECT ALGR(FR_O)    // §\n#define FR_PARA ALGR(FR_P)    // ¶\n#define FR_LBRC ALGR(FR_DCIR) // [\n#define FR_RBRC ALGR(FR_CEDL) // ]\n#define FR_TILD ALGR(FR_SCLN) // ~\n#define FR_LCBR ALGR(FR_DGRV) // {\n#define FR_RCBR ALGR(FR_LABK) // }\n#define FR_DEG  ALGR(FR_LDAQ) // °\n#define FR_MICR ALGR(FR_M)    // µ\n#define FR_MACR ALGR(FR_COMM) // ¯\n#define FR_SHYP ALGR(FR_DOT)  // ­ (soft hyphen)\n#define FR_ACUT ALGR(FR_EACU) // ´ (dead)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_canadian_multilingual.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_CANADIAN_MULTILINGUAL_KEYCODES_VERSION \"0.0.1\"\n#define QMK_CANADIAN_MULTILINGUAL_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_CANADIAN_MULTILINGUAL_KEYCODES_VERSION_MAJOR 0\n#define QMK_CANADIAN_MULTILINGUAL_KEYCODES_VERSION_MINOR 0\n#define QMK_CANADIAN_MULTILINGUAL_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define CA_SLSH KC_GRV  // /\n#define CA_1    KC_1    // 1\n#define CA_2    KC_2    // 2\n#define CA_3    KC_3    // 3\n#define CA_4    KC_4    // 4\n#define CA_5    KC_5    // 5\n#define CA_6    KC_6    // 6\n#define CA_7    KC_7    // 7\n#define CA_8    KC_8    // 8\n#define CA_9    KC_9    // 9\n#define CA_0    KC_0    // 0\n#define CA_MINS KC_MINS // -\n#define CA_EQL  KC_EQL  // =\n#define CA_Q    KC_Q    // Q\n#define CA_W    KC_W    // W\n#define CA_E    KC_E    // E\n#define CA_R    KC_R    // R\n#define CA_T    KC_T    // T\n#define CA_Y    KC_Y    // Y\n#define CA_U    KC_U    // U\n#define CA_I    KC_I    // I\n#define CA_O    KC_O    // O\n#define CA_P    KC_P    // P\n#define CA_CIRC KC_LBRC // ^ (dead)\n#define CA_CCED KC_RBRC // Ç\n#define CA_A    KC_A    // A\n#define CA_S    KC_S    // S\n#define CA_D    KC_D    // D\n#define CA_F    KC_F    // F\n#define CA_G    KC_G    // G\n#define CA_H    KC_H    // H\n#define CA_J    KC_J    // J\n#define CA_K    KC_K    // K\n#define CA_L    KC_L    // L\n#define CA_SCLN KC_SCLN // ;\n#define CA_EGRV KC_QUOT // É\n#define CA_AGRV KC_NUHS // À\n#define CA_UGRV KC_NUBS // Ù\n#define CA_Z    KC_Z    // Z\n#define CA_X    KC_X    // X\n#define CA_C    KC_C    // C\n#define CA_V    KC_V    // V\n#define CA_B    KC_B    // B\n#define CA_N    KC_N    // N\n#define CA_M    KC_M    // M\n#define CA_COMM KC_COMM // ,\n#define CA_DOT  KC_DOT  // .\n#define CA_EACU KC_SLSH // É\n#define CA_BSLS S(CA_SLSH) // (backslash)\n#define CA_EXLM S(CA_1)    // !\n#define CA_AT   S(CA_2)    // @\n#define CA_HASH S(CA_3)    // #\n#define CA_DLR  S(CA_4)    // $\n#define CA_PERC S(CA_5)    // %\n#define CA_QUES S(CA_6)    // ?\n#define CA_AMPR S(CA_7)    // &\n#define CA_ASTR S(CA_8)    // *\n#define CA_LPRN S(CA_9)    // (\n#define CA_RPRN S(CA_0)    // )\n#define CA_UNDS S(CA_MINS) // _\n#define CA_PLUS S(CA_EQL)  // +\n#define CA_DIAE S(CA_CIRC) // ¨ (dead)\n#define CA_COLN S(CA_SCLN) // :\n#define CA_QUOT S(CA_COMM) // '\n#define CA_DQUO S(CA_DOT)  // \"\n#define CA_PIPE ALGR(CA_SLSH) // |\n#define CA_CURR ALGR(CA_4)    // ¤\n#define CA_LCBR ALGR(CA_7)    // {\n#define CA_RCBR ALGR(CA_8)    // }\n#define CA_LBRC ALGR(CA_9)    // [\n#define CA_RBRC ALGR(CA_0)    // ]\n#define CA_NOT  ALGR(CA_EQL)  // ¬\n#define CA_EURO ALGR(CA_E)    // €\n#define CA_GRV  ALGR(CA_CIRC) // ` (dead)\n#define CA_DTIL ALGR(CA_CCED) // ~ (dead)\n#define CA_DEG  ALGR(CA_SCLN) // °\n#define CA_LDAQ ALGR(CA_Z)    // «\n#define CA_RDAQ ALGR(CA_X)    // »\n#define CA_LABK ALGR(CA_COMM) // <\n#define CA_RABK ALGR(CA_DOT)  // >\n#define CA_SUP1 RCTL(CA_1)    // ¹\n#define CA_SUP2 RCTL(CA_2)    // ²\n#define CA_SUP3 RCTL(CA_3)    // ³\n#define CA_QRTR RCTL(CA_4)    // ¼\n#define CA_HALF RCTL(CA_5)    // ½\n#define CA_TQTR RCTL(CA_6)    // ¾\n#define CA_CEDL RCTL(CA_EQL)  // ¸ (dead)\n#define CA_OMEG RCTL(CA_Q)    // Ω\n#define CA_LSTR RCTL(CA_W)    // Ł\n#define CA_OE   RCTL(CA_E)    // Œ\n#define CA_PARA RCTL(CA_R)    // ¶\n#define CA_TSTR RCTL(CA_T)    // Ŧ\n#define CA_LARR RCTL(CA_Y)    // ←\n#define CA_DARR RCTL(CA_U)    // ↓\n#define CA_RARR RCTL(CA_I)    // →\n#define CA_OSTR RCTL(CA_O)    // Ø\n#define CA_THRN RCTL(CA_P)    // Þ\n#define CA_TILD RCTL(CA_CCED) // ~\n#define CA_AE   RCTL(CA_A)    // Æ\n#define CA_SS   RCTL(CA_S)    // ß\n#define CA_ETH  RCTL(CA_D)    // Ð\n#define CA_ENG  RCTL(CA_G)    // Ŋ\n#define CA_HSTR RCTL(CA_H)    // Ħ\n#define CA_IJ   RCTL(CA_J)    // Ĳ\n#define CA_KRA  RCTL(CA_K)    // ĸ\n#define CA_LMDT RCTL(CA_L)    // Ŀ\n#define CA_ACUT RCTL(CA_SCLN) // ´ (dead)\n#define CA_CENT RCTL(CA_C)    // ¢\n#define CA_LDQU RCTL(CA_V)    // “\n#define CA_RDQU RCTL(CA_B)    // ”\n#define CA_APSN RCTL(CA_N)    // ŉ\n#define CA_MICR RCTL(CA_M)    // μ\n#define CA_HRZB RCTL(CA_COMM) // ―\n#define CA_DOTA RCTL(CA_DOT)  // ˙ (dead)\n#define CA_SHYP RCTL(S(CA_SLSH)) // ­ (soft hyphen)\n#define CA_IEXL RCTL(S(CA_1))    // ¡\n#define CA_PND  RCTL(S(CA_3))    // £\n#define CA_TEIG RCTL(S(CA_5))    // ⅜\n#define CA_FEIG RCTL(S(CA_6))    // ⅝\n#define CA_SEIG RCTL(S(CA_7))    // ⅞\n#define CA_TM   RCTL(S(CA_8))    // ™\n#define CA_PLMN RCTL(S(CA_9))    // ±\n#define CA_IQUE RCTL(S(CA_MINS)) // ¿\n#define CA_OGON RCTL(S(CA_EQL))  // ˛ (dead)\n#define CA_REGD RCTL(S(CA_R))    // ®\n#define CA_YEN  RCTL(S(CA_Y))    // ¥\n#define CA_UARR RCTL(S(CA_U))    // ↑\n#define CA_DLSI RCTL(S(CA_I))    // ı\n#define CA_RNGA RCTL(S(CA_CIRC)) // ° (dead)\n#define CA_MACR RCTL(S(CA_CCED)) // ¯ (dead)\n#define CA_SECT RCTL(S(CA_S))    // §\n#define CA_FORD RCTL(S(CA_F))    // ª\n#define CA_DACU RCTL(S(CA_SCLN)) // ˝ (dead)\n#define CA_CARN RCTL(S(CA_EGRV)) // ˇ (dead)\n#define CA_BREV RCTL(S(CA_AGRV)) // ˘ (dead)\n#define CA_BRKP RCTL(S(CA_UGRV)) // ¦\n#define CA_COPY RCTL(S(CA_C))    // ©\n#define CA_LSQU RCTL(S(CA_V))    // ‘\n#define CA_RSQU RCTL(S(CA_B))    // ’\n#define CA_ENOT RCTL(S(CA_N))    // ♪\n#define CA_MORD RCTL(S(CA_M))    // º\n#define CA_MUL  RCTL(S(CA_COMM)) // ×\n#define CA_DIV  RCTL(S(CA_DOT))  // ÷\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_colemak.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_COLEMAK_KEYCODES_VERSION \"0.0.1\"\n#define QMK_COLEMAK_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_COLEMAK_KEYCODES_VERSION_MAJOR 0\n#define QMK_COLEMAK_KEYCODES_VERSION_MINOR 0\n#define QMK_COLEMAK_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define CM_GRV  KC_GRV  // `\n#define CM_1    KC_1    // 1\n#define CM_2    KC_2    // 2\n#define CM_3    KC_3    // 3\n#define CM_4    KC_4    // 4\n#define CM_5    KC_5    // 5\n#define CM_6    KC_6    // 6\n#define CM_7    KC_7    // 7\n#define CM_8    KC_8    // 8\n#define CM_9    KC_9    // 9\n#define CM_0    KC_0    // 0\n#define CM_MINS KC_MINS // -\n#define CM_EQL  KC_EQL  // =\n#define CM_Q    KC_Q    // Q\n#define CM_W    KC_W    // W\n#define CM_F    KC_E    // F\n#define CM_P    KC_R    // P\n#define CM_G    KC_T    // G\n#define CM_J    KC_Y    // J\n#define CM_L    KC_U    // L\n#define CM_U    KC_I    // U\n#define CM_Y    KC_O    // Y\n#define CM_SCLN KC_P    // ;\n#define CM_LBRC KC_LBRC // [\n#define CM_RBRC KC_RBRC // ]\n#define CM_BSLS KC_BSLS // (backslash)\n#define CM_A    KC_A    // A\n#define CM_R    KC_S    // R\n#define CM_S    KC_D    // S\n#define CM_T    KC_F    // T\n#define CM_D    KC_G    // D\n#define CM_H    KC_H    // H\n#define CM_N    KC_J    // N\n#define CM_E    KC_K    // E\n#define CM_I    KC_L    // I\n#define CM_O    KC_SCLN // O\n#define CM_QUOT KC_QUOT // '\n#define CM_Z    KC_Z    // Z\n#define CM_X    KC_X    // X\n#define CM_C    KC_C    // C\n#define CM_V    KC_V    // V\n#define CM_B    KC_B    // B\n#define CM_K    KC_N    // K\n#define CM_M    KC_M    // M\n#define CM_COMM KC_COMM // ,\n#define CM_DOT  KC_DOT  // .\n#define CM_SLSH KC_SLSH // /\n#define CM_TILD S(CM_GRV)  // ~\n#define CM_EXLM S(CM_1)    // !\n#define CM_AT   S(CM_2)    // @\n#define CM_HASH S(CM_3)    // #\n#define CM_DLR  S(CM_4)    // $\n#define CM_PERC S(CM_5)    // %\n#define CM_CIRC S(CM_6)    // ^\n#define CM_AMPR S(CM_7)    // &\n#define CM_ASTR S(CM_8)    // *\n#define CM_LPRN S(CM_9)    // (\n#define CM_RPRN S(CM_0)    // )\n#define CM_UNDS S(CM_MINS) // _\n#define CM_PLUS S(CM_EQL)  // +\n#define CM_COLN S(CM_SCLN) // :\n#define CM_LCBR S(CM_LBRC) // {\n#define CM_RCBR S(CM_RBRC) // }\n#define CM_PIPE S(CM_BSLS) // |\n#define CM_DQUO S(CM_QUOT) // \"\n#define CM_LABK S(CM_COMM) // <\n#define CM_RABK S(CM_DOT)  // >\n#define CM_QUES S(CM_SLSH) // ?\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_croatian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_CROATIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_CROATIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_CROATIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_CROATIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_CROATIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define HR_CEDL KC_GRV  // ¸ (dead)\n#define HR_1    KC_1    // 1\n#define HR_2    KC_2    // 2\n#define HR_3    KC_3    // 3\n#define HR_4    KC_4    // 4\n#define HR_5    KC_5    // 5\n#define HR_6    KC_6    // 6\n#define HR_7    KC_7    // 7\n#define HR_8    KC_8    // 8\n#define HR_9    KC_9    // 9\n#define HR_0    KC_0    // 0\n#define HR_QUOT KC_MINS // '\n#define HR_PLUS KC_EQL  // +\n#define HR_Q    KC_Q    // Q\n#define HR_W    KC_W    // W\n#define HR_E    KC_E    // E\n#define HR_R    KC_R    // R\n#define HR_T    KC_T    // T\n#define HR_Z    KC_Y    // Z\n#define HR_U    KC_U    // U\n#define HR_I    KC_I    // I\n#define HR_O    KC_O    // O\n#define HR_P    KC_P    // P\n#define HR_SCAR KC_LBRC // Š\n#define HR_DSTR KC_RBRC // Đ\n#define HR_A    KC_A    // A\n#define HR_S    KC_S    // S\n#define HR_D    KC_D    // D\n#define HR_F    KC_F    // F\n#define HR_G    KC_G    // G\n#define HR_H    KC_H    // H\n#define HR_J    KC_J    // J\n#define HR_K    KC_K    // K\n#define HR_L    KC_L    // L\n#define HR_CCAR KC_SCLN // Č\n#define HR_CACU KC_QUOT // Ć\n#define HR_ZCAR KC_NUHS // Ž\n#define HR_LABK KC_NUBS // <\n#define HR_Y    KC_Z    // Y\n#define HR_X    KC_X    // X\n#define HR_C    KC_C    // C\n#define HR_V    KC_V    // V\n#define HR_B    KC_B    // B\n#define HR_N    KC_N    // N\n#define HR_M    KC_M    // M\n#define HR_COMM KC_COMM // ,\n#define HR_DOT  KC_DOT  // .\n#define HR_MINS KC_SLSH // -\n#define HR_DIAE S(HR_CEDL) // ¨ (dead)\n#define HR_EXLM S(HR_1)    // !\n#define HR_DQUO S(HR_2)    // \"\n#define HR_HASH S(HR_3)    // #\n#define HR_DLR  S(HR_4)    // $\n#define HR_PERC S(HR_5)    // %\n#define HR_AMPR S(HR_6)    // &\n#define HR_SLSH S(HR_7)    // /\n#define HR_LPRN S(HR_8)    // (\n#define HR_RPRN S(HR_9)    // )\n#define HR_EQL  S(HR_0)    // =\n#define HR_QUES S(HR_QUOT) // ?\n#define HR_ASTR S(HR_PLUS) // *\n#define HR_RABK S(HR_LABK) // >\n#define HR_SCLN S(HR_COMM) // ;\n#define HR_COLN S(HR_DOT)  // :\n#define HR_UNDS S(HR_MINS) // _\n#define HR_TILD ALGR(HR_1)    // ~\n#define HR_CARN ALGR(HR_2)    // ˇ (dead)\n#define HR_CIRC ALGR(HR_3)    // ^ (dead)\n#define HR_BREV ALGR(HR_4)    // ˘ (dead)\n#define HR_RNGA ALGR(HR_5)    // ° (dead)\n#define HR_OGON ALGR(HR_6)    // ˛ (dead)\n#define HR_GRV  ALGR(HR_7)    // `\n#define HR_DOTA ALGR(HR_8)    // ˙ (dead)\n#define HR_ACUT ALGR(HR_9)    // ´ (dead)\n#define HR_DACU ALGR(HR_0)    // ˝ (dead)\n#define HR_BSLS ALGR(HR_Q)    // (backslash)\n#define HR_PIPE ALGR(HR_W)    // |\n#define HR_EURO ALGR(HR_E)    // €\n#define HR_DIV  ALGR(HR_SCAR) // ÷\n#define HR_MUL  ALGR(HR_DSTR) // ×\n#define HR_LBRC ALGR(HR_F)    // [\n#define HR_RBRC ALGR(HR_G)    // ]\n#define HR_LLST ALGR(HR_K)    // ł\n#define HR_CLST ALGR(HR_L)    // Ł\n#define HR_SS   ALGR(HR_CACU) // ß\n#define HR_CURR ALGR(HR_ZCAR) // ¤\n#define HR_AT   ALGR(HR_V)    // @\n#define HR_LCBR ALGR(HR_B)    // {\n#define HR_RCBR ALGR(HR_N)    // }\n#define HR_SECT ALGR(HR_M)    // §\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_czech.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_CZECH_KEYCODES_VERSION \"0.0.1\"\n#define QMK_CZECH_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_CZECH_KEYCODES_VERSION_MAJOR 0\n#define QMK_CZECH_KEYCODES_VERSION_MINOR 0\n#define QMK_CZECH_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define CZ_SCLN KC_GRV  // ;\n#define CZ_PLUS KC_1    // +\n#define CZ_ECAR KC_2    // ě\n#define CZ_SCAR KC_3    // š\n#define CZ_CCAR KC_4    // č\n#define CZ_RCAR KC_5    // ř\n#define CZ_ZCAR KC_6    // ž\n#define CZ_YACU KC_7    // ý\n#define CZ_AACU KC_8    // á\n#define CZ_IACU KC_9    // í\n#define CZ_EACU KC_0    // é\n#define CZ_EQL  KC_MINS // =\n#define CZ_ACUT KC_EQL  // ´ (dead)\n#define CZ_Q    KC_Q    // Q\n#define CZ_W    KC_W    // W\n#define CZ_E    KC_E    // E\n#define CZ_R    KC_R    // R\n#define CZ_T    KC_T    // T\n#define CZ_Z    KC_Y    // Z\n#define CZ_U    KC_U    // U\n#define CZ_I    KC_I    // I\n#define CZ_O    KC_O    // O\n#define CZ_P    KC_P    // P\n#define CZ_UACU KC_LBRC // ú\n#define CZ_RPRN KC_RBRC // )\n#define CZ_A    KC_A    // A\n#define CZ_S    KC_S    // S\n#define CZ_D    KC_D    // D\n#define CZ_F    KC_F    // F\n#define CZ_G    KC_G    // G\n#define CZ_H    KC_H    // H\n#define CZ_J    KC_J    // J\n#define CZ_K    KC_K    // K\n#define CZ_L    KC_L    // L\n#define CZ_URNG KC_SCLN // ů\n#define CZ_SECT KC_QUOT // §\n#define CZ_DIAE KC_NUHS // ¨ (dead)\n#define CZ_BSLS KC_NUBS // (backslash)\n#define CZ_Y    KC_Z    // Y\n#define CZ_X    KC_X    // X\n#define CZ_C    KC_C    // C\n#define CZ_V    KC_V    // V\n#define CZ_B    KC_B    // B\n#define CZ_N    KC_N    // N\n#define CZ_M    KC_M    // M\n#define CZ_COMM KC_COMM // ,\n#define CZ_DOT  KC_DOT  // .\n#define CZ_MINS KC_SLSH // -\n#define CZ_RNGA S(CZ_SCLN) // ° (dead)\n#define CZ_1    S(CZ_PLUS) // 1\n#define CZ_2    S(CZ_ECAR) // 2\n#define CZ_3    S(CZ_SCAR) // 3\n#define CZ_4    S(CZ_CCAR) // 4\n#define CZ_5    S(CZ_RCAR) // 5\n#define CZ_6    S(CZ_ZCAR) // 6\n#define CZ_7    S(CZ_YACU) // 7\n#define CZ_8    S(CZ_AACU) // 8\n#define CZ_9    S(CZ_IACU) // 9\n#define CZ_0    S(CZ_EACU) // 0\n#define CZ_PERC S(CZ_EQL)  // %\n#define CZ_CARN S(CZ_ACUT) // ˇ (dead)\n#define CZ_SLSH S(CZ_UACU) // /\n#define CZ_LPRN S(CZ_RPRN) // (\n#define CZ_DQUO S(CZ_URNG) // \"\n#define CZ_EXLM S(CZ_SECT) // !\n#define CZ_QUOT S(CZ_DIAE) // '\n#define CZ_PIPE S(CZ_BSLS) // |\n#define CZ_QUES S(CZ_COMM) // ?\n#define CZ_COLN S(CZ_DOT)  // :\n#define CZ_UNDS S(CZ_MINS) // _\n#define CZ_TILD ALGR(CZ_PLUS) // ~\n#define CZ_CIRC ALGR(CZ_SCAR) // ^ (dead)\n#define CZ_BREV ALGR(CZ_CCAR) // ˘ (dead)\n#define CZ_OGON ALGR(CZ_ZCAR) // ˛ (dead)\n#define CZ_GRV  ALGR(CZ_YACU) // ` (dead)\n#define CZ_DOTA ALGR(CZ_AACU) // ˙ (dead)\n#define CZ_DACU ALGR(CZ_EACU) // ˝ (dead)\n#define CZ_CEDL ALGR(CZ_ACUT) // ¸ (dead)\n#define CZ_EURO ALGR(CZ_E)    // €\n#define CZ_DIV  ALGR(CZ_UACU) // ÷\n#define CZ_MUL  ALGR(CZ_RPRN) // ×\n#define CZ_LDST ALGR(CZ_S)    // đ\n#define CZ_CDST ALGR(CZ_D)    // Đ\n#define CZ_LBRC ALGR(CZ_F)    // [\n#define CZ_RBRC ALGR(CZ_G)    // ]\n#define CZ_LLST ALGR(CZ_K)    // ł\n#define CZ_CLST ALGR(CZ_L)    // Ł\n#define CZ_DLR  ALGR(CZ_URNG) // $\n#define CZ_SS   ALGR(CZ_SECT) // ß\n#define CZ_CURR ALGR(CZ_DIAE) // ¤\n#define CZ_HASH ALGR(CZ_X)    // #\n#define CZ_AMPR ALGR(CZ_C)    // &\n#define CZ_AT   ALGR(CZ_V)    // @\n#define CZ_LCBR ALGR(CZ_B)    // {\n#define CZ_RCBR ALGR(CZ_N)    // }\n#define CZ_LABK ALGR(CZ_COMM) // <\n#define CZ_RABK ALGR(CZ_DOT)  // >\n#define CZ_ASTR ALGR(CZ_MINS) // *\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_czech_mac_ansi.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_CZECH_MAC_ANSI_KEYCODES_VERSION \"0.0.1\"\n#define QMK_CZECH_MAC_ANSI_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_CZECH_MAC_ANSI_KEYCODES_VERSION_MAJOR 0\n#define QMK_CZECH_MAC_ANSI_KEYCODES_VERSION_MINOR 0\n#define QMK_CZECH_MAC_ANSI_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define CZ_BSLS KC_GRV  // (backslash)\n#define CZ_PLUS KC_1    // +\n#define CZ_ECAR KC_2    // ě\n#define CZ_SCAR KC_3    // š\n#define CZ_CCAR KC_4    // č\n#define CZ_RCAR KC_5    // ř\n#define CZ_ZCAR KC_6    // ž\n#define CZ_YACU KC_7    // ý\n#define CZ_AACU KC_8    // á\n#define CZ_IACU KC_9    // í\n#define CZ_EACU KC_0    // é\n#define CZ_EQL  KC_MINS // =\n#define CZ_ACUT KC_EQL  // ' (dead)\n#define CZ_Q    KC_Q    // Q\n#define CZ_W    KC_W    // W\n#define CZ_E    KC_E    // E\n#define CZ_R    KC_R    // R\n#define CZ_T    KC_T    // T\n#define CZ_Z    KC_Y    // Z\n#define CZ_U    KC_U    // U\n#define CZ_I    KC_I    // I\n#define CZ_O    KC_O    // O\n#define CZ_P    KC_P    // P\n#define CZ_UACU KC_LBRC // ú\n#define CZ_RPRN KC_RBRC // )\n#define CZ_DIAE KC_NUHS // ¨ (dead)\n#define CZ_A    KC_A    // A\n#define CZ_S    KC_S    // S\n#define CZ_D    KC_D    // D\n#define CZ_F    KC_F    // F\n#define CZ_G    KC_G    // G\n#define CZ_H    KC_H    // H\n#define CZ_J    KC_J    // J\n#define CZ_K    KC_K    // K\n#define CZ_L    KC_L    // L\n#define CZ_URNG KC_SCLN // ů\n#define CZ_SECT KC_QUOT // §\n#define CZ_Y    KC_Z    // Y\n#define CZ_X    KC_X    // X\n#define CZ_C    KC_C    // C\n#define CZ_V    KC_V    // V\n#define CZ_B    KC_B    // B\n#define CZ_N    KC_N    // N\n#define CZ_M    KC_M    // M\n#define CZ_COMM KC_COMM // ,\n#define CZ_DOT  KC_DOT  // .\n#define CZ_MINS KC_SLSH // -\n#define CZ_PIPE S(CZ_BSLS) // |\n#define CZ_1    S(CZ_PLUS) // 1\n#define CZ_2    S(CZ_ECAR) // 2\n#define CZ_3    S(CZ_SCAR) // 3\n#define CZ_4    S(CZ_CCAR) // 4\n#define CZ_5    S(CZ_RCAR) // 5\n#define CZ_6    S(CZ_ZCAR) // 6\n#define CZ_7    S(CZ_YACU) // 7\n#define CZ_8    S(CZ_AACU) // 8\n#define CZ_9    S(CZ_IACU) // 9\n#define CZ_0    S(CZ_EACU) // 0\n#define CZ_PERC S(CZ_EQL)  // %\n#define CZ_CARN S(CZ_ACUT) // ˇ (dead)\n#define CZ_SLSH S(CZ_UACU) // /\n#define CZ_LPRN S(CZ_RPRN) // (\n#define CZ_GRV  S(CZ_DIAE) // `\n#define CZ_DQUO S(CZ_URNG) // \"\n#define CZ_EXLM S(CZ_SECT) // !\n#define CZ_QUES S(CZ_COMM) // ?\n#define CZ_COLN S(CZ_DOT)  // :\n#define CZ_UNDS S(CZ_MINS) // _\n#define CZ_AT   A(CZ_ECAR) // @\n#define CZ_HASH A(CZ_SCAR) // #\n#define CZ_DLR  A(CZ_CCAR) // $\n#define CZ_TILD A(CZ_RCAR) // ~\n#define CZ_CIRC A(CZ_ZCAR) // ^\n#define CZ_AMPR A(CZ_YACU) // &\n#define CZ_ASTR A(CZ_AACU) // *\n#define CZ_LCBR A(CZ_IACU) // {\n#define CZ_RCBR A(CZ_EACU) // }\n#define CZ_RNGA A(CZ_EQL)  // ° (dead)\n#define CZ_DCIR A(CZ_ACUT) // ^ (dead)\n#define CZ_LEDT A(CZ_W)    // ė\n#define CZ_LEOG A(CZ_E)    // ę\n#define CZ_EURO A(CZ_R)    // €\n#define CZ_LZDT A(CZ_Z)    // ż\n#define CZ_LBRC A(CZ_UACU) // [\n#define CZ_RBRC A(CZ_RPRN) // ]\n#define CZ_LAOG A(CZ_A)    // ą\n#define CZ_SS   A(CZ_S)    // ß\n#define CZ_PDIF A(CZ_D)    // ∂\n#define CZ_LSQU A(CZ_H)    // ‘\n#define CZ_RSQU A(CZ_J)    // ’\n#define CZ_LLST A(CZ_L)    // ł\n#define CZ_SCLN A(CZ_URNG) // ;\n#define CZ_QUOT A(CZ_SECT) // '\n#define CZ_SLQU A(CZ_N)    // ‚\n#define CZ_LABK A(CZ_COMM) // <\n#define CZ_RABK A(CZ_DOT)  // >\n#define CZ_NDSH A(CZ_MINS) // –\n#define CZ_NOT  S(A(CZ_1))    // ¬\n#define CZ_BULT S(A(CZ_2))    // •\n#define CZ_NEQL S(A(CZ_3))    // ≠\n#define CZ_PND  S(A(CZ_4))    // £\n#define CZ_LOZN S(A(CZ_5))    // ◊\n#define CZ_DAGG S(A(CZ_6))    // †\n#define CZ_PARA S(A(CZ_7))    // ¶\n#define CZ_DIV  S(A(CZ_8))    // ÷\n#define CZ_LDAQ S(A(CZ_9))    // «\n#define CZ_RDAQ S(A(CZ_0))    // »\n#define CZ_DCOM S(A(CZ_EQL))  // , (dead)\n#define CZ_DHPN S(A(CZ_ACUT)) // - (dead)\n#define CZ_CEDT S(A(CZ_W))    // Ė\n#define CZ_CEOG S(A(CZ_E))    // Ę\n#define CZ_REGD S(A(CZ_R))    // ®\n#define CZ_TM   S(A(CZ_T))    // ™\n#define CZ_CZDT S(A(CZ_Z))    // Ż\n#define CZ_LSAQ S(A(CZ_UACU)) // ‹\n#define CZ_RSAQ S(A(CZ_RPRN)) // ›\n#define CZ_DDQT S(A(CZ_DIAE)) // \" (dead)\n#define CZ_CAOG S(A(CZ_A))    // Ą\n#define CZ_NARS S(A(CZ_S))    // ∑\n#define CZ_INCR S(A(CZ_D))    // ∆\n#define CZ_LDQU S(A(CZ_H))    // “\n#define CZ_RDQU S(A(CZ_J))    // ”\n#define CZ_CLST S(A(CZ_L))    // Ł\n#define CZ_ELLP S(A(CZ_URNG)) // …\n#define CZ_DTIL S(A(CZ_SECT)) // ~ (dead)\n#define CZ_COPY S(A(CZ_C))    // ©\n#define CZ_SQRT S(A(CZ_V))    // √\n#define CZ_DLQU S(A(CZ_N))    // „\n#define CZ_LEQL S(A(CZ_COMM)) // ≤\n#define CZ_GEQL S(A(CZ_DOT))  // ≥\n#define CZ_MDSH S(A(CZ_MINS)) // —\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_czech_mac_iso.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_CZECH_MAC_ISO_KEYCODES_VERSION \"0.0.1\"\n#define QMK_CZECH_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_CZECH_MAC_ISO_KEYCODES_VERSION_MAJOR 0\n#define QMK_CZECH_MAC_ISO_KEYCODES_VERSION_MINOR 0\n#define QMK_CZECH_MAC_ISO_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define CZ_PLUS KC_1    // +\n#define CZ_ECAR KC_2    // ě\n#define CZ_SCAR KC_3    // š\n#define CZ_CCAR KC_4    // č\n#define CZ_RCAR KC_5    // ř\n#define CZ_ZCAR KC_6    // ž\n#define CZ_YACU KC_7    // ý\n#define CZ_AACU KC_8    // á\n#define CZ_IACU KC_9    // í\n#define CZ_EACU KC_0    // é\n#define CZ_EQL  KC_MINS // =\n#define CZ_ACUT KC_EQL  // ' (dead)\n#define CZ_Q    KC_Q    // Q\n#define CZ_W    KC_W    // W\n#define CZ_E    KC_E    // E\n#define CZ_R    KC_R    // R\n#define CZ_T    KC_T    // T\n#define CZ_Z    KC_Y    // Z\n#define CZ_U    KC_U    // U\n#define CZ_I    KC_I    // I\n#define CZ_O    KC_O    // O\n#define CZ_P    KC_P    // P\n#define CZ_UACU KC_LBRC // ú\n#define CZ_RPRN KC_RBRC // )\n#define CZ_A    KC_A    // A\n#define CZ_S    KC_S    // S\n#define CZ_D    KC_D    // D\n#define CZ_F    KC_F    // F\n#define CZ_G    KC_G    // G\n#define CZ_H    KC_H    // H\n#define CZ_J    KC_J    // J\n#define CZ_K    KC_K    // K\n#define CZ_L    KC_L    // L\n#define CZ_URNG KC_SCLN // ů\n#define CZ_SECT KC_QUOT // §\n#define CZ_DIAE KC_NUHS // ¨ (dead)\n#define CZ_BSLS KC_NUBS // (backslash)\n#define CZ_Y    KC_Z    // Y\n#define CZ_X    KC_X    // X\n#define CZ_C    KC_C    // C\n#define CZ_V    KC_V    // V\n#define CZ_B    KC_B    // B\n#define CZ_N    KC_N    // N\n#define CZ_M    KC_M    // M\n#define CZ_COMM KC_COMM // ,\n#define CZ_DOT  KC_DOT  // .\n#define CZ_MINS KC_SLSH // -\n#define CZ_1    S(CZ_PLUS) // 1\n#define CZ_2    S(CZ_ECAR) // 2\n#define CZ_3    S(CZ_SCAR) // 3\n#define CZ_4    S(CZ_CCAR) // 4\n#define CZ_5    S(CZ_RCAR) // 5\n#define CZ_6    S(CZ_ZCAR) // 6\n#define CZ_7    S(CZ_YACU) // 7\n#define CZ_8    S(CZ_AACU) // 8\n#define CZ_9    S(CZ_IACU) // 9\n#define CZ_0    S(CZ_EACU) // 0\n#define CZ_PERC S(CZ_EQL)  // %\n#define CZ_CARN S(CZ_ACUT) // ˇ (dead)\n#define CZ_SLSH S(CZ_UACU) // /\n#define CZ_LPRN S(CZ_RPRN) // (\n#define CZ_DQUO S(CZ_URNG) // \"\n#define CZ_EXLM S(CZ_SECT) // !\n#define CZ_GRV  S(CZ_DIAE) // `\n#define CZ_PIPE S(CZ_BSLS) // |\n#define CZ_QUES S(CZ_COMM) // ?\n#define CZ_COLN S(CZ_DOT)  // :\n#define CZ_UNDS S(CZ_MINS) // _\n#define CZ_AT   A(CZ_ECAR) // @\n#define CZ_HASH A(CZ_SCAR) // #\n#define CZ_DLR  A(CZ_CCAR) // $\n#define CZ_TILD A(CZ_RCAR) // ~\n#define CZ_CIRC A(CZ_ZCAR) // ^\n#define CZ_AMPR A(CZ_YACU) // &\n#define CZ_ASTR A(CZ_AACU) // *\n#define CZ_LCBR A(CZ_IACU) // {\n#define CZ_RCBR A(CZ_EACU) // }\n#define CZ_RNGA A(CZ_EQL)  // ° (dead)\n#define CZ_DCIR A(CZ_ACUT) // ^ (dead)\n#define CZ_LEDT A(CZ_W)    // ė\n#define CZ_LEOG A(CZ_E)    // ę\n#define CZ_EURO A(CZ_R)    // €\n#define CZ_LZDT A(CZ_Z)    // ż\n#define CZ_LBRC A(CZ_UACU) // [\n#define CZ_RBRC A(CZ_RPRN) // ]\n#define CZ_LAOG A(CZ_A)    // ą\n#define CZ_SS   A(CZ_S)    // ß\n#define CZ_PDIF A(CZ_D)    // ∂\n#define CZ_LSQU A(CZ_H)    // ‘\n#define CZ_RSQU A(CZ_J)    // ’\n#define CZ_LLST A(CZ_L)    // ł\n#define CZ_SCLN A(CZ_URNG) // ;\n#define CZ_QUOT A(CZ_SECT) // '\n#define CZ_SLQU A(CZ_N)    // ‚\n#define CZ_LABK A(CZ_COMM) // <\n#define CZ_RABK A(CZ_DOT)  // >\n#define CZ_NDSH A(CZ_MINS) // –\n#define CZ_NOT  S(A(CZ_1))    // ¬\n#define CZ_BULT S(A(CZ_2))    // •\n#define CZ_NEQL S(A(CZ_3))    // ≠\n#define CZ_PND  S(A(CZ_4))    // £\n#define CZ_LOZN S(A(CZ_5))    // ◊\n#define CZ_DAGG S(A(CZ_6))    // †\n#define CZ_PARA S(A(CZ_7))    // ¶\n#define CZ_DIV  S(A(CZ_8))    // ÷\n#define CZ_LDAQ S(A(CZ_9))    // «\n#define CZ_RDAQ S(A(CZ_0))    // »\n#define CZ_DCOM S(A(CZ_EQL))  // , (dead)\n#define CZ_DHPN S(A(CZ_ACUT)) // - (dead)\n#define CZ_CEDT S(A(CZ_W))    // Ė\n#define CZ_CEOG S(A(CZ_E))    // Ę\n#define CZ_REGD S(A(CZ_R))    // ®\n#define CZ_TM   S(A(CZ_T))    // ™\n#define CZ_CZDT S(A(CZ_Z))    // Ż\n#define CZ_LSAQ S(A(CZ_UACU)) // ‹\n#define CZ_RSAQ S(A(CZ_RPRN)) // ›\n#define CZ_CAOG S(A(CZ_A))    // Ą\n#define CZ_NARS S(A(CZ_S))    // ∑\n#define CZ_INCR S(A(CZ_D))    // ∆\n#define CZ_LDQU S(A(CZ_H))    // “\n#define CZ_RDQU S(A(CZ_J))    // ”\n#define CZ_CLST S(A(CZ_L))    // Ł\n#define CZ_ELLP S(A(CZ_URNG)) // …\n#define CZ_DTIL S(A(CZ_SECT)) // ~ (dead)\n#define CZ_DDQT S(A(CZ_DIAE)) // \" (dead)\n#define CZ_COPY S(A(CZ_C))    // ©\n#define CZ_SQRT S(A(CZ_V))    // √\n#define CZ_DLQU S(A(CZ_N))    // „\n#define CZ_LEQL S(A(CZ_COMM)) // ≤\n#define CZ_GEQL S(A(CZ_DOT))  // ≥\n#define CZ_MDSH S(A(CZ_MINS)) // —\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_danish.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_DANISH_KEYCODES_VERSION \"0.0.1\"\n#define QMK_DANISH_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_DANISH_KEYCODES_VERSION_MAJOR 0\n#define QMK_DANISH_KEYCODES_VERSION_MINOR 0\n#define QMK_DANISH_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define DK_HALF KC_GRV  // ½\n#define DK_1    KC_1    // 1\n#define DK_2    KC_2    // 2\n#define DK_3    KC_3    // 3\n#define DK_4    KC_4    // 4\n#define DK_5    KC_5    // 5\n#define DK_6    KC_6    // 6\n#define DK_7    KC_7    // 7\n#define DK_8    KC_8    // 8\n#define DK_9    KC_9    // 9\n#define DK_0    KC_0    // 0\n#define DK_PLUS KC_MINS // +\n#define DK_ACUT KC_EQL  // ´ (dead)\n#define DK_Q    KC_Q    // Q\n#define DK_W    KC_W    // W\n#define DK_E    KC_E    // E\n#define DK_R    KC_R    // R\n#define DK_T    KC_T    // T\n#define DK_Y    KC_Y    // Y\n#define DK_U    KC_U    // U\n#define DK_I    KC_I    // I\n#define DK_O    KC_O    // O\n#define DK_P    KC_P    // P\n#define DK_ARNG KC_LBRC // Å\n#define DK_DIAE KC_RBRC // ¨ (dead)\n#define DK_A    KC_A    // A\n#define DK_S    KC_S    // S\n#define DK_D    KC_D    // D\n#define DK_F    KC_F    // F\n#define DK_G    KC_G    // G\n#define DK_H    KC_H    // H\n#define DK_J    KC_J    // J\n#define DK_K    KC_K    // K\n#define DK_L    KC_L    // L\n#define DK_AE   KC_SCLN // Æ\n#define DK_OSTR KC_QUOT // Ø\n#define DK_QUOT KC_NUHS // '\n#define DK_LABK KC_NUBS // <\n#define DK_Z    KC_Z    // Z\n#define DK_X    KC_X    // X\n#define DK_C    KC_C    // C\n#define DK_V    KC_V    // V\n#define DK_B    KC_B    // B\n#define DK_N    KC_N    // N\n#define DK_M    KC_M    // M\n#define DK_COMM KC_COMM // ,\n#define DK_DOT  KC_DOT  // .\n#define DK_MINS KC_SLSH // -\n#define DK_SECT S(DK_HALF) // §\n#define DK_EXLM S(DK_1)    // !\n#define DK_DQUO S(DK_2)    // \"\n#define DK_HASH S(DK_3)    // #\n#define DK_CURR S(DK_4)    // ¤\n#define DK_PERC S(DK_5)    // %\n#define DK_AMPR S(DK_6)    // &\n#define DK_SLSH S(DK_7)    // /\n#define DK_LPRN S(DK_8)    // (\n#define DK_RPRN S(DK_9)    // )\n#define DK_EQL  S(DK_0)    // =\n#define DK_QUES S(DK_PLUS) // ?\n#define DK_GRV  S(DK_ACUT) // ` (dead)\n#define DK_CIRC S(DK_DIAE) // ^ (dead)\n#define DK_ASTR S(DK_QUOT) // *\n#define DK_RABK S(DK_LABK) // >\n#define DK_SCLN S(DK_COMM) // ;\n#define DK_COLN S(DK_DOT)  // :\n#define DK_UNDS S(DK_MINS) // _\n#define DK_AT   ALGR(DK_2)    // @\n#define DK_PND  ALGR(DK_3)    // £\n#define DK_DLR  ALGR(DK_4)    // $\n#define DK_EURO ALGR(DK_5)    // €\n#define DK_LCBR ALGR(DK_7)    // {\n#define DK_LBRC ALGR(DK_8)    // [\n#define DK_RBRC ALGR(DK_9)    // ]\n#define DK_RCBR ALGR(DK_0)    // }\n#define DK_PIPE ALGR(DK_ACUT) // |\n#define DK_TILD ALGR(DK_DIAE) // ~ (dead)\n#define DK_BSLS ALGR(DK_LABK) // (backslash)\n#define DK_MICR ALGR(DK_M)    // µ\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_dvorak.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_DVORAK_KEYCODES_VERSION \"0.0.1\"\n#define QMK_DVORAK_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_DVORAK_KEYCODES_VERSION_MAJOR 0\n#define QMK_DVORAK_KEYCODES_VERSION_MINOR 0\n#define QMK_DVORAK_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define DV_GRV  KC_GRV  // `\n#define DV_1    KC_1    // 1\n#define DV_2    KC_2    // 2\n#define DV_3    KC_3    // 3\n#define DV_4    KC_4    // 4\n#define DV_5    KC_5    // 5\n#define DV_6    KC_6    // 6\n#define DV_7    KC_7    // 7\n#define DV_8    KC_8    // 8\n#define DV_9    KC_9    // 9\n#define DV_0    KC_0    // 0\n#define DV_LBRC KC_MINS // [\n#define DV_RBRC KC_EQL  // ]\n#define DV_QUOT KC_Q    // '\n#define DV_COMM KC_W    // ,\n#define DV_DOT  KC_E    // .\n#define DV_P    KC_R    // P\n#define DV_Y    KC_T    // Y\n#define DV_F    KC_Y    // F\n#define DV_G    KC_U    // G\n#define DV_C    KC_I    // C\n#define DV_R    KC_O    // R\n#define DV_L    KC_P    // L\n#define DV_SLSH KC_LBRC // /\n#define DV_EQL  KC_RBRC // =\n#define DV_BSLS KC_BSLS // (backslash)\n#define DV_A    KC_A    // A\n#define DV_O    KC_S    // O\n#define DV_E    KC_D    // E\n#define DV_U    KC_F    // U\n#define DV_I    KC_G    // I\n#define DV_D    KC_H    // D\n#define DV_H    KC_J    // H\n#define DV_T    KC_K    // T\n#define DV_N    KC_L    // N\n#define DV_S    KC_SCLN // S\n#define DV_MINS KC_QUOT // -\n#define DV_SCLN KC_Z    // ;\n#define DV_Q    KC_X    // Q\n#define DV_J    KC_C    // J\n#define DV_K    KC_V    // K\n#define DV_X    KC_B    // X\n#define DV_B    KC_N    // B\n#define DV_M    KC_M    // M\n#define DV_W    KC_COMM // W\n#define DV_V    KC_DOT  // V\n#define DV_Z    KC_SLSH // Z\n#define DV_TILD S(DV_GRV)  // ~\n#define DV_EXLM S(DV_1)    // !\n#define DV_AT   S(DV_2)    // @\n#define DV_HASH S(DV_3)    // #\n#define DV_DLR  S(DV_4)    // $\n#define DV_PERC S(DV_5)    // %\n#define DV_CIRC S(DV_6)    // ^\n#define DV_AMPR S(DV_7)    // &\n#define DV_ASTR S(DV_8)    // *\n#define DV_LPRN S(DV_9)    // (\n#define DV_RPRN S(DV_0)    // )\n#define DV_LCBR S(DV_LBRC) // {\n#define DV_RCBR S(DV_RBRC) // }\n#define DV_DQUO S(DV_QUOT) // \"\n#define DV_LABK S(DV_COMM) // <\n#define DV_RABK S(DV_DOT)  // >\n#define DV_QUES S(DV_SLSH) // ?\n#define DV_PLUS S(DV_EQL)  // +\n#define DV_PIPE S(DV_BSLS) // |\n#define DV_UNDS S(DV_MINS) // _\n#define DV_COLN S(DV_SCLN) // :\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_dvorak_fr.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_DVORAK_FR_KEYCODES_VERSION \"0.0.1\"\n#define QMK_DVORAK_FR_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_DVORAK_FR_KEYCODES_VERSION_MAJOR 0\n#define QMK_DVORAK_FR_KEYCODES_VERSION_MINOR 0\n#define QMK_DVORAK_FR_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define DV_LDAQ KC_GRV  // «\n#define DV_RDAQ KC_1    // »\n#define DV_SLSH KC_2    // /\n#define DV_MINS KC_3    // -\n#define DV_EGRV KC_4    // è\n#define DV_BSLS KC_5    // (backslash)\n#define DV_CIRC KC_6    // ^ (dead)\n#define DV_LPRN KC_7    // (\n#define DV_GRV  KC_8    // ` (dead)\n#define DV_RPRN KC_9    // )\n#define DV_UNDS KC_0    // _\n#define DV_LBRC KC_MINS // [\n#define DV_RBRC KC_EQL  // ]\n#define DV_COLN KC_Q    // :\n#define DV_QUOT KC_W    // '\n#define DV_EACU KC_E    // é\n#define DV_G    KC_R    // G\n#define DV_DOT  KC_T    // .\n#define DV_H    KC_Y    // H\n#define DV_V    KC_U    // V\n#define DV_C    KC_I    // C\n#define DV_M    KC_O    // M\n#define DV_K    KC_P    // K\n#define DV_Z    KC_LBRC // Z\n#define DV_DIAE KC_RBRC // ¨ (dead)\n#define DV_O    KC_A    // O\n#define DV_A    KC_S    // A\n#define DV_U    KC_D    // U\n#define DV_E    KC_F    // E\n#define DV_B    KC_G    // B\n#define DV_F    KC_H    // F\n#define DV_S    KC_J    // S\n#define DV_T    KC_K    // T\n#define DV_N    KC_L    // N\n#define DV_D    KC_SCLN // D\n#define DV_W    KC_QUOT // W\n#define DV_TILD KC_NUHS // ~ (dead)\n#define DV_AGRV KC_NUBS // à\n#define DV_SCLN KC_Z    // ;\n#define DV_Q    KC_X    // Q\n#define DV_COMM KC_C    // ,\n#define DV_I    KC_V    // I\n#define DV_Y    KC_B    // Y\n#define DV_X    KC_N    // X\n#define DV_R    KC_M    // R\n#define DV_L    KC_COMM // L\n#define DV_P    KC_DOT  // P\n#define DV_J    KC_SLSH // J\n#define DV_ASTR S(DV_LDAQ) // *\n#define DV_1    S(DV_RDAQ) // 1\n#define DV_2    S(DV_SLSH) // 2\n#define DV_3    S(DV_MINS) // 3\n#define DV_4    S(DV_EGRV) // 4\n#define DV_5    S(DV_BSLS) // 5\n#define DV_6    S(DV_CIRC) // 6\n#define DV_7    S(DV_LPRN) // 7\n#define DV_8    S(DV_GRV)  // 8\n#define DV_9    S(DV_RPRN) // 9\n#define DV_0    S(DV_UNDS) // 0\n#define DV_PLUS S(DV_LBRC) // +\n#define DV_PERC S(DV_RBRC) // %\n#define DV_QUES S(DV_COLN) // ?\n#define DV_LABK S(DV_QUOT) // <\n#define DV_RABK S(DV_EACU) // >\n#define DV_EXLM S(DV_DOT)  // !\n#define DV_EQL  S(DV_DIAE) // =\n#define DV_HASH S(DV_TILD) // #\n#define DV_CCED S(DV_AGRV) // ç\n#define DV_PIPE S(DV_SCLN) // |\n#define DV_AT   S(DV_COMM) // @\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_dvorak_programmer.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_DVORAK_PROGRAMMER_KEYCODES_VERSION \"0.0.1\"\n#define QMK_DVORAK_PROGRAMMER_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_DVORAK_PROGRAMMER_KEYCODES_VERSION_MAJOR 0\n#define QMK_DVORAK_PROGRAMMER_KEYCODES_VERSION_MINOR 0\n#define QMK_DVORAK_PROGRAMMER_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define DP_DLR  KC_GRV  // $\n#define DP_AMPR KC_1    // &\n#define DP_LBRC KC_2    // [\n#define DP_LCBR KC_3    // {\n#define DP_RCBR KC_4    // }\n#define DP_LPRN KC_5    // (\n#define DP_EQL  KC_6    // =\n#define DP_ASTR KC_7    // *\n#define DP_RPRN KC_8    // )\n#define DP_PLUS KC_9    // +\n#define DP_RBRC KC_0    // ]\n#define DP_EXLM KC_MINS // !\n#define DP_HASH KC_EQL  // #\n#define DP_SCLN KC_Q    // ;\n#define DP_COMM KC_W    // ,\n#define DP_DOT  KC_E    // .\n#define DP_P    KC_R    // P\n#define DP_Y    KC_T    // Y\n#define DP_F    KC_Y    // F\n#define DP_G    KC_U    // G\n#define DP_C    KC_I    // C\n#define DP_R    KC_O    // R\n#define DP_L    KC_P    // L\n#define DP_SLSH KC_LBRC // /\n#define DP_AT   KC_RBRC // @\n#define DP_BSLS KC_BSLS // (backslash)\n#define DP_A    KC_A    // A\n#define DP_O    KC_S    // O\n#define DP_E    KC_D    // E\n#define DP_U    KC_F    // U\n#define DP_I    KC_G    // I\n#define DP_D    KC_H    // D\n#define DP_H    KC_J    // H\n#define DP_T    KC_K    // T\n#define DP_N    KC_L    // N\n#define DP_S    KC_SCLN // S\n#define DP_MINS KC_QUOT // -\n#define DP_QUOT KC_Z    // '\n#define DP_Q    KC_X    // Q\n#define DP_J    KC_C    // J\n#define DP_K    KC_V    // K\n#define DP_X    KC_B    // X\n#define DP_B    KC_N    // B\n#define DP_M    KC_M    // M\n#define DP_W    KC_COMM // W\n#define DP_V    KC_DOT  // V\n#define DP_Z    KC_SLSH // Z\n#define DP_TILD S(DP_DLR)  // ~\n#define DP_PERC S(DP_AMPR) // %\n#define DP_7    S(DP_LBRC) // 7\n#define DP_5    S(DP_LCBR) // 5\n#define DP_3    S(DP_RCBR) // 3\n#define DP_1    S(DP_LPRN) // 1\n#define DP_9    S(DP_EQL)  // 9\n#define DP_0    S(DP_ASTR) // 0\n#define DP_2    S(DP_RPRN) // 2\n#define DP_4    S(DP_PLUS) // 4\n#define DP_6    S(DP_RBRC) // 6\n#define DP_8    S(DP_EXLM) // 8\n#define DP_GRV  S(DP_HASH) // `\n#define DP_COLN S(DP_SCLN) // :\n#define DP_LABK S(DP_COMM) // <\n#define DP_RABK S(DP_DOT)  // >\n#define DP_QUES S(DP_SLSH) // ?\n#define DP_CIRC S(DP_AT)   // ^\n#define DP_PIPE S(DP_BSLS) // |\n#define DP_UNDS S(DP_MINS) // _\n#define DP_DQUO S(DP_QUOT) // \"\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_estonian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_ESTONIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_ESTONIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_ESTONIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_ESTONIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_ESTONIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define EE_CARN KC_GRV  // ˇ (dead)\n#define EE_1    KC_1    // 1\n#define EE_2    KC_2    // 2\n#define EE_3    KC_3    // 3\n#define EE_4    KC_4    // 4\n#define EE_5    KC_5    // 5\n#define EE_6    KC_6    // 6\n#define EE_7    KC_7    // 7\n#define EE_8    KC_8    // 8\n#define EE_9    KC_9    // 9\n#define EE_0    KC_0    // 0\n#define EE_PLUS KC_MINS // +\n#define EE_ACUT KC_EQL  // ´ (dead)\n#define EE_Q    KC_Q    // Q\n#define EE_W    KC_W    // W\n#define EE_E    KC_E    // E\n#define EE_R    KC_R    // R\n#define EE_T    KC_T    // T\n#define EE_Y    KC_Y    // Y\n#define EE_U    KC_U    // U\n#define EE_I    KC_I    // I\n#define EE_O    KC_O    // O\n#define EE_P    KC_P    // P\n#define EE_UDIA KC_LBRC // Ü\n#define EE_OTIL KC_RBRC // Õ\n#define EE_A    KC_A    // A\n#define EE_S    KC_S    // S\n#define EE_D    KC_D    // D\n#define EE_F    KC_F    // F\n#define EE_G    KC_G    // G\n#define EE_H    KC_H    // H\n#define EE_J    KC_J    // J\n#define EE_K    KC_K    // K\n#define EE_L    KC_L    // L\n#define EE_ODIA KC_SCLN // Ö\n#define EE_ADIA KC_QUOT // Ä\n#define EE_QUOT KC_NUHS // '\n#define EE_LABK KC_NUBS // <\n#define EE_Z    KC_Z    // Z\n#define EE_X    KC_X    // X\n#define EE_C    KC_C    // C\n#define EE_V    KC_V    // V\n#define EE_B    KC_B    // B\n#define EE_N    KC_N    // N\n#define EE_M    KC_M    // M\n#define EE_COMM KC_COMM // ,\n#define EE_DOT  KC_DOT  // .\n#define EE_MINS KC_SLSH // -\n#define EE_TILD S(EE_CARN) // ~ (dead)\n#define EE_EXLM S(EE_1)    // !\n#define EE_DQUO S(EE_2)    // \"\n#define EE_HASH S(EE_3)    // #\n#define EE_CURR S(EE_4)    // ¤\n#define EE_PERC S(EE_5)    // %\n#define EE_AMPR S(EE_6)    // &\n#define EE_SLSH S(EE_7)    // /\n#define EE_LPRN S(EE_8)    // (\n#define EE_RPRN S(EE_9)    // )\n#define EE_EQL  S(EE_0)    // =\n#define EE_QUES S(EE_PLUS) // ?\n#define EE_GRV  S(EE_ACUT) // ` (dead)\n#define EE_ASTR S(EE_QUOT) // *\n#define EE_RABK S(EE_LABK) // >\n#define EE_SCLN S(EE_COMM) // ;\n#define EE_COLN S(EE_DOT)  // :\n#define EE_UNDS S(EE_MINS) // _\n#define EE_AT   ALGR(EE_2)    // @\n#define EE_PND  ALGR(EE_3)    // £\n#define EE_DLR  ALGR(EE_4)    // $\n#define EE_EURO ALGR(EE_5)    // €\n#define EE_LCBR ALGR(EE_7)    // {\n#define EE_LBRC ALGR(EE_8)    // [\n#define EE_RBRC ALGR(EE_9)    // ]\n#define EE_RCBR ALGR(EE_0)    // }\n#define EE_BSLS ALGR(EE_PLUS) // (backslash)\n#define EE_SECT ALGR(EE_OTIL) // §\n#define EE_SCAR ALGR(EE_S)    // š\n#define EE_CIRC ALGR(EE_ADIA) // ^ (dead)\n#define EE_HALF ALGR(EE_QUOT) // ½\n#define EE_PIPE ALGR(EE_LABK) // |\n#define EE_ZCAR ALGR(EE_Z)    // ž\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_eurkey.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_EURKEY_KEYCODES_VERSION \"0.0.1\"\n#define QMK_EURKEY_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_EURKEY_KEYCODES_VERSION_MAJOR 0\n#define QMK_EURKEY_KEYCODES_VERSION_MINOR 0\n#define QMK_EURKEY_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define EU_GRV  KC_GRV  // `\n#define EU_1    KC_1    // 1\n#define EU_2    KC_2    // 2\n#define EU_3    KC_3    // 3\n#define EU_4    KC_4    // 4\n#define EU_5    KC_5    // 5\n#define EU_6    KC_6    // 6\n#define EU_7    KC_7    // 7\n#define EU_8    KC_8    // 8\n#define EU_9    KC_9    // 9\n#define EU_0    KC_0    // 0\n#define EU_MINS KC_MINS // -\n#define EU_EQL  KC_EQL  // =\n#define EU_Q    KC_Q    // Q\n#define EU_W    KC_W    // W\n#define EU_E    KC_E    // E\n#define EU_R    KC_R    // R\n#define EU_T    KC_T    // T\n#define EU_Y    KC_Y    // Y\n#define EU_U    KC_U    // U\n#define EU_I    KC_I    // I\n#define EU_O    KC_O    // O\n#define EU_P    KC_P    // P\n#define EU_LBRC KC_LBRC // [\n#define EU_RBRC KC_RBRC // ]\n#define EU_BSLS KC_BSLS // (backslash)\n#define EU_A    KC_A    // A\n#define EU_S    KC_S    // S\n#define EU_D    KC_D    // D\n#define EU_F    KC_F    // F\n#define EU_G    KC_G    // G\n#define EU_H    KC_H    // H\n#define EU_J    KC_J    // J\n#define EU_K    KC_K    // K\n#define EU_L    KC_L    // L\n#define EU_SCLN KC_SCLN // ;\n#define EU_QUOT KC_QUOT // '\n#define EU_Z    KC_Z    // Z\n#define EU_X    KC_X    // X\n#define EU_C    KC_C    // C\n#define EU_V    KC_V    // V\n#define EU_B    KC_B    // B\n#define EU_N    KC_N    // N\n#define EU_M    KC_M    // M\n#define EU_COMM KC_COMM // ,\n#define EU_DOT  KC_DOT  // .\n#define EU_SLSH KC_SLSH // /\n#define EU_TILD S(EU_GRV)  // ~\n#define EU_EXLM S(EU_1)    // !\n#define EU_AT   S(EU_2)    // @\n#define EU_HASH S(EU_3)    // #\n#define EU_DLR  S(EU_4)    // $\n#define EU_PERC S(EU_5)    // %\n#define EU_CIRC S(EU_6)    // ^\n#define EU_AMPR S(EU_7)    // &\n#define EU_ASTR S(EU_8)    // *\n#define EU_LPRN S(EU_9)    // (\n#define EU_RPRN S(EU_0)    // )\n#define EU_UNDS S(EU_MINS) // _\n#define EU_PLUS S(EU_EQL)  // +\n#define EU_LCBR S(EU_LBRC) // {\n#define EU_RCBR S(EU_RBRC) // }\n#define EU_PIPE S(EU_BSLS) // |\n#define EU_COLN S(EU_SCLN) // :\n#define EU_DQUO S(EU_QUOT) // \"\n#define EU_LABK S(EU_COMM) // <\n#define EU_RABK S(EU_DOT)  // >\n#define EU_QUES S(EU_SLSH) // ?\n#define EU_DGRV ALGR(EU_GRV)  // ` (dead)\n#define EU_IEXL ALGR(EU_1)    // ¡\n#define EU_FORD ALGR(EU_2)    // ª\n#define EU_MORD ALGR(EU_3)    // º\n#define EU_PND  ALGR(EU_4)    // £\n#define EU_EURO ALGR(EU_5)    // €\n#define EU_DCIR ALGR(EU_6)    // ^ (dead)\n#define EU_RNGA ALGR(EU_7)    // ˚ (dead)\n#define EU_DLQU ALGR(EU_8)    // „\n#define EU_LDQU ALGR(EU_9)    // “\n#define EU_RDQU ALGR(EU_0)    // ”\n#define EU_NDSH ALGR(EU_MINS) // –\n#define EU_MUL  ALGR(EU_EQL)  // ×\n#define EU_AE   ALGR(EU_Q)    // æ\n#define EU_ARNG ALGR(EU_W)    // Å\n#define EU_EDIA ALGR(EU_E)    // Ë\n#define EU_YACU ALGR(EU_R)    // Ý\n#define EU_THRN ALGR(EU_T)    // Þ\n#define EU_YDIA ALGR(EU_Y)    // Ÿ\n#define EU_UDIA ALGR(EU_U)    // Ü\n#define EU_IDIA ALGR(EU_I)    // Ï\n#define EU_ODIA ALGR(EU_O)    // Ö\n#define EU_OE   ALGR(EU_P)    // Œ\n#define EU_LDAQ ALGR(EU_LBRC) // «\n#define EU_RDAQ ALGR(EU_RBRC) // »\n#define EU_NOT  ALGR(EU_BSLS) // ¬\n#define EU_ADIA ALGR(EU_A)    // Ä\n#define EU_SS   ALGR(EU_S)    // ß\n#define EU_ETH  ALGR(EU_D)    // Ð\n#define EU_EGRV ALGR(EU_F)    // È\n#define EU_EACU ALGR(EU_G)    // É\n#define EU_UGRV ALGR(EU_H)    // Ù\n#define EU_UACU ALGR(EU_J)    // Ú\n#define EU_IJ   ALGR(EU_K)    // Ĳ\n#define EU_OSTR ALGR(EU_L)    // Ø\n#define EU_DEG  ALGR(EU_SCLN) // °\n#define EU_ACUT ALGR(EU_QUOT) // ´ (dead)\n#define EU_AGRV ALGR(EU_Z)    // À\n#define EU_AACU ALGR(EU_X)    // Á\n#define EU_CCED ALGR(EU_C)    // Ç\n#define EU_IGRV ALGR(EU_V)    // Ì\n#define EU_IACU ALGR(EU_B)    // Í\n#define EU_NTIL ALGR(EU_N)    // Ñ\n#define EU_DGRK ALGR(EU_M)    // μ (dead Greek key)\n#define EU_OGRV ALGR(EU_COMM) // Ò\n#define EU_OACU ALGR(EU_DOT)  // Ó\n#define EU_IQUE ALGR(EU_SLSH) // ¿\n#define EU_DTIL ALGR(EU_TILD) // ~ (dead)\n#define EU_SUP1 S(ALGR(EU_1))    // ¹\n#define EU_SUP2 S(ALGR(EU_2))    // ²\n#define EU_SUP3 S(ALGR(EU_3))    // ³\n#define EU_YEN  ALGR(EU_DLR)  // ¥\n#define EU_CENT S(EU_EURO) // ¢\n#define EU_CARN S(EU_DCIR) // ˇ (dead)\n#define EU_MACR S(ALGR(EU_7))    // ¯ (dead)\n#define EU_SLQU S(EU_DLQU) // ‚\n#define EU_LSQU S(EU_LDQU) // ‘\n#define EU_RSQU S(EU_RDQU) // ’\n#define EU_MDSH S(EU_NDSH) // —\n#define EU_DIV  S(EU_MUL)  // ÷\n#define EU_LSAQ S(EU_LDAQ) // ‹\n#define EU_RSAQ S(EU_RDAQ) // ›\n#define EU_BRKP S(ALGR(EU_BSLS)) // ¦\n#define EU_SECT S(ALGR(EU_S))    // §\n#define EU_MDDT S(ALGR(EU_SCLN)) // ·\n#define EU_DIAE ALGR(EU_DQUO) // ¨ (dead)\n#define EU_ELLP ALGR(EU_QUES) // …\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_farsi.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_FARSI_KEYCODES_VERSION \"0.0.1\"\n#define QMK_FARSI_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_FARSI_KEYCODES_VERSION_MAJOR 0\n#define QMK_FARSI_KEYCODES_VERSION_MINOR 0\n#define QMK_FARSI_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define FA_ZWJ  KC_GRV  // (zero-width joiner)\n#define FA_1A   KC_1    // ۱\n#define FA_2A   KC_2    // ۲\n#define FA_3A   KC_3    // ۳\n#define FA_4A   KC_4    // ۴\n#define FA_5A   KC_5    // ۵\n#define FA_6A   KC_6    // ۶\n#define FA_7A   KC_7    // ۷\n#define FA_8A   KC_8    // ۸\n#define FA_9A   KC_9    // ۹\n#define FA_0A   KC_0    // ۰\n#define FA_MINS KC_MINS // -\n#define FA_EQL  KC_EQL  // =\n#define FA_ZAD  KC_Q    // ض\n#define FA_SAD  KC_W    // ص\n#define FA_SE   KC_E    // ث\n#define FA_QAF  KC_R    // ق\n#define FA_FE   KC_T    // ف\n#define FA_GHYN KC_Y    // غ\n#define FA_EYN  KC_U    // ع\n#define FA_HE   KC_I    // ه\n#define FA_KHE  KC_O    // خ\n#define FA_HEJ  KC_P    // ح\n#define FA_JIM  KC_LBRC // ج\n#define FA_CHE  KC_RBRC // چ\n#define FA_SHIN KC_A    // ش\n#define FA_SIN  KC_S    // س\n#define FA_YE   KC_D    // ی\n#define FA_BE   KC_F    // ب\n#define FA_LAM  KC_G    // ل\n#define FA_ALEF KC_H    // ا\n#define FA_TE   KC_J    // ت\n#define FA_NOON KC_K    // ن\n#define FA_MIM  KC_L    // م\n#define FA_KAF  KC_SCLN // ک\n#define FA_GAF  KC_QUOT // گ\n#define FA_BSLS KC_BSLS // (backslash)\n#define FA_LT   KC_LT   // <\n#define FA_ZA   KC_Z    // ظ\n#define FA_TA   KC_X    // ط\n#define FA_ZE   KC_C    // ز\n#define FA_RE   KC_V    // ر\n#define FA_ZAL  KC_B    // ذ\n#define FA_DAL  KC_N    // د\n#define FA_PE   KC_M    // پ\n#define FA_WAW  KC_COMM // و\n#define FA_DOT  KC_DOT  // .\n#define FA_SLSH KC_SLSH // /\n#define FA_SPC  KC_SPC  //  \n#define FA_DIV  S(FA_ZWJ)  // ÷\n#define FA_EXLM S(FA_1A)   // !\n#define FA_THS  S(FA_2A)   // ٬\n#define FA_DECS S(FA_3A)   // ٫\n#define FA_RIAL S(FA_4A)   // ﷼\n#define FA_PRCA S(FA_5A)   // ٪\n#define FA_MUL  S(FA_6A)   // ×\n#define FA_COMA S(FA_7A)   // ،\n#define FA_ASTR S(FA_8A)   // *\n#define FA_RPRN S(FA_9A)   // )\n#define FA_LPRN S(FA_0A)   // (\n#define FA_TATW S(FA_MINS) // ـ\n#define FA_PLUS S(FA_EQL)  // +\n#define FA_SUK  S(FA_ZAD)  // ْ\n#define FA_DMTN S(FA_SAD)  // ٌ\n#define FA_KSTN S(FA_SE)   // ٍ\n#define FA_FTHN S(FA_QAF)  // ً\n#define FA_DMM  S(FA_FE)   // ُ\n#define FA_KAS  S(FA_GHYN) // ِ\n#define FA_FAT  S(FA_EYN)  // َ\n#define FA_TSDD S(FA_HE)   // \n#define FA_RBRC S(FA_KHE)  // ]\n#define FA_LBRC S(FA_HEJ)  // [\n#define FA_RCBR S(FA_JIM)  // }\n#define FA_LCBR S(FA_CHE)  // {\n#define FA_HMZV S(FA_SHIN) // ؤ\n#define FA_HMZY S(FA_SIN)  // ئ\n#define FA_YEA  S(FA_YE)   // ي\n#define FA_HMZU S(FA_BE)   // إ\n#define FA_HMZO S(FA_LAM)  // أ\n#define FA_MALF S(FA_ALEF) // آ\n#define FA_TEHM S(FA_TE)   // ة\n#define FA_RQOT S(FA_NOON) // »\n#define FA_LQOT S(FA_MIM)  // «\n#define FA_COLN S(FA_KAF)  // :\n#define FA_SCLA S(FA_GAF)  // ؛\n#define FA_GT   S(FA_LT)   // >\n#define FA_KAFA S(FA_ZA)   // ك\n#define FA_MADO S(FA_TA)   // ٓ\n#define FA_JEH  S(FA_ZE)   // ژ\n#define FA_SUPA S(FA_RE)   // ٰ\n#define FA_ZWNJ S(FA_ZAL)  // (zero-width non-joiner)\n#define FA_HMZA S(FA_DAL)  // ٔ\n#define FA_HMZ  S(FA_PE)   // ء\n#define FA_QSA  S(FA_SLSH) // ؟\n#define FA_TILD ALGR(FA_ZWJ)  // ~\n#define FA_GRV  ALGR(FA_1A)   // `\n#define FA_AT   ALGR(FA_2A)   // @\n#define FA_HASH ALGR(FA_3A)   // #\n#define FA_DLR  ALGR(FA_4A)   // $\n#define FA_PERC ALGR(FA_5A)   // %\n#define FA_CIRC ALGR(FA_6A)   // ^\n#define FA_AMPR ALGR(FA_7A)   // &\n#define FA_BULT ALGR(FA_8A)   // •\n#define FA_LRM  ALGR(FA_9A)   // (left-to-right mark)\n#define FA_RLM  ALGR(FA_0A)   // (right-to-left mark)\n#define FA_UNDS ALGR(FA_MINS) // _\n#define FA_DMNS ALGR(FA_EQL)  // − (dead)\n#define FA_DEG  ALGR(FA_ZAD)  // °\n#define FA_EURO ALGR(FA_SE)   // €\n#define FA_LRO  ALGR(FA_HE)   // (left-to-right override)\n#define FA_RLO  ALGR(FA_KHE)  // (right-to-left override)\n#define FA_PDF  ALGR(FA_HEJ)  // (pop directional formatting)\n#define FA_LRE  ALGR(FA_JIM)  // (left-to-right embedding)\n#define FA_RLE  ALGR(FA_CHE)  // (right-to-left embedding)\n#define FA_ALFM ALGR(FA_YE)   // ى\n#define FA_ALFW ALGR(FA_ALEF) // ٱ\n#define FA_LORP ALGR(FA_NOON) // ﴾\n#define FA_RORP ALGR(FA_MIM)  // ﴿\n#define FA_SCLN ALGR(FA_KAF)  // ;\n#define FA_DQT  ALGR(FA_GAF)  // \"\n#define FA_MINA ALGR(FA_BSLS) // -\n#define FA_PIPE ALGR(FA_ZA)   // |\n#define FA_SUBA ALGR(FA_RE)   // ٖ\n#define FA_HMZB ALGR(FA_DAL)  // ء\n#define FA_ELLP ALGR(FA_PE)   // …\n#define FA_COMM ALGR(FA_WAW)  // ,\n#define FA_QUOT ALGR(FA_DOT)  // '\n#define FA_QUES ALGR(FA_SLSH) // ?\n#define FA_1    S(ALGR(FA_1A))   // 1\n#define FA_2    S(ALGR(FA_2A))   // 2\n#define FA_3    S(ALGR(FA_3A))   // 3\n#define FA_4    S(ALGR(FA_4A))   // 4\n#define FA_5    S(ALGR(FA_5A))   // 5\n#define FA_6    S(ALGR(FA_6A))   // 6\n#define FA_7    S(ALGR(FA_7A))   // 7\n#define FA_8    S(ALGR(FA_8A))   // 8\n#define FA_9    S(ALGR(FA_9A))   // 9\n#define FA_0    S(ALGR(FA_0A))   // 0\n#define FA_BRKP S(ALGR(FA_LT))   // ¦\n#define FA_NNBS S(ALGR(FA_SPC))  // (narrow non-breaking space)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_finnish.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_FINNISH_KEYCODES_VERSION \"0.0.1\"\n#define QMK_FINNISH_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_FINNISH_KEYCODES_VERSION_MAJOR 0\n#define QMK_FINNISH_KEYCODES_VERSION_MINOR 0\n#define QMK_FINNISH_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define FI_SECT KC_GRV  // §\n#define FI_1    KC_1    // 1\n#define FI_2    KC_2    // 2\n#define FI_3    KC_3    // 3\n#define FI_4    KC_4    // 4\n#define FI_5    KC_5    // 5\n#define FI_6    KC_6    // 6\n#define FI_7    KC_7    // 7\n#define FI_8    KC_8    // 8\n#define FI_9    KC_9    // 9\n#define FI_0    KC_0    // 0\n#define FI_PLUS KC_MINS // +\n#define FI_ACUT KC_EQL  // ´ (dead)\n#define FI_Q    KC_Q    // Q\n#define FI_W    KC_W    // W\n#define FI_E    KC_E    // E\n#define FI_R    KC_R    // R\n#define FI_T    KC_T    // T\n#define FI_Y    KC_Y    // Y\n#define FI_U    KC_U    // U\n#define FI_I    KC_I    // I\n#define FI_O    KC_O    // O\n#define FI_P    KC_P    // P\n#define FI_ARNG KC_LBRC // Å\n#define FI_DIAE KC_RBRC // ¨ (dead)\n#define FI_A    KC_A    // A\n#define FI_S    KC_S    // S\n#define FI_D    KC_D    // D\n#define FI_F    KC_F    // F\n#define FI_G    KC_G    // G\n#define FI_H    KC_H    // H\n#define FI_J    KC_J    // J\n#define FI_K    KC_K    // K\n#define FI_L    KC_L    // L\n#define FI_ODIA KC_SCLN // Ö\n#define FI_ADIA KC_QUOT // Ä\n#define FI_QUOT KC_NUHS // '\n#define FI_LABK KC_NUBS // <\n#define FI_Z    KC_Z    // Z\n#define FI_X    KC_X    // X\n#define FI_C    KC_C    // C\n#define FI_V    KC_V    // V\n#define FI_B    KC_B    // B\n#define FI_N    KC_N    // N\n#define FI_M    KC_M    // M\n#define FI_COMM KC_COMM // ,\n#define FI_DOT  KC_DOT  // .\n#define FI_MINS KC_SLSH // -\n#define FI_HALF S(FI_SECT) // ½\n#define FI_EXLM S(FI_1)    // !\n#define FI_DQUO S(FI_2)    // \"\n#define FI_HASH S(FI_3)    // #\n#define FI_CURR S(FI_4)    // ¤\n#define FI_PERC S(FI_5)    // %\n#define FI_AMPR S(FI_6)    // &\n#define FI_SLSH S(FI_7)    // /\n#define FI_LPRN S(FI_8)    // (\n#define FI_RPRN S(FI_9)    // )\n#define FI_EQL  S(FI_0)    // =\n#define FI_QUES S(FI_PLUS) // ?\n#define FI_GRV  S(FI_ACUT) // ` (dead)\n#define FI_CIRC S(FI_DIAE) // ^ (dead)\n#define FI_ASTR S(FI_QUOT) // *\n#define FI_RABK S(FI_LABK) // >\n#define FI_SCLN S(FI_COMM) // ;\n#define FI_COLN S(FI_DOT)  // :\n#define FI_UNDS S(FI_MINS) // _\n#define FI_AT   ALGR(FI_2)    // @\n#define FI_PND  ALGR(FI_3)    // £\n#define FI_DLR  ALGR(FI_4)    // $\n#define FI_EURO ALGR(FI_5)    // €\n#define FI_LCBR ALGR(FI_7)    // {\n#define FI_LBRC ALGR(FI_8)    // [\n#define FI_RBRC ALGR(FI_9)    // ]\n#define FI_RCBR ALGR(FI_0)    // }\n#define FI_BSLS ALGR(FI_PLUS) // (backslash)\n#define FI_TILD ALGR(FI_DIAE) // ~ (dead)\n#define FI_PIPE ALGR(FI_LABK) // |\n#define FI_MICR ALGR(FI_M)    // µ\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_french.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_FRENCH_KEYCODES_VERSION \"0.0.1\"\n#define QMK_FRENCH_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_FRENCH_KEYCODES_VERSION_MAJOR 0\n#define QMK_FRENCH_KEYCODES_VERSION_MINOR 0\n#define QMK_FRENCH_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define FR_SUP2 KC_GRV  // ²\n#define FR_AMPR KC_1    // &\n#define FR_EACU KC_2    // é\n#define FR_DQUO KC_3    // \"\n#define FR_QUOT KC_4    // '\n#define FR_LPRN KC_5    // (\n#define FR_MINS KC_6    // -\n#define FR_EGRV KC_7    // è\n#define FR_UNDS KC_8    // _\n#define FR_CCED KC_9    // ç\n#define FR_AGRV KC_0    // à\n#define FR_RPRN KC_MINS // )\n#define FR_EQL  KC_EQL  // =\n#define FR_A    KC_Q    // A\n#define FR_Z    KC_W    // Z\n#define FR_E    KC_E    // E\n#define FR_R    KC_R    // R\n#define FR_T    KC_T    // T\n#define FR_Y    KC_Y    // Y\n#define FR_U    KC_U    // U\n#define FR_I    KC_I    // I\n#define FR_O    KC_O    // O\n#define FR_P    KC_P    // P\n#define FR_CIRC KC_LBRC // ^ (dead)\n#define FR_DLR  KC_RBRC // $\n#define FR_Q    KC_A    // Q\n#define FR_S    KC_S    // S\n#define FR_D    KC_D    // D\n#define FR_F    KC_F    // F\n#define FR_G    KC_G    // G\n#define FR_H    KC_H    // H\n#define FR_J    KC_J    // J\n#define FR_K    KC_K    // K\n#define FR_L    KC_L    // L\n#define FR_M    KC_SCLN // M\n#define FR_UGRV KC_QUOT // ù\n#define FR_ASTR KC_NUHS // *\n#define FR_LABK KC_NUBS // <\n#define FR_W    KC_Z    // W\n#define FR_X    KC_X    // X\n#define FR_C    KC_C    // C\n#define FR_V    KC_V    // V\n#define FR_B    KC_B    // B\n#define FR_N    KC_N    // N\n#define FR_COMM KC_M    // ,\n#define FR_SCLN KC_COMM // ;\n#define FR_COLN KC_DOT  // :\n#define FR_EXLM KC_SLSH // !\n#define FR_1    S(FR_AMPR) // 1\n#define FR_2    S(FR_EACU) // 2\n#define FR_3    S(FR_DQUO) // 3\n#define FR_4    S(FR_QUOT) // 4\n#define FR_5    S(FR_LPRN) // 5\n#define FR_6    S(FR_MINS) // 6\n#define FR_7    S(FR_EGRV) // 7\n#define FR_8    S(FR_UNDS) // 8\n#define FR_9    S(FR_CCED) // 9\n#define FR_0    S(FR_AGRV) // 0\n#define FR_DEG  S(FR_RPRN) // °\n#define FR_PLUS S(FR_EQL)  // +\n#define FR_DIAE S(FR_CIRC) // ¨ (dead)\n#define FR_PND  S(FR_DLR)  // £\n#define FR_PERC S(FR_UGRV) // %\n#define FR_MICR S(FR_ASTR) // µ\n#define FR_RABK S(FR_LABK) // >\n#define FR_QUES S(FR_COMM) // ?\n#define FR_DOT  S(FR_SCLN) // .\n#define FR_SLSH S(FR_COLN) // /\n#define FR_SECT S(FR_EXLM) // §\n#define FR_TILD ALGR(FR_EACU) // ~ (dead)\n#define FR_HASH ALGR(FR_DQUO) // #\n#define FR_LCBR ALGR(FR_QUOT) // {\n#define FR_LBRC ALGR(FR_LPRN) // [\n#define FR_PIPE ALGR(FR_MINS) // |\n#define FR_GRV  ALGR(FR_EGRV) // ` (dead)\n#define FR_BSLS ALGR(FR_UNDS) // (backslash)\n#define FR_AT   ALGR(FR_AGRV) // @\n#define FR_RBRC ALGR(FR_RPRN) // ]\n#define FR_RCBR ALGR(FR_EQL)  // }\n#define FR_EURO ALGR(KC_E)    // €\n#define FR_CURR ALGR(FR_DLR)  // ¤\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_french_afnor.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_FRENCH_AFNOR_KEYCODES_VERSION \"0.0.1\"\n#define QMK_FRENCH_AFNOR_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_FRENCH_AFNOR_KEYCODES_VERSION_MAJOR 0\n#define QMK_FRENCH_AFNOR_KEYCODES_VERSION_MINOR 0\n#define QMK_FRENCH_AFNOR_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define FR_AT   KC_GRV  // @\n#define FR_AGRV KC_1    // à\n#define FR_EACU KC_2    // é\n#define FR_EGRV KC_3    // è\n#define FR_ECIR KC_4    // ê\n#define FR_LPRN KC_5    // (\n#define FR_RPRN KC_6    // )\n#define FR_LSQU KC_7    // ‘\n#define FR_RSQU KC_8    // ’\n#define FR_LDAQ KC_9    // «\n#define FR_RDAQ KC_0    // »\n#define FR_QUOT KC_MINS // '\n#define FR_DCIR KC_EQL  // ^ (dead)\n#define FR_A    KC_Q    // A\n#define FR_Z    KC_W    // Z\n#define FR_E    KC_E    // E\n#define FR_R    KC_R    // R\n#define FR_T    KC_T    // T\n#define FR_Y    KC_Y    // Y\n#define FR_U    KC_U    // U\n#define FR_I    KC_I    // I\n#define FR_O    KC_O    // O\n#define FR_P    KC_P    // P\n#define FR_MINS KC_LBRC // -\n#define FR_PLUS KC_RBRC // +\n#define FR_Q    KC_A    // Q\n#define FR_S    KC_S    // S\n#define FR_D    KC_D    // D\n#define FR_F    KC_F    // F\n#define FR_G    KC_G    // G\n#define FR_H    KC_H    // H\n#define FR_J    KC_J    // J\n#define FR_K    KC_K    // K\n#define FR_L    KC_L    // L\n#define FR_M    KC_SCLN // M\n#define FR_SLSH KC_QUOT // /\n#define FR_ASTR KC_NUHS // *\n#define FR_LABK KC_NUBS // <\n#define FR_W    KC_Z    // W\n#define FR_X    KC_X    // X\n#define FR_C    KC_C    // C\n#define FR_V    KC_V    // V\n#define FR_B    KC_B    // B\n#define FR_N    KC_N    // N\n#define FR_DOT  KC_M    // .\n#define FR_COMM KC_COMM // ,\n#define FR_COLN KC_DOT  // :\n#define FR_SCLN KC_SLSH // ;\n#define FR_HASH S(FR_AT)   // #\n#define FR_1    S(FR_AGRV) // 1\n#define FR_2    S(FR_EACU) // 2\n#define FR_3    S(FR_EGRV) // 3\n#define FR_4    S(FR_ECIR) // 4\n#define FR_5    S(FR_LPRN) // 5\n#define FR_6    S(FR_RPRN) // 6\n#define FR_7    S(FR_LSQU) // 7\n#define FR_8    S(FR_RSQU) // 8\n#define FR_9    S(FR_LDAQ) // 9\n#define FR_0    S(FR_RDAQ) // 0\n#define FR_DQUO S(FR_QUOT) // \"\n#define FR_DIAE S(FR_DCIR) // ¨ (dead)\n#define FR_NDSH S(FR_MINS) // –\n#define FR_PLMN S(FR_PLUS) // ±\n#define FR_BSLS S(FR_SLSH) // (backslash)\n#define FR_HALF S(FR_ASTR) // ½\n#define FR_RABK S(FR_LABK) // >\n#define FR_QUES S(FR_DOT)  // ?\n#define FR_EXLM S(FR_COMM) // !\n#define FR_ELLP S(FR_COLN) // …\n#define FR_EQL  S(FR_SCLN) // =\n#define FR_BREV ALGR(FR_AT)   // ˘ (dead)\n#define FR_SECT ALGR(FR_AGRV) // §\n#define FR_ACUT ALGR(FR_EACU) // ´ (dead)\n#define FR_GRV  ALGR(FR_EGRV) // ` (dead)\n#define FR_AMPR ALGR(FR_ECIR) // &\n#define FR_LBRC ALGR(FR_LPRN) // [\n#define FR_RBRC ALGR(FR_RPRN) // ]\n#define FR_MACR ALGR(FR_LSQU) // ¯ (dead)\n#define FR_UNDS ALGR(FR_RSQU) // _\n#define FR_LDQU ALGR(FR_LDAQ) // “\n#define FR_RDQU ALGR(FR_RDAQ) // ”\n#define FR_DEG  ALGR(FR_QUOT) // °\n#define FR_CARN ALGR(FR_DCIR) // ˇ (dead)\n#define FR_AE   ALGR(FR_A)    // æ\n#define FR_PND  ALGR(FR_Z)    // £\n#define FR_EURO ALGR(FR_E)    // €\n#define FR_REGD ALGR(FR_R)    // ®\n#define FR_LCBR ALGR(FR_T)    // {\n#define FR_RCBR ALGR(FR_Y)    // }\n#define FR_UGRV ALGR(FR_U)    // ù\n#define FR_DOTA ALGR(FR_I)    // ˙ (dead)\n#define FR_OE   ALGR(FR_O)    // œ\n#define FR_PERC ALGR(FR_P)    // %\n#define FR_MMNS ALGR(FR_MINS) // −\n#define FR_DAGG ALGR(FR_PLUS) // †\n#define FR_THET ALGR(FR_Q)    // θ\n#define FR_SS   ALGR(FR_S)    // ß\n#define FR_DLR  ALGR(FR_D)    // $\n#define FR_CURR ALGR(FR_F)    // ¤ (dead monetary key)\n#define FR_DGRK ALGR(FR_G)    // µ (dead Greek key)\n#define FR_EU   ALGR(FR_H)    // Eu (dead European symbol key)\n#define FR_DSLS ALGR(FR_K)    // ∕ (dead)\n#define FR_PIPE ALGR(FR_L)    // |\n#define FR_INFN ALGR(FR_M)    // ∞\n#define FR_DIV  ALGR(FR_SLSH) // ÷\n#define FR_MUL  ALGR(FR_ASTR) // ×\n#define FR_LEQL ALGR(FR_LABK) // ≤\n#define FR_EZH  ALGR(FR_W)    // ʒ\n#define FR_COPY ALGR(FR_X)    // ©\n#define FR_CCED ALGR(FR_C)    // ç\n#define FR_CEDL ALGR(FR_V)    // ¸ (dead)\n#define FR_DMNS ALGR(FR_B)    // − (dead)\n#define FR_DTIL ALGR(FR_N)    // ~ (dead)\n#define FR_IQUE ALGR(FR_DOT)  // ¿\n#define FR_IEXL ALGR(FR_COMM) // ¡\n#define FR_MDDT ALGR(FR_COLN) // ·\n#define FR_AEQL ALGR(FR_SCLN) // ≃\n#define FR_IBRV S(ALGR(FR_AT))   // ̑ (dead)\n#define FR_DACU S(ALGR(FR_LPRN)) // ˝ (dead)\n#define FR_DGRV S(ALGR(FR_RPRN)) // ̏ (dead)\n#define FR_MDSH S(ALGR(FR_RSQU)) // —\n#define FR_LSAQ S(ALGR(FR_LDAQ)) // ‹\n#define FR_RSAQ S(ALGR(FR_RDAQ)) // ›\n#define FR_RNGA S(ALGR(FR_QUOT)) // ˚ (dead)\n#define FR_TM   S(ALGR(FR_T))    // ™\n#define FR_DOTB S(ALGR(FR_I))    // ̣ (dead)\n#define FR_PERM S(ALGR(FR_P))    // ‰\n#define FR_NBHY S(ALGR(FR_MINS)) // ‑ (non-breaking hyphen)\n#define FR_DDAG S(ALGR(FR_PLUS)) // ‡\n#define FR_MACB S(ALGR(FR_H))    // ˍ (dead)\n#define FR_SQRT S(ALGR(FR_SLSH)) // √\n#define FR_QRTR S(ALGR(FR_ASTR)) // ¼\n#define FR_GEQL S(ALGR(FR_LABK)) // ≥\n#define FR_OGON S(ALGR(FR_V))    // ˛ (dead)\n#define FR_DCMM S(ALGR(FR_COMM)) // ̦ (dead)\n#define FR_NEQL S(ALGR(FR_SCLN)) // ≠\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_french_mac_iso.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_FRENCH_MAC_ISO_KEYCODES_VERSION \"0.0.1\"\n#define QMK_FRENCH_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_FRENCH_MAC_ISO_KEYCODES_VERSION_MAJOR 0\n#define QMK_FRENCH_MAC_ISO_KEYCODES_VERSION_MINOR 0\n#define QMK_FRENCH_MAC_ISO_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define FR_AT   KC_GRV  // @\n#define FR_AMPR KC_1    // &\n#define FR_LEAC KC_2    // é\n#define FR_DQUO KC_3    // \"\n#define FR_QUOT KC_4    // '\n#define FR_LPRN KC_5    // (\n#define FR_SECT KC_6    // §\n#define FR_LEGR KC_7    // è\n#define FR_EXLM KC_8    // !\n#define FR_LCCE KC_9    // ç\n#define FR_LAGR KC_0    // à\n#define FR_RPRN KC_MINS // )\n#define FR_MINS KC_EQL  // -\n#define FR_A    KC_Q    // A\n#define FR_Z    KC_W    // Z\n#define FR_E    KC_E    // E\n#define FR_R    KC_R    // R\n#define FR_T    KC_T    // T\n#define FR_Y    KC_Y    // Y\n#define FR_U    KC_U    // U\n#define FR_I    KC_I    // I\n#define FR_O    KC_O    // O\n#define FR_P    KC_P    // P\n#define FR_CIRC KC_LBRC // ^\n#define FR_DLR  KC_RBRC // $\n#define FR_Q    KC_A    // Q\n#define FR_S    KC_S    // S\n#define FR_D    KC_D    // D\n#define FR_F    KC_F    // F\n#define FR_G    KC_G    // G\n#define FR_H    KC_H    // H\n#define FR_J    KC_J    // J\n#define FR_K    KC_K    // K\n#define FR_L    KC_L    // L\n#define FR_M    KC_SCLN // M\n#define FR_LUGR KC_QUOT // ù\n#define FR_GRV  KC_NUHS // `\n#define FR_LABK KC_NUBS // <\n#define FR_W    KC_Z    // W\n#define FR_X    KC_X    // X\n#define FR_C    KC_C    // C\n#define FR_V    KC_V    // V\n#define FR_B    KC_B    // B\n#define FR_N    KC_N    // N\n#define FR_COMM KC_M    // ,\n#define FR_SCLN KC_COMM // ;\n#define FR_COLN KC_DOT  // :\n#define FR_EQL  KC_SLSH // =\n#define FR_HASH S(FR_AT)   // #\n#define FR_1    S(FR_AMPR) // 1\n#define FR_2    S(FR_LEAC) // 2\n#define FR_3    S(FR_DQUO) // 3\n#define FR_4    S(FR_QUOT) // 4\n#define FR_5    S(FR_LPRN) // 5\n#define FR_6    S(FR_SECT) // 6\n#define FR_7    S(FR_LEGR) // 7\n#define FR_8    S(FR_EXLM) // 8\n#define FR_9    S(FR_LCCE) // 9\n#define FR_0    S(FR_LAGR) // 0\n#define FR_DEG  S(FR_RPRN) // °\n#define FR_UNDS S(FR_MINS) // _\n#define FR_DIAE S(FR_CIRC) // ¨ (dead)\n#define FR_ASTR S(FR_DLR)  // *\n#define FR_PERC S(FR_LUGR) // %\n#define FR_PND  S(FR_GRV)  // £\n#define FR_RABK S(FR_LABK) // >\n#define FR_QUES S(FR_COMM) // ?\n#define FR_DOT  S(FR_SCLN) // .\n#define FR_SLSH S(FR_COLN) // /\n#define FR_PLUS S(FR_EQL)  // +\n#define FR_BULT A(FR_AT)   // •\n#define FR_APPL A(FR_AMPR) //  (Apple logo)\n#define FR_LEDI A(FR_LEAC) // ë\n#define FR_LDQU A(FR_DQUO) // “\n#define FR_LSQU A(FR_QUOT) // ‘\n#define FR_LCBR A(FR_LPRN) // {\n#define FR_PILC A(FR_SECT) // ¶\n#define FR_LDAQ A(FR_LEGR) // «\n#define FR_IEXL A(FR_EXLM) // ¡\n#define FR_CCCE A(FR_LCCE) // Ç\n#define FR_OSTR A(FR_LAGR) // Ø\n#define FR_RCBR A(FR_RPRN) // }\n#define FR_MDSH A(FR_MINS) // —\n#define FR_AE   A(FR_A)    // Æ\n#define FR_CACI A(FR_Z)    // Â\n#define FR_ECIR A(FR_E)    // Ê\n#define FR_REGD A(FR_R)    // ®\n#define FR_DAGG A(FR_T)    // †\n#define FR_CUAC A(FR_Y)    // Ú\n#define FR_MORD A(FR_U)    // º\n#define FR_LICI A(FR_I)    // î\n#define FR_OE   A(FR_O)    // Œ\n#define FR_PI   A(FR_P)    // π\n#define FR_OCIR A(FR_CIRC) // Ô\n#define FR_EURO A(FR_DLR)  // €\n#define FR_DDAG A(FR_Q)    // ‡\n#define FR_COGR A(FR_S)    // Ò\n#define FR_PDIF A(FR_D)    // ∂\n#define FR_FHK  A(FR_F)    // ƒ\n#define FR_FI   A(FR_G)    // ﬁ\n#define FR_CIGR A(FR_H)    // Ì\n#define FR_CIDI A(FR_J)    // Ï\n#define FR_CEGR A(FR_K)    // È\n#define FR_NOT  A(FR_L)    // ¬\n#define FR_MICR A(FR_M)    // µ\n#define FR_CUGR A(FR_LUGR) // Ù\n#define FR_LTEQ A(FR_LABK) // ≤\n#define FR_LSAQ A(FR_W)    // ‹\n#define FR_AEQL A(FR_X)    // ≈\n#define FR_COPY A(FR_C)    // ©\n#define FR_LOZN A(FR_V)    // ◊\n#define FR_SS   A(FR_B)    // ß\n#define FR_TILD A(FR_N)    // ~ (dead)\n#define FR_INFN A(FR_COMM) // ∞\n#define FR_ELLP A(FR_SCLN) // …\n#define FR_DIV  A(FR_COLN) // ÷\n#define FR_NEQL A(FR_EQL)  // ≠\n#define FR_CYDI S(A(FR_AT))   // Ÿ\n#define FR_ACUT S(A(FR_AMPR)) // ´ (dead)\n#define FR_DLQU S(A(FR_LEAC)) // „\n#define FR_LBRC S(A(FR_LPRN)) // [\n#define FR_LARI S(A(FR_SECT)) // å\n#define FR_RDAQ S(A(FR_LEGR)) // »\n#define FR_CUCI S(A(FR_EXLM)) // Û\n#define FR_CAAC S(A(FR_LCCE)) // Á\n#define FR_RBRC S(A(FR_RPRN)) // ]\n#define FR_NDSH S(A(FR_MINS)) // –\n#define FR_CARI S(A(FR_Z))    // Å\n#define FR_SLQU S(A(FR_R))    // ‚\n#define FR_TM   S(A(FR_T))    // ™\n#define FR_FORD S(A(FR_U))    // ª\n#define FR_LIDI S(A(FR_I))    // ï\n#define FR_NARP S(A(FR_P))    // ∏\n#define FR_YEN  S(A(FR_DLR))  // ¥\n#define FR_OMEG S(A(FR_Q))    // Ω\n#define FR_NARS S(A(FR_S))    // ∑\n#define FR_INCR S(A(FR_D))    // ∆\n#define FR_MDDT S(A(FR_F))    // ·\n#define FR_FL   S(A(FR_G))    // ﬂ\n#define FR_CICI S(A(FR_H))    // Î\n#define FR_CIAC S(A(FR_J))    // Í\n#define FR_CEDI S(A(FR_K))    // Ë\n#define FR_PIPE S(A(FR_L))    // |\n#define FR_COAC S(A(FR_M))    // Ó\n#define FR_PERM S(A(FR_LUGR)) // ‰\n#define FR_GTEQ S(A(FR_LABK)) // ≥\n#define FR_RSAQ S(A(FR_W))    // ›\n#define FR_FRSL S(A(FR_X))    // ⁄\n#define FR_CENT S(A(FR_C))    // ¢\n#define FR_SQRT S(A(FR_V))    // √\n#define FR_INTG S(A(FR_B))    // ∫\n#define FR_DLSI S(A(FR_N))    // ı\n#define FR_IQUE S(A(FR_COMM)) // ¿\n#define FR_BSLS S(A(FR_COLN)) // (backslash)\n#define FR_PLMN S(A(FR_EQL))  // ±\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_german.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_GERMAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_GERMAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_GERMAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_GERMAN_KEYCODES_VERSION_MINOR 0\n#define QMK_GERMAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define DE_CIRC KC_GRV  // ^ (dead)\n#define DE_1    KC_1    // 1\n#define DE_2    KC_2    // 2\n#define DE_3    KC_3    // 3\n#define DE_4    KC_4    // 4\n#define DE_5    KC_5    // 5\n#define DE_6    KC_6    // 6\n#define DE_7    KC_7    // 7\n#define DE_8    KC_8    // 8\n#define DE_9    KC_9    // 9\n#define DE_0    KC_0    // 0\n#define DE_SS   KC_MINS // ß\n#define DE_ACUT KC_EQL  // ´ (dead)\n#define DE_Q    KC_Q    // Q\n#define DE_W    KC_W    // W\n#define DE_E    KC_E    // E\n#define DE_R    KC_R    // R\n#define DE_T    KC_T    // T\n#define DE_Z    KC_Y    // Z\n#define DE_U    KC_U    // U\n#define DE_I    KC_I    // I\n#define DE_O    KC_O    // O\n#define DE_P    KC_P    // P\n#define DE_UDIA KC_LBRC // Ü\n#define DE_PLUS KC_RBRC // +\n#define DE_A    KC_A    // A\n#define DE_S    KC_S    // S\n#define DE_D    KC_D    // D\n#define DE_F    KC_F    // F\n#define DE_G    KC_G    // G\n#define DE_H    KC_H    // H\n#define DE_J    KC_J    // J\n#define DE_K    KC_K    // K\n#define DE_L    KC_L    // L\n#define DE_ODIA KC_SCLN // Ö\n#define DE_ADIA KC_QUOT // Ä\n#define DE_HASH KC_NUHS // #\n#define DE_LABK KC_NUBS // <\n#define DE_Y    KC_Z    // Y\n#define DE_X    KC_X    // X\n#define DE_C    KC_C    // C\n#define DE_V    KC_V    // V\n#define DE_B    KC_B    // B\n#define DE_N    KC_N    // N\n#define DE_M    KC_M    // M\n#define DE_COMM KC_COMM // ,\n#define DE_DOT  KC_DOT  // .\n#define DE_MINS KC_SLSH // -\n#define DE_DEG  S(DE_CIRC) // °\n#define DE_EXLM S(DE_1)    // !\n#define DE_DQUO S(DE_2)    // \"\n#define DE_SECT S(DE_3)    // §\n#define DE_DLR  S(DE_4)    // $\n#define DE_PERC S(DE_5)    // %\n#define DE_AMPR S(DE_6)    // &\n#define DE_SLSH S(DE_7)    // /\n#define DE_LPRN S(DE_8)    // (\n#define DE_RPRN S(DE_9)    // )\n#define DE_EQL  S(DE_0)    // =\n#define DE_QUES S(DE_SS)   // ?\n#define DE_GRV  S(DE_ACUT) // ` (dead)\n#define DE_ASTR S(DE_PLUS) // *\n#define DE_QUOT S(DE_HASH) // '\n#define DE_RABK S(DE_LABK) // >\n#define DE_SCLN S(DE_COMM) // ;\n#define DE_COLN S(DE_DOT)  // :\n#define DE_UNDS S(DE_MINS) // _\n#define DE_SUP2 ALGR(DE_2)    // ²\n#define DE_SUP3 ALGR(DE_3)    // ³\n#define DE_LCBR ALGR(DE_7)    // {\n#define DE_LBRC ALGR(DE_8)    // [\n#define DE_RBRC ALGR(DE_9)    // ]\n#define DE_RCBR ALGR(DE_0)    // }\n#define DE_BSLS ALGR(DE_SS)   // (backslash)\n#define DE_AT   ALGR(DE_Q)    // @\n#define DE_EURO ALGR(DE_E)    // €\n#define DE_TILD ALGR(DE_PLUS) // ~\n#define DE_PIPE ALGR(DE_LABK) // |\n#define DE_MICR ALGR(DE_M)    // µ\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_german_mac_iso.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_GERMAN_MAC_ISO_KEYCODES_VERSION \"0.0.1\"\n#define QMK_GERMAN_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_GERMAN_MAC_ISO_KEYCODES_VERSION_MAJOR 0\n#define QMK_GERMAN_MAC_ISO_KEYCODES_VERSION_MINOR 0\n#define QMK_GERMAN_MAC_ISO_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define DE_CIRC KC_GRV  // ^ (dead)\n#define DE_1    KC_1    // 1\n#define DE_2    KC_2    // 2\n#define DE_3    KC_3    // 3\n#define DE_4    KC_4    // 4\n#define DE_5    KC_5    // 5\n#define DE_6    KC_6    // 6\n#define DE_7    KC_7    // 7\n#define DE_8    KC_8    // 8\n#define DE_9    KC_9    // 9\n#define DE_0    KC_0    // 0\n#define DE_SS   KC_MINS // ß\n#define DE_ACUT KC_EQL  // ´ (dead)\n#define DE_Q    KC_Q    // Q\n#define DE_W    KC_W    // W\n#define DE_E    KC_E    // E\n#define DE_R    KC_R    // R\n#define DE_T    KC_T    // T\n#define DE_Z    KC_Y    // Z\n#define DE_U    KC_U    // U\n#define DE_I    KC_I    // I\n#define DE_O    KC_O    // O\n#define DE_P    KC_P    // P\n#define DE_UDIA KC_LBRC // Ü\n#define DE_PLUS KC_RBRC // +\n#define DE_A    KC_A    // A\n#define DE_S    KC_S    // S\n#define DE_D    KC_D    // D\n#define DE_F    KC_F    // F\n#define DE_G    KC_G    // G\n#define DE_H    KC_H    // H\n#define DE_J    KC_J    // J\n#define DE_K    KC_K    // K\n#define DE_L    KC_L    // L\n#define DE_ODIA KC_SCLN // Ö\n#define DE_ADIA KC_QUOT // Ä\n#define DE_HASH KC_NUHS // #\n#define DE_LABK KC_NUBS // <\n#define DE_Y    KC_Z    // Y\n#define DE_X    KC_X    // X\n#define DE_C    KC_C    // C\n#define DE_V    KC_V    // V\n#define DE_B    KC_B    // B\n#define DE_N    KC_N    // N\n#define DE_M    KC_M    // M\n#define DE_COMM KC_COMM // ,\n#define DE_DOT  KC_DOT  // .\n#define DE_MINS KC_SLSH // -\n#define DE_DEG  S(DE_CIRC) // °\n#define DE_EXLM S(DE_1)    // !\n#define DE_DQUO S(DE_2)    // \"\n#define DE_SECT S(DE_3)    // §\n#define DE_DLR  S(DE_4)    // $\n#define DE_PERC S(DE_5)    // %\n#define DE_AMPR S(DE_6)    // &\n#define DE_SLSH S(DE_7)    // /\n#define DE_LPRN S(DE_8)    // (\n#define DE_RPRN S(DE_9)    // )\n#define DE_EQL  S(DE_0)    // =\n#define DE_QUES S(DE_SS)   // ?\n#define DE_GRV  S(DE_ACUT) // ` (dead)\n#define DE_ASTR S(DE_PLUS) // *\n#define DE_QUOT S(DE_HASH) // '\n#define DE_RABK S(DE_LABK) // >\n#define DE_SCLN S(DE_COMM) // ;\n#define DE_COLN S(DE_DOT)  // :\n#define DE_UNDS S(DE_MINS) // _\n#define DE_DLQU A(DE_CIRC) // „\n#define DE_IEXL A(DE_1)    // ¡\n#define DE_LDQU A(DE_2)    // “\n#define DE_PILC A(DE_3)    // ¶\n#define DE_CENT A(DE_4)    // ¢\n#define DE_LBRC A(DE_5)    // [\n#define DE_RBRC A(DE_6)    // ]\n#define DE_PIPE A(DE_7)    // |\n#define DE_LCBR A(DE_8)    // {\n#define DE_RCBR A(DE_9)    // }\n#define DE_NEQL A(DE_0)    // ≠\n#define DE_IQUE A(DE_SS)   // ¿\n#define DE_LDAQ A(DE_Q)    // «\n#define DE_NARS A(DE_W)    // ∑\n#define DE_EURO A(DE_E)    // €\n#define DE_REGD A(DE_R)    // ®\n#define DE_DAGG A(DE_T)    // †\n#define DE_OMEG A(DE_Z)    // Ω\n#define DE_DIAE A(DE_U)    // ¨ (dead)\n#define DE_FRSL A(DE_I)    // ⁄\n#define DE_OSTR A(DE_O)    // Ø\n#define DE_PI   A(DE_P)    // π\n#define DE_BULT A(DE_UDIA) // •\n#define DE_PLMN A(DE_PLUS) // ±\n#define DE_ARNG A(DE_A)    // Å\n#define DE_SLQU A(DE_S)    // ‚\n#define DE_PDIF A(DE_D)    // ∂\n#define DE_FHK  A(DE_F)    // ƒ\n#define DE_COPY A(DE_G)    // ©\n#define DE_FORD A(DE_H)    // ª\n#define DE_MORD A(DE_J)    // º\n#define DE_INCR A(DE_K)    // ∆\n#define DE_AT   A(DE_L)    // @\n#define DE_OE   A(DE_ODIA) // Œ\n#define DE_AE   A(DE_ADIA) // Æ\n#define DE_LSQU A(DE_HASH) // ‘\n#define DE_LTEQ A(DE_LABK) // ≤\n#define DE_YEN  A(DE_Y)    // ¥\n#define DE_AEQL A(DE_X)    // ≈\n#define DE_CCCE A(DE_C)    // Ç\n#define DE_SQRT A(DE_V)    // √\n#define DE_INTG A(DE_B)    // ∫\n#define DE_TILD A(DE_N)    // ~ (dead)\n#define DE_MICR A(DE_M)    // µ\n#define DE_INFN A(DE_COMM) // ∞\n#define DE_ELLP A(DE_DOT)  // …\n#define DE_NDSH A(DE_MINS) // –\n#define DE_NOT  S(A(DE_1))    // ¬\n#define DE_RDQU S(A(DE_2))    // ”\n#define DE_PND  S(A(DE_4))    // £\n#define DE_FI   S(A(DE_5))    // ﬁ\n#define DE_BSLS S(A(DE_7))    // (backslash)\n#define DE_STIL S(A(DE_8))    // ˜\n#define DE_MDDT S(A(DE_9))    // ·\n#define DE_MACR S(A(DE_0))    // ¯\n#define DE_DOTA S(A(DE_SS))   // ˙\n#define DE_RNGA S(A(DE_ACUT)) // ˚\n#define DE_RDAQ S(A(DE_Q))    // »\n#define DE_PERM S(A(DE_E))    // ‰\n#define DE_CEDL S(A(DE_R))    // ¸\n#define DE_DACU S(A(DE_T))    // ˝\n#define DE_CARN S(A(DE_Z))    // ˇ\n#define DE_AACU S(A(DE_U))    // Á\n#define DE_UCIR S(A(DE_I))    // Û\n#define DE_NARP S(A(DE_P))    // ∏\n#define DE_APPL S(A(DE_PLUS)) //  (Apple logo)\n#define DE_IACU S(A(DE_S))    // Í\n#define DE_TM   S(A(DE_D))    // ™\n#define DE_IDIA S(A(DE_F))    // Ï\n#define DE_IGRV S(A(DE_G))    // Ì\n#define DE_OACU S(A(DE_H))    // Ó\n#define DE_DLSI S(A(DE_J))    // ı\n#define DE_FL   S(A(DE_L))    // ﬂ\n#define DE_GTEQ S(A(DE_LABK)) // ≥\n#define DE_DDAG S(A(DE_Y))    // ‡\n#define DE_UGRV S(A(DE_X))    // Ù\n#define DE_LOZN S(A(DE_V))    // ◊\n#define DE_LSAQ S(A(DE_B))    // ‹\n#define DE_RSAQ S(A(DE_N))    // ›\n#define DE_BREV S(A(DE_M))    // ˘\n#define DE_OGON S(A(DE_COMM)) // ˛\n#define DE_DIV  S(A(DE_DOT))  // ÷\n#define DE_MDSH S(A(DE_MINS)) // —\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_greek.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_GREEK_KEYCODES_VERSION \"0.0.1\"\n#define QMK_GREEK_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_GREEK_KEYCODES_VERSION_MAJOR 0\n#define QMK_GREEK_KEYCODES_VERSION_MINOR 0\n#define QMK_GREEK_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define GR_GRV  KC_GRV  // `\n#define GR_1    KC_1    // 1\n#define GR_2    KC_2    // 2\n#define GR_3    KC_3    // 3\n#define GR_4    KC_4    // 4\n#define GR_5    KC_5    // 5\n#define GR_6    KC_6    // 6\n#define GR_7    KC_7    // 7\n#define GR_8    KC_8    // 8\n#define GR_9    KC_9    // 9\n#define GR_0    KC_0    // 0\n#define GR_MINS KC_MINS // -\n#define GR_EQL  KC_EQL  // =\n#define GR_SCLN KC_Q    // ;\n#define GR_FSIG KC_W    // ς\n#define GR_EPSL KC_E    // Ε\n#define GR_RHO  KC_R    // Ρ\n#define GR_TAU  KC_T    // Τ\n#define GR_UPSL KC_Y    // Υ\n#define GR_THET KC_U    // Θ\n#define GR_IOTA KC_I    // Ι\n#define GR_OMCR KC_O    // Ο\n#define GR_PI   KC_P    // Π\n#define GR_LBRC KC_LBRC // [\n#define GR_RBRC KC_RBRC // ]\n#define GR_ALPH KC_A    // Α\n#define GR_SIGM KC_S    // Σ\n#define GR_DELT KC_D    // Δ\n#define GR_PHI  KC_F    // Φ\n#define GR_GAMM KC_G    // Γ\n#define GR_ETA  KC_H    // Η\n#define GR_XI   KC_J    // Ξ\n#define GR_KAPP KC_K    // Κ\n#define GR_LAMB KC_L    // Λ\n#define GR_TONS KC_SCLN // ΄ (dead)\n#define GR_QUOT KC_QUOT // '\n#define GR_BSLS KC_NUHS // (backslash)\n#define GR_ZETA KC_Z    // Ζ\n#define GR_CHI  KC_X    // Χ\n#define GR_PSI  KC_C    // Ψ\n#define GR_OMEG KC_V    // Ω\n#define GR_BETA KC_B    // Β\n#define GR_NU   KC_N    // Ν\n#define GR_MU   KC_M    // Μ\n#define GR_COMM KC_COMM // ,\n#define GR_DOT  KC_DOT  // .\n#define GR_SLSH KC_SLSH // /\n#define GR_TILD S(GR_GRV)  // ~\n#define GR_EXLM S(GR_1)    // !\n#define GR_AT   S(GR_2)    // @\n#define GR_HASH S(GR_3)    // #\n#define GR_DLR  S(GR_4)    // $\n#define GR_PERC S(GR_5)    // %\n#define GR_CIRC S(GR_6)    // ^\n#define GR_AMPR S(GR_7)    // &\n#define GR_ASTR S(GR_8)    // *\n#define GR_LPRN S(GR_9)    // (\n#define GR_RPRN S(GR_0)    // )\n#define GR_UNDS S(GR_MINS) // _\n#define GR_PLUS S(GR_EQL)  // +\n#define GR_COLN S(GR_SCLN) // :\n#define GR_DIAT S(GR_FSIG) // ΅ (dead)\n#define GR_LCBR S(GR_LBRC) // {\n#define GR_RCBR S(GR_RBRC) // }\n#define GR_DIAE S(GR_TONS) // ¨ (dead)\n#define GR_DQUO S(GR_QUOT) // \"\n#define GR_PIPE S(GR_BSLS) // |\n#define GR_LABK S(GR_COMM) // <\n#define GR_RABK S(GR_DOT)  // >\n#define GR_QUES S(GR_SLSH) // ?\n#define GR_SUP2 ALGR(GR_2)    // ²\n#define GR_SUP3 ALGR(GR_3)    // ³\n#define GR_PND  ALGR(GR_4)    // £\n#define GR_SECT ALGR(GR_5)    // §\n#define GR_PILC ALGR(GR_6)    // ¶\n#define GR_CURR ALGR(GR_8)    // ¤\n#define GR_BRKP ALGR(GR_9)    // ¦\n#define GR_DEG  ALGR(GR_0)    // °\n#define GR_PLMN ALGR(GR_MINS) // ±\n#define GR_HALF ALGR(GR_EQL)  // ½\n#define GR_EURO ALGR(GR_EPSL) // €\n#define GR_REGD ALGR(GR_RHO)  // ®\n#define GR_YEN  ALGR(GR_UPSL) // ¥\n#define GR_LDAQ ALGR(GR_LBRC) // «\n#define GR_RDAQ ALGR(GR_RBRC) // »\n#define GR_NOT  ALGR(GR_BSLS) // ¬\n#define GR_COPY ALGR(GR_PSI)  // ©\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_hebrew.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_HEBREW_KEYCODES_VERSION \"0.0.1\"\n#define QMK_HEBREW_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_HEBREW_KEYCODES_VERSION_MAJOR 0\n#define QMK_HEBREW_KEYCODES_VERSION_MINOR 0\n#define QMK_HEBREW_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define IL_SCLN KC_GRV  // ;\n#define IL_1    KC_1    // 1\n#define IL_2    KC_2    // 2\n#define IL_3    KC_3    // 3\n#define IL_4    KC_4    // 4\n#define IL_5    KC_5    // 5\n#define IL_6    KC_6    // 6\n#define IL_7    KC_7    // 7\n#define IL_8    KC_8    // 8\n#define IL_9    KC_9    // 9\n#define IL_0    KC_0    // 0\n#define IL_MINS KC_MINS // -\n#define IL_EQL  KC_EQL  // =\n#define IL_SLSH KC_Q    // /\n#define IL_QUOT KC_W    // '\n#define IL_QOF  KC_E    // ק\n#define IL_RESH KC_R    // ר\n#define IL_ALEF KC_T    // א\n#define IL_TET  KC_Y    // ט\n#define IL_VAV  KC_U    // ו\n#define IL_FNUN KC_I    // ן\n#define IL_FMEM KC_O    // ם\n#define IL_PE   KC_P    // פ\n#define IL_RBRC KC_LBRC // ]\n#define IL_LBRC KC_RBRC // [\n#define IL_SHIN KC_A    // ש\n#define IL_DALT KC_S    // ד\n#define IL_GIML KC_D    // ג\n#define IL_KAF  KC_F    // כ\n#define IL_AYIN KC_G    // ע\n#define IL_YOD  KC_H    // י\n#define IL_HET  KC_J    // ח\n#define IL_LAMD KC_K    // ל\n#define IL_FKAF KC_L    // ך\n#define IL_FPE  KC_SCLN // ף\n#define IL_COMM KC_QUOT // ,\n#define IL_BSLS KC_NUHS // (backslash)\n#define IL_ZAYN KC_Z    // ז\n#define IL_SMKH KC_X    // ס\n#define IL_BET  KC_C    // ב\n#define IL_HE   KC_V    // ה\n#define IL_NUN  KC_B    // נ\n#define IL_MEM  KC_N    // מ\n#define IL_TSDI KC_M    // צ\n#define IL_TAV  KC_COMM // ת\n#define IL_FTSD KC_DOT  // ץ\n#define IL_DOT  KC_SLSH // .\n#define IL_TILD S(IL_SCLN) // ~\n#define IL_EXLM S(IL_1)    // !\n#define IL_AT   S(IL_2)    // @\n#define IL_PND  S(IL_3)    // #\n#define IL_DLR  S(IL_4)    // $\n#define IL_PERC S(IL_5)    // %\n#define IL_CIRC S(IL_6)    // ^\n#define IL_AMPR S(IL_7)    // &\n#define IL_ASTR S(IL_8)    // *\n#define IL_RPRN S(IL_9)    // )\n#define IL_LPRN S(IL_0)    // (\n#define IL_UNDS S(IL_MINS) // _\n#define IL_PLUS S(IL_EQL)  // +\n#define IL_RCBR S(IL_RBRC) // }\n#define IL_LCBR S(IL_LBRC) // {\n#define IL_COLN S(IL_FPE)  // :\n#define IL_DQUO S(IL_COMM) // \"\n#define IL_PIPE S(IL_BSLS) // |\n#define IL_RABK S(IL_TAV)  // >\n#define IL_LABK S(IL_FTSD) // <\n#define IL_QUES S(IL_DOT)  // ?\n#define IL_EURO ALGR(IL_3)    // €\n#define IL_SHKL ALGR(IL_4)    // ₪\n#define IL_DEG  ALGR(IL_5)    // °\n#define IL_MUL  ALGR(IL_8)    // ×\n#define IL_DVAV ALGR(IL_TET)  // װ\n#define IL_VYOD ALGR(IL_AYIN) // ױ\n#define IL_DYOD ALGR(IL_YOD)  // ײ\n#define IL_DIV  ALGR(IL_DOT)  // ÷\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_hungarian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_HUNGARIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_HUNGARIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_HUNGARIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_HUNGARIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_HUNGARIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define HU_0    KC_GRV  // 0\n#define HU_1    KC_1    // 1\n#define HU_2    KC_2    // 2\n#define HU_3    KC_3    // 3\n#define HU_4    KC_4    // 4\n#define HU_5    KC_5    // 5\n#define HU_6    KC_6    // 6\n#define HU_7    KC_7    // 7\n#define HU_8    KC_8    // 8\n#define HU_9    KC_9    // 9\n#define HU_ODIA KC_0    // Ö\n#define HU_UDIA KC_MINS // Ü\n#define HU_OACU KC_EQL  // Ó\n#define HU_Q    KC_Q    // Q\n#define HU_W    KC_W    // W\n#define HU_E    KC_E    // E\n#define HU_R    KC_R    // R\n#define HU_T    KC_T    // T\n#define HU_Z    KC_Y    // Z\n#define HU_U    KC_U    // U\n#define HU_I    KC_I    // I\n#define HU_O    KC_O    // O\n#define HU_P    KC_P    // P\n#define HU_ODAC KC_LBRC // Ő\n#define HU_UACU KC_RBRC // Ú\n#define HU_A    KC_A    // A\n#define HU_S    KC_S    // S\n#define HU_D    KC_D    // D\n#define HU_F    KC_F    // F\n#define HU_G    KC_G    // G\n#define HU_H    KC_H    // H\n#define HU_J    KC_J    // J\n#define HU_K    KC_K    // K\n#define HU_L    KC_L    // L\n#define HU_EACU KC_SCLN // É\n#define HU_AACU KC_QUOT // Á\n#define HU_UDAC KC_NUHS // Ű\n#define HU_IACU KC_NUBS // Í\n#define HU_Y    KC_Z    // Y\n#define HU_X    KC_X    // X\n#define HU_C    KC_C    // C\n#define HU_V    KC_V    // V\n#define HU_B    KC_B    // B\n#define HU_N    KC_N    // N\n#define HU_M    KC_M    // M\n#define HU_COMM KC_COMM // ,\n#define HU_DOT  KC_DOT  // .\n#define HU_MINS KC_SLSH // -\n#define HU_SECT S(HU_0)    // §\n#define HU_QUOT S(HU_1)    // '\n#define HU_DQUO S(HU_2)    // \"\n#define HU_PLUS S(HU_3)    // +\n#define HU_EXLM S(HU_4)    // !\n#define HU_PERC S(HU_5)    // %\n#define HU_SLSH S(HU_6)    // /\n#define HU_EQL  S(HU_7)    // =\n#define HU_LPRN S(HU_8)    // (\n#define HU_RPRN S(HU_9)    // )\n#define HU_QUES S(HU_COMM) // ?\n#define HU_COLN S(HU_DOT)  // :\n#define HU_UNDS S(HU_MINS) // _\n#define HU_TILD ALGR(HU_1)    // ~\n#define HU_CARN ALGR(HU_2)    // ˇ (dead)\n#define HU_CIRC ALGR(HU_3)    // ^ (dead)\n#define HU_BREV ALGR(HU_4)    // ˘ (dead)\n#define HU_RNGA ALGR(HU_5)    // ° (dead)\n#define HU_OGON ALGR(HU_6)    // ˛ (dead)\n#define HU_GRV  ALGR(HU_7)    // `\n#define HU_DOTA ALGR(HU_8)    // ˙ (dead)\n#define HU_ACUT ALGR(HU_9)    // ´ (dead)\n#define HU_DACU ALGR(HU_ODIA) // ˝ (dead)\n#define HU_DIAE ALGR(HU_UDIA) // ¨ (dead)\n#define HU_CEDL ALGR(HU_OACU) // ¸ (dead)\n#define HU_BSLS ALGR(HU_Q)    // (backslash)\n#define HU_PIPE ALGR(HU_W)    // |\n#define HU_CADI ALGR(HU_E)    // Ä\n#define HU_EURO ALGR(HU_U)    // €\n#define HU_DIV  ALGR(HU_ODAC) // ÷\n#define HU_MUL  ALGR(HU_UACU) // ×\n#define HU_LADI ALGR(HU_A)    // ä\n#define HU_LDST ALGR(HU_S)    // đ\n#define HU_CDST ALGR(HU_D)    // Đ\n#define HU_LBRC ALGR(HU_F)    // [\n#define HU_RBRC ALGR(HU_G)    // ]\n#define HU_LLST ALGR(HU_K)    // ł\n#define HU_CLST ALGR(HU_L)    // Ł\n#define HU_DLR  ALGR(HU_EACU) // $\n#define HU_SS   ALGR(HU_AACU) // ß\n#define HU_CURR ALGR(HU_UDAC) // ¤\n#define HU_LABK ALGR(HU_IACU) // <\n#define HU_RABK ALGR(HU_Y)    // >\n#define HU_HASH ALGR(HU_X)    // #\n#define HU_AMPR ALGR(HU_C)    // &\n#define HU_AT   ALGR(HU_V)    // @\n#define HU_LCBR ALGR(HU_B)    // {\n#define HU_RCBR ALGR(HU_N)    // }\n#define HU_SCLN ALGR(HU_COMM) // ;\n#define HU_ASTR ALGR(HU_MINS) // *\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_icelandic.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_ICELANDIC_KEYCODES_VERSION \"0.0.1\"\n#define QMK_ICELANDIC_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_ICELANDIC_KEYCODES_VERSION_MAJOR 0\n#define QMK_ICELANDIC_KEYCODES_VERSION_MINOR 0\n#define QMK_ICELANDIC_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define IS_RNGA KC_GRV  // ° (dead)\n#define IS_1    KC_1    // 1\n#define IS_2    KC_2    // 2\n#define IS_3    KC_3    // 3\n#define IS_4    KC_4    // 4\n#define IS_5    KC_5    // 5\n#define IS_6    KC_6    // 6\n#define IS_7    KC_7    // 7\n#define IS_8    KC_8    // 8\n#define IS_9    KC_9    // 9\n#define IS_0    KC_0    // 0\n#define IS_ODIA KC_MINS // Ö\n#define IS_MINS KC_EQL  // -\n#define IS_Q    KC_Q    // Q\n#define IS_W    KC_W    // W\n#define IS_E    KC_E    // E\n#define IS_R    KC_R    // R\n#define IS_T    KC_T    // T\n#define IS_Y    KC_Y    // Y\n#define IS_U    KC_U    // U\n#define IS_I    KC_I    // I\n#define IS_O    KC_O    // O\n#define IS_P    KC_P    // P\n#define IS_ETH  KC_LBRC // Ð\n#define IS_QUOT KC_RBRC // '\n#define IS_A    KC_A    // A\n#define IS_S    KC_S    // S\n#define IS_D    KC_D    // D\n#define IS_F    KC_F    // F\n#define IS_G    KC_G    // G\n#define IS_H    KC_H    // H\n#define IS_J    KC_J    // J\n#define IS_K    KC_K    // K\n#define IS_L    KC_L    // L\n#define IS_AE   KC_SCLN // Æ\n#define IS_ACUT KC_QUOT // ´ (dead)\n#define IS_PLUS KC_NUHS // +\n#define IS_LABK KC_NUBS // <\n#define IS_Z    KC_Z    // Z\n#define IS_X    KC_X    // X\n#define IS_C    KC_C    // C\n#define IS_V    KC_V    // V\n#define IS_B    KC_B    // B\n#define IS_N    KC_N    // N\n#define IS_M    KC_M    // M\n#define IS_COMM KC_COMM // ,\n#define IS_DOT  KC_DOT  // .\n#define IS_THRN KC_SLSH // Þ\n#define IS_DIAE S(IS_RNGA) // ¨ (dead)\n#define IS_EXLM S(IS_1)    // !\n#define IS_DQUO S(IS_2)    // \"\n#define IS_HASH S(IS_3)    // #\n#define IS_DLR  S(IS_4)    // $\n#define IS_PERC S(IS_5)    // %\n#define IS_AMPR S(IS_6)    // &\n#define IS_SLSH S(IS_7)    // /\n#define IS_LPRN S(IS_8)    // (\n#define IS_RPRN S(IS_9)    // )\n#define IS_EQL  S(IS_0)    // =\n#define IS_UNDS S(IS_MINS) // _\n#define IS_QUES S(IS_QUOT) // ?\n#define IS_ASTR S(IS_PLUS) // *\n#define IS_RABK S(IS_LABK) // >\n#define IS_SCLN S(IS_COMM) // ;\n#define IS_COLN S(IS_DOT)  // :\n#define IS_DEG  ALGR(IS_RNGA) // °\n#define IS_LCBR ALGR(IS_7)    // {\n#define IS_LBRC ALGR(IS_8)    // [\n#define IS_RBRC ALGR(IS_9)    // ]\n#define IS_RCBR ALGR(IS_0)    // }\n#define IS_BSLS ALGR(IS_ODIA) // (backslash)\n#define IS_AT   ALGR(IS_Q)    // @\n#define IS_EURO ALGR(IS_E)    // €\n#define IS_TILD ALGR(IS_QUOT) // ~\n#define IS_CIRC ALGR(IS_ACUT) // ^ (dead)\n#define IS_GRV  ALGR(IS_PLUS) // ` (dead)\n#define IS_PIPE ALGR(IS_LABK) // |\n#define IS_MICR ALGR(IS_M)    // µ\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_irish.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_IRISH_KEYCODES_VERSION \"0.0.1\"\n#define QMK_IRISH_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_IRISH_KEYCODES_VERSION_MAJOR 0\n#define QMK_IRISH_KEYCODES_VERSION_MINOR 0\n#define QMK_IRISH_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define IE_GRV  KC_GRV  // `\n#define IE_1    KC_1    // 1\n#define IE_2    KC_2    // 2\n#define IE_3    KC_3    // 3\n#define IE_4    KC_4    // 4\n#define IE_5    KC_5    // 5\n#define IE_6    KC_6    // 6\n#define IE_7    KC_7    // 7\n#define IE_8    KC_8    // 8\n#define IE_9    KC_9    // 9\n#define IE_0    KC_0    // 0\n#define IE_MINS KC_MINS // -\n#define IE_EQL  KC_EQL  // =\n#define IE_Q    KC_Q    // Q\n#define IE_W    KC_W    // W\n#define IE_E    KC_E    // E\n#define IE_R    KC_R    // R\n#define IE_T    KC_T    // T\n#define IE_Y    KC_Y    // Y\n#define IE_U    KC_U    // U\n#define IE_I    KC_I    // I\n#define IE_O    KC_O    // O\n#define IE_P    KC_P    // P\n#define IE_LBRC KC_LBRC // [\n#define IE_RBRC KC_RBRC // ]\n#define IE_A    KC_A    // A\n#define IE_S    KC_S    // S\n#define IE_D    KC_D    // D\n#define IE_F    KC_F    // F\n#define IE_G    KC_G    // G\n#define IE_H    KC_H    // H\n#define IE_J    KC_J    // J\n#define IE_K    KC_K    // K\n#define IE_L    KC_L    // L\n#define IE_SCLN KC_SCLN // ;\n#define IE_QUOT KC_QUOT // '\n#define IE_HASH KC_NUHS // #\n#define IE_BSLS KC_NUBS // (backslash)\n#define IE_Z    KC_Z    // Z\n#define IE_X    KC_X    // X\n#define IE_C    KC_C    // C\n#define IE_V    KC_V    // V\n#define IE_B    KC_B    // B\n#define IE_N    KC_N    // N\n#define IE_M    KC_M    // M\n#define IE_COMM KC_COMM // ,\n#define IE_DOT  KC_DOT  // .\n#define IE_SLSH KC_SLSH // /\n#define IE_NOT  S(IE_GRV)  // ¬\n#define IE_EXLM S(IE_1)    // !\n#define IE_DQUO S(IE_2)    // \"\n#define IE_PND  S(IE_3)    // £\n#define IE_DLR  S(IE_4)    // $\n#define IE_PERC S(IE_5)    // %\n#define IE_CIRC S(IE_6)    // ^\n#define IE_AMPR S(IE_7)    // &\n#define IE_ASTR S(IE_8)    // *\n#define IE_LPRN S(IE_9)    // (\n#define IE_RPRN S(IE_0)    // )\n#define IE_UNDS S(IE_MINS) // _\n#define IE_PLUS S(IE_EQL)  // +\n#define IE_LCBR S(IE_LBRC) // {\n#define IE_RCBR S(IE_RBRC) // }\n#define IE_COLN S(IE_SCLN) // :\n#define IE_AT   S(IE_QUOT) // @\n#define IE_TILD S(IE_HASH) // ~\n#define IE_PIPE S(IE_BSLS) // |\n#define IE_LABK S(IE_COMM) // <\n#define IE_RABK S(IE_DOT)  // >\n#define IE_QUES S(IE_SLSH) // ?\n#define IE_BRKP ALGR(IE_GRV)  // ¦\n#define IE_EURO ALGR(IE_4)    // €\n#define IE_EACU ALGR(IE_E)    // É\n#define IE_UACU ALGR(IE_U)    // Ú\n#define IE_IACU ALGR(IE_I)    // Í\n#define IE_OACU ALGR(IE_O)    // Ó\n#define IE_AACU ALGR(IE_A)    // Á\n#define IE_ACUT ALGR(IE_QUOT) // ´ (dead)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_italian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_ITALIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_ITALIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_ITALIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_ITALIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_ITALIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define IT_BSLS KC_GRV  // (backslash)\n#define IT_1    KC_1    // 1\n#define IT_2    KC_2    // 2\n#define IT_3    KC_3    // 3\n#define IT_4    KC_4    // 4\n#define IT_5    KC_5    // 5\n#define IT_6    KC_6    // 6\n#define IT_7    KC_7    // 7\n#define IT_8    KC_8    // 8\n#define IT_9    KC_9    // 9\n#define IT_0    KC_0    // 0\n#define IT_QUOT KC_MINS // '\n#define IT_IGRV KC_EQL  // ì\n#define IT_Q    KC_Q    // Q\n#define IT_W    KC_W    // W\n#define IT_E    KC_E    // E\n#define IT_R    KC_R    // R\n#define IT_T    KC_T    // T\n#define IT_Y    KC_Y    // Y\n#define IT_U    KC_U    // U\n#define IT_I    KC_I    // I\n#define IT_O    KC_O    // O\n#define IT_P    KC_P    // P\n#define IT_EGRV KC_LBRC // è\n#define IT_PLUS KC_RBRC // +\n#define IT_A    KC_A    // A\n#define IT_S    KC_S    // S\n#define IT_D    KC_D    // D\n#define IT_F    KC_F    // F\n#define IT_G    KC_G    // G\n#define IT_H    KC_H    // H\n#define IT_J    KC_J    // J\n#define IT_K    KC_K    // K\n#define IT_L    KC_L    // L\n#define IT_OGRV KC_SCLN // ò\n#define IT_AGRV KC_QUOT // à\n#define IT_UGRV KC_NUHS // ù\n#define IT_LABK KC_NUBS // <\n#define IT_Z    KC_Z    // Z\n#define IT_X    KC_X    // X\n#define IT_C    KC_C    // C\n#define IT_B    KC_B    // B\n#define IT_V    KC_V    // V\n#define IT_N    KC_N    // N\n#define IT_M    KC_M    // M\n#define IT_COMM KC_COMM // ,\n#define IT_DOT  KC_DOT  // .\n#define IT_MINS KC_SLSH // -\n#define IT_PIPE S(IT_BSLS) // |\n#define IT_EXLM S(IT_1)    // !\n#define IT_DQUO S(IT_2)    // \"\n#define IT_PND  S(IT_3)    // £\n#define IT_DLR  S(IT_4)    // $\n#define IT_PERC S(IT_5)    // %\n#define IT_AMPR S(IT_6)    // &\n#define IT_SLSH S(IT_7)    // /\n#define IT_LPRN S(IT_8)    // (\n#define IT_RPRN S(IT_9)    // )\n#define IT_EQL  S(IT_0)    // =\n#define IT_QUES S(IT_QUOT) // ?\n#define IT_CIRC S(IT_IGRV) // ^\n#define IT_EACU S(IT_EGRV) // é\n#define IT_ASTR S(IT_PLUS) // *\n#define IT_CCED S(IT_OGRV) // ç\n#define IT_DEG  S(IT_AGRV) // °\n#define IT_SECT S(IT_UGRV) // §\n#define IT_RABK S(IT_LABK) // >\n#define IT_COLN S(IT_DOT)  // :\n#define IT_SCLN S(IT_COMM) // ;\n#define IT_UNDS S(IT_MINS) // _\n#define IT_EURO ALGR(IT_E)    // €\n#define IT_LBRC ALGR(IT_EGRV) // [\n#define IT_RBRC ALGR(IT_PLUS) // ]\n#define IT_AT   ALGR(IT_OGRV) // @\n#define IT_HASH ALGR(IT_AGRV) // #\n#define IT_LCBR S(ALGR(IT_EGRV)) // {\n#define IT_RCBR S(ALGR(IT_PLUS)) // }\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_italian_mac_ansi.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_ITALIAN_MAC_ANSI_KEYCODES_VERSION \"0.0.1\"\n#define QMK_ITALIAN_MAC_ANSI_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_ITALIAN_MAC_ANSI_KEYCODES_VERSION_MAJOR 0\n#define QMK_ITALIAN_MAC_ANSI_KEYCODES_VERSION_MINOR 0\n#define QMK_ITALIAN_MAC_ANSI_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define IT_LABK KC_GRV  // <\n#define IT_1    KC_1    // 1\n#define IT_2    KC_2    // 2\n#define IT_3    KC_3    // 3\n#define IT_4    KC_4    // 4\n#define IT_5    KC_5    // 5\n#define IT_6    KC_6    // 6\n#define IT_7    KC_7    // 7\n#define IT_8    KC_8    // 8\n#define IT_9    KC_9    // 9\n#define IT_0    KC_0    // 0\n#define IT_QUOT KC_MINS // '\n#define IT_IGRV KC_EQL  // ì\n#define IT_Q    KC_Q    // Q\n#define IT_W    KC_W    // W\n#define IT_E    KC_E    // E\n#define IT_R    KC_R    // R\n#define IT_T    KC_T    // T\n#define IT_Y    KC_Y    // Y\n#define IT_U    KC_U    // U\n#define IT_I    KC_I    // I\n#define IT_O    KC_O    // O\n#define IT_P    KC_P    // P\n#define IT_EGRV KC_LBRC // è\n#define IT_PLUS KC_RBRC // +\n#define IT_UGRV KC_BSLS // ù\n#define IT_A    KC_A    // A\n#define IT_S    KC_S    // S\n#define IT_D    KC_D    // D\n#define IT_F    KC_F    // F\n#define IT_G    KC_G    // G\n#define IT_H    KC_H    // H\n#define IT_J    KC_J    // J\n#define IT_K    KC_K    // K\n#define IT_L    KC_L    // L\n#define IT_OGRV KC_SCLN // ò\n#define IT_AGRV KC_QUOT // à\n#define IT_BSLS KC_NUBS // (backslash, not physically present)\n#define IT_Z    KC_Z    // Z\n#define IT_X    KC_X    // X\n#define IT_C    KC_C    // C\n#define IT_V    KC_V    // V\n#define IT_B    KC_B    // B\n#define IT_N    KC_N    // N\n#define IT_M    KC_M    // M\n#define IT_COMM KC_COMM // ,\n#define IT_DOT  KC_DOT  // .\n#define IT_MINS KC_SLSH // -\n#define IT_RABK S(IT_LABK) // >\n#define IT_EXLM S(IT_1)    // !\n#define IT_DQUO S(IT_2)    // \"\n#define IT_PND  S(IT_3)    // £\n#define IT_DLR  S(IT_4)    // $\n#define IT_PERC S(IT_5)    // %\n#define IT_AMPR S(IT_6)    // &\n#define IT_SLSH S(IT_7)    // /\n#define IT_LPRN S(IT_8)    // (\n#define IT_RPRN S(IT_9)    // )\n#define IT_EQL  S(IT_0)    // =\n#define IT_QUES S(IT_QUOT) // ?\n#define IT_CIRC S(IT_IGRV) // ^\n#define IT_EACU S(IT_EGRV) // é\n#define IT_ASTR S(IT_PLUS) // *\n#define IT_SECT S(IT_UGRV) // §\n#define IT_LCCE S(IT_OGRV) // ç\n#define IT_DEG  S(IT_AGRV) // °\n#define IT_PIPE S(IT_BSLS) // | (not physically present)\n#define IT_SCLN S(IT_COMM) // ;\n#define IT_COLN S(IT_DOT)  // :\n#define IT_UNDS S(IT_MINS) // _\n#define IT_LTEQ A(IT_LABK) // ≤\n#define IT_LDAQ A(IT_1)    // «\n#define IT_LDQU A(IT_2)    // “\n#define IT_LSQU A(IT_3)    // ‘\n#define IT_YEN  A(IT_4)    // ¥\n#define IT_TILD A(IT_5)    // ~\n#define IT_LSAQ A(IT_6)    // ‹\n#define IT_DIV  A(IT_7)    // ÷\n#define IT_ACUT A(IT_8)    // ´ (dead)\n#define IT_DGRV A(IT_9)    // ` (dead)\n#define IT_NEQL A(IT_0)    // ≠\n#define IT_IEXL A(IT_QUOT) // ¡\n#define IT_DCIR A(IT_IGRV) // ˆ (dead)\n#define IT_DLQU A(IT_Q)    // „\n#define IT_OMEG A(IT_W)    // Ω\n#define IT_EURO A(IT_E)    // €\n#define IT_REGD A(IT_R)    // ®\n#define IT_TM   A(IT_T)    // ™\n#define IT_AE   A(IT_Y)    // Æ\n#define IT_DIAE A(IT_U)    // ¨ (dead)\n#define IT_OE   A(IT_I)    // Œ\n#define IT_OSTR A(IT_O)    // Ø\n#define IT_PI   A(IT_P)    // π\n#define IT_LBRC A(IT_EGRV) // [\n#define IT_RBRC A(IT_PLUS) // ]\n#define IT_ARNG A(IT_A)    // Å\n#define IT_SS   A(IT_S)    // ß\n#define IT_PDIF A(IT_D)    // ∂\n#define IT_FHK  A(IT_F)    // ƒ\n#define IT_INFN A(IT_G)    // ∞\n#define IT_INCR A(IT_H)    // ∆\n#define IT_FORD A(IT_J)    // ª\n#define IT_MORD A(IT_K)    // º\n#define IT_NOT  A(IT_L)    // ¬\n#define IT_AT   A(IT_OGRV) // @\n#define IT_HASH A(IT_AGRV) // #\n#define IT_PILC A(IT_UGRV) // ¶\n#define IT_GRV  A(IT_BSLS) // ` (not physically present)\n#define IT_NARS A(IT_Z)    // ∑\n#define IT_DAGG A(IT_X)    // †\n#define IT_COPY A(IT_C)    // ©\n#define IT_SQRT A(IT_V)    // √\n#define IT_INTG A(IT_B)    // ∫\n#define IT_STIL A(IT_N)    // ˜ (dead)\n#define IT_MICR A(IT_M)    // µ\n#define IT_ELLP A(IT_COMM) // …\n#define IT_BULT A(IT_DOT)  // •\n#define IT_NDSH A(IT_MINS) // –\n#define IT_GTEQ S(A(IT_LABK)) // ≥\n#define IT_RDAQ S(A(IT_1))    // »\n#define IT_RDQU S(A(IT_2))    // ”\n#define IT_RSQU S(A(IT_3))    // ’\n#define IT_CENT S(A(IT_4))    // ¢\n#define IT_PERM S(A(IT_5))    // ‰\n#define IT_RSAQ S(A(IT_6))    // ›\n#define IT_FRSL S(A(IT_7))    // ⁄\n#define IT_APPL S(A(IT_8))    //  (Apple logo)\n#define IT_AEQL S(A(IT_0))    // ≈\n#define IT_IQUE S(A(IT_QUOT)) // ¿\n#define IT_PLMN S(A(IT_IGRV)) // ±\n#define IT_SLQU S(A(IT_Q))    // ‚\n#define IT_CAGR S(A(IT_W))    // À\n#define IT_CEGR S(A(IT_E))    // È\n#define IT_CIGR S(A(IT_R))    // Ì\n#define IT_COGR S(A(IT_T))    // Ò\n#define IT_CUGR S(A(IT_U))    // Ù\n#define IT_NARP S(A(IT_P))    // ∏\n#define IT_LCBR S(A(IT_EGRV)) // {\n#define IT_RCBR S(A(IT_PLUS)) // }\n#define IT_LOZN S(A(IT_UGRV)) // ◊\n#define IT_MACR S(A(IT_S))    // ¯\n#define IT_BREV S(A(IT_D))    // ˘\n#define IT_DOTA S(A(IT_F))    // ˙\n#define IT_RGNA S(A(IT_G))    // ˚\n#define IT_CEDL S(A(IT_H))    // ¸\n#define IT_DACU S(A(IT_J))    // ˝\n#define IT_OGON S(A(IT_K))    // ˛\n#define IT_CARN S(A(IT_L))    // ˇ\n#define IT_CCCE S(A(IT_OGRV)) // Ç\n#define IT_DDAG S(A(IT_X))    // ‡\n#define IT_CAAC S(A(IT_C))    // Á\n#define IT_CEAC S(A(IT_V))    // É\n#define IT_CIAC S(A(IT_B))    // Í\n#define IT_COAC S(A(IT_N))    // Ó\n#define IT_CUAC S(A(IT_M))    // Ú\n#define IT_MDDT S(A(IT_DOT))  // ·\n#define IT_MDSH S(A(IT_MINS)) // —\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_italian_mac_iso.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_ITALIAN_MAC_ISO_KEYCODES_VERSION \"0.0.1\"\n#define QMK_ITALIAN_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_ITALIAN_MAC_ISO_KEYCODES_VERSION_MAJOR 0\n#define QMK_ITALIAN_MAC_ISO_KEYCODES_VERSION_MINOR 0\n#define QMK_ITALIAN_MAC_ISO_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define IT_BSLS KC_GRV  // (backslash)\n#define IT_1    KC_1    // 1\n#define IT_2    KC_2    // 2\n#define IT_3    KC_3    // 3\n#define IT_4    KC_4    // 4\n#define IT_5    KC_5    // 5\n#define IT_6    KC_6    // 6\n#define IT_7    KC_7    // 7\n#define IT_8    KC_8    // 8\n#define IT_9    KC_9    // 9\n#define IT_0    KC_0    // 0\n#define IT_QUOT KC_MINS // '\n#define IT_IGRV KC_EQL  // ì\n#define IT_Q    KC_Q    // Q\n#define IT_W    KC_W    // W\n#define IT_E    KC_E    // E\n#define IT_R    KC_R    // R\n#define IT_T    KC_T    // T\n#define IT_Y    KC_Y    // Y\n#define IT_U    KC_U    // U\n#define IT_I    KC_I    // I\n#define IT_O    KC_O    // O\n#define IT_P    KC_P    // P\n#define IT_EGRV KC_LBRC // è\n#define IT_PLUS KC_RBRC // +\n#define IT_A    KC_A    // A\n#define IT_S    KC_S    // S\n#define IT_D    KC_D    // D\n#define IT_F    KC_F    // F\n#define IT_G    KC_G    // G\n#define IT_H    KC_H    // H\n#define IT_J    KC_J    // J\n#define IT_K    KC_K    // K\n#define IT_L    KC_L    // L\n#define IT_OGRV KC_SCLN // ò\n#define IT_AGRV KC_QUOT // à\n#define IT_UGRV KC_NUHS // ù\n#define IT_LABK KC_NUBS // <\n#define IT_Z    KC_Z    // Z\n#define IT_X    KC_X    // X\n#define IT_C    KC_C    // C\n#define IT_V    KC_V    // V\n#define IT_B    KC_B    // B\n#define IT_N    KC_N    // N\n#define IT_M    KC_M    // M\n#define IT_COMM KC_COMM // ,\n#define IT_DOT  KC_DOT  // .\n#define IT_MINS KC_SLSH // -\n#define IT_PIPE S(IT_BSLS) // |\n#define IT_EXLM S(IT_1)    // !\n#define IT_DQUO S(IT_2)    // \"\n#define IT_PND  S(IT_3)    // £\n#define IT_DLR  S(IT_4)    // $\n#define IT_PERC S(IT_5)    // %\n#define IT_AMPR S(IT_6)    // &\n#define IT_SLSH S(IT_7)    // /\n#define IT_LPRN S(IT_8)    // (\n#define IT_RPRN S(IT_9)    // )\n#define IT_EQL  S(IT_0)    // =\n#define IT_QUES S(IT_QUOT) // ?\n#define IT_CIRC S(IT_IGRV) // ^\n#define IT_EACU S(IT_EGRV) // é\n#define IT_ASTR S(IT_PLUS) // *\n#define IT_LCCE S(IT_OGRV) // ç\n#define IT_DEG  S(IT_AGRV) // °\n#define IT_SECT S(IT_UGRV) // §\n#define IT_RABK S(IT_LABK) // >\n#define IT_SCLN S(IT_COMM) // ;\n#define IT_COLN S(IT_DOT)  // :\n#define IT_UNDS S(IT_MINS) // _\n#define IT_GRV  A(IT_BSLS) // `\n#define IT_LDAQ A(IT_1)    // «\n#define IT_LDQU A(IT_2)    // “\n#define IT_LSQU A(IT_3)    // ‘\n#define IT_YEN  A(IT_4)    // ¥\n#define IT_TILD A(IT_5)    // ~\n#define IT_LSAQ A(IT_6)    // ‹\n#define IT_DIV  A(IT_7)    // ÷\n#define IT_ACUT A(IT_8)    // ´ (dead)\n#define IT_DGRV A(IT_9)    // ` (dead)\n#define IT_NEQL A(IT_0)    // ≠\n#define IT_IEXL A(IT_QUOT) // ¡\n#define IT_DCIR A(IT_IGRV) // ˆ (dead)\n#define IT_DLQU A(IT_Q)    // „\n#define IT_OMEG A(IT_W)    // Ω\n#define IT_EURO A(IT_E)    // €\n#define IT_REGD A(IT_R)    // ®\n#define IT_TM   A(IT_T)    // ™\n#define IT_AE   A(IT_Y)    // Æ\n#define IT_DIAE A(IT_U)    // ¨ (dead)\n#define IT_OE   A(IT_I)    // Œ\n#define IT_OSTR A(IT_O)    // Ø\n#define IT_PI   A(IT_P)    // π\n#define IT_LBRC A(IT_EGRV) // [\n#define IT_RBRC A(IT_PLUS) // ]\n#define IT_ARNG A(IT_A)    // Å\n#define IT_SS   A(IT_S)    // ß\n#define IT_PDIF A(IT_D)    // ∂\n#define IT_FHK  A(IT_F)    // ƒ\n#define IT_INFN A(IT_G)    // ∞\n#define IT_INCR A(IT_H)    // ∆\n#define IT_FORD A(IT_J)    // ª\n#define IT_MORD A(IT_K)    // º\n#define IT_NOT  A(IT_L)    // ¬\n#define IT_AT   A(IT_OGRV) // @\n#define IT_HASH A(IT_AGRV) // #\n#define IT_PILC A(IT_UGRV) // ¶\n#define IT_LTEQ A(IT_LABK) // ≤\n#define IT_NARS A(IT_Z)    // ∑\n#define IT_DAGG A(IT_X)    // †\n#define IT_COPY A(IT_C)    // ©\n#define IT_SQRT A(IT_V)    // √\n#define IT_INTG A(IT_B)    // ∫\n#define IT_STIL A(IT_N)    // ˜ (dead)\n#define IT_MICR A(IT_M)    // µ\n#define IT_ELLP A(IT_COMM) // …\n#define IT_BULT A(IT_DOT)  // •\n#define IT_NDSH A(IT_MINS) // –\n#define IT_DLSI S(A(IT_BSLS)) // ı\n#define IT_RDAQ S(A(IT_1))    // »\n#define IT_RDQU S(A(IT_2))    // ”\n#define IT_RSQU S(A(IT_3))    // ’\n#define IT_CENT S(A(IT_4))    // ¢\n#define IT_PERM S(A(IT_5))    // ‰\n#define IT_RSAQ S(A(IT_6))    // ›\n#define IT_FRSL S(A(IT_7))    // ⁄\n#define IT_APPL S(A(IT_8))    //  (Apple logo)\n#define IT_AEQL S(A(IT_0))    // ≈\n#define IT_IQUE S(A(IT_QUOT)) // ¿\n#define IT_PLMN S(A(IT_IGRV)) // ±\n#define IT_SLQU S(A(IT_Q))    // ‚\n#define IT_CAGR S(A(IT_W))    // À\n#define IT_CEGR S(A(IT_E))    // È\n#define IT_CIGR S(A(IT_R))    // Ì\n#define IT_COGR S(A(IT_T))    // Ò\n#define IT_CUGR S(A(IT_U))    // Ù\n#define IT_NARP S(A(IT_P))    // ∏\n#define IT_LCBR S(A(IT_EGRV)) // {\n#define IT_RCBR S(A(IT_PLUS)) // }\n#define IT_MACR S(A(IT_S))    // ¯\n#define IT_BREV S(A(IT_D))    // ˘\n#define IT_DOTA S(A(IT_F))    // ˙\n#define IT_RNGA S(A(IT_G))    // ˚\n#define IT_CEDL S(A(IT_H))    // ¸\n#define IT_DACU S(A(IT_J))    // ˝\n#define IT_OGON S(A(IT_K))    // ˛\n#define IT_CARN S(A(IT_L))    // ˇ\n#define IT_CCCE S(A(IT_OGRV)) // Ç\n#define IT_LOZN S(A(IT_UGRV)) // ◊\n#define IT_GTEQ S(A(IT_LABK)) // ≥\n#define IT_DDAG S(A(IT_X))    // ‡\n#define IT_CAAC S(A(IT_C))    // Á\n#define IT_CEAC S(A(IT_V))    // É\n#define IT_CIAC S(A(IT_B))    // Í\n#define IT_COAC S(A(IT_N))    // Ó\n#define IT_CUAC S(A(IT_M))    // Ú\n#define IT_MDDT S(A(IT_DOT))  // ·\n#define IT_MDSH S(A(IT_MINS)) // —\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_japanese.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_JAPANESE_KEYCODES_VERSION \"0.0.1\"\n#define QMK_JAPANESE_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_JAPANESE_KEYCODES_VERSION_MAJOR 0\n#define QMK_JAPANESE_KEYCODES_VERSION_MINOR 0\n#define QMK_JAPANESE_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define JP_ZKHK KC_GRV  // Zenkaku ↔ Hankaku ↔ Kanji (半角 ↔ 全角 ↔ 漢字)\n#define JP_1    KC_1    // 1\n#define JP_2    KC_2    // 2\n#define JP_3    KC_3    // 3\n#define JP_4    KC_4    // 4\n#define JP_5    KC_5    // 5\n#define JP_6    KC_6    // 6\n#define JP_7    KC_7    // 7\n#define JP_8    KC_8    // 8\n#define JP_9    KC_9    // 9\n#define JP_0    KC_0    // 0\n#define JP_MINS KC_MINS // -\n#define JP_CIRC KC_EQL  // ^\n#define JP_YEN  KC_INT3 // ¥\n#define JP_Q    KC_Q    // Q\n#define JP_W    KC_W    // W\n#define JP_E    KC_E    // E\n#define JP_R    KC_R    // R\n#define JP_T    KC_T    // T\n#define JP_Y    KC_Y    // Y\n#define JP_U    KC_U    // U\n#define JP_I    KC_I    // I\n#define JP_O    KC_O    // O\n#define JP_P    KC_P    // P\n#define JP_AT   KC_LBRC // @\n#define JP_LBRC KC_RBRC // [\n#define JP_EISU KC_CAPS // Eisū (英数)\n#define JP_A    KC_A    // A\n#define JP_S    KC_S    // S\n#define JP_D    KC_D    // D\n#define JP_F    KC_F    // F\n#define JP_G    KC_G    // G\n#define JP_H    KC_H    // H\n#define JP_J    KC_J    // J\n#define JP_K    KC_K    // K\n#define JP_L    KC_L    // L\n#define JP_SCLN KC_SCLN // ;\n#define JP_COLN KC_QUOT // :\n#define JP_RBRC KC_NUHS // ]\n#define JP_Z    KC_Z    // Z\n#define JP_X    KC_X    // X\n#define JP_C    KC_C    // C\n#define JP_V    KC_V    // V\n#define JP_B    KC_B    // B\n#define JP_N    KC_N    // N\n#define JP_M    KC_M    // M\n#define JP_COMM KC_COMM // ,\n#define JP_DOT  KC_DOT  // .\n#define JP_SLSH KC_SLSH // /\n#define JP_BSLS KC_INT1 // (backslash)\n#define JP_MHEN KC_INT5 // Muhenkan (無変換)\n#define JP_HENK KC_INT4 // Henkan (変換)\n#define JP_KANA KC_INT2 // Katakana ↔ Hiragana ↔ Rōmaji (カタカナ ↔ ひらがな ↔ ローマ字)\n#define JP_EXLM S(JP_1)    // !\n#define JP_DQUO S(JP_2)    // \"\n#define JP_HASH S(JP_3)    // #\n#define JP_DLR  S(JP_4)    // $\n#define JP_PERC S(JP_5)    // %\n#define JP_AMPR S(JP_6)    // &\n#define JP_QUOT S(JP_7)    // '\n#define JP_LPRN S(JP_8)    // (\n#define JP_RPRN S(JP_9)    // )\n#define JP_EQL  S(JP_MINS) // =\n#define JP_TILD S(JP_CIRC) // ~\n#define JP_PIPE S(JP_YEN)  // |\n#define JP_GRV  S(JP_AT)   // `\n#define JP_LCBR S(JP_LBRC) // {\n#define JP_CAPS S(JP_EISU) // Caps Lock\n#define JP_PLUS S(JP_SCLN) // +\n#define JP_ASTR S(JP_COLN) // *\n#define JP_RCBR S(JP_RBRC) // }\n#define JP_LABK S(JP_COMM) // <\n#define JP_RABK S(JP_DOT)  // >\n#define JP_QUES S(JP_SLSH) // ?\n#define JP_UNDS S(JP_BSLS) // _\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_korean.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_KOREAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_KOREAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_KOREAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_KOREAN_KEYCODES_VERSION_MINOR 0\n#define QMK_KOREAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define KR_GRV  KC_GRV  // `\n#define KR_1    KC_1    // 1\n#define KR_2    KC_2    // 2\n#define KR_3    KC_3    // 3\n#define KR_4    KC_4    // 4\n#define KR_5    KC_5    // 5\n#define KR_6    KC_6    // 6\n#define KR_7    KC_7    // 7\n#define KR_8    KC_8    // 8\n#define KR_9    KC_9    // 9\n#define KR_0    KC_0    // 0\n#define KR_MINS KC_MINS // -\n#define KR_EQL  KC_EQL  // =\n#define KR_Q    KC_Q    // Q\n#define KR_W    KC_W    // W\n#define KR_E    KC_E    // E\n#define KR_R    KC_R    // R\n#define KR_T    KC_T    // T\n#define KR_Y    KC_Y    // Y\n#define KR_U    KC_U    // U\n#define KR_I    KC_I    // I\n#define KR_O    KC_O    // O\n#define KR_P    KC_P    // P\n#define KR_LBRC KC_LBRC // [\n#define KR_RBRC KC_RBRC // ]\n#define KR_WON  KC_BSLS // ₩\n#define KR_A    KC_A    // A\n#define KR_S    KC_S    // S\n#define KR_D    KC_D    // D\n#define KR_F    KC_F    // F\n#define KR_G    KC_G    // G\n#define KR_H    KC_H    // H\n#define KR_J    KC_J    // J\n#define KR_K    KC_K    // K\n#define KR_L    KC_L    // L\n#define KR_SCLN KC_SCLN // ;\n#define KR_QUOT KC_QUOT // '\n#define KR_Z    KC_Z    // Z\n#define KR_X    KC_X    // X\n#define KR_C    KC_C    // C\n#define KR_V    KC_V    // V\n#define KR_B    KC_B    // B\n#define KR_N    KC_N    // N\n#define KR_M    KC_M    // M\n#define KR_COMM KC_COMM // ,\n#define KR_DOT  KC_DOT  // .\n#define KR_SLSH KC_SLSH // /\n#define KR_HANJ KC_LNG2 // Hanja (한자)\n#define KR_HAEN KC_LNG1 // Han ↔ Yeong (한 ↔ 영)\n#define KR_TILD S(KR_GRV)  // ~\n#define KR_EXLM S(KR_1)    // !\n#define KR_AT   S(KR_2)    // @\n#define KR_HASH S(KR_3)    // #\n#define KR_DLR  S(KR_4)    // $\n#define KR_PERC S(KR_5)    // %\n#define KR_CIRC S(KR_6)    // ^\n#define KR_AMPR S(KR_7)    // &\n#define KR_ASTR S(KR_8)    // *\n#define KR_LPRN S(KR_9)    // (\n#define KR_RPRN S(KR_0)    // )\n#define KR_UNDS S(KR_MINS) // _\n#define KR_PLUS S(KR_EQL)  // +\n#define KR_LCBR S(KR_LBRC) // {\n#define KR_RCBR S(KR_RBRC) // }\n#define KR_PIPE S(KR_WON)  // |\n#define KR_COLN S(KR_SCLN) // :\n#define KR_DQUO S(KR_QUOT) // \"\n#define KR_LABK S(KR_COMM) // <\n#define KR_RABK S(KR_DOT)  // >\n#define KR_QUES S(KR_SLSH) // ?\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_latvian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_LATVIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_LATVIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_LATVIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_LATVIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_LATVIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define LV_GRV  KC_GRV  // `\n#define LV_1    KC_1    // 1\n#define LV_2    KC_2    // 2\n#define LV_3    KC_3    // 3\n#define LV_4    KC_4    // 4\n#define LV_5    KC_5    // 5\n#define LV_6    KC_6    // 6\n#define LV_7    KC_7    // 7\n#define LV_8    KC_8    // 8\n#define LV_9    KC_9    // 9\n#define LV_0    KC_0    // 0\n#define LV_MINS KC_MINS // -\n#define LV_EQL  KC_EQL  // =\n#define LV_Q    KC_Q    // Q\n#define LV_W    KC_W    // W\n#define LV_E    KC_E    // E\n#define LV_R    KC_R    // R\n#define LV_T    KC_T    // T\n#define LV_Y    KC_Y    // Y\n#define LV_U    KC_U    // U\n#define LV_I    KC_I    // I\n#define LV_O    KC_O    // O\n#define LV_P    KC_P    // P\n#define LV_LBRC KC_LBRC // [\n#define LV_RBRC KC_RBRC // ]\n#define LV_A    KC_A    // A\n#define LV_S    KC_S    // S\n#define LV_D    KC_D    // D\n#define LV_F    KC_F    // F\n#define LV_G    KC_G    // G\n#define LV_H    KC_H    // H\n#define LV_J    KC_J    // J\n#define LV_K    KC_K    // K\n#define LV_L    KC_L    // L\n#define LV_SCLN KC_SCLN // ;\n#define LV_QUOT KC_QUOT // ' (dead)\n#define LV_BSLS KC_NUHS // (backslash)\n#define LV_NUBS KC_NUBS // (backslash)\n#define LV_Z    KC_Z    // Z\n#define LV_X    KC_X    // X\n#define LV_C    KC_C    // C\n#define LV_V    KC_V    // V\n#define LV_B    KC_B    // B\n#define LV_N    KC_N    // N\n#define LV_M    KC_M    // M\n#define LV_COMM KC_COMM // ,\n#define LV_DOT  KC_DOT  // .\n#define LV_SLSH KC_SLSH // /\n#define LV_TILD S(LV_GRV)  // ~\n#define LV_EXLM S(LV_1)    // !\n#define LV_AT   S(LV_2)    // @\n#define LV_HASH S(LV_3)    // #\n#define LV_DLR  S(LV_4)    // $\n#define LV_PERC S(LV_5)    // %\n#define LV_CIRC S(LV_6)    // ^\n#define LV_AMPR S(LV_7)    // &\n#define LV_ASTR S(LV_8)    // *\n#define LV_LPRN S(LV_9)    // (\n#define LV_RPRN S(LV_0)    // )\n#define LV_UNDS S(LV_MINS) // _\n#define LV_PLUS S(LV_EQL)  // +\n#define LV_LCBR S(LV_LBRC) // {\n#define LV_RCBR S(LV_RBRC) // }\n#define LV_COLN S(LV_SCLN) // :\n#define LV_DQUO S(LV_QUOT) // \" (dead)\n#define LV_PIPE S(LV_BSLS) // |\n#define LV_LABK S(LV_COMM) // <\n#define LV_RABK S(LV_DOT)  // >\n#define LV_QUES S(LV_SLSH) // ?\n#define LV_SHYP ALGR(LV_GRV)  // ­ (soft hyphen)\n#define LV_NBSP ALGR(LV_1)    // (non-breaking space)\n#define LV_LDAQ ALGR(LV_2)    // «\n#define LV_RDAQ ALGR(LV_3)    // »\n#define LV_EURO ALGR(LV_4)    // €\n#define LV_RSQU ALGR(LV_6)    // ’\n#define LV_NDSH ALGR(LV_MINS) // –\n#define LV_EMAC ALGR(LV_E)    // Ē\n#define LV_RCED ALGR(LV_R)    // Ŗ\n#define LV_UMAC ALGR(LV_U)    // Ū\n#define LV_IMAC ALGR(LV_I)    // Ī\n#define LV_OMAC ALGR(LV_O)    // Ō\n#define LV_AMAC ALGR(LV_A)    // Ā\n#define LV_SCAR ALGR(LV_S)    // Š\n#define LV_GCED ALGR(LV_G)    // Ģ\n#define LV_KCED ALGR(LV_K)    // Ķ\n#define LV_LCED ALGR(LV_L)    // Ļ\n#define LV_ACUT ALGR(LV_QUOT) // ´ (dead)\n#define LV_ZCAR ALGR(LV_Z)    // Ž\n#define LV_CCAR ALGR(LV_C)    // Č\n#define LV_NCED ALGR(LV_N)    // Ņ\n#define LV_SECT S(ALGR(LV_4))    // §\n#define LV_DEG  S(ALGR(LV_5))    // °\n#define LV_PLMN S(ALGR(LV_7))    // ±\n#define LV_MUL  S(ALGR(LV_8))    // ×\n#define LV_MDSH S(ALGR(LV_MINS)) // —\n#define LV_DIAE S(ALGR(LV_QUOT)) // ¨ (dead)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_lithuanian_azerty.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_LITHUANIAN_AZERTY_KEYCODES_VERSION \"0.0.1\"\n#define QMK_LITHUANIAN_AZERTY_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_LITHUANIAN_AZERTY_KEYCODES_VERSION_MAJOR 0\n#define QMK_LITHUANIAN_AZERTY_KEYCODES_VERSION_MINOR 0\n#define QMK_LITHUANIAN_AZERTY_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define LT_GRV  KC_GRV  // `\n#define LT_EXLM KC_1    // !\n#define LT_MINS KC_2    // -\n#define LT_SLSH KC_3    // /\n#define LT_SCLN KC_4    // ;\n#define LT_COLN KC_5    // :\n#define LT_COMM KC_6    // ,\n#define LT_DOT  KC_7    // .\n#define LT_EQL  KC_8    // =\n#define LT_LPRN KC_9    // (\n#define LT_RPRN KC_0    // )\n#define LT_QUES KC_MINS // ?\n#define LT_X    KC_EQL  // X\n#define LT_AOGO KC_Q    // Ą\n#define LT_ZCAR KC_W    // Ž\n#define LT_E    KC_E    // E\n#define LT_R    KC_R    // R\n#define LT_T    KC_T    // T\n#define LT_Y    KC_Y    // Y\n#define LT_U    KC_U    // U\n#define LT_I    KC_I    // I\n#define LT_O    KC_O    // O\n#define LT_P    KC_P    // P\n#define LT_IOGO KC_LBRC // Į\n#define LT_W    KC_RBRC // W\n#define LT_A    KC_A    // A\n#define LT_S    KC_S    // S\n#define LT_D    KC_D    // D\n#define LT_SCAR KC_F    // Š\n#define LT_G    KC_G    // G\n#define LT_H    KC_H    // H\n#define LT_J    KC_J    // J\n#define LT_K    KC_K    // K\n#define LT_L    KC_L    // L\n#define LT_UOGO KC_SCLN // Ų\n#define LT_EDOT KC_QUOT // Ė\n#define LT_Q    KC_NUHS // Q\n#define LT_LABK KC_NUBS // <\n#define LT_Z    KC_Z    // Z\n#define LT_UMAC KC_X    // Ū\n#define LT_C    KC_C    // C\n#define LT_V    KC_V    // V\n#define LT_B    KC_B    // B\n#define LT_N    KC_N    // N\n#define LT_M    KC_M    // M\n#define LT_CCAR KC_COMM // Č\n#define LT_F    KC_DOT  // F\n#define LT_EOGO KC_SLSH // Ę\n#define LT_TILD S(LT_GRV)  // ~\n#define LT_1    S(LT_EXLM) // 1\n#define LT_2    S(LT_MINS) // 2\n#define LT_3    S(LT_SLSH) // 3\n#define LT_4    S(LT_SCLN) // 4\n#define LT_5    S(LT_COLN) // 5\n#define LT_6    S(LT_COMM) // 6\n#define LT_7    S(LT_DOT)  // 7\n#define LT_8    S(LT_EQL)  // 8\n#define LT_9    S(LT_LPRN) // 9\n#define LT_0    S(LT_RPRN) // 0\n#define LT_PLUS S(LT_QUES) // +\n#define LT_RABK S(LT_LABK) // >\n#define LT_ACUT ALGR(LT_GRV)  // ´\n#define LT_AT   ALGR(LT_EXLM) // @\n#define LT_UNDS ALGR(LT_MINS) // _\n#define LT_HASH ALGR(LT_SLSH) // #\n#define LT_DLR  ALGR(LT_SCLN) // $\n#define LT_SECT ALGR(LT_COLN) // §\n#define LT_CIRC ALGR(LT_COMM) // ^\n#define LT_AMPR ALGR(LT_DOT)  // &\n#define LT_ASTR ALGR(LT_EQL)  // *\n#define LT_LBRC ALGR(LT_LPRN) // [\n#define LT_RBRC ALGR(LT_RPRN) // ]\n#define LT_QUOT ALGR(LT_QUES) // '\n#define LT_PERC ALGR(LT_X)    // %\n#define LT_EURO ALGR(LT_E)    // €\n#define LT_LCBR ALGR(LT_IOGO) // {\n#define LT_RCBR ALGR(LT_W)    // }\n#define LT_DQUO ALGR(LT_EDOT) // \"\n#define LT_PIPE ALGR(LT_Q)    // |\n#define LT_NDSH ALGR(LT_LABK) // –\n#define LT_DLQU ALGR(LT_CCAR) // „\n#define LT_LDQU ALGR(LT_F)    // “\n#define LT_BSLS ALGR(LT_EOGO) // (backslash)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_lithuanian_qwerty.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_LITHUANIAN_QWERTY_KEYCODES_VERSION \"0.0.1\"\n#define QMK_LITHUANIAN_QWERTY_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_LITHUANIAN_QWERTY_KEYCODES_VERSION_MAJOR 0\n#define QMK_LITHUANIAN_QWERTY_KEYCODES_VERSION_MINOR 0\n#define QMK_LITHUANIAN_QWERTY_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define LT_GRV  KC_GRV  // `\n#define LT_AOGO KC_1    // Ą\n#define LT_CCAR KC_2    // Č\n#define LT_EOGO KC_3    // Ę\n#define LT_EDOT KC_4    // Ė\n#define LT_IOGO KC_5    // Į\n#define LT_SCAR KC_6    // Š\n#define LT_UOGO KC_7    // Ų\n#define LT_UMAC KC_8    // Ū\n#define LT_9    KC_9    // 9\n#define LT_0    KC_0    // 0\n#define LT_MINS KC_MINS // -\n#define LT_ZCAR KC_EQL  // Ž\n#define LT_Q    KC_Q    // Q\n#define LT_W    KC_W    // W\n#define LT_E    KC_E    // E\n#define LT_R    KC_R    // R\n#define LT_T    KC_T    // T\n#define LT_Y    KC_Y    // Y\n#define LT_U    KC_U    // U\n#define LT_I    KC_I    // I\n#define LT_O    KC_O    // O\n#define LT_P    KC_P    // P\n#define LT_LBRC KC_LBRC // [\n#define LT_RBRC KC_RBRC // ]\n#define LT_A    KC_A    // A\n#define LT_S    KC_S    // S\n#define LT_D    KC_D    // D\n#define LT_F    KC_F    // F\n#define LT_G    KC_G    // G\n#define LT_H    KC_H    // H\n#define LT_J    KC_J    // J\n#define LT_K    KC_K    // K\n#define LT_L    KC_L    // L\n#define LT_SCLN KC_SCLN // ;\n#define LT_QUOT KC_QUOT // '\n#define LT_BSLS KC_BSLS // (backslash)\n#define LT_Z    KC_Z    // Z\n#define LT_X    KC_X    // X\n#define LT_C    KC_C    // C\n#define LT_V    KC_V    // V\n#define LT_B    KC_B    // B\n#define LT_N    KC_N    // N\n#define LT_M    KC_M    // M\n#define LT_COMM KC_COMM // ,\n#define LT_DOT  KC_DOT  // .\n#define LT_SLSH KC_SLSH // /\n#define LT_TILD S(LT_GRV)  // ~\n#define LT_LPRN S(LT_9)    // (\n#define LT_RPRN S(LT_0)    // )\n#define LT_UNDS S(LT_MINS) // _\n#define LT_LCBR S(LT_LBRC) // {\n#define LT_RCBR S(LT_RBRC) // }\n#define LT_COLN S(LT_SCLN) // :\n#define LT_DQUO S(LT_QUOT) // \"\n#define LT_PIPE S(LT_BSLS) // |\n#define LT_LABK S(LT_COMM) // <\n#define LT_RABK S(LT_DOT)  // >\n#define LT_QUES S(LT_SLSH) // ?\n#define LT_1    ALGR(LT_AOGO) // 1\n#define LT_2    ALGR(LT_CCAR) // 2\n#define LT_3    ALGR(LT_EOGO) // 3\n#define LT_4    ALGR(LT_EDOT) // 4\n#define LT_5    ALGR(LT_IOGO) // 5\n#define LT_6    ALGR(LT_SCAR) // 6\n#define LT_7    ALGR(LT_UOGO) // 7\n#define LT_8    ALGR(LT_UMAC) // 8\n#define LT_EQL  ALGR(LT_ZCAR) // =\n#define LT_EURO ALGR(LT_E)    // €\n#define LT_EXLM S(ALGR(LT_AOGO)) // !\n#define LT_AT   S(ALGR(LT_CCAR)) // @\n#define LT_HASH S(ALGR(LT_EOGO)) // #\n#define LT_DLR  S(ALGR(LT_EDOT)) // $\n#define LT_PERC S(ALGR(LT_IOGO)) // %\n#define LT_CIRC S(ALGR(LT_SCAR)) // ^\n#define LT_AMPR S(ALGR(LT_UOGO)) // &\n#define LT_ASTR S(ALGR(LT_UMAC)) // *\n#define LT_PLUS S(ALGR(LT_ZCAR)) // +\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_neo2.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_NEO2_KEYCODES_VERSION \"0.0.1\"\n#define QMK_NEO2_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_NEO2_KEYCODES_VERSION_MAJOR 0\n#define QMK_NEO2_KEYCODES_VERSION_MINOR 0\n#define QMK_NEO2_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define NE_CIRC KC_GRV  // ^ (dead)\n#define NE_1    KC_1    // 1\n#define NE_2    KC_2    // 2\n#define NE_3    KC_3    // 3\n#define NE_4    KC_4    // 4\n#define NE_5    KC_5    // 5\n#define NE_6    KC_6    // 6\n#define NE_7    KC_7    // 7\n#define NE_8    KC_8    // 8\n#define NE_9    KC_9    // 9\n#define NE_0    KC_0    // 0\n#define NE_MINS KC_MINS // -\n#define NE_GRV  KC_EQL  // ` (dead)\n#define NE_X    KC_Q    // X\n#define NE_V    KC_W    // V\n#define NE_L    KC_E    // L\n#define NE_C    KC_R    // C\n#define NE_W    KC_T    // W\n#define NE_K    KC_Y    // K\n#define NE_H    KC_U    // H\n#define NE_G    KC_I    // G\n#define NE_F    KC_O    // F\n#define NE_Q    KC_P    // Q\n#define NE_SS   KC_LBRC // ß\n#define NE_ACUT KC_RBRC // ´ (dead)\n#define NE_L3L  KC_CAPS // (layer 3)\n#define NE_U    KC_A    // U\n#define NE_I    KC_S    // I\n#define NE_A    KC_D    // A\n#define NE_E    KC_F    // E\n#define NE_O    KC_G    // O\n#define NE_S    KC_H    // S\n#define NE_N    KC_J    // N\n#define NE_R    KC_K    // R\n#define NE_T    KC_L    // T\n#define NE_D    KC_SCLN // D\n#define NE_Y    KC_QUOT // Y\n#define NE_L3R  KC_NUHS // (layer 3)\n#define NE_L4L  KC_NUBS // (layer 4)\n#define NE_UDIA KC_Z    // Ü\n#define NE_ODIA KC_X    // Ö\n#define NE_ADIA KC_C    // Ä\n#define NE_P    KC_V    // P\n#define NE_Z    KC_B    // Z\n#define NE_B    KC_N    // B\n#define NE_M    KC_M    // M\n#define NE_COMM KC_COMM // ,\n#define NE_DOT  KC_DOT  // .\n#define NE_J    KC_SLSH // J\n#define NE_L4R  KC_ALGR // (layer 4)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_nordic.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_NORDIC_KEYCODES_VERSION \"0.0.1\"\n#define QMK_NORDIC_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_NORDIC_KEYCODES_VERSION_MAJOR 0\n#define QMK_NORDIC_KEYCODES_VERSION_MINOR 0\n#define QMK_NORDIC_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define NO_HALF KC_GRV \n#define NO_PLUS KC_MINS\n#define NO_ACUT KC_EQL \n#define NO_AM   KC_LBRC\n#define NO_QUOT KC_RBRC // this is the \"umlaut\" char on Nordic keyboards, Apple layout\n#define NO_AE   KC_SCLN\n#define NO_OSLH KC_QUOT\n#define NO_APOS KC_NUHS\n#define NO_LESS KC_NUBS\n#define NO_MINS KC_SLSH\n#define NO_SECT LSFT(NO_HALF)\n#define NO_QUO2 LSFT(KC_2)\n#define NO_BULT LSFT(KC_4)\n#define NO_AMPR LSFT(KC_6)\n#define NO_SLSH LSFT(KC_7)\n#define NO_LPRN LSFT(KC_8)\n#define NO_RPRN LSFT(KC_9)\n#define NO_EQL  LSFT(KC_0)\n#define NO_QUES LSFT(NO_PLUS)\n#define NO_GRV  LSFT(NO_ACUT)\n#define NO_CIRC LSFT(NO_QUOT)\n#define NO_GRTR LSFT(NO_LESS)\n#define NO_SCLN LSFT(KC_COMM)\n#define NO_COLN LSFT(KC_DOT)\n#define NO_UNDS LSFT(NO_MINS)\n#define NO_AT   ALGR(KC_2)   \n#define NO_PND  ALGR(KC_3)   \n#define NO_DLR  ALGR(KC_4)   \n#define NO_LCBR ALGR(KC_7)   \n#define NO_LBRC ALGR(KC_8)   \n#define NO_RBRC ALGR(KC_9)   \n#define NO_RCBR ALGR(KC_0)   \n#define NO_PIPE ALGR(KC_NUBS)\n#define NO_EURO ALGR(KC_E)   \n#define NO_TILD ALGR(NO_QUOT)\n#define NO_BSLS ALGR(KC_MINS)\n#define NO_MU   ALGR(KC_M)   \n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_norman.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_NORMAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_NORMAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_NORMAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_NORMAN_KEYCODES_VERSION_MINOR 0\n#define QMK_NORMAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define NM_GRV  KC_GRV  // `\n#define NM_1    KC_1    // 1\n#define NM_2    KC_2    // 2\n#define NM_3    KC_3    // 3\n#define NM_4    KC_4    // 4\n#define NM_5    KC_5    // 5\n#define NM_6    KC_6    // 6\n#define NM_7    KC_7    // 7\n#define NM_8    KC_8    // 8\n#define NM_9    KC_9    // 9\n#define NM_0    KC_0    // 0\n#define NM_MINS KC_MINS // -\n#define NM_EQL  KC_EQL  // =\n#define NM_Q    KC_Q    // Q\n#define NM_W    KC_W    // W\n#define NM_D    KC_E    // D\n#define NM_F    KC_R    // F\n#define NM_K    KC_T    // K\n#define NM_J    KC_Y    // J\n#define NM_U    KC_U    // U\n#define NM_R    KC_I    // R\n#define NM_L    KC_O    // L\n#define NM_SCLN KC_P    // ;\n#define NM_LBRC KC_LBRC // [\n#define NM_RBRC KC_RBRC // ]\n#define NM_BSLS KC_BSLS // (backslash)\n#define NM_A    KC_A    // A\n#define NM_S    KC_S    // S\n#define NM_E    KC_D    // E\n#define NM_T    KC_F    // T\n#define NM_G    KC_G    // G\n#define NM_Y    KC_H    // Y\n#define NM_N    KC_J    // N\n#define NM_I    KC_K    // I\n#define NM_O    KC_L    // O\n#define NM_H    KC_SCLN // H\n#define NM_QUOT KC_QUOT // '\n#define NM_Z    KC_Z    // Z\n#define NM_X    KC_X    // X\n#define NM_C    KC_C    // C\n#define NM_V    KC_V    // V\n#define NM_B    KC_B    // B\n#define NM_P    KC_N    // P\n#define NM_M    KC_M    // M\n#define NM_COMM KC_COMM // ,\n#define NM_DOT  KC_DOT  // .\n#define NM_SLSH KC_SLSH // /\n#define NM_TILD S(NM_GRV)  // ~\n#define NM_EXLM S(NM_1)    // !\n#define NM_AT   S(NM_2)    // @\n#define NM_HASH S(NM_3)    // #\n#define NM_DLR  S(NM_4)    // $\n#define NM_PERC S(NM_5)    // %\n#define NM_CIRC S(NM_6)    // ^\n#define NM_AMPR S(NM_7)    // &\n#define NM_ASTR S(NM_8)    // *\n#define NM_LPRN S(NM_9)    // (\n#define NM_RPRN S(NM_0)    // )\n#define NM_UNDS S(NM_MINS) // _\n#define NM_PLUS S(NM_EQL)  // +\n#define NM_COLN S(NM_SCLN) // :\n#define NM_LCBR S(NM_LBRC) // {\n#define NM_RCBR S(NM_RBRC) // }\n#define NM_PIPE S(NM_BSLS) // |\n#define NM_DQUO S(NM_QUOT) // \"\n#define NM_LABK S(NM_COMM) // <\n#define NM_RABK S(NM_DOT)  // >\n#define NM_QUES S(NM_SLSH) // ?\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_norwegian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_NORWEGIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_NORWEGIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_NORWEGIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_NORWEGIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_NORWEGIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define NO_PIPE KC_GRV  // |\n#define NO_1    KC_1    // 1\n#define NO_2    KC_2    // 2\n#define NO_3    KC_3    // 3\n#define NO_4    KC_4    // 4\n#define NO_5    KC_5    // 5\n#define NO_6    KC_6    // 6\n#define NO_7    KC_7    // 7\n#define NO_8    KC_8    // 8\n#define NO_9    KC_9    // 9\n#define NO_0    KC_0    // 0\n#define NO_PLUS KC_MINS // +\n#define NO_BSLS KC_EQL  // (backslash)\n#define NO_Q    KC_Q    // Q\n#define NO_W    KC_W    // W\n#define NO_E    KC_E    // E\n#define NO_R    KC_R    // R\n#define NO_T    KC_T    // T\n#define NO_Y    KC_Y    // Y\n#define NO_U    KC_U    // U\n#define NO_I    KC_I    // I\n#define NO_O    KC_O    // O\n#define NO_P    KC_P    // P\n#define NO_ARNG KC_LBRC // Å\n#define NO_DIAE KC_RBRC // ¨ (dead)\n#define NO_A    KC_A    // A\n#define NO_S    KC_S    // S\n#define NO_D    KC_D    // D\n#define NO_F    KC_F    // F\n#define NO_G    KC_G    // G\n#define NO_H    KC_H    // H\n#define NO_J    KC_J    // J\n#define NO_K    KC_K    // K\n#define NO_L    KC_L    // L\n#define NO_OSTR KC_SCLN // Ø\n#define NO_AE   KC_QUOT // Æ\n#define NO_QUOT KC_NUHS // '\n#define NO_LABK KC_NUBS // <\n#define NO_Z    KC_Z    // Z\n#define NO_X    KC_X    // X\n#define NO_C    KC_C    // C\n#define NO_V    KC_V    // V\n#define NO_B    KC_B    // B\n#define NO_N    KC_N    // N\n#define NO_M    KC_M    // M\n#define NO_COMM KC_COMM // ,\n#define NO_DOT  KC_DOT  // .\n#define NO_MINS KC_SLSH // -\n#define NO_SECT S(NO_PIPE) // §\n#define NO_EXLM S(NO_1)    // !\n#define NO_DQUO S(NO_2)    // \"\n#define NO_HASH S(NO_3)    // #\n#define NO_CURR S(NO_4)    // ¤\n#define NO_PERC S(NO_5)    // %\n#define NO_AMPR S(NO_6)    // &\n#define NO_SLSH S(NO_7)    // /\n#define NO_LPRN S(NO_8)    // (\n#define NO_RPRN S(NO_9)    // )\n#define NO_EQL  S(NO_0)    // =\n#define NO_QUES S(NO_PLUS) // ?\n#define NO_GRV  S(NO_BSLS) // ` (dead)\n#define NO_CIRC S(NO_DIAE) // ^ (dead)\n#define NO_ASTR S(NO_QUOT) // *\n#define NO_RABK S(NO_LABK) // >\n#define NO_SCLN S(NO_COMM) // ;\n#define NO_COLN S(NO_DOT)  // :\n#define NO_UNDS S(NO_MINS) // _\n#define NO_AT   ALGR(NO_2)    // @\n#define NO_PND  ALGR(NO_3)    // £\n#define NO_DLR  ALGR(NO_4)    // $\n#define NO_EURO ALGR(NO_5)    // €\n#define NO_LCBR ALGR(NO_7)    // {\n#define NO_LBRC ALGR(NO_8)    // [\n#define NO_RBRC ALGR(NO_9)    // ]\n#define NO_RCBR ALGR(NO_0)    // }\n#define NO_ACUT ALGR(NO_BSLS) // ´ (dead)\n#define NO_TILD ALGR(NO_DIAE) // ~ (dead)\n#define NO_MICR ALGR(NO_M)    // µ\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_plover.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_PLOVER_KEYCODES_VERSION \"0.0.1\"\n#define QMK_PLOVER_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_PLOVER_KEYCODES_VERSION_MAJOR 0\n#define QMK_PLOVER_KEYCODES_VERSION_MINOR 0\n#define QMK_PLOVER_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define PV_NUM  KC_1   \n#define PV_LS   KC_Q   \n#define PV_LT   KC_W   \n#define PV_LP   KC_E   \n#define PV_LH   KC_R   \n#define PV_STAR KC_Y   \n#define PV_RF   KC_U   \n#define PV_RP   KC_I   \n#define PV_RL   KC_O   \n#define PV_RT   KC_P   \n#define PV_RD   KC_LBRC\n#define PV_LK   KC_S   \n#define PV_LW   KC_D   \n#define PV_LR   KC_F   \n#define PV_RR   KC_J   \n#define PV_RB   KC_K   \n#define PV_RG   KC_L   \n#define PV_RS   KC_SCLN\n#define PV_RZ   KC_QUOT\n#define PV_A    KC_C   \n#define PV_O    KC_V   \n#define PV_E    KC_N   \n#define PV_U    KC_M   \n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_plover_dvorak.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_PLOVER_DVORAK_KEYCODES_VERSION \"0.0.1\"\n#define QMK_PLOVER_DVORAK_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_PLOVER_DVORAK_KEYCODES_VERSION_MAJOR 0\n#define QMK_PLOVER_DVORAK_KEYCODES_VERSION_MINOR 0\n#define QMK_PLOVER_DVORAK_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define PD_NUM  DV_1   \n#define PD_LS   DV_Q   \n#define PD_LT   DV_W   \n#define PD_LP   DV_E   \n#define PD_LH   DV_R   \n#define PD_LK   DV_S   \n#define PD_LW   DV_D   \n#define PD_LR   DV_F   \n#define PD_STAR DV_Y   \n#define PD_RF   DV_U   \n#define PD_RP   DV_I   \n#define PD_RL   DV_O   \n#define PD_RT   DV_P   \n#define PD_RD   DV_LBRC\n#define PD_RR   DV_J   \n#define PD_RB   DV_K   \n#define PD_RG   DV_L   \n#define PD_RS   DV_SCLN\n#define PD_RZ   DV_QUOT\n#define PD_A    DV_C   \n#define PD_O    DV_V   \n#define PD_E    DV_N   \n#define PD_U    DV_M   \n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_polish.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_POLISH_KEYCODES_VERSION \"0.0.1\"\n#define QMK_POLISH_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_POLISH_KEYCODES_VERSION_MAJOR 0\n#define QMK_POLISH_KEYCODES_VERSION_MINOR 0\n#define QMK_POLISH_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define PL_GRV  KC_GRV  // `\n#define PL_1    KC_1    // 1\n#define PL_2    KC_2    // 2\n#define PL_3    KC_3    // 3\n#define PL_4    KC_4    // 4\n#define PL_5    KC_5    // 5\n#define PL_6    KC_6    // 6\n#define PL_7    KC_7    // 7\n#define PL_8    KC_8    // 8\n#define PL_9    KC_9    // 9\n#define PL_0    KC_0    // 0\n#define PL_MINS KC_MINS // -\n#define PL_EQL  KC_EQL  // =\n#define PL_Q    KC_Q    // Q\n#define PL_W    KC_W    // W\n#define PL_E    KC_E    // E\n#define PL_R    KC_R    // R\n#define PL_T    KC_T    // T\n#define PL_Y    KC_Y    // Y\n#define PL_U    KC_U    // U\n#define PL_I    KC_I    // I\n#define PL_O    KC_O    // O\n#define PL_P    KC_P    // P\n#define PL_LBRC KC_LBRC // [\n#define PL_RBRC KC_RBRC // ]\n#define PL_BSLS KC_BSLS // (backslash)\n#define PL_A    KC_A    // A\n#define PL_S    KC_S    // S\n#define PL_D    KC_D    // D\n#define PL_F    KC_F    // F\n#define PL_G    KC_G    // G\n#define PL_H    KC_H    // H\n#define PL_J    KC_J    // J\n#define PL_K    KC_K    // K\n#define PL_L    KC_L    // L\n#define PL_SCLN KC_SCLN // ;\n#define PL_QUOT KC_QUOT // '\n#define PL_Z    KC_Z    // Z\n#define PL_X    KC_X    // X\n#define PL_C    KC_C    // C\n#define PL_V    KC_V    // V\n#define PL_B    KC_B    // B\n#define PL_N    KC_N    // N\n#define PL_M    KC_M    // M\n#define PL_COMM KC_COMM // ,\n#define PL_DOT  KC_DOT  // .\n#define PL_SLSH KC_SLSH // /\n#define PL_TILD S(PL_GRV)  // ~\n#define PL_EXLM S(PL_1)    // !\n#define PL_AT   S(PL_2)    // @\n#define PL_HASH S(PL_3)    // #\n#define PL_DLR  S(PL_4)    // $\n#define PL_PERC S(PL_5)    // %\n#define PL_CIRC S(PL_6)    // ^\n#define PL_AMPR S(PL_7)    // &\n#define PL_ASTR S(PL_8)    // *\n#define PL_LPRN S(PL_9)    // (\n#define PL_RPRN S(PL_0)    // )\n#define PL_UNDS S(PL_MINS) // _\n#define PL_PLUS S(PL_EQL)  // +\n#define PL_LCBR S(PL_LBRC) // {\n#define PL_RCBR S(PL_RBRC) // }\n#define PL_PIPE S(PL_BSLS) // |\n#define PL_COLN S(PL_SCLN) // :\n#define PL_DQUO S(PL_QUOT) // \"\n#define PL_LABK S(PL_COMM) // <\n#define PL_RABK S(PL_DOT)  // >\n#define PL_QUES S(PL_SLSH) // ?\n#define PL_EOGO ALGR(PL_E)    // Ę\n#define PL_EURO ALGR(PL_U)    // €\n#define PL_OACU ALGR(PL_O)    // Ó\n#define PL_AOGO ALGR(PL_A)    // Ą\n#define PL_SACU ALGR(PL_S)    // Ś\n#define PL_LSTR ALGR(PL_L)    // Ł\n#define PL_ZDOT ALGR(PL_Z)    // Ż\n#define PL_ZACU ALGR(PL_X)    // Ź\n#define PL_CACU ALGR(PL_C)    // Ć\n#define PL_NACU ALGR(PL_N)    // Ń\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_portuguese.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_PORTUGUESE_KEYCODES_VERSION \"0.0.1\"\n#define QMK_PORTUGUESE_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_PORTUGUESE_KEYCODES_VERSION_MAJOR 0\n#define QMK_PORTUGUESE_KEYCODES_VERSION_MINOR 0\n#define QMK_PORTUGUESE_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define PT_BSLS KC_GRV  // (backslash)\n#define PT_1    KC_1    // 1\n#define PT_2    KC_2    // 2\n#define PT_3    KC_3    // 3\n#define PT_4    KC_4    // 4\n#define PT_5    KC_5    // 5\n#define PT_6    KC_6    // 6\n#define PT_7    KC_7    // 7\n#define PT_8    KC_8    // 8\n#define PT_9    KC_9    // 9\n#define PT_0    KC_0    // 0\n#define PT_QUOT KC_MINS // '\n#define PT_LDAQ KC_EQL  // «\n#define PT_Q    KC_Q    // Q\n#define PT_W    KC_W    // W\n#define PT_E    KC_E    // E\n#define PT_R    KC_R    // R\n#define PT_T    KC_T    // T\n#define PT_Y    KC_Y    // Y\n#define PT_U    KC_U    // U\n#define PT_I    KC_I    // I\n#define PT_O    KC_O    // O\n#define PT_P    KC_P    // P\n#define PT_PLUS KC_LBRC // +\n#define PT_ACUT KC_RBRC // ´ (dead)\n#define PT_A    KC_A    // A\n#define PT_S    KC_S    // S\n#define PT_D    KC_D    // D\n#define PT_F    KC_F    // F\n#define PT_G    KC_G    // G\n#define PT_H    KC_H    // H\n#define PT_J    KC_J    // J\n#define PT_K    KC_K    // K\n#define PT_L    KC_L    // L\n#define PT_CCED KC_SCLN // Ç\n#define PT_MORD KC_QUOT // º\n#define PT_TILD KC_NUHS // ~ (dead)\n#define PT_LABK KC_NUBS // <\n#define PT_Z    KC_Z    // Z\n#define PT_X    KC_X    // X\n#define PT_C    KC_C    // C\n#define PT_V    KC_V    // V\n#define PT_B    KC_B    // B\n#define PT_N    KC_N    // N\n#define PT_M    KC_M    // M\n#define PT_COMM KC_COMM // ,\n#define PT_DOT  KC_DOT  // .\n#define PT_MINS KC_SLSH // -\n#define PT_PIPE S(PT_BSLS) // |\n#define PT_EXLM S(PT_1)    // !\n#define PT_DQUO S(PT_2)    // \"\n#define PT_HASH S(PT_3)    // #\n#define PT_DLR  S(PT_4)    // $\n#define PT_PERC S(PT_5)    // %\n#define PT_AMPR S(PT_6)    // &\n#define PT_SLSH S(PT_7)    // /\n#define PT_LPRN S(PT_8)    // (\n#define PT_RPRN S(PT_9)    // )\n#define PT_EQL  S(PT_0)    // =\n#define PT_QUES S(PT_QUOT) // ?\n#define PT_RDAQ S(PT_LDAQ) // »\n#define PT_ASTR S(PT_PLUS) // *\n#define PT_GRV  S(PT_ACUT) // ` (dead)\n#define PT_FORD S(PT_MORD) // ª\n#define PT_CIRC S(PT_TILD) // ^ (dead)\n#define PT_RABK S(PT_LABK) // >\n#define PT_SCLN S(PT_COMM) // ;\n#define PT_COLN S(PT_DOT)  // :\n#define PT_UNDS S(PT_MINS) // _\n#define PT_AT   ALGR(PT_2)    // @\n#define PT_PND  ALGR(PT_3)    // £\n#define PT_SECT ALGR(PT_4)    // §\n#define PT_LCBR ALGR(PT_7)    // {\n#define PT_LBRC ALGR(PT_8)    // [\n#define PT_RBRC ALGR(PT_9)    // ]\n#define PT_RCBR ALGR(PT_0)    // }\n#define PT_DIAE ALGR(PT_PLUS) // ¨ (dead)\n#define PT_EURO ALGR(PT_E)    // €\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_portuguese_mac_iso.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_PORTUGUESE_MAC_ISO_KEYCODES_VERSION \"0.0.1\"\n#define QMK_PORTUGUESE_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_PORTUGUESE_MAC_ISO_KEYCODES_VERSION_MAJOR 0\n#define QMK_PORTUGUESE_MAC_ISO_KEYCODES_VERSION_MINOR 0\n#define QMK_PORTUGUESE_MAC_ISO_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define PT_SECT KC_GRV  // §\n#define PT_1    KC_1    // 1\n#define PT_2    KC_2    // 2\n#define PT_3    KC_3    // 3\n#define PT_4    KC_4    // 4\n#define PT_5    KC_5    // 5\n#define PT_6    KC_6    // 6\n#define PT_7    KC_7    // 7\n#define PT_8    KC_8    // 8\n#define PT_9    KC_9    // 9\n#define PT_0    KC_0    // 0\n#define PT_QUOT KC_MINS // '\n#define PT_PLUS KC_EQL  // +\n#define PT_Q    KC_Q    // Q\n#define PT_W    KC_W    // W\n#define PT_E    KC_E    // E\n#define PT_R    KC_R    // R\n#define PT_T    KC_T    // T\n#define PT_Y    KC_Y    // Y\n#define PT_U    KC_U    // U\n#define PT_I    KC_I    // I\n#define PT_O    KC_O    // O\n#define PT_P    KC_P    // P\n#define PT_MORD KC_LBRC // º\n#define PT_ACUT KC_RBRC // ´ (dead)\n#define PT_A    KC_A    // A\n#define PT_S    KC_S    // S\n#define PT_D    KC_D    // D\n#define PT_F    KC_F    // F\n#define PT_G    KC_G    // G\n#define PT_H    KC_H    // H\n#define PT_J    KC_J    // J\n#define PT_K    KC_K    // K\n#define PT_L    KC_L    // L\n#define PT_CCED KC_SCLN // Ç\n#define PT_TILD KC_QUOT // ~ (dead)\n#define PT_BSLS KC_NUHS // (backslash)\n#define PT_LABK KC_NUBS // <\n#define PT_Z    KC_Z    // Z\n#define PT_X    KC_X    // X\n#define PT_C    KC_C    // C\n#define PT_V    KC_V    // V\n#define PT_B    KC_B    // B\n#define PT_N    KC_N    // N\n#define PT_M    KC_M    // M\n#define PT_COMM KC_COMM // ,\n#define PT_DOT  KC_DOT  // .\n#define PT_MINS KC_SLSH // -\n#define PT_PLMN S(PT_SECT) // ±\n#define PT_EXLM S(PT_1)    // !\n#define PT_DQUO S(PT_2)    // \"\n#define PT_HASH S(PT_3)    // #\n#define PT_DLR  S(PT_4)    // $\n#define PT_PERC S(PT_5)    // %\n#define PT_AMPR S(PT_6)    // &\n#define PT_SLSH S(PT_7)    // /\n#define PT_LPRN S(PT_8)    // (\n#define PT_RPRN S(PT_9)    // )\n#define PT_EQL  S(PT_0)    // =\n#define PT_QUES S(PT_QUOT) // ?\n#define PT_ASTR S(PT_PLUS) // *\n#define PT_FORD S(PT_MORD) // ª\n#define PT_GRV  S(PT_ACUT) // ` (dead)\n#define PT_CIRC S(PT_TILD) // ^ (dead)\n#define PT_PIPE S(PT_BSLS) // |\n#define PT_RABK S(PT_LABK) // >\n#define PT_SCLN S(PT_COMM) // ;\n#define PT_COLN S(PT_DOT)  // :\n#define PT_UNDS S(PT_MINS) // _\n#define PT_APPL A(PT_1)    //  (Apple logo)\n#define PT_AT   A(PT_2)    // @\n#define PT_EURO A(PT_3)    // €\n#define PT_PND  A(PT_4)    // £\n#define PT_PERM A(PT_5)    // ‰\n#define PT_PILC A(PT_6)    // ¶\n#define PT_DIV  A(PT_7)    // ÷\n#define PT_LBRC A(PT_8)    // [\n#define PT_RBRC A(PT_9)    // ]\n#define PT_NEQL A(PT_0)    // ≠\n#define PT_OE   A(PT_Q)    // Œ\n#define PT_NARS A(PT_W)    // ∑\n#define PT_AE   A(PT_E)    // Æ\n#define PT_REGD A(PT_R)    // ®\n#define PT_TM   A(PT_T)    // ™\n#define PT_YEN  A(PT_Y)    // ¥\n#define PT_DAGG A(PT_U)    // †\n#define PT_DLSI A(PT_I)    // ı\n#define PT_OSTR A(PT_O)    // Ø\n#define PT_PI   A(PT_P)    // π\n#define PT_DEG  A(PT_MORD) // °\n#define PT_DIAE A(PT_ACUT) // ¨ (dead)\n#define PT_ARNG A(PT_A)    // å\n#define PT_SS   A(PT_S)    // ß\n#define PT_PDIF A(PT_D)    // ∂\n#define PT_FHK  A(PT_F)    // ƒ\n#define PT_DOTA A(PT_G)    // ˙\n#define PT_CARN A(PT_H)    // ˇ\n#define PT_MACR A(PT_J)    // ¯\n#define PT_DLQU A(PT_K)    // „\n#define PT_LSQU A(PT_L)    // ‘\n#define PT_CEDL A(PT_CCED) // ¸\n#define PT_STIL A(PT_TILD) // ˜ (dead)\n#define PT_LSAQ A(PT_BSLS) // ‹\n#define PT_LTEQ A(PT_LABK) // ≤\n#define PT_OMEG A(PT_Z)    // Ω\n#define PT_LDAQ A(PT_X)    // «\n#define PT_COPY A(PT_C)    // ©\n#define PT_SQRT A(PT_V)    // √\n#define PT_INTG A(PT_B)    // ∫\n#define PT_NOT  A(PT_N)    // ¬\n#define PT_MICR A(PT_M)    // µ\n#define PT_LDQU A(PT_COMM) // “\n#define PT_ELLP A(PT_DOT)  // …\n#define PT_MDSH A(PT_MINS) // —\n#define PT_IEXL S(A(PT_1))    // ¡\n#define PT_FI   S(A(PT_2))    // ﬁ\n#define PT_FL   S(A(PT_3))    // ﬂ\n#define PT_CENT S(A(PT_4))    // ¢\n#define PT_INFN S(A(PT_5))    // ∞\n#define PT_BULT S(A(PT_6))    // •\n#define PT_FRSL S(A(PT_7))    // ⁄\n#define PT_LCBR S(A(PT_8))    // {\n#define PT_RCBR S(A(PT_9))    // }\n#define PT_AEQL S(A(PT_0))    // ≈\n#define PT_IQUE S(A(PT_QUOT)) // ¿\n#define PT_LOZN S(A(PT_PLUS)) // ◊\n#define PT_DDAG S(A(PT_U))    // ‡\n#define PT_RNGA S(A(PT_I))    // ˚\n#define PT_NARP S(A(PT_P))    // ∏\n#define PT_DACU S(A(PT_ACUT)) // ˝\n#define PT_INCR S(A(PT_D))    // ∆\n#define PT_SLQU S(A(PT_K))    // ‚\n#define PT_RSQU S(A(PT_L))    // ’\n#define PT_OGON S(A(PT_CCED)) // ˛\n#define PT_DCIR S(A(PT_TILD)) // ˆ (dead)\n#define PT_RSAQ S(A(PT_BSLS)) // ›\n#define PT_GTEQ S(A(PT_LABK)) // ≥\n#define PT_RDAQ S(A(PT_X))    // »\n#define PT_RDQU S(A(PT_COMM)) // ”\n#define PT_MDDT S(A(PT_DOT))  // ·\n#define PT_NDSH S(A(PT_MINS)) // –\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_romanian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_ROMANIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_ROMANIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_ROMANIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_ROMANIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_ROMANIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define RO_DLQU KC_GRV  // „\n#define RO_1    KC_1    // 1\n#define RO_2    KC_2    // 2\n#define RO_3    KC_3    // 3\n#define RO_4    KC_4    // 4\n#define RO_5    KC_5    // 5\n#define RO_6    KC_6    // 6\n#define RO_7    KC_7    // 7\n#define RO_8    KC_8    // 8\n#define RO_9    KC_9    // 9\n#define RO_0    KC_0    // 0\n#define RO_MINS KC_MINS // -\n#define RO_EQL  KC_EQL  // =\n#define RO_Q    KC_Q    // Q\n#define RO_W    KC_W    // W\n#define RO_E    KC_E    // E\n#define RO_R    KC_R    // R\n#define RO_T    KC_T    // T\n#define RO_Y    KC_Y    // Y\n#define RO_U    KC_U    // U\n#define RO_I    KC_I    // I\n#define RO_O    KC_O    // O\n#define RO_P    KC_P    // P\n#define RO_ABRV KC_LBRC // Ă\n#define RO_ICIR KC_RBRC // Î\n#define RO_A    KC_A    // A\n#define RO_S    KC_S    // S\n#define RO_D    KC_D    // D\n#define RO_F    KC_F    // F\n#define RO_G    KC_G    // G\n#define RO_H    KC_H    // H\n#define RO_J    KC_J    // J\n#define RO_K    KC_K    // K\n#define RO_L    KC_L    // L\n#define RO_SCOM KC_SCLN // Ș\n#define RO_TCOM KC_QUOT // Ț\n#define RO_ACIR KC_NUHS // Â\n#define RO_BSLS KC_NUBS // (backslash)\n#define RO_Z    KC_Z    // Z\n#define RO_X    KC_X    // X\n#define RO_C    KC_C    // C\n#define RO_V    KC_V    // V\n#define RO_B    KC_B    // B\n#define RO_N    KC_N    // N\n#define RO_M    KC_M    // M\n#define RO_COMM KC_COMM // ,\n#define RO_DOT  KC_DOT  // .\n#define RO_SLSH KC_SLSH // /\n#define RO_RDQU S(RO_DLQU) // ”\n#define RO_EXLM S(RO_1)    // !\n#define RO_AT   S(RO_2)    // @\n#define RO_HASH S(RO_3)    // #\n#define RO_DLR  S(RO_4)    // $\n#define RO_PERC S(RO_5)    // %\n#define RO_CIRC S(RO_6)    // ^\n#define RO_AMPR S(RO_7)    // &\n#define RO_ASTR S(RO_8)    // *\n#define RO_LPRN S(RO_9)    // (\n#define RO_RPRN S(RO_0)    // )\n#define RO_UNDS S(RO_MINS) // _\n#define RO_PLUS S(RO_EQL)  // +\n#define RO_PIPE S(RO_BSLS) // |\n#define RO_SCLN S(RO_COMM) // ;\n#define RO_COLN S(RO_DOT)  // :\n#define RO_QUES S(RO_SLSH) // ?\n#define RO_GRV  ALGR(RO_DLQU) // `\n#define RO_DTIL ALGR(RO_1)    // ~ (dead)\n#define RO_CARN ALGR(RO_2)    // ˇ (dead)\n#define RO_DCIR ALGR(RO_3)    // ^ (dead)\n#define RO_BREV ALGR(RO_4)    // ˘ (dead)\n#define RO_RNGA ALGR(RO_5)    // ° (dead)\n#define RO_OGON ALGR(RO_6)    // ˛ (dead)\n#define RO_DGRV ALGR(RO_7)    // ` (dead)\n#define RO_DOTA ALGR(RO_8)    // ˙ (dead)\n#define RO_ACUT ALGR(RO_9)    // ´ (dead)\n#define RO_DACU ALGR(RO_0)    // ˝ (dead)\n#define RO_DIAE ALGR(RO_MINS) // ¨ (dead)\n#define RO_CEDL ALGR(RO_EQL)  // ¸ (dead)\n#define RO_EURO ALGR(RO_E)    // €\n#define RO_SECT ALGR(RO_P)    // §\n#define RO_LBRC ALGR(RO_ABRV) // [\n#define RO_RBRC ALGR(RO_ICIR) // ]\n#define RO_SS   ALGR(RO_S)    // ß\n#define RO_DSTR ALGR(RO_D)    // Đ\n#define RO_LSTR ALGR(RO_L)    // Ł\n#define RO_QUOT ALGR(RO_TCOM) // '\n#define RO_COPY ALGR(RO_C)    // ©\n#define RO_LABK ALGR(RO_COMM) // <\n#define RO_RABK ALGR(RO_DOT)  // >\n#define RO_TILD S(ALGR(RO_DLQU)) // ~\n#define RO_NDSH S(ALGR(RO_MINS)) // –\n#define RO_PLMN S(ALGR(RO_EQL))  // ±\n#define RO_LCBR S(ALGR(RO_ABRV)) // {\n#define RO_RCBR S(ALGR(RO_ICIR)) // }\n#define RO_DQUO S(ALGR(RO_TCOM)) // \"\n#define RO_LDAQ S(ALGR(RO_COMM)) // «\n#define RO_RDAQ S(ALGR(RO_DOT))  // »\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_russian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_RUSSIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_RUSSIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_RUSSIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_RUSSIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_RUSSIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define RU_YO   KC_GRV  // Ё\n#define RU_1    KC_1    // 1\n#define RU_2    KC_2    // 2\n#define RU_3    KC_3    // 3\n#define RU_4    KC_4    // 4\n#define RU_5    KC_5    // 5\n#define RU_6    KC_6    // 6\n#define RU_7    KC_7    // 7\n#define RU_8    KC_8    // 8\n#define RU_9    KC_9    // 9\n#define RU_0    KC_0    // 0\n#define RU_MINS KC_MINS // -\n#define RU_EQL  KC_EQL  // =\n#define RU_SHTI KC_Q    // Й\n#define RU_TSE  KC_W    // Ц\n#define RU_U    KC_E    // У\n#define RU_KA   KC_R    // К\n#define RU_IE   KC_T    // Е\n#define RU_EN   KC_Y    // Н\n#define RU_GHE  KC_U    // Г\n#define RU_SHA  KC_I    // Ш\n#define RU_SHCH KC_O    // Щ\n#define RU_ZE   KC_P    // З\n#define RU_HA   KC_LBRC // Х\n#define RU_HARD KC_RBRC // Ъ\n#define RU_BSLS KC_BSLS // (backslash)\n#define RU_EF   KC_A    // Ф\n#define RU_YERU KC_S    // Ы\n#define RU_VE   KC_D    // В\n#define RU_A    KC_F    // А\n#define RU_PE   KC_G    // П\n#define RU_ER   KC_H    // Р\n#define RU_O    KC_J    // О\n#define RU_EL   KC_K    // Л\n#define RU_DE   KC_L    // Д\n#define RU_ZHE  KC_SCLN // Ж\n#define RU_E    KC_QUOT // Э\n#define RU_YA   KC_Z    // Я\n#define RU_CHE  KC_X    // Ч\n#define RU_ES   KC_C    // С\n#define RU_EM   KC_V    // М\n#define RU_I    KC_B    // И\n#define RU_TE   KC_N    // Т\n#define RU_SOFT KC_M    // Ь\n#define RU_BE   KC_COMM // Б\n#define RU_YU   KC_DOT  // Ю\n#define RU_DOT  KC_SLSH // .\n#define RU_EXLM S(RU_1)    // !\n#define RU_DQUO S(RU_2)    // \"\n#define RU_NUM  S(RU_3)    // №\n#define RU_SCLN S(RU_4)    // ;\n#define RU_PERC S(RU_5)    // %\n#define RU_COLN S(RU_6)    // :\n#define RU_QUES S(RU_7)    // ?\n#define RU_ASTR S(RU_8)    // *\n#define RU_LPRN S(RU_9)    // (\n#define RU_RPRN S(RU_0)    // )\n#define RU_UNDS S(RU_MINS) // _\n#define RU_PLUS S(RU_EQL)  // +\n#define RU_SLSH S(RU_BSLS) // /\n#define RU_COMM S(RU_DOT)  // ,\n#define RU_RUBL ALGR(RU_8)    // ₽\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_russian_typewriter.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_RUSSIAN_TYPEWRITER_KEYCODES_VERSION \"0.0.1\"\n#define QMK_RUSSIAN_TYPEWRITER_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_RUSSIAN_TYPEWRITER_KEYCODES_VERSION_MAJOR 0\n#define QMK_RUSSIAN_TYPEWRITER_KEYCODES_VERSION_MINOR 0\n#define QMK_RUSSIAN_TYPEWRITER_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define RU_PIPE KC_GRV  // |\n#define RU_NUM  KC_1    // №\n#define RU_MINS KC_2    // -\n#define RU_SLSH KC_3    // /\n#define RU_DQUO KC_4    // \"\n#define RU_COLN KC_5    // :\n#define RU_COMM KC_6    // ,\n#define RU_DOT  KC_7    // .\n#define RU_UNDS KC_8    // _\n#define RU_QUES KC_9    // ?\n#define RU_PERC KC_0    // %\n#define RU_EXLM KC_MINS // !\n#define RU_SCLN KC_EQL  // ;\n#define RU_SHTI KC_Q    // Й\n#define RU_TSE  KC_W    // Ц\n#define RU_U    KC_E    // У\n#define RU_KA   KC_R    // К\n#define RU_IE   KC_T    // Е\n#define RU_EN   KC_Y    // Н\n#define RU_GHE  KC_U    // Г\n#define RU_SHA  KC_I    // Ш\n#define RU_SHCH KC_O    // Щ\n#define RU_ZE   KC_P    // З\n#define RU_HA   KC_LBRC // Х\n#define RU_HARD KC_RBRC // Ъ\n#define RU_RPRN KC_BSLS // )\n#define RU_EF   KC_A    // Ф\n#define RU_YERU KC_S    // Ы\n#define RU_VE   KC_D    // В\n#define RU_A    KC_F    // А\n#define RU_PE   KC_G    // П\n#define RU_ER   KC_H    // Р\n#define RU_O    KC_J    // О\n#define RU_EL   KC_K    // Л\n#define RU_DE   KC_L    // Д\n#define RU_ZHE  KC_SCLN // Ж\n#define RU_E    KC_QUOT // Э\n#define RU_YA   KC_Z    // Я\n#define RU_CHE  KC_X    // Ч\n#define RU_ES   KC_C    // С\n#define RU_EM   KC_V    // М\n#define RU_I    KC_B    // И\n#define RU_TE   KC_N    // Т\n#define RU_SOFT KC_M    // Ь\n#define RU_BE   KC_COMM // Б\n#define RU_YU   KC_DOT  // Ю\n#define RU_YO   KC_SLSH // Ё\n#define RU_PLUS S(RU_PIPE) // +\n#define RU_1    S(RU_NUM)  // 1\n#define RU_2    S(RU_MINS) // 2\n#define RU_3    S(RU_SLSH) // 3\n#define RU_4    S(RU_DQUO) // 4\n#define RU_5    S(RU_COLN) // 5\n#define RU_6    S(RU_COMM) // 6\n#define RU_7    S(RU_DOT)  // 7\n#define RU_8    S(RU_UNDS) // 8\n#define RU_9    S(RU_QUES) // 9\n#define RU_0    S(RU_PERC) // 0\n#define RU_EQL  S(RU_EXLM) // =\n#define RU_BSLS S(RU_SCLN) // (backslash)\n#define RU_LPRN S(RU_RPRN) // (\n#define RU_RUBL ALGR(RU_UNDS) // ₽\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_serbian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SERBIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SERBIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SERBIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_SERBIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_SERBIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define RS_GRV  KC_GRV  // `\n#define RS_1    KC_1    // 1\n#define RS_2    KC_2    // 2\n#define RS_3    KC_3    // 3\n#define RS_4    KC_4    // 4\n#define RS_5    KC_5    // 5\n#define RS_6    KC_6    // 6\n#define RS_7    KC_7    // 7\n#define RS_8    KC_8    // 8\n#define RS_9    KC_9    // 9\n#define RS_0    KC_0    // 0\n#define RS_QUOT KC_MINS // ' (dead)\n#define RS_PLUS KC_EQL  // +\n#define RS_LJE  KC_Q    // Љ\n#define RS_NJE  KC_W    // Њ\n#define RS_IE   KC_E    // Е\n#define RS_ER   KC_R    // Р\n#define RS_TE   KC_T    // Т\n#define RS_ZE   KC_Y    // З\n#define RS_U    KC_U    // У\n#define RS_I    KC_I    // И\n#define RS_O    KC_O    // О\n#define RS_PE   KC_P    // П\n#define RS_SHA  KC_LBRC // Ш\n#define RS_DJE  KC_RBRC // Ђ\n#define RS_A    KC_A    // А\n#define RS_ES   KC_S    // С\n#define RS_DE   KC_D    // Д\n#define RS_EF   KC_F    // Ф\n#define RS_GHE  KC_G    // Г\n#define RS_HA   KC_H    // Х\n#define RS_JE   KC_J    // Ј\n#define RS_KA   KC_K    // К\n#define RS_EL   KC_L    // Л\n#define RS_CHE  KC_SCLN // Ч\n#define RS_TSHE KC_QUOT // Ћ\n#define RS_ZHE  KC_NUHS // Ж\n#define RS_LABK KC_NUBS // <\n#define RS_DZE  KC_Z    // Ѕ\n#define RS_DZHE KC_X    // Џ\n#define RS_TSE  KC_C    // Ц\n#define RS_VE   KC_V    // В\n#define RS_BE   KC_B    // Б\n#define RS_EN   KC_N    // Н\n#define RS_EM   KC_M    // М\n#define RS_COMM KC_COMM // ,\n#define RS_DOT  KC_DOT  // .\n#define RS_MINS KC_SLSH // -\n#define RS_TILD S(RS_GRV)  // ~\n#define RS_EXLM S(RS_1)    // !\n#define RS_DQUO S(RS_2)    // \"\n#define RS_HASH S(RS_3)    // #\n#define RS_DLR  S(RS_4)    // $\n#define RS_PERC S(RS_5)    // %\n#define RS_AMPR S(RS_6)    // &\n#define RS_SLSH S(RS_7)    // /\n#define RS_LPRN S(RS_8)    // (\n#define RS_RPRN S(RS_9)    // )\n#define RS_EQL  S(RS_0)    // =\n#define RS_QUES S(RS_QUOT) // ?\n#define RS_ASTR S(RS_PLUS) // *\n#define RS_RABK S(RS_LABK) // >\n#define RS_SCLN S(RS_COMM) // ;\n#define RS_COLN S(RS_DOT)  // :\n#define RS_UNDS S(RS_MINS) // _\n#define RS_EURO ALGR(RS_IE)   // €\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_serbian_latin.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SERBIAN_LATIN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SERBIAN_LATIN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SERBIAN_LATIN_KEYCODES_VERSION_MAJOR 0\n#define QMK_SERBIAN_LATIN_KEYCODES_VERSION_MINOR 0\n#define QMK_SERBIAN_LATIN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define RS_SLQU KC_GRV  // ‚ (dead)\n#define RS_1    KC_1    // 1\n#define RS_2    KC_2    // 2\n#define RS_3    KC_3    // 3\n#define RS_4    KC_4    // 4\n#define RS_5    KC_5    // 5\n#define RS_6    KC_6    // 6\n#define RS_7    KC_7    // 7\n#define RS_8    KC_8    // 8\n#define RS_9    KC_9    // 9\n#define RS_0    KC_0    // 0\n#define RS_QUOT KC_MINS // '\n#define RS_PLUS KC_EQL  // +\n#define RS_Q    KC_Q    // Q\n#define RS_W    KC_W    // W\n#define RS_E    KC_E    // E\n#define RS_R    KC_R    // R\n#define RS_T    KC_T    // T\n#define RS_Z    KC_Y    // Z\n#define RS_U    KC_U    // U\n#define RS_I    KC_I    // I\n#define RS_O    KC_O    // O\n#define RS_P    KC_P    // P\n#define RS_SCAR KC_LBRC // Š\n#define RS_DSTR KC_RBRC // Đ\n#define RS_A    KC_A    // A\n#define RS_S    KC_S    // S\n#define RS_D    KC_D    // D\n#define RS_F    KC_F    // F\n#define RS_G    KC_G    // G\n#define RS_H    KC_H    // H\n#define RS_J    KC_J    // J\n#define RS_K    KC_K    // K\n#define RS_L    KC_L    // L\n#define RS_CCAR KC_SCLN // Č\n#define RS_CACU KC_QUOT // Ć\n#define RS_ZCAR KC_NUHS // Ž\n#define RS_LABK KC_NUBS // <\n#define RS_Y    KC_Z    // Y\n#define RS_X    KC_X    // X\n#define RS_C    KC_C    // C\n#define RS_V    KC_V    // V\n#define RS_B    KC_B    // B\n#define RS_N    KC_N    // N\n#define RS_M    KC_M    // M\n#define RS_COMM KC_COMM // ,\n#define RS_DOT  KC_DOT  // .\n#define RS_MINS KC_SLSH // -\n#define RS_TILD S(RS_SLQU) // ~\n#define RS_EXLM S(RS_1)    // !\n#define RS_DQUO S(RS_2)    // \"\n#define RS_HASH S(RS_3)    // #\n#define RS_DLR  S(RS_4)    // $\n#define RS_PERC S(RS_5)    // %\n#define RS_AMPR S(RS_6)    // &\n#define RS_SLSH S(RS_7)    // /\n#define RS_LPRN S(RS_8)    // (\n#define RS_RPRN S(RS_9)    // )\n#define RS_EQL  S(RS_0)    // =\n#define RS_QUES S(RS_QUOT) // ?\n#define RS_ASTR S(RS_PLUS) // *\n#define RS_RABK S(RS_LABK) // >\n#define RS_SCLN S(RS_COMM) // ;\n#define RS_COLN S(RS_DOT)  // :\n#define RS_UNDS S(RS_MINS) // _\n#define RS_CARN ALGR(RS_2)    // ˇ (dead)\n#define RS_CIRC ALGR(RS_3)    // ^ (dead)\n#define RS_BREV ALGR(RS_4)    // ˘ (dead)\n#define RS_RNGA ALGR(RS_5)    // ° (dead)\n#define RS_OGON ALGR(RS_6)    // ˛ (dead)\n#define RS_GRV  ALGR(RS_7)    // `\n#define RS_DOTA ALGR(RS_8)    // ˙ (dead)\n#define RS_ACUT ALGR(RS_9)    // ´ (dead)\n#define RS_DACU ALGR(RS_0)    // ˝ (dead)\n#define RS_DIAE ALGR(RS_QUOT) // ¨ (dead)\n#define RS_CEDL ALGR(RS_PLUS) // ¸ (dead)\n#define RS_BSLS ALGR(RS_Q)    // (backslash)\n#define RS_PIPE ALGR(RS_W)    // |\n#define RS_EURO ALGR(RS_E)    // €\n#define RS_DIV  ALGR(RS_SCAR) // ÷\n#define RS_MUL  ALGR(RS_DSTR) // ×\n#define RS_LBRC ALGR(RS_F)    // [\n#define RS_RBRC ALGR(RS_G)    // ]\n#define RS_LLST ALGR(RS_K)    // ł\n#define RS_CLST ALGR(RS_L)    // Ł\n#define RS_SS   ALGR(RS_CACU) // ß\n#define RS_CURR ALGR(RS_ZCAR) // ¤\n#define RS_AT   ALGR(RS_V)    // @\n#define RS_LCBR ALGR(RS_B)    // {\n#define RS_RCBR ALGR(RS_N)    // }\n#define RS_SECT ALGR(RS_M)    // §\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_slovak.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SLOVAK_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SLOVAK_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SLOVAK_KEYCODES_VERSION_MAJOR 0\n#define QMK_SLOVAK_KEYCODES_VERSION_MINOR 0\n#define QMK_SLOVAK_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define SK_SCLN KC_GRV  // ;\n#define SK_PLUS KC_1    // +\n#define SK_LCAR KC_2    // ľ\n#define SK_SCAR KC_3    // š\n#define SK_CCAR KC_4    // č\n#define SK_TCAR KC_5    // ť\n#define SK_ZCAR KC_6    // ž\n#define SK_YACU KC_7    // ý\n#define SK_AACU KC_8    // á\n#define SK_IACU KC_9    // í\n#define SK_EACU KC_0    // é\n#define SK_EQL  KC_MINS // =\n#define SK_ACUT KC_EQL  // ´ (dead)\n#define SK_Q    KC_Q    // Q\n#define SK_W    KC_W    // W\n#define SK_E    KC_E    // E\n#define SK_R    KC_R    // R\n#define SK_T    KC_T    // T\n#define SK_Z    KC_Y    // Z\n#define SK_U    KC_U    // U\n#define SK_I    KC_I    // I\n#define SK_O    KC_O    // O\n#define SK_P    KC_P    // P\n#define SK_UACU KC_LBRC // ú\n#define SK_ADIA KC_RBRC // ä\n#define SK_A    KC_A    // A\n#define SK_S    KC_S    // S\n#define SK_D    KC_D    // D\n#define SK_F    KC_F    // F\n#define SK_G    KC_G    // G\n#define SK_H    KC_H    // H\n#define SK_J    KC_J    // J\n#define SK_K    KC_K    // K\n#define SK_L    KC_L    // L\n#define SK_OCIR KC_SCLN // ô\n#define SK_SECT KC_QUOT // §\n#define SK_NCAR KC_NUHS // ň\n#define SK_AMPR KC_NUBS // &\n#define SK_Y    KC_Z    // Y\n#define SK_X    KC_X    // X\n#define SK_C    KC_C    // C\n#define SK_V    KC_V    // V\n#define SK_B    KC_B    // B\n#define SK_N    KC_N    // N\n#define SK_M    KC_M    // M\n#define SK_COMM KC_COMM // ,\n#define SK_DOT  KC_DOT  // .\n#define SK_MINS KC_SLSH // -\n#define SK_RNGA S(SK_SCLN) // ° (dead)\n#define SK_1    S(SK_PLUS) // 1\n#define SK_2    S(SK_LCAR) // 2\n#define SK_3    S(SK_SCAR) // 3\n#define SK_4    S(SK_CCAR) // 4\n#define SK_5    S(SK_TCAR) // 5\n#define SK_6    S(SK_ZCAR) // 6\n#define SK_7    S(SK_YACU) // 7\n#define SK_8    S(SK_AACU) // 8\n#define SK_9    S(SK_IACU) // 9\n#define SK_0    S(SK_EACU) // 0\n#define SK_PERC S(SK_EQL)  // %\n#define SK_CARN S(SK_ACUT) // ˇ (dead)\n#define SK_SLSH S(SK_UACU) // /\n#define SK_LPRN S(SK_ADIA) // (\n#define SK_DQUO S(SK_OCIR) // \"\n#define SK_EXLM S(SK_SECT) // !\n#define SK_RPRN S(SK_NCAR) // )\n#define SK_ASTR S(SK_AMPR) // *\n#define SK_QUES S(SK_COMM) // ?\n#define SK_COLN S(SK_DOT)  // :\n#define SK_UNDS S(SK_MINS) // _\n#define SK_TILD ALGR(SK_PLUS) // ~\n#define SK_CIRC ALGR(SK_SCAR) // ^ (dead)\n#define SK_BREV ALGR(SK_CCAR) // ˘ (dead)\n#define SK_OGON ALGR(SK_TCAR) // ˛ (dead)\n#define SK_GRV  ALGR(SK_ZCAR) // `\n#define SK_DOTA ALGR(SK_YACU) // ˙ (dead)\n#define SK_DACU ALGR(SK_EACU) // ˝ (dead)\n#define SK_DIAE ALGR(SK_EQL)  // ¨ (dead)\n#define SK_CEDL ALGR(SK_ACUT) // ¸ (dead)\n#define SK_BSLS ALGR(SK_Q)    // (backslash)\n#define SK_PIPE ALGR(SK_W)    // |\n#define SK_EURO ALGR(SK_E)    // €\n#define SK_QUOT ALGR(SK_P)    // '\n#define SK_DIV  ALGR(SK_UACU) // ÷\n#define SK_MUL  ALGR(SK_ADIA) // ×\n#define SK_LDST ALGR(SK_S)    // đ\n#define SK_CDST ALGR(SK_D)    // Đ\n#define SK_LBRC ALGR(SK_F)    // [\n#define SK_RBRC ALGR(SK_G)    // ]\n#define SK_LLST ALGR(SK_K)    // ł\n#define SK_CLST ALGR(SK_L)    // Ł\n#define SK_DLR  ALGR(SK_OCIR) // $\n#define SK_SS   ALGR(SK_SECT) // ß\n#define SK_CURR ALGR(SK_NCAR) // ¤\n#define SK_LABK ALGR(SK_AMPR) // <\n#define SK_RABK ALGR(SK_Y)    // >\n#define SK_HASH ALGR(SK_X)    // #\n#define SK_AT   ALGR(SK_V)    // @\n#define SK_LCBR ALGR(SK_B)    // {\n#define SK_RCBR ALGR(SK_N)    // }\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_slovenian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SLOVENIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SLOVENIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SLOVENIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_SLOVENIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_SLOVENIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define SI_CEDL KC_GRV  // ¸ (dead)\n#define SI_1    KC_1    // 1\n#define SI_2    KC_2    // 2\n#define SI_3    KC_3    // 3\n#define SI_4    KC_4    // 4\n#define SI_5    KC_5    // 5\n#define SI_6    KC_6    // 6\n#define SI_7    KC_7    // 7\n#define SI_8    KC_8    // 8\n#define SI_9    KC_9    // 9\n#define SI_0    KC_0    // 0\n#define SI_QUOT KC_MINS // '\n#define SI_PLUS KC_EQL  // +\n#define SI_Q    KC_Q    // Q\n#define SI_W    KC_W    // W\n#define SI_E    KC_E    // E\n#define SI_R    KC_R    // R\n#define SI_T    KC_T    // T\n#define SI_Z    KC_Y    // Z\n#define SI_U    KC_U    // U\n#define SI_I    KC_I    // I\n#define SI_O    KC_O    // O\n#define SI_P    KC_P    // P\n#define SI_SCAR KC_LBRC // Š\n#define SI_DSTR KC_RBRC // Đ\n#define SI_A    KC_A    // A\n#define SI_S    KC_S    // S\n#define SI_D    KC_D    // D\n#define SI_F    KC_F    // F\n#define SI_G    KC_G    // G\n#define SI_H    KC_H    // H\n#define SI_J    KC_J    // J\n#define SI_K    KC_K    // K\n#define SI_L    KC_L    // L\n#define SI_CCAR KC_SCLN // Č\n#define SI_CACU KC_QUOT // Ć\n#define SI_ZCAR KC_NUHS // Ž\n#define SI_LABK KC_NUBS // <\n#define SI_Y    KC_Z    // Y\n#define SI_X    KC_X    // X\n#define SI_C    KC_C    // C\n#define SI_V    KC_V    // V\n#define SI_B    KC_B    // B\n#define SI_N    KC_N    // N\n#define SI_M    KC_M    // M\n#define SI_COMM KC_COMM // ,\n#define SI_DOT  KC_DOT  // .\n#define SI_MINS KC_SLSH // -\n#define SI_DIAE S(SI_CEDL) // ¨ (dead)\n#define SI_EXLM S(SI_1)    // !\n#define SI_DQUO S(SI_2)    // \"\n#define SI_HASH S(SI_3)    // #\n#define SI_DLR  S(SI_4)    // $\n#define SI_PERC S(SI_5)    // %\n#define SI_AMPR S(SI_6)    // &\n#define SI_SLSH S(SI_7)    // /\n#define SI_LPRN S(SI_8)    // (\n#define SI_RPRN S(SI_9)    // )\n#define SI_EQL  S(SI_0)    // =\n#define SI_QUES S(SI_QUOT) // ?\n#define SI_ASTR S(SI_PLUS) // *\n#define SI_RABK S(SI_LABK) // >\n#define SI_SCLN S(SI_COMM) // ;\n#define SI_COLN S(SI_DOT)  // :\n#define SI_UNDS S(SI_MINS) // _\n#define SI_TILD ALGR(SI_1)    // ~\n#define SI_CARN ALGR(SI_2)    // ˇ (dead)\n#define SI_CIRC ALGR(SI_3)    // ^ (dead)\n#define SI_BREV ALGR(SI_4)    // ˘ (dead)\n#define SI_RNGA ALGR(SI_5)    // ° (dead)\n#define SI_OGON ALGR(SI_6)    // ˛ (dead)\n#define SI_GRV  ALGR(SI_7)    // `\n#define SI_DOTA ALGR(SI_8)    // ˙ (dead)\n#define SI_ACUT ALGR(SI_9)    // ´ (dead)\n#define SI_DACU ALGR(SI_0)    // ˝ (dead)\n#define SI_BSLS ALGR(SI_Q)    // (backslash)\n#define SI_PIPE ALGR(SI_W)    // |\n#define SI_EURO ALGR(SI_E)    // €\n#define SI_DIV  ALGR(SI_SCAR) // ÷\n#define SI_MUL  ALGR(SI_DSTR) // ×\n#define SI_LBRC ALGR(SI_F)    // [\n#define SI_RBRC ALGR(SI_G)    // ]\n#define SI_LLST ALGR(SI_K)    // ł\n#define SI_CLST ALGR(SI_L)    // Ł\n#define SI_SS   ALGR(SI_CACU) // ß\n#define SI_CURR ALGR(SI_ZCAR) // ¤\n#define SI_AT   ALGR(SI_V)    // @\n#define SI_LCBR ALGR(SI_B)    // {\n#define SI_RCBR ALGR(SI_N)    // }\n#define SI_SECT ALGR(SI_M)    // §\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_spanish.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SPANISH_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SPANISH_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SPANISH_KEYCODES_VERSION_MAJOR 0\n#define QMK_SPANISH_KEYCODES_VERSION_MINOR 0\n#define QMK_SPANISH_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define ES_MORD KC_GRV  // º\n#define ES_1    KC_1    // 1\n#define ES_2    KC_2    // 2\n#define ES_3    KC_3    // 3\n#define ES_4    KC_4    // 4\n#define ES_5    KC_5    // 5\n#define ES_6    KC_6    // 6\n#define ES_7    KC_7    // 7\n#define ES_8    KC_8    // 8\n#define ES_9    KC_9    // 9\n#define ES_0    KC_0    // 0\n#define ES_QUOT KC_MINS // '\n#define ES_IEXL KC_EQL  // ¡\n#define ES_Q    KC_Q    // Q\n#define ES_W    KC_W    // W\n#define ES_E    KC_E    // E\n#define ES_R    KC_R    // R\n#define ES_T    KC_T    // T\n#define ES_Y    KC_Y    // Y\n#define ES_U    KC_U    // U\n#define ES_I    KC_I    // I\n#define ES_O    KC_O    // O\n#define ES_P    KC_P    // P\n#define ES_GRV  KC_LBRC // ` (dead)\n#define ES_PLUS KC_RBRC // +\n#define ES_A    KC_A    // A\n#define ES_S    KC_S    // S\n#define ES_D    KC_D    // D\n#define ES_F    KC_F    // F\n#define ES_G    KC_G    // G\n#define ES_H    KC_H    // H\n#define ES_J    KC_J    // J\n#define ES_K    KC_K    // K\n#define ES_L    KC_L    // L\n#define ES_NTIL KC_SCLN // Ñ\n#define ES_ACUT KC_QUOT // ´ (dead)\n#define ES_CCED KC_NUHS // Ç\n#define ES_LABK KC_NUBS // <\n#define ES_Z    KC_Z    // Z\n#define ES_X    KC_X    // X\n#define ES_C    KC_C    // C\n#define ES_V    KC_V    // V\n#define ES_B    KC_B    // B\n#define ES_N    KC_N    // N\n#define ES_M    KC_M    // M\n#define ES_COMM KC_COMM // ,\n#define ES_DOT  KC_DOT  // .\n#define ES_MINS KC_SLSH // -\n#define ES_FORD S(ES_MORD) // ª\n#define ES_EXLM S(ES_1)    // !\n#define ES_DQUO S(ES_2)    // \"\n#define ES_BULT S(ES_3)    // ·\n#define ES_DLR  S(ES_4)    // $\n#define ES_PERC S(ES_5)    // %\n#define ES_AMPR S(ES_6)    // &\n#define ES_SLSH S(ES_7)    // /\n#define ES_LPRN S(ES_8)    // (\n#define ES_RPRN S(ES_9)    // )\n#define ES_EQL  S(ES_0)    // =\n#define ES_QUES S(ES_QUOT) // ?\n#define ES_IQUE S(ES_IEXL) // ¿\n#define ES_CIRC S(ES_GRV)  // ^ (dead)\n#define ES_ASTR S(ES_PLUS) // *\n#define ES_DIAE S(ES_ACUT) // ¨ (dead)\n#define ES_RABK S(ES_LABK) // >\n#define ES_SCLN S(KC_COMM) // ;\n#define ES_COLN S(KC_DOT)  // :\n#define ES_UNDS S(ES_MINS) // _\n#define ES_BSLS ALGR(ES_MORD) // (backslash)\n#define ES_PIPE ALGR(ES_1)    // |\n#define ES_AT   ALGR(ES_2)    // @\n#define ES_HASH ALGR(ES_3)    // #\n#define ES_TILD ALGR(ES_4)    // ~\n#define ES_EURO ALGR(ES_5)    // €\n#define ES_NOT  ALGR(ES_6)    // ¬\n#define ES_LBRC ALGR(ES_GRV)  // [\n#define ES_RBRC ALGR(ES_PLUS) // ]\n#define ES_LCBR ALGR(ES_ACUT) // {\n#define ES_RCBR ALGR(ES_CCED) // }\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_spanish_dvorak.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SPANISH_DVORAK_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SPANISH_DVORAK_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SPANISH_DVORAK_KEYCODES_VERSION_MAJOR 0\n#define QMK_SPANISH_DVORAK_KEYCODES_VERSION_MINOR 0\n#define QMK_SPANISH_DVORAK_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define DV_MORD KC_GRV  // º\n#define DV_1    KC_1    // 1\n#define DV_2    KC_2    // 2\n#define DV_3    KC_3    // 3\n#define DV_4    KC_4    // 4\n#define DV_5    KC_5    // 5\n#define DV_6    KC_6    // 6\n#define DV_7    KC_7    // 7\n#define DV_8    KC_8    // 8\n#define DV_9    KC_9    // 9\n#define DV_0    KC_0    // 0\n#define DV_QUOT KC_MINS // '\n#define DV_IEXL KC_EQL  // ¡\n#define DV_DOT  KC_Q    // .\n#define DV_COMM KC_W    // ,\n#define DV_NTIL KC_E    // Ñ\n#define DV_P    KC_R    // P\n#define DV_Y    KC_T    // Y\n#define DV_F    KC_Y    // F\n#define DV_G    KC_U    // G\n#define DV_C    KC_I    // C\n#define DV_H    KC_O    // H\n#define DV_L    KC_P    // L\n#define DV_GRV  KC_LBRC // ` (dead)\n#define DV_PLUS KC_RBRC // +\n#define DV_A    KC_A    // A\n#define DV_O    KC_S    // O\n#define DV_E    KC_D    // E\n#define DV_U    KC_F    // U\n#define DV_I    KC_G    // I\n#define DV_D    KC_H    // D\n#define DV_R    KC_J    // R\n#define DV_T    KC_K    // T\n#define DV_N    KC_L    // N\n#define DV_S    KC_SCLN // S\n#define DV_ACUT KC_QUOT // ´ (dead)\n#define DV_CCED KC_NUHS // Ç\n#define DV_LABK KC_NUBS // <\n#define DV_MINS KC_Z    // -\n#define DV_Q    KC_X    // Q\n#define DV_J    KC_C    // J\n#define DV_K    KC_V    // K\n#define DV_X    KC_B    // X\n#define DV_B    KC_N    // B\n#define DV_M    KC_M    // M\n#define DV_W    KC_COMM // W\n#define DV_V    KC_DOT  // V\n#define DV_Z    KC_SLSH // Z\n#define DV_FORD S(DV_MORD) // ª\n#define DV_EXLM S(DV_1)    // !\n#define DV_DQUO S(DV_2)    // \"\n#define DV_BULT S(DV_3)    // ·\n#define DV_DLR  S(DV_4)    // $\n#define DV_PERC S(DV_5)    // %\n#define DV_AMPR S(DV_6)    // &\n#define DV_SLSH S(DV_7)    // /\n#define DV_LPRN S(DV_8)    // (\n#define DV_RPRN S(DV_9)    // )\n#define DV_EQL  S(DV_0)    // =\n#define DV_QUES S(DV_QUOT) // ?\n#define DV_IQUE S(DV_IEXL) // ¿\n#define DV_COLN S(DV_DOT)  // :\n#define DV_SCLN S(DV_COMM) // ;\n#define DV_CIRC S(DV_GRV)  // ^ (dead)\n#define DV_ASTR S(DV_PLUS) // *\n#define DV_DIAE S(DV_ACUT) // ¨ (dead)\n#define DV_RABK S(DV_LABK) // >\n#define DV_UNDS S(DV_MINS) // _\n#define DV_BSLS ALGR(DV_MORD) // (backslash)\n#define DV_PIPE ALGR(DV_1)    // |\n#define DV_AT   ALGR(DV_2)    // @\n#define DV_HASH ALGR(DV_3)    // #\n#define DV_TILD ALGR(DV_4)    // ~\n#define DV_EURO ALGR(DV_5)    // €\n#define DV_NOT  ALGR(DV_6)    // ¬\n#define DV_LBRC ALGR(DV_GRV)  // [\n#define DV_RBRC ALGR(DV_PLUS) // ]\n#define DV_LCBR ALGR(DV_ACUT) // {\n#define DV_RCBR ALGR(DV_CCED) // }\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_spanish_latin_america.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SPANISH_LATIN_AMERICA_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SPANISH_LATIN_AMERICA_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SPANISH_LATIN_AMERICA_KEYCODES_VERSION_MAJOR 0\n#define QMK_SPANISH_LATIN_AMERICA_KEYCODES_VERSION_MINOR 0\n#define QMK_SPANISH_LATIN_AMERICA_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define ES_PIPE KC_GRV  // |\n#define ES_1    KC_1    // 1\n#define ES_2    KC_2    // 2\n#define ES_3    KC_3    // 3\n#define ES_4    KC_4    // 4\n#define ES_5    KC_5    // 5\n#define ES_6    KC_6    // 6\n#define ES_7    KC_7    // 7\n#define ES_8    KC_8    // 8\n#define ES_9    KC_9    // 9\n#define ES_0    KC_0    // 0\n#define ES_QUOT KC_MINS // '\n#define ES_IQUE KC_EQL  // ¿\n#define ES_Q    KC_Q    // Q\n#define ES_W    KC_W    // W\n#define ES_E    KC_E    // E\n#define ES_R    KC_R    // R\n#define ES_T    KC_T    // T\n#define ES_Y    KC_Y    // Y\n#define ES_U    KC_U    // U\n#define ES_I    KC_I    // I\n#define ES_O    KC_O    // O\n#define ES_P    KC_P    // P\n#define ES_ACUT KC_LBRC // ´ (dead)\n#define ES_PLUS KC_RBRC // +\n#define ES_A    KC_A    // A\n#define ES_S    KC_S    // S\n#define ES_D    KC_D    // D\n#define ES_F    KC_F    // F\n#define ES_G    KC_G    // G\n#define ES_H    KC_H    // H\n#define ES_J    KC_J    // J\n#define ES_K    KC_K    // K\n#define ES_L    KC_L    // L\n#define ES_NTIL KC_SCLN // Ñ\n#define ES_LCBR KC_QUOT // {\n#define ES_RCBR KC_NUHS // }\n#define ES_LABK KC_NUBS // <\n#define ES_Z    KC_Z    // Z\n#define ES_X    KC_X    // X\n#define ES_C    KC_C    // C\n#define ES_V    KC_V    // V\n#define ES_B    KC_B    // B\n#define ES_N    KC_N    // N\n#define ES_M    KC_M    // M\n#define ES_COMM KC_COMM // ,\n#define ES_DOT  KC_DOT  // .\n#define ES_MINS KC_SLSH // -\n#define ES_MORD S(ES_PIPE) // °\n#define ES_EXLM S(ES_1)    // !\n#define ES_DQUO S(ES_2)    // \"\n#define ES_NUMB S(ES_3)    // #\n#define ES_DLR  S(ES_4)    // $\n#define ES_PERC S(ES_5)    // %\n#define ES_AMPR S(ES_6)    // &\n#define ES_SLSH S(ES_7)    // /\n#define ES_LPRN S(ES_8)    // (\n#define ES_RPRN S(ES_9)    // )\n#define ES_EQL  S(ES_0)    // =\n#define ES_QUES S(ES_QUOT) // ?\n#define ES_IEXL S(ES_IQUE) // ¡\n#define ES_DIAE S(ES_ACUT) // ¨ (dead)\n#define ES_ASTR S(ES_PLUS) // *\n#define ES_LBRC S(ES_LCBR) // [\n#define ES_RBRC S(ES_RCBR) // ]\n#define ES_RABK S(ES_LABK) // >\n#define ES_SCLN S(ES_COMM) // ;\n#define ES_COLN S(ES_DOT)  // :\n#define ES_UNDS S(ES_MINS) // _\n#define ES_NOT  ALGR(ES_PIPE) // ¬\n#define ES_BSLS ALGR(ES_QUOT) // (backslash)\n#define ES_AT   ALGR(ES_Q)    // @\n#define ES_TILD ALGR(ES_PLUS) // ~\n#define ES_CIRC ALGR(ES_LCBR) // ^\n#define ES_GRV  ALGR(KC_NUHS) // `\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_steno.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#pragma message(\"keymap_steno.h include is no longer required\")\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_swedish.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SWEDISH_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SWEDISH_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SWEDISH_KEYCODES_VERSION_MAJOR 0\n#define QMK_SWEDISH_KEYCODES_VERSION_MINOR 0\n#define QMK_SWEDISH_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define SE_SECT KC_GRV  // §\n#define SE_1    KC_1    // 1\n#define SE_2    KC_2    // 2\n#define SE_3    KC_3    // 3\n#define SE_4    KC_4    // 4\n#define SE_5    KC_5    // 5\n#define SE_6    KC_6    // 6\n#define SE_7    KC_7    // 7\n#define SE_8    KC_8    // 8\n#define SE_9    KC_9    // 9\n#define SE_0    KC_0    // 0\n#define SE_PLUS KC_MINS // +\n#define SE_ACUT KC_EQL  // ´ (dead)\n#define SE_Q    KC_Q    // Q\n#define SE_W    KC_W    // W\n#define SE_E    KC_E    // E\n#define SE_R    KC_R    // R\n#define SE_T    KC_T    // T\n#define SE_Y    KC_Y    // Y\n#define SE_U    KC_U    // U\n#define SE_I    KC_I    // I\n#define SE_O    KC_O    // O\n#define SE_P    KC_P    // P\n#define SE_ARNG KC_LBRC // Å\n#define SE_DIAE KC_RBRC // ¨ (dead)\n#define SE_A    KC_A    // A\n#define SE_S    KC_S    // S\n#define SE_D    KC_D    // D\n#define SE_F    KC_F    // F\n#define SE_G    KC_G    // G\n#define SE_H    KC_H    // H\n#define SE_J    KC_J    // J\n#define SE_K    KC_K    // K\n#define SE_L    KC_L    // L\n#define SE_ODIA KC_SCLN // Ö\n#define SE_ADIA KC_QUOT // Ä\n#define SE_QUOT KC_NUHS // '\n#define SE_LABK KC_NUBS // <\n#define SE_Z    KC_Z    // Z\n#define SE_X    KC_X    // X\n#define SE_C    KC_C    // C\n#define SE_V    KC_V    // V\n#define SE_B    KC_B    // B\n#define SE_N    KC_N    // N\n#define SE_M    KC_M    // M\n#define SE_COMM KC_COMM // ,\n#define SE_DOT  KC_DOT  // .\n#define SE_MINS KC_SLSH // -\n#define SE_HALF S(SE_SECT) // ½\n#define SE_EXLM S(SE_1)    // !\n#define SE_DQUO S(SE_2)    // \"\n#define SE_HASH S(SE_3)    // #\n#define SE_CURR S(SE_4)    // ¤\n#define SE_PERC S(SE_5)    // %\n#define SE_AMPR S(SE_6)    // &\n#define SE_SLSH S(SE_7)    // /\n#define SE_LPRN S(SE_8)    // (\n#define SE_RPRN S(SE_9)    // )\n#define SE_EQL  S(SE_0)    // =\n#define SE_QUES S(SE_PLUS) // ?\n#define SE_GRV  S(SE_ACUT) // ` (dead)\n#define SE_CIRC S(SE_DIAE) // ^ (dead)\n#define SE_ASTR S(SE_QUOT) // *\n#define SE_RABK S(SE_LABK) // >\n#define SE_SCLN S(SE_COMM) // ;\n#define SE_COLN S(SE_DOT)  // :\n#define SE_UNDS S(SE_MINS) // _\n#define SE_AT   ALGR(SE_2)    // @\n#define SE_PND  ALGR(SE_3)    // £\n#define SE_DLR  ALGR(SE_4)    // $\n#define SE_EURO ALGR(SE_5)    // €\n#define SE_LCBR ALGR(SE_7)    // {\n#define SE_LBRC ALGR(SE_8)    // [\n#define SE_RBRC ALGR(SE_9)    // ]\n#define SE_RCBR ALGR(SE_0)    // }\n#define SE_BSLS ALGR(SE_PLUS) // (backslash)\n#define SE_TILD ALGR(SE_DIAE) // ~ (dead)\n#define SE_PIPE ALGR(SE_LABK) // |\n#define SE_MICR ALGR(SE_M)    // µ\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_swedish_mac_ansi.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SWEDISH_MAC_ANSI_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SWEDISH_MAC_ANSI_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SWEDISH_MAC_ANSI_KEYCODES_VERSION_MAJOR 0\n#define QMK_SWEDISH_MAC_ANSI_KEYCODES_VERSION_MINOR 0\n#define QMK_SWEDISH_MAC_ANSI_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define SE_LABK KC_GRV  // <\n#define SE_1    KC_1    // 1\n#define SE_2    KC_2    // 2\n#define SE_3    KC_3    // 3\n#define SE_4    KC_4    // 4\n#define SE_5    KC_5    // 5\n#define SE_6    KC_6    // 6\n#define SE_7    KC_7    // 7\n#define SE_8    KC_8    // 8\n#define SE_9    KC_9    // 9\n#define SE_0    KC_0    // 0\n#define SE_PLUS KC_MINS // +\n#define SE_ACUT KC_EQL  // ´ (dead)\n#define SE_Q    KC_Q    // Q\n#define SE_W    KC_W    // W\n#define SE_E    KC_E    // E\n#define SE_R    KC_R    // R\n#define SE_T    KC_T    // T\n#define SE_Y    KC_Y    // Y\n#define SE_U    KC_U    // U\n#define SE_I    KC_I    // I\n#define SE_O    KC_O    // O\n#define SE_P    KC_P    // P\n#define SE_ARNG KC_LBRC // Å\n#define SE_DIAE KC_RBRC // ¨ (dead)\n#define SE_QUOT KC_NUHS // '\n#define SE_A    KC_A    // A\n#define SE_S    KC_S    // S\n#define SE_D    KC_D    // D\n#define SE_F    KC_F    // F\n#define SE_G    KC_G    // G\n#define SE_H    KC_H    // H\n#define SE_J    KC_J    // J\n#define SE_K    KC_K    // K\n#define SE_L    KC_L    // L\n#define SE_ODIA KC_SCLN // Ö\n#define SE_ADIA KC_QUOT // Ä\n#define SE_Z    KC_Z    // Z\n#define SE_X    KC_X    // X\n#define SE_C    KC_C    // C\n#define SE_V    KC_V    // V\n#define SE_B    KC_B    // B\n#define SE_N    KC_N    // N\n#define SE_M    KC_M    // M\n#define SE_COMM KC_COMM // ,\n#define SE_DOT  KC_DOT  // .\n#define SE_MINS KC_SLSH // -\n#define SE_RABK S(SE_LABK) // >\n#define SE_EXLM S(SE_1)    // !\n#define SE_DQUO S(SE_2)    // \"\n#define SE_HASH S(SE_3)    // #\n#define SE_EURO S(SE_4)    // €\n#define SE_PERC S(SE_5)    // %\n#define SE_AMPR S(SE_6)    // &\n#define SE_SLSH S(SE_7)    // /\n#define SE_LPRN S(SE_8)    // (\n#define SE_RPRN S(SE_9)    // )\n#define SE_EQL  S(SE_0)    // =\n#define SE_QUES S(SE_PLUS) // ?\n#define SE_GRV  S(SE_ACUT) // `\n#define SE_CIRC S(SE_DIAE) // ^ (dead)\n#define SE_ASTR S(SE_QUOT) // *\n#define SE_SCLN S(SE_COMM) // ;\n#define SE_COLN S(SE_DOT)  // :\n#define SE_UNDS S(SE_MINS) // _\n#define SE_LTEQ A(SE_LABK) // ≤\n#define SE_COPY A(SE_1)    // ©\n#define SE_TM   A(SE_2)    // ™\n#define SE_PND  A(SE_3)    // £\n#define SE_DLR  A(SE_4)    // $\n#define SE_INFN A(SE_5)    // ∞\n#define SE_SECT A(SE_6)    // §\n#define SE_PIPE A(SE_7)    // |\n#define SE_LBRC A(SE_8)    // [\n#define SE_RBRC A(SE_9)    // ]\n#define SE_AEQL A(SE_0)    // ≈\n#define SE_PLMN A(SE_PLUS) // ±\n#define SE_BULT A(SE_Q)    // •\n#define SE_OMEG A(SE_W)    // Ω\n#define SE_EACU A(SE_E)    // É\n#define SE_REGD A(SE_R)    // ®\n#define SE_DAGG A(SE_T)    // †\n#define SE_MICR A(SE_Y)    // µ\n#define SE_UDIA A(SE_U)    // Ü\n#define SE_DLSI A(SE_I)    // ı\n#define SE_OE   A(SE_O)    // Œ\n#define SE_PI   A(SE_P)    // π\n#define SE_DOTA A(SE_ARNG) // ˙\n#define SE_TILD A(SE_DIAE) // ~ (dead)\n#define SE_AT   A(SE_QUOT) // @\n#define SE_APPL A(SE_A)    //  (Apple logo)\n#define SE_SS   A(SE_S)    // ß\n#define SE_PDIF A(SE_D)    // ∂\n#define SE_FHK  A(SE_F)    // ƒ\n#define SE_CEDL A(SE_G)    // ¸\n#define SE_OGON A(SE_H)    // ˛\n#define SE_SQRT A(SE_J)    // √\n#define SE_FORD A(SE_K)    // ª\n#define SE_FI   A(SE_L)    // ﬁ\n#define SE_OSTR A(SE_ODIA) // Ø\n#define SE_AE   A(SE_ADIA) // Æ\n#define SE_DIV  A(SE_Z)    // ÷\n#define SE_CCED A(SE_C)    // Ç\n#define SE_LSAQ A(SE_V)    // ‹\n#define SE_RSAQ A(SE_B)    // ›\n#define SE_LSQU A(SE_N)    // ‘\n#define SE_RSQU A(SE_M)    // ’\n#define SE_SLQU A(SE_COMM) // ‚\n#define SE_ELLP A(SE_DOT)  // …\n#define SE_NDSH A(SE_MINS) // –\n#define SE_GTEQ S(A(SE_LABK)) // ≥\n#define SE_IEXL S(A(SE_1))    // ¡\n#define SE_YEN  S(A(SE_3))    // ¥\n#define SE_CENT S(A(SE_4))    // ¢\n#define SE_PERM S(A(SE_5))    // ‰\n#define SE_PILC S(A(SE_6))    // ¶\n#define SE_BSLS S(A(SE_7))    // (backslash)\n#define SE_LCBR S(A(SE_8))    // {\n#define SE_RCBR S(A(SE_9))    // }\n#define SE_NEQL S(A(SE_0))    // ≠\n#define SE_IQUE S(A(SE_PLUS)) // ¿\n#define SE_DEG  S(A(SE_Q))    // °\n#define SE_DACU S(A(SE_W))    // ˝\n#define SE_DDAG S(A(SE_T))    // ‡\n#define SE_STIL S(A(SE_Y))    // ˜\n#define SE_DCIR S(A(SE_I))    // ˆ\n#define SE_NARP S(A(SE_P))    // ∏\n#define SE_RNGA S(A(SE_ARNG)) // ˚\n#define SE_LOZN S(A(SE_A))    // ◊\n#define SE_NARS S(A(SE_S))    // ∑\n#define SE_INCR S(A(SE_D))    // ∆\n#define SE_INTG S(A(SE_F))    // ∫\n#define SE_MACR S(A(SE_G))    // ¯\n#define SE_BREV S(A(SE_H))    // ˘\n#define SE_NOT  S(A(SE_J))    // ¬\n#define SE_MORD S(A(SE_K))    // º\n#define SE_FL   S(A(SE_L))    // ﬂ\n#define SE_FRSL S(A(SE_Z))    // ⁄\n#define SE_CARN S(A(SE_X))    // ˇ\n#define SE_LDAQ S(A(SE_V))    // «\n#define SE_RDAQ S(A(SE_B))    // »\n#define SE_LDQU S(A(SE_N))    // “\n#define SE_RDQU S(A(SE_M))    // ”\n#define SE_DLQU S(A(SE_COMM)) // „\n#define SE_MDDT S(A(SE_DOT))  // ·\n#define SE_MDSH S(A(SE_MINS)) // —\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_swedish_mac_iso.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SWEDISH_MAC_ISO_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SWEDISH_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SWEDISH_MAC_ISO_KEYCODES_VERSION_MAJOR 0\n#define QMK_SWEDISH_MAC_ISO_KEYCODES_VERSION_MINOR 0\n#define QMK_SWEDISH_MAC_ISO_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define SE_SECT KC_GRV  // §\n#define SE_1    KC_1    // 1\n#define SE_2    KC_2    // 2\n#define SE_3    KC_3    // 3\n#define SE_4    KC_4    // 4\n#define SE_5    KC_5    // 5\n#define SE_6    KC_6    // 6\n#define SE_7    KC_7    // 7\n#define SE_8    KC_8    // 8\n#define SE_9    KC_9    // 9\n#define SE_0    KC_0    // 0\n#define SE_PLUS KC_MINS // +\n#define SE_ACUT KC_EQL  // ´ (dead)\n#define SE_Q    KC_Q    // Q\n#define SE_W    KC_W    // W\n#define SE_E    KC_E    // E\n#define SE_R    KC_R    // R\n#define SE_T    KC_T    // T\n#define SE_Y    KC_Y    // Y\n#define SE_U    KC_U    // U\n#define SE_I    KC_I    // I\n#define SE_O    KC_O    // O\n#define SE_P    KC_P    // P\n#define SE_ARNG KC_LBRC // Å\n#define SE_DIAE KC_RBRC // ¨ (dead)\n#define SE_A    KC_A    // A\n#define SE_S    KC_S    // S\n#define SE_D    KC_D    // D\n#define SE_F    KC_F    // F\n#define SE_G    KC_G    // G\n#define SE_H    KC_H    // H\n#define SE_J    KC_J    // J\n#define SE_K    KC_K    // K\n#define SE_L    KC_L    // L\n#define SE_ODIA KC_SCLN // Ö\n#define SE_ADIA KC_QUOT // Ä\n#define SE_QUOT KC_NUHS // '\n#define SE_LABK KC_NUBS // <\n#define SE_Z    KC_Z    // Z\n#define SE_X    KC_X    // X\n#define SE_C    KC_C    // C\n#define SE_V    KC_V    // V\n#define SE_B    KC_B    // B\n#define SE_N    KC_N    // N\n#define SE_M    KC_M    // M\n#define SE_COMM KC_COMM // ,\n#define SE_DOT  KC_DOT  // .\n#define SE_MINS KC_SLSH // -\n#define SE_DEG  S(SE_SECT) // °\n#define SE_EXLM S(SE_1)    // !\n#define SE_DQUO S(SE_2)    // \"\n#define SE_HASH S(SE_3)    // #\n#define SE_EURO S(SE_4)    // €\n#define SE_PERC S(SE_5)    // %\n#define SE_AMPR S(SE_6)    // &\n#define SE_SLSH S(SE_7)    // /\n#define SE_LPRN S(SE_8)    // (\n#define SE_RPRN S(SE_9)    // )\n#define SE_EQL  S(SE_0)    // =\n#define SE_QUES S(SE_PLUS) // ?\n#define SE_GRV  S(SE_ACUT) // `\n#define SE_CIRC S(SE_DIAE) // ^ (dead)\n#define SE_ASTR S(SE_QUOT) // *\n#define SE_RABK S(SE_LABK) // >\n#define SE_SCLN S(SE_COMM) // ;\n#define SE_COLN S(SE_DOT)  // :\n#define SE_UNDS S(SE_MINS) // _\n#define SE_PILC A(SE_SECT) // ¶\n#define SE_COPY A(SE_1)    // ©\n#define SE_TM   A(SE_2)    // ™\n#define SE_PND  A(SE_3)    // £\n#define SE_DLR  A(SE_4)    // $\n#define SE_INFN A(SE_5)    // ∞\n#define SE_PIPE A(SE_7)    // |\n#define SE_LBRC A(SE_8)    // [\n#define SE_RBRC A(SE_9)    // ]\n#define SE_AEQL A(SE_0)    // ≈\n#define SE_PLMN A(SE_PLUS) // ±\n#define SE_BULT A(SE_Q)    // •\n#define SE_OMEG A(SE_W)    // Ω\n#define SE_EACU A(SE_E)    // É\n#define SE_REGD A(SE_R)    // ®\n#define SE_DAGG A(SE_T)    // †\n#define SE_MICR A(SE_Y)    // µ\n#define SE_UDIA A(SE_U)    // Ü\n#define SE_DLSI A(SE_I)    // ı\n#define SE_OE   A(SE_O)    // Œ\n#define SE_PI   A(SE_P)    // π\n#define SE_DOTA A(SE_ARNG) // ˙\n#define SE_TILD A(SE_DIAE) // ~ (dead)\n#define SE_APPL A(SE_A)    //  (Apple logo)\n#define SE_SS   A(SE_S)    // ß\n#define SE_PDIF A(SE_D)    // ∂\n#define SE_FHK  A(SE_F)    // ƒ\n#define SE_CEDL A(SE_G)    // ¸\n#define SE_OGON A(SE_H)    // ˛\n#define SE_SQRT A(SE_J)    // √\n#define SE_FORD A(SE_K)    // ª\n#define SE_FI   A(SE_L)    // ﬁ\n#define SE_OSTR A(SE_ODIA) // Ø\n#define SE_AE   A(SE_ADIA) // Æ\n#define SE_AT   A(SE_QUOT) // @\n#define SE_LTEQ A(SE_LABK) // ≤\n#define SE_DIV  A(SE_Z)    // ÷\n#define SE_CCED A(SE_C)    // Ç\n#define SE_LSAQ A(SE_V)    // ‹\n#define SE_RSAQ A(SE_B)    // ›\n#define SE_LSQU A(SE_N)    // ‘\n#define SE_RSQU A(SE_M)    // ’\n#define SE_SLQU A(SE_COMM) // ‚\n#define SE_ELLP A(SE_DOT)  // …\n#define SE_NDSH A(SE_MINS) // –\n#define SE_IEXL S(A(SE_1))    // ¡\n#define SE_YEN  S(A(SE_3))    // ¥\n#define SE_CENT S(A(SE_4))    // ¢\n#define SE_PERM S(A(SE_5))    // ‰\n#define SE_BSLS S(A(SE_7))    // (backslash)\n#define SE_LCBR S(A(SE_8))    // {\n#define SE_RCBR S(A(SE_9))    // }\n#define SE_NEQL S(A(SE_0))    // ≠\n#define SE_IQUE S(A(SE_PLUS)) // ¿\n#define SE_DACU S(A(SE_W))    // ˝\n#define SE_DDAG S(A(SE_T))    // ‡\n#define SE_STIL S(A(SE_Y))    // ˜\n#define SE_DCIR S(A(SE_I))    // ˆ\n#define SE_NARP S(A(SE_P))    // ∏\n#define SE_RNGA S(A(SE_ARNG)) // ˚\n#define SE_LOZN S(A(SE_A))    // ◊\n#define SE_NARS S(A(SE_S))    // ∑\n#define SE_INCR S(A(SE_D))    // ∆\n#define SE_INTG S(A(SE_F))    // ∫\n#define SE_MACR S(A(SE_G))    // ¯\n#define SE_BREV S(A(SE_H))    // ˘\n#define SE_NOT  S(A(SE_J))    // ¬\n#define SE_MORD S(A(SE_K))    // º\n#define SE_FL   S(A(SE_L))    // ﬂ\n#define SE_GTEQ S(A(SE_LABK)) // ≥\n#define SE_FRSL S(A(SE_Z))    // ⁄\n#define SE_CARN S(A(SE_X))    // ˇ\n#define SE_LDAQ S(A(SE_V))    // «\n#define SE_RDAQ S(A(SE_B))    // »\n#define SE_LDQU S(A(SE_N))    // “\n#define SE_RDQU S(A(SE_M))    // ”\n#define SE_DLQU S(A(SE_COMM)) // „\n#define SE_MDDT S(A(SE_DOT))  // ·\n#define SE_MDSH S(A(SE_MINS)) // —\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_swedish_pro_mac_ansi.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SWEDISH_PRO_MAC_ANSI_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SWEDISH_PRO_MAC_ANSI_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SWEDISH_PRO_MAC_ANSI_KEYCODES_VERSION_MAJOR 0\n#define QMK_SWEDISH_PRO_MAC_ANSI_KEYCODES_VERSION_MINOR 0\n#define QMK_SWEDISH_PRO_MAC_ANSI_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define SE_LABK KC_GRV  // <\n#define SE_1    KC_1    // 1\n#define SE_2    KC_2    // 2\n#define SE_3    KC_3    // 3\n#define SE_4    KC_4    // 4\n#define SE_5    KC_5    // 5\n#define SE_6    KC_6    // 6\n#define SE_7    KC_7    // 7\n#define SE_8    KC_8    // 8\n#define SE_9    KC_9    // 9\n#define SE_0    KC_0    // 0\n#define SE_PLUS KC_MINS // +\n#define SE_ACUT KC_EQL  // ´ (dead)\n#define SE_Q    KC_Q    // Q\n#define SE_W    KC_W    // W\n#define SE_E    KC_E    // E\n#define SE_R    KC_R    // R\n#define SE_T    KC_T    // T\n#define SE_Y    KC_Y    // Y\n#define SE_U    KC_U    // U\n#define SE_I    KC_I    // I\n#define SE_O    KC_O    // O\n#define SE_P    KC_P    // P\n#define SE_ARNG KC_LBRC // Å\n#define SE_DIAE KC_RBRC // ¨ (dead)\n#define SE_QUOT KC_NUHS // '\n#define SE_A    KC_A    // A\n#define SE_S    KC_S    // S\n#define SE_D    KC_D    // D\n#define SE_F    KC_F    // F\n#define SE_G    KC_G    // G\n#define SE_H    KC_H    // H\n#define SE_J    KC_J    // J\n#define SE_K    KC_K    // K\n#define SE_L    KC_L    // L\n#define SE_ODIA KC_SCLN // Ö\n#define SE_ADIA KC_QUOT // Ä\n#define SE_Z    KC_Z    // Z\n#define SE_X    KC_X    // X\n#define SE_C    KC_C    // C\n#define SE_V    KC_V    // V\n#define SE_B    KC_B    // B\n#define SE_N    KC_N    // N\n#define SE_M    KC_M    // M\n#define SE_COMM KC_COMM // ,\n#define SE_DOT  KC_DOT  // .\n#define SE_MINS KC_SLSH // -\n#define SE_RABK S(SE_LABK) // >\n#define SE_EXLM S(SE_1)    // !\n#define SE_DQUO S(SE_2)    // \"\n#define SE_HASH S(SE_3)    // #\n#define SE_EURO S(SE_4)    // €\n#define SE_PERC S(SE_5)    // %\n#define SE_AMPR S(SE_6)    // &\n#define SE_SLSH S(SE_7)    // /\n#define SE_LPRN S(SE_8)    // (\n#define SE_RPRN S(SE_9)    // )\n#define SE_EQL  S(SE_0)    // =\n#define SE_QUES S(SE_PLUS) // ?\n#define SE_GRV  S(SE_ACUT) // `\n#define SE_CIRC S(SE_DIAE) // ^ (dead)\n#define SE_ASTR S(SE_QUOT) // *\n#define SE_SCLN S(SE_COMM) // ;\n#define SE_COLN S(SE_DOT)  // :\n#define SE_UNDS S(SE_MINS) // _\n#define SE_LTEQ A(SE_LABK) // ≤\n#define SE_COPY A(SE_1)    // ©\n#define SE_AT   A(SE_2)    // @\n#define SE_PND  A(SE_3)    // £\n#define SE_DLR  A(SE_4)    // $\n#define SE_INFN A(SE_5)    // ∞\n#define SE_SECT A(SE_6)    // §\n#define SE_PIPE A(SE_7)    // |\n#define SE_LBRC A(SE_8)    // [\n#define SE_RBRC A(SE_9)    // ]\n#define SE_AEQL A(SE_0)    // ≈\n#define SE_PLMN A(SE_PLUS) // ±\n#define SE_BULT A(SE_Q)    // •\n#define SE_OMEG A(SE_W)    // Ω\n#define SE_EACU A(SE_E)    // É\n#define SE_REGD A(SE_R)    // ®\n#define SE_DAGG A(SE_T)    // †\n#define SE_MICR A(SE_Y)    // µ\n#define SE_UDIA A(SE_U)    // Ü\n#define SE_DLSI A(SE_I)    // ı\n#define SE_OE   A(SE_O)    // Œ\n#define SE_PI   A(SE_P)    // π\n#define SE_DOTA A(SE_ARNG) // ˙\n#define SE_TILD A(SE_DIAE) // ~ (dead)\n#define SE_TM   A(SE_QUOT) // ™\n#define SE_APPL A(SE_A)    //  (Apple logo)\n#define SE_SS   A(SE_S)    // ß\n#define SE_PDIF A(SE_D)    // ∂\n#define SE_FHK  A(SE_F)    // ƒ\n#define SE_CEDL A(SE_G)    // ¸\n#define SE_OGON A(SE_H)    // ˛\n#define SE_SQRT A(SE_J)    // √\n#define SE_FORD A(SE_K)    // ª\n#define SE_FI   A(SE_L)    // ﬁ\n#define SE_OSTR A(SE_ODIA) // Ø\n#define SE_AE   A(SE_ADIA) // Æ\n#define SE_DIV  A(SE_Z)    // ÷\n#define SE_CCED A(SE_C)    // Ç\n#define SE_LSAQ A(SE_V)    // ‹\n#define SE_RSAQ A(SE_B)    // ›\n#define SE_LSQU A(SE_N)    // ‘\n#define SE_RSQU A(SE_M)    // ’\n#define SE_SLQU A(SE_COMM) // ‚\n#define SE_ELLP A(SE_DOT)  // …\n#define SE_NDSH A(SE_MINS) // –\n#define SE_GTEQ S(A(SE_LABK)) // ≥\n#define SE_IEXL S(A(SE_1))    // ¡\n#define SE_YEN  S(A(SE_3))    // ¥\n#define SE_CENT S(A(SE_4))    // ¢\n#define SE_PERM S(A(SE_5))    // ‰\n#define SE_PILC S(A(SE_6))    // ¶\n#define SE_BSLS S(A(SE_7))    // (backslash)\n#define SE_LCBR S(A(SE_8))    // {\n#define SE_RCBR S(A(SE_9))    // }\n#define SE_NEQL S(A(SE_0))    // ≠\n#define SE_IQUE S(A(SE_PLUS)) // ¿\n#define SE_DEG  S(A(SE_Q))    // °\n#define SE_DACU S(A(SE_W))    // ˝\n#define SE_DDAG S(A(SE_T))    // ‡\n#define SE_STIL S(A(SE_Y))    // ˜\n#define SE_DCIR S(A(SE_I))    // ˆ\n#define SE_NARP S(A(SE_P))    // ∏\n#define SE_RNGA S(A(SE_ARNG)) // ˚\n#define SE_LOZN S(A(SE_A))    // ◊\n#define SE_NARS S(A(SE_S))    // ∑\n#define SE_INCR S(A(SE_D))    // ∆\n#define SE_INTG S(A(SE_F))    // ∫\n#define SE_MACR S(A(SE_G))    // ¯\n#define SE_BREV S(A(SE_H))    // ˘\n#define SE_NOT  S(A(SE_J))    // ¬\n#define SE_MORD S(A(SE_K))    // º\n#define SE_FL   S(A(SE_L))    // ﬂ\n#define SE_FRSL S(A(SE_Z))    // ⁄\n#define SE_CARN S(A(SE_X))    // ˇ\n#define SE_LDAQ S(A(SE_V))    // «\n#define SE_RDAQ S(A(SE_B))    // »\n#define SE_LDQU S(A(SE_N))    // “\n#define SE_RDQU S(A(SE_M))    // ”\n#define SE_DLQU S(A(SE_COMM)) // „\n#define SE_MDDT S(A(SE_DOT))  // ·\n#define SE_MDSH S(A(SE_MINS)) // —\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_swedish_pro_mac_iso.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SWEDISH_PRO_MAC_ISO_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SWEDISH_PRO_MAC_ISO_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SWEDISH_PRO_MAC_ISO_KEYCODES_VERSION_MAJOR 0\n#define QMK_SWEDISH_PRO_MAC_ISO_KEYCODES_VERSION_MINOR 0\n#define QMK_SWEDISH_PRO_MAC_ISO_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define SE_SECT KC_GRV  // §\n#define SE_1    KC_1    // 1\n#define SE_2    KC_2    // 2\n#define SE_3    KC_3    // 3\n#define SE_4    KC_4    // 4\n#define SE_5    KC_5    // 5\n#define SE_6    KC_6    // 6\n#define SE_7    KC_7    // 7\n#define SE_8    KC_8    // 8\n#define SE_9    KC_9    // 9\n#define SE_0    KC_0    // 0\n#define SE_PLUS KC_MINS // +\n#define SE_ACUT KC_EQL  // ´ (dead)\n#define SE_Q    KC_Q    // Q\n#define SE_W    KC_W    // W\n#define SE_E    KC_E    // E\n#define SE_R    KC_R    // R\n#define SE_T    KC_T    // T\n#define SE_Y    KC_Y    // Y\n#define SE_U    KC_U    // U\n#define SE_I    KC_I    // I\n#define SE_O    KC_O    // O\n#define SE_P    KC_P    // P\n#define SE_ARNG KC_LBRC // Å\n#define SE_DIAE KC_RBRC // ¨ (dead)\n#define SE_A    KC_A    // A\n#define SE_S    KC_S    // S\n#define SE_D    KC_D    // D\n#define SE_F    KC_F    // F\n#define SE_G    KC_G    // G\n#define SE_H    KC_H    // H\n#define SE_J    KC_J    // J\n#define SE_K    KC_K    // K\n#define SE_L    KC_L    // L\n#define SE_ODIA KC_SCLN // Ö\n#define SE_ADIA KC_QUOT // Ä\n#define SE_QUOT KC_NUHS // '\n#define SE_LABK KC_NUBS // <\n#define SE_Z    KC_Z    // Z\n#define SE_X    KC_X    // X\n#define SE_C    KC_C    // C\n#define SE_V    KC_V    // V\n#define SE_B    KC_B    // B\n#define SE_N    KC_N    // N\n#define SE_M    KC_M    // M\n#define SE_COMM KC_COMM // ,\n#define SE_DOT  KC_DOT  // .\n#define SE_MINS KC_SLSH // -\n#define SE_DEG  S(SE_SECT) // °\n#define SE_EXLM S(SE_1)    // !\n#define SE_DQUO S(SE_2)    // \"\n#define SE_HASH S(SE_3)    // #\n#define SE_EURO S(SE_4)    // €\n#define SE_PERC S(SE_5)    // %\n#define SE_AMPR S(SE_6)    // &\n#define SE_SLSH S(SE_7)    // /\n#define SE_LPRN S(SE_8)    // (\n#define SE_RPRN S(SE_9)    // )\n#define SE_EQL  S(SE_0)    // =\n#define SE_QUES S(SE_PLUS) // ?\n#define SE_GRV  S(SE_ACUT) // `\n#define SE_CIRC S(SE_DIAE) // ^ (dead)\n#define SE_ASTR S(SE_QUOT) // *\n#define SE_RABK S(SE_LABK) // >\n#define SE_SCLN S(SE_COMM) // ;\n#define SE_COLN S(SE_DOT)  // :\n#define SE_UNDS S(SE_MINS) // _\n#define SE_PILC A(SE_SECT) // ¶\n#define SE_COPY A(SE_1)    // ©\n#define SE_AT   A(SE_2)    // @\n#define SE_PND  A(SE_3)    // £\n#define SE_DLR  A(SE_4)    // $\n#define SE_INFN A(SE_5)    // ∞\n#define SE_PIPE A(SE_7)    // |\n#define SE_LBRC A(SE_8)    // [\n#define SE_RBRC A(SE_9)    // ]\n#define SE_AEQL A(SE_0)    // ≈\n#define SE_PLMN A(SE_PLUS) // ±\n#define SE_BULT A(SE_Q)    // •\n#define SE_OMEG A(SE_W)    // Ω\n#define SE_EACU A(SE_E)    // É\n#define SE_REGD A(SE_R)    // ®\n#define SE_DAGG A(SE_T)    // †\n#define SE_MICR A(SE_Y)    // µ\n#define SE_UDIA A(SE_U)    // Ü\n#define SE_DLSI A(SE_I)    // ı\n#define SE_OE   A(SE_O)    // Œ\n#define SE_PI   A(SE_P)    // π\n#define SE_DOTA A(SE_ARNG) // ˙\n#define SE_TILD A(SE_DIAE) // ~ (dead)\n#define SE_APPL A(SE_A)    //  (Apple logo)\n#define SE_SS   A(SE_S)    // ß\n#define SE_PDIF A(SE_D)    // ∂\n#define SE_FHK  A(SE_F)    // ƒ\n#define SE_CEDL A(SE_G)    // ¸\n#define SE_OGON A(SE_H)    // ˛\n#define SE_SQRT A(SE_J)    // √\n#define SE_FORD A(SE_K)    // ª\n#define SE_FI   A(SE_L)    // ﬁ\n#define SE_OSTR A(SE_ODIA) // Ø\n#define SE_AE   A(SE_ADIA) // Æ\n#define SE_TM   A(SE_QUOT) // ™\n#define SE_LTEQ A(SE_LABK) // ≤\n#define SE_DIV  A(SE_Z)    // ÷\n#define SE_CCED A(SE_C)    // Ç\n#define SE_LSAQ A(SE_V)    // ‹\n#define SE_RSAQ A(SE_B)    // ›\n#define SE_LSQU A(SE_N)    // ‘\n#define SE_RSQU A(SE_M)    // ’\n#define SE_SLQU A(SE_COMM) // ‚\n#define SE_ELLP A(SE_DOT)  // …\n#define SE_NDSH A(SE_MINS) // –\n#define SE_IEXL S(A(SE_1))    // ¡\n#define SE_YEN  S(A(SE_3))    // ¥\n#define SE_CENT S(A(SE_4))    // ¢\n#define SE_PERM S(A(SE_5))    // ‰\n#define SE_BSLS S(A(SE_7))    // (backslash)\n#define SE_LCBR S(A(SE_8))    // {\n#define SE_RCBR S(A(SE_9))    // }\n#define SE_NEQL S(A(SE_0))    // ≠\n#define SE_IQUE S(A(SE_PLUS)) // ¿\n#define SE_DACU S(A(SE_W))    // ˝\n#define SE_DDAG S(A(SE_T))    // ‡\n#define SE_STIL S(A(SE_Y))    // ˜\n#define SE_DCIR S(A(SE_I))    // ˆ\n#define SE_NARP S(A(SE_P))    // ∏\n#define SE_RNGA S(A(SE_ARNG)) // ˚\n#define SE_LOZN S(A(SE_A))    // ◊\n#define SE_NARS S(A(SE_S))    // ∑\n#define SE_INCR S(A(SE_D))    // ∆\n#define SE_INTG S(A(SE_F))    // ∫\n#define SE_MACR S(A(SE_G))    // ¯\n#define SE_BREV S(A(SE_H))    // ˘\n#define SE_NOT  S(A(SE_J))    // ¬\n#define SE_MORD S(A(SE_K))    // º\n#define SE_FL   S(A(SE_L))    // ﬂ\n#define SE_GTEQ S(A(SE_LABK)) // ≥\n#define SE_FRSL S(A(SE_Z))    // ⁄\n#define SE_CARN S(A(SE_X))    // ˇ\n#define SE_LDAQ S(A(SE_V))    // «\n#define SE_RDAQ S(A(SE_B))    // »\n#define SE_LDQU S(A(SE_N))    // “\n#define SE_RDQU S(A(SE_M))    // ”\n#define SE_DLQU S(A(SE_COMM)) // „\n#define SE_MDDT S(A(SE_DOT))  // ·\n#define SE_MDSH S(A(SE_MINS)) // —\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_swiss_de.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SWISS_DE_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SWISS_DE_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SWISS_DE_KEYCODES_VERSION_MAJOR 0\n#define QMK_SWISS_DE_KEYCODES_VERSION_MINOR 0\n#define QMK_SWISS_DE_KEYCODES_VERSION_PATCH 1\n\n#undef CH_H\n\n// Aliases\n#define CH_SECT KC_GRV  // §\n#define CH_1    KC_1    // 1\n#define CH_2    KC_2    // 2\n#define CH_3    KC_3    // 3\n#define CH_4    KC_4    // 4\n#define CH_5    KC_5    // 5\n#define CH_6    KC_6    // 6\n#define CH_7    KC_7    // 7\n#define CH_8    KC_8    // 8\n#define CH_9    KC_9    // 9\n#define CH_0    KC_0    // 0\n#define CH_QUOT KC_MINS // '\n#define CH_CIRC KC_EQL  // ^ (dead)\n#define CH_Q    KC_Q    // Q\n#define CH_W    KC_W    // W\n#define CH_E    KC_E    // E\n#define CH_R    KC_R    // R\n#define CH_T    KC_T    // T\n#define CH_Z    KC_Y    // Z\n#define CH_U    KC_U    // U\n#define CH_I    KC_I    // I\n#define CH_O    KC_O    // O\n#define CH_P    KC_P    // P\n#define CH_UDIA KC_LBRC // ü\n#define CH_DIAE KC_RBRC // ¨ (dead)\n#define CH_A    KC_A    // A\n#define CH_S    KC_S    // S\n#define CH_D    KC_D    // D\n#define CH_F    KC_F    // F\n#define CH_G    KC_G    // G\n#define CH_H    KC_H    // H\n#define CH_J    KC_J    // J\n#define CH_K    KC_K    // K\n#define CH_L    KC_L    // L\n#define CH_ODIA KC_SCLN // ö\n#define CH_ADIA KC_QUOT // ä\n#define CH_DLR  KC_NUHS // $\n#define CH_LABK KC_NUBS // <\n#define CH_Y    KC_Z    // Y\n#define CH_X    KC_X    // X\n#define CH_C    KC_C    // C\n#define CH_V    KC_V    // V\n#define CH_B    KC_B    // B\n#define CH_N    KC_N    // N\n#define CH_M    KC_M    // M\n#define CH_COMM KC_COMM // ,\n#define CH_DOT  KC_DOT  // .\n#define CH_MINS KC_SLSH // -\n#define CH_DEG  S(CH_SECT) // °\n#define CH_PLUS S(CH_1)    // +\n#define CH_DQUO S(CH_2)    // \"\n#define CH_ASTR S(CH_3)    // *\n#define CH_CCED S(CH_4)    // ç\n#define CH_PERC S(CH_5)    // %\n#define CH_AMPR S(CH_6)    // &\n#define CH_SLSH S(CH_7)    // /\n#define CH_LPRN S(CH_8)    // (\n#define CH_RPRN S(CH_9)    // )\n#define CH_EQL  S(CH_0)    // =\n#define CH_QUES S(CH_QUOT) // ?\n#define CH_GRV  S(CH_CIRC) // ` (dead)\n#define CH_EGRV S(CH_UDIA) // è\n#define CH_EXLM S(CH_DIAE) // !\n#define CH_EACU S(CH_ODIA) // é\n#define CH_AGRV S(CH_ADIA) // à\n#define CH_PND  S(CH_DLR)  // £\n#define CH_RABK S(CH_LABK) // >\n#define CH_SCLN S(CH_COMM) // ;\n#define CH_COLN S(CH_DOT)  // :\n#define CH_UNDS S(CH_MINS) // _\n#define CH_BRKP ALGR(CH_1)    // ¦\n#define CH_AT   ALGR(CH_2)    // @\n#define CH_HASH ALGR(CH_3)    // #\n#define CH_NOT  ALGR(CH_6)    // ¬\n#define CH_PIPE ALGR(CH_7)    // |\n#define CH_CENT ALGR(CH_8)    // ¢\n#define CH_ACUT ALGR(CH_QUOT) // ´ (dead)\n#define CH_TILD ALGR(CH_CIRC) // ~ (dead)\n#define CH_EURO ALGR(CH_E)    // €\n#define CH_LBRC ALGR(CH_UDIA) // [\n#define CH_RBRC ALGR(CH_DIAE) // ]\n#define CH_LCBR ALGR(CH_ADIA) // {\n#define CH_RCBR ALGR(CH_DLR)  // }\n#define CH_BSLS ALGR(CH_LABK) // (backslash)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_swiss_fr.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_SWISS_FR_KEYCODES_VERSION \"0.0.1\"\n#define QMK_SWISS_FR_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_SWISS_FR_KEYCODES_VERSION_MAJOR 0\n#define QMK_SWISS_FR_KEYCODES_VERSION_MINOR 0\n#define QMK_SWISS_FR_KEYCODES_VERSION_PATCH 1\n\n#undef CH_H\n\n// Aliases\n#define CH_SECT KC_GRV  // §\n#define CH_1    KC_1    // 1\n#define CH_2    KC_2    // 2\n#define CH_3    KC_3    // 3\n#define CH_4    KC_4    // 4\n#define CH_5    KC_5    // 5\n#define CH_6    KC_6    // 6\n#define CH_7    KC_7    // 7\n#define CH_8    KC_8    // 8\n#define CH_9    KC_9    // 9\n#define CH_0    KC_0    // 0\n#define CH_QUOT KC_MINS // '\n#define CH_CIRC KC_EQL  // ^ (dead)\n#define CH_Q    KC_Q    // Q\n#define CH_W    KC_W    // W\n#define CH_E    KC_E    // E\n#define CH_R    KC_R    // R\n#define CH_T    KC_T    // T\n#define CH_Z    KC_Y    // Z\n#define CH_U    KC_U    // U\n#define CH_I    KC_I    // I\n#define CH_O    KC_O    // O\n#define CH_P    KC_P    // P\n#define CH_EGRV KC_LBRC // è\n#define CH_DIAE KC_RBRC // ¨ (dead)\n#define CH_A    KC_A    // A\n#define CH_S    KC_S    // S\n#define CH_D    KC_D    // D\n#define CH_F    KC_F    // F\n#define CH_G    KC_G    // G\n#define CH_H    KC_H    // H\n#define CH_J    KC_J    // J\n#define CH_K    KC_K    // K\n#define CH_L    KC_L    // L\n#define CH_EACU KC_SCLN // é\n#define CH_AGRV KC_QUOT // à\n#define CH_DLR  KC_NUHS // $\n#define CH_LABK KC_NUBS // <\n#define CH_Y    KC_Z    // Y\n#define CH_X    KC_X    // X\n#define CH_C    KC_C    // C\n#define CH_V    KC_V    // V\n#define CH_B    KC_B    // B\n#define CH_N    KC_N    // N\n#define CH_M    KC_M    // M\n#define CH_COMM KC_COMM // ,\n#define CH_DOT  KC_DOT  // .\n#define CH_MINS KC_SLSH // -\n#define CH_DEG  S(CH_SECT) // °\n#define CH_PLUS S(CH_1)    // +\n#define CH_DQUO S(CH_2)    // \"\n#define CH_ASTR S(CH_3)    // *\n#define CH_CCED S(CH_4)    // ç\n#define CH_PERC S(CH_5)    // %\n#define CH_AMPR S(CH_6)    // &\n#define CH_SLSH S(CH_7)    // /\n#define CH_LPRN S(CH_8)    // (\n#define CH_RPRN S(CH_9)    // )\n#define CH_EQL  S(CH_0)    // =\n#define CH_QUES S(CH_QUOT) // ?\n#define CH_GRV  S(CH_CIRC) // ` (dead)\n#define CH_UDIA S(CH_EGRV) // ü\n#define CH_EXLM S(CH_DIAE) // !\n#define CH_ODIA S(CH_EACU) // ö\n#define CH_ADIA S(CH_AGRV) // ä\n#define CH_PND  S(CH_DLR)  // £\n#define CH_RABK S(CH_LABK) // >\n#define CH_SCLN S(CH_COMM) // ;\n#define CH_COLN S(CH_DOT)  // :\n#define CH_UNDS S(CH_MINS) // _\n#define CH_BRKP ALGR(CH_1)    // ¦\n#define CH_AT   ALGR(CH_2)    // @\n#define CH_HASH ALGR(CH_3)    // #\n#define CH_NOT  ALGR(CH_6)    // ¬\n#define CH_PIPE ALGR(CH_7)    // |\n#define CH_CENT ALGR(CH_8)    // ¢\n#define CH_ACUT ALGR(CH_QUOT) // ´ (dead)\n#define CH_TILD ALGR(CH_CIRC) // ~ (dead)\n#define CH_EURO ALGR(CH_E)    // €\n#define CH_LBRC ALGR(CH_EGRV) // [\n#define CH_RBRC ALGR(CH_DIAE) // ]\n#define CH_LCBR ALGR(CH_AGRV) // {\n#define CH_RCBR ALGR(CH_DLR)  // }\n#define CH_BSLS ALGR(CH_LABK) // (backslash)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_turkish_f.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_TURKISH_F_KEYCODES_VERSION \"0.0.1\"\n#define QMK_TURKISH_F_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_TURKISH_F_KEYCODES_VERSION_MAJOR 0\n#define QMK_TURKISH_F_KEYCODES_VERSION_MINOR 0\n#define QMK_TURKISH_F_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define TR_PLUS KC_GRV  // +\n#define TR_1    KC_1    // 1\n#define TR_2    KC_2    // 2\n#define TR_3    KC_3    // 3\n#define TR_4    KC_4    // 4\n#define TR_5    KC_5    // 5\n#define TR_6    KC_6    // 6\n#define TR_7    KC_7    // 7\n#define TR_8    KC_8    // 8\n#define TR_9    KC_9    // 9\n#define TR_0    KC_0    // 0\n#define TR_SLSH KC_MINS // /\n#define TR_MINS KC_EQL  // -\n#define TR_F    KC_Q    // F\n#define TR_G    KC_W    // G\n#define TR_GBRV KC_E    // Ğ\n#define TR_I    KC_R    // I\n#define TR_O    KC_T    // O\n#define TR_D    KC_Y    // D\n#define TR_R    KC_U    // R\n#define TR_N    KC_I    // N\n#define TR_H    KC_O    // H\n#define TR_P    KC_P    // P\n#define TR_Q    KC_LBRC // Q\n#define TR_W    KC_RBRC // W\n#define TR_U    KC_A    // U\n#define TR_IDOT KC_S    // İ\n#define TR_E    KC_D    // E\n#define TR_A    KC_F    // A\n#define TR_UDIA KC_G    // Ü\n#define TR_T    KC_H    // T\n#define TR_K    KC_J    // K\n#define TR_M    KC_K    // M\n#define TR_L    KC_L    // L\n#define TR_Y    KC_SCLN // Y\n#define TR_SCED KC_QUOT // Ş\n#define TR_X    KC_NUHS // X\n#define TR_LABK KC_NUBS // <\n#define TR_J    KC_Z    // J\n#define TR_ODIA KC_X    // Ö\n#define TR_V    KC_C    // V\n#define TR_C    KC_V    // C\n#define TR_CCED KC_B    // Ç\n#define TR_Z    KC_N    // Z\n#define TR_S    KC_M    // S\n#define TR_B    KC_COMM // B\n#define TR_DOT  KC_DOT  // .\n#define TR_COMM KC_SLSH // ,\n#define TR_ASTR S(TR_PLUS) // *\n#define TR_EXLM S(TR_1)    // !\n#define TR_DQUO S(TR_2)    // \"\n#define TR_CIRC S(TR_3)    // ^ (dead)\n#define TR_DLR  S(TR_4)    // $\n#define TR_PERC S(TR_5)    // %\n#define TR_AMPR S(TR_6)    // &\n#define TR_QUOT S(TR_7)    // '\n#define TR_LPRN S(TR_8)    // (\n#define TR_RPRN S(TR_9)    // )\n#define TR_EQL  S(TR_0)    // =\n#define TR_QUES S(TR_SLSH) // ?\n#define TR_UNDS S(TR_MINS) // _\n#define TR_RABK S(TR_LABK) // >\n#define TR_COLN S(TR_DOT)  // :\n#define TR_SCLN S(TR_COMM) // ;\n#define TR_NOT  ALGR(TR_PLUS) // ¬\n#define TR_SUP1 ALGR(TR_1)    // ¹\n#define TR_SUP2 ALGR(TR_2)    // ²\n#define TR_HASH ALGR(TR_3)    // #\n#define TR_QRTR ALGR(TR_4)    // ¼\n#define TR_HALF ALGR(TR_5)    // ½\n#define TR_TQTR ALGR(TR_6)    // ¾\n#define TR_LCBR ALGR(TR_7)    // {\n#define TR_LBRC ALGR(TR_8)    // [\n#define TR_RBRC ALGR(TR_9)    // ]\n#define TR_RCBR ALGR(TR_0)    // }\n#define TR_BSLS ALGR(TR_SLSH) // (backslash)\n#define TR_PIPE ALGR(TR_MINS) // |\n#define TR_AT   ALGR(TR_F)    // @\n#define TR_PILC ALGR(TR_I)    // ¶\n#define TR_YEN  ALGR(TR_D)    // ¥\n#define TR_OSTR ALGR(TR_H)    // Ø\n#define TR_PND  ALGR(TR_P)    // £\n#define TR_DIAE ALGR(TR_Q)    // ¨ (dead)\n#define TR_TILD ALGR(TR_W)    // ~ (dead)\n#define TR_AE   ALGR(TR_U)    // Æ\n#define TR_SS   ALGR(TR_IDOT) // ß\n#define TR_EURO ALGR(TR_E)    // €\n#define TR_LIRA ALGR(TR_T)    // ₺\n#define TR_ACUT ALGR(TR_Y)    // ´ (dead)\n#define TR_GRV  ALGR(TR_X)    // ` (dead)\n#define TR_LDAQ ALGR(TR_J)    // «\n#define TR_RDAQ ALGR(TR_ODIA) // »\n#define TR_CENT ALGR(TR_V)    // ¢\n#define TR_MICR ALGR(TR_S)    // µ\n#define TR_MUL  ALGR(TR_B)    // ×\n#define TR_DIV  ALGR(TR_DOT)  // ÷\n#define TR_SHYP ALGR(TR_COMM) // ­ (soft hyphen)\n#define TR_SUP3 S(ALGR(TR_3))    // ³\n#define TR_CURR S(ALGR(TR_4))    // ¤\n#define TR_IQUE S(ALGR(TR_SLSH)) // ¿\n#define TR_REGD S(ALGR(TR_I))    // ®\n#define TR_SECT S(ALGR(TR_IDOT)) // §\n#define TR_FORD S(ALGR(TR_A))    // ª\n#define TR_BRKP S(ALGR(TR_LABK)) // ¦\n#define TR_COPY S(ALGR(TR_V))    // ©\n#define TR_MORD S(ALGR(TR_S))    // º\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_turkish_q.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_TURKISH_Q_KEYCODES_VERSION \"0.0.1\"\n#define QMK_TURKISH_Q_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_TURKISH_Q_KEYCODES_VERSION_MAJOR 0\n#define QMK_TURKISH_Q_KEYCODES_VERSION_MINOR 0\n#define QMK_TURKISH_Q_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define TR_DQUO KC_GRV  // \"\n#define TR_1    KC_1    // 1\n#define TR_2    KC_2    // 2\n#define TR_3    KC_3    // 3\n#define TR_4    KC_4    // 4\n#define TR_5    KC_5    // 5\n#define TR_6    KC_6    // 6\n#define TR_7    KC_7    // 7\n#define TR_8    KC_8    // 8\n#define TR_9    KC_9    // 9\n#define TR_0    KC_0    // 0\n#define TR_ASTR KC_MINS // *\n#define TR_MINS KC_EQL  // -\n#define TR_Q    KC_Q    // Q\n#define TR_W    KC_W    // W\n#define TR_E    KC_E    // E\n#define TR_R    KC_R    // R\n#define TR_T    KC_T    // T\n#define TR_Y    KC_Y    // Y\n#define TR_U    KC_U    // U\n#define TR_I    KC_I    // I\n#define TR_O    KC_O    // O\n#define TR_P    KC_P    // P\n#define TR_GBRV KC_LBRC // Ğ\n#define TR_UDIA KC_RBRC // Ü\n#define TR_A    KC_A    // A\n#define TR_S    KC_S    // S\n#define TR_D    KC_D    // D\n#define TR_F    KC_F    // F\n#define TR_G    KC_G    // G\n#define TR_H    KC_H    // H\n#define TR_J    KC_J    // J\n#define TR_K    KC_K    // K\n#define TR_L    KC_L    // L\n#define TR_SCED KC_SCLN // Ş\n#define TR_IDOT KC_QUOT // İ\n#define TR_COMM KC_NUHS // ,\n#define TR_LABK KC_NUBS // <\n#define TR_Z    KC_Z    // Z\n#define TR_X    KC_X    // X\n#define TR_C    KC_C    // C\n#define TR_V    KC_V    // V\n#define TR_B    KC_B    // B\n#define TR_N    KC_N    // N\n#define TR_M    KC_M    // M\n#define TR_ODIA KC_COMM // Ö\n#define TR_CCED KC_DOT  // Ç\n#define TR_DOT  KC_SLSH // .\n#define TR_EACU S(TR_DQUO) // é\n#define TR_EXLM S(TR_1)    // !\n#define TR_QUOT S(TR_2)    // '\n#define TR_CIRC S(TR_3)    // ^ (dead)\n#define TR_PLUS S(TR_4)    // +\n#define TR_PERC S(TR_5)    // %\n#define TR_AMPR S(TR_6)    // &\n#define TR_SLSH S(TR_7)    // /\n#define TR_LPRN S(TR_8)    // (\n#define TR_RPRN S(TR_9)    // )\n#define TR_EQL  S(TR_0)    // =\n#define TR_QUES S(TR_ASTR) // ?\n#define TR_UNDS S(TR_MINS) // _\n#define TR_SCLN S(TR_COMM) // ;\n#define TR_RABK S(TR_LABK) // >\n#define TR_COLN S(TR_DOT)  // :\n#define TR_PND  ALGR(TR_2)    // £\n#define TR_HASH ALGR(TR_3)    // #\n#define TR_DLR  ALGR(TR_4)    // $\n#define TR_HALF ALGR(TR_5)    // ½\n#define TR_LCBR ALGR(TR_7)    // {\n#define TR_LBRC ALGR(TR_8)    // [\n#define TR_RBRC ALGR(TR_9)    // ]\n#define TR_RCBR ALGR(TR_0)    // }\n#define TR_BSLS ALGR(TR_ASTR) // (backslash)\n#define TR_PIPE ALGR(TR_MINS) // |\n#define TR_AT   ALGR(TR_Q)    // @\n#define TR_EURO ALGR(TR_E)    // €\n#define TR_LIRA ALGR(TR_T)    // ₺\n#define TR_DIAE ALGR(TR_GBRV) // ¨ (dead)\n#define TR_TILD ALGR(TR_UDIA) // ~ (dead)\n#define TR_AE   ALGR(TR_A)    // Æ\n#define TR_SS   ALGR(TR_S)    // ß\n#define TR_ACUT ALGR(TR_SCED) // ´ (dead)\n#define TR_GRV  ALGR(TR_COMM) // ` (dead)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_uk.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_UK_KEYCODES_VERSION \"0.0.1\"\n#define QMK_UK_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_UK_KEYCODES_VERSION_MAJOR 0\n#define QMK_UK_KEYCODES_VERSION_MINOR 0\n#define QMK_UK_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define UK_GRV  KC_GRV  // `\n#define UK_1    KC_1    // 1\n#define UK_2    KC_2    // 2\n#define UK_3    KC_3    // 3\n#define UK_4    KC_4    // 4\n#define UK_5    KC_5    // 5\n#define UK_6    KC_6    // 6\n#define UK_7    KC_7    // 7\n#define UK_8    KC_8    // 8\n#define UK_9    KC_9    // 9\n#define UK_0    KC_0    // 0\n#define UK_MINS KC_MINS // -\n#define UK_EQL  KC_EQL  // =\n#define UK_Q    KC_Q    // Q\n#define UK_W    KC_W    // W\n#define UK_E    KC_E    // E\n#define UK_R    KC_R    // R\n#define UK_T    KC_T    // T\n#define UK_Y    KC_Y    // Y\n#define UK_U    KC_U    // U\n#define UK_I    KC_I    // I\n#define UK_O    KC_O    // O\n#define UK_P    KC_P    // P\n#define UK_LBRC KC_LBRC // [\n#define UK_RBRC KC_RBRC // ]\n#define UK_A    KC_A    // A\n#define UK_S    KC_S    // S\n#define UK_D    KC_D    // D\n#define UK_F    KC_F    // F\n#define UK_G    KC_G    // G\n#define UK_H    KC_H    // H\n#define UK_J    KC_J    // J\n#define UK_K    KC_K    // K\n#define UK_L    KC_L    // L\n#define UK_SCLN KC_SCLN // ;\n#define UK_QUOT KC_QUOT // '\n#define UK_HASH KC_NUHS // #\n#define UK_BSLS KC_NUBS // (backslash)\n#define UK_Z    KC_Z    // Z\n#define UK_X    KC_X    // X\n#define UK_C    KC_C    // C\n#define UK_V    KC_V    // V\n#define UK_B    KC_B    // B\n#define UK_N    KC_N    // N\n#define UK_M    KC_M    // M\n#define UK_COMM KC_COMM // ,\n#define UK_DOT  KC_DOT  // .\n#define UK_SLSH KC_SLSH // /\n#define UK_NOT  S(UK_GRV)  // ¬\n#define UK_EXLM S(UK_1)    // !\n#define UK_DQUO S(UK_2)    // \"\n#define UK_PND  S(UK_3)    // £\n#define UK_DLR  S(UK_4)    // $\n#define UK_PERC S(UK_5)    // %\n#define UK_CIRC S(UK_6)    // ^\n#define UK_AMPR S(UK_7)    // &\n#define UK_ASTR S(UK_8)    // *\n#define UK_LPRN S(UK_9)    // (\n#define UK_RPRN S(UK_0)    // )\n#define UK_UNDS S(UK_MINS) // _\n#define UK_PLUS S(UK_EQL)  // +\n#define UK_LCBR S(UK_LBRC) // {\n#define UK_RCBR S(UK_RBRC) // }\n#define UK_COLN S(UK_SCLN) // :\n#define UK_AT   S(UK_QUOT) // @\n#define UK_TILD S(UK_HASH) // ~\n#define UK_PIPE S(UK_BSLS) // |\n#define UK_LABK S(UK_COMM) // <\n#define UK_RABK S(UK_DOT)  // >\n#define UK_QUES S(UK_SLSH) // ?\n#define UK_BRKP ALGR(UK_GRV)  // ¦\n#define UK_EURO ALGR(UK_4)    // €\n#define UK_EACU ALGR(KC_E)    // É\n#define UK_UACU ALGR(KC_U)    // Ú\n#define UK_IACU ALGR(KC_I)    // Í\n#define UK_OACU ALGR(KC_O)    // Ó\n#define UK_AACU ALGR(KC_A)    // Á\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_ukrainian.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_UKRAINIAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_UKRAINIAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_UKRAINIAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_UKRAINIAN_KEYCODES_VERSION_MINOR 0\n#define QMK_UKRAINIAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define UA_QUOT KC_GRV  // '\n#define UA_1    KC_1    // 1\n#define UA_2    KC_2    // 2\n#define UA_3    KC_3    // 3\n#define UA_4    KC_4    // 4\n#define UA_5    KC_5    // 5\n#define UA_6    KC_6    // 6\n#define UA_7    KC_7    // 7\n#define UA_8    KC_8    // 8\n#define UA_9    KC_9    // 9\n#define UA_0    KC_0    // 0\n#define UA_MINS KC_MINS // -\n#define UA_EQL  KC_EQL  // =\n#define UA_YOT  KC_Q    // Й\n#define UA_TSE  KC_W    // Ц\n#define UA_U    KC_E    // У\n#define UA_KA   KC_R    // К\n#define UA_E    KC_T    // Е\n#define UA_EN   KC_Y    // Н\n#define UA_HE   KC_U    // Г\n#define UA_SHA  KC_I    // Ш\n#define UA_SHCH KC_O    // Щ\n#define UA_ZE   KC_P    // З\n#define UA_KHA  KC_LBRC // Х\n#define UA_YI   KC_RBRC // Ї\n#define UA_BSLS KC_BSLS // (backslash)\n#define UA_EF   KC_A    // Ф\n#define UA_I    KC_S    // І\n#define UA_VE   KC_D    // В\n#define UA_A    KC_F    // А\n#define UA_PE   KC_G    // П\n#define UA_ER   KC_H    // Р\n#define UA_O    KC_J    // О\n#define UA_EL   KC_K    // Л\n#define UA_DE   KC_L    // Д\n#define UA_ZHE  KC_SCLN // Ж\n#define UA_YE   KC_QUOT // Є\n#define UA_YA   KC_Z    // Я\n#define UA_CHE  KC_X    // Ч\n#define UA_ES   KC_C    // С\n#define UA_EM   KC_V    // М\n#define UA_Y    KC_B    // И\n#define UA_TE   KC_N    // Т\n#define UA_SOFT KC_M    // Ь\n#define UA_BE   KC_COMM // Б\n#define UA_YU   KC_DOT  // Ю\n#define UA_DOT  KC_SLSH // .\n#define UA_HRYV S(UA_QUOT) // ₴\n#define UA_EXLM S(UA_1)    // !\n#define UA_DQUO S(UA_2)    // \"\n#define UA_NUM  S(UA_3)    // №\n#define UA_SCLN S(UA_4)    // ;\n#define UA_PERC S(UA_5)    // %\n#define UA_COLN S(UA_6)    // :\n#define UA_QUES S(UA_7)    // ?\n#define UA_ASTR S(UA_8)    // *\n#define UA_LPRN S(UA_9)    // (\n#define UA_RPRN S(UA_0)    // )\n#define UA_UNDS S(UA_MINS) // _\n#define UA_PLUS S(UA_EQL)  // +\n#define UA_SLSH S(UA_BSLS) // /\n#define UA_COMM S(UA_DOT)  // ,\n#define UA_GE   ALGR(UA_HE)   // ґ\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_us.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_US_KEYCODES_VERSION \"0.0.1\"\n#define QMK_US_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_US_KEYCODES_VERSION_MAJOR 0\n#define QMK_US_KEYCODES_VERSION_MINOR 0\n#define QMK_US_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define KC_TILD S(KC_GRAVE) // ~\n#define KC_EXLM S(KC_1)    // !\n#define KC_AT   S(KC_2)    // @\n#define KC_HASH S(KC_3)    // #\n#define KC_DLR  S(KC_4)    // $\n#define KC_PERC S(KC_5)    // %\n#define KC_CIRC S(KC_6)    // ^\n#define KC_AMPR S(KC_7)    // &\n#define KC_ASTR S(KC_8)    // *\n#define KC_LPRN S(KC_9)    // (\n#define KC_RPRN S(KC_0)    // )\n#define KC_UNDS S(KC_MINUS) // _\n#define KC_PLUS S(KC_EQUAL) // +\n#define KC_LCBR S(KC_LEFT_BRACKET) // {\n#define KC_RCBR S(KC_RIGHT_BRACKET) // }\n#define KC_PIPE S(KC_BACKSLASH) // |\n#define KC_COLN S(KC_SEMICOLON) // :\n#define KC_DQUO S(KC_QUOTE) // \"\n#define KC_LABK S(KC_COMMA) // <\n#define KC_RABK S(KC_DOT)  // >\n#define KC_QUES S(KC_SLASH) // ?\n\n#define KC_TILDE KC_TILD\n#define KC_EXCLAIM KC_EXLM\n#define KC_DOLLAR KC_DLR\n#define KC_PERCENT KC_PERC\n#define KC_CIRCUMFLEX KC_CIRC\n#define KC_AMPERSAND KC_AMPR\n#define KC_ASTERISK KC_ASTR\n#define KC_LEFT_PAREN KC_LPRN\n#define KC_RIGHT_PAREN KC_RPRN\n#define KC_UNDERSCORE KC_UNDS\n#define KC_LEFT_CURLY_BRACE KC_LCBR\n#define KC_RIGHT_CURLY_BRACE KC_RCBR\n#define KC_COLON KC_COLN\n#define KC_DOUBLE_QUOTE KC_DQUO\n#define KC_DQT KC_DQUO\n#define KC_LEFT_ANGLE_BRACKET KC_LABK\n#define KC_LT KC_LABK\n#define KC_RIGHT_ANGLE_BRACKET KC_RABK\n#define KC_GT KC_RABK\n#define KC_QUESTION KC_QUES\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_us_extended.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_US_EXTENDED_KEYCODES_VERSION \"0.0.1\"\n#define QMK_US_EXTENDED_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_US_EXTENDED_KEYCODES_VERSION_MAJOR 0\n#define QMK_US_EXTENDED_KEYCODES_VERSION_MINOR 0\n#define QMK_US_EXTENDED_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define US_GRV  KC_GRV  // `\n#define US_1    KC_1    // 1\n#define US_2    KC_2    // 2\n#define US_3    KC_3    // 3\n#define US_4    KC_4    // 4\n#define US_5    KC_5    // 5\n#define US_6    KC_6    // 6\n#define US_7    KC_7    // 7\n#define US_8    KC_8    // 8\n#define US_9    KC_9    // 9\n#define US_0    KC_0    // 0\n#define US_MINS KC_MINS // -\n#define US_EQL  KC_EQL  // =\n#define US_Q    KC_Q    // Q\n#define US_W    KC_W    // W\n#define US_E    KC_E    // E\n#define US_R    KC_R    // R\n#define US_T    KC_T    // T\n#define US_Y    KC_Y    // Y\n#define US_U    KC_U    // U\n#define US_I    KC_I    // I\n#define US_O    KC_O    // O\n#define US_P    KC_P    // P\n#define US_LBRC KC_LBRC // [\n#define US_RBRC KC_RBRC // ]\n#define US_BSLS KC_BSLS // (backslash)\n#define US_A    KC_A    // A\n#define US_S    KC_S    // S\n#define US_D    KC_D    // D\n#define US_F    KC_F    // F\n#define US_G    KC_G    // G\n#define US_H    KC_H    // H\n#define US_J    KC_J    // J\n#define US_K    KC_K    // K\n#define US_L    KC_L    // L\n#define US_SCLN KC_SCLN // ;\n#define US_QUOT KC_QUOT // '\n#define US_Z    KC_Z    // Z\n#define US_X    KC_X    // X\n#define US_C    KC_C    // C\n#define US_V    KC_V    // V\n#define US_B    KC_B    // B\n#define US_N    KC_N    // N\n#define US_M    KC_M    // M\n#define US_COMM KC_COMM // ,\n#define US_DOT  KC_DOT  // .\n#define US_SLSH KC_SLSH // /\n#define US_TILD S(US_GRV)  // ~\n#define US_EXLM S(US_1)    // !\n#define US_AT   S(US_2)    // @\n#define US_HASH S(US_3)    // #\n#define US_DLR  S(US_4)    // $\n#define US_PERC S(US_5)    // %\n#define US_CIRC S(US_6)    // ^\n#define US_AMPR S(US_7)    // &\n#define US_ASTR S(US_8)    // *\n#define US_LPRN S(US_9)    // (\n#define US_RPRN S(US_0)    // )\n#define US_UNDS S(US_MINS) // _\n#define US_PLUS S(US_EQL)  // +\n#define US_LCBR S(US_LBRC) // {\n#define US_RCBR S(US_RBRC) // }\n#define US_PIPE S(US_BSLS) // |\n#define US_COLN S(US_SCLN) // :\n#define US_DQUO S(US_QUOT) // \"\n#define US_LABK S(US_COMM) // <\n#define US_RABK S(US_DOT)  // >\n#define US_QUES S(US_SLSH) // ?\n#define US_DGRV ALGR(US_GRV)  // ` (dead)\n#define US_SUP1 ALGR(US_1)    // ¹\n#define US_SUP2 ALGR(US_2)    // ²\n#define US_SUP3 ALGR(US_3)    // ³\n#define US_CURR ALGR(US_4)    // ¤\n#define US_EURO ALGR(US_5)    // €\n#define US_DCIR ALGR(US_6)    // ^ (dead)\n#define US_HORN ALGR(US_7)    // ̛ (dead)\n#define US_OGON ALGR(US_8)    // ˛ (dead)\n#define US_LSQU ALGR(US_9)    // ‘\n#define US_RSQU ALGR(US_0)    // ’\n#define US_YEN  ALGR(US_MINS) // ¥\n#define US_MUL  ALGR(US_EQL)  // ×\n#define US_ADIA ALGR(US_Q)    // Ä\n#define US_ARNG ALGR(US_W)    // Å\n#define US_EACU ALGR(US_E)    // É\n#define US_EDIA ALGR(US_R)    // Ë\n#define US_THRN ALGR(US_T)    // Þ\n#define US_UDIA ALGR(US_Y)    // Ü\n#define US_UACU ALGR(US_U)    // Ú\n#define US_IACU ALGR(US_I)    // Í\n#define US_OACU ALGR(US_O)    // Ó\n#define US_ODIA ALGR(US_P)    // Ö\n#define US_LDAQ ALGR(US_LBRC) // «\n#define US_RDAQ ALGR(US_RBRC) // »\n#define US_NOT  ALGR(US_BSLS) // ¬\n#define US_AACU ALGR(US_A)    // Á\n#define US_SS   ALGR(US_S)    // ß\n#define US_ETH  ALGR(US_D)    // Ð\n#define US_IDIA ALGR(US_J)    // Ï\n#define US_OE   ALGR(US_K)    // Œ\n#define US_OSTR ALGR(US_L)    // Ø\n#define US_PILC ALGR(US_SCLN) // ¶\n#define US_ACUT ALGR(US_QUOT) // ´ (dead)\n#define US_AE   ALGR(US_Z)    // Æ\n#define US_OE_2 ALGR(US_X)    // Œ\n#define US_COPY ALGR(US_C)    // ©\n#define US_REGD ALGR(US_V)    // ®\n#define US_NTIL ALGR(US_N)    // Ñ\n#define US_MICR ALGR(US_M)    // µ\n#define US_CCED ALGR(US_COMM) // Ç\n#define US_DOTA ALGR(US_DOT)  // ˙ (dead)\n#define US_IQUE ALGR(US_SLSH) // ¿\n#define US_DTIL S(ALGR(US_GRV))  // ~ (dead)\n#define US_IEXL S(ALGR(US_1))    // ¡\n#define US_DACU S(ALGR(US_2))    // ˝ (dead)\n#define US_MACR S(ALGR(US_3))    // ¯ (dead)\n#define US_PND  S(ALGR(US_4))    // £\n#define US_CEDL S(ALGR(US_5))    // ¸ (dead)\n#define US_QRTR S(ALGR(US_6))    // ¼\n#define US_HALF S(ALGR(US_7))    // ½\n#define US_TQTR S(ALGR(US_8))    // ¾\n#define US_BREV S(ALGR(US_9))    // ˘ (dead)\n#define US_RNGA S(ALGR(US_0))    // ° (dead)\n#define US_DOTB S(ALGR(US_MINS)) // ̣ (dead)\n#define US_DIV  S(ALGR(US_EQL))  // ÷\n#define US_LDQU S(ALGR(US_LBRC)) // “\n#define US_RDQU S(ALGR(US_RBRC)) // ”\n#define US_BRKP S(ALGR(US_BSLS)) // ¦\n#define US_SECT S(ALGR(US_S))    // §\n#define US_DEG  S(ALGR(US_SCLN)) // °\n#define US_DIAE S(ALGR(US_QUOT)) // ¨ (dead)\n#define US_CENT S(ALGR(US_C))    // ¢\n#define US_CARN S(ALGR(US_DOT))  // ˇ (dead)\n#define US_HOKA S(ALGR(US_SLSH)) // ̉ (dead)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_us_international.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_US_INTERNATIONAL_KEYCODES_VERSION \"0.0.1\"\n#define QMK_US_INTERNATIONAL_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_US_INTERNATIONAL_KEYCODES_VERSION_MAJOR 0\n#define QMK_US_INTERNATIONAL_KEYCODES_VERSION_MINOR 0\n#define QMK_US_INTERNATIONAL_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define US_DGRV KC_GRV  // ` (dead)\n#define US_1    KC_1    // 1\n#define US_2    KC_2    // 2\n#define US_3    KC_3    // 3\n#define US_4    KC_4    // 4\n#define US_5    KC_5    // 5\n#define US_6    KC_6    // 6\n#define US_7    KC_7    // 7\n#define US_8    KC_8    // 8\n#define US_9    KC_9    // 9\n#define US_0    KC_0    // 0\n#define US_MINS KC_MINS // -\n#define US_EQL  KC_EQL  // =\n#define US_Q    KC_Q    // Q\n#define US_W    KC_W    // W\n#define US_E    KC_E    // E\n#define US_R    KC_R    // R\n#define US_T    KC_T    // T\n#define US_Y    KC_Y    // Y\n#define US_U    KC_U    // U\n#define US_I    KC_I    // I\n#define US_O    KC_O    // O\n#define US_P    KC_P    // P\n#define US_LBRC KC_LBRC // [\n#define US_RBRC KC_RBRC // ]\n#define US_BSLS KC_BSLS // (backslash)\n#define US_A    KC_A    // A\n#define US_S    KC_S    // S\n#define US_D    KC_D    // D\n#define US_F    KC_F    // F\n#define US_G    KC_G    // G\n#define US_H    KC_H    // H\n#define US_J    KC_J    // J\n#define US_K    KC_K    // K\n#define US_L    KC_L    // L\n#define US_SCLN KC_SCLN // ;\n#define US_ACUT KC_QUOT // ´ (dead)\n#define US_Z    KC_Z    // Z\n#define US_X    KC_X    // X\n#define US_C    KC_C    // C\n#define US_V    KC_V    // V\n#define US_B    KC_B    // B\n#define US_N    KC_N    // N\n#define US_M    KC_M    // M\n#define US_COMM KC_COMM // ,\n#define US_DOT  KC_DOT  // .\n#define US_SLSH KC_SLSH // /\n#define US_DTIL S(US_DGRV) // ~ (dead)\n#define US_EXLM S(US_1)    // !\n#define US_AT   S(US_2)    // @\n#define US_HASH S(US_3)    // #\n#define US_DLR  S(US_4)    // $\n#define US_PERC S(US_5)    // %\n#define US_DCIR S(US_6)    // ^ (dead)\n#define US_AMPR S(US_7)    // &\n#define US_ASTR S(US_8)    // *\n#define US_LPRN S(US_9)    // (\n#define US_RPRN S(US_0)    // )\n#define US_UNDS S(US_MINS) // _\n#define US_PLUS S(US_EQL)  // +\n#define US_LCBR S(US_LBRC) // {\n#define US_RCBR S(US_RBRC) // }\n#define US_PIPE S(US_BSLS) // |\n#define US_COLN S(US_SCLN) // :\n#define US_DIAE S(US_ACUT) // ¨ (dead)\n#define US_LABK S(US_COMM) // <\n#define US_RABK S(US_DOT)  // >\n#define US_QUES S(US_SLSH) // ?\n#define US_IEXL ALGR(US_1)    // ¡\n#define US_SUP2 ALGR(US_2)    // ²\n#define US_SUP3 ALGR(US_3)    // ³\n#define US_CURR ALGR(US_4)    // ¤\n#define US_EURO ALGR(US_5)    // €\n#define US_QRTR ALGR(US_6)    // ¼\n#define US_HALF ALGR(US_7)    // ½\n#define US_TQTR ALGR(US_8)    // ¾\n#define US_LSQU ALGR(US_9)    // ‘\n#define US_RSQU ALGR(US_0)    // ’\n#define US_YEN  ALGR(US_MINS) // ¥\n#define US_MUL  ALGR(US_EQL)  // ×\n#define US_ADIA ALGR(US_Q)    // Ä\n#define US_ARNG ALGR(US_W)    // Å\n#define US_EACU ALGR(US_E)    // É\n#define US_REGD ALGR(US_R)    // ®\n#define US_THRN ALGR(US_T)    // Þ\n#define US_UDIA ALGR(US_Y)    // Ü\n#define US_UACU ALGR(US_U)    // Ú\n#define US_IACU ALGR(US_I)    // Í\n#define US_OACU ALGR(US_O)    // Ó\n#define US_ODIA ALGR(US_P)    // Ö\n#define US_LDAQ ALGR(US_LBRC) // «\n#define US_RDAQ ALGR(US_RBRC) // »\n#define US_NOT  ALGR(US_BSLS) // ¬\n#define US_AACU ALGR(US_A)    // Á\n#define US_SS   ALGR(US_S)    // ß\n#define US_ETH  ALGR(US_D)    // Ð\n#define US_OSTR ALGR(US_L)    // Ø\n#define US_PILC ALGR(US_SCLN) // ¶\n#define US_NDAC ALGR(US_ACUT) // ´\n#define US_AE   ALGR(US_Z)    // Æ\n#define US_COPY ALGR(US_C)    // ©\n#define US_NTIL ALGR(US_N)    // Ñ\n#define US_MICR ALGR(US_M)    // µ\n#define US_CCED ALGR(US_COMM) // Ç\n#define US_IQUE ALGR(US_SLSH) // ¿\n#define US_SUP1 S(ALGR(US_1))    // ¹\n#define US_PND  S(ALGR(US_4))    // £\n#define US_DIV  S(ALGR(US_EQL))  // ÷\n#define US_BRKP S(ALGR(US_BSLS)) // ¦\n#define US_SECT S(ALGR(US_S))    // §\n#define US_DEG  S(ALGR(US_SCLN)) // °\n#define US_NDDR S(ALGR(US_ACUT)) // ¨\n#define US_CENT S(ALGR(US_C))    // ¢\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_us_international_linux.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_US_INTERNATIONAL_LINUX_KEYCODES_VERSION \"0.0.1\"\n#define QMK_US_INTERNATIONAL_LINUX_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_US_INTERNATIONAL_LINUX_KEYCODES_VERSION_MAJOR 0\n#define QMK_US_INTERNATIONAL_LINUX_KEYCODES_VERSION_MINOR 0\n#define QMK_US_INTERNATIONAL_LINUX_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define US_DGRV KC_GRV  // ` (dead)\n#define US_1    KC_1    // 1\n#define US_2    KC_2    // 2\n#define US_3    KC_3    // 3\n#define US_4    KC_4    // 4\n#define US_5    KC_5    // 5\n#define US_6    KC_6    // 6\n#define US_7    KC_7    // 7\n#define US_8    KC_8    // 8\n#define US_9    KC_9    // 9\n#define US_0    KC_0    // 0\n#define US_MINS KC_MINS // -\n#define US_EQL  KC_EQL  // =\n#define US_Q    KC_Q    // Q\n#define US_W    KC_W    // W\n#define US_E    KC_E    // E\n#define US_R    KC_R    // R\n#define US_T    KC_T    // T\n#define US_Y    KC_Y    // Y\n#define US_U    KC_U    // U\n#define US_I    KC_I    // I\n#define US_O    KC_O    // O\n#define US_P    KC_P    // P\n#define US_LBRC KC_LBRC // [\n#define US_RBRC KC_RBRC // ]\n#define US_BSLS KC_BSLS // (backslash)\n#define US_A    KC_A    // A\n#define US_S    KC_S    // S\n#define US_D    KC_D    // D\n#define US_F    KC_F    // F\n#define US_G    KC_G    // G\n#define US_H    KC_H    // H\n#define US_J    KC_J    // J\n#define US_K    KC_K    // K\n#define US_L    KC_L    // L\n#define US_SCLN KC_SCLN // ;\n#define US_ACUT KC_QUOT // ´ (dead)\n#define US_Z    KC_Z    // Z\n#define US_X    KC_X    // X\n#define US_C    KC_C    // C\n#define US_V    KC_V    // V\n#define US_B    KC_B    // B\n#define US_N    KC_N    // N\n#define US_M    KC_M    // M\n#define US_COMM KC_COMM // ,\n#define US_DOT  KC_DOT  // .\n#define US_SLSH KC_SLSH // /\n#define US_DTIL S(US_DGRV) // ~ (dead)\n#define US_EXLM S(US_1)    // !\n#define US_AT   S(US_2)    // @\n#define US_HASH S(US_3)    // #\n#define US_DLR  S(US_4)    // $\n#define US_PERC S(US_5)    // %\n#define US_DCIR S(US_6)    // ^ (dead)\n#define US_AMPR S(US_7)    // &\n#define US_ASTR S(US_8)    // *\n#define US_LPRN S(US_9)    // (\n#define US_RPRN S(US_0)    // )\n#define US_UNDS S(US_MINS) // _\n#define US_PLUS S(US_EQL)  // +\n#define US_LCBR S(US_LBRC) // {\n#define US_RCBR S(US_RBRC) // }\n#define US_PIPE S(US_BSLS) // |\n#define US_COLN S(US_SCLN) // :\n#define US_DIAE S(US_ACUT) // ¨ (dead)\n#define US_LABK S(US_COMM) // <\n#define US_RABK S(US_DOT)  // >\n#define US_QUES S(US_SLSH) // ?\n#define US_GRV  ALGR(US_DGRV) // `\n#define US_IEXL ALGR(US_1)    // ¡\n#define US_SUP2 ALGR(US_2)    // ²\n#define US_SUP3 ALGR(US_3)    // ³\n#define US_CURR ALGR(US_4)    // ¤\n#define US_EURO ALGR(US_5)    // €\n#define US_QRTR ALGR(US_6)    // ¼\n#define US_HALF ALGR(US_7)    // ½\n#define US_TQTR ALGR(US_8)    // ¾\n#define US_LSQU ALGR(US_9)    // ‘\n#define US_RSQU ALGR(US_0)    // ’\n#define US_YEN  ALGR(US_MINS) // ¥\n#define US_MUL  ALGR(US_EQL)  // ×\n#define US_ADIA ALGR(US_Q)    // Ä\n#define US_ARNG ALGR(US_W)    // Å\n#define US_EACU ALGR(US_E)    // É\n#define US_REGD ALGR(US_R)    // ®\n#define US_THRN ALGR(US_T)    // Þ\n#define US_UDIA ALGR(US_Y)    // Ü\n#define US_UACU ALGR(US_U)    // Ú\n#define US_IACU ALGR(US_I)    // Í\n#define US_OACU ALGR(US_O)    // Ó\n#define US_ODIA ALGR(US_P)    // Ö\n#define US_LDAQ ALGR(US_LBRC) // «\n#define US_RDAQ ALGR(US_RBRC) // »\n#define US_NOT  ALGR(US_BSLS) // ¬\n#define US_AACU ALGR(US_A)    // Á\n#define US_SS   ALGR(US_S)    // ß\n#define US_ETH  ALGR(US_D)    // Ð\n#define US_OE   ALGR(US_K)    // Œ\n#define US_OSTR ALGR(US_L)    // Ø\n#define US_PILC ALGR(US_SCLN) // ¶\n#define US_QUOT ALGR(US_ACUT) // '\n#define US_AE   ALGR(US_Z)    // Æ\n#define US_COPY ALGR(US_C)    // ©\n#define US_NTIL ALGR(US_N)    // Ñ\n#define US_MICR ALGR(US_M)    // µ\n#define US_CCED ALGR(US_COMM) // Ç\n#define US_DOTA ALGR(US_DOT)  // ˙ (dead)\n#define US_IQUE ALGR(US_SLSH) // ¿\n#define US_TILD S(ALGR(US_DGRV)) // ~\n#define US_SUP1 S(ALGR(US_1))    // ¹\n#define US_DACU S(ALGR(US_2))    // ˝ (dead)\n#define US_MACR S(ALGR(US_3))    // ¯ (dead)\n#define US_PND  S(ALGR(US_4))    // £\n#define US_CEDL S(ALGR(US_5))    // ¸ (dead)\n#define US_CIRC S(ALGR(US_6))    // ^\n#define US_HORN S(ALGR(US_7))    // ̛ (dead)\n#define US_OGON S(ALGR(US_8))    // ˛ (dead)\n#define US_BREV S(ALGR(US_9))    // ˘ (dead)\n#define US_RNGA S(ALGR(US_0))    // ° (dead)\n#define US_DOTB S(ALGR(US_MINS)) // ̣ (dead)\n#define US_DIV  S(ALGR(US_EQL))  // ÷\n#define US_LDQU S(ALGR(US_LBRC)) // “\n#define US_RDQU S(ALGR(US_RBRC)) // ”\n#define US_BRKP S(ALGR(US_BSLS)) // ¦\n#define US_SECT S(ALGR(US_S))    // §\n#define US_DEG  S(ALGR(US_SCLN)) // °\n#define US_DQUO S(ALGR(US_ACUT)) // \"\n#define US_CENT S(ALGR(US_C))    // ¢\n#define US_CARN S(ALGR(US_DOT))  // ˇ (dead)\n#define US_HOKA S(ALGR(US_SLSH)) // ̉ (dead)\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_workman.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_WORKMAN_KEYCODES_VERSION \"0.0.1\"\n#define QMK_WORKMAN_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_WORKMAN_KEYCODES_VERSION_MAJOR 0\n#define QMK_WORKMAN_KEYCODES_VERSION_MINOR 0\n#define QMK_WORKMAN_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define WK_GRV  KC_GRV  // `\n#define WK_1    KC_1    // 1\n#define WK_2    KC_2    // 2\n#define WK_3    KC_3    // 3\n#define WK_4    KC_4    // 4\n#define WK_5    KC_5    // 5\n#define WK_6    KC_6    // 6\n#define WK_7    KC_7    // 7\n#define WK_8    KC_8    // 8\n#define WK_9    KC_9    // 9\n#define WK_0    KC_0    // 0\n#define WK_MINS KC_MINS // -\n#define WK_EQL  KC_EQL  // =\n#define WK_Q    KC_Q    // Q\n#define WK_D    KC_W    // D\n#define WK_R    KC_E    // R\n#define WK_W    KC_R    // W\n#define WK_B    KC_T    // B\n#define WK_J    KC_Y    // J\n#define WK_F    KC_U    // F\n#define WK_U    KC_I    // U\n#define WK_P    KC_O    // P\n#define WK_SCLN KC_P    // ;\n#define WK_LBRC KC_LBRC // [\n#define WK_RBRC KC_RBRC // ]\n#define WK_BSLS KC_BSLS // (backslash)\n#define WK_A    KC_A    // A\n#define WK_S    KC_S    // S\n#define WK_H    KC_D    // H\n#define WK_T    KC_F    // T\n#define WK_G    KC_G    // G\n#define WK_Y    KC_H    // Y\n#define WK_N    KC_J    // N\n#define WK_E    KC_K    // E\n#define WK_O    KC_L    // O\n#define WK_I    KC_SCLN // I\n#define WK_QUOT KC_QUOT // '\n#define WK_Z    KC_Z    // Z\n#define WK_X    KC_X    // X\n#define WK_M    KC_C    // M\n#define WK_C    KC_V    // C\n#define WK_V    KC_B    // V\n#define WK_K    KC_N    // K\n#define WK_L    KC_M    // L\n#define WK_COMM KC_COMM // ,\n#define WK_DOT  KC_DOT  // .\n#define WK_SLSH KC_SLSH // /\n#define WK_TILD S(WK_GRV)  // ~\n#define WK_EXLM S(WK_1)    // !\n#define WK_AT   S(WK_2)    // @\n#define WK_HASH S(WK_3)    // #\n#define WK_DLR  S(WK_4)    // $\n#define WK_PERC S(WK_5)    // %\n#define WK_CIRC S(WK_6)    // ^\n#define WK_AMPR S(WK_7)    // &\n#define WK_ASTR S(WK_8)    // *\n#define WK_LPRN S(WK_9)    // (\n#define WK_RPRN S(WK_0)    // )\n#define WK_UNDS S(WK_MINS) // _\n#define WK_PLUS S(WK_EQL)  // +\n#define WK_COLN S(WK_SCLN) // :\n#define WK_LCBR S(WK_LBRC) // {\n#define WK_RCBR S(WK_RBRC) // }\n#define WK_PIPE S(WK_BSLS) // |\n#define WK_DQUO S(WK_QUOT) // \"\n#define WK_LABK S(WK_COMM) // <\n#define WK_RABK S(WK_DOT)  // >\n#define WK_QUES S(WK_SLSH) // ?\n\n"
  },
  {
    "path": "quantum/keymap_extras/keymap_workman_zxcvm.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n#include \"keycodes.h\"\n// clang-format off\n\n#define QMK_WORKMAN_ZXCVM_KEYCODES_VERSION \"0.0.1\"\n#define QMK_WORKMAN_ZXCVM_KEYCODES_VERSION_BCD 0x00000001\n#define QMK_WORKMAN_ZXCVM_KEYCODES_VERSION_MAJOR 0\n#define QMK_WORKMAN_ZXCVM_KEYCODES_VERSION_MINOR 0\n#define QMK_WORKMAN_ZXCVM_KEYCODES_VERSION_PATCH 1\n\n// Aliases\n#define WK_GRV  KC_GRV  // `\n#define WK_1    KC_1    // 1\n#define WK_2    KC_2    // 2\n#define WK_3    KC_3    // 3\n#define WK_4    KC_4    // 4\n#define WK_5    KC_5    // 5\n#define WK_6    KC_6    // 6\n#define WK_7    KC_7    // 7\n#define WK_8    KC_8    // 8\n#define WK_9    KC_9    // 9\n#define WK_0    KC_0    // 0\n#define WK_MINS KC_MINS // -\n#define WK_EQL  KC_EQL  // =\n#define WK_Q    KC_Q    // Q\n#define WK_D    KC_W    // D\n#define WK_R    KC_E    // R\n#define WK_W    KC_R    // W\n#define WK_B    KC_T    // B\n#define WK_J    KC_Y    // J\n#define WK_F    KC_U    // F\n#define WK_U    KC_I    // U\n#define WK_P    KC_O    // P\n#define WK_SCLN KC_P    // ;\n#define WK_LBRC KC_LBRC // [\n#define WK_RBRC KC_RBRC // ]\n#define WK_BSLS KC_BSLS // (backslash)\n#define WK_A    KC_A    // A\n#define WK_S    KC_S    // S\n#define WK_H    KC_D    // H\n#define WK_T    KC_F    // T\n#define WK_G    KC_G    // G\n#define WK_Y    KC_H    // Y\n#define WK_N    KC_J    // N\n#define WK_E    KC_K    // E\n#define WK_O    KC_L    // O\n#define WK_I    KC_SCLN // I\n#define WK_QUOT KC_QUOT // '\n#define WK_Z    KC_Z    // Z\n#define WK_X    KC_X    // X\n#define WK_C    KC_C    // C\n#define WK_V    KC_V    // V\n#define WK_M    KC_B    // M\n#define WK_K    KC_N    // K\n#define WK_L    KC_M    // L\n#define WK_COMM KC_COMM // ,\n#define WK_DOT  KC_DOT  // .\n#define WK_SLSH KC_SLSH // /\n#define WK_TILD S(WK_GRV)  // ~\n#define WK_EXLM S(WK_1)    // !\n#define WK_AT   S(WK_2)    // @\n#define WK_HASH S(WK_3)    // #\n#define WK_DLR  S(WK_4)    // $\n#define WK_PERC S(WK_5)    // %\n#define WK_CIRC S(WK_6)    // ^\n#define WK_AMPR S(WK_7)    // &\n#define WK_ASTR S(WK_8)    // *\n#define WK_LPRN S(WK_9)    // (\n#define WK_RPRN S(WK_0)    // )\n#define WK_UNDS S(WK_MINS) // _\n#define WK_PLUS S(WK_EQL)  // +\n#define WK_COLN S(WK_SCLN) // :\n#define WK_LCBR S(WK_LBRC) // {\n#define WK_RCBR S(WK_RBRC) // }\n#define WK_PIPE S(WK_BSLS) // |\n#define WK_DQUO S(WK_QUOT) // \"\n#define WK_LABK S(WK_COMM) // <\n#define WK_RABK S(WK_DOT)  // >\n#define WK_QUES S(WK_SLSH) // ?\n\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_belgian.h",
    "content": "/* Copyright 2019 kimat\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Belgian layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_belgian.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 0, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 0, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  BE_EXLM, BE_DQUO, BE_DQUO, BE_DLR,  BE_UGRV, BE_AMPR, BE_QUOT,\n    // (     )        *        +        ,        -        .        /\n    BE_LPRN, BE_RPRN, BE_DLR,  BE_EQL,  BE_COMM, BE_MINS, BE_SCLN, BE_COLN,\n    // 0     1        2        3        4        5        6        7\n    BE_AGRV, BE_AMPR, BE_EACU, BE_DQUO, BE_QUOT, BE_LPRN, BE_SECT, BE_EGRV,\n    // 8     9        :        ;        <        =        >        ?\n    BE_EXLM, BE_CCED, BE_COLN, BE_SCLN, BE_LABK, BE_EQL,  BE_LABK, BE_COMM,\n    // @     A        B        C        D        E        F        G\n    BE_EACU, BE_A,    BE_B,    BE_C,    BE_D,    BE_E,    BE_F,    BE_G,\n    // H     I        J        K        L        M        N        O\n    BE_H,    BE_I,    BE_J,    BE_K,    BE_L,    BE_M,    BE_N,    BE_O,\n    // P     Q        R        S        T        U        V        W\n    BE_P,    BE_Q,    BE_R,    BE_S,    BE_T,    BE_U,    BE_V,    BE_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    BE_X,    BE_Y,    BE_Z,    BE_DCIR, BE_LABK, BE_DLR,  BE_SECT, BE_MINS,\n    // `     a        b        c        d        e        f        g\n    BE_MICR, BE_A,    BE_B,    BE_C,    BE_D,    BE_E,    BE_F,    BE_G,\n    // h     i        j        k        l        m        n        o\n    BE_H,    BE_I,    BE_J,    BE_K,    BE_L,    BE_M,    BE_N,    BE_O,\n    // p     q        r        s        t        u        v        w\n    BE_P,    BE_Q,    BE_R,    BE_S,    BE_T,    BE_U,    BE_V,    BE_W,\n    // x     y        z        {        |        }        ~        DEL\n    BE_X,    BE_Y,    BE_Z,    BE_CCED, BE_AMPR, BE_AGRV, BE_EQL,  KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_bepo.h",
    "content": "/* Copyright 2018 Jonathan Nifenecker\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for BÉPO layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_bepo.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 0, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  BP_DCIR, BP_DQUO, BP_DLR,  BP_DLR,  BP_PERC, BP_P,    BP_QUOT,\n    // (     )        *        +        ,        -        .        /\n    BP_LPRN, BP_RPRN, BP_ASTR, BP_PLUS, BP_COMM, BP_MINS, BP_DOT,  BP_SLSH,\n    // 0     1        2        3        4        5        6        7\n    BP_ASTR, BP_DQUO, BP_LDAQ, BP_RDAQ, BP_LPRN, BP_RPRN, BP_AT,   BP_PLUS,\n    // 8     9        :        ;        <        =        >        ?\n    BP_MINS, BP_SLSH, BP_DOT,  BP_COMM, BP_LDAQ, BP_EQL,  BP_RDAQ, BP_QUOT,\n    // @     A        B        C        D        E        F        G\n    BP_AT,   BP_A,    BP_B,    BP_C,    BP_D,    BP_E,    BP_F,    BP_G,\n    // H     I        J        K        L        M        N        O\n    BP_H,    BP_I,    BP_J,    BP_K,    BP_L,    BP_M,    BP_N,    BP_O,\n    // P     Q        R        S        T        U        V        W\n    BP_P,    BP_Q,    BP_R,    BP_S,    BP_T,    BP_U,    BP_V,    BP_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    BP_X,    BP_Y,    BP_Z,    BP_LPRN, BP_AGRV, BP_RPRN, BP_AT,   KC_SPC,\n    // `     a        b        c        d        e        f        g\n    BP_PERC, BP_A,    BP_B,    BP_C,    BP_D,    BP_E,    BP_F,    BP_G,\n    // h     i        j        k        l        m        n        o\n    BP_H,    BP_I,    BP_J,    BP_K,    BP_L,    BP_M,    BP_N,    BP_O,\n    // p     q        r        s        t        u        v        w\n    BP_P,    BP_Q,    BP_R,    BP_S,    BP_T,    BP_U,    BP_V,    BP_W,\n    // x     y        z        {        |        }        ~        DEL\n    BP_X,    BP_Y,    BP_Z,    BP_Y,    BP_B,    BP_X,    BP_K,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_brazilian_abnt2.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Brazilian (ABNT2) layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_brazilian_abnt2.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 0, 1, 0, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  BR_1,    BR_QUOT, BR_3,    BR_4,    BR_5,    BR_7,    BR_QUOT,\n    // (     )        *        +        ,        -        .        /\n    BR_9,    BR_0,    BR_8,    BR_EQL,  BR_COMM, BR_MINS, BR_DOT,  BR_SLSH,\n    // 0     1        2        3        4        5        6        7\n    BR_0,    BR_1,    BR_2,    BR_3,    BR_4,    BR_5,    BR_6,    BR_7,\n    // 8     9        :        ;        <        =        >        ?\n    BR_8,    BR_9,    BR_SCLN, BR_SCLN, BR_COMM, BR_EQL,  BR_DOT,  BR_SLSH,\n    // @     A        B        C        D        E        F        G\n    BR_2,    BR_A,    BR_B,    BR_C,    BR_D,    BR_E,    BR_F,    BR_G,\n    // H     I        J        K        L        M        N        O\n    BR_H,    BR_I,    BR_J,    BR_K,    BR_L,    BR_M,    BR_N,    BR_O,\n    // P     Q        R        S        T        U        V        W\n    BR_P,    BR_Q,    BR_R,    BR_S,    BR_T,    BR_U,    BR_V,    BR_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    BR_X,    BR_Y,    BR_Z,    BR_LBRC, BR_BSLS, BR_RBRC, BR_TILD, BR_MINS,\n    // `     a        b        c        d        e        f        g\n    BR_ACUT, BR_A,    BR_B,    BR_C,    BR_D,    BR_E,    BR_F,    BR_G,\n    // h     i        j        k        l        m        n        o\n    BR_H,    BR_I,    BR_J,    BR_K,    BR_L,    BR_M,    BR_N,    BR_O,\n    // p     q        r        s        t        u        v        w\n    BR_P,    BR_Q,    BR_R,    BR_S,    BR_T,    BR_U,    BR_V,    BR_W,\n    // x     y        z        {        |        }        ~        DEL\n    BR_X,    BR_Y,    BR_Z,    BR_LBRC, BR_BSLS, BR_RBRC, BR_TILD, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_canadian_french.h",
    "content": "/* Copyright 2023 Nebuleon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Canadian French layouts\n\n#pragma once\n\n#include \"keymap_canadian_french.h\"\n#include \"send_string.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  FR_1,    FR_2,    FR_HASH, FR_4,    FR_5,    FR_7,    FR_COMM,\n    // (     )        *        +        ,        -        .        /\n    FR_9,    FR_0,    FR_8,    FR_EQL,  FR_COMM, FR_MINS, FR_DOT,  FR_3,\n    // 0     1        2        3        4        5        6        7\n    FR_0,    FR_1,    FR_2,    FR_3,    FR_4,    FR_5,    FR_6,    FR_7,\n    // 8     9        :        ;        <        =        >        ?\n    FR_8,    FR_9,    FR_SCLN, FR_SCLN, FR_LABK, FR_EQL,  FR_LABK, FR_6,\n    // @     A        B        C        D        E        F        G\n    FR_2,    FR_A,    FR_B,    FR_C,    FR_D,    FR_E,    FR_F,    FR_G,\n    // H     I        J        K        L        M        N        O\n    FR_H,    FR_I,    FR_J,    FR_K,    FR_L,    FR_M,    FR_N,    FR_O,\n    // P     Q        R        S        T        U        V        W\n    FR_P,    FR_Q,    FR_R,    FR_S,    FR_T,    FR_U,    FR_V,    FR_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    FR_X,    FR_Y,    FR_Z,    FR_DCIR, FR_HASH, FR_CEDL, FR_DCIR, FR_MINS,\n    // `     a        b        c        d        e        f        g\n    FR_DGRV, FR_A,    FR_B,    FR_C,    FR_D,    FR_E,    FR_F,    FR_G,\n    // h     i        j        k        l        m        n        o\n    FR_H,    FR_I,    FR_J,    FR_K,    FR_L,    FR_M,    FR_N,    FR_O,\n    // p     q        r        s        t        u        v        w\n    FR_P,    FR_Q,    FR_R,    FR_S,    FR_T,    FR_U,    FR_V,    FR_W,\n    // x     y        z        {        |        }        ~        DEL\n    FR_X,    FR_Y,    FR_Z,    FR_DGRV, FR_HASH, FR_LABK, FR_SCLN, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_canadian_multilingual.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Canadian Multilingual layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_canadian_multilingual.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 1, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  CA_1,    CA_DOT,  CA_3,    CA_4,    CA_5,    CA_7,    CA_COMM,\n    // (     )        *        +        ,        -        .        /\n    CA_9,    CA_0,    CA_8,    CA_EQL,  CA_COMM, CA_MINS, CA_DOT,  CA_SLSH,\n    // 0     1        2        3        4        5        6        7\n    CA_0,    CA_1,    CA_2,    CA_3,    CA_4,    CA_5,    CA_6,    CA_7,\n    // 8     9        :        ;        <        =        >        ?\n    CA_8,    CA_9,    CA_SCLN, CA_SCLN, CA_COMM, CA_EQL,  CA_DOT,  CA_6,\n    // @     A        B        C        D        E        F        G\n    CA_2,    CA_A,    CA_B,    CA_C,    CA_D,    CA_E,    CA_F,    CA_G,\n    // H     I        J        K        L        M        N        O\n    CA_H,    CA_I,    CA_J,    CA_K,    CA_L,    CA_M,    CA_N,    CA_O,\n    // P     Q        R        S        T        U        V        W\n    CA_P,    CA_Q,    CA_R,    CA_S,    CA_T,    CA_U,    CA_V,    CA_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    CA_X,    CA_Y,    CA_Z,    CA_9,    CA_SLSH, CA_0,    CA_CIRC, CA_MINS,\n    // `     a        b        c        d        e        f        g\n    CA_CIRC, CA_A,    CA_B,    CA_C,    CA_D,    CA_E,    CA_F,    CA_G,\n    // h     i        j        k        l        m        n        o\n    CA_H,    CA_I,    CA_J,    CA_K,    CA_L,    CA_M,    CA_N,    CA_O,\n    // p     q        r        s        t        u        v        w\n    CA_P,    CA_Q,    CA_R,    CA_S,    CA_T,    CA_U,    CA_V,    CA_W,\n    // x     y        z        {        |        }        ~        DEL\n    CA_X,    CA_Y,    CA_Z,    CA_7,    CA_SLSH, CA_8,    CA_CCED, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_colemak.h",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Colemak layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_colemak.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  CM_1,    CM_QUOT, CM_3,    CM_4,    CM_5,    CM_7,    CM_QUOT,\n    // (     )        *        +        ,        -        .        /\n    CM_9,    CM_0,    CM_8,    CM_EQL,  CM_COMM, CM_MINS, CM_DOT,  CM_SLSH,\n    // 0     1        2        3        4        5        6        7\n    CM_0,    CM_1,    CM_2,    CM_3,    CM_4,    CM_5,    CM_6,    CM_7,\n    // 8     9        :        ;        <        =        >        ?\n    CM_8,    CM_9,    CM_SCLN, CM_SCLN, CM_COMM, CM_EQL,  CM_DOT,  CM_SLSH,\n    // @     A        B        C        D        E        F        G\n    CM_2,    CM_A,    CM_B,    CM_C,    CM_D,    CM_E,    CM_F,    CM_G,\n    // H     I        J        K        L        M        N        O\n    CM_H,    CM_I,    CM_J,    CM_K,    CM_L,    CM_M,    CM_N,    CM_O,\n    // P     Q        R        S        T        U        V        W\n    CM_P,    CM_Q,    CM_R,    CM_S,    CM_T,    CM_U,    CM_V,    CM_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    CM_X,    CM_Y,    CM_Z,    CM_LBRC, CM_BSLS, CM_RBRC, CM_6,    CM_MINS,\n    // `     a        b        c        d        e        f        g\n    CM_GRV,  CM_A,    CM_B,    CM_C,    CM_D,    CM_E,    CM_F,    CM_G,\n    // h     i        j        k        l        m        n        o\n    CM_H,    CM_I,    CM_J,    CM_K,    CM_L,    CM_M,    CM_N,    CM_O,\n    // p     q        r        s        t        u        v        w\n    CM_P,    CM_Q,    CM_R,    CM_S,    CM_T,    CM_U,    CM_V,    CM_W,\n    // x     y        z        {        |        }        ~        DEL\n    CM_X,    CM_Y,    CM_Z,    CM_LBRC, CM_BSLS, CM_RBRC, CM_GRV,  KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_croatian.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Croatian layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_croatian.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  HR_1,    HR_2,    HR_3,    HR_4,    HR_5,    HR_6,    HR_QUOT,\n    // (     )        *        +        ,        -        .        /\n    HR_8,    HR_9,    HR_PLUS, HR_PLUS, HR_COMM, HR_MINS, HR_DOT,  HR_7,\n    // 0     1        2        3        4        5        6        7\n    HR_0,    HR_1,    HR_2,    HR_3,    HR_4,    HR_5,    HR_6,    HR_7,\n    // 8     9        :        ;        <        =        >        ?\n    HR_8,    HR_9,    HR_DOT,  HR_COMM, HR_LABK, HR_0,    HR_LABK, HR_QUOT,\n    // @     A        B        C        D        E        F        G\n    HR_V,    HR_A,    HR_B,    HR_C,    HR_D,    HR_E,    HR_F,    HR_G,\n    // H     I        J        K        L        M        N        O\n    HR_H,    HR_I,    HR_J,    HR_K,    HR_L,    HR_M,    HR_N,    HR_O,\n    // P     Q        R        S        T        U        V        W\n    HR_P,    HR_Q,    HR_R,    HR_S,    HR_T,    HR_U,    HR_V,    HR_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    HR_X,    HR_Y,    HR_Z,    HR_F,    HR_Q,    HR_G,    HR_3,    HR_MINS,\n    // `     a        b        c        d        e        f        g\n    HR_7,    HR_A,    HR_B,    HR_C,    HR_D,    HR_E,    HR_F,    HR_G,\n    // h     i        j        k        l        m        n        o\n    HR_H,    HR_I,    HR_J,    HR_K,    HR_L,    HR_M,    HR_N,    HR_O,\n    // p     q        r        s        t        u        v        w\n    HR_P,    HR_Q,    HR_R,    HR_S,    HR_T,    HR_U,    HR_V,    HR_W,\n    // x     y        z        {        |        }        ~        DEL\n    HR_X,    HR_Y,    HR_Z,    HR_B,    HR_W,    HR_N,    HR_1,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_czech.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Czech layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_czech.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 0, 1, 0, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0),\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  CZ_SECT, CZ_URNG, CZ_X,    CZ_URNG, CZ_EQL,  CZ_C,    CZ_DIAE,\n    // (     )        *        +        ,        -        .        /\n    CZ_RPRN, CZ_RPRN, CZ_MINS, CZ_PLUS, CZ_COMM, CZ_MINS, CZ_DOT,  CZ_UACU,\n    // 0     1        2        3        4        5        6        7\n    CZ_EACU, CZ_PLUS, CZ_ECAR, CZ_SCAR, CZ_CCAR, CZ_RCAR, CZ_ZCAR, CZ_YACU,\n    // 8     9        :        ;        <        =        >        ?\n    CZ_AACU, CZ_IACU, CZ_DOT,  CZ_SCLN, CZ_COMM, CZ_EQL,  CZ_DOT,  CZ_COMM,\n    // @     A        B        C        D        E        F        G\n    CZ_V,    CZ_A,    CZ_B,    CZ_C,    CZ_D,    CZ_E,    CZ_F,    CZ_G,\n    // H     I        J        K        L        M        N        O\n    CZ_H,    CZ_I,    CZ_J,    CZ_K,    CZ_L,    CZ_M,    CZ_N,    CZ_O,\n    // P     Q        R        S        T        U        V        W\n    CZ_P,    CZ_Q,    CZ_R,    CZ_S,    CZ_T,    CZ_U,    CZ_V,    CZ_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    CZ_X,    CZ_Y,    CZ_Z,    CZ_F,    CZ_BSLS, CZ_G,    CZ_SCAR, CZ_MINS,\n    // `     a        b        c        d        e        f        g\n    CZ_YACU, CZ_A,    CZ_B,    CZ_C,    CZ_D,    CZ_E,    CZ_F,    CZ_G,\n    // h     i        j        k        l        m        n        o\n    CZ_H,    CZ_I,    CZ_J,    CZ_K,    CZ_L,    CZ_M,    CZ_N,    CZ_O,\n    // p     q        r        s        t        u        v        w\n    CZ_P,    CZ_Q,    CZ_R,    CZ_S,    CZ_T,    CZ_U,    CZ_V,    CZ_W,\n    // x     y        z        {        |        }        ~        DEL\n    CZ_X,    CZ_Y,    CZ_Z,    CZ_B,    CZ_BSLS, CZ_N,    CZ_PLUS, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_czech_mac_ansi.h",
    "content": "/* Copyright 2024 Tabonx\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for macOS Czech ANSI layouts\n\n#pragma once\n\n#include \"keymap_czech_mac_ansi.h\"\n#include \"send_string.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 0, 1, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0),\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  CZ_SECT, CZ_URNG, CZ_SCAR, CZ_CCAR, CZ_EQL,  CZ_YACU, CZ_SECT,\n    // (     )        *        +        ,        -        .        /\n    CZ_RPRN, CZ_RPRN, CZ_AACU, CZ_PLUS, CZ_COMM, CZ_MINS, CZ_DOT,  CZ_UACU,\n    // 0     1        2        3        4        5        6        7\n    CZ_EACU, CZ_PLUS, CZ_ECAR, CZ_SCAR, CZ_CCAR, CZ_RCAR, CZ_ZCAR, CZ_YACU,\n    // 8     9        :        ;        <        =        >        ?\n    CZ_AACU, CZ_IACU, CZ_DOT,  CZ_URNG, CZ_COMM, CZ_EQL,  CZ_DOT,  CZ_COMM,\n    // @     A        B        C        D        E        F        G\n    CZ_ECAR, CZ_A,    CZ_B,    CZ_C,    CZ_D,    CZ_E,    CZ_F,    CZ_G,\n    // H     I        J        K        L        M        N        O\n    CZ_H,    CZ_I,    CZ_J,    CZ_K,    CZ_L,    CZ_M,    CZ_N,    CZ_O,\n    // P     Q        R        S        T        U        V        W\n    CZ_P,    CZ_Q,    CZ_R,    CZ_S,    CZ_T,    CZ_U,    CZ_V,    CZ_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    CZ_X,    CZ_Y,    CZ_Z,    CZ_UACU, CZ_BSLS, CZ_RPRN, CZ_ZCAR, CZ_MINS,\n    // `     a        b        c        d        e        f        g\n    CZ_DIAE, CZ_A,    CZ_B,    CZ_C,    CZ_D,    CZ_E,    CZ_F,    CZ_G,\n    // h     i        j        k        l        m        n        o\n    CZ_H,    CZ_I,    CZ_J,    CZ_K,    CZ_L,    CZ_M,    CZ_N,    CZ_O,\n    // p     q        r        s        t        u        v        w\n    CZ_P,    CZ_Q,    CZ_R,    CZ_S,    CZ_T,    CZ_U,    CZ_V,    CZ_W,\n    // x     y        z        {        |        }        ~        DEL\n    CZ_X,    CZ_Y,    CZ_Z,    CZ_IACU, CZ_BSLS, CZ_EACU, CZ_RCAR, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_czech_mac_iso.h",
    "content": "/* Copyright 2024 Tabonx\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for macOS Czech ISO layouts\n\n#pragma once\n\n#include \"keymap_czech_mac_iso.h\"\n#include \"send_string.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 0, 1, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0),\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  CZ_SECT, CZ_URNG, CZ_SCAR, CZ_CCAR, CZ_EQL,  CZ_YACU, CZ_SECT,\n    // (     )        *        +        ,        -        .        /\n    CZ_RPRN, CZ_RPRN, CZ_AACU, CZ_PLUS, CZ_COMM, CZ_MINS, CZ_DOT,  CZ_UACU,\n    // 0     1        2        3        4        5        6        7\n    CZ_EACU, CZ_PLUS, CZ_ECAR, CZ_SCAR, CZ_CCAR, CZ_RCAR, CZ_ZCAR, CZ_YACU,\n    // 8     9        :        ;        <        =        >        ?\n    CZ_AACU, CZ_IACU, CZ_DOT,  CZ_URNG, CZ_COMM, CZ_EQL,  CZ_DOT,  CZ_COMM,\n    // @     A        B        C        D        E        F        G\n    CZ_ECAR, CZ_A,    CZ_B,    CZ_C,    CZ_D,    CZ_E,    CZ_F,    CZ_G,\n    // H     I        J        K        L        M        N        O\n    CZ_H,    CZ_I,    CZ_J,    CZ_K,    CZ_L,    CZ_M,    CZ_N,    CZ_O,\n    // P     Q        R        S        T        U        V        W\n    CZ_P,    CZ_Q,    CZ_R,    CZ_S,    CZ_T,    CZ_U,    CZ_V,    CZ_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    CZ_X,    CZ_Y,    CZ_Z,    CZ_UACU, CZ_BSLS, CZ_RPRN, CZ_ZCAR, CZ_MINS,\n    // `     a        b        c        d        e        f        g\n    CZ_DIAE, CZ_A,    CZ_B,    CZ_C,    CZ_D,    CZ_E,    CZ_F,    CZ_G,\n    // h     i        j        k        l        m        n        o\n    CZ_H,    CZ_I,    CZ_J,    CZ_K,    CZ_L,    CZ_M,    CZ_N,    CZ_O,\n    // p     q        r        s        t        u        v        w\n    CZ_P,    CZ_Q,    CZ_R,    CZ_S,    CZ_T,    CZ_U,    CZ_V,    CZ_W,\n    // x     y        z        {        |        }        ~        DEL\n    CZ_X,    CZ_Y,    CZ_Z,    CZ_IACU, CZ_BSLS, CZ_EACU, CZ_RCAR, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_danish.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Danish layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_danish.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 0, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  DK_1,    DK_2,    DK_3,    DK_4,    DK_5,    DK_6,    DK_QUOT,\n    // (     )        *        +        ,        -        .        /\n    DK_8,    DK_9,    DK_QUOT, DK_PLUS, DK_COMM, DK_MINS, DK_DOT,  DK_7,\n    // 0     1        2        3        4        5        6        7\n    DK_0,    DK_1,    DK_2,    DK_3,    DK_4,    DK_5,    DK_6,    DK_7,\n    // 8     9        :        ;        <        =        >        ?\n    DK_8,    DK_9,    DK_DOT,  DK_COMM, DK_LABK, DK_0,    DK_LABK, DK_PLUS,\n    // @     A        B        C        D        E        F        G\n    DK_2,    DK_A,    DK_B,    DK_C,    DK_D,    DK_E,    DK_F,    DK_G,\n    // H     I        J        K        L        M        N        O\n    DK_H,    DK_I,    DK_J,    DK_K,    DK_L,    DK_M,    DK_N,    DK_O,\n    // P     Q        R        S        T        U        V        W\n    DK_P,    DK_Q,    DK_R,    DK_S,    DK_T,    DK_U,    DK_V,    DK_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    DK_X,    DK_Y,    DK_Z,    DK_8,    DK_LABK, DK_9,    DK_DIAE, DK_MINS,\n    // `     a        b        c        d        e        f        g\n    DK_ACUT, DK_A,    DK_B,    DK_C,    DK_D,    DK_E,    DK_F,    DK_G,\n    // h     i        j        k        l        m        n        o\n    DK_H,    DK_I,    DK_J,    DK_K,    DK_L,    DK_M,    DK_N,    DK_O,\n    // p     q        r        s        t        u        v        w\n    DK_P,    DK_Q,    DK_R,    DK_S,    DK_T,    DK_U,    DK_V,    DK_W,\n    // x     y        z        {        |        }        ~        DEL\n    DK_X,    DK_Y,    DK_Z,    DK_7,    DK_ACUT, DK_0,    DK_DIAE, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_dvorak.h",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Dvorak layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_dvorak.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  DV_1,    DV_QUOT, DV_3,    DV_4,    DV_5,    DV_7,    DV_QUOT,\n    // (     )        *        +        ,        -        .        /\n    DV_9,    DV_0,    DV_8,    DV_EQL,  DV_COMM, DV_MINS, DV_DOT,  DV_SLSH,\n    // 0     1        2        3        4        5        6        7\n    DV_0,    DV_1,    DV_2,    DV_3,    DV_4,    DV_5,    DV_6,    DV_7,\n    // 8     9        :        ;        <        =        >        ?\n    DV_8,    DV_9,    DV_SCLN, DV_SCLN, DV_COMM, DV_EQL,  DV_DOT,  DV_SLSH,\n    // @     A        B        C        D        E        F        G\n    DV_2,    DV_A,    DV_B,    DV_C,    DV_D,    DV_E,    DV_F,    DV_G,\n    // H     I        J        K        L        M        N        O\n    DV_H,    DV_I,    DV_J,    DV_K,    DV_L,    DV_M,    DV_N,    DV_O,\n    // P     Q        R        S        T        U        V        W\n    DV_P,    DV_Q,    DV_R,    DV_S,    DV_T,    DV_U,    DV_V,    DV_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    DV_X,    DV_Y,    DV_Z,    DV_LBRC, DV_BSLS, DV_RBRC, DV_6,    DV_MINS,\n    // `     a        b        c        d        e        f        g\n    DV_GRV,  DV_A,    DV_B,    DV_C,    DV_D,    DV_E,    DV_F,    DV_G,\n    // h     i        j        k        l        m        n        o\n    DV_H,    DV_I,    DV_J,    DV_K,    DV_L,    DV_M,    DV_N,    DV_O,\n    // p     q        r        s        t        u        v        w\n    DV_P,    DV_Q,    DV_R,    DV_S,    DV_T,    DV_U,    DV_V,    DV_W,\n    // x     y        z        {        |        }        ~        DEL\n    DV_X,    DV_Y,    DV_Z,    DV_LBRC, DV_BSLS, DV_RBRC, DV_GRV,  KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_dvorak_fr.h",
    "content": "/* Copyright 2020 Guillaume Gérard\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Dvorak French layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_dvorak_fr.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 0, 1, 0, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 0, 0, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0),\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  DV_DOT,  XXXXXXX, DV_TILD, XXXXXXX, DV_RBRC, XXXXXXX, DV_QUOT,\n    // (     )        *        +        ,        -        .        /\n    DV_LPRN, DV_RPRN, DV_LDAQ, DV_LBRC, DV_COMM, DV_MINS, DV_DOT,  DV_SLSH,\n    // 0     1        2        3        4        5        6        7\n    DV_UNDS, DV_RDAQ, DV_SLSH, DV_MINS, DV_EGRV, DV_BSLS, DV_CIRC, DV_LPRN,\n    // 8     9        :        ;        <        =        >        ?\n    DV_GRV,  DV_RPRN, DV_COLN, DV_SCLN, DV_QUOT, DV_DIAE, DV_EACU, DV_COLN,\n    // @     A        B        C        D        E        F        G\n    DV_COMM, DV_A,    DV_B,    DV_C,    DV_D,    DV_E,    DV_F,    DV_G,\n    // H     I        J        K        L        M        N        O\n    DV_H,    DV_I,    DV_J,    DV_K,    DV_L,    DV_M,    DV_N,    DV_O,\n    // P     Q        R        S        T        U        V        W\n    DV_P,    DV_Q,    DV_R,    DV_S,    DV_T,    DV_U,    DV_V,    DV_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    DV_X,    DV_Y,    DV_Z,    DV_LBRC, DV_BSLS, DV_RBRC, DV_CIRC, DV_UNDS,\n    // `     a        b        c        d        e        f        g\n    DV_GRV,  DV_A,    DV_B,    DV_C,    DV_D,    DV_E,    DV_F,    DV_G,\n    // h     i        j        k        l        m        n        o\n    DV_H,    DV_I,    DV_J,    DV_K,    DV_L,    DV_M,    DV_N,    DV_O,\n    // p     q        r        s        t        u        v        w\n    DV_P,    DV_Q,    DV_R,    DV_S,    DV_T,    DV_U,    DV_V,    DV_W,\n    // x     y        z        {        |        }        ~        DEL\n    DV_X,    DV_Y,    DV_Z,    XXXXXXX, DV_SCLN, XXXXXXX, DV_TILD, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_dvorak_programmer.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Programmer Dvorak layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_dvorak_programmer.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 1, 0, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  DP_EXLM, DP_QUOT, DP_HASH, DP_DLR,  DP_AMPR, DP_AMPR, DP_QUOT,\n    // (     )        *        +        ,        -        .        /\n    DP_LPRN, DP_RPRN, DP_ASTR, DP_PLUS, DP_COMM, DP_MINS, DP_DOT,  DP_SLSH,\n    // 0     1        2        3        4        5        6        7\n    DP_ASTR, DP_LPRN, DP_RPRN, DP_RCBR, DP_PLUS, DP_LCBR, DP_RBRC, DP_LBRC,\n    // 8     9        :        ;        <        =        >        ?\n    DP_EXLM, DP_EQL,  DP_SCLN, DP_SCLN, DP_COMM, DP_EQL,  DP_DOT,  DP_SLSH,\n    // @     A        B        C        D        E        F        G\n    DP_AT,   DP_A,    DP_B,    DP_C,    DP_D,    DP_E,    DP_F,    DP_G,\n    // H     I        J        K        L        M        N        O\n    DP_H,    DP_I,    DP_J,    DP_K,    DP_L,    DP_M,    DP_N,    DP_O,\n    // P     Q        R        S        T        U        V        W\n    DP_P,    DP_Q,    DP_R,    DP_S,    DP_T,    DP_U,    DP_V,    DP_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    DP_X,    DP_Y,    DP_Z,    DP_LBRC, DP_BSLS, DP_RBRC, DP_AT,   DP_MINS,\n    // `     a        b        c        d        e        f        g\n    DP_HASH, DP_A,    DP_B,    DP_C,    DP_D,    DP_E,    DP_F,    DP_G,\n    // h     i        j        k        l        m        n        o\n    DP_H,    DP_I,    DP_J,    DP_K,    DP_L,    DP_M,    DP_N,    DP_O,\n    // p     q        r        s        t        u        v        w\n    DP_P,    DP_Q,    DP_R,    DP_S,    DP_T,    DP_U,    DP_V,    DP_W,\n    // x     y        z        {        |        }        ~        DEL\n    DP_X,    DP_Y,    DP_Z,    DP_LCBR, DP_BSLS, DP_RCBR, DP_DLR,  KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_estonian.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Estonian layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_estonian.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 0, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  EE_1,    EE_2,    EE_3,    EE_4,    EE_5,    EE_6,    EE_QUOT,\n    // (     )        *        +        ,        -        .        /\n    EE_8,    EE_9,    EE_QUOT, EE_PLUS, EE_COMM, EE_MINS, EE_DOT,  EE_7,\n    // 0     1        2        3        4        5        6        7\n    EE_0,    EE_1,    EE_2,    EE_3,    EE_4,    EE_5,    EE_6,    EE_7,\n    // 8     9        :        ;        <        =        >        ?\n    EE_8,    EE_9,    EE_DOT,  EE_COMM, EE_LABK, EE_0,    EE_LABK, EE_PLUS,\n    // @     A        B        C        D        E        F        G\n    EE_2,    EE_A,    EE_B,    EE_C,    EE_D,    EE_E,    EE_F,    EE_G,\n    // H     I        J        K        L        M        N        O\n    EE_H,    EE_I,    EE_J,    EE_K,    EE_L,    EE_M,    EE_N,    EE_O,\n    // P     Q        R        S        T        U        V        W\n    EE_P,    EE_Q,    EE_R,    EE_S,    EE_T,    EE_U,    EE_V,    EE_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    EE_X,    EE_Y,    EE_Z,    EE_8,    EE_PLUS, EE_9,    EE_ADIA, EE_MINS,\n    // `     a        b        c        d        e        f        g\n    EE_ACUT, EE_A,    EE_B,    EE_C,    EE_D,    EE_E,    EE_F,    EE_G,\n    // h     i        j        k        l        m        n        o\n    EE_H,    EE_I,    EE_J,    EE_K,    EE_L,    EE_M,    EE_N,    EE_O,\n    // p     q        r        s        t        u        v        w\n    EE_P,    EE_Q,    EE_R,    EE_S,    EE_T,    EE_U,    EE_V,    EE_W,\n    // x     y        z        {        |        }        ~        DEL\n    EE_X,    EE_Y,    EE_Z,    EE_7,    EE_LABK, EE_0,    EE_CARN, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_finnish.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Finnish layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_finnish.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 0, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  FI_1,    FI_2,    FI_3,    FI_4,    FI_5,    FI_6,    FI_QUOT,\n    // (     )        *        +        ,        -        .        /\n    FI_8,    FI_9,    FI_QUOT, FI_PLUS, FI_COMM, FI_MINS, FI_DOT,  FI_7,\n    // 0     1        2        3        4        5        6        7\n    FI_0,    FI_1,    FI_2,    FI_3,    FI_4,    FI_5,    FI_6,    FI_7,\n    // 8     9        :        ;        <        =        >        ?\n    FI_8,    FI_9,    FI_DOT,  FI_COMM, FI_LABK, FI_0,    FI_LABK, FI_PLUS,\n    // @     A        B        C        D        E        F        G\n    FI_2,    FI_A,    FI_B,    FI_C,    FI_D,    FI_E,    FI_F,    FI_G,\n    // H     I        J        K        L        M        N        O\n    FI_H,    FI_I,    FI_J,    FI_K,    FI_L,    FI_M,    FI_N,    FI_O,\n    // P     Q        R        S        T        U        V        W\n    FI_P,    FI_Q,    FI_R,    FI_S,    FI_T,    FI_U,    FI_V,    FI_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    FI_X,    FI_Y,    FI_Z,    FI_8,    FI_PLUS, FI_9,    FI_DIAE, FI_MINS,\n    // `     a        b        c        d        e        f        g\n    FI_ACUT, FI_A,    FI_B,    FI_C,    FI_D,    FI_E,    FI_F,    FI_G,\n    // h     i        j        k        l        m        n        o\n    FI_H,    FI_I,    FI_J,    FI_K,    FI_L,    FI_M,    FI_N,    FI_O,\n    // p     q        r        s        t        u        v        w\n    FI_P,    FI_Q,    FI_R,    FI_S,    FI_T,    FI_U,    FI_V,    FI_W,\n    // x     y        z        {        |        }        ~        DEL\n    FI_X,    FI_Y,    FI_Z,    FI_7,    FI_LABK, FI_0,    FI_DIAE, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_french.h",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for French (AZERTY) layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_french.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 0, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 0, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  FR_EXLM, FR_DQUO, FR_DQUO, FR_DLR,  FR_UGRV, FR_AMPR, FR_QUOT,\n    // (     )        *        +        ,        -        .        /\n    FR_LPRN, FR_RPRN, FR_ASTR, FR_EQL,  FR_COMM, FR_MINS, FR_SCLN, FR_COLN,\n    // 0     1        2        3        4        5        6        7\n    FR_AGRV, FR_AMPR, FR_EACU, FR_DQUO, FR_QUOT, FR_LPRN, FR_MINS, FR_EGRV,\n    // 8     9        :        ;        <        =        >        ?\n    FR_UNDS, FR_CCED, FR_COLN, FR_SCLN, FR_LABK, FR_EQL,  FR_LABK, FR_COMM,\n    // @     A        B        C        D        E        F        G\n    FR_AGRV, FR_A,    FR_B,    FR_C,    FR_D,    FR_E,    FR_F,    FR_G,\n    // H     I        J        K        L        M        N        O\n    FR_H,    FR_I,    FR_J,    FR_K,    FR_L,    FR_M,    FR_N,    FR_O,\n    // P     Q        R        S        T        U        V        W\n    FR_P,    FR_Q,    FR_R,    FR_S,    FR_T,    FR_U,    FR_V,    FR_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    FR_X,    FR_Y,    FR_Z,    FR_LPRN, FR_UNDS, FR_RPRN, FR_CIRC, FR_UNDS,\n    // `     a        b        c        d        e        f        g\n    FR_EGRV, FR_A,    FR_B,    FR_C,    FR_D,    FR_E,    FR_F,    FR_G,\n    // h     i        j        k        l        m        n        o\n    FR_H,    FR_I,    FR_J,    FR_K,    FR_L,    FR_M,    FR_N,    FR_O,\n    // p     q        r        s        t        u        v        w\n    FR_P,    FR_Q,    FR_R,    FR_S,    FR_T,    FR_U,    FR_V,    FR_W,\n    // x     y        z        {        |        }        ~        DEL\n    FR_X,    FR_Y,    FR_Z,    FR_QUOT, FR_MINS, FR_EQL,  FR_EACU, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_french_afnor.h",
    "content": "/* Copyright 2020 Guillaume Gérard\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for French (AZERTY - AFNOR NF Z71-300) layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_french_afnor.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 0, 0, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 1, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 1, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  FR_COMM, FR_QUOT, FR_AT,   FR_D,    FR_P,    FR_ECIR, FR_QUOT,\n    // (     )        *        +        ,        -        .        /\n    FR_LPRN, FR_RPRN, FR_ASTR, FR_PLUS, FR_COMM, FR_MINS, FR_DOT,  FR_SLSH,\n    // 0     1        2        3        4        5        6        7\n    FR_RDAQ, FR_AGRV, FR_EACU, FR_EGRV, FR_ECIR, FR_LPRN, FR_RPRN, FR_LSQU,\n    // 8     9        :        ;        <        =        >        ?\n    FR_RSQU, FR_LDAQ, FR_COLN, FR_SCLN, FR_LABK, FR_SCLN, FR_LABK, FR_DOT,\n    // @     A        B        C        D        E        F        G\n    FR_AT,   FR_A,    FR_B,    FR_C,    FR_D,    FR_E,    FR_F,    FR_G,\n    // H     I        J        K        L        M        N        O\n    FR_H,    FR_I,    FR_J,    FR_K,    FR_L,    FR_M,    FR_N,    FR_O,\n    // P     Q        R        S        T        U        V        W\n    FR_P,    FR_Q,    FR_R,    FR_S,    FR_T,    FR_U,    FR_V,    FR_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    FR_X,    FR_Y,    FR_Z,    FR_LPRN, FR_SLSH, FR_RPRN, FR_DCIR, FR_RSQU,\n    // `     a        b        c        d        e        f        g\n    FR_EGRV, FR_A,    FR_B,    FR_C,    FR_D,    FR_E,    FR_F,    FR_G,\n    // h     i        j        k        l        m        n        o\n    FR_H,    FR_I,    FR_J,    FR_K,    FR_L,    FR_M,    FR_N,    FR_O,\n    // p     q        r        s        t        u        v        w\n    FR_P,    FR_Q,    FR_R,    FR_S,    FR_T,    FR_U,    FR_V,    FR_W,\n    // x     y        z        {        |        }        ~        DEL\n    FR_X,    FR_Y,    FR_Z,    FR_T,    FR_L,    FR_Y,    FR_N,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_french_mac_iso.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for macOS French (AZERTY) layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_french_mac_iso.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 0, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 0, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  FR_EXLM, FR_DQUO, FR_AT,   FR_DLR,  FR_LUGR, FR_AMPR, FR_QUOT,\n    // (     )        *        +        ,        -        .        /\n    FR_LPRN, FR_RPRN, FR_DLR,  FR_EQL,  FR_COMM, FR_MINS, FR_SCLN, FR_COLN,\n    // 0     1        2        3        4        5        6        7\n    FR_LAGR, FR_AMPR, FR_LEAC, FR_DQUO, FR_QUOT, FR_LPRN, FR_SECT, FR_LEGR,\n    // 8     9        :        ;        <        =        >        ?\n    FR_EXLM, FR_LCCE, FR_COLN, FR_SCLN, FR_LABK, FR_EQL,  FR_LABK, FR_COMM,\n    // @     A        B        C        D        E        F        G\n    FR_AT,   FR_A,    FR_B,    FR_C,    FR_D,    FR_E,    FR_F,    FR_G,\n    // H     I        J        K        L        M        N        O\n    FR_H,    FR_I,    FR_J,    FR_K,    FR_L,    FR_M,    FR_N,    FR_O,\n    // P     Q        R        S        T        U        V        W\n    FR_P,    FR_Q,    FR_R,    FR_S,    FR_T,    FR_U,    FR_V,    FR_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    FR_X,    FR_Y,    FR_Z,    FR_LPRN, FR_COLN, FR_RPRN, FR_CIRC, FR_MINS,\n    // `     a        b        c        d        e        f        g\n    FR_GRV,  FR_A,    FR_B,    FR_C,    FR_D,    FR_E,    FR_F,    FR_G,\n    // h     i        j        k        l        m        n        o\n    FR_H,    FR_I,    FR_J,    FR_K,    FR_L,    FR_M,    FR_N,    FR_O,\n    // p     q        r        s        t        u        v        w\n    FR_P,    FR_Q,    FR_R,    FR_S,    FR_T,    FR_U,    FR_V,    FR_W,\n    // x     y        z        {        |        }        ~        DEL\n    FR_X,    FR_Y,    FR_Z,    FR_LPRN, FR_L,    FR_RPRN, FR_N,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_german.h",
    "content": "/* Copyright 2018 Patrick Hener\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for German layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_german.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  DE_1,    DE_2,    DE_HASH, DE_4,    DE_5,    DE_6,    DE_HASH,\n    // (     )        *        +        ,        -        .        /\n    DE_8,    DE_9,    DE_PLUS, DE_PLUS, DE_COMM, DE_MINS, DE_DOT,  DE_7,\n    // 0     1        2        3        4        5        6        7\n    DE_0,    DE_1,    DE_2,    DE_3,    DE_4,    DE_5,    DE_6,    DE_7,\n    // 8     9        :        ;        <        =        >        ?\n    DE_8,    DE_9,    DE_DOT,  DE_COMM, DE_LABK, DE_0,    DE_LABK, DE_SS,\n    // @     A        B        C        D        E        F        G\n    DE_Q,    DE_A,    DE_B,    DE_C,    DE_D,    DE_E,    DE_F,    DE_G,\n    // H     I        J        K        L        M        N        O\n    DE_H,    DE_I,    DE_J,    DE_K,    DE_L,    DE_M,    DE_N,    DE_O,\n    // P     Q        R        S        T        U        V        W\n    DE_P,    DE_Q,    DE_R,    DE_S,    DE_T,    DE_U,    DE_V,    DE_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    DE_X,    DE_Y,    DE_Z,    DE_8,    DE_SS,   DE_9,    DE_CIRC, DE_MINS,\n    // `     a        b        c        d        e        f        g\n    DE_ACUT, DE_A,    DE_B,    DE_C,    DE_D,    DE_E,    DE_F,    DE_G,\n    // h     i        j        k        l        m        n        o\n    DE_H,    DE_I,    DE_J,    DE_K,    DE_L,    DE_M,    DE_N,    DE_O,\n    // p     q        r        s        t        u        v        w\n    DE_P,    DE_Q,    DE_R,    DE_S,    DE_T,    DE_U,    DE_V,    DE_W,\n    // x     y        z        {        |        }        ~        DEL\n    DE_X,    DE_Y,    DE_Z,    DE_7,    DE_LABK, DE_0,    DE_PLUS, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_german_mac_iso.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for macOS German layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_german_mac_iso.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 1, 0, 0, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  DE_1,    DE_2,    DE_HASH, DE_4,    DE_5,    DE_6,    DE_HASH,\n    // (     )        *        +        ,        -        .        /\n    DE_8,    DE_9,    DE_PLUS, DE_PLUS, DE_COMM, DE_MINS, DE_DOT,  DE_7,\n    // 0     1        2        3        4        5        6        7\n    DE_0,    DE_1,    DE_2,    DE_3,    DE_4,    DE_5,    DE_6,    DE_7,\n    // 8     9        :        ;        <        =        >        ?\n    DE_8,    DE_9,    DE_DOT,  DE_COMM, DE_LABK, DE_0,    DE_LABK, DE_SS,\n    // @     A        B        C        D        E        F        G\n    DE_L,    DE_A,    DE_B,    DE_C,    DE_D,    DE_E,    DE_F,    DE_G,\n    // H     I        J        K        L        M        N        O\n    DE_H,    DE_I,    DE_J,    DE_K,    DE_L,    DE_M,    DE_N,    DE_O,\n    // P     Q        R        S        T        U        V        W\n    DE_P,    DE_Q,    DE_R,    DE_S,    DE_T,    DE_U,    DE_V,    DE_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    DE_X,    DE_Y,    DE_Z,    DE_5,    DE_7,    DE_6,    DE_CIRC, DE_MINS,\n    // `     a        b        c        d        e        f        g\n    DE_ACUT, DE_A,    DE_B,    DE_C,    DE_D,    DE_E,    DE_F,    DE_G,\n    // h     i        j        k        l        m        n        o\n    DE_H,    DE_I,    DE_J,    DE_K,    DE_L,    DE_M,    DE_N,    DE_O,\n    // p     q        r        s        t        u        v        w\n    DE_P,    DE_Q,    DE_R,    DE_S,    DE_T,    DE_U,    DE_V,    DE_W,\n    // x     y        z        {        |        }        ~        DEL\n    DE_X,    DE_Y,    DE_Z,    DE_8,    DE_7,    DE_9,    DE_N,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_hungarian.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Hungarian layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_hungarian.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 0, 1, 0, 1),\n    KCLUT_ENTRY(1, 1, 0, 1, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 1, 0, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  HU_4,    HU_2,    HU_X,    HU_EACU, HU_5,    HU_C,    HU_1,\n    // (     )        *        +        ,        -        .        /\n    HU_8,    HU_9,    HU_MINS, HU_3,    HU_COMM, HU_MINS, HU_DOT,  HU_6,\n    // 0     1        2        3        4        5        6        7\n    HU_0,    HU_1,    HU_2,    HU_3,    HU_4,    HU_5,    HU_6,    HU_7,\n    // 8     9        :        ;        <        =        >        ?\n    HU_8,    HU_9,    HU_DOT,  HU_COMM, HU_M,    HU_7,    HU_DOT,  HU_COMM,\n    // @     A        B        C        D        E        F        G\n    HU_V,    HU_A,    HU_B,    HU_C,    HU_D,    HU_E,    HU_F,    HU_G,\n    // H     I        J        K        L        M        N        O\n    HU_H,    HU_I,    HU_J,    HU_K,    HU_L,    HU_M,    HU_N,    HU_O,\n    // P     Q        R        S        T        U        V        W\n    HU_P,    HU_Q,    HU_R,    HU_S,    HU_T,    HU_U,    HU_V,    HU_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    HU_X,    HU_Y,    HU_Z,    HU_F,    HU_Q,    HU_G,    HU_3,    HU_MINS,\n    // `     a        b        c        d        e        f        g\n    HU_7,    HU_A,    HU_B,    HU_C,    HU_D,    HU_E,    HU_F,    HU_G,\n    // h     i        j        k        l        m        n        o\n    HU_H,    HU_I,    HU_J,    HU_K,    HU_L,    HU_M,    HU_N,    HU_O,\n    // p     q        r        s        t        u        v        w\n    HU_P,    HU_Q,    HU_R,    HU_S,    HU_T,    HU_U,    HU_V,    HU_W,\n    // x     y        z        {        |        }        ~        DEL\n    HU_X,    HU_Y,    HU_Z,    HU_B,    HU_W,    HU_N,    HU_1,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_icelandic.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Icelandic layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_icelandic.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  IS_1,    IS_2,    IS_3,    IS_4,    IS_5,    IS_6,    IS_QUOT,\n    // (     )        *        +        ,        -        .        /\n    IS_8,    IS_9,    IS_PLUS, IS_PLUS, IS_COMM, IS_MINS, IS_DOT,  IS_7,\n    // 0     1        2        3        4        5        6        7\n    IS_0,    IS_1,    IS_2,    IS_3,    IS_4,    IS_5,    IS_6,    IS_7,\n    // 8     9        :        ;        <        =        >        ?\n    IS_8,    IS_9,    IS_DOT,  IS_COMM, IS_LABK, IS_0,    IS_LABK, IS_QUOT,\n    // @     A        B        C        D        E        F        G\n    IS_Q,    IS_A,    IS_B,    IS_C,    IS_D,    IS_E,    IS_F,    IS_G,\n    // H     I        J        K        L        M        N        O\n    IS_H,    IS_I,    IS_J,    IS_K,    IS_L,    IS_M,    IS_N,    IS_O,\n    // P     Q        R        S        T        U        V        W\n    IS_P,    IS_Q,    IS_R,    IS_S,    IS_T,    IS_U,    IS_V,    IS_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    IS_X,    IS_Y,    IS_Z,    IS_8,    IS_ODIA, IS_9,    IS_ACUT, IS_MINS,\n    // `     a        b        c        d        e        f        g\n    IS_PLUS, IS_A,    IS_B,    IS_C,    IS_D,    IS_E,    IS_F,    IS_G,\n    // h     i        j        k        l        m        n        o\n    IS_H,    IS_I,    IS_J,    IS_K,    IS_L,    IS_M,    IS_N,    IS_O,\n    // p     q        r        s        t        u        v        w\n    IS_P,    IS_Q,    IS_R,    IS_S,    IS_T,    IS_U,    IS_V,    IS_W,\n    // x     y        z        {        |        }        ~        DEL\n    IS_X,    IS_Y,    IS_Z,    IS_7,    IS_LABK, IS_0,    IS_QUOT, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_italian.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Italian layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_italian.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  IT_1,    IT_2,    IT_AGRV, IT_4,    IT_5,    IT_6,    IT_QUOT,\n    // (     )        *        +        ,        -        .        /\n    IT_8,    IT_9,    IT_PLUS, IT_PLUS, IT_COMM, IT_MINS, IT_DOT,  IT_7,\n    // 0     1        2        3        4        5        6        7\n    IT_0,    IT_1,    IT_2,    IT_3,    IT_4,    IT_5,    IT_6,    IT_7,\n    // 8     9        :        ;        <        =        >        ?\n    IT_8,    IT_9,    IT_DOT,  IT_COMM, IT_LABK, IT_0,    IT_LABK, IT_QUOT,\n    // @     A        B        C        D        E        F        G\n    IT_OGRV, IT_A,    IT_B,    IT_C,    IT_D,    IT_E,    IT_F,    IT_G,\n    // H     I        J        K        L        M        N        O\n    IT_H,    IT_I,    IT_J,    IT_K,    IT_L,    IT_M,    IT_N,    IT_O,\n    // P     Q        R        S        T        U        V        W\n    IT_P,    IT_Q,    IT_R,    IT_S,    IT_T,    IT_U,    IT_V,    IT_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    IT_X,    IT_Y,    IT_Z,    IT_EGRV, IT_BSLS, IT_PLUS, IT_IGRV, IT_MINS,\n    // `     a        b        c        d        e        f        g\n    XXXXXXX, IT_A,    IT_B,    IT_C,    IT_D,    IT_E,    IT_F,    IT_G,\n    // h     i        j        k        l        m        n        o\n    IT_H,    IT_I,    IT_J,    IT_K,    IT_L,    IT_M,    IT_N,    IT_O,\n    // p     q        r        s        t        u        v        w\n    IT_P,    IT_Q,    IT_R,    IT_S,    IT_T,    IT_U,    IT_V,    IT_W,\n    // x     y        z        {        |        }        ~        DEL\n    IT_X,    IT_Y,    IT_Z,    IT_EGRV, IT_BSLS, IT_PLUS, XXXXXXX, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_italian_mac_ansi.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for macOS Italian ANSI layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_italian_mac_ansi.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  IT_1,    IT_2,    IT_AGRV, IT_4,    IT_5,    IT_6,    IT_QUOT,\n    // (     )        *        +        ,        -        .        /\n    IT_8,    IT_9,    IT_PLUS, IT_PLUS, IT_COMM, IT_MINS, IT_DOT,  IT_7,\n    // 0     1        2        3        4        5        6        7\n    IT_0,    IT_1,    IT_2,    IT_3,    IT_4,    IT_5,    IT_6,    IT_7,\n    // 8     9        :        ;        <        =        >        ?\n    IT_8,    IT_9,    IT_DOT,  IT_COMM, IT_LABK, IT_0,    IT_LABK, IT_QUOT,\n    // @     A        B        C        D        E        F        G\n    IT_OGRV, IT_A,    IT_B,    IT_C,    IT_D,    IT_E,    IT_F,    IT_G,\n    // H     I        J        K        L        M        N        O\n    IT_H,    IT_I,    IT_J,    IT_K,    IT_L,    IT_M,    IT_N,    IT_O,\n    // P     Q        R        S        T        U        V        W\n    IT_P,    IT_Q,    IT_R,    IT_S,    IT_T,    IT_U,    IT_V,    IT_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    IT_X,    IT_Y,    IT_Z,    IT_EGRV, IT_BSLS, IT_PLUS, IT_IGRV, IT_MINS,\n    // `     a        b        c        d        e        f        g\n    IT_BSLS, IT_A,    IT_B,    IT_C,    IT_D,    IT_E,    IT_F,    IT_G,\n    // h     i        j        k        l        m        n        o\n    IT_H,    IT_I,    IT_J,    IT_K,    IT_L,    IT_M,    IT_N,    IT_O,\n    // p     q        r        s        t        u        v        w\n    IT_P,    IT_Q,    IT_R,    IT_S,    IT_T,    IT_U,    IT_V,    IT_W,\n    // x     y        z        {        |        }        ~        DEL\n    IT_X,    IT_Y,    IT_Z,    IT_EGRV, IT_BSLS, IT_PLUS, IT_5,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_italian_mac_iso.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for macOS Italian ISO layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_italian_mac_iso.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  IT_1,    IT_2,    IT_AGRV, IT_4,    IT_5,    IT_6,    IT_QUOT,\n    // (     )        *        +        ,        -        .        /\n    IT_8,    IT_9,    IT_PLUS, IT_PLUS, IT_COMM, IT_MINS, IT_DOT,  IT_7,\n    // 0     1        2        3        4        5        6        7\n    IT_0,    IT_1,    IT_2,    IT_3,    IT_4,    IT_5,    IT_6,    IT_7,\n    // 8     9        :        ;        <        =        >        ?\n    IT_8,    IT_9,    IT_DOT,  IT_COMM, IT_LABK, IT_0,    IT_LABK, IT_QUOT,\n    // @     A        B        C        D        E        F        G\n    IT_OGRV, IT_A,    IT_B,    IT_C,    IT_D,    IT_E,    IT_F,    IT_G,\n    // H     I        J        K        L        M        N        O\n    IT_H,    IT_I,    IT_J,    IT_K,    IT_L,    IT_M,    IT_N,    IT_O,\n    // P     Q        R        S        T        U        V        W\n    IT_P,    IT_Q,    IT_R,    IT_S,    IT_T,    IT_U,    IT_V,    IT_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    IT_X,    IT_Y,    IT_Z,    IT_EGRV, IT_BSLS, IT_PLUS, IT_IGRV, IT_MINS,\n    // `     a        b        c        d        e        f        g\n    IT_BSLS, IT_A,    IT_B,    IT_C,    IT_D,    IT_E,    IT_F,    IT_G,\n    // h     i        j        k        l        m        n        o\n    IT_H,    IT_I,    IT_J,    IT_K,    IT_L,    IT_M,    IT_N,    IT_O,\n    // p     q        r        s        t        u        v        w\n    IT_P,    IT_Q,    IT_R,    IT_S,    IT_T,    IT_U,    IT_V,    IT_W,\n    // x     y        z        {        |        }        ~        DEL\n    IT_X,    IT_Y,    IT_Z,    IT_EGRV, IT_BSLS, IT_PLUS, IT_5,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_japanese.h",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for JIS layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_japanese.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  KC_1,    KC_2,    KC_3,    KC_4,    KC_5,    KC_6,    KC_7,\n    // (     )        *        +        ,        -        .        /\n    KC_8,    KC_9,    JP_COLN, JP_SCLN, JP_COMM, JP_MINS, JP_DOT,  JP_SLSH,\n    // 0     1        2        3        4        5        6        7\n    KC_0,    KC_1,    KC_2,    KC_3,    KC_4,    KC_5,    KC_6,    KC_7,\n    // 8     9        :        ;        <        =        >        ?\n    KC_8,    KC_9,    JP_COLN, JP_SCLN, JP_COMM, JP_MINS, JP_DOT,  JP_SLSH,\n    // @     A        B        C        D        E        F        G\n    JP_AT,   KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,\n    // H     I        J        K        L        M        N        O\n    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,    KC_M,    KC_N,    KC_O,\n    // P     Q        R        S        T        U        V        W\n    KC_P,    KC_Q,    KC_R,    KC_S,    KC_T,    KC_U,    KC_V,    KC_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    KC_X,    KC_Y,    KC_Z,    JP_LBRC, JP_BSLS, JP_RBRC, JP_CIRC, JP_BSLS,\n    // `     a        b        c        d        e        f        g\n    JP_AT,   KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,\n    // h     i        j        k        l        m        n        o\n    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,    KC_M,    KC_N,    KC_O,\n    // p     q        r        s        t        u        v        w\n    KC_P,    KC_Q,    KC_R,    KC_S,    KC_T,    KC_U,    KC_V,    KC_W,\n    // x     y        z        {        |        }        ~        DEL\n    KC_X,    KC_Y,    KC_Z,    JP_LBRC, JP_YEN,  JP_RBRC, JP_CIRC, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_latvian.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Latvian layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_latvian.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  LV_1,    LV_QUOT, LV_3,    LV_4,    LV_5,    LV_7,    LV_QUOT,\n    // (     )        *        +        ,        -        .        /\n    LV_9,    LV_0,    LV_8,    LV_EQL,  LV_COMM, LV_MINS, LV_DOT,  LV_SLSH,\n    // 0     1        2        3        4        5        6        7\n    LV_0,    LV_1,    LV_2,    LV_3,    LV_4,    LV_5,    LV_6,    LV_7,\n    // 8     9        :        ;        <        =        >        ?\n    LV_8,    LV_9,    LV_SCLN, LV_SCLN, LV_COMM, LV_EQL,  LV_DOT,  LV_SLSH,\n    // @     A        B        C        D        E        F        G\n    LV_2,    LV_A,    LV_B,    LV_C,    LV_D,    LV_E,    LV_F,    LV_G,\n    // H     I        J        K        L        M        N        O\n    LV_H,    LV_I,    LV_J,    LV_K,    LV_L,    LV_M,    LV_N,    LV_O,\n    // P     Q        R        S        T        U        V        W\n    LV_P,    LV_Q,    LV_R,    LV_S,    LV_T,    LV_U,    LV_V,    LV_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    LV_X,    LV_Y,    LV_Z,    LV_LBRC, LV_BSLS, LV_RBRC, LV_6,    LV_MINS,\n    // `     a        b        c        d        e        f        g\n    LV_GRV,  LV_A,    LV_B,    LV_C,    LV_D,    LV_E,    LV_F,    LV_G,\n    // h     i        j        k        l        m        n        o\n    LV_H,    LV_I,    LV_J,    LV_K,    LV_L,    LV_M,    LV_N,    LV_O,\n    // p     q        r        s        t        u        v        w\n    LV_P,    LV_Q,    LV_R,    LV_S,    LV_T,    LV_U,    LV_V,    LV_W,\n    // x     y        z        {        |        }        ~        DEL\n    LV_X,    LV_Y,    LV_Z,    LV_LBRC, LV_BSLS, LV_RBRC, LV_GRV,  KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_lithuanian_azerty.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Lithuanian ĄŽERTY layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_lithuanian_azerty.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  LT_EXLM, LT_EDOT, LT_SLSH, LT_SCLN, LT_X,    LT_DOT,  LT_QUES,\n    // (     )        *        +        ,        -        .        /\n    LT_LPRN, LT_RPRN, LT_EQL,  LT_QUES, LT_COMM, LT_MINS, LT_DOT,  LT_SLSH,\n    // 0     1        2        3        4        5        6        7\n    LT_RPRN, LT_EXLM, LT_MINS, LT_SLSH, LT_SCLN, LT_COLN, LT_COMM, LT_DOT,\n    // 8     9        :        ;        <        =        >        ?\n    LT_EQL,  LT_LPRN, LT_COLN, LT_SCLN, LT_LABK, LT_EQL,  LT_LABK, LT_QUES,\n    // @     A        B        C        D        E        F        G\n    LT_EXLM, LT_A,    LT_B,    LT_C,    LT_D,    LT_E,    LT_F,    LT_G,\n    // H     I        J        K        L        M        N        O\n    LT_H,    LT_I,    LT_J,    LT_K,    LT_L,    LT_M,    LT_N,    LT_O,\n    // P     Q        R        S        T        U        V        W\n    LT_P,    LT_Q,    LT_R,    LT_S,    LT_T,    LT_U,    LT_V,    LT_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    LT_X,    LT_Y,    LT_Z,    LT_LPRN, LT_EOGO, LT_RPRN, LT_COMM, LT_MINS,\n    // `     a        b        c        d        e        f        g\n    LT_GRV,  LT_A,    LT_B,    LT_C,    LT_D,    LT_E,    LT_F,    LT_G,\n    // h     i        j        k        l        m        n        o\n    LT_H,    LT_I,    LT_J,    LT_K,    LT_L,    LT_M,    LT_N,    LT_O,\n    // p     q        r        s        t        u        v        w\n    LT_P,    LT_Q,    LT_R,    LT_S,    LT_T,    LT_U,    LT_V,    LT_W,\n    // x     y        z        {        |        }        ~        DEL\n    LT_X,    LT_Y,    LT_Z,    LT_IOGO, LT_Q,    LT_W,    LT_GRV,  KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_lithuanian_qwerty.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Lithuanian QWERTY layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_lithuanian_qwerty.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 0, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 1, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  LT_1,    LT_QUOT, LT_3,    LT_4,    LT_5,    LT_7,    LT_QUOT,\n    // (     )        *        +        ,        -        .        /\n    LT_9,    LT_0,    LT_8,    LT_ZCAR, LT_COMM, LT_MINS, LT_DOT,  LT_SLSH,\n    // 0     1        2        3        4        5        6        7\n    LT_0,    LT_AOGO, LT_CCAR, LT_EOGO, LT_EDOT, LT_IOGO, LT_SCAR, LT_UOGO,\n    // 8     9        :        ;        <        =        >        ?\n    LT_UMAC, LT_9,    LT_SCLN, LT_SCLN, LT_COMM, LT_PLUS, LT_DOT,  LT_SLSH,\n    // @     A        B        C        D        E        F        G\n    LT_CCAR, LT_A,    LT_B,    LT_C,    LT_D,    LT_E,    LT_F,    LT_G,\n    // H     I        J        K        L        M        N        O\n    LT_H,    LT_I,    LT_J,    LT_K,    LT_L,    LT_M,    LT_N,    LT_O,\n    // P     Q        R        S        T        U        V        W\n    LT_P,    LT_Q,    LT_R,    LT_S,    LT_T,    LT_U,    LT_V,    LT_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    LT_X,    LT_Y,    LT_Z,    LT_LBRC, LT_BSLS, LT_RBRC, LT_SCAR, LT_MINS,\n    // `     a        b        c        d        e        f        g\n    LT_GRV,  LT_A,    LT_B,    LT_C,    LT_D,    LT_E,    LT_F,    LT_G,\n    // h     i        j        k        l        m        n        o\n    LT_H,    LT_I,    LT_J,    LT_K,    LT_L,    LT_M,    LT_N,    LT_O,\n    // p     q        r        s        t        u        v        w\n    LT_P,    LT_Q,    LT_R,    LT_S,    LT_T,    LT_U,    LT_V,    LT_W,\n    // x     y        z        {        |        }        ~        DEL\n    LT_X,    LT_Y,    LT_Z,    LT_LBRC, LT_BSLS, LT_RBRC, LT_GRV,  KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_norman.h",
    "content": "/* Copyright 2019 Torben Hoffmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Norman layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_norman.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  NM_1,    NM_QUOT, NM_3,    NM_4,    NM_5,    NM_7,    NM_QUOT,\n    // (     )        *        +        ,        -        .        /\n    NM_9,    NM_0,    NM_8,    NM_EQL,  NM_COMM, NM_MINS, NM_DOT,  NM_SLSH,\n    // 0     1        2        3        4        5        6        7\n    NM_0,    NM_1,    NM_2,    NM_3,    NM_4,    NM_5,    NM_6,    NM_7,\n    // 8     9        :        ;        <        =        >        ?\n    NM_8,    NM_9,    NM_SCLN, NM_SCLN, NM_COMM, NM_EQL,  NM_DOT,  NM_SLSH,\n    // @     A        B        C        D        E        F        G\n    NM_2,    NM_A,    NM_B,    NM_C,    NM_D,    NM_E,    NM_F,    NM_G,\n    // H     I        J        K        L        M        N        O\n    NM_H,    NM_I,    NM_J,    NM_K,    NM_L,    NM_M,    NM_N,    NM_O,\n    // P     Q        R        S        T        U        V        W\n    NM_P,    NM_Q,    NM_R,    NM_S,    NM_T,    NM_U,    NM_V,    NM_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    NM_X,    NM_Y,    NM_Z,    NM_LBRC, NM_BSLS, NM_RBRC, NM_6,    NM_MINS,\n    // `     a        b        c        d        e        f        g\n    NM_GRV,  NM_A,    NM_B,    NM_C,    NM_D,    NM_E,    NM_F,    NM_G,\n    // h     i        j        k        l        m        n        o\n    NM_H,    NM_I,    NM_J,    NM_K,    NM_L,    NM_M,    NM_N,    NM_O,\n    // p     q        r        s        t        u        v        w\n    NM_P,    NM_Q,    NM_R,    NM_S,    NM_T,    NM_U,    NM_V,    NM_W,\n    // x     y        z        {        |        }        ~        DEL\n    NM_X,    NM_Y,    NM_Z,    NM_LBRC, NM_BSLS, NM_RBRC, NM_GRV,  KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_norwegian.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Norwegian layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_norwegian.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 0, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  NO_1,    NO_2,    NO_3,    NO_4,    NO_5,    NO_6,    NO_QUOT,\n    // (     )        *        +        ,        -        .        /\n    NO_8,    NO_9,    NO_QUOT, NO_PLUS, NO_COMM, NO_MINS, NO_DOT,  NO_7,\n    // 0     1        2        3        4        5        6        7\n    NO_0,    NO_1,    NO_2,    NO_3,    NO_4,    NO_5,    NO_6,    NO_7,\n    // 8     9        :        ;        <        =        >        ?\n    NO_8,    NO_9,    NO_DOT,  NO_COMM, NO_LABK, NO_0,    NO_LABK, NO_PLUS,\n    // @     A        B        C        D        E        F        G\n    NO_2,    NO_A,    NO_B,    NO_C,    NO_D,    NO_E,    NO_F,    NO_G,\n    // H     I        J        K        L        M        N        O\n    NO_H,    NO_I,    NO_J,    NO_K,    NO_L,    NO_M,    NO_N,    NO_O,\n    // P     Q        R        S        T        U        V        W\n    NO_P,    NO_Q,    NO_R,    NO_S,    NO_T,    NO_U,    NO_V,    NO_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    NO_X,    NO_Y,    NO_Z,    NO_8,    NO_BSLS, NO_9,    NO_DIAE, NO_MINS,\n    // `     a        b        c        d        e        f        g\n    NO_BSLS, NO_A,    NO_B,    NO_C,    NO_D,    NO_E,    NO_F,    NO_G,\n    // h     i        j        k        l        m        n        o\n    NO_H,    NO_I,    NO_J,    NO_K,    NO_L,    NO_M,    NO_N,    NO_O,\n    // p     q        r        s        t        u        v        w\n    NO_P,    NO_Q,    NO_R,    NO_S,    NO_T,    NO_U,    NO_V,    NO_W,\n    // x     y        z        {        |        }        ~        DEL\n    NO_X,    NO_Y,    NO_Z,    NO_7,    NO_PIPE, NO_0,    NO_DIAE, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_portuguese.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Portuguese layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_portuguese.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  PT_1,    PT_2,    PT_3,    PT_4,    PT_5,    PT_6,    PT_QUOT,\n    // (     )        *        +        ,        -        .        /\n    PT_8,    PT_9,    PT_PLUS, PT_PLUS, PT_COMM, PT_MINS, PT_DOT,  PT_7,\n    // 0     1        2        3        4        5        6        7\n    PT_0,    PT_1,    PT_2,    PT_3,    PT_4,    PT_5,    PT_6,    PT_7,\n    // 8     9        :        ;        <        =        >        ?\n    PT_8,    PT_9,    PT_DOT,  PT_COMM, PT_LABK, PT_0,    PT_LABK, PT_QUOT,\n    // @     A        B        C        D        E        F        G\n    PT_2,    PT_A,    PT_B,    PT_C,    PT_D,    PT_E,    PT_F,    PT_G,\n    // H     I        J        K        L        M        N        O\n    PT_H,    PT_I,    PT_J,    PT_K,    PT_L,    PT_M,    PT_N,    PT_O,\n    // P     Q        R        S        T        U        V        W\n    PT_P,    PT_Q,    PT_R,    PT_S,    PT_T,    PT_U,    PT_V,    PT_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    PT_X,    PT_Y,    PT_Z,    PT_8,    PT_BSLS, PT_9,    PT_TILD, PT_MINS,\n    // `     a        b        c        d        e        f        g\n    PT_ACUT, PT_A,    PT_B,    PT_C,    PT_D,    PT_E,    PT_F,    PT_G,\n    // h     i        j        k        l        m        n        o\n    PT_H,    PT_I,    PT_J,    PT_K,    PT_L,    PT_M,    PT_N,    PT_O,\n    // p     q        r        s        t        u        v        w\n    PT_P,    PT_Q,    PT_R,    PT_S,    PT_T,    PT_U,    PT_V,    PT_W,\n    // x     y        z        {        |        }        ~        DEL\n    PT_X,    PT_Y,    PT_Z,    PT_7,    PT_BSLS, PT_0,    PT_TILD, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_portuguese_mac_iso.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Portuguese layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_portuguese_mac_iso.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 0, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  PT_1,    PT_2,    PT_3,    PT_4,    PT_5,    PT_6,    PT_QUOT,\n    // (     )        *        +        ,        -        .        /\n    PT_8,    PT_9,    PT_PLUS, PT_PLUS, PT_COMM, PT_MINS, PT_DOT,  PT_7,\n    // 0     1        2        3        4        5        6        7\n    PT_0,    PT_1,    PT_2,    PT_3,    PT_4,    PT_5,    PT_6,    PT_7,\n    // 8     9        :        ;        <        =        >        ?\n    PT_8,    PT_9,    PT_DOT,  PT_COMM, PT_LABK, PT_0,    PT_LABK, PT_QUOT,\n    // @     A        B        C        D        E        F        G\n    PT_2,    PT_A,    PT_B,    PT_C,    PT_D,    PT_E,    PT_F,    PT_G,\n    // H     I        J        K        L        M        N        O\n    PT_H,    PT_I,    PT_J,    PT_K,    PT_L,    PT_M,    PT_N,    PT_O,\n    // P     Q        R        S        T        U        V        W\n    PT_P,    PT_Q,    PT_R,    PT_S,    PT_T,    PT_U,    PT_V,    PT_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    PT_X,    PT_Y,    PT_Z,    PT_8,    PT_BSLS, PT_9,    PT_TILD, PT_MINS,\n    // `     a        b        c        d        e        f        g\n    PT_ACUT, PT_A,    PT_B,    PT_C,    PT_D,    PT_E,    PT_F,    PT_G,\n    // h     i        j        k        l        m        n        o\n    PT_H,    PT_I,    PT_J,    PT_K,    PT_L,    PT_M,    PT_N,    PT_O,\n    // p     q        r        s        t        u        v        w\n    PT_P,    PT_Q,    PT_R,    PT_S,    PT_T,    PT_U,    PT_V,    PT_W,\n    // x     y        z        {        |        }        ~        DEL\n    PT_X,    PT_Y,    PT_Z,    PT_8,    PT_BSLS, PT_9,    PT_TILD, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_romanian.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Romanian layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_romanian.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  RO_1,    RO_TCOM, RO_3,    RO_4,    RO_5,    RO_7,    RO_TCOM,\n    // (     )        *        +        ,        -        .        /\n    RO_9,    RO_0,    RO_8,    RO_EQL,  RO_COMM, RO_MINS, RO_DOT,  RO_SLSH,\n    // 0     1        2        3        4        5        6        7\n    RO_0,    RO_1,    RO_2,    RO_3,    RO_4,    RO_5,    RO_6,    RO_7,\n    // 8     9        :        ;        <        =        >        ?\n    RO_8,    RO_9,    RO_DOT,  RO_COMM, RO_COMM, RO_EQL,  RO_DOT,  RO_SLSH,\n    // @     A        B        C        D        E        F        G\n    RO_2,    RO_A,    RO_B,    RO_C,    RO_D,    RO_E,    RO_F,    RO_G,\n    // H     I        J        K        L        M        N        O\n    RO_H,    RO_I,    RO_J,    RO_K,    RO_L,    RO_M,    RO_N,    RO_O,\n    // P     Q        R        S        T        U        V        W\n    RO_P,    RO_Q,    RO_R,    RO_S,    RO_T,    RO_U,    RO_V,    RO_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    RO_X,    RO_Y,    RO_Z,    RO_ABRV, RO_BSLS, RO_ICIR, RO_6,    RO_MINS,\n    // `     a        b        c        d        e        f        g\n    RO_DLQU, RO_A,    RO_B,    RO_C,    RO_D,    RO_E,    RO_F,    RO_G,\n    // h     i        j        k        l        m        n        o\n    RO_H,    RO_I,    RO_J,    RO_K,    RO_L,    RO_M,    RO_N,    RO_O,\n    // p     q        r        s        t        u        v        w\n    RO_P,    RO_Q,    RO_R,    RO_S,    RO_T,    RO_U,    RO_V,    RO_W,\n    // x     y        z        {        |        }        ~        DEL\n    RO_X,    RO_Y,    RO_Z,    RO_ABRV, RO_BSLS, RO_ICIR, RO_DLQU, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_serbian_latin.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Serbian (Latin) layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_serbian_latin.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  RS_1,    RS_2,    RS_3,    RS_4,    RS_5,    RS_6,    RS_QUOT,\n    // (     )        *        +        ,        -        .        /\n    RS_8,    RS_9,    RS_PLUS, RS_PLUS, RS_COMM, RS_MINS, RS_DOT,  RS_7,\n    // 0     1        2        3        4        5        6        7\n    RS_0,    RS_1,    RS_2,    RS_3,    RS_4,    RS_5,    RS_6,    RS_7,\n    // 8     9        :        ;        <        =        >        ?\n    RS_8,    RS_9,    RS_DOT,  RS_COMM, RS_LABK, RS_0,    RS_LABK, RS_QUOT,\n    // @     A        B        C        D        E        F        G\n    RS_V,    RS_A,    RS_B,    RS_C,    RS_D,    RS_E,    RS_F,    RS_G,\n    // H     I        J        K        L        M        N        O\n    RS_H,    RS_I,    RS_J,    RS_K,    RS_L,    RS_M,    RS_N,    RS_O,\n    // P     Q        R        S        T        U        V        W\n    RS_P,    RS_Q,    RS_R,    RS_S,    RS_T,    RS_U,    RS_V,    RS_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    RS_X,    RS_Y,    RS_Z,    RS_F,    RS_Q,    RS_G,    RS_3,    RS_MINS,\n    // `     a        b        c        d        e        f        g\n    RS_7,    RS_A,    RS_B,    RS_C,    RS_D,    RS_E,    RS_F,    RS_G,\n    // h     i        j        k        l        m        n        o\n    RS_H,    RS_I,    RS_J,    RS_K,    RS_L,    RS_M,    RS_N,    RS_O,\n    // p     q        r        s        t        u        v        w\n    RS_P,    RS_Q,    RS_R,    RS_S,    RS_T,    RS_U,    RS_V,    RS_W,\n    // x     y        z        {        |        }        ~        DEL\n    RS_X,    RS_Y,    RS_Z,    RS_B,    RS_W,    RS_N,    RS_1,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_slovak.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Slovak layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_slovak.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  SK_SECT, SK_OCIR, SK_X,    SK_OCIR, SK_EQL,  SK_AMPR, SK_P,\n    // (     )        *        +        ,        -        .        /\n    SK_ADIA, SK_NCAR, SK_AMPR, SK_PLUS, SK_COMM, SK_MINS, SK_DOT,  SK_UACU,\n    // 0     1        2        3        4        5        6        7\n    SK_EACU, SK_PLUS, SK_LCAR, SK_SCAR, SK_CCAR, SK_TCAR, SK_ZCAR, SK_YACU,\n    // 8     9        :        ;        <        =        >        ?\n    SK_AACU, SK_IACU, SK_DOT,  SK_SCLN, SK_AMPR, SK_EQL,  SK_Y,    SK_COMM,\n    // @     A        B        C        D        E        F        G\n    SK_V,    SK_A,    SK_B,    SK_C,    SK_D,    SK_E,    SK_F,    SK_G,\n    // H     I        J        K        L        M        N        O\n    SK_H,    SK_I,    SK_J,    SK_K,    SK_L,    SK_M,    SK_N,    SK_O,\n    // P     Q        R        S        T        U        V        W\n    SK_P,    SK_Q,    SK_R,    SK_S,    SK_T,    SK_U,    SK_V,    SK_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    SK_X,    SK_Y,    SK_Z,    SK_F,    SK_Q,    SK_G,    SK_3,    SK_MINS,\n    // `     a        b        c        d        e        f        g\n    SK_7,    SK_A,    SK_B,    SK_C,    SK_D,    SK_E,    SK_F,    SK_G,\n    // h     i        j        k        l        m        n        o\n    SK_H,    SK_I,    SK_J,    SK_K,    SK_L,    SK_M,    SK_N,    SK_O,\n    // p     q        r        s        t        u        v        w\n    SK_P,    SK_Q,    SK_R,    SK_S,    SK_T,    SK_U,    SK_V,    SK_W,\n    // x     y        z        {        |        }        ~        DEL\n    SK_X,    SK_Y,    SK_Z,    SK_B,    SK_W,    SK_N,    SK_1,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_slovenian.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Slovenian layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_slovenian.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  SI_1,    SI_2,    SI_3,    SI_4,    SI_5,    SI_6,    SI_QUOT,\n    // (     )        *        +        ,        -        .        /\n    SI_8,    SI_9,    SI_PLUS, SI_PLUS, SI_COMM, SI_MINS, SI_DOT,  SI_7,\n    // 0     1        2        3        4        5        6        7\n    SI_0,    SI_1,    SI_2,    SI_3,    SI_4,    SI_5,    SI_6,    SI_7,\n    // 8     9        :        ;        <        =        >        ?\n    SI_8,    SI_9,    SI_DOT,  SI_COMM, SI_LABK, SI_0,    SI_LABK, SI_QUOT,\n    // @     A        B        C        D        E        F        G\n    SI_V,    SI_A,    SI_B,    SI_C,    SI_D,    SI_E,    SI_F,    SI_G,\n    // H     I        J        K        L        M        N        O\n    SI_H,    SI_I,    SI_J,    SI_K,    SI_L,    SI_M,    SI_N,    SI_O,\n    // P     Q        R        S        T        U        V        W\n    SI_P,    SI_Q,    SI_R,    SI_S,    SI_T,    SI_U,    SI_V,    SI_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    SI_X,    SI_Y,    SI_Z,    SI_F,    SI_Q,    SI_G,    SI_3,    SI_MINS,\n    // `     a        b        c        d        e        f        g\n    SI_7,    SI_A,    SI_B,    SI_C,    SI_D,    SI_E,    SI_F,    SI_G,\n    // h     i        j        k        l        m        n        o\n    SI_H,    SI_I,    SI_J,    SI_K,    SI_L,    SI_M,    SI_N,    SI_O,\n    // p     q        r        s        t        u        v        w\n    SI_P,    SI_Q,    SI_R,    SI_S,    SI_T,    SI_U,    SI_V,    SI_W,\n    // x     y        z        {        |        }        ~        DEL\n    SI_X,    SI_Y,    SI_Z,    SI_B,    SI_W,    SI_N,    SI_1,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_spanish.h",
    "content": "/* Copyright 2018 Daniel Rodríguez\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Spanish layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_spanish.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  ES_1,    ES_2,    ES_3,    ES_4,    ES_5,    ES_6,    ES_QUOT,\n    // (     )        *        +        ,        -        .        /\n    ES_8,    ES_9,    ES_PLUS, ES_PLUS, ES_COMM, ES_MINS, ES_DOT,  ES_7,\n    // 0     1        2        3        4        5        6        7\n    ES_0,    ES_1,    ES_2,    ES_3,    ES_4,    ES_5,    ES_6,    ES_7,\n    // 8     9        :        ;        <        =        >        ?\n    ES_8,    ES_9,    ES_DOT,  ES_COMM, ES_LABK, ES_0,    ES_LABK, ES_QUOT,\n    // @     A        B        C        D        E        F        G\n    ES_2,    ES_A,    ES_B,    ES_C,    ES_D,    ES_E,    ES_F,    ES_G,\n    // H     I        J        K        L        M        N        O\n    ES_H,    ES_I,    ES_J,    ES_K,    ES_L,    ES_M,    ES_N,    ES_O,\n    // P     Q        R        S        T        U        V        W\n    ES_P,    ES_Q,    ES_R,    ES_S,    ES_T,    ES_U,    ES_V,    ES_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    ES_X,    ES_Y,    ES_Z,    ES_GRV,  ES_MORD, ES_PLUS, ES_GRV,  ES_MINS,\n    // `     a        b        c        d        e        f        g\n    ES_GRV,  ES_A,    ES_B,    ES_C,    ES_D,    ES_E,    ES_F,    ES_G,\n    // h     i        j        k        l        m        n        o\n    ES_H,    ES_I,    ES_J,    ES_K,    ES_L,    ES_M,    ES_N,    ES_O,\n    // p     q        r        s        t        u        v        w\n    ES_P,    ES_Q,    ES_R,    ES_S,    ES_T,    ES_U,    ES_V,    ES_W,\n    // x     y        z        {        |        }        ~        DEL\n    ES_X,    ES_Y,    ES_Z,    ES_ACUT, ES_1,    ES_CCED, ES_4,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_spanish_dvorak.h",
    "content": "/* Copyright 2020 José Andrés García\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Spanish Dvorak layout\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_spanish_dvorak.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  DV_1,    DV_2,    DV_3,    DV_4,    DV_5,    DV_6,    DV_QUOT,\n    // (     )        *        +        ,        -        .        /\n    DV_8,    DV_9,    DV_PLUS, DV_PLUS, DV_COMM, DV_MINS, DV_DOT,  DV_7,\n    // 0     1        2        3        4        5        6        7\n    DV_0,    DV_1,    DV_2,    DV_3,    DV_4,    DV_5,    DV_6,    DV_7,\n    // 8     9        :        ;        <        =        >        ?\n    DV_8,    DV_9,    DV_DOT,  DV_COMM, DV_LABK, DV_0,    DV_LABK, DV_QUOT,\n    // @     A        B        C        D        E        F        G\n    DV_2,    DV_A,    DV_B,    DV_C,    DV_D,    DV_E,    DV_F,    DV_G,\n    // H     I        J        K        L        M        N        O\n    DV_H,    DV_I,    DV_J,    DV_K,    DV_L,    DV_M,    DV_N,    DV_O,\n    // P     Q        R        S        T        U        V        W\n    DV_P,    DV_Q,    DV_R,    DV_S,    DV_T,    DV_U,    DV_V,    DV_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    DV_X,    DV_Y,    DV_Z,    DV_GRV,  DV_MORD, DV_PLUS, DV_GRV,  DV_MINS,\n    // `     a        b        c        d        e        f        g\n    DV_GRV,  DV_A,    DV_B,    DV_C,    DV_D,    DV_E,    DV_F,    DV_G,\n    // h     i        j        k        l        m        n        o\n    DV_H,    DV_I,    DV_J,    DV_K,    DV_L,    DV_M,    DV_N,    DV_O,\n    // p     q        r        s        t        u        v        w\n    DV_P,    DV_Q,    DV_R,    DV_S,    DV_T,    DV_U,    DV_V,    DV_W,\n    // x     y        z        {        |        }        ~        DEL\n    DV_X,    DV_Y,    DV_Z,    DV_ACUT, DV_1,    DV_CCED, DV_4,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_spanish_latin_america.h",
    "content": "/* Copyright 2023 Juan David Díaz\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Latam Spanish layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_spanish_latin_america.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 1, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  ES_1,    ES_2,    ES_3,    ES_4,    ES_5,    ES_6,    ES_QUOT,\n    // (     )        *        +        ,        -        .        /\n    ES_8,    ES_9,    ES_PLUS, ES_PLUS, ES_COMM, ES_MINS, ES_DOT,  ES_7,\n    // 0     1        2        3        4        5        6        7\n    ES_0,    ES_1,    ES_2,    ES_3,    ES_4,    ES_5,    ES_6,    ES_7,\n    // 8     9        :        ;        <        =        >        ?\n    ES_8,    ES_9,    ES_DOT,  ES_COMM, ES_LABK, ES_0,    ES_LABK, ES_QUOT,\n    // @     A        B        C        D        E        F        G\n    ES_Q,    ES_A,    ES_B,    ES_C,    ES_D,    ES_E,    ES_F,    ES_G,\n    // H     I        J        K        L        M        N        O\n    ES_H,    ES_I,    ES_J,    ES_K,    ES_L,    ES_M,    ES_N,    ES_O,\n    // P     Q        R        S        T        U        V        W\n    ES_P,    ES_Q,    ES_R,    ES_S,    ES_T,    ES_U,    ES_V,    ES_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    ES_X,    ES_Y,    ES_Z,    ES_LCBR, ES_QUOT, ES_RCBR, ES_LCBR, ES_MINS,\n    // `     a        b        c        d        e        f        g\n    ES_RCBR, ES_A,    ES_B,    ES_C,    ES_D,    ES_E,    ES_F,    ES_G,\n    // h     i        j        k        l        m        n        o\n    ES_H,    ES_I,    ES_J,    ES_K,    ES_L,    ES_M,    ES_N,    ES_O,\n    // p     q        r        s        t        u        v        w\n    ES_P,    ES_Q,    ES_R,    ES_S,    ES_T,    ES_U,    ES_V,    ES_W,\n    // x     y        z        {        |        }        ~        DEL\n    ES_X,    ES_Y,    ES_Z,    ES_LCBR, ES_PIPE, ES_RCBR, ES_PLUS, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_swedish.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Swedish layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_swedish.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 0, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 1, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  SE_1,    SE_2,    SE_3,    SE_4,    SE_5,    SE_6,    SE_QUOT,\n    // (     )        *        +        ,        -        .        /\n    SE_8,    SE_9,    SE_QUOT, SE_PLUS, SE_COMM, SE_MINS, SE_DOT,  SE_7,\n    // 0     1        2        3        4        5        6        7\n    SE_0,    SE_1,    SE_2,    SE_3,    SE_4,    SE_5,    SE_6,    SE_7,\n    // 8     9        :        ;        <        =        >        ?\n    SE_8,    SE_9,    SE_DOT,  SE_COMM, SE_LABK, SE_0,    SE_LABK, SE_PLUS,\n    // @     A        B        C        D        E        F        G\n    SE_2,    SE_A,    SE_B,    SE_C,    SE_D,    SE_E,    SE_F,    SE_G,\n    // H     I        J        K        L        M        N        O\n    SE_H,    SE_I,    SE_J,    SE_K,    SE_L,    SE_M,    SE_N,    SE_O,\n    // P     Q        R        S        T        U        V        W\n    SE_P,    SE_Q,    SE_R,    SE_S,    SE_T,    SE_U,    SE_V,    SE_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    SE_X,    SE_Y,    SE_Z,    SE_8,    SE_PLUS, SE_9,    SE_DIAE, SE_MINS,\n    // `     a        b        c        d        e        f        g\n    SE_ACUT, SE_A,    SE_B,    SE_C,    SE_D,    SE_E,    SE_F,    SE_G,\n    // h     i        j        k        l        m        n        o\n    SE_H,    SE_I,    SE_J,    SE_K,    SE_L,    SE_M,    SE_N,    SE_O,\n    // p     q        r        s        t        u        v        w\n    SE_P,    SE_Q,    SE_R,    SE_S,    SE_T,    SE_U,    SE_V,    SE_W,\n    // x     y        z        {        |        }        ~        DEL\n    SE_X,    SE_Y,    SE_Z,    SE_7,    SE_LABK, SE_0,    SE_DIAE, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_swiss_de.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Swiss German layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_swiss_de.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 0, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  CH_DIAE, CH_2,    CH_3,    CH_DLR,  CH_5,    CH_6,    CH_QUOT,\n    // (     )        *        +        ,        -        .        /\n    CH_8,    CH_9,    CH_3,    CH_1,    CH_COMM, CH_MINS, CH_DOT,  CH_7,\n    // 0     1        2        3        4        5        6        7\n    CH_0,    CH_1,    CH_2,    CH_3,    CH_4,    CH_5,    CH_6,    CH_7,\n    // 8     9        :        ;        <        =        >        ?\n    CH_8,    CH_9,    CH_DOT,  CH_COMM, CH_LABK, CH_0,    CH_LABK, CH_QUOT,\n    // @     A        B        C        D        E        F        G\n    CH_2,    CH_A,    CH_B,    CH_C,    CH_D,    CH_E,    CH_F,    CH_G,\n    // H     I        J        K        L        M        N        O\n    CH_H,    CH_I,    CH_J,    CH_K,    CH_L,    CH_M,    CH_N,    CH_O,\n    // P     Q        R        S        T        U        V        W\n    CH_P,    CH_Q,    CH_R,    CH_S,    CH_T,    CH_U,    CH_V,    CH_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    CH_X,    CH_Y,    CH_Z,    CH_UDIA, CH_LABK, CH_DIAE, CH_CIRC, CH_MINS,\n    // `     a        b        c        d        e        f        g\n    CH_CIRC, CH_A,    CH_B,    CH_C,    CH_D,    CH_E,    CH_F,    CH_G,\n    // h     i        j        k        l        m        n        o\n    CH_H,    CH_I,    CH_J,    CH_K,    CH_L,    CH_M,    CH_N,    CH_O,\n    // p     q        r        s        t        u        v        w\n    CH_P,    CH_Q,    CH_R,    CH_S,    CH_T,    CH_U,    CH_V,    CH_W,\n    // x     y        z        {        |        }        ~        DEL\n    CH_X,    CH_Y,    CH_Z,    CH_ADIA, CH_7,    CH_DLR,  CH_CIRC, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_swiss_fr.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Swiss French layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_swiss_fr.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 0, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  CH_DIAE, CH_2,    CH_3,    CH_DLR,  CH_5,    CH_6,    CH_QUOT,\n    // (     )        *        +        ,        -        .        /\n    CH_8,    CH_9,    CH_3,    CH_0,    CH_COMM, CH_MINS, CH_DOT,  CH_7,\n    // 0     1        2        3        4        5        6        7\n    CH_0,    CH_1,    CH_2,    CH_3,    CH_4,    CH_5,    CH_6,    CH_7,\n    // 8     9        :        ;        <        =        >        ?\n    CH_8,    CH_9,    CH_DOT,  CH_COMM, CH_LABK, CH_0,    CH_LABK, CH_QUOT,\n    // @     A        B        C        D        E        F        G\n    CH_2,    CH_A,    CH_B,    CH_C,    CH_D,    CH_E,    CH_F,    CH_G,\n    // H     I        J        K        L        M        N        O\n    CH_H,    CH_I,    CH_J,    CH_K,    CH_L,    CH_M,    CH_N,    CH_O,\n    // P     Q        R        S        T        U        V        W\n    CH_P,    CH_Q,    CH_R,    CH_S,    CH_T,    CH_U,    CH_V,    CH_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    CH_X,    CH_Y,    CH_Z,    CH_EGRV, CH_LABK, CH_DIAE, CH_CIRC, CH_MINS,\n    // `     a        b        c        d        e        f        g\n    CH_CIRC, CH_A,    CH_B,    CH_C,    CH_D,    CH_E,    CH_F,    CH_G,\n    // h     i        j        k        l        m        n        o\n    CH_H,    CH_I,    CH_J,    CH_K,    CH_L,    CH_M,    CH_N,    CH_O,\n    // p     q        r        s        t        u        v        w\n    CH_P,    CH_Q,    CH_R,    CH_S,    CH_T,    CH_U,    CH_V,    CH_W,\n    // x     y        z        {        |        }        ~        DEL\n    CH_X,    CH_Y,    CH_Z,    CH_AGRV, CH_7,    CH_DLR,  CH_CIRC, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_turkish_f.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Turkish F layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_turkish_f.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  TR_1,    TR_2,    TR_3,    TR_4,    TR_5,    TR_6,    TR_7,\n    // (     )        *        +        ,        -        .        /\n    TR_8,    TR_9,    TR_PLUS, TR_PLUS, TR_COMM, TR_MINS, TR_DOT,  TR_SLSH,\n    // 0     1        2        3        4        5        6        7\n    TR_0,    TR_1,    TR_2,    TR_3,    TR_4,    TR_5,    TR_6,    TR_7,\n    // 8     9        :        ;        <        =        >        ?\n    TR_8,    TR_9,    TR_DOT,  TR_COMM, TR_LABK, TR_0,    TR_LABK, TR_SLSH,\n    // @     A        B        C        D        E        F        G\n    TR_F,    TR_A,    TR_B,    TR_C,    TR_D,    TR_E,    TR_F,    TR_G,\n    // H     I        J        K        L        M        N        O\n    TR_H,    TR_I,    TR_J,    TR_K,    TR_L,    TR_M,    TR_N,    TR_O,\n    // P     Q        R        S        T        U        V        W\n    TR_P,    TR_Q,    TR_R,    TR_S,    TR_T,    TR_U,    TR_V,    TR_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    TR_X,    TR_Y,    TR_Z,    TR_8,    TR_SLSH, TR_9,    TR_3,    TR_MINS,\n    // `     a        b        c        d        e        f        g\n    TR_X,    TR_A,    TR_B,    TR_C,    TR_D,    TR_E,    TR_F,    TR_G,\n    // h     i        j        k        l        m        n        o\n    TR_H,    TR_I,    TR_J,    TR_K,    TR_L,    TR_M,    TR_N,    TR_O,\n    // p     q        r        s        t        u        v        w\n    TR_P,    TR_Q,    TR_R,    TR_S,    TR_T,    TR_U,    TR_V,    TR_W,\n    // x     y        z        {        |        }        ~        DEL\n    TR_X,    TR_Y,    TR_Z,    TR_7,    TR_MINS, TR_0,    TR_W,    KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_turkish_q.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Turkish Q layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_turkish_q.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 0, 0, 0, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 0, 1, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 1, 0, 1, 1, 1),\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\nconst uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 0, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  TR_1,    TR_DQUO, TR_3,    TR_4,    TR_5,    TR_6,    TR_2,\n    // (     )        *        +        ,        -        .        /\n    TR_8,    TR_9,    TR_ASTR, TR_4,    TR_COMM, TR_MINS, TR_DOT,  TR_7,\n    // 0     1        2        3        4        5        6        7\n    TR_0,    TR_1,    TR_2,    TR_3,    TR_4,    TR_5,    TR_6,    TR_7,\n    // 8     9        :        ;        <        =        >        ?\n    TR_8,    TR_9,    TR_DOT,  TR_COMM, TR_LABK, TR_0,    TR_LABK, TR_ASTR,\n    // @     A        B        C        D        E        F        G\n    TR_Q,    TR_A,    TR_B,    TR_C,    TR_D,    TR_E,    TR_F,    TR_G,\n    // H     I        J        K        L        M        N        O\n    TR_H,    TR_I,    TR_J,    TR_K,    TR_L,    TR_M,    TR_N,    TR_O,\n    // P     Q        R        S        T        U        V        W\n    TR_P,    TR_Q,    TR_R,    TR_S,    TR_T,    TR_U,    TR_V,    TR_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    TR_X,    TR_Y,    TR_Z,    TR_8,    TR_ASTR, TR_9,    TR_3,    TR_MINS,\n    // `     a        b        c        d        e        f        g\n    TR_COMM, TR_A,    TR_B,    TR_C,    TR_D,    TR_E,    TR_F,    TR_G,\n    // h     i        j        k        l        m        n        o\n    TR_H,    TR_I,    TR_J,    TR_K,    TR_L,    TR_M,    TR_N,    TR_O,\n    // p     q        r        s        t        u        v        w\n    TR_P,    TR_Q,    TR_R,    TR_S,    TR_T,    TR_U,    TR_V,    TR_W,\n    // x     y        z        {        |        }        ~        DEL\n    TR_X,    TR_Y,    TR_Z,    TR_7,    TR_MINS, TR_0,    TR_UDIA, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_uk.h",
    "content": "/* Copyright 2019 Rys Sommefeldt\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for UK layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_uk.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 0, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 0, 1, 0, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  UK_1,    UK_2,    UK_HASH, UK_4,    UK_5,    UK_7,    UK_QUOT,\n    // (     )        *        +        ,        -        .        /\n    UK_9,    UK_0,    UK_8,    UK_EQL,  UK_COMM, UK_MINS, UK_DOT,  UK_SLSH,\n    // 0     1        2        3        4        5        6        7\n    UK_0,    UK_1,    UK_2,    UK_3,    UK_4,    UK_5,    UK_6,    UK_7,\n    // 8     9        :        ;        <        =        >        ?\n    UK_8,    UK_9,    UK_SCLN, UK_SCLN, UK_COMM, UK_EQL,  UK_DOT,  UK_SLSH,\n    // @     A        B        C        D        E        F        G\n    UK_QUOT, UK_A,    UK_B,    UK_C,    UK_D,    UK_E,    UK_F,    UK_G,\n    // H     I        J        K        L        M        N        O\n    UK_H,    UK_I,    UK_J,    UK_K,    UK_L,    UK_M,    UK_N,    UK_O,\n    // P     Q        R        S        T        U        V        W\n    UK_P,    UK_Q,    UK_R,    UK_S,    UK_T,    UK_U,    UK_V,    UK_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    UK_X,    UK_Y,    UK_Z,    UK_LBRC, UK_BSLS, UK_RBRC, UK_6,    UK_MINS,\n    // `     a        b        c        d        e        f        g\n    UK_GRV,  UK_A,    UK_B,    UK_C,    UK_D,    UK_E,    UK_F,    UK_G,\n    // h     i        j        k        l        m        n        o\n    UK_H,    UK_I,    UK_J,    UK_K,    UK_L,    UK_M,    UK_N,    UK_O,\n    // p     q        r        s        t        u        v        w\n    UK_P,    UK_Q,    UK_R,    UK_S,    UK_T,    UK_U,    UK_V,    UK_W,\n    // x     y        z        {        |        }        ~        DEL\n    UK_X,    UK_Y,    UK_Z,    UK_LBRC, UK_BSLS, UK_RBRC, UK_HASH, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_us_international.h",
    "content": "/* Copyright 2019 Rys Sommefeldt\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for US International layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_us_international.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 0, 1, 0, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0),\n};\n\n__attribute__((weak)) const uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 1, 0, 0, 0, 0, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n    KCLUT_ENTRY(1, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 1, 0),\n};\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  US_1,    US_ACUT, US_3,    US_4,    US_5,    US_7,    US_ACUT,\n    // (     )        *        +        ,        -        .        /\n    US_9,    US_0,    US_8,    US_EQL,  US_COMM, US_MINS, US_DOT,  US_SLSH,\n    // 0     1        2        3        4        5        6        7\n    US_0,    US_1,    US_2,    US_3,    US_4,    US_5,    US_6,    US_7,\n    // 8     9        :        ;        <        =        >        ?\n    US_8,    US_9,    US_SCLN, US_SCLN, US_COMM, US_EQL,  US_DOT,  US_SLSH,\n    // @     A        B        C        D        E        F        G\n    US_2,    US_A,    US_B,    US_C,    US_D,    US_E,    US_F,    US_G,\n    // H     I        J        K        L        M        N        O\n    US_H,    US_I,    US_J,    US_K,    US_L,    US_M,    US_N,    US_O,\n    // P     Q        R        S        T        U        V        W\n    US_P,    US_Q,    US_R,    US_S,    US_T,    US_U,    US_V,    US_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    US_X,    US_Y,    US_Z,    US_LBRC, US_BSLS, US_RBRC, US_6,    US_MINS,\n    // `     a        b        c        d        e        f        g\n    US_DGRV, US_A,    US_B,    US_C,    US_D,    US_E,    US_F,    US_G,\n    // h     i        j        k        l        m        n        o\n    US_H,    US_I,    US_J,    US_K,    US_L,    US_M,    US_N,    US_O,\n    // p     q        r        s        t        u        v        w\n    US_P,    US_Q,    US_R,    US_S,    US_T,    US_U,    US_V,    US_W,\n    // x     y        z        {        |        }        ~        DEL\n    US_X,    US_Y,    US_Z,    US_LBRC, US_BSLS, US_RBRC, US_DGRV, KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_workman.h",
    "content": "/* Copyright 2018 Jacob Jerrell\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Workman layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_workman.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  WK_1,    WK_QUOT, WK_3,    WK_4,    WK_5,    WK_7,    WK_QUOT,\n    // (     )        *        +        ,        -        .        /\n    WK_9,    WK_0,    WK_8,    WK_EQL,  WK_COMM, WK_MINS, WK_DOT,  WK_SLSH,\n    // 0     1        2        3        4        5        6        7\n    WK_0,    WK_1,    WK_2,    WK_3,    WK_4,    WK_5,    WK_6,    WK_7,\n    // 8     9        :        ;        <        =        >        ?\n    WK_8,    WK_9,    WK_SCLN, WK_SCLN, WK_COMM, WK_EQL,  WK_DOT,  WK_SLSH,\n    // @     A        B        C        D        E        F        G\n    WK_2,    WK_A,    WK_B,    WK_C,    WK_D,    WK_E,    WK_F,    WK_G,\n    // H     I        J        K        L        M        N        O\n    WK_H,    WK_I,    WK_J,    WK_K,    WK_L,    WK_M,    WK_N,    WK_O,\n    // P     Q        R        S        T        U        V        W\n    WK_P,    WK_Q,    WK_R,    WK_S,    WK_T,    WK_U,    WK_V,    WK_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    WK_X,    WK_Y,    WK_Z,    WK_LBRC, WK_BSLS, WK_RBRC, WK_6,    WK_MINS,\n    // `     a        b        c        d        e        f        g\n    WK_GRV,  WK_A,    WK_B,    WK_C,    WK_D,    WK_E,    WK_F,    WK_G,\n    // h     i        j        k        l        m        n        o\n    WK_H,    WK_I,    WK_J,    WK_K,    WK_L,    WK_M,    WK_N,    WK_O,\n    // p     q        r        s        t        u        v        w\n    WK_P,    WK_Q,    WK_R,    WK_S,    WK_T,    WK_U,    WK_V,    WK_W,\n    // x     y        z        {        |        }        ~        DEL\n    WK_X,    WK_Y,    WK_Z,    WK_LBRC, WK_BSLS, WK_RBRC, WK_GRV,  KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_extras/sendstring_workman_zxcvm.h",
    "content": "/* Copyright 2018 Jacob Jerrell\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n// Sendstring lookup tables for Workman ZXCVM layouts\n\n#pragma once\n\n#include \"send_string.h\"\n#include \"keymap_workman_zxcvm.h\"\n\n// clang-format off\n\nconst uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  WK_1,    WK_QUOT, WK_3,    WK_4,    WK_5,    WK_7,    WK_QUOT,\n    // (     )        *        +        ,        -        .        /\n    WK_9,    WK_0,    WK_8,    WK_EQL,  WK_COMM, WK_MINS, WK_DOT,  WK_SLSH,\n    // 0     1        2        3        4        5        6        7\n    WK_0,    WK_1,    WK_2,    WK_3,    WK_4,    WK_5,    WK_6,    WK_7,\n    // 8     9        :        ;        <        =        >        ?\n    WK_8,    WK_9,    WK_SCLN, WK_SCLN, WK_COMM, WK_EQL,  WK_DOT,  WK_SLSH,\n    // @     A        B        C        D        E        F        G\n    WK_2,    WK_A,    WK_B,    WK_C,    WK_D,    WK_E,    WK_F,    WK_G,\n    // H     I        J        K        L        M        N        O\n    WK_H,    WK_I,    WK_J,    WK_K,    WK_L,    WK_M,    WK_N,    WK_O,\n    // P     Q        R        S        T        U        V        W\n    WK_P,    WK_Q,    WK_R,    WK_S,    WK_T,    WK_U,    WK_V,    WK_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    WK_X,    WK_Y,    WK_Z,    WK_LBRC, WK_BSLS, WK_RBRC, WK_6,    WK_MINS,\n    // `     a        b        c        d        e        f        g\n    WK_GRV,  WK_A,    WK_B,    WK_C,    WK_D,    WK_E,    WK_F,    WK_G,\n    // h     i        j        k        l        m        n        o\n    WK_H,    WK_I,    WK_J,    WK_K,    WK_L,    WK_M,    WK_N,    WK_O,\n    // p     q        r        s        t        u        v        w\n    WK_P,    WK_Q,    WK_R,    WK_S,    WK_T,    WK_U,    WK_V,    WK_W,\n    // x     y        z        {        |        }        ~        DEL\n    WK_X,    WK_Y,    WK_Z,    WK_LBRC, WK_BSLS, WK_RBRC, WK_GRV,  KC_DEL\n};\n"
  },
  {
    "path": "quantum/keymap_introspection.c",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#if defined(COMMUNITY_MODULES_ENABLE)\n#    include \"community_modules_introspection.h\"\n#endif // defined(COMMUNITY_MODULES_ENABLE)\n\n// Pull the actual keymap code so that we can inspect stuff from it\n#include KEYMAP_C\n\n// Allow for keymap or userspace rules.mk to specify an alternate location for the keymap array\n#ifdef INTROSPECTION_KEYMAP_C\n#    include INTROSPECTION_KEYMAP_C\n#endif // INTROSPECTION_KEYMAP_C\n\n#include \"compiler_support.h\"\n#include \"keymap_introspection.h\"\n#include \"util.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Key mapping\n\n#define NUM_KEYMAP_LAYERS_RAW ((uint8_t)(sizeof(keymaps) / ((MATRIX_ROWS) * (MATRIX_COLS) * sizeof(uint16_t))))\n\nuint8_t keymap_layer_count_raw(void) {\n    return NUM_KEYMAP_LAYERS_RAW;\n}\n\n__attribute__((weak)) uint8_t keymap_layer_count(void) {\n    return keymap_layer_count_raw();\n}\n\n#ifdef DYNAMIC_KEYMAP_ENABLE\nSTATIC_ASSERT(NUM_KEYMAP_LAYERS_RAW <= MAX_LAYER, \"Number of keymap layers exceeds maximum set by DYNAMIC_KEYMAP_LAYER_COUNT\");\n#else\nSTATIC_ASSERT(NUM_KEYMAP_LAYERS_RAW <= MAX_LAYER, \"Number of keymap layers exceeds maximum set by LAYER_STATE_(8|16|32)BIT\");\n#endif\n\nuint16_t keycode_at_keymap_location_raw(uint8_t layer_num, uint8_t row, uint8_t column) {\n    if (layer_num < NUM_KEYMAP_LAYERS_RAW && row < MATRIX_ROWS && column < MATRIX_COLS) {\n        return pgm_read_word(&keymaps[layer_num][row][column]);\n    }\n    return KC_TRNS;\n}\n\n__attribute__((weak)) uint16_t keycode_at_keymap_location(uint8_t layer_num, uint8_t row, uint8_t column) {\n    return keycode_at_keymap_location_raw(layer_num, row, column);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Encoder mapping\n\n#if defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE)\n\n#    define NUM_ENCODERMAP_LAYERS_RAW ((uint8_t)(sizeof(encoder_map) / ((NUM_ENCODERS) * (NUM_DIRECTIONS) * sizeof(uint16_t))))\n\nuint8_t encodermap_layer_count_raw(void) {\n    return NUM_ENCODERMAP_LAYERS_RAW;\n}\n\n__attribute__((weak)) uint8_t encodermap_layer_count(void) {\n    return encodermap_layer_count_raw();\n}\n\nSTATIC_ASSERT(NUM_KEYMAP_LAYERS_RAW == NUM_ENCODERMAP_LAYERS_RAW, \"Number of encoder_map layers doesn't match the number of keymap layers\");\n\nuint16_t keycode_at_encodermap_location_raw(uint8_t layer_num, uint8_t encoder_idx, bool clockwise) {\n    if (layer_num < NUM_ENCODERMAP_LAYERS_RAW && encoder_idx < NUM_ENCODERS) {\n        return pgm_read_word(&encoder_map[layer_num][encoder_idx][clockwise ? 0 : 1]);\n    }\n    return KC_TRNS;\n}\n\n__attribute__((weak)) uint16_t keycode_at_encodermap_location(uint8_t layer_num, uint8_t encoder_idx, bool clockwise) {\n    return keycode_at_encodermap_location_raw(layer_num, encoder_idx, clockwise);\n}\n\n#endif // defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE)\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Dip Switch mapping\n\n#if defined(DIP_SWITCH_ENABLE) && defined(DIP_SWITCH_MAP_ENABLE)\n\nuint16_t keycode_at_dip_switch_map_location_raw(uint8_t switch_idx, bool on) {\n    if (switch_idx < NUM_DIP_SWITCHES) {\n        return pgm_read_word(&dip_switch_map[switch_idx][!!on]);\n    }\n    return KC_TRNS;\n}\n\n__attribute__((weak)) uint16_t keycode_at_dip_switch_map_location(uint8_t switch_idx, bool on) {\n    return keycode_at_dip_switch_map_location_raw(switch_idx, on);\n}\n\n#endif // defined(DIP_SWITCH_ENABLE) && defined(DIP_SWITCH_MAP_ENABLE)\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Combos\n\n#if defined(COMBO_ENABLE)\n\nuint16_t combo_count_raw(void) {\n    return ARRAY_SIZE(key_combos);\n}\n__attribute__((weak)) uint16_t combo_count(void) {\n    return combo_count_raw();\n}\n\nSTATIC_ASSERT(ARRAY_SIZE(key_combos) <= (QK_KB), \"Number of combos is abnormally high. Are you using SAFE_RANGE in an enum for combos?\");\n\ncombo_t* combo_get_raw(uint16_t combo_idx) {\n    if (combo_idx >= combo_count_raw()) {\n        return NULL;\n    }\n    return &key_combos[combo_idx];\n}\n__attribute__((weak)) combo_t* combo_get(uint16_t combo_idx) {\n    return combo_get_raw(combo_idx);\n}\n\n#endif // defined(COMBO_ENABLE)\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Tap Dance\n\n#if defined(TAP_DANCE_ENABLE)\n\nuint16_t tap_dance_count_raw(void) {\n    return ARRAY_SIZE(tap_dance_actions);\n}\n\n__attribute__((weak)) uint16_t tap_dance_count(void) {\n    return tap_dance_count_raw();\n}\n\nSTATIC_ASSERT(ARRAY_SIZE(tap_dance_actions) <= (QK_TAP_DANCE_MAX - QK_TAP_DANCE), \"Number of tap dance actions exceeds maximum. Are you using SAFE_RANGE in tap dance enum?\");\n\ntap_dance_action_t* tap_dance_get_raw(uint16_t tap_dance_idx) {\n    if (tap_dance_idx >= tap_dance_count_raw()) {\n        return NULL;\n    }\n    return &tap_dance_actions[tap_dance_idx];\n}\n\n__attribute__((weak)) tap_dance_action_t* tap_dance_get(uint16_t tap_dance_idx) {\n    return tap_dance_get_raw(tap_dance_idx);\n}\n\n#endif // defined(TAP_DANCE_ENABLE)\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Key Overrides\n\n#if defined(KEY_OVERRIDE_ENABLE)\n\nuint16_t key_override_count_raw(void) {\n    return ARRAY_SIZE(key_overrides);\n}\n\n__attribute__((weak)) uint16_t key_override_count(void) {\n    return key_override_count_raw();\n}\n\nSTATIC_ASSERT(ARRAY_SIZE(key_overrides) <= (QK_KB), \"Number of key overrides is abnormally high. Are you using SAFE_RANGE in an enum for key overrides?\");\n\nconst key_override_t* key_override_get_raw(uint16_t key_override_idx) {\n    if (key_override_idx >= key_override_count_raw()) {\n        return NULL;\n    }\n    return key_overrides[key_override_idx];\n}\n\n__attribute__((weak)) const key_override_t* key_override_get(uint16_t key_override_idx) {\n    return key_override_get_raw(key_override_idx);\n}\n\n#endif // defined(KEY_OVERRIDE_ENABLE)\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Community modules (must be last in this file!)\n\n#if defined(COMMUNITY_MODULES_ENABLE)\n#    include \"community_modules_introspection.c\"\n#endif // defined(COMMUNITY_MODULES_ENABLE)\n"
  },
  {
    "path": "quantum/keymap_introspection.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Key mapping\n\n// Get the number of layers defined in the keymap, stored in firmware rather than any other persistent storage\nuint8_t keymap_layer_count_raw(void);\n// Get the number of layers defined in the keymap, potentially stored dynamically\nuint8_t keymap_layer_count(void);\n\n// Get the keycode for the keymap location, stored in firmware rather than any other persistent storage\nuint16_t keycode_at_keymap_location_raw(uint8_t layer_num, uint8_t row, uint8_t column);\n// Get the keycode for the keymap location, potentially stored dynamically\nuint16_t keycode_at_keymap_location(uint8_t layer_num, uint8_t row, uint8_t column);\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Encoder mapping\n\n#if defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE)\n\n// Get the number of layers defined in the encoder map, stored in firmware rather than any other persistent storage\nuint8_t encodermap_layer_count_raw(void);\n// Get the number of layers defined in the encoder map, potentially stored dynamically\nuint8_t encodermap_layer_count(void);\n\n// Get the keycode for the encoder mapping location, stored in firmware rather than any other persistent storage\nuint16_t keycode_at_encodermap_location_raw(uint8_t layer_num, uint8_t encoder_idx, bool clockwise);\n// Get the keycode for the encoder mapping location, potentially stored dynamically\nuint16_t keycode_at_encodermap_location(uint8_t layer_num, uint8_t encoder_idx, bool clockwise);\n\n#endif // defined(ENCODER_ENABLE) && defined(ENCODER_MAP_ENABLE)\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Dip Switch mapping\n\n#if defined(DIP_SWITCH_ENABLE) && defined(DIP_SWITCH_MAP_ENABLE)\n\n// Get the keycode for the dip_switch mapping location, stored in firmware rather than any other persistent storage\nuint16_t keycode_at_dip_switch_map_location_raw(uint8_t switch_idx, bool on);\n// Get the keycode for the dip_switch mapping location, potentially stored dynamically\nuint16_t keycode_at_dip_switch_map_location(uint8_t switch_idx, bool on);\n\n#endif // defined(DIP_SWITCH_ENABLE) && defined(DIP_SWITCH_MAP_ENABLE)\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Combos\n\n#if defined(COMBO_ENABLE)\n\n// Forward declaration of combo_t so we don't need to deal with header reordering\nstruct combo_t;\ntypedef struct combo_t combo_t;\n\n// Get the number of combos defined in the user's keymap, stored in firmware rather than any other persistent storage\nuint16_t combo_count_raw(void);\n// Get the number of combos defined in the user's keymap, potentially stored dynamically\nuint16_t combo_count(void);\n\n// Get the combo definition, stored in firmware rather than any other persistent storage\ncombo_t* combo_get_raw(uint16_t combo_idx);\n// Get the combo definition, potentially stored dynamically\ncombo_t* combo_get(uint16_t combo_idx);\n\n#endif // defined(COMBO_ENABLE)\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Tap Dance\n\n#if defined(TAP_DANCE_ENABLE)\n\n// Forward declaration of tap_dance_action_t so we don't need to deal with header reordering\nstruct tap_dance_action_t;\ntypedef struct tap_dance_action_t tap_dance_action_t;\n\n// Get the number of tap dances defined in the user's keymap, stored in firmware rather than any other persistent storage\nuint16_t tap_dance_count_raw(void);\n// Get the number of tap dances defined in the user's keymap, potentially stored dynamically\nuint16_t tap_dance_count(void);\n\n// Get the tap dance definitions, stored in firmware rather than any other persistent storage\ntap_dance_action_t* tap_dance_get_raw(uint16_t tap_dance_idx);\n// Get the tap dance definitions, potentially stored dynamically\ntap_dance_action_t* tap_dance_get(uint16_t tap_dance_idx);\n\n#endif // defined(TAP_DANCE_ENABLE)\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Key Overrides\n\n#if defined(KEY_OVERRIDE_ENABLE)\n\n// Forward declaration of key_override_t so we don't need to deal with header reordering\nstruct key_override_t;\ntypedef struct key_override_t key_override_t;\n\n// Get the number of key overrides defined in the user's keymap, stored in firmware rather than any other persistent storage\nuint16_t key_override_count_raw(void);\n// Get the number of key overrides defined in the user's keymap, potentially stored dynamically\nuint16_t key_override_count(void);\n\n// Get the key override definitions, stored in firmware rather than any other persistent storage\nconst key_override_t* key_override_get_raw(uint16_t key_override_idx);\n// Get the key override definitions, potentially stored dynamically\nconst key_override_t* key_override_get(uint16_t key_override_idx);\n\n#endif // defined(KEY_OVERRIDE_ENABLE)\n"
  },
  {
    "path": "quantum/layer_lock.c",
    "content": "// Copyright 2022-2023 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"layer_lock.h\"\n#include \"quantum_keycodes.h\"\n\n#ifndef NO_ACTION_LAYER\n// The current lock state. The kth bit is on if layer k is locked.\nlayer_state_t locked_layers = 0;\n\n// Layer Lock timer to disable layer lock after X seconds inactivity\n#    if defined(LAYER_LOCK_IDLE_TIMEOUT) && LAYER_LOCK_IDLE_TIMEOUT > 0\nuint32_t layer_lock_timer = 0;\n\nvoid layer_lock_timeout_task(void) {\n    if (locked_layers && timer_elapsed32(layer_lock_timer) > LAYER_LOCK_IDLE_TIMEOUT) {\n        layer_lock_all_off();\n        layer_lock_timer = timer_read32();\n    }\n}\nvoid layer_lock_activity_trigger(void) {\n    layer_lock_timer = timer_read32();\n}\n#    else\nvoid layer_lock_timeout_task(void) {}\nvoid layer_lock_activity_trigger(void) {}\n#    endif // LAYER_LOCK_IDLE_TIMEOUT > 0\n\nbool is_layer_locked(uint8_t layer) {\n    return locked_layers & ((layer_state_t)1 << layer);\n}\n\nvoid layer_lock_invert(uint8_t layer) {\n    const layer_state_t mask = (layer_state_t)1 << layer;\n    if ((locked_layers & mask) == 0) { // Layer is being locked.\n#    ifndef NO_ACTION_ONESHOT\n        if (layer == get_oneshot_layer()) {\n            reset_oneshot_layer(); // Reset so that OSL doesn't turn layer off.\n        }\n#    endif // NO_ACTION_ONESHOT\n        layer_on(layer);\n\n        layer_lock_activity_trigger();\n    } else { // Layer is being unlocked.\n        layer_off(layer);\n    }\n    layer_lock_set_kb(locked_layers ^= mask);\n}\n\n// Implement layer_lock_on/off by deferring to layer_lock_invert.\nvoid layer_lock_on(uint8_t layer) {\n    if (!is_layer_locked(layer)) {\n        layer_lock_invert(layer);\n    }\n}\n\nvoid layer_lock_off(uint8_t layer) {\n    if (is_layer_locked(layer)) {\n        layer_lock_invert(layer);\n    }\n}\n\nvoid layer_lock_all_off(void) {\n    layer_and(~locked_layers);\n    locked_layers = 0;\n\n    layer_lock_set_kb(locked_layers);\n}\n\n#else  // NO_ACTION_LAYER\nbool is_layer_locked(uint8_t layer) {\n    return false;\n}\nvoid layer_lock_on(uint8_t layer) {}\nvoid layer_lock_off(uint8_t layer) {}\nvoid layer_lock_all_off(void) {}\nvoid layer_lock_invert(uint8_t layer) {}\nvoid layer_lock_timeout_task(void) {}\nvoid layer_lock_activity_trigger(void) {}\n#endif // NO_ACTION_LAYER\n\n__attribute__((weak)) bool layer_lock_set_kb(layer_state_t locked_layers) {\n    return layer_lock_set_user(locked_layers);\n}\n__attribute__((weak)) bool layer_lock_set_user(layer_state_t locked_layers) {\n    return true;\n}\n\nvoid layer_lock_task(void) {\n    layer_lock_timeout_task();\n}\n"
  },
  {
    "path": "quantum/layer_lock.h",
    "content": "// Copyright 2022-2023 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n/**\n * @file layer_lock.h\n * @brief Layer Lock, a key to stay in the current layer.\n *\n * Overview\n * --------\n *\n * Layers are often accessed by holding a button, e.g. with a momentary layer\n * switch `MO(layer)` or layer tap `LT(layer, key)` key. But you may sometimes\n * want to \"lock\" or \"toggle\" the layer so that it stays on without having to\n * hold down a button. One way to do that is with a tap-toggle `TT` layer key,\n * but here is an alternative.\n *\n * This library implements a \"Layer Lock key\". When tapped, it \"locks\" the\n * highest layer to stay active, assuming the layer was activated by one of the\n * following keys:\n *\n *  * `MO(layer)` momentary layer switch\n *  * `LT(layer, key)` layer tap\n *  * `OSL(layer)` one-shot layer\n *  * `TT(layer)` layer tap toggle\n *  * `LM(layer, mod)` layer-mod key (the layer is locked, but not the mods)\n *\n * Tapping the Layer Lock key again unlocks and turns off the layer.\n *\n * @note When a layer is \"locked\", other layer keys such as `TO(layer)` or\n * manually calling `layer_off(layer)` will override and unlock the layer.\n *\n * Configuration\n * -------------\n *\n * Optionally, a timeout may be defined so that Layer Lock disables\n * automatically if not keys are pressed for `LAYER_LOCK_IDLE_TIMEOUT`\n * milliseconds. Define `LAYER_LOCK_IDLE_TIMEOUT` in your config.h, for instance\n *\n *     #define LAYER_LOCK_IDLE_TIMEOUT 60000  // Turn off after 60 seconds.\n *\n * For full documentation, see\n * <https://getreuer.info/posts/keyboards/layer-lock>\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action_layer.h\"\n#include \"action_util.h\"\n\n/** Returns true if `layer` is currently locked. */\nbool is_layer_locked(uint8_t layer);\n\n/** Locks and turns on `layer`. */\nvoid layer_lock_on(uint8_t layer);\n\n/** Unlocks and turns off `layer`. */\nvoid layer_lock_off(uint8_t layer);\n\n/** Unlocks and turns off all locked layers. */\nvoid layer_lock_all_off(void);\n\n/** Toggles whether `layer` is locked. */\nvoid layer_lock_invert(uint8_t layer);\n\n/**\n * Optional callback that gets called when a layer is locked or unlocked.\n *\n * This is useful to represent the current lock state, e.g. by setting an LED or\n * playing a sound. In your keymap, define\n *\n *     void layer_lock_set_user(layer_state_t locked_layers) {\n *       // Do something like `set_led(is_layer_locked(NAV));`\n *     }\n *\n * @param locked_layers Bitfield in which the kth bit represents whether the\n *                      kth layer is on.\n */\nbool layer_lock_set_kb(layer_state_t locked_layers);\nbool layer_lock_set_user(layer_state_t locked_layers);\n\n/** Handle various background tasks */\nvoid layer_lock_task(void);\n\n/** Update any configured timeouts */\nvoid layer_lock_activity_trigger(void);\n"
  },
  {
    "path": "quantum/leader.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"leader.h\"\n#include \"timer.h\"\n#include \"util.h\"\n\n#include <string.h>\n\n#ifndef LEADER_TIMEOUT\n#    define LEADER_TIMEOUT 300\n#endif\n\n// Leader key stuff\nbool     leading              = false;\nuint16_t leader_time          = 0;\nuint16_t leader_sequence[5]   = {0, 0, 0, 0, 0};\nuint8_t  leader_sequence_size = 0;\n\n__attribute__((weak)) void leader_start_user(void) {}\n\n__attribute__((weak)) void leader_end_user(void) {}\n\n__attribute__((weak)) bool leader_add_user(uint16_t keycode) {\n    return false;\n}\n\nvoid leader_start(void) {\n    if (leading) {\n        return;\n    }\n    leader_start_user();\n    leading              = true;\n    leader_time          = timer_read();\n    leader_sequence_size = 0;\n    memset(leader_sequence, 0, sizeof(leader_sequence));\n}\n\nvoid leader_end(void) {\n    leading = false;\n    leader_end_user();\n}\n\nvoid leader_task(void) {\n    if (leader_sequence_active() && leader_sequence_timed_out()) {\n        leader_end();\n    }\n}\n\nbool leader_sequence_active(void) {\n    return leading;\n}\n\nbool leader_sequence_add(uint16_t keycode) {\n    if (leader_sequence_size >= ARRAY_SIZE(leader_sequence)) {\n        return false;\n    }\n\n#if defined(LEADER_NO_TIMEOUT)\n    if (leader_sequence_size == 0) {\n        leader_reset_timer();\n    }\n#endif\n\n    leader_sequence[leader_sequence_size] = keycode;\n    leader_sequence_size++;\n\n    if (leader_add_user(keycode)) {\n        leader_end();\n    }\n    return true;\n}\n\nbool leader_sequence_timed_out(void) {\n#if defined(LEADER_NO_TIMEOUT)\n    return leader_sequence_size > 0 && timer_elapsed(leader_time) > LEADER_TIMEOUT;\n#else\n    return timer_elapsed(leader_time) > LEADER_TIMEOUT;\n#endif\n}\n\nvoid leader_reset_timer(void) {\n    leader_time = timer_read();\n}\n\nbool leader_sequence_is(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4, uint16_t kc5) {\n    return leader_sequence[0] == kc1 && leader_sequence[1] == kc2 && leader_sequence[2] == kc3 && leader_sequence[3] == kc4 && leader_sequence[4] == kc5;\n}\n\nbool leader_sequence_one_key(uint16_t kc) {\n    return leader_sequence_is(kc, 0, 0, 0, 0);\n}\n\nbool leader_sequence_two_keys(uint16_t kc1, uint16_t kc2) {\n    return leader_sequence_is(kc1, kc2, 0, 0, 0);\n}\n\nbool leader_sequence_three_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3) {\n    return leader_sequence_is(kc1, kc2, kc3, 0, 0);\n}\n\nbool leader_sequence_four_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4) {\n    return leader_sequence_is(kc1, kc2, kc3, kc4, 0);\n}\n\nbool leader_sequence_five_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4, uint16_t kc5) {\n    return leader_sequence_is(kc1, kc2, kc3, kc4, kc5);\n}\n"
  },
  {
    "path": "quantum/leader.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <stdbool.h>\n#include <stdint.h>\n\n/**\n * \\file\n *\n * \\defgroup leader Leader Key\n * \\{\n */\n\n/**\n * \\brief User callback, invoked when the leader sequence begins.\n */\nvoid leader_start_user(void);\n\n/**\n * \\brief User callback, invoked when the leader sequence ends.\n */\nvoid leader_end_user(void);\n\n/**\n * \\brief User callback, invoked when a keycode is added to the leader sequence.\n *\n * \\param keycode The keycode added to the leader sequence.\n *\n * \\return `true` to finish the key sequence, `false` to continue.\n */\nbool leader_add_user(uint16_t keycode);\n\n/**\n * Begin the leader sequence, resetting the buffer and timer.\n */\nvoid leader_start(void);\n\n/**\n * End the leader sequence.\n */\nvoid leader_end(void);\n\nvoid leader_task(void);\n\n/**\n * Whether the leader sequence is active.\n */\nbool leader_sequence_active(void);\n\n/**\n * Add the given keycode to the sequence buffer.\n *\n * If `LEADER_NO_TIMEOUT` is defined, the timer is reset if the buffer is empty.\n *\n * \\param keycode The keycode to add.\n *\n * \\return `true` if the keycode was added, `false` if the buffer is full.\n */\nbool leader_sequence_add(uint16_t keycode);\n\n/**\n * Whether the leader sequence has reached the timeout.\n *\n * If `LEADER_NO_TIMEOUT` is defined, the buffer must also contain at least one key.\n */\nbool leader_sequence_timed_out(void);\n\n/**\n * Reset the leader sequence timer.\n */\nvoid leader_reset_timer(void);\n\n/**\n * Check the sequence buffer for the given keycode.\n *\n * \\param kc The keycode to check.\n *\n * \\return `true` if the sequence buffer matches.\n */\nbool leader_sequence_one_key(uint16_t kc);\n\n/**\n * Check the sequence buffer for the given keycodes.\n *\n * \\param kc1 The first keycode to check.\n * \\param kc2 The second keycode to check.\n *\n * \\return `true` if the sequence buffer matches.\n */\nbool leader_sequence_two_keys(uint16_t kc1, uint16_t kc2);\n\n/**\n * Check the sequence buffer for the given keycodes.\n *\n * \\param kc1 The first keycode to check.\n * \\param kc2 The second keycode to check.\n * \\param kc3 The third keycode to check.\n *\n * \\return `true` if the sequence buffer matches.\n */\nbool leader_sequence_three_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3);\n\n/**\n * Check the sequence buffer for the given keycodes.\n *\n * \\param kc1 The first keycode to check.\n * \\param kc2 The second keycode to check.\n * \\param kc3 The third keycode to check.\n * \\param kc4 The fourth keycode to check.\n *\n * \\return `true` if the sequence buffer matches.\n */\nbool leader_sequence_four_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4);\n\n/**\n * Check the sequence buffer for the given keycodes.\n *\n * \\param kc1 The first keycode to check.\n * \\param kc2 The second keycode to check.\n * \\param kc3 The third keycode to check.\n * \\param kc4 The fourth keycode to check.\n * \\param kc5 The fifth keycode to check.\n *\n * \\return `true` if the sequence buffer matches.\n */\nbool leader_sequence_five_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4, uint16_t kc5);\n\n/** \\} */\n"
  },
  {
    "path": "quantum/led.c",
    "content": "/* Copyright 2020 zvecr<git@zvecr.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"led.h\"\n#include \"host.h\"\n#include \"timer.h\"\n#include \"debug.h\"\n#include \"gpio.h\"\n\n#ifdef BACKLIGHT_CAPS_LOCK\n#    ifdef BACKLIGHT_ENABLE\n#        include \"backlight.h\"\nextern backlight_config_t backlight_config;\n#    else\n#        pragma message \"Cannot use BACKLIGHT_CAPS_LOCK without backlight being enabled\"\n#        undef BACKLIGHT_CAPS_LOCK\n#    endif\n#endif\n\n#ifndef LED_PIN_ON_STATE\n#    define LED_PIN_ON_STATE 1\n#endif\n\n#ifdef BACKLIGHT_CAPS_LOCK\n/** \\brief Caps Lock indicator using backlight (for keyboards without dedicated LED)\n */\nstatic void handle_backlight_caps_lock(led_t led_state) {\n    // Use backlight as Caps Lock indicator\n    uint8_t bl_toggle_lvl = 0;\n\n    if (led_state.caps_lock && !backlight_config.enable) {\n        // Turning Caps Lock ON and backlight is disabled in config\n        // Toggling backlight to the brightest level\n        bl_toggle_lvl = BACKLIGHT_LEVELS;\n    } else if (!led_state.caps_lock && backlight_config.enable) {\n        // Turning Caps Lock OFF and backlight is enabled in config\n        // Toggling backlight and restoring config level\n        bl_toggle_lvl = backlight_config.level;\n    }\n\n    // Set level without modify backlight_config to keep ability to restore state\n    backlight_set(bl_toggle_lvl);\n}\n#endif\n\nstatic uint32_t last_led_modification_time = 0;\n\nuint32_t last_led_activity_time(void) {\n    return last_led_modification_time;\n}\n\nuint32_t last_led_activity_elapsed(void) {\n    return timer_elapsed32(last_led_modification_time);\n}\n\n/** \\brief Lock LED update callback - keymap/user level\n *\n * \\return True if led_update_kb() should run its own code, false otherwise.\n */\n__attribute__((weak)) bool led_update_user(led_t led_state) {\n    return true;\n}\n\n/** \\brief Lock LED update callback - keyboard level\n *\n * \\return Ignored for now.\n */\n__attribute__((weak)) bool led_update_kb(led_t led_state) {\n    bool res = led_update_user(led_state);\n    if (res) {\n        led_update_ports(led_state);\n    }\n    return res;\n}\n\n/** \\brief Write LED state to hardware\n */\n__attribute__((weak)) void led_update_ports(led_t led_state) {\n#if LED_PIN_ON_STATE == 0\n    // invert the whole thing to avoid having to conditionally !led_state.x later\n    led_state.raw = ~led_state.raw;\n#endif\n\n#ifdef LED_NUM_LOCK_PIN\n    gpio_write_pin(LED_NUM_LOCK_PIN, led_state.num_lock);\n#endif\n#ifdef LED_CAPS_LOCK_PIN\n    gpio_write_pin(LED_CAPS_LOCK_PIN, led_state.caps_lock);\n#endif\n#ifdef LED_SCROLL_LOCK_PIN\n    gpio_write_pin(LED_SCROLL_LOCK_PIN, led_state.scroll_lock);\n#endif\n#ifdef LED_COMPOSE_PIN\n    gpio_write_pin(LED_COMPOSE_PIN, led_state.compose);\n#endif\n#ifdef LED_KANA_PIN\n    gpio_write_pin(LED_KANA_PIN, led_state.kana);\n#endif\n}\n\n/** \\brief Initialise any LED related hardware and/or state\n */\n__attribute__((weak)) void led_init_ports(void) {\n#ifdef LED_NUM_LOCK_PIN\n    gpio_set_pin_output(LED_NUM_LOCK_PIN);\n    gpio_write_pin(LED_NUM_LOCK_PIN, !LED_PIN_ON_STATE);\n#endif\n#ifdef LED_CAPS_LOCK_PIN\n    gpio_set_pin_output(LED_CAPS_LOCK_PIN);\n    gpio_write_pin(LED_CAPS_LOCK_PIN, !LED_PIN_ON_STATE);\n#endif\n#ifdef LED_SCROLL_LOCK_PIN\n    gpio_set_pin_output(LED_SCROLL_LOCK_PIN);\n    gpio_write_pin(LED_SCROLL_LOCK_PIN, !LED_PIN_ON_STATE);\n#endif\n#ifdef LED_COMPOSE_PIN\n    gpio_set_pin_output(LED_COMPOSE_PIN);\n    gpio_write_pin(LED_COMPOSE_PIN, !LED_PIN_ON_STATE);\n#endif\n#ifdef LED_KANA_PIN\n    gpio_set_pin_output(LED_KANA_PIN);\n    gpio_write_pin(LED_KANA_PIN, !LED_PIN_ON_STATE);\n#endif\n}\n\n/** \\brief Entrypoint for protocol to LED binding\n */\n__attribute__((weak)) void led_set(uint8_t usb_led) {\n#ifdef BACKLIGHT_CAPS_LOCK\n    handle_backlight_caps_lock((led_t)usb_led);\n#endif\n\n    led_update_kb((led_t)usb_led);\n}\n\n/** \\brief Trigger behaviour on transition to suspend\n */\nvoid led_suspend(void) {\n    led_t leds_off = {0};\n#ifdef BACKLIGHT_CAPS_LOCK\n    if (is_backlight_enabled()) {\n        // Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off\n        leds_off.caps_lock = true;\n    }\n#endif\n    led_set(leds_off.raw);\n}\n\n/** \\brief Trigger behaviour on transition from suspend\n */\nvoid led_wakeup(void) {\n    led_set(host_keyboard_leds());\n}\n\n/** \\brief set host led state\n *\n * Only sets state if change detected\n */\nvoid led_task(void) {\n    static uint8_t last_led_status = 0;\n\n    // update LED\n    uint8_t led_status = host_keyboard_leds();\n    if (last_led_status != led_status) {\n        last_led_status            = led_status;\n        last_led_modification_time = timer_read32();\n\n        if (debug_keyboard) {\n            dprintf(\"led_task: %02X\\n\", led_status);\n        }\n        led_set(led_status);\n    }\n}\n"
  },
  {
    "path": "quantum/led.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n/* FIXME: Add doxygen comments here. */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef union {\n    uint8_t raw;\n    struct {\n        bool    num_lock : 1;\n        bool    caps_lock : 1;\n        bool    scroll_lock : 1;\n        bool    compose : 1;\n        bool    kana : 1;\n        uint8_t reserved : 3;\n    };\n} led_t;\n\nvoid led_set(uint8_t usb_led);\n\nvoid led_init_ports(void);\n\nvoid led_suspend(void);\n\nvoid led_wakeup(void);\n\nvoid led_task(void);\n\n/* Callbacks */\nbool led_update_user(led_t led_state);\nbool led_update_kb(led_t led_state);\nvoid led_update_ports(led_t led_state);\n\nuint32_t last_led_activity_time(void);    // Timestamp of the LED activity\nuint32_t last_led_activity_elapsed(void); // Number of milliseconds since the last LED activity\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/led_matrix/animations/alpha_mods_anim.h",
    "content": "#ifdef ENABLE_LED_MATRIX_ALPHAS_MODS\nLED_MATRIX_EFFECT(ALPHAS_MODS)\n#    ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\n// alphas = val1, mods = val2\nbool ALPHAS_MODS(effect_params_t* params) {\n    LED_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint8_t val1 = led_matrix_eeconfig.val;\n    uint8_t val2 = val1 + led_matrix_eeconfig.speed;\n\n    for (uint8_t i = led_min; i < led_max; i++) {\n        LED_MATRIX_TEST_LED_FLAGS();\n        if (HAS_FLAGS(g_led_config.flags[i], LED_FLAG_MODIFIER)) {\n            led_matrix_set_value(i, val2);\n        } else {\n            led_matrix_set_value(i, val1);\n        }\n    }\n    return led_matrix_check_finished_leds(led_max);\n}\n\n#    endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_LED_MATRIX_ALPHAS_MODS\n"
  },
  {
    "path": "quantum/led_matrix/animations/band_anim.h",
    "content": "#ifdef ENABLE_LED_MATRIX_BAND\nLED_MATRIX_EFFECT(BAND)\n#    ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t BAND_math(uint8_t val, uint8_t i, uint8_t time) {\n    int16_t v = val - abs(scale8(g_led_config.point[i].x, 228) + 28 - time) * 8;\n    return scale8(v < 0 ? 0 : v, val);\n}\n\nbool BAND(effect_params_t* params) {\n    return effect_runner_i(params, &BAND_math);\n}\n\n#    endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_LED_MATRIX_BAND\n"
  },
  {
    "path": "quantum/led_matrix/animations/band_pinwheel_anim.h",
    "content": "#ifdef ENABLE_LED_MATRIX_BAND_PINWHEEL\nLED_MATRIX_EFFECT(BAND_PINWHEEL)\n#    ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t BAND_PINWHEEL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t time) {\n    return scale8(val - time - atan2_8(dy, dx) * 3, val);\n}\n\nbool BAND_PINWHEEL(effect_params_t* params) {\n    return effect_runner_dx_dy(params, &BAND_PINWHEEL_math);\n}\n\n#    endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_LED_MATRIX_BAND_PINWHEEL\n"
  },
  {
    "path": "quantum/led_matrix/animations/band_spiral_anim.h",
    "content": "#ifdef ENABLE_LED_MATRIX_BAND_SPIRAL\nLED_MATRIX_EFFECT(BAND_SPIRAL)\n#    ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t BAND_SPIRAL_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint8_t time) {\n    return scale8(val + dist - time - atan2_8(dy, dx), val);\n}\n\nbool BAND_SPIRAL(effect_params_t* params) {\n    return effect_runner_dx_dy_dist(params, &BAND_SPIRAL_math);\n}\n\n#    endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_LED_MATRIX_BAND_SPIRAL\n"
  },
  {
    "path": "quantum/led_matrix/animations/breathing_anim.h",
    "content": "#ifdef ENABLE_LED_MATRIX_BREATHING\nLED_MATRIX_EFFECT(BREATHING)\n#    ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t BREATHING_math(uint8_t val, uint8_t i, uint8_t time) {\n    return scale8(abs8(sin8(time / 2) - 128) * 2, val);\n}\n\nbool BREATHING(effect_params_t* params) {\n    return effect_runner_i(params, &BREATHING_math);\n}\n\n#    endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_LED_MATRIX_BREATHING\n"
  },
  {
    "path": "quantum/led_matrix/animations/cycle_left_right_anim.h",
    "content": "#ifdef ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT\nLED_MATRIX_EFFECT(CYCLE_LEFT_RIGHT)\n#    ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t CYCLE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) {\n    return scale8(g_led_config.point[i].x - time, val);\n}\n\nbool CYCLE_LEFT_RIGHT(effect_params_t* params) {\n    return effect_runner_i(params, &CYCLE_LEFT_RIGHT_math);\n}\n\n#    endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_LED_MATRIX_CYCLE_LEFT_RIGHT\n"
  },
  {
    "path": "quantum/led_matrix/animations/cycle_out_in_anim.h",
    "content": "#ifdef ENABLE_LED_MATRIX_CYCLE_OUT_IN\nLED_MATRIX_EFFECT(CYCLE_OUT_IN)\n#    ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t CYCLE_OUT_IN_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint8_t time) {\n    return scale8(3 * dist / 2 + time, val);\n}\n\nbool CYCLE_OUT_IN(effect_params_t* params) {\n    return effect_runner_dx_dy_dist(params, &CYCLE_OUT_IN_math);\n}\n\n#    endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_LED_MATRIX_CYCLE_OUT_IN\n"
  },
  {
    "path": "quantum/led_matrix/animations/cycle_up_down_anim.h",
    "content": "#ifdef ENABLE_LED_MATRIX_CYCLE_UP_DOWN\nLED_MATRIX_EFFECT(CYCLE_UP_DOWN)\n#    ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t CYCLE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) {\n    return scale8(g_led_config.point[i].y - time, val);\n}\n\nbool CYCLE_UP_DOWN(effect_params_t* params) {\n    return effect_runner_i(params, &CYCLE_UP_DOWN_math);\n}\n\n#    endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_LED_MATRIX_CYCLE_UP_DOWN\n"
  },
  {
    "path": "quantum/led_matrix/animations/dual_beacon_anim.h",
    "content": "#ifdef ENABLE_LED_MATRIX_DUAL_BEACON\nLED_MATRIX_EFFECT(DUAL_BEACON)\n#    ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t DUAL_BEACON_math(uint8_t val, int8_t sin, int8_t cos, uint8_t i, uint8_t time) {\n    return scale8(((g_led_config.point[i].y - k_led_matrix_center.y) * cos + (g_led_config.point[i].x - k_led_matrix_center.x) * sin) / 128, val);\n}\n\nbool DUAL_BEACON(effect_params_t* params) {\n    return effect_runner_sin_cos_i(params, &DUAL_BEACON_math);\n}\n\n#    endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_LED_MATRIX_DUAL_BEACON\n"
  },
  {
    "path": "quantum/led_matrix/animations/led_matrix_effects.inc",
    "content": "// Add your new core led matrix effect here, order determines enum order\n#include \"solid_anim.h\"\n#include \"alpha_mods_anim.h\"\n#include \"breathing_anim.h\"\n#include \"band_anim.h\"\n#include \"band_pinwheel_anim.h\"\n#include \"band_spiral_anim.h\"\n#include \"cycle_left_right_anim.h\"\n#include \"cycle_up_down_anim.h\"\n#include \"cycle_out_in_anim.h\"\n#include \"dual_beacon_anim.h\"\n#include \"solid_reactive_simple_anim.h\"\n#include \"solid_reactive_wide.h\"\n#include \"solid_reactive_cross.h\"\n#include \"solid_reactive_nexus.h\"\n#include \"solid_splash_anim.h\"\n#include \"wave_left_right_anim.h\"\n#include \"wave_up_down_anim.h\"\n"
  },
  {
    "path": "quantum/led_matrix/animations/runners/effect_runner_dx_dy.h",
    "content": "#pragma once\n\ntypedef uint8_t (*dx_dy_f)(uint8_t val, int16_t dx, int16_t dy, uint8_t time);\n\nbool effect_runner_dx_dy(effect_params_t* params, dx_dy_f effect_func) {\n    LED_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint8_t time = scale16by8(g_led_timer, led_matrix_eeconfig.speed / 2);\n    for (uint8_t i = led_min; i < led_max; i++) {\n        LED_MATRIX_TEST_LED_FLAGS();\n        int16_t dx = g_led_config.point[i].x - k_led_matrix_center.x;\n        int16_t dy = g_led_config.point[i].y - k_led_matrix_center.y;\n        led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, dx, dy, time));\n    }\n    return led_matrix_check_finished_leds(led_max);\n}\n"
  },
  {
    "path": "quantum/led_matrix/animations/runners/effect_runner_dx_dy_dist.h",
    "content": "#pragma once\n\ntypedef uint8_t (*dx_dy_dist_f)(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint8_t time);\n\nbool effect_runner_dx_dy_dist(effect_params_t* params, dx_dy_dist_f effect_func) {\n    LED_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint8_t time = scale16by8(g_led_timer, led_matrix_eeconfig.speed / 2);\n    for (uint8_t i = led_min; i < led_max; i++) {\n        LED_MATRIX_TEST_LED_FLAGS();\n        int16_t dx   = g_led_config.point[i].x - k_led_matrix_center.x;\n        int16_t dy   = g_led_config.point[i].y - k_led_matrix_center.y;\n        uint8_t dist = sqrt16(dx * dx + dy * dy);\n        led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, dx, dy, dist, time));\n    }\n    return led_matrix_check_finished_leds(led_max);\n}\n"
  },
  {
    "path": "quantum/led_matrix/animations/runners/effect_runner_i.h",
    "content": "#pragma once\n\ntypedef uint8_t (*i_f)(uint8_t val, uint8_t i, uint8_t time);\n\nbool effect_runner_i(effect_params_t* params, i_f effect_func) {\n    LED_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint8_t time = scale16by8(g_led_timer, led_matrix_eeconfig.speed / 4);\n    for (uint8_t i = led_min; i < led_max; i++) {\n        LED_MATRIX_TEST_LED_FLAGS();\n        led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, i, time));\n    }\n    return led_matrix_check_finished_leds(led_max);\n}\n"
  },
  {
    "path": "quantum/led_matrix/animations/runners/effect_runner_reactive.h",
    "content": "#pragma once\n\n#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\n\ntypedef uint8_t (*reactive_f)(uint8_t val, uint16_t offset);\n\nbool effect_runner_reactive(effect_params_t* params, reactive_f effect_func) {\n    LED_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint16_t max_tick = 65535 / led_matrix_eeconfig.speed;\n    for (uint8_t i = led_min; i < led_max; i++) {\n        LED_MATRIX_TEST_LED_FLAGS();\n        uint16_t tick = max_tick;\n        // Reverse search to find most recent key hit\n        for (int8_t j = g_last_hit_tracker.count - 1; j >= 0; j--) {\n            if (g_last_hit_tracker.index[j] == i && g_last_hit_tracker.tick[j] < tick) {\n                tick = g_last_hit_tracker.tick[j];\n                break;\n            }\n        }\n\n        uint16_t offset = scale16by8(tick, led_matrix_eeconfig.speed);\n        led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, offset));\n    }\n    return led_matrix_check_finished_leds(led_max);\n}\n\n#endif // LED_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/led_matrix/animations/runners/effect_runner_reactive_splash.h",
    "content": "#pragma once\n\n#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\n\ntypedef uint8_t (*reactive_splash_f)(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick);\n\nbool effect_runner_reactive_splash(uint8_t start, effect_params_t* params, reactive_splash_f effect_func) {\n    LED_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint8_t count = g_last_hit_tracker.count;\n    for (uint8_t i = led_min; i < led_max; i++) {\n        LED_MATRIX_TEST_LED_FLAGS();\n        uint8_t val = 0;\n        for (uint8_t j = start; j < count; j++) {\n            int16_t  dx   = g_led_config.point[i].x - g_last_hit_tracker.x[j];\n            int16_t  dy   = g_led_config.point[i].y - g_last_hit_tracker.y[j];\n            uint8_t  dist = sqrt16(dx * dx + dy * dy);\n            uint16_t tick = scale16by8(g_last_hit_tracker.tick[j], led_matrix_eeconfig.speed);\n            val           = effect_func(val, dx, dy, dist, tick);\n        }\n        led_matrix_set_value(i, scale8(val, led_matrix_eeconfig.val));\n    }\n    return led_matrix_check_finished_leds(led_max);\n}\n\n#endif // LED_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/led_matrix/animations/runners/effect_runner_sin_cos_i.h",
    "content": "#pragma once\n\ntypedef uint8_t (*sin_cos_i_f)(uint8_t val, int8_t sin, int8_t cos, uint8_t i, uint8_t time);\n\nbool effect_runner_sin_cos_i(effect_params_t* params, sin_cos_i_f effect_func) {\n    LED_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint16_t time      = scale16by8(g_led_timer, led_matrix_eeconfig.speed / 4);\n    int8_t   cos_value = cos8(time) - 128;\n    int8_t   sin_value = sin8(time) - 128;\n    for (uint8_t i = led_min; i < led_max; i++) {\n        LED_MATRIX_TEST_LED_FLAGS();\n        led_matrix_set_value(i, effect_func(led_matrix_eeconfig.val, cos_value, sin_value, i, time));\n    }\n    return led_matrix_check_finished_leds(led_max);\n}\n"
  },
  {
    "path": "quantum/led_matrix/animations/runners/led_matrix_runners.inc",
    "content": "#include \"effect_runner_dx_dy_dist.h\"\n#include \"effect_runner_dx_dy.h\"\n#include \"effect_runner_i.h\"\n#include \"effect_runner_sin_cos_i.h\"\n#include \"effect_runner_reactive.h\"\n#include \"effect_runner_reactive_splash.h\"\n"
  },
  {
    "path": "quantum/led_matrix/animations/solid_anim.h",
    "content": "LED_MATRIX_EFFECT(SOLID)\n#ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nbool SOLID(effect_params_t* params) {\n    LED_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint8_t val = led_matrix_eeconfig.val;\n    for (uint8_t i = led_min; i < led_max; i++) {\n        LED_MATRIX_TEST_LED_FLAGS();\n        led_matrix_set_value(i, val);\n    }\n    return led_matrix_check_finished_leds(led_max);\n}\n\n#endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n"
  },
  {
    "path": "quantum/led_matrix/animations/solid_reactive_cross.h",
    "content": "#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\n#    if defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)\n\n#        ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS\nLED_MATRIX_EFFECT(SOLID_REACTIVE_CROSS)\n#        endif\n\n#        ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS\nLED_MATRIX_EFFECT(SOLID_REACTIVE_MULTICROSS)\n#        endif\n\n#        ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t SOLID_REACTIVE_CROSS_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick) {\n    uint16_t effect = tick + dist;\n    dx              = dx < 0 ? dx * -1 : dx;\n    dy              = dy < 0 ? dy * -1 : dy;\n    dx              = dx * 16 > 255 ? 255 : dx * 16;\n    dy              = dy * 16 > 255 ? 255 : dy * 16;\n    effect += dx > dy ? dy : dx;\n    if (effect > 255) effect = 255;\n    return qadd8(val, 255 - effect);\n}\n\n#            ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS\nbool SOLID_REACTIVE_CROSS(effect_params_t* params) {\n    return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_CROSS_math);\n}\n#            endif\n\n#            ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS\nbool SOLID_REACTIVE_MULTICROSS(effect_params_t* params) {\n    return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_CROSS_math);\n}\n#            endif\n\n#        endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS)\n#endif         // LED_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/led_matrix/animations/solid_reactive_nexus.h",
    "content": "#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\n#    if defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS)\n\n#        ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS\nLED_MATRIX_EFFECT(SOLID_REACTIVE_NEXUS)\n#        endif\n\n#        ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS\nLED_MATRIX_EFFECT(SOLID_REACTIVE_MULTINEXUS)\n#        endif\n\n#        ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t SOLID_REACTIVE_NEXUS_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick) {\n    uint16_t effect = tick - dist;\n    if (effect > 255) effect = 255;\n    if (dist > 72) effect = 255;\n    if ((dx > 8 || dx < -8) && (dy > 8 || dy < -8)) effect = 255;\n    return qadd8(val, 255 - effect);\n}\n\n#            ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS\nbool SOLID_REACTIVE_NEXUS(effect_params_t* params) {\n    return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_NEXUS_math);\n}\n#            endif\n\n#            ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS\nbool SOLID_REACTIVE_MULTINEXUS(effect_params_t* params) {\n    return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_NEXUS_math);\n}\n#            endif\n\n#        endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS)\n#endif         // LED_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/led_matrix/animations/solid_reactive_simple_anim.h",
    "content": "#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\n#    ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE\nLED_MATRIX_EFFECT(SOLID_REACTIVE_SIMPLE)\n#        ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t SOLID_REACTIVE_SIMPLE_math(uint8_t val, uint16_t offset) {\n    return scale8(255 - offset, val);\n}\n\nbool SOLID_REACTIVE_SIMPLE(effect_params_t* params) {\n    return effect_runner_reactive(params, &SOLID_REACTIVE_SIMPLE_math);\n}\n\n#        endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE\n#endif         // LED_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/led_matrix/animations/solid_reactive_wide.h",
    "content": "#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\n#    if defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)\n\n#        ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE\nLED_MATRIX_EFFECT(SOLID_REACTIVE_WIDE)\n#        endif\n\n#        ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE\nLED_MATRIX_EFFECT(SOLID_REACTIVE_MULTIWIDE)\n#        endif\n\n#        ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t SOLID_REACTIVE_WIDE_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick) {\n    uint16_t effect = tick + dist * 5;\n    if (effect > 255) effect = 255;\n    return qadd8(val, 255 - effect);\n}\n\n#            ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE\nbool SOLID_REACTIVE_WIDE(effect_params_t* params) {\n    return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_WIDE_math);\n}\n#            endif\n\n#            ifdef ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE\nbool SOLID_REACTIVE_MULTIWIDE(effect_params_t* params) {\n    return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_WIDE_math);\n}\n#            endif\n\n#        endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE)\n#endif         // LED_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/led_matrix/animations/solid_splash_anim.h",
    "content": "#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\n#    if defined(ENABLE_LED_MATRIX_SOLID_SPLASH) || defined(ENABLE_LED_MATRIX_SOLID_MULTISPLASH)\n\n#        ifdef ENABLE_LED_MATRIX_SOLID_SPLASH\nLED_MATRIX_EFFECT(SOLID_SPLASH)\n#        endif\n\n#        ifdef ENABLE_LED_MATRIX_SOLID_MULTISPLASH\nLED_MATRIX_EFFECT(SOLID_MULTISPLASH)\n#        endif\n\n#        ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nuint8_t SOLID_SPLASH_math(uint8_t val, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick) {\n    uint16_t effect = tick - dist;\n    if (effect > 255) effect = 255;\n    return qadd8(val, 255 - effect);\n}\n\n#            ifdef ENABLE_LED_MATRIX_SOLID_SPLASH\nbool SOLID_SPLASH(effect_params_t* params) {\n    return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_SPLASH_math);\n}\n#            endif\n\n#            ifdef ENABLE_LED_MATRIX_SOLID_MULTISPLASH\nbool SOLID_MULTISPLASH(effect_params_t* params) {\n    return effect_runner_reactive_splash(0, params, &SOLID_SPLASH_math);\n}\n#            endif\n\n#        endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // defined(ENABLE_LED_MATRIX_SPLASH) || defined(ENABLE_LED_MATRIX_MULTISPLASH)\n#endif         // LED_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/led_matrix/animations/wave_left_right_anim.h",
    "content": "#ifdef ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT\nLED_MATRIX_EFFECT(WAVE_LEFT_RIGHT)\n#    ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t WAVE_LEFT_RIGHT_math(uint8_t val, uint8_t i, uint8_t time) {\n    return scale8(sin8(g_led_config.point[i].x - time), val);\n}\n\nbool WAVE_LEFT_RIGHT(effect_params_t* params) {\n    return effect_runner_i(params, &WAVE_LEFT_RIGHT_math);\n}\n\n#    endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT\n"
  },
  {
    "path": "quantum/led_matrix/animations/wave_up_down_anim.h",
    "content": "#ifdef ENABLE_LED_MATRIX_WAVE_UP_DOWN\nLED_MATRIX_EFFECT(WAVE_UP_DOWN)\n#    ifdef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t WAVE_UP_DOWN_math(uint8_t val, uint8_t i, uint8_t time) {\n    return scale8(sin8(g_led_config.point[i].y - time), val);\n}\n\nbool WAVE_UP_DOWN(effect_params_t* params) {\n    return effect_runner_i(params, &WAVE_UP_DOWN_math);\n}\n\n#    endif // LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_LED_MATRIX_WAVE_UP_DOWN\n"
  },
  {
    "path": "quantum/led_matrix/led_matrix.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2017 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2019 Clueboard\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"led_matrix.h\"\n#include \"progmem.h\"\n#include \"eeconfig.h\"\n#include \"keyboard.h\"\n#include \"sync_timer.h\"\n#include \"debug.h\"\n#include <string.h>\n#include <math.h>\n#include <stdlib.h>\n#include \"led_tables.h\"\n\n#include <lib/lib8tion/lib8tion.h>\n\n#ifndef LED_MATRIX_CENTER\nconst led_point_t k_led_matrix_center = {112, 32};\n#else\nconst led_point_t k_led_matrix_center = LED_MATRIX_CENTER;\n#endif\n\n// Generic effect runners\n#include \"led_matrix_runners.inc\"\n\n// ------------------------------------------\n// -----Begin led effect includes macros-----\n#define LED_MATRIX_EFFECT(name)\n#define LED_MATRIX_CUSTOM_EFFECT_IMPLS\n\n#include \"led_matrix_effects.inc\"\n#ifdef COMMUNITY_MODULES_ENABLE\n#    include \"led_matrix_community_modules.inc\"\n#endif\n#ifdef LED_MATRIX_CUSTOM_KB\n#    include \"led_matrix_kb.inc\"\n#endif\n#ifdef LED_MATRIX_CUSTOM_USER\n#    include \"led_matrix_user.inc\"\n#endif\n\n#undef LED_MATRIX_CUSTOM_EFFECT_IMPLS\n#undef LED_MATRIX_EFFECT\n// -----End led effect includes macros-------\n// ------------------------------------------\n\n// globals\nled_eeconfig_t led_matrix_eeconfig; // TODO: would like to prefix this with g_ for global consistancy, do this in another pr\nuint32_t       g_led_timer;\n#ifdef LED_MATRIX_FRAMEBUFFER_EFFECTS\nuint8_t g_led_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}};\n#endif // LED_MATRIX_FRAMEBUFFER_EFFECTS\n#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\nlast_hit_t g_last_hit_tracker;\n#endif // LED_MATRIX_KEYREACTIVE_ENABLED\n\n// internals\nstatic bool            suspend_state     = false;\nstatic uint8_t         led_last_enable   = UINT8_MAX;\nstatic uint8_t         led_last_effect   = UINT8_MAX;\nstatic effect_params_t led_effect_params = {0, LED_FLAG_ALL, false};\nstatic led_task_states led_task_state    = SYNCING;\n\n// double buffers\nstatic uint32_t led_timer_buffer;\n#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\nstatic last_hit_t last_hit_buffer;\n#endif // LED_MATRIX_KEYREACTIVE_ENABLED\n\n// split led matrix\n#if defined(LED_MATRIX_SPLIT)\nconst uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT;\n#endif\n\nEECONFIG_DEBOUNCE_HELPER(led_matrix, led_matrix_eeconfig);\n\nvoid eeconfig_force_flush_led_matrix(void) {\n    eeconfig_flush_led_matrix(true);\n}\n\nvoid eeconfig_update_led_matrix_default(void) {\n    dprintf(\"eeconfig_update_led_matrix_default\\n\");\n    led_matrix_eeconfig.enable = LED_MATRIX_DEFAULT_ON;\n    led_matrix_eeconfig.mode   = LED_MATRIX_DEFAULT_MODE;\n    led_matrix_eeconfig.val    = LED_MATRIX_DEFAULT_VAL;\n    led_matrix_eeconfig.speed  = LED_MATRIX_DEFAULT_SPD;\n    led_matrix_eeconfig.flags  = LED_MATRIX_DEFAULT_FLAGS;\n    eeconfig_flush_led_matrix(true);\n}\n\nvoid eeconfig_debug_led_matrix(void) {\n    dprintf(\"led_matrix_eeconfig EEPROM\\n\");\n    dprintf(\"led_matrix_eeconfig.enable = %d\\n\", led_matrix_eeconfig.enable);\n    dprintf(\"led_matrix_eeconfig.mode = %d\\n\", led_matrix_eeconfig.mode);\n    dprintf(\"led_matrix_eeconfig.val = %d\\n\", led_matrix_eeconfig.val);\n    dprintf(\"led_matrix_eeconfig.speed = %d\\n\", led_matrix_eeconfig.speed);\n    dprintf(\"led_matrix_eeconfig.flags = %d\\n\", led_matrix_eeconfig.flags);\n}\n\nvoid led_matrix_reload_from_eeprom(void) {\n    led_matrix_disable_noeeprom();\n    /* Reset back to what we have in eeprom */\n    eeconfig_init_led_matrix();\n    eeconfig_debug_led_matrix(); // display current eeprom values\n    if (led_matrix_eeconfig.enable) {\n        led_matrix_mode_noeeprom(led_matrix_eeconfig.mode);\n    }\n}\n\n__attribute__((weak)) uint8_t led_matrix_map_row_column_to_led_kb(uint8_t row, uint8_t column, uint8_t *led_i) {\n    return 0;\n}\n\nuint8_t led_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i) {\n    uint8_t led_count = led_matrix_map_row_column_to_led_kb(row, column, led_i);\n    uint8_t led_index = g_led_config.matrix_co[row][column];\n    if (led_index != NO_LED) {\n        led_i[led_count] = led_index;\n        led_count++;\n    }\n    return led_count;\n}\n\nvoid led_matrix_update_pwm_buffers(void) {\n    led_matrix_driver.flush();\n}\n\n__attribute__((weak)) int led_matrix_led_index(int index) {\n#if defined(LED_MATRIX_SPLIT)\n    if (!is_keyboard_left() && index >= k_led_matrix_split[0]) {\n        return index - k_led_matrix_split[0];\n    }\n#endif\n    return index;\n}\n\nvoid led_matrix_set_value(int index, uint8_t value) {\n#ifdef USE_CIE1931_CURVE\n    value = pgm_read_byte(&CIE1931_CURVE[value]);\n#endif\n    led_matrix_driver.set_value(led_matrix_led_index(index), value);\n}\n\nvoid led_matrix_set_value_all(uint8_t value) {\n#if defined(LED_MATRIX_SPLIT)\n    for (uint8_t i = 0; i < LED_MATRIX_LED_COUNT; i++)\n        led_matrix_set_value(i, value);\n#else\n#    ifdef USE_CIE1931_CURVE\n    led_matrix_driver.set_value_all(pgm_read_byte(&CIE1931_CURVE[value]));\n#    else\n    led_matrix_driver.set_value_all(value);\n#    endif\n#endif\n}\n\nvoid led_matrix_handle_key_event(uint8_t row, uint8_t col, bool pressed) {\n#ifndef LED_MATRIX_SPLIT\n    if (!is_keyboard_master()) return;\n#endif\n\n#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\n    uint8_t led[LED_HITS_TO_REMEMBER];\n    uint8_t led_count = 0;\n\n#    if defined(LED_MATRIX_KEYRELEASES)\n    if (!pressed)\n#    elif defined(LED_MATRIX_KEYPRESSES)\n    if (pressed)\n#    endif // defined(LED_MATRIX_KEYRELEASES)\n    {\n        led_count = led_matrix_map_row_column_to_led(row, col, led);\n    }\n\n    if (last_hit_buffer.count + led_count > LED_HITS_TO_REMEMBER) {\n        memcpy(&last_hit_buffer.x[0], &last_hit_buffer.x[led_count], LED_HITS_TO_REMEMBER - led_count);\n        memcpy(&last_hit_buffer.y[0], &last_hit_buffer.y[led_count], LED_HITS_TO_REMEMBER - led_count);\n        memcpy(&last_hit_buffer.tick[0], &last_hit_buffer.tick[led_count], (LED_HITS_TO_REMEMBER - led_count) * 2); // 16 bit\n        memcpy(&last_hit_buffer.index[0], &last_hit_buffer.index[led_count], LED_HITS_TO_REMEMBER - led_count);\n        last_hit_buffer.count = LED_HITS_TO_REMEMBER - led_count;\n    }\n\n    for (uint8_t i = 0; i < led_count; i++) {\n        uint8_t index                = last_hit_buffer.count;\n        last_hit_buffer.x[index]     = g_led_config.point[led[i]].x;\n        last_hit_buffer.y[index]     = g_led_config.point[led[i]].y;\n        last_hit_buffer.index[index] = led[i];\n        last_hit_buffer.tick[index]  = 0;\n        last_hit_buffer.count++;\n    }\n#endif // LED_MATRIX_KEYREACTIVE_ENABLED\n\n#if defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)\n    if (led_matrix_eeconfig.mode == LED_MATRIX_TYPING_HEATMAP) {\n        process_led_matrix_typing_heatmap(row, col);\n    }\n#endif // defined(LED_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_LED_MATRIX_TYPING_HEATMAP)\n}\n\nstatic bool led_matrix_none(effect_params_t *params) {\n    if (!params->init) {\n        return false;\n    }\n\n    led_matrix_set_value_all(0);\n    return false;\n}\n\nstatic void led_task_timers(void) {\n#if defined(LED_MATRIX_KEYREACTIVE_ENABLED)\n    uint32_t deltaTime = sync_timer_elapsed32(led_timer_buffer);\n#endif // defined(LED_MATRIX_KEYREACTIVE_ENABLED)\n    led_timer_buffer = sync_timer_read32();\n\n    // Update double buffer last hit timers\n#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\n    uint8_t count = last_hit_buffer.count;\n    for (uint8_t i = 0; i < count; ++i) {\n        if (UINT16_MAX - deltaTime < last_hit_buffer.tick[i]) {\n            last_hit_buffer.count--;\n            continue;\n        }\n        last_hit_buffer.tick[i] += deltaTime;\n    }\n#endif // LED_MATRIX_KEYREACTIVE_ENABLED\n}\n\nstatic void led_task_sync(void) {\n    eeconfig_flush_led_matrix(false);\n    // next task\n    if (sync_timer_elapsed32(g_led_timer) >= LED_MATRIX_LED_FLUSH_LIMIT) led_task_state = STARTING;\n}\n\nstatic void led_task_start(void) {\n    // reset iter\n    led_effect_params.iter = 0;\n\n    // update double buffers\n    g_led_timer = led_timer_buffer;\n#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\n    g_last_hit_tracker = last_hit_buffer;\n#endif // LED_MATRIX_KEYREACTIVE_ENABLED\n\n    // next task\n    led_task_state = RENDERING;\n}\n\nstatic void led_task_render(uint8_t effect) {\n    bool rendering         = false;\n    led_effect_params.init = (effect != led_last_effect) || (led_matrix_eeconfig.enable != led_last_enable);\n    if (led_effect_params.flags != led_matrix_eeconfig.flags) {\n        led_effect_params.flags = led_matrix_eeconfig.flags;\n        led_matrix_set_value_all(0);\n    }\n\n    // each effect can opt to do calculations\n    // and/or request PWM buffer updates.\n    switch (effect) {\n        case LED_MATRIX_NONE:\n            rendering = led_matrix_none(&led_effect_params);\n            break;\n\n// ---------------------------------------------\n// -----Begin led effect switch case macros-----\n#define LED_MATRIX_EFFECT(name, ...)          \\\n    case LED_MATRIX_##name:                   \\\n        rendering = name(&led_effect_params); \\\n        break;\n#include \"led_matrix_effects.inc\"\n#undef LED_MATRIX_EFFECT\n\n#ifdef COMMUNITY_MODULES_ENABLE\n#    define LED_MATRIX_EFFECT(name, ...)          \\\n        case LED_MATRIX_COMMUNITY_MODULE_##name:  \\\n            rendering = name(&led_effect_params); \\\n            break;\n#    include \"led_matrix_community_modules.inc\"\n#    undef LED_MATRIX_EFFECT\n#endif\n\n#if defined(LED_MATRIX_CUSTOM_KB) || defined(LED_MATRIX_CUSTOM_USER)\n#    define LED_MATRIX_EFFECT(name, ...)          \\\n        case LED_MATRIX_CUSTOM_##name:            \\\n            rendering = name(&led_effect_params); \\\n            break;\n#    ifdef LED_MATRIX_CUSTOM_KB\n#        include \"led_matrix_kb.inc\"\n#    endif\n#    ifdef LED_MATRIX_CUSTOM_USER\n#        include \"led_matrix_user.inc\"\n#    endif\n#    undef LED_MATRIX_EFFECT\n#endif\n            // -----End led effect switch case macros-------\n            // ---------------------------------------------\n    }\n\n    led_effect_params.iter++;\n\n    // next task\n    if (!rendering) {\n        led_task_state = FLUSHING;\n        if (!led_effect_params.init && effect == LED_MATRIX_NONE) {\n            // We only need to flush once if we are LED_MATRIX_NONE\n            led_task_state = SYNCING;\n        }\n    }\n}\n\nstatic void led_task_flush(uint8_t effect) {\n    // update last trackers after the first full render so we can init over several frames\n    led_last_effect = effect;\n    led_last_enable = led_matrix_eeconfig.enable;\n\n    // update pwm buffers\n    led_matrix_update_pwm_buffers();\n\n    // next task\n    led_task_state = SYNCING;\n}\n\nvoid led_matrix_task(void) {\n    led_task_timers();\n\n    // Ideally we would also stop sending zeros to the LED driver PWM buffers\n    // while suspended and just do a software shutdown. This is a cheap hack for now.\n    bool suspend_backlight = suspend_state ||\n#if LED_MATRIX_TIMEOUT > 0\n                             (last_input_activity_elapsed() > (uint32_t)LED_MATRIX_TIMEOUT) ||\n#endif // LED_MATRIX_TIMEOUT > 0\n                             false;\n\n    uint8_t effect = suspend_backlight || !led_matrix_eeconfig.enable ? 0 : led_matrix_eeconfig.mode;\n\n    switch (led_task_state) {\n        case STARTING:\n            led_task_start();\n            break;\n        case RENDERING:\n            led_task_render(effect);\n            if (effect) {\n                if (led_task_state == FLUSHING) {\n                    led_matrix_indicators(); // ensure we only draw basic indicators once rendering is finished\n                }\n                led_matrix_indicators_advanced(&led_effect_params);\n            }\n            break;\n        case FLUSHING:\n            led_task_flush(effect);\n            break;\n        case SYNCING:\n            led_task_sync();\n            break;\n    }\n}\n\n__attribute__((weak)) bool led_matrix_indicators_modules(void) {\n    return true;\n}\n\nvoid led_matrix_indicators(void) {\n    led_matrix_indicators_modules();\n    led_matrix_indicators_kb();\n}\n\n__attribute__((weak)) bool led_matrix_indicators_kb(void) {\n    return led_matrix_indicators_user();\n}\n\n__attribute__((weak)) bool led_matrix_indicators_user(void) {\n    return true;\n}\n\n__attribute__((weak)) bool led_matrix_indicators_advanced_modules(uint8_t led_min, uint8_t led_max) {\n    return true;\n}\n\nvoid led_matrix_indicators_advanced(effect_params_t *params) {\n    /* special handling is needed for \"params->iter\", since it's already been incremented.\n     * Could move the invocations to led_task_render, but then it's missing a few checks\n     * and not sure which would be better. Otherwise, this should be called from\n     * led_task_render, right before the iter++ line.\n     */\n    LED_MATRIX_USE_LIMITS_ITER(min, max, params->iter - 1);\n    led_matrix_indicators_advanced_modules(min, max);\n    led_matrix_indicators_advanced_kb(min, max);\n}\n\n__attribute__((weak)) bool led_matrix_indicators_advanced_kb(uint8_t led_min, uint8_t led_max) {\n    return led_matrix_indicators_advanced_user(led_min, led_max);\n}\n\n__attribute__((weak)) bool led_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {\n    return true;\n}\n\nstruct led_matrix_limits_t led_matrix_get_limits(uint8_t iter) {\n    struct led_matrix_limits_t limits = {0};\n#if defined(LED_MATRIX_LED_PROCESS_LIMIT) && LED_MATRIX_LED_PROCESS_LIMIT > 0 && LED_MATRIX_LED_PROCESS_LIMIT < LED_MATRIX_LED_COUNT\n#    if defined(LED_MATRIX_SPLIT)\n    limits.led_min_index = LED_MATRIX_LED_PROCESS_LIMIT * (iter);\n    limits.led_max_index = limits.led_min_index + LED_MATRIX_LED_PROCESS_LIMIT;\n    if (limits.led_max_index > LED_MATRIX_LED_COUNT) limits.led_max_index = LED_MATRIX_LED_COUNT;\n    if (is_keyboard_left() && (limits.led_max_index > k_led_matrix_split[0])) limits.led_max_index = k_led_matrix_split[0];\n    if (!(is_keyboard_left()) && (limits.led_min_index < k_led_matrix_split[0])) limits.led_min_index = k_led_matrix_split[0];\n#    else\n    limits.led_min_index = LED_MATRIX_LED_PROCESS_LIMIT * (iter);\n    limits.led_max_index = limits.led_min_index + LED_MATRIX_LED_PROCESS_LIMIT;\n    if (limits.led_max_index > LED_MATRIX_LED_COUNT) limits.led_max_index = LED_MATRIX_LED_COUNT;\n#    endif\n#else\n#    if defined(LED_MATRIX_SPLIT)\n    limits.led_min_index = 0;\n    limits.led_max_index = LED_MATRIX_LED_COUNT;\n    if (is_keyboard_left() && (limits.led_max_index > k_led_matrix_split[0])) limits.led_max_index = k_led_matrix_split[0];\n    if (!(is_keyboard_left()) && (limits.led_min_index < k_led_matrix_split[0])) limits.led_min_index = k_led_matrix_split[0];\n#    else\n    limits.led_min_index = 0;\n    limits.led_max_index = LED_MATRIX_LED_COUNT;\n#    endif\n#endif\n    return limits;\n}\n\nvoid led_matrix_init(void) {\n    led_matrix_driver.init();\n\n#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\n    g_last_hit_tracker.count = 0;\n    for (uint8_t i = 0; i < LED_HITS_TO_REMEMBER; ++i) {\n        g_last_hit_tracker.tick[i] = UINT16_MAX;\n    }\n\n    last_hit_buffer.count = 0;\n    for (uint8_t i = 0; i < LED_HITS_TO_REMEMBER; ++i) {\n        last_hit_buffer.tick[i] = UINT16_MAX;\n    }\n#endif // LED_MATRIX_KEYREACTIVE_ENABLED\n\n    eeconfig_init_led_matrix();\n    if (!led_matrix_eeconfig.mode) {\n        dprintf(\"led_matrix_init_drivers led_matrix_eeconfig.mode = 0. Write default values to EEPROM.\\n\");\n        eeconfig_update_led_matrix_default();\n    }\n    eeconfig_debug_led_matrix(); // display current eeprom values\n}\n\nvoid led_matrix_set_suspend_state(bool state) {\n#ifdef LED_MATRIX_SLEEP\n    if (state && !suspend_state && is_keyboard_master()) { // only run if turning off, and only once\n        led_task_render(0);                                // turn off all LEDs when suspending\n        led_task_flush(0);                                 // and actually flash led state to LEDs\n    }\n    suspend_state = state;\n#endif\n}\n\nbool led_matrix_get_suspend_state(void) {\n    return suspend_state;\n}\n\nvoid led_matrix_toggle_eeprom_helper(bool write_to_eeprom) {\n    led_matrix_eeconfig.enable ^= 1;\n    led_task_state = STARTING;\n    eeconfig_flag_led_matrix(write_to_eeprom);\n    dprintf(\"led matrix toggle [%s]: led_matrix_eeconfig.enable = %u\\n\", (write_to_eeprom) ? \"EEPROM\" : \"NOEEPROM\", led_matrix_eeconfig.enable);\n}\nvoid led_matrix_toggle_noeeprom(void) {\n    led_matrix_toggle_eeprom_helper(false);\n}\nvoid led_matrix_toggle(void) {\n    led_matrix_toggle_eeprom_helper(true);\n}\n\nvoid led_matrix_enable(void) {\n    led_matrix_enable_noeeprom();\n    eeconfig_flag_led_matrix(true);\n}\n\nvoid led_matrix_enable_noeeprom(void) {\n    if (!led_matrix_eeconfig.enable) led_task_state = STARTING;\n    led_matrix_eeconfig.enable = 1;\n}\n\nvoid led_matrix_disable(void) {\n    led_matrix_disable_noeeprom();\n    eeconfig_flag_led_matrix(true);\n}\n\nvoid led_matrix_disable_noeeprom(void) {\n    if (led_matrix_eeconfig.enable) led_task_state = STARTING;\n    led_matrix_eeconfig.enable = 0;\n}\n\nuint8_t led_matrix_is_enabled(void) {\n    return led_matrix_eeconfig.enable;\n}\n\nvoid led_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {\n    if (!led_matrix_eeconfig.enable) {\n        return;\n    }\n    if (mode < 1) {\n        led_matrix_eeconfig.mode = 1;\n    } else if (mode >= LED_MATRIX_EFFECT_MAX) {\n        led_matrix_eeconfig.mode = LED_MATRIX_EFFECT_MAX - 1;\n    } else {\n        led_matrix_eeconfig.mode = mode;\n    }\n    led_task_state = STARTING;\n    eeconfig_flag_led_matrix(write_to_eeprom);\n    dprintf(\"led matrix mode [%s]: %u\\n\", (write_to_eeprom) ? \"EEPROM\" : \"NOEEPROM\", led_matrix_eeconfig.mode);\n}\nvoid led_matrix_mode_noeeprom(uint8_t mode) {\n    led_matrix_mode_eeprom_helper(mode, false);\n}\nvoid led_matrix_mode(uint8_t mode) {\n    led_matrix_mode_eeprom_helper(mode, true);\n}\n\nuint8_t led_matrix_get_mode(void) {\n    return led_matrix_eeconfig.mode;\n}\n\nvoid led_matrix_step_helper(bool write_to_eeprom) {\n    uint8_t mode = led_matrix_eeconfig.mode + 1;\n    led_matrix_mode_eeprom_helper((mode < LED_MATRIX_EFFECT_MAX) ? mode : 1, write_to_eeprom);\n}\nvoid led_matrix_step_noeeprom(void) {\n    led_matrix_step_helper(false);\n}\nvoid led_matrix_step(void) {\n    led_matrix_step_helper(true);\n}\n\nvoid led_matrix_step_reverse_helper(bool write_to_eeprom) {\n    uint8_t mode = led_matrix_eeconfig.mode - 1;\n    led_matrix_mode_eeprom_helper((mode < 1) ? LED_MATRIX_EFFECT_MAX - 1 : mode, write_to_eeprom);\n}\nvoid led_matrix_step_reverse_noeeprom(void) {\n    led_matrix_step_reverse_helper(false);\n}\nvoid led_matrix_step_reverse(void) {\n    led_matrix_step_reverse_helper(true);\n}\n\nvoid led_matrix_set_val_eeprom_helper(uint8_t val, bool write_to_eeprom) {\n    if (!led_matrix_eeconfig.enable) {\n        return;\n    }\n    led_matrix_eeconfig.val = (val > LED_MATRIX_MAXIMUM_BRIGHTNESS) ? LED_MATRIX_MAXIMUM_BRIGHTNESS : val;\n    eeconfig_flag_led_matrix(write_to_eeprom);\n    dprintf(\"led matrix set val [%s]: %u\\n\", (write_to_eeprom) ? \"EEPROM\" : \"NOEEPROM\", led_matrix_eeconfig.val);\n}\nvoid led_matrix_set_val_noeeprom(uint8_t val) {\n    led_matrix_set_val_eeprom_helper(val, false);\n}\nvoid led_matrix_set_val(uint8_t val) {\n    led_matrix_set_val_eeprom_helper(val, true);\n}\n\nuint8_t led_matrix_get_val(void) {\n    return led_matrix_eeconfig.val;\n}\n\nvoid led_matrix_increase_val_helper(bool write_to_eeprom) {\n    led_matrix_set_val_eeprom_helper(qadd8(led_matrix_eeconfig.val, LED_MATRIX_VAL_STEP), write_to_eeprom);\n}\nvoid led_matrix_increase_val_noeeprom(void) {\n    led_matrix_increase_val_helper(false);\n}\nvoid led_matrix_increase_val(void) {\n    led_matrix_increase_val_helper(true);\n}\n\nvoid led_matrix_decrease_val_helper(bool write_to_eeprom) {\n    led_matrix_set_val_eeprom_helper(qsub8(led_matrix_eeconfig.val, LED_MATRIX_VAL_STEP), write_to_eeprom);\n}\nvoid led_matrix_decrease_val_noeeprom(void) {\n    led_matrix_decrease_val_helper(false);\n}\nvoid led_matrix_decrease_val(void) {\n    led_matrix_decrease_val_helper(true);\n}\n\nvoid led_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {\n    led_matrix_eeconfig.speed = speed;\n    eeconfig_flag_led_matrix(write_to_eeprom);\n    dprintf(\"led matrix set speed [%s]: %u\\n\", (write_to_eeprom) ? \"EEPROM\" : \"NOEEPROM\", led_matrix_eeconfig.speed);\n}\nvoid led_matrix_set_speed_noeeprom(uint8_t speed) {\n    led_matrix_set_speed_eeprom_helper(speed, false);\n}\nvoid led_matrix_set_speed(uint8_t speed) {\n    led_matrix_set_speed_eeprom_helper(speed, true);\n}\n\nuint8_t led_matrix_get_speed(void) {\n    return led_matrix_eeconfig.speed;\n}\n\nvoid led_matrix_increase_speed_helper(bool write_to_eeprom) {\n    led_matrix_set_speed_eeprom_helper(qadd8(led_matrix_eeconfig.speed, LED_MATRIX_SPD_STEP), write_to_eeprom);\n}\nvoid led_matrix_increase_speed_noeeprom(void) {\n    led_matrix_increase_speed_helper(false);\n}\nvoid led_matrix_increase_speed(void) {\n    led_matrix_increase_speed_helper(true);\n}\n\nvoid led_matrix_decrease_speed_helper(bool write_to_eeprom) {\n    led_matrix_set_speed_eeprom_helper(qsub8(led_matrix_eeconfig.speed, LED_MATRIX_SPD_STEP), write_to_eeprom);\n}\nvoid led_matrix_decrease_speed_noeeprom(void) {\n    led_matrix_decrease_speed_helper(false);\n}\nvoid led_matrix_decrease_speed(void) {\n    led_matrix_decrease_speed_helper(true);\n}\n\nvoid led_matrix_set_flags_eeprom_helper(led_flags_t flags, bool write_to_eeprom) {\n    led_matrix_eeconfig.flags = flags;\n    eeconfig_flag_led_matrix(write_to_eeprom);\n    dprintf(\"led matrix set flags [%s]: %u\\n\", (write_to_eeprom) ? \"EEPROM\" : \"NOEEPROM\", led_matrix_eeconfig.flags);\n}\n\nled_flags_t led_matrix_get_flags(void) {\n    return led_matrix_eeconfig.flags;\n}\n\nvoid led_matrix_set_flags(led_flags_t flags) {\n    led_matrix_set_flags_eeprom_helper(flags, true);\n}\n\nvoid led_matrix_set_flags_noeeprom(led_flags_t flags) {\n    led_matrix_set_flags_eeprom_helper(flags, false);\n}\n"
  },
  {
    "path": "quantum/led_matrix/led_matrix.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2017 Jack Humbert\n * Copyright 2018 Yiancar\n * Copyright 2019 Clueboard\n * Copyright 2021 Leo Deng\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"led_matrix_types.h\"\n#include \"led_matrix_drivers.h\"\n#include \"keyboard.h\"\n\n#ifndef LED_MATRIX_TIMEOUT\n#    define LED_MATRIX_TIMEOUT 0\n#endif\n\n#ifndef LED_MATRIX_MAXIMUM_BRIGHTNESS\n#    define LED_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX\n#endif\n\n#ifndef LED_MATRIX_VAL_STEP\n#    define LED_MATRIX_VAL_STEP 8\n#endif\n\n#ifndef LED_MATRIX_SPD_STEP\n#    define LED_MATRIX_SPD_STEP 16\n#endif\n\n#ifndef LED_MATRIX_DEFAULT_ON\n#    define LED_MATRIX_DEFAULT_ON true\n#endif\n\n#ifndef LED_MATRIX_DEFAULT_MODE\n#    define LED_MATRIX_DEFAULT_MODE LED_MATRIX_SOLID\n#endif\n\n#ifndef LED_MATRIX_DEFAULT_VAL\n#    define LED_MATRIX_DEFAULT_VAL LED_MATRIX_MAXIMUM_BRIGHTNESS\n#endif\n\n#ifndef LED_MATRIX_DEFAULT_SPD\n#    define LED_MATRIX_DEFAULT_SPD UINT8_MAX / 2\n#endif\n\n#ifndef LED_MATRIX_DEFAULT_FLAGS\n#    define LED_MATRIX_DEFAULT_FLAGS LED_FLAG_ALL\n#endif\n\n#ifndef LED_MATRIX_LED_FLUSH_LIMIT\n#    define LED_MATRIX_LED_FLUSH_LIMIT 16\n#endif\n\n#ifndef LED_MATRIX_LED_PROCESS_LIMIT\n#    define LED_MATRIX_LED_PROCESS_LIMIT ((LED_MATRIX_LED_COUNT + 4) / 5)\n#endif\n\nstruct led_matrix_limits_t {\n    uint8_t led_min_index;\n    uint8_t led_max_index;\n};\n\nstruct led_matrix_limits_t led_matrix_get_limits(uint8_t iter);\n\n#define LED_MATRIX_USE_LIMITS_ITER(min, max, iter)                   \\\n    struct led_matrix_limits_t limits = led_matrix_get_limits(iter); \\\n    uint8_t                    min    = limits.led_min_index;        \\\n    uint8_t                    max    = limits.led_max_index;        \\\n    (void)min;                                                       \\\n    (void)max;\n\n#define LED_MATRIX_USE_LIMITS(min, max) LED_MATRIX_USE_LIMITS_ITER(min, max, params->iter)\n\n#define LED_MATRIX_TEST_LED_FLAGS() \\\n    if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) continue\n\nenum led_matrix_effects {\n    LED_MATRIX_NONE = 0,\n\n// --------------------------------------\n// -----Begin led effect enum macros-----\n#define LED_MATRIX_EFFECT(name, ...) LED_MATRIX_##name,\n#include \"led_matrix_effects.inc\"\n#undef LED_MATRIX_EFFECT\n\n#ifdef COMMUNITY_MODULES_ENABLE\n#    define LED_MATRIX_EFFECT(name, ...) LED_MATRIX_COMMUNITY_MODULE_##name,\n#    include \"led_matrix_community_modules.inc\"\n#    undef LED_MATRIX_EFFECT\n#endif\n\n#if defined(LED_MATRIX_CUSTOM_KB) || defined(LED_MATRIX_CUSTOM_USER)\n#    define LED_MATRIX_EFFECT(name, ...) LED_MATRIX_CUSTOM_##name,\n#    ifdef LED_MATRIX_CUSTOM_KB\n#        include \"led_matrix_kb.inc\"\n#    endif\n#    ifdef LED_MATRIX_CUSTOM_USER\n#        include \"led_matrix_user.inc\"\n#    endif\n#    undef LED_MATRIX_EFFECT\n#endif\n    // --------------------------------------\n    // -----End led effect enum macros-------\n\n    LED_MATRIX_EFFECT_MAX\n};\n\nvoid eeconfig_update_led_matrix_default(void);\nvoid eeconfig_force_flush_led_matrix(void);\nvoid eeconfig_debug_led_matrix(void);\n\nuint8_t led_matrix_map_row_column_to_led_kb(uint8_t row, uint8_t column, uint8_t *led_i);\nuint8_t led_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i);\n\nint led_matrix_led_index(int index);\n\nvoid led_matrix_set_value(int index, uint8_t value);\nvoid led_matrix_set_value_all(uint8_t value);\n\nvoid led_matrix_handle_key_event(uint8_t row, uint8_t col, bool pressed);\n\nvoid led_matrix_task(void);\n\n// This runs after another backlight effect and replaces\n// values already set\nvoid led_matrix_indicators(void);\nbool led_matrix_indicators_kb(void);\nbool led_matrix_indicators_user(void);\n\nvoid led_matrix_indicators_advanced(effect_params_t *params);\nbool led_matrix_indicators_advanced_kb(uint8_t led_min, uint8_t led_max);\nbool led_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max);\n\nvoid led_matrix_init(void);\n\nvoid led_matrix_reload_from_eeprom(void);\n\nvoid        led_matrix_set_suspend_state(bool state);\nbool        led_matrix_get_suspend_state(void);\nvoid        led_matrix_toggle(void);\nvoid        led_matrix_toggle_noeeprom(void);\nvoid        led_matrix_enable(void);\nvoid        led_matrix_enable_noeeprom(void);\nvoid        led_matrix_disable(void);\nvoid        led_matrix_disable_noeeprom(void);\nuint8_t     led_matrix_is_enabled(void);\nvoid        led_matrix_mode(uint8_t mode);\nvoid        led_matrix_mode_noeeprom(uint8_t mode);\nuint8_t     led_matrix_get_mode(void);\nvoid        led_matrix_step(void);\nvoid        led_matrix_step_noeeprom(void);\nvoid        led_matrix_step_reverse(void);\nvoid        led_matrix_step_reverse_noeeprom(void);\nvoid        led_matrix_set_val(uint8_t val);\nvoid        led_matrix_set_val_noeeprom(uint8_t val);\nuint8_t     led_matrix_get_val(void);\nvoid        led_matrix_increase_val(void);\nvoid        led_matrix_increase_val_noeeprom(void);\nvoid        led_matrix_decrease_val(void);\nvoid        led_matrix_decrease_val_noeeprom(void);\nvoid        led_matrix_set_speed(uint8_t speed);\nvoid        led_matrix_set_speed_noeeprom(uint8_t speed);\nuint8_t     led_matrix_get_speed(void);\nvoid        led_matrix_increase_speed(void);\nvoid        led_matrix_increase_speed_noeeprom(void);\nvoid        led_matrix_decrease_speed(void);\nvoid        led_matrix_decrease_speed_noeeprom(void);\nled_flags_t led_matrix_get_flags(void);\nvoid        led_matrix_set_flags(led_flags_t flags);\nvoid        led_matrix_set_flags_noeeprom(led_flags_t flags);\n\nstatic inline bool led_matrix_check_finished_leds(uint8_t led_idx) {\n#if defined(LED_MATRIX_SPLIT)\n    if (is_keyboard_left()) {\n        uint8_t k_led_matrix_split[2] = LED_MATRIX_SPLIT;\n        return led_idx < k_led_matrix_split[0];\n    } else\n        return led_idx < LED_MATRIX_LED_COUNT;\n#else\n    return led_idx < LED_MATRIX_LED_COUNT;\n#endif\n}\n\nextern led_eeconfig_t led_matrix_eeconfig;\n\nextern uint32_t     g_led_timer;\nextern led_config_t g_led_config;\n#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\nextern last_hit_t g_last_hit_tracker;\n#endif\n#ifdef LED_MATRIX_FRAMEBUFFER_EFFECTS\nextern uint8_t g_led_frame_buffer[MATRIX_ROWS][MATRIX_COLS];\n#endif\n"
  },
  {
    "path": "quantum/led_matrix/led_matrix_drivers.c",
    "content": "/* Copyright 2018 James Laird-Wah\n * Copyright 2019 Clueboard\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"led_matrix_drivers.h\"\n\n/* Each driver needs to define a struct:\n *\n *    const led_matrix_driver_t led_matrix_driver;\n *\n * All members must be provided. Keyboard custom drivers must define this\n * in their own files.\n */\n\n#if defined(LED_MATRIX_IS31FL3218)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3218_init,\n    .flush         = is31fl3218_update_pwm_buffers,\n    .set_value     = is31fl3218_set_value,\n    .set_value_all = is31fl3218_set_value_all,\n};\n\n#elif defined(LED_MATRIX_IS31FL3236)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3236_init_drivers,\n    .flush         = is31fl3236_flush,\n    .set_value     = is31fl3236_set_value,\n    .set_value_all = is31fl3236_set_value_all,\n};\n\n#elif defined(LED_MATRIX_IS31FL3729)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3729_init_drivers,\n    .flush         = is31fl3729_flush,\n    .set_value     = is31fl3729_set_value,\n    .set_value_all = is31fl3729_set_value_all,\n};\n\n#elif defined(LED_MATRIX_IS31FL3731)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3731_init_drivers,\n    .flush         = is31fl3731_flush,\n    .set_value     = is31fl3731_set_value,\n    .set_value_all = is31fl3731_set_value_all,\n};\n\n#elif defined(LED_MATRIX_IS31FL3733)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3733_init_drivers,\n    .flush         = is31fl3733_flush,\n    .set_value     = is31fl3733_set_value,\n    .set_value_all = is31fl3733_set_value_all,\n};\n\n#elif defined(LED_MATRIX_IS31FL3736)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3736_init_drivers,\n    .flush         = is31fl3736_flush,\n    .set_value     = is31fl3736_set_value,\n    .set_value_all = is31fl3736_set_value_all,\n};\n\n#elif defined(LED_MATRIX_IS31FL3737)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3737_init_drivers,\n    .flush         = is31fl3737_flush,\n    .set_value     = is31fl3737_set_value,\n    .set_value_all = is31fl3737_set_value_all,\n};\n\n#elif defined(LED_MATRIX_IS31FL3741)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3741_init_drivers,\n    .flush         = is31fl3741_flush,\n    .set_value     = is31fl3741_set_value,\n    .set_value_all = is31fl3741_set_value_all,\n};\n\n#elif defined(LED_MATRIX_IS31FL3742A)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3742a_init_drivers,\n    .flush         = is31fl3742a_flush,\n    .set_value     = is31fl3742a_set_value,\n    .set_value_all = is31fl3742a_set_value_all,\n};\n\n#elif defined(LED_MATRIX_IS31FL3743A)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3743a_init_drivers,\n    .flush         = is31fl3743a_flush,\n    .set_value     = is31fl3743a_set_value,\n    .set_value_all = is31fl3743a_set_value_all,\n};\n\n#elif defined(LED_MATRIX_IS31FL3745)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3745_init_drivers,\n    .flush         = is31fl3745_flush,\n    .set_value     = is31fl3745_set_value,\n    .set_value_all = is31fl3745_set_value_all,\n};\n\n#elif defined(LED_MATRIX_IS31FL3746A)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = is31fl3746a_init_drivers,\n    .flush         = is31fl3746a_flush,\n    .set_value     = is31fl3746a_set_value,\n    .set_value_all = is31fl3746a_set_value_all,\n};\n\n#elif defined(LED_MATRIX_SNLED27351)\nconst led_matrix_driver_t led_matrix_driver = {\n    .init          = snled27351_init_drivers,\n    .flush         = snled27351_flush,\n    .set_value     = snled27351_set_value,\n    .set_value_all = snled27351_set_value_all,\n};\n\n#endif\n"
  },
  {
    "path": "quantum/led_matrix/led_matrix_drivers.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n\n#if defined(LED_MATRIX_IS31FL3218)\n#    include \"is31fl3218-mono.h\"\n#elif defined(LED_MATRIX_IS31FL3236)\n#    include \"is31fl3236-mono.h\"\n#elif defined(LED_MATRIX_IS31FL3729)\n#    include \"is31fl3729-mono.h\"\n#elif defined(LED_MATRIX_IS31FL3731)\n#    include \"is31fl3731-mono.h\"\n#elif defined(LED_MATRIX_IS31FL3733)\n#    include \"is31fl3733-mono.h\"\n#elif defined(LED_MATRIX_IS31FL3736)\n#    include \"is31fl3736-mono.h\"\n#elif defined(LED_MATRIX_IS31FL3737)\n#    include \"is31fl3737-mono.h\"\n#elif defined(LED_MATRIX_IS31FL3741)\n#    include \"is31fl3741-mono.h\"\n#elif defined(LED_MATRIX_IS31FL3742A)\n#    include \"is31fl3742a-mono.h\"\n#elif defined(LED_MATRIX_IS31FL3743A)\n#    include \"is31fl3743a-mono.h\"\n#elif defined(LED_MATRIX_IS31FL3745)\n#    include \"is31fl3745-mono.h\"\n#elif defined(LED_MATRIX_IS31FL3746A)\n#    include \"is31fl3746a-mono.h\"\n#elif defined(LED_MATRIX_SNLED27351)\n#    include \"snled27351-mono.h\"\n#endif\n\ntypedef struct {\n    /* Perform any initialisation required for the other driver functions to work. */\n    void (*init)(void);\n\n    /* Set the brightness of a single LED in the buffer. */\n    void (*set_value)(int index, uint8_t value);\n    /* Set the brightness of all LEDS on the keyboard in the buffer. */\n    void (*set_value_all)(uint8_t value);\n    /* Flush any buffered changes to the hardware. */\n    void (*flush)(void);\n} led_matrix_driver_t;\n\nextern const led_matrix_driver_t led_matrix_driver;\n"
  },
  {
    "path": "quantum/led_matrix/led_matrix_types.h",
    "content": "/* Copyright 2021\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"compiler_support.h\"\n#include \"util.h\"\n\n#if defined(LED_MATRIX_KEYPRESSES) || defined(LED_MATRIX_KEYRELEASES)\n#    define LED_MATRIX_KEYREACTIVE_ENABLED\n#endif\n\n// Last led hit\n#ifndef LED_HITS_TO_REMEMBER\n#    define LED_HITS_TO_REMEMBER 8\n#endif // LED_HITS_TO_REMEMBER\n\n#ifdef LED_MATRIX_KEYREACTIVE_ENABLED\ntypedef struct PACKED {\n    uint8_t  count;\n    uint8_t  x[LED_HITS_TO_REMEMBER];\n    uint8_t  y[LED_HITS_TO_REMEMBER];\n    uint8_t  index[LED_HITS_TO_REMEMBER];\n    uint16_t tick[LED_HITS_TO_REMEMBER];\n} last_hit_t;\n#endif // LED_MATRIX_KEYREACTIVE_ENABLED\n\ntypedef enum led_task_states { STARTING, RENDERING, FLUSHING, SYNCING } led_task_states;\n\ntypedef uint8_t led_flags_t;\n\ntypedef struct PACKED {\n    uint8_t     iter;\n    led_flags_t flags;\n    bool        init;\n} effect_params_t;\n\ntypedef struct PACKED {\n    uint8_t x;\n    uint8_t y;\n} led_point_t;\n\n#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)\n#define HAS_ANY_FLAGS(bits, flags) ((bits & flags) != 0x00)\n\n#define LED_FLAG_ALL 0xFF\n#define LED_FLAG_NONE 0x00\n#define LED_FLAG_MODIFIER 0x01\n#define LED_FLAG_KEYLIGHT 0x04\n#define LED_FLAG_INDICATOR 0x08\n\n#define NO_LED 255\n\ntypedef struct PACKED {\n    uint8_t     matrix_co[MATRIX_ROWS][MATRIX_COLS];\n    led_point_t point[LED_MATRIX_LED_COUNT];\n    uint8_t     flags[LED_MATRIX_LED_COUNT];\n} led_config_t;\n\ntypedef union led_eeconfig_t {\n    uint32_t raw;\n    struct PACKED {\n        uint8_t     enable : 2;\n        uint8_t     mode : 6;\n        uint8_t     val;\n        uint8_t     speed;\n        led_flags_t flags;\n    };\n} led_eeconfig_t;\n\nSTATIC_ASSERT(sizeof(led_eeconfig_t) == sizeof(uint32_t), \"LED Matrix EECONFIG out of spec.\");\n"
  },
  {
    "path": "quantum/led_matrix/post_config.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// clang-format off\n\n// reactive\n#if defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE) || \\\n    defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE) || \\\n    defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE) || \\\n    defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_CROSS) || \\\n    defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTICROSS) || \\\n    defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_NEXUS) || \\\n    defined(ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS) || \\\n    defined(ENABLE_LED_MATRIX_SOLID_SPLASH) || \\\n    defined(ENABLE_LED_MATRIX_SOLID_MULTISPLASH)\n#    define LED_MATRIX_KEYPRESSES\n#endif\n"
  },
  {
    "path": "quantum/led_tables.c",
    "content": "/*\nCopyright 2017 Fred Sundvik\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"led_tables.h\"\n\n// clang-format off\n\n#ifdef USE_CIE1931_CURVE\n// Lightness curve using the CIE 1931 lightness formula\n// Generated by the python script provided in http://jared.geek.nz/2013/feb/linear-led-pwm\nconst uint8_t CIE1931_CURVE[256] PROGMEM = {\n    0,   1,   1,   1,   1,   1,   1,   1,   1,   1,   2,   2,   2,   2,   2,   2,\n    2,   2,   2,   3,   3,   3,   3,   3,   3,   3,   3,   4,   4,   4,   4,   4,\n    4,   4,   5,   5,   5,   5,   5,   6,   6,   6,   6,   6,   7,   7,   7,   7,\n    7,   8,   8,   8,   8,   9,   9,   9,   9,  10,  10,  10,  11,  11,  11,  12,\n   12,  12,  13,  13,  13,  14,  14,  14,  15,  15,  15,  16,  16,  17,  17,  17,\n   18,  18,  19,  19,  20,  20,  21,  21,  22,  22,  23,  23,  24,  24,  25,  25,\n   26,  26,  27,  27,  28,  29,  29,  30,  30,  31,  32,  32,  33,  34,  34,  35,\n   36,  36,  37,  38,  38,  39,  40,  41,  41,  42,  43,  44,  45,  45,  46,  47,\n   48,  49,  50,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,\n   63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  76,  77,  78,  79,\n   80,  81,  83,  84,  85,  86,  88,  89,  90,  91,  93,  94,  95,  97,  98, 100,\n  101, 102, 104, 105, 107, 108, 109, 111, 112, 114, 115, 117, 119, 120, 122, 123,\n  125, 126, 128, 130, 131, 133, 135, 136, 138, 140, 142, 143, 145, 147, 149, 150,\n  152, 154, 156, 158, 160, 162, 163, 165, 167, 169, 171, 173, 175, 177, 179, 181,\n  183, 186, 188, 190, 192, 194, 196, 198, 201, 203, 205, 207, 209, 212, 214, 216,\n  219, 221, 223, 226, 228, 231, 233, 235, 238, 240, 243, 245, 248, 250, 253, 255\n};\n#endif\n\n// clang-format on\n"
  },
  {
    "path": "quantum/led_tables.h",
    "content": "/*\nCopyright 2017 Fred Sundvik\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include \"progmem.h\"\n#include <stdint.h>\n\n#ifdef USE_CIE1931_CURVE\nextern const uint8_t CIE1931_CURVE[] PROGMEM;\n#endif\n"
  },
  {
    "path": "quantum/logging/debug.c",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n#include \"debug.h\"\n\ndebug_config_t debug_config = {\n    .enable   = false, //\n    .matrix   = false, //\n    .keyboard = false, //\n    .mouse    = false, //\n    .reserved = 0      //\n};\n"
  },
  {
    "path": "quantum/logging/debug.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdio.h>\n#include <stdbool.h>\n#include \"print.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Debug output control\n */\ntypedef union debug_config_t {\n    struct {\n        bool    enable : 1;\n        bool    matrix : 1;\n        bool    keyboard : 1;\n        bool    mouse : 1;\n        uint8_t reserved : 4;\n    };\n    uint8_t raw;\n} debug_config_t;\n\nextern debug_config_t debug_config;\n\n#ifdef __cplusplus\n}\n#endif\n\n/* for backward compatibility */\n#define debug_enable (debug_config.enable)\n#define debug_matrix (debug_config.matrix)\n#define debug_keyboard (debug_config.keyboard)\n#define debug_mouse (debug_config.mouse)\n\n/*\n * Debug print utils\n */\n#ifndef NO_DEBUG\n#    define dprintf(fmt, ...)                                     \\\n        do {                                                      \\\n            if (debug_config.enable) xprintf(fmt, ##__VA_ARGS__); \\\n        } while (0)\n#else /* NO_DEBUG */\n#    define dprintf(fmt, ...)\n#endif /* NO_DEBUG */\n\n#define dprint(s) dprintf(s)\n#define dprintln(s) dprintf(s \"\\r\\n\")\n#define dmsg(s) dprintf(\"%s at %d: %s\\n\", __FILE__, __LINE__, s)\n"
  },
  {
    "path": "quantum/logging/print.c",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n#include <stddef.h>\n#include \"sendchar.h\"\n\n// bind lib/printf to console interface - sendchar\n\nstatic int8_t null_sendchar_func(uint8_t c) {\n    return 0;\n}\nstatic sendchar_func_t func = null_sendchar_func;\n\nvoid print_set_sendchar(sendchar_func_t send) {\n    func = send;\n}\n\nvoid putchar_(char character) {\n    func(character);\n}\n"
  },
  {
    "path": "quantum/logging/print.h",
    "content": "/* Copyright 2012 Jun Wako <wakojun@gmail.com> */\n/* Very basic print functions, intended to be used with usb_debug_only.c\n * http://www.pjrc.com/teensy/\n * Copyright (c) 2008 PJRC.COM, LLC\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"util.h\"\n#include \"sendchar.h\"\n#include \"progmem.h\"\n\nvoid print_set_sendchar(sendchar_func_t func);\n\n/**\n * @brief This macro suppress format warnings for the function that is passed\n * in. The main use-case is that `b` format specifier for printing binary\n * numbers is not in the official C standard. Inclusion is planned for the\n * upcoming C2X C standard, but until then GCC will always output a warning for\n * a unknown format specifier.\n */\n#define IGNORE_FORMAT_WARNING(func)                                \\\n    do {                                                           \\\n        _Pragma(\"GCC diagnostic push\");                            \\\n        _Pragma(\"GCC diagnostic ignored \\\"-Wformat\\\"\");            \\\n        _Pragma(\"GCC diagnostic ignored \\\"-Wformat-extra-args\\\"\"); \\\n        func;                                                      \\\n        _Pragma(\"GCC diagnostic pop\");                             \\\n    } while (0)\n\n#ifndef NO_PRINT\n#    if __has_include_next(\"_print.h\")\n#        include_next \"_print.h\" /* Include the platforms print.h */\n#    else\n#        include \"printf.h\" // // Fall back to lib/printf/printf.h\n#        define xprintf printf\n#    endif\n#else\n// Remove print defines\n#    undef xprintf\n#    define xprintf(fmt, ...)\n#endif\n\n// Resolve before USER_PRINT can remove\n#define uprintf xprintf\n\n#ifdef USER_PRINT\n// Remove normal print defines\n#    undef xprintf\n#    define xprintf(fmt, ...)\n#endif\n\n#define print(s) xprintf(s)\n#define println(s) xprintf(s \"\\r\\n\")\n\n#define print_dec(i) xprintf(\"%u\", i)\n#define print_decs(i) xprintf(\"%d\", i)\n/* hex */\n#define print_hex4(i) xprintf(\"%X\", i)\n#define print_hex8(i) xprintf(\"%02X\", i)\n#define print_hex16(i) xprintf(\"%04X\", i)\n#define print_hex32(i) xprintf(\"%08lX\", i)\n/* binary */\n#define print_bin4(i) IGNORE_FORMAT_WARNING(xprintf(\"%04b\", i))\n#define print_bin8(i) IGNORE_FORMAT_WARNING(xprintf(\"%08b\", i))\n#define print_bin16(i) IGNORE_FORMAT_WARNING(xprintf(\"%016b\", i))\n#define print_bin32(i) IGNORE_FORMAT_WARNING(xprintf(\"%032lb\", i))\n#define print_bin_reverse8(i) IGNORE_FORMAT_WARNING(xprintf(\"%08b\", bitrev(i)))\n#define print_bin_reverse16(i) IGNORE_FORMAT_WARNING(xprintf(\"%016b\", bitrev16(i)))\n#define print_bin_reverse32(i) IGNORE_FORMAT_WARNING(xprintf(\"%032lb\", bitrev32(i)))\n/* print value utility */\n#define print_val_dec(v) xprintf(#v \": %u\\n\", v)\n#define print_val_decs(v) xprintf(#v \": %d\\n\", v)\n#define print_val_hex8(v) xprintf(#v \": %X\\n\", v)\n#define print_val_hex16(v) xprintf(#v \": %02X\\n\", v)\n#define print_val_hex32(v) xprintf(#v \": %04lX\\n\", v)\n#define print_val_bin8(v) IGNORE_FORMAT_WARNING(xprintf(#v \": %08b\\n\", v))\n#define print_val_bin16(v) IGNORE_FORMAT_WARNING(xprintf(#v \": %016b\\n\", v))\n#define print_val_bin32(v) IGNORE_FORMAT_WARNING(xprintf(#v \": %032lb\\n\", v))\n#define print_val_bin_reverse8(v) IGNORE_FORMAT_WARNING(xprintf(#v \": %08b\\n\", bitrev(v)))\n#define print_val_bin_reverse16(v) IGNORE_FORMAT_WARNING(xprintf(#v \": %016b\\n\", bitrev16(v)))\n#define print_val_bin_reverse32(v) IGNORE_FORMAT_WARNING(xprintf(#v \": %032lb\\n\", bitrev32(v)))\n\n// User print disables the normal print messages in the body of QMK/TMK code and\n// is meant as a lightweight alternative to NOPRINT. Use it when you only want to do\n// a spot of debugging but lack flash resources for allowing all of the codebase to\n// print (and store their wasteful strings).\n//\n// !!! DO NOT USE USER PRINT CALLS IN THE BODY OF QMK/TMK !!!\n\n#define uprint(s) uprintf(s)\n#define uprintln(s) uprintf(s \"\\r\\n\")\n\n/* decimal */\n#define uprint_dec(i) uprintf(\"%u\", i)\n#define uprint_decs(i) uprintf(\"%d\", i)\n/* hex */\n#define uprint_hex4(i) uprintf(\"%X\", i)\n#define uprint_hex8(i) uprintf(\"%02X\", i)\n#define uprint_hex16(i) uprintf(\"%04X\", i)\n#define uprint_hex32(i) uprintf(\"%08lX\", i)\n/* binary */\n#define uprint_bin4(i) IGNORE_FORMAT_WARNING(uprintf(\"%04b\", i))\n#define uprint_bin8(i) IGNORE_FORMAT_WARNING(uprintf(\"%08b\", i))\n#define uprint_bin16(i) IGNORE_FORMAT_WARNING(uprintf(\"%016b\", i))\n#define uprint_bin32(i) IGNORE_FORMAT_WARNING(uprintf(\"%032lb\", i))\n#define uprint_bin_reverse8(i) IGNORE_FORMAT_WARNING(uprintf(\"%08b\", bitrev(i)))\n#define uprint_bin_reverse16(i) IGNORE_FORMAT_WARNING(uprintf(\"%016b\", bitrev16(i)))\n#define uprint_bin_reverse32(i) IGNORE_FORMAT_WARNING(uprintf(\"%032lb\", bitrev32(i)))\n/* print value utility */\n#define uprint_val_dec(v) uprintf(#v \": %u\\n\", v)\n#define uprint_val_decs(v) uprintf(#v \": %d\\n\", v)\n#define uprint_val_hex8(v) uprintf(#v \": %X\\n\", v)\n#define uprint_val_hex16(v) uprintf(#v \": %02X\\n\", v)\n#define uprint_val_hex32(v) uprintf(#v \": %04lX\\n\", v)\n#define uprint_val_bin8(v) IGNORE_FORMAT_WARNING(uprintf(#v \": %08b\\n\", v))\n#define uprint_val_bin16(v) IGNORE_FORMAT_WARNING(uprintf(#v \": %016b\\n\", v))\n#define uprint_val_bin32(v) IGNORE_FORMAT_WARNING(uprintf(#v \": %032lb\\n\", v))\n#define uprint_val_bin_reverse8(v) IGNORE_FORMAT_WARNING(uprintf(#v \": %08b\\n\", bitrev(v)))\n#define uprint_val_bin_reverse16(v) IGNORE_FORMAT_WARNING(uprintf(#v \": %016b\\n\", bitrev16(v)))\n#define uprint_val_bin_reverse32(v) IGNORE_FORMAT_WARNING(uprintf(#v \": %032lb\\n\", bitrev32(v)))\n"
  },
  {
    "path": "quantum/logging/print.mk",
    "content": "PRINTF_PATH = $(LIB_PATH)/printf/src\n\nVPATH += $(PRINTF_PATH) $(PRINTF_PATH)/printf\nSRC += printf.c\nQUANTUM_SRC +=$(QUANTUM_DIR)/logging/print.c\n\nOPT_DEFS += -DPRINTF_SUPPORT_DECIMAL_SPECIFIERS=0\nOPT_DEFS += -DPRINTF_SUPPORT_EXPONENTIAL_SPECIFIERS=0\nOPT_DEFS += -DPRINTF_SUPPORT_LONG_LONG=0\nOPT_DEFS += -DPRINTF_SUPPORT_WRITEBACK_SPECIFIER=0\nOPT_DEFS += -DSUPPORT_MSVC_STYLE_INTEGER_SPECIFIERS=0\nOPT_DEFS += -DPRINTF_ALIAS_STANDARD_FUNCTION_NAMES=1\n"
  },
  {
    "path": "quantum/logging/sendchar.c",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n#include \"sendchar.h\"\n\n/* default noop \"null\" implementation */\n__attribute__((weak)) int8_t sendchar(uint8_t c) {\n    return 0;\n}\n"
  },
  {
    "path": "quantum/logging/sendchar.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef int8_t (*sendchar_func_t)(uint8_t c);\n\n/* transmit a character.  return 0 on success, -1 on error. */\nint8_t sendchar(uint8_t c);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/main.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard.h\"\n\nvoid platform_setup(void);\n\nvoid protocol_setup(void);\nvoid protocol_pre_init(void);\nvoid protocol_post_init(void);\nvoid protocol_pre_task(void);\nvoid protocol_post_task(void);\n\n// Bodge as refactoring this area sucks....\nvoid protocol_keyboard_task(void) __attribute__((weak));\nvoid protocol_keyboard_task(void) {\n    keyboard_task();\n}\n\n/** \\brief Main\n *\n * FIXME: Needs doc\n */\nint main(void) __attribute__((weak));\nint main(void) {\n    platform_setup();\n    protocol_setup();\n    keyboard_setup();\n\n    protocol_pre_init();\n    keyboard_init();\n    protocol_post_init();\n\n    /* Main loop */\n    while (true) {\n        protocol_pre_task();\n        protocol_keyboard_task();\n        protocol_post_task();\n\n#ifdef RAW_ENABLE\n        void raw_hid_task(void);\n        raw_hid_task();\n#endif\n\n#ifdef CONSOLE_ENABLE\n        void console_task(void);\n        console_task();\n#endif\n\n#ifdef QUANTUM_PAINTER_ENABLE\n        // Run Quantum Painter task\n        void qp_internal_task(void);\n        qp_internal_task();\n#endif\n\n#ifdef DEFERRED_EXEC_ENABLE\n        // Run deferred executions\n        void deferred_exec_task(void);\n        deferred_exec_task();\n#endif // DEFERRED_EXEC_ENABLE\n\n        housekeeping_task();\n    }\n}\n"
  },
  {
    "path": "quantum/matrix.c",
    "content": "/*\nCopyright 2012-2018 Jun Wako, Jack Humbert, Yiancar\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n#include <stdint.h>\n#include <stdbool.h>\n#include <string.h>\n#include \"util.h\"\n#include \"matrix.h\"\n#include \"debounce.h\"\n#include \"atomic_util.h\"\n\n#ifdef SPLIT_KEYBOARD\n#    include \"split_common/split_util.h\"\n#    include \"split_common/transactions.h\"\n\n#    define ROWS_PER_HAND (MATRIX_ROWS / 2)\n#else\n#    define ROWS_PER_HAND (MATRIX_ROWS)\n#endif\n\n#ifdef DIRECT_PINS_RIGHT\n#    define SPLIT_MUTABLE\n#else\n#    define SPLIT_MUTABLE const\n#endif\n#ifdef MATRIX_ROW_PINS_RIGHT\n#    define SPLIT_MUTABLE_ROW\n#else\n#    define SPLIT_MUTABLE_ROW const\n#endif\n#ifdef MATRIX_COL_PINS_RIGHT\n#    define SPLIT_MUTABLE_COL\n#else\n#    define SPLIT_MUTABLE_COL const\n#endif\n\n#ifndef MATRIX_INPUT_PRESSED_STATE\n#    define MATRIX_INPUT_PRESSED_STATE 0\n#endif\n\n#ifdef DIRECT_PINS\nstatic SPLIT_MUTABLE pin_t direct_pins[ROWS_PER_HAND][MATRIX_COLS] = DIRECT_PINS;\n#elif (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW)\n#    ifdef MATRIX_ROW_PINS\nstatic SPLIT_MUTABLE_ROW pin_t row_pins[ROWS_PER_HAND] = MATRIX_ROW_PINS;\n#    endif // MATRIX_ROW_PINS\n#    ifdef MATRIX_COL_PINS\nstatic SPLIT_MUTABLE_COL pin_t col_pins[MATRIX_COLS]   = MATRIX_COL_PINS;\n#    endif // MATRIX_COL_PINS\n#endif\n\n/* matrix state(1:on, 0:off) */\nextern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values\nextern matrix_row_t matrix[MATRIX_ROWS];     // debounced values\n\n#ifdef SPLIT_KEYBOARD\n// row offsets for each hand\nextern uint8_t thisHand, thatHand;\n#endif\n\n// user-defined overridable functions\n__attribute__((weak)) void matrix_init_pins(void);\n__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);\n__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter);\n\nstatic inline void gpio_atomic_set_pin_output_low(pin_t pin) {\n    ATOMIC_BLOCK_FORCEON {\n        gpio_set_pin_output(pin);\n        gpio_write_pin_low(pin);\n    }\n}\n\nstatic inline void gpio_atomic_set_pin_output_high(pin_t pin) {\n    ATOMIC_BLOCK_FORCEON {\n        gpio_set_pin_output(pin);\n        gpio_write_pin_high(pin);\n    }\n}\n\nstatic inline void gpio_atomic_set_pin_input_high(pin_t pin) {\n    ATOMIC_BLOCK_FORCEON {\n        gpio_set_pin_input_high(pin);\n    }\n}\n\nstatic inline uint8_t readMatrixPin(pin_t pin) {\n    if (pin != NO_PIN) {\n        return (gpio_read_pin(pin) == MATRIX_INPUT_PRESSED_STATE) ? 0 : 1;\n    } else {\n        return 1;\n    }\n}\n\n// matrix code\n\n#ifdef DIRECT_PINS\n\n__attribute__((weak)) void matrix_init_pins(void) {\n    for (int row = 0; row < ROWS_PER_HAND; row++) {\n        for (int col = 0; col < MATRIX_COLS; col++) {\n            pin_t pin = direct_pins[row][col];\n            if (pin != NO_PIN) {\n                gpio_set_pin_input_high(pin);\n            }\n        }\n    }\n}\n\n__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {\n    // Start with a clear matrix row\n    matrix_row_t current_row_value = 0;\n\n    matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;\n    for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++, row_shifter <<= 1) {\n        pin_t pin = direct_pins[current_row][col_index];\n        current_row_value |= readMatrixPin(pin) ? 0 : row_shifter;\n    }\n\n    // Update the matrix\n    current_matrix[current_row] = current_row_value;\n}\n\n#elif defined(DIODE_DIRECTION)\n#    if defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)\n#        if (DIODE_DIRECTION == COL2ROW)\n\nstatic bool select_row(uint8_t row) {\n    pin_t pin = row_pins[row];\n    if (pin != NO_PIN) {\n        gpio_atomic_set_pin_output_low(pin);\n        return true;\n    }\n    return false;\n}\n\nstatic void unselect_row(uint8_t row) {\n    pin_t pin = row_pins[row];\n    if (pin != NO_PIN) {\n#            ifdef MATRIX_UNSELECT_DRIVE_HIGH\n        gpio_atomic_set_pin_output_high(pin);\n#            else\n        gpio_atomic_set_pin_input_high(pin);\n#            endif\n    }\n}\n\nstatic void unselect_rows(void) {\n    for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {\n        unselect_row(x);\n    }\n}\n\n__attribute__((weak)) void matrix_init_pins(void) {\n    unselect_rows();\n    for (uint8_t x = 0; x < MATRIX_COLS; x++) {\n        if (col_pins[x] != NO_PIN) {\n            gpio_atomic_set_pin_input_high(col_pins[x]);\n        }\n    }\n}\n\n__attribute__((weak)) void matrix_read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row) {\n    // Start with a clear matrix row\n    matrix_row_t current_row_value = 0;\n\n    if (!select_row(current_row)) { // Select row\n        return;                     // skip NO_PIN row\n    }\n    matrix_output_select_delay();\n\n    // For each col...\n    matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;\n    for (uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++, row_shifter <<= 1) {\n        uint8_t pin_state = readMatrixPin(col_pins[col_index]);\n\n        // Populate the matrix row with the state of the col pin\n        current_row_value |= pin_state ? 0 : row_shifter;\n    }\n\n    // Unselect row\n    unselect_row(current_row);\n    matrix_output_unselect_delay(current_row, current_row_value != 0); // wait for all Col signals to go HIGH\n\n    // Update the matrix\n    current_matrix[current_row] = current_row_value;\n}\n\n#        elif (DIODE_DIRECTION == ROW2COL)\n\nstatic bool select_col(uint8_t col) {\n    pin_t pin = col_pins[col];\n    if (pin != NO_PIN) {\n        gpio_atomic_set_pin_output_low(pin);\n        return true;\n    }\n    return false;\n}\n\nstatic void unselect_col(uint8_t col) {\n    pin_t pin = col_pins[col];\n    if (pin != NO_PIN) {\n#            ifdef MATRIX_UNSELECT_DRIVE_HIGH\n        gpio_atomic_set_pin_output_high(pin);\n#            else\n        gpio_atomic_set_pin_input_high(pin);\n#            endif\n    }\n}\n\nstatic void unselect_cols(void) {\n    for (uint8_t x = 0; x < MATRIX_COLS; x++) {\n        unselect_col(x);\n    }\n}\n\n__attribute__((weak)) void matrix_init_pins(void) {\n    unselect_cols();\n    for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {\n        if (row_pins[x] != NO_PIN) {\n            gpio_atomic_set_pin_input_high(row_pins[x]);\n        }\n    }\n}\n\n__attribute__((weak)) void matrix_read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col, matrix_row_t row_shifter) {\n    bool key_pressed = false;\n\n    // Select col\n    if (!select_col(current_col)) { // select col\n        return;                     // skip NO_PIN col\n    }\n    matrix_output_select_delay();\n\n    // For each row...\n    for (uint8_t row_index = 0; row_index < ROWS_PER_HAND; row_index++) {\n        // Check row pin state\n        if (readMatrixPin(row_pins[row_index]) == 0) {\n            // Pin LO, set col bit\n            current_matrix[row_index] |= row_shifter;\n            key_pressed = true;\n        } else {\n            // Pin HI, clear col bit\n            current_matrix[row_index] &= ~row_shifter;\n        }\n    }\n\n    // Unselect col\n    unselect_col(current_col);\n    matrix_output_unselect_delay(current_col, key_pressed); // wait for all Row signals to go HIGH\n}\n\n#        else\n#            error DIODE_DIRECTION must be one of COL2ROW or ROW2COL!\n#        endif\n#    endif // defined(MATRIX_ROW_PINS) && defined(MATRIX_COL_PINS)\n#else\n#    error DIODE_DIRECTION is not defined!\n#endif\n\nvoid matrix_init(void) {\n#ifdef SPLIT_KEYBOARD\n    // Set pinout for right half if pinout for that half is defined\n    if (!isLeftHand) {\n#    ifdef DIRECT_PINS_RIGHT\n        const pin_t direct_pins_right[ROWS_PER_HAND][MATRIX_COLS] = DIRECT_PINS_RIGHT;\n        for (uint8_t i = 0; i < ROWS_PER_HAND; i++) {\n            for (uint8_t j = 0; j < MATRIX_COLS; j++) {\n                direct_pins[i][j] = direct_pins_right[i][j];\n            }\n        }\n#    endif\n#    ifdef MATRIX_ROW_PINS_RIGHT\n        const pin_t row_pins_right[ROWS_PER_HAND] = MATRIX_ROW_PINS_RIGHT;\n        for (uint8_t i = 0; i < ROWS_PER_HAND; i++) {\n            row_pins[i] = row_pins_right[i];\n        }\n#    endif\n#    ifdef MATRIX_COL_PINS_RIGHT\n        const pin_t col_pins_right[MATRIX_COLS] = MATRIX_COL_PINS_RIGHT;\n        for (uint8_t i = 0; i < MATRIX_COLS; i++) {\n            col_pins[i] = col_pins_right[i];\n        }\n#    endif\n    }\n\n    thisHand = isLeftHand ? 0 : (ROWS_PER_HAND);\n    thatHand = ROWS_PER_HAND - thisHand;\n#endif\n\n    // initialize key pins\n    matrix_init_pins();\n\n    // initialize matrix state: all keys off\n    memset(matrix, 0, sizeof(matrix));\n    memset(raw_matrix, 0, sizeof(raw_matrix));\n\n    debounce_init(ROWS_PER_HAND);\n\n    matrix_init_kb();\n}\n\n#ifdef SPLIT_KEYBOARD\n// Fallback implementation for keyboards not using the standard split_util.c\n__attribute__((weak)) bool transport_master_if_connected(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    transport_master(master_matrix, slave_matrix);\n    return true; // Treat the transport as always connected\n}\n#endif\n\nuint8_t matrix_scan(void) {\n    matrix_row_t curr_matrix[MATRIX_ROWS] = {0};\n\n#if defined(DIRECT_PINS) || (DIODE_DIRECTION == COL2ROW)\n    // Set row, read cols\n    for (uint8_t current_row = 0; current_row < ROWS_PER_HAND; current_row++) {\n        matrix_read_cols_on_row(curr_matrix, current_row);\n    }\n#elif (DIODE_DIRECTION == ROW2COL)\n    // Set col, read rows\n    matrix_row_t row_shifter = MATRIX_ROW_SHIFTER;\n    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++, row_shifter <<= 1) {\n        matrix_read_rows_on_col(curr_matrix, current_col, row_shifter);\n    }\n#endif\n\n    bool changed = memcmp(raw_matrix, curr_matrix, sizeof(curr_matrix)) != 0;\n    if (changed) memcpy(raw_matrix, curr_matrix, sizeof(curr_matrix));\n\n#ifdef SPLIT_KEYBOARD\n    changed = debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed) | matrix_post_scan();\n#else\n    changed = debounce(raw_matrix, matrix, ROWS_PER_HAND, changed);\n    matrix_scan_kb();\n#endif\n    return (uint8_t)changed;\n}\n"
  },
  {
    "path": "quantum/matrix.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"gpio.h\"\n\n/* diode directions */\n#define COL2ROW 0\n#define ROW2COL 1\n\n#if (MATRIX_COLS <= 8)\ntypedef uint8_t matrix_row_t;\n#elif (MATRIX_COLS <= 16)\ntypedef uint16_t matrix_row_t;\n#elif (MATRIX_COLS <= 32)\ntypedef uint32_t matrix_row_t;\n#else\n#    error \"MATRIX_COLS: invalid value\"\n#endif\n\n#define MATRIX_ROW_SHIFTER ((matrix_row_t)1)\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* number of matrix rows */\nuint8_t matrix_rows(void);\n/* number of matrix columns */\nuint8_t matrix_cols(void);\n/* should be called at early stage of startup before matrix_init.(optional) */\nvoid matrix_setup(void);\n/* intialize matrix for scaning. */\nvoid matrix_init(void);\n/* scan all key states on matrix */\nuint8_t matrix_scan(void);\n/* whether matrix scanning operations should be executed */\nbool matrix_can_read(void);\n/* whether a switch is on */\nbool matrix_is_on(uint8_t row, uint8_t col);\n/* matrix state on row */\nmatrix_row_t matrix_get_row(uint8_t row);\n/* print matrix for debug */\nvoid matrix_print(void);\n/* delay between changing matrix pin state and reading values */\nvoid matrix_output_select_delay(void);\nvoid matrix_output_unselect_delay(uint8_t line, bool key_pressed);\n/* only for backwards compatibility. delay between changing matrix pin state and reading values */\nvoid matrix_io_delay(void);\n\n/* power control */\nvoid matrix_power_up(void);\nvoid matrix_power_down(void);\n\nvoid matrix_init_kb(void);\nvoid matrix_scan_kb(void);\n\nvoid matrix_init_user(void);\nvoid matrix_scan_user(void);\n\n#ifdef SPLIT_KEYBOARD\nbool matrix_post_scan(void);\nvoid matrix_slave_scan_kb(void);\nvoid matrix_slave_scan_user(void);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/matrix_common.c",
    "content": "#include \"matrix.h\"\n#include \"debounce.h\"\n#include \"wait.h\"\n#include \"print.h\"\n#include \"debug.h\"\n\n#ifdef SPLIT_KEYBOARD\n#    include \"split_common/split_util.h\"\n#    include \"split_common/transactions.h\"\n#    include <string.h>\n\n#    define ROWS_PER_HAND (MATRIX_ROWS / 2)\n#else\n#    define ROWS_PER_HAND (MATRIX_ROWS)\n#endif\n\n#ifndef MATRIX_IO_DELAY\n#    define MATRIX_IO_DELAY 30\n#endif\n\n/* matrix state(1:on, 0:off) */\nmatrix_row_t raw_matrix[MATRIX_ROWS];\nmatrix_row_t matrix[MATRIX_ROWS];\n\n#ifdef SPLIT_KEYBOARD\n// row offsets for each hand\nuint8_t thisHand, thatHand;\n#endif\n\n#ifdef MATRIX_MASKED\nextern const matrix_row_t matrix_mask[];\n#endif\n\n// user-defined overridable functions\n\n__attribute__((weak)) void matrix_init_kb(void) {\n    matrix_init_user();\n}\n\n__attribute__((weak)) void matrix_scan_kb(void) {\n    matrix_scan_user();\n}\n\n__attribute__((weak)) void matrix_init_user(void) {}\n\n__attribute__((weak)) void matrix_scan_user(void) {}\n\n// helper functions\n\ninline uint8_t matrix_rows(void) {\n    return MATRIX_ROWS;\n}\n\ninline uint8_t matrix_cols(void) {\n    return MATRIX_COLS;\n}\n\ninline bool matrix_is_on(uint8_t row, uint8_t col) {\n    return (matrix[row] & ((matrix_row_t)1 << col));\n}\n\ninline matrix_row_t matrix_get_row(uint8_t row) {\n    // Matrix mask lets you disable switches in the returned matrix data. For example, if you have a\n    // switch blocker installed and the switch is always pressed.\n#ifdef MATRIX_MASKED\n    return matrix[row] & matrix_mask[row];\n#else\n    return matrix[row];\n#endif\n}\n\n#if (MATRIX_COLS <= 8)\n#    define print_matrix_header() print(\"\\nr/c 01234567\\n\")\n#    define print_matrix_row(row) print_bin_reverse8(matrix_get_row(row))\n#elif (MATRIX_COLS <= 16)\n#    define print_matrix_header() print(\"\\nr/c 0123456789ABCDEF\\n\")\n#    define print_matrix_row(row) print_bin_reverse16(matrix_get_row(row))\n#elif (MATRIX_COLS <= 32)\n#    define print_matrix_header() print(\"\\nr/c 0123456789ABCDEF0123456789ABCDEF\\n\")\n#    define print_matrix_row(row) print_bin_reverse32(matrix_get_row(row))\n#endif\n\nvoid matrix_print(void) {\n    print_matrix_header();\n\n    for (uint8_t row = 0; row < MATRIX_ROWS; row++) {\n        print_hex8(row);\n        print(\": \");\n        print_matrix_row(row);\n        print(\"\\n\");\n    }\n}\n\n#ifdef SPLIT_KEYBOARD\nbool matrix_post_scan(void) {\n    bool changed = false;\n    if (is_keyboard_master()) {\n        static bool  last_connected              = false;\n        matrix_row_t slave_matrix[ROWS_PER_HAND] = {0};\n        if (transport_master_if_connected(matrix + thisHand, slave_matrix)) {\n            changed = memcmp(matrix + thatHand, slave_matrix, sizeof(slave_matrix)) != 0;\n\n            last_connected = true;\n        } else if (last_connected) {\n            // reset other half when disconnected\n            memset(slave_matrix, 0, sizeof(slave_matrix));\n            changed = true;\n\n            last_connected = false;\n        }\n\n        if (changed) memcpy(matrix + thatHand, slave_matrix, sizeof(slave_matrix));\n\n        matrix_scan_kb();\n    } else {\n        transport_slave(matrix + thatHand, matrix + thisHand);\n\n        matrix_slave_scan_kb();\n    }\n\n    return changed;\n}\n#endif\n\n/* `matrix_io_delay ()` exists for backwards compatibility. From now on, use matrix_output_unselect_delay(). */\n__attribute__((weak)) void matrix_io_delay(void) {\n    wait_us(MATRIX_IO_DELAY);\n}\n__attribute__((weak)) void matrix_output_select_delay(void) {\n    waitInputPinDelay();\n}\n__attribute__((weak)) void matrix_output_unselect_delay(uint8_t line, bool key_pressed) {\n    matrix_io_delay();\n}\n\n// CUSTOM MATRIX 'LITE'\n__attribute__((weak)) void matrix_init_custom(void) {}\n__attribute__((weak)) bool matrix_scan_custom(matrix_row_t current_matrix[]) {\n    return true;\n}\n\n#ifdef SPLIT_KEYBOARD\n__attribute__((weak)) void matrix_slave_scan_kb(void) {\n    matrix_slave_scan_user();\n}\n__attribute__((weak)) void matrix_slave_scan_user(void) {}\n#endif\n\n__attribute__((weak)) void matrix_init(void) {\n#ifdef SPLIT_KEYBOARD\n    thisHand = isLeftHand ? 0 : (ROWS_PER_HAND);\n    thatHand = ROWS_PER_HAND - thisHand;\n#endif\n\n    matrix_init_custom();\n\n    // initialize matrix state: all keys off\n    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {\n        raw_matrix[i] = 0;\n        matrix[i]     = 0;\n    }\n\n    debounce_init(ROWS_PER_HAND);\n\n    matrix_init_kb();\n}\n\n__attribute__((weak)) uint8_t matrix_scan(void) {\n    bool changed = matrix_scan_custom(raw_matrix);\n\n#ifdef SPLIT_KEYBOARD\n    changed = debounce(raw_matrix, matrix + thisHand, ROWS_PER_HAND, changed) | matrix_post_scan();\n#else\n    changed = debounce(raw_matrix, matrix, ROWS_PER_HAND, changed);\n    matrix_scan_kb();\n#endif\n\n    return changed;\n}\n\n__attribute__((weak)) bool peek_matrix(uint8_t row_index, uint8_t col_index, bool raw) {\n    return 0 != ((raw ? raw_matrix[row_index] : matrix[row_index]) & (MATRIX_ROW_SHIFTER << col_index));\n}\n"
  },
  {
    "path": "quantum/midi/Config/LUFAConfig.h",
    "content": "/*\n             LUFA Library\n     Copyright (C) Dean Camera, 2012.\n\n  dean [at] fourwalledcubicle [dot] com\n           www.lufa-lib.org\n*/\n\n/*\n  Copyright 2012  Dean Camera (dean [at] fourwalledcubicle [dot] com)\n\n  Permission to use, copy, modify, distribute, and sell this\n  software and its documentation for any purpose is hereby granted\n  without fee, provided that the above copyright notice appear in\n  all copies and that both that the copyright notice and this\n  permission notice and warranty disclaimer appear in supporting\n  documentation, and that the name of the author not be used in\n  advertising or publicity pertaining to distribution of the\n  software without specific, written prior permission.\n\n  The author disclaim all warranties with regard to this\n  software, including all implied warranties of merchantability\n  and fitness.  In no event shall the author be liable for any\n  special, indirect or consequential damages or any damages\n  whatsoever resulting from loss of use, data or profits, whether\n  in an action of contract, negligence or other tortious action,\n  arising out of or in connection with the use or performance of\n  this software.\n*/\n\n/** \\file\n *  \\brief LUFA Library Configuration Header File\n *\n *  This header file is used to configure LUFA's compile time options,\n *  as an alternative to the compile time constants supplied through\n *  a makefile.\n *\n *  For information on what each token does, refer to the LUFA\n *  manual section \"Summary of Compile Tokens\".\n */\n\n#pragma once\n\n#if (ARCH == ARCH_AVR8)\n\n/* Non-USB Related Configuration Tokens: */\n//\t\t#define DISABLE_TERMINAL_CODES\n\n/* USB Class Driver Related Tokens: */\n//\t\t#define HID_HOST_BOOT_PROTOCOL_ONLY\n//\t\t#define HID_STATETABLE_STACK_DEPTH       {Insert Value Here}\n//\t\t#define HID_USAGE_STACK_DEPTH            {Insert Value Here}\n//\t\t#define HID_MAX_COLLECTIONS              {Insert Value Here}\n//\t\t#define HID_MAX_REPORTITEMS              {Insert Value Here}\n//\t\t#define HID_MAX_REPORT_IDS               {Insert Value Here}\n//\t\t#define NO_CLASS_DRIVER_AUTOFLUSH\n\n/* General USB Driver Related Tokens: */\n//\t\t#define ORDERED_EP_CONFIG\n#    define USE_STATIC_OPTIONS (USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)\n#    define USB_DEVICE_ONLY\n//\t\t#define USB_HOST_ONLY\n//\t\t#define USB_STREAM_TIMEOUT_MS            {Insert Value Here}\n//\t\t#define NO_LIMITED_CONTROLLER_CONNECT\n//\t\t#define NO_SOF_EVENTS\n\n/* USB Device Mode Driver Related Tokens: */\n//\t\t#define USE_RAM_DESCRIPTORS\n#    define USE_FLASH_DESCRIPTORS\n//\t\t#define USE_EEPROM_DESCRIPTORS\n//\t\t#define NO_INTERNAL_SERIAL\n#    define FIXED_CONTROL_ENDPOINT_SIZE 8\n//\t\t#define DEVICE_STATE_AS_GPIOR            {Insert Value Here}\n#    define FIXED_NUM_CONFIGURATIONS 1\n//\t\t#define CONTROL_ONLY_DEVICE\n//\t\t#define INTERRUPT_CONTROL_ENDPOINT\n//\t\t#define NO_DEVICE_REMOTE_WAKEUP\n//\t\t#define NO_DEVICE_SELF_POWER\n\n/* USB Host Mode Driver Related Tokens: */\n//\t\t#define HOST_STATE_AS_GPIOR              {Insert Value Here}\n//\t\t#define USB_HOST_TIMEOUT_MS              {Insert Value Here}\n//\t\t#define HOST_DEVICE_SETTLE_DELAY_MS\t     {Insert Value Here}\n//      #define NO_AUTO_VBUS_MANAGEMENT\n//      #define INVERTED_VBUS_ENABLE_LINE\n\n#else\n\n#    error Unsupported architecture for this LUFA configuration file.\n\n#endif\n"
  },
  {
    "path": "quantum/midi/bytequeue/COPYING",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n"
  },
  {
    "path": "quantum/midi/bytequeue/bytequeue.c",
    "content": "// this is a single reader [maybe multiple writer?] byte queue\n// Copyright 2008 Alex Norman\n// writen by Alex Norman\n//\n// This file is part of avr-bytequeue.\n//\n// avr-bytequeue is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n//(at your option) any later version.\n//\n// avr-bytequeue is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with avr-bytequeue.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"bytequeue.h\"\n#include \"interrupt_setting.h\"\n\nvoid bytequeue_init(byteQueue_t* queue, uint8_t* dataArray, byteQueueIndex_t arrayLen) {\n    queue->length = arrayLen;\n    queue->data   = dataArray;\n    queue->start = queue->end = 0;\n}\n\nbool bytequeue_enqueue(byteQueue_t* queue, uint8_t item) {\n    interrupt_setting_t setting = store_and_clear_interrupt();\n    // full\n    if (((queue->end + 1) % queue->length) == queue->start) {\n        restore_interrupt_setting(setting);\n        return false;\n    } else {\n        queue->data[queue->end] = item;\n        queue->end              = (queue->end + 1) % queue->length;\n        restore_interrupt_setting(setting);\n        return true;\n    }\n}\n\nbyteQueueIndex_t bytequeue_length(byteQueue_t* queue) {\n    byteQueueIndex_t    len;\n    interrupt_setting_t setting = store_and_clear_interrupt();\n    if (queue->end >= queue->start)\n        len = queue->end - queue->start;\n    else\n        len = (queue->length - queue->start) + queue->end;\n    restore_interrupt_setting(setting);\n    return len;\n}\n\n// we don't need to avoid interrupts if there is only one reader\nuint8_t bytequeue_get(byteQueue_t* queue, byteQueueIndex_t index) {\n    return queue->data[(queue->start + index) % queue->length];\n}\n\n// we just update the start index to remove elements\nvoid bytequeue_remove(byteQueue_t* queue, byteQueueIndex_t numToRemove) {\n    interrupt_setting_t setting = store_and_clear_interrupt();\n    queue->start                = (queue->start + numToRemove) % queue->length;\n    restore_interrupt_setting(setting);\n}\n"
  },
  {
    "path": "quantum/midi/bytequeue/bytequeue.h",
    "content": "// this is a single reader [maybe multiple writer?] byte queue\n// Copyright 2008 Alex Norman\n// writen by Alex Norman\n//\n// This file is part of avr-bytequeue.\n//\n// avr-bytequeue is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n//(at your option) any later version.\n//\n// avr-bytequeue is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with avr-bytequeue.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <inttypes.h>\n#include <stdbool.h>\n\ntypedef uint8_t byteQueueIndex_t;\n\ntypedef struct {\n    byteQueueIndex_t start;\n    byteQueueIndex_t end;\n    byteQueueIndex_t length;\n    uint8_t*         data;\n} byteQueue_t;\n\n// you must have a queue, an array of data which the queue will use, and the length of that array\nvoid bytequeue_init(byteQueue_t* queue, uint8_t* dataArray, byteQueueIndex_t arrayLen);\n\n// add an item to the queue, returns false if the queue is full\nbool bytequeue_enqueue(byteQueue_t* queue, uint8_t item);\n\n// get the length of the queue\nbyteQueueIndex_t bytequeue_length(byteQueue_t* queue);\n\n// this grabs data at the index given [starting at queue->start]\nuint8_t bytequeue_get(byteQueue_t* queue, byteQueueIndex_t index);\n\n// update the index in the queue to reflect data that has been dealt with\nvoid bytequeue_remove(byteQueue_t* queue, byteQueueIndex_t numToRemove);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/midi/bytequeue/interrupt_setting.c",
    "content": "// Copyright 20010 Alex Norman\n// writen by Alex Norman\n//\n// This file is part of avr-bytequeue.\n//\n// avr-bytequeue is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n//(at your option) any later version.\n//\n// avr-bytequeue is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with avr-bytequeue.  If not, see <http://www.gnu.org/licenses/>.\n\n// AVR specific code\n// should be able to port to other systems by simply providing chip specific\n// implementations of the typedef and these functions\n\n#include \"interrupt_setting.h\"\n#if defined(__AVR__)\n#    include <avr/interrupt.h>\n\ninterrupt_setting_t store_and_clear_interrupt(void) {\n    uint8_t sreg = SREG;\n    cli();\n    return sreg;\n}\n\nvoid restore_interrupt_setting(interrupt_setting_t setting) {\n    SREG = setting;\n}\n#elif defined(__arm__)\n#    include <ch.h>\n\ninterrupt_setting_t store_and_clear_interrupt(void) {\n    chSysLock();\n    return 0;\n}\n\nvoid restore_interrupt_setting(interrupt_setting_t setting) {\n    chSysUnlock();\n}\n#endif\n"
  },
  {
    "path": "quantum/midi/bytequeue/interrupt_setting.h",
    "content": "// Copyright 20010 Alex Norman\n// writen by Alex Norman\n//\n// This file is part of avr-bytequeue.\n//\n// avr-bytequeue is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n//(at your option) any later version.\n//\n// avr-bytequeue is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with avr-bytequeue.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <inttypes.h>\n\n// AVR specific typedef\ntypedef uint8_t interrupt_setting_t;\n\ninterrupt_setting_t store_and_clear_interrupt(void);\nvoid                restore_interrupt_setting(interrupt_setting_t setting);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/midi/midi.c",
    "content": "// midi for embedded chips,\n// Copyright 2010 Alex Norman\n//\n// This file is part of avr-midi.\n//\n// avr-midi is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n//(at your option) any later version.\n//\n// avr-midi is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with avr-midi.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"midi.h\"\n#include <string.h> //for memcpy\n#include \"util.h\"\n\n#ifndef NULL\n#    define NULL 0\n#endif\n\nbool midi_is_statusbyte(uint8_t theByte) {\n    return (bool)(theByte & MIDI_STATUSMASK);\n}\n\nbool midi_is_realtime(uint8_t theByte) {\n    return (theByte >= MIDI_CLOCK);\n}\n\nmidi_packet_length_t midi_packet_length(uint8_t status) {\n    switch (status & 0xF0) {\n        case MIDI_CC:\n        case MIDI_NOTEON:\n        case MIDI_NOTEOFF:\n        case MIDI_AFTERTOUCH:\n        case MIDI_PITCHBEND:\n            return THREE;\n        case MIDI_PROGCHANGE:\n        case MIDI_CHANPRESSURE:\n        case MIDI_SONGSELECT:\n            return TWO;\n        case 0xF0:\n            switch (status) {\n                case MIDI_CLOCK:\n                case MIDI_TICK:\n                case MIDI_START:\n                case MIDI_CONTINUE:\n                case MIDI_STOP:\n                case MIDI_ACTIVESENSE:\n                case MIDI_RESET:\n                case MIDI_TUNEREQUEST:\n                    return ONE;\n                case MIDI_SONGPOSITION:\n                    return THREE;\n                case MIDI_TC_QUARTERFRAME:\n                case MIDI_SONGSELECT:\n                    return TWO;\n                case SYSEX_END:\n                case SYSEX_BEGIN:\n                default:\n                    return UNDEFINED;\n            }\n        default:\n            return UNDEFINED;\n    }\n}\n\nvoid midi_send_cc(MidiDevice* device, uint8_t chan, uint8_t num, uint8_t val) {\n    // CC Status: 0xB0 to 0xBF where the low nibble is the MIDI channel.\n    // CC Data: Controller Num, Controller Val\n    device->send_func(device, 3, MIDI_CC | (chan & MIDI_CHANMASK), num & 0x7F, val & 0x7F);\n}\n\nvoid midi_send_noteon(MidiDevice* device, uint8_t chan, uint8_t num, uint8_t vel) {\n    // Note Data: Note Num, Note Velocity\n    device->send_func(device, 3, MIDI_NOTEON | (chan & MIDI_CHANMASK), num & 0x7F, vel & 0x7F);\n}\n\nvoid midi_send_noteoff(MidiDevice* device, uint8_t chan, uint8_t num, uint8_t vel) {\n    // Note Data: Note Num, Note Velocity\n    device->send_func(device, 3, MIDI_NOTEOFF | (chan & MIDI_CHANMASK), num & 0x7F, vel & 0x7F);\n}\n\nvoid midi_send_aftertouch(MidiDevice* device, uint8_t chan, uint8_t note_num, uint8_t amt) {\n    device->send_func(device, 3, MIDI_AFTERTOUCH | (chan & MIDI_CHANMASK), note_num & 0x7F, amt & 0x7F);\n}\n\n// XXX does this work right?\n// amt in range -0x2000, 0x1fff\n// uAmt should be in range..\n// 0x0000 to 0x3FFF\nvoid midi_send_pitchbend(MidiDevice* device, uint8_t chan, int16_t amt) {\n    uint16_t uAmt;\n    // check range\n    if (amt > 0x1fff) {\n        uAmt = 0x3FFF;\n    } else if (amt < -0x2000) {\n        uAmt = 0;\n    } else {\n        uAmt = amt + 0x2000;\n    }\n    device->send_func(device, 3, MIDI_PITCHBEND | (chan & MIDI_CHANMASK), uAmt & 0x7F, (uAmt >> 7) & 0x7F);\n}\n\nvoid midi_send_programchange(MidiDevice* device, uint8_t chan, uint8_t num) {\n    device->send_func(device, 2, MIDI_PROGCHANGE | (chan & MIDI_CHANMASK), num & 0x7F, 0);\n}\n\nvoid midi_send_channelpressure(MidiDevice* device, uint8_t chan, uint8_t amt) {\n    device->send_func(device, 2, MIDI_CHANPRESSURE | (chan & MIDI_CHANMASK), amt & 0x7F, 0);\n}\n\nvoid midi_send_clock(MidiDevice* device) {\n    device->send_func(device, 1, MIDI_CLOCK, 0, 0);\n}\n\nvoid midi_send_tick(MidiDevice* device) {\n    device->send_func(device, 1, MIDI_TICK, 0, 0);\n}\n\nvoid midi_send_start(MidiDevice* device) {\n    device->send_func(device, 1, MIDI_START, 0, 0);\n}\n\nvoid midi_send_continue(MidiDevice* device) {\n    device->send_func(device, 1, MIDI_CONTINUE, 0, 0);\n}\n\nvoid midi_send_stop(MidiDevice* device) {\n    device->send_func(device, 1, MIDI_STOP, 0, 0);\n}\n\nvoid midi_send_activesense(MidiDevice* device) {\n    device->send_func(device, 1, MIDI_ACTIVESENSE, 0, 0);\n}\n\nvoid midi_send_reset(MidiDevice* device) {\n    device->send_func(device, 1, MIDI_RESET, 0, 0);\n}\n\nvoid midi_send_tcquarterframe(MidiDevice* device, uint8_t time) {\n    device->send_func(device, 2, MIDI_TC_QUARTERFRAME, time & 0x7F, 0);\n}\n\n// XXX is this right?\nvoid midi_send_songposition(MidiDevice* device, uint16_t pos) {\n    device->send_func(device, 3, MIDI_SONGPOSITION, pos & 0x7F, (pos >> 7) & 0x7F);\n}\n\nvoid midi_send_songselect(MidiDevice* device, uint8_t song) {\n    device->send_func(device, 2, MIDI_SONGSELECT, song & 0x7F, 0);\n}\n\nvoid midi_send_tunerequest(MidiDevice* device) {\n    device->send_func(device, 1, MIDI_TUNEREQUEST, 0, 0);\n}\n\nvoid midi_send_byte(MidiDevice* device, uint8_t b) {\n    device->send_func(device, 1, b, 0, 0);\n}\n\nvoid midi_send_data(MidiDevice* device, uint16_t count, uint8_t byte0, uint8_t byte1, uint8_t byte2) {\n    // ensure that the count passed along is always 3 or lower\n    if (count > 3) {\n        // TODO how to do this correctly?\n    }\n    device->send_func(device, count, byte0, byte1, byte2);\n}\n\nvoid midi_send_array(MidiDevice* device, uint16_t count, uint8_t* array) {\n    uint16_t i;\n    for (i = 0; i < count; i += 3) {\n        uint8_t  b[3]    = {0, 0, 0};\n        uint16_t to_send = count - i;\n        to_send          = (to_send > 3) ? 3 : to_send;\n        memcpy(b, array + i, to_send);\n        midi_send_data(device, to_send, b[0], b[1], b[2]);\n    }\n}\n\nvoid midi_register_cc_callback(MidiDevice* device, midi_three_byte_func_t func) {\n    device->input_cc_callback = func;\n}\n\nvoid midi_register_noteon_callback(MidiDevice* device, midi_three_byte_func_t func) {\n    device->input_noteon_callback = func;\n}\n\nvoid midi_register_noteoff_callback(MidiDevice* device, midi_three_byte_func_t func) {\n    device->input_noteoff_callback = func;\n}\n\nvoid midi_register_aftertouch_callback(MidiDevice* device, midi_three_byte_func_t func) {\n    device->input_aftertouch_callback = func;\n}\n\nvoid midi_register_pitchbend_callback(MidiDevice* device, midi_three_byte_func_t func) {\n    device->input_pitchbend_callback = func;\n}\n\nvoid midi_register_songposition_callback(MidiDevice* device, midi_three_byte_func_t func) {\n    device->input_songposition_callback = func;\n}\n\nvoid midi_register_progchange_callback(MidiDevice* device, midi_two_byte_func_t func) {\n    device->input_progchange_callback = func;\n}\n\nvoid midi_register_chanpressure_callback(MidiDevice* device, midi_two_byte_func_t func) {\n    device->input_chanpressure_callback = func;\n}\n\nvoid midi_register_songselect_callback(MidiDevice* device, midi_two_byte_func_t func) {\n    device->input_songselect_callback = func;\n}\n\nvoid midi_register_tc_quarterframe_callback(MidiDevice* device, midi_two_byte_func_t func) {\n    device->input_tc_quarterframe_callback = func;\n}\n\nvoid midi_register_realtime_callback(MidiDevice* device, midi_one_byte_func_t func) {\n    device->input_realtime_callback = func;\n}\n\nvoid midi_register_tunerequest_callback(MidiDevice* device, midi_one_byte_func_t func) {\n    device->input_tunerequest_callback = func;\n}\n\nvoid midi_register_sysex_callback(MidiDevice* device, midi_sysex_func_t func) {\n    device->input_sysex_callback = func;\n}\n\nvoid midi_register_fallthrough_callback(MidiDevice* device, midi_var_byte_func_t func) {\n    device->input_fallthrough_callback = func;\n}\n\nvoid midi_register_catchall_callback(MidiDevice* device, midi_var_byte_func_t func) {\n    device->input_catchall_callback = func;\n}\n"
  },
  {
    "path": "quantum/midi/midi.h",
    "content": "// midi for embedded chips,\n// Copyright 2010 Alex Norman\n//\n// This file is part of avr-midi.\n//\n// avr-midi is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n//(at your option) any later version.\n//\n// avr-midi is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with avr-midi.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * @file\n * @brief The main midi functions\n *\n * This file includes all of the functions you need to set up and process a\n * midi device, send midi, and register midi callbacks.\n *\n */\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"midi_device.h\"\n#include \"midi_function_types.h\"\n\n/**\n * @defgroup midi_device_setup_process Device initialization and processing\n * @brief These are method that you must use to initialize and run a device\n *\n * @{\n */\n\n/**\n * @brief Initialize a device\n *\n * You must call this before using the device in question.\n *\n * @param device the device to initialize\n */\nvoid midi_device_init(MidiDevice* device); // [implementation in midi_device.c]\n\n/**\n * @brief Process input data\n *\n * This method drives the input processing, you must call this method frequently\n * if you expect to have your input callbacks called.\n *\n * @param device the device to process\n */\nvoid midi_device_process(MidiDevice* device); // [implementation in midi_device.c]\n\n/**@}*/\n\n/**\n * @defgroup send_functions Midi send functions\n * @brief These are the functions you use to send midi data through a device.\n * @{\n */\n\n/**\n * @brief Send a control change message (cc) via the given device.\n *\n * @param device the device to use for sending\n * @param chan the channel to send on, 0-15\n * @param num the cc num\n * @param val the value of that cc num\n */\nvoid midi_send_cc(MidiDevice* device, uint8_t chan, uint8_t num, uint8_t val);\n\n/**\n * @brief Send a note on message via the given device.\n *\n * @param device the device to use for sending\n * @param chan the channel to send on, 0-15\n * @param num the note number\n * @param vel the note velocity\n */\nvoid midi_send_noteon(MidiDevice* device, uint8_t chan, uint8_t num, uint8_t vel);\n\n/**\n * @brief Send a note off message via the given device.\n *\n * @param device the device to use for sending\n * @param chan the channel to send on, 0-15\n * @param num the note number\n * @param vel the note velocity\n */\nvoid midi_send_noteoff(MidiDevice* device, uint8_t chan, uint8_t num, uint8_t vel);\n\n/**\n * @brief Send an after touch message via the given device.\n *\n * @param device the device to use for sending\n * @param chan the channel to send on, 0-15\n * @param note_num the note number\n * @param amt the after touch amount\n */\nvoid midi_send_aftertouch(MidiDevice* device, uint8_t chan, uint8_t note_num, uint8_t amt);\n\n/**\n * @brief Send a pitch bend message via the given device.\n *\n * @param device the device to use for sending\n * @param chan the channel to send on, 0-15\n * @param amt the bend amount range: -8192..8191, 0 means no bend\n */\nvoid midi_send_pitchbend(MidiDevice* device, uint8_t chan, int16_t amt); // range -8192, 8191\n\n/**\n * @brief Send a program change message via the given device.\n *\n * @param device the device to use for sending\n * @param chan the channel to send on, 0-15\n * @param num the program to change to\n */\nvoid midi_send_programchange(MidiDevice* device, uint8_t chan, uint8_t num);\n\n/**\n * @brief Send a channel pressure message via the given device.\n *\n * @param device the device to use for sending\n * @param chan the channel to send on, 0-15\n * @param amt the amount of channel pressure\n */\nvoid midi_send_channelpressure(MidiDevice* device, uint8_t chan, uint8_t amt);\n\n/**\n * @brief Send a clock message via the given device.\n *\n * @param device the device to use for sending\n */\nvoid midi_send_clock(MidiDevice* device);\n\n/**\n * @brief Send a tick message via the given device.\n *\n * @param device the device to use for sending\n */\nvoid midi_send_tick(MidiDevice* device);\n\n/**\n * @brief Send a start message via the given device.\n *\n * @param device the device to use for sending\n */\nvoid midi_send_start(MidiDevice* device);\n\n/**\n * @brief Send a continue message via the given device.\n *\n * @param device the device to use for sending\n */\nvoid midi_send_continue(MidiDevice* device);\n\n/**\n * @brief Send a stop message via the given device.\n *\n * @param device the device to use for sending\n */\nvoid midi_send_stop(MidiDevice* device);\n\n/**\n * @brief Send an active sense message via the given device.\n *\n * @param device the device to use for sending\n */\nvoid midi_send_activesense(MidiDevice* device);\n\n/**\n * @brief Send a reset message via the given device.\n *\n * @param device the device to use for sending\n */\nvoid midi_send_reset(MidiDevice* device);\n\n/**\n * @brief Send a tc quarter frame message via the given device.\n *\n * @param device the device to use for sending\n * @param time the time of this quarter frame, range 0..16383\n */\nvoid midi_send_tcquarterframe(MidiDevice* device, uint8_t time);\n\n/**\n * @brief Send a song position message via the given device.\n *\n * @param device the device to use for sending\n * @param pos the song position\n */\nvoid midi_send_songposition(MidiDevice* device, uint16_t pos);\n\n/**\n * @brief Send a song select message via the given device.\n *\n * @param device the device to use for sending\n * @param song the song to select\n */\nvoid midi_send_songselect(MidiDevice* device, uint8_t song);\n\n/**\n * @brief Send a tune request message via the given device.\n *\n * @param device the device to use for sending\n */\nvoid midi_send_tunerequest(MidiDevice* device);\n\n/**\n * @brief Send a byte via the given device.\n *\n * This is a generic method for sending data via the given midi device.\n * This would be useful for sending sysex data or messages that are not\n * implemented in this API, if there are any.  Please contact the author\n * if you find some so we can add them.\n *\n * @param device the device to use for sending\n * @param b the byte to send\n */\nvoid midi_send_byte(MidiDevice* device, uint8_t b);\n\n/**\n * @brief Send up to 3 bytes of data\n *\n * % 4 is applied to count so that you can use this to pass sysex through\n *\n * @param device the device to use for sending\n * @param count the count of bytes to send, %4 is applied\n * @param byte0 the first byte\n * @param byte1 the second byte, ignored if cnt % 4 != 2\n * @param byte2 the third byte, ignored if cnt % 4 != 3\n */\nvoid midi_send_data(MidiDevice* device, uint16_t count, uint8_t byte0, uint8_t byte1, uint8_t byte2);\n\n/**\n * @brief Send an array of formatted midi data.\n *\n * Can be used for sysex.\n *\n * @param device the device to use for sending\n * @param count the count of bytes to send\n * @param array the array of bytes\n */\nvoid midi_send_array(MidiDevice* device, uint16_t count, uint8_t* array);\n\n/**@}*/\n\n/**\n * @defgroup input_callback_reg Input callback registration functions\n *\n * @brief These are the functions you use to register your input callbacks.\n *\n * The functions are called when the appropriate midi message is matched on the\n * associated device's input.\n *\n * @{\n */\n\n// three byte funcs\n\n/**\n * @brief Register a control change message (cc) callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_cc_callback(MidiDevice* device, midi_three_byte_func_t func);\n\n/**\n * @brief Register a note on callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_noteon_callback(MidiDevice* device, midi_three_byte_func_t func);\n\n/**\n * @brief Register a note off callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_noteoff_callback(MidiDevice* device, midi_three_byte_func_t func);\n\n/**\n * @brief Register an after touch callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\n\nvoid midi_register_aftertouch_callback(MidiDevice* device, midi_three_byte_func_t func);\n\n/**\n * @brief Register a pitch bend callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_pitchbend_callback(MidiDevice* device, midi_three_byte_func_t func);\n\n/**\n * @brief Register a song position callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_songposition_callback(MidiDevice* device, midi_three_byte_func_t func);\n\n// two byte funcs\n\n/**\n * @brief Register a program change callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_progchange_callback(MidiDevice* device, midi_two_byte_func_t func);\n\n/**\n * @brief Register a channel pressure callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_chanpressure_callback(MidiDevice* device, midi_two_byte_func_t func);\n\n/**\n * @brief Register a song select callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_songselect_callback(MidiDevice* device, midi_two_byte_func_t func);\n\n/**\n * @brief Register a tc quarter frame callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_tc_quarterframe_callback(MidiDevice* device, midi_two_byte_func_t func);\n\n// one byte funcs\n\n/**\n * @brief Register a realtime callback.\n *\n * The callback will be called for all of the real time message types.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_realtime_callback(MidiDevice* device, midi_one_byte_func_t func);\n\n/**\n * @brief Register a tune request callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_tunerequest_callback(MidiDevice* device, midi_one_byte_func_t func);\n\n/**\n * @brief Register a sysex callback.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_sysex_callback(MidiDevice* device, midi_sysex_func_t func);\n\n/**\n * @brief Register fall through callback.\n *\n * This is only called if a more specific callback is not matched and called.\n * For instance, if you don't register a note on callback but you get a note on message\n * the fall through callback will be called, if it is registered.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_fallthrough_callback(MidiDevice* device, midi_var_byte_func_t func);\n\n/**\n * @brief Register a catch all callback.\n *\n * If registered, the catch all callback is called for every message that is\n * matched, even if a more specific or the fallthrough callback is registered.\n *\n * @param device the device associate with\n * @param func the callback function to register\n */\nvoid midi_register_catchall_callback(MidiDevice* device, midi_var_byte_func_t func);\n\n/**@}*/\n\n/**\n * @defgroup midi_util Device independent utility functions.\n * @{\n */\n\n/**\n * \\enum midi_packet_length_t\n *\n * An enumeration of the possible packet length values.\n */\ntypedef enum { UNDEFINED = 0, ONE = 1, TWO = 2, THREE = 3 } midi_packet_length_t;\n\n/**\n * @brief Test to see if the byte given is a status byte\n * @param theByte the byte to test\n * @return true if the byte given is a midi status byte\n */\nbool midi_is_statusbyte(uint8_t theByte);\n\n/**\n * @brief Test to see if the byte given is a realtime message\n * @param theByte the byte to test\n * @return true if it is a realtime message, false otherwise\n */\nbool midi_is_realtime(uint8_t theByte);\n\n/**\n * @brief Find the length of the packet associated with the status byte given\n * @param status the status byte\n * @return the length of the packet, will return UNDEFINED if the byte is not\n * a status byte or if it is a sysex status byte\n */\nmidi_packet_length_t midi_packet_length(uint8_t status);\n\n/**@}*/\n\n/**\n * @defgroup defines Midi status and miscellaneous utility #defines\n *\n * @{\n */\n\n#define SYSEX_BEGIN 0xF0\n#define SYSEX_END 0xF7\n\n// if you and this with a byte and you get anything non-zero\n// it is a status message\n#define MIDI_STATUSMASK 0x80\n// if you and this with a status message that contains channel info,\n// you'll get the channel\n#define MIDI_CHANMASK 0x0F\n\n#define MIDI_CC 0xB0\n#define MIDI_NOTEON 0x90\n#define MIDI_NOTEOFF 0x80\n#define MIDI_AFTERTOUCH 0xA0\n#define MIDI_PITCHBEND 0xE0\n#define MIDI_PROGCHANGE 0xC0\n#define MIDI_CHANPRESSURE 0xD0\n\n// midi realtime\n#define MIDI_CLOCK 0xF8\n#define MIDI_TICK 0xF9\n#define MIDI_START 0xFA\n#define MIDI_CONTINUE 0xFB\n#define MIDI_STOP 0xFC\n#define MIDI_ACTIVESENSE 0xFE\n#define MIDI_RESET 0xFF\n\n#define MIDI_TC_QUARTERFRAME 0xF1\n#define MIDI_SONGPOSITION 0xF2\n#define MIDI_SONGSELECT 0xF3\n#define MIDI_TUNEREQUEST 0xF6\n\n// This ID is for educational or development use only\n#define SYSEX_EDUMANUFID 0x7D\n\n/**@}*/\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/midi/midi_device.c",
    "content": "// midi for embedded chips,\n// Copyright 2010 Alex Norman\n//\n// This file is part of avr-midi.\n//\n// avr-midi is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n//(at your option) any later version.\n//\n// avr-midi is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with avr-midi.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"midi_device.h\"\n#include \"midi.h\"\n\n#ifndef NULL\n#    define NULL 0\n#endif\n\n// forward declarations, internally used to call the callbacks\nvoid midi_input_callbacks(MidiDevice* device, uint16_t cnt, uint8_t byte0, uint8_t byte1, uint8_t byte2);\nvoid midi_process_byte(MidiDevice* device, uint8_t input);\n\nvoid midi_device_init(MidiDevice* device) {\n    device->input_state = IDLE;\n    device->input_count = 0;\n    bytequeue_init(&device->input_queue, device->input_queue_data, MIDI_INPUT_QUEUE_LENGTH);\n\n    // three byte funcs\n    device->input_cc_callback           = NULL;\n    device->input_noteon_callback       = NULL;\n    device->input_noteoff_callback      = NULL;\n    device->input_aftertouch_callback   = NULL;\n    device->input_pitchbend_callback    = NULL;\n    device->input_songposition_callback = NULL;\n\n    // two byte funcs\n    device->input_progchange_callback      = NULL;\n    device->input_chanpressure_callback    = NULL;\n    device->input_songselect_callback      = NULL;\n    device->input_tc_quarterframe_callback = NULL;\n\n    // one byte funcs\n    device->input_realtime_callback    = NULL;\n    device->input_tunerequest_callback = NULL;\n\n    // var byte functions\n    device->input_sysex_callback       = NULL;\n    device->input_fallthrough_callback = NULL;\n    device->input_catchall_callback    = NULL;\n\n    device->pre_input_process_callback = NULL;\n}\n\nvoid midi_device_input(MidiDevice* device, uint8_t cnt, uint8_t* input) {\n    uint8_t i;\n    for (i = 0; i < cnt; i++)\n        bytequeue_enqueue(&device->input_queue, input[i]);\n}\n\nvoid midi_device_set_send_func(MidiDevice* device, midi_var_byte_func_t send_func) {\n    device->send_func = send_func;\n}\n\nvoid midi_device_set_pre_input_process_func(MidiDevice* device, midi_no_byte_func_t pre_process_func) {\n    device->pre_input_process_callback = pre_process_func;\n}\n\nvoid midi_device_process(MidiDevice* device) {\n    // call the pre_input_process_callback if there is one\n    if (device->pre_input_process_callback) device->pre_input_process_callback(device);\n\n    // pull stuff off the queue and process\n    byteQueueIndex_t len = bytequeue_length(&device->input_queue);\n    uint16_t         i;\n    // TODO limit number of bytes processed?\n    for (i = 0; i < len; i++) {\n        uint8_t val = bytequeue_get(&device->input_queue, 0);\n        midi_process_byte(device, val);\n        bytequeue_remove(&device->input_queue, 1);\n    }\n}\n\nvoid midi_process_byte(MidiDevice* device, uint8_t input) {\n    if (midi_is_realtime(input)) {\n        // call callback, store and restore state\n        input_state_t state = device->input_state;\n        device->input_state = ONE_BYTE_MESSAGE;\n        midi_input_callbacks(device, 1, input, 0, 0);\n        device->input_state = state;\n    } else if (midi_is_statusbyte(input)) {\n        // store the byte\n        if (device->input_state != SYSEX_MESSAGE) {\n            device->input_buffer[0] = input;\n            device->input_count     = 1;\n        }\n        switch (midi_packet_length(input)) {\n            case ONE:\n                device->input_state = ONE_BYTE_MESSAGE;\n                ;\n                midi_input_callbacks(device, 1, input, 0, 0);\n                device->input_state = IDLE;\n                break;\n            case TWO:\n                device->input_state = TWO_BYTE_MESSAGE;\n                break;\n            case THREE:\n                device->input_state = THREE_BYTE_MESSAGE;\n                break;\n            case UNDEFINED:\n                switch (input) {\n                    case SYSEX_BEGIN:\n                        device->input_state     = SYSEX_MESSAGE;\n                        device->input_buffer[0] = input;\n                        device->input_count     = 1;\n                        break;\n                    case SYSEX_END:\n                        // send what is left in the input buffer, set idle\n                        device->input_buffer[device->input_count % 3] = input;\n                        device->input_count += 1;\n                        // call the callback\n                        midi_input_callbacks(device, device->input_count, device->input_buffer[0], device->input_buffer[1], device->input_buffer[2]);\n                        device->input_state = IDLE;\n                        break;\n                    default:\n                        device->input_state = IDLE;\n                        device->input_count = 0;\n                }\n\n                break;\n            default:\n                device->input_state = IDLE;\n                device->input_count = 0;\n                break;\n        }\n    } else {\n        if (device->input_state != IDLE) {\n            // store the byte\n            device->input_buffer[device->input_count % 3] = input;\n            // increment count\n            uint16_t prev = device->input_count;\n            device->input_count += 1;\n\n            switch (prev % 3) {\n                case 2:\n                    // call callback\n                    midi_input_callbacks(device, device->input_count, device->input_buffer[0], device->input_buffer[1], device->input_buffer[2]);\n                    if (device->input_state != SYSEX_MESSAGE) {\n                        // set to 1, keeping status byte, allowing for running status\n                        device->input_count = 1;\n                    }\n                    break;\n                case 1:\n                    if (device->input_state == TWO_BYTE_MESSAGE) {\n                        // call callback\n                        midi_input_callbacks(device, device->input_count, device->input_buffer[0], device->input_buffer[1], 0);\n                        if (device->input_state != SYSEX_MESSAGE) {\n                            // set to 1, keeping status byte, allowing for running status\n                            device->input_count = 1;\n                        }\n                    }\n                    break;\n                case 0:\n                default:\n                    // one byte messages are dealt with directly\n                    break;\n            }\n        }\n    }\n}\n\nvoid midi_input_callbacks(MidiDevice* device, uint16_t cnt, uint8_t byte0, uint8_t byte1, uint8_t byte2) {\n    // did we end up calling a callback?\n    bool called = false;\n    if (device->input_state == SYSEX_MESSAGE) {\n        if (device->input_sysex_callback) {\n            const uint16_t start  = ((cnt - 1) / 3) * 3;\n            const uint8_t  length = (cnt - start);\n            uint8_t        data[3];\n            data[0] = byte0;\n            data[1] = byte1;\n            data[2] = byte2;\n            device->input_sysex_callback(device, start, length, data);\n            called = true;\n        }\n    } else {\n        switch (cnt) {\n            case 3: {\n                midi_three_byte_func_t func = NULL;\n                switch (byte0 & 0xF0) {\n                    case MIDI_CC:\n                        func = device->input_cc_callback;\n                        break;\n                    case MIDI_NOTEON:\n                        func = device->input_noteon_callback;\n                        break;\n                    case MIDI_NOTEOFF:\n                        func = device->input_noteoff_callback;\n                        break;\n                    case MIDI_AFTERTOUCH:\n                        func = device->input_aftertouch_callback;\n                        break;\n                    case MIDI_PITCHBEND:\n                        func = device->input_pitchbend_callback;\n                        break;\n                    case 0xF0:\n                        if (byte0 == MIDI_SONGPOSITION) func = device->input_songposition_callback;\n                        break;\n                    default:\n                        break;\n                }\n                if (func) {\n                    // mask off the channel for non song position functions\n                    if (byte0 == MIDI_SONGPOSITION)\n                        func(device, byte0, byte1, byte2);\n                    else\n                        func(device, byte0 & 0x0F, byte1, byte2);\n                    called = true;\n                }\n            } break;\n            case 2: {\n                midi_two_byte_func_t func = NULL;\n                switch (byte0 & 0xF0) {\n                    case MIDI_PROGCHANGE:\n                        func = device->input_progchange_callback;\n                        break;\n                    case MIDI_CHANPRESSURE:\n                        func = device->input_chanpressure_callback;\n                        break;\n                    case 0xF0:\n                        if (byte0 == MIDI_SONGSELECT)\n                            func = device->input_songselect_callback;\n                        else if (byte0 == MIDI_TC_QUARTERFRAME)\n                            func = device->input_tc_quarterframe_callback;\n                        break;\n                    default:\n                        break;\n                }\n                if (func) {\n                    // mask off the channel\n                    if (byte0 == MIDI_SONGSELECT || byte0 == MIDI_TC_QUARTERFRAME)\n                        func(device, byte0, byte1);\n                    else\n                        func(device, byte0 & 0x0F, byte1);\n                    called = true;\n                }\n            } break;\n            case 1: {\n                midi_one_byte_func_t func = NULL;\n                if (midi_is_realtime(byte0))\n                    func = device->input_realtime_callback;\n                else if (byte0 == MIDI_TUNEREQUEST)\n                    func = device->input_tunerequest_callback;\n                if (func) {\n                    func(device, byte0);\n                    called = true;\n                }\n            } break;\n            default:\n                // just in case\n                if (cnt > 3) cnt = 0;\n                break;\n        }\n    }\n\n    // if there is fallthrough default callback and we haven't called a more specific one,\n    // call the fallthrough\n    if (!called && device->input_fallthrough_callback) device->input_fallthrough_callback(device, cnt, byte0, byte1, byte2);\n    // always call the catch all if it exists\n    if (device->input_catchall_callback) device->input_catchall_callback(device, cnt, byte0, byte1, byte2);\n}\n"
  },
  {
    "path": "quantum/midi/midi_device.h",
    "content": "// midi for embedded chips,\n// Copyright 2010 Alex Norman\n//\n// This file is part of avr-midi.\n//\n// avr-midi is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n//(at your option) any later version.\n//\n// avr-midi is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with avr-midi.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * @file\n * @brief Device implementation functions\n */\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * @defgroup midi_device Functions used when implementing your own midi device.\n *\n * You use the functions when you are implementing your own midi device.\n *\n * You set a send function to actually send bytes via your device, this method\n * is called when you call a send function with this device, for instance\n * midi_send_cc\n *\n * You use the midi_device_input to process input data from the device and pass\n * it through the device's associated callbacks.\n *\n * You use the midi_device_set_pre_input_process_func if you want to have a\n * function called at the beginning of the device's process function, generally\n * to poll for input and pass that into midi_device_input\n *\n * @{\n */\n\n#include \"midi_function_types.h\"\n#include \"bytequeue/bytequeue.h\"\n#define MIDI_INPUT_QUEUE_LENGTH 192\n\ntypedef enum { IDLE, ONE_BYTE_MESSAGE = 1, TWO_BYTE_MESSAGE = 2, THREE_BYTE_MESSAGE = 3, SYSEX_MESSAGE } input_state_t;\n\ntypedef void (*midi_no_byte_func_t)(MidiDevice* device);\n\n/**\n * \\struct _midi_device\n *\n * @brief This structure represents the input and output functions and\n * processing data for a midi device.\n *\n * A device can represent an actual physical device [serial port, usb port] or\n * something virtual.\n * You should not need to modify this structure directly.\n */\nstruct _midi_device {\n    // output send function\n    midi_var_byte_func_t send_func;\n\n    //********input callbacks\n    // three byte funcs\n    midi_three_byte_func_t input_cc_callback;\n    midi_three_byte_func_t input_noteon_callback;\n    midi_three_byte_func_t input_noteoff_callback;\n    midi_three_byte_func_t input_aftertouch_callback;\n    midi_three_byte_func_t input_pitchbend_callback;\n    midi_three_byte_func_t input_songposition_callback;\n    // two byte funcs\n    midi_two_byte_func_t input_progchange_callback;\n    midi_two_byte_func_t input_chanpressure_callback;\n    midi_two_byte_func_t input_songselect_callback;\n    midi_two_byte_func_t input_tc_quarterframe_callback;\n    // one byte funcs\n    midi_one_byte_func_t input_realtime_callback;\n    midi_one_byte_func_t input_tunerequest_callback;\n\n    // sysex\n    midi_sysex_func_t input_sysex_callback;\n\n    // only called if more specific callback is not matched\n    midi_var_byte_func_t input_fallthrough_callback;\n    // called if registered, independent of other callbacks\n    midi_var_byte_func_t input_catchall_callback;\n\n    // pre input processing function\n    midi_no_byte_func_t pre_input_process_callback;\n\n    // for internal input processing\n    uint8_t       input_buffer[3];\n    input_state_t input_state;\n    uint16_t      input_count;\n\n    // for queueing data between the input and the processing functions\n    uint8_t     input_queue_data[MIDI_INPUT_QUEUE_LENGTH];\n    byteQueue_t input_queue;\n};\n\n/**\n * @brief Process input bytes.  This function parses bytes and calls the\n * appropriate callbacks associated with the given device.  You use this\n * function if you are creating a custom device and you want to have midi\n * input.\n *\n * @param device the midi device to associate the input with\n * @param cnt the number of bytes you are processing\n * @param input the bytes to process\n */\nvoid midi_device_input(MidiDevice* device, uint8_t cnt, uint8_t* input);\n\n/**\n * @brief Set the callback function that will be used for sending output\n * data bytes.  This is only used if you're creating a custom device.\n * You'll most likely want the callback function to disable interrupts so\n * that you can call the various midi send functions without worrying about\n * locking.\n *\n * \\param device the midi device to associate this callback with\n * \\param send_func the callback function that will do the sending\n */\nvoid midi_device_set_send_func(MidiDevice* device, midi_var_byte_func_t send_func);\n\n/**\n * @brief Set a callback which is called at the beginning of the\n * midi_device_process call.  This can be used to poll for input\n * data and send the data through the midi_device_input function.\n * You'll probably only use this if you're creating a custom device.\n *\n * \\param device the midi device to associate this callback with\n * \\param midi_no_byte_func_t the actual callback function\n */\nvoid midi_device_set_pre_input_process_func(MidiDevice* device, midi_no_byte_func_t pre_process_func);\n\n/**@}*/\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/midi/midi_function_types.h",
    "content": "// midi for embedded chips,\n// Copyright 2010 Alex Norman\n//\n// This file is part of avr-midi.\n//\n// avr-midi is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n//(at your option) any later version.\n//\n// avr-midi is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with avr-midi.  If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * @file\n * @brief Function signature definitions\n */\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <inttypes.h>\n#include <stdbool.h>\n\n// forward declaration\ntypedef struct _midi_device MidiDevice;\n\ntypedef void (*midi_one_byte_func_t)(MidiDevice *device, uint8_t byte);\ntypedef void (*midi_two_byte_func_t)(MidiDevice *device, uint8_t byte0, uint8_t byte1);\ntypedef void (*midi_three_byte_func_t)(MidiDevice *device, uint8_t byte0, uint8_t byte1, uint8_t byte2);\n// all bytes after count bytes should be ignored\ntypedef void (*midi_var_byte_func_t)(MidiDevice *device, uint16_t count, uint8_t byte0, uint8_t byte1, uint8_t byte2);\n\n// the start byte tells you how far into the sysex message you are, the data_length tells you how many bytes data is\ntypedef void (*midi_sysex_func_t)(MidiDevice *device, uint16_t start_byte, uint8_t data_length, uint8_t *data);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/midi/qmk_midi.c",
    "content": "#include <LUFA/Drivers/USB/USB.h>\n#include \"qmk_midi.h\"\n#include \"sysex_tools.h\"\n#include \"midi.h\"\n#include \"usb_descriptor.h\"\n#include \"process_midi.h\"\n\n#ifdef AUDIO_ENABLE\n#    include \"audio.h\"\n#    include <math.h>\n#endif\n\n/*******************************************************************************\n * MIDI\n ******************************************************************************/\n\nMidiDevice midi_device;\n\n#define SYSEX_START_OR_CONT 0x40\n#define SYSEX_ENDS_IN_1 0x50\n#define SYSEX_ENDS_IN_2 0x60\n#define SYSEX_ENDS_IN_3 0x70\n\n#define SYS_COMMON_1 0x50\n#define SYS_COMMON_2 0x20\n#define SYS_COMMON_3 0x30\n\nstatic void usb_send_func(MidiDevice* device, uint16_t cnt, uint8_t byte0, uint8_t byte1, uint8_t byte2) {\n    MIDI_EventPacket_t event;\n    event.Data1 = byte0;\n    event.Data2 = byte1;\n    event.Data3 = byte2;\n\n    uint8_t cable = 0;\n\n    // if the length is undefined we assume it is a SYSEX message\n    if (midi_packet_length(byte0) == UNDEFINED) {\n        switch (cnt) {\n            case 3:\n                if (byte2 == SYSEX_END)\n                    event.Event = MIDI_EVENT(cable, SYSEX_ENDS_IN_3);\n                else\n                    event.Event = MIDI_EVENT(cable, SYSEX_START_OR_CONT);\n                break;\n            case 2:\n                if (byte1 == SYSEX_END)\n                    event.Event = MIDI_EVENT(cable, SYSEX_ENDS_IN_2);\n                else\n                    event.Event = MIDI_EVENT(cable, SYSEX_START_OR_CONT);\n                break;\n            case 1:\n                if (byte0 == SYSEX_END)\n                    event.Event = MIDI_EVENT(cable, SYSEX_ENDS_IN_1);\n                else\n                    event.Event = MIDI_EVENT(cable, SYSEX_START_OR_CONT);\n                break;\n            default:\n                return; // invalid cnt\n        }\n    } else {\n        // deal with 'system common' messages\n        // TODO are there any more?\n        switch (byte0 & 0xF0) {\n            case MIDI_SONGPOSITION:\n                event.Event = MIDI_EVENT(cable, SYS_COMMON_3);\n                break;\n            case MIDI_SONGSELECT:\n            case MIDI_TC_QUARTERFRAME:\n                event.Event = MIDI_EVENT(cable, SYS_COMMON_2);\n                break;\n            default:\n                event.Event = MIDI_EVENT(cable, byte0);\n                break;\n        }\n    }\n\n    send_midi_packet(&event);\n}\n\nstatic void usb_get_midi(MidiDevice* device) {\n    MIDI_EventPacket_t event;\n    while (recv_midi_packet(&event)) {\n        midi_packet_length_t length = midi_packet_length(event.Data1);\n        uint8_t              input[3];\n        input[0] = event.Data1;\n        input[1] = event.Data2;\n        input[2] = event.Data3;\n        if (length == UNDEFINED) {\n            // sysex\n            if (event.Event == MIDI_EVENT(0, SYSEX_START_OR_CONT) || event.Event == MIDI_EVENT(0, SYSEX_ENDS_IN_3)) {\n                length = 3;\n            } else if (event.Event == MIDI_EVENT(0, SYSEX_ENDS_IN_2)) {\n                length = 2;\n            } else if (event.Event == MIDI_EVENT(0, SYSEX_ENDS_IN_1)) {\n                length = 1;\n            } else {\n                // XXX what to do?\n            }\n        }\n\n        // pass the data to the device input function\n        if (length != UNDEFINED) midi_device_input(device, length, input);\n    }\n}\n\nstatic void fallthrough_callback(MidiDevice* device, uint16_t cnt, uint8_t byte0, uint8_t byte1, uint8_t byte2) {\n#ifdef AUDIO_ENABLE\n    if (cnt == 3) {\n        switch (byte0 & 0xF0) {\n            case MIDI_NOTEON:\n                play_note(440.0f * powf(2.0f, ((byte1 & 0x7F) - 57) / 12.0f), (byte2 & 0x7F) / 8);\n                break;\n            case MIDI_NOTEOFF:\n                stop_note(440.0f * powf(2.0f, ((byte1 & 0x7F) - 57) / 12.0f));\n                break;\n        }\n    }\n    if (byte0 == MIDI_STOP) {\n        stop_all_notes();\n    }\n#endif\n}\n\nstatic void cc_callback(MidiDevice* device, uint8_t chan, uint8_t num, uint8_t val) {\n    // sending it back on the next channel\n    // midi_send_cc(device, (chan + 1) % 16, num, val);\n}\n\nvoid midi_init(void);\n\nvoid setup_midi(void) {\n#ifdef MIDI_ADVANCED\n    midi_init();\n#endif\n    midi_device_init(&midi_device);\n    midi_device_set_send_func(&midi_device, usb_send_func);\n    midi_device_set_pre_input_process_func(&midi_device, usb_get_midi);\n    midi_register_fallthrough_callback(&midi_device, fallthrough_callback);\n    midi_register_cc_callback(&midi_device, cc_callback);\n}\n"
  },
  {
    "path": "quantum/midi/qmk_midi.h",
    "content": "#pragma once\n\n#ifdef MIDI_ENABLE\n#    include \"midi.h\"\n#    include <LUFA/Drivers/USB/USB.h>\nextern MidiDevice midi_device;\nvoid              setup_midi(void);\nvoid              send_midi_packet(MIDI_EventPacket_t* event);\nbool              recv_midi_packet(MIDI_EventPacket_t* const event);\n#endif\n"
  },
  {
    "path": "quantum/midi/sysex_tools.c",
    "content": "// midi for embedded chips,\n// Copyright 2010 Alex Norman\n//\n// This file is part of avr-midi.\n//\n// avr-midi is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n//(at your option) any later version.\n//\n// avr-midi is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with avr-midi.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"sysex_tools.h\"\n\nuint16_t sysex_encoded_length(uint16_t decoded_length) {\n    uint8_t remainder = decoded_length % 7;\n    if (remainder)\n        return (decoded_length / 7) * 8 + remainder + 1;\n    else\n        return (decoded_length / 7) * 8;\n}\n\nuint16_t sysex_decoded_length(uint16_t encoded_length) {\n    uint8_t remainder = encoded_length % 8;\n    if (remainder)\n        return (encoded_length / 8) * 7 + remainder - 1;\n    else\n        return (encoded_length / 8) * 7;\n}\n\nuint16_t sysex_encode(uint8_t *encoded, const uint8_t *source, const uint16_t length) {\n    uint16_t encoded_full = length / 7; // number of full 8 byte sections from 7 bytes of input\n    uint16_t i, j;\n\n    // fill out the fully encoded sections\n    for (i = 0; i < encoded_full; i++) {\n        uint16_t encoded_msb_idx = i * 8;\n        uint16_t input_start_idx = i * 7;\n        encoded[encoded_msb_idx] = 0;\n        for (j = 0; j < 7; j++) {\n            uint8_t current = source[input_start_idx + j];\n            encoded[encoded_msb_idx] |= (0x80 & current) >> (1 + j);\n            encoded[encoded_msb_idx + 1 + j] = 0x7F & current;\n        }\n    }\n\n    // fill out the rest if there is any more\n    uint8_t remainder = length % 7;\n    if (remainder) {\n        uint16_t encoded_msb_idx = encoded_full * 8;\n        uint16_t input_start_idx = encoded_full * 7;\n        encoded[encoded_msb_idx] = 0;\n        for (j = 0; j < remainder; j++) {\n            uint8_t current = source[input_start_idx + j];\n            encoded[encoded_msb_idx] |= (0x80 & current) >> (1 + j);\n            encoded[encoded_msb_idx + 1 + j] = 0x7F & current;\n        }\n        return encoded_msb_idx + remainder + 1;\n    } else {\n        return encoded_full * 8;\n    }\n}\n\nuint16_t sysex_decode(uint8_t *decoded, const uint8_t *source, const uint16_t length) {\n    uint16_t decoded_full = length / 8;\n    uint16_t i, j;\n\n    if (length < 2) return 0;\n\n    // fill out the fully encoded sections\n    for (i = 0; i < decoded_full; i++) {\n        uint16_t encoded_msb_idx    = i * 8;\n        uint16_t output_start_index = i * 7;\n        for (j = 0; j < 7; j++) {\n            decoded[output_start_index + j] = 0x7F & source[encoded_msb_idx + j + 1];\n            decoded[output_start_index + j] |= (0x80 & (source[encoded_msb_idx] << (1 + j)));\n        }\n    }\n    uint8_t remainder = length % 8;\n    if (remainder) {\n        uint16_t encoded_msb_idx    = decoded_full * 8;\n        uint16_t output_start_index = decoded_full * 7;\n        for (j = 0; j < (remainder - 1); j++) {\n            decoded[output_start_index + j] = 0x7F & source[encoded_msb_idx + j + 1];\n            decoded[output_start_index + j] |= (0x80 & (source[encoded_msb_idx] << (1 + j)));\n        }\n        return decoded_full * 7 + remainder - 1;\n    } else {\n        return decoded_full * 7;\n    }\n}\n"
  },
  {
    "path": "quantum/midi/sysex_tools.h",
    "content": "// midi for embedded chips,\n// Copyright 2010 Alex Norman\n//\n// This file is part of avr-midi.\n//\n// avr-midi is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n//(at your option) any later version.\n//\n// avr-midi is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with avr-midi.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <inttypes.h>\n\n/**\n * @file\n * @brief Sysex utility functions\n *\n * These functions are for converting data to and from a \"midi-safe\" format,\n * which can be use to send data with sysex messages.  Sysex messages may only\n * contain data where the to bit is not set.\n *\n * An \"encoded\" midi message is one that contains all of the data from its\n * original state, but does not have any of the top bits set.\n *\n * Every 7 bytes of decoded data is converted into 8 bytes of encoded data and\n * visa-versa.  If you'd like to operate on small segments, make sure that you\n * encode in 7 byte increments and decode in 8 byte increments.\n *\n */\n\n/** @defgroup sysex_tools Sysex utility functions\n * @{\n */\n\n/**\n * @brief Compute the length of a message after it is encoded.\n *\n * @param decoded_length The length, in bytes, of the message to encode.\n *\n * @return The length, in bytes, of the message after encodeing.\n */\nuint16_t sysex_encoded_length(uint16_t decoded_length);\n\n/**\n * @brief Compute the length of a message after it is decoded.\n *\n * @param encoded_length The length, in bytes, of the encoded message.\n *\n * @return The length, in bytes, of the message after it is decoded.\n */\nuint16_t sysex_decoded_length(uint16_t encoded_length);\n\n/**\n * @brief Encode data so that it can be transmitted safely in a sysex message.\n *\n * @param encoded The output data buffer, must be at least sysex_encoded_length(length) bytes long.\n * @param source The input buffer of data to be encoded.\n * @param length The number of bytes from the input buffer to encode.\n *\n * @return number of bytes encoded.\n */\nuint16_t sysex_encode(uint8_t *encoded, const uint8_t *source, uint16_t length);\n\n/**\n * @brief Decode encoded data.\n *\n * @param decoded The output data buffer, must be at least sysex_decoded_length(length) bytes long.\n * @param source The input buffer of data to be decoded.\n * @param length The number of bytes from the input buffer to decode.\n *\n * @return number of bytes decoded.\n */\nuint16_t sysex_decode(uint8_t *decoded, const uint8_t *source, uint16_t length);\n\n/**@}*/\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/modifiers.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n/** \\brief 5-bit packed modifiers\n *\n * Mod bits:    43210\n *   bit 0      ||||+- Control\n *   bit 1      |||+-- Shift\n *   bit 2      ||+--- Alt\n *   bit 3      |+---- Gui\n *   bit 4      +----- LR flag(Left:0, Right:1)\n */\nenum mods_5bit {\n    MOD_LCTL = 0x01,\n    MOD_LSFT = 0x02,\n    MOD_LALT = 0x04,\n    MOD_LGUI = 0x08,\n    MOD_RCTL = 0x11,\n    MOD_RSFT = 0x12,\n    MOD_RALT = 0x14,\n    MOD_RGUI = 0x18,\n};\n#define MOD_HYPR (MOD_LCTL | MOD_LSFT | MOD_LALT | MOD_LGUI)\n#define MOD_MEH (MOD_LCTL | MOD_LSFT | MOD_LALT)\n\n/** \\brief 8-bit packed modifiers\n */\nenum mods_8bit {\n    MOD_BIT_LCTRL  = 0b00000001,\n    MOD_BIT_LSHIFT = 0b00000010,\n    MOD_BIT_LALT   = 0b00000100,\n    MOD_BIT_LGUI   = 0b00001000,\n    MOD_BIT_RCTRL  = 0b00010000,\n    MOD_BIT_RSHIFT = 0b00100000,\n    MOD_BIT_RALT   = 0b01000000,\n    MOD_BIT_RGUI   = 0b10000000,\n};\n#define MOD_MASK_CTRL (MOD_BIT_LCTRL | MOD_BIT_RCTRL)\n#define MOD_MASK_SHIFT (MOD_BIT_LSHIFT | MOD_BIT_RSHIFT)\n#define MOD_MASK_ALT (MOD_BIT_LALT | MOD_BIT_RALT)\n#define MOD_MASK_GUI (MOD_BIT_LGUI | MOD_BIT_RGUI)\n#define MOD_MASK_CS (MOD_MASK_CTRL | MOD_MASK_SHIFT)\n#define MOD_MASK_CA (MOD_MASK_CTRL | MOD_MASK_ALT)\n#define MOD_MASK_CG (MOD_MASK_CTRL | MOD_MASK_GUI)\n#define MOD_MASK_SA (MOD_MASK_SHIFT | MOD_MASK_ALT)\n#define MOD_MASK_SG (MOD_MASK_SHIFT | MOD_MASK_GUI)\n#define MOD_MASK_AG (MOD_MASK_ALT | MOD_MASK_GUI)\n#define MOD_MASK_CSA (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT)\n#define MOD_MASK_CSG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_GUI)\n#define MOD_MASK_CAG (MOD_MASK_CTRL | MOD_MASK_ALT | MOD_MASK_GUI)\n#define MOD_MASK_SAG (MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI)\n#define MOD_MASK_CSAG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI)\n"
  },
  {
    "path": "quantum/mousekey.c",
    "content": "/*\n * Copyright 2011 Jun Wako <wakojun@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <stdint.h>\n#include <string.h>\n#include \"keycode.h\"\n#include \"host.h\"\n#include \"timer.h\"\n#include \"print.h\"\n#include \"debug.h\"\n#include \"mousekey.h\"\n\nstatic inline int8_t times_inv_sqrt2(int8_t x) {\n    // 181/256 (0.70703125) is used as an approximation for 1/sqrt(2)\n    // because it is close to the exact value which is 0.707106781\n    const int16_t  n = x * 181;\n    const uint16_t d = 256;\n\n    // To ensure that the integer result is rounded accurately after\n    // division, check the sign of the numerator:\n    // If negative, subtract half of the denominator before dividing\n    // Otherwise, add half of the denominator before dividing\n    return n < 0 ? (n - d / 2) / d : (n + d / 2) / d;\n}\n\nstatic report_mouse_t mouse_report = {0};\nstatic void           mousekey_debug(void);\nstatic uint8_t        mousekey_accel        = 0;\nstatic uint8_t        mousekey_repeat       = 0;\nstatic uint8_t        mousekey_wheel_repeat = 0;\n#ifdef MOUSEKEY_INERTIA\nstatic uint8_t mousekey_frame     = 0; // track whether gesture is inactive, first frame, or repeating\nstatic int8_t  mousekey_x_dir     = 0; // -1 / 0 / 1 = left / neutral / right\nstatic int8_t  mousekey_y_dir     = 0; // -1 / 0 / 0 = up / neutral / down\nstatic int8_t  mousekey_x_inertia = 0; // current velocity, limit +/- MOUSEKEY_TIME_TO_MAX\nstatic int8_t  mousekey_y_inertia = 0; // ...\n#endif\n#ifdef MK_KINETIC_SPEED\nstatic uint16_t mouse_timer = 0;\n#endif\n\n#ifndef MK_3_SPEED\n\nstatic uint16_t last_timer_c = 0;\nstatic uint16_t last_timer_w = 0;\n\n/*\n * Mouse keys acceleration algorithm\n *  http://en.wikipedia.org/wiki/Mouse_keys\n *\n *  speed = delta * max_speed * (repeat / time_to_max)**((1000+curve)/1000)\n */\n/* milliseconds between the initial key press and first repeated motion event (0-2550) */\nuint8_t mk_delay = MOUSEKEY_DELAY / 10;\n/* milliseconds between repeated motion events (0-255) */\nuint8_t mk_interval = MOUSEKEY_INTERVAL;\n/* steady speed (in action_delta units) applied each event (0-255) */\nuint8_t mk_max_speed = MOUSEKEY_MAX_SPEED;\n/* number of events (count) accelerating to steady speed (0-255) */\nuint8_t mk_time_to_max = MOUSEKEY_TIME_TO_MAX;\n/* ramp used to reach maximum pointer speed (NOT SUPPORTED) */\n// int8_t mk_curve = 0;\n/* wheel params */\n/* milliseconds between the initial key press and first repeated motion event (0-2550) */\nuint8_t mk_wheel_delay = MOUSEKEY_WHEEL_DELAY / 10;\n/* milliseconds between repeated motion events (0-255) */\n#    ifdef MK_KINETIC_SPEED\nuint16_t mk_wheel_interval = 1000U / MOUSEKEY_WHEEL_INITIAL_MOVEMENTS;\n#    else\nuint8_t mk_wheel_interval = MOUSEKEY_WHEEL_INTERVAL;\n#    endif\nuint8_t mk_wheel_max_speed   = MOUSEKEY_WHEEL_MAX_SPEED;\nuint8_t mk_wheel_time_to_max = MOUSEKEY_WHEEL_TIME_TO_MAX;\n\n#    ifndef MK_COMBINED\n#        ifndef MK_KINETIC_SPEED\n#            ifndef MOUSEKEY_INERTIA\n\n/* Default accelerated mode */\n\nstatic uint8_t move_unit(void) {\n    uint16_t unit;\n    if (mousekey_accel & (1 << 0)) {\n        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 4;\n    } else if (mousekey_accel & (1 << 1)) {\n        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 2;\n    } else if (mousekey_accel & (1 << 2)) {\n        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed);\n    } else if (mousekey_repeat == 0) {\n        unit = MOUSEKEY_MOVE_DELTA;\n    } else if (mousekey_repeat >= mk_time_to_max) {\n        unit = MOUSEKEY_MOVE_DELTA * mk_max_speed;\n    } else {\n        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * mousekey_repeat) / mk_time_to_max;\n    }\n    return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit));\n}\n\n#            else // MOUSEKEY_INERTIA mode\n\nstatic int8_t move_unit(uint8_t axis) {\n    int16_t unit;\n\n    // handle X or Y axis\n    int8_t inertia, dir;\n    if (axis) {\n        inertia = mousekey_y_inertia;\n        dir     = mousekey_y_dir;\n    } else {\n        inertia = mousekey_x_inertia;\n        dir     = mousekey_x_dir;\n    }\n\n    if (mousekey_frame < 2) { // first frame(s): initial keypress moves one pixel\n        mousekey_frame = 1;\n        unit           = dir * MOUSEKEY_MOVE_DELTA;\n    } else { // acceleration\n        // linear acceleration (is here for reference, but doesn't feel as good during use)\n        // unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * inertia) / mk_time_to_max;\n\n        // x**2 acceleration (quadratic, more precise for short movements)\n        int16_t percent = (inertia << 8) / mk_time_to_max;\n        percent         = ((int32_t)percent * percent) >> 8;\n        if (inertia < 0) percent = -percent;\n\n        // unit = sign(inertia) + (percent of max speed)\n        if (inertia > 0)\n            unit = 1;\n        else if (inertia < 0)\n            unit = -1;\n        else\n            unit = 0;\n\n        unit = unit + ((mk_max_speed * percent) >> 8);\n    }\n\n    if (unit > MOUSEKEY_MOVE_MAX)\n        unit = MOUSEKEY_MOVE_MAX;\n    else if (unit < -MOUSEKEY_MOVE_MAX)\n        unit = -MOUSEKEY_MOVE_MAX;\n    return unit;\n}\n\n#            endif // end MOUSEKEY_INERTIA mode\n\nstatic uint8_t wheel_unit(void) {\n    uint16_t unit;\n    if (mousekey_accel & (1 << 0)) {\n        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 4;\n    } else if (mousekey_accel & (1 << 1)) {\n        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 2;\n    } else if (mousekey_accel & (1 << 2)) {\n        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed);\n    } else if (mousekey_wheel_repeat == 0) {\n        unit = MOUSEKEY_WHEEL_DELTA;\n    } else if (mousekey_wheel_repeat >= mk_wheel_time_to_max) {\n        unit = MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed;\n    } else {\n        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed * mousekey_wheel_repeat) / mk_wheel_time_to_max;\n    }\n    return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit));\n}\n\n#        else /* #ifndef MK_KINETIC_SPEED */\n\n/*\n * Kinetic movement  acceleration algorithm\n *\n *  current speed = I + A * T/50 + A * (T/50)^2 * 1/2 | maximum B\n *\n * T: time since the mouse movement started\n * E: mouse events per second (set through MOUSEKEY_INTERVAL, UHK sends 250, the\n *    pro micro on my Signum 3.0 sends only 125!)\n * I: initial speed at time 0\n * A: acceleration\n * B: base mouse travel speed\n */\nconst uint16_t mk_accelerated_speed = MOUSEKEY_ACCELERATED_SPEED;\nconst uint16_t mk_base_speed        = MOUSEKEY_BASE_SPEED;\nconst uint16_t mk_decelerated_speed = MOUSEKEY_DECELERATED_SPEED;\nconst uint16_t mk_initial_speed     = MOUSEKEY_INITIAL_SPEED;\n\nstatic uint8_t move_unit(void) {\n    uint16_t speed = mk_initial_speed;\n\n    if (mousekey_accel & (1 << 0)) {\n        speed = mk_decelerated_speed;\n    } else if (mousekey_accel & (1 << 2)) {\n        speed = mk_accelerated_speed;\n    } else if (mousekey_repeat && mouse_timer) {\n        const uint16_t time_elapsed = timer_elapsed(mouse_timer) / 50;\n        speed                       = mk_initial_speed + MOUSEKEY_MOVE_DELTA * time_elapsed + (MOUSEKEY_MOVE_DELTA * time_elapsed * time_elapsed) / 2;\n        if (speed > mk_base_speed) {\n            speed = mk_base_speed;\n        }\n    }\n    /* convert speed to USB mouse speed 1 to 127 */\n    speed = (uint8_t)(speed / (1000U / mk_interval));\n\n    if (speed > MOUSEKEY_MOVE_MAX) {\n        speed = MOUSEKEY_MOVE_MAX;\n    } else if (speed < 1) {\n        speed = 1;\n    }\n    return speed;\n}\n\nstatic uint8_t wheel_unit(void) {\n    uint16_t speed = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS;\n\n    if (mousekey_accel & (1 << 0)) {\n        speed = MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS;\n    } else if (mousekey_accel & (1 << 2)) {\n        speed = MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS;\n    } else if (mousekey_wheel_repeat && mouse_timer) {\n        if (mk_wheel_interval != MOUSEKEY_WHEEL_BASE_MOVEMENTS) {\n            const uint16_t time_elapsed = timer_elapsed(mouse_timer) / 50;\n            speed                       = MOUSEKEY_WHEEL_INITIAL_MOVEMENTS + 1 * time_elapsed + (1 * time_elapsed * time_elapsed) / 2;\n        }\n        if (speed > MOUSEKEY_WHEEL_BASE_MOVEMENTS) {\n            speed = MOUSEKEY_WHEEL_BASE_MOVEMENTS;\n        }\n    }\n    mk_wheel_interval = 1000U / speed;\n    return 1;\n}\n\n#        endif /* #ifndef MK_KINETIC_SPEED */\n#    else      /* #ifndef MK_COMBINED */\n\n/* Combined mode */\n\nstatic uint8_t move_unit(void) {\n    uint16_t unit;\n    if (mousekey_accel & (1 << 0)) {\n        unit = 1;\n    } else if (mousekey_accel & (1 << 1)) {\n        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed) / 2;\n    } else if (mousekey_accel & (1 << 2)) {\n        unit = MOUSEKEY_MOVE_MAX;\n    } else if (mousekey_repeat == 0) {\n        unit = MOUSEKEY_MOVE_DELTA;\n    } else if (mousekey_repeat >= mk_time_to_max) {\n        unit = MOUSEKEY_MOVE_DELTA * mk_max_speed;\n    } else {\n        unit = (MOUSEKEY_MOVE_DELTA * mk_max_speed * mousekey_repeat) / mk_time_to_max;\n    }\n    return (unit > MOUSEKEY_MOVE_MAX ? MOUSEKEY_MOVE_MAX : (unit == 0 ? 1 : unit));\n}\n\nstatic uint8_t wheel_unit(void) {\n    uint16_t unit;\n    if (mousekey_accel & (1 << 0)) {\n        unit = 1;\n    } else if (mousekey_accel & (1 << 1)) {\n        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed) / 2;\n    } else if (mousekey_accel & (1 << 2)) {\n        unit = MOUSEKEY_WHEEL_MAX;\n    } else if (mousekey_repeat == 0) {\n        unit = MOUSEKEY_WHEEL_DELTA;\n    } else if (mousekey_repeat >= mk_wheel_time_to_max) {\n        unit = MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed;\n    } else {\n        unit = (MOUSEKEY_WHEEL_DELTA * mk_wheel_max_speed * mousekey_repeat) / mk_wheel_time_to_max;\n    }\n    return (unit > MOUSEKEY_WHEEL_MAX ? MOUSEKEY_WHEEL_MAX : (unit == 0 ? 1 : unit));\n}\n\n#    endif /* #ifndef MK_COMBINED */\n\n#    ifdef MOUSEKEY_INERTIA\n\nstatic int8_t calc_inertia(int8_t direction, int8_t velocity) {\n    // simulate acceleration and deceleration\n\n    // deceleration\n    if ((direction > -1) && (velocity < 0))\n        velocity = (velocity + 1) * (256 - MOUSEKEY_FRICTION) / 256;\n    else if ((direction < 1) && (velocity > 0))\n        velocity = velocity * (256 - MOUSEKEY_FRICTION) / 256;\n\n    // acceleration\n    if ((direction > 0) && (velocity < mk_time_to_max))\n        velocity++;\n    else if ((direction < 0) && (velocity > -mk_time_to_max))\n        velocity--;\n\n    return velocity;\n}\n\n#    endif\n\nvoid mousekey_task(void) {\n    // report cursor and scroll movement independently\n    report_mouse_t tmpmr = mouse_report;\n\n    mouse_report.x = 0;\n    mouse_report.y = 0;\n    mouse_report.v = 0;\n    mouse_report.h = 0;\n\n#    ifdef MOUSEKEY_INERTIA\n\n    // if an animation is in progress and it's time for the next frame\n    if ((mousekey_frame) && timer_elapsed(last_timer_c) > ((mousekey_frame > 1) ? mk_interval : mk_delay * 10)) {\n        mousekey_x_inertia = calc_inertia(mousekey_x_dir, mousekey_x_inertia);\n        mousekey_y_inertia = calc_inertia(mousekey_y_dir, mousekey_y_inertia);\n\n        mouse_report.x = move_unit(0);\n        mouse_report.y = move_unit(1);\n\n        // prevent sticky \"drift\"\n        if ((!mousekey_x_dir) && (!mousekey_x_inertia)) tmpmr.x = 0;\n        if ((!mousekey_y_dir) && (!mousekey_y_inertia)) tmpmr.y = 0;\n\n        if (mousekey_frame < 2) mousekey_frame++;\n    }\n\n    // reset if not moving and no movement keys are held\n    if ((!mousekey_x_dir) && (!mousekey_y_dir) && (!mousekey_x_inertia) && (!mousekey_y_inertia)) {\n        mousekey_frame = 0;\n        tmpmr.x        = 0;\n        tmpmr.y        = 0;\n    }\n\n#    else // default acceleration\n\n    if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > (mousekey_repeat ? mk_interval : mk_delay * 10)) {\n        if (mousekey_repeat != UINT8_MAX) mousekey_repeat++;\n        if (tmpmr.x != 0) mouse_report.x = move_unit() * ((tmpmr.x > 0) ? 1 : -1);\n        if (tmpmr.y != 0) mouse_report.y = move_unit() * ((tmpmr.y > 0) ? 1 : -1);\n\n        /* diagonal move [1/sqrt(2)] */\n        if (mouse_report.x && mouse_report.y) {\n            mouse_report.x = times_inv_sqrt2(mouse_report.x);\n            if (mouse_report.x == 0) {\n                mouse_report.x = 1;\n            }\n            mouse_report.y = times_inv_sqrt2(mouse_report.y);\n            if (mouse_report.y == 0) {\n                mouse_report.y = 1;\n            }\n        }\n    }\n\n#    endif // MOUSEKEY_INERTIA or not\n\n    if ((tmpmr.v || tmpmr.h) && timer_elapsed(last_timer_w) > (mousekey_wheel_repeat ? mk_wheel_interval : mk_wheel_delay * 10)) {\n        if (mousekey_wheel_repeat != UINT8_MAX) mousekey_wheel_repeat++;\n        if (tmpmr.v != 0) mouse_report.v = wheel_unit() * ((tmpmr.v > 0) ? 1 : -1);\n        if (tmpmr.h != 0) mouse_report.h = wheel_unit() * ((tmpmr.h > 0) ? 1 : -1);\n\n        /* diagonal move [1/sqrt(2)] */\n        if (mouse_report.v && mouse_report.h) {\n            mouse_report.v = times_inv_sqrt2(mouse_report.v);\n            if (mouse_report.v == 0) {\n                mouse_report.v = 1;\n            }\n            mouse_report.h = times_inv_sqrt2(mouse_report.h);\n            if (mouse_report.h == 0) {\n                mouse_report.h = 1;\n            }\n        }\n    }\n\n    if (has_mouse_report_changed(&mouse_report, &tmpmr) || should_mousekey_report_send(&mouse_report)) {\n        mousekey_send();\n    }\n    // save the state for later\n    memcpy(&mouse_report, &tmpmr, sizeof(tmpmr));\n}\n\nvoid mousekey_on(uint8_t code) {\n#    ifdef MK_KINETIC_SPEED\n    if (mouse_timer == 0) {\n        mouse_timer = timer_read();\n    }\n#    endif\n\n#    if defined(MOUSEKEY_OVERLAP_RESET) && !defined(MOUSEKEY_INERTIA)\n    // If mouse report is not zero, the current mousekey press is overlapping\n    // with another. Restart acceleration for smoother directional transition.\n    if (mouse_report.x || mouse_report.y || mouse_report.h || mouse_report.v) {\n#        ifdef MK_KINETIC_SPEED\n        mouse_timer = timer_read() - MOUSEKEY_OVERLAP_INTERVAL;\n#        else\n        mousekey_repeat       = MOUSEKEY_OVERLAP_MOVE_DELTA;\n        mousekey_wheel_repeat = MOUSEKEY_OVERLAP_WHEEL_DELTA;\n#        endif\n    }\n#    endif // defined(MOUSEKEY_OVERLAP_RESET) && !defined(MOUSEKEY_INERTIA)\n\n#    ifdef MOUSEKEY_INERTIA\n\n    // initial keypress sets impulse and activates first frame of movement\n    if ((code == QK_MOUSE_CURSOR_UP) || (code == QK_MOUSE_CURSOR_DOWN)) {\n        mousekey_y_dir = (code == QK_MOUSE_CURSOR_DOWN) ? 1 : -1;\n        if (mousekey_frame < 2) mouse_report.y = move_unit(1);\n    } else if ((code == QK_MOUSE_CURSOR_LEFT) || (code == QK_MOUSE_CURSOR_RIGHT)) {\n        mousekey_x_dir = (code == QK_MOUSE_CURSOR_RIGHT) ? 1 : -1;\n        if (mousekey_frame < 2) mouse_report.x = move_unit(0);\n    }\n\n#    else // no inertia\n\n    if (code == QK_MOUSE_CURSOR_UP)\n        mouse_report.y = move_unit() * -1;\n    else if (code == QK_MOUSE_CURSOR_DOWN)\n        mouse_report.y = move_unit();\n    else if (code == QK_MOUSE_CURSOR_LEFT)\n        mouse_report.x = move_unit() * -1;\n    else if (code == QK_MOUSE_CURSOR_RIGHT)\n        mouse_report.x = move_unit();\n\n#    endif // inertia or not\n\n    else if (code == QK_MOUSE_WHEEL_UP)\n        mouse_report.v = wheel_unit();\n    else if (code == QK_MOUSE_WHEEL_DOWN)\n        mouse_report.v = wheel_unit() * -1;\n    else if (code == QK_MOUSE_WHEEL_LEFT)\n        mouse_report.h = wheel_unit() * -1;\n    else if (code == QK_MOUSE_WHEEL_RIGHT)\n        mouse_report.h = wheel_unit();\n    else if (IS_MOUSEKEY_BUTTON(code))\n        mouse_report.buttons |= 1 << (code - QK_MOUSE_BUTTON_1);\n    else if (code == QK_MOUSE_ACCELERATION_0)\n        mousekey_accel |= (1 << 0);\n    else if (code == QK_MOUSE_ACCELERATION_1)\n        mousekey_accel |= (1 << 1);\n    else if (code == QK_MOUSE_ACCELERATION_2)\n        mousekey_accel |= (1 << 2);\n}\n\nvoid mousekey_off(uint8_t code) {\n#    ifdef MOUSEKEY_INERTIA\n\n    // key release clears impulse unless opposite direction is held\n    if ((code == QK_MOUSE_CURSOR_UP) && (mousekey_y_dir < 1))\n        mousekey_y_dir = 0;\n    else if ((code == QK_MOUSE_CURSOR_DOWN) && (mousekey_y_dir > -1))\n        mousekey_y_dir = 0;\n    else if ((code == QK_MOUSE_CURSOR_LEFT) && (mousekey_x_dir < 1))\n        mousekey_x_dir = 0;\n    else if ((code == QK_MOUSE_CURSOR_RIGHT) && (mousekey_x_dir > -1))\n        mousekey_x_dir = 0;\n\n#    else // no inertia\n\n    if (code == QK_MOUSE_CURSOR_UP && mouse_report.y < 0)\n        mouse_report.y = 0;\n    else if (code == QK_MOUSE_CURSOR_DOWN && mouse_report.y > 0)\n        mouse_report.y = 0;\n    else if (code == QK_MOUSE_CURSOR_LEFT && mouse_report.x < 0)\n        mouse_report.x = 0;\n    else if (code == QK_MOUSE_CURSOR_RIGHT && mouse_report.x > 0)\n        mouse_report.x = 0;\n\n#    endif // inertia or not\n\n    else if (code == QK_MOUSE_WHEEL_UP && mouse_report.v > 0)\n        mouse_report.v = 0;\n    else if (code == QK_MOUSE_WHEEL_DOWN && mouse_report.v < 0)\n        mouse_report.v = 0;\n    else if (code == QK_MOUSE_WHEEL_LEFT && mouse_report.h < 0)\n        mouse_report.h = 0;\n    else if (code == QK_MOUSE_WHEEL_RIGHT && mouse_report.h > 0)\n        mouse_report.h = 0;\n    else if (IS_MOUSEKEY_BUTTON(code))\n        mouse_report.buttons &= ~(1 << (code - QK_MOUSE_BUTTON_1));\n    else if (code == QK_MOUSE_ACCELERATION_0)\n        mousekey_accel &= ~(1 << 0);\n    else if (code == QK_MOUSE_ACCELERATION_1)\n        mousekey_accel &= ~(1 << 1);\n    else if (code == QK_MOUSE_ACCELERATION_2)\n        mousekey_accel &= ~(1 << 2);\n    if (mouse_report.x == 0 && mouse_report.y == 0) {\n        mousekey_repeat = 0;\n#    ifdef MK_KINETIC_SPEED\n        mouse_timer = 0;\n#    endif /* #ifdef MK_KINETIC_SPEED */\n    }\n    if (mouse_report.v == 0 && mouse_report.h == 0) mousekey_wheel_repeat = 0;\n}\n\n#else /* #ifndef MK_3_SPEED */\n\nenum { mkspd_unmod, mkspd_0, mkspd_1, mkspd_2, mkspd_COUNT };\n#    ifndef MK_MOMENTARY_ACCEL\nstatic uint8_t  mk_speed                 = mkspd_1;\n#    else\nstatic uint8_t mk_speed      = mkspd_unmod;\nstatic uint8_t mkspd_DEFAULT = mkspd_unmod;\n#    endif\nstatic uint16_t last_timer_c             = 0;\nstatic uint16_t last_timer_w             = 0;\nuint16_t        c_offsets[mkspd_COUNT]   = {MK_C_OFFSET_UNMOD, MK_C_OFFSET_0, MK_C_OFFSET_1, MK_C_OFFSET_2};\nuint16_t        c_intervals[mkspd_COUNT] = {MK_C_INTERVAL_UNMOD, MK_C_INTERVAL_0, MK_C_INTERVAL_1, MK_C_INTERVAL_2};\nuint16_t        w_offsets[mkspd_COUNT]   = {MK_W_OFFSET_UNMOD, MK_W_OFFSET_0, MK_W_OFFSET_1, MK_W_OFFSET_2};\nuint16_t        w_intervals[mkspd_COUNT] = {MK_W_INTERVAL_UNMOD, MK_W_INTERVAL_0, MK_W_INTERVAL_1, MK_W_INTERVAL_2};\n\nvoid mousekey_task(void) {\n    // report cursor and scroll movement independently\n    report_mouse_t tmpmr = mouse_report;\n    mouse_report.x       = 0;\n    mouse_report.y       = 0;\n    mouse_report.v       = 0;\n    mouse_report.h       = 0;\n\n    if ((tmpmr.x || tmpmr.y) && timer_elapsed(last_timer_c) > c_intervals[mk_speed]) {\n        mouse_report.x = tmpmr.x;\n        mouse_report.y = tmpmr.y;\n    }\n    if ((tmpmr.h || tmpmr.v) && timer_elapsed(last_timer_w) > w_intervals[mk_speed]) {\n        mouse_report.v = tmpmr.v;\n        mouse_report.h = tmpmr.h;\n    }\n\n    if (has_mouse_report_changed(&mouse_report, &tmpmr) || should_mousekey_report_send(&mouse_report)) {\n        mousekey_send();\n    }\n    memcpy(&mouse_report, &tmpmr, sizeof(tmpmr));\n}\n\nvoid adjust_speed(void) {\n    uint16_t const c_offset = c_offsets[mk_speed];\n    uint16_t const w_offset = w_offsets[mk_speed];\n    if (mouse_report.x > 0) mouse_report.x = c_offset;\n    if (mouse_report.x < 0) mouse_report.x = c_offset * -1;\n    if (mouse_report.y > 0) mouse_report.y = c_offset;\n    if (mouse_report.y < 0) mouse_report.y = c_offset * -1;\n    if (mouse_report.h > 0) mouse_report.h = w_offset;\n    if (mouse_report.h < 0) mouse_report.h = w_offset * -1;\n    if (mouse_report.v > 0) mouse_report.v = w_offset;\n    if (mouse_report.v < 0) mouse_report.v = w_offset * -1;\n    // adjust for diagonals\n    if (mouse_report.x && mouse_report.y) {\n        mouse_report.x = times_inv_sqrt2(mouse_report.x);\n        if (mouse_report.x == 0) {\n            mouse_report.x = 1;\n        }\n        mouse_report.y = times_inv_sqrt2(mouse_report.y);\n        if (mouse_report.y == 0) {\n            mouse_report.y = 1;\n        }\n    }\n    if (mouse_report.h && mouse_report.v) {\n        mouse_report.h = times_inv_sqrt2(mouse_report.h);\n        mouse_report.v = times_inv_sqrt2(mouse_report.v);\n    }\n}\n\nvoid mousekey_on(uint8_t code) {\n    uint16_t const c_offset  = c_offsets[mk_speed];\n    uint16_t const w_offset  = w_offsets[mk_speed];\n    uint8_t const  old_speed = mk_speed;\n    if (code == QK_MOUSE_CURSOR_UP)\n        mouse_report.y = c_offset * -1;\n    else if (code == QK_MOUSE_CURSOR_DOWN)\n        mouse_report.y = c_offset;\n    else if (code == QK_MOUSE_CURSOR_LEFT)\n        mouse_report.x = c_offset * -1;\n    else if (code == QK_MOUSE_CURSOR_RIGHT)\n        mouse_report.x = c_offset;\n    else if (code == QK_MOUSE_WHEEL_UP)\n        mouse_report.v = w_offset;\n    else if (code == QK_MOUSE_WHEEL_DOWN)\n        mouse_report.v = w_offset * -1;\n    else if (code == QK_MOUSE_WHEEL_LEFT)\n        mouse_report.h = w_offset * -1;\n    else if (code == QK_MOUSE_WHEEL_RIGHT)\n        mouse_report.h = w_offset;\n    else if (IS_MOUSEKEY_BUTTON(code))\n        mouse_report.buttons |= 1 << (code - QK_MOUSE_BUTTON_1);\n    else if (code == QK_MOUSE_ACCELERATION_0)\n        mk_speed = mkspd_0;\n    else if (code == QK_MOUSE_ACCELERATION_1)\n        mk_speed = mkspd_1;\n    else if (code == QK_MOUSE_ACCELERATION_2)\n        mk_speed = mkspd_2;\n    if (mk_speed != old_speed) adjust_speed();\n}\n\nvoid mousekey_off(uint8_t code) {\n#    ifdef MK_MOMENTARY_ACCEL\n    uint8_t const old_speed = mk_speed;\n#    endif\n    if (code == QK_MOUSE_CURSOR_UP && mouse_report.y < 0)\n        mouse_report.y = 0;\n    else if (code == QK_MOUSE_CURSOR_DOWN && mouse_report.y > 0)\n        mouse_report.y = 0;\n    else if (code == QK_MOUSE_CURSOR_LEFT && mouse_report.x < 0)\n        mouse_report.x = 0;\n    else if (code == QK_MOUSE_CURSOR_RIGHT && mouse_report.x > 0)\n        mouse_report.x = 0;\n    else if (code == QK_MOUSE_WHEEL_UP && mouse_report.v > 0)\n        mouse_report.v = 0;\n    else if (code == QK_MOUSE_WHEEL_DOWN && mouse_report.v < 0)\n        mouse_report.v = 0;\n    else if (code == QK_MOUSE_WHEEL_LEFT && mouse_report.h < 0)\n        mouse_report.h = 0;\n    else if (code == QK_MOUSE_WHEEL_RIGHT && mouse_report.h > 0)\n        mouse_report.h = 0;\n    else if (IS_MOUSEKEY_BUTTON(code))\n        mouse_report.buttons &= ~(1 << (code - QK_MOUSE_BUTTON_1));\n#    ifdef MK_MOMENTARY_ACCEL\n    else if (code == QK_MOUSE_ACCELERATION_0)\n        mk_speed = mkspd_DEFAULT;\n    else if (code == QK_MOUSE_ACCELERATION_1)\n        mk_speed = mkspd_DEFAULT;\n    else if (code == QK_MOUSE_ACCELERATION_2)\n        mk_speed = mkspd_DEFAULT;\n    if (mk_speed != old_speed) adjust_speed();\n#    endif\n}\n\n#endif /* #ifndef MK_3_SPEED */\n\nvoid mousekey_send(void) {\n    mousekey_debug();\n    uint16_t time = timer_read();\n    if (mouse_report.x || mouse_report.y) last_timer_c = time;\n    if (mouse_report.v || mouse_report.h) last_timer_w = time;\n    host_mouse_send(&mouse_report);\n}\n\nvoid mousekey_clear(void) {\n    mouse_report          = (report_mouse_t){};\n    mousekey_repeat       = 0;\n    mousekey_wheel_repeat = 0;\n    mousekey_accel        = 0;\n#ifdef MOUSEKEY_INERTIA\n    mousekey_frame     = 0;\n    mousekey_x_inertia = 0;\n    mousekey_y_inertia = 0;\n    mousekey_x_dir     = 0;\n    mousekey_y_dir     = 0;\n#endif\n}\n\nstatic void mousekey_debug(void) {\n    if (!debug_mouse) return;\n    print(\"mousekey [btn|x y v h](rep/acl): [\");\n    print_hex8(mouse_report.buttons);\n    print(\"|\");\n    print_decs(mouse_report.x);\n    print(\" \");\n    print_decs(mouse_report.y);\n    print(\" \");\n    print_decs(mouse_report.v);\n    print(\" \");\n    print_decs(mouse_report.h);\n    print(\"](\");\n    print_dec(mousekey_repeat);\n    print(\"/\");\n    print_dec(mousekey_accel);\n    print(\")\\n\");\n}\n\nreport_mouse_t mousekey_get_report(void) {\n    return mouse_report;\n}\n\nbool should_mousekey_report_send(report_mouse_t *mouse_report) {\n    return mouse_report->x || mouse_report->y || mouse_report->v || mouse_report->h;\n}\n"
  },
  {
    "path": "quantum/mousekey.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include \"host.h\"\n\n#ifndef MK_3_SPEED\n\n/* max value on report descriptor */\n#    ifndef MOUSEKEY_MOVE_MAX\n#        define MOUSEKEY_MOVE_MAX 127\n#    elif MOUSEKEY_MOVE_MAX > 127\n#        error MOUSEKEY_MOVE_MAX needs to be smaller than 127\n#    endif\n\n#    ifndef MOUSEKEY_WHEEL_MAX\n#        define MOUSEKEY_WHEEL_MAX 127\n#    elif MOUSEKEY_WHEEL_MAX > 127\n#        error MOUSEKEY_WHEEL_MAX needs to be smaller than 127\n#    endif\n\n#    ifndef MOUSEKEY_MOVE_DELTA\n#        if defined(MK_KINETIC_SPEED)\n#            define MOUSEKEY_MOVE_DELTA 16\n#        elif defined(MOUSEKEY_INERTIA)\n#            define MOUSEKEY_MOVE_DELTA 1\n#        else\n#            define MOUSEKEY_MOVE_DELTA 8\n#        endif\n#    endif\n#    ifndef MOUSEKEY_DELAY\n#        if defined(MK_KINETIC_SPEED)\n#            define MOUSEKEY_DELAY 5\n#        elif defined(MOUSEKEY_INERTIA)\n#            define MOUSEKEY_DELAY 150 // allow single-pixel movements before repeat activates\n#        else\n#            define MOUSEKEY_DELAY 10\n#        endif\n#    endif\n#    ifndef MOUSEKEY_INTERVAL\n#        if defined(MK_KINETIC_SPEED)\n#            define MOUSEKEY_INTERVAL 10\n#        elif defined(MOUSEKEY_INERTIA)\n#            define MOUSEKEY_INTERVAL 16 // 60 fps\n#        else\n#            define MOUSEKEY_INTERVAL 20\n#        endif\n#    endif\n#    ifndef MOUSEKEY_MAX_SPEED\n#        if defined(MOUSEKEY_INERTIA)\n#            define MOUSEKEY_MAX_SPEED 32\n#        else\n#            define MOUSEKEY_MAX_SPEED 10\n#        endif\n#    endif\n#    ifndef MOUSEKEY_TIME_TO_MAX\n#        if defined(MOUSEKEY_INERTIA)\n#            define MOUSEKEY_TIME_TO_MAX 32\n#        else\n#            define MOUSEKEY_TIME_TO_MAX 30\n#        endif\n#    endif\n#    ifndef MOUSEKEY_WHEEL_DELAY\n#        define MOUSEKEY_WHEEL_DELAY 10\n#    endif\n#    ifndef MOUSEKEY_WHEEL_INTERVAL\n#        define MOUSEKEY_WHEEL_INTERVAL 80\n#    endif\n#    ifndef MOUSEKEY_WHEEL_DELTA\n#        define MOUSEKEY_WHEEL_DELTA 1\n#    endif\n#    ifndef MOUSEKEY_WHEEL_MAX_SPEED\n#        define MOUSEKEY_WHEEL_MAX_SPEED 8\n#    endif\n#    ifndef MOUSEKEY_WHEEL_TIME_TO_MAX\n#        define MOUSEKEY_WHEEL_TIME_TO_MAX 40\n#    endif\n\n#    ifndef MOUSEKEY_FRICTION\n#        define MOUSEKEY_FRICTION 24 // 0 to 255\n#    endif\n#    ifndef MOUSEKEY_INITIAL_SPEED\n#        define MOUSEKEY_INITIAL_SPEED 100\n#    endif\n#    ifndef MOUSEKEY_BASE_SPEED\n#        define MOUSEKEY_BASE_SPEED 5000\n#    endif\n#    ifndef MOUSEKEY_DECELERATED_SPEED\n#        define MOUSEKEY_DECELERATED_SPEED 400\n#    endif\n#    ifndef MOUSEKEY_ACCELERATED_SPEED\n#        define MOUSEKEY_ACCELERATED_SPEED 3000\n#    endif\n#    ifndef MOUSEKEY_WHEEL_INITIAL_MOVEMENTS\n#        define MOUSEKEY_WHEEL_INITIAL_MOVEMENTS 16\n#    endif\n#    ifndef MOUSEKEY_WHEEL_BASE_MOVEMENTS\n#        define MOUSEKEY_WHEEL_BASE_MOVEMENTS 32\n#    endif\n#    ifndef MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS\n#        define MOUSEKEY_WHEEL_ACCELERATED_MOVEMENTS 48\n#    endif\n#    ifndef MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS\n#        define MOUSEKEY_WHEEL_DECELERATED_MOVEMENTS 8\n#    endif\n\n#else /* #ifndef MK_3_SPEED */\n\n#    ifndef MK_C_OFFSET_UNMOD\n#        define MK_C_OFFSET_UNMOD 16\n#    endif\n#    ifndef MK_C_INTERVAL_UNMOD\n#        define MK_C_INTERVAL_UNMOD 16\n#    endif\n#    ifndef MK_C_OFFSET_0\n#        define MK_C_OFFSET_0 1\n#    endif\n#    ifndef MK_C_INTERVAL_0\n#        define MK_C_INTERVAL_0 32\n#    endif\n#    ifndef MK_C_OFFSET_1\n#        define MK_C_OFFSET_1 4\n#    endif\n#    ifndef MK_C_INTERVAL_1\n#        define MK_C_INTERVAL_1 16\n#    endif\n#    ifndef MK_C_OFFSET_2\n#        define MK_C_OFFSET_2 32\n#    endif\n#    ifndef MK_C_INTERVAL_2\n#        define MK_C_INTERVAL_2 16\n#    endif\n\n#    ifndef MK_W_OFFSET_UNMOD\n#        define MK_W_OFFSET_UNMOD 1\n#    endif\n#    ifndef MK_W_INTERVAL_UNMOD\n#        define MK_W_INTERVAL_UNMOD 40\n#    endif\n#    ifndef MK_W_OFFSET_0\n#        define MK_W_OFFSET_0 1\n#    endif\n#    ifndef MK_W_INTERVAL_0\n#        define MK_W_INTERVAL_0 360\n#    endif\n#    ifndef MK_W_OFFSET_1\n#        define MK_W_OFFSET_1 1\n#    endif\n#    ifndef MK_W_INTERVAL_1\n#        define MK_W_INTERVAL_1 120\n#    endif\n#    ifndef MK_W_OFFSET_2\n#        define MK_W_OFFSET_2 1\n#    endif\n#    ifndef MK_W_INTERVAL_2\n#        define MK_W_INTERVAL_2 20\n#    endif\n\n#endif /* #ifndef MK_3_SPEED */\n\n#ifndef MOUSEKEY_OVERLAP_MOVE_DELTA\n#    define MOUSEKEY_OVERLAP_MOVE_DELTA MOUSEKEY_MOVE_DELTA\n#endif\n#ifndef MOUSEKEY_OVERLAP_WHEEL_DELTA\n#    define MOUSEKEY_OVERLAP_WHEEL_DELTA MOUSEKEY_WHEEL_DELTA\n#endif\n#ifndef MOUSEKEY_OVERLAP_INTERVAL\n#    define MOUSEKEY_OVERLAP_INTERVAL MOUSEKEY_INTERVAL\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern uint8_t mk_delay;\nextern uint8_t mk_interval;\nextern uint8_t mk_max_speed;\nextern uint8_t mk_time_to_max;\nextern uint8_t mk_wheel_max_speed;\nextern uint8_t mk_wheel_time_to_max;\n\nvoid           mousekey_task(void);\nvoid           mousekey_on(uint8_t code);\nvoid           mousekey_off(uint8_t code);\nvoid           mousekey_clear(void);\nvoid           mousekey_send(void);\nreport_mouse_t mousekey_get_report(void);\nbool           should_mousekey_report_send(report_mouse_t *mouse_report);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/nvm/eeprom/nvm_dynamic_keymap.c",
    "content": "// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"compiler_support.h\"\n#include \"keycodes.h\"\n#include \"eeprom.h\"\n#include \"dynamic_keymap.h\"\n#include \"nvm_dynamic_keymap.h\"\n#include \"nvm_eeprom_eeconfig_internal.h\"\n#include \"nvm_eeprom_via_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n#ifdef ENCODER_ENABLE\n#    include \"encoder.h\"\n#endif\n\n#ifdef VIA_ENABLE\n#    include \"via.h\"\n#    define DYNAMIC_KEYMAP_EEPROM_START (VIA_EEPROM_CONFIG_END)\n#else\n#    define DYNAMIC_KEYMAP_EEPROM_START (EECONFIG_SIZE)\n#endif\n\n#ifndef DYNAMIC_KEYMAP_EEPROM_MAX_ADDR\n#    define DYNAMIC_KEYMAP_EEPROM_MAX_ADDR (TOTAL_EEPROM_BYTE_COUNT - 1)\n#endif\n\nSTATIC_ASSERT(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR <= (TOTAL_EEPROM_BYTE_COUNT - 1), \"DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is configured to use more space than what is available for the selected EEPROM driver\");\n\n// Due to usage of uint16_t check for max 65535\nSTATIC_ASSERT(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR <= 65535, \"DYNAMIC_KEYMAP_EEPROM_MAX_ADDR must be less than 65536\");\n\n// If DYNAMIC_KEYMAP_EEPROM_ADDR not explicitly defined in config.h,\n#ifndef DYNAMIC_KEYMAP_EEPROM_ADDR\n#    define DYNAMIC_KEYMAP_EEPROM_ADDR DYNAMIC_KEYMAP_EEPROM_START\n#endif\n\n// Dynamic encoders starts after dynamic keymaps\n#ifndef DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR\n#    define DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR (DYNAMIC_KEYMAP_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2))\n#endif\n\n// Dynamic macro starts after dynamic encoders, but only when using ENCODER_MAP\n#ifdef ENCODER_MAP_ENABLE\n#    ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR\n#        define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR + (DYNAMIC_KEYMAP_LAYER_COUNT * NUM_ENCODERS * 2 * 2))\n#    endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR\n#else      // ENCODER_MAP_ENABLE\n#    ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR\n#        define DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR (DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR)\n#    endif // DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR\n#endif     // ENCODER_MAP_ENABLE\n\n// Sanity check that dynamic keymaps fit in available EEPROM\n// If there's not 100 bytes available for macros, then something is wrong.\n// The keyboard should override DYNAMIC_KEYMAP_LAYER_COUNT to reduce it,\n// or DYNAMIC_KEYMAP_EEPROM_MAX_ADDR to increase it, *only if* the microcontroller has\n// more than the default.\nSTATIC_ASSERT((DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) - (DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR) >= 100, \"Dynamic keymaps are configured to use more EEPROM than is available.\");\n\n#ifndef TOTAL_EEPROM_BYTE_COUNT\n#    error Unknown total EEPROM size. Cannot derive maximum for dynamic keymaps.\n#endif\n// Dynamic macros are stored after the keymaps and use what is available\n// up to and including DYNAMIC_KEYMAP_EEPROM_MAX_ADDR.\n#ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE\n#    define DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR - DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + 1)\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid nvm_dynamic_keymap_erase(void) {\n    // No-op, nvm_eeconfig_erase() will have already erased EEPROM if necessary.\n}\n\nvoid nvm_dynamic_keymap_macro_erase(void) {\n    // No-op, nvm_eeconfig_erase() will have already erased EEPROM if necessary.\n}\n\nstatic inline void *dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t column) {\n    return ((void *)DYNAMIC_KEYMAP_EEPROM_ADDR) + (layer * MATRIX_ROWS * MATRIX_COLS * 2) + (row * MATRIX_COLS * 2) + (column * 2);\n}\n\nuint16_t nvm_dynamic_keymap_read_keycode(uint8_t layer, uint8_t row, uint8_t column) {\n    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return KC_NO;\n    void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);\n    // Big endian, so we can read/write EEPROM directly from host if we want\n    uint16_t keycode = eeprom_read_byte(address) << 8;\n    keycode |= eeprom_read_byte(address + 1);\n    return keycode;\n}\n\nvoid nvm_dynamic_keymap_update_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode) {\n    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || row >= MATRIX_ROWS || column >= MATRIX_COLS) return;\n    void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);\n    // Big endian, so we can read/write EEPROM directly from host if we want\n    eeprom_update_byte(address, (uint8_t)(keycode >> 8));\n    eeprom_update_byte(address + 1, (uint8_t)(keycode & 0xFF));\n}\n\n#ifdef ENCODER_MAP_ENABLE\nstatic void *dynamic_keymap_encoder_to_eeprom_address(uint8_t layer, uint8_t encoder_id) {\n    return ((void *)DYNAMIC_KEYMAP_ENCODER_EEPROM_ADDR) + (layer * NUM_ENCODERS * 2 * 2) + (encoder_id * 2 * 2);\n}\n\nuint16_t nvm_dynamic_keymap_read_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise) {\n    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return KC_NO;\n    void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);\n    // Big endian, so we can read/write EEPROM directly from host if we want\n    uint16_t keycode = ((uint16_t)eeprom_read_byte(address + (clockwise ? 0 : 2))) << 8;\n    keycode |= eeprom_read_byte(address + (clockwise ? 0 : 2) + 1);\n    return keycode;\n}\n\nvoid nvm_dynamic_keymap_update_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode) {\n    if (layer >= DYNAMIC_KEYMAP_LAYER_COUNT || encoder_id >= NUM_ENCODERS) return;\n    void *address = dynamic_keymap_encoder_to_eeprom_address(layer, encoder_id);\n    // Big endian, so we can read/write EEPROM directly from host if we want\n    eeprom_update_byte(address + (clockwise ? 0 : 2), (uint8_t)(keycode >> 8));\n    eeprom_update_byte(address + (clockwise ? 0 : 2) + 1, (uint8_t)(keycode & 0xFF));\n}\n#endif // ENCODER_MAP_ENABLE\n\nvoid nvm_dynamic_keymap_read_buffer(uint32_t offset, uint32_t size, uint8_t *data) {\n    uint32_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;\n    void *   source                     = (void *)(uintptr_t)(DYNAMIC_KEYMAP_EEPROM_ADDR + offset);\n    uint8_t *target                     = data;\n    for (uint32_t i = 0; i < size; i++) {\n        if (offset + i < dynamic_keymap_eeprom_size) {\n            *target = eeprom_read_byte(source);\n        } else {\n            *target = 0x00;\n        }\n        source++;\n        target++;\n    }\n}\n\nvoid nvm_dynamic_keymap_update_buffer(uint32_t offset, uint32_t size, uint8_t *data) {\n    uint32_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;\n    void *   target                     = (void *)(uintptr_t)(DYNAMIC_KEYMAP_EEPROM_ADDR + offset);\n    uint8_t *source                     = data;\n    for (uint32_t i = 0; i < size; i++) {\n        if (offset + i < dynamic_keymap_eeprom_size) {\n            eeprom_update_byte(target, *source);\n        }\n        source++;\n        target++;\n    }\n}\n\nuint32_t nvm_dynamic_keymap_macro_size(void) {\n    return DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE;\n}\n\nvoid nvm_dynamic_keymap_macro_read_buffer(uint32_t offset, uint32_t size, uint8_t *data) {\n    void *   source = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + offset);\n    uint8_t *target = data;\n    for (uint16_t i = 0; i < size; i++) {\n        if (offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE) {\n            *target = eeprom_read_byte(source);\n        } else {\n            *target = 0x00;\n        }\n        source++;\n        target++;\n    }\n}\n\nvoid nvm_dynamic_keymap_macro_update_buffer(uint32_t offset, uint32_t size, uint8_t *data) {\n    void *   target = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + offset);\n    uint8_t *source = data;\n    for (uint16_t i = 0; i < size; i++) {\n        if (offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE) {\n            eeprom_update_byte(target, *source);\n        }\n        source++;\n        target++;\n    }\n}\n\nvoid nvm_dynamic_keymap_macro_reset(void) {\n    void *  start     = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR);\n    void *  end       = (void *)(uintptr_t)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR + DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE);\n    long    remaining = end - start;\n    uint8_t dummy[16] = {0};\n    for (int i = 0; i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE; i += sizeof(dummy)) {\n        int this_loop = remaining < sizeof(dummy) ? remaining : sizeof(dummy);\n        eeprom_update_block(dummy, start, this_loop);\n        start += this_loop;\n        remaining -= this_loop;\n    }\n}\n"
  },
  {
    "path": "quantum/nvm/eeprom/nvm_eeconfig.c",
    "content": "// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <string.h>\n#include \"nvm_eeconfig.h\"\n#include \"nvm_eeprom_eeconfig_internal.h\"\n#include \"util.h\"\n#include \"eeconfig.h\"\n#include \"debug.h\"\n#include \"eeprom.h\"\n#include \"keycode_config.h\"\n\n#ifdef EEPROM_DRIVER\n#    include \"eeprom_driver.h\"\n#endif\n\n#ifdef AUDIO_ENABLE\n#    include \"audio.h\"\n#endif\n\n#ifdef BACKLIGHT_ENABLE\n#    include \"backlight.h\"\n#endif\n\n#ifdef RGBLIGHT_ENABLE\n#    include \"rgblight.h\"\n#endif\n\n#ifdef RGB_MATRIX_ENABLE\n#    include \"rgb_matrix_types.h\"\n#endif\n\n#ifdef LED_MATRIX_ENABLE\n#    include \"led_matrix_types.h\"\n#endif\n\n#ifdef UNICODE_COMMON_ENABLE\n#    include \"unicode.h\"\n#endif\n\n#ifdef HAPTIC_ENABLE\n#    include \"haptic.h\"\n#endif\n\n#ifdef CONNECTION_ENABLE\n#    include \"connection.h\"\n#endif\n\nvoid nvm_eeconfig_erase(void) {\n#ifdef EEPROM_DRIVER\n    eeprom_driver_format(false);\n#endif // EEPROM_DRIVER\n}\n\nbool nvm_eeconfig_is_enabled(void) {\n    return eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER;\n}\n\nbool nvm_eeconfig_is_disabled(void) {\n    return eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER_OFF;\n}\n\nvoid nvm_eeconfig_enable(void) {\n    eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER);\n}\n\nvoid nvm_eeconfig_disable(void) {\n#if defined(EEPROM_DRIVER)\n    eeprom_driver_format(false);\n#endif\n    eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER_OFF);\n}\n\nvoid nvm_eeconfig_read_debug(debug_config_t *debug_config) {\n    debug_config->raw = eeprom_read_byte(EECONFIG_DEBUG);\n}\nvoid nvm_eeconfig_update_debug(const debug_config_t *debug_config) {\n    eeprom_update_byte(EECONFIG_DEBUG, debug_config->raw);\n}\n\nlayer_state_t nvm_eeconfig_read_default_layer(void) {\n    uint8_t val = eeprom_read_byte(EECONFIG_DEFAULT_LAYER);\n#ifdef DEFAULT_LAYER_STATE_IS_VALUE_NOT_BITMASK\n    // stored as a layer number, so convert back to bitmask\n    return (layer_state_t)1 << val;\n#else\n    // stored as 8-bit-wide bitmask, so read the value directly - handling padding to 16/32 bit layer_state_t\n    return (layer_state_t)val;\n#endif\n}\nvoid nvm_eeconfig_update_default_layer(layer_state_t state) {\n#ifdef DEFAULT_LAYER_STATE_IS_VALUE_NOT_BITMASK\n    // stored as a layer number, so only store the highest layer\n    uint8_t val = get_highest_layer(state);\n#else\n    // stored as 8-bit-wide bitmask, so write the value directly - handling truncation from 16/32 bit layer_state_t\n    uint8_t val = (uint8_t)state;\n#endif\n    eeprom_update_byte(EECONFIG_DEFAULT_LAYER, val);\n}\n\nvoid nvm_eeconfig_read_keymap(keymap_config_t *keymap_config) {\n    keymap_config->raw = eeprom_read_word(EECONFIG_KEYMAP);\n}\nvoid nvm_eeconfig_update_keymap(const keymap_config_t *keymap_config) {\n    eeprom_update_word(EECONFIG_KEYMAP, keymap_config->raw);\n}\n\n#ifdef AUDIO_ENABLE\nvoid nvm_eeconfig_read_audio(audio_config_t *audio_config) {\n    audio_config->raw = eeprom_read_byte(EECONFIG_AUDIO);\n}\nvoid nvm_eeconfig_update_audio(const audio_config_t *audio_config) {\n    eeprom_update_byte(EECONFIG_AUDIO, audio_config->raw);\n}\n#endif // AUDIO_ENABLE\n\n#ifdef UNICODE_COMMON_ENABLE\nvoid nvm_eeconfig_read_unicode_mode(unicode_config_t *unicode_config) {\n    unicode_config->raw = eeprom_read_byte(EECONFIG_UNICODEMODE);\n}\nvoid nvm_eeconfig_update_unicode_mode(const unicode_config_t *unicode_config) {\n    eeprom_update_byte(EECONFIG_UNICODEMODE, unicode_config->raw);\n}\n#endif // UNICODE_COMMON_ENABLE\n\n#ifdef BACKLIGHT_ENABLE\nvoid nvm_eeconfig_read_backlight(backlight_config_t *backlight_config) {\n    backlight_config->raw = eeprom_read_byte(EECONFIG_BACKLIGHT);\n}\nvoid nvm_eeconfig_update_backlight(const backlight_config_t *backlight_config) {\n    eeprom_update_byte(EECONFIG_BACKLIGHT, backlight_config->raw);\n}\n#endif // BACKLIGHT_ENABLE\n\n#ifdef STENO_ENABLE\nuint8_t nvm_eeconfig_read_steno_mode(void) {\n    return eeprom_read_byte(EECONFIG_STENOMODE);\n}\nvoid nvm_eeconfig_update_steno_mode(uint8_t val) {\n    eeprom_update_byte(EECONFIG_STENOMODE, val);\n}\n#endif // STENO_ENABLE\n\n#ifdef RGBLIGHT_ENABLE\n#endif // RGBLIGHT_ENABLE\n\n#ifdef RGB_MATRIX_ENABLE\nvoid nvm_eeconfig_read_rgb_matrix(rgb_config_t *rgb_matrix_config) {\n    eeprom_read_block(rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_config_t));\n}\nvoid nvm_eeconfig_update_rgb_matrix(const rgb_config_t *rgb_matrix_config) {\n    eeprom_update_block(rgb_matrix_config, EECONFIG_RGB_MATRIX, sizeof(rgb_config_t));\n}\n#endif // RGB_MATRIX_ENABLE\n\n#ifdef LED_MATRIX_ENABLE\nvoid nvm_eeconfig_read_led_matrix(led_eeconfig_t *led_matrix_config) {\n    eeprom_read_block(led_matrix_config, EECONFIG_LED_MATRIX, sizeof(led_eeconfig_t));\n}\nvoid nvm_eeconfig_update_led_matrix(const led_eeconfig_t *led_matrix_config) {\n    eeprom_update_block(led_matrix_config, EECONFIG_LED_MATRIX, sizeof(led_eeconfig_t));\n}\n#endif // LED_MATRIX_ENABLE\n\n#ifdef RGBLIGHT_ENABLE\nvoid nvm_eeconfig_read_rgblight(rgblight_config_t *rgblight_config) {\n    rgblight_config->raw = eeprom_read_dword(EECONFIG_RGBLIGHT);\n    rgblight_config->raw |= ((uint64_t)eeprom_read_byte(EECONFIG_RGBLIGHT_EXTENDED) << 32);\n}\nvoid nvm_eeconfig_update_rgblight(const rgblight_config_t *rgblight_config) {\n    eeprom_update_dword(EECONFIG_RGBLIGHT, rgblight_config->raw & 0xFFFFFFFF);\n    eeprom_update_byte(EECONFIG_RGBLIGHT_EXTENDED, (rgblight_config->raw >> 32) & 0xFF);\n}\n#endif // RGBLIGHT_ENABLE\n\n#if (EECONFIG_KB_DATA_SIZE) == 0\nuint32_t nvm_eeconfig_read_kb(void) {\n    return eeprom_read_dword(EECONFIG_KEYBOARD);\n}\nvoid nvm_eeconfig_update_kb(uint32_t val) {\n    eeprom_update_dword(EECONFIG_KEYBOARD, val);\n}\n#endif // (EECONFIG_KB_DATA_SIZE) == 0\n\n#if (EECONFIG_USER_DATA_SIZE) == 0\nuint32_t nvm_eeconfig_read_user(void) {\n    return eeprom_read_dword(EECONFIG_USER);\n}\nvoid nvm_eeconfig_update_user(uint32_t val) {\n    eeprom_update_dword(EECONFIG_USER, val);\n}\n#endif // (EECONFIG_USER_DATA_SIZE) == 0\n\n#ifdef HAPTIC_ENABLE\nvoid nvm_eeconfig_read_haptic(haptic_config_t *haptic_config) {\n    haptic_config->raw = eeprom_read_dword(EECONFIG_HAPTIC);\n}\nvoid nvm_eeconfig_update_haptic(const haptic_config_t *haptic_config) {\n    eeprom_update_dword(EECONFIG_HAPTIC, haptic_config->raw);\n}\n#endif // HAPTIC_ENABLE\n\n#ifdef CONNECTION_ENABLE\nvoid nvm_eeconfig_read_connection(connection_config_t *config) {\n    config->raw = eeprom_read_byte(EECONFIG_CONNECTION);\n}\nvoid nvm_eeconfig_update_connection(const connection_config_t *config) {\n    eeprom_update_byte(EECONFIG_CONNECTION, config->raw);\n}\n#endif // CONNECTION_ENABLE\n\nbool nvm_eeconfig_read_handedness(void) {\n    return !!eeprom_read_byte(EECONFIG_HANDEDNESS);\n}\nvoid nvm_eeconfig_update_handedness(bool val) {\n    eeprom_update_byte(EECONFIG_HANDEDNESS, !!val);\n}\n\n#if (EECONFIG_KB_DATA_SIZE) > 0\n\nbool nvm_eeconfig_is_kb_datablock_valid(void) {\n    return eeprom_read_dword(EECONFIG_KEYBOARD) == (EECONFIG_KB_DATA_VERSION);\n}\n\nuint32_t nvm_eeconfig_read_kb_datablock(void *data, uint32_t offset, uint32_t length) {\n    if (eeconfig_is_kb_datablock_valid()) {\n        void *ee_start = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK + offset);\n        void *ee_end   = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK + MIN(EECONFIG_KB_DATA_SIZE, offset + length));\n        eeprom_read_block(data, ee_start, ee_end - ee_start);\n        return ee_end - ee_start;\n    } else {\n        memset(data, 0, length);\n        return length;\n    }\n}\n\nuint32_t nvm_eeconfig_update_kb_datablock(const void *data, uint32_t offset, uint32_t length) {\n    eeprom_update_dword(EECONFIG_KEYBOARD, (EECONFIG_KB_DATA_VERSION));\n\n    void *ee_start = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK + offset);\n    void *ee_end   = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK + MIN(EECONFIG_KB_DATA_SIZE, offset + length));\n    eeprom_update_block(data, ee_start, ee_end - ee_start);\n    return ee_end - ee_start;\n}\n\nvoid nvm_eeconfig_init_kb_datablock(void) {\n    eeprom_update_dword(EECONFIG_KEYBOARD, (EECONFIG_KB_DATA_VERSION));\n\n    void *  start     = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK);\n    void *  end       = (void *)(uintptr_t)(EECONFIG_KB_DATABLOCK + EECONFIG_KB_DATA_SIZE);\n    long    remaining = end - start;\n    uint8_t dummy[16] = {0};\n    for (int i = 0; i < EECONFIG_KB_DATA_SIZE; i += sizeof(dummy)) {\n        int this_loop = remaining < sizeof(dummy) ? remaining : sizeof(dummy);\n        eeprom_update_block(dummy, start, this_loop);\n        start += this_loop;\n        remaining -= this_loop;\n    }\n}\n\n#endif // (EECONFIG_KB_DATA_SIZE) > 0\n\n#if (EECONFIG_USER_DATA_SIZE) > 0\n\nbool nvm_eeconfig_is_user_datablock_valid(void) {\n    return eeprom_read_dword(EECONFIG_USER) == (EECONFIG_USER_DATA_VERSION);\n}\n\nuint32_t nvm_eeconfig_read_user_datablock(void *data, uint32_t offset, uint32_t length) {\n    if (eeconfig_is_user_datablock_valid()) {\n        void *ee_start = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK + offset);\n        void *ee_end   = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK + MIN(EECONFIG_USER_DATA_SIZE, offset + length));\n        eeprom_read_block(data, ee_start, ee_end - ee_start);\n        return ee_end - ee_start;\n    } else {\n        memset(data, 0, length);\n        return length;\n    }\n}\n\nuint32_t nvm_eeconfig_update_user_datablock(const void *data, uint32_t offset, uint32_t length) {\n    eeprom_update_dword(EECONFIG_USER, (EECONFIG_USER_DATA_VERSION));\n\n    void *ee_start = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK + offset);\n    void *ee_end   = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK + MIN(EECONFIG_USER_DATA_SIZE, offset + length));\n    eeprom_update_block(data, ee_start, ee_end - ee_start);\n    return ee_end - ee_start;\n}\n\nvoid nvm_eeconfig_init_user_datablock(void) {\n    eeprom_update_dword(EECONFIG_USER, (EECONFIG_USER_DATA_VERSION));\n\n    void *  start     = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK);\n    void *  end       = (void *)(uintptr_t)(EECONFIG_USER_DATABLOCK + EECONFIG_USER_DATA_SIZE);\n    long    remaining = end - start;\n    uint8_t dummy[16] = {0};\n    for (int i = 0; i < EECONFIG_USER_DATA_SIZE; i += sizeof(dummy)) {\n        int this_loop = remaining < sizeof(dummy) ? remaining : sizeof(dummy);\n        eeprom_update_block(dummy, start, this_loop);\n        start += this_loop;\n        remaining -= this_loop;\n    }\n}\n\n#endif // (EECONFIG_USER_DATA_SIZE) > 0\n"
  },
  {
    "path": "quantum/nvm/eeprom/nvm_eeprom_eeconfig_internal.h",
    "content": "// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include <stdint.h>\n#include <stddef.h> // offsetof\n\n#include \"compiler_support.h\"\n#include \"eeconfig.h\"\n#include \"util.h\"\n\n// Dummy struct only used to calculate offsets\ntypedef struct PACKED {\n    uint16_t magic;\n    uint8_t  debug;\n    uint8_t  default_layer;\n    uint16_t keymap;\n    uint8_t  backlight;\n    uint8_t  audio;\n    uint32_t rgblight;\n    uint8_t  unicode;\n    uint8_t  steno;\n    uint8_t  handedness;\n    uint32_t keyboard;\n    uint32_t user;\n    union { // Mutually exclusive\n        uint32_t led_matrix;\n        uint64_t rgb_matrix;\n    };\n    uint32_t haptic;\n    uint8_t  rgblight_ext;\n    uint8_t  connection;\n} eeprom_core_t;\n\n/* EEPROM parameter address */\n#define EECONFIG_MAGIC (uint16_t *)(offsetof(eeprom_core_t, magic))\n#define EECONFIG_DEBUG (uint8_t *)(offsetof(eeprom_core_t, debug))\n#define EECONFIG_DEFAULT_LAYER (uint8_t *)(offsetof(eeprom_core_t, default_layer))\n#define EECONFIG_KEYMAP (uint16_t *)(offsetof(eeprom_core_t, keymap))\n#define EECONFIG_BACKLIGHT (uint8_t *)(offsetof(eeprom_core_t, backlight))\n#define EECONFIG_AUDIO (uint8_t *)(offsetof(eeprom_core_t, audio))\n#define EECONFIG_RGBLIGHT (uint32_t *)(offsetof(eeprom_core_t, rgblight))\n#define EECONFIG_UNICODEMODE (uint8_t *)(offsetof(eeprom_core_t, unicode))\n#define EECONFIG_STENOMODE (uint8_t *)(offsetof(eeprom_core_t, steno))\n#define EECONFIG_HANDEDNESS (uint8_t *)(offsetof(eeprom_core_t, handedness))\n#define EECONFIG_KEYBOARD (uint32_t *)(offsetof(eeprom_core_t, keyboard))\n#define EECONFIG_USER (uint32_t *)(offsetof(eeprom_core_t, user))\n#define EECONFIG_LED_MATRIX (uint32_t *)(offsetof(eeprom_core_t, led_matrix))\n#define EECONFIG_RGB_MATRIX (uint64_t *)(offsetof(eeprom_core_t, rgb_matrix))\n#define EECONFIG_HAPTIC (uint32_t *)(offsetof(eeprom_core_t, haptic))\n#define EECONFIG_RGBLIGHT_EXTENDED (uint8_t *)(offsetof(eeprom_core_t, rgblight_ext))\n#define EECONFIG_CONNECTION (uint8_t *)(offsetof(eeprom_core_t, connection))\n\n// Size of EEPROM being used for core data storage\n#define EECONFIG_BASE_SIZE ((uint8_t)sizeof(eeprom_core_t))\n\n#define EECONFIG_KB_DATABLOCK ((uint8_t *)(EECONFIG_BASE_SIZE))\n#define EECONFIG_USER_DATABLOCK ((uint8_t *)((EECONFIG_BASE_SIZE) + (EECONFIG_KB_DATA_SIZE)))\n\n// Size of EEPROM being used, other code can refer to this for available EEPROM\n#define EECONFIG_SIZE ((EECONFIG_BASE_SIZE) + (EECONFIG_KB_DATA_SIZE) + (EECONFIG_USER_DATA_SIZE))\n\nSTATIC_ASSERT((intptr_t)EECONFIG_HANDEDNESS == 14, \"EEPROM handedness offset is incorrect\");\n"
  },
  {
    "path": "quantum/nvm/eeprom/nvm_eeprom_via_internal.h",
    "content": "// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n// Keyboard level code can change where VIA stores the magic.\n// The magic is the build date YYMMDD encoded as BCD in 3 bytes,\n// thus installing firmware built on a different date to the one\n// already installed can be detected and the EEPROM data is reset.\n// The only reason this is important is in case EEPROM usage changes\n// and the EEPROM was not explicitly reset by bootmagic lite.\n#ifndef VIA_EEPROM_MAGIC_ADDR\n#    define VIA_EEPROM_MAGIC_ADDR (EECONFIG_SIZE)\n#endif\n\n#define VIA_EEPROM_LAYOUT_OPTIONS_ADDR (VIA_EEPROM_MAGIC_ADDR + 3)\n\n// The end of the EEPROM memory used by VIA\n// By default, dynamic keymaps will start at this if there is no\n// custom config\n#define VIA_EEPROM_CUSTOM_CONFIG_ADDR (VIA_EEPROM_LAYOUT_OPTIONS_ADDR + VIA_EEPROM_LAYOUT_OPTIONS_SIZE)\n\n#define VIA_EEPROM_CONFIG_END (VIA_EEPROM_CUSTOM_CONFIG_ADDR + VIA_EEPROM_CUSTOM_CONFIG_SIZE)\n"
  },
  {
    "path": "quantum/nvm/eeprom/nvm_via.c",
    "content": "// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"eeprom.h\"\n#include \"util.h\"\n#include \"via.h\"\n#include \"nvm_via.h\"\n#include \"nvm_eeprom_eeconfig_internal.h\"\n#include \"nvm_eeprom_via_internal.h\"\n\nvoid nvm_via_erase(void) {\n    // No-op, nvm_eeconfig_erase() will have already erased EEPROM if necessary.\n}\n\nvoid nvm_via_read_magic(uint8_t *magic0, uint8_t *magic1, uint8_t *magic2) {\n    if (magic0) {\n        *magic0 = eeprom_read_byte((void *)VIA_EEPROM_MAGIC_ADDR + 0);\n    }\n\n    if (magic1) {\n        *magic1 = eeprom_read_byte((void *)VIA_EEPROM_MAGIC_ADDR + 1);\n    }\n\n    if (magic2) {\n        *magic2 = eeprom_read_byte((void *)VIA_EEPROM_MAGIC_ADDR + 2);\n    }\n}\n\nvoid nvm_via_update_magic(uint8_t magic0, uint8_t magic1, uint8_t magic2) {\n    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 0, magic0);\n    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 1, magic1);\n    eeprom_update_byte((void *)VIA_EEPROM_MAGIC_ADDR + 2, magic2);\n}\n\nuint32_t nvm_via_read_layout_options(void) {\n    uint32_t value = 0;\n    // Start at the most significant byte\n    void *source = (void *)(VIA_EEPROM_LAYOUT_OPTIONS_ADDR);\n    for (uint8_t i = 0; i < VIA_EEPROM_LAYOUT_OPTIONS_SIZE; i++) {\n        value = value << 8;\n        value |= eeprom_read_byte(source);\n        source++;\n    }\n    return value;\n}\n\nvoid nvm_via_update_layout_options(uint32_t val) {\n    // Start at the least significant byte\n    void *target = (void *)(VIA_EEPROM_LAYOUT_OPTIONS_ADDR + VIA_EEPROM_LAYOUT_OPTIONS_SIZE - 1);\n    for (uint8_t i = 0; i < VIA_EEPROM_LAYOUT_OPTIONS_SIZE; i++) {\n        eeprom_update_byte(target, val & 0xFF);\n        val = val >> 8;\n        target--;\n    }\n}\n\nuint32_t nvm_via_read_custom_config(void *buf, uint32_t offset, uint32_t length) {\n#if VIA_EEPROM_CUSTOM_CONFIG_SIZE > 0\n    void *ee_start = (void *)(uintptr_t)(VIA_EEPROM_CUSTOM_CONFIG_ADDR + offset);\n    void *ee_end   = (void *)(uintptr_t)(VIA_EEPROM_CUSTOM_CONFIG_ADDR + MIN(VIA_EEPROM_CUSTOM_CONFIG_SIZE, offset + length));\n    eeprom_read_block(buf, ee_start, ee_end - ee_start);\n    return ee_end - ee_start;\n#else\n    return 0;\n#endif\n}\n\nuint32_t nvm_via_update_custom_config(const void *buf, uint32_t offset, uint32_t length) {\n#if VIA_EEPROM_CUSTOM_CONFIG_SIZE > 0\n    void *ee_start = (void *)(uintptr_t)(VIA_EEPROM_CUSTOM_CONFIG_ADDR + offset);\n    void *ee_end   = (void *)(uintptr_t)(VIA_EEPROM_CUSTOM_CONFIG_ADDR + MIN(VIA_EEPROM_CUSTOM_CONFIG_SIZE, offset + length));\n    eeprom_update_block(buf, ee_start, ee_end - ee_start);\n    return ee_end - ee_start;\n#else\n    return 0;\n#endif\n}\n"
  },
  {
    "path": "quantum/nvm/nvm_dynamic_keymap.h",
    "content": "// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\nvoid nvm_dynamic_keymap_erase(void);\nvoid nvm_dynamic_keymap_macro_erase(void);\n\nuint16_t nvm_dynamic_keymap_read_keycode(uint8_t layer, uint8_t row, uint8_t column);\nvoid     nvm_dynamic_keymap_update_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode);\n\n#ifdef ENCODER_MAP_ENABLE\nuint16_t nvm_dynamic_keymap_read_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise);\nvoid     nvm_dynamic_keymap_update_encoder(uint8_t layer, uint8_t encoder_id, bool clockwise, uint16_t keycode);\n#endif // ENCODER_MAP_ENABLE\n\nvoid nvm_dynamic_keymap_read_buffer(uint32_t offset, uint32_t size, uint8_t *data);\nvoid nvm_dynamic_keymap_update_buffer(uint32_t offset, uint32_t size, uint8_t *data);\n\nuint32_t nvm_dynamic_keymap_macro_size(void);\n\nvoid nvm_dynamic_keymap_macro_read_buffer(uint32_t offset, uint32_t size, uint8_t *data);\nvoid nvm_dynamic_keymap_macro_update_buffer(uint32_t offset, uint32_t size, uint8_t *data);\n\nvoid nvm_dynamic_keymap_macro_reset(void);\n"
  },
  {
    "path": "quantum/nvm/nvm_eeconfig.h",
    "content": "// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action_layer.h\" // layer_state_t\n\n#ifndef EECONFIG_MAGIC_NUMBER\n#    define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEE3 // When changing, decrement this value to avoid future re-init issues\n#endif\n#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF\n\nvoid nvm_eeconfig_erase(void);\n\nbool nvm_eeconfig_is_enabled(void);\nbool nvm_eeconfig_is_disabled(void);\n\nvoid nvm_eeconfig_enable(void);\nvoid nvm_eeconfig_disable(void);\n\ntypedef union debug_config_t debug_config_t;\nvoid                         nvm_eeconfig_read_debug(debug_config_t *debug_config);\nvoid                         nvm_eeconfig_update_debug(const debug_config_t *debug_config);\n\nlayer_state_t nvm_eeconfig_read_default_layer(void);\nvoid          nvm_eeconfig_update_default_layer(layer_state_t state);\n\ntypedef union keymap_config_t keymap_config_t;\nvoid                          nvm_eeconfig_read_keymap(keymap_config_t *keymap_config);\nvoid                          nvm_eeconfig_update_keymap(const keymap_config_t *keymap_config);\n\n#ifdef AUDIO_ENABLE\ntypedef union audio_config_t audio_config_t;\nvoid                         nvm_eeconfig_read_audio(audio_config_t *audio_config);\nvoid                         nvm_eeconfig_update_audio(const audio_config_t *audio_config);\n#endif // AUDIO_ENABLE\n\n#ifdef UNICODE_COMMON_ENABLE\ntypedef union unicode_config_t unicode_config_t;\nvoid                           nvm_eeconfig_read_unicode_mode(unicode_config_t *unicode_config);\nvoid                           nvm_eeconfig_update_unicode_mode(const unicode_config_t *unicode_config);\n#endif // UNICODE_COMMON_ENABLE\n\n#ifdef BACKLIGHT_ENABLE\ntypedef union backlight_config_t backlight_config_t;\nvoid                             nvm_eeconfig_read_backlight(backlight_config_t *backlight_config);\nvoid                             nvm_eeconfig_update_backlight(const backlight_config_t *backlight_config);\n#endif // BACKLIGHT_ENABLE\n\n#ifdef STENO_ENABLE\nuint8_t nvm_eeconfig_read_steno_mode(void);\nvoid    nvm_eeconfig_update_steno_mode(uint8_t val);\n#endif // STENO_ENABLE\n\n#ifdef RGB_MATRIX_ENABLE\ntypedef union rgb_config_t rgb_config_t;\nvoid                       nvm_eeconfig_read_rgb_matrix(rgb_config_t *rgb_matrix_config);\nvoid                       nvm_eeconfig_update_rgb_matrix(const rgb_config_t *rgb_matrix_config);\n#endif\n\n#ifdef LED_MATRIX_ENABLE\ntypedef union led_eeconfig_t led_eeconfig_t;\nvoid                         nvm_eeconfig_read_led_matrix(led_eeconfig_t *led_matrix_config);\nvoid                         nvm_eeconfig_update_led_matrix(const led_eeconfig_t *led_matrix_config);\n#endif // LED_MATRIX_ENABLE\n\n#ifdef RGBLIGHT_ENABLE\ntypedef union rgblight_config_t rgblight_config_t;\nvoid                            nvm_eeconfig_read_rgblight(rgblight_config_t *rgblight_config);\nvoid                            nvm_eeconfig_update_rgblight(const rgblight_config_t *rgblight_config);\n#endif // RGBLIGHT_ENABLE\n\n#if (EECONFIG_KB_DATA_SIZE) == 0\nuint32_t nvm_eeconfig_read_kb(void);\nvoid     nvm_eeconfig_update_kb(uint32_t val);\n#endif // (EECONFIG_KB_DATA_SIZE) == 0\n\n#if (EECONFIG_USER_DATA_SIZE) == 0\nuint32_t nvm_eeconfig_read_user(void);\nvoid     nvm_eeconfig_update_user(uint32_t val);\n#endif // (EECONFIG_USER_DATA_SIZE) == 0\n\n#ifdef HAPTIC_ENABLE\ntypedef union haptic_config_t haptic_config_t;\nvoid                          nvm_eeconfig_read_haptic(haptic_config_t *haptic_config);\nvoid                          nvm_eeconfig_update_haptic(const haptic_config_t *haptic_config);\n#endif // HAPTIC_ENABLE\n\n#ifdef CONNECTION_ENABLE\ntypedef union connection_config_t connection_config_t;\nvoid                              nvm_eeconfig_read_connection(connection_config_t *config);\nvoid                              nvm_eeconfig_update_connection(const connection_config_t *config);\n#endif // CONNECTION_ENABLE\n\nbool nvm_eeconfig_read_handedness(void);\nvoid nvm_eeconfig_update_handedness(bool val);\n\n#if (EECONFIG_KB_DATA_SIZE) > 0\nbool     nvm_eeconfig_is_kb_datablock_valid(void);\nuint32_t nvm_eeconfig_read_kb_datablock(void *data, uint32_t offset, uint32_t length);\nuint32_t nvm_eeconfig_update_kb_datablock(const void *data, uint32_t offset, uint32_t length);\nvoid     nvm_eeconfig_init_kb_datablock(void);\n#endif // (EECONFIG_KB_DATA_SIZE) > 0\n\n#if (EECONFIG_USER_DATA_SIZE) > 0\nbool     nvm_eeconfig_is_user_datablock_valid(void);\nuint32_t nvm_eeconfig_read_user_datablock(void *data, uint32_t offset, uint32_t length);\nuint32_t nvm_eeconfig_update_user_datablock(const void *data, uint32_t offset, uint32_t length);\nvoid     nvm_eeconfig_init_user_datablock(void);\n#endif // (EECONFIG_USER_DATA_SIZE) > 0\n"
  },
  {
    "path": "quantum/nvm/nvm_via.h",
    "content": "// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\nvoid nvm_via_erase(void);\n\nvoid nvm_via_read_magic(uint8_t *magic0, uint8_t *magic1, uint8_t *magic2);\nvoid nvm_via_update_magic(uint8_t magic0, uint8_t magic1, uint8_t magic2);\n\nuint32_t nvm_via_read_layout_options(void);\nvoid     nvm_via_update_layout_options(uint32_t val);\n\nuint32_t nvm_via_read_custom_config(void *buf, uint32_t offset, uint32_t length);\nuint32_t nvm_via_update_custom_config(const void *buf, uint32_t offset, uint32_t length);\n"
  },
  {
    "path": "quantum/nvm/readme.md",
    "content": "# Non-volatile Memory - Data Repositories\n\nThis area is intentionally structured in the following way:\n\n```\n╰- quantum\n   ╰- nvm\n      ├- readme.md\n      ├- rules.mk\n      |\n      ├- nvm_eeconfig.h\n      ├- nvm_<<system>>.h\n      |\n      ├- eeprom\n      |  ├- nvm_eeconfig.c\n      |  ├- nvm_<<system>>.c\n      |  ╰- ...\n      |\n      ├- <<another provider>>\n      |  ├- nvm_eeconfig.c\n      |  ├- nvm_<<system>>.c\n      |  ╰- ...\n      ╰- ...\n```\n\nAt the base `nvm` level, for every QMK core system which requires persistence there must be a corresponding `nvm_<<system>>.h` header file. This provides the data repository API to the \"owner\" system, and allows the underlying data persistence mechanism to be abstracted away from upper code. Any conversion to/from a `.raw` field should occur inside the `nvm_<<system>>.c` layer, with the API using values, such as structs or unions exposed to the rest of QMK.\n\nEach `nvm` \"provider\" is a corresponding child directory consisting of its name, such as `eeprom`, and corresponding `nvm_<<system>>.c` implementation files which provide the concrete implementation of the upper `nvm_<<system>>.h`.\n\nNew systems requiring persistence can add the corresponding `nvm_<<system>>.h` file, and in most circumstances must also implement equivalent `nvm_<<system>>.c` files for every `nvm` provider. If persistence is not possible for that system, a `nvm_<<system>>.c` file with simple stubs which ignore writes and provide sane defaults must be used instead."
  },
  {
    "path": "quantum/nvm/rules.mk",
    "content": "# Copyright 2024 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\n\nVPATH += $(QUANTUM_DIR)/nvm\n\nVALID_NVM_DRIVERS := eeprom custom none\n\nNVM_DRIVER ?= eeprom\n\nifeq ($(filter $(NVM_DRIVER),$(VALID_NVM_DRIVERS)),)\n    $(call CATASTROPHIC_ERROR,Invalid NVM_DRIVER,NVM_DRIVER=\"$(NVM_DRIVER)\" is not a valid NVM driver)\nelse\n\n    # If we don't want one, fake it with transient eeprom.\n    ifeq ($(NVM_DRIVER),none)\n        NVM_DRIVER := eeprom\n        EEPROM_DRIVER := transient\n    endif\n\n    NVM_DRIVER_UPPER := $(shell echo $(NVM_DRIVER) | tr '[:lower:]' '[:upper:]')\n    NVM_DRIVER_LOWER := $(shell echo $(NVM_DRIVER) | tr '[:upper:]' '[:lower:]')\n\n    OPT_DEFS += -DNVM_DRIVER_$(NVM_DRIVER_UPPER) -DNVM_DRIVER=\"$(NVM_DRIVER)\"\n\n    ifneq (\"$(wildcard $(QUANTUM_DIR)/nvm/$(NVM_DRIVER_LOWER))\",\"\")\n        COMMON_VPATH += $(QUANTUM_DIR)/nvm/$(NVM_DRIVER_LOWER)\n    endif\n\n    QUANTUM_SRC += nvm_eeconfig.c\n\nendif\n"
  },
  {
    "path": "quantum/os_detection/tests/os_detection.cpp",
    "content": "/* Copyright 2022 Ruslan Sayfutdinov (@KapJI)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\nextern \"C\" {\n#include \"os_detection.h\"\n#include \"timer.h\"\n\nvoid advance_time(uint32_t ms);\n}\n\nstatic uint32_t     reported_count;\nstatic os_variant_t reported_os;\n\nclass OsDetectionTest : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        erase_wlength_data();\n        reported_count = 0;\n        reported_os    = OS_UNSURE;\n    }\n};\n\nos_variant_t check_sequence(const std::vector<uint16_t> &w_lengths) {\n    for (auto &w_length : w_lengths) {\n        process_wlength(w_length);\n    }\n    return detected_host_os();\n}\n\nbool process_detected_host_os_kb(os_variant_t os) {\n    reported_count = reported_count + 1;\n    reported_os    = os;\n\n    return true;\n}\n\nvoid assert_not_reported(void) {\n    // check that it does not report the result, nor any intermediate results\n    EXPECT_EQ(reported_count, 0);\n    EXPECT_EQ(reported_os, OS_UNSURE);\n}\n\nvoid assert_reported(os_variant_t os) {\n    // check that it reports exclusively the result, not any intermediate results\n    EXPECT_EQ(reported_count, 1);\n    EXPECT_EQ(reported_os, os);\n    EXPECT_EQ(reported_os, detected_host_os());\n}\n\n/* Some collected data.\n\nChibiOS:\nWindows 10: [FF, FF, 4, 24, 4, 24, 4, FF, 24, FF, 4, FF, 24, 4, 24, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A, 20A]\nWindows 10 (another host): [FF, FF, 4, 24, 4, 24, 4, 24, 4, 24, 4, 24]\nmacOS 12.5: [2, 24, 2, 28, FF]\nmacOS 15.1.x: [ 2, 4E, 2, 1C, 2, 1A, FF, FF]\nmacOS 15.x (another host): [ 2, 0E, 2, 1E, 2, 42, FF]\nmacOS 15.x (periodic weirdness): [ 2, 42, 2, 1C, 2, 1A, FF, 2, 42, 2, 1C, 2, 1A, FF ]\niOS/iPadOS 15.6: [2, 24, 2, 28]\nLinux (including Android, Raspberry Pi and WebOS TV): [FF, FF, FF]\nLinux (another host): [FF, FF, FF, FF, FF, FF]\nPS5: [2, 4, 2, 28, 2, 24]\nNintendo Switch: [82, FF, 40, 40, FF, 40, 40, FF, 40, 40, FF, 40, 40, FF, 40, 40]\nQuest 2: [FF, FF, FF, FE, FF, FE, FF, FE, FF, FE, FF]\n\nLUFA:\nWindows 10 (first connect): [12, FF, FF, 4, 10, FF, FF, FF, 4, 10, 20A, 20A, 20A, 20A, 20A, 20A]\nWindows 10 (subsequent connect): [FF, FF, 4, 10, FF, 4, FF, 10, FF, 20A, 20A, 20A, 20A, 20A, 20A]\nWindows 10 (another host): [FF, FF, 4, 10, 4, 10]\nmacOS: [2, 10, 2, E, FF]\nmacOS 15.x: [ 2, 64, 2, 28, FF, FF]\niOS/iPadOS: [2, 10, 2, E]\nLinux: [FF, FF, FF]\nPS5: [2, 4, 2, E, 2, 10]\nNintendo Switch: [82, FF, 40, 40, FF, 40, 40]\n\nV-USB:\nWindows 10: [FF, FF, 4, E, FF]\nWindows 10 (another host): [FF, FF, 4, E, 4]\nmacOS: [2, E, 2, E, FF]\niOS/iPadOS: [2, E, 2, E]\nLinux: [FF, FF, FF]\nPS5: [2, 4, 2, E, 2]\nNintendo Switch: [82, FF, 40, 40]\nQuest 2: [FF, FF, FF, FE]\n\nCommon parts:\nWindows: [..., FF, FF, 4, ...]\nmacOS: [2, _, 2, _, FF]\niOS/iPadOS: [2, _, 2, _]\nLinux: [FF, FF, FF]\nPS5: [2, 4, 2, _, 2, ...]\nNintendo Switch: [82, FF, 40, 40, ...]\nQuest 2: [FF, FF, FF, FE, ...]\n*/\nTEST_F(OsDetectionTest, TestLinux) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosLinux) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosMacos) {\n    EXPECT_EQ(check_sequence({0x2, 0x24, 0x2, 0x28, 0xFF}), OS_MACOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosMacos2) {\n    EXPECT_EQ(check_sequence({0x2, 0x42, 0x2, 0x1C, 0x2, 0x1A, 0xFF}), OS_MACOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosMacos3) {\n    EXPECT_EQ(check_sequence({0x2, 0x42, 0x2, 0x1C, 0x2, 0x1A, 0xFF, 0x2, 0x42, 0x2, 0x1C, 0x2, 0x1A, 0xFF}), OS_MACOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\n// Regression reported in https://github.com/qmk/qmk_firmware/pull/21777#issuecomment-1922815841\nTEST_F(OsDetectionTest, TestChibiosMacM1) {\n    EXPECT_EQ(check_sequence({0x02, 0x32, 0x02, 0x24, 0x101, 0xFF}), OS_MACOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosMacSequoia) {\n    EXPECT_EQ(check_sequence({0x02, 0x4E, 0x02, 0x1C, 0x02, 0x1A, 0xFF, 0xFF}), OS_MACOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosMacSequoia2) {\n    EXPECT_EQ(check_sequence({0x02, 0x4E, 0x02, 0x1C, 0x02, 0x1A, 0xFF, 0x02, 0x42, 0x02, 0x1C, 0x02, 0x1A, 0xFF}), OS_MACOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosMacSequoia3) {\n    EXPECT_EQ(check_sequence({0x02, 0x0E, 0x02, 0x1E, 0x02, 0x42, 0xFF}), OS_MACOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestLufaMacos) {\n    EXPECT_EQ(check_sequence({0x2, 0x10, 0x2, 0xE, 0xFF}), OS_MACOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestDetectLufaMacSequoia2) {\n    EXPECT_EQ(check_sequence({0x02, 0x64, 0x02, 0x28, 0xFF, 0xFF}), OS_MACOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestVusbMacos) {\n    EXPECT_EQ(check_sequence({0x2, 0xE, 0x2, 0xE, 0xFF}), OS_MACOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosIos) {\n    EXPECT_EQ(check_sequence({0x2, 0x24, 0x2, 0x28}), OS_IOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestLufaIos) {\n    EXPECT_EQ(check_sequence({0x2, 0x10, 0x2, 0xE}), OS_IOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestVusbIos) {\n    EXPECT_EQ(check_sequence({0x2, 0xE, 0x2, 0xE}), OS_IOS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosWindows10) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x24, 0x4, 0x24, 0x4, 0xFF, 0x24, 0xFF, 0x4, 0xFF, 0x24, 0x4, 0x24, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosWindows10_2) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24, 0x4, 0x24}), OS_WINDOWS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestLufaWindows10) {\n    EXPECT_EQ(check_sequence({0x12, 0xFF, 0xFF, 0x4, 0x10, 0xFF, 0xFF, 0xFF, 0x4, 0x10, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestLufaWindows10_2) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x10, 0xFF, 0x4, 0xFF, 0x10, 0xFF, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestLufaWindows10_3) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0x10, 0x4, 0x10}), OS_WINDOWS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestVusbWindows10) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0xE, 0xFF}), OS_WINDOWS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestVusbWindows10_2) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0x4, 0xE, 0x4}), OS_WINDOWS);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosPs5) {\n    EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0x28, 0x2, 0x24}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestLufaPs5) {\n    EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0xE, 0x2, 0x10}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestVusbPs5) {\n    EXPECT_EQ(check_sequence({0x2, 0x4, 0x2, 0xE, 0x2}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosNintendoSwitch) {\n    EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestLufaNintendoSwitch) {\n    EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40, 0xFF, 0x40, 0x40}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestVusbNintendoSwitch) {\n    EXPECT_EQ(check_sequence({0x82, 0xFF, 0x40, 0x40}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestChibiosQuest2) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF, 0xFE, 0xFF}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestVusbQuest2) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n}\n\nTEST_F(OsDetectionTest, TestDoNotReportIfUsbUnstable) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n\n    advance_time(OS_DETECTION_DEBOUNCE);\n    os_detection_task();\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_LINUX);\n}\n\nstatic struct usb_device_state usb_device_state_configured = {.configure_state = USB_DEVICE_STATE_CONFIGURED};\n\nTEST_F(OsDetectionTest, TestReportAfterDebounce) {\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE}), OS_LINUX);\n    os_detection_notify_usb_device_state_change(usb_device_state_configured);\n    os_detection_task();\n    assert_not_reported();\n\n    advance_time(1);\n    os_detection_task();\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_LINUX);\n\n    advance_time(OS_DETECTION_DEBOUNCE - 3);\n    os_detection_task();\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_LINUX);\n\n    advance_time(1);\n    os_detection_task();\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_LINUX);\n\n    // advancing the timer alone must not cause a report\n    advance_time(1);\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_LINUX);\n    // the task will cause a report\n    os_detection_task();\n    assert_reported(OS_LINUX);\n    EXPECT_EQ(detected_host_os(), OS_LINUX);\n\n    // check that it remains the same after a long time\n    advance_time(OS_DETECTION_DEBOUNCE * 15);\n    assert_reported(OS_LINUX);\n    EXPECT_EQ(detected_host_os(), OS_LINUX);\n}\n\nTEST_F(OsDetectionTest, TestReportAfterDebounceLongWait) {\n    EXPECT_EQ(check_sequence({0x12, 0xFF, 0xFF, 0x4, 0x10, 0xFF, 0xFF, 0xFF, 0x4, 0x10, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);\n    os_detection_notify_usb_device_state_change(usb_device_state_configured);\n    os_detection_task();\n    assert_not_reported();\n\n    advance_time(1);\n    os_detection_task();\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_WINDOWS);\n\n    // advancing the timer alone must not cause a report\n    advance_time(OS_DETECTION_DEBOUNCE * 15);\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_WINDOWS);\n    // the task will cause a report\n    os_detection_task();\n    assert_reported(OS_WINDOWS);\n    EXPECT_EQ(detected_host_os(), OS_WINDOWS);\n\n    // check that it remains the same after a long time\n    advance_time(OS_DETECTION_DEBOUNCE * 10);\n    os_detection_task();\n    assert_reported(OS_WINDOWS);\n    EXPECT_EQ(detected_host_os(), OS_WINDOWS);\n}\n\nTEST_F(OsDetectionTest, TestReportUnsure) {\n    EXPECT_EQ(check_sequence({0x12, 0xFF}), OS_UNSURE);\n    os_detection_notify_usb_device_state_change(usb_device_state_configured);\n    os_detection_task();\n    assert_not_reported();\n\n    advance_time(1);\n    os_detection_task();\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_UNSURE);\n\n    // advancing the timer alone must not cause a report\n    advance_time(OS_DETECTION_DEBOUNCE - 1);\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_UNSURE);\n    // the task will cause a report\n    os_detection_task();\n    assert_reported(OS_UNSURE);\n    EXPECT_EQ(detected_host_os(), OS_UNSURE);\n\n    // check that it remains the same after a long time\n    advance_time(OS_DETECTION_DEBOUNCE * 10);\n    os_detection_task();\n    assert_reported(OS_UNSURE);\n    EXPECT_EQ(detected_host_os(), OS_UNSURE);\n}\n\nTEST_F(OsDetectionTest, TestDoNotReportIntermediateResults) {\n    EXPECT_EQ(check_sequence({0x12, 0xFF}), OS_UNSURE);\n    os_detection_notify_usb_device_state_change(usb_device_state_configured);\n    os_detection_task();\n    assert_not_reported();\n\n    advance_time(OS_DETECTION_DEBOUNCE - 1);\n    os_detection_task();\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_UNSURE);\n\n    // at this stage, the final result has not been reached yet\n    EXPECT_EQ(check_sequence({0xFF}), OS_LINUX);\n    os_detection_notify_usb_device_state_change(usb_device_state_configured);\n    advance_time(OS_DETECTION_DEBOUNCE - 1);\n    os_detection_task();\n    assert_not_reported();\n    // the intermedite but yet unstable result is exposed through detected_host_os()\n    EXPECT_EQ(detected_host_os(), OS_LINUX);\n\n    // the remainder is processed\n    EXPECT_EQ(check_sequence({0x4, 0x10, 0xFF, 0xFF, 0xFF, 0x4, 0x10, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A, 0x20A}), OS_WINDOWS);\n    os_detection_notify_usb_device_state_change(usb_device_state_configured);\n    advance_time(OS_DETECTION_DEBOUNCE - 1);\n    os_detection_task();\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_WINDOWS);\n\n    // advancing the timer alone must not cause a report\n    advance_time(1);\n    assert_not_reported();\n    EXPECT_EQ(detected_host_os(), OS_WINDOWS);\n    // the task will cause a report\n    os_detection_task();\n    assert_reported(OS_WINDOWS);\n    EXPECT_EQ(detected_host_os(), OS_WINDOWS);\n\n    // check that it remains the same after a long time\n    advance_time(OS_DETECTION_DEBOUNCE * 10);\n    os_detection_task();\n    assert_reported(OS_WINDOWS);\n    EXPECT_EQ(detected_host_os(), OS_WINDOWS);\n}\n\nTEST_F(OsDetectionTest, TestDoNotGoBackToUnsure) {\n    // 0x02 would cause it to go back to Unsure, so check that it does not\n    EXPECT_EQ(check_sequence({0xFF, 0xFF, 0xFF, 0xFE, 0x02}), OS_LINUX);\n    os_detection_task();\n    assert_not_reported();\n}\n"
  },
  {
    "path": "quantum/os_detection/tests/rules.mk",
    "content": "os_detection_DEFS := -DOS_DETECTION_ENABLE\nos_detection_DEFS += -DOS_DETECTION_DEBOUNCE=50\n\nos_detection_SRC := \\\n    $(QUANTUM_PATH)/os_detection/tests/os_detection.cpp \\\n    $(QUANTUM_PATH)/os_detection.c \\\n    $(PLATFORM_PATH)/timer.c \\\n    $(PLATFORM_PATH)/$(PLATFORM_KEY)/timer.c\n"
  },
  {
    "path": "quantum/os_detection/tests/testlist.mk",
    "content": "TEST_LIST += os_detection\n"
  },
  {
    "path": "quantum/os_detection.c",
    "content": "/* Copyright 2022 Ruslan Sayfutdinov (@KapJI)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"os_detection.h\"\n\n#include <string.h>\n#include \"timer.h\"\n#ifdef OS_DETECTION_KEYBOARD_RESET\n#    include \"quantum.h\"\n#endif\n\n#ifdef OS_DETECTION_DEBUG_ENABLE\n#    include \"eeconfig.h\"\n#    include \"eeprom.h\"\n#    include \"print.h\"\n\n#    define STORED_USB_SETUPS 50\n#    define EEPROM_USER_OFFSET (uint8_t*)EECONFIG_SIZE\n\nstatic uint16_t usb_setups[STORED_USB_SETUPS];\n#endif\n\n#ifndef OS_DETECTION_DEBOUNCE\n#    define OS_DETECTION_DEBOUNCE 250\n#endif\n\n// 2s should always be more than enough (otherwise, you may have other issues)\n#if OS_DETECTION_DEBOUNCE > 2000\n#    undef OS_DETECTION_DEBOUNCE\n#    define OS_DETECTION_DEBOUNCE 2000\n#endif\n\nstruct setups_data_t {\n    uint8_t  count;\n    uint8_t  cnt_02;\n    uint8_t  cnt_04;\n    uint8_t  cnt_ff;\n    uint16_t last_wlength;\n};\n\nstruct setups_data_t setups_data = {\n    .count  = 0,\n    .cnt_02 = 0,\n    .cnt_04 = 0,\n    .cnt_ff = 0,\n};\n\nstatic volatile os_variant_t detected_os = OS_UNSURE;\nstatic volatile os_variant_t reported_os = OS_UNSURE;\n\n// we need to be able to report OS_UNSURE if that is the stable result of the guesses\nstatic volatile bool first_report = true;\n\n// to react on USB state changes\nstatic volatile struct usb_device_state current_usb_device_state = {.configure_state = USB_DEVICE_STATE_NO_INIT};\nstatic volatile struct usb_device_state maxprev_usb_device_state = {.configure_state = USB_DEVICE_STATE_NO_INIT};\n\n// to reset the keyboard on USB state change\n#ifdef OS_DETECTION_KEYBOARD_RESET\n#    ifndef OS_DETECTION_RESET_DEBOUNCE\n#        define OS_DETECTION_RESET_DEBOUNCE OS_DETECTION_DEBOUNCE\n#    endif\nstatic volatile fast_timer_t configured_since = 0;\nstatic volatile bool         reset_pending    = false;\n#endif\n\n// the OS detection might be unstable for a while, \"debounce\" it\nstatic volatile bool         debouncing = false;\nstatic volatile fast_timer_t last_time  = 0;\n\nbool process_detected_host_os_modules(os_variant_t os);\n\nvoid os_detection_task(void) {\n#ifdef OS_DETECTION_KEYBOARD_RESET\n    // resetting the keyboard on the USB device state change callback results in instability, so delegate that to this task\n    if (reset_pending) {\n        soft_reset_keyboard();\n    }\n    // reset the keyboard if it is stuck in the init state for longer than debounce duration, which can happen with some KVMs\n    if (current_usb_device_state.configure_state <= USB_DEVICE_STATE_INIT && maxprev_usb_device_state.configure_state >= USB_DEVICE_STATE_CONFIGURED) {\n        if (debouncing && timer_elapsed_fast(last_time) >= OS_DETECTION_DEBOUNCE) {\n            soft_reset_keyboard();\n        }\n        return;\n    }\n#endif\n#ifdef OS_DETECTION_SINGLE_REPORT\n    if (!first_report) {\n        return;\n    }\n#endif\n    if (current_usb_device_state.configure_state == USB_DEVICE_STATE_CONFIGURED) {\n        // debouncing goes for both the detected OS as well as the USB state\n        if (debouncing && timer_elapsed_fast(last_time) >= OS_DETECTION_DEBOUNCE) {\n            debouncing = false;\n            last_time  = 0;\n            if (detected_os != reported_os || first_report) {\n                first_report = false;\n                reported_os  = detected_os;\n                process_detected_host_os_modules(detected_os);\n                process_detected_host_os_kb(detected_os);\n            }\n        }\n    }\n}\n\n__attribute__((weak)) bool process_detected_host_os_modules(os_variant_t os) {\n    return true;\n}\n\n__attribute__((weak)) bool process_detected_host_os_kb(os_variant_t detected_os) {\n    return process_detected_host_os_user(detected_os);\n}\n\n__attribute__((weak)) bool process_detected_host_os_user(os_variant_t detected_os) {\n    return true;\n}\n\n// Some collected sequences of wLength can be found in tests.\nvoid process_wlength(const uint16_t w_length) {\n#ifdef OS_DETECTION_DEBUG_ENABLE\n    usb_setups[setups_data.count] = w_length;\n#endif\n    setups_data.count++;\n    setups_data.last_wlength = w_length;\n    if (w_length == 0x2) {\n        setups_data.cnt_02++;\n    } else if (w_length == 0x4) {\n        setups_data.cnt_04++;\n    } else if (w_length == 0xFF) {\n        setups_data.cnt_ff++;\n    }\n\n    // now try to make a guess\n    os_variant_t guessed = OS_UNSURE;\n    if (setups_data.count >= 3) {\n        if (setups_data.cnt_ff >= 2 && setups_data.cnt_04 >= 1) {\n            guessed = OS_WINDOWS;\n        } else if (setups_data.count == setups_data.cnt_ff) {\n            // Linux has 3 packets with 0xFF.\n            guessed = OS_LINUX;\n        } else if (setups_data.count >= 5 && setups_data.last_wlength == 0xFF && setups_data.cnt_ff >= 1 && setups_data.cnt_02 >= 2) {\n            guessed = OS_MACOS;\n        } else if (setups_data.count == 4 && setups_data.cnt_ff == 0 && setups_data.cnt_02 == 2) {\n            // iOS and iPadOS don't have the last 0xFF packet.\n            guessed = OS_IOS;\n        } else if (setups_data.cnt_ff == 0 && setups_data.cnt_02 == 3 && setups_data.cnt_04 == 1) {\n            // This is actually PS5.\n            guessed = OS_LINUX;\n        } else if (setups_data.cnt_ff >= 1 && setups_data.cnt_02 == 0 && setups_data.cnt_04 == 0) {\n            // This is actually Quest 2 or Nintendo Switch.\n            guessed = OS_LINUX;\n        }\n    }\n\n    // only replace the guessed value if not unsure\n    if (guessed != OS_UNSURE) {\n        detected_os = guessed;\n    }\n\n    // whatever the result, debounce\n    last_time  = timer_read_fast();\n    debouncing = true;\n}\n\nos_variant_t detected_host_os(void) {\n    return detected_os;\n}\n\nvoid erase_wlength_data(void) {\n    memset(&setups_data, 0, sizeof(setups_data));\n    detected_os                              = OS_UNSURE;\n    reported_os                              = OS_UNSURE;\n    current_usb_device_state.configure_state = USB_DEVICE_STATE_NO_INIT;\n    maxprev_usb_device_state.configure_state = USB_DEVICE_STATE_NO_INIT;\n    debouncing                               = false;\n    last_time                                = 0;\n    first_report                             = true;\n}\n\nvoid os_detection_notify_usb_device_state_change(struct usb_device_state usb_device_state) {\n    // treat this like any other source of instability\n    if (maxprev_usb_device_state.configure_state < current_usb_device_state.configure_state) {\n        maxprev_usb_device_state.configure_state = current_usb_device_state.configure_state;\n    }\n    current_usb_device_state = usb_device_state;\n    last_time                = timer_read_fast();\n    debouncing               = true;\n\n#ifdef OS_DETECTION_KEYBOARD_RESET\n    if (configured_since == 0 && current_usb_device_state.configure_state == USB_DEVICE_STATE_CONFIGURED) {\n        configured_since = timer_read_fast();\n    } else if (current_usb_device_state.configure_state == USB_DEVICE_STATE_INIT) {\n        // reset the keyboard only if it's been stable for at least debounce duration, to avoid issues with some KVMs\n        if (configured_since > 0 && timer_elapsed_fast(configured_since) >= OS_DETECTION_RESET_DEBOUNCE) {\n            reset_pending = true;\n        }\n        configured_since = 0;\n    }\n#endif\n}\n\n#if defined(SPLIT_KEYBOARD) && defined(SPLIT_DETECTED_OS_ENABLE)\nvoid slave_update_detected_host_os(os_variant_t os) {\n    detected_os = os;\n    last_time   = timer_read_fast();\n    debouncing  = true;\n}\n#endif\n\n#ifdef OS_DETECTION_DEBUG_ENABLE\nvoid print_stored_setups(void) {\n#    ifdef CONSOLE_ENABLE\n    uint8_t cnt = eeprom_read_byte(EEPROM_USER_OFFSET);\n    for (uint16_t i = 0; i < cnt; ++i) {\n        uint16_t* addr = (uint16_t*)EEPROM_USER_OFFSET + i * sizeof(uint16_t) + sizeof(uint8_t);\n        xprintf(\"i: %d, wLength: 0x%02X\\n\", i, eeprom_read_word(addr));\n    }\n#    endif\n}\n\nvoid store_setups_in_eeprom(void) {\n    eeprom_update_byte(EEPROM_USER_OFFSET, setups_data.count);\n    for (uint16_t i = 0; i < setups_data.count; ++i) {\n        uint16_t* addr = (uint16_t*)EEPROM_USER_OFFSET + i * sizeof(uint16_t) + sizeof(uint8_t);\n        eeprom_update_word(addr, usb_setups[i]);\n    }\n}\n\n#endif // OS_DETECTION_DEBUG_ENABLE\n"
  },
  {
    "path": "quantum/os_detection.h",
    "content": "/* Copyright 2022 Ruslan Sayfutdinov (@KapJI)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"usb_device_state.h\"\n\ntypedef enum {\n    OS_UNSURE,\n    OS_LINUX,\n    OS_WINDOWS,\n    OS_MACOS,\n    OS_IOS,\n} os_variant_t;\n\nvoid         process_wlength(const uint16_t w_length);\nos_variant_t detected_host_os(void);\nvoid         erase_wlength_data(void);\nvoid         os_detection_notify_usb_device_state_change(struct usb_device_state usb_device_state);\n\nvoid os_detection_task(void);\n\nbool process_detected_host_os_kb(os_variant_t os);\nbool process_detected_host_os_user(os_variant_t os);\n\n#if defined(SPLIT_KEYBOARD) && defined(SPLIT_DETECTED_OS_ENABLE)\nvoid slave_update_detected_host_os(os_variant_t os);\n#endif\n\n#ifdef OS_DETECTION_DEBUG_ENABLE\n#    if defined(DYNAMIC_KEYMAP_ENABLE) || defined(VIA_ENABLE)\n#        error Cannot enable OS Detection debug mode simultaneously with DYNAMIC_KEYMAP or VIA\n#    endif\nvoid print_stored_setups(void);\nvoid store_setups_in_eeprom(void);\n#endif\n"
  },
  {
    "path": "quantum/painter/lvgl/qp_lvgl.c",
    "content": "// Copyright 2022 Jose Pablo Ramirez (@jpe230)\n// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_lvgl.h\"\n#include \"timer.h\"\n#include \"deferred_exec.h\"\n#include \"lvgl.h\"\n\ntypedef struct lvgl_state_t {\n    uint8_t        fnc_id; // Ideally this should be the pointer of the function to run\n    uint16_t       delay_ms;\n    deferred_token defer_token;\n} lvgl_state_t;\n\nstatic deferred_executor_t lvgl_executors[2] = {0}; // For lv_tick_inc and lv_task_handler\nstatic lvgl_state_t        lvgl_states[2]    = {0}; // For lv_tick_inc and lv_task_handler\n\npainter_device_t selected_display = NULL;\nvoid *           color_buffer     = NULL;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter LVGL Integration Internal: qp_lvgl_flush\n\nvoid qp_lvgl_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {\n    if (selected_display) {\n        uint32_t number_pixels = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1);\n        qp_viewport(selected_display, area->x1, area->y1, area->x2, area->y2);\n        qp_pixdata(selected_display, (void *)color_p, number_pixels);\n        qp_flush(selected_display);\n        lv_disp_flush_ready(disp);\n    }\n}\n\nstatic uint32_t tick_task_callback(uint32_t trigger_time, void *cb_arg) {\n    lvgl_state_t *  state     = (lvgl_state_t *)cb_arg;\n    static uint32_t last_tick = 0;\n    switch (state->fnc_id) {\n        case 0: {\n            uint32_t now = timer_read32();\n            lv_tick_inc(TIMER_DIFF_32(now, last_tick));\n            last_tick = now;\n        } break;\n        case 1:\n            lv_task_handler();\n            break;\n\n        default:\n            break;\n    }\n\n    // The tasks should run indefinitely\n    return state->delay_ms;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter LVGL Integration API: qp_lvgl_attach\n\nbool qp_lvgl_attach(painter_device_t device) {\n    qp_dprintf(\"qp_lvgl_start: entry\\n\");\n    qp_lvgl_detach();\n\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_lvgl_attach: fail (validation_ok == false)\\n\");\n        qp_lvgl_detach();\n        return false;\n    }\n\n    // Setting up the tasks\n    lvgl_state_t *lv_tick_inc_state = &lvgl_states[0];\n    lv_tick_inc_state->fnc_id       = 0;\n    lv_tick_inc_state->delay_ms     = 1;\n    lv_tick_inc_state->defer_token  = defer_exec_advanced(lvgl_executors, 2, 1, tick_task_callback, lv_tick_inc_state);\n\n    if (lv_tick_inc_state->defer_token == INVALID_DEFERRED_TOKEN) {\n        qp_dprintf(\"qp_lvgl_attach: fail (could not set up qp_lvgl executor)\\n\");\n        qp_lvgl_detach();\n        return false;\n    }\n\n    lvgl_state_t *lv_task_handler_state = &lvgl_states[1];\n    lv_task_handler_state->fnc_id       = 1;\n    lv_task_handler_state->delay_ms     = QP_LVGL_TASK_PERIOD;\n    lv_task_handler_state->defer_token  = defer_exec_advanced(lvgl_executors, 2, QP_LVGL_TASK_PERIOD, tick_task_callback, lv_task_handler_state);\n\n    if (lv_task_handler_state->defer_token == INVALID_DEFERRED_TOKEN) {\n        qp_dprintf(\"qp_lvgl_attach: fail (could not set up qp_lvgl executor)\\n\");\n        qp_lvgl_detach();\n        return false;\n    }\n\n    // Init LVGL\n    lv_init();\n\n    // Set up lvgl display buffer\n    static lv_disp_draw_buf_t draw_buf;\n    // Allocate a buffer for 1/10 screen size\n    const size_t count_required   = driver->panel_width * driver->panel_height / 10;\n    void *       new_color_buffer = realloc(color_buffer, sizeof(lv_color_t) * count_required);\n    if (!new_color_buffer) {\n        qp_dprintf(\"qp_lvgl_attach: fail (could not set up memory buffer)\\n\");\n        qp_lvgl_detach();\n        return false;\n    }\n    color_buffer = new_color_buffer;\n    memset(color_buffer, 0, sizeof(lv_color_t) * count_required);\n    // Initialize the display buffer.\n    lv_disp_draw_buf_init(&draw_buf, color_buffer, NULL, count_required);\n\n    selected_display = device;\n\n    uint16_t panel_width, panel_height, offset_x, offset_y;\n    qp_get_geometry(selected_display, &panel_width, &panel_height, NULL, &offset_x, &offset_y);\n\n    // Setting up display driver\n    static lv_disp_drv_t disp_drv;     /*Descriptor of a display driver*/\n    lv_disp_drv_init(&disp_drv);       /*Basic initialization*/\n    disp_drv.flush_cb = qp_lvgl_flush; /*Set your driver function*/\n    disp_drv.draw_buf = &draw_buf;     /*Assign the buffer to the display*/\n    disp_drv.hor_res  = panel_width;   /*Set the horizontal resolution of the display*/\n    disp_drv.ver_res  = panel_height;  /*Set the vertical resolution of the display*/\n    lv_disp_drv_register(&disp_drv);   /*Finally register the driver*/\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter LVGL Integration API: qp_lvgl_detach\n\nvoid qp_lvgl_detach(void) {\n    for (int i = 0; i < 2; ++i) {\n        cancel_deferred_exec_advanced(lvgl_executors, 2, lvgl_states[i].defer_token);\n    }\n    if (color_buffer) {\n        free(color_buffer);\n        color_buffer = NULL;\n    }\n    selected_display = NULL;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter LVGL Integration Internal: qp_lvgl_internal_tick\n\nvoid qp_lvgl_internal_tick(void) {\n    static uint32_t last_lvgl_exec = 0;\n    deferred_exec_advanced_task(lvgl_executors, 2, &last_lvgl_exec);\n}\n"
  },
  {
    "path": "quantum/painter/lvgl/qp_lvgl.h",
    "content": "// Copyright 2022 Jose Pablo Ramirez (@jpe230)\n// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"qp.h\"\n#include \"lvgl.h\"\n\n#ifndef QP_LVGL_TASK_PERIOD\n#    define QP_LVGL_TASK_PERIOD 5\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter - LVGL External API\n\n/**\n * Sets up LVGL with the supplied display.\n *\n * @param device[in] the handle of the device to control\n * @return true if init. of LVGL succeeded\n * @return false if init. of LVGL failed\n */\nbool qp_lvgl_attach(painter_device_t device);\n\n/**\n * Disconnects LVGL from any attached display\n */\nvoid qp_lvgl_detach(void);\n"
  },
  {
    "path": "quantum/painter/lvgl/rules.mk",
    "content": "# LVGL Integration\n\nOPT_DEFS += -DQUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE -DLV_CONF_INCLUDE_SIMPLE\nDEFERRED_EXEC_ENABLE := yes\n\nLVGL_DIR_NAME = lvgl\nLVGL_DIR = $(LIB_DIR)\nLVGL_PATH = $(LVGL_DIR)/$(LVGL_DIR_NAME)\n\nCOMMON_VPATH += $(PLATFORM_PATH) \\\n                $(QUANTUM_DIR)/painter/$(LVGL_DIR_NAME) \\\n                $(LVGL_PATH)\n\ninclude $(LVGL_PATH)/src/extra/extra.mk\ninclude $(LVGL_PATH)/src/core/lv_core.mk\ninclude $(LVGL_PATH)/src/draw/lv_draw.mk\ninclude $(LVGL_PATH)/src/draw/sw/lv_draw_sw.mk\ninclude $(LVGL_PATH)/src/font/lv_font.mk\ninclude $(LVGL_PATH)/src/hal/lv_hal.mk\ninclude $(LVGL_PATH)/src/misc/lv_misc.mk\ninclude $(LVGL_PATH)/src/widgets/lv_widgets.mk\n\nSRC += qp_lvgl.c \\\n       $(CSRCS)\n"
  },
  {
    "path": "quantum/painter/qff.c",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n// Quantum Font File \"QFF\" File Format.\n// See https://docs.qmk.fm/#/quantum_painter_qff for more information.\n\n#include \"qff.h\"\n#include \"qp_draw.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// QFF API\n\nbool qff_read_font_descriptor(qp_stream_t *stream, uint8_t *line_height, bool *has_ascii_table, uint16_t *num_unicode_glyphs, uint8_t *bpp, bool *has_palette, bool *is_panel_native, painter_compression_t *compression_scheme, uint32_t *total_bytes) {\n    // Seek to the start\n    qp_stream_setpos(stream, 0);\n\n    // Read and validate the font descriptor\n    qff_font_descriptor_v1_t font_descriptor;\n    if (qp_stream_read(&font_descriptor, sizeof(qff_font_descriptor_v1_t), 1, stream) != 1) {\n        qp_dprintf(\"Failed to read font_descriptor, expected length was not %d\\n\", (int)sizeof(qff_font_descriptor_v1_t));\n        return false;\n    }\n\n    // Make sure this block is valid\n    if (!qgf_validate_block_header(&font_descriptor.header, QFF_FONT_DESCRIPTOR_TYPEID, (sizeof(qff_font_descriptor_v1_t) - sizeof(qgf_block_header_v1_t)))) {\n        return false;\n    }\n\n    // Make sure the magic and version are correct\n    if (font_descriptor.magic != QFF_MAGIC || font_descriptor.qff_version != 0x01) {\n        qp_dprintf(\"Failed to validate font_descriptor, expected magic 0x%06X was 0x%06X, expected version = 0x%02X was 0x%02X\\n\", (int)QFF_MAGIC, (int)font_descriptor.magic, (int)0x01, (int)font_descriptor.qff_version);\n        return false;\n    }\n\n    // Make sure the file length is valid\n    if (font_descriptor.neg_total_file_size != ~font_descriptor.total_file_size) {\n        qp_dprintf(\"Failed to validate font_descriptor, expected negated length 0x%08X was 0x%08X\\n\", (int)(~font_descriptor.total_file_size), (int)font_descriptor.neg_total_file_size);\n        return false;\n    }\n\n    // Copy out the required info\n    if (line_height) {\n        *line_height = font_descriptor.line_height;\n    }\n    if (has_ascii_table) {\n        *has_ascii_table = font_descriptor.has_ascii_table;\n    }\n    if (num_unicode_glyphs) {\n        *num_unicode_glyphs = font_descriptor.num_unicode_glyphs;\n    }\n    if (bpp || has_palette) {\n        if (!qgf_parse_format(font_descriptor.format, bpp, has_palette, is_panel_native)) {\n            return false;\n        }\n    }\n    if (compression_scheme) {\n        *compression_scheme = font_descriptor.compression_scheme;\n    }\n    if (total_bytes) {\n        *total_bytes = font_descriptor.total_file_size;\n    }\n\n    return true;\n}\n\nstatic bool qff_validate_ascii_descriptor(qp_stream_t *stream) {\n    // Read the raw descriptor\n    qff_ascii_glyph_table_v1_t ascii_descriptor;\n    if (qp_stream_read(&ascii_descriptor, sizeof(qff_ascii_glyph_table_v1_t), 1, stream) != 1) {\n        qp_dprintf(\"Failed to read ascii_descriptor, expected length was not %d\\n\", (int)sizeof(qff_ascii_glyph_table_v1_t));\n        return false;\n    }\n\n    // Make sure this block is valid\n    if (!qgf_validate_block_header(&ascii_descriptor.header, QFF_ASCII_GLYPH_DESCRIPTOR_TYPEID, (sizeof(qff_ascii_glyph_table_v1_t) - sizeof(qgf_block_header_v1_t)))) {\n        return false;\n    }\n\n    return true;\n}\n\nstatic bool qff_validate_unicode_descriptor(qp_stream_t *stream, uint16_t num_unicode_glyphs) {\n    // Read the raw descriptor\n    qff_unicode_glyph_table_v1_t unicode_descriptor;\n    if (qp_stream_read(&unicode_descriptor, sizeof(qff_unicode_glyph_table_v1_t), 1, stream) != 1) {\n        qp_dprintf(\"Failed to read unicode_descriptor, expected length was not %d\\n\", (int)sizeof(qff_unicode_glyph_table_v1_t));\n        return false;\n    }\n\n    // Make sure this block is valid\n    if (!qgf_validate_block_header(&unicode_descriptor.header, QFF_UNICODE_GLYPH_DESCRIPTOR_TYPEID, num_unicode_glyphs * 6)) {\n        return false;\n    }\n\n    // Skip the necessary amount of data to get to the next block\n    qp_stream_seek(stream, num_unicode_glyphs * sizeof(qff_unicode_glyph_v1_t), SEEK_CUR);\n\n    return true;\n}\n\nbool qff_validate_stream(qp_stream_t *stream) {\n    bool     has_ascii_table;\n    uint16_t num_unicode_glyphs;\n\n    if (!qff_read_font_descriptor(stream, NULL, &has_ascii_table, &num_unicode_glyphs, NULL, NULL, NULL, NULL, NULL)) {\n        return false;\n    }\n\n    if (has_ascii_table) {\n        if (!qff_validate_ascii_descriptor(stream)) {\n            return false;\n        }\n    }\n\n    if (num_unicode_glyphs > 0) {\n        if (!qff_validate_unicode_descriptor(stream, num_unicode_glyphs)) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\nuint32_t qff_get_total_size(qp_stream_t *stream) {\n    // Get the original location\n    uint32_t oldpos = qp_stream_tell(stream);\n\n    // Read the font descriptor, grabbing the size\n    uint32_t total_size;\n    if (!qff_read_font_descriptor(stream, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &total_size)) {\n        return false;\n    }\n\n    // Restore the original location\n    qp_stream_setpos(stream, oldpos);\n    return total_size;\n}\n"
  },
  {
    "path": "quantum/painter/qff.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Quantum Font File \"QFF\" File Format.\n// See https://docs.qmk.fm/#/quantum_painter_qff for more information.\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"compiler_support.h\"\n#include \"qp_stream.h\"\n#include \"qp_internal.h\"\n#include \"qgf.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// QFF structures\n\n/////////////////////////////////////////\n// Font descriptor\n\n#define QFF_FONT_DESCRIPTOR_TYPEID 0x00\n\ntypedef struct QP_PACKED qff_font_descriptor_v1_t {\n    qgf_block_header_v1_t header;              // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 20 }\n    uint32_t              magic : 24;          // constant, equal to 0x464651 (\"QFF\")\n    uint8_t               qff_version;         // constant, equal to 0x01\n    uint32_t              total_file_size;     // total size of the entire file, starting at offset zero\n    uint32_t              neg_total_file_size; // negated value of total_file_size, used for detecting parsing errors\n    uint8_t               line_height;         // glyph height in pixels\n    bool                  has_ascii_table;     // whether the font has an ascii table of glyphs (0x20...0x7E)\n    uint16_t              num_unicode_glyphs;  // the number of glyphs in the unicode table -- no table specified if zero\n    qp_image_format_t     format : 8;          // Frame format, see qp.h.\n    uint8_t               flags;               // frame flags, see below.\n    uint8_t               compression_scheme;  // compression scheme, see below.\n    uint8_t               transparency_index;  // palette index used for transparent pixels (not yet implemented)\n} qff_font_descriptor_v1_t;\n\nSTATIC_ASSERT(sizeof(qff_font_descriptor_v1_t) == (sizeof(qgf_block_header_v1_t) + 20), \"qff_font_descriptor_v1_t must be 25 bytes in v1 of QFF\");\n\n#define QFF_MAGIC 0x464651\n\n/////////////////////////////////////////\n// ASCII glyph table descriptor\n\n#define QFF_ASCII_GLYPH_DESCRIPTOR_TYPEID 0x01\n\n#define QFF_GLYPH_WIDTH_BITS 6\n#define QFF_GLYPH_WIDTH_MASK ((1 << QFF_GLYPH_WIDTH_BITS) - 1)\n#define QFF_GLYPH_OFFSET_BITS 18\n#define QFF_GLYPH_OFFSET_MASK (((1 << QFF_GLYPH_OFFSET_BITS) - 1) << QFF_GLYPH_WIDTH_BITS)\n\ntypedef struct QP_PACKED qff_ascii_glyph_v1_t {\n    uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined\n} qff_ascii_glyph_v1_t;\n\nSTATIC_ASSERT(sizeof(qff_ascii_glyph_v1_t) == 3, \"qff_ascii_glyph_v1_t must be 3 bytes in v1 of QFF\");\n\ntypedef struct QP_PACKED qff_ascii_glyph_table_v1_t {\n    qgf_block_header_v1_t header;    // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = 285 }\n    qff_ascii_glyph_v1_t  glyph[95]; // 95 glyphs, 0x20..0x7E\n} qff_ascii_glyph_table_v1_t;\n\nSTATIC_ASSERT(sizeof(qff_ascii_glyph_table_v1_t) == (sizeof(qgf_block_header_v1_t) + (95 * sizeof(qff_ascii_glyph_v1_t))), \"qff_ascii_glyph_table_v1_t must be 290 bytes in v1 of QFF\");\n\n/////////////////////////////////////////\n// Unicode glyph table descriptor\n\n#define QFF_UNICODE_GLYPH_DESCRIPTOR_TYPEID 0x02\n\ntypedef struct QP_PACKED qff_unicode_glyph_v1_t {\n    uint32_t code_point : 24;\n    uint32_t value : 24; // Uses QFF_GLYPH_*_(BITS|MASK) as bitfield ordering is compiler-defined\n} qff_unicode_glyph_v1_t;\n\nSTATIC_ASSERT(sizeof(qff_unicode_glyph_v1_t) == 6, \"qff_unicode_glyph_v1_t must be 6 bytes in v1 of QFF\");\n\ntypedef struct QP_PACKED qff_unicode_glyph_table_v1_t {\n    qgf_block_header_v1_t  header;   // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = (N * 6) }\n    qff_unicode_glyph_v1_t glyph[0]; // Extent of '0' signifies that this struct is immediately followed by the glyph data\n} qff_unicode_glyph_table_v1_t;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// QFF API\n\nbool     qff_validate_stream(qp_stream_t *stream);\nuint32_t qff_get_total_size(qp_stream_t *stream);\nbool     qff_read_font_descriptor(qp_stream_t *stream, uint8_t *line_height, bool *has_ascii_table, uint16_t *num_unicode_glyphs, uint8_t *bpp, bool *has_palette, bool *is_panel_native, painter_compression_t *compression_scheme, uint32_t *total_bytes);\n"
  },
  {
    "path": "quantum/painter/qgf.c",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n// Quantum Graphics File \"QGF\" File Format.\n// See https://docs.qmk.fm/#/quantum_painter_qgf for more information.\n\n#include \"qgf.h\"\n#include \"qp_draw.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// QGF API\n\nbool qgf_validate_block_header(qgf_block_header_v1_t *desc, uint8_t expected_typeid, int32_t expected_length) {\n    if (desc->type_id != expected_typeid || desc->neg_type_id != ((~expected_typeid) & 0xFF)) {\n        qp_dprintf(\"Failed to validate header, expected typeid 0x%02X, was 0x%02X, expected negated typeid 0x%02X, was 0x%02X\\n\", (int)expected_typeid, (int)desc->type_id, (int)((~desc->type_id) & 0xFF), (int)desc->neg_type_id);\n        return false;\n    }\n\n    if (expected_length >= 0 && desc->length != expected_length) {\n        qp_dprintf(\"Failed to validate header (typeid 0x%02X), expected length %d, was %d\\n\", (int)desc->type_id, (int)expected_length, (int)desc->length);\n        return false;\n    }\n\n    return true;\n}\n\nbool qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette, bool *is_panel_native) {\n    // clang-format off\n    static const struct QP_PACKED {\n        uint8_t bpp;\n        bool    has_palette;\n        bool is_panel_native;\n    } formats[] = {\n        [GRAYSCALE_1BPP] = {.bpp = 1, .has_palette = false, .is_panel_native = false},\n        [GRAYSCALE_2BPP] = {.bpp = 2, .has_palette = false, .is_panel_native = false},\n        [GRAYSCALE_4BPP] = {.bpp = 4, .has_palette = false, .is_panel_native = false},\n        [GRAYSCALE_8BPP] = {.bpp = 8, .has_palette = false, .is_panel_native = false},\n        [PALETTE_1BPP] = {.bpp = 1, .has_palette = true, .is_panel_native = false},\n        [PALETTE_2BPP] = {.bpp = 2, .has_palette = true, .is_panel_native = false},\n        [PALETTE_4BPP] = {.bpp = 4, .has_palette = true, .is_panel_native = false},\n        [PALETTE_8BPP] = {.bpp = 8, .has_palette = true, .is_panel_native = false},\n        [RGB565_16BPP] = {.bpp = 16, .has_palette = false, .is_panel_native = true},\n        [RGB888_24BPP] = {.bpp = 24, .has_palette = false, .is_panel_native = true},\n    };\n    // clang-format on\n\n    // Copy out the required info\n    if (format > RGB888_24BPP) {\n        qp_dprintf(\"Failed to parse frame_descriptor, invalid format 0x%02X\\n\", (int)format);\n        return false;\n    }\n\n    // Copy out the required info\n    if (bpp) {\n        *bpp = formats[format].bpp;\n    }\n    if (has_palette) {\n        *has_palette = formats[format].has_palette;\n    }\n    if (is_panel_native) {\n        *is_panel_native = formats[format].is_panel_native;\n    }\n\n    return true;\n}\n\nbool qgf_parse_frame_descriptor(qgf_frame_v1_t *frame_descriptor, uint8_t *bpp, bool *has_palette, bool *is_panel_native, bool *is_delta, painter_compression_t *compression_scheme, uint16_t *delay) {\n    // Decode the format\n    qgf_parse_format(frame_descriptor->format, bpp, has_palette, is_panel_native);\n\n    // Copy out the required info\n    if (is_delta) {\n        *is_delta = (frame_descriptor->flags & QGF_FRAME_FLAG_DELTA) == QGF_FRAME_FLAG_DELTA;\n    }\n    if (compression_scheme) {\n        *compression_scheme = frame_descriptor->compression_scheme;\n    }\n    if (delay) {\n        *delay = frame_descriptor->delay;\n    }\n\n    return true;\n}\n\nbool qgf_read_graphics_descriptor(qp_stream_t *stream, uint16_t *image_width, uint16_t *image_height, uint16_t *frame_count, uint32_t *total_bytes) {\n    // Seek to the start\n    qp_stream_setpos(stream, 0);\n\n    // Read and validate the graphics descriptor\n    qgf_graphics_descriptor_v1_t graphics_descriptor;\n    if (qp_stream_read(&graphics_descriptor, sizeof(qgf_graphics_descriptor_v1_t), 1, stream) != 1) {\n        qp_dprintf(\"Failed to read graphics_descriptor, expected length was not %d\\n\", (int)sizeof(qgf_graphics_descriptor_v1_t));\n        return false;\n    }\n\n    // Make sure this block is valid\n    if (!qgf_validate_block_header(&graphics_descriptor.header, QGF_GRAPHICS_DESCRIPTOR_TYPEID, (sizeof(qgf_graphics_descriptor_v1_t) - sizeof(qgf_block_header_v1_t)))) {\n        return false;\n    }\n\n    // Make sure the magic and version are correct\n    if (graphics_descriptor.magic != QGF_MAGIC || graphics_descriptor.qgf_version != 0x01) {\n        qp_dprintf(\"Failed to validate graphics_descriptor, expected magic 0x%06X was 0x%06X, expected version = 0x%02X was 0x%02X\\n\", (int)QGF_MAGIC, (int)graphics_descriptor.magic, (int)0x01, (int)graphics_descriptor.qgf_version);\n        return false;\n    }\n\n    // Make sure the file length is valid\n    if (graphics_descriptor.neg_total_file_size != ~graphics_descriptor.total_file_size) {\n        qp_dprintf(\"Failed to validate graphics_descriptor, expected negated length 0x%08X was 0x%08X\\n\", (int)(~graphics_descriptor.total_file_size), (int)graphics_descriptor.neg_total_file_size);\n        return false;\n    }\n\n    // Copy out the required info\n    if (image_width) {\n        *image_width = graphics_descriptor.image_width;\n    }\n    if (image_height) {\n        *image_height = graphics_descriptor.image_height;\n    }\n    if (frame_count) {\n        *frame_count = graphics_descriptor.frame_count;\n    }\n    if (total_bytes) {\n        *total_bytes = graphics_descriptor.total_file_size;\n    }\n\n    return true;\n}\n\nstatic bool qgf_read_frame_offset(qp_stream_t *stream, uint16_t frame_number, uint32_t *frame_offset) {\n    uint16_t frame_count;\n    if (!qgf_read_graphics_descriptor(stream, NULL, NULL, &frame_count, NULL)) {\n        return false;\n    }\n\n    // Read the frame offsets descriptor\n    qgf_frame_offsets_v1_t frame_offsets;\n    if (qp_stream_read(&frame_offsets, sizeof(qgf_frame_offsets_v1_t), 1, stream) != 1) {\n        qp_dprintf(\"Failed to read frame_offsets, expected length was not %d\\n\", (int)sizeof(qgf_frame_offsets_v1_t));\n        return false;\n    }\n\n    // Make sure this block is valid\n    if (!qgf_validate_block_header(&frame_offsets.header, QGF_FRAME_OFFSET_DESCRIPTOR_TYPEID, (frame_count * sizeof(uint32_t)))) {\n        return false;\n    }\n\n    if (frame_number >= frame_count) {\n        qp_dprintf(\"Invalid frame number, was %d but only %d frames in image\\n\", (int)frame_number, (int)frame_count);\n        return false;\n    }\n\n    // Skip the necessary amount of data to get to the requested frame offset\n    qp_stream_seek(stream, frame_number * sizeof(uint32_t), SEEK_CUR);\n\n    // Read the frame offset\n    uint32_t offset = 0;\n    if (qp_stream_read(&offset, sizeof(uint32_t), 1, stream) != 1) {\n        qp_dprintf(\"Failed to read frame offset, expected length was not %d\\n\", (int)sizeof(uint32_t));\n        return false;\n    }\n\n    // Copy out the required info\n    if (frame_offset) {\n        *frame_offset = offset;\n    }\n\n    return true;\n}\n\nvoid qgf_seek_to_frame_descriptor(qp_stream_t *stream, uint16_t frame_number) {\n    // Read the offset\n    uint32_t offset = 0;\n    qgf_read_frame_offset(stream, frame_number, &offset);\n\n    // Move to the offset\n    qp_stream_setpos(stream, offset);\n}\n\nbool qgf_validate_frame_descriptor(qp_stream_t *stream, uint16_t frame_number, uint8_t *bpp, bool *has_palette, bool *is_panel_native, bool *is_delta) {\n    // Seek to the correct location\n    qgf_seek_to_frame_descriptor(stream, frame_number);\n\n    // Read the raw descriptor\n    qgf_frame_v1_t frame_descriptor;\n    if (qp_stream_read(&frame_descriptor, sizeof(qgf_frame_v1_t), 1, stream) != 1) {\n        qp_dprintf(\"Failed to read frame_descriptor, expected length was not %d\\n\", (int)sizeof(qgf_frame_v1_t));\n        return false;\n    }\n\n    // Make sure this block is valid\n    if (!qgf_validate_block_header(&frame_descriptor.header, QGF_FRAME_DESCRIPTOR_TYPEID, (sizeof(qgf_frame_v1_t) - sizeof(qgf_block_header_v1_t)))) {\n        return false;\n    }\n\n    return qgf_parse_frame_descriptor(&frame_descriptor, bpp, has_palette, is_panel_native, is_delta, NULL, NULL);\n}\n\nbool qgf_validate_palette_descriptor(qp_stream_t *stream, uint16_t frame_number, uint8_t bpp) {\n    // Read the palette descriptor\n    qgf_palette_v1_t palette_descriptor;\n    if (qp_stream_read(&palette_descriptor, sizeof(qgf_palette_v1_t), 1, stream) != 1) {\n        qp_dprintf(\"Failed to read palette_descriptor, expected length was not %d\\n\", (int)sizeof(qgf_palette_v1_t));\n        return false;\n    }\n\n    // Make sure this block is valid\n    uint32_t expected_length = (1 << bpp) * 3 * sizeof(uint8_t);\n    if (!qgf_validate_block_header(&palette_descriptor.header, QGF_FRAME_PALETTE_DESCRIPTOR_TYPEID, expected_length)) {\n        return false;\n    }\n\n    // Move forward in the stream to the next block\n    qp_stream_seek(stream, expected_length, SEEK_CUR);\n    return true;\n}\n\nbool qgf_validate_delta_descriptor(qp_stream_t *stream, uint16_t frame_number) {\n    // Read the delta descriptor\n    qgf_delta_v1_t delta_descriptor;\n    if (qp_stream_read(&delta_descriptor, sizeof(qgf_delta_v1_t), 1, stream) != 1) {\n        qp_dprintf(\"Failed to read delta_descriptor, expected length was not %d\\n\", (int)sizeof(qgf_delta_v1_t));\n        return false;\n    }\n\n    // Make sure this block is valid\n    if (!qgf_validate_block_header(&delta_descriptor.header, QGF_FRAME_DELTA_DESCRIPTOR_TYPEID, (sizeof(qgf_delta_v1_t) - sizeof(qgf_block_header_v1_t)))) {\n        return false;\n    }\n\n    return true;\n}\n\nbool qgf_validate_frame_data_descriptor(qp_stream_t *stream, uint16_t frame_number) {\n    // Read and validate the data block\n    qgf_data_v1_t data_descriptor;\n    if (qp_stream_read(&data_descriptor, sizeof(qgf_data_v1_t), 1, stream) != 1) {\n        qp_dprintf(\"Failed to read data_descriptor, expected length was not %d\\n\", (int)sizeof(qgf_data_v1_t));\n        return false;\n    }\n\n    if (!qgf_validate_block_header(&data_descriptor.header, QGF_FRAME_DATA_DESCRIPTOR_TYPEID, -1)) {\n        return false;\n    }\n\n    return true;\n}\n\nbool qgf_validate_stream(qp_stream_t *stream) {\n    uint16_t frame_count;\n    if (!qgf_read_graphics_descriptor(stream, NULL, NULL, &frame_count, NULL)) {\n        return false;\n    }\n\n    // Read and validate all the frames (automatically validates the frame offset descriptor in the process)\n    for (uint16_t i = 0; i < frame_count; ++i) {\n        // Validate the frame descriptor block\n        uint8_t bpp             = 0;\n        bool    has_palette     = false;\n        bool    is_panel_native = false;\n        bool    has_delta       = false;\n        if (!qgf_validate_frame_descriptor(stream, i, &bpp, &has_palette, &is_panel_native, &has_delta)) {\n            return false;\n        }\n\n        // If we've got a palette block, check it\n        if (has_palette && !qgf_validate_palette_descriptor(stream, i, bpp)) {\n            return false;\n        }\n\n        // If we've got a delta block, check it\n        if (has_delta && !qgf_validate_delta_descriptor(stream, i)) {\n            return false;\n        }\n\n        // Check the data block\n        if (!qgf_validate_frame_data_descriptor(stream, i)) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\n// Work out the total size of an image definition, assuming we can read far enough into the file\nuint32_t qgf_get_total_size(qp_stream_t *stream) {\n    // Get the original location\n    uint32_t oldpos = qp_stream_tell(stream);\n\n    // Read the graphics descriptor, grabbing the size\n    uint32_t total_size;\n    if (!qgf_read_graphics_descriptor(stream, NULL, NULL, NULL, &total_size)) {\n        return false;\n    }\n\n    // Restore the original location\n    qp_stream_setpos(stream, oldpos);\n    return total_size;\n}\n"
  },
  {
    "path": "quantum/painter/qgf.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// Quantum Graphics File \"QGF\" File Format.\n// See https://docs.qmk.fm/#/quantum_painter_qgf for more information.\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"compiler_support.h\"\n#include \"qp_stream.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// QGF structures\n\n/////////////////////////////////////////\n// Common block header\n\ntypedef struct QP_PACKED qgf_block_header_v1_t {\n    uint8_t  type_id;     // See each respective block type below.\n    uint8_t  neg_type_id; // Negated type ID, used for detecting parsing errors.\n    uint32_t length : 24; // 24-bit blob length, allowing for block sizes of a maximum of 16MB.\n} qgf_block_header_v1_t;\n\nSTATIC_ASSERT(sizeof(qgf_block_header_v1_t) == 5, \"qgf_block_header_v1_t must be 5 bytes in v1 of QGF\");\n\n/////////////////////////////////////////\n// Graphics descriptor\n\n#define QGF_GRAPHICS_DESCRIPTOR_TYPEID 0x00\n\ntypedef struct QP_PACKED qgf_graphics_descriptor_v1_t {\n    qgf_block_header_v1_t header;              // = { .type_id = 0x00, .neg_type_id = (~0x00), .length = 18 }\n    uint32_t              magic : 24;          // constant, equal to 0x464751 (\"QGF\")\n    uint8_t               qgf_version;         // constant, equal to 0x01\n    uint32_t              total_file_size;     // total size of the entire file, starting at offset zero\n    uint32_t              neg_total_file_size; // negated value of total_file_size\n    uint16_t              image_width;         // in pixels\n    uint16_t              image_height;        // in pixels\n    uint16_t              frame_count;         // minimum of 1\n} qgf_graphics_descriptor_v1_t;\n\nSTATIC_ASSERT(sizeof(qgf_graphics_descriptor_v1_t) == (sizeof(qgf_block_header_v1_t) + 18), \"qgf_graphics_descriptor_v1_t must be 23 bytes in v1 of QGF\");\n\n#define QGF_MAGIC 0x464751\n\n/////////////////////////////////////////\n// Frame offset descriptor\n\n#define QGF_FRAME_OFFSET_DESCRIPTOR_TYPEID 0x01\n\ntypedef struct QP_PACKED qgf_frame_offsets_v1_t {\n    qgf_block_header_v1_t header;    // = { .type_id = 0x01, .neg_type_id = (~0x01), .length = (N * sizeof(uint32_t)) }\n    uint32_t              offset[0]; // '0' signifies that this struct is immediately followed by the frame offsets\n} qgf_frame_offsets_v1_t;\n\nSTATIC_ASSERT(sizeof(qgf_frame_offsets_v1_t) == sizeof(qgf_block_header_v1_t), \"qgf_frame_offsets_v1_t must only contain qgf_block_header_v1_t in v1 of QGF\");\n\n/////////////////////////////////////////\n// Frame descriptor\n\n#define QGF_FRAME_DESCRIPTOR_TYPEID 0x02\n\ntypedef struct QP_PACKED qgf_frame_v1_t {\n    qgf_block_header_v1_t header;                 // = { .type_id = 0x02, .neg_type_id = (~0x02), .length = 6 }\n    qp_image_format_t     format : 8;             // Frame format, see qp_internal_formats.h.\n    uint8_t               flags;                  // Frame flags, see below.\n    painter_compression_t compression_scheme : 8; // Compression scheme, see qp.h.\n    uint8_t               transparency_index;     // palette index used for transparent pixels (not yet implemented)\n    uint16_t              delay;                  // frame delay time for animations (in units of milliseconds)\n} qgf_frame_v1_t;\n\nSTATIC_ASSERT(sizeof(qgf_frame_v1_t) == (sizeof(qgf_block_header_v1_t) + 6), \"qgf_frame_v1_t must be 11 bytes in v1 of QGF\");\n\n#define QGF_FRAME_FLAG_DELTA 0x02\n#define QGF_FRAME_FLAG_TRANSPARENT 0x01\n\n/////////////////////////////////////////\n// Frame palette descriptor\n\n#define QGF_FRAME_PALETTE_DESCRIPTOR_TYPEID 0x03\n\ntypedef struct QP_PACKED qgf_palette_entry_v1_t {\n    uint8_t h; // hue component: `[0,360)` degrees is mapped to `[0,255]` uint8_t.\n    uint8_t s; // saturation component: `[0,1]` is mapped to `[0,255]` uint8_t.\n    uint8_t v; // value component: `[0,1]` is mapped to `[0,255]` uint8_t.\n} qgf_palette_entry_v1_t;\n\nSTATIC_ASSERT(sizeof(qgf_palette_entry_v1_t) == 3, \"Palette entry is not 3 bytes in size\");\n\ntypedef struct QP_PACKED qgf_palette_v1_t {\n    qgf_block_header_v1_t  header; // = { .type_id = 0x03, .neg_type_id = (~0x03), .length = (N * 3 * sizeof(uint8_t)) }\n    qgf_palette_entry_v1_t hsv[0]; // N * hsv, where N is the number of palette entries depending on the frame format in the descriptor\n} qgf_palette_v1_t;\n\nSTATIC_ASSERT(sizeof(qgf_palette_v1_t) == sizeof(qgf_block_header_v1_t), \"qgf_palette_v1_t must only contain qgf_block_header_v1_t in v1 of QGF\");\n\n/////////////////////////////////////////\n// Frame delta descriptor\n\n#define QGF_FRAME_DELTA_DESCRIPTOR_TYPEID 0x04\n\ntypedef struct QP_PACKED qgf_delta_v1_t {\n    qgf_block_header_v1_t header; // = { .type_id = 0x04, .neg_type_id = (~0x04), .length = 8 }\n    uint16_t              left;   // The left pixel location to draw the delta image\n    uint16_t              top;    // The top pixel location to draw the delta image\n    uint16_t              right;  // The right pixel location to to draw the delta image\n    uint16_t              bottom; // The bottom pixel location to to draw the delta image\n} qgf_delta_v1_t;\n\nSTATIC_ASSERT(sizeof(qgf_delta_v1_t) == (sizeof(qgf_block_header_v1_t) + 8), \"qgf_delta_v1_t must be 13 bytes in v1 of QGF\");\n\n/////////////////////////////////////////\n// Frame data descriptor\n\n#define QGF_FRAME_DATA_DESCRIPTOR_TYPEID 0x05\n\ntypedef struct QP_PACKED qgf_data_v1_t {\n    qgf_block_header_v1_t header;  // = { .type_id = 0x05, .neg_type_id = (~0x05), .length = N }\n    uint8_t               data[0]; // 0 signifies that this struct is immediately followed by the length of data specified in the header\n} qgf_data_v1_t;\n\nSTATIC_ASSERT(sizeof(qgf_data_v1_t) == sizeof(qgf_block_header_v1_t), \"qgf_data_v1_t must only contain qgf_block_header_v1_t in v1 of QGF\");\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// QGF API\n\nuint32_t qgf_get_total_size(qp_stream_t *stream);\nbool     qgf_validate_stream(qp_stream_t *stream);\nbool     qgf_validate_block_header(qgf_block_header_v1_t *desc, uint8_t expected_typeid, int32_t expected_length);\nbool     qgf_read_graphics_descriptor(qp_stream_t *stream, uint16_t *image_width, uint16_t *image_height, uint16_t *frame_count, uint32_t *total_bytes);\nbool     qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette, bool *is_panel_native);\nvoid     qgf_seek_to_frame_descriptor(qp_stream_t *stream, uint16_t frame_number);\nbool     qgf_parse_frame_descriptor(qgf_frame_v1_t *frame_descriptor, uint8_t *bpp, bool *has_palette, bool *is_panel_native, bool *is_delta, painter_compression_t *compression_scheme, uint16_t *delay);\n"
  },
  {
    "path": "quantum/painter/qp.c",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <quantum.h>\n#include <utf8.h>\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_draw.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Internal driver validation\n\nstatic bool validate_driver_vtable(painter_driver_t *driver) {\n    return (driver && driver->driver_vtable && driver->driver_vtable->init && driver->driver_vtable->power && driver->driver_vtable->clear && driver->driver_vtable->viewport && driver->driver_vtable->pixdata && driver->driver_vtable->palette_convert && driver->driver_vtable->append_pixels && driver->driver_vtable->append_pixdata) ? true : false;\n}\n\nstatic bool validate_comms_vtable(painter_driver_t *driver) {\n    return (driver && driver->comms_vtable && driver->comms_vtable->comms_init && driver->comms_vtable->comms_start && driver->comms_vtable->comms_stop && driver->comms_vtable->comms_send) ? true : false;\n}\n\nstatic bool validate_driver_integrity(painter_driver_t *driver) {\n    return validate_driver_vtable(driver) && validate_comms_vtable(driver);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_init\n\nbool qp_init(painter_device_t device, painter_rotation_t rotation) {\n    qp_dprintf(\"qp_init: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    if (!driver) {\n        qp_dprintf(\"qp_init: fail (pointer to NULL)\\n\");\n        return false;\n    }\n\n    driver->validate_ok = false;\n    if (!validate_driver_integrity(driver)) {\n        qp_dprintf(\"Failed to validate driver integrity in qp_init\\n\");\n        return false;\n    }\n\n    driver->validate_ok = true;\n\n    if (!qp_comms_init(device)) {\n        driver->validate_ok = false;\n        qp_dprintf(\"qp_init: fail (could not init comms)\\n\");\n        return false;\n    }\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"qp_init: fail (could not start comms)\\n\");\n        return false;\n    }\n\n    // Set the rotation before init\n    driver->rotation = rotation;\n\n    // Invoke init\n    bool ret = driver->driver_vtable->init(device, rotation);\n    qp_comms_stop(device);\n    qp_dprintf(\"qp_init: %s\\n\", ret ? \"ok\" : \"fail\");\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_power\n\nbool qp_power(painter_device_t device, bool power_on) {\n    qp_dprintf(\"qp_power: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_power: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"qp_power: fail (could not start comms)\\n\");\n        return false;\n    }\n\n    bool ret = driver->driver_vtable->power(device, power_on);\n    qp_comms_stop(device);\n    qp_dprintf(\"qp_power: %s\\n\", ret ? \"ok\" : \"fail\");\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_clear\n\nbool qp_clear(painter_device_t device) {\n    qp_dprintf(\"qp_clear: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_clear: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"qp_clear: fail (could not start comms)\\n\");\n        return false;\n    }\n\n    bool ret = driver->driver_vtable->clear(device);\n    qp_comms_stop(device);\n    qp_dprintf(\"qp_clear: %s\\n\", ret ? \"ok\" : \"fail\");\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_flush\n\nbool qp_flush(painter_device_t device) {\n    qp_dprintf(\"qp_flush: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_flush: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"qp_flush: fail (could not start comms)\\n\");\n        return false;\n    }\n\n    bool ret = driver->driver_vtable->flush(device);\n    qp_comms_stop(device);\n    qp_dprintf(\"qp_flush: %s\\n\", ret ? \"ok\" : \"fail\");\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_get_*\n\nuint16_t qp_get_width(painter_device_t device) {\n    qp_dprintf(\"qp_get_width: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_get_width: fail (invalid driver)\\n\");\n        return 0;\n    }\n\n    uint16_t width;\n    switch (driver->rotation) {\n        default:\n        case QP_ROTATION_0:\n        case QP_ROTATION_180:\n            width = driver->panel_width;\n            break;\n        case QP_ROTATION_90:\n        case QP_ROTATION_270:\n            width = driver->panel_height;\n            break;\n    }\n\n    qp_dprintf(\"qp_get_width: ok\\n\");\n    return width;\n}\n\nuint16_t qp_get_height(painter_device_t device) {\n    qp_dprintf(\"qp_get_height: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_get_height: fail (invalid driver)\\n\");\n        return 0;\n    }\n\n    uint16_t height;\n    switch (driver->rotation) {\n        default:\n        case QP_ROTATION_0:\n        case QP_ROTATION_180:\n            height = driver->panel_height;\n            break;\n        case QP_ROTATION_90:\n        case QP_ROTATION_270:\n            height = driver->panel_width;\n            break;\n    }\n\n    qp_dprintf(\"qp_get_height: ok\\n\");\n    return height;\n}\n\npainter_rotation_t qp_get_rotation(painter_device_t device) {\n    qp_dprintf(\"qp_get_rotation: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_get_rotation: fail (invalid driver)\\n\");\n        return QP_ROTATION_0;\n    }\n\n    qp_dprintf(\"qp_get_rotation: ok\\n\");\n    return driver->rotation;\n}\n\nuint16_t qp_get_offset_x(painter_device_t device) {\n    qp_dprintf(\"qp_get_offset_x: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_get_offset_x: fail (invalid driver)\\n\");\n        return 0;\n    }\n\n    qp_dprintf(\"qp_get_offset_x: ok\\n\");\n    return driver->offset_x;\n}\n\nuint16_t qp_get_offset_y(painter_device_t device) {\n    qp_dprintf(\"qp_get_offset_y: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_get_offset_y: fail (invalid driver)\\n\");\n        return 0;\n    }\n\n    qp_dprintf(\"qp_get_offset_y: ok\\n\");\n    return driver->offset_y;\n}\n\nvoid qp_get_geometry(painter_device_t device, uint16_t *width, uint16_t *height, painter_rotation_t *rotation, uint16_t *offset_x, uint16_t *offset_y) {\n    qp_dprintf(\"qp_geometry: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_geometry: fail (invalid driver)\\n\");\n        return;\n    }\n\n    if (width) {\n        *width = qp_get_width(device);\n    }\n\n    if (height) {\n        *height = qp_get_height(device);\n    }\n\n    if (rotation) {\n        *rotation = qp_get_rotation(device);\n    }\n\n    if (offset_x) {\n        *offset_x = qp_get_offset_x(device);\n    }\n\n    if (offset_y) {\n        *offset_y = qp_get_offset_y(device);\n    }\n\n    qp_dprintf(\"qp_get_geometry: ok\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_set_viewport_offsets\n\nvoid qp_set_viewport_offsets(painter_device_t device, uint16_t offset_x, uint16_t offset_y) {\n    qp_dprintf(\"qp_set_viewport_offsets: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    if (!driver) {\n        qp_dprintf(\"qp_set_viewport_offsets: fail (pointer to NULL)\\n\");\n        return;\n    }\n\n    driver->offset_x = offset_x;\n    driver->offset_y = offset_y;\n\n    qp_dprintf(\"qp_set_viewport_offsets: ok\\n\");\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_viewport\n\nbool qp_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {\n    qp_dprintf(\"qp_viewport: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_viewport: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"qp_viewport: fail (could not start comms)\\n\");\n        return false;\n    }\n\n    // Set the viewport\n    bool ret = driver->driver_vtable->viewport(device, left, top, right, bottom);\n    qp_dprintf(\"qp_viewport: %s\\n\", ret ? \"ok\" : \"fail\");\n    qp_comms_stop(device);\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_pixdata\n\nbool qp_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count) {\n    qp_dprintf(\"qp_pixdata: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_pixdata: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"qp_pixdata: fail (could not start comms)\\n\");\n        return false;\n    }\n\n    bool ret = driver->driver_vtable->pixdata(device, pixel_data, native_pixel_count);\n    qp_dprintf(\"qp_pixdata: %s\\n\", ret ? \"ok\" : \"fail\");\n    qp_comms_stop(device);\n    return ret;\n}\n"
  },
  {
    "path": "quantum/painter/qp.h",
    "content": "// Copyright 2021-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"deferred_exec.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter global configurables (add to your keyboard's config.h)\n\n#ifndef QUANTUM_PAINTER_DISPLAY_TIMEOUT\n/**\n * @def This controls the amount of time (in milliseconds) that all displays will remain on after the last user input.\n *      If set to 0, the display will remain on indefinitely.\n */\n#    define QUANTUM_PAINTER_DISPLAY_TIMEOUT 30000\n#endif // QUANTUM_PAINTER_DISPLAY_TIMEOUT\n\n#ifndef QUANTUM_PAINTER_TASK_THROTTLE\n/**\n * @def This controls the amount of time (in milliseconds) that the Quantum Painter internal task will wait between\n *      each execution.\n */\n#    define QUANTUM_PAINTER_TASK_THROTTLE 1\n#endif // QUANTUM_PAINTER_TASK_THROTTLE\n\n#ifndef QUANTUM_PAINTER_NUM_IMAGES\n/**\n * @def This controls the maximum number of images that Quantum Painter can load at any one time. Images can be loaded\n *      using \\ref qp_load_image_mem, and can be unloaded by calling \\ref qp_close_image. Increasing this number in\n *      order to load more images increases the amount of RAM required. Image data is not held in RAM, just metadata.\n */\n#    define QUANTUM_PAINTER_NUM_IMAGES 8\n#endif // QUANTUM_PAINTER_NUM_IMAGES\n\n#ifndef QUANTUM_PAINTER_NUM_FONTS\n/**\n * @def This controls the maximum number of fonts that Quantum Painter can load. Fonts can be loaded using\n *      \\ref qp_load_font_mem, and can be unloaded by calling \\ref qp_close_font. Increasing this number in order to\n *      load more fonts increases the amount of RAM required. Font data is not held in RAM, unless\n *      \\ref QUANTUM_PAINTER_LOAD_FONTS_TO_RAM is set to TRUE.\n */\n#    define QUANTUM_PAINTER_NUM_FONTS 4\n#endif // QUANTUM_PAINTER_NUM_FONTS\n\n#ifndef QUANTUM_PAINTER_LOAD_FONTS_TO_RAM\n/**\n * @def This controls whether or not fonts should be cached in RAM. Under normal circumstances, fonts can have quite\n *      random access patterns, and due to timing of flash memory or external storage, it may be a significant speedup\n *      moving the font into RAM before use. Defaults to \"off\", but if it's enabled it will fallback to reading from the\n *      original location if corresponding RAM could not be allocated (such as being too large).\n */\n#    define QUANTUM_PAINTER_LOAD_FONTS_TO_RAM FALSE\n#endif\n\n#ifndef QUANTUM_PAINTER_CONCURRENT_ANIMATIONS\n/**\n * @def This controls the maximum number of animations that Quantum Painter can play simultaneously. Increasing this\n *      number in order to play more animations at the same time increases the amount of RAM required.\n */\n#    define QUANTUM_PAINTER_CONCURRENT_ANIMATIONS 4\n#endif // QUANTUM_PAINTER_CONCURRENT_ANIMATIONS\n\n#ifndef QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE\n/**\n * @def This controls the maximum size of the pixel data buffer used for single blocks of transmission. Larger buffers\n *      means more data is processed at one time, with less frequent transmissions, at the cost of RAM.\n */\n#    define QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE 1024\n#endif\n\n#ifndef QUANTUM_PAINTER_SUPPORTS_256_PALETTE\n/**\n * @def This controls whether 256-color palettes are supported. This has relatively hefty requirements on RAM -- at\n *      least 1kB extra is required just to store the palette information, with more required for other metadata.\n */\n#    define QUANTUM_PAINTER_SUPPORTS_256_PALETTE FALSE\n#endif\n\n#ifndef QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS\n/**\n * @def This controls whether the native color range is supported. This avoids the use of palettes but each image\n *      requires more storage space.\n */\n#    define QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS FALSE\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter types\n\n/**\n * @typedef A handle to a Quantum Painter device, such as an LCD or OLED. Most Quantum Painter APIs require this\n *          argument in order to perform operations on the display.\n */\ntypedef const void *painter_device_t;\n\n/**\n * @typedef The desired rotation of a panel. Used as a parameter to \\ref qp_init, and can be queried by\n *          \\ref qp_get_geometry.\n */\ntypedef enum { QP_ROTATION_0, QP_ROTATION_90, QP_ROTATION_180, QP_ROTATION_270 } painter_rotation_t;\n\n/**\n * @typedef A descriptor for a Quantum Painter image.\n */\ntypedef struct painter_image_desc_t {\n    uint16_t width;       ///< Image width\n    uint16_t height;      ///< Image height\n    uint16_t frame_count; ///< Number of frames in this image\n} painter_image_desc_t;\n\n/**\n * @typedef A handle to a Quantum Painter image.\n */\ntypedef const painter_image_desc_t *painter_image_handle_t;\n\n/**\n * @typedef A descriptor for a Quantum Painter font.\n */\ntypedef struct painter_font_desc_t {\n    uint8_t line_height; ///< The number of pixels in height for each line\n} painter_font_desc_t;\n\n/**\n * @typedef A handle to a Quantum Painter font.\n */\ntypedef const painter_font_desc_t *painter_font_handle_t;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API\n\n/**\n * Initialize a device and set its rotation.\n *\n * @param device[in] the handle of the device to initialize\n * @param rotation[in] the rotation to use\n * @return true if initialization succeeded\n * @return false if initialization failed\n */\nbool qp_init(painter_device_t device, painter_rotation_t rotation);\n\n/**\n * Controls whether a display is on or off.\n *\n * @note If backlighting is used to control brightness (such as for an LCD), it will need to be handled external to\n *       Quantum Painter.\n *\n * @param device[in] the handle of the device to control\n * @param power_on[in] whether or not the device should be on\n * @return true if controlling the power state succeeded\n * @return false if controlling the power state failed\n */\nbool qp_power(painter_device_t device, bool power_on);\n\n/**\n * Clears a device's screen.\n *\n * @param device[in] the handle of the device to control\n * @return true if clearing the screen succeeded\n * @return false if clearing the screen failed\n */\nbool qp_clear(painter_device_t device);\n\n/**\n * Transmits any outstanding data to the screen in order to persist all changes to the display.\n *\n * @note Drivers without internal framebuffers will likely ignore this API.\n *\n * @param device[in] the handle of the device to control\n * @return true if flushing changes to the screen succeeded\n * @return false if flushing changes to the screen failed\n */\nbool qp_flush(painter_device_t device);\n\n/**\n * Retrieves the width of the display.\n *\n * @param device[in] the handle of the device to control\n */\nuint16_t qp_get_width(painter_device_t device);\n\n/**\n * Retrieves the height of the display.\n *\n * @param device[in] the handle of the device to control\n */\nuint16_t qp_get_height(painter_device_t device);\n\n/**\n * Retrieves the rotation of the display.\n *\n * @param device[in] the handle of the device to control\n */\npainter_rotation_t qp_get_rotation(painter_device_t device);\n\n/**\n * Retrieves the x-offset of the display.\n *\n * @param device[in] the handle of the device to control\n */\nuint16_t qp_get_offset_x(painter_device_t device);\n\n/**\n * Retrieves the y-offset of the display.\n *\n * @param device[in] the handle of the device to control\n */\nuint16_t qp_get_offset_y(painter_device_t device);\n\n/**\n * Retrieves the size, rotation, and offsets for the display.\n *\n * @note Any arguments of NULL will be ignored.\n *\n * @param device[in] the handle of the device to control\n * @param width[out] the device's width\n * @param height[out] the device's height\n * @param rotation[out] the device's rotation\n * @param offset_x[out] the device's x-offset applied while drawing\n * @param offset_y[out] the device's y-offset applied while drawing\n */\nvoid qp_get_geometry(painter_device_t device, uint16_t *width, uint16_t *height, painter_rotation_t *rotation, uint16_t *offset_x, uint16_t *offset_y);\n\n/**\n * Allows repositioning of the viewport if the panel geometry offsets are non-zero.\n *\n * @param device[in] the handle of the device to control\n * @param offset_x[in] the device's x-offset applied while drawing\n * @param offset_y[in] the device's y-offset applied while drawing\n */\nvoid qp_set_viewport_offsets(painter_device_t device, uint16_t offset_x, uint16_t offset_y);\n\n/**\n * Sets a pixel to the specified color.\n *\n * @param device[in] the handle of the device to control\n * @param x[in] the x-position to draw onto the device\n * @param y[in] the y-position to draw onto the device\n * @param hue[in] the hue to use, with 0-360 mapped to 0-255\n * @param sat[in] the saturation to use, with 0-100% mapped to 0-255\n * @param val[in] the value to use, with 0-100% mapped to 0-255\n * @return true if setting the pixel succeeded\n * @return false if setting the pixel failed\n */\nbool qp_setpixel(painter_device_t device, uint16_t x, uint16_t y, uint8_t hue, uint8_t sat, uint8_t val);\n\n/**\n * Draws a line using the specified color.\n *\n * @param device[in] the handle of the device to control\n * @param x0[in] the device's x-position to start\n * @param y0[in] the device's y-position to start\n * @param x1[in] the device's x-position to finish\n * @param y1[in] the device's y-position to finish\n * @param hue[in] the hue to use, with 0-360 mapped to 0-255\n * @param sat[in] the saturation to use, with 0-100% mapped to 0-255\n * @param val[in] the value to use, with 0-100% mapped to 0-255\n * @return true if drawing the line succeeded\n * @return false if drawing the line failed\n */\nbool qp_line(painter_device_t device, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t hue, uint8_t sat, uint8_t val);\n\n/**\n * Draws a rectangle using the specified color, optionally filled.\n *\n * @param device[in] the handle of the device to control\n * @param left[in] the device's x-position to start\n * @param top[in] the device's y-position to start\n * @param right[in] the device's x-position to finish\n * @param bottom[in] the device's y-position to finish\n * @param hue[in] the hue to use, with 0-360 mapped to 0-255\n * @param sat[in] the saturation to use, with 0-100% mapped to 0-255\n * @param val[in] the value to use, with 0-100% mapped to 0-255\n * @param filled[in] whether the rectangle should be filled\n * @return true if drawing the rectangle succeeded\n * @return false if drawing the rectangle failed\n */\nbool qp_rect(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom, uint8_t hue, uint8_t sat, uint8_t val, bool filled);\n\n/**\n * Draws a circle using the specified color, optionally filled.\n *\n * @param device[in] the handle of the device to control\n * @param x[in] the x-position of the centre of the circle to draw onto the device\n * @param y[in] the y-position of the centre of the circle to draw onto the device\n * @param radius[in] the radius of the circle to draw\n * @param hue[in] the hue to use, with 0-360 mapped to 0-255\n * @param sat[in] the saturation to use, with 0-100% mapped to 0-255\n * @param val[in] the value to use, with 0-100% mapped to 0-255\n * @param filled[in] whether the circle should be filled\n * @return true if drawing the circle succeeded\n * @return false if drawing the circle failed\n */\nbool qp_circle(painter_device_t device, uint16_t x, uint16_t y, uint16_t radius, uint8_t hue, uint8_t sat, uint8_t val, bool filled);\n\n/**\n * Draws a ellipse using the specified color, optionally filled.\n *\n * @param device[in] the handle of the device to control\n * @param x[in] the x-position of the centre of the ellipse to draw onto the device\n * @param y[in] the y-position of the centre of the ellipse to draw onto the device\n * @param sizex[in] the horizontal size of the ellipse\n * @param sizey[in] the vertical size of the ellipse\n * @param hue[in] the hue to use, with 0-360 mapped to 0-255\n * @param sat[in] the saturation to use, with 0-100% mapped to 0-255\n * @param val[in] the value to use, with 0-100% mapped to 0-255\n * @param filled[in] whether the ellipse should be filled\n * @return true if drawing the ellipse succeeded\n * @return false if drawing the ellipse failed\n */\nbool qp_ellipse(painter_device_t device, uint16_t x, uint16_t y, uint16_t sizex, uint16_t sizey, uint8_t hue, uint8_t sat, uint8_t val, bool filled);\n\n/**\n * Sets up the location on the display to stream raw pixel data to the display, using \\ref qp_pixdata.\n *\n * @note This is for advanced uses only, and should not be required for normal Quantum Painter functionality.\n *\n * @param device[in] the handle of the device to control\n * @param left[in] the device's x-position to start\n * @param top[in] the device's y-position to start\n * @param right[in] the device's x-position to finish\n * @param bottom[in] the device's y-position to finish\n * @return true if setting the viewport succeeded\n * @return false if setting the viewport failed\n */\nbool qp_viewport(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom);\n\n/**\n * Streams raw pixel data (in the native panel format) to the area previously set by \\ref qp_viewport.\n *\n * @note This is for advanced uses only, and should not be required for normal Quantum Painter functionality.\n *\n * @param device[in] the handle of the device to control\n * @param pixel_data[in] pointer to buffer data\n * @param native_pixel_count[in] the number of pixels to transmit\n * @return true if streaming of data succeeded\n * @return false if streaming of data failed\n */\nbool qp_pixdata(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count);\n\n/**\n * Loads an image into memory.\n *\n * @note Images can be unloaded by calling \\ref qp_close_image.\n *\n * @param buffer[in] the image data to load\n * @return an image handle usable with \\ref qp_drawimage, \\ref qp_drawimage_recolor, \\ref qp_animate, and\n *         \\ref qp_animate_recolor.\n * @return NULL if loading the image failed\n */\npainter_image_handle_t qp_load_image_mem(const void *buffer);\n\n/**\n * Closes an image handle when no longer in use.\n *\n * @param image[in] the handle of the image to unload\n * @return true if unloading the image succeeded\n * @return false if unloading the image failed\n */\nbool qp_close_image(painter_image_handle_t image);\n\n/**\n * Draws an image to the display.\n *\n * @param device[in] the handle of the device to control\n * @param x[in] the x-position where the image should be drawn onto the device\n * @param y[in] the y-position where the image should be drawn onto the device\n * @param image[in] the handle of the image to draw\n * @return true if drawing the image succeeded\n * @return false if drawing the image failed\n */\nbool qp_drawimage(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image);\n\n/**\n * Draws an image to the display, recoloring monochrome images to the desired foreground/background.\n *\n * @param device[in] the handle of the device to control\n * @param x[in] the x-position where the image should be drawn onto the device\n * @param y[in] the y-position where the image should be drawn onto the device\n * @param image[in] the handle of the image to draw\n * @param hue_fg[in] the foreground hue to use, with 0-360 mapped to 0-255\n * @param sat_fg[in] the foreground saturation to use, with 0-100% mapped to 0-255\n * @param val_fg[in] the foreground value to use, with 0-100% mapped to 0-255\n * @param hue_bg[in] the background hue to use, with 0-360 mapped to 0-255\n * @param sat_bg[in] the background saturation to use, with 0-100% mapped to 0-255\n * @param val_bg[in] the background value to use, with 0-100% mapped to 0-255\n * @return true if drawing the image succeeded\n * @return false if drawing the image failed\n */\nbool qp_drawimage_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg);\n\n/**\n * Draws an animation to the display.\n *\n * @param device[in] the handle of the device to control\n * @param x[in] the x-position where the image should be drawn onto the device\n * @param y[in] the y-position where the image should be drawn onto the device\n * @param image[in] the handle of the image to draw\n * @return the \\ref deferred_token to use with \\ref qp_stop_animation in order to stop animating\n * @return INVALID_DEFERRED_TOKEN if animating the image failed\n */\ndeferred_token qp_animate(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image);\n\n/**\n * Draws an animation to the display, recoloring monochrome images to the desired foreground/background.\n *\n * @param device[in] the handle of the device to control\n * @param x[in] the x-position where the image should be drawn onto the device\n * @param y[in] the y-position where the image should be drawn onto the device\n * @param image[in] the handle of the image to draw\n * @param hue_fg[in] the foreground hue to use, with 0-360 mapped to 0-255\n * @param sat_fg[in] the foreground saturation to use, with 0-100% mapped to 0-255\n * @param val_fg[in] the foreground value to use, with 0-100% mapped to 0-255\n * @param hue_bg[in] the background hue to use, with 0-360 mapped to 0-255\n * @param sat_bg[in] the background saturation to use, with 0-100% mapped to 0-255\n * @param val_bg[in] the background value to use, with 0-100% mapped to 0-255\n * @return the \\ref deferred_token to use with \\ref qp_stop_animation in order to stop animating\n * @return INVALID_DEFERRED_TOKEN if animating the image failed\n */\ndeferred_token qp_animate_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg);\n\n/**\n * Cancels a running animation.\n *\n * @param anim_token[in] the animation token returned by \\ref qp_animate, or \\ref qp_animate_recolor.\n */\nvoid qp_stop_animation(deferred_token anim_token);\n\n/**\n * Loads a font into memory.\n *\n * @note Fonts can be unloaded by calling \\ref qp_close_font.\n *\n * @param buffer[in] the font data to load\n * @return an image handle usable with \\ref qp_textwidth, \\ref qp_drawtext, and \\ref qp_drawtext_recolor.\n * @return NULL if loading the font failed\n */\npainter_font_handle_t qp_load_font_mem(const void *buffer);\n\n/**\n * Closes a font handle when no longer in use.\n *\n * @param font[in] the handle of the font to unload\n * @return true if unloading the font succeeded\n * @return false if unloading the font failed\n */\nbool qp_close_font(painter_font_handle_t font);\n\n/**\n * Measures the width (in pixels) of the supplied string, given the specified font.\n *\n * @param font[in] the handle of the font\n * @param str[in] the string to measure\n * @return the width (in pixels) needed to draw the specified string\n */\nint16_t qp_textwidth(painter_font_handle_t font, const char *str);\n\n/**\n * Draws text to the display.\n *\n * @param device[in] the handle of the device to control\n * @param x[in] the x-position where the text should be drawn onto the device\n * @param y[in] the y-position where the text should be drawn onto the device\n * @param font[in] the handle of the font\n * @param str[in] the string to draw\n * @return the width (in pixels) used when drawing the specified string\n */\nint16_t qp_drawtext(painter_device_t device, uint16_t x, uint16_t y, painter_font_handle_t font, const char *str);\n\n/**\n * Draws text to the display, recoloring monochrome fonts to the desired foreground/background.\n *\n * @param device[in] the handle of the device to control\n * @param x[in] the x-position where the text should be drawn onto the device\n * @param y[in] the y-position where the text should be drawn onto the device\n * @param font[in] the handle of the font\n * @param str[in] the string to draw\n * @param hue_fg[in] the foreground hue to use, with 0-360 mapped to 0-255\n * @param sat_fg[in] the foreground saturation to use, with 0-100% mapped to 0-255\n * @param val_fg[in] the foreground value to use, with 0-100% mapped to 0-255\n * @param hue_bg[in] the background hue to use, with 0-360 mapped to 0-255\n * @param sat_bg[in] the background saturation to use, with 0-100% mapped to 0-255\n * @param val_bg[in] the background value to use, with 0-100% mapped to 0-255\n * @return the width (in pixels) used when drawing the specified string\n */\nint16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_font_handle_t font, const char *str, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg);\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter Drivers\n\n#ifdef QUANTUM_PAINTER_RGB565_SURFACE_ENABLE\n#    include \"qp_rgb565_surface.h\"\n#else // QUANTUM_PAINTER_RGB565_SURFACE_ENABLE\n#    define RGB565_SURFACE_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_RGB565_SURFACE_ENABLE\n\n#ifdef QUANTUM_PAINTER_ILI9163_ENABLE\n#    include \"qp_ili9163.h\"\n#else // QUANTUM_PAINTER_ILI9163_ENABLE\n#    define ILI9163_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_ILI9163_ENABLE\n\n#ifdef QUANTUM_PAINTER_ILI9341_ENABLE\n#    include \"qp_ili9341.h\"\n#else // QUANTUM_PAINTER_ILI9341_ENABLE\n#    define ILI9341_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_ILI9341_ENABLE\n\n#ifdef QUANTUM_PAINTER_ILI9486_ENABLE\n#    include \"qp_ili9486.h\"\n#else // QUANTUM_PAINTER_ILI9486_ENABLE\n#    define ILI9486_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_ILI9486_ENABLE\n\n#ifdef QUANTUM_PAINTER_ILI9488_ENABLE\n#    include \"qp_ili9488.h\"\n#else // QUANTUM_PAINTER_ILI9488_ENABLE\n#    define ILI9488_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_ILI9488_ENABLE\n\n#ifdef QUANTUM_PAINTER_ST7789_ENABLE\n#    include \"qp_st7789.h\"\n#else // QUANTUM_PAINTER_ST7789_ENABLE\n#    define ST7789_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_ST7789_ENABLE\n\n#ifdef QUANTUM_PAINTER_ST7735_ENABLE\n#    include \"qp_st7735.h\"\n#else // QUANTUM_PAINTER_ST7735_ENABLE\n#    define ST7735_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_ST7735_ENABLE\n\n#ifdef QUANTUM_PAINTER_GC9A01_ENABLE\n#    include \"qp_gc9a01.h\"\n#else // QUANTUM_PAINTER_GC9A01_ENABLE\n#    define GC9A01_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_GC9A01_ENABLE\n\n#ifdef QUANTUM_PAINTER_GC9107_ENABLE\n#    include \"qp_gc9107.h\"\n#else // QUANTUM_PAINTER_GC9107_ENABLE\n#    define GC9107_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_GC9107_ENABLE\n\n#ifdef QUANTUM_PAINTER_SSD1351_ENABLE\n#    include \"qp_ssd1351.h\"\n#else // QUANTUM_PAINTER_SSD1351_ENABLE\n#    define SSD1351_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_SSD1351_ENABLE\n\n#ifdef QUANTUM_PAINTER_SH1106_ENABLE\n#    include \"qp_sh1106.h\"\n#else // QUANTUM_PAINTER_SH1106_ENABLE\n#    define SH1106_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_SH1106_ENABLE\n\n#ifdef QUANTUM_PAINTER_SH1107_ENABLE\n#    include \"qp_sh1107.h\"\n#else // QUANTUM_PAINTER_SH1107_ENABLE\n#    define SH1107_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_SH1107_ENABLE\n\n#ifdef QUANTUM_PAINTER_LD7032_ENABLE\n#    include \"qp_ld7032.h\"\n#else // QUANTUM_PAINTER_LD7032_ENABLE\n#    define LD7032_NUM_DEVICES 0\n#endif // QUANTUM_PAINTER_LD7032_ENABLE\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter Extras\n\n#ifdef QUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE\n#    include \"qp_lvgl.h\"\n#endif // QUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE\n"
  },
  {
    "path": "quantum/painter/qp_comms.c",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_comms.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Base comms APIs\n\nbool qp_comms_init(painter_device_t device) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_comms_init: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    return driver->comms_vtable->comms_init(device);\n}\n\nbool qp_comms_start(painter_device_t device) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_comms_start: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    return driver->comms_vtable->comms_start(device);\n}\n\nvoid qp_comms_stop(painter_device_t device) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_comms_stop: fail (validation_ok == false)\\n\");\n        return;\n    }\n\n    driver->comms_vtable->comms_stop(device);\n}\n\nuint32_t qp_comms_send(painter_device_t device, const void *data, uint32_t byte_count) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_comms_send: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    return driver->comms_vtable->comms_send(device, data, byte_count);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Comms APIs that use a D/C pin\n\nvoid qp_comms_command(painter_device_t device, uint8_t cmd) {\n    painter_driver_t *                   driver       = (painter_driver_t *)device;\n    painter_comms_with_command_vtable_t *comms_vtable = (painter_comms_with_command_vtable_t *)driver->comms_vtable;\n    comms_vtable->send_command(device, cmd);\n}\n\nvoid qp_comms_command_databyte(painter_device_t device, uint8_t cmd, uint8_t data) {\n    qp_comms_command(device, cmd);\n    qp_comms_send(device, &data, sizeof(data));\n}\n\nuint32_t qp_comms_command_databuf(painter_device_t device, uint8_t cmd, const void *data, uint32_t byte_count) {\n    qp_comms_command(device, cmd);\n    return qp_comms_send(device, data, byte_count);\n}\n\nvoid qp_comms_bulk_command_sequence(painter_device_t device, const uint8_t *sequence, size_t sequence_len) {\n    painter_driver_t *                   driver       = (painter_driver_t *)device;\n    painter_comms_with_command_vtable_t *comms_vtable = (painter_comms_with_command_vtable_t *)driver->comms_vtable;\n    comms_vtable->bulk_command_sequence(device, sequence, sequence_len);\n}\n"
  },
  {
    "path": "quantum/painter/qp_comms.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdlib.h>\n\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Base comms APIs\n\nbool     qp_comms_init(painter_device_t device);\nbool     qp_comms_start(painter_device_t device);\nvoid     qp_comms_stop(painter_device_t device);\nuint32_t qp_comms_send(painter_device_t device, const void* data, uint32_t byte_count);\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Comms APIs that use a D/C pin\n\nvoid     qp_comms_command(painter_device_t device, uint8_t cmd);\nvoid     qp_comms_command_databyte(painter_device_t device, uint8_t cmd, uint8_t data);\nuint32_t qp_comms_command_databuf(painter_device_t device, uint8_t cmd, const void* data, uint32_t byte_count);\nvoid     qp_comms_bulk_command_sequence(painter_device_t device, const uint8_t* sequence, size_t sequence_len);\n"
  },
  {
    "path": "quantum/painter/qp_draw.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"qp_internal.h\"\n#include \"qp_stream.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter utility functions\n\n// Global variable used for native pixel data streaming.\nextern uint8_t qp_internal_global_pixdata_buffer[QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE];\n\n// Check if the supplied bpp is capable of being rendered\nbool qp_internal_bpp_capable(uint8_t bits_per_pixel);\n\n// Returns the number of pixels that can fit in the pixdata buffer\nuint32_t qp_internal_num_pixels_in_buffer(painter_device_t device);\n\n// Fills the supplied buffer with equivalent native pixels matching the supplied HSV\nvoid qp_internal_fill_pixdata(painter_device_t device, uint32_t num_pixels, uint8_t hue, uint8_t sat, uint8_t val);\n\n// qp_setpixel internal implementation, but uses the global pixdata buffer with pre-converted native pixel. Only the first pixel is used.\nbool qp_internal_setpixel_impl(painter_device_t device, uint16_t x, uint16_t y);\n\n// qp_rect internal implementation, but uses the global pixdata buffer with pre-converted native pixels.\nbool qp_internal_fillrect_helper_impl(painter_device_t device, uint16_t l, uint16_t t, uint16_t r, uint16_t b);\n\n// Convert from input pixel data + palette to equivalent pixels\ntypedef int16_t (*qp_internal_byte_input_callback)(void* cb_arg);\ntypedef bool (*qp_internal_pixel_output_callback)(qp_pixel_t* palette, uint8_t index, void* cb_arg);\ntypedef bool (*qp_internal_byte_output_callback)(uint8_t byte, void* cb_arg);\nbool qp_internal_decode_palette(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_pixel_t* palette, qp_internal_pixel_output_callback output_callback, void* output_arg);\nbool qp_internal_decode_grayscale(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_internal_pixel_output_callback output_callback, void* output_arg);\nbool qp_internal_decode_recolor(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_pixel_t fg_hsv888, qp_pixel_t bg_hsv888, qp_internal_pixel_output_callback output_callback, void* output_arg);\nbool qp_internal_send_bytes(painter_device_t device, uint32_t byte_count, qp_internal_byte_input_callback input_callback, void* input_arg, qp_internal_byte_output_callback output_callback, void* output_arg);\n\n// Global variable used for interpolated pixel lookup table.\n#if QUANTUM_PAINTER_SUPPORTS_256_PALETTE\nextern qp_pixel_t qp_internal_global_pixel_lookup_table[256];\n#else\nextern qp_pixel_t qp_internal_global_pixel_lookup_table[16];\n#endif\n\n// Generates a color-interpolated lookup table based off the number of items, from foreground to background, for use with monochrome image rendering.\n// Returns true if a palette was created, false if the palette is reused.\n// As this uses a global, this may present a problem if using the same parameters but a different screen converts pixels -- use qp_internal_invalidate_palette() below to reset.\nbool qp_internal_interpolate_palette(qp_pixel_t fg_hsv888, qp_pixel_t bg_hsv888, int16_t steps);\n\n// Resets the global palette so that it can be regenerated. Only needed if the colors are identical, but a different display is used with a different internal pixel format.\nvoid qp_internal_invalidate_palette(void);\n\n// Helper shared between image and font rendering -- sets up the global palette to match the palette block specified in the asset. Expects the stream to be positioned at the start of the block header.\nbool qp_internal_load_qgf_palette(qp_stream_t* stream, uint8_t bpp);\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter codec functions\n\nenum qp_internal_rle_mode_t {\n    MARKER_BYTE,\n    REPEATING_RUN,\n    NON_REPEATING_RUN,\n};\n\ntypedef struct qp_internal_byte_input_state_t {\n    painter_device_t device;\n    qp_stream_t*     src_stream;\n    int16_t          curr;\n    union {\n        // RLE-specific\n        struct {\n            enum qp_internal_rle_mode_t mode;\n            uint8_t                     remain; // number of bytes remaining in the current mode\n        } rle;\n    };\n} qp_internal_byte_input_state_t;\n\ntypedef struct qp_internal_pixel_output_state_t {\n    painter_device_t device;\n    uint32_t         pixel_write_pos;\n    uint32_t         max_pixels;\n} qp_internal_pixel_output_state_t;\n\nbool qp_internal_pixel_appender(qp_pixel_t* palette, uint8_t index, void* cb_arg);\n\ntypedef struct qp_internal_byte_output_state_t {\n    painter_device_t device;\n    uint32_t         byte_write_pos;\n    uint32_t         max_bytes;\n} qp_internal_byte_output_state_t;\n\nbool qp_internal_byte_appender(uint8_t byteval, void* cb_arg);\n\n// Helper shared between image and font rendering, sends pixels to the display using:\n//     - qp_internal_decode_palette + qp_internal_pixel_appender (bpp <= 8)\n//     - qp_internal_send_bytes                                  (bpp > 8)\nbool qp_internal_appender(painter_device_t device, uint8_t bpp, uint32_t pixel_count, qp_internal_byte_input_callback input_callback, void* input_state);\n\nqp_internal_byte_input_callback qp_internal_prepare_input_state(qp_internal_byte_input_state_t* input_state, painter_compression_t compression);\n"
  },
  {
    "path": "quantum/painter/qp_draw_circle.c",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp.h\"\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_draw.h\"\n\n// Utilize 8-way symmetry to draw circles\nstatic bool qp_circle_helper_impl(painter_device_t device, uint16_t centerx, uint16_t centery, uint16_t offsetx, uint16_t offsety, bool filled) {\n    /*\n    Circles have the property of 8-way symmetry, so eight pixels can be drawn\n    for each computed [offsetx,offsety] given the center coordinates\n    represented by [centerx,centery].\n\n    For filled circles, we can draw horizontal lines between each pair of\n    pixels with the same final value of y.\n\n    Two special cases exist and have been optimized:\n    1) offsetx == offsety (the final point), makes half the coordinates\n    equivalent, so we can omit them (and the corresponding fill lines)\n    2) offsetx == 0 (the starting point) means that some horizontal lines\n    would be a single pixel in length, so we write individual pixels instead.\n    This also makes half the symmetrical points identical to their twins,\n    so we only need four points or two points and one line\n    */\n\n    int16_t xpx = ((int16_t)centerx) + ((int16_t)offsetx);\n    int16_t xmx = ((int16_t)centerx) - ((int16_t)offsetx);\n    int16_t xpy = ((int16_t)centerx) + ((int16_t)offsety);\n    int16_t xmy = ((int16_t)centerx) - ((int16_t)offsety);\n    int16_t ypx = ((int16_t)centery) + ((int16_t)offsetx);\n    int16_t ymx = ((int16_t)centery) - ((int16_t)offsetx);\n    int16_t ypy = ((int16_t)centery) + ((int16_t)offsety);\n    int16_t ymy = ((int16_t)centery) - ((int16_t)offsety);\n\n    if (offsetx == 0) {\n        if (!qp_internal_setpixel_impl(device, centerx, ypy)) {\n            return false;\n        }\n        if (!qp_internal_setpixel_impl(device, centerx, ymy)) {\n            return false;\n        }\n        if (filled) {\n            if (!qp_internal_fillrect_helper_impl(device, xpy, centery, xmy, centery)) {\n                return false;\n            }\n        } else {\n            if (!qp_internal_setpixel_impl(device, xpy, centery)) {\n                return false;\n            }\n            if (!qp_internal_setpixel_impl(device, xmy, centery)) {\n                return false;\n            }\n        }\n    } else if (offsetx == offsety) {\n        if (filled) {\n            if (!qp_internal_fillrect_helper_impl(device, xpy, ypy, xmy, ypy)) {\n                return false;\n            }\n            if (!qp_internal_fillrect_helper_impl(device, xpy, ymy, xmy, ymy)) {\n                return false;\n            }\n        } else {\n            if (!qp_internal_setpixel_impl(device, xpy, ypy)) {\n                return false;\n            }\n            if (!qp_internal_setpixel_impl(device, xmy, ypy)) {\n                return false;\n            }\n            if (!qp_internal_setpixel_impl(device, xpy, ymy)) {\n                return false;\n            }\n            if (!qp_internal_setpixel_impl(device, xmy, ymy)) {\n                return false;\n            }\n        }\n\n    } else {\n        if (filled) {\n            if (!qp_internal_fillrect_helper_impl(device, xpx, ypy, xmx, ypy)) {\n                return false;\n            }\n            if (!qp_internal_fillrect_helper_impl(device, xpx, ymy, xmx, ymy)) {\n                return false;\n            }\n            if (!qp_internal_fillrect_helper_impl(device, xpy, ypx, xmy, ypx)) {\n                return false;\n            }\n            if (!qp_internal_fillrect_helper_impl(device, xpy, ymx, xmy, ymx)) {\n                return false;\n            }\n        } else {\n            if (!qp_internal_setpixel_impl(device, xpx, ypy)) {\n                return false;\n            }\n            if (!qp_internal_setpixel_impl(device, xmx, ypy)) {\n                return false;\n            }\n            if (!qp_internal_setpixel_impl(device, xpx, ymy)) {\n                return false;\n            }\n            if (!qp_internal_setpixel_impl(device, xmx, ymy)) {\n                return false;\n            }\n            if (!qp_internal_setpixel_impl(device, xpy, ypx)) {\n                return false;\n            }\n            if (!qp_internal_setpixel_impl(device, xmy, ypx)) {\n                return false;\n            }\n            if (!qp_internal_setpixel_impl(device, xpy, ymx)) {\n                return false;\n            }\n            if (!qp_internal_setpixel_impl(device, xmy, ymx)) {\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_circle\n\nbool qp_circle(painter_device_t device, uint16_t x, uint16_t y, uint16_t radius, uint8_t hue, uint8_t sat, uint8_t val, bool filled) {\n    qp_dprintf(\"qp_circle: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_circle: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    // plot the initial set of points for x, y and r\n    int16_t xcalc = 0;\n    int16_t ycalc = (int16_t)radius;\n    int16_t err   = ((5 - (radius >> 2)) >> 2);\n\n    qp_internal_fill_pixdata(device, (radius * 2) + 1, hue, sat, val);\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"qp_circle: fail (could not start comms)\\n\");\n        return false;\n    }\n\n    bool ret = true;\n    if (!qp_circle_helper_impl(device, x, y, xcalc, ycalc, filled)) {\n        ret = false;\n    }\n\n    if (ret) {\n        while (xcalc < ycalc) {\n            xcalc++;\n            if (err < 0) {\n                err += (xcalc << 1) + 1;\n            } else {\n                ycalc--;\n                err += ((xcalc - ycalc) << 1) + 1;\n            }\n            if (!qp_circle_helper_impl(device, x, y, xcalc, ycalc, filled)) {\n                ret = false;\n                break;\n            }\n        }\n    }\n\n    qp_dprintf(\"qp_circle: %s\\n\", ret ? \"ok\" : \"fail\");\n    qp_comms_stop(device);\n    return ret;\n}\n"
  },
  {
    "path": "quantum/painter/qp_draw_codec.c",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// Copyright 2023 Pablo Martinez (@elpekenin) <elpekenin@elpekenin.dev>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_draw.h\"\n#include \"qp_comms.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Palette / Monochrome-format decoder\n\nstatic const qp_pixel_t qp_pixel_white = {.hsv888 = {.h = 0, .s = 0, .v = 255}};\nstatic const qp_pixel_t qp_pixel_black = {.hsv888 = {.h = 0, .s = 0, .v = 0}};\n\nbool qp_internal_bpp_capable(uint8_t bits_per_pixel) {\n#if !(QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS)\n#    if !(QUANTUM_PAINTER_SUPPORTS_256_PALETTE)\n    if (bits_per_pixel > 4) {\n        qp_dprintf(\"qp_internal_decode_palette: image bpp greater than 4\\n\");\n        return false;\n    }\n#    endif\n\n    if (bits_per_pixel > 8) {\n        qp_dprintf(\"qp_internal_decode_palette: image bpp greater than 8\\n\");\n        return false;\n    }\n#endif\n    return true;\n}\n\nbool qp_internal_decode_palette(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_pixel_t* palette, qp_internal_pixel_output_callback output_callback, void* output_arg) {\n    const uint8_t pixel_bitmask    = (1 << bits_per_pixel) - 1;\n    const uint8_t pixels_per_byte  = 8 / bits_per_pixel;\n    uint32_t      remaining_pixels = pixel_count; // don't try to derive from byte_count, we may not use an entire byte\n    while (remaining_pixels > 0) {\n        int16_t byteval = input_callback(input_arg);\n        if (byteval < 0) {\n            return false;\n        }\n        uint8_t loop_pixels = remaining_pixels < pixels_per_byte ? remaining_pixels : pixels_per_byte;\n        for (uint8_t q = 0; q < loop_pixels; ++q) {\n            if (!output_callback(palette, byteval & pixel_bitmask, output_arg)) {\n                return false;\n            }\n            byteval >>= bits_per_pixel;\n        }\n        remaining_pixels -= loop_pixels;\n    }\n    return true;\n}\n\nbool qp_internal_decode_grayscale(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_internal_pixel_output_callback output_callback, void* output_arg) {\n    return qp_internal_decode_recolor(device, pixel_count, bits_per_pixel, input_callback, input_arg, qp_pixel_white, qp_pixel_black, output_callback, output_arg);\n}\n\nbool qp_internal_decode_recolor(painter_device_t device, uint32_t pixel_count, uint8_t bits_per_pixel, qp_internal_byte_input_callback input_callback, void* input_arg, qp_pixel_t fg_hsv888, qp_pixel_t bg_hsv888, qp_internal_pixel_output_callback output_callback, void* output_arg) {\n    painter_driver_t* driver = (painter_driver_t*)device;\n    int16_t           steps  = 1 << bits_per_pixel; // number of items we need to interpolate\n    if (qp_internal_interpolate_palette(fg_hsv888, bg_hsv888, steps)) {\n        if (!driver->driver_vtable->palette_convert(device, steps, qp_internal_global_pixel_lookup_table)) {\n            return false;\n        }\n    }\n\n    return qp_internal_decode_palette(device, pixel_count, bits_per_pixel, input_callback, input_arg, qp_internal_global_pixel_lookup_table, output_callback, output_arg);\n}\n\nbool qp_internal_send_bytes(painter_device_t device, uint32_t byte_count, qp_internal_byte_input_callback input_callback, void* input_arg, qp_internal_byte_output_callback output_callback, void* output_arg) {\n    uint32_t remaining_bytes = byte_count;\n    while (remaining_bytes > 0) {\n        int16_t byteval = input_callback(input_arg);\n        if (byteval < 0) {\n            return false;\n        }\n        if (!output_callback(byteval, output_arg)) {\n            return false;\n        }\n        remaining_bytes -= 1;\n    }\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Progressive pull of bytes, push of pixels\n\nstatic inline int16_t qp_drawimage_byte_uncompressed_decoder(void* cb_arg) {\n    qp_internal_byte_input_state_t* state = (qp_internal_byte_input_state_t*)cb_arg;\n    state->curr                           = qp_stream_get(state->src_stream);\n    return state->curr;\n}\n\nstatic inline int16_t qp_drawimage_byte_rle_decoder(void* cb_arg) {\n    qp_internal_byte_input_state_t* state = (qp_internal_byte_input_state_t*)cb_arg;\n\n    // Work out if we're parsing the initial marker byte\n    if (state->rle.mode == MARKER_BYTE) {\n        uint8_t c = qp_stream_get(state->src_stream);\n        if (c >= 128) {\n            state->rle.mode   = NON_REPEATING_RUN; // non-repeated run\n            state->rle.remain = c - 127;\n        } else {\n            state->rle.mode   = REPEATING_RUN; // repeated run\n            state->rle.remain = c;\n        }\n\n        state->curr = qp_stream_get(state->src_stream);\n    }\n\n    // Work out which byte we're returning\n    uint8_t c = state->curr;\n\n    // Decrement the counter of the bytes remaining\n    state->rle.remain--;\n\n    if (state->rle.remain > 0) {\n        // If we're in a non-repeating run, queue up the next byte\n        if (state->rle.mode == NON_REPEATING_RUN) {\n            state->curr = qp_stream_get(state->src_stream);\n        }\n    } else {\n        // Swap back to querying the marker byte mode\n        state->rle.mode = MARKER_BYTE;\n    }\n\n    return c;\n}\n\nbool qp_internal_pixel_appender(qp_pixel_t* palette, uint8_t index, void* cb_arg) {\n    qp_internal_pixel_output_state_t* state  = (qp_internal_pixel_output_state_t*)cb_arg;\n    painter_driver_t*                 driver = (painter_driver_t*)state->device;\n\n    if (!driver->driver_vtable->append_pixels(state->device, qp_internal_global_pixdata_buffer, palette, state->pixel_write_pos++, 1, &index)) {\n        return false;\n    }\n\n    // If we've hit the transmit limit, send out the entire buffer and reset the write position\n    if (state->pixel_write_pos == state->max_pixels) {\n        if (!driver->driver_vtable->pixdata(state->device, qp_internal_global_pixdata_buffer, state->pixel_write_pos)) {\n            return false;\n        }\n        state->pixel_write_pos = 0;\n    }\n\n    return true;\n}\n\nbool qp_internal_byte_appender(uint8_t byteval, void* cb_arg) {\n    qp_internal_byte_output_state_t* state  = (qp_internal_byte_output_state_t*)cb_arg;\n    painter_driver_t*                driver = (painter_driver_t*)state->device;\n\n    if (!driver->driver_vtable->append_pixdata(state->device, qp_internal_global_pixdata_buffer, state->byte_write_pos++, byteval)) {\n        return false;\n    }\n\n    // If we've hit the transmit limit, send out the entire buffer and reset the write position\n    if (state->byte_write_pos == state->max_bytes) {\n        painter_driver_t* driver = (painter_driver_t*)state->device;\n        if (!driver->driver_vtable->pixdata(state->device, qp_internal_global_pixdata_buffer, state->byte_write_pos * 8 / driver->native_bits_per_pixel)) {\n            return false;\n        }\n        state->byte_write_pos = 0;\n    }\n\n    return true;\n}\n\n// Helper shared between image and font rendering -- uses either (qp_internal_decode_palette + qp_internal_pixel_appender) or (qp_internal_send_bytes) to send data data to the display based on the asset's native-ness\nbool qp_internal_appender(painter_device_t device, uint8_t bpp, uint32_t pixel_count, qp_internal_byte_input_callback input_callback, void* input_state) {\n    painter_driver_t* driver = (painter_driver_t*)device;\n\n    bool ret = false;\n\n    // Non-native pixel format\n    if (bpp <= 8) {\n        // Set up the output state\n        qp_internal_pixel_output_state_t output_state = {.device = device, .pixel_write_pos = 0, .max_pixels = qp_internal_num_pixels_in_buffer(device)};\n\n        // Decode the pixel data and stream to the display\n        ret = qp_internal_decode_palette(device, pixel_count, bpp, input_callback, input_state, qp_internal_global_pixel_lookup_table, qp_internal_pixel_appender, &output_state);\n        // Any leftovers need transmission as well.\n        if (ret && output_state.pixel_write_pos > 0) {\n            ret &= driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, output_state.pixel_write_pos);\n        }\n    }\n\n    // Native pixel format\n    else if (bpp != driver->native_bits_per_pixel) {\n        qp_dprintf(\"Asset's bpp (%d) doesn't match the target display's native_bits_per_pixel (%d)\\n\", bpp, driver->native_bits_per_pixel);\n        return false;\n    } else {\n        // Set up the output state\n        qp_internal_byte_output_state_t output_state = {.device = device, .byte_write_pos = 0, .max_bytes = qp_internal_num_pixels_in_buffer(device) * driver->native_bits_per_pixel / 8};\n\n        // Stream the raw pixel data to the display\n        uint32_t byte_count = pixel_count * bpp / 8;\n        ret                 = qp_internal_send_bytes(device, byte_count, input_callback, input_state, qp_internal_byte_appender, &output_state);\n        // Any leftovers need transmission as well.\n        if (ret && output_state.byte_write_pos > 0) {\n            ret &= driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, output_state.byte_write_pos * 8 / driver->native_bits_per_pixel);\n        }\n    }\n\n    return ret;\n}\n\nqp_internal_byte_input_callback qp_internal_prepare_input_state(qp_internal_byte_input_state_t* input_state, painter_compression_t compression) {\n    switch (compression) {\n        case IMAGE_UNCOMPRESSED:\n            return qp_drawimage_byte_uncompressed_decoder;\n        case IMAGE_COMPRESSED_RLE:\n            input_state->rle.mode   = MARKER_BYTE;\n            input_state->rle.remain = 0;\n            return qp_drawimage_byte_rle_decoder;\n        default:\n            return NULL;\n    }\n}\n"
  },
  {
    "path": "quantum/painter/qp_draw_core.c",
    "content": "// Copyright 2021-2022 Nick Brassel (@tzarc)\n// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"compiler_support.h\"\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_draw.h\"\n#include \"qgf.h\"\n\nSTATIC_ASSERT((QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE > 0) && (QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE % 16) == 0, \"QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE needs to be a non-zero multiple of 16\");\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Global variables\n//\n// NOTE: The variables in this section are intentionally outside a stack frame. They are able to be defined with larger\n//       sizes than the normal stack frames would allow, and as such need to be external.\n//\n//       **** DO NOT refactor this and decide to place the variables inside the function calling them -- you will ****\n//       **** very likely get artifacts rendered to the screen as a result.                                       ****\n//\n\n// Buffer used for transmitting native pixel data to the downstream device.\n__attribute__((__aligned__(4))) uint8_t qp_internal_global_pixdata_buffer[QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE];\n\n// Static buffer to contain a generated color palette\nstatic bool                                       generated_palette = false;\nstatic int16_t                                    generated_steps   = -1;\n__attribute__((__aligned__(4))) static qp_pixel_t interpolated_fg_hsv888;\n__attribute__((__aligned__(4))) static qp_pixel_t interpolated_bg_hsv888;\n#if QUANTUM_PAINTER_SUPPORTS_256_PALETTE\n__attribute__((__aligned__(4))) qp_pixel_t qp_internal_global_pixel_lookup_table[256];\n#else\n__attribute__((__aligned__(4))) qp_pixel_t qp_internal_global_pixel_lookup_table[16];\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Helpers\n\nuint32_t qp_internal_num_pixels_in_buffer(painter_device_t device) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n    return ((QUANTUM_PAINTER_PIXDATA_BUFFER_SIZE * 8) / driver->native_bits_per_pixel);\n}\n\n// qp_setpixel internal implementation, but accepts a buffer with pre-converted native pixel. Only the first pixel is used.\nbool qp_internal_setpixel_impl(painter_device_t device, uint16_t x, uint16_t y) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n    return driver->driver_vtable->viewport(device, x, y, x, y) && driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, 1);\n}\n\n// Fills the global native pixel buffer with equivalent pixels matching the supplied HSV\nvoid qp_internal_fill_pixdata(painter_device_t device, uint32_t num_pixels, uint8_t hue, uint8_t sat, uint8_t val) {\n    painter_driver_t *driver            = (painter_driver_t *)device;\n    uint32_t          pixels_in_pixdata = qp_internal_num_pixels_in_buffer(device);\n    num_pixels                          = QP_MIN(pixels_in_pixdata, num_pixels);\n\n    // Convert the color to native pixel format\n    qp_pixel_t color = {.hsv888 = {.h = hue, .s = sat, .v = val}};\n    driver->driver_vtable->palette_convert(device, 1, &color);\n\n    // Append the required number of pixels\n    uint8_t palette_idx = 0;\n    for (uint32_t i = 0; i < num_pixels; ++i) {\n        driver->driver_vtable->append_pixels(device, qp_internal_global_pixdata_buffer, &color, i, 1, &palette_idx);\n    }\n}\n\n// Resets the global palette so that it can be regenerated. Only needed if the colors are identical, but a different display is used with a different internal pixel format.\nvoid qp_internal_invalidate_palette(void) {\n    generated_palette = false;\n    generated_steps   = -1;\n}\n\n// Interpolates between two colors to generate a palette\nbool qp_internal_interpolate_palette(qp_pixel_t fg_hsv888, qp_pixel_t bg_hsv888, int16_t steps) {\n    // Check if we need to generate a new palette -- if the input parameters match then assume the palette can stay unchanged.\n    // This may present a problem if using the same parameters but a different screen converts pixels -- use qp_internal_invalidate_palette() to reset.\n    if (generated_palette == true && generated_steps == steps && memcmp(&interpolated_fg_hsv888, &fg_hsv888, sizeof(fg_hsv888)) == 0 && memcmp(&interpolated_bg_hsv888, &bg_hsv888, sizeof(bg_hsv888)) == 0) {\n        // We already have the correct palette, no point regenerating it.\n        return false;\n    }\n\n    // Save the parameters so we know whether we can skip generation\n    generated_palette      = true;\n    generated_steps        = steps;\n    interpolated_fg_hsv888 = fg_hsv888;\n    interpolated_bg_hsv888 = bg_hsv888;\n\n    int16_t hue_fg = fg_hsv888.hsv888.h;\n    int16_t hue_bg = bg_hsv888.hsv888.h;\n\n    // Make sure we take the \"shortest\" route from one hue to the other\n    if ((hue_fg - hue_bg) >= 128) {\n        hue_bg += 256;\n    } else if ((hue_fg - hue_bg) <= -128) {\n        hue_bg -= 256;\n    }\n\n    // Interpolate each of the lookup table entries\n    for (int16_t i = 0; i < steps; ++i) {\n        qp_internal_global_pixel_lookup_table[i].hsv888.h = (uint8_t)((hue_fg - hue_bg) * i / (steps - 1) + hue_bg);\n        qp_internal_global_pixel_lookup_table[i].hsv888.s = (uint8_t)((fg_hsv888.hsv888.s - bg_hsv888.hsv888.s) * i / (steps - 1) + bg_hsv888.hsv888.s);\n        qp_internal_global_pixel_lookup_table[i].hsv888.v = (uint8_t)((fg_hsv888.hsv888.v - bg_hsv888.hsv888.v) * i / (steps - 1) + bg_hsv888.hsv888.v);\n\n        qp_dprintf(\"qp_internal_interpolate_palette: %3d of %d -- H: %3d, S: %3d, V: %3d\\n\", (int)(i + 1), (int)steps, (int)qp_internal_global_pixel_lookup_table[i].hsv888.h, (int)qp_internal_global_pixel_lookup_table[i].hsv888.s, (int)qp_internal_global_pixel_lookup_table[i].hsv888.v);\n    }\n\n    return true;\n}\n\n// Helper shared between image and font rendering -- sets up the global palette to match the palette block specified in the asset. Expects the stream to be positioned at the start of the block header.\nbool qp_internal_load_qgf_palette(qp_stream_t *stream, uint8_t bpp) {\n    qgf_palette_v1_t palette_descriptor;\n    if (qp_stream_read(&palette_descriptor, sizeof(qgf_palette_v1_t), 1, stream) != 1) {\n        qp_dprintf(\"Failed to read palette_descriptor, expected length was not %d\\n\", (int)sizeof(qgf_palette_v1_t));\n        return false;\n    }\n\n    // BPP determines the number of palette entries, each entry is a HSV888 triplet.\n    const uint16_t palette_entries = 1u << bpp;\n\n    // Ensure we aren't reusing any palette\n    qp_internal_invalidate_palette();\n\n    // Read the palette entries\n    for (uint16_t i = 0; i < palette_entries; ++i) {\n        // Read the palette entry\n        qgf_palette_entry_v1_t entry;\n        if (qp_stream_read(&entry, sizeof(qgf_palette_entry_v1_t), 1, stream) != 1) {\n            return false;\n        }\n\n        // Update the lookup table\n        qp_internal_global_pixel_lookup_table[i].hsv888.h = entry.h;\n        qp_internal_global_pixel_lookup_table[i].hsv888.s = entry.s;\n        qp_internal_global_pixel_lookup_table[i].hsv888.v = entry.v;\n\n        qp_dprintf(\"qp_internal_load_qgf_palette: %3d of %d -- H: %3d, S: %3d, V: %3d\\n\", (int)(i + 1), (int)palette_entries, (int)qp_internal_global_pixel_lookup_table[i].hsv888.h, (int)qp_internal_global_pixel_lookup_table[i].hsv888.s, (int)qp_internal_global_pixel_lookup_table[i].hsv888.v);\n    }\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_setpixel\n\nbool qp_setpixel(painter_device_t device, uint16_t x, uint16_t y, uint8_t hue, uint8_t sat, uint8_t val) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_setpixel: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"Failed to start comms in qp_setpixel\\n\");\n        return false;\n    }\n\n    qp_internal_fill_pixdata(device, 1, hue, sat, val);\n    bool ret = qp_internal_setpixel_impl(device, x, y);\n    qp_comms_stop(device);\n    qp_dprintf(\"qp_setpixel: %s\\n\", ret ? \"ok\" : \"fail\");\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_line\n\nbool qp_line(painter_device_t device, uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t hue, uint8_t sat, uint8_t val) {\n    if (x0 == x1 || y0 == y1) {\n        qp_dprintf(\"qp_line(%d, %d, %d, %d): entry (deferring to qp_rect)\\n\", (int)x0, (int)y0, (int)x1, (int)y1);\n        bool ret = qp_rect(device, x0, y0, x1, y1, hue, sat, val, true);\n        qp_dprintf(\"qp_line(%d, %d, %d, %d): %s (deferred to qp_rect)\\n\", (int)x0, (int)y0, (int)x1, (int)y1, ret ? \"ok\" : \"fail\");\n        return ret;\n    }\n\n    qp_dprintf(\"qp_line(%d, %d, %d, %d): entry\\n\", (int)x0, (int)y0, (int)x1, (int)y1);\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_line: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"Failed to start comms in qp_line\\n\");\n        return false;\n    }\n\n    qp_internal_fill_pixdata(device, 1, hue, sat, val);\n\n    // draw angled line using Bresenham's algo\n    int16_t x      = ((int16_t)x0);\n    int16_t y      = ((int16_t)y0);\n    int16_t slopex = ((int16_t)x0) < ((int16_t)x1) ? 1 : -1;\n    int16_t slopey = ((int16_t)y0) < ((int16_t)y1) ? 1 : -1;\n    int16_t dx     = abs(((int16_t)x1) - ((int16_t)x0));\n    int16_t dy     = -abs(((int16_t)y1) - ((int16_t)y0));\n\n    int16_t e  = dx + dy;\n    int16_t e2 = 2 * e;\n\n    bool ret = true;\n    while (x != x1 || y != y1) {\n        if (!qp_internal_setpixel_impl(device, x, y)) {\n            ret = false;\n            break;\n        }\n        e2 = 2 * e;\n        if (e2 >= dy) {\n            e += dy;\n            x += slopex;\n        }\n        if (e2 <= dx) {\n            e += dx;\n            y += slopey;\n        }\n    }\n    // draw the last pixel\n    if (!qp_internal_setpixel_impl(device, x, y)) {\n        ret = false;\n    }\n\n    qp_comms_stop(device);\n    qp_dprintf(\"qp_line(%d, %d, %d, %d): %s\\n\", (int)x0, (int)y0, (int)x1, (int)y1, ret ? \"ok\" : \"fail\");\n    return ret;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_rect\n\nbool qp_internal_fillrect_helper_impl(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom) {\n    uint32_t          pixels_in_pixdata = qp_internal_num_pixels_in_buffer(device);\n    painter_driver_t *driver            = (painter_driver_t *)device;\n\n    uint16_t l = QP_MIN(left, right);\n    uint16_t r = QP_MAX(left, right);\n    uint16_t t = QP_MIN(top, bottom);\n    uint16_t b = QP_MAX(top, bottom);\n    uint16_t w = r - l + 1;\n    uint16_t h = b - t + 1;\n\n    uint32_t remaining = w * h;\n    driver->driver_vtable->viewport(device, l, t, r, b);\n    while (remaining > 0) {\n        uint32_t transmit = QP_MIN(remaining, pixels_in_pixdata);\n        if (!driver->driver_vtable->pixdata(device, qp_internal_global_pixdata_buffer, transmit)) {\n            return false;\n        }\n        remaining -= transmit;\n    }\n    return true;\n}\n\nbool qp_rect(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom, uint8_t hue, uint8_t sat, uint8_t val, bool filled) {\n    qp_dprintf(\"qp_rect(%d, %d, %d, %d): entry\\n\", (int)left, (int)top, (int)right, (int)bottom);\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_rect: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    // Cater for cases where people have submitted the coordinates backwards\n    uint16_t l = QP_MIN(left, right);\n    uint16_t r = QP_MAX(left, right);\n    uint16_t t = QP_MIN(top, bottom);\n    uint16_t b = QP_MAX(top, bottom);\n    uint16_t w = r - l + 1;\n    uint16_t h = b - t + 1;\n\n    bool ret = true;\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"Failed to start comms in qp_rect\\n\");\n        return false;\n    }\n\n    if (filled) {\n        // Fill up the pixdata buffer with the required number of native pixels\n        qp_internal_fill_pixdata(device, w * h, hue, sat, val);\n\n        // Perform the draw\n        ret = qp_internal_fillrect_helper_impl(device, l, t, r, b);\n    } else {\n        // Fill up the pixdata buffer with the required number of native pixels\n        qp_internal_fill_pixdata(device, QP_MAX(w, h), hue, sat, val);\n\n        // Draw 4x filled single-width rects to create an outline\n        if (!qp_internal_fillrect_helper_impl(device, l, t, r, t) || !qp_internal_fillrect_helper_impl(device, l, b, r, b) || !qp_internal_fillrect_helper_impl(device, l, t + 1, l, b - 1) || !qp_internal_fillrect_helper_impl(device, r, t + 1, r, b - 1)) {\n            ret = false;\n        }\n    }\n\n    qp_comms_stop(device);\n    qp_dprintf(\"qp_rect(%d, %d, %d, %d): %s\\n\", (int)l, (int)t, (int)r, (int)b, ret ? \"ok\" : \"fail\");\n    return ret;\n}\n"
  },
  {
    "path": "quantum/painter/qp_draw_ellipse.c",
    "content": "// Copyright 2021 Paul Cotter (@gr1mr3aver)\n// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_comms.h\"\n#include \"qp_draw.h\"\n\n// Utilize 4-way symmetry to draw an ellipse\nstatic bool qp_ellipse_helper_impl(painter_device_t device, uint16_t centerx, uint16_t centery, uint16_t offsetx, uint16_t offsety, bool filled) {\n    /*\n    Ellipses have the property of 4-way symmetry, so four pixels can be drawn\n    for each computed [offsetx,offsety] given the center coordinates\n    represented by [centerx,centery].\n\n    For filled ellipses, we can draw horizontal lines between each pair of\n    pixels with the same final value of y.\n\n    When offsetx == 0 only two pixels can be drawn for filled or unfilled ellipses\n    */\n\n    int16_t xpx = ((int16_t)centerx) + ((int16_t)offsetx);\n    int16_t xmx = ((int16_t)centerx) - ((int16_t)offsetx);\n    int16_t ypy = ((int16_t)centery) + ((int16_t)offsety);\n    int16_t ymy = ((int16_t)centery) - ((int16_t)offsety);\n\n    if (offsetx == 0) {\n        if (!qp_internal_setpixel_impl(device, xpx, ypy)) {\n            return false;\n        }\n        if (!qp_internal_setpixel_impl(device, xpx, ymy)) {\n            return false;\n        }\n    } else if (filled) {\n        if (!qp_internal_fillrect_helper_impl(device, xpx, ypy, xmx, ypy)) {\n            return false;\n        }\n        if (offsety > 0 && !qp_internal_fillrect_helper_impl(device, xpx, ymy, xmx, ymy)) {\n            return false;\n        }\n    } else {\n        if (!qp_internal_setpixel_impl(device, xpx, ypy)) {\n            return false;\n        }\n        if (!qp_internal_setpixel_impl(device, xpx, ymy)) {\n            return false;\n        }\n        if (!qp_internal_setpixel_impl(device, xmx, ypy)) {\n            return false;\n        }\n        if (!qp_internal_setpixel_impl(device, xmx, ymy)) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_ellipse\n\nbool qp_ellipse(painter_device_t device, uint16_t x, uint16_t y, uint16_t sizex, uint16_t sizey, uint8_t hue, uint8_t sat, uint8_t val, bool filled) {\n    qp_dprintf(\"qp_ellipse: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_ellipse: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    int32_t aa = ((int32_t)sizex) * ((int32_t)sizex);\n    int32_t bb = ((int32_t)sizey) * ((int32_t)sizey);\n    int32_t fa = 4 * aa;\n    int32_t fb = 4 * bb;\n\n    int16_t dx = 0;\n    int16_t dy = ((int16_t)sizey);\n\n    qp_internal_fill_pixdata(device, QP_MAX(sizex, sizey), hue, sat, val);\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"qp_ellipse: fail (could not start comms)\\n\");\n        return false;\n    }\n\n    bool ret = true;\n    for (int32_t delta = (2 * bb) + (aa * (1 - (2 * sizey))); bb * dx <= aa * dy; dx++) {\n        if (!qp_ellipse_helper_impl(device, x, y, dx, dy, filled)) {\n            ret = false;\n            break;\n        }\n        if (delta >= 0) {\n            delta += fa * (1 - dy);\n            dy--;\n        }\n        delta += bb * (4 * dx + 6);\n    }\n\n    dx = sizex;\n    dy = 0;\n\n    for (int32_t delta = (2 * aa) + (bb * (1 - (2 * sizex))); aa * dy <= bb * dx; dy++) {\n        if (!qp_ellipse_helper_impl(device, x, y, dx, dy, filled)) {\n            ret = false;\n            break;\n        }\n        if (delta >= 0) {\n            delta += fb * (1 - dx);\n            dx--;\n        }\n        delta += aa * (4 * dy + 6);\n    }\n\n    qp_dprintf(\"qp_ellipse: %s\\n\", ret ? \"ok\" : \"fail\");\n    qp_comms_stop(device);\n    return ret;\n}\n"
  },
  {
    "path": "quantum/painter/qp_draw_image.c",
    "content": "// Copyright 2021-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n#include \"qp_draw.h\"\n#include \"qp_comms.h\"\n#include \"qgf.h\"\n#include \"deferred_exec.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// QGF image handles\n\ntypedef struct qgf_image_handle_t {\n    painter_image_desc_t base;\n    bool                 validate_ok;\n    union {\n        qp_stream_t        stream;\n        qp_memory_stream_t mem_stream;\n#ifdef QP_STREAM_HAS_FILE_IO\n        qp_file_stream_t file_stream;\n#endif // QP_STREAM_HAS_FILE_IO\n    };\n} qgf_image_handle_t;\n\nstatic qgf_image_handle_t image_descriptors[QUANTUM_PAINTER_NUM_IMAGES] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Helper: load image from stream\n\nstatic painter_image_handle_t qp_load_image_internal(bool (*stream_factory)(qgf_image_handle_t *image, void *arg), void *arg) {\n    qp_dprintf(\"qp_load_image: entry\\n\");\n    qgf_image_handle_t *image = NULL;\n\n    // Find a free slot\n    for (int i = 0; i < QUANTUM_PAINTER_NUM_IMAGES; ++i) {\n        if (!image_descriptors[i].validate_ok) {\n            image = &image_descriptors[i];\n            break;\n        }\n    }\n\n    // Drop out if not found\n    if (!image) {\n        qp_dprintf(\"qp_load_image: fail (no free slot)\\n\");\n        return NULL;\n    }\n\n    if (!stream_factory(image, arg)) {\n        qp_dprintf(\"qp_load_image: fail (could not create stream)\\n\");\n        return NULL;\n    }\n\n    // Now that we know the length, validate the input data\n    if (!qgf_validate_stream(&image->stream)) {\n        qp_dprintf(\"qp_load_image: fail (failed validation)\\n\");\n        return NULL;\n    }\n\n    // Fill out the QP image descriptor\n    qgf_read_graphics_descriptor(&image->stream, &image->base.width, &image->base.height, &image->base.frame_count, NULL);\n\n    // Validation success, we can return the handle\n    image->validate_ok = true;\n    qp_dprintf(\"qp_load_image: ok\\n\");\n    return (painter_image_handle_t)image;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_load_image_mem\n\nstatic inline bool image_mem_stream_factory(qgf_image_handle_t *image, void *arg) {\n    void *buffer = arg;\n\n    // Assume we can read the graphics descriptor\n    image->mem_stream = qp_make_memory_stream((void *)buffer, sizeof(qgf_graphics_descriptor_v1_t));\n\n    // Update the length of the stream to match, and rewind to the start\n    image->mem_stream.length   = qgf_get_total_size(&image->stream);\n    image->mem_stream.position = 0;\n\n    return true;\n}\n\npainter_image_handle_t qp_load_image_mem(const void *buffer) {\n    return qp_load_image_internal(image_mem_stream_factory, (void *)buffer);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_close_image\n\nbool qp_close_image(painter_image_handle_t image) {\n    qgf_image_handle_t *qgf_image = (qgf_image_handle_t *)image;\n    if (!qgf_image || !qgf_image->validate_ok) {\n        qp_dprintf(\"qp_close_image: fail (invalid image)\\n\");\n        return false;\n    }\n\n    // Free up this image for use elsewhere.\n    qgf_image->validate_ok = false;\n    qp_stream_close(&qgf_image->stream);\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_drawimage\n\nbool qp_drawimage(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image) {\n    return qp_drawimage_recolor(device, x, y, image, 0, 0, 255, 0, 0, 0);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_drawimage_recolor\n\ntypedef struct qgf_frame_info_t {\n    painter_compression_t compression_scheme;\n    uint8_t               bpp;\n    bool                  has_palette;\n    bool                  is_panel_native;\n    bool                  is_delta;\n    uint16_t              left;\n    uint16_t              top;\n    uint16_t              right;\n    uint16_t              bottom;\n    uint16_t              delay;\n} qgf_frame_info_t;\n\nstatic bool qp_drawimage_prepare_frame_for_stream_read(painter_device_t device, qgf_image_handle_t *qgf_image, uint16_t frame_number, qp_pixel_t fg_hsv888, qp_pixel_t bg_hsv888, qgf_frame_info_t *info) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    // Drop out if we can't actually place the data we read out anywhere\n    if (!info) {\n        qp_dprintf(\"Failed to prepare stream for read, output info buffer unavailable\\n\");\n        return false;\n    }\n\n    // Seek to the frame\n    qgf_seek_to_frame_descriptor(&qgf_image->stream, frame_number);\n\n    // Read the frame descriptor\n    qgf_frame_v1_t frame_descriptor;\n    if (qp_stream_read(&frame_descriptor, sizeof(qgf_frame_v1_t), 1, &qgf_image->stream) != 1) {\n        qp_dprintf(\"Failed to read frame_descriptor, expected length was not %d\\n\", (int)sizeof(qgf_frame_v1_t));\n        return false;\n    }\n\n    // Parse out the frame info\n    if (!qgf_parse_frame_descriptor(&frame_descriptor, &info->bpp, &info->has_palette, &info->is_panel_native, &info->is_delta, &info->compression_scheme, &info->delay)) {\n        return false;\n    }\n\n    // Ensure we aren't reusing any palette\n    qp_internal_invalidate_palette();\n\n    if (!qp_internal_bpp_capable(info->bpp)) {\n        qp_dprintf(\"qp_drawimage_recolor: fail (image bpp too high (%d), check QUANTUM_PAINTER_SUPPORTS_256_PALETTE or QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS)\\n\", (int)info->bpp);\n        qp_comms_stop(device);\n        return false;\n    }\n\n    // Handle palette if needed\n    const uint16_t palette_entries  = 1u << info->bpp;\n    bool           needs_pixconvert = false;\n    if (info->has_palette) {\n        // Load the palette from the stream\n        if (!qp_internal_load_qgf_palette((qp_stream_t *)&qgf_image->stream, info->bpp)) {\n            return false;\n        }\n\n        needs_pixconvert = true;\n    } else {\n        if (info->bpp <= 8) {\n            // Interpolate from fg/bg\n            needs_pixconvert = qp_internal_interpolate_palette(fg_hsv888, bg_hsv888, palette_entries);\n        }\n    }\n\n    if (needs_pixconvert) {\n        // Convert the palette to native format\n        if (!driver->driver_vtable->palette_convert(device, palette_entries, qp_internal_global_pixel_lookup_table)) {\n            qp_dprintf(\"qp_drawimage_recolor: fail (could not convert pixels to native)\\n\");\n            qp_comms_stop(device);\n            return false;\n        }\n    }\n\n    // Handle delta if needed\n    if (info->is_delta) {\n        qgf_delta_v1_t delta_descriptor;\n        if (qp_stream_read(&delta_descriptor, sizeof(qgf_delta_v1_t), 1, &qgf_image->stream) != 1) {\n            qp_dprintf(\"Failed to read delta_descriptor, expected length was not %d\\n\", (int)sizeof(qgf_delta_v1_t));\n            return false;\n        }\n\n        info->left   = delta_descriptor.left;\n        info->top    = delta_descriptor.top;\n        info->right  = delta_descriptor.right;\n        info->bottom = delta_descriptor.bottom;\n    }\n\n    // Read the data block\n    qgf_data_v1_t data_descriptor;\n    if (qp_stream_read(&data_descriptor, sizeof(qgf_data_v1_t), 1, &qgf_image->stream) != 1) {\n        qp_dprintf(\"Failed to read data_descriptor, expected length was not %d\\n\", (int)sizeof(qgf_data_v1_t));\n        return false;\n    }\n\n    // Stream is now at the point of being able to read pixdata\n    return true;\n}\n\nstatic bool qp_drawimage_recolor_impl(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image, int frame_number, qgf_frame_info_t *frame_info, qp_pixel_t fg_hsv888, qp_pixel_t bg_hsv888) {\n    qp_dprintf(\"qp_drawimage_recolor: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_drawimage_recolor: fail (validation_ok == false)\\n\");\n        return false;\n    }\n\n    qgf_image_handle_t *qgf_image = (qgf_image_handle_t *)image;\n    if (!qgf_image || !qgf_image->validate_ok) {\n        qp_dprintf(\"qp_drawimage_recolor: fail (invalid image)\\n\");\n        return false;\n    }\n\n    // Read the frame info\n    if (!qp_drawimage_prepare_frame_for_stream_read(device, qgf_image, frame_number, fg_hsv888, bg_hsv888, frame_info)) {\n        qp_dprintf(\"qp_drawimage_recolor: fail (could not read frame %d)\\n\", frame_number);\n        return false;\n    }\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"qp_drawimage_recolor: fail (could not start comms)\\n\");\n        return false;\n    }\n\n    uint16_t l, t, r, b;\n    if (frame_info->is_delta) {\n        l = x + frame_info->left;\n        t = y + frame_info->top;\n        r = x + frame_info->right;\n        b = y + frame_info->bottom;\n    } else {\n        l = x;\n        t = y;\n        r = x + image->width - 1;\n        b = y + image->height - 1;\n    }\n    uint32_t pixel_count = ((uint32_t)(r - l + 1)) * (b - t + 1);\n\n    // Configure where we're going to be rendering to\n    if (!driver->driver_vtable->viewport(device, l, t, r, b)) {\n        qp_dprintf(\"qp_drawimage_recolor: fail (could not set viewport)\\n\");\n        qp_comms_stop(device);\n        return false;\n    }\n\n    // Set up the input state\n    qp_internal_byte_input_state_t  input_state    = {.device = device, .src_stream = &qgf_image->stream};\n    qp_internal_byte_input_callback input_callback = qp_internal_prepare_input_state(&input_state, frame_info->compression_scheme);\n    if (input_callback == NULL) {\n        qp_dprintf(\"qp_drawimage_recolor: fail (invalid image compression scheme)\\n\");\n        qp_comms_stop(device);\n        return false;\n    }\n\n    // Decode and stream pixels\n    bool ret = qp_internal_appender(device, frame_info->bpp, pixel_count, input_callback, &input_state);\n\n    qp_dprintf(\"qp_drawimage_recolor: %s\\n\", ret ? \"ok\" : \"fail\");\n    qp_comms_stop(device);\n    return ret;\n}\n\nbool qp_drawimage_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg) {\n    qgf_frame_info_t frame_info = {0};\n    qp_pixel_t       fg_hsv888  = {.hsv888 = {.h = hue_fg, .s = sat_fg, .v = val_fg}};\n    qp_pixel_t       bg_hsv888  = {.hsv888 = {.h = hue_bg, .s = sat_bg, .v = val_bg}};\n    return qp_drawimage_recolor_impl(device, x, y, image, 0, &frame_info, fg_hsv888, bg_hsv888);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_animate\n\ndeferred_token qp_animate(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image) {\n    return qp_animate_recolor(device, x, y, image, 0, 0, 255, 0, 0, 0);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_animate_recolor\n\ntypedef struct animation_state_t {\n    painter_device_t       device;\n    uint16_t               x;\n    uint16_t               y;\n    painter_image_handle_t image;\n    qp_pixel_t             fg_hsv888;\n    qp_pixel_t             bg_hsv888;\n    uint16_t               frame_number;\n    deferred_token         defer_token;\n} animation_state_t;\n\nstatic deferred_executor_t animation_executors[QUANTUM_PAINTER_CONCURRENT_ANIMATIONS] = {0};\nstatic animation_state_t   animation_states[QUANTUM_PAINTER_CONCURRENT_ANIMATIONS]    = {0};\n\nstatic deferred_token qp_render_animation_state(animation_state_t *state, uint16_t *delay_ms) {\n    qgf_frame_info_t frame_info = {0};\n    qp_dprintf(\"qp_render_animation_state: entry (frame #%d)\\n\", (int)state->frame_number);\n    bool ret = qp_drawimage_recolor_impl(state->device, state->x, state->y, state->image, state->frame_number, &frame_info, state->fg_hsv888, state->bg_hsv888);\n    if (ret) {\n        ++state->frame_number;\n        if (state->frame_number >= state->image->frame_count) {\n            state->frame_number = 0;\n        }\n        *delay_ms = frame_info.delay;\n    }\n    qp_dprintf(\"qp_render_animation_state: %s (delay %dms)\\n\", ret ? \"ok\" : \"fail\", (int)(*delay_ms));\n    return ret;\n}\n\nstatic uint32_t animation_callback(uint32_t trigger_time, void *cb_arg) {\n    animation_state_t *state    = (animation_state_t *)cb_arg;\n    uint16_t           delay_ms = 0;\n    bool               ret      = qp_render_animation_state(state, &delay_ms);\n    if (!ret) {\n        // Setting the device to NULL clears the animation slot\n        state->device = NULL;\n    }\n    // If we're successful, keep animating -- returning 0 cancels the deferred execution\n    return ret ? delay_ms : 0;\n}\n\ndeferred_token qp_animate_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_image_handle_t image, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg) {\n    qp_dprintf(\"qp_animate_recolor: entry\\n\");\n\n    animation_state_t *anim_state = NULL;\n    for (int i = 0; i < QUANTUM_PAINTER_CONCURRENT_ANIMATIONS; ++i) {\n        if (animation_states[i].device == NULL) {\n            anim_state = &animation_states[i];\n            break;\n        }\n    }\n\n    if (!anim_state) {\n        qp_dprintf(\"qp_animate_recolor: fail (could not find free animation slot)\\n\");\n        return INVALID_DEFERRED_TOKEN;\n    }\n\n    // Prepare the animation state\n    anim_state->device       = device;\n    anim_state->x            = x;\n    anim_state->y            = y;\n    anim_state->image        = image;\n    anim_state->fg_hsv888    = (qp_pixel_t){.hsv888 = {.h = hue_fg, .s = sat_fg, .v = val_fg}};\n    anim_state->bg_hsv888    = (qp_pixel_t){.hsv888 = {.h = hue_bg, .s = sat_bg, .v = val_bg}};\n    anim_state->frame_number = 0;\n\n    // Draw the first frame\n    uint16_t delay_ms;\n    if (!qp_render_animation_state(anim_state, &delay_ms)) {\n        anim_state->device = NULL; // disregard the allocated animation slot\n        qp_dprintf(\"qp_animate_recolor: fail (could not render first frame)\\n\");\n        return INVALID_DEFERRED_TOKEN;\n    }\n\n    // Set up the timer\n    anim_state->defer_token = defer_exec_advanced(animation_executors, QUANTUM_PAINTER_CONCURRENT_ANIMATIONS, delay_ms, animation_callback, anim_state);\n    if (anim_state->defer_token == INVALID_DEFERRED_TOKEN) {\n        anim_state->device = NULL; // disregard the allocated animation slot\n        qp_dprintf(\"qp_animate_recolor: fail (could not set up animation executor)\\n\");\n        return INVALID_DEFERRED_TOKEN;\n    }\n\n    qp_dprintf(\"qp_animate_recolor: ok (deferred token = %d)\\n\", (int)anim_state->defer_token);\n    return anim_state->defer_token;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_stop_animation\n\nvoid qp_stop_animation(deferred_token anim_token) {\n    for (int i = 0; i < QUANTUM_PAINTER_CONCURRENT_ANIMATIONS; ++i) {\n        if (animation_states[i].defer_token == anim_token) {\n            cancel_deferred_exec_advanced(animation_executors, QUANTUM_PAINTER_CONCURRENT_ANIMATIONS, anim_token);\n            animation_states[i].device = NULL;\n            return;\n        }\n    }\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter Core API: qp_internal_animation_tick\n\nvoid qp_internal_animation_tick(void) {\n    static uint32_t last_anim_exec = 0;\n    deferred_exec_advanced_task(animation_executors, QUANTUM_PAINTER_CONCURRENT_ANIMATIONS, &last_anim_exec);\n}\n"
  },
  {
    "path": "quantum/painter/qp_draw_text.c",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <quantum.h>\n#include <utf8.h>\n\n#include \"qp_internal.h\"\n#include \"qp_draw.h\"\n#include \"qp_comms.h\"\n#include \"qff.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// QFF font handles\n\ntypedef struct qff_font_handle_t {\n    painter_font_desc_t   base;\n    bool                  validate_ok;\n    bool                  has_ascii_table;\n    uint16_t              num_unicode_glyphs;\n    uint8_t               bpp;\n    bool                  has_palette;\n    bool                  is_panel_native;\n    painter_compression_t compression_scheme;\n    union {\n        qp_stream_t        stream;\n        qp_memory_stream_t mem_stream;\n#ifdef QP_STREAM_HAS_FILE_IO\n        qp_file_stream_t file_stream;\n#endif // QP_STREAM_HAS_FILE_IO\n    };\n#if QUANTUM_PAINTER_LOAD_FONTS_TO_RAM\n    bool  owns_buffer;\n    void *buffer;\n#endif // QUANTUM_PAINTER_LOAD_FONTS_TO_RAM\n} qff_font_handle_t;\n\nstatic qff_font_handle_t font_descriptors[QUANTUM_PAINTER_NUM_FONTS] = {0};\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Helper: load font from stream\n\nstatic painter_font_handle_t qp_load_font_internal(bool (*stream_factory)(qff_font_handle_t *font, void *arg), void *arg) {\n    qp_dprintf(\"qp_load_font: entry\\n\");\n    qff_font_handle_t *font = NULL;\n\n    // Find a free slot\n    for (int i = 0; i < QUANTUM_PAINTER_NUM_FONTS; ++i) {\n        if (!font_descriptors[i].validate_ok) {\n            font = &font_descriptors[i];\n            break;\n        }\n    }\n\n    // Drop out if not found\n    if (!font) {\n        qp_dprintf(\"qp_load_font: fail (no free slot)\\n\");\n        return NULL;\n    }\n\n    if (!stream_factory(font, arg)) {\n        qp_dprintf(\"qp_load_font: fail (could not create stream)\\n\");\n        return NULL;\n    }\n\n    // Now that we know the length, validate the input data\n    if (!qff_validate_stream(&font->stream)) {\n        qp_dprintf(\"qp_load_font: fail (failed validation)\\n\");\n        return NULL;\n    }\n\n#if QUANTUM_PAINTER_LOAD_FONTS_TO_RAM\n    // Clear out any existing data\n    font->owns_buffer = false;\n    font->buffer      = NULL;\n\n    void *ram_buffer = malloc(font->mem_stream.length);\n    if (ram_buffer == NULL) {\n        qp_dprintf(\"qp_load_font: could not allocate enough RAM for font, falling back to original\\n\");\n    } else {\n        do {\n            // Copy the data into RAM\n            if (qp_stream_read(ram_buffer, 1, font->mem_stream.length, &font->mem_stream) != font->mem_stream.length) {\n                qp_dprintf(\"qp_load_font: could not copy from flash to RAM, falling back to original\\n\");\n                break;\n            }\n\n            // Create the new stream with the new buffer\n            font->buffer      = ram_buffer;\n            font->owns_buffer = true;\n            font->mem_stream  = qp_make_memory_stream(font->buffer, font->mem_stream.length);\n        } while (0);\n    }\n\n    // Free the buffer if we were unable to recreate the RAM copy.\n    if (ram_buffer != NULL && !font->owns_buffer) {\n        free(ram_buffer);\n    }\n#endif // QUANTUM_PAINTER_LOAD_FONTS_TO_RAM\n\n    // Read the info (parsing already successful above, no need to check return value)\n    qff_read_font_descriptor(&font->stream, &font->base.line_height, &font->has_ascii_table, &font->num_unicode_glyphs, &font->bpp, &font->has_palette, &font->is_panel_native, &font->compression_scheme, NULL);\n\n    if (!qp_internal_bpp_capable(font->bpp)) {\n        qp_dprintf(\"qp_load_font: fail (image bpp too high (%d), check QUANTUM_PAINTER_SUPPORTS_256_PALETTE or QUANTUM_PAINTER_SUPPORTS_NATIVE_COLORS)\\n\", (int)font->bpp);\n        qp_close_font((painter_font_handle_t)font);\n        return NULL;\n    }\n\n    // Validation success, we can return the handle\n    font->validate_ok = true;\n    qp_dprintf(\"qp_load_font: ok\\n\");\n    return (painter_font_handle_t)font;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_load_font_mem\n\nstatic inline bool font_mem_stream_factory(qff_font_handle_t *font, void *arg) {\n    void *buffer = arg;\n\n    // Assume we can read the graphics descriptor\n    font->mem_stream = qp_make_memory_stream(buffer, sizeof(qff_font_descriptor_v1_t));\n\n    // Update the length of the stream to match, and rewind to the start\n    font->mem_stream.length   = qff_get_total_size(&font->stream);\n    font->mem_stream.position = 0;\n\n    return true;\n}\n\npainter_font_handle_t qp_load_font_mem(const void *buffer) {\n    return qp_load_font_internal(font_mem_stream_factory, (void *)buffer);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_close_font\n\nbool qp_close_font(painter_font_handle_t font) {\n    qff_font_handle_t *qff_font = (qff_font_handle_t *)font;\n    if (!qff_font || !qff_font->validate_ok) {\n        qp_dprintf(\"qp_close_font: fail (invalid font)\\n\");\n        return false;\n    }\n\n#if QUANTUM_PAINTER_LOAD_FONTS_TO_RAM\n    // Nuke the buffer, if required\n    if (qff_font->owns_buffer) {\n        free(qff_font->buffer);\n        qff_font->buffer      = NULL;\n        qff_font->owns_buffer = false;\n    }\n#endif // QUANTUM_PAINTER_LOAD_FONTS_TO_RAM\n\n    // Free up this font for use elsewhere.\n    qp_stream_close(&qff_font->stream);\n    qff_font->validate_ok = false;\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Helpers\n\n// Callback to be invoked for each codepoint detected in the UTF8 input string\ntypedef bool (*code_point_handler)(qff_font_handle_t *qff_font, uint32_t code_point, uint8_t width, uint8_t height, void *cb_arg);\n\n// Helper that sets up the palette (if required) and returns the offset in the stream that the data starts\nstatic inline bool qp_drawtext_prepare_font_for_render(painter_device_t device, qff_font_handle_t *qff_font, qp_pixel_t fg_hsv888, qp_pixel_t bg_hsv888, uint32_t *data_offset) {\n    painter_driver_t *driver = (painter_driver_t *)device;\n\n    // Drop out if we can't actually place the data we read out anywhere\n    if (!data_offset) {\n        qp_dprintf(\"Failed to prepare stream for read, output info buffer unavailable\\n\");\n        return false;\n    }\n\n    // Work out where we're reading from\n    uint32_t offset = sizeof(qff_font_descriptor_v1_t);\n    if (qff_font->has_ascii_table) {\n        offset += sizeof(qff_ascii_glyph_table_v1_t);\n    }\n    if (qff_font->num_unicode_glyphs > 0) {\n        offset += sizeof(qff_unicode_glyph_table_v1_t) + (qff_font->num_unicode_glyphs * 6);\n    }\n\n    // Handle palette if needed\n    const uint16_t palette_entries  = 1u << qff_font->bpp;\n    bool           needs_pixconvert = false;\n    if (qff_font->has_palette) {\n        // If this font has a palette, we need to read it out and set up the pixel lookup table\n        qp_stream_setpos(&qff_font->stream, offset);\n        if (!qp_internal_load_qgf_palette(&qff_font->stream, qff_font->bpp)) {\n            return false;\n        }\n\n        // Skip this block, as far as offset calculations go\n        offset += sizeof(qgf_palette_v1_t) + (palette_entries * 3);\n        needs_pixconvert = true;\n    } else {\n        // Interpolate from fg/bg\n        int16_t palette_entries = 1 << qff_font->bpp;\n        needs_pixconvert        = qp_internal_interpolate_palette(fg_hsv888, bg_hsv888, palette_entries);\n    }\n\n    if (needs_pixconvert) {\n        // Convert the palette to native format\n        if (!driver->driver_vtable->palette_convert(device, palette_entries, qp_internal_global_pixel_lookup_table)) {\n            qp_dprintf(\"qp_drawtext_recolor: fail (could not convert pixels to native)\\n\");\n            qp_comms_stop(device);\n            return false;\n        }\n    }\n\n    *data_offset = offset;\n    return true;\n}\n\nstatic inline bool qp_drawtext_prepare_glyph_for_render(qff_font_handle_t *qff_font, uint32_t code_point, uint8_t *width) {\n    if (code_point >= 0x20 && code_point < 0x7F && qff_font->has_ascii_table) {\n        // Do ascii table\n        qff_ascii_glyph_v1_t glyph_info;\n        uint32_t             glyph_info_offset = sizeof(qff_font_descriptor_v1_t)          // Skip the font descriptor\n                                     + sizeof(qgf_block_header_v1_t)                       // Skip the ascii table header\n                                     + (code_point - 0x20) * sizeof(qff_ascii_glyph_v1_t); // Jump direct to the data offset based on the glyph index\n        if (qp_stream_setpos(&qff_font->stream, glyph_info_offset) < 0) {\n            qp_dprintf(\"Failed to set stream position while reading ascii glyph info\\n\");\n            return false;\n        }\n\n        if (qp_stream_read(&glyph_info, sizeof(qff_ascii_glyph_v1_t), 1, &qff_font->stream) != 1) {\n            qp_dprintf(\"Failed to read glyph info\\n\");\n            return false;\n        }\n\n        uint8_t  glyph_width  = (uint8_t)(glyph_info.value & QFF_GLYPH_WIDTH_MASK);\n        uint32_t glyph_offset = ((glyph_info.value & QFF_GLYPH_OFFSET_MASK) >> QFF_GLYPH_WIDTH_BITS);\n        uint32_t data_offset  = sizeof(qff_font_descriptor_v1_t)                                                                                                                   // Skip the font descriptor\n                               + (qff_font->has_ascii_table ? sizeof(qff_ascii_glyph_table_v1_t) : 0)                                                                              // Skip the ascii table\n                               + (qff_font->num_unicode_glyphs > 0 ? (sizeof(qff_unicode_glyph_table_v1_t) + (qff_font->num_unicode_glyphs * sizeof(qff_unicode_glyph_v1_t))) : 0) // Skip the unicode table\n                               + (qff_font->has_palette ? (sizeof(qgf_palette_v1_t) + ((1 << qff_font->bpp) * sizeof(qgf_palette_entry_v1_t))) : 0)                                // Skip the palette\n                               + sizeof(qgf_block_header_v1_t)                                                                                                                     // Skip the data block header\n                               + glyph_offset;                                                                                                                                     // Jump to the specified glyph offset\n\n        if (qp_stream_setpos(&qff_font->stream, data_offset) < 0) {\n            qp_dprintf(\"Failed to set stream position while preparing ascii glyph data\\n\");\n            return false;\n        }\n\n        *width = glyph_width;\n        return true;\n    } else {\n        // Do unicode table, which may include singular ascii glyphs if full ascii table isn't specified\n        uint32_t glyph_info_offset = sizeof(qff_font_descriptor_v1_t)                                       // Skip the font descriptor\n                                     + (qff_font->has_ascii_table ? sizeof(qff_ascii_glyph_table_v1_t) : 0) // Skip the ascii table\n                                     + sizeof(qgf_block_header_v1_t);                                       // Skip the unicode block header\n\n        if (qp_stream_setpos(&qff_font->stream, glyph_info_offset) < 0) {\n            qp_dprintf(\"Failed to set stream position while preparing glyph data\\n\");\n            return false;\n        }\n\n        qff_unicode_glyph_v1_t glyph_info;\n        for (uint16_t i = 0; i < qff_font->num_unicode_glyphs; ++i) {\n            if (qp_stream_read(&glyph_info, sizeof(qff_unicode_glyph_v1_t), 1, &qff_font->stream) != 1) {\n                qp_dprintf(\"Failed to set stream position while reading unicode glyph info\\n\");\n                return false;\n            }\n\n            if (glyph_info.code_point == code_point) {\n                uint8_t  glyph_width  = (uint8_t)(glyph_info.value & QFF_GLYPH_WIDTH_MASK);\n                uint32_t glyph_offset = ((glyph_info.value & QFF_GLYPH_OFFSET_MASK) >> QFF_GLYPH_WIDTH_BITS);\n                uint32_t data_offset  = sizeof(qff_font_descriptor_v1_t)                                                                                                                   // Skip the font descriptor\n                                       + (qff_font->has_ascii_table ? sizeof(qff_ascii_glyph_table_v1_t) : 0)                                                                              // Skip the ascii table\n                                       + (qff_font->num_unicode_glyphs > 0 ? (sizeof(qff_unicode_glyph_table_v1_t) + (qff_font->num_unicode_glyphs * sizeof(qff_unicode_glyph_v1_t))) : 0) // Skip the unicode table\n                                       + (qff_font->has_palette ? (sizeof(qgf_palette_v1_t) + ((1 << qff_font->bpp) * sizeof(qgf_palette_entry_v1_t))) : 0)                                // Skip the palette\n                                       + sizeof(qgf_block_header_v1_t)                                                                                                                     // Skip the data block header\n                                       + glyph_offset;                                                                                                                                     // Jump to the specified glyph offset\n\n                if (qp_stream_setpos(&qff_font->stream, data_offset) < 0) {\n                    qp_dprintf(\"Failed to set stream position while preparing unicode glyph data\\n\");\n                    return false;\n                }\n\n                *width = glyph_width;\n                return true;\n            }\n        }\n\n        // Not found\n        qp_dprintf(\"Failed to find unicode glyph info\\n\");\n        return false;\n    }\n    return false;\n}\n\n// Function to iterate over each UTF8 codepoint, invoking the callback for each decoded glyph\nstatic inline bool qp_iterate_code_points(qff_font_handle_t *qff_font, const char *str, code_point_handler handler, void *cb_arg) {\n    while (*str) {\n        int32_t code_point = 0;\n        str                = decode_utf8(str, &code_point);\n        if (code_point < 0) {\n            qp_dprintf(\"Invalid unicode code point decoded. Cannot render.\\n\");\n            return false;\n        }\n\n        uint8_t width;\n        if (!qp_drawtext_prepare_glyph_for_render(qff_font, code_point, &width)) {\n            qp_dprintf(\"Failed to prepare glyph for rendering.\\n\");\n            return false;\n        }\n\n        if (!handler(qff_font, code_point, width, qff_font->base.line_height, cb_arg)) {\n            qp_dprintf(\"Failed to execute glyph handler.\\n\");\n            return false;\n        }\n    }\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// String width calculation\n\n// Callback state\ntypedef struct code_point_iter_calcwidth_state_t {\n    int16_t width;\n} code_point_iter_calcwidth_state_t;\n\n// Codepoint handler callback: width calc\nstatic inline bool qp_font_code_point_handler_calcwidth(qff_font_handle_t *qff_font, uint32_t code_point, uint8_t width, uint8_t height, void *cb_arg) {\n    code_point_iter_calcwidth_state_t *state = (code_point_iter_calcwidth_state_t *)cb_arg;\n\n    // Increment the overall width by this glyph's width\n    state->width += width;\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// String drawing implementation\n\n// Callback state\ntypedef struct code_point_iter_drawglyph_state_t {\n    painter_device_t                  device;\n    int16_t                           xpos;\n    int16_t                           ypos;\n    qp_internal_byte_input_callback   input_callback;\n    qp_internal_byte_input_state_t *  input_state;\n    qp_internal_pixel_output_state_t *output_state;\n} code_point_iter_drawglyph_state_t;\n\n// Codepoint handler callback: drawing\nstatic inline bool qp_font_code_point_handler_drawglyph(qff_font_handle_t *qff_font, uint32_t code_point, uint8_t width, uint8_t height, void *cb_arg) {\n    code_point_iter_drawglyph_state_t *state  = (code_point_iter_drawglyph_state_t *)cb_arg;\n    painter_driver_t *                 driver = (painter_driver_t *)state->device;\n\n    // Reset the input state's RLE mode -- the stream should already be correctly positioned by qp_iterate_code_points()\n    state->input_state->rle.mode = MARKER_BYTE; // ignored if not using RLE\n\n    // Reset the output state\n    state->output_state->pixel_write_pos = 0;\n\n    // Configure where we're going to be rendering to\n    driver->driver_vtable->viewport(state->device, state->xpos, state->ypos, state->xpos + width - 1, state->ypos + height - 1);\n\n    // Move the x-position for the next glyph\n    state->xpos += width;\n\n    // Decode the pixel data for the glyph, and stream it\n    uint32_t pixel_count = ((uint32_t)width) * height;\n    return qp_internal_appender(state->device, qff_font->bpp, pixel_count, state->input_callback, state->input_state);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_textwidth\n\nint16_t qp_textwidth(painter_font_handle_t font, const char *str) {\n    qff_font_handle_t *qff_font = (qff_font_handle_t *)font;\n    if (!qff_font || !qff_font->validate_ok) {\n        qp_dprintf(\"qp_textwidth: fail (invalid font)\\n\");\n        return false;\n    }\n\n    // Create the codepoint iterator state\n    code_point_iter_calcwidth_state_t state = {.width = 0};\n    // Iterate each codepoint, return the calculated width if successful.\n    return qp_iterate_code_points(qff_font, str, qp_font_code_point_handler_calcwidth, &state) ? state.width : 0;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_drawtext\n\nint16_t qp_drawtext(painter_device_t device, uint16_t x, uint16_t y, painter_font_handle_t font, const char *str) {\n    // Offload to the recolor variant, substituting fg=white bg=black.\n    // Traditional LCDs with those colors will need to manually invoke qp_drawtext_recolor with the colors reversed.\n    return qp_drawtext_recolor(device, x, y, font, str, 0, 0, 255, 0, 0, 0);\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter External API: qp_drawtext_recolor\n\nint16_t qp_drawtext_recolor(painter_device_t device, uint16_t x, uint16_t y, painter_font_handle_t font, const char *str, uint8_t hue_fg, uint8_t sat_fg, uint8_t val_fg, uint8_t hue_bg, uint8_t sat_bg, uint8_t val_bg) {\n    qp_dprintf(\"qp_drawtext_recolor: entry\\n\");\n    painter_driver_t *driver = (painter_driver_t *)device;\n    if (!driver || !driver->validate_ok) {\n        qp_dprintf(\"qp_drawtext_recolor: fail (validation_ok == false)\\n\");\n        return 0;\n    }\n\n    qff_font_handle_t *qff_font = (qff_font_handle_t *)font;\n    if (!qff_font || !qff_font->validate_ok) {\n        qp_dprintf(\"qp_drawtext_recolor: fail (invalid font)\\n\");\n        return false;\n    }\n\n    if (!qp_comms_start(device)) {\n        qp_dprintf(\"qp_drawtext_recolor: fail (could not start comms)\\n\");\n        return 0;\n    }\n\n    // Set up the byte input state and input callback\n    qp_internal_byte_input_state_t  input_state    = {.device = device, .src_stream = &qff_font->stream};\n    qp_internal_byte_input_callback input_callback = qp_internal_prepare_input_state(&input_state, qff_font->compression_scheme);\n    if (input_callback == NULL) {\n        qp_dprintf(\"qp_drawtext_recolor: fail (invalid font compression scheme)\\n\");\n        qp_comms_stop(device);\n        return false;\n    }\n\n    // Set up the pixel output state\n    qp_internal_pixel_output_state_t output_state = {.device = device, .pixel_write_pos = 0, .max_pixels = qp_internal_num_pixels_in_buffer(device)};\n\n    // Set up the codepoint iteration state\n    code_point_iter_drawglyph_state_t state = {// Common\n                                               .device = device,\n                                               .xpos   = x,\n                                               .ypos   = y,\n                                               // Input\n                                               .input_callback = input_callback,\n                                               .input_state    = &input_state,\n                                               // Output\n                                               .output_state = &output_state};\n\n    qp_pixel_t fg_hsv888 = {.hsv888 = {.h = hue_fg, .s = sat_fg, .v = val_fg}};\n    qp_pixel_t bg_hsv888 = {.hsv888 = {.h = hue_bg, .s = sat_bg, .v = val_bg}};\n    uint32_t   data_offset;\n    if (!qp_drawtext_prepare_font_for_render(driver, qff_font, fg_hsv888, bg_hsv888, &data_offset)) {\n        qp_dprintf(\"qp_drawtext_recolor: fail (failed to prepare font for rendering)\\n\");\n        qp_comms_stop(device);\n        return false;\n    }\n\n    // Iterate the codepoints with the drawglyph callback\n    bool ret = qp_iterate_code_points(qff_font, str, qp_font_code_point_handler_drawglyph, &state);\n\n    qp_dprintf(\"qp_drawtext_recolor: %s\\n\", ret ? \"ok\" : \"fail\");\n    qp_comms_stop(device);\n    return ret ? (state.xpos - x) : 0;\n}\n"
  },
  {
    "path": "quantum/painter/qp_internal.c",
    "content": "// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_internal.h\"\n\n#include \"compiler_support.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter Core API: device registration\n\nenum {\n    // Work out how many devices we're actually going to be instantiating\n    // NOTE: We intentionally do not include surfaces here, despite them conforming to the same API.\n    QP_NUM_DEVICES = (ILI9163_NUM_DEVICES)   // ILI9163\n                     + (ILI9341_NUM_DEVICES) // ILI9341\n                     + (ILI9486_NUM_DEVICES) // ILI9486\n                     + (ILI9488_NUM_DEVICES) // ILI9488\n                     + (ST7789_NUM_DEVICES)  // ST7789\n                     + (ST7735_NUM_DEVICES)  // ST7735\n                     + (GC9A01_NUM_DEVICES)  // GC9A01\n                     + (GC9107_NUM_DEVICES)  // GC9107\n                     + (SSD1351_NUM_DEVICES) // SSD1351\n                     + (SH1106_NUM_DEVICES)  // SH1106\n                     + (SH1107_NUM_DEVICES)  // SH1107\n                     + (LD7032_NUM_DEVICES)  // LD7032\n};\n\nstatic painter_device_t qp_devices[QP_NUM_DEVICES] = {NULL};\n\nbool qp_internal_register_device(painter_device_t driver) {\n    for (uint8_t i = 0; i < QP_NUM_DEVICES; i++) {\n        if (qp_devices[i] == NULL) {\n            qp_devices[i] = driver;\n            return true;\n        }\n    }\n\n    // We should never get here -- someone has screwed up their device counts during config\n    qp_dprintf(\"qp_internal_register_device: no more space for devices!\\n\");\n    return false;\n}\n\n#if (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0\nstatic void qp_internal_display_timeout_task(void) {\n    // Handle power on/off state\n    static bool display_on                  = true;\n    bool        should_change_display_state = false;\n    bool        target_display_state        = false;\n    if (last_input_activity_elapsed() < (QUANTUM_PAINTER_DISPLAY_TIMEOUT)) {\n        should_change_display_state = display_on == false;\n        target_display_state        = true;\n    } else {\n        should_change_display_state = display_on == true;\n        target_display_state        = false;\n    }\n\n    if (should_change_display_state) {\n        for (uint8_t i = 0; i < QP_NUM_DEVICES; i++) {\n            if (qp_devices[i] != NULL) {\n                qp_power(qp_devices[i], target_display_state);\n            }\n        }\n\n        display_on = target_display_state;\n    }\n}\n#endif // (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter Core API: qp_internal_task\n\nSTATIC_ASSERT((QUANTUM_PAINTER_TASK_THROTTLE) > 0 && (QUANTUM_PAINTER_TASK_THROTTLE) < 1000, \"QUANTUM_PAINTER_TASK_THROTTLE must be between 1 and 999\");\n\nvoid qp_internal_task(void) {\n    // Perform throttling of the internal processing of Quantum Painter\n    static uint32_t last_tick = 0;\n    uint32_t        now       = timer_read32();\n    if (TIMER_DIFF_32(now, last_tick) < (QUANTUM_PAINTER_TASK_THROTTLE)) {\n        return;\n    }\n    last_tick = now;\n\n#if (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0\n    qp_internal_display_timeout_task();\n#endif // (QUANTUM_PAINTER_DISPLAY_TIMEOUT) > 0\n\n    // Handle animations\n    void qp_internal_animation_tick(void);\n    qp_internal_animation_tick();\n\n#ifdef QUANTUM_PAINTER_LVGL_INTEGRATION_ENABLE\n    // Run LVGL ticks\n    void qp_lvgl_internal_tick(void);\n    qp_lvgl_internal_tick();\n#endif\n\n    // Flush (render) dirty regions to corresponding displays\n#if !defined(QUANTUM_PAINTER_DEBUG_ENABLE_FLUSH_TASK_OUTPUT)\n    bool old_debug_state = debug_enable;\n    debug_enable         = false;\n#endif // defined(QUANTUM_PAINTER_DEBUG_ENABLE_FLUSH_TASK_OUTPUT)\n    for (uint8_t i = 0; i < QP_NUM_DEVICES; i++) {\n        if (qp_devices[i] != NULL) {\n            qp_flush(qp_devices[i]);\n        }\n    }\n#if !defined(QUANTUM_PAINTER_DEBUG_ENABLE_FLUSH_TASK_OUTPUT)\n    debug_enable = old_debug_state;\n#endif // defined(QUANTUM_PAINTER_DEBUG_ENABLE_FLUSH_TASK_OUTPUT)\n}\n"
  },
  {
    "path": "quantum/painter/qp_internal.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"quantum.h\"\n#include \"qp.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Helpers\n\n// Mark certain types that there should be no padding bytes between members.\n#define QP_PACKED __attribute__((packed))\n\n// Min/max defines\n#define QP_MIN(X, Y) (((X) < (Y)) ? (X) : (Y))\n#define QP_MAX(X, Y) (((X) > (Y)) ? (X) : (Y))\n\n#ifdef QUANTUM_PAINTER_DEBUG\n#    include <debug.h>\n#    include <print.h>\n#    define qp_dprintf(...) dprintf(__VA_ARGS__)\n#else\n#    define qp_dprintf(...) \\\n        do {                \\\n        } while (0)\n#endif\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Specific internal definitions\n\n#include <qp_internal_formats.h>\n#include <qp_internal_driver.h>\n"
  },
  {
    "path": "quantum/painter/qp_internal_driver.h",
    "content": "// Copyright 2021-2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver callbacks\n\ntypedef bool (*painter_driver_init_func)(painter_device_t device, painter_rotation_t rotation);\ntypedef bool (*painter_driver_power_func)(painter_device_t device, bool power_on);\ntypedef bool (*painter_driver_clear_func)(painter_device_t device);\ntypedef bool (*painter_driver_flush_func)(painter_device_t device);\ntypedef bool (*painter_driver_viewport_func)(painter_device_t device, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom);\ntypedef bool (*painter_driver_pixdata_func)(painter_device_t device, const void *pixel_data, uint32_t native_pixel_count);\ntypedef bool (*painter_driver_convert_palette_func)(painter_device_t device, int16_t palette_size, qp_pixel_t *palette);\ntypedef bool (*painter_driver_append_pixels)(painter_device_t device, uint8_t *target_buffer, qp_pixel_t *palette, uint32_t pixel_offset, uint32_t pixel_count, uint8_t *palette_indices);\ntypedef bool (*painter_driver_append_pixdata)(painter_device_t device, uint8_t *target_buffer, uint32_t pixdata_offset, uint8_t pixdata_byte);\n\n// Driver vtable definition\ntypedef struct painter_driver_vtable_t {\n    painter_driver_init_func            init;\n    painter_driver_power_func           power;\n    painter_driver_clear_func           clear;\n    painter_driver_flush_func           flush;\n    painter_driver_viewport_func        viewport;\n    painter_driver_pixdata_func         pixdata;\n    painter_driver_convert_palette_func palette_convert;\n    painter_driver_append_pixels        append_pixels;\n    painter_driver_append_pixdata       append_pixdata;\n} painter_driver_vtable_t;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Comms callbacks\n\ntypedef bool (*painter_driver_comms_init_func)(painter_device_t device);\ntypedef bool (*painter_driver_comms_start_func)(painter_device_t device);\ntypedef void (*painter_driver_comms_stop_func)(painter_device_t device);\ntypedef uint32_t (*painter_driver_comms_send_func)(painter_device_t device, const void *data, uint32_t byte_count);\n\ntypedef struct painter_comms_vtable_t {\n    painter_driver_comms_init_func  comms_init;\n    painter_driver_comms_start_func comms_start;\n    painter_driver_comms_stop_func  comms_stop;\n    painter_driver_comms_send_func  comms_send;\n} painter_comms_vtable_t;\n\ntypedef void (*painter_driver_comms_send_command_func)(painter_device_t device, uint8_t cmd);\ntypedef void (*painter_driver_comms_bulk_command_sequence)(painter_device_t device, const uint8_t *sequence, size_t sequence_len);\n\ntypedef struct painter_comms_with_command_vtable_t {\n    painter_comms_vtable_t                     base; // must be first, so this object can be cast from the painter_comms_vtable_t* type\n    painter_driver_comms_send_command_func     send_command;\n    painter_driver_comms_bulk_command_sequence bulk_command_sequence;\n} painter_comms_with_command_vtable_t;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Driver base definition\n\ntypedef struct painter_driver_t {\n    const painter_driver_vtable_t *driver_vtable;\n    const painter_comms_vtable_t * comms_vtable;\n\n    // Flag signifying if validation was successful\n    bool validate_ok;\n\n    // Panel geometry\n    uint16_t panel_width;\n    uint16_t panel_height;\n\n    // Target drawing rotation\n    painter_rotation_t rotation;\n\n    // Automated offsets for setting viewport\n    uint16_t offset_x;\n    uint16_t offset_y;\n\n    // Number of bits per pixel, used for determining how many pixels can be sent during a transmission of the pixdata buffer\n    uint8_t native_bits_per_pixel;\n\n    // Comms config pointer -- needs to point to an appropriate comms config if the comms driver requires it.\n    void *comms_config;\n} painter_driver_t;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Device internals\n\nbool qp_internal_register_device(painter_device_t driver);\n"
  },
  {
    "path": "quantum/painter/qp_internal_formats.h",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"compiler_support.h\"\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter pixel formats\n\n// Datatype containing a pixel's color. The internal member used is dependent on the external context.\ntypedef union QP_PACKED qp_pixel_t {\n    uint8_t mono;\n    uint8_t palette_idx;\n\n    struct QP_PACKED {\n        uint8_t h;\n        uint8_t s;\n        uint8_t v;\n    } hsv888;\n\n    struct QP_PACKED {\n        uint8_t r;\n        uint8_t g;\n        uint8_t b;\n    } rgb888;\n\n    uint16_t rgb565;\n\n    uint32_t dummy;\n} qp_pixel_t;\nSTATIC_ASSERT(sizeof(qp_pixel_t) == 4, \"Invalid size for qp_pixel_t\");\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Quantum Painter image format\n\ntypedef enum qp_image_format_t {\n    // Pixel formats available in the QGF frame format\n    GRAYSCALE_1BPP = 0x00,\n    GRAYSCALE_2BPP = 0x01,\n    GRAYSCALE_4BPP = 0x02,\n    GRAYSCALE_8BPP = 0x03,\n    PALETTE_1BPP   = 0x04,\n    PALETTE_2BPP   = 0x05,\n    PALETTE_4BPP   = 0x06,\n    PALETTE_8BPP   = 0x07,\n    RGB565_16BPP   = 0x08, // Natively streamed to the panel, no interpolation or palette handling\n    RGB888_24BPP   = 0x09, // Natively streamed to the panel, no interpolation or palette handling\n} qp_image_format_t;\n\ntypedef enum painter_compression_t { IMAGE_UNCOMPRESSED, IMAGE_COMPRESSED_RLE } painter_compression_t;\n"
  },
  {
    "path": "quantum/painter/qp_stream.c",
    "content": "// Copyright 2021 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"qp_stream.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Stream API\n\nuint32_t qp_stream_read_impl(void *output_buf, uint32_t member_size, uint32_t num_members, qp_stream_t *stream) {\n    uint8_t *output_ptr = (uint8_t *)output_buf;\n\n    uint32_t i;\n    for (i = 0; i < (num_members * member_size); ++i) {\n        int16_t c = qp_stream_get(stream);\n        if (c < 0) {\n            break;\n        }\n\n        output_ptr[i] = (uint8_t)(c & 0xFF);\n    }\n\n    return i / member_size;\n}\n\nuint32_t qp_stream_write_impl(const void *input_buf, uint32_t member_size, uint32_t num_members, qp_stream_t *stream) {\n    uint8_t *input_ptr = (uint8_t *)input_buf;\n\n    uint32_t i;\n    for (i = 0; i < (num_members * member_size); ++i) {\n        if (!qp_stream_put(stream, input_ptr[i])) {\n            break;\n        }\n    }\n\n    return i / member_size;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Memory streams\n\nstatic inline int16_t mem_get(qp_stream_t *stream) {\n    qp_memory_stream_t *s = (qp_memory_stream_t *)stream;\n    if (s->position >= s->length) {\n        s->is_eof = true;\n        return STREAM_EOF;\n    }\n    return s->buffer[s->position++];\n}\n\nstatic inline bool mem_put(qp_stream_t *stream, uint8_t c) {\n    qp_memory_stream_t *s = (qp_memory_stream_t *)stream;\n    if (s->position >= s->length) {\n        s->is_eof = true;\n        return false;\n    }\n    s->buffer[s->position++] = c;\n    return true;\n}\n\nstatic inline int mem_seek(qp_stream_t *stream, int32_t offset, int origin) {\n    qp_memory_stream_t *s = (qp_memory_stream_t *)stream;\n\n    // Handle as per fseek\n    int32_t position = s->position;\n    switch (origin) {\n        case SEEK_SET:\n            position = offset;\n            break;\n        case SEEK_CUR:\n            position += offset;\n            break;\n        case SEEK_END:\n            position = s->length + offset;\n            break;\n        default:\n            return -1;\n    }\n\n    // If we're before the start, ignore it.\n    if (position < 0) {\n        return -1;\n    }\n\n    // If we're at the end it's okay, we only care if we're after the end for failure purposes -- as per lseek()\n    if (position > s->length) {\n        return -1;\n    }\n\n    // Update the offset\n    s->position = position;\n\n    // Successful invocation of fseek() results in clearing of the EOF flag by default, mirror the same functionality\n    s->is_eof = false;\n\n    return 0;\n}\n\nstatic inline int32_t mem_tell(qp_stream_t *stream) {\n    qp_memory_stream_t *s = (qp_memory_stream_t *)stream;\n    return s->position;\n}\n\nstatic inline bool mem_is_eof(qp_stream_t *stream) {\n    qp_memory_stream_t *s = (qp_memory_stream_t *)stream;\n    return s->is_eof;\n}\n\nstatic inline void mem_close(qp_stream_t *stream) {\n    // No-op.\n}\n\nqp_memory_stream_t qp_make_memory_stream(void *buffer, int32_t length) {\n    qp_memory_stream_t stream = {\n        .base     = {.get = mem_get, .put = mem_put, .seek = mem_seek, .tell = mem_tell, .is_eof = mem_is_eof, .close = mem_close},\n        .buffer   = (uint8_t *)buffer,\n        .length   = length,\n        .position = 0,\n    };\n    return stream;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// FILE streams\n\n#ifdef QP_STREAM_HAS_FILE_IO\n\nstatic inline int16_t file_get(qp_stream_t *stream) {\n    qp_file_stream_t *s = (qp_file_stream_t *)stream;\n    int               c = fgetc(s->file);\n    if (c < 0 || feof(s->file)) return STREAM_EOF;\n    return (uint16_t)c;\n}\n\nstatic inline bool file_put(qp_stream_t *stream, uint8_t c) {\n    qp_file_stream_t *s = (qp_file_stream_t *)stream;\n    return fputc(c, s->file) == c;\n}\n\nstatic inline int file_seek(qp_stream_t *stream, int32_t offset, int origin) {\n    qp_file_stream_t *s = (qp_file_stream_t *)stream;\n    return fseek(s->file, offset, origin);\n}\n\nstatic inline int32_t file_tell(qp_stream_t *stream) {\n    qp_file_stream_t *s = (qp_file_stream_t *)stream;\n    return (int32_t)ftell(s->file);\n}\n\nstatic inline bool file_is_eof(qp_stream_t *stream) {\n    qp_file_stream_t *s = (qp_file_stream_t *)stream;\n    return (bool)feof(s->file);\n}\n\nstatic inline void file_close(qp_stream_t *stream) {\n    qp_file_stream_t *s = (qp_file_stream_t *)stream;\n    fclose(s->file);\n}\n\nqp_file_stream_t qp_make_file_stream(FILE *f) {\n    qp_file_stream_t stream = {\n        .base = {.get = file_get, .put = file_put, .seek = file_seek, .tell = file_tell, .is_eof = file_is_eof, .close = file_close},\n        .file = f,\n    };\n    return stream;\n}\n#endif // QP_STREAM_HAS_FILE_IO\n"
  },
  {
    "path": "quantum/painter/qp_stream.h",
    "content": "/* Copyright 2021 Nick Brassel (@tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdio.h>\n\n#include \"qp_internal.h\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Stream API\n\ntypedef struct qp_stream_t qp_stream_t;\n\n#define qp_stream_get(stream_ptr) (((qp_stream_t *)(stream_ptr))->get((qp_stream_t *)(stream_ptr)))\n#define qp_stream_put(stream_ptr, c) (((qp_stream_t *)(stream_ptr))->put((qp_stream_t *)(stream_ptr), (c)))\n#define qp_stream_seek(stream_ptr, offset, origin) (((qp_stream_t *)(stream_ptr))->seek((qp_stream_t *)(stream_ptr), (offset), (origin)))\n#define qp_stream_tell(stream_ptr) (((qp_stream_t *)(stream_ptr))->tell((qp_stream_t *)(stream_ptr)))\n#define qp_stream_eof(stream_ptr) (((qp_stream_t *)(stream_ptr))->is_eof((qp_stream_t *)(stream_ptr)))\n#define qp_stream_setpos(stream_ptr, offset) qp_stream_seek((stream_ptr), (offset), SEEK_SET)\n#define qp_stream_getpos(stream_ptr) qp_stream_tell((stream_ptr))\n#define qp_stream_read(output_buf, member_size, num_members, stream_ptr) qp_stream_read_impl((output_buf), (member_size), (num_members), (qp_stream_t *)(stream_ptr))\n#define qp_stream_write(input_buf, member_size, num_members, stream_ptr) qp_stream_write_impl((input_buf), (member_size), (num_members), (qp_stream_t *)(stream_ptr))\n\nuint32_t qp_stream_read_impl(void *output_buf, uint32_t member_size, uint32_t num_members, qp_stream_t *stream);\nuint32_t qp_stream_write_impl(const void *input_buf, uint32_t member_size, uint32_t num_members, qp_stream_t *stream);\n\n#define qp_stream_close(stream_ptr) (((qp_stream_t *)(stream_ptr))->close((qp_stream_t *)(stream_ptr)))\n\n#define STREAM_EOF ((int16_t)(-1))\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Stream definition\n\ntypedef struct qp_stream_t {\n    int16_t (*get)(qp_stream_t *stream);\n    bool (*put)(qp_stream_t *stream, uint8_t c);\n    int (*seek)(qp_stream_t *stream, int32_t offset, int origin);\n    int32_t (*tell)(qp_stream_t *stream);\n    bool (*is_eof)(qp_stream_t *stream);\n    void (*close)(qp_stream_t *stream);\n} qp_stream_t;\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Memory streams\n\ntypedef struct qp_memory_stream_t {\n    qp_stream_t base;\n    uint8_t *   buffer;\n    int32_t     length;\n    int32_t     position;\n    bool        is_eof;\n} qp_memory_stream_t;\n\nqp_memory_stream_t qp_make_memory_stream(void *buffer, int32_t length);\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// FILE streams\n\n#ifdef QP_STREAM_HAS_FILE_IO\n\ntypedef struct qp_file_stream_t {\n    qp_stream_t base;\n    FILE *      file;\n} qp_file_stream_t;\n\nqp_file_stream_t qp_make_file_stream(FILE *f);\n\n#endif // QP_STREAM_HAS_FILE_IO\n"
  },
  {
    "path": "quantum/painter/rules.mk",
    "content": "# Quantum Painter Configurables\nQUANTUM_PAINTER_DRIVERS ?=\nQUANTUM_PAINTER_ANIMATIONS_ENABLE ?= yes\n\nQUANTUM_PAINTER_LVGL_INTEGRATION ?= no\n\n# The list of permissible drivers that can be listed in QUANTUM_PAINTER_DRIVERS\nVALID_QUANTUM_PAINTER_DRIVERS := \\\n    surface \\\n    ili9163_spi \\\n    ili9341_spi \\\n    ili9486_spi \\\n    ili9488_spi \\\n    st7735_spi \\\n    st7789_spi \\\n    gc9a01_spi \\\n    gc9107_spi \\\n    ssd1351_spi \\\n    sh1106_i2c \\\n    sh1106_spi \\\n    sh1107_i2c \\\n    sh1107_spi \\\n    ld7032_i2c \\\n    ld7032_spi\n\n#-------------------------------------------------------------------------------\n\nOPT_DEFS += -DQUANTUM_PAINTER_ENABLE\nCOMMON_VPATH += $(QUANTUM_DIR)/painter \\\n                $(QUANTUM_DIR)/unicode\nSRC += \\\n    $(QUANTUM_DIR)/unicode/utf8.c \\\n    $(QUANTUM_DIR)/color.c \\\n    $(QUANTUM_DIR)/painter/qp.c \\\n    $(QUANTUM_DIR)/painter/qp_internal.c \\\n    $(QUANTUM_DIR)/painter/qp_stream.c \\\n    $(QUANTUM_DIR)/painter/qgf.c \\\n    $(QUANTUM_DIR)/painter/qff.c \\\n    $(QUANTUM_DIR)/painter/qp_draw_core.c \\\n    $(QUANTUM_DIR)/painter/qp_draw_codec.c \\\n    $(QUANTUM_DIR)/painter/qp_draw_circle.c \\\n    $(QUANTUM_DIR)/painter/qp_draw_ellipse.c \\\n    $(QUANTUM_DIR)/painter/qp_draw_image.c \\\n    $(QUANTUM_DIR)/painter/qp_draw_text.c\n\n# Check if people want animations... enable the defered exec if so.\nifeq ($(strip $(QUANTUM_PAINTER_ANIMATIONS_ENABLE)), yes)\n    DEFERRED_EXEC_ENABLE := yes\n    OPT_DEFS += -DQUANTUM_PAINTER_ANIMATIONS_ENABLE\nendif\n\n# Comms flags\nQUANTUM_PAINTER_NEEDS_COMMS_DUMMY ?= no\nQUANTUM_PAINTER_NEEDS_COMMS_SPI ?= no\nQUANTUM_PAINTER_NEEDS_COMMS_I2C ?= no\n\n# Handler for each driver\ndefine handle_quantum_painter_driver\n    CURRENT_PAINTER_DRIVER := $1\n\n    ifeq ($$(filter $$(strip $$(CURRENT_PAINTER_DRIVER)),$$(VALID_QUANTUM_PAINTER_DRIVERS)),)\n        $$(error \"$$(CURRENT_PAINTER_DRIVER)\" is not a valid Quantum Painter driver)\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),surface)\n        QUANTUM_PAINTER_NEEDS_SURFACE := yes\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9163_spi)\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_ILI9163_ENABLE -DQUANTUM_PAINTER_ILI9163_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/tft_panel \\\n            $(DRIVER_PATH)/painter/ili9xxx\n        SRC += \\\n            $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \\\n            $(DRIVER_PATH)/painter/ili9xxx/qp_ili9163.c \\\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9341_spi)\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_ILI9341_ENABLE -DQUANTUM_PAINTER_ILI9341_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/tft_panel \\\n            $(DRIVER_PATH)/painter/ili9xxx\n        SRC += \\\n            $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \\\n            $(DRIVER_PATH)/painter/ili9xxx/qp_ili9341.c \\\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9486_spi)\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_ILI9486_ENABLE -DQUANTUM_PAINTER_ILI9486_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/tft_panel \\\n            $(DRIVER_PATH)/painter/ili9xxx\n        SRC += \\\n            $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \\\n            $(DRIVER_PATH)/painter/ili9xxx/qp_ili9486.c \\\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ili9488_spi)\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_ILI9488_ENABLE -DQUANTUM_PAINTER_ILI9488_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/tft_panel \\\n            $(DRIVER_PATH)/painter/ili9xxx\n        SRC += \\\n            $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \\\n            $(DRIVER_PATH)/painter/ili9xxx/qp_ili9488.c \\\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),st7735_spi)\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_ST7735_ENABLE -DQUANTUM_PAINTER_ST7735_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/tft_panel \\\n            $(DRIVER_PATH)/painter/st77xx\n        SRC += \\\n            $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \\\n            $(DRIVER_PATH)/painter/st77xx/qp_st7735.c\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),st7789_spi)\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_ST7789_ENABLE -DQUANTUM_PAINTER_ST7789_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/tft_panel \\\n            $(DRIVER_PATH)/painter/st77xx\n        SRC += \\\n            $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \\\n            $(DRIVER_PATH)/painter/st77xx/qp_st7789.c\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),gc9a01_spi)\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_GC9A01_ENABLE -DQUANTUM_PAINTER_GC9A01_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/tft_panel \\\n            $(DRIVER_PATH)/painter/gc9xxx\n        SRC += \\\n            $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \\\n            $(DRIVER_PATH)/painter/gc9xxx/qp_gc9a01.c\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),gc9107_spi)\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_GC9107_ENABLE -DQUANTUM_PAINTER_GC9107_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/tft_panel \\\n            $(DRIVER_PATH)/painter/gc9xxx\n        SRC += \\\n            $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \\\n            $(DRIVER_PATH)/painter/gc9xxx/qp_gc9107.c\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ssd1351_spi)\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_SSD1351_ENABLE -DQUANTUM_PAINTER_SSD1351_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/tft_panel \\\n            $(DRIVER_PATH)/painter/ssd1351\n        SRC += \\\n            $(DRIVER_PATH)/painter/tft_panel/qp_tft_panel.c \\\n            $(DRIVER_PATH)/painter/ssd1351/qp_ssd1351.c\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),sh1106_spi)\n        QUANTUM_PAINTER_NEEDS_SURFACE := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_SH1106_ENABLE -DQUANTUM_PAINTER_SH1106_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/oled_panel \\\n            $(DRIVER_PATH)/painter/sh1106\n        SRC += \\\n            $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \\\n            $(DRIVER_PATH)/painter/sh1106/qp_sh1106.c\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),sh1106_i2c)\n        QUANTUM_PAINTER_NEEDS_SURFACE := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_I2C := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_SH1106_ENABLE -DQUANTUM_PAINTER_SH1106_I2C_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/oled_panel \\\n            $(DRIVER_PATH)/painter/sh1106\n        SRC += \\\n            $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \\\n            $(DRIVER_PATH)/painter/sh1106/qp_sh1106.c\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),sh1107_spi)\n        QUANTUM_PAINTER_NEEDS_SURFACE := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_SH1107_ENABLE -DQUANTUM_PAINTER_SH1107_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/oled_panel \\\n            $(DRIVER_PATH)/painter/sh1107\n        SRC += \\\n            $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \\\n            $(DRIVER_PATH)/painter/sh1107/qp_sh1107.c\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),sh1107_i2c)\n        QUANTUM_PAINTER_NEEDS_SURFACE := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_I2C := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_SH1107_ENABLE -DQUANTUM_PAINTER_SH1107_I2C_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/oled_panel \\\n            $(DRIVER_PATH)/painter/sh1107\n        SRC += \\\n            $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \\\n            $(DRIVER_PATH)/painter/sh1107/qp_sh1107.c\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ld7032_spi)\n        QUANTUM_PAINTER_NEEDS_SURFACE := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_LD7032_ENABLE -DQUANTUM_PAINTER_LD7032_SPI_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/oled_panel \\\n            $(DRIVER_PATH)/painter/ld7032\n        SRC += \\\n            $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \\\n            $(DRIVER_PATH)/painter/ld7032/qp_ld7032.c\n\n    else ifeq ($$(strip $$(CURRENT_PAINTER_DRIVER)),ld7032_i2c)\n        QUANTUM_PAINTER_NEEDS_SURFACE := yes\n        QUANTUM_PAINTER_NEEDS_COMMS_I2C := yes\n        OPT_DEFS += -DQUANTUM_PAINTER_LD7032_ENABLE -DQUANTUM_PAINTER_LD7032_I2C_ENABLE\n        COMMON_VPATH += \\\n            $(DRIVER_PATH)/painter/oled_panel \\\n            $(DRIVER_PATH)/painter/ld7032\n        SRC += \\\n            $(DRIVER_PATH)/painter/oled_panel/qp_oled_panel.c \\\n            $(DRIVER_PATH)/painter/ld7032/qp_ld7032.c\n\n    endif\nendef\n\n# Iterate through the listed drivers for the build, including what's necessary\n$(foreach qp_driver,$(QUANTUM_PAINTER_DRIVERS),$(eval $(call handle_quantum_painter_driver,$(qp_driver))))\n\n# If a surface is needed, set up the required files\nifeq ($(strip $(QUANTUM_PAINTER_NEEDS_SURFACE)), yes)\n    QUANTUM_PAINTER_NEEDS_COMMS_DUMMY := yes\n    OPT_DEFS += -DQUANTUM_PAINTER_SURFACE_ENABLE\n    COMMON_VPATH += \\\n        $(DRIVER_PATH)/painter/generic\n    SRC += \\\n        $(DRIVER_PATH)/painter/generic/qp_surface_common.c \\\n        $(DRIVER_PATH)/painter/generic/qp_surface_mono1bpp.c \\\n        $(DRIVER_PATH)/painter/generic/qp_surface_rgb565.c\nendif\n\n# If dummy comms is needed, set up the required files\nifeq ($(strip $(QUANTUM_PAINTER_NEEDS_COMMS_DUMMY)), yes)\n    OPT_DEFS += -DQUANTUM_PAINTER_DUMMY_COMMS_ENABLE\n    VPATH += $(DRIVER_PATH)/painter/comms\n    SRC += \\\n        $(QUANTUM_DIR)/painter/qp_comms.c \\\n        $(DRIVER_PATH)/painter/comms/qp_comms_dummy.c\nendif\n\n# If SPI comms is needed, set up the required files\nifeq ($(strip $(QUANTUM_PAINTER_NEEDS_COMMS_SPI)), yes)\n    OPT_DEFS += -DQUANTUM_PAINTER_SPI_ENABLE\n    SPI_DRIVER_REQUIRED = yes\n    VPATH += $(DRIVER_PATH)/painter/comms\n    SRC += \\\n        $(QUANTUM_DIR)/painter/qp_comms.c \\\n        $(DRIVER_PATH)/painter/comms/qp_comms_spi.c\n\n    ifeq ($(strip $(QUANTUM_PAINTER_NEEDS_COMMS_SPI_DC_RESET)), yes)\n        OPT_DEFS += -DQUANTUM_PAINTER_SPI_DC_RESET_ENABLE\n    endif\nendif\n\n# If I2C comms is needed, set up the required files\nifeq ($(strip $(QUANTUM_PAINTER_NEEDS_COMMS_I2C)), yes)\n    OPT_DEFS += -DQUANTUM_PAINTER_I2C_ENABLE\n    I2C_DRIVER_REQUIRED = yes\n    VPATH += $(DRIVER_PATH)/painter/comms\n    SRC += \\\n        $(QUANTUM_DIR)/painter/qp_comms.c \\\n        $(DRIVER_PATH)/painter/comms/qp_comms_i2c.c\nendif\n\n# Check if LVGL needs to be enabled\nifeq ($(strip $(QUANTUM_PAINTER_LVGL_INTEGRATION)), yes)\n    include $(QUANTUM_DIR)/painter/lvgl/rules.mk\nendif\n"
  },
  {
    "path": "quantum/pointing_device/pointing_device.c",
    "content": "/* Copyright 2017 Joshua Broekhuijsen <snipeye+qmk@gmail.com>\n * Copyright 2020 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n * Copyright 2021 Dasky (@daskygit)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"pointing_device.h\"\n#include <string.h>\n#include \"timer.h\"\n#include \"gpio.h\"\n\n#ifdef MOUSEKEY_ENABLE\n#    include \"mousekey.h\"\n#endif\n\n#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE\n#    include \"usb_descriptor_common.h\"\n#endif\n\n#if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1\n#    error More than one rotation selected.  This is not supported.\n#endif\n\n#if defined(POINTING_DEVICE_LEFT) || defined(POINTING_DEVICE_RIGHT) || defined(POINTING_DEVICE_COMBINED)\n#    ifndef SPLIT_POINTING_ENABLE\n#        error \"Using POINTING_DEVICE_LEFT or POINTING_DEVICE_RIGHT or POINTING_DEVICE_COMBINED, then SPLIT_POINTING_ENABLE is required but has not been defined\"\n#    endif\n#endif\n\n#if defined(SPLIT_POINTING_ENABLE)\n#    include \"transactions.h\"\n#    include \"keyboard.h\"\n\nreport_mouse_t shared_mouse_report = {};\nuint16_t       shared_cpi          = 0;\n\n/**\n * @brief Sets the shared mouse report used be pointing device task\n *\n * NOTE : Only available when using SPLIT_POINTING_ENABLE\n *\n * @param[in] new_mouse_report report_mouse_t\n */\nvoid pointing_device_set_shared_report(report_mouse_t new_mouse_report) {\n    shared_mouse_report = new_mouse_report;\n}\n\n/**\n * @brief Gets current pointing device CPI if supported\n *\n * Gets current cpi of the shared report and returns it as uint16_t\n *\n * NOTE : Only available when using SPLIT_POINTING_ENABLE\n *\n * @return cpi value as uint16_t\n */\nuint16_t pointing_device_get_shared_cpi(void) {\n    return shared_cpi;\n}\n\n#    if defined(POINTING_DEVICE_LEFT)\n#        define POINTING_DEVICE_THIS_SIDE is_keyboard_left()\n#    elif defined(POINTING_DEVICE_RIGHT)\n#        define POINTING_DEVICE_THIS_SIDE !is_keyboard_left()\n#    elif defined(POINTING_DEVICE_COMBINED)\n#        define POINTING_DEVICE_THIS_SIDE true\n#    endif\n\n#endif // defined(SPLIT_POINTING_ENABLE)\n\nstatic report_mouse_t local_mouse_report         = {};\nstatic bool           pointing_device_force_send = false;\n#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE\nstatic uint16_t hires_scroll_resolution;\n#endif\n\n#define POINTING_DEVICE_DRIVER_CONCAT(name) name##_pointing_device_driver\n#define POINTING_DEVICE_DRIVER(name) POINTING_DEVICE_DRIVER_CONCAT(name)\n\n#ifdef POINTING_DEVICE_DRIVER_custom\n__attribute__((weak)) void           pointing_device_driver_init(void) {}\n__attribute__((weak)) report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) {\n    return mouse_report;\n}\n__attribute__((weak)) uint16_t pointing_device_driver_get_cpi(void) {\n    return 0;\n}\n__attribute__((weak)) void pointing_device_driver_set_cpi(uint16_t cpi) {}\n\nconst pointing_device_driver_t custom_pointing_device_driver = {\n    .init       = pointing_device_driver_init,\n    .get_report = pointing_device_driver_get_report,\n    .get_cpi    = pointing_device_driver_get_cpi,\n    .set_cpi    = pointing_device_driver_set_cpi,\n};\n#endif\n\nconst pointing_device_driver_t *pointing_device_driver = &POINTING_DEVICE_DRIVER(POINTING_DEVICE_DRIVER_NAME);\n\n__attribute__((weak)) void           pointing_device_init_modules(void) {}\n__attribute__((weak)) report_mouse_t pointing_device_task_modules(report_mouse_t mouse_report) {\n    return mouse_report;\n}\n\n/**\n * @brief Keyboard level code pointing device initialisation\n *\n */\n__attribute__((weak)) void pointing_device_init_kb(void) {}\n\n/**\n * @brief User level code pointing device initialisation\n *\n */\n__attribute__((weak)) void pointing_device_init_user(void) {}\n\n/**\n * @brief Weak function allowing for keyboard level mouse report modification\n *\n * Takes report_mouse_t struct allowing modification at keyboard level then returns report_mouse_t.\n *\n * @param[in] mouse_report report_mouse_t\n * @return report_mouse_t\n */\n__attribute__((weak)) report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) {\n    return pointing_device_task_user(mouse_report);\n}\n\n/**\n * @brief Weak function allowing for user level mouse report modification\n *\n * Takes report_mouse_t struct allowing modification at user level then returns report_mouse_t.\n *\n * @param[in] mouse_report report_mouse_t\n * @return report_mouse_t\n */\n__attribute__((weak)) report_mouse_t pointing_device_task_user(report_mouse_t mouse_report) {\n    return mouse_report;\n}\n\n/**\n * @brief Handles pointing device buttons\n *\n * Returns modified button bitmask using bool pressed and selected pointing_device_buttons_t button in uint8_t buttons bitmask.\n *\n * @param buttons[in] uint8_t bitmask\n * @param pressed[in] bool\n * @param button[in] pointing_device_buttons_t value\n * @return Modified uint8_t bitmask buttons\n */\n__attribute__((weak)) uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button) {\n    if (pressed) {\n        buttons |= 1 << (button);\n    } else {\n        buttons &= ~(1 << (button));\n    }\n    return buttons;\n}\n\n/**\n * @brief Initialises pointing device\n *\n * Initialises pointing device, perform driver init and optional keyboard/user level code.\n */\n__attribute__((weak)) void pointing_device_init(void) {\n#if defined(SPLIT_POINTING_ENABLE)\n    if ((POINTING_DEVICE_THIS_SIDE))\n#endif\n    {\n        pointing_device_driver->init();\n#ifdef POINTING_DEVICE_MOTION_PIN\n#    ifdef POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW\n        gpio_set_pin_input_high(POINTING_DEVICE_MOTION_PIN);\n#    else\n        gpio_set_pin_input(POINTING_DEVICE_MOTION_PIN);\n#    endif\n#endif\n    }\n#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE\n    hires_scroll_resolution = POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER;\n    for (int i = 0; i < POINTING_DEVICE_HIRES_SCROLL_EXPONENT; i++) {\n        hires_scroll_resolution *= 10;\n    }\n#endif\n\n    pointing_device_init_modules();\n    pointing_device_init_kb();\n    pointing_device_init_user();\n}\n\n/**\n * @brief Sends processed mouse report to host\n *\n * This sends the mouse report generated by pointing_device_task if changed since the last report. Once send zeros mouse report except buttons.\n *\n */\n__attribute__((weak)) bool pointing_device_send(void) {\n    static report_mouse_t old_report         = {};\n    bool                  should_send_report = has_mouse_report_changed(&local_mouse_report, &old_report);\n\n    if (should_send_report) {\n        host_mouse_send(&local_mouse_report);\n    }\n    // send it and 0 it out except for buttons, so those stay until they are explicity over-ridden using update_pointing_device\n    uint8_t buttons = local_mouse_report.buttons;\n    memset(&local_mouse_report, 0, sizeof(local_mouse_report));\n    local_mouse_report.buttons = buttons;\n    memcpy(&old_report, &local_mouse_report, sizeof(local_mouse_report));\n\n    return should_send_report || buttons;\n}\n\n/**\n * @brief Adjust mouse report by any optional common pointing configuration defines\n *\n * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines.\n *\n * @param mouse_report[in] takes a report_mouse_t to be adjusted\n * @return report_mouse_t with adjusted values\n */\nreport_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report) {\n    // Support rotation of the sensor data\n#if defined(POINTING_DEVICE_ROTATION_90) || defined(POINTING_DEVICE_ROTATION_180) || defined(POINTING_DEVICE_ROTATION_270)\n    mouse_xy_report_t x = mouse_report.x;\n    mouse_xy_report_t y = mouse_report.y;\n#    if defined(POINTING_DEVICE_ROTATION_90)\n    mouse_report.x = y;\n    mouse_report.y = -x;\n#    elif defined(POINTING_DEVICE_ROTATION_180)\n    mouse_report.x = -x;\n    mouse_report.y = -y;\n#    elif defined(POINTING_DEVICE_ROTATION_270)\n    mouse_report.x = -y;\n    mouse_report.y = x;\n#    else\n#        error \"How the heck did you get here?!\"\n#    endif\n#endif\n    // Support Inverting the X and Y Axises\n#if defined(POINTING_DEVICE_INVERT_X)\n    mouse_report.x = -mouse_report.x;\n#endif\n#if defined(POINTING_DEVICE_INVERT_Y)\n    mouse_report.y = -mouse_report.y;\n#endif\n    return mouse_report;\n}\n\n/**\n * @brief Retrieves and processes pointing device data.\n *\n * This function is part of the keyboard loop and retrieves the mouse report from the pointing device driver.\n * It applies any optional configuration e.g. rotation or axis inversion and then initiates a send.\n *\n */\n__attribute__((weak)) bool pointing_device_task(void) {\n#if defined(SPLIT_POINTING_ENABLE)\n    // Don't poll the target side pointing device.\n    if (!is_keyboard_master()) {\n        return false;\n    };\n#endif\n\n#if (POINTING_DEVICE_TASK_THROTTLE_MS > 0)\n    static uint32_t last_exec = 0;\n    if (timer_elapsed32(last_exec) < POINTING_DEVICE_TASK_THROTTLE_MS) {\n        return false;\n    }\n    last_exec = timer_read32();\n#endif\n\n    // Gather report info\n#ifdef POINTING_DEVICE_MOTION_PIN\n#    if defined(SPLIT_POINTING_ENABLE)\n#        error POINTING_DEVICE_MOTION_PIN not supported when sharing the pointing device report between sides.\n#    endif\n#    ifdef POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW\n    if (!gpio_read_pin(POINTING_DEVICE_MOTION_PIN))\n#    else\n    if (gpio_read_pin(POINTING_DEVICE_MOTION_PIN))\n#    endif\n    {\n#endif\n\n#if defined(SPLIT_POINTING_ENABLE)\n#    if defined(POINTING_DEVICE_COMBINED)\n        static uint8_t old_buttons = 0;\n        local_mouse_report.buttons = old_buttons;\n        local_mouse_report         = pointing_device_driver->get_report(local_mouse_report);\n        old_buttons                = local_mouse_report.buttons;\n#    elif defined(POINTING_DEVICE_LEFT) || defined(POINTING_DEVICE_RIGHT)\n        local_mouse_report = POINTING_DEVICE_THIS_SIDE ? pointing_device_driver->get_report(local_mouse_report) : shared_mouse_report;\n#    else\n#        error \"You need to define the side(s) the pointing device is on. POINTING_DEVICE_COMBINED / POINTING_DEVICE_LEFT / POINTING_DEVICE_RIGHT\"\n#    endif\n#else\n    local_mouse_report = pointing_device_driver->get_report(local_mouse_report);\n#endif // defined(SPLIT_POINTING_ENABLE)\n\n#ifdef POINTING_DEVICE_MOTION_PIN\n    }\n#endif\n\n    // allow kb to intercept and modify report\n#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)\n    if (is_keyboard_left()) {\n        local_mouse_report  = pointing_device_adjust_by_defines(local_mouse_report);\n        shared_mouse_report = pointing_device_adjust_by_defines_right(shared_mouse_report);\n    } else {\n        local_mouse_report  = pointing_device_adjust_by_defines_right(local_mouse_report);\n        shared_mouse_report = pointing_device_adjust_by_defines(shared_mouse_report);\n    }\n    local_mouse_report = is_keyboard_left() ? pointing_device_task_combined(local_mouse_report, shared_mouse_report) : pointing_device_task_combined(shared_mouse_report, local_mouse_report);\n#else\n    local_mouse_report = pointing_device_adjust_by_defines(local_mouse_report);\n#endif\n    local_mouse_report = pointing_device_task_modules(local_mouse_report);\n    local_mouse_report = pointing_device_task_kb(local_mouse_report);\n    // automatic mouse layer function\n#ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE\n    pointing_device_task_auto_mouse(local_mouse_report);\n#endif\n    // combine with mouse report to ensure that the combined is sent correctly\n#ifdef MOUSEKEY_ENABLE\n    report_mouse_t mousekey_report = mousekey_get_report();\n    local_mouse_report.buttons     = local_mouse_report.buttons | mousekey_report.buttons;\n#endif\n\n    const bool send_report     = pointing_device_send() || pointing_device_force_send;\n    pointing_device_force_send = false;\n\n    return send_report;\n}\n\n/**\n * @brief Gets current mouse report used by pointing device task\n *\n * @return report_mouse_t\n */\nreport_mouse_t pointing_device_get_report(void) {\n    return local_mouse_report;\n}\n\n/**\n * @brief Sets mouse report used be pointing device task\n *\n * @param[in] mouse_report\n */\nvoid pointing_device_set_report(report_mouse_t mouse_report) {\n    pointing_device_force_send = has_mouse_report_changed(&local_mouse_report, &mouse_report);\n    memcpy(&local_mouse_report, &mouse_report, sizeof(local_mouse_report));\n}\n\n/**\n * @brief Gets current pointing device CPI if supported\n *\n * Gets current cpi from pointing device driver if supported and returns it as uint16_t\n *\n * @return cpi value as uint16_t\n */\nuint16_t pointing_device_get_cpi(void) {\n#if defined(SPLIT_POINTING_ENABLE)\n    return POINTING_DEVICE_THIS_SIDE ? pointing_device_driver->get_cpi() : shared_cpi;\n#else\n    return pointing_device_driver->get_cpi();\n#endif\n}\n\n/**\n * @brief Set pointing device CPI if supported\n *\n * Takes a uint16_t value to set pointing device cpi if supported by driver.\n *\n * @param[in] cpi uint16_t value.\n */\nvoid pointing_device_set_cpi(uint16_t cpi) {\n#if defined(SPLIT_POINTING_ENABLE)\n    if (POINTING_DEVICE_THIS_SIDE) {\n        pointing_device_driver->set_cpi(cpi);\n    } else {\n        shared_cpi = cpi;\n    }\n#else\n    pointing_device_driver->set_cpi(cpi);\n#endif\n}\n\n#if defined(SPLIT_POINTING_ENABLE) && defined(POINTING_DEVICE_COMBINED)\n/**\n * @brief Set pointing device CPI if supported\n *\n * Takes a bool and uint16_t and allows setting cpi for a single side when using 2 pointing devices with a split keyboard.\n *\n * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED\n *\n * @param[in] left true = left, false = right.\n * @param[in] cpi uint16_t value.\n */\nvoid pointing_device_set_cpi_on_side(bool left, uint16_t cpi) {\n    bool local = (is_keyboard_left() == left);\n    if (local) {\n        pointing_device_driver->set_cpi(cpi);\n    } else {\n        shared_cpi = cpi;\n    }\n}\n\n/**\n * @brief clamps int16_t to int8_t, or int32_t to int16_t\n *\n * @param[in] hv_clamp_range_t value\n * @return mouse_hv_report_t clamped value\n */\nstatic inline mouse_hv_report_t pointing_device_hv_clamp(hv_clamp_range_t value) {\n    if (value < MOUSE_REPORT_HV_MIN) {\n        return MOUSE_REPORT_HV_MIN;\n    } else if (value > MOUSE_REPORT_HV_MAX) {\n        return MOUSE_REPORT_HV_MAX;\n    } else {\n        return value;\n    }\n}\n\n/**\n * @brief clamps int16_t to int8_t, or int32_t to int16_t\n *\n * @param[in] xy_clamp_range_t value\n * @return mouse_xy_report_t clamped value\n */\nstatic inline mouse_xy_report_t pointing_device_xy_clamp(xy_clamp_range_t value) {\n    if (value < MOUSE_REPORT_XY_MIN) {\n        return MOUSE_REPORT_XY_MIN;\n    } else if (value > MOUSE_REPORT_XY_MAX) {\n        return MOUSE_REPORT_XY_MAX;\n    } else {\n        return value;\n    }\n}\n/**\n * @brief combines 2 mouse reports and returns 2\n *\n * Combines 2 report_mouse_t structs, clamping movement values to int8_t and ignores report_id then returns the resulting report_mouse_t struct.\n *\n * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED\n *\n * @param[in] left_report left report_mouse_t\n * @param[in] right_report right report_mouse_t\n * @return combined report_mouse_t of left_report and right_report\n */\nreport_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report) {\n    left_report.x = pointing_device_xy_clamp((xy_clamp_range_t)left_report.x + right_report.x);\n    left_report.y = pointing_device_xy_clamp((xy_clamp_range_t)left_report.y + right_report.y);\n    left_report.h = pointing_device_hv_clamp((hv_clamp_range_t)left_report.h + right_report.h);\n    left_report.v = pointing_device_hv_clamp((hv_clamp_range_t)left_report.v + right_report.v);\n    left_report.buttons |= right_report.buttons;\n    return left_report;\n}\n\n/**\n * @brief Adjust mouse report by any optional right pointing configuration defines\n *\n * This applies rotation or inversion to the mouse report as selected by the pointing device common configuration defines.\n *\n * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED\n *\n * @param[in] mouse_report report_mouse_t to be adjusted\n * @return report_mouse_t with adjusted values\n */\nreport_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report) {\n    // Support rotation of the sensor data\n#    if defined(POINTING_DEVICE_ROTATION_90_RIGHT) || defined(POINTING_DEVICE_ROTATION_180_RIGHT) || defined(POINTING_DEVICE_ROTATION_270_RIGHT)\n    mouse_xy_report_t x = mouse_report.x;\n    mouse_xy_report_t y = mouse_report.y;\n#        if defined(POINTING_DEVICE_ROTATION_90_RIGHT)\n    mouse_report.x = y;\n    mouse_report.y = -x;\n#        elif defined(POINTING_DEVICE_ROTATION_180_RIGHT)\n    mouse_report.x = -x;\n    mouse_report.y = -y;\n#        elif defined(POINTING_DEVICE_ROTATION_270_RIGHT)\n    mouse_report.x = -y;\n    mouse_report.y = x;\n#        else\n#            error \"How the heck did you get here?!\"\n#        endif\n#    endif\n    // Support Inverting the X and Y Axises\n#    if defined(POINTING_DEVICE_INVERT_X_RIGHT)\n    mouse_report.x = -mouse_report.x;\n#    endif\n#    if defined(POINTING_DEVICE_INVERT_Y_RIGHT)\n    mouse_report.y = -mouse_report.y;\n#    endif\n    return mouse_report;\n}\n\n/**\n * @brief Weak function allowing for keyboard level mouse report modification\n *\n * Takes 2 report_mouse_t structs allowing individual modification of sides at keyboard level then returns pointing_device_task_combined_user.\n *\n * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED\n *\n * @param[in] left_report report_mouse_t\n * @param[in] right_report report_mouse_t\n * @return pointing_device_task_combined_user(left_report, right_report) by default\n */\nreport_mouse_t pointing_device_task_combined(report_mouse_t left_report, report_mouse_t right_report) {\n    return pointing_device_task_modules(pointing_device_task_combined_kb(left_report, right_report));\n}\n\n/**\n * @brief Weak function allowing for keyboard level mouse report modification\n *\n * Takes 2 report_mouse_t structs allowing individual modification of sides at keyboard level then returns pointing_device_task_combined_user.\n *\n * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED\n *\n * @param[in] left_report report_mouse_t\n * @param[in] right_report report_mouse_t\n * @return pointing_device_task_combined_user(left_report, right_report) by default\n */\n__attribute__((weak)) report_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report) {\n    return pointing_device_task_combined_user(left_report, right_report);\n}\n\n/**\n * @brief Weak function allowing for user level mouse report modification\n *\n * Takes 2 report_mouse_t structs allowing individual modification of sides at user level then returns pointing_device_combine_reports.\n *\n * NOTE: Only available when using SPLIT_POINTING_ENABLE and POINTING_DEVICE_COMBINED\n *\n * @param[in] left_report report_mouse_t\n * @param[in] right_report report_mouse_t\n * @return pointing_device_combine_reports(left_report, right_report) by default\n */\n__attribute__((weak)) report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report) {\n    return pointing_device_combine_reports(left_report, right_report);\n}\n#endif\n\n__attribute__((weak)) void pointing_device_keycode_handler(uint16_t keycode, bool pressed) {\n    if IS_MOUSEKEY_BUTTON (keycode) {\n        local_mouse_report.buttons = pointing_device_handle_buttons(local_mouse_report.buttons, pressed, keycode - QK_MOUSE_BUTTON_1);\n        pointing_device_send();\n    }\n}\n\n#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE\nuint16_t pointing_device_get_hires_scroll_resolution(void) {\n    return hires_scroll_resolution;\n}\n#endif\n"
  },
  {
    "path": "quantum/pointing_device/pointing_device.h",
    "content": "/*\nCopyright 2017 Joshua Broekhuijsen <snipeye+qmk@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"host.h\"\n#include \"report.h\"\n\ntypedef struct {\n    void (*init)(void);\n    report_mouse_t (*get_report)(report_mouse_t mouse_report);\n    void (*set_cpi)(uint16_t);\n    uint16_t (*get_cpi)(void);\n} pointing_device_driver_t;\n\n#ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE\n#    include \"pointing_device_auto_mouse.h\"\n#endif\n\n#if defined(POINTING_DEVICE_DRIVER_adns5050)\n#    include \"drivers/sensors/adns5050.h\"\n#    define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW\n#elif defined(POINTING_DEVICE_DRIVER_pmw3320)\n#    include \"drivers/sensors/pmw3320.h\"\n#    define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW\n#elif defined(POINTING_DEVICE_DRIVER_adns9800)\n#    include \"spi_master.h\"\n#    include \"drivers/sensors/adns9800.h\"\n#    define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW\n#elif defined(POINTING_DEVICE_DRIVER_analog_joystick)\n#    include \"analog.h\"\n#    include \"drivers/sensors/analog_joystick.h\"\n#    define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW\n#elif defined(POINTING_DEVICE_DRIVER_azoteq_iqs5xx)\n#    include \"i2c_master.h\"\n#    include \"drivers/sensors/azoteq_iqs5xx.h\"\n#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)\n#    include \"drivers/sensors/cirque_pinnacle.h\"\n#    include \"pointing_device_gestures.h\"\n#elif defined(POINTING_DEVICE_DRIVER_paw3204)\n#    include \"drivers/sensors/paw3204.h\"\n#    define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW\n#elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball)\n#    include \"i2c_master.h\"\n#    include \"drivers/sensors/pimoroni_trackball.h\"\n// support for legacy pimoroni defines\n#    ifdef PIMORONI_TRACKBALL_INVERT_X\n#        define POINTING_DEVICE_INVERT_X\n#    endif\n#    ifdef PIMORONI_TRACKBALL_INVERT_Y\n#        define POINTING_DEVICE_INVERT_Y\n#    endif\n#    ifdef PIMORONI_TRACKBALL_ROTATE\n#        define POINTING_DEVICE_ROTATION_90\n#    endif\n#    define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW\n#elif defined(POINTING_DEVICE_DRIVER_pmw3360) || defined(POINTING_DEVICE_DRIVER_pmw3389)\n#    include \"spi_master.h\"\n#    include \"drivers/sensors/pmw33xx_common.h\"\n#    define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW\n#elif defined(POINTING_DEVICE_DRIVER_navigator_trackpad)\n#    include \"i2c_master.h\"\n#    include \"drivers/sensors/navigator_trackpad.h\"\n#    include \"drivers/sensors/navigator.h\"\n#else\nvoid           pointing_device_driver_init(void);\nreport_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report);\nuint16_t       pointing_device_driver_get_cpi(void);\nvoid           pointing_device_driver_set_cpi(uint16_t cpi);\n#endif\n\ntypedef enum {\n    POINTING_DEVICE_BUTTON1,\n    POINTING_DEVICE_BUTTON2,\n    POINTING_DEVICE_BUTTON3,\n    POINTING_DEVICE_BUTTON4,\n    POINTING_DEVICE_BUTTON5,\n    POINTING_DEVICE_BUTTON6,\n    POINTING_DEVICE_BUTTON7,\n    POINTING_DEVICE_BUTTON8,\n} pointing_device_buttons_t;\n\n#ifdef MOUSE_EXTENDED_REPORT\ntypedef int32_t xy_clamp_range_t;\n#else\ntypedef int16_t xy_clamp_range_t;\n#endif\n\n#ifdef WHEEL_EXTENDED_REPORT\ntypedef int32_t hv_clamp_range_t;\n#else\ntypedef int16_t hv_clamp_range_t;\n#endif\n\n#define CONSTRAIN_HID(amt) ((amt) < INT8_MIN ? INT8_MIN : ((amt) > INT8_MAX ? INT8_MAX : (amt)))\n#define CONSTRAIN_HID_XY(amt) ((amt) < MOUSE_REPORT_XY_MIN ? MOUSE_REPORT_XY_MIN : ((amt) > MOUSE_REPORT_XY_MAX ? MOUSE_REPORT_XY_MAX : (amt)))\n\nvoid           pointing_device_init(void);\nbool           pointing_device_task(void);\nbool           pointing_device_send(void);\nreport_mouse_t pointing_device_get_report(void);\nvoid           pointing_device_set_report(report_mouse_t mouse_report);\nuint16_t       pointing_device_get_cpi(void);\nvoid           pointing_device_set_cpi(uint16_t cpi);\n\nvoid           pointing_device_init_kb(void);\nvoid           pointing_device_init_user(void);\nreport_mouse_t pointing_device_task_kb(report_mouse_t mouse_report);\nreport_mouse_t pointing_device_task_user(report_mouse_t mouse_report);\nuint8_t        pointing_device_handle_buttons(uint8_t buttons, bool pressed, pointing_device_buttons_t button);\nreport_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report);\nvoid           pointing_device_keycode_handler(uint16_t keycode, bool pressed);\n\n#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE\nuint16_t pointing_device_get_hires_scroll_resolution(void);\n#endif\n\n#if defined(SPLIT_POINTING_ENABLE)\nvoid     pointing_device_set_shared_report(report_mouse_t report);\nuint16_t pointing_device_get_shared_cpi(void);\n#    if !defined(POINTING_DEVICE_TASK_THROTTLE_MS)\n#        define POINTING_DEVICE_TASK_THROTTLE_MS 1\n#    endif\n#    if defined(POINTING_DEVICE_COMBINED)\nvoid           pointing_device_set_cpi_on_side(bool left, uint16_t cpi);\nreport_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report);\nreport_mouse_t pointing_device_task_combined(report_mouse_t left_report, report_mouse_t right_report);\nreport_mouse_t pointing_device_task_combined_kb(report_mouse_t left_report, report_mouse_t right_report);\nreport_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, report_mouse_t right_report);\nreport_mouse_t pointing_device_adjust_by_defines_right(report_mouse_t mouse_report);\n#    endif // defined(POINTING_DEVICE_COMBINED)\n#endif     // defined(SPLIT_POINTING_ENABLE)\n"
  },
  {
    "path": "quantum/pointing_device/pointing_device_auto_mouse.c",
    "content": "/* Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n * Copyright 2022 Alabastard\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef POINTING_DEVICE_AUTO_MOUSE_ENABLE\n\n#    include <stdlib.h>\n#    include <string.h>\n#    include \"pointing_device_auto_mouse.h\"\n#    include \"debug.h\"\n#    include \"action_util.h\"\n#    include \"quantum_keycodes.h\"\n#ifdef LAYER_LOCK_ENABLE\n#    include \"layer_lock.h\"\n#endif\n\n/* local data structure for tracking auto mouse */\nstatic auto_mouse_context_t auto_mouse_context = {\n    .config.layer    = (uint8_t)(AUTO_MOUSE_DEFAULT_LAYER),\n    .config.timeout  = (uint16_t)(AUTO_MOUSE_TIME),\n    .config.debounce = (uint8_t)(AUTO_MOUSE_DEBOUNCE),\n#ifdef AUTO_MOUSE_ONESHOT\n    .one_shot = false,\n    .one_shot_triggered = false,\n#endif\n};\n\n/* local functions */\nstatic bool is_mouse_record(uint16_t keycode, keyrecord_t* record);\nstatic void auto_mouse_reset(void);\n\n/* check for target layer deactivation overrides */\nstatic inline bool layer_hold_check(void) {\n    return get_auto_mouse_toggle() ||\n#    ifndef NO_ACTION_ONESHOT\n           get_oneshot_layer() == (AUTO_MOUSE_TARGET_LAYER) ||\n#    endif\n           false;\n}\n\n/* check all layer activation criteria */\nbool is_auto_mouse_active(void) {\n#ifdef AUTO_MOUSE_ONESHOT\n    return auto_mouse_context.status.is_activated || auto_mouse_context.status.mouse_key_tracker || layer_hold_check() || auto_mouse_context.one_shot;\n#else\n    return auto_mouse_context.status.is_activated || auto_mouse_context.status.mouse_key_tracker || layer_hold_check();\n#endif\n}\n\n/**\n * @brief Get auto mouse enable state\n *\n * Return is_enabled value\n *\n * @return bool true: auto mouse enabled false: auto mouse disabled\n */\nbool get_auto_mouse_enable(void) {\n    return auto_mouse_context.config.is_enabled;\n}\n\n/**\n * @brief get current target layer index\n *\n * NOTE: (AUTO_MOUSE_TARGET_LAYER) is an alias for this function\n *\n * @return uint8_t target layer index\n */\nuint8_t get_auto_mouse_layer(void) {\n    return auto_mouse_context.config.layer;\n}\n\n/**\n * @brief Get the current timeout to turn off mouse layer\n *\n * @return uint16_t timeout in ms\n */\nuint16_t get_auto_mouse_timeout(void) {\n    return auto_mouse_context.config.timeout;\n}\n\n/**\n * @brief Get the auto mouse debouncing timeout\n *\n * @return uint8_t\n */\nuint8_t get_auto_mouse_debounce(void) {\n    return auto_mouse_context.config.debounce;\n}\n\n/**\n * @brief get layer_toggled value\n *\n * @return bool of current layer_toggled state\n */\nbool get_auto_mouse_toggle(void) {\n    return auto_mouse_context.status.is_toggled;\n}\n\n/**\n * @brief get key tracker value\n *\n * @return bool of current layer_toggled state\n */\nint8_t get_auto_mouse_key_tracker(void) {\n    return auto_mouse_context.status.mouse_key_tracker;\n}\n\n/**\n * @brief Reset auto mouse context\n *\n * Clear timers and status\n *\n * NOTE: this will set is_toggled to false so careful when using it\n */\nstatic void auto_mouse_reset(void) {\n    memset(&auto_mouse_context.status, 0, sizeof(auto_mouse_context.status));\n    memset(&auto_mouse_context.timer, 0, sizeof(auto_mouse_context.timer));\n}\n\n/**\n * @brief Set auto mouse enable state\n *\n * Set local auto mouse enabled state\n *\n * @param[in] state bool\n */\nvoid set_auto_mouse_enable(bool enable) {\n    // skip if unchanged\n    if (auto_mouse_context.config.is_enabled == enable) return;\n    auto_mouse_context.config.is_enabled = enable;\n    auto_mouse_reset();\n}\n\n/**\n * @brief Change target layer for auto mouse\n *\n * Sets input as the new target layer if different from current and resets auto mouse\n *\n * NOTE: remove_auto_mouse_layer(state, false) or auto_mouse_layer_off should be called\n *       before this function to avoid issues with layers getting stuck\n *\n * @param[in] layer uint8_t\n */\nvoid set_auto_mouse_layer(uint8_t layer) {\n    // skip if unchanged\n    if (auto_mouse_context.config.layer == layer) return;\n    auto_mouse_context.config.layer = layer;\n    auto_mouse_reset();\n}\n\n/**\n * @brief Changes the timeout for the mouse auto layer to be disabled\n *\n * @param timeout\n */\nvoid set_auto_mouse_timeout(uint16_t timeout) {\n    if (auto_mouse_context.config.timeout == timeout) return;\n    auto_mouse_context.config.timeout = timeout;\n    auto_mouse_reset();\n}\n\n/**\n * @brief Set the auto mouse key debounce\n *\n * @param debounce\n */\nvoid set_auto_mouse_debounce(uint8_t debounce) {\n    if (auto_mouse_context.config.debounce == debounce) return;\n    auto_mouse_context.config.debounce = debounce;\n    auto_mouse_reset();\n}\n\n/**\n * @brief Changes the timeout for the mouse auto layer to be disabled\n *\n * @param key_tracker\n */\nvoid set_auto_mouse_key_tracker(int8_t key_tracker) {\n    auto_mouse_context.status.mouse_key_tracker = key_tracker;\n}\n\n/**\n * @brief toggle mouse layer setting\n *\n * Change state of local layer_toggled bool meant to track when the mouse layer is toggled on by other means\n *\n * NOTE: While is_toggled is true it will prevent deactiving target layer (but not activation)\n */\nvoid auto_mouse_toggle(void) {\n    auto_mouse_context.status.is_toggled ^= 1;\n    auto_mouse_context.timer.delay = 0;\n}\n\n/** @brief set toggled mouse layer flag\n *\n * Change state of local layer_toggled bool meant to track when the mouse layer is toggled on by other means\n *\n * NOTE: While is_toggled is true it will prevent deactiving target layer (but not activation)\n *\n * @param[in] toggled bool\n */\nvoid set_auto_mouse_toggled(bool toggled) {\n    auto_mouse_context.status.is_toggled = toggled;\n}\n\n/**\n * @brief Remove current auto mouse target layer from layer state\n *\n * Will remove auto mouse target layer from given layer state if appropriate.\n *\n * NOTE: Removal can be forced, ignoring appropriate critera\n *\n * @params state[in] layer_state_t original layer state\n * @params force[in] bool force removal\n *\n * @return layer_state_t modified layer state\n */\nlayer_state_t remove_auto_mouse_layer(layer_state_t state, bool force) {\n    if (force || ((AUTO_MOUSE_ENABLED) && !layer_hold_check())) {\n        state &= ~((layer_state_t)1 << (AUTO_MOUSE_TARGET_LAYER));\n    }\n    return state;\n}\n\n/**\n * @brief Disable target layer\n *\n * Will disable target layer if appropriate.\n * NOTE: NOT TO BE USED in layer_state_set stack!!!\n */\nvoid auto_mouse_layer_off(void) {\n    if (layer_state_is((AUTO_MOUSE_TARGET_LAYER)) && (AUTO_MOUSE_ENABLED) && !layer_hold_check()) {\n        layer_off((AUTO_MOUSE_TARGET_LAYER));\n    }\n}\n\n/**\n * @brief Weak function to handel testing if pointing_device is active\n *\n * Will trigger target layer activation(if delay timer has expired) and prevent deactivation when true.\n * May be replaced by bool in report_mouse_t in future\n *\n * NOTE: defined weakly to allow for changing and adding conditions for specific hardware/customization\n *\n * @param[in] mouse_report report_mouse_t\n * @return bool of pointing_device activation\n */\n__attribute__((weak)) bool auto_mouse_activation(report_mouse_t mouse_report) {\n    auto_mouse_context.total_mouse_movement.x += mouse_report.x;\n    auto_mouse_context.total_mouse_movement.y += mouse_report.y;\n    auto_mouse_context.total_mouse_movement.h += mouse_report.h;\n    auto_mouse_context.total_mouse_movement.v += mouse_report.v;\n    return abs(auto_mouse_context.total_mouse_movement.x) > AUTO_MOUSE_THRESHOLD || abs(auto_mouse_context.total_mouse_movement.y) > AUTO_MOUSE_THRESHOLD || abs(auto_mouse_context.total_mouse_movement.h) > AUTO_MOUSE_SCROLL_THRESHOLD || abs(auto_mouse_context.total_mouse_movement.v) > AUTO_MOUSE_SCROLL_THRESHOLD || (mouse_report.buttons && layer_state_is(AUTO_MOUSE_TARGET_LAYER));\n}\n\n/**\n * @brief Update the auto mouse based on mouse_report\n *\n * Handel activation/deactivation of target layer based on auto_mouse_activation and state timers and local key/layer tracking data\n *\n * @param[in] mouse_report report_mouse_t\n */\nvoid pointing_device_task_auto_mouse(report_mouse_t mouse_report) {\n    // skip if disabled, delay timer running, or debounce\n    if (!(AUTO_MOUSE_ENABLED) || timer_elapsed(auto_mouse_context.timer.active) <= auto_mouse_context.config.debounce || timer_elapsed(auto_mouse_context.timer.delay) <= AUTO_MOUSE_DELAY) {\n        return;\n    }\n    // update activation and reset debounce\n    auto_mouse_context.status.is_activated = auto_mouse_activation(mouse_report);\n    // Check for actual activity (mouse movement, held mouse keys, or layer hold)\n    // Also include one_shot that hasn't been triggered yet (keeps layer alive until first key press)\n    bool has_activity = auto_mouse_context.status.is_activated || auto_mouse_context.status.mouse_key_tracker || layer_hold_check();\n#ifdef AUTO_MOUSE_ONESHOT\n    has_activity = has_activity || (auto_mouse_context.one_shot && !auto_mouse_context.one_shot_triggered);\n#endif\n    if (has_activity) {\n        auto_mouse_context.total_mouse_movement = (total_mouse_movement_t){.x = 0, .y = 0, .h = 0, .v = 0};\n        auto_mouse_context.timer.active         = timer_read();\n        auto_mouse_context.timer.delay          = 0;\n        if (!layer_state_is((AUTO_MOUSE_TARGET_LAYER))) {\n            layer_on((AUTO_MOUSE_TARGET_LAYER));\n        }\n#ifdef AUTO_MOUSE_ONESHOT\n        if (!auto_mouse_context.one_shot) {\n            auto_mouse_context.one_shot = true;\n        }\n#endif\n    } else if (layer_state_is((AUTO_MOUSE_TARGET_LAYER)) && timer_elapsed(auto_mouse_context.timer.active) > auto_mouse_context.config.timeout) {\n#ifdef LAYER_LOCK_ENABLE\n        if(is_layer_locked(AUTO_MOUSE_DEFAULT_LAYER)) return;\n#endif\n        layer_off((AUTO_MOUSE_TARGET_LAYER));\n        auto_mouse_context.timer.active         = 0;\n        auto_mouse_context.total_mouse_movement = (total_mouse_movement_t){.x = 0, .y = 0, .h = 0, .v = 0};\n#ifdef AUTO_MOUSE_ONESHOT\n        auto_mouse_context.one_shot = false;\n        auto_mouse_context.one_shot_triggered = false;\n#endif\n    }\n}\n\n/**\n * @brief Handle mouskey event\n *\n * Increments/decrements mouse_key_tracker and restart active timer\n *\n * @param[in] pressed bool\n */\nvoid auto_mouse_keyevent(bool pressed) {\n    if (pressed) {\n        auto_mouse_context.status.mouse_key_tracker++;\n    } else {\n        auto_mouse_context.status.mouse_key_tracker--;\n    }\n    auto_mouse_context.timer.delay = 0;\n}\n\n/**\n * @brief Handle auto mouse non mousekey reset\n *\n * Start/restart delay timer and reset auto mouse on keydown as well as turn the\n * target layer off if on and reset toggle status\n *\n * NOTE: NOT TO BE USED in layer_state_set stack!!!\n *\n * @param[in] pressed bool\n */\nvoid auto_mouse_reset_trigger(bool pressed) {\n    if (pressed) {\n#ifdef LAYER_LOCK_ENABLE\n        if(is_layer_locked(AUTO_MOUSE_DEFAULT_LAYER)) return;\n#endif\n        auto_mouse_reset();\n    }\n    auto_mouse_context.timer.delay = timer_read();\n}\n\n/**\n * @brief handle key events processing for auto mouse\n *\n * Will process keys differently depending on if key is defined as mousekey or not.\n * Some keys have built in behaviour(not overwritable):\n * mouse buttons        : auto_mouse_keyevent()\n * non-mouse keys       : auto_mouse_reset_trigger()\n * mod keys             : skip auto mouse key processing\n * mod tap              : skip on hold (mod keys)\n * QK mods e.g. LCTL(kc): default to non-mouse key, add at kb/user level as needed\n * non target layer keys: skip auto mouse key processing (same as mod keys)\n * MO(target layer)     : auto_mouse_keyevent()\n * target layer toggles : auto_mouse_toggle() (on both key up and keydown)\n * target layer tap     : default processing on tap mouse key on hold\n * all other keycodes   : default to non-mouse key, add at kb/user level as needed\n *\n * Will deactivate target layer once a non mouse key is pressed if nothing is holding the layer active\n * such as held mousekey, toggled current target layer, or auto_mouse_activation is true\n *\n * @params keycode[in] uint16_t\n * @params record[in] keyrecord_t pointer\n */\nbool process_auto_mouse(uint16_t keycode, keyrecord_t* record) {\n    // skip if not enabled or mouse_layer not set\n    if (!(AUTO_MOUSE_ENABLED)) return true;\n\n    switch (keycode) {\n        // Skip Mod keys, KC_NO, and layer lock to avoid layer reset\n        case KC_NO:\n        case KC_LEFT_CTRL ... KC_RIGHT_GUI:\n        case QK_MODS ... QK_MODS_MAX:\n        case QK_LLCK:\n            break;\n        // TO((AUTO_MOUSE_TARGET_LAYER))-------------------------------------------------------------------------------\n        case QK_TO ... QK_TO_MAX:\n            if (QK_TO_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) {\n                if (!(record->event.pressed)) auto_mouse_toggle();\n            } else {\n                auto_mouse_context.status.is_toggled = false;\n            }\n            break;\n        // TG((AUTO_MOUSE_TARGET_LAYER))-------------------------------------------------------------------------------\n        case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:\n            if (QK_TOGGLE_LAYER_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) {\n                if (!(record->event.pressed)) auto_mouse_toggle();\n            }\n            break;\n        // MO((AUTO_MOUSE_TARGET_LAYER))-------------------------------------------------------------------------------\n        case QK_MOMENTARY ... QK_MOMENTARY_MAX:\n            if (QK_MOMENTARY_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) {\n                auto_mouse_keyevent(record->event.pressed);\n            }\n        // DF ---------------------------------------------------------------------------------------------------------\n        case QK_DEF_LAYER ... QK_DEF_LAYER_MAX:\n        // PDF --------------------------------------------------------------------------------------------------------\n        case QK_PERSISTENT_DEF_LAYER ... QK_PERSISTENT_DEF_LAYER_MAX:\n#    ifndef NO_ACTION_ONESHOT\n        // OSL((AUTO_MOUSE_TARGET_LAYER))------------------------------------------------------------------------------\n        case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:\n        case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:\n#    endif\n            break;\n        // LM((AUTO_MOUSE_TARGET_LAYER), mod)--------------------------------------------------------------------------\n        case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:\n            if (QK_LAYER_MOD_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) {\n                auto_mouse_keyevent(record->event.pressed);\n            }\n            break;\n            // TT((AUTO_MOUSE_TARGET_LAYER))---------------------------------------------------------------------------\n#    ifndef NO_ACTION_TAPPING\n        case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:\n            if (QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) {\n                auto_mouse_keyevent(record->event.pressed);\n#        if TAPPING_TOGGLE != 0\n                if (record->tap.count == TAPPING_TOGGLE) {\n                    if (record->event.pressed) {\n                        auto_mouse_context.status.mouse_key_tracker--;\n                    } else {\n                        auto_mouse_toggle();\n                        auto_mouse_context.status.mouse_key_tracker++;\n                    }\n                }\n#        endif\n            }\n            break;\n        // LT((AUTO_MOUSE_TARGET_LAYER), kc)---------------------------------------------------------------------------\n        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:\n            if (!record->tap.count) {\n                if (QK_LAYER_TAP_GET_LAYER(keycode) == (AUTO_MOUSE_TARGET_LAYER)) {\n                    auto_mouse_keyevent(record->event.pressed);\n                }\n                break;\n            }\n        // MT(kc) only skip on hold\n        case QK_MOD_TAP ... QK_MOD_TAP_MAX:\n            if (!record->tap.count) break;\n#    endif\n        // QK_MODS goes to default\n        default:\n            // skip on no event\n            if (IS_NOEVENT(record->event)) break;\n            // check if keyrecord is mousekey\n            if (is_mouse_record(keycode, record)) {\n                auto_mouse_keyevent(record->event.pressed);\n            } else if (!is_auto_mouse_active()) {\n                // all non-mousekey presses restart delay timer and reset status\n                auto_mouse_reset_trigger(record->event.pressed);\n            }\n#ifdef AUTO_MOUSE_ONESHOT\n            else if (auto_mouse_context.one_shot && !auto_mouse_context.one_shot_triggered && record->event.pressed) {\n                // First non-mouse key press while one_shot is active - start the countdown\n                auto_mouse_context.one_shot_triggered = true;\n            }\n#endif\n    }\n    if (auto_mouse_context.status.mouse_key_tracker < 0) {\n        auto_mouse_context.status.mouse_key_tracker = 0;\n        dprintf(\"key tracker error (<0) \\n\");\n    }\n\n    return true;\n}\n\n/**\n * @brief Local function to handle checking if a keycode is a mouse button\n *\n * Starts code stack for checking keyrecord if defined as mousekey\n *\n * @params keycode[in] uint16_t\n * @params record[in]  keyrecord_t pointer\n * @return bool true: keyrecord is mousekey false: keyrecord is not mousekey\n */\nstatic bool is_mouse_record(uint16_t keycode, keyrecord_t* record) {\n    // allow for keyboard to hook in and override if need be\n    if (is_mouse_record_kb(keycode, record)) return true;\n\n    return false;\n}\n\n/**\n * @brief Weakly defined keyboard level callback for adding keyrecords as mouse keys\n *\n * Meant for redefinition at keyboard level and should return is_mouse_record_user by default at end of function\n *\n * @params keycode[in] uint16_t\n * @params record[in] keyrecord_t pointer\n * @return bool true: keyrecord is defined as mouse key false: keyrecord is not defined as mouse key\n */\n__attribute__((weak)) bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record) {\n    return is_mouse_record_user(keycode, record);\n}\n\n/**\n * @brief Weakly defined keymap/user level callback for adding keyrecords as mouse keys\n *\n * Meant for redefinition at keymap/user level and should return false by default at end of function\n *\n * @params keycode[in] uint16_t\n * @params record[in] keyrecord_t pointer\n * @return bool true: keyrecord is defined as mouse key false: keyrecord is not defined as mouse key\n */\n__attribute__((weak)) bool is_mouse_record_user(uint16_t keycode, keyrecord_t* record) {\n    return false;\n}\n\n#endif // POINTING_DEVICE_AUTO_MOUSE_ENABLE\n"
  },
  {
    "path": "quantum/pointing_device/pointing_device_auto_mouse.h",
    "content": "/* Copyright 2022 Alabastard\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"pointing_device.h\"\n#include \"keycodes.h\"\n#include \"action.h\"\n#include \"report.h\"\n#include \"action_layer.h\"\n#include \"action_tapping.h\"\n\n/* check settings and set defaults */\n#ifndef POINTING_DEVICE_AUTO_MOUSE_ENABLE\n#    error \"POINTING_DEVICE_AUTO_MOUSE_ENABLE not defined! check config settings\"\n#endif\n\n#ifndef AUTO_MOUSE_DEFAULT_LAYER\n#    define AUTO_MOUSE_DEFAULT_LAYER 1\n#endif\n#ifndef AUTO_MOUSE_TIME\n#    define AUTO_MOUSE_TIME 650\n#endif\n#ifndef AUTO_MOUSE_DELAY\n#    define AUTO_MOUSE_DELAY GET_TAPPING_TERM(QK_MOUSE_BUTTON_1, &(keyrecord_t){})\n#endif\n#ifndef AUTO_MOUSE_DEBOUNCE\n#    define AUTO_MOUSE_DEBOUNCE 25\n#endif\n#ifndef AUTO_MOUSE_THRESHOLD\n#    define AUTO_MOUSE_THRESHOLD 10\n#endif\n#ifndef AUTO_MOUSE_SCROLL_THRESHOLD\n#    define AUTO_MOUSE_SCROLL_THRESHOLD AUTO_MOUSE_THRESHOLD\n#endif\n\n/* data structure */\ntypedef struct {\n    mouse_xy_report_t x;\n    mouse_xy_report_t y;\n    int8_t            v;\n    int8_t            h;\n} total_mouse_movement_t;\ntypedef struct {\n    struct {\n        bool     is_enabled;\n        uint8_t  layer;\n        uint16_t timeout;\n        uint8_t  debounce;\n    } config;\n    struct {\n        uint16_t active;\n        uint16_t delay;\n    } timer;\n    struct {\n        bool   is_activated;\n        bool   is_toggled;\n        int8_t mouse_key_tracker;\n    } status;\n    total_mouse_movement_t total_mouse_movement;\n#ifdef AUTO_MOUSE_ONESHOT\n    bool one_shot;\n    bool one_shot_triggered;\n#endif\n} auto_mouse_context_t;\n\n/* ----------Set up and control------------------------------------------------------------------------------ */\nvoid          set_auto_mouse_enable(bool enable);                       // enable/disable auto mouse feature\nbool          get_auto_mouse_enable(void);                              // get auto_mouse_enable\nvoid          set_auto_mouse_layer(uint8_t layer);                      // set target layer by index\nuint8_t       get_auto_mouse_layer(void);                               // get target layer index\nvoid          set_auto_mouse_timeout(uint16_t timeout);                 // set layer timeout\nuint16_t      get_auto_mouse_timeout(void);                             // get layer timeout\nvoid          set_auto_mouse_debounce(uint8_t debounce);                // set debounce\nvoid          set_auto_mouse_toggled(bool toggled);                     // set toggled mouse layer flag\nuint8_t       get_auto_mouse_debounce(void);                            // get debounce\nvoid          set_auto_mouse_key_tracker(int8_t key_tracker);           // set key tracker\nint8_t        get_auto_mouse_key_tracker(void);                         // get key tracker\nvoid          auto_mouse_layer_off(void);                               // disable target layer if appropriate (DO NOT USE in layer_state_set stack!!)\nlayer_state_t remove_auto_mouse_layer(layer_state_t state, bool force); // remove auto mouse target layer from state if appropriate (can be forced)\nbool          is_auto_mouse_active(void);                               // check if target layer is active\n/* ----------For custom pointing device activation----------------------------------------------------------- */\nbool auto_mouse_activation(report_mouse_t mouse_report); // handles pointing device trigger conditions for target layer activation (overwritable)\n\n/* ----------Handling keyevents------------------------------------------------------------------------------ */\nvoid auto_mouse_keyevent(bool pressed);      // trigger auto mouse keyevent: mouse_keytracker increment/decrement on press/release\nvoid auto_mouse_reset_trigger(bool pressed); // trigger non mouse keyevent: reset and start delay timer (DO NOT USE in layer_state_set stack!!)\nvoid auto_mouse_toggle(void);                // toggle mouse layer flag disables mouse layer deactivation while on (meant for tap toggle or toggle of target)\nbool get_auto_mouse_toggle(void);            // get toggle mouse layer flag value\n\n/* ----------Callbacks for adding keycodes to mouse record checking------------------------------------------ */\nbool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record);\nbool is_mouse_record_user(uint16_t keycode, keyrecord_t* record);\n\n/* ----------Core functions (only used in custom pointing devices or key processing)------------------------- */\nvoid pointing_device_task_auto_mouse(report_mouse_t mouse_report); // add to pointing_device_task_*\nbool process_auto_mouse(uint16_t keycode, keyrecord_t* record);    // add to process_record_*\n\n/* ----------Macros/Aliases---------------------------------------------------------------------------------- */\n#define AUTO_MOUSE_TARGET_LAYER get_auto_mouse_layer()\n#define AUTO_MOUSE_ENABLED get_auto_mouse_enable()\n"
  },
  {
    "path": "quantum/pointing_device/pointing_device_gestures.c",
    "content": "/* Copyright 2022 Daniel Kao <daniel.m.kao@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include <string.h>\n#include \"pointing_device_gestures.h\"\n#include \"timer.h\"\n\n#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE\n#    ifdef POINTING_DEVICE_MOTION_PIN\n#        error POINTING_DEVICE_MOTION_PIN not supported when using inertial cursor. Need repeated calls to get_report() to generate glide events.\n#    endif\n\nstatic void cursor_glide_stop(cursor_glide_context_t* glide) {\n    memset(&glide->status, 0, sizeof(glide->status));\n}\n\nstatic cursor_glide_t cursor_glide(cursor_glide_context_t* glide) {\n    cursor_glide_status_t* status = &glide->status;\n    cursor_glide_t         report;\n    int32_t                p;\n    int32_t                x, y;\n\n    if (status->v0 == 0) {\n        report.dx    = 0;\n        report.dy    = 0;\n        report.valid = false;\n        cursor_glide_stop(glide);\n        goto exit;\n    }\n\n    status->counter++;\n    /* Calculate current 1D position */\n    p = status->v0 * status->counter - (int32_t)glide->config.coef * status->counter * status->counter / 2;\n    /*\n     * Translate to x & y axes\n     * Done this way instead of applying friction to each axis separately, so we don't end up with the shorter axis stuck at 0 towards the end of diagonal movements.\n     */\n    x            = (int32_t)(p * status->dx0 / status->v0);\n    y            = (int32_t)(p * status->dy0 / status->v0);\n    report.dx    = (mouse_xy_report_t)(x - status->x);\n    report.dy    = (mouse_xy_report_t)(y - status->y);\n    report.valid = true;\n    if (report.dx <= 1 && report.dx >= -1 && report.dy <= 1 && report.dy >= -1) {\n        /* Stop gliding once speed is low enough */\n        cursor_glide_stop(glide);\n        goto exit;\n    }\n    status->x     = x;\n    status->y     = y;\n    status->timer = timer_read();\n\nexit:\n    return report;\n}\n\ncursor_glide_t cursor_glide_check(cursor_glide_context_t* glide) {\n    cursor_glide_t         invalid_report = {0, 0, false};\n    cursor_glide_status_t* status         = &glide->status;\n\n    if (status->z || (status->dx0 == 0 && status->dy0 == 0) || timer_elapsed(status->timer) < glide->config.interval) {\n        return invalid_report;\n    } else {\n        return cursor_glide(glide);\n    }\n}\n\nstatic inline uint16_t sqrt32(uint32_t x) {\n    uint32_t l, m, h;\n\n    if (x == 0) {\n        return 0;\n    } else if (x > (UINT16_MAX >> 2)) {\n        /* Safe upper bound to avoid integer overflow with m * m */\n        h = UINT16_MAX;\n    } else {\n        /* Upper bound based on closest log2 */\n        h = (1 << (((__builtin_clzl(1) - __builtin_clzl(x) + 1) + 1) >> 1));\n    }\n    /* Lower bound based on closest log2 */\n    l = (1 << ((__builtin_clzl(1) - __builtin_clzl(x)) >> 1));\n\n    /* Binary search to find integer square root */\n    while (l != h - 1) {\n        m = (l + h) / 2;\n        if (m * m <= x) {\n            l = m;\n        } else {\n            h = m;\n        }\n    }\n    return l;\n}\n\ncursor_glide_t cursor_glide_start(cursor_glide_context_t* glide) {\n    cursor_glide_t         invalid_report = {0, 0, false};\n    cursor_glide_status_t* status         = &glide->status;\n\n    status->timer   = timer_read();\n    status->counter = 0;\n    status->v0      = (status->dx0 == 0 && status->dy0 == 0) ? 0.0 : sqrt32(((int32_t)status->dx0 * 256 * status->dx0 * 256) + ((int32_t)status->dy0 * 256 * status->dy0 * 256)); // skip trigonometry if not needed, calculate distance in Q8\n    status->x       = 0;\n    status->y       = 0;\n    status->z       = 0;\n\n    if (status->v0 < ((uint32_t)glide->config.trigger_px * 256)) { /* Q8 comparison */\n        /* Not enough velocity to be worth gliding, abort */\n        cursor_glide_stop(glide);\n        return invalid_report;\n    }\n\n    return cursor_glide(glide);\n}\n\nvoid cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z) {\n    cursor_glide_status_t* status = &glide->status;\n\n    status->dx0 = dx;\n    status->dy0 = dy;\n    status->z   = z;\n}\n#endif\n"
  },
  {
    "path": "quantum/pointing_device/pointing_device_gestures.h",
    "content": "/* Copyright 2022 Daniel Kao <daniel.m.kao@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <stdint.h>\n#include \"report.h\"\n\n#ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE\ntypedef struct {\n    mouse_xy_report_t dx;\n    mouse_xy_report_t dy;\n    bool              valid;\n} cursor_glide_t;\n\ntypedef struct {\n    uint16_t trigger_px; /* Pixels of movement needed to trigger cursor glide */\n    uint16_t coef;       /* Coefficient of friction */\n    uint16_t interval;   /* Glide report interval, in milliseconds */\n} cursor_glide_config_t;\n\ntypedef struct {\n    int32_t           v0;\n    int32_t           x;\n    int32_t           y;\n    uint16_t          z;\n    uint16_t          timer;\n    uint16_t          counter;\n    mouse_xy_report_t dx0;\n    mouse_xy_report_t dy0;\n} cursor_glide_status_t;\n\ntypedef struct {\n    cursor_glide_config_t config;\n    cursor_glide_status_t status;\n} cursor_glide_context_t;\n\n/* Check glide report conditions, calculates glide coordinates */\ncursor_glide_t cursor_glide_check(cursor_glide_context_t* glide);\n\n/* Start glide reporting, gives first set of glide coordinates */\ncursor_glide_t cursor_glide_start(cursor_glide_context_t* glide);\n\n/* Update glide engine on the latest cursor movement, cursor glide is based on the final movement */\nvoid cursor_glide_update(cursor_glide_context_t* glide, mouse_xy_report_t dx, mouse_xy_report_t dy, uint16_t z);\n#endif\n"
  },
  {
    "path": "quantum/pointing_device_internal.h",
    "content": "// Copyright 2022 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#ifdef POINTING_DEVICE_DEBUG\n#    include \"debug.h\"\n#    include \"print.h\"\n#    define pd_dprintf(...) dprintf(__VA_ARGS__)\n#else\n#    define pd_dprintf(...) \\\n        do {                \\\n        } while (0)\n#endif\n"
  },
  {
    "path": "quantum/process_keycode/autocorrect_data_default.h",
    "content": "// Generated code.\n\n// Autocorrection dictionary (70 entries):\n//   :guage     -> gauge\n//   :the:the:  -> the\n//   :thier     -> their\n//   :ture      -> true\n//   accomodate -> accommodate\n//   acommodate -> accommodate\n//   aparent    -> apparent\n//   aparrent   -> apparent\n//   apparant   -> apparent\n//   apparrent  -> apparent\n//   aquire     -> acquire\n//   becuase    -> because\n//   cauhgt     -> caught\n//   cheif      -> chief\n//   choosen    -> chosen\n//   cieling    -> ceiling\n//   collegue   -> colleague\n//   concensus  -> consensus\n//   contians   -> contains\n//   cosnt      -> const\n//   dervied    -> derived\n//   fales      -> false\n//   fasle      -> false\n//   fitler     -> filter\n//   flase      -> false\n//   foward     -> forward\n//   frequecy   -> frequency\n//   gaurantee  -> guarantee\n//   guaratee   -> guarantee\n//   heigth     -> height\n//   heirarchy  -> hierarchy\n//   inclued    -> include\n//   interator  -> iterator\n//   intput     -> input\n//   invliad    -> invalid\n//   lenght     -> length\n//   liasion    -> liaison\n//   libary     -> library\n//   listner    -> listener\n//   looses:    -> loses\n//   looup      -> lookup\n//   manefist   -> manifest\n//   namesapce  -> namespace\n//   namespcae  -> namespace\n//   occassion  -> occasion\n//   occured    -> occurred\n//   ouptut     -> output\n//   ouput      -> output\n//   overide    -> override\n//   postion    -> position\n//   priviledge -> privilege\n//   psuedo     -> pseudo\n//   recieve    -> receive\n//   refered    -> referred\n//   relevent   -> relevant\n//   repitition -> repetition\n//   retrun     -> return\n//   retun      -> return\n//   reuslt     -> result\n//   reutrn     -> return\n//   saftey     -> safety\n//   seperate   -> separate\n//   singed     -> signed\n//   stirng     -> string\n//   strign     -> string\n//   swithc     -> switch\n//   swtich     -> switch\n//   thresold   -> threshold\n//   udpate     -> update\n//   widht      -> width\n\n#define AUTOCORRECT_MIN_LENGTH 5  // \":ture\"\n#define AUTOCORRECT_MAX_LENGTH 10 // \"accomodate\"\n\n#define DICTIONARY_SIZE 1104\n\nstatic const uint8_t autocorrect_data[DICTIONARY_SIZE] PROGMEM = {108, 43,  0,   6,   71, 0,  7,   81, 0,   8,   199, 0,   9,   240, 1,  10,  250, 1,  11,  26,  2,   17,  53,  2,   18, 190, 2,   19,  202, 2,   21,  212, 2,   22,  20,  3,   23,  67,  3,   28,  16,  4,   0,  72,  50,  0,   22,  60,  0,   0,   11,  23,  44, 8,   11, 23,  44,  0,   132, 0,   8,   22,  18,  18,  15,  0,  132, 115, 101, 115, 0,   11,  23,  12,  26,  22,  0,   129, 99,  104, 0,   68,  94,  0,   8,   106, 0,   15, 174, 0,   21, 187, 0,   0,   12,  15,  25,  17,  12,  0,   131, 97,  108, 105, 100, 0,   74,  119, 0,   12,  129, 0,   21,  140, 0,   24,  165, 0,   0,   17,  12,  22,  0,   131, 103, 110, 101, 100, 0,   25,  21, 8,   7,   0,   131, 105, 118, 101, 100, 0,   72,  147, 0,  24,  156, 0,  0,   9,   8,   21,  0,   129, 114, 101, 100, 0,   6,   6,   18,  0,   129, 114, 101, 100, 0,   15,  6,   17,  12,  0,   129, 100, 101, 0,   18, 22,  8,   21,  11,  23,  0,   130, 104, 111,\n                                                                  108, 100, 0,   4,   26, 18, 9,   0,  131, 114, 119, 97,  114, 100, 0,  68,  233, 0,  6,   246, 0,   7,   4,   1,   8,  16,  1,   10,  52,  1,   15,  81,  1,   21,  90,  1,   22,  117, 1,   23,  144, 1,   24, 215, 1,   25,  228, 1,   0,   6,   19,  22,  8,  16,  4,  17,  0,   130, 97,  99,  101, 0,   19,  4,   22,  8,  16,  4,   17,  0,   131, 112, 97,  99,  101, 0,   12,  21,  8,   25,  18,  0,   130, 114, 105, 100, 101, 0,  23,  0,   68, 25,  1,   17,  36,  1,   0,   21,  4,   24,  10,  0,   130, 110, 116, 101, 101, 0,   4,   21,  24,  4,   10,  0,   135, 117, 97,  114, 97,  110, 116, 101, 101, 0,   68,  59,  1,   7,   69,  1,   0,  24,  10,  44,  0,   131, 97,  117, 103, 101, 0,   8,   15, 12,  25,  12, 21,  19,  0,   130, 103, 101, 0,   22,  4,   9,   0,   130, 108, 115, 101, 0,   76,  97,  1,   24,  109, 1,   0,   24,  20,  4,   0,   132, 99, 113, 117, 105, 114, 101, 0,   23,  44,  0,\n                                                                  130, 114, 117, 101, 0,  4,  0,   79, 126, 1,   24,  134, 1,   0,   9,  0,   131, 97, 108, 115, 101, 0,   6,   8,   5,  0,   131, 97,  117, 115, 101, 0,   4,   0,   71,  156, 1,   19,  193, 1,   21,  203, 1,  0,   18,  16,  0,   80,  166, 1,   18,  181, 1,  0,   18, 6,   4,   0,   135, 99,  111, 109, 109, 111, 100, 97, 116, 101, 0,   6,   6,   4,   0,   132, 109, 111, 100, 97,  116, 101, 0,   7,   24,  0,   132, 112, 100, 97, 116, 101, 0,  8,   19,  8,   22,  0,   132, 97,  114, 97,  116, 101, 0,   10,  8,   15,  15,  18,  6,   0,   130, 97,  103, 117, 101, 0,   8,   12,  6,   8,   21,  0,   131, 101, 105, 118, 101, 0,   12,  8,   11, 6,   0,   130, 105, 101, 102, 0,   17,  0,   76,  3,   2,  21,  16,  2,  0,   15,  8,   12,  6,   0,   133, 101, 105, 108, 105, 110, 103, 0,   12,  23,  22,  0,   131, 114, 105, 110, 103, 0,   70,  33,  2,   23,  44, 2,   0,   12,  23,  26,  22,  0,   131, 105,\n                                                                  116, 99,  104, 0,   10, 12, 8,   11, 0,   129, 104, 116, 0,   72,  69, 2,   10,  80, 2,   18,  89,  2,   21,  156, 2,  24,  167, 2,   0,   22,  18,  18,  11,  6,   0,   131, 115, 101, 110, 0,   12,  21,  23, 22,  0,   129, 110, 103, 0,   12,  0,   86,  98, 2,   23, 124, 2,   0,   68,  105, 2,   22,  114, 2,   0,   12, 15,  0,   131, 105, 115, 111, 110, 0,   4,   6,   6,   18,  0,   131, 105, 111, 110, 0,   76,  131, 2,   22, 146, 2,   0,  23,  12,  19,  8,   21,  0,   134, 101, 116, 105, 116, 105, 111, 110, 0,   18,  19,  0,   131, 105, 116, 105, 111, 110, 0,   23,  24,  8,   21,  0,   131, 116, 117, 114, 110, 0,   85,  174, 2,   23, 183, 2,   0,   23,  8,   21,  0,   130, 117, 114, 110, 0,  8,   21,  0,  128, 114, 110, 0,   7,   8,   24,  22,  19,  0,   131, 101, 117, 100, 111, 0,   24,  18,  18,  15,  0,   129, 107, 117, 112, 0,   72,  219, 2,  18,  3,   3,   0,   76,  229, 2,   15,  238,\n                                                                  2,   17,  248, 2,   0,  11, 23,  44, 0,   130, 101, 105, 114, 0,   23, 12,  9,   0,  131, 108, 116, 101, 114, 0,   23, 22,  12,  15,  0,   130, 101, 110, 101, 114, 0,   23,  4,   21,  8,   23,  17,  12,  0,  135, 116, 101, 114, 97,  116, 111, 114, 0,   72, 30,  3,  17,  38,  3,   24,  51,  3,   0,   15,  4,   9,   0,  129, 115, 101, 0,   4,   12,  23,  17,  18,  6,   0,   131, 97,  105, 110, 115, 0,   22,  17,  8,   6,   17, 18,  6,   0,  133, 115, 101, 110, 115, 117, 115, 0,   74,  86,  3,   11,  96,  3,   15,  118, 3,   17,  129, 3,   22,  218, 3,   24,  232, 3,   0,   11,  24,  4,   6,   0,   130, 103, 104, 116, 0,   71,  103, 3,  10,  110, 3,   0,   12,  26,  0,   129, 116, 104, 0,   17, 8,   15,  0,  129, 116, 104, 0,   22,  24,  8,   21,  0,   131, 115, 117, 108, 116, 0,   68,  139, 3,   8,   150, 3,   22,  210, 3,   0,   21,  4,   19,  19, 4,   0,   130, 101, 110, 116, 0,   85,  157,\n                                                                  3,   25,  200, 3,   0,  68, 164, 3,  21,  175, 3,   0,   19,  4,   0,  132, 112, 97, 114, 101, 110, 116, 0,   4,   19, 0,   68,  185, 3,   19,  193, 3,   0,   133, 112, 97,  114, 101, 110, 116, 0,   4,   0,  131, 101, 110, 116, 0,   8,   15,  8,   21,  0,  130, 97, 110, 116, 0,   18,  6,   0,   130, 110, 115, 116, 0,  12,  9,   8,   17,  4,   16,  0,   132, 105, 102, 101, 115, 116, 0,   83,  239, 3,   23,  6,   4,   0,   87, 246, 3,   24, 254, 3,   0,   17,  12,  0,   131, 112, 117, 116, 0,   18,  0,   130, 116, 112, 117, 116, 0,   19,  24,  18,  0,   131, 116, 112, 117, 116, 0,   70,  29,  4,   8,   41,  4,   11,  51,  4,   21,  69, 4,   0,   8,   24,  20,  8,   21,  9,   0,   129, 110, 99, 121, 0,   23, 9,   4,   22,  0,   130, 101, 116, 121, 0,   6,   21,  4,   21,  12,  8,   11,  0,   135, 105, 101, 114, 97,  114, 99,  104, 121, 0,   4,   5,  12,  15,  0,   130, 114, 97,  114, 121, 0};\n"
  },
  {
    "path": "quantum/process_keycode/process_achordion.c",
    "content": "// Copyright 2022-2023 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n/**\n * @file achordion.c\n * @brief Achordion implementation\n *\n * For full documentation, see\n * <https://getreuer.info/posts/keyboards/achordion>\n */\n\n#include \"process_achordion.h\"\n\n#if !defined(IS_QK_MOD_TAP)\n// Attempt to detect out-of-date QMK installation, which would fail with\n// implicit-function-declaration errors in the code below.\n#    error \"achordion: QMK version is too old to build. Please update QMK.\"\n#else\n\n// Copy of the `record` and `keycode` args for the current active tap-hold key.\nstatic keyrecord_t tap_hold_record;\nstatic uint16_t    tap_hold_keycode = KC_NO;\n// Timeout timer. When it expires, the key is considered held.\nstatic uint16_t hold_timer = 0;\n// Eagerly applied mods, if any.\nstatic uint8_t eager_mods = 0;\n\n// Achordion's current state.\nenum {\n    // A tap-hold key is pressed, but hasn't yet been settled as tapped or held.\n    STATE_UNSETTLED,\n    // Achordion is inactive.\n    STATE_RELEASED,\n    // Active tap-hold key has been settled as tapped.\n    STATE_TAPPING,\n    // Active tap-hold key has been settled as held.\n    STATE_HOLDING,\n    // This state is set while calling `process_record()`, which will recursively\n    // call `process_achordion()`. This state is checked so that we don't process\n    // events generated by Achordion and potentially create an infinite loop.\n    STATE_RECURSING,\n};\nstatic uint8_t achordion_state = STATE_RELEASED;\n\n// Calls `process_record()` with state set to RECURSING.\nstatic void recursively_process_record(keyrecord_t* record, uint8_t state) {\n    achordion_state = STATE_RECURSING;\n    process_record(record);\n    achordion_state = state;\n}\n\n// Clears eagerly-applied mods.\nstatic void clear_eager_mods(void) {\n    unregister_mods(eager_mods);\n    eager_mods = 0;\n}\n\n// Sends hold press event and settles the active tap-hold key as held.\nstatic void settle_as_hold(void) {\n    clear_eager_mods();\n    // Create hold press event.\n    recursively_process_record(&tap_hold_record, STATE_HOLDING);\n}\n\nbool process_achordion(uint16_t keycode, keyrecord_t* record) {\n    // Don't process events that Achordion generated.\n    if (achordion_state == STATE_RECURSING) {\n        return true;\n    }\n\n    // Determine whether the current event is for a mod-tap or layer-tap key.\n    const bool is_mt       = IS_QK_MOD_TAP(keycode);\n    const bool is_tap_hold = is_mt || IS_QK_LAYER_TAP(keycode);\n    // Check that this is a normal key event, don't act on combos.\n#    ifdef IS_KEYEVENT\n    const bool is_key_event = IS_KEYEVENT(record->event);\n#    else\n    const bool is_key_event = (record->event.key.row < 254 && record->event.key.col < 254);\n#    endif\n\n    if (achordion_state == STATE_RELEASED) {\n        if (is_tap_hold && record->tap.count == 0 && record->event.pressed && is_key_event) {\n            // A tap-hold key is pressed and considered by QMK as \"held\".\n            const uint16_t timeout = achordion_timeout(keycode);\n            if (timeout > 0) {\n                achordion_state = STATE_UNSETTLED;\n                // Save info about this key.\n                tap_hold_keycode = keycode;\n                tap_hold_record  = *record;\n                hold_timer       = record->event.time + timeout;\n\n                if (is_mt) { // Apply mods immediately if they are \"eager.\"\n                    uint8_t mod = mod_config(QK_MOD_TAP_GET_MODS(tap_hold_keycode));\n                    if (achordion_eager_mod(mod)) {\n                        eager_mods = ((mod & 0x10) == 0) ? mod : (mod << 4);\n                        register_mods(eager_mods);\n                    }\n                }\n\n                dprintf(\"Achordion: Key 0x%04X pressed.%s\\n\", keycode, eager_mods ? \" Set eager mods.\" : \"\");\n                return false; // Skip default handling.\n            }\n        }\n\n        return true; // Otherwise, continue with default handling.\n    }\n\n    if (keycode == tap_hold_keycode && !record->event.pressed) {\n        // The active tap-hold key is being released.\n        if (achordion_state == STATE_HOLDING) {\n            dprintln(\"Achordion: Key released. Plumbing hold release.\");\n            tap_hold_record.event.pressed = false;\n            // Plumb hold release event.\n            recursively_process_record(&tap_hold_record, STATE_RELEASED);\n        } else {\n            dprintf(\"Achordion: Key released.%s\\n\", eager_mods ? \" Clearing eager mods.\" : \"\");\n            if (is_mt) {\n                clear_eager_mods();\n            }\n        }\n\n        achordion_state = STATE_RELEASED;\n        return false;\n    }\n\n    if (achordion_state == STATE_UNSETTLED && record->event.pressed) {\n        // Press event occurred on a key other than the active tap-hold key.\n\n        // If the other key is *also* a tap-hold key and considered by QMK to be\n        // held, then we settle the active key as held. This way, things like\n        // chording multiple home row modifiers will work, but let's our logic\n        // consider simply a single tap-hold key as \"active\" at a time.\n        //\n        // Otherwise, we call `achordion_chord()` to determine whether to settle the\n        // tap-hold key as tapped vs. held. We implement the tap or hold by plumbing\n        // events back into the handling pipeline so that QMK features and other\n        // user code can see them. This is done by calling `process_record()`, which\n        // in turn calls most handlers including `process_record_user()`.\n        if (!is_key_event || (is_tap_hold && record->tap.count == 0) || achordion_chord(tap_hold_keycode, &tap_hold_record, keycode, record)) {\n            dprintln(\"Achordion: Plumbing hold press.\");\n            settle_as_hold();\n        } else {\n            clear_eager_mods(); // Clear in case eager mods were set.\n\n            dprintln(\"Achordion: Plumbing tap press.\");\n            tap_hold_record.tap.count       = 1; // Revise event as a tap.\n            tap_hold_record.tap.interrupted = true;\n            // Plumb tap press event.\n            recursively_process_record(&tap_hold_record, STATE_TAPPING);\n\n            send_keyboard_report();\n#    if TAP_CODE_DELAY > 0\n            wait_ms(TAP_CODE_DELAY);\n#    endif // TAP_CODE_DELAY > 0\n\n            dprintln(\"Achordion: Plumbing tap release.\");\n            tap_hold_record.event.pressed = false;\n            // Plumb tap release event.\n            recursively_process_record(&tap_hold_record, STATE_TAPPING);\n        }\n\n        recursively_process_record(record, achordion_state); // Re-process event.\n        return false;                                        // Block the original event.\n    }\n\n    return true;\n}\n\nvoid achordion_task(void) {\n    if (achordion_state == STATE_UNSETTLED && timer_expired(timer_read(), hold_timer)) {\n        dprintln(\"Achordion: Timeout. Plumbing hold press.\");\n        settle_as_hold(); // Timeout expired, settle the key as held.\n    }\n}\n\n// Returns true if `pos` on the left hand of the keyboard, false if right.\nstatic bool on_left_hand(keypos_t pos) {\n#    ifdef SPLIT_KEYBOARD\n    return pos.row < MATRIX_ROWS / 2;\n#    else\n    return (MATRIX_COLS > MATRIX_ROWS) ? pos.col < MATRIX_COLS / 2 : pos.row < MATRIX_ROWS / 2;\n#    endif\n}\n\nbool achordion_opposite_hands(const keyrecord_t* tap_hold_record, const keyrecord_t* other_record) {\n    return on_left_hand(tap_hold_record->event.key) != on_left_hand(other_record->event.key);\n}\n\n// By default, use the BILATERAL_COMBINATIONS rule to consider the tap-hold key\n// \"held\" only when it and the other key are on opposite hands.\n__attribute__((weak)) bool achordion_chord(uint16_t tap_hold_keycode, keyrecord_t* tap_hold_record, uint16_t other_keycode, keyrecord_t* other_record) {\n    return achordion_opposite_hands(tap_hold_record, other_record);\n}\n\n// By default, the timeout is 1000 ms for all keys.\n__attribute__((weak)) uint16_t achordion_timeout(uint16_t tap_hold_keycode) {\n    return 1000;\n}\n\n// By default, Shift and Ctrl mods are eager, and Alt and GUI are not.\n__attribute__((weak)) bool achordion_eager_mod(uint8_t mod) {\n    return (mod & (MOD_LALT | MOD_LGUI)) == 0;\n}\n\n#endif // version check\n"
  },
  {
    "path": "quantum/process_keycode/process_achordion.h",
    "content": "// Copyright 2022-2023 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n/**\n * @file achordion.h\n * @brief Achordion: Customizing the tap-hold decision.\n *\n * Overview\n * --------\n *\n * This library customizes when tap-hold keys are considered held vs. tapped\n * based on the next pressed key, like Manna Harbour's Bilateral Combinations or\n * ZMK's positional hold. The library works on top of QMK's existing tap-hold\n * implementation. You define mod-tap and layer-tap keys as usual and use\n * Achordion to fine-tune the behavior.\n *\n * When QMK settles a tap-hold key as held, Achordion intercepts the event.\n * Achordion then revises the event as a tap or passes it along as a hold:\n *\n *  * Chord condition: On the next key press, a customizable `achordion_chord()`\n *    function is called, which takes the tap-hold key and the next key pressed\n *    as args. When the function returns true, the tap-hold key is settled as\n *    held, and otherwise as tapped.\n *\n *  * Timeout: If no other key press occurs within a timeout, the tap-hold key\n *    is settled as held. This is customizable with `achordion_timeout()`.\n *\n * Achordion only changes the behavior when QMK considered the key held. It\n * changes some would-be holds to taps, but no taps to holds.\n *\n * @note Some QMK features handle events before the point where Achordion can\n * intercept them, particularly: Combos, Key Lock, and Dynamic Macros. It's\n * still possible to use these features and Achordion in your keymap, but beware\n * they might behave poorly when used simultaneously with tap-hold keys.\n *\n *\n * For full documentation, see\n * <https://getreuer.info/posts/keyboards/achordion>\n */\n\n#pragma once\n\n#include \"quantum.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Handler function for Achordion.\n *\n * Call this function from `process_record_user()` as\n *\n *     #include \"features/achordion.h\"\n *\n *     bool process_record_user(uint16_t keycode, keyrecord_t* record) {\n *       if (!process_achordion(keycode, record)) { return false; }\n *       // Your macros...\n *       return true;\n *     }\n */\nbool process_achordion(uint16_t keycode, keyrecord_t* record);\n\n/**\n * Matrix task function for Achordion.\n *\n * Call this function from `matrix_scan_user()` as\n *\n *     void matrix_scan_user(void) {\n *       achordion_task();\n *     }\n */\nvoid achordion_task(void);\n\n/**\n * Optional callback to customize which key chords are considered \"held\".\n *\n * In your keymap.c, define the callback\n *\n *     bool achordion_chord(uint16_t tap_hold_keycode,\n *                          keyrecord_t* tap_hold_record,\n *                          uint16_t other_keycode,\n *                          keyrecord_t* other_record) {\n *        // Conditions...\n *     }\n *\n * This callback is called if while `tap_hold_keycode` is pressed,\n * `other_keycode` is pressed. Return true if the tap-hold key should be\n * considered held, or false to consider it tapped.\n *\n * @param tap_hold_keycode Keycode of the tap-hold key.\n * @param tap_hold_record keyrecord_t from the tap-hold press event.\n * @param other_keycode Keycode of the other key.\n * @param other_record keyrecord_t from the other key's press event.\n * @return True if the tap-hold key should be considered held.\n */\nbool achordion_chord(uint16_t tap_hold_keycode, keyrecord_t* tap_hold_record, uint16_t other_keycode, keyrecord_t* other_record);\n\n/**\n * Optional callback to define a timeout duration per keycode.\n *\n * In your keymap.c, define the callback\n *\n *     uint16_t achordion_timeout(uint16_t tap_hold_keycode) {\n *       // ...\n *     }\n *\n * The callback determines Achordion's timeout duration for `tap_hold_keycode`\n * in units of milliseconds. The timeout be in the range 0 to 32767 ms (upper\n * bound is due to 16-bit timer limitations). Use a timeout of 0 to bypass\n * Achordion.\n *\n * @param tap_hold_keycode Keycode of the tap-hold key.\n * @return Timeout duration in milliseconds in the range 0 to 32767.\n */\nuint16_t achordion_timeout(uint16_t tap_hold_keycode);\n\n/**\n * Optional callback defining which mods are \"eagerly\" applied.\n *\n * This callback defines  which mods are \"eagerly\" applied while a mod-tap\n * key is still being settled. This is helpful to reduce delay particularly when\n * using mod-tap keys with an external mouse.\n *\n * Define this callback in your keymap.c. The default callback is eager for\n * Shift and Ctrl, and not for Alt and GUI:\n *\n *     bool achordion_eager_mod(uint8_t mod) {\n *       return (mod & (MOD_LALT | MOD_LGUI)) == 0;\n *     }\n *\n * @note `mod` should be compared with `MOD_` prefixed codes, not `KC_` codes,\n * described at <https://docs.qmk.fm/#/mod_tap>.\n *\n * @param mod Modifier `MOD_` code.\n * @return True if the modifier should be eagerly applied.\n */\nbool achordion_eager_mod(uint8_t mod);\n\n/**\n * Returns true if the args come from keys on opposite hands.\n *\n * @param tap_hold_record keyrecord_t from the tap-hold key's event.\n * @param other_record keyrecord_t from the other key's event.\n * @return True if the keys are on opposite hands.\n */\nbool achordion_opposite_hands(const keyrecord_t* tap_hold_record, const keyrecord_t* other_record);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/process_keycode/process_audio.c",
    "content": "#include \"audio.h\"\n#include \"process_audio.h\"\n#include <math.h>\n\n#ifndef VOICE_CHANGE_SONG\n#    define VOICE_CHANGE_SONG SONG(VOICE_CHANGE_SOUND)\n#endif\nfloat voice_change_song[][2] = VOICE_CHANGE_SONG;\n\n#ifndef PITCH_STANDARD_A\n#    define PITCH_STANDARD_A 440.0f\n#endif\n\nfloat compute_freq_for_midi_note(uint8_t note) {\n    // https://en.wikipedia.org/wiki/MIDI_tuning_standard\n    return powf(2.0f, (note - 69) / 12.0f) * PITCH_STANDARD_A;\n}\n\nbool process_audio(uint16_t keycode, keyrecord_t *record) {\n    if (keycode == QK_AUDIO_ON && record->event.pressed) {\n        audio_on();\n        return false;\n    }\n\n    if (keycode == QK_AUDIO_OFF && record->event.pressed) {\n        audio_off();\n        return false;\n    }\n\n    if (keycode == QK_AUDIO_TOGGLE && record->event.pressed) {\n        if (is_audio_on()) {\n            audio_off();\n        } else {\n            audio_on();\n        }\n        return false;\n    }\n\n    if (keycode == QK_AUDIO_VOICE_NEXT && record->event.pressed) {\n        voice_iterate();\n        PLAY_SONG(voice_change_song);\n        return false;\n    }\n\n    if (keycode == QK_AUDIO_VOICE_PREVIOUS && record->event.pressed) {\n        voice_deiterate();\n        PLAY_SONG(voice_change_song);\n        return false;\n    }\n\n    return true;\n}\n\nvoid process_audio_noteon(uint8_t note) {\n    play_note(compute_freq_for_midi_note(note), 0xF);\n}\n\nvoid process_audio_noteoff(uint8_t note) {\n    stop_note(compute_freq_for_midi_note(note));\n}\n\nvoid process_audio_all_notes_off(void) {\n    stop_all_notes();\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_audio.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nfloat compute_freq_for_midi_note(uint8_t note);\n\nbool process_audio(uint16_t keycode, keyrecord_t *record);\nvoid process_audio_noteon(uint8_t note);\nvoid process_audio_noteoff(uint8_t note);\nvoid process_audio_all_notes_off(void);\n"
  },
  {
    "path": "quantum/process_keycode/process_auto_shift.c",
    "content": "/* Copyright 2017 Jeremy Cowgar\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_auto_shift.h\"\n#include \"quantum.h\"\n#include \"action_util.h\"\n#include \"timer.h\"\n#include \"keycodes.h\"\n\n#ifndef AUTO_SHIFT_DISABLED_AT_STARTUP\n#    define AUTO_SHIFT_STARTUP_STATE true /* enabled */\n#else\n#    define AUTO_SHIFT_STARTUP_STATE false /* disabled */\n#endif\n\n// Stores the last Auto Shift key's up or down time, for evaluation or keyrepeat.\nstatic uint16_t autoshift_time = 0;\n#if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)\n// Stores the last key's up or down time, to replace autoshift_time so that Tap Hold times are accurate.\nstatic uint16_t retroshift_time = 0;\n// Stores a possibly Retro Shift key's up or down time, as retroshift_time needs\n// to be set before the Retro Shift key is evaluated if it is interrupted by an\n// Auto Shifted key.\nstatic uint16_t last_retroshift_time;\n#endif\nstatic uint16_t    autoshift_timeout = AUTO_SHIFT_TIMEOUT;\nstatic uint16_t    autoshift_lastkey = KC_NO;\nstatic keyrecord_t autoshift_lastrecord;\n// Keys take 8 bits if modifiers are excluded. This records the shift state\n// when pressed for each key, so that can be passed to the release function\n// and it knows which key needs to be released (if shifted is different base).\nstatic uint16_t autoshift_shift_states[((1 << 8) + 15) / 16];\nstatic struct {\n    // Whether Auto Shift is enabled.\n    bool enabled : 1;\n    // Whether the last auto-shifted key was released after the timeout.  This\n    // is used to replicate the last key for a tap-then-hold.\n    bool lastshifted : 1;\n    // Whether an auto-shiftable key has been pressed but not processed.\n    bool in_progress : 1;\n    // Whether the auto-shifted keypress has been registered.\n    bool holding_shift : 1;\n    // Whether the user is holding a shift and we removed it.\n    bool cancelling_lshift : 1;\n    bool cancelling_rshift : 1;\n    // clang-format wants to remove the true for some reason.\n    // clang-format off\n} autoshift_flags = {AUTO_SHIFT_STARTUP_STATE, false, false, false, false, false};\n// clang-format on\n\n/** \\brief Called on physical press, returns whether key should be added to Auto Shift */\n__attribute__((weak)) bool get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {\n    return false;\n}\n\n/** \\brief Called on physical press, returns whether key is an Auto Shift key */\n__attribute__((weak)) bool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {\n    switch (keycode) {\n#ifndef NO_AUTO_SHIFT_ALPHA\n        case AUTO_SHIFT_ALPHA:\n#endif\n#ifndef NO_AUTO_SHIFT_NUMERIC\n        case AUTO_SHIFT_NUMERIC:\n#endif\n#ifndef NO_AUTO_SHIFT_SPECIAL\n#    ifndef NO_AUTO_SHIFT_TAB\n        case KC_TAB:\n#    endif\n#    ifndef NO_AUTO_SHIFT_SYMBOLS\n        case AUTO_SHIFT_SYMBOLS:\n#    endif\n#endif\n#ifdef AUTO_SHIFT_ENTER\n        case KC_ENT:\n#endif\n            return true;\n    }\n    return get_custom_auto_shifted_key(keycode, record);\n}\n\n/** \\brief Called to check whether defines should apply if PER_KEY is set for it */\n__attribute__((weak)) bool get_auto_shift_repeat(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n__attribute__((weak)) bool get_auto_shift_no_auto_repeat(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\n/** \\brief Called when an Auto Shift key needs to be pressed */\n__attribute__((weak)) void autoshift_press_user(uint16_t keycode, bool shifted, keyrecord_t *record) {\n    if (shifted) {\n        add_weak_mods(MOD_BIT(KC_LSFT));\n    }\n    register_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode);\n}\n\n/** \\brief Called when an Auto Shift key needs to be released */\n__attribute__((weak)) void autoshift_release_user(uint16_t keycode, bool shifted, keyrecord_t *record) {\n    unregister_code16((IS_RETRO(keycode)) ? keycode & 0xFF : keycode);\n}\n\n/** \\brief Sets the shift state to use when keyrepeating, required by custom shifts */\nvoid set_autoshift_shift_state(uint16_t keycode, bool shifted) {\n    keycode = keycode & 0xFF;\n    if (shifted) {\n        autoshift_shift_states[keycode / 16] |= (uint16_t)1 << keycode % 16;\n    } else {\n        autoshift_shift_states[keycode / 16] &= ~((uint16_t)1 << keycode % 16);\n    }\n}\n\n/** \\brief Gets the shift state to use when keyrepeating, required by custom shifts */\nbool get_autoshift_shift_state(uint16_t keycode) {\n    keycode = keycode & 0xFF;\n    return (autoshift_shift_states[keycode / 16] & (uint16_t)1 << keycode % 16) != (uint16_t)0;\n}\n\n/** \\brief Restores the shift key if it was cancelled by Auto Shift */\nstatic void autoshift_flush_shift(void) {\n    autoshift_flags.holding_shift = false;\n#ifdef CAPS_WORD_ENABLE\n    if (!is_caps_word_on())\n#endif // CAPS_WORD_ENABLE\n    {\n        del_weak_mods(MOD_BIT(KC_LSFT));\n    }\n    if (autoshift_flags.cancelling_lshift) {\n        autoshift_flags.cancelling_lshift = false;\n        add_mods(MOD_BIT(KC_LSFT));\n    }\n    if (autoshift_flags.cancelling_rshift) {\n        autoshift_flags.cancelling_rshift = false;\n        add_mods(MOD_BIT(KC_RSFT));\n    }\n    send_keyboard_report();\n}\n\n/** \\brief Record the press of an autoshiftable key\n *\n *  \\return Whether the record should be further processed.\n */\nstatic bool autoshift_press(uint16_t keycode, uint16_t now, keyrecord_t *record) {\n    // clang-format off\n    if ((get_mods()\n#if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)\n            | get_oneshot_mods()\n#endif\n        ) & (~MOD_BIT(KC_LSFT))\n    ) {\n        // clang-format on\n        // Prevents keyrepeating unshifted value of key after using it in a key combo.\n        autoshift_lastkey = KC_NO;\n#ifndef AUTO_SHIFT_MODIFIERS\n        // We can't return true here anymore because custom unshifted values are\n        // possible and there's no good way to tell whether the press returned\n        // true upon release.\n        set_autoshift_shift_state(keycode, false);\n        autoshift_press_user(keycode, false, record);\n#    if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)\n        set_oneshot_mods(get_oneshot_mods() & (~MOD_BIT(KC_LSFT)));\n        clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);\n#    endif\n        return false;\n#endif\n    }\n\n    // Store record to be sent to user functions if there's no release record then.\n    autoshift_lastrecord            = *record;\n    autoshift_lastrecord.event.time = 0;\n    // clang-format off\n#if defined(AUTO_SHIFT_REPEAT) || defined(AUTO_SHIFT_REPEAT_PER_KEY)\n    if (keycode == autoshift_lastkey &&\n#    ifdef AUTO_SHIFT_REPEAT_PER_KEY\n        get_auto_shift_repeat(autoshift_lastkey, record) &&\n#    endif\n#    if !defined(AUTO_SHIFT_NO_AUTO_REPEAT) || defined(AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY)\n        (\n            !autoshift_flags.lastshifted\n#        ifdef AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY\n            || get_auto_shift_no_auto_repeat(autoshift_lastkey, record)\n#        endif\n        ) &&\n#    endif\n        TIMER_DIFF_16(now, autoshift_time) < GET_TAPPING_TERM(autoshift_lastkey, record)\n    ) {\n        // clang-format on\n        // Allow a tap-then-hold for keyrepeat.\n        if (get_mods() & MOD_BIT(KC_LSFT)) {\n            autoshift_flags.cancelling_lshift = true;\n            del_mods(MOD_BIT(KC_LSFT));\n        }\n        if (get_mods() & MOD_BIT(KC_RSFT)) {\n            autoshift_flags.cancelling_rshift = true;\n            del_mods(MOD_BIT(KC_RSFT));\n        }\n        // autoshift_shift_state doesn't need to be changed.\n        autoshift_press_user(autoshift_lastkey, autoshift_flags.lastshifted, record);\n        return false;\n    }\n#endif\n\n    // Use physical shift state of press event to be more like normal typing.\n#if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)\n    autoshift_flags.lastshifted = (get_mods() | get_oneshot_mods()) & MOD_BIT(KC_LSFT);\n    set_oneshot_mods(get_oneshot_mods() & (~MOD_BIT(KC_LSFT)));\n#else\n    autoshift_flags.lastshifted = get_mods() & MOD_BIT(KC_LSFT);\n#endif\n    // Record the keycode so we can simulate it later.\n    autoshift_lastkey           = keycode;\n    autoshift_time              = now;\n    autoshift_flags.in_progress = true;\n\n#if !defined(NO_ACTION_ONESHOT) && !defined(NO_ACTION_TAPPING)\n    clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED);\n#endif\n    return false;\n}\n\n/** \\brief Registers an autoshiftable key under the right conditions\n *\n * If autoshift_timeout has elapsed, register a shift and the key.\n *\n * If the Auto Shift key is released before the delay has elapsed, register the\n * key without a shift.\n *\n * Called on key down with keycode=KC_NO, auto-shifted key up, and timeout.\n */\nstatic void autoshift_end(uint16_t keycode, uint16_t now, bool matrix_trigger, keyrecord_t *record) {\n    if (autoshift_flags.in_progress && (keycode == autoshift_lastkey || keycode == KC_NO)) {\n        // Process the auto-shiftable key.\n        autoshift_flags.in_progress = false;\n        // clang-format off\n        autoshift_flags.lastshifted =\n            autoshift_flags.lastshifted\n            || TIMER_DIFF_16(now, autoshift_time) >=\n#ifdef AUTO_SHIFT_TIMEOUT_PER_KEY\n                get_autoshift_timeout(autoshift_lastkey, record)\n#else\n                autoshift_timeout\n#endif\n        ;\n        // clang-format on\n        set_autoshift_shift_state(autoshift_lastkey, autoshift_flags.lastshifted);\n        if (get_mods() & MOD_BIT(KC_LSFT)) {\n            autoshift_flags.cancelling_lshift = true;\n            del_mods(MOD_BIT(KC_LSFT));\n        }\n        if (get_mods() & MOD_BIT(KC_RSFT)) {\n            autoshift_flags.cancelling_rshift = true;\n            del_mods(MOD_BIT(KC_RSFT));\n        }\n        autoshift_press_user(autoshift_lastkey, autoshift_flags.lastshifted, record);\n\n        // clang-format off\n#if (defined(AUTO_SHIFT_REPEAT) || defined(AUTO_SHIFT_REPEAT_PER_KEY)) && (!defined(AUTO_SHIFT_NO_AUTO_REPEAT) || defined(AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY))\n        if (matrix_trigger\n#    ifdef AUTO_SHIFT_REPEAT_PER_KEY\n            && get_auto_shift_repeat(autoshift_lastkey, record)\n#    endif\n#    ifdef AUTO_SHIFT_NO_AUTO_REPEAT_PER_KEY\n            && !get_auto_shift_no_auto_repeat(autoshift_lastkey, record)\n#    endif\n        ) {\n            // Prevents release.\n            return;\n        }\n#endif\n        // clang-format on\n#if TAP_CODE_DELAY > 0\n        wait_ms(TAP_CODE_DELAY);\n#endif\n\n        autoshift_release_user(autoshift_lastkey, autoshift_flags.lastshifted, record);\n        autoshift_flush_shift();\n    } else {\n        // Release after keyrepeat.\n        autoshift_release_user(keycode, get_autoshift_shift_state(keycode), record);\n        if (keycode == autoshift_lastkey) {\n            // This will only fire when the key was the last auto-shiftable\n            // pressed. That prevents 'aaaaBBBB' then releasing a from unshifting\n            // later 'B's (if 'B' wasn't auto-shiftable).\n            autoshift_flush_shift();\n        }\n    }\n    // Roll the autoshift_time forward for detecting tap-and-hold.\n    autoshift_time = now;\n}\n\n/** \\brief Simulates auto-shifted key releases when timeout is hit\n *\n *  Can be called from \\c matrix_scan_user so that auto-shifted keys are sent\n *  immediately after the timeout has expired, rather than waiting for the key\n *  to be released.\n */\nvoid autoshift_matrix_scan(void) {\n    if (autoshift_flags.in_progress) {\n        const uint16_t now = timer_read();\n        if (TIMER_DIFF_16(now, autoshift_time) >=\n#ifdef AUTO_SHIFT_TIMEOUT_PER_KEY\n            get_autoshift_timeout(autoshift_lastkey, &autoshift_lastrecord)\n#else\n            autoshift_timeout\n#endif\n        ) {\n            autoshift_end(autoshift_lastkey, now, true, &autoshift_lastrecord);\n        }\n    }\n}\n\nvoid autoshift_toggle(void) {\n    autoshift_flags.enabled = !autoshift_flags.enabled;\n    autoshift_flush_shift();\n}\n\nvoid autoshift_enable(void) {\n    autoshift_flags.enabled = true;\n}\n\nvoid autoshift_disable(void) {\n    autoshift_flags.enabled = false;\n    autoshift_flush_shift();\n}\n\n#ifndef AUTO_SHIFT_NO_SETUP\nvoid autoshift_timer_report(void) {\n#    ifdef SEND_STRING_ENABLE\n    const char *autoshift_timeout_str = get_u16_str(autoshift_timeout, ' ');\n    // Skip padding spaces\n    while (*autoshift_timeout_str == ' ') {\n        autoshift_timeout_str++;\n    }\n    send_string(autoshift_timeout_str);\n#    endif\n}\n#endif\n\nbool get_autoshift_state(void) {\n    return autoshift_flags.enabled;\n}\n\nuint16_t get_generic_autoshift_timeout(void) {\n    return autoshift_timeout;\n}\n__attribute__((weak)) uint16_t get_autoshift_timeout(uint16_t keycode, keyrecord_t *record) {\n    return autoshift_timeout;\n}\n\nvoid set_autoshift_timeout(uint16_t timeout) {\n    autoshift_timeout = timeout;\n}\n\nbool process_auto_shift(uint16_t keycode, keyrecord_t *record) {\n    // Note that record->event.time isn't reliable, see:\n    // https://github.com/qmk/qmk_firmware/pull/9826#issuecomment-733559550\n    // clang-format off\n    const uint16_t now =\n#if !defined(RETRO_SHIFT) || defined(NO_ACTION_TAPPING)\n        timer_read()\n#else\n        (record->event.pressed) ? retroshift_time : timer_read()\n#endif\n    ;\n    // clang-format on\n\n    if (record->event.pressed) {\n        if (autoshift_flags.in_progress) {\n            // Evaluate previous key if there is one.\n            autoshift_end(KC_NO, now, false, &autoshift_lastrecord);\n        }\n\n        switch (keycode) {\n            case AS_TOGG:\n                autoshift_toggle();\n                break;\n            case AS_ON:\n                autoshift_enable();\n                break;\n            case AS_OFF:\n                autoshift_disable();\n                break;\n\n#ifndef AUTO_SHIFT_NO_SETUP\n            case AS_UP:\n                autoshift_timeout += 5;\n                break;\n            case AS_DOWN:\n                autoshift_timeout -= 5;\n                break;\n            case AS_RPT:\n                autoshift_timer_report();\n                break;\n#endif\n        }\n            // If Retro Shift is disabled, possible custom actions shouldn't happen.\n            // clang-format off\n#if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)\n#    ifdef HOLD_ON_OTHER_KEY_PRESS\n            const bool is_hold_on_interrupt = (IS_QK_MOD_TAP(keycode)\n#        ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY\n                && get_hold_on_other_key_press(keycode, record)\n#        endif\n            );\n#    else\n            const bool is_hold_on_interrupt = false;\n#    endif\n#endif\n        if (IS_RETRO(keycode)\n#if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)\n            // Not tapped or #defines mean that rolls should use hold action.\n            && (\n                record->tap.count == 0\n#    ifdef RETRO_TAPPING_PER_KEY\n                || !get_retro_tapping(keycode, record)\n#    endif\n                || (record->tap.interrupted && is_hold_on_interrupt))\n#endif\n        ) {\n            // clang-format on\n            autoshift_lastkey = KC_NO;\n            return true;\n        }\n    } else {\n        if (keycode == KC_LSFT) {\n            autoshift_flags.cancelling_lshift = false;\n        } else if (keycode == KC_RSFT) {\n            autoshift_flags.cancelling_rshift = false;\n        }\n        // Same as above (for pressed), additional checks are not needed because\n        // tap.count gets set to 0 in process_action\n        // clang-format off\n        else if (IS_RETRO(keycode)\n#if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)\n            && (\n                record->tap.count == 0\n#    ifdef RETRO_TAPPING_PER_KEY\n                || !get_retro_tapping(keycode, record)\n#    endif\n            )\n#endif\n        ) {\n            // Fixes modifiers not being applied to rolls with AUTO_SHIFT_MODIFIERS set.\n#ifdef HOLD_ON_OTHER_KEY_PRESS\n            if (autoshift_flags.in_progress\n#    ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY\n                && get_hold_on_other_key_press(keycode, record)\n#    endif\n            ) {\n                autoshift_end(KC_NO, now, false, &autoshift_lastrecord);\n            }\n#endif\n            // clang-format on\n            return true;\n        }\n    }\n\n    if (!autoshift_flags.enabled) {\n        return true;\n    }\n    if (get_auto_shifted_key(keycode, record)) {\n        if (record->event.pressed) {\n            return autoshift_press(keycode, now, record);\n        } else {\n            autoshift_end(keycode, now, false, record);\n            return false;\n        }\n    }\n\n    // Prevent keyrepeating of older keys upon non-AS key event.\n    // Not commented at above returns but they serve the same function.\n    if (record->event.pressed) {\n        autoshift_lastkey = KC_NO;\n    }\n    return true;\n}\n\n#if defined(RETRO_SHIFT) && !defined(NO_ACTION_TAPPING)\n// Called to record time before possible delays by action_tapping_process.\nvoid retroshift_poll_time(keyevent_t *event) {\n    last_retroshift_time = retroshift_time;\n    retroshift_time      = timer_read();\n}\n// Used to swap the times of Retro Shifted key and Auto Shift key that interrupted it.\nvoid retroshift_swap_times(void) {\n    if (autoshift_flags.in_progress) {\n        autoshift_time = last_retroshift_time;\n    }\n}\n#endif\n"
  },
  {
    "path": "quantum/process_keycode/process_auto_shift.h",
    "content": "/* Copyright 2017 Jeremy Cowgar\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n#include \"keyboard.h\"\n#include \"keycodes.h\"\n\n#ifndef AUTO_SHIFT_TIMEOUT\n#    define AUTO_SHIFT_TIMEOUT 175\n#endif\n\n#define IS_RETRO(kc) (IS_QK_MOD_TAP(kc) || IS_QK_LAYER_TAP(kc))\n\n#define DO_GET_AUTOSHIFT_TIMEOUT(keycode, record, ...) record\n// clang-format off\n#define AUTO_SHIFT_ALPHA KC_A ... KC_Z\n#define AUTO_SHIFT_NUMERIC KC_1 ... KC_0\n#define AUTO_SHIFT_SYMBOLS          \\\n             KC_MINUS ... KC_SLASH: \\\n        case KC_NONUS_BACKSLASH\n\n// Kept to avoid breaking existing keymaps.\n#define AUTO_SHIFT_SPECIAL          \\\n             KC_TAB:                \\\n        case AUTO_SHIFT_SYMBOLS\n// clang-format on\n\nbool process_auto_shift(uint16_t keycode, keyrecord_t *record);\nvoid retroshift_poll_time(keyevent_t *event);\nvoid retroshift_swap_times(void);\n\nvoid     autoshift_enable(void);\nvoid     autoshift_disable(void);\nvoid     autoshift_toggle(void);\nbool     get_autoshift_state(void);\nuint16_t get_generic_autoshift_timeout(void);\n// clang-format off\nuint16_t (get_autoshift_timeout)(uint16_t keycode, keyrecord_t *record);\nvoid     set_autoshift_timeout(uint16_t timeout);\nvoid     autoshift_matrix_scan(void);\nbool     get_custom_auto_shifted_key(uint16_t keycode, keyrecord_t *record);\nbool     get_auto_shifted_key(uint16_t keycode, keyrecord_t *record);\n// clang-format on\n"
  },
  {
    "path": "quantum/process_keycode/process_autocorrect.c",
    "content": "// Copyright 2021 Google LLC\n// Copyright 2021 @filterpaper\n// Copyright 2023 Pablo Martinez (@elpekenin) <elpekenin@elpekenin.dev>\n// SPDX-License-Identifier: Apache-2.0\n// Original source: https://getreuer.info/posts/keyboards/autocorrection\n\n#include \"process_autocorrect.h\"\n#include <string.h>\n#include \"keycodes.h\"\n#include \"quantum_keycodes.h\"\n#include \"keycode_config.h\"\n#include \"send_string.h\"\n#include \"action_util.h\"\n\n#if __has_include(\"autocorrect_data.h\")\n#    include \"autocorrect_data.h\"\n#else\n#    pragma message \"Autocorrect is using the default library.\"\n#    include \"autocorrect_data_default.h\"\n#endif\n\nstatic uint8_t typo_buffer[AUTOCORRECT_MAX_LENGTH] = {KC_SPC};\nstatic uint8_t typo_buffer_size                    = 1;\n\n/**\n * @brief function for querying the enabled state of autocorrect\n *\n * @return true if enabled\n * @return false if disabled\n */\nbool autocorrect_is_enabled(void) {\n    return keymap_config.autocorrect_enable;\n}\n\n/**\n * @brief Enables autocorrect and saves state to eeprom\n *\n */\nvoid autocorrect_enable(void) {\n    keymap_config.autocorrect_enable = true;\n    eeconfig_update_keymap(&keymap_config);\n}\n\n/**\n * @brief Disables autocorrect and saves state to eeprom\n *\n */\nvoid autocorrect_disable(void) {\n    keymap_config.autocorrect_enable = false;\n    typo_buffer_size                 = 0;\n    eeconfig_update_keymap(&keymap_config);\n}\n\n/**\n * @brief Toggles autocorrect's status and save state to eeprom\n *\n */\nvoid autocorrect_toggle(void) {\n    keymap_config.autocorrect_enable = !keymap_config.autocorrect_enable;\n    typo_buffer_size                 = 0;\n    eeconfig_update_keymap(&keymap_config);\n}\n\n/**\n * @brief handler for user to override whether autocorrect should process this keypress\n *\n * @param keycode Keycode registered by matrix press, per keymap\n * @param record keyrecord_t structure\n * @param typo_buffer_size passed along to allow resetting of autocorrect buffer\n * @param mods allow processing of mod status\n * @return true Allow autocorection\n * @return false Stop processing and escape from autocorrect.\n */\n__attribute__((weak)) bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods) {\n    return process_autocorrect_default_handler(keycode, record, typo_buffer_size, mods);\n}\n\n/**\n * @brief fallback handler for determining if autocorrect should process this keypress\n *        can be used by user callback to get the basic keycode being \"wrapped\"\n *\n * NOTE: These values may have been edited by user callback before getting here\n *\n * @param keycode Keycode registered by matrix press, per keymap\n * @param record keyrecord_t structure\n * @param typo_buffer_size passed along to allow resetting of autocorrect buffer\n * @param mods allow processing of mod status\n * @return true Allow autocorection\n * @return false Stop processing and escape from autocorrect.\n */\nbool process_autocorrect_default_handler(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods) {\n    // See quantum_keycodes.h for reference on these matched ranges.\n    switch (*keycode) {\n        // Exclude these keycodes from processing.\n        case KC_LSFT:\n        case KC_RSFT:\n        case KC_CAPS:\n        case QK_TO ... QK_TO_MAX:\n        case QK_MOMENTARY ... QK_MOMENTARY_MAX:\n        case QK_DEF_LAYER ... QK_DEF_LAYER_MAX:\n        case QK_PERSISTENT_DEF_LAYER ... QK_PERSISTENT_DEF_LAYER_MAX:\n        case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:\n        case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:\n        case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:\n        case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:\n        case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:\n            return false;\n\n        // Mask for base keycode from shifted keys.\n        case QK_LSFT ... QK_LSFT + 255:\n        case QK_RSFT ... QK_RSFT + 255:\n            if (*keycode >= QK_LSFT && *keycode <= (QK_LSFT + 255)) {\n                *mods |= MOD_LSFT;\n            } else {\n                *mods |= MOD_RSFT;\n            }\n            *keycode = QK_MODS_GET_BASIC_KEYCODE(*keycode); // Get the basic keycode.\n            return true;\n#ifndef NO_ACTION_TAPPING\n        // Exclude tap-hold keys when they are held down\n        // and mask for base keycode when they are tapped.\n        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:\n#    ifdef NO_ACTION_LAYER\n            // Exclude Layer Tap, if layers are disabled\n            // but action tapping is still enabled.\n            return false;\n#    else\n            // Exclude hold keycode\n            if (!record->tap.count) {\n                return false;\n            }\n            *keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(*keycode);\n            break;\n#    endif\n        case QK_MOD_TAP ... QK_MOD_TAP_MAX:\n            // Exclude hold keycode\n            if (!record->tap.count) {\n                return false;\n            }\n            *keycode = QK_MOD_TAP_GET_TAP_KEYCODE(*keycode);\n            break;\n#else\n        case QK_MOD_TAP ... QK_MOD_TAP_MAX:\n        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:\n            // Exclude if disabled\n            return false;\n#endif\n        // Exclude swap hands keys when they are held down\n        // and mask for base keycode when they are tapped.\n        case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:\n#ifdef SWAP_HANDS_ENABLE\n            // Note: IS_SWAP_HANDS_KEYCODE() actually tests for the special action keycodes like SH_TOGG, SH_TT, ...,\n            // which currently overlap the SH_T(kc) range.\n            if (IS_SWAP_HANDS_KEYCODE(*keycode)\n#    ifndef NO_ACTION_TAPPING\n                || !record->tap.count\n#    endif // NO_ACTION_TAPPING\n            ) {\n                return false;\n            }\n            *keycode = QK_SWAP_HANDS_GET_TAP_KEYCODE(*keycode);\n            break;\n#else\n            // Exclude if disabled\n            return false;\n#endif\n    }\n\n    // Disable autocorrect while a mod other than shift is active.\n    if ((*mods & ~MOD_MASK_SHIFT) != 0) {\n        *typo_buffer_size = 0;\n        return false;\n    }\n\n    return true;\n}\n\n/**\n * @brief handling for when autocorrection has been triggered\n *\n * @param backspaces number of characters to remove\n * @param str pointer to PROGMEM string to replace mistyped seletion with\n * @param typo the wrong string that triggered a correction\n * @param correct what it would become after the changes\n * @return true apply correction\n * @return false user handled replacement\n */\n__attribute__((weak)) bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) {\n    return true;\n}\n\n/**\n * @brief Process handler for autocorrect feature\n *\n * @param keycode Keycode registered by matrix press, per keymap\n * @param record keyrecord_t structure\n * @return true Continue processing keycodes, and send to host\n * @return false Stop processing keycodes, and don't send to host\n */\nbool process_autocorrect(uint16_t keycode, keyrecord_t *record) {\n    uint8_t mods = get_mods();\n#ifndef NO_ACTION_ONESHOT\n    mods |= get_oneshot_mods();\n#endif\n\n    if ((keycode >= QK_AUTOCORRECT_ON && keycode <= QK_AUTOCORRECT_TOGGLE) && record->event.pressed) {\n        if (keycode == QK_AUTOCORRECT_ON) {\n            autocorrect_enable();\n        } else if (keycode == QK_AUTOCORRECT_OFF) {\n            autocorrect_disable();\n        } else if (keycode == QK_AUTOCORRECT_TOGGLE) {\n            autocorrect_toggle();\n        } else {\n            return true;\n        }\n\n        return false;\n    }\n\n    if (!keymap_config.autocorrect_enable) {\n        typo_buffer_size = 0;\n        return true;\n    }\n\n    if (!record->event.pressed) {\n        return true;\n    }\n\n    // autocorrect keycode verification and extraction\n    if (!process_autocorrect_user(&keycode, record, &typo_buffer_size, &mods)) {\n        return true;\n    }\n\n    // keycode buffer check\n    switch (keycode) {\n        case KC_A ... KC_Z:\n            // process normally\n            break;\n        case KC_1 ... KC_0:\n        case KC_TAB ... KC_SEMICOLON:\n        case KC_GRAVE ... KC_SLASH:\n            // Set a word boundary if space, period, digit, etc. is pressed.\n            keycode = KC_SPC;\n            break;\n        case KC_ENTER:\n            // Behave more conservatively for the enter key. Reset, so that enter\n            // can't be used on a word ending.\n            typo_buffer_size = 0;\n            keycode          = KC_SPC;\n            break;\n        case KC_BSPC:\n            // Remove last character from the buffer.\n            if (typo_buffer_size > 0) {\n                --typo_buffer_size;\n            }\n            return true;\n        case KC_QUOTE:\n            // Treat \" (shifted ') as a word boundary.\n            if ((mods & MOD_MASK_SHIFT) != 0) {\n                keycode = KC_SPC;\n            }\n            break;\n        default:\n            // Clear state if some other non-alpha key is pressed.\n            typo_buffer_size = 0;\n            return true;\n    }\n\n    // Rotate oldest character if buffer is full.\n    if (typo_buffer_size >= AUTOCORRECT_MAX_LENGTH) {\n        memmove(typo_buffer, typo_buffer + 1, AUTOCORRECT_MAX_LENGTH - 1);\n        typo_buffer_size = AUTOCORRECT_MAX_LENGTH - 1;\n    }\n\n    // Append `keycode` to buffer.\n    typo_buffer[typo_buffer_size++] = keycode;\n    // Return if buffer is smaller than the shortest word.\n    if (typo_buffer_size < AUTOCORRECT_MIN_LENGTH) {\n        return true;\n    }\n\n    // Check for typo in buffer using a trie stored in `autocorrect_data`.\n    uint16_t state = 0;\n    uint8_t  code  = pgm_read_byte(autocorrect_data + state);\n    for (int8_t i = typo_buffer_size - 1; i >= 0; --i) {\n        uint8_t const key_i = typo_buffer[i];\n\n        if (code & 64) { // Check for match in node with multiple children.\n            code &= 63;\n            for (; code != key_i; code = pgm_read_byte(autocorrect_data + (state += 3))) {\n                if (!code) return true;\n            }\n            // Follow link to child node.\n            state = (pgm_read_byte(autocorrect_data + state + 1) | pgm_read_byte(autocorrect_data + state + 2) << 8);\n            // Check for match in node with single child.\n        } else if (code != key_i) {\n            return true;\n        } else if (!(code = pgm_read_byte(autocorrect_data + (++state)))) {\n            ++state;\n        }\n\n        // Stop if `state` becomes an invalid index. This should not normally\n        // happen, it is a safeguard in case of a bug, data corruption, etc.\n        if (state >= DICTIONARY_SIZE) {\n            return true;\n        }\n\n        code = pgm_read_byte(autocorrect_data + state);\n\n        if (code & 128) { // A typo was found! Apply autocorrect.\n            const uint8_t backspaces = (code & 63) + !record->event.pressed;\n            const char *  changes    = (const char *)(autocorrect_data + state + 1);\n\n            /* Gather info about the typo'd word\n             *\n             * Since buffer may contain several words, delimited by spaces, we\n             * iterate from the end to find the start and length of the typo\n             */\n            char typo[AUTOCORRECT_MAX_LENGTH + 1] = {0}; // extra char for null terminator\n\n            uint8_t typo_len   = 0;\n            uint8_t typo_start = 0;\n            bool    space_last = typo_buffer[typo_buffer_size - 1] == KC_SPC;\n            for (uint8_t i = typo_buffer_size; i > 0; --i) {\n                // stop counting after finding space (unless it is the last thing)\n                if (typo_buffer[i - 1] == KC_SPC && i != typo_buffer_size) {\n                    typo_start = i;\n                    break;\n                }\n\n                ++typo_len;\n            }\n\n            // when detecting 'typo:', reduce the length of the string by one\n            if (space_last) {\n                --typo_len;\n            }\n\n            // convert buffer of keycodes into a string\n            for (uint8_t i = 0; i < typo_len; ++i) {\n                typo[i] = typo_buffer[typo_start + i] - KC_A + 'a';\n            }\n\n            /* Gather the corrected word\n             *\n             * A) Correction of 'typo:' -- Code takes into account\n             * an extra backspace to delete the space (which we dont copy)\n             * for this reason the offset is correct to \"skip\" the null terminator\n             *\n             * B) When correcting 'typo' -- Need extra offset for terminator\n             */\n            char correct[AUTOCORRECT_MAX_LENGTH + 10] = {0}; // let's hope this is big enough\n\n            uint8_t offset = space_last ? backspaces : backspaces + 1;\n            strcpy(correct, typo);\n            strcpy_P(correct + typo_len - offset, changes);\n\n            if (apply_autocorrect(backspaces, changes, typo, correct)) {\n                for (uint8_t i = 0; i < backspaces; ++i) {\n                    tap_code(KC_BSPC);\n                }\n                send_string_P(changes);\n            }\n\n            if (keycode == KC_SPC) {\n                typo_buffer[0]   = KC_SPC;\n                typo_buffer_size = 1;\n                return true;\n            } else {\n                typo_buffer_size = 0;\n                return false;\n            }\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_autocorrect.h",
    "content": "// Copyright 2021 Google LLC\n// Copyright 2021 @filterpaper\n// Copyright 2023 Pablo Martinez (@elpekenin) <elpekenin@elpekenin.dev>\n// SPDX-License-Identifier: Apache-2.0\n// Original source: https://getreuer.info/posts/keyboards/autocorrection\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_autocorrect(uint16_t keycode, keyrecord_t *record);\nbool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods);\nbool process_autocorrect_default_handler(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods);\nbool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct);\n\nbool autocorrect_is_enabled(void);\nvoid autocorrect_enable(void);\nvoid autocorrect_disable(void);\nvoid autocorrect_toggle(void);\n"
  },
  {
    "path": "quantum/process_keycode/process_backlight.c",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_backlight.h\"\n#include \"backlight.h\"\n\nbool process_backlight(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n        switch (keycode) {\n            case QK_BACKLIGHT_ON:\n                backlight_level(BACKLIGHT_LEVELS);\n                return false;\n            case QK_BACKLIGHT_OFF:\n                backlight_level(0);\n                return false;\n            case QK_BACKLIGHT_DOWN:\n                backlight_decrease();\n                return false;\n            case QK_BACKLIGHT_UP:\n                backlight_increase();\n                return false;\n            case QK_BACKLIGHT_TOGGLE:\n                backlight_toggle();\n                return false;\n            case QK_BACKLIGHT_STEP:\n                backlight_step();\n                return false;\n#ifdef BACKLIGHT_BREATHING\n            case QK_BACKLIGHT_TOGGLE_BREATHING:\n                backlight_toggle_breathing();\n                return false;\n#endif\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_backlight.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_backlight(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_caps_word.c",
    "content": "// Copyright 2021-2022 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"process_caps_word.h\"\n#include \"process_auto_shift.h\"\n#include \"process_space_cadet.h\"\n#include \"caps_word.h\"\n#include \"keycodes.h\"\n#include \"quantum_keycodes.h\"\n#include \"modifiers.h\"\n#include \"timer.h\"\n#include \"action_tapping.h\"\n#include \"action_util.h\"\n\n#ifdef CAPS_WORD_INVERT_ON_SHIFT\nstatic uint8_t held_mods = 0;\n\nstatic bool handle_shift(uint16_t keycode, keyrecord_t* record) {\n    switch (keycode) {\n        case OSM(MOD_LSFT):\n            keycode = KC_LSFT;\n            break;\n        case OSM(MOD_RSFT):\n            keycode = KC_RSFT;\n            break;\n\n#    ifndef NO_ACTION_TAPPING\n        case QK_MOD_TAP ... QK_MOD_TAP_MAX:\n            if (record->tap.count == 0) { // Mod-tap key is held.\n                switch (QK_MOD_TAP_GET_MODS(keycode)) {\n                    case MOD_LSFT:\n                        keycode = KC_LSFT;\n                        break;\n                    case MOD_RSFT:\n                        keycode = KC_RSFT;\n                        break;\n                }\n            }\n#    endif // NO_ACTION_TAPPING\n    }\n\n    if (keycode == KC_LSFT || keycode == KC_RSFT) {\n        const uint8_t mod = MOD_BIT(keycode);\n\n        if (is_caps_word_on()) {\n            if (record->event.pressed) {\n                held_mods |= mod;\n            } else {\n                held_mods &= ~mod;\n            }\n            return false;\n        } else if ((held_mods & mod) != 0) {\n            held_mods &= ~mod;\n            del_mods(mod);\n            return record->event.pressed;\n        }\n    }\n\n    return true;\n}\n#endif // CAPS_WORD_INVERT_ON_SHIFT\n\nbool process_caps_word(uint16_t keycode, keyrecord_t* record) {\n    if (keycode == QK_CAPS_WORD_TOGGLE) {\n        if (record->event.pressed) {\n            caps_word_toggle();\n        }\n        return false;\n    }\n#ifdef CAPS_WORD_INVERT_ON_SHIFT\n    if (!handle_shift(keycode, record)) {\n        return false;\n    }\n#endif // CAPS_WORD_INVERT_ON_SHIFT\n\n#ifndef NO_ACTION_ONESHOT\n    const uint8_t mods = get_mods() | get_oneshot_mods();\n#else\n    const uint8_t mods = get_mods();\n#endif // NO_ACTION_ONESHOT\n\n    if (!is_caps_word_on()) {\n        // The following optionally turns on Caps Word by holding left and\n        // right shifts or by double tapping left shift. This way Caps Word\n        // may be used without needing a dedicated key and also without\n        // needing combos or tap dance.\n\n#ifdef BOTH_SHIFTS_TURNS_ON_CAPS_WORD\n        // Many keyboards enable the Command feature by default, which also\n        // uses left+right shift. It can be configured to use a different\n        // key combination by defining IS_COMMAND(). We make a non-fatal\n        // warning if Command is enabled but IS_COMMAND() is *not* defined.\n#    if defined(COMMAND_ENABLE) && !defined(IS_COMMAND)\n#        pragma message \"BOTH_SHIFTS_TURNS_ON_CAPS_WORD and Command should not be enabled at the same time, since both use the Left Shift + Right Shift key combination. Please disable Command, or ensure that `IS_COMMAND` is not set to (get_mods() == MOD_MASK_SHIFT).\"\n#    else\n        if (mods == MOD_MASK_SHIFT\n#        ifdef COMMAND_ENABLE\n            // Don't activate Caps Word at the same time as Command.\n            && !(IS_COMMAND())\n#        endif // COMMAND_ENABLE\n        ) {\n            caps_word_on();\n#        ifdef SPACE_CADET_ENABLE\n            reset_space_cadet();\n#        endif // SPACE_CADET_ENABLE\n        }\n#    endif     // defined(COMMAND_ENABLE) && !defined(IS_COMMAND)\n#endif         // BOTH_SHIFTS_TURNS_ON_CAPS_WORD\n\n#ifdef DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD\n        // Double tapping left shift turns on Caps Word.\n        //\n        // NOTE: This works with KC_LSFT and one-shot left shift. It\n        // wouldn't make sense with mod-tap or Space Cadet shift since\n        // double tapping would of course trigger the tapping action.\n        if (record->event.pressed) {\n            static bool     tapped = false;\n            static uint16_t timer  = 0;\n            if (keycode == KC_LSFT || keycode == OSM(MOD_LSFT)) {\n                if (tapped && !timer_expired(record->event.time, timer)) {\n                    // Left shift was double tapped, activate Caps Word.\n                    caps_word_on();\n                }\n                tapped = true;\n                timer  = record->event.time + GET_TAPPING_TERM(keycode, record);\n            } else {\n                tapped = false; // Reset when any other key is pressed.\n            }\n        }\n#endif // DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD\n\n        return true;\n    }\n\n#if CAPS_WORD_IDLE_TIMEOUT > 0\n    caps_word_reset_idle_timer();\n#endif // CAPS_WORD_IDLE_TIMEOUT > 0\n\n    // From here on, we only take action on press events.\n    if (!record->event.pressed) {\n        return true;\n    }\n\n    if (!(mods & ~(MOD_MASK_SHIFT | MOD_BIT(KC_RALT)))) {\n        switch (keycode) {\n            // Ignore MO, TO, TG, TT, and OSL layer switch keys.\n            case QK_MOMENTARY ... QK_MOMENTARY_MAX:\n            case QK_TO ... QK_TO_MAX:\n            case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:\n            case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:\n            case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:\n#ifdef TRI_LAYER_ENABLE // Ignore Tri Layer keys.\n            case QK_TRI_LAYER_LOWER ... QK_TRI_LAYER_UPPER:\n#endif                   // TRI_LAYER_ENABLE\n#ifdef LAYER_LOCK_ENABLE // Ignore Layer Lock key.\n            case QK_LAYER_LOCK:\n#endif // LAYER_LOCK_ENABLE\n       // Ignore AltGr.\n            case KC_RALT:\n            case OSM(MOD_RALT):\n                return true;\n\n#ifndef NO_ACTION_TAPPING\n            // Corresponding to mod keys above, a held mod-tap is handled as:\n            // * For shift mods, pass KC_LSFT or KC_RSFT to\n            //   caps_word_press_user() to determine whether to continue.\n            // * For Shift + AltGr (MOD_RSFT | MOD_RALT), pass RSFT(KC_RALT).\n            // * AltGr (MOD_RALT) is ignored.\n            // * Otherwise stop Caps Word.\n            case QK_MOD_TAP ... QK_MOD_TAP_MAX:\n                if (record->tap.count == 0) { // Mod-tap key is held.\n                    const uint8_t mods = QK_MOD_TAP_GET_MODS(keycode);\n                    switch (mods) {\n#    ifndef CAPS_WORD_INVERT_ON_SHIFT\n                        case MOD_LSFT:\n                            keycode = KC_LSFT;\n                            break;\n                        case MOD_RSFT:\n                            keycode = KC_RSFT;\n                            break;\n#    endif // CAPS_WORD_INVERT_ON_SHIFT\n                        case MOD_RSFT | MOD_RALT:\n                            keycode = RSFT(KC_RALT);\n                            break;\n                        case MOD_RALT:\n                            return true;\n                        default:\n                            caps_word_off();\n#    ifdef CAPS_WORD_INVERT_ON_SHIFT\n                            add_mods(held_mods);\n#    endif // CAPS_WORD_INVERT_ON_SHIFT\n                            return true;\n                    }\n                } else {\n                    keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode);\n                }\n                break;\n\n#    ifndef NO_ACTION_LAYER\n            case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:\n#    endif // NO_ACTION_LAYER\n                if (record->tap.count == 0) {\n                    return true;\n                }\n                keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode);\n                break;\n#endif // NO_ACTION_TAPPING\n\n#ifdef SWAP_HANDS_ENABLE\n            case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:\n                // Note: IS_SWAP_HANDS_KEYCODE() actually tests for the special action keycodes like SH_TOGG, SH_TT, ...,\n                // which currently overlap the SH_T(kc) range.\n                if (IS_SWAP_HANDS_KEYCODE(keycode)\n#    ifndef NO_ACTION_TAPPING\n                    || record->tap.count == 0\n#    endif // NO_ACTION_TAPPING\n                ) {\n                    return true;\n                }\n                keycode = QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode);\n                break;\n#endif // SWAP_HANDS_ENABLE\n        }\n\n#ifdef AUTO_SHIFT_ENABLE\n        del_weak_mods(get_autoshift_state() ? ~MOD_BIT(KC_LSFT) : 0xff);\n#else\n        clear_weak_mods();\n#endif // AUTO_SHIFT_ENABLE\n        if (caps_word_press_user(keycode)) {\n#ifdef CAPS_WORD_INVERT_ON_SHIFT\n            if (held_mods) {\n                set_weak_mods(get_weak_mods() ^ MOD_BIT(KC_LSFT));\n            }\n#endif // CAPS_WORD_INVERT_ON_SHIFT\n            send_keyboard_report();\n            return true;\n        }\n    }\n\n    caps_word_off();\n#ifdef CAPS_WORD_INVERT_ON_SHIFT\n    add_mods(held_mods);\n#endif // CAPS_WORD_INVERT_ON_SHIFT\n    return true;\n}\n\n__attribute__((weak)) bool caps_word_press_user(uint16_t keycode) {\n    switch (keycode) {\n        // Keycodes that continue Caps Word, with shift applied.\n        case KC_A ... KC_Z:\n        case KC_MINS:\n            add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key.\n            return true;\n\n        // Keycodes that continue Caps Word, without shifting.\n        case KC_1 ... KC_0:\n        case KC_BSPC:\n        case KC_DEL:\n        case KC_UNDS:\n            return true;\n\n        default:\n            return false; // Deactivate Caps Word.\n    }\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_caps_word.h",
    "content": "// Copyright 2021-2022 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\n/**\n * @brief Process handler for Caps Word feature.\n *\n * @param keycode  Keycode registered by matrix press, per keymap\n * @param record   keyrecord_t structure\n * @return true    Continue processing keycodes, and send to host\n * @return false   Stop processing keycodes, and don't send to host\n */\nbool process_caps_word(uint16_t keycode, keyrecord_t* record);\n\n/**\n * @brief Weak function for user-level Caps Word press modification.\n *\n * @param keycode   Keycode registered by matrix press, per keymap\n * @return true     Continue Caps Word\n * @return false    Stop Caps Word\n */\nbool caps_word_press_user(uint16_t keycode);\n"
  },
  {
    "path": "quantum/process_keycode/process_clicky.c",
    "content": "#include \"process_clicky.h\"\n#include \"audio.h\"\n#include \"eeconfig.h\"\n#include <stdlib.h>\n\n#ifdef AUDIO_CLICKY\n\n#    ifndef AUDIO_CLICKY_DELAY_DURATION\n#        define AUDIO_CLICKY_DELAY_DURATION 1\n#    endif // !AUDIO_CLICKY_DELAY_DURATION\n#    ifndef AUDIO_CLICKY_FREQ_DEFAULT\n#        define AUDIO_CLICKY_FREQ_DEFAULT 440.0f\n#    endif // !AUDIO_CLICKY_FREQ_DEFAULT\n#    ifndef AUDIO_CLICKY_FREQ_MIN\n#        define AUDIO_CLICKY_FREQ_MIN 65.0f\n#    endif // !AUDIO_CLICKY_FREQ_MIN\n#    ifndef AUDIO_CLICKY_FREQ_MAX\n#        define AUDIO_CLICKY_FREQ_MAX 1500.0f\n#    endif // !AUDIO_CLICKY_FREQ_MAX\n#    ifndef AUDIO_CLICKY_FREQ_FACTOR\n#        define AUDIO_CLICKY_FREQ_FACTOR 1.18921f\n#    endif // !AUDIO_CLICKY_FREQ_FACTOR\n#    ifndef AUDIO_CLICKY_FREQ_RANDOMNESS\n#        define AUDIO_CLICKY_FREQ_RANDOMNESS 0.05f\n#    endif // !AUDIO_CLICKY_FREQ_RANDOMNESS\n\nfloat clicky_freq = AUDIO_CLICKY_FREQ_DEFAULT;\nfloat clicky_rand = AUDIO_CLICKY_FREQ_RANDOMNESS;\n\n// the first \"note\" is an intentional delay; the 2nd and 3rd notes are the \"clicky\"\nfloat clicky_song[][2] = {{0.0f, AUDIO_CLICKY_DELAY_DURATION}, {AUDIO_CLICKY_FREQ_DEFAULT, 3}, {AUDIO_CLICKY_FREQ_DEFAULT, 1}}; // 3 and 1 --> durations\n\nextern audio_config_t audio_config;\n\n#    ifndef NO_MUSIC_MODE\nextern bool music_activated;\nextern bool midi_activated;\n#    endif // !NO_MUSIC_MODE\n\nvoid clicky_play(void) {\n#    ifndef NO_MUSIC_MODE\n    if (music_activated || midi_activated || !audio_config.enable) return;\n#    endif // !NO_MUSIC_MODE\n    clicky_song[1][0] = 2.0f * clicky_freq * (1.0f + clicky_rand * (((float)rand()) / ((float)(RAND_MAX))));\n    clicky_song[2][0] = clicky_freq * (1.0f + clicky_rand * (((float)rand()) / ((float)(RAND_MAX))));\n    PLAY_SONG(clicky_song);\n}\n\nvoid clicky_freq_up(void) {\n    float new_freq = clicky_freq * AUDIO_CLICKY_FREQ_FACTOR;\n    if (new_freq < AUDIO_CLICKY_FREQ_MAX) {\n        clicky_freq = new_freq;\n    }\n}\n\nvoid clicky_freq_down(void) {\n    float new_freq = clicky_freq / AUDIO_CLICKY_FREQ_FACTOR;\n    if (new_freq > AUDIO_CLICKY_FREQ_MIN) {\n        clicky_freq = new_freq;\n    }\n}\n\nvoid clicky_freq_reset(void) {\n    clicky_freq = AUDIO_CLICKY_FREQ_DEFAULT;\n}\n\nvoid clicky_toggle(void) {\n    audio_config.clicky_enable ^= 1;\n    eeconfig_update_audio(&audio_config);\n}\n\nvoid clicky_on(void) {\n    audio_config.clicky_enable = 1;\n    eeconfig_update_audio(&audio_config);\n}\n\nvoid clicky_off(void) {\n    audio_config.clicky_enable = 0;\n    eeconfig_update_audio(&audio_config);\n}\n\nbool is_clicky_on(void) {\n    return (audio_config.clicky_enable != 0);\n}\n\nbool process_clicky(uint16_t keycode, keyrecord_t *record) {\n    if (keycode == QK_AUDIO_CLICKY_TOGGLE && record->event.pressed) {\n        clicky_toggle();\n    }\n\n    if (keycode == QK_AUDIO_CLICKY_ON && record->event.pressed) {\n        clicky_on();\n    }\n    if (keycode == QK_AUDIO_CLICKY_OFF && record->event.pressed) {\n        clicky_off();\n    }\n\n    if (keycode == QK_AUDIO_CLICKY_RESET && record->event.pressed) {\n        clicky_freq_reset();\n    }\n\n    if (keycode == QK_AUDIO_CLICKY_UP && record->event.pressed) {\n        clicky_freq_up();\n    }\n    if (keycode == QK_AUDIO_CLICKY_DOWN && record->event.pressed) {\n        clicky_freq_down();\n    }\n\n    if (audio_config.enable && audio_config.clicky_enable) {\n        if (record->event.pressed) {                                 // Leave this separate so it's easier to add upstroke sound\n            if (keycode != QK_AUDIO_ON && keycode != QK_AUDIO_OFF) { // DO NOT PLAY if audio will be disabled, and causes issuse on ARM\n                clicky_play();\n            }\n        }\n    }\n    return true;\n}\n\n#endif // AUDIO_CLICKY\n"
  },
  {
    "path": "quantum/process_keycode/process_clicky.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nvoid clicky_play(void);\nbool process_clicky(uint16_t keycode, keyrecord_t *record);\n\nvoid clicky_freq_up(void);\nvoid clicky_freq_down(void);\nvoid clicky_freq_reset(void);\n\nvoid clicky_toggle(void);\nvoid clicky_on(void);\nvoid clicky_off(void);\n\nbool is_clicky_on(void);\n"
  },
  {
    "path": "quantum/process_keycode/process_combo.c",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_combo.h\"\n#include <stddef.h>\n#include \"process_auto_shift.h\"\n#include \"caps_word.h\"\n#include \"timer.h\"\n#include \"wait.h\"\n#include \"keyboard.h\"\n#include \"keymap_common.h\"\n#include \"action_layer.h\"\n#include \"action_tapping.h\"\n#include \"action_util.h\"\n#include \"keymap_introspection.h\"\n\n__attribute__((weak)) void process_combo_event(uint16_t combo_index, bool pressed) {}\n\n#ifndef COMBO_ONLY_FROM_LAYER\n__attribute__((weak)) uint8_t combo_ref_from_layer(uint8_t layer) {\n    return layer;\n}\n#endif\n\n#ifdef COMBO_MUST_HOLD_PER_COMBO\n__attribute__((weak)) bool get_combo_must_hold(uint16_t combo_index, combo_t *combo) {\n    return false;\n}\n#endif\n\n#ifdef COMBO_MUST_TAP_PER_COMBO\n__attribute__((weak)) bool get_combo_must_tap(uint16_t combo_index, combo_t *combo) {\n    return false;\n}\n#endif\n\n#ifdef COMBO_TERM_PER_COMBO\n__attribute__((weak)) uint16_t get_combo_term(uint16_t combo_index, combo_t *combo) {\n    return COMBO_TERM;\n}\n#endif\n\n#ifdef COMBO_MUST_PRESS_IN_ORDER_PER_COMBO\n__attribute__((weak)) bool get_combo_must_press_in_order(uint16_t combo_index, combo_t *combo) {\n    return true;\n}\n#endif\n\n#ifdef COMBO_PROCESS_KEY_RELEASE\n__attribute__((weak)) bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) {\n    return false;\n}\n#endif\n\n#ifdef COMBO_PROCESS_KEY_REPRESS\n__attribute__((weak)) bool process_combo_key_repress(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) {\n    return false;\n}\n#endif\n\n#ifdef COMBO_SHOULD_TRIGGER\n__attribute__((weak)) bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n#endif\n\ntypedef enum { COMBO_KEY_NOT_PRESSED, COMBO_KEY_PRESSED, COMBO_KEY_REPRESSED } combo_key_action_t;\n\n#ifndef COMBO_NO_TIMER\nstatic uint16_t timer = 0;\n#endif\nstatic bool     b_combo_enable = true; // defaults to enabled\nstatic uint16_t longest_term   = 0;\n\ntypedef struct {\n    keyrecord_t record;\n    uint16_t    combo_index;\n    uint16_t    keycode;\n} queued_record_t;\nstatic uint8_t         key_buffer_size = 0;\nstatic queued_record_t key_buffer[COMBO_KEY_BUFFER_LENGTH];\n\ntypedef struct {\n    uint16_t combo_index;\n} queued_combo_t;\nstatic uint8_t        combo_buffer_write = 0;\nstatic uint8_t        combo_buffer_read  = 0;\nstatic queued_combo_t combo_buffer[COMBO_BUFFER_LENGTH];\n\n#define INCREMENT_MOD(i) i = (i + 1) % COMBO_BUFFER_LENGTH\n\n#ifndef EXTRA_SHORT_COMBOS\n/* flags are their own elements in combo_t struct. */\n#    define COMBO_ACTIVE(combo) (combo->active)\n#    define COMBO_DISABLED(combo) (combo->disabled)\n#    define COMBO_STATE(combo) (combo->state)\n\n#    define ACTIVATE_COMBO(combo) \\\n        do {                      \\\n            combo->active = true; \\\n        } while (0)\n#    define DEACTIVATE_COMBO(combo) \\\n        do {                        \\\n            combo->active = false;  \\\n        } while (0)\n#    define DISABLE_COMBO(combo)    \\\n        do {                        \\\n            combo->disabled = true; \\\n        } while (0)\n#    define RESET_COMBO_STATE(combo) \\\n        do {                         \\\n            combo->disabled = false; \\\n            combo->state    = 0;     \\\n        } while (0)\n#else\n/* flags are at the two high bits of state. */\n#    define COMBO_ACTIVE(combo) (combo->state & 0x80)\n#    define COMBO_DISABLED(combo) (combo->state & 0x40)\n#    define COMBO_STATE(combo) (combo->state & 0x3F)\n\n#    define ACTIVATE_COMBO(combo) \\\n        do {                      \\\n            combo->state |= 0x80; \\\n        } while (0)\n#    define DEACTIVATE_COMBO(combo) \\\n        do {                        \\\n            combo->state &= ~0x80;  \\\n        } while (0)\n#    define DISABLE_COMBO(combo)  \\\n        do {                      \\\n            combo->state |= 0x40; \\\n        } while (0)\n#    define RESET_COMBO_STATE(combo) \\\n        do {                         \\\n            combo->state &= ~0x7F;   \\\n        } while (0)\n#endif\n\nstatic inline void release_combo(uint16_t combo_index, combo_t *combo) {\n    if (combo->keycode) {\n        keyrecord_t record = {\n            .event   = MAKE_COMBOEVENT(false),\n            .keycode = combo->keycode,\n        };\n#ifndef NO_ACTION_TAPPING\n        action_tapping_process(record);\n#else\n        process_record(&record);\n#endif\n    } else {\n        process_combo_event(combo_index, false);\n    }\n    DEACTIVATE_COMBO(combo);\n}\n\nstatic inline bool _get_combo_must_hold(uint16_t combo_index, combo_t *combo) {\n#ifdef COMBO_NO_TIMER\n    return false;\n#elif defined(COMBO_MUST_HOLD_PER_COMBO)\n    return get_combo_must_hold(combo_index, combo);\n#elif defined(COMBO_MUST_HOLD_MODS)\n    return (KEYCODE_IS_MOD(combo->keycode) || (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX));\n#endif\n    return false;\n}\n\nstatic inline uint16_t _get_wait_time(uint16_t combo_index, combo_t *combo) {\n    if (_get_combo_must_hold(combo_index, combo)\n#ifdef COMBO_MUST_TAP_PER_COMBO\n        || get_combo_must_tap(combo_index, combo)\n#endif\n    ) {\n        if (longest_term < COMBO_HOLD_TERM) {\n            return COMBO_HOLD_TERM;\n        }\n    }\n\n    return longest_term;\n}\n\nstatic inline uint16_t _get_combo_term(uint16_t combo_index, combo_t *combo) {\n#if defined(COMBO_TERM_PER_COMBO)\n    return get_combo_term(combo_index, combo);\n#endif\n\n    return COMBO_TERM;\n}\n\nvoid clear_combos(void) {\n    uint16_t index = 0;\n    longest_term   = 0;\n    for (index = 0; index < combo_count(); ++index) {\n        combo_t *combo = combo_get(index);\n        if (!COMBO_ACTIVE(combo)) {\n            RESET_COMBO_STATE(combo);\n        }\n    }\n}\n\nstatic inline void dump_key_buffer(void) {\n    /* First call start from 0 index; recursive calls need to start from i+1 index */\n    static uint8_t key_buffer_next = 0;\n#if TAP_CODE_DELAY > 0\n    bool delay_done = false;\n#endif\n\n    if (key_buffer_size == 0) {\n        return;\n    }\n\n    for (uint8_t key_buffer_i = key_buffer_next; key_buffer_i < key_buffer_size; key_buffer_i++) {\n        key_buffer_next = key_buffer_i + 1;\n\n        queued_record_t *qrecord = &key_buffer[key_buffer_i];\n        keyrecord_t *    record  = &qrecord->record;\n\n        if (IS_NOEVENT(record->event)) {\n            continue;\n        }\n\n        if (!record->keycode && qrecord->combo_index != (uint16_t)-1) {\n            process_combo_event(qrecord->combo_index, true);\n        } else {\n#ifndef NO_ACTION_TAPPING\n            action_tapping_process(*record);\n#else\n            process_record(record);\n#endif\n        }\n        record->event.type = TICK_EVENT;\n\n#if defined(CAPS_WORD_ENABLE) && defined(AUTO_SHIFT_ENABLE)\n        // Edge case: preserve the weak Left Shift mod if both Caps Word and\n        // Auto Shift are on. Caps Word capitalizes by setting the weak Left\n        // Shift mod during the press event, but Auto Shift doesn't send the\n        // key until it receives the release event.\n        del_weak_mods((is_caps_word_on() && get_autoshift_state()) ? ~MOD_BIT(KC_LSFT) : 0xff);\n#else\n        clear_weak_mods();\n#endif // defined(CAPS_WORD_ENABLE) && defined(AUTO_SHIFT_ENABLE)\n\n#if TAP_CODE_DELAY > 0\n        // only delay once and for a non-tapping key\n        if (!delay_done && !is_tap_record(record)) {\n            delay_done = true;\n            wait_ms(TAP_CODE_DELAY);\n        }\n#endif\n    }\n\n    key_buffer_next = key_buffer_size = 0;\n}\n\n#define NO_COMBO_KEYS_ARE_DOWN (0 == COMBO_STATE(combo))\n#define ALL_COMBO_KEYS_ARE_DOWN(state, key_count) (((1 << key_count) - 1) == state)\n#define ONLY_ONE_KEY_IS_DOWN(state) !(state & (state - 1))\n#define KEY_NOT_YET_RELEASED(state, key_index) ((1 << key_index) & state)\n#define KEY_STATE_DOWN(state, key_index) \\\n    do {                                 \\\n        state |= (1 << key_index);       \\\n    } while (0)\n#define KEY_STATE_UP(state, key_index) \\\n    do {                               \\\n        state &= ~(1 << key_index);    \\\n    } while (0)\n\nstatic inline void _find_key_index_and_count(const uint16_t *keys, uint16_t keycode, uint16_t *key_index, uint8_t *key_count) {\n    while (true) {\n        uint16_t key = pgm_read_word(&keys[*key_count]);\n        if (keycode == key) *key_index = *key_count;\n        if (COMBO_END == key) break;\n        (*key_count)++;\n    }\n}\n\nvoid drop_combo_from_buffer(uint16_t combo_index) {\n    /* Mark a combo as processed from the buffer. If the buffer is in the\n     * beginning of the buffer, drop it.  */\n    uint8_t i = combo_buffer_read;\n    while (i != combo_buffer_write) {\n        queued_combo_t *qcombo = &combo_buffer[i];\n\n        if (qcombo->combo_index == combo_index) {\n            combo_t *combo = combo_get(combo_index);\n            DISABLE_COMBO(combo);\n\n            if (i == combo_buffer_read) {\n                INCREMENT_MOD(combo_buffer_read);\n            }\n            break;\n        }\n        INCREMENT_MOD(i);\n    }\n}\n\nvoid apply_combo(uint16_t combo_index, combo_t *combo) {\n    /* Apply combo's result keycode to the last chord key of the combo and\n     * disable the other keys. */\n\n    if (COMBO_DISABLED(combo)) {\n        return;\n    }\n\n    // state to check against so we find the last key of the combo from the buffer\n#if defined(EXTRA_EXTRA_LONG_COMBOS)\n    uint32_t state = 0;\n#elif defined(EXTRA_LONG_COMBOS)\n    uint16_t state         = 0;\n#else\n    uint8_t state = 0;\n#endif\n\n    for (uint8_t key_buffer_i = 0; key_buffer_i < key_buffer_size; key_buffer_i++) {\n        queued_record_t *qrecord = &key_buffer[key_buffer_i];\n        keyrecord_t *    record  = &qrecord->record;\n        uint16_t         keycode = qrecord->keycode;\n\n        uint8_t  key_count = 0;\n        uint16_t key_index = -1;\n        _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);\n\n        if (-1 == (int16_t)key_index) {\n            // key not part of this combo\n            continue;\n        }\n\n        KEY_STATE_DOWN(state, key_index);\n        if (ALL_COMBO_KEYS_ARE_DOWN(state, key_count)) {\n            // this in the end executes the combo when the key_buffer is dumped.\n            record->keycode    = combo->keycode;\n            record->event.type = COMBO_EVENT;\n            record->event.key  = MAKE_KEYPOS(0, 0);\n\n            qrecord->combo_index = combo_index;\n            ACTIVATE_COMBO(combo);\n\n            break;\n        } else {\n            // key was part of the combo but not the last one, \"disable\" it\n            // by making it a TICK event.\n            record->event.type = TICK_EVENT;\n        }\n    }\n    drop_combo_from_buffer(combo_index);\n}\n\nstatic inline void apply_combos(void) {\n    // Apply all buffered normal combos.\n    for (uint8_t i = combo_buffer_read; i != combo_buffer_write; INCREMENT_MOD(i)) {\n        queued_combo_t *buffered_combo = &combo_buffer[i];\n        combo_t *       combo          = combo_get(buffered_combo->combo_index);\n\n#ifdef COMBO_MUST_TAP_PER_COMBO\n        if (get_combo_must_tap(buffered_combo->combo_index, combo)) {\n            // Tap-only combos are applied on key release only, so let's drop 'em here.\n            drop_combo_from_buffer(buffered_combo->combo_index);\n            continue;\n        }\n#endif\n        apply_combo(buffered_combo->combo_index, combo);\n    }\n    dump_key_buffer();\n    clear_combos();\n}\n\ncombo_t *overlaps(combo_t *combo1, combo_t *combo2) {\n    /* Checks if the combos overlap and returns the combo that should be\n     * dropped from the combo buffer.\n     * The combo that has less keys will be dropped. If they have the same\n     * amount of keys, drop combo1. */\n\n    uint8_t  idx1 = 0, idx2 = 0;\n    uint16_t key1, key2;\n    bool     overlaps = false;\n\n    while ((key1 = pgm_read_word(&combo1->keys[idx1])) != COMBO_END) {\n        idx2 = 0;\n        while ((key2 = pgm_read_word(&combo2->keys[idx2])) != COMBO_END) {\n            if (key1 == key2) overlaps = true;\n            idx2 += 1;\n        }\n        idx1 += 1;\n    }\n\n    if (!overlaps) return NULL;\n    if (idx2 < idx1) return combo2;\n    return combo1;\n}\n\n#if defined(COMBO_MUST_PRESS_IN_ORDER) || defined(COMBO_MUST_PRESS_IN_ORDER_PER_COMBO)\nstatic bool keys_pressed_in_order(uint16_t combo_index, combo_t *combo, uint16_t key_index, uint16_t keycode, keyrecord_t *record) {\n#    ifdef COMBO_MUST_PRESS_IN_ORDER_PER_COMBO\n    if (!get_combo_must_press_in_order(combo_index, combo)) {\n        return true;\n    }\n#    endif\n    if (\n        // The `state` bit for the key being pressed.\n        (1 << key_index) ==\n        // The *next* combo key's bit.\n        (COMBO_STATE(combo) + 1)\n        // E.g. two keys already pressed: `state == 11`.\n        // Next possible `state` is `111`.\n        // So the needed bit is `100` which we get with `11 + 1`.\n    ) {\n        return true;\n    }\n    return false;\n}\n#endif\n\nstatic combo_key_action_t process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record, uint16_t combo_index) {\n    uint8_t  key_count = 0;\n    uint16_t key_index = -1;\n    _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);\n\n    /* Continue processing if key isn't part of current combo. */\n    if (-1 == (int16_t)key_index) {\n        return COMBO_KEY_NOT_PRESSED;\n    }\n\n    bool key_is_part_of_combo = (!COMBO_DISABLED(combo) && is_combo_enabled()\n#if defined(COMBO_MUST_PRESS_IN_ORDER) || defined(COMBO_MUST_PRESS_IN_ORDER_PER_COMBO)\n                                 && keys_pressed_in_order(combo_index, combo, key_index, keycode, record)\n#endif\n#ifdef COMBO_SHOULD_TRIGGER\n                                 && combo_should_trigger(combo_index, combo, keycode, record)\n#endif\n    );\n\n    if (record->event.pressed && key_is_part_of_combo) {\n        uint16_t time = _get_combo_term(combo_index, combo);\n        if (!COMBO_ACTIVE(combo)) {\n            KEY_STATE_DOWN(combo->state, key_index);\n            if (longest_term < time) {\n                longest_term = time;\n            }\n        }\n        if (ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) {\n            /* Combo was fully pressed */\n            /* Buffer the combo so we can fire it after COMBO_TERM */\n\n#ifndef COMBO_NO_TIMER\n            /* Don't buffer this combo if its combo term has passed. */\n            if (timer && timer_elapsed(timer) > time) {\n                DISABLE_COMBO(combo);\n                return COMBO_KEY_PRESSED;\n            } else\n#endif\n            {\n\n                // disable readied combos that overlap with this combo\n                combo_t *drop = NULL;\n                for (uint8_t combo_buffer_i = combo_buffer_read; combo_buffer_i != combo_buffer_write; INCREMENT_MOD(combo_buffer_i)) {\n                    queued_combo_t *qcombo         = &combo_buffer[combo_buffer_i];\n                    combo_t *       buffered_combo = combo_get(qcombo->combo_index);\n\n                    if ((drop = overlaps(buffered_combo, combo))) {\n                        DISABLE_COMBO(drop);\n                        if (drop == combo) {\n                            // stop checking for overlaps if dropped combo was current combo.\n                            break;\n                        } else if (combo_buffer_i == combo_buffer_read && drop == buffered_combo) {\n                            /* Drop the disabled buffered combo from the buffer if\n                             * it is in the beginning of the buffer. */\n                            INCREMENT_MOD(combo_buffer_read);\n                        }\n                    }\n                }\n\n                if (drop != combo) {\n                    // save this combo to buffer\n                    combo_buffer[combo_buffer_write] = (queued_combo_t){\n                        .combo_index = combo_index,\n                    };\n                    INCREMENT_MOD(combo_buffer_write);\n\n                    // get possible longer waiting time for tap-/hold-only combos.\n                    longest_term = _get_wait_time(combo_index, combo);\n                }\n            } // if timer elapsed end\n        }\n#ifdef COMBO_PROCESS_KEY_REPRESS\n    } else if (record->event.pressed) {\n        if (COMBO_ACTIVE(combo)) {\n            if (process_combo_key_repress(combo_index, combo, key_index, keycode)) {\n                KEY_STATE_DOWN(combo->state, key_index);\n                return COMBO_KEY_REPRESSED;\n            }\n        }\n#endif\n    } else {\n        // chord releases\n        if (!COMBO_ACTIVE(combo) && ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) {\n            /* First key quickly released */\n            if (COMBO_DISABLED(combo) || _get_combo_must_hold(combo_index, combo)) {\n                // combo wasn't tappable, disable it and drop it from buffer.\n                drop_combo_from_buffer(combo_index);\n                key_is_part_of_combo = false;\n            }\n#ifdef COMBO_MUST_TAP_PER_COMBO\n            else if (get_combo_must_tap(combo_index, combo)) {\n                // immediately apply tap-only combo\n                apply_combo(combo_index, combo);\n                apply_combos(); // also apply other prepared combos and dump key buffer\n#    ifdef COMBO_PROCESS_KEY_RELEASE\n                if (process_combo_key_release(combo_index, combo, key_index, keycode)) {\n                    release_combo(combo_index, combo);\n                }\n#    endif\n            }\n#endif\n        } else if (COMBO_ACTIVE(combo) && ONLY_ONE_KEY_IS_DOWN(COMBO_STATE(combo)) && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)) {\n            /* last key released */\n            release_combo(combo_index, combo);\n            key_is_part_of_combo = true;\n\n#ifdef COMBO_PROCESS_KEY_RELEASE\n            process_combo_key_release(combo_index, combo, key_index, keycode);\n#endif\n        } else if (COMBO_ACTIVE(combo) && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)) {\n            /* first or middle key released */\n            key_is_part_of_combo = true;\n\n#ifdef COMBO_PROCESS_KEY_RELEASE\n            if (process_combo_key_release(combo_index, combo, key_index, keycode)) {\n                release_combo(combo_index, combo);\n            }\n#endif\n        } else {\n            /* The released key was part of an incomplete combo */\n            key_is_part_of_combo = false;\n        }\n\n        KEY_STATE_UP(combo->state, key_index);\n    }\n\n    return key_is_part_of_combo ? COMBO_KEY_PRESSED : COMBO_KEY_NOT_PRESSED;\n}\n\nbool process_combo(uint16_t keycode, keyrecord_t *record) {\n    uint8_t is_combo_key          = COMBO_KEY_NOT_PRESSED;\n    bool    no_combo_keys_pressed = true;\n\n    if (keycode == QK_COMBO_ON && record->event.pressed) {\n        combo_enable();\n        return true;\n    }\n\n    if (keycode == QK_COMBO_OFF && record->event.pressed) {\n        combo_disable();\n        return true;\n    }\n\n    if (keycode == QK_COMBO_TOGGLE && record->event.pressed) {\n        combo_toggle();\n        return true;\n    }\n\n#ifdef COMBO_ONLY_FROM_LAYER\n    /* Only check keycodes from one layer. */\n    keycode = keymap_key_to_keycode(COMBO_ONLY_FROM_LAYER, record->event.key);\n#else\n    uint8_t  highest_layer = get_highest_layer(layer_state | default_layer_state);\n    uint8_t  ref_layer     = combo_ref_from_layer(highest_layer);\n    if (ref_layer != highest_layer) {\n        keycode = keymap_key_to_keycode(ref_layer, record->event.key);\n    }\n#endif\n\n    for (uint16_t idx = 0; idx < combo_count(); ++idx) {\n        combo_t *combo = combo_get(idx);\n        is_combo_key |= process_single_combo(combo, keycode, record, idx);\n        no_combo_keys_pressed = no_combo_keys_pressed && (NO_COMBO_KEYS_ARE_DOWN || COMBO_ACTIVE(combo) || COMBO_DISABLED(combo));\n    }\n\n    if (record->event.pressed && is_combo_key) {\n#ifndef COMBO_NO_TIMER\n#    ifdef COMBO_STRICT_TIMER\n        if (!timer) {\n            // timer is set only on the first key\n            timer = timer_read();\n        }\n#    else\n        timer = timer_read();\n#    endif\n#endif\n\n#ifdef COMBO_PROCESS_KEY_REPRESS\n        if (is_combo_key == COMBO_KEY_PRESSED)\n#endif\n        {\n            if (key_buffer_size < COMBO_KEY_BUFFER_LENGTH) {\n                key_buffer[key_buffer_size++] = (queued_record_t){\n                    .record      = *record,\n                    .keycode     = keycode,\n                    .combo_index = -1, // this will be set when applying combos\n                };\n            }\n        }\n    } else {\n        if (combo_buffer_read != combo_buffer_write) {\n            // some combo is prepared\n            apply_combos();\n        } else {\n            // reset state if there are no combo keys pressed at all\n            dump_key_buffer();\n#ifndef COMBO_NO_TIMER\n            timer = 0;\n#endif\n            clear_combos();\n        }\n    }\n    return !is_combo_key;\n}\n\nvoid combo_task(void) {\n    if (!b_combo_enable) {\n        return;\n    }\n\n#ifndef COMBO_NO_TIMER\n    if (timer && timer_elapsed(timer) > longest_term) {\n        if (combo_buffer_read != combo_buffer_write) {\n            apply_combos();\n            longest_term = 0;\n            timer        = 0;\n        } else {\n            dump_key_buffer();\n            timer = 0;\n            clear_combos();\n        }\n    }\n#endif\n}\n\nvoid combo_enable(void) {\n    b_combo_enable = true;\n}\n\nvoid combo_disable(void) {\n#ifndef COMBO_NO_TIMER\n    timer = 0;\n#endif\n    b_combo_enable    = false;\n    combo_buffer_read = combo_buffer_write;\n    clear_combos();\n    dump_key_buffer();\n}\n\nvoid combo_toggle(void) {\n    if (b_combo_enable) {\n        combo_disable();\n    } else {\n        combo_enable();\n    }\n}\n\nbool is_combo_enabled(void) {\n    return b_combo_enable;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_combo.h",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n#include \"keycodes.h\"\n#include \"quantum_keycodes.h\"\n\n#ifdef EXTRA_SHORT_COMBOS\n#    define MAX_COMBO_LENGTH 6\n#elif defined(EXTRA_EXTRA_LONG_COMBOS)\n#    define MAX_COMBO_LENGTH 32\n#elif defined(EXTRA_LONG_COMBOS)\n#    define MAX_COMBO_LENGTH 16\n#else\n#    define MAX_COMBO_LENGTH 8\n#endif\n\n#ifndef COMBO_KEY_BUFFER_LENGTH\n#    define COMBO_KEY_BUFFER_LENGTH MAX_COMBO_LENGTH\n#endif\n#ifndef COMBO_BUFFER_LENGTH\n#    define COMBO_BUFFER_LENGTH 4\n#endif\n\ntypedef struct combo_t {\n    const uint16_t *keys;\n    uint16_t        keycode;\n#ifdef EXTRA_SHORT_COMBOS\n    uint8_t state;\n#else\n    bool     disabled;\n    bool     active;\n#    if defined(EXTRA_EXTRA_LONG_COMBOS)\n    uint32_t state;\n#    elif defined(EXTRA_LONG_COMBOS)\n    uint16_t state;\n#    else\n    uint8_t state;\n#    endif\n#endif\n} combo_t;\n\n#define COMBO(ck, ca) \\\n    { .keys = &(ck)[0], .keycode = (ca) }\n#define COMBO_ACTION(ck) \\\n    { .keys = &(ck)[0] }\n\n#define COMBO_END 0\n#ifndef COMBO_TERM\n#    define COMBO_TERM 50\n#endif\n#ifndef COMBO_HOLD_TERM\n#    define COMBO_HOLD_TERM TAPPING_TERM\n#endif\n\n/* check if keycode is only modifiers */\n#define KEYCODE_IS_MOD(code) (IS_MODIFIER_KEYCODE(code) || (IS_QK_MODS(code) && !QK_MODS_GET_BASIC_KEYCODE(code)))\n\nbool process_combo(uint16_t keycode, keyrecord_t *record);\nvoid combo_task(void);\nvoid process_combo_event(uint16_t combo_index, bool pressed);\n\nvoid combo_enable(void);\nvoid combo_disable(void);\nvoid combo_toggle(void);\nbool is_combo_enabled(void);\n"
  },
  {
    "path": "quantum/process_keycode/process_connection.c",
    "content": "// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include \"connection.h\"\n#include \"process_connection.h\"\n\nbool process_connection(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n        switch (keycode) {\n            case QK_OUTPUT_NEXT:\n                connection_next_host();\n                return false;\n            case QK_OUTPUT_PREV:\n                connection_prev_host();\n                return false;\n\n            case QK_OUTPUT_AUTO:\n                connection_set_host(CONNECTION_HOST_AUTO);\n                return false;\n            case QK_OUTPUT_NONE:\n                connection_set_host(CONNECTION_HOST_NONE);\n                return false;\n            case QK_OUTPUT_USB:\n                connection_set_host(CONNECTION_HOST_USB);\n                return false;\n            case QK_OUTPUT_BLUETOOTH:\n                connection_set_host(CONNECTION_HOST_BLUETOOTH);\n                return false;\n            case QK_OUTPUT_2P4GHZ:\n                connection_set_host(CONNECTION_HOST_2P4GHZ);\n                return false;\n\n            case QK_BLUETOOTH_PROFILE_NEXT:\n            case QK_BLUETOOTH_PROFILE_PREV:\n            case QK_BLUETOOTH_UNPAIR:\n            case QK_BLUETOOTH_PROFILE1:\n            case QK_BLUETOOTH_PROFILE2:\n            case QK_BLUETOOTH_PROFILE3:\n            case QK_BLUETOOTH_PROFILE4:\n            case QK_BLUETOOTH_PROFILE5:\n                // As-yet unimplemented.\n                // When implementation is done, ensure `docs/keycodes.md`, `docs/features/bluetooth.md` are updated accordingly.\n                return false;\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_connection.h",
    "content": "// Copyright 2024 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_connection(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_default_layer.c",
    "content": "/* Copyright 2023 Nebuleon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"process_default_layer.h\"\n#include \"quantum.h\"\n#include \"quantum_keycodes.h\"\n\n#if !defined(NO_ACTION_LAYER)\n\nbool process_default_layer(uint16_t keycode, keyrecord_t *record) {\n    if (IS_QK_PERSISTENT_DEF_LAYER(keycode) && !record->event.pressed) {\n        uint8_t layer = QK_PERSISTENT_DEF_LAYER_GET_LAYER(keycode);\n        set_single_persistent_default_layer(layer);\n        return false;\n    }\n\n    return true;\n}\n\n#endif // !defined(NO_ACTION_LAYER)\n"
  },
  {
    "path": "quantum/process_keycode/process_default_layer.h",
    "content": "/* Copyright 2023 Nebuleon\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\n#if !defined(NO_ACTION_LAYER)\n\nbool process_default_layer(uint16_t keycode, keyrecord_t *record);\n\n#endif // !defined(NO_ACTION_LAYER)\n"
  },
  {
    "path": "quantum/process_keycode/process_dynamic_macro.c",
    "content": "/* Copyright 2016 Jack Humbert\n * Copyright 2019 Drashna Jael're (@drashna, aka Christopher Courtney)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* Author: Wojciech Siewierski < wojciech dot siewierski at onet dot pl > */\n#include \"process_dynamic_macro.h\"\n#include <stddef.h>\n#include \"action_layer.h\"\n#include \"keycodes.h\"\n#include \"debug.h\"\n#include \"wait.h\"\n\n#ifdef BACKLIGHT_ENABLE\n#    include \"backlight.h\"\n#endif\n\n// default feedback method\nvoid dynamic_macro_led_blink(void) {\n#ifdef BACKLIGHT_ENABLE\n    backlight_toggle();\n    wait_ms(100);\n    backlight_toggle();\n#endif\n}\n\n/* User hooks for Dynamic Macros */\n\n__attribute__((weak)) bool dynamic_macro_record_start_kb(int8_t direction) {\n    return dynamic_macro_record_start_user(direction);\n}\n\n__attribute__((weak)) bool dynamic_macro_record_start_user(int8_t direction) {\n    dynamic_macro_led_blink();\n    return true;\n}\n\n__attribute__((weak)) bool dynamic_macro_play_kb(int8_t direction) {\n    return dynamic_macro_play_user(direction);\n}\n\n__attribute__((weak)) bool dynamic_macro_play_user(int8_t direction) {\n    dynamic_macro_led_blink();\n    return true;\n}\n\n__attribute__((weak)) bool dynamic_macro_record_key_kb(int8_t direction, keyrecord_t *record) {\n    return dynamic_macro_record_key_user(direction, record);\n}\n\n__attribute__((weak)) bool dynamic_macro_record_key_user(int8_t direction, keyrecord_t *record) {\n    dynamic_macro_led_blink();\n    return true;\n}\n\n__attribute__((weak)) bool dynamic_macro_record_end_kb(int8_t direction) {\n    return dynamic_macro_record_end_user(direction);\n}\n\n__attribute__((weak)) bool dynamic_macro_record_end_user(int8_t direction) {\n    dynamic_macro_led_blink();\n    return true;\n}\n\n__attribute__((weak)) bool dynamic_macro_valid_key_kb(uint16_t keycode, keyrecord_t *record) {\n    return dynamic_macro_valid_key_user(keycode, record);\n}\n\n__attribute__((weak)) bool dynamic_macro_valid_key_user(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\n/* Convenience macros used for retrieving the debug info. All of them\n * need a `direction` variable accessible at the call site.\n */\n#define DYNAMIC_MACRO_CURRENT_SLOT() (direction > 0 ? 1 : 2)\n#define DYNAMIC_MACRO_CURRENT_LENGTH(BEGIN, POINTER) ((int)(direction * ((POINTER) - (BEGIN))))\n#define DYNAMIC_MACRO_CURRENT_CAPACITY(BEGIN, END2) ((int)(direction * ((END2) - (BEGIN)) + 1))\n\n/**\n * Start recording of the dynamic macro.\n *\n * @param[out] macro_pointer The new macro buffer iterator.\n * @param[in]  macro_buffer  The macro buffer used to initialize macro_pointer.\n */\nvoid dynamic_macro_record_start(keyrecord_t **macro_pointer, keyrecord_t *macro_buffer, int8_t direction) {\n    dprintln(\"dynamic macro recording: started\");\n\n    dynamic_macro_record_start_kb(direction);\n\n    clear_keyboard();\n    layer_clear();\n    *macro_pointer = macro_buffer;\n}\n\n/**\n * Play the dynamic macro.\n *\n * @param macro_buffer[in] The beginning of the macro buffer being played.\n * @param macro_end[in]    The element after the last macro buffer element.\n * @param direction[in]    Either +1 or -1, which way to iterate the buffer.\n */\nvoid dynamic_macro_play(keyrecord_t *macro_buffer, keyrecord_t *macro_end, int8_t direction) {\n    dprintf(\"dynamic macro: slot %d playback\\n\", DYNAMIC_MACRO_CURRENT_SLOT());\n\n    layer_state_t saved_layer_state = layer_state;\n\n    clear_keyboard();\n    layer_clear();\n\n    while (macro_buffer != macro_end) {\n        process_record(macro_buffer);\n        macro_buffer += direction;\n#ifdef DYNAMIC_MACRO_DELAY\n        wait_ms(DYNAMIC_MACRO_DELAY);\n#endif\n    }\n\n    clear_keyboard();\n\n    layer_state_set(saved_layer_state);\n\n    dynamic_macro_play_kb(direction);\n}\n\n/**\n * Record a single key in a dynamic macro.\n *\n * @param macro_buffer[in] The start of the used macro buffer.\n * @param macro_pointer[in,out] The current buffer position.\n * @param macro2_end[in] The end of the other macro.\n * @param direction[in]  Either +1 or -1, which way to iterate the buffer.\n * @param record[in]     The current keypress.\n */\nvoid dynamic_macro_record_key(keyrecord_t *macro_buffer, keyrecord_t **macro_pointer, keyrecord_t *macro2_end, int8_t direction, keyrecord_t *record) {\n    /* If we've just started recording, ignore all the key releases. */\n    if (!record->event.pressed && *macro_pointer == macro_buffer) {\n        dprintln(\"dynamic macro: ignoring a leading key-up event\");\n        return;\n    }\n\n    /* The other end of the other macro is the last buffer element it\n     * is safe to use before overwriting the other macro.\n     */\n    if (*macro_pointer - direction != macro2_end) {\n        **macro_pointer = *record;\n        *macro_pointer += direction;\n    }\n    dynamic_macro_record_key_kb(direction, record);\n\n    dprintf(\"dynamic macro: slot %d length: %d/%d\\n\", DYNAMIC_MACRO_CURRENT_SLOT(), DYNAMIC_MACRO_CURRENT_LENGTH(macro_buffer, *macro_pointer), DYNAMIC_MACRO_CURRENT_CAPACITY(macro_buffer, macro2_end));\n}\n\n/**\n * End recording of the dynamic macro. Essentially just update the\n * pointer to the end of the macro.\n */\nvoid dynamic_macro_record_end(keyrecord_t *macro_buffer, keyrecord_t *macro_pointer, int8_t direction, keyrecord_t **macro_end) {\n    dynamic_macro_record_end_kb(direction);\n\n    /* Do not save the keys being held when stopping the recording,\n     * i.e. the keys used to access the layer DM_RSTP is on.\n     */\n    while (macro_pointer != macro_buffer && (macro_pointer - direction)->event.pressed) {\n        dprintln(\"dynamic macro: trimming a trailing key-down event\");\n        macro_pointer -= direction;\n    }\n\n    dprintf(\"dynamic macro: slot %d saved, length: %d\\n\", DYNAMIC_MACRO_CURRENT_SLOT(), DYNAMIC_MACRO_CURRENT_LENGTH(macro_buffer, macro_pointer));\n\n    *macro_end = macro_pointer;\n}\n\n/* Both macros use the same buffer but read/write on different\n * ends of it.\n *\n * Macro1 is written left-to-right starting from the beginning of\n * the buffer.\n *\n * Macro2 is written right-to-left starting from the end of the\n * buffer.\n *\n * &macro_buffer   macro_end\n *  v                   v\n * +------------------------------------------------------------+\n * |>>>>>> MACRO1 >>>>>>      <<<<<<<<<<<<< MACRO2 <<<<<<<<<<<<<|\n * +------------------------------------------------------------+\n *                           ^                                 ^\n *                         r_macro_end                  r_macro_buffer\n *\n * During the recording when one macro encounters the end of the\n * other macro, the recording is stopped. Apart from this, there\n * are no arbitrary limits for the macros' length in relation to\n * each other: for example one can either have two medium sized\n * macros or one long macro and one short macro. Or even one empty\n * and one using the whole buffer.\n */\nstatic keyrecord_t macro_buffer[DYNAMIC_MACRO_SIZE];\n\n/* Pointer to the first buffer element after the first macro.\n * Initially points to the very beginning of the buffer since the\n * macro is empty. */\nstatic keyrecord_t *macro_end = macro_buffer;\n\n/* The other end of the macro buffer. Serves as the beginning of\n * the second macro. */\nstatic keyrecord_t *const r_macro_buffer = macro_buffer + DYNAMIC_MACRO_SIZE - 1;\n\n/* Like macro_end but for the second macro. */\nstatic keyrecord_t *r_macro_end = macro_buffer + DYNAMIC_MACRO_SIZE - 1;\n\n/* A persistent pointer to the current macro position (iterator)\n * used during the recording. */\nstatic keyrecord_t *macro_pointer = NULL;\n\n/* 0   - no macro is being recorded right now\n * 1,2 - either macro 1 or 2 is being recorded */\nstatic uint8_t macro_id = 0;\n\n/**\n * If a dynamic macro is currently being recorded, stop recording.\n */\nvoid dynamic_macro_stop_recording(void) {\n    switch (macro_id) {\n        case 1:\n            dynamic_macro_record_end(macro_buffer, macro_pointer, +1, &macro_end);\n            break;\n        case 2:\n            dynamic_macro_record_end(r_macro_buffer, macro_pointer, -1, &r_macro_end);\n            break;\n    }\n    macro_id = 0;\n}\n\n/* Handle the key events related to the dynamic macros.\n */\nbool process_dynamic_macro(uint16_t keycode, keyrecord_t *record) {\n    if (macro_id == 0) {\n        /* No macro recording in progress. */\n        if (!record->event.pressed) {\n            switch (keycode) {\n                case QK_DYNAMIC_MACRO_RECORD_START_1:\n                    dynamic_macro_record_start(&macro_pointer, macro_buffer, +1);\n                    macro_id = 1;\n                    return false;\n                case QK_DYNAMIC_MACRO_RECORD_START_2:\n                    dynamic_macro_record_start(&macro_pointer, r_macro_buffer, -1);\n                    macro_id = 2;\n                    return false;\n                case QK_DYNAMIC_MACRO_PLAY_1:\n                    dynamic_macro_play(macro_buffer, macro_end, +1);\n                    return false;\n                case QK_DYNAMIC_MACRO_PLAY_2:\n                    dynamic_macro_play(r_macro_buffer, r_macro_end, -1);\n                    return false;\n            }\n        }\n    } else {\n        /* A macro is being recorded right now. */\n        switch (keycode) {\n            case QK_DYNAMIC_MACRO_RECORD_START_1:\n            case QK_DYNAMIC_MACRO_RECORD_START_2:\n            case QK_DYNAMIC_MACRO_RECORD_STOP:\n                /* Stop the macro recording. */\n                if (record->event.pressed ^ (keycode != QK_DYNAMIC_MACRO_RECORD_STOP)) { /* Ignore the initial release\n                                                                                          * just after the recording\n                                                                                          * starts for DM_RSTP. */\n                    dynamic_macro_stop_recording();\n                }\n                return false;\n#ifdef DYNAMIC_MACRO_NO_NESTING\n            case QK_DYNAMIC_MACRO_PLAY_1:\n            case QK_DYNAMIC_MACRO_PLAY_2:\n                dprintln(\"dynamic macro: ignoring macro play key while recording\");\n                return false;\n#endif\n            default:\n                if (dynamic_macro_valid_key_kb(keycode, record)) {\n                    /* Store the key in the macro buffer and process it normally. */\n                    switch (macro_id) {\n                        case 1:\n                            dynamic_macro_record_key(macro_buffer, &macro_pointer, r_macro_end, +1, record);\n                            break;\n                        case 2:\n                            dynamic_macro_record_key(r_macro_buffer, &macro_pointer, macro_end, -1, record);\n                            break;\n                    }\n                }\n                return true;\n                break;\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_dynamic_macro.h",
    "content": "/* Copyright 2016 Jack Humbert\n * Copyright 2019 Drashna Jael're (@drashna, aka Christopher Courtney)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/* Author: Wojciech Siewierski < wojciech dot siewierski at onet dot pl > */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\n/* May be overridden with a custom value. Be aware that the effective\n * macro length is half of this value: each keypress is recorded twice\n * because of the down-event and up-event. This is not a bug, it's the\n * intended behavior.\n *\n * Usually it should be fine to set the macro size to at least 256 but\n * there have been reports of it being too much in some users' cases,\n * so 128 is considered a safe default.\n */\n#ifndef DYNAMIC_MACRO_SIZE\n#    define DYNAMIC_MACRO_SIZE 128\n#endif\n\nvoid dynamic_macro_led_blink(void);\nbool process_dynamic_macro(uint16_t keycode, keyrecord_t *record);\nbool dynamic_macro_record_start_kb(int8_t direction);\nbool dynamic_macro_record_start_user(int8_t direction);\nbool dynamic_macro_play_kb(int8_t direction);\nbool dynamic_macro_play_user(int8_t direction);\nbool dynamic_macro_record_key_kb(int8_t direction, keyrecord_t *record);\nbool dynamic_macro_record_key_user(int8_t direction, keyrecord_t *record);\nbool dynamic_macro_record_end_kb(int8_t direction);\nbool dynamic_macro_record_end_user(int8_t direction);\nbool dynamic_macro_valid_key_kb(uint16_t keycode, keyrecord_t *record);\nbool dynamic_macro_valid_key_user(uint16_t keycode, keyrecord_t *record);\nvoid dynamic_macro_stop_recording(void);\n"
  },
  {
    "path": "quantum/process_keycode/process_dynamic_tapping_term.c",
    "content": "/* Copyright 2020 Vladislav Kucheriavykh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_dynamic_tapping_term.h\"\n#include \"quantum.h\"\n#include \"keycodes.h\"\n#include \"send_string.h\"\n\n#ifndef DYNAMIC_TAPPING_TERM_INCREMENT\n#    define DYNAMIC_TAPPING_TERM_INCREMENT 5\n#endif\n\nstatic void tapping_term_report(void) {\n#ifdef SEND_STRING_ENABLE\n    const char *tapping_term_str = get_u16_str(g_tapping_term, ' ');\n    // Skip padding spaces\n    while (*tapping_term_str == ' ') {\n        tapping_term_str++;\n    }\n    send_string(tapping_term_str);\n#endif\n}\n\nbool process_dynamic_tapping_term(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n        switch (keycode) {\n            case QK_DYNAMIC_TAPPING_TERM_PRINT:\n                tapping_term_report();\n                return false;\n\n            case QK_DYNAMIC_TAPPING_TERM_UP:\n                g_tapping_term += DYNAMIC_TAPPING_TERM_INCREMENT;\n                return false;\n\n            case QK_DYNAMIC_TAPPING_TERM_DOWN:\n                g_tapping_term -= DYNAMIC_TAPPING_TERM_INCREMENT;\n                return false;\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_dynamic_tapping_term.h",
    "content": "/* Copyright 2020 Vladislav Kucheriavykh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\n#ifndef DYNAMIC_TAPPING_TERM_INCREMENT\n#    define DYNAMIC_TAPPING_TERM_INCREMENT 5\n#endif\n\nbool process_dynamic_tapping_term(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_grave_esc.c",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"process_grave_esc.h\"\n#include \"keycodes.h\"\n#include \"modifiers.h\"\n#include \"action_util.h\"\n\n/* true if the last press of QK_GRAVE_ESCAPE was shifted (i.e. GUI or SHIFT were pressed), false otherwise.\n * Used to ensure that the correct keycode is released if the key is released.\n */\nstatic bool grave_esc_was_shifted = false;\n\nbool process_grave_esc(uint16_t keycode, keyrecord_t *record) {\n    if (keycode == QK_GRAVE_ESCAPE) {\n        const uint8_t mods    = get_mods();\n        uint8_t       shifted = mods & MOD_MASK_SG;\n\n#ifdef GRAVE_ESC_ALT_OVERRIDE\n        // if ALT is pressed, ESC is always sent\n        // this is handy for the cmd+opt+esc shortcut on macOS, among other things.\n        if (mods & MOD_MASK_ALT) {\n            shifted = 0;\n        }\n#endif\n\n#ifdef GRAVE_ESC_CTRL_OVERRIDE\n        // if CTRL is pressed, ESC is always sent\n        // this is handy for the ctrl+shift+esc shortcut on windows, among other things.\n        if (mods & MOD_MASK_CTRL) {\n            shifted = 0;\n        }\n#endif\n\n#ifdef GRAVE_ESC_GUI_OVERRIDE\n        // if GUI is pressed, ESC is always sent\n        if (mods & MOD_MASK_GUI) {\n            shifted = 0;\n        }\n#endif\n\n#ifdef GRAVE_ESC_SHIFT_OVERRIDE\n        // if SHIFT is pressed, ESC is always sent\n        if (mods & MOD_MASK_SHIFT) {\n            shifted = 0;\n        }\n#endif\n\n        if (record->event.pressed) {\n            grave_esc_was_shifted = shifted;\n            add_key(shifted ? KC_GRAVE : KC_ESCAPE);\n        } else {\n            del_key(grave_esc_was_shifted ? KC_GRAVE : KC_ESCAPE);\n        }\n\n        send_keyboard_report();\n        return false;\n    }\n\n    // Not a grave keycode so continue processing\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_grave_esc.h",
    "content": "/* Copyright 2020\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_grave_esc(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_haptic.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"haptic.h\"\n#include \"process_haptic.h\"\n#include \"quantum_keycodes.h\"\n#include \"action_tapping.h\"\n#include \"usb_device_state.h\"\n\n__attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t *record) {\n    switch (keycode) {\n#ifdef NO_HAPTIC_MOD\n        case QK_MOD_TAP ... QK_MOD_TAP_MAX:\n            if (record->tap.count == 0) return false;\n            break;\n        case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:\n            if (record->tap.count != TAPPING_TOGGLE) return false;\n            break;\n        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:\n            if (record->tap.count == 0) return false;\n            break;\n        case KC_LEFT_CTRL ... KC_RIGHT_GUI:\n        case QK_MOMENTARY ... QK_MOMENTARY_MAX:\n        case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:\n#endif\n#ifdef NO_HAPTIC_ALPHA\n        case KC_A ... KC_Z:\n#endif\n#ifdef NO_HAPTIC_PUNCTUATION\n        case KC_ENTER:\n        case KC_ESCAPE:\n        case KC_BACKSPACE:\n        case KC_SPACE:\n        case KC_MINUS:\n        case KC_EQUAL:\n        case KC_LEFT_BRACKET:\n        case KC_RIGHT_BRACKET:\n        case KC_BACKSLASH:\n        case KC_NONUS_HASH:\n        case KC_SEMICOLON:\n        case KC_QUOTE:\n        case KC_GRAVE:\n        case KC_COMMA:\n        case KC_SLASH:\n        case KC_DOT:\n        case KC_NONUS_BACKSLASH:\n#endif\n#ifdef NO_HAPTIC_LOCKKEYS\n        case KC_CAPS_LOCK:\n        case KC_SCROLL_LOCK:\n        case KC_NUM_LOCK:\n#endif\n#ifdef NO_HAPTIC_NAV\n        case KC_PRINT_SCREEN:\n        case KC_PAUSE:\n        case KC_INSERT:\n        case KC_DELETE:\n        case KC_PAGE_DOWN:\n        case KC_PAGE_UP:\n        case KC_LEFT:\n        case KC_UP:\n        case KC_RIGHT:\n        case KC_DOWN:\n        case KC_END:\n        case KC_HOME:\n#endif\n#ifdef NO_HAPTIC_NUMERIC\n        case KC_1 ... KC_0:\n#endif\n            return false;\n    }\n    return true;\n}\n\nbool process_haptic(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n        switch (keycode) {\n            case QK_HAPTIC_ON:\n                haptic_enable();\n                break;\n            case QK_HAPTIC_OFF:\n                haptic_disable();\n                break;\n            case QK_HAPTIC_TOGGLE:\n                haptic_toggle();\n                break;\n            case QK_HAPTIC_RESET:\n                haptic_reset();\n                break;\n            case QK_HAPTIC_FEEDBACK_TOGGLE:\n                haptic_feedback_toggle();\n                break;\n            case QK_HAPTIC_BUZZ_TOGGLE:\n                haptic_buzz_toggle();\n                break;\n            case QK_HAPTIC_MODE_NEXT:\n                haptic_mode_increase();\n                break;\n            case QK_HAPTIC_MODE_PREVIOUS:\n                haptic_mode_decrease();\n                break;\n            case QK_HAPTIC_DWELL_UP:\n                haptic_dwell_increase();\n                break;\n            case QK_HAPTIC_DWELL_DOWN:\n                haptic_dwell_decrease();\n                break;\n            case QK_HAPTIC_CONTINUOUS_TOGGLE:\n                haptic_toggle_continuous();\n                break;\n            case QK_HAPTIC_CONTINUOUS_UP:\n                haptic_cont_increase();\n                break;\n            case QK_HAPTIC_CONTINUOUS_DOWN:\n                haptic_cont_decrease();\n                break;\n        }\n    }\n\n    if (haptic_get_enable() && ((!HAPTIC_OFF_IN_LOW_POWER) || (usb_device_state_get_configure_state() == USB_DEVICE_STATE_CONFIGURED))) {\n        if (record->event.pressed) {\n            // keypress\n            if (haptic_get_feedback() < 2 && get_haptic_enabled_key(keycode, record)) {\n                haptic_play();\n            }\n        } else {\n            // keyrelease\n            if (haptic_get_feedback() > 0 && get_haptic_enabled_key(keycode, record)) {\n                haptic_play();\n            }\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_haptic.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_haptic(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_joystick.c",
    "content": "/* Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_joystick.h\"\n#include \"joystick.h\"\n\nbool process_joystick(uint16_t keycode, keyrecord_t *record) {\n    switch (keycode) {\n        case QK_JOYSTICK ... QK_JOYSTICK_MAX:\n            if (record->event.pressed) {\n                register_joystick_button(keycode - QK_JOYSTICK);\n            } else {\n                unregister_joystick_button(keycode - QK_JOYSTICK);\n            }\n            return false;\n    }\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_joystick.h",
    "content": "/* Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_joystick(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_key_lock.c",
    "content": "/* Copyright 2017 Fredric Silberberg\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <inttypes.h>\n#include <stdint.h>\n#include \"process_key_lock.h\"\n\n#define BV_64(shift) (((uint64_t)1) << (shift))\n#define GET_KEY_ARRAY(code) (((code) < 0x40) ? key_state[0] : ((code) < 0x80) ? key_state[1] : ((code) < 0xC0) ? key_state[2] : key_state[3])\n#define GET_CODE_INDEX(code) (((code) < 0x40) ? (code) : ((code) < 0x80) ? (code)-0x40 : ((code) < 0xC0) ? (code)-0x80 : (code)-0xC0)\n#define KEY_STATE(code) (GET_KEY_ARRAY(code) & BV_64(GET_CODE_INDEX(code))) == BV_64(GET_CODE_INDEX(code))\n#define SET_KEY_ARRAY_STATE(code, val) \\\n    do {                               \\\n        switch (code) {                \\\n            case 0x00 ... 0x3F:        \\\n                key_state[0] = (val);  \\\n                break;                 \\\n            case 0x40 ... 0x7F:        \\\n                key_state[1] = (val);  \\\n                break;                 \\\n            case 0x80 ... 0xBF:        \\\n                key_state[2] = (val);  \\\n                break;                 \\\n            case 0xC0 ... 0xFF:        \\\n                key_state[3] = (val);  \\\n                break;                 \\\n        }                              \\\n    } while (0)\n#define SET_KEY_STATE(code) SET_KEY_ARRAY_STATE(code, (GET_KEY_ARRAY(code) | BV_64(GET_CODE_INDEX(code))))\n#define UNSET_KEY_STATE(code) SET_KEY_ARRAY_STATE(code, (GET_KEY_ARRAY(code)) & ~(BV_64(GET_CODE_INDEX(code))))\n#define IS_STANDARD_KEYCODE(code) ((code) <= 0xFF)\n\n// Locked key state. This is an array of 256 bits, one for each of the standard keys supported qmk.\nuint64_t key_state[4] = {0x0, 0x0, 0x0, 0x0};\nbool     watching     = false;\n\n// Translate any OSM keycodes back to their unmasked versions.\nstatic inline uint16_t translate_keycode(uint16_t keycode) {\n    if (keycode > QK_ONE_SHOT_MOD && keycode <= QK_ONE_SHOT_MOD_MAX) {\n        return keycode ^ QK_ONE_SHOT_MOD;\n    } else {\n        return keycode;\n    }\n}\n\nvoid cancel_key_lock(void) {\n    watching = false;\n    UNSET_KEY_STATE(0x0);\n}\n\nbool process_key_lock(uint16_t *keycode, keyrecord_t *record) {\n    // We start by categorizing the keypress event. In the event of a down\n    // event, there are several possibilities:\n    // 1. The key is not being locked, and we are not watching for new keys.\n    //    In this case, we bail immediately. This is the common case for down events.\n    // 2. The key was locked, and we need to unlock it. In this case, we will\n    //    reset the state in our map and return false. When the user releases the\n    //    key, the up event will no longer be masked and the OS will observe the\n    //    released key.\n    // 3. QK_LOCK was just pressed. In this case, we set up the state machine\n    //    to watch for the next key down event, and finish processing\n    // 4. The keycode is below 0xFF, and we are watching for new keys. In this case,\n    //    we will send the key down event to the os, and set the key_state for that\n    //    key to mask the up event.\n    // 5. The keycode is above 0xFF, and we're wathing for new keys. In this case,\n    //    the user pressed a key that we cannot \"lock\", as it's a series of keys,\n    //    or a macro invocation, or a layer transition, or a custom-defined key, or\n    //    or some other arbitrary code. In this case, we bail immediately, reset\n    //    our watch state, and return true.\n    //\n    // In the event of an up event, there are these possibilities:\n    // 1. The key is not being locked. In this case, we return true and bail\n    //    immediately. This is the common case.\n    // 2. The key is being locked. In this case, we will mask the up event\n    //    by returning false, so the OS never sees that the key was released\n    //    until the user pressed the key again.\n\n    // We translate any OSM keycodes back to their original keycodes, so that if the key being\n    // one-shot modded is a standard keycode, we can handle it. This is the only set of special\n    // keys that we handle\n    uint16_t translated_keycode = translate_keycode(*keycode);\n\n    if (record->event.pressed) {\n        // Non-standard keycode, reset and return\n        if (!(IS_STANDARD_KEYCODE(translated_keycode) || translated_keycode == QK_LOCK)) {\n            watching = false;\n            return true;\n        }\n\n        // If we're already watching, turn off the watch.\n        if (translated_keycode == QK_LOCK) {\n            watching = !watching;\n            return false;\n        }\n\n        if (IS_STANDARD_KEYCODE(translated_keycode)) {\n            // We check watching first. This is so that in the following scenario, we continue to\n            // hold the key: QK_LOCK, KC_F, QK_LOCK, KC_F\n            // If we checked in reverse order, we'd end up holding the key pressed after the second\n            // KC_F press is registered, when the user likely meant to hold F\n            if (watching) {\n                watching = false;\n                SET_KEY_STATE(translated_keycode);\n                // We need to set the keycode passed in to be the translated keycode, in case we\n                // translated a OSM back to the original keycode.\n                *keycode = translated_keycode;\n                // Let the standard keymap send the keycode down event. The up event will be masked.\n                return true;\n            }\n\n            if (KEY_STATE(translated_keycode)) {\n                UNSET_KEY_STATE(translated_keycode);\n                // The key is already held, stop this process. The up event will be sent when the user\n                // releases the key.\n                return false;\n            }\n        }\n\n        // Either the key isn't a standard key, or we need to send the down event. Continue standard\n        // processing\n        return true;\n    } else {\n        // Stop processing if it's a standard key and we're masking up.\n        return !(IS_STANDARD_KEYCODE(translated_keycode) && KEY_STATE(translated_keycode));\n    }\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_key_lock.h",
    "content": "/* Copyright 2017 Fredric Silberberg\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nvoid cancel_key_lock(void);\nbool process_key_lock(uint16_t *keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_key_override.c",
    "content": "/*\n * Copyright 2021 Jonas Gessner\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_key_override.h\"\n#include \"report.h\"\n#include \"timer.h\"\n#include \"debug.h\"\n#include \"wait.h\"\n#include \"action_util.h\"\n#include \"quantum.h\"\n#include \"quantum_keycodes.h\"\n#include \"keymap_introspection.h\"\n\n#ifndef KEY_OVERRIDE_REPEAT_DELAY\n#    define KEY_OVERRIDE_REPEAT_DELAY 500\n#endif\n\n// For benchmarking the time it takes to call process_key_override on every key press (needs keyboard debugging enabled as well)\n// #define BENCH_KEY_OVERRIDE\n\n// For debug output (needs keyboard debugging enabled as well)\n// #define DEBUG_KEY_OVERRIDE\n\n#ifdef DEBUG_KEY_OVERRIDE\n#    define key_override_printf dprintf\n#else\n#    define key_override_printf(str, ...) \\\n        {}\n#endif\n\n// Helpers\n\n// Private functions implemented elsewhere in qmk/tmk\nextern uint8_t extract_mod_bits(uint16_t code);\nextern void    set_weak_override_mods(uint8_t mods);\nextern void    clear_weak_override_mods(void);\nextern void    set_suppressed_override_mods(uint8_t mods);\nextern void    clear_suppressed_override_mods(void);\n\nstatic uint16_t clear_mods_from(uint16_t keycode) {\n    switch (keycode) {\n        case QK_MODS ... QK_MODS_MAX:\n            break;\n        default:\n            return keycode;\n    }\n\n    static const uint16_t all_mods = QK_LCTL | QK_LSFT | QK_LALT | QK_LGUI | QK_RCTL | QK_RSFT | QK_RALT | QK_RGUI;\n\n    return (keycode & ~(all_mods));\n}\n\n// Internal variables\nstatic const key_override_t *active_override                 = NULL;\nstatic bool                  active_override_trigger_is_down = false;\n\n// Used to keep track of what non-modifier key was last pressed down. We never want to activate an override for a trigger key that is not the last non-mod key that was pressed down. OSes internally completely unregister a key that is held when a different key is held down after. We want to respect this here.\nstatic uint16_t last_key_down = 0;\n// When was the last key pressed down?\nstatic uint32_t last_key_down_time = 0;\n\n// What timestamp are we comparing to when waiting to register a deferred key?\nstatic uint32_t defer_reference_time = 0;\n// What delay should pass until deferred key is registered?\nstatic uint32_t defer_delay = 0;\n\n// Holds the keycode that should be registered at a later time, in order to not get false key presses\nstatic uint16_t deferred_register = 0;\n\n// TODO: in future maybe save in EEPROM?\nstatic bool enabled = true;\n\n// Forward decls\nstatic const key_override_t *clear_active_override(const bool allow_reregister);\n\nvoid key_override_on(void) {\n    enabled = true;\n    key_override_printf(\"Key override ON\\n\");\n}\n\nvoid key_override_off(void) {\n    enabled = false;\n    clear_active_override(false);\n    key_override_printf(\"Key override OFF\\n\");\n}\n\nvoid key_override_toggle(void) {\n    if (key_override_is_enabled()) {\n        key_override_off();\n    } else {\n        key_override_on();\n    }\n}\n\nbool key_override_is_enabled(void) {\n    return enabled;\n}\n\n// Returns whether the modifiers that are pressed are such that the override should activate\nstatic bool key_override_matches_active_modifiers(const key_override_t *override, const uint8_t mods) {\n    // Check that negative keys pass\n    if ((override->negative_mod_mask & mods) != 0) {\n        return false;\n    }\n\n    // Immediately return true if the override requires no mods down\n    if (override->trigger_mods == 0) {\n        return true;\n    }\n\n    if ((override->options & ko_option_one_mod) != 0) {\n        // At least one of the trigger modifiers must be down\n        return (override->trigger_mods & mods) != 0;\n    } else {\n        // All trigger modifiers must be down, but each mod can be active on either side (if both sides are specified).\n\n        // Which mods, regardless of side, are required?\n        uint8_t one_sided_required_mods = (override->trigger_mods & 0b1111) | (override->trigger_mods >> 4);\n\n        // Which of the required modifiers are active?\n        uint8_t active_required_mods = override->trigger_mods & mods;\n\n        // Move the active requird mods to one side\n        uint8_t one_sided_active_required_mods = (active_required_mods & 0b1111) | (active_required_mods >> 4);\n\n        // Check that there is a full match between the required one-sided mods and active required one sided mods\n        return one_sided_active_required_mods == one_sided_required_mods;\n    }\n\n    return false;\n}\n\nstatic void schedule_deferred_register(const uint16_t keycode) {\n    if (timer_elapsed32(last_key_down_time) < KEY_OVERRIDE_REPEAT_DELAY) {\n        // Defer until KEY_OVERRIDE_REPEAT_DELAY has passed since the trigger key was pressed down. This emulates the behavior as holding down a key x, then holding down shift shortly after. Usually the shifted key X is not immediately produced, but rather a 'key repeat delay' passes before any repeated character is output.\n        defer_reference_time = last_key_down_time;\n        defer_delay          = KEY_OVERRIDE_REPEAT_DELAY;\n    } else {\n        // Wait a very short time when a modifier event triggers the override to avoid false activations when e.g. a modifier is pressed just before a key is released (with the intention of pairing the modifier with a different key), or a modifier is lifted shortly before the trigger key is lifted. Operating systems by default reject modifier-events that happen very close to a non-modifier event.\n        defer_reference_time = timer_read32();\n        defer_delay          = 50; // 50ms\n    }\n    deferred_register = keycode;\n}\n\nconst key_override_t *clear_active_override(const bool allow_reregister) {\n    if (active_override == NULL) {\n        return NULL;\n    }\n\n    key_override_printf(\"Deactivating override\\n\");\n\n    deferred_register = 0;\n\n    // Clear the suppressed mods\n    clear_suppressed_override_mods();\n\n    // Unregister the replacement. First remove the weak override mods\n    clear_weak_override_mods();\n\n    const key_override_t *const old = active_override;\n\n    const uint8_t mod_free_replacement = clear_mods_from(active_override->replacement);\n\n    bool unregister_replacement = mod_free_replacement != KC_NO &&   // KC_NO is never registered\n                                  mod_free_replacement < SAFE_RANGE; // Custom keycodes are never registered\n\n    // Try firing the custom handler\n    if (active_override->custom_action != NULL) {\n        unregister_replacement &= active_override->custom_action(false, active_override->context);\n    }\n\n    // Then unregister the mod-free replacement key if desired\n    if (unregister_replacement) {\n        if (IS_BASIC_KEYCODE(mod_free_replacement)) {\n            del_key(mod_free_replacement);\n        } else {\n            key_override_printf(\"NOT KEY 1\\n\");\n            send_keyboard_report();\n            unregister_code(mod_free_replacement);\n        }\n    }\n\n    const uint16_t trigger = active_override->trigger;\n\n    const bool reregister_trigger = allow_reregister &&                                                  // Check if allowed from caller\n                                    (active_override->options & ko_option_no_reregister_trigger) == 0 && // Check if override allows\n                                    active_override_trigger_is_down &&                                   // Check if trigger is even down\n                                    trigger != KC_NO &&                                                  // KC_NO is never registered\n                                    trigger < SAFE_RANGE;                                                // A custom keycode should not be registered\n\n    // Optionally re-register the trigger if it is still down\n    if (reregister_trigger) {\n        key_override_printf(\"Re-registering trigger deferred: %u\\n\", trigger);\n\n        // This will always be a modifier event, so defer always\n        schedule_deferred_register(trigger);\n    }\n\n    send_keyboard_report();\n\n    active_override                 = NULL;\n    active_override_trigger_is_down = false;\n\n    return old;\n}\n\n/** Checks if the key event is an allowed activation event for the provided override. Does not check things like whether the correct mods or correct trigger key is down. */\nstatic bool check_activation_event(const key_override_t *override, const bool key_down, const bool is_mod) {\n    ko_option_t options = override->options;\n\n    if ((options & ko_options_all_activations) == 0) {\n        // No activation option provided at all. This is wrong, but let's assume the default activations (ko_options_all_activations) were meant...\n        options = ko_options_all_activations;\n    }\n\n    if (is_mod) {\n        if (key_down) {\n            return (options & ko_option_activation_required_mod_down) != 0;\n        } else {\n            return (options & ko_option_activation_negative_mod_up) != 0;\n        }\n    } else {\n        if (key_down) {\n            return (options & ko_option_activation_trigger_down) != 0;\n        } else {\n            return false;\n        }\n    }\n}\n\n/** Iterates through the list of key overrides and tries activating each, until it finds one that activates or reaches the end of overrides. Returns true if the key action for `keycode` should be sent */\nstatic bool try_activating_override(const uint16_t keycode, const uint8_t layer, const bool key_down, const bool is_mod, const uint8_t active_mods, bool *activated) {\n    if (key_override_count() == 0) {\n        return true;\n    }\n\n    for (uint8_t i = 0; i < key_override_count(); i++) {\n        const key_override_t *const override = key_override_get(i);\n\n        // End of array\n        if (override == NULL) {\n            break;\n        }\n\n        // Fast, but not full mods check. Most key presses will not have any mods down, and most overrides will require mods. Hence here we filter overrides that require mods to be down while no mods are down\n        if (active_mods == 0 && override->trigger_mods != 0) {\n            key_override_printf(\"Not activating override: Modifiers don't match\\n\");\n            continue;\n        }\n\n        // Check layer\n        if ((override->layers & (1 << layer)) == 0) {\n            key_override_printf(\"Not activating override: Not set to activate on pressed layer\\n\");\n            continue;\n        }\n\n        // Check allowed activation events\n        if (!check_activation_event(override, key_down, is_mod)) {\n            key_override_printf(\"Not activating override: Activation event not allowed\\n\");\n            continue;\n        }\n\n        const bool is_trigger = override->trigger == keycode;\n\n        // Check if trigger lifted. This is a small optimization in order to skip the remaining checks\n        if (is_trigger && !key_down) {\n            key_override_printf(\"Not activating override: Trigger lifted\\n\");\n            continue;\n        }\n\n        // If the trigger is KC_NO it means 'no key', so only the required modifiers need to be down.\n        const bool no_trigger = override->trigger == KC_NO;\n\n        // Check if aleady active\n        if (override == active_override) {\n            key_override_printf(\"Not activating override: Alerady actived\\n\");\n            continue;\n        }\n\n        // Check if enabled\n        if (override->enabled != NULL && !((*(override->enabled) & 1))) {\n            key_override_printf(\"Not activating override: Not enabled\\n\");\n            continue;\n        }\n\n        // Check mods precisely\n        if (!key_override_matches_active_modifiers(override, active_mods)) {\n            key_override_printf(\"Not activating override: Modifiers don't match\\n\");\n            continue;\n        }\n\n        // Check if trigger key is down.\n        const bool trigger_down = is_trigger && key_down;\n\n        // At this point, all requirements for activation are checked, except whether the trigger key is pressed. Now we check if the required trigger is down\n        // If no trigger key is required, yes.\n        // If the trigger was just pressed, yes.\n        // If the last non-mod key that was pressed down is the trigger key, yes.\n        bool should_activate = no_trigger || trigger_down || last_key_down == override->trigger;\n\n        if (!should_activate) {\n            key_override_printf(\"Not activating override. Trigger not down\\n\");\n            continue;\n        }\n\n        key_override_printf(\"Activating override\\n\");\n\n        clear_active_override(false);\n\n#ifdef DUMMY_MOD_NEUTRALIZER_KEYCODE\n        // Send a dummy keycode before unregistering the modifier(s)\n        // so that suppressing the modifier(s) doesn't falsely get interpreted\n        // by the host OS as a tap of a modifier key.\n        // For example, unintended activations of the start menu on Windows when\n        // using a GUI+<kc> key override with suppressed mods.\n        neutralize_flashing_modifiers(active_mods);\n#endif\n\n        active_override                 = override;\n        active_override_trigger_is_down = true;\n\n        set_suppressed_override_mods(override->suppressed_mods);\n\n        if (!trigger_down && !no_trigger) {\n            // When activating a key override the trigger is is always unregistered. In the case where the key that newly pressed is not the trigger key, we have to explicitly remove the trigger key from the keyboard report. If the trigger was just pressed down we simply suppress the event which also has the effect of the trigger key not being registered in the keyboard report.\n            if (IS_BASIC_KEYCODE(override->trigger)) {\n                del_key(override->trigger);\n            } else {\n                unregister_code(override->trigger);\n            }\n        }\n\n        const uint16_t mod_free_replacement = clear_mods_from(override->replacement);\n\n        bool register_replacement = mod_free_replacement != KC_NO &&   // KC_NO is never registered\n                                    mod_free_replacement < SAFE_RANGE; // Custom keycodes are never registered\n\n        // Try firing the custom handler\n        if (override->custom_action != NULL) {\n            register_replacement &= override->custom_action(true, override->context);\n        }\n\n        if (register_replacement) {\n            const uint8_t override_mods = extract_mod_bits(override->replacement);\n            set_weak_override_mods(override_mods);\n\n            // If this is a modifier event that activates the key override we _always_ defer the actual full activation of the override\n            if (is_mod) {\n                key_override_printf(\"Deferring register replacement key\\n\");\n                schedule_deferred_register(mod_free_replacement);\n                send_keyboard_report();\n            } else {\n                if (IS_BASIC_KEYCODE(mod_free_replacement)) {\n                    add_key(mod_free_replacement);\n                } else {\n                    key_override_printf(\"NOT KEY 2\\n\");\n                    send_keyboard_report();\n                    // On macOS there seems to be a race condition when it comes to the keyboard report and consumer keycodes. It seems the OS may recognize a consumer keycode before an updated keyboard report, even if the keyboard report is actually sent before the consumer key. I assume it is some sort of race condition because it happens infrequently and very irregularly. Waiting for about at least 10ms between sending the keyboard report and sending the consumer code has shown to fix this.\n                    wait_ms(10);\n                    register_code(mod_free_replacement);\n                }\n            }\n        } else {\n            // If not registering the replacement key send keyboard report to update the unregistered keys.\n            send_keyboard_report();\n        }\n\n        *activated = true;\n\n        // If the trigger is down, suppress the event so that it does not get added to the keyboard report.\n        return !trigger_down;\n    }\n\n    *activated = false;\n\n    return true;\n}\n\nvoid key_override_task(void) {\n    if (deferred_register == 0) {\n        return;\n    }\n\n    if (timer_elapsed32(defer_reference_time) >= defer_delay) {\n        key_override_printf(\"Registering deferred key\\n\");\n        register_code16(deferred_register);\n        deferred_register    = 0;\n        defer_reference_time = 0;\n        defer_delay          = 0;\n    }\n}\n\nbool process_key_override(const uint16_t keycode, const keyrecord_t *const record) {\n#ifdef BENCH_KEY_OVERRIDE\n    uint16_t start = timer_read();\n#endif\n\n    const bool key_down = record->event.pressed;\n    const bool is_mod   = IS_MODIFIER_KEYCODE(keycode);\n\n    if (key_down) {\n        switch (keycode) {\n            case QK_KEY_OVERRIDE_TOGGLE:\n                key_override_toggle();\n                return false;\n\n            case QK_KEY_OVERRIDE_ON:\n                key_override_on();\n                return false;\n\n            case QK_KEY_OVERRIDE_OFF:\n                key_override_off();\n                return false;\n\n            default:\n                break;\n        }\n    }\n\n    if (!enabled) {\n        return true;\n    }\n\n    uint8_t effective_mods = get_mods();\n\n#ifdef KEY_OVERRIDE_INCLUDE_WEAK_MODS\n    effective_mods |= get_weak_mods();\n#endif\n\n#ifndef NO_ACTION_ONESHOT\n    // Locked one shot mods are added to get_mods(), I think (why??) while oneshot mods are in get_oneshot_mods(). Still OR with get_locked_oneshot_mods because that's where those mods _should_ be saved.\n    effective_mods |= get_oneshot_locked_mods() | get_oneshot_mods();\n#endif\n\n    if (is_mod) {\n        // The mods returned from get_mods() will be updated with this new event _after_ this code runs. Hence we manually update the effective mods here to really know the effective mods.\n        if (key_down) {\n            effective_mods |= MOD_BIT(keycode);\n        } else {\n            effective_mods &= ~MOD_BIT(keycode);\n        }\n    } else {\n        if (key_down) {\n            last_key_down      = keycode;\n            last_key_down_time = timer_read32();\n            deferred_register  = 0;\n        }\n\n        // The last key that was pressed was just released. No more keys are therefore sending input\n        if (!key_down && keycode == last_key_down) {\n            last_key_down      = 0;\n            last_key_down_time = 0;\n            // We also cancel any deferred registers because, again, no keys are sending any input. Only the last key that is pressed creates an input – this key was just lifted.\n            deferred_register = 0;\n        }\n    }\n\n    key_override_printf(\"key down: %u keycode: %u is mod: %u effective mods: %u\\n\", key_down, keycode, is_mod, effective_mods);\n\n    bool send_key_action = true;\n    bool activated       = false;\n\n    // Non-mod key up events never activate a key override\n    if (is_mod || key_down) {\n        // Get the exact layer that was hit. It will be cached at this point\n        const uint8_t layer = read_source_layers_cache(record->event.key);\n\n        // Use blocked to ensure the same override is not activated again immediately after it is deactivated\n        send_key_action = try_activating_override(keycode, layer, key_down, is_mod, effective_mods, &activated);\n\n        if (!send_key_action) {\n            send_keyboard_report();\n        }\n    }\n\n    if (!activated && active_override != NULL) {\n        if (is_mod) {\n            // Check if necessary modifier of current override goes up or a negative mod goes down\n            if (!key_override_matches_active_modifiers(active_override, effective_mods)) {\n                key_override_printf(\"Deactivating override because necessary modifier lifted or negative mod pressed\\n\");\n                clear_active_override(true);\n            }\n        } else {\n            // Check if trigger of current override goes up or if override does not allow additional keys to be down and another key goes down\n            const bool is_trigger        = keycode == active_override->trigger;\n            bool       should_deactivate = false;\n\n            // Check if trigger key lifted\n            if (is_trigger && !key_down) {\n                should_deactivate               = true;\n                active_override_trigger_is_down = false;\n                key_override_printf(\"Deactivating override because trigger key up\\n\");\n            }\n\n            // Check if another key was pressed\n            if (key_down && (active_override->options & ko_option_no_unregister_on_other_key_down) == 0) {\n                should_deactivate = true;\n                key_override_printf(\"Deactivating override because another key was pressed\\n\");\n            }\n\n            if (should_deactivate) {\n                clear_active_override(false);\n            }\n        }\n    }\n\n#ifdef BENCH_KEY_OVERRIDE\n    uint16_t elapsed = timer_elapsed(start);\n\n    dprintf(\"Processing key overrides took: %u ms\\n\", elapsed);\n#endif\n\n    return send_key_action;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_key_override.h",
    "content": "/*\n * Copyright 2021 Jonas Gessner\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"action.h\"\n#include \"action_layer.h\"\n\n/**\n * Key overrides allow you to send a different key-modifier combination or perform a custom action when a certain modifier-key combination is pressed.\n *\n * For example, you may configure a key override to send the delete key when shift + backspace are pressed together, or that your volume keys become screen brightness keys when holding ctrl. The possibilities are quite vast and the documentation contains a few examples for inspiration.\n *\n * See the documentation and examples here: https://docs.qmk.fm/#/feature_key_overrides\n */\n\n/** Bitfield with various options controlling the behavior of a key override. */\ntypedef enum {\n    /** Allow activating when the trigger key is pressed down. */\n    ko_option_activation_trigger_down = (1 << 0),\n    /** Allow activating when a necessary modifier is pressed down. */\n    ko_option_activation_required_mod_down = (1 << 1),\n    /** Allow activating when a negative modifier is released. */\n    ko_option_activation_negative_mod_up = (1 << 2),\n\n    ko_options_all_activations = ko_option_activation_negative_mod_up | ko_option_activation_required_mod_down | ko_option_activation_trigger_down,\n\n    /** If set, any of the modifiers in trigger_mods will be enough to activate the override (logical OR of modifiers). If not set, all the modifiers in trigger_mods have to be pressed (logical AND of modifiers). */\n    ko_option_one_mod = (1 << 3),\n\n    /** If set, the trigger key will never be registered again after the override is deactivated. */\n    ko_option_no_reregister_trigger = (1 << 4),\n\n    /** If set, the override will not deactivate when another key is pressed down. Use only if you really know you need this. */\n    ko_option_no_unregister_on_other_key_down = (1 << 5),\n\n    /** The default options used by the ko_make_xxx functions. */\n    ko_options_default = ko_options_all_activations,\n} ko_option_t;\n\n/** Defines a single key override */\ntypedef struct key_override_t {\n    // The non-modifier keycode that triggers the override. This keycode, and the necessary modifiers (trigger_mods) must be pressed to activate this override. Set this to the keycode of the key that should activate the override. Set to KC_NO to require only the necessary modifiers to be pressed and no non-modifier.\n    uint16_t trigger;\n\n    // Which mods need to be down for activation. If both sides of a modifier are set (e.g. left ctrl and right ctrl) then only one is required to be pressed (e.g. left ctrl suffices). Use the MOD_MASK_XXX and MOD_BIT() macros for this.\n    uint8_t trigger_mods;\n\n    // This is a BITMASK (!), defining which layers this override applies to. To use this override on layer i set the ith bit (1 << i).\n    layer_state_t layers;\n\n    // Which modifiers cannot be down. It must hold that (active_mods & negative_mod_mask) == 0, otherwise the key override will not be activated. An active override will be deactivated once this is no longer true.\n    uint8_t negative_mod_mask;\n\n    // Modifiers to 'suppress' while the override is active. To suppress a modifier means that even though the modifier key is held down, the host OS sees the modifier as not pressed. Can be used to suppress the trigger modifiers, as a trivial example.\n    uint8_t suppressed_mods;\n\n    // The complex keycode to send as replacement when this override is triggered. This can be a simple keycode, a key-modifier combination (e.g. C(KC_A)), or KC_NO (to register no replacement keycode). Use in combination with suppressed_mods to get the correct modifiers to be sent.\n    uint16_t replacement;\n\n    // Options controlling the behavior of the override, such as what actions are allowed to activate the override.\n    ko_option_t options;\n\n    // If not NULL, this function will be called right before the replacement key is registered, along with the provided context and a flag indicating whether the override was activated or deactivated. This function allows you to run some custom actions for specific key overrides. If you return `false`, the replacement key is not registered/unregistered as it would normally. Return `true` to register and unregister the override normally.\n    bool (*custom_action)(bool activated, void *context);\n\n    // A context that will be passed to the custom action function.\n    void *context;\n\n    // If this points to false this override will not be used. Set to NULL to always have this override enabled.\n    bool *enabled;\n} key_override_t;\n\n/** Turns key overrides on */\nvoid key_override_on(void);\n\n/** Turns key overrides off */\nvoid key_override_off(void);\n\n/** Toggles key overrides on */\nvoid key_override_toggle(void);\n\n/** Returns whether key overrides are enabled */\nbool key_override_is_enabled(void);\n\n/** Handling of key overrides and its implemented keycodes */\nbool process_key_override(const uint16_t keycode, const keyrecord_t *const record);\n\n/** Perform any deferred keys */\nvoid key_override_task(void);\n\n/**\n *  Preferrably use these macros to create key overrides. They fix many of the options to a standard setting that should satisfy most basic use-cases. Only directly create a key_override_t struct when you really need to.\n */\n\n// clang-format off\n\n/**\n * Convenience initializer to create a basic key override. Activates the override on all layers.\n */\n#define ko_make_basic(trigger_mods, trigger_key, replacement_key) \\\n    ko_make_with_layers(trigger_mods, trigger_key, replacement_key, ~0)\n\n/**\n * Convenience initializer to create a basic key override. Provide a bitmap (of type layer_state_t) with the bits set for each layer on which the override should activate.\n */\n#define ko_make_with_layers(trigger_mods, trigger_key, replacement_key, layers) \\\n    ko_make_with_layers_and_negmods(trigger_mods, trigger_key, replacement_key, layers, 0)\n\n/**\n * Convenience initializer to create a basic key override. Provide a bitmap with the bits set for each layer on which the override should activate. Also provide a negative modifier mask, that is used to define which modifiers may not be pressed.\n */\n#define ko_make_with_layers_and_negmods(trigger_mods, trigger_key, replacement_key, layers, negative_mask) \\\n    ko_make_with_layers_negmods_and_options(trigger_mods, trigger_key, replacement_key, layers, negative_mask, ko_options_default)\n\n /**\n  *  Convenience initializer to create a basic key override. Provide a bitmap with the bits set for each layer on which the override should activate. Also provide a negative modifier mask, that is used to define which modifiers may not be pressed. Provide options for additional control of the behavior of the override.\n */\n#define ko_make_with_layers_negmods_and_options(trigger_mods_, trigger_key, replacement_key, layer_mask, negative_mask, options_) \\\n    ((const key_override_t){                                                                \\\n        .trigger_mods                           = (trigger_mods_),                          \\\n        .layers                                 = (layer_mask),                             \\\n        .suppressed_mods                        = (trigger_mods_),                          \\\n        .options                                = (options_),                               \\\n        .negative_mod_mask                      = (negative_mask),                          \\\n        .custom_action                          = NULL,                                     \\\n        .context                                = NULL,                                     \\\n        .trigger                                = (trigger_key),                            \\\n        .replacement                            = (replacement_key),                        \\\n        .enabled                                = NULL                                      \\\n    })\n\n// clang-format on\n"
  },
  {
    "path": "quantum/process_keycode/process_layer_lock.c",
    "content": "// Copyright 2022-2023 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"layer_lock.h\"\n#include \"process_layer_lock.h\"\n#include \"quantum_keycodes.h\"\n#include \"action_util.h\"\n\n// The current lock state. The kth bit is on if layer k is locked.\nextern layer_state_t locked_layers;\n\n// Handles an event on an `MO` or `TT` layer switch key.\nstatic inline bool handle_mo_or_tt(uint8_t layer, keyrecord_t* record) {\n    if (is_layer_locked(layer)) {\n        if (record->event.pressed) { // On press, unlock the layer.\n            layer_lock_invert(layer);\n        }\n        return false; // Skip default handling.\n    }\n    return true;\n}\n\nbool process_layer_lock(uint16_t keycode, keyrecord_t* record) {\n#ifndef NO_ACTION_LAYER\n    layer_lock_activity_trigger();\n\n    // The intention is that locked layers remain on. If something outside of\n    // this feature turned any locked layers off, unlock them.\n    if ((locked_layers & ~layer_state) != 0) {\n        layer_lock_set_kb(locked_layers &= layer_state);\n    }\n\n    if (keycode == QK_LAYER_LOCK) {\n        if (record->event.pressed) { // The layer lock key was pressed.\n            layer_lock_invert(get_highest_layer(layer_state));\n        }\n        return false;\n    }\n\n    switch (keycode) {\n        case QK_MOMENTARY ... QK_MOMENTARY_MAX: // `MO(layer)` keys.\n            return handle_mo_or_tt(QK_MOMENTARY_GET_LAYER(keycode), record);\n\n        case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX: // `TT(layer)`.\n            return handle_mo_or_tt(QK_LAYER_TAP_TOGGLE_GET_LAYER(keycode), record);\n\n        case QK_LAYER_MOD ... QK_LAYER_MOD_MAX: { // `LM(layer, mod)`.\n            uint8_t layer = QK_LAYER_MOD_GET_LAYER(keycode);\n            if (is_layer_locked(layer)) {\n                if (record->event.pressed) { // On press, unlock the layer.\n                    layer_lock_invert(layer);\n                } else { // On release, clear the mods.\n                    clear_mods();\n                    send_keyboard_report();\n                }\n                return false; // Skip default handling.\n            }\n        } break;\n\n#    ifndef NO_ACTION_TAPPING\n        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: // `LT(layer, key)` keys.\n            if (record->tap.count == 0 && !record->event.pressed && is_layer_locked(QK_LAYER_TAP_GET_LAYER(keycode))) {\n                // Release event on a held layer-tap key where the layer is locked.\n                return false; // Skip default handling so that layer stays on.\n            }\n            break;\n#    endif // NO_ACTION_TAPPING\n    }\n#endif // NO_ACTION_LAYER\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_layer_lock.h",
    "content": "// Copyright 2022-2023 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_layer_lock(uint16_t keycode, keyrecord_t* record);\n"
  },
  {
    "path": "quantum/process_keycode/process_leader.c",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_leader.h\"\n#include \"leader.h\"\n#include \"quantum_keycodes.h\"\n\nbool process_leader(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n        if (leader_sequence_active() && !leader_sequence_timed_out()) {\n#ifndef LEADER_KEY_STRICT_KEY_PROCESSING\n            keycode = get_tap_keycode(keycode);\n#endif\n\n            if (!leader_sequence_add(keycode)) {\n                leader_end();\n\n                return true;\n            }\n\n#ifdef LEADER_PER_KEY_TIMING\n            leader_reset_timer();\n#endif\n\n            return false;\n        } else if (keycode == QK_LEADER) {\n            leader_start();\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_leader.h",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_leader(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_led_matrix.c",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"process_led_matrix.h\"\n#include \"led_matrix.h\"\n\nbool process_led_matrix(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n        switch (keycode) {\n            case QK_BACKLIGHT_ON: // TODO: Remove backlight keycodes\n            case QK_LED_MATRIX_ON:\n                led_matrix_enable();\n                return false;\n            case QK_BACKLIGHT_OFF:\n            case QK_LED_MATRIX_OFF:\n                led_matrix_disable();\n                return false;\n            case QK_BACKLIGHT_TOGGLE:\n            case QK_LED_MATRIX_TOGGLE:\n                led_matrix_toggle();\n                return false;\n            case QK_BACKLIGHT_STEP:\n            case QK_LED_MATRIX_MODE_NEXT:\n                led_matrix_step();\n                return false;\n            case QK_LED_MATRIX_MODE_PREVIOUS:\n                led_matrix_step_reverse();\n                return false;\n            case QK_BACKLIGHT_UP:\n            case QK_LED_MATRIX_BRIGHTNESS_UP:\n                led_matrix_increase_val();\n                return false;\n            case QK_BACKLIGHT_DOWN:\n            case QK_LED_MATRIX_BRIGHTNESS_DOWN:\n                led_matrix_decrease_val();\n                return false;\n            case QK_LED_MATRIX_SPEED_UP:\n                led_matrix_increase_speed();\n                return false;\n            case QK_LED_MATRIX_SPEED_DOWN:\n                led_matrix_decrease_speed();\n                return false;\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_led_matrix.h",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_led_matrix(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_magic.c",
    "content": "/* Copyright 2019 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"process_magic.h\"\n#include \"keycode_config.h\"\n#include \"keycodes.h\"\n#include \"eeconfig.h\"\n\n#ifdef AUDIO_ENABLE\n#    include \"audio.h\"\n\n#    ifndef AG_NORM_SONG\n#        define AG_NORM_SONG SONG(AG_NORM_SOUND)\n#    endif\n#    ifndef AG_SWAP_SONG\n#        define AG_SWAP_SONG SONG(AG_SWAP_SOUND)\n#    endif\n#    ifndef CG_NORM_SONG\n#        define CG_NORM_SONG SONG(AG_NORM_SOUND)\n#    endif\n#    ifndef CG_SWAP_SONG\n#        define CG_SWAP_SONG SONG(AG_SWAP_SOUND)\n#    endif\nfloat ag_norm_song[][2] = AG_NORM_SONG;\nfloat ag_swap_song[][2] = AG_SWAP_SONG;\nfloat cg_norm_song[][2] = CG_NORM_SONG;\nfloat cg_swap_song[][2] = CG_SWAP_SONG;\n#endif\n\n/**\n * MAGIC actions (BOOTMAGIC without the boot)\n */\nbool process_magic(uint16_t keycode, keyrecord_t *record) {\n    // skip anything that isn't a keyup\n    if (record->event.pressed) {\n        if (IS_MAGIC_KEYCODE(keycode)) {\n            /* keymap config */\n            eeconfig_read_keymap(&keymap_config);\n            switch (keycode) {\n                case QK_MAGIC_SWAP_CONTROL_CAPS_LOCK:\n                    keymap_config.swap_control_capslock = true;\n                    break;\n                case QK_MAGIC_SWAP_ESCAPE_CAPS_LOCK:\n                    keymap_config.swap_escape_capslock = true;\n                    break;\n                case QK_MAGIC_CAPS_LOCK_AS_CONTROL_ON:\n                    keymap_config.capslock_to_control = true;\n                    break;\n                case QK_MAGIC_SWAP_LALT_LGUI:\n                    keymap_config.swap_lalt_lgui = true;\n                    break;\n                case QK_MAGIC_SWAP_RALT_RGUI:\n                    keymap_config.swap_ralt_rgui = true;\n                    break;\n                case QK_MAGIC_SWAP_LCTL_LGUI:\n                    keymap_config.swap_lctl_lgui = true;\n                    break;\n                case QK_MAGIC_SWAP_RCTL_RGUI:\n                    keymap_config.swap_rctl_rgui = true;\n                    break;\n                case QK_MAGIC_GUI_OFF:\n                    keymap_config.no_gui = true;\n                    break;\n                case QK_MAGIC_SWAP_GRAVE_ESC:\n                    keymap_config.swap_grave_esc = true;\n                    break;\n                case QK_MAGIC_SWAP_BACKSLASH_BACKSPACE:\n                    keymap_config.swap_backslash_backspace = true;\n                    break;\n                case QK_MAGIC_NKRO_ON:\n                    clear_keyboard(); // clear first buffer to prevent stuck keys\n                    keymap_config.nkro = true;\n                    break;\n                case QK_MAGIC_SWAP_ALT_GUI:\n                    keymap_config.swap_lalt_lgui = keymap_config.swap_ralt_rgui = true;\n#ifdef AUDIO_ENABLE\n                    PLAY_SONG(ag_swap_song);\n#endif\n                    break;\n                case QK_MAGIC_SWAP_CTL_GUI:\n                    keymap_config.swap_lctl_lgui = keymap_config.swap_rctl_rgui = true;\n#ifdef AUDIO_ENABLE\n                    PLAY_SONG(cg_swap_song);\n#endif\n                    break;\n                case QK_MAGIC_UNSWAP_CONTROL_CAPS_LOCK:\n                    keymap_config.swap_control_capslock = false;\n                    break;\n                case QK_MAGIC_UNSWAP_ESCAPE_CAPS_LOCK:\n                    keymap_config.swap_escape_capslock = false;\n                    break;\n                case QK_MAGIC_CAPS_LOCK_AS_CONTROL_OFF:\n                    keymap_config.capslock_to_control = false;\n                    break;\n                case QK_MAGIC_UNSWAP_LALT_LGUI:\n                    keymap_config.swap_lalt_lgui = false;\n                    break;\n                case QK_MAGIC_UNSWAP_RALT_RGUI:\n                    keymap_config.swap_ralt_rgui = false;\n                    break;\n                case QK_MAGIC_UNSWAP_LCTL_LGUI:\n                    keymap_config.swap_lctl_lgui = false;\n                    break;\n                case QK_MAGIC_UNSWAP_RCTL_RGUI:\n                    keymap_config.swap_rctl_rgui = false;\n                    break;\n                case QK_MAGIC_GUI_ON:\n                    keymap_config.no_gui = false;\n                    break;\n                case QK_MAGIC_UNSWAP_GRAVE_ESC:\n                    keymap_config.swap_grave_esc = false;\n                    break;\n                case QK_MAGIC_UNSWAP_BACKSLASH_BACKSPACE:\n                    keymap_config.swap_backslash_backspace = false;\n                    break;\n                case QK_MAGIC_NKRO_OFF:\n                    clear_keyboard(); // clear first buffer to prevent stuck keys\n                    keymap_config.nkro = false;\n                    break;\n                case QK_MAGIC_UNSWAP_ALT_GUI:\n                    keymap_config.swap_lalt_lgui = keymap_config.swap_ralt_rgui = false;\n#ifdef AUDIO_ENABLE\n                    PLAY_SONG(ag_norm_song);\n#endif\n                    break;\n                case QK_MAGIC_UNSWAP_CTL_GUI:\n                    keymap_config.swap_lctl_lgui = keymap_config.swap_rctl_rgui = false;\n#ifdef AUDIO_ENABLE\n                    PLAY_SONG(cg_norm_song);\n#endif\n                    break;\n                case QK_MAGIC_TOGGLE_ALT_GUI:\n                    keymap_config.swap_lalt_lgui = !keymap_config.swap_lalt_lgui;\n                    keymap_config.swap_ralt_rgui = keymap_config.swap_lalt_lgui;\n#ifdef AUDIO_ENABLE\n                    if (keymap_config.swap_ralt_rgui) {\n                        PLAY_SONG(ag_swap_song);\n                    } else {\n                        PLAY_SONG(ag_norm_song);\n                    }\n#endif\n                    break;\n                case QK_MAGIC_TOGGLE_CTL_GUI:\n                    keymap_config.swap_lctl_lgui = !keymap_config.swap_lctl_lgui;\n                    keymap_config.swap_rctl_rgui = keymap_config.swap_lctl_lgui;\n#ifdef AUDIO_ENABLE\n                    if (keymap_config.swap_rctl_rgui) {\n                        PLAY_SONG(cg_swap_song);\n                    } else {\n                        PLAY_SONG(cg_norm_song);\n                    }\n#endif\n                    break;\n                case QK_MAGIC_TOGGLE_BACKSLASH_BACKSPACE:\n                    keymap_config.swap_backslash_backspace = !keymap_config.swap_backslash_backspace;\n                    break;\n                case QK_MAGIC_TOGGLE_NKRO:\n                    clear_keyboard(); // clear first buffer to prevent stuck keys\n                    keymap_config.nkro = !keymap_config.nkro;\n                    break;\n                case QK_MAGIC_EE_HANDS_LEFT:\n                    eeconfig_update_handedness(true);\n                    break;\n                case QK_MAGIC_EE_HANDS_RIGHT:\n                    eeconfig_update_handedness(false);\n                    break;\n                case QK_MAGIC_TOGGLE_GUI:\n                    keymap_config.no_gui = !keymap_config.no_gui;\n                    break;\n                case QK_MAGIC_TOGGLE_CONTROL_CAPS_LOCK:\n                    keymap_config.swap_control_capslock = !keymap_config.swap_control_capslock;\n                    break;\n                case QK_MAGIC_TOGGLE_ESCAPE_CAPS_LOCK:\n                    keymap_config.swap_escape_capslock = !keymap_config.swap_escape_capslock;\n                    break;\n            }\n\n            eeconfig_update_keymap(&keymap_config);\n            clear_keyboard(); // clear to prevent stuck keys\n\n            return false;\n        }\n    }\n\n    // Not a magic keycode so continue processing\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_magic.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_magic(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_midi.c",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"process_midi.h\"\n\n#include <LUFA/Drivers/USB/USB.h>\n#include \"midi.h\"\n#include \"qmk_midi.h\"\n#include \"timer.h\"\n#include \"debug.h\"\n\n#ifdef MIDI_BASIC\n\nvoid process_midi_basic_noteon(uint8_t note) {\n    midi_send_noteon(&midi_device, 0, note, 127);\n}\n\nvoid process_midi_basic_noteoff(uint8_t note) {\n    midi_send_noteoff(&midi_device, 0, note, 0);\n}\n\nvoid process_midi_all_notes_off(void) {\n    midi_send_cc(&midi_device, 0, 0x7B, 0);\n}\n\n#endif // MIDI_BASIC\n\n#ifdef MIDI_ADVANCED\nstatic uint8_t tone_status[MIDI_TONE_COUNT];\n\nstatic uint8_t  midi_modulation;\nstatic int8_t   midi_modulation_step;\nstatic uint16_t midi_modulation_timer;\nmidi_config_t   midi_config;\n\ninline uint8_t compute_velocity(uint8_t setting) {\n    return setting * (128 / (MIDI_VELOCITY_MAX - MIDI_VELOCITY_MIN));\n}\n\nvoid midi_init(void) {\n    midi_config.octave              = QK_MIDI_OCTAVE_2 - MIDI_OCTAVE_MIN;\n    midi_config.transpose           = 0;\n    midi_config.velocity            = 127;\n    midi_config.channel             = 0;\n    midi_config.modulation_interval = 8;\n\n    for (uint8_t i = 0; i < MIDI_TONE_COUNT; i++) {\n        tone_status[i] = MIDI_INVALID_NOTE;\n    }\n\n    midi_modulation       = 0;\n    midi_modulation_step  = 0;\n    midi_modulation_timer = 0;\n}\n\nuint8_t midi_compute_note(uint16_t keycode) {\n    return 12 * midi_config.octave + (keycode - MIDI_TONE_MIN) + midi_config.transpose;\n}\n\nbool process_midi(uint16_t keycode, keyrecord_t *record) {\n    switch (keycode) {\n        case MIDI_TONE_MIN ... MIDI_TONE_MAX: {\n            uint8_t channel  = midi_config.channel;\n            uint8_t tone     = keycode - MIDI_TONE_MIN;\n            uint8_t velocity = midi_config.velocity;\n            if (record->event.pressed) {\n                if (tone_status[tone] == MIDI_INVALID_NOTE) {\n                    uint8_t note = midi_compute_note(keycode);\n                    midi_send_noteon(&midi_device, channel, note, velocity);\n                    dprintf(\"midi noteon channel:%d note:%d velocity:%d\\n\", channel, note, velocity);\n                    tone_status[tone] = note;\n                }\n            } else {\n                uint8_t note = tone_status[tone];\n                if (note != MIDI_INVALID_NOTE) {\n                    midi_send_noteoff(&midi_device, channel, note, velocity);\n                    dprintf(\"midi noteoff channel:%d note:%d velocity:%d\\n\", channel, note, velocity);\n                }\n                tone_status[tone] = MIDI_INVALID_NOTE;\n            }\n            return false;\n        }\n        case MIDI_OCTAVE_MIN ... MIDI_OCTAVE_MAX:\n            if (record->event.pressed) {\n                midi_config.octave = keycode - MIDI_OCTAVE_MIN;\n                dprintf(\"midi octave %d\\n\", midi_config.octave);\n            }\n            return false;\n        case QK_MIDI_OCTAVE_DOWN:\n            if (record->event.pressed && midi_config.octave > 0) {\n                midi_config.octave--;\n                dprintf(\"midi octave %d\\n\", midi_config.octave);\n            }\n            return false;\n        case QK_MIDI_OCTAVE_UP:\n            if (record->event.pressed && midi_config.octave < (MIDI_OCTAVE_MAX - MIDI_OCTAVE_MIN)) {\n                midi_config.octave++;\n                dprintf(\"midi octave %d\\n\", midi_config.octave);\n            }\n            return false;\n        case MIDI_TRANSPOSE_MIN ... MIDI_TRANSPOSE_MAX:\n            if (record->event.pressed) {\n                midi_config.transpose = keycode - QK_MIDI_TRANSPOSE_0;\n                dprintf(\"midi transpose %d\\n\", midi_config.transpose);\n            }\n            return false;\n        case QK_MIDI_TRANSPOSE_DOWN:\n            if (record->event.pressed && midi_config.transpose > (MIDI_TRANSPOSE_MIN - QK_MIDI_TRANSPOSE_0)) {\n                midi_config.transpose--;\n                dprintf(\"midi transpose %d\\n\", midi_config.transpose);\n            }\n            return false;\n        case QK_MIDI_TRANSPOSE_UP:\n            if (record->event.pressed && midi_config.transpose < (MIDI_TRANSPOSE_MAX - QK_MIDI_TRANSPOSE_0)) {\n                const bool positive = midi_config.transpose > 0;\n                midi_config.transpose++;\n                if (positive && midi_config.transpose < 0) midi_config.transpose--;\n                dprintf(\"midi transpose %d\\n\", midi_config.transpose);\n            }\n            return false;\n        case MIDI_VELOCITY_MIN ... MIDI_VELOCITY_MAX:\n            if (record->event.pressed) {\n                midi_config.velocity = compute_velocity(keycode - MIDI_VELOCITY_MIN);\n                dprintf(\"midi velocity %d\\n\", midi_config.velocity);\n            }\n            return false;\n        case QK_MIDI_VELOCITY_DOWN:\n            if (record->event.pressed && midi_config.velocity > 0) {\n                if (midi_config.velocity == 127) {\n                    midi_config.velocity -= 10;\n                } else if (midi_config.velocity > 12) {\n                    midi_config.velocity -= 13;\n                } else {\n                    midi_config.velocity = 0;\n                }\n\n                dprintf(\"midi velocity %d\\n\", midi_config.velocity);\n            }\n            return false;\n        case QK_MIDI_VELOCITY_UP:\n            if (record->event.pressed && midi_config.velocity < 127) {\n                if (midi_config.velocity < 115) {\n                    midi_config.velocity += 13;\n                } else {\n                    midi_config.velocity = 127;\n                }\n                dprintf(\"midi velocity %d\\n\", midi_config.velocity);\n            }\n            return false;\n        case MIDI_CHANNEL_MIN ... MIDI_CHANNEL_MAX:\n            if (record->event.pressed) {\n                midi_config.channel = keycode - MIDI_CHANNEL_MIN;\n                dprintf(\"midi channel %d\\n\", midi_config.channel);\n            }\n            return false;\n        case QK_MIDI_CHANNEL_DOWN:\n            if (record->event.pressed) {\n                midi_config.channel--;\n                dprintf(\"midi channel %d\\n\", midi_config.channel);\n            }\n            return false;\n        case QK_MIDI_CHANNEL_UP:\n            if (record->event.pressed) {\n                midi_config.channel++;\n                dprintf(\"midi channel %d\\n\", midi_config.channel);\n            }\n            return false;\n        case QK_MIDI_ALL_NOTES_OFF:\n            if (record->event.pressed) {\n                midi_send_cc(&midi_device, midi_config.channel, 0x7B, 0);\n                dprintf(\"midi all notes off\\n\");\n            }\n            return false;\n        case QK_MIDI_SUSTAIN:\n            midi_send_cc(&midi_device, midi_config.channel, 0x40, record->event.pressed ? 127 : 0);\n            dprintf(\"midi sustain %d\\n\", record->event.pressed);\n            return false;\n        case QK_MIDI_PORTAMENTO:\n            midi_send_cc(&midi_device, midi_config.channel, 0x41, record->event.pressed ? 127 : 0);\n            dprintf(\"midi portamento %d\\n\", record->event.pressed);\n            return false;\n        case QK_MIDI_SOSTENUTO:\n            midi_send_cc(&midi_device, midi_config.channel, 0x42, record->event.pressed ? 127 : 0);\n            dprintf(\"midi sostenuto %d\\n\", record->event.pressed);\n            return false;\n        case QK_MIDI_SOFT:\n            midi_send_cc(&midi_device, midi_config.channel, 0x43, record->event.pressed ? 127 : 0);\n            dprintf(\"midi soft %d\\n\", record->event.pressed);\n            return false;\n        case QK_MIDI_LEGATO:\n            midi_send_cc(&midi_device, midi_config.channel, 0x44, record->event.pressed ? 127 : 0);\n            dprintf(\"midi legato %d\\n\", record->event.pressed);\n            return false;\n        case QK_MIDI_MODULATION:\n            midi_modulation_step = record->event.pressed ? 1 : -1;\n            return false;\n        case QK_MIDI_MODULATION_SPEED_DOWN:\n            if (record->event.pressed) {\n                midi_config.modulation_interval++;\n                // prevent overflow\n                if (midi_config.modulation_interval == 0) midi_config.modulation_interval--;\n                dprintf(\"midi modulation interval %d\\n\", midi_config.modulation_interval);\n            }\n            return false;\n        case QK_MIDI_MODULATION_SPEED_UP:\n            if (record->event.pressed && midi_config.modulation_interval > 0) {\n                midi_config.modulation_interval--;\n                dprintf(\"midi modulation interval %d\\n\", midi_config.modulation_interval);\n            }\n            return false;\n        case QK_MIDI_PITCH_BEND_DOWN:\n            if (record->event.pressed) {\n                midi_send_pitchbend(&midi_device, midi_config.channel, -0x2000);\n                dprintf(\"midi pitchbend channel:%d amount:%d\\n\", midi_config.channel, -0x2000);\n            } else {\n                midi_send_pitchbend(&midi_device, midi_config.channel, 0);\n                dprintf(\"midi pitchbend channel:%d amount:%d\\n\", midi_config.channel, 0);\n            }\n            return false;\n        case QK_MIDI_PITCH_BEND_UP:\n            if (record->event.pressed) {\n                midi_send_pitchbend(&midi_device, midi_config.channel, 0x1fff);\n                dprintf(\"midi pitchbend channel:%d amount:%d\\n\", midi_config.channel, 0x1fff);\n            } else {\n                midi_send_pitchbend(&midi_device, midi_config.channel, 0);\n                dprintf(\"midi pitchbend channel:%d amount:%d\\n\", midi_config.channel, 0);\n            }\n            return false;\n    };\n\n    return true;\n}\n\n#endif // MIDI_ADVANCED\n\nvoid midi_task(void) {\n    midi_device_process(&midi_device);\n#ifdef MIDI_ADVANCED\n    if (timer_elapsed(midi_modulation_timer) < midi_config.modulation_interval) return;\n    midi_modulation_timer = timer_read();\n\n    if (midi_modulation_step != 0) {\n        dprintf(\"midi modulation %d\\n\", midi_modulation);\n        midi_send_cc(&midi_device, midi_config.channel, 0x1, midi_modulation);\n\n        if (midi_modulation_step < 0 && midi_modulation < -midi_modulation_step) {\n            midi_modulation      = 0;\n            midi_modulation_step = 0;\n            return;\n        }\n\n        midi_modulation += midi_modulation_step;\n\n        if (midi_modulation > 127) midi_modulation = 127;\n    }\n#endif\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_midi.h",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n#include \"quantum_keycodes.h\"\n\n#ifdef MIDI_ENABLE\n\n#    ifdef MIDI_BASIC\nvoid process_midi_basic_noteon(uint8_t note);\nvoid process_midi_basic_noteoff(uint8_t note);\nvoid process_midi_all_notes_off(void);\n#    endif\n\nvoid midi_task(void);\n\n#    ifdef MIDI_ADVANCED\ntypedef union {\n    uint32_t raw;\n    struct {\n        uint8_t octave : 4;\n        int8_t  transpose : 4;\n        uint8_t velocity : 7;\n        uint8_t channel : 4;\n        uint8_t modulation_interval : 4;\n    };\n} midi_config_t;\n\nextern midi_config_t midi_config;\n\nvoid midi_init(void);\nbool process_midi(uint16_t keycode, keyrecord_t *record);\n\n#        define MIDI_INVALID_NOTE 0xFF\n#        define MIDI_TONE_COUNT (MIDI_TONE_MAX - MIDI_TONE_MIN + 1)\n\nuint8_t midi_compute_note(uint16_t keycode);\n#    endif // MIDI_ADVANCED\n\n#endif // MIDI_ENABLE\n"
  },
  {
    "path": "quantum/process_keycode/process_music.c",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"process_music.h\"\n#include \"timer.h\"\n\n#ifdef AUDIO_ENABLE\n#    include \"audio.h\"\n#    include \"process_audio.h\"\n#endif\n#if defined(MIDI_ENABLE) && defined(MIDI_BASIC)\n#    include \"process_midi.h\"\n#endif\n\n#if defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))\n\nbool    music_activated     = false;\nbool    midi_activated      = false;\nuint8_t music_starting_note = 0x0C;\nint     music_offset        = 7;\nuint8_t music_mode          = MUSIC_MODE_MAJOR;\n\n// music sequencer\nstatic bool    music_sequence_recording = false;\nstatic bool    music_sequence_recorded  = false;\nstatic bool    music_sequence_playing   = false;\nstatic uint8_t music_sequence[16]       = {0};\nstatic uint8_t music_sequence_count     = 0;\nstatic uint8_t music_sequence_position  = 0;\n\nstatic uint16_t music_sequence_timer    = 0;\nstatic uint16_t music_sequence_interval = 100;\n\n#    ifdef AUDIO_ENABLE\n#        ifndef MUSIC_ON_SONG\n#            define MUSIC_ON_SONG SONG(MUSIC_ON_SOUND)\n#        endif\n#        ifndef MUSIC_OFF_SONG\n#            define MUSIC_OFF_SONG SONG(MUSIC_OFF_SOUND)\n#        endif\n#        ifndef MIDI_ON_SONG\n#            define MIDI_ON_SONG SONG(MUSIC_ON_SOUND)\n#        endif\n#        ifndef MIDI_OFF_SONG\n#            define MIDI_OFF_SONG SONG(MUSIC_OFF_SOUND)\n#        endif\n#        ifndef CHROMATIC_SONG\n#            define CHROMATIC_SONG SONG(CHROMATIC_SOUND)\n#        endif\n#        ifndef GUITAR_SONG\n#            define GUITAR_SONG SONG(GUITAR_SOUND)\n#        endif\n#        ifndef VIOLIN_SONG\n#            define VIOLIN_SONG SONG(VIOLIN_SOUND)\n#        endif\n#        ifndef MAJOR_SONG\n#            define MAJOR_SONG SONG(MAJOR_SOUND)\n#        endif\nfloat music_mode_songs[NUMBER_OF_MODES][5][2] = {CHROMATIC_SONG, GUITAR_SONG, VIOLIN_SONG, MAJOR_SONG};\nfloat music_on_song[][2]                      = MUSIC_ON_SONG;\nfloat music_off_song[][2]                     = MUSIC_OFF_SONG;\nfloat midi_on_song[][2]                       = MIDI_ON_SONG;\nfloat midi_off_song[][2]                      = MIDI_OFF_SONG;\n#    endif\n\nstatic void music_noteon(uint8_t note) {\n#    ifdef AUDIO_ENABLE\n    if (music_activated) process_audio_noteon(note);\n#    endif\n#    if defined(MIDI_ENABLE) && defined(MIDI_BASIC)\n    if (midi_activated) process_midi_basic_noteon(note);\n#    endif\n}\n\nstatic void music_noteoff(uint8_t note) {\n#    ifdef AUDIO_ENABLE\n    if (music_activated) process_audio_noteoff(note);\n#    endif\n#    if defined(MIDI_ENABLE) && defined(MIDI_BASIC)\n    if (midi_activated) process_midi_basic_noteoff(note);\n#    endif\n}\n\nvoid music_all_notes_off(void) {\n#    ifdef AUDIO_ENABLE\n    if (music_activated) process_audio_all_notes_off();\n#    endif\n#    if defined(MIDI_ENABLE) && defined(MIDI_BASIC)\n    if (midi_activated) process_midi_all_notes_off();\n#    endif\n}\n\nbool process_music(uint16_t keycode, keyrecord_t *record) {\n    if (keycode == QK_MUSIC_ON && record->event.pressed) {\n        music_on();\n        return false;\n    }\n\n    if (keycode == QK_MUSIC_OFF && record->event.pressed) {\n        music_off();\n        return false;\n    }\n\n    if (keycode == QK_MUSIC_TOGGLE && record->event.pressed) {\n        if (music_activated) {\n            music_off();\n        } else {\n            music_on();\n        }\n        return false;\n    }\n\n    if (keycode == QK_MIDI_ON && record->event.pressed) {\n        midi_on();\n        return false;\n    }\n\n    if (keycode == QK_MIDI_OFF && record->event.pressed) {\n        midi_off();\n        return false;\n    }\n\n    if (keycode == QK_MIDI_TOGGLE && record->event.pressed) {\n        if (midi_activated) {\n            midi_off();\n        } else {\n            midi_on();\n        }\n        return false;\n    }\n\n    if (keycode == QK_MUSIC_MODE_NEXT && record->event.pressed) {\n        music_mode_cycle();\n        return false;\n    }\n\n    if (music_activated || midi_activated) {\n        if (record->event.pressed) {\n            if (keycode == KC_LEFT_CTRL) { // Start recording\n                music_all_notes_off();\n                music_sequence_recording = true;\n                music_sequence_recorded  = false;\n                music_sequence_playing   = false;\n                music_sequence_count     = 0;\n                return false;\n            }\n\n            if (keycode == KC_LEFT_ALT) { // Stop recording/playing\n                music_all_notes_off();\n                if (music_sequence_recording) { // was recording\n                    music_sequence_recorded = true;\n                }\n                music_sequence_recording = false;\n                music_sequence_playing   = false;\n                return false;\n            }\n\n            if (keycode == KC_LEFT_GUI && music_sequence_recorded) { // Start playing\n                music_all_notes_off();\n                music_sequence_recording = false;\n                music_sequence_playing   = true;\n                music_sequence_position  = 0;\n                music_sequence_timer     = 0;\n                return false;\n            }\n\n            if (keycode == KC_UP) {\n                music_sequence_interval -= 10;\n                return false;\n            }\n\n            if (keycode == KC_DOWN) {\n                music_sequence_interval += 10;\n                return false;\n            }\n        }\n\n        uint8_t note = 36;\n#    ifdef MUSIC_MAP\n        if (music_mode == MUSIC_MODE_CHROMATIC) {\n            note = music_starting_note + music_offset + 36 + music_map[record->event.key.row][record->event.key.col];\n        } else {\n            uint8_t position = music_map[record->event.key.row][record->event.key.col];\n            note             = music_starting_note + music_offset + 36 + SCALE[position % 7] + (position / 7) * 12;\n        }\n#    else\n        if (music_mode == MUSIC_MODE_CHROMATIC)\n            note = (music_starting_note + record->event.key.col + music_offset - 3) + 12 * (MATRIX_ROWS - record->event.key.row);\n        else if (music_mode == MUSIC_MODE_GUITAR)\n            note = (music_starting_note + record->event.key.col + music_offset + 32) + 5 * (MATRIX_ROWS - record->event.key.row);\n        else if (music_mode == MUSIC_MODE_VIOLIN)\n            note = (music_starting_note + record->event.key.col + music_offset + 32) + 7 * (MATRIX_ROWS - record->event.key.row);\n        else if (music_mode == MUSIC_MODE_MAJOR)\n            note = (music_starting_note + SCALE[record->event.key.col + music_offset] - 3) + 12 * (MATRIX_ROWS - record->event.key.row);\n        else\n            note = music_starting_note;\n#    endif\n\n        if (record->event.pressed) {\n            music_noteon(note);\n            if (music_sequence_recording) {\n                music_sequence[music_sequence_count] = note;\n                music_sequence_count++;\n            }\n        } else {\n            music_noteoff(note);\n        }\n\n        if (music_mask(keycode)) return false;\n    }\n\n    return true;\n}\n\nbool music_mask(uint16_t keycode) {\n#    ifdef MUSIC_MASK\n    return MUSIC_MASK;\n#    else\n    return music_mask_kb(keycode);\n#    endif\n}\n\n__attribute__((weak)) bool music_mask_kb(uint16_t keycode) {\n    return music_mask_user(keycode);\n}\n\n__attribute__((weak)) bool music_mask_user(uint16_t keycode) {\n    return keycode < 0xFF;\n}\n\nbool is_music_on(void) {\n    return (music_activated != 0);\n}\n\nvoid music_toggle(void) {\n    if (!music_activated) {\n        music_on();\n    } else {\n        music_off();\n    }\n}\n\nvoid music_on(void) {\n    music_activated = 1;\n#    ifdef AUDIO_ENABLE\n    PLAY_SONG(music_on_song);\n#    endif\n    music_on_user();\n}\n\nvoid music_off(void) {\n    music_all_notes_off();\n    music_activated = 0;\n#    ifdef AUDIO_ENABLE\n    PLAY_SONG(music_off_song);\n#    endif\n}\n\nbool is_midi_on(void) {\n    return (midi_activated != 0);\n}\n\nvoid midi_toggle(void) {\n    if (!midi_activated) {\n        midi_on();\n    } else {\n        midi_off();\n    }\n}\n\nvoid midi_on(void) {\n    midi_activated = 1;\n#    ifdef AUDIO_ENABLE\n    PLAY_SONG(midi_on_song);\n#    endif\n    midi_on_user();\n}\n\nvoid midi_off(void) {\n#    if defined(MIDI_ENABLE) && defined(MIDI_BASIC)\n    process_midi_all_notes_off();\n#    endif\n    midi_activated = 0;\n#    ifdef AUDIO_ENABLE\n    PLAY_SONG(midi_off_song);\n#    endif\n}\n\nvoid music_mode_cycle(void) {\n    music_all_notes_off();\n    music_mode = (music_mode + 1) % NUMBER_OF_MODES;\n#    ifdef AUDIO_ENABLE\n    PLAY_SONG(music_mode_songs[music_mode]);\n#    endif\n}\n\nvoid music_task(void) {\n    if (music_sequence_playing) {\n        if ((music_sequence_timer == 0) || (timer_elapsed(music_sequence_timer) > music_sequence_interval)) {\n            music_sequence_timer = timer_read();\n            uint8_t prev_note    = music_sequence[(music_sequence_position - 1 < 0) ? (music_sequence_position - 1 + music_sequence_count) : (music_sequence_position - 1)];\n            uint8_t next_note    = music_sequence[music_sequence_position];\n            music_noteoff(prev_note);\n            music_noteon(next_note);\n            music_sequence_position = (music_sequence_position + 1) % music_sequence_count;\n        }\n    }\n}\n\n__attribute__((weak)) void music_on_user(void) {}\n\n__attribute__((weak)) void midi_on_user(void) {}\n\n__attribute__((weak)) void music_scale_user(void) {}\n\n#endif // defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))\n"
  },
  {
    "path": "quantum/process_keycode/process_music.h",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\n#if defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))\n\nenum music_modes { MUSIC_MODE_CHROMATIC, MUSIC_MODE_GUITAR, MUSIC_MODE_VIOLIN, MUSIC_MODE_MAJOR, NUMBER_OF_MODES };\n\n#    ifdef MUSIC_MAP\nextern const uint8_t music_map[MATRIX_ROWS][MATRIX_COLS];\n#    endif\n\nbool process_music(uint16_t keycode, keyrecord_t *record);\n\nbool is_music_on(void);\nvoid music_toggle(void);\nvoid music_on(void);\nvoid music_off(void);\n\nbool is_midi_on(void);\nvoid midi_toggle(void);\nvoid midi_on(void);\nvoid midi_off(void);\n\nvoid music_on_user(void);\nvoid midi_on_user(void);\nvoid music_scale_user(void);\nvoid music_all_notes_off(void);\nvoid music_mode_cycle(void);\n\nvoid music_task(void);\n\nbool music_mask(uint16_t keycode);\nbool music_mask_kb(uint16_t keycode);\nbool music_mask_user(uint16_t keycode);\n\n#    ifndef SCALE\n#        define SCALE                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          \\\n            (int8_t[]) {                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       \\\n                0 + (12 * 0), 2 + (12 * 0), 4 + (12 * 0), 5 + (12 * 0), 7 + (12 * 0), 9 + (12 * 0), 11 + (12 * 0), 0 + (12 * 1), 2 + (12 * 1), 4 + (12 * 1), 5 + (12 * 1), 7 + (12 * 1), 9 + (12 * 1), 11 + (12 * 1), 0 + (12 * 2), 2 + (12 * 2), 4 + (12 * 2), 5 + (12 * 2), 7 + (12 * 2), 9 + (12 * 2), 11 + (12 * 2), 0 + (12 * 3), 2 + (12 * 3), 4 + (12 * 3), 5 + (12 * 3), 7 + (12 * 3), 9 + (12 * 3), 11 + (12 * 3), 0 + (12 * 4), 2 + (12 * 4), 4 + (12 * 4), 5 + (12 * 4), 7 + (12 * 4), 9 + (12 * 4), 11 + (12 * 4), \\\n            }\n#    endif\n\n#endif // defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))\n"
  },
  {
    "path": "quantum/process_keycode/process_programmable_button.c",
    "content": "/*\nCopyright 2021 Thomas Weißschuh <thomas@t-8ch.de>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"process_programmable_button.h\"\n#include \"programmable_button.h\"\n\nbool process_programmable_button(uint16_t keycode, keyrecord_t *record) {\n    if (IS_QK_PROGRAMMABLE_BUTTON(keycode)) {\n        uint8_t button = keycode - QK_PROGRAMMABLE_BUTTON + 1;\n        if (record->event.pressed) {\n            programmable_button_register(button);\n        } else {\n            programmable_button_unregister(button);\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_programmable_button.h",
    "content": "/*\nCopyright 2021 Thomas Weißschuh <thomas@t-8ch.de>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_programmable_button(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_repeat_key.c",
    "content": "// Copyright 2022-2023 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"process_repeat_key.h\"\n#include \"repeat_key.h\"\n#include \"keycodes.h\"\n#include \"quantum_keycodes.h\"\n#include \"action_util.h\"\n\n// Default implementation of remember_last_key_user().\n__attribute__((weak)) bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) {\n    return true;\n}\n\nstatic bool remember_last_key(uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) {\n    switch (keycode) {\n        // Ignore MO, TO, TG, TT, and TL layer switch keys.\n        case QK_MOMENTARY ... QK_MOMENTARY_MAX:\n        case QK_TO ... QK_TO_MAX:\n        case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:\n        case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:\n        // Ignore mod keys.\n        case KC_LCTL ... KC_RGUI:\n        case KC_HYPR:\n        case KC_MEH:\n#ifndef NO_ACTION_ONESHOT // Ignore one-shot keys.\n        case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:\n        case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:\n#endif                  // NO_ACTION_ONESHOT\n#ifdef TRI_LAYER_ENABLE // Ignore Tri Layer keys.\n        case QK_TRI_LAYER_LOWER:\n        case QK_TRI_LAYER_UPPER:\n#endif                   // TRI_LAYER_ENABLE\n#ifdef LAYER_LOCK_ENABLE // Ignore Layer Lock key.\n        case QK_LAYER_LOCK:\n#endif // LAYER_LOCK_ENABLE\n            return false;\n\n            // Ignore hold events on tap-hold keys.\n#ifndef NO_ACTION_TAPPING\n        case QK_MOD_TAP ... QK_MOD_TAP_MAX:\n#    ifndef NO_ACTION_LAYER\n        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:\n#    endif // NO_ACTION_LAYER\n            if (record->tap.count == 0) {\n                return false;\n            }\n            break;\n#endif // NO_ACTION_TAPPING\n\n#ifdef SWAP_HANDS_ENABLE\n        case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:\n            if (IS_SWAP_HANDS_KEYCODE(keycode) || record->tap.count == 0) {\n                return false;\n            }\n            break;\n#endif // SWAP_HANDS_ENABLE\n\n        case QK_REPEAT_KEY:\n#ifndef NO_ALT_REPEAT_KEY\n        case QK_ALT_REPEAT_KEY:\n#endif // NO_ALT_REPEAT_KEY\n            return false;\n    }\n\n    return remember_last_key_user(keycode, record, remembered_mods);\n}\n\nbool process_last_key(uint16_t keycode, keyrecord_t* record) {\n    if (get_repeat_key_count()) {\n        return true;\n    }\n\n    if (record->event.pressed) {\n        uint8_t remembered_mods = get_mods() | get_weak_mods();\n#ifndef NO_ACTION_ONESHOT\n        remembered_mods |= get_oneshot_mods();\n#endif // NO_ACTION_ONESHOT\n\n        if (remember_last_key(keycode, record, &remembered_mods)) {\n            set_last_record(keycode, record);\n            set_last_mods(remembered_mods);\n        }\n    }\n\n    return true;\n}\n\nbool process_repeat_key(uint16_t keycode, keyrecord_t* record) {\n    if (get_repeat_key_count()) {\n        return true;\n    }\n\n    if (keycode == QK_REPEAT_KEY) {\n        repeat_key_invoke(&record->event);\n        return false;\n#ifndef NO_ALT_REPEAT_KEY\n    } else if (keycode == QK_ALT_REPEAT_KEY) {\n        alt_repeat_key_invoke(&record->event);\n        return false;\n#endif // NO_ALT_REPEAT_KEY\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_repeat_key.h",
    "content": "// Copyright 2022-2023 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\n/**\n * @brief Process handler for remembering the last key.\n *\n * @param keycode  Keycode registered by matrix press, per keymap\n * @param record   keyrecord_t structure\n * @return true    Continue processing keycodes, and send to host\n * @return false   Stop processing keycodes, and don't send to host\n */\nbool process_last_key(uint16_t keycode, keyrecord_t* record);\n\n/**\n * @brief Optional callback defining which keys are remembered.\n *\n * @param keycode          Keycode that was just pressed\n * @param record           keyrecord_t structure\n * @param remembered_mods  Mods that will be remembered with this key\n * @return true            Key is remembered\n * @return false           Key is ignored\n *\n * Modifier and layer switch keys are always ignored. For all other keys, this\n * callback is called on every key press. Returning true means that the key is\n * remembered, false means it is ignored. By default, all non-modifier,\n * non-layer switch keys are remembered.\n *\n * The `remembered_mods` arg represents the mods that will be remembered with\n * this key. It can be modified to forget certain mods, for instance to forget\n * capitalization when repeating shifted letters:\n *\n *     // Forget Shift on letter keys.\n *     if (KC_A <= keycode && keycode <= KC_Z && (*remembered_mods & ~MOD_MASK_SHIFT) == 0) {\n *         *remembered_mods = 0;\n *     }\n */\nbool remember_last_key_user(uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods);\n\n/**\n * @brief Process handler for Repeat Key feature.\n *\n * @param keycode  Keycode registered by matrix press, per keymap\n * @param record   keyrecord_t structure\n * @return true    Continue processing keycodes, and send to host\n * @return false   Stop processing keycodes, and don't send to host\n */\nbool process_repeat_key(uint16_t keycode, keyrecord_t* record);\n"
  },
  {
    "path": "quantum/process_keycode/process_rgb_matrix.c",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"process_rgb_matrix.h\"\n#include \"rgb_matrix.h\"\n#include \"action_util.h\"\n#include \"keycodes.h\"\n#include \"modifiers.h\"\n\nbool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) {\n#ifdef RGB_TRIGGER_ON_KEYDOWN\n    if (record->event.pressed) {\n#else\n    if (!record->event.pressed) {\n#endif\n        bool shifted = get_mods() & MOD_MASK_SHIFT;\n        switch (keycode) {\n            case QK_RGB_MATRIX_ON:\n                rgb_matrix_enable();\n                return false;\n            case QK_RGB_MATRIX_OFF:\n                rgb_matrix_disable();\n                return false;\n            case QK_RGB_MATRIX_TOGGLE:\n                rgb_matrix_toggle();\n                return false;\n            case QK_RGB_MATRIX_MODE_NEXT:\n                if (shifted) {\n                    rgb_matrix_step_reverse();\n                } else {\n                    rgb_matrix_step();\n                }\n                return false;\n            case QK_RGB_MATRIX_MODE_PREVIOUS:\n                if (shifted) {\n                    rgb_matrix_step();\n                } else {\n                    rgb_matrix_step_reverse();\n                }\n                return false;\n            case QK_RGB_MATRIX_HUE_UP:\n                if (shifted) {\n                    rgb_matrix_decrease_hue();\n                } else {\n                    rgb_matrix_increase_hue();\n                }\n                return false;\n            case QK_RGB_MATRIX_HUE_DOWN:\n                if (shifted) {\n                    rgb_matrix_increase_hue();\n                } else {\n                    rgb_matrix_decrease_hue();\n                }\n                return false;\n            case QK_RGB_MATRIX_SATURATION_UP:\n                if (shifted) {\n                    rgb_matrix_decrease_sat();\n                } else {\n                    rgb_matrix_increase_sat();\n                }\n                return false;\n            case QK_RGB_MATRIX_SATURATION_DOWN:\n                if (shifted) {\n                    rgb_matrix_increase_sat();\n                } else {\n                    rgb_matrix_decrease_sat();\n                }\n                return false;\n            case QK_RGB_MATRIX_VALUE_UP:\n                if (shifted) {\n                    rgb_matrix_decrease_val();\n                } else {\n                    rgb_matrix_increase_val();\n                }\n                return false;\n            case QK_RGB_MATRIX_VALUE_DOWN:\n                if (shifted) {\n                    rgb_matrix_increase_val();\n                } else {\n                    rgb_matrix_decrease_val();\n                }\n                return false;\n            case QK_RGB_MATRIX_SPEED_UP:\n                if (shifted) {\n                    rgb_matrix_decrease_speed();\n                } else {\n                    rgb_matrix_increase_speed();\n                }\n                return false;\n            case QK_RGB_MATRIX_SPEED_DOWN:\n                if (shifted) {\n                    rgb_matrix_increase_speed();\n                } else {\n                    rgb_matrix_decrease_speed();\n                }\n                return false;\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_rgb_matrix.h",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_rgb_matrix(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_secure.c",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"secure.h\"\n#include \"process_secure.h\"\n#include \"quantum_keycodes.h\"\n\nbool preprocess_secure(uint16_t keycode, keyrecord_t *record) {\n    if (secure_is_unlocking()) {\n        // !pressed will trigger on any already held keys (such as layer keys),\n        // and cause the request secure check to prematurely fail.\n        if (record->event.pressed) {\n            secure_keypress_event(record->event.key.row, record->event.key.col);\n        }\n\n        // Normal keypresses should be disabled until the sequence is completed\n        return false;\n    }\n\n    return true;\n}\n\nbool process_secure(uint16_t keycode, keyrecord_t *record) {\n#ifndef SECURE_DISABLE_KEYCODES\n    if (!record->event.pressed) {\n        if (keycode == QK_SECURE_LOCK) {\n            secure_lock();\n            return false;\n        }\n        if (keycode == QK_SECURE_UNLOCK) {\n            secure_unlock();\n            return false;\n        }\n        if (keycode == QK_SECURE_TOGGLE) {\n            secure_is_locked() ? secure_unlock() : secure_lock();\n            return false;\n        }\n        if (keycode == QK_SECURE_REQUEST) {\n            secure_request_unlock();\n            return false;\n        }\n    }\n#endif\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_secure.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\n/** \\brief Intercept keycodes and detect unlock sequences\n */\nbool preprocess_secure(uint16_t keycode, keyrecord_t *record);\n\n/** \\brief Handle any secure specific keycodes\n */\nbool process_secure(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_sequencer.c",
    "content": "/* Copyright 2020 Rodolphe Belouin\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_sequencer.h\"\n\nbool process_sequencer(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n        switch (keycode) {\n            case QK_SEQUENCER_ON:\n                sequencer_on();\n                return false;\n            case QK_SEQUENCER_OFF:\n                sequencer_off();\n                return false;\n            case QK_SEQUENCER_TOGGLE:\n                sequencer_toggle();\n                return false;\n            case QK_SEQUENCER_TEMPO_DOWN:\n                sequencer_decrease_tempo();\n                return false;\n            case QK_SEQUENCER_TEMPO_UP:\n                sequencer_increase_tempo();\n                return false;\n            case QK_SEQUENCER_RESOLUTION_DOWN:\n                sequencer_decrease_resolution();\n                return false;\n            case QK_SEQUENCER_RESOLUTION_UP:\n                sequencer_increase_resolution();\n                return false;\n            case QK_SEQUENCER_STEPS_ALL:\n                sequencer_set_all_steps_on();\n                return false;\n            case QK_SEQUENCER_STEPS_CLEAR:\n                sequencer_set_all_steps_off();\n                return false;\n            case SEQUENCER_STEP_MIN ... SEQUENCER_STEP_MAX:\n                sequencer_toggle_step(keycode - SEQUENCER_STEP_MIN);\n                return false;\n            case SEQUENCER_RESOLUTION_MIN ... SEQUENCER_RESOLUTION_MAX:\n                sequencer_set_resolution(keycode - SEQUENCER_RESOLUTION_MIN);\n                return false;\n            case SEQUENCER_TRACK_MIN ... SEQUENCER_TRACK_MAX:\n                sequencer_toggle_single_active_track(keycode - SEQUENCER_TRACK_MIN);\n                return false;\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_sequencer.h",
    "content": "/* Copyright 2020 Rodolphe Belouin\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_sequencer(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_space_cadet.c",
    "content": "/* Copyright 2019 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_space_cadet.h\"\n#include \"keycodes.h\"\n#include \"timer.h\"\n#include \"action.h\"\n#include \"action_tapping.h\"\n#include \"action_util.h\"\n\n// ********** OBSOLETE DEFINES, STOP USING! (pls?) **********\n// Shift / paren setup\n#ifndef LSPO_KEY\n#    define LSPO_KEY KC_9\n#endif\n#ifndef RSPC_KEY\n#    define RSPC_KEY KC_0\n#endif\n\n// Shift / Enter setup\n#ifndef SFTENT_KEY\n#    define SFTENT_KEY KC_ENTER\n#endif\n\n#ifdef DISABLE_SPACE_CADET_MODIFIER\n#    ifndef LSPO_MOD\n#        define LSPO_MOD KC_TRANSPARENT\n#    endif\n#    ifndef RSPC_MOD\n#        define RSPC_MOD KC_TRANSPARENT\n#    endif\n#else\n#    ifndef LSPO_MOD\n#        define LSPO_MOD KC_LEFT_SHIFT\n#    endif\n#    ifndef RSPC_MOD\n#        define RSPC_MOD KC_RIGHT_SHIFT\n#    endif\n#endif\n// **********************************************************\n\n// Shift / paren setup\n#ifndef LSPO_KEYS\n#    define LSPO_KEYS KC_LEFT_SHIFT, LSPO_MOD, LSPO_KEY\n#endif\n#ifndef RSPC_KEYS\n#    define RSPC_KEYS KC_RIGHT_SHIFT, RSPC_MOD, RSPC_KEY\n#endif\n\n// Control / paren setup\n#ifndef LCPO_KEYS\n#    define LCPO_KEYS KC_LEFT_CTRL, KC_LEFT_SHIFT, KC_9\n#endif\n#ifndef RCPC_KEYS\n#    define RCPC_KEYS KC_RIGHT_CTRL, KC_RIGHT_SHIFT, KC_0\n#endif\n\n// Alt / paren setup\n#ifndef LAPO_KEYS\n#    define LAPO_KEYS KC_LEFT_ALT, KC_LEFT_SHIFT, KC_9\n#endif\n#ifndef RAPC_KEYS\n#    define RAPC_KEYS KC_RIGHT_ALT, KC_RIGHT_SHIFT, KC_0\n#endif\n\n// Shift / Enter setup\n#ifndef SFTENT_KEYS\n#    define SFTENT_KEYS KC_RIGHT_SHIFT, KC_TRANSPARENT, SFTENT_KEY\n#endif\n\nstatic uint8_t  sc_last  = 0;\nstatic uint16_t sc_timer = 0;\n#ifdef SPACE_CADET_MODIFIER_CARRYOVER\nstatic uint8_t sc_mods = 0;\n#endif\n\nvoid perform_space_cadet(keyrecord_t *record, uint16_t sc_keycode, uint8_t holdMod, uint8_t tapMod, uint8_t keycode) {\n    if (record->event.pressed) {\n        sc_last  = holdMod;\n        sc_timer = timer_read();\n#ifdef SPACE_CADET_MODIFIER_CARRYOVER\n        sc_mods = get_mods();\n#endif\n        if (IS_MODIFIER_KEYCODE(holdMod)) {\n            register_mods(MOD_BIT(holdMod));\n        }\n    } else {\n        if (sc_last == holdMod && timer_elapsed(sc_timer) < GET_TAPPING_TERM(sc_keycode, record)) {\n            if (holdMod != tapMod) {\n                if (IS_MODIFIER_KEYCODE(holdMod)) {\n                    unregister_mods(MOD_BIT(holdMod));\n                }\n                if (IS_MODIFIER_KEYCODE(tapMod)) {\n                    register_mods(MOD_BIT(tapMod));\n                }\n            }\n#ifdef SPACE_CADET_MODIFIER_CARRYOVER\n            set_weak_mods(sc_mods);\n#endif\n            tap_code(keycode);\n#ifdef SPACE_CADET_MODIFIER_CARRYOVER\n            clear_weak_mods();\n#endif\n            if (IS_MODIFIER_KEYCODE(tapMod)) {\n                unregister_mods(MOD_BIT(tapMod));\n            }\n        } else {\n            if (IS_MODIFIER_KEYCODE(holdMod)) {\n                unregister_mods(MOD_BIT(holdMod));\n            }\n        }\n    }\n}\n\nbool process_space_cadet(uint16_t keycode, keyrecord_t *record) {\n    switch (keycode) {\n        case QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN: {\n            perform_space_cadet(record, keycode, LSPO_KEYS);\n            return false;\n        }\n        case QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE: {\n            perform_space_cadet(record, keycode, RSPC_KEYS);\n            return false;\n        }\n        case QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN: {\n            perform_space_cadet(record, keycode, LCPO_KEYS);\n            return false;\n        }\n        case QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE: {\n            perform_space_cadet(record, keycode, RCPC_KEYS);\n            return false;\n        }\n        case QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN: {\n            perform_space_cadet(record, keycode, LAPO_KEYS);\n            return false;\n        }\n        case QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE: {\n            perform_space_cadet(record, keycode, RAPC_KEYS);\n            return false;\n        }\n        case QK_SPACE_CADET_RIGHT_SHIFT_ENTER: {\n            perform_space_cadet(record, keycode, SFTENT_KEYS);\n            return false;\n        }\n        default: {\n            if (record->event.pressed) {\n                reset_space_cadet();\n            }\n            break;\n        }\n    }\n    return true;\n}\n\nvoid reset_space_cadet() {\n    sc_last = 0;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_space_cadet.h",
    "content": "/* Copyright 2019 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nvoid perform_space_cadet(keyrecord_t *record, uint16_t sc_keycode, uint8_t holdMod, uint8_t tapMod, uint8_t keycode);\nbool process_space_cadet(uint16_t keycode, keyrecord_t *record);\nvoid reset_space_cadet(void);\n"
  },
  {
    "path": "quantum/process_keycode/process_steno.c",
    "content": "/* Copyright 2017, 2022 Joseph Wasson, Vladislav Kucheriavykh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include \"process_steno.h\"\n#include \"quantum_keycodes.h\"\n#include \"eeconfig.h\"\n#include <string.h>\n#ifdef VIRTSER_ENABLE\n#    include \"virtser.h\"\n#endif\n\n// All steno keys that have been pressed to form this chord,\n// stored in MAX_STROKE_SIZE groups of 8-bit arrays.\nstatic uint8_t chord[MAX_STROKE_SIZE] = {0};\n// The number of physical keys actually being held down.\n// This is not always equal to the number of 1 bits in `chord` because it is possible to\n// simultaneously press down four keys, then release three of those four keys and then press yet\n// another key while the fourth finger is still holding down its key.\n// At the end of this scenario given as an example, `chord` would have five bits set to 1 but\n// `n_pressed_keys` would be set to 2 because there are only two keys currently being pressed down.\nstatic int8_t n_pressed_keys = 0;\n\n#ifdef STENO_ENABLE_ALL\nstatic steno_mode_t mode;\n#elif defined(STENO_ENABLE_GEMINI)\nstatic const steno_mode_t mode = STENO_MODE_GEMINI;\n#elif defined(STENO_ENABLE_BOLT)\nstatic const steno_mode_t mode = STENO_MODE_BOLT;\n#endif\n\nstatic inline void steno_clear_chord(void) {\n    memset(chord, 0, sizeof(chord));\n}\n\n#ifdef STENO_ENABLE_GEMINI\n\n#    ifdef VIRTSER_ENABLE\nvoid send_steno_chord_gemini(void) {\n    // Set MSB to 1 to indicate the start of packet\n    chord[0] |= 0x80;\n    for (uint8_t i = 0; i < GEMINI_STROKE_SIZE; ++i) {\n        virtser_send(chord[i]);\n    }\n}\n#    else\n#        pragma message \"VIRTSER_ENABLE = yes is required for Gemini PR to work properly out of the box!\"\n#    endif // VIRTSER_ENABLE\n\n/**\n * @precondition: `key` is pressed\n */\nbool add_gemini_key_to_chord(uint8_t key) {\n    // Although each group of the packet is 8 bits long, the MSB is reserved\n    // to indicate whether that byte is the first byte of the packet (MSB=1)\n    // or one of the remaining five bytes of the packet (MSB=0).\n    // As a consequence, only 7 out of the 8 bits are left to be used as a bit array\n    // for the steno keys of that group.\n    const int group_idx       = key / 7;\n    const int intra_group_idx = key - group_idx * 7;\n    // The 0th steno key of the group has bit=0b01000000, the 1st has bit=0b00100000, etc.\n    const uint8_t bit = 1 << (6 - intra_group_idx);\n    chord[group_idx] |= bit;\n    return false;\n}\n#endif // STENO_ENABLE_GEMINI\n\n#ifdef STENO_ENABLE_BOLT\n\n#    define TXB_GRP0 0b00000000\n#    define TXB_GRP1 0b01000000\n#    define TXB_GRP2 0b10000000\n#    define TXB_GRP3 0b11000000\n#    define TXB_GRPMASK 0b11000000\n\n#    define TXB_GET_GROUP(code) ((code & TXB_GRPMASK) >> 6)\n\nstatic const uint8_t boltmap[64] PROGMEM = {TXB_NUL, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_S_L, TXB_S_L, TXB_T_L, TXB_K_L, TXB_P_L, TXB_W_L, TXB_H_L, TXB_R_L, TXB_A_L, TXB_O_L, TXB_STR, TXB_STR, TXB_NUL, TXB_NUL, TXB_NUL, TXB_STR, TXB_STR, TXB_E_R, TXB_U_R, TXB_F_R, TXB_R_R, TXB_P_R, TXB_B_R, TXB_L_R, TXB_G_R, TXB_T_R, TXB_S_R, TXB_D_R, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_Z_R};\n\n#    ifdef VIRTSER_ENABLE\nstatic void send_steno_chord_bolt(void) {\n    for (uint8_t i = 0; i < BOLT_STROKE_SIZE; ++i) {\n        // TX Bolt uses variable length packets where each byte corresponds to a bit array of certain keys.\n        // If a user chorded the keys of the first group with keys of the last group, for example, there\n        // would be bytes of 0x00 in `chord` for the middle groups which we mustn't send.\n        if (chord[i]) {\n            virtser_send(chord[i]);\n        }\n    }\n    // Sending a null packet is not always necessary, but it is simpler and more reliable\n    // to unconditionally send it every time instead of keeping track of more states and\n    // creating more branches in the execution of the program.\n    virtser_send(0);\n}\n#    else\n#        pragma message \"VIRTSER_ENABLE = yes is required for TX Bolt to work properly out of the box!\"\n#    endif // VIRTSER_ENABLE\n\n/**\n * @precondition: `key` is pressed\n */\nstatic bool add_bolt_key_to_chord(uint8_t key) {\n    uint8_t boltcode = pgm_read_byte(boltmap + key);\n    chord[TXB_GET_GROUP(boltcode)] |= boltcode;\n    return false;\n}\n#endif // STENO_ENABLE_BOLT\n\n#ifdef STENO_COMBINEDMAP\n/* Used to look up when pressing the middle row key to combine two consonant or vowel keys */\nstatic const uint16_t combinedmap_first[] PROGMEM  = {STN_S1, STN_TL, STN_PL, STN_HL, STN_FR, STN_PR, STN_LR, STN_TR, STN_DR, STN_A, STN_E};\nstatic const uint16_t combinedmap_second[] PROGMEM = {STN_S2, STN_KL, STN_WL, STN_RL, STN_RR, STN_BR, STN_GR, STN_SR, STN_ZR, STN_O, STN_U};\n#endif\n\n#ifdef STENO_ENABLE_ALL\nvoid steno_init(void) {\n    mode = eeconfig_read_steno_mode();\n}\n\nvoid steno_set_mode(steno_mode_t new_mode) {\n    steno_clear_chord();\n    mode = new_mode;\n    eeconfig_update_steno_mode(mode);\n}\n#endif // STENO_ENABLE_ALL\n\n/* override to intercept chords right before they get sent.\n * return zero to suppress normal sending behavior.\n */\n__attribute__((weak)) bool send_steno_chord_user(steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE]) {\n    return true;\n}\n\n__attribute__((weak)) bool post_process_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE], int8_t n_pressed_keys) {\n    return true;\n}\n\n__attribute__((weak)) bool process_steno_user(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\nbool process_steno(uint16_t keycode, keyrecord_t *record) {\n    if (keycode < QK_STENO || keycode > QK_STENO_MAX) {\n        return true; // Not a steno key, pass it further along the chain\n        /*\n         * Clearing or sending the chord state is not necessary as we intentionally ignore whatever\n         * normal keyboard keys the user may have tapped while chording steno keys.\n         */\n    }\n    if (IS_NOEVENT(record->event)) {\n        return true;\n    }\n    if (!process_steno_user(keycode, record)) {\n        return false; // User fully processed the steno key themselves\n    }\n    switch (keycode) {\n#ifdef STENO_ENABLE_ALL\n        case QK_STENO_BOLT:\n            if (record->event.pressed) {\n                steno_set_mode(STENO_MODE_BOLT);\n            }\n            return false;\n\n        case QK_STENO_GEMINI:\n            if (record->event.pressed) {\n                steno_set_mode(STENO_MODE_GEMINI);\n            }\n            return false;\n#endif // STENO_ENABLE_ALL\n\n#ifdef STENO_COMBINEDMAP\n        case QK_STENO_COMB ... QK_STENO_COMB_MAX: {\n            bool first_result  = process_steno(combinedmap_first[keycode - QK_STENO_COMB], record);\n            bool second_result = process_steno(combinedmap_second[keycode - QK_STENO_COMB], record);\n            return first_result && second_result;\n        }\n#endif // STENO_COMBINEDMAP\n        case STN__MIN ... STN__MAX:\n            if (record->event.pressed) {\n                n_pressed_keys++;\n                switch (mode) {\n#ifdef STENO_ENABLE_BOLT\n                    case STENO_MODE_BOLT:\n                        add_bolt_key_to_chord(keycode - QK_STENO);\n                        break;\n#endif // STENO_ENABLE_BOLT\n#ifdef STENO_ENABLE_GEMINI\n                    case STENO_MODE_GEMINI:\n                        add_gemini_key_to_chord(keycode - QK_STENO);\n                        break;\n#endif // STENO_ENABLE_GEMINI\n                    default:\n                        return false;\n                }\n                if (!post_process_steno_user(keycode, record, mode, chord, n_pressed_keys)) {\n                    return false;\n                }\n            } else { // is released\n                n_pressed_keys--;\n                if (!post_process_steno_user(keycode, record, mode, chord, n_pressed_keys)) {\n                    return false;\n                }\n                if (n_pressed_keys > 0) {\n                    // User hasn't released all keys yet,\n                    // so the chord cannot be sent\n                    return false;\n                }\n                n_pressed_keys = 0;\n                if (!send_steno_chord_user(mode, chord)) {\n                    steno_clear_chord();\n                    return false;\n                }\n                switch (mode) {\n#if defined(STENO_ENABLE_BOLT) && defined(VIRTSER_ENABLE)\n                    case STENO_MODE_BOLT:\n                        send_steno_chord_bolt();\n                        break;\n#endif // STENO_ENABLE_BOLT && VIRTSER_ENABLE\n#if defined(STENO_ENABLE_GEMINI) && defined(VIRTSER_ENABLE)\n                    case STENO_MODE_GEMINI:\n                        send_steno_chord_gemini();\n                        break;\n#endif // STENO_ENABLE_GEMINI && VIRTSER_ENABLE\n                    default:\n                        break;\n                }\n                steno_clear_chord();\n            }\n            break;\n    }\n    return false;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_steno.h",
    "content": "/* Copyright 2017 Joseph Wasson\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n#include \"steno_keycodes.h\"\n\n#define BOLT_STROKE_SIZE 4\n#define GEMINI_STROKE_SIZE 6\n\n#ifdef STENO_ENABLE_GEMINI\n#    define MAX_STROKE_SIZE GEMINI_STROKE_SIZE\n#else\n#    define MAX_STROKE_SIZE BOLT_STROKE_SIZE\n#endif\n\ntypedef enum {\n    STENO_MODE_GEMINI,\n    STENO_MODE_BOLT,\n} steno_mode_t;\n\nbool process_steno(uint16_t keycode, keyrecord_t *record);\n#ifdef STENO_ENABLE_ALL\nvoid steno_init(void);\nvoid steno_set_mode(steno_mode_t mode);\n#endif // STENO_ENABLE_ALL\n"
  },
  {
    "path": "quantum/process_keycode/process_tap_dance.c",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_tap_dance.h\"\n#include \"quantum.h\"\n#include \"action_layer.h\"\n#include \"action_tapping.h\"\n#include \"action_util.h\"\n#include \"timer.h\"\n#include \"wait.h\"\n#include \"keymap_introspection.h\"\n\nstatic uint16_t active_td;\nstatic uint16_t last_tap_time;\n\nvoid tap_dance_pair_on_each_tap(tap_dance_state_t *state, void *user_data) {\n    tap_dance_pair_t *pair = (tap_dance_pair_t *)user_data;\n\n    if (state->count == 2) {\n        register_code16(pair->kc2);\n        state->finished = true;\n    }\n}\n\nvoid tap_dance_pair_finished(tap_dance_state_t *state, void *user_data) {\n    tap_dance_pair_t *pair = (tap_dance_pair_t *)user_data;\n\n    register_code16(pair->kc1);\n}\n\nvoid tap_dance_pair_reset(tap_dance_state_t *state, void *user_data) {\n    tap_dance_pair_t *pair = (tap_dance_pair_t *)user_data;\n\n    if (state->count == 1) {\n        wait_ms(TAP_CODE_DELAY);\n        unregister_code16(pair->kc1);\n    } else if (state->count == 2) {\n        unregister_code16(pair->kc2);\n    }\n}\n\nvoid tap_dance_dual_role_on_each_tap(tap_dance_state_t *state, void *user_data) {\n    tap_dance_dual_role_t *pair = (tap_dance_dual_role_t *)user_data;\n\n    if (state->count == 2) {\n        layer_move(pair->layer);\n        state->finished = true;\n    }\n}\n\nvoid tap_dance_dual_role_finished(tap_dance_state_t *state, void *user_data) {\n    tap_dance_dual_role_t *pair = (tap_dance_dual_role_t *)user_data;\n\n    if (state->count == 1) {\n        register_code16(pair->kc);\n    } else if (state->count == 2) {\n        pair->layer_function(pair->layer);\n    }\n}\n\nvoid tap_dance_dual_role_reset(tap_dance_state_t *state, void *user_data) {\n    tap_dance_dual_role_t *pair = (tap_dance_dual_role_t *)user_data;\n\n    if (state->count == 1) {\n        wait_ms(TAP_CODE_DELAY);\n        unregister_code16(pair->kc);\n    }\n}\n\nstatic inline void _process_tap_dance_action_fn(tap_dance_state_t *state, void *user_data, tap_dance_user_fn_t fn) {\n    if (fn) {\n        fn(state, user_data);\n    }\n}\n\nstatic inline void process_tap_dance_action_on_each_tap(tap_dance_action_t *action) {\n    action->state.count++;\n    action->state.weak_mods = get_mods();\n    action->state.weak_mods |= get_weak_mods();\n#ifndef NO_ACTION_ONESHOT\n    action->state.oneshot_mods = get_oneshot_mods();\n#endif\n    _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_tap);\n}\n\nstatic inline void process_tap_dance_action_on_each_release(tap_dance_action_t *action) {\n    _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_each_release);\n}\n\nstatic inline void process_tap_dance_action_on_reset(tap_dance_action_t *action) {\n    _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_reset);\n    del_weak_mods(action->state.weak_mods);\n#ifndef NO_ACTION_ONESHOT\n    del_mods(action->state.oneshot_mods);\n#endif\n    send_keyboard_report();\n    action->state = (const tap_dance_state_t){0};\n}\n\nstatic inline void process_tap_dance_action_on_dance_finished(tap_dance_action_t *action) {\n    if (!action->state.finished) {\n        action->state.finished = true;\n        add_weak_mods(action->state.weak_mods);\n#ifndef NO_ACTION_ONESHOT\n        add_mods(action->state.oneshot_mods);\n#endif\n        send_keyboard_report();\n        _process_tap_dance_action_fn(&action->state, action->user_data, action->fn.on_dance_finished);\n    }\n    active_td = 0;\n    if (!action->state.pressed) {\n        // There will not be a key release event, so reset now.\n        process_tap_dance_action_on_reset(action);\n    }\n}\n\nbool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) {\n    tap_dance_action_t *action;\n\n    if (!record->event.pressed) return false;\n\n    if (!active_td || keycode == active_td) return false;\n\n    action                             = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td));\n    action->state.interrupted          = true;\n    action->state.interrupting_keycode = keycode;\n    process_tap_dance_action_on_dance_finished(action);\n\n    // Tap dance actions can leave some weak mods active (e.g., if the tap dance is mapped to a keycode with\n    // modifiers), but these weak mods should not affect the keypress which interrupted the tap dance.\n    clear_weak_mods();\n\n    // Signal that a tap dance has been finished due to being interrupted,\n    // therefore the keymap lookup for the currently processed event needs to\n    // be repeated with the current layer state that might have been updated by\n    // the finished tap dance.\n    return true;\n}\n\nbool process_tap_dance(uint16_t keycode, keyrecord_t *record) {\n    int                 td_index;\n    tap_dance_action_t *action;\n\n    switch (keycode) {\n        case QK_TAP_DANCE ... QK_TAP_DANCE_MAX:\n            td_index = QK_TAP_DANCE_GET_INDEX(keycode);\n            if (td_index >= tap_dance_count()) {\n                return false;\n            }\n            action = tap_dance_get(td_index);\n\n            action->state.pressed = record->event.pressed;\n            if (record->event.pressed) {\n                last_tap_time = timer_read();\n                process_tap_dance_action_on_each_tap(action);\n                active_td = action->state.finished ? 0 : keycode;\n            } else {\n                process_tap_dance_action_on_each_release(action);\n                if (action->state.finished) {\n                    process_tap_dance_action_on_reset(action);\n                    if (active_td == keycode) {\n                        active_td = 0;\n                    }\n                }\n            }\n\n            break;\n    }\n\n    return true;\n}\n\nvoid tap_dance_task(void) {\n    tap_dance_action_t *action;\n\n    if (!active_td || timer_elapsed(last_tap_time) <= GET_TAPPING_TERM(active_td, &(keyrecord_t){})) return;\n\n    action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(active_td));\n    if (!action->state.interrupted) {\n        process_tap_dance_action_on_dance_finished(action);\n    }\n}\n\nvoid reset_tap_dance(tap_dance_state_t *state) {\n    active_td = 0;\n    process_tap_dance_action_on_reset((tap_dance_action_t *)state);\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_tap_dance.h",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n#include \"quantum_keycodes.h\"\n\ntypedef struct {\n    uint16_t interrupting_keycode;\n    uint8_t  count;\n    uint8_t  weak_mods;\n#ifndef NO_ACTION_ONESHOT\n    uint8_t oneshot_mods;\n#endif\n    bool pressed : 1;\n    bool finished : 1;\n    bool interrupted : 1;\n} tap_dance_state_t;\n\ntypedef void (*tap_dance_user_fn_t)(tap_dance_state_t *state, void *user_data);\n\ntypedef struct tap_dance_action_t {\n    tap_dance_state_t state;\n    struct {\n        tap_dance_user_fn_t on_each_tap;\n        tap_dance_user_fn_t on_dance_finished;\n        tap_dance_user_fn_t on_reset;\n        tap_dance_user_fn_t on_each_release;\n    } fn;\n    void *user_data;\n} tap_dance_action_t;\n\ntypedef struct {\n    uint16_t kc1;\n    uint16_t kc2;\n} tap_dance_pair_t;\n\ntypedef struct {\n    uint16_t kc;\n    uint8_t  layer;\n    void (*layer_function)(uint8_t);\n} tap_dance_dual_role_t;\n\n#define ACTION_TAP_DANCE_DOUBLE(kc1, kc2) \\\n    { .fn = {tap_dance_pair_on_each_tap, tap_dance_pair_finished, tap_dance_pair_reset, NULL}, .user_data = (void *)&((tap_dance_pair_t){kc1, kc2}), }\n\n#define ACTION_TAP_DANCE_LAYER_MOVE(kc, layer) \\\n    { .fn = {tap_dance_dual_role_on_each_tap, tap_dance_dual_role_finished, tap_dance_dual_role_reset, NULL}, .user_data = (void *)&((tap_dance_dual_role_t){kc, layer, layer_move}), }\n\n#define ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer) \\\n    { .fn = {NULL, tap_dance_dual_role_finished, tap_dance_dual_role_reset, NULL}, .user_data = (void *)&((tap_dance_dual_role_t){kc, layer, layer_invert}), }\n\n#define ACTION_TAP_DANCE_FN(user_fn) \\\n    { .fn = {NULL, user_fn, NULL, NULL}, .user_data = NULL, }\n\n#define ACTION_TAP_DANCE_FN_ADVANCED(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset) \\\n    { .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset, NULL}, .user_data = NULL, }\n\n#define ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(user_fn_on_each_tap, user_fn_on_each_release, user_fn_on_dance_finished, user_fn_on_dance_reset) \\\n    { .fn = {user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset, user_fn_on_each_release}, .user_data = NULL, }\n\n#define TD_INDEX(code) QK_TAP_DANCE_GET_INDEX(code)\n#define TAP_DANCE_KEYCODE(state) TD(((tap_dance_action_t *)state) - tap_dance_actions)\n\nvoid reset_tap_dance(tap_dance_state_t *state);\n\n/* To be used internally */\n\nbool preprocess_tap_dance(uint16_t keycode, keyrecord_t *record);\nbool process_tap_dance(uint16_t keycode, keyrecord_t *record);\nvoid tap_dance_task(void);\n\nvoid tap_dance_pair_on_each_tap(tap_dance_state_t *state, void *user_data);\nvoid tap_dance_pair_finished(tap_dance_state_t *state, void *user_data);\nvoid tap_dance_pair_reset(tap_dance_state_t *state, void *user_data);\n\nvoid tap_dance_dual_role_on_each_tap(tap_dance_state_t *state, void *user_data);\nvoid tap_dance_dual_role_finished(tap_dance_state_t *state, void *user_data);\nvoid tap_dance_dual_role_reset(tap_dance_state_t *state, void *user_data);\n"
  },
  {
    "path": "quantum/process_keycode/process_tri_layer.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"process_tri_layer.h\"\n#include \"tri_layer.h\"\n#include \"action_layer.h\"\n\nbool process_tri_layer(uint16_t keycode, keyrecord_t *record) {\n    switch (keycode) {\n        case QK_TRI_LAYER_LOWER:\n            if (record->event.pressed) {\n                layer_on(get_tri_layer_lower_layer());\n                update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer());\n            } else {\n                layer_off(get_tri_layer_lower_layer());\n                update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer());\n            }\n            return false;\n        case QK_TRI_LAYER_UPPER:\n            if (record->event.pressed) {\n                layer_on(get_tri_layer_upper_layer());\n                update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer());\n            } else {\n                layer_off(get_tri_layer_upper_layer());\n                update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer());\n            }\n            return false;\n    }\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_tri_layer.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\n/**\n * @brief Handles tri layer behavior\n *\n * @param keycode the keycode\n * @param record the key record structure\n * @return true continue handling keycodes\n * @return false stop handling keycodes\n */\nbool process_tri_layer(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_ucis.c",
    "content": "/* Copyright 2017 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_ucis.h\"\n#include \"ucis.h\"\n#include \"keycodes.h\"\n\nbool process_ucis(uint16_t keycode, keyrecord_t *record) {\n    if (ucis_active() && record->event.pressed) {\n        bool special = keycode == KC_SPACE || keycode == KC_ENTER || keycode == KC_ESCAPE || keycode == KC_BACKSPACE;\n        if (ucis_count() >= UCIS_MAX_INPUT_LENGTH && !special) {\n            return false;\n        }\n\n        if (!ucis_add(keycode)) {\n            switch (keycode) {\n                case KC_BACKSPACE:\n                    return ucis_remove_last();\n                case KC_ESCAPE:\n                    ucis_cancel();\n                    return false;\n                case KC_SPACE:\n                case KC_ENTER:\n                    ucis_finish();\n                    return false;\n            }\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_ucis.h",
    "content": "/* Copyright 2017 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"action.h\"\n\nbool process_ucis(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_underglow.c",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"process_underglow.h\"\n#if defined(RGBLIGHT_ENABLE)\n#    include \"rgblight.h\"\n#endif\n#include \"action_util.h\"\n#include \"keycodes.h\"\n#include \"modifiers.h\"\n\n// TODO: Remove this\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n#    include \"rgb_matrix.h\"\n#endif\n\nbool process_underglow(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n#if defined(RGBLIGHT_ENABLE) || (defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES))\n        const uint8_t shifted = get_mods() & MOD_MASK_SHIFT;\n#endif\n\n        switch (keycode) {\n            case QK_UNDERGLOW_TOGGLE:\n#if defined(RGBLIGHT_ENABLE)\n                rgblight_toggle();\n#endif\n\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n                rgb_matrix_toggle();\n#endif\n                return false;\n            case QK_UNDERGLOW_MODE_NEXT:\n#if defined(RGBLIGHT_ENABLE)\n                if (shifted) {\n                    rgblight_step_reverse();\n                } else {\n                    rgblight_step();\n                }\n#endif\n\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n                if (shifted) {\n                    rgb_matrix_step_reverse();\n                } else {\n                    rgb_matrix_step();\n                }\n#endif\n                return false;\n            case QK_UNDERGLOW_MODE_PREVIOUS:\n#if defined(RGBLIGHT_ENABLE)\n                if (shifted) {\n                    rgblight_step();\n                } else {\n                    rgblight_step_reverse();\n                }\n#endif\n\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n                if (shifted) {\n                    rgb_matrix_step();\n                } else {\n                    rgb_matrix_step_reverse();\n                }\n#endif\n                return false;\n            case QK_UNDERGLOW_HUE_UP:\n#if defined(RGBLIGHT_ENABLE)\n                if (shifted) {\n                    rgblight_decrease_hue();\n                } else {\n                    rgblight_increase_hue();\n                }\n#endif\n\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n                if (shifted) {\n                    rgb_matrix_decrease_hue();\n                } else {\n                    rgb_matrix_increase_hue();\n                }\n#endif\n                return false;\n            case QK_UNDERGLOW_HUE_DOWN:\n#if defined(RGBLIGHT_ENABLE)\n                if (shifted) {\n                    rgblight_increase_hue();\n                } else {\n                    rgblight_decrease_hue();\n                }\n#endif\n\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n                if (shifted) {\n                    rgb_matrix_increase_hue();\n                } else {\n                    rgb_matrix_decrease_hue();\n                }\n#endif\n                return false;\n            case QK_UNDERGLOW_SATURATION_UP:\n#if defined(RGBLIGHT_ENABLE)\n                if (shifted) {\n                    rgblight_decrease_sat();\n                } else {\n                    rgblight_increase_sat();\n                }\n#endif\n\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n                if (shifted) {\n                    rgb_matrix_decrease_sat();\n                } else {\n                    rgb_matrix_increase_sat();\n                }\n#endif\n                return false;\n            case QK_UNDERGLOW_SATURATION_DOWN:\n#if defined(RGBLIGHT_ENABLE)\n                if (shifted) {\n                    rgblight_increase_sat();\n                } else {\n                    rgblight_decrease_sat();\n                }\n#endif\n\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n                if (shifted) {\n                    rgb_matrix_increase_sat();\n                } else {\n                    rgb_matrix_decrease_sat();\n                }\n#endif\n                return false;\n            case QK_UNDERGLOW_VALUE_UP:\n#if defined(RGBLIGHT_ENABLE)\n                if (shifted) {\n                    rgblight_decrease_val();\n                } else {\n                    rgblight_increase_val();\n                }\n#endif\n\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n                if (shifted) {\n                    rgb_matrix_decrease_val();\n                } else {\n                    rgb_matrix_increase_val();\n                }\n#endif\n                return false;\n            case QK_UNDERGLOW_VALUE_DOWN:\n#if defined(RGBLIGHT_ENABLE)\n                if (shifted) {\n                    rgblight_increase_val();\n                } else {\n                    rgblight_decrease_val();\n                }\n#endif\n\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n                if (shifted) {\n                    rgb_matrix_increase_val();\n                } else {\n                    rgb_matrix_decrease_val();\n                }\n#endif\n                return false;\n            case QK_UNDERGLOW_SPEED_UP:\n#if defined(RGBLIGHT_ENABLE)\n                if (shifted) {\n                    rgblight_decrease_speed();\n                } else {\n                    rgblight_increase_speed();\n                }\n#endif\n\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n                if (shifted) {\n                    rgb_matrix_decrease_speed();\n                } else {\n                    rgb_matrix_increase_speed();\n                }\n#endif\n                return false;\n            case QK_UNDERGLOW_SPEED_DOWN:\n#if defined(RGBLIGHT_ENABLE)\n                if (shifted) {\n                    rgblight_increase_speed();\n                } else {\n                    rgblight_decrease_speed();\n                }\n#endif\n\n#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_SHARED_KEYCODES)\n                if (shifted) {\n                    rgb_matrix_increase_speed();\n                } else {\n                    rgb_matrix_decrease_speed();\n                }\n#endif\n                return false;\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_underglow.h",
    "content": "// Copyright 2024 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n\nbool process_underglow(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_unicode.c",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_unicode.h\"\n#include \"unicode.h\"\n#include \"keycodes.h\"\n#include \"quantum_keycodes.h\"\n\nbool process_unicode(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n        if (keycode >= QK_UNICODE && keycode <= QK_UNICODE_MAX) {\n            register_unicode(QK_UNICODE_GET_CODE_POINT(keycode));\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_unicode.h",
    "content": "/* Copyright 2016 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"action.h\"\n\nbool process_unicode(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_unicode_common.c",
    "content": "/* Copyright 2017 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_unicode_common.h\"\n#include \"unicode.h\"\n#include \"action_util.h\"\n#include \"keycodes.h\"\n#include \"modifiers.h\"\n\n#if defined(UNICODE_ENABLE)\n#    include \"process_unicode.h\"\n#elif defined(UNICODEMAP_ENABLE)\n#    include \"process_unicodemap.h\"\n#elif defined(UCIS_ENABLE)\n#    include \"process_ucis.h\"\n#endif\n\nbool process_unicode_common(uint16_t keycode, keyrecord_t *record) {\n    if (record->event.pressed) {\n        bool shifted = get_mods() & MOD_MASK_SHIFT;\n        switch (keycode) {\n            case QK_UNICODE_MODE_NEXT:\n                if (shifted) {\n                    unicode_input_mode_step_reverse();\n                } else {\n                    unicode_input_mode_step();\n                }\n                break;\n            case QK_UNICODE_MODE_PREVIOUS:\n                if (shifted) {\n                    unicode_input_mode_step();\n                } else {\n                    unicode_input_mode_step_reverse();\n                }\n                break;\n            case QK_UNICODE_MODE_MACOS:\n                set_unicode_input_mode(UNICODE_MODE_MACOS);\n                break;\n            case QK_UNICODE_MODE_LINUX:\n                set_unicode_input_mode(UNICODE_MODE_LINUX);\n                break;\n            case QK_UNICODE_MODE_WINDOWS:\n                set_unicode_input_mode(UNICODE_MODE_WINDOWS);\n                break;\n            case QK_UNICODE_MODE_BSD:\n                set_unicode_input_mode(UNICODE_MODE_BSD);\n                break;\n            case QK_UNICODE_MODE_WINCOMPOSE:\n                set_unicode_input_mode(UNICODE_MODE_WINCOMPOSE);\n                break;\n            case QK_UNICODE_MODE_EMACS:\n                set_unicode_input_mode(UNICODE_MODE_EMACS);\n                break;\n        }\n    }\n\n#if defined(UNICODE_ENABLE)\n    return process_unicode(keycode, record);\n#elif defined(UNICODEMAP_ENABLE)\n    return process_unicodemap(keycode, record);\n#elif defined(UCIS_ENABLE)\n    return process_ucis(keycode, record);\n#else\n    return true;\n#endif\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_unicode_common.h",
    "content": "/* Copyright 2017 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"action.h\"\n\nbool process_unicode_common(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/process_keycode/process_unicodemap.c",
    "content": "/* Copyright 2017 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"process_unicodemap.h\"\n#include \"unicodemap.h\"\n#include \"keycodes.h\"\n\nbool process_unicodemap(uint16_t keycode, keyrecord_t *record) {\n    if (keycode >= QK_UNICODEMAP && keycode <= QK_UNICODEMAP_PAIR_MAX && record->event.pressed) {\n        register_unicodemap(unicodemap_index(keycode));\n    }\n    return true;\n}\n"
  },
  {
    "path": "quantum/process_keycode/process_unicodemap.h",
    "content": "/* Copyright 2017 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"action.h\"\n\nbool process_unicodemap(uint16_t keycode, keyrecord_t *record);\n"
  },
  {
    "path": "quantum/programmable_button.c",
    "content": "/*\nCopyright 2021 Thomas Weißschuh <thomas@t-8ch.de>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include \"programmable_button.h\"\n#include \"host.h\"\n\n#define REPORT_BIT(index) (((uint32_t)1) << (index - 1))\n\nstatic uint32_t programmable_button_report = 0;\n\nvoid programmable_button_clear(void) {\n    programmable_button_report = 0;\n    programmable_button_flush();\n}\n\nvoid programmable_button_add(uint8_t index) {\n    programmable_button_report |= REPORT_BIT(index);\n}\n\nvoid programmable_button_remove(uint8_t index) {\n    programmable_button_report &= ~REPORT_BIT(index);\n}\n\nvoid programmable_button_register(uint8_t index) {\n    programmable_button_add(index);\n    programmable_button_flush();\n}\n\nvoid programmable_button_unregister(uint8_t index) {\n    programmable_button_remove(index);\n    programmable_button_flush();\n}\n\nbool programmable_button_is_on(uint8_t index) {\n    return !!(programmable_button_report & REPORT_BIT(index));\n}\n\nvoid programmable_button_flush(void) {\n    host_programmable_button_send(programmable_button_report);\n}\n\nuint32_t programmable_button_get_report(void) {\n    return programmable_button_report;\n}\n\nvoid programmable_button_set_report(uint32_t report) {\n    programmable_button_report = report;\n}\n"
  },
  {
    "path": "quantum/programmable_button.h",
    "content": "/*\nCopyright 2021 Thomas Weißschuh <thomas@t-8ch.de>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n/**\n * \\file\n *\n * \\defgroup programmable_button HID Programmable Buttons\n * \\{\n */\n\n/**\n * \\brief Clear the programmable button report.\n */\nvoid programmable_button_clear(void);\n\n/**\n * \\brief Set the state of a button.\n *\n * \\param index The index of the button to press, from 0 to 31.\n */\nvoid programmable_button_add(uint8_t index);\n\n/**\n * \\brief Reset the state of a button.\n *\n * \\param index The index of the button to release, from 0 to 31.\n */\nvoid programmable_button_remove(uint8_t index);\n\n/**\n * \\brief Set the state of a button, and flush the report.\n *\n * \\param index The index of the button to press, from 0 to 31.\n */\nvoid programmable_button_register(uint8_t index);\n\n/**\n * \\brief Reset the state of a button, and flush the report.\n *\n * \\param index The index of the button to release, from 0 to 31.\n */\nvoid programmable_button_unregister(uint8_t index);\n\n/**\n * \\brief Get the state of a button.\n *\n * \\param index The index of the button to check, from 0 to 31.\n *\n * \\return `true` if the button is pressed.\n */\nbool programmable_button_is_on(uint8_t index);\n\n/**\n * \\brief Send the programmable button report to the host.\n */\nvoid programmable_button_flush(void);\n\n/**\n * \\brief Get the programmable button report.\n *\n * \\return The bitmask of programmable button states.\n */\nuint32_t programmable_button_get_report(void);\n\n/**\n * \\brief Set the programmable button report.\n *\n * \\param report A bitmask of programmable button states.\n */\nvoid programmable_button_set_report(uint32_t report);\n\n/** \\} */\n"
  },
  {
    "path": "quantum/quantum.c",
    "content": "/* Copyright 2016-2017 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"quantum.h\"\n\n#ifdef BACKLIGHT_ENABLE\n#    include \"process_backlight.h\"\n#endif\n\n#ifdef CONNECTION_ENABLE\n#    include \"process_connection.h\"\n#endif\n\n#ifdef GRAVE_ESC_ENABLE\n#    include \"process_grave_esc.h\"\n#endif\n\n#ifdef HAPTIC_ENABLE\n#    include \"process_haptic.h\"\n#endif\n\n#ifdef JOYSTICK_ENABLE\n#    include \"process_joystick.h\"\n#endif\n\n#ifdef LEADER_ENABLE\n#    include \"process_leader.h\"\n#endif\n\n#ifdef LED_MATRIX_ENABLE\n#    include \"process_led_matrix.h\"\n#endif\n\n#ifdef MAGIC_ENABLE\n#    include \"process_magic.h\"\n#endif\n\n#ifdef MIDI_ENABLE\n#    include \"process_midi.h\"\n#endif\n\n#if !defined(NO_ACTION_LAYER)\n#    include \"process_default_layer.h\"\n#endif\n\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\n#    include \"process_programmable_button.h\"\n#endif\n\n#if defined(RGB_MATRIX_ENABLE)\n#    include \"process_rgb_matrix.h\"\n#endif\n\n#if defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE)\n#    include \"process_underglow.h\"\n#endif\n\n#ifdef SECURE_ENABLE\n#    include \"process_secure.h\"\n#endif\n\n#ifdef TRI_LAYER_ENABLE\n#    include \"process_tri_layer.h\"\n#endif\n\n#ifdef UNICODE_COMMON_ENABLE\n#    include \"process_unicode_common.h\"\n#endif\n\n#ifdef LAYER_LOCK_ENABLE\n#    include \"process_layer_lock.h\"\n#endif\n\n#ifdef AUDIO_ENABLE\n#    ifndef GOODBYE_SONG\n#        define GOODBYE_SONG SONG(GOODBYE_SOUND)\n#    endif\nfloat goodbye_song[][2] = GOODBYE_SONG;\n#    ifdef DEFAULT_LAYER_SONGS\nfloat default_layer_songs[][16][2] = DEFAULT_LAYER_SONGS;\n#    endif\n#endif\n\nuint8_t extract_mod_bits(uint16_t code) {\n    switch (code) {\n        case QK_MODS ... QK_MODS_MAX:\n            break;\n        default:\n            return 0;\n    }\n\n    uint8_t mods_to_send = 0;\n\n    if (code & QK_RMODS_MIN) { // Right mod flag is set\n        if (code & QK_LCTL) mods_to_send |= MOD_BIT(KC_RIGHT_CTRL);\n        if (code & QK_LSFT) mods_to_send |= MOD_BIT(KC_RIGHT_SHIFT);\n        if (code & QK_LALT) mods_to_send |= MOD_BIT(KC_RIGHT_ALT);\n        if (code & QK_LGUI) mods_to_send |= MOD_BIT(KC_RIGHT_GUI);\n    } else {\n        if (code & QK_LCTL) mods_to_send |= MOD_BIT(KC_LEFT_CTRL);\n        if (code & QK_LSFT) mods_to_send |= MOD_BIT(KC_LEFT_SHIFT);\n        if (code & QK_LALT) mods_to_send |= MOD_BIT(KC_LEFT_ALT);\n        if (code & QK_LGUI) mods_to_send |= MOD_BIT(KC_LEFT_GUI);\n    }\n\n    return mods_to_send;\n}\n\nvoid do_code16(uint16_t code, void (*f)(uint8_t)) {\n    f(extract_mod_bits(code));\n}\n\n__attribute__((weak)) void register_code16(uint16_t code) {\n    if (IS_MODIFIER_KEYCODE(code) || code == KC_NO) {\n        do_code16(code, register_mods);\n    } else {\n        do_code16(code, register_weak_mods);\n    }\n    register_code(code);\n}\n\n__attribute__((weak)) void unregister_code16(uint16_t code) {\n    unregister_code(code);\n    if (IS_MODIFIER_KEYCODE(code) || code == KC_NO) {\n        do_code16(code, unregister_mods);\n    } else {\n        do_code16(code, unregister_weak_mods);\n    }\n}\n\n/** \\brief Tap a keycode with a delay.\n *\n * \\param code The modded keycode to tap.\n * \\param delay The amount of time in milliseconds to leave the keycode registered, before unregistering it.\n */\n__attribute__((weak)) void tap_code16_delay(uint16_t code, uint16_t delay) {\n    register_code16(code);\n    for (uint16_t i = delay; i > 0; i--) {\n        wait_ms(1);\n    }\n    unregister_code16(code);\n}\n\n/** \\brief Tap a keycode with the default delay.\n *\n * \\param code The modded keycode to tap. If `code` is `KC_CAPS_LOCK`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined.\n */\n__attribute__((weak)) void tap_code16(uint16_t code) {\n    tap_code16_delay(code, code == KC_CAPS_LOCK ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY);\n}\n\n__attribute__((weak)) bool pre_process_record_modules(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\n__attribute__((weak)) bool pre_process_record_kb(uint16_t keycode, keyrecord_t *record) {\n    return pre_process_record_user(keycode, record);\n}\n\n__attribute__((weak)) bool pre_process_record_user(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\n__attribute__((weak)) bool process_action_kb(keyrecord_t *record) {\n    return true;\n}\n\n__attribute__((weak)) bool process_record_modules(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\n__attribute__((weak)) bool process_record_kb(uint16_t keycode, keyrecord_t *record) {\n    return process_record_user(keycode, record);\n}\n\n__attribute__((weak)) bool process_record_user(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\n__attribute__((weak)) void post_process_record_modules(uint16_t keycode, keyrecord_t *record) {}\n\n__attribute__((weak)) void post_process_record_kb(uint16_t keycode, keyrecord_t *record) {\n    post_process_record_user(keycode, record);\n}\n\n__attribute__((weak)) void post_process_record_user(uint16_t keycode, keyrecord_t *record) {}\n\n__attribute__((weak)) bool shutdown_modules(bool jump_to_bootloader) {\n    return true;\n}\n\n__attribute__((weak)) void suspend_power_down_modules(void) {}\n\n__attribute__((weak)) void suspend_wakeup_init_modules(void) {}\n\nvoid shutdown_quantum(bool jump_to_bootloader) {\n    clear_keyboard();\n#if defined(MIDI_ENABLE) && defined(MIDI_BASIC)\n    process_midi_all_notes_off();\n#endif\n#ifdef AUDIO_ENABLE\n#    ifndef NO_MUSIC_MODE\n    music_all_notes_off();\n#    endif\n    uint16_t timer_start = timer_read();\n    PLAY_SONG(goodbye_song);\n    shutdown_modules(jump_to_bootloader);\n    shutdown_kb(jump_to_bootloader);\n    while (timer_elapsed(timer_start) < 250)\n        wait_ms(1);\n    stop_all_notes();\n#else\n    shutdown_modules(jump_to_bootloader);\n    shutdown_kb(jump_to_bootloader);\n    wait_ms(250);\n#endif\n#ifdef HAPTIC_ENABLE\n    haptic_shutdown();\n#endif\n}\n\nvoid reset_keyboard(void) {\n    shutdown_quantum(true);\n    bootloader_jump();\n}\n\nvoid soft_reset_keyboard(void) {\n    shutdown_quantum(false);\n    mcu_reset();\n}\n\n/* Convert record into usable keycode via the contained event. */\nuint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache) {\n#if defined(COMBO_ENABLE) || defined(REPEAT_KEY_ENABLE)\n    if (record->keycode) {\n        return record->keycode;\n    }\n#endif\n    return get_event_keycode(record->event, update_layer_cache);\n}\n\n/* Convert event into usable keycode. Checks the layer cache to ensure that it\n * retains the correct keycode after a layer change, if the key is still pressed.\n * \"update_layer_cache\" is to ensure that it only updates the layer cache when\n * appropriate, otherwise, it will update it and cause layer tap (and other keys)\n * from triggering properly.\n */\nuint16_t get_event_keycode(keyevent_t event, bool update_layer_cache) {\n#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)\n    /* TODO: Use store_or_get_action() or a similar function. */\n    if (!disable_action_cache) {\n        uint8_t layer;\n\n        if (event.pressed && update_layer_cache) {\n            layer = layer_switch_get_layer(event.key);\n            update_source_layers_cache(event.key, layer);\n        } else {\n            layer = read_source_layers_cache(event.key);\n        }\n        return keymap_key_to_keycode(layer, event.key);\n    } else\n#endif\n        return keymap_key_to_keycode(layer_switch_get_layer(event.key), event.key);\n}\n\n/* Get keycode, and then process pre tapping functionality */\nbool pre_process_record_quantum(keyrecord_t *record) {\n    return pre_process_record_modules(get_record_keycode(record, true), record) && pre_process_record_kb(get_record_keycode(record, true), record) &&\n#ifdef COMBO_ENABLE\n           process_combo(get_record_keycode(record, true), record) &&\n#endif\n           true;\n}\n\n/* Get keycode, and then call keyboard function */\nvoid post_process_record_quantum(keyrecord_t *record) {\n    uint16_t keycode = get_record_keycode(record, false);\n    post_process_record_modules(keycode, record);\n    post_process_record_kb(keycode, record);\n}\n\n/* Core keycode function, hands off handling to other functions,\n    then processes internal quantum keycodes, and then processes\n    ACTIONs.                                                      */\nbool process_record_quantum(keyrecord_t *record) {\n    uint16_t keycode = get_record_keycode(record, true);\n\n    // This is how you use actions here\n    // if (keycode == QK_LEADER) {\n    //   action_t action;\n    //   action.code = ACTION_DEFAULT_LAYER_SET(0);\n    //   process_action(record, action);\n    //   return false;\n    // }\n\n#if defined(SECURE_ENABLE)\n    if (!preprocess_secure(keycode, record)) {\n        return false;\n    }\n#endif\n\n#ifdef TAP_DANCE_ENABLE\n    if (preprocess_tap_dance(keycode, record)) {\n        // The tap dance might have updated the layer state, therefore the\n        // result of the keycode lookup might change.\n        keycode = get_record_keycode(record, true);\n    }\n#endif\n\n#ifdef RGBLIGHT_ENABLE\n    if (record->event.pressed) {\n        preprocess_rgblight();\n    }\n#endif\n\n#ifdef WPM_ENABLE\n    if (record->event.pressed) {\n        update_wpm(keycode);\n    }\n#endif\n\n    if (!(\n#if defined(KEY_LOCK_ENABLE)\n            // Must run first to be able to mask key_up events.\n            process_key_lock(&keycode, record) &&\n#endif\n#if defined(DYNAMIC_MACRO_ENABLE) && !defined(DYNAMIC_MACRO_USER_CALL)\n            // Must run asap to ensure all keypresses are recorded.\n            process_dynamic_macro(keycode, record) &&\n#endif\n#ifdef REPEAT_KEY_ENABLE\n            process_last_key(keycode, record) && process_repeat_key(keycode, record) &&\n#endif\n#if defined(AUDIO_ENABLE) && defined(AUDIO_CLICKY)\n            process_clicky(keycode, record) &&\n#endif\n#ifdef HAPTIC_ENABLE\n            process_haptic(keycode, record) &&\n#endif\n#if defined(POINTING_DEVICE_ENABLE) && defined(POINTING_DEVICE_AUTO_MOUSE_ENABLE)\n            process_auto_mouse(keycode, record) &&\n#endif\n            process_record_modules(keycode, record) && // modules must run before kb\n            process_record_kb(keycode, record) &&\n#if defined(VIA_ENABLE)\n            process_record_via(keycode, record) &&\n#endif\n#if defined(SECURE_ENABLE)\n            process_secure(keycode, record) &&\n#endif\n#if defined(SEQUENCER_ENABLE)\n            process_sequencer(keycode, record) &&\n#endif\n#if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED)\n            process_midi(keycode, record) &&\n#endif\n#ifdef AUDIO_ENABLE\n            process_audio(keycode, record) &&\n#endif\n#if defined(BACKLIGHT_ENABLE)\n            process_backlight(keycode, record) &&\n#endif\n#if defined(LED_MATRIX_ENABLE)\n            process_led_matrix(keycode, record) &&\n#endif\n#ifdef STENO_ENABLE\n            process_steno(keycode, record) &&\n#endif\n#if (defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))) && !defined(NO_MUSIC_MODE)\n            process_music(keycode, record) &&\n#endif\n#ifdef CAPS_WORD_ENABLE\n            process_caps_word(keycode, record) &&\n#endif\n#ifdef KEY_OVERRIDE_ENABLE\n            process_key_override(keycode, record) &&\n#endif\n#ifdef TAP_DANCE_ENABLE\n            process_tap_dance(keycode, record) &&\n#endif\n#if defined(UNICODE_COMMON_ENABLE)\n            process_unicode_common(keycode, record) &&\n#endif\n#ifdef LEADER_ENABLE\n            process_leader(keycode, record) &&\n#endif\n#ifdef AUTO_SHIFT_ENABLE\n            process_auto_shift(keycode, record) &&\n#endif\n#ifdef DYNAMIC_TAPPING_TERM_ENABLE\n            process_dynamic_tapping_term(keycode, record) &&\n#endif\n#ifdef SPACE_CADET_ENABLE\n            process_space_cadet(keycode, record) &&\n#endif\n#ifdef MAGIC_ENABLE\n            process_magic(keycode, record) &&\n#endif\n#ifdef GRAVE_ESC_ENABLE\n            process_grave_esc(keycode, record) &&\n#endif\n#if defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE)\n            process_underglow(keycode, record) &&\n#endif\n#if defined(RGB_MATRIX_ENABLE)\n            process_rgb_matrix(keycode, record) &&\n#endif\n#ifdef JOYSTICK_ENABLE\n            process_joystick(keycode, record) &&\n#endif\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\n            process_programmable_button(keycode, record) &&\n#endif\n#ifdef AUTOCORRECT_ENABLE\n            process_autocorrect(keycode, record) &&\n#endif\n#ifdef TRI_LAYER_ENABLE\n            process_tri_layer(keycode, record) &&\n#endif\n\n#if !defined(NO_ACTION_LAYER)\n            process_default_layer(keycode, record) &&\n#endif\n#ifdef LAYER_LOCK_ENABLE\n            process_layer_lock(keycode, record) &&\n#endif\n#ifdef CONNECTION_ENABLE\n            process_connection(keycode, record) &&\n#endif\n            true)) {\n        return false;\n    }\n\n    if (record->event.pressed) {\n        switch (keycode) {\n#ifndef NO_RESET\n            case QK_BOOTLOADER:\n                reset_keyboard();\n                return false;\n            case QK_REBOOT:\n                soft_reset_keyboard();\n                return false;\n#endif\n#ifndef NO_DEBUG\n            case QK_DEBUG_TOGGLE:\n                debug_enable ^= 1;\n                if (debug_enable) {\n                    print(\"DEBUG: enabled.\\n\");\n                } else {\n                    print(\"DEBUG: disabled.\\n\");\n                }\n#endif\n                return false;\n            case QK_CLEAR_EEPROM:\n#ifdef NO_RESET\n                eeconfig_init();\n#else\n                eeconfig_disable();\n                soft_reset_keyboard();\n#endif\n                return false;\n#ifdef VELOCIKEY_ENABLE\n            case QK_VELOCIKEY_TOGGLE:\n                velocikey_toggle();\n                return false;\n#endif\n#ifndef NO_ACTION_ONESHOT\n            case QK_ONE_SHOT_TOGGLE:\n                oneshot_toggle();\n                break;\n            case QK_ONE_SHOT_ON:\n                oneshot_enable();\n                break;\n            case QK_ONE_SHOT_OFF:\n                oneshot_disable();\n                break;\n#endif\n#ifdef ENABLE_COMPILE_KEYCODE\n            case QK_MAKE: // Compiles the firmware, and adds the flash command based on keyboard bootloader\n            {\n#    ifdef NO_ACTION_ONESHOT\n                const uint8_t temp_mod = mod_config(get_mods());\n#    else\n                const uint8_t temp_mod = mod_config(get_mods() | get_oneshot_mods());\n                clear_oneshot_mods();\n#    endif\n                clear_mods();\n\n                SEND_STRING_DELAY(\"qmk\", TAP_CODE_DELAY);\n                if (temp_mod & MOD_MASK_SHIFT) { // if shift is held, flash rather than compile\n                    SEND_STRING_DELAY(\" flash \", TAP_CODE_DELAY);\n                } else {\n                    SEND_STRING_DELAY(\" compile \", TAP_CODE_DELAY);\n                }\n#    if defined(CONVERTER_ENABLED)\n                SEND_STRING_DELAY(\"-kb \" QMK_KEYBOARD \" -km \" QMK_KEYMAP \" -e CONVERT_TO=\" CONVERTER_TARGET SS_TAP(X_ENTER), TAP_CODE_DELAY);\n#    else\n                SEND_STRING_DELAY(\"-kb \" QMK_KEYBOARD \" -km \" QMK_KEYMAP SS_TAP(X_ENTER), TAP_CODE_DELAY);\n#    endif\n                if (temp_mod & MOD_MASK_SHIFT && temp_mod & MOD_MASK_CTRL) {\n                    reset_keyboard();\n                }\n            }\n#endif\n        }\n    }\n\n    return process_action_kb(record);\n}\n\nvoid set_single_default_layer(uint8_t default_layer) {\n#if defined(AUDIO_ENABLE) && defined(DEFAULT_LAYER_SONGS)\n    PLAY_SONG(default_layer_songs[default_layer]);\n#endif\n    default_layer_set((layer_state_t)1 << default_layer);\n}\n\nvoid set_single_persistent_default_layer(uint8_t default_layer) {\n    eeconfig_update_default_layer((layer_state_t)1 << default_layer);\n    set_single_default_layer(default_layer);\n}\n\n//------------------------------------------------------------------------------\n// Override these functions in your keymap file to play different tunes on\n// different events such as startup and bootloader jump\n\n__attribute__((weak)) bool shutdown_user(bool jump_to_bootloader) {\n    return true;\n}\n\n__attribute__((weak)) bool shutdown_kb(bool jump_to_bootloader) {\n    if (!shutdown_user(jump_to_bootloader)) {\n        return false;\n    }\n    return true;\n}\n\nvoid suspend_power_down_quantum(void) {\n    suspend_power_down_modules();\n    suspend_power_down_kb();\n#ifndef NO_SUSPEND_POWER_DOWN\n// Turn off backlight\n#    ifdef BACKLIGHT_ENABLE\n    backlight_level_noeeprom(0);\n#    endif\n\n#    ifdef LED_MATRIX_ENABLE\n    led_matrix_task();\n#    endif\n#    ifdef RGB_MATRIX_ENABLE\n    rgb_matrix_task();\n#    endif\n\n    // Turn off LED indicators\n    led_suspend();\n\n// Turn off audio\n#    ifdef AUDIO_ENABLE\n    stop_all_notes();\n#    endif\n\n// Turn off underglow\n#    if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)\n    rgblight_suspend();\n#    endif\n\n#    if defined(LED_MATRIX_ENABLE)\n    led_matrix_set_suspend_state(true);\n#    endif\n#    if defined(RGB_MATRIX_ENABLE)\n    rgb_matrix_set_suspend_state(true);\n#    endif\n\n#    ifdef OLED_ENABLE\n    oled_off();\n#    endif\n#    ifdef ST7565_ENABLE\n    st7565_off();\n#    endif\n#    if defined(POINTING_DEVICE_ENABLE)\n    // run to ensure scanning occurs while suspended\n    pointing_device_task();\n#    endif\n#endif\n}\n\n__attribute__((weak)) void suspend_wakeup_init_quantum(void) {\n// Turn on backlight\n#ifdef BACKLIGHT_ENABLE\n    backlight_init();\n#endif\n\n    // Restore LED indicators\n    led_wakeup();\n\n// Wake up underglow\n#if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)\n    rgblight_wakeup();\n#endif\n\n#if defined(LED_MATRIX_ENABLE)\n    led_matrix_set_suspend_state(false);\n#endif\n#if defined(RGB_MATRIX_ENABLE)\n    rgb_matrix_set_suspend_state(false);\n#endif\n    suspend_wakeup_init_modules();\n    suspend_wakeup_init_kb();\n}\n\n/** \\brief converts unsigned integers into char arrays\n *\n * Takes an unsigned integer and converts that value into an equivalent char array\n * A padding character may be specified, ' ' for leading spaces, '0' for leading zeros.\n */\n\nconst char *get_numeric_str(char *buf, size_t buf_len, uint32_t curr_num, char curr_pad) {\n    buf[buf_len - 1] = '\\0';\n    for (size_t i = 0; i < buf_len - 1; ++i) {\n        char c               = '0' + curr_num % 10;\n        buf[buf_len - 2 - i] = (c == '0' && i == 0) ? '0' : (curr_num > 0 ? c : curr_pad);\n        curr_num /= 10;\n    }\n    return buf;\n}\n\n/** \\brief converts uint8_t into char array\n *\n * Takes an uint8_t, and uses an internal static buffer to render that value into a char array\n * A padding character may be specified, ' ' for leading spaces, '0' for leading zeros.\n *\n * NOTE: Subsequent invocations will reuse the same static buffer and overwrite the previous\n *       contents. Use the result immediately, instead of caching it.\n */\nconst char *get_u8_str(uint8_t curr_num, char curr_pad) {\n    static char    buf[4]   = {0};\n    static uint8_t last_num = 0xFF;\n    static char    last_pad = '\\0';\n    if (last_num == curr_num && last_pad == curr_pad) {\n        return buf;\n    }\n    last_num = curr_num;\n    last_pad = curr_pad;\n    return get_numeric_str(buf, sizeof(buf), curr_num, curr_pad);\n}\n\n/** \\brief converts uint16_t into char array\n *\n * Takes an uint16_t, and uses an internal static buffer to render that value into a char array\n * A padding character may be specified, ' ' for leading spaces, '0' for leading zeros.\n *\n * NOTE: Subsequent invocations will reuse the same static buffer and overwrite the previous\n *       contents. Use the result immediately, instead of caching it.\n */\nconst char *get_u16_str(uint16_t curr_num, char curr_pad) {\n    static char     buf[6]   = {0};\n    static uint16_t last_num = 0xFF;\n    static char     last_pad = '\\0';\n    if (last_num == curr_num && last_pad == curr_pad) {\n        return buf;\n    }\n    last_num = curr_num;\n    last_pad = curr_pad;\n    return get_numeric_str(buf, sizeof(buf), curr_num, curr_pad);\n}\n\n#if defined(SECURE_ENABLE)\nvoid secure_hook_quantum(secure_status_t secure_status) {\n    // If keys are being held when this is triggered, they may not be released properly\n    // this can result in stuck keys, mods and layers.  To prevent that, manually\n    // clear these, when it is triggered.\n\n    if (secure_status == SECURE_PENDING) {\n        clear_keyboard();\n        layer_clear();\n    }\n}\n#endif\n"
  },
  {
    "path": "quantum/quantum.h",
    "content": "/* Copyright 2016-2018 Erez Zukerman, Jack Humbert, Yiancar\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#pragma once\n\n#include \"platform_deps.h\"\n#include \"wait.h\"\n#include \"matrix.h\"\n#include \"keyboard.h\"\n\n#ifdef BACKLIGHT_ENABLE\n#    include \"backlight.h\"\n#endif\n\n#ifdef LED_MATRIX_ENABLE\n#    include \"led_matrix.h\"\n#endif\n\n#if defined(RGBLIGHT_ENABLE)\n#    include \"rgblight.h\"\n#endif\n\n#ifdef RGB_MATRIX_ENABLE\n#    include \"rgb_matrix.h\"\n#endif\n\n#include \"keymap_common.h\"\n#include \"quantum_keycodes.h\"\n#include \"keycode_config.h\"\n#include \"keycode_string.h\"\n#include \"action_layer.h\"\n#include \"eeconfig.h\"\n#include \"bootloader.h\"\n#include \"timer.h\"\n#include \"sync_timer.h\"\n#include \"gpio.h\"\n#include \"atomic_util.h\"\n#include \"host.h\"\n#include \"led.h\"\n#include \"action_util.h\"\n#include \"action_tapping.h\"\n#include \"print.h\"\n#include \"debug.h\"\n#include \"suspend.h\"\n#include <stddef.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\n#ifdef BOOTMAGIC_ENABLE\n#    include \"bootmagic.h\"\n#endif\n\n#ifdef DEFERRED_EXEC_ENABLE\n#    include \"deferred_exec.h\"\n#endif\n#ifdef ACHORDION_ENABLE\n#    include \"process_achordion.h\"\n#endif\n\nextern layer_state_t default_layer_state;\n\n#ifndef NO_ACTION_LAYER\nextern layer_state_t layer_state;\n#endif\n\n#if defined(SEQUENCER_ENABLE)\n#    include \"sequencer.h\"\n#    include \"process_sequencer.h\"\n#endif\n\n#if defined(MIDI_ENABLE) && defined(MIDI_ADVANCED)\n#    include \"process_midi.h\"\n#endif\n\n#ifdef AUDIO_ENABLE\n#    include \"audio.h\"\n#    include \"process_audio.h\"\n#    include \"song_list.h\"\n#    ifdef AUDIO_CLICKY\n#        include \"process_clicky.h\"\n#    endif\n#endif\n\n#ifdef STENO_ENABLE\n#    include \"process_steno.h\"\n#endif\n\n#if defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))\n#    include \"process_music.h\"\n#endif\n\n#ifdef LEADER_ENABLE\n#    include \"leader.h\"\n#endif\n\n#ifdef UNICODE_COMMON_ENABLE\n#    include \"unicode.h\"\n#endif\n\n#ifdef UCIS_ENABLE\n#    include \"ucis.h\"\n#endif\n\n#ifdef UNICODEMAP_ENABLE\n#    include \"unicodemap.h\"\n#endif\n\n#ifdef KEY_OVERRIDE_ENABLE\n#    include \"process_key_override.h\"\n#endif\n\n#ifdef TAP_DANCE_ENABLE\n#    include \"process_tap_dance.h\"\n#endif\n\n#ifdef AUTO_SHIFT_ENABLE\n#    include \"process_auto_shift.h\"\n#endif\n\n#ifdef DYNAMIC_TAPPING_TERM_ENABLE\n#    include \"process_dynamic_tapping_term.h\"\n#endif\n\n#ifdef COMBO_ENABLE\n#    include \"process_combo.h\"\n#endif\n\n#ifdef KEY_LOCK_ENABLE\n#    include \"process_key_lock.h\"\n#endif\n\n#ifdef SPACE_CADET_ENABLE\n#    include \"process_space_cadet.h\"\n#endif\n\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\n#    include \"programmable_button.h\"\n#endif\n\n#ifdef HD44780_ENABLE\n#    include \"hd44780.h\"\n#endif\n\n#ifdef SEND_STRING_ENABLE\n#    include \"send_string.h\"\n#endif\n\n#ifdef HAPTIC_ENABLE\n#    include \"haptic.h\"\n#endif\n\n#ifdef OLED_ENABLE\n#    include \"oled_driver.h\"\n#endif\n\n#ifdef ST7565_ENABLE\n#    include \"st7565.h\"\n#endif\n\n#ifdef QUANTUM_PAINTER_ENABLE\n#    include \"qp.h\"\n#endif\n\n#ifdef DIP_SWITCH_ENABLE\n#    include \"dip_switch.h\"\n#endif\n\n#ifdef DYNAMIC_MACRO_ENABLE\n#    include \"process_dynamic_macro.h\"\n#endif\n\n#ifdef SECURE_ENABLE\n#    include \"secure.h\"\n#endif\n\n#ifdef DYNAMIC_KEYMAP_ENABLE\n#    include \"dynamic_keymap.h\"\n#endif\n\n#ifdef JOYSTICK_ENABLE\n#    include \"joystick.h\"\n#endif\n\n#ifdef DIGITIZER_ENABLE\n#    include \"digitizer.h\"\n#endif\n\n#ifdef VIA_ENABLE\n#    include \"via.h\"\n#endif\n\n#ifdef WPM_ENABLE\n#    include \"wpm.h\"\n#endif\n\n#ifdef USBPD_ENABLE\n#    include \"usbpd.h\"\n#endif\n\n#ifdef ENCODER_ENABLE\n#    include \"encoder.h\"\n#endif\n\n#ifdef POINTING_DEVICE_ENABLE\n#    include \"pointing_device.h\"\n#endif\n\n#ifdef MOUSEKEY_ENABLE\n#    include \"mousekey.h\"\n#endif\n\n#ifdef CAPS_WORD_ENABLE\n#    include \"caps_word.h\"\n#    include \"process_caps_word.h\"\n#endif\n\n#ifdef AUTOCORRECT_ENABLE\n#    include \"process_autocorrect.h\"\n#endif\n\n#ifdef TRI_LAYER_ENABLE\n#    include \"tri_layer.h\"\n#endif\n\n#ifdef REPEAT_KEY_ENABLE\n#    include \"repeat_key.h\"\n#    include \"process_repeat_key.h\"\n#endif\n\n#ifdef OS_DETECTION_ENABLE\n#    include \"os_detection.h\"\n#endif\n\n#ifdef LAYER_LOCK_ENABLE\n#    include \"layer_lock.h\"\n#endif\n\n#ifdef COMMUNITY_MODULES_ENABLE\n#    include \"community_modules.h\"\n#endif\n\nvoid set_single_default_layer(uint8_t default_layer);\nvoid set_single_persistent_default_layer(uint8_t default_layer);\n\n#define IS_LAYER_ON(layer) layer_state_is(layer)\n#define IS_LAYER_OFF(layer) !layer_state_is(layer)\n\n#define IS_LAYER_ON_STATE(state, layer) layer_state_cmp(state, layer)\n#define IS_LAYER_OFF_STATE(state, layer) !layer_state_cmp(state, layer)\n\nuint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache);\nuint16_t get_event_keycode(keyevent_t event, bool update_layer_cache);\nbool     pre_process_record_quantum(keyrecord_t *record);\nbool     pre_process_record_kb(uint16_t keycode, keyrecord_t *record);\nbool     pre_process_record_user(uint16_t keycode, keyrecord_t *record);\nbool     process_action_kb(keyrecord_t *record);\nbool     process_record_kb(uint16_t keycode, keyrecord_t *record);\nbool     process_record_user(uint16_t keycode, keyrecord_t *record);\nvoid     post_process_record_kb(uint16_t keycode, keyrecord_t *record);\nvoid     post_process_record_user(uint16_t keycode, keyrecord_t *record);\n\nvoid reset_keyboard(void);\nvoid soft_reset_keyboard(void);\n\nbool shutdown_kb(bool jump_to_bootloader);\nbool shutdown_user(bool jump_to_bootloader);\n\nvoid register_code16(uint16_t code);\nvoid unregister_code16(uint16_t code);\nvoid tap_code16(uint16_t code);\nvoid tap_code16_delay(uint16_t code, uint16_t delay);\n\nconst char *get_numeric_str(char *buf, size_t buf_len, uint32_t curr_num, char curr_pad);\nconst char *get_u8_str(uint8_t curr_num, char curr_pad);\nconst char *get_u16_str(uint16_t curr_num, char curr_pad);\n"
  },
  {
    "path": "quantum/quantum_keycodes.h",
    "content": "/* Copyright 2016-2017 Jack Humbert\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n//  Pull in dd keycodes to maintain header compatibility\n#include \"keycodes.h\"\n\n// US ANSI shifted keycode aliases\n#include \"keymap_us.h\"\n\n// TODO: sub-ranges?\n// clang-format off\n#define QK_LCTL                0x0100\n#define QK_LSFT                0x0200\n#define QK_LALT                0x0400\n#define QK_LGUI                0x0800\n#define QK_RMODS_MIN           0x1000\n#define QK_RCTL                0x1100\n#define QK_RSFT                0x1200\n#define QK_RALT                0x1400\n#define QK_RGUI                0x1800\n\n#define SAFE_RANGE             QK_USER\n// clang-format on\n\n// Generic decoding for the whole QK_MODS range\n#define QK_MODS_GET_MODS(kc) (((kc) >> 8) & 0x1F)\n#define QK_MODS_GET_BASIC_KEYCODE(kc) ((kc)&0xFF)\n\n// Keycode modifiers & aliases\n#define LCTL(kc) (QK_LCTL | (kc))\n#define LSFT(kc) (QK_LSFT | (kc))\n#define LALT(kc) (QK_LALT | (kc))\n#define LGUI(kc) (QK_LGUI | (kc))\n#define LOPT(kc) LALT(kc)\n#define LCMD(kc) LGUI(kc)\n#define LWIN(kc) LGUI(kc)\n#define RCTL(kc) (QK_RCTL | (kc))\n#define RSFT(kc) (QK_RSFT | (kc))\n#define RALT(kc) (QK_RALT | (kc))\n#define RGUI(kc) (QK_RGUI | (kc))\n#define ALGR(kc) RALT(kc)\n#define ROPT(kc) RALT(kc)\n#define RCMD(kc) RGUI(kc)\n#define RWIN(kc) RGUI(kc)\n\n#define HYPR(kc) (QK_LCTL | QK_LSFT | QK_LALT | QK_LGUI | (kc))\n#define MEH(kc) (QK_LCTL | QK_LSFT | QK_LALT | (kc))\n#define LCAG(kc) (QK_LCTL | QK_LALT | QK_LGUI | (kc))\n#define LSG(kc) (QK_LSFT | QK_LGUI | (kc))\n#define SGUI(kc) LSG(kc)\n#define SCMD(kc) LSG(kc)\n#define SWIN(kc) LSG(kc)\n#define LAG(kc) (QK_LALT | QK_LGUI | (kc))\n#define RSG(kc) (QK_RSFT | QK_RGUI | (kc))\n#define RAG(kc) (QK_RALT | QK_RGUI | (kc))\n#define LCA(kc) (QK_LCTL | QK_LALT | (kc))\n#define LSA(kc) (QK_LSFT | QK_LALT | (kc))\n#define RSA(kc) (QK_RSFT | QK_RALT | (kc))\n#define RCS(kc) (QK_RCTL | QK_RSFT | (kc))\n#define SAGR(kc) RSA(kc)\n\n// Modified keycode aliases\n#define C(kc) LCTL(kc)\n#define S(kc) LSFT(kc)\n#define A(kc) LALT(kc)\n#define G(kc) LGUI(kc)\n\n// GOTO layer - 32 layer max\n#define TO(layer) (QK_TO | ((layer)&0x1F))\n#define QK_TO_GET_LAYER(kc) ((kc)&0x1F)\n\n// Momentary switch layer - 32 layer max\n#define MO(layer) (QK_MOMENTARY | ((layer)&0x1F))\n#define QK_MOMENTARY_GET_LAYER(kc) ((kc)&0x1F)\n\n// Set default layer - 32 layer max\n#define DF(layer) (QK_DEF_LAYER | ((layer)&0x1F))\n#define QK_DEF_LAYER_GET_LAYER(kc) ((kc)&0x1F)\n\n// Set persistent default layer - 32 layer max\n#define PDF(layer) (QK_PERSISTENT_DEF_LAYER | ((layer)&0x1F))\n#define QK_PERSISTENT_DEF_LAYER_GET_LAYER(kc) ((kc)&0x1F)\n\n// Toggle to layer - 32 layer max\n#define TG(layer) (QK_TOGGLE_LAYER | ((layer)&0x1F))\n#define QK_TOGGLE_LAYER_GET_LAYER(kc) ((kc)&0x1F)\n\n// One-shot layer - 32 layer max\n#define OSL(layer) (QK_ONE_SHOT_LAYER | ((layer)&0x1F))\n#define QK_ONE_SHOT_LAYER_GET_LAYER(kc) ((kc)&0x1F)\n\n// L-ayer M-od: Momentary switch layer with modifiers active - 16 layer max\n#define LM(layer, mod) (QK_LAYER_MOD | (((layer)&0xF) << 5) | ((mod)&0x1F))\n#define QK_LAYER_MOD_GET_LAYER(kc) (((kc) >> 5) & 0xF)\n#define QK_LAYER_MOD_GET_MODS(kc) ((kc)&0x1F)\n\n// One-shot mod\n#define OSM(mod) (QK_ONE_SHOT_MOD | ((mod)&0x1F))\n#define QK_ONE_SHOT_MOD_GET_MODS(kc) ((kc)&0x1F)\n\n// Layer tap-toggle - 32 layer max\n#define TT(layer) (QK_LAYER_TAP_TOGGLE | ((layer)&0x1F))\n#define QK_LAYER_TAP_TOGGLE_GET_LAYER(kc) ((kc)&0x1F)\n\n// L-ayer, T-ap - 256 keycode max, 16 layer max\n#define LT(layer, kc) (QK_LAYER_TAP | (((layer)&0xF) << 8) | ((kc)&0xFF))\n#define QK_LAYER_TAP_GET_LAYER(kc) (((kc) >> 8) & 0xF)\n#define QK_LAYER_TAP_GET_TAP_KEYCODE(kc) ((kc)&0xFF)\n\n// M-od, T-ap - 256 keycode max\n#define MT(mod, kc) (QK_MOD_TAP | (((mod)&0x1F) << 8) | ((kc)&0xFF))\n#define QK_MOD_TAP_GET_MODS(kc) (((kc) >> 8) & 0x1F)\n#define QK_MOD_TAP_GET_TAP_KEYCODE(kc) ((kc)&0xFF)\n\n#define LCTL_T(kc) MT(MOD_LCTL, kc)\n#define RCTL_T(kc) MT(MOD_RCTL, kc)\n#define CTL_T(kc) LCTL_T(kc)\n\n#define LSFT_T(kc) MT(MOD_LSFT, kc)\n#define RSFT_T(kc) MT(MOD_RSFT, kc)\n#define SFT_T(kc) LSFT_T(kc)\n\n#define LALT_T(kc) MT(MOD_LALT, kc)\n#define RALT_T(kc) MT(MOD_RALT, kc)\n#define LOPT_T(kc) LALT_T(kc)\n#define ROPT_T(kc) RALT_T(kc)\n#define ALGR_T(kc) RALT_T(kc)\n#define ALT_T(kc) LALT_T(kc)\n#define OPT_T(kc) LOPT_T(kc)\n\n#define LGUI_T(kc) MT(MOD_LGUI, kc)\n#define RGUI_T(kc) MT(MOD_RGUI, kc)\n#define LCMD_T(kc) LGUI_T(kc)\n#define LWIN_T(kc) LGUI_T(kc)\n#define RCMD_T(kc) RGUI_T(kc)\n#define RWIN_T(kc) RGUI_T(kc)\n#define GUI_T(kc) LGUI_T(kc)\n#define CMD_T(kc) LCMD_T(kc)\n#define WIN_T(kc) LWIN_T(kc)\n\n#define C_S_T(kc) MT(MOD_LCTL | MOD_LSFT, kc)                        // Left Control + Shift e.g. for gnome-terminal\n#define MEH_T(kc) MT(MOD_LCTL | MOD_LSFT | MOD_LALT, kc)             // Meh is a less hyper version of the Hyper key -- doesn't include GUI, so just Left Control + Shift + Alt\n#define LCAG_T(kc) MT(MOD_LCTL | MOD_LALT | MOD_LGUI, kc)            // Left Control + Alt + GUI\n#define RCAG_T(kc) MT(MOD_RCTL | MOD_RALT | MOD_RGUI, kc)            // Right Control + Alt + GUI\n#define HYPR_T(kc) MT(MOD_LCTL | MOD_LSFT | MOD_LALT | MOD_LGUI, kc) // see http://brettterpstra.com/2012/12/08/a-useful-caps-lock-key/\n#define LSG_T(kc) MT(MOD_LSFT | MOD_LGUI, kc)                        // Left Shift + GUI\n#define SGUI_T(kc) LSG_T(kc)\n#define SCMD_T(kc) LSG_T(kc)\n#define SWIN_T(kc) LSG_T(kc)\n#define LAG_T(kc) MT(MOD_LALT | MOD_LGUI, kc) // Left Alt + GUI\n#define RSG_T(kc) MT(MOD_RSFT | MOD_RGUI, kc) // Right Shift + GUI\n#define RAG_T(kc) MT(MOD_RALT | MOD_RGUI, kc) // Right Alt + GUI\n#define LCA_T(kc) MT(MOD_LCTL | MOD_LALT, kc) // Left Control + Alt\n#define LSA_T(kc) MT(MOD_LSFT | MOD_LALT, kc) // Left Shift + Alt\n#define RSA_T(kc) MT(MOD_RSFT | MOD_RALT, kc) // Right Shift + Alt\n#define RCS_T(kc) MT(MOD_RCTL | MOD_RSFT, kc) // Right Control + Shift\n#define SAGR_T(kc) RSA_T(kc)\n\n#define ALL_T(kc) HYPR_T(kc)\n\n// Dedicated keycode versions for Hyper and Meh, if you want to use them as standalone keys rather than mod-tap\n#define KC_HYPR HYPR(KC_NO)\n#define KC_MEH MEH(KC_NO)\n\n// Unicode aliases\n// UNICODE_ENABLE - Allows Unicode input up to 0x7FFF\n#define UC(c) (QK_UNICODE | (c))\n#define QK_UNICODE_GET_CODE_POINT(kc) ((kc)&0x7FFF)\n\n// UNICODEMAP_ENABLE - Allows Unicode input up to 0x10FFFF, requires unicode_map\n#define UM(i) (QK_UNICODEMAP | ((i)&0x3FFF))\n#define QK_UNICODEMAP_GET_INDEX(kc) ((kc)&0x3FFF)\n\n#define UP(i, j) (QK_UNICODEMAP_PAIR | ((i)&0x7F) | (((j)&0x7F) << 7)) // 127 max i and j\n#define QK_UNICODEMAP_PAIR_GET_UNSHIFTED_INDEX(kc) ((kc)&0x7F)\n#define QK_UNICODEMAP_PAIR_GET_SHIFTED_INDEX(kc) (((kc) >> 7) & 0x7F)\n\n// Swap Hands\n#define SH_T(kc) (QK_SWAP_HANDS | ((kc)&0xFF))\n#define QK_SWAP_HANDS_GET_TAP_KEYCODE(kc) ((kc)&0xFF)\n\n// Tap dance\n#define TD(i) (QK_TAP_DANCE | ((i)&0xFF))\n#define QK_TAP_DANCE_GET_INDEX(kc) ((kc)&0xFF)\n\n// MIDI aliases\n#define MIDI_TONE_MIN QK_MIDI_NOTE_C_0\n#define MIDI_TONE_MAX QK_MIDI_NOTE_B_5\n#define MIDI_OCTAVE_MIN QK_MIDI_OCTAVE_N2\n#define MIDI_OCTAVE_MAX QK_MIDI_OCTAVE_7\n#define MIDI_TRANSPOSE_MIN QK_MIDI_TRANSPOSE_N6\n#define MIDI_TRANSPOSE_MAX QK_MIDI_TRANSPOSE_6\n#define MIDI_VELOCITY_MIN QK_MIDI_VELOCITY_0\n#define MIDI_VELOCITY_MAX QK_MIDI_VELOCITY_10\n#define MIDI_CHANNEL_MIN QK_MIDI_CHANNEL_1\n#define MIDI_CHANNEL_MAX QK_MIDI_CHANNEL_16\n\n// TODO: somehow migrate sequencer to DD?\n#include \"sequencer.h\"\n\n#define SEQUENCER_STEP_MIN (QK_SEQUENCER + 0xF)\n#define SEQUENCER_STEP_MAX (SEQUENCER_STEP_MIN + SEQUENCER_STEPS)\n\n#define SEQUENCER_RESOLUTION_MIN (SEQUENCER_STEP_MAX + 1)\n#define SEQUENCER_RESOLUTION_MAX (SEQUENCER_RESOLUTION_MIN + SEQUENCER_RESOLUTIONS)\n\n#define SEQUENCER_TRACK_MIN (SEQUENCER_RESOLUTION_MAX + 1)\n#define SEQUENCER_TRACK_MAX (SEQUENCER_TRACK_MIN + SEQUENCER_TRACKS)\n\n#define SQ_S(n) (n < SEQUENCER_STEPS ? SEQUENCER_STEP_MIN + n : KC_NO)\n#define SQ_R(n) (n < SEQUENCER_RESOLUTIONS ? SEQUENCER_RESOLUTION_MIN + n : KC_NO)\n#define SQ_T(n) (n < SEQUENCER_TRACKS ? SEQUENCER_TRACK_MIN + n : KC_NO)\n\n#include \"quantum_keycodes_legacy.h\"\n"
  },
  {
    "path": "quantum/quantum_keycodes_legacy.h",
    "content": "#pragma once\n\n// clang-format off\n\n// Deprecated Quantum keycodes\n#define RGB_TOG QK_UNDERGLOW_TOGGLE\n#define RGB_MOD QK_UNDERGLOW_MODE_NEXT\n#define RGB_MODE_FORWARD QK_UNDERGLOW_MODE_NEXT\n#define RGB_RMOD QK_UNDERGLOW_MODE_PREVIOUS\n#define RGB_MODE_REVERSE QK_UNDERGLOW_MODE_PREVIOUS\n#define RGB_HUI QK_UNDERGLOW_HUE_UP\n#define RGB_HUD QK_UNDERGLOW_HUE_DOWN\n#define RGB_SAI QK_UNDERGLOW_SATURATION_UP\n#define RGB_SAD QK_UNDERGLOW_SATURATION_DOWN\n#define RGB_VAI QK_UNDERGLOW_VALUE_UP\n#define RGB_VAD QK_UNDERGLOW_VALUE_DOWN\n#define RGB_SPI QK_UNDERGLOW_SPEED_UP\n#define RGB_SPD QK_UNDERGLOW_SPEED_DOWN\n\n#define KC_MS_UP QK_MOUSE_CURSOR_UP\n#define KC_MS_U QK_MOUSE_CURSOR_UP\n#define KC_MS_DOWN QK_MOUSE_CURSOR_DOWN\n#define KC_MS_D QK_MOUSE_CURSOR_DOWN\n#define KC_MS_LEFT QK_MOUSE_CURSOR_LEFT\n#define KC_MS_L QK_MOUSE_CURSOR_LEFT\n#define KC_MS_RIGHT QK_MOUSE_CURSOR_RIGHT\n#define KC_MS_R QK_MOUSE_CURSOR_RIGHT\n#define KC_MS_BTN1 QK_MOUSE_BUTTON_1\n#define KC_BTN1 QK_MOUSE_BUTTON_1\n#define KC_MS_BTN2 QK_MOUSE_BUTTON_2\n#define KC_BTN2 QK_MOUSE_BUTTON_2\n#define KC_MS_BTN3 QK_MOUSE_BUTTON_3\n#define KC_BTN3 QK_MOUSE_BUTTON_3\n#define KC_MS_BTN4 QK_MOUSE_BUTTON_4\n#define KC_BTN4 QK_MOUSE_BUTTON_4\n#define KC_MS_BTN5 QK_MOUSE_BUTTON_5\n#define KC_BTN5 QK_MOUSE_BUTTON_5\n#define KC_MS_BTN6 QK_MOUSE_BUTTON_6\n#define KC_BTN6 QK_MOUSE_BUTTON_6\n#define KC_MS_BTN7 QK_MOUSE_BUTTON_7\n#define KC_BTN7 QK_MOUSE_BUTTON_7\n#define KC_MS_BTN8 QK_MOUSE_BUTTON_8\n#define KC_BTN8 QK_MOUSE_BUTTON_8\n#define KC_MS_WH_UP QK_MOUSE_WHEEL_UP\n#define KC_WH_U QK_MOUSE_WHEEL_UP\n#define KC_MS_WH_DOWN QK_MOUSE_WHEEL_DOWN\n#define KC_WH_D QK_MOUSE_WHEEL_DOWN\n#define KC_MS_WH_LEFT QK_MOUSE_WHEEL_LEFT\n#define KC_WH_L QK_MOUSE_WHEEL_LEFT\n#define KC_MS_WH_RIGHT QK_MOUSE_WHEEL_RIGHT\n#define KC_WH_R QK_MOUSE_WHEEL_RIGHT\n#define KC_MS_ACCEL0 QK_MOUSE_ACCELERATION_0\n#define KC_ACL0 QK_MOUSE_ACCELERATION_0\n#define KC_MS_ACCEL1 QK_MOUSE_ACCELERATION_1\n#define KC_ACL1 QK_MOUSE_ACCELERATION_1\n#define KC_MS_ACCEL2 QK_MOUSE_ACCELERATION_2\n#define KC_ACL2 QK_MOUSE_ACCELERATION_2\n\n#define QK_OUTPUT_AUTO OU_AUTO\n"
  },
  {
    "path": "quantum/raw_hid.c",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"raw_hid.h\"\n#include \"host.h\"\n\nvoid raw_hid_send(uint8_t *data, uint8_t length) {\n    host_raw_hid_send(data, length);\n}\n\n__attribute__((weak)) void raw_hid_receive(uint8_t *data, uint8_t length) {\n    // Users should #include \"raw_hid.h\" in their own code\n    // and implement this function there. Leave this as weak linkage\n    // so users can opt to not handle data coming in.\n}\n"
  },
  {
    "path": "quantum/raw_hid.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n\n/**\n * \\file\n *\n * \\defgroup raw_hid Raw HID API\n * \\{\n */\n\n/**\n * \\brief Callback, invoked when a raw HID report has been received from the host.\n *\n * \\param data A pointer to the received data. Always 32 bytes in length.\n * \\param length The length of the buffer. Always 32.\n */\nvoid raw_hid_receive(uint8_t *data, uint8_t length);\n\n/**\n * \\brief Send an HID report.\n *\n * \\param data A pointer to the data to send. Must always be 32 bytes in length.\n * \\param length The length of the buffer. Must always be 32.\n */\nvoid raw_hid_send(uint8_t *data, uint8_t length);\n\n/** \\} */\n"
  },
  {
    "path": "quantum/repeat_key.c",
    "content": "// Copyright 2022-2023 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"repeat_key.h\"\n#include \"quantum_keycodes.h\"\n\n// Variables saving the state of the last key press.\nstatic keyrecord_t last_record = {0};\nstatic uint8_t     last_mods   = 0;\n// Signed count of the number of times the last key has been repeated or\n// alternate repeated: it is 0 when a key is pressed normally, positive when\n// repeated, and negative when alternate repeated.\nstatic int8_t last_repeat_count = 0;\n// The repeat_count, but set to 0 outside of repeat_key_invoke() so that it is\n// nonzero only while a repeated key is being processed.\nstatic int8_t processing_repeat_count = 0;\n\nuint16_t get_last_keycode(void) {\n    return last_record.keycode;\n}\n\nuint8_t get_last_mods(void) {\n    return last_mods;\n}\n\nvoid set_last_keycode(uint16_t keycode) {\n    set_last_record(keycode, &(keyrecord_t){\n#ifndef NO_ACTION_TAPPING\n                                 .tap.interrupted = false,\n                                 .tap.count       = 1,\n#endif\n                             });\n}\n\nvoid set_last_mods(uint8_t mods) {\n    last_mods = mods;\n}\n\nvoid set_last_record(uint16_t keycode, keyrecord_t* record) {\n    last_record         = *record;\n    last_record.keycode = keycode;\n    last_repeat_count   = 0;\n}\n\n/** @brief Updates `last_repeat_count` in direction `dir`. */\nstatic void update_last_repeat_count(int8_t dir) {\n    if (dir * last_repeat_count < 0) {\n        last_repeat_count = dir;\n    } else if (dir * last_repeat_count < 127) {\n        last_repeat_count += dir;\n    }\n}\n\nint8_t get_repeat_key_count(void) {\n    return processing_repeat_count;\n}\n\nvoid repeat_key_invoke(const keyevent_t* event) {\n    // It is possible (e.g. in rolled presses) that the last key changes while\n    // the Repeat Key is pressed. To prevent stuck keys, it is important to\n    // remember separately what key record was processed on press so that the\n    // the corresponding record is generated on release.\n    static keyrecord_t registered_record       = {0};\n    static int8_t      registered_repeat_count = 0;\n    // Since this function calls process_record(), it may recursively call\n    // itself. We return early if `processing_repeat_count` is nonzero to\n    // prevent infinite recursion.\n    if (processing_repeat_count || !last_record.keycode) {\n        return;\n    }\n\n    if (event->pressed) {\n        update_last_repeat_count(1);\n        // On press, apply the last mods state, stacking on top of current mods.\n        register_weak_mods(last_mods);\n        registered_record       = last_record;\n        registered_repeat_count = last_repeat_count;\n    }\n\n    // Generate a keyrecord and plumb it into the event pipeline.\n    registered_record.event = *event;\n    processing_repeat_count = registered_repeat_count;\n    process_record(&registered_record);\n    processing_repeat_count = 0;\n\n    // On release, restore the mods state.\n    if (!event->pressed) {\n        unregister_weak_mods(last_mods);\n    }\n}\n\n#ifndef NO_ALT_REPEAT_KEY\n/**\n * @brief Find alternate keycode from a table of opposing keycode pairs.\n * @param table Array of pairs of basic keycodes, declared as PROGMEM.\n * @param table_size_bytes The size of the table in bytes.\n * @param target The basic keycode to find.\n * @return The alternate basic keycode, or KC_NO if none was found.\n *\n * @note The table keycodes and target must be basic keycodes.\n *\n * This helper is used several times below to define alternate keys. Given a\n * table of pairs of basic keycodes, the function finds the pair containing\n * `target` and returns the other keycode in the pair.\n */\nstatic uint8_t find_alt_keycode(const uint8_t (*table)[2], uint8_t table_size_bytes, uint8_t target) {\n    const uint8_t* keycodes = (const uint8_t*)table;\n    for (uint8_t i = 0; i < table_size_bytes; ++i) {\n        if (target == pgm_read_byte(keycodes + i)) {\n            // Xor (i ^ 1) the index to get the other element in the pair.\n            return pgm_read_byte(keycodes + (i ^ 1));\n        }\n    }\n    return KC_NO;\n}\n\nuint16_t get_alt_repeat_key_keycode(void) {\n    uint16_t keycode = last_record.keycode;\n    uint8_t  mods    = last_mods;\n\n    // Call the user callback first to give it a chance to override the default\n    // alternate key definitions that follow.\n    uint16_t alt_keycode = get_alt_repeat_key_keycode_user(keycode, mods);\n\n    if (alt_keycode != KC_TRANSPARENT) {\n        return alt_keycode;\n    }\n\n    // Convert 8-bit mods to the 5-bit format used in keycodes. This is lossy:\n    // if left and right handed mods were mixed, they all become right handed.\n    mods = ((mods & 0xf0) ? /* set right hand bit */ 0x10 : 0)\n           // Combine right and left hand mods.\n           | (((mods >> 4) | mods) & 0xf);\n\n    switch (keycode) {\n        case QK_MODS ... QK_MODS_MAX: // Unpack modifier + basic key.\n            mods |= QK_MODS_GET_MODS(keycode);\n            keycode = QK_MODS_GET_BASIC_KEYCODE(keycode);\n            break;\n\n#    ifndef NO_ACTION_TAPPING\n        case QK_MOD_TAP ... QK_MOD_TAP_MAX:\n            keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode);\n            break;\n#        ifndef NO_ACTION_LAYER\n        case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:\n            keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode);\n            break;\n#        endif // NO_ACTION_LAYER\n#    endif     // NO_ACTION_TAPPING\n\n#    ifdef SWAP_HANDS_ENABLE\n        case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:\n            if (IS_SWAP_HANDS_KEYCODE(keycode)) {\n                return KC_NO;\n            }\n            keycode = QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode);\n            break;\n#    endif // SWAP_HANDS_ENABLE\n    }\n\n    if (IS_QK_BASIC(keycode)) {\n        if ((mods & (MOD_LCTL | MOD_LALT | MOD_LGUI))) {\n            // The last key was pressed with a modifier other than Shift.\n            // The following maps\n            //   mod + F <-> mod + B\n            // and a few others, supporting several core hotkeys used in\n            // Emacs, Vim, less, and other programs.\n            // clang-format off\n            static const uint8_t pairs[][2] PROGMEM = {\n                {KC_F   , KC_B   },  // Forward / Backward.\n                {KC_D   , KC_U   },  // Down / Up.\n                {KC_N   , KC_P   },  // Next / Previous.\n                {KC_A   , KC_E   },  // Home / End.\n                {KC_O   , KC_I   },  // Older / Newer in Vim jump list.\n            };\n            // clang-format on\n            alt_keycode = find_alt_keycode(pairs, sizeof(pairs), keycode);\n        } else {\n            // The last key was pressed with no mods or only Shift. The\n            // following map a few more Vim hotkeys.\n            // clang-format off\n            static const uint8_t pairs[][2] PROGMEM = {\n                {KC_J   , KC_K   },  // Down / Up.\n                {KC_H   , KC_L   },  // Left / Right.\n                // These two lines map W and E to B, and B to W.\n                {KC_W   , KC_B   },  // Forward / Backward by word.\n                {KC_E   , KC_B   },  // Forward / Backward by word.\n            };\n            // clang-format on\n            alt_keycode = find_alt_keycode(pairs, sizeof(pairs), keycode);\n        }\n\n        if (!alt_keycode) {\n            // The following key pairs are considered with any mods.\n            // clang-format off\n            static const uint8_t pairs[][2] PROGMEM = {\n                {KC_LEFT, KC_RGHT},  // Left / Right Arrow.\n                {KC_UP  , KC_DOWN},  // Up / Down Arrow.\n                {KC_HOME, KC_END },  // Home / End.\n                {KC_PGUP, KC_PGDN},  // Page Up / Page Down.\n                {KC_BSPC, KC_DEL },  // Backspace / Delete.\n                {KC_LBRC, KC_RBRC},  // Brackets [ ] and { }.\n#ifdef EXTRAKEY_ENABLE\n                {KC_WBAK, KC_WFWD},  // Browser Back / Forward.\n                {KC_MNXT, KC_MPRV},  // Next / Previous Media Track.\n                {KC_MFFD, KC_MRWD},  // Fast Forward / Rewind Media.\n                {KC_VOLU, KC_VOLD},  // Volume Up / Down.\n                {KC_BRIU, KC_BRID},  // Brightness Up / Down.\n#endif  // EXTRAKEY_ENABLE\n#ifdef MOUSEKEY_ENABLE\n                {MS_LEFT, MS_RGHT},  // Mouse Cursor Left / Right.\n                {MS_UP,   MS_DOWN},  // Mouse Cursor Up / Down.\n                {MS_WHLL, MS_WHLR},  // Mouse Wheel Left / Right.\n                {MS_WHLU, MS_WHLD},  // Mouse Wheel Up / Down.\n#endif  // MOUSEKEY_ENABLE\n            };\n            // clang-format on\n            alt_keycode = find_alt_keycode(pairs, sizeof(pairs), keycode);\n        }\n\n        if (alt_keycode) {\n            // Combine basic keycode with mods.\n            return (mods << 8) | alt_keycode;\n        }\n    }\n\n    return KC_NO; // No alternate key found.\n}\n\nvoid alt_repeat_key_invoke(const keyevent_t* event) {\n    static keyrecord_t registered_record       = {0};\n    static int8_t      registered_repeat_count = 0;\n    // Since this function calls process_record(), it may recursively call\n    // itself. We return early if `processing_repeat_count` is nonzero to\n    // prevent infinite recursion.\n    if (processing_repeat_count) {\n        return;\n    }\n\n    if (event->pressed) {\n        registered_record = (keyrecord_t){\n#    ifndef NO_ACTION_TAPPING\n            .tap.interrupted = false,\n            .tap.count       = 0,\n#    endif\n            .keycode = get_alt_repeat_key_keycode(),\n        };\n    }\n\n    // Early return if there is no alternate key defined.\n    if (!registered_record.keycode) {\n        return;\n    }\n\n    if (event->pressed) {\n        update_last_repeat_count(-1);\n        registered_repeat_count = last_repeat_count;\n    }\n\n    // Generate a keyrecord and plumb it into the event pipeline.\n    registered_record.event = *event;\n    processing_repeat_count = registered_repeat_count;\n    process_record(&registered_record);\n    processing_repeat_count = 0;\n}\n\n// Default implementation of get_alt_repeat_key_keycode_user().\n__attribute__((weak)) uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) {\n    return KC_TRANSPARENT;\n}\n#endif // NO_ALT_REPEAT_KEY\n"
  },
  {
    "path": "quantum/repeat_key.h",
    "content": "// Copyright 2022-2023 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"action.h\"\n#include \"keyboard.h\"\n\nuint16_t get_last_keycode(void);             /**< Keycode of the last key. */\nuint8_t  get_last_mods(void);                /**< Mods active with the last key. */\nvoid     set_last_keycode(uint16_t keycode); /**< Sets the last key. */\nvoid     set_last_mods(uint8_t mods);        /**< Sets the last mods. */\n\n/** @brief Gets the record for the last key. */\nkeyrecord_t* get_last_record(void);\n\n/** @brief Sets keycode and record info for the last key. */\nvoid set_last_record(uint16_t keycode, keyrecord_t* record);\n\n/**\n * @brief Signed count of times the key has been repeated or alternate repeated.\n *\n * @note The count is nonzero only while a repeated or alternate-repeated key is\n *       being processed.\n *\n * When a key is pressed normally, the count is 0. When the Repeat Key is used\n * to repeat a key, the count is 1 on the first repeat, 2 on the second repeat,\n * and continuing up to 127.\n *\n * Negative counts are used similarly for alternate repeating. When the\n * Alternate Repeat Key is used, the count is -1 on the first alternate repeat,\n * -2 on the second, continuing down to -127.\n */\nint8_t get_repeat_key_count(void);\n\n/**\n * @brief Calls `process_record()` on a generated record repeating the last key.\n * @param event Event information in the generated record.\n */\nvoid repeat_key_invoke(const keyevent_t* event);\n\n#ifndef NO_ALT_REPEAT_KEY\n\n/**\n * @brief Keycode to be used for alternate repeating.\n *\n * Alternate Repeat performs this keycode based on the last eligible pressed key\n * and mods, get_last_keycode() and get_last_mods(). For example, when the last\n * key was KC_UP, this function returns KC_DOWN. The function returns KC_NO if\n * the last key doesn't have a defined alternate.\n */\nuint16_t get_alt_repeat_key_keycode(void);\n\n/**\n * @brief Calls `process_record()` to alternate repeat the last key.\n * @param event Event information in the generated record.\n */\nvoid alt_repeat_key_invoke(const keyevent_t* event);\n\n/**\n * @brief Optional user callback to define additional alternate keys.\n *\n * When `get_alt_repeat_key_keycode()` is called, it first calls this callback.\n * It should return a keycode representing the \"alternate\" of the given keycode\n * and mods. Returning KC_NO defers to the default definitions in\n * `get_alt_repeat_key_keycode()`.\n */\nuint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods);\n\n#endif // NO_ALT_REPEAT_KEY\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/alpha_mods_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_ALPHAS_MODS\nRGB_MATRIX_EFFECT(ALPHAS_MODS)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\n// alphas = color1, mods = color2\nbool ALPHAS_MODS(effect_params_t* params) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    hsv_t hsv  = rgb_matrix_config.hsv;\n    rgb_t rgb1 = rgb_matrix_hsv_to_rgb(hsv);\n    hsv.h += rgb_matrix_config.speed;\n    rgb_t rgb2 = rgb_matrix_hsv_to_rgb(hsv);\n\n    for (uint8_t i = led_min; i < led_max; i++) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        if (HAS_FLAGS(g_led_config.flags[i], LED_FLAG_MODIFIER)) {\n            rgb_matrix_set_color(i, rgb2.r, rgb2.g, rgb2.b);\n        } else {\n            rgb_matrix_set_color(i, rgb1.r, rgb1.g, rgb1.b);\n        }\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_ALPHAS_MODS\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/breathing_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_BREATHING\nRGB_MATRIX_EFFECT(BREATHING)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nhsv_t BREATHING_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    hsv.v = scale8(abs8(sin8(time / 2) - 128) * 2, hsv.v);\n    return hsv;\n}\n\nbool BREATHING(effect_params_t* params) {\n    return effect_runner_i(params, &BREATHING_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_BREATHING\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/colorband_pinwheel_sat_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT\nRGB_MATRIX_EFFECT(BAND_PINWHEEL_SAT)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t BAND_PINWHEEL_SAT_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t time) {\n    hsv.s = scale8(hsv.s - time - atan2_8(dy, dx) * 3, hsv.s);\n    return hsv;\n}\n\nbool BAND_PINWHEEL_SAT(effect_params_t* params) {\n    return effect_runner_dx_dy(params, &BAND_PINWHEEL_SAT_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/colorband_pinwheel_val_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL\nRGB_MATRIX_EFFECT(BAND_PINWHEEL_VAL)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t BAND_PINWHEEL_VAL_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t time) {\n    hsv.v = scale8(hsv.v - time - atan2_8(dy, dx) * 3, hsv.v);\n    return hsv;\n}\n\nbool BAND_PINWHEEL_VAL(effect_params_t* params) {\n    return effect_runner_dx_dy(params, &BAND_PINWHEEL_VAL_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/colorband_sat_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_BAND_SAT\nRGB_MATRIX_EFFECT(BAND_SAT)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t BAND_SAT_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    int16_t s = hsv.s - abs(scale8(g_led_config.point[i].x, 228) + 28 - time) * 8;\n    hsv.s     = scale8(s < 0 ? 0 : s, hsv.s);\n    return hsv;\n}\n\nbool BAND_SAT(effect_params_t* params) {\n    return effect_runner_i(params, &BAND_SAT_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_BAND_SAT\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/colorband_spiral_sat_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT\nRGB_MATRIX_EFFECT(BAND_SPIRAL_SAT)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t BAND_SPIRAL_SAT_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t dist, uint8_t time) {\n    hsv.s = scale8(hsv.s + dist - time - atan2_8(dy, dx), hsv.s);\n    return hsv;\n}\n\nbool BAND_SPIRAL_SAT(effect_params_t* params) {\n    return effect_runner_dx_dy_dist(params, &BAND_SPIRAL_SAT_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/colorband_spiral_val_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL\nRGB_MATRIX_EFFECT(BAND_SPIRAL_VAL)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t BAND_SPIRAL_VAL_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t dist, uint8_t time) {\n    hsv.v = scale8(hsv.v + dist - time - atan2_8(dy, dx), hsv.v);\n    return hsv;\n}\n\nbool BAND_SPIRAL_VAL(effect_params_t* params) {\n    return effect_runner_dx_dy_dist(params, &BAND_SPIRAL_VAL_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/colorband_val_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_BAND_VAL\nRGB_MATRIX_EFFECT(BAND_VAL)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t BAND_VAL_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    int16_t v = hsv.v - abs(scale8(g_led_config.point[i].x, 228) + 28 - time) * 8;\n    hsv.v     = scale8(v < 0 ? 0 : v, hsv.v);\n    return hsv;\n}\n\nbool BAND_VAL(effect_params_t* params) {\n    return effect_runner_i(params, &BAND_VAL_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_BAND_VAL\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/cycle_all_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_CYCLE_ALL\nRGB_MATRIX_EFFECT(CYCLE_ALL)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t CYCLE_ALL_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    hsv.h = time;\n    return hsv;\n}\n\nbool CYCLE_ALL(effect_params_t* params) {\n    return effect_runner_i(params, &CYCLE_ALL_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_CYCLE_ALL\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/cycle_left_right_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT\nRGB_MATRIX_EFFECT(CYCLE_LEFT_RIGHT)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t CYCLE_LEFT_RIGHT_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    hsv.h = g_led_config.point[i].x - time;\n    return hsv;\n}\n\nbool CYCLE_LEFT_RIGHT(effect_params_t* params) {\n    return effect_runner_i(params, &CYCLE_LEFT_RIGHT_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/cycle_out_in_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN\nRGB_MATRIX_EFFECT(CYCLE_OUT_IN)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t CYCLE_OUT_IN_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t dist, uint8_t time) {\n    hsv.h = 3 * dist / 2 + time;\n    return hsv;\n}\n\nbool CYCLE_OUT_IN(effect_params_t* params) {\n    return effect_runner_dx_dy_dist(params, &CYCLE_OUT_IN_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_CYCLE_OUT_IN\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/cycle_out_in_dual_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL\nRGB_MATRIX_EFFECT(CYCLE_OUT_IN_DUAL)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t CYCLE_OUT_IN_DUAL_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t time) {\n    dx           = (k_rgb_matrix_center.x / 2) - abs8(dx);\n    uint8_t dist = sqrt16(dx * dx + dy * dy);\n    hsv.h        = 3 * dist + time;\n    return hsv;\n}\n\nbool CYCLE_OUT_IN_DUAL(effect_params_t* params) {\n    return effect_runner_dx_dy(params, &CYCLE_OUT_IN_DUAL_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/cycle_pinwheel_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_CYCLE_PINWHEEL\nRGB_MATRIX_EFFECT(CYCLE_PINWHEEL)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t CYCLE_PINWHEEL_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t time) {\n    hsv.h = atan2_8(dy, dx) + time;\n    return hsv;\n}\n\nbool CYCLE_PINWHEEL(effect_params_t* params) {\n    return effect_runner_dx_dy(params, &CYCLE_PINWHEEL_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_CYCLE_PINWHEEL\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/cycle_spiral_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_CYCLE_SPIRAL\nRGB_MATRIX_EFFECT(CYCLE_SPIRAL)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t CYCLE_SPIRAL_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t dist, uint8_t time) {\n    hsv.h = dist - time - atan2_8(dy, dx);\n    return hsv;\n}\n\nbool CYCLE_SPIRAL(effect_params_t* params) {\n    return effect_runner_dx_dy_dist(params, &CYCLE_SPIRAL_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_CYCLE_SPIRAL\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/cycle_up_down_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_CYCLE_UP_DOWN\nRGB_MATRIX_EFFECT(CYCLE_UP_DOWN)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t CYCLE_UP_DOWN_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    hsv.h = g_led_config.point[i].y - time;\n    return hsv;\n}\n\nbool CYCLE_UP_DOWN(effect_params_t* params) {\n    return effect_runner_i(params, &CYCLE_UP_DOWN_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_CYCLE_UP_DOWN\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/digital_rain_anim.h",
    "content": "#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_RGB_MATRIX_DIGITAL_RAIN)\nRGB_MATRIX_EFFECT(DIGITAL_RAIN)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\n#        ifndef RGB_DIGITAL_RAIN_DROPS\n// lower the number for denser effect/wider keyboard\n#            define RGB_DIGITAL_RAIN_DROPS 24\n#        endif\n\nbool DIGITAL_RAIN(effect_params_t* params) {\n    // algorithm ported from https://github.com/tremby/Kaleidoscope-LEDEffect-DigitalRain\n    const uint8_t drop_ticks           = 28;\n    const uint8_t pure_green_intensity = (((uint16_t)rgb_matrix_config.hsv.v) * 3) >> 2;\n    const uint8_t max_brightness_boost = (((uint16_t)rgb_matrix_config.hsv.v) * 3) >> 2;\n    const uint8_t max_intensity        = rgb_matrix_config.hsv.v;\n    const uint8_t decay_ticks          = 0xff / max_intensity;\n\n    static uint8_t drop  = 0;\n    static uint8_t decay = 0;\n\n    if (params->init) {\n        rgb_matrix_set_color_all(0, 0, 0);\n        memset(g_rgb_frame_buffer, 0, sizeof(g_rgb_frame_buffer));\n        drop = 0;\n    }\n\n    decay++;\n    for (uint8_t col = 0; col < MATRIX_COLS; col++) {\n        for (uint8_t row = 0; row < MATRIX_ROWS; row++) {\n            if (row == 0 && drop == 0 && rand() < RAND_MAX / RGB_DIGITAL_RAIN_DROPS) {\n                // top row, pixels have just fallen and we're\n                // making a new rain drop in this column\n                g_rgb_frame_buffer[row][col] = max_intensity;\n            } else if (g_rgb_frame_buffer[row][col] > 0 && g_rgb_frame_buffer[row][col] < max_intensity) {\n                // neither fully bright nor dark, decay it\n                if (decay == decay_ticks) {\n                    g_rgb_frame_buffer[row][col]--;\n                }\n            }\n            // set the pixel colour\n            uint8_t led[LED_HITS_TO_REMEMBER];\n            uint8_t led_count = rgb_matrix_map_row_column_to_led(row, col, led);\n\n            // TODO: multiple leds are supported mapped to the same row/column\n            if (led_count > 0) {\n                if (g_rgb_frame_buffer[row][col] > pure_green_intensity) {\n                    const uint8_t boost = (uint8_t)((uint16_t)max_brightness_boost * (g_rgb_frame_buffer[row][col] - pure_green_intensity) / (max_intensity - pure_green_intensity));\n                    rgb_matrix_set_color(led[0], boost, max_intensity, boost);\n                } else {\n                    const uint8_t green = (uint8_t)((uint16_t)max_intensity * g_rgb_frame_buffer[row][col] / pure_green_intensity);\n                    rgb_matrix_set_color(led[0], 0, green, 0);\n                }\n            }\n        }\n    }\n    if (decay == decay_ticks) {\n        decay = 0;\n    }\n\n    if (++drop > drop_ticks) {\n        // reset drop timer\n        drop = 0;\n        for (uint8_t row = MATRIX_ROWS - 1; row > 0; row--) {\n            for (uint8_t col = 0; col < MATRIX_COLS; col++) {\n                // if ths is on the bottom row and bright allow decay\n                if (row == MATRIX_ROWS - 1 && g_rgb_frame_buffer[row][col] == max_intensity) {\n                    g_rgb_frame_buffer[row][col]--;\n                }\n                // check if the pixel above is bright\n                if (g_rgb_frame_buffer[row - 1][col] >= max_intensity) { // Note: can be larger than max_intensity if val was recently decreased\n                    // allow old bright pixel to decay\n                    g_rgb_frame_buffer[row - 1][col] = max_intensity - 1;\n                    // make this pixel bright\n                    g_rgb_frame_buffer[row][col] = max_intensity;\n                }\n            }\n        }\n    }\n    return false;\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && !defined(ENABLE_RGB_MATRIX_DIGITAL_RAIN)\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/dual_beacon_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_DUAL_BEACON\nRGB_MATRIX_EFFECT(DUAL_BEACON)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t DUAL_BEACON_math(hsv_t hsv, int8_t sin, int8_t cos, uint8_t i, uint8_t time) {\n    hsv.h += ((g_led_config.point[i].y - k_rgb_matrix_center.y) * cos + (g_led_config.point[i].x - k_rgb_matrix_center.x) * sin) / 128;\n    return hsv;\n}\n\nbool DUAL_BEACON(effect_params_t* params) {\n    return effect_runner_sin_cos_i(params, &DUAL_BEACON_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_DUAL_BEACON\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/flower_blooming_anim.h",
    "content": "/* Copyright 2023 HorrorTroll <https://github.com/HorrorTroll>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifdef ENABLE_RGB_MATRIX_FLOWER_BLOOMING\nRGB_MATRIX_EFFECT(FLOWER_BLOOMING)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\ntypedef hsv_t (*flower_blooming_f)(hsv_t hsv, uint8_t i, uint8_t time);\n\nbool effect_runner_bloom(effect_params_t* params, flower_blooming_f effect_func) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint8_t time = scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed / 10, 1));\n    for (uint8_t i = led_min; i < led_max; i++) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        if (g_led_config.point[i].y > k_rgb_matrix_center.y) {\n            rgb_t bgr = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, i, time));\n            rgb_matrix_set_color(i, bgr.b, bgr.g, bgr.r);\n        } else {\n            rgb_t rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, i, time));\n            rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n        }\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\nstatic hsv_t FLOWER_BLOOMING_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    if (g_led_config.point[i].y > k_rgb_matrix_center.y)\n        hsv.h = g_led_config.point[i].x * 3 - g_led_config.point[i].y * 3 + time;\n    else\n        hsv.h = g_led_config.point[i].x * 3 - g_led_config.point[i].y * 3 - time;\n    return hsv;\n}\n\nbool FLOWER_BLOOMING(effect_params_t* params) {\n    return effect_runner_bloom(params, &FLOWER_BLOOMING_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_FLOWER_BLOOMING\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/gradient_left_right_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT\nRGB_MATRIX_EFFECT(GRADIENT_LEFT_RIGHT)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nbool GRADIENT_LEFT_RIGHT(effect_params_t* params) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    hsv_t   hsv   = rgb_matrix_config.hsv;\n    uint8_t scale = scale8(64, rgb_matrix_config.speed);\n    for (uint8_t i = led_min; i < led_max; i++) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        // The x range will be 0..224, map this to 0..7\n        // Relies on hue being 8-bit and wrapping\n        hsv.h     = rgb_matrix_config.hsv.h + (scale * g_led_config.point[i].x >> 5);\n        rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);\n        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/gradient_up_down_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN\nRGB_MATRIX_EFFECT(GRADIENT_UP_DOWN)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nbool GRADIENT_UP_DOWN(effect_params_t* params) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    hsv_t   hsv   = rgb_matrix_config.hsv;\n    uint8_t scale = scale8(64, rgb_matrix_config.speed);\n    for (uint8_t i = led_min; i < led_max; i++) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        // The y range will be 0..64, map this to 0..4\n        // Relies on hue being 8-bit and wrapping\n        hsv.h     = rgb_matrix_config.hsv.h + scale * (g_led_config.point[i].y >> 4);\n        rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);\n        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/hue_breathing_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_HUE_BREATHING\nRGB_MATRIX_EFFECT(HUE_BREATHING)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\n// Hue Breathing - All LED's light up\nhsv_t HUE_BREATHING_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    // Adjust delta between 0-255 to change hue range\n    uint8_t delta = 12;\n    hsv.h         = hsv.h + scale8(abs8(sin8(time / 2) - 128) * 2, delta);\n    return hsv;\n}\n\nbool HUE_BREATHING(effect_params_t* params) {\n    return effect_runner_i(params, &HUE_BREATHING_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_HUE_BREATHING\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/hue_pendulum_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_HUE_PENDULUM\nRGB_MATRIX_EFFECT(HUE_PENDULUM)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\n// Change huedelta to adjust range of hue change. 0-255.\n// Looks better with a low value and slow speed for subtle change.\n// Hue Pendulum - color changes in a wave to the right before reversing direction\nstatic hsv_t HUE_PENDULUM_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    uint8_t huedelta = 12;\n    hsv.h            = hsv.h + scale8(abs8(sin8(time) + (g_led_config.point[i].x) - 128) * 2, huedelta);\n    return hsv;\n}\n\nbool HUE_PENDULUM(effect_params_t* params) {\n    return effect_runner_i(params, &HUE_PENDULUM_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // DISABLE_RGB_HUE_PENDULUM\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/hue_wave_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_HUE_WAVE\nRGB_MATRIX_EFFECT(HUE_WAVE)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\n// Change huedelta to adjust range of hue change. 0-255.\n// Looks better with a low value and slow speed for subtle change.\n// Hue Wave - color changes in a wave to the right\nstatic hsv_t HUE_WAVE_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    uint8_t huedelta = 24;\n    hsv.h            = hsv.h + scale8(abs8(g_led_config.point[i].x - time), huedelta);\n    return hsv;\n}\n\nbool HUE_WAVE(effect_params_t* params) {\n    return effect_runner_i(params, &HUE_WAVE_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // DISABLE_RGB_HUE_WAVE\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/jellybean_raindrops_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS\nRGB_MATRIX_EFFECT(JELLYBEAN_RAINDROPS)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic void jellybean_raindrops_set_color(uint8_t i, effect_params_t* params) {\n    if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return;\n\n    hsv_t hsv = {random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v};\n    rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);\n    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n}\n\nbool JELLYBEAN_RAINDROPS(effect_params_t* params) {\n    static uint16_t index = RGB_MATRIX_LED_COUNT + 1;\n\n    // Periodic trigger for LED change\n    if ((params->iter == 0) && (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 16)) % 5 == 0)) {\n        index = random8_max(RGB_MATRIX_LED_COUNT);\n    }\n\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n    if (params->init) {\n        for (uint8_t i = led_min; i < led_max; i++) {\n            jellybean_raindrops_set_color(i, params);\n        }\n    }\n    // Change LED once and set index out of range till next trigger\n    else if (led_min <= index && index < led_max) {\n        jellybean_raindrops_set_color(index, params);\n        index = RGB_MATRIX_LED_COUNT + 1;\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/pixel_flow_anim.h",
    "content": "// Copyright 2022 @filterpaper\n// SPDX-License-Identifier: GPL-2.0+\n\n#ifdef ENABLE_RGB_MATRIX_PIXEL_FLOW\nRGB_MATRIX_EFFECT(PIXEL_FLOW)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic bool PIXEL_FLOW(effect_params_t* params) {\n    // LED state array\n    static rgb_t led[RGB_MATRIX_LED_COUNT];\n\n    static uint32_t wait_timer = 0;\n    if (wait_timer > g_rgb_timer) {\n        return false;\n    }\n\n    inline uint32_t interval(void) {\n        return 3000 / scale16by8(qadd8(rgb_matrix_config.speed, 16), 16);\n    }\n\n    if (params->init) {\n        // Clear LEDs and fill the state array\n        rgb_matrix_set_color_all(0, 0, 0);\n        for (uint8_t j = 0; j < RGB_MATRIX_LED_COUNT; ++j) {\n            led[j] = (random8() & 2) ? (rgb_t){0, 0, 0} : rgb_matrix_hsv_to_rgb((hsv_t){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v});\n        }\n    }\n\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n    // Light LEDs based on state array\n    for (uint8_t i = led_min; i < led_max; ++i) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        rgb_matrix_set_color(i, led[i].r, led[i].g, led[i].b);\n    }\n\n    if (!rgb_matrix_check_finished_leds(led_max)) {\n        // Shift LED state forward\n        for (uint8_t j = 0; j < led_max - 1; ++j) {\n            led[j] = led[j + 1];\n        }\n        // Fill last LED\n        led[led_max - 1] = (random8() & 2) ? (rgb_t){0, 0, 0} : rgb_matrix_hsv_to_rgb((hsv_t){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v});\n        // Set pulse timer\n        wait_timer = g_rgb_timer + interval();\n    }\n\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_PIXEL_FLOW\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/pixel_fractal_anim.h",
    "content": "// Copyright (C) 2022 @filterpaper\n// SPDX-License-Identifier: GPL-2.0-or-later\n// Inspired by 4x12 fractal from @GEIGEIGEIST\n\n#ifdef ENABLE_RGB_MATRIX_PIXEL_FRACTAL\nRGB_MATRIX_EFFECT(PIXEL_FRACTAL)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic bool PIXEL_FRACTAL(effect_params_t* params) {\n#        if MATRIX_COLS < 2\n#            define MID_COL 1\n#        else\n#            define MID_COL MATRIX_COLS / 2\n#        endif\n    static bool     led[MATRIX_ROWS][MID_COL];\n    static uint32_t wait_timer = 0;\n\n    inline uint32_t interval(void) {\n        return 3000 / scale16by8(qadd8(rgb_matrix_config.speed, 16), 16);\n    }\n\n    if (params->init) {\n        rgb_matrix_set_color_all(0, 0, 0);\n    }\n\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    if (g_rgb_timer > wait_timer) {\n        rgb_t rgb = rgb_matrix_hsv_to_rgb(rgb_matrix_config.hsv);\n        for (uint8_t h = 0; h < MATRIX_ROWS; ++h) {\n            // Light and copy columns outward\n            for (uint8_t l = 0; l < MID_COL - 1; ++l) {\n                rgb_t index_rgb = led[h][l] ? (rgb_t){rgb.r, rgb.g, rgb.b} : (rgb_t){0, 0, 0};\n                if (HAS_ANY_FLAGS(g_led_config.flags[g_led_config.matrix_co[h][l]], params->flags)) {\n                    rgb_matrix_set_color(g_led_config.matrix_co[h][l], index_rgb.r, index_rgb.g, index_rgb.b);\n                }\n                if (HAS_ANY_FLAGS(g_led_config.flags[g_led_config.matrix_co[h][MATRIX_COLS - 1 - l]], params->flags)) {\n                    rgb_matrix_set_color(g_led_config.matrix_co[h][MATRIX_COLS - 1 - l], index_rgb.r, index_rgb.g, index_rgb.b);\n                }\n                led[h][l] = led[h][l + 1];\n            }\n\n            // Light both middle columns\n            rgb_t index_rgb = led[h][MID_COL - 1] ? (rgb_t){rgb.r, rgb.g, rgb.b} : (rgb_t){0, 0, 0};\n            if (HAS_ANY_FLAGS(g_led_config.flags[g_led_config.matrix_co[h][MID_COL - 1]], params->flags)) {\n                rgb_matrix_set_color(g_led_config.matrix_co[h][MID_COL - 1], index_rgb.r, index_rgb.g, index_rgb.b);\n            }\n            if (HAS_ANY_FLAGS(g_led_config.flags[g_led_config.matrix_co[h][MATRIX_COLS - MID_COL]], params->flags)) {\n                rgb_matrix_set_color(g_led_config.matrix_co[h][MATRIX_COLS - MID_COL], index_rgb.r, index_rgb.g, index_rgb.b);\n            }\n\n            // Generate new random fractal column\n            led[h][MID_COL - 1] = (random8() & 3) ? false : true;\n        }\n\n        wait_timer = g_rgb_timer + interval();\n    }\n\n    return rgb_matrix_check_finished_leds(led_max);\n}\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_PIXEL_FRACTAL\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/pixel_rain_anim.h",
    "content": "// Copyright 2022 @filterpaper\n// SPDX-License-Identifier: GPL-2.0+\n\n#ifdef ENABLE_RGB_MATRIX_PIXEL_RAIN\nRGB_MATRIX_EFFECT(PIXEL_RAIN)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic bool PIXEL_RAIN(effect_params_t* params) {\n    static fast_timer_t timer = 0;\n    static uint16_t     index = RGB_MATRIX_LED_COUNT + 1;\n\n    if ((params->iter == 0) && (timer_elapsed_fast(timer) > (320 - rgb_matrix_config.speed))) {\n        index = random8_max(RGB_MATRIX_LED_COUNT);\n        timer = timer_read_fast();\n    }\n\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n    if (led_min <= index && index < led_max && HAS_ANY_FLAGS(g_led_config.flags[index], params->flags)) {\n        hsv_t hsv = (random8() & 2) ? (hsv_t){0, 0, 0} : (hsv_t){random8(), random8_min_max(127, 255), rgb_matrix_config.hsv.v};\n        rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);\n        rgb_matrix_set_color(index, rgb.r, rgb.g, rgb.b);\n        index = RGB_MATRIX_LED_COUNT + 1;\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_PIXEL_RAIN\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/rainbow_beacon_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_RAINBOW_BEACON\nRGB_MATRIX_EFFECT(RAINBOW_BEACON)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t RAINBOW_BEACON_math(hsv_t hsv, int8_t sin, int8_t cos, uint8_t i, uint8_t time) {\n    hsv.h += ((g_led_config.point[i].y - k_rgb_matrix_center.y) * 2 * cos + (g_led_config.point[i].x - k_rgb_matrix_center.x) * 2 * sin) / 128;\n    return hsv;\n}\n\nbool RAINBOW_BEACON(effect_params_t* params) {\n    return effect_runner_sin_cos_i(params, &RAINBOW_BEACON_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_RAINBOW_BEACON\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/rainbow_moving_chevron_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON\nRGB_MATRIX_EFFECT(RAINBOW_MOVING_CHEVRON)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t RAINBOW_MOVING_CHEVRON_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    hsv.h += abs8(g_led_config.point[i].y - k_rgb_matrix_center.y) + (g_led_config.point[i].x - time);\n    return hsv;\n}\n\nbool RAINBOW_MOVING_CHEVRON(effect_params_t* params) {\n    return effect_runner_i(params, &RAINBOW_MOVING_CHEVRON_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/rainbow_pinwheels_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS\nRGB_MATRIX_EFFECT(RAINBOW_PINWHEELS)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t RAINBOW_PINWHEELS_math(hsv_t hsv, int8_t sin, int8_t cos, uint8_t i, uint8_t time) {\n    hsv.h += ((g_led_config.point[i].y - k_rgb_matrix_center.y) * 3 * cos + (56 - abs8(g_led_config.point[i].x - k_rgb_matrix_center.x)) * 3 * sin) / 128;\n    return hsv;\n}\n\nbool RAINBOW_PINWHEELS(effect_params_t* params) {\n    return effect_runner_sin_cos_i(params, &RAINBOW_PINWHEELS_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/raindrops_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_RAINDROPS\nRGB_MATRIX_EFFECT(RAINDROPS)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\nstatic void raindrops_set_color(int i, effect_params_t* params) {\n    if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return;\n    hsv_t hsv = {0, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v};\n\n    // Take the shortest path between hues\n    int16_t deltaH = ((rgb_matrix_config.hsv.h + 180) % 360 - rgb_matrix_config.hsv.h) / 4;\n    if (deltaH > 127) {\n        deltaH -= 256;\n    } else if (deltaH < -127) {\n        deltaH += 256;\n    }\n\n    hsv.h     = rgb_matrix_config.hsv.h + (deltaH * (random8() & 0x03));\n    rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);\n    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n}\n\nbool RAINDROPS(effect_params_t* params) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n    if (!params->init) {\n        // Change one LED every tick, make sure speed is not 0\n        if (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 16)) % 10 == 0) {\n            raindrops_set_color(random8_max(RGB_MATRIX_LED_COUNT), params);\n        }\n    } else {\n        for (int i = led_min; i < led_max; i++) {\n            raindrops_set_color(i, params);\n        }\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_RAINDROPS\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/rgb_matrix_effects.inc",
    "content": "// Add your new core rgb matrix effect here, order determines enum order\n#include \"solid_color_anim.h\"\n#include \"alpha_mods_anim.h\"\n#include \"gradient_up_down_anim.h\"\n#include \"gradient_left_right_anim.h\"\n#include \"breathing_anim.h\"\n#include \"colorband_sat_anim.h\"\n#include \"colorband_val_anim.h\"\n#include \"colorband_pinwheel_sat_anim.h\"\n#include \"colorband_pinwheel_val_anim.h\"\n#include \"colorband_spiral_sat_anim.h\"\n#include \"colorband_spiral_val_anim.h\"\n#include \"cycle_all_anim.h\"\n#include \"cycle_left_right_anim.h\"\n#include \"cycle_up_down_anim.h\"\n#include \"rainbow_moving_chevron_anim.h\"\n#include \"cycle_out_in_anim.h\"\n#include \"cycle_out_in_dual_anim.h\"\n#include \"cycle_pinwheel_anim.h\"\n#include \"cycle_spiral_anim.h\"\n#include \"dual_beacon_anim.h\"\n#include \"rainbow_beacon_anim.h\"\n#include \"rainbow_pinwheels_anim.h\"\n#include \"flower_blooming_anim.h\"\n#include \"raindrops_anim.h\"\n#include \"jellybean_raindrops_anim.h\"\n#include \"hue_breathing_anim.h\"\n#include \"hue_pendulum_anim.h\"\n#include \"hue_wave_anim.h\"\n#include \"pixel_rain_anim.h\"\n#include \"pixel_flow_anim.h\"\n#include \"pixel_fractal_anim.h\"\n#include \"typing_heatmap_anim.h\"\n#include \"digital_rain_anim.h\"\n#include \"solid_reactive_simple_anim.h\"\n#include \"solid_reactive_anim.h\"\n#include \"solid_reactive_wide.h\"\n#include \"solid_reactive_cross.h\"\n#include \"solid_reactive_nexus.h\"\n#include \"splash_anim.h\"\n#include \"solid_splash_anim.h\"\n#include \"starlight_smooth_anim.h\"\n#include \"starlight_anim.h\"\n#include \"starlight_dual_sat_anim.h\"\n#include \"starlight_dual_hue_anim.h\"\n#include \"riverflow_anim.h\"\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/riverflow_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_RIVERFLOW\nRGB_MATRIX_EFFECT(RIVERFLOW)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\n// inspired by @PleasureTek's Massdrop Alt LED animation\n\nhsv_t RIVERFLOW_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    time  = scale16by8(g_rgb_timer + (i * 315), rgb_matrix_config.speed / 8);\n    hsv.v = scale8(abs8(sin8(time) - 128) * 2, hsv.v);\n    return hsv;\n}\n\nbool RIVERFLOW(effect_params_t* params) {\n    return effect_runner_i(params, &RIVERFLOW_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_RIVERFLOW\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/runners/effect_runner_dx_dy.h",
    "content": "#pragma once\n\ntypedef hsv_t (*dx_dy_f)(hsv_t hsv, int16_t dx, int16_t dy, uint8_t time);\n\nbool effect_runner_dx_dy(effect_params_t* params, dx_dy_f effect_func) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint8_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 2);\n    for (uint8_t i = led_min; i < led_max; i++) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        int16_t dx  = g_led_config.point[i].x - k_rgb_matrix_center.x;\n        int16_t dy  = g_led_config.point[i].y - k_rgb_matrix_center.y;\n        rgb_t   rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, time));\n        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/runners/effect_runner_dx_dy_dist.h",
    "content": "#pragma once\n\ntypedef hsv_t (*dx_dy_dist_f)(hsv_t hsv, int16_t dx, int16_t dy, uint8_t dist, uint8_t time);\n\nbool effect_runner_dx_dy_dist(effect_params_t* params, dx_dy_dist_f effect_func) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint8_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 2);\n    for (uint8_t i = led_min; i < led_max; i++) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        int16_t dx   = g_led_config.point[i].x - k_rgb_matrix_center.x;\n        int16_t dy   = g_led_config.point[i].y - k_rgb_matrix_center.y;\n        uint8_t dist = sqrt16(dx * dx + dy * dy);\n        rgb_t   rgb  = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, dx, dy, dist, time));\n        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/runners/effect_runner_i.h",
    "content": "#pragma once\n\ntypedef hsv_t (*i_f)(hsv_t hsv, uint8_t i, uint8_t time);\n\nbool effect_runner_i(effect_params_t* params, i_f effect_func) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint8_t time = scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed / 4, 1));\n    for (uint8_t i = led_min; i < led_max; i++) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        rgb_t rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, i, time));\n        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/runners/effect_runner_reactive.h",
    "content": "#pragma once\n\n#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n\ntypedef hsv_t (*reactive_f)(hsv_t hsv, uint16_t offset);\n\nbool effect_runner_reactive(effect_params_t* params, reactive_f effect_func) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint16_t max_tick = 65535 / qadd8(rgb_matrix_config.speed, 1);\n    for (uint8_t i = led_min; i < led_max; i++) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        uint16_t tick = max_tick;\n        // Reverse search to find most recent key hit\n        for (int8_t j = g_last_hit_tracker.count - 1; j >= 0; j--) {\n            if (g_last_hit_tracker.index[j] == i && g_last_hit_tracker.tick[j] < tick) {\n                tick = g_last_hit_tracker.tick[j];\n                break;\n            }\n        }\n\n        uint16_t offset = scale16by8(tick, qadd8(rgb_matrix_config.speed, 1));\n        rgb_t    rgb    = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, offset));\n        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#endif // RGB_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/runners/effect_runner_reactive_splash.h",
    "content": "#pragma once\n\n#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n\ntypedef hsv_t (*reactive_splash_f)(hsv_t hsv, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick);\n\nbool effect_runner_reactive_splash(uint8_t start, effect_params_t* params, reactive_splash_f effect_func) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint8_t count = g_last_hit_tracker.count;\n    for (uint8_t i = led_min; i < led_max; i++) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        hsv_t hsv = rgb_matrix_config.hsv;\n        hsv.v     = 0;\n        for (uint8_t j = start; j < count; j++) {\n            int16_t  dx   = g_led_config.point[i].x - g_last_hit_tracker.x[j];\n            int16_t  dy   = g_led_config.point[i].y - g_last_hit_tracker.y[j];\n            uint8_t  dist = sqrt16(dx * dx + dy * dy);\n            uint16_t tick = scale16by8(g_last_hit_tracker.tick[j], qadd8(rgb_matrix_config.speed, 1));\n            hsv           = effect_func(hsv, dx, dy, dist, tick);\n        }\n        hsv.v     = scale8(hsv.v, rgb_matrix_config.hsv.v);\n        rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);\n        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#endif // RGB_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/runners/effect_runner_sin_cos_i.h",
    "content": "#pragma once\n\ntypedef hsv_t (*sin_cos_i_f)(hsv_t hsv, int8_t sin, int8_t cos, uint8_t i, uint8_t time);\n\nbool effect_runner_sin_cos_i(effect_params_t* params, sin_cos_i_f effect_func) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    uint16_t time      = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 4);\n    int8_t   cos_value = cos8(time) - 128;\n    int8_t   sin_value = sin8(time) - 128;\n    for (uint8_t i = led_min; i < led_max; i++) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        rgb_t rgb = rgb_matrix_hsv_to_rgb(effect_func(rgb_matrix_config.hsv, cos_value, sin_value, i, time));\n        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/runners/rgb_matrix_runners.inc",
    "content": "#include \"effect_runner_dx_dy_dist.h\"\n#include \"effect_runner_dx_dy.h\"\n#include \"effect_runner_i.h\"\n#include \"effect_runner_sin_cos_i.h\"\n#include \"effect_runner_reactive.h\"\n#include \"effect_runner_reactive_splash.h\"\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/solid_color_anim.h",
    "content": "RGB_MATRIX_EFFECT(SOLID_COLOR)\n#ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nbool SOLID_COLOR(effect_params_t* params) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    rgb_t rgb = rgb_matrix_hsv_to_rgb(rgb_matrix_config.hsv);\n    for (uint8_t i = led_min; i < led_max; i++) {\n        RGB_MATRIX_TEST_LED_FLAGS();\n        rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/solid_reactive_anim.h",
    "content": "#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n#    ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE\nRGB_MATRIX_EFFECT(SOLID_REACTIVE)\n#        ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t SOLID_REACTIVE_math(hsv_t hsv, uint16_t offset) {\n#            ifdef RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE\n    hsv.h = scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 8) >> 4);\n#            endif\n    hsv.h += scale8(255 - offset, 64);\n    return hsv;\n}\n\nbool SOLID_REACTIVE(effect_params_t* params) {\n    return effect_runner_reactive(params, &SOLID_REACTIVE_math);\n}\n\n#        endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // ENABLE_RGB_MATRIX_SOLID_REACTIVE\n#endif         // RGB_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/solid_reactive_cross.h",
    "content": "#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n#    if defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS) || defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS)\n\n#        ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS\nRGB_MATRIX_EFFECT(SOLID_REACTIVE_CROSS)\n#        endif\n\n#        ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS\nRGB_MATRIX_EFFECT(SOLID_REACTIVE_MULTICROSS)\n#        endif\n\n#        ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t SOLID_REACTIVE_CROSS_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick) {\n    uint16_t effect = tick + dist;\n    dx              = dx < 0 ? dx * -1 : dx;\n    dy              = dy < 0 ? dy * -1 : dy;\n    dx              = dx * 16 > 255 ? 255 : dx * 16;\n    dy              = dy * 16 > 255 ? 255 : dy * 16;\n    effect += dx > dy ? dy : dx;\n    if (effect > 255) effect = 255;\n#            ifdef RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE\n    hsv.h = scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 8) >> 4);\n#            endif\n    hsv.v = qadd8(hsv.v, 255 - effect);\n    return hsv;\n}\n\n#            ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS\nbool SOLID_REACTIVE_CROSS(effect_params_t* params) {\n    return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_CROSS_math);\n}\n#            endif\n\n#            ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS\nbool SOLID_REACTIVE_MULTICROSS(effect_params_t* params) {\n    return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_CROSS_math);\n}\n#            endif\n\n#        endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // !defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS) || defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS)\n#endif         // RGB_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/solid_reactive_nexus.h",
    "content": "#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n#    if defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS) || defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS)\n\n#        ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS\nRGB_MATRIX_EFFECT(SOLID_REACTIVE_NEXUS)\n#        endif\n\n#        ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS\nRGB_MATRIX_EFFECT(SOLID_REACTIVE_MULTINEXUS)\n#        endif\n\n#        ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t SOLID_REACTIVE_NEXUS_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick) {\n    uint16_t effect = tick - dist;\n    if (effect > 255) effect = 255;\n    if (dist > 72) effect = 255;\n    if ((dx > 8 || dx < -8) && (dy > 8 || dy < -8)) effect = 255;\n#            ifdef RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE\n    hsv.h = scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 8) >> 4) + dy / 4;\n#            else\n    hsv.h = rgb_matrix_config.hsv.h + dy / 4;\n#            endif\n    hsv.v = qadd8(hsv.v, 255 - effect);\n    return hsv;\n}\n\n#            ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS\nbool SOLID_REACTIVE_NEXUS(effect_params_t* params) {\n    return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_NEXUS_math);\n}\n#            endif\n\n#            ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS\nbool SOLID_REACTIVE_MULTINEXUS(effect_params_t* params) {\n    return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_NEXUS_math);\n}\n#            endif\n\n#        endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // !defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS) || !defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS)\n#endif         // RGB_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/solid_reactive_simple_anim.h",
    "content": "#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n#    ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE\nRGB_MATRIX_EFFECT(SOLID_REACTIVE_SIMPLE)\n#        ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t SOLID_REACTIVE_SIMPLE_math(hsv_t hsv, uint16_t offset) {\n#            ifdef RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE\n    hsv.h = scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 8) >> 4);\n#            endif\n    hsv.v = scale8(257 - offset, hsv.v);\n    return hsv;\n}\n\nbool SOLID_REACTIVE_SIMPLE(effect_params_t* params) {\n    return effect_runner_reactive(params, &SOLID_REACTIVE_SIMPLE_math);\n}\n\n#        endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE\n#endif         // RGB_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/solid_reactive_wide.h",
    "content": "#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n#    if defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE) || defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE)\n\n#        ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE\nRGB_MATRIX_EFFECT(SOLID_REACTIVE_WIDE)\n#        endif\n\n#        ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE\nRGB_MATRIX_EFFECT(SOLID_REACTIVE_MULTIWIDE)\n#        endif\n\n#        ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic hsv_t SOLID_REACTIVE_WIDE_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick) {\n    uint16_t effect = tick + dist * 5;\n    if (effect > 255) effect = 255;\n#            ifdef RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE\n    hsv.h = scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 8) >> 4);\n#            endif\n    hsv.v = qadd8(hsv.v, 255 - effect);\n    return hsv;\n}\n\n#            ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE\nbool SOLID_REACTIVE_WIDE(effect_params_t* params) {\n    return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_REACTIVE_WIDE_math);\n}\n#            endif\n\n#            ifdef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE\nbool SOLID_REACTIVE_MULTIWIDE(effect_params_t* params) {\n    return effect_runner_reactive_splash(0, params, &SOLID_REACTIVE_WIDE_math);\n}\n#            endif\n\n#        endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // !defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE) || !defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE)\n#endif         // RGB_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/solid_splash_anim.h",
    "content": "#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n#    if defined(ENABLE_RGB_MATRIX_SOLID_SPLASH) || defined(ENABLE_RGB_MATRIX_SOLID_MULTISPLASH)\n\n#        ifdef ENABLE_RGB_MATRIX_SOLID_SPLASH\nRGB_MATRIX_EFFECT(SOLID_SPLASH)\n#        endif\n\n#        ifdef ENABLE_RGB_MATRIX_SOLID_MULTISPLASH\nRGB_MATRIX_EFFECT(SOLID_MULTISPLASH)\n#        endif\n\n#        ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nhsv_t SOLID_SPLASH_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick) {\n    uint16_t effect = tick - dist;\n    if (effect > 255) effect = 255;\n    hsv.v = qadd8(hsv.v, 255 - effect);\n    return hsv;\n}\n\n#            ifdef ENABLE_RGB_MATRIX_SOLID_SPLASH\nbool SOLID_SPLASH(effect_params_t* params) {\n    return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SOLID_SPLASH_math);\n}\n#            endif\n\n#            ifdef ENABLE_RGB_MATRIX_SOLID_MULTISPLASH\nbool SOLID_MULTISPLASH(effect_params_t* params) {\n    return effect_runner_reactive_splash(0, params, &SOLID_SPLASH_math);\n}\n#            endif\n\n#        endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // !defined(ENABLE_RGB_MATRIX_SPLASH) && !defined(ENABLE_RGB_MATRIX_MULTISPLASH)\n#endif         // RGB_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/splash_anim.h",
    "content": "#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n#    if defined(ENABLE_RGB_MATRIX_SPLASH) || defined(ENABLE_RGB_MATRIX_MULTISPLASH)\n\n#        ifdef ENABLE_RGB_MATRIX_SPLASH\nRGB_MATRIX_EFFECT(SPLASH)\n#        endif\n\n#        ifdef ENABLE_RGB_MATRIX_MULTISPLASH\nRGB_MATRIX_EFFECT(MULTISPLASH)\n#        endif\n\n#        ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nhsv_t SPLASH_math(hsv_t hsv, int16_t dx, int16_t dy, uint8_t dist, uint16_t tick) {\n    uint16_t effect = tick - dist;\n    if (effect > 255) effect = 255;\n    hsv.h += effect;\n    hsv.v = qadd8(hsv.v, 255 - effect);\n    return hsv;\n}\n\n#            ifdef ENABLE_RGB_MATRIX_SPLASH\nbool SPLASH(effect_params_t* params) {\n    return effect_runner_reactive_splash(qsub8(g_last_hit_tracker.count, 1), params, &SPLASH_math);\n}\n#            endif\n\n#            ifdef ENABLE_RGB_MATRIX_MULTISPLASH\nbool MULTISPLASH(effect_params_t* params) {\n    return effect_runner_reactive_splash(0, params, &SPLASH_math);\n}\n#            endif\n\n#        endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#    endif     // !defined(ENABLE_RGB_MATRIX_SPLASH) || !defined(ENABLE_RGB_MATRIX_MULTISPLASH)\n#endif         // RGB_MATRIX_KEYREACTIVE_ENABLED\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/starlight_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_STARLIGHT\nRGB_MATRIX_EFFECT(STARLIGHT)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic void set_starlight_color(uint8_t i, effect_params_t* params) {\n    if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return;\n\n    uint16_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 8);\n    hsv_t    hsv  = rgb_matrix_config.hsv;\n    hsv.v         = scale8(abs8(sin8(time) - 128) * 2, hsv.v);\n    rgb_t rgb     = rgb_matrix_hsv_to_rgb(hsv);\n    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n}\n\nbool STARLIGHT(effect_params_t* params) {\n    static uint16_t index = RGB_MATRIX_LED_COUNT + 1;\n\n    // Periodic trigger for LED change\n    if ((params->iter == 0) && (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 5)) % 5 == 0)) {\n        index = random8_max(RGB_MATRIX_LED_COUNT);\n    }\n\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n    if (params->init) {\n        for (uint8_t i = led_min; i < led_max; i++) {\n            set_starlight_color(i, params);\n        }\n    }\n    // Change LED once and set index out of range till next trigger\n    else if (led_min <= index && index < led_max) {\n        set_starlight_color(index, params);\n        index = RGB_MATRIX_LED_COUNT + 1;\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_STARLIGHT\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/starlight_dual_hue_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_STARLIGHT_DUAL_HUE\nRGB_MATRIX_EFFECT(STARLIGHT_DUAL_HUE)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic void set_starlight_dual_hue_color(uint8_t i, effect_params_t* params) {\n    if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return;\n\n    uint16_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 8);\n    hsv_t    hsv  = rgb_matrix_config.hsv;\n    hsv.v         = scale8(abs8(sin8(time) - 128) * 2, hsv.v);\n    hsv.h         = hsv.h + random8_max(31);\n    rgb_t rgb     = rgb_matrix_hsv_to_rgb(hsv);\n    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n}\n\nbool STARLIGHT_DUAL_HUE(effect_params_t* params) {\n    static uint16_t index = RGB_MATRIX_LED_COUNT + 1;\n\n    // Periodic trigger for LED change\n    if ((params->iter == 0) && (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 5)) % 5 == 0)) {\n        index = random8_max(RGB_MATRIX_LED_COUNT);\n    }\n\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n    if (params->init) {\n        for (uint8_t i = led_min; i < led_max; i++) {\n            set_starlight_dual_hue_color(i, params);\n        }\n    }\n    // Change LED once and set index out of range till next trigger\n    else if (led_min <= index && index < led_max) {\n        set_starlight_dual_hue_color(index, params);\n        index = RGB_MATRIX_LED_COUNT + 1;\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_STARLIGHT_DUAL_HUE\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/starlight_dual_sat_anim.h",
    "content": "#ifdef ENABLE_RGB_MATRIX_STARLIGHT_DUAL_SAT\nRGB_MATRIX_EFFECT(STARLIGHT_DUAL_SAT)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic void set_starlight_dual_sat_color(uint8_t i, effect_params_t* params) {\n    if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) return;\n\n    uint16_t time = scale16by8(g_rgb_timer, rgb_matrix_config.speed / 8);\n    hsv_t    hsv  = rgb_matrix_config.hsv;\n    hsv.v         = scale8(abs8(sin8(time) - 128) * 2, hsv.v);\n    hsv.s         = hsv.s + random8_max(31);\n    rgb_t rgb     = rgb_matrix_hsv_to_rgb(hsv);\n    rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);\n}\n\nbool STARLIGHT_DUAL_SAT(effect_params_t* params) {\n    static uint16_t index = RGB_MATRIX_LED_COUNT + 1;\n\n    // Periodic trigger for LED change\n    if ((params->iter == 0) && (scale16by8(g_rgb_timer, qadd8(rgb_matrix_config.speed, 5)) % 5 == 0)) {\n        index = random8_max(RGB_MATRIX_LED_COUNT);\n    }\n\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n    if (params->init) {\n        for (uint8_t i = led_min; i < led_max; i++) {\n            set_starlight_dual_sat_color(i, params);\n        }\n    }\n    // Change LED once and set index out of range till next trigger\n    else if (led_min <= index && index < led_max) {\n        set_starlight_dual_sat_color(index, params);\n        index = RGB_MATRIX_LED_COUNT + 1;\n    }\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_STARLIGHT_DUAL_SAT\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/starlight_smooth_anim.h",
    "content": "// Copyright 2022 @art-was-here\n// SPDX-License-Identifier: GPL-2.0+\n\n#ifdef ENABLE_RGB_MATRIX_STARLIGHT_SMOOTH\nRGB_MATRIX_EFFECT(STARLIGHT_SMOOTH)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\nstatic uint8_t phase_offsets[RGB_MATRIX_LED_COUNT];\n\nhsv_t STARLIGHT_SMOOTH_math(hsv_t hsv, uint8_t i, uint8_t time) {\n    if (phase_offsets[i] == 0) {\n        phase_offsets[i] = rand() % 255;\n    }\n    hsv.v = scale8(abs8(sin8((time + phase_offsets[i]) / 2) - 128) * 2, hsv.v);\n    return hsv;\n}\n\nbool STARLIGHT_SMOOTH(effect_params_t* params) {\n    if (params->init) {\n        memset(phase_offsets, 0, sizeof(phase_offsets));\n    }\n    return effect_runner_i(params, &STARLIGHT_SMOOTH_math);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // ENABLE_RGB_MATRIX_STARLIGHT_SMOOTH\n"
  },
  {
    "path": "quantum/rgb_matrix/animations/typing_heatmap_anim.h",
    "content": "#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_RGB_MATRIX_TYPING_HEATMAP)\nRGB_MATRIX_EFFECT(TYPING_HEATMAP)\n#    ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#        ifndef RGB_MATRIX_TYPING_HEATMAP_INCREASE_STEP\n#            define RGB_MATRIX_TYPING_HEATMAP_INCREASE_STEP 32\n#        endif\n\n#        ifndef RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS\n#            define RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS 25\n#        endif\n\n#        ifndef RGB_MATRIX_TYPING_HEATMAP_SPREAD\n#            define RGB_MATRIX_TYPING_HEATMAP_SPREAD 40\n#        endif\n\n#        ifndef RGB_MATRIX_TYPING_HEATMAP_AREA_LIMIT\n#            define RGB_MATRIX_TYPING_HEATMAP_AREA_LIMIT 16\n#        endif\nvoid process_rgb_matrix_typing_heatmap(uint8_t row, uint8_t col) {\n#        ifdef RGB_MATRIX_TYPING_HEATMAP_SLIM\n    // Limit effect to pressed keys\n    g_rgb_frame_buffer[row][col] = qadd8(g_rgb_frame_buffer[row][col], RGB_MATRIX_TYPING_HEATMAP_INCREASE_STEP);\n#        else\n    if (g_led_config.matrix_co[row][col] == NO_LED) { // skip as pressed key doesn't have an led position\n        return;\n    }\n    for (uint8_t i_row = 0; i_row < MATRIX_ROWS; i_row++) {\n        for (uint8_t i_col = 0; i_col < MATRIX_COLS; i_col++) {\n            if (g_led_config.matrix_co[i_row][i_col] == NO_LED) { // skip as target key doesn't have an led position\n                continue;\n            }\n            if (i_row == row && i_col == col) {\n                g_rgb_frame_buffer[row][col] = qadd8(g_rgb_frame_buffer[row][col], RGB_MATRIX_TYPING_HEATMAP_INCREASE_STEP);\n            } else {\n#            define LED_DISTANCE(led_a, led_b) sqrt16(((int16_t)(led_a.x - led_b.x) * (int16_t)(led_a.x - led_b.x)) + ((int16_t)(led_a.y - led_b.y) * (int16_t)(led_a.y - led_b.y)))\n                uint8_t distance = LED_DISTANCE(g_led_config.point[g_led_config.matrix_co[row][col]], g_led_config.point[g_led_config.matrix_co[i_row][i_col]]);\n#            undef LED_DISTANCE\n                if (distance <= RGB_MATRIX_TYPING_HEATMAP_SPREAD) {\n                    uint8_t amount = qsub8(RGB_MATRIX_TYPING_HEATMAP_SPREAD, distance);\n                    if (amount > RGB_MATRIX_TYPING_HEATMAP_AREA_LIMIT) {\n                        amount = RGB_MATRIX_TYPING_HEATMAP_AREA_LIMIT;\n                    }\n                    g_rgb_frame_buffer[i_row][i_col] = qadd8(g_rgb_frame_buffer[i_row][i_col], amount);\n                }\n            }\n        }\n    }\n#        endif\n}\n\n// A timer to track the last time we decremented all heatmap values.\nstatic uint16_t heatmap_decrease_timer;\n// Whether we should decrement the heatmap values during the next update.\nstatic bool decrease_heatmap_values;\n\nbool TYPING_HEATMAP(effect_params_t* params) {\n    RGB_MATRIX_USE_LIMITS(led_min, led_max);\n\n    if (params->init) {\n        rgb_matrix_set_color_all(0, 0, 0);\n        memset(g_rgb_frame_buffer, 0, sizeof g_rgb_frame_buffer);\n    }\n\n    // The heatmap animation might run in several iterations depending on\n    // `RGB_MATRIX_LED_PROCESS_LIMIT`, therefore we only want to update the\n    // timer when the animation starts.\n    if (params->iter == 0) {\n        decrease_heatmap_values = timer_elapsed(heatmap_decrease_timer) >= RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS;\n\n        // Restart the timer if we are going to decrease the heatmap this frame.\n        if (decrease_heatmap_values) {\n            heatmap_decrease_timer = timer_read();\n        }\n    }\n\n    // Render heatmap & decrease\n    uint8_t count = 0;\n    for (uint8_t row = 0; row < MATRIX_ROWS && count < RGB_MATRIX_LED_PROCESS_LIMIT; row++) {\n        for (uint8_t col = 0; col < MATRIX_COLS && RGB_MATRIX_LED_PROCESS_LIMIT; col++) {\n            if (g_led_config.matrix_co[row][col] >= led_min && g_led_config.matrix_co[row][col] < led_max) {\n                count++;\n                uint8_t val = g_rgb_frame_buffer[row][col];\n                if (!HAS_ANY_FLAGS(g_led_config.flags[g_led_config.matrix_co[row][col]], params->flags)) continue;\n\n                hsv_t hsv = {170 - qsub8(val, 85), rgb_matrix_config.hsv.s, scale8((qadd8(170, val) - 170) * 3, rgb_matrix_config.hsv.v)};\n                rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);\n                rgb_matrix_set_color(g_led_config.matrix_co[row][col], rgb.r, rgb.g, rgb.b);\n\n                if (decrease_heatmap_values) {\n                    g_rgb_frame_buffer[row][col] = qsub8(val, 1);\n                }\n            }\n        }\n    }\n\n    return rgb_matrix_check_finished_leds(led_max);\n}\n\n#    endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#endif     // defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_RGB_MATRIX_TYPING_HEATMAP)\n"
  },
  {
    "path": "quantum/rgb_matrix/post_config.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n// clang-format off\n\n// framebuffer\n#if defined(ENABLE_RGB_MATRIX_TYPING_HEATMAP) || \\\n    defined(ENABLE_RGB_MATRIX_DIGITAL_RAIN)\n#    define RGB_MATRIX_FRAMEBUFFER_EFFECTS\n#endif\n\n// reactive\n#if defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE) || \\\n    defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE) || \\\n    defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE) || \\\n    defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE) || \\\n    defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS) || \\\n    defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS) || \\\n    defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS) || \\\n    defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS) || \\\n    defined(ENABLE_RGB_MATRIX_SPLASH) || \\\n    defined(ENABLE_RGB_MATRIX_MULTISPLASH) || \\\n    defined(ENABLE_RGB_MATRIX_SOLID_SPLASH) || \\\n    defined(ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS) || \\\n    defined(ENABLE_RGB_MATRIX_SOLID_MULTISPLASH)\n#    define RGB_MATRIX_KEYPRESSES\n#endif\n"
  },
  {
    "path": "quantum/rgb_matrix/rgb_matrix.c",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2017 Jack Humbert\n * Copyright 2018 Yiancar\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"rgb_matrix.h\"\n#include \"progmem.h\"\n#include \"eeconfig.h\"\n#include \"keyboard.h\"\n#include \"sync_timer.h\"\n#include \"debug.h\"\n#include <string.h>\n#include <math.h>\n#include <stdlib.h>\n\n#include <lib/lib8tion/lib8tion.h>\n\n#ifndef RGB_MATRIX_CENTER\nconst led_point_t k_rgb_matrix_center = {112, 32};\n#else\nconst led_point_t k_rgb_matrix_center = RGB_MATRIX_CENTER;\n#endif\n\n__attribute__((weak)) rgb_t rgb_matrix_hsv_to_rgb(hsv_t hsv) {\n    return hsv_to_rgb(hsv);\n}\n\n// Generic effect runners\n#include \"rgb_matrix_runners.inc\"\n\n// ------------------------------------------\n// -----Begin rgb effect includes macros-----\n#define RGB_MATRIX_EFFECT(name)\n#define RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n\n#include \"rgb_matrix_effects.inc\"\n#ifdef COMMUNITY_MODULES_ENABLE\n#    include \"rgb_matrix_community_modules.inc\"\n#endif\n#ifdef RGB_MATRIX_CUSTOM_KB\n#    include \"rgb_matrix_kb.inc\"\n#endif\n#ifdef RGB_MATRIX_CUSTOM_USER\n#    include \"rgb_matrix_user.inc\"\n#endif\n\n#undef RGB_MATRIX_CUSTOM_EFFECT_IMPLS\n#undef RGB_MATRIX_EFFECT\n// -----End rgb effect includes macros-------\n// ------------------------------------------\n\n// globals\nrgb_config_t rgb_matrix_config; // TODO: would like to prefix this with g_ for global consistancy, do this in another pr\nuint32_t     g_rgb_timer;\n#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS\nuint8_t g_rgb_frame_buffer[MATRIX_ROWS][MATRIX_COLS] = {{0}};\n#endif // RGB_MATRIX_FRAMEBUFFER_EFFECTS\n#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\nlast_hit_t g_last_hit_tracker;\n#endif // RGB_MATRIX_KEYREACTIVE_ENABLED\n\n// internals\nstatic bool            suspend_state     = false;\nstatic uint8_t         rgb_last_enable   = UINT8_MAX;\nstatic uint8_t         rgb_last_effect   = UINT8_MAX;\nstatic effect_params_t rgb_effect_params = {0, LED_FLAG_ALL, false};\nstatic rgb_task_states rgb_task_state    = SYNCING;\n\n// double buffers\nstatic uint32_t rgb_timer_buffer;\n#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\nstatic last_hit_t last_hit_buffer;\n#endif // RGB_MATRIX_KEYREACTIVE_ENABLED\n\n// split rgb matrix\n#if defined(RGB_MATRIX_SPLIT)\nconst uint8_t k_rgb_matrix_split[2] = RGB_MATRIX_SPLIT;\n#endif\n\nEECONFIG_DEBOUNCE_HELPER(rgb_matrix, rgb_matrix_config);\n\nvoid eeconfig_force_flush_rgb_matrix(void) {\n    eeconfig_flush_rgb_matrix(true);\n}\n\nvoid eeconfig_update_rgb_matrix_default(void) {\n    dprintf(\"eeconfig_update_rgb_matrix_default\\n\");\n    rgb_matrix_config.enable = RGB_MATRIX_DEFAULT_ON;\n    rgb_matrix_config.mode   = RGB_MATRIX_DEFAULT_MODE;\n    rgb_matrix_config.hsv    = (hsv_t){RGB_MATRIX_DEFAULT_HUE, RGB_MATRIX_DEFAULT_SAT, RGB_MATRIX_DEFAULT_VAL};\n    rgb_matrix_config.speed  = RGB_MATRIX_DEFAULT_SPD;\n    rgb_matrix_config.flags  = RGB_MATRIX_DEFAULT_FLAGS;\n    eeconfig_flush_rgb_matrix(true);\n}\n\nvoid eeconfig_debug_rgb_matrix(void) {\n    dprintf(\"rgb_matrix_config EEPROM\\n\");\n    dprintf(\"rgb_matrix_config.enable = %d\\n\", rgb_matrix_config.enable);\n    dprintf(\"rgb_matrix_config.mode = %d\\n\", rgb_matrix_config.mode);\n    dprintf(\"rgb_matrix_config.hsv.h = %d\\n\", rgb_matrix_config.hsv.h);\n    dprintf(\"rgb_matrix_config.hsv.s = %d\\n\", rgb_matrix_config.hsv.s);\n    dprintf(\"rgb_matrix_config.hsv.v = %d\\n\", rgb_matrix_config.hsv.v);\n    dprintf(\"rgb_matrix_config.speed = %d\\n\", rgb_matrix_config.speed);\n    dprintf(\"rgb_matrix_config.flags = %d\\n\", rgb_matrix_config.flags);\n}\n\nvoid rgb_matrix_reload_from_eeprom(void) {\n    rgb_matrix_disable_noeeprom();\n    /* Reset back to what we have in eeprom */\n    eeconfig_init_rgb_matrix();\n    eeconfig_debug_rgb_matrix(); // display current eeprom values\n    if (rgb_matrix_config.enable) {\n        rgb_matrix_mode_noeeprom(rgb_matrix_config.mode);\n    }\n}\n\n__attribute__((weak)) uint8_t rgb_matrix_map_row_column_to_led_kb(uint8_t row, uint8_t column, uint8_t *led_i) {\n    return 0;\n}\n\nuint8_t rgb_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i) {\n    uint8_t led_count = rgb_matrix_map_row_column_to_led_kb(row, column, led_i);\n    uint8_t led_index = g_led_config.matrix_co[row][column];\n    if (led_index != NO_LED) {\n        led_i[led_count] = led_index;\n        led_count++;\n    }\n    return led_count;\n}\n\nvoid rgb_matrix_update_pwm_buffers(void) {\n    rgb_matrix_driver.flush();\n}\n\n__attribute__((weak)) int rgb_matrix_led_index(int index) {\n#if defined(RGB_MATRIX_SPLIT)\n    if (!is_keyboard_left() && index >= k_rgb_matrix_split[0]) {\n        return index - k_rgb_matrix_split[0];\n    }\n#endif\n    return index;\n}\n\nvoid rgb_matrix_set_color(int index, uint8_t red, uint8_t green, uint8_t blue) {\n    rgb_matrix_driver.set_color(rgb_matrix_led_index(index), red, green, blue);\n}\n\nvoid rgb_matrix_set_color_all(uint8_t red, uint8_t green, uint8_t blue) {\n#if defined(RGB_MATRIX_SPLIT)\n    for (uint8_t i = 0; i < RGB_MATRIX_LED_COUNT; i++)\n        rgb_matrix_set_color(i, red, green, blue);\n#else\n    rgb_matrix_driver.set_color_all(red, green, blue);\n#endif\n}\n\nvoid rgb_matrix_handle_key_event(uint8_t row, uint8_t col, bool pressed) {\n#ifndef RGB_MATRIX_SPLIT\n    if (!is_keyboard_master()) return;\n#endif\n\n#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n    uint8_t led[LED_HITS_TO_REMEMBER];\n    uint8_t led_count = 0;\n\n#    if defined(RGB_MATRIX_KEYRELEASES)\n    if (!pressed)\n#    elif defined(RGB_MATRIX_KEYPRESSES)\n    if (pressed)\n#    endif // defined(RGB_MATRIX_KEYRELEASES)\n    {\n        led_count = rgb_matrix_map_row_column_to_led(row, col, led);\n    }\n\n    if (last_hit_buffer.count + led_count > LED_HITS_TO_REMEMBER) {\n        memcpy(&last_hit_buffer.x[0], &last_hit_buffer.x[led_count], LED_HITS_TO_REMEMBER - led_count);\n        memcpy(&last_hit_buffer.y[0], &last_hit_buffer.y[led_count], LED_HITS_TO_REMEMBER - led_count);\n        memcpy(&last_hit_buffer.tick[0], &last_hit_buffer.tick[led_count], (LED_HITS_TO_REMEMBER - led_count) * 2); // 16 bit\n        memcpy(&last_hit_buffer.index[0], &last_hit_buffer.index[led_count], LED_HITS_TO_REMEMBER - led_count);\n        last_hit_buffer.count = LED_HITS_TO_REMEMBER - led_count;\n    }\n\n    for (uint8_t i = 0; i < led_count; i++) {\n        uint8_t index                = last_hit_buffer.count;\n        last_hit_buffer.x[index]     = g_led_config.point[led[i]].x;\n        last_hit_buffer.y[index]     = g_led_config.point[led[i]].y;\n        last_hit_buffer.index[index] = led[i];\n        last_hit_buffer.tick[index]  = 0;\n        last_hit_buffer.count++;\n    }\n#endif // RGB_MATRIX_KEYREACTIVE_ENABLED\n\n#if defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_RGB_MATRIX_TYPING_HEATMAP)\n#    if defined(RGB_MATRIX_KEYRELEASES)\n    if (!pressed)\n#    else\n    if (pressed)\n#    endif // defined(RGB_MATRIX_KEYRELEASES)\n    {\n        if (rgb_matrix_config.mode == RGB_MATRIX_TYPING_HEATMAP) {\n            process_rgb_matrix_typing_heatmap(row, col);\n        }\n    }\n#endif // defined(RGB_MATRIX_FRAMEBUFFER_EFFECTS) && defined(ENABLE_RGB_MATRIX_TYPING_HEATMAP)\n}\n\nvoid rgb_matrix_test(void) {\n    // Mask out bits 4 and 5\n    // Increase the factor to make the test animation slower (and reduce to make it faster)\n    uint8_t factor = 10;\n    switch ((g_rgb_timer & (0b11 << factor)) >> factor) {\n        case 0: {\n            rgb_matrix_set_color_all(20, 0, 0);\n            break;\n        }\n        case 1: {\n            rgb_matrix_set_color_all(0, 20, 0);\n            break;\n        }\n        case 2: {\n            rgb_matrix_set_color_all(0, 0, 20);\n            break;\n        }\n        case 3: {\n            rgb_matrix_set_color_all(20, 20, 20);\n            break;\n        }\n    }\n}\n\nstatic bool rgb_matrix_none(effect_params_t *params) {\n    if (!params->init) {\n        return false;\n    }\n\n    rgb_matrix_set_color_all(0, 0, 0);\n    return false;\n}\n\nstatic void rgb_task_timers(void) {\n#if defined(RGB_MATRIX_KEYREACTIVE_ENABLED)\n    uint32_t deltaTime = sync_timer_elapsed32(rgb_timer_buffer);\n#endif // defined(RGB_MATRIX_KEYREACTIVE_ENABLED)\n    rgb_timer_buffer = sync_timer_read32();\n\n    // Update double buffer last hit timers\n#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n    uint8_t count = last_hit_buffer.count;\n    for (uint8_t i = 0; i < count; ++i) {\n        if (UINT16_MAX - deltaTime < last_hit_buffer.tick[i]) {\n            last_hit_buffer.count--;\n            continue;\n        }\n        last_hit_buffer.tick[i] += deltaTime;\n    }\n#endif // RGB_MATRIX_KEYREACTIVE_ENABLED\n}\n\nstatic void rgb_task_sync(void) {\n    eeconfig_flush_rgb_matrix(false);\n    // next task\n    if (sync_timer_elapsed32(g_rgb_timer) >= RGB_MATRIX_LED_FLUSH_LIMIT) rgb_task_state = STARTING;\n}\n\nstatic void rgb_task_start(void) {\n    // reset iter\n    rgb_effect_params.iter = 0;\n\n    // update double buffers\n    g_rgb_timer = rgb_timer_buffer;\n#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n    g_last_hit_tracker = last_hit_buffer;\n#endif // RGB_MATRIX_KEYREACTIVE_ENABLED\n\n    // next task\n    rgb_task_state = RENDERING;\n}\n\nstatic void rgb_task_render(uint8_t effect) {\n    bool rendering         = false;\n    rgb_effect_params.init = (effect != rgb_last_effect) || (rgb_matrix_config.enable != rgb_last_enable);\n    if (rgb_effect_params.flags != rgb_matrix_config.flags) {\n        rgb_effect_params.flags = rgb_matrix_config.flags;\n        rgb_matrix_set_color_all(0, 0, 0);\n    }\n\n    // each effect can opt to do calculations\n    // and/or request PWM buffer updates.\n    switch (effect) {\n        case RGB_MATRIX_NONE:\n            rendering = rgb_matrix_none(&rgb_effect_params);\n            break;\n\n// ---------------------------------------------\n// -----Begin rgb effect switch case macros-----\n#define RGB_MATRIX_EFFECT(name, ...)          \\\n    case RGB_MATRIX_##name:                   \\\n        rendering = name(&rgb_effect_params); \\\n        break;\n#include \"rgb_matrix_effects.inc\"\n#undef RGB_MATRIX_EFFECT\n\n#ifdef COMMUNITY_MODULES_ENABLE\n#    define RGB_MATRIX_EFFECT(name, ...)          \\\n        case RGB_MATRIX_COMMUNITY_MODULE_##name:  \\\n            rendering = name(&rgb_effect_params); \\\n            break;\n#    include \"rgb_matrix_community_modules.inc\"\n#    undef RGB_MATRIX_EFFECT\n#endif\n\n#if defined(RGB_MATRIX_CUSTOM_KB) || defined(RGB_MATRIX_CUSTOM_USER)\n#    define RGB_MATRIX_EFFECT(name, ...)          \\\n        case RGB_MATRIX_CUSTOM_##name:            \\\n            rendering = name(&rgb_effect_params); \\\n            break;\n#    ifdef RGB_MATRIX_CUSTOM_KB\n#        include \"rgb_matrix_kb.inc\"\n#    endif\n#    ifdef RGB_MATRIX_CUSTOM_USER\n#        include \"rgb_matrix_user.inc\"\n#    endif\n#    undef RGB_MATRIX_EFFECT\n#endif\n            // -----End rgb effect switch case macros-------\n            // ---------------------------------------------\n\n        // Factory default magic value\n        case UINT8_MAX: {\n            rgb_matrix_test();\n            rgb_task_state = FLUSHING;\n        }\n            return;\n    }\n\n    rgb_effect_params.iter++;\n\n    // next task\n    if (!rendering) {\n        rgb_task_state = FLUSHING;\n        if (!rgb_effect_params.init && effect == RGB_MATRIX_NONE) {\n            // We only need to flush once if we are RGB_MATRIX_NONE\n            rgb_task_state = SYNCING;\n        }\n    }\n}\n\nstatic void rgb_task_flush(uint8_t effect) {\n    // update last trackers after the first full render so we can init over several frames\n    rgb_last_effect = effect;\n    rgb_last_enable = rgb_matrix_config.enable;\n\n    // update pwm buffers\n    rgb_matrix_update_pwm_buffers();\n\n    // next task\n    rgb_task_state = SYNCING;\n}\n\nvoid rgb_matrix_task(void) {\n    rgb_task_timers();\n\n    // Ideally we would also stop sending zeros to the LED driver PWM buffers\n    // while suspended and just do a software shutdown. This is a cheap hack for now.\n    bool suspend_backlight = suspend_state ||\n#if RGB_MATRIX_TIMEOUT > 0\n                             (last_input_activity_elapsed() > (uint32_t)RGB_MATRIX_TIMEOUT) ||\n#endif // RGB_MATRIX_TIMEOUT > 0\n                             false;\n\n    uint8_t effect = suspend_backlight || !rgb_matrix_config.enable ? 0 : rgb_matrix_config.mode;\n\n    switch (rgb_task_state) {\n        case STARTING:\n            rgb_task_start();\n            break;\n        case RENDERING:\n            rgb_task_render(effect);\n            if (effect) {\n                if (rgb_task_state == FLUSHING) { // ensure we only draw basic indicators once rendering is finished\n                    rgb_matrix_indicators();\n                }\n                rgb_matrix_indicators_advanced(&rgb_effect_params);\n            }\n            break;\n        case FLUSHING:\n            rgb_task_flush(effect);\n            break;\n        case SYNCING:\n            rgb_task_sync();\n            break;\n    }\n}\n\n__attribute__((weak)) bool rgb_matrix_indicators_modules(void) {\n    return true;\n}\n\nvoid rgb_matrix_indicators(void) {\n    rgb_matrix_indicators_modules();\n    rgb_matrix_indicators_kb();\n}\n\n__attribute__((weak)) bool rgb_matrix_indicators_kb(void) {\n    return rgb_matrix_indicators_user();\n}\n\n__attribute__((weak)) bool rgb_matrix_indicators_user(void) {\n    return true;\n}\n\nstruct rgb_matrix_limits_t rgb_matrix_get_limits(uint8_t iter) {\n    struct rgb_matrix_limits_t limits = {0};\n#if defined(RGB_MATRIX_LED_PROCESS_LIMIT) && RGB_MATRIX_LED_PROCESS_LIMIT > 0 && RGB_MATRIX_LED_PROCESS_LIMIT < RGB_MATRIX_LED_COUNT\n#    if defined(RGB_MATRIX_SPLIT)\n    limits.led_min_index = RGB_MATRIX_LED_PROCESS_LIMIT * (iter);\n    limits.led_max_index = limits.led_min_index + RGB_MATRIX_LED_PROCESS_LIMIT;\n    if (limits.led_max_index > RGB_MATRIX_LED_COUNT) limits.led_max_index = RGB_MATRIX_LED_COUNT;\n    if (is_keyboard_left() && (limits.led_max_index > k_rgb_matrix_split[0])) limits.led_max_index = k_rgb_matrix_split[0];\n    if (!(is_keyboard_left()) && (limits.led_min_index < k_rgb_matrix_split[0])) limits.led_min_index = k_rgb_matrix_split[0];\n#    else\n    limits.led_min_index = RGB_MATRIX_LED_PROCESS_LIMIT * (iter);\n    limits.led_max_index = limits.led_min_index + RGB_MATRIX_LED_PROCESS_LIMIT;\n    if (limits.led_max_index > RGB_MATRIX_LED_COUNT) limits.led_max_index = RGB_MATRIX_LED_COUNT;\n#    endif\n#else\n#    if defined(RGB_MATRIX_SPLIT)\n    limits.led_min_index = 0;\n    limits.led_max_index = RGB_MATRIX_LED_COUNT;\n    if (is_keyboard_left() && (limits.led_max_index > k_rgb_matrix_split[0])) limits.led_max_index = k_rgb_matrix_split[0];\n    if (!(is_keyboard_left()) && (limits.led_min_index < k_rgb_matrix_split[0])) limits.led_min_index = k_rgb_matrix_split[0];\n#    else\n    limits.led_min_index = 0;\n    limits.led_max_index = RGB_MATRIX_LED_COUNT;\n#    endif\n#endif\n    return limits;\n}\n\n__attribute__((weak)) bool rgb_matrix_indicators_advanced_modules(uint8_t led_min, uint8_t led_max) {\n    return true;\n}\n\nvoid rgb_matrix_indicators_advanced(effect_params_t *params) {\n    /* special handling is needed for \"params->iter\", since it's already been incremented.\n     * Could move the invocations to rgb_task_render, but then it's missing a few checks\n     * and not sure which would be better. Otherwise, this should be called from\n     * rgb_task_render, right before the iter++ line.\n     */\n    RGB_MATRIX_USE_LIMITS_ITER(min, max, params->iter - 1);\n    rgb_matrix_indicators_advanced_modules(min, max);\n    rgb_matrix_indicators_advanced_kb(min, max);\n}\n\n__attribute__((weak)) bool rgb_matrix_indicators_advanced_kb(uint8_t led_min, uint8_t led_max) {\n    return rgb_matrix_indicators_advanced_user(led_min, led_max);\n}\n\n__attribute__((weak)) bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {\n    return true;\n}\n\nvoid rgb_matrix_init(void) {\n    rgb_matrix_driver.init();\n\n#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\n    g_last_hit_tracker.count = 0;\n    for (uint8_t i = 0; i < LED_HITS_TO_REMEMBER; ++i) {\n        g_last_hit_tracker.tick[i] = UINT16_MAX;\n    }\n\n    last_hit_buffer.count = 0;\n    for (uint8_t i = 0; i < LED_HITS_TO_REMEMBER; ++i) {\n        last_hit_buffer.tick[i] = UINT16_MAX;\n    }\n#endif // RGB_MATRIX_KEYREACTIVE_ENABLED\n\n    eeconfig_init_rgb_matrix();\n    if (!rgb_matrix_config.mode) {\n        dprintf(\"rgb_matrix_init_drivers rgb_matrix_config.mode = 0. Write default values to EEPROM.\\n\");\n        eeconfig_update_rgb_matrix_default();\n    }\n    eeconfig_debug_rgb_matrix(); // display current eeprom values\n}\n\nvoid rgb_matrix_set_suspend_state(bool state) {\n#ifdef RGB_MATRIX_SLEEP\n    if (state && !suspend_state) { // only run if turning off, and only once\n        rgb_task_render(0);        // turn off all LEDs when suspending\n        rgb_task_flush(0);         // and actually flash led state to LEDs\n    }\n    suspend_state = state;\n#endif\n}\n\nbool rgb_matrix_get_suspend_state(void) {\n    return suspend_state;\n}\n\nvoid rgb_matrix_toggle_eeprom_helper(bool write_to_eeprom) {\n    rgb_matrix_config.enable ^= 1;\n    rgb_task_state = STARTING;\n    eeconfig_flag_rgb_matrix(write_to_eeprom);\n    dprintf(\"rgb matrix toggle [%s]: rgb_matrix_config.enable = %u\\n\", (write_to_eeprom) ? \"EEPROM\" : \"NOEEPROM\", rgb_matrix_config.enable);\n}\nvoid rgb_matrix_toggle_noeeprom(void) {\n    rgb_matrix_toggle_eeprom_helper(false);\n}\nvoid rgb_matrix_toggle(void) {\n    rgb_matrix_toggle_eeprom_helper(true);\n}\n\nvoid rgb_matrix_enable(void) {\n    rgb_matrix_enable_noeeprom();\n    eeconfig_flag_rgb_matrix(true);\n}\n\nvoid rgb_matrix_enable_noeeprom(void) {\n    if (!rgb_matrix_config.enable) rgb_task_state = STARTING;\n    rgb_matrix_config.enable = 1;\n}\n\nvoid rgb_matrix_disable(void) {\n    rgb_matrix_disable_noeeprom();\n    eeconfig_flag_rgb_matrix(true);\n}\n\nvoid rgb_matrix_disable_noeeprom(void) {\n    if (rgb_matrix_config.enable) rgb_task_state = STARTING;\n    rgb_matrix_config.enable = 0;\n}\n\nuint8_t rgb_matrix_is_enabled(void) {\n    return rgb_matrix_config.enable;\n}\n\nvoid rgb_matrix_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {\n    if (!rgb_matrix_config.enable) {\n        return;\n    }\n    if (mode < 1) {\n        rgb_matrix_config.mode = 1;\n    } else if (mode >= RGB_MATRIX_EFFECT_MAX) {\n        rgb_matrix_config.mode = RGB_MATRIX_EFFECT_MAX - 1;\n    } else {\n        rgb_matrix_config.mode = mode;\n    }\n    rgb_task_state = STARTING;\n    eeconfig_flag_rgb_matrix(write_to_eeprom);\n    dprintf(\"rgb matrix mode [%s]: %u\\n\", (write_to_eeprom) ? \"EEPROM\" : \"NOEEPROM\", rgb_matrix_config.mode);\n}\nvoid rgb_matrix_mode_noeeprom(uint8_t mode) {\n    rgb_matrix_mode_eeprom_helper(mode, false);\n}\nvoid rgb_matrix_mode(uint8_t mode) {\n    rgb_matrix_mode_eeprom_helper(mode, true);\n}\n\nuint8_t rgb_matrix_get_mode(void) {\n    return rgb_matrix_config.mode;\n}\n\nvoid rgb_matrix_step_helper(bool write_to_eeprom) {\n    uint8_t mode = rgb_matrix_config.mode + 1;\n    rgb_matrix_mode_eeprom_helper((mode < RGB_MATRIX_EFFECT_MAX) ? mode : 1, write_to_eeprom);\n}\nvoid rgb_matrix_step_noeeprom(void) {\n    rgb_matrix_step_helper(false);\n}\nvoid rgb_matrix_step(void) {\n    rgb_matrix_step_helper(true);\n}\n\nvoid rgb_matrix_step_reverse_helper(bool write_to_eeprom) {\n    uint8_t mode = rgb_matrix_config.mode - 1;\n    rgb_matrix_mode_eeprom_helper((mode < 1) ? RGB_MATRIX_EFFECT_MAX - 1 : mode, write_to_eeprom);\n}\nvoid rgb_matrix_step_reverse_noeeprom(void) {\n    rgb_matrix_step_reverse_helper(false);\n}\nvoid rgb_matrix_step_reverse(void) {\n    rgb_matrix_step_reverse_helper(true);\n}\n\nvoid rgb_matrix_sethsv_eeprom_helper(uint16_t hue, uint8_t sat, uint8_t val, bool write_to_eeprom) {\n    if (!rgb_matrix_config.enable) {\n        return;\n    }\n    rgb_matrix_config.hsv.h = hue;\n    rgb_matrix_config.hsv.s = sat;\n    rgb_matrix_config.hsv.v = (val > RGB_MATRIX_MAXIMUM_BRIGHTNESS) ? RGB_MATRIX_MAXIMUM_BRIGHTNESS : val;\n    eeconfig_flag_rgb_matrix(write_to_eeprom);\n    dprintf(\"rgb matrix set hsv [%s]: %u,%u,%u\\n\", (write_to_eeprom) ? \"EEPROM\" : \"NOEEPROM\", rgb_matrix_config.hsv.h, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v);\n}\nvoid rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) {\n    rgb_matrix_sethsv_eeprom_helper(hue, sat, val, false);\n}\nvoid rgb_matrix_sethsv(uint16_t hue, uint8_t sat, uint8_t val) {\n    rgb_matrix_sethsv_eeprom_helper(hue, sat, val, true);\n}\n\nhsv_t rgb_matrix_get_hsv(void) {\n    return rgb_matrix_config.hsv;\n}\nuint8_t rgb_matrix_get_hue(void) {\n    return rgb_matrix_config.hsv.h;\n}\nuint8_t rgb_matrix_get_sat(void) {\n    return rgb_matrix_config.hsv.s;\n}\nuint8_t rgb_matrix_get_val(void) {\n    return rgb_matrix_config.hsv.v;\n}\n\nvoid rgb_matrix_increase_hue_helper(bool write_to_eeprom) {\n    rgb_matrix_sethsv_eeprom_helper(rgb_matrix_config.hsv.h + RGB_MATRIX_HUE_STEP, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v, write_to_eeprom);\n}\nvoid rgb_matrix_increase_hue_noeeprom(void) {\n    rgb_matrix_increase_hue_helper(false);\n}\nvoid rgb_matrix_increase_hue(void) {\n    rgb_matrix_increase_hue_helper(true);\n}\n\nvoid rgb_matrix_decrease_hue_helper(bool write_to_eeprom) {\n    rgb_matrix_sethsv_eeprom_helper(rgb_matrix_config.hsv.h - RGB_MATRIX_HUE_STEP, rgb_matrix_config.hsv.s, rgb_matrix_config.hsv.v, write_to_eeprom);\n}\nvoid rgb_matrix_decrease_hue_noeeprom(void) {\n    rgb_matrix_decrease_hue_helper(false);\n}\nvoid rgb_matrix_decrease_hue(void) {\n    rgb_matrix_decrease_hue_helper(true);\n}\n\nvoid rgb_matrix_increase_sat_helper(bool write_to_eeprom) {\n    rgb_matrix_sethsv_eeprom_helper(rgb_matrix_config.hsv.h, qadd8(rgb_matrix_config.hsv.s, RGB_MATRIX_SAT_STEP), rgb_matrix_config.hsv.v, write_to_eeprom);\n}\nvoid rgb_matrix_increase_sat_noeeprom(void) {\n    rgb_matrix_increase_sat_helper(false);\n}\nvoid rgb_matrix_increase_sat(void) {\n    rgb_matrix_increase_sat_helper(true);\n}\n\nvoid rgb_matrix_decrease_sat_helper(bool write_to_eeprom) {\n    rgb_matrix_sethsv_eeprom_helper(rgb_matrix_config.hsv.h, qsub8(rgb_matrix_config.hsv.s, RGB_MATRIX_SAT_STEP), rgb_matrix_config.hsv.v, write_to_eeprom);\n}\nvoid rgb_matrix_decrease_sat_noeeprom(void) {\n    rgb_matrix_decrease_sat_helper(false);\n}\nvoid rgb_matrix_decrease_sat(void) {\n    rgb_matrix_decrease_sat_helper(true);\n}\n\nvoid rgb_matrix_increase_val_helper(bool write_to_eeprom) {\n    rgb_matrix_sethsv_eeprom_helper(rgb_matrix_config.hsv.h, rgb_matrix_config.hsv.s, qadd8(rgb_matrix_config.hsv.v, RGB_MATRIX_VAL_STEP), write_to_eeprom);\n}\nvoid rgb_matrix_increase_val_noeeprom(void) {\n    rgb_matrix_increase_val_helper(false);\n}\nvoid rgb_matrix_increase_val(void) {\n    rgb_matrix_increase_val_helper(true);\n}\n\nvoid rgb_matrix_decrease_val_helper(bool write_to_eeprom) {\n    rgb_matrix_sethsv_eeprom_helper(rgb_matrix_config.hsv.h, rgb_matrix_config.hsv.s, qsub8(rgb_matrix_config.hsv.v, RGB_MATRIX_VAL_STEP), write_to_eeprom);\n}\nvoid rgb_matrix_decrease_val_noeeprom(void) {\n    rgb_matrix_decrease_val_helper(false);\n}\nvoid rgb_matrix_decrease_val(void) {\n    rgb_matrix_decrease_val_helper(true);\n}\n\nvoid rgb_matrix_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {\n    rgb_matrix_config.speed = speed;\n    eeconfig_flag_rgb_matrix(write_to_eeprom);\n    dprintf(\"rgb matrix set speed [%s]: %u\\n\", (write_to_eeprom) ? \"EEPROM\" : \"NOEEPROM\", rgb_matrix_config.speed);\n}\nvoid rgb_matrix_set_speed_noeeprom(uint8_t speed) {\n    rgb_matrix_set_speed_eeprom_helper(speed, false);\n}\nvoid rgb_matrix_set_speed(uint8_t speed) {\n    rgb_matrix_set_speed_eeprom_helper(speed, true);\n}\n\nuint8_t rgb_matrix_get_speed(void) {\n    return rgb_matrix_config.speed;\n}\n\nvoid rgb_matrix_increase_speed_helper(bool write_to_eeprom) {\n    rgb_matrix_set_speed_eeprom_helper(qadd8(rgb_matrix_config.speed, RGB_MATRIX_SPD_STEP), write_to_eeprom);\n}\nvoid rgb_matrix_increase_speed_noeeprom(void) {\n    rgb_matrix_increase_speed_helper(false);\n}\nvoid rgb_matrix_increase_speed(void) {\n    rgb_matrix_increase_speed_helper(true);\n}\n\nvoid rgb_matrix_decrease_speed_helper(bool write_to_eeprom) {\n    rgb_matrix_set_speed_eeprom_helper(qsub8(rgb_matrix_config.speed, RGB_MATRIX_SPD_STEP), write_to_eeprom);\n}\nvoid rgb_matrix_decrease_speed_noeeprom(void) {\n    rgb_matrix_decrease_speed_helper(false);\n}\nvoid rgb_matrix_decrease_speed(void) {\n    rgb_matrix_decrease_speed_helper(true);\n}\n\nvoid rgb_matrix_set_flags_eeprom_helper(led_flags_t flags, bool write_to_eeprom) {\n    rgb_matrix_config.flags = flags;\n    eeconfig_flag_rgb_matrix(write_to_eeprom);\n    dprintf(\"rgb matrix set flags [%s]: %u\\n\", (write_to_eeprom) ? \"EEPROM\" : \"NOEEPROM\", rgb_matrix_config.flags);\n}\n\nled_flags_t rgb_matrix_get_flags(void) {\n    return rgb_matrix_config.flags;\n}\n\nvoid rgb_matrix_set_flags(led_flags_t flags) {\n    rgb_matrix_set_flags_eeprom_helper(flags, true);\n}\n\nvoid rgb_matrix_set_flags_noeeprom(led_flags_t flags) {\n    rgb_matrix_set_flags_eeprom_helper(flags, false);\n}\n"
  },
  {
    "path": "quantum/rgb_matrix/rgb_matrix.h",
    "content": "/* Copyright 2017 Jason Williams\n * Copyright 2017 Jack Humbert\n * Copyright 2018 Yiancar\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"rgb_matrix_types.h\"\n#include \"rgb_matrix_drivers.h\"\n#include \"color.h\"\n#include \"keyboard.h\"\n\n#ifndef RGB_MATRIX_TIMEOUT\n#    define RGB_MATRIX_TIMEOUT 0\n#endif\n\n#ifndef RGB_MATRIX_MAXIMUM_BRIGHTNESS\n#    define RGB_MATRIX_MAXIMUM_BRIGHTNESS UINT8_MAX\n#endif\n\n#ifndef RGB_MATRIX_HUE_STEP\n#    define RGB_MATRIX_HUE_STEP 8\n#endif\n\n#ifndef RGB_MATRIX_SAT_STEP\n#    define RGB_MATRIX_SAT_STEP 16\n#endif\n\n#ifndef RGB_MATRIX_VAL_STEP\n#    define RGB_MATRIX_VAL_STEP 16\n#endif\n\n#ifndef RGB_MATRIX_SPD_STEP\n#    define RGB_MATRIX_SPD_STEP 16\n#endif\n\n#ifndef RGB_MATRIX_DEFAULT_ON\n#    define RGB_MATRIX_DEFAULT_ON true\n#endif\n\n#ifndef RGB_MATRIX_DEFAULT_MODE\n#    ifdef ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT\n#        define RGB_MATRIX_DEFAULT_MODE RGB_MATRIX_CYCLE_LEFT_RIGHT\n#    else\n// fallback to solid colors if RGB_MATRIX_CYCLE_LEFT_RIGHT is disabled in userspace\n#        define RGB_MATRIX_DEFAULT_MODE RGB_MATRIX_SOLID_COLOR\n#    endif\n#endif\n\n#ifndef RGB_MATRIX_DEFAULT_HUE\n#    define RGB_MATRIX_DEFAULT_HUE 0\n#endif\n\n#ifndef RGB_MATRIX_DEFAULT_SAT\n#    define RGB_MATRIX_DEFAULT_SAT UINT8_MAX\n#endif\n\n#ifndef RGB_MATRIX_DEFAULT_VAL\n#    define RGB_MATRIX_DEFAULT_VAL RGB_MATRIX_MAXIMUM_BRIGHTNESS\n#endif\n\n#ifndef RGB_MATRIX_DEFAULT_SPD\n#    define RGB_MATRIX_DEFAULT_SPD UINT8_MAX / 2\n#endif\n\n#ifndef RGB_MATRIX_DEFAULT_FLAGS\n#    define RGB_MATRIX_DEFAULT_FLAGS LED_FLAG_ALL\n#endif\n\n#ifndef RGB_MATRIX_LED_FLUSH_LIMIT\n#    define RGB_MATRIX_LED_FLUSH_LIMIT 16\n#endif\n\n#ifndef RGB_MATRIX_LED_PROCESS_LIMIT\n#    define RGB_MATRIX_LED_PROCESS_LIMIT ((RGB_MATRIX_LED_COUNT + 4) / 5)\n#endif\n\nstruct rgb_matrix_limits_t {\n    uint8_t led_min_index;\n    uint8_t led_max_index;\n};\n\nstruct rgb_matrix_limits_t rgb_matrix_get_limits(uint8_t iter);\n\n#define RGB_MATRIX_USE_LIMITS_ITER(min, max, iter)                   \\\n    struct rgb_matrix_limits_t limits = rgb_matrix_get_limits(iter); \\\n    uint8_t                    min    = limits.led_min_index;        \\\n    uint8_t                    max    = limits.led_max_index;        \\\n    (void)min;                                                       \\\n    (void)max;\n\n#define RGB_MATRIX_USE_LIMITS(min, max) RGB_MATRIX_USE_LIMITS_ITER(min, max, params->iter)\n\n#define RGB_MATRIX_INDICATOR_SET_COLOR(i, r, g, b) \\\n    if (i >= led_min && i < led_max) {             \\\n        rgb_matrix_set_color(i, r, g, b);          \\\n    }\n\n#define RGB_MATRIX_TEST_LED_FLAGS() \\\n    if (!HAS_ANY_FLAGS(g_led_config.flags[i], params->flags)) continue\n\nenum rgb_matrix_effects {\n    RGB_MATRIX_NONE = 0,\n\n// --------------------------------------\n// -----Begin rgb effect enum macros-----\n#define RGB_MATRIX_EFFECT(name, ...) RGB_MATRIX_##name,\n#include \"rgb_matrix_effects.inc\"\n#undef RGB_MATRIX_EFFECT\n\n#ifdef COMMUNITY_MODULES_ENABLE\n#    define RGB_MATRIX_EFFECT(name, ...) RGB_MATRIX_COMMUNITY_MODULE_##name,\n#    include \"rgb_matrix_community_modules.inc\"\n#    undef RGB_MATRIX_EFFECT\n#endif\n\n#if defined(RGB_MATRIX_CUSTOM_KB) || defined(RGB_MATRIX_CUSTOM_USER)\n#    define RGB_MATRIX_EFFECT(name, ...) RGB_MATRIX_CUSTOM_##name,\n#    ifdef RGB_MATRIX_CUSTOM_KB\n#        include \"rgb_matrix_kb.inc\"\n#    endif\n#    ifdef RGB_MATRIX_CUSTOM_USER\n#        include \"rgb_matrix_user.inc\"\n#    endif\n#    undef RGB_MATRIX_EFFECT\n#endif\n    // --------------------------------------\n    // -----End rgb effect enum macros-------\n\n    RGB_MATRIX_EFFECT_MAX\n};\n\nvoid eeconfig_update_rgb_matrix_default(void);\nvoid eeconfig_force_flush_rgb_matrix(void);\n\nuint8_t rgb_matrix_map_row_column_to_led_kb(uint8_t row, uint8_t column, uint8_t *led_i);\nuint8_t rgb_matrix_map_row_column_to_led(uint8_t row, uint8_t column, uint8_t *led_i);\n\nint rgb_matrix_led_index(int index);\n\nvoid rgb_matrix_set_color(int index, uint8_t red, uint8_t green, uint8_t blue);\nvoid rgb_matrix_set_color_all(uint8_t red, uint8_t green, uint8_t blue);\n\nvoid rgb_matrix_handle_key_event(uint8_t row, uint8_t col, bool pressed);\n\nvoid rgb_matrix_task(void);\n\n// This runs after another backlight effect and replaces\n// colors already set\nvoid rgb_matrix_indicators(void);\nbool rgb_matrix_indicators_kb(void);\nbool rgb_matrix_indicators_user(void);\n\nvoid rgb_matrix_indicators_advanced(effect_params_t *params);\nbool rgb_matrix_indicators_advanced_kb(uint8_t led_min, uint8_t led_max);\nbool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max);\n\nvoid rgb_matrix_init(void);\n\nvoid rgb_matrix_reload_from_eeprom(void);\n\nvoid        rgb_matrix_set_suspend_state(bool state);\nbool        rgb_matrix_get_suspend_state(void);\nvoid        rgb_matrix_toggle(void);\nvoid        rgb_matrix_toggle_noeeprom(void);\nvoid        rgb_matrix_enable(void);\nvoid        rgb_matrix_enable_noeeprom(void);\nvoid        rgb_matrix_disable(void);\nvoid        rgb_matrix_disable_noeeprom(void);\nuint8_t     rgb_matrix_is_enabled(void);\nvoid        rgb_matrix_mode(uint8_t mode);\nvoid        rgb_matrix_mode_noeeprom(uint8_t mode);\nuint8_t     rgb_matrix_get_mode(void);\nvoid        rgb_matrix_step(void);\nvoid        rgb_matrix_step_noeeprom(void);\nvoid        rgb_matrix_step_reverse(void);\nvoid        rgb_matrix_step_reverse_noeeprom(void);\nvoid        rgb_matrix_sethsv(uint16_t hue, uint8_t sat, uint8_t val);\nvoid        rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val);\nhsv_t       rgb_matrix_get_hsv(void);\nuint8_t     rgb_matrix_get_hue(void);\nuint8_t     rgb_matrix_get_sat(void);\nuint8_t     rgb_matrix_get_val(void);\nvoid        rgb_matrix_increase_hue(void);\nvoid        rgb_matrix_increase_hue_noeeprom(void);\nvoid        rgb_matrix_decrease_hue(void);\nvoid        rgb_matrix_decrease_hue_noeeprom(void);\nvoid        rgb_matrix_increase_sat(void);\nvoid        rgb_matrix_increase_sat_noeeprom(void);\nvoid        rgb_matrix_decrease_sat(void);\nvoid        rgb_matrix_decrease_sat_noeeprom(void);\nvoid        rgb_matrix_increase_val(void);\nvoid        rgb_matrix_increase_val_noeeprom(void);\nvoid        rgb_matrix_decrease_val(void);\nvoid        rgb_matrix_decrease_val_noeeprom(void);\nvoid        rgb_matrix_set_speed(uint8_t speed);\nvoid        rgb_matrix_set_speed_noeeprom(uint8_t speed);\nuint8_t     rgb_matrix_get_speed(void);\nvoid        rgb_matrix_increase_speed(void);\nvoid        rgb_matrix_increase_speed_noeeprom(void);\nvoid        rgb_matrix_decrease_speed(void);\nvoid        rgb_matrix_decrease_speed_noeeprom(void);\nled_flags_t rgb_matrix_get_flags(void);\nvoid        rgb_matrix_set_flags(led_flags_t flags);\nvoid        rgb_matrix_set_flags_noeeprom(led_flags_t flags);\nvoid        rgb_matrix_update_pwm_buffers(void);\n\n#ifndef RGBLIGHT_ENABLE\n#    define eeconfig_update_rgblight_current eeconfig_force_flush_rgb_matrix\n#    define rgblight_reload_from_eeprom rgb_matrix_reload_from_eeprom\n#    define rgblight_toggle rgb_matrix_toggle\n#    define rgblight_toggle_noeeprom rgb_matrix_toggle_noeeprom\n#    define rgblight_enable rgb_matrix_enable\n#    define rgblight_enable_noeeprom rgb_matrix_enable_noeeprom\n#    define rgblight_disable rgb_matrix_disable\n#    define rgblight_disable_noeeprom rgb_matrix_disable_noeeprom\n#    define rgblight_is_enabled rgb_matrix_is_enabled\n#    define rgblight_mode rgb_matrix_mode\n#    define rgblight_mode_noeeprom rgb_matrix_mode_noeeprom\n#    define rgblight_get_mode rgb_matrix_get_mode\n#    define rgblight_get_hue rgb_matrix_get_hue\n#    define rgblight_get_sat rgb_matrix_get_sat\n#    define rgblight_get_val rgb_matrix_get_val\n#    define rgblight_get_hsv rgb_matrix_get_hsv\n#    define rgblight_step rgb_matrix_step\n#    define rgblight_step_noeeprom rgb_matrix_step_noeeprom\n#    define rgblight_step_reverse rgb_matrix_step_reverse\n#    define rgblight_step_reverse_noeeprom rgb_matrix_step_reverse_noeeprom\n#    define rgblight_sethsv rgb_matrix_sethsv\n#    define rgblight_sethsv_noeeprom rgb_matrix_sethsv_noeeprom\n#    define rgblight_increase_hue rgb_matrix_increase_hue\n#    define rgblight_increase_hue_noeeprom rgb_matrix_increase_hue_noeeprom\n#    define rgblight_decrease_hue rgb_matrix_decrease_hue\n#    define rgblight_decrease_hue_noeeprom rgb_matrix_decrease_hue_noeeprom\n#    define rgblight_increase_sat rgb_matrix_increase_sat\n#    define rgblight_increase_sat_noeeprom rgb_matrix_increase_sat_noeeprom\n#    define rgblight_decrease_sat rgb_matrix_decrease_sat\n#    define rgblight_decrease_sat_noeeprom rgb_matrix_decrease_sat_noeeprom\n#    define rgblight_increase_val rgb_matrix_increase_val\n#    define rgblight_increase_val_noeeprom rgb_matrix_increase_val_noeeprom\n#    define rgblight_decrease_val rgb_matrix_decrease_val\n#    define rgblight_decrease_val_noeeprom rgb_matrix_decrease_val_noeeprom\n#    define rgblight_set_speed rgb_matrix_set_speed\n#    define rgblight_set_speed_noeeprom rgb_matrix_set_speed_noeeprom\n#    define rgblight_get_speed rgb_matrix_get_speed\n#    define rgblight_increase_speed rgb_matrix_increase_speed\n#    define rgblight_increase_speed_noeeprom rgb_matrix_increase_speed_noeeprom\n#    define rgblight_decrease_speed rgb_matrix_decrease_speed\n#    define rgblight_decrease_speed_noeeprom rgb_matrix_decrease_speed_noeeprom\n#endif\n\nstatic inline bool rgb_matrix_check_finished_leds(uint8_t led_idx) {\n#if defined(RGB_MATRIX_SPLIT)\n    if (is_keyboard_left()) {\n        uint8_t k_rgb_matrix_split[2] = RGB_MATRIX_SPLIT;\n        return led_idx < k_rgb_matrix_split[0];\n    } else\n        return led_idx < RGB_MATRIX_LED_COUNT;\n#else\n    return led_idx < RGB_MATRIX_LED_COUNT;\n#endif\n}\n\nextern rgb_config_t rgb_matrix_config;\n\nextern uint32_t     g_rgb_timer;\nextern led_config_t g_led_config;\n#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\nextern last_hit_t g_last_hit_tracker;\n#endif\n#ifdef RGB_MATRIX_FRAMEBUFFER_EFFECTS\nextern uint8_t g_rgb_frame_buffer[MATRIX_ROWS][MATRIX_COLS];\n#endif\n"
  },
  {
    "path": "quantum/rgb_matrix/rgb_matrix_drivers.c",
    "content": "/* Copyright 2018 James Laird-Wah\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"rgb_matrix_drivers.h\"\n\n#include <stdbool.h>\n#include \"keyboard.h\"\n#include \"color.h\"\n#include \"util.h\"\n\n/* Each driver needs to define the struct\n *    const rgb_matrix_driver_t rgb_matrix_driver;\n * All members must be provided.\n * Keyboard custom drivers can define this in their own files, it should only\n * be here if shared between boards.\n */\n\n#if defined(RGB_MATRIX_IS31FL3218)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3218_init,\n    .flush         = is31fl3218_update_pwm_buffers,\n    .set_color     = is31fl3218_set_color,\n    .set_color_all = is31fl3218_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_IS31FL3236)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3236_init_drivers,\n    .flush         = is31fl3236_flush,\n    .set_color     = is31fl3236_set_color,\n    .set_color_all = is31fl3236_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_IS31FL3729)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3729_init_drivers,\n    .flush         = is31fl3729_flush,\n    .set_color     = is31fl3729_set_color,\n    .set_color_all = is31fl3729_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_IS31FL3731)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3731_init_drivers,\n    .flush         = is31fl3731_flush,\n    .set_color     = is31fl3731_set_color,\n    .set_color_all = is31fl3731_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_IS31FL3733)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3733_init_drivers,\n    .flush         = is31fl3733_flush,\n    .set_color     = is31fl3733_set_color,\n    .set_color_all = is31fl3733_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_IS31FL3736)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3736_init_drivers,\n    .flush         = is31fl3736_flush,\n    .set_color     = is31fl3736_set_color,\n    .set_color_all = is31fl3736_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_IS31FL3737)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3737_init_drivers,\n    .flush         = is31fl3737_flush,\n    .set_color     = is31fl3737_set_color,\n    .set_color_all = is31fl3737_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_IS31FL3741)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3741_init_drivers,\n    .flush         = is31fl3741_flush,\n    .set_color     = is31fl3741_set_color,\n    .set_color_all = is31fl3741_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_IS31FL3742A)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3742a_init_drivers,\n    .flush         = is31fl3742a_flush,\n    .set_color     = is31fl3742a_set_color,\n    .set_color_all = is31fl3742a_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_IS31FL3743A)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3743a_init_drivers,\n    .flush         = is31fl3743a_flush,\n    .set_color     = is31fl3743a_set_color,\n    .set_color_all = is31fl3743a_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_IS31FL3745)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3745_init_drivers,\n    .flush         = is31fl3745_flush,\n    .set_color     = is31fl3745_set_color,\n    .set_color_all = is31fl3745_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_IS31FL3746A)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = is31fl3746a_init_drivers,\n    .flush         = is31fl3746a_flush,\n    .set_color     = is31fl3746a_set_color,\n    .set_color_all = is31fl3746a_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_SNLED27351)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = snled27351_init_drivers,\n    .flush         = snled27351_flush,\n    .set_color     = snled27351_set_color,\n    .set_color_all = snled27351_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_AW20216S)\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = aw20216s_init_drivers,\n    .flush         = aw20216s_flush,\n    .set_color     = aw20216s_set_color,\n    .set_color_all = aw20216s_set_color_all,\n};\n\n#elif defined(RGB_MATRIX_WS2812)\n#    if defined(RGBLIGHT_WS2812)\n#        pragma message \"Cannot use RGBLIGHT and RGB Matrix using WS2812 at the same time.\"\n#        pragma message \"You need to use a custom driver, or re-implement the WS2812 driver to use a different configuration.\"\n#    endif\n\nconst rgb_matrix_driver_t rgb_matrix_driver = {\n    .init          = ws2812_init,\n    .flush         = ws2812_flush,\n    .set_color     = ws2812_set_color,\n    .set_color_all = ws2812_set_color_all,\n};\n\n#endif\n"
  },
  {
    "path": "quantum/rgb_matrix/rgb_matrix_drivers.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n\n#if defined(RGB_MATRIX_AW20216S)\n#    include \"aw20216s.h\"\n#elif defined(RGB_MATRIX_IS31FL3236)\n#    include \"is31fl3236.h\"\n#elif defined(RGB_MATRIX_IS31FL3218)\n#    include \"is31fl3218.h\"\n#elif defined(RGB_MATRIX_IS31FL3729)\n#    include \"is31fl3729.h\"\n#elif defined(RGB_MATRIX_IS31FL3731)\n#    include \"is31fl3731.h\"\n#elif defined(RGB_MATRIX_IS31FL3733)\n#    include \"is31fl3733.h\"\n#elif defined(RGB_MATRIX_IS31FL3736)\n#    include \"is31fl3736.h\"\n#elif defined(RGB_MATRIX_IS31FL3737)\n#    include \"is31fl3737.h\"\n#elif defined(RGB_MATRIX_IS31FL3741)\n#    include \"is31fl3741.h\"\n#elif defined(RGB_MATRIX_IS31FL3742A)\n#    include \"is31fl3742a.h\"\n#elif defined(RGB_MATRIX_IS31FL3743A)\n#    include \"is31fl3743a.h\"\n#elif defined(RGB_MATRIX_IS31FL3745)\n#    include \"is31fl3745.h\"\n#elif defined(RGB_MATRIX_IS31FL3746A)\n#    include \"is31fl3746a.h\"\n#elif defined(RGB_MATRIX_SNLED27351)\n#    include \"snled27351.h\"\n#elif defined(RGB_MATRIX_WS2812)\n#    include \"ws2812.h\"\n#endif\n\ntypedef struct {\n    /* Perform any initialisation required for the other driver functions to work. */\n    void (*init)(void);\n    /* Set the colour of a single LED in the buffer. */\n    void (*set_color)(int index, uint8_t r, uint8_t g, uint8_t b);\n    /* Set the colour of all LEDS on the keyboard in the buffer. */\n    void (*set_color_all)(uint8_t r, uint8_t g, uint8_t b);\n    /* Flush any buffered changes to the hardware. */\n    void (*flush)(void);\n} rgb_matrix_driver_t;\n\nextern const rgb_matrix_driver_t rgb_matrix_driver;\n"
  },
  {
    "path": "quantum/rgb_matrix/rgb_matrix_types.h",
    "content": "/* Copyright 2021\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"compiler_support.h\"\n#include \"color.h\"\n#include \"util.h\"\n\n#if defined(RGB_MATRIX_KEYPRESSES) || defined(RGB_MATRIX_KEYRELEASES)\n#    define RGB_MATRIX_KEYREACTIVE_ENABLED\n#endif\n\n// Last led hit\n#ifndef LED_HITS_TO_REMEMBER\n#    define LED_HITS_TO_REMEMBER 8\n#endif // LED_HITS_TO_REMEMBER\n\n#ifdef RGB_MATRIX_KEYREACTIVE_ENABLED\ntypedef struct PACKED {\n    uint8_t  count;\n    uint8_t  x[LED_HITS_TO_REMEMBER];\n    uint8_t  y[LED_HITS_TO_REMEMBER];\n    uint8_t  index[LED_HITS_TO_REMEMBER];\n    uint16_t tick[LED_HITS_TO_REMEMBER];\n} last_hit_t;\n#endif // RGB_MATRIX_KEYREACTIVE_ENABLED\n\ntypedef enum rgb_task_states { STARTING, RENDERING, FLUSHING, SYNCING } rgb_task_states;\n\ntypedef uint8_t led_flags_t;\n\ntypedef struct PACKED {\n    uint8_t     iter;\n    led_flags_t flags;\n    bool        init;\n} effect_params_t;\n\ntypedef struct PACKED {\n    uint8_t x;\n    uint8_t y;\n} led_point_t;\n\n#define HAS_FLAGS(bits, flags) ((bits & flags) == flags)\n#define HAS_ANY_FLAGS(bits, flags) ((bits & flags) != 0x00)\n\n#define LED_FLAG_ALL 0xFF\n#define LED_FLAG_NONE 0x00\n#define LED_FLAG_MODIFIER 0x01\n#define LED_FLAG_UNDERGLOW 0x02\n#define LED_FLAG_KEYLIGHT 0x04\n#define LED_FLAG_INDICATOR 0x08\n\n#define NO_LED 255\n\ntypedef struct PACKED {\n    uint8_t     matrix_co[MATRIX_ROWS][MATRIX_COLS];\n    led_point_t point[RGB_MATRIX_LED_COUNT];\n    uint8_t     flags[RGB_MATRIX_LED_COUNT];\n} led_config_t;\n\ntypedef union rgb_config_t {\n    uint64_t raw;\n    struct PACKED {\n        uint8_t     enable : 2;\n        uint8_t     mode : 6;\n        hsv_t       hsv;\n        uint8_t     speed;\n        led_flags_t flags;\n    };\n} rgb_config_t;\n\nSTATIC_ASSERT(sizeof(rgb_config_t) == sizeof(uint64_t), \"RGB Matrix EECONFIG out of spec.\");\n"
  },
  {
    "path": "quantum/rgblight/rgblight.c",
    "content": "/* Copyright 2016-2017 Yang Liu\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include <math.h>\n#include <string.h>\n#include <stdlib.h>\n#include \"progmem.h\"\n#include \"sync_timer.h\"\n#include \"rgblight.h\"\n#include \"color.h\"\n#include \"debug.h\"\n#include \"util.h\"\n#include \"led_tables.h\"\n#include <lib/lib8tion/lib8tion.h>\n#include \"eeconfig.h\"\n\n#ifdef RGBLIGHT_SPLIT\n/* for split keyboard */\n#    define RGBLIGHT_SPLIT_SET_CHANGE_MODE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_MODE\n#    define RGBLIGHT_SPLIT_SET_CHANGE_HSVS rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_HSVS\n#    define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS rgblight_status.change_flags |= (RGBLIGHT_STATUS_CHANGE_MODE | RGBLIGHT_STATUS_CHANGE_HSVS)\n#    define RGBLIGHT_SPLIT_SET_CHANGE_LAYERS rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_LAYERS\n#    define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE rgblight_status.change_flags |= RGBLIGHT_STATUS_CHANGE_TIMER\n#    define RGBLIGHT_SPLIT_ANIMATION_TICK rgblight_status.change_flags |= RGBLIGHT_STATUS_ANIMATION_TICK\n#else\n#    define RGBLIGHT_SPLIT_SET_CHANGE_MODE\n#    define RGBLIGHT_SPLIT_SET_CHANGE_HSVS\n#    define RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS\n#    define RGBLIGHT_SPLIT_SET_CHANGE_LAYERS\n#    define RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE\n#    define RGBLIGHT_SPLIT_ANIMATION_TICK\n#endif\n\n#define _RGBM_SINGLE_STATIC(sym) RGBLIGHT_MODE_##sym,\n#define _RGBM_SINGLE_DYNAMIC(sym)\n#define _RGBM_MULTI_STATIC(sym) RGBLIGHT_MODE_##sym,\n#define _RGBM_MULTI_DYNAMIC(sym)\n#define _RGBM_TMP_STATIC(sym, msym) RGBLIGHT_MODE_##sym,\n#define _RGBM_TMP_DYNAMIC(sym, msym)\nstatic uint8_t static_effect_table[] = {\n#include \"rgblight_modes.h\"\n};\n\n#define _RGBM_SINGLE_STATIC(sym) RGBLIGHT_MODE_##sym,\n#define _RGBM_SINGLE_DYNAMIC(sym) RGBLIGHT_MODE_##sym,\n#define _RGBM_MULTI_STATIC(sym) RGBLIGHT_MODE_##sym,\n#define _RGBM_MULTI_DYNAMIC(sym) RGBLIGHT_MODE_##sym,\n#define _RGBM_TMP_STATIC(sym, msym) RGBLIGHT_MODE_##msym,\n#define _RGBM_TMP_DYNAMIC(sym, msym) RGBLIGHT_MODE_##msym,\nstatic uint8_t mode_base_table[] = {\n    0, // RGBLIGHT_MODE_zero\n#include \"rgblight_modes.h\"\n};\n\n#if !defined(RGBLIGHT_DEFAULT_MODE)\n#    define RGBLIGHT_DEFAULT_MODE RGBLIGHT_MODE_STATIC_LIGHT\n#endif\n\n#if !defined(RGBLIGHT_DEFAULT_HUE)\n#    define RGBLIGHT_DEFAULT_HUE 0\n#endif\n\n#if !defined(RGBLIGHT_DEFAULT_SAT)\n#    define RGBLIGHT_DEFAULT_SAT UINT8_MAX\n#endif\n\n#if !defined(RGBLIGHT_DEFAULT_VAL)\n#    define RGBLIGHT_DEFAULT_VAL RGBLIGHT_LIMIT_VAL\n#endif\n\n#if !defined(RGBLIGHT_DEFAULT_SPD)\n#    define RGBLIGHT_DEFAULT_SPD 0\n#endif\n\n#if !defined(RGBLIGHT_DEFAULT_ON)\n#    define RGBLIGHT_DEFAULT_ON true\n#endif\n\nstatic inline int is_static_effect(uint8_t mode) {\n    return memchr(static_effect_table, mode, sizeof(static_effect_table)) != NULL;\n}\n\n#ifdef RGBLIGHT_LED_MAP\nconst uint8_t led_map[] PROGMEM = RGBLIGHT_LED_MAP;\n#endif\n\n#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT\n__attribute__((weak)) const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 170, 127, 85, 64};\n#endif\n\nrgblight_config_t rgblight_config;\nrgblight_status_t rgblight_status         = {.timer_enabled = false};\nbool              is_rgblight_initialized = false;\n\n#ifdef RGBLIGHT_SLEEP\nstatic bool is_suspended;\nstatic bool pre_suspend_enabled;\n#endif\n\n#ifdef RGBLIGHT_USE_TIMER\nanimation_status_t animation_status = {};\n#endif\n\n#ifdef RGBLIGHT_LAYERS\nrgblight_segment_t const *const *rgblight_layers = NULL;\n\nstatic bool deferred_set_layer_state = false;\n#endif\n\nrgblight_ranges_t rgblight_ranges = {0, RGBLIGHT_LED_COUNT, 0, RGBLIGHT_LED_COUNT, RGBLIGHT_LED_COUNT};\n\nvoid rgblight_set_clipping_range(uint8_t start_pos, uint8_t num_leds) {\n    rgblight_ranges.clipping_start_pos = start_pos;\n    rgblight_ranges.clipping_num_leds  = num_leds;\n}\n\nvoid rgblight_set_effect_range(uint8_t start_pos, uint8_t num_leds) {\n    if (start_pos >= RGBLIGHT_LED_COUNT) return;\n    if (start_pos + num_leds > RGBLIGHT_LED_COUNT) return;\n    rgblight_ranges.effect_start_pos = start_pos;\n    rgblight_ranges.effect_end_pos   = start_pos + num_leds;\n    rgblight_ranges.effect_num_leds  = num_leds;\n}\n\n__attribute__((weak)) rgb_t rgblight_hsv_to_rgb(hsv_t hsv) {\n    return hsv_to_rgb(hsv);\n}\n\nuint8_t rgblight_led_index(uint8_t index) {\n#if defined(RGBLIGHT_LED_MAP)\n    return pgm_read_byte(&led_map[index]) - rgblight_ranges.clipping_start_pos;\n#else\n    return index - rgblight_ranges.clipping_start_pos;\n#endif\n}\n\nvoid setrgb(uint8_t r, uint8_t g, uint8_t b, int index) {\n    rgblight_driver.set_color(rgblight_led_index(index), r, g, b);\n}\n\nvoid sethsv_raw(uint8_t hue, uint8_t sat, uint8_t val, int index) {\n    hsv_t hsv = {hue, sat, val};\n    rgb_t rgb = rgblight_hsv_to_rgb(hsv);\n    setrgb(rgb.r, rgb.g, rgb.b, index);\n}\n\nvoid sethsv(uint8_t hue, uint8_t sat, uint8_t val, int index) {\n    sethsv_raw(hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val, index);\n}\n\nvoid rgblight_check_config(void) {\n    /* Add some out of bound checks for RGB light config */\n\n    if (rgblight_config.mode < RGBLIGHT_MODE_STATIC_LIGHT) {\n        rgblight_config.mode = RGBLIGHT_MODE_STATIC_LIGHT;\n    } else if (rgblight_config.mode > RGBLIGHT_MODES) {\n        rgblight_config.mode = RGBLIGHT_MODES;\n    }\n\n    if (rgblight_config.val > RGBLIGHT_LIMIT_VAL) {\n        rgblight_config.val = RGBLIGHT_LIMIT_VAL;\n    }\n}\n\nvoid eeconfig_update_rgblight_current(void) {\n    rgblight_check_config();\n    eeconfig_update_rgblight(&rgblight_config);\n}\n\nvoid eeconfig_update_rgblight_default(void) {\n    rgblight_config.enable    = RGBLIGHT_DEFAULT_ON;\n    rgblight_config.velocikey = 0;\n    rgblight_config.mode      = RGBLIGHT_DEFAULT_MODE;\n    rgblight_config.hue       = RGBLIGHT_DEFAULT_HUE;\n    rgblight_config.sat       = RGBLIGHT_DEFAULT_SAT;\n    rgblight_config.val       = RGBLIGHT_DEFAULT_VAL;\n    rgblight_config.speed     = RGBLIGHT_DEFAULT_SPD;\n    RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;\n    eeconfig_update_rgblight(&rgblight_config);\n}\n\nvoid eeconfig_debug_rgblight(void) {\n    dprintf(\"rgblight_config EEPROM:\\n\");\n    dprintf(\"rgblight_config.enable = %d\\n\", rgblight_config.enable);\n    dprintf(\"rgblight_config.velocikey = %d\\n\", rgblight_config.velocikey);\n    dprintf(\"rghlight_config.mode = %d\\n\", rgblight_config.mode);\n    dprintf(\"rgblight_config.hue = %d\\n\", rgblight_config.hue);\n    dprintf(\"rgblight_config.sat = %d\\n\", rgblight_config.sat);\n    dprintf(\"rgblight_config.val = %d\\n\", rgblight_config.val);\n    dprintf(\"rgblight_config.speed = %d\\n\", rgblight_config.speed);\n}\n\nvoid rgblight_init(void) {\n    /* if already initialized, don't do it again.\n       If you must do it again, extern this and set to false, first.\n       This is a dirty, dirty hack until proper hooks can be added for keyboard startup. */\n    if (is_rgblight_initialized) {\n        return;\n    }\n\n    dprintf(\"rgblight_init start!\\n\");\n    eeconfig_read_rgblight(&rgblight_config);\n    RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;\n    if (!rgblight_config.mode) {\n        dprintf(\"rgblight_init rgblight_config.mode = 0. Write default values to EEPROM.\\n\");\n        eeconfig_update_rgblight_default();\n        eeconfig_read_rgblight(&rgblight_config);\n    }\n    rgblight_check_config();\n\n    eeconfig_debug_rgblight(); // display current eeprom values\n\n    rgblight_timer_init(); // setup the timer\n\n    rgblight_driver.init();\n\n    if (rgblight_config.enable) {\n        rgblight_mode_noeeprom(rgblight_config.mode);\n    }\n\n    is_rgblight_initialized = true;\n}\n\nvoid rgblight_reload_from_eeprom(void) {\n    /* Reset back to what we have in eeprom */\n    eeconfig_read_rgblight(&rgblight_config);\n    RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;\n    rgblight_check_config();\n    eeconfig_debug_rgblight(); // display current eeprom values\n    if (rgblight_config.enable) {\n        rgblight_mode_noeeprom(rgblight_config.mode);\n    }\n}\n\nuint64_t rgblight_read_qword(void) {\n    return rgblight_config.raw;\n}\n\nvoid rgblight_update_qword(uint64_t qword) {\n    RGBLIGHT_SPLIT_SET_CHANGE_MODEHSVS;\n    rgblight_config.raw = qword;\n    if (rgblight_config.enable)\n        rgblight_mode_noeeprom(rgblight_config.mode);\n    else {\n        rgblight_timer_disable();\n        rgblight_set();\n    }\n}\n\nvoid rgblight_increase(void) {\n    uint8_t mode = 0;\n    if (rgblight_config.mode < RGBLIGHT_MODES) {\n        mode = rgblight_config.mode + 1;\n    }\n    rgblight_mode(mode);\n}\nvoid rgblight_decrease(void) {\n    uint8_t mode = 0;\n    // Mode will never be < 1. If it ever is, eeprom needs to be initialized.\n    if (rgblight_config.mode > RGBLIGHT_MODE_STATIC_LIGHT) {\n        mode = rgblight_config.mode - 1;\n    }\n    rgblight_mode(mode);\n}\nvoid rgblight_step_helper(bool write_to_eeprom) {\n    uint8_t mode = 0;\n    mode         = rgblight_config.mode + 1;\n    if (mode > RGBLIGHT_MODES) {\n        mode = 1;\n    }\n    rgblight_mode_eeprom_helper(mode, write_to_eeprom);\n}\nvoid rgblight_step_noeeprom(void) {\n    rgblight_step_helper(false);\n}\nvoid rgblight_step(void) {\n    rgblight_step_helper(true);\n}\nvoid rgblight_step_reverse_helper(bool write_to_eeprom) {\n    uint8_t mode = 0;\n    mode         = rgblight_config.mode - 1;\n    if (mode < 1) {\n        mode = RGBLIGHT_MODES;\n    }\n    rgblight_mode_eeprom_helper(mode, write_to_eeprom);\n}\nvoid rgblight_step_reverse_noeeprom(void) {\n    rgblight_step_reverse_helper(false);\n}\nvoid rgblight_step_reverse(void) {\n    rgblight_step_reverse_helper(true);\n}\n\nuint8_t rgblight_get_mode(void) {\n    if (!rgblight_config.enable) {\n        return false;\n    }\n\n    return rgblight_config.mode;\n}\n\nvoid rgblight_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {\n    if (!rgblight_config.enable) {\n        return;\n    }\n    if (mode < RGBLIGHT_MODE_STATIC_LIGHT) {\n        rgblight_config.mode = RGBLIGHT_MODE_STATIC_LIGHT;\n    } else if (mode > RGBLIGHT_MODES) {\n        rgblight_config.mode = RGBLIGHT_MODES;\n    } else {\n        rgblight_config.mode = mode;\n    }\n    RGBLIGHT_SPLIT_SET_CHANGE_MODE;\n    if (write_to_eeprom) {\n        eeconfig_update_rgblight(&rgblight_config);\n        dprintf(\"rgblight mode [EEPROM]: %u\\n\", rgblight_config.mode);\n    } else {\n        dprintf(\"rgblight mode [NOEEPROM]: %u\\n\", rgblight_config.mode);\n    }\n    if (is_static_effect(rgblight_config.mode)) {\n        rgblight_timer_disable();\n    } else {\n        rgblight_timer_enable();\n    }\n#ifdef RGBLIGHT_USE_TIMER\n    animation_status.restart = true;\n#endif\n    rgblight_sethsv_noeeprom(rgblight_config.hue, rgblight_config.sat, rgblight_config.val);\n}\n\nvoid rgblight_mode(uint8_t mode) {\n    rgblight_mode_eeprom_helper(mode, true);\n}\n\nvoid rgblight_mode_noeeprom(uint8_t mode) {\n    rgblight_mode_eeprom_helper(mode, false);\n}\n\nvoid rgblight_toggle(void) {\n    dprintf(\"rgblight toggle [EEPROM]: rgblight_config.enable = %u\\n\", !rgblight_config.enable);\n    if (rgblight_config.enable) {\n        rgblight_disable();\n    } else {\n        rgblight_enable();\n    }\n}\n\nvoid rgblight_toggle_noeeprom(void) {\n    dprintf(\"rgblight toggle [NOEEPROM]: rgblight_config.enable = %u\\n\", !rgblight_config.enable);\n    if (rgblight_config.enable) {\n        rgblight_disable_noeeprom();\n    } else {\n        rgblight_enable_noeeprom();\n    }\n}\n\nvoid rgblight_enable(void) {\n    rgblight_config.enable = 1;\n    // No need to update EEPROM here. rgblight_mode() will do that, actually\n    // eeconfig_update_rgblight(&rgblight_config);\n    dprintf(\"rgblight enable [EEPROM]: rgblight_config.enable = %u\\n\", rgblight_config.enable);\n    rgblight_mode(rgblight_config.mode);\n}\n\nvoid rgblight_enable_noeeprom(void) {\n    rgblight_config.enable = 1;\n    dprintf(\"rgblight enable [NOEEPROM]: rgblight_config.enable = %u\\n\", rgblight_config.enable);\n    rgblight_mode_noeeprom(rgblight_config.mode);\n}\n\nvoid rgblight_disable(void) {\n    rgblight_config.enable = 0;\n    eeconfig_update_rgblight(&rgblight_config);\n    dprintf(\"rgblight disable [EEPROM]: rgblight_config.enable = %u\\n\", rgblight_config.enable);\n    rgblight_timer_disable();\n    RGBLIGHT_SPLIT_SET_CHANGE_MODE;\n    rgblight_set();\n}\n\nvoid rgblight_disable_noeeprom(void) {\n    rgblight_config.enable = 0;\n    dprintf(\"rgblight disable [NOEEPROM]: rgblight_config.enable = %u\\n\", rgblight_config.enable);\n    rgblight_timer_disable();\n    RGBLIGHT_SPLIT_SET_CHANGE_MODE;\n    rgblight_set();\n}\n\nvoid rgblight_enabled_noeeprom(bool state) {\n    state ? rgblight_enable_noeeprom() : rgblight_disable_noeeprom();\n}\n\nbool rgblight_is_enabled(void) {\n    return rgblight_config.enable;\n}\n\nvoid rgblight_increase_hue_helper(bool write_to_eeprom) {\n    uint8_t hue = rgblight_config.hue + RGBLIGHT_HUE_STEP;\n    rgblight_sethsv_eeprom_helper(hue, rgblight_config.sat, rgblight_config.val, write_to_eeprom);\n}\nvoid rgblight_increase_hue_noeeprom(void) {\n    rgblight_increase_hue_helper(false);\n}\nvoid rgblight_increase_hue(void) {\n    rgblight_increase_hue_helper(true);\n}\nvoid rgblight_decrease_hue_helper(bool write_to_eeprom) {\n    uint8_t hue = rgblight_config.hue - RGBLIGHT_HUE_STEP;\n    rgblight_sethsv_eeprom_helper(hue, rgblight_config.sat, rgblight_config.val, write_to_eeprom);\n}\nvoid rgblight_decrease_hue_noeeprom(void) {\n    rgblight_decrease_hue_helper(false);\n}\nvoid rgblight_decrease_hue(void) {\n    rgblight_decrease_hue_helper(true);\n}\nvoid rgblight_increase_sat_helper(bool write_to_eeprom) {\n    uint8_t sat = qadd8(rgblight_config.sat, RGBLIGHT_SAT_STEP);\n    rgblight_sethsv_eeprom_helper(rgblight_config.hue, sat, rgblight_config.val, write_to_eeprom);\n}\nvoid rgblight_increase_sat_noeeprom(void) {\n    rgblight_increase_sat_helper(false);\n}\nvoid rgblight_increase_sat(void) {\n    rgblight_increase_sat_helper(true);\n}\nvoid rgblight_decrease_sat_helper(bool write_to_eeprom) {\n    uint8_t sat = qsub8(rgblight_config.sat, RGBLIGHT_SAT_STEP);\n    rgblight_sethsv_eeprom_helper(rgblight_config.hue, sat, rgblight_config.val, write_to_eeprom);\n}\nvoid rgblight_decrease_sat_noeeprom(void) {\n    rgblight_decrease_sat_helper(false);\n}\nvoid rgblight_decrease_sat(void) {\n    rgblight_decrease_sat_helper(true);\n}\nvoid rgblight_increase_val_helper(bool write_to_eeprom) {\n    uint8_t val = qadd8(rgblight_config.val, RGBLIGHT_VAL_STEP);\n    rgblight_sethsv_eeprom_helper(rgblight_config.hue, rgblight_config.sat, val, write_to_eeprom);\n}\nvoid rgblight_increase_val_noeeprom(void) {\n    rgblight_increase_val_helper(false);\n}\nvoid rgblight_increase_val(void) {\n    rgblight_increase_val_helper(true);\n}\nvoid rgblight_decrease_val_helper(bool write_to_eeprom) {\n    uint8_t val = qsub8(rgblight_config.val, RGBLIGHT_VAL_STEP);\n    rgblight_sethsv_eeprom_helper(rgblight_config.hue, rgblight_config.sat, val, write_to_eeprom);\n}\nvoid rgblight_decrease_val_noeeprom(void) {\n    rgblight_decrease_val_helper(false);\n}\nvoid rgblight_decrease_val(void) {\n    rgblight_decrease_val_helper(true);\n}\n\nvoid rgblight_increase_speed_helper(bool write_to_eeprom) {\n    if (rgblight_config.speed < 3) rgblight_config.speed++;\n    // RGBLIGHT_SPLIT_SET_CHANGE_HSVS; // NEED?\n    if (write_to_eeprom) {\n        eeconfig_update_rgblight(&rgblight_config);\n    }\n}\nvoid rgblight_increase_speed(void) {\n    rgblight_increase_speed_helper(true);\n}\nvoid rgblight_increase_speed_noeeprom(void) {\n    rgblight_increase_speed_helper(false);\n}\n\nvoid rgblight_decrease_speed_helper(bool write_to_eeprom) {\n    if (rgblight_config.speed > 0) rgblight_config.speed--;\n    // RGBLIGHT_SPLIT_SET_CHANGE_HSVS; // NEED??\n    if (write_to_eeprom) {\n        eeconfig_update_rgblight(&rgblight_config);\n    }\n}\nvoid rgblight_decrease_speed(void) {\n    rgblight_decrease_speed_helper(true);\n}\nvoid rgblight_decrease_speed_noeeprom(void) {\n    rgblight_decrease_speed_helper(false);\n}\n\nvoid rgblight_sethsv_noeeprom_old(uint8_t hue, uint8_t sat, uint8_t val) {\n    if (rgblight_config.enable) {\n        rgb_t rgb = rgblight_hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});\n        rgblight_setrgb(rgb.r, rgb.g, rgb.b);\n    }\n}\n\nvoid rgblight_sethsv_eeprom_helper(uint8_t hue, uint8_t sat, uint8_t val, bool write_to_eeprom) {\n    if (rgblight_config.enable) {\n#ifdef RGBLIGHT_SPLIT\n        if (rgblight_config.hue != hue || rgblight_config.sat != sat || rgblight_config.val != val) {\n            RGBLIGHT_SPLIT_SET_CHANGE_HSVS;\n        }\n#endif\n        rgblight_status.base_mode = mode_base_table[rgblight_config.mode];\n        if (rgblight_config.mode == RGBLIGHT_MODE_STATIC_LIGHT) {\n            // same static color\n#ifdef RGBLIGHT_LAYERS_RETAIN_VAL\n            // needed for rgblight_layers_write() to get the new val, since it reads rgblight_config.val\n            rgblight_config.val = val;\n#endif\n            rgb_t rgb = rgblight_hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});\n            rgblight_setrgb(rgb.r, rgb.g, rgb.b);\n        } else {\n            // all LEDs in same color\n            if (1 == 0) { // dummy\n            }\n#ifdef RGBLIGHT_EFFECT_BREATHING\n            else if (rgblight_status.base_mode == RGBLIGHT_MODE_BREATHING) {\n                // breathing mode, ignore the change of val, use in memory value instead\n                val = rgblight_config.val;\n            }\n#endif\n#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD\n            else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_MOOD) {\n                // rainbow mood, ignore the change of hue\n                hue = rgblight_config.hue;\n            }\n#endif\n#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL\n            else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_SWIRL) {\n                // rainbow swirl, ignore the change of hue\n                hue = rgblight_config.hue;\n            }\n#endif\n#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT\n            else if (rgblight_status.base_mode == RGBLIGHT_MODE_STATIC_GRADIENT) {\n                // static gradient\n                uint8_t delta     = rgblight_config.mode - rgblight_status.base_mode;\n                bool    direction = (delta % 2) == 0;\n\n                uint8_t range = pgm_read_byte(&RGBLED_GRADIENT_RANGES[delta / 2]);\n                for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) {\n                    uint8_t _hue = ((uint16_t)i * (uint16_t)range) / rgblight_ranges.effect_num_leds;\n                    if (direction) {\n                        _hue = hue + _hue;\n                    } else {\n                        _hue = hue - _hue;\n                    }\n                    dprintf(\"rgblight rainbow set hsv: %d,%d,%d,%u\\n\", i, _hue, direction, range);\n                    sethsv(_hue, sat, val, i + rgblight_ranges.effect_start_pos);\n                }\n#    ifdef RGBLIGHT_LAYERS_RETAIN_VAL\n                // needed for rgblight_layers_write() to get the new val, since it reads rgblight_config.val\n                rgblight_config.val = val;\n#    endif\n                rgblight_set();\n            }\n#endif\n        }\n        rgblight_config.hue = hue;\n        rgblight_config.sat = sat;\n        rgblight_config.val = val;\n        if (write_to_eeprom) {\n            eeconfig_update_rgblight(&rgblight_config);\n            dprintf(\"rgblight set hsv [EEPROM]: %u,%u,%u\\n\", rgblight_config.hue, rgblight_config.sat, rgblight_config.val);\n        } else {\n            dprintf(\"rgblight set hsv [NOEEPROM]: %u,%u,%u\\n\", rgblight_config.hue, rgblight_config.sat, rgblight_config.val);\n        }\n    }\n}\n\nvoid rgblight_sethsv(uint8_t hue, uint8_t sat, uint8_t val) {\n    rgblight_sethsv_eeprom_helper(hue, sat, val, true);\n}\n\nvoid rgblight_sethsv_noeeprom(uint8_t hue, uint8_t sat, uint8_t val) {\n    rgblight_sethsv_eeprom_helper(hue, sat, val, false);\n}\n\nuint8_t rgblight_get_speed(void) {\n    return rgblight_config.speed;\n}\n\nvoid rgblight_set_speed_eeprom_helper(uint8_t speed, bool write_to_eeprom) {\n    rgblight_config.speed = speed;\n    if (write_to_eeprom) {\n        eeconfig_update_rgblight(&rgblight_config);\n        dprintf(\"rgblight set speed [EEPROM]: %u\\n\", rgblight_config.speed);\n    } else {\n        dprintf(\"rgblight set speed [NOEEPROM]: %u\\n\", rgblight_config.speed);\n    }\n}\n\nvoid rgblight_set_speed(uint8_t speed) {\n    rgblight_set_speed_eeprom_helper(speed, true);\n}\n\nvoid rgblight_set_speed_noeeprom(uint8_t speed) {\n    rgblight_set_speed_eeprom_helper(speed, false);\n}\n\nuint8_t rgblight_get_hue(void) {\n    return rgblight_config.hue;\n}\n\nuint8_t rgblight_get_sat(void) {\n    return rgblight_config.sat;\n}\n\nuint8_t rgblight_get_val(void) {\n    return rgblight_config.val;\n}\n\nhsv_t rgblight_get_hsv(void) {\n    return (hsv_t){rgblight_config.hue, rgblight_config.sat, rgblight_config.val};\n}\n\nvoid rgblight_setrgb(uint8_t r, uint8_t g, uint8_t b) {\n    if (!rgblight_config.enable) {\n        return;\n    }\n\n    for (uint8_t i = rgblight_ranges.effect_start_pos; i < rgblight_ranges.effect_end_pos; i++) {\n        rgblight_driver.set_color(rgblight_led_index(i), r, g, b);\n    }\n    rgblight_set();\n}\n\nvoid rgblight_setrgb_at(uint8_t r, uint8_t g, uint8_t b, uint8_t index) {\n    if (!rgblight_config.enable || index >= RGBLIGHT_LED_COUNT) {\n        return;\n    }\n\n    rgblight_driver.set_color(rgblight_led_index(index), r, g, b);\n    rgblight_set();\n}\n\nvoid rgblight_sethsv_at(uint8_t hue, uint8_t sat, uint8_t val, uint8_t index) {\n    if (!rgblight_config.enable) {\n        return;\n    }\n\n    rgb_t rgb = rgblight_hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});\n    rgblight_setrgb_at(rgb.r, rgb.g, rgb.b, index);\n}\n\n#if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_RAINBOW_MOOD) || defined(RGBLIGHT_EFFECT_RAINBOW_SWIRL) || defined(RGBLIGHT_EFFECT_SNAKE) || defined(RGBLIGHT_EFFECT_KNIGHT) || defined(RGBLIGHT_EFFECT_TWINKLE)\n\nstatic uint8_t get_interval_time(const uint8_t *default_interval_address, uint8_t velocikey_min, uint8_t velocikey_max) {\n    return\n#    ifdef VELOCIKEY_ENABLE\n        rgblight_velocikey_enabled() ? rgblight_velocikey_match_speed(velocikey_min, velocikey_max) :\n#    endif\n                                     pgm_read_byte(default_interval_address);\n}\n\n#endif\n\nvoid rgblight_setrgb_range(uint8_t r, uint8_t g, uint8_t b, uint8_t start, uint8_t end) {\n    if (!rgblight_config.enable || start < 0 || start >= end || end > RGBLIGHT_LED_COUNT) {\n        return;\n    }\n\n    for (uint8_t i = start; i < end; i++) {\n        rgblight_driver.set_color(rgblight_led_index(i), r, g, b);\n    }\n    rgblight_set();\n}\n\nvoid rgblight_sethsv_range(uint8_t hue, uint8_t sat, uint8_t val, uint8_t start, uint8_t end) {\n    if (!rgblight_config.enable) {\n        return;\n    }\n\n    rgb_t rgb = rgblight_hsv_to_rgb((hsv_t){hue, sat, val > RGBLIGHT_LIMIT_VAL ? RGBLIGHT_LIMIT_VAL : val});\n    rgblight_setrgb_range(rgb.r, rgb.g, rgb.b, start, end);\n}\n\n#ifndef RGBLIGHT_SPLIT\nvoid rgblight_setrgb_master(uint8_t r, uint8_t g, uint8_t b) {\n    rgblight_setrgb_range(r, g, b, 0, (uint8_t)RGBLIGHT_LED_COUNT / 2);\n}\n\nvoid rgblight_setrgb_slave(uint8_t r, uint8_t g, uint8_t b) {\n    rgblight_setrgb_range(r, g, b, (uint8_t)RGBLIGHT_LED_COUNT / 2, (uint8_t)RGBLIGHT_LED_COUNT);\n}\n\nvoid rgblight_sethsv_master(uint8_t hue, uint8_t sat, uint8_t val) {\n    rgblight_sethsv_range(hue, sat, val, 0, (uint8_t)RGBLIGHT_LED_COUNT / 2);\n}\n\nvoid rgblight_sethsv_slave(uint8_t hue, uint8_t sat, uint8_t val) {\n    rgblight_sethsv_range(hue, sat, val, (uint8_t)RGBLIGHT_LED_COUNT / 2, (uint8_t)RGBLIGHT_LED_COUNT);\n}\n#endif // ifndef RGBLIGHT_SPLIT\n\n#ifdef RGBLIGHT_LAYERS\nvoid rgblight_set_layer_state(uint8_t layer, bool enabled) {\n    rgblight_layer_mask_t mask = (rgblight_layer_mask_t)1 << layer;\n    if (enabled) {\n        rgblight_status.enabled_layer_mask |= mask;\n    } else {\n        rgblight_status.enabled_layer_mask &= ~mask;\n    }\n    RGBLIGHT_SPLIT_SET_CHANGE_LAYERS;\n\n    // Calling rgblight_set() here (directly or indirectly) could\n    // potentially cause timing issues when there are multiple\n    // successive calls to rgblight_set_layer_state(). Instead,\n    // set a flag and do it the next time rgblight_task() runs.\n\n    deferred_set_layer_state = true;\n}\n\nbool rgblight_get_layer_state(uint8_t layer) {\n    rgblight_layer_mask_t mask = (rgblight_layer_mask_t)1 << layer;\n    return (rgblight_status.enabled_layer_mask & mask) != 0;\n}\n\n// Write any enabled LED layers into the buffer\nstatic void rgblight_layers_write(void) {\n#    ifdef RGBLIGHT_LAYERS_RETAIN_VAL\n    uint8_t current_val = rgblight_get_val();\n#    endif\n    uint8_t i = 0;\n    // For each layer\n    for (const rgblight_segment_t *const *layer_ptr = rgblight_layers; i < RGBLIGHT_MAX_LAYERS; layer_ptr++, i++) {\n        if (!rgblight_get_layer_state(i)) {\n            continue; // Layer is disabled\n        }\n        const rgblight_segment_t *segment_ptr = pgm_read_ptr(layer_ptr);\n        if (segment_ptr == NULL) {\n            break; // No more layers\n        }\n        // For each segment\n        while (1) {\n            rgblight_segment_t segment;\n            memcpy_P(&segment, segment_ptr, sizeof(rgblight_segment_t));\n            if (segment.index == RGBLIGHT_END_SEGMENT_INDEX) {\n                break; // No more segments\n            }\n            // Write segment.count LEDs\n            int limit = MIN(segment.index + segment.count, RGBLIGHT_LED_COUNT);\n            for (int i = segment.index; i < limit; i++) {\n#    ifdef RGBLIGHT_LAYERS_RETAIN_VAL\n                sethsv(segment.hue, segment.sat, current_val, i);\n#    else\n                sethsv(segment.hue, segment.sat, segment.val, i);\n#    endif\n            }\n            segment_ptr++;\n        }\n    }\n}\n\n#    ifdef RGBLIGHT_LAYER_BLINK\nrgblight_layer_mask_t _blinking_layer_mask = 0;\nstatic uint16_t       _repeat_timer;\nstatic uint8_t        _times_remaining;\nstatic uint16_t       _dur;\n\nvoid rgblight_blink_layer(uint8_t layer, uint16_t duration_ms) {\n    rgblight_blink_layer_repeat(layer, duration_ms, 1);\n}\n\nvoid rgblight_blink_layer_repeat(uint8_t layer, uint16_t duration_ms, uint8_t times) {\n    if (times > UINT8_MAX / 2) {\n        times = UINT8_MAX / 2;\n    }\n\n    _times_remaining = times * 2;\n    _dur             = duration_ms;\n\n    rgblight_set_layer_state(layer, true);\n    _times_remaining--;\n    _blinking_layer_mask |= (rgblight_layer_mask_t)1 << layer;\n    _repeat_timer = sync_timer_read() + duration_ms;\n}\n\nvoid rgblight_unblink_layer(uint8_t layer) {\n    rgblight_set_layer_state(layer, false);\n    _blinking_layer_mask &= ~((rgblight_layer_mask_t)1 << layer);\n}\n\nvoid rgblight_unblink_all_but_layer(uint8_t layer) {\n    for (uint8_t i = 0; i < RGBLIGHT_MAX_LAYERS; i++) {\n        if (i != layer) {\n            if ((_blinking_layer_mask & (rgblight_layer_mask_t)1 << i) != 0) {\n                rgblight_unblink_layer(i);\n            }\n        }\n    }\n}\n\nvoid rgblight_blink_layer_repeat_helper(void) {\n    if (_blinking_layer_mask != 0 && timer_expired(sync_timer_read(), _repeat_timer)) {\n        for (uint8_t layer = 0; layer < RGBLIGHT_MAX_LAYERS; layer++) {\n            if ((_blinking_layer_mask & (rgblight_layer_mask_t)1 << layer) != 0) {\n                if (_times_remaining % 2 == 1) {\n                    rgblight_set_layer_state(layer, false);\n                } else {\n                    rgblight_set_layer_state(layer, true);\n                }\n            }\n        }\n        _times_remaining--;\n        if (_times_remaining <= 0) {\n            _blinking_layer_mask = 0;\n        } else {\n            _repeat_timer = sync_timer_read() + _dur;\n        }\n    }\n}\n#    endif\n\n#endif\n\n#ifdef RGBLIGHT_SLEEP\n\nvoid rgblight_suspend(void) {\n    rgblight_timer_disable();\n    if (!is_suspended) {\n        is_suspended        = true;\n        pre_suspend_enabled = rgblight_config.enable;\n\n#    ifdef RGBLIGHT_LAYER_BLINK\n        // make sure any layer blinks don't come back after suspend\n        rgblight_status.enabled_layer_mask &= ~_blinking_layer_mask;\n        _blinking_layer_mask = 0;\n#    endif\n\n        rgblight_disable_noeeprom();\n    }\n}\n\nvoid rgblight_wakeup(void) {\n    is_suspended = false;\n\n    if (pre_suspend_enabled) {\n        rgblight_enable_noeeprom();\n    }\n#    ifdef RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF\n    // Need this or else the LEDs won't be set\n    else if (rgblight_status.enabled_layer_mask != 0) {\n        rgblight_set();\n    }\n#    endif\n\n    rgblight_timer_enable();\n}\n\n#endif\n\nvoid rgblight_set(void) {\n    if (!rgblight_config.enable) {\n        for (uint8_t i = rgblight_ranges.effect_start_pos; i < rgblight_ranges.effect_end_pos; i++) {\n            rgblight_driver.set_color(rgblight_led_index(i), 0, 0, 0);\n        }\n    }\n\n#ifdef RGBLIGHT_LAYERS\n    if (rgblight_layers != NULL\n#    if !defined(RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF)\n        && rgblight_config.enable\n#    elif defined(RGBLIGHT_SLEEP)\n        && !is_suspended\n#    endif\n    ) {\n        rgblight_layers_write();\n    }\n#endif\n\n    rgblight_driver.flush();\n}\n\n#ifdef RGBLIGHT_SPLIT\n/* for split keyboard master side */\nuint8_t rgblight_get_change_flags(void) {\n    return rgblight_status.change_flags;\n}\n\nvoid rgblight_clear_change_flags(void) {\n    rgblight_status.change_flags = 0;\n}\n\nvoid rgblight_get_syncinfo(rgblight_syncinfo_t *syncinfo) {\n    syncinfo->config = rgblight_config;\n    syncinfo->status = rgblight_status;\n}\n\n/* for split keyboard slave side */\nvoid rgblight_update_sync(rgblight_syncinfo_t *syncinfo, bool write_to_eeprom) {\n#    ifdef RGBLIGHT_LAYERS\n    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_LAYERS) {\n        rgblight_status.enabled_layer_mask = syncinfo->status.enabled_layer_mask;\n    }\n#    endif\n    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_MODE) {\n        if (syncinfo->config.enable) {\n            rgblight_config.enable = 1; // == rgblight_enable_noeeprom();\n            rgblight_mode_eeprom_helper(syncinfo->config.mode, write_to_eeprom);\n        } else {\n            rgblight_disable_noeeprom();\n        }\n    }\n    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_HSVS) {\n        rgblight_sethsv_eeprom_helper(syncinfo->config.hue, syncinfo->config.sat, syncinfo->config.val, write_to_eeprom);\n        // rgblight_config.speed = config->speed; // NEED???\n    }\n#    ifdef RGBLIGHT_USE_TIMER\n    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_CHANGE_TIMER) {\n        if (syncinfo->status.timer_enabled) {\n            rgblight_timer_enable();\n        } else {\n            rgblight_timer_disable();\n        }\n    }\n#        ifndef RGBLIGHT_SPLIT_NO_ANIMATION_SYNC\n    if (syncinfo->status.change_flags & RGBLIGHT_STATUS_ANIMATION_TICK) {\n        animation_status.restart = true;\n    }\n#        endif /* RGBLIGHT_SPLIT_NO_ANIMATION_SYNC */\n#    endif     /* RGBLIGHT_USE_TIMER */\n}\n#endif /* RGBLIGHT_SPLIT */\n\n#ifdef RGBLIGHT_USE_TIMER\n\ntypedef void (*effect_func_t)(animation_status_t *anim);\n\n// Animation timer -- use system timer (AVR Timer0)\nvoid rgblight_timer_init(void) {\n    rgblight_status.timer_enabled = false;\n    RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE;\n}\nvoid rgblight_timer_enable(void) {\n    if (!is_static_effect(rgblight_config.mode)) {\n        rgblight_status.timer_enabled = true;\n    }\n    animation_status.last_timer = sync_timer_read();\n    RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE;\n    dprintf(\"rgblight timer enabled.\\n\");\n}\nvoid rgblight_timer_disable(void) {\n    rgblight_status.timer_enabled = false;\n    RGBLIGHT_SPLIT_SET_CHANGE_TIMER_ENABLE;\n    dprintf(\"rgblight timer disable.\\n\");\n}\nvoid rgblight_timer_toggle(void) {\n    dprintf(\"rgblight timer toggle.\\n\");\n    if (rgblight_status.timer_enabled) {\n        rgblight_timer_disable();\n    } else {\n        rgblight_timer_enable();\n    }\n}\n\nvoid rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b) {\n    rgblight_enable();\n    rgblight_mode(RGBLIGHT_MODE_STATIC_LIGHT);\n    rgblight_setrgb(r, g, b);\n}\n\nstatic void rgblight_effect_dummy(animation_status_t *anim) {\n    // do nothing\n    /********\n    dprintf(\"rgblight_task() what happened?\\n\");\n    dprintf(\"is_static_effect %d\\n\", is_static_effect(rgblight_config.mode));\n    dprintf(\"mode = %d, base_mode = %d, timer_enabled %d, \",\n            rgblight_config.mode, rgblight_status.base_mode,\n            rgblight_status.timer_enabled);\n    dprintf(\"last_timer = %d\\n\",anim->last_timer);\n    **/\n}\n\nvoid rgblight_timer_task(void) {\n    if (rgblight_status.timer_enabled) {\n        effect_func_t effect_func   = rgblight_effect_dummy;\n        uint16_t      interval_time = 2000; // dummy interval\n        uint8_t       delta         = rgblight_config.mode - rgblight_status.base_mode;\n        animation_status.delta      = delta;\n\n        // static light mode, do nothing here\n        if (1 == 0) { // dummy\n        }\n#    ifdef RGBLIGHT_EFFECT_BREATHING\n        else if (rgblight_status.base_mode == RGBLIGHT_MODE_BREATHING) {\n            // breathing mode\n            interval_time = get_interval_time(&RGBLED_BREATHING_INTERVALS[delta], 1, 100);\n            effect_func   = rgblight_effect_breathing;\n        }\n#    endif\n#    ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD\n        else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_MOOD) {\n            // rainbow mood mode\n            interval_time = get_interval_time(&RGBLED_RAINBOW_MOOD_INTERVALS[delta], 5, 100);\n            effect_func   = rgblight_effect_rainbow_mood;\n        }\n#    endif\n#    ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL\n        else if (rgblight_status.base_mode == RGBLIGHT_MODE_RAINBOW_SWIRL) {\n            // rainbow swirl mode\n            interval_time = get_interval_time(&RGBLED_RAINBOW_SWIRL_INTERVALS[delta / 2], 1, 100);\n            effect_func   = rgblight_effect_rainbow_swirl;\n        }\n#    endif\n#    ifdef RGBLIGHT_EFFECT_SNAKE\n        else if (rgblight_status.base_mode == RGBLIGHT_MODE_SNAKE) {\n            // snake mode\n            interval_time = get_interval_time(&RGBLED_SNAKE_INTERVALS[delta / 2], 1, 200);\n            effect_func   = rgblight_effect_snake;\n        }\n#    endif\n#    ifdef RGBLIGHT_EFFECT_KNIGHT\n        else if (rgblight_status.base_mode == RGBLIGHT_MODE_KNIGHT) {\n            // knight mode\n            interval_time = get_interval_time(&RGBLED_KNIGHT_INTERVALS[delta], 5, 100);\n            effect_func   = rgblight_effect_knight;\n        }\n#    endif\n#    ifdef RGBLIGHT_EFFECT_CHRISTMAS\n        else if (rgblight_status.base_mode == RGBLIGHT_MODE_CHRISTMAS) {\n            // christmas mode\n            interval_time = RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL;\n            effect_func   = (effect_func_t)rgblight_effect_christmas;\n        }\n#    endif\n#    ifdef RGBLIGHT_EFFECT_RGB_TEST\n        else if (rgblight_status.base_mode == RGBLIGHT_MODE_RGB_TEST) {\n            // RGB test mode\n            interval_time = pgm_read_word(&RGBLED_RGBTEST_INTERVALS[0]);\n            effect_func   = (effect_func_t)rgblight_effect_rgbtest;\n        }\n#    endif\n#    ifdef RGBLIGHT_EFFECT_ALTERNATING\n        else if (rgblight_status.base_mode == RGBLIGHT_MODE_ALTERNATING) {\n            interval_time = 500;\n            effect_func   = (effect_func_t)rgblight_effect_alternating;\n        }\n#    endif\n#    ifdef RGBLIGHT_EFFECT_TWINKLE\n        else if (rgblight_status.base_mode == RGBLIGHT_MODE_TWINKLE) {\n            interval_time = get_interval_time(&RGBLED_TWINKLE_INTERVALS[delta % 3], 5, 30);\n            effect_func   = (effect_func_t)rgblight_effect_twinkle;\n        }\n#    endif\n        if (animation_status.restart) {\n            animation_status.restart    = false;\n            animation_status.last_timer = sync_timer_read();\n            animation_status.pos16      = 0; // restart signal to local each effect\n        }\n        uint16_t now = sync_timer_read();\n        if (timer_expired(now, animation_status.last_timer)) {\n#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)\n            static uint16_t report_last_timer = 0;\n            static bool     tick_flag         = false;\n            uint16_t        oldpos16;\n            if (tick_flag) {\n                tick_flag = false;\n                if (timer_expired(now, report_last_timer)) {\n                    report_last_timer += 30000;\n                    dprintf(\"rgblight animation tick report to slave\\n\");\n                    RGBLIGHT_SPLIT_ANIMATION_TICK;\n                }\n            }\n            oldpos16 = animation_status.pos16;\n#    endif\n            animation_status.last_timer += interval_time;\n            effect_func(&animation_status);\n#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)\n            if (animation_status.pos16 == 0 && oldpos16 != 0) {\n                tick_flag = true;\n            }\n#    endif\n        }\n    }\n\n#    ifdef RGBLIGHT_LAYERS\n#        ifdef RGBLIGHT_LAYER_BLINK\n    rgblight_blink_layer_repeat_helper();\n#        endif\n\n    if (deferred_set_layer_state) {\n        deferred_set_layer_state = false;\n\n        // Static modes don't have a ticker running to update the LEDs\n        if (rgblight_status.timer_enabled == false) {\n            rgblight_mode_noeeprom(rgblight_config.mode);\n        }\n\n#        ifdef RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF\n        // If not enabled, then nothing else will actually set the LEDs...\n        if (!rgblight_config.enable) {\n            rgblight_set();\n        }\n#        endif\n    }\n#    endif\n}\n\n#endif /* RGBLIGHT_USE_TIMER */\n\n#if defined(RGBLIGHT_EFFECT_BREATHING) || defined(RGBLIGHT_EFFECT_TWINKLE)\n\n#    ifndef RGBLIGHT_EFFECT_BREATHE_CENTER\n#        ifndef RGBLIGHT_BREATHE_TABLE_SIZE\n#            define RGBLIGHT_BREATHE_TABLE_SIZE 256 // 256 or 128 or 64\n#        endif\n#        include <rgblight_breathe_table.h>\n#    endif\n\nstatic uint8_t breathe_calc(uint8_t pos) {\n    // http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/\n#    ifdef RGBLIGHT_EFFECT_BREATHE_TABLE\n    return pgm_read_byte(&rgblight_effect_breathe_table[pos / table_scale]);\n#    else\n    return (exp(sin((pos / 255.0) * M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER / M_E) * (RGBLIGHT_EFFECT_BREATHE_MAX / (M_E - 1 / M_E));\n#    endif\n}\n\n#endif\n\n// Effects\n#ifdef RGBLIGHT_EFFECT_BREATHING\n\n__attribute__((weak)) const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5};\n\nvoid rgblight_effect_breathing(animation_status_t *anim) {\n    uint8_t val = breathe_calc(anim->pos);\n    rgblight_sethsv_noeeprom_old(rgblight_config.hue, rgblight_config.sat, val);\n    anim->pos = (anim->pos + 1);\n}\n#endif\n\n#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD\n__attribute__((weak)) const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[] PROGMEM = {120, 60, 30};\n\nvoid rgblight_effect_rainbow_mood(animation_status_t *anim) {\n    rgblight_sethsv_noeeprom_old(anim->current_hue, rgblight_config.sat, rgblight_config.val);\n    anim->current_hue++;\n}\n#endif\n\n#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL\n#    ifndef RGBLIGHT_RAINBOW_SWIRL_RANGE\n#        define RGBLIGHT_RAINBOW_SWIRL_RANGE 255\n#    endif\n\n__attribute__((weak)) const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[] PROGMEM = {100, 50, 20};\n\nvoid rgblight_effect_rainbow_swirl(animation_status_t *anim) {\n    uint8_t hue;\n    uint8_t i;\n\n    for (i = 0; i < rgblight_ranges.effect_num_leds; i++) {\n        hue = (RGBLIGHT_RAINBOW_SWIRL_RANGE / rgblight_ranges.effect_num_leds * i + anim->current_hue);\n        sethsv(hue, rgblight_config.sat, rgblight_config.val, i + rgblight_ranges.effect_start_pos);\n    }\n    rgblight_set();\n\n    if (anim->delta % 2) {\n        anim->current_hue++;\n    } else {\n        anim->current_hue--;\n    }\n}\n#endif\n\n#ifdef RGBLIGHT_EFFECT_SNAKE\n__attribute__((weak)) const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20};\n\nvoid rgblight_effect_snake(animation_status_t *anim) {\n    static uint8_t pos = 0;\n    uint8_t        i, j;\n    int8_t         k;\n    int8_t         increment = 1;\n\n    if (anim->delta % 2) {\n        increment = -1;\n    }\n\n#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)\n    if (anim->pos == 0) { // restart signal\n        if (increment == 1) {\n            pos = rgblight_ranges.effect_num_leds - 1;\n        } else {\n            pos = 0;\n        }\n        anim->pos = 1;\n    }\n#    endif\n\n    for (i = 0; i < rgblight_ranges.effect_num_leds; i++) {\n        rgblight_driver.set_color(rgblight_led_index(i + rgblight_ranges.effect_start_pos), 0, 0, 0);\n\n        for (j = 0; j < RGBLIGHT_EFFECT_SNAKE_LENGTH; j++) {\n            k = pos + j * increment;\n            if (k > RGBLIGHT_LED_COUNT) {\n                k = k % (RGBLIGHT_LED_COUNT);\n            }\n            if (k < 0) {\n                k = k + rgblight_ranges.effect_num_leds;\n            }\n            if (i == k) {\n                sethsv(rgblight_config.hue, rgblight_config.sat, (uint8_t)(rgblight_config.val * (RGBLIGHT_EFFECT_SNAKE_LENGTH - j) / RGBLIGHT_EFFECT_SNAKE_LENGTH), i + rgblight_ranges.effect_start_pos);\n            }\n        }\n    }\n    rgblight_set();\n    if (increment == 1) {\n        if (pos - RGBLIGHT_EFFECT_SNAKE_INCREMENT < 0) {\n            pos = rgblight_ranges.effect_num_leds - 1;\n#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)\n            anim->pos = 0;\n#    endif\n        } else {\n            pos -= RGBLIGHT_EFFECT_SNAKE_INCREMENT;\n#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)\n            anim->pos = 1;\n#    endif\n        }\n    } else {\n        pos = (pos + RGBLIGHT_EFFECT_SNAKE_INCREMENT) % rgblight_ranges.effect_num_leds;\n#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)\n        anim->pos = pos;\n#    endif\n    }\n}\n#endif\n\n#ifdef RGBLIGHT_EFFECT_KNIGHT\n__attribute__((weak)) const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31};\n\nvoid rgblight_effect_knight(animation_status_t *anim) {\n    static int8_t low_bound  = 0;\n    static int8_t high_bound = RGBLIGHT_EFFECT_KNIGHT_LENGTH - 1;\n    static int8_t increment  = RGBLIGHT_EFFECT_KNIGHT_INCREMENT;\n    uint8_t       i, cur;\n\n#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)\n    if (anim->pos == 0) { // restart signal\n        anim->pos  = 1;\n        low_bound  = 0;\n        high_bound = RGBLIGHT_EFFECT_KNIGHT_LENGTH - 1;\n        increment  = 1;\n    }\n#    endif\n    // Set all the LEDs to 0\n    for (i = rgblight_ranges.effect_start_pos; i < rgblight_ranges.effect_end_pos; i++) {\n        rgblight_driver.set_color(rgblight_led_index(i), 0, 0, 0);\n    }\n    // Determine which LEDs should be lit up\n    for (i = 0; i < RGBLIGHT_EFFECT_KNIGHT_LED_NUM; i++) {\n        cur = (i + RGBLIGHT_EFFECT_KNIGHT_OFFSET) % rgblight_ranges.effect_num_leds + rgblight_ranges.effect_start_pos;\n\n        if (i >= low_bound && i <= high_bound) {\n            sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, cur);\n        } else {\n            rgblight_driver.set_color(rgblight_led_index(cur), 0, 0, 0);\n        }\n    }\n    rgblight_set();\n\n    // Move from low_bound to high_bound changing the direction we increment each\n    // time a boundary is hit.\n    low_bound += increment;\n    high_bound += increment;\n\n    if (high_bound <= 0 || low_bound >= RGBLIGHT_EFFECT_KNIGHT_LED_NUM - 1) {\n        increment = -increment;\n#    if defined(RGBLIGHT_SPLIT) && !defined(RGBLIGHT_SPLIT_NO_ANIMATION_SYNC)\n        if (increment == 1) {\n            anim->pos = 0;\n        }\n#    endif\n    }\n}\n#endif\n\n#ifdef RGBLIGHT_EFFECT_CHRISTMAS\n#    define CUBED(x) ((x) * (x) * (x))\n\n/**\n * Christmas lights effect, with a smooth animation between red & green.\n */\nvoid rgblight_effect_christmas(animation_status_t *anim) {\n    static int8_t increment = 1;\n    const uint8_t max_pos   = 32;\n    const uint8_t hue_green = 85;\n\n    uint32_t xa;\n    uint8_t  hue, val;\n    uint8_t  i;\n\n    // The effect works by animating anim->pos from 0 to 32 and back to 0.\n    // The pos is used in a cubic bezier formula to ease-in-out between red and green, leaving the interpolated colors visible as short as possible.\n    xa  = CUBED((uint32_t)anim->pos);\n    hue = ((uint32_t)hue_green) * xa / (xa + CUBED((uint32_t)(max_pos - anim->pos)));\n    // Additionally, these interpolated colors get shown with a slightly darker value, to make them less prominent than the main colors.\n    val = 255 - (3 * (hue < hue_green / 2 ? hue : hue_green - hue) / 2);\n\n    for (i = 0; i < rgblight_ranges.effect_num_leds; i++) {\n        uint8_t local_hue = (i / RGBLIGHT_EFFECT_CHRISTMAS_STEP) % 2 ? hue : hue_green - hue;\n        sethsv(local_hue, rgblight_config.sat, val, i + rgblight_ranges.effect_start_pos);\n    }\n    rgblight_set();\n\n    if (anim->pos == 0) {\n        increment = 1;\n    } else if (anim->pos == max_pos) {\n        increment = -1;\n    }\n    anim->pos += increment;\n}\n#endif\n\n#ifdef RGBLIGHT_EFFECT_RGB_TEST\n__attribute__((weak)) const uint16_t RGBLED_RGBTEST_INTERVALS[] PROGMEM = {1024};\n\nvoid rgblight_effect_rgbtest(animation_status_t *anim) {\n    uint8_t val = rgblight_get_val();\n\n    uint8_t r = anim->pos & 1 ? val : 0;\n    uint8_t g = anim->pos & 2 ? val : 0;\n    uint8_t b = anim->pos & 4 ? val : 0;\n    rgblight_setrgb(r, g, b);\n    anim->pos = (anim->pos + 1) % 8;\n}\n#endif\n\n#ifdef RGBLIGHT_EFFECT_ALTERNATING\nvoid rgblight_effect_alternating(animation_status_t *anim) {\n    for (int i = 0; i < rgblight_ranges.effect_num_leds; i++) {\n        if (i < rgblight_ranges.effect_num_leds / 2 && anim->pos) {\n            sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, i + rgblight_ranges.effect_start_pos);\n        } else if (i >= rgblight_ranges.effect_num_leds / 2 && !anim->pos) {\n            sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, i + rgblight_ranges.effect_start_pos);\n        } else {\n            sethsv(rgblight_config.hue, rgblight_config.sat, 0, i + rgblight_ranges.effect_start_pos);\n        }\n    }\n    rgblight_set();\n    anim->pos = (anim->pos + 1) % 2;\n}\n#endif\n\n#ifdef RGBLIGHT_EFFECT_TWINKLE\n__attribute__((weak)) const uint8_t RGBLED_TWINKLE_INTERVALS[] PROGMEM = {30, 15, 5};\n\ntypedef struct PACKED {\n    hsv_t   hsv;\n    uint8_t life;\n    uint8_t max_life;\n} TwinkleState;\n\nstatic TwinkleState led_twinkle_state[RGBLIGHT_LED_COUNT];\n\nvoid rgblight_effect_twinkle(animation_status_t *anim) {\n    const bool random_color = anim->delta / 3;\n    const bool restart      = anim->pos == 0;\n    anim->pos               = 1;\n\n    const uint8_t bottom = breathe_calc(0);\n    const uint8_t top    = breathe_calc(127);\n\n    uint8_t frac(uint8_t n, uint8_t d) {\n        return (uint16_t)255 * n / d;\n    }\n    uint8_t scale(uint16_t v, uint8_t scale) {\n        return (v * scale) >> 8;\n    }\n\n    const uint8_t trigger = scale((uint16_t)0xFF * RGBLIGHT_EFFECT_TWINKLE_PROBABILITY, 127 + rgblight_config.val / 2);\n\n    for (uint8_t i = 0; i < rgblight_ranges.effect_num_leds; i++) {\n        TwinkleState *t = &(led_twinkle_state[i]);\n        hsv_t *       c = &(t->hsv);\n\n        if (!random_color) {\n            c->h = rgblight_config.hue;\n            c->s = rgblight_config.sat;\n        }\n\n        if (restart) {\n            // Restart\n            t->life = 0;\n            c->v    = 0;\n        } else if (t->life) {\n            // This LED is already on, either brightening or dimming\n            t->life--;\n            uint8_t unscaled = frac(breathe_calc(frac(t->life, t->max_life)) - bottom, top - bottom);\n            c->v             = scale(rgblight_config.val, unscaled);\n        } else if ((rand() % 0xFF) < trigger) {\n            // This LED is off, but was randomly selected to start brightening\n            if (random_color) {\n                c->h = rand() % 0xFF;\n                c->s = (rand() % (rgblight_config.sat / 2)) + (rgblight_config.sat / 2);\n            }\n            c->v        = 0;\n            t->max_life = MAX(20, MIN(RGBLIGHT_EFFECT_TWINKLE_LIFE, rgblight_config.val));\n            t->life     = t->max_life;\n        } else {\n            // This LED is off, and was NOT selected to start brightening\n        }\n\n        sethsv(c->h, c->s, c->v, i + rgblight_ranges.effect_start_pos);\n    }\n\n    rgblight_set();\n}\n#endif\n\nvoid preprocess_rgblight(void) {\n#ifdef VELOCIKEY_ENABLE\n    if (rgblight_velocikey_enabled()) {\n        rgblight_velocikey_accelerate();\n    }\n#endif\n}\n\nvoid rgblight_task(void) {\n#ifdef RGBLIGHT_USE_TIMER\n    rgblight_timer_task();\n#endif\n\n#ifdef VELOCIKEY_ENABLE\n    if (rgblight_velocikey_enabled()) {\n        rgblight_velocikey_decelerate();\n    }\n#endif\n}\n\n#ifdef VELOCIKEY_ENABLE\n#    define TYPING_SPEED_MAX_VALUE 200\n\nstatic uint8_t typing_speed = 0;\n\nbool rgblight_velocikey_enabled(void) {\n    return rgblight_config.velocikey;\n}\n\nvoid rgblight_velocikey_toggle(void) {\n    dprintf(\"rgblight velocikey toggle [EEPROM]: rgblight_config.velocikey = %u\\n\", !rgblight_config.velocikey);\n    rgblight_config.velocikey = !rgblight_config.velocikey;\n    eeconfig_update_rgblight_current();\n}\n\nvoid rgblight_velocikey_accelerate(void) {\n    if (typing_speed < TYPING_SPEED_MAX_VALUE) typing_speed += (TYPING_SPEED_MAX_VALUE / 100);\n}\n\nvoid rgblight_velocikey_decelerate(void) {\n    static uint16_t decay_timer = 0;\n\n    if (timer_elapsed(decay_timer) > 500 || decay_timer == 0) {\n        if (typing_speed > 0) typing_speed -= 1;\n        // Decay a little faster at half of max speed\n        if (typing_speed > TYPING_SPEED_MAX_VALUE / 2) typing_speed -= 1;\n        // Decay even faster at 3/4 of max speed\n        if (typing_speed > TYPING_SPEED_MAX_VALUE / 4 * 3) typing_speed -= 2;\n        decay_timer = timer_read();\n    }\n}\n\nuint8_t rgblight_velocikey_match_speed(uint8_t minValue, uint8_t maxValue) {\n    return MAX(minValue, maxValue - (maxValue - minValue) * ((float)typing_speed / TYPING_SPEED_MAX_VALUE));\n}\n\n#endif\n"
  },
  {
    "path": "quantum/rgblight/rgblight.h",
    "content": "/* Copyright 2017 Yang Liu\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"compiler_support.h\"\n\n// DEPRECATED DEFINES - DO NOT USE\n#if defined(RGBLED_NUM)\n#    define RGBLIGHT_LED_COUNT RGBLED_NUM\n#endif\n// ========\n\n/***** rgblight_mode(mode)/rgblight_mode_noeeprom(mode) ****\n\n old mode number (before 0.6.117) to new mode name table\n\n|-----------------|-----------------------------------|\n| old mode number | new mode name                     |\n|-----------------|-----------------------------------|\n|        1        | RGBLIGHT_MODE_STATIC_LIGHT        |\n|        2        | RGBLIGHT_MODE_BREATHING           |\n|        3        | RGBLIGHT_MODE_BREATHING + 1       |\n|        4        | RGBLIGHT_MODE_BREATHING + 2       |\n|        5        | RGBLIGHT_MODE_BREATHING + 3       |\n|        6        | RGBLIGHT_MODE_RAINBOW_MOOD        |\n|        7        | RGBLIGHT_MODE_RAINBOW_MOOD + 1    |\n|        8        | RGBLIGHT_MODE_RAINBOW_MOOD + 2    |\n|        9        | RGBLIGHT_MODE_RAINBOW_SWIRL       |\n|       10        | RGBLIGHT_MODE_RAINBOW_SWIRL + 1   |\n|       11        | RGBLIGHT_MODE_RAINBOW_SWIRL + 2   |\n|       12        | RGBLIGHT_MODE_RAINBOW_SWIRL + 3   |\n|       13        | RGBLIGHT_MODE_RAINBOW_SWIRL + 4   |\n|       14        | RGBLIGHT_MODE_RAINBOW_SWIRL + 5   |\n|       15        | RGBLIGHT_MODE_SNAKE               |\n|       16        | RGBLIGHT_MODE_SNAKE + 1           |\n|       17        | RGBLIGHT_MODE_SNAKE + 2           |\n|       18        | RGBLIGHT_MODE_SNAKE + 3           |\n|       19        | RGBLIGHT_MODE_SNAKE + 4           |\n|       20        | RGBLIGHT_MODE_SNAKE + 5           |\n|       21        | RGBLIGHT_MODE_KNIGHT              |\n|       22        | RGBLIGHT_MODE_KNIGHT + 1          |\n|       23        | RGBLIGHT_MODE_KNIGHT + 2          |\n|       24        | RGBLIGHT_MODE_CHRISTMAS           |\n|       25        | RGBLIGHT_MODE_STATIC_GRADIENT     |\n|       26        | RGBLIGHT_MODE_STATIC_GRADIENT + 1 |\n|       27        | RGBLIGHT_MODE_STATIC_GRADIENT + 2 |\n|       28        | RGBLIGHT_MODE_STATIC_GRADIENT + 3 |\n|       29        | RGBLIGHT_MODE_STATIC_GRADIENT + 4 |\n|       30        | RGBLIGHT_MODE_STATIC_GRADIENT + 5 |\n|       31        | RGBLIGHT_MODE_STATIC_GRADIENT + 6 |\n|       32        | RGBLIGHT_MODE_STATIC_GRADIENT + 7 |\n|       33        | RGBLIGHT_MODE_STATIC_GRADIENT + 8 |\n|       34        | RGBLIGHT_MODE_STATIC_GRADIENT + 9 |\n|       35        | RGBLIGHT_MODE_RGB_TEST            |\n|       36        | RGBLIGHT_MODE_ALTERNATING         |\n|       37        | RGBLIGHT_MODE_TWINKLE             |\n|       38        | RGBLIGHT_MODE_TWINKLE + 1         |\n|       39        | RGBLIGHT_MODE_TWINKLE + 2         |\n|       40        | RGBLIGHT_MODE_TWINKLE + 3         |\n|       41        | RGBLIGHT_MODE_TWINKLE + 4         |\n|       42        | RGBLIGHT_MODE_TWINKLE + 5         |\n|-----------------|-----------------------------------|\n *****/\n\n// clang-format off\n\n// check dynamic animation effects chose ?\n#if  defined(RGBLIGHT_EFFECT_BREATHING)     \\\n  || defined(RGBLIGHT_EFFECT_RAINBOW_MOOD)  \\\n  || defined(RGBLIGHT_EFFECT_RAINBOW_SWIRL) \\\n  || defined(RGBLIGHT_EFFECT_SNAKE)         \\\n  || defined(RGBLIGHT_EFFECT_KNIGHT)        \\\n  || defined(RGBLIGHT_EFFECT_CHRISTMAS)     \\\n  || defined(RGBLIGHT_EFFECT_RGB_TEST)      \\\n  || defined(RGBLIGHT_EFFECT_ALTERNATING)   \\\n  || defined(RGBLIGHT_EFFECT_TWINKLE)\n#    define RGBLIGHT_USE_TIMER\n#endif\n\n// clang-format on\n\n#define _RGBM_SINGLE_STATIC(sym) RGBLIGHT_MODE_##sym,\n#define _RGBM_SINGLE_DYNAMIC(sym) RGBLIGHT_MODE_##sym,\n#define _RGBM_MULTI_STATIC(sym) RGBLIGHT_MODE_##sym,\n#define _RGBM_MULTI_DYNAMIC(sym) RGBLIGHT_MODE_##sym,\n#define _RGBM_TMP_STATIC(sym, msym) RGBLIGHT_MODE_##sym,\n#define _RGBM_TMP_DYNAMIC(sym, msym) RGBLIGHT_MODE_##sym,\nenum RGBLIGHT_EFFECT_MODE {\n    RGBLIGHT_MODE_zero = 0,\n#include \"rgblight_modes.h\"\n    RGBLIGHT_MODE_last\n};\n\n#define RGBLIGHT_MODES (RGBLIGHT_MODE_last - 1)\n\n// sample: #define RGBLIGHT_EFFECT_BREATHE_CENTER   1.85\n\n#ifndef RGBLIGHT_EFFECT_BREATHE_MAX\n#    define RGBLIGHT_EFFECT_BREATHE_MAX 255 // 0-255\n#endif\n\n#ifndef RGBLIGHT_EFFECT_SNAKE_LENGTH\n#    define RGBLIGHT_EFFECT_SNAKE_LENGTH 4\n#endif\n\n#ifndef RGBLIGHT_EFFECT_SNAKE_INCREMENT\n#    define RGBLIGHT_EFFECT_SNAKE_INCREMENT 1\n#endif\n\n#ifndef RGBLIGHT_EFFECT_KNIGHT_LENGTH\n#    define RGBLIGHT_EFFECT_KNIGHT_LENGTH 3\n#endif\n\n#ifndef RGBLIGHT_EFFECT_KNIGHT_INCREMENT\n#    define RGBLIGHT_EFFECT_KNIGHT_INCREMENT 1\n#endif\n\n#ifndef RGBLIGHT_EFFECT_KNIGHT_OFFSET\n#    define RGBLIGHT_EFFECT_KNIGHT_OFFSET 0\n#endif\n\n#ifndef RGBLIGHT_EFFECT_KNIGHT_LED_NUM\n#    define RGBLIGHT_EFFECT_KNIGHT_LED_NUM (rgblight_ranges.effect_num_leds)\n#endif\n\n#ifndef RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL\n#    define RGBLIGHT_EFFECT_CHRISTMAS_INTERVAL 40\n#endif\n\n#ifndef RGBLIGHT_EFFECT_CHRISTMAS_STEP\n#    define RGBLIGHT_EFFECT_CHRISTMAS_STEP 2\n#endif\n\n#ifndef RGBLIGHT_EFFECT_TWINKLE_LIFE\n#    define RGBLIGHT_EFFECT_TWINKLE_LIFE 200\n#endif\n\n#ifndef RGBLIGHT_EFFECT_TWINKLE_PROBABILITY\n#    define RGBLIGHT_EFFECT_TWINKLE_PROBABILITY 1 / 127\n#endif\n\n#ifndef RGBLIGHT_HUE_STEP\n#    define RGBLIGHT_HUE_STEP 8\n#endif\n#ifndef RGBLIGHT_SAT_STEP\n#    define RGBLIGHT_SAT_STEP 17\n#endif\n#ifndef RGBLIGHT_VAL_STEP\n#    define RGBLIGHT_VAL_STEP 17\n#endif\n#ifndef RGBLIGHT_LIMIT_VAL\n#    define RGBLIGHT_LIMIT_VAL 255\n#endif\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"rgblight_drivers.h\"\n#include \"progmem.h\"\n#include \"color.h\"\n\n#ifdef RGBLIGHT_LAYERS\ntypedef struct {\n    uint8_t index; // The first LED to light\n    uint8_t count; // The number of LEDs to light\n    uint8_t hue;\n    uint8_t sat;\n    uint8_t val;\n} rgblight_segment_t;\n\n// rgblight_set_layer_state doesn't take effect until the next time\n// rgblight_task runs, so timers must be enabled for layers to work.\n#    define RGBLIGHT_USE_TIMER\n\n#    define RGBLIGHT_END_SEGMENT_INDEX (255)\n#    define RGBLIGHT_END_SEGMENTS \\\n        { RGBLIGHT_END_SEGMENT_INDEX, 0, 0, 0 }\n#    ifndef RGBLIGHT_MAX_LAYERS\n#        define RGBLIGHT_MAX_LAYERS 8\n#    endif\n#    if RGBLIGHT_MAX_LAYERS <= 0\n#        error invalid RGBLIGHT_MAX_LAYERS value (must be >= 1)\n#    elif RGBLIGHT_MAX_LAYERS <= 8\ntypedef uint8_t rgblight_layer_mask_t;\n#    elif RGBLIGHT_MAX_LAYERS <= 16\ntypedef uint16_t rgblight_layer_mask_t;\n#    elif RGBLIGHT_MAX_LAYERS <= 32\ntypedef uint32_t rgblight_layer_mask_t;\n#    else\n#        error invalid RGBLIGHT_MAX_LAYERS value (must be <= 32)\n#    endif\n#    define RGBLIGHT_LAYER_SEGMENTS(...) \\\n        { __VA_ARGS__, RGBLIGHT_END_SEGMENTS }\n#    define RGBLIGHT_LAYERS_LIST(...) \\\n        { __VA_ARGS__, NULL }\n\n// Get/set enabled rgblight layers\nvoid rgblight_set_layer_state(uint8_t layer, bool enabled);\nbool rgblight_get_layer_state(uint8_t layer);\n\n// Point this to an array of rgblight_segment_t arrays in keyboard_post_init_user to use rgblight layers\nextern const rgblight_segment_t *const *rgblight_layers;\n\n#    ifdef RGBLIGHT_LAYER_BLINK\n#        define RGBLIGHT_USE_TIMER\nvoid rgblight_blink_layer(uint8_t layer, uint16_t duration_ms);\nvoid rgblight_blink_layer_repeat(uint8_t layer, uint16_t duration_ms, uint8_t times);\n/**\n * \\brief Stop blinking on one layer.\n *\n * Stop a layer that is blinking. If the layer is not blinking it will\n * be unaffected.\n *\n * \\param layer Layer number to stop blinking.\n */\nvoid rgblight_unblink_layer(uint8_t layer);\n/**\n * \\brief Stop blinking all layers except one.\n *\n * Stop all layers that are blinking except for one specific layer.\n * Layers that are not blinking are unaffected.\n *\n * \\param layer Layer number to keep blinking.\n */\nvoid rgblight_unblink_all_but_layer(uint8_t layer);\n#    endif\n\n#endif\n\nextern const uint8_t  RGBLED_BREATHING_INTERVALS[4] PROGMEM;\nextern const uint8_t  RGBLED_RAINBOW_MOOD_INTERVALS[3] PROGMEM;\nextern const uint8_t  RGBLED_RAINBOW_SWIRL_INTERVALS[3] PROGMEM;\nextern const uint8_t  RGBLED_SNAKE_INTERVALS[3] PROGMEM;\nextern const uint8_t  RGBLED_KNIGHT_INTERVALS[3] PROGMEM;\nextern const uint16_t RGBLED_RGBTEST_INTERVALS[1] PROGMEM;\nextern const uint8_t  RGBLED_TWINKLE_INTERVALS[3] PROGMEM;\nextern bool           is_rgblight_initialized;\n\ntypedef union rgblight_config_t {\n    uint64_t raw;\n    struct {\n        bool    enable : 1;\n        bool    velocikey : 1;\n        uint8_t mode : 6;\n        uint8_t hue : 8;\n        uint8_t sat : 8;\n        uint8_t val : 8;\n        uint8_t speed : 8;\n    };\n} rgblight_config_t;\n\nSTATIC_ASSERT(sizeof(rgblight_config_t) == sizeof(uint64_t), \"RGB Light EECONFIG out of spec.\");\n\ntypedef struct _rgblight_status_t {\n    uint8_t base_mode;\n    bool    timer_enabled;\n#ifdef RGBLIGHT_SPLIT\n    uint8_t change_flags;\n#endif\n#ifdef RGBLIGHT_LAYERS\n    rgblight_layer_mask_t enabled_layer_mask;\n#endif\n} rgblight_status_t;\n\n/*\n * Structure for RGB Light clipping ranges\n */\ntypedef struct _rgblight_ranges_t {\n    uint8_t clipping_start_pos;\n    uint8_t clipping_num_leds;\n    uint8_t effect_start_pos;\n    uint8_t effect_end_pos;\n    uint8_t effect_num_leds;\n} rgblight_ranges_t;\n\nextern rgblight_ranges_t rgblight_ranges;\n\n/* === Low level Functions === */\nvoid rgblight_set(void);\nvoid rgblight_set_clipping_range(uint8_t start_pos, uint8_t num_leds);\n\n/* === Effects and Animations Functions === */\n/*   effect range setting */\nvoid rgblight_set_effect_range(uint8_t start_pos, uint8_t num_leds);\n\n/*   direct operation */\nvoid rgblight_setrgb_at(uint8_t r, uint8_t g, uint8_t b, uint8_t index);\nvoid rgblight_sethsv_at(uint8_t hue, uint8_t sat, uint8_t val, uint8_t index);\nvoid rgblight_setrgb_range(uint8_t r, uint8_t g, uint8_t b, uint8_t start, uint8_t end);\nvoid rgblight_sethsv_range(uint8_t hue, uint8_t sat, uint8_t val, uint8_t start, uint8_t end);\nvoid rgblight_setrgb(uint8_t r, uint8_t g, uint8_t b);\n\n#ifndef RGBLIGHT_SPLIT\nvoid rgblight_setrgb_master(uint8_t r, uint8_t g, uint8_t b);\nvoid rgblight_setrgb_slave(uint8_t r, uint8_t g, uint8_t b);\nvoid rgblight_sethsv_master(uint8_t hue, uint8_t sat, uint8_t val);\nvoid rgblight_sethsv_slave(uint8_t hue, uint8_t sat, uint8_t val);\n#endif\n\n/*   effect mode change */\nvoid rgblight_mode(uint8_t mode);\nvoid rgblight_mode_noeeprom(uint8_t mode);\nvoid rgblight_increase(void);\nvoid rgblight_decrease(void);\nvoid rgblight_step(void);\nvoid rgblight_step_noeeprom(void);\nvoid rgblight_step_reverse(void);\nvoid rgblight_step_reverse_noeeprom(void);\n\n/*   effects mode disable/enable */\nvoid rgblight_toggle(void);\nvoid rgblight_toggle_noeeprom(void);\nvoid rgblight_enable(void);\nvoid rgblight_enable_noeeprom(void);\nvoid rgblight_disable(void);\nvoid rgblight_disable_noeeprom(void);\nvoid rgblight_enabled_noeeprom(bool state);\n\n/*   hue, sat, val change */\nvoid rgblight_increase_hue(void);\nvoid rgblight_increase_hue_noeeprom(void);\nvoid rgblight_decrease_hue(void);\nvoid rgblight_decrease_hue_noeeprom(void);\nvoid rgblight_increase_sat(void);\nvoid rgblight_increase_sat_noeeprom(void);\nvoid rgblight_decrease_sat(void);\nvoid rgblight_decrease_sat_noeeprom(void);\nvoid rgblight_increase_val(void);\nvoid rgblight_increase_val_noeeprom(void);\nvoid rgblight_decrease_val(void);\nvoid rgblight_decrease_val_noeeprom(void);\nvoid rgblight_increase_speed(void);\nvoid rgblight_increase_speed_noeeprom(void);\nvoid rgblight_decrease_speed(void);\nvoid rgblight_decrease_speed_noeeprom(void);\nvoid rgblight_sethsv(uint8_t hue, uint8_t sat, uint8_t val);\nvoid rgblight_sethsv_noeeprom(uint8_t hue, uint8_t sat, uint8_t val);\n\n/*   effect speed */\nuint8_t rgblight_get_speed(void);\nvoid    rgblight_set_speed(uint8_t speed);\nvoid    rgblight_set_speed_noeeprom(uint8_t speed);\n\n/*   reset */\nvoid rgblight_reload_from_eeprom(void);\n\n/*       query */\nuint8_t rgblight_get_mode(void);\nuint8_t rgblight_get_hue(void);\nuint8_t rgblight_get_sat(void);\nuint8_t rgblight_get_val(void);\nbool    rgblight_is_enabled(void);\nhsv_t   rgblight_get_hsv(void);\n\n/* === qmk_firmware (core)internal Functions === */\nvoid     rgblight_init(void);\nvoid     rgblight_suspend(void);\nvoid     rgblight_wakeup(void);\nuint64_t rgblight_read_qword(void);\nvoid     rgblight_update_qword(uint64_t qword);\nvoid     eeconfig_update_rgblight_current(void);\nvoid     eeconfig_update_rgblight_default(void);\nvoid     eeconfig_debug_rgblight(void);\n\nvoid rgb_matrix_increase(void);\nvoid rgb_matrix_decrease(void);\n\nvoid rgblight_sethsv_eeprom_helper(uint8_t hue, uint8_t sat, uint8_t val, bool write_to_eeprom);\nvoid rgblight_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom);\n\n#define EZ_RGB(val) rgblight_show_solid_color((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)\nvoid rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b);\n\nvoid preprocess_rgblight(void);\nvoid rgblight_task(void);\n\n#ifdef RGBLIGHT_USE_TIMER\nvoid rgblight_timer_init(void);\nvoid rgblight_timer_enable(void);\nvoid rgblight_timer_disable(void);\nvoid rgblight_timer_toggle(void);\n#else\n#    define rgblight_timer_init()\n#    define rgblight_timer_enable()\n#    define rgblight_timer_disable()\n#    define rgblight_timer_toggle()\n#endif\n\n#ifdef RGBLIGHT_SPLIT\n#    define RGBLIGHT_STATUS_CHANGE_MODE (1 << 0)\n#    define RGBLIGHT_STATUS_CHANGE_HSVS (1 << 1)\n#    define RGBLIGHT_STATUS_CHANGE_TIMER (1 << 2)\n#    define RGBLIGHT_STATUS_ANIMATION_TICK (1 << 3)\n#    define RGBLIGHT_STATUS_CHANGE_LAYERS (1 << 4)\n\ntypedef struct _rgblight_syncinfo_t {\n    rgblight_config_t config;\n    rgblight_status_t status;\n} rgblight_syncinfo_t;\n\n/* for split keyboard master side */\nuint8_t rgblight_get_change_flags(void);\nvoid    rgblight_clear_change_flags(void);\nvoid    rgblight_get_syncinfo(rgblight_syncinfo_t *syncinfo);\n/* for split keyboard slave side */\nvoid rgblight_update_sync(rgblight_syncinfo_t *syncinfo, bool write_to_eeprom);\n#endif\n\n#ifdef RGBLIGHT_USE_TIMER\n\ntypedef struct _animation_status_t {\n    uint16_t last_timer;\n    uint8_t  delta; /* mode - base_mode */\n    bool     restart;\n    union {\n        uint16_t pos16;\n        uint8_t  pos;\n        int8_t   current_hue;\n        uint16_t current_offset;\n    };\n} animation_status_t;\n\nextern animation_status_t animation_status;\n\nvoid rgblight_effect_breathing(animation_status_t *anim);\nvoid rgblight_effect_rainbow_mood(animation_status_t *anim);\nvoid rgblight_effect_rainbow_swirl(animation_status_t *anim);\nvoid rgblight_effect_snake(animation_status_t *anim);\nvoid rgblight_effect_knight(animation_status_t *anim);\nvoid rgblight_effect_christmas(animation_status_t *anim);\nvoid rgblight_effect_rgbtest(animation_status_t *anim);\nvoid rgblight_effect_alternating(animation_status_t *anim);\nvoid rgblight_effect_twinkle(animation_status_t *anim);\n\n#endif\n\n#ifdef VELOCIKEY_ENABLE\nbool    rgblight_velocikey_enabled(void);\nvoid    rgblight_velocikey_toggle(void);\nvoid    rgblight_velocikey_accelerate(void);\nvoid    rgblight_velocikey_decelerate(void);\nuint8_t rgblight_velocikey_match_speed(uint8_t minValue, uint8_t maxValue);\n\n#    define velocikey_enabled rgblight_velocikey_enabled\n#    define velocikey_toggle rgblight_velocikey_toggle\n#endif\n"
  },
  {
    "path": "quantum/rgblight/rgblight_breathe_table.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n/*******************************************************************************\n  88888888888 888      d8b                .d888 d8b 888               d8b\n      888     888      Y8P               d88P\"  Y8P 888               Y8P\n      888     888                        888        888\n      888     88888b.  888 .d8888b       888888 888 888  .d88b.       888 .d8888b\n      888     888 \"88b 888 88K           888    888 888 d8P  Y8b      888 88K\n      888     888  888 888 \"Y8888b.      888    888 888 88888888      888 \"Y8888b.\n      888     888  888 888      X88      888    888 888 Y8b.          888      X88\n      888     888  888 888  88888P'      888    888 888  \"Y8888       888  88888P'\n                                                        888                 888\n                                                        888                 888\n                                                        888                 888\n     .d88b.   .d88b.  88888b.   .d88b.  888d888 8888b.  888888 .d88b.   .d88888\n    d88P\"88b d8P  Y8b 888 \"88b d8P  Y8b 888P\"      \"88b 888   d8P  Y8b d88\" 888\n    888  888 88888888 888  888 88888888 888    .d888888 888   88888888 888  888\n    Y88b 888 Y8b.     888  888 Y8b.     888    888  888 Y88b. Y8b.     Y88b 888\n     \"Y88888  \"Y8888  888  888  \"Y8888  888    \"Y888888  \"Y888 \"Y8888   \"Y88888\n         888\n    Y8b d88P\n     \"Y88P\"\n*******************************************************************************/\n\n#pragma once\n// clang-format off\n#define RGBLIGHT_EFFECT_BREATHE_TABLE\n\n// Breathing center: 1.85\n// Breathing max:    255\n\nconst uint8_t PROGMEM rgblight_effect_breathe_table[] = {\n#if RGBLIGHT_BREATHE_TABLE_SIZE == 256\n    0x22, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2A, 0x2C,\n    0x2D, 0x2F, 0x30, 0x32, 0x33, 0x35, 0x36, 0x38,\n    0x3A, 0x3B, 0x3D, 0x3E, 0x40, 0x42, 0x43, 0x45,\n    0x47, 0x49, 0x4A, 0x4C, 0x4E, 0x50, 0x51, 0x53,\n    0x55, 0x57, 0x59, 0x5A, 0x5C, 0x5E, 0x60, 0x62,\n    0x64, 0x66, 0x68, 0x69, 0x6B, 0x6D, 0x6F, 0x71,\n    0x73, 0x75, 0x77, 0x79, 0x7B, 0x7D, 0x7F, 0x81,\n    0x83, 0x85, 0x87, 0x89, 0x8A, 0x8C, 0x8E, 0x90,\n    0x92, 0x94, 0x96, 0x98, 0x9A, 0x9C, 0x9E, 0x9F,\n    0xA1, 0xA3, 0xA5, 0xA7, 0xA8, 0xAA, 0xAC, 0xAE,\n    0xAF, 0xB1, 0xB3, 0xB4, 0xB6, 0xB8, 0xB9, 0xBB,\n    0xBC, 0xBE, 0xBF, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6,\n    0xC7, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xD0,\n    0xD1, 0xD2, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,\n    0xD7, 0xD8, 0xD9, 0xD9, 0xDA, 0xDA, 0xDB, 0xDB,\n    0xDB, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDD, 0xDD,\n    0xDD, 0xDD, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDB,\n    0xDB, 0xDB, 0xDA, 0xDA, 0xD9, 0xD9, 0xD8, 0xD7,\n    0xD7, 0xD6, 0xD5, 0xD4, 0xD3, 0xD2, 0xD2, 0xD1,\n    0xD0, 0xCE, 0xCD, 0xCC, 0xCB, 0xCA, 0xC9, 0xC7,\n    0xC6, 0xC5, 0xC3, 0xC2, 0xC1, 0xBF, 0xBE, 0xBC,\n    0xBB, 0xB9, 0xB8, 0xB6, 0xB4, 0xB3, 0xB1, 0xAF,\n    0xAE, 0xAC, 0xAA, 0xA8, 0xA7, 0xA5, 0xA3, 0xA1,\n    0x9F, 0x9E, 0x9C, 0x9A, 0x98, 0x96, 0x94, 0x92,\n    0x90, 0x8E, 0x8C, 0x8A, 0x89, 0x87, 0x85, 0x83,\n    0x81, 0x7F, 0x7D, 0x7B, 0x79, 0x77, 0x75, 0x73,\n    0x71, 0x6F, 0x6D, 0x6B, 0x69, 0x68, 0x66, 0x64,\n    0x62, 0x60, 0x5E, 0x5C, 0x5A, 0x59, 0x57, 0x55,\n    0x53, 0x51, 0x50, 0x4E, 0x4C, 0x4A, 0x49, 0x47,\n    0x45, 0x43, 0x42, 0x40, 0x3E, 0x3D, 0x3B, 0x3A,\n    0x38, 0x36, 0x35, 0x33, 0x32, 0x30, 0x2F, 0x2D,\n    0x2C, 0x2A, 0x29, 0x28, 0x26, 0x25, 0x23, 0x22\n#endif\n\n#if RGBLIGHT_BREATHE_TABLE_SIZE == 128\n    0x22, 0x25, 0x28, 0x2A,\n    0x2D, 0x30, 0x33, 0x36,\n    0x3A, 0x3D, 0x40, 0x43,\n    0x47, 0x4A, 0x4E, 0x51,\n    0x55, 0x59, 0x5C, 0x60,\n    0x64, 0x68, 0x6B, 0x6F,\n    0x73, 0x77, 0x7B, 0x7F,\n    0x83, 0x87, 0x8A, 0x8E,\n    0x92, 0x96, 0x9A, 0x9E,\n    0xA1, 0xA5, 0xA8, 0xAC,\n    0xAF, 0xB3, 0xB6, 0xB9,\n    0xBC, 0xBF, 0xC2, 0xC5,\n    0xC7, 0xCA, 0xCC, 0xCE,\n    0xD1, 0xD2, 0xD4, 0xD6,\n    0xD7, 0xD9, 0xDA, 0xDB,\n    0xDB, 0xDC, 0xDC, 0xDD,\n    0xDD, 0xDC, 0xDC, 0xDC,\n    0xDB, 0xDA, 0xD9, 0xD8,\n    0xD7, 0xD5, 0xD3, 0xD2,\n    0xD0, 0xCD, 0xCB, 0xC9,\n    0xC6, 0xC3, 0xC1, 0xBE,\n    0xBB, 0xB8, 0xB4, 0xB1,\n    0xAE, 0xAA, 0xA7, 0xA3,\n    0x9F, 0x9C, 0x98, 0x94,\n    0x90, 0x8C, 0x89, 0x85,\n    0x81, 0x7D, 0x79, 0x75,\n    0x71, 0x6D, 0x69, 0x66,\n    0x62, 0x5E, 0x5A, 0x57,\n    0x53, 0x50, 0x4C, 0x49,\n    0x45, 0x42, 0x3E, 0x3B,\n    0x38, 0x35, 0x32, 0x2F,\n    0x2C, 0x29, 0x26, 0x23\n#endif\n\n#if RGBLIGHT_BREATHE_TABLE_SIZE == 64\n    0x22, 0x28,\n    0x2D, 0x33,\n    0x3A, 0x40,\n    0x47, 0x4E,\n    0x55, 0x5C,\n    0x64, 0x6B,\n    0x73, 0x7B,\n    0x83, 0x8A,\n    0x92, 0x9A,\n    0xA1, 0xA8,\n    0xAF, 0xB6,\n    0xBC, 0xC2,\n    0xC7, 0xCC,\n    0xD1, 0xD4,\n    0xD7, 0xDA,\n    0xDB, 0xDC,\n    0xDD, 0xDC,\n    0xDB, 0xD9,\n    0xD7, 0xD3,\n    0xD0, 0xCB,\n    0xC6, 0xC1,\n    0xBB, 0xB4,\n    0xAE, 0xA7,\n    0x9F, 0x98,\n    0x90, 0x89,\n    0x81, 0x79,\n    0x71, 0x69,\n    0x62, 0x5A,\n    0x53, 0x4C,\n    0x45, 0x3E,\n    0x38, 0x32,\n    0x2C, 0x26\n#endif\n};\n\nstatic const int table_scale = 256 / sizeof(rgblight_effect_breathe_table);\n\n"
  },
  {
    "path": "quantum/rgblight/rgblight_drivers.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"rgblight_drivers.h\"\n\n#if defined(RGBLIGHT_WS2812)\n#    include \"ws2812.h\"\n\nconst rgblight_driver_t rgblight_driver = {\n    .init          = ws2812_init,\n    .set_color     = ws2812_set_color,\n    .set_color_all = ws2812_set_color_all,\n    .flush         = ws2812_flush,\n};\n\n#elif defined(RGBLIGHT_APA102)\n#    include \"apa102.h\"\n\nconst rgblight_driver_t rgblight_driver = {\n    .init          = apa102_init,\n    .set_color     = apa102_set_color,\n    .set_color_all = apa102_set_color_all,\n    .flush         = apa102_flush,\n};\n\n#endif\n"
  },
  {
    "path": "quantum/rgblight/rgblight_drivers.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n\ntypedef struct {\n    void (*init)(void);\n    void (*set_color)(int index, uint8_t red, uint8_t green, uint8_t blue);\n    void (*set_color_all)(uint8_t red, uint8_t green, uint8_t blue);\n    void (*flush)(void);\n} rgblight_driver_t;\n\nextern const rgblight_driver_t rgblight_driver;\n"
  },
  {
    "path": "quantum/rgblight/rgblight_modes.h",
    "content": "#ifdef _RGBM_SINGLE_STATIC\n_RGBM_SINGLE_STATIC(STATIC_LIGHT)\n#    ifdef RGBLIGHT_EFFECT_BREATHING\n_RGBM_MULTI_DYNAMIC(BREATHING)\n_RGBM_TMP_DYNAMIC(breathing_3, BREATHING)\n_RGBM_TMP_DYNAMIC(breathing_4, BREATHING)\n_RGBM_TMP_DYNAMIC(BREATHING_end, BREATHING)\n#    endif\n#    ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD\n_RGBM_MULTI_DYNAMIC(RAINBOW_MOOD)\n_RGBM_TMP_DYNAMIC(rainbow_mood_7, RAINBOW_MOOD)\n_RGBM_TMP_DYNAMIC(RAINBOW_MOOD_end, RAINBOW_MOOD)\n#    endif\n#    ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL\n_RGBM_MULTI_DYNAMIC(RAINBOW_SWIRL)\n_RGBM_TMP_DYNAMIC(rainbow_swirl_10, RAINBOW_SWIRL)\n_RGBM_TMP_DYNAMIC(rainbow_swirl_11, RAINBOW_SWIRL)\n_RGBM_TMP_DYNAMIC(rainbow_swirl_12, RAINBOW_SWIRL)\n_RGBM_TMP_DYNAMIC(rainbow_swirl_13, RAINBOW_SWIRL)\n_RGBM_TMP_DYNAMIC(RAINBOW_SWIRL_end, RAINBOW_SWIRL)\n#    endif\n#    ifdef RGBLIGHT_EFFECT_SNAKE\n_RGBM_MULTI_DYNAMIC(SNAKE)\n_RGBM_TMP_DYNAMIC(snake_16, SNAKE)\n_RGBM_TMP_DYNAMIC(snake_17, SNAKE)\n_RGBM_TMP_DYNAMIC(snake_18, SNAKE)\n_RGBM_TMP_DYNAMIC(snake_19, SNAKE)\n_RGBM_TMP_DYNAMIC(SNAKE_end, SNAKE)\n#    endif\n#    ifdef RGBLIGHT_EFFECT_KNIGHT\n_RGBM_MULTI_DYNAMIC(KNIGHT)\n_RGBM_TMP_DYNAMIC(knight_22, KNIGHT)\n_RGBM_TMP_DYNAMIC(KNIGHT_end, KNIGHT)\n#    endif\n#    ifdef RGBLIGHT_EFFECT_CHRISTMAS\n_RGBM_SINGLE_DYNAMIC(CHRISTMAS)\n#    endif\n#    ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT\n_RGBM_MULTI_STATIC(STATIC_GRADIENT)\n_RGBM_TMP_STATIC(static_gradient_26, STATIC_GRADIENT)\n_RGBM_TMP_STATIC(static_gradient_27, STATIC_GRADIENT)\n_RGBM_TMP_STATIC(static_gradient_28, STATIC_GRADIENT)\n_RGBM_TMP_STATIC(static_gradient_29, STATIC_GRADIENT)\n_RGBM_TMP_STATIC(static_gradient_30, STATIC_GRADIENT)\n_RGBM_TMP_STATIC(static_gradient_31, STATIC_GRADIENT)\n_RGBM_TMP_STATIC(static_gradient_32, STATIC_GRADIENT)\n_RGBM_TMP_STATIC(static_gradient_33, STATIC_GRADIENT)\n_RGBM_TMP_STATIC(STATIC_GRADIENT_end, STATIC_GRADIENT)\n#    endif\n#    ifdef RGBLIGHT_EFFECT_RGB_TEST\n_RGBM_SINGLE_DYNAMIC(RGB_TEST)\n#    endif\n#    ifdef RGBLIGHT_EFFECT_ALTERNATING\n_RGBM_SINGLE_DYNAMIC(ALTERNATING)\n#    endif\n#    ifdef RGBLIGHT_EFFECT_TWINKLE\n_RGBM_MULTI_DYNAMIC(TWINKLE)\n_RGBM_TMP_DYNAMIC(twinkle_38, TWINKLE)\n_RGBM_TMP_DYNAMIC(twinkle_39, TWINKLE)\n_RGBM_TMP_DYNAMIC(twinkle_40, TWINKLE)\n_RGBM_TMP_DYNAMIC(twinkle_41, TWINKLE)\n_RGBM_TMP_DYNAMIC(TWINKLE_end, TWINKLE)\n#    endif\n////  Add a new mode here.\n// #ifdef RGBLIGHT_EFFECT_<name>\n//    _RGBM_<SINGLE|MULTI>_<STATIC|DYNAMIC>( <name> )\n// #endif\n#endif\n\n#undef _RGBM_SINGLE_STATIC\n#undef _RGBM_SINGLE_DYNAMIC\n#undef _RGBM_MULTI_STATIC\n#undef _RGBM_MULTI_DYNAMIC\n#undef _RGBM_TMP_STATIC\n#undef _RGBM_TMP_DYNAMIC\n"
  },
  {
    "path": "quantum/rgblight/rgblight_post_config.h",
    "content": "#if defined(RGBLED_SPLIT) && !defined(RGBLIGHT_SPLIT)\n// When RGBLED_SPLIT is defined,\n// it is considered that RGBLIGHT_SPLIT is defined implicitly.\n#    define RGBLIGHT_SPLIT\n#endif\n"
  },
  {
    "path": "quantum/ring_buffer.h",
    "content": "#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"atomic_util.h\"\n\n#ifndef RBUF_SIZE\n#    define RBUF_SIZE 32\n#endif\n\nstatic uint8_t     rbuf[RBUF_SIZE];\nstatic uint8_t     rbuf_head = 0;\nstatic uint8_t     rbuf_tail = 0;\nstatic inline bool rbuf_enqueue(uint8_t data) {\n    bool ret = false;\n    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {\n        uint8_t next = (rbuf_head + 1) % RBUF_SIZE;\n        if (next != rbuf_tail) {\n            rbuf[rbuf_head] = data;\n            rbuf_head       = next;\n            ret             = true;\n        }\n    }\n    return ret;\n}\nstatic inline uint8_t rbuf_dequeue(void) {\n    uint8_t val = 0;\n    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {\n        if (rbuf_head != rbuf_tail) {\n            val       = rbuf[rbuf_tail];\n            rbuf_tail = (rbuf_tail + 1) % RBUF_SIZE;\n        }\n    }\n\n    return val;\n}\nstatic inline bool rbuf_has_data(void) {\n    bool has_data;\n    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {\n        has_data = (rbuf_head != rbuf_tail);\n    }\n    return has_data;\n}\nstatic inline void rbuf_clear(void) {\n    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {\n        rbuf_head = rbuf_tail = 0;\n    }\n}\n"
  },
  {
    "path": "quantum/secure.c",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"secure.h\"\n#include \"timer.h\"\n#include \"util.h\"\n\n#ifndef SECURE_UNLOCK_TIMEOUT\n#    define SECURE_UNLOCK_TIMEOUT 5000\n#endif\n\n#ifndef SECURE_IDLE_TIMEOUT\n#    define SECURE_IDLE_TIMEOUT 60000\n#endif\n\n#ifndef SECURE_UNLOCK_SEQUENCE\n#    define SECURE_UNLOCK_SEQUENCE \\\n        {                          \\\n            { 0, 0 }               \\\n        }\n#endif\n\nstatic secure_status_t secure_status = SECURE_LOCKED;\nstatic uint32_t        unlock_time   = 0;\nstatic uint32_t        idle_time     = 0;\n\nstatic void secure_hook(secure_status_t secure_status) {\n    secure_hook_quantum(secure_status);\n    secure_hook_kb(secure_status);\n}\n\nsecure_status_t secure_get_status(void) {\n    return secure_status;\n}\n\nvoid secure_lock(void) {\n    secure_status = SECURE_LOCKED;\n    secure_hook(secure_status);\n}\n\nvoid secure_unlock(void) {\n    secure_status = SECURE_UNLOCKED;\n    idle_time     = timer_read32();\n    secure_hook(secure_status);\n}\n\nvoid secure_request_unlock(void) {\n    if (secure_status == SECURE_LOCKED) {\n        secure_status = SECURE_PENDING;\n        unlock_time   = timer_read32();\n    }\n    secure_hook(secure_status);\n}\n\nvoid secure_activity_event(void) {\n    if (secure_status == SECURE_UNLOCKED) {\n        idle_time = timer_read32();\n    }\n}\n\nvoid secure_keypress_event(uint8_t row, uint8_t col) {\n    static const uint8_t sequence[][2] = SECURE_UNLOCK_SEQUENCE;\n    static const uint8_t sequence_len  = ARRAY_SIZE(sequence);\n\n    static uint8_t offset = 0;\n    if ((sequence[offset][0] == row) && (sequence[offset][1] == col)) {\n        offset++;\n        if (offset == sequence_len) {\n            offset = 0;\n            secure_unlock();\n        }\n    } else {\n        offset = 0;\n        secure_lock();\n    }\n}\n\nvoid secure_task(void) {\n#if SECURE_UNLOCK_TIMEOUT != 0\n    // handle unlock timeout\n    if (secure_status == SECURE_PENDING) {\n        if (timer_elapsed32(unlock_time) >= SECURE_UNLOCK_TIMEOUT) {\n            secure_lock();\n        }\n    }\n#endif\n\n#if SECURE_IDLE_TIMEOUT != 0\n    // handle idle timeout\n    if (secure_status == SECURE_UNLOCKED) {\n        if (timer_elapsed32(idle_time) >= SECURE_IDLE_TIMEOUT) {\n            secure_lock();\n        }\n    }\n#endif\n}\n\n__attribute__((weak)) bool secure_hook_user(secure_status_t secure_status) {\n    return true;\n}\n__attribute__((weak)) bool secure_hook_kb(secure_status_t secure_status) {\n    return secure_hook_user(secure_status);\n}\n"
  },
  {
    "path": "quantum/secure.h",
    "content": "// Copyright 2022 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n/**\n * \\file\n *\n * \\defgroup secure Secure API\n *\n * \\brief Exposes a set of functionality to act as a virtual padlock for your device\n * ...as long as that padlock is made of paper and it's currently raining.\n *\n * \\{\n */\n\n#include <stdint.h>\n#include <stdbool.h>\n\n/** \\brief Available secure states\n */\ntypedef enum {\n    SECURE_LOCKED,\n    SECURE_PENDING,\n    SECURE_UNLOCKED,\n} secure_status_t;\n\n/** \\brief Query current secure state\n */\nsecure_status_t secure_get_status(void);\n\n/** \\brief Helper to check if unlocking is currently locked\n */\n#define secure_is_locked() (secure_get_status() == SECURE_LOCKED)\n\n/** \\brief Helper to check if unlocking is currently in progress\n */\n#define secure_is_unlocking() (secure_get_status() == SECURE_PENDING)\n\n/** \\brief Helper to check if unlocking is currently unlocked\n */\n#define secure_is_unlocked() (secure_get_status() == SECURE_UNLOCKED)\n\n/** \\brief Lock down the device\n */\nvoid secure_lock(void);\n\n/** \\brief Force unlock the device\n *\n * \\warning bypasses user unlock sequence\n */\nvoid secure_unlock(void);\n\n/** \\brief Begin listening for an unlock sequence\n */\nvoid secure_request_unlock(void);\n\n/** \\brief Flag to the secure subsystem that user activity has happened\n *\n * Call when some user activity has happened and the device should remain unlocked\n */\nvoid secure_activity_event(void);\n\n/** \\brief Flag to the secure subsystem that user has triggered a keypress\n *\n * Call to trigger processing of the unlock sequence\n */\nvoid secure_keypress_event(uint8_t row, uint8_t col);\n\n/** \\brief Handle various secure subsystem background tasks\n */\nvoid secure_task(void);\n\n/** \\brief quantum hook called when changing secure status device\n */\nvoid secure_hook_quantum(secure_status_t secure_status);\n\n/** \\brief user hook called when changing secure status device\n */\nbool secure_hook_user(secure_status_t secure_status);\n\n/** \\brief keyboard hook called when changing secure status device\n */\nbool secure_hook_kb(secure_status_t secure_status);\n\n/** \\} */\n"
  },
  {
    "path": "quantum/send_string/send_string.c",
    "content": "/* Copyright 2021\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"send_string.h\"\n\n#include <ctype.h>\n#include <stdlib.h>\n\n#include \"quantum_keycodes.h\"\n#include \"keycode.h\"\n#include \"action.h\"\n#include \"wait.h\"\n\n#if defined(AUDIO_ENABLE) && defined(SENDSTRING_BELL)\n#    include \"audio.h\"\n#    ifndef BELL_SOUND\n#        define BELL_SOUND TERMINAL_SOUND\n#    endif\nfloat bell_song[][2] = SONG(BELL_SOUND);\n#endif\n\n// clang-format off\n\n/* Bit-Packed look-up table to convert an ASCII character to whether\n * [Shift] needs to be sent with the keycode.\n */\n__attribute__((weak)) const uint8_t ascii_to_shift_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),\n    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 1, 0, 1, 0, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),\n    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)\n};\n\n/* Bit-Packed look-up table to convert an ASCII character to whether\n * [AltGr] needs to be sent with the keycode.\n */\n__attribute__((weak)) const uint8_t ascii_to_altgr_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\n/* Bit-Packed look-up table to convert an ASCII character to whether\n * [Space] needs to be sent after the keycode\n */\n__attribute__((weak)) const uint8_t ascii_to_dead_lut[16] PROGMEM = {\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),\n    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)\n};\n\n/* Look-up table to convert an ASCII character to a keycode.\n */\n__attribute__((weak)) const uint8_t ascii_to_keycode_lut[128] PROGMEM = {\n    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // BS    TAB      LF       VT       FF       CR       SO       SI\n    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB\n    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n    // CAN   EM       SUB      ESC      FS       GS       RS       US\n    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,\n\n    //       !        \"        #        $        %        &        '\n    KC_SPC,  KC_1,    KC_QUOT, KC_3,    KC_4,    KC_5,    KC_7,    KC_QUOT,\n    // (     )        *        +        ,        -        .        /\n    KC_9,    KC_0,    KC_8,    KC_EQL,  KC_COMM, KC_MINS, KC_DOT,  KC_SLSH,\n    // 0     1        2        3        4        5        6        7\n    KC_0,    KC_1,    KC_2,    KC_3,    KC_4,    KC_5,    KC_6,    KC_7,\n    // 8     9        :        ;        <        =        >        ?\n    KC_8,    KC_9,    KC_SCLN, KC_SCLN, KC_COMM, KC_EQL,  KC_DOT,  KC_SLSH,\n    // @     A        B        C        D        E        F        G\n    KC_2,    KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,\n    // H     I        J        K        L        M        N        O\n    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,    KC_M,    KC_N,    KC_O,\n    // P     Q        R        S        T        U        V        W\n    KC_P,    KC_Q,    KC_R,    KC_S,    KC_T,    KC_U,    KC_V,    KC_W,\n    // X     Y        Z        [        \\        ]        ^        _\n    KC_X,    KC_Y,    KC_Z,    KC_LBRC, KC_BSLS, KC_RBRC, KC_6,    KC_MINS,\n    // `     a        b        c        d        e        f        g\n    KC_GRV,  KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,\n    // h     i        j        k        l        m        n        o\n    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,    KC_M,    KC_N,    KC_O,\n    // p     q        r        s        t        u        v        w\n    KC_P,    KC_Q,    KC_R,    KC_S,    KC_T,    KC_U,    KC_V,    KC_W,\n    // x     y        z        {        |        }        ~        DEL\n    KC_X,    KC_Y,    KC_Z,    KC_LBRC, KC_BSLS, KC_RBRC, KC_GRV,  KC_DEL\n};\n\n// clang-format on\n\n// Note: we bit-pack in \"reverse\" order to optimize loading\n#define PGM_LOADBIT(mem, pos) ((pgm_read_byte(&((mem)[(pos) / 8])) >> ((pos) % 8)) & 0x01)\n\nvoid send_string(const char *string) {\n    send_string_with_delay(string, TAP_CODE_DELAY);\n}\n\nvoid send_string_with_delay_impl(char (*getter)(void *), void *arg, uint8_t interval) {\n    while (1) {\n        char ascii_code = getter(arg);\n        if (!ascii_code) break;\n        if (ascii_code == SS_QMK_PREFIX) {\n            ascii_code = getter(arg);\n\n            if (ascii_code == SS_TAP_CODE) {\n                // tap\n                uint8_t keycode = getter(arg);\n                tap_code(keycode);\n            } else if (ascii_code == SS_DOWN_CODE) {\n                // down\n                uint8_t keycode = getter(arg);\n                register_code(keycode);\n            } else if (ascii_code == SS_UP_CODE) {\n                // up\n                uint8_t keycode = getter(arg);\n                unregister_code(keycode);\n            } else if (ascii_code == SS_DELAY_CODE) {\n                // delay\n                int ms     = 0;\n                ascii_code = getter(arg);\n\n                while (isdigit(ascii_code)) {\n                    ms *= 10;\n                    ms += ascii_code - '0';\n                    ascii_code = getter(arg);\n                }\n\n                wait_ms(ms);\n            }\n\n            wait_ms(interval);\n\n            // if we had a delay that terminated with a null, we're done\n            if (ascii_code == 0) break;\n        } else {\n            send_char_with_delay(ascii_code, interval);\n        }\n    }\n}\n\ntypedef struct send_string_memory_state_t {\n    const char *string;\n} send_string_memory_state_t;\n\nchar send_string_get_next_ram(void *arg) {\n    send_string_memory_state_t *state = (send_string_memory_state_t *)arg;\n    char                        ret   = *state->string;\n    state->string++;\n    return ret;\n}\n\nvoid send_string_with_delay(const char *string, uint8_t interval) {\n    send_string_memory_state_t state = {string};\n    send_string_with_delay_impl(send_string_get_next_ram, &state, interval);\n}\n\nvoid send_char(char ascii_code) {\n    send_char_with_delay(ascii_code, TAP_CODE_DELAY);\n}\n\nvoid send_char_with_delay(char ascii_code, uint8_t interval) {\n#if defined(AUDIO_ENABLE) && defined(SENDSTRING_BELL)\n    if (ascii_code == '\\a') { // BEL\n        PLAY_SONG(bell_song);\n        return;\n    }\n#endif\n\n    uint8_t keycode    = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]);\n    bool    is_shifted = PGM_LOADBIT(ascii_to_shift_lut, (uint8_t)ascii_code);\n    bool    is_altgred = PGM_LOADBIT(ascii_to_altgr_lut, (uint8_t)ascii_code);\n    bool    is_dead    = PGM_LOADBIT(ascii_to_dead_lut, (uint8_t)ascii_code);\n\n    if (is_shifted) {\n        register_code(KC_LEFT_SHIFT);\n        wait_ms(interval);\n    }\n\n    if (is_altgred) {\n        register_code(KC_RIGHT_ALT);\n        wait_ms(interval);\n    }\n\n    tap_code_delay(keycode, interval);\n    wait_ms(interval);\n\n    if (is_altgred) {\n        unregister_code(KC_RIGHT_ALT);\n        wait_ms(interval);\n    }\n\n    if (is_shifted) {\n        unregister_code(KC_LEFT_SHIFT);\n        wait_ms(interval);\n    }\n\n    if (is_dead) {\n        tap_code(KC_SPACE);\n        wait_ms(interval);\n    }\n}\n\nvoid send_dword(uint32_t number) {\n    send_word(number >> 16);\n    send_word(number & 0xFFFFUL);\n}\n\nvoid send_word(uint16_t number) {\n    send_byte(number >> 8);\n    send_byte(number & 0xFF);\n}\n\nvoid send_byte(uint8_t number) {\n    send_nibble(number >> 4);\n    send_nibble(number & 0xF);\n}\n\nvoid send_nibble(uint8_t number) {\n    switch (number & 0xF) {\n        case 0 ... 9:\n            send_char(number + '0');\n            break;\n        case 10 ... 15:\n            send_char(number - 10 + 'a');\n            break;\n    }\n}\n\nvoid tap_random_base64(void) {\n#if defined(__AVR_ATmega32U4__)\n    uint8_t key = (TCNT0 + TCNT1 + TCNT3 + TCNT4) % 64;\n#else\n    uint8_t key = rand() % 64;\n#endif\n    switch (key) {\n        case 0 ... 25:\n            send_char(key + 'A');\n            break;\n        case 26 ... 51:\n            send_char(key - 26 + 'a');\n            break;\n        case 52:\n            send_char('0');\n            break;\n        case 53 ... 61:\n            send_char(key - 53 + '1');\n            break;\n        case 62:\n            send_char('+');\n            break;\n        case 63:\n            send_char('/');\n            break;\n    }\n}\n\n#if defined(__AVR__)\nvoid send_string_P(const char *string) {\n    send_string_with_delay_P(string, TAP_CODE_DELAY);\n}\n\nchar send_string_get_next_progmem(void *arg) {\n    send_string_memory_state_t *state = (send_string_memory_state_t *)arg;\n    char                        ret   = pgm_read_byte(state->string);\n    state->string++;\n    return ret;\n}\n\nvoid send_string_with_delay_P(const char *string, uint8_t interval) {\n    send_string_memory_state_t state = {string};\n    send_string_with_delay_impl(send_string_get_next_progmem, &state, interval);\n}\n#endif\n"
  },
  {
    "path": "quantum/send_string/send_string.h",
    "content": "/* Copyright 2021\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n/**\n * \\file\n *\n * \\defgroup send_string Send String API\n *\n * \\brief These functions allow you to create macros by typing out sequences of keystrokes.\n * \\{\n */\n\n#include <stdint.h>\n\n#include \"progmem.h\"\n#include \"send_string_keycodes.h\"\n\n// Look-Up Tables (LUTs) to convert ASCII character to keycode sequence.\nextern const uint8_t ascii_to_shift_lut[16];\nextern const uint8_t ascii_to_altgr_lut[16];\nextern const uint8_t ascii_to_dead_lut[16];\nextern const uint8_t ascii_to_keycode_lut[128];\n\n// clang-format off\n#define KCLUT_ENTRY(a, b, c, d, e, f, g, h) \\\n    ( ((a) ? 1 : 0) << 0 \\\n    | ((b) ? 1 : 0) << 1 \\\n    | ((c) ? 1 : 0) << 2 \\\n    | ((d) ? 1 : 0) << 3 \\\n    | ((e) ? 1 : 0) << 4 \\\n    | ((f) ? 1 : 0) << 5 \\\n    | ((g) ? 1 : 0) << 6 \\\n    | ((h) ? 1 : 0) << 7 )\n// clang-format on\n\n/**\n * \\brief Type out a string of ASCII characters.\n *\n * This function simply calls `send_string_with_delay(string, TAP_CODE_DELAY)`.\n *\n * Most keycodes from the basic keycode range are also supported by way of a special sequence - see `send_string_keycodes.h`.\n *\n * \\param string The string to type out.\n */\nvoid send_string(const char *string);\n\n/**\n * \\brief Type out a string of ASCII characters, with a delay between each character.\n *\n * \\param string The string to type out.\n * \\param interval The amount of time, in milliseconds, to wait before typing the next character. Note this can be set to 0 to ensure no delay, regardless of what TAP_CODE_DELAY is set to.\n */\nvoid send_string_with_delay(const char *string, uint8_t interval);\n\n/**\n * \\brief Type out an ASCII character.\n *\n * This function simply calls `send_char_with_delay(string, TAP_CODE_DELAY)`.\n *\n * \\param ascii_code The character to type.\n */\nvoid send_char(char ascii_code);\n\n/**\n * \\brief Type out an ASCII character, with a delay between any modifiers.\n *\n * \\param ascii_code The character to type.\n * \\param interval The amount of time, in milliseconds, to wait in between key presses. Note this can be set to 0 to ensure no delay, regardless of what TAP_CODE_DELAY is set to.\n */\nvoid send_char_with_delay(char ascii_code, uint8_t interval);\n\n/**\n * \\brief Type out an eight digit (unsigned 32-bit) hexadecimal value.\n *\n * The format is `[0-9a-f]{8}`, eg. `00000000` through `ffffffff`.\n *\n * \\param number The value to type, from 0 to 4,294,967,295.\n */\nvoid send_dword(uint32_t number);\n\n/**\n * \\brief Type out a four digit (unsigned 16-bit) hexadecimal value.\n *\n * The format is `[0-9a-f]{4}`, eg. `0000` through `ffff`.\n *\n * \\param number The value to type, from 0 to 65,535.\n */\nvoid send_word(uint16_t number);\n\n/**\n * \\brief Type out a two digit (8-bit) hexadecimal value.\n *\n * The format is `[0-9a-f]{2}`, eg. `00` through `ff`.\n *\n * \\param number The value to type, from 0 to 255.\n */\nvoid send_byte(uint8_t number);\n\n/**\n * \\brief Type out a single hexadecimal digit.\n *\n * The format is `[0-9a-f]{1}`, eg. `0` through `f`.\n *\n * \\param number The value to type, from 0 to 15.\n */\nvoid send_nibble(uint8_t number);\n\n/**\n * \\brief Type a pseudorandom character from the set `A-Z`, `a-z`, `0-9`, `+` and `/`.\n */\nvoid tap_random_base64(void);\n\n#if defined(__AVR__) || defined(__DOXYGEN__)\n/**\n * \\brief Type out a PROGMEM string of ASCII characters.\n *\n * On ARM devices, this function is simply an alias for send_string_with_delay(string, 0).\n *\n * \\param string The string to type out.\n */\nvoid send_string_P(const char *string);\n\n/**\n * \\brief Type out a PROGMEM string of ASCII characters, with a delay between each character.\n *\n * On ARM devices, this function is simply an alias for send_string_with_delay(string, interval).\n *\n * \\param string The string to type out.\n * \\param interval The amount of time, in milliseconds, to wait before typing the next character.\n */\nvoid send_string_with_delay_P(const char *string, uint8_t interval);\n#else\n#    define send_string_P(string) send_string_with_delay(string, 0)\n#    define send_string_with_delay_P(string, interval) send_string_with_delay(string, interval)\n#endif\n\n/**\n * \\brief Shortcut macro for send_string_with_delay_P(PSTR(string), 0).\n *\n * On ARM devices, this define evaluates to send_string_with_delay(string, 0).\n */\n#define SEND_STRING(string) send_string_with_delay_P(PSTR(string), 0)\n\n/**\n * \\brief Shortcut macro for send_string_with_delay_P(PSTR(string), interval).\n *\n * On ARM devices, this define evaluates to send_string_with_delay(string, interval).\n */\n#define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval)\n\n/**\n * \\brief Actual implementation function that iterates and sends the string returned by the getter function.\n *\n * The getter assumes that the next byte is available to be read, and returns it. `arg` is passed in and can be whatever\n * makes most sense for the getter -- each invocation of `getter` must advance its position in the source.\n */\nvoid send_string_with_delay_impl(char (*getter)(void *), void *arg, uint8_t interval);\n\n/** \\} */\n"
  },
  {
    "path": "quantum/send_string/send_string_keycodes.h",
    "content": "/* Copyright 2019\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n// clang-format off\n\n/* Punctuation */\n#define X_ENT  X_ENTER\n#define X_ESC  X_ESCAPE\n#define X_BSPC X_BACKSPACE\n#define X_SPC  X_SPACE\n#define X_MINS X_MINUS\n#define X_EQL  X_EQUAL\n#define X_LBRC X_LEFT_BRACKET\n#define X_RBRC X_RIGHT_BRACKET\n#define X_BSLS X_BACKSLASH\n#define X_NUHS X_NONUS_HASH\n#define X_SCLN X_SEMICOLON\n#define X_QUOT X_QUOTE\n#define X_GRV  X_GRAVE\n#define X_COMM X_COMMA\n#define X_SLSH X_SLASH\n#define X_NUBS X_NONUS_BACKSLASH\n\n/* Lock Keys */\n#define X_CAPS X_CAPS_LOCK\n#define X_SCRL X_SCROLL_LOCK\n#define X_NUM  X_NUM_LOCK\n#define X_LCAP X_LOCKING_CAPS_LOCK\n#define X_LNUM X_LOCKING_NUM_LOCK\n#define X_LSCR X_LOCKING_SCROLL_LOCK\n\n/* Commands */\n#define X_PSCR X_PRINT_SCREEN\n#define X_PAUS X_PAUSE\n#define X_BRK  X_PAUSE\n#define X_INS  X_INSERT\n#define X_PGUP X_PAGE_UP\n#define X_DEL  X_DELETE\n#define X_PGDN X_PAGE_DOWN\n#define X_RGHT X_RIGHT\n#define X_APP  X_APPLICATION\n#define X_EXEC X_EXECUTE\n#define X_SLCT X_SELECT\n#define X_AGIN X_AGAIN\n#define X_PSTE X_PASTE\n#define X_ERAS X_ALTERNATE_ERASE\n#define X_SYRQ X_SYSTEM_REQUEST\n#define X_CNCL X_CANCEL\n#define X_CLR  X_CLEAR\n#define X_PRIR X_PRIOR\n#define X_RETN X_RETURN\n#define X_SEPR X_SEPARATOR\n#define X_CLAG X_CLEAR_AGAIN\n#define X_CRSL X_CRSEL\n#define X_EXSL X_EXSEL\n\n/* Keypad */\n#define X_PSLS X_KP_SLASH\n#define X_PAST X_KP_ASTERISK\n#define X_PMNS X_KP_MINUS\n#define X_PPLS X_KP_PLUS\n#define X_PENT X_KP_ENTER\n#define X_P1   X_KP_1\n#define X_P2   X_KP_2\n#define X_P3   X_KP_3\n#define X_P4   X_KP_4\n#define X_P5   X_KP_5\n#define X_P6   X_KP_6\n#define X_P7   X_KP_7\n#define X_P8   X_KP_8\n#define X_P9   X_KP_9\n#define X_P0   X_KP_0\n#define X_PDOT X_KP_DOT\n#define X_PEQL X_KP_EQUAL\n#define X_PCMM X_KP_COMMA\n\n/* Language Specific */\n#define X_INT1 X_INTERNATIONAL_1\n#define X_INT2 X_INTERNATIONAL_2\n#define X_INT3 X_INTERNATIONAL_3\n#define X_INT4 X_INTERNATIONAL_4\n#define X_INT5 X_INTERNATIONAL_5\n#define X_INT6 X_INTERNATIONAL_6\n#define X_INT7 X_INTERNATIONAL_7\n#define X_INT8 X_INTERNATIONAL_8\n#define X_INT9 X_INTERNATIONAL_9\n#define X_LNG1 X_LANGUAGE_1\n#define X_LNG2 X_LANGUAGE_2\n#define X_LNG3 X_LANGUAGE_3\n#define X_LNG4 X_LANGUAGE_4\n#define X_LNG5 X_LANGUAGE_5\n#define X_LNG6 X_LANGUAGE_6\n#define X_LNG7 X_LANGUAGE_7\n#define X_LNG8 X_LANGUAGE_8\n#define X_LNG9 X_LANGUAGE_9\n\n/* Modifiers */\n#define X_LCTL X_LEFT_CTRL\n#define X_LSFT X_LEFT_SHIFT\n#define X_LALT X_LEFT_ALT\n#define X_LOPT X_LEFT_ALT\n#define X_LGUI X_LEFT_GUI\n#define X_LCMD X_LEFT_GUI\n#define X_LWIN X_LEFT_GUI\n#define X_RCTL X_RIGHT_CTRL\n#define X_RSFT X_RIGHT_SHIFT\n#define X_RALT X_RIGHT_ALT\n#define X_ALGR X_RIGHT_ALT\n#define X_ROPT X_RIGHT_ALT\n#define X_RGUI X_RIGHT_GUI\n#define X_RCMD X_RIGHT_GUI\n#define X_RWIN X_RIGHT_GUI\n\n/* Generic Desktop Page (0x01) */\n#define X_PWR  X_SYSTEM_POWER\n#define X_SLEP X_SYSTEM_SLEEP\n#define X_WAKE X_SYSTEM_WAKE\n\n/* Consumer Page (0x0C) */\n#define X_MUTE X_AUDIO_MUTE\n#define X_VOLU X_AUDIO_VOL_UP\n#define X_VOLD X_AUDIO_VOL_DOWN\n#define X_MNXT X_MEDIA_NEXT_TRACK\n#define X_MPRV X_MEDIA_PREV_TRACK\n#define X_MSTP X_MEDIA_STOP\n#define X_MPLY X_MEDIA_PLAY_PAUSE\n#define X_MSEL X_MEDIA_SELECT\n#define X_EJCT X_MEDIA_EJECT\n#define X_CALC X_CALCULATOR\n#define X_MYCM X_MY_COMPUTER\n#define X_WSCH X_WWW_SEARCH\n#define X_WHOM X_WWW_HOME\n#define X_WBAK X_WWW_BACK\n#define X_WFWD X_WWW_FORWARD\n#define X_WSTP X_WWW_STOP\n#define X_WREF X_WWW_REFRESH\n#define X_WFAV X_WWW_FAVORITES\n#define X_MFFD X_MEDIA_FAST_FORWARD\n#define X_MRWD X_MEDIA_REWIND\n#define X_BRIU X_BRIGHTNESS_UP\n#define X_BRID X_BRIGHTNESS_DOWN\n#define X_CPNL X_CONTROL_PANEL\n#define X_ASST X_ASSISTANT\n\n/* System Specific */\n#define X_BRMU X_PAUSE\n#define X_BRMD X_SCROLL_LOCK\n\n/* Mouse Keys */\n#define X_MS_U X_MS_UP\n#define X_MS_D X_MS_DOWN\n#define X_MS_L X_MS_LEFT\n#define X_MS_R X_MS_RIGHT\n#define X_BTN1 X_MS_BTN1\n#define X_BTN2 X_MS_BTN2\n#define X_BTN3 X_MS_BTN3\n#define X_BTN4 X_MS_BTN4\n#define X_BTN5 X_MS_BTN5\n#define X_BTN6 X_MS_BTN6\n#define X_BTN7 X_MS_BTN7\n#define X_BTN8 X_MS_BTN8\n#define X_WH_U X_MS_WH_UP\n#define X_WH_D X_MS_WH_DOWN\n#define X_WH_L X_MS_WH_LEFT\n#define X_WH_R X_MS_WH_RIGHT\n#define X_ACL0 X_MS_ACCEL0\n#define X_ACL1 X_MS_ACCEL1\n#define X_ACL2 X_MS_ACCEL2\n\n/* Keyboard/Keypad Page (0x07) */\n#define X_A                   04\n#define X_B                   05\n#define X_C                   06\n#define X_D                   07\n#define X_E                   08\n#define X_F                   09\n#define X_G                   0a\n#define X_H                   0b\n#define X_I                   0c\n#define X_J                   0d\n#define X_K                   0e\n#define X_L                   0f\n#define X_M                   10\n#define X_N                   11\n#define X_O                   12\n#define X_P                   13\n#define X_Q                   14\n#define X_R                   15\n#define X_S                   16\n#define X_T                   17\n#define X_U                   18\n#define X_V                   19\n#define X_W                   1a\n#define X_X                   1b\n#define X_Y                   1c\n#define X_Z                   1d\n#define X_1                   1e\n#define X_2                   1f\n#define X_3                   20\n#define X_4                   21\n#define X_5                   22\n#define X_6                   23\n#define X_7                   24\n#define X_8                   25\n#define X_9                   26\n#define X_0                   27\n#define X_ENTER               28\n#define X_ESCAPE              29\n#define X_BACKSPACE           2a\n#define X_TAB                 2b\n#define X_SPACE               2c\n#define X_MINUS               2d\n#define X_EQUAL               2e\n#define X_LEFT_BRACKET        2f\n#define X_RIGHT_BRACKET       30\n#define X_BACKSLASH           31\n#define X_NONUS_HASH          32\n#define X_SEMICOLON           33\n#define X_QUOTE               34\n#define X_GRAVE               35\n#define X_COMMA               36\n#define X_DOT                 37\n#define X_SLASH               38\n#define X_CAPS_LOCK           39\n#define X_F1                  3a\n#define X_F2                  3b\n#define X_F3                  3c\n#define X_F4                  3d\n#define X_F5                  3e\n#define X_F6                  3f\n#define X_F7                  40\n#define X_F8                  41\n#define X_F9                  42\n#define X_F10                 43\n#define X_F11                 44\n#define X_F12                 45\n#define X_PRINT_SCREEN        46\n#define X_SCROLL_LOCK         47\n#define X_PAUSE               48\n#define X_INSERT              49\n#define X_HOME                4a\n#define X_PAGE_UP             4b\n#define X_DELETE              4c\n#define X_END                 4d\n#define X_PAGE_DOWN           4e\n#define X_RIGHT               4f\n#define X_LEFT                50\n#define X_DOWN                51\n#define X_UP                  52\n#define X_NUM_LOCK            53\n#define X_KP_SLASH            54\n#define X_KP_ASTERISK         55\n#define X_KP_MINUS            56\n#define X_KP_PLUS             57\n#define X_KP_ENTER            58\n#define X_KP_1                59\n#define X_KP_2                5a\n#define X_KP_3                5b\n#define X_KP_4                5c\n#define X_KP_5                5d\n#define X_KP_6                5e\n#define X_KP_7                5f\n#define X_KP_8                60\n#define X_KP_9                61\n#define X_KP_0                62\n#define X_KP_DOT              63\n#define X_NONUS_BACKSLASH     64\n#define X_APPLICATION         65\n#define X_KB_POWER            66\n#define X_KP_EQUAL            67\n#define X_F13                 68\n#define X_F14                 69\n#define X_F15                 6a\n#define X_F16                 6b\n#define X_F17                 6c\n#define X_F18                 6d\n#define X_F19                 6e\n#define X_F20                 6f\n#define X_F21                 70\n#define X_F22                 71\n#define X_F23                 72\n#define X_F24                 73\n#define X_EXECUTE             74\n#define X_HELP                75\n#define X_MENU                76\n#define X_SELECT              77\n#define X_STOP                78\n#define X_AGAIN               79\n#define X_UNDO                7a\n#define X_CUT                 7b\n#define X_COPY                7c\n#define X_PASTE               7d\n#define X_FIND                7e\n#define X_KB_MUTE             7f\n#define X_KB_VOLUME_UP        80\n#define X_KB_VOLUME_DOWN      81\n#define X_LOCKING_CAPS_LOCK   82\n#define X_LOCKING_NUM_LOCK    83\n#define X_LOCKING_SCROLL_LOCK 84\n#define X_KP_COMMA            85\n#define X_KP_EQUAL_AS400      86\n#define X_INTERNATIONAL_1     87\n#define X_INTERNATIONAL_2     88\n#define X_INTERNATIONAL_3     89\n#define X_INTERNATIONAL_4     8a\n#define X_INTERNATIONAL_5     8b\n#define X_INTERNATIONAL_6     8c\n#define X_INTERNATIONAL_7     8d\n#define X_INTERNATIONAL_8     8e\n#define X_INTERNATIONAL_9     8f\n#define X_LANGUAGE_1          90\n#define X_LANGUAGE_2          91\n#define X_LANGUAGE_3          92\n#define X_LANGUAGE_4          93\n#define X_LANGUAGE_5          94\n#define X_LANGUAGE_6          95\n#define X_LANGUAGE_7          96\n#define X_LANGUAGE_8          97\n#define X_LANGUAGE_9          98\n#define X_ALTERNATE_ERASE     99\n#define X_SYSTEM_REQUEST      9a\n#define X_CANCEL              9b\n#define X_CLEAR               9c\n#define X_PRIOR               9d\n#define X_RETURN              9e\n#define X_SEPARATOR           9f\n#define X_OUT                 a0\n#define X_OPER                a1\n#define X_CLEAR_AGAIN         a2\n#define X_CRSEL               a3\n#define X_EXSEL               a4\n\n/* Modifiers */\n#define X_LEFT_CTRL           e0\n#define X_LEFT_SHIFT          e1\n#define X_LEFT_ALT            e2\n#define X_LEFT_GUI            e3\n#define X_RIGHT_CTRL          e4\n#define X_RIGHT_SHIFT         e5\n#define X_RIGHT_ALT           e6\n#define X_RIGHT_GUI           e7\n\n/* Media and Function keys */\n/* Generic Desktop Page (0x01) */\n#define X_SYSTEM_POWER        a5\n#define X_SYSTEM_SLEEP        a6\n#define X_SYSTEM_WAKE         a7\n\n/* Consumer Page (0x0C) */\n#define X_AUDIO_MUTE          a8\n#define X_AUDIO_VOL_UP        a9\n#define X_AUDIO_VOL_DOWN      aa\n#define X_MEDIA_NEXT_TRACK    ab\n#define X_MEDIA_PREV_TRACK    ac\n#define X_MEDIA_STOP          ad\n#define X_MEDIA_PLAY_PAUSE    ae\n#define X_MEDIA_SELECT        af\n#define X_MEDIA_EJECT         b0\n#define X_MAIL                b1\n#define X_CALCULATOR          b2\n#define X_MY_COMPUTER         b3\n#define X_WWW_SEARCH          b4\n#define X_WWW_HOME            b5\n#define X_WWW_BACK            b6\n#define X_WWW_FORWARD         b7\n#define X_WWW_STOP            b8\n#define X_WWW_REFRESH         b9\n#define X_WWW_FAVORITES       ba\n#define X_MEDIA_FAST_FORWARD  bb\n#define X_MEDIA_REWIND        bc\n#define X_BRIGHTNESS_UP       bd\n#define X_BRIGHTNESS_DOWN     be\n#define X_CONTROL_PANEL       bf\n#define X_ASSISTANT           c0\n\n/* Mouse Buttons (unallocated range in HID spec) */\n#define X_MS_UP              cd\n#define X_MS_DOWN            ce\n#define X_MS_LEFT            cf\n#define X_MS_RIGHT           d0\n#define X_MS_BTN1            d1\n#define X_MS_BTN2            d2\n#define X_MS_BTN3            d3\n#define X_MS_BTN4            d4\n#define X_MS_BTN5            d5\n#define X_MS_BTN6            d6\n#define X_MS_BTN7            d7\n#define X_MS_BTN8            d8\n#define X_MS_WH_UP           d9\n#define X_MS_WH_DOWN         da\n#define X_MS_WH_LEFT         db\n#define X_MS_WH_RIGHT        dc\n#define X_MS_ACCEL0          dd\n#define X_MS_ACCEL1          de\n#define X_MS_ACCEL2          df\n\n// Send string macros\n#define STRINGIZE(z) #z\n#define ADD_SLASH_X(y) STRINGIZE(\\x##y)\n#define SYMBOL_STR(x) ADD_SLASH_X(x)\n\n#define SS_QMK_PREFIX 1\n\n#define SS_TAP_CODE 1\n#define SS_DOWN_CODE 2\n#define SS_UP_CODE 3\n#define SS_DELAY_CODE 4\n\n#define SS_TAP(keycode) \"\\1\\1\" SYMBOL_STR(keycode)\n#define SS_DOWN(keycode) \"\\1\\2\" SYMBOL_STR(keycode)\n#define SS_UP(keycode) \"\\1\\3\" SYMBOL_STR(keycode)\n#define SS_DELAY(msecs) \"\\1\\4\" STRINGIZE(msecs) \"|\"\n\n// `string` arguments must not be parenthesized\n#define SS_LCTL(string) SS_DOWN(X_LCTL) string SS_UP(X_LCTL)\n#define SS_LSFT(string) SS_DOWN(X_LSFT) string SS_UP(X_LSFT)\n#define SS_LALT(string) SS_DOWN(X_LALT) string SS_UP(X_LALT)\n#define SS_LGUI(string) SS_DOWN(X_LGUI) string SS_UP(X_LGUI)\n#define SS_LOPT(string) SS_LALT(string)\n#define SS_LCMD(string) SS_LGUI(string)\n#define SS_LWIN(string) SS_LGUI(string)\n\n#define SS_RCTL(string) SS_DOWN(X_RCTL) string SS_UP(X_RCTL)\n#define SS_RSFT(string) SS_DOWN(X_RSFT) string SS_UP(X_RSFT)\n#define SS_RALT(string) SS_DOWN(X_RALT) string SS_UP(X_RALT)\n#define SS_RGUI(string) SS_DOWN(X_RGUI) string SS_UP(X_RGUI)\n#define SS_ALGR(string) SS_RALT(string)\n#define SS_ROPT(string) SS_RALT(string)\n#define SS_RCMD(string) SS_RGUI(string)\n#define SS_RWIN(string) SS_RGUI(string)\n"
  },
  {
    "path": "quantum/sequencer/sequencer.c",
    "content": "/* Copyright 2020 Rodolphe Belouin\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"sequencer.h\"\n#include \"debug.h\"\n#include \"timer.h\"\n\n#ifdef MIDI_ENABLE\n#    include \"process_midi.h\"\n#endif\n\n#ifdef MIDI_MOCKED\n#    include \"tests/midi_mock.h\"\n#endif\n\nsequencer_config_t sequencer_config = {\n    false,    // enabled\n    {false},  // steps\n    {0},      // track notes\n    60,       // tempo\n    SQ_RES_4, // resolution\n};\n\nsequencer_state_t sequencer_internal_state = {0, 0, 0, 0, SEQUENCER_PHASE_ATTACK};\n\nbool is_sequencer_on(void) {\n    return sequencer_config.enabled;\n}\n\nvoid sequencer_on(void) {\n    dprintln(\"sequencer on\");\n    sequencer_config.enabled               = true;\n    sequencer_internal_state.current_track = 0;\n    sequencer_internal_state.current_step  = 0;\n    sequencer_internal_state.timer         = timer_read();\n    sequencer_internal_state.phase         = SEQUENCER_PHASE_ATTACK;\n}\n\nvoid sequencer_off(void) {\n    dprintln(\"sequencer off\");\n    sequencer_config.enabled              = false;\n    sequencer_internal_state.current_step = 0;\n}\n\nvoid sequencer_toggle(void) {\n    if (is_sequencer_on()) {\n        sequencer_off();\n    } else {\n        sequencer_on();\n    }\n}\n\nvoid sequencer_set_track_notes(const uint16_t track_notes[SEQUENCER_TRACKS]) {\n    for (uint8_t i = 0; i < SEQUENCER_TRACKS; i++) {\n        sequencer_config.track_notes[i] = track_notes[i];\n    }\n}\n\nbool is_sequencer_track_active(uint8_t track) {\n    return (sequencer_internal_state.active_tracks >> track) & true;\n}\n\nvoid sequencer_set_track_activation(uint8_t track, bool value) {\n    if (value) {\n        sequencer_internal_state.active_tracks |= (1 << track);\n    } else {\n        sequencer_internal_state.active_tracks &= ~(1 << track);\n    }\n    dprintf(\"sequencer: track %d is %s\\n\", track, value ? \"active\" : \"inactive\");\n}\n\nvoid sequencer_toggle_track_activation(uint8_t track) {\n    sequencer_set_track_activation(track, !is_sequencer_track_active(track));\n}\n\nvoid sequencer_toggle_single_active_track(uint8_t track) {\n    if (is_sequencer_track_active(track)) {\n        sequencer_internal_state.active_tracks = 0;\n    } else {\n        sequencer_internal_state.active_tracks = 1 << track;\n    }\n}\n\nbool is_sequencer_step_on(uint8_t step) {\n    return step < SEQUENCER_STEPS && (sequencer_config.steps[step] & sequencer_internal_state.active_tracks) > 0;\n}\n\nbool is_sequencer_step_on_for_track(uint8_t step, uint8_t track) {\n    return step < SEQUENCER_STEPS && (sequencer_config.steps[step] >> track) & true;\n}\n\nvoid sequencer_set_step(uint8_t step, bool value) {\n    if (step < SEQUENCER_STEPS) {\n        if (value) {\n            sequencer_config.steps[step] |= sequencer_internal_state.active_tracks;\n        } else {\n            sequencer_config.steps[step] &= ~sequencer_internal_state.active_tracks;\n        }\n        dprintf(\"sequencer: step %d is %s\\n\", step, value ? \"on\" : \"off\");\n    } else {\n        dprintf(\"sequencer: step %d is out of range\\n\", step);\n    }\n}\n\nvoid sequencer_toggle_step(uint8_t step) {\n    if (is_sequencer_step_on(step)) {\n        sequencer_set_step_off(step);\n    } else {\n        sequencer_set_step_on(step);\n    }\n}\n\nvoid sequencer_set_all_steps(bool value) {\n    for (uint8_t step = 0; step < SEQUENCER_STEPS; step++) {\n        if (value) {\n            sequencer_config.steps[step] |= sequencer_internal_state.active_tracks;\n        } else {\n            sequencer_config.steps[step] &= ~sequencer_internal_state.active_tracks;\n        }\n    }\n    dprintf(\"sequencer: all steps are %s\\n\", value ? \"on\" : \"off\");\n}\n\nuint8_t sequencer_get_tempo(void) {\n    return sequencer_config.tempo;\n}\n\nvoid sequencer_set_tempo(uint8_t tempo) {\n    if (tempo > 0) {\n        sequencer_config.tempo = tempo;\n        dprintf(\"sequencer: tempo set to %d bpm\\n\", tempo);\n    } else {\n        dprintln(\"sequencer: cannot set tempo to 0\");\n    }\n}\n\nvoid sequencer_increase_tempo(void) {\n    // Handling potential uint8_t overflow\n    if (sequencer_config.tempo < UINT8_MAX) {\n        sequencer_set_tempo(sequencer_config.tempo + 1);\n    } else {\n        dprintf(\"sequencer: cannot set tempo above %d\\n\", UINT8_MAX);\n    }\n}\n\nvoid sequencer_decrease_tempo(void) {\n    sequencer_set_tempo(sequencer_config.tempo - 1);\n}\n\nsequencer_resolution_t sequencer_get_resolution(void) {\n    return sequencer_config.resolution;\n}\n\nvoid sequencer_set_resolution(sequencer_resolution_t resolution) {\n    if (resolution >= 0 && resolution < SEQUENCER_RESOLUTIONS) {\n        sequencer_config.resolution = resolution;\n        dprintf(\"sequencer: resolution set to %d\\n\", resolution);\n    } else {\n        dprintf(\"sequencer: resolution %d is out of range\\n\", resolution);\n    }\n}\n\nvoid sequencer_increase_resolution(void) {\n    sequencer_set_resolution(sequencer_config.resolution + 1);\n}\n\nvoid sequencer_decrease_resolution(void) {\n    sequencer_set_resolution(sequencer_config.resolution - 1);\n}\n\nuint8_t sequencer_get_current_step(void) {\n    return sequencer_internal_state.current_step;\n}\n\nvoid sequencer_phase_attack(void) {\n    dprintf(\"sequencer: step %d\\n\", sequencer_internal_state.current_step);\n    dprintf(\"sequencer: time %d\\n\", timer_read());\n\n    if (sequencer_internal_state.current_track == 0) {\n        sequencer_internal_state.timer = timer_read();\n    }\n\n    if (timer_elapsed(sequencer_internal_state.timer) < sequencer_internal_state.current_track * SEQUENCER_TRACK_THROTTLE) {\n        return;\n    }\n\n#if defined(MIDI_ENABLE) || defined(MIDI_MOCKED)\n    if (is_sequencer_step_on_for_track(sequencer_internal_state.current_step, sequencer_internal_state.current_track)) {\n        process_midi_basic_noteon(midi_compute_note(sequencer_config.track_notes[sequencer_internal_state.current_track]));\n    }\n#endif\n\n    if (sequencer_internal_state.current_track < SEQUENCER_TRACKS - 1) {\n        sequencer_internal_state.current_track++;\n    } else {\n        sequencer_internal_state.phase = SEQUENCER_PHASE_RELEASE;\n    }\n}\n\nvoid sequencer_phase_release(void) {\n    if (timer_elapsed(sequencer_internal_state.timer) < SEQUENCER_PHASE_RELEASE_TIMEOUT + sequencer_internal_state.current_track * SEQUENCER_TRACK_THROTTLE) {\n        return;\n    }\n#if defined(MIDI_ENABLE) || defined(MIDI_MOCKED)\n    if (is_sequencer_step_on_for_track(sequencer_internal_state.current_step, sequencer_internal_state.current_track)) {\n        process_midi_basic_noteoff(midi_compute_note(sequencer_config.track_notes[sequencer_internal_state.current_track]));\n    }\n#endif\n    if (sequencer_internal_state.current_track > 0) {\n        sequencer_internal_state.current_track--;\n    } else {\n        sequencer_internal_state.phase = SEQUENCER_PHASE_PAUSE;\n    }\n}\n\nvoid sequencer_phase_pause(void) {\n    if (timer_elapsed(sequencer_internal_state.timer) < sequencer_get_step_duration()) {\n        return;\n    }\n\n    sequencer_internal_state.current_step = (sequencer_internal_state.current_step + 1) % SEQUENCER_STEPS;\n    sequencer_internal_state.phase        = SEQUENCER_PHASE_ATTACK;\n}\n\nvoid sequencer_task(void) {\n    if (!sequencer_config.enabled) {\n        return;\n    }\n\n    if (sequencer_internal_state.phase == SEQUENCER_PHASE_PAUSE) {\n        sequencer_phase_pause();\n    }\n\n    if (sequencer_internal_state.phase == SEQUENCER_PHASE_RELEASE) {\n        sequencer_phase_release();\n    }\n\n    if (sequencer_internal_state.phase == SEQUENCER_PHASE_ATTACK) {\n        sequencer_phase_attack();\n    }\n}\n\nuint16_t sequencer_get_beat_duration(void) {\n    return get_beat_duration(sequencer_config.tempo);\n}\n\nuint16_t sequencer_get_step_duration(void) {\n    return get_step_duration(sequencer_config.tempo, sequencer_config.resolution);\n}\n\nuint16_t get_beat_duration(uint8_t tempo) {\n    // Don’t crash in the unlikely case where the given tempo is 0\n    if (tempo == 0) {\n        return get_beat_duration(60);\n    }\n\n    /**\n     * Given\n     *  t = tempo and d = duration, both strictly greater than 0\n     * When\n     *  t beats / minute = 1 beat / d ms\n     * Then\n     *  t beats / 60000ms = 1 beat / d ms\n     *  d ms = 60000ms / t\n     */\n    return 60000 / tempo;\n}\n\nuint16_t get_step_duration(uint8_t tempo, sequencer_resolution_t resolution) {\n    /**\n     * Resolution cheatsheet:\n     * 1/2  => 2 steps per 4 beats\n     * 1/2T => 3 steps per 4 beats\n     * 1/4  => 4 steps per 4 beats\n     * 1/4T => 6 steps per 4 beats\n     * 1/8  => 8 steps per 4 beats\n     * 1/8T => 12 steps per 4 beats\n     * 1/16 => 16 steps per 4 beats\n     * 1/16T => 24 steps per 4 beats\n     * 1/32 => 32 steps per 4 beats\n     *\n     * The number of steps for binary resolutions follows the powers of 2.\n     * The ternary variants are simply 1.5x faster.\n     */\n    bool     is_binary            = resolution % 2 == 0;\n    uint8_t  binary_steps         = 2 << (resolution / 2);\n    uint16_t binary_step_duration = get_beat_duration(tempo) * 4 / binary_steps;\n\n    return is_binary ? binary_step_duration : 2 * binary_step_duration / 3;\n}\n"
  },
  {
    "path": "quantum/sequencer/sequencer.h",
    "content": "/* Copyright 2020 Rodolphe Belouin\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n// Maximum number of steps: 256\n#ifndef SEQUENCER_STEPS\n#    define SEQUENCER_STEPS 16\n#endif\n\n// Maximum number of tracks: 8\n#ifndef SEQUENCER_TRACKS\n#    define SEQUENCER_TRACKS 8\n#endif\n\n#ifndef SEQUENCER_TRACK_THROTTLE\n#    define SEQUENCER_TRACK_THROTTLE 3\n#endif\n\n#ifndef SEQUENCER_PHASE_RELEASE_TIMEOUT\n#    define SEQUENCER_PHASE_RELEASE_TIMEOUT 30\n#endif\n\n/**\n * Make sure that the items of this enumeration follow the powers of 2, separated by a ternary variant.\n * Check the implementation of `get_step_duration` for further explanation.\n */\ntypedef enum {\n    SQ_RES_2, //\n    SQ_RES_2T,\n    SQ_RES_4,\n    SQ_RES_4T,\n    SQ_RES_8,\n    SQ_RES_8T,\n    SQ_RES_16,\n    SQ_RES_16T,\n    SQ_RES_32,\n    SEQUENCER_RESOLUTIONS\n} sequencer_resolution_t;\n\ntypedef struct {\n    bool                   enabled;\n    uint8_t                steps[SEQUENCER_STEPS];\n    uint16_t               track_notes[SEQUENCER_TRACKS];\n    uint8_t                tempo; // Is a maximum tempo of 255 reasonable?\n    sequencer_resolution_t resolution;\n} sequencer_config_t;\n\n/**\n * Because Digital Audio Workstations get overwhelmed when too many MIDI signals are sent concurrently,\n * We use a \"phase\" state machine to delay some of the events.\n */\ntypedef enum sequencer_phase_t {\n    SEQUENCER_PHASE_ATTACK,  // t=0ms, send the MIDI note on signal\n    SEQUENCER_PHASE_RELEASE, // t=SEQUENCER_PHASE_RELEASE_TIMEOUT ms, send the MIDI note off signal\n    SEQUENCER_PHASE_PAUSE    // t=step duration ms, loop\n} sequencer_phase_t;\n\ntypedef struct {\n    uint8_t           active_tracks;\n    uint8_t           current_track;\n    uint8_t           current_step;\n    uint16_t          timer;\n    sequencer_phase_t phase;\n} sequencer_state_t;\n\nextern sequencer_config_t sequencer_config;\n\n// We expose the internal state to make the feature more \"unit-testable\"\nextern sequencer_state_t sequencer_internal_state;\n\nbool is_sequencer_on(void);\nvoid sequencer_toggle(void);\nvoid sequencer_on(void);\nvoid sequencer_off(void);\n\nvoid sequencer_set_track_notes(const uint16_t track_notes[SEQUENCER_TRACKS]);\n\nbool is_sequencer_track_active(uint8_t track);\nvoid sequencer_set_track_activation(uint8_t track, bool value);\nvoid sequencer_toggle_track_activation(uint8_t track);\nvoid sequencer_toggle_single_active_track(uint8_t track);\n\n#define sequencer_activate_track(track) sequencer_set_track_activation(track, true)\n#define sequencer_deactivate_track(track) sequencer_set_track_activation(track, false)\n\nbool is_sequencer_step_on(uint8_t step);\nbool is_sequencer_step_on_for_track(uint8_t step, uint8_t track);\nvoid sequencer_set_step(uint8_t step, bool value);\nvoid sequencer_toggle_step(uint8_t step);\nvoid sequencer_set_all_steps(bool value);\n\n#define sequencer_set_step_on(step) sequencer_set_step(step, true)\n#define sequencer_set_step_off(step) sequencer_set_step(step, false)\n#define sequencer_set_all_steps_on() sequencer_set_all_steps(true)\n#define sequencer_set_all_steps_off() sequencer_set_all_steps(false)\n\nuint8_t sequencer_get_tempo(void);\nvoid    sequencer_set_tempo(uint8_t tempo);\nvoid    sequencer_increase_tempo(void);\nvoid    sequencer_decrease_tempo(void);\n\nsequencer_resolution_t sequencer_get_resolution(void);\nvoid                   sequencer_set_resolution(sequencer_resolution_t resolution);\nvoid                   sequencer_increase_resolution(void);\nvoid                   sequencer_decrease_resolution(void);\n\nuint8_t sequencer_get_current_step(void);\n\nuint16_t sequencer_get_beat_duration(void);\nuint16_t sequencer_get_step_duration(void);\n\nuint16_t get_beat_duration(uint8_t tempo);\nuint16_t get_step_duration(uint8_t tempo, sequencer_resolution_t resolution);\n\nvoid sequencer_task(void);\n"
  },
  {
    "path": "quantum/sequencer/tests/midi_mock.c",
    "content": "/* Copyright 2020 Rodolphe Belouin\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"midi_mock.h\"\n\nuint16_t last_noteon  = 0;\nuint16_t last_noteoff = 0;\n\nuint16_t midi_compute_note(uint16_t keycode) {\n    return keycode;\n}\n\nvoid process_midi_basic_noteon(uint16_t note) {\n    last_noteon = note;\n}\n\nvoid process_midi_basic_noteoff(uint16_t note) {\n    last_noteoff = note;\n}\n"
  },
  {
    "path": "quantum/sequencer/tests/midi_mock.h",
    "content": "/* Copyright 2020 Rodolphe Belouin\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n\nextern uint16_t last_noteon;\nextern uint16_t last_noteoff;\n\nuint16_t midi_compute_note(uint16_t keycode);\nvoid     process_midi_basic_noteon(uint16_t note);\nvoid     process_midi_basic_noteoff(uint16_t note);\n"
  },
  {
    "path": "quantum/sequencer/tests/rules.mk",
    "content": "# The letter case of these variables might seem odd. However:\n# - it is consistent with the example that is used as a reference in the Unit Testing article (https://docs.qmk.fm/#/unit_testing?id=adding-tests-for-new-or-existing-features)\n# - Neither `make test:sequencer` or `make test:SEQUENCER` work when using SCREAMING_SNAKE_CASE\n\nsequencer_DEFS := -DMATRIX_ROWS=1 -DMATRIX_COLS=1 -DNO_DEBUG -DMIDI_MOCKED\n\nsequencer_SRC := \\\n\t$(QUANTUM_PATH)/sequencer/tests/midi_mock.c \\\n\t$(QUANTUM_PATH)/sequencer/tests/sequencer_tests.cpp \\\n\t$(QUANTUM_PATH)/sequencer/sequencer.c \\\n\t$(PLATFORM_PATH)/timer.c \\\n\t$(PLATFORM_PATH)/$(PLATFORM_KEY)/timer.c\n"
  },
  {
    "path": "quantum/sequencer/tests/sequencer_tests.cpp",
    "content": "/* Copyright 2020 Rodolphe Belouin\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n\nextern \"C\" {\n#include \"sequencer.h\"\n#include \"midi_mock.h\"\n#include \"quantum/quantum_keycodes.h\"\n}\n\nextern \"C\" {\nvoid set_time(uint32_t t);\nvoid advance_time(uint32_t ms);\n}\n\nclass SequencerTest : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        config_copy.enabled = sequencer_config.enabled;\n\n        for (int i = 0; i < SEQUENCER_STEPS; i++) {\n            config_copy.steps[i] = sequencer_config.steps[i];\n        }\n\n        for (int i = 0; i < SEQUENCER_TRACKS; i++) {\n            config_copy.track_notes[i] = sequencer_config.track_notes[i];\n        }\n\n        config_copy.tempo      = sequencer_config.tempo;\n        config_copy.resolution = sequencer_config.resolution;\n\n        state_copy.active_tracks = sequencer_internal_state.active_tracks;\n        state_copy.current_track = sequencer_internal_state.current_track;\n        state_copy.current_step  = sequencer_internal_state.current_step;\n        state_copy.timer         = sequencer_internal_state.timer;\n\n        last_noteon  = 0;\n        last_noteoff = 0;\n\n        set_time(0);\n    }\n\n    void TearDown() override {\n        sequencer_config.enabled = config_copy.enabled;\n\n        for (int i = 0; i < SEQUENCER_STEPS; i++) {\n            sequencer_config.steps[i] = config_copy.steps[i];\n        }\n\n        for (int i = 0; i < SEQUENCER_TRACKS; i++) {\n            sequencer_config.track_notes[i] = config_copy.track_notes[i];\n        }\n\n        sequencer_config.tempo      = config_copy.tempo;\n        sequencer_config.resolution = config_copy.resolution;\n\n        sequencer_internal_state.active_tracks = state_copy.active_tracks;\n        sequencer_internal_state.current_track = state_copy.current_track;\n        sequencer_internal_state.current_step  = state_copy.current_step;\n        sequencer_internal_state.timer         = state_copy.timer;\n    }\n\n    sequencer_config_t config_copy;\n    sequencer_state_t  state_copy;\n};\n\nTEST_F(SequencerTest, TestOffByDefault) {\n    EXPECT_EQ(is_sequencer_on(), false);\n}\n\nTEST_F(SequencerTest, TestOn) {\n    sequencer_config.enabled = false;\n\n    sequencer_on();\n    EXPECT_EQ(is_sequencer_on(), true);\n\n    // sequencer_on is idempotent\n    sequencer_on();\n    EXPECT_EQ(is_sequencer_on(), true);\n}\n\nTEST_F(SequencerTest, TestOff) {\n    sequencer_config.enabled = true;\n\n    sequencer_off();\n    EXPECT_EQ(is_sequencer_on(), false);\n\n    // sequencer_off is idempotent\n    sequencer_off();\n    EXPECT_EQ(is_sequencer_on(), false);\n}\n\nTEST_F(SequencerTest, TestToggle) {\n    sequencer_config.enabled = false;\n\n    sequencer_toggle();\n    EXPECT_EQ(is_sequencer_on(), true);\n\n    sequencer_toggle();\n    EXPECT_EQ(is_sequencer_on(), false);\n}\n\nTEST_F(SequencerTest, TestNoActiveTrackByDefault) {\n    for (int i = 0; i < SEQUENCER_TRACKS; i++) {\n        EXPECT_EQ(is_sequencer_track_active(i), false);\n    }\n}\n\nTEST_F(SequencerTest, TestGetActiveTracks) {\n    sequencer_internal_state.active_tracks = (1 << 7) + (1 << 6) + (1 << 3) + (1 << 1) + (1 << 0);\n\n    EXPECT_EQ(is_sequencer_track_active(0), true);\n    EXPECT_EQ(is_sequencer_track_active(1), true);\n    EXPECT_EQ(is_sequencer_track_active(2), false);\n    EXPECT_EQ(is_sequencer_track_active(3), true);\n    EXPECT_EQ(is_sequencer_track_active(4), false);\n    EXPECT_EQ(is_sequencer_track_active(5), false);\n    EXPECT_EQ(is_sequencer_track_active(6), true);\n    EXPECT_EQ(is_sequencer_track_active(7), true);\n}\n\nTEST_F(SequencerTest, TestGetActiveTracksOutOfBound) {\n    sequencer_set_track_activation(-1, true);\n    sequencer_set_track_activation(8, true);\n\n    EXPECT_EQ(is_sequencer_track_active(-1), false);\n    EXPECT_EQ(is_sequencer_track_active(8), false);\n}\n\nTEST_F(SequencerTest, TestToggleTrackActivation) {\n    sequencer_internal_state.active_tracks = (1 << 7) + (1 << 6) + (1 << 3) + (1 << 1) + (1 << 0);\n\n    sequencer_toggle_track_activation(6);\n\n    EXPECT_EQ(is_sequencer_track_active(0), true);\n    EXPECT_EQ(is_sequencer_track_active(1), true);\n    EXPECT_EQ(is_sequencer_track_active(2), false);\n    EXPECT_EQ(is_sequencer_track_active(3), true);\n    EXPECT_EQ(is_sequencer_track_active(4), false);\n    EXPECT_EQ(is_sequencer_track_active(5), false);\n    EXPECT_EQ(is_sequencer_track_active(6), false);\n    EXPECT_EQ(is_sequencer_track_active(7), true);\n}\n\nTEST_F(SequencerTest, TestToggleSingleTrackActivation) {\n    sequencer_internal_state.active_tracks = (1 << 7) + (1 << 6) + (1 << 3) + (1 << 1) + (1 << 0);\n\n    sequencer_toggle_single_active_track(2);\n\n    EXPECT_EQ(is_sequencer_track_active(0), false);\n    EXPECT_EQ(is_sequencer_track_active(1), false);\n    EXPECT_EQ(is_sequencer_track_active(2), true);\n    EXPECT_EQ(is_sequencer_track_active(3), false);\n    EXPECT_EQ(is_sequencer_track_active(4), false);\n    EXPECT_EQ(is_sequencer_track_active(5), false);\n    EXPECT_EQ(is_sequencer_track_active(6), false);\n    EXPECT_EQ(is_sequencer_track_active(7), false);\n}\n\nTEST_F(SequencerTest, TestStepOffByDefault) {\n    for (int i = 0; i < SEQUENCER_STEPS; i++) {\n        EXPECT_EQ(is_sequencer_step_on(i), false);\n    }\n}\n\nTEST_F(SequencerTest, TestIsStepOffWithNoActiveTracks) {\n    sequencer_config.steps[3] = 0xFF;\n    EXPECT_EQ(is_sequencer_step_on(3), false);\n}\n\nTEST_F(SequencerTest, TestIsStepOffWithGivenActiveTracks) {\n    sequencer_set_track_activation(2, true);\n    sequencer_set_track_activation(3, true);\n\n    sequencer_config.steps[3] = (1 << 0) + (1 << 1);\n\n    // No active tracks have the step enabled, so it is off\n    EXPECT_EQ(is_sequencer_step_on(3), false);\n}\n\nTEST_F(SequencerTest, TestIsStepOnWithGivenActiveTracks) {\n    sequencer_set_track_activation(2, true);\n    sequencer_set_track_activation(3, true);\n\n    sequencer_config.steps[3] = (1 << 2);\n\n    // Track 2 has the step enabled, so it is on\n    EXPECT_EQ(is_sequencer_step_on(3), true);\n}\n\nTEST_F(SequencerTest, TestIsStepOffForGivenTrack) {\n    sequencer_config.steps[3] = 0x00;\n    EXPECT_EQ(is_sequencer_step_on_for_track(3, 5), false);\n}\n\nTEST_F(SequencerTest, TestIsStepOnForGivenTrack) {\n    sequencer_config.steps[3] = (1 << 5);\n    EXPECT_EQ(is_sequencer_step_on_for_track(3, 5), true);\n}\n\nTEST_F(SequencerTest, TestSetStepOn) {\n    sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);\n    sequencer_config.steps[2]              = (1 << 5) + (1 << 2);\n\n    sequencer_set_step(2, true);\n\n    EXPECT_EQ(sequencer_config.steps[2], (1 << 6) + (1 << 5) + (1 << 3) + (1 << 2));\n}\n\nTEST_F(SequencerTest, TestSetStepOff) {\n    sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);\n    sequencer_config.steps[2]              = (1 << 5) + (1 << 2);\n\n    sequencer_set_step(2, false);\n\n    EXPECT_EQ(sequencer_config.steps[2], (1 << 5));\n}\n\nTEST_F(SequencerTest, TestToggleStepOff) {\n    sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);\n    sequencer_config.steps[2]              = (1 << 5) + (1 << 2);\n\n    sequencer_toggle_step(2);\n\n    EXPECT_EQ(sequencer_config.steps[2], (1 << 5));\n}\n\nTEST_F(SequencerTest, TestToggleStepOn) {\n    sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);\n    sequencer_config.steps[2]              = 0;\n\n    sequencer_toggle_step(2);\n\n    EXPECT_EQ(sequencer_config.steps[2], (1 << 6) + (1 << 3) + (1 << 2));\n}\n\nTEST_F(SequencerTest, TestSetAllStepsOn) {\n    sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);\n    sequencer_config.steps[2]              = (1 << 7) + (1 << 6);\n    sequencer_config.steps[4]              = (1 << 3) + (1 << 1);\n\n    sequencer_set_all_steps(true);\n\n    EXPECT_EQ(sequencer_config.steps[2], (1 << 7) + (1 << 6) + (1 << 3) + (1 << 2));\n    EXPECT_EQ(sequencer_config.steps[4], (1 << 6) + (1 << 3) + (1 << 2) + (1 << 1));\n}\n\nTEST_F(SequencerTest, TestSetAllStepsOff) {\n    sequencer_internal_state.active_tracks = (1 << 6) + (1 << 3) + (1 << 2);\n    sequencer_config.steps[2]              = (1 << 7) + (1 << 6);\n    sequencer_config.steps[4]              = (1 << 3) + (1 << 1);\n\n    sequencer_set_all_steps(false);\n\n    EXPECT_EQ(sequencer_config.steps[2], (1 << 7));\n    EXPECT_EQ(sequencer_config.steps[4], (1 << 1));\n}\n\nTEST_F(SequencerTest, TestSetTempoZero) {\n    sequencer_config.tempo = 123;\n\n    sequencer_set_tempo(0);\n\n    EXPECT_EQ(sequencer_config.tempo, 123);\n}\n\nTEST_F(SequencerTest, TestIncreaseTempoMax) {\n    sequencer_config.tempo = UINT8_MAX;\n\n    sequencer_increase_tempo();\n\n    EXPECT_EQ(sequencer_config.tempo, UINT8_MAX);\n}\n\nTEST_F(SequencerTest, TestSetResolutionLowerBound) {\n    sequencer_config.resolution = SQ_RES_4;\n\n    sequencer_set_resolution((sequencer_resolution_t)-1);\n\n    EXPECT_EQ(sequencer_config.resolution, SQ_RES_4);\n}\n\nTEST_F(SequencerTest, TestSetResolutionUpperBound) {\n    sequencer_config.resolution = SQ_RES_4;\n\n    sequencer_set_resolution(SEQUENCER_RESOLUTIONS);\n\n    EXPECT_EQ(sequencer_config.resolution, SQ_RES_4);\n}\n\nTEST_F(SequencerTest, TestGetBeatDuration) {\n    EXPECT_EQ(get_beat_duration(60), 1000);\n    EXPECT_EQ(get_beat_duration(120), 500);\n    EXPECT_EQ(get_beat_duration(240), 250);\n    EXPECT_EQ(get_beat_duration(0), 1000);\n}\n\nTEST_F(SequencerTest, TestGetStepDuration60) {\n    /**\n     * Resolution cheatsheet:\n     * 1/2  => 2 steps per 4 beats\n     * 1/2T => 3 steps per 4 beats\n     * 1/4  => 4 steps per 4 beats\n     * 1/4T => 6 steps per 4 beats\n     * 1/8  => 8 steps per 4 beats\n     * 1/8T => 12 steps per 4 beats\n     * 1/16 => 16 steps per 4 beats\n     * 1/16T => 24 steps per 4 beats\n     * 1/32 => 32 steps per 4 beats\n     *\n     * The number of steps for binary resolutions follows the powers of 2.\n     * The ternary variants are simply 1.5x faster.\n     */\n    EXPECT_EQ(get_step_duration(60, SQ_RES_2), 2000);\n    EXPECT_EQ(get_step_duration(60, SQ_RES_4), 1000);\n    EXPECT_EQ(get_step_duration(60, SQ_RES_8), 500);\n    EXPECT_EQ(get_step_duration(60, SQ_RES_16), 250);\n    EXPECT_EQ(get_step_duration(60, SQ_RES_32), 125);\n\n    EXPECT_EQ(get_step_duration(60, SQ_RES_2T), 1333);\n    EXPECT_EQ(get_step_duration(60, SQ_RES_4T), 666);\n    EXPECT_EQ(get_step_duration(60, SQ_RES_8T), 333);\n    EXPECT_EQ(get_step_duration(60, SQ_RES_16T), 166);\n}\n\nTEST_F(SequencerTest, TestGetStepDuration120) {\n    /**\n     * Resolution cheatsheet:\n     * 1/2  => 2 steps per 4 beats\n     * 1/2T => 3 steps per 4 beats\n     * 1/4  => 4 steps per 4 beats\n     * 1/4T => 6 steps per 4 beats\n     * 1/8  => 8 steps per 4 beats\n     * 1/8T => 12 steps per 4 beats\n     * 1/16 => 16 steps per 4 beats\n     * 1/16T => 24 steps per 4 beats\n     * 1/32 => 32 steps per 4 beats\n     *\n     * The number of steps for binary resolutions follows the powers of 2.\n     * The ternary variants are simply 1.5x faster.\n     */\n    EXPECT_EQ(get_step_duration(30, SQ_RES_2), 4000);\n    EXPECT_EQ(get_step_duration(30, SQ_RES_4), 2000);\n    EXPECT_EQ(get_step_duration(30, SQ_RES_8), 1000);\n    EXPECT_EQ(get_step_duration(30, SQ_RES_16), 500);\n    EXPECT_EQ(get_step_duration(30, SQ_RES_32), 250);\n\n    EXPECT_EQ(get_step_duration(30, SQ_RES_2T), 2666);\n    EXPECT_EQ(get_step_duration(30, SQ_RES_4T), 1333);\n    EXPECT_EQ(get_step_duration(30, SQ_RES_8T), 666);\n    EXPECT_EQ(get_step_duration(30, SQ_RES_16T), 333);\n}\n\nvoid setUpMatrixScanSequencerTest(void) {\n    sequencer_config.enabled    = true;\n    sequencer_config.tempo      = 120;\n    sequencer_config.resolution = SQ_RES_16;\n\n    // Configure the notes for each track\n    sequencer_config.track_notes[0] = QK_MIDI_NOTE_C_0;\n    sequencer_config.track_notes[1] = QK_MIDI_NOTE_D_0;\n    sequencer_config.track_notes[2] = QK_MIDI_NOTE_E_0;\n    sequencer_config.track_notes[3] = QK_MIDI_NOTE_F_0;\n    sequencer_config.track_notes[4] = QK_MIDI_NOTE_G_0;\n    sequencer_config.track_notes[5] = QK_MIDI_NOTE_A_0;\n    sequencer_config.track_notes[6] = QK_MIDI_NOTE_B_0;\n    sequencer_config.track_notes[7] = QK_MIDI_NOTE_C_0;\n\n    // Turn on some steps\n    sequencer_config.steps[0] = (1 << 0);\n    sequencer_config.steps[2] = (1 << 1) + (1 << 0);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackFirstTrackOfFirstStep) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_task();\n    EXPECT_EQ(last_noteon, QK_MIDI_NOTE_C_0);\n    EXPECT_EQ(last_noteoff, 0);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackSecondTrackAfterFirstTrackOfFirstStep) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_task();\n    EXPECT_EQ(sequencer_internal_state.current_step, 0);\n    EXPECT_EQ(sequencer_internal_state.current_track, 1);\n    EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldNotAttackInactiveTrackFirstStep) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_internal_state.current_step  = 0;\n    sequencer_internal_state.current_track = 1;\n\n    // Wait some time after the first track has been attacked\n    advance_time(SEQUENCER_TRACK_THROTTLE);\n\n    sequencer_task();\n    EXPECT_EQ(last_noteon, 0);\n    EXPECT_EQ(last_noteoff, 0);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldAttackThirdTrackAfterSecondTrackOfFirstStep) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_internal_state.current_step  = 0;\n    sequencer_internal_state.current_track = 1;\n\n    // Wait some time after the second track has been attacked\n    advance_time(2 * SEQUENCER_TRACK_THROTTLE);\n\n    sequencer_task();\n    EXPECT_EQ(sequencer_internal_state.current_step, 0);\n    EXPECT_EQ(sequencer_internal_state.current_track, 2);\n    EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldEnterReleasePhaseAfterLastTrackHasBeenProcessedFirstStep) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_internal_state.current_step  = 0;\n    sequencer_internal_state.current_track = SEQUENCER_TRACKS - 1;\n\n    // Wait until all notes have been attacked\n    advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);\n\n    sequencer_task();\n    EXPECT_EQ(last_noteon, 0);\n    EXPECT_EQ(last_noteoff, 0);\n    EXPECT_EQ(sequencer_internal_state.current_step, 0);\n    EXPECT_EQ(sequencer_internal_state.current_track, SEQUENCER_TRACKS - 1);\n    EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_RELEASE);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldReleaseBackwards) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_internal_state.current_step  = 0;\n    sequencer_internal_state.current_track = SEQUENCER_TRACKS - 1;\n    sequencer_internal_state.phase         = SEQUENCER_PHASE_RELEASE;\n\n    // Wait until all notes have been attacked\n    advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);\n    // + the release timeout\n    advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);\n\n    sequencer_task();\n    EXPECT_EQ(sequencer_internal_state.current_step, 0);\n    EXPECT_EQ(sequencer_internal_state.current_track, SEQUENCER_TRACKS - 2);\n    EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_RELEASE);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldNotReleaseInactiveTrackFirstStep) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_internal_state.current_step  = 0;\n    sequencer_internal_state.current_track = SEQUENCER_TRACKS - 1;\n    sequencer_internal_state.phase         = SEQUENCER_PHASE_RELEASE;\n\n    // Wait until all notes have been attacked\n    advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);\n    // + the release timeout\n    advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);\n\n    sequencer_task();\n    EXPECT_EQ(last_noteon, 0);\n    EXPECT_EQ(last_noteoff, 0);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldReleaseFirstTrackFirstStep) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_internal_state.current_step  = 0;\n    sequencer_internal_state.current_track = 0;\n    sequencer_internal_state.phase         = SEQUENCER_PHASE_RELEASE;\n\n    // Wait until all notes have been attacked\n    advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);\n    // + the release timeout\n    advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);\n    // + all the other notes have been released\n    advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);\n\n    sequencer_task();\n    EXPECT_EQ(last_noteon, 0);\n    EXPECT_EQ(last_noteoff, QK_MIDI_NOTE_C_0);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldEnterPausePhaseAfterRelease) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_internal_state.current_step  = 0;\n    sequencer_internal_state.current_track = 0;\n    sequencer_internal_state.phase         = SEQUENCER_PHASE_RELEASE;\n\n    // Wait until all notes have been attacked\n    advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);\n    // + the release timeout\n    advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);\n    // + all the other notes have been released\n    advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);\n\n    sequencer_task();\n    EXPECT_EQ(sequencer_internal_state.current_step, 0);\n    EXPECT_EQ(sequencer_internal_state.current_track, 0);\n    EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_PAUSE);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessFirstTrackOfSecondStepAfterPause) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_internal_state.current_step  = 0;\n    sequencer_internal_state.current_track = 0;\n    sequencer_internal_state.phase         = SEQUENCER_PHASE_PAUSE;\n\n    // Wait until all notes have been attacked\n    advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);\n    // + the release timeout\n    advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);\n    // + all the other notes have been released\n    advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);\n    // + the step duration (one 16th at tempo=120 lasts 125ms)\n    advance_time(125);\n\n    sequencer_task();\n    EXPECT_EQ(sequencer_internal_state.current_step, 1);\n    EXPECT_EQ(sequencer_internal_state.current_track, 1);\n    EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessSecondTrackTooEarly) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_internal_state.current_step  = 2;\n    sequencer_internal_state.current_track = 1;\n\n    sequencer_task();\n    EXPECT_EQ(last_noteon, 0);\n    EXPECT_EQ(last_noteoff, 0);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldProcessSecondTrackOnTime) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_internal_state.current_step  = 2;\n    sequencer_internal_state.current_track = 1;\n\n    // Wait until first track has been attacked\n    advance_time(SEQUENCER_TRACK_THROTTLE);\n\n    sequencer_task();\n    EXPECT_EQ(last_noteon, QK_MIDI_NOTE_D_0);\n    EXPECT_EQ(last_noteoff, 0);\n}\n\nTEST_F(SequencerTest, TestMatrixScanSequencerShouldLoopOnceSequenceIsOver) {\n    setUpMatrixScanSequencerTest();\n\n    sequencer_internal_state.current_step  = SEQUENCER_STEPS - 1;\n    sequencer_internal_state.current_track = 0;\n    sequencer_internal_state.phase         = SEQUENCER_PHASE_PAUSE;\n\n    // Wait until all notes have been attacked\n    advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);\n    // + the release timeout\n    advance_time(SEQUENCER_PHASE_RELEASE_TIMEOUT);\n    // + all the other notes have been released\n    advance_time((SEQUENCER_TRACKS - 1) * SEQUENCER_TRACK_THROTTLE);\n    // + the step duration (one 16th at tempo=120 lasts 125ms)\n    advance_time(125);\n\n    sequencer_task();\n    EXPECT_EQ(sequencer_internal_state.current_step, 0);\n    EXPECT_EQ(sequencer_internal_state.current_track, 1);\n    EXPECT_EQ(sequencer_internal_state.phase, SEQUENCER_PHASE_ATTACK);\n}\n"
  },
  {
    "path": "quantum/sequencer/tests/testlist.mk",
    "content": "TEST_LIST += sequencer\n"
  },
  {
    "path": "quantum/split_common/post_config.h",
    "content": "#if defined(USE_I2C)\n// When using I2C, using rgblight implicitly involves split support.\n#    if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_SPLIT)\n#        define RGBLIGHT_SPLIT\n#    endif\n\n#    ifndef F_SCL\n#        define F_SCL 100000UL // SCL frequency\n#    endif\n#endif\n"
  },
  {
    "path": "quantum/split_common/split_util.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"compiler_support.h\"\n#include \"split_util.h\"\n#include \"matrix.h\"\n#include \"keyboard.h\"\n#include \"timer.h\"\n#include \"transport.h\"\n#include \"wait.h\"\n#include \"debug.h\"\n#include \"usb_util.h\"\n#include \"bootloader.h\"\n\n#ifdef EE_HANDS\n#    include \"eeconfig.h\"\n#endif\n\n#if defined(RGBLIGHT_ENABLE) && defined(RGBLED_SPLIT)\n#    include \"rgblight.h\"\n#endif\n\n#ifndef SPLIT_USB_TIMEOUT\n#    define SPLIT_USB_TIMEOUT 2000\n#endif\n\n#ifndef SPLIT_USB_TIMEOUT_POLL\n#    define SPLIT_USB_TIMEOUT_POLL 10\n#endif\n\n// Max number of consecutive failed communications (one per scan cycle) before the communication is seen as disconnected.\n// Set to 0 to disable the disconnection check altogether.\n#ifndef SPLIT_MAX_CONNECTION_ERRORS\n#    define SPLIT_MAX_CONNECTION_ERRORS 10\n#endif // SPLIT_MAX_CONNECTION_ERRORS\n\n// How long (in milliseconds) to block all connection attempts after the communication has been flagged as disconnected.\n// One communication attempt will be allowed everytime this amount of time has passed since the last attempt. If that attempt succeeds, the communication is seen as working again.\n// Set to 0 to disable communication throttling while disconnected\n#ifndef SPLIT_CONNECTION_CHECK_TIMEOUT\n#    define SPLIT_CONNECTION_CHECK_TIMEOUT 500\n#endif // SPLIT_CONNECTION_CHECK_TIMEOUT\n\nstatic uint8_t connection_errors = 0;\n\nvolatile bool isLeftHand = true;\n\nstatic struct {\n    bool master;\n    bool left;\n} split_config;\n\n#if defined(SPLIT_USB_DETECT)\nSTATIC_ASSERT((SPLIT_USB_TIMEOUT / SPLIT_USB_TIMEOUT_POLL) <= UINT16_MAX, \"Please lower SPLIT_USB_TIMEOUT and/or increase SPLIT_USB_TIMEOUT_POLL.\");\nstatic bool usb_bus_detected(void) {\n    for (uint16_t i = 0; i < (SPLIT_USB_TIMEOUT / SPLIT_USB_TIMEOUT_POLL); i++) {\n        // This will return true if a USB connection has been established\n        if (usb_connected_state()) {\n            return true;\n        }\n        wait_ms(SPLIT_USB_TIMEOUT_POLL);\n    }\n    return false;\n}\n#else\nstatic inline bool usb_bus_detected(void) {\n    return usb_vbus_state();\n}\n#endif\n\n#if defined(SPLIT_WATCHDOG_ENABLE)\n#    if !defined(SPLIT_WATCHDOG_TIMEOUT)\n#        if defined(SPLIT_USB_TIMEOUT)\n#            define SPLIT_WATCHDOG_TIMEOUT (SPLIT_USB_TIMEOUT + 100)\n#        else\n#            define SPLIT_WATCHDOG_TIMEOUT 3000\n#        endif\n#    endif\n#    if defined(SPLIT_USB_DETECT)\nSTATIC_ASSERT(SPLIT_USB_TIMEOUT < SPLIT_WATCHDOG_TIMEOUT, \"SPLIT_WATCHDOG_TIMEOUT should not be below SPLIT_USB_TIMEOUT.\");\n#    endif\nSTATIC_ASSERT(SPLIT_MAX_CONNECTION_ERRORS > 0, \"SPLIT_WATCHDOG_ENABLE requires SPLIT_MAX_CONNECTION_ERRORS be above 0 for a functioning disconnection check.\");\n\nstatic uint32_t split_watchdog_started = 0;\nstatic bool     split_watchdog_done    = false;\n\nvoid split_watchdog_init(void) {\n    split_watchdog_started = timer_read32();\n}\n\nvoid split_watchdog_update(bool done) {\n    split_watchdog_done = done;\n}\n\nbool split_watchdog_check(void) {\n    if (!is_transport_connected()) {\n        split_watchdog_done = false;\n    }\n    return split_watchdog_done;\n}\n\nvoid split_watchdog_task(void) {\n    if (!split_watchdog_done && !is_keyboard_master()) {\n        if (timer_elapsed32(split_watchdog_started) > SPLIT_WATCHDOG_TIMEOUT) {\n            mcu_reset();\n        }\n    }\n}\n#endif // defined(SPLIT_WATCHDOG_ENABLE)\n\n#ifdef SPLIT_HAND_MATRIX_GRID\nvoid matrix_io_delay(void);\n\nstatic uint8_t peek_matrix_intersection(pin_t out_pin, pin_t in_pin) {\n    gpio_set_pin_input_high(in_pin);\n    gpio_set_pin_output(out_pin);\n    gpio_write_pin_low(out_pin);\n    // It's almost unnecessary, but wait until it's down to low, just in case.\n    wait_us(1);\n    uint8_t pin_state = gpio_read_pin(in_pin);\n    // Set out_pin to a setting that is less susceptible to noise.\n    gpio_set_pin_input_high(out_pin);\n    matrix_io_delay(); // Wait for the pull-up to go HIGH.\n    return pin_state;\n}\n#endif\n\n__attribute__((weak)) bool is_keyboard_left_impl(void) {\n#if defined(SPLIT_HAND_PIN)\n    gpio_set_pin_input(SPLIT_HAND_PIN);\n    wait_us(100);\n    // Test pin SPLIT_HAND_PIN for High/Low, if low it's right hand\n#    ifdef SPLIT_HAND_PIN_LOW_IS_LEFT\n    return !gpio_read_pin(SPLIT_HAND_PIN);\n#    else\n    return gpio_read_pin(SPLIT_HAND_PIN);\n#    endif\n#elif defined(SPLIT_HAND_MATRIX_GRID)\n#    ifdef SPLIT_HAND_MATRIX_GRID_LOW_IS_LEFT\n    return !peek_matrix_intersection(SPLIT_HAND_MATRIX_GRID);\n#    else\n    return peek_matrix_intersection(SPLIT_HAND_MATRIX_GRID);\n#    endif\n#elif defined(EE_HANDS)\n    if (!eeconfig_is_enabled()) {\n        eeconfig_init();\n    }\n    // TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS within the emulated eeprom via dfu-util or another tool\n#    if defined(INIT_EE_HANDS_LEFT) || defined(INIT_EE_HANDS_RIGHT)\n#        if defined(INIT_EE_HANDS_LEFT)\n#            pragma message \"Faking EE_HANDS for left hand\"\n    const bool should_be_left = true;\n#        else\n#            pragma message \"Faking EE_HANDS for right hand\"\n    const bool should_be_left = false;\n#        endif\n    bool       is_left        = eeconfig_read_handedness();\n    if (is_left != should_be_left) {\n        eeconfig_update_handedness(should_be_left);\n    }\n#    endif // defined(INIT_EE_HANDS_LEFT) || defined(INIT_EE_HANDS_RIGHT)\n    return eeconfig_read_handedness();\n#elif defined(MASTER_RIGHT)\n    return !is_keyboard_master();\n#else\n    return is_keyboard_master();\n#endif\n}\n\n__attribute__((weak)) bool is_keyboard_master_impl(void) {\n    bool is_master = usb_bus_detected();\n\n    // Avoid NO_USB_STARTUP_CHECK - Disable USB as the previous checks seem to enable it somehow\n    if (!is_master) {\n        usb_disconnect();\n    }\n    return is_master;\n}\n\n__attribute__((weak)) bool is_keyboard_left(void) {\n    return split_config.left;\n}\n\n__attribute__((weak)) bool is_keyboard_master(void) {\n    return split_config.master;\n}\n\n// this code runs before the keyboard is fully initialized\nvoid split_pre_init(void) {\n    split_config.master = is_keyboard_master_impl();\n    split_config.left   = is_keyboard_left_impl();\n\n    isLeftHand = is_keyboard_left(); // TODO: Remove isLeftHand\n\n#if defined(RGBLIGHT_ENABLE) && defined(RGBLED_SPLIT)\n    uint8_t num_rgb_leds_split[2] = RGBLED_SPLIT;\n    if (is_keyboard_left()) {\n        rgblight_set_clipping_range(0, num_rgb_leds_split[0]);\n    } else {\n        rgblight_set_clipping_range(num_rgb_leds_split[0], num_rgb_leds_split[1]);\n    }\n#endif\n\n    if (is_keyboard_master()) {\n        transport_master_init();\n    }\n}\n\n// this code runs after the keyboard is fully initialized\n//   - avoids race condition during matrix_init_quantum where slave can start\n//     receiving before the init process has completed\nvoid split_post_init(void) {\n    if (!is_keyboard_master()) {\n        transport_slave_init();\n#if defined(SPLIT_WATCHDOG_ENABLE)\n        split_watchdog_init();\n#endif\n    }\n}\n\nbool is_transport_connected(void) {\n    return connection_errors < SPLIT_MAX_CONNECTION_ERRORS;\n}\n\nbool transport_master_if_connected(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n#if SPLIT_MAX_CONNECTION_ERRORS > 0 && SPLIT_CONNECTION_CHECK_TIMEOUT > 0\n    // Throttle transaction attempts if target doesn't seem to be connected\n    // Without this, a solo half becomes unusable due to constant read timeouts\n    static uint16_t connection_check_timer = 0;\n    const bool      is_disconnected        = !is_transport_connected();\n    if (is_disconnected && timer_elapsed(connection_check_timer) < SPLIT_CONNECTION_CHECK_TIMEOUT) {\n        return false;\n    }\n#endif // SPLIT_MAX_CONNECTION_ERRORS > 0 && SPLIT_CONNECTION_CHECK_TIMEOUT > 0\n\n    __attribute__((unused)) bool okay = transport_master(master_matrix, slave_matrix);\n#if SPLIT_MAX_CONNECTION_ERRORS > 0\n    if (!okay) {\n        if (connection_errors < UINT8_MAX) {\n            connection_errors++;\n        }\n#    if SPLIT_CONNECTION_CHECK_TIMEOUT > 0\n        bool connected = is_transport_connected();\n        if (!connected) {\n            connection_check_timer = timer_read();\n            dprintln(\"Target disconnected, throttling connection attempts\");\n        }\n        return connected;\n    } else if (is_disconnected) {\n        dprintln(\"Target connected\");\n#    endif // SPLIT_CONNECTION_CHECK_TIMEOUT > 0\n    }\n\n    connection_errors = 0;\n#endif // SPLIT_MAX_CONNECTION_ERRORS > 0\n    return true;\n}\n"
  },
  {
    "path": "quantum/split_common/split_util.h",
    "content": "#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#include \"matrix.h\"\n\nextern volatile bool isLeftHand;\n\nvoid split_pre_init(void);\nvoid split_post_init(void);\n\nbool transport_master_if_connected(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);\nbool is_transport_connected(void);\n\nvoid split_watchdog_update(bool done);\nvoid split_watchdog_task(void);\nbool split_watchdog_check(void);\n"
  },
  {
    "path": "quantum/split_common/transaction_id_define.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"compiler_support.h\"\n\nenum serial_transaction_id {\n#ifdef USE_I2C\n    I2C_EXECUTE_CALLBACK,\n#endif // USE_I2C\n\n    GET_SLAVE_MATRIX_CHECKSUM,\n    GET_SLAVE_MATRIX_DATA,\n\n#ifdef SPLIT_TRANSPORT_MIRROR\n    PUT_MASTER_MATRIX,\n#endif // SPLIT_TRANSPORT_MIRROR\n\n#ifdef ENCODER_ENABLE\n    GET_ENCODERS_CHECKSUM,\n    GET_ENCODERS_DATA,\n    CMD_ENCODER_DRAIN,\n#endif // ENCODER_ENABLE\n\n#ifndef DISABLE_SYNC_TIMER\n    PUT_SYNC_TIMER,\n#endif // DISABLE_SYNC_TIMER\n\n#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)\n    PUT_LAYER_STATE,\n    PUT_DEFAULT_LAYER_STATE,\n#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)\n\n#ifdef SPLIT_LED_STATE_ENABLE\n    PUT_LED_STATE,\n#endif // SPLIT_LED_STATE_ENABLE\n\n#ifdef SPLIT_MODS_ENABLE\n    PUT_MODS,\n#endif // SPLIT_MODS_ENABLE\n\n#ifdef BACKLIGHT_ENABLE\n    PUT_BACKLIGHT,\n#endif // BACKLIGHT_ENABLE\n\n#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)\n    PUT_RGBLIGHT,\n#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)\n\n#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)\n    PUT_LED_MATRIX,\n#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)\n\n#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)\n    PUT_RGB_MATRIX,\n#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)\n\n#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)\n    PUT_WPM,\n#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)\n\n#if defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)\n    PUT_OLED,\n#endif // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)\n\n#if defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)\n    PUT_ST7565,\n#endif // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)\n\n#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)\n    GET_POINTING_CHECKSUM,\n    GET_POINTING_DATA,\n    PUT_POINTING_CPI,\n#endif // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)\n\n#if defined(SPLIT_WATCHDOG_ENABLE)\n    PUT_WATCHDOG,\n#endif // defined(SPLIT_WATCHDOG_ENABLE)\n\n#if defined(HAPTIC_ENABLE) && defined(SPLIT_HAPTIC_ENABLE)\n    PUT_HAPTIC,\n#endif // defined(HAPTIC_ENABLE) && defined(SPLIT_HAPTIC_ENABLE)\n\n#if defined(SPLIT_ACTIVITY_ENABLE)\n    PUT_ACTIVITY,\n#endif // SPLIT_ACTIVITY_ENABLE\n\n#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\n    PUT_RPC_INFO,\n    PUT_RPC_REQ_DATA,\n    EXECUTE_RPC,\n    GET_RPC_RESP_DATA,\n#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\n\n// keyboard-specific\n#ifdef SPLIT_TRANSACTION_IDS_KB\n    SPLIT_TRANSACTION_IDS_KB,\n#endif // SPLIT_TRANSACTION_IDS_KB\n\n// user/keymap-specific\n#ifdef SPLIT_TRANSACTION_IDS_USER\n    SPLIT_TRANSACTION_IDS_USER,\n#endif // SPLIT_TRANSACTION_IDS_USER\n\n#if defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)\n    PUT_DETECTED_OS,\n#endif // defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)\n\n    NUM_TOTAL_TRANSACTIONS\n};\n\n// Ensure we only use 5 bits for transaction\nSTATIC_ASSERT(NUM_TOTAL_TRANSACTIONS <= (1 << 5), \"Max number of usable transactions exceeded\");\n"
  },
  {
    "path": "quantum/split_common/transactions.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <stdint.h>\n#include <string.h>\n#include <stddef.h>\n\n#include \"crc.h\"\n#include \"debug.h\"\n#include \"matrix.h\"\n#include \"host.h\"\n#include \"action_util.h\"\n#include \"sync_timer.h\"\n#include \"wait.h\"\n#include \"transactions.h\"\n#include \"transport.h\"\n#include \"transaction_id_define.h\"\n#include \"split_util.h\"\n#include \"synchronization_util.h\"\n\n#ifdef BACKLIGHT_ENABLE\n#    include \"backlight.h\"\n#endif\n#ifdef RGBLIGHT_ENABLE\n#    include \"rgblight.h\"\n#endif\n#ifdef LED_MATRIX_ENABLE\n#    include \"led_matrix.h\"\n#endif\n#ifdef RGB_MATRIX_ENABLE\n#    include \"rgb_matrix.h\"\n#endif\n#ifdef OLED_ENABLE\n#    include \"oled_driver.h\"\n#endif\n#ifdef ST7565_ENABLE\n#    include \"st7565.h\"\n#endif\n#ifdef ENCODER_ENABLE\n#    include \"encoder.h\"\n#endif\n#ifdef HAPTIC_ENABLE\n#    include \"haptic.h\"\n#endif\n#ifdef POINTING_DEVICE_ENABLE\n#    include \"pointing_device.h\"\n#endif\n#ifdef OS_DETECTION_ENABLE\n#    include \"os_detection.h\"\n#endif\n#ifdef WPM_ENABLE\n#    include \"wpm.h\"\n#endif\n\n#define SYNC_TIMER_OFFSET 2\n\n#ifndef FORCED_SYNC_THROTTLE_MS\n#    define FORCED_SYNC_THROTTLE_MS 100\n#endif // FORCED_SYNC_THROTTLE_MS\n\n#define sizeof_member(type, member) sizeof(((type *)NULL)->member)\n\n#define trans_initiator2target_initializer_cb(member, cb) \\\n    { sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), 0, 0, cb }\n#define trans_initiator2target_initializer(member) trans_initiator2target_initializer_cb(member, NULL)\n\n#define trans_target2initiator_initializer_cb(member, cb) \\\n    { 0, 0, sizeof_member(split_shared_memory_t, member), offsetof(split_shared_memory_t, member), cb }\n#define trans_target2initiator_initializer(member) trans_target2initiator_initializer_cb(member, NULL)\n\n#define trans_initiator2target_cb(cb) \\\n    { 0, 0, 0, 0, cb }\n\n#define transport_write(id, data, length) transport_execute_transaction(id, data, length, NULL, 0)\n#define transport_read(id, data, length) transport_execute_transaction(id, NULL, 0, data, length)\n#define transport_exec(id) transport_execute_transaction(id, NULL, 0, NULL, 0)\n\n#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\n// Forward-declare the RPC callback handlers\nvoid slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);\nvoid slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);\n#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\n\n////////////////////////////////////////////////////\n// Helpers\n\nstatic bool transaction_handler_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[], const char *prefix, bool (*handler)(matrix_row_t master_matrix[], matrix_row_t slave_matrix[])) {\n    int num_retries = is_transport_connected() ? 10 : 1;\n    for (int iter = 1; iter <= num_retries; ++iter) {\n        if (iter > 1) {\n            for (int i = 0; i < iter * iter; ++i) {\n                wait_us(10);\n            }\n        }\n        bool this_okay = true;\n        this_okay      = handler(master_matrix, slave_matrix);\n        if (this_okay) return true;\n    }\n    dprintf(\"Failed to execute %s\\n\", prefix);\n    return false;\n}\n\n#define TRANSACTION_HANDLER_MASTER(prefix)                                                                              \\\n    do {                                                                                                                \\\n        if (!transaction_handler_master(master_matrix, slave_matrix, #prefix, &prefix##_handlers_master)) return false; \\\n    } while (0)\n\n/**\n * @brief Constructs a transaction handler that doesn't acquire a lock to the\n * split shared memory. Therefore the locking and unlocking has to be done\n * manually inside the handler. Use this macro only if the handler is\n * non-deterministic in runtime and thus needs a manual lock unlock\n * implementation to hold the lock for the shortest possible time.\n */\n#define TRANSACTION_HANDLER_SLAVE(prefix)                     \\\n    do {                                                      \\\n        prefix##_handlers_slave(master_matrix, slave_matrix); \\\n    } while (0)\n\n/**\n * @brief Constructs a transaction handler that automatically acquires a lock to\n * safely access the split shared memory and releases the lock again after\n * processing the handler. Use this macro if the handler is fast and\n * deterministic in runtime and thus holds the lock only for a very short time.\n * If not fallback to manually locking and unlocking inside the handler.\n */\n#define TRANSACTION_HANDLER_SLAVE_AUTOLOCK(prefix)            \\\n    do {                                                      \\\n        split_shared_memory_lock();                           \\\n        prefix##_handlers_slave(master_matrix, slave_matrix); \\\n        split_shared_memory_unlock();                         \\\n    } while (0)\n\ninline static bool read_if_checksum_mismatch(int8_t trans_id_checksum, int8_t trans_id_retrieve, uint32_t *last_update, void *destination, const void *equiv_shmem, size_t length) {\n    uint8_t curr_checksum;\n    bool    okay = transport_read(trans_id_checksum, &curr_checksum, sizeof(curr_checksum));\n    if (okay && (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || curr_checksum != crc8(equiv_shmem, length))) {\n        okay &= transport_read(trans_id_retrieve, destination, length);\n        okay &= curr_checksum == crc8(equiv_shmem, length);\n        if (okay) {\n            *last_update = timer_read32();\n        }\n    } else {\n        memcpy(destination, equiv_shmem, length);\n    }\n    return okay;\n}\n\ninline static bool send_if_condition(int8_t trans_id, uint32_t *last_update, bool condition, void *source, size_t length) {\n    bool okay = true;\n    if (timer_elapsed32(*last_update) >= FORCED_SYNC_THROTTLE_MS || condition) {\n        okay &= transport_write(trans_id, source, length);\n        if (okay) {\n            *last_update = timer_read32();\n        }\n    }\n    return okay;\n}\n\ninline static bool send_if_data_mismatch(int8_t trans_id, uint32_t *last_update, void *source, const void *equiv_shmem, size_t length) {\n    // Just run a memcmp to compare the source and equivalent shmem location\n    return send_if_condition(trans_id, last_update, (memcmp(source, equiv_shmem, length) != 0), source, length);\n}\n\n////////////////////////////////////////////////////\n// Slave matrix\n\nstatic bool slave_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t     last_update                    = 0;\n    static matrix_row_t last_matrix[(MATRIX_ROWS) / 2] = {0}; // last successfully-read matrix, so we can replicate if there are checksum errors\n    matrix_row_t        temp_matrix[(MATRIX_ROWS) / 2];       // holding area while we test whether or not checksum is correct\n\n    bool okay = read_if_checksum_mismatch(GET_SLAVE_MATRIX_CHECKSUM, GET_SLAVE_MATRIX_DATA, &last_update, temp_matrix, split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix));\n    if (okay) {\n        // Checksum matches the received data, save as the last matrix state\n        memcpy(last_matrix, temp_matrix, sizeof(temp_matrix));\n    }\n    // Copy out the last-known-good matrix state to the slave matrix\n    memcpy(slave_matrix, last_matrix, sizeof(last_matrix));\n    return okay;\n}\n\nstatic void slave_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    memcpy(split_shmem->smatrix.matrix, slave_matrix, sizeof(split_shmem->smatrix.matrix));\n    split_shmem->smatrix.checksum = crc8(split_shmem->smatrix.matrix, sizeof(split_shmem->smatrix.matrix));\n}\n\n// clang-format off\n#define TRANSACTIONS_SLAVE_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(slave_matrix)\n#define TRANSACTIONS_SLAVE_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(slave_matrix)\n#define TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS \\\n    [GET_SLAVE_MATRIX_CHECKSUM] = trans_target2initiator_initializer(smatrix.checksum), \\\n    [GET_SLAVE_MATRIX_DATA]     = trans_target2initiator_initializer(smatrix.matrix),\n// clang-format on\n\n////////////////////////////////////////////////////\n// Master matrix\n\n#ifdef SPLIT_TRANSPORT_MIRROR\n\nstatic bool master_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t last_update = 0;\n    return send_if_data_mismatch(PUT_MASTER_MATRIX, &last_update, master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));\n}\n\nstatic void master_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    // Always copy to the master matrix\n    memcpy(master_matrix, split_shmem->mmatrix.matrix, sizeof(split_shmem->mmatrix.matrix));\n}\n\n#    define TRANSACTIONS_MASTER_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(master_matrix)\n#    define TRANSACTIONS_MASTER_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(master_matrix)\n#    define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS [PUT_MASTER_MATRIX] = trans_initiator2target_initializer(mmatrix.matrix),\n\n#else // SPLIT_TRANSPORT_MIRROR\n\n#    define TRANSACTIONS_MASTER_MATRIX_MASTER()\n#    define TRANSACTIONS_MASTER_MATRIX_SLAVE()\n#    define TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS\n\n#endif // SPLIT_TRANSPORT_MIRROR\n\n////////////////////////////////////////////////////\n// Encoders\n\n#ifdef ENCODER_ENABLE\n\nstatic bool encoder_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t  last_update   = 0;\n    static uint8_t   last_checksum = 0;\n    encoder_events_t temp_events;\n\n    bool okay = read_if_checksum_mismatch(GET_ENCODERS_CHECKSUM, GET_ENCODERS_DATA, &last_update, &temp_events, &split_shmem->encoders.events, sizeof(temp_events));\n    if (okay) {\n        if (last_checksum != split_shmem->encoders.checksum) {\n            bool    actioned = false;\n            uint8_t index;\n            bool    clockwise;\n            while (okay && encoder_dequeue_event_advanced(&split_shmem->encoders.events, &index, &clockwise)) {\n                okay &= encoder_queue_event(index, clockwise);\n                actioned = true;\n            }\n\n            if (actioned) {\n                okay &= transport_exec(CMD_ENCODER_DRAIN);\n            }\n            last_checksum = split_shmem->encoders.checksum;\n        }\n    }\n    return okay;\n}\n\nstatic void encoder_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    // Always prepare the encoder state for read.\n    encoder_retrieve_events(&split_shmem->encoders.events);\n    // Now update the checksum given that the encoders has been written to\n    split_shmem->encoders.checksum = crc8(&split_shmem->encoders.events, sizeof(split_shmem->encoders.events));\n}\n\nstatic void encoder_handlers_slave_drain(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {\n    encoder_signal_queue_drain();\n}\n\n// clang-format off\n#    define TRANSACTIONS_ENCODERS_MASTER() TRANSACTION_HANDLER_MASTER(encoder)\n#    define TRANSACTIONS_ENCODERS_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(encoder)\n#    define TRANSACTIONS_ENCODERS_REGISTRATIONS \\\n    [GET_ENCODERS_CHECKSUM] = trans_target2initiator_initializer(encoders.checksum), \\\n    [GET_ENCODERS_DATA]     = trans_target2initiator_initializer(encoders.events), \\\n    [CMD_ENCODER_DRAIN]     = trans_initiator2target_cb(encoder_handlers_slave_drain),\n// clang-format on\n\n#else // ENCODER_ENABLE\n\n#    define TRANSACTIONS_ENCODERS_MASTER()\n#    define TRANSACTIONS_ENCODERS_SLAVE()\n#    define TRANSACTIONS_ENCODERS_REGISTRATIONS\n\n#endif // ENCODER_ENABLE\n\n////////////////////////////////////////////////////\n// Sync timer\n\n#ifndef DISABLE_SYNC_TIMER\n\nstatic bool sync_timer_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t last_update = 0;\n\n    bool okay = true;\n    if (timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS) {\n        uint32_t sync_timer = sync_timer_read32() + SYNC_TIMER_OFFSET;\n        okay &= transport_write(PUT_SYNC_TIMER, &sync_timer, sizeof(sync_timer));\n        if (okay) {\n            last_update = timer_read32();\n        }\n    }\n    return okay;\n}\n\nstatic void sync_timer_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t last_sync_timer = 0;\n    if (last_sync_timer != split_shmem->sync_timer) {\n        last_sync_timer = split_shmem->sync_timer;\n        sync_timer_update(last_sync_timer);\n    }\n}\n\n#    define TRANSACTIONS_SYNC_TIMER_MASTER() TRANSACTION_HANDLER_MASTER(sync_timer)\n#    define TRANSACTIONS_SYNC_TIMER_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(sync_timer)\n#    define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS [PUT_SYNC_TIMER] = trans_initiator2target_initializer(sync_timer),\n\n#else // DISABLE_SYNC_TIMER\n\n#    define TRANSACTIONS_SYNC_TIMER_MASTER()\n#    define TRANSACTIONS_SYNC_TIMER_SLAVE()\n#    define TRANSACTIONS_SYNC_TIMER_REGISTRATIONS\n\n#endif // DISABLE_SYNC_TIMER\n\n////////////////////////////////////////////////////\n// Layer state\n\n#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)\n\nstatic bool layer_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t last_layer_state_update         = 0;\n    static uint32_t last_default_layer_state_update = 0;\n\n    bool okay = send_if_condition(PUT_LAYER_STATE, &last_layer_state_update, (layer_state != split_shmem->layers.layer_state), &layer_state, sizeof(layer_state));\n    if (okay) {\n        okay &= send_if_condition(PUT_DEFAULT_LAYER_STATE, &last_default_layer_state_update, (default_layer_state != split_shmem->layers.default_layer_state), &default_layer_state, sizeof(default_layer_state));\n    }\n    return okay;\n}\n\nstatic void layer_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    layer_state         = split_shmem->layers.layer_state;\n    default_layer_state = split_shmem->layers.default_layer_state;\n}\n\n// clang-format off\n#    define TRANSACTIONS_LAYER_STATE_MASTER() TRANSACTION_HANDLER_MASTER(layer_state)\n#    define TRANSACTIONS_LAYER_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(layer_state)\n#    define TRANSACTIONS_LAYER_STATE_REGISTRATIONS \\\n    [PUT_LAYER_STATE]         = trans_initiator2target_initializer(layers.layer_state), \\\n    [PUT_DEFAULT_LAYER_STATE] = trans_initiator2target_initializer(layers.default_layer_state),\n// clang-format on\n\n#else // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)\n\n#    define TRANSACTIONS_LAYER_STATE_MASTER()\n#    define TRANSACTIONS_LAYER_STATE_SLAVE()\n#    define TRANSACTIONS_LAYER_STATE_REGISTRATIONS\n\n#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)\n\n////////////////////////////////////////////////////\n// LED state\n\n#ifdef SPLIT_LED_STATE_ENABLE\n\nstatic bool led_state_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t last_update = 0;\n    uint8_t         led_state   = host_keyboard_leds();\n    return send_if_data_mismatch(PUT_LED_STATE, &last_update, &led_state, &split_shmem->led_state, sizeof(led_state));\n}\n\nstatic void led_state_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    void set_split_host_keyboard_leds(uint8_t led_state);\n    set_split_host_keyboard_leds(split_shmem->led_state);\n}\n\n#    define TRANSACTIONS_LED_STATE_MASTER() TRANSACTION_HANDLER_MASTER(led_state)\n#    define TRANSACTIONS_LED_STATE_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(led_state)\n#    define TRANSACTIONS_LED_STATE_REGISTRATIONS [PUT_LED_STATE] = trans_initiator2target_initializer(led_state),\n\n#else // SPLIT_LED_STATE_ENABLE\n\n#    define TRANSACTIONS_LED_STATE_MASTER()\n#    define TRANSACTIONS_LED_STATE_SLAVE()\n#    define TRANSACTIONS_LED_STATE_REGISTRATIONS\n\n#endif // SPLIT_LED_STATE_ENABLE\n\n////////////////////////////////////////////////////\n// Mods\n\n#ifdef SPLIT_MODS_ENABLE\n\nstatic bool mods_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t   last_update    = 0;\n    bool              mods_need_sync = timer_elapsed32(last_update) >= FORCED_SYNC_THROTTLE_MS;\n    split_mods_sync_t new_mods;\n    new_mods.real_mods = get_mods();\n    if (!mods_need_sync && new_mods.real_mods != split_shmem->mods.real_mods) {\n        mods_need_sync = true;\n    }\n\n    new_mods.weak_mods = get_weak_mods();\n    if (!mods_need_sync && new_mods.weak_mods != split_shmem->mods.weak_mods) {\n        mods_need_sync = true;\n    }\n\n#    ifndef NO_ACTION_ONESHOT\n    new_mods.oneshot_mods = get_oneshot_mods();\n    if (!mods_need_sync && new_mods.oneshot_mods != split_shmem->mods.oneshot_mods) {\n        mods_need_sync = true;\n    }\n    new_mods.oneshot_locked_mods = get_oneshot_locked_mods();\n    if (!mods_need_sync && new_mods.oneshot_locked_mods != split_shmem->mods.oneshot_locked_mods) {\n        mods_need_sync = true;\n    }\n#    endif // NO_ACTION_ONESHOT\n\n    bool okay = true;\n    if (mods_need_sync) {\n        okay &= transport_write(PUT_MODS, &new_mods, sizeof(new_mods));\n        if (okay) {\n            last_update = timer_read32();\n        }\n    }\n\n    return okay;\n}\n\nstatic void mods_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    split_shared_memory_lock();\n    split_mods_sync_t mods;\n    memcpy(&mods, &split_shmem->mods, sizeof(split_mods_sync_t));\n    split_shared_memory_unlock();\n\n    set_mods(mods.real_mods);\n    set_weak_mods(mods.weak_mods);\n#    ifndef NO_ACTION_ONESHOT\n    set_oneshot_mods(mods.oneshot_mods);\n    set_oneshot_locked_mods(mods.oneshot_locked_mods);\n#    endif\n}\n\n#    define TRANSACTIONS_MODS_MASTER() TRANSACTION_HANDLER_MASTER(mods)\n#    define TRANSACTIONS_MODS_SLAVE() TRANSACTION_HANDLER_SLAVE(mods)\n#    define TRANSACTIONS_MODS_REGISTRATIONS [PUT_MODS] = trans_initiator2target_initializer(mods),\n\n#else // SPLIT_MODS_ENABLE\n\n#    define TRANSACTIONS_MODS_MASTER()\n#    define TRANSACTIONS_MODS_SLAVE()\n#    define TRANSACTIONS_MODS_REGISTRATIONS\n\n#endif // SPLIT_MODS_ENABLE\n\n////////////////////////////////////////////////////\n// Backlight\n\n#ifdef BACKLIGHT_ENABLE\n\nstatic bool backlight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t last_update = 0;\n    uint8_t         level       = is_backlight_enabled() ? get_backlight_level() : 0;\n    return send_if_condition(PUT_BACKLIGHT, &last_update, (level != split_shmem->backlight_level), &level, sizeof(level));\n}\n\nstatic void backlight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    split_shared_memory_lock();\n    uint8_t backlight_level = split_shmem->backlight_level;\n    split_shared_memory_unlock();\n\n    backlight_level_noeeprom(backlight_level);\n}\n\n#    define TRANSACTIONS_BACKLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(backlight)\n#    define TRANSACTIONS_BACKLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(backlight)\n#    define TRANSACTIONS_BACKLIGHT_REGISTRATIONS [PUT_BACKLIGHT] = trans_initiator2target_initializer(backlight_level),\n\n#else // BACKLIGHT_ENABLE\n\n#    define TRANSACTIONS_BACKLIGHT_MASTER()\n#    define TRANSACTIONS_BACKLIGHT_SLAVE()\n#    define TRANSACTIONS_BACKLIGHT_REGISTRATIONS\n\n#endif // BACKLIGHT_ENABLE\n\n////////////////////////////////////////////////////\n// RGBLIGHT\n\n#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)\n\nstatic bool rgblight_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t     last_update = 0;\n    rgblight_syncinfo_t rgblight_sync;\n    rgblight_get_syncinfo(&rgblight_sync);\n    if (send_if_condition(PUT_RGBLIGHT, &last_update, (rgblight_sync.status.change_flags != 0), &rgblight_sync, sizeof(rgblight_sync))) {\n        rgblight_clear_change_flags();\n    } else {\n        return false;\n    }\n    return true;\n}\n\nstatic void rgblight_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    split_shared_memory_lock();\n    // Update the RGB with the new data\n    rgblight_syncinfo_t rgblight_sync;\n    memcpy(&rgblight_sync, &split_shmem->rgblight_sync, sizeof(rgblight_syncinfo_t));\n    split_shmem->rgblight_sync.status.change_flags = 0;\n    split_shared_memory_unlock();\n\n    if (rgblight_sync.status.change_flags != 0) {\n        rgblight_update_sync(&rgblight_sync, false);\n    }\n}\n\n#    define TRANSACTIONS_RGBLIGHT_MASTER() TRANSACTION_HANDLER_MASTER(rgblight)\n#    define TRANSACTIONS_RGBLIGHT_SLAVE() TRANSACTION_HANDLER_SLAVE(rgblight)\n#    define TRANSACTIONS_RGBLIGHT_REGISTRATIONS [PUT_RGBLIGHT] = trans_initiator2target_initializer(rgblight_sync),\n\n#else // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)\n\n#    define TRANSACTIONS_RGBLIGHT_MASTER()\n#    define TRANSACTIONS_RGBLIGHT_SLAVE()\n#    define TRANSACTIONS_RGBLIGHT_REGISTRATIONS\n\n#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)\n\n////////////////////////////////////////////////////\n// LED Matrix\n\n#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)\n\nstatic bool led_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t   last_update = 0;\n    led_matrix_sync_t led_matrix_sync;\n    memcpy(&led_matrix_sync.led_matrix, &led_matrix_eeconfig, sizeof(led_eeconfig_t));\n    led_matrix_sync.led_suspend_state = led_matrix_get_suspend_state();\n    return send_if_data_mismatch(PUT_LED_MATRIX, &last_update, &led_matrix_sync, &split_shmem->led_matrix_sync, sizeof(led_matrix_sync));\n}\n\nstatic void led_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    split_shared_memory_lock();\n    memcpy(&led_matrix_eeconfig, &split_shmem->led_matrix_sync.led_matrix, sizeof(led_eeconfig_t));\n    bool led_suspend_state = split_shmem->led_matrix_sync.led_suspend_state;\n    split_shared_memory_unlock();\n\n    led_matrix_set_suspend_state(led_suspend_state);\n}\n\n#    define TRANSACTIONS_LED_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(led_matrix)\n#    define TRANSACTIONS_LED_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(led_matrix)\n#    define TRANSACTIONS_LED_MATRIX_REGISTRATIONS [PUT_LED_MATRIX] = trans_initiator2target_initializer(led_matrix_sync),\n\n#else // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)\n\n#    define TRANSACTIONS_LED_MATRIX_MASTER()\n#    define TRANSACTIONS_LED_MATRIX_SLAVE()\n#    define TRANSACTIONS_LED_MATRIX_REGISTRATIONS\n\n#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)\n\n////////////////////////////////////////////////////\n// RGB Matrix\n\n#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)\n\nstatic bool rgb_matrix_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t   last_update = 0;\n    rgb_matrix_sync_t rgb_matrix_sync;\n    memcpy(&rgb_matrix_sync.rgb_matrix, &rgb_matrix_config, sizeof(rgb_config_t));\n    rgb_matrix_sync.rgb_suspend_state = rgb_matrix_get_suspend_state();\n    return send_if_data_mismatch(PUT_RGB_MATRIX, &last_update, &rgb_matrix_sync, &split_shmem->rgb_matrix_sync, sizeof(rgb_matrix_sync));\n}\n\nstatic void rgb_matrix_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    split_shared_memory_lock();\n    memcpy(&rgb_matrix_config, &split_shmem->rgb_matrix_sync.rgb_matrix, sizeof(rgb_config_t));\n    bool rgb_suspend_state = split_shmem->rgb_matrix_sync.rgb_suspend_state;\n    split_shared_memory_unlock();\n\n    rgb_matrix_set_suspend_state(rgb_suspend_state);\n}\n\n#    define TRANSACTIONS_RGB_MATRIX_MASTER() TRANSACTION_HANDLER_MASTER(rgb_matrix)\n#    define TRANSACTIONS_RGB_MATRIX_SLAVE() TRANSACTION_HANDLER_SLAVE(rgb_matrix)\n#    define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS [PUT_RGB_MATRIX] = trans_initiator2target_initializer(rgb_matrix_sync),\n\n#else // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)\n\n#    define TRANSACTIONS_RGB_MATRIX_MASTER()\n#    define TRANSACTIONS_RGB_MATRIX_SLAVE()\n#    define TRANSACTIONS_RGB_MATRIX_REGISTRATIONS\n\n#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)\n\n////////////////////////////////////////////////////\n// WPM\n\n#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)\n\nstatic bool wpm_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t last_update = 0;\n    uint8_t         current_wpm = get_current_wpm();\n    return send_if_condition(PUT_WPM, &last_update, (current_wpm != split_shmem->current_wpm), &current_wpm, sizeof(current_wpm));\n}\n\nstatic void wpm_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    set_current_wpm(split_shmem->current_wpm);\n}\n\n#    define TRANSACTIONS_WPM_MASTER() TRANSACTION_HANDLER_MASTER(wpm)\n#    define TRANSACTIONS_WPM_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(wpm)\n#    define TRANSACTIONS_WPM_REGISTRATIONS [PUT_WPM] = trans_initiator2target_initializer(current_wpm),\n\n#else // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)\n\n#    define TRANSACTIONS_WPM_MASTER()\n#    define TRANSACTIONS_WPM_SLAVE()\n#    define TRANSACTIONS_WPM_REGISTRATIONS\n\n#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)\n\n////////////////////////////////////////////////////\n// OLED\n\n#if defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)\n\nstatic bool oled_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t last_update        = 0;\n    bool            current_oled_state = is_oled_on();\n    return send_if_condition(PUT_OLED, &last_update, (current_oled_state != split_shmem->current_oled_state), &current_oled_state, sizeof(current_oled_state));\n}\n\nstatic void oled_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    split_shared_memory_lock();\n    uint8_t current_oled_state = split_shmem->current_oled_state;\n    split_shared_memory_unlock();\n\n    if (current_oled_state) {\n        oled_on();\n    } else {\n        oled_off();\n    }\n}\n\n#    define TRANSACTIONS_OLED_MASTER() TRANSACTION_HANDLER_MASTER(oled)\n#    define TRANSACTIONS_OLED_SLAVE() TRANSACTION_HANDLER_SLAVE(oled)\n#    define TRANSACTIONS_OLED_REGISTRATIONS [PUT_OLED] = trans_initiator2target_initializer(current_oled_state),\n\n#else // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)\n\n#    define TRANSACTIONS_OLED_MASTER()\n#    define TRANSACTIONS_OLED_SLAVE()\n#    define TRANSACTIONS_OLED_REGISTRATIONS\n\n#endif // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)\n\n////////////////////////////////////////////////////\n// ST7565\n\n#if defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)\n\nstatic bool st7565_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t last_update          = 0;\n    bool            current_st7565_state = st7565_is_on();\n    return send_if_condition(PUT_ST7565, &last_update, (current_st7565_state != split_shmem->current_st7565_state), &current_st7565_state, sizeof(current_st7565_state));\n}\n\nstatic void st7565_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    split_shared_memory_lock();\n    uint8_t current_st7565_state = split_shmem->current_st7565_state;\n    split_shared_memory_unlock();\n\n    if (current_st7565_state) {\n        st7565_on();\n    } else {\n        st7565_off();\n    }\n}\n\n#    define TRANSACTIONS_ST7565_MASTER() TRANSACTION_HANDLER_MASTER(st7565)\n#    define TRANSACTIONS_ST7565_SLAVE() TRANSACTION_HANDLER_SLAVE(st7565)\n#    define TRANSACTIONS_ST7565_REGISTRATIONS [PUT_ST7565] = trans_initiator2target_initializer(current_st7565_state),\n\n#else // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)\n\n#    define TRANSACTIONS_ST7565_MASTER()\n#    define TRANSACTIONS_ST7565_SLAVE()\n#    define TRANSACTIONS_ST7565_REGISTRATIONS\n\n#endif // defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)\n\n////////////////////////////////////////////////////\n// POINTING\n\n#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)\n\nstatic bool pointing_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n#    if defined(POINTING_DEVICE_LEFT)\n    if (is_keyboard_left()) {\n        return true;\n    }\n#    elif defined(POINTING_DEVICE_RIGHT)\n    if (!is_keyboard_left()) {\n        return true;\n    }\n#    endif\n    static uint32_t last_update     = 0;\n    static uint32_t last_cpi_update = 0;\n    static uint16_t last_cpi        = 0;\n    report_mouse_t  temp_state;\n    uint16_t        temp_cpi;\n    bool            okay = read_if_checksum_mismatch(GET_POINTING_CHECKSUM, GET_POINTING_DATA, &last_update, &temp_state, &split_shmem->pointing.report, sizeof(temp_state));\n    if (okay) pointing_device_set_shared_report(temp_state);\n    temp_cpi = pointing_device_get_shared_cpi();\n    if (temp_cpi) {\n        split_shmem->pointing.cpi = temp_cpi;\n        okay                      = send_if_condition(PUT_POINTING_CPI, &last_cpi_update, last_cpi != temp_cpi, &split_shmem->pointing.cpi, sizeof(split_shmem->pointing.cpi));\n        if (okay) {\n            last_cpi = temp_cpi;\n        }\n    }\n    return okay;\n}\n\nextern const pointing_device_driver_t *pointing_device_driver;\n\nstatic void pointing_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n#    if defined(POINTING_DEVICE_LEFT)\n    if (!is_keyboard_left()) {\n        return;\n    }\n#    elif defined(POINTING_DEVICE_RIGHT)\n    if (is_keyboard_left()) {\n        return;\n    }\n#    endif\n#    if (POINTING_DEVICE_TASK_THROTTLE_MS > 0)\n    static uint32_t last_exec = 0;\n    if (timer_elapsed32(last_exec) < POINTING_DEVICE_TASK_THROTTLE_MS) {\n        return;\n    }\n    last_exec = timer_read32();\n#    endif\n\n    uint16_t temp_cpi = !pointing_device_driver->get_cpi ? 0 : pointing_device_driver->get_cpi(); // check for NULL\n\n    split_shared_memory_lock();\n    split_slave_pointing_sync_t pointing;\n    memcpy(&pointing, &split_shmem->pointing, sizeof(split_slave_pointing_sync_t));\n    split_shared_memory_unlock();\n\n    if (pointing.cpi && pointing.cpi != temp_cpi && pointing_device_driver->set_cpi) {\n        pointing_device_driver->set_cpi(pointing.cpi);\n    }\n\n    pointing.report = pointing_device_driver->get_report((report_mouse_t){0});\n    // Now update the checksum given that the pointing has been written to\n    pointing.checksum = crc8(&pointing.report, sizeof(report_mouse_t));\n\n    split_shared_memory_lock();\n    memcpy(&split_shmem->pointing, &pointing, sizeof(split_slave_pointing_sync_t));\n    split_shared_memory_unlock();\n}\n\n#    define TRANSACTIONS_POINTING_MASTER() TRANSACTION_HANDLER_MASTER(pointing)\n#    define TRANSACTIONS_POINTING_SLAVE() TRANSACTION_HANDLER_SLAVE(pointing)\n#    define TRANSACTIONS_POINTING_REGISTRATIONS [GET_POINTING_CHECKSUM] = trans_target2initiator_initializer(pointing.checksum), [GET_POINTING_DATA] = trans_target2initiator_initializer(pointing.report), [PUT_POINTING_CPI] = trans_initiator2target_initializer(pointing.cpi),\n\n#else // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)\n\n#    define TRANSACTIONS_POINTING_MASTER()\n#    define TRANSACTIONS_POINTING_SLAVE()\n#    define TRANSACTIONS_POINTING_REGISTRATIONS\n\n#endif // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)\n\n////////////////////////////////////////////////////\n// WATCHDOG\n\n#if defined(SPLIT_WATCHDOG_ENABLE)\n\nstatic bool watchdog_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    bool okay = true;\n    if (!split_watchdog_check()) {\n        okay = transport_write(PUT_WATCHDOG, &okay, sizeof(okay));\n        split_watchdog_update(okay);\n    }\n    return okay;\n}\n\nstatic void watchdog_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    split_watchdog_update(split_shmem->watchdog_pinged);\n}\n\n#    define TRANSACTIONS_WATCHDOG_MASTER() TRANSACTION_HANDLER_MASTER(watchdog)\n#    define TRANSACTIONS_WATCHDOG_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(watchdog)\n#    define TRANSACTIONS_WATCHDOG_REGISTRATIONS [PUT_WATCHDOG] = trans_initiator2target_initializer(watchdog_pinged),\n\n#else // defined(SPLIT_WATCHDOG_ENABLE)\n\n#    define TRANSACTIONS_WATCHDOG_MASTER()\n#    define TRANSACTIONS_WATCHDOG_SLAVE()\n#    define TRANSACTIONS_WATCHDOG_REGISTRATIONS\n\n#endif // defined(SPLIT_WATCHDOG_ENABLE)\n\n#if defined(HAPTIC_ENABLE) && defined(SPLIT_HAPTIC_ENABLE)\n\nuint8_t                split_haptic_play = 0xFF;\nextern haptic_config_t haptic_config;\n\nstatic bool haptic_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t           last_update = 0;\n    split_slave_haptic_sync_t haptic_sync;\n\n    memcpy(&haptic_sync.haptic_config, &haptic_config, sizeof(haptic_config_t));\n    haptic_sync.haptic_play = split_haptic_play;\n\n    bool okay = send_if_data_mismatch(PUT_HAPTIC, &last_update, &haptic_sync, &split_shmem->haptic_sync, sizeof(haptic_sync));\n\n    split_haptic_play = 0xFF;\n\n    return okay;\n}\n\nstatic void haptic_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    memcpy(&haptic_config, &split_shmem->haptic_sync.haptic_config, sizeof(haptic_config_t));\n\n    if (split_shmem->haptic_sync.haptic_play != 0xFF) {\n        haptic_set_mode(split_shmem->haptic_sync.haptic_play);\n        haptic_play();\n    }\n}\n\n// clang-format off\n#    define TRANSACTIONS_HAPTIC_MASTER() TRANSACTION_HANDLER_MASTER(haptic)\n#    define TRANSACTIONS_HAPTIC_SLAVE() TRANSACTION_HANDLER_SLAVE(haptic)\n#    define TRANSACTIONS_HAPTIC_REGISTRATIONS [PUT_HAPTIC] = trans_initiator2target_initializer(haptic_sync),\n// clang-format on\n\n#else // defined(HAPTIC_ENABLE) && defined(SPLIT_HAPTIC_ENABLE)\n\n#    define TRANSACTIONS_HAPTIC_MASTER()\n#    define TRANSACTIONS_HAPTIC_SLAVE()\n#    define TRANSACTIONS_HAPTIC_REGISTRATIONS\n\n#endif // defined(HAPTIC_ENABLE) && defined(SPLIT_HAPTIC_ENABLE)\n\n#if defined(SPLIT_ACTIVITY_ENABLE)\n\nstatic bool activity_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t             last_update = 0;\n    split_slave_activity_sync_t activity_sync;\n    activity_sync.matrix_timestamp          = last_matrix_activity_time();\n    activity_sync.encoder_timestamp         = last_encoder_activity_time();\n    activity_sync.pointing_device_timestamp = last_pointing_device_activity_time();\n    return send_if_data_mismatch(PUT_ACTIVITY, &last_update, &activity_sync, &split_shmem->activity_sync, sizeof(activity_sync));\n}\n\nstatic void activity_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    set_activity_timestamps(split_shmem->activity_sync.matrix_timestamp, split_shmem->activity_sync.encoder_timestamp, split_shmem->activity_sync.pointing_device_timestamp);\n}\n\n// clang-format off\n#    define TRANSACTIONS_ACTIVITY_MASTER() TRANSACTION_HANDLER_MASTER(activity)\n#    define TRANSACTIONS_ACTIVITY_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(activity)\n#    define TRANSACTIONS_ACTIVITY_REGISTRATIONS [PUT_ACTIVITY] = trans_initiator2target_initializer(activity_sync),\n// clang-format on\n\n#else // defined(SPLIT_ACTIVITY_ENABLE)\n\n#    define TRANSACTIONS_ACTIVITY_MASTER()\n#    define TRANSACTIONS_ACTIVITY_SLAVE()\n#    define TRANSACTIONS_ACTIVITY_REGISTRATIONS\n\n#endif // defined(SPLIT_ACTIVITY_ENABLE)\n\n////////////////////////////////////////////////////\n// Detected OS\n\n#if defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)\n\nstatic bool detected_os_handlers_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    static uint32_t last_detected_os_update = 0;\n    os_variant_t    detected_os             = detected_host_os();\n    bool            okay                    = send_if_condition(PUT_DETECTED_OS, &last_detected_os_update, (detected_os != split_shmem->detected_os), &detected_os, sizeof(os_variant_t));\n    return okay;\n}\n\nstatic void detected_os_handlers_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    slave_update_detected_host_os(split_shmem->detected_os);\n}\n\n#    define TRANSACTIONS_DETECTED_OS_MASTER() TRANSACTION_HANDLER_MASTER(detected_os)\n#    define TRANSACTIONS_DETECTED_OS_SLAVE() TRANSACTION_HANDLER_SLAVE_AUTOLOCK(detected_os)\n#    define TRANSACTIONS_DETECTED_OS_REGISTRATIONS [PUT_DETECTED_OS] = trans_initiator2target_initializer(detected_os),\n\n#else // defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)\n\n#    define TRANSACTIONS_DETECTED_OS_MASTER()\n#    define TRANSACTIONS_DETECTED_OS_SLAVE()\n#    define TRANSACTIONS_DETECTED_OS_REGISTRATIONS\n\n#endif // defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)\n\n////////////////////////////////////////////////////\n\nsplit_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS] = {\n    // Set defaults\n    [0 ...(NUM_TOTAL_TRANSACTIONS - 1)] = {0, 0, 0, 0, 0},\n\n#ifdef USE_I2C\n    [I2C_EXECUTE_CALLBACK] = trans_initiator2target_initializer(transaction_id),\n#endif // USE_I2C\n\n    // clang-format off\n    TRANSACTIONS_SLAVE_MATRIX_REGISTRATIONS\n    TRANSACTIONS_MASTER_MATRIX_REGISTRATIONS\n    TRANSACTIONS_ENCODERS_REGISTRATIONS\n    TRANSACTIONS_SYNC_TIMER_REGISTRATIONS\n    TRANSACTIONS_LAYER_STATE_REGISTRATIONS\n    TRANSACTIONS_LED_STATE_REGISTRATIONS\n    TRANSACTIONS_MODS_REGISTRATIONS\n    TRANSACTIONS_BACKLIGHT_REGISTRATIONS\n    TRANSACTIONS_RGBLIGHT_REGISTRATIONS\n    TRANSACTIONS_LED_MATRIX_REGISTRATIONS\n    TRANSACTIONS_RGB_MATRIX_REGISTRATIONS\n    TRANSACTIONS_WPM_REGISTRATIONS\n    TRANSACTIONS_OLED_REGISTRATIONS\n    TRANSACTIONS_ST7565_REGISTRATIONS\n    TRANSACTIONS_POINTING_REGISTRATIONS\n    TRANSACTIONS_WATCHDOG_REGISTRATIONS\n    TRANSACTIONS_HAPTIC_REGISTRATIONS\n    TRANSACTIONS_ACTIVITY_REGISTRATIONS\n    TRANSACTIONS_DETECTED_OS_REGISTRATIONS\n// clang-format on\n\n#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\n        [PUT_RPC_INFO]  = trans_initiator2target_initializer_cb(rpc_info, slave_rpc_info_callback),\n    [PUT_RPC_REQ_DATA]  = trans_initiator2target_initializer(rpc_m2s_buffer),\n    [EXECUTE_RPC]       = trans_initiator2target_initializer_cb(rpc_info.payload.transaction_id, slave_rpc_exec_callback),\n    [GET_RPC_RESP_DATA] = trans_target2initiator_initializer(rpc_s2m_buffer),\n#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\n};\n\nbool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    TRANSACTIONS_SLAVE_MATRIX_MASTER();\n    TRANSACTIONS_MASTER_MATRIX_MASTER();\n    TRANSACTIONS_ENCODERS_MASTER();\n    TRANSACTIONS_SYNC_TIMER_MASTER();\n    TRANSACTIONS_LAYER_STATE_MASTER();\n    TRANSACTIONS_LED_STATE_MASTER();\n    TRANSACTIONS_MODS_MASTER();\n    TRANSACTIONS_BACKLIGHT_MASTER();\n    TRANSACTIONS_RGBLIGHT_MASTER();\n    TRANSACTIONS_LED_MATRIX_MASTER();\n    TRANSACTIONS_RGB_MATRIX_MASTER();\n    TRANSACTIONS_WPM_MASTER();\n    TRANSACTIONS_OLED_MASTER();\n    TRANSACTIONS_ST7565_MASTER();\n    TRANSACTIONS_POINTING_MASTER();\n    TRANSACTIONS_WATCHDOG_MASTER();\n    TRANSACTIONS_HAPTIC_MASTER();\n    TRANSACTIONS_ACTIVITY_MASTER();\n    TRANSACTIONS_DETECTED_OS_MASTER();\n    return true;\n}\n\nvoid transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    TRANSACTIONS_SLAVE_MATRIX_SLAVE();\n    TRANSACTIONS_MASTER_MATRIX_SLAVE();\n    TRANSACTIONS_ENCODERS_SLAVE();\n    TRANSACTIONS_SYNC_TIMER_SLAVE();\n    TRANSACTIONS_LAYER_STATE_SLAVE();\n    TRANSACTIONS_LED_STATE_SLAVE();\n    TRANSACTIONS_MODS_SLAVE();\n    TRANSACTIONS_BACKLIGHT_SLAVE();\n    TRANSACTIONS_RGBLIGHT_SLAVE();\n    TRANSACTIONS_LED_MATRIX_SLAVE();\n    TRANSACTIONS_RGB_MATRIX_SLAVE();\n    TRANSACTIONS_WPM_SLAVE();\n    TRANSACTIONS_OLED_SLAVE();\n    TRANSACTIONS_ST7565_SLAVE();\n    TRANSACTIONS_POINTING_SLAVE();\n    TRANSACTIONS_WATCHDOG_SLAVE();\n    TRANSACTIONS_HAPTIC_SLAVE();\n    TRANSACTIONS_ACTIVITY_SLAVE();\n    TRANSACTIONS_DETECTED_OS_SLAVE();\n}\n\n#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\n\nvoid transaction_register_rpc(int8_t transaction_id, slave_callback_t callback) {\n    // Prevent invoking RPC on QMK core sync data\n    if (transaction_id <= GET_RPC_RESP_DATA) return;\n\n    // Set the callback\n    split_transaction_table[transaction_id].slave_callback          = callback;\n    split_transaction_table[transaction_id].initiator2target_offset = offsetof(split_shared_memory_t, rpc_m2s_buffer);\n    split_transaction_table[transaction_id].target2initiator_offset = offsetof(split_shared_memory_t, rpc_s2m_buffer);\n}\n\nbool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {\n    // Prevent transaction attempts while transport is disconnected\n    if (!is_transport_connected()) {\n        return false;\n    }\n    // Prevent invoking RPC on QMK core sync data\n    if (transaction_id <= GET_RPC_RESP_DATA) return false;\n    // Prevent sizing issues\n    if (initiator2target_buffer_size > RPC_M2S_BUFFER_SIZE) return false;\n    if (target2initiator_buffer_size > RPC_S2M_BUFFER_SIZE) return false;\n\n    // Prepare the metadata block\n    rpc_sync_info_t info = {.payload = {.transaction_id = transaction_id, .m2s_length = initiator2target_buffer_size, .s2m_length = target2initiator_buffer_size}};\n    info.checksum        = crc8(&info.payload, sizeof(info.payload));\n\n    // Make sure the local side knows that we're not sending the full block of data\n    split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size  = initiator2target_buffer_size;\n    split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = target2initiator_buffer_size;\n\n    // Run through the sequence:\n    // * set the transaction ID and lengths\n    // * send the request data\n    // * execute RPC callback\n    // * retrieve the response data\n    if (!transport_write(PUT_RPC_INFO, &info, sizeof(info))) {\n        return false;\n    }\n    if (!transport_write(PUT_RPC_REQ_DATA, initiator2target_buffer, initiator2target_buffer_size)) {\n        return false;\n    }\n    if (!transport_write(EXECUTE_RPC, &transaction_id, sizeof(transaction_id))) {\n        return false;\n    }\n    if (!transport_read(GET_RPC_RESP_DATA, target2initiator_buffer, target2initiator_buffer_size)) {\n        return false;\n    }\n    return true;\n}\n\nvoid slave_rpc_info_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {\n    // The RPC info block contains the intended transaction ID, as well as the sizes for both inbound and outbound data.\n    // Ignore the args -- the `split_shmem` already has the info, we just need to act upon it.\n    // We must keep the `split_transaction_table` non-const, so that it is able to be modified at runtime.\n\n    split_transaction_table[PUT_RPC_REQ_DATA].initiator2target_buffer_size  = split_shmem->rpc_info.payload.m2s_length;\n    split_transaction_table[GET_RPC_RESP_DATA].target2initiator_buffer_size = split_shmem->rpc_info.payload.s2m_length;\n}\n\nvoid slave_rpc_exec_callback(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer) {\n    // We can assume that the buffer lengths are correctly set, now, given that sequentially the rpc_info callback was already executed.\n    // Go through the rpc_info and execute _that_ transaction's callback, with the scratch buffers as inputs.\n    // As a safety precaution we check that the received payload matches its checksum first.\n    if (crc8(&split_shmem->rpc_info.payload, sizeof(split_shmem->rpc_info.payload)) != split_shmem->rpc_info.checksum) {\n        return;\n    }\n\n    int8_t transaction_id = split_shmem->rpc_info.payload.transaction_id;\n    if (transaction_id < NUM_TOTAL_TRANSACTIONS) {\n        split_transaction_desc_t *trans = &split_transaction_table[transaction_id];\n        if (trans->slave_callback) {\n            trans->slave_callback(split_shmem->rpc_info.payload.m2s_length, split_shmem->rpc_m2s_buffer, split_shmem->rpc_info.payload.s2m_length, split_shmem->rpc_s2m_buffer);\n        }\n    }\n}\n\n#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\n"
  },
  {
    "path": "quantum/split_common/transactions.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"matrix.h\"\n#include \"transaction_id_define.h\"\n#include \"transport.h\"\n\ntypedef void (*slave_callback_t)(uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);\n\n// Split transaction Descriptor\ntypedef struct _split_transaction_desc_t {\n    uint8_t          initiator2target_buffer_size;\n    uint16_t         initiator2target_offset;\n    uint8_t          target2initiator_buffer_size;\n    uint16_t         target2initiator_offset;\n    slave_callback_t slave_callback;\n} split_transaction_desc_t;\n\n// Forward declaration for the split transactions\nextern split_transaction_desc_t split_transaction_table[NUM_TOTAL_TRANSACTIONS];\n\n#define split_shmem_offset_ptr(offset) (((uint8_t *)split_shmem) + (offset))\n#define split_trans_initiator2target_buffer(trans) (split_shmem_offset_ptr((trans)->initiator2target_offset))\n#define split_trans_target2initiator_buffer(trans) (split_shmem_offset_ptr((trans)->target2initiator_offset))\n\n// returns false if valid data not received from slave\nbool transactions_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);\nvoid transactions_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);\n\nvoid transaction_register_rpc(int8_t transaction_id, slave_callback_t callback);\n\nbool transaction_rpc_exec(int8_t transaction_id, uint8_t initiator2target_buffer_size, const void *initiator2target_buffer, uint8_t target2initiator_buffer_size, void *target2initiator_buffer);\n\n#define transaction_rpc_send(transaction_id, initiator2target_buffer_size, initiator2target_buffer) transaction_rpc_exec(transaction_id, initiator2target_buffer_size, initiator2target_buffer, 0, NULL)\n#define transaction_rpc_recv(transaction_id, target2initiator_buffer_size, target2initiator_buffer) transaction_rpc_exec(transaction_id, 0, NULL, target2initiator_buffer_size, target2initiator_buffer)\n"
  },
  {
    "path": "quantum/split_common/transport.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <string.h>\n#include <debug.h>\n\n#include \"compiler_support.h\"\n#include \"transactions.h\"\n#include \"transport.h\"\n#include \"transaction_id_define.h\"\n#include \"atomic_util.h\"\n\n#ifdef USE_I2C\n\n#    ifndef SLAVE_I2C_TIMEOUT\n#        define SLAVE_I2C_TIMEOUT 100\n#    endif // SLAVE_I2C_TIMEOUT\n\n#    ifndef SLAVE_I2C_ADDRESS\n#        define SLAVE_I2C_ADDRESS 0x32\n#    endif\n\n#    include \"i2c_master.h\"\n#    include \"i2c_slave.h\"\n\n// Ensure the I2C buffer has enough space\nSTATIC_ASSERT(sizeof(split_shared_memory_t) <= I2C_SLAVE_REG_COUNT, \"split_shared_memory_t too large for I2C_SLAVE_REG_COUNT\");\n\nsplit_shared_memory_t *const split_shmem = (split_shared_memory_t *)i2c_slave_reg;\n\nvoid transport_master_init(void) {\n    i2c_init();\n}\nvoid transport_slave_init(void) {\n    i2c_slave_init(SLAVE_I2C_ADDRESS);\n}\n\ni2c_status_t transport_trigger_callback(int8_t id) {\n    // If there's no callback, indicate that we were successful\n    if (!split_transaction_table[id].slave_callback) {\n        return I2C_STATUS_SUCCESS;\n    }\n\n    // Kick off the \"callback executor\", now that data has been written to the slave\n    split_shmem->transaction_id     = id;\n    split_transaction_desc_t *trans = &split_transaction_table[I2C_EXECUTE_CALLBACK];\n    return i2c_write_register(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), trans->initiator2target_buffer_size, SLAVE_I2C_TIMEOUT);\n}\n\nbool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) {\n    i2c_status_t              status;\n    split_transaction_desc_t *trans = &split_transaction_table[id];\n    if (initiator2target_length > 0) {\n        size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length;\n        memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len);\n        if ((status = i2c_write_register(SLAVE_I2C_ADDRESS, trans->initiator2target_offset, split_trans_initiator2target_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) {\n            return false;\n        }\n    }\n\n    // If we need to execute a callback on the slave, do so\n    if ((status = transport_trigger_callback(id)) < 0) {\n        return false;\n    }\n\n    if (target2initiator_length > 0) {\n        size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length;\n        if ((status = i2c_read_register(SLAVE_I2C_ADDRESS, trans->target2initiator_offset, split_trans_target2initiator_buffer(trans), len, SLAVE_I2C_TIMEOUT)) < 0) {\n            return false;\n        }\n        memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len);\n    }\n\n    return true;\n}\n\n#else // USE_I2C\n\n#    include \"serial.h\"\n\nstatic split_shared_memory_t shared_memory;\nsplit_shared_memory_t *const split_shmem = &shared_memory;\n\nvoid transport_master_init(void) {\n    soft_serial_initiator_init();\n}\nvoid transport_slave_init(void) {\n    soft_serial_target_init();\n}\n\nbool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length) {\n    split_transaction_desc_t *trans = &split_transaction_table[id];\n    if (initiator2target_length > 0) {\n        size_t len = trans->initiator2target_buffer_size < initiator2target_length ? trans->initiator2target_buffer_size : initiator2target_length;\n        memcpy(split_trans_initiator2target_buffer(trans), initiator2target_buf, len);\n    }\n\n    if (!soft_serial_transaction(id)) {\n        return false;\n    }\n\n    if (target2initiator_length > 0) {\n        size_t len = trans->target2initiator_buffer_size < target2initiator_length ? trans->target2initiator_buffer_size : target2initiator_length;\n        memcpy(target2initiator_buf, split_trans_target2initiator_buffer(trans), len);\n    }\n\n    return true;\n}\n\n#endif // USE_I2C\n\nbool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    return transactions_master(master_matrix, slave_matrix);\n}\n\nvoid transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]) {\n    transactions_slave(master_matrix, slave_matrix);\n}\n"
  },
  {
    "path": "quantum/split_common/transport.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"progmem.h\"\n#include \"action_layer.h\"\n#include \"matrix.h\"\n\n#ifndef RPC_M2S_BUFFER_SIZE\n#    define RPC_M2S_BUFFER_SIZE 32\n#endif // RPC_M2S_BUFFER_SIZE\n\n#ifndef RPC_S2M_BUFFER_SIZE\n#    define RPC_S2M_BUFFER_SIZE 32\n#endif // RPC_S2M_BUFFER_SIZE\n\nvoid transport_master_init(void);\nvoid transport_slave_init(void);\n\n// returns false if valid data not received from slave\nbool transport_master(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);\nvoid transport_slave(matrix_row_t master_matrix[], matrix_row_t slave_matrix[]);\n\nbool transport_execute_transaction(int8_t id, const void *initiator2target_buf, uint16_t initiator2target_length, void *target2initiator_buf, uint16_t target2initiator_length);\n\n#ifdef ENCODER_ENABLE\n#    include \"encoder.h\"\n#endif // ENCODER_ENABLE\n\n#ifdef BACKLIGHT_ENABLE\n#    include \"backlight.h\"\n#endif // BACKLIGHT_ENABLE\n\n#ifdef RGBLIGHT_ENABLE\n#    include \"rgblight.h\"\n#endif // RGBLIGHT_ENABLE\n\ntypedef struct _split_slave_matrix_sync_t {\n    uint8_t      checksum;\n    matrix_row_t matrix[(MATRIX_ROWS) / 2];\n} split_slave_matrix_sync_t;\n\n#ifdef SPLIT_TRANSPORT_MIRROR\ntypedef struct _split_master_matrix_sync_t {\n    matrix_row_t matrix[(MATRIX_ROWS) / 2];\n} split_master_matrix_sync_t;\n#endif // SPLIT_TRANSPORT_MIRROR\n\n#ifdef ENCODER_ENABLE\ntypedef struct _split_slave_encoder_sync_t {\n    uint8_t          checksum;\n    encoder_events_t events;\n} split_slave_encoder_sync_t;\n#endif // ENCODER_ENABLE\n\n#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)\ntypedef struct _split_layers_sync_t {\n    layer_state_t layer_state;\n    layer_state_t default_layer_state;\n} split_layers_sync_t;\n#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)\n\n#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)\n#    include \"led_matrix.h\"\n\ntypedef struct _led_matrix_sync_t {\n    led_eeconfig_t led_matrix;\n    bool           led_suspend_state;\n} led_matrix_sync_t;\n#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)\n\n#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)\n#    include \"rgb_matrix.h\"\n\ntypedef struct _rgb_matrix_sync_t {\n    rgb_config_t rgb_matrix;\n    bool         rgb_suspend_state;\n} rgb_matrix_sync_t;\n#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)\n\n#ifdef SPLIT_MODS_ENABLE\ntypedef struct _split_mods_sync_t {\n    uint8_t real_mods;\n    uint8_t weak_mods;\n#    ifndef NO_ACTION_ONESHOT\n    uint8_t oneshot_mods;\n    uint8_t oneshot_locked_mods;\n#    endif // NO_ACTION_ONESHOT\n} split_mods_sync_t;\n#endif // SPLIT_MODS_ENABLE\n\n#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)\n#    include \"pointing_device.h\"\ntypedef struct _split_slave_pointing_sync_t {\n    uint8_t        checksum;\n    report_mouse_t report;\n    uint16_t       cpi;\n} split_slave_pointing_sync_t;\n#endif // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)\n\n#if defined(HAPTIC_ENABLE) && defined(SPLIT_HAPTIC_ENABLE)\n#    include \"haptic.h\"\ntypedef struct _split_slave_haptic_sync_t {\n    haptic_config_t haptic_config;\n    uint8_t         haptic_play;\n} split_slave_haptic_sync_t;\n#endif // defined(HAPTIC_ENABLE) && defined(SPLIT_HAPTIC_ENABLE)\n\n#if defined(SPLIT_ACTIVITY_ENABLE)\n#    include \"keyboard.h\"\ntypedef struct _split_slave_activity_sync_t {\n    uint32_t matrix_timestamp;\n    uint32_t encoder_timestamp;\n    uint32_t pointing_device_timestamp;\n} split_slave_activity_sync_t;\n#endif // defined(SPLIT_ACTIVITY_ENABLE)\n\n#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\ntypedef struct _rpc_sync_info_t {\n    uint8_t checksum;\n    struct {\n        int8_t  transaction_id;\n        uint8_t m2s_length;\n        uint8_t s2m_length;\n    } payload;\n} rpc_sync_info_t;\n#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\n\n#if defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)\n#    include \"os_detection.h\"\n#endif // defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)\n\ntypedef struct _split_shared_memory_t {\n#ifdef USE_I2C\n    int8_t transaction_id;\n#endif // USE_I2C\n\n    split_slave_matrix_sync_t smatrix;\n\n#ifdef SPLIT_TRANSPORT_MIRROR\n    split_master_matrix_sync_t mmatrix;\n#endif // SPLIT_TRANSPORT_MIRROR\n\n#ifdef ENCODER_ENABLE\n    split_slave_encoder_sync_t encoders;\n#endif // ENCODER_ENABLE\n\n#ifndef DISABLE_SYNC_TIMER\n    uint32_t sync_timer;\n#endif // DISABLE_SYNC_TIMER\n\n#if !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)\n    split_layers_sync_t layers;\n#endif // !defined(NO_ACTION_LAYER) && defined(SPLIT_LAYER_STATE_ENABLE)\n\n#ifdef SPLIT_LED_STATE_ENABLE\n    uint8_t led_state;\n#endif // SPLIT_LED_STATE_ENABLE\n\n#ifdef SPLIT_MODS_ENABLE\n    split_mods_sync_t mods;\n#endif // SPLIT_MODS_ENABLE\n\n#ifdef BACKLIGHT_ENABLE\n    uint8_t backlight_level;\n#endif // BACKLIGHT_ENABLE\n\n#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)\n    rgblight_syncinfo_t rgblight_sync;\n#endif // defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)\n\n#if defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)\n    led_matrix_sync_t led_matrix_sync;\n#endif // defined(LED_MATRIX_ENABLE) && defined(LED_MATRIX_SPLIT)\n\n#if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)\n    rgb_matrix_sync_t rgb_matrix_sync;\n#endif // defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_SPLIT)\n\n#if defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)\n    uint8_t current_wpm;\n#endif // defined(WPM_ENABLE) && defined(SPLIT_WPM_ENABLE)\n\n#if defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)\n    uint8_t current_oled_state;\n#endif // defined(OLED_ENABLE) && defined(SPLIT_OLED_ENABLE)\n\n#if defined(ST7565_ENABLE) && defined(SPLIT_ST7565_ENABLE)\n    uint8_t current_st7565_state;\n#endif // ST7565_ENABLE(OLED_ENABLE) && defined(SPLIT_ST7565_ENABLE)\n\n#if defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)\n    split_slave_pointing_sync_t pointing;\n#endif // defined(POINTING_DEVICE_ENABLE) && defined(SPLIT_POINTING_ENABLE)\n\n#if defined(SPLIT_WATCHDOG_ENABLE)\n    bool watchdog_pinged;\n#endif // defined(SPLIT_WATCHDOG_ENABLE)\n\n#if defined(HAPTIC_ENABLE) && defined(SPLIT_HAPTIC_ENABLE)\n    split_slave_haptic_sync_t haptic_sync;\n#endif // defined(HAPTIC_ENABLE)\n\n#if defined(SPLIT_ACTIVITY_ENABLE)\n    split_slave_activity_sync_t activity_sync;\n#endif // defined(SPLIT_ACTIVITY_ENABLE)\n\n#if defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\n    rpc_sync_info_t rpc_info;\n    uint8_t         rpc_m2s_buffer[RPC_M2S_BUFFER_SIZE];\n    uint8_t         rpc_s2m_buffer[RPC_S2M_BUFFER_SIZE];\n#endif // defined(SPLIT_TRANSACTION_IDS_KB) || defined(SPLIT_TRANSACTION_IDS_USER)\n\n#if defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)\n    os_variant_t detected_os;\n#endif // defined(OS_DETECTION_ENABLE) && defined(SPLIT_DETECTED_OS_ENABLE)\n} split_shared_memory_t;\n\nextern split_shared_memory_t *const split_shmem;\n"
  },
  {
    "path": "quantum/steno_keycodes.h",
    "content": "/* Copyright 2017 Joseph Wasson\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"keycodes.h\"\n\n// List of keycodes for the steno keyboard. To prevent\n// errors, this must be <= 42 total entries in order to\n// support the GeminiPR protocol.\nenum steno_keycodes {\n    STN__MIN = QK_STENO,\n    STN_FN   = STN__MIN,\n    STN_NUM,\n    STN_N1 = STN_NUM,\n    STN_N2,\n    STN_N3,\n    STN_N4,\n    STN_N5,\n    STN_N6,\n    STN_SL,\n    STN_S1 = STN_SL,\n    STN_S2,\n    STN_TL,\n    STN_KL,\n    STN_PL,\n    STN_WL,\n    STN_HL,\n    STN_RL,\n    STN_A,\n    STN_O,\n    STN_STR,\n    STN_ST1 = STN_STR,\n    STN_ST2,\n    STN_RES1,\n    STN_RE1 = STN_RES1,\n    STN_RES2,\n    STN_RE2 = STN_RES2,\n    STN_PWR,\n    STN_ST3,\n    STN_ST4,\n    STN_E,\n    STN_U,\n    STN_FR,\n    STN_RR,\n    STN_PR,\n    STN_BR,\n    STN_LR,\n    STN_GR,\n    STN_TR,\n    STN_SR,\n    STN_DR,\n    STN_N7,\n    STN_N8,\n    STN_N9,\n    STN_NA,\n    STN_NB,\n    STN_NC,\n    STN_ZR,\n    STN__MAX = STN_ZR, // must be less than QK_STENO_BOLT\n};\n\n#ifdef STENO_COMBINEDMAP\nenum steno_combined_keycodes {\n    STN_S3 = QK_STENO_COMB,\n    STN_TKL,\n    STN_PWL,\n    STN_HRL,\n    STN_FRR,\n    STN_PBR,\n    STN_LGR,\n    STN_TSR,\n    STN_DZR,\n    STN_AO,\n    STN_EU,\n    STN_COMB_MAX = STN_EU,\n};\n#endif\n\n#ifdef STENO_ENABLE_BOLT\n// TxBolt Codes\n#    define TXB_NUL 0\n#    define TXB_S_L 0b00000001\n#    define TXB_T_L 0b00000010\n#    define TXB_K_L 0b00000100\n#    define TXB_P_L 0b00001000\n#    define TXB_W_L 0b00010000\n#    define TXB_H_L 0b00100000\n#    define TXB_R_L 0b01000001\n#    define TXB_A_L 0b01000010\n#    define TXB_O_L 0b01000100\n#    define TXB_STR 0b01001000\n#    define TXB_E_R 0b01010000\n#    define TXB_U_R 0b01100000\n#    define TXB_F_R 0b10000001\n#    define TXB_R_R 0b10000010\n#    define TXB_P_R 0b10000100\n#    define TXB_B_R 0b10001000\n#    define TXB_L_R 0b10010000\n#    define TXB_G_R 0b10100000\n#    define TXB_T_R 0b11000001\n#    define TXB_S_R 0b11000010\n#    define TXB_D_R 0b11000100\n#    define TXB_Z_R 0b11001000\n#    define TXB_NUM 0b11010000\n#endif // STENO_ENABLE_BOLT\n"
  },
  {
    "path": "quantum/sync_timer.c",
    "content": "/*\nCopyright (C) 2020 Ryan Caltabiano <https://github.com/XScorpion2>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nIf you happen to meet one of the copyright holders in a bar you are obligated\nto buy them one pint of beer.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n#include \"sync_timer.h\"\n#include \"keyboard.h\"\n\n#if defined(SPLIT_KEYBOARD) && !defined(DISABLE_SYNC_TIMER)\nvolatile int32_t sync_timer_ms;\n\nvoid sync_timer_init(void) {\n    sync_timer_ms = 0;\n}\n\nvoid sync_timer_update(uint32_t time) {\n    if (is_keyboard_master()) return;\n    sync_timer_ms = time - timer_read32();\n}\n\nuint16_t sync_timer_read(void) {\n    if (is_keyboard_master()) return timer_read();\n    return sync_timer_read32();\n}\n\nuint32_t sync_timer_read32(void) {\n    if (is_keyboard_master()) return timer_read32();\n    return sync_timer_ms + timer_read32();\n}\n\nuint16_t sync_timer_elapsed(uint16_t last) {\n    if (is_keyboard_master()) return timer_elapsed(last);\n    return TIMER_DIFF_16(sync_timer_read(), last);\n}\n\nuint32_t sync_timer_elapsed32(uint32_t last) {\n    if (is_keyboard_master()) return timer_elapsed32(last);\n    return TIMER_DIFF_32(sync_timer_read32(), last);\n}\n#endif\n"
  },
  {
    "path": "quantum/sync_timer.h",
    "content": "/*\nCopyright (C) 2020 Ryan Caltabiano <https://github.com/XScorpion2>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nIf you happen to meet one of the copyright holders in a bar you are obligated\nto buy them one pint of beer.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include \"timer.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#if defined(SPLIT_KEYBOARD) && !defined(DISABLE_SYNC_TIMER)\nvoid     sync_timer_init(void);\nvoid     sync_timer_update(uint32_t time);\nuint16_t sync_timer_read(void);\nuint32_t sync_timer_read32(void);\nuint16_t sync_timer_elapsed(uint16_t last);\nuint32_t sync_timer_elapsed32(uint32_t last);\n#else\n#    define sync_timer_init()\n#    define sync_timer_clear()\n#    define sync_timer_update(t)\n#    define sync_timer_read() timer_read()\n#    define sync_timer_read32() timer_read32()\n#    define sync_timer_elapsed(t) timer_elapsed(t)\n#    define sync_timer_elapsed32(t) timer_elapsed32(t)\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "quantum/tri_layer.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"tri_layer.h\"\n#include <stdint.h>\n\nstatic uint8_t tri_layer_lower_layer  = TRI_LAYER_LOWER_LAYER;\nstatic uint8_t tri_layer_upper_layer  = TRI_LAYER_UPPER_LAYER;\nstatic uint8_t tri_layer_adjust_layer = TRI_LAYER_ADJUST_LAYER;\n\nvoid set_tri_layer_lower_layer(uint8_t layer) {\n    tri_layer_lower_layer = layer;\n}\n\nvoid set_tri_layer_upper_layer(uint8_t layer) {\n    tri_layer_upper_layer = layer;\n}\n\nvoid set_tri_layer_adjust_layer(uint8_t layer) {\n    tri_layer_adjust_layer = layer;\n}\n\nvoid set_tri_layer_layers(uint8_t lower, uint8_t raise, uint8_t adjust) {\n    tri_layer_lower_layer  = lower;\n    tri_layer_upper_layer  = raise;\n    tri_layer_adjust_layer = adjust;\n}\n\nuint8_t get_tri_layer_lower_layer(void) {\n    return tri_layer_lower_layer;\n}\n\nuint8_t get_tri_layer_upper_layer(void) {\n    return tri_layer_upper_layer;\n}\n\nuint8_t get_tri_layer_adjust_layer(void) {\n    return tri_layer_adjust_layer;\n}\n"
  },
  {
    "path": "quantum/tri_layer.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include <stdint.h>\n\n#ifndef TRI_LAYER_LOWER_LAYER\n#    define TRI_LAYER_LOWER_LAYER 1\n#endif\n#ifndef TRI_LAYER_UPPER_LAYER\n#    define TRI_LAYER_UPPER_LAYER 2\n#endif\n#ifndef TRI_LAYER_ADJUST_LAYER\n#    define TRI_LAYER_ADJUST_LAYER 3\n#endif\n\n/**\n * @brief Set the tri layer lower layer index\n *\n * @param layer\n */\nvoid set_tri_layer_lower_layer(uint8_t layer);\n/**\n * @brief Set the tri layer upper layer index\n *\n * @param layer\n */\nvoid set_tri_layer_upper_layer(uint8_t layer);\n/**\n * @brief Set the tri layer adjust layer index\n *\n * @param layer\n */\nvoid set_tri_layer_adjust_layer(uint8_t layer);\n/**\n * @brief Set the tri layer indices\n *\n * @param lower\n * @param upper\n * @param adjust\n */\nvoid set_tri_layer_layers(uint8_t lower, uint8_t upper, uint8_t adjust);\n/**\n * @brief Get the tri layer lower layer index\n *\n * @return uint8_t\n */\nuint8_t get_tri_layer_lower_layer(void);\n/**\n * @brief Get the tri layer upper layer index\n *\n * @return uint8_t\n */\nuint8_t get_tri_layer_upper_layer(void);\n/**\n * @brief Get the tri layer adjust layer index\n *\n * @return uint8_t\n */\nuint8_t get_tri_layer_adjust_layer(void);\n"
  },
  {
    "path": "quantum/unicode/ucis.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"ucis.h\"\n#include \"unicode.h\"\n#include \"action.h\"\n\nuint8_t count                        = 0;\nbool    active                       = false;\nchar    input[UCIS_MAX_INPUT_LENGTH] = {0};\n\nvoid ucis_start(void) {\n    count  = 0;\n    active = true;\n\n    register_unicode(0x2328); // ⌨\n}\n\nbool ucis_active(void) {\n    return active;\n}\n\nuint8_t ucis_count(void) {\n    return count;\n}\n\nstatic char keycode_to_char(uint16_t keycode) {\n    if (keycode >= KC_A && keycode <= KC_Z) {\n        return 'a' + (keycode - KC_A);\n    } else if (keycode >= KC_1 && keycode <= KC_9) {\n        return '1' + (keycode - KC_1);\n    } else if (keycode == KC_0) {\n        return '0';\n    }\n    return 0;\n}\n\nbool ucis_add(uint16_t keycode) {\n    char c = keycode_to_char(keycode);\n    if (c) {\n        input[count++] = c;\n        return true;\n    }\n    return false;\n}\n\nbool ucis_remove_last(void) {\n    if (count) {\n        count--;\n        return true;\n    }\n\n    return false;\n}\n\nstatic bool match_mnemonic(char *mnemonic) {\n    for (uint8_t i = 0; input[i]; i++) {\n        if (i > count || input[i] != mnemonic[i]) {\n            return false;\n        }\n    }\n    return true;\n}\n\nvoid ucis_finish(void) {\n    uint8_t i     = 0;\n    bool    found = false;\n    for (; ucis_symbol_table[i].mnemonic; i++) {\n        if (match_mnemonic(ucis_symbol_table[i].mnemonic)) {\n            found = true;\n            break;\n        }\n    }\n\n    if (found) {\n        for (uint8_t j = 0; j <= count; j++) {\n            tap_code(KC_BACKSPACE);\n        }\n        register_ucis(i);\n    }\n\n    active = false;\n}\n\nvoid ucis_cancel(void) {\n    count  = 0;\n    active = false;\n}\n\nvoid register_ucis(uint8_t index) {\n    const uint32_t *code_points = ucis_symbol_table[index].code_points;\n\n    for (int i = 0; i < UCIS_MAX_CODE_POINTS && code_points[i]; i++) {\n        register_unicode(code_points[i]);\n    }\n}\n"
  },
  {
    "path": "quantum/unicode/ucis.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n/**\n * \\file\n *\n * \\defgroup ucis UCIS\n * \\{\n */\n\n#ifndef UCIS_MAX_INPUT_LENGTH\n#    define UCIS_MAX_INPUT_LENGTH 32\n#endif\n\n#ifndef UCIS_MAX_CODE_POINTS\n#    define UCIS_MAX_CODE_POINTS 3\n#endif\n\ntypedef struct {\n    char*    mnemonic;\n    uint32_t code_points[UCIS_MAX_CODE_POINTS];\n} ucis_symbol_t;\n\n// clang-format off\n\n#define UCIS_TABLE(...) { \\\n    __VA_ARGS__,          \\\n    { NULL, {} }          \\\n}\n\n#define UCIS_SYM(name, ...) {    \\\n    .mnemonic    = name,         \\\n    .code_points = {__VA_ARGS__} \\\n}\n\n// clang-format on\n\nextern const ucis_symbol_t ucis_symbol_table[];\n\n/**\n * \\brief Begin the input sequence.\n */\nvoid ucis_start(void);\n\n/**\n * \\brief Whether UCIS is currently active.\n *\n * \\return `true` if UCIS is active.\n */\nbool ucis_active(void);\n\n/**\n * \\brief Get the number of characters in the input sequence buffer.\n *\n * \\return The current input sequence buffer length.\n */\nuint8_t ucis_count(void);\n\n/**\n * \\brief Add the given keycode to the input sequence buffer.\n *\n * \\param keycode The keycode to add. Must be between `KC_A` and `KC_Z`, or `KC_1` and `KC_0`.\n *\n * \\return `true` if the keycode was added.\n */\nbool ucis_add(uint16_t keycode);\n\n/**\n * \\brief Remove the last character from the input sequence.\n *\n * \\return `true` if the sequence was not empty.\n */\nbool ucis_remove_last(void);\n\n/**\n * Mark the input sequence as complete, and attempt to match.\n */\nvoid ucis_finish(void);\n\n/**\n * \\brief Cancel the input sequence.\n */\nvoid ucis_cancel(void);\n\n/**\n * Send the code point(s) for the given UCIS index.\n *\n * \\param index The index into the UCIS symbol table.\n */\nvoid register_ucis(uint8_t index);\n\n/** \\} */\n"
  },
  {
    "path": "quantum/unicode/unicode.c",
    "content": "/* Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"unicode.h\"\n\n#include \"eeconfig.h\"\n#include \"action.h\"\n#include \"action_util.h\"\n#include \"host.h\"\n#include \"keycode.h\"\n#include \"wait.h\"\n#include \"send_string.h\"\n#include \"utf8.h\"\n#include \"debug.h\"\n#include \"quantum.h\"\n\n#if defined(AUDIO_ENABLE)\n#    include \"audio.h\"\n#endif\n\n#if defined(UNICODE_ENABLE) + defined(UNICODEMAP_ENABLE) + defined(UCIS_ENABLE) > 1\n#    error \"Cannot enable more than one Unicode method (UNICODE, UNICODEMAP, UCIS) at the same time\"\n#endif\n\n// Keycodes used for starting Unicode input on different platforms\n#ifndef UNICODE_KEY_MAC\n#    define UNICODE_KEY_MAC KC_LEFT_ALT\n#endif\n#ifndef UNICODE_KEY_LNX\n#    define UNICODE_KEY_LNX LCTL(LSFT(KC_U))\n#endif\n#ifndef UNICODE_KEY_WINC\n#    define UNICODE_KEY_WINC KC_RIGHT_ALT\n#endif\n\n// Comma-delimited, ordered list of input modes selected for use (e.g. in cycle)\n// Example: #define UNICODE_SELECTED_MODES UNICODE_MODE_WINCOMPOSE, UNICODE_MODE_LINUX\n#ifndef UNICODE_SELECTED_MODES\n#    define UNICODE_SELECTED_MODES -1\n#endif\n\n// Whether input mode changes in cycle should be written to EEPROM\n#ifndef UNICODE_CYCLE_PERSIST\n#    define UNICODE_CYCLE_PERSIST true\n#endif\n\n// Delay between starting Unicode input and sending a sequence, in ms\n#ifndef UNICODE_TYPE_DELAY\n#    define UNICODE_TYPE_DELAY 10\n#endif\n\nunicode_config_t unicode_config;\nuint8_t          unicode_saved_mods;\nled_t            unicode_saved_led_state;\n\n#if UNICODE_SELECTED_MODES != -1\nstatic uint8_t selected[]     = {UNICODE_SELECTED_MODES};\nstatic int8_t  selected_count = ARRAY_SIZE(selected);\nstatic int8_t  selected_index;\n#endif\n\n__attribute__((weak)) void unicode_input_mode_set_user(uint8_t input_mode) {}\n\n__attribute__((weak)) void unicode_input_mode_set_kb(uint8_t input_mode) {\n    unicode_input_mode_set_user(input_mode);\n}\n\n#ifdef AUDIO_ENABLE\n#    ifdef UNICODE_SONG_MAC\nstatic float song_mac[][2] = UNICODE_SONG_MAC;\n#    endif\n#    ifdef UNICODE_SONG_LNX\nstatic float song_lnx[][2] = UNICODE_SONG_LNX;\n#    endif\n#    ifdef UNICODE_SONG_WIN\nstatic float song_win[][2] = UNICODE_SONG_WIN;\n#    endif\n#    ifdef UNICODE_SONG_BSD\nstatic float song_bsd[][2] = UNICODE_SONG_BSD;\n#    endif\n#    ifdef UNICODE_SONG_WINC\nstatic float song_winc[][2] = UNICODE_SONG_WINC;\n#    endif\n#    ifdef UNICODE_SONG_EMACS\nstatic float song_emacs[][2] = UNICODE_SONG_EMACS;\n#    endif\n\nstatic void unicode_play_song(uint8_t mode) {\n    switch (mode) {\n#    ifdef UNICODE_SONG_MAC\n        case UNICODE_MODE_MACOS:\n            PLAY_SONG(song_mac);\n            break;\n#    endif\n#    ifdef UNICODE_SONG_LNX\n        case UNICODE_MODE_LINUX:\n            PLAY_SONG(song_lnx);\n            break;\n#    endif\n#    ifdef UNICODE_SONG_WIN\n        case UNICODE_MODE_WINDOWS:\n            PLAY_SONG(song_win);\n            break;\n#    endif\n#    ifdef UNICODE_SONG_BSD\n        case UNICODE_MODE_BSD:\n            PLAY_SONG(song_bsd);\n            break;\n#    endif\n#    ifdef UNICODE_SONG_WINC\n        case UNICODE_MODE_WINCOMPOSE:\n            PLAY_SONG(song_winc);\n            break;\n#    endif\n#    ifdef UNICODE_SONG_EMACS\n        case UNICODE_MODE_EMACS:\n            PLAY_SONG(song_emacs);\n            break;\n#    endif\n    }\n}\n#endif\n\nvoid unicode_input_mode_init(void) {\n    eeconfig_read_unicode_mode(&unicode_config);\n#if UNICODE_SELECTED_MODES != -1\n#    if UNICODE_CYCLE_PERSIST\n    // Find input_mode in selected modes\n    int8_t i;\n    for (i = 0; i < selected_count; i++) {\n        if (selected[i] == unicode_config.input_mode) {\n            selected_index = i;\n            break;\n        }\n    }\n    if (i == selected_count) {\n        // Not found: input_mode isn't selected, change to one that is\n        unicode_config.input_mode = selected[selected_index = 0];\n    }\n#    else\n    // Always change to the first selected input mode\n    unicode_config.input_mode = selected[selected_index = 0];\n#    endif\n#endif\n    unicode_input_mode_set_kb(unicode_config.input_mode);\n    dprintf(\"Unicode input mode init to: %u\\n\", unicode_config.input_mode);\n}\n\nuint8_t get_unicode_input_mode(void) {\n    return unicode_config.input_mode;\n}\n\nstatic void persist_unicode_input_mode(void) {\n    eeconfig_update_unicode_mode(&unicode_config);\n}\n\nvoid set_unicode_input_mode(uint8_t mode) {\n    unicode_config.input_mode = mode;\n    persist_unicode_input_mode();\n#ifdef AUDIO_ENABLE\n    unicode_play_song(mode);\n#endif\n    unicode_input_mode_set_kb(mode);\n    dprintf(\"Unicode input mode set to: %u\\n\", unicode_config.input_mode);\n}\n\nstatic void cycle_unicode_input_mode(int8_t offset) {\n#if UNICODE_SELECTED_MODES != -1\n    selected_index = (selected_index + offset) % selected_count;\n    if (selected_index < 0) {\n        selected_index += selected_count;\n    }\n\n    unicode_config.input_mode = selected[selected_index];\n\n#    if UNICODE_CYCLE_PERSIST\n    persist_unicode_input_mode();\n#    endif\n\n#    ifdef AUDIO_ENABLE\n    unicode_play_song(unicode_config.input_mode);\n#    endif\n\n    unicode_input_mode_set_kb(unicode_config.input_mode);\n    dprintf(\"Unicode input mode cycle to: %u\\n\", unicode_config.input_mode);\n#endif\n}\n\nvoid unicode_input_mode_step(void) {\n    cycle_unicode_input_mode(1);\n}\n\nvoid unicode_input_mode_step_reverse(void) {\n    cycle_unicode_input_mode(-1);\n}\n\n__attribute__((weak)) void unicode_input_start(void) {\n    unicode_saved_led_state = host_keyboard_led_state();\n\n    // Note the order matters here!\n    // Need to do this before we mess around with the mods, or else\n    // UNICODE_KEY_LNX (which is usually Ctrl-Shift-U) might not work\n    // correctly in the shifted case.\n    if (unicode_config.input_mode == UNICODE_MODE_LINUX && unicode_saved_led_state.caps_lock) {\n        tap_code(KC_CAPS_LOCK);\n    }\n\n    unicode_saved_mods = get_mods(); // Save current mods\n    clear_mods();                    // Unregister mods to start from a clean state\n    clear_weak_mods();\n\n    switch (unicode_config.input_mode) {\n        case UNICODE_MODE_MACOS:\n            register_code(UNICODE_KEY_MAC);\n            break;\n        case UNICODE_MODE_LINUX:\n            tap_code16(UNICODE_KEY_LNX);\n            break;\n        case UNICODE_MODE_WINDOWS:\n            // For increased reliability, use numpad keys for inputting digits\n            if (!unicode_saved_led_state.num_lock) {\n                tap_code(KC_NUM_LOCK);\n            }\n            register_code(KC_LEFT_ALT);\n            wait_ms(UNICODE_TYPE_DELAY);\n            tap_code(KC_KP_PLUS);\n            break;\n        case UNICODE_MODE_WINCOMPOSE:\n            tap_code(UNICODE_KEY_WINC);\n            tap_code(KC_U);\n            break;\n        case UNICODE_MODE_EMACS:\n            // The usual way to type unicode in emacs is C-x-8 <RET> then the unicode number in hex\n            tap_code16(LCTL(KC_X));\n            tap_code16(KC_8);\n            tap_code16(KC_ENTER);\n            break;\n    }\n\n    wait_ms(UNICODE_TYPE_DELAY);\n}\n\n__attribute__((weak)) void unicode_input_finish(void) {\n    switch (unicode_config.input_mode) {\n        case UNICODE_MODE_MACOS:\n            unregister_code(UNICODE_KEY_MAC);\n            break;\n        case UNICODE_MODE_LINUX:\n            tap_code(KC_SPACE);\n            if (unicode_saved_led_state.caps_lock) {\n                tap_code(KC_CAPS_LOCK);\n            }\n            break;\n        case UNICODE_MODE_WINDOWS:\n            unregister_code(KC_LEFT_ALT);\n            if (!unicode_saved_led_state.num_lock) {\n                tap_code(KC_NUM_LOCK);\n            }\n            break;\n        case UNICODE_MODE_WINCOMPOSE:\n            tap_code(KC_ENTER);\n            break;\n        case UNICODE_MODE_EMACS:\n            tap_code16(KC_ENTER);\n            break;\n    }\n\n    set_mods(unicode_saved_mods); // Reregister previously set mods\n}\n\n__attribute__((weak)) void unicode_input_cancel(void) {\n    switch (unicode_config.input_mode) {\n        case UNICODE_MODE_MACOS:\n            unregister_code(UNICODE_KEY_MAC);\n            break;\n        case UNICODE_MODE_LINUX:\n            tap_code(KC_ESCAPE);\n            if (unicode_saved_led_state.caps_lock) {\n                tap_code(KC_CAPS_LOCK);\n            }\n            break;\n        case UNICODE_MODE_WINCOMPOSE:\n            tap_code(KC_ESCAPE);\n            break;\n        case UNICODE_MODE_WINDOWS:\n            unregister_code(KC_LEFT_ALT);\n            if (!unicode_saved_led_state.num_lock) {\n                tap_code(KC_NUM_LOCK);\n            }\n            break;\n        case UNICODE_MODE_EMACS:\n            tap_code16(LCTL(KC_G)); // C-g cancels\n            break;\n    }\n\n    set_mods(unicode_saved_mods); // Reregister previously set mods\n}\n\n// clang-format off\n\nstatic void send_nibble_wrapper(uint8_t digit) {\n    if (unicode_config.input_mode == UNICODE_MODE_WINDOWS) {\n        uint8_t kc = digit < 10\n                   ? KC_KP_1 + (10 + digit - 1) % 10\n                   : KC_A + (digit - 10);\n        tap_code(kc);\n        return;\n    }\n    send_nibble(digit);\n}\n\n// clang-format on\n\nvoid register_hex(uint16_t hex) {\n    for (int i = 3; i >= 0; i--) {\n        uint8_t digit = ((hex >> (i * 4)) & 0xF);\n        send_nibble_wrapper(digit);\n    }\n}\n\nvoid register_hex32(uint32_t hex) {\n    bool first_digit        = true;\n    bool needs_leading_zero = (unicode_config.input_mode == UNICODE_MODE_WINCOMPOSE);\n    for (int i = 7; i >= 0; i--) {\n        // Work out the digit we're going to transmit\n        uint8_t digit = ((hex >> (i * 4)) & 0xF);\n\n        // If we're still searching for the first digit, and found one\n        // that needs a leading zero sent out, send the zero.\n        if (first_digit && needs_leading_zero && digit > 9) {\n            send_nibble_wrapper(0);\n        }\n\n        // Always send digits (including zero) if we're down to the last\n        // two bytes of nibbles.\n        bool must_send = i < 4;\n\n        // If we've found a digit worth transmitting, do so.\n        if (digit != 0 || !first_digit || must_send) {\n            send_nibble_wrapper(digit);\n            first_digit = false;\n        }\n    }\n}\n\nvoid register_unicode(uint32_t code_point) {\n    if (code_point > 0x10FFFF || (code_point > 0xFFFF && unicode_config.input_mode == UNICODE_MODE_WINDOWS)) {\n        // Code point out of range, do nothing\n        return;\n    }\n\n    unicode_input_start();\n    if (code_point > 0xFFFF && unicode_config.input_mode == UNICODE_MODE_MACOS) {\n        // Convert code point to UTF-16 surrogate pair on macOS\n        code_point -= 0x10000;\n        uint32_t lo = code_point & 0x3FF, hi = (code_point & 0xFFC00) >> 10;\n        register_hex32(hi + 0xD800);\n        register_hex32(lo + 0xDC00);\n    } else {\n        register_hex32(code_point);\n    }\n    unicode_input_finish();\n}\n\nvoid send_unicode_string(const char *str) {\n    if (!str) {\n        return;\n    }\n\n    while (*str) {\n        int32_t code_point = 0;\n        str                = decode_utf8(str, &code_point);\n\n        if (code_point >= 0) {\n            register_unicode(code_point);\n        }\n    }\n}\n"
  },
  {
    "path": "quantum/unicode/unicode.h",
    "content": "/* Copyright 2022\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n#include \"compiler_support.h\"\n#include \"unicode_keycodes.h\"\n\n/**\n * \\file\n *\n * \\defgroup unicode Unicode\n * \\{\n */\n\ntypedef union unicode_config_t {\n    uint8_t raw;\n    struct {\n        uint8_t input_mode : 8;\n    };\n} unicode_config_t;\n\nSTATIC_ASSERT(sizeof(unicode_config_t) == sizeof(uint8_t), \"Unicode EECONFIG out of spec.\");\n\nextern unicode_config_t unicode_config;\n\nenum unicode_input_modes {\n    UNICODE_MODE_MACOS,      // macOS using Unicode Hex Input\n    UNICODE_MODE_LINUX,      // Linux using IBus\n    UNICODE_MODE_WINDOWS,    // Windows using EnableHexNumpad\n    UNICODE_MODE_BSD,        // BSD (not implemented)\n    UNICODE_MODE_WINCOMPOSE, // Windows using WinCompose (https://github.com/samhocevar/wincompose)\n    UNICODE_MODE_EMACS,      // Emacs is an operating system in search of a good text editor\n\n    UNICODE_MODE_COUNT // Number of available input modes (always leave at the end)\n};\n\nvoid unicode_input_mode_init(void);\n\n/**\n * \\brief Get the current Unicode input mode.\n *\n * \\return The currently active Unicode input mode.\n */\nuint8_t get_unicode_input_mode(void);\n\n/**\n * \\brief Set the Unicode input mode.\n *\n * \\param mode The input mode to set.\n */\nvoid set_unicode_input_mode(uint8_t mode);\n\n/**\n * \\brief Change to the next Unicode input mode.\n */\nvoid unicode_input_mode_step(void);\n\n/**\n * \\brief Change to the previous Unicode input mode.\n */\nvoid unicode_input_mode_step_reverse(void);\n\n/**\n * \\brief User-level callback, invoked when the input mode is changed.\n *\n * \\param input_mode The new input mode.\n */\nvoid unicode_input_mode_set_user(uint8_t input_mode);\n\n/**\n * \\brief Keyboard-level callback, invoked when the input mode is changed.\n *\n * \\param input_mode The new input mode.\n */\nvoid unicode_input_mode_set_kb(uint8_t input_mode);\n\n/**\n * \\brief Begin the Unicode input sequence. The exact behavior depends on the currently selected input mode.\n */\nvoid unicode_input_start(void);\n\n/**\n * \\brief Complete the Unicode input sequence. The exact behavior depends on the currently selected input mode.\n */\nvoid unicode_input_finish(void);\n\n/**\n * \\brief Cancel the Unicode input sequence. The exact behavior depends on the currently selected input mode.\n */\nvoid unicode_input_cancel(void);\n\n/**\n * \\brief Send a 16-bit hex number.\n *\n * \\param hex The number to send.\n */\nvoid register_hex(uint16_t hex);\n\n/**\n * \\brief Send a 32-bit hex number.\n *\n * \\param hex The number to send.\n */\nvoid register_hex32(uint32_t hex);\n\n/**\n * \\brief Input a single Unicode character. A surrogate pair will be sent if required by the input mode.\n *\n * \\param code_point The code point of the character to send.\n */\nvoid register_unicode(uint32_t code_point);\n\n/**\n * \\brief Send a string containing Unicode characters.\n *\n * \\param str The string to send.\n */\nvoid send_unicode_string(const char *str);\n\n/** \\} */\n"
  },
  {
    "path": "quantum/unicode/unicode_keycodes.h",
    "content": "/* Copyright 2023 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"quantum_keycodes.h\"\n\n// clang-format off\n\n#define UC_BSPC UC(0x0008) // (backspace)\n\n#define UC_SPC  UC(0x0020) // (space)\n#define UC_EXLM UC(0x0021) // !\n#define UC_DQUT UC(0x0022) // \"\n#define UC_HASH UC(0x0023) // #\n#define UC_DLR  UC(0x0024) // $\n#define UC_PERC UC(0x0025) // %\n#define UC_AMPR UC(0x0026) // &\n#define UC_QUOT UC(0x0027) // '\n#define UC_LPRN UC(0x0028) // (\n#define UC_RPRN UC(0x0029) // )\n#define UC_ASTR UC(0x002A) // *\n#define UC_PLUS UC(0x002B) // +\n#define UC_COMM UC(0x002C) // ,\n#define UC_DASH UC(0x002D) // -\n#define UC_DOT  UC(0x002E) // .\n#define UC_SLSH UC(0x002F) // /\n\n#define UC_0    UC(0x0030) // 0\n#define UC_1    UC(0x0031) // 1\n#define UC_2    UC(0x0032) // 2\n#define UC_3    UC(0x0033) // 3\n#define UC_4    UC(0x0034) // 4\n#define UC_5    UC(0x0035) // 5\n#define UC_6    UC(0x0036) // 6\n#define UC_7    UC(0x0037) // 7\n#define UC_8    UC(0x0038) // 8\n#define UC_9    UC(0x0039) // 9\n#define UC_COLN UC(0x003A) // :\n#define UC_SCLN UC(0x003B) // ;\n#define UC_LT   UC(0x003C) // <\n#define UC_EQL  UC(0x003D) // =\n#define UC_GT   UC(0x003E) // >\n#define UC_QUES UC(0x003F) // ?\n\n#define UC_AT   UC(0x0040) // @\n#define UC_A    UC(0x0041) // A\n#define UC_B    UC(0x0042) // B\n#define UC_C    UC(0x0043) // C\n#define UC_D    UC(0x0044) // D\n#define UC_E    UC(0x0045) // E\n#define UC_F    UC(0x0046) // F\n#define UC_G    UC(0x0047) // G\n#define UC_H    UC(0x0048) // H\n#define UC_I    UC(0x0049) // I\n#define UC_J    UC(0x004A) // J\n#define UC_K    UC(0x004B) // K\n#define UC_L    UC(0x004C) // L\n#define UC_M    UC(0x004D) // M\n#define UC_N    UC(0x004E) // N\n#define UC_O    UC(0x004F) // O\n\n#define UC_P    UC(0x0050) // P\n#define UC_Q    UC(0x0051) // Q\n#define UC_R    UC(0x0052) // R\n#define UC_S    UC(0x0053) // S\n#define UC_T    UC(0x0054) // T\n#define UC_U    UC(0x0055) // U\n#define UC_V    UC(0x0056) // V\n#define UC_W    UC(0x0057) // W\n#define UC_X    UC(0x0058) // X\n#define UC_Y    UC(0x0059) // Y\n#define UC_Z    UC(0x005A) // Z\n#define UC_LBRC UC(0x005B) // [\n#define UC_BSLS UC(0x005C) // (backslash)\n#define UC_RBRC UC(0x005D) // ]\n#define UC_CIRM UC(0x005E) // ^\n#define UC_UNDR UC(0x005F) // _\n\n#define UC_GRV  UC(0x0060) // `\n#define UC_a    UC(0x0061) // a\n#define UC_b    UC(0x0062) // b\n#define UC_c    UC(0x0063) // c\n#define UC_d    UC(0x0064) // d\n#define UC_e    UC(0x0065) // e\n#define UC_f    UC(0x0066) // f\n#define UC_g    UC(0x0067) // g\n#define UC_h    UC(0x0068) // h\n#define UC_i    UC(0x0069) // i\n#define UC_j    UC(0x006A) // j\n#define UC_k    UC(0x006B) // k\n#define UC_l    UC(0x006C) // l\n#define UC_m    UC(0x006D) // m\n#define UC_n    UC(0x006E) // n\n#define UC_o    UC(0x006F) // o\n\n#define UC_p    UC(0x0070) // p\n#define UC_q    UC(0x0071) // q\n#define UC_r    UC(0x0072) // r\n#define UC_s    UC(0x0073) // s\n#define UC_t    UC(0x0074) // t\n#define UC_u    UC(0x0075) // u\n#define UC_v    UC(0x0076) // v\n#define UC_w    UC(0x0077) // w\n#define UC_x    UC(0x0078) // x\n#define UC_y    UC(0x0079) // y\n#define UC_z    UC(0x007A) // z\n#define UC_LCBR UC(0x007B) // {\n#define UC_PIPE UC(0x007C) // |\n#define UC_RCBR UC(0x007D) // }\n#define UC_TILD UC(0x007E) // ~\n#define UC_DEL  UC(0x007F) // (delete)\n"
  },
  {
    "path": "quantum/unicode/unicodemap.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"unicodemap.h\"\n#include \"unicode.h\"\n#include \"keycodes.h\"\n#include \"quantum_keycodes.h\"\n#include \"modifiers.h\"\n#include \"host.h\"\n#include \"action_util.h\"\n\nuint8_t unicodemap_index(uint16_t keycode) {\n    if (keycode >= QK_UNICODEMAP_PAIR) {\n        // Keycode is a pair: extract index based on Shift / Caps Lock state\n        uint16_t index;\n\n        uint8_t mods = get_mods() | get_weak_mods();\n#ifndef NO_ACTION_ONESHOT\n        mods |= get_oneshot_mods();\n#endif\n\n        bool shift = mods & MOD_MASK_SHIFT;\n        bool caps  = host_keyboard_led_state().caps_lock;\n        if (shift ^ caps) {\n            index = QK_UNICODEMAP_PAIR_GET_SHIFTED_INDEX(keycode);\n        } else {\n            index = QK_UNICODEMAP_PAIR_GET_UNSHIFTED_INDEX(keycode);\n        }\n\n        return index;\n    } else {\n        // Keycode is a regular index\n        return QK_UNICODEMAP_GET_INDEX(keycode);\n    }\n}\n\nuint32_t unicodemap_get_code_point(uint8_t index) {\n    return pgm_read_dword(unicode_map + index);\n}\n\nvoid register_unicodemap(uint8_t index) {\n    register_unicode(unicodemap_get_code_point(index));\n}\n"
  },
  {
    "path": "quantum/unicode/unicodemap.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include <stdint.h>\n#include \"progmem.h\"\n\n/**\n * \\file\n *\n * \\defgroup unicodemap Unicode Map\n * \\{\n */\n\nextern const uint32_t unicode_map[] PROGMEM;\n\n/**\n * \\brief Get the index into the `unicode_map` array for the given keycode, respecting shift state for pair keycodes.\n *\n * \\param keycode The Unicode Map keycode to get the index of.\n *\n * \\return An index into the `unicode_map` array.\n */\nuint8_t unicodemap_index(uint16_t keycode);\n\n/**\n * \\brief Get the code point for the given index in the `unicode_map` array.\n *\n * \\param index The index into the `unicode_map` array.\n *\n * \\return A Unicode code point value.\n */\nuint32_t unicodemap_get_code_point(uint8_t index);\n\n/**\n * \\brief Send the code point for the given index in the `unicode_map` array.\n *\n * \\param index The index into the `unicode_map` array.\n */\nvoid register_unicodemap(uint8_t index);\n\n/** \\} */\n"
  },
  {
    "path": "quantum/unicode/utf8.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"utf8.h\"\n\n// Borrowed from https://nullprogram.com/blog/2017/10/06/\nconst char *decode_utf8(const char *str, int32_t *code_point) {\n    const char *next;\n\n    if (str[0] < 0x80) { // U+0000-007F\n        *code_point = str[0];\n        next        = str + 1;\n    } else if ((str[0] & 0xE0) == 0xC0) { // U+0080-07FF\n        *code_point = ((int32_t)(str[0] & 0x1F) << 6) | ((int32_t)(str[1] & 0x3F) << 0);\n        next        = str + 2;\n    } else if ((str[0] & 0xF0) == 0xE0) { // U+0800-FFFF\n        *code_point = ((int32_t)(str[0] & 0x0F) << 12) | ((int32_t)(str[1] & 0x3F) << 6) | ((int32_t)(str[2] & 0x3F) << 0);\n        next        = str + 3;\n    } else if ((str[0] & 0xF8) == 0xF0 && (str[0] <= 0xF4)) { // U+10000-10FFFF\n        *code_point = ((int32_t)(str[0] & 0x07) << 18) | ((int32_t)(str[1] & 0x3F) << 12) | ((int32_t)(str[2] & 0x3F) << 6) | ((int32_t)(str[3] & 0x3F) << 0);\n        next        = str + 4;\n    } else {\n        *code_point = -1;\n        next        = str + 1;\n    }\n\n    // part of a UTF-16 surrogate pair - invalid\n    if (*code_point >= 0xD800 && *code_point <= 0xDFFF) {\n        *code_point = -1;\n    }\n\n    return next;\n}\n"
  },
  {
    "path": "quantum/unicode/utf8.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdint.h>\n\nconst char *decode_utf8(const char *str, int32_t *code_point);\n"
  },
  {
    "path": "quantum/util.h",
    "content": "// Copyright 2022 Stefan Kerkmann (KarlK90)\n// Copyright 2011 Jun Wako <wakojun@gmail.com>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"bits.h\"\n#include \"bitwise.h\"\n\n// convert to string\n#define STR(s) XSTR(s)\n#define XSTR(s) #s\n\n#if !defined(MIN)\n#    define MIN(x, y) (((x) < (y)) ? (x) : (y))\n#endif\n\n#if !defined(MAX)\n#    define MAX(x, y) (((x) > (y)) ? (x) : (y))\n#endif\n\n#if !defined(CEILING)\n/**\n * @brief Computes the rounded up result of a division of two integers at\n * compile time.\n */\n#    define CEILING(dividend, divisor) (((dividend) + (divisor)-1) / (divisor))\n#endif\n\n#if !defined(IS_ARRAY)\n/**\n * @brief Returns true if the value is an array, false if it's a pointer.\n *\n * This macro is ill-formed for scalars, which is OK for its intended use in\n * ARRAY_SIZE.\n */\n#    define IS_ARRAY(value) (!__builtin_types_compatible_p(typeof((value)), typeof(&(value)[0])))\n#endif\n\n#if !defined(ARRAY_SIZE)\n/**\n * @brief Computes the number of elements of the given array at compile time.\n *\n * This Macro can only be used for statically allocated arrays that have not\n * been decayed into a pointer. This is detected at compile time, though the\n * error message for scalar values is poor.\n */\n#    define ARRAY_SIZE(array) (__builtin_choose_expr(IS_ARRAY((array)), sizeof((array)) / sizeof((array)[0]), (void)0))\n#endif\n\n#if !defined(PACKED)\n#    define PACKED __attribute__((__packed__))\n#endif\n\n#if __has_include(\"_util.h\")\n#    include \"_util.h\" /* Include the platform's _util.h */\n#endif\n"
  },
  {
    "path": "quantum/variable_trace.c",
    "content": "/* Copyright 2016 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"variable_trace.h\"\n#include <stddef.h>\n#include <string.h>\n\n#ifdef NO_PRINT\n#    error \"You need undef NO_PRINT to use the variable trace feature\"\n#endif\n\n#ifndef CONSOLE_ENABLE\n#    error \"The console needs to be enabled in the makefile to use the variable trace feature\"\n#endif\n\n#define NUM_TRACED_VARIABLES 1\n#ifndef MAX_VARIABLE_TRACE_SIZE\n#    define MAX_VARIABLE_TRACE_SIZE 4\n#endif\n\ntypedef struct {\n    const char* name;\n    void*       addr;\n    unsigned    size;\n    const char* func;\n    int         line;\n    uint8_t     last_value[MAX_VARIABLE_TRACE_SIZE];\n\n} traced_variable_t;\n\nstatic traced_variable_t traced_variables[NUM_TRACED_VARIABLES];\n\nvoid add_traced_variable(const char* name, void* addr, unsigned size, const char* func, int line) {\n    verify_traced_variables(func, line);\n    if (size > MAX_VARIABLE_TRACE_SIZE) {\n#if defined(__AVR__)\n        xprintf(\"Traced variable \\\"%S\\\" exceeds the maximum size %d\\n\", name, size);\n#else\n        xprintf(\"Traced variable \\\"%s\\\" exceeds the maximum size %d\\n\", name, size);\n#endif\n        size = MAX_VARIABLE_TRACE_SIZE;\n    }\n    int index = -1;\n    for (int i = 0; i < NUM_TRACED_VARIABLES; i++) {\n        if (index == -1 && traced_variables[i].addr == NULL) {\n            index = i;\n        } else if (strcmp_P(name, traced_variables[i].name) == 0) {\n            index = i;\n            break;\n        }\n    }\n\n    if (index == -1) {\n        xprintf(\"You can only trace %d variables at the same time\\n\", NUM_TRACED_VARIABLES);\n        return;\n    }\n\n    traced_variable_t* t = &traced_variables[index];\n    t->name              = name;\n    t->addr              = addr;\n    t->size              = size;\n    t->func              = func;\n    t->line              = line;\n    memcpy(&t->last_value[0], addr, size);\n}\n\nvoid remove_traced_variable(const char* name, const char* func, int line) {\n    verify_traced_variables(func, line);\n    for (int i = 0; i < NUM_TRACED_VARIABLES; i++) {\n        if (strcmp_P(name, traced_variables[i].name) == 0) {\n            traced_variables[i].name = 0;\n            traced_variables[i].addr = NULL;\n            break;\n        }\n    }\n}\n\nvoid verify_traced_variables(const char* func, int line) {\n    for (int i = 0; i < NUM_TRACED_VARIABLES; i++) {\n        traced_variable_t* t = &traced_variables[i];\n        if (t->addr != NULL && t->name != NULL) {\n            if (memcmp(t->last_value, t->addr, t->size) != 0) {\n#if defined(__AVR__)\n                xprintf(\"Traced variable \\\"%S\\\" has been modified\\n\", t->name);\n                xprintf(\"Between %S:%d\\n\", t->func, t->line);\n                xprintf(\"And %S:%d\\n\", func, line);\n\n#else\n                xprintf(\"Traced variable \\\"%s\\\" has been modified\\n\", t->name);\n                xprintf(\"Between %s:%d\\n\", t->func, t->line);\n                xprintf(\"And %s:%d\\n\", func, line);\n#endif\n                xprintf(\"Previous value \");\n                for (int j = 0; j < t->size; j++) {\n                    print_hex8(t->last_value[j]);\n                }\n                xprintf(\"\\nNew value \");\n                uint8_t* addr = (uint8_t*)(t->addr);\n                for (int j = 0; j < t->size; j++) {\n                    print_hex8(addr[j]);\n                }\n                xprintf(\"\\n\");\n                memcpy(t->last_value, addr, t->size);\n            }\n        }\n\n        t->func = func;\n        t->line = line;\n    }\n}\n"
  },
  {
    "path": "quantum/variable_trace.h",
    "content": "/* Copyright 2016 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n// For more information about the variable tracing see the readme.\n\n#include \"print.h\"\n\n#ifdef NUM_TRACED_VARIABLES\n\n// Start tracing a variable at the memory address addr\n// The name can be anything and is used only for reporting\n// The size should usually be the same size as the variable you are interested in\n#    define ADD_TRACED_VARIABLE(name, addr, size) add_traced_variable(PSTR(name), (void*)addr, size, PSTR(__FILE__), __LINE__)\n\n// Stop tracing the variable with the given name\n#    define REMOVE_TRACED_VARIABLE(name) remove_traced_variable(PSTR(name), PSTR(__FILE__), __LINE__)\n\n// Call to get messages when the variable has been changed\n#    define VERIFY_TRACED_VARIABLES() verify_traced_variables(PSTR(__FILE__), __LINE__)\n\n#else\n\n#    define ADD_TRACED_VARIABLE(name, addr, size)\n#    define REMOVE_TRACED_VARIABLE(name)\n#    define VERIFY_TRACED_VARIABLES()\n\n#endif\n\n// Don't call directly, use the macros instead\nvoid add_traced_variable(const char* name, void* addr, unsigned size, const char* func, int line);\nvoid remove_traced_variable(const char* name, const char* func, int line);\nvoid verify_traced_variables(const char* func, int line);\n"
  },
  {
    "path": "quantum/via.c",
    "content": "/* Copyright 2019 Jason Williams (Wilba)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#ifndef RAW_ENABLE\n#    error \"RAW_ENABLE is not enabled\"\n#endif\n\n#ifndef DYNAMIC_KEYMAP_ENABLE\n#    error \"DYNAMIC_KEYMAP_ENABLE is not enabled\"\n#endif\n\n#include \"via.h\"\n\n#include \"raw_hid.h\"\n#include \"dynamic_keymap.h\"\n#include \"eeconfig.h\"\n#include \"matrix.h\"\n#include \"timer.h\"\n#include \"wait.h\"\n#include \"version.h\" // for QMK_BUILDDATE used in EEPROM magic\n#include \"nvm_via.h\"\n\n#if defined(AUDIO_ENABLE)\n#    include \"audio.h\"\n#endif\n\n#if defined(BACKLIGHT_ENABLE)\n#    include \"backlight.h\"\n#endif\n\n#if defined(RGBLIGHT_ENABLE)\n#    include \"rgblight.h\"\n#endif\n\n#if (defined(RGB_MATRIX_ENABLE) || defined(LED_MATRIX_ENABLE))\n#    include <lib/lib8tion/lib8tion.h>\n#endif\n\n#if defined(RGB_MATRIX_ENABLE)\n#    include \"rgb_matrix.h\"\n#endif\n\n#if defined(LED_MATRIX_ENABLE)\n#    include \"led_matrix.h\"\n#endif\n\n// Can be called in an overriding via_init_kb() to test if keyboard level code usage of\n// EEPROM is invalid and use/save defaults.\nbool via_eeprom_is_valid(void) {\n    char *  p      = QMK_BUILDDATE; // e.g. \"2019-11-05-11:29:54\"\n    uint8_t magic0 = ((p[2] & 0x0F) << 4) | (p[3] & 0x0F);\n    uint8_t magic1 = ((p[5] & 0x0F) << 4) | (p[6] & 0x0F);\n    uint8_t magic2 = ((p[8] & 0x0F) << 4) | (p[9] & 0x0F);\n\n    uint8_t ee_magic0;\n    uint8_t ee_magic1;\n    uint8_t ee_magic2;\n    nvm_via_read_magic(&ee_magic0, &ee_magic1, &ee_magic2);\n\n    return ee_magic0 == magic0 && ee_magic1 == magic1 && ee_magic2 == magic2;\n}\n\n// Sets VIA/keyboard level usage of EEPROM to valid/invalid\n// Keyboard level code (eg. via_init_kb()) should not call this\nvoid via_eeprom_set_valid(bool valid) {\n    if (valid) {\n        char *  p      = QMK_BUILDDATE; // e.g. \"2019-11-05-11:29:54\"\n        uint8_t magic0 = ((p[2] & 0x0F) << 4) | (p[3] & 0x0F);\n        uint8_t magic1 = ((p[5] & 0x0F) << 4) | (p[6] & 0x0F);\n        uint8_t magic2 = ((p[8] & 0x0F) << 4) | (p[9] & 0x0F);\n        nvm_via_update_magic(magic0, magic1, magic2);\n    } else {\n        nvm_via_update_magic(0xFF, 0xFF, 0xFF);\n    }\n}\n\n// Override this at the keyboard code level to check\n// VIA's EEPROM valid state and reset to defaults as needed.\n// Used by keyboards that store their own state in EEPROM,\n// for backlight, rotary encoders, etc.\n// The override should not set via_eeprom_set_valid(true) as\n// the caller also needs to check the valid state.\n__attribute__((weak)) void via_init_kb(void) {}\n\n// Called by QMK core to initialize dynamic keymaps etc.\nvoid via_init(void) {\n    // Let keyboard level test EEPROM valid state,\n    // but not set it valid, it is done here.\n    via_init_kb();\n    via_set_layout_options_kb(via_get_layout_options());\n\n    // If the EEPROM has the magic, the data is good.\n    // OK to load from EEPROM.\n    if (!via_eeprom_is_valid()) {\n        eeconfig_init_via();\n    }\n}\n\nvoid eeconfig_init_via(void) {\n    // Erase any NVM storage if necessary\n    nvm_via_erase();\n    // set the magic number to false, in case this gets interrupted\n    via_eeprom_set_valid(false);\n    // This resets the layout options\n    via_set_layout_options(VIA_EEPROM_LAYOUT_OPTIONS_DEFAULT);\n    // This resets the keymaps in EEPROM to what is in flash.\n    dynamic_keymap_reset();\n    // This resets the macros in EEPROM to nothing.\n    dynamic_keymap_macro_reset();\n    // Save the magic number last, in case saving was interrupted\n    via_eeprom_set_valid(true);\n}\n\n// This is generalized so the layout options EEPROM usage can be\n// variable, between 1 and 4 bytes.\nuint32_t via_get_layout_options(void) {\n    return nvm_via_read_layout_options();\n}\n\n__attribute__((weak)) void via_set_layout_options_kb(uint32_t value) {}\n\nvoid via_set_layout_options(uint32_t value) {\n    via_set_layout_options_kb(value);\n    nvm_via_update_layout_options(value);\n}\n\n#if VIA_EEPROM_CUSTOM_CONFIG_SIZE > 0\nuint32_t via_read_custom_config(void *buf, uint32_t offset, uint32_t length) {\n    return nvm_via_read_custom_config(buf, offset, length);\n}\nuint32_t via_update_custom_config(const void *buf, uint32_t offset, uint32_t length) {\n    return nvm_via_update_custom_config(buf, offset, length);\n}\n#endif\n\n#if defined(AUDIO_ENABLE)\nfloat via_device_indication_song[][2] = SONG(STARTUP_SOUND);\n#endif // AUDIO_ENABLE\n\n// Used by VIA to tell a device to flash LEDs (or do something else) when that\n// device becomes the active device being configured, on startup or switching\n// between devices. This function will be called six times, at 200ms interval,\n// with an incrementing value starting at zero. Since this function is called\n// an even number of times, it can call a toggle function and leave things in\n// the original state.\n__attribute__((weak)) void via_set_device_indication(uint8_t value) {\n#if defined(BACKLIGHT_ENABLE)\n    backlight_toggle();\n#endif // BACKLIGHT_ENABLE\n#if defined(RGBLIGHT_ENABLE)\n    rgblight_toggle_noeeprom();\n#endif // RGBLIGHT_ENABLE\n#if defined(RGB_MATRIX_ENABLE)\n    rgb_matrix_toggle_noeeprom();\n#endif // RGB_MATRIX_ENABLE\n#if defined(LED_MATRIX_ENABLE)\n    led_matrix_toggle_noeeprom();\n#endif // LED_MATRIX_ENABLE\n#if defined(AUDIO_ENABLE)\n    if (value == 0) {\n        wait_ms(10);\n        PLAY_SONG(via_device_indication_song);\n    }\n#endif // AUDIO_ENABLE\n}\n\n// Called by QMK core to process VIA-specific keycodes.\nbool process_record_via(uint16_t keycode, keyrecord_t *record) {\n    // Handle macros\n    if (record->event.pressed) {\n        if (keycode >= QK_MACRO && keycode <= QK_MACRO_MAX) {\n            uint8_t id = keycode - QK_MACRO;\n            dynamic_keymap_macro_send(id);\n            return false;\n        }\n    }\n\n    return true;\n}\n\n//\n// via_custom_value_command() has the default handling of custom values for Core modules.\n// If a keyboard is using the default Core modules, it does not need to be overridden,\n// the VIA keyboard definition will have matching channel/IDs.\n//\n// If a keyboard has some extra custom values, then via_custom_value_command_kb() can be\n// overridden to handle the extra custom values, leaving via_custom_value_command() to\n// handle the custom values for Core modules.\n//\n// If a keyboard has custom values and code that are overlapping with Core modules,\n// then via_custom_value_command() can be overridden and call the same functions\n// as the default implementation, or do whatever else is required.\n//\n// DO NOT call raw_hid_send() in the override function.\n//\n\n// This is the default handler for \"extra\" custom values, i.e. keyboard-specific custom values\n// that are not handled by via_custom_value_command().\n__attribute__((weak)) void via_custom_value_command_kb(uint8_t *data, uint8_t length) {\n    // data = [ command_id, channel_id, value_id, value_data ]\n    uint8_t *command_id = &(data[0]);\n    // Return the unhandled state\n    *command_id = id_unhandled;\n}\n\n// This is the default handler for custom value commands.\n// It routes commands with channel IDs to command handlers as such:\n//\n//      id_qmk_backlight_channel    ->  via_qmk_backlight_command()\n//      id_qmk_rgblight_channel     ->  via_qmk_rgblight_command()\n//      id_qmk_rgb_matrix_channel   ->  via_qmk_rgb_matrix_command()\n//      id_qmk_led_matrix_channel   ->  via_qmk_led_matrix_command()\n//      id_qmk_audio_channel        ->  via_qmk_audio_command()\n//\n__attribute__((weak)) void via_custom_value_command(uint8_t *data, uint8_t length) {\n    // data = [ command_id, channel_id, value_id, value_data ]\n    uint8_t *channel_id = &(data[1]);\n\n#if defined(BACKLIGHT_ENABLE)\n    if (*channel_id == id_qmk_backlight_channel) {\n        via_qmk_backlight_command(data, length);\n        return;\n    }\n#endif // BACKLIGHT_ENABLE\n\n#if defined(RGBLIGHT_ENABLE)\n    if (*channel_id == id_qmk_rgblight_channel) {\n        via_qmk_rgblight_command(data, length);\n        return;\n    }\n#endif // RGBLIGHT_ENABLE\n\n#if defined(RGB_MATRIX_ENABLE)\n    if (*channel_id == id_qmk_rgb_matrix_channel) {\n        via_qmk_rgb_matrix_command(data, length);\n        return;\n    }\n#endif // RGB_MATRIX_ENABLE\n\n#if defined(LED_MATRIX_ENABLE)\n    if (*channel_id == id_qmk_led_matrix_channel) {\n        via_qmk_led_matrix_command(data, length);\n        return;\n    }\n#endif // LED_MATRIX_ENABLE\n\n#if defined(AUDIO_ENABLE)\n    if (*channel_id == id_qmk_audio_channel) {\n        via_qmk_audio_command(data, length);\n        return;\n    }\n#endif // AUDIO_ENABLE\n\n    (void)channel_id; // force use of variable\n\n    // If we haven't returned before here, then let the keyboard level code\n    // handle this, if it is overridden, otherwise by default, this will\n    // return the unhandled state.\n    via_custom_value_command_kb(data, length);\n}\n\n// Keyboard level code can override this, but shouldn't need to.\n// Controlling custom features should be done by overriding\n// via_custom_value_command_kb() instead.\n__attribute__((weak)) bool via_command_kb(uint8_t *data, uint8_t length) {\n    return false;\n}\n\nvoid raw_hid_receive(uint8_t *data, uint8_t length) {\n    uint8_t *command_id   = &(data[0]);\n    uint8_t *command_data = &(data[1]);\n\n    // If via_command_kb() returns true, the command was fully\n    // handled, including calling raw_hid_send()\n    if (via_command_kb(data, length)) {\n        return;\n    }\n\n    switch (*command_id) {\n        case id_get_protocol_version: {\n            command_data[0] = VIA_PROTOCOL_VERSION >> 8;\n            command_data[1] = VIA_PROTOCOL_VERSION & 0xFF;\n            break;\n        }\n        case id_get_keyboard_value: {\n            switch (command_data[0]) {\n                case id_uptime: {\n                    uint32_t value  = timer_read32();\n                    command_data[1] = (value >> 24) & 0xFF;\n                    command_data[2] = (value >> 16) & 0xFF;\n                    command_data[3] = (value >> 8) & 0xFF;\n                    command_data[4] = value & 0xFF;\n                    break;\n                }\n                case id_layout_options: {\n                    uint32_t value  = via_get_layout_options();\n                    command_data[1] = (value >> 24) & 0xFF;\n                    command_data[2] = (value >> 16) & 0xFF;\n                    command_data[3] = (value >> 8) & 0xFF;\n                    command_data[4] = value & 0xFF;\n                    break;\n                }\n                case id_switch_matrix_state: {\n                    uint8_t offset = command_data[1];\n                    uint8_t rows   = 28 / ((MATRIX_COLS + 7) / 8);\n                    uint8_t i      = 2;\n                    for (uint8_t row = 0; row < rows && row + offset < MATRIX_ROWS; row++) {\n                        matrix_row_t value = matrix_get_row(row + offset);\n#if (MATRIX_COLS > 24)\n                        command_data[i++] = (value >> 24) & 0xFF;\n#endif\n#if (MATRIX_COLS > 16)\n                        command_data[i++] = (value >> 16) & 0xFF;\n#endif\n#if (MATRIX_COLS > 8)\n                        command_data[i++] = (value >> 8) & 0xFF;\n#endif\n                        command_data[i++] = value & 0xFF;\n                    }\n                    break;\n                }\n                case id_firmware_version: {\n                    uint32_t value  = VIA_FIRMWARE_VERSION;\n                    command_data[1] = (value >> 24) & 0xFF;\n                    command_data[2] = (value >> 16) & 0xFF;\n                    command_data[3] = (value >> 8) & 0xFF;\n                    command_data[4] = value & 0xFF;\n                    break;\n                }\n                default: {\n                    // The value ID is not known\n                    // Return the unhandled state\n                    *command_id = id_unhandled;\n                    break;\n                }\n            }\n            break;\n        }\n        case id_set_keyboard_value: {\n            switch (command_data[0]) {\n                case id_layout_options: {\n                    uint32_t value = ((uint32_t)command_data[1] << 24) | ((uint32_t)command_data[2] << 16) | ((uint32_t)command_data[3] << 8) | (uint32_t)command_data[4];\n                    via_set_layout_options(value);\n                    break;\n                }\n                case id_device_indication: {\n                    uint8_t value = command_data[1];\n                    via_set_device_indication(value);\n                    break;\n                }\n                default: {\n                    // The value ID is not known\n                    // Return the unhandled state\n                    *command_id = id_unhandled;\n                    break;\n                }\n            }\n            break;\n        }\n        case id_dynamic_keymap_get_keycode: {\n            uint16_t keycode = dynamic_keymap_get_keycode(command_data[0], command_data[1], command_data[2]);\n            command_data[3]  = keycode >> 8;\n            command_data[4]  = keycode & 0xFF;\n            break;\n        }\n        case id_dynamic_keymap_set_keycode: {\n            dynamic_keymap_set_keycode(command_data[0], command_data[1], command_data[2], (command_data[3] << 8) | command_data[4]);\n            break;\n        }\n        case id_dynamic_keymap_reset: {\n            dynamic_keymap_reset();\n            break;\n        }\n        case id_custom_set_value:\n        case id_custom_get_value:\n        case id_custom_save: {\n            via_custom_value_command(data, length);\n            break;\n        }\n#ifdef VIA_EEPROM_ALLOW_RESET\n        case id_eeprom_reset: {\n            via_eeprom_set_valid(false);\n            eeconfig_init_via();\n            break;\n        }\n#endif\n        case id_dynamic_keymap_macro_get_count: {\n            command_data[0] = dynamic_keymap_macro_get_count();\n            break;\n        }\n        case id_dynamic_keymap_macro_get_buffer_size: {\n            uint16_t size   = dynamic_keymap_macro_get_buffer_size();\n            command_data[0] = size >> 8;\n            command_data[1] = size & 0xFF;\n            break;\n        }\n        case id_dynamic_keymap_macro_get_buffer: {\n            uint16_t offset = (command_data[0] << 8) | command_data[1];\n            uint16_t size   = command_data[2]; // size <= 28\n            dynamic_keymap_macro_get_buffer(offset, size, &command_data[3]);\n            break;\n        }\n        case id_dynamic_keymap_macro_set_buffer: {\n            uint16_t offset = (command_data[0] << 8) | command_data[1];\n            uint16_t size   = command_data[2]; // size <= 28\n            dynamic_keymap_macro_set_buffer(offset, size, &command_data[3]);\n            break;\n        }\n        case id_dynamic_keymap_macro_reset: {\n            dynamic_keymap_macro_reset();\n            break;\n        }\n        case id_dynamic_keymap_get_layer_count: {\n            command_data[0] = dynamic_keymap_get_layer_count();\n            break;\n        }\n        case id_dynamic_keymap_get_buffer: {\n            uint16_t offset = (command_data[0] << 8) | command_data[1];\n            uint16_t size   = command_data[2]; // size <= 28\n            dynamic_keymap_get_buffer(offset, size, &command_data[3]);\n            break;\n        }\n        case id_dynamic_keymap_set_buffer: {\n            uint16_t offset = (command_data[0] << 8) | command_data[1];\n            uint16_t size   = command_data[2]; // size <= 28\n            dynamic_keymap_set_buffer(offset, size, &command_data[3]);\n            break;\n        }\n#ifdef ENCODER_MAP_ENABLE\n        case id_dynamic_keymap_get_encoder: {\n            uint16_t keycode = dynamic_keymap_get_encoder(command_data[0], command_data[1], command_data[2] != 0);\n            command_data[3]  = keycode >> 8;\n            command_data[4]  = keycode & 0xFF;\n            break;\n        }\n        case id_dynamic_keymap_set_encoder: {\n            dynamic_keymap_set_encoder(command_data[0], command_data[1], command_data[2] != 0, (command_data[3] << 8) | command_data[4]);\n            break;\n        }\n#endif\n        default: {\n            // The command ID is not known\n            // Return the unhandled state\n            *command_id = id_unhandled;\n            break;\n        }\n    }\n\n    // Return the same buffer, optionally with values changed\n    // (i.e. returning state to the host, or the unhandled state).\n    raw_hid_send(data, length);\n}\n\n#if defined(BACKLIGHT_ENABLE)\n\nvoid via_qmk_backlight_command(uint8_t *data, uint8_t length) {\n    // data = [ command_id, channel_id, value_id, value_data ]\n    uint8_t *command_id        = &(data[0]);\n    uint8_t *value_id_and_data = &(data[2]);\n\n    switch (*command_id) {\n        case id_custom_set_value: {\n            via_qmk_backlight_set_value(value_id_and_data);\n            break;\n        }\n        case id_custom_get_value: {\n            via_qmk_backlight_get_value(value_id_and_data);\n            break;\n        }\n        case id_custom_save: {\n            via_qmk_backlight_save();\n            break;\n        }\n        default: {\n            *command_id = id_unhandled;\n            break;\n        }\n    }\n}\n\n#    if BACKLIGHT_LEVELS == 0\n#        error BACKLIGHT_LEVELS == 0\n#    endif\n\nvoid via_qmk_backlight_get_value(uint8_t *data) {\n    // data = [ value_id, value_data ]\n    uint8_t *value_id   = &(data[0]);\n    uint8_t *value_data = &(data[1]);\n    switch (*value_id) {\n        case id_qmk_backlight_brightness: {\n            // level / BACKLIGHT_LEVELS * 255\n            value_data[0] = ((uint16_t)get_backlight_level() * UINT8_MAX) / BACKLIGHT_LEVELS;\n            break;\n        }\n        case id_qmk_backlight_effect: {\n#    ifdef BACKLIGHT_BREATHING\n            value_data[0] = is_backlight_breathing() ? 1 : 0;\n#    else\n            value_data[0] = 0;\n#    endif\n            break;\n        }\n    }\n}\n\nvoid via_qmk_backlight_set_value(uint8_t *data) {\n    // data = [ value_id, value_data ]\n    uint8_t *value_id   = &(data[0]);\n    uint8_t *value_data = &(data[1]);\n    switch (*value_id) {\n        case id_qmk_backlight_brightness: {\n            // level / 255 * BACKLIGHT_LEVELS\n            backlight_level_noeeprom(((uint16_t)value_data[0] * BACKLIGHT_LEVELS) / UINT8_MAX);\n            break;\n        }\n        case id_qmk_backlight_effect: {\n#    ifdef BACKLIGHT_BREATHING\n            if (value_data[0] == 0) {\n                backlight_disable_breathing();\n            } else {\n                backlight_enable_breathing();\n            }\n#    endif\n            break;\n        }\n    }\n}\n\nvoid via_qmk_backlight_save(void) {\n    eeconfig_update_backlight_current();\n}\n\n#endif // BACKLIGHT_ENABLE\n\n#if defined(RGBLIGHT_ENABLE)\n#    ifndef RGBLIGHT_LIMIT_VAL\n#        define RGBLIGHT_LIMIT_VAL 255\n#    endif\n\nvoid via_qmk_rgblight_command(uint8_t *data, uint8_t length) {\n    // data = [ command_id, channel_id, value_id, value_data ]\n    uint8_t *command_id        = &(data[0]);\n    uint8_t *value_id_and_data = &(data[2]);\n\n    switch (*command_id) {\n        case id_custom_set_value: {\n            via_qmk_rgblight_set_value(value_id_and_data);\n            break;\n        }\n        case id_custom_get_value: {\n            via_qmk_rgblight_get_value(value_id_and_data);\n            break;\n        }\n        case id_custom_save: {\n            via_qmk_rgblight_save();\n            break;\n        }\n        default: {\n            *command_id = id_unhandled;\n            break;\n        }\n    }\n}\n\nvoid via_qmk_rgblight_get_value(uint8_t *data) {\n    // data = [ value_id, value_data ]\n    uint8_t *value_id   = &(data[0]);\n    uint8_t *value_data = &(data[1]);\n    switch (*value_id) {\n        case id_qmk_rgblight_brightness: {\n            value_data[0] = ((uint16_t)rgblight_get_val() * UINT8_MAX) / RGBLIGHT_LIMIT_VAL;\n            break;\n        }\n        case id_qmk_rgblight_effect: {\n            value_data[0] = rgblight_is_enabled() ? rgblight_get_mode() : 0;\n            break;\n        }\n        case id_qmk_rgblight_effect_speed: {\n            value_data[0] = rgblight_get_speed();\n            break;\n        }\n        case id_qmk_rgblight_color: {\n            value_data[0] = rgblight_get_hue();\n            value_data[1] = rgblight_get_sat();\n            break;\n        }\n    }\n}\n\nvoid via_qmk_rgblight_set_value(uint8_t *data) {\n    // data = [ value_id, value_data ]\n    uint8_t *value_id   = &(data[0]);\n    uint8_t *value_data = &(data[1]);\n    switch (*value_id) {\n        case id_qmk_rgblight_brightness: {\n            rgblight_sethsv_noeeprom(rgblight_get_hue(), rgblight_get_sat(), ((uint16_t)value_data[0] * RGBLIGHT_LIMIT_VAL) / UINT8_MAX);\n            break;\n        }\n        case id_qmk_rgblight_effect: {\n            if (value_data[0] == 0) {\n                rgblight_disable_noeeprom();\n            } else {\n                rgblight_enable_noeeprom();\n                rgblight_mode_noeeprom(value_data[0]);\n            }\n            break;\n        }\n        case id_qmk_rgblight_effect_speed: {\n            rgblight_set_speed_noeeprom(value_data[0]);\n            break;\n        }\n        case id_qmk_rgblight_color: {\n            rgblight_sethsv_noeeprom(value_data[0], value_data[1], rgblight_get_val());\n            break;\n        }\n    }\n}\n\nvoid via_qmk_rgblight_save(void) {\n    eeconfig_update_rgblight_current();\n}\n\n#endif // QMK_RGBLIGHT_ENABLE\n\n#if defined(RGB_MATRIX_ENABLE)\n\nvoid via_qmk_rgb_matrix_command(uint8_t *data, uint8_t length) {\n    // data = [ command_id, channel_id, value_id, value_data ]\n    uint8_t *command_id        = &(data[0]);\n    uint8_t *value_id_and_data = &(data[2]);\n\n    switch (*command_id) {\n        case id_custom_set_value: {\n            via_qmk_rgb_matrix_set_value(value_id_and_data);\n            break;\n        }\n        case id_custom_get_value: {\n            via_qmk_rgb_matrix_get_value(value_id_and_data);\n            break;\n        }\n        case id_custom_save: {\n            via_qmk_rgb_matrix_save();\n            break;\n        }\n        default: {\n            *command_id = id_unhandled;\n            break;\n        }\n    }\n}\n\nvoid via_qmk_rgb_matrix_get_value(uint8_t *data) {\n    // data = [ value_id, value_data ]\n    uint8_t *value_id   = &(data[0]);\n    uint8_t *value_data = &(data[1]);\n\n    switch (*value_id) {\n        case id_qmk_rgb_matrix_brightness: {\n            value_data[0] = ((uint16_t)rgb_matrix_get_val() * UINT8_MAX) / RGB_MATRIX_MAXIMUM_BRIGHTNESS;\n            break;\n        }\n        case id_qmk_rgb_matrix_effect: {\n            value_data[0] = rgb_matrix_is_enabled() ? rgb_matrix_get_mode() : 0;\n            break;\n        }\n        case id_qmk_rgb_matrix_effect_speed: {\n            value_data[0] = rgb_matrix_get_speed();\n            break;\n        }\n        case id_qmk_rgb_matrix_color: {\n            value_data[0] = rgb_matrix_get_hue();\n            value_data[1] = rgb_matrix_get_sat();\n            break;\n        }\n    }\n}\n\nvoid via_qmk_rgb_matrix_set_value(uint8_t *data) {\n    // data = [ value_id, value_data ]\n    uint8_t *value_id   = &(data[0]);\n    uint8_t *value_data = &(data[1]);\n    switch (*value_id) {\n        case id_qmk_rgb_matrix_brightness: {\n            rgb_matrix_sethsv_noeeprom(rgb_matrix_get_hue(), rgb_matrix_get_sat(), scale8(value_data[0], RGB_MATRIX_MAXIMUM_BRIGHTNESS));\n            break;\n        }\n        case id_qmk_rgb_matrix_effect: {\n            if (value_data[0] == 0) {\n                rgb_matrix_disable_noeeprom();\n            } else {\n                rgb_matrix_enable_noeeprom();\n                rgb_matrix_mode_noeeprom(value_data[0]);\n            }\n            break;\n        }\n        case id_qmk_rgb_matrix_effect_speed: {\n            rgb_matrix_set_speed_noeeprom(value_data[0]);\n            break;\n        }\n        case id_qmk_rgb_matrix_color: {\n            rgb_matrix_sethsv_noeeprom(value_data[0], value_data[1], rgb_matrix_get_val());\n            break;\n        }\n    }\n}\n\nvoid via_qmk_rgb_matrix_save(void) {\n    eeconfig_force_flush_rgb_matrix();\n}\n\n#endif // RGB_MATRIX_ENABLE\n\n#if defined(LED_MATRIX_ENABLE)\n\nvoid via_qmk_led_matrix_command(uint8_t *data, uint8_t length) {\n    // data = [ command_id, channel_id, value_id, value_data ]\n    uint8_t *command_id        = &(data[0]);\n    uint8_t *value_id_and_data = &(data[2]);\n\n    switch (*command_id) {\n        case id_custom_set_value: {\n            via_qmk_led_matrix_set_value(value_id_and_data);\n            break;\n        }\n        case id_custom_get_value: {\n            via_qmk_led_matrix_get_value(value_id_and_data);\n            break;\n        }\n        case id_custom_save: {\n            via_qmk_led_matrix_save();\n            break;\n        }\n        default: {\n            *command_id = id_unhandled;\n            break;\n        }\n    }\n}\n\nvoid via_qmk_led_matrix_get_value(uint8_t *data) {\n    // data = [ value_id, value_data ]\n    uint8_t *value_id   = &(data[0]);\n    uint8_t *value_data = &(data[1]);\n\n    switch (*value_id) {\n        case id_qmk_led_matrix_brightness: {\n            value_data[0] = ((uint16_t)led_matrix_get_val() * UINT8_MAX) / LED_MATRIX_MAXIMUM_BRIGHTNESS;\n            break;\n        }\n        case id_qmk_led_matrix_effect: {\n            value_data[0] = led_matrix_is_enabled() ? led_matrix_get_mode() : 0;\n            break;\n        }\n        case id_qmk_led_matrix_effect_speed: {\n            value_data[0] = led_matrix_get_speed();\n            break;\n        }\n    }\n}\n\nvoid via_qmk_led_matrix_set_value(uint8_t *data) {\n    // data = [ value_id, value_data ]\n    uint8_t *value_id   = &(data[0]);\n    uint8_t *value_data = &(data[1]);\n    switch (*value_id) {\n        case id_qmk_led_matrix_brightness: {\n            led_matrix_set_val_noeeprom(scale8(value_data[0], LED_MATRIX_MAXIMUM_BRIGHTNESS));\n            break;\n        }\n        case id_qmk_led_matrix_effect: {\n            if (value_data[0] == 0) {\n                led_matrix_disable_noeeprom();\n            } else {\n                led_matrix_enable_noeeprom();\n                led_matrix_mode_noeeprom(value_data[0]);\n            }\n            break;\n        }\n        case id_qmk_led_matrix_effect_speed: {\n            led_matrix_set_speed_noeeprom(value_data[0]);\n            break;\n        }\n    }\n}\n\nvoid via_qmk_led_matrix_save(void) {\n    eeconfig_force_flush_led_matrix();\n}\n\n#endif // LED_MATRIX_ENABLE\n\n#if defined(AUDIO_ENABLE)\n\nextern audio_config_t audio_config;\n\nvoid via_qmk_audio_command(uint8_t *data, uint8_t length) {\n    // data = [ command_id, channel_id, value_id, value_data ]\n    uint8_t *command_id        = &(data[0]);\n    uint8_t *value_id_and_data = &(data[2]);\n\n    switch (*command_id) {\n        case id_custom_set_value: {\n            via_qmk_audio_set_value(value_id_and_data);\n            break;\n        }\n        case id_custom_get_value: {\n            via_qmk_audio_get_value(value_id_and_data);\n            break;\n        }\n        case id_custom_save: {\n            via_qmk_audio_save();\n            break;\n        }\n        default: {\n            *command_id = id_unhandled;\n            break;\n        }\n    }\n}\n\nvoid via_qmk_audio_get_value(uint8_t *data) {\n    // data = [ value_id, value_data ]\n    uint8_t *value_id   = &(data[0]);\n    uint8_t *value_data = &(data[1]);\n    switch (*value_id) {\n        case id_qmk_audio_enable: {\n            value_data[0] = audio_config.enable ? 1 : 0;\n            break;\n        }\n        case id_qmk_audio_clicky_enable: {\n            value_data[0] = audio_config.clicky_enable ? 1 : 0;\n            break;\n        }\n    }\n}\n\nvoid via_qmk_audio_set_value(uint8_t *data) {\n    // data = [ value_id, value_data ]\n    uint8_t *value_id   = &(data[0]);\n    uint8_t *value_data = &(data[1]);\n    switch (*value_id) {\n        case id_qmk_audio_enable: {\n            audio_config.enable = value_data[0] ? 1 : 0;\n            break;\n        }\n        case id_qmk_audio_clicky_enable: {\n            audio_config.clicky_enable = value_data[0] ? 1 : 0;\n            break;\n        }\n    }\n}\n\nvoid via_qmk_audio_save(void) {\n    eeconfig_update_audio(&audio_config);\n}\n\n#endif // QMK_AUDIO_ENABLE\n"
  },
  {
    "path": "quantum/via.h",
    "content": "/* Copyright 2019 Jason Williams (Wilba)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"action.h\"\n\n// Changing the layout options size after release will invalidate EEPROM,\n// but this is something that should be set correctly on initial implementation.\n// 1 byte is enough for most uses (i.e. 8 binary states, or 6 binary + 1 ternary/quaternary )\n#ifndef VIA_EEPROM_LAYOUT_OPTIONS_SIZE\n#    define VIA_EEPROM_LAYOUT_OPTIONS_SIZE 1\n#endif\n\n// Allow override of the layout options default value.\n// This requires advanced knowledge of how VIA stores layout options\n// and is only really useful for setting a boolean layout option\n// state to true by default.\n#ifndef VIA_EEPROM_LAYOUT_OPTIONS_DEFAULT\n#    define VIA_EEPROM_LAYOUT_OPTIONS_DEFAULT 0x00000000\n#endif\n\n#ifndef VIA_EEPROM_CUSTOM_CONFIG_SIZE\n#    define VIA_EEPROM_CUSTOM_CONFIG_SIZE 0\n#endif\n\n// This is changed only when the command IDs change,\n// so VIA Configurator can detect compatible firmware.\n#define VIA_PROTOCOL_VERSION 0x000C\n\n// This is a version number for the firmware for the keyboard.\n// It can be used to ensure the VIA keyboard definition and the firmware\n// have the same version, especially if there are changes to custom values.\n// Define this in config.h to override and bump this number.\n// This is *not* required if the keyboard is only using basic functionality\n// and not using custom values for lighting, rotary encoders, etc.\n#ifndef VIA_FIRMWARE_VERSION\n#    define VIA_FIRMWARE_VERSION 0x00000000\n#endif\n\nenum via_command_id {\n    id_get_protocol_version                 = 0x01, // always 0x01\n    id_get_keyboard_value                   = 0x02,\n    id_set_keyboard_value                   = 0x03,\n    id_dynamic_keymap_get_keycode           = 0x04,\n    id_dynamic_keymap_set_keycode           = 0x05,\n    id_dynamic_keymap_reset                 = 0x06,\n    id_custom_set_value                     = 0x07,\n    id_custom_get_value                     = 0x08,\n    id_custom_save                          = 0x09,\n    id_eeprom_reset                         = 0x0A,\n    id_bootloader_jump                      = 0x0B,\n    id_dynamic_keymap_macro_get_count       = 0x0C,\n    id_dynamic_keymap_macro_get_buffer_size = 0x0D,\n    id_dynamic_keymap_macro_get_buffer      = 0x0E,\n    id_dynamic_keymap_macro_set_buffer      = 0x0F,\n    id_dynamic_keymap_macro_reset           = 0x10,\n    id_dynamic_keymap_get_layer_count       = 0x11,\n    id_dynamic_keymap_get_buffer            = 0x12,\n    id_dynamic_keymap_set_buffer            = 0x13,\n    id_dynamic_keymap_get_encoder           = 0x14,\n    id_dynamic_keymap_set_encoder           = 0x15,\n    id_unhandled                            = 0xFF,\n};\n\nenum via_keyboard_value_id {\n    id_uptime              = 0x01,\n    id_layout_options      = 0x02,\n    id_switch_matrix_state = 0x03,\n    id_firmware_version    = 0x04,\n    id_device_indication   = 0x05,\n};\n\nenum via_channel_id {\n    id_custom_channel         = 0,\n    id_qmk_backlight_channel  = 1,\n    id_qmk_rgblight_channel   = 2,\n    id_qmk_rgb_matrix_channel = 3,\n    id_qmk_audio_channel      = 4,\n    id_qmk_led_matrix_channel = 5,\n};\n\nenum via_qmk_backlight_value {\n    id_qmk_backlight_brightness = 1,\n    id_qmk_backlight_effect     = 2,\n};\n\nenum via_qmk_rgblight_value {\n    id_qmk_rgblight_brightness   = 1,\n    id_qmk_rgblight_effect       = 2,\n    id_qmk_rgblight_effect_speed = 3,\n    id_qmk_rgblight_color        = 4,\n};\n\nenum via_qmk_rgb_matrix_value {\n    id_qmk_rgb_matrix_brightness   = 1,\n    id_qmk_rgb_matrix_effect       = 2,\n    id_qmk_rgb_matrix_effect_speed = 3,\n    id_qmk_rgb_matrix_color        = 4,\n};\n\nenum via_qmk_led_matrix_value {\n    id_qmk_led_matrix_brightness   = 1,\n    id_qmk_led_matrix_effect       = 2,\n    id_qmk_led_matrix_effect_speed = 3,\n};\n\nenum via_qmk_audio_value {\n    id_qmk_audio_enable        = 1,\n    id_qmk_audio_clicky_enable = 2,\n};\n\n// Can be called in an overriding via_init_kb() to test if keyboard level code usage of\n// EEPROM is invalid and use/save defaults.\nbool via_eeprom_is_valid(void);\n\n// Sets VIA/keyboard level usage of EEPROM to valid/invalid\n// Keyboard level code (eg. via_init_kb()) should not call this\nvoid via_eeprom_set_valid(bool valid);\n\n// Called by QMK core to initialize dynamic keymaps etc.\nvoid eeconfig_init_via(void);\nvoid via_init(void);\n\n// Used by VIA to store and retrieve the layout options.\nuint32_t via_get_layout_options(void);\nvoid     via_set_layout_options(uint32_t value);\nvoid     via_set_layout_options_kb(uint32_t value);\n\n#if VIA_EEPROM_CUSTOM_CONFIG_SIZE > 0\nuint32_t via_read_custom_config(void *buf, uint32_t offset, uint32_t length);\nuint32_t via_update_custom_config(const void *buf, uint32_t offset, uint32_t length);\n#endif\n\n// Used by VIA to tell a device to flash LEDs (or do something else) when that\n// device becomes the active device being configured, on startup or switching\n// between devices.\nvoid via_set_device_indication(uint8_t value);\n\n// Called by QMK core to process VIA-specific keycodes.\nbool process_record_via(uint16_t keycode, keyrecord_t *record);\n\n// These are made external so that keyboard level custom value handlers can use them.\n#if defined(BACKLIGHT_ENABLE)\nvoid via_qmk_backlight_command(uint8_t *data, uint8_t length);\nvoid via_qmk_backlight_set_value(uint8_t *data);\nvoid via_qmk_backlight_get_value(uint8_t *data);\nvoid via_qmk_backlight_save(void);\n#endif\n\n#if defined(RGBLIGHT_ENABLE)\nvoid via_qmk_rgblight_command(uint8_t *data, uint8_t length);\nvoid via_qmk_rgblight_set_value(uint8_t *data);\nvoid via_qmk_rgblight_get_value(uint8_t *data);\nvoid via_qmk_rgblight_save(void);\n#endif\n\n#if defined(RGB_MATRIX_ENABLE)\nvoid via_qmk_rgb_matrix_command(uint8_t *data, uint8_t length);\nvoid via_qmk_rgb_matrix_set_value(uint8_t *data);\nvoid via_qmk_rgb_matrix_get_value(uint8_t *data);\nvoid via_qmk_rgb_matrix_save(void);\n#endif\n\n#if defined(LED_MATRIX_ENABLE)\nvoid via_qmk_led_matrix_command(uint8_t *data, uint8_t length);\nvoid via_qmk_led_matrix_set_value(uint8_t *data);\nvoid via_qmk_led_matrix_get_value(uint8_t *data);\nvoid via_qmk_led_matrix_save(void);\n#endif\n\n#if defined(AUDIO_ENABLE)\nvoid via_qmk_audio_command(uint8_t *data, uint8_t length);\nvoid via_qmk_audio_set_value(uint8_t *data);\nvoid via_qmk_audio_get_value(uint8_t *data);\nvoid via_qmk_audio_save(void);\n#endif\n"
  },
  {
    "path": "quantum/virtser.h",
    "content": "#pragma once\n\nvoid virtser_init(void);\n\n/* Define this function in your code to process incoming bytes */\nvoid virtser_recv(const uint8_t ch);\n\n/* Call this to send a character over the Virtual Serial Device */\nvoid virtser_send(const uint8_t byte);\n"
  },
  {
    "path": "quantum/wear_leveling/tests/backing_mocks.cpp",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include \"backing_mocks.hpp\"\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Backing Store Mock implementation\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nvoid MockBackingStore::reset_instance() {\n    for (auto&& e : backing_storage)\n        e.reset();\n\n    locked = true;\n\n    backing_erasure_count     = 0;\n    backing_max_write_count   = 0;\n    backing_total_write_count = 0;\n\n    backing_init_invoke_count   = 0;\n    backing_unlock_invoke_count = 0;\n    backing_erase_invoke_count  = 0;\n    backing_write_invoke_count  = 0;\n    backing_lock_invoke_count   = 0;\n\n    init_success_callback   = [](std::uint64_t) { return true; };\n    erase_success_callback  = [](std::uint64_t) { return true; };\n    unlock_success_callback = [](std::uint64_t) { return true; };\n    write_success_callback  = [](std::uint64_t, std::uint32_t) { return true; };\n    lock_success_callback   = [](std::uint64_t) { return true; };\n\n    write_log.clear();\n}\n\nbool MockBackingStore::init(void) {\n    ++backing_init_invoke_count;\n\n    if (init_success_callback) {\n        return init_success_callback(backing_init_invoke_count);\n    }\n    return true;\n}\n\nbool MockBackingStore::unlock(void) {\n    ++backing_unlock_invoke_count;\n\n    EXPECT_TRUE(is_locked()) << \"Attempted to unlock but was not locked\";\n    locked = false;\n\n    if (unlock_success_callback) {\n        return unlock_success_callback(backing_unlock_invoke_count);\n    }\n    return true;\n}\n\nbool MockBackingStore::erase(void) {\n    ++backing_erase_invoke_count;\n\n    // Erase each slot\n    for (std::size_t i = 0; i < backing_storage.size(); ++i) {\n        // Drop out of erase early with failure if we need to\n        if (erase_success_callback && !erase_success_callback(backing_erase_invoke_count)) {\n            append_log(true);\n            return false;\n        }\n\n        backing_storage[i].erase();\n    }\n\n    // Keep track of the erase in the write log so that we can verify during tests\n    append_log(true);\n\n    ++backing_erasure_count;\n    return true;\n}\n\nbool MockBackingStore::write(uint32_t address, backing_store_int_t value) {\n    ++backing_write_invoke_count;\n\n    // precondition: value's buffer size already matches BACKING_STORE_WRITE_SIZE\n    EXPECT_TRUE(address % BACKING_STORE_WRITE_SIZE == 0) << \"Supplied address was not aligned with the backing store integral size\";\n    EXPECT_TRUE(address + BACKING_STORE_WRITE_SIZE <= WEAR_LEVELING_BACKING_SIZE) << \"Address would result of out-of-bounds access\";\n    EXPECT_FALSE(is_locked()) << \"Write was attempted without being unlocked first\";\n\n    // Drop out of write early with failure if we need to\n    if (write_success_callback && !write_success_callback(backing_write_invoke_count, address)) {\n        return false;\n    }\n\n    // Write the complement as we're simulating flash memory -- 0xFF means 0x00\n    std::size_t index = address / BACKING_STORE_WRITE_SIZE;\n    backing_storage[index].set(~value);\n\n    // Keep track of the write log so that we can verify during tests\n    append_log(address, value);\n\n    // Keep track of the total number of writes into the backing store\n    ++backing_total_write_count;\n\n    return true;\n}\n\nbool MockBackingStore::lock(void) {\n    ++backing_lock_invoke_count;\n\n    EXPECT_FALSE(is_locked()) << \"Attempted to lock but was not unlocked\";\n    locked = true;\n\n    if (lock_success_callback) {\n        return lock_success_callback(backing_lock_invoke_count);\n    }\n    return true;\n}\n\nbool MockBackingStore::read(uint32_t address, backing_store_int_t& value) const {\n    // precondition: value's buffer size already matches BACKING_STORE_WRITE_SIZE\n    EXPECT_TRUE(address % BACKING_STORE_WRITE_SIZE == 0) << \"Supplied address was not aligned with the backing store integral size\";\n    EXPECT_TRUE(address + BACKING_STORE_WRITE_SIZE <= WEAR_LEVELING_BACKING_SIZE) << \"Address would result of out-of-bounds access\";\n\n    // Read and take the complement as we're simulating flash memory -- 0xFF means 0x00\n    std::size_t index = address / BACKING_STORE_WRITE_SIZE;\n    value             = ~backing_storage[index].get();\n\n    return true;\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Backing Implementation\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\nextern \"C\" bool backing_store_init(void) {\n    return MockBackingStore::Instance().init();\n}\n\nextern \"C\" bool backing_store_unlock(void) {\n    return MockBackingStore::Instance().unlock();\n}\n\nextern \"C\" bool backing_store_erase(void) {\n    return MockBackingStore::Instance().erase();\n}\n\nextern \"C\" bool backing_store_write(uint32_t address, backing_store_int_t value) {\n    return MockBackingStore::Instance().write(address, value);\n}\n\nextern \"C\" bool backing_store_lock(void) {\n    return MockBackingStore::Instance().lock();\n}\n\nextern \"C\" bool backing_store_read(uint32_t address, backing_store_int_t* value) {\n    return MockBackingStore::Instance().read(address, *value);\n}\n"
  },
  {
    "path": "quantum/wear_leveling/tests/backing_mocks.hpp",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n#include <algorithm>\n#include <array>\n#include <cstdint>\n#include <cstdlib>\n#include <functional>\n#include <type_traits>\n#include <vector>\n\nextern \"C\" {\n#include \"fnv.h\"\n#include \"wear_leveling.h\"\n#include \"wear_leveling_internal.h\"\n};\n\n// Maximum number of mock write log entries to keep\nusing MOCK_WRITE_LOG_MAX_ENTRIES = std::integral_constant<std::size_t, 1024>;\n// Complement to the backing store integral, for emulating flash erases of all bytes=0xFF\nusing BACKING_STORE_INTEGRAL_COMPLEMENT = std::integral_constant<backing_store_int_t, ((backing_store_int_t)(~(backing_store_int_t)0))>;\n// Total number of elements stored in the backing arrays\nusing BACKING_STORE_ELEMENT_COUNT = std::integral_constant<std::size_t, (WEAR_LEVELING_BACKING_SIZE / sizeof(backing_store_int_t))>;\n\nclass MockBackingStoreElement {\n   private:\n    backing_store_int_t value;\n    std::size_t         writes;\n    std::size_t         erases;\n\n   public:\n    MockBackingStoreElement() : value(BACKING_STORE_INTEGRAL_COMPLEMENT::value), writes(0), erases(0) {}\n    void reset() {\n        erase();\n        writes = 0;\n        erases = 0;\n    }\n    void erase() {\n        if (!is_erased()) {\n            ++erases;\n        }\n        value = BACKING_STORE_INTEGRAL_COMPLEMENT::value;\n    }\n    backing_store_int_t get() const {\n        return value;\n    }\n    void set(const backing_store_int_t& v) {\n        EXPECT_TRUE(is_erased()) << \"Attempted write at index which isn't empty.\";\n        value = v;\n        ++writes;\n    }\n    std::size_t num_writes() const {\n        return writes;\n    }\n    std::size_t num_erases() const {\n        return erases;\n    }\n    bool is_erased() const {\n        return value == BACKING_STORE_INTEGRAL_COMPLEMENT::value;\n    }\n};\n\nstruct MockBackingStoreLogEntry {\n    MockBackingStoreLogEntry(uint32_t address, backing_store_int_t value) : address(address), value(value), erased(false) {}\n    MockBackingStoreLogEntry(bool erased) : address(0), value(0), erased(erased) {}\n    uint32_t            address = 0;     // The address of the operation\n    backing_store_int_t value   = 0;     // The value of the operation\n    bool                erased  = false; // Whether the entire backing store was erased\n};\n\nclass MockBackingStore {\n   private:\n    MockBackingStore() {\n        reset_instance();\n    }\n\n    // Type containing each of the entries and the write counts\n    using storage_t = std::array<MockBackingStoreElement, BACKING_STORE_ELEMENT_COUNT::value>;\n\n    // Whether the backing store is locked\n    bool locked;\n    // The actual data stored in the emulated flash\n    storage_t backing_storage;\n    // The number of erase cycles that have occurred\n    std::uint64_t backing_erasure_count;\n    // The max number of writes to an element of the backing store\n    std::uint64_t backing_max_write_count;\n    // The total number of writes to all elements of the backing store\n    std::uint64_t backing_total_write_count;\n    // The write log for the backing store\n    std::vector<MockBackingStoreLogEntry> write_log;\n\n    // The number of times each API was invoked\n    std::uint64_t backing_init_invoke_count;\n    std::uint64_t backing_unlock_invoke_count;\n    std::uint64_t backing_erase_invoke_count;\n    std::uint64_t backing_write_invoke_count;\n    std::uint64_t backing_lock_invoke_count;\n\n    // Whether init should succeed\n    std::function<bool(std::uint64_t)> init_success_callback;\n    // Whether erase should succeed\n    std::function<bool(std::uint64_t)> erase_success_callback;\n    // Whether unlocks should succeed\n    std::function<bool(std::uint64_t)> unlock_success_callback;\n    // Whether writes should succeed\n    std::function<bool(std::uint64_t, std::uint32_t)> write_success_callback;\n    // Whether locks should succeed\n    std::function<bool(std::uint64_t)> lock_success_callback;\n\n    template <typename... Args>\n    void append_log(Args&&... args) {\n        if (write_log.size() < MOCK_WRITE_LOG_MAX_ENTRIES::value) {\n            write_log.emplace_back(std::forward<Args>(args)...);\n        }\n    }\n\n   public:\n    static MockBackingStore& Instance() {\n        static MockBackingStore instance;\n        return instance;\n    }\n\n    std::uint64_t erasure_count() const {\n        return backing_erasure_count;\n    }\n    std::uint64_t max_write_count() const {\n        return backing_max_write_count;\n    }\n    std::uint64_t total_write_count() const {\n        return backing_total_write_count;\n    }\n\n    // The number of times each API was invoked\n    std::uint64_t init_invoke_count() const {\n        return backing_init_invoke_count;\n    }\n    std::uint64_t unlock_invoke_count() const {\n        return backing_unlock_invoke_count;\n    }\n    std::uint64_t erase_invoke_count() const {\n        return backing_erase_invoke_count;\n    }\n    std::uint64_t write_invoke_count() const {\n        return backing_write_invoke_count;\n    }\n    std::uint64_t lock_invoke_count() const {\n        return backing_lock_invoke_count;\n    }\n\n    // Clear out the internal data for the next run\n    void reset_instance();\n\n    bool is_locked() const {\n        return locked;\n    }\n\n    // APIs for the backing store\n    bool init();\n    bool unlock();\n    bool erase();\n    bool write(std::uint32_t address, backing_store_int_t value);\n    bool lock();\n    bool read(std::uint32_t address, backing_store_int_t& value) const;\n\n    // Control over when init/writes/erases should succeed\n    void set_init_callback(std::function<bool(std::uint64_t)> callback) {\n        init_success_callback = callback;\n    }\n    void set_erase_callback(std::function<bool(std::uint64_t)> callback) {\n        erase_success_callback = callback;\n    }\n    void set_unlock_callback(std::function<bool(std::uint64_t)> callback) {\n        unlock_success_callback = callback;\n    }\n    void set_write_callback(std::function<bool(std::uint64_t, std::uint32_t)> callback) {\n        write_success_callback = callback;\n    }\n    void set_lock_callback(std::function<bool(std::uint64_t)> callback) {\n        lock_success_callback = callback;\n    }\n\n    auto storage_begin() const -> decltype(backing_storage.begin()) {\n        return backing_storage.begin();\n    }\n    auto storage_end() const -> decltype(backing_storage.end()) {\n        return backing_storage.end();\n    }\n\n    auto storage_begin() -> decltype(backing_storage.begin()) {\n        return backing_storage.begin();\n    }\n    auto storage_end() -> decltype(backing_storage.end()) {\n        return backing_storage.end();\n    }\n\n    auto log_begin() -> decltype(write_log.begin()) {\n        return write_log.begin();\n    }\n    auto log_end() -> decltype(write_log.end()) {\n        return write_log.end();\n    }\n\n    auto log_begin() const -> decltype(write_log.begin()) {\n        return write_log.begin();\n    }\n    auto log_end() const -> decltype(write_log.end()) {\n        return write_log.end();\n    }\n};\n"
  },
  {
    "path": "quantum/wear_leveling/tests/rules.mk",
    "content": "wear_leveling_common_DEFS := \\\n\t-DWEAR_LEVELING_TESTS\nwear_leveling_common_SRC := \\\n\t$(LIB_PATH)/fnv/qmk_fnv_type_validation.c \\\n\t$(LIB_PATH)/fnv/hash_32a.c \\\n\t$(LIB_PATH)/fnv/hash_64a.c \\\n\t$(QUANTUM_PATH)/wear_leveling/wear_leveling.c \\\n\t$(QUANTUM_PATH)/wear_leveling/tests/backing_mocks.cpp\nwear_leveling_common_INC := \\\n\t$(LIB_PATH)/fnv \\\n\t$(QUANTUM_PATH)/wear_leveling\n\nwear_leveling_general_DEFS := \\\n\t$(wear_leveling_common_DEFS) \\\n\t-DBACKING_STORE_WRITE_SIZE=2 \\\n\t-DWEAR_LEVELING_BACKING_SIZE=48 \\\n\t-DWEAR_LEVELING_LOGICAL_SIZE=16\nwear_leveling_general_SRC := \\\n\t$(wear_leveling_common_SRC) \\\n\t$(QUANTUM_PATH)/wear_leveling/tests/wear_leveling_general.cpp\nwear_leveling_general_INC := \\\n\t$(wear_leveling_common_INC)\n\nwear_leveling_2byte_optimized_writes_DEFS := \\\n\t$(wear_leveling_common_DEFS) \\\n\t-DBACKING_STORE_WRITE_SIZE=2 \\\n\t-DWEAR_LEVELING_BACKING_SIZE=65536 \\\n\t-DWEAR_LEVELING_LOGICAL_SIZE=32768\nwear_leveling_2byte_optimized_writes_SRC := \\\n\t$(wear_leveling_common_SRC) \\\n\t$(QUANTUM_PATH)/wear_leveling/tests/wear_leveling_2byte_optimized_writes.cpp\nwear_leveling_2byte_optimized_writes_INC := \\\n\t$(wear_leveling_common_INC)\n\nwear_leveling_2byte_DEFS := \\\n\t$(wear_leveling_common_DEFS) \\\n\t-DBACKING_STORE_WRITE_SIZE=2 \\\n\t-DWEAR_LEVELING_BACKING_SIZE=48 \\\n\t-DWEAR_LEVELING_LOGICAL_SIZE=16\nwear_leveling_2byte_SRC := \\\n\t$(wear_leveling_common_SRC) \\\n\t$(QUANTUM_PATH)/wear_leveling/tests/wear_leveling_2byte.cpp\nwear_leveling_2byte_INC := \\\n\t$(wear_leveling_common_INC)\n\nwear_leveling_4byte_DEFS := \\\n\t$(wear_leveling_common_DEFS) \\\n\t-DBACKING_STORE_WRITE_SIZE=4 \\\n\t-DWEAR_LEVELING_BACKING_SIZE=48 \\\n\t-DWEAR_LEVELING_LOGICAL_SIZE=16\nwear_leveling_4byte_SRC := \\\n\t$(wear_leveling_common_SRC) \\\n\t$(QUANTUM_PATH)/wear_leveling/tests/wear_leveling_4byte.cpp\nwear_leveling_4byte_INC := \\\n\t$(wear_leveling_common_INC)\n\nwear_leveling_8byte_DEFS := \\\n\t$(wear_leveling_common_DEFS) \\\n\t-DBACKING_STORE_WRITE_SIZE=8 \\\n\t-DWEAR_LEVELING_BACKING_SIZE=48 \\\n\t-DWEAR_LEVELING_LOGICAL_SIZE=16\nwear_leveling_8byte_SRC := \\\n\t$(wear_leveling_common_SRC) \\\n\t$(QUANTUM_PATH)/wear_leveling/tests/wear_leveling_8byte.cpp\nwear_leveling_8byte_INC := \\\n\t$(wear_leveling_common_INC)\n"
  },
  {
    "path": "quantum/wear_leveling/tests/testlist.mk",
    "content": "TEST_LIST += \\\n\twear_leveling_general \\\n\twear_leveling_2byte_optimized_writes \\\n\twear_leveling_2byte \\\n\twear_leveling_4byte \\\n\twear_leveling_8byte\n"
  },
  {
    "path": "quantum/wear_leveling/tests/wear_leveling_2byte.cpp",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <numeric>\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include \"backing_mocks.hpp\"\n\nclass WearLeveling2Byte : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        MockBackingStore::Instance().reset_instance();\n        wear_leveling_init();\n    }\n};\n\nstatic std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> verify_data;\n\nstatic wear_leveling_status_t test_write(const uint32_t address, const void* value, size_t length) {\n    memcpy(&verify_data[address], value, length);\n    return wear_leveling_write(address, value, length);\n}\n\n/**\n * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location.\n */\nTEST_F(WearLeveling2Byte, FirstWriteOccursAfterHash) {\n    auto&   inst       = MockBackingStore::Instance();\n    uint8_t test_value = 0x15;\n    test_write(0x02, &test_value, sizeof(test_value));\n    EXPECT_EQ(inst.log_begin()->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << \"Invalid first write address.\";\n}\n\n/**\n * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location, after an erase has occurred.\n */\nTEST_F(WearLeveling2Byte, FirstWriteOccursAfterHash_AfterErase) {\n    auto&   inst       = MockBackingStore::Instance();\n    uint8_t test_value = 0x15;\n    wear_leveling_erase();\n    test_write(0x02, &test_value, sizeof(test_value));\n    EXPECT_EQ((inst.log_begin() + 1)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << \"Invalid first write address.\";\n}\n\n/**\n * This test forces consolidation by writing enough to the write log that it overflows, consolidating the data into the\n * base logical area.\n */\nTEST_F(WearLeveling2Byte, ConsolidationOverflow) {\n    auto& inst = MockBackingStore::Instance();\n\n    // Generate a test block of data which forces OPTIMIZED_64 writes\n    std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> testvalue;\n\n    // Write the data\n    std::iota(testvalue.begin(), testvalue.end(), 0x20);\n    EXPECT_EQ(test_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_CONSOLIDATED) << \"Write returned incorrect status\";\n    uint8_t dummy = 0x40;\n    EXPECT_EQ(test_write(0x04, &dummy, sizeof(dummy)), WEAR_LEVELING_SUCCESS) << \"Write returned incorrect status\";\n\n    // All writes are at address<64, so each logical byte written will generate 1 write log entry, thus 1 backing store write.\n    // Expected log:\n    // [0..11]:  optimised64,        backing address 0x18, logical address 0x00\n    // [12]:     erase\n    // [13..20]: consolidated data,  backing address 0x00, logical address 0x00\n    // [21..24]: FNV1a_64 result,    backing address 0x10\n    // [25]:     optimised64,        backing address 0x18, logical address 0x04\n    EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), 26);\n\n    // Verify the backing store writes for the write log\n    std::size_t       index;\n    write_log_entry_t e;\n    for (index = 0; index < 12; ++index) {\n        auto write_iter = inst.log_begin() + index;\n        EXPECT_EQ(write_iter->address, WEAR_LEVELING_LOGICAL_SIZE + 8 + (index * BACKING_STORE_WRITE_SIZE)) << \"Invalid write log address\";\n        e.raw16[0] = write_iter->value;\n        EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_OPTIMIZED_64) << \"Invalid write log entry type\";\n    }\n\n    // Verify the backing store erase\n    {\n        index           = 12;\n        auto write_iter = inst.log_begin() + index;\n        e.raw16[0]      = write_iter->value;\n        EXPECT_TRUE(write_iter->erased) << \"Backing store erase did not occur as required\";\n    }\n\n    // Verify the backing store writes for consolidation\n    for (index = 13; index < 21; ++index) {\n        auto write_iter = inst.log_begin() + index;\n        EXPECT_EQ(write_iter->address, (index - 13) * BACKING_STORE_WRITE_SIZE) << \"Invalid write log entry address\";\n    }\n\n    // Verify the FNV1a_64 write\n    {\n        EXPECT_EQ((inst.log_begin() + 21)->address, WEAR_LEVELING_LOGICAL_SIZE) << \"Invalid write log address\";\n        e.raw16[0] = (inst.log_begin() + 21)->value;\n        e.raw16[1] = (inst.log_begin() + 22)->value;\n        e.raw16[2] = (inst.log_begin() + 23)->value;\n        e.raw16[3] = (inst.log_begin() + 24)->value;\n        EXPECT_EQ(e.raw64, fnv_64a_buf(testvalue.data(), testvalue.size(), FNV1A_64_INIT)) << \"Invalid checksum\"; // Note that checksum is based on testvalue, as we overwrote one byte and need to consult the consolidated data, not the current\n    }\n\n    // Verify the final write\n    EXPECT_EQ((inst.log_begin() + 25)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << \"Invalid write log address\";\n\n    // Verify the data is what we expected\n    std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> readback;\n    EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << \"Failed to read back the saved data\";\n    EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << \"Readback did not match\";\n\n    // Re-init and re-read, verifying the reload capability\n    EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << \"Re-initialisation failed\";\n    EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << \"Failed to read back the saved data\";\n    EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << \"Readback did not match\";\n}\n\n/**\n * This test verifies multibyte readback gets canceled with an out-of-bounds address.\n */\nTEST_F(WearLeveling2Byte, PlaybackReadbackMultibyte_OOB) {\n    auto& inst     = MockBackingStore::Instance();\n    auto  logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));\n\n    // Invalid FNV1a_64 hash\n    (logstart + 0)->set(0);\n    (logstart + 1)->set(0);\n    (logstart + 2)->set(0);\n    (logstart + 3)->set(0);\n\n    // Set up a 2-byte logical write of [0x11,0x12] at logical offset 0x01\n    auto entry0    = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2);\n    entry0.raw8[3] = 0x11;\n    entry0.raw8[4] = 0x12;\n    (logstart + 4)->set(~entry0.raw16[0]);\n    (logstart + 5)->set(~entry0.raw16[1]);\n    (logstart + 6)->set(~entry0.raw16[2]);\n\n    // Set up a 2-byte logical write of [0x13,0x14] at logical offset 0x1000 (out of bounds)\n    auto entry1    = LOG_ENTRY_MAKE_MULTIBYTE(0x1000, 2);\n    entry1.raw8[3] = 0x13;\n    entry1.raw8[4] = 0x14;\n    (logstart + 7)->set(~entry1.raw16[0]);\n    (logstart + 8)->set(~entry1.raw16[1]);\n    (logstart + 9)->set(~entry1.raw16[2]);\n\n    // Set up a 2-byte logical write of [0x15,0x16] at logical offset 0x01\n    auto entry2    = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2);\n    entry2.raw8[3] = 0x15;\n    entry2.raw8[4] = 0x16;\n    (logstart + 10)->set(~entry2.raw16[0]);\n    (logstart + 11)->set(~entry2.raw16[1]);\n    (logstart + 12)->set(~entry2.raw16[2]);\n\n    EXPECT_EQ(inst.erasure_count(), 0) << \"Invalid initial erase count\";\n    EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << \"Readback should have failed and triggered consolidation\";\n    EXPECT_EQ(inst.erasure_count(), 1) << \"Invalid final erase count\";\n\n    uint8_t buf[2];\n    wear_leveling_read(0x01, buf, sizeof(buf));\n    EXPECT_EQ(buf[0], 0x11) << \"Readback should have maintained the previous pre-failure value from the write log\";\n    EXPECT_EQ(buf[1], 0x12) << \"Readback should have maintained the previous pre-failure value from the write log\";\n}\n\n/**\n * This test verifies optimized 64 readback gets canceled with an out-of-bounds address.\n */\nTEST_F(WearLeveling2Byte, PlaybackReadbackOptimized64_OOB) {\n    auto& inst     = MockBackingStore::Instance();\n    auto  logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));\n\n    // Invalid FNV1a_64 hash\n    (logstart + 0)->set(0);\n    (logstart + 1)->set(0);\n    (logstart + 2)->set(0);\n    (logstart + 3)->set(0);\n\n    // Set up a 1-byte logical write of 0x11 at logical offset 0x01\n    auto entry0 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x01, 0x11);\n    (logstart + 4)->set(~entry0.raw16[0]);\n\n    // Set up a 1-byte logical write of 0x11 at logical offset 0x30 (out of bounds)\n    auto entry1 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x30, 0x11);\n    (logstart + 5)->set(~entry1.raw16[0]);\n\n    // Set up a 1-byte logical write of 0x12 at logical offset 0x01\n    auto entry2 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x01, 0x12);\n    (logstart + 6)->set(~entry2.raw16[0]);\n\n    EXPECT_EQ(inst.erasure_count(), 0) << \"Invalid initial erase count\";\n    EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << \"Readback should have failed and triggered consolidation\";\n    EXPECT_EQ(inst.erasure_count(), 1) << \"Invalid final erase count\";\n    uint8_t tmp;\n    wear_leveling_read(0x01, &tmp, sizeof(tmp));\n    EXPECT_EQ(tmp, 0x11) << \"Readback should have maintained the previous pre-failure value from the write log\";\n}\n\n/**\n * This test verifies word 0/1 readback gets canceled with an out-of-bounds address.\n */\nTEST_F(WearLeveling2Byte, PlaybackReadbackWord01_OOB) {\n    auto& inst     = MockBackingStore::Instance();\n    auto  logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));\n\n    // Invalid FNV1a_64 hash\n    (logstart + 0)->set(0);\n    (logstart + 1)->set(0);\n    (logstart + 2)->set(0);\n    (logstart + 3)->set(0);\n\n    // Set up a 1-byte logical write of 1 at logical offset 0x02\n    auto entry0 = LOG_ENTRY_MAKE_WORD_01(0x02, 1);\n    (logstart + 4)->set(~entry0.raw16[0]);\n\n    // Set up a 1-byte logical write of 1 at logical offset 0x1000 (out of bounds)\n    auto entry1 = LOG_ENTRY_MAKE_WORD_01(0x1000, 1);\n    (logstart + 5)->set(~entry1.raw16[0]);\n\n    // Set up a 1-byte logical write of 0 at logical offset 0x02\n    auto entry2 = LOG_ENTRY_MAKE_WORD_01(0x02, 0);\n    (logstart + 6)->set(~entry2.raw16[0]);\n\n    EXPECT_EQ(inst.erasure_count(), 0) << \"Invalid initial erase count\";\n    EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << \"Readback should have failed and triggered consolidation\";\n    EXPECT_EQ(inst.erasure_count(), 1) << \"Invalid final erase count\";\n    uint8_t tmp;\n    wear_leveling_read(0x02, &tmp, sizeof(tmp));\n    EXPECT_EQ(tmp, 1) << \"Readback should have maintained the previous pre-failure value from the write log\";\n}\n"
  },
  {
    "path": "quantum/wear_leveling/tests/wear_leveling_2byte_optimized_writes.cpp",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <numeric>\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include \"backing_mocks.hpp\"\n\nclass WearLeveling2ByteOptimizedWrites : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        MockBackingStore::Instance().reset_instance();\n        wear_leveling_init();\n    }\n};\n\nstatic std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> verify_data;\n\nstatic wear_leveling_status_t test_write(const uint32_t address, const void* value, size_t length) {\n    memcpy(&verify_data[address], value, length);\n    return wear_leveling_write(address, value, length);\n}\n\n/**\n * This test ensures the correct number of backing store writes occurs with a multibyte write, given the input buffer size.\n */\nTEST_F(WearLeveling2ByteOptimizedWrites, MultibyteBackingStoreWriteCounts) {\n    auto& inst = MockBackingStore::Instance();\n\n    for (std::size_t length = 1; length <= 5; ++length) {\n        // Clear things out\n        std::fill(verify_data.begin(), verify_data.end(), 0);\n        inst.reset_instance();\n        wear_leveling_init();\n\n        // Generate a test block of data\n        std::vector<std::uint8_t> testvalue(length);\n        std::iota(testvalue.begin(), testvalue.end(), 0x20);\n\n        // Write the data\n        EXPECT_EQ(test_write(2000, testvalue.data(), testvalue.size()), WEAR_LEVELING_SUCCESS) << \"Write failed with incorrect status\";\n\n        std::size_t expected;\n        if (length > 3) {\n            expected = 4;\n        } else if (length > 1) {\n            expected = 3;\n        } else {\n            expected = 2;\n        }\n\n        // Check that we got the expected number of write log entries\n        EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), expected);\n    }\n}\n\n/**\n * This test runs through writing U16 values of `0` or `1` over the entire logical address range, to even addresses only.\n *  - Addresses <16384 will result in a single optimised backing write\n *  - Higher addresses will result in a multibyte write of 3 backing writes\n */\nTEST_F(WearLeveling2ByteOptimizedWrites, WriteOneThenZeroToEvenAddresses) {\n    auto& inst = MockBackingStore::Instance();\n\n    // Only attempt writes for each address up to a limit that would NOT force a consolidated data write.\n    std::size_t writes_per_loop = (MOCK_WRITE_LOG_MAX_ENTRIES::value / 6) - 1; // Worst case is 6 writes for each pair of writes of 0/1\n    std::size_t final_address;\n    for (uint32_t address = 0; address < WEAR_LEVELING_LOGICAL_SIZE; address += (writes_per_loop * 2)) {\n        // Clear things out\n        std::fill(verify_data.begin(), verify_data.end(), 0);\n        inst.reset_instance();\n        wear_leveling_init();\n\n        // Loop through all the addresses in this range\n        std::size_t expected = 0;\n        for (uint32_t offset = 0; offset < (writes_per_loop * 2); offset += 2) {\n            // If we're about to exceed the limit of the logical store, skip the writes\n            if (address + offset + 2 > WEAR_LEVELING_LOGICAL_SIZE) {\n                break;\n            }\n\n            // The default erased value of the wear-leveling cache is zero, so we write a one first, then a zero, to ensure a backing store write occurs.\n            uint16_t val = 1;\n            EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << \"Write failed with incorrect status\";\n            val = 0;\n            EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << \"Write failed with incorrect status\";\n\n            std::size_t backing_store_writes_expected = 0;\n            if (address + offset < 16384) {\n                // A U16 value of 0/1 at an even address <16384 will result in 1 backing write each, so we need 2 backing writes for 2 logical writes\n                backing_store_writes_expected = 2;\n            } else {\n                // All other addresses result in a multibyte write (3 backing store writes) to write two local bytes of data\n                backing_store_writes_expected = 6;\n            }\n\n            // Keep track of the total number of expected writes to the backing store\n            expected += backing_store_writes_expected;\n\n            // Verify we're at the correct number of writes\n            EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), expected) << \"Write log doesn't match required number of backing store writes for address \" << (address + offset);\n\n            // Verify that the write log entries we expect are actually present\n            std::size_t       write_index = expected - backing_store_writes_expected;\n            auto              write_iter  = inst.log_begin() + write_index;\n            write_log_entry_t e;\n            if (address + offset < 16384) {\n                // A U16 value of 0/1 at an even address <16384 will result in 1 backing write each, so we need 2 backing writes for 2 logical writes\n                for (std::size_t i = 0; i < 2; ++i) {\n                    e.raw16[0] = write_iter->value;\n                    EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_WORD_01) << \"Invalid write log entry type at \" << (address + offset);\n                    ++write_iter;\n                }\n            } else {\n                // Multibyte write\n                e.raw16[0] = write_iter->value;\n                EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << \"Invalid write log entry type at \" << (address + offset);\n                EXPECT_EQ(LOG_ENTRY_MULTIBYTE_GET_LENGTH(e), 2) << \"Invalid write log entry length at \" << (address + offset);\n                ++write_iter;\n            }\n\n            // Keep track of the final address written, so we can verify the entire logical range was handled\n            final_address = address + offset;\n        }\n\n        // Verify the number of writes that occurred to the backing store\n        size_t backing_write_count = std::distance(inst.log_begin(), inst.log_end());\n        EXPECT_EQ(backing_write_count, expected) << \"Invalid write count at address \" << address;\n\n        // Verify the data is what we expected\n        std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> readback;\n        EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << \"Failed to read back the saved data\";\n        EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << \"Readback for address \" << address << \" did not match\";\n\n        // Re-init and re-read, testing the reload capability\n        EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << \"Re-initialisation failed\";\n        EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << \"Failed to read back the saved data\";\n        EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << \"Readback for address \" << address << \" did not match\";\n    }\n\n    // Verify the full range of the logical area got written\n    EXPECT_EQ(final_address, WEAR_LEVELING_LOGICAL_SIZE - 2) << \"Invalid final write address\";\n}\n\n/**\n * This test runs through writing U16 values of `0` or `1` over the entire logical address range, to odd addresses only.\n *  - Addresses <63 will result in 2 optimised backing writes\n *  - Address 63 results in a single optimised backing write for the first logical byte, and a multibyte write of 2 backing writes for the second logical byte\n *  - Higher addresses will result in a multibyte write of 3 backing writes\n */\nTEST_F(WearLeveling2ByteOptimizedWrites, WriteOneThenZeroToOddAddresses) {\n    auto& inst = MockBackingStore::Instance();\n\n    // Only attempt writes for each address up to a limit that would NOT force a consolidated data write.\n    std::size_t writes_per_loop = (MOCK_WRITE_LOG_MAX_ENTRIES::value / 6) - 1; // Worst case is 6 writes for each pair of writes of 0/1\n    std::size_t final_address;\n    for (uint32_t address = 1; address < WEAR_LEVELING_LOGICAL_SIZE; address += (writes_per_loop * 2)) {\n        // Clear things out\n        std::fill(verify_data.begin(), verify_data.end(), 0);\n        inst.reset_instance();\n        wear_leveling_init();\n\n        // Loop through all the addresses in this range\n        std::size_t expected = 0;\n        for (uint32_t offset = 0; offset < (writes_per_loop * 2); offset += 2) {\n            // If we're about to exceed the limit of the logical store, skip the writes\n            if (address + offset + 2 > WEAR_LEVELING_LOGICAL_SIZE) {\n                break;\n            }\n\n            // The default erased value of the wear-leveling cache is zero, so we write a one first, then a zero, to ensure a backing store write occurs.\n            uint16_t val = 1;\n            EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << \"Write failed with incorrect status\";\n            val = 0;\n            EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << \"Write failed with incorrect status\";\n\n            std::size_t backing_store_writes_expected = 0;\n            if (address + offset < 63) {\n                // A U16 value of 0/1 at an odd address <64 will result in 2 backing writes each, so we need 4 backing writes for 2 logical writes\n                backing_store_writes_expected = 4;\n            } else if (address + offset == 63) {\n                // If we're straddling the boundary for optimised bytes (addr==64), then the first logical byte is written using the optimised write (1 backing\n                // store write), and the second logical byte uses a multibyte write (2 backing store writes)\n                backing_store_writes_expected = 2    // First logical bytes written using optimised log entries\n                                                + 4; // Second logical bytes written using multibyte log entries\n            } else {\n                // All other addresses result in a multibyte write (3 backing store writes) to write two local bytes of data\n                backing_store_writes_expected = 6;\n            }\n\n            // Keep track of the total number of expected writes to the backing store\n            expected += backing_store_writes_expected;\n\n            // Verify we're at the correct number of writes\n            EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), expected) << \"Write log doesn't match required number of backing store writes for address \" << (address + offset);\n\n            // Verify that the write log entries we expect are actually present\n            std::size_t       write_index = expected - backing_store_writes_expected;\n            auto              write_iter  = inst.log_begin() + write_index;\n            write_log_entry_t e;\n            if (address + offset < 63) {\n                // A U16 value of 0/1 at an odd address <64 will result in 2 backing writes each, so we need 4 backing writes for 2 logical writes\n                for (std::size_t i = 0; i < 4; ++i) {\n                    e.raw16[0] = write_iter->value;\n                    EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_OPTIMIZED_64) << \"Invalid write log entry type\";\n                    ++write_iter;\n                }\n            } else if (address + offset == 63) {\n                // First log entry is the 64-addr optimised one\n                e.raw16[0] = write_iter->value;\n                EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_OPTIMIZED_64) << \"Invalid write log entry type\";\n                ++write_iter;\n\n                // Second log entry is the multibyte entry for the second logical byte\n                e.raw16[0] = write_iter->value;\n                EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << \"Invalid write log entry type\";\n                EXPECT_EQ(LOG_ENTRY_MULTIBYTE_GET_LENGTH(e), 1) << \"Invalid write log entry length\";\n                ++write_iter;\n            } else {\n                // Multibyte write\n                e.raw16[0] = write_iter->value;\n                EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << \"Invalid write log entry type\";\n                EXPECT_EQ(LOG_ENTRY_MULTIBYTE_GET_LENGTH(e), 2) << \"Invalid write log entry length\";\n                ++write_iter;\n            }\n\n            // Keep track of the final address written, so we can verify the entire logical range was handled\n            final_address = address + offset;\n        }\n\n        // Verify the number of writes that occurred to the backing store\n        size_t backing_write_count = std::distance(inst.log_begin(), inst.log_end());\n        EXPECT_EQ(backing_write_count, expected) << \"Invalid write count at address \" << address;\n\n        // Verify the data is what we expected\n        std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> readback;\n        EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << \"Failed to read back the saved data\";\n        EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << \"Readback for address \" << address << \" did not match\";\n\n        // Re-init and re-read, testing the reload capability\n        EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << \"Re-initialisation failed\";\n        EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << \"Failed to read back the saved data\";\n        EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << \"Readback for address \" << address << \" did not match\";\n    }\n\n    // Verify the full range of the logical area got written\n    EXPECT_EQ(final_address, WEAR_LEVELING_LOGICAL_SIZE - 3) << \"Invalid final write address\";\n}\n\n/**\n * This test verifies readback after playback of the write log, simulating power loss and reboot.\n */\nTEST_F(WearLeveling2ByteOptimizedWrites, PlaybackReadbackOptimized64_Success) {\n    auto& inst     = MockBackingStore::Instance();\n    auto  logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));\n\n    // Invalid FNV1a_64 hash\n    (logstart + 0)->set(0);\n    (logstart + 1)->set(0);\n    (logstart + 2)->set(0);\n    (logstart + 3)->set(0);\n\n    // Set up a 1-byte logical write of 0x11 at logical offset 0x01\n    auto entry0 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x01, 0x11);\n    (logstart + 4)->set(~entry0.raw16[0]); // start at offset 4 to skip FNV1a_64 result\n\n    wear_leveling_init();\n    uint8_t tmp;\n\n    wear_leveling_read(0x01, &tmp, sizeof(tmp));\n    EXPECT_EQ(tmp, 0x11) << \"Failed to read back the seeded data\";\n}\n\n/**\n * This test verifies readback after playback of the write log, simulating power loss and reboot.\n */\nTEST_F(WearLeveling2ByteOptimizedWrites, PlaybackReadbackWord01_Success) {\n    auto& inst     = MockBackingStore::Instance();\n    auto  logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));\n\n    // Invalid FNV1a_64 hash\n    (logstart + 0)->set(0);\n    (logstart + 1)->set(0);\n    (logstart + 2)->set(0);\n    (logstart + 3)->set(0);\n\n    // Set up a 1-byte logical write of 1 at logical offset 0x02\n    auto entry0 = LOG_ENTRY_MAKE_WORD_01(0x02, 1);\n    (logstart + 4)->set(~entry0.raw16[0]); // start at offset 4 to skip FNV1a_64 result\n\n    wear_leveling_init();\n    uint8_t tmp;\n\n    wear_leveling_read(0x02, &tmp, sizeof(tmp));\n    EXPECT_EQ(tmp, 1) << \"Failed to read back the seeded data\";\n}\n"
  },
  {
    "path": "quantum/wear_leveling/tests/wear_leveling_4byte.cpp",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <numeric>\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include \"backing_mocks.hpp\"\n\nclass WearLeveling4Byte : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        MockBackingStore::Instance().reset_instance();\n        wear_leveling_init();\n    }\n};\n\nstatic std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> verify_data;\n\nstatic wear_leveling_status_t test_write(const uint32_t address, const void* value, size_t length) {\n    memcpy(&verify_data[address], value, length);\n    return wear_leveling_write(address, value, length);\n}\n\n/**\n * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location.\n */\nTEST_F(WearLeveling4Byte, FirstWriteOccursAfterHash) {\n    auto&   inst       = MockBackingStore::Instance();\n    uint8_t test_value = 0x15;\n    test_write(0x02, &test_value, sizeof(test_value));\n    EXPECT_EQ(inst.log_begin()->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << \"Invalid first write address.\";\n}\n\n/**\n * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location, after an erase has occurred.\n */\nTEST_F(WearLeveling4Byte, FirstWriteOccursAfterHash_AfterErase) {\n    auto&   inst       = MockBackingStore::Instance();\n    uint8_t test_value = 0x15;\n    wear_leveling_erase();\n    test_write(0x02, &test_value, sizeof(test_value));\n    EXPECT_EQ((inst.log_begin() + 1)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << \"Invalid first write address.\";\n}\n\n/**\n * This test ensures the correct number of backing store writes occurs with a multibyte write, given the input buffer size.\n */\nTEST_F(WearLeveling4Byte, MultibyteBackingStoreWriteCounts) {\n    auto& inst = MockBackingStore::Instance();\n\n    for (std::size_t length = 1; length <= 5; ++length) {\n        // Clear things out\n        std::fill(verify_data.begin(), verify_data.end(), 0);\n        inst.reset_instance();\n        wear_leveling_init();\n\n        // Generate a test block of data\n        std::vector<std::uint8_t> testvalue(length);\n        std::iota(testvalue.begin(), testvalue.end(), 0x20);\n\n        // Write the data\n        EXPECT_EQ(test_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_SUCCESS) << \"Write failed with incorrect status\";\n\n        std::size_t expected;\n        if (length > 1) {\n            expected = 2;\n        } else {\n            expected = 1;\n        }\n\n        // Check that we got the expected number of write log entries\n        EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), expected);\n    }\n}\n\n/**\n * This test forces consolidation by writing enough to the write log that it overflows, consolidating the data into the\n * base logical area.\n */\nTEST_F(WearLeveling4Byte, ConsolidationOverflow) {\n    auto& inst = MockBackingStore::Instance();\n\n    // Generate a test block of data\n    std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> testvalue;\n\n    // Write the data\n    std::iota(testvalue.begin(), testvalue.end(), 0x20);\n    EXPECT_EQ(test_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_CONSOLIDATED) << \"Write returned incorrect status\";\n    uint8_t dummy = 0x40;\n    EXPECT_EQ(test_write(0x04, &dummy, sizeof(dummy)), WEAR_LEVELING_SUCCESS) << \"Write returned incorrect status\";\n\n    // Expected log:\n    // [0,1]:   multibyte, 5 bytes, backing address 0x18, logical address 0x00\n    // [2,3]:   multibyte, 5 bytes, backing address 0x20, logical address 0x05\n    // [4,5]:   multibyte, 5 bytes, backing address 0x28, logical address 0x0A, triggers consolidation\n    // [6]:     erase\n    // [7,8]:   consolidated data,  backing address 0x00, logical address 0x00\n    // [9,10]:  consolidated data,  backing address 0x08, logical address 0x08\n    // [11,12]: FNV1a_64 result,    backing address 0x10\n    // [13]:    multibyte, 1 byte,  backing address 0x18, logical address 0x04\n    EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), 14);\n\n    // Verify the backing store writes for the write log\n    std::size_t       index;\n    write_log_entry_t e;\n    for (index = 0; index < 6; ++index) {\n        auto write_iter = inst.log_begin() + index;\n        EXPECT_EQ(write_iter->address, WEAR_LEVELING_LOGICAL_SIZE + 8 + (index * BACKING_STORE_WRITE_SIZE)) << \"Invalid write log address\";\n\n        // If this is the backing store write that contains the metadata, verify it\n        if (index % 2 == 0) {\n            write_log_entry_t e;\n            e.raw64 = write_iter->value;\n            EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << \"Invalid write log entry type\";\n        }\n    }\n\n    // Verify the backing store erase\n    {\n        index           = 6;\n        auto write_iter = inst.log_begin() + index;\n        e.raw64         = write_iter->value;\n        EXPECT_TRUE(write_iter->erased) << \"Backing store erase did not occur as required\";\n    }\n\n    // Verify the backing store writes for consolidation\n    for (index = 7; index < 11; ++index) {\n        auto write_iter = inst.log_begin() + index;\n        EXPECT_EQ(write_iter->address, (index - 7) * BACKING_STORE_WRITE_SIZE) << \"Invalid write log entry address\";\n    }\n\n    // Verify the FNV1a_64 write\n    {\n        EXPECT_EQ((inst.log_begin() + 11)->address, WEAR_LEVELING_LOGICAL_SIZE) << \"Invalid write log address\";\n        e.raw32[0] = (inst.log_begin() + 11)->value;\n        e.raw32[1] = (inst.log_begin() + 12)->value;\n        EXPECT_EQ(e.raw64, fnv_64a_buf(testvalue.data(), testvalue.size(), FNV1A_64_INIT)) << \"Invalid checksum\"; // Note that checksum is based on testvalue, as we overwrote one byte and need to consult the consolidated data, not the current\n    }\n\n    // Verify the final write\n    EXPECT_EQ((inst.log_begin() + 13)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << \"Invalid write log address\";\n\n    // Verify the data is what we expected\n    std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> readback;\n    EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << \"Failed to read back the saved data\";\n    EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << \"Readback did not match\";\n\n    // Re-init and re-read, verifying the reload capability\n    EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << \"Re-initialisation failed\";\n    EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << \"Failed to read back the saved data\";\n    EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << \"Readback did not match\";\n}\n\n/**\n * This test verifies multibyte readback gets canceled with an out-of-bounds address.\n */\nTEST_F(WearLeveling4Byte, PlaybackReadbackMultibyte_OOB) {\n    auto& inst     = MockBackingStore::Instance();\n    auto  logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));\n\n    // Invalid FNV1a_64 hash\n    (logstart + 0)->set(0);\n    (logstart + 1)->set(0);\n\n    // Set up a 2-byte logical write of [0x11,0x12] at logical offset 0x01\n    auto entry0    = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2);\n    entry0.raw8[3] = 0x11;\n    entry0.raw8[4] = 0x12;\n    (logstart + 2)->set(~entry0.raw32[0]);\n    (logstart + 3)->set(~entry0.raw32[1]);\n\n    // Set up a 2-byte logical write of [0x13,0x14] at logical offset 0x1000 (out of bounds)\n    auto entry1    = LOG_ENTRY_MAKE_MULTIBYTE(0x1000, 2);\n    entry1.raw8[3] = 0x13;\n    entry1.raw8[4] = 0x14;\n    (logstart + 4)->set(~entry1.raw32[0]);\n    (logstart + 5)->set(~entry1.raw32[1]);\n\n    // Set up a 2-byte logical write of [0x15,0x16] at logical offset 0x10\n    auto entry2    = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2);\n    entry2.raw8[3] = 0x15;\n    entry2.raw8[4] = 0x16;\n    (logstart + 6)->set(~entry2.raw32[0]);\n    (logstart + 7)->set(~entry2.raw32[1]);\n\n    EXPECT_EQ(inst.erasure_count(), 0) << \"Invalid initial erase count\";\n    EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << \"Readback should have failed and triggered consolidation\";\n    EXPECT_EQ(inst.erasure_count(), 1) << \"Invalid final erase count\";\n\n    uint8_t buf[2];\n    wear_leveling_read(0x01, buf, sizeof(buf));\n    EXPECT_EQ(buf[0], 0x11) << \"Readback should have maintained the previous pre-failure value from the write log\";\n    EXPECT_EQ(buf[1], 0x12) << \"Readback should have maintained the previous pre-failure value from the write log\";\n}\n"
  },
  {
    "path": "quantum/wear_leveling/tests/wear_leveling_8byte.cpp",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <numeric>\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include \"backing_mocks.hpp\"\n\nclass WearLeveling8Byte : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        MockBackingStore::Instance().reset_instance();\n        wear_leveling_init();\n    }\n};\n\nstatic std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> verify_data;\n\nstatic wear_leveling_status_t test_write(const uint32_t address, const void* value, size_t length) {\n    memcpy(&verify_data[address], value, length);\n    return wear_leveling_write(address, value, length);\n}\n\n/**\n * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location.\n */\nTEST_F(WearLeveling8Byte, FirstWriteOccursAfterHash) {\n    auto&   inst       = MockBackingStore::Instance();\n    uint8_t test_value = 0x15;\n    test_write(0x02, &test_value, sizeof(test_value));\n    EXPECT_EQ(inst.log_begin()->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << \"Invalid first write address.\";\n}\n\n/**\n * This test verifies that the first write after initialisation occurs after the FNV1a_64 hash location, after an erase has occurred.\n */\nTEST_F(WearLeveling8Byte, FirstWriteOccursAfterHash_AfterErase) {\n    auto&   inst       = MockBackingStore::Instance();\n    uint8_t test_value = 0x15;\n    wear_leveling_erase();\n    test_write(0x02, &test_value, sizeof(test_value));\n    EXPECT_EQ((inst.log_begin() + 1)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << \"Invalid first write address.\";\n}\n\n/**\n * This test ensures the correct number of backing store writes occurs with a multibyte write, given the input buffer size.\n */\nTEST_F(WearLeveling8Byte, MultibyteBackingStoreWriteCounts) {\n    auto& inst = MockBackingStore::Instance();\n\n    for (std::size_t length = 1; length <= 5; ++length) {\n        // Clear things out\n        std::fill(verify_data.begin(), verify_data.end(), 0);\n        inst.reset_instance();\n        wear_leveling_init();\n\n        // Generate a test block of data\n        std::vector<std::uint8_t> testvalue(length);\n        std::iota(testvalue.begin(), testvalue.end(), 0x20);\n\n        // Write the data\n        EXPECT_EQ(test_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_SUCCESS) << \"Write failed with incorrect status\";\n\n        // Check that we got the expected number of write log entries\n        EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), 1);\n    }\n}\n\n/**\n * This test forces consolidation by writing enough to the write log that it overflows, consolidating the data into the\n * base logical area.\n */\nTEST_F(WearLeveling8Byte, ConsolidationOverflow) {\n    auto& inst = MockBackingStore::Instance();\n\n    // Generate a test block of data\n    std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> testvalue;\n\n    // Write the data\n    std::iota(testvalue.begin(), testvalue.end(), 0x20);\n    EXPECT_EQ(test_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_CONSOLIDATED) << \"Write returned incorrect status\";\n    uint8_t dummy = 0x40;\n    EXPECT_EQ(test_write(0x04, &dummy, sizeof(dummy)), WEAR_LEVELING_SUCCESS) << \"Write returned incorrect status\";\n\n    // Expected log:\n    // [0]: multibyte, 5 bytes, backing address 0x18, logical address 0x00\n    // [1]: multibyte, 5 bytes, backing address 0x20, logical address 0x05\n    // [2]: multibyte, 5 bytes, backing address 0x28, logical address 0x0A, triggers consolidation\n    // [3]: erase\n    // [4]: consolidated data, backing address 0x00, logical address 0x00\n    // [5]: consolidated data, backing address 0x08, logical address 0x08\n    // [6]: FNV1a_64 result, backing address 0x10\n    // [7]: multibyte, 1 byte,  backing address 0x18, logical address 0x04\n    EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), 8);\n\n    // Verify the backing store writes for the write log\n    std::size_t       index;\n    write_log_entry_t e;\n    for (index = 0; index < 3; ++index) {\n        auto write_iter = inst.log_begin() + index;\n        EXPECT_EQ(write_iter->address, WEAR_LEVELING_LOGICAL_SIZE + 8 + (index * BACKING_STORE_WRITE_SIZE)) << \"Invalid write log address\";\n\n        write_log_entry_t e;\n        e.raw64 = write_iter->value;\n        EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << \"Invalid write log entry type\";\n    }\n\n    // Verify the backing store erase\n    {\n        index           = 3;\n        auto write_iter = inst.log_begin() + index;\n        e.raw64         = write_iter->value;\n        EXPECT_TRUE(write_iter->erased) << \"Backing store erase did not occur as required\";\n    }\n\n    // Verify the backing store writes for consolidation\n    for (index = 4; index < 6; ++index) {\n        auto write_iter = inst.log_begin() + index;\n        EXPECT_EQ(write_iter->address, (index - 4) * BACKING_STORE_WRITE_SIZE) << \"Invalid write log entry address\";\n    }\n\n    // Verify the FNV1a_64 write\n    {\n        EXPECT_EQ((inst.log_begin() + 6)->address, WEAR_LEVELING_LOGICAL_SIZE) << \"Invalid write log address\";\n        e.raw64 = (inst.log_begin() + 6)->value;\n        EXPECT_EQ(e.raw64, fnv_64a_buf(testvalue.data(), testvalue.size(), FNV1A_64_INIT)) << \"Invalid checksum\"; // Note that checksum is based on testvalue, as we overwrote one byte and need to consult the consolidated data, not the current\n    }\n\n    // Verify the final write\n    EXPECT_EQ((inst.log_begin() + 7)->address, WEAR_LEVELING_LOGICAL_SIZE + 8) << \"Invalid write log address\";\n\n    // Verify the data is what we expected\n    std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> readback;\n    EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << \"Failed to read back the saved data\";\n    EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << \"Readback did not match\";\n\n    // Re-init and re-read, verifying the reload capability\n    EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << \"Re-initialisation failed\";\n    EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << \"Failed to read back the saved data\";\n    EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << \"Readback did not match\";\n}\n\n/**\n * This test verifies multibyte readback gets canceled with an out-of-bounds address.\n */\nTEST_F(WearLeveling8Byte, PlaybackReadbackMultibyte_OOB) {\n    auto& inst     = MockBackingStore::Instance();\n    auto  logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));\n\n    // Invalid FNV1a_64 hash\n    (logstart + 0)->set(0);\n\n    // Set up a 2-byte logical write of [0x11,0x12] at logical offset 0x01\n    auto entry0    = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2);\n    entry0.raw8[3] = 0x11;\n    entry0.raw8[4] = 0x12;\n    (logstart + 1)->set(~entry0.raw64);\n\n    // Set up a 2-byte logical write of [0x13,0x14] at logical offset 0x1000 (out of bounds)\n    auto entry1    = LOG_ENTRY_MAKE_MULTIBYTE(0x1000, 2);\n    entry1.raw8[3] = 0x13;\n    entry1.raw8[4] = 0x14;\n    (logstart + 2)->set(~entry1.raw64);\n\n    // Set up a 2-byte logical write of [0x15,0x16] at logical offset 0x10\n    auto entry2    = LOG_ENTRY_MAKE_MULTIBYTE(0x01, 2);\n    entry2.raw8[3] = 0x15;\n    entry2.raw8[4] = 0x16;\n    (logstart + 3)->set(~entry2.raw64);\n\n    EXPECT_EQ(inst.erasure_count(), 0) << \"Invalid initial erase count\";\n    EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_CONSOLIDATED) << \"Readback should have failed and triggered consolidation\";\n    EXPECT_EQ(inst.erasure_count(), 1) << \"Invalid final erase count\";\n\n    uint8_t buf[2];\n    wear_leveling_read(0x01, buf, sizeof(buf));\n    EXPECT_EQ(buf[0], 0x11) << \"Readback should have maintained the previous pre-failure value from the write log\";\n    EXPECT_EQ(buf[1], 0x12) << \"Readback should have maintained the previous pre-failure value from the write log\";\n}\n"
  },
  {
    "path": "quantum/wear_leveling/tests/wear_leveling_general.cpp",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <numeric>\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n#include \"backing_mocks.hpp\"\n\nclass WearLevelingGeneral : public ::testing::Test {\n   protected:\n    void SetUp() override {\n        MockBackingStore::Instance().reset_instance();\n        wear_leveling_init();\n    }\n};\n\n/**\n * This test verifies that even if there is consolidated data present, if the checksum doesn't match then the cache is zero'd after reading the consolidated area, but before write log is played back.\n */\nTEST_F(WearLevelingGeneral, InvalidChecksum_ConsolidatedDataIgnored) {\n    auto& inst     = MockBackingStore::Instance();\n    auto  logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));\n\n    // Generate a test block of data\n    std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> testvalue;\n    std::iota(testvalue.begin(), testvalue.end(), 0x20);\n\n    // Write the data\n    EXPECT_EQ(wear_leveling_write(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_CONSOLIDATED) << \"Write returned incorrect status\";\n\n    // Invalidate the checksum\n    (logstart + 0)->erase();\n    (logstart + 1)->erase();\n    (logstart + 2)->erase();\n    (logstart + 3)->erase();\n\n    // Set up a 1-byte logical write of [0x11] at logical offset 0x01\n    auto entry0 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x01, 0x11);\n    (logstart + 4)->set(~entry0.raw16[0]);\n\n    // Re-init\n    EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_SUCCESS) << \"Init returned incorrect status\";\n    EXPECT_EQ(wear_leveling_read(0, testvalue.data(), testvalue.size()), WEAR_LEVELING_SUCCESS) << \"Failed to read\";\n    for (int i = 0; i < WEAR_LEVELING_LOGICAL_SIZE; ++i) {\n        EXPECT_EQ(testvalue[i], i == 0x01 ? 0x11 : 0x00) << \"Invalid readback\";\n    }\n}\n\n/**\n * This test verifies that writing the same data multiple times does not result in subsequent writes to the backing store.\n */\nTEST_F(WearLevelingGeneral, SameValue_SingleBackingWrite) {\n    auto& inst = MockBackingStore::Instance();\n\n    uint8_t test_val = 0x14;\n    EXPECT_EQ(wear_leveling_write(0x02, &test_val, sizeof(test_val)), WEAR_LEVELING_SUCCESS) << \"First overall write operation should have succeeded\";\n\n    uint64_t invoke_count = inst.unlock_invoke_count();\n    uint64_t erase_count  = inst.erase_invoke_count();\n    uint64_t write_count  = inst.write_invoke_count();\n    uint64_t lock_count   = inst.lock_invoke_count();\n\n    for (int i = 0; i < 10; ++i) {\n        EXPECT_EQ(wear_leveling_write(0x02, &test_val, sizeof(test_val)), WEAR_LEVELING_SUCCESS) << \"Subsequent overall write operation should have succeeded\";\n\n        EXPECT_EQ(inst.unlock_invoke_count(), invoke_count) << \"Unlock count should match\";\n        EXPECT_EQ(inst.erase_invoke_count(), erase_count) << \"Erase count should match\";\n        EXPECT_EQ(inst.write_invoke_count(), write_count) << \"Write count should match\";\n        EXPECT_EQ(inst.lock_invoke_count(), lock_count) << \"Lock count should match\";\n    }\n}\n\n/**\n * This test verifies that no other invocations occur if `backing_store_init()` fails.\n */\nTEST_F(WearLevelingGeneral, InitFailure) {\n    auto& inst = MockBackingStore::Instance();\n    inst.reset_instance(); // make sure the counters are all zero\n    inst.set_init_callback([](std::uint64_t count) { return false; });\n\n    EXPECT_EQ(inst.erasure_count(), 0) << \"Invalid initial erase count\";\n    EXPECT_EQ(wear_leveling_init(), WEAR_LEVELING_FAILED) << \"Init should have failed\";\n    EXPECT_EQ(inst.erasure_count(), 0) << \"Invalid final erase count\";\n\n    EXPECT_EQ(inst.init_invoke_count(), 1) << \"Init should have been invoked once\";\n    EXPECT_EQ(inst.unlock_invoke_count(), 0) << \"Unlock should not have been invoked\";\n    EXPECT_EQ(inst.erase_invoke_count(), 0) << \"Erase should not have been invoked\";\n    EXPECT_EQ(inst.write_invoke_count(), 0) << \"Write should not have been invoked\";\n    EXPECT_EQ(inst.lock_invoke_count(), 0) << \"Lock should not have been invoked\";\n}\n\n/**\n * This test verifies that no invocations occur if the supplied address is out of range while writing.\n */\nTEST_F(WearLevelingGeneral, WriteFailure_OOB) {\n    auto& inst = MockBackingStore::Instance();\n\n    uint8_t test_val = 0x14;\n    EXPECT_EQ(wear_leveling_write(0x21349830, &test_val, sizeof(test_val)), WEAR_LEVELING_FAILED) << \"Overall write operation should have failed\";\n\n    EXPECT_EQ(inst.unlock_invoke_count(), 0) << \"Unlock should not have been invoked\";\n    EXPECT_EQ(inst.erase_invoke_count(), 0) << \"Erase should not have been invoked\";\n    EXPECT_EQ(inst.write_invoke_count(), 0) << \"Write should not have been invoked\";\n    EXPECT_EQ(inst.lock_invoke_count(), 0) << \"Lock should not have been invoked\";\n}\n\n/**\n * This test verifies that a single write occurs if the supplied address and data length hits the edge of the logical area.\n */\nTEST_F(WearLevelingGeneral, WriteSuccess_BoundaryOK) {\n    auto& inst = MockBackingStore::Instance();\n\n    uint16_t test_val = 0x14;\n    EXPECT_EQ(wear_leveling_write(WEAR_LEVELING_LOGICAL_SIZE - sizeof(test_val), &test_val, sizeof(test_val)), WEAR_LEVELING_SUCCESS) << \"Overall write operation should have succeeded\";\n\n    EXPECT_EQ(inst.unlock_invoke_count(), 1) << \"Unlock should have been invoked once\";\n    EXPECT_EQ(inst.erase_invoke_count(), 0) << \"Erase should not have been invoked\";\n    EXPECT_EQ(inst.write_invoke_count(), 2) << \"Write should have been invoked twice\";\n    EXPECT_EQ(inst.lock_invoke_count(), 1) << \"Lock should have been invoked once\";\n}\n\n/**\n * This test verifies that no invocations occur if the supplied address and length would generate writes outside the logical range.\n */\nTEST_F(WearLevelingGeneral, WriteFailure_BoundaryOverflow) {\n    auto& inst = MockBackingStore::Instance();\n\n    uint16_t test_val = 0x14;\n    EXPECT_EQ(wear_leveling_write(WEAR_LEVELING_LOGICAL_SIZE - sizeof(test_val) + 1, &test_val, sizeof(test_val)), WEAR_LEVELING_FAILED) << \"Overall write operation should have failed\";\n\n    EXPECT_EQ(inst.unlock_invoke_count(), 0) << \"Unlock should not have been invoked\";\n    EXPECT_EQ(inst.erase_invoke_count(), 0) << \"Erase should not have been invoked\";\n    EXPECT_EQ(inst.write_invoke_count(), 0) << \"Write should not have been invoked\";\n    EXPECT_EQ(inst.lock_invoke_count(), 0) << \"Lock should not have been invoked\";\n}\n\n/**\n * This test verifies that no invocations occur if the supplied address is out of range while reading.\n */\nTEST_F(WearLevelingGeneral, ReadFailure_OOB) {\n    auto& inst = MockBackingStore::Instance();\n\n    uint8_t test_val = 0;\n    EXPECT_EQ(wear_leveling_read(0x21349830, &test_val, sizeof(test_val)), WEAR_LEVELING_FAILED) << \"Overall read operation should have failed\";\n\n    EXPECT_EQ(inst.unlock_invoke_count(), 0) << \"Unlock should not have been invoked\";\n    EXPECT_EQ(inst.erase_invoke_count(), 0) << \"Erase should not have been invoked\";\n    EXPECT_EQ(inst.write_invoke_count(), 0) << \"Write should not have been invoked\";\n    EXPECT_EQ(inst.lock_invoke_count(), 0) << \"Lock should not have been invoked\";\n}\n\n/**\n * This test verifies that no write invocations occur if `backing_store_unlock()` fails.\n */\nTEST_F(WearLevelingGeneral, UnlockFailure_NoWrite) {\n    auto& inst = MockBackingStore::Instance();\n    inst.set_unlock_callback([](std::uint64_t count) { return false; });\n\n    uint8_t test_val = 0x14;\n    EXPECT_EQ(wear_leveling_write(0x04, &test_val, sizeof(test_val)), WEAR_LEVELING_FAILED) << \"Overall write operation should have failed\";\n\n    EXPECT_EQ(inst.unlock_invoke_count(), 1) << \"Unlock should have been invoked once\";\n    EXPECT_EQ(inst.erase_invoke_count(), 0) << \"Erase should not have been invoked\";\n    EXPECT_EQ(inst.write_invoke_count(), 0) << \"Write should not have been invoked\";\n    EXPECT_EQ(inst.lock_invoke_count(), 0) << \"Lock should not have been invoked\";\n\n    test_val = 0;\n    wear_leveling_read(0x04, &test_val, sizeof(test_val));\n    EXPECT_EQ(test_val, 0x14) << \"Readback should come from cache regardless of unlock failure\";\n}\n\n/**\n * This test verifies that no erase invocations occur if `backing_store_unlock()` fails.\n */\nTEST_F(WearLevelingGeneral, UnlockFailure_NoErase) {\n    auto& inst = MockBackingStore::Instance();\n    inst.set_unlock_callback([](std::uint64_t count) { return false; });\n\n    EXPECT_EQ(wear_leveling_erase(), WEAR_LEVELING_FAILED) << \"Overall erase operation should have failed\";\n\n    EXPECT_EQ(inst.unlock_invoke_count(), 1) << \"Unlock should have been invoked once\";\n    EXPECT_EQ(inst.erase_invoke_count(), 0) << \"Erase should not have been invoked\";\n    EXPECT_EQ(inst.write_invoke_count(), 0) << \"Write should not have been invoked\";\n    EXPECT_EQ(inst.lock_invoke_count(), 0) << \"Lock should not have been invoked\";\n}\n\n/**\n * This test verifies that only one write invocation occurs if `backing_store_write()` fails.\n */\nTEST_F(WearLevelingGeneral, WriteFailure_NoSubsequentWrites) {\n    auto& inst = MockBackingStore::Instance();\n    inst.set_write_callback([](std::uint64_t count, std::uint32_t address) { return false; });\n\n    uint8_t test_val = 0x14;\n    EXPECT_EQ(wear_leveling_write(0x04, &test_val, sizeof(test_val)), WEAR_LEVELING_FAILED) << \"Overall write operation should have failed\";\n\n    EXPECT_EQ(inst.unlock_invoke_count(), 1) << \"Unlock should have been invoked once\";\n    EXPECT_EQ(inst.erase_invoke_count(), 0) << \"Erase should not have been invoked\";\n    EXPECT_EQ(inst.write_invoke_count(), 1) << \"Write should have been invoked once\";\n    EXPECT_EQ(inst.lock_invoke_count(), 1) << \"Lock should have been invoked once\";\n\n    test_val = 0;\n    wear_leveling_read(0x04, &test_val, sizeof(test_val));\n    EXPECT_EQ(test_val, 0x14) << \"Readback should come from cache regardless of unlock failure\";\n}\n"
  },
  {
    "path": "quantum/wear_leveling/wear_leveling.c",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include <stdbool.h>\n#include \"fnv.h\"\n#include \"wear_leveling.h\"\n#include \"wear_leveling_drivers.h\"\n#include \"wear_leveling_internal.h\"\n\n/*\n    This wear leveling algorithm is adapted from algorithms from previous\n    implementations in QMK, namely:\n        - Artur F. (http://engsta.com/stm32-flash-memory-eeprom-emulator/)\n        - Yiancar -- QMK's base implementation for STM32F303\n        - Ilya Zhuravlev -- initial wear leveling algorithm\n        - Don Kjer -- increased flash density algorithm\n        - Nick Brassel (@tzarc) -- decoupled for use on other peripherals\n\n    At this layer, it is assumed that any reads/writes from the backing store\n    have a \"reset state\" after erasure of zero.\n    It is up to the backing store to perform translation of values, such as\n    taking the complement in order to deal with flash memory's reset value.\n\n    Terminology:\n\n        - Backing store: this is the storage area used by the wear leveling\n            algorithm.\n\n        - Backing size: this is the amount of storage provided by the backing\n            store for use by the wear leveling algorithm.\n\n        - Backing write size: this is the minimum number of bytes the backing\n            store can write in a single operation.\n\n        - Logical data: this is the externally-visible \"emulated EEPROM\" that\n            external subsystems \"see\" when performing reads/writes.\n\n        - Logical size: this is the amount of storage available for use\n            externally. Effectively, the \"size of the EEPROM\".\n\n        - Write log: this is a section of the backing store used to keep track\n            of modifications without overwriting existing data. This log is\n            \"played back\" on startup such that any subsequent reads are capable\n            of returning the latest data.\n\n        - Consolidated data: this is a section of the backing store reserved for\n            use for the latest copy of logical data. This is only ever written\n            when the write log is full -- the latest values for the logical data\n            are written here and the write log is cleared.\n\n    Configurables:\n\n        - BACKING_STORE_WRITE_SIZE: The number of bytes requires for a write\n            operation. This is defined by the capabilities of the backing store.\n\n        - WEAR_LEVELING_BACKING_SIZE: The number of bytes provided by the\n            backing store for use by the wear leveling algorithm.  This is\n            defined by the capabilities of the backing store. This value must\n            also be at least twice the size of the logical size, as well as a\n            multiple of the logical size.\n\n        - WEAR_LEVELING_LOGICAL_SIZE: The number of bytes externally visible\n            to other subsystems performing reads/writes. This must be a multiple\n            of the write size.\n\n    General algorithm:\n\n        During initialization:\n            * The contents of the consolidated data section are read into cache.\n            * The contents of the write log are \"played back\" and update the\n                cache accordingly.\n\n        During reads:\n            * Logical data is served from the cache.\n\n        During writes:\n            * The cache is updated with the new data.\n            * A new write log entry is appended to the log.\n            * If the log's full, data is consolidated and the write log cleared.\n\n    Write log structure:\n\n        The first 8 bytes of the write log are a FNV1a_64 hash of the contents\n        of the consolidated data area, in an attempt to detect and guard against\n        any data corruption.\n\n        The write log follows the hash:\n\n        Given that the algorithm needs to cater for 2-, 4-, and 8-byte writes,\n        a variable-length write log entry is used such that the minimal amount\n        of storage is used based off the backing store write size.\n\n        Firstly, an empty log entry is expected to be all zeros. If the backing\n        store uses 0xFF for cleared bytes, it should return the complement, such\n        that this wear-leveling algorithm \"receives\" zeros.\n\n        For multi-byte writes, up to 8 bytes will be used for each log entry,\n        depending on the size of backing store writes:\n\n        ╔ Multi-byte Log Entry (2, 4-byte) ═╗\n        ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║\n        ║  └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║\n        ║  LenAdd║ Address║ Address║Value[0]║\n        ╚════════╩════════╩════════╩════════╝\n        ╔ Multi-byte Log Entry (2-byte) ══════════════════════╗\n        ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║BBBBBBBB║CCCCCCCC║\n        ║  └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║\n        ║  LenAdd║ Address║ Address║Value[0]║Value[1]║Value[2]║\n        ╚════════╩════════╩════════╩════════╩════════╩════════╝\n        ╔ Multi-byte Log Entry (2, 4, 8-byte) ══════════════════════════════════╗\n        ║00XXXYYY║YYYYYYYY║YYYYYYYY║AAAAAAAA║BBBBBBBB║CCCCCCCC║DDDDDDDD║EEEEEEEE║\n        ║  └┬┘└┬┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║└──┬───┘║\n        ║  LenAdd║ Address║ Address║Value[0]║Value[1]║Value[2]║Value[3]║Value[4]║\n        ╚════════╩════════╩════════╩════════╩════════╩════════╩════════╩════════╝\n\n        19 bits are used for the address, which allows for a max logical size of\n        512kB. Up to 5 bytes can be included in a single log entry.\n\n        For 2-byte backing store writes, the last two bytes are optional\n            depending on the length of data to be written. Accordingly, either 3\n            or 4 backing store write operations will occur.\n        For 4-byte backing store writes, either one or two write operations\n            occur, depending on the length.\n        For 8-byte backing store writes, one write operation occur.\n\n    2-byte backing store optimizations:\n\n        For single byte writes, addresses between 0...63 are encoded in a single\n        backing store write operation. 4- and 8-byte backing stores do not have\n        this optimization as it does not minimize the number of bytes written.\n\n        ╔ Byte-Entry ════╗\n        ║01XXXXXXYYYYYYYY║\n        ║  └─┬──┘└──┬───┘║\n        ║ Address Value  ║\n        ╚════════════════╝\n        0 <= Address < 0x40 (64)\n\n        A second optimization takes into account uint16_t writes of 0 or 1,\n        specifically catering for KC_NO and KC_TRANSPARENT in the dynamic keymap\n        subsystem. This is valid only for the first 16kB of logical data --\n        addresses outside this range will use the multi-byte encoding above.\n\n        ╔ U16-Encoded 0 ═╗\n        ║100XXXXXXXXXXXXX║\n        ║  │└─────┬─────┘║\n        ║  │Address >> 1 ║\n        ║  └── Value: 0  ║\n        ╚════════════════╝\n        0 <= Address <= 0x3FFE (16382)\n\n        ╔ U16-Encoded 1 ═╗\n        ║101XXXXXXXXXXXXX║\n        ║  │└─────┬─────┘║\n        ║  │Address >> 1 ║\n        ║  └── Value: 1  ║\n        ╚════════════════╝\n        0 <= Address <= 0x3FFE (16382) */\n\n/**\n * Storage area for the wear-leveling cache.\n */\nstatic struct __attribute__((__aligned__(BACKING_STORE_WRITE_SIZE))) {\n    __attribute__((__aligned__(BACKING_STORE_WRITE_SIZE))) uint8_t cache[(WEAR_LEVELING_LOGICAL_SIZE)];\n    uint32_t                                                       write_address;\n    bool                                                           unlocked;\n} wear_leveling;\n\n/**\n * Locking helper: status\n */\ntypedef enum backing_store_lock_status_t { STATUS_FAILURE = 0, STATUS_SUCCESS, STATUS_UNCHANGED } backing_store_lock_status_t;\n\n/**\n * Locking helper: unlock\n */\nstatic inline backing_store_lock_status_t wear_leveling_unlock(void) {\n    if (wear_leveling.unlocked) {\n        return STATUS_UNCHANGED;\n    }\n    if (!backing_store_unlock()) {\n        return STATUS_FAILURE;\n    }\n    wear_leveling.unlocked = true;\n    return STATUS_SUCCESS;\n}\n\n/**\n * Locking helper: lock\n */\nstatic inline backing_store_lock_status_t wear_leveling_lock(void) {\n    if (!wear_leveling.unlocked) {\n        return STATUS_UNCHANGED;\n    }\n    if (!backing_store_lock()) {\n        return STATUS_FAILURE;\n    }\n    wear_leveling.unlocked = false;\n    return STATUS_SUCCESS;\n}\n\n/**\n * Resets the cache, ensuring the write address is correctly initialised.\n */\nstatic void wear_leveling_clear_cache(void) {\n    memset(wear_leveling.cache, 0, (WEAR_LEVELING_LOGICAL_SIZE));\n    wear_leveling.write_address = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 is due to the FNV1a_64 of the consolidated buffer\n}\n\n/**\n * Reads the consolidated data from the backing store into the cache.\n * Does not consider the write log.\n */\nstatic wear_leveling_status_t wear_leveling_read_consolidated(void) {\n    wl_dprintf(\"Reading consolidated data\\n\");\n\n    wear_leveling_status_t status = WEAR_LEVELING_SUCCESS;\n    if (!backing_store_read_bulk(0, (backing_store_int_t *)wear_leveling.cache, sizeof(wear_leveling.cache) / sizeof(backing_store_int_t))) {\n        wl_dprintf(\"Failed to read from backing store\\n\");\n        status = WEAR_LEVELING_FAILED;\n    }\n\n    // Verify the FNV1a_64 result\n    if (status != WEAR_LEVELING_FAILED) {\n        uint64_t          expected = fnv_64a_buf(wear_leveling.cache, (WEAR_LEVELING_LOGICAL_SIZE), FNV1A_64_INIT);\n        write_log_entry_t entry;\n        wl_dprintf(\"Reading checksum\\n\");\n#if BACKING_STORE_WRITE_SIZE == 2\n        backing_store_read_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw16, 4);\n#elif BACKING_STORE_WRITE_SIZE == 4\n        backing_store_read_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw32, 2);\n#elif BACKING_STORE_WRITE_SIZE == 8\n        backing_store_read((WEAR_LEVELING_LOGICAL_SIZE) + 0, &entry.raw64);\n#endif\n        // If we have a mismatch, clear the cache but do not flag a failure,\n        // which will cater for the completely clean MCU case.\n        if (entry.raw64 == expected) {\n            wl_dprintf(\"Checksum matches, consolidated data is correct\\n\");\n        } else {\n            wl_dprintf(\"Checksum mismatch, clearing cache\\n\");\n            wear_leveling_clear_cache();\n        }\n    }\n\n    // If we failed for any reason, then clear the cache\n    if (status == WEAR_LEVELING_FAILED) {\n        wear_leveling_clear_cache();\n    }\n\n    return status;\n}\n\n/**\n * Writes the current cache to consolidated data at the beginning of the backing store.\n * Does not clear the write log.\n * Pre-condition: this is just after an erase, so we can write directly without reading.\n */\nstatic wear_leveling_status_t wear_leveling_write_consolidated(void) {\n    wl_dprintf(\"Writing consolidated data\\n\");\n\n    backing_store_lock_status_t lock_status = wear_leveling_unlock();\n    wear_leveling_status_t      status      = WEAR_LEVELING_CONSOLIDATED;\n    if (!backing_store_write_bulk(0, (backing_store_int_t *)wear_leveling.cache, sizeof(wear_leveling.cache) / sizeof(backing_store_int_t))) {\n        wl_dprintf(\"Failed to write to backing store\\n\");\n        status = WEAR_LEVELING_FAILED;\n    }\n\n    if (status != WEAR_LEVELING_FAILED) {\n        // Write out the FNV1a_64 result of the consolidated data\n        write_log_entry_t entry;\n        entry.raw64 = fnv_64a_buf(wear_leveling.cache, (WEAR_LEVELING_LOGICAL_SIZE), FNV1A_64_INIT);\n        wl_dprintf(\"Writing checksum\\n\");\n        do {\n#if BACKING_STORE_WRITE_SIZE == 2\n            if (!backing_store_write_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw16, 4)) {\n                status = WEAR_LEVELING_FAILED;\n                break;\n            }\n#elif BACKING_STORE_WRITE_SIZE == 4\n            if (!backing_store_write_bulk((WEAR_LEVELING_LOGICAL_SIZE), entry.raw32, 2)) {\n                status = WEAR_LEVELING_FAILED;\n                break;\n            }\n#elif BACKING_STORE_WRITE_SIZE == 8\n            if (!backing_store_write((WEAR_LEVELING_LOGICAL_SIZE), entry.raw64)) {\n                status = WEAR_LEVELING_FAILED;\n                break;\n            }\n#endif\n        } while (0);\n    }\n\n    if (lock_status == STATUS_SUCCESS) {\n        wear_leveling_lock();\n    }\n    return status;\n}\n\n/**\n * Forces a write of the current cache.\n * Erases the backing store, including the write log.\n * During this operation, there is the potential for data loss if a power loss occurs.\n */\nstatic wear_leveling_status_t wear_leveling_consolidate_force(void) {\n    wl_dprintf(\"Erasing backing store\\n\");\n\n    // Erase the backing store. Expectation is that any un-written values that are read back after this call come back as zero.\n    bool ok = backing_store_erase();\n    if (!ok) {\n        wl_dprintf(\"Failed to erase backing store\\n\");\n        return WEAR_LEVELING_FAILED;\n    }\n\n    // Write the cache to the first section of the backing store.\n    wear_leveling_status_t status = wear_leveling_write_consolidated();\n    if (status == WEAR_LEVELING_FAILED) {\n        wl_dprintf(\"Failed to write consolidated data\\n\");\n    }\n\n    // Next write of the log occurs after the consolidated values at the start of the backing store.\n    wear_leveling.write_address = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 due to the FNV1a_64 of the consolidated area\n\n    return status;\n}\n\n/**\n * Potential write of the current cache to the backing store.\n * Skipped if the current write log position is not at the end of the backing store.\n * During this operation, there is the potential for data loss if a power loss occurs.\n *\n * @return true if consolidation occurred\n */\nstatic wear_leveling_status_t wear_leveling_consolidate_if_needed(void) {\n    if (wear_leveling.write_address >= (WEAR_LEVELING_BACKING_SIZE)) {\n        return wear_leveling_consolidate_force();\n    }\n\n    return WEAR_LEVELING_SUCCESS;\n}\n\n/**\n * Appends the supplied fixed-width entry to the write log, optionally consolidating if the log is full.\n *\n * @return true if consolidation occurred\n */\nstatic wear_leveling_status_t wear_leveling_append_raw(backing_store_int_t value) {\n    bool ok = backing_store_write(wear_leveling.write_address, value);\n    if (!ok) {\n        wl_dprintf(\"Failed to write to backing store\\n\");\n        return WEAR_LEVELING_FAILED;\n    }\n    wear_leveling.write_address += (BACKING_STORE_WRITE_SIZE);\n    return wear_leveling_consolidate_if_needed();\n}\n\n/**\n * Handles writing multi_byte-encoded data to the backing store.\n *\n * @return true if consolidation occurred\n */\nstatic wear_leveling_status_t wear_leveling_write_raw_multibyte(uint32_t address, const void *value, size_t length) {\n    const uint8_t *   p   = value;\n    write_log_entry_t log = LOG_ENTRY_MAKE_MULTIBYTE(address, length);\n    for (size_t i = 0; i < length; ++i) {\n        log.raw8[3 + i] = p[i];\n    }\n\n    // Write to the backing store. See the multi-byte log format in the documentation header at the top of the file.\n    wear_leveling_status_t status;\n#if BACKING_STORE_WRITE_SIZE == 2\n    status = wear_leveling_append_raw(log.raw16[0]);\n    if (status != WEAR_LEVELING_SUCCESS) {\n        return status;\n    }\n\n    status = wear_leveling_append_raw(log.raw16[1]);\n    if (status != WEAR_LEVELING_SUCCESS) {\n        return status;\n    }\n\n    if (length > 1) {\n        status = wear_leveling_append_raw(log.raw16[2]);\n        if (status != WEAR_LEVELING_SUCCESS) {\n            return status;\n        }\n    }\n\n    if (length > 3) {\n        status = wear_leveling_append_raw(log.raw16[3]);\n        if (status != WEAR_LEVELING_SUCCESS) {\n            return status;\n        }\n    }\n#elif BACKING_STORE_WRITE_SIZE == 4\n    status = wear_leveling_append_raw(log.raw32[0]);\n    if (status != WEAR_LEVELING_SUCCESS) {\n        return status;\n    }\n\n    if (length > 1) {\n        status = wear_leveling_append_raw(log.raw32[1]);\n        if (status != WEAR_LEVELING_SUCCESS) {\n            return status;\n        }\n    }\n#elif BACKING_STORE_WRITE_SIZE == 8\n    status = wear_leveling_append_raw(log.raw64);\n    if (status != WEAR_LEVELING_SUCCESS) {\n        return status;\n    }\n#endif\n    return status;\n}\n\n/**\n * Handles the actual writing of logical data into the write log section of the backing store.\n */\nstatic wear_leveling_status_t wear_leveling_write_raw(uint32_t address, const void *value, size_t length) {\n    const uint8_t *        p         = value;\n    size_t                 remaining = length;\n    wear_leveling_status_t status    = WEAR_LEVELING_SUCCESS;\n    while (remaining > 0) {\n#if BACKING_STORE_WRITE_SIZE == 2\n        // Small-write optimizations - uint16_t, 0 or 1, address is even, address <16384:\n        if (remaining >= 2 && address % 2 == 0 && address < 16384) {\n            const uint16_t v = ((uint16_t)p[1]) << 8 | p[0]; // don't just dereference a uint16_t here -- if unaligned it generates faults on some MCUs\n            if (v == 0 || v == 1) {\n                const write_log_entry_t log = LOG_ENTRY_MAKE_WORD_01(address, v);\n                status                      = wear_leveling_append_raw(log.raw16[0]);\n                if (status != WEAR_LEVELING_SUCCESS) {\n                    // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue.\n                    // If a failure occurred, pass it on.\n                    return status;\n                }\n\n                remaining -= 2;\n                address += 2;\n                p += 2;\n                continue;\n            }\n        }\n\n        // Small-write optimizations - address<64:\n        if (address < 64) {\n            const write_log_entry_t log = LOG_ENTRY_MAKE_OPTIMIZED_64(address, *p);\n            status                      = wear_leveling_append_raw(log.raw16[0]);\n            if (status != WEAR_LEVELING_SUCCESS) {\n                // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue.\n                // If a failure occurred, pass it on.\n                return status;\n            }\n\n            remaining--;\n            address++;\n            p++;\n            continue;\n        }\n#endif // BACKING_STORE_WRITE_SIZE == 2\n        const size_t this_length = remaining >= LOG_ENTRY_MULTIBYTE_MAX_BYTES ? LOG_ENTRY_MULTIBYTE_MAX_BYTES : remaining;\n        status                   = wear_leveling_write_raw_multibyte(address, p, this_length);\n        if (status != WEAR_LEVELING_SUCCESS) {\n            // If consolidation occurred, then the cache has already been written to the consolidated area. No need to continue.\n            // If a failure occurred, pass it on.\n            return status;\n        }\n        remaining -= this_length;\n        address += (uint32_t)this_length;\n        p += this_length;\n    }\n\n    return status;\n}\n\n/**\n * \"Replays\" the write log from the backing store, updating the local cache with updated values.\n */\nstatic wear_leveling_status_t wear_leveling_playback_log(void) {\n    wl_dprintf(\"Playback write log\\n\");\n\n    wear_leveling_status_t status          = WEAR_LEVELING_SUCCESS;\n    bool                   cancel_playback = false;\n    uint32_t               address         = (WEAR_LEVELING_LOGICAL_SIZE) + 8; // +8 due to the FNV1a_64 of the consolidated area\n    while (!cancel_playback && address < (WEAR_LEVELING_BACKING_SIZE)) {\n        backing_store_int_t value;\n        bool                ok = backing_store_read(address, &value);\n        if (!ok) {\n            wl_dprintf(\"Failed to load from backing store, skipping playback of write log\\n\");\n            cancel_playback = true;\n            status          = WEAR_LEVELING_FAILED;\n            break;\n        }\n        if (value == 0) {\n            wl_dprintf(\"Found empty slot, no more log entries\\n\");\n            cancel_playback = true;\n            break;\n        }\n\n        // If we got a nonzero value, then we need to increment the address to ensure next write occurs at next location\n        address += (BACKING_STORE_WRITE_SIZE);\n\n        // Read from the write log\n        write_log_entry_t log;\n#if BACKING_STORE_WRITE_SIZE == 2\n        log.raw16[0] = value;\n#elif BACKING_STORE_WRITE_SIZE == 4\n        log.raw32[0] = value;\n#elif BACKING_STORE_WRITE_SIZE == 8\n        log.raw64 = value;\n#endif\n\n        switch (LOG_ENTRY_GET_TYPE(log)) {\n            case LOG_ENTRY_TYPE_MULTIBYTE: {\n#if BACKING_STORE_WRITE_SIZE == 2\n                ok = backing_store_read(address, &log.raw16[1]);\n                if (!ok) {\n                    wl_dprintf(\"Failed to load from backing store, skipping playback of write log\\n\");\n                    cancel_playback = true;\n                    status          = WEAR_LEVELING_FAILED;\n                    break;\n                }\n                address += (BACKING_STORE_WRITE_SIZE);\n#endif // BACKING_STORE_WRITE_SIZE == 2\n                const uint32_t a = LOG_ENTRY_MULTIBYTE_GET_ADDRESS(log);\n                const uint8_t  l = LOG_ENTRY_MULTIBYTE_GET_LENGTH(log);\n\n                if (a + l > (WEAR_LEVELING_LOGICAL_SIZE)) {\n                    cancel_playback = true;\n                    status          = WEAR_LEVELING_FAILED;\n                    break;\n                }\n\n#if BACKING_STORE_WRITE_SIZE == 2\n                if (l > 1) {\n                    ok = backing_store_read(address, &log.raw16[2]);\n                    if (!ok) {\n                        wl_dprintf(\"Failed to load from backing store, skipping playback of write log\\n\");\n                        cancel_playback = true;\n                        status          = WEAR_LEVELING_FAILED;\n                        break;\n                    }\n                    address += (BACKING_STORE_WRITE_SIZE);\n                }\n                if (l > 3) {\n                    ok = backing_store_read(address, &log.raw16[3]);\n                    if (!ok) {\n                        wl_dprintf(\"Failed to load from backing store, skipping playback of write log\\n\");\n                        cancel_playback = true;\n                        status          = WEAR_LEVELING_FAILED;\n                        break;\n                    }\n                    address += (BACKING_STORE_WRITE_SIZE);\n                }\n#elif BACKING_STORE_WRITE_SIZE == 4\n                if (l > 1) {\n                    ok = backing_store_read(address, &log.raw32[1]);\n                    if (!ok) {\n                        wl_dprintf(\"Failed to load from backing store, skipping playback of write log\\n\");\n                        cancel_playback = true;\n                        status = WEAR_LEVELING_FAILED;\n                        break;\n                    }\n                    address += (BACKING_STORE_WRITE_SIZE);\n                }\n#endif\n\n                memcpy(&wear_leveling.cache[a], &log.raw8[3], l);\n            } break;\n#if BACKING_STORE_WRITE_SIZE == 2\n            case LOG_ENTRY_TYPE_OPTIMIZED_64: {\n                const uint32_t a = LOG_ENTRY_OPTIMIZED_64_GET_ADDRESS(log);\n                const uint8_t  v = LOG_ENTRY_OPTIMIZED_64_GET_VALUE(log);\n\n                if (a >= (WEAR_LEVELING_LOGICAL_SIZE)) {\n                    cancel_playback = true;\n                    status          = WEAR_LEVELING_FAILED;\n                    break;\n                }\n\n                wear_leveling.cache[a] = v;\n            } break;\n            case LOG_ENTRY_TYPE_WORD_01: {\n                const uint32_t a = LOG_ENTRY_WORD_01_GET_ADDRESS(log);\n                const uint8_t  v = LOG_ENTRY_WORD_01_GET_VALUE(log);\n\n                if (a + 1 >= (WEAR_LEVELING_LOGICAL_SIZE)) {\n                    cancel_playback = true;\n                    status          = WEAR_LEVELING_FAILED;\n                    break;\n                }\n\n                wear_leveling.cache[a + 0] = v;\n                wear_leveling.cache[a + 1] = 0;\n            } break;\n#endif // BACKING_STORE_WRITE_SIZE == 2\n            default: {\n                cancel_playback = true;\n                status          = WEAR_LEVELING_FAILED;\n            } break;\n        }\n    }\n\n    // We've reached the end of the log, so we're at the new write location\n    wear_leveling.write_address = address;\n\n    if (status == WEAR_LEVELING_FAILED) {\n        // If we had a failure during readback, assume we're corrupted -- force a consolidation with the data we already have\n        status = wear_leveling_consolidate_force();\n    } else {\n        // Consolidate the cache + write log if required\n        status = wear_leveling_consolidate_if_needed();\n    }\n\n    return status;\n}\n\n/**\n * Wear-leveling initialization\n */\nwear_leveling_status_t wear_leveling_init(void) {\n    wl_dprintf(\"Init\\n\");\n\n    // Reset the cache\n    wear_leveling_clear_cache();\n\n    // Initialise the backing store\n    if (!backing_store_init()) {\n        // If it failed, clear the cache and return with failure\n        wear_leveling_clear_cache();\n        return WEAR_LEVELING_FAILED;\n    }\n\n    // Read the previous consolidated values, then replay the existing write log so that the cache has the \"live\" values\n    wear_leveling_status_t status = wear_leveling_read_consolidated();\n    if (status == WEAR_LEVELING_FAILED) {\n        // If it failed, clear the cache and return with failure\n        wear_leveling_clear_cache();\n        return status;\n    }\n\n    status = wear_leveling_playback_log();\n    if (status == WEAR_LEVELING_FAILED) {\n        // If it failed, clear the cache and return with failure\n        wear_leveling_clear_cache();\n        return status;\n    }\n\n    return status;\n}\n\n/**\n * Wear-leveling erase.\n * Post-condition: any reads from the backing store directly after an erase operation must come back as zero.\n */\nwear_leveling_status_t wear_leveling_erase(void) {\n    wl_dprintf(\"Erase\\n\");\n\n    // Unlock the backing store\n    backing_store_lock_status_t lock_status = wear_leveling_unlock();\n    if (lock_status == STATUS_FAILURE) {\n        wear_leveling_lock();\n        return WEAR_LEVELING_FAILED;\n    }\n\n    // Perform the erase\n    bool ret = backing_store_erase();\n    wear_leveling_clear_cache();\n\n    // Lock the backing store if we acquired the lock successfully\n    if (lock_status == STATUS_SUCCESS) {\n        ret &= (wear_leveling_lock() != STATUS_FAILURE);\n    }\n\n    return ret ? WEAR_LEVELING_SUCCESS : WEAR_LEVELING_FAILED;\n}\n\n/**\n * Writes logical data into the backing store. Skips writes if there are no changes to values.\n */\nwear_leveling_status_t wear_leveling_write(const uint32_t address, const void *value, size_t length) {\n    wl_assert(address + length <= (WEAR_LEVELING_LOGICAL_SIZE));\n    if (address + length > (WEAR_LEVELING_LOGICAL_SIZE)) {\n        return WEAR_LEVELING_FAILED;\n    }\n\n    wl_dprintf(\"Write \");\n    wl_dump(address, value, length);\n\n    // Skip write if there's no change compared to the current cached value\n    if (memcmp(value, &wear_leveling.cache[address], length) == 0) {\n        return true;\n    }\n\n    // Update the cache before writing to the backing store -- if we hit the end of the backing store during writes to the log then we'll force a consolidation in-line\n    memcpy(&wear_leveling.cache[address], value, length);\n\n    // Unlock the backing store\n    backing_store_lock_status_t lock_status = wear_leveling_unlock();\n    if (lock_status == STATUS_FAILURE) {\n        wear_leveling_lock();\n        return WEAR_LEVELING_FAILED;\n    }\n\n    // Perform the actual write\n    wear_leveling_status_t status = wear_leveling_write_raw(address, value, length);\n    switch (status) {\n        case WEAR_LEVELING_CONSOLIDATED:\n        case WEAR_LEVELING_FAILED:\n            // If the write triggered consolidation, or the write failed, then nothing else needs to occur.\n            break;\n\n        case WEAR_LEVELING_SUCCESS:\n            // Consolidate the cache + write log if required\n            status = wear_leveling_consolidate_if_needed();\n            break;\n\n        default:\n            // Unsure how we'd get here...\n            status = WEAR_LEVELING_FAILED;\n            break;\n    }\n\n    if (lock_status == STATUS_SUCCESS) {\n        if (wear_leveling_lock() == STATUS_FAILURE) {\n            status = WEAR_LEVELING_FAILED;\n        }\n    }\n\n    return status;\n}\n\n/**\n * Reads logical data from the cache.\n */\nwear_leveling_status_t wear_leveling_read(const uint32_t address, void *value, size_t length) {\n    wl_assert(address + length <= (WEAR_LEVELING_LOGICAL_SIZE));\n    if (address + length > (WEAR_LEVELING_LOGICAL_SIZE)) {\n        return WEAR_LEVELING_FAILED;\n    }\n\n    // Only need to copy from the cache\n    memcpy(value, &wear_leveling.cache[address], length);\n\n    wl_dprintf(\"Read  \");\n    wl_dump(address, value, length);\n    return WEAR_LEVELING_SUCCESS;\n}\n\n/**\n * Weak implementation of bulk read, drivers can implement more optimised implementations.\n */\n__attribute__((weak)) bool backing_store_read_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {\n    for (size_t i = 0; i < item_count; ++i) {\n        if (!backing_store_read(address + (i * BACKING_STORE_WRITE_SIZE), &values[i])) {\n            return false;\n        }\n    }\n    return true;\n}\n\n/**\n * Weak implementation of bulk write, drivers can implement more optimised implementations.\n */\n__attribute__((weak)) bool backing_store_write_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) {\n    for (size_t i = 0; i < item_count; ++i) {\n        if (!backing_store_write(address + (i * BACKING_STORE_WRITE_SIZE), values[i])) {\n            return false;\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "quantum/wear_leveling/wear_leveling.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n#include <stdint.h>\n#include <stdlib.h>\n\n/**\n * @typedef Status returned from any wear-leveling API.\n */\ntypedef enum wear_leveling_status_t {\n    WEAR_LEVELING_FAILED,      //< Invocation failed\n    WEAR_LEVELING_SUCCESS,     //< Invocation succeeded\n    WEAR_LEVELING_CONSOLIDATED //< Invocation succeeded, consolidation occurred\n} wear_leveling_status_t;\n\n/**\n * Wear-leveling initialization\n *\n * @return Status of the request\n */\nwear_leveling_status_t wear_leveling_init(void);\n\n/**\n * Wear-leveling erasure.\n *\n * Clears the wear-leveling area, with the definition that the \"reset state\" of all data is zero.\n *\n * @return Status of the request\n */\nwear_leveling_status_t wear_leveling_erase(void);\n\n/**\n * Writes logical data into the backing store.\n *\n * Skips writes if there are no changes to written values. The entire written block is considered when attempting to\n * determine if an overwrite should occur -- if there is any data mismatch the entire block will be written to the log,\n * not just the changed bytes.\n *\n * @param address[in] the logical address to write data\n * @param value[in] pointer to the source buffer\n * @param length[in] length of the data\n * @return Status of the request\n */\nwear_leveling_status_t wear_leveling_write(uint32_t address, const void* value, size_t length);\n\n/**\n * Reads logical data from the cache.\n *\n * @param address[in] the logical address to read data\n * @param value[out] pointer to the destination buffer\n * @param length[in] length of the data\n * @return Status of the request\n */\nwear_leveling_status_t wear_leveling_read(uint32_t address, void* value, size_t length);\n"
  },
  {
    "path": "quantum/wear_leveling/wear_leveling_drivers.h",
    "content": "// Copyright 2025 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#if defined(WEAR_LEVELING_EMBEDDED_FLASH)\n#    include \"wear_leveling/wear_leveling_efl_config.h\"\n#elif defined(WEAR_LEVELING_SPI_FLASH)\n#    include \"wear_leveling/wear_leveling_flash_spi_config.h\"\n#elif defined(WEAR_LEVELING_RP2040_FLASH)\n#    include \"wear_leveling/wear_leveling_rp2040_flash_config.h\"\n#elif defined(WEAR_LEVELING_LEGACY)\n#    include \"wear_leveling/wear_leveling_legacy_config.h\"\n#endif\n"
  },
  {
    "path": "quantum/wear_leveling/wear_leveling_internal.h",
    "content": "// Copyright 2022 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#pragma once\n\n#include \"compiler_support.h\"\n\n#include <stdint.h>\n#include <string.h>\n\n#if BACKING_STORE_WRITE_SIZE == 2\ntypedef uint16_t backing_store_int_t;\n#elif BACKING_STORE_WRITE_SIZE == 4\ntypedef uint32_t backing_store_int_t;\n#elif BACKING_STORE_WRITE_SIZE == 8\ntypedef uint64_t backing_store_int_t;\n#else\n#    error Invalid BACKING_STORE_WRITE_SIZE, needs to be 2/4/8.\n#endif\n\n#ifndef WEAR_LEVELING_BACKING_SIZE\n#    error WEAR_LEVELING_BACKING_SIZE was not set.\n#endif\n\n#ifndef WEAR_LEVELING_LOGICAL_SIZE\n#    error WEAR_LEVELING_LOGICAL_SIZE was not set.\n#endif\n\n#ifdef WEAR_LEVELING_DEBUG_OUTPUT\n#    include <debug.h>\n#    define bs_dprintf(...) dprintf(\"Backing store: \" __VA_ARGS__)\n#    define wl_dprintf(...) dprintf(\"Wear leveling: \" __VA_ARGS__)\n#    define wl_dump(address, value, length)             \\\n        do {                                            \\\n            dprintf(\"[0x%04X]: \", (int)(address));      \\\n            const uint8_t* p = (const uint8_t*)(value); \\\n            for (int i = 0; i < (length); ++i) {        \\\n                dprintf(\" %02X\", (int)p[i]);            \\\n            }                                           \\\n            dprintf(\"\\n\");                              \\\n        } while (0)\n#else\n#    define wl_dprintf(...) \\\n        do {                \\\n        } while (0)\n#    define bs_dprintf(...) \\\n        do {                \\\n        } while (0)\n#    define wl_dump(...) \\\n        do {             \\\n        } while (0)\n#endif // WEAR_LEVELING_DEBUG_OUTPUT\n\n#ifdef WEAR_LEVELING_ASSERTS\n#    include <assert.h>\n#    define wl_assert(...) assert(__VA_ARGS__)\n#else\n#    define wl_assert(...) \\\n        do {               \\\n        } while (0)\n#endif // WEAR_LEVELING_ASSERTS\n\n// Compile-time validation of configurable options\nSTATIC_ASSERT(WEAR_LEVELING_BACKING_SIZE >= (WEAR_LEVELING_LOGICAL_SIZE * 2), \"Total backing size must be at least twice the size of the logical size\");\nSTATIC_ASSERT(WEAR_LEVELING_LOGICAL_SIZE % BACKING_STORE_WRITE_SIZE == 0, \"Logical size must be a multiple of write size\");\nSTATIC_ASSERT(WEAR_LEVELING_BACKING_SIZE % WEAR_LEVELING_LOGICAL_SIZE == 0, \"Backing size must be a multiple of logical size\");\n\n// Backing Store API, to be implemented elsewhere by flash driver etc.\nbool backing_store_init(void);\nbool backing_store_unlock(void);\nbool backing_store_erase(void);\nbool backing_store_write(uint32_t address, backing_store_int_t value);\nbool backing_store_write_bulk(uint32_t address, backing_store_int_t* values, size_t item_count); // weak implementation already provided, optimized implementation can be implemented by driver\nbool backing_store_lock(void);\nbool backing_store_read(uint32_t address, backing_store_int_t* value);\nbool backing_store_read_bulk(uint32_t address, backing_store_int_t* values, size_t item_count); // weak implementation already provided, optimized implementation can be implemented by driver\n\n/**\n * Helper type used to contain a write log entry.\n */\ntypedef union write_log_entry_t {\n    uint64_t raw64;\n    uint32_t raw32[2];\n    uint16_t raw16[4];\n    uint8_t  raw8[8];\n} write_log_entry_t;\n\nSTATIC_ASSERT(sizeof(write_log_entry_t) == 8, \"Wear leveling write log entry size was not 8\");\n\n/**\n * Log entry type discriminator.\n */\nenum {\n    // 0x00 -- Multi-byte storage type\n    LOG_ENTRY_TYPE_MULTIBYTE,\n\n    // 0x01 -- 2-byte backing store write optimization: address < 64\n    LOG_ENTRY_TYPE_OPTIMIZED_64,\n\n    // 0x02 -- 2-byte backing store write optimization: word-encoded 0/1 values\n    LOG_ENTRY_TYPE_WORD_01,\n\n    LOG_ENTRY_TYPES\n};\n\nSTATIC_ASSERT(LOG_ENTRY_TYPES <= (1 << 2), \"Too many log entry types to fit into 2 bits of storage\");\n\n#define BITMASK_FOR_BITCOUNT(n) ((1 << (n)) - 1)\n\n#define LOG_ENTRY_GET_TYPE(entry) (((entry).raw8[0] >> 6) & BITMASK_FOR_BITCOUNT(2))\n\n#define LOG_ENTRY_MULTIBYTE_MAX_BYTES 5\n#define LOG_ENTRY_MULTIBYTE_GET_ADDRESS(entry) (((((uint32_t)((entry).raw8[0])) & BITMASK_FOR_BITCOUNT(3)) << 16) | (((uint32_t)((entry).raw8[1])) << 8) | (entry).raw8[2])\n#define LOG_ENTRY_MULTIBYTE_GET_LENGTH(entry) ((uint8_t)(((entry).raw8[0] >> 3) & BITMASK_FOR_BITCOUNT(3)))\n#define LOG_ENTRY_MAKE_MULTIBYTE(address, length)                                                       \\\n    (write_log_entry_t) {                                                                               \\\n        .raw8 = {                                                                                       \\\n            [0] = (((((uint8_t)LOG_ENTRY_TYPE_MULTIBYTE) & BITMASK_FOR_BITCOUNT(2)) << 6) /* type */    \\\n                   | ((((uint8_t)(length)) & BITMASK_FOR_BITCOUNT(3)) << 3)               /* length */  \\\n                   | ((((uint8_t)((address) >> 16))) & BITMASK_FOR_BITCOUNT(3))           /* address */ \\\n                   ),                                                                                   \\\n            [1] = (((uint8_t)((address) >> 8)) & BITMASK_FOR_BITCOUNT(8)), /* address */                \\\n            [2] = (((uint8_t)(address)) & BITMASK_FOR_BITCOUNT(8)),        /* address */                \\\n        }                                                                                               \\\n    }\n\n#define LOG_ENTRY_OPTIMIZED_64_GET_ADDRESS(entry) ((uint32_t)((entry).raw8[0] & BITMASK_FOR_BITCOUNT(6)))\n#define LOG_ENTRY_OPTIMIZED_64_GET_VALUE(entry) ((entry).raw8[1])\n#define LOG_ENTRY_MAKE_OPTIMIZED_64(address, value)                                                        \\\n    (write_log_entry_t) {                                                                                  \\\n        .raw8 = {                                                                                          \\\n            [0] = (((((uint8_t)LOG_ENTRY_TYPE_OPTIMIZED_64) & BITMASK_FOR_BITCOUNT(2)) << 6) /* type */    \\\n                   | ((((uint8_t)(address))) & BITMASK_FOR_BITCOUNT(6))                      /* address */ \\\n                   ),                                                                                      \\\n            [1] = ((uint8_t)(value)), /* value */                                                          \\\n        }                                                                                                  \\\n    }\n\n#define LOG_ENTRY_WORD_01_GET_ADDRESS(entry) ((((uint32_t)(((entry).raw8[0]) & BITMASK_FOR_BITCOUNT(5))) << 9) | (((uint32_t)((entry).raw8[1])) << 1))\n#define LOG_ENTRY_WORD_01_GET_VALUE(entry) ((uint8_t)((entry).raw8[0] >> 5) & BITMASK_FOR_BITCOUNT(1))\n#define LOG_ENTRY_MAKE_WORD_01(address, value)                                                        \\\n    (write_log_entry_t) {                                                                             \\\n        .raw8 = {                                                                                     \\\n            [0] = (((((uint8_t)LOG_ENTRY_TYPE_WORD_01) & BITMASK_FOR_BITCOUNT(2)) << 6) /* type */    \\\n                   | (((((uint8_t)((value) ? 1 : 0))) & BITMASK_FOR_BITCOUNT(1)) << 5)  /* value */   \\\n                   | ((((uint8_t)((address) >> 9))) & BITMASK_FOR_BITCOUNT(5))          /* address */ \\\n                   ),                                                                                 \\\n            [1] = (uint8_t)((address) >> 1), /* address */                                            \\\n        }                                                                                             \\\n    }\n"
  },
  {
    "path": "quantum/wpm.c",
    "content": "/*\n * Copyright 2020 Richard Sutherland (rich@brickbots.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"wpm.h\"\n#include \"timer.h\"\n#include \"keycode.h\"\n#include \"quantum_keycodes.h\"\n#include \"action_util.h\"\n#include <math.h>\n\n// WPM Stuff\nstatic uint8_t  current_wpm = 0;\nstatic uint32_t wpm_timer   = 0;\n\n/* The WPM calculation works by specifying a certain number of 'periods' inside\n * a ring buffer, and we count the number of keypresses which occur in each of\n * those periods.  Then to calculate WPM, we add up all of the keypresses in\n * the whole ring buffer, divide by the number of keypresses in a 'word', and\n * then adjust for how much time is captured by our ring buffer.  The size\n * of the ring buffer can be configured using the keymap configuration\n * value `WPM_SAMPLE_PERIODS`.\n *\n */\n#define MAX_PERIODS (WPM_SAMPLE_PERIODS)\n#define PERIOD_DURATION (1000 * WPM_SAMPLE_SECONDS / MAX_PERIODS)\n\nstatic int16_t period_presses[MAX_PERIODS] = {0};\nstatic uint8_t current_period              = 0;\nstatic uint8_t periods                     = 1;\n\n#if !defined(WPM_UNFILTERED)\n/* LATENCY is used as part of filtering, and controls how quickly the reported\n * WPM trails behind our actual instantaneous measured WPM value, and is\n * defined in milliseconds.  So for LATENCY == 100, the displayed WPM is\n * smoothed out over periods of 0.1 seconds.  This results in a nice,\n * smoothly-moving reported WPM value which nevertheless is never more than\n * 0.1 seconds behind the typist's actual current WPM.\n *\n * LATENCY is not used if WPM_UNFILTERED is defined.\n */\n#    define LATENCY (100)\nstatic uint32_t smoothing_timer = 0;\nstatic uint8_t  prev_wpm        = 0;\nstatic uint8_t  next_wpm        = 0;\n#endif\n\nvoid set_current_wpm(uint8_t new_wpm) {\n    current_wpm = new_wpm;\n}\nuint8_t get_current_wpm(void) {\n    return current_wpm;\n}\n\nbool wpm_keycode(uint16_t keycode) {\n    return wpm_keycode_kb(keycode);\n}\n\n__attribute__((weak)) bool wpm_keycode_kb(uint16_t keycode) {\n    return wpm_keycode_user(keycode);\n}\n\n__attribute__((weak)) bool wpm_keycode_user(uint16_t keycode) {\n    if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX) || (keycode >= QK_MODS && keycode <= QK_MODS_MAX)) {\n        keycode = keycode & 0xFF;\n    } else if (keycode > 0xFF) {\n        keycode = 0;\n    }\n    if ((keycode >= KC_A && keycode <= KC_0) || (keycode >= KC_TAB && keycode <= KC_SLASH)) {\n        return true;\n    }\n\n    return false;\n}\n\n#if defined(WPM_ALLOW_COUNT_REGRESSION)\n__attribute__((weak)) uint8_t wpm_regress_count(uint16_t keycode) {\n    bool weak_modded = (keycode >= QK_LCTL && keycode < QK_LSFT) || (keycode >= QK_RCTL && keycode < QK_RSFT);\n\n    if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX) || (keycode >= QK_MODS && keycode <= QK_MODS_MAX)) {\n        keycode = keycode & 0xFF;\n    } else if (keycode > 0xFF) {\n        keycode = 0;\n    }\n    if (keycode == KC_DELETE || keycode == KC_BACKSPACE) {\n        if (((get_mods() | get_oneshot_mods()) & MOD_MASK_CTRL) || weak_modded) {\n            return WPM_ESTIMATED_WORD_SIZE;\n        } else {\n            return 1;\n        }\n    } else {\n        return 0;\n    }\n}\n#endif\n\n// Outside 'raw' mode we smooth results over time.\n\nvoid update_wpm(uint16_t keycode) {\n    if (wpm_keycode(keycode) && period_presses[current_period] < INT16_MAX) {\n        period_presses[current_period]++;\n    }\n#if defined(WPM_ALLOW_COUNT_REGRESSION)\n    uint8_t regress = wpm_regress_count(keycode);\n    if (regress && period_presses[current_period] > INT16_MIN) {\n        period_presses[current_period]--;\n    }\n#endif\n}\n\nvoid decay_wpm(void) {\n    int32_t presses = period_presses[0];\n    for (int i = 1; i <= periods; i++) {\n        presses += period_presses[i];\n    }\n    if (presses < 0) {\n        presses = 0;\n    }\n    int32_t  elapsed  = timer_elapsed32(wpm_timer);\n    uint32_t duration = (((periods)*PERIOD_DURATION) + elapsed);\n    int32_t  wpm_now  = (60000 * presses) / (duration * WPM_ESTIMATED_WORD_SIZE);\n\n    if (wpm_now < 0) // set some reasonable WPM measurement limits\n        wpm_now = 0;\n    if (wpm_now > 240) wpm_now = 240;\n\n    if (elapsed > PERIOD_DURATION) {\n        current_period                 = (current_period + 1) % MAX_PERIODS;\n        period_presses[current_period] = 0;\n        periods                        = (periods < MAX_PERIODS - 1) ? periods + 1 : MAX_PERIODS - 1;\n        elapsed                        = 0;\n        wpm_timer                      = timer_read32();\n    }\n    if (presses < 2) // don't guess high WPM based on a single keypress.\n        wpm_now = 0;\n\n#if defined(WPM_LAUNCH_CONTROL)\n    /*\n     * If the `WPM_LAUNCH_CONTROL` option is enabled, then whenever our WPM\n     * drops to absolute zero due to no typing occurring within our sample\n     * ring buffer, we reset and start measuring fresh, which lets our WPM\n     * immediately reach the correct value even before a full sampling buffer\n     * has been filled.\n     */\n    if (presses == 0) {\n        current_period    = 0;\n        periods           = 0;\n        wpm_now           = 0;\n        period_presses[0] = 0;\n    }\n#endif // WPM_LAUNCH_CONTROL\n\n#if defined(WPM_UNFILTERED)\n    current_wpm = wpm_now;\n#else\n    int32_t latency = timer_elapsed32(smoothing_timer);\n    if (latency > LATENCY) {\n        smoothing_timer = timer_read32();\n        prev_wpm        = current_wpm;\n        next_wpm        = wpm_now;\n    }\n\n    current_wpm = prev_wpm + (latency * ((int)next_wpm - (int)prev_wpm) / LATENCY);\n#endif\n}\n"
  },
  {
    "path": "quantum/wpm.h",
    "content": "/*\n * Copyright 2020 Richard Sutherland (rich@brickbots.com)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\n#ifndef WPM_ESTIMATED_WORD_SIZE\n#    define WPM_ESTIMATED_WORD_SIZE 5\n#endif\n#ifndef WPM_SAMPLE_SECONDS\n#    define WPM_SAMPLE_SECONDS 5\n#endif\n#ifndef WPM_SAMPLE_PERIODS\n#    define WPM_SAMPLE_PERIODS 25\n#endif\n\nbool wpm_keycode(uint16_t keycode);\nbool wpm_keycode_kb(uint16_t keycode);\nbool wpm_keycode_user(uint16_t keycode);\n\n#ifdef WPM_ALLOW_COUNT_REGRESSION\nuint8_t wpm_regress_count(uint16_t keycode);\n#endif\n\nvoid    set_current_wpm(uint8_t);\nuint8_t get_current_wpm(void);\nvoid    update_wpm(uint16_t);\n\nvoid decay_wpm(void);\n"
  },
  {
    "path": "readme.md",
    "content": "# ZSA's fork of QMK Firmware\n\n[![Current Version](https://img.shields.io/github/tag/zsa/qmk_firmware.svg)](https://github.com/zsa/qmk_firmware/tags)\n[![Build firmware](https://github.com/zsa/qmk_firmware/actions/workflows/build.yml/badge.svg)](https://github.com/zsa/qmk_firmware/actions/workflows/build.yml)\n[![Unit Tests](https://github.com/zsa/qmk_firmware/actions/workflows/unit_test.yml/badge.svg)](https://github.com/zsa/qmk_firmware/actions/workflows/unit_test.yml)\n[![GitHub contributors](https://img.shields.io/github/contributors/zsa/qmk_firmware.svg)](https://github.com/zsa/qmk_firmware/pulse/monthly)\n[![GitHub forks](https://img.shields.io/github/forks/zsa/qmk_firmware.svg?style=social&label=Fork)](https://github.com/zsa/qmk_firmware/)\n\nThis purpose of this fork is maintain a clean repo that only contains the keyboard code that we need, and as little else as possible.  This is to keep it lightweight, since we only need a few keyboards. This is the repo that Oryx pulls from.\n\n## Documentation\n\n* [See the official documentation on docs.qmk.fm](https://docs.qmk.fm)\n\nThe docs are powered by [VitePress](https://vitepress.dev/). They are also viewable offline; see [Previewing the Documentation](https://docs.qmk.fm/#/contributing?id=previewing-the-documentation) for more details.\n\n## Supported Keyboards\n* [ErgoDox EZ](/keyboards/zsa/ergodox_ez/)\n* [Planck EZ](/keyboards/zsa/planck_ez/)\n* [Moonlander Mark I](/keyboards/zsa/moonlander/)\n* [Voyager](/keyboards/zsa/voyager/)\n\n## Building\nTo set up the local build enviroment to create the firmware image manually, head to the [Newbs guide from QMK](https://docs.qmk.fm/#/newbs).\n\nAnd instead of using just `qmk setup`, you will want to run this instead:\n\n```sh\nqmk setup zsa/qmk_firmware -b firmware25\n```\n\nNote the current branch of ZSA's QMK fork and replace the above command with that if this is out of date. You can also compile against different firmware revisions by specifying a different branch.\n\n## Developing\n\nIf your text editor supports LSP for C with `clangd`, you need to generate a compilation database:\n\n```\nqmk build --compiledb\n```\n\nThe generated compilation database must be regenerated each time you change board or keymap.\n\n## Maintainers\nQMK is developed and maintained by Jack Humbert of OLKB with contributions from the community, and of course, [Hasu](https://github.com/tmk). The OLKB product firmwares are maintained by [Jack Humbert](https://github.com/jackhumbert), the Ergodox EZ by [ZSA Technology Labs](https://github.com/zsa), the Clueboard by [Zach White](https://github.com/skullydazed), and the Atreus by [Phil Hagelberg](https://github.com/technomancy).\n\n# Update Process\n1. Check out branch from ZSA's master branch:\n    1. `git remote add zsa https://github.com/zsa/qmk_firmware.git`\n    2. `git fetch --all`\n    3. `git checkout -B branchname zsa/master`\n    4. `git push -u zsa branchname`\n2. Check for core changes:\n    - [https://github.com/qmk/qmk_firmware/commits/master/quantum](https://github.com/qmk/qmk_firmware/commits/master/quantum)\n    - [https://github.com/qmk/qmk_firmware/commits/master/tmk_core](https://github.com/qmk/qmk_firmware/commits/master/tmk_core)\n    - [https://github.com/qmk/qmk_firmware/commits/master/util](https://github.com/qmk/qmk_firmware/commits/master/util)\n    - [https://github.com/qmk/qmk_firmware/commits/master/drivers](https://github.com/qmk/qmk_firmware/commits/master/drivers)\n    - [https://github.com/qmk/qmk_firmware/commits/master/lib](https://github.com/qmk/qmk_firmware/commits/master/lib)\n    - These folders are the important ones for maintaining the repo and keeping it properly up to date. Most, but not all, changes on this list should be pulled into our repo.\n4. `git merge (hash|tag)`\n    - `git rm -rf docs users layouts .vscode` to remove the docs and user code that we don't want.\n    - To remove all of the keyboard exept the ones we want:\n      ```sh\n      find ./keyboards -mindepth 1 -maxdepth 1 -type d -not -name ergodox_ez -not -name planck -not -name moonlander -not -name zsa -exec git rm -rf '{}' \\;\n      find ./keyboards/planck -mindepth 1 -maxdepth 1 -type d -not -name ez -not -name base -not -name glow -not -name keymaps -exec git rm -rf '{}' \\;\n      ```\n    - To remove all of the keymaps from folder that we don't want:\n      ```sh\n      find ./keyboards/ -mindepth 3 -maxdepth 3 -type d -not -name ld -not -name default -not -name oryx -not -name webusb -not -name base -not -name glow -not -name reactive -not -name shine -not -name keymaps -not -name halfmoon -exec git rm -rf '{}' \\;\n      ```\n    - Restore necessary files/folders:\n      ```sh\n      git checkout HEAD -- keyboards/handwired/pytest\n      ```\n    - Resolve merge conflicts, and commit.\n4. Commit update\n   * Include commit info in `[changelog.md](changelog.md)`\n5. Open Pull request, and include information about the commit\n\n## Strategy\nTo keep PRs small and easier to test, they should ideally be 1:1 with commits from QMK Firmware master. They should only group commits if/when it makes sense, such as multiple commits for a specific feature (split RGB support, for instance).\n"
  },
  {
    "path": "requirements-dev.txt",
    "content": "# Install the necessary requirements\n-r requirements.txt\n\n# Python development requirements\nnose2\nflake8\npep8-naming\npyflakes\nyapf\n"
  },
  {
    "path": "requirements.txt",
    "content": "# Python requirements\n# platformdirs\nargcomplete\ncolorama\ndotty-dict\nhid\nhjson\njsonschema>=4\nmilc>=1.9.0\npygments\npyserial\npyusb\npillow\n"
  },
  {
    "path": "setup.cfg",
    "content": "# Python settings for QMK\n[flake8]\nignore =\n    # QMK is ok with long lines.\n    E501\n    # Conflicts with our yapf config\n    E231\nper_file_ignores =\n    # Module imported but unused\n    **/__init__.py:F401\n    # Quantum Painter also outputs append data using bytes object arithmetic on multiple lines\n    **/painter_qgf.py:W503\n    **/painter_qff.py:W503\n\n# Let's slowly crank this down\nmax_complexity=16\n\n[yapf]\n# Align closing bracket with visual indentation.\nalign_closing_bracket_with_visual_indent=True\n\n# Allow dictionary keys to exist on multiple lines. For example:\n#\n#   x = {\n#       ('this is the first element of a tuple',\n#        'this is the second element of a tuple'):\n#            value,\n#   }\nallow_multiline_dictionary_keys=False\n\n# Allow lambdas to be formatted on more than one line.\nallow_multiline_lambdas=False\n\n# Allow splitting before a default / named assignment in an argument list.\nallow_split_before_default_or_named_assigns=True\n\n# Allow splits before the dictionary value.\nallow_split_before_dict_value=True\n\n#   Let spacing indicate operator precedence. For example:\n#\n#     a = 1 * 2 + 3 / 4\n#     b = 1 / 2 - 3 * 4\n#     c = (1 + 2) * (3 - 4)\n#     d = (1 - 2) / (3 + 4)\n#     e = 1 * 2 - 3\n#     f = 1 + 2 + 3 + 4\n#\n# will be formatted as follows to indicate precedence:\n#\n#     a = 1*2 + 3/4\n#     b = 1/2 - 3*4\n#     c = (1+2) * (3-4)\n#     d = (1-2) / (3+4)\n#     e = 1*2 - 3\n#     f = 1 + 2 + 3 + 4\n#\narithmetic_precedence_indication=False\n\n# Number of blank lines surrounding top-level function and class\n# definitions.\nblank_lines_around_top_level_definition=2\n\n# Insert a blank line before a class-level docstring.\nblank_line_before_class_docstring=False\n\n# Insert a blank line before a module docstring.\nblank_line_before_module_docstring=False\n\n# Insert a blank line before a 'def' or 'class' immediately nested\n# within another 'def' or 'class'. For example:\n#\n#   class Foo:\n#                      # <------ this blank line\n#     def method():\n#       ...\nblank_line_before_nested_class_or_def=False\n\n# Do not split consecutive brackets. Only relevant when\n# dedent_closing_brackets is set. For example:\n#\n#    call_func_that_takes_a_dict(\n#        {\n#            'key1': 'value1',\n#            'key2': 'value2',\n#        }\n#    )\n#\n# would reformat to:\n#\n#    call_func_that_takes_a_dict({\n#        'key1': 'value1',\n#        'key2': 'value2',\n#    })\ncoalesce_brackets=True\n\n# The column limit.\ncolumn_limit=256\n\n# The style for continuation alignment. Possible values are:\n#\n# - SPACE: Use spaces for continuation alignment. This is default behavior.\n# - FIXED: Use fixed number (CONTINUATION_INDENT_WIDTH) of columns\n#   (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs) for continuation\n#   alignment.\n# - VALIGN-RIGHT: Vertically align continuation lines with indent\n#   characters. Slightly right (one more indent character) if cannot\n#   vertically align continuation lines with indent characters.\n#\n# For options FIXED, and VALIGN-RIGHT are only available when USE_TABS is\n# enabled.\ncontinuation_align_style=SPACE\n\n# Indent width used for line continuations.\ncontinuation_indent_width=4\n\n# Put closing brackets on a separate line, dedented, if the bracketed\n# expression can't fit in a single line. Applies to all kinds of brackets,\n# including function definitions and calls. For example:\n#\n#   config = {\n#       'key1': 'value1',\n#       'key2': 'value2',\n#   }        # <--- this bracket is dedented and on a separate line\n#\n#   time_series = self.remote_client.query_entity_counters(\n#       entity='dev3246.region1',\n#       key='dns.query_latency_tcp',\n#       transform=Transformation.AVERAGE(window=timedelta(seconds=60)),\n#       start_ts=now()-timedelta(days=3),\n#       end_ts=now(),\n#   )        # <--- this bracket is dedented and on a separate line\ndedent_closing_brackets=True\n\n# Disable the heuristic which places each list element on a separate line\n# if the list is comma-terminated.\ndisable_ending_comma_heuristic=False\n\n# Place each dictionary entry onto its own line.\neach_dict_entry_on_separate_line=True\n\n# The regex for an i18n comment. The presence of this comment stops\n# reformatting of that line, because the comments are required to be\n# next to the string they translate.\ni18n_comment=\n\n# The i18n function call names. The presence of this function stops\n# reformattting on that line, because the string it has cannot be moved\n# away from the i18n comment.\ni18n_function_call=\n\n# Indent blank lines.\nindent_blank_lines=False\n\n# Indent the dictionary value if it cannot fit on the same line as the\n# dictionary key. For example:\n#\n#   config = {\n#       'key1':\n#           'value1',\n#       'key2': value1 +\n#               value2,\n#   }\nindent_dictionary_value=True\n\n# The number of columns to use for indentation.\nindent_width=4\n\n# Join short lines into one line. E.g., single line 'if' statements.\njoin_multiple_lines=False\n\n# Do not include spaces around selected binary operators. For example:\n#\n#   1 + 2 * 3 - 4 / 5\n#\n# will be formatted as follows when configured with \"*,/\":\n#\n#   1 + 2*3 - 4/5\nno_spaces_around_selected_binary_operators=\n\n# Use spaces around default or named assigns.\nspaces_around_default_or_named_assign=False\n\n# Use spaces around the power operator.\nspaces_around_power_operator=False\n\n# The number of spaces required before a trailing comment.\n# This can be a single value (representing the number of spaces\n# before each trailing comment) or list of values (representing\n# alignment column values; trailing comments within a block will\n# be aligned to the first column value that is greater than the maximum\n# line length within the block). For example:\n#\n# With spaces_before_comment=5:\n#\n#   1 + 1 # Adding values\n#\n# will be formatted as:\n#\n#   1 + 1     # Adding values <-- 5 spaces between the end of the statement and comment\n#\n# With spaces_before_comment=15, 20:\n#\n#   1 + 1 # Adding values\n#   two + two # More adding\n#\n#   longer_statement # This is a longer statement\n#   short # This is a shorter statement\n#\n#   a_very_long_statement_that_extends_beyond_the_final_column # Comment\n#   short # This is a shorter statement\n#\n# will be formatted as:\n#\n#   1 + 1          # Adding values <-- end of line comments in block aligned to col 15\n#   two + two      # More adding\n#\n#   longer_statement    # This is a longer statement <-- end of line comments in block aligned to col 20\n#   short               # This is a shorter statement\n#\n#   a_very_long_statement_that_extends_beyond_the_final_column  # Comment <-- the end of line comments are aligned based on the line length\n#   short                                                       # This is a shorter statement\n#\nspaces_before_comment=2\n\n# Insert a space between the ending comma and closing bracket of a list,\n# etc.\nspace_between_ending_comma_and_closing_bracket=False\n\n# Split before arguments\nsplit_all_comma_separated_values=False\n\n# Split before arguments if the argument list is terminated by a\n# comma.\nsplit_arguments_when_comma_terminated=True\n\n# Set to True to prefer splitting before '+', '-', '*', '/', '//', or '@'\n# rather than after.\nsplit_before_arithmetic_operator=False\n\n# Set to True to prefer splitting before '&', '|' or '^' rather than\n# after.\nsplit_before_bitwise_operator=True\n\n# Split before the closing bracket if a list or dict literal doesn't fit on\n# a single line.\nsplit_before_closing_bracket=True\n\n# Split before a dictionary or set generator (comp_for). For example, note\n# the split before the 'for':\n#\n#   foo = {\n#       variable: 'Hello world, have a nice day!'\n#       for variable in bar if variable != 42\n#   }\nsplit_before_dict_set_generator=True\n\n# Split before the '.' if we need to split a longer expression:\n#\n#   foo = ('This is a really long string: {}, {}, {}, {}'.format(a, b, c, d))\n#\n# would reformat to something like:\n#\n#   foo = ('This is a really long string: {}, {}, {}, {}'\n#          .format(a, b, c, d))\nsplit_before_dot=False\n\n# Split after the opening paren which surrounds an expression if it doesn't\n# fit on a single line.\nsplit_before_expression_after_opening_paren=False\n\n# If an argument / parameter list is going to be split, then split before\n# the first argument.\nsplit_before_first_argument=False\n\n# Set to True to prefer splitting before 'and' or 'or' rather than\n# after.\nsplit_before_logical_operator=False\n\n# Split named assignments onto individual lines.\nsplit_before_named_assigns=True\n\n# Set to True to split list comprehensions and generators that have\n# non-trivial expressions and multiple clauses before each of these\n# clauses. For example:\n#\n#   result = [\n#       a_long_var + 100 for a_long_var in xrange(1000)\n#       if a_long_var % 10]\n#\n# would reformat to something like:\n#\n#   result = [\n#       a_long_var + 100\n#       for a_long_var in xrange(1000)\n#       if a_long_var % 10]\nsplit_complex_comprehension=True\n\n# The penalty for splitting right after the opening bracket.\nsplit_penalty_after_opening_bracket=300\n\n# The penalty for splitting the line after a unary operator.\nsplit_penalty_after_unary_operator=10000\n\n# The penalty of splitting the line around the '+', '-', '*', '/', '//',\n# ``%``, and '@' operators.\nsplit_penalty_arithmetic_operator=300\n\n# The penalty for splitting right before an if expression.\nsplit_penalty_before_if_expr=0\n\n# The penalty of splitting the line around the '&', '|', and '^'\n# operators.\nsplit_penalty_bitwise_operator=300\n\n# The penalty for splitting a list comprehension or generator\n# expression.\nsplit_penalty_comprehension=80\n\n# The penalty for characters over the column limit.\nsplit_penalty_excess_character=7000\n\n# The penalty incurred by adding a line split to the unwrapped line. The\n# more line splits added the higher the penalty.\nsplit_penalty_for_added_line_split=30\n\n# The penalty of splitting a list of \"import as\" names. For example:\n#\n#   from a_very_long_or_indented_module_name_yada_yad import (long_argument_1,\n#                                                             long_argument_2,\n#                                                             long_argument_3)\n#\n# would reformat to something like:\n#\n#   from a_very_long_or_indented_module_name_yada_yad import (\n#       long_argument_1, long_argument_2, long_argument_3)\nsplit_penalty_import_names=0\n\n# The penalty of splitting the line around the 'and' and 'or'\n# operators.\nsplit_penalty_logical_operator=300\n\n# Use the Tab character for indentation.\nuse_tabs=False\n"
  },
  {
    "path": "tests/audio/config.h",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/audio/test.mk",
    "content": "# Copyright 2023 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAUDIO_ENABLE = yes\n"
  },
  {
    "path": "tests/audio/test_audio.cpp",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include <cmath>\n#include <random>\n\n#include \"gtest/gtest.h\"\n#include \"keyboard_report_util.hpp\"\n#include \"test_common.hpp\"\n\nnamespace {\n\nclass AudioTest : public TestFixture {\n   public:\n    uint16_t infer_tempo() {\n        return audio_ms_to_duration(1875) / 2;\n    }\n};\n\nTEST_F(AudioTest, OnOffToggle) {\n    audio_on();\n    EXPECT_TRUE(audio_is_on());\n\n    audio_off();\n    EXPECT_FALSE(audio_is_on());\n\n    audio_toggle();\n    EXPECT_TRUE(audio_is_on());\n\n    audio_toggle();\n    EXPECT_FALSE(audio_is_on());\n}\n\nTEST_F(AudioTest, ChangeTempo) {\n    for (int tempo = 50; tempo <= 250; tempo += 50) {\n        audio_set_tempo(tempo);\n        EXPECT_EQ(infer_tempo(), tempo);\n    }\n\n    audio_set_tempo(10);\n    audio_increase_tempo(25);\n    EXPECT_EQ(infer_tempo(), 35);\n\n    audio_decrease_tempo(4);\n    EXPECT_EQ(infer_tempo(), 31);\n\n    audio_increase_tempo(250);\n    EXPECT_EQ(infer_tempo(), 255);\n\n    audio_set_tempo(9);\n    EXPECT_EQ(infer_tempo(), 10);\n\n    audio_decrease_tempo(100);\n    EXPECT_EQ(infer_tempo(), 10);\n}\n\nTEST_F(AudioTest, BpmConversion) {\n    const int tol = 1;\n\n    audio_set_tempo(120);\n    // At 120 bpm, there are 2 beats per second, and a whole note is 500 ms.\n    EXPECT_NEAR(audio_duration_to_ms(64 /* whole note */), 500, tol);\n    EXPECT_NEAR(audio_ms_to_duration(500), 64, tol);\n    EXPECT_EQ(audio_duration_to_ms(0), 0);\n    EXPECT_EQ(audio_ms_to_duration(0), 0);\n\n    audio_set_tempo(10);\n    // At 10 bpm, UINT16_MAX ms corresponds to 699/64 beats and is the longest\n    // duration that can be converted without overflow.\n    EXPECT_NEAR(audio_ms_to_duration(UINT16_MAX), 699, tol);\n    EXPECT_NEAR(audio_duration_to_ms(699), 65531, tol);\n\n    audio_set_tempo(255);\n    // At 255 bpm, UINT16_MAX ms corresponds to 17825/64 beats and is the longest\n    // duration that can be converted without overflow.\n    EXPECT_NEAR(audio_ms_to_duration(UINT16_MAX), 17825, tol);\n    EXPECT_NEAR(audio_duration_to_ms(17825), 65533, tol);\n\n    std::mt19937                       rng(0 /*seed*/);\n    std::uniform_int_distribution<int> dist_tempo(10, 255);\n    std::uniform_int_distribution<int> dist_ms(0, UINT16_MAX);\n\n    // Test bpm <-> ms conversions for random tempos and durations.\n    for (int trial = 0; trial < 50; ++trial) {\n        const int tempo       = dist_tempo(rng);\n        const int duration_ms = dist_ms(rng);\n        SCOPED_TRACE(\"tempo \" + testing::PrintToString(tempo) + \", duration \" + testing::PrintToString(duration_ms) + \" ms\");\n\n        audio_set_tempo(tempo);\n        int duration_bpm = std::round((64.0f / (60.0f * 1000.0f)) * duration_ms * tempo);\n        ASSERT_NEAR(audio_ms_to_duration(duration_ms), duration_bpm, tol);\n\n        int roundtrip_ms = std::round((60.0f * 1000.0f / 64.0f) * duration_bpm / tempo);\n        // Because of round-off error, duration_ms and roundtrip_ms may differ by\n        // about (60 * 1000 / 64) / tempo.\n        int roundtrip_tol = tol * (60.0f * 1000.0f / 64.0f) / tempo;\n        ASSERT_NEAR(roundtrip_ms, duration_ms, roundtrip_tol);\n\n        // Only test converting back to ms if the result would be in uint16_t range.\n        if (roundtrip_ms <= UINT16_MAX) {\n            ASSERT_NEAR(audio_duration_to_ms(duration_bpm), roundtrip_ms, tol);\n        }\n    }\n}\n\n} // namespace\n"
  },
  {
    "path": "tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/config.h",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define AUTO_SHIFT_REPEAT\n#define AUTO_SHIFT_NO_AUTO_REPEAT\n"
  },
  {
    "path": "tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/test.mk",
    "content": "# Copyright 2022 Isaac Elenbaas\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nAUTO_SHIFT_ENABLE = yes\n"
  },
  {
    "path": "tests/auto_shift/auto_shift_repeat/auto_shift_no_auto_repeat/test_auto_shift.cpp",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::AnyNumber;\nusing testing::AnyOf;\nusing testing::InSequence;\n\nclass AutoShiftNoAutoRepeat : public TestFixture {};\n\nTEST_F(AutoShiftNoAutoRepeat, no_auto_repeat) {\n    TestDriver driver;\n    InSequence s;\n    auto       repeat_key = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({repeat_key});\n\n    /* Press repeat key. */\n    EXPECT_NO_REPORT(driver);\n    repeat_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Idle for auto-repeat to (not) kick in. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release repeat key. */\n    EXPECT_NO_REPORT(driver);\n    repeat_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(AutoShiftNoAutoRepeat, tap_regular_key_while_another_key_repeats) {\n    TestDriver driver;\n    InSequence s;\n    auto       repeat_key  = KeymapKey(0, 1, 0, KC_P);\n    auto       regular_key = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({repeat_key, regular_key});\n\n    /* Press repeat key. */\n    EXPECT_NO_REPORT(driver);\n    repeat_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release repeat key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    repeat_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press repeat key. */\n    EXPECT_REPORT(driver, (KC_P));\n    repeat_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_P));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release repeat key. */\n    EXPECT_EMPTY_REPORT(driver);\n    repeat_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n"
  },
  {
    "path": "tests/auto_shift/auto_shift_repeat/config.h",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define AUTO_SHIFT_REPEAT\n"
  },
  {
    "path": "tests/auto_shift/auto_shift_repeat/test.mk",
    "content": "# Copyright 2022 Isaac Elenbaas\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nAUTO_SHIFT_ENABLE = yes\n"
  },
  {
    "path": "tests/auto_shift/auto_shift_repeat/test_auto_shift.cpp",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::AnyNumber;\nusing testing::InSequence;\n\nclass AutoShiftRepeat : public TestFixture {};\n\nTEST_F(AutoShiftRepeat, tap_regular_key_cancelling_another_key_hold) {\n    TestDriver driver;\n    InSequence s;\n    auto       repeat_key  = KeymapKey(0, 1, 0, KC_P);\n    auto       regular_key = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({repeat_key, regular_key});\n\n    /* Press repeat key. */\n    EXPECT_NO_REPORT(driver);\n    repeat_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(testing::AnyNumber());\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(testing::AnyNumber());\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(testing::AnyNumber());\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(testing::AnyNumber());\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release repeat key. */\n    EXPECT_NO_REPORT(driver);\n    repeat_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(AutoShiftRepeat, tap_regular_key_while_another_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       repeat_key  = KeymapKey(0, 1, 0, KC_P);\n    auto       regular_key = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({repeat_key, regular_key});\n\n    /* Press repeat key. */\n    EXPECT_NO_REPORT(driver);\n    repeat_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Idle for auto-repeat to kick in. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_P));\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_P));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release repeat key. */\n    EXPECT_EMPTY_REPORT(driver);\n    repeat_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n"
  },
  {
    "path": "tests/auto_shift/config.h",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/config.h",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define RETRO_SHIFT 2 * TAPPING_TERM\n// releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested\n#define AUTO_SHIFT_TIMEOUT TAPPING_TERM\n#define AUTO_SHIFT_MODIFIERS\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/config.h",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define RETRO_SHIFT\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/test.mk",
    "content": "# Copyright 2022 Isaac Elenbaas\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nAUTO_SHIFT_ENABLE = yes\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/no_timeout/test_retro_shift.cpp",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nbool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\nusing testing::_;\nusing testing::AnyNumber;\nusing testing::AnyOf;\nusing testing::InSequence;\n\nclass RetroShiftDefaultTapHold : public TestFixture {};\n\nTEST_F(RetroShiftDefaultTapHold, hold_mod_tap_key_for_long) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_A));\n\n    set_keymap({mod_tap_hold_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    idle_for(4 * TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/test.mk",
    "content": "# Copyright 2022 Isaac Elenbaas\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nAUTO_SHIFT_ENABLE = yes\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/default_mod_tap/test_retro_shift.cpp",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nbool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\nusing testing::_;\nusing testing::AnyNumber;\nusing testing::AnyOf;\nusing testing::InSequence;\n\nclass RetroShiftDefaultTapHold : public TestFixture {};\n\nTEST_F(RetroShiftDefaultTapHold, tap_mod_tap_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_A));\n\n    set_keymap({mod_tap_hold_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, hold_mod_tap_key_under_retro_shift) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_A));\n\n    set_keymap({mod_tap_hold_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, hold_mod_tap_key_over_retro_shift) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_A));\n\n    set_keymap({mod_tap_hold_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    idle_for(RETRO_SHIFT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, tap_regular_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, tap_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, hold_regular_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, hold_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, roll_tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, roll_tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, roll_hold_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftDefaultTapHold, roll_hold_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/config.h",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define HOLD_ON_OTHER_KEY_PRESS\n\n#define RETRO_SHIFT 2 * TAPPING_TERM\n// releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested\n#define AUTO_SHIFT_TIMEOUT TAPPING_TERM\n#define AUTO_SHIFT_MODIFIERS\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/test.mk",
    "content": "# Copyright 2022 Isaac Elenbaas\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nAUTO_SHIFT_ENABLE = yes\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/hold_on_other_key_press/test_retro_shift.cpp",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nbool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\nusing testing::_;\nusing testing::AnyNumber;\nusing testing::AnyOf;\nusing testing::InSequence;\n\nclass RetroShiftHoldOnOtherKeyPress : public TestFixture {};\n\nTEST_F(RetroShiftHoldOnOtherKeyPress, tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftHoldOnOtherKeyPress, tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftHoldOnOtherKeyPress, tap_regular_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftHoldOnOtherKeyPress, tap_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftHoldOnOtherKeyPress, hold_regular_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftHoldOnOtherKeyPress, hold_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftHoldOnOtherKeyPress, roll_tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftHoldOnOtherKeyPress, roll_tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftHoldOnOtherKeyPress, roll_hold_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftHoldOnOtherKeyPress, roll_hold_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/config.h",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define PERMISSIVE_HOLD\n\n#define RETRO_SHIFT 2 * TAPPING_TERM\n// releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested\n#define AUTO_SHIFT_TIMEOUT TAPPING_TERM\n#define AUTO_SHIFT_MODIFIERS\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/test.mk",
    "content": "# Copyright 2022 Isaac Elenbaas\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nAUTO_SHIFT_ENABLE = yes\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold/test_retro_shift.cpp",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nbool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\nusing testing::_;\nusing testing::AnyNumber;\nusing testing::AnyOf;\nusing testing::InSequence;\n\nclass RetroShiftPermissiveHold : public TestFixture {};\n\nTEST_F(RetroShiftPermissiveHold, tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, tap_regular_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, tap_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, hold_regular_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, hold_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, roll_tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, roll_tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, roll_hold_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, roll_hold_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/config.h",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define HOLD_ON_OTHER_KEY_PRESS\n#define PERMISSIVE_HOLD\n\n#define RETRO_SHIFT 2 * TAPPING_TERM\n// releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested\n#define AUTO_SHIFT_TIMEOUT TAPPING_TERM\n#define AUTO_SHIFT_MODIFIERS\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/test.mk",
    "content": "# Copyright 2022 Isaac Elenbaas\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nAUTO_SHIFT_ENABLE = yes\n"
  },
  {
    "path": "tests/auto_shift/retro_shift/tap_hold_configurations/permissive_hold_hold_on_other_key_press/test_retro_shift.cpp",
    "content": "/* Copyright 2022 Isaac Elenbaas\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nbool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\nusing testing::_;\nusing testing::AnyNumber;\nusing testing::AnyOf;\nusing testing::InSequence;\n\nclass RetroShiftPermissiveHoldHoldOnOtherKeyPress : public TestFixture {};\n\nTEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, tap_regular_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, tap_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, hold_regular_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, hold_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, roll_tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, roll_tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, roll_hold_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHoldHoldOnOtherKeyPress, roll_hold_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n"
  },
  {
    "path": "tests/auto_shift/test.mk",
    "content": "# Copyright 2021 Stefan Kerkmann\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nAUTO_SHIFT_ENABLE = yes\n"
  },
  {
    "path": "tests/auto_shift/test_auto_shift.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass AutoShift : public TestFixture {};\n\nTEST_F(AutoShift, key_release_before_timeout) {\n    TestDriver driver;\n    InSequence s;\n    auto       regular_key = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({regular_key});\n\n    /* Press regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(AutoShift, key_release_after_timeout) {\n    TestDriver driver;\n    InSequence s;\n    auto       regular_key = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({regular_key});\n\n    /* Press regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_REPORT(driver, (KC_LSFT));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/autocorrect/config.h",
    "content": "// Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/autocorrect/test.mk",
    "content": "# Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n# SPDX-License-Identifier: GPL-2.0-or-later\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nAUTOCORRECT_ENABLE = yes\n"
  },
  {
    "path": "tests/autocorrect/test_autocorrect.cpp",
    "content": "// Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n\nusing ::testing::_;\nusing ::testing::AnyNumber;\nusing ::testing::InSequence;\n\nclass AutoCorrect : public TestFixture {\n   public:\n    void SetUp() override {\n        autocorrect_enable();\n    }\n    // Convenience function to tap `key`.\n    void TapKey(KeymapKey key) {\n        key.press();\n        run_one_scan_loop();\n        key.release();\n        run_one_scan_loop();\n    }\n\n    // Taps in order each key in `keys`.\n    template <typename... Ts>\n    void TapKeys(Ts... keys) {\n        for (KeymapKey key : {keys...}) {\n            TapKey(key);\n        }\n    }\n};\n\n// Test that verifies enable/disable/toggling works\nTEST_F(AutoCorrect, OnOffToggle) {\n    TestDriver driver;\n\n    EXPECT_EQ(autocorrect_is_enabled(), true);\n\n    autocorrect_disable();\n    EXPECT_EQ(autocorrect_is_enabled(), false);\n    autocorrect_disable();\n    EXPECT_EQ(autocorrect_is_enabled(), false);\n\n    autocorrect_enable();\n    EXPECT_EQ(autocorrect_is_enabled(), true);\n    autocorrect_enable();\n    EXPECT_EQ(autocorrect_is_enabled(), true);\n\n    autocorrect_toggle();\n    EXPECT_EQ(autocorrect_is_enabled(), false);\n    autocorrect_toggle();\n    EXPECT_EQ(autocorrect_is_enabled(), true);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Test that typing \"fales\" autocorrects to \"false\"\nTEST_F(AutoCorrect, fales_to_false_autocorrection) {\n    TestDriver driver;\n    auto       key_f = KeymapKey(0, 0, 0, KC_F);\n    auto       key_a = KeymapKey(0, 1, 0, KC_A);\n    auto       key_l = KeymapKey(0, 2, 0, KC_L);\n    auto       key_e = KeymapKey(0, 3, 0, KC_E);\n    auto       key_s = KeymapKey(0, 4, 0, KC_S);\n\n    set_keymap({key_f, key_a, key_l, key_e, key_s});\n\n    // Allow any number of empty reports.\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber());\n    { // Expect the following reports in this order.\n        InSequence s;\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_BACKSPACE)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_S)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));\n    }\n\n    TapKeys(key_f, key_a, key_l, key_e, key_s);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Test that typing \"fales\" doesn't autocorrect if disabled\nTEST_F(AutoCorrect, fales_disabled_autocorrect) {\n    TestDriver driver;\n    auto       key_f = KeymapKey(0, 0, 0, KC_F);\n    auto       key_a = KeymapKey(0, 1, 0, KC_A);\n    auto       key_l = KeymapKey(0, 2, 0, KC_L);\n    auto       key_e = KeymapKey(0, 3, 0, KC_E);\n    auto       key_s = KeymapKey(0, 4, 0, KC_S);\n\n    set_keymap({key_f, key_a, key_l, key_e, key_s});\n\n    // Allow any number of empty reports.\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber());\n    { // Expect the following reports in this order.\n        InSequence s;\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_S)));\n    }\n\n    autocorrect_disable();\n    TapKeys(key_f, key_a, key_l, key_e, key_s);\n    autocorrect_enable();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Test that typing \"falsify\" doesn't autocorrect if disabled\nTEST_F(AutoCorrect, falsify_should_not_autocorrect) {\n    TestDriver driver;\n    auto       key_f = KeymapKey(0, 0, 0, KC_F);\n    auto       key_a = KeymapKey(0, 1, 0, KC_A);\n    auto       key_l = KeymapKey(0, 2, 0, KC_L);\n    auto       key_s = KeymapKey(0, 3, 0, KC_S);\n    auto       key_i = KeymapKey(0, 4, 0, KC_I);\n    auto       key_y = KeymapKey(0, 5, 0, KC_Y);\n\n    set_keymap({key_f, key_a, key_l, key_s, key_i, key_y});\n\n    // Allow any number of empty reports.\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber());\n    { // Expect the following reports in this order.\n        InSequence s;\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_A)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_L)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_S)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_I)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_F)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_Y)));\n    }\n\n    TapKeys(key_f, key_a, key_l, key_s, key_i, key_f, key_y);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Test that  typing \"ture\" autocorrect to \"true\"\nTEST_F(AutoCorrect, ture_to_true_autocorrect) {\n    TestDriver driver;\n    auto       key_t_code = KeymapKey(0, 0, 0, KC_T);\n    auto       key_r      = KeymapKey(0, 1, 0, KC_R);\n    auto       key_u      = KeymapKey(0, 2, 0, KC_U);\n    auto       key_e      = KeymapKey(0, 3, 0, KC_E);\n    auto       key_space  = KeymapKey(0, 4, 0, KC_SPACE);\n\n    set_keymap({key_t_code, key_r, key_u, key_e, key_space});\n\n    // Allow any number of empty reports.\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber());\n    { // Expect the following reports in this order.\n        InSequence s;\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_SPACE)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_T)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_U)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_BACKSPACE))).Times(2);\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_U)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));\n    }\n\n    TapKeys(key_space, key_t_code, key_u, key_r, key_e);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Test that  typing \"overture\" does not autocorrect\nTEST_F(AutoCorrect, overture_should_not_autocorrect) {\n    TestDriver driver;\n    auto       key_t_code = KeymapKey(0, 0, 0, KC_T);\n    auto       key_r      = KeymapKey(0, 1, 0, KC_R);\n    auto       key_u      = KeymapKey(0, 2, 0, KC_U);\n    auto       key_e      = KeymapKey(0, 3, 0, KC_E);\n    auto       key_o      = KeymapKey(0, 4, 0, KC_O);\n    auto       key_v      = KeymapKey(0, 5, 0, KC_V);\n\n    set_keymap({key_t_code, key_r, key_u, key_e, key_o, key_v});\n\n    // Allow any number of empty reports.\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport())).Times(AnyNumber());\n    { // Expect the following reports in this order.\n        InSequence s;\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_O)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_V)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_T)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_U)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_R)));\n        EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_E)));\n    }\n\n    TapKeys(key_o, key_v, key_e, key_r, key_t_code, key_u, key_r, key_e);\n\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/basic/config.h",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/basic/test.mk",
    "content": "# Copyright 2017 Fred Sundvik\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n"
  },
  {
    "path": "tests/basic/test_action_layer.cpp",
    "content": "/* Copyright 2017 Colin T.A. Gray\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n#include \"keyboard_report_util.hpp\"\n#include \"test_common.hpp\"\n\nusing testing::_;\nusing testing::AnyNumber;\nusing testing::InSequence;\n\nclass ActionLayer : public TestFixture {};\n\nTEST_F(ActionLayer, LayerStateDBG) {\n    TestDriver driver;\n\n    layer_state_set(0);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerStateSet) {\n    TestDriver driver;\n\n    layer_state_set(0);\n    EXPECT_EQ(layer_state, 0);\n    layer_state_set(0b001100);\n    EXPECT_EQ(layer_state, 0b001100);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerStateIs) {\n    TestDriver driver;\n\n    layer_state_set(0);\n    EXPECT_EQ(layer_state_is(0), true);\n    EXPECT_EQ(layer_state_is(1), false);\n    layer_state_set(1);\n    EXPECT_EQ(layer_state_is(0), true);\n    EXPECT_EQ(layer_state_is(1), false);\n    layer_state_set(2);\n    EXPECT_EQ(layer_state_is(0), false);\n    EXPECT_EQ(layer_state_is(1), true);\n    EXPECT_EQ(layer_state_is(2), false);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerStateCmp) {\n    TestDriver driver;\n    uint32_t   prev_layer;\n\n    prev_layer = 0;\n    EXPECT_EQ(layer_state_cmp(prev_layer, 0), true);\n    EXPECT_EQ(layer_state_cmp(prev_layer, 1), false);\n\n    prev_layer = 1;\n    EXPECT_EQ(layer_state_cmp(prev_layer, 0), true);\n    EXPECT_EQ(layer_state_cmp(prev_layer, 1), false);\n\n    prev_layer = 2;\n    EXPECT_EQ(layer_state_cmp(prev_layer, 0), false);\n    EXPECT_EQ(layer_state_cmp(prev_layer, 1), true);\n    EXPECT_EQ(layer_state_cmp(prev_layer, 2), false);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerClear) {\n    TestDriver driver;\n\n    layer_clear();\n    EXPECT_EQ(layer_state, 0);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerMove) {\n    TestDriver driver;\n\n    layer_move(0);\n    EXPECT_EQ(layer_state, 1);\n    layer_move(3);\n    EXPECT_EQ(layer_state, 0b1000);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerOn) {\n    TestDriver driver;\n\n    layer_clear();\n    layer_on(1);\n    layer_on(3);\n    layer_on(3);\n    EXPECT_EQ(layer_state, 0b1010);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerOff) {\n    TestDriver driver;\n\n    layer_clear();\n    layer_on(1);\n    layer_on(3);\n    layer_off(3);\n    layer_off(2);\n    EXPECT_EQ(layer_state, 0b0010);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, MomentaryLayerDoesNothing) {\n    TestDriver driver;\n    KeymapKey  layer_key = KeymapKey{0, 0, 0, MO(1)};\n\n    set_keymap({layer_key});\n\n    /* Press and release MO, nothing should happen. */\n    EXPECT_NO_REPORT(driver);\n    layer_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    layer_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, MomentaryLayerWithKeypress) {\n    TestDriver driver;\n    KeymapKey  layer_key = KeymapKey{0, 0, 0, MO(1)};\n\n    /* These keys must have the same position in the matrix, only the layer is different. */\n    KeymapKey regular_key = KeymapKey{0, 1, 0, KC_A};\n    set_keymap({layer_key, regular_key, KeymapKey{1, 1, 0, KC_B}});\n\n    /* Press MO. */\n    EXPECT_NO_REPORT(driver);\n    layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press key on layer 1 */\n    EXPECT_REPORT(driver, (KC_B)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release key on layer 1 */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release MO */\n    EXPECT_NO_REPORT(driver);\n    layer_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, ToggleLayerDoesNothing) {\n    GTEST_SKIP() << \"TODO: Toggle layer does not activate the expected layer on key press but on release.\";\n\n    TestDriver driver;\n    KeymapKey  layer_key = KeymapKey{0, 0, 0, TG(1)};\n\n    set_keymap({layer_key});\n\n    /* Press TG. Layer state should not change as it's applied on release. */\n    EXPECT_NO_REPORT(driver);\n    layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release TG. */\n    EXPECT_NO_REPORT(driver);\n    layer_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, ToggleLayerUpAndDown) {\n    GTEST_SKIP() << \"TODO: Toggle layer does not activate the expected layer on key press but on release.\";\n\n    TestDriver driver;\n    KeymapKey  toggle_layer_1_on_layer_0 = KeymapKey{0, 0, 0, TG(1)};\n    KeymapKey  toggle_layer_0_on_layer_1 = KeymapKey{1, 1, 0, TG(0)};\n\n    set_keymap({toggle_layer_1_on_layer_0, toggle_layer_0_on_layer_1});\n\n    /* Toggle Layer 1. */\n    EXPECT_NO_REPORT(driver);\n    toggle_layer_1_on_layer_0.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    toggle_layer_1_on_layer_0.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Toggle Layer 0. */\n    EXPECT_NO_REPORT(driver);\n    toggle_layer_0_on_layer_1.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    toggle_layer_0_on_layer_1.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerTapToggleDoesNothing) {\n    GTEST_SKIP() << \"TODO: Tap toggle layer does not activate the expected layer on key press.\";\n\n    TestDriver driver;\n    KeymapKey  layer_key = KeymapKey{0, 0, 0, TT(1)};\n\n    set_keymap({layer_key});\n\n    /* Press and release TT. */\n    EXPECT_NO_REPORT(driver);\n    layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    layer_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerTapToggleWithKeypress) {\n    GTEST_SKIP() << \"TODO: Tap toggle layer does not activate the expected layer on key press.\";\n\n    TestDriver driver;\n    KeymapKey  layer_key = KeymapKey{0, 0, 0, TT(1)};\n\n    /* These keys must have the same position in the matrix, only the layer is different. */\n    KeymapKey regular_key = KeymapKey{0, 1, 0, KC_A};\n    set_keymap({layer_key, regular_key, KeymapKey{1, 1, 0, KC_B}});\n\n    /* Press TT. */\n    EXPECT_NO_REPORT(driver);\n    layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    layer_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerTapToggleWithToggleWithKeypress) {\n    GTEST_SKIP() << \"TODO: Tap toggle layer does not activate the expected layer on key press.\";\n\n    TestDriver driver;\n    KeymapKey  layer_key = KeymapKey{0, 0, 0, TT(1)};\n\n    /* These keys must have the same position in the matrix, only the layer is different. */\n    KeymapKey regular_key = KeymapKey{0, 1, 0, KC_A};\n    set_keymap({layer_key, regular_key, KeymapKey{1, 1, 0, KC_B}});\n\n    /* Tap TT five times . */\n    EXPECT_NO_REPORT(driver);\n\n    layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    layer_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n\n    layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    layer_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n\n    layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    layer_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n\n    layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    layer_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n\n    layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    layer_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerTapReleasedBeforeKeypressReleaseWithModifiers) {\n    TestDriver driver;\n    InSequence s;\n\n    KeymapKey layer_0_key_0 = KeymapKey{0, 0, 0, LT(1, KC_T)};\n    KeymapKey layer_0_key_1 = KeymapKey{0, 1, 0, KC_X};\n    KeymapKey layer_1_key_1 = KeymapKey{1, 1, 0, RALT(KC_9)};\n\n    set_keymap({layer_0_key_0, layer_0_key_1, layer_1_key_1});\n\n    /* Press layer tap and wait for tapping term to switch to layer 1 */\n    EXPECT_NO_REPORT(driver);\n    layer_0_key_0.press();\n    idle_for(TAPPING_TERM);\n    EXPECT_TRUE(layer_state_is(0));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press key with layer 1 mapping, result basically expected\n     * altough more reports are send then necessary. */\n    EXPECT_REPORT(driver, (KC_RALT)).Times(1);\n    EXPECT_REPORT(driver, (KC_RALT, KC_9)).Times(1);\n    layer_1_key_1.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release layer tap key, no report is send because key is still held. */\n    EXPECT_NO_REPORT(driver);\n    layer_0_key_0.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Unregister keycode and modifier. */\n    EXPECT_REPORT(driver, (KC_RALT)).Times(1);\n    EXPECT_EMPTY_REPORT(driver);\n    layer_1_key_1.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerModWithKeypress) {\n    TestDriver driver;\n    KeymapKey  layer_key   = KeymapKey{0, 0, 0, LM(1, MOD_RALT)};\n    KeymapKey  regular_key = KeymapKey{0, 1, 0, KC_A};\n    set_keymap({layer_key, regular_key, KeymapKey{1, 1, 0, KC_B}});\n\n    // Allow any number of reports with no keys or only KC_RALT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_RALT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_RALT, KC_B)).Times(1);\n\n    layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_EQ(get_mods(), MOD_BIT(KC_RALT));\n\n    tap_key(regular_key);\n\n    layer_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    EXPECT_EQ(get_mods(), 0);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ActionLayer, LayerModHonorsModConfig) {\n    TestDriver driver;\n    KeymapKey  layer_key   = KeymapKey{0, 0, 0, LM(1, MOD_RALT)};\n    KeymapKey  regular_key = KeymapKey{0, 1, 0, KC_A};\n    set_keymap({layer_key, regular_key, KeymapKey{1, 1, 0, KC_B}});\n\n    // Allow any number of reports with no keys or only KC_RALT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_RGUI))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_RGUI, KC_B)).Times(1);\n\n    keymap_config.swap_ralt_rgui = true;\n\n    layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_EQ(get_mods(), MOD_BIT(KC_RGUI));\n\n    tap_key(regular_key);\n\n    layer_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    EXPECT_EQ(get_mods(), 0);\n\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/basic/test_keypress.cpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass KeyPress : public TestFixture {};\n\nTEST_F(KeyPress, SendKeyboardIsNotCalledWhenNoKeyIsPressed) {\n    TestDriver driver;\n    EXPECT_NO_REPORT(driver);\n    keyboard_task();\n}\n\nTEST_F(KeyPress, CorrectKeyIsReportedWhenPressed) {\n    TestDriver driver;\n    auto       key = KeymapKey(0, 0, 0, KC_A);\n\n    set_keymap({key});\n\n    key.press();\n    EXPECT_REPORT(driver, (key.report_code));\n    keyboard_task();\n\n    key.release();\n    EXPECT_EMPTY_REPORT(driver);\n    keyboard_task();\n}\n\nTEST_F(KeyPress, ANonMappedKeyDoesNothing) {\n    TestDriver driver;\n    auto       key = KeymapKey(0, 0, 0, KC_NO);\n\n    set_keymap({key});\n\n    key.press();\n    EXPECT_NO_REPORT(driver);\n    keyboard_task();\n    keyboard_task();\n}\n\nTEST_F(KeyPress, CorrectKeysAreReportedWhenTwoKeysArePressed) {\n    TestDriver driver;\n    auto       key_b = KeymapKey(0, 0, 0, KC_B);\n    auto       key_c = KeymapKey(0, 1, 1, KC_C);\n\n    set_keymap({key_b, key_c});\n\n    key_b.press();\n    key_c.press();\n    EXPECT_REPORT(driver, (key_b.report_code));\n    EXPECT_REPORT(driver, (key_b.report_code, key_c.report_code));\n    keyboard_task();\n\n    key_b.release();\n    key_c.release();\n    // Note that the first key released is the first one in the matrix order\n    EXPECT_REPORT(driver, (key_c.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    keyboard_task();\n}\n\nTEST_F(KeyPress, LeftShiftIsReportedCorrectly) {\n    TestDriver driver;\n    auto       key_a    = KeymapKey(0, 0, 0, KC_A);\n    auto       key_lsft = KeymapKey(0, 3, 0, KC_LEFT_SHIFT);\n\n    set_keymap({key_a, key_lsft});\n\n    key_lsft.press();\n    key_a.press();\n\n    EXPECT_REPORT(driver, (key_a.report_code));\n    EXPECT_REPORT(driver, (key_a.report_code, key_lsft.report_code));\n    keyboard_task();\n\n    key_a.release();\n    EXPECT_REPORT(driver, (key_lsft.report_code));\n    keyboard_task();\n\n    key_lsft.release();\n    EXPECT_EMPTY_REPORT(driver);\n    keyboard_task();\n}\n\nTEST_F(KeyPress, PressLeftShiftAndControl) {\n    TestDriver driver;\n    auto       key_lsft  = KeymapKey(0, 3, 0, KC_LEFT_SHIFT);\n    auto       key_lctrl = KeymapKey(0, 5, 0, KC_LEFT_CTRL);\n\n    set_keymap({key_lctrl, key_lsft});\n\n    key_lsft.press();\n    key_lctrl.press();\n\n    EXPECT_REPORT(driver, (key_lsft.report_code));\n    EXPECT_REPORT(driver, (key_lsft.report_code, key_lctrl.report_code));\n    keyboard_task();\n\n    key_lsft.release();\n    key_lctrl.release();\n\n    EXPECT_REPORT(driver, (key_lctrl.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    keyboard_task();\n}\n\nTEST_F(KeyPress, LeftAndRightShiftCanBePressedAtTheSameTime) {\n    TestDriver driver;\n    auto       key_lsft = KeymapKey(0, 3, 0, KC_LEFT_SHIFT);\n    auto       key_rsft = KeymapKey(0, 4, 0, KC_RIGHT_SHIFT);\n\n    set_keymap({key_rsft, key_lsft});\n\n    key_lsft.press();\n    key_rsft.press();\n    EXPECT_REPORT(driver, (key_lsft.report_code));\n    EXPECT_REPORT(driver, (key_lsft.report_code, key_rsft.report_code));\n    keyboard_task();\n\n    key_lsft.release();\n    key_rsft.release();\n    EXPECT_REPORT(driver, (key_rsft.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    keyboard_task();\n}\n\nTEST_F(KeyPress, RightShiftLeftControlAndCharWithTheSameKey) {\n    TestDriver driver;\n    auto       combo_key = KeymapKey(0, 0, 0, RSFT(LCTL(KC_O)));\n\n    set_keymap({combo_key});\n\n    // BUG: The press is split into two reports\n    // BUG: It reports RSFT instead of LSFT\n    // See issue #524 for more information\n    // The underlying cause is that we use only one bit to represent the right hand\n    // modifiers.\n    combo_key.press();\n    EXPECT_REPORT(driver, (KC_RIGHT_SHIFT, KC_RIGHT_CTRL));\n    EXPECT_REPORT(driver, (KC_RIGHT_SHIFT, KC_RIGHT_CTRL, KC_O));\n    keyboard_task();\n\n    combo_key.release();\n    EXPECT_REPORT(driver, (KC_RIGHT_SHIFT, KC_RIGHT_CTRL));\n    EXPECT_EMPTY_REPORT(driver);\n    keyboard_task();\n}\n\nTEST_F(KeyPress, PressPlusEqualReleaseBeforePress) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_plus = KeymapKey(0, 1, 1, KC_PLUS);\n    auto       key_eql  = KeymapKey(0, 0, 1, KC_EQUAL);\n\n    set_keymap({key_plus, key_eql});\n\n    key_plus.press();\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_EQUAL));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_plus.release();\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_eql.press();\n    EXPECT_REPORT(driver, (key_eql.report_code));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_eql.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(KeyPress, PressPlusEqualDontReleaseBeforePress) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_plus = KeymapKey(0, 1, 1, KC_PLUS);\n    auto       key_eql  = KeymapKey(0, 0, 1, KC_EQUAL);\n\n    set_keymap({key_plus, key_eql});\n\n    key_plus.press();\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_EQUAL));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_eql.press();\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_EQUAL));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_plus.release();\n    // BUG: Should really still return KC_EQUAL, but this is fine too\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_eql.release();\n    EXPECT_NO_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(KeyPress, PressEqualPlusReleaseBeforePress) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_plus = KeymapKey(0, 1, 1, KC_PLUS);\n    auto       key_eql  = KeymapKey(0, 0, 1, KC_EQUAL);\n\n    set_keymap({key_plus, key_eql});\n\n    key_eql.press();\n    EXPECT_REPORT(driver, (KC_EQUAL));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_eql.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_plus.press();\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_EQUAL));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_plus.release();\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(KeyPress, PressEqualPlusDontReleaseBeforePress) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_plus = KeymapKey(0, 1, 1, KC_PLUS);\n    auto       key_eql  = KeymapKey(0, 0, 1, KC_EQUAL);\n\n    set_keymap({key_plus, key_eql});\n\n    key_eql.press();\n    EXPECT_REPORT(driver, (KC_EQUAL));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_plus.press();\n    // BUG: The sequence is a bit strange, but it works, the end result is that\n    // KC_PLUS is sent\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_EQUAL));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_EQUAL));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_eql.release();\n    // I guess it's fine to still report shift here\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    key_plus.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/basic/test_one_shot_keys.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"action_util.h\"\n#include \"keyboard_report_util.hpp\"\n#include \"test_common.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass OneShot : public TestFixture {};\nclass OneShotParametrizedTestFixture : public ::testing::WithParamInterface<std::pair<KeymapKey, KeymapKey>>, public OneShot {};\n\nTEST_F(OneShot, OSMWithoutAdditionalKeypressDoesNothing) {\n    TestDriver driver;\n    auto       osm_key = KeymapKey(0, 0, 0, OSM(MOD_LSFT), KC_LSFT);\n\n    set_keymap({osm_key});\n\n    /* Press and release OSM key*/\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key);\n    VERIFY_AND_CLEAR(driver);\n\n    /* OSM are added when an actual report is send */\n    EXPECT_REPORT(driver, (osm_key.report_code));\n    send_keyboard_report();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Make unit-test pass */\n    clear_oneshot_mods();\n}\n\n#if defined(ONESHOT_TIMEOUT)\n\nTEST_P(OneShotParametrizedTestFixture, OSMExpiredDoesNothing) {\n    TestDriver driver;\n    KeymapKey  osm_key     = GetParam().first;\n    KeymapKey  regular_key = GetParam().second;\n\n    set_keymap({osm_key, regular_key});\n\n    /* Press and release OSM */\n    EXPECT_NO_REPORT(driver);\n    osm_key.press();\n    run_one_scan_loop();\n    osm_key.release();\n    idle_for(ONESHOT_TIMEOUT);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (regular_key.report_code)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\n#endif\n\nTEST_P(OneShotParametrizedTestFixture, OSMWithAdditionalKeypress) {\n    TestDriver driver;\n    KeymapKey  osm_key     = GetParam().first;\n    KeymapKey  regular_key = GetParam().second;\n\n    set_keymap({osm_key, regular_key});\n\n    /* Press and release OSM */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_P(OneShotParametrizedTestFixture, OSMAsRegularModifierWithAdditionalKeypress) {\n    TestDriver          driver;\n    testing::InSequence s;\n\n    KeymapKey osm_key     = GetParam().first;\n    KeymapKey regular_key = GetParam().second;\n\n    set_keymap({osm_key, regular_key});\n\n    /* Press OSM */\n    EXPECT_NO_REPORT(driver);\n    osm_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release OSM */\n    EXPECT_REPORT(driver, (regular_key.report_code, osm_key.report_code)).Times(1);\n    EXPECT_EMPTY_REPORT(driver);\n    osm_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\n// clang-format off\n\nINSTANTIATE_TEST_CASE_P(\n    OneShotModifierTests,\n    OneShotParametrizedTestFixture,\n    ::testing::Values(\n        /* first is osm key, second is regular key. */\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LCTL), KC_LCTL}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LALT), KC_LALT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LGUI), KC_LGUI}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RCTL), KC_RCTL}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RSFT), KC_RSFT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RALT), KC_RALT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RGUI), KC_RGUI}, KeymapKey{0, 1, 1, KC_A})\n        ));\n// clang-format on\n\nTEST_F(OneShot, OSMChainingTwoOSMs) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osm_key1    = KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT};\n    KeymapKey  osm_key2    = KeymapKey{0, 0, 1, OSM(MOD_LCTL), KC_LCTL};\n    KeymapKey  regular_key = KeymapKey{0, 1, 0, KC_A};\n\n    set_keymap({osm_key1, osm_key2, regular_key});\n\n    /* Press and release OSM1 */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press and release OSM2 */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key2);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (osm_key1.report_code, osm_key2.report_code, regular_key.report_code)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSMDoubleTapNotLockingOSMs) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osm_key1    = KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT};\n    KeymapKey  osm_key2    = KeymapKey{0, 0, 1, OSM(MOD_LCTL), KC_LCTL};\n    KeymapKey  regular_key = KeymapKey{0, 1, 0, KC_A};\n\n    set_keymap({osm_key1, osm_key2, regular_key});\n\n    /* Press and release OSM1 */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press and release OSM2 twice */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key2);\n    tap_key(osm_key2);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (osm_key1.report_code, osm_key2.report_code, regular_key.report_code)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (regular_key.report_code)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSMHoldNotLockingOSMs) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osm_key1    = KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT};\n    KeymapKey  osm_key2    = KeymapKey{0, 0, 1, OSM(MOD_LCTL), KC_LCTL};\n    KeymapKey  regular_key = KeymapKey{0, 1, 0, KC_A};\n\n    set_keymap({osm_key1, osm_key2, regular_key});\n\n    /* Press and release OSM1 */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press and hold OSM2 */\n    EXPECT_REPORT(driver, (osm_key1.report_code, osm_key2.report_code)).Times(1);\n    osm_key2.press();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press and release regular key */\n    EXPECT_REPORT(driver, (osm_key1.report_code, osm_key2.report_code, regular_key.report_code)).Times(1);\n    EXPECT_REPORT(driver, (osm_key2.report_code)).Times(1);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release OSM2 */\n    EXPECT_EMPTY_REPORT(driver);\n    osm_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (regular_key.report_code)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSLWithAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key     = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  regular_key = KeymapKey{1, 1, 0, KC_A};\n\n    set_keymap({osl_key, regular_key});\n\n    /* Press OSL key */\n    EXPECT_NO_REPORT(driver);\n    osl_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release OSL key */\n    EXPECT_NO_REPORT(driver);\n    osl_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (regular_key.report_code)).Times(1);\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSLWithOsmAndAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key     = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  osm_key     = KeymapKey{1, 1, 0, OSM(MOD_LSFT), KC_LSFT};\n    KeymapKey  regular_key = KeymapKey{1, 1, 1, KC_A};\n\n    set_keymap({osl_key, osm_key, regular_key});\n\n    /* Press OSL key */\n    EXPECT_NO_REPORT(driver);\n    osl_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release OSL key */\n    EXPECT_NO_REPORT(driver);\n    osl_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press and release OSM */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key);\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code)).Times(1);\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSLWithMoAndAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key     = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  mo_key      = KeymapKey{1, 1, 0, MO(2)};\n    KeymapKey  regular_key = KeymapKey{2, 1, 1, KC_A};\n\n    set_keymap({osl_key, mo_key, regular_key});\n\n    /* Press OSL key */\n    EXPECT_NO_REPORT(driver);\n    osl_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release OSL key */\n    EXPECT_NO_REPORT(driver);\n    osl_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press MO */\n    EXPECT_NO_REPORT(driver);\n    mo_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(2));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (regular_key.report_code)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(2));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release MO */\n    EXPECT_NO_REPORT(driver);\n    mo_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    VERIFY_AND_CLEAR(driver);\n}\n\nclass OneShotLayerParametrizedTestFixture : public ::testing::WithParamInterface<uint16_t>, public OneShot {};\n\nTEST_P(OneShotLayerParametrizedTestFixture, OSLWithActionAndAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key     = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  action_key  = KeymapKey{1, 1, 0, GetParam()};\n    KeymapKey  regular_key = KeymapKey{2, 1, 1, KC_A};\n\n    set_keymap({osl_key, action_key, regular_key});\n\n    /* Tap OSL key */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osl_key);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Tag Action key */\n    EXPECT_NO_REPORT(driver);\n    tap_key(action_key);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(2));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (regular_key.report_code)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(2));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nINSTANTIATE_TEST_CASE_P(OneShotLayerTests, OneShotLayerParametrizedTestFixture, ::testing::Values(TG(2), TO(2)));\n\nTEST_F(OneShot, OSLWithDFAndAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key     = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  df_key      = KeymapKey{1, 1, 0, DF(2)};\n    KeymapKey  regular_key = KeymapKey{2, 1, 1, KC_A};\n\n    set_keymap({osl_key, df_key, regular_key});\n\n    layer_state_t default_layer_state_bak = default_layer_state;\n\n    /* Tap OSL key */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osl_key);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press DF key */\n    EXPECT_NO_REPORT(driver);\n    df_key.press();\n    run_one_scan_loop();\n    EXPECT_EQ(default_layer_state, 0b001);\n\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release DF key */\n    EXPECT_NO_REPORT(driver);\n    df_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(default_layer_state, 0b100);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (regular_key.report_code)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    default_layer_state = default_layer_state_bak;\n}\n\nTEST_F(OneShot, OSLChainingTwoOSLsAndAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl1_key    = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  osl2_key    = KeymapKey{1, 1, 0, OSL(2)};\n    KeymapKey  regular_key = KeymapKey{2, 1, 1, KC_A};\n\n    set_keymap({osl1_key, osl2_key, regular_key});\n\n    /* Press and release first OSL key */\n    EXPECT_NO_REPORT(driver);\n    osl1_key.press();\n    run_one_scan_loop();\n    osl1_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press and release second OSL */\n    EXPECT_NO_REPORT(driver);\n    osl2_key.press();\n    run_one_scan_loop();\n    osl2_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(2));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (regular_key.report_code)).Times(1);\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSLWithShortLT) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  lt_key  = KeymapKey(1, 1, 0, LT(2, KC_A));\n\n    set_keymap({osl_key, lt_key});\n\n    /* Tap OSL key */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osl_key);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Tap LT key. */\n    EXPECT_REPORT(driver, (lt_key.report_code)).Times(1);\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(lt_key);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(0));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSLWithLongLTAndRegularKey) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key     = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  lt_key      = KeymapKey(1, 1, 0, LT(2, KC_A));\n    KeymapKey  regular_key = KeymapKey(2, 1, 1, KC_B);\n\n    set_keymap({osl_key, lt_key, regular_key});\n\n    /* Tap OSL key */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osl_key);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press LT key. */\n    EXPECT_NO_REPORT(driver);\n    lt_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Idle for tapping term of mod tap hold key. */\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n    EXPECT_TRUE(layer_state_is(2));\n\n    /* Press regular key. */\n    EXPECT_REPORT(driver, (regular_key.report_code)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSLWithShortModTapKeyAndRegularKey) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key          = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  mod_tap_hold_key = KeymapKey(1, 1, 0, SFT_T(KC_P));\n    KeymapKey  regular_key      = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({osl_key, mod_tap_hold_key, regular_key});\n\n    /* Tap OSL key */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osl_key);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key. */\n    EXPECT_REPORT(driver, (regular_key.report_code));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSLWithLongModTapKeyAndRegularKey) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key          = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  mod_tap_hold_key = KeymapKey(1, 1, 0, SFT_T(KC_P));\n    KeymapKey  regular_key      = KeymapKey(1, 2, 0, KC_A);\n\n    set_keymap({osl_key, mod_tap_hold_key, regular_key});\n\n    /* Tap OSL key */\n    EXPECT_NO_REPORT(driver);\n    tap_key(osl_key);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Idle for tapping term of mod tap hold key. */\n    EXPECT_REPORT(driver, (KC_LSFT));\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key. */\n    EXPECT_REPORT(driver, (regular_key.report_code)).Times(1);\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/basic/test_tapping.cpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass Tapping : public TestFixture {};\n\nTEST_F(Tapping, TapA_SHFT_T_KeyReportsKey) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_shift_hold_p_tap = KeymapKey(0, 7, 0, SFT_T(KC_P));\n\n    set_keymap({key_shift_hold_p_tap});\n\n    // Tapping keys does nothing on press\n    key_shift_hold_p_tap.press();\n    EXPECT_NO_REPORT(driver);\n    run_one_scan_loop();\n\n    // First we get the key press\n    key_shift_hold_p_tap.release();\n    EXPECT_REPORT(driver, (KC_P));\n\n    // Then the release\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(Tapping, HoldA_SHFT_T_KeyReportsShift) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 7, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    mod_tap_hold_key.press();\n\n    // Tapping keys does nothing on press\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n\n    EXPECT_REPORT(driver, (KC_LSFT));\n    run_one_scan_loop();\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n}\n\nTEST_F(Tapping, ANewTapWithinTappingTermIsBuggy) {\n    // See issue #1478 for more information\n    TestDriver driver;\n    InSequence s;\n    auto       key_shift_hold_p_tap = KeymapKey(0, 7, 0, SFT_T(KC_P));\n\n    set_keymap({key_shift_hold_p_tap});\n\n    // Tapping keys does nothing on press\n    key_shift_hold_p_tap.press();\n    EXPECT_NO_REPORT(driver);\n    run_one_scan_loop();\n    key_shift_hold_p_tap.release();\n\n    // First we get the key press\n    EXPECT_REPORT(driver, (KC_P));\n    // Then the release\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    // This sends KC_P, even if it should do nothing\n    key_shift_hold_p_tap.press();\n    // This test should not succed if everything works correctly\n    EXPECT_REPORT(driver, (KC_P));\n    run_one_scan_loop();\n\n    key_shift_hold_p_tap.release();\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n\n    // On the other hand, nothing is sent if we are outside the tapping term\n    key_shift_hold_p_tap.press();\n    EXPECT_NO_REPORT(driver);\n    run_one_scan_loop();\n    key_shift_hold_p_tap.release();\n\n    // First we get the key press\n    EXPECT_REPORT(driver, (KC_P));\n    // Then the release\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n\n    // Now we are geting into strange territory, as the hold registers too early here\n    // But the stranges part is:\n    // If TAPPING_TERM + 1 above is changed to TAPPING_TERM or TAPPING_TERM + 2 it doesn't\n    key_shift_hold_p_tap.press();\n    // Shouldn't be called here really\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT)).Times(1);\n    idle_for(TAPPING_TERM);\n\n    EXPECT_EMPTY_REPORT(driver);\n    key_shift_hold_p_tap.release();\n    run_one_scan_loop();\n}\n\nTEST_F(Tapping, TapA_CTL_T_KeyWhileReleasingShift) {\n    TestDriver driver;\n    InSequence s;\n    auto       shift_key        = KeymapKey(0, 7, 0, KC_LSFT);\n    auto       mod_tap_hold_key = KeymapKey(0, 8, 0, CTL_T(KC_P));\n\n    set_keymap({shift_key, mod_tap_hold_key});\n\n    shift_key.press();\n    // Shift is reported\n    EXPECT_REPORT(driver, (KC_LSFT));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    mod_tap_hold_key.press();\n    // Tapping keys does nothing on press\n    EXPECT_NO_REPORT(driver);\n    run_one_scan_loop();\n\n    shift_key.release();\n    // Releasing shift is delayed while tapping is in progress\n    EXPECT_NO_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    mod_tap_hold_key.release();\n    // Releasing mod-tap key reports the tap and releases shift\n    EXPECT_REPORT(driver, (KC_LSFT, KC_P));\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Tapping, TapA_CTL_T_KeyWhileReleasingLayer) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_key         = KeymapKey(0, 7, 0, MO(1));\n    auto       trans_key         = KeymapKey(1, 7, 0, KC_TRNS);\n    auto       mod_tap_hold_key0 = KeymapKey(0, 8, 0, CTL_T(KC_P));\n    auto       mod_tap_hold_key1 = KeymapKey(1, 8, 0, CTL_T(KC_Q));\n\n    set_keymap({layer_key, trans_key, mod_tap_hold_key0, mod_tap_hold_key1});\n\n    layer_key.press();\n    // Pressing the layer key does nothing\n    EXPECT_NO_REPORT(driver);\n    run_one_scan_loop();\n\n    mod_tap_hold_key1.press();\n    // Tapping layer 1 mod-tap key does nothing on press\n    EXPECT_NO_REPORT(driver);\n    run_one_scan_loop();\n\n    layer_key.release();\n    // Releasing layer is delayed while tapping is in progress\n    EXPECT_NO_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    mod_tap_hold_key1.release();\n    // Releasing mod-tap key reports the tap of the layer 1 key\n    // If delayed layer release is broken, this reports the layer 0 key\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/caps_word/auto_shift/config.h",
    "content": "// Copyright 2022 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define TAPPING_TERM 200\n#define AUTO_SHIFT_TIMEOUT 150\n"
  },
  {
    "path": "tests/caps_word/auto_shift/retro_shift/config.h",
    "content": "// Copyright 2022 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define TAPPING_TERM 200\n#define AUTO_SHIFT_TIMEOUT 150\n#define RETRO_SHIFT 500\n"
  },
  {
    "path": "tests/caps_word/auto_shift/retro_shift/test.mk",
    "content": "# Copyright 2022 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nCAPS_WORD_ENABLE = yes\nAUTO_SHIFT_ENABLE = yes\n\n"
  },
  {
    "path": "tests/caps_word/auto_shift/retro_shift/test_caps_word_retroshift.cpp",
    "content": "// Copyright 2022 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\n// Allow reports with no keys or only KC_LSFT.\n// clang-format off\n#define EXPECT_EMPTY_OR_LSFT(driver)              \\\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf( \\\n            KeyboardReport(),                     \\\n            KeyboardReport(KC_LSFT))))\n// clang-format on\n\nbool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\nusing ::testing::_;\nusing ::testing::AnyNumber;\nusing ::testing::AnyOf;\nusing ::testing::InSequence;\n\nclass CapsWord : public TestFixture {\n   public:\n    void SetUp() override {\n        caps_word_off();\n    }\n};\n\n// Tests that with Auto Shift, letter keys are shifted by Caps Word\n// regardless of whether they are released before AUTO_SHIFT_TIMEOUT.\nTEST_F(CapsWord, AutoShiftKeys) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_spc(0, 1, 0, KC_SPC);\n    set_keymap({key_a, key_spc});\n\n    EXPECT_EMPTY_OR_LSFT(driver).Times(AnyNumber());\n    { // Expect: \"A, A, space, a\".\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_SPC));\n        EXPECT_REPORT(driver, (KC_A));\n    }\n\n    // Turn on Caps Word and type \"A (quick tap), A (long press), space, A\".\n    caps_word_on();\n\n    tap_key(key_a);                         // Tap A quickly.\n    tap_key(key_a, AUTO_SHIFT_TIMEOUT + 1); // Long press A.\n    tap_key(key_spc);\n    tap_key(key_a);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Test Caps Word + Auto Shift where keys A and B are rolled.\nTEST_F(CapsWord, AutoShiftRolledShiftedKeys) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_b(0, 0, 1, KC_B);\n    set_keymap({key_a, key_b});\n\n    EXPECT_EMPTY_OR_LSFT(driver).Times(AnyNumber());\n    { // Expect: \"A, B, A, B\".\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n    }\n\n    caps_word_on();\n\n    key_a.press(); // Overlapping taps: A down, B down, A up, B up.\n    run_one_scan_loop();\n    key_b.press();\n    run_one_scan_loop();\n    key_a.release();\n    run_one_scan_loop();\n    key_b.release();\n    run_one_scan_loop();\n\n    key_a.press(); // Nested taps: A down, B down, B up, A up.\n    run_one_scan_loop();\n    key_b.press();\n    run_one_scan_loop();\n    key_b.release();\n    run_one_scan_loop();\n    key_a.release();\n    run_one_scan_loop();\n\n    caps_word_off();\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Tests that with tap-hold keys with Retro Shift, letter keys are shifted by\n// Caps Word regardless of whether they are retroshifted.\nTEST_F(CapsWord, RetroShiftKeys) {\n    TestDriver driver;\n    KeymapKey  key_modtap_a(0, 0, 0, LCTL_T(KC_A));\n    KeymapKey  key_layertap_b(0, 1, 0, LT(1, KC_B));\n    set_keymap({key_modtap_a, key_layertap_b});\n\n    EXPECT_EMPTY_OR_LSFT(driver).Times(AnyNumber());\n    { // Expect: \"B, A, B, A\".\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    }\n\n    // Turn on Caps Word and type \"B, A (long press), B (long press), A\".\n    caps_word_on();\n\n    tap_key(key_layertap_b);                   // Tap B quickly.\n    tap_key(key_modtap_a, TAPPING_TERM + 1);   // Long press A.\n    tap_key(key_layertap_b, TAPPING_TERM + 1); // Long press B.\n    tap_key(key_modtap_a);                     // Tap A quickly.\n\n    EXPECT_EQ(is_caps_word_on(), true);\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/caps_word/auto_shift/test.mk",
    "content": "# Copyright 2022 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nCAPS_WORD_ENABLE = yes\nAUTO_SHIFT_ENABLE = yes\n\n"
  },
  {
    "path": "tests/caps_word/auto_shift/test_caps_word_autoshift.cpp",
    "content": "// Copyright 2022 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing ::testing::_;\nusing ::testing::AnyNumber;\nusing ::testing::AnyOf;\nusing ::testing::InSequence;\n\nclass CapsWord : public TestFixture {\n   public:\n    void SetUp() override {\n        caps_word_off();\n    }\n};\n\n// Tests that with Auto Shift, letter keys are shifted by Caps Word\n// regardless of whether they are released before AUTO_SHIFT_TIMEOUT.\nTEST_F(CapsWord, AutoShiftKeys) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_spc(0, 1, 0, KC_SPC);\n    set_keymap({key_a, key_spc});\n\n    // Allow any number of reports with no keys or only KC_LSFT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    { // Expect: \"A, A, space, a\".\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_SPC));\n        EXPECT_REPORT(driver, (KC_A));\n    }\n\n    // Turn on Caps Word and type \"A (quick tap), A (long press), space, A\".\n    caps_word_on();\n\n    tap_key(key_a);                         // Tap A quickly.\n    tap_key(key_a, AUTO_SHIFT_TIMEOUT + 1); // Long press A.\n    tap_key(key_spc);\n    tap_key(key_a);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n"
  },
  {
    "path": "tests/caps_word/caps_word_combo/config.h",
    "content": "// Copyright 2022 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define TAPPING_TERM 200\n"
  },
  {
    "path": "tests/caps_word/caps_word_combo/test.mk",
    "content": "# Copyright 2022 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nCAPS_WORD_ENABLE = yes\nCOMBO_ENABLE = yes\nAUTO_SHIFT_ENABLE = yes\n\nINTROSPECTION_KEYMAP_C = test_combos.c\n"
  },
  {
    "path": "tests/caps_word/caps_word_combo/test_caps_word_combo.cpp",
    "content": "// Copyright 2022 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n// Test Caps Word + Combos, with and without Auto Shift.\n\n#include <algorithm>\n#include <numeric>\n#include <vector>\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\n// Allow reports with no keys or only KC_LSFT.\n// clang-format off\n#define EXPECT_EMPTY_OR_LSFT(driver)              \\\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf( \\\n            KeyboardReport(),                     \\\n            KeyboardReport(KC_LSFT))))\n// clang-format on\n\nusing ::testing::AnyNumber;\nusing ::testing::AnyOf;\nusing ::testing::InSequence;\nusing ::testing::TestParamInfo;\n\nnamespace {\n\n// To test combos thorougly, we test them with pressing the chord keys with\n// a few different orders and timings.\nstruct TestParams {\n    std::string name;\n    bool        autoshift_on;\n\n    static const std::string& GetName(const TestParamInfo<TestParams>& info) {\n        return info.param.name;\n    }\n};\n\nclass CapsWord : public ::testing::WithParamInterface<TestParams>, public TestFixture {\n   public:\n    void SetUp() override {\n        caps_word_off();\n        if (GetParam().autoshift_on) {\n            autoshift_enable();\n        } else {\n            autoshift_disable();\n        }\n    }\n};\n\n// Test pressing the keys in a combo with different orders and timings.\nTEST_P(CapsWord, SingleCombo) {\n    TestDriver driver;\n    KeymapKey  key_b(0, 0, 1, KC_B);\n    KeymapKey  key_c(0, 0, 2, KC_C);\n    set_keymap({key_b, key_c});\n\n    EXPECT_EMPTY_OR_LSFT(driver).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_X));\n\n    caps_word_on();\n    tap_combo({key_b, key_c});\n\n    EXPECT_TRUE(is_caps_word_on());\n    caps_word_off();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Test a longer 4-key combo.\nTEST_P(CapsWord, LongerCombo) {\n    TestDriver driver;\n    KeymapKey  key_f(0, 0, 0, KC_F);\n    KeymapKey  key_g(0, 0, 1, KC_G);\n    KeymapKey  key_h(0, 0, 2, KC_H);\n    KeymapKey  key_i(0, 0, 3, KC_I);\n    set_keymap({key_f, key_g, key_h, key_i});\n\n    EXPECT_EMPTY_OR_LSFT(driver).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_W));\n\n    caps_word_on();\n    tap_combo({key_f, key_g, key_h, key_i});\n\n    EXPECT_TRUE(is_caps_word_on());\n    caps_word_off();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Test with two overlapping combos on regular keys:\n// KC_A + KC_B = KC_SPC,\n// KC_B + KC_C = KC_X.\nTEST_P(CapsWord, ComboRegularKeys) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_b(0, 0, 1, KC_B);\n    KeymapKey  key_c(0, 0, 2, KC_C);\n    KeymapKey  key_1(0, 0, 3, KC_1);\n    set_keymap({key_a, key_b, key_c, key_1});\n\n    EXPECT_EMPTY_OR_LSFT(driver).Times(AnyNumber());\n    { // Expect: \"A, B, 1, X, 1, C, space, a\".\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n        EXPECT_REPORT(driver, (KC_1));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_X));\n        EXPECT_REPORT(driver, (KC_1));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_C));\n        EXPECT_REPORT(driver, (KC_SPC));\n        EXPECT_REPORT(driver, (KC_A));\n    }\n\n    caps_word_on();\n    tap_key(key_a);\n    tap_key(key_b);\n    tap_key(key_1);\n    tap_combo({key_b, key_c}); // BC combo types \"x\".\n    tap_key(key_1);\n    tap_key(key_c);\n    tap_combo({key_a, key_b}); // AB combo types space.\n    tap_key(key_a);\n\n    EXPECT_FALSE(is_caps_word_on());\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Test where combo chords involve tap-hold keys:\n// KC_A + LCTL_T(KC_D) = KC_Y,\n// LCTL_T(KC_D) + LT(1, KC_E) = KC_Z,\nTEST_P(CapsWord, ComboModTapKey) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_modtap_d(0, 0, 1, LCTL_T(KC_D));\n    KeymapKey  key_layertap_e(0, 0, 2, LT(1, KC_E));\n    set_keymap({key_a, key_modtap_d, key_layertap_e});\n\n    EXPECT_EMPTY_OR_LSFT(driver).Times(AnyNumber());\n    { // Expect: \"A, D, E, Y, Z\".\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_D));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_E));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_Y));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_Z));\n    }\n\n    caps_word_on();\n    tap_key(key_a);\n    tap_key(key_modtap_d);\n    tap_key(key_layertap_e);\n    tap_combo({key_a, key_modtap_d});          // AD combo types \"y\".\n    tap_combo({key_modtap_d, key_layertap_e}); // DE combo types \"z\".\n\n    EXPECT_TRUE(is_caps_word_on());\n    caps_word_off();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    Combos,\n    CapsWord,\n    ::testing::Values(\n        TestParams{\"AutoshiftDisabled\", false},\n        TestParams{\"AutoshiftEnabled\", true}\n        ),\n    TestParams::GetName\n    );\n// clang-format on\n\n} // namespace\n"
  },
  {
    "path": "tests/caps_word/caps_word_combo/test_combos.c",
    "content": "#include <quantum.h>\n\n// Define some combos to use for the test, including overlapping combos and\n// combos that chord tap-hold keys.\nenum combo_events { AB_COMBO, BC_COMBO, AD_COMBO, DE_COMBO, FGHI_COMBO };\n\nconst uint16_t ab_combo[] PROGMEM   = {KC_A, KC_B, COMBO_END};\nconst uint16_t bc_combo[] PROGMEM   = {KC_B, KC_C, COMBO_END};\nconst uint16_t ad_combo[] PROGMEM   = {KC_A, LCTL_T(KC_D), COMBO_END};\nconst uint16_t de_combo[] PROGMEM   = {LCTL_T(KC_D), LT(1, KC_E), COMBO_END};\nconst uint16_t fghi_combo[] PROGMEM = {KC_F, KC_G, KC_H, KC_I, COMBO_END};\n\n// clang-format off\ncombo_t key_combos[] = {\n    [AB_COMBO] = COMBO(ab_combo, KC_SPC),  // KC_A + KC_B = KC_SPC\n    [BC_COMBO] = COMBO(bc_combo, KC_X),    // KC_B + KC_C = KC_X\n    [AD_COMBO] = COMBO(ad_combo, KC_Y),    // KC_A + LCTL_T(KC_D) = KC_Y\n    [DE_COMBO] = COMBO(de_combo, KC_Z),    // LCTL_T(KC_D) + LT(1, KC_E) = KC_Z\n    [FGHI_COMBO] = COMBO(fghi_combo, KC_W) // KC_F + KC_G + KC_H + KC_I = KC_W\n};\n"
  },
  {
    "path": "tests/caps_word/caps_word_invert_on_shift/config.h",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define CAPS_WORD_INVERT_ON_SHIFT\n#define PERMISSIVE_HOLD\n"
  },
  {
    "path": "tests/caps_word/caps_word_invert_on_shift/test.mk",
    "content": "# Copyright 2023 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nCAPS_WORD_ENABLE = yes\n\n"
  },
  {
    "path": "tests/caps_word/caps_word_invert_on_shift/test_caps_word_invert_on_shift.cpp",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing ::testing::_;\nusing ::testing::AnyNumber;\nusing ::testing::AnyOf;\nusing ::testing::InSequence;\nusing ::testing::TestParamInfo;\n\nnamespace {\n\nstruct ShiftKeyParams {\n    std::string name;\n    uint16_t    keycode;\n    uint16_t    report_shift_code;\n\n    static const std::string& GetName(const TestParamInfo<ShiftKeyParams>& info) {\n        return info.param.name;\n    }\n};\n\nclass CapsWordInvertOnShift : public ::testing::WithParamInterface<ShiftKeyParams>, public TestFixture {\n    void SetUp() override {\n        caps_word_off();\n    }\n};\n\n// With Caps Word on, type \"A, 4, Shift(A, 4, A), A, Shift(A), 4\".\nTEST_P(CapsWordInvertOnShift, ShiftWithinWord) {\n    TestDriver driver;\n    KeymapKey  key_shift(0, 0, 0, GetParam().keycode);\n    KeymapKey  key_a(0, 1, 0, KC_A);\n    KeymapKey  key_4(0, 2, 0, KC_4);\n    set_keymap({key_shift, key_a, key_4});\n\n    // Allow any number of reports with no keys or only KC_LSFT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    { // Expect: \"A4a$aAa4\"\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_4));\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_4));\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_REPORT(driver, (KC_4));\n    }\n\n    caps_word_on();\n    tap_keys(key_a, key_4); // Type \"A, 4\".\n\n    key_shift.press(); // Type \"Shift(A, 4, A)\".\n    run_one_scan_loop();\n    tap_keys(key_a, key_4, key_a);\n    key_shift.release();\n    run_one_scan_loop();\n\n    tap_key(key_a); // Type \"A\".\n\n    key_shift.press(); // Type \"Shift(A)\".\n    run_one_scan_loop();\n    tap_key(key_a);\n    key_shift.release();\n    run_one_scan_loop();\n\n    tap_key(key_4); // Type \"4\".\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_P(CapsWordInvertOnShift, ShiftHeldAtWordEnd) {\n    TestDriver driver;\n    KeymapKey  key_shift(0, 0, 0, GetParam().keycode);\n    KeymapKey  key_a(0, 1, 0, KC_A);\n    KeymapKey  key_slsh(0, 2, 0, KC_SLSH);\n    set_keymap({key_shift, key_a, key_slsh});\n\n    // Allow any number of reports with no keys or only KC_LSFT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_RSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    { // Expect: \"Aa?A\"\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_REPORT(driver, (GetParam().report_shift_code, KC_SLSH));\n        EXPECT_REPORT(driver, (GetParam().report_shift_code, KC_A));\n    }\n\n    caps_word_on();\n    tap_key(key_a);\n\n    key_shift.press(); // Press Shift.\n    run_one_scan_loop();\n\n    EXPECT_EQ(get_mods(), 0);\n\n    tap_key(key_a);\n    tap_key(key_slsh); // Tap '/' key, which is word breaking, ending Caps Word.\n\n    EXPECT_FALSE(is_caps_word_on());\n    EXPECT_EQ(get_mods(), MOD_BIT(GetParam().report_shift_code));\n\n    tap_key(key_a);\n    key_shift.release(); // Release Shift.\n    run_one_scan_loop();\n\n    EXPECT_EQ(get_mods(), 0);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_P(CapsWordInvertOnShift, TwoShiftsHeld) {\n    TestDriver driver;\n    KeymapKey  key_shift1(0, 0, 0, GetParam().keycode);\n    KeymapKey  key_shift2(0, 1, 0, GetParam().report_shift_code);\n    KeymapKey  key_a(0, 2, 0, KC_A);\n    KeymapKey  key_slsh(0, 3, 0, KC_SLSH);\n    set_keymap({key_shift1, key_shift2, key_a, key_slsh});\n\n    // Allow any number of reports with no keys or only KC_LSFT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_RSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    { // Expect: \"Aa?a\"\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_REPORT(driver, (GetParam().report_shift_code, KC_SLSH));\n        EXPECT_REPORT(driver, (KC_A));\n    }\n\n    caps_word_on();\n    tap_key(key_a);\n\n    key_shift1.press(); // Press shift1.\n    run_one_scan_loop();\n\n    EXPECT_EQ(get_mods(), 0);\n\n    tap_key(key_a);\n    tap_key(key_slsh); // Tap '/' key, which is word breaking, ending Caps Word.\n\n    EXPECT_FALSE(is_caps_word_on());\n    EXPECT_EQ(get_mods(), MOD_BIT(GetParam().report_shift_code));\n\n    key_shift2.press(); // Press shift2.\n    run_one_scan_loop();\n\n    EXPECT_EQ(get_mods(), MOD_BIT(GetParam().report_shift_code));\n\n    key_shift1.release(); // Release shift1.\n    run_one_scan_loop();\n\n    EXPECT_EQ(get_mods(), 0);\n    tap_key(key_a);\n\n    key_shift2.release(); // Release shift2.\n    run_one_scan_loop();\n\n    EXPECT_EQ(get_mods(), 0);\n    VERIFY_AND_CLEAR(driver);\n}\n\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    Shifts,\n    CapsWordInvertOnShift,\n    ::testing::Values(\n        ShiftKeyParams{\"KC_LSFT\", KC_LSFT, KC_LSFT},\n        ShiftKeyParams{\"KC_RSFT\", KC_RSFT, KC_RSFT},\n        ShiftKeyParams{\"LSFT_T\", LSFT_T(KC_A), KC_LSFT},\n        ShiftKeyParams{\"RSFT_T\", RSFT_T(KC_A), KC_RSFT},\n        ShiftKeyParams{\"OSM_LSFT\", OSM(MOD_LSFT), KC_LSFT},\n        ShiftKeyParams{\"OSM_RSFT\", OSM(MOD_RSFT), KC_RSFT}\n      ),\n    ShiftKeyParams::GetName\n    );\n// clang-format on\n\n} // namespace\n"
  },
  {
    "path": "tests/caps_word/config.h",
    "content": "// Copyright 2022 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define BOTH_SHIFTS_TURNS_ON_CAPS_WORD\n#define DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD\n"
  },
  {
    "path": "tests/caps_word/test.mk",
    "content": "# Copyright 2022 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nCAPS_WORD_ENABLE = yes\nCOMMAND_ENABLE = no\nLAYER_LOCK_ENABLE = yes\nSPACE_CADET_ENABLE = yes\nTRI_LAYER_ENABLE = yes\n\n"
  },
  {
    "path": "tests/caps_word/test_caps_word.cpp",
    "content": "// Copyright 2022 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing ::testing::_;\nusing ::testing::AnyNumber;\nusing ::testing::AnyOf;\nusing ::testing::InSequence;\nusing ::testing::TestParamInfo;\n\nnamespace {\n\nbool press_user_default(uint16_t keycode) {\n    switch (keycode) {\n        // Keycodes that continue Caps Word, with shift applied.\n        case KC_A ... KC_Z:\n        case KC_MINS:\n            add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key.\n            return true;\n\n        // Keycodes that continue Caps Word, without shifting.\n        case KC_1 ... KC_0:\n        case KC_BSPC:\n        case KC_DEL:\n        case KC_UNDS:\n            return true;\n\n        default:\n            return false; // Deactivate Caps Word.\n    }\n}\n\nuint16_t passed_keycode;\nbool     press_user_save_passed_keycode(uint16_t keycode) {\n    passed_keycode = keycode;\n    return true;\n}\n\nbool (*press_user_fun)(uint16_t) = press_user_default;\n\nextern \"C\" {\nbool caps_word_press_user(uint16_t keycode) {\n    return press_user_fun(keycode);\n}\n} // extern \"C\"\n\nclass CapsWord : public TestFixture {\n   public:\n    void SetUp() override {\n        caps_word_off();\n        press_user_fun = press_user_default;\n    }\n};\n\n// Tests caps_word_on(), _off(), and _toggle() functions.\nTEST_F(CapsWord, OnOffToggleFuns) {\n    TestDriver driver;\n\n    EXPECT_EQ(is_caps_word_on(), false);\n\n    caps_word_on();\n    EXPECT_EQ(is_caps_word_on(), true);\n    caps_word_on();\n    EXPECT_EQ(is_caps_word_on(), true);\n\n    caps_word_off();\n    EXPECT_EQ(is_caps_word_on(), false);\n    caps_word_off();\n    EXPECT_EQ(is_caps_word_on(), false);\n\n    caps_word_toggle();\n    EXPECT_EQ(is_caps_word_on(), true);\n    caps_word_toggle();\n    EXPECT_EQ(is_caps_word_on(), false);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Tests the default `caps_word_press_user()` function.\nTEST_F(CapsWord, DefaultCapsWordPressUserFun) {\n    // Spot check some keycodes that continue Caps Word, with shift applied.\n    for (uint16_t keycode : {KC_A, KC_B, KC_Z, KC_MINS}) {\n        SCOPED_TRACE(\"keycode: \" + testing::PrintToString(keycode));\n        clear_weak_mods();\n        EXPECT_TRUE(caps_word_press_user(keycode));\n        EXPECT_EQ(get_weak_mods(), MOD_BIT(KC_LSFT));\n    }\n\n    // Some keycodes that continue Caps Word, without shifting.\n    for (uint16_t keycode : {KC_1, KC_9, KC_0, KC_BSPC, KC_DEL}) {\n        SCOPED_TRACE(\"keycode: \" + testing::PrintToString(keycode));\n        clear_weak_mods();\n        EXPECT_TRUE(caps_word_press_user(keycode));\n        EXPECT_EQ(get_weak_mods(), 0);\n    }\n\n    // Some keycodes that turn off Caps Word.\n    for (uint16_t keycode : {KC_SPC, KC_DOT, KC_COMM, KC_TAB, KC_ESC, KC_ENT}) {\n        SCOPED_TRACE(\"keycode: \" + testing::PrintToString(keycode));\n        EXPECT_FALSE(caps_word_press_user(keycode));\n    }\n}\n\n// Tests that `QK_CAPS_WORD_TOGGLE` key toggles Caps Word.\nTEST_F(CapsWord, CapswrdKey) {\n    TestDriver driver;\n    KeymapKey  key_capswrd(0, 0, 0, QK_CAPS_WORD_TOGGLE);\n    set_keymap({key_capswrd});\n\n    // No keyboard reports should be sent.\n    EXPECT_NO_REPORT(driver);\n\n    tap_key(key_capswrd); // Tap the QK_CAPS_WORD_TOGGLE key.\n    EXPECT_EQ(is_caps_word_on(), true);\n\n    tap_key(key_capswrd); // Tap the QK_CAPS_WORD_TOGGLE key again.\n    EXPECT_EQ(is_caps_word_on(), false);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Tests that being idle for CAPS_WORD_IDLE_TIMEOUT turns off Caps Word.\nTEST_F(CapsWord, IdleTimeout) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    set_keymap({key_a});\n\n    // Allow any number of reports with no keys or only KC_LSFT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    // Expect \"Shift+A\".\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n\n    // Turn on Caps Word and tap \"A\".\n    caps_word_on();\n    tap_key(key_a);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(CAPS_WORD_IDLE_TIMEOUT);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Caps Word should be off and mods should be clear.\n    EXPECT_EQ(is_caps_word_on(), false);\n    EXPECT_EQ(get_mods() | get_weak_mods(), 0);\n\n    // Expect unshifted \"A\".\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Tests that typing \"A, 4, A, 4\" produces \"Shift+A, 4, Shift+A, 4\".\nTEST_F(CapsWord, ShiftsLettersButNotDigits) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_4(0, 1, 0, KC_4);\n    set_keymap({key_a, key_4});\n\n    // Allow any number of reports with no keys or only KC_LSFT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    { // Expect: \"Shift+A, 4, Shift+A, 4\".\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_4));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_4));\n    }\n\n    // Turn on Caps Word and tap \"A, 4, A, 4\".\n    caps_word_on();\n    tap_keys(key_a, key_4, key_a, key_4);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Tests that typing \"A, Space, A\" produces \"Shift+A, Space, A\".\nTEST_F(CapsWord, SpaceTurnsOffCapsWord) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_spc(0, 1, 0, KC_SPC);\n    set_keymap({key_a, key_spc});\n\n    // Allow any number of reports with no keys or only KC_LSFT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    { // Expect: \"Shift+A, Space, A\".\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_SPC));\n        EXPECT_REPORT(driver, (KC_A));\n    }\n\n    // Turn on Caps Word and tap \"A, Space, A\".\n    caps_word_on();\n    tap_keys(key_a, key_spc, key_a);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Tests that typing \"AltGr + A\" produces \"Shift + AltGr + A\".\nTEST_F(CapsWord, ShiftsAltGrSymbols) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_altgr(0, 1, 0, KC_RALT);\n    set_keymap({key_a, key_altgr});\n\n    // Allow any number of reports with no keys or only modifiers.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_RALT),\n                KeyboardReport(KC_LSFT, KC_RALT))))\n        .Times(AnyNumber());\n    // Expect \"Shift + AltGr + A\".\n    EXPECT_REPORT(driver, (KC_LSFT, KC_RALT, KC_A));\n    // clang-format on\n\n    // Turn on Caps Word and type \"AltGr + A\".\n    caps_word_on();\n\n    key_altgr.press();\n    run_one_scan_loop();\n    tap_key(key_a);\n    run_one_scan_loop();\n    key_altgr.release();\n    run_one_scan_loop();\n\n    idle_for(CAPS_WORD_IDLE_TIMEOUT);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Tests typing \"AltGr + A\" using a mod-tap key.\nTEST_F(CapsWord, ShiftsModTapAltGrSymbols) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_altgr_t(0, 1, 0, RALT_T(KC_B));\n    set_keymap({key_a, key_altgr_t});\n\n    // Allow any number of reports with no keys or only modifiers.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_RALT),\n                KeyboardReport(KC_LSFT, KC_RALT))))\n        .Times(AnyNumber());\n    // Expect \"Shift + AltGr + A\".\n    EXPECT_REPORT(driver, (KC_LSFT, KC_RALT, KC_A));\n    // clang-format on\n\n    // Turn on Caps Word and type \"AltGr + A\".\n    caps_word_on();\n\n    key_altgr_t.press();\n    idle_for(TAPPING_TERM + 1);\n    tap_key(key_a);\n    run_one_scan_loop();\n    key_altgr_t.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(is_caps_word_on());\n\n    idle_for(CAPS_WORD_IDLE_TIMEOUT);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nstruct CapsWordPressUserParams {\n    std::string name;\n    uint16_t    keycode;\n    uint16_t    delay_ms;\n    uint16_t    expected_passed_keycode;\n    bool        continues_caps_word;\n\n    static const std::string& GetName(const TestParamInfo<CapsWordPressUserParams>& info) {\n        return info.param.name;\n    }\n};\n\nclass CapsWordPressUser : public ::testing::WithParamInterface<CapsWordPressUserParams>, public CapsWord {\n    void SetUp() override {\n        caps_word_on();\n        passed_keycode = KC_NO;\n        press_user_fun = press_user_save_passed_keycode;\n    }\n};\n\n// Tests keycodes passed to caps_word_press_user() function for various keys.\nTEST_P(CapsWordPressUser, KeyCode) {\n    TestDriver driver;\n    KeymapKey  key(0, 0, 0, GetParam().keycode);\n    set_keymap({key});\n\n    EXPECT_ANY_REPORT(driver).Times(AnyNumber());\n    tap_key(key, GetParam().delay_ms);\n\n    EXPECT_EQ(passed_keycode, GetParam().expected_passed_keycode);\n    EXPECT_EQ(is_caps_word_on(), GetParam().continues_caps_word);\n    clear_oneshot_mods();\n    VERIFY_AND_CLEAR(driver);\n}\n\nconst uint16_t LT_1_KC_A = LT(1, KC_A);\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    PressUser,\n    CapsWordPressUser,\n    ::testing::Values(\n        CapsWordPressUserParams{\n            \"KC_A\", KC_A, 1, KC_A, true},\n        CapsWordPressUserParams{\n            \"KC_HASH\", KC_HASH, 1, KC_HASH, true},\n        CapsWordPressUserParams{\n            \"KC_LSFT\", KC_LSFT, 1, KC_LSFT, true},\n        CapsWordPressUserParams{\n            \"KC_RSFT\", KC_RSFT, 1, KC_RSFT, true},\n        CapsWordPressUserParams{\n            \"LSFT_T_tapped\", LSFT_T(KC_A), 1, KC_A, true},\n        CapsWordPressUserParams{\n            \"LSFT_T_held\", LSFT_T(KC_A), TAPPING_TERM + 1, KC_LSFT, true},\n        CapsWordPressUserParams{\n            \"RSFT_T_held\", RSFT_T(KC_A), TAPPING_TERM + 1, KC_RSFT, true},\n        CapsWordPressUserParams{\n            \"RSA_T_held\", RSA_T(KC_A), TAPPING_TERM + 1, RSFT(KC_RALT), true},\n        // Holding a mod-tap other than Shift or AltGr stops Caps Word.\n        CapsWordPressUserParams{\n            \"LCTL_T_held\", LCTL_T(KC_A), TAPPING_TERM + 1, KC_NO, false},\n        CapsWordPressUserParams{\n            \"LALT_T_held\", LALT_T(KC_A), TAPPING_TERM + 1, KC_NO, false},\n        CapsWordPressUserParams{\n            \"LGUI_T_held\", LGUI_T(KC_A), TAPPING_TERM + 1, KC_NO, false},\n        // Layer keys are ignored and continue Caps Word.\n        CapsWordPressUserParams{\n            \"MO\", MO(1), 1, KC_NO, true},\n        CapsWordPressUserParams{\n            \"TO\", TO(1), 1, KC_NO, true},\n        CapsWordPressUserParams{\n            \"TG\", TG(1), 1, KC_NO, true},\n        CapsWordPressUserParams{\n            \"TT\", TT(1), 1, KC_NO, true},\n        CapsWordPressUserParams{\n            \"OSL\", OSL(1), 1, KC_NO, true},\n        CapsWordPressUserParams{\n            \"LT_held\", LT_1_KC_A, TAPPING_TERM + 1, KC_NO, true},\n        // Tri-Layer keys are ignored and continue Caps Word.\n        CapsWordPressUserParams{\n            \"TL_LOWR\", TL_LOWR, 1, KC_NO, true},\n        CapsWordPressUserParams{\n            \"TL_UPPR\", TL_UPPR, 1, KC_NO, true},\n        // AltGr keys are ignored and continue Caps Word.\n        CapsWordPressUserParams{\n            \"KC_RALT\", KC_RALT, 1, KC_NO, true},\n        CapsWordPressUserParams{\n            \"OSM_MOD_RALT\", OSM(MOD_RALT), 1, KC_NO, true},\n        CapsWordPressUserParams{\n            \"RALT_T_held\", RALT_T(KC_A), TAPPING_TERM + 1, KC_NO, true}\n        ),\n    CapsWordPressUserParams::GetName\n    );\n// clang-format on\n\nstruct CapsWordBothShiftsParams {\n    std::string name;\n    uint16_t    left_shift_keycode;\n    uint16_t    right_shift_keycode;\n\n    static const std::string& GetName(const TestParamInfo<CapsWordBothShiftsParams>& info) {\n        return info.param.name;\n    }\n};\n\n// Tests the BOTH_SHIFTS_TURNS_ON_CAPS_WORD method to turn on Caps Word.\nclass CapsWordBothShifts : public ::testing::WithParamInterface<CapsWordBothShiftsParams>, public CapsWord {};\n\n// Pressing shifts as \"Left down, Right down, Left up, Right up\".\nTEST_P(CapsWordBothShifts, PressLRLR) {\n    TestDriver driver;\n    KeymapKey  left_shift(0, 0, 0, GetParam().left_shift_keycode);\n    KeymapKey  right_shift(0, 1, 0, GetParam().right_shift_keycode);\n    set_keymap({left_shift, right_shift});\n\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_RSFT),\n                KeyboardReport(KC_LSFT, KC_RSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    EXPECT_EQ(is_caps_word_on(), false);\n\n    left_shift.press(); // Press both shifts.\n    run_one_scan_loop();\n    right_shift.press();\n\n    // For mod-tap, wait for the tapping term.\n    if (left_shift.code == LSFT_T(KC_A)) {\n        idle_for(TAPPING_TERM);\n    }\n\n    run_one_scan_loop();\n    left_shift.release(); // Release both.\n    run_one_scan_loop();\n    right_shift.release();\n    run_one_scan_loop();\n\n    EXPECT_EQ(is_caps_word_on(), true);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Pressing shifts as \"Left down, Right down, Right up, Left up\".\nTEST_P(CapsWordBothShifts, PressLRRL) {\n    TestDriver driver;\n    KeymapKey  left_shift(0, 0, 0, GetParam().left_shift_keycode);\n    KeymapKey  right_shift(0, 1, 0, GetParam().right_shift_keycode);\n    set_keymap({left_shift, right_shift});\n\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_RSFT),\n                KeyboardReport(KC_LSFT, KC_RSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    EXPECT_EQ(is_caps_word_on(), false);\n\n    left_shift.press(); // Press both shifts.\n    run_one_scan_loop();\n    right_shift.press();\n\n    if (left_shift.code == LSFT_T(KC_A)) {\n        idle_for(TAPPING_TERM);\n    }\n    run_one_scan_loop();\n\n    right_shift.release(); // Release both.\n    run_one_scan_loop();\n    left_shift.release();\n    run_one_scan_loop();\n\n    EXPECT_EQ(is_caps_word_on(), true);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    ShiftPairs,\n    CapsWordBothShifts,\n    ::testing::Values(\n        CapsWordBothShiftsParams{\n            \"PlainShifts\", KC_LSFT, KC_RSFT},\n        CapsWordBothShiftsParams{\n            \"OneshotShifts\", OSM(MOD_LSFT), OSM(MOD_RSFT)},\n        CapsWordBothShiftsParams{\n            \"SpaceCadetShifts\", SC_LSPO, SC_RSPC},\n        CapsWordBothShiftsParams{\n            \"ModTapShifts\", LSFT_T(KC_A), RSFT_T(KC_B)}\n        ),\n    CapsWordBothShiftsParams::GetName\n    );\n// clang-format on\n\nstruct CapsWordDoubleTapShiftParams {\n    std::string name;\n    uint16_t    left_shift_keycode;\n\n    static const std::string& GetName(const TestParamInfo<CapsWordDoubleTapShiftParams>& info) {\n        return info.param.name;\n    }\n};\n\n// Tests the DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD method to turn on Caps Word.\nclass CapsWordDoubleTapShift : public ::testing::WithParamInterface<CapsWordDoubleTapShiftParams>, public CapsWord {};\n\n// Tests that double tapping activates Caps Word.\nTEST_P(CapsWordDoubleTapShift, Activation) {\n    TestDriver driver;\n    KeymapKey  left_shift(0, 0, 0, GetParam().left_shift_keycode);\n    KeymapKey  esc(0, 0, 1, KC_ESCAPE);\n    set_keymap({left_shift, esc});\n\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    EXPECT_EQ(is_caps_word_on(), false);\n\n    // Tapping shift twice within the tapping term turns on Caps Word.\n    tap_key(left_shift);\n    idle_for(TAPPING_TERM - 10);\n    tap_key(left_shift);\n\n    EXPECT_EQ(is_caps_word_on(), true);\n\n    VERIFY_AND_CLEAR(driver);\n\n    // We have to manually reset the internal state of the caps word state\n    // machine at this point. This due to imperfect test isolation which can't\n    // reset the caps word double shift timer on test case setup.\n    idle_for(CAPS_WORD_IDLE_TIMEOUT);\n\n    EXPECT_REPORT(driver, (KC_ESC));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(esc);\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Double tap doesn't count if another key is pressed between the taps.\nTEST_P(CapsWordDoubleTapShift, Interrupted) {\n    TestDriver driver;\n    KeymapKey  left_shift(0, 0, 0, GetParam().left_shift_keycode);\n    KeymapKey  key_a(0, 1, 0, KC_A);\n    set_keymap({left_shift, key_a});\n\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LSFT, KC_A))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    left_shift.press();\n    run_one_scan_loop();\n\n    tap_key(key_a); // 'A' key interrupts the double tap.\n\n    left_shift.release();\n    run_one_scan_loop();\n\n    idle_for(TAPPING_TERM - 10);\n    tap_key(left_shift);\n\n    EXPECT_EQ(is_caps_word_on(), false); // Caps Word is still off.\n    clear_oneshot_mods();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Double tap doesn't count if taps are more than tapping term apart.\nTEST_P(CapsWordDoubleTapShift, SlowTaps) {\n    TestDriver driver;\n    KeymapKey  left_shift(0, 0, 0, GetParam().left_shift_keycode);\n    set_keymap({left_shift});\n\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    tap_key(left_shift);\n    idle_for(TAPPING_TERM + 1);\n    tap_key(left_shift);\n\n    EXPECT_EQ(is_caps_word_on(), false); // Caps Word is still off.\n    clear_oneshot_mods();\n    send_keyboard_report();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    Shifts,\n    CapsWordDoubleTapShift,\n    ::testing::Values(\n        CapsWordDoubleTapShiftParams{\"PlainShift\", KC_LSFT},\n        CapsWordDoubleTapShiftParams{\"OneshotShift\", OSM(MOD_LSFT)}\n        ),\n    CapsWordDoubleTapShiftParams::GetName\n    );\n\n// Tests that holding a OSL keeps caps word active and shifts keys on the layer that need to be shifted.\nTEST_F(CapsWord, IgnoresOSLHold) {\n    TestDriver driver;\n    KeymapKey key_a(0, 0, 0, KC_A);\n    KeymapKey key_osl(0, 1, 0, OSL(1));\n    KeymapKey key_b(1, 0, 0, KC_B);\n    set_keymap({key_a, key_osl, key_b});\n\n    // Allow any number of reports with no keys or only modifiers.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n\n    EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n    caps_word_on();\n\n    key_osl.press();\n    run_one_scan_loop();\n    tap_key(key_b);\n    key_osl.release();\n    idle_for(CAPS_WORD_IDLE_TIMEOUT + 1);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Tests that tapping a OSL keeps caps word active and shifts keys on the layer that need to be shifted.\nTEST_F(CapsWord, IgnoresOSLTap) {\n    TestDriver driver;\n    KeymapKey key_a(0, 0, 0, KC_A);\n    KeymapKey key_osl(0, 1, 0, OSL(1));\n    KeymapKey key_b(1, 0, 0, KC_B);\n    set_keymap({key_a, key_osl, key_b});\n\n    // Allow any number of reports with no keys or only modifiers.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n    caps_word_on();\n\n    tap_key(key_osl);\n    tap_key(key_b);\n    idle_for(CAPS_WORD_IDLE_TIMEOUT);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(CapsWord, IgnoresLayerLockKey) {\n    TestDriver driver;\n    KeymapKey  key_llock(0, 1, 0, QK_LAYER_LOCK);\n    KeymapKey  key_b(0, 0, 0, KC_B);\n    set_keymap({key_llock, key_b});\n\n    // Allow any number of reports with no keys or only modifiers.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n    caps_word_on();\n\n    tap_key(key_llock);\n    tap_key(key_b);\n    idle_for(CAPS_WORD_IDLE_TIMEOUT);\n\n    VERIFY_AND_CLEAR(driver);\n}\n} // namespace\n"
  },
  {
    "path": "tests/caps_word/unicodemap/config.h",
    "content": "// Copyright 2022 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX\n"
  },
  {
    "path": "tests/caps_word/unicodemap/test.mk",
    "content": "# Copyright 2022 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nCAPS_WORD_ENABLE = yes\nUNICODEMAP_ENABLE = yes\n\n"
  },
  {
    "path": "tests/caps_word/unicodemap/test_caps_word_unicodemap.cpp",
    "content": "// Copyright 2022 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing ::testing::_;\nusing ::testing::AnyNumber;\nusing ::testing::AnyOf;\nusing ::testing::InSequence;\n\nextern \"C\" {\nenum unicode_names {\n    ENDASH,\n    EMDASH,\n    DELTA_LOWERCASE,\n    DELTA_UPPERCASE,\n};\n\nconst uint32_t unicode_map[] PROGMEM = {\n    [ENDASH]          = 0x2013,\n    [EMDASH]          = 0x2014,\n    [DELTA_LOWERCASE] = 0x03b4,\n    [DELTA_UPPERCASE] = 0x0394,\n};\n\n#define U_DASH UP(ENDASH, EMDASH)\n#define U_DELTA UP(DELTA_LOWERCASE, DELTA_UPPERCASE)\n\nbool caps_word_press_user(uint16_t keycode) {\n    switch (keycode) {\n        // Keycodes that continue Caps Word, with shift applied.\n        case U_DELTA:\n            add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key.\n            return true;\n\n        // Keycodes that continue Caps Word, without shifting.\n        case U_DASH:\n            return true;\n\n        default:\n            return false; // Deactivate Caps Word.\n    }\n}\n} // extern \"C\"\n\nclass CapsWord : public TestFixture {\n   public:\n    void SetUp() override {\n        caps_word_off();\n    }\n};\n\n// Tests that typing U_DELTA while Caps Word is on sends the uppercase Delta.\nTEST_F(CapsWord, ShiftedUnicodeMapKey) {\n    TestDriver driver;\n    KeymapKey  key_delta(0, 0, 0, U_DELTA);\n    KeymapKey  key_spc(0, 1, 0, KC_SPC);\n    set_keymap({key_delta, key_spc});\n\n    // Allow any number of reports with no keys or only KC_LSFT and KC_LCTL.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL, KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    { // Expect: \"Uppercase Delta, space, lowercase delta\".\n        InSequence s;\n        EXPECT_UNICODE(driver, unicode_map[DELTA_UPPERCASE]);\n        EXPECT_REPORT(driver, (KC_SPC));\n        EXPECT_UNICODE(driver, unicode_map[DELTA_LOWERCASE]);\n    }\n\n    // Turn on Caps Word and tap \"delta, space, delta\".\n    caps_word_on();\n    tap_keys(key_delta, key_spc, key_delta);\n\n    EXPECT_EQ(is_caps_word_on(), false);\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Tests typing U_ENDASH while Caps Word is on.\nTEST_F(CapsWord, UnshiftedUnicodeMapKey) {\n    TestDriver driver;\n    KeymapKey  key_dash(0, 0, 0, U_DASH);\n    set_keymap({key_dash});\n\n    // Allow any number of reports with no keys or only KC_LSFT and KC_LCTL.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL, KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_UNICODE(driver, unicode_map[ENDASH]);\n\n    // Turn on Caps Word and tap U_DASH key.\n    caps_word_on();\n    tap_key(key_dash);\n\n    EXPECT_EQ(is_caps_word_on(), true);\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/combo/combo_repress/config.h",
    "content": "// Copyright 2024 @Filios92\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define TAPPING_TERM 200\n\n#define COMBO_PROCESS_KEY_REPRESS\n"
  },
  {
    "path": "tests/combo/combo_repress/test.mk",
    "content": "# Copyright 2024 @Filios92\n# SPDX-License-Identifier: GPL-2.0-or-later\n\nCOMBO_ENABLE = yes\n\nINTROSPECTION_KEYMAP_C = test_combos_repress.c\n"
  },
  {
    "path": "tests/combo/combo_repress/test_combo.cpp",
    "content": "// Copyright 2024 @Filios92\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.h\"\n#include \"test_common.hpp\"\n#include \"test_driver.hpp\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass ComboRepress : public TestFixture {};\n\nTEST_F(ComboRepress, combo_repress_tapped) {\n    TestDriver driver;\n    KeymapKey  key_f(0, 0, 0, KC_F);\n    KeymapKey  key_g(0, 0, 1, KC_G);\n    set_keymap({key_f, key_g});\n\n    EXPECT_REPORT(driver, (KC_LEFT_ALT)).Times(2);\n    EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_combo({key_f, key_g}, 20);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ComboRepress, combo_repress_held_released_one_key_and_repressed) {\n    TestDriver driver;\n    KeymapKey  key_f(0, 0, 0, KC_F);\n    KeymapKey  key_g(0, 0, 1, KC_G);\n    KeymapKey  key_h(0, 0, 2, KC_H);\n    KeymapKey  key_j(0, 0, 3, KC_J);\n    set_keymap({key_f, key_g, key_h, key_j});\n\n    /* Press combo F+G */\n    EXPECT_REPORT(driver, (KC_LEFT_ALT)).Times(2);\n    EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT));\n    key_f.press();\n    run_one_scan_loop();\n    key_g.press();\n    run_one_scan_loop();\n    idle_for(COMBO_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release G */\n    EXPECT_NO_REPORT(driver);\n    key_g.release();\n    idle_for(80);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Tap G */\n    EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT));\n    EXPECT_REPORT(driver, (KC_LEFT_ALT));\n    tap_key(key_g, TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Tap G, but hold for longer */\n    EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT));\n    EXPECT_REPORT(driver, (KC_LEFT_ALT));\n    tap_key(key_g, TAPPING_TERM * 2);\n    VERIFY_AND_CLEAR(driver);\n\n    idle_for(500);\n\n    /* Tap other combo while holding F */\n    EXPECT_REPORT(driver, (KC_ESCAPE, KC_LEFT_ALT));\n    EXPECT_REPORT(driver, (KC_LEFT_ALT));\n    tap_combo({key_h, key_j}, TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* G press and hold */\n    EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT));\n    EXPECT_REPORT(driver, (KC_LEFT_ALT));\n    key_g.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* F release and tap */\n    EXPECT_REPORT(driver, (KC_LEFT_ALT, KC_LEFT_SHIFT)).Times(2);\n    EXPECT_REPORT(driver, (KC_TAB, KC_LEFT_ALT, KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_ALT));\n    key_f.release();\n    run_one_scan_loop();\n    tap_key(key_f);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release G */\n    EXPECT_EMPTY_REPORT(driver);\n    key_g.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ComboRepress, combo_repress_normal_combo) {\n    TestDriver driver;\n    KeymapKey  key_f(0, 0, 0, KC_F);\n    KeymapKey  key_g(0, 0, 1, KC_G);\n    KeymapKey  key_h(0, 0, 2, KC_H);\n    KeymapKey  key_j(0, 0, 3, KC_J);\n    set_keymap({key_f, key_g, key_h, key_j});\n\n    /* Press combo H+J */\n    EXPECT_REPORT(driver, (KC_ESCAPE));\n    key_h.press();\n    run_one_scan_loop();\n    key_j.press();\n    run_one_scan_loop();\n    idle_for(COMBO_TERM + 10);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release H */\n    EXPECT_NO_REPORT(driver);\n    key_h.release();\n    idle_for(80);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Tap H */\n    EXPECT_REPORT(driver, (KC_H, KC_ESCAPE));\n    EXPECT_REPORT(driver, (KC_ESCAPE));\n    tap_key(key_h);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Tap H, but hold for longer */\n    EXPECT_REPORT(driver, (KC_H, KC_ESCAPE));\n    EXPECT_REPORT(driver, (KC_ESCAPE));\n    tap_key(key_h, TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    idle_for(500);\n\n    /* Tap other combo while holding K */\n    EXPECT_REPORT(driver, (KC_ESCAPE, KC_LEFT_ALT)).Times(2);\n    EXPECT_REPORT(driver, (KC_ESCAPE, KC_TAB, KC_LEFT_ALT));\n    EXPECT_REPORT(driver, (KC_ESCAPE));\n    tap_combo({key_f, key_g}, TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* H press and hold */\n    EXPECT_REPORT(driver, (KC_H, KC_ESCAPE));\n    key_h.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* J release and tap */\n    EXPECT_REPORT(driver, (KC_H));\n    key_j.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release G */\n    EXPECT_EMPTY_REPORT(driver);\n    key_h.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/combo/combo_repress/test_combos_repress.c",
    "content": "// Copyright 2024 @Filios92\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include \"quantum.h\"\n\nenum combos { alttab, esc };\n\nuint16_t const alttab_combo[] = {KC_F, KC_G, COMBO_END};\nuint16_t const esc_combo[]    = {KC_H, KC_J, COMBO_END};\n\n// clang-format off\ncombo_t key_combos[] = {\n    [alttab]  = COMBO(alttab_combo, KC_NO),\n    [esc]     = COMBO(esc_combo, KC_ESC)\n};\n// clang-format on\n\nvoid process_combo_event(uint16_t combo_index, bool pressed) {\n    switch (combo_index) {\n        case alttab:\n            if (pressed) {\n                register_mods(MOD_LALT);\n                tap_code(KC_TAB);\n            } else {\n                unregister_mods(MOD_LALT);\n            }\n            break;\n    }\n}\n\nbool process_combo_key_repress(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) {\n    switch (combo_index) {\n        case alttab:\n            switch (keycode) {\n                case KC_F:\n                    tap_code16(S(KC_TAB));\n                    return true;\n                case KC_G:\n                    tap_code(KC_TAB);\n                    return true;\n            }\n    }\n    return false;\n}\n"
  },
  {
    "path": "tests/combo/config.h",
    "content": "// Copyright 2023 Stefan Kerkmann (@KarlK90)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define TAPPING_TERM 200\n"
  },
  {
    "path": "tests/combo/test.mk",
    "content": "# Copyright 2023 Stefan Kerkmann (@KarlK90)\n# SPDX-License-Identifier: GPL-2.0-or-later\n\nCOMBO_ENABLE = yes\n\nINTROSPECTION_KEYMAP_C = test_combos.c\n"
  },
  {
    "path": "tests/combo/test_combo.cpp",
    "content": "// Copyright 2023 Stefan Kerkmann (@KarlK90)\n// Copyright 2023 @filterpaper\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.h\"\n#include \"test_driver.hpp\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass Combo : public TestFixture {};\n\nTEST_F(Combo, combo_modtest_tapped) {\n    TestDriver driver;\n    KeymapKey  key_y(0, 0, 1, KC_Y);\n    KeymapKey  key_u(0, 0, 2, KC_U);\n    set_keymap({key_y, key_u});\n\n    EXPECT_REPORT(driver, (KC_SPACE));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_combo({key_y, key_u});\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Combo, combo_modtest_held_longer_than_tapping_term) {\n    TestDriver driver;\n    KeymapKey  key_y(0, 0, 1, KC_Y);\n    KeymapKey  key_u(0, 0, 2, KC_U);\n    set_keymap({key_y, key_u});\n\n    EXPECT_REPORT(driver, (KC_RIGHT_SHIFT));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_combo({key_y, key_u}, TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Combo, combo_osmshift_tapped) {\n    TestDriver driver;\n    KeymapKey  key_z(0, 0, 1, KC_Z);\n    KeymapKey  key_x(0, 0, 2, KC_X);\n    KeymapKey  key_i(0, 0, 3, KC_I);\n    set_keymap({key_z, key_x, key_i});\n\n    EXPECT_NO_REPORT(driver);\n    tap_combo({key_z, key_x});\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_I, KC_LEFT_SHIFT));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_i);\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/combo/test_combos.c",
    "content": "// Copyright 2023 Stefan Kerkmann (@KarlK90)\n// Copyright 2023 @filterpaper\n// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include \"quantum.h\"\n\nenum combos { modtest, osmshift };\n\nuint16_t const modtest_combo[]  = {KC_Y, KC_U, COMBO_END};\nuint16_t const osmshift_combo[] = {KC_Z, KC_X, COMBO_END};\n\n// clang-format off\ncombo_t key_combos[] = {\n    [modtest]  = COMBO(modtest_combo, RSFT_T(KC_SPACE)),\n    [osmshift] = COMBO(osmshift_combo, OSM(MOD_LSFT))\n};\n// clang-format on\n"
  },
  {
    "path": "tests/housekeeping/config.h",
    "content": "/* Copyright 2024 leep-frog\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/housekeeping/test.mk",
    "content": "# Copyright 2024 leep-frog\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n"
  },
  {
    "path": "tests/housekeeping/test_housekeeping.cpp",
    "content": "/* Copyright 2024 leep-frog\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\n\nclass HousekeepingMock {\n   public:\n    virtual ~HousekeepingMock() {}\n\n    // mock methods\n    MOCK_METHOD0(housekeeping_task_kb, void(void));\n    MOCK_METHOD0(housekeeping_task_user, void(void));\n};\n\nclass Housekeeping : public TestFixture {\n   public:\n    Housekeeping() {\n        _housekeepingMock.reset(new ::testing::NiceMock<HousekeepingMock>());\n    }\n    virtual ~Housekeeping() {\n        _housekeepingMock.reset();\n    }\n\n    static std::unique_ptr<HousekeepingMock> _housekeepingMock;\n};\n\nstd::unique_ptr<HousekeepingMock> Housekeeping::_housekeepingMock;\n\nextern \"C\" {\nvoid housekeeping_task_kb(void) {\n    if (Housekeeping::_housekeepingMock) {\n        Housekeeping::_housekeepingMock->housekeeping_task_kb();\n    }\n}\n\nvoid housekeeping_task_user(void) {\n    if (Housekeeping::_housekeepingMock) {\n        Housekeeping::_housekeepingMock->housekeeping_task_user();\n    }\n}\n}\n\nTEST_F(Housekeeping, Works) {\n    TestDriver driver;\n\n    EXPECT_CALL(*_housekeepingMock, housekeeping_task_kb()).Times(1);\n    EXPECT_CALL(*_housekeepingMock, housekeeping_task_user()).Times(1);\n    run_one_scan_loop();\n}\n"
  },
  {
    "path": "tests/keycode_string/config.h",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/keycode_string/test.mk",
    "content": "# Copyright 2025 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nEXTRAKEY_ENABLE = yes\nKEYCODE_STRING_ENABLE = yes\nKEY_LOCK_ENABLE = yes\nMAGIC_ENABLE = yes\nMOUSEKEY_ENABLE = yes\nPROGRAMMABLE_BUTTON_ENABLE = yes\nSECURE_ENABLE = yes\nSWAP_HANDS_ENABLE = yes\n"
  },
  {
    "path": "tests/keycode_string/test_keycode_string.cpp",
    "content": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <iostream>\n\n#include \"test_common.hpp\"\n\nenum {\n    MYMACRO1 = SAFE_RANGE,\n    MYMACRO2,\n};\n\n// clang-format off\nextern \"C\" {\n\nKEYCODE_STRING_NAMES_KB(\n    KEYCODE_STRING_NAME(MYMACRO1),\n);\n\nKEYCODE_STRING_NAMES_USER(\n    KEYCODE_STRING_NAME(MYMACRO2),\n    KEYCODE_STRING_NAME(KC_EXLM),\n);\n\nconst keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS] = {\n  {{9, 0}, {8, 0}, {7, 0}, {6, 0}, {5, 0}, {4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}},\n  {{9, 1}, {8, 1}, {7, 1}, {6, 1}, {5, 1}, {4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}},\n  {{9, 2}, {8, 2}, {7, 2}, {6, 2}, {5, 2}, {4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}},\n  {{9, 3}, {8, 3}, {7, 3}, {6, 3}, {5, 3}, {4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}},\n};\n\n} // extern \"C\"\n// clang-format on\n\nclass KeycodeStringTest : public TestFixture {};\n\nTEST_F(KeycodeStringTest, get_keycode_string) {\n    struct TestParams {\n        uint16_t    keycode;\n        std::string expected;\n    };\n    for (const auto [keycode, expected] : std::vector<TestParams>({\n             {KC_TRNS, \"KC_TRNS\"},\n             {KC_ESC, \"KC_ESC\"},\n             {KC_A, \"KC_A\"},\n             {KC_Z, \"KC_Z\"},\n             {KC_0, \"KC_0\"},\n             {KC_9, \"KC_9\"},\n             {KC_KP_0, \"KC_KP_0\"},\n             {KC_KP_9, \"KC_KP_9\"},\n             {KC_LBRC, \"KC_LBRC\"},\n             {KC_NUHS, \"KC_NUHS\"},\n             {KC_NUBS, \"KC_NUBS\"},\n             {KC_CAPS, \"KC_CAPS\"},\n             {DB_TOGG, \"DB_TOGG\"},\n             {KC_LCTL, \"KC_LCTL\"},\n             {KC_LSFT, \"KC_LSFT\"},\n             {KC_RALT, \"KC_RALT\"},\n             {KC_RGUI, \"KC_RGUI\"},\n             {KC_UP, \"KC_UP\"},\n             {KC_HYPR, \"KC_HYPR\"},\n             {KC_MEH, \"KC_MEH\"},\n             // F1-F24 keycodes.\n             {KC_F1, \"KC_F1\"},\n             {KC_F12, \"KC_F12\"},\n             {KC_F13, \"KC_F13\"},\n             {KC_F24, \"KC_F24\"},\n             // Macro keycodes.\n             {MC_0, \"MC_0\"},\n             {MC_31, \"MC_31\"},\n             // Keyboard range keycodes.\n             {QK_KB_0, \"QK_KB_0\"},\n             {QK_KB_31, \"QK_KB_31\"},\n             // User range keycodes.\n             {QK_USER_2, \"QK_USER_2\"},\n             {QK_USER_31, \"QK_USER_31\"},\n             // Modified keycodes.\n             {KC_COLN, \"S(KC_SCLN)\"},\n             {C(KC_PGUP), \"C(KC_PGUP)\"},\n             {RALT(KC_BSPC), \"RALT(KC_BSPC)\"},\n             // One-shot mods.\n             {OSM(MOD_LSFT), \"OSM(MOD_LSFT)\"},\n             {OSM(MOD_RGUI), \"OSM(MOD_RGUI)\"},\n             {OSM(MOD_RCTL | MOD_RGUI), \"OSM(0x19)\"},\n             // Layer switch keycodes.\n             {DF(2), \"DF(2)\"},\n             {PDF(12), \"PDF(12)\"},\n             {MO(3), \"MO(3)\"},\n             {TO(0), \"TO(0)\"},\n             {TT(1), \"TT(1)\"},\n             {TG(3), \"TG(3)\"},\n             {OSL(3), \"OSL(3)\"},\n             {LM(3, MOD_RALT), \"LM(3,MOD_RALT)\"},\n             {LT(15, KC_QUOT), \"LT(15,KC_QUOT)\"},\n             // Tap dance keycodes.\n             {TD(0), \"TD(0)\"},\n             {TD(31), \"TD(31)\"},\n             // Mod-tap keycodes.\n             {LSFT_T(KC_ENT), \"LSFT_T(KC_ENT)\"},\n             {RCTL_T(KC_RGHT), \"RCTL_T(KC_RGHT)\"},\n             {HYPR_T(KC_GRV), \"HYPR_T(KC_GRV)\"},\n             {MEH_T(KC_EQL), \"MEH_T(KC_EQL)\"},\n             {RSA_T(KC_LBRC), \"MT(0x16,KC_LBRC)\"},\n             // Extrakey keycodes.\n             {KC_WBAK, \"KC_WBAK\"},\n             {KC_WFWD, \"KC_WFWD\"},\n             {KC_WREF, \"KC_WREF\"},\n             {KC_VOLU, \"KC_VOLU\"},\n             {KC_VOLD, \"KC_VOLD\"},\n             // Mouse Key keycodes.\n             {MS_LEFT, \"MS_LEFT\"},\n             {MS_RGHT, \"MS_RGHT\"},\n             {MS_UP, \"MS_UP\"},\n             {MS_WHLU, \"MS_WHLU\"},\n             {MS_WHLD, \"MS_WHLD\"},\n             {MS_BTN1, \"MS_BTN1\"},\n             {MS_BTN8, \"MS_BTN8\"},\n             // Swap Hands keycodes.\n             {SH_MON, \"SH_MON\"},\n             {SH_TOGG, \"SH_TOGG\"},\n             {SH_T(KC_PSCR), \"SH_T(KC_PSCR)\"},\n             // Secure keycodes.\n             {SE_LOCK, \"SE_LOCK\"},\n             {SE_UNLK, \"SE_UNLK\"},\n             {SE_TOGG, \"SE_TOGG\"},\n             {SE_REQ, \"SE_REQ\"},\n             // Programmable button keycodes.\n             {PB_1, \"PB_1\"},\n             {PB_32, \"PB_32\"},\n             // Magic button keycodes.\n             {QK_MAGIC + 7, \"QK_MAGIC+7\"},\n             // Quantum keycodes.\n             {QK_LOCK, \"QK_LOCK\"},\n             {QK_QUANTUM + 7, \"QK_QUANTUM+7\"},\n             // Custom keycode names.\n             {MYMACRO1, \"MYMACRO1\"},\n             {MYMACRO2, \"MYMACRO2\"},\n             {KC_EXLM, \"KC_EXLM\"},\n         })) {\n        EXPECT_EQ(get_keycode_string(keycode), expected) << \"where keycode = 0x\" << std::hex << keycode;\n    }\n}\n"
  },
  {
    "path": "tests/layer_lock/config.h",
    "content": "// Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define LAYER_LOCK_IDLE_TIMEOUT 1000\n"
  },
  {
    "path": "tests/layer_lock/test.mk",
    "content": "# Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n# SPDX-License-Identifier: GPL-2.0-or-later\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nLAYER_LOCK_ENABLE = yes\n"
  },
  {
    "path": "tests/layer_lock/test_layer_lock.cpp",
    "content": "// Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keycodes.h\"\n#include \"test_common.hpp\"\n\nusing testing::_;\n\nclass LayerLock : public TestFixture {};\n\nTEST_F(LayerLock, LayerLockState) {\n    TestDriver driver;\n    KeymapKey  key_a = KeymapKey(0, 0, 0, KC_A);\n    KeymapKey  key_b = KeymapKey(1, 0, 0, KC_B);\n    KeymapKey  key_c = KeymapKey(2, 0, 0, KC_C);\n    KeymapKey  key_d = KeymapKey(3, 0, 0, KC_C);\n\n    set_keymap({key_a, key_b, key_c, key_d});\n\n    EXPECT_FALSE(is_layer_locked(1));\n    EXPECT_FALSE(is_layer_locked(2));\n    EXPECT_FALSE(is_layer_locked(3));\n\n    layer_lock_invert(1); // Layer 1: unlocked -> locked\n    layer_lock_on(2);     // Layer 2: unlocked -> locked\n    layer_lock_off(3);    // Layer 3: stays unlocked\n\n    // Layers 1 and 2 are now on.\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_TRUE(layer_state_is(2));\n    // Layers 1 and 2 are now locked.\n    EXPECT_TRUE(is_layer_locked(1));\n    EXPECT_TRUE(is_layer_locked(2));\n    EXPECT_FALSE(is_layer_locked(3));\n\n    layer_lock_invert(1); // Layer 1: locked -> unlocked\n    layer_lock_on(2);     // Layer 2: stays locked\n    layer_lock_on(3);     // Layer 3: unlocked -> locked\n\n    EXPECT_FALSE(layer_state_is(1));\n    EXPECT_TRUE(layer_state_is(2));\n    EXPECT_TRUE(layer_state_is(3));\n    EXPECT_FALSE(is_layer_locked(1));\n    EXPECT_TRUE(is_layer_locked(2));\n    EXPECT_TRUE(is_layer_locked(3));\n\n    layer_lock_invert(1); // Layer 1: unlocked -> locked\n    layer_lock_off(2);    // Layer 2: locked -> unlocked\n\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_FALSE(layer_state_is(2));\n    EXPECT_TRUE(layer_state_is(3));\n    EXPECT_TRUE(is_layer_locked(1));\n    EXPECT_FALSE(is_layer_locked(2));\n    EXPECT_TRUE(is_layer_locked(3));\n\n    layer_lock_all_off(); // Layers 1 and 3: locked -> unlocked\n\n    EXPECT_FALSE(layer_state_is(1));\n    EXPECT_FALSE(layer_state_is(2));\n    EXPECT_FALSE(layer_state_is(3));\n    EXPECT_FALSE(is_layer_locked(1));\n    EXPECT_FALSE(is_layer_locked(2));\n    EXPECT_FALSE(is_layer_locked(3));\n}\n\nTEST_F(LayerLock, LayerLockMomentaryTest) {\n    TestDriver driver;\n    KeymapKey  key_layer = KeymapKey(0, 0, 0, MO(1));\n    KeymapKey  key_a     = KeymapKey(0, 1, 0, KC_A);\n    KeymapKey  key_trns  = KeymapKey(1, 0, 0, KC_TRNS);\n    KeymapKey  key_ll    = KeymapKey(1, 1, 0, QK_LAYER_LOCK);\n\n    set_keymap({key_layer, key_a, key_trns, key_ll});\n\n    EXPECT_NO_REPORT(driver);\n    key_layer.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_FALSE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_ll);\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_TRUE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    key_layer.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_TRUE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n\n    // Pressing Layer Lock again unlocks the lock.\n    EXPECT_NO_REPORT(driver);\n    key_ll.press();\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(1));\n    EXPECT_FALSE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(LayerLock, LayerLockLayerTapTest) {\n    TestDriver driver;\n    KeymapKey  key_layer = KeymapKey(0, 0, 0, LT(1, KC_B));\n    KeymapKey  key_a     = KeymapKey(0, 1, 0, KC_A);\n    KeymapKey  key_trns  = KeymapKey(1, 0, 0, KC_TRNS);\n    KeymapKey  key_ll    = KeymapKey(1, 1, 0, QK_LAYER_LOCK);\n\n    set_keymap({key_layer, key_a, key_trns, key_ll});\n\n    EXPECT_NO_REPORT(driver);\n    key_layer.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_ll);\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_TRUE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n\n    // Pressing Layer Lock again unlocks the lock.\n    EXPECT_NO_REPORT(driver);\n    key_ll.press();\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(1));\n    EXPECT_FALSE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(LayerLock, LayerLockOneshotTapTest) {\n    TestDriver driver;\n    KeymapKey  key_layer = KeymapKey(0, 0, 0, OSL(1));\n    KeymapKey  key_a     = KeymapKey(0, 1, 0, KC_A);\n    KeymapKey  key_trns  = KeymapKey(1, 0, 0, KC_TRNS);\n    KeymapKey  key_ll    = KeymapKey(1, 1, 0, QK_LAYER_LOCK);\n\n    set_keymap({key_layer, key_a, key_trns, key_ll});\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_layer);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_ll);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_TRUE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n\n    // Pressing Layer Lock again unlocks the lock.\n    EXPECT_NO_REPORT(driver);\n    key_ll.press();\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(1));\n    EXPECT_FALSE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(LayerLock, LayerLockOneshotHoldTest) {\n    TestDriver driver;\n    KeymapKey  key_layer = KeymapKey(0, 0, 0, OSL(1));\n    KeymapKey  key_a     = KeymapKey(0, 1, 0, KC_A);\n    KeymapKey  key_trns  = KeymapKey(1, 0, 0, KC_TRNS);\n    KeymapKey  key_ll    = KeymapKey(1, 1, 0, QK_LAYER_LOCK);\n\n    set_keymap({key_layer, key_a, key_trns, key_ll});\n\n    EXPECT_NO_REPORT(driver);\n    key_layer.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_ll);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    key_layer.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_TRUE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n\n    // Pressing Layer Lock again unlocks the lock.\n    EXPECT_NO_REPORT(driver);\n    key_ll.press();\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(1));\n    EXPECT_FALSE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(LayerLock, LayerLockTimeoutTest) {\n    TestDriver driver;\n    KeymapKey  key_layer = KeymapKey(0, 0, 0, MO(1));\n    KeymapKey  key_a     = KeymapKey(0, 1, 0, KC_A);\n    KeymapKey  key_trns  = KeymapKey(1, 0, 0, KC_TRNS);\n    KeymapKey  key_ll    = KeymapKey(1, 1, 0, QK_LAYER_LOCK);\n\n    set_keymap({key_layer, key_a, key_trns, key_ll});\n\n    EXPECT_NO_REPORT(driver);\n    key_layer.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_ll);\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    key_layer.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_TRUE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    idle_for(LAYER_LOCK_IDLE_TIMEOUT);\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(1));\n    EXPECT_FALSE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(LayerLock, ToKeyOverridesLayerLock) {\n    TestDriver driver;\n    KeymapKey  key_layer = KeymapKey(0, 0, 0, MO(1));\n    KeymapKey  key_to0   = KeymapKey(1, 0, 0, TO(0));\n    KeymapKey  key_ll    = KeymapKey(1, 1, 0, QK_LAYER_LOCK);\n\n    set_keymap({key_layer, key_to0, key_ll});\n\n    EXPECT_NO_REPORT(driver);\n    layer_lock_on(1);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_TRUE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_to0); // TO(0) overrides Layer Lock and unlocks layer 1.\n    EXPECT_FALSE(layer_state_is(1));\n    EXPECT_FALSE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(LayerLock, LayerClearOverridesLayerLock) {\n    TestDriver driver;\n    KeymapKey  key_layer = KeymapKey(0, 0, 0, MO(1));\n    KeymapKey  key_a     = KeymapKey(0, 1, 0, KC_A);\n    KeymapKey  key_ll    = KeymapKey(1, 1, 0, QK_LAYER_LOCK);\n\n    set_keymap({key_layer, key_a, key_ll});\n\n    EXPECT_NO_REPORT(driver);\n    layer_lock_on(1);\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    EXPECT_TRUE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A));\n    layer_clear(); // layer_clear() overrides Layer Lock and unlocks layer 1.\n    key_a.press();\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(1));\n    EXPECT_FALSE(is_layer_locked(1));\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/leader/config.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/leader/leader_no_initial_timeout/config.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define LEADER_NO_TIMEOUT\n"
  },
  {
    "path": "tests/leader/leader_no_initial_timeout/test.mk",
    "content": "# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nLEADER_ENABLE = yes\n\nSRC += ../leader_sequences.c\n"
  },
  {
    "path": "tests/leader/leader_no_initial_timeout/test_leader_no_initial_timeout.cpp",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\n\nclass Leader : public TestFixture {};\n\nTEST_F(Leader, does_not_timeout_until_next_key_pressed) {\n    TestDriver driver;\n\n    auto key_leader = KeymapKey(0, 0, 0, QK_LEADER);\n    auto key_a      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({key_leader, key_a});\n\n    EXPECT_EQ(leader_sequence_active(), false);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_leader);\n\n    EXPECT_EQ(leader_sequence_active(), true);\n\n    idle_for(1000);\n\n    EXPECT_EQ(leader_sequence_active(), true);\n    EXPECT_EQ(leader_sequence_timed_out(), false);\n\n    EXPECT_REPORT(driver, (KC_1));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n\n    EXPECT_EQ(leader_sequence_active(), true);\n    EXPECT_EQ(leader_sequence_timed_out(), false);\n\n    idle_for(300);\n\n    EXPECT_EQ(leader_sequence_active(), false);\n    EXPECT_EQ(leader_sequence_timed_out(), true);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n}\n"
  },
  {
    "path": "tests/leader/leader_per_key_timeout/config.h",
    "content": "#pragma once\n\n#include \"test_common.h\"\n\n#define LEADER_PER_KEY_TIMING\n"
  },
  {
    "path": "tests/leader/leader_per_key_timeout/test.mk",
    "content": "# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nLEADER_ENABLE = yes\n\nSRC += ../leader_sequences.c\n"
  },
  {
    "path": "tests/leader/leader_per_key_timeout/test_leader_per_key_timeout.cpp",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\n\nclass Leader : public TestFixture {};\n\nTEST_F(Leader, does_not_timeout_during_sequence) {\n    TestDriver driver;\n\n    auto key_leader = KeymapKey(0, 0, 0, QK_LEADER);\n    auto key_a      = KeymapKey(0, 1, 0, KC_A);\n    auto key_b      = KeymapKey(0, 2, 0, KC_B);\n    auto key_c      = KeymapKey(0, 3, 0, KC_C);\n\n    set_keymap({key_leader, key_a, key_b, key_c});\n\n    tap_key(key_leader);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_a);\n\n    idle_for(150);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_b);\n\n    idle_for(150);\n\n    EXPECT_REPORT(driver, (KC_3));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_c);\n\n    idle_for(300);\n}\n"
  },
  {
    "path": "tests/leader/leader_sequences.c",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"quantum.h\"\n\nvoid leader_end_user(void) {\n    if (leader_sequence_one_key(KC_A)) {\n        tap_code(KC_1);\n    }\n\n    if (leader_sequence_two_keys(KC_A, KC_B)) {\n        tap_code(KC_2);\n    }\n\n    if (leader_sequence_three_keys(KC_A, KC_B, KC_C)) {\n        tap_code(KC_3);\n    }\n\n    if (leader_sequence_four_keys(KC_A, KC_B, KC_C, KC_D)) {\n        tap_code(KC_4);\n    }\n\n    if (leader_sequence_five_keys(KC_A, KC_B, KC_C, KC_D, KC_E)) {\n        tap_code(KC_5);\n    }\n}\n"
  },
  {
    "path": "tests/leader/leader_strict_key_processing/config.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define LEADER_KEY_STRICT_KEY_PROCESSING\n"
  },
  {
    "path": "tests/leader/leader_strict_key_processing/test.mk",
    "content": "# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nLEADER_ENABLE = yes\n\nSRC += ../leader_sequences.c\n"
  },
  {
    "path": "tests/leader/leader_strict_key_processing/test_leader_strict_key_processing.cpp",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\n\nclass Leader : public TestFixture {};\n\nTEST_F(Leader, does_not_extract_mod_tap_keycode) {\n    TestDriver driver;\n\n    auto key_leader = KeymapKey(0, 0, 0, QK_LEADER);\n    auto key_mt     = KeymapKey(0, 1, 0, LSFT_T(KC_A));\n\n    set_keymap({key_leader, key_mt});\n\n    tap_key(key_leader);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_mt);\n\n    EXPECT_EQ(leader_sequence_one_key(KC_A), false);\n    EXPECT_EQ(leader_sequence_one_key(LSFT_T(KC_A)), true);\n}\n\nTEST_F(Leader, does_not_extract_layer_tap_keycode) {\n    TestDriver driver;\n\n    auto key_leader = KeymapKey(0, 0, 0, QK_LEADER);\n    auto key_lt     = KeymapKey(0, 1, 0, LT(1, KC_A));\n\n    set_keymap({key_leader, key_lt});\n\n    tap_key(key_leader);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_lt);\n\n    EXPECT_EQ(leader_sequence_one_key(KC_A), false);\n    EXPECT_EQ(leader_sequence_one_key(LT(1, KC_A)), true);\n}\n"
  },
  {
    "path": "tests/leader/test.mk",
    "content": "# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nLEADER_ENABLE = yes\n\nSRC += leader_sequences.c\n"
  },
  {
    "path": "tests/leader/test_leader.cpp",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\n\nclass Leader : public TestFixture {};\n\nTEST_F(Leader, triggers_one_key_sequence) {\n    TestDriver driver;\n\n    auto key_leader = KeymapKey(0, 0, 0, QK_LEADER);\n    auto key_a      = KeymapKey(0, 1, 0, KC_A);\n\n    set_keymap({key_leader, key_a});\n\n    EXPECT_EQ(leader_sequence_active(), false);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_leader);\n\n    EXPECT_EQ(leader_sequence_active(), true);\n\n    EXPECT_REPORT(driver, (KC_1));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n\n    EXPECT_EQ(leader_sequence_timed_out(), false);\n\n    idle_for(300);\n\n    EXPECT_EQ(leader_sequence_active(), false);\n    EXPECT_EQ(leader_sequence_timed_out(), true);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n}\n\nTEST_F(Leader, triggers_two_key_sequence) {\n    TestDriver driver;\n\n    auto key_leader = KeymapKey(0, 0, 0, QK_LEADER);\n    auto key_a      = KeymapKey(0, 1, 0, KC_A);\n    auto key_b      = KeymapKey(0, 2, 0, KC_B);\n\n    set_keymap({key_leader, key_a, key_b});\n\n    EXPECT_EQ(leader_sequence_active(), false);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_leader);\n\n    EXPECT_EQ(leader_sequence_active(), true);\n\n    EXPECT_REPORT(driver, (KC_2));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n    tap_key(key_b);\n\n    EXPECT_EQ(leader_sequence_timed_out(), false);\n\n    idle_for(300);\n\n    EXPECT_EQ(leader_sequence_active(), false);\n    EXPECT_EQ(leader_sequence_timed_out(), true);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n}\n\nTEST_F(Leader, triggers_three_key_sequence) {\n    TestDriver driver;\n\n    auto key_leader = KeymapKey(0, 0, 0, QK_LEADER);\n    auto key_a      = KeymapKey(0, 1, 0, KC_A);\n    auto key_b      = KeymapKey(0, 2, 0, KC_B);\n    auto key_c      = KeymapKey(0, 3, 0, KC_C);\n\n    set_keymap({key_leader, key_a, key_b, key_c});\n\n    EXPECT_EQ(leader_sequence_active(), false);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_leader);\n\n    EXPECT_EQ(leader_sequence_active(), true);\n\n    EXPECT_REPORT(driver, (KC_3));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n    tap_key(key_b);\n    tap_key(key_c);\n\n    EXPECT_EQ(leader_sequence_timed_out(), false);\n\n    idle_for(300);\n\n    EXPECT_EQ(leader_sequence_active(), false);\n    EXPECT_EQ(leader_sequence_timed_out(), true);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n}\n\nTEST_F(Leader, triggers_four_key_sequence) {\n    TestDriver driver;\n\n    auto key_leader = KeymapKey(0, 0, 0, QK_LEADER);\n    auto key_a      = KeymapKey(0, 1, 0, KC_A);\n    auto key_b      = KeymapKey(0, 2, 0, KC_B);\n    auto key_c      = KeymapKey(0, 3, 0, KC_C);\n    auto key_d      = KeymapKey(0, 4, 0, KC_D);\n\n    set_keymap({key_leader, key_a, key_b, key_c, key_d});\n\n    EXPECT_EQ(leader_sequence_active(), false);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_leader);\n\n    EXPECT_EQ(leader_sequence_active(), true);\n\n    EXPECT_REPORT(driver, (KC_4));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n    tap_key(key_b);\n    tap_key(key_c);\n    tap_key(key_d);\n\n    EXPECT_EQ(leader_sequence_timed_out(), false);\n\n    idle_for(300);\n\n    EXPECT_EQ(leader_sequence_active(), false);\n    EXPECT_EQ(leader_sequence_timed_out(), true);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n}\n\nTEST_F(Leader, triggers_five_key_sequence) {\n    TestDriver driver;\n\n    auto key_leader = KeymapKey(0, 0, 0, QK_LEADER);\n    auto key_a      = KeymapKey(0, 1, 0, KC_A);\n    auto key_b      = KeymapKey(0, 2, 0, KC_B);\n    auto key_c      = KeymapKey(0, 3, 0, KC_C);\n    auto key_d      = KeymapKey(0, 4, 0, KC_D);\n    auto key_e      = KeymapKey(0, 5, 0, KC_E);\n\n    set_keymap({key_leader, key_a, key_b, key_c, key_d, key_e});\n\n    EXPECT_EQ(leader_sequence_active(), false);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_leader);\n\n    EXPECT_EQ(leader_sequence_active(), true);\n\n    EXPECT_REPORT(driver, (KC_5));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n    tap_key(key_b);\n    tap_key(key_c);\n    tap_key(key_d);\n    tap_key(key_e);\n\n    EXPECT_EQ(leader_sequence_timed_out(), false);\n\n    idle_for(300);\n\n    EXPECT_EQ(leader_sequence_active(), false);\n    EXPECT_EQ(leader_sequence_timed_out(), true);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_a);\n}\n\nTEST_F(Leader, extracts_mod_tap_keycode) {\n    TestDriver driver;\n\n    auto key_leader = KeymapKey(0, 0, 0, QK_LEADER);\n    auto key_mt     = KeymapKey(0, 1, 0, LSFT_T(KC_A));\n\n    set_keymap({key_leader, key_mt});\n\n    tap_key(key_leader);\n\n    EXPECT_REPORT(driver, (KC_1));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_mt);\n\n    EXPECT_EQ(leader_sequence_one_key(KC_A), true);\n\n    idle_for(300);\n}\n\nTEST_F(Leader, extracts_layer_tap_keycode) {\n    TestDriver driver;\n\n    auto key_leader = KeymapKey(0, 0, 0, QK_LEADER);\n    auto key_lt     = KeymapKey(0, 1, 0, LT(1, KC_A));\n\n    set_keymap({key_leader, key_lt});\n\n    tap_key(key_leader);\n\n    EXPECT_REPORT(driver, (KC_1));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_lt);\n\n    EXPECT_EQ(leader_sequence_one_key(KC_A), true);\n\n    idle_for(300);\n}\n"
  },
  {
    "path": "tests/mousekeys/config.h",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/mousekeys/test.mk",
    "content": "MOUSEKEY_ENABLE = yes\n"
  },
  {
    "path": "tests/mousekeys/test_mousekeys.cpp",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"gtest/gtest.h\"\n#include \"mouse_report_util.hpp\"\n#include \"test_common.hpp\"\n\nusing testing::_;\n\nstruct MouseKeyExpectations {\n    int16_t  x;\n    int16_t  y;\n    int16_t  h;\n    int16_t  v;\n    uint16_t button_mask;\n};\n\nclass Mousekey : public TestFixture {};\nclass MousekeyParametrized : public ::testing::WithParamInterface<std::pair<KeymapKey, MouseKeyExpectations>>, public Mousekey {};\n\nTEST_F(Mousekey, SendMouseNotCalledWhenNoKeyIsPressed) {\n    TestDriver driver;\n    EXPECT_NO_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(Mousekey, PressAndHoldCursorUpIsCorrectlyReported) {\n    TestDriver driver;\n    KeymapKey  mouse_key = KeymapKey{0, 0, 0, QK_MOUSE_CURSOR_UP};\n\n    set_keymap({mouse_key});\n\n    EXPECT_MOUSE_REPORT(driver, (0, -8, 0, 0, 0));\n    mouse_key.press();\n    run_one_scan_loop();\n\n    EXPECT_MOUSE_REPORT(driver, (0, -2, 0, 0, 0));\n    idle_for(MOUSEKEY_INTERVAL);\n\n    EXPECT_EMPTY_MOUSE_REPORT(driver);\n    mouse_key.release();\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Mousekey, PressAndHoldButtonOneCorrectlyReported) {\n    TestDriver driver;\n    KeymapKey  mouse_key = KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_1};\n\n    set_keymap({mouse_key});\n\n    EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, 1));\n    mouse_key.press();\n    run_one_scan_loop();\n\n    idle_for(MOUSEKEY_INTERVAL);\n\n    EXPECT_EMPTY_MOUSE_REPORT(driver);\n    mouse_key.release();\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_P(MousekeyParametrized, PressAndReleaseIsCorrectlyReported) {\n    TestDriver           driver;\n    KeymapKey            mouse_key    = GetParam().first;\n    MouseKeyExpectations expectations = GetParam().second;\n\n    set_keymap({mouse_key});\n\n    EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, expectations.button_mask));\n    mouse_key.press();\n    run_one_scan_loop();\n\n    EXPECT_EMPTY_MOUSE_REPORT(driver);\n    mouse_key.release();\n    run_one_scan_loop();\n\n    EXPECT_NO_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    Keys,\n    MousekeyParametrized,\n    ::testing::Values(\n        //                                Key               ,                           X, Y, H, V, Buttons Mask\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_1},     MouseKeyExpectations{ 0, 0, 0, 0, 1}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_2},     MouseKeyExpectations{ 0, 0, 0, 0, 2}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_3},     MouseKeyExpectations{ 0, 0, 0, 0, 4}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_4},     MouseKeyExpectations{ 0, 0, 0, 0, 8}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_5},     MouseKeyExpectations{ 0, 0, 0, 0, 16}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_6},     MouseKeyExpectations{ 0, 0, 0, 0, 32}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_7},     MouseKeyExpectations{ 0, 0, 0, 0, 64}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_8},     MouseKeyExpectations{ 0, 0, 0, 0, 128}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_CURSOR_UP},    MouseKeyExpectations{ 0,-8, 0, 0, 0}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_CURSOR_DOWN},  MouseKeyExpectations{ 0, 8, 0, 0, 0}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_CURSOR_LEFT},  MouseKeyExpectations{-8, 0, 0, 0, 0}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_CURSOR_RIGHT}, MouseKeyExpectations{ 8, 0, 0, 0, 0}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_WHEEL_UP},     MouseKeyExpectations{ 0, 0, 0, 1, 0}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_WHEEL_DOWN},   MouseKeyExpectations{ 0, 0, 0,-1, 0}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_WHEEL_LEFT},   MouseKeyExpectations{ 0, 0,-1, 0, 0}),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_WHEEL_RIGHT},  MouseKeyExpectations{ 0, 0, 1, 0, 0})\n        ));\n// clang-format on\n"
  },
  {
    "path": "tests/no_tapping/no_action_tapping/config.h",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define NO_ACTION_TAPPING\n"
  },
  {
    "path": "tests/no_tapping/no_action_tapping/test.mk",
    "content": "# Copyright 2017 Fred Sundvik\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n"
  },
  {
    "path": "tests/no_tapping/no_action_tapping/test_layer_tap.cpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass Tapping : public TestFixture {};\n\nTEST_F(Tapping, TapP_Layer_Tap_KeyReportsKey) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_shift_hold_p_tap = KeymapKey(0, 7, 0, LT(1, KC_P));\n\n    set_keymap({key_shift_hold_p_tap});\n\n    key_shift_hold_p_tap.press();\n    EXPECT_REPORT(driver, (KC_P));\n    run_one_scan_loop();\n\n    key_shift_hold_p_tap.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Tapping, HoldP_Layer_Tap_KeyReportsKey) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 7, 0, LT(1, KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    mod_tap_hold_key.press();\n    EXPECT_REPORT(driver, (KC_P));\n\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    EXPECT_NO_REPORT(driver);\n\n    mod_tap_hold_key.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/no_tapping/no_action_tapping/test_mod_tap.cpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass Tapping : public TestFixture {};\n\nTEST_F(Tapping, TapA_SHFT_T_KeyReportsKey) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_shift_hold_p_tap = KeymapKey(0, 7, 0, SFT_T(KC_P));\n\n    set_keymap({key_shift_hold_p_tap});\n\n    key_shift_hold_p_tap.press();\n    EXPECT_REPORT(driver, (KC_P));\n    run_one_scan_loop();\n\n    key_shift_hold_p_tap.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Tapping, HoldA_SHFT_T_KeyReportsShift) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 7, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    mod_tap_hold_key.press();\n    EXPECT_REPORT(driver, (KC_P));\n\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    EXPECT_NO_REPORT(driver);\n\n    mod_tap_hold_key.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Tapping, ANewTapWithinTappingTermIsBuggy) {\n    // See issue #1478 for more information\n    TestDriver driver;\n    InSequence s;\n    auto       key_shift_hold_p_tap = KeymapKey(0, 7, 0, SFT_T(KC_P));\n\n    set_keymap({key_shift_hold_p_tap});\n\n    // Tapping keys does nothing on press\n    key_shift_hold_p_tap.press();\n    EXPECT_REPORT(driver, (KC_P));\n    run_one_scan_loop();\n\n    key_shift_hold_p_tap.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    key_shift_hold_p_tap.press();\n    EXPECT_REPORT(driver, (KC_P));\n    run_one_scan_loop();\n\n    key_shift_hold_p_tap.release();\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n\n    key_shift_hold_p_tap.press();\n    EXPECT_REPORT(driver, (KC_P));\n    run_one_scan_loop();\n    key_shift_hold_p_tap.release();\n\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n\n    key_shift_hold_p_tap.press();\n    // Shouldn't be called here really\n    EXPECT_REPORT(driver, (KC_P));\n    idle_for(TAPPING_TERM);\n\n    EXPECT_EMPTY_REPORT(driver);\n    key_shift_hold_p_tap.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/no_tapping/no_action_tapping/test_one_shot_keys.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"action_util.h\"\n#include \"keyboard_report_util.hpp\"\n#include \"test_common.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass OneShot : public TestFixture {};\n\nTEST_F(OneShot, OSMWithoutAdditionalKeypressDoesNothing) {\n    TestDriver driver;\n    auto       osm_key = KeymapKey(0, 0, 0, OSM(MOD_LSFT), KC_LSFT);\n\n    set_keymap({osm_key});\n\n    /* Press and release OSM key*/\n    EXPECT_NO_REPORT(driver);\n    osm_key.press();\n    EXPECT_REPORT(driver, (osm_key.report_code));\n    run_one_scan_loop();\n    EXPECT_NO_REPORT(driver);\n    osm_key.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSL_No_ReportPress) {\n    TestDriver driver;\n    auto       osl_key     = KeymapKey{0, 0, 0, OSL(1)};\n    auto       empty_key   = KeymapKey{0, 1, 0, KC_NO};\n    auto       regular_key = KeymapKey{1, 1, 0, KC_A};\n\n    set_keymap({osl_key, empty_key, regular_key});\n\n    /* Press OSL key */\n    EXPECT_NO_REPORT(driver);\n    osl_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release OSL key */\n    EXPECT_NO_REPORT(driver);\n    osl_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSL_ReportPress) {\n    TestDriver driver;\n    auto       osl_key     = KeymapKey{0, 0, 0, OSL(1)};\n    auto       empty_key   = KeymapKey{0, 1, 0, KC_NO};\n    auto       regular_key = KeymapKey{1, 1, 0, KC_A};\n\n    set_keymap({osl_key, empty_key, regular_key});\n\n    /* Press OSL key */\n    osl_key.press();\n    run_one_scan_loop();\n    EXPECT_NO_REPORT(driver);\n\n    /* Press regular key */\n    regular_key.press();\n    run_one_scan_loop();\n    EXPECT_NO_REPORT(driver);\n\n    /* Release regular key */\n    regular_key.release();\n    run_one_scan_loop();\n    EXPECT_NO_REPORT(driver);\n\n    /* Release OSL key */\n    osl_key.release();\n    run_one_scan_loop();\n    EXPECT_NO_REPORT(driver);\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/no_tapping/no_mod_tap_mods/config.h",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define NO_ACTION_TAPPING\n#define NO_ACTION_TAPPING_MODTAP_MODS\n"
  },
  {
    "path": "tests/no_tapping/no_mod_tap_mods/test.mk",
    "content": "# Copyright 2017 Fred Sundvik\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n"
  },
  {
    "path": "tests/no_tapping/no_mod_tap_mods/test_tapping.cpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass Tapping : public TestFixture {};\n\nTEST_F(Tapping, TapA_SHFT_T_KeyReportsKey) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_shift_hold_p_tap = KeymapKey(0, 7, 0, SFT_T(KC_P));\n\n    set_keymap({key_shift_hold_p_tap});\n\n    key_shift_hold_p_tap.press();\n    EXPECT_REPORT(driver, (KC_LSFT));\n    run_one_scan_loop();\n\n    key_shift_hold_p_tap.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(Tapping, HoldA_SHFT_T_KeyReportsShift) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 7, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    mod_tap_hold_key.press();\n    EXPECT_REPORT(driver, (KC_LSFT));\n\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    EXPECT_NO_REPORT(driver);\n\n    mod_tap_hold_key.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(Tapping, ANewTapWithinTappingTermIsBuggy) {\n    // See issue #1478 for more information\n    TestDriver driver;\n    InSequence s;\n    auto       key_shift_hold_p_tap = KeymapKey(0, 7, 0, SFT_T(KC_P));\n\n    set_keymap({key_shift_hold_p_tap});\n\n    // Tapping keys does nothing on press\n    key_shift_hold_p_tap.press();\n    EXPECT_REPORT(driver, (KC_LSFT));\n    run_one_scan_loop();\n\n    key_shift_hold_p_tap.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    key_shift_hold_p_tap.press();\n    EXPECT_REPORT(driver, (KC_LSFT));\n    run_one_scan_loop();\n\n    key_shift_hold_p_tap.release();\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n\n    key_shift_hold_p_tap.press();\n    EXPECT_REPORT(driver, (KC_LSFT));\n    run_one_scan_loop();\n    key_shift_hold_p_tap.release();\n\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n\n    key_shift_hold_p_tap.press();\n    // Shouldn't be called here really\n    EXPECT_REPORT(driver, (KC_LSFT));\n    idle_for(TAPPING_TERM);\n\n    EXPECT_EMPTY_REPORT(driver);\n    key_shift_hold_p_tap.release();\n    run_one_scan_loop();\n}\n"
  },
  {
    "path": "tests/pointing/config.h",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/pointing/invertandrotate/config.h",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define POINTING_DEVICE_INVERT_X\n#define POINTING_DEVICE_ROTATION_90\n"
  },
  {
    "path": "tests/pointing/invertandrotate/test.mk",
    "content": "POINTING_DEVICE_ENABLE = yes\nPOINTING_DEVICE_DRIVER = custom\n"
  },
  {
    "path": "tests/pointing/invertandrotate/test_invertandrotate.cpp",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"gtest/gtest.h\"\n#include \"mouse_report_util.hpp\"\n#include \"test_common.hpp\"\n#include \"test_pointing_device_driver.h\"\n\nusing testing::_;\n\nstruct SimpleReport {\n    int16_t x;\n    int16_t y;\n    int16_t h;\n    int16_t v;\n};\n\nclass Pointing : public TestFixture {};\nclass PointingInvertAndRotateParametrized : public ::testing::WithParamInterface<std::pair<SimpleReport, SimpleReport>>, public Pointing {};\n\nTEST_P(PointingInvertAndRotateParametrized, PointingInvertAndRotateOrder) {\n    TestDriver   driver;\n    SimpleReport input        = GetParam().first;\n    SimpleReport expectations = GetParam().second;\n\n    pd_set_x(input.x);\n    pd_set_y(input.y);\n    pd_set_h(input.h);\n    pd_set_v(input.v);\n\n    EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0));\n    run_one_scan_loop();\n\n    // EXPECT_EMPTY_MOUSE_REPORT(driver);\n    pd_clear_movement();\n    run_one_scan_loop();\n\n    EXPECT_NO_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    InvertAndRotate,\n    PointingInvertAndRotateParametrized,\n    ::testing::Values(\n        //                      Input                       Expected              // Actual Result - Rotate 90 then Invert X\n        std::make_pair(SimpleReport{ -1,  0, 0, 0}, SimpleReport{  0,  1, 0, 0}), // LEFT  - DOWN\n        std::make_pair(SimpleReport{  0, -1, 0, 0}, SimpleReport{  1,  0, 0, 0}), // UP    - RIGHT\n        std::make_pair(SimpleReport{  1,  0, 0, 0}, SimpleReport{  0, -1, 0, 0}), // RIGHT - UP\n        std::make_pair(SimpleReport{  0,  1, 0, 0}, SimpleReport{ -1,  0, 0, 0})  // DOWN  - LEFT\n        //                      Input                       Expected              // Invert X then Rotate 90\n        // std::make_pair(SimpleReport{ -1,  0, 0, 0}, SimpleReport{  0, -1, 0, 0}), // LEFT  - UP\n        // std::make_pair(SimpleReport{  0, -1, 0, 0}, SimpleReport{ -1,  0, 0, 0}), // UP    - LEFT\n        // std::make_pair(SimpleReport{  1,  0, 0, 0}, SimpleReport{  0,  1, 0, 0}), // RIGHT - DOWN\n        // std::make_pair(SimpleReport{  0,  1, 0, 0}, SimpleReport{  1,  0, 0, 0})  // DOWN  - RIGHT\n        ));\n// clang-format on\n"
  },
  {
    "path": "tests/pointing/invertxy/config.h",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define POINTING_DEVICE_INVERT_X\n#define POINTING_DEVICE_INVERT_Y\n"
  },
  {
    "path": "tests/pointing/invertxy/test.mk",
    "content": "POINTING_DEVICE_ENABLE = yes\nPOINTING_DEVICE_DRIVER = custom\n"
  },
  {
    "path": "tests/pointing/invertxy/test_invertxy.cpp",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"gtest/gtest.h\"\n#include \"mouse_report_util.hpp\"\n#include \"test_common.hpp\"\n#include \"test_pointing_device_driver.h\"\n\nusing testing::_;\n\nstruct SimpleReport {\n    int16_t x;\n    int16_t y;\n    int16_t h;\n    int16_t v;\n};\n\nclass Pointing : public TestFixture {};\nclass PointingInvertXYParametrized : public ::testing::WithParamInterface<std::pair<SimpleReport, SimpleReport>>, public Pointing {};\n\nTEST_P(PointingInvertXYParametrized, PointingInvertXY) {\n    TestDriver   driver;\n    SimpleReport input        = GetParam().first;\n    SimpleReport expectations = GetParam().second;\n\n    pd_set_x(input.x);\n    pd_set_y(input.y);\n    pd_set_h(input.h);\n    pd_set_v(input.v);\n\n    EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0));\n    run_one_scan_loop();\n\n    // EXPECT_EMPTY_MOUSE_REPORT(driver);\n    pd_clear_movement();\n    run_one_scan_loop();\n\n    EXPECT_NO_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    X_Y_XY,\n    PointingInvertXYParametrized,\n    ::testing::Values(\n        //                      Input                       Expected\n        std::make_pair(SimpleReport{ 33,    0, 0, 0}, SimpleReport{ -33,   0, 0, 0}),\n        std::make_pair(SimpleReport{  0, -127, 0, 0}, SimpleReport{   0, 127, 0, 0}),\n        std::make_pair(SimpleReport{  10, -20, 0, 0}, SimpleReport{ -10,  20, 0, 0})\n        ));\n// clang-format on\n"
  },
  {
    "path": "tests/pointing/rotate180/config.h",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define POINTING_DEVICE_ROTATION_180\n"
  },
  {
    "path": "tests/pointing/rotate180/test.mk",
    "content": "POINTING_DEVICE_ENABLE = yes\nPOINTING_DEVICE_DRIVER = custom\n"
  },
  {
    "path": "tests/pointing/rotate180/test_rotate180.cpp",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"gtest/gtest.h\"\n#include \"mouse_report_util.hpp\"\n#include \"test_common.hpp\"\n#include \"test_pointing_device_driver.h\"\n\nusing testing::_;\n\nstruct SimpleReport {\n    int16_t x;\n    int16_t y;\n    int16_t h;\n    int16_t v;\n};\n\nclass Pointing : public TestFixture {};\nclass PointingRotateParametrized : public ::testing::WithParamInterface<std::pair<SimpleReport, SimpleReport>>, public Pointing {};\n\nTEST_P(PointingRotateParametrized, PointingRotateXY) {\n    TestDriver   driver;\n    SimpleReport input        = GetParam().first;\n    SimpleReport expectations = GetParam().second;\n\n    pd_set_x(input.x);\n    pd_set_y(input.y);\n    pd_set_h(input.h);\n    pd_set_v(input.v);\n\n    EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0));\n    run_one_scan_loop();\n\n    // EXPECT_EMPTY_MOUSE_REPORT(driver);\n    pd_clear_movement();\n    run_one_scan_loop();\n\n    EXPECT_NO_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    Rotate180,\n    PointingRotateParametrized,\n    ::testing::Values(\n        //                      Input                       Expected\n        // Rotate Clockwise\n        std::make_pair(SimpleReport{  0,-1, 0, 0}, SimpleReport{ 0, 1, 0, 0}), // UP    - DOWN\n        std::make_pair(SimpleReport{  0, 1, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // DOWN  - UP\n        std::make_pair(SimpleReport{  1, 0, 0, 0}, SimpleReport{-1, 0, 0, 0}), // RIGHT - LEFT\n        std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 1, 0, 0, 0})  // LEFT  - RIGHT\n        ));\n// clang-format on\n"
  },
  {
    "path": "tests/pointing/rotate270/config.h",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define POINTING_DEVICE_ROTATION_270\n"
  },
  {
    "path": "tests/pointing/rotate270/test.mk",
    "content": "POINTING_DEVICE_ENABLE = yes\nPOINTING_DEVICE_DRIVER = custom\n"
  },
  {
    "path": "tests/pointing/rotate270/test_rotate270.cpp",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"gtest/gtest.h\"\n#include \"mouse_report_util.hpp\"\n#include \"test_common.hpp\"\n#include \"test_pointing_device_driver.h\"\n\nusing testing::_;\n\nstruct SimpleReport {\n    int16_t x;\n    int16_t y;\n    int16_t h;\n    int16_t v;\n};\n\nclass Pointing : public TestFixture {};\nclass PointingRotateParametrized : public ::testing::WithParamInterface<std::pair<SimpleReport, SimpleReport>>, public Pointing {};\n\nTEST_P(PointingRotateParametrized, PointingRotateXY) {\n    TestDriver   driver;\n    SimpleReport input        = GetParam().first;\n    SimpleReport expectations = GetParam().second;\n\n    pd_set_x(input.x);\n    pd_set_y(input.y);\n    pd_set_h(input.h);\n    pd_set_v(input.v);\n\n    EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0));\n    run_one_scan_loop();\n\n    // EXPECT_EMPTY_MOUSE_REPORT(driver);\n    pd_clear_movement();\n    run_one_scan_loop();\n\n    EXPECT_NO_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    Rotate270,\n    PointingRotateParametrized,\n    ::testing::Values(\n        //                      Input                       Expected\n        // Actual Result - Rotate Anticlockwise\n        std::make_pair(SimpleReport{  0,-1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // UP    - RIGHT\n        std::make_pair(SimpleReport{  0, 1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // DOWN  - LEFT\n        std::make_pair(SimpleReport{  1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}), // RIGHT - DOWN\n        std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0})  // LEFT  - UP\n        // Rotate Clockwise\n        // std::make_pair(SimpleReport{  0,-1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // UP    - LEFT\n        // std::make_pair(SimpleReport{  0, 1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // DOWN  - RIGHT\n        // std::make_pair(SimpleReport{  1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // RIGHT - UP\n        // std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0})  // LEFT  - DOWN\n        ));\n// clang-format on\n"
  },
  {
    "path": "tests/pointing/rotate90/config.h",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define POINTING_DEVICE_ROTATION_90\n"
  },
  {
    "path": "tests/pointing/rotate90/test.mk",
    "content": "POINTING_DEVICE_ENABLE = yes\nPOINTING_DEVICE_DRIVER = custom\n"
  },
  {
    "path": "tests/pointing/rotate90/test_rotate90.cpp",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"gtest/gtest.h\"\n#include \"mouse_report_util.hpp\"\n#include \"test_common.hpp\"\n#include \"test_pointing_device_driver.h\"\n\nusing testing::_;\n\nstruct SimpleReport {\n    int16_t x;\n    int16_t y;\n    int16_t h;\n    int16_t v;\n};\n\nclass Pointing : public TestFixture {};\nclass PointingRotateParametrized : public ::testing::WithParamInterface<std::pair<SimpleReport, SimpleReport>>, public Pointing {};\n\nTEST_P(PointingRotateParametrized, PointingRotateXY) {\n    TestDriver   driver;\n    SimpleReport input        = GetParam().first;\n    SimpleReport expectations = GetParam().second;\n\n    pd_set_x(input.x);\n    pd_set_y(input.y);\n    pd_set_h(input.h);\n    pd_set_v(input.v);\n\n    EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0));\n    run_one_scan_loop();\n\n    // EXPECT_EMPTY_MOUSE_REPORT(driver);\n    pd_clear_movement();\n    run_one_scan_loop();\n\n    EXPECT_NO_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    Rotate90,\n    PointingRotateParametrized,\n    ::testing::Values(\n        //                      Input                       Expected\n        // Actual Result - Rotate Anticlockwise\n        std::make_pair(SimpleReport{  0,-1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // UP    - LEFT\n        std::make_pair(SimpleReport{  0, 1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // DOWN  - RIGHT\n        std::make_pair(SimpleReport{  1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // RIGHT - UP\n        std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0})  // LEFT  - DOWN\n        // Rotate Clockwise\n        // std::make_pair(SimpleReport{  0,-1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // UP    - RIGHT\n        // std::make_pair(SimpleReport{  0, 1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // DOWN  - LEFT\n        // std::make_pair(SimpleReport{  1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // RIGHT - DOWN\n        // std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0})  // LEFT  - UP\n        ));\n// clang-format on\n"
  },
  {
    "path": "tests/pointing/test.mk",
    "content": "POINTING_DEVICE_ENABLE = yes\nMOUSEKEY_ENABLE = no\nPOINTING_DEVICE_DRIVER = custom\n\n"
  },
  {
    "path": "tests/pointing/test_pointing.cpp",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"gtest/gtest.h\"\n#include \"mouse_report_util.hpp\"\n#include \"test_common.hpp\"\n#include \"test_pointing_device_driver.h\"\n#include \"mousekey.h\"\n\nusing testing::_;\n\nclass Pointing : public TestFixture {};\nclass PointingButtonsViaMousekeysParametrized : public ::testing::WithParamInterface<std::pair<KeymapKey, uint8_t>>, public Pointing {};\n\nTEST_F(Pointing, SendMouseIsNotCalledWithNoInput) {\n    TestDriver driver;\n    EXPECT_NO_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(Pointing, Xnegative) {\n    TestDriver driver;\n\n    pd_set_x(-10);\n    EXPECT_MOUSE_REPORT(driver, (-10, 0, 0, 0, 0));\n    run_one_scan_loop();\n\n    pd_clear_movement();\n    // EXPECT_EMPTY_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Pointing, Xpositive) {\n    TestDriver driver;\n\n    pd_set_x(10);\n    EXPECT_MOUSE_REPORT(driver, (10, 0, 0, 0, 0));\n    run_one_scan_loop();\n\n    pd_clear_movement();\n    // EXPECT_EMPTY_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Pointing, Ynegative) {\n    TestDriver driver;\n\n    pd_set_y(-20);\n    EXPECT_MOUSE_REPORT(driver, (0, -20, 0, 0, 0));\n    run_one_scan_loop();\n\n    pd_clear_movement();\n    // EXPECT_EMPTY_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Pointing, Ypositive) {\n    TestDriver driver;\n\n    pd_set_y(20);\n    EXPECT_MOUSE_REPORT(driver, (0, 20, 0, 0, 0));\n    run_one_scan_loop();\n\n    pd_clear_movement();\n    // EXPECT_EMPTY_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Pointing, XandY) {\n    TestDriver driver;\n\n    pd_set_x(-50);\n    pd_set_y(100);\n    EXPECT_MOUSE_REPORT(driver, (-50, 100, 0, 0, 0));\n    run_one_scan_loop();\n\n    pd_clear_movement();\n    // EXPECT_EMPTY_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Pointing, CorrectButtonIsReportedWhenPressed) {\n    TestDriver driver;\n\n    EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, 1));\n    pd_press_button(POINTING_DEVICE_BUTTON1);\n    run_one_scan_loop();\n\n    EXPECT_EMPTY_MOUSE_REPORT(driver);\n    pd_release_button(POINTING_DEVICE_BUTTON1);\n    run_one_scan_loop();\n\n    EXPECT_NO_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    pd_clear_all_buttons();\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Pointing, CorrectButtonIsReportedWhenKeyPressed) {\n    TestDriver driver;\n    auto       key = KeymapKey(0, 0, 0, KC_MS_BTN1);\n    set_keymap({key});\n\n    EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, 1));\n    key.press();\n    run_one_scan_loop();\n\n    EXPECT_EMPTY_MOUSE_REPORT(driver);\n    key.release();\n    run_one_scan_loop();\n\n    EXPECT_NO_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_P(PointingButtonsViaMousekeysParametrized, MouseKeysViaPointingDriver) {\n    TestDriver driver;\n    KeymapKey  mouse_key   = GetParam().first;\n    uint8_t    button_mask = GetParam().second;\n\n    set_keymap({mouse_key});\n\n    EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, button_mask));\n    mouse_key.press();\n    run_one_scan_loop();\n\n    EXPECT_EMPTY_MOUSE_REPORT(driver);\n    mouse_key.release();\n    run_one_scan_loop();\n\n    EXPECT_NO_MOUSE_REPORT(driver);\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    ButtonsOneToEight,\n    PointingButtonsViaMousekeysParametrized,\n    ::testing::Values(\n        //                                Key               , Buttons Mask\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_1}, 1),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_2}, 2),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_3}, 4),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_4}, 8),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_5}, 16),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_6}, 32),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_7}, 64),\n        std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_8}, 128)\n        ));\n// clang-format on\n"
  },
  {
    "path": "tests/repeat_key/alt_repeat_key/config.h",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/repeat_key/alt_repeat_key/test.mk",
    "content": "# Copyright 2023 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nREPEAT_KEY_ENABLE = yes\n\nEXTRAKEY_ENABLE = yes\n"
  },
  {
    "path": "tests/repeat_key/alt_repeat_key/test_alt_repeat_key.cpp",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include <functional>\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing ::testing::AnyNumber;\nusing ::testing::InSequence;\n\nnamespace {\n\nbool process_record_user_default(uint16_t keycode, keyrecord_t* record) {\n    return true;\n}\n\nbool remember_last_key_user_default(uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) {\n    return true;\n}\n\nuint16_t get_alt_repeat_key_keycode_user_default(uint16_t keycode, uint8_t mods) {\n    return KC_TRNS;\n}\n\n// Indirections so that process_record_user() can be replaced with other\n// functions in the test cases below.\nstd::function<bool(uint16_t, keyrecord_t*)>           process_record_user_fun             = process_record_user_default;\nstd::function<bool(uint16_t, keyrecord_t*, uint8_t*)> remember_last_key_user_fun          = remember_last_key_user_default;\nstd::function<uint16_t(uint16_t, uint8_t)>            get_alt_repeat_key_keycode_user_fun = get_alt_repeat_key_keycode_user_default;\n\nextern \"C\" bool process_record_user(uint16_t keycode, keyrecord_t* record) {\n    return process_record_user_fun(keycode, record);\n}\n\nextern \"C\" bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) {\n    return remember_last_key_user_fun(keycode, record, remembered_mods);\n}\n\nextern \"C\" uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods) {\n    return get_alt_repeat_key_keycode_user_fun(keycode, mods);\n}\n\nclass AltRepeatKey : public TestFixture {\n   public:\n    bool process_record_user_was_called_;\n\n    void SetUp() override {\n        process_record_user_fun             = process_record_user_default;\n        remember_last_key_user_fun          = remember_last_key_user_default;\n        get_alt_repeat_key_keycode_user_fun = get_alt_repeat_key_keycode_user_default;\n    }\n\n    void ExpectProcessRecordUserCalledWith(bool expected_press, uint16_t expected_keycode, int8_t expected_repeat_key_count) {\n        process_record_user_was_called_ = false;\n        process_record_user_fun         = [=](uint16_t keycode, keyrecord_t* record) {\n            EXPECT_EQ(record->event.pressed, expected_press);\n            EXPECT_KEYCODE_EQ(keycode, expected_keycode);\n            EXPECT_EQ(get_repeat_key_count(), expected_repeat_key_count);\n            // Tests below use this to verify process_record_user() was called.\n            process_record_user_was_called_ = true;\n            return true;\n        };\n    }\n\n    // Expects that the characters of `s` are sent.\n    // NOTE: This implementation is limited to chars a-z, A-Z.\n    void ExpectString(TestDriver& driver, const std::string& s) {\n        InSequence seq;\n        for (int c : s) {\n            switch (c) {\n                case 'a' ... 'z': { // Lowercase letter.\n                    uint16_t keycode = c - ('a' - KC_A);\n                    EXPECT_REPORT(driver, (keycode));\n                } break;\n\n                case 'A' ... 'Z': { // Capital letter = KC_LSFT + letter key.\n                    uint16_t keycode = c - ('A' - KC_A);\n                    EXPECT_REPORT(driver, (KC_LSFT, keycode));\n                } break;\n            }\n        }\n    }\n};\n\nTEST_F(AltRepeatKey, AlternateBasic) {\n    TestDriver driver;\n    KeymapKey  key_bspc(0, 0, 0, KC_BSPC);\n    KeymapKey  key_pgdn(0, 1, 0, KC_PGDN);\n    KeymapKey  key_pgup(0, 2, 0, KC_PGUP);\n    KeymapKey  key_repeat(0, 4, 0, QK_REP);\n    KeymapKey  key_alt_repeat(0, 5, 0, QK_AREP);\n    set_keymap({key_bspc, key_pgdn, key_pgup, key_repeat, key_alt_repeat});\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    {\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_BSPC));\n        EXPECT_REPORT(driver, (KC_DEL));\n        EXPECT_REPORT(driver, (KC_DEL));\n        EXPECT_REPORT(driver, (KC_BSPC));\n        EXPECT_REPORT(driver, (KC_DEL));\n        EXPECT_REPORT(driver, (KC_PGDN));\n        EXPECT_REPORT(driver, (KC_PGUP));\n        EXPECT_REPORT(driver, (KC_PGUP));\n        EXPECT_REPORT(driver, (KC_PGDN));\n    }\n\n    tap_key(key_bspc);\n\n    for (int n = 1; n <= 2; ++n) { // Tap the Alternate Repeat Key twice.\n        ExpectProcessRecordUserCalledWith(true, KC_DEL, -n);\n        key_alt_repeat.press(); // Press the Alternate Repeat Key.\n        run_one_scan_loop();\n        EXPECT_TRUE(process_record_user_was_called_);\n\n        // Expect the corresponding release event.\n        ExpectProcessRecordUserCalledWith(false, KC_DEL, -n);\n        key_alt_repeat.release(); // Release the Repeat Key.\n        run_one_scan_loop();\n        EXPECT_TRUE(process_record_user_was_called_);\n    }\n\n    process_record_user_fun = process_record_user_default;\n    tap_keys(key_repeat, key_alt_repeat);\n    tap_keys(key_pgdn, key_alt_repeat);\n    tap_keys(key_pgup, key_alt_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nstruct TestParamsAlternateKeyCodes {\n    uint16_t keycode;\n    uint8_t  mods;\n    uint16_t expected_alt_keycode;\n};\n\n// Tests `get_alt_repeat_key_keycode()` for various keycodes.\nTEST_F(AltRepeatKey, GetAltRepeatKeyKeycode) {\n    for (const auto& params : std::vector<TestParamsAlternateKeyCodes>({\n             // clang-format off\n          // Each line tests one call to `get_alt_repeat_key_keycode()`:\n          // {keycode, mods, expected_alt_keycode}.\n          // Arrows.\n          {KC_LEFT, 0, KC_RGHT},\n          {KC_RGHT, 0, KC_LEFT},\n          {KC_LEFT, MOD_BIT(KC_LSFT), LSFT(KC_RGHT)},\n          {KC_LEFT, MOD_BIT(KC_RSFT), RSFT(KC_RGHT)},\n          {KC_LEFT, MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT), C(S(KC_RGHT))},\n          {KC_LEFT, MOD_BIT(KC_LGUI), LGUI(KC_RGHT)},\n          {C(KC_LEFT), MOD_BIT(KC_LSFT), C(S(KC_RGHT))},\n          {KC_UP, 0, KC_DOWN},\n          // Navigation keys.\n          {KC_PGUP, 0, KC_PGDN},\n          {KC_HOME, 0, KC_END },\n          // Media keys.\n          {KC_WBAK, 0, KC_WFWD},\n          {KC_MNXT, 0, KC_MPRV},\n          {KC_MRWD, 0, KC_MFFD},\n          {KC_VOLU, 0, KC_VOLD},\n          {KC_BRIU, 0, KC_BRID},\n          // Emacs navigation.\n          {KC_N, MOD_BIT(KC_LCTL), C(KC_P)},\n          {KC_B, MOD_BIT(KC_LCTL), LCTL(KC_F)},\n          {KC_B, MOD_BIT(KC_RCTL), RCTL(KC_F)},\n          {KC_B, MOD_BIT(KC_LALT), LALT(KC_F)},\n          {KC_F, MOD_BIT(KC_LCTL), C(KC_B)},\n          {KC_A, MOD_BIT(KC_LCTL), C(KC_E)},\n          {KC_D, MOD_BIT(KC_LCTL), C(KC_U)},\n          // Vim navigation.\n          {KC_J, 0, KC_K},\n          {KC_K, 0, KC_J},\n          {KC_H, 0, KC_L},\n          {KC_B, 0, KC_W},\n          {KC_W, 0, KC_B},\n          {KC_E, 0, KC_B},\n          {KC_B, MOD_BIT(KC_LSFT), S(KC_W)},\n          {KC_W, MOD_BIT(KC_LSFT), S(KC_B)},\n          {KC_E, MOD_BIT(KC_LSFT), S(KC_B)},\n          {KC_O, MOD_BIT(KC_LCTL), C(KC_I)},\n          {KC_I, MOD_BIT(KC_LCTL), C(KC_O)},\n          // Other.\n          {KC_DEL, 0, KC_BSPC},\n          {KC_LBRC, 0, KC_RBRC},\n          {KC_LCBR, 0, KC_RCBR},\n          // Some keys where the last key is a tap-hold key.\n          {LSFT_T(KC_F), MOD_BIT(KC_RCTL), RCTL(KC_B)},\n          {LT(1, KC_A), MOD_BIT(KC_RGUI), RGUI(KC_E)},\n          {RALT_T(KC_J), 0, KC_K},\n          // Some keys where no alternate is defined.\n          {KC_A, 0, KC_NO},\n          {KC_F1, 0, KC_NO},\n          {QK_LEAD, 0, KC_NO},\n          {MO(1), 0, KC_NO},\n             // clang-format on\n         })) {\n        SCOPED_TRACE(std::string(\"Input keycode: \") + get_keycode_string(params.keycode));\n        set_last_keycode(params.keycode);\n        set_last_mods(params.mods);\n\n        const uint16_t actual = get_alt_repeat_key_keycode();\n\n        EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), params.expected_alt_keycode);\n    }\n}\n\n// Test adding to and overriding the above through the\n// `get_alt_repeat_key_keycode_user()` callback.\nTEST_F(AltRepeatKey, GetAltRepeatKeyKeycodeUser) {\n    get_alt_repeat_key_keycode_user_fun = [](uint16_t keycode, uint8_t mods) -> uint16_t {\n        bool shifted = (mods & MOD_MASK_SHIFT);\n        switch (keycode) {\n            case KC_LEFT:\n                return KC_ENT;\n            case MO(1):\n                return TG(1);\n            case KC_TAB: // Tab <-> Shift + Tab example.\n                if (shifted) {\n                    return KC_TAB;\n                } else {\n                    return S(KC_TAB);\n                }\n        }\n\n        // Ctrl + Y <-> Ctrl + Z example.\n        if ((mods & MOD_MASK_CTRL)) {\n            switch (keycode) {\n                case KC_Y:\n                    return C(KC_Z);\n                case KC_Z:\n                    return C(KC_Y);\n            }\n        }\n\n        return KC_NO;\n    };\n\n    set_last_keycode(KC_LEFT);\n    EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), KC_ENT);\n\n    set_last_keycode(MO(1));\n    EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), TG(1));\n\n    set_last_keycode(KC_TAB);\n    EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), S(KC_TAB));\n\n    set_last_keycode(KC_TAB);\n    set_last_mods(MOD_BIT(KC_LSFT));\n    EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), KC_TAB);\n\n    set_last_keycode(KC_Z);\n    set_last_mods(MOD_BIT(KC_LCTL));\n    EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), C(KC_Y));\n\n    set_last_keycode(KC_Y);\n    set_last_mods(MOD_BIT(KC_LCTL));\n    EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), C(KC_Z));\n}\n\n// Tests rolling from a key to Alternate Repeat.\nTEST_F(AltRepeatKey, RollingToAltRepeat) {\n    TestDriver driver;\n    KeymapKey  key_left(0, 0, 0, KC_LEFT);\n    KeymapKey  key_alt_repeat(0, 1, 0, QK_AREP);\n    set_keymap({key_left, key_alt_repeat});\n\n    {\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_LEFT));\n        EXPECT_REPORT(driver, (KC_LEFT, KC_RGHT));\n        EXPECT_REPORT(driver, (KC_RGHT));\n        EXPECT_EMPTY_REPORT(driver);\n        EXPECT_REPORT(driver, (KC_RGHT));\n        EXPECT_EMPTY_REPORT(driver);\n    }\n\n    // Perform a rolled press from Left to Alternate Repeat.\n\n    ExpectProcessRecordUserCalledWith(true, KC_LEFT, 0);\n    key_left.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    ExpectProcessRecordUserCalledWith(true, KC_RGHT, -1);\n    key_alt_repeat.press(); // Press the Alternate Repeat Key.\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    ExpectProcessRecordUserCalledWith(false, KC_LEFT, 0);\n    key_left.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    ExpectProcessRecordUserCalledWith(false, KC_RGHT, -1);\n    key_alt_repeat.release(); // Release the Alternate Repeat Key.\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    process_record_user_fun = process_record_user_default;\n    tap_key(key_alt_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests rolling from Alternate Repeat to another key.\nTEST_F(AltRepeatKey, RollingFromAltRepeat) {\n    TestDriver driver;\n    KeymapKey  key_left(0, 0, 0, KC_LEFT);\n    KeymapKey  key_up(0, 1, 0, KC_UP);\n    KeymapKey  key_alt_repeat(0, 2, 0, QK_AREP);\n    set_keymap({key_left, key_up, key_alt_repeat});\n\n    {\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_LEFT));\n        EXPECT_EMPTY_REPORT(driver);\n        EXPECT_REPORT(driver, (KC_RGHT));\n        EXPECT_REPORT(driver, (KC_RGHT, KC_UP));\n        EXPECT_REPORT(driver, (KC_UP));\n        EXPECT_EMPTY_REPORT(driver);\n        EXPECT_REPORT(driver, (KC_DOWN));\n        EXPECT_EMPTY_REPORT(driver);\n    }\n\n    tap_key(key_left);\n\n    // Perform a rolled press from Alternate Repeat to Up.\n\n    ExpectProcessRecordUserCalledWith(true, KC_RGHT, -1);\n    key_alt_repeat.press(); // Press the Alternate Repeat Key.\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    ExpectProcessRecordUserCalledWith(true, KC_UP, 0);\n    key_up.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_UP);\n\n    ExpectProcessRecordUserCalledWith(false, KC_RGHT, -1);\n    key_alt_repeat.release(); // Release the Alternate Repeat Key.\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    ExpectProcessRecordUserCalledWith(false, KC_UP, 0);\n    key_up.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    process_record_user_fun = process_record_user_default;\n    tap_key(key_alt_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests using the Alternate Repeat Key on a macro that doesn't have an\n// alternate keycode defined.\nTEST_F(AltRepeatKey, AlternateUnsupportedMacro) {\n    TestDriver driver;\n    KeymapKey  key_foo(0, 0, 0, QK_USER_0);\n    KeymapKey  key_alt_repeat(0, 1, 0, QK_AREP);\n    set_keymap({key_foo, key_alt_repeat});\n\n    process_record_user_fun = [=](uint16_t keycode, keyrecord_t* record) {\n        process_record_user_was_called_ = true;\n        switch (keycode) {\n            case QK_USER_0:\n                if (record->event.pressed) {\n                    SEND_STRING(\"foo\");\n                }\n                break;\n        }\n        return true;\n    };\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    ExpectString(driver, \"foofoo\");\n\n    process_record_user_was_called_ = false;\n    tap_key(key_foo);\n\n    EXPECT_TRUE(process_record_user_was_called_);\n    EXPECT_KEYCODE_EQ(get_last_keycode(), QK_USER_0);\n    EXPECT_KEYCODE_EQ(get_alt_repeat_key_keycode(), KC_NO);\n\n    process_record_user_was_called_ = false;\n    key_alt_repeat.press(); // Press Alternate Repeat.\n    run_one_scan_loop();\n\n    EXPECT_FALSE(process_record_user_was_called_);\n\n    process_record_user_was_called_ = false;\n    key_alt_repeat.release(); // Release Alternate Repeat.\n    run_one_scan_loop();\n\n    EXPECT_FALSE(process_record_user_was_called_);\n\n    process_record_user_was_called_ = false;\n    tap_key(key_foo);\n\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests a macro with custom alternate behavior.\nTEST_F(AltRepeatKey, MacroCustomAlternate) {\n    TestDriver driver;\n    KeymapKey  key_foo(0, 0, 0, QK_USER_0);\n    KeymapKey  key_alt_repeat(0, 1, 0, QK_AREP);\n    set_keymap({key_foo, key_alt_repeat});\n\n    get_alt_repeat_key_keycode_user_fun = [](uint16_t keycode, uint8_t mods) -> uint16_t {\n        switch (keycode) {\n            case QK_USER_0:\n                return QK_USER_0; // QK_USER_0 handles its own alternate.\n            default:\n                return KC_NO; // No key by default.\n        }\n    };\n    process_record_user_fun = [=](uint16_t keycode, keyrecord_t* record) {\n        process_record_user_was_called_ = true;\n        switch (keycode) {\n            case QK_USER_0:\n                if (record->event.pressed) {\n                    if (get_repeat_key_count() >= 0) {\n                        SEND_STRING(\"foo\");\n                    } else { // Key is being alternate repeated.\n                        SEND_STRING(\"bar\");\n                    }\n                }\n                break;\n        }\n        return true;\n    };\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    ExpectString(driver, \"foobarbar\");\n\n    tap_keys(key_foo, key_alt_repeat, key_alt_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests the Additional \"Alternate\" keys example from the documentation page.\nTEST_F(AltRepeatKey, AdditionalAlternateKeysExample) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_w(0, 1, 0, KC_W);\n    KeymapKey  key_altrep2(0, 2, 0, QK_USER_0);\n    KeymapKey  key_altrep3(0, 3, 0, QK_USER_1);\n    set_keymap({key_a, key_w, key_altrep2, key_altrep3});\n\n    remember_last_key_user_fun = [](uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) {\n        switch (keycode) {\n            case QK_USER_0:\n            case QK_USER_1:\n                return false; // Ignore ALTREP keys.\n        }\n        return true; // Other keys can be repeated.\n    };\n    process_record_user_fun = [=](uint16_t keycode, keyrecord_t* record) {\n        switch (keycode) {\n            case QK_USER_0:\n                if (record->event.pressed) {\n                    const uint16_t last_key = get_last_keycode();\n                    switch (last_key) {\n                        case KC_A:\n                            SEND_STRING(/*a*/ \"tion\");\n                            break;\n                        case KC_W:\n                            SEND_STRING(/*w*/ \"hich\");\n                            break;\n                    }\n                }\n                return false;\n            case QK_USER_1:\n                if (record->event.pressed) {\n                    const uint16_t last_key = get_last_keycode();\n                    switch (last_key) {\n                        case KC_A:\n                            SEND_STRING(/*a*/ \"bout\");\n                            break;\n                        case KC_W:\n                            SEND_STRING(/*w*/ \"ould\");\n                            break;\n                    }\n                }\n                return false;\n        }\n        return true;\n    };\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    ExpectString(driver, \"ationwhichaboutwould\");\n\n    tap_keys(key_a, key_altrep2, key_w, key_altrep2);\n    tap_keys(key_a, key_altrep3, key_w, key_altrep3);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n} // namespace\n"
  },
  {
    "path": "tests/repeat_key/config.h",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define NO_ALT_REPEAT_KEY\n"
  },
  {
    "path": "tests/repeat_key/repeat_key_combo/config.h",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/repeat_key/repeat_key_combo/test.mk",
    "content": "# Copyright 2023 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nREPEAT_KEY_ENABLE = yes\n\nCOMBO_ENABLE = yes\nINTROSPECTION_KEYMAP_C = test_combos.c\n"
  },
  {
    "path": "tests/repeat_key/repeat_key_combo/test_combos.c",
    "content": "// Copyright 2023 Stefan Kerkmann (@KarlK90)\n// Copyright 2023 @filterpaper\n// Copyright 2023 Nick Brassel (@tzarc)\n// SPDX-License-Identifier: GPL-2.0-or-later\n#include \"quantum.h\"\n\nconst uint16_t xy_combo[] PROGMEM = {KC_X, KC_Y, COMBO_END};\ncombo_t        key_combos[]       = {COMBO(xy_combo, KC_Q)};\n"
  },
  {
    "path": "tests/repeat_key/repeat_key_combo/test_repeat_key_combo.cpp",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing ::testing::AnyNumber;\nusing ::testing::InSequence;\n\nnamespace {\n\nclass RepeatKey : public TestFixture {};\n\n// Tests repeating a combo, KC_X + KC_Y = KC_Q, by typing\n// \"X, Repeat, Repeat, {X Y}, Repeat, Repeat\". This produces \"xxxqqq\".\nTEST_F(RepeatKey, Combo) {\n    TestDriver driver;\n    KeymapKey  key_x(0, 0, 0, KC_X);\n    KeymapKey  key_y(0, 1, 0, KC_Y);\n    KeymapKey  key_repeat(0, 2, 0, QK_REP);\n    set_keymap({key_x, key_y, key_repeat});\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    {\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_X));\n        EXPECT_REPORT(driver, (KC_X));\n        EXPECT_REPORT(driver, (KC_X));\n        EXPECT_REPORT(driver, (KC_Q));\n        EXPECT_REPORT(driver, (KC_Q));\n        EXPECT_REPORT(driver, (KC_Q));\n    }\n\n    tap_keys(key_x, key_repeat, key_repeat);\n    tap_combo({key_x, key_y});\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_Q);\n\n    tap_keys(key_repeat, key_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n} // namespace\n"
  },
  {
    "path": "tests/repeat_key/test.mk",
    "content": "# Copyright 2023 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nREPEAT_KEY_ENABLE = yes\n\nAUTO_SHIFT_ENABLE = yes\nLAYER_LOCK_ENABLE = yes\n"
  },
  {
    "path": "tests/repeat_key/test_repeat_key.cpp",
    "content": "// Copyright 2023 Google LLC\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 2 of the License, or\n// (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n#include <functional>\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing ::testing::AnyNumber;\nusing ::testing::AnyOf;\nusing ::testing::InSequence;\n\n#define FOO_MACRO SAFE_RANGE\n\nnamespace {\n\nbool process_record_user_default(uint16_t keycode, keyrecord_t* record) {\n    return true;\n}\n\nbool remember_last_key_user_default(uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) {\n    return true;\n}\n\n// Indirection so that process_record_user() and remember_last_key_user()\n// can be replaced with other functions in the test cases below.\nstd::function<bool(uint16_t, keyrecord_t*)>           process_record_user_fun    = process_record_user_default;\nstd::function<bool(uint16_t, keyrecord_t*, uint8_t*)> remember_last_key_user_fun = remember_last_key_user_default;\n\nextern \"C\" bool process_record_user(uint16_t keycode, keyrecord_t* record) {\n    return process_record_user_fun(keycode, record);\n}\nextern \"C\" bool remember_last_key_user(uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) {\n    return remember_last_key_user_fun(keycode, record, remembered_mods);\n}\n\nclass RepeatKey : public TestFixture {\n   public:\n    bool process_record_user_was_called_;\n\n    void SetUp() override {\n        autoshift_disable();\n        process_record_user_fun    = process_record_user_default;\n        remember_last_key_user_fun = remember_last_key_user_default;\n    }\n\n    void ExpectProcessRecordUserCalledWith(bool expected_press, uint16_t expected_keycode, int8_t expected_repeat_key_count) {\n        process_record_user_was_called_ = false;\n        process_record_user_fun         = [=](uint16_t keycode, keyrecord_t* record) {\n            EXPECT_EQ(record->event.pressed, expected_press);\n            EXPECT_KEYCODE_EQ(keycode, expected_keycode);\n            EXPECT_EQ(get_repeat_key_count(), expected_repeat_key_count);\n            // Tests below use this to verify process_record_user() was called.\n            process_record_user_was_called_ = true;\n            return true;\n        };\n    }\n\n    // Expects that the characters of `s` are sent.\n    // NOTE: This implementation is limited to chars a-z, A-Z.\n    void ExpectString(TestDriver& driver, const std::string& s) {\n        InSequence seq;\n        for (int c : s) {\n            switch (c) {\n                case 'a' ... 'z': { // Lowercase letter.\n                    uint16_t keycode = c - ('a' - KC_A);\n                    EXPECT_REPORT(driver, (keycode));\n                } break;\n\n                case 'A' ... 'Z': { // Capital letter = KC_LSFT + letter key.\n                    uint16_t keycode = c - ('A' - KC_A);\n                    EXPECT_REPORT(driver, (KC_LSFT, keycode));\n                } break;\n            }\n        }\n    }\n};\n\n// Tests that \"A, Repeat, Repeat, B, Repeat\" produces \"aaabb\".\nTEST_F(RepeatKey, Basic) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_b(0, 1, 0, KC_B);\n    KeymapKey  key_repeat(0, 2, 0, QK_REP);\n    set_keymap({key_a, key_b, key_repeat});\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    ExpectString(driver, \"aaabb\");\n\n    // When KC_A is pressed, process_record_user() should be called\n    // with a press event with keycode == KC_A and repeat_key_count() == 0.\n    ExpectProcessRecordUserCalledWith(true, KC_A, 0);\n    key_a.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    // After pressing A, the keycode of the key to be repeated is KC_A.\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_A);\n    EXPECT_EQ(get_last_mods(), 0);\n\n    // Expect the corresponding release event when A is released.\n    ExpectProcessRecordUserCalledWith(false, KC_A, 0);\n    key_a.release();\n    run_one_scan_loop();\n\n    for (int n = 1; n <= 2; ++n) { // Tap the Repeat Key twice.\n        // When Repeat is pressed, process_record_user() should be called with a\n        // press event with keycode == KC_A and repeat_key_count() == n.\n        ExpectProcessRecordUserCalledWith(true, KC_A, n);\n        key_repeat.press(); // Press the Repeat Key.\n        run_one_scan_loop();\n        EXPECT_TRUE(process_record_user_was_called_);\n\n        // Expect the corresponding release event.\n        ExpectProcessRecordUserCalledWith(false, KC_A, n);\n        key_repeat.release(); // Release the Repeat Key.\n        run_one_scan_loop();\n        EXPECT_TRUE(process_record_user_was_called_);\n    }\n\n    process_record_user_fun = process_record_user_default;\n    tap_key(key_b);\n    // Then after tapping key_b, the keycode to be repeated becomes KC_B.\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_B);\n\n    tap_key(key_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests repeating a macro. The keycode FOO_MACRO sends \"foo\" when pressed. The\n// test taps \"FOO_MACRO, Repeat, Repeat\", producing \"foofoofoo\".\nTEST_F(RepeatKey, Macro) {\n    TestDriver driver;\n    KeymapKey  key_foo(0, 0, 0, FOO_MACRO);\n    KeymapKey  key_repeat(0, 1, 0, QK_REP);\n    set_keymap({key_foo, key_repeat});\n\n    // Define process_record_user() to handle FOO_MACRO.\n    process_record_user_fun = [](uint16_t keycode, keyrecord_t* record) {\n        switch (keycode) {\n            case FOO_MACRO:\n                if (record->event.pressed) {\n                    SEND_STRING(\"foo\");\n                }\n                break;\n        }\n        return true;\n    };\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    ExpectString(driver, \"foofoofoo\");\n\n    tap_key(key_foo);\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), FOO_MACRO);\n\n    tap_keys(key_repeat, key_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests a macro with customized repeat behavior: \"foo\" is sent normally, \"bar\"\n// on the first repeat, and \"baz\" on subsequent repeats. The test taps\n// \"FOO_MACRO, Repeat, Repeat, FOO_MACRO, Repeat\", producing \"foobarbazfoobar\".\nTEST_F(RepeatKey, MacroCustomRepeat) {\n    TestDriver driver;\n    KeymapKey  key_foo(0, 0, 0, FOO_MACRO);\n    KeymapKey  key_repeat(0, 1, 0, QK_REP);\n    set_keymap({key_foo, key_repeat});\n\n    process_record_user_fun = [](uint16_t keycode, keyrecord_t* record) {\n        switch (keycode) {\n            case FOO_MACRO:\n                if (record->event.pressed) {\n                    switch (get_repeat_key_count()) {\n                        case 0: // When pressed normally.\n                            SEND_STRING(\"foo\");\n                            break;\n                        case 1: // On first repeat.\n                            SEND_STRING(\"bar\");\n                            break;\n                        default: // On subsequent repeats.\n                            SEND_STRING(\"baz\");\n                            break;\n                    }\n                }\n                break;\n        }\n        return true;\n    };\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    ExpectString(driver, \"foobarbazfoobar\");\n\n    tap_key(key_foo);\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), FOO_MACRO);\n\n    tap_keys(key_repeat, key_repeat, key_foo, key_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests repeating keys on different layers. A 2-layer keymap is defined:\n//   Layer 0:   QK_REP , MO(1)  , KC_A\n//   Layer 1:   KC_TRNS, KC_TRNS, KC_B\n// The test does the following, which should produce \"bbbaaa\":\n// 1. Hold MO(1), switching to layer 1.\n// 2. Tap KC_B on layer 1.\n// 3. Release MO(1), switching back to layer 0.\n// 4. Tap Repeat twice.\n// 5. Tap KC_A on layer 0.\n// 6. Hold MO(1), switching to layer 1.\n// 7. Tap Repeat twice.\nTEST_F(RepeatKey, AcrossLayers) {\n    TestDriver driver;\n    KeymapKey  key_repeat(0, 0, 0, QK_REP);\n    KeymapKey  key_mo_1(0, 1, 0, MO(1));\n    KeymapKey  regular_key(0, 2, 0, KC_A);\n    set_keymap({// Layer 0.\n                key_repeat, key_mo_1, regular_key,\n                // Layer 1.\n                KeymapKey{1, 0, 0, KC_TRNS}, KeymapKey{1, 1, 0, KC_TRNS}, KeymapKey{1, 2, 0, KC_B}});\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    ExpectString(driver, \"bbbaaa\");\n\n    key_mo_1.press(); // Hold the MO(1) layer key.\n    run_one_scan_loop();\n    tap_key(regular_key); // Taps the KC_B key on layer 1.\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_B);\n\n    key_mo_1.release(); // Release the layer key.\n    run_one_scan_loop();\n    tap_keys(key_repeat, key_repeat);\n    tap_key(regular_key); // Taps the KC_A key on layer 0.\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_A);\n\n    key_mo_1.press(); // Hold the layer key.\n    run_one_scan_loop();\n    tap_keys(key_repeat, key_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests \"A(down), Repeat(down), A(up), Repeat(up), Repeat\" produces \"aaa\".\nTEST_F(RepeatKey, RollingToRepeat) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_repeat(0, 1, 0, QK_REP);\n    set_keymap({key_a, key_repeat});\n\n    {\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_EMPTY_REPORT(driver);\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_EMPTY_REPORT(driver);\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_EMPTY_REPORT(driver);\n    }\n\n    // Perform a rolled press from A to Repeat.\n\n    ExpectProcessRecordUserCalledWith(true, KC_A, 0);\n    key_a.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    ExpectProcessRecordUserCalledWith(true, KC_A, 1);\n    key_repeat.press(); // Press the Repeat Key.\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    ExpectProcessRecordUserCalledWith(false, KC_A, 0);\n    key_a.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    ExpectProcessRecordUserCalledWith(false, KC_A, 1);\n    key_repeat.release(); // Release the Repeat Key.\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    process_record_user_fun = process_record_user_default;\n    tap_key(key_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests \"A, Repeat(down), B(down), Repeat(up), B(up), Repeat\" produces \"aabb\".\nTEST_F(RepeatKey, RollingFromRepeat) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_b(0, 1, 0, KC_B);\n    KeymapKey  key_repeat(0, 2, 0, QK_REP);\n    set_keymap({key_a, key_b, key_repeat});\n\n    {\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_EMPTY_REPORT(driver);\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_REPORT(driver, (KC_A, KC_B));\n        EXPECT_REPORT(driver, (KC_B));\n        EXPECT_EMPTY_REPORT(driver);\n        EXPECT_REPORT(driver, (KC_B));\n        EXPECT_EMPTY_REPORT(driver);\n    }\n\n    tap_key(key_a);\n\n    // Perform a rolled press from Repeat to B.\n\n    ExpectProcessRecordUserCalledWith(true, KC_A, 1);\n    key_repeat.press(); // Press the Repeat Key.\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    ExpectProcessRecordUserCalledWith(true, KC_B, 0);\n    key_b.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_B);\n\n    ExpectProcessRecordUserCalledWith(false, KC_A, 1);\n    key_repeat.release(); // Release the Repeat Key.\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    ExpectProcessRecordUserCalledWith(false, KC_B, 0);\n    key_b.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    process_record_user_fun = process_record_user_default;\n    tap_key(key_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests Repeat Key with a modifier, types \"AltGr+C, Repeat, Repeat, C\".\nTEST_F(RepeatKey, RecallMods) {\n    TestDriver driver;\n    KeymapKey  key_c(0, 0, 0, KC_C);\n    KeymapKey  key_altgr(0, 1, 0, KC_RALT);\n    KeymapKey  key_repeat(0, 2, 0, QK_REP);\n    set_keymap({key_c, key_altgr, key_repeat});\n\n    // Allow any number of reports with no keys or only KC_RALT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_RALT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    { // Expect: \"AltGr+C, AltGr+C, AltGr+C, C\".\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_RALT, KC_C));\n        EXPECT_REPORT(driver, (KC_RALT, KC_C));\n        EXPECT_REPORT(driver, (KC_RALT, KC_C));\n        EXPECT_REPORT(driver, (KC_C));\n    }\n\n    key_altgr.press();\n    run_one_scan_loop();\n    tap_key(key_c);\n    key_altgr.release();\n    run_one_scan_loop();\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_C);\n    EXPECT_EQ(get_last_mods(), MOD_BIT(KC_RALT));\n\n    tap_keys(key_repeat, key_repeat, key_c);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests that Repeat Key stacks mods, types\n// \"Ctrl+Left, Repeat, Shift+Repeat, Shift+Repeat, Repeat, Left\".\nTEST_F(RepeatKey, StackMods) {\n    TestDriver driver;\n    KeymapKey  key_left(0, 0, 0, KC_LEFT);\n    KeymapKey  key_shift(0, 1, 0, KC_LSFT);\n    KeymapKey  key_ctrl(0, 2, 0, KC_LCTL);\n    KeymapKey  key_repeat(0, 3, 0, QK_REP);\n    set_keymap({key_left, key_shift, key_ctrl, key_repeat});\n\n    // Allow any number of reports with no keys or only mods.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LCTL),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL, KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    { // Expect: \"Ctrl+Left, Ctrl+Shift+Left\".\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_LCTL, KC_LEFT));\n        EXPECT_REPORT(driver, (KC_LCTL, KC_LEFT));\n        EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_LEFT));\n        EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_LEFT));\n        EXPECT_REPORT(driver, (KC_LCTL, KC_LEFT));\n        EXPECT_REPORT(driver, (KC_LEFT));\n    }\n\n    key_ctrl.press();\n    run_one_scan_loop();\n    tap_key(key_left);\n    run_one_scan_loop();\n    key_ctrl.release();\n    run_one_scan_loop();\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_LEFT);\n    EXPECT_EQ(get_last_mods(), MOD_BIT(KC_LCTL));\n\n    tap_key(key_repeat);\n\n    key_shift.press();\n    run_one_scan_loop();\n    tap_keys(key_repeat, key_repeat);\n    key_shift.release();\n    run_one_scan_loop();\n\n    EXPECT_EQ(get_last_mods(), MOD_BIT(KC_LCTL));\n\n    tap_keys(key_repeat, key_left);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Types: \"S(KC_1), Repeat, Ctrl+Repeat, Ctrl+Repeat, Repeat, KC_2\".\nTEST_F(RepeatKey, ShiftedKeycode) {\n    TestDriver driver;\n    KeymapKey  key_exlm(0, 0, 0, S(KC_1));\n    KeymapKey  key_2(0, 1, 0, KC_2);\n    KeymapKey  key_ctrl(0, 2, 0, KC_LCTL);\n    KeymapKey  key_repeat(0, 3, 0, QK_REP);\n    set_keymap({key_exlm, key_2, key_ctrl, key_repeat});\n\n    // Allow any number of reports with no keys or only mods.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LCTL),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL, KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    { // Expect: \"Shift+1, Shift+1, Ctrl+Shift+1, Ctrl+Shift+1, Shift+1, 2\".\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_LSFT, KC_1));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_1));\n        EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_1));\n        EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_1));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_1));\n        EXPECT_REPORT(driver, (KC_2));\n    }\n\n    tap_key(key_exlm);\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), S(KC_1));\n\n    tap_key(key_repeat);\n\n    key_ctrl.press();\n    run_one_scan_loop();\n    tap_keys(key_repeat, key_repeat);\n    key_ctrl.release();\n    run_one_scan_loop();\n\n    tap_keys(key_repeat, key_2);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests Repeat Key with a one-shot Shift, types\n// \"A, OSM(MOD_LSFT), Repeat, Repeat\".\nTEST_F(RepeatKey, WithOneShotShift) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_oneshot_shift(0, 1, 0, OSM(MOD_LSFT));\n    KeymapKey  key_repeat(0, 2, 0, QK_REP);\n    set_keymap({key_a, key_oneshot_shift, key_repeat});\n\n    // Allow any number of reports with no keys or only KC_RALT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    ExpectString(driver, \"aAa\");\n\n    tap_keys(key_a, key_oneshot_shift, key_repeat, key_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests Repeat Key with a mod-tap key, types\n// \"A, Repeat, Repeat, A(down), Repeat, Repeat, A(up), Repeat\".\nTEST_F(RepeatKey, ModTap) {\n    TestDriver driver;\n    KeymapKey  key_mt_a(0, 0, 0, LSFT_T(KC_A));\n    KeymapKey  key_repeat(0, 1, 0, QK_REP);\n    set_keymap({key_mt_a, key_repeat});\n\n    // Allow any number of reports with no keys or only KC_LSFT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    ExpectString(driver, \"aaaAAa\");\n\n    tap_key(key_mt_a);\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), LSFT_T(KC_A));\n\n    tap_keys(key_repeat, key_repeat);\n    key_mt_a.press();\n    run_one_scan_loop();\n    tap_key(key_repeat, TAPPING_TERM + 1);\n    tap_key(key_repeat);\n    key_mt_a.release();\n    run_one_scan_loop();\n    tap_key(key_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests with Auto Shift. When repeating an autoshiftable key, it does not\n// matter how long the original key was held, rather, quickly tapping vs.\n// long-pressing the Repeat Key determines whether the shifted key is repeated.\n//\n// The test does the following, which should produce \"aaABbB\":\n// 1. Tap KC_A quickly.\n// 2. Tap Repeat Key quickly.\n// 3. Long-press Repeat Key.\n// 4. Long-press KC_B.\n// 5. Tap Repeat Key quickly.\n// 6. Long-press Repeat Key.\nTEST_F(RepeatKey, AutoShift) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_b(0, 1, 0, KC_B);\n    KeymapKey  key_repeat(0, 2, 0, QK_REP);\n    set_keymap({key_a, key_b, key_repeat});\n\n    autoshift_enable();\n\n    // Allow any number of reports with no keys or only KC_LSFT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    ExpectString(driver, \"aaABbB\");\n\n    tap_key(key_a); // Tap A quickly.\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_A);\n    EXPECT_EQ(get_last_mods(), 0);\n\n    tap_key(key_repeat);\n    tap_key(key_repeat, AUTO_SHIFT_TIMEOUT + 1);\n\n    tap_key(key_b, AUTO_SHIFT_TIMEOUT + 1); // Long press B.\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_B);\n    EXPECT_EQ(get_last_mods(), 0);\n\n    tap_key(key_repeat);\n    tap_key(key_repeat, AUTO_SHIFT_TIMEOUT + 1);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Defines `remember_last_key_user()` to forget the Shift mod and types:\n// \"Ctrl+A, Repeat, Shift+A, Repeat, Shift+Repeat\".\nTEST_F(RepeatKey, FilterRememberedMods) {\n    TestDriver driver;\n    KeymapKey  key_a(0, 0, 0, KC_A);\n    KeymapKey  key_ctrl(0, 1, 0, KC_LCTL);\n    KeymapKey  key_shift(0, 2, 0, KC_LSFT);\n    KeymapKey  key_repeat(0, 3, 0, QK_REP);\n    set_keymap({key_a, key_ctrl, key_shift, key_repeat});\n\n    remember_last_key_user_fun = [](uint16_t keycode, keyrecord_t* record, uint8_t* remembered_mods) {\n        *remembered_mods &= ~MOD_MASK_SHIFT;\n        return true;\n    };\n\n    // Allow any number of reports with no keys or only mods.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LCTL),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL, KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n\n    { // Expect: \"Ctrl+A, Ctrl+A, Shift+A, A, Shift+A\".\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n        EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    }\n\n    key_ctrl.press();\n    run_one_scan_loop();\n    tap_key(key_a);\n\n    EXPECT_EQ(get_last_mods(), MOD_BIT(KC_LCTL));\n\n    key_ctrl.release();\n    run_one_scan_loop();\n\n    tap_key(key_repeat);\n    key_shift.press();\n    run_one_scan_loop();\n    tap_key(key_a);\n\n    EXPECT_EQ(get_last_mods(), 0); // Shift should be forgotten.\n\n    key_shift.release();\n    run_one_scan_loop();\n\n    tap_key(key_repeat);\n\n    key_shift.press();\n    run_one_scan_loop();\n    tap_key(key_repeat);\n    key_shift.release();\n    run_one_scan_loop();\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests set_last_keycode() and set_last_mods().\nTEST_F(RepeatKey, SetRepeatKeyKeycode) {\n    TestDriver driver;\n    KeymapKey  key_repeat(0, 0, 0, QK_REP);\n    set_keymap({key_repeat});\n\n    // Allow any number of reports with no keys or only KC_LSFT.\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    ExpectString(driver, \"aaBB\");\n\n    set_last_keycode(KC_A);\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_A);\n\n    for (int n = 1; n <= 2; ++n) { // Tap the Repeat Key twice.\n        // When Repeat is pressed, process_record_user() should be called with a\n        // press event with keycode == KC_A and repeat_key_count() == n.\n        ExpectProcessRecordUserCalledWith(true, KC_A, n);\n        key_repeat.press(); // Press the Repeat Key.\n        run_one_scan_loop();\n        EXPECT_TRUE(process_record_user_was_called_);\n\n        // Expect the corresponding release event.\n        ExpectProcessRecordUserCalledWith(false, KC_A, n);\n        key_repeat.release(); // Release the Repeat Key.\n        run_one_scan_loop();\n        EXPECT_TRUE(process_record_user_was_called_);\n    }\n\n    process_record_user_fun = process_record_user_default;\n    set_last_keycode(KC_B);\n    set_last_mods(MOD_BIT(KC_LSFT));\n\n    tap_keys(key_repeat, key_repeat);\n\n    set_last_keycode(KC_NO);\n    tap_keys(key_repeat, key_repeat); // Has no effect.\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Tests the `repeat_key_invoke()` function.\nTEST_F(RepeatKey, RepeatKeyInvoke) {\n    TestDriver driver;\n    KeymapKey  key_s(0, 0, 0, KC_S);\n    set_keymap({key_s});\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    ExpectString(driver, \"ss\");\n\n    tap_key(key_s);\n\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_S);\n\n    // Calling repeat_key_invoke() should result in process_record_user()\n    // getting a press event with keycode KC_S.\n    ExpectProcessRecordUserCalledWith(true, KC_S, 1);\n    keyevent_t event;\n    event.key     = {0, 0};\n    event.pressed = true;\n    event.time    = timer_read();\n    event.type    = KEY_EVENT;\n    repeat_key_invoke(&event);\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    // Make the release event.\n    ExpectProcessRecordUserCalledWith(false, KC_S, 1);\n    event.pressed = false;\n    event.time    = timer_read();\n    repeat_key_invoke(&event);\n    run_one_scan_loop();\n    EXPECT_TRUE(process_record_user_was_called_);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n// Check that mods and Layer Lock are not remembered.\nTEST_F(RepeatKey, IgnoredKeys) {\n    TestDriver driver;\n    KeymapKey  regular_key(0, 0, 0, KC_A);\n    KeymapKey  key_repeat(0, 1, 0, QK_REP);\n    KeymapKey  key_lsft(0, 2, 0, KC_LSFT);\n    KeymapKey  key_lctl(0, 3, 0, KC_LCTL);\n    KeymapKey  key_llck(0, 4, 0, QK_LAYER_LOCK);\n    set_keymap({regular_key, key_repeat, key_lsft, key_lctl, key_llck});\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    {\n        InSequence seq;\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_REPORT(driver, (KC_LSFT));\n        EXPECT_REPORT(driver, (KC_LCTL));\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_REPORT(driver, (KC_A));\n    }\n\n    tap_key(regular_key); // Taps the KC_A key.\n\n    // Tap Shift, Ctrl, and Layer Lock keys, which should not be remembered.\n    tap_keys(key_lsft, key_lctl, key_llck);\n    EXPECT_KEYCODE_EQ(get_last_keycode(), KC_A);\n\n    // Tapping the Repeat Key should still reproduce KC_A.\n    tap_keys(key_repeat, key_repeat);\n\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\n} // namespace\n"
  },
  {
    "path": "tests/secure/config.h",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n// clang-format off\n#define SECURE_UNLOCK_SEQUENCE \\\n    {                          \\\n        {0, 1},                \\\n        {0, 2},                \\\n        {0, 3},                \\\n        {0, 4}                 \\\n    }\n// clang-format on\n\n#define SECURE_UNLOCK_TIMEOUT 20\n#define SECURE_IDLE_TIMEOUT 50\n"
  },
  {
    "path": "tests/secure/test.mk",
    "content": "# Copyright 2021 Stefan Kerkmann\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nSECURE_ENABLE = yes\n"
  },
  {
    "path": "tests/secure/test_secure.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n#include \"keyboard_report_util.hpp\"\n#include \"test_common.hpp\"\n\nusing testing::_;\nusing testing::AnyNumber;\nusing testing::InSequence;\n\nclass Secure : public TestFixture {\n   public:\n    void SetUp() override {\n        secure_lock();\n    }\n};\n\nTEST_F(Secure, test_lock) {\n    TestDriver driver;\n\n    // Don't allow empty reports.\n    EXPECT_NO_REPORT(driver);\n\n    EXPECT_FALSE(secure_is_unlocked());\n    secure_unlock();\n    EXPECT_TRUE(secure_is_unlocked());\n    run_one_scan_loop();\n    EXPECT_TRUE(secure_is_unlocked());\n    secure_lock();\n    EXPECT_FALSE(secure_is_unlocked());\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Secure, test_unlock_timeout) {\n    TestDriver driver;\n\n    // Don't allow empty reports.\n    EXPECT_NO_REPORT(driver);\n\n    EXPECT_FALSE(secure_is_unlocked());\n    secure_unlock();\n    EXPECT_TRUE(secure_is_unlocked());\n    idle_for(SECURE_IDLE_TIMEOUT + 1);\n    EXPECT_FALSE(secure_is_unlocked());\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Secure, test_unlock_request) {\n    TestDriver driver;\n    auto       key_mo = KeymapKey(0, 0, 0, MO(1));\n    auto       key_a  = KeymapKey(0, 1, 0, KC_A);\n    auto       key_b  = KeymapKey(0, 2, 0, KC_B);\n    auto       key_c  = KeymapKey(0, 3, 0, KC_C);\n    auto       key_d  = KeymapKey(0, 4, 0, KC_D);\n\n    set_keymap({key_mo, key_a, key_b, key_c, key_d});\n\n    // Don't allow empty reports.\n    EXPECT_NO_REPORT(driver);\n\n    EXPECT_TRUE(secure_is_locked());\n    secure_request_unlock();\n    EXPECT_TRUE(secure_is_unlocking());\n    tap_keys(key_a, key_b, key_c, key_d);\n    EXPECT_TRUE(secure_is_unlocked());\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Secure, test_unlock_request_fail) {\n    TestDriver driver;\n    auto       key_e = KeymapKey(0, 0, 0, KC_E);\n    auto       key_a = KeymapKey(0, 1, 0, KC_A);\n    auto       key_b = KeymapKey(0, 2, 0, KC_B);\n    auto       key_c = KeymapKey(0, 3, 0, KC_C);\n    auto       key_d = KeymapKey(0, 4, 0, KC_D);\n\n    set_keymap({key_e, key_a, key_b, key_c, key_d});\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    { // Expect the following reports in this order.\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_A));\n        EXPECT_REPORT(driver, (KC_B));\n        EXPECT_REPORT(driver, (KC_C));\n        EXPECT_REPORT(driver, (KC_D));\n    }\n    EXPECT_TRUE(secure_is_locked());\n    secure_request_unlock();\n    EXPECT_TRUE(secure_is_unlocking());\n    tap_keys(key_e, key_a, key_b, key_c, key_d);\n    EXPECT_FALSE(secure_is_unlocked());\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Secure, test_unlock_request_timeout) {\n    TestDriver driver;\n\n    // Don't allow empty reports.\n    EXPECT_NO_REPORT(driver);\n\n    EXPECT_FALSE(secure_is_unlocked());\n    secure_request_unlock();\n    EXPECT_TRUE(secure_is_unlocking());\n    idle_for(SECURE_UNLOCK_TIMEOUT + 1);\n    EXPECT_FALSE(secure_is_unlocking());\n    EXPECT_FALSE(secure_is_unlocked());\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Secure, test_unlock_request_fail_mid) {\n    TestDriver driver;\n    auto       key_e = KeymapKey(0, 0, 0, KC_E);\n    auto       key_a = KeymapKey(0, 1, 0, KC_A);\n    auto       key_b = KeymapKey(0, 2, 0, KC_B);\n    auto       key_c = KeymapKey(0, 3, 0, KC_C);\n    auto       key_d = KeymapKey(0, 4, 0, KC_D);\n\n    set_keymap({key_e, key_a, key_b, key_c, key_d});\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    { // Expect the following reports in this order.\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_C));\n        EXPECT_REPORT(driver, (KC_D));\n    }\n    EXPECT_FALSE(secure_is_unlocked());\n    secure_request_unlock();\n    EXPECT_TRUE(secure_is_unlocking());\n    tap_keys(key_a, key_b, key_e, key_c, key_d);\n    EXPECT_FALSE(secure_is_unlocking());\n    EXPECT_FALSE(secure_is_unlocked());\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Secure, test_unlock_request_fail_out_of_order) {\n    TestDriver driver;\n    auto       key_e = KeymapKey(0, 0, 0, KC_E);\n    auto       key_a = KeymapKey(0, 1, 0, KC_A);\n    auto       key_b = KeymapKey(0, 2, 0, KC_B);\n    auto       key_c = KeymapKey(0, 3, 0, KC_C);\n    auto       key_d = KeymapKey(0, 4, 0, KC_D);\n\n    set_keymap({key_e, key_a, key_b, key_c, key_d});\n\n    // Allow any number of empty reports.\n    EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n    { // Expect the following reports in this order.\n        InSequence s;\n        EXPECT_REPORT(driver, (KC_B));\n        EXPECT_REPORT(driver, (KC_C));\n    }\n    EXPECT_FALSE(secure_is_unlocked());\n    secure_request_unlock();\n    EXPECT_TRUE(secure_is_unlocking());\n    tap_keys(key_a, key_d, key_b, key_c);\n    EXPECT_TRUE(secure_is_locked());\n    EXPECT_FALSE(secure_is_unlocking());\n    EXPECT_FALSE(secure_is_unlocked());\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Secure, test_unlock_request_on_layer) {\n    TestDriver driver;\n    auto       key_mo = KeymapKey(0, 0, 0, MO(1));\n    auto       key_a  = KeymapKey(0, 1, 0, KC_A);\n    auto       key_b  = KeymapKey(0, 2, 0, KC_B);\n    auto       key_c  = KeymapKey(0, 3, 0, KC_C);\n    auto       key_d  = KeymapKey(0, 4, 0, KC_D);\n\n    set_keymap({key_mo, key_a, key_b, key_c, key_d});\n\n    // Don't allow empty reports.\n    EXPECT_NO_REPORT(driver);\n\n    EXPECT_TRUE(secure_is_locked());\n    key_mo.press();\n    run_one_scan_loop();\n    secure_request_unlock();\n    key_mo.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(secure_is_unlocking());\n    tap_keys(key_a, key_b, key_c, key_d);\n    EXPECT_TRUE(secure_is_unlocked());\n    EXPECT_FALSE(layer_state_is(1));\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Secure, test_unlock_request_mid_stroke) {\n    TestDriver driver;\n    auto       key_e = KeymapKey(0, 0, 0, KC_E);\n    auto       key_a = KeymapKey(0, 1, 0, KC_A);\n    auto       key_b = KeymapKey(0, 2, 0, KC_B);\n    auto       key_c = KeymapKey(0, 3, 0, KC_C);\n    auto       key_d = KeymapKey(0, 4, 0, KC_D);\n\n    set_keymap({key_e, key_a, key_b, key_c, key_d});\n\n    EXPECT_REPORT(driver, (KC_E));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_TRUE(secure_is_locked());\n    key_e.press();\n    run_one_scan_loop();\n    secure_request_unlock();\n    key_e.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(secure_is_unlocking());\n    tap_keys(key_a, key_b, key_c, key_d);\n    EXPECT_TRUE(secure_is_unlocked());\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Secure, test_unlock_request_mods) {\n    TestDriver driver;\n    auto       key_lsft = KeymapKey(0, 0, 0, KC_LSFT);\n    auto       key_a    = KeymapKey(0, 1, 0, KC_A);\n    auto       key_b    = KeymapKey(0, 2, 0, KC_B);\n    auto       key_c    = KeymapKey(0, 3, 0, KC_C);\n    auto       key_d    = KeymapKey(0, 4, 0, KC_D);\n\n    set_keymap({key_lsft, key_a, key_b, key_c, key_d});\n\n    EXPECT_REPORT(driver, (key_lsft.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_TRUE(secure_is_locked());\n    key_lsft.press();\n    run_one_scan_loop();\n    secure_request_unlock();\n    key_lsft.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(secure_is_unlocking());\n    tap_keys(key_a, key_b, key_c, key_d);\n    EXPECT_TRUE(secure_is_unlocked());\n\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_dance/config.h",
    "content": "/* Copyright 2022 Jouke Witteveen\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/tap_dance/examples.c",
    "content": "/* Copyright 2022 Jouke Witteveen\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"quantum.h\"\n#include \"examples.h\"\n#include \"keymap_introspection.h\"\n\n// Example code from the tap dance documentation, adapted for testing\n\n// clang-format off\n\n// Example 1\n\nvoid dance_egg(tap_dance_state_t *state, void *user_data) {\n    if (state->count >= 100) {\n        // SEND_STRING(\"Safety dance!\");\n        tap_code(KC_C);\n        reset_tap_dance(state);\n    }\n}\n\n\n// Example 2\n\nvoid dance_flsh_each(tap_dance_state_t *state, void *user_data) {\n    switch (state->count) {\n        case 1:\n            register_code(KC_3);\n            break;\n        case 2:\n            register_code(KC_2);\n            break;\n        case 3:\n            register_code(KC_1);\n            break;\n        case 4:\n            unregister_code(KC_3);\n            // wait_ms(50);\n            unregister_code(KC_2);\n            // wait_ms(50);\n            unregister_code(KC_1);\n    }\n}\n\nvoid dance_flsh_finished(tap_dance_state_t *state, void *user_data) {\n    if (state->count >= 4) {\n        // reset_keyboard();\n        tap_code(KC_R);\n    }\n}\n\nvoid dance_flsh_reset(tap_dance_state_t *state, void *user_data) {\n    unregister_code(KC_1);\n    // wait_ms(50);\n    unregister_code(KC_2);\n    // wait_ms(50);\n    unregister_code(KC_3);\n}\n\n\n// Example 3\n\ntypedef struct {\n    uint16_t tap;\n    uint16_t hold;\n    uint16_t held;\n} tap_dance_tap_hold_t;\n\nbool process_record_user(uint16_t keycode, keyrecord_t *record) {\n    tap_dance_action_t *action;\n\n    switch (keycode) {\n        case TD(CT_CLN):\n            action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(keycode));\n            if (!record->event.pressed && action->state.count && !action->state.finished) {\n                tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data;\n                tap_code16(tap_hold->tap);\n            }\n    }\n    return true;\n}\n\nvoid tap_dance_tap_hold_finished(tap_dance_state_t *state, void *user_data) {\n    tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data;\n\n    if (state->pressed) {\n        if (state->count == 1\n#ifndef PERMISSIVE_HOLD\n            && !state->interrupted\n#endif\n        ) {\n            register_code16(tap_hold->hold);\n            tap_hold->held = tap_hold->hold;\n        } else {\n            register_code16(tap_hold->tap);\n            tap_hold->held = tap_hold->tap;\n        }\n    }\n}\n\nvoid tap_dance_tap_hold_reset(tap_dance_state_t *state, void *user_data) {\n    tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data;\n\n    if (tap_hold->held) {\n        unregister_code16(tap_hold->held);\n        tap_hold->held = 0;\n    }\n}\n\n#define ACTION_TAP_DANCE_TAP_HOLD(tap, hold) \\\n    { .fn = {NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, .user_data = (void *)&((tap_dance_tap_hold_t){tap, hold, 0}), }\n\n\n// Example 4\n\ntypedef enum {\n    TD_NONE,\n    TD_UNKNOWN,\n    TD_SINGLE_TAP,\n    TD_SINGLE_HOLD,\n    TD_DOUBLE_TAP,\n    TD_DOUBLE_HOLD,\n    TD_DOUBLE_SINGLE_TAP,\n    TD_TRIPLE_TAP,\n    TD_TRIPLE_HOLD\n} td_state_t;\n\ntypedef struct {\n    bool is_press_action;\n    td_state_t state;\n} td_tap_t;\n\ntd_state_t cur_dance(tap_dance_state_t *state) {\n    if (state->count == 1) {\n        if (state->interrupted || !state->pressed) return TD_SINGLE_TAP;\n        else return TD_SINGLE_HOLD;\n    } else if (state->count == 2) {\n        if (state->interrupted) return TD_DOUBLE_SINGLE_TAP;\n        else if (state->pressed) return TD_DOUBLE_HOLD;\n        else return TD_DOUBLE_TAP;\n    }\n\n    if (state->count == 3) {\n        if (state->interrupted || !state->pressed) return TD_TRIPLE_TAP;\n        else return TD_TRIPLE_HOLD;\n    } else return TD_UNKNOWN;\n}\n\nstatic td_tap_t xtap_state = {\n    .is_press_action = true,\n    .state = TD_NONE\n};\n\nvoid x_finished(tap_dance_state_t *state, void *user_data) {\n    xtap_state.state = cur_dance(state);\n    switch (xtap_state.state) {\n        case TD_SINGLE_TAP: register_code(KC_X); break;\n        case TD_SINGLE_HOLD: register_code(KC_LCTL); break;\n        case TD_DOUBLE_TAP: register_code(KC_ESC); break;\n        case TD_DOUBLE_HOLD: register_code(KC_LALT); break;\n        case TD_DOUBLE_SINGLE_TAP: tap_code(KC_X); register_code(KC_X);\n        default: break;  // Not present in documentation\n    }\n}\n\nvoid x_reset(tap_dance_state_t *state, void *user_data) {\n    switch (xtap_state.state) {\n        case TD_SINGLE_TAP: unregister_code(KC_X); break;\n        case TD_SINGLE_HOLD: unregister_code(KC_LCTL); break;\n        case TD_DOUBLE_TAP: unregister_code(KC_ESC); break;\n        case TD_DOUBLE_HOLD: unregister_code(KC_LALT);\n        case TD_DOUBLE_SINGLE_TAP: unregister_code(KC_X);\n        default: break;  // Not present in documentation\n    }\n    xtap_state.state = TD_NONE;\n}\n\nstatic void release_press(tap_dance_state_t *state, void *user_data) {\n    tap_code16(KC_P);\n}\n\nstatic void release_unpress(tap_dance_state_t *state, void *user_data) {\n    tap_code16(KC_U);\n}\n\nstatic void release_unpress_mark_finished(tap_dance_state_t *state, void *user_data) {\n    tap_code16(KC_U);\n    state->finished = true;\n}\n\nstatic void release_finished(tap_dance_state_t *state, void *user_data) {\n    tap_code16(KC_F);\n}\n\nstatic void release_reset(tap_dance_state_t *state, void *user_data) {\n    tap_code16(KC_R);\n}\n\ntap_dance_action_t tap_dance_actions[] = {\n    [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS),\n    [CT_EGG]      = ACTION_TAP_DANCE_FN(dance_egg),\n    [CT_FLSH]     = ACTION_TAP_DANCE_FN_ADVANCED(dance_flsh_each, dance_flsh_finished, dance_flsh_reset),\n    [CT_CLN]      = ACTION_TAP_DANCE_TAP_HOLD(KC_COLN, KC_SCLN),\n    [X_CTL]       = ACTION_TAP_DANCE_FN_ADVANCED(NULL, x_finished, x_reset),\n    [TD_RELEASE]  = ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(release_press, release_unpress, release_finished, release_reset),\n    [TD_RELEASE_AND_FINISH]  = ACTION_TAP_DANCE_FN_ADVANCED_WITH_RELEASE(release_press, release_unpress_mark_finished, release_finished, release_reset),\n};\n\n// clang-format on\n"
  },
  {
    "path": "tests/tap_dance/examples.h",
    "content": "/* Copyright 2022 Jouke Witteveen\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum {\n    TD_ESC_CAPS,\n    CT_EGG,\n    CT_FLSH,\n    CT_CLN,\n    X_CTL,\n    TD_RELEASE,\n    TD_RELEASE_AND_FINISH,\n};\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "tests/tap_dance/tap_dance_layers/config.h",
    "content": "// Copyright 2022 Sergey Vlasov (@sigprof)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/tap_dance/tap_dance_layers/tap_dance_defs.c",
    "content": "// Copyright 2022 Sergey Vlasov (@sigprof)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"quantum.h\"\n#include \"tap_dance_defs.h\"\n\n// Implement custom keycodes which are used to check that the layer switching\n// behaves properly.\nbool process_record_user(uint16_t keycode, keyrecord_t *record) {\n    switch (keycode) {\n        case FAST_AB:\n        case SLOW_AB:\n            if (record->event.pressed) {\n                tap_code(KC_A);\n            } else {\n                tap_code(KC_B);\n            }\n            return keycode == SLOW_AB;\n        case FAST_CD:\n        case SLOW_CD:\n            if (record->event.pressed) {\n                tap_code(KC_C);\n            } else {\n                tap_code(KC_D);\n            }\n            return keycode == SLOW_CD;\n    }\n    return true;\n}\n\n// Implement a custom tap dance with the following behavior:\n// - single tap: KC_APP\n// - single hold: MO(1)\n// - double tap/hold: KC_RCTL\n// (The single tap and hold actions are mostly equivalent to LT(1, KC_APP).)\n\nenum lt_app_state {\n    LTA_NONE,\n    LTA_SINGLE_TAP,\n    LTA_SINGLE_HOLD,\n    LTA_DOUBLE_HOLD,\n};\n\nstatic enum lt_app_state saved_lt_app_state;\n\nstatic enum lt_app_state get_lt_app_state(tap_dance_state_t *state) {\n    if (state->count == 1) {\n        if (!state->pressed) {\n            return LTA_SINGLE_TAP;\n        } else {\n            return LTA_SINGLE_HOLD;\n        }\n    } else if (state->count == 2) {\n        return LTA_DOUBLE_HOLD;\n    } else {\n        return LTA_NONE;\n    }\n}\n\nstatic void lt_app_finished(tap_dance_state_t *state, void *user_data) {\n    saved_lt_app_state = get_lt_app_state(state);\n    switch (saved_lt_app_state) {\n        case LTA_NONE:\n            break;\n        case LTA_SINGLE_TAP:\n            register_code(KC_APP);\n            break;\n        case LTA_SINGLE_HOLD:\n            layer_on(1);\n            break;\n        case LTA_DOUBLE_HOLD:\n            register_code(KC_RCTL);\n            break;\n    }\n}\n\nstatic void lt_app_reset(tap_dance_state_t *state, void *user_data) {\n    switch (saved_lt_app_state) {\n        case LTA_NONE:\n            break;\n        case LTA_SINGLE_TAP:\n            unregister_code(KC_APP);\n            break;\n        case LTA_SINGLE_HOLD:\n            layer_off(1);\n            break;\n        case LTA_DOUBLE_HOLD:\n            unregister_code(KC_RCTL);\n            break;\n    }\n}\n\ntap_dance_action_t tap_dance_actions[] = {\n    [TD_L_MOVE] = ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1),\n    [TD_L_TOGG] = ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1),\n    [TD_LT_APP] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, lt_app_finished, lt_app_reset),\n};\n"
  },
  {
    "path": "tests/tap_dance/tap_dance_layers/tap_dance_defs.h",
    "content": "// Copyright 2022 Sergey Vlasov (@sigprof)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum custom_keycodes {\n    // (FAST|SLOW)_xy = tap KC_x on press, tap KC_y on release.  For FAST_xy\n    // process_record_user() returns false to stop processing early; for\n    // SLOW_xy process_record_user() returns true, therefore all other key\n    // handlers are invoked.\n    FAST_AB = SAFE_RANGE,\n    FAST_CD,\n    SLOW_AB,\n    SLOW_CD,\n};\n\nenum tap_dance_ids {\n    TD_L_MOVE, // ACTION_TAP_DANCE_LAYER_MOVE(KC_APP, 1)\n    TD_L_TOGG, // ACTION_TAP_DANCE_LAYER_TOGGLE(KC_APP, 1)\n    TD_LT_APP, // similar to LT(1, KC_APP) with KC_RCTL on tap+hold or double tap\n};\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "tests/tap_dance/tap_dance_layers/test.mk",
    "content": "# Copyright 2022 Sergey Vlasov (@sigprof)\n# SPDX-License-Identifier: GPL-2.0-or-later\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nTAP_DANCE_ENABLE = yes\n\nINTROSPECTION_KEYMAP_C = tap_dance_defs.c\n"
  },
  {
    "path": "tests/tap_dance/tap_dance_layers/test_tap_dance_layers.cpp",
    "content": "// Copyright 2022 Sergey Vlasov (@sigprof)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_keymap_key.hpp\"\n#include \"tap_dance_defs.h\"\n\nusing testing::_;\nusing testing::InSequence;\n\nstruct TapDanceKeyParams {\n    std::string name;                  // Tap dance name (part of test name)\n    uint16_t    keycode;               // Tap dance keycode (TD(n))\n    uint16_t    expect_on_tap;         // Keycode for single tap\n    uint16_t    expect_on_hold;        // Keycode for single hold (may be MO(1))\n    uint16_t    expect_on_double_tap;  // Keycode for double tap (may be MO(1))\n    uint16_t    expect_on_double_hold; // Keycode for double hold (may be MO(1))\n};\n\nstruct OtherKeyLayerParams {\n    uint16_t keycode;           // Keycode in the keymap\n    uint16_t expect_on_press;   // Keycode to expect on press\n    uint16_t expect_on_release; // Keycode to expect on release (may be KC_NO if none)\n};\n\nstruct OtherKeyParams {\n    std::string         name; // Other key name (part of test name)\n    OtherKeyLayerParams l0;   // Keycodes for layer 0\n    OtherKeyLayerParams l1;   // Keycodes for layer 1\n};\n\ntypedef std::tuple<TapDanceKeyParams, OtherKeyParams> TapDanceLayersParams;\n\nclass TapDanceLayers : public ::testing::WithParamInterface<TapDanceLayersParams>, public TestFixture {\n   protected:\n    TapDanceKeyParams tap_dance;\n    OtherKeyParams    other_key;\n\n    std::unique_ptr<KeymapKey> key_td, key_td_l1, key_other, key_other_l1;\n\n    void SetUp() override {\n        std::tie(tap_dance, other_key) = GetParam();\n\n        key_td       = std::make_unique<KeymapKey>(0, 1, 0, tap_dance.keycode);\n        key_td_l1    = std::make_unique<KeymapKey>(1, 1, 0, KC_TRNS);\n        key_other    = std::make_unique<KeymapKey>(0, 2, 0, other_key.l0.keycode);\n        key_other_l1 = std::make_unique<KeymapKey>(1, 2, 0, other_key.l1.keycode);\n\n        set_keymap({*key_td, *key_td_l1, *key_other, *key_other_l1});\n    }\n};\n\nstatic const TapDanceKeyParams tap_dance_keys[] = {\n    TapDanceKeyParams{\n        \"LayerMove\",\n        TD(TD_L_MOVE),\n        KC_APP,\n        KC_APP,\n        MO(1),\n        MO(1),\n    },\n    TapDanceKeyParams{\n        \"LayerToggle\",\n        TD(TD_L_TOGG),\n        KC_APP,\n        KC_APP,\n        MO(1),\n        MO(1),\n    },\n    TapDanceKeyParams{\n        \"CustomLT\",\n        TD(TD_LT_APP),\n        KC_APP,\n        MO(1),\n        KC_RCTL,\n        KC_RCTL,\n    },\n};\n\nstatic const OtherKeyParams other_keys[] = {\n    OtherKeyParams{\n        \"Builtin\",\n        OtherKeyLayerParams{KC_A, KC_A, KC_NO},\n        OtherKeyLayerParams{KC_B, KC_B, KC_NO},\n    },\n    OtherKeyParams{\n        \"CustomFast\",\n        OtherKeyLayerParams{FAST_AB, KC_A, KC_B},\n        OtherKeyLayerParams{FAST_CD, KC_C, KC_D},\n    },\n    OtherKeyParams{\n        \"CustomSlow\",\n        OtherKeyLayerParams{SLOW_AB, KC_A, KC_B},\n        OtherKeyLayerParams{SLOW_CD, KC_C, KC_D},\n    },\n};\n\n// clang-format off\nINSTANTIATE_TEST_CASE_P(\n    Layers,\n    TapDanceLayers,\n    ::testing::Combine(\n        ::testing::ValuesIn(tap_dance_keys),\n        ::testing::ValuesIn(other_keys)\n    ),\n    [](const ::testing::TestParamInfo<TapDanceLayersParams>& info) {\n        return std::get<0>(info.param).name + std::get<1>(info.param).name;\n    }\n);\n// clang-format on\n\n// Test single tap of the tap dance key with tapping term delay after the tap.\nTEST_P(TapDanceLayers, SingleTap) {\n    TestDriver driver;\n    InSequence s;\n\n    // The tap of the tap dance key does not result in sending a report\n    // immediately.\n    EXPECT_NO_REPORT(driver);\n    tap_key(*key_td);\n\n    // After the tapping term expires, a tap event for the single tap keycode\n    // is generated.\n    idle_for(TAPPING_TERM - 1);\n    EXPECT_REPORT(driver, (tap_dance.expect_on_tap));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    // Pressing the other key produces the reports for the layer 0 mapping of\n    // that key.\n    EXPECT_REPORT(driver, (other_key.l0.expect_on_press));\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the reports for the layer 0 mapping of\n    // that key.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test single tap of the tap dance key without a delay between the tap dance\n// key and the other key.\nTEST_P(TapDanceLayers, SingleTapFast) {\n    TestDriver driver;\n    InSequence s;\n\n    // The tap of the tap dance key does not result in sending a report\n    // immediately.\n    EXPECT_NO_REPORT(driver);\n    tap_key(*key_td);\n\n    // A quick press of the other key causes the tap event for the tap dance to\n    // be sent before the press event for the other key, and the layer 0\n    // mapping is used for the other key.\n    EXPECT_REPORT(driver, (tap_dance.expect_on_tap));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (other_key.l0.expect_on_press));\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the reports for the layer 0 mapping of\n    // that key.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test single hold of the tap dance key with tapping term delay after the hold\n// (test variant for tap dances which switch the layer on hold).\nTEST_P(TapDanceLayers, SingleHoldLayer) {\n    if (tap_dance.expect_on_hold != MO(1)) {\n        // Do nothing - the SingleHoldKeycode test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the hold of the tap dance key.\n    EXPECT_NO_REPORT(driver);\n    key_td->press();\n    run_one_scan_loop();\n\n    // After the tapping term expires, the tap dance finishes and switches the\n    // layer, but does not send a report.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n\n    // Pressing the other key produces the reports for the layer 1 mapping of\n    // that key.\n    EXPECT_REPORT(driver, (other_key.l1.expect_on_press));\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the tap dance key does not produce a report.\n    EXPECT_NO_REPORT(driver);\n    key_td->release();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the report for the layer 1 mapping of\n    // that key.\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l1.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test single hold of the tap dance key with tapping term delay after the hold\n// (test variant for tap dances which send a keycode on single hold).\nTEST_P(TapDanceLayers, SingleHoldKeycode) {\n    if (tap_dance.expect_on_hold == MO(1)) {\n        // Do nothing - the SingleHoldLayer test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the hold of the tap dance key.\n    EXPECT_NO_REPORT(driver);\n    key_td->press();\n    run_one_scan_loop();\n\n    // After the tapping term expires, the tap dance sends the report with the\n    // hold keycode.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (tap_dance.expect_on_hold));\n    run_one_scan_loop();\n\n    // Pressing the other key produces the reports for the layer 0 mapping of\n    // that key.\n    EXPECT_REPORT(driver, (tap_dance.expect_on_hold, other_key.l0.expect_on_press));\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (tap_dance.expect_on_hold));\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the tap dance key sends the release report for the\n    // corresponding hold keycode.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    } else {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_press));\n    }\n    key_td->release();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the reports for the layer 0 mapping of\n    // that key.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test single hold of the tap dance key without tapping term delay after the\n// hold (test variant for tap dances which switch the layer on hold).\nTEST_P(TapDanceLayers, SingleHoldFastLayer) {\n    if (tap_dance.expect_on_hold != MO(1)) {\n        // Do nothing - the SingleHoldFastKeycode test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the hold of the tap dance key.\n    EXPECT_NO_REPORT(driver);\n    key_td->press();\n    run_one_scan_loop();\n\n    // Pressing the other key produces the reports for the layer 1 mapping of\n    // that key.\n    EXPECT_REPORT(driver, (other_key.l1.expect_on_press));\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the tap dance key does not produce a report.\n    EXPECT_NO_REPORT(driver);\n    key_td->release();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the reports for the layer 1 mapping of\n    // that key.\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l1.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test single hold of the tap dance key without tapping term delay after the hold\n// (test variant for tap dances which send a keycode on single hold).\nTEST_P(TapDanceLayers, SingleHoldFastKeycode) {\n    if (tap_dance.expect_on_hold == MO(1)) {\n        // Do nothing - the SingleHoldFastLayer test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the hold of the tap dance key.\n    EXPECT_NO_REPORT(driver);\n    key_td->press();\n    run_one_scan_loop();\n\n    // Pressing the other key produces first the report for the tap dance hold\n    // keycode, and then the reports for the layer 0 mapping of the other key.\n    EXPECT_REPORT(driver, (tap_dance.expect_on_hold));\n    EXPECT_REPORT(driver, (tap_dance.expect_on_hold, other_key.l0.expect_on_press));\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (tap_dance.expect_on_hold));\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the tap dance key sends a release report for the corresponding\n    // hold keycode.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    } else {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_press));\n    }\n    key_td->release();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the report for the layer 0 mapping of\n    // that key.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test double tap of the tap dance key with tapping term delay after the hold\n// (test variant for tap dances which switch the layer on double tap).\nTEST_P(TapDanceLayers, DoubleTapLayer) {\n    if (tap_dance.expect_on_double_tap != MO(1)) {\n        // Do nothing - the DoubleTapKeycode test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the double tap of the tap dance\n    // key.\n    EXPECT_NO_REPORT(driver);\n    tap_key(*key_td);\n    tap_key(*key_td);\n\n    // After the tapping term this tap dance does not send a report too.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n\n    // Pressing the other key produces the reports for the layer 1 mapping of\n    // that key.\n    EXPECT_REPORT(driver, (other_key.l1.expect_on_press));\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the report for the layer 1 mapping of\n    // that key.\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l1.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test double tap of the tap dance key with tapping term delay after the hold\n// (test variant for tap dances which send a keycode on double tap).\nTEST_P(TapDanceLayers, DoubleTapKeycode) {\n    if (tap_dance.expect_on_double_tap == MO(1)) {\n        // Do nothing - the DoubleTapLayer test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the double tap of the tap dance\n    // key.\n    EXPECT_NO_REPORT(driver);\n    tap_key(*key_td);\n    tap_key(*key_td);\n\n    // After the tapping term this tap dance sends the double tap keycode.\n    idle_for(TAPPING_TERM - 1);\n    EXPECT_REPORT(driver, (tap_dance.expect_on_double_tap));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    // Pressing the other key produces the reports for the layer 0 mapping of\n    // that key.\n    EXPECT_REPORT(driver, (other_key.l0.expect_on_press));\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the report for the layer 0 mapping of\n    // that key.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test double tap of the tap dance key without tapping term delay after the\n// hold (test variant for tap dances which switch the layer on double tap).\nTEST_P(TapDanceLayers, DoubleTapFastLayer) {\n    if (tap_dance.expect_on_double_tap != MO(1)) {\n        // Do nothing - the DoubleTapFastKeycode test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the double tap of the tap dance\n    // key.\n    EXPECT_NO_REPORT(driver);\n    tap_key(*key_td);\n    tap_key(*key_td);\n\n    // Pressing the other key produces the reports for the layer 1 mapping of\n    // that key.\n    EXPECT_REPORT(driver, (other_key.l1.expect_on_press));\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the report for the layer 1 mapping of\n    // that key.\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l1.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test double tap of the tap dance key without tapping term delay after the\n// hold (test variant for tap dances which send a keycode on double tap).\nTEST_P(TapDanceLayers, DoubleTapFastKeycode) {\n    if (tap_dance.expect_on_double_tap == MO(1)) {\n        // Do nothing - the DoubleTapFastLayer test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the double tap of the tap dance\n    // key.\n    EXPECT_NO_REPORT(driver);\n    tap_key(*key_td);\n    tap_key(*key_td);\n\n    // Pressing the other key produces first the report for the tap dance\n    // double tap keycode, and then the reports for the layer 0 mapping of the\n    // other key.\n    EXPECT_REPORT(driver, (tap_dance.expect_on_double_tap));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (other_key.l0.expect_on_press));\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the report for the layer 0 mapping of\n    // that key.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test double hold of the tap dance key with tapping term delay after the hold\n// (test variant for tap dances which switch the layer on double hold).\nTEST_P(TapDanceLayers, DoubleHoldLayer) {\n    if (tap_dance.expect_on_double_hold != MO(1)) {\n        // Do nothing - the DoubleHoldKeycode test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the double hold of the tap dance\n    // key.\n    EXPECT_NO_REPORT(driver);\n    tap_key(*key_td);\n    key_td->press();\n    run_one_scan_loop();\n\n    // After the tapping term expires, the tap dance finishes and switches the\n    // layer, but does not send a report.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n\n    // Pressing the other key produces the reports for the layer 1 mapping of\n    // that key.\n    EXPECT_REPORT(driver, (other_key.l1.expect_on_press));\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the tap dance key does not produce a report.\n    EXPECT_NO_REPORT(driver);\n    key_td->release();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the report for the layer 1 mapping of\n    // that key.\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l1.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test double hold of the tap dance key with tapping term delay after the hold\n// (test variant for tap dances which send a keycode on double hold).\nTEST_P(TapDanceLayers, DoubleHoldKeycode) {\n    if (tap_dance.expect_on_double_hold == MO(1)) {\n        // Do nothing - the DoubleHoldLayer test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the double hold of the tap dance\n    // key.\n    EXPECT_NO_REPORT(driver);\n    tap_key(*key_td);\n    key_td->press();\n    run_one_scan_loop();\n\n    // After the tapping term expires, the tap dance sends the report with the\n    // double hold keycode.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold));\n    run_one_scan_loop();\n\n    // Pressing the other key produces the reports for the layer 0 mapping of\n    // that key.\n    EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold, other_key.l0.expect_on_press));\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold));\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the tap dance key sends the release report for the\n    // corresponding double hold keycode.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    } else {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_press));\n    }\n    key_td->release();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the reports for the layer 0 mapping of\n    // that key.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test double hold of the tap dance key without tapping term delay after the\n// hold (test variant for tap dances which switch the layer on double hold).\nTEST_P(TapDanceLayers, DoubleHoldFastLayer) {\n    if (tap_dance.expect_on_double_hold != MO(1)) {\n        // Do nothing - the DoubleHoldFastKeycode test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the double hold of the tap dance\n    // key.\n    EXPECT_NO_REPORT(driver);\n    tap_key(*key_td);\n    key_td->press();\n    run_one_scan_loop();\n\n    // Pressing the other key produces the reports for the layer 1 mapping of\n    // that key.\n    EXPECT_REPORT(driver, (other_key.l1.expect_on_press));\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the tap dance key does not produce a report.\n    EXPECT_NO_REPORT(driver);\n    key_td->release();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the reports for the layer 1 mapping of\n    // that key.\n    if (other_key.l1.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l1.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n\n// Test double hold of the tap dance key without tapping term delay after the hold\n// (test variant for tap dances which send a keycode on double hold).\nTEST_P(TapDanceLayers, DoubleHoldFastKeycode) {\n    if (tap_dance.expect_on_double_hold == MO(1)) {\n        // Do nothing - the DoubleHoldFastLayer test would run instead.\n        return;\n    }\n\n    TestDriver driver;\n    InSequence s;\n\n    // No report gets sent immediately after the double hold of the tap dance\n    // key.\n    EXPECT_NO_REPORT(driver);\n    tap_key(*key_td);\n    key_td->press();\n    run_one_scan_loop();\n\n    // Pressing the other key produces first the report for the tap dance\n    // double hold keycode, and then the reports for the layer 0 mapping of the\n    // other key.\n    EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold));\n    EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold, other_key.l0.expect_on_press));\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (tap_dance.expect_on_double_hold));\n    }\n    key_other->press();\n    run_one_scan_loop();\n\n    // Releasing the tap dance key sends a release report for the corresponding\n    // double hold keycode.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_EMPTY_REPORT(driver);\n    } else {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_press));\n    }\n    key_td->release();\n    run_one_scan_loop();\n\n    // Releasing the other key produces the report for the layer 0 mapping of\n    // that key.\n    if (other_key.l0.expect_on_release != KC_NO) {\n        EXPECT_REPORT(driver, (other_key.l0.expect_on_release));\n    }\n    EXPECT_EMPTY_REPORT(driver);\n    key_other->release();\n    run_one_scan_loop();\n}\n"
  },
  {
    "path": "tests/tap_dance/test.mk",
    "content": "# Copyright 2022 Jouke Witteveen\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nTAP_DANCE_ENABLE = yes\nINTROSPECTION_KEYMAP_C = examples.c\n"
  },
  {
    "path": "tests/tap_dance/test_examples.cpp",
    "content": "/* Copyright 2022 Jouke Witteveen\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_keymap_key.hpp\"\n#include \"examples.h\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass TapDance : public TestFixture {};\n\nTEST_F(TapDance, DoubleTap) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)};\n\n    set_keymap({key_esc_caps});\n\n    /* The tap dance key does nothing on the first press */\n    key_esc_caps.press();\n    run_one_scan_loop();\n    key_esc_caps.release();\n    EXPECT_NO_REPORT(driver);\n\n    /* We get the key press and the release on timeout */\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (KC_ESC));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    /* Double tap gets us the second key */\n    tap_key(key_esc_caps);\n    EXPECT_NO_REPORT(driver);\n    key_esc_caps.press();\n    EXPECT_REPORT(driver, (KC_CAPS));\n    run_one_scan_loop();\n    key_esc_caps.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(TapDance, DoubleTapWithMod) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)};\n    auto       key_shift    = KeymapKey{0, 2, 0, KC_LSFT};\n\n    set_keymap({key_esc_caps, key_shift});\n\n    /* The tap dance key does nothing on the first press */\n    key_shift.press();\n    EXPECT_REPORT(driver, (KC_LSFT));\n    run_one_scan_loop();\n    key_esc_caps.press();\n    run_one_scan_loop();\n\n    key_esc_caps.release();\n    key_shift.release();\n    EXPECT_EMPTY_REPORT(driver);\n\n    /* We get the key press and the release */\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (KC_LSFT));\n    EXPECT_REPORT(driver, (KC_LSFT, KC_ESC));\n    EXPECT_REPORT(driver, (KC_LSFT));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    /* Double tap gets us the second key */\n    key_shift.press();\n    EXPECT_REPORT(driver, (KC_LSFT));\n    run_one_scan_loop();\n    tap_key(key_esc_caps);\n    EXPECT_NO_REPORT(driver);\n    key_shift.release();\n    key_esc_caps.press();\n    EXPECT_REPORT(driver, (KC_LSFT, KC_CAPS));\n    EXPECT_REPORT(driver, (KC_CAPS));\n    run_one_scan_loop();\n    key_esc_caps.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(TapDance, DoubleTapInterrupted) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_esc_caps = KeymapKey{0, 1, 0, TD(TD_ESC_CAPS)};\n    auto       regular_key  = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({key_esc_caps, regular_key});\n\n    /* Interrupted double tap */\n    tap_key(key_esc_caps);\n    regular_key.press();\n    /* Immediate tap of the first key */\n    EXPECT_REPORT(driver, (KC_ESC));\n    EXPECT_EMPTY_REPORT(driver);\n    /* Followed by the interrupting key */\n    EXPECT_REPORT(driver, (KC_A));\n    run_one_scan_loop();\n    regular_key.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    /* Second tap after being interrupted acts as a single tap */\n    key_esc_caps.press();\n    run_one_scan_loop();\n    key_esc_caps.release();\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (KC_ESC));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(TapDance, DanceFn) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_egg = KeymapKey(0, 1, 0, TD(CT_EGG));\n\n    set_keymap({key_egg});\n\n    /* 99 taps do nothing */\n    for (int i = 0; i < 99; i++) {\n        run_one_scan_loop();\n        key_egg.press();\n        run_one_scan_loop();\n        key_egg.release();\n    }\n    idle_for(TAPPING_TERM);\n    EXPECT_NO_REPORT(driver);\n    run_one_scan_loop();\n\n    /* 100 taps trigger the action */\n    for (int i = 0; i < 100; i++) {\n        run_one_scan_loop();\n        key_egg.press();\n        run_one_scan_loop();\n        key_egg.release();\n    }\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    /* 250 taps act the same as 100 taps */\n    /* Taps are counted in an uint8_t, so the count overflows after 255 taps */\n    for (int i = 0; i < 250; i++) {\n        run_one_scan_loop();\n        key_egg.press();\n        run_one_scan_loop();\n        key_egg.release();\n    }\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(TapDance, DanceFnAdvanced) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_flsh = KeymapKey(0, 1, 0, TD(CT_FLSH));\n\n    set_keymap({key_flsh});\n\n    /* Three taps don't trigger a reset */\n    EXPECT_REPORT(driver, (KC_3));\n    EXPECT_REPORT(driver, (KC_3, KC_2));\n    EXPECT_REPORT(driver, (KC_3, KC_2, KC_1));\n    for (int i = 0; i < 3; i++) {\n        run_one_scan_loop();\n        key_flsh.press();\n        run_one_scan_loop();\n        key_flsh.release();\n    }\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (KC_3, KC_2));\n    EXPECT_REPORT(driver, (KC_3));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    /* Four taps trigger a reset */\n    EXPECT_REPORT(driver, (KC_3));\n    EXPECT_REPORT(driver, (KC_3, KC_2));\n    EXPECT_REPORT(driver, (KC_3, KC_2, KC_1));\n    EXPECT_REPORT(driver, (KC_2, KC_1));\n    EXPECT_REPORT(driver, (KC_1));\n    EXPECT_EMPTY_REPORT(driver);\n    for (int i = 0; i < 4; i++) {\n        run_one_scan_loop();\n        key_flsh.press();\n        run_one_scan_loop();\n        key_flsh.release();\n    }\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (KC_R));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(TapDance, TapHold) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_cln = KeymapKey{0, 1, 0, TD(CT_CLN)};\n\n    set_keymap({key_cln});\n\n    /* Short taps fire on release */\n    key_cln.press();\n    run_one_scan_loop();\n    key_cln.release();\n    EXPECT_REPORT(driver, (KC_LSFT));\n    EXPECT_REPORT(driver, (KC_LSFT, KC_SCLN));\n    EXPECT_REPORT(driver, (KC_LSFT));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    /* Holds immediate following a tap apply to the tap key */\n    key_cln.press();\n    EXPECT_REPORT(driver, (KC_LSFT));\n    EXPECT_REPORT(driver, (KC_LSFT, KC_SCLN));\n    idle_for(TAPPING_TERM * 2);\n    key_cln.release();\n    EXPECT_REPORT(driver, (KC_LSFT));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    /* Holds trigger the hold key */\n    key_cln.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    EXPECT_REPORT(driver, (KC_SCLN));\n    run_one_scan_loop();\n    key_cln.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(TapDance, QuadFunction) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_quad    = KeymapKey{0, 1, 0, TD(X_CTL)};\n    auto       regular_key = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({key_quad, regular_key});\n\n    /* Single tap */\n    key_quad.press();\n    run_one_scan_loop();\n    key_quad.release();\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (KC_X));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    /* Single hold */\n    key_quad.press();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (KC_LCTL));\n    run_one_scan_loop();\n    key_quad.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    /* Double tap */\n    tap_key(key_quad);\n    key_quad.press();\n    run_one_scan_loop();\n    key_quad.release();\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (KC_ESC));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    /* Double tap and hold */\n    tap_key(key_quad);\n    key_quad.press();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    EXPECT_REPORT(driver, (KC_LALT));\n    run_one_scan_loop();\n    key_quad.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    /* Double single tap */\n    tap_key(key_quad);\n    tap_key(key_quad);\n    regular_key.press();\n    EXPECT_REPORT(driver, (KC_X));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_X));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_A));\n    run_one_scan_loop();\n    regular_key.release();\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(TapDance, DanceFnAdvancedWithRelease) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_rls = KeymapKey(0, 1, 0, TD(TD_RELEASE));\n\n    set_keymap({key_rls});\n\n    /* Single press and unpress */\n    key_rls.press();\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    key_rls.release();\n    EXPECT_REPORT(driver, (KC_U));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    EXPECT_REPORT(driver, (KC_F));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_R));\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n\n    /* Double press and unpress */\n    key_rls.press();\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    key_rls.release();\n    EXPECT_REPORT(driver, (KC_U));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    key_rls.press();\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    key_rls.release();\n    EXPECT_REPORT(driver, (KC_U));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    EXPECT_REPORT(driver, (KC_F));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_R));\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n\n    /* Unpress after tapping term has elapsed (key is registered as held) */\n    key_rls.press();\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    EXPECT_REPORT(driver, (KC_F));\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n\n    key_rls.release();\n    EXPECT_REPORT(driver, (KC_U));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_R));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n\nTEST_F(TapDance, DanceFnAdvancedWithReleaseAndFinish) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_rls = KeymapKey(0, 1, 0, TD(TD_RELEASE_AND_FINISH));\n\n    set_keymap({key_rls});\n\n    /* Single press and unpress */\n    key_rls.press();\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    key_rls.release();\n    EXPECT_REPORT(driver, (KC_U));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_R));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    // Verify the finished and/or reset functions aren't called\n    // after the tapping term elapses\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n\n    /* Unpress after tapping term has elapsed (key is registered as held) */\n    key_rls.press();\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n\n    EXPECT_REPORT(driver, (KC_F));\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n\n    key_rls.release();\n    EXPECT_REPORT(driver, (KC_U));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_R));\n    EXPECT_EMPTY_REPORT(driver);\n    run_one_scan_loop();\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/default/config.h",
    "content": "/* Copyright 2022 Vladislav Kucheriavykh\n * Copyright 2024 Google LLC\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n#define CHORDAL_HOLD\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/default/test.mk",
    "content": "# Copyright 2022 Vladislav Kucheriavykh\n# Copyright 2024 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nINTROSPECTION_KEYMAP_C = test_keymap.c\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/default/test_keymap.c",
    "content": "// Copyright 2024 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"quantum.h\"\n\nconst char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = {\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'*', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n};\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/default/test_one_shot_keys.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n * Copyright 2024 Google LLC\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"action_util.h\"\n#include \"keyboard_report_util.hpp\"\n#include \"test_common.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass OneShot : public TestFixture {};\nclass OneShotParametrizedTestFixture : public ::testing::WithParamInterface<std::pair<KeymapKey, KeymapKey>>, public OneShot {};\n\nTEST_P(OneShotParametrizedTestFixture, OSMWithAdditionalKeypress) {\n    TestDriver driver;\n    KeymapKey  osm_key     = GetParam().first;\n    KeymapKey  regular_key = GetParam().second;\n\n    set_keymap({osm_key, regular_key});\n\n    // Press and release OSM.\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_P(OneShotParametrizedTestFixture, OSMAsRegularModifierWithAdditionalKeypress) {\n    TestDriver driver;\n    KeymapKey  osm_key     = GetParam().first;\n    KeymapKey  regular_key = GetParam().second;\n\n    set_keymap({osm_key, regular_key});\n\n    // Press OSM.\n    EXPECT_NO_REPORT(driver);\n    osm_key.press();\n    run_one_scan_loop();\n    // Press regular key.\n    regular_key.press();\n    run_one_scan_loop();\n    // Release regular key.\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release OSM.\n    EXPECT_REPORT(driver, (regular_key.report_code, osm_key.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    osm_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\n// clang-format off\n\nINSTANTIATE_TEST_CASE_P(\n    OneShotModifierTests,\n    OneShotParametrizedTestFixture,\n    ::testing::Values(\n        // First is osm key, second is regular key.\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LCTL), KC_LCTL}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LALT), KC_LALT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LGUI), KC_LGUI}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RCTL), KC_RCTL}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RSFT), KC_RSFT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RALT), KC_RALT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RGUI), KC_RGUI}, KeymapKey{0, 1, 1, KC_A})\n        ));\n// clang-format on\n\nTEST_F(OneShot, OSLWithAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key      = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  osl_key1     = KeymapKey{1, 0, 0, KC_X};\n    KeymapKey  regular_key0 = KeymapKey{0, 1, 0, KC_Y};\n    KeymapKey  regular_key1 = KeymapKey{1, 1, 0, KC_A};\n\n    set_keymap({osl_key, osl_key1, regular_key0, regular_key1});\n\n    // Press OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (regular_key1.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key1.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_NO_REPORT(driver);\n    regular_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSLWithOsmAndAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key     = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  osm_key     = KeymapKey{1, 1, 0, OSM(MOD_LSFT), KC_LSFT};\n    KeymapKey  regular_key = KeymapKey{1, 1, 1, KC_A};\n    KeymapKey  blank_key   = KeymapKey{1, 0, 0, KC_NO};\n\n    set_keymap({osl_key, osm_key, regular_key, blank_key});\n\n    // Press OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    // Press and release OSM.\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key);\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/default/test_tap_hold.cpp",
    "content": "// Copyright 2024 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass ChordalHoldDefault : public TestFixture {};\n\nTEST_F(ChordalHoldDefault, chord_nested_press_settled_as_tap) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    // Regular key on the right hand.\n    auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    // Tap regular key.\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap key.\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldDefault, chord_rolled_press_settled_as_tap) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    // Regular key on the right hand.\n    auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap key and regular key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap key.\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldDefault, non_chord_with_mod_tap_settled_as_tap) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key and regular key both on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto regular_key = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_REPORT(driver, (KC_P));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap-hold key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldDefault, tap_mod_tap_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM - 1);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldDefault, hold_mod_tap_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldDefault, two_mod_taps_same_hand_hold_til_timeout) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, MATRIX_COLS - 2, 0, RCTL_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Continue holding til the tapping term.\n    EXPECT_REPORT(driver, (KC_RIGHT_CTRL));\n    EXPECT_REPORT(driver, (KC_RIGHT_CTRL, KC_RIGHT_SHIFT));\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_RIGHT_SHIFT));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldDefault, three_mod_taps_same_hand_streak_roll) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 1, 2, 3.\n    //\n    // NOTE: The correct order of events should be\n    // EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    // EXPECT_REPORT(driver, (KC_B, KC_C));\n    // EXPECT_REPORT(driver, (KC_C));\n    // EXPECT_EMPTY_REPORT(driver);\n    //\n    // However, due to a workaround for https://github.com/tmk/tmk_keyboard/issues/60,\n    // the events are processed out of order, with the first two keys released\n    // before pressing KC_C.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldDefault, tap_regular_key_while_layer_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto       regular_key        = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n    auto       layer_key          = KeymapKey(1, MATRIX_COLS - 1, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key, layer_key});\n\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press(); // Press layer-tap-hold key.\n    run_one_scan_loop();\n    regular_key.press(); // Press regular key.\n    run_one_scan_loop();\n    regular_key.release(); // Release regular key.\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    layer_tap_hold_key.release(); // Release layer-tap-hold key.\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/hold_on_other_key_press/config.h",
    "content": "/* Copyright 2022 Vladislav Kucheriavykh\n * Copyright 2024 Google LLC\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n#define CHORDAL_HOLD\n#define HOLD_ON_OTHER_KEY_PRESS\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/hold_on_other_key_press/test.mk",
    "content": "# Copyright 2022 Vladislav Kucheriavykh\n# Copyright 2024 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nINTROSPECTION_KEYMAP_C = test_keymap.c\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/hold_on_other_key_press/test_keymap.c",
    "content": "// Copyright 2024 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"quantum.h\"\n\nconst char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = {\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'*', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n};\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/hold_on_other_key_press/test_tap_hold.cpp",
    "content": "// Copyright 2024-2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass ChordalHoldHoldOnOtherKeyPress : public TestFixture {};\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, chord_with_mod_tap_settled_as_hold) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    // Regular key on the right hand.\n    auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap-hold key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, chord_nested_press_settled_as_hold) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    // Regular key on the right hand.\n    auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_A));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap-hold key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, chord_rolled_press_settled_as_hold) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    // Regular key on the right hand.\n    auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap key.\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, non_chord_with_mod_tap_settled_as_tap) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key and regular key both on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto regular_key = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_REPORT(driver, (KC_P));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap-hold key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, tap_mod_tap_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM - 1);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, hold_mod_tap_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, two_mod_taps_same_hand_hold_til_timeout) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, MATRIX_COLS - 2, 0, RCTL_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Continue holding til the tapping term.\n    EXPECT_REPORT(driver, (KC_RIGHT_CTRL));\n    EXPECT_REPORT(driver, (KC_RIGHT_CTRL, KC_RIGHT_SHIFT));\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_RIGHT_SHIFT));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, two_mod_taps_nested_press_opposite_hands) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Press first mod-tap key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press second mod-tap key.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release second mod-tap key.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release first mod-tap key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, two_mod_taps_nested_press_same_hand) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, RSFT_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, three_mod_taps_same_hand_streak_roll) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 1, 2, 3.\n    //\n    // NOTE: The correct order of events should be\n    // EXPECT_REPORT(driver, (KC_A));\n    // EXPECT_REPORT(driver, (KC_A, KC_B));\n    // EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    // EXPECT_REPORT(driver, (KC_B, KC_C));\n    // EXPECT_REPORT(driver, (KC_C));\n    // EXPECT_EMPTY_REPORT(driver);\n    //\n    // However, due to a workaround for https://github.com/tmk/tmk_keyboard/issues/60,\n    // the events are processed out of order, with the first two keys released\n    // before pressing KC_C.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, three_mod_taps_same_hand_streak_orders) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    // Press mod-tap keys.\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    // Release keys 3, 2, 1.\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    // Press mod-tap keys.\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    // Release keys 3, 1, 2.\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // NOTE: The correct order of events should be\n    // EXPECT_REPORT(driver, (KC_A));\n    // EXPECT_REPORT(driver, (KC_A, KC_B));\n    // EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    // EXPECT_REPORT(driver, (KC_A, KC_C));\n    // EXPECT_REPORT(driver, (KC_A));\n    // EXPECT_EMPTY_REPORT(driver);\n    //\n    // However, due to a workaround for https://github.com/tmk/tmk_keyboard/issues/60,\n    // the events are processed out of order, with the first two keys released\n    // before pressing KC_C.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_C));\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    // Press mod-tap keys.\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    // Release keys 2, 3, 1.\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, three_mod_taps_two_left_one_right) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 3.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 2, then key 1.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 3.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 1, then key 2.\n    EXPECT_REPORT(driver, (KC_LEFT_CTRL));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, three_mod_taps_one_held_two_tapped) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 3, 2, 1.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 3, 1, 2.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, tap_regular_key_while_layer_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto       regular_key        = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n    auto       no_key             = KeymapKey(1, 1, 0, XXXXXXX);\n    auto       layer_key          = KeymapKey(1, MATRIX_COLS - 1, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key, no_key, layer_key});\n\n    // Press layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_B));\n    regular_key.press();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 0);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, long_distinct_taps_of_layer_tap_key_and_regular_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto       regular_key        = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n    auto       layer_key          = KeymapKey(0, MATRIX_COLS - 1, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key});\n\n    // Press layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Idle for tapping term of layer tap hold key.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    EXPECT_EQ(layer_state, 2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 0);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, nested_tap_of_layer_0_layer_tap_keys) {\n    TestDriver driver;\n    InSequence s;\n    // The keys are layer-taps on layer 0 but regular keys on layer 1.\n    auto first_layer_tap_key  = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto second_layer_tap_key = KeymapKey(0, MATRIX_COLS - 1, 0, LT(1, KC_P));\n    auto first_key_on_layer   = KeymapKey(1, 1, 0, KC_B);\n    auto second_key_on_layer  = KeymapKey(1, MATRIX_COLS - 1, 0, KC_Q);\n\n    set_keymap({first_layer_tap_key, second_layer_tap_key, first_key_on_layer, second_key_on_layer});\n\n    // Press first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_layer_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press second layer-tap key.\n    EXPECT_REPORT(driver, (KC_Q));\n    second_layer_tap_key.press();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release second layer-tap key.\n    EXPECT_EMPTY_REPORT(driver);\n    second_layer_tap_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_layer_tap_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 0);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, lt_mt_one_regular_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       lt_key      = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto       mt_key0     = KeymapKey(0, 2, 0, SFT_T(KC_B));\n    auto       mt_key1     = KeymapKey(1, 2, 0, CTL_T(KC_C));\n    auto       regular_key = KeymapKey(1, MATRIX_COLS - 1, 0, KC_X);\n    auto       no_key0     = KeymapKey(0, MATRIX_COLS - 1, 0, XXXXXXX);\n    auto       no_key1     = KeymapKey(1, 1, 0, XXXXXXX);\n\n    set_keymap({lt_key, mt_key0, mt_key1, regular_key, no_key0, no_key1});\n\n    // Press LT, MT.\n    EXPECT_NO_REPORT(driver);\n    lt_key.press();\n    run_one_scan_loop();\n    mt_key1.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_LCTL));\n    EXPECT_REPORT(driver, (KC_LCTL, KC_X));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release the regular key.\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release MT key.\n    EXPECT_EMPTY_REPORT(driver);\n    mt_key1.release();\n    run_one_scan_loop();\n    EXPECT_EQ(get_mods(), 0);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release LT key.\n    lt_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 0);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, nested_tap_of_layer_tap_keys) {\n    TestDriver driver;\n    InSequence s;\n    // The keys are layer-taps on all layers.\n    auto first_key_layer_0  = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto second_key_layer_0 = KeymapKey(0, MATRIX_COLS - 1, 0, LT(1, KC_P));\n    auto first_key_layer_1  = KeymapKey(1, 1, 0, LT(2, KC_B));\n    auto second_key_layer_1 = KeymapKey(1, MATRIX_COLS - 1, 0, LT(2, KC_Q));\n    auto first_key_layer_2  = KeymapKey(2, 1, 0, KC_TRNS);\n    auto second_key_layer_2 = KeymapKey(2, MATRIX_COLS - 1, 0, KC_TRNS);\n\n    set_keymap({first_key_layer_0, second_key_layer_0, first_key_layer_1, second_key_layer_1, first_key_layer_2, second_key_layer_2});\n\n    // Press first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_key_layer_0.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press second layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    second_key_layer_0.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release second layer-tap key.\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    second_key_layer_0.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_key_layer_0.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, roll_layer_tap_key_with_regular_key) {\n    TestDriver driver;\n    InSequence s;\n\n    auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto regular_key        = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n    auto layer_key          = KeymapKey(1, MATRIX_COLS - 1, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key, layer_key});\n\n    // Press layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_B));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldHoldOnOtherKeyPress, two_mod_tap_keys_stuttered_press) {\n    TestDriver driver;\n    InSequence s;\n\n    auto mod_tap_key1 = KeymapKey(0, 1, 0, LSFT_T(KC_A));\n    auto mod_tap_key2 = KeymapKey(0, 2, 0, LCTL_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Hold first mod-tap key until the tapping term.\n    EXPECT_REPORT(driver, (KC_LSFT));\n    mod_tap_key1.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press the second mod-tap key, then quickly release and press the first.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_REPORT(driver, (KC_B, KC_A));\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    EXPECT_EQ(get_mods(), 0); // Verify that Shift was released.\n    VERIFY_AND_CLEAR(driver);\n\n    // Release both keys.\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/permissive_hold/config.h",
    "content": "/* Copyright 2022 Vladislav Kucheriavykh\n * Copyright 2024 Google LLC\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n#define CHORDAL_HOLD\n#define PERMISSIVE_HOLD\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/permissive_hold/test.mk",
    "content": "# Copyright 2022 Vladislav Kucheriavykh\n# Copyright 2024 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nINTROSPECTION_KEYMAP_C = test_keymap.c\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/permissive_hold/test_keymap.c",
    "content": "// Copyright 2024 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"quantum.h\"\n\nconst char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = {\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'*', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n};\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/permissive_hold/test_one_shot_keys.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n * Copyright 2024 Google LLC\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"action_util.h\"\n#include \"keyboard_report_util.hpp\"\n#include \"test_common.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass OneShot : public TestFixture {};\nclass OneShotParametrizedTestFixture : public ::testing::WithParamInterface<std::pair<KeymapKey, KeymapKey>>, public OneShot {};\n\nTEST_P(OneShotParametrizedTestFixture, OSMWithAdditionalKeypress) {\n    TestDriver driver;\n    KeymapKey  osm_key     = GetParam().first;\n    KeymapKey  regular_key = GetParam().second;\n\n    set_keymap({osm_key, regular_key});\n\n    // Press and release OSM.\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_P(OneShotParametrizedTestFixture, OSMAsRegularModifierWithAdditionalKeypress) {\n    TestDriver driver;\n    KeymapKey  osm_key     = GetParam().first;\n    KeymapKey  regular_key = GetParam().second;\n\n    set_keymap({osm_key, regular_key});\n\n    // Press OSM.\n    EXPECT_NO_REPORT(driver);\n    osm_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_REPORT(driver, (osm_key.report_code)).Times(2);\n    EXPECT_REPORT(driver, (regular_key.report_code, osm_key.report_code));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release OSM.\n    EXPECT_EMPTY_REPORT(driver);\n    osm_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\n// clang-format off\n\nINSTANTIATE_TEST_CASE_P(\n    OneShotModifierTests,\n    OneShotParametrizedTestFixture,\n    ::testing::Values(\n        // First is osm key, second is regular key.\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LCTL), KC_LCTL}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LALT), KC_LALT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LGUI), KC_LGUI}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RCTL), KC_RCTL}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RSFT), KC_RSFT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RALT), KC_RALT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RGUI), KC_RGUI}, KeymapKey{0, 1, 1, KC_A})\n        ));\n// clang-format on\n\nTEST_F(OneShot, OSLWithAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key      = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  osl_key1     = KeymapKey{1, 0, 0, KC_X};\n    KeymapKey  regular_key0 = KeymapKey{0, 1, 0, KC_Y};\n    KeymapKey  regular_key1 = KeymapKey{1, 1, 0, KC_A};\n\n    set_keymap({osl_key, osl_key1, regular_key0, regular_key1});\n\n    // Press OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (regular_key1.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key1.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_NO_REPORT(driver);\n    regular_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSLWithOsmAndAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key     = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  osm_key     = KeymapKey{1, 1, 0, OSM(MOD_LSFT), KC_LSFT};\n    KeymapKey  regular_key = KeymapKey{1, 1, 1, KC_A};\n    KeymapKey  blank_key   = KeymapKey{1, 0, 0, KC_NO};\n\n    set_keymap({osl_key, osm_key, regular_key, blank_key});\n\n    // Press OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    // Press and release OSM.\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key);\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/permissive_hold/test_tap_hold.cpp",
    "content": "// Copyright 2024-2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass ChordalHoldPermissiveHold : public TestFixture {};\n\nTEST_F(ChordalHoldPermissiveHold, chordal_hold_handedness) {\n    EXPECT_EQ(chordal_hold_handedness({.col = 0, .row = 0}), 'L');\n    EXPECT_EQ(chordal_hold_handedness({.col = MATRIX_COLS - 1, .row = 0}), 'R');\n    EXPECT_EQ(chordal_hold_handedness({.col = 0, .row = 2}), '*');\n}\n\nTEST_F(ChordalHoldPermissiveHold, get_chordal_hold_default) {\n    auto make_record = [](uint8_t row, uint8_t col, keyevent_type_t type = KEY_EVENT) {\n        return keyrecord_t{\n            .event =\n                {\n                    .key     = {.col = col, .row = row},\n                    .type    = type,\n                    .pressed = true,\n                },\n        };\n    };\n    // Create two records on the left hand.\n    keyrecord_t record_l0 = make_record(0, 0);\n    keyrecord_t record_l1 = make_record(1, 0);\n    // Create a record on the right hand.\n    keyrecord_t record_r = make_record(0, MATRIX_COLS - 1);\n\n    // Function should return true when records are on opposite hands.\n    EXPECT_TRUE(get_chordal_hold_default(&record_l0, &record_r));\n    EXPECT_TRUE(get_chordal_hold_default(&record_r, &record_l0));\n    // ... and false when on the same hand.\n    EXPECT_FALSE(get_chordal_hold_default(&record_l0, &record_l1));\n    EXPECT_FALSE(get_chordal_hold_default(&record_l1, &record_l0));\n    // But (2, 0) has handedness '*', for which true is returned for chords\n    // with either hand.\n    keyrecord_t record_l2 = make_record(2, 0);\n    EXPECT_TRUE(get_chordal_hold_default(&record_l2, &record_l0));\n    EXPECT_TRUE(get_chordal_hold_default(&record_l2, &record_r));\n\n    // Create a record resulting from a combo.\n    keyrecord_t record_combo = make_record(0, 0, COMBO_EVENT);\n    // Function returns true in all cases.\n    EXPECT_TRUE(get_chordal_hold_default(&record_l0, &record_combo));\n    EXPECT_TRUE(get_chordal_hold_default(&record_r, &record_combo));\n    EXPECT_TRUE(get_chordal_hold_default(&record_combo, &record_l0));\n    EXPECT_TRUE(get_chordal_hold_default(&record_combo, &record_r));\n}\n\nTEST_F(ChordalHoldPermissiveHold, chord_nested_press_settled_as_hold) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    // Regular key on the right hand.\n    auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_A));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, chord_rolled_press_settled_as_tap) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    // Regular key on the right hand.\n    auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap key and regular key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap key.\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, non_chord_with_mod_tap_settled_as_tap) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key and regular key both on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto regular_key = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_REPORT(driver, (KC_P));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap-hold key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, tap_mod_tap_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM - 1);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, hold_mod_tap_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, two_mod_taps_same_hand_hold_til_timeout) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, MATRIX_COLS - 2, 0, RCTL_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Continue holding til the tapping term.\n    EXPECT_REPORT(driver, (KC_RIGHT_CTRL));\n    EXPECT_REPORT(driver, (KC_RIGHT_CTRL, KC_RIGHT_SHIFT));\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_RIGHT_SHIFT));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, two_mod_taps_nested_press_opposite_hands) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, two_mod_taps_nested_press_same_hand) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, RSFT_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, three_mod_taps_same_hand_streak_roll) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 1, 2, 3.\n    //\n    // NOTE: The correct order of events should be\n    // EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    // EXPECT_REPORT(driver, (KC_B, KC_C));\n    // EXPECT_REPORT(driver, (KC_C));\n    // EXPECT_EMPTY_REPORT(driver);\n    //\n    // However, due to a workaround for https://github.com/tmk/tmk_keyboard/issues/60,\n    // the events are processed out of order, with the first two keys released\n    // before pressing KC_C.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, three_mod_taps_same_hand_streak_orders) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 3, 2, 1.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 3, 1, 2.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 2, 3, 1.\n    //\n    // NOTE: The correct order of events should be\n    // EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    // EXPECT_REPORT(driver, (KC_A, KC_C));\n    // EXPECT_REPORT(driver, (KC_A));\n    // EXPECT_EMPTY_REPORT(driver);\n    //\n    // However, due to a workaround for https://github.com/tmk/tmk_keyboard/issues/60,\n    // the events are processed out of order.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_C));\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, three_mod_taps_opposite_hands_roll) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 1, 2, 3.\n    //\n    // NOTE: The correct order of events should be\n    // EXPECT_REPORT(driver, (KC_A, KC_B));\n    // EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    // EXPECT_REPORT(driver, (KC_B, KC_C));\n    // EXPECT_REPORT(driver, (KC_C));\n    // EXPECT_EMPTY_REPORT(driver);\n    //\n    // However, due to a workaround for https://github.com/tmk/tmk_keyboard/issues/60,\n    // the events are processed out of order, with the first two keys released\n    // before pressing KC_C.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, three_mod_taps_two_left_one_right) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 3.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 2, then key 1.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 3.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 1, then key 2.\n    EXPECT_REPORT(driver, (KC_LEFT_CTRL));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, three_mod_taps_one_held_two_tapped) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 3, 2, 1.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 3, 1, 2.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, two_mod_taps_one_regular_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 2, 0, CTL_T(KC_B));\n    auto       regular_key  = KeymapKey(0, MATRIX_COLS - 1, 0, KC_C);\n\n    set_keymap({mod_tap_key1, mod_tap_key2, regular_key});\n\n    // Press keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_C));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, tap_regular_key_while_layer_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto       regular_key        = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n    auto       no_key             = KeymapKey(1, 1, 0, XXXXXXX);\n    auto       layer_key          = KeymapKey(1, MATRIX_COLS - 1, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key, no_key, layer_key});\n\n    // Press layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    // Press regular key.\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 0);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, nested_tap_of_layer_0_layer_tap_keys) {\n    TestDriver driver;\n    InSequence s;\n    // The keys are layer-taps on layer 2 but regular keys on layer 1.\n    auto first_layer_tap_key  = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto second_layer_tap_key = KeymapKey(0, MATRIX_COLS - 1, 0, LT(1, KC_P));\n    auto first_key_on_layer   = KeymapKey(1, 1, 0, KC_B);\n    auto second_key_on_layer  = KeymapKey(1, MATRIX_COLS - 1, 0, KC_Q);\n\n    set_keymap({first_layer_tap_key, second_layer_tap_key, first_key_on_layer, second_key_on_layer});\n\n    // Press first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_layer_tap_key.press();\n    run_one_scan_loop();\n    // Press second layer-tap key.\n    second_layer_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release second layer-tap key.\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    second_layer_tap_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_layer_tap_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 0);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, lt_mt_one_regular_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       lt_key      = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto       mt_key0     = KeymapKey(0, 2, 0, SFT_T(KC_B));\n    auto       mt_key1     = KeymapKey(1, 2, 0, CTL_T(KC_C));\n    auto       regular_key = KeymapKey(1, MATRIX_COLS - 1, 0, KC_X);\n    auto       no_key0     = KeymapKey(0, MATRIX_COLS - 1, 0, XXXXXXX);\n    auto       no_key1     = KeymapKey(1, 1, 0, XXXXXXX);\n\n    set_keymap({lt_key, mt_key0, mt_key1, regular_key, no_key0, no_key1});\n\n    // Press LT, MT, and regular key.\n    EXPECT_NO_REPORT(driver);\n    lt_key.press();\n    run_one_scan_loop();\n    mt_key1.press();\n    run_one_scan_loop();\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release the regular key.\n    EXPECT_REPORT(driver, (KC_LCTL));\n    EXPECT_REPORT(driver, (KC_LCTL, KC_X));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(get_mods(), MOD_BIT_LCTRL);\n    EXPECT_EQ(layer_state, 2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release MT key.\n    EXPECT_EMPTY_REPORT(driver);\n    mt_key1.release();\n    run_one_scan_loop();\n    EXPECT_EQ(get_mods(), 0);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release LT key.\n    EXPECT_NO_REPORT(driver);\n    lt_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 0);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, nested_tap_of_layer_tap_keys) {\n    TestDriver driver;\n    InSequence s;\n    // The keys are layer-taps on all layers.\n    auto first_key_layer_0  = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto second_key_layer_0 = KeymapKey(0, MATRIX_COLS - 1, 0, LT(1, KC_P));\n    auto first_key_layer_1  = KeymapKey(1, 1, 0, LT(2, KC_B));\n    auto second_key_layer_1 = KeymapKey(1, MATRIX_COLS - 1, 0, LT(2, KC_Q));\n    auto first_key_layer_2  = KeymapKey(2, 1, 0, KC_TRNS);\n    auto second_key_layer_2 = KeymapKey(2, MATRIX_COLS - 1, 0, KC_TRNS);\n\n    set_keymap({first_key_layer_0, second_key_layer_0, first_key_layer_1, second_key_layer_1, first_key_layer_2, second_key_layer_2});\n\n    // Press first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_key_layer_0.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press second layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    second_key_layer_0.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release second layer-tap key.\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    second_key_layer_0.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_key_layer_0.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, roll_layer_tap_key_with_regular_key) {\n    TestDriver driver;\n    InSequence s;\n\n    auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto regular_key        = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n    auto layer_key          = KeymapKey(1, MATRIX_COLS - 1, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key, layer_key});\n\n    // Press layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release layer-tap-hold key.\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_A));\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHold, two_mod_tap_keys_stuttered_press) {\n    TestDriver driver;\n    InSequence s;\n\n    auto mod_tap_key1 = KeymapKey(0, 1, 0, LSFT_T(KC_A));\n    auto mod_tap_key2 = KeymapKey(0, 2, 0, LCTL_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Hold first mod-tap key until the tapping term.\n    EXPECT_REPORT(driver, (KC_LSFT));\n    mod_tap_key1.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press the second mod-tap key, then quickly release and press the first.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_REPORT(driver, (KC_B, KC_A));\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    EXPECT_EQ(get_mods(), 0); // Verify that Shift was released.\n    VERIFY_AND_CLEAR(driver);\n\n    // Release both keys.\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/config.h",
    "content": "/* Copyright 2022 Vladislav Kucheriavykh\n * Copyright 2024-2025 Google LLC\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n#define CHORDAL_HOLD\n#define PERMISSIVE_HOLD\n#define FLOW_TAP_TERM 150\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test.mk",
    "content": "# Copyright 2022 Vladislav Kucheriavykh\n# Copyright 2024 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nINTROSPECTION_KEYMAP_C = test_keymap.c\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_keymap.c",
    "content": "// Copyright 2024 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"quantum.h\"\n\nconst char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = {\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'*', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n};\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_one_shot_keys.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n * Copyright 2024 Google LLC\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"action_util.h\"\n#include \"keyboard_report_util.hpp\"\n#include \"test_common.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass OneShot : public TestFixture {};\nclass OneShotParametrizedTestFixture : public ::testing::WithParamInterface<std::pair<KeymapKey, KeymapKey>>, public OneShot {};\n\nTEST_P(OneShotParametrizedTestFixture, OSMWithAdditionalKeypress) {\n    TestDriver driver;\n    KeymapKey  osm_key     = GetParam().first;\n    KeymapKey  regular_key = GetParam().second;\n\n    set_keymap({osm_key, regular_key});\n\n    // Press and release OSM.\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_P(OneShotParametrizedTestFixture, OSMAsRegularModifierWithAdditionalKeypress) {\n    TestDriver driver;\n    KeymapKey  osm_key     = GetParam().first;\n    KeymapKey  regular_key = GetParam().second;\n\n    set_keymap({osm_key, regular_key});\n\n    // Press OSM.\n    EXPECT_NO_REPORT(driver);\n    osm_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_REPORT(driver, (osm_key.report_code)).Times(2);\n    EXPECT_REPORT(driver, (regular_key.report_code, osm_key.report_code));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release OSM.\n    EXPECT_EMPTY_REPORT(driver);\n    osm_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\n// clang-format off\n\nINSTANTIATE_TEST_CASE_P(\n    OneShotModifierTests,\n    OneShotParametrizedTestFixture,\n    ::testing::Values(\n        // First is osm key, second is regular key.\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LCTL), KC_LCTL}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LALT), KC_LALT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LGUI), KC_LGUI}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RCTL), KC_RCTL}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RSFT), KC_RSFT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RALT), KC_RALT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RGUI), KC_RGUI}, KeymapKey{0, 1, 1, KC_A})\n        ));\n// clang-format on\n\nTEST_F(OneShot, OSLWithAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key      = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  osl_key1     = KeymapKey{1, 0, 0, KC_X};\n    KeymapKey  regular_key0 = KeymapKey{0, 1, 0, KC_Y};\n    KeymapKey  regular_key1 = KeymapKey{1, 1, 0, KC_A};\n\n    set_keymap({osl_key, osl_key1, regular_key0, regular_key1});\n\n    // Press OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (regular_key1.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key1.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_NO_REPORT(driver);\n    regular_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(OneShot, OSLWithOsmAndAdditionalKeypress) {\n    TestDriver driver;\n    InSequence s;\n    KeymapKey  osl_key     = KeymapKey{0, 0, 0, OSL(1)};\n    KeymapKey  osm_key     = KeymapKey{1, 1, 0, OSM(MOD_LSFT), KC_LSFT};\n    KeymapKey  regular_key = KeymapKey{1, 1, 1, KC_A};\n    KeymapKey  blank_key   = KeymapKey{1, 0, 0, KC_NO};\n\n    set_keymap({osl_key, osm_key, regular_key, blank_key});\n\n    // Press OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release OSL key.\n    EXPECT_NO_REPORT(driver);\n    osl_key.release();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    // Press and release OSM.\n    EXPECT_NO_REPORT(driver);\n    tap_key(osm_key);\n    EXPECT_TRUE(layer_state_is(1));\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/permissive_hold_flow_tap/test_tap_hold.cpp",
    "content": "// Copyright 2024-2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass ChordalHoldPermissiveHoldFlowTap : public TestFixture {};\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, chordal_hold_handedness) {\n    EXPECT_EQ(chordal_hold_handedness({.col = 0, .row = 0}), 'L');\n    EXPECT_EQ(chordal_hold_handedness({.col = MATRIX_COLS - 1, .row = 0}), 'R');\n    EXPECT_EQ(chordal_hold_handedness({.col = 0, .row = 2}), '*');\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, get_chordal_hold_default) {\n    auto make_record = [](uint8_t row, uint8_t col, keyevent_type_t type = KEY_EVENT) {\n        return keyrecord_t{\n            .event =\n                {\n                    .key     = {.col = col, .row = row},\n                    .type    = type,\n                    .pressed = true,\n                },\n        };\n    };\n    // Create two records on the left hand.\n    keyrecord_t record_l0 = make_record(0, 0);\n    keyrecord_t record_l1 = make_record(1, 0);\n    // Create a record on the right hand.\n    keyrecord_t record_r = make_record(0, MATRIX_COLS - 1);\n\n    // Function should return true when records are on opposite hands.\n    EXPECT_TRUE(get_chordal_hold_default(&record_l0, &record_r));\n    EXPECT_TRUE(get_chordal_hold_default(&record_r, &record_l0));\n    // ... and false when on the same hand.\n    EXPECT_FALSE(get_chordal_hold_default(&record_l0, &record_l1));\n    EXPECT_FALSE(get_chordal_hold_default(&record_l1, &record_l0));\n    // But (2, 0) has handedness '*', for which true is returned for chords\n    // with either hand.\n    keyrecord_t record_l2 = make_record(2, 0);\n    EXPECT_TRUE(get_chordal_hold_default(&record_l2, &record_l0));\n    EXPECT_TRUE(get_chordal_hold_default(&record_l2, &record_r));\n\n    // Create a record resulting from a combo.\n    keyrecord_t record_combo = make_record(0, 0, COMBO_EVENT);\n    // Function returns true in all cases.\n    EXPECT_TRUE(get_chordal_hold_default(&record_l0, &record_combo));\n    EXPECT_TRUE(get_chordal_hold_default(&record_r, &record_combo));\n    EXPECT_TRUE(get_chordal_hold_default(&record_combo, &record_l0));\n    EXPECT_TRUE(get_chordal_hold_default(&record_combo, &record_r));\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, chord_nested_press_settled_as_hold) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    // Regular key on the right hand.\n    auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_A));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, chord_rolled_press_settled_as_tap) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    // Regular key on the right hand.\n    auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap key and regular key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap key.\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, non_chord_with_mod_tap_settled_as_tap) {\n    TestDriver driver;\n    InSequence s;\n    // Mod-tap key and regular key both on the left hand.\n    auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto regular_key = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_key, regular_key});\n\n    // Press mod-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_REPORT(driver, (KC_P));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap-hold key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, tap_mod_tap_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM - 1);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, hold_mod_tap_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_same_hand_hold_til_timeout) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, MATRIX_COLS - 2, 0, RCTL_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Continue holding til the tapping term.\n    EXPECT_REPORT(driver, (KC_RIGHT_CTRL));\n    EXPECT_REPORT(driver, (KC_RIGHT_CTRL, KC_RIGHT_SHIFT));\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_RIGHT_SHIFT));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_nested_press_opposite_hands) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_nested_press_same_hand) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, RSFT_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_same_hand_streak_roll) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 1, 2, 3.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_same_hand_streak_orders) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 3, 2, 1.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 3, 1, 2.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 2, 3, 1.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_A, KC_C));\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_opposite_hands_roll) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 1, 2, 3.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_two_left_one_right) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 3.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 2, then key 1.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 3.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_CTRL));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release key 1, then key 2.\n    EXPECT_REPORT(driver, (KC_LEFT_CTRL));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, three_mod_taps_one_held_two_tapped) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 2, 0, CTL_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 3, 2, 1.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys 3, 1, 2.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_taps_one_regular_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 2, 0, CTL_T(KC_B));\n    auto       regular_key  = KeymapKey(0, MATRIX_COLS - 1, 0, KC_C);\n\n    set_keymap({mod_tap_key1, mod_tap_key2, regular_key});\n\n    // Press keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_C));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys.\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, tap_regular_key_while_layer_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto       regular_key        = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n    auto       no_key             = KeymapKey(1, 1, 0, XXXXXXX);\n    auto       layer_key          = KeymapKey(1, MATRIX_COLS - 1, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key, no_key, layer_key});\n\n    // Press layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    // Press regular key.\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 0);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, nested_tap_of_layer_0_layer_tap_keys) {\n    TestDriver driver;\n    InSequence s;\n    // The keys are layer-taps on layer 2 but regular keys on layer 1.\n    auto first_layer_tap_key  = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto second_layer_tap_key = KeymapKey(0, MATRIX_COLS - 1, 0, LT(1, KC_P));\n    auto first_key_on_layer   = KeymapKey(1, 1, 0, KC_B);\n    auto second_key_on_layer  = KeymapKey(1, MATRIX_COLS - 1, 0, KC_Q);\n\n    set_keymap({first_layer_tap_key, second_layer_tap_key, first_key_on_layer, second_key_on_layer});\n\n    // Press first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_layer_tap_key.press();\n    run_one_scan_loop();\n    // Press second layer-tap key.\n    second_layer_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release second layer-tap key.\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    second_layer_tap_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_layer_tap_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 0);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, lt_mt_one_regular_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       lt_key      = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto       mt_key0     = KeymapKey(0, 2, 0, SFT_T(KC_B));\n    auto       mt_key1     = KeymapKey(1, 2, 0, CTL_T(KC_C));\n    auto       regular_key = KeymapKey(1, MATRIX_COLS - 1, 0, KC_X);\n    auto       no_key0     = KeymapKey(0, MATRIX_COLS - 1, 0, XXXXXXX);\n    auto       no_key1     = KeymapKey(1, 1, 0, XXXXXXX);\n\n    set_keymap({lt_key, mt_key0, mt_key1, regular_key, no_key0, no_key1});\n\n    // Press LT, MT, and regular key.\n    EXPECT_NO_REPORT(driver);\n    lt_key.press();\n    run_one_scan_loop();\n    mt_key1.press();\n    run_one_scan_loop();\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release the regular key.\n    EXPECT_REPORT(driver, (KC_LCTL));\n    EXPECT_REPORT(driver, (KC_LCTL, KC_X));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(get_mods(), MOD_BIT_LCTRL);\n    EXPECT_EQ(layer_state, 2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release MT key.\n    EXPECT_EMPTY_REPORT(driver);\n    mt_key1.release();\n    run_one_scan_loop();\n    EXPECT_EQ(get_mods(), 0);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release LT key.\n    EXPECT_NO_REPORT(driver);\n    lt_key.release();\n    run_one_scan_loop();\n    EXPECT_EQ(layer_state, 0);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, nested_tap_of_layer_tap_keys) {\n    TestDriver driver;\n    InSequence s;\n    // The keys are layer-taps on all layers.\n    auto first_key_layer_0  = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto second_key_layer_0 = KeymapKey(0, MATRIX_COLS - 1, 0, LT(1, KC_P));\n    auto first_key_layer_1  = KeymapKey(1, 1, 0, LT(2, KC_B));\n    auto second_key_layer_1 = KeymapKey(1, MATRIX_COLS - 1, 0, LT(2, KC_Q));\n    auto first_key_layer_2  = KeymapKey(2, 1, 0, KC_TRNS);\n    auto second_key_layer_2 = KeymapKey(2, MATRIX_COLS - 1, 0, KC_TRNS);\n\n    set_keymap({first_key_layer_0, second_key_layer_0, first_key_layer_1, second_key_layer_1, first_key_layer_2, second_key_layer_2});\n\n    // Press first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_key_layer_0.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press second layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    second_key_layer_0.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release second layer-tap key.\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    second_key_layer_0.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release first layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    first_key_layer_0.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, roll_layer_tap_key_with_regular_key) {\n    TestDriver driver;\n    InSequence s;\n\n    auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto regular_key        = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n    auto layer_key          = KeymapKey(1, MATRIX_COLS - 1, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key, layer_key});\n\n    // Press layer-tap-hold key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release layer-tap-hold key.\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_A));\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release regular key.\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(ChordalHoldPermissiveHoldFlowTap, two_mod_tap_keys_stuttered_press) {\n    TestDriver driver;\n    InSequence s;\n\n    auto mod_tap_key1 = KeymapKey(0, 1, 0, LSFT_T(KC_A));\n    auto mod_tap_key2 = KeymapKey(0, 2, 0, LCTL_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    // Hold first mod-tap key until the tapping term.\n    EXPECT_REPORT(driver, (KC_LSFT));\n    mod_tap_key1.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press the second mod-tap key, then quickly release and press the first.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_REPORT(driver, (KC_B, KC_A));\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    EXPECT_EQ(get_mods(), 0); // Verify that Shift was released.\n    VERIFY_AND_CLEAR(driver);\n\n    // Release both keys.\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/retro_shift_permissive_hold/config.h",
    "content": "/* Copyright 2022 Isaac Elenbaas\n * Copyright 2024 Google LLC\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define CHORDAL_HOLD\n#define PERMISSIVE_HOLD\n\n#define RETRO_SHIFT 2 * TAPPING_TERM\n// releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested\n#define AUTO_SHIFT_TIMEOUT TAPPING_TERM\n#define AUTO_SHIFT_MODIFIERS\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/retro_shift_permissive_hold/test.mk",
    "content": "# Copyright 2022 Isaac Elenbaas\n# Copyright 2024 Google LLC\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAUTO_SHIFT_ENABLE = yes\nINTROSPECTION_KEYMAP_C = test_keymap.c\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/retro_shift_permissive_hold/test_keymap.c",
    "content": "// Copyright 2024 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"quantum.h\"\n\nconst char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = {\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'*', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n    {'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},\n};\n"
  },
  {
    "path": "tests/tap_hold_configurations/chordal_hold/retro_shift_permissive_hold/test_retro_shift.cpp",
    "content": "/* Copyright 2022 Isaac Elenbaas\n * Copyright 2024 Google LLC\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nbool get_auto_shifted_key(uint16_t keycode, keyrecord_t *record) {\n    return true;\n}\n\nusing testing::_;\nusing testing::AnyNumber;\nusing testing::AnyOf;\nusing testing::InSequence;\n\nclass RetroShiftPermissiveHold : public TestFixture {};\n\nTEST_F(RetroShiftPermissiveHold, tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, tap_regular_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, tap_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LCTL))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LCTL, KC_A));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, hold_regular_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL));\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, hold_mod_tap_key_while_mod_tap_key_is_held_over_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT),\n                KeyboardReport(KC_LCTL))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL, KC_LSFT, KC_A));\n    // clang-format off\n    EXPECT_CALL(driver, send_keyboard_mock(AnyOf(\n                KeyboardReport(KC_LCTL, KC_LSFT),\n                KeyboardReport(KC_LSFT))))\n        .Times(AnyNumber());\n    // clang-format on\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    idle_for(TAPPING_TERM);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, roll_tap_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, roll_tap_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, roll_hold_regular_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       regular_key      = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(RetroShiftPermissiveHold, roll_hold_mod_tap_key_while_mod_tap_key_is_held_under_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key    = KeymapKey(0, 0, 0, CTL_T(KC_P));\n    auto       mod_tap_regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, ALT_T(KC_A));\n\n    set_keymap({mod_tap_hold_key, mod_tap_regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-regular key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_regular_key.press();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-regular key. */\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(AUTO_SHIFT_TIMEOUT);\n    mod_tap_regular_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/default_mod_tap/config.h",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/tap_hold_configurations/default_mod_tap/test.mk",
    "content": "# Copyright 2021 Stefan Kerkmann\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n"
  },
  {
    "path": "tests/tap_hold_configurations/default_mod_tap/test_one_shot_layer.cpp",
    "content": "#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass OneShotLayerModTap : public TestFixture {};\n\nTEST_F(OneShotLayerModTap, tap_mod_tap_hold_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       osl_key          = KeymapKey{0, 0, 0, OSL(1)};\n    auto       mod_tap_hold_key = KeymapKey(1, 1, 0, SFT_T(KC_A));\n\n    set_keymap({osl_key, mod_tap_hold_key});\n\n    /* Set one shot layer */\n    tap_key(osl_key);\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key */\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    expect_layer_state(0);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(OneShotLayerModTap, tap_and_hold_mod_tap_hold_key_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       osl_key          = KeymapKey{0, 0, 0, OSL(1)};\n    auto       mod_tap_hold_key = KeymapKey(1, 1, 0, SFT_T(KC_A));\n\n    set_keymap({osl_key, mod_tap_hold_key});\n\n    /* Set one shot layer */\n    tap_key(osl_key);\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-hold key */\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM + 1);\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(OneShotLayerModTap, tap_regular_key_while_mod_tap_key_is_held_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       osl_key          = KeymapKey{0, 0, 0, OSL(1)};\n    auto       mod_tap_hold_key = KeymapKey(1, 1, 0, SFT_T(KC_A));\n    auto       regular_key1     = KeymapKey(1, 2, 0, KC_1);\n\n    set_keymap({osl_key, mod_tap_hold_key, regular_key1});\n\n    /* Set one shot layer */\n    tap_key(osl_key);\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-hold key */\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM + 1);\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_1));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    regular_key1.press();\n    run_one_scan_loop();\n    expect_layer_state(0);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key1.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(OneShotLayerModTap, tap_a_mod_tap_key_while_another_mod_tap_key_is_held_tapping_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       osl_key                 = KeymapKey{0, 0, 0, OSL(1)};\n    auto       regular_key0            = KeymapKey(0, 2, 0, KC_0);\n    auto       first_mod_tap_hold_key  = KeymapKey(1, 1, 0, SFT_T(KC_A));\n    auto       second_mod_tap_hold_key = KeymapKey(1, 2, 0, CTL_T(KC_B));\n\n    set_keymap({osl_key, regular_key0, first_mod_tap_hold_key, second_mod_tap_hold_key});\n\n    /* Set one shot layer */\n    tap_key(osl_key);\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press first mod-tap-hold key */\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    first_mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM + 1);\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press second tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    second_mod_tap_hold_key.press();\n    run_one_scan_loop();\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release second tap-hold key */\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    second_mod_tap_hold_key.release();\n    run_one_scan_loop();\n    expect_layer_state(0);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release first mod-tap-hold key */\n    EXPECT_EMPTY_REPORT(driver);\n    first_mod_tap_hold_key.release();\n    run_one_scan_loop();\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(OneShotLayerModTap, tap_regular_key_while_mod_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       osl_key          = KeymapKey{0, 0, 0, OSL(1)};\n    auto       mod_tap_hold_key = KeymapKey(1, 1, 0, SFT_T(KC_A));\n    auto       regular_key0     = KeymapKey(0, 2, 0, KC_0);\n    auto       regular_key1     = KeymapKey(1, 2, 0, KC_1);\n\n    set_keymap({osl_key, mod_tap_hold_key, regular_key0, regular_key1});\n\n    /* Set one shot layer */\n    tap_key(osl_key);\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press mod-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key1.press();\n    run_one_scan_loop();\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key1.release();\n    run_one_scan_loop();\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release mod-tap-hold key */\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_0));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    expect_layer_state(0);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n\nTEST_F(OneShotLayerModTap, tap_a_mod_tap_key_while_another_mod_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       osl_key                 = KeymapKey{0, 0, 0, OSL(1)};\n    auto       regular_key0            = KeymapKey(0, 2, 0, KC_0);\n    auto       first_mod_tap_hold_key  = KeymapKey(1, 1, 0, SFT_T(KC_A));\n    auto       second_mod_tap_hold_key = KeymapKey(1, 2, 0, CTL_T(KC_B));\n\n    set_keymap({osl_key, regular_key0, first_mod_tap_hold_key, second_mod_tap_hold_key});\n\n    /* Set one shot layer */\n    tap_key(osl_key);\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press first mod-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    first_mod_tap_hold_key.press();\n    run_one_scan_loop();\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Press second tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    second_mod_tap_hold_key.press();\n    run_one_scan_loop();\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release second tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    second_mod_tap_hold_key.release();\n    run_one_scan_loop();\n    expect_layer_state(1);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n\n    /* Release first mod-tap-hold key */\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_0));\n    EXPECT_EMPTY_REPORT(driver);\n    first_mod_tap_hold_key.release();\n    run_one_scan_loop();\n    expect_layer_state(0);\n    testing::Mock::VerifyAndClearExpectations(&driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/default_mod_tap/test_tap_hold.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass DefaultTapHold : public TestFixture {};\n\nTEST_F(DefaultTapHold, tap_regular_key_while_mod_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Idle for tapping term of mod tap hold key. */\n    idle_for(TAPPING_TERM - 3);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(DefaultTapHold, tap_a_mod_tap_key_while_another_mod_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       first_mod_tap_hold_key  = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto       second_mod_tap_hold_key = KeymapKey(0, 2, 0, RSFT_T(KC_A));\n\n    set_keymap({first_mod_tap_hold_key, second_mod_tap_hold_key});\n\n    /* Press first mod-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    first_mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press second tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    second_mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release second tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    second_mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release first mod-tap-hold key */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    first_mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(DefaultTapHold, tap_regular_key_while_layer_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto       regular_key        = KeymapKey(0, 2, 0, KC_A);\n    auto       layer_key          = KeymapKey(1, 2, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key, layer_key});\n\n    /* Press layer-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release layer-tap-hold key */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(DefaultTapHold, tap_mod_tap_hold_key_two_times) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press mod-tap-hold key again. */\n    EXPECT_REPORT(driver, (KC_P));\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(DefaultTapHold, tap_mod_tap_hold_key_twice_and_hold_on_second_time) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press mod-tap-hold key again. */\n    EXPECT_REPORT(driver, (KC_P));\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(DefaultTapHold, tap_and_hold_mod_tap_hold_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/flow_tap/config.h",
    "content": "/* Copyright 2022 Vladislav Kucheriavykh\n * Copyright 2025 Google LLC\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define FLOW_TAP_TERM 150\n#define PERMISSIVE_HOLD\n"
  },
  {
    "path": "tests/tap_hold_configurations/flow_tap/test.mk",
    "content": "# Copyright 2022 Vladislav Kucheriavykh\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nCOMBO_ENABLE = yes\n\nINTROSPECTION_KEYMAP_C = test_keymap.c\n"
  },
  {
    "path": "tests/tap_hold_configurations/flow_tap/test_keymap.c",
    "content": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"quantum.h\"\n\nuint16_t const mt_lt_combo[] = {SFT_T(KC_X), LT(1, KC_Y), COMBO_END};\n\n// clang-format off\ncombo_t key_combos[] = {\n    COMBO(mt_lt_combo, KC_Z),\n};\n// clang-format on\n"
  },
  {
    "path": "tests/tap_hold_configurations/flow_tap/test_tap_hold.cpp",
    "content": "// Copyright 2025 Google LLC\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::AnyNumber;\nusing testing::InSequence;\n\nclass FlowTapTest : public TestFixture {};\n\n// Test an input of quick distinct taps. All should be settled as tapped.\nTEST_F(FlowTapTest, distinct_taps) {\n    TestDriver driver;\n    InSequence s;\n    auto       regular_key  = KeymapKey(0, 0, 0, KC_A);\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C));\n    auto       mod_tap_key3 = KeymapKey(0, 3, 0, ALT_T(KC_D));\n\n    set_keymap({regular_key, mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key, FLOW_TAP_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap mod-tap 1.\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap mod-tap 2.\n    EXPECT_REPORT(driver, (KC_C));\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap mod-tap 3.\n    EXPECT_REPORT(driver, (KC_D));\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    mod_tap_key3.release();\n    idle_for(FLOW_TAP_TERM + 1); // Pause between taps.\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap mod-tap 1.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap mod-tap 2.\n    EXPECT_REPORT(driver, (KC_C));\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    mod_tap_key2.release();\n    idle_for(FLOW_TAP_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n}\n\n// By default, Flow Tap is disabled when mods other than Shift and AltGr are on.\nTEST_F(FlowTapTest, hotkey_taps) {\n    TestDriver driver;\n    InSequence s;\n    auto       ctrl_key    = KeymapKey(0, 0, 0, KC_LCTL);\n    auto       shft_key    = KeymapKey(0, 1, 0, KC_LSFT);\n    auto       alt_key     = KeymapKey(0, 2, 0, KC_LALT);\n    auto       gui_key     = KeymapKey(0, 3, 0, KC_LGUI);\n    auto       regular_key = KeymapKey(0, 4, 0, KC_A);\n    auto       mod_tap_key = KeymapKey(0, 5, 0, RCTL_T(KC_B));\n\n    set_keymap({ctrl_key, shft_key, alt_key, gui_key, regular_key, mod_tap_key});\n\n    for (KeymapKey* mod_key : {&ctrl_key, &alt_key, &gui_key}) {\n        // Hold mod key.\n        EXPECT_REPORT(driver, (mod_key->code));\n        mod_key->press();\n        run_one_scan_loop();\n\n        // Tap regular key.\n        EXPECT_REPORT(driver, (mod_key->code, KC_A));\n        regular_key.press();\n        run_one_scan_loop();\n        VERIFY_AND_CLEAR(driver);\n\n        EXPECT_REPORT(driver, (mod_key->code));\n        regular_key.release();\n        run_one_scan_loop();\n        VERIFY_AND_CLEAR(driver);\n\n        // Press mod-tap, where Flow Tap is disabled due to the held mod.\n        EXPECT_REPORT(driver, (mod_key->code, KC_RCTL));\n        mod_tap_key.press();\n        idle_for(TAPPING_TERM + 1);\n        VERIFY_AND_CLEAR(driver);\n\n        // Release mod-tap.\n        EXPECT_REPORT(driver, (mod_key->code));\n        mod_tap_key.release();\n        run_one_scan_loop();\n\n        // Release mod key.\n        EXPECT_EMPTY_REPORT(driver);\n        mod_key->release();\n        run_one_scan_loop();\n        VERIFY_AND_CLEAR(driver);\n    }\n\n    // Hold Shift key.\n    EXPECT_REPORT(driver, (KC_LSFT));\n    shft_key.press();\n    run_one_scan_loop();\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LSFT));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap, where Flow Tap applies to settle as tapped.\n    EXPECT_REPORT(driver, (KC_LSFT, KC_B));\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap.\n    EXPECT_REPORT(driver, (KC_LSFT));\n    mod_tap_key.release();\n    run_one_scan_loop();\n\n    // Release Shift key.\n    EXPECT_EMPTY_REPORT(driver);\n    shft_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\n// Test input with two mod-taps in a rolled press quickly after a regular key.\nTEST_F(FlowTapTest, rolled_press) {\n    TestDriver driver;\n    InSequence s;\n    auto       regular_key  = KeymapKey(0, 0, 0, KC_A);\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C));\n\n    set_keymap({regular_key, mod_tap_key1, mod_tap_key2});\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap key 1 quickly after regular key. The mod-tap should settle\n    // immediately as tapped, sending `KC_B`.\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap key 2 quickly.\n    EXPECT_REPORT(driver, (KC_B, KC_C));\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Hold for longer than the tapping term.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, long_flow_tap_settled_as_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       regular_key = KeymapKey(0, 0, 0, KC_A);\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_B));\n\n    set_keymap({regular_key, mod_tap_key});\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap key.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Hold for the tapping term.\n    EXPECT_REPORT(driver, (KC_LSFT));\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, holding_multiple_mod_taps) {\n    TestDriver driver;\n    InSequence s;\n    auto       regular_key  = KeymapKey(0, 0, 0, KC_A);\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_B));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_C));\n\n    set_keymap({regular_key, mod_tap_key1, mod_tap_key2});\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    idle_for(TAPPING_TERM - 5); // Hold almost until tapping term.\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_LSFT));\n    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL));\n    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL, KC_A));\n    regular_key.press();\n    idle_for(10);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys.\n    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, holding_mod_tap_with_regular_mod) {\n    TestDriver driver;\n    InSequence s;\n    auto       regular_key = KeymapKey(0, 0, 0, KC_A);\n    auto       mod_key     = KeymapKey(0, 1, 0, KC_LSFT);\n    auto       mod_tap_key = KeymapKey(0, 2, 0, CTL_T(KC_C));\n\n    set_keymap({regular_key, mod_key, mod_tap_key});\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod and mod-tap keys.\n    EXPECT_REPORT(driver, (KC_LSFT));\n    mod_key.press();\n    run_one_scan_loop();\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM - 5); // Hold almost until tapping term.\n    VERIFY_AND_CLEAR(driver);\n\n    // Press regular key.\n    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL));\n    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL, KC_A));\n    regular_key.press();\n    idle_for(10);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release keys.\n    EXPECT_REPORT(driver, (KC_LSFT, KC_LCTL));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    mod_key.release();\n    run_one_scan_loop();\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, layer_tap_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       regular_key   = KeymapKey(0, 0, 0, KC_A);\n    auto       layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_B));\n    auto       regular_key2  = KeymapKey(1, 0, 0, KC_C);\n\n    set_keymap({regular_key, layer_tap_key, regular_key2});\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press layer-tap key, quickly after the regular key.\n    EXPECT_REPORT(driver, (KC_B));\n    layer_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release layer-tap key.\n    EXPECT_EMPTY_REPORT(driver);\n    layer_tap_key.release();\n    run_one_scan_loop();\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press layer-tap key, slowly after the regular key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    EXPECT_EQ(layer_state, 1 << 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap regular key2.\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, layer_tap_ignored_with_disabled_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       no_key        = KeymapKey(0, 0, 0, KC_NO);\n    auto       regular_key   = KeymapKey(1, 0, 0, KC_ESC);\n    auto       layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto       mod_tap_key   = KeymapKey(0, 2, 0, CTL_T(KC_B));\n\n    set_keymap({no_key, regular_key, layer_tap_key, mod_tap_key});\n\n    EXPECT_REPORT(driver, (KC_ESC));\n    EXPECT_EMPTY_REPORT(driver);\n    layer_tap_key.press();\n    idle_for(TAPPING_TERM + 1);\n    tap_key(regular_key);\n    layer_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LCTL));\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, layer_tap_ignored_with_disabled_key_complex) {\n    TestDriver driver;\n    InSequence s;\n    auto       regular_key1  = KeymapKey(0, 0, 0, KC_Q);\n    auto       layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_SPC));\n    auto       mod_tap_key1  = KeymapKey(0, 2, 0, CTL_T(KC_T));\n    // Place RALT_T(KC_I), where Flow Tap is enabled, in the same position on\n    // layer 0 as KC_RGHT, where Flow Tap is disabled. This tests that Flow Tap\n    // tracks the keycode from the correct layer.\n    auto mod_tap_key2 = KeymapKey(0, 3, 0, RALT_T(KC_I));\n    auto regular_key2 = KeymapKey(1, 3, 0, KC_RGHT);\n\n    set_keymap({regular_key1, layer_tap_key, mod_tap_key1, mod_tap_key2, regular_key2});\n\n    // Tap regular key 1.\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key1);\n    idle_for(FLOW_TAP_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Hold layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_key.press();\n    run_one_scan_loop();\n    // idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap regular key 2.\n    EXPECT_REPORT(driver, (KC_RGHT));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key2);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release layer-tap key.\n    EXPECT_NO_REPORT(driver);\n    layer_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Quickly hold mod-tap key 1.\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LCTL));\n    EXPECT_REPORT(driver, (KC_LCTL, KC_Q));\n    EXPECT_REPORT(driver, (KC_LCTL));\n    tap_key(regular_key1);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, layer_tap_ignored_with_enabled_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       no_key        = KeymapKey(0, 0, 0, KC_NO);\n    auto       regular_key   = KeymapKey(1, 0, 0, KC_C);\n    auto       layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto       mod_tap_key   = KeymapKey(0, 2, 0, CTL_T(KC_B));\n\n    set_keymap({no_key, regular_key, layer_tap_key, mod_tap_key});\n\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    layer_tap_key.press();\n    idle_for(TAPPING_TERM + 1);\n    tap_key(regular_key);\n    layer_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, combo_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       regular_key   = KeymapKey(0, 0, 0, KC_A);\n    auto       mod_tap_key   = KeymapKey(0, 1, 0, SFT_T(KC_X));\n    auto       layer_tap_key = KeymapKey(0, 2, 0, LT(1, KC_Y));\n\n    set_keymap({regular_key, mod_tap_key, layer_tap_key});\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Press combo keys quickly after regular key.\n    EXPECT_REPORT(driver, (KC_Z));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_combo({mod_tap_key, layer_tap_key});\n    VERIFY_AND_CLEAR(driver);\n\n    // Press mod-tap key quickly.\n    EXPECT_REPORT(driver, (KC_X));\n    mod_tap_key.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, oneshot_mod_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       regular_key = KeymapKey(0, 0, 0, KC_A);\n    auto       osm_key     = KeymapKey(0, 1, 0, OSM(MOD_LSFT));\n\n    set_keymap({regular_key, osm_key});\n\n    // Tap regular key.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Tap OSM, tap regular key.\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(osm_key);\n    tap_key(regular_key);\n    VERIFY_AND_CLEAR(driver);\n\n    // Nested press of OSM and regular keys.\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n    EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT))).Times(AnyNumber());\n    EXPECT_EMPTY_REPORT(driver);\n    osm_key.press();\n    run_one_scan_loop();\n    tap_key(regular_key);\n    osm_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, quick_tap) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_A));\n\n    set_keymap({mod_tap_key});\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(mod_tap_key);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap key.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, rolling_mt_mt) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));\n\n    set_keymap({mod_tap_key1, mod_tap_key2});\n\n    EXPECT_NO_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Hold for longer than the tapping term.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, rolling_lt_mt_regular) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));\n    auto       mod_tap_key   = KeymapKey(0, 1, 0, CTL_T(KC_B));\n    auto       regular_key   = KeymapKey(0, 2, 0, KC_C);\n\n    set_keymap({layer_tap_key, mod_tap_key, regular_key});\n\n    EXPECT_NO_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    layer_tap_key.press();\n    run_one_scan_loop();\n    mod_tap_key.press();\n    run_one_scan_loop();\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_B, KC_C));\n    layer_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Hold for longer than the tapping term.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, rolling_lt_regular_mt) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_key = KeymapKey(0, 0, 0, LT(1, KC_A));\n    auto       regular_key   = KeymapKey(0, 1, 0, KC_B);\n    auto       mod_tap_key   = KeymapKey(0, 2, 0, CTL_T(KC_C));\n\n    set_keymap({layer_tap_key, regular_key, mod_tap_key});\n\n    EXPECT_NO_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    layer_tap_key.press();\n    run_one_scan_loop();\n    regular_key.press();\n    run_one_scan_loop();\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_B, KC_C));\n    layer_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Hold for longer than the tapping term.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release mod-tap keys.\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, rolling_mt_mt_mt) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 0, 0, CTL_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 1, 0, GUI_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, 2, 0, ALT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    run_one_scan_loop();\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release first mod-tap key.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_B, KC_C));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Hold for longer than the tapping term.\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    // Release other mod-tap keys.\n    EXPECT_REPORT(driver, (KC_C));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(FlowTapTest, roll_release_132) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key1 = KeymapKey(0, 0, 0, CTL_T(KC_A));\n    auto       mod_tap_key2 = KeymapKey(0, 1, 0, GUI_T(KC_B));\n    auto       mod_tap_key3 = KeymapKey(0, 2, 0, ALT_T(KC_C));\n\n    set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});\n\n    // Press mod-tap keys.\n    EXPECT_NO_REPORT(driver);\n    idle_for(FLOW_TAP_TERM + 1);\n    mod_tap_key1.press();\n    run_one_scan_loop();\n    mod_tap_key2.press();\n    idle_for(FLOW_TAP_TERM + 1);\n    mod_tap_key3.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release first mod-tap key.\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_REPORT(driver, (KC_A, KC_B));\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_key1.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    // Release other mod-tap keys.\n    EXPECT_REPORT(driver, (KC_B, KC_C));\n    EXPECT_REPORT(driver, (KC_B));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key3.release();\n    run_one_scan_loop();\n    mod_tap_key2.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/hold_on_other_key_press/config.h",
    "content": "/* Copyright 2022 Vladislav Kucheriavykh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n#define HOLD_ON_OTHER_KEY_PRESS\n"
  },
  {
    "path": "tests/tap_hold_configurations/hold_on_other_key_press/test.mk",
    "content": "# Copyright 2022 Vladislav Kucheriavykh\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n"
  },
  {
    "path": "tests/tap_hold_configurations/hold_on_other_key_press/test_tap_hold.cpp",
    "content": "/* Copyright 2022 Vladislav Kucheriavykh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass HoldOnOtherKeyPress : public TestFixture {};\n\nTEST_F(HoldOnOtherKeyPress, short_distinct_taps_of_mod_tap_key_and_regular_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key. */\n    EXPECT_REPORT(driver, (KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(HoldOnOtherKeyPress, long_distinct_taps_of_mod_tap_key_and_regular_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Idle for tapping term of mod tap hold key. */\n    EXPECT_REPORT(driver, (KC_LSFT));\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key. */\n    EXPECT_REPORT(driver, (KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(HoldOnOtherKeyPress, short_distinct_taps_of_layer_tap_key_and_regular_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto       regular_key        = KeymapKey(0, 2, 0, KC_A);\n    auto       layer_key          = KeymapKey(0, 2, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key});\n\n    /* Press layer-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release layer-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key. */\n    EXPECT_REPORT(driver, (KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(HoldOnOtherKeyPress, long_distinct_taps_of_layer_tap_key_and_regular_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto       regular_key        = KeymapKey(0, 2, 0, KC_A);\n    auto       layer_key          = KeymapKey(0, 2, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key});\n\n    /* Press layer-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Idle for tapping term of layer tap hold key. */\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release layer-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key. */\n    EXPECT_REPORT(driver, (KC_A));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(HoldOnOtherKeyPress, tap_regular_key_while_mod_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key. */\n    EXPECT_REPORT(driver, (KC_LSFT));\n    EXPECT_REPORT(driver, (KC_A, KC_LSFT));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_REPORT(driver, (KC_LSFT));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Idle for tapping term of mod tap hold key. */\n    idle_for(TAPPING_TERM - 3);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(HoldOnOtherKeyPress, tap_a_mod_tap_key_while_another_mod_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       first_mod_tap_hold_key  = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto       second_mod_tap_hold_key = KeymapKey(0, 2, 0, RSFT_T(KC_A));\n\n    set_keymap({first_mod_tap_hold_key, second_mod_tap_hold_key});\n\n    /* Press first mod-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    first_mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press second tap-hold key */\n    EXPECT_REPORT(driver, (KC_LSFT));\n    second_mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release second tap-hold key */\n    EXPECT_REPORT(driver, (KC_A, KC_LSFT));\n    EXPECT_REPORT(driver, (KC_LSFT));\n    second_mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release first mod-tap-hold key */\n    EXPECT_EMPTY_REPORT(driver);\n    first_mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(HoldOnOtherKeyPress, tap_regular_key_while_layer_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto       regular_key        = KeymapKey(0, 2, 0, KC_A);\n    auto       layer_key          = KeymapKey(1, 2, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key, layer_key});\n\n    /* Press layer-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (KC_B));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release layer-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(HoldOnOtherKeyPress, nested_tap_of_layer_0_layer_tap_keys) {\n    TestDriver driver;\n    InSequence s;\n    /* The keys are layer-taps on layer 0 but regular keys on layer 1 */\n    auto first_layer_tap_key  = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto second_layer_tap_key = KeymapKey(0, 2, 0, LT(1, KC_P));\n    auto first_key_on_layer   = KeymapKey(1, 1, 0, KC_B);\n    auto second_key_on_layer  = KeymapKey(1, 2, 0, KC_Q);\n\n    set_keymap({first_layer_tap_key, second_layer_tap_key, first_key_on_layer, second_key_on_layer});\n\n    /* Press first layer-tap key */\n    EXPECT_NO_REPORT(driver);\n    first_layer_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press second layer-tap key */\n    EXPECT_REPORT(driver, (KC_Q));\n    second_layer_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release second layer-tap key */\n    EXPECT_EMPTY_REPORT(driver);\n    second_layer_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release first layer-tap key */\n    EXPECT_NO_REPORT(driver);\n    first_layer_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(HoldOnOtherKeyPress, nested_tap_of_layer_tap_keys) {\n    TestDriver driver;\n    InSequence s;\n    /* The keys are layer-taps on all layers */\n    auto first_key_layer_0  = KeymapKey(0, 1, 0, LT(1, KC_A));\n    auto second_key_layer_0 = KeymapKey(0, 2, 0, LT(1, KC_P));\n    auto first_key_layer_1  = KeymapKey(1, 1, 0, LT(2, KC_B));\n    auto second_key_layer_1 = KeymapKey(1, 2, 0, LT(2, KC_Q));\n    auto first_key_layer_2  = KeymapKey(2, 1, 0, KC_TRNS);\n    auto second_key_layer_2 = KeymapKey(2, 2, 0, KC_TRNS);\n\n    set_keymap({first_key_layer_0, second_key_layer_0, first_key_layer_1, second_key_layer_1, first_key_layer_2, second_key_layer_2});\n\n    /* Press first layer-tap key */\n    EXPECT_NO_REPORT(driver);\n    first_key_layer_0.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press second layer-tap key */\n    EXPECT_NO_REPORT(driver);\n    second_key_layer_0.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release second layer-tap key */\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    second_key_layer_0.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release first layer-tap key */\n    EXPECT_NO_REPORT(driver);\n    first_key_layer_0.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(HoldOnOtherKeyPress, roll_mod_tap_key_with_regular_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key. */\n    EXPECT_REPORT(driver, (KC_LSFT));\n    EXPECT_REPORT(driver, (KC_A, KC_LSFT));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_A));\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(HoldOnOtherKeyPress, roll_layer_tap_key_with_regular_key) {\n    TestDriver driver;\n    InSequence s;\n\n    auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto regular_key        = KeymapKey(0, 2, 0, KC_A);\n    auto layer_key          = KeymapKey(1, 2, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key, layer_key});\n\n    /* Press layer-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_REPORT(driver, (KC_B));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release layer-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/permissive_hold/config.h",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define PERMISSIVE_HOLD\n"
  },
  {
    "path": "tests/tap_hold_configurations/permissive_hold/test.mk",
    "content": "# Copyright 2021 Stefan Kerkmann\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n"
  },
  {
    "path": "tests/tap_hold_configurations/permissive_hold/test_one_shot_keys.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"action_util.h\"\n#include \"keyboard_report_util.hpp\"\n#include \"test_common.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass OneShot : public TestFixture {};\nclass OneShotParametrizedTestFixture : public ::testing::WithParamInterface<std::pair<KeymapKey, KeymapKey>>, public OneShot {};\n\nTEST_P(OneShotParametrizedTestFixture, OSMAsRegularModifierWithAdditionalKeypress) {\n    TestDriver driver;\n    KeymapKey  osm_key     = GetParam().first;\n    KeymapKey  regular_key = GetParam().second;\n\n    set_keymap({osm_key, regular_key});\n\n    /* Press OSM */\n    EXPECT_NO_REPORT(driver);\n    osm_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_REPORT(driver, (osm_key.report_code)).Times(2);\n    EXPECT_REPORT(driver, (regular_key.report_code, osm_key.report_code)).Times(1);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release OSM */\n    EXPECT_EMPTY_REPORT(driver).Times(1);\n    osm_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\n// clang-format off\n\nINSTANTIATE_TEST_CASE_P(\n    OneShotModifierTests,\n    OneShotParametrizedTestFixture,\n    ::testing::Values(\n        /* first is osm key, second is regular key. */\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LCTL), KC_LCTL}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LALT), KC_LALT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LGUI), KC_LGUI}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RCTL), KC_RCTL}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RSFT), KC_RSFT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RALT), KC_RALT}, KeymapKey{0, 1, 1, KC_A}),\n        std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RGUI), KC_RGUI}, KeymapKey{0, 1, 1, KC_A})\n        ));\n// clang-format on\n"
  },
  {
    "path": "tests/tap_hold_configurations/permissive_hold/test_tap_hold.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\nclass PermissiveHold : public TestFixture {};\n\nTEST_F(PermissiveHold, tap_regular_key_while_mod_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, regular_key.report_code));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(PermissiveHold, tap_a_mod_tap_key_while_another_mod_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       first_mod_tap_hold_key  = KeymapKey(0, 1, 0, SFT_T(KC_P));\n    auto       second_mod_tap_hold_key = KeymapKey(0, 2, 0, RSFT_T(KC_A));\n\n    set_keymap({first_mod_tap_hold_key, second_mod_tap_hold_key});\n\n    /* Press first mod-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    first_mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press second mod-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    second_mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release second mod-tap-hold key */\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, second_mod_tap_hold_key.report_code));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    second_mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release first mod-tap-hold key */\n    EXPECT_EMPTY_REPORT(driver);\n    first_mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(PermissiveHold, tap_regular_key_while_layer_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto       regular_key        = KeymapKey(0, 2, 0, KC_A);\n    auto       layer_key          = KeymapKey(1, 2, 0, KC_B);\n\n    set_keymap({layer_tap_hold_key, regular_key, layer_key});\n\n    /* Press layer-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_REPORT(driver, (layer_key.report_code));\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release layer-tap-hold key */\n    EXPECT_NO_REPORT(driver);\n    layer_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/quick_tap/config.h",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define QUICK_TAP_TERM 100\n// Although a seemingly superfluous addition since the default per-key function behaves\n// no differently from defining a single global QUICK_TAP_TERM, this has been useful\n// to catch compilation errors and prevent regressions in the future; see PR #19893.\n#define QUICK_TAP_TERM_PER_KEY\n"
  },
  {
    "path": "tests/tap_hold_configurations/quick_tap/test.mk",
    "content": "# Copyright 2021 Stefan Kerkmann\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n"
  },
  {
    "path": "tests/tap_hold_configurations/quick_tap/test_action_layer.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"test_common.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass ActionLayer : public TestFixture {};\n\nTEST_F(ActionLayer, LayerTapToggleWithToggleWithKeypress) {\n    TestDriver driver;\n    KeymapKey  layer_key = KeymapKey{0, 0, 0, TT(1)};\n\n    /* These keys must have the same position in the matrix, only the layer is different. */\n    KeymapKey regular_key = KeymapKey{0, 1, 0, KC_A};\n    set_keymap({layer_key, regular_key, KeymapKey{1, 1, 0, KC_B}});\n\n    /* Tap TT five times . */\n    /* TODO: Tapping Force Hold breaks TT */\n    EXPECT_NO_REPORT(driver);\n\n    layer_key.press();\n    run_one_scan_loop();\n    layer_key.release();\n    run_one_scan_loop();\n    expect_layer_state(0);\n\n    idle_for(QUICK_TAP_TERM + 10);\n\n    layer_key.press();\n    run_one_scan_loop();\n    layer_key.release();\n    run_one_scan_loop();\n    expect_layer_state(0);\n\n    layer_key.press();\n    run_one_scan_loop();\n    layer_key.release();\n    run_one_scan_loop();\n    expect_layer_state(0);\n\n    layer_key.press();\n    run_one_scan_loop();\n    layer_key.release();\n    run_one_scan_loop();\n    expect_layer_state(0);\n\n    layer_key.press();\n    run_one_scan_loop();\n    layer_key.release();\n    run_one_scan_loop();\n    expect_layer_state(0);\n\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A)).Times(1);\n    regular_key.press();\n    run_one_scan_loop();\n    expect_layer_state(0);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver).Times(1);\n    regular_key.release();\n    run_one_scan_loop();\n    expect_layer_state(0);\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/quick_tap/test_quick_tap.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"config.h\"\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass QuickTap : public TestFixture {};\n\nTEST_F(QuickTap, tap_regular_key_while_layer_tap_key_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       layer_tap_key = KeymapKey(0, 1, 0, LT(1, KC_P));\n    auto       regular_key   = KeymapKey(0, 2, 0, KC_A);\n    auto       layer_key     = KeymapKey(1, 2, 0, KC_B);\n\n    set_keymap({layer_tap_key, regular_key, layer_key});\n\n    /* Press layer-tap key */\n    EXPECT_NO_REPORT(driver);\n    layer_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release layer-tap key */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_A, KC_P));\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    layer_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(QuickTap, tap_key_and_tap_again_before_quick_tap_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    /* Press mod-tap key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    idle_for(QUICK_TAP_TERM - 10);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press and tap mod-tap key again. */\n    EXPECT_REPORT(driver, (KC_P));\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(QuickTap, tap_key_and_hold_again_before_quick_tap_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    /* Press mod-tap key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    idle_for(QUICK_TAP_TERM - 10);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press and hold mod-tap key again. */\n    EXPECT_REPORT(driver, (KC_P));\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Wait until tapping term expired */\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(QuickTap, tap_key_and_tap_again_after_quick_tap_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    /* Press mod-tap key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    idle_for(QUICK_TAP_TERM + 10);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press mod-tap key again. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(QuickTap, tap_key_and_hold_again_after_quick_tap_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_key});\n\n    /* Press mod-tap key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    idle_for(QUICK_TAP_TERM + 10);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press and hold mod-tap key again. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Wait until tapping term expired */\n    EXPECT_REPORT(driver, (KC_LSFT));\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/retro_tapping/config.h",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define RETRO_TAPPING\n#define DUMMY_MOD_NEUTRALIZER_KEYCODE KC_RIGHT_CTRL\n#define MODS_TO_NEUTRALIZE \\\n    { MOD_BIT(KC_LEFT_GUI) }\n"
  },
  {
    "path": "tests/tap_hold_configurations/retro_tapping/test.mk",
    "content": "# Copyright 2021 Stefan Kerkmann\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n"
  },
  {
    "path": "tests/tap_hold_configurations/retro_tapping/test_key_roll.cpp",
    "content": "/* Copyright 2024 John Rigoni\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"keycodes.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass RetroTapKeyRoll : public TestFixture {};\n\nTEST_F(RetroTapKeyRoll, regular_to_left_gui_mod_over_tap_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, LGUI_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_B);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    EXPECT_REPORT(driver, (KC_B));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B, KC_LEFT_GUI));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_GUI));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_GUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));\n    EXPECT_REPORT(driver, (KC_LEFT_GUI));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapKeyRoll, regular_to_mod_over_tap_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_B);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    EXPECT_REPORT(driver, (KC_B));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B, KC_LEFT_SHIFT));\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapKeyRoll, regular_to_mod_under_tap_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_B);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    EXPECT_REPORT(driver, (KC_B));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapKeyRoll, mod_under_tap_term_to_regular) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, LGUI_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_B);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_B, KC_P));\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapKeyRoll, mod_over_tap_term_to_regular) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_A));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_B);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_B));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_B));\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapKeyRoll, mod_under_tap_term_to_mod_under_tap_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_gui   = KeymapKey(0, 1, 0, LGUI_T(KC_P));\n    auto       mod_tap_hold_lshft = KeymapKey(0, 2, 0, SFT_T(KC_A));\n\n    set_keymap({mod_tap_hold_gui, mod_tap_hold_lshft});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_lshft.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_gui.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_lshft.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_gui.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapKeyRoll, mod_over_tap_term_to_mod_under_tap_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_gui   = KeymapKey(0, 1, 0, LGUI_T(KC_P));\n    auto       mod_tap_hold_lshft = KeymapKey(0, 2, 0, SFT_T(KC_A));\n\n    set_keymap({mod_tap_hold_gui, mod_tap_hold_lshft});\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_hold_lshft.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_gui.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_lshft.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_P));\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_gui.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapKeyRoll, mod_under_tap_term_to_mod_over_tap_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_gui   = KeymapKey(0, 1, 0, LGUI_T(KC_P));\n    auto       mod_tap_hold_lshft = KeymapKey(0, 2, 0, SFT_T(KC_A));\n\n    set_keymap({mod_tap_hold_gui, mod_tap_hold_lshft});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_lshft.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_GUI));\n    mod_tap_hold_gui.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_GUI));\n    mod_tap_hold_lshft.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_GUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));\n    EXPECT_REPORT(driver, (KC_LEFT_GUI));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_P, KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_gui.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapKeyRoll, mod_under_tap_term_to_mod_over_tap_term_offset) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_gui   = KeymapKey(0, 1, 0, LGUI_T(KC_P));\n    auto       mod_tap_hold_lshft = KeymapKey(0, 2, 0, SFT_T(KC_A));\n\n    set_keymap({mod_tap_hold_gui, mod_tap_hold_lshft});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_lshft.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_gui.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_A));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_lshft.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_GUI));\n    EXPECT_REPORT(driver, (KC_LEFT_GUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));\n    EXPECT_REPORT(driver, (KC_LEFT_GUI));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    idle_for(TAPPING_TERM);\n    mod_tap_hold_gui.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapKeyRoll, mod_over_tap_term_to_mod_over_tap_term) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_gui   = KeymapKey(0, 1, 0, LGUI_T(KC_P));\n    auto       mod_tap_hold_lshft = KeymapKey(0, 2, 0, SFT_T(KC_A));\n\n    set_keymap({mod_tap_hold_gui, mod_tap_hold_lshft});\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_hold_lshft.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_GUI));\n    mod_tap_hold_gui.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_GUI));\n    mod_tap_hold_lshft.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_GUI, DUMMY_MOD_NEUTRALIZER_KEYCODE));\n    EXPECT_REPORT(driver, (KC_LEFT_GUI));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_P, KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_gui.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapKeyRoll, mod_to_mod_to_mod) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_lalt  = KeymapKey(0, 1, 0, LALT_T(KC_R));\n    auto       mod_tap_hold_lshft = KeymapKey(0, 2, 0, SFT_T(KC_A));\n    auto       mod_tap_hold_lctrl = KeymapKey(0, 3, 0, LCTL_T(KC_C));\n\n    set_keymap({mod_tap_hold_lalt, mod_tap_hold_lshft, mod_tap_hold_lctrl});\n\n    EXPECT_REPORT(driver, (KC_LEFT_ALT));\n    mod_tap_hold_lalt.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT, KC_LEFT_ALT));\n    mod_tap_hold_lshft.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    mod_tap_hold_lalt.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_CTRL, KC_LEFT_SHIFT));\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_lctrl.press();\n    idle_for(TAPPING_TERM);\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_CTRL));\n    mod_tap_hold_lshft.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_C, KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_lctrl.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/retro_tapping/test_neutralization.cpp",
    "content": "/* Copyright 2023 Vladislav Kucheriavykh\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass RetroTapNeutralization : public TestFixture {};\n\nTEST_F(RetroTapNeutralization, neutralize_retro_tapped_left_gui_mod_tap) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 7, 0, LGUI_T(KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LGUI));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (DUMMY_MOD_NEUTRALIZER_KEYCODE, KC_LGUI));\n    EXPECT_REPORT(driver, (KC_LGUI));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapNeutralization, do_not_neutralize_retro_tapped_left_shift_mod_tap) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 7, 0, LSFT_T(KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapNeutralization, do_not_neutralize_retro_tapped_right_gui_mod_tap) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 7, 0, RGUI_T(KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_RGUI));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapNeutralization, do_not_neutralize_retro_tapped_left_gui_shift_mod_tap) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 7, 0, MT(MOD_LGUI | MOD_LSFT, KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LSFT, KC_LGUI));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapNeutralization, do_not_neutralize_roll_of_regular_and_mod_tap_keys) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, LGUI_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_NO_REPORT(driver);\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_REPORT(driver, (KC_P, KC_A));\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Idle for tapping term of mod tap hold key. */\n    idle_for(TAPPING_TERM - 3);\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(RetroTapNeutralization, do_not_neutralize_tap_regular_key_while_mod_tap_is_held) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, LGUI_T(KC_P));\n    auto       regular_key      = KeymapKey(0, 2, 0, KC_A);\n\n    set_keymap({mod_tap_hold_key, regular_key});\n\n    /* Press and hold mod-tap key. */\n    EXPECT_REPORT(driver, (KC_LEFT_GUI));\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press regular key. */\n    EXPECT_REPORT(driver, (KC_A, KC_LEFT_GUI));\n    regular_key.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release regular key. */\n    EXPECT_REPORT(driver, (KC_LEFT_GUI));\n    regular_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Idle for tapping term of mod tap hold key. */\n    idle_for(TAPPING_TERM - 3);\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/retro_tapping/test_tap_hold.cpp",
    "content": "\n/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_fixture.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass RetroTapping : public TestFixture {};\n\nTEST_F(RetroTapping, tap_and_hold_mod_tap_hold_key) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 1, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    /* Press mod-tap-hold key. */\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod-tap-hold key. */\n    /* TODO: Why is LSHIFT send at all? */\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/tap_hold_configurations/retro_tapping/test_tapping.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"action_tapping.h\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass Tapping : public TestFixture {};\n\nTEST_F(Tapping, HoldA_SHFT_T_KeyReportsShift) {\n    TestDriver driver;\n    InSequence s;\n    auto       mod_tap_hold_key = KeymapKey(0, 7, 0, SFT_T(KC_P));\n\n    set_keymap({mod_tap_hold_key});\n\n    EXPECT_NO_REPORT(driver);\n    mod_tap_hold_key.press();\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_REPORT(driver, (KC_LSFT));\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    mod_tap_hold_key.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Tapping, ANewTapWithinTappingTermIsBuggy) {\n    TestDriver driver;\n    InSequence s;\n    auto       key_shift_hold_p_tap = KeymapKey(0, 7, 0, SFT_T(KC_P));\n\n    set_keymap({key_shift_hold_p_tap});\n\n    /* Press mod_tap_hold key */\n    EXPECT_NO_REPORT(driver);\n    key_shift_hold_p_tap.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod_tap_hold key */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    key_shift_hold_p_tap.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press mod_tap_hold key again */\n    EXPECT_REPORT(driver, (KC_P));\n    key_shift_hold_p_tap.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod_tap_hold key again */\n    EXPECT_EMPTY_REPORT(driver);\n    key_shift_hold_p_tap.release();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press mod_tap_hold key again */\n    EXPECT_NO_REPORT(driver);\n    key_shift_hold_p_tap.press();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod_tap_hold key again */\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    key_shift_hold_p_tap.release();\n    idle_for(TAPPING_TERM + 1);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Press mod_tap_hold key again */\n    EXPECT_NO_REPORT(driver);\n    key_shift_hold_p_tap.press();\n    idle_for(TAPPING_TERM);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release mod_tap_hold key again */\n    /* TODO: Why is KC_LSFT send? */\n    EXPECT_REPORT(driver, (KC_LSFT));\n    EXPECT_EMPTY_REPORT(driver);\n    EXPECT_REPORT(driver, (KC_P));\n    EXPECT_EMPTY_REPORT(driver);\n    key_shift_hold_p_tap.release();\n    run_one_scan_loop();\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/test_common/build.mk",
    "content": "# Copyright 2021 Stefan Kerkmann\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nCUSTOM_MATRIX=yes\nKEYCODE_STRING_ENABLE = yes\n"
  },
  {
    "path": "tests/test_common/keyboard_report_util.cpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"keyboard_report_util.hpp\"\n#include <cstdint>\n#include <vector>\n#include <algorithm>\n\nextern \"C\" {\n#include \"keycode_string.h\"\n}\n\nusing namespace testing;\n\nextern std::map<uint16_t, std::string> KEYCODE_ID_TABLE;\n\nnamespace {\n\nstd::vector<uint8_t> get_keys(const report_keyboard_t& report) {\n    std::vector<uint8_t> result;\n#if defined(NKRO_ENABLE)\n#    error NKRO support not implemented yet\n#else\n    for (size_t i = 0; i < KEYBOARD_REPORT_KEYS; i++) {\n        if (report.keys[i]) {\n            result.emplace_back(report.keys[i]);\n        }\n    }\n#endif\n    std::sort(result.begin(), result.end());\n    return result;\n}\n\nstd::vector<uint8_t> get_mods(const report_keyboard_t& report) {\n    std::vector<uint8_t> result;\n    for (size_t i = 0; i < 8; i++) {\n        if (report.mods & (1 << i)) {\n            uint8_t code = KC_LEFT_CTRL + i;\n            result.emplace_back(code);\n        }\n    }\n    std::sort(result.begin(), result.end());\n    return result;\n}\n\n} // namespace\n\nbool operator==(const report_keyboard_t& lhs, const report_keyboard_t& rhs) {\n    auto lhskeys = get_keys(lhs);\n    auto rhskeys = get_keys(rhs);\n    return lhs.mods == rhs.mods && lhskeys == rhskeys;\n}\n\nstd::ostream& operator<<(std::ostream& os, const report_keyboard_t& report) {\n    auto keys = get_keys(report);\n    auto mods = get_mods(report);\n\n    os << std::setw(10) << std::left << \"report: \";\n\n    if (!keys.size() && !mods.size()) {\n        return os << \"empty\" << std::endl;\n    }\n\n    os << \"(\";\n    for (auto key = keys.cbegin(); key != keys.cend();) {\n        os << get_keycode_string(*key);\n        key++;\n        if (key != keys.cend()) {\n            os << \", \";\n        }\n    }\n\n    os << \") [\";\n\n    for (auto mod = mods.cbegin(); mod != mods.cend();) {\n        os << get_keycode_string(*mod);\n        mod++;\n        if (mod != mods.cend()) {\n            os << \", \";\n        }\n    }\n\n    return os << \"]\" << std::endl;\n}\n\nKeyboardReportMatcher::KeyboardReportMatcher(const std::vector<uint8_t>& keys) {\n    memset(&m_report, 0, sizeof(report_keyboard_t));\n    for (auto k : keys) {\n        if (IS_MODIFIER_KEYCODE(k)) {\n            m_report.mods |= MOD_BIT(k);\n        } else {\n            add_key_byte(&m_report, k);\n        }\n    }\n}\n\nbool KeyboardReportMatcher::MatchAndExplain(report_keyboard_t& report, MatchResultListener* listener) const {\n    return m_report == report;\n}\n\nvoid KeyboardReportMatcher::DescribeTo(::std::ostream* os) const {\n    *os << \"is equal to \" << m_report;\n}\n\nvoid KeyboardReportMatcher::DescribeNegationTo(::std::ostream* os) const {\n    *os << \"is not equal to \" << m_report;\n}\n"
  },
  {
    "path": "tests/test_common/keyboard_report_util.hpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n#include \"report.h\"\n#include <ostream>\n#include \"gmock/gmock.h\"\n\nbool operator==(const report_keyboard_t& lhs, const report_keyboard_t& rhs);\nstd::ostream& operator<<(std::ostream& stream, const report_keyboard_t& value);\n\nclass KeyboardReportMatcher : public testing::MatcherInterface<report_keyboard_t&> {\n public:\n    KeyboardReportMatcher(const std::vector<uint8_t>& keys);\n    virtual bool MatchAndExplain(report_keyboard_t& report, testing::MatchResultListener* listener) const override;\n    virtual void DescribeTo(::std::ostream* os) const override;\n    virtual void DescribeNegationTo(::std::ostream* os) const override;\nprivate:\n    report_keyboard_t m_report;\n};\n\n\ntemplate<typename... Ts>\ninline testing::Matcher<report_keyboard_t&> KeyboardReport(Ts... keys) {\n    return testing::MakeMatcher(new KeyboardReportMatcher(std::vector<uint8_t>({keys...})));\n}\n"
  },
  {
    "path": "tests/test_common/keymap.c",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"quantum.h\"\n\n// clang-format off\n\nconst uint16_t PROGMEM\n               keymaps[][MATRIX_ROWS][MATRIX_COLS] =\n        {\n            [0] =\n                {\n                   {KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO},\n                   {KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO},\n                   {KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO},\n                   {KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO},\n                },\n};\n\n// clang-format on\n"
  },
  {
    "path": "tests/test_common/main.cpp",
    "content": "#include \"gtest/gtest.h\"\n\nextern \"C\" {\n#include \"stdio.h\"\n#include \"debug.h\"\n\nint8_t sendchar(uint8_t c) {\n    fprintf(stdout, \"%c\", c);\n    return 0;\n}\n\n__attribute__((weak)) debug_config_t debug_config = {0};\n\nvoid init_logging(void) {\n    print_set_sendchar(sendchar);\n\n    // Customise these values to desired behaviour\n    // debug_enable   = true;\n    // debug_matrix   = true;\n    // debug_keyboard = true;\n    // debug_mouse    = true;\n    debug_config.raw = 0xFF;\n}\n}\n\nint main(int argc, char **argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n\n    init_logging();\n\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "tests/test_common/matrix.c",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"matrix.h\"\n#include \"test_matrix.h\"\n#include <string.h>\n\nstatic matrix_row_t matrix[MATRIX_ROWS] = {};\n\nvoid matrix_init(void) {\n    clear_all_keys();\n    matrix_init_kb();\n}\n\nuint8_t matrix_scan(void) {\n    matrix_scan_kb();\n    return 1;\n}\n\nmatrix_row_t matrix_get_row(uint8_t row) {\n    return matrix[row];\n}\n\nvoid matrix_print(void) {}\n\nvoid matrix_init_kb(void) {}\n\nvoid matrix_scan_kb(void) {}\n\nvoid press_key(uint8_t col, uint8_t row) {\n    matrix[row] |= (matrix_row_t)1 << col;\n}\n\nvoid release_key(uint8_t col, uint8_t row) {\n    matrix[row] &= ~((matrix_row_t)1 << col);\n}\n\nbool matrix_is_on(uint8_t row, uint8_t col) {\n    return (matrix[row] & ((matrix_row_t)1 << col));\n}\n\nvoid clear_all_keys(void) {\n    memset(matrix, 0, sizeof(matrix));\n}\n\nvoid led_set(uint8_t usb_led) {}\n"
  },
  {
    "path": "tests/test_common/mouse_report_util.cpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"mouse_report_util.hpp\"\n#include <cstdint>\n#include <vector>\n#include <algorithm>\n\nusing namespace testing;\n\nbool operator==(const report_mouse_t& lhs, const report_mouse_t& rhs) {\n    return lhs.x == rhs.x && lhs.y == rhs.y && lhs.h == rhs.h && lhs.v == rhs.v && lhs.buttons == rhs.buttons;\n}\n\nstd::ostream& operator<<(std::ostream& os, const report_mouse_t& report) {\n    os << std::setw(10) << std::left << \"mouse report: \";\n\n    if (report.x == 0 && report.y == 0 && report.h == 0 && report.v == 0 && report.buttons == 0) {\n        return os << \"empty\" << std::endl;\n    }\n\n    os << \"(X:\" << (int)report.x << \", Y:\" << (int)report.y << \", H:\" << (int)report.h << \", V:\" << (int)report.v << \", B:\" << (int)report.buttons << \")\";\n    return os << std::endl;\n}\n\nMouseReportMatcher::MouseReportMatcher(int16_t x, int16_t y, int8_t h, int8_t v, uint8_t button_mask) {\n    memset(&m_report, 0, sizeof(report_mouse_t));\n    m_report.x       = x;\n    m_report.y       = y;\n    m_report.h       = h;\n    m_report.v       = v;\n    m_report.buttons = button_mask;\n}\n\nbool MouseReportMatcher::MatchAndExplain(report_mouse_t& report, MatchResultListener* listener) const {\n    return m_report == report;\n}\n\nvoid MouseReportMatcher::DescribeTo(::std::ostream* os) const {\n    *os << \"is equal to \" << m_report;\n}\n\nvoid MouseReportMatcher::DescribeNegationTo(::std::ostream* os) const {\n    *os << \"is not equal to \" << m_report;\n}\n"
  },
  {
    "path": "tests/test_common/mouse_report_util.hpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n#include \"report.h\"\n#include <ostream>\n#include \"gmock/gmock.h\"\n\nbool          operator==(const report_mouse_t& lhs, const report_mouse_t& rhs);\nstd::ostream& operator<<(std::ostream& stream, const report_mouse_t& value);\n\nclass MouseReportMatcher : public testing::MatcherInterface<report_mouse_t&> {\n   public:\n    MouseReportMatcher(int16_t x, int16_t y, int8_t h, int8_t v, uint8_t button_mask);\n    virtual bool MatchAndExplain(report_mouse_t& report, testing::MatchResultListener* listener) const override;\n    virtual void DescribeTo(::std::ostream* os) const override;\n    virtual void DescribeNegationTo(::std::ostream* os) const override;\n\n   private:\n    report_mouse_t m_report;\n};\n\ninline testing::Matcher<report_mouse_t&> MouseReport(int16_t x, int16_t y, int8_t h, int8_t v, uint8_t button_mask) {\n    return testing::MakeMatcher(new MouseReportMatcher(x, y, h, v, button_mask));\n}\n"
  },
  {
    "path": "tests/test_common/pointing_device_driver.c",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"report.h\"\n#include \"test_pointing_device_driver.h\"\n#include <string.h>\n\ntypedef struct {\n    bool pressed;\n    bool dirty;\n} pd_button_state_t;\n\ntypedef struct {\n    int16_t           x;\n    int16_t           y;\n    int16_t           h;\n    int16_t           v;\n    pd_button_state_t button_state[8];\n    uint16_t          cpi;\n    bool              initiated;\n} pd_config_t;\n\nstatic pd_config_t pd_config = {0};\n\nvoid pointing_device_driver_init(void) {\n    pd_set_init(true);\n}\n\nreport_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) {\n    for (uint8_t i = 0; i < 8; i++) {\n        if (pd_config.button_state[i].dirty) {\n            pd_config.button_state[i].dirty = false;\n            if (pd_config.button_state[i].pressed) {\n                mouse_report.buttons |= 1 << (i);\n            } else {\n                mouse_report.buttons &= ~(1 << (i));\n            }\n        }\n    }\n    mouse_report.x = pd_config.x;\n    mouse_report.y = pd_config.y;\n    mouse_report.h = pd_config.h;\n    mouse_report.v = pd_config.v;\n    return mouse_report;\n}\n\n__attribute__((weak)) uint16_t pointing_device_driver_get_cpi(void) {\n    return pd_config.cpi;\n}\n\n__attribute__((weak)) void pointing_device_driver_set_cpi(uint16_t cpi) {\n    pd_config.cpi = cpi;\n}\n\nvoid pd_press_button(uint8_t btn) {\n    pd_config.button_state[btn].dirty   = true;\n    pd_config.button_state[btn].pressed = true;\n}\nvoid pd_release_button(uint8_t btn) {\n    pd_config.button_state[btn].dirty   = true;\n    pd_config.button_state[btn].pressed = false;\n}\n\nvoid pd_clear_all_buttons(void) {\n    for (uint8_t i = 0; i < 8; i++) {\n        pd_config.button_state[i].dirty   = true;\n        pd_config.button_state[i].pressed = false;\n    }\n}\n\nvoid pd_set_x(int16_t x) {\n    pd_config.x = x;\n}\n\nvoid pd_clear_x(void) {\n    pd_set_x(0);\n}\n\nvoid pd_set_y(int16_t y) {\n    pd_config.y = y;\n}\nvoid pd_clear_y(void) {\n    pd_set_y(0);\n}\n\nvoid pd_set_h(int16_t h) {\n    pd_config.h = h;\n}\nvoid pd_clear_h(void) {\n    pd_set_h(0);\n}\n\nvoid pd_set_v(int16_t v) {\n    pd_config.v = v;\n}\nvoid pd_clear_v(void) {\n    pd_set_v(0);\n}\n\nvoid pd_clear_movement(void) {\n    pd_set_x(0);\n    pd_set_y(0);\n    pd_set_h(0);\n    pd_set_v(0);\n}\n\nvoid pd_set_init(bool success) {\n    pd_config.initiated = success;\n}\n"
  },
  {
    "path": "tests/test_common/test_common.h",
    "content": "#pragma once\n\n#define MATRIX_ROWS 4\n#define MATRIX_COLS 10\n"
  },
  {
    "path": "tests/test_common/test_common.hpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"gtest/gtest.h\"\n#include \"gmock/gmock.h\"\n\nextern \"C\" {\n#include \"quantum.h\"\n}\n#include \"test_driver.hpp\"\n#include \"test_matrix.h\"\n#include \"test_keymap_key.hpp\"\n#include \"keyboard_report_util.hpp\"\n#include \"test_fixture.hpp\"\n"
  },
  {
    "path": "tests/test_common/test_driver.cpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"test_driver.hpp\"\n\nTestDriver* TestDriver::m_this = nullptr;\n\nnamespace {\n// Given a hex digit between 0 and 15, returns the corresponding keycode.\nuint8_t hex_digit_to_keycode(uint8_t digit) {\n    // clang-format off\n    static const uint8_t hex_keycodes[] = {\n        KC_0, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7,\n        KC_8, KC_9, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F\n    };\n    // clang-format on\n    return hex_keycodes[digit];\n}\n} // namespace\n\nTestDriver::TestDriver() : m_driver{&TestDriver::keyboard_leds, &TestDriver::send_keyboard, &TestDriver::send_nkro, &TestDriver::send_mouse, &TestDriver::send_extra} {\n    host_set_driver(&m_driver);\n    m_this = this;\n}\n\nTestDriver::~TestDriver() {\n    m_this = nullptr;\n}\n\nuint8_t TestDriver::keyboard_leds(void) {\n    return m_this->m_leds;\n}\n\nvoid TestDriver::send_keyboard(report_keyboard_t* report) {\n    test_logger.trace() << *report;\n    m_this->send_keyboard_mock(*report);\n}\n\nvoid TestDriver::send_nkro(report_nkro_t* report) {\n    m_this->send_nkro_mock(*report);\n}\n\nvoid TestDriver::send_mouse(report_mouse_t* report) {\n    test_logger.trace() << std::setw(10) << std::left << \"send_mouse: (X:\" << (int)report->x << \", Y:\" << (int)report->y << \", H:\" << (int)report->h << \", V:\" << (int)report->v << \", B:\" << (int)report->buttons << \")\" << std::endl;\n    m_this->send_mouse_mock(*report);\n}\n\nvoid TestDriver::send_extra(report_extra_t* report) {\n    m_this->send_extra_mock(*report);\n}\n\nnamespace internal {\nvoid expect_unicode_code_point(TestDriver& driver, uint32_t code_point) {\n    testing::InSequence seq;\n    EXPECT_REPORT(driver, (KC_LEFT_CTRL, KC_LEFT_SHIFT));\n    EXPECT_REPORT(driver, (KC_LEFT_CTRL, KC_LEFT_SHIFT, KC_U));\n    EXPECT_REPORT(driver, (KC_LEFT_CTRL, KC_LEFT_SHIFT));\n    EXPECT_EMPTY_REPORT(driver);\n\n    bool print_zero = false;\n    for (int i = 7; i >= 0; --i) {\n        if (i <= 3) {\n            print_zero = true;\n        }\n\n        const uint8_t digit = (code_point >> (i * 4)) & 0xf;\n        if (digit || print_zero) {\n            EXPECT_REPORT(driver, (hex_digit_to_keycode(digit)));\n            EXPECT_EMPTY_REPORT(driver);\n            print_zero = true;\n        }\n    }\n\n    EXPECT_REPORT(driver, (KC_SPACE));\n    EXPECT_EMPTY_REPORT(driver);\n}\n} // namespace internal\n"
  },
  {
    "path": "tests/test_common/test_driver.hpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include \"gmock/gmock.h\"\n#include <stdint.h>\n#include \"host.h\"\n#include \"keyboard_report_util.hpp\"\nextern \"C\" {\n#include \"keycode_string.h\"\n}\n#include \"test_logger.hpp\"\n\nclass TestDriver {\n   public:\n    TestDriver();\n    ~TestDriver();\n    void set_leds(uint8_t leds) {\n        m_leds = leds;\n    }\n\n    MOCK_METHOD1(send_keyboard_mock, void(report_keyboard_t&));\n    MOCK_METHOD1(send_nkro_mock, void(report_nkro_t&));\n    MOCK_METHOD1(send_mouse_mock, void(report_mouse_t&));\n    MOCK_METHOD1(send_extra_mock, void(report_extra_t&));\n\n   private:\n    static uint8_t     keyboard_leds(void);\n    static void        send_keyboard(report_keyboard_t* report);\n    static void        send_nkro(report_nkro_t* report);\n    static void        send_mouse(report_mouse_t* report);\n    static void        send_extra(report_extra_t* report);\n    host_driver_t      m_driver;\n    uint8_t            m_leds = 0;\n    static TestDriver* m_this;\n};\n\n/**\n * @brief Sets gmock expectation that a keyboard report of `report` keys will be sent.\n * For this macro to parse correctly, the `report` arg must be surrounded by\n * parentheses ( ). For instance,\n *\n *   // Expect that a report of \"KC_LSFT + KC_A\" is sent to the host.\n *   EXPECT_REPORT(driver, (KC_LSFT, KC_A));\n *\n * is shorthand for\n *\n *   EXPECT_CALL(driver, send_keyboard_mock(KeyboardReport(KC_LSFT, KC_A)));\n *\n * It is possible to use .Times() and other gmock APIS with EXPECT_REPORT, for instance,\n * allow only single report to be sent:\n *\n *   EXPECT_REPORT(driver, (KC_LSFT, KC_A)).Times(1);\n */\n#define EXPECT_REPORT(driver, report) EXPECT_CALL((driver), send_keyboard_mock(KeyboardReport report))\n\n/**\n * @brief Sets gmock expectation that a mouse report of `report` will be sent.\n * For this macro to parse correctly, the `report` arg must be surrounded by\n * parentheses ( ). For instance,\n *\n *   // Expect that a report of \"X:-10 Y:0 H:0 V:10 BTN:1 \" is sent to the host.\n *   EXPECT_REPORT(driver, (-10, 0, 0, 0, 1));\n *\n * is shorthand for\n *\n *   EXPECT_CALL(driver, send_mouse_mock(MouseReport(-10, 0, 0, 0, 1)));\n *\n * It is possible to use .Times() and other gmock APIS with EXPECT_REPORT, for instance,\n * allow only single report to be sent:\n *\n *    EXPECT_REPORT(driver, (-10, 0, 0, 0, 1)).Times(1);\n */\n#define EXPECT_MOUSE_REPORT(driver, report) EXPECT_CALL((driver), send_mouse_mock(MouseReport report))\n\n/**\n * @brief Sets gmock expectation that Unicode `code_point` is sent with UNICODE_MODE_LINUX input\n * mode. For instance for U+2013,\n *\n *   EXPECT_UNICODE(driver, 0x2013);\n *\n * expects the sequence of keys:\n *\n *   \"Ctrl+Shift+U, 2, 0, 1, 3, space\".\n */\n#define EXPECT_UNICODE(driver, code_point) internal::expect_unicode_code_point((driver), (code_point))\n\n/**\n * @brief Sets gmock expectation that a empty keyboard report will be sent.\n * It is possible to use .Times() and other gmock APIS with EXPECT_EMPTY_REPORT, for instance,\n * allow any number of empty reports with:\n *\n *   EXPECT_EMPTY_REPORT(driver).Times(AnyNumber());\n */\n#define EXPECT_EMPTY_REPORT(driver) EXPECT_REPORT(driver, ())\n\n/**\n * @brief Sets gmock expectation that a empty keyboard report will be sent.\n * It is possible to use .Times() and other gmock APIS with EXPECT_EMPTY_MOUSE_REPORT, for instance,\n * allow any number of empty reports with:\n *\n *   EXPECT_EMPTY_MOUSE_REPORT(driver).Times(AnyNumber());\n */\n#define EXPECT_EMPTY_MOUSE_REPORT(driver) EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, 0))\n\n/**\n * @brief Sets gmock expectation that a keyboard report will be sent, without matching its content.\n * It is possible to use .Times() and other gmock APIS with EXPECT_ANY_REPORT, for instance,\n * allow a single arbitrary report with:\n *\n *   EXPECT_ANY_REPORT(driver).Times(1);\n */\n#define EXPECT_ANY_REPORT(driver) EXPECT_CALL((driver), send_keyboard_mock(_))\n\n/**\n * @brief Sets gmock expectation that a mouse report will be sent, without matching its content.\n * It is possible to use .Times() and other gmock APIS with EXPECT_ANY_MOUSE_REPORT, for instance,\n * allow a single arbitrary report with:\n *\n *   EXPECT_ANY_MOUSE_REPORT(driver).Times(1);\n */\n#define EXPECT_ANY_MOUSE_REPORT(driver) EXPECT_CALL((driver), send_mouse_mock(_))\n\n/**\n * @brief Sets gmock expectation that no keyboard report will be sent at all.\n */\n#define EXPECT_NO_REPORT(driver) EXPECT_ANY_REPORT(driver).Times(0)\n\n/**\n * @brief Sets gmock expectation that no keyboard report will be sent at all.\n */\n#define EXPECT_NO_MOUSE_REPORT(driver) EXPECT_ANY_MOUSE_REPORT(driver).Times(0)\n\n/** @brief Tests whether keycode `actual` is equal to `expected`. */\n#define EXPECT_KEYCODE_EQ(actual, expected) EXPECT_THAT((actual), KeycodeEq((expected)))\n\nMATCHER_P(KeycodeEq, expected_keycode, \"is equal to \" + testing::PrintToString(expected_keycode) + \", keycode \" + get_keycode_string(expected_keycode)) {\n    if (arg == expected_keycode) {\n        return true;\n    }\n    *result_listener << \"keycode \" << get_keycode_string(arg);\n    return false;\n}\n\n/**\n * @brief Verify and clear all gmock expectations that have been setup until\n * this point.\n */\n#define VERIFY_AND_CLEAR(driver) testing::Mock::VerifyAndClearExpectations(&driver)\n\nnamespace internal {\nvoid expect_unicode_code_point(TestDriver& driver, uint32_t code_point);\n} // namespace internal\n"
  },
  {
    "path": "tests/test_common/test_fixture.cpp",
    "content": "#include \"test_fixture.hpp\"\n#include <algorithm>\n#include <cstdint>\n#include <cstdio>\n#include <cstdlib>\n#include \"gmock/gmock-cardinalities.h\"\n#include \"gmock/gmock.h\"\n#include \"gtest/gtest.h\"\n#include \"keyboard_report_util.hpp\"\n#include \"mouse_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_driver.hpp\"\n#include \"test_logger.hpp\"\n#include \"test_matrix.h\"\n#include \"test_keymap_key.hpp\"\n#include \"timer.h\"\n\nextern \"C\" {\n#include \"action.h\"\n#include \"action_tapping.h\"\n#include \"action_util.h\"\n#include \"action_layer.h\"\n#include \"debug.h\"\n#include \"eeconfig.h\"\n#include \"keyboard.h\"\n\nvoid set_time(uint32_t t);\nvoid advance_time(uint32_t ms);\n}\n\nusing testing::_;\n\n/* This is used for dynamic dispatching keymap_key_to_keycode calls to the current active test_fixture. */\nTestFixture* TestFixture::m_this = nullptr;\n\n/* Override weak QMK function to allow the usage of isolated per-test keymaps in unit-tests.\n * The actual call is dynamicaly dispatched to the current active test fixture, which in turn has it's own keymap. */\nextern \"C\" uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t position) {\n    uint16_t keycode;\n    TestFixture::m_this->get_keycode(layer, position, &keycode);\n    return keycode;\n}\n\nvoid TestFixture::SetUpTestCase() {\n    test_logger.info() << \"test fixture setup-up start.\" << std::endl;\n\n    // The following is enough to bootstrap the values set in main\n    eeconfig_init_quantum();\n    eeconfig_update_debug(&debug_config);\n\n    TestDriver driver;\n    keyboard_init();\n\n    test_logger.info() << \"test fixture setup-up end.\" << std::endl;\n}\n\nvoid TestFixture::TearDownTestCase() {}\n\nTestFixture::TestFixture() {\n    m_this = this;\n    timer_clear();\n    keyrecord_t empty_keyrecord = {0};\n    test_logger.info() << \"tapping term is \" << +GET_TAPPING_TERM(KC_TRANSPARENT, &empty_keyrecord) << \"ms\" << std::endl;\n}\n\nTestFixture::~TestFixture() {\n    test_logger.info() << \"test fixture clean-up start.\" << std::endl;\n    TestDriver driver;\n\n    /* Reset keyboard state. */\n    clear_all_keys();\n\n#ifdef MOUSEKEY_ENABLE\n    EXPECT_EMPTY_MOUSE_REPORT(driver);\n#endif\n    clear_keyboard();\n\n    clear_oneshot_mods();\n    clear_oneshot_locked_mods();\n    reset_oneshot_layer();\n\n    layer_clear();\n\n#if defined(SWAP_HANDS_ENABLE)\n    clear_oneshot_swaphands();\n#endif\n\n    idle_for(TAPPING_TERM * 10);\n    VERIFY_AND_CLEAR(driver);\n\n    /* Verify that the matrix really is cleared */\n    EXPECT_NO_REPORT(driver);\n    idle_for(TAPPING_TERM * 10);\n    VERIFY_AND_CLEAR(driver);\n    m_this = nullptr;\n\n    test_logger.info() << \"test fixture clean-up end.\" << std::endl;\n    print_test_log();\n}\n\nvoid TestFixture::add_key(KeymapKey key) {\n    if (this->find_key(key.layer, key.position)) {\n        FAIL() << \"key is already mapped for layer \" << +key.layer << \" and (column,row) (\" << +key.position.col << \",\" << +key.position.row << \")\";\n    }\n\n    this->keymap.push_back(key);\n}\n\nvoid TestFixture::tap_key(KeymapKey key, unsigned delay_ms) {\n    key.press();\n    idle_for(delay_ms);\n    key.release();\n    run_one_scan_loop();\n}\n\nvoid TestFixture::tap_combo(const std::vector<KeymapKey>& chord_keys, unsigned delay_ms) {\n    for (KeymapKey key : chord_keys) { // Press each key.\n        key.press();\n        run_one_scan_loop();\n    }\n\n    if (delay_ms > 1) {\n        idle_for(delay_ms - 1);\n    }\n\n    for (KeymapKey key : chord_keys) { // Release each key.\n        key.release();\n        run_one_scan_loop();\n    }\n}\n\nvoid TestFixture::set_keymap(std::initializer_list<KeymapKey> keys) {\n    this->keymap.clear();\n    for (auto& key : keys) {\n        add_key(key);\n    }\n}\n\nconst KeymapKey* TestFixture::find_key(layer_t layer, keypos_t position) const {\n    auto keymap_key_predicate = [&](KeymapKey candidate) { return candidate.layer == layer && candidate.position.col == position.col && candidate.position.row == position.row; };\n\n    auto result = std::find_if(this->keymap.begin(), this->keymap.end(), keymap_key_predicate);\n\n    if (result != std::end(this->keymap)) {\n        return &(*result);\n    }\n    return nullptr;\n}\n\nvoid TestFixture::get_keycode(const layer_t layer, const keypos_t position, uint16_t* result) const {\n    bool key_is_out_of_bounds = position.col >= MATRIX_COLS && position.row >= MATRIX_ROWS;\n\n    if (key_is_out_of_bounds) {\n        /* See if this is done in hardware as well, because this is 100% out of bounds reads on all QMK keebs out there. */\n        auto msg = [&]() {\n            std::stringstream msg;\n            msg << \"keycode for position (\" << +position.col << \",\" << +position.row << \") requested! This is out of bounds.\" << std::endl;\n            return msg.str();\n        }();\n\n        *result = KC_NO;\n        test_logger.error() << msg;\n        EXPECT_FALSE(key_is_out_of_bounds) << msg;\n        return;\n    }\n\n    if (auto key = this->find_key(layer, position)) {\n        *result = key->code;\n        return;\n    }\n\n    FAIL() << \"no key is mapped for layer \" << +layer << \" and (column,row) \" << +position.col << \",\" << +position.row << \")\";\n}\n\nvoid TestFixture::run_one_scan_loop() {\n    this->idle_for(1);\n}\n\nvoid TestFixture::idle_for(unsigned time) {\n    test_logger.trace() << +time << \" keyboard task \" << (time > 1 ? \"loops\" : \"loop\") << std::endl;\n    for (unsigned i = 0; i < time; i++) {\n        keyboard_task();\n        housekeeping_task();\n        advance_time(1);\n    }\n}\n\nvoid TestFixture::print_test_log() const {\n    const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();\n    if (HasFailure()) {\n        std::cerr << test_info->test_case_name() << \".\" << test_info->name() << \" failed!\" << std::endl;\n        test_logger.print_header();\n        test_logger.print_log();\n    }\n    test_logger.reset();\n}\n\nvoid TestFixture::expect_layer_state(layer_t layer_state) const {\n    test_logger.trace() << \"layer state: (\" << +layer_state << \") highest layer bit: (\" << +get_highest_layer(layer_state) << \")\" << std::endl;\n    EXPECT_TRUE(layer_state_is(layer_state));\n}\n"
  },
  {
    "path": "tests/test_common/test_fixture.hpp",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <unordered_map>\n#include <optional>\n#include \"gtest/gtest.h\"\n#include \"keyboard.h\"\n#include \"test_keymap_key.hpp\"\n\nclass TestFixture : public testing::Test {\n   public:\n    static TestFixture* m_this;\n\n    TestFixture();\n    ~TestFixture();\n    static void SetUpTestCase();\n    static void TearDownTestCase();\n\n    void set_keymap(std::initializer_list<KeymapKey> keycodes);\n    void add_key(const KeymapKey key);\n\n    const KeymapKey* find_key(const layer_t layer_t, const keypos_t position) const;\n    void             get_keycode(const layer_t layer, const keypos_t position, uint16_t* result) const;\n\n    /**\n     * @brief Taps `key` with `delay_ms` delay between press and release.\n     */\n    void tap_key(KeymapKey key, unsigned delay_ms = 1);\n\n    /**\n     * @brief Taps multiple KeymapKey keys in order, e.g. `tap_keys(key_a, key_b)`.\n     */\n    template <typename... Ts>\n    void tap_keys(Ts... keys) {\n        for (KeymapKey key : {keys...}) {\n            tap_key(key);\n        }\n    }\n\n    /**\n     * @brief Taps a combo with `delay_ms` delay between press and release.\n     *\n     * Example: `tap_combo({key_a, key_b})` to tap the chord A + B.\n     */\n    void tap_combo(const std::vector<KeymapKey>& chord_keys, unsigned delay_ms = 1);\n\n    void run_one_scan_loop();\n    void idle_for(unsigned ms);\n\n    void expect_layer_state(layer_t layer) const;\n\n   protected:\n    void                   print_test_log() const;\n    std::vector<KeymapKey> keymap;\n};\n"
  },
  {
    "path": "tests/test_common/test_keymap_key.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"test_keymap_key.hpp\"\n#include <cstdint>\n#include <ios>\n#include \"matrix.h\"\n#include \"test_logger.hpp\"\n#include \"gtest/gtest-message.h\"\n#include \"gtest/gtest.h\"\n#include \"timer.h\"\n\nvoid KeymapKey::press() {\n    EXPECT_FALSE(matrix_is_on(position.row, position.col)) << \"tried to press key \" << this->name << \" that was already pressed! Check the test code.\" << std::endl;\n\n    press_key(this->position.col, this->position.row);\n    this->timestamp_pressed = timer_read32();\n    test_logger.trace() << std::setw(10) << std::left << \"pressed: \" << this->name << std::endl;\n}\n\nvoid KeymapKey::release() {\n    EXPECT_TRUE(matrix_is_on(this->position.row, this->position.col)) << \"tried to release key \" << this->name << \" that wasn't pressed before! Check the test code.\" << std::endl;\n\n    release_key(this->position.col, this->position.row);\n    uint32_t now = timer_read32();\n    test_logger.trace() << std::setw(10) << std::left << \"released: \" << this->name << \" was pressed for \" << now - this->timestamp_pressed << \"ms\" << std::endl;\n}\n"
  },
  {
    "path": "tests/test_common/test_keymap_key.hpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <string>\nextern \"C\" {\n#include \"keyboard.h\"\n#include \"test_matrix.h\"\n#include \"keycode_string.h\"\n}\n\n#include <cassert>\n\ntypedef uint8_t layer_t;\n\nstruct KeymapKey {\n    KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(keycode), name(get_keycode_string(keycode)) {\n        validate();\n    }\n\n    KeymapKey(layer_t layer, uint8_t col, uint8_t row, uint16_t keycode, uint16_t report_code) : layer(layer), position({.col = col, .row = row}), code(keycode), report_code(report_code), name{get_keycode_string(keycode)} {\n        validate();\n    }\n\n    void press();\n    void release();\n\n    const layer_t  layer;\n    const keypos_t position;\n    const uint16_t code;\n    std::string    name;\n    /* Sometimes the keycode does not match the code that is send in the usb report, so we provide it here. */\n    const uint16_t report_code;\n\n   private:\n    void validate() {\n        assert(position.col <= MATRIX_COLS);\n        assert(position.row <= MATRIX_ROWS);\n    }\n    uint32_t timestamp_pressed;\n};\n"
  },
  {
    "path": "tests/test_common/test_logger.cpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include <iomanip>\n#include <iostream>\n#include \"test_logger.hpp\"\n#include \"timer.h\"\n\nTestLogger test_logger;\n\nTestLogger& TestLogger::info() {\n    *this << \"[ INFO     ] \";\n    return this->timestamp();\n}\n\nTestLogger& TestLogger::trace() {\n    *this << \"[ TRACE    ] \";\n    return this->timestamp();\n}\n\nTestLogger& TestLogger::error() {\n    *this << \"[ ERROR    ] \";\n    return this->timestamp();\n}\n\nTestLogger& TestLogger::timestamp() {\n    *this << std::setw(6) << timer_read32() << \" \";\n    return *this;\n}\nvoid TestLogger::reset() {\n    this->m_log.str(\"\");\n};\n\nvoid TestLogger::print_header() {\n    std::cerr << \"[ LEVEL    ] [TIME] [EVENT]\" << std::endl;\n}\n\nvoid TestLogger::print_log() {\n    std::cerr << this->m_log.str();\n}\n"
  },
  {
    "path": "tests/test_common/test_logger.hpp",
    "content": "/* Copyright 2021 Stefan Kerkmann\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <ostream>\n#include <sstream>\n\nclass TestLogger : public std::ostream {\n   public:\n    TestLogger() : std::ostream(&m_log){};\n    TestLogger& info();\n    TestLogger& trace();\n    TestLogger& error();\n    void        print_log();\n    void        print_header();\n    void        reset();\n\n   private:\n    TestLogger&    timestamp();\n    std::stringbuf m_log;\n};\n\nextern TestLogger test_logger;\n"
  },
  {
    "path": "tests/test_common/test_matrix.h",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid press_key(uint8_t col, uint8_t row);\nvoid release_key(uint8_t col, uint8_t row);\nvoid clear_all_keys(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "tests/test_common/test_pointing_device_driver.h",
    "content": "// Copyright 2024 Dasky (@daskygit)\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid pd_press_button(uint8_t btn);\nvoid pd_release_button(uint8_t btn);\nvoid pd_clear_all_buttons(void);\n\nvoid pd_set_x(int16_t x);\nvoid clear_x(void);\n\nvoid pd_set_y(int16_t y);\nvoid pd_clear_y(void);\n\nvoid pd_set_h(int16_t h);\nvoid pd_clear_h(void);\n\nvoid pd_set_v(int16_t v);\nvoid pd_clear_v(void);\n\nvoid pd_clear_movement(void);\n\nvoid pd_set_init(bool success);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "tests/tri_layer/config.h",
    "content": "// Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n"
  },
  {
    "path": "tests/tri_layer/test.mk",
    "content": "# Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n# SPDX-License-Identifier: GPL-2.0-or-later\n\n# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nTRI_LAYER_ENABLE = yes\n"
  },
  {
    "path": "tests/tri_layer/test_tri_layer.cpp",
    "content": "// Copyright 2021 Christopher Courtney, aka Drashna Jael're  (@drashna) <drashna@live.com>\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"test_common.hpp\"\n\nusing testing::_;\nusing testing::InSequence;\n\nclass TriLayer : public TestFixture {};\n\nTEST_F(TriLayer, TriLayerLowerTest) {\n    TestDriver driver;\n    KeymapKey  lower_layer_key = KeymapKey{0, 0, 0, QK_TRI_LAYER_LOWER};\n\n    set_keymap({lower_layer_key, KeymapKey{1, 0, 0, KC_TRNS}});\n\n    /* Press Lower. */\n    EXPECT_NO_REPORT(driver);\n    lower_layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(get_tri_layer_lower_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release Lower. */\n    EXPECT_NO_REPORT(driver);\n    lower_layer_key.release();\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(TriLayer, TriLayerUpperTest) {\n    TestDriver driver;\n    KeymapKey  upper_layer_key = KeymapKey{0, 0, 0, QK_TRI_LAYER_UPPER};\n\n    set_keymap({upper_layer_key, KeymapKey{2, 0, 0, KC_TRNS}});\n\n    /* Press Raise. */\n    EXPECT_NO_REPORT(driver);\n    upper_layer_key.press();\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer()));\n    EXPECT_TRUE(layer_state_is(get_tri_layer_upper_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));\n    VERIFY_AND_CLEAR(driver);\n\n    /* Release Raise. */\n    EXPECT_NO_REPORT(driver);\n    upper_layer_key.release();\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(TriLayer, TriLayerAdjustTest) {\n    TestDriver driver;\n    KeymapKey  lower_layer_key = KeymapKey{0, 0, 0, QK_TRI_LAYER_LOWER};\n    KeymapKey  upper_layer_key = KeymapKey{0, 1, 0, QK_TRI_LAYER_UPPER};\n\n    set_keymap({\n        upper_layer_key,\n        lower_layer_key,\n        KeymapKey{1, 0, 0, KC_TRNS},\n        KeymapKey{1, 1, 0, KC_TRNS},\n        KeymapKey{2, 0, 0, KC_TRNS},\n        KeymapKey{2, 1, 0, KC_TRNS},\n        KeymapKey{3, 0, 0, KC_TRNS},\n        KeymapKey{3, 1, 0, KC_TRNS},\n    });\n\n    /* Press Lower, then upper, and release upper and then lower. */\n    EXPECT_NO_REPORT(driver);\n    lower_layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(get_tri_layer_lower_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));\n\n    upper_layer_key.press();\n    run_one_scan_loop();\n    EXPECT_TRUE(layer_state_is(get_tri_layer_lower_layer()));\n    EXPECT_TRUE(layer_state_is(get_tri_layer_upper_layer()));\n    EXPECT_TRUE(layer_state_is(get_tri_layer_adjust_layer()));\n\n    lower_layer_key.release();\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer()));\n    EXPECT_TRUE(layer_state_is(get_tri_layer_upper_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));\n\n    upper_layer_key.release();\n    run_one_scan_loop();\n    EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer()));\n    EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer()));\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/unicode/config.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX, UNICODE_MODE_MACOS\n"
  },
  {
    "path": "tests/unicode/test.mk",
    "content": "# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nUNICODE_COMMON = yes\n"
  },
  {
    "path": "tests/unicode/test_unicode.cpp",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\n\nclass Unicode : public TestFixture {};\n\nTEST_F(Unicode, sends_bmp_unicode_sequence) {\n    TestDriver driver;\n\n    set_unicode_input_mode(UNICODE_MODE_LINUX);\n\n    EXPECT_UNICODE(driver, 0x03A8); // Ψ\n    register_unicode(0x03A8);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Unicode, sends_smp_unicode_sequence) {\n    TestDriver driver;\n\n    set_unicode_input_mode(UNICODE_MODE_LINUX);\n\n    EXPECT_UNICODE(driver, 0x1F9D9); // 🧙\n    register_unicode(0x1F9D9);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Unicode, sends_surrogate_pair_for_macos) {\n    TestDriver driver;\n\n    set_unicode_input_mode(UNICODE_MODE_MACOS);\n\n    // EXPECT_UNICODE() assumes Linux input mode\n    {\n        testing::InSequence s;\n\n        // Alt+D83EDDD9 🧙\n        EXPECT_REPORT(driver, (KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_8, KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_3, KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_E, KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_D, KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_9, KC_LEFT_ALT));\n        EXPECT_REPORT(driver, (KC_LEFT_ALT));\n        EXPECT_EMPTY_REPORT(driver);\n    }\n\n    register_unicode(0x1F9D9);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(Unicode, sends_unicode_string) {\n    TestDriver driver;\n\n    set_unicode_input_mode(UNICODE_MODE_LINUX);\n\n    {\n        testing::InSequence s;\n\n        EXPECT_UNICODE(driver, 0xFF31);\n        EXPECT_UNICODE(driver, 0xFF2D);\n        EXPECT_UNICODE(driver, 0xFF2B);\n        EXPECT_UNICODE(driver, 0xFF01);\n    }\n    send_unicode_string(\"ＱＭＫ！\");\n\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/unicode/unicode_basic/config.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX, UNICODE_MODE_MACOS\n"
  },
  {
    "path": "tests/unicode/unicode_basic/test.mk",
    "content": "# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nUNICODE_ENABLE = yes\n"
  },
  {
    "path": "tests/unicode/unicode_basic/test_unicode_basic.cpp",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\n\nclass UnicodeBasic : public TestFixture {};\n\nTEST_F(UnicodeBasic, sends_unicode_sequence) {\n    TestDriver driver;\n\n    set_unicode_input_mode(UNICODE_MODE_LINUX);\n\n    auto key_uc = KeymapKey(0, 0, 0, UC(0x03A8)); // Ψ\n\n    set_keymap({key_uc});\n\n    EXPECT_UNICODE(driver, 0x03A8);\n    tap_key(key_uc);\n\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/unicode/unicode_map/config.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX\n"
  },
  {
    "path": "tests/unicode/unicode_map/test.mk",
    "content": "# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nUNICODEMAP_ENABLE = yes\n"
  },
  {
    "path": "tests/unicode/unicode_map/test_unicode_map.cpp",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\n\nconst uint32_t PROGMEM unicode_map[] = {\n    0x03A8, // Ψ\n    0x2318  // ⌘\n};\n\nclass UnicodeMap : public TestFixture {};\n\nTEST_F(UnicodeMap, sends_unicodemap_code_point_from_keycode) {\n    TestDriver driver;\n\n    auto key_um = KeymapKey(0, 0, 0, UM(0));\n\n    set_keymap({key_um});\n\n    EXPECT_UNICODE(driver, 0x03A8);\n    tap_key(key_um);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(UnicodeMap, sends_unicodemap_pair_from_keycode) {\n    TestDriver driver;\n\n    auto key_shift = KeymapKey(0, 0, 0, KC_LEFT_SHIFT);\n    auto key_up    = KeymapKey(0, 1, 0, UP(0, 1));\n\n    set_keymap({key_shift, key_up});\n\n    EXPECT_UNICODE(driver, 0x03A8);\n    tap_key(key_up);\n\n    EXPECT_REPORT(driver, (KC_LEFT_SHIFT));\n    key_shift.press();\n    run_one_scan_loop();\n\n    EXPECT_UNICODE(driver, 0x2318);\n    tap_key(key_up);\n\n    EXPECT_NO_REPORT(driver);\n    key_shift.release();\n    run_one_scan_loop();\n\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tests/unicode/unicode_ucis/config.h",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"test_common.h\"\n\n#define UNICODE_SELECTED_MODES UNICODE_MODE_LINUX\n"
  },
  {
    "path": "tests/unicode/unicode_ucis/test.mk",
    "content": "# --------------------------------------------------------------------------------\n# Keep this file, even if it is empty, as a marker that this folder contains tests\n# --------------------------------------------------------------------------------\n\nUCIS_ENABLE = yes\n"
  },
  {
    "path": "tests/unicode/unicode_ucis/test_unicode_ucis.cpp",
    "content": "// Copyright 2023 QMK\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#include \"keyboard_report_util.hpp\"\n#include \"keycode.h\"\n#include \"test_common.hpp\"\n#include \"test_keymap_key.hpp\"\n\nusing testing::_;\n\n// clang-format off\nconst ucis_symbol_t ucis_symbol_table[] = UCIS_TABLE(\n    UCIS_SYM(\"qmk\", 0x03A8) // Ψ\n);\n// clang-format on\n\nclass UnicodeUCIS : public TestFixture {};\n\nTEST_F(UnicodeUCIS, matches_sequence) {\n    TestDriver driver;\n\n    auto key_q     = KeymapKey(0, 0, 0, KC_Q);\n    auto key_m     = KeymapKey(0, 1, 0, KC_M);\n    auto key_k     = KeymapKey(0, 2, 0, KC_K);\n    auto key_enter = KeymapKey(0, 3, 0, KC_ENTER);\n\n    set_keymap({key_q, key_m, key_k, key_enter});\n\n    EXPECT_UNICODE(driver, 0x2328); // ⌨\n    ucis_start();\n\n    EXPECT_EQ(ucis_active(), true);\n    EXPECT_EQ(ucis_count(), 0);\n\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_q);\n    EXPECT_EQ(ucis_count(), 1);\n\n    EXPECT_REPORT(driver, (KC_M));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_m);\n    EXPECT_EQ(ucis_count(), 2);\n\n    EXPECT_REPORT(driver, (KC_K));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_k);\n    EXPECT_EQ(ucis_count(), 3);\n\n    EXPECT_REPORT(driver, (KC_BACKSPACE)).Times(4);\n    EXPECT_EMPTY_REPORT(driver).Times(4);\n    EXPECT_UNICODE(driver, 0x03A8);\n    tap_key(key_enter);\n\n    EXPECT_EQ(ucis_active(), false);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(UnicodeUCIS, cancels_sequence) {\n    TestDriver driver;\n\n    auto key_q      = KeymapKey(0, 0, 0, KC_Q);\n    auto key_m      = KeymapKey(0, 1, 0, KC_M);\n    auto key_k      = KeymapKey(0, 2, 0, KC_K);\n    auto key_escape = KeymapKey(0, 3, 0, KC_ESCAPE);\n\n    set_keymap({key_q, key_m, key_k, key_escape});\n\n    EXPECT_UNICODE(driver, 0x2328); // ⌨\n    ucis_start();\n\n    EXPECT_EQ(ucis_active(), true);\n    EXPECT_EQ(ucis_count(), 0);\n\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_q);\n    EXPECT_EQ(ucis_count(), 1);\n\n    EXPECT_REPORT(driver, (KC_M));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_m);\n    EXPECT_EQ(ucis_count(), 2);\n\n    EXPECT_REPORT(driver, (KC_K));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_k);\n    EXPECT_EQ(ucis_count(), 3);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_escape);\n\n    EXPECT_EQ(ucis_active(), false);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(UnicodeUCIS, matches_sequence_with_corrected_typo) {\n    TestDriver driver;\n\n    auto key_q         = KeymapKey(0, 0, 0, KC_Q);\n    auto key_m         = KeymapKey(0, 1, 0, KC_M);\n    auto key_j         = KeymapKey(0, 2, 0, KC_J);\n    auto key_k         = KeymapKey(0, 3, 0, KC_K);\n    auto key_backspace = KeymapKey(0, 4, 0, KC_BACKSPACE);\n    auto key_enter     = KeymapKey(0, 5, 0, KC_ENTER);\n\n    set_keymap({key_q, key_m, key_j, key_k, key_backspace, key_enter});\n\n    EXPECT_UNICODE(driver, 0x2328); // ⌨\n    ucis_start();\n\n    EXPECT_EQ(ucis_active(), true);\n    EXPECT_EQ(ucis_count(), 0);\n\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_q);\n    EXPECT_EQ(ucis_count(), 1);\n\n    EXPECT_REPORT(driver, (KC_M));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_m);\n    EXPECT_EQ(ucis_count(), 2);\n\n    EXPECT_REPORT(driver, (KC_J));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_j);\n    EXPECT_EQ(ucis_count(), 3);\n\n    EXPECT_REPORT(driver, (KC_BACKSPACE));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_backspace);\n    EXPECT_EQ(ucis_count(), 2);\n\n    EXPECT_REPORT(driver, (KC_K));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_k);\n    EXPECT_EQ(ucis_count(), 3);\n\n    EXPECT_REPORT(driver, (KC_BACKSPACE)).Times(4);\n    EXPECT_EMPTY_REPORT(driver).Times(4);\n    EXPECT_UNICODE(driver, 0x03A8);\n    tap_key(key_enter);\n\n    EXPECT_EQ(ucis_active(), false);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(UnicodeUCIS, does_not_match_longer_sequence) {\n    TestDriver driver;\n\n    auto key_q     = KeymapKey(0, 0, 0, KC_Q);\n    auto key_m     = KeymapKey(0, 1, 0, KC_M);\n    auto key_k     = KeymapKey(0, 2, 0, KC_K);\n    auto key_enter = KeymapKey(0, 3, 0, KC_ENTER);\n\n    set_keymap({key_q, key_m, key_k, key_enter});\n\n    EXPECT_UNICODE(driver, 0x2328); // ⌨\n    ucis_start();\n\n    EXPECT_EQ(ucis_active(), true);\n    EXPECT_EQ(ucis_count(), 0);\n\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_q);\n    EXPECT_EQ(ucis_count(), 1);\n\n    EXPECT_REPORT(driver, (KC_M));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_m);\n    EXPECT_EQ(ucis_count(), 2);\n\n    EXPECT_REPORT(driver, (KC_K));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_k);\n    EXPECT_EQ(ucis_count(), 3);\n\n    EXPECT_REPORT(driver, (KC_K));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_k);\n    EXPECT_EQ(ucis_count(), 4);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_enter);\n\n    EXPECT_EQ(ucis_active(), false);\n\n    VERIFY_AND_CLEAR(driver);\n}\n\nTEST_F(UnicodeUCIS, does_not_match_shorter_sequence) {\n    TestDriver driver;\n\n    auto key_q     = KeymapKey(0, 0, 0, KC_Q);\n    auto key_enter = KeymapKey(0, 1, 0, KC_ENTER);\n\n    set_keymap({key_q, key_enter});\n\n    EXPECT_UNICODE(driver, 0x2328); // ⌨\n    ucis_start();\n\n    EXPECT_EQ(ucis_active(), true);\n    EXPECT_EQ(ucis_count(), 0);\n\n    EXPECT_REPORT(driver, (KC_Q));\n    EXPECT_EMPTY_REPORT(driver);\n    tap_key(key_q);\n    EXPECT_EQ(ucis_count(), 1);\n\n    EXPECT_NO_REPORT(driver);\n    tap_key(key_enter);\n\n    EXPECT_EQ(ucis_active(), false);\n\n    VERIFY_AND_CLEAR(driver);\n}\n"
  },
  {
    "path": "tmk_core/protocol/chibios/README.md",
    "content": "## TMK running on top of ChibiOS\n\nThis code can be used to run TMK keyboard logic on top of [ChibiOS], meaning that you can run TMK on whatever [ChibiOS] supports. The notable examples are ARM-based Teensies (3.x and LC) and on the boards with STM32 MCUs.\n\n### Usage\n\n- To use, [get a zip of chibios](https://github.com/ChibiOS/ChibiOS/archive/a7df9a891067621e8e1a5c2a2c0ceada82403afe.zip) and unpack/rename it to `tmk_core/tool/chibios/chibios`; or you can just clone [the repo](https://github.com/ChibiOS/ChibiOS) there. For Freescale/NXP Kinetis support (meaning ARM Teensies and the Infinity keyboard), you'll also need [a zip of chibios-contrib](https://github.com/ChibiOS/ChibiOS-Contrib/archive/e1311c4db6cd366cf760673f769e925741ac0ad3.zip), unpacked/renamed to `tmk_core/tool/chibios/chibios-contrib`. Likewise, for git-savvy people, just clone [the repo](https://github.com/ChibiOS/ChibiOS-Contrib) there.\n- Note: the abovementioned directories are the defaults. You can have the two chibios repositories wherever you want, just define their location in `CHIBIOS` and `CHIBIOS_CONTRIB` variables in your `Makefile`.\n- You will also need to install an ARM toolchain, for instance from [here](https://launchpad.net/gcc-arm-embedded). On linux, this is usually also present as a package for your distribution (as `gcc-arm` or something similar). On OS X, you can use [homebrew](http://brew.sh/) with an appropriate tap.\n\n### Notes\n\n- Some comments about ChibiOS syntax and the most commonly used GPIO functions are, as well as an example for ARM Teensies, is [here](https://github.com/tmk/tmk_keyboard/blob/master/keyboard/teensy_lc_onekey/instructions.md).\n- For gcc options, inspect `tmk_core/tool/chibios/chibios.mk`. For instance, I enabled `-Wno-missing-field-initializers`, because TMK common bits generated a lot of warnings on that.\n- For debugging, it is sometimes useful disable gcc optimisations, you can do that by adding `-O0` to `OPT_DEFS` in your `Makefile`.\n- USB string descriptors are messy. I did not find a way to cleanly generate the right structures from actual strings, so the definitions in individual keyboards' `config.h` are ugly as heck.\n- It is easy to add some code for testing (e.g. blink LED, do stuff on button press, etc...) - just create another thread in `main.c`, it will run independently of the keyboard business.\n- Jumping to (the built-in) bootloaders on STM32 works, but it is not entirely pleasant, since it is very much MCU dependent. So, one needs to dig out the right address to jump to, and either pass it to the compiler in the `Makefile`, or better, define it in `<your_kb>/bootloader_defs.h`. An additional startup code is also needed; the best way to deal with this is to define custom board files. (Example forthcoming.) In any case, there are no problems for Teensies.\n\n\n### Immediate todo\n\n- power saving for suspend\n\n### Not tested, but possibly working\n\n- backlight\n\n### Missing / not working (TMK vs ChibiOS bits)\n\n- eeprom / bootmagic for STM32 (will be chip dependent; eeprom needs to be emulated in flash, which means less writes; wear-levelling?) There is a semi-official ST \"driver\" for eeprom, with wear-levelling, but I think it consumes a lot of RAM (like 2 pages, i.e. 1kB or so).\n\n### Tried with\n\n- Infinity, WhiteFox keyboards\n- all ARM-based Teensies\n- some STM32-based boards (e.g. ST-F072RB-DISCOVERY board, STM32F042 breakout board, Maple Mini (STM32F103-based))\n\n## ChibiOS-supported MCUs\n\n- Pretty much all STM32 chips.\n- K20x and KL2x Freescale/NXP chips (i.e. Teensy 3.x/LC, mchck, FRDM-KL2{5,6}Z, FRDM-K20D50M), via the [ChibiOS-Contrib](https://github.com/ChibiOS/ChibiOS-Contrib) repository.\n- There is also support for AVR8, but the USB stack is not implemented for them yet (some news on that front recently though), and also the kernel itself takes about 1k of RAM. I think people managed to get ChibiOS running on atmega32[8p/u4] though.\n- There is also support for Nordic NRF51822 (the chip in Adafruit's Bluefruit bluetooth-low-energy boards), but be aware that that chip does *not* have USB, and the BLE softdevice (i.e. Bluetooth) is not supported directly at the moment.\n\n## STM32-based keyboard design considerations\n\n- STM32F0x2 chips can do crystal-less USB, but they still need a 3.3V voltage regulator.\n- The BOOT0 pin should be tied to GND.\n- For a hardware way of accessing the in-built DFU bootloader, in addition to the reset button, put another button between the BOOT0 pin and 3V3.\n- There is a working example of a STM32F042-based keyboard: [firmware here](https://github.com/flabbergast/flabber_kbs/tree/master/kb45p) and [hardware (kicad) here](https://github.com/flabbergast/kicad/tree/master/kb45p). You can check this example firmware for custom board files, and a more complicated matrix than just one key.\n\n\n\n[ChibiOS]: http://chibios.org\n"
  },
  {
    "path": "tmk_core/protocol/chibios/chibios.c",
    "content": "/*\n * (c) 2015 flabberast <s3+flabbergast@sdfeu.org>\n *\n * Based on the following work:\n *  - Guillaume Duc's raw hid example (MIT License)\n *    https://github.com/guiduc/usb-hid-chibios-example\n *  - PJRC Teensy examples (MIT License)\n *    https://www.pjrc.com/teensy/usb_keyboard.html\n *  - hasu's TMK keyboard code (GPL v2 and some code Modified BSD)\n *    https://github.com/tmk/tmk_keyboard/\n *  - ChibiOS demo code (Apache 2.0 License)\n *    http://www.chibios.org\n *\n * Since some GPL'd code is used, this work is licensed under\n * GPL v2 or later.\n */\n\n#include <ch.h>\n#include <hal.h>\n\n#include \"usb_main.h\"\n\n/* TMK includes */\n#include \"report.h\"\n#include \"host.h\"\n#include \"host_driver.h\"\n#include \"keyboard.h\"\n#include \"action.h\"\n#include \"action_util.h\"\n#include \"usb_device_state.h\"\n#include \"mousekey.h\"\n#include \"led.h\"\n#include \"sendchar.h\"\n#include \"debug.h\"\n#include \"print.h\"\n\n#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n// Change this to be TRUE once we've migrated keyboards to the new init system\n// Remember to change docs/platformdev_chibios_earlyinit.md as well.\n#    define EARLY_INIT_PERFORM_BOOTLOADER_JUMP FALSE\n#endif\n\n#ifdef SLEEP_LED_ENABLE\n#    include \"sleep_led.h\"\n#endif\n#ifdef MIDI_ENABLE\n#    include \"qmk_midi.h\"\n#endif\n#include \"suspend.h\"\n#include \"wait.h\"\n\n#define USB_GETSTATUS_REMOTE_WAKEUP_ENABLED (2U)\n\n#ifdef WAIT_FOR_USB\n// TODO: Remove backwards compatibility with old define\n#    define USB_WAIT_FOR_ENUMERATION\n#endif\n\n/* -------------------------\n *   TMK host driver defs\n * -------------------------\n */\n\n/* declarations */\nvoid send_keyboard(report_keyboard_t *report);\nvoid send_nkro(report_nkro_t *report);\nvoid send_mouse(report_mouse_t *report);\nvoid send_extra(report_extra_t *report);\nvoid send_raw_hid(uint8_t *data, uint8_t length);\n\n/* host struct */\nhost_driver_t chibios_driver = {\n    .keyboard_leds = usb_device_state_get_leds,\n    .send_keyboard = send_keyboard,\n    .send_nkro     = send_nkro,\n    .send_mouse    = send_mouse,\n    .send_extra    = send_extra,\n#ifdef RAW_ENABLE\n    .send_raw_hid = send_raw_hid,\n#endif\n};\n\n#ifdef VIRTSER_ENABLE\nvoid virtser_task(void);\n#endif\n\n/* TESTING\n * Amber LED blinker thread, times are in milliseconds.\n */\n/* set this variable to non-zero anywhere to blink once */\n// static THD_WORKING_AREA(waThread1, 128);\n// static THD_FUNCTION(Thread1, arg) {\n\n//   (void)arg;\n//   chRegSetThreadName(\"blinker\");\n//   while (true) {\n//     systime_t time;\n\n//     time = USB_DRIVER.state == USB_ACTIVE ? 250 : 500;\n//     palClearLine(LINE_CAPS_LOCK);\n//     chSysPolledDelayX(MS2RTC(STM32_HCLK, time));\n//     palSetLine(LINE_CAPS_LOCK);\n//     chSysPolledDelayX(MS2RTC(STM32_HCLK, time));\n//   }\n// }\n\n/* Early initialisation\n */\n__attribute__((weak)) void early_hardware_init_pre(void) {\n#if EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n    void enter_bootloader_mode_if_requested(void);\n    enter_bootloader_mode_if_requested();\n#endif // EARLY_INIT_PERFORM_BOOTLOADER_JUMP\n}\n\n__attribute__((weak)) void early_hardware_init_post(void) {}\n\n__attribute__((weak)) void board_init(void) {}\n\n// This overrides what's normally in ChibiOS board definitions\nvoid __early_init(void) {\n    early_hardware_init_pre();\n\n    // This is the renamed equivalent of __early_init in the board.c file\n    void __chibios_override___early_init(void);\n    __chibios_override___early_init();\n\n    early_hardware_init_post();\n}\n\n// This overrides what's normally in ChibiOS board definitions\nvoid boardInit(void) {\n    // This is the renamed equivalent of boardInit in the board.c file\n    void __chibios_override_boardInit(void);\n    __chibios_override_boardInit();\n\n    board_init();\n}\n\nvoid protocol_setup(void) {\n    usb_device_state_init();\n\n    // TESTING\n    // chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);\n}\n\nvoid protocol_pre_init(void) {\n    /* Init USB */\n    usb_event_queue_init();\n    init_usb_driver(&USB_DRIVER);\n\n#ifdef MIDI_ENABLE\n    setup_midi();\n#endif\n\n    /* Wait until USB is active */\n#ifdef USB_WAIT_FOR_ENUMERATION\n    while (USB_DRIVER.state != USB_ACTIVE) {\n        wait_ms(50);\n    }\n#endif\n\n    /* Do need to wait here!\n     * Otherwise the next print might start a transfer on console EP\n     * before the USB is completely ready, which sometimes causes\n     * HardFaults.\n     */\n    wait_ms(50);\n\n    print(\"USB configured.\\n\");\n}\n\nvoid protocol_post_init(void) {\n    host_set_driver(&chibios_driver);\n}\n\nvoid protocol_pre_task(void) {\n    usb_event_queue_task();\n\n#if !defined(NO_USB_STARTUP_CHECK)\n    if (USB_DRIVER.state == USB_SUSPENDED) {\n        dprintln(\"suspending keyboard\");\n        while (USB_DRIVER.state == USB_SUSPENDED) {\n            /* Do this in the suspended state */\n            suspend_power_down(); // on AVR this deep sleeps for 15ms\n            /* Remote wakeup */\n            if (suspend_wakeup_condition() && (USB_DRIVER.status & USB_GETSTATUS_REMOTE_WAKEUP_ENABLED)) {\n                usbWakeupHost(&USB_DRIVER);\n#    if USB_SUSPEND_WAKEUP_DELAY > 0\n                // Some hubs, kvm switches, and monitors do\n                // weird things, with USB device state bouncing\n                // around wildly on wakeup, yielding race\n                // conditions that can corrupt the keyboard state.\n                //\n                // Pause for a while to let things settle...\n                wait_ms(USB_SUSPEND_WAKEUP_DELAY);\n#    endif\n            }\n        }\n        /* Woken up */\n    }\n#endif\n}\n\nvoid protocol_post_task(void) {\n#ifdef VIRTSER_ENABLE\n    virtser_task();\n#endif\n    usb_idle_task();\n}\n"
  },
  {
    "path": "tmk_core/protocol/chibios/chibios.mk",
    "content": "PROTOCOL_DIR = protocol\nCHIBIOS_DIR = $(PROTOCOL_DIR)/chibios\n\n\nSRC += $(CHIBIOS_DIR)/usb_main.c\nSRC += $(CHIBIOS_DIR)/chibios.c\nSRC += usb_descriptor.c\nSRC += $(CHIBIOS_DIR)/usb_driver.c\nSRC += $(CHIBIOS_DIR)/usb_endpoints.c\nSRC += $(CHIBIOS_DIR)/usb_report_handling.c\nSRC += $(CHIBIOS_DIR)/usb_util.c\nSRC += $(LIBSRC)\n\nVPATH += $(TMK_PATH)/$(PROTOCOL_DIR)\nVPATH += $(TMK_PATH)/$(CHIBIOS_DIR)\nVPATH += $(TMK_PATH)/$(CHIBIOS_DIR)/lufa_utils\n\nOPT_DEFS += -DFIXED_CONTROL_ENDPOINT_SIZE=64\nOPT_DEFS += -DFIXED_NUM_CONFIGURATIONS=1\n"
  },
  {
    "path": "tmk_core/protocol/chibios/init_hooks.h",
    "content": "#pragma once\n\n// Override the initialisation functions inside the ChibiOS board.c files\n#define __early_init __chibios_override___early_init\n#define boardInit __chibios_override_boardInit\n"
  },
  {
    "path": "tmk_core/protocol/chibios/lufa_utils/LUFA/Drivers/USB/USB.h",
    "content": "#include \"progmem.h\"\n#include <stddef.h>\n#include <inttypes.h>\n\n#define ATTR_PACKED __attribute__((packed))\n/** Concatenates the given input into a single token, via the C Preprocessor.\n *\n *  \\param[in] x  First item to concatenate.\n *  \\param[in] y  Second item to concatenate.\n *\n *  \\return Concatenated version of the input.\n */\n#define CONCAT(x, y) x##y\n\n/** CConcatenates the given input into a single token after macro expansion, via the C Preprocessor.\n *\n *  \\param[in] x  First item to concatenate.\n *  \\param[in] y  Second item to concatenate.\n *\n *  \\return Concatenated version of the expanded input.\n */\n#define CONCAT_EXPANDED(x, y) CONCAT(x, y)\n#define CPU_TO_LE16(x) (x)\n\n// We don't need anything from the following files, or we have defined it already\n#define __LUFA_COMMON_H__\n#define __USBMODE_H__\n#define __USBEVENTS_H__\n#define __HIDPARSER_H__\n#define __USBCONTROLLER_AVR8_H__\n\n#define __INCLUDE_FROM_USB_DRIVER\n#define __INCLUDE_FROM_HID_DRIVER\n#define __INCLUDE_FROM_CDC_DRIVER\n#define __INCLUDE_FROM_AUDIO_DRIVER\n#define __INCLUDE_FROM_MIDI_DRIVER\n#include \"lib/lufa/LUFA/Drivers/USB/Class/Common/HIDClassCommon.h\"\n#include \"lib/lufa/LUFA/Drivers/USB/Class/Common/HIDReportData.h\"\n#include \"lib/lufa/LUFA/Drivers/USB/Class/Common/CDCClassCommon.h\"\n#include \"lib/lufa/LUFA/Drivers/USB/Class/Common/AudioClassCommon.h\"\n#include \"lib/lufa/LUFA/Drivers/USB/Class/Common/MIDIClassCommon.h\"\n#include \"lib/lufa/LUFA/Drivers/USB/Core/USBController.h\"\n"
  },
  {
    "path": "tmk_core/protocol/chibios/usb_driver.c",
    "content": "// Copyright 2023 Stefan Kerkmann (@KarlK90)\n// Copyright 2021 Purdea Andrei\n// Copyright 2021 Michael Stapelberg\n// Copyright 2020 Ryan (@fauxpark)\n// Copyright 2016 Fredizzimo\n// Copyright 2016 Giovanni Di Sirio\n// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0\n\n#include <hal.h>\n#include <string.h>\n\n#include \"usb_driver.h\"\n#include \"util.h\"\n\n/*===========================================================================*/\n/* Driver local functions.                                                   */\n/*===========================================================================*/\n\nstatic void usb_start_receive(usb_endpoint_out_t *endpoint) {\n    /* If the USB driver is not in the appropriate state then transactions\n       must not be started.*/\n    if ((usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE)) {\n        return;\n    }\n\n    /* Checking if there is already a transaction ongoing on the endpoint.*/\n    if (usbGetReceiveStatusI(endpoint->config.usbp, endpoint->config.ep)) {\n        return;\n    }\n\n    /* Checking if there is a buffer ready for incoming data.*/\n    uint8_t *buffer = ibqGetEmptyBufferI(&endpoint->ibqueue);\n    if (buffer == NULL) {\n        return;\n    }\n\n    /* Buffer found, starting a new transaction.*/\n    usbStartReceiveI(endpoint->config.usbp, endpoint->config.ep, buffer, endpoint->ibqueue.bsize - sizeof(size_t));\n}\n\n/**\n * @brief   Notification of empty buffer released into the input buffers queue.\n *\n * @param[in] bqp       the buffers queue pointer.\n */\nstatic void ibnotify(io_buffers_queue_t *bqp) {\n    usb_endpoint_out_t *endpoint = bqGetLinkX(bqp);\n    usb_start_receive(endpoint);\n}\n\n/**\n * @brief   Notification of filled buffer inserted into the output buffers queue.\n *\n * @param[in] bqp       the buffers queue pointer.\n */\nstatic void obnotify(io_buffers_queue_t *bqp) {\n    usb_endpoint_in_t *endpoint = bqGetLinkX(bqp);\n\n    /* If the USB endpoint is not in the appropriate state then transactions\n       must not be started.*/\n    if ((usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE)) {\n        return;\n    }\n\n    /* Checking if there is already a transaction ongoing on the endpoint.*/\n    if (!usbGetTransmitStatusI(endpoint->config.usbp, endpoint->config.ep)) {\n        /* Trying to get a full buffer.*/\n        size_t   n;\n        uint8_t *buffer = obqGetFullBufferI(&endpoint->obqueue, &n);\n        if (buffer != NULL) {\n            /* Buffer found, starting a new transaction.*/\n            usbStartTransmitI(endpoint->config.usbp, endpoint->config.ep, buffer, n);\n        }\n    }\n}\n\n/*===========================================================================*/\n/* Driver exported functions.                                                */\n/*===========================================================================*/\n\nvoid usb_endpoint_in_init(usb_endpoint_in_t *endpoint) {\n    usb_endpoint_config_t *config = &endpoint->config;\n    endpoint->ep_config.in_state  = &endpoint->ep_in_state;\n\n#if defined(USB_ENDPOINTS_ARE_REORDERABLE)\n    if (endpoint->is_shared) {\n        endpoint->ep_config.out_state = &endpoint->ep_out_state;\n    }\n#endif\n    obqObjectInit(&endpoint->obqueue, true, config->buffer, config->buffer_size, config->buffer_capacity, obnotify, endpoint);\n}\n\nvoid usb_endpoint_out_init(usb_endpoint_out_t *endpoint) {\n    usb_endpoint_config_t *config = &endpoint->config;\n    endpoint->ep_config.out_state = &endpoint->ep_out_state;\n    ibqObjectInit(&endpoint->ibqueue, true, config->buffer, config->buffer_size, config->buffer_capacity, ibnotify, endpoint);\n}\n\nvoid usb_endpoint_in_start(usb_endpoint_in_t *endpoint) {\n    osalDbgCheck(endpoint != NULL);\n\n    osalSysLock();\n    osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), \"invalid state\");\n    endpoint->config.usbp->in_params[endpoint->config.ep - 1U] = endpoint;\n    endpoint->timed_out                                        = false;\n    osalSysUnlock();\n}\n\nvoid usb_endpoint_out_start(usb_endpoint_out_t *endpoint) {\n    osalDbgCheck(endpoint != NULL);\n\n    osalSysLock();\n    osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), \"invalid state\");\n    endpoint->config.usbp->out_params[endpoint->config.ep - 1U] = endpoint;\n    endpoint->timed_out                                         = false;\n    osalSysUnlock();\n}\n\nvoid usb_endpoint_in_stop(usb_endpoint_in_t *endpoint) {\n    osalDbgCheck(endpoint != NULL);\n\n    osalSysLock();\n    endpoint->config.usbp->in_params[endpoint->config.ep - 1U] = NULL;\n\n    bqSuspendI(&endpoint->obqueue);\n    obqResetI(&endpoint->obqueue);\n    if (endpoint->report_storage != NULL) {\n        endpoint->report_storage->reset_report(endpoint->report_storage->reports);\n    }\n    osalOsRescheduleS();\n    osalSysUnlock();\n}\n\nvoid usb_endpoint_out_stop(usb_endpoint_out_t *endpoint) {\n    osalDbgCheck(endpoint != NULL);\n\n    osalSysLock();\n    osalDbgAssert((usbGetDriverStateI(endpoint->config.usbp) == USB_STOP) || (usbGetDriverStateI(endpoint->config.usbp) == USB_READY), \"invalid state\");\n\n    bqSuspendI(&endpoint->ibqueue);\n    ibqResetI(&endpoint->ibqueue);\n    osalOsRescheduleS();\n    osalSysUnlock();\n}\n\nvoid usb_endpoint_in_suspend_cb(usb_endpoint_in_t *endpoint) {\n    bqSuspendI(&endpoint->obqueue);\n    obqResetI(&endpoint->obqueue);\n\n    if (endpoint->report_storage != NULL) {\n        endpoint->report_storage->reset_report(endpoint->report_storage->reports);\n    }\n}\n\nvoid usb_endpoint_out_suspend_cb(usb_endpoint_out_t *endpoint) {\n    bqSuspendI(&endpoint->ibqueue);\n    ibqResetI(&endpoint->ibqueue);\n}\n\nvoid usb_endpoint_in_wakeup_cb(usb_endpoint_in_t *endpoint) {\n    bqResumeX(&endpoint->obqueue);\n}\n\nvoid usb_endpoint_out_wakeup_cb(usb_endpoint_out_t *endpoint) {\n    bqResumeX(&endpoint->ibqueue);\n}\n\nvoid usb_endpoint_in_configure_cb(usb_endpoint_in_t *endpoint) {\n    usbInitEndpointI(endpoint->config.usbp, endpoint->config.ep, &endpoint->ep_config);\n    obqResetI(&endpoint->obqueue);\n    bqResumeX(&endpoint->obqueue);\n}\n\nvoid usb_endpoint_out_configure_cb(usb_endpoint_out_t *endpoint) {\n    /* The current assumption is that there are no standalone OUT endpoints,\n     * therefore if we share an endpoint with an IN endpoint, it is already\n     * initialized. */\n#if !defined(USB_ENDPOINTS_ARE_REORDERABLE)\n    usbInitEndpointI(endpoint->config.usbp, endpoint->config.ep, &endpoint->ep_config);\n#endif\n    ibqResetI(&endpoint->ibqueue);\n    bqResumeX(&endpoint->ibqueue);\n    (void)usb_start_receive(endpoint);\n}\n\nvoid usb_endpoint_in_tx_complete_cb(USBDriver *usbp, usbep_t ep) {\n    usb_endpoint_in_t *endpoint = usbp->in_params[ep - 1U];\n    size_t             n;\n    uint8_t *          buffer;\n\n    if (endpoint == NULL) {\n        return;\n    }\n\n    osalSysLockFromISR();\n\n    /* Sending succeded, so we can reset the timed out state. */\n    endpoint->timed_out = false;\n\n    /* Freeing the buffer just transmitted, if it was not a zero size packet.*/\n    if (!obqIsEmptyI(&endpoint->obqueue) && usbp->epc[ep]->in_state->txsize > 0U) {\n        /* Store the last send report in the endpoint to be retrieved by a\n         * GET_REPORT request or IDLE report handling. */\n        if (endpoint->report_storage != NULL) {\n            buffer = obqGetFullBufferI(&endpoint->obqueue, &n);\n            endpoint->report_storage->set_report(endpoint->report_storage->reports, buffer, n);\n        }\n        obqReleaseEmptyBufferI(&endpoint->obqueue);\n    }\n\n    /* Checking if there is a buffer ready for transmission.*/\n    buffer = obqGetFullBufferI(&endpoint->obqueue, &n);\n\n    if (buffer != NULL) {\n        /* The endpoint cannot be busy, we are in the context of the callback,\n           so it is safe to transmit without a check.*/\n        usbStartTransmitI(usbp, ep, buffer, n);\n    } else if ((usbp->epc[ep]->ep_mode == USB_EP_MODE_TYPE_BULK) && (usbp->epc[ep]->in_state->txsize > 0U) && ((usbp->epc[ep]->in_state->txsize & ((size_t)usbp->epc[ep]->in_maxsize - 1U)) == 0U)) {\n        /* Transmit zero sized packet in case the last one has maximum allowed\n         * size. Otherwise the recipient may expect more data coming soon and\n         * not return buffered data to app. See section 5.8.3 Bulk Transfer\n         * Packet Size Constraints of the USB Specification document. */\n        usbStartTransmitI(usbp, ep, usbp->setup, 0);\n    } else {\n        /* Nothing to transmit.*/\n    }\n\n    osalSysUnlockFromISR();\n}\n\nvoid usb_endpoint_out_rx_complete_cb(USBDriver *usbp, usbep_t ep) {\n    usb_endpoint_out_t *endpoint = usbp->out_params[ep - 1U];\n    if (endpoint == NULL) {\n        return;\n    }\n\n    osalSysLockFromISR();\n\n    size_t size = usbGetReceiveTransactionSizeX(usbp, ep);\n    if (size > 0) {\n        /* Posting the filled buffer in the queue.*/\n        ibqPostFullBufferI(&endpoint->ibqueue, usbGetReceiveTransactionSizeX(endpoint->config.usbp, endpoint->config.ep));\n    }\n\n    /* The endpoint cannot be busy, we are in the context of the callback, so a\n     * packet is in the buffer for sure. Trying to get a free buffer for the\n     * next transaction.*/\n    usb_start_receive(endpoint);\n\n    osalSysUnlockFromISR();\n}\n\nbool usb_endpoint_in_send(usb_endpoint_in_t *endpoint, const uint8_t *data, size_t size, sysinterval_t timeout, bool buffered) {\n    osalDbgCheck((endpoint != NULL) && (data != NULL) && (size > 0U) && (size <= endpoint->config.buffer_size));\n\n    osalSysLock();\n    if (usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE) {\n        osalSysUnlock();\n        return false;\n    }\n\n    /* Short circuit the waiting if this endpoint timed out before, e.g. if\n     * nobody is listening on this endpoint (is disconnected) such as\n     * `hid_listen`/`qmk console` or we are in an environment with a very\n     * restricted USB stack. The reason is to not introduce micro lock-ups if\n     * the report is send periodically. */\n    if (endpoint->timed_out && timeout != TIME_INFINITE) {\n        timeout = TIME_IMMEDIATE;\n    }\n    osalSysUnlock();\n\n    while (true) {\n        size_t sent = obqWriteTimeout(&endpoint->obqueue, data, size, timeout);\n\n        if (sent < size) {\n            osalSysLock();\n            endpoint->timed_out |= sent == 0;\n            bqSuspendI(&endpoint->obqueue);\n            obqResetI(&endpoint->obqueue);\n            bqResumeX(&endpoint->obqueue);\n            osalOsRescheduleS();\n            osalSysUnlock();\n            continue;\n        }\n\n        if (!buffered) {\n            obqFlush(&endpoint->obqueue);\n        }\n\n        return true;\n    }\n}\n\nvoid usb_endpoint_in_flush(usb_endpoint_in_t *endpoint, bool padded) {\n    osalDbgCheck(endpoint != NULL);\n\n    output_buffers_queue_t *obqp = &endpoint->obqueue;\n\n    if (padded && obqp->ptr != NULL) {\n        ptrdiff_t bytes_left = (size_t)obqp->top - (size_t)obqp->ptr;\n        while (bytes_left > 0) {\n            // Putting bytes into a buffer that has space left should never\n            // fail and be instant, therefore we don't check the return value\n            // for errors here.\n            obqPutTimeout(obqp, 0, TIME_IMMEDIATE);\n            bytes_left--;\n        }\n    }\n\n    obqFlush(obqp);\n}\n\nbool usb_endpoint_in_is_inactive(usb_endpoint_in_t *endpoint) {\n    osalDbgCheck(endpoint != NULL);\n\n    osalSysLock();\n    bool inactive = obqIsEmptyI(&endpoint->obqueue) && !usbGetTransmitStatusI(endpoint->config.usbp, endpoint->config.ep);\n    osalSysUnlock();\n\n    return inactive;\n}\n\nbool usb_endpoint_out_receive(usb_endpoint_out_t *endpoint, uint8_t *data, size_t size, sysinterval_t timeout) {\n    osalDbgCheck((endpoint != NULL) && (data != NULL) && (size > 0U));\n\n    osalSysLock();\n    if (usbGetDriverStateI(endpoint->config.usbp) != USB_ACTIVE) {\n        osalSysUnlock();\n        return false;\n    }\n\n    if (endpoint->timed_out && timeout != TIME_INFINITE) {\n        timeout = TIME_IMMEDIATE;\n    }\n    osalSysUnlock();\n\n    const size_t received = ibqReadTimeout(&endpoint->ibqueue, data, size, timeout);\n    endpoint->timed_out   = received == 0;\n\n    return received == size;\n}\n"
  },
  {
    "path": "tmk_core/protocol/chibios/usb_driver.h",
    "content": "// Copyright 2023 Stefan Kerkmann (@KarlK90)\n// Copyright 2020 Ryan (@fauxpark)\n// Copyright 2016 Fredizzimo\n// Copyright 2016 Giovanni Di Sirio\n// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0\n\n#pragma once\n\n#include <hal_buffers.h>\n#include \"usb_descriptor.h\"\n#include \"chibios_config.h\"\n#include \"usb_report_handling.h\"\n#include \"string.h\"\n#include \"timer.h\"\n\n#if HAL_USE_USB == FALSE\n#    error \"The USB Driver requires HAL_USE_USB\"\n#endif\n\n/* USB Low Level driver specific endpoint fields */\n#if !defined(usb_lld_endpoint_fields)\n#    define usb_lld_endpoint_fields   \\\n        2,        /* IN multiplier */ \\\n            NULL, /* SETUP buffer (not a SETUP endpoint) */\n#endif\n\n/*\n * Implementation notes:\n *\n * USBEndpointConfig - Configured using explicit order instead of struct member name.\n *   This is due to ChibiOS hal LLD differences, which is dependent on hardware,\n *   \"USBv1\" devices have `ep_buffers` and \"OTGv1\" have `in_multiplier`.\n *   Given `USBv1/hal_usb_lld.h` marks the field as \"not currently used\" this code file\n *   makes the assumption this is safe to avoid littering with preprocessor directives.\n */\n#define QMK_USB_ENDPOINT_IN(mode, ep_size, ep_num, _buffer_capacity, _usb_requests_cb, _report_storage) \\\n    {                                                                                                   \\\n        .usb_requests_cb = _usb_requests_cb, .report_storage = _report_storage,                         \\\n        .ep_config =                                                                                    \\\n            {                                                                                           \\\n                mode,                           /* EP Mode */                                           \\\n                NULL,                           /* SETUP packet notification callback */                \\\n                usb_endpoint_in_tx_complete_cb, /* IN notification callback */                          \\\n                NULL,                           /* OUT notification callback */                         \\\n                ep_size,                        /* IN maximum packet size */                            \\\n                0,                              /* OUT maximum packet size */                           \\\n                NULL,                           /* IN Endpoint state */                                 \\\n                NULL,                           /* OUT endpoint state */                                \\\n                usb_lld_endpoint_fields         /* USB driver specific endpoint fields */               \\\n            },                                                                                          \\\n        .config = {                                                                                     \\\n            .usbp            = &USB_DRIVER,                                                             \\\n            .ep              = ep_num,                                                                  \\\n            .buffer_capacity = _buffer_capacity,                                                        \\\n            .buffer_size     = ep_size,                                                                 \\\n            .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0},     \\\n        }                                                                                               \\\n    }\n\n#if !defined(USB_ENDPOINTS_ARE_REORDERABLE)\n\n#    define QMK_USB_ENDPOINT_OUT(mode, ep_size, ep_num, _buffer_capacity)                              \\\n        {                                                                                              \\\n            .ep_config =                                                                               \\\n                {                                                                                      \\\n                    mode,                            /* EP Mode */                                     \\\n                    NULL,                            /* SETUP packet notification callback */          \\\n                    NULL,                            /* IN notification callback */                    \\\n                    usb_endpoint_out_rx_complete_cb, /* OUT notification callback */                   \\\n                    0,                               /* IN maximum packet size */                      \\\n                    ep_size,                         /* OUT maximum packet size */                     \\\n                    NULL,                            /* IN Endpoint state */                           \\\n                    NULL,                            /* OUT endpoint state */                          \\\n                    usb_lld_endpoint_fields          /* USB driver specific endpoint fields */         \\\n                },                                                                                     \\\n            .config = {                                                                                \\\n                .usbp            = &USB_DRIVER,                                                        \\\n                .ep              = ep_num,                                                             \\\n                .buffer_capacity = _buffer_capacity,                                                   \\\n                .buffer_size     = ep_size,                                                            \\\n                .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0} \\\n            }                                                                                          \\\n        }\n\n#else\n\n#    define QMK_USB_ENDPOINT_IN_SHARED(mode, ep_size, ep_num, _buffer_capacity, _usb_requests_cb, _report_storage) \\\n        {                                                                                                          \\\n            .usb_requests_cb = _usb_requests_cb, .is_shared = true, .report_storage = _report_storage,             \\\n            .ep_config =                                                                                           \\\n                {                                                                                                  \\\n                    mode,                            /* EP Mode */                                                 \\\n                    NULL,                            /* SETUP packet notification callback */                      \\\n                    usb_endpoint_in_tx_complete_cb,  /* IN notification callback */                                \\\n                    usb_endpoint_out_rx_complete_cb, /* OUT notification callback */                               \\\n                    ep_size,                         /* IN maximum packet size */                                  \\\n                    ep_size,                         /* OUT maximum packet size */                                 \\\n                    NULL,                            /* IN Endpoint state */                                       \\\n                    NULL,                            /* OUT endpoint state */                                      \\\n                    usb_lld_endpoint_fields          /* USB driver specific endpoint fields */                     \\\n                },                                                                                                 \\\n            .config = {                                                                                            \\\n                .usbp            = &USB_DRIVER,                                                                    \\\n                .ep              = ep_num,                                                                         \\\n                .buffer_capacity = _buffer_capacity,                                                               \\\n                .buffer_size     = ep_size,                                                                        \\\n                .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0},            \\\n            }                                                                                                      \\\n        }\n\n/* The current assumption is that there are no standalone OUT endpoints, so the\n * OUT endpoint is always initialized by the IN endpoint. */\n#    define QMK_USB_ENDPOINT_OUT(mode, ep_size, ep_num, _buffer_capacity)                              \\\n        {                                                                                              \\\n            .ep_config =                                                                               \\\n                {                                                                                      \\\n                    0 /* Already defined in the IN endpoint */                                         \\\n                },                                                                                     \\\n            .config = {                                                                                \\\n                .usbp            = &USB_DRIVER,                                                        \\\n                .ep              = ep_num,                                                             \\\n                .buffer_capacity = _buffer_capacity,                                                   \\\n                .buffer_size     = ep_size,                                                            \\\n                .buffer          = (_Alignas(4) uint8_t[BQ_BUFFER_SIZE(_buffer_capacity, ep_size)]){0} \\\n            }                                                                                          \\\n        }\n\n#endif\n\ntypedef struct {\n    /**\n     * @brief   USB driver to use.\n     */\n    USBDriver *usbp;\n\n    /**\n     * @brief   Endpoint used for data transfer\n     */\n    usbep_t ep;\n\n    /**\n     * @brief The number of buffers in the queue\n     */\n    size_t buffer_capacity;\n\n    /**\n     * @brief The size of each buffer in the queue, same as the endpoint size\n     */\n    size_t buffer_size;\n\n    /**\n     * @brief Buffer backing storage\n     */\n    uint8_t *buffer;\n} usb_endpoint_config_t;\n\ntypedef struct {\n    output_buffers_queue_t obqueue;\n    USBEndpointConfig      ep_config;\n    USBInEndpointState     ep_in_state;\n#if defined(USB_ENDPOINTS_ARE_REORDERABLE)\n    USBOutEndpointState ep_out_state;\n    bool                is_shared;\n#endif\n    usb_endpoint_config_t config;\n    usbreqhandler_t       usb_requests_cb;\n    bool                  timed_out;\n    usb_report_storage_t *report_storage;\n} usb_endpoint_in_t;\n\ntypedef struct {\n    input_buffers_queue_t ibqueue;\n    USBEndpointConfig     ep_config;\n    USBOutEndpointState   ep_out_state;\n    usb_endpoint_config_t config;\n    bool                  timed_out;\n} usb_endpoint_out_t;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nvoid usb_endpoint_in_init(usb_endpoint_in_t *endpoint);\nvoid usb_endpoint_in_start(usb_endpoint_in_t *endpoint);\nvoid usb_endpoint_in_stop(usb_endpoint_in_t *endpoint);\n\nbool usb_endpoint_in_send(usb_endpoint_in_t *endpoint, const uint8_t *data, size_t size, sysinterval_t timeout, bool buffered);\nvoid usb_endpoint_in_flush(usb_endpoint_in_t *endpoint, bool padded);\nbool usb_endpoint_in_is_inactive(usb_endpoint_in_t *endpoint);\n\nvoid usb_endpoint_in_suspend_cb(usb_endpoint_in_t *endpoint);\nvoid usb_endpoint_in_wakeup_cb(usb_endpoint_in_t *endpoint);\nvoid usb_endpoint_in_configure_cb(usb_endpoint_in_t *endpoint);\nvoid usb_endpoint_in_tx_complete_cb(USBDriver *usbp, usbep_t ep);\n\nvoid usb_endpoint_out_init(usb_endpoint_out_t *endpoint);\nvoid usb_endpoint_out_start(usb_endpoint_out_t *endpoint);\nvoid usb_endpoint_out_stop(usb_endpoint_out_t *endpoint);\n\nbool usb_endpoint_out_receive(usb_endpoint_out_t *endpoint, uint8_t *data, size_t size, sysinterval_t timeout);\n\nvoid usb_endpoint_out_suspend_cb(usb_endpoint_out_t *endpoint);\nvoid usb_endpoint_out_wakeup_cb(usb_endpoint_out_t *endpoint);\nvoid usb_endpoint_out_configure_cb(usb_endpoint_out_t *endpoint);\nvoid usb_endpoint_out_rx_complete_cb(USBDriver *usbp, usbep_t ep);\n\n#ifdef __cplusplus\n}\n#endif\n\n/** @} */\n"
  },
  {
    "path": "tmk_core/protocol/chibios/usb_endpoints.c",
    "content": "// Copyright 2023 Stefan Kerkmann (@KarlK90)\n// SPDX-License-Identifier: GPL-3.0-or-later\n\n#include <ch.h>\n#include <hal.h>\n\n#include \"usb_main.h\"\n#include \"usb_driver.h\"\n#include \"usb_endpoints.h\"\n#include \"report.h\"\n\nusb_endpoint_in_t usb_endpoints_in[USB_ENDPOINT_IN_COUNT] = {\n// clang-format off\n#if defined(SHARED_EP_ENABLE)\n    [USB_ENDPOINT_IN_SHARED] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, SHARED_EPSIZE, SHARED_IN_EPNUM, SHARED_IN_CAPACITY, NULL,\n    QMK_USB_REPORT_STORAGE(\n        &usb_shared_get_report,\n        &usb_shared_set_report,\n        &usb_shared_reset_report,\n        &usb_shared_get_idle_rate,\n        &usb_shared_set_idle_rate,\n        &usb_shared_idle_timer_elapsed,\n        (REPORT_ID_COUNT + 1),\n#if defined(KEYBOARD_SHARED_EP)\n        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_KEYBOARD, sizeof(report_keyboard_t)),\n#endif\n#if defined(MOUSE_SHARED_EP)\n        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_MOUSE, sizeof(report_mouse_t)),\n#endif\n#if defined(EXTRAKEY_ENABLE)\n        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_SYSTEM, sizeof(report_extra_t)),\n        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_CONSUMER, sizeof(report_extra_t)),\n#endif\n#if defined(PROGRAMMABLE_BUTTON_ENABLE)\n        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_PROGRAMMABLE_BUTTON, sizeof(report_programmable_button_t)),\n#endif\n#if defined(NKRO_ENABLE)\n        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_NKRO, sizeof(report_nkro_t)),\n#endif\n#if defined(JOYSTICK_SHARED_EP)\n        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_JOYSTICK, sizeof(report_joystick_t)),\n#endif\n#if defined(DIGITIZER_SHARED_EP)\n        QMK_USB_REPORT_STROAGE_ENTRY(REPORT_ID_DIGITIZER, sizeof(report_digitizer_t)),\n#endif\n        )\n    ),\n#endif\n// clang-format on\n\n#if !defined(KEYBOARD_SHARED_EP)\n    [USB_ENDPOINT_IN_KEYBOARD] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, KEYBOARD_EPSIZE, KEYBOARD_IN_EPNUM, KEYBOARD_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_keyboard_t))),\n#endif\n\n#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)\n    [USB_ENDPOINT_IN_MOUSE] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, MOUSE_EPSIZE, MOUSE_IN_EPNUM, MOUSE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_mouse_t))),\n#endif\n\n#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)\n    [USB_ENDPOINT_IN_JOYSTICK] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, JOYSTICK_EPSIZE, JOYSTICK_IN_EPNUM, JOYSTICK_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_joystick_t))),\n#endif\n\n#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)\n    [USB_ENDPOINT_IN_DIGITIZER] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, DIGITIZER_EPSIZE, DIGITIZER_IN_EPNUM, DIGITIZER_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(sizeof(report_digitizer_t))),\n#endif\n\n#if defined(CONSOLE_ENABLE)\n#    if defined(USB_ENDPOINTS_ARE_REORDERABLE)\n    [USB_ENDPOINT_IN_CONSOLE] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_INTR, CONSOLE_EPSIZE, CONSOLE_IN_EPNUM, CONSOLE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(CONSOLE_EPSIZE)),\n#    else\n    [USB_ENDPOINT_IN_CONSOLE]  = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, CONSOLE_EPSIZE, CONSOLE_IN_EPNUM, CONSOLE_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(CONSOLE_EPSIZE)),\n#    endif\n#endif\n\n#if defined(RAW_ENABLE)\n#    if defined(USB_ENDPOINTS_ARE_REORDERABLE)\n    [USB_ENDPOINT_IN_RAW] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_IN_EPNUM, RAW_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(RAW_EPSIZE)),\n#    else\n    [USB_ENDPOINT_IN_RAW]      = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_IN_EPNUM, RAW_IN_CAPACITY, NULL, QMK_USB_REPORT_STORAGE_DEFAULT(RAW_EPSIZE)),\n#    endif\n#endif\n\n#if defined(MIDI_ENABLE)\n#    if defined(USB_ENDPOINTS_ARE_REORDERABLE)\n    [USB_ENDPOINT_IN_MIDI] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_IN_EPNUM, MIDI_STREAM_IN_CAPACITY, NULL, NULL),\n#    else\n    [USB_ENDPOINT_IN_MIDI]     = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_IN_EPNUM, MIDI_STREAM_IN_CAPACITY, NULL, NULL),\n#    endif\n#endif\n\n#if defined(VIRTSER_ENABLE)\n#    if defined(USB_ENDPOINTS_ARE_REORDERABLE)\n    [USB_ENDPOINT_IN_CDC_DATA] = QMK_USB_ENDPOINT_IN_SHARED(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_IN_EPNUM, CDC_IN_CAPACITY, virtser_usb_request_cb, NULL),\n#    else\n    [USB_ENDPOINT_IN_CDC_DATA] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_IN_EPNUM, CDC_IN_CAPACITY, virtser_usb_request_cb, NULL),\n#    endif\n    [USB_ENDPOINT_IN_CDC_SIGNALING] = QMK_USB_ENDPOINT_IN(USB_EP_MODE_TYPE_INTR, CDC_NOTIFICATION_EPSIZE, CDC_NOTIFICATION_EPNUM, CDC_SIGNALING_DUMMY_CAPACITY, NULL, NULL),\n#endif\n};\n\nusb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES] = {\n#if !defined(KEYBOARD_SHARED_EP)\n    [KEYBOARD_INTERFACE] = USB_ENDPOINT_IN_KEYBOARD,\n#endif\n\n#if defined(RAW_ENABLE)\n    [RAW_INTERFACE] = USB_ENDPOINT_IN_RAW,\n#endif\n\n#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)\n    [MOUSE_INTERFACE] = USB_ENDPOINT_IN_MOUSE,\n#endif\n\n#if defined(SHARED_EP_ENABLE)\n    [SHARED_INTERFACE] = USB_ENDPOINT_IN_SHARED,\n#endif\n\n#if defined(CONSOLE_ENABLE)\n    [CONSOLE_INTERFACE] = USB_ENDPOINT_IN_CONSOLE,\n#endif\n\n#if defined(MIDI_ENABLE)\n    [AS_INTERFACE] = USB_ENDPOINT_IN_MIDI,\n#endif\n\n#if defined(VIRTSER_ENABLE)\n    [CCI_INTERFACE] = USB_ENDPOINT_IN_CDC_SIGNALING,\n    [CDI_INTERFACE] = USB_ENDPOINT_IN_CDC_DATA,\n#endif\n\n#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)\n    [JOYSTICK_INTERFACE] = USB_ENDPOINT_IN_JOYSTICK,\n#endif\n\n#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)\n    [DIGITIZER_INTERFACE] = USB_ENDPOINT_IN_DIGITIZER,\n#endif\n};\n\nusb_endpoint_out_t usb_endpoints_out[USB_ENDPOINT_OUT_COUNT] = {\n#if defined(RAW_ENABLE)\n    [USB_ENDPOINT_OUT_RAW] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_INTR, RAW_EPSIZE, RAW_OUT_EPNUM, RAW_OUT_CAPACITY),\n#endif\n\n#if defined(MIDI_ENABLE)\n    [USB_ENDPOINT_OUT_MIDI] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_BULK, MIDI_STREAM_EPSIZE, MIDI_STREAM_OUT_EPNUM, MIDI_STREAM_OUT_CAPACITY),\n#endif\n\n#if defined(VIRTSER_ENABLE)\n    [USB_ENDPOINT_OUT_CDC_DATA] = QMK_USB_ENDPOINT_OUT(USB_EP_MODE_TYPE_BULK, CDC_EPSIZE, CDC_OUT_EPNUM, CDC_OUT_CAPACITY),\n#endif\n};\n"
  },
  {
    "path": "tmk_core/protocol/chibios/usb_endpoints.h",
    "content": "// Copyright 2023 Stefan Kerkmann (@KarlK90)\n// SPDX-License-Identifier: GPL-3.0-or-later\n\n#pragma once\n\n#include \"usb_descriptor.h\"\n\n#if !defined(USB_DEFAULT_BUFFER_CAPACITY)\n#    define USB_DEFAULT_BUFFER_CAPACITY 4\n#endif\n\n#if !defined(KEYBOARD_IN_CAPACITY)\n#    define KEYBOARD_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n#if !defined(SHARED_IN_CAPACITY)\n#    define SHARED_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n#if !defined(MOUSE_IN_CAPACITY)\n#    define MOUSE_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n\n#if !defined(JOYSTICK_IN_CAPACITY)\n#    define JOYSTICK_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n\n#if !defined(DIGITIZER_IN_CAPACITY)\n#    define DIGITIZER_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n\n#if !defined(CONSOLE_IN_CAPACITY)\n#    define CONSOLE_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n\n#if !defined(CONSOLE_OUT_CAPACITY)\n#    define CONSOLE_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n\n#if !defined(RAW_IN_CAPACITY)\n#    define RAW_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n\n#if !defined(RAW_OUT_CAPACITY)\n#    define RAW_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n\n#if !defined(MIDI_STREAM_IN_CAPACITY)\n#    define MIDI_STREAM_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n\n#if !defined(MIDI_STREAM_OUT_CAPACITY)\n#    define MIDI_STREAM_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n\n#if !defined(CDC_IN_CAPACITY)\n#    define CDC_IN_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n\n#if !defined(CDC_OUT_CAPACITY)\n#    define CDC_OUT_CAPACITY USB_DEFAULT_BUFFER_CAPACITY\n#endif\n\n#define CDC_SIGNALING_DUMMY_CAPACITY 1\n\ntypedef enum {\n#if defined(SHARED_EP_ENABLE)\n    USB_ENDPOINT_IN_SHARED,\n#endif\n\n#if !defined(KEYBOARD_SHARED_EP)\n    USB_ENDPOINT_IN_KEYBOARD,\n#endif\n\n#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)\n    USB_ENDPOINT_IN_MOUSE,\n#endif\n\n#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)\n    USB_ENDPOINT_IN_JOYSTICK,\n#endif\n\n#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)\n    USB_ENDPOINT_IN_DIGITIZER,\n#endif\n\n#if defined(CONSOLE_ENABLE)\n    USB_ENDPOINT_IN_CONSOLE,\n#endif\n\n#if defined(RAW_ENABLE)\n    USB_ENDPOINT_IN_RAW,\n#endif\n\n#if defined(MIDI_ENABLE)\n    USB_ENDPOINT_IN_MIDI,\n#endif\n\n#if defined(VIRTSER_ENABLE)\n    USB_ENDPOINT_IN_CDC_DATA,\n    USB_ENDPOINT_IN_CDC_SIGNALING,\n#endif\n    USB_ENDPOINT_IN_COUNT,\n/* All non shared endpoints have to be consequtive numbers starting from 0, so\n * that they can be used as array indices. The shared endpoints all point to\n * the same endpoint so they have to be defined last to not reset the enum\n * counter. */\n#if defined(SHARED_EP_ENABLE)\n#    if defined(KEYBOARD_SHARED_EP)\n    USB_ENDPOINT_IN_KEYBOARD = USB_ENDPOINT_IN_SHARED,\n#    endif\n#    if defined(MOUSE_SHARED_EP)\n    USB_ENDPOINT_IN_MOUSE = USB_ENDPOINT_IN_SHARED,\n#    endif\n#    if defined(JOYSTICK_SHARED_EP)\n    USB_ENDPOINT_IN_JOYSTICK = USB_ENDPOINT_IN_SHARED,\n#    endif\n#    if defined(DIGITIZER_SHARED_EP)\n    USB_ENDPOINT_IN_DIGITIZER = USB_ENDPOINT_IN_SHARED,\n#    endif\n#endif\n} usb_endpoint_in_lut_t;\n\n#define IS_VALID_USB_ENDPOINT_IN_LUT(i) ((i) >= 0 && (i) < USB_ENDPOINT_IN_COUNT)\n\nusb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES];\n\ntypedef enum {\n#if defined(RAW_ENABLE)\n    USB_ENDPOINT_OUT_RAW,\n#endif\n#if defined(MIDI_ENABLE)\n    USB_ENDPOINT_OUT_MIDI,\n#endif\n#if defined(VIRTSER_ENABLE)\n    USB_ENDPOINT_OUT_CDC_DATA,\n#endif\n    USB_ENDPOINT_OUT_COUNT,\n} usb_endpoint_out_lut_t;\n"
  },
  {
    "path": "tmk_core/protocol/chibios/usb_main.c",
    "content": "// Copyright 2023 Stefan Kerkmann\n// Copyright 2020-2021 Ryan (@fauxpark)\n// Copyright 2020 Nick Brassel (@tzarc)\n// Copyright 2020 a-chol\n// Copyright 2020 xyzz\n// Copyright 2020 Joel Challis (@zvecr)\n// Copyright 2020 George (@goshdarnharris)\n// Copyright 2018 James Laird-Wah\n// Copyright 2018 Drashna Jaelre (@drashna)\n// Copyright 2016 Fredizzimo\n// Copyright 2016 Giovanni Di Sirio\n// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0\n\n#include <ch.h>\n#include <hal.h>\n#include <string.h>\n\n#include \"usb_main.h\"\n#include \"usb_report_handling.h\"\n\n#include \"host.h\"\n#include \"suspend.h\"\n#include \"timer.h\"\n#ifdef SLEEP_LED_ENABLE\n#    include \"sleep_led.h\"\n#    include \"led.h\"\n#endif\n#include \"wait.h\"\n#include \"usb_endpoints.h\"\n#include \"usb_device_state.h\"\n#include \"usb_descriptor.h\"\n#include \"usb_driver.h\"\n#include \"usb_types.h\"\n\n#ifdef RAW_ENABLE\n#    include \"raw_hid.h\"\n#endif\n\n#ifdef NKRO_ENABLE\n#    include \"keycode_config.h\"\n\nextern keymap_config_t keymap_config;\n#endif\n\n/* ---------------------------------------------------------\n *       Global interface variables and declarations\n * ---------------------------------------------------------\n */\n\n#ifndef usb_lld_connect_bus\n#    define usb_lld_connect_bus(usbp)\n#endif\n\n#ifndef usb_lld_disconnect_bus\n#    define usb_lld_disconnect_bus(usbp)\n#endif\n\nextern usb_endpoint_in_t  usb_endpoints_in[USB_ENDPOINT_IN_COUNT];\nextern usb_endpoint_out_t usb_endpoints_out[USB_ENDPOINT_OUT_COUNT];\n\nstatic bool __attribute__((__unused__)) send_report_buffered(usb_endpoint_in_lut_t endpoint, void *report, size_t size);\nstatic void __attribute__((__unused__)) flush_report_buffered(usb_endpoint_in_lut_t endpoint, bool padded);\nstatic bool __attribute__((__unused__)) receive_report(usb_endpoint_out_lut_t endpoint, void *report, size_t size);\n\n/* ---------------------------------------------------------\n *            Descriptors and USB driver objects\n * ---------------------------------------------------------\n */\n\n/* USB Low Level driver specific endpoint fields */\n#if !defined(usb_lld_endpoint_fields)\n#    define usb_lld_endpoint_fields   \\\n        2,        /* IN multiplier */ \\\n            NULL, /* SETUP buffer (not a SETUP endpoint) */\n#endif\n\n/*\n * Handles the GET_DESCRIPTOR callback\n *\n * Returns the proper descriptor\n */\nstatic const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype, uint8_t dindex, uint16_t wIndex) {\n    usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;\n\n    static USBDescriptor descriptor;\n    descriptor.ud_string = NULL;\n    descriptor.ud_size   = get_usb_descriptor(setup->wValue.word, setup->wIndex, setup->wLength, (const void **const) & descriptor.ud_string);\n\n    if (descriptor.ud_string == NULL) {\n        return NULL;\n    }\n\n    return &descriptor;\n}\n\n/* ---------------------------------------------------------\n *                  USB driver functions\n * ---------------------------------------------------------\n */\n\n#define USB_EVENT_QUEUE_SIZE 16\nusbevent_t event_queue[USB_EVENT_QUEUE_SIZE];\nuint8_t    event_queue_head;\nuint8_t    event_queue_tail;\n\nvoid usb_event_queue_init(void) {\n    // Initialise the event queue\n    memset(&event_queue, 0, sizeof(event_queue));\n    event_queue_head = 0;\n    event_queue_tail = 0;\n}\n\nstatic inline bool usb_event_queue_enqueue(usbevent_t event) {\n    uint8_t next = (event_queue_head + 1) % USB_EVENT_QUEUE_SIZE;\n    if (next == event_queue_tail) {\n        return false;\n    }\n    event_queue[event_queue_head] = event;\n    event_queue_head              = next;\n    return true;\n}\n\nstatic inline bool usb_event_queue_dequeue(usbevent_t *event) {\n    if (event_queue_head == event_queue_tail) {\n        return false;\n    }\n    *event           = event_queue[event_queue_tail];\n    event_queue_tail = (event_queue_tail + 1) % USB_EVENT_QUEUE_SIZE;\n    return true;\n}\n\nstatic inline void usb_event_suspend_handler(void) {\n    usb_device_state_set_suspend(USB_DRIVER.configuration != 0, USB_DRIVER.configuration);\n#ifdef SLEEP_LED_ENABLE\n    sleep_led_enable();\n#endif /* SLEEP_LED_ENABLE */\n}\n\nstatic inline void usb_event_wakeup_handler(void) {\n    suspend_wakeup_init();\n    usb_device_state_set_resume(USB_DRIVER.configuration != 0, USB_DRIVER.configuration);\n#ifdef SLEEP_LED_ENABLE\n    sleep_led_disable();\n    // NOTE: converters may not accept this\n    led_set(host_keyboard_leds());\n#endif /* SLEEP_LED_ENABLE */\n}\n\nbool last_suspend_state = false;\n\nvoid usb_event_queue_task(void) {\n    usbevent_t event;\n    while (usb_event_queue_dequeue(&event)) {\n        switch (event) {\n            case USB_EVENT_SUSPEND:\n                last_suspend_state = true;\n                usb_event_suspend_handler();\n                break;\n            case USB_EVENT_WAKEUP:\n                last_suspend_state = false;\n                usb_event_wakeup_handler();\n                break;\n            case USB_EVENT_CONFIGURED:\n                usb_device_state_set_configuration(USB_DRIVER.configuration != 0, USB_DRIVER.configuration);\n                break;\n            case USB_EVENT_UNCONFIGURED:\n                usb_device_state_set_configuration(false, 0);\n                break;\n            case USB_EVENT_RESET:\n                usb_device_state_set_reset();\n                usb_device_state_set_protocol(USB_PROTOCOL_REPORT);\n                break;\n            default:\n                // Nothing to do, we don't handle it.\n                break;\n        }\n    }\n}\n\n/* Handles the USB driver global events. */\nstatic void usb_event_cb(USBDriver *usbp, usbevent_t event) {\n    switch (event) {\n        case USB_EVENT_ADDRESS:\n            return;\n\n        case USB_EVENT_CONFIGURED:\n            osalSysLockFromISR();\n            for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {\n                usb_endpoint_in_configure_cb(&usb_endpoints_in[i]);\n            }\n            for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {\n                usb_endpoint_out_configure_cb(&usb_endpoints_out[i]);\n            }\n            osalSysUnlockFromISR();\n            if (last_suspend_state) {\n                usb_event_queue_enqueue(USB_EVENT_WAKEUP);\n            }\n            usb_event_queue_enqueue(USB_EVENT_CONFIGURED);\n            return;\n        case USB_EVENT_SUSPEND:\n            /* Falls into.*/\n        case USB_EVENT_UNCONFIGURED:\n            /* Falls into.*/\n        case USB_EVENT_RESET:\n            usb_event_queue_enqueue(event);\n            chSysLockFromISR();\n            for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {\n                usb_endpoint_in_suspend_cb(&usb_endpoints_in[i]);\n            }\n            for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {\n                usb_endpoint_out_suspend_cb(&usb_endpoints_out[i]);\n            }\n            chSysUnlockFromISR();\n            return;\n\n        case USB_EVENT_WAKEUP:\n            chSysLockFromISR();\n            for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {\n                usb_endpoint_in_wakeup_cb(&usb_endpoints_in[i]);\n            }\n            for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {\n                usb_endpoint_out_wakeup_cb(&usb_endpoints_out[i]);\n            }\n            chSysUnlockFromISR();\n            usb_event_queue_enqueue(USB_EVENT_WAKEUP);\n            return;\n\n        case USB_EVENT_STALLED:\n            return;\n    }\n}\n\n/*\n * Appendix G: HID Request Support Requirements\n *\n * The following table enumerates the requests that need to be supported by various types of HID class devices.\n * Device type     GetReport   SetReport   GetIdle     SetIdle     GetProtocol SetProtocol\n * ------------------------------------------------------------------------------------------\n * Boot Mouse      Required    Optional    Optional    Optional    Required    Required\n * Non-Boot Mouse  Required    Optional    Optional    Optional    Optional    Optional\n * Boot Keyboard   Required    Optional    Required    Required    Required    Required\n * Non-Boot Keybrd Required    Optional    Required    Required    Optional    Optional\n * Other Device    Required    Optional    Optional    Optional    Optional    Optional\n */\n\nstatic uint8_t _Alignas(4) set_report_buf[2];\n\nstatic void set_led_transfer_cb(USBDriver *usbp) {\n    usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;\n\n    if (setup->wLength == 2) {\n        uint8_t report_id = set_report_buf[0];\n        if ((report_id == REPORT_ID_KEYBOARD) || (report_id == REPORT_ID_NKRO)) {\n            usb_device_state_set_leds(set_report_buf[1]);\n        }\n    } else {\n        usb_device_state_set_leds(set_report_buf[0]);\n    }\n}\n\nstatic bool usb_requests_hook_cb(USBDriver *usbp) {\n    usb_control_request_t *setup = (usb_control_request_t *)usbp->setup;\n\n    /* Handle HID class specific requests */\n    if ((setup->bmRequestType & (USB_RTYPE_TYPE_MASK | USB_RTYPE_RECIPIENT_MASK)) == (USB_RTYPE_TYPE_CLASS | USB_RTYPE_RECIPIENT_INTERFACE)) {\n        switch (setup->bmRequestType & USB_RTYPE_DIR_MASK) {\n            case USB_RTYPE_DIR_DEV2HOST:\n                switch (setup->bRequest) {\n                    case HID_REQ_GetReport:\n                        return usb_get_report_cb(usbp);\n                    case HID_REQ_GetProtocol:\n                        if (setup->wIndex == KEYBOARD_INTERFACE) {\n                            static uint8_t keyboard_protocol;\n                            keyboard_protocol = usb_device_state_get_protocol();\n                            usbSetupTransfer(usbp, &keyboard_protocol, sizeof(keyboard_protocol), NULL);\n                            return true;\n                        }\n                        break;\n\n                    case HID_REQ_GetIdle:\n                        return usb_get_idle_cb(usbp);\n                }\n\n            case USB_RTYPE_DIR_HOST2DEV:\n                switch (setup->bRequest) {\n                    case HID_REQ_SetReport:\n                        switch (setup->wIndex) {\n                            case KEYBOARD_INTERFACE:\n#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)\n                            case SHARED_INTERFACE:\n#endif\n                                usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb);\n                                return true;\n                        }\n                        break;\n                    case HID_REQ_SetProtocol:\n                        if (setup->wIndex == KEYBOARD_INTERFACE) {\n                            usb_device_state_set_protocol(setup->wValue.lbyte);\n                        }\n                        usbSetupTransfer(usbp, NULL, 0, NULL);\n                        return true;\n                    case HID_REQ_SetIdle:\n                        usb_device_state_set_idle_rate(setup->wValue.hbyte);\n                        return usb_set_idle_cb(usbp);\n                }\n                break;\n        }\n    }\n\n    /* Handle the Get_Descriptor Request for HID class, which is not handled by\n     * the ChibiOS USB driver */\n    if (((setup->bmRequestType & (USB_RTYPE_DIR_MASK | USB_RTYPE_RECIPIENT_MASK)) == (USB_RTYPE_DIR_DEV2HOST | USB_RTYPE_RECIPIENT_INTERFACE)) && (setup->bRequest == USB_REQ_GET_DESCRIPTOR)) {\n        const USBDescriptor *descriptor = usbp->config->get_descriptor_cb(usbp, setup->wValue.lbyte, setup->wValue.hbyte, setup->wIndex);\n        if (descriptor == NULL) {\n            return false;\n        }\n        usbSetupTransfer(usbp, (uint8_t *)descriptor->ud_string, descriptor->ud_size, NULL);\n        return true;\n    }\n\n    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {\n        if (usb_endpoints_in[i].usb_requests_cb != NULL) {\n            if (usb_endpoints_in[i].usb_requests_cb(usbp)) {\n                return true;\n            }\n        }\n    }\n\n    return false;\n}\n\nstatic const USBConfig usbcfg = {\n    usb_event_cb,          /* USB events callback */\n    usb_get_descriptor_cb, /* Device GET_DESCRIPTOR request callback */\n    usb_requests_hook_cb,  /* Requests hook callback */\n};\n\nvoid init_usb_driver(USBDriver *usbp) {\n    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {\n        usb_endpoint_in_init(&usb_endpoints_in[i]);\n        usb_endpoint_in_start(&usb_endpoints_in[i]);\n    }\n\n    for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {\n        usb_endpoint_out_init(&usb_endpoints_out[i]);\n        usb_endpoint_out_start(&usb_endpoints_out[i]);\n    }\n\n    /*\n     * Activates the USB driver and then the USB bus pull-up on D+.\n     * Note, a delay is inserted in order to not have to disconnect the cable\n     * after a reset.\n     */\n    usbDisconnectBus(usbp);\n    usbStop(usbp);\n    wait_ms(50);\n    usbStart(usbp, &usbcfg);\n    usbConnectBus(usbp);\n}\n\n__attribute__((weak)) void restart_usb_driver(USBDriver *usbp) {\n    usbDisconnectBus(usbp);\n    usbStop(usbp);\n\n    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {\n        usb_endpoint_in_stop(&usb_endpoints_in[i]);\n    }\n\n    for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {\n        usb_endpoint_out_stop(&usb_endpoints_out[i]);\n    }\n\n    wait_ms(50);\n\n    for (int i = 0; i < USB_ENDPOINT_IN_COUNT; i++) {\n        usb_endpoint_in_init(&usb_endpoints_in[i]);\n        usb_endpoint_in_start(&usb_endpoints_in[i]);\n    }\n\n    for (int i = 0; i < USB_ENDPOINT_OUT_COUNT; i++) {\n        usb_endpoint_out_init(&usb_endpoints_out[i]);\n        usb_endpoint_out_start(&usb_endpoints_out[i]);\n    }\n\n    usbStart(usbp, &usbcfg);\n    usbConnectBus(usbp);\n}\n\n/* ---------------------------------------------------------\n *                  Keyboard functions\n * ---------------------------------------------------------\n */\n\n/**\n * @brief Send a report to the host, the report is enqueued into an output\n * queue and send once the USB endpoint becomes empty.\n *\n * @param endpoint USB IN endpoint to send the report from\n * @param report pointer to the report\n * @param size size of the report\n * @return true Success\n * @return false Failure\n */\nbool send_report(usb_endpoint_in_lut_t endpoint, void *report, size_t size) {\n    return usb_endpoint_in_send(&usb_endpoints_in[endpoint], (uint8_t *)report, size, TIME_MS2I(100), false);\n}\n\n/**\n * @brief Send a report to the host, but delay the sending until the size of\n * endpoint report is reached or the incompletely filled buffer is flushed with\n * a call to `flush_report_buffered`. This is useful if the report is being\n * updated frequently. The complete report is then enqueued into an output\n * queue and send once the USB endpoint becomes empty.\n *\n * @param endpoint USB IN endpoint to send the report from\n * @param report pointer to the report\n * @param size size of the report\n * @return true Success\n * @return false Failure\n */\nstatic bool send_report_buffered(usb_endpoint_in_lut_t endpoint, void *report, size_t size) {\n    return usb_endpoint_in_send(&usb_endpoints_in[endpoint], (uint8_t *)report, size, TIME_MS2I(100), true);\n}\n\n/** @brief Flush all buffered reports which were enqueued with a call to\n * `send_report_buffered` that haven't been send. If necessary the buffered\n * report can be padded with zeros up to the endpoints maximum size.\n *\n * @param endpoint USB IN endpoint to flush the reports from\n * @param padded Pad the buffered report with zeros up to the endpoints maximum size\n */\nstatic void flush_report_buffered(usb_endpoint_in_lut_t endpoint, bool padded) {\n    usb_endpoint_in_flush(&usb_endpoints_in[endpoint], padded);\n}\n\n/**\n * @brief Receive a report from the host.\n *\n * @param endpoint USB OUT endpoint to receive the report from\n * @param report pointer to the report\n * @param size size of the report\n * @return true Success\n * @return false Failure\n */\nstatic bool receive_report(usb_endpoint_out_lut_t endpoint, void *report, size_t size) {\n    return usb_endpoint_out_receive(&usb_endpoints_out[endpoint], (uint8_t *)report, size, TIME_IMMEDIATE);\n}\n\nvoid send_keyboard(report_keyboard_t *report) {\n    /* If we're in Boot Protocol, don't send any report ID or other funky fields */\n    if (usb_device_state_get_protocol() == USB_PROTOCOL_BOOT) {\n        send_report(USB_ENDPOINT_IN_KEYBOARD, &report->mods, 8);\n    } else {\n        send_report(USB_ENDPOINT_IN_KEYBOARD, report, KEYBOARD_REPORT_SIZE);\n    }\n}\n\nvoid send_nkro(report_nkro_t *report) {\n#ifdef NKRO_ENABLE\n    send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_nkro_t));\n#endif\n}\n\n/* ---------------------------------------------------------\n *                     Mouse functions\n * ---------------------------------------------------------\n */\n\nvoid send_mouse(report_mouse_t *report) {\n#ifdef MOUSE_ENABLE\n    send_report(USB_ENDPOINT_IN_MOUSE, report, sizeof(report_mouse_t));\n#endif\n}\n\n/* ---------------------------------------------------------\n *                   Extrakey functions\n * ---------------------------------------------------------\n */\n\nvoid send_extra(report_extra_t *report) {\n#ifdef EXTRAKEY_ENABLE\n    send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_extra_t));\n#endif\n}\n\nvoid send_programmable_button(report_programmable_button_t *report) {\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\n    send_report(USB_ENDPOINT_IN_SHARED, report, sizeof(report_programmable_button_t));\n#endif\n}\n\nvoid send_joystick(report_joystick_t *report) {\n#ifdef JOYSTICK_ENABLE\n    send_report(USB_ENDPOINT_IN_JOYSTICK, report, sizeof(report_joystick_t));\n#endif\n}\n\nvoid send_digitizer(report_digitizer_t *report) {\n#ifdef DIGITIZER_ENABLE\n    send_report(USB_ENDPOINT_IN_DIGITIZER, report, sizeof(report_digitizer_t));\n#endif\n}\n\n/* ---------------------------------------------------------\n *                   Console functions\n * ---------------------------------------------------------\n */\n\n#ifdef CONSOLE_ENABLE\n\nint8_t sendchar(uint8_t c) {\n    return (int8_t)send_report_buffered(USB_ENDPOINT_IN_CONSOLE, &c, sizeof(uint8_t));\n}\n\nvoid console_task(void) {\n    flush_report_buffered(USB_ENDPOINT_IN_CONSOLE, true);\n}\n\n#endif /* CONSOLE_ENABLE */\n\n#ifdef RAW_ENABLE\nvoid send_raw_hid(uint8_t *data, uint8_t length) {\n    if (length != RAW_EPSIZE) {\n        return;\n    }\n    send_report(USB_ENDPOINT_IN_RAW, data, length);\n}\n\nvoid raw_hid_task(void) {\n    uint8_t buffer[RAW_EPSIZE];\n    while (receive_report(USB_ENDPOINT_OUT_RAW, buffer, sizeof(buffer))) {\n        raw_hid_receive(buffer, sizeof(buffer));\n    }\n}\n\n#endif\n\n#ifdef MIDI_ENABLE\n\nvoid send_midi_packet(MIDI_EventPacket_t *event) {\n    send_report(USB_ENDPOINT_IN_MIDI, (uint8_t *)event, sizeof(MIDI_EventPacket_t));\n}\n\nbool recv_midi_packet(MIDI_EventPacket_t *const event) {\n    return receive_report(USB_ENDPOINT_OUT_MIDI, (uint8_t *)event, sizeof(MIDI_EventPacket_t));\n}\n\n#endif\n\n#ifdef VIRTSER_ENABLE\n\n#    include \"hal_usb_cdc.h\"\n/**\n * @brief CDC serial driver configuration structure. Set to 9600 baud, 1 stop bit, no parity, 8 data bits.\n */\nstatic cdc_linecoding_t linecoding = {{0x00, 0x96, 0x00, 0x00}, LC_STOP_1, LC_PARITY_NONE, 8};\n\nbool virtser_usb_request_cb(USBDriver *usbp) {\n    if ((usbp->setup[0] & USB_RTYPE_TYPE_MASK) == USB_RTYPE_TYPE_CLASS) { /* bmRequestType */\n        if (usbp->setup[4] == CCI_INTERFACE) {                            /* wIndex (LSB) */\n            switch (usbp->setup[1]) {                                     /* bRequest */\n                case CDC_GET_LINE_CODING:\n                    usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL);\n                    return true;\n                case CDC_SET_LINE_CODING:\n                    usbSetupTransfer(usbp, (uint8_t *)&linecoding, sizeof(linecoding), NULL);\n                    return true;\n                case CDC_SET_CONTROL_LINE_STATE:\n                    /* Nothing to do, there are no control lines.*/\n                    usbSetupTransfer(usbp, NULL, 0, NULL);\n                    return true;\n                default:\n                    return false;\n            }\n        }\n    }\n\n    return false;\n}\n\nvoid virtser_init(void) {}\n\nvoid virtser_send(const uint8_t byte) {\n    send_report_buffered(USB_ENDPOINT_IN_CDC_DATA, (void *)&byte, sizeof(byte));\n}\n\n__attribute__((weak)) void virtser_recv(uint8_t c) {\n    // Ignore by default\n}\n\nvoid virtser_task(void) {\n    uint8_t buffer[CDC_EPSIZE];\n    while (receive_report(USB_ENDPOINT_OUT_CDC_DATA, buffer, sizeof(buffer))) {\n        for (int i = 0; i < sizeof(buffer); i++) {\n            virtser_recv(buffer[i]);\n        }\n    }\n\n    flush_report_buffered(USB_ENDPOINT_IN_CDC_DATA, false);\n}\n\n#endif\n"
  },
  {
    "path": "tmk_core/protocol/chibios/usb_main.h",
    "content": "// Copyright 2023 Stefan Kerkmann (@KarlK90)\n// Copyright 2020 Ryan (@fauxpark)\n// Copyright 2020 Joel Challis (@zvecr)\n// Copyright 2018 James Laird-Wah\n// Copyright 2016 Fredizzimo\n// Copyright 2016 Giovanni Di Sirio\n// SPDX-License-Identifier: GPL-3.0-or-later OR Apache-2.0\n\n#pragma once\n\n#include <ch.h>\n#include <hal.h>\n\n#include \"usb_device_state.h\"\n#include \"usb_descriptor.h\"\n#include \"usb_driver.h\"\n#include \"usb_endpoints.h\"\n\n/* -------------------------\n * General USB driver header\n * -------------------------\n */\n\n/* The USB driver to use */\n#ifndef USB_DRIVER\n#    define USB_DRIVER USBD1\n#endif // USB_DRIVER\n\n/* Initialize the USB driver and bus */\nvoid init_usb_driver(USBDriver *usbp);\n\n/* Restart the USB driver and bus */\nvoid restart_usb_driver(USBDriver *usbp);\n\nbool send_report(usb_endpoint_in_lut_t endpoint, void *report, size_t size);\n\n/* ---------------\n * USB Event queue\n * ---------------\n */\n\n/* Initialisation of the FIFO */\nvoid usb_event_queue_init(void);\n\n/* Task to dequeue and execute any handlers for the USB events on the main thread */\nvoid usb_event_queue_task(void);\n\n/* --------------\n * Console header\n * --------------\n */\n\n#ifdef CONSOLE_ENABLE\n\n/* Putchar over the USB console */\nint8_t sendchar(uint8_t c);\n\n#endif /* CONSOLE_ENABLE */\n\n/* --------------\n * Virtser header\n * --------------\n */\n\n#if defined(VIRTSER_ENABLE)\n\nbool virtser_usb_request_cb(USBDriver *usbp);\n\n#endif\n"
  },
  {
    "path": "tmk_core/protocol/chibios/usb_report_handling.c",
    "content": "// Copyright 2023 Stefan Kerkmann (@KarlK90)\n// SPDX-License-Identifier: GPL-3.0-or-later\n\n#include <string.h>\n#include <stdint.h>\n#include <stdbool.h>\n\n#include \"usb_report_handling.h\"\n#include \"usb_endpoints.h\"\n#include \"usb_main.h\"\n#include \"usb_types.h\"\n#include \"usb_driver.h\"\n#include \"report.h\"\n\nextern usb_endpoint_in_t     usb_endpoints_in[USB_ENDPOINT_IN_COUNT];\nextern usb_endpoint_in_lut_t usb_endpoint_interface_lut[TOTAL_INTERFACES];\n\nvoid usb_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length) {\n    if (*reports == NULL) {\n        return;\n    }\n\n    (*reports)->last_report = chVTGetSystemTimeX();\n    (*reports)->length      = length;\n    memcpy(&(*reports)->data, data, length);\n}\n\nvoid usb_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report) {\n    (void)report_id;\n    if (*reports == NULL) {\n        return;\n    }\n\n    report->length = (*reports)->length;\n    memcpy(&report->data, &(*reports)->data, report->length);\n}\n\nvoid usb_reset_report(usb_fs_report_t **reports) {\n    if (*reports == NULL) {\n        return;\n    }\n\n    memset(&(*reports)->data, 0, (*reports)->length);\n    (*reports)->idle_rate   = 0;\n    (*reports)->last_report = 0;\n}\n\nvoid usb_shared_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length) {\n    uint8_t report_id = data[0];\n\n    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {\n        return;\n    }\n\n    reports[report_id]->last_report = chVTGetSystemTimeX();\n    reports[report_id]->length      = length;\n    memcpy(&reports[report_id]->data, data, length);\n}\n\nvoid usb_shared_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report) {\n    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {\n        return;\n    }\n\n    report->length = reports[report_id]->length;\n    memcpy(&report->data, &reports[report_id]->data, report->length);\n}\n\nvoid usb_shared_reset_report(usb_fs_report_t **reports) {\n    for (int i = 0; i <= REPORT_ID_COUNT; i++) {\n        if (reports[i] == NULL) {\n            continue;\n        }\n        memset(&reports[i]->data, 0, reports[i]->length);\n        reports[i]->idle_rate   = 0;\n        reports[i]->last_report = 0;\n    }\n}\n\nbool usb_get_report_cb(USBDriver *driver) {\n    usb_control_request_t *setup     = (usb_control_request_t *)driver->setup;\n    uint8_t                interface = setup->wIndex;\n    uint8_t                report_id = setup->wValue.lbyte;\n\n    static usb_fs_report_t report;\n\n    if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {\n        return false;\n    }\n\n    usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];\n\n    if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {\n        return false;\n    }\n\n    usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;\n\n    if (report_storage == NULL) {\n        return false;\n    }\n\n    report_storage->get_report(report_storage->reports, report_id, &report);\n\n    usbSetupTransfer(driver, (uint8_t *)report.data, report.length, NULL);\n\n    return true;\n}\n\nstatic bool run_idle_task = false;\n\nvoid usb_set_idle_rate(usb_fs_report_t **reports, uint8_t report_id, uint8_t idle_rate) {\n    (void)report_id;\n\n    if (*reports == NULL) {\n        return;\n    }\n\n    (*reports)->idle_rate = idle_rate * 4;\n\n    run_idle_task |= idle_rate != 0;\n}\n\nuint8_t usb_get_idle_rate(usb_fs_report_t **reports, uint8_t report_id) {\n    (void)report_id;\n\n    if (*reports == NULL) {\n        return 0;\n    }\n\n    return (*reports)->idle_rate / 4;\n}\n\nbool usb_idle_timer_elapsed(usb_fs_report_t **reports, uint8_t report_id) {\n    (void)report_id;\n\n    if (*reports == NULL) {\n        return false;\n    }\n\n    osalSysLock();\n    time_msecs_t idle_rate   = (*reports)->idle_rate;\n    systime_t    last_report = (*reports)->last_report;\n    osalSysUnlock();\n\n    if (idle_rate == 0) {\n        return false;\n    }\n\n    return chTimeI2MS(chVTTimeElapsedSinceX(last_report)) >= idle_rate;\n}\n\nvoid usb_shared_set_idle_rate(usb_fs_report_t **reports, uint8_t report_id, uint8_t idle_rate) {\n    // USB spec demands that a report_id of 0 would set the idle rate for all\n    // reports of that endpoint, but this can easily lead to resource\n    // exhaustion, therefore we deliberalty break the spec at this point.\n    if (report_id == 0 || report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {\n        return;\n    }\n\n    reports[report_id]->idle_rate = idle_rate * 4;\n\n    run_idle_task |= idle_rate != 0;\n}\n\nuint8_t usb_shared_get_idle_rate(usb_fs_report_t **reports, uint8_t report_id) {\n    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {\n        return 0;\n    }\n\n    return reports[report_id]->idle_rate / 4;\n}\n\nbool usb_shared_idle_timer_elapsed(usb_fs_report_t **reports, uint8_t report_id) {\n    if (report_id > REPORT_ID_COUNT || reports[report_id] == NULL) {\n        return false;\n    }\n\n    osalSysLock();\n    time_msecs_t idle_rate   = reports[report_id]->idle_rate;\n    systime_t    last_report = reports[report_id]->last_report;\n    osalSysUnlock();\n\n    if (idle_rate == 0) {\n        return false;\n    }\n\n    return chTimeI2MS(chVTTimeElapsedSinceX(last_report)) >= idle_rate;\n}\n\nvoid usb_idle_task(void) {\n    if (!run_idle_task) {\n        return;\n    }\n\n    static usb_fs_report_t report;\n    bool                   non_zero_idle_rate_found = false;\n\n    for (int ep = 0; ep < USB_ENDPOINT_IN_COUNT; ep++) {\n        usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;\n\n        if (report_storage == NULL) {\n            continue;\n        }\n\n#if defined(SHARED_EP_ENABLE)\n        if (ep == USB_ENDPOINT_IN_SHARED) {\n            for (int report_id = 1; report_id <= REPORT_ID_COUNT; report_id++) {\n                osalSysLock();\n                non_zero_idle_rate_found |= report_storage->get_idle(report_storage->reports, report_id) != 0;\n                osalSysUnlock();\n\n                if (usb_endpoint_in_is_inactive(&usb_endpoints_in[ep]) && report_storage->idle_timer_elasped(report_storage->reports, report_id)) {\n                    osalSysLock();\n                    report_storage->get_report(report_storage->reports, report_id, &report);\n                    osalSysUnlock();\n                    send_report(ep, &report.data, report.length);\n                }\n            }\n            continue;\n        }\n#endif\n\n        osalSysLock();\n        non_zero_idle_rate_found |= report_storage->get_idle(report_storage->reports, 0) != 0;\n        osalSysUnlock();\n\n        if (usb_endpoint_in_is_inactive(&usb_endpoints_in[ep]) && report_storage->idle_timer_elasped(report_storage->reports, 0)) {\n            osalSysLock();\n            report_storage->get_report(report_storage->reports, 0, &report);\n            osalSysUnlock();\n            send_report(ep, &report.data, report.length);\n        }\n    }\n\n    run_idle_task = non_zero_idle_rate_found;\n}\n\nbool usb_get_idle_cb(USBDriver *driver) {\n    usb_control_request_t *setup     = (usb_control_request_t *)driver->setup;\n    uint8_t                interface = setup->wIndex;\n    uint8_t                report_id = setup->wValue.lbyte;\n\n    static uint8_t _Alignas(4) idle_rate;\n\n    if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {\n        return false;\n    }\n\n    usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];\n\n    if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {\n        return false;\n    }\n\n    usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;\n\n    if (report_storage == NULL) {\n        return false;\n    }\n\n    idle_rate = report_storage->get_idle(report_storage->reports, report_id);\n\n    usbSetupTransfer(driver, &idle_rate, 1, NULL);\n\n    return true;\n}\n\nbool usb_set_idle_cb(USBDriver *driver) {\n    usb_control_request_t *setup     = (usb_control_request_t *)driver->setup;\n    uint8_t                interface = setup->wIndex;\n    uint8_t                report_id = setup->wValue.lbyte;\n    uint8_t                idle_rate = setup->wValue.hbyte;\n\n    if (!IS_VALID_INTERFACE(interface) || !IS_VALID_REPORT_ID(report_id)) {\n        return false;\n    }\n\n    usb_endpoint_in_lut_t ep = usb_endpoint_interface_lut[interface];\n\n    if (!IS_VALID_USB_ENDPOINT_IN_LUT(ep)) {\n        return false;\n    }\n\n    usb_report_storage_t *report_storage = usb_endpoints_in[ep].report_storage;\n\n    if (report_storage == NULL) {\n        return false;\n    }\n\n    report_storage->set_idle(report_storage->reports, report_id, idle_rate);\n\n    usbSetupTransfer(driver, NULL, 0, NULL);\n\n    return true;\n}\n"
  },
  {
    "path": "tmk_core/protocol/chibios/usb_report_handling.h",
    "content": "// Copyright 2023 Stefan Kerkmann (@KarlK90)\n// SPDX-License-Identifier: GPL-3.0-or-later\n\n#pragma once\n\n#include <ch.h>\n#include <hal.h>\n#include <stdint.h>\n\n#include \"timer.h\"\n\ntypedef struct {\n    time_msecs_t idle_rate;\n    systime_t    last_report;\n    uint8_t      data[64];\n    size_t       length;\n} usb_fs_report_t;\n\ntypedef struct {\n    usb_fs_report_t **reports;\n    const void (*get_report)(usb_fs_report_t **, uint8_t, usb_fs_report_t *);\n    const void (*set_report)(usb_fs_report_t **, const uint8_t *, size_t);\n    const void (*reset_report)(usb_fs_report_t **);\n    const void (*set_idle)(usb_fs_report_t **, uint8_t, uint8_t);\n    const uint8_t (*get_idle)(usb_fs_report_t **, uint8_t);\n    const bool (*idle_timer_elasped)(usb_fs_report_t **, uint8_t);\n} usb_report_storage_t;\n\n#define QMK_USB_REPORT_STROAGE_ENTRY(_report_id, _report_size) [_report_id] = &((usb_fs_report_t){.data = {[0] = _report_id}, .length = _report_size})\n\n#define QMK_USB_REPORT_STORAGE(_get_report, _set_report, _reset_report, _get_idle, _set_idle, _idle_timer_elasped, _report_count, _reports...) \\\n    &((usb_report_storage_t){                                                                                                                  \\\n        .reports            = (_Alignas(4) usb_fs_report_t *[_report_count]){_reports},                                                        \\\n        .get_report         = _get_report,                                                                                                     \\\n        .set_report         = _set_report,                                                                                                     \\\n        .reset_report       = _reset_report,                                                                                                   \\\n        .get_idle           = _get_idle,                                                                                                       \\\n        .set_idle           = _set_idle,                                                                                                       \\\n        .idle_timer_elasped = _idle_timer_elasped,                                                                                             \\\n    })\n\n#define QMK_USB_REPORT_STORAGE_DEFAULT(_report_length)                        \\\n    QMK_USB_REPORT_STORAGE(&usb_get_report,         /* _get_report */         \\\n                           &usb_set_report,         /* _set_report */         \\\n                           &usb_reset_report,       /* _reset_report */       \\\n                           &usb_get_idle_rate,      /* _get_idle */           \\\n                           &usb_set_idle_rate,      /* _set_idle */           \\\n                           &usb_idle_timer_elapsed, /* _idle_timer_elasped */ \\\n                           1,                       /* _report_count */       \\\n                           QMK_USB_REPORT_STROAGE_ENTRY(0, _report_length))\n\n// USB HID SET_REPORT and GET_REPORT  handling functions\nvoid usb_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length);\nvoid usb_shared_set_report(usb_fs_report_t **reports, const uint8_t *data, size_t length);\n\nvoid usb_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report);\nvoid usb_shared_get_report(usb_fs_report_t **reports, uint8_t report_id, usb_fs_report_t *report);\n\nvoid usb_reset_report(usb_fs_report_t **reports);\nvoid usb_shared_reset_report(usb_fs_report_t **reports);\n\nbool usb_get_report_cb(USBDriver *driver);\n\n// USB HID SET_IDLE and GET_IDLE handling functions\nvoid usb_idle_task(void);\n\nvoid usb_set_idle_rate(usb_fs_report_t **timers, uint8_t report_id, uint8_t idle_rate);\nvoid usb_shared_set_idle_rate(usb_fs_report_t **timers, uint8_t report_id, uint8_t idle_rate);\n\nuint8_t usb_get_idle_rate(usb_fs_report_t **timers, uint8_t report_id);\nuint8_t usb_shared_get_idle_rate(usb_fs_report_t **timers, uint8_t report_id);\n\nbool usb_idle_timer_elapsed(usb_fs_report_t **timers, uint8_t report_id);\nbool usb_shared_idle_timer_elapsed(usb_fs_report_t **timers, uint8_t report_id);\n\nbool usb_get_idle_cb(USBDriver *driver);\nbool usb_set_idle_cb(USBDriver *driver);\n"
  },
  {
    "path": "tmk_core/protocol/chibios/usb_util.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include <hal.h>\n#include \"usb_main.h\"\n#include \"usb_util.h\"\n\nvoid usb_disconnect(void) {\n    usbDisconnectBus(&USB_DRIVER);\n    usbStop(&USB_DRIVER);\n}\n\nbool usb_connected_state(void) {\n    return usbGetDriverStateI(&USB_DRIVER) == USB_ACTIVE;\n}\n"
  },
  {
    "path": "tmk_core/protocol/host.c",
    "content": "/*\nCopyright 2011,2012 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include <stdint.h>\n#include \"keyboard.h\"\n#include \"keycode.h\"\n#include \"host.h\"\n#include \"util.h\"\n#include \"debug.h\"\n#include \"usb_device_state.h\"\n\n#ifdef DIGITIZER_ENABLE\n#    include \"digitizer.h\"\n#endif\n\n#ifdef JOYSTICK_ENABLE\n#    include \"joystick.h\"\n#endif\n\n#ifdef CONNECTION_ENABLE\n#    include \"connection.h\"\n#endif\n\n#ifdef BLUETOOTH_ENABLE\n#    include \"bluetooth.h\"\n\nstatic void bluetooth_send_extra(report_extra_t *report) {\n    switch (report->report_id) {\n        case REPORT_ID_SYSTEM:\n            bluetooth_send_system(report->usage);\n            return;\n        case REPORT_ID_CONSUMER:\n            bluetooth_send_consumer(report->usage);\n            return;\n    }\n}\n\nhost_driver_t bt_driver = {\n    .keyboard_leds = bluetooth_keyboard_leds,\n    .send_keyboard = bluetooth_send_keyboard,\n    .send_nkro     = bluetooth_send_nkro,\n    .send_mouse    = bluetooth_send_mouse,\n    .send_extra    = bluetooth_send_extra,\n#    ifdef RAW_ENABLE\n    .send_raw_hid = bluetooth_send_raw_hid,\n#    endif\n};\n#endif\n\n#ifdef NKRO_ENABLE\n#    include \"keycode_config.h\"\nextern keymap_config_t keymap_config;\n#endif\n\nstatic host_driver_t *driver;\nstatic uint16_t       last_system_usage   = 0;\nstatic uint16_t       last_consumer_usage = 0;\n\nvoid host_set_driver(host_driver_t *d) {\n    driver = d;\n}\n\nhost_driver_t *host_get_driver(void) {\n    return driver;\n}\n\nstatic host_driver_t *host_get_active_driver(void) {\n#ifdef CONNECTION_ENABLE\n    switch (connection_get_host()) {\n#    ifdef BLUETOOTH_ENABLE\n        case CONNECTION_HOST_BLUETOOTH:\n            return &bt_driver;\n#    endif\n        case CONNECTION_HOST_NONE:\n            return NULL;\n        default:\n            break;\n    }\n#endif\n    return driver;\n}\n\nbool host_can_send_nkro(void) {\n#ifdef CONNECTION_ENABLE\n    switch (connection_get_host()) {\n#    ifdef BLUETOOTH_ENABLE\n        case CONNECTION_HOST_BLUETOOTH:\n            return bluetooth_can_send_nkro();\n#    endif\n        case CONNECTION_HOST_NONE:\n            return false;\n        default:\n            break;\n    }\n#endif\n\n    return usb_device_state_get_protocol() == USB_PROTOCOL_REPORT;\n}\n\n#ifdef SPLIT_KEYBOARD\nuint8_t split_led_state = 0;\nvoid    set_split_host_keyboard_leds(uint8_t led_state) {\n    split_led_state = led_state;\n}\n#endif\n\nuint8_t host_keyboard_leds(void) {\n#ifdef SPLIT_KEYBOARD\n    if (!is_keyboard_master()) return split_led_state;\n#endif\n\n    host_driver_t *driver = host_get_active_driver();\n    if (!driver || !driver->keyboard_leds) return 0;\n\n    return (*driver->keyboard_leds)();\n}\n\nled_t host_keyboard_led_state(void) {\n    return (led_t)host_keyboard_leds();\n}\n\n/* send report */\nvoid host_keyboard_send(report_keyboard_t *report) {\n    host_driver_t *driver = host_get_active_driver();\n    if (!driver || !driver->send_keyboard) return;\n\n#ifdef KEYBOARD_SHARED_EP\n    report->report_id = REPORT_ID_KEYBOARD;\n#endif\n    (*driver->send_keyboard)(report);\n\n    if (debug_keyboard) {\n        dprintf(\"keyboard_report: %02X | \", report->mods);\n        for (uint8_t i = 0; i < KEYBOARD_REPORT_KEYS; i++) {\n            dprintf(\"%02X \", report->keys[i]);\n        }\n        dprint(\"\\n\");\n    }\n}\n\nvoid host_nkro_send(report_nkro_t *report) {\n    host_driver_t *driver = host_get_active_driver();\n    if (!driver || !driver->send_nkro) return;\n\n    report->report_id = REPORT_ID_NKRO;\n    (*driver->send_nkro)(report);\n\n    if (debug_keyboard) {\n        dprintf(\"nkro_report: %02X | \", report->mods);\n        for (uint8_t i = 0; i < NKRO_REPORT_BITS; i++) {\n            dprintf(\"%02X \", report->bits[i]);\n        }\n        dprint(\"\\n\");\n    }\n}\n\nvoid host_mouse_send(report_mouse_t *report) {\n    host_driver_t *driver = host_get_active_driver();\n    if (!driver || !driver->send_mouse) return;\n\n#ifdef MOUSE_SHARED_EP\n    report->report_id = REPORT_ID_MOUSE;\n#endif\n#ifdef MOUSE_EXTENDED_REPORT\n    // clip and copy to Boot protocol XY\n    report->boot_x = (report->x > 127) ? 127 : ((report->x < -127) ? -127 : report->x);\n    report->boot_y = (report->y > 127) ? 127 : ((report->y < -127) ? -127 : report->y);\n#endif\n    (*driver->send_mouse)(report);\n}\n\nvoid host_system_send(uint16_t usage) {\n    if (usage == last_system_usage) return;\n    last_system_usage = usage;\n\n    host_driver_t *driver = host_get_active_driver();\n    if (!driver || !driver->send_extra) return;\n\n    report_extra_t report = {\n        .report_id = REPORT_ID_SYSTEM,\n        .usage     = usage,\n    };\n    (*driver->send_extra)(&report);\n}\n\nvoid host_consumer_send(uint16_t usage) {\n    if (usage == last_consumer_usage) return;\n    last_consumer_usage = usage;\n\n    host_driver_t *driver = host_get_active_driver();\n    if (!driver || !driver->send_extra) return;\n\n    report_extra_t report = {\n        .report_id = REPORT_ID_CONSUMER,\n        .usage     = usage,\n    };\n    (*driver->send_extra)(&report);\n}\n\n#ifdef JOYSTICK_ENABLE\nvoid host_joystick_send(joystick_t *joystick) {\n    if (!driver) return;\n\n    report_joystick_t report = {\n#    ifdef JOYSTICK_SHARED_EP\n        .report_id = REPORT_ID_JOYSTICK,\n#    endif\n#    if JOYSTICK_AXIS_COUNT > 0\n        .axes =\n            {\n                joystick->axes[0],\n\n#        if JOYSTICK_AXIS_COUNT >= 2\n                joystick->axes[1],\n#        endif\n#        if JOYSTICK_AXIS_COUNT >= 3\n                joystick->axes[2],\n#        endif\n#        if JOYSTICK_AXIS_COUNT >= 4\n                joystick->axes[3],\n#        endif\n#        if JOYSTICK_AXIS_COUNT >= 5\n                joystick->axes[4],\n#        endif\n#        if JOYSTICK_AXIS_COUNT >= 6\n                joystick->axes[5],\n#        endif\n            },\n#    endif\n\n#    ifdef JOYSTICK_HAS_HAT\n        .hat = joystick->hat,\n#    endif\n\n#    if JOYSTICK_BUTTON_COUNT > 0\n        .buttons =\n            {\n                joystick->buttons[0],\n\n#        if JOYSTICK_BUTTON_COUNT > 8\n                joystick->buttons[1],\n#        endif\n#        if JOYSTICK_BUTTON_COUNT > 16\n                joystick->buttons[2],\n#        endif\n#        if JOYSTICK_BUTTON_COUNT > 24\n                joystick->buttons[3],\n#        endif\n            },\n#    endif\n    };\n\n    send_joystick(&report);\n}\n#endif\n\n__attribute__((weak)) void send_joystick(report_joystick_t *report) {}\n\n#ifdef DIGITIZER_ENABLE\nvoid host_digitizer_send(digitizer_t *digitizer) {\n    report_digitizer_t report = {\n#    ifdef DIGITIZER_SHARED_EP\n        .report_id = REPORT_ID_DIGITIZER,\n#    endif\n        .in_range = digitizer->in_range,\n        .tip      = digitizer->tip,\n        .barrel   = digitizer->barrel,\n        .x        = (uint16_t)(digitizer->x * 0x7FFF),\n        .y        = (uint16_t)(digitizer->y * 0x7FFF),\n    };\n\n    send_digitizer(&report);\n}\n#endif\n\n__attribute__((weak)) void send_digitizer(report_digitizer_t *report) {}\n\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\nvoid host_programmable_button_send(uint32_t data) {\n    report_programmable_button_t report = {\n        .report_id = REPORT_ID_PROGRAMMABLE_BUTTON,\n        .usage     = data,\n    };\n\n    send_programmable_button(&report);\n}\n#endif\n\n__attribute__((weak)) void send_programmable_button(report_programmable_button_t *report) {}\n\n#ifdef RAW_ENABLE\nvoid host_raw_hid_send(uint8_t *data, uint8_t length) {\n    host_driver_t *driver = host_get_active_driver();\n    if (!driver || !driver->send_raw_hid) return;\n\n    (*driver->send_raw_hid)(data, length);\n}\n#endif\n\nuint16_t host_last_system_usage(void) {\n    return last_system_usage;\n}\n\nuint16_t host_last_consumer_usage(void) {\n    return last_consumer_usage;\n}\n"
  },
  {
    "path": "tmk_core/protocol/host.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"report.h\"\n#include \"host_driver.h\"\n#include \"led.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* host driver */\nvoid           host_set_driver(host_driver_t *driver);\nhost_driver_t *host_get_driver(void);\n\n/* host driver interface */\nbool    host_can_send_nkro(void);\nuint8_t host_keyboard_leds(void);\nled_t   host_keyboard_led_state(void);\nvoid    host_keyboard_send(report_keyboard_t *report);\nvoid    host_nkro_send(report_nkro_t *report);\nvoid    host_mouse_send(report_mouse_t *report);\nvoid    host_system_send(uint16_t usage);\nvoid    host_consumer_send(uint16_t usage);\nvoid    host_programmable_button_send(uint32_t data);\nvoid    host_raw_hid_send(uint8_t *data, uint8_t length);\n\nuint16_t host_last_system_usage(void);\nuint16_t host_last_consumer_usage(void);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "tmk_core/protocol/host_driver.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include \"report.h\"\n#ifdef MIDI_ENABLE\n#    include \"midi.h\"\n#endif\n\ntypedef struct {\n    uint8_t (*keyboard_leds)(void);\n    void (*send_keyboard)(report_keyboard_t *);\n    void (*send_nkro)(report_nkro_t *);\n    void (*send_mouse)(report_mouse_t *);\n    void (*send_extra)(report_extra_t *);\n#ifdef RAW_ENABLE\n    void (*send_raw_hid)(uint8_t *, uint8_t);\n#endif\n} host_driver_t;\n\nvoid send_joystick(report_joystick_t *report);\nvoid send_digitizer(report_digitizer_t *report);\nvoid send_programmable_button(report_programmable_button_t *report);\n"
  },
  {
    "path": "tmk_core/protocol/lufa/lufa.c",
    "content": "/*\n * Copyright 2012 Jun Wako <wakojun@gmail.com>\n * This file is based on:\n *     LUFA-120219/Demos/Device/Lowlevel/KeyboardMouse\n *     LUFA-120219/Demos/Device/Lowlevel/GenericHID\n */\n\n/*\n             LUFA Library\n     Copyright (C) Dean Camera, 2012.\n\n  dean [at] fourwalledcubicle [dot] com\n           www.lufa-lib.org\n*/\n\n/*\n  Copyright 2012  Dean Camera (dean [at] fourwalledcubicle [dot] com)\n  Copyright 2010  Denver Gingerich (denver [at] ossguy [dot] com)\n\n  Permission to use, copy, modify, distribute, and sell this\n  software and its documentation for any purpose is hereby granted\n  without fee, provided that the above copyright notice appear in\n  all copies and that both that the copyright notice and this\n  permission notice and warranty disclaimer appear in supporting\n  documentation, and that the name of the author not be used in\n  advertising or publicity pertaining to distribution of the\n  software without specific, written prior permission.\n\n  The author disclaim all warranties with regard to this\n  software, including all implied warranties of merchantability\n  and fitness.  In no event shall the author be liable for any\n  special, indirect or consequential damages or any damages\n  whatsoever resulting from loss of use, data or profits, whether\n  in an action of contract, negligence or other tortious action,\n  arising out of or in connection with the use or performance of\n  this software.\n*/\n\n#include \"report.h\"\n#include \"host.h\"\n#include \"host_driver.h\"\n#include \"keyboard.h\"\n#include \"action.h\"\n#include \"led.h\"\n#include \"sendchar.h\"\n#include \"debug.h\"\n#ifdef SLEEP_LED_ENABLE\n#    include \"sleep_led.h\"\n#endif\n#include \"suspend.h\"\n#include \"wait.h\"\n\n#include \"usb_descriptor.h\"\n#include \"lufa.h\"\n#include \"usb_device_state.h\"\n#include <util/atomic.h>\n\n#ifdef VIRTSER_ENABLE\n#    include \"virtser.h\"\n#endif\n\n#ifdef MIDI_ENABLE\n#    include \"qmk_midi.h\"\n#endif\n\n#ifdef RAW_ENABLE\n#    include \"raw_hid.h\"\n#endif\n\n#ifdef WAIT_FOR_USB\n// TODO: Remove backwards compatibility with old define\n#    define USB_WAIT_FOR_ENUMERATION\n#endif\n\nstatic report_keyboard_t keyboard_report_sent;\n\n/* Host driver */\nstatic void send_keyboard(report_keyboard_t *report);\nstatic void send_nkro(report_nkro_t *report);\nstatic void send_mouse(report_mouse_t *report);\nstatic void send_extra(report_extra_t *report);\n#ifdef RAW_ENABLE\nstatic void send_raw_hid(uint8_t *data, uint8_t length);\n#endif\n\nhost_driver_t lufa_driver = {\n    .keyboard_leds = usb_device_state_get_leds,\n    .send_keyboard = send_keyboard,\n    .send_nkro     = send_nkro,\n    .send_mouse    = send_mouse,\n    .send_extra    = send_extra,\n#ifdef RAW_ENABLE\n    .send_raw_hid = send_raw_hid,\n#endif\n};\n\nbool send_report(uint8_t endpoint, void *report, size_t size) {\n    uint8_t timeout = 255;\n\n    if (USB_DeviceState != DEVICE_STATE_Configured) return false;\n\n    Endpoint_SelectEndpoint(endpoint);\n\n    /* Check if write ready for a polling interval around 10ms */\n    while (timeout-- && !Endpoint_IsReadWriteAllowed()) {\n        _delay_us(40);\n    }\n    if (!Endpoint_IsReadWriteAllowed()) return false;\n\n    uint8_t report_status = Endpoint_Write_Stream_LE(report, size, NULL);\n    Endpoint_ClearIN();\n    return (bool)report_status;\n}\n\n#ifdef VIRTSER_ENABLE\n// clang-format off\n\nUSB_ClassInfo_CDC_Device_t cdc_device = {\n    .Config = {\n        .ControlInterfaceNumber = CCI_INTERFACE,\n        .DataINEndpoint = {\n            .Address            = (CDC_IN_EPNUM | ENDPOINT_DIR_IN),\n            .Size               = CDC_EPSIZE,\n            .Banks              = 1\n        },\n        .DataOUTEndpoint = {\n            .Address            = (CDC_OUT_EPNUM | ENDPOINT_DIR_OUT),\n            .Size               = CDC_EPSIZE,\n            .Banks              = 1\n        },\n        .NotificationEndpoint = {\n            .Address            = (CDC_NOTIFICATION_EPNUM | ENDPOINT_DIR_IN),\n            .Size               = CDC_NOTIFICATION_EPSIZE,\n            .Banks              = 1\n        }\n    }\n};\n\n// clang-format on\n#endif\n\n#ifdef RAW_ENABLE\n\n/** \\brief Raw HID Send\n *\n * FIXME: Needs doc\n */\nstatic void send_raw_hid(uint8_t *data, uint8_t length) {\n    if (length != RAW_EPSIZE) return;\n    send_report(RAW_IN_EPNUM, data, RAW_EPSIZE);\n}\n\n/** \\brief Raw HID Task\n *\n * FIXME: Needs doc\n */\nvoid raw_hid_task(void) {\n    // Create a temporary buffer to hold the read in data from the host\n    uint8_t data[RAW_EPSIZE];\n    bool    data_read = false;\n\n    // Device must be connected and configured for the task to run\n    if (USB_DeviceState != DEVICE_STATE_Configured) return;\n\n    Endpoint_SelectEndpoint(RAW_OUT_EPNUM);\n\n    // Check to see if a packet has been sent from the host\n    if (Endpoint_IsOUTReceived()) {\n        // Check to see if the packet contains data\n        if (Endpoint_IsReadWriteAllowed()) {\n            /* Read data */\n            Endpoint_Read_Stream_LE(data, sizeof(data), NULL);\n            data_read = true;\n        }\n\n        // Finalize the stream transfer to receive the last packet\n        Endpoint_ClearOUT();\n\n        if (data_read) {\n            raw_hid_receive(data, sizeof(data));\n        }\n    }\n}\n#endif\n\n/*******************************************************************************\n * Console\n ******************************************************************************/\n#ifdef CONSOLE_ENABLE\n/** \\brief Console Tasks\n *\n * FIXME: Needs doc\n */\nstatic void console_flush_task(void) {\n    /* Device must be connected and configured for the task to run */\n    if (USB_DeviceState != DEVICE_STATE_Configured) return;\n\n    uint8_t ep = Endpoint_GetCurrentEndpoint();\n\n    /* IN packet */\n    Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);\n    if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {\n        Endpoint_SelectEndpoint(ep);\n        return;\n    }\n\n    // fill empty bank\n    while (Endpoint_IsReadWriteAllowed())\n        Endpoint_Write_8(0);\n\n    // flush sendchar packet\n    if (Endpoint_IsINReady()) {\n        Endpoint_ClearIN();\n    }\n\n    Endpoint_SelectEndpoint(ep);\n}\n\nvoid console_task(void) {\n    // do nothing\n}\n#endif\n\n/*******************************************************************************\n * USB Events\n ******************************************************************************/\n/*\n * Event Order of Plug in:\n * 0) EVENT_USB_Device_Connect\n * 1) EVENT_USB_Device_Suspend\n * 2) EVENT_USB_Device_Reset\n * 3) EVENT_USB_Device_Wake\n */\n/** \\brief Event USB Device Connect\n *\n * FIXME: Needs doc\n */\nvoid EVENT_USB_Device_Connect(void) {\n    print(\"[C]\");\n    /* For battery powered device */\n    if (!USB_IsInitialized) {\n        USB_Disable();\n        USB_Init();\n        USB_Device_EnableSOFEvents();\n    }\n}\n\n/** \\brief Event USB Device Connect\n *\n * FIXME: Needs doc\n */\nvoid EVENT_USB_Device_Disconnect(void) {\n    print(\"[D]\");\n    /* For battery powered device */\n    USB_IsInitialized = false;\n    /* TODO: This doesn't work. After several plug in/outs can not be enumerated.\n        if (USB_IsInitialized) {\n            USB_Disable();  // Disable all interrupts\n        USB_Controller_Enable();\n            USB_INT_Enable(USB_INT_VBUSTI);\n        }\n    */\n}\n\n/** \\brief Event USB Device Connect\n *\n * FIXME: Needs doc\n */\nvoid EVENT_USB_Device_Reset(void) {\n    print(\"[R]\");\n    usb_device_state_set_reset();\n    usb_device_state_set_protocol(USB_PROTOCOL_REPORT);\n}\n\n/** \\brief Event USB Device Connect\n *\n * FIXME: Needs doc\n */\nvoid EVENT_USB_Device_Suspend(void) {\n    print(\"[S]\");\n    usb_device_state_set_suspend(USB_Device_ConfigurationNumber != 0, USB_Device_ConfigurationNumber);\n\n#ifdef SLEEP_LED_ENABLE\n    sleep_led_enable();\n#endif\n}\n\n/** \\brief Event USB Device Connect\n *\n * FIXME: Needs doc\n */\nvoid EVENT_USB_Device_WakeUp(void) {\n    print(\"[W]\");\n#if defined(NO_USB_STARTUP_CHECK)\n    suspend_wakeup_init();\n#endif\n\n    usb_device_state_set_resume(USB_DeviceState == DEVICE_STATE_Configured, USB_Device_ConfigurationNumber);\n\n#ifdef SLEEP_LED_ENABLE\n    sleep_led_disable();\n    // NOTE: converters may not accept this\n    led_set(host_keyboard_leds());\n#endif\n}\n\n#ifdef CONSOLE_ENABLE\nstatic bool console_flush = false;\n#    define CONSOLE_FLUSH_SET(b)                \\\n        do {                                    \\\n            ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { \\\n                console_flush = b;              \\\n            }                                   \\\n        } while (0)\n\n/** \\brief Event USB Device Start Of Frame\n *\n * FIXME: Needs doc\n * called every 1ms\n */\nvoid EVENT_USB_Device_StartOfFrame(void) {\n    static uint8_t count;\n    if (++count % 50) return;\n    count = 0;\n\n    if (!console_flush) return;\n    console_flush_task();\n    console_flush = false;\n}\n\n#endif\n\n/** \\brief Event handler for the USB_ConfigurationChanged event.\n *\n * This is fired when the host sets the current configuration of the USB device after enumeration.\n *\n * ATMega32u2 supports dual bank(ping-pong mode) only on endpoint 3 and 4,\n * it is safe to use single bank for all endpoints.\n */\nvoid EVENT_USB_Device_ConfigurationChanged(void) {\n    bool ConfigSuccess = true;\n\n#ifndef KEYBOARD_SHARED_EP\n    /* Setup keyboard report endpoint */\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((KEYBOARD_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, KEYBOARD_EPSIZE, 1);\n#endif\n\n#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)\n    /* Setup mouse report endpoint */\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((MOUSE_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, MOUSE_EPSIZE, 1);\n#endif\n\n#ifdef SHARED_EP_ENABLE\n    /* Setup shared report endpoint */\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((SHARED_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, SHARED_EPSIZE, 1);\n#endif\n\n#ifdef RAW_ENABLE\n    /* Setup raw HID endpoints */\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((RAW_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, RAW_EPSIZE, 1);\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((RAW_OUT_EPNUM | ENDPOINT_DIR_OUT), EP_TYPE_INTERRUPT, RAW_EPSIZE, 1);\n#endif\n\n#ifdef CONSOLE_ENABLE\n    /* Setup console endpoint */\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((CONSOLE_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, CONSOLE_EPSIZE, 1);\n#endif\n\n#ifdef MIDI_ENABLE\n    /* Setup MIDI stream endpoints */\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((MIDI_STREAM_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_BULK, MIDI_STREAM_EPSIZE, 1);\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((MIDI_STREAM_OUT_EPNUM | ENDPOINT_DIR_OUT), EP_TYPE_BULK, MIDI_STREAM_EPSIZE, 1);\n#endif\n\n#ifdef VIRTSER_ENABLE\n    /* Setup virtual serial endpoints */\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((CDC_NOTIFICATION_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, CDC_NOTIFICATION_EPSIZE, 1);\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((CDC_OUT_EPNUM | ENDPOINT_DIR_OUT), EP_TYPE_BULK, CDC_EPSIZE, 1);\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((CDC_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_BULK, CDC_EPSIZE, 1);\n#endif\n\n#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)\n    /* Setup joystick endpoint */\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((JOYSTICK_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, JOYSTICK_EPSIZE, 1);\n#endif\n\n#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)\n    /* Setup digitizer endpoint */\n    ConfigSuccess &= Endpoint_ConfigureEndpoint((DIGITIZER_IN_EPNUM | ENDPOINT_DIR_IN), EP_TYPE_INTERRUPT, DIGITIZER_EPSIZE, 1);\n#endif\n\n    usb_device_state_set_configuration(USB_DeviceState == DEVICE_STATE_Configured, USB_Device_ConfigurationNumber);\n}\n\n/* FIXME: Expose this table in the docs somehow\nAppendix G: HID Request Support Requirements\n\nThe following table enumerates the requests that need to be supported by various types of HID class devices.\n\nDevice type     GetReport   SetReport   GetIdle     SetIdle     GetProtocol SetProtocol\n------------------------------------------------------------------------------------------\nBoot Mouse      Required    Optional    Optional    Optional    Required    Required\nNon-Boot Mouse  Required    Optional    Optional    Optional    Optional    Optional\nBoot Keyboard   Required    Optional    Required    Required    Required    Required\nNon-Boot Keybrd Required    Optional    Required    Required    Optional    Optional\nOther Device    Required    Optional    Optional    Optional    Optional    Optional\n*/\n/** \\brief Event handler for the USB_ControlRequest event.\n *\n *  This is fired before passing along unhandled control requests to the library for processing internally.\n */\nvoid EVENT_USB_Device_ControlRequest(void) {\n    uint8_t *ReportData = NULL;\n    uint8_t  ReportSize = 0;\n\n    /* Handle HID Class specific requests */\n    switch (USB_ControlRequest.bRequest) {\n        case HID_REQ_GetReport:\n            if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) {\n                Endpoint_ClearSETUP();\n\n                // Interface\n                switch (USB_ControlRequest.wIndex) {\n                    case KEYBOARD_INTERFACE:\n                        // TODO: test/check\n                        ReportData = (uint8_t *)&keyboard_report_sent;\n                        ReportSize = sizeof(keyboard_report_sent);\n                        break;\n                }\n\n                /* Write the report data to the control endpoint */\n                Endpoint_Write_Control_Stream_LE(ReportData, ReportSize);\n                Endpoint_ClearOUT();\n            }\n\n            break;\n        case HID_REQ_SetReport:\n            if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) {\n                // Interface\n                switch (USB_ControlRequest.wIndex) {\n                    case KEYBOARD_INTERFACE:\n#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)\n                    case SHARED_INTERFACE:\n#endif\n                        Endpoint_ClearSETUP();\n\n                        while (!(Endpoint_IsOUTReceived())) {\n                            if (USB_DeviceState == DEVICE_STATE_Unattached) return;\n                        }\n\n                        if (Endpoint_BytesInEndpoint() == 2) {\n                            uint8_t report_id = Endpoint_Read_8();\n\n                            if (report_id == REPORT_ID_KEYBOARD || report_id == REPORT_ID_NKRO) {\n                                usb_device_state_set_leds(Endpoint_Read_8());\n                            }\n                        } else {\n                            usb_device_state_set_leds(Endpoint_Read_8());\n                        }\n\n                        Endpoint_ClearOUT();\n                        Endpoint_ClearStatusStage();\n                        break;\n                }\n            }\n\n            break;\n\n        case HID_REQ_GetProtocol:\n            if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) {\n                if (USB_ControlRequest.wIndex == KEYBOARD_INTERFACE) {\n                    Endpoint_ClearSETUP();\n                    while (!(Endpoint_IsINReady()))\n                        ;\n                    Endpoint_Write_8(usb_device_state_get_protocol());\n                    Endpoint_ClearIN();\n                    Endpoint_ClearStatusStage();\n                }\n            }\n\n            break;\n        case HID_REQ_SetProtocol:\n            if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) {\n                if (USB_ControlRequest.wIndex == KEYBOARD_INTERFACE) {\n                    Endpoint_ClearSETUP();\n                    Endpoint_ClearStatusStage();\n\n                    usb_device_state_set_protocol(USB_ControlRequest.wValue & 0xFF);\n                    clear_keyboard();\n                }\n            }\n\n            break;\n        case HID_REQ_SetIdle:\n            if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) {\n                Endpoint_ClearSETUP();\n                Endpoint_ClearStatusStage();\n\n                usb_device_state_set_idle_rate(USB_ControlRequest.wValue >> 8);\n            }\n\n            break;\n        case HID_REQ_GetIdle:\n            if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) {\n                Endpoint_ClearSETUP();\n                while (!(Endpoint_IsINReady()))\n                    ;\n                Endpoint_Write_8(usb_device_state_get_idle_rate());\n                Endpoint_ClearIN();\n                Endpoint_ClearStatusStage();\n            }\n\n            break;\n    }\n\n#ifdef VIRTSER_ENABLE\n    CDC_Device_ProcessControlRequest(&cdc_device);\n#endif\n}\n\n/*******************************************************************************\n * Host driver\n ******************************************************************************/\n\n/** \\brief Send Keyboard\n *\n * FIXME: Needs doc\n */\nstatic void send_keyboard(report_keyboard_t *report) {\n    /* If we're in Boot Protocol, don't send any report ID or other funky fields */\n    if (usb_device_state_get_protocol() == USB_PROTOCOL_BOOT) {\n        send_report(KEYBOARD_IN_EPNUM, &report->mods, 8);\n    } else {\n        send_report(KEYBOARD_IN_EPNUM, report, KEYBOARD_REPORT_SIZE);\n    }\n\n    keyboard_report_sent = *report;\n}\n\n/** \\brief Send NKRO\n *\n * FIXME: Needs doc\n */\nstatic void send_nkro(report_nkro_t *report) {\n#ifdef NKRO_ENABLE\n    send_report(SHARED_IN_EPNUM, report, sizeof(report_nkro_t));\n#endif\n}\n\n/** \\brief Send Mouse\n *\n * FIXME: Needs doc\n */\nstatic void send_mouse(report_mouse_t *report) {\n#ifdef MOUSE_ENABLE\n    send_report(MOUSE_IN_EPNUM, report, sizeof(report_mouse_t));\n#endif\n}\n\n/** \\brief Send Extra\n *\n * FIXME: Needs doc\n */\nstatic void send_extra(report_extra_t *report) {\n#ifdef EXTRAKEY_ENABLE\n    send_report(SHARED_IN_EPNUM, report, sizeof(report_extra_t));\n#endif\n}\n\nvoid send_joystick(report_joystick_t *report) {\n#ifdef JOYSTICK_ENABLE\n    send_report(JOYSTICK_IN_EPNUM, report, sizeof(report_joystick_t));\n#endif\n}\n\nvoid send_programmable_button(report_programmable_button_t *report) {\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\n    send_report(SHARED_IN_EPNUM, report, sizeof(report_programmable_button_t));\n#endif\n}\n\nvoid send_digitizer(report_digitizer_t *report) {\n#ifdef DIGITIZER_ENABLE\n    send_report(DIGITIZER_IN_EPNUM, report, sizeof(report_digitizer_t));\n#endif\n}\n\n/*******************************************************************************\n * sendchar\n ******************************************************************************/\n#ifdef CONSOLE_ENABLE\n#    define SEND_TIMEOUT 5\n/** \\brief Send Char\n *\n * FIXME: Needs doc\n */\nint8_t sendchar(uint8_t c) {\n    // Do not wait if the previous write has timed_out.\n    // Because sendchar() is called so many times, waiting each call causes big lag.\n    // The `timed_out` state is an approximation of the ideal `is_listener_disconnected?` state.\n    static bool timed_out = false;\n\n    // prevents console_flush_task() from running during sendchar() runs.\n    // or char will be lost. These two function is mutually exclusive.\n    CONSOLE_FLUSH_SET(false);\n\n    if (USB_DeviceState != DEVICE_STATE_Configured) return -1;\n\n    uint8_t ep = Endpoint_GetCurrentEndpoint();\n    Endpoint_SelectEndpoint(CONSOLE_IN_EPNUM);\n    if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {\n        goto ERROR_EXIT;\n    }\n\n    if (timed_out && !Endpoint_IsReadWriteAllowed()) {\n        goto ERROR_EXIT;\n    }\n\n    timed_out = false;\n\n    uint8_t timeout = SEND_TIMEOUT;\n    while (!Endpoint_IsReadWriteAllowed()) {\n        if (USB_DeviceState != DEVICE_STATE_Configured) {\n            goto ERROR_EXIT;\n        }\n        if (Endpoint_IsStalled()) {\n            goto ERROR_EXIT;\n        }\n        if (!(timeout--)) {\n            timed_out = true;\n            goto ERROR_EXIT;\n        }\n        _delay_ms(1);\n    }\n\n    Endpoint_Write_8(c);\n\n    // send when bank is full\n    if (!Endpoint_IsReadWriteAllowed()) {\n        while (!(Endpoint_IsINReady()))\n            ;\n        Endpoint_ClearIN();\n    } else {\n        CONSOLE_FLUSH_SET(true);\n    }\n\n    Endpoint_SelectEndpoint(ep);\n    return 0;\nERROR_EXIT:\n    Endpoint_SelectEndpoint(ep);\n    return -1;\n}\n#endif\n\n/*******************************************************************************\n * MIDI\n ******************************************************************************/\n\n#ifdef MIDI_ENABLE\n// clang-format off\n\nUSB_ClassInfo_MIDI_Device_t USB_MIDI_Interface = {\n    .Config = {\n        .StreamingInterfaceNumber = AS_INTERFACE,\n        .DataINEndpoint = {\n            .Address              = (MIDI_STREAM_IN_EPNUM | ENDPOINT_DIR_IN),\n            .Size                 = MIDI_STREAM_EPSIZE,\n            .Banks                = 1\n        },\n        .DataOUTEndpoint = {\n            .Address              = (MIDI_STREAM_OUT_EPNUM | ENDPOINT_DIR_OUT),\n            .Size                 = MIDI_STREAM_EPSIZE,\n            .Banks                = 1\n        }\n    }\n};\n\n// clang-format on\n\nvoid send_midi_packet(MIDI_EventPacket_t *event) {\n    MIDI_Device_SendEventPacket(&USB_MIDI_Interface, event);\n}\n\nbool recv_midi_packet(MIDI_EventPacket_t *const event) {\n    return MIDI_Device_ReceiveEventPacket(&USB_MIDI_Interface, event);\n}\n\n#endif\n\n/*******************************************************************************\n * VIRTUAL SERIAL\n ******************************************************************************/\n\n#ifdef VIRTSER_ENABLE\n/** \\brief Virtual Serial Init\n *\n * FIXME: Needs doc\n */\nvoid virtser_init(void) {\n    cdc_device.State.ControlLineStates.DeviceToHost = CDC_CONTROL_LINE_IN_DSR;\n    CDC_Device_SendControlLineStateChange(&cdc_device);\n}\n\n/** \\brief Virtual Serial Receive\n *\n * FIXME: Needs doc\n */\nvoid virtser_recv(uint8_t c) __attribute__((weak));\nvoid virtser_recv(uint8_t c) {\n    // Ignore by default\n}\n\n/** \\brief Virtual Serial Task\n *\n * FIXME: Needs doc\n */\nvoid virtser_task(void) {\n    uint16_t count = CDC_Device_BytesReceived(&cdc_device);\n    uint8_t  ch;\n    for (; count; --count) {\n        ch = CDC_Device_ReceiveByte(&cdc_device);\n        virtser_recv(ch);\n    }\n}\n/** \\brief Virtual Serial Send\n *\n * FIXME: Needs doc\n */\nvoid virtser_send(const uint8_t byte) {\n    uint8_t timeout = 255;\n    uint8_t ep      = Endpoint_GetCurrentEndpoint();\n\n    if (cdc_device.State.ControlLineStates.HostToDevice & CDC_CONTROL_LINE_OUT_DTR) {\n        /* IN packet */\n        Endpoint_SelectEndpoint(cdc_device.Config.DataINEndpoint.Address);\n\n        if (!Endpoint_IsEnabled() || !Endpoint_IsConfigured()) {\n            Endpoint_SelectEndpoint(ep);\n            return;\n        }\n\n        while (timeout-- && !Endpoint_IsReadWriteAllowed())\n            _delay_us(40);\n\n        Endpoint_Write_8(byte);\n        CDC_Device_Flush(&cdc_device);\n\n        if (Endpoint_IsINReady()) {\n            Endpoint_ClearIN();\n        }\n\n        Endpoint_SelectEndpoint(ep);\n    }\n}\n#endif\n\n/*******************************************************************************\n * main\n ******************************************************************************/\n/** \\brief Setup MCU\n *\n * FIXME: Needs doc\n */\nstatic void setup_mcu(void) {\n    /* Disable watchdog if enabled by bootloader/fuses */\n    MCUSR &= ~_BV(WDRF);\n    wdt_disable();\n\n// For boards running at 3.3V and crystal at 16 MHz\n#if (F_CPU == 8000000 && F_USB == 16000000)\n    /* Divide clock by 2 */\n    clock_prescale_set(clock_div_2);\n#else /* Disable clock division */\n    clock_prescale_set(clock_div_1);\n#endif\n}\n\n/** \\brief Setup USB\n *\n * FIXME: Needs doc\n */\nstatic void setup_usb(void) {\n    // Leonardo needs. Without this USB device is not recognized.\n    USB_Disable();\n\n    USB_Init();\n\n    // for console_flush_task\n    USB_Device_EnableSOFEvents();\n}\n\nvoid protocol_setup(void) {\n#ifdef MIDI_ENABLE\n    setup_midi();\n#endif\n\n    setup_mcu();\n    usb_device_state_init();\n}\n\nvoid protocol_pre_init(void) {\n    setup_usb();\n    sei();\n\n    /* wait for USB startup & debug output */\n\n#ifdef USB_WAIT_FOR_ENUMERATION\n    while (USB_DeviceState != DEVICE_STATE_Configured) {\n#    if defined(INTERRUPT_CONTROL_ENDPOINT)\n        ;\n#    else\n        USB_USBTask();\n#    endif\n    }\n    print(\"USB configured.\\n\");\n#else\n    USB_USBTask();\n#endif\n}\n\nvoid protocol_post_init(void) {\n    host_set_driver(&lufa_driver);\n}\n\nvoid protocol_pre_task(void) {\n#if !defined(NO_USB_STARTUP_CHECK)\n    if (USB_DeviceState == DEVICE_STATE_Suspended) {\n        dprintln(\"suspending keyboard\");\n        while (USB_DeviceState == DEVICE_STATE_Suspended) {\n            suspend_power_down();\n            if (suspend_wakeup_condition() && USB_Device_RemoteWakeupEnabled) {\n                USB_Device_SendRemoteWakeup();\n                clear_keyboard();\n\n#    if USB_SUSPEND_WAKEUP_DELAY > 0\n                // Some hubs, kvm switches, and monitors do\n                // weird things, with USB device state bouncing\n                // around wildly on wakeup, yielding race\n                // conditions that can corrupt the keyboard state.\n                //\n                // Pause for a while to let things settle...\n                wait_ms(USB_SUSPEND_WAKEUP_DELAY);\n#    endif\n            }\n        }\n        suspend_wakeup_init();\n    }\n#endif\n}\n\nvoid protocol_post_task(void) {\n#ifdef CONSOLE_ENABLE\n    console_task();\n#endif\n\n#ifdef MIDI_ENABLE\n    MIDI_Device_USBTask(&USB_MIDI_Interface);\n#endif\n\n#ifdef VIRTSER_ENABLE\n    virtser_task();\n    CDC_Device_USBTask(&cdc_device);\n#endif\n\n#if !defined(INTERRUPT_CONTROL_ENDPOINT)\n    USB_USBTask();\n#endif\n}\n\nuint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, const uint16_t wIndex, const void **const DescriptorAddress) {\n    return get_usb_descriptor(wValue, wIndex, USB_ControlRequest.wLength, DescriptorAddress);\n}\n"
  },
  {
    "path": "tmk_core/protocol/lufa/lufa.h",
    "content": "/*\n * Copyright 2012 Jun Wako <wakojun@gmail.com>\n * This file is based on:\n *     LUFA-120219/Demos/Device/Lowlevel/KeyboardMouse\n *     LUFA-120219/Demos/Device/Lowlevel/GenericHID\n */\n\n/*\n             LUFA Library\n     Copyright (C) Dean Camera, 2012.\n\n  dean [at] fourwalledcubicle [dot] com\n           www.lufa-lib.org\n*/\n\n/*\n  Copyright 2012  Dean Camera (dean [at] fourwalledcubicle [dot] com)\n  Copyright 2010  Denver Gingerich (denver [at] ossguy [dot] com)\n\n  Permission to use, copy, modify, distribute, and sell this\n  software and its documentation for any purpose is hereby granted\n  without fee, provided that the above copyright notice appear in\n  all copies and that both that the copyright notice and this\n  permission notice and warranty disclaimer appear in supporting\n  documentation, and that the name of the author not be used in\n  advertising or publicity pertaining to distribution of the\n  software without specific, written prior permission.\n\n  The author disclaim all warranties with regard to this\n  software, including all implied warranties of merchantability\n  and fitness.  In no event shall the author be liable for any\n  special, indirect or consequential damages or any damages\n  whatsoever resulting from loss of use, data or profits, whether\n  in an action of contract, negligence or other tortious action,\n  arising out of or in connection with the use or performance of\n  this software.\n*/\n\n#pragma once\n\n#include <avr/io.h>\n#include <avr/wdt.h>\n#include <avr/power.h>\n#include <avr/interrupt.h>\n#include <stdbool.h>\n#include <string.h>\n#include <LUFA/Version.h>\n#include <LUFA/Drivers/USB/USB.h>\n#include \"host.h\"\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern host_driver_t lufa_driver;\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "tmk_core/protocol/lufa/lufa.mk",
    "content": "LUFA_DIR = protocol/lufa\n\n# Path to the LUFA library\nLUFA_PATH = $(LIB_PATH)/lufa\n\n# Create the LUFA source path variables by including the LUFA makefile\nifneq (, $(wildcard $(LUFA_PATH)/LUFA/Build/lufa_sources.mk))\n    # New build system from 20120730\n    LUFA_ROOT_PATH = $(LUFA_PATH)/LUFA\n    DMBS_LUFA_PATH = $(LUFA_PATH)/LUFA/Build/LUFA\n    include $(LUFA_PATH)/LUFA/Build/lufa_sources.mk\nelse\n    include $(LUFA_PATH)/LUFA/makefile\nendif\n\nLUFA_SRC = lufa.c \\\n\tusb_descriptor.c \\\n\t$(LUFA_SRC_USB)\n\nifeq ($(strip $(MIDI_ENABLE)), yes)\n\tLUFA_SRC += $(LUFA_ROOT_PATH)/Drivers/USB/Class/Device/MIDIClassDevice.c\nendif\n\nifeq ($(strip $(VIRTSER_ENABLE)), yes)\n\tLUFA_SRC += $(LUFA_ROOT_PATH)/Drivers/USB/Class/Device/CDCClassDevice.c\nendif\n\nSRC += $(LUFA_SRC)\nSRC += $(LUFA_DIR)/usb_util.c\n\n# Search Path\nVPATH += $(TMK_PATH)/$(LUFA_DIR)\nVPATH += $(LUFA_PATH)\n\n# LUFA library compile-time options and predefined tokens\nLUFA_OPTS  = -DUSB_DEVICE_ONLY\nLUFA_OPTS += -DUSE_FLASH_DESCRIPTORS\nLUFA_OPTS += -DUSE_STATIC_OPTIONS=\"(USB_DEVICE_OPT_FULLSPEED | USB_OPT_REG_ENABLED | USB_OPT_AUTO_PLL)\"\nLUFA_OPTS += -DFIXED_CONTROL_ENDPOINT_SIZE=8\nLUFA_OPTS += -DFIXED_NUM_CONFIGURATIONS=1\n\n# Remote wakeup fix for ATmega16/32U2        https://github.com/tmk/tmk_keyboard/issues/361\nifneq (,$(filter $(MCU), at90usb162 atmega16u2 atmega32u2))\n\tLUFA_OPTS += -DNO_LIMITED_CONTROLLER_CONNECT\nendif\n\nOPT_DEFS += -DF_USB=$(F_USB)UL\nOPT_DEFS += -DARCH=ARCH_$(ARCH)\nOPT_DEFS += $(LUFA_OPTS)\n\n# This indicates using LUFA stack\nOPT_DEFS += -DPROTOCOL_LUFA\n"
  },
  {
    "path": "tmk_core/protocol/lufa/usb_util.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include <LUFA/Drivers/USB/USB.h>\n#include \"usb_util.h\"\n#include \"wait.h\"\n\nvoid usb_disconnect(void) {\n    USB_Disable();\n    USB_DeviceState = DEVICE_STATE_Unattached;\n}\n\nbool usb_connected_state(void) {\n    return USB_Device_IsAddressSet();\n}\n\n#if defined(OTGPADE)\nbool usb_vbus_state(void) {\n    USB_OTGPAD_On(); // enables VBUS pad\n    wait_us(5);\n\n    return USB_VBUS_GetStatus(); // checks state of VBUS\n}\n#endif\n"
  },
  {
    "path": "tmk_core/protocol/report.c",
    "content": "/* Copyright 2017 Fred Sundvik\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"report.h\"\n#include \"action_util.h\"\n#include \"host.h\"\n#include \"keycode_config.h\"\n#include \"debug.h\"\n#include \"usb_device_state.h\"\n#include \"util.h\"\n#include <string.h>\n\n/** \\brief has_anykey\n *\n * FIXME: Needs doc\n */\nuint8_t has_anykey(void) {\n    uint8_t  cnt = 0;\n    uint8_t* p   = keyboard_report->keys;\n    uint8_t  lp  = sizeof(keyboard_report->keys);\n#ifdef NKRO_ENABLE\n    if (host_can_send_nkro() && keymap_config.nkro) {\n        p  = nkro_report->bits;\n        lp = sizeof(nkro_report->bits);\n    }\n#endif\n    while (lp--) {\n        if (*p++) cnt++;\n    }\n    return cnt;\n}\n\n/** \\brief get_first_key\n *\n * FIXME: Needs doc\n */\nuint8_t get_first_key(void) {\n#ifdef NKRO_ENABLE\n    if (host_can_send_nkro() && keymap_config.nkro) {\n        uint8_t i = 0;\n        for (; i < NKRO_REPORT_BITS && !nkro_report->bits[i]; i++)\n            ;\n        return i << 3 | biton(nkro_report->bits[i]);\n    }\n#endif\n    return keyboard_report->keys[0];\n}\n\n/** \\brief Checks if a key is pressed in the report\n *\n * Returns true if the keyboard_report reports that the key is pressed, otherwise false\n * Note: The function doesn't support modifers currently, and it returns false for KC_NO\n */\nbool is_key_pressed(uint8_t key) {\n    if (key == KC_NO) {\n        return false;\n    }\n#ifdef NKRO_ENABLE\n    if (host_can_send_nkro() && keymap_config.nkro) {\n        if ((key >> 3) < NKRO_REPORT_BITS) {\n            return nkro_report->bits[key >> 3] & 1 << (key & 7);\n        } else {\n            return false;\n        }\n    }\n#endif\n    for (int i = 0; i < KEYBOARD_REPORT_KEYS; i++) {\n        if (keyboard_report->keys[i] == key) {\n            return true;\n        }\n    }\n    return false;\n}\n\n/** \\brief add key byte\n *\n * FIXME: Needs doc\n */\nvoid add_key_byte(report_keyboard_t* keyboard_report, uint8_t code) {\n    int8_t i     = 0;\n    int8_t empty = -1;\n    for (; i < KEYBOARD_REPORT_KEYS; i++) {\n        if (keyboard_report->keys[i] == code) {\n            break;\n        }\n        if (empty == -1 && keyboard_report->keys[i] == 0) {\n            empty = i;\n        }\n    }\n    if (i == KEYBOARD_REPORT_KEYS) {\n        if (empty != -1) {\n            keyboard_report->keys[empty] = code;\n        }\n    }\n}\n\n/** \\brief del key byte\n *\n * FIXME: Needs doc\n */\nvoid del_key_byte(report_keyboard_t* keyboard_report, uint8_t code) {\n    for (uint8_t i = 0; i < KEYBOARD_REPORT_KEYS; i++) {\n        if (keyboard_report->keys[i] == code) {\n            keyboard_report->keys[i] = 0;\n        }\n    }\n}\n\n#ifdef NKRO_ENABLE\n/** \\brief add key bit\n *\n * FIXME: Needs doc\n */\nvoid add_key_bit(report_nkro_t* nkro_report, uint8_t code) {\n    if ((code >> 3) < NKRO_REPORT_BITS) {\n        nkro_report->bits[code >> 3] |= 1 << (code & 7);\n    } else {\n        dprintf(\"add_key_bit: can't add: %02X\\n\", code);\n    }\n}\n\n/** \\brief del key bit\n *\n * FIXME: Needs doc\n */\nvoid del_key_bit(report_nkro_t* nkro_report, uint8_t code) {\n    if ((code >> 3) < NKRO_REPORT_BITS) {\n        nkro_report->bits[code >> 3] &= ~(1 << (code & 7));\n    } else {\n        dprintf(\"del_key_bit: can't del: %02X\\n\", code);\n    }\n}\n#endif\n\n/** \\brief add key to report\n *\n * FIXME: Needs doc\n */\nvoid add_key_to_report(uint8_t key) {\n#ifdef NKRO_ENABLE\n    if (host_can_send_nkro() && keymap_config.nkro) {\n        add_key_bit(nkro_report, key);\n        return;\n    }\n#endif\n    add_key_byte(keyboard_report, key);\n}\n\n/** \\brief del key from report\n *\n * FIXME: Needs doc\n */\nvoid del_key_from_report(uint8_t key) {\n#ifdef NKRO_ENABLE\n    if (host_can_send_nkro() && keymap_config.nkro) {\n        del_key_bit(nkro_report, key);\n        return;\n    }\n#endif\n    del_key_byte(keyboard_report, key);\n}\n\n/** \\brief clear key from report\n *\n * FIXME: Needs doc\n */\nvoid clear_keys_from_report(void) {\n    // not clear mods\n#ifdef NKRO_ENABLE\n    if (host_can_send_nkro() && keymap_config.nkro) {\n        memset(nkro_report->bits, 0, sizeof(nkro_report->bits));\n        return;\n    }\n#endif\n    memset(keyboard_report->keys, 0, sizeof(keyboard_report->keys));\n}\n\n#ifdef MOUSE_ENABLE\n/**\n * @brief Compares 2 mouse reports for difference and returns result. Empty\n * reports always evaluate as unchanged.\n *\n * @param[in] new_report report_mouse_t\n * @param[in] old_report report_mouse_t\n * @return bool result\n */\n__attribute__((weak)) bool has_mouse_report_changed(report_mouse_t* new_report, report_mouse_t* old_report) {\n    // memcmp doesn't work here because of the `report_id` field when using\n    // shared mouse endpoint\n    bool changed = ((new_report->buttons != old_report->buttons) ||\n#    ifdef MOUSE_EXTENDED_REPORT\n                    (new_report->boot_x != 0 && new_report->boot_x != old_report->boot_x) || (new_report->boot_y != 0 && new_report->boot_y != old_report->boot_y) ||\n#    endif\n                    (new_report->x != 0 && new_report->x != old_report->x) || (new_report->y != 0 && new_report->y != old_report->y) || (new_report->h != 0 && new_report->h != old_report->h) || (new_report->v != 0 && new_report->v != old_report->v));\n    return changed;\n}\n#endif\n"
  },
  {
    "path": "tmk_core/protocol/report.h",
    "content": "/*\nCopyright 2011,2012 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"keycode.h\"\n#include \"util.h\"\n\n#ifdef JOYSTICK_ENABLE\n#    include \"joystick.h\"\n#endif\n\n// clang-format off\n\n/* HID report IDs */\nenum hid_report_ids { \n    REPORT_ID_ALL = 0,\n    REPORT_ID_KEYBOARD = 1,\n    REPORT_ID_MOUSE,\n    REPORT_ID_SYSTEM,\n    REPORT_ID_CONSUMER,\n    REPORT_ID_PROGRAMMABLE_BUTTON,\n    REPORT_ID_NKRO,\n    REPORT_ID_JOYSTICK,\n    REPORT_ID_DIGITIZER,\n    REPORT_ID_COUNT = REPORT_ID_DIGITIZER\n};\n\n#define IS_VALID_REPORT_ID(id) ((id) >= REPORT_ID_ALL && (id) <= REPORT_ID_COUNT)\n\n/* Mouse buttons */\n#define MOUSE_BTN_MASK(n) (1 << (n))\nenum mouse_buttons {\n    MOUSE_BTN1 = MOUSE_BTN_MASK(0),\n    MOUSE_BTN2 = MOUSE_BTN_MASK(1),\n    MOUSE_BTN3 = MOUSE_BTN_MASK(2),\n    MOUSE_BTN4 = MOUSE_BTN_MASK(3),\n    MOUSE_BTN5 = MOUSE_BTN_MASK(4),\n    MOUSE_BTN6 = MOUSE_BTN_MASK(5),\n    MOUSE_BTN7 = MOUSE_BTN_MASK(6),\n    MOUSE_BTN8 = MOUSE_BTN_MASK(7)\n};\n\n/* Consumer Page (0x0C)\n *\n * See https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf#page=75\n */\nenum consumer_usages {\n    // 15.5 Display Controls\n    SNAPSHOT        = 0x065,\n    BRIGHTNESS_UP   = 0x06F, // https://www.usb.org/sites/default/files/hutrr41_0.pdf\n    BRIGHTNESS_DOWN = 0x070,\n    // 15.7 Transport Controls\n    TRANSPORT_RECORD       = 0x0B2,\n    TRANSPORT_FAST_FORWARD = 0x0B3,\n    TRANSPORT_REWIND       = 0x0B4,\n    TRANSPORT_NEXT_TRACK   = 0x0B5,\n    TRANSPORT_PREV_TRACK   = 0x0B6,\n    TRANSPORT_STOP         = 0x0B7,\n    TRANSPORT_EJECT        = 0x0B8,\n    TRANSPORT_RANDOM_PLAY  = 0x0B9,\n    TRANSPORT_STOP_EJECT   = 0x0CC,\n    TRANSPORT_PLAY_PAUSE   = 0x0CD,\n    // 15.9.1 Audio Controls - Volume\n    AUDIO_MUTE     = 0x0E2,\n    AUDIO_VOL_UP   = 0x0E9,\n    AUDIO_VOL_DOWN = 0x0EA,\n    // 15.15 Application Launch Buttons\n    AL_CC_CONFIG       = 0x183,\n    AL_EMAIL           = 0x18A,\n    AL_CALCULATOR      = 0x192,\n    AL_LOCAL_BROWSER   = 0x194,\n    AL_LOCK            = 0x19E,\n    AL_CONTROL_PANEL   = 0x19F,\n    AL_ASSISTANT       = 0x1CB,\n    AL_KEYBOARD_LAYOUT = 0x1AE,\n    // 15.16 Generic GUI Application Controls\n    AC_NEW                         = 0x201,\n    AC_OPEN                        = 0x202,\n    AC_CLOSE                       = 0x203,\n    AC_EXIT                        = 0x204,\n    AC_MAXIMIZE                    = 0x205,\n    AC_MINIMIZE                    = 0x206,\n    AC_SAVE                        = 0x207,\n    AC_PRINT                       = 0x208,\n    AC_PROPERTIES                  = 0x209,\n    AC_UNDO                        = 0x21A,\n    AC_COPY                        = 0x21B,\n    AC_CUT                         = 0x21C,\n    AC_PASTE                       = 0x21D,\n    AC_SELECT_ALL                  = 0x21E,\n    AC_FIND                        = 0x21F,\n    AC_SEARCH                      = 0x221,\n    AC_HOME                        = 0x223,\n    AC_BACK                        = 0x224,\n    AC_FORWARD                     = 0x225,\n    AC_STOP                        = 0x226,\n    AC_REFRESH                     = 0x227,\n    AC_BOOKMARKS                   = 0x22A,\n    AC_NEXT_KEYBOARD_LAYOUT_SELECT = 0x29D,\n    AC_DESKTOP_SHOW_ALL_WINDOWS    = 0x29F,\n    AC_SOFT_KEY_LEFT               = 0x2A0\n};\n\n/* Generic Desktop Page (0x01)\n *\n * See https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf#page=26\n */\nenum desktop_usages {\n    // 4.5.1 System Controls - Power Controls\n    SYSTEM_POWER_DOWN             = 0x81,\n    SYSTEM_SLEEP                  = 0x82,\n    SYSTEM_WAKE_UP                = 0x83,\n    SYSTEM_RESTART                = 0x8F,\n    // 4.10 System Display Controls\n    SYSTEM_DISPLAY_TOGGLE_INT_EXT = 0xB5\n};\n\n// clang-format on\n\n#define NKRO_REPORT_BITS 30\n\n#ifdef KEYBOARD_SHARED_EP\n#    define KEYBOARD_REPORT_SIZE 9\n#else\n#    define KEYBOARD_REPORT_SIZE 8\n#endif\n\n#define KEYBOARD_REPORT_KEYS 6\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * keyboard report is 8-byte array retains state of 8 modifiers and 6 keys.\n *\n * byte |0       |1       |2       |3       |4       |5       |6       |7\n * -----+--------+--------+--------+--------+--------+--------+--------+--------\n * desc |mods    |reserved|keys[0] |keys[1] |keys[2] |keys[3] |keys[4] |keys[5]\n *\n * It is exended to 16 bytes to retain 120keys+8mods when NKRO mode.\n *\n * byte |0       |1       |2       |3       |4       |5       |6       |7        ... |15\n * -----+--------+--------+--------+--------+--------+--------+--------+--------     +--------\n * desc |mods    |bits[0] |bits[1] |bits[2] |bits[3] |bits[4] |bits[5] |bits[6]  ... |bit[14]\n *\n * mods retains state of 8 modifiers.\n *\n *  bit |0       |1       |2       |3       |4       |5       |6       |7\n * -----+--------+--------+--------+--------+--------+--------+--------+--------\n * desc |Lcontrol|Lshift  |Lalt    |Lgui    |Rcontrol|Rshift  |Ralt    |Rgui\n *\n */\ntypedef struct {\n#ifdef KEYBOARD_SHARED_EP\n    uint8_t report_id;\n#endif\n    uint8_t mods;\n    uint8_t reserved;\n    uint8_t keys[KEYBOARD_REPORT_KEYS];\n} PACKED report_keyboard_t;\n\ntypedef struct {\n    uint8_t report_id;\n    uint8_t mods;\n    uint8_t bits[NKRO_REPORT_BITS];\n} PACKED report_nkro_t;\n\ntypedef struct {\n    uint8_t  report_id;\n    uint16_t usage;\n} PACKED report_extra_t;\n\ntypedef struct {\n    uint8_t  report_id;\n    uint32_t usage;\n} PACKED report_programmable_button_t;\n\n#ifdef MOUSE_EXTENDED_REPORT\n#    define MOUSE_REPORT_XY_MIN INT16_MIN\n#    define MOUSE_REPORT_XY_MAX INT16_MAX\ntypedef int16_t mouse_xy_report_t;\n#else\n#    define MOUSE_REPORT_XY_MIN INT8_MIN\n#    define MOUSE_REPORT_XY_MAX INT8_MAX\ntypedef int8_t mouse_xy_report_t;\n#endif\n\n#ifdef WHEEL_EXTENDED_REPORT\n#    define MOUSE_REPORT_HV_MIN INT16_MIN\n#    define MOUSE_REPORT_HV_MAX INT16_MAX\ntypedef int16_t mouse_hv_report_t;\n#else\n#    define MOUSE_REPORT_HV_MIN INT8_MIN\n#    define MOUSE_REPORT_HV_MAX INT8_MAX\ntypedef int8_t mouse_hv_report_t;\n#endif\n\ntypedef struct {\n#ifdef MOUSE_SHARED_EP\n    uint8_t report_id;\n#endif\n    uint8_t buttons;\n#ifdef MOUSE_EXTENDED_REPORT\n    int8_t boot_x;\n    int8_t boot_y;\n#endif\n    mouse_xy_report_t x;\n    mouse_xy_report_t y;\n    mouse_hv_report_t v;\n    mouse_hv_report_t h;\n} PACKED report_mouse_t;\n\ntypedef struct {\n#ifdef DIGITIZER_SHARED_EP\n    uint8_t report_id;\n#endif\n    bool     in_range : 1;\n    bool     tip : 1;\n    bool     barrel : 1;\n    uint8_t  reserved : 5;\n    uint16_t x;\n    uint16_t y;\n} PACKED report_digitizer_t;\n\n#if JOYSTICK_AXIS_RESOLUTION > 8\ntypedef int16_t joystick_axis_t;\n#else\ntypedef int8_t joystick_axis_t;\n#endif\n\ntypedef struct {\n#ifdef JOYSTICK_SHARED_EP\n    uint8_t report_id;\n#endif\n#if JOYSTICK_AXIS_COUNT > 0\n    joystick_axis_t axes[JOYSTICK_AXIS_COUNT];\n#endif\n\n#ifdef JOYSTICK_HAS_HAT\n    int8_t  hat : 4;\n    uint8_t reserved : 4;\n#endif\n\n#if JOYSTICK_BUTTON_COUNT > 0\n    uint8_t buttons[(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1];\n#endif\n} PACKED report_joystick_t;\n\n/* keycode to system usage */\nstatic inline uint16_t KEYCODE2SYSTEM(uint8_t key) {\n    switch (key) {\n        case KC_SYSTEM_POWER:\n            return SYSTEM_POWER_DOWN;\n        case KC_SYSTEM_SLEEP:\n            return SYSTEM_SLEEP;\n        case KC_SYSTEM_WAKE:\n            return SYSTEM_WAKE_UP;\n        default:\n            return 0;\n    }\n}\n\n/* keycode to consumer usage */\nstatic inline uint16_t KEYCODE2CONSUMER(uint8_t key) {\n    switch (key) {\n        case KC_AUDIO_MUTE:\n            return AUDIO_MUTE;\n        case KC_AUDIO_VOL_UP:\n            return AUDIO_VOL_UP;\n        case KC_AUDIO_VOL_DOWN:\n            return AUDIO_VOL_DOWN;\n        case KC_MEDIA_NEXT_TRACK:\n            return TRANSPORT_NEXT_TRACK;\n        case KC_MEDIA_PREV_TRACK:\n            return TRANSPORT_PREV_TRACK;\n        case KC_MEDIA_FAST_FORWARD:\n            return TRANSPORT_FAST_FORWARD;\n        case KC_MEDIA_REWIND:\n            return TRANSPORT_REWIND;\n        case KC_MEDIA_STOP:\n            return TRANSPORT_STOP;\n        case KC_MEDIA_EJECT:\n            return TRANSPORT_STOP_EJECT;\n        case KC_MEDIA_PLAY_PAUSE:\n            return TRANSPORT_PLAY_PAUSE;\n        case KC_MEDIA_SELECT:\n            return AL_CC_CONFIG;\n        case KC_MAIL:\n            return AL_EMAIL;\n        case KC_CALCULATOR:\n            return AL_CALCULATOR;\n        case KC_MY_COMPUTER:\n            return AL_LOCAL_BROWSER;\n        case KC_CONTROL_PANEL:\n            return AL_CONTROL_PANEL;\n        case KC_ASSISTANT:\n            return AL_ASSISTANT;\n        case KC_WWW_SEARCH:\n            return AC_SEARCH;\n        case KC_WWW_HOME:\n            return AC_HOME;\n        case KC_WWW_BACK:\n            return AC_BACK;\n        case KC_WWW_FORWARD:\n            return AC_FORWARD;\n        case KC_WWW_STOP:\n            return AC_STOP;\n        case KC_WWW_REFRESH:\n            return AC_REFRESH;\n        case KC_BRIGHTNESS_UP:\n            return BRIGHTNESS_UP;\n        case KC_BRIGHTNESS_DOWN:\n            return BRIGHTNESS_DOWN;\n        case KC_WWW_FAVORITES:\n            return AC_BOOKMARKS;\n        case KC_MISSION_CONTROL:\n            return AC_DESKTOP_SHOW_ALL_WINDOWS;\n        case KC_LAUNCHPAD:\n            return AC_SOFT_KEY_LEFT;\n        default:\n            return 0;\n    }\n}\n\nuint8_t has_anykey(void);\nuint8_t get_first_key(void);\nbool    is_key_pressed(uint8_t key);\n\nvoid add_key_byte(report_keyboard_t* keyboard_report, uint8_t code);\nvoid del_key_byte(report_keyboard_t* keyboard_report, uint8_t code);\n#ifdef NKRO_ENABLE\nvoid add_key_bit(report_nkro_t* nkro_report, uint8_t code);\nvoid del_key_bit(report_nkro_t* nkro_report, uint8_t code);\n#endif\n\nvoid add_key_to_report(uint8_t key);\nvoid del_key_from_report(uint8_t key);\nvoid clear_keys_from_report(void);\n\n#ifdef MOUSE_ENABLE\nbool has_mouse_report_changed(report_mouse_t* new_report, report_mouse_t* old_report);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "tmk_core/protocol/usb_descriptor.c",
    "content": "/*\n * Copyright 2012 Jun Wako <wakojun@gmail.com>\n * This file is based on:\n *     LUFA-120219/Demos/Device/Lowlevel/KeyboardMouse\n *     LUFA-120219/Demos/Device/Lowlevel/GenericHID\n */\n\n/*\n                         LUFA Library\n         Copyright (C) Dean Camera, 2012.\n\n    dean [at] fourwalledcubicle [dot] com\n                     www.lufa-lib.org\n*/\n\n/*\n    Copyright 2012  Dean Camera (dean [at] fourwalledcubicle [dot] com)\n    Copyright 2010  Denver Gingerich (denver [at] ossguy [dot] com)\n\n    Permission to use, copy, modify, distribute, and sell this\n    software and its documentation for any purpose is hereby granted\n    without fee, provided that the above copyright notice appear in\n    all copies and that both that the copyright notice and this\n    permission notice and warranty disclaimer appear in supporting\n    documentation, and that the name of the author not be used in\n    advertising or publicity pertaining to distribution of the\n    software without specific, written prior permission.\n\n    The author disclaim all warranties with regard to this\n    software, including all implied warranties of merchantability\n    and fitness.  In no event shall the author be liable for any\n    special, indirect or consequential damages or any damages\n    whatsoever resulting from loss of use, data or profits, whether\n    in an action of contract, negligence or other tortious action,\n    arising out of or in connection with the use or performance of\n    this software.\n*/\n\n#include \"util.h\"\n#include \"report.h\"\n#include \"usb_descriptor.h\"\n#include \"usb_descriptor_common.h\"\n\n#ifdef JOYSTICK_ENABLE\n#    include \"joystick.h\"\n#endif\n\n#ifdef OS_DETECTION_ENABLE\n#    include \"os_detection.h\"\n#endif\n\n#if defined(SERIAL_NUMBER) || (defined(SERIAL_NUMBER_USE_HARDWARE_ID) && SERIAL_NUMBER_USE_HARDWARE_ID == TRUE)\n\n#    define HAS_SERIAL_NUMBER\n\n#    if defined(SERIAL_NUMBER_USE_HARDWARE_ID) && SERIAL_NUMBER_USE_HARDWARE_ID == TRUE\n#        include \"hardware_id.h\"\n#    endif\n\n#endif // defined(SERIAL_NUMBER) || (defined(SERIAL_NUMBER_USE_HARDWARE_ID) && SERIAL_NUMBER_USE_HARDWARE_ID == TRUE)\n\n// clang-format off\n\n/*\n * HID report descriptors\n */\n#ifdef KEYBOARD_SHARED_EP\nconst USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {\n#    define SHARED_REPORT_STARTED\n#else\nconst USB_Descriptor_HIDReport_Datatype_t PROGMEM KeyboardReport[] = {\n#endif\n    HID_RI_USAGE_PAGE(8, 0x01),        // Generic Desktop\n    HID_RI_USAGE(8, 0x06),             // Keyboard\n    HID_RI_COLLECTION(8, 0x01),        // Application\n#ifdef KEYBOARD_SHARED_EP\n        HID_RI_REPORT_ID(8, REPORT_ID_KEYBOARD),\n#endif\n        // Modifiers (8 bits)\n        HID_RI_USAGE_PAGE(8, 0x07),    // Keyboard/Keypad\n        HID_RI_USAGE_MINIMUM(8, 0xE0), // Keyboard Left Control\n        HID_RI_USAGE_MAXIMUM(8, 0xE7), // Keyboard Right GUI\n        HID_RI_LOGICAL_MINIMUM(8, 0x00),\n        HID_RI_LOGICAL_MAXIMUM(8, 0x01),\n        HID_RI_REPORT_COUNT(8, 0x08),\n        HID_RI_REPORT_SIZE(8, 0x01),\n        HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n        // Reserved (1 byte)\n        HID_RI_REPORT_COUNT(8, 0x01),\n        HID_RI_REPORT_SIZE(8, 0x08),\n        HID_RI_INPUT(8, HID_IOF_CONSTANT),\n        // Keycodes (6 bytes)\n        HID_RI_USAGE_PAGE(8, 0x07),    // Keyboard/Keypad\n        HID_RI_USAGE_MINIMUM(8, 0x00),\n        HID_RI_USAGE_MAXIMUM(8, 0xFF),\n        HID_RI_LOGICAL_MINIMUM(8, 0x00),\n        HID_RI_LOGICAL_MAXIMUM(16, 0x00FF),\n        HID_RI_REPORT_COUNT(8, 0x06),\n        HID_RI_REPORT_SIZE(8, 0x08),\n        HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),\n\n        // Status LEDs (5 bits)\n        HID_RI_USAGE_PAGE(8, 0x08),    // LED\n        HID_RI_USAGE_MINIMUM(8, 0x01), // Num Lock\n        HID_RI_USAGE_MAXIMUM(8, 0x05), // Kana\n        HID_RI_LOGICAL_MINIMUM(8, 0x00),\n        HID_RI_LOGICAL_MAXIMUM(8, 0x01),\n        HID_RI_REPORT_COUNT(8, 0x05),\n        HID_RI_REPORT_SIZE(8, 0x01),\n        HID_RI_OUTPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE),\n        // LED padding (3 bits)\n        HID_RI_REPORT_COUNT(8, 0x01),\n        HID_RI_REPORT_SIZE(8, 0x03),\n        HID_RI_OUTPUT(8, HID_IOF_CONSTANT),\n    HID_RI_END_COLLECTION(0),\n#ifndef KEYBOARD_SHARED_EP\n};\n#endif\n\n#ifdef MOUSE_ENABLE\n#    ifndef MOUSE_SHARED_EP\nconst USB_Descriptor_HIDReport_Datatype_t PROGMEM MouseReport[] = {\n#    elif !defined(SHARED_REPORT_STARTED)\nconst USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {\n#        define SHARED_REPORT_STARTED\n#    endif\n    HID_RI_USAGE_PAGE(8, 0x01),            // Generic Desktop\n    HID_RI_USAGE(8, 0x02),                 // Mouse\n    HID_RI_COLLECTION(8, 0x01),            // Application\n#    ifdef MOUSE_SHARED_EP\n        HID_RI_REPORT_ID(8, REPORT_ID_MOUSE),\n#    endif\n        HID_RI_USAGE(8, 0x01),             // Pointer\n        HID_RI_COLLECTION(8, 0x00),        // Physical\n            // Buttons (8 bits)\n            HID_RI_USAGE_PAGE(8, 0x09),    // Button\n            HID_RI_USAGE_MINIMUM(8, 0x01), // Button 1\n            HID_RI_USAGE_MAXIMUM(8, 0x08), // Button 8\n            HID_RI_LOGICAL_MINIMUM(8, 0x00),\n            HID_RI_LOGICAL_MAXIMUM(8, 0x01),\n            HID_RI_REPORT_COUNT(8, 0x08),\n            HID_RI_REPORT_SIZE(8, 0x01),\n            HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n\n#    ifdef MOUSE_EXTENDED_REPORT\n            // Boot protocol XY ignored in Report protocol\n            HID_RI_REPORT_COUNT(8, 0x02),\n            HID_RI_REPORT_SIZE(8, 0x08),\n            HID_RI_INPUT(8, HID_IOF_CONSTANT),\n#    endif\n            // X/Y position (2 or 4 bytes)\n            HID_RI_USAGE_PAGE(8, 0x01),    // Generic Desktop\n            HID_RI_USAGE(8, 0x30),         // X\n            HID_RI_USAGE(8, 0x31),         // Y\n#    ifndef MOUSE_EXTENDED_REPORT\n            HID_RI_LOGICAL_MINIMUM(8, -127),\n            HID_RI_LOGICAL_MAXIMUM(8, 127),\n            HID_RI_REPORT_COUNT(8, 0x02),\n            HID_RI_REPORT_SIZE(8, 0x08),\n#    else\n            HID_RI_LOGICAL_MINIMUM(16, -32767),\n            HID_RI_LOGICAL_MAXIMUM(16,  32767),\n            HID_RI_REPORT_COUNT(8, 0x02),\n            HID_RI_REPORT_SIZE(8, 0x10),\n#    endif\n            HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),\n\n#    ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE\n            HID_RI_COLLECTION(8, 0x02),\n            // Feature report and padding (1 byte)\n            HID_RI_USAGE(8, 0x48),     // Resolution Multiplier\n            HID_RI_REPORT_COUNT(8, 0x01),\n            HID_RI_REPORT_SIZE(8, 0x02),\n            HID_RI_LOGICAL_MINIMUM(8, 0x00),\n            HID_RI_LOGICAL_MAXIMUM(8, 0x01),\n            HID_RI_PHYSICAL_MINIMUM(8, 1),\n            HID_RI_PHYSICAL_MAXIMUM(8, POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER),\n            HID_RI_UNIT_EXPONENT(8, POINTING_DEVICE_HIRES_SCROLL_EXPONENT),\n            HID_RI_FEATURE(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n            HID_RI_PHYSICAL_MINIMUM(8, 0x00),\n            HID_RI_PHYSICAL_MAXIMUM(8, 0x00),\n            HID_RI_REPORT_SIZE(8, 0x06),\n            HID_RI_FEATURE(8, HID_IOF_CONSTANT),\n#    endif\n\n            // Vertical wheel (1 or 2 bytes)\n            HID_RI_USAGE(8, 0x38),     // Wheel\n#    ifndef WHEEL_EXTENDED_REPORT\n            HID_RI_LOGICAL_MINIMUM(8, -127),\n            HID_RI_LOGICAL_MAXIMUM(8, 127),\n            HID_RI_REPORT_COUNT(8, 0x01),\n            HID_RI_REPORT_SIZE(8, 0x08),\n#    else\n            HID_RI_LOGICAL_MINIMUM(16, -32767),\n            HID_RI_LOGICAL_MAXIMUM(16,  32767),\n            HID_RI_REPORT_COUNT(8, 0x01),\n            HID_RI_REPORT_SIZE(8, 0x10),\n#    endif\n            HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),\n\n            // Horizontal wheel (1 or 2 bytes)\n            HID_RI_USAGE_PAGE(8, 0x0C),// Consumer\n            HID_RI_USAGE(16, 0x0238),  // AC Pan\n#    ifndef WHEEL_EXTENDED_REPORT\n            HID_RI_LOGICAL_MINIMUM(8, -127),\n            HID_RI_LOGICAL_MAXIMUM(8, 127),\n            HID_RI_REPORT_COUNT(8, 0x01),\n            HID_RI_REPORT_SIZE(8, 0x08),\n#    else\n            HID_RI_LOGICAL_MINIMUM(16, -32767),\n            HID_RI_LOGICAL_MAXIMUM(16,  32767),\n            HID_RI_REPORT_COUNT(8, 0x01),\n            HID_RI_REPORT_SIZE(8, 0x10),\n#    endif\n            HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE),\n\n#    ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE\n            HID_RI_END_COLLECTION(0),\n#    endif\n\n        HID_RI_END_COLLECTION(0),\n    HID_RI_END_COLLECTION(0),\n#    ifndef MOUSE_SHARED_EP\n};\n#    endif\n#endif\n\n#ifdef JOYSTICK_ENABLE\n#    ifndef JOYSTICK_SHARED_EP\nconst USB_Descriptor_HIDReport_Datatype_t PROGMEM JoystickReport[] = {\n#    elif !defined(SHARED_REPORT_STARTED)\nconst USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {\n#        define SHARED_REPORT_STARTED\n#    endif\n    HID_RI_USAGE_PAGE(8, 0x01),     // Generic Desktop\n    HID_RI_USAGE(8, 0x04),          // Joystick\n    HID_RI_COLLECTION(8, 0x01),     // Application\n#    ifdef JOYSTICK_SHARED_EP\n        HID_RI_REPORT_ID(8, REPORT_ID_JOYSTICK),\n#    endif\n        HID_RI_COLLECTION(8, 0x00), // Physical\n#    if JOYSTICK_AXIS_COUNT > 0\n            HID_RI_USAGE_PAGE(8, 0x01), // Generic Desktop\n            HID_RI_USAGE(8, 0x30),      // X\n#        if JOYSTICK_AXIS_COUNT > 1\n            HID_RI_USAGE(8, 0x31),      // Y\n#        endif\n#        if JOYSTICK_AXIS_COUNT > 2\n            HID_RI_USAGE(8, 0x32),      // Z\n#        endif\n#        if JOYSTICK_AXIS_COUNT > 3\n            HID_RI_USAGE(8, 0x33),      // Rx\n#        endif\n#        if JOYSTICK_AXIS_COUNT > 4\n            HID_RI_USAGE(8, 0x34),      // Ry\n#        endif\n#        if JOYSTICK_AXIS_COUNT > 5\n            HID_RI_USAGE(8, 0x35),      // Rz\n#        endif\n#        if JOYSTICK_AXIS_RESOLUTION == 8\n            HID_RI_LOGICAL_MINIMUM(8, -JOYSTICK_MAX_VALUE),\n            HID_RI_LOGICAL_MAXIMUM(8, JOYSTICK_MAX_VALUE),\n            HID_RI_REPORT_COUNT(8, JOYSTICK_AXIS_COUNT),\n            HID_RI_REPORT_SIZE(8, 0x08),\n#        else\n            HID_RI_LOGICAL_MINIMUM(16, -JOYSTICK_MAX_VALUE),\n            HID_RI_LOGICAL_MAXIMUM(16, JOYSTICK_MAX_VALUE),\n            HID_RI_REPORT_COUNT(8, JOYSTICK_AXIS_COUNT),\n            HID_RI_REPORT_SIZE(8, 0x10),\n#        endif\n            HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n#    endif\n\n#    ifdef JOYSTICK_HAS_HAT\n            // Hat Switch (4 bits)\n            HID_RI_USAGE(8, 0x39), // Hat Switch\n            HID_RI_LOGICAL_MINIMUM(8, 0x00),\n            HID_RI_LOGICAL_MAXIMUM(8, 0x07),\n            HID_RI_PHYSICAL_MINIMUM(8, 0),\n            HID_RI_PHYSICAL_MAXIMUM(16, 315),\n            HID_RI_UNIT(8, 0x14),  // Degree, English Rotation\n            HID_RI_REPORT_COUNT(8, 1),\n            HID_RI_REPORT_SIZE(8, 4),\n            HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NULLSTATE),\n            // Padding (4 bits)\n            HID_RI_REPORT_COUNT(8, 0x04),\n            HID_RI_REPORT_SIZE(8, 0x01),\n            HID_RI_INPUT(8, HID_IOF_CONSTANT),\n#    endif\n\n#    if JOYSTICK_BUTTON_COUNT > 0\n            HID_RI_USAGE_PAGE(8, 0x09), // Button\n            HID_RI_USAGE_MINIMUM(8, 0x01),\n            HID_RI_USAGE_MAXIMUM(8, JOYSTICK_BUTTON_COUNT),\n            HID_RI_LOGICAL_MINIMUM(8, 0x00),\n            HID_RI_LOGICAL_MAXIMUM(8, 0x01),\n            HID_RI_REPORT_COUNT(8, JOYSTICK_BUTTON_COUNT),\n            HID_RI_REPORT_SIZE(8, 0x01),\n            HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n\n#        if (JOYSTICK_BUTTON_COUNT % 8) != 0\n            HID_RI_REPORT_COUNT(8, 8 - (JOYSTICK_BUTTON_COUNT % 8)),\n            HID_RI_REPORT_SIZE(8, 0x01),\n            HID_RI_INPUT(8, HID_IOF_CONSTANT),\n#        endif\n#    endif\n        HID_RI_END_COLLECTION(0),\n    HID_RI_END_COLLECTION(0),\n#    ifndef JOYSTICK_SHARED_EP\n};\n#    endif\n#endif\n\n#ifdef DIGITIZER_ENABLE\n#    ifndef DIGITIZER_SHARED_EP\nconst USB_Descriptor_HIDReport_Datatype_t PROGMEM DigitizerReport[] = {\n#    elif !defined(SHARED_REPORT_STARTED)\nconst USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {\n#        define SHARED_REPORT_STARTED\n#    endif\n    HID_RI_USAGE_PAGE(8, 0x0D),            // Digitizers\n    HID_RI_USAGE(8, 0x01),                 // Digitizer\n    HID_RI_COLLECTION(8, 0x01),            // Application\n#    ifdef DIGITIZER_SHARED_EP\n        HID_RI_REPORT_ID(8, REPORT_ID_DIGITIZER),\n#    endif\n        HID_RI_USAGE(8, 0x20),             // Stylus\n        HID_RI_COLLECTION(8, 0x00),        // Physical\n            // In Range, Tip Switch & Barrel Switch (3 bits)\n            HID_RI_USAGE(8, 0x32),         // In Range\n            HID_RI_USAGE(8, 0x42),         // Tip Switch\n            HID_RI_USAGE(8, 0x44),         // Barrel Switch\n            HID_RI_LOGICAL_MINIMUM(8, 0x00),\n            HID_RI_LOGICAL_MAXIMUM(8, 0x01),\n            HID_RI_REPORT_COUNT(8, 0x03),\n            HID_RI_REPORT_SIZE(8, 0x01),\n            HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n            // Padding (5 bits)\n            HID_RI_REPORT_COUNT(8, 0x05),\n            HID_RI_INPUT(8, HID_IOF_CONSTANT),\n\n            // X/Y Position (4 bytes)\n            HID_RI_USAGE_PAGE(8, 0x01),    // Generic Desktop\n            HID_RI_USAGE(8, 0x30),         // X\n            HID_RI_USAGE(8, 0x31),         // Y\n            HID_RI_LOGICAL_MAXIMUM(16, 0x7FFF),\n            HID_RI_REPORT_COUNT(8, 0x02),\n            HID_RI_REPORT_SIZE(8, 0x10),\n            HID_RI_UNIT(8, 0x13),          // Inch, English Linear\n            HID_RI_UNIT_EXPONENT(8, 0x0E), // -2\n            HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n        HID_RI_END_COLLECTION(0),\n    HID_RI_END_COLLECTION(0),\n#    ifndef DIGITIZER_SHARED_EP\n};\n#    endif\n#endif\n\n#if defined(SHARED_EP_ENABLE) && !defined(SHARED_REPORT_STARTED)\nconst USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {\n#endif\n\n#ifdef EXTRAKEY_ENABLE\n    HID_RI_USAGE_PAGE(8, 0x01),           // Generic Desktop\n    HID_RI_USAGE(8, 0x80),                // System Control\n    HID_RI_COLLECTION(8, 0x01),           // Application\n        HID_RI_REPORT_ID(8, REPORT_ID_SYSTEM),\n        HID_RI_USAGE_MINIMUM(8, 0x01),    // Pointer\n        HID_RI_USAGE_MAXIMUM(16, 0x00B7), // System Display LCD Autoscale\n        HID_RI_LOGICAL_MINIMUM(8, 0x01),\n        HID_RI_LOGICAL_MAXIMUM(16, 0x00B7),\n        HID_RI_REPORT_COUNT(8, 1),\n        HID_RI_REPORT_SIZE(8, 16),\n        HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),\n    HID_RI_END_COLLECTION(0),\n\n    HID_RI_USAGE_PAGE(8, 0x0C),           // Consumer\n    HID_RI_USAGE(8, 0x01),                // Consumer Control\n    HID_RI_COLLECTION(8, 0x01),           // Application\n        HID_RI_REPORT_ID(8, REPORT_ID_CONSUMER),\n        HID_RI_USAGE_MINIMUM(8, 0x01),    // Consumer Control\n        HID_RI_USAGE_MAXIMUM(16, 0x02A0), // AC Desktop Show All Applications\n        HID_RI_LOGICAL_MINIMUM(8, 0x01),\n        HID_RI_LOGICAL_MAXIMUM(16, 0x02A0),\n        HID_RI_REPORT_COUNT(8, 1),\n        HID_RI_REPORT_SIZE(8, 16),\n        HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE),\n    HID_RI_END_COLLECTION(0),\n#endif\n\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\n    HID_RI_USAGE_PAGE(8, 0x0C),            // Consumer\n    HID_RI_USAGE(8, 0x01),                 // Consumer Control\n    HID_RI_COLLECTION(8, 0x01),            // Application\n        HID_RI_REPORT_ID(8, REPORT_ID_PROGRAMMABLE_BUTTON),\n        HID_RI_USAGE(8, 0x03),             // Programmable Buttons\n        HID_RI_COLLECTION(8, 0x04),        // Named Array\n            HID_RI_USAGE_PAGE(8, 0x09),    // Button\n            HID_RI_USAGE_MINIMUM(8, 0x01), // Button 1\n            HID_RI_USAGE_MAXIMUM(8, 0x20), // Button 32\n            HID_RI_LOGICAL_MINIMUM(8, 0x00),\n            HID_RI_LOGICAL_MAXIMUM(8, 0x01),\n            HID_RI_REPORT_COUNT(8, 32),\n            HID_RI_REPORT_SIZE(8, 1),\n            HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n        HID_RI_END_COLLECTION(0),\n    HID_RI_END_COLLECTION(0),\n#endif\n\n#ifdef NKRO_ENABLE\n    HID_RI_USAGE_PAGE(8, 0x01),        // Generic Desktop\n    HID_RI_USAGE(8, 0x06),             // Keyboard\n    HID_RI_COLLECTION(8, 0x01),        // Application\n        HID_RI_REPORT_ID(8, REPORT_ID_NKRO),\n        // Modifiers (8 bits)\n        HID_RI_USAGE_PAGE(8, 0x07),    // Keyboard/Keypad\n        HID_RI_USAGE_MINIMUM(8, 0xE0), // Keyboard Left Control\n        HID_RI_USAGE_MAXIMUM(8, 0xE7), // Keyboard Right GUI\n        HID_RI_LOGICAL_MINIMUM(8, 0x00),\n        HID_RI_LOGICAL_MAXIMUM(8, 0x01),\n        HID_RI_REPORT_COUNT(8, 0x08),\n        HID_RI_REPORT_SIZE(8, 0x01),\n        HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n        // Keycodes\n        HID_RI_USAGE_PAGE(8, 0x07),    // Keyboard/Keypad\n        HID_RI_USAGE_MINIMUM(8, 0x00),\n        HID_RI_USAGE_MAXIMUM(8, NKRO_REPORT_BITS * 8 - 1),\n        HID_RI_LOGICAL_MINIMUM(8, 0x00),\n        HID_RI_LOGICAL_MAXIMUM(8, 0x01),\n        HID_RI_REPORT_COUNT(8, NKRO_REPORT_BITS * 8),\n        HID_RI_REPORT_SIZE(8, 0x01),\n        HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n\n        // Status LEDs (5 bits)\n        HID_RI_USAGE_PAGE(8, 0x08),    // LED\n        HID_RI_USAGE_MINIMUM(8, 0x01), // Num Lock\n        HID_RI_USAGE_MAXIMUM(8, 0x05), // Kana\n        HID_RI_REPORT_COUNT(8, 0x05),\n        HID_RI_REPORT_SIZE(8, 0x01),\n        HID_RI_OUTPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE),\n        // LED padding (3 bits)\n        HID_RI_REPORT_COUNT(8, 0x01),\n        HID_RI_REPORT_SIZE(8, 0x03),\n        HID_RI_OUTPUT(8, HID_IOF_CONSTANT),\n    HID_RI_END_COLLECTION(0),\n#endif\n\n#ifdef SHARED_EP_ENABLE\n};\n#endif\n\n#ifdef RAW_ENABLE\nconst USB_Descriptor_HIDReport_Datatype_t PROGMEM RawReport[] = {\n    HID_RI_USAGE_PAGE(16, RAW_USAGE_PAGE), // Vendor Defined\n    HID_RI_USAGE(8, RAW_USAGE_ID),         // Vendor Defined\n    HID_RI_COLLECTION(8, 0x01),    // Application\n        // Data to host\n        HID_RI_USAGE(8, 0x62),     // Vendor Defined\n        HID_RI_LOGICAL_MINIMUM(8, 0x00),\n        HID_RI_LOGICAL_MAXIMUM(16, 0x00FF),\n        HID_RI_REPORT_COUNT(8, RAW_EPSIZE),\n        HID_RI_REPORT_SIZE(8, 0x08),\n        HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n\n        // Data from host\n        HID_RI_USAGE(8, 0x63),     // Vendor Defined\n        HID_RI_LOGICAL_MINIMUM(8, 0x00),\n        HID_RI_LOGICAL_MAXIMUM(16, 0x00FF),\n        HID_RI_REPORT_COUNT(8, RAW_EPSIZE),\n        HID_RI_REPORT_SIZE(8, 0x08),\n        HID_RI_OUTPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE),\n    HID_RI_END_COLLECTION(0),\n};\n#endif\n\n#ifdef CONSOLE_ENABLE\nconst USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = {\n    HID_RI_USAGE_PAGE(16, 0xFF31), // Vendor Defined (PJRC Teensy compatible)\n    HID_RI_USAGE(8, 0x74),         // Vendor Defined (PJRC Teensy compatible)\n    HID_RI_COLLECTION(8, 0x01),    // Application\n        // Data to host\n        HID_RI_USAGE(8, 0x75),     // Vendor Defined\n        HID_RI_LOGICAL_MINIMUM(8, 0x00),\n        HID_RI_LOGICAL_MAXIMUM(16, 0x00FF),\n        HID_RI_REPORT_COUNT(8, CONSOLE_EPSIZE),\n        HID_RI_REPORT_SIZE(8, 0x08),\n        HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE),\n    HID_RI_END_COLLECTION(0),\n};\n#endif\n\n/*\n * Device descriptor\n */\nconst USB_Descriptor_Device_t PROGMEM DeviceDescriptor = {\n    .Header = {\n        .Size                   = sizeof(USB_Descriptor_Device_t),\n        .Type                   = DTYPE_Device\n    },\n    .USBSpecification           = VERSION_BCD(2, 0, 0),\n\n#if VIRTSER_ENABLE\n    .Class                      = USB_CSCP_IADDeviceClass,\n    .SubClass                   = USB_CSCP_IADDeviceSubclass,\n    .Protocol                   = USB_CSCP_IADDeviceProtocol,\n#else\n    .Class                      = USB_CSCP_NoDeviceClass,\n    .SubClass                   = USB_CSCP_NoDeviceSubclass,\n    .Protocol                   = USB_CSCP_NoDeviceProtocol,\n#endif\n\n    .Endpoint0Size              = FIXED_CONTROL_ENDPOINT_SIZE,\n    // Specified in config.h\n    .VendorID                   = VENDOR_ID,\n    .ProductID                  = PRODUCT_ID,\n    .ReleaseNumber              = DEVICE_VER,\n    .ManufacturerStrIndex       = 0x01,\n    .ProductStrIndex            = 0x02,\n#ifdef HAS_SERIAL_NUMBER\n    .SerialNumStrIndex          = 0x03,\n#else // HAS_SERIAL_NUMBER\n    .SerialNumStrIndex          = 0x00,\n#endif // HAS_SERIAL_NUMBER\n    .NumberOfConfigurations     = FIXED_NUM_CONFIGURATIONS\n};\n\n#ifndef USB_MAX_POWER_CONSUMPTION\n#    define USB_MAX_POWER_CONSUMPTION 500\n#endif\n\n#ifndef USB_POLLING_INTERVAL_MS\n#    define USB_POLLING_INTERVAL_MS 1\n#endif\n\n/*\n * Configuration descriptors\n */\nconst USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {\n    .Config = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Configuration_Header_t),\n            .Type               = DTYPE_Configuration\n        },\n        .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t),\n        .TotalInterfaces        = TOTAL_INTERFACES,\n        .ConfigurationNumber    = 1,\n        .ConfigurationStrIndex  = NO_DESCRIPTOR,\n        .ConfigAttributes       = (USB_CONFIG_ATTR_RESERVED | USB_CONFIG_ATTR_REMOTEWAKEUP),\n        .MaxPowerConsumption    = USB_CONFIG_POWER_MA(USB_MAX_POWER_CONSUMPTION)\n    },\n#ifndef KEYBOARD_SHARED_EP\n    /*\n     * Keyboard\n     */\n    .Keyboard_Interface = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_t),\n            .Type               = DTYPE_Interface\n        },\n        .InterfaceNumber        = KEYBOARD_INTERFACE,\n        .AlternateSetting       = 0x00,\n        .TotalEndpoints         = 1,\n        .Class                  = HID_CSCP_HIDClass,\n        .SubClass               = HID_CSCP_BootSubclass,\n        .Protocol               = HID_CSCP_KeyboardBootProtocol,\n        .InterfaceStrIndex      = NO_DESCRIPTOR\n    },\n    .Keyboard_HID = {\n        .Header = {\n            .Size               = sizeof(USB_HID_Descriptor_HID_t),\n            .Type               = HID_DTYPE_HID\n        },\n        .HIDSpec                = VERSION_BCD(1, 1, 1),\n        .CountryCode            = 0x00,\n        .TotalReportDescriptors = 1,\n        .HIDReportType          = HID_DTYPE_Report,\n        .HIDReportLength        = sizeof(KeyboardReport)\n    },\n    .Keyboard_INEndpoint = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Endpoint_t),\n            .Type               = DTYPE_Endpoint\n        },\n        .EndpointAddress        = (ENDPOINT_DIR_IN | KEYBOARD_IN_EPNUM),\n        .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n        .EndpointSize           = KEYBOARD_EPSIZE,\n        .PollingIntervalMS      = USB_POLLING_INTERVAL_MS\n    },\n#endif\n\n#ifdef RAW_ENABLE\n    /*\n     * Raw HID\n     */\n    .Raw_Interface = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_t),\n            .Type               = DTYPE_Interface\n        },\n        .InterfaceNumber        = RAW_INTERFACE,\n        .AlternateSetting       = 0x00,\n        .TotalEndpoints         = 2,\n        .Class                  = HID_CSCP_HIDClass,\n        .SubClass               = HID_CSCP_NonBootSubclass,\n        .Protocol               = HID_CSCP_NonBootProtocol,\n        .InterfaceStrIndex      = NO_DESCRIPTOR\n    },\n    .Raw_HID = {\n        .Header = {\n            .Size               = sizeof(USB_HID_Descriptor_HID_t),\n            .Type               = HID_DTYPE_HID\n        },\n        .HIDSpec                = VERSION_BCD(1, 1, 1),\n        .CountryCode            = 0x00,\n        .TotalReportDescriptors = 1,\n        .HIDReportType          = HID_DTYPE_Report,\n        .HIDReportLength        = sizeof(RawReport)\n    },\n    .Raw_INEndpoint = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Endpoint_t),\n            .Type               = DTYPE_Endpoint\n        },\n        .EndpointAddress        = (ENDPOINT_DIR_IN | RAW_IN_EPNUM),\n        .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n        .EndpointSize           = RAW_EPSIZE,\n        .PollingIntervalMS      = 0x01\n    },\n    .Raw_OUTEndpoint = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Endpoint_t),\n            .Type               = DTYPE_Endpoint\n        },\n        .EndpointAddress        = (ENDPOINT_DIR_OUT | RAW_OUT_EPNUM),\n        .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n        .EndpointSize           = RAW_EPSIZE,\n        .PollingIntervalMS      = 0x01\n    },\n#endif\n\n#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)\n    /*\n     * Mouse\n     */\n    .Mouse_Interface  = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_t),\n            .Type               = DTYPE_Interface\n        },\n        .InterfaceNumber        = MOUSE_INTERFACE,\n        .AlternateSetting       = 0x00,\n        .TotalEndpoints         = 1,\n        .Class                  = HID_CSCP_HIDClass,\n        .SubClass               = HID_CSCP_BootSubclass,\n        .Protocol               = HID_CSCP_MouseBootProtocol,\n        .InterfaceStrIndex      = NO_DESCRIPTOR\n    },\n    .Mouse_HID = {\n        .Header = {\n            .Size               = sizeof(USB_HID_Descriptor_HID_t),\n            .Type               = HID_DTYPE_HID\n        },\n        .HIDSpec                = VERSION_BCD(1, 1, 1),\n        .CountryCode            = 0x00,\n        .TotalReportDescriptors = 1,\n        .HIDReportType          = HID_DTYPE_Report,\n        .HIDReportLength        = sizeof(MouseReport)\n    },\n    .Mouse_INEndpoint = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Endpoint_t),\n            .Type               = DTYPE_Endpoint\n        },\n        .EndpointAddress        = (ENDPOINT_DIR_IN | MOUSE_IN_EPNUM),\n        .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n        .EndpointSize           = MOUSE_EPSIZE,\n        .PollingIntervalMS      = USB_POLLING_INTERVAL_MS\n    },\n#endif\n\n#ifdef SHARED_EP_ENABLE\n    /*\n     * Shared\n     */\n    .Shared_Interface = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_t),\n            .Type               = DTYPE_Interface\n        },\n        .InterfaceNumber        = SHARED_INTERFACE,\n        .AlternateSetting       = 0x00,\n        .TotalEndpoints         = 1,\n        .Class                  = HID_CSCP_HIDClass,\n#    ifdef KEYBOARD_SHARED_EP\n        .SubClass               = HID_CSCP_BootSubclass,\n        .Protocol               = HID_CSCP_KeyboardBootProtocol,\n#    else\n        .SubClass               = HID_CSCP_NonBootSubclass,\n        .Protocol               = HID_CSCP_NonBootProtocol,\n#    endif\n        .InterfaceStrIndex      = NO_DESCRIPTOR\n    },\n    .Shared_HID = {\n        .Header = {\n            .Size               = sizeof(USB_HID_Descriptor_HID_t),\n            .Type               = HID_DTYPE_HID\n        },\n        .HIDSpec                = VERSION_BCD(1, 1, 1),\n        .CountryCode            = 0x00,\n        .TotalReportDescriptors = 1,\n        .HIDReportType          = HID_DTYPE_Report,\n        .HIDReportLength        = sizeof(SharedReport)\n    },\n    .Shared_INEndpoint = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Endpoint_t),\n            .Type               = DTYPE_Endpoint\n        },\n        .EndpointAddress        = (ENDPOINT_DIR_IN | SHARED_IN_EPNUM),\n        .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n        .EndpointSize           = SHARED_EPSIZE,\n        .PollingIntervalMS      = USB_POLLING_INTERVAL_MS\n    },\n#endif\n\n#ifdef CONSOLE_ENABLE\n    /*\n     * Console\n     */\n    .Console_Interface = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_t),\n            .Type               = DTYPE_Interface\n        },\n        .InterfaceNumber        = CONSOLE_INTERFACE,\n        .AlternateSetting       = 0x00,\n        .TotalEndpoints         = 1,\n        .Class                  = HID_CSCP_HIDClass,\n        .SubClass               = HID_CSCP_NonBootSubclass,\n        .Protocol               = HID_CSCP_NonBootProtocol,\n        .InterfaceStrIndex      = NO_DESCRIPTOR\n    },\n    .Console_HID = {\n        .Header = {\n            .Size               = sizeof(USB_HID_Descriptor_HID_t),\n            .Type               = HID_DTYPE_HID\n        },\n        .HIDSpec                = VERSION_BCD(1, 1, 1),\n        .CountryCode            = 0x00,\n        .TotalReportDescriptors = 1,\n        .HIDReportType          = HID_DTYPE_Report,\n        .HIDReportLength        = sizeof(ConsoleReport)\n    },\n    .Console_INEndpoint = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Endpoint_t),\n            .Type               = DTYPE_Endpoint\n        },\n        .EndpointAddress        = (ENDPOINT_DIR_IN | CONSOLE_IN_EPNUM),\n        .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n        .EndpointSize           = CONSOLE_EPSIZE,\n        .PollingIntervalMS      = 0x01\n    },\n#endif\n\n#ifdef MIDI_ENABLE\n    /*\n     * MIDI\n     */\n    .Audio_Interface_Association = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_Association_t),\n            .Type               = DTYPE_InterfaceAssociation\n        },\n        .FirstInterfaceIndex    = AC_INTERFACE,\n        .TotalInterfaces        = 2,\n        .Class                  = AUDIO_CSCP_AudioClass,\n        .SubClass               = AUDIO_CSCP_ControlSubclass,\n        .Protocol               = AUDIO_CSCP_ControlProtocol,\n        .IADStrIndex            = NO_DESCRIPTOR,\n    },\n    .Audio_ControlInterface = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_t),\n            .Type               = DTYPE_Interface\n        },\n        .InterfaceNumber        = AC_INTERFACE,\n        .AlternateSetting       = 0,\n        .TotalEndpoints         = 0,\n        .Class                  = AUDIO_CSCP_AudioClass,\n        .SubClass               = AUDIO_CSCP_ControlSubclass,\n        .Protocol               = AUDIO_CSCP_ControlProtocol,\n        .InterfaceStrIndex      = NO_DESCRIPTOR\n    },\n    .Audio_ControlInterface_SPC = {\n        .Header = {\n            .Size               = sizeof(USB_Audio_Descriptor_Interface_AC_t),\n            .Type               = AUDIO_DTYPE_CSInterface\n        },\n        .Subtype                = AUDIO_DSUBTYPE_CSInterface_Header,\n        .ACSpecification        = VERSION_BCD(1, 0, 0),\n        .TotalLength            = sizeof(USB_Audio_Descriptor_Interface_AC_t),\n        .InCollection           = 1,\n        .InterfaceNumber        = AS_INTERFACE,\n    },\n    .Audio_StreamInterface = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_t),\n            .Type               = DTYPE_Interface\n        },\n        .InterfaceNumber        = AS_INTERFACE,\n        .AlternateSetting       = 0,\n        .TotalEndpoints         = 2,\n        .Class                  = AUDIO_CSCP_AudioClass,\n        .SubClass               = AUDIO_CSCP_MIDIStreamingSubclass,\n        .Protocol               = AUDIO_CSCP_StreamingProtocol,\n        .InterfaceStrIndex      = NO_DESCRIPTOR\n    },\n    .Audio_StreamInterface_SPC = {\n        .Header = {\n            .Size               = sizeof(USB_MIDI_Descriptor_AudioInterface_AS_t),\n            .Type               = AUDIO_DTYPE_CSInterface\n        },\n        .Subtype                = AUDIO_DSUBTYPE_CSInterface_General,\n        .AudioSpecification     = VERSION_BCD(1, 0, 0),\n        .TotalLength            = offsetof(USB_Descriptor_Configuration_t, MIDI_Out_Jack_Endpoint_SPC) + sizeof(USB_MIDI_Descriptor_Jack_Endpoint_t) - offsetof(USB_Descriptor_Configuration_t, Audio_StreamInterface_SPC)\n    },\n    .MIDI_In_Jack_Emb = {\n        .Header = {\n            .Size               = sizeof(USB_MIDI_Descriptor_InputJack_t),\n            .Type               = AUDIO_DTYPE_CSInterface\n        },\n        .Subtype                = AUDIO_DSUBTYPE_CSInterface_InputTerminal,\n        .JackType               = MIDI_JACKTYPE_Embedded,\n        .JackID = 0x01,\n        .JackStrIndex           = NO_DESCRIPTOR\n    },\n    .MIDI_In_Jack_Ext = {\n        .Header = {\n            .Size               = sizeof(USB_MIDI_Descriptor_InputJack_t),\n            .Type               = AUDIO_DTYPE_CSInterface\n        },\n        .Subtype                = AUDIO_DSUBTYPE_CSInterface_InputTerminal,\n        .JackType               = MIDI_JACKTYPE_External,\n        .JackID                 = 0x02,\n        .JackStrIndex           = NO_DESCRIPTOR\n    },\n    .MIDI_Out_Jack_Emb = {\n        .Header = {\n            .Size               = sizeof(USB_MIDI_Descriptor_OutputJack_t),\n            .Type               = AUDIO_DTYPE_CSInterface\n        },\n        .Subtype                = AUDIO_DSUBTYPE_CSInterface_OutputTerminal,\n        .JackType               = MIDI_JACKTYPE_Embedded,\n        .JackID                 = 0x03,\n        .NumberOfPins           = 1,\n        .SourceJackID           = {0x02},\n        .SourcePinID            = {0x01},\n        .JackStrIndex           = NO_DESCRIPTOR\n    },\n    .MIDI_Out_Jack_Ext = {\n        .Header = {\n            .Size               = sizeof(USB_MIDI_Descriptor_OutputJack_t),\n            .Type               = AUDIO_DTYPE_CSInterface\n        },\n        .Subtype                = AUDIO_DSUBTYPE_CSInterface_OutputTerminal,\n        .JackType               = MIDI_JACKTYPE_External,\n        .JackID                 = 0x04,\n        .NumberOfPins           = 1,\n        .SourceJackID           = {0x01},\n        .SourcePinID            = {0x01},\n        .JackStrIndex           = NO_DESCRIPTOR\n    },\n    .MIDI_In_Jack_Endpoint = {\n        .Endpoint = {\n            .Header = {\n                .Size           = sizeof(USB_Audio_Descriptor_StreamEndpoint_Std_t),\n                .Type           = DTYPE_Endpoint\n            },\n            .EndpointAddress    = (ENDPOINT_DIR_OUT | MIDI_STREAM_OUT_EPNUM),\n            .Attributes         = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n            .EndpointSize       = MIDI_STREAM_EPSIZE,\n            .PollingIntervalMS  = 0x05\n        },\n        .Refresh                = 0,\n        .SyncEndpointNumber     = 0\n    },\n    .MIDI_In_Jack_Endpoint_SPC = {\n        .Header = {\n            .Size               = sizeof(USB_MIDI_Descriptor_Jack_Endpoint_t),\n            .Type               = AUDIO_DTYPE_CSEndpoint\n        },\n        .Subtype                = AUDIO_DSUBTYPE_CSEndpoint_General,\n        .TotalEmbeddedJacks     = 0x01,\n        .AssociatedJackID       = {0x01}\n    },\n    .MIDI_Out_Jack_Endpoint = {\n        .Endpoint = {\n            .Header = {\n                .Size           = sizeof(USB_Audio_Descriptor_StreamEndpoint_Std_t),\n                .Type           = DTYPE_Endpoint\n            },\n            .EndpointAddress    = (ENDPOINT_DIR_IN | MIDI_STREAM_IN_EPNUM),\n            .Attributes         = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n            .EndpointSize       = MIDI_STREAM_EPSIZE,\n            .PollingIntervalMS  = 0x05\n        },\n        .Refresh                = 0,\n        .SyncEndpointNumber     = 0\n    },\n    .MIDI_Out_Jack_Endpoint_SPC = {\n        .Header = {\n            .Size               = sizeof(USB_MIDI_Descriptor_Jack_Endpoint_t),\n            .Type               = AUDIO_DTYPE_CSEndpoint\n        },\n        .Subtype                = AUDIO_DSUBTYPE_CSEndpoint_General,\n        .TotalEmbeddedJacks     = 0x01,\n        .AssociatedJackID       = {0x03}\n    },\n#endif\n\n#ifdef VIRTSER_ENABLE\n    /*\n     * Virtual Serial\n     */\n    .CDC_Interface_Association = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_Association_t),\n            .Type               = DTYPE_InterfaceAssociation\n        },\n        .FirstInterfaceIndex    = CCI_INTERFACE,\n        .TotalInterfaces        = 2,\n        .Class                  = CDC_CSCP_CDCClass,\n        .SubClass               = CDC_CSCP_ACMSubclass,\n        .Protocol               = CDC_CSCP_ATCommandProtocol,\n        .IADStrIndex            = NO_DESCRIPTOR,\n    },\n    .CDC_CCI_Interface = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_t),\n            .Type               = DTYPE_Interface\n        },\n        .InterfaceNumber        = CCI_INTERFACE,\n        .AlternateSetting       = 0,\n        .TotalEndpoints         = 1,\n        .Class                  = CDC_CSCP_CDCClass,\n        .SubClass               = CDC_CSCP_ACMSubclass,\n        .Protocol               = CDC_CSCP_ATCommandProtocol,\n        .InterfaceStrIndex      = NO_DESCRIPTOR\n    },\n    .CDC_Functional_Header = {\n        .Header = {\n            .Size               = sizeof(USB_CDC_Descriptor_FunctionalHeader_t),\n            .Type               = CDC_DTYPE_CSInterface\n        },\n        .Subtype                = 0x00,\n        .CDCSpecification       = VERSION_BCD(1, 1, 0),\n    },\n    .CDC_Functional_ACM = {\n        .Header = {\n            .Size               = sizeof(USB_CDC_Descriptor_FunctionalACM_t),\n            .Type               = CDC_DTYPE_CSInterface\n        },\n        .Subtype                = 0x02,\n        .Capabilities           = 0x02,\n    },\n    .CDC_Functional_Union = {\n        .Header = {\n            .Size               = sizeof(USB_CDC_Descriptor_FunctionalUnion_t),\n            .Type               = CDC_DTYPE_CSInterface\n        },\n        .Subtype                = 0x06,\n        .MasterInterfaceNumber  = CCI_INTERFACE,\n        .SlaveInterfaceNumber   = CDI_INTERFACE,\n    },\n    .CDC_NotificationEndpoint = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Endpoint_t),\n            .Type               = DTYPE_Endpoint\n        },\n        .EndpointAddress        = (ENDPOINT_DIR_IN | CDC_NOTIFICATION_EPNUM),\n        .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n        .EndpointSize           = CDC_NOTIFICATION_EPSIZE,\n        .PollingIntervalMS      = 0xFF\n    },\n    .CDC_DCI_Interface = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_t),\n            .Type               = DTYPE_Interface\n        },\n        .InterfaceNumber        = CDI_INTERFACE,\n        .AlternateSetting       = 0,\n        .TotalEndpoints         = 2,\n        .Class                  = CDC_CSCP_CDCDataClass,\n        .SubClass               = CDC_CSCP_NoDataSubclass,\n        .Protocol               = CDC_CSCP_NoDataProtocol,\n        .InterfaceStrIndex      = NO_DESCRIPTOR\n    },\n    .CDC_DataOutEndpoint = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Endpoint_t),\n            .Type               = DTYPE_Endpoint\n        },\n        .EndpointAddress        = (ENDPOINT_DIR_OUT | CDC_OUT_EPNUM),\n        .Attributes             = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n        .EndpointSize           = CDC_EPSIZE,\n        .PollingIntervalMS      = 0x05\n    },\n    .CDC_DataInEndpoint = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Endpoint_t),\n            .Type               = DTYPE_Endpoint\n        },\n        .EndpointAddress        = (ENDPOINT_DIR_IN | CDC_IN_EPNUM),\n        .Attributes             = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n        .EndpointSize           = CDC_EPSIZE,\n        .PollingIntervalMS      = 0x05\n    },\n#endif\n\n#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)\n    /*\n     * Joystick\n     */\n    .Joystick_Interface = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_t),\n            .Type               = DTYPE_Interface\n        },\n        .InterfaceNumber        = JOYSTICK_INTERFACE,\n        .AlternateSetting       = 0x00,\n        .TotalEndpoints         = 1,\n        .Class                  = HID_CSCP_HIDClass,\n        .SubClass               = HID_CSCP_NonBootSubclass,\n        .Protocol               = HID_CSCP_NonBootProtocol,\n        .InterfaceStrIndex      = NO_DESCRIPTOR\n    },\n    .Joystick_HID = {\n        .Header = {\n            .Size               = sizeof(USB_HID_Descriptor_HID_t),\n            .Type               = HID_DTYPE_HID\n        },\n        .HIDSpec                = VERSION_BCD(1, 1, 1),\n        .CountryCode            = 0x00,\n        .TotalReportDescriptors = 1,\n        .HIDReportType          = HID_DTYPE_Report,\n        .HIDReportLength        = sizeof(JoystickReport)\n    },\n    .Joystick_INEndpoint = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Endpoint_t),\n            .Type               = DTYPE_Endpoint\n        },\n        .EndpointAddress        = (ENDPOINT_DIR_IN | JOYSTICK_IN_EPNUM),\n        .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n        .EndpointSize           = JOYSTICK_EPSIZE,\n        .PollingIntervalMS      = USB_POLLING_INTERVAL_MS\n    },\n#endif\n\n#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)\n    /*\n     * Digitizer\n     */\n    .Digitizer_Interface  = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Interface_t),\n            .Type               = DTYPE_Interface\n        },\n        .InterfaceNumber        = DIGITIZER_INTERFACE,\n        .AlternateSetting       = 0x00,\n        .TotalEndpoints         = 1,\n        .Class                  = HID_CSCP_HIDClass,\n        .SubClass               = HID_CSCP_NonBootSubclass,\n        .Protocol               = HID_CSCP_NonBootProtocol,\n        .InterfaceStrIndex      = NO_DESCRIPTOR\n    },\n    .Digitizer_HID = {\n        .Header = {\n            .Size               = sizeof(USB_HID_Descriptor_HID_t),\n            .Type               = HID_DTYPE_HID\n        },\n        .HIDSpec                = VERSION_BCD(1, 1, 1),\n        .CountryCode            = 0x00,\n        .TotalReportDescriptors = 1,\n        .HIDReportType          = HID_DTYPE_Report,\n        .HIDReportLength        = sizeof(DigitizerReport)\n    },\n    .Digitizer_INEndpoint = {\n        .Header = {\n            .Size               = sizeof(USB_Descriptor_Endpoint_t),\n            .Type               = DTYPE_Endpoint\n        },\n        .EndpointAddress        = (ENDPOINT_DIR_IN | DIGITIZER_IN_EPNUM),\n        .Attributes             = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA),\n        .EndpointSize           = DIGITIZER_EPSIZE,\n        .PollingIntervalMS      = USB_POLLING_INTERVAL_MS\n    },\n#endif\n};\n\n/*\n * String descriptors\n */\n\n#define USB_DESCRIPTOR_SIZE_LITERAL_U16STRING(str) \\\n    (sizeof(USB_Descriptor_Header_t) + sizeof(str) - sizeof(wchar_t)) // include header, don't count null terminator\n\nconst USB_Descriptor_String_t PROGMEM LanguageString = {\n    .Header = {\n        .Size                   = sizeof(USB_Descriptor_Header_t) + sizeof(uint16_t),\n        .Type                   = DTYPE_String\n    },\n    .UnicodeString              = {LANGUAGE_ID_ENG}\n};\n\nconst USB_Descriptor_String_t PROGMEM ManufacturerString = {\n    .Header = {\n        .Size                   = USB_DESCRIPTOR_SIZE_LITERAL_U16STRING(USBSTR(MANUFACTURER)),\n        .Type                   = DTYPE_String\n    },\n    .UnicodeString              = USBSTR(MANUFACTURER)\n};\n\nconst USB_Descriptor_String_t PROGMEM ProductString = {\n    .Header = {\n        .Size                   = USB_DESCRIPTOR_SIZE_LITERAL_U16STRING(USBSTR(PRODUCT)),\n        .Type                   = DTYPE_String\n    },\n    .UnicodeString              = USBSTR(PRODUCT)\n};\n\n// clang-format on\n\n#if defined(SERIAL_NUMBER)\n// clang-format off\nconst USB_Descriptor_String_t PROGMEM SerialNumberString = {\n    .Header = {\n        .Size                   = USB_DESCRIPTOR_SIZE_LITERAL_U16STRING(USBSTR(SERIAL_NUMBER)),\n        .Type                   = DTYPE_String\n    },\n    .UnicodeString              = USBSTR(SERIAL_NUMBER)\n};\n// clang-format on\n\n#else // defined(SERIAL_NUMBER)\n\n#    if defined(SERIAL_NUMBER_USE_HARDWARE_ID) && SERIAL_NUMBER_USE_HARDWARE_ID == TRUE\n\n#        if defined(__AVR__)\n#            error Dynamically setting the serial number on AVR is unsupported as LUFA requires the string to be in PROGMEM.\n#        endif // defined(__AVR__)\n\n#        ifndef SERIAL_NUMBER_LENGTH\n#            define SERIAL_NUMBER_LENGTH (sizeof(hardware_id_t) * 2)\n#        endif\n\n#        define SERIAL_NUMBER_DESCRIPTOR_SIZE                                            \\\n            (sizeof(USB_Descriptor_Header_t)                     /* Descriptor header */ \\\n             + (((SERIAL_NUMBER_LENGTH) + 1) * sizeof(wchar_t))) /* Length of serial number, with potential extra character as we're converting 2 nibbles at a time */\n\nuint8_t SerialNumberString[SERIAL_NUMBER_DESCRIPTOR_SIZE] = {0};\n\nvoid set_serial_number_descriptor(void) {\n    static bool is_set = false;\n    if (is_set) {\n        return;\n    }\n    is_set = true;\n\n    static const char        hex_str[] = \"0123456789ABCDEF\";\n    hardware_id_t            id        = get_hardware_id();\n    USB_Descriptor_String_t* desc      = (USB_Descriptor_String_t*)SerialNumberString;\n\n    // Copy across nibbles from the hardware ID as unicode hex characters\n    int      length = MIN(sizeof(id) * 2, SERIAL_NUMBER_LENGTH);\n    uint8_t* p      = (uint8_t*)&id;\n    for (int i = 0; i < length; i += 2) {\n        desc->UnicodeString[i + 0] = hex_str[p[i / 2] >> 4];\n        desc->UnicodeString[i + 1] = hex_str[p[i / 2] & 0xF];\n    }\n\n    desc->Header.Size = sizeof(USB_Descriptor_Header_t) + (length * sizeof(wchar_t)); // includes header, don't count null terminator\n    desc->Header.Type = DTYPE_String;\n}\n\n#    endif // defined(SERIAL_NUMBER_USE_HARDWARE_ID) && SERIAL_NUMBER_USE_HARDWARE_ID == TRUE\n\n#endif // defined(SERIAL_NUMBER)\n\n/**\n * This function is called by the library when in device mode, and must be overridden (see library \"USB Descriptors\"\n * documentation) by the application code so that the address and size of a requested descriptor can be given\n * to the USB library. When the device receives a Get Descriptor request on the control endpoint, this function\n * is called so that the descriptor details can be passed back and the appropriate descriptor sent back to the\n * USB host.\n */\nuint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const uint16_t wLength, const void** const DescriptorAddress) {\n    const uint8_t DescriptorType  = (wValue >> 8);\n    const uint8_t DescriptorIndex = (wValue & 0xFF);\n    const void*   Address         = NULL;\n    uint16_t      Size            = NO_DESCRIPTOR;\n\n    switch (DescriptorType) {\n        case DTYPE_Device:\n            Address = &DeviceDescriptor;\n            Size    = sizeof(USB_Descriptor_Device_t);\n\n            break;\n        case DTYPE_Configuration:\n            Address = &ConfigurationDescriptor;\n            Size    = sizeof(USB_Descriptor_Configuration_t);\n\n            break;\n        case DTYPE_String:\n            switch (DescriptorIndex) {\n                case 0x00:\n                    Address = &LanguageString;\n                    Size    = pgm_read_byte(&LanguageString.Header.Size);\n\n                    break;\n                case 0x01:\n                    Address = &ManufacturerString;\n                    Size    = pgm_read_byte(&ManufacturerString.Header.Size);\n\n                    break;\n                case 0x02:\n                    Address = &ProductString;\n                    Size    = pgm_read_byte(&ProductString.Header.Size);\n\n                    break;\n#ifdef HAS_SERIAL_NUMBER\n                case 0x03:\n                    Address = (const USB_Descriptor_String_t*)&SerialNumberString;\n#    if defined(SERIAL_NUMBER)\n                    Size = pgm_read_byte(&SerialNumberString.Header.Size);\n#    else\n                    set_serial_number_descriptor();\n                    Size = ((const USB_Descriptor_String_t*)SerialNumberString)->Header.Size;\n#    endif\n\n                    break;\n#endif // HAS_SERIAL_NUMBER\n            }\n#ifdef OS_DETECTION_ENABLE\n            process_wlength(wLength);\n#endif\n\n            break;\n        case HID_DTYPE_HID:\n            switch (wIndex) {\n#ifndef KEYBOARD_SHARED_EP\n                case KEYBOARD_INTERFACE:\n                    Address = &ConfigurationDescriptor.Keyboard_HID;\n                    Size    = sizeof(USB_HID_Descriptor_HID_t);\n                    break;\n#endif\n\n#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)\n                case MOUSE_INTERFACE:\n                    Address = &ConfigurationDescriptor.Mouse_HID;\n                    Size    = sizeof(USB_HID_Descriptor_HID_t);\n\n                    break;\n#endif\n\n#ifdef SHARED_EP_ENABLE\n                case SHARED_INTERFACE:\n                    Address = &ConfigurationDescriptor.Shared_HID;\n                    Size    = sizeof(USB_HID_Descriptor_HID_t);\n\n                    break;\n#endif\n\n#ifdef RAW_ENABLE\n                case RAW_INTERFACE:\n                    Address = &ConfigurationDescriptor.Raw_HID;\n                    Size    = sizeof(USB_HID_Descriptor_HID_t);\n\n                    break;\n#endif\n\n#ifdef CONSOLE_ENABLE\n                case CONSOLE_INTERFACE:\n                    Address = &ConfigurationDescriptor.Console_HID;\n                    Size    = sizeof(USB_HID_Descriptor_HID_t);\n\n                    break;\n#endif\n#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)\n                case JOYSTICK_INTERFACE:\n                    Address = &ConfigurationDescriptor.Joystick_HID;\n                    Size    = sizeof(USB_HID_Descriptor_HID_t);\n                    break;\n#endif\n#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)\n                case DIGITIZER_INTERFACE:\n                    Address = &ConfigurationDescriptor.Digitizer_HID;\n                    Size    = sizeof(USB_HID_Descriptor_HID_t);\n\n                    break;\n#endif\n            }\n\n            break;\n        case HID_DTYPE_Report:\n            switch (wIndex) {\n#ifndef KEYBOARD_SHARED_EP\n                case KEYBOARD_INTERFACE:\n                    Address = &KeyboardReport;\n                    Size    = sizeof(KeyboardReport);\n\n                    break;\n#endif\n\n#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)\n                case MOUSE_INTERFACE:\n                    Address = &MouseReport;\n                    Size    = sizeof(MouseReport);\n\n                    break;\n#endif\n\n#ifdef SHARED_EP_ENABLE\n                case SHARED_INTERFACE:\n                    Address = &SharedReport;\n                    Size    = sizeof(SharedReport);\n\n                    break;\n#endif\n\n#ifdef RAW_ENABLE\n                case RAW_INTERFACE:\n                    Address = &RawReport;\n                    Size    = sizeof(RawReport);\n\n                    break;\n#endif\n\n#ifdef CONSOLE_ENABLE\n                case CONSOLE_INTERFACE:\n                    Address = &ConsoleReport;\n                    Size    = sizeof(ConsoleReport);\n\n                    break;\n#endif\n#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)\n                case JOYSTICK_INTERFACE:\n                    Address = &JoystickReport;\n                    Size    = sizeof(JoystickReport);\n                    break;\n#endif\n#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)\n                case DIGITIZER_INTERFACE:\n                    Address = &DigitizerReport;\n                    Size    = sizeof(DigitizerReport);\n                    break;\n#endif\n            }\n\n            break;\n    }\n\n    *DescriptorAddress = Address;\n\n    return Size;\n}\n"
  },
  {
    "path": "tmk_core/protocol/usb_descriptor.h",
    "content": "/*\n * Copyright 2012,2013 Jun Wako <wakojun@gmail.com>\n * This file is based on:\n *     LUFA-120219/Demos/Device/Lowlevel/KeyboardMouse\n *     LUFA-120219/Demos/Device/Lowlevel/GenericHID\n */\n\n/*\n                         LUFA Library\n         Copyright (C) Dean Camera, 2012.\n\n    dean [at] fourwalledcubicle [dot] com\n                     www.lufa-lib.org\n*/\n\n/*\n    Copyright 2012  Dean Camera (dean [at] fourwalledcubicle [dot] com)\n    Copyright 2010  Denver Gingerich (denver [at] ossguy [dot] com)\n\n    Permission to use, copy, modify, distribute, and sell this\n    software and its documentation for any purpose is hereby granted\n    without fee, provided that the above copyright notice appear in\n    all copies and that both that the copyright notice and this\n    permission notice and warranty disclaimer appear in supporting\n    documentation, and that the name of the author not be used in\n    advertising or publicity pertaining to distribution of the\n    software without specific, written prior permission.\n\n    The author disclaim all warranties with regard to this\n    software, including all implied warranties of merchantability\n    and fitness.  In no event shall the author be liable for any\n    special, indirect or consequential damages or any damages\n    whatsoever resulting from loss of use, data or profits, whether\n    in an action of contract, negligence or other tortious action,\n    arising out of or in connection with the use or performance of\n    this software.\n*/\n\n/** \\file\n *\n *  Header file for Descriptors.c.\n */\n\n#pragma once\n\n#include <LUFA/Drivers/USB/USB.h>\n\n#ifdef PROTOCOL_CHIBIOS\n#    include <hal.h>\n#    if STM32_USB_USE_OTG1 == TRUE\n#        define USB_ENDPOINTS_ARE_REORDERABLE\n#    endif\n#endif\n\n/*\n * USB descriptor structure\n */\ntypedef struct {\n    USB_Descriptor_Configuration_Header_t Config;\n\n#ifndef KEYBOARD_SHARED_EP\n    // Keyboard HID Interface\n    USB_Descriptor_Interface_t Keyboard_Interface;\n    USB_HID_Descriptor_HID_t   Keyboard_HID;\n    USB_Descriptor_Endpoint_t  Keyboard_INEndpoint;\n#else\n    // Shared Interface\n    USB_Descriptor_Interface_t Shared_Interface;\n    USB_HID_Descriptor_HID_t   Shared_HID;\n    USB_Descriptor_Endpoint_t  Shared_INEndpoint;\n#endif\n\n#ifdef RAW_ENABLE\n    // Raw HID Interface\n    USB_Descriptor_Interface_t Raw_Interface;\n    USB_HID_Descriptor_HID_t   Raw_HID;\n    USB_Descriptor_Endpoint_t  Raw_INEndpoint;\n    USB_Descriptor_Endpoint_t  Raw_OUTEndpoint;\n#endif\n\n#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)\n    // Mouse HID Interface\n    USB_Descriptor_Interface_t Mouse_Interface;\n    USB_HID_Descriptor_HID_t   Mouse_HID;\n    USB_Descriptor_Endpoint_t  Mouse_INEndpoint;\n#endif\n\n#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)\n    // Shared Interface\n    USB_Descriptor_Interface_t Shared_Interface;\n    USB_HID_Descriptor_HID_t   Shared_HID;\n    USB_Descriptor_Endpoint_t  Shared_INEndpoint;\n#endif\n\n#ifdef CONSOLE_ENABLE\n    // Console HID Interface\n    USB_Descriptor_Interface_t Console_Interface;\n    USB_HID_Descriptor_HID_t   Console_HID;\n    USB_Descriptor_Endpoint_t  Console_INEndpoint;\n#endif\n\n#ifdef MIDI_ENABLE\n    USB_Descriptor_Interface_Association_t Audio_Interface_Association;\n    // MIDI Audio Control Interface\n    USB_Descriptor_Interface_t          Audio_ControlInterface;\n    USB_Audio_Descriptor_Interface_AC_t Audio_ControlInterface_SPC;\n    // MIDI Audio Streaming Interface\n    USB_Descriptor_Interface_t                Audio_StreamInterface;\n    USB_MIDI_Descriptor_AudioInterface_AS_t   Audio_StreamInterface_SPC;\n    USB_MIDI_Descriptor_InputJack_t           MIDI_In_Jack_Emb;\n    USB_MIDI_Descriptor_InputJack_t           MIDI_In_Jack_Ext;\n    USB_MIDI_Descriptor_OutputJack_t          MIDI_Out_Jack_Emb;\n    USB_MIDI_Descriptor_OutputJack_t          MIDI_Out_Jack_Ext;\n    USB_Audio_Descriptor_StreamEndpoint_Std_t MIDI_In_Jack_Endpoint;\n    USB_MIDI_Descriptor_Jack_Endpoint_t       MIDI_In_Jack_Endpoint_SPC;\n    USB_Audio_Descriptor_StreamEndpoint_Std_t MIDI_Out_Jack_Endpoint;\n    USB_MIDI_Descriptor_Jack_Endpoint_t       MIDI_Out_Jack_Endpoint_SPC;\n#endif\n\n#ifdef VIRTSER_ENABLE\n    USB_Descriptor_Interface_Association_t CDC_Interface_Association;\n    // CDC Control Interface\n    USB_Descriptor_Interface_t            CDC_CCI_Interface;\n    USB_CDC_Descriptor_FunctionalHeader_t CDC_Functional_Header;\n    USB_CDC_Descriptor_FunctionalACM_t    CDC_Functional_ACM;\n    USB_CDC_Descriptor_FunctionalUnion_t  CDC_Functional_Union;\n    USB_Descriptor_Endpoint_t             CDC_NotificationEndpoint;\n    // CDC Data Interface\n    USB_Descriptor_Interface_t CDC_DCI_Interface;\n    USB_Descriptor_Endpoint_t  CDC_DataOutEndpoint;\n    USB_Descriptor_Endpoint_t  CDC_DataInEndpoint;\n#endif\n\n#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)\n    // Joystick HID Interface\n    USB_Descriptor_Interface_t Joystick_Interface;\n    USB_HID_Descriptor_HID_t   Joystick_HID;\n    USB_Descriptor_Endpoint_t  Joystick_INEndpoint;\n#endif\n\n#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)\n    // Digitizer HID Interface\n    USB_Descriptor_Interface_t Digitizer_Interface;\n    USB_HID_Descriptor_HID_t   Digitizer_HID;\n    USB_Descriptor_Endpoint_t  Digitizer_INEndpoint;\n#endif\n} USB_Descriptor_Configuration_t;\n\n/*\n * Interface indexes\n */\nenum usb_interfaces {\n#ifndef KEYBOARD_SHARED_EP\n    KEYBOARD_INTERFACE,\n#else\n    SHARED_INTERFACE,\n#    define KEYBOARD_INTERFACE SHARED_INTERFACE\n#endif\n\n// It is important that the Raw HID interface is at a constant\n// interface number, to support Linux/OSX platforms and chrome.hid\n// If Raw HID is enabled, let it be always 1.\n#ifdef RAW_ENABLE\n    RAW_INTERFACE,\n#endif\n\n#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)\n    MOUSE_INTERFACE,\n#endif\n\n#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)\n    SHARED_INTERFACE,\n#endif\n\n#ifdef CONSOLE_ENABLE\n    CONSOLE_INTERFACE,\n#endif\n\n#ifdef MIDI_ENABLE\n    AC_INTERFACE,\n    AS_INTERFACE,\n#endif\n\n#ifdef VIRTSER_ENABLE\n    CCI_INTERFACE,\n    CDI_INTERFACE,\n#endif\n\n#if defined(JOYSTICK_ENABLE) && !defined(JOYSTICK_SHARED_EP)\n    JOYSTICK_INTERFACE,\n#endif\n\n#if defined(DIGITIZER_ENABLE) && !defined(DIGITIZER_SHARED_EP)\n    DIGITIZER_INTERFACE,\n#endif\n    TOTAL_INTERFACES\n};\n\n#define IS_VALID_INTERFACE(i) ((i) >= 0 && (i) < TOTAL_INTERFACES)\n\n#define NEXT_EPNUM __COUNTER__\n\n/*\n * Endpoint numbers\n */\nenum usb_endpoints {\n    __unused_epnum__ = NEXT_EPNUM, // Endpoint numbering starts at 1\n\n#ifndef KEYBOARD_SHARED_EP\n    KEYBOARD_IN_EPNUM = NEXT_EPNUM,\n#else\n#    define KEYBOARD_IN_EPNUM SHARED_IN_EPNUM\n#endif\n\n#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP)\n    MOUSE_IN_EPNUM = NEXT_EPNUM,\n#else\n#    define MOUSE_IN_EPNUM SHARED_IN_EPNUM\n#endif\n\n#ifdef RAW_ENABLE\n    RAW_IN_EPNUM = NEXT_EPNUM,\n#    ifdef USB_ENDPOINTS_ARE_REORDERABLE\n#        define RAW_OUT_EPNUM RAW_IN_EPNUM\n#    else\n    RAW_OUT_EPNUM         = NEXT_EPNUM,\n#    endif\n#endif\n\n#ifdef SHARED_EP_ENABLE\n    SHARED_IN_EPNUM = NEXT_EPNUM,\n#endif\n\n#ifdef CONSOLE_ENABLE\n    CONSOLE_IN_EPNUM = NEXT_EPNUM,\n#endif\n\n#ifdef MIDI_ENABLE\n    MIDI_STREAM_IN_EPNUM = NEXT_EPNUM,\n#    ifdef USB_ENDPOINTS_ARE_REORDERABLE\n#        define MIDI_STREAM_OUT_EPNUM MIDI_STREAM_IN_EPNUM\n#    else\n    MIDI_STREAM_OUT_EPNUM = NEXT_EPNUM,\n#    endif\n#endif\n\n#ifdef VIRTSER_ENABLE\n    CDC_NOTIFICATION_EPNUM = NEXT_EPNUM,\n    CDC_IN_EPNUM           = NEXT_EPNUM,\n#    ifdef USB_ENDPOINTS_ARE_REORDERABLE\n#        define CDC_OUT_EPNUM CDC_IN_EPNUM\n#    else\n    CDC_OUT_EPNUM         = NEXT_EPNUM,\n#    endif\n#endif\n\n#ifdef JOYSTICK_ENABLE\n#    if !defined(JOYSTICK_SHARED_EP)\n    JOYSTICK_IN_EPNUM = NEXT_EPNUM,\n#    else\n#        define JOYSTICK_IN_EPNUM SHARED_IN_EPNUM\n#    endif\n#endif\n\n#ifdef DIGITIZER_ENABLE\n#    if !defined(DIGITIZER_SHARED_EP)\n    DIGITIZER_IN_EPNUM = NEXT_EPNUM,\n#    else\n#        define DIGITIZER_IN_EPNUM SHARED_IN_EPNUM\n#    endif\n#endif\n};\n\n#ifdef PROTOCOL_LUFA\n// LUFA tells us total endpoints including control\n#    define MAX_ENDPOINTS (ENDPOINT_TOTAL_ENDPOINTS - 1)\n#elif defined(PROTOCOL_CHIBIOS)\n// ChibiOS gives us number of available user endpoints, not control\n#    define MAX_ENDPOINTS USB_MAX_ENDPOINTS\n#endif\n\n#if (NEXT_EPNUM - 1) > MAX_ENDPOINTS\n#    error There are not enough available endpoints to support all functions. Please disable one or more of the following: Mouse Keys, Extra Keys, Console, NKRO, MIDI, Serial, Steno\n#endif\n\n#define KEYBOARD_EPSIZE 8\n#define SHARED_EPSIZE 32\n#define MOUSE_EPSIZE 16\n#define RAW_EPSIZE 32\n#define CONSOLE_EPSIZE 32\n#define MIDI_STREAM_EPSIZE 64\n#define CDC_NOTIFICATION_EPSIZE 8\n#define CDC_EPSIZE 16\n#define JOYSTICK_EPSIZE 8\n#define DIGITIZER_EPSIZE 8\n\nuint16_t get_usb_descriptor(const uint16_t wValue, const uint16_t wIndex, const uint16_t wLength, const void** const DescriptorAddress);\n"
  },
  {
    "path": "tmk_core/protocol/usb_descriptor_common.h",
    "content": "/* Copyright 2020 Nick Brassel (tzarc)\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n// Prefix string literal with L for descriptors\n#define USBCONCAT(a, b) a##b\n#define USBSTR(s) USBCONCAT(L, s)\n\n#define HID_VALUE_16(v) ((uint8_t)(v & 0xFF)), ((uint8_t)(v >> 8))\n\n/////////////////////\n// RAW Usage page and ID configuration\n\n#ifndef RAW_USAGE_PAGE\n#    define RAW_USAGE_PAGE 0xFF60\n#endif\n\n#ifndef RAW_USAGE_ID\n#    define RAW_USAGE_ID 0x61\n#endif\n\n/////////////////////\n// Hires Scroll Defaults\n\n#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE\n#    ifdef POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER\n#        if POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER > 127 || POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER < 1\n#            error \"POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER must be between 1 and 127, inclusive!\"\n#        endif\n#    else\n#        define POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER 120\n#    endif\n#    ifdef POINTING_DEVICE_HIRES_SCROLL_EXPONENT\n#        if POINTING_DEVICE_HIRES_SCROLL_EXPONENT > 127 || POINTING_DEVICE_HIRES_SCROLL_EXPONENT < 0\n#            error \"POINTING_DEVICE_HIRES_SCROLL_EXPONENT must be between 0 and 127, inclusive!\"\n#        endif\n#    else\n#        define POINTING_DEVICE_HIRES_SCROLL_EXPONENT 0\n#    endif\n#endif"
  },
  {
    "path": "tmk_core/protocol/usb_device_state.c",
    "content": "/*\n * Copyright 2021 Andrei Purdea <andrei@purdea.ro>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"usb_device_state.h\"\n#if defined(HAPTIC_ENABLE)\n#    include \"haptic.h\"\n#endif\n\n#ifdef OS_DETECTION_ENABLE\n#    include \"os_detection.h\"\n#endif\n\nstatic struct usb_device_state usb_device_state = {.idle_rate = 0, .leds = 0, .protocol = USB_PROTOCOL_REPORT, .configure_state = USB_DEVICE_STATE_NO_INIT};\n\n__attribute__((weak)) void notify_usb_device_state_change_kb(struct usb_device_state usb_device_state) {\n    notify_usb_device_state_change_user(usb_device_state);\n}\n\n__attribute__((weak)) void notify_usb_device_state_change_user(struct usb_device_state usb_device_state) {}\n\nstatic void notify_usb_device_state_change(struct usb_device_state usb_device_state) {\n#if defined(HAPTIC_ENABLE) && HAPTIC_OFF_IN_LOW_POWER\n    haptic_notify_usb_device_state_change();\n#endif\n\n    notify_usb_device_state_change_kb(usb_device_state);\n\n#ifdef OS_DETECTION_ENABLE\n    os_detection_notify_usb_device_state_change(usb_device_state);\n#endif\n}\n\nvoid usb_device_state_set_configuration(bool is_configured, uint8_t configuration_number) {\n    usb_device_state.configure_state = is_configured ? USB_DEVICE_STATE_CONFIGURED : USB_DEVICE_STATE_INIT;\n    notify_usb_device_state_change(usb_device_state);\n}\n\nvoid usb_device_state_set_suspend(bool is_configured, uint8_t configuration_number) {\n    usb_device_state.configure_state = USB_DEVICE_STATE_SUSPEND;\n    notify_usb_device_state_change(usb_device_state);\n}\n\nvoid usb_device_state_set_resume(bool is_configured, uint8_t configuration_number) {\n    usb_device_state.configure_state = is_configured ? USB_DEVICE_STATE_CONFIGURED : USB_DEVICE_STATE_INIT;\n    notify_usb_device_state_change(usb_device_state);\n}\n\nvoid usb_device_state_set_reset(void) {\n    usb_device_state.configure_state = USB_DEVICE_STATE_INIT;\n    notify_usb_device_state_change(usb_device_state);\n}\n\nvoid usb_device_state_init(void) {\n    usb_device_state.configure_state = USB_DEVICE_STATE_INIT;\n    notify_usb_device_state_change(usb_device_state);\n}\n\ninline usb_configure_state_t usb_device_state_get_configure_state(void) {\n    return usb_device_state.configure_state;\n}\n\nvoid usb_device_state_set_protocol(usb_hid_protocol_t protocol) {\n    usb_device_state.protocol = protocol == USB_PROTOCOL_BOOT ? USB_PROTOCOL_BOOT : USB_PROTOCOL_REPORT;\n    notify_usb_device_state_change(usb_device_state);\n}\n\ninline usb_hid_protocol_t usb_device_state_get_protocol() {\n    return usb_device_state.protocol;\n}\n\nvoid usb_device_state_set_leds(uint8_t leds) {\n    usb_device_state.leds = leds;\n    notify_usb_device_state_change(usb_device_state);\n}\n\ninline uint8_t usb_device_state_get_leds(void) {\n    return usb_device_state.leds;\n}\n\nvoid usb_device_state_set_idle_rate(uint8_t idle_rate) {\n    usb_device_state.idle_rate = idle_rate;\n    notify_usb_device_state_change(usb_device_state);\n}\n\ninline uint8_t usb_device_state_get_idle_rate(void) {\n    return usb_device_state.idle_rate;\n}\n"
  },
  {
    "path": "tmk_core/protocol/usb_device_state.h",
    "content": "/*\n * Copyright 2021 Andrei Purdea <andrei@purdea.ro>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 2 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <stdint.h>\n\ntypedef enum {\n    USB_DEVICE_STATE_NO_INIT    = 0, // We're in this state before calling usb_device_state_init()\n    USB_DEVICE_STATE_INIT       = 1, // Can consume up to 100mA\n    USB_DEVICE_STATE_CONFIGURED = 2, // Can consume up to what is specified in configuration descriptor, typically 500mA\n    USB_DEVICE_STATE_SUSPEND    = 3  // Can consume only suspend current\n} usb_configure_state_t;\n\ntypedef enum {\n    USB_PROTOCOL_BOOT   = 0,\n    USB_PROTOCOL_REPORT = 1,\n} usb_hid_protocol_t;\n\n// note: we can't typedef this struct to usb_device_state_t because it would\n// conflict with the previous definition in:\n// lib/chibios-contrib/ext/nxp-middleware-usb/device/usb_device.h\nstruct usb_device_state {\n    uint8_t               idle_rate;\n    uint8_t               leds;\n    usb_hid_protocol_t    protocol;\n    usb_configure_state_t configure_state;\n};\n\nvoid                  usb_device_state_set_configuration(bool is_configured, uint8_t configuration_number);\nvoid                  usb_device_state_set_suspend(bool is_configured, uint8_t configuration_number);\nvoid                  usb_device_state_set_resume(bool is_configured, uint8_t configuration_number);\nvoid                  usb_device_state_set_reset(void);\nvoid                  usb_device_state_init(void);\nusb_configure_state_t usb_device_state_get_configure_state(void);\nvoid                  usb_device_state_set_protocol(usb_hid_protocol_t protocol);\nusb_hid_protocol_t    usb_device_state_get_protocol(void);\nvoid                  usb_device_state_set_leds(uint8_t leds);\nuint8_t               usb_device_state_get_leds(void);\nvoid                  usb_device_state_set_idle_rate(uint8_t idle_rate);\nuint8_t               usb_device_state_get_idle_rate(void);\nvoid                  usb_device_state_reset_hid_state(void);\n\nvoid notify_usb_device_state_change_kb(struct usb_device_state usb_device_state);\nvoid notify_usb_device_state_change_user(struct usb_device_state usb_device_state);\n"
  },
  {
    "path": "tmk_core/protocol/usb_hid/README",
    "content": "USB HID protocol\n================\nHost side of USB HID keyboard protocol implementation.\nOnly standard HID Boot mode is supported at this time. This means most of normal keyboards are supported while proprietary >6KRO and NKRO is not.\n\nThird party Libraries\n---------------------\nUSB_Host_Shield_2.0\n    Circuits@Home repository is git-submoduled. Do git submodule init & update to get the content.\n    https://github.com/felis/USB_Host_Shield_2.0\n\narduino-1.0.1\n    Arduino files copied from:\n    https://github.com/arduino/Arduino/hardware/arduino/{cores,variants}\n\n\nTest build\n----------\nIn test directory;\n    $ make\n    $ DEV=/dev/ttyACM0 make program\n\nYou can see HID keyboard reports on debug output.\n\n\nRestriction and Bug\n-------------------\nNot supported/confirmed yet.\n    Hub, suspend, keyboard LED\n\nSwitching power on VBUS:\n    To power reset device.\n    http://www.circuitsathome.com/camera-control/simulating-cable-disconnect-on-usb-host-shield-2-0\n    This is needed for a device which are not initilized with 'USB Bus Reset'(long SE0)\n\nCan't bus-reset a keyboard which already attached on bus properly.\n    Slow start up of Leonardo's bootloader causes this?\n    Need to unplug/plug a keyboard after firmware starts up.\n    MAX3421E doesn't work SAMPLEBUS well to know whether device connected or not.\n\nKeyboard with other endpoints than boot keyboard may go wrong.\n    On my keyboard with mouse key the converter locks up when using mouse key function.\n\nCan't compile on Windows filesystem.\n    On Linux no problem.\n    Windows doesn't know difference between common/print.h and arduino/Print.h.\n    Change file name common/print.h to console.h ?\n"
  },
  {
    "path": "tmk_core/protocol/usb_hid/override_Serial.cpp",
    "content": "/*\n * Null implementation of Serial to dump debug print into blackhole\n */\n#include \"Arduino.h\"\n#include \"sendchar.h\"\n\n#include \"USBAPI.h\"\n\n\nvoid Serial_::begin(uint16_t baud_count)\n{\n}\n\nvoid Serial_::end(void)\n{\n}\n\nvoid Serial_::accept(void)\n{\n}\n\nint Serial_::available(void)\n{\n    return 0;\n}\n\nint Serial_::peek(void)\n{\n    return -1;\n}\n\nint Serial_::read(void)\n{\n    return -1;\n}\n\nvoid Serial_::flush(void)\n{\n}\n\nsize_t Serial_::write(uint8_t c)\n{\n    sendchar(c);\n    return 1;\n}\n\nSerial_::operator bool() {\n    return true;\n}\n\nSerial_ Serial;\n"
  },
  {
    "path": "tmk_core/protocol/usb_hid/override_wiring.c",
    "content": "/*\n * To keep Timer0 for common/timer.c override arduino/wiring.c.\n */\n#define __DELAY_BACKWARD_COMPATIBLE__\n#include \"wait.h\"\n#include \"platforms/timer.h\"\n\n\nunsigned long millis(void)\n{\n    return timer_read32();\n}\nunsigned long micros(void)\n{\n    return timer_read32() * 1000UL;\n}\nvoid delay(unsigned long ms)\n{\n    wait_ms(ms);\n}\nvoid delayMicroseconds(unsigned int us)\n{\n    wait_us(us);\n}\nvoid init(void)\n{\n    timer_init();\n}\n"
  },
  {
    "path": "tmk_core/protocol/usb_hid/parser.cpp",
    "content": "#include \"parser.h\"\n#include \"usb_hid.h\"\n\n#include \"debug.h\"\n\n\nvoid KBDReportParser::Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf)\n{\n    ::memcpy(&report, buf, sizeof(report_keyboard_t));\n    time_stamp = millis();\n\n    dprintf(\"input %d:  %02X %02X\", hid->GetAddress(), report.mods, report.reserved);\n    for (uint8_t i = 0; i < KEYBOARD_REPORT_KEYS; i++) {\n        dprintf(\" %02X\", report.keys[i]);\n    }\n    dprint(\"\\r\\n\");\n}\n"
  },
  {
    "path": "tmk_core/protocol/usb_hid/parser.h",
    "content": "#pragma once\n\n#include \"hid.h\"\n#include \"report.h\"\n\nclass KBDReportParser : public HIDReportParser\n{\npublic:\n    report_keyboard_t report;\n    uint16_t time_stamp;\n    virtual void Parse(HID *hid, bool is_rpt_id, uint8_t len, uint8_t *buf);\n};\n"
  },
  {
    "path": "tmk_core/protocol/usb_hid/usb_hid.h",
    "content": "#pragma once\n\n#include \"report.h\"\n\nextern report_keyboard_t usb_hid_keyboard_report;\nextern uint16_t usb_hid_time_stamp;\n"
  },
  {
    "path": "tmk_core/protocol/usb_hid/usb_hid.mk",
    "content": "USB_HID_DIR = protocol/usb_hid\nUSB_HOST_LIB_DIR = $(LIB_PATH)/usbhost\n\n#\n# USB Host Shield\n#\nUSB_HOST_SHIELD_DIR = $(USB_HOST_LIB_DIR)/USB_Host_Shield_2.0\nUSB_HOST_SHIELD_SRC = \\\n\t$(USB_HOST_SHIELD_DIR)/Usb.cpp \\\n\t$(USB_HOST_SHIELD_DIR)/hid.cpp \\\n\t$(USB_HOST_SHIELD_DIR)/usbhub.cpp \\\n\t$(USB_HOST_SHIELD_DIR)/parsetools.cpp \\\n\t$(USB_HOST_SHIELD_DIR)/message.cpp \n\n\n\n#\n# Arduino\n#\nARDUINO_DIR = $(USB_HOST_LIB_DIR)/arduino-1.0.1\nARDUINO_CORES_DIR = $(ARDUINO_DIR)/cores/arduino\nARDUINO_CORES_SRC = \\\n\t$(ARDUINO_CORES_DIR)/Print.cpp \\\n\t$(ARDUINO_CORES_DIR)/Stream.cpp\n\n# replaced with override_Serial.c\n#\t$(ARDUINO_CORES_DIR)/CDC.cpp \\\n#\t$(ARDUINO_CORES_DIR)/HID.cpp \\\n#\t$(ARDUINO_CORES_DIR)/USBCore.cpp \\\n\n# replaced with override_wiring.c and common/timer.c\n#\t$(ARDUINO_CORES_DIR)/wiring.c \\\n\n\n\n#\n# HID parser\n#\nSRC += $(USB_HID_DIR)/parser.cpp\n\n# replace arduino/CDC.cpp\nSRC += $(USB_HID_DIR)/override_Serial.cpp\n\n# replace arduino/wiring.c\nSRC += $(USB_HID_DIR)/override_wiring.c\n\nSRC += $(USB_HOST_SHIELD_SRC)\nSRC += $(ARDUINO_CORES_SRC)\n\n\nOPT_DEFS += -DARDUINO=101\n# Arduino USBCore needs USB_VID and USB_PID.\n#OPT_DEFS += -DARDUINO=101 -DUSB_VID=0x2341 -DUSB_PID=0x8036\n\n\n\n#\n# Search Path\n#\nVPATH += $(TMK_DIR)/$(USB_HID_DIR)\nVPATH += $(USB_HOST_SHIELD_DIR)\n\n# for #include \"Arduino.h\"\nVPATH += $(ARDUINO_CORES_DIR)\n\n# for #include \"pins_arduino.h\"\nVPATH += $(ARDUINO_DIR)/variants/leonardo\n\n# ad hoc workaround for compile problem on Windows:\n#    Windows doesn't know difference between common/print.h and arduino/Print.h.\n#    On Linux no problem.\n#    Change file name common/print.h to console.h ?\nVPATH := $(TMK_DIR)/common $(VPATH)\n"
  },
  {
    "path": "tmk_core/protocol/usb_types.h",
    "content": "// Copyright 2023 Stefan Kerkmann\n// SPDX-License-Identifier: GPL-2.0-or-later\n\n#pragma once\n\n#include \"util.h\"\n\n/**\n * @brief Common USB 2.0 control request structure\n */\ntypedef struct {\n    uint8_t bmRequestType; // [0] (Bitmask)\n    uint8_t bRequest;      // [1]\n    union {\n        struct {\n            uint8_t lbyte; // [2] (LSB)\n            uint8_t hbyte; // [3] (MSB)\n        };\n        uint16_t word; // [2,3] (LSB,MSB)\n    } wValue;\n    uint16_t wIndex;  // [4,5] (LSB,MSB)\n    uint16_t wLength; // [6,7] (LSB,MSB)\n} PACKED usb_control_request_t;\n"
  },
  {
    "path": "tmk_core/protocol/usb_util.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#include \"usb_util.h\"\n#include \"gpio.h\"\n#include \"wait.h\"\n\n__attribute__((weak)) void usb_disconnect(void) {}\n\n__attribute__((weak)) bool usb_connected_state(void) {\n    return true;\n}\n\n__attribute__((weak)) bool usb_vbus_state(void) {\n#ifdef USB_VBUS_PIN\n    gpio_set_pin_input(USB_VBUS_PIN);\n    wait_us(5);\n    return gpio_read_pin(USB_VBUS_PIN);\n#else\n    return true;\n#endif\n}\n"
  },
  {
    "path": "tmk_core/protocol/usb_util.h",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n\n#pragma once\n\n#include <stdbool.h>\n\nvoid usb_disconnect(void);\n\nbool usb_connected_state(void);\n\nbool usb_vbus_state(void);\n"
  },
  {
    "path": "tmk_core/protocol/vusb/protocol.c",
    "content": "/* Name: main.c\n * Project: hid-mouse, a very simple HID example\n * Author: Christian Starkjohann\n * Creation Date: 2008-04-07\n * Tabsize: 4\n * Copyright: (c) 2008 by OBJECTIVE DEVELOPMENT Software GmbH\n * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)\n * This Revision: $Id: main.c 790 2010-05-30 21:00:26Z cs $\n */\n\n#include <stdint.h>\n\n#include <avr/interrupt.h>\n#include <avr/power.h>\n#include <avr/wdt.h>\n#include <avr/sleep.h>\n\n#include <usbdrv/usbdrv.h>\n\n#include \"vusb.h\"\n\n#include \"keyboard.h\"\n#include \"host.h\"\n#include \"timer.h\"\n#include \"debug.h\"\n#include \"suspend.h\"\n#include \"wait.h\"\n#include \"sendchar.h\"\n\n#ifdef SLEEP_LED_ENABLE\n#    include \"sleep_led.h\"\n#endif\n\n/* This is from main.c of USBaspLoader */\nstatic void initForUsbConnectivity(void) {\n    uint8_t i = 0;\n\n    usbInit();\n    /* enforce USB re-enumerate: */\n    usbDeviceDisconnect(); /* do this while interrupts are disabled */\n    while (--i) {          /* fake USB disconnect for > 250 ms */\n        wdt_reset();\n        wait_ms(1);\n    }\n    usbDeviceConnect();\n}\n\nstatic inline void vusb_send_remote_wakeup(void) {\n    cli();\n\n    uint8_t ddr_orig = USBDDR;\n    USBOUT |= (1 << USBMINUS);\n    USBDDR = ddr_orig | USBMASK;\n    USBOUT ^= USBMASK;\n\n    wait_ms(25);\n\n    USBOUT ^= USBMASK;\n    USBDDR = ddr_orig;\n    USBOUT &= ~(1 << USBMINUS);\n\n    sei();\n}\n\nbool vusb_suspended = false;\n\nstatic inline void vusb_suspend(void) {\n#ifdef SLEEP_LED_ENABLE\n    sleep_led_enable();\n#endif\n\n    suspend_power_down();\n}\n\nstatic inline void vusb_wakeup(void) {\n    suspend_wakeup_init();\n\n#ifdef SLEEP_LED_ENABLE\n    sleep_led_disable();\n#endif\n}\n\n/** \\brief Setup USB\n *\n * FIXME: Needs doc\n */\nstatic void setup_usb(void) {\n    initForUsbConnectivity();\n}\n\nuint16_t sof_timer = 0;\n\nvoid protocol_setup(void) {\n#if USB_COUNT_SOF\n    sof_timer = timer_read();\n#endif\n\n#ifdef CLKPR\n    // avoid unintentional changes of clock frequency in devices that have a\n    // clock prescaler\n    clock_prescale_set(clock_div_1);\n#endif\n}\n\nvoid protocol_pre_init(void) {\n    setup_usb();\n    sei();\n}\n\nvoid protocol_post_init(void) {\n    host_set_driver(vusb_driver());\n    wait_ms(50);\n}\n\nstatic inline bool should_do_suspend(void) {\n#if USB_COUNT_SOF\n    if (usbSofCount != 0) {\n        usbSofCount    = 0;\n        sof_timer      = timer_read();\n        vusb_suspended = false;\n    } else {\n        // Suspend when no SOF in 3ms-10ms(7.1.7.4 Suspending of USB1.1)\n        if (!vusb_suspended && timer_elapsed(sof_timer) > 5) {\n            vusb_suspended = true;\n        }\n    }\n#endif\n    return vusb_suspended;\n}\n\nvoid protocol_pre_task(void) {\n#if !defined(NO_USB_STARTUP_CHECK)\n    if (should_do_suspend()) {\n        dprintln(\"suspending keyboard\");\n        while (should_do_suspend()) {\n            vusb_suspend();\n            if (suspend_wakeup_condition()) {\n                vusb_send_remote_wakeup();\n\n#    if USB_SUSPEND_WAKEUP_DELAY > 0\n                // Some hubs, kvm switches, and monitors do\n                // weird things, with USB device state bouncing\n                // around wildly on wakeup, yielding race\n                // conditions that can corrupt the keyboard state.\n                //\n                // Pause for a while to let things settle...\n                wait_ms(USB_SUSPEND_WAKEUP_DELAY);\n#    endif\n            }\n        }\n        vusb_wakeup();\n    }\n#endif\n}\n\nvoid protocol_keyboard_task(void) {\n    usbPoll();\n\n    // TODO: configuration process is inconsistent. it sometime fails.\n    // To prevent failing to configure NOT scan keyboard during configuration\n    if (usbConfiguration && usbInterruptIsReady()) {\n        keyboard_task();\n    }\n}\n\nvoid protocol_post_task(void) {\n    // do nothing\n}\n"
  },
  {
    "path": "tmk_core/protocol/vusb/usb_util.c",
    "content": "/* Copyright 2021 QMK\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n */\n#include <usbdrv/usbdrv.h>\n#include \"usb_util.h\"\n\nvoid usb_disconnect(void) {\n    usbDeviceDisconnect();\n}\n\nbool usb_connected_state(void) {\n    usbPoll();\n    return usbConfiguration;\n}\n"
  },
  {
    "path": "tmk_core/protocol/vusb/usbconfig.h",
    "content": "/* Name: usbconfig.h\n * Project: V-USB, virtual USB port for Atmel's(r) AVR(r) microcontrollers\n * Author: Christian Starkjohann\n * Creation Date: 2005-04-01\n * Tabsize: 4\n * Copyright: (c) 2005 by OBJECTIVE DEVELOPMENT Software GmbH\n * License: GNU GPL v2 (see License.txt), GNU GPL v3 or proprietary (CommercialLicense.txt)\n * This Revision: $Id: usbconfig-prototype.h 785 2010-05-30 17:57:07Z cs $\n */\n\n#pragma once\n\n// clang-format off\n\n/*\nGeneral Description:\nThis file is an example configuration (with inline documentation) for the USB\ndriver. It configures V-USB for USB D+ connected to Port D bit 2 (which is\nalso hardware interrupt 0 on many devices) and USB D- to Port D bit 4. You may\nwire the lines to any other port, as long as D+ is also wired to INT0 (or any\nother hardware interrupt, as long as it is the highest level interrupt, see\nsection at the end of this file).\n*/\n\n/* ---------------------------- Hardware Config ---------------------------- */\n\n#ifndef USB_CFG_IOPORTNAME\n#define USB_CFG_IOPORTNAME      D\n#endif\n/* This is the port where the USB bus is connected. When you configure it to\n * \"B\", the registers PORTB, PINB and DDRB will be used.\n */\n#ifndef USB_CFG_DMINUS_BIT\n#define USB_CFG_DMINUS_BIT      3\n#endif\n/* This is the bit number in USB_CFG_IOPORT where the USB D- line is connected.\n * This may be any bit in the port.\n */\n#ifndef USB_CFG_DPLUS_BIT\n#define USB_CFG_DPLUS_BIT       2\n#endif\n/* This is the bit number in USB_CFG_IOPORT where the USB D+ line is connected.\n * This may be any bit in the port. Please note that D+ must also be connected\n * to interrupt pin INT0! [You can also use other interrupts, see section\n * \"Optional MCU Description\" below, or you can connect D- to the interrupt, as\n * it is required if you use the USB_COUNT_SOF feature. If you use D- for the\n * interrupt, the USB interrupt will also be triggered at Start-Of-Frame\n * markers every millisecond.]\n */\n#define USB_CFG_CHECK_CRC       0\n/* Define this to 1 if you want that the driver checks integrity of incoming\n * data packets (CRC checks). CRC checks cost quite a bit of code size and are\n * currently only available for 18 MHz crystal clock. You must choose\n * USB_CFG_CLOCK_KHZ = 18000 if you enable this option.\n */\n\n/* ----------------------- Optional Hardware Config ------------------------ */\n\n/* #define USB_CFG_PULLUP_IOPORTNAME   D */\n/* If you connect the 1.5k pullup resistor from D- to a port pin instead of\n * V+, you can connect and disconnect the device from firmware by calling\n * the macros usbDeviceConnect() and usbDeviceDisconnect() (see usbdrv.h).\n * This constant defines the port on which the pullup resistor is connected.\n */\n/* #define USB_CFG_PULLUP_BIT          4 */\n/* This constant defines the bit number in USB_CFG_PULLUP_IOPORT (defined\n * above) where the 1.5k pullup resistor is connected. See description\n * above for details.\n */\n\n/* --------------------------- Functional Range ---------------------------- */\n\n#define USB_CFG_HAVE_INTRIN_ENDPOINT    1\n/* Define this to 1 if you want to compile a version with two endpoints: The\n * default control endpoint 0 and an interrupt-in endpoint (any other endpoint\n * number).\n */\n#define USB_CFG_HAVE_INTRIN_ENDPOINT3   1\n/* Define this to 1 if you want to compile a version with three endpoints: The\n * default control endpoint 0, an interrupt-in endpoint 3 (or the number\n * configured below) and a catch-all default interrupt-in endpoint as above.\n * You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature.\n */\n#define USB_CFG_EP3_NUMBER              3\n/* If the so-called endpoint 3 is used, it can now be configured to any other\n * endpoint number (except 0) with this macro. Default if undefined is 3.\n */\n#define USB_CFG_HAVE_INTRIN_ENDPOINT4   1\n/* Define this to 1 if you want to compile a version with three endpoints: The\n * default control endpoint 0, an interrupt-in endpoint 4 (or the number\n * configured below) and a catch-all default interrupt-in endpoint as above.\n * You must also define USB_CFG_HAVE_INTRIN_ENDPOINT to 1 for this feature.\n */\n#define USB_CFG_EP4_NUMBER              4\n/* If the so-called endpoint 4 is used, it can now be configured to any other\n * endpoint number (except 0) with this macro. Default if undefined is 4.\n */\n/* #define USB_INITIAL_DATATOKEN           USBPID_DATA1 */\n/* The above macro defines the startup condition for data toggling on the\n * interrupt/bulk endpoints 1, 3 and 4. Defaults to USBPID_DATA1.\n * Since the token is toggled BEFORE sending any data, the first packet is\n * sent with the oposite value of this configuration!\n */\n#define USB_CFG_IMPLEMENT_HALT          0\n/* Define this to 1 if you also want to implement the ENDPOINT_HALT feature\n * for endpoint 1 (interrupt endpoint). Although you may not need this feature,\n * it is required by the standard. We have made it a config option because it\n * bloats the code considerably.\n */\n#define USB_CFG_SUPPRESS_INTR_CODE      0\n/* Define this to 1 if you want to declare interrupt-in endpoints, but don't\n * want to send any data over them. If this macro is defined to 1, functions\n * usbSetInterrupt(), usbSetInterrupt3() and usbSetInterrupt4() are omitted.\n * This is useful if you need the interrupt-in endpoints in order to comply\n * to an interface (e.g. HID), but never want to send any data. This option\n * saves a couple of bytes in flash memory and the transmit buffers in RAM.\n */\n#define USB_CFG_IS_SELF_POWERED         0\n/* Define this to 1 if the device has its own power supply. Set it to 0 if the\n * device is powered from the USB bus.\n */\n#define USB_CFG_IMPLEMENT_FN_WRITE      1\n/* Set this to 1 if you want usbFunctionWrite() to be called for control-out\n * transfers. Set it to 0 if you don't need it and want to save a couple of\n * bytes.\n */\n#define USB_CFG_IMPLEMENT_FN_READ       0\n/* Set this to 1 if you need to send control replies which are generated\n * \"on the fly\" when usbFunctionRead() is called. If you only want to send\n * data from a static buffer, set it to 0 and return the data from\n * usbFunctionSetup(). This saves a couple of bytes.\n */\n#define USB_CFG_IMPLEMENT_FN_WRITEOUT   1\n/* Define this to 1 if you want to use interrupt-out (or bulk out) endpoints.\n * You must implement the function usbFunctionWriteOut() which receives all\n * interrupt/bulk data sent to any endpoint other than 0. The endpoint number\n * can be found in 'usbRxToken'.\n */\n#define USB_CFG_HAVE_FLOWCONTROL        0\n/* Define this to 1 if you want flowcontrol over USB data. See the definition\n * of the macros usbDisableAllRequests() and usbEnableAllRequests() in\n * usbdrv.h.\n */\n#define USB_CFG_DRIVER_FLASH_PAGE       0\n/* If the device has more than 64 kBytes of flash, define this to the 64 k page\n * where the driver's constants (descriptors) are located. Or in other words:\n * Define this to 1 for boot loaders on the ATMega128.\n */\n#define USB_CFG_LONG_TRANSFERS          0\n/* Define this to 1 if you want to send/receive blocks of more than 254 bytes\n * in a single control-in or control-out transfer. Note that the capability\n * for long transfers increases the driver size.\n */\n/* #define USB_RX_USER_HOOK(data, len)     if(usbRxToken == (uchar)USBPID_SETUP) blinkLED(); */\n/* This macro is a hook if you want to do unconventional things. If it is\n * defined, it's inserted at the beginning of received message processing.\n * If you eat the received message and don't want default processing to\n * proceed, do a return after doing your things. One possible application\n * (besides debugging) is to flash a status LED on each packet.\n */\n/* #define USB_RESET_HOOK(resetStarts)     if(!resetStarts){hadUsbReset();} */\n/* This macro is a hook if you need to know when an USB RESET occurs. It has\n * one parameter which distinguishes between the start of RESET state and its\n * end.\n */\n/* #define USB_SET_ADDRESS_HOOK()              hadAddressAssigned(); */\n/* This macro (if defined) is executed when a USB SET_ADDRESS request was\n * received.\n */\n#ifndef USB_COUNT_SOF\n#define USB_COUNT_SOF                   1\n#endif\n/* define this macro to 1 if you need the global variable \"usbSofCount\" which\n * counts SOF packets. This feature requires that the hardware interrupt is\n * connected to D- instead of D+.\n */\n/* #ifdef __ASSEMBLER__\n * macro myAssemblerMacro\n *     in      YL, TCNT0\n *     sts     timer0Snapshot, YL\n *     endm\n * #endif\n * #define USB_SOF_HOOK                    myAssemblerMacro\n * This macro (if defined) is executed in the assembler module when a\n * Start Of Frame condition is detected. It is recommended to define it to\n * the name of an assembler macro which is defined here as well so that more\n * than one assembler instruction can be used. The macro may use the register\n * YL and modify SREG. If it lasts longer than a couple of cycles, USB messages\n * immediately after an SOF pulse may be lost and must be retried by the host.\n * What can you do with this hook? Since the SOF signal occurs exactly every\n * 1 ms (unless the host is in sleep mode), you can use it to tune OSCCAL in\n * designs running on the internal RC oscillator.\n * Please note that Start Of Frame detection works only if D- is wired to the\n * interrupt, not D+. THIS IS DIFFERENT THAN MOST EXAMPLES!\n */\n#define USB_CFG_CHECK_DATA_TOGGLING     0\n/* define this macro to 1 if you want to filter out duplicate data packets\n * sent by the host. Duplicates occur only as a consequence of communication\n * errors, when the host does not receive an ACK. Please note that you need to\n * implement the filtering yourself in usbFunctionWriteOut() and\n * usbFunctionWrite(). Use the global usbCurrentDataToken and a static variable\n * for each control- and out-endpoint to check for duplicate packets.\n */\n#define USB_CFG_HAVE_MEASURE_FRAME_LENGTH   0\n/* define this macro to 1 if you want the function usbMeasureFrameLength()\n * compiled in. This function can be used to calibrate the AVR's RC oscillator.\n */\n#define USB_USE_FAST_CRC                0\n/* The assembler module has two implementations for the CRC algorithm. One is\n * faster, the other is smaller. This CRC routine is only used for transmitted\n * messages where timing is not critical. The faster routine needs 31 cycles\n * per byte while the smaller one needs 61 to 69 cycles. The faster routine\n * may be worth the 32 bytes bigger code size if you transmit lots of data and\n * run the AVR close to its limit.\n */\n\n/* -------------------------- Device Description --------------------------- */\n\n#define USB_CFG_VENDOR_ID\n/* USB vendor ID for the device, low byte first. If you have registered your\n * own Vendor ID, define it here. Otherwise you may use one of obdev's free\n * shared VID/PID pairs. Be sure to read USB-IDs-for-free.txt for rules!\n * *** IMPORTANT NOTE ***\n * This template uses obdev's shared VID/PID pair for Vendor Class devices\n * with libusb: 0x16c0/0x5dc.  Use this VID/PID pair ONLY if you understand\n * the implications!\n */\n#define USB_CFG_DEVICE_ID\n/* This is the ID of the product, low byte first. It is interpreted in the\n * scope of the vendor ID. If you have registered your own VID with usb.org\n * or if you have licensed a PID from somebody else, define it here. Otherwise\n * you may use one of obdev's free shared VID/PID pairs. See the file\n * USB-IDs-for-free.txt for details!\n * *** IMPORTANT NOTE ***\n * This template uses obdev's shared VID/PID pair for Vendor Class devices\n * with libusb: 0x16c0/0x5dc.  Use this VID/PID pair ONLY if you understand\n * the implications!\n */\n#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH    0\n/* Define this to the length of the HID report descriptor, if you implement\n * an HID device. Otherwise don't define it or define it to 0.\n * If you use this define, you must add a PROGMEM character array named\n * \"usbHidReportDescriptor\" to your code which contains the report descriptor.\n * Don't forget to keep the array and this define in sync!\n */\n\n/* #define USB_PUBLIC static */\n/* Use the define above if you #include usbdrv.c instead of linking against it.\n * This technique saves a couple of bytes in flash memory.\n */\n\n/* ------------------- Fine Control over USB Descriptors ------------------- */\n/* If you don't want to use the driver's default USB descriptors, you can\n * provide our own. These can be provided as (1) fixed length static data in\n * flash memory, (2) fixed length static data in RAM or (3) dynamically at\n * runtime in the function usbFunctionDescriptor(). See usbdrv.h for more\n * information about this function.\n * Descriptor handling is configured through the descriptor's properties. If\n * no properties are defined or if they are 0, the default descriptor is used.\n * Possible properties are:\n *   + USB_PROP_IS_DYNAMIC: The data for the descriptor should be fetched\n *     at runtime via usbFunctionDescriptor(). If the usbMsgPtr mechanism is\n *     used, the data is in FLASH by default. Add property USB_PROP_IS_RAM if\n *     you want RAM pointers.\n *   + USB_PROP_IS_RAM: The data returned by usbFunctionDescriptor() or found\n *     in static memory is in RAM, not in flash memory.\n *   + USB_PROP_LENGTH(len): If the data is in static memory (RAM or flash),\n *     the driver must know the descriptor's length. The descriptor itself is\n *     found at the address of a well known identifier (see below).\n * List of static descriptor names (must be declared PROGMEM if in flash):\n *   char usbDescriptorDevice[];\n *   char usbDescriptorConfiguration[];\n *   char usbDescriptorHidReport[];\n *   char usbDescriptorString0[];\n *   int usbDescriptorStringVendor[];\n *   int usbDescriptorStringDevice[];\n *   int usbDescriptorStringSerialNumber[];\n * Other descriptors can't be provided statically, they must be provided\n * dynamically at runtime.\n *\n * Descriptor properties are or-ed or added together, e.g.:\n * #define USB_CFG_DESCR_PROPS_DEVICE   (USB_PROP_IS_RAM | USB_PROP_LENGTH(18))\n *\n * The following descriptors are defined:\n *   USB_CFG_DESCR_PROPS_DEVICE\n *   USB_CFG_DESCR_PROPS_CONFIGURATION\n *   USB_CFG_DESCR_PROPS_STRINGS\n *   USB_CFG_DESCR_PROPS_STRING_0\n *   USB_CFG_DESCR_PROPS_STRING_VENDOR\n *   USB_CFG_DESCR_PROPS_STRING_PRODUCT\n *   USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER\n *   USB_CFG_DESCR_PROPS_HID\n *   USB_CFG_DESCR_PROPS_HID_REPORT\n *   USB_CFG_DESCR_PROPS_UNKNOWN (for all descriptors not handled by the driver)\n *\n * Note about string descriptors: String descriptors are not just strings, they\n * are Unicode strings prefixed with a 2 byte header. Example:\n * int  serialNumberDescriptor[] = {\n *     USB_STRING_DESCRIPTOR_HEADER(6),\n *     'S', 'e', 'r', 'i', 'a', 'l'\n * };\n */\n\n#define USB_CFG_DESCR_PROPS_DEVICE                  USB_PROP_IS_DYNAMIC\n#define USB_CFG_DESCR_PROPS_CONFIGURATION           USB_PROP_IS_DYNAMIC\n#define USB_CFG_DESCR_PROPS_STRINGS                 USB_PROP_IS_DYNAMIC\n#define USB_CFG_DESCR_PROPS_STRING_0                USB_PROP_IS_DYNAMIC\n#define USB_CFG_DESCR_PROPS_STRING_VENDOR           USB_PROP_IS_DYNAMIC\n#define USB_CFG_DESCR_PROPS_STRING_PRODUCT          USB_PROP_IS_DYNAMIC\n#define USB_CFG_DESCR_PROPS_STRING_SERIAL_NUMBER    USB_PROP_IS_DYNAMIC\n#define USB_CFG_DESCR_PROPS_HID                     USB_PROP_IS_DYNAMIC\n#define USB_CFG_DESCR_PROPS_HID_REPORT              USB_PROP_IS_DYNAMIC\n#define USB_CFG_DESCR_PROPS_UNKNOWN                 0\n\n#define usbMsgPtr_t unsigned short\n/* If usbMsgPtr_t is not defined, it defaults to 'uchar *'. We define it to\n * a scalar type here because gcc generates slightly shorter code for scalar\n * arithmetics than for pointer arithmetics. Remove this define for backward\n * type compatibility or define it to an 8 bit type if you use data in RAM only\n * and all RAM is below 256 bytes (tiny memory model in IAR CC).\n */\n\n/* ----------------------- Optional MCU Description ------------------------ */\n\n/* The following configurations have working defaults in usbdrv.h. You\n * usually don't need to set them explicitly. Only if you want to run\n * the driver on a device which is not yet supported or with a compiler\n * which is not fully supported (such as IAR C) or if you use a differnt\n * interrupt than INT0, you may have to define some of these.\n */\n/* #define USB_INTR_CFG            MCUCR */\n/* #define USB_INTR_CFG_SET        ((1 << ISC00) | (1 << ISC01)) */\n/* #define USB_INTR_CFG_CLR        0 */\n/* #define USB_INTR_ENABLE         GIMSK */\n/* #define USB_INTR_ENABLE_BIT     INT0 */\n/* #define USB_INTR_PENDING        GIFR */\n/* #define USB_INTR_PENDING_BIT    INTF0 */\n/* #define USB_INTR_VECTOR         INT0_vect */\n\n/* Set INT1 for D- falling edge to count SOF */\n/* #define USB_INTR_CFG            EICRA */\n#ifndef USB_INTR_CFG_SET\n#define USB_INTR_CFG_SET        ((1 << ISC11) | (0 << ISC10))\n#endif\n/* #define USB_INTR_CFG_CLR        0 */\n/* #define USB_INTR_ENABLE         EIMSK */\n#ifndef USB_INTR_ENABLE_BIT\n#define USB_INTR_ENABLE_BIT     INT1\n#endif\n/* #define USB_INTR_PENDING        EIFR */\n#ifndef USB_INTR_PENDING_BIT\n#define USB_INTR_PENDING_BIT    INTF1\n#endif\n#ifndef USB_INTR_VECTOR\n#define USB_INTR_VECTOR         INT1_vect\n#endif\n"
  },
  {
    "path": "tmk_core/protocol/vusb/vusb.c",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#include <stdint.h>\n\n#include <avr/wdt.h>\n\n#include <usbdrv/usbdrv.h>\n\n#include \"compiler_support.h\"\n#include \"usbconfig.h\"\n#include \"host.h\"\n#include \"report.h\"\n#include \"host_driver.h\"\n#include \"vusb.h\"\n#include \"print.h\"\n#include \"debug.h\"\n#include \"wait.h\"\n#include \"usb_descriptor_common.h\"\n#include \"usb_device_state.h\"\n\n#ifdef RAW_ENABLE\n#    include \"raw_hid.h\"\n#endif\n\n#ifdef JOYSTICK_ENABLE\n#    include \"joystick.h\"\n#endif\n\n#if defined(CONSOLE_ENABLE)\n#    define RBUF_SIZE 128\n#    include \"ring_buffer.h\"\n#endif\n\n#ifdef OS_DETECTION_ENABLE\n#    include \"os_detection.h\"\n#endif\n\n/*\n * Interface indexes\n */\nenum usb_interfaces {\n#ifndef KEYBOARD_SHARED_EP\n    KEYBOARD_INTERFACE,\n#else\n    SHARED_INTERFACE,\n#    define KEYBOARD_INTERFACE SHARED_INTERFACE\n#endif\n\n// It is important that the Raw HID interface is at a constant\n// interface number, to support Linux/OSX platforms and chrome.hid\n// If Raw HID is enabled, let it be always 1.\n#ifdef RAW_ENABLE\n    RAW_INTERFACE,\n#endif\n\n#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)\n    SHARED_INTERFACE,\n#endif\n\n#ifdef CONSOLE_ENABLE\n    CONSOLE_INTERFACE,\n#endif\n\n    TOTAL_INTERFACES\n};\n\n#define MAX_INTERFACES 3\n\nSTATIC_ASSERT(TOTAL_INTERFACES <= MAX_INTERFACES, \"There are not enough available interfaces to support all functions. Please disable one or more of the following: Mouse Keys, Extra Keys, Raw HID, Console.\");\n\n#if (defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE)) && CONSOLE_ENABLE\n#    error Mouse/Extra Keys share an endpoint with Console. Please disable one of the two.\n#endif\n\nstatic report_keyboard_t keyboard_report_sent;\n\nstatic void send_report_fragment(uint8_t endpoint, void *data, size_t size) {\n    for (uint8_t retries = 5; retries > 0; retries--) {\n        switch (endpoint) {\n            case 1:\n                if (usbInterruptIsReady()) {\n                    usbSetInterrupt(data, size);\n                    return;\n                }\n                break;\n            case USB_CFG_EP3_NUMBER:\n                if (usbInterruptIsReady3()) {\n                    usbSetInterrupt3(data, size);\n                    return;\n                }\n                break;\n            case USB_CFG_EP4_NUMBER:\n                if (usbInterruptIsReady4()) {\n                    usbSetInterrupt4(data, size);\n                    return;\n                }\n                break;\n            default:\n                return;\n        }\n\n        usbPoll();\n        wait_ms(5);\n    }\n}\n\nstatic void send_report(uint8_t endpoint, void *report, size_t size) {\n    uint8_t *temp = (uint8_t *)report;\n\n    // Send as many full packets as possible\n    for (uint8_t i = 0; i < size / 8; i++) {\n        send_report_fragment(endpoint, temp, 8);\n        temp += 8;\n    }\n\n    // Send any data left over\n    uint8_t remainder = size % 8;\n    if (remainder) {\n        send_report_fragment(endpoint, temp, remainder);\n    }\n}\n\n/*------------------------------------------------------------------*\n * RAW HID\n *------------------------------------------------------------------*/\n#ifdef RAW_ENABLE\n#    define RAW_BUFFER_SIZE 32\n#    define RAW_EPSIZE 8\n\nstatic uint8_t raw_output_buffer[RAW_BUFFER_SIZE];\nstatic uint8_t raw_output_received_bytes = 0;\n\nstatic void send_raw_hid(uint8_t *data, uint8_t length) {\n    if (length != RAW_BUFFER_SIZE) {\n        return;\n    }\n\n    send_report(4, data, 32);\n}\n\nvoid raw_hid_task(void) {\n    usbPoll();\n\n    if (!usbConfiguration || !usbInterruptIsReady4()) {\n        return;\n    }\n\n    if (raw_output_received_bytes == RAW_BUFFER_SIZE) {\n        raw_hid_receive(raw_output_buffer, RAW_BUFFER_SIZE);\n        raw_output_received_bytes = 0;\n    }\n}\n#endif\n\n/*------------------------------------------------------------------*\n * Console\n *------------------------------------------------------------------*/\n#ifdef CONSOLE_ENABLE\n#    define CONSOLE_BUFFER_SIZE 32\n#    define CONSOLE_EPSIZE 8\n\nint8_t sendchar(uint8_t c) {\n    rbuf_enqueue(c);\n    return 0;\n}\n\nvoid console_task(void) {\n    usbPoll();\n\n    if (!usbConfiguration || !usbInterruptIsReady3()) {\n        return;\n    }\n\n    if (!rbuf_has_data()) {\n        return;\n    }\n\n    // Send in chunks of 8 padded to 32\n    char    send_buf[CONSOLE_BUFFER_SIZE] = {0};\n    uint8_t send_buf_count                = 0;\n    while (rbuf_has_data() && send_buf_count < CONSOLE_EPSIZE) {\n        send_buf[send_buf_count++] = rbuf_dequeue();\n    }\n\n    send_report(3, send_buf, CONSOLE_BUFFER_SIZE);\n}\n#endif\n\n/*------------------------------------------------------------------*\n * Host driver\n *------------------------------------------------------------------*/\nstatic void send_keyboard(report_keyboard_t *report);\nstatic void send_nkro(report_nkro_t *report);\nstatic void send_mouse(report_mouse_t *report);\nstatic void send_extra(report_extra_t *report);\n#ifdef RAW_ENABLE\nstatic void send_raw_hid(uint8_t *data, uint8_t length);\n#endif\n\nstatic host_driver_t driver = {\n    .keyboard_leds = usb_device_state_get_leds,\n    .send_keyboard = send_keyboard,\n    .send_nkro     = send_nkro,\n    .send_mouse    = send_mouse,\n    .send_extra    = send_extra,\n#ifdef RAW_ENABLE\n    .send_raw_hid = send_raw_hid,\n#endif\n};\n\nhost_driver_t *vusb_driver(void) {\n    return &driver;\n}\n\nstatic void send_keyboard(report_keyboard_t *report) {\n    if (usb_device_state_get_protocol() == USB_PROTOCOL_BOOT) {\n        send_report(1, &report->mods, 8);\n    } else {\n        send_report(1, report, sizeof(report_keyboard_t));\n    }\n\n    keyboard_report_sent = *report;\n}\n\n#ifndef KEYBOARD_SHARED_EP\n#    define MOUSE_IN_EPNUM 3\n#    define SHARED_IN_EPNUM 3\n#else\n#    define MOUSE_IN_EPNUM 1\n#    define SHARED_IN_EPNUM 1\n#endif\n\nstatic void send_nkro(report_nkro_t *report) {\n#ifdef NKRO_ENABLE\n    send_report(3, report, sizeof(report_nkro_t));\n#endif\n}\n\nstatic void send_mouse(report_mouse_t *report) {\n#ifdef MOUSE_ENABLE\n    send_report(MOUSE_IN_EPNUM, report, sizeof(report_mouse_t));\n#endif\n}\n\nstatic void send_extra(report_extra_t *report) {\n#ifdef EXTRAKEY_ENABLE\n    send_report(SHARED_IN_EPNUM, report, sizeof(report_extra_t));\n#endif\n}\n\nvoid send_joystick(report_joystick_t *report) {\n#ifdef JOYSTICK_ENABLE\n    send_report(SHARED_IN_EPNUM, report, sizeof(report_joystick_t));\n#endif\n}\n\nvoid send_digitizer(report_digitizer_t *report) {\n#ifdef DIGITIZER_ENABLE\n    send_report(SHARED_IN_EPNUM, report, sizeof(report_digitizer_t));\n#endif\n}\n\nvoid send_programmable_button(report_programmable_button_t *report) {\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\n    send_report(SHARED_IN_EPNUM, report, sizeof(report_programmable_button_t));\n#endif\n}\n\n/*------------------------------------------------------------------*\n * Request from host                                                *\n *------------------------------------------------------------------*/\nstatic struct {\n    uint16_t len;\n    enum { NONE, SET_LED } kind;\n} last_req;\n\nusbMsgLen_t usbFunctionSetup(uchar data[8]) {\n    usbRequest_t *rq = (void *)data;\n\n    if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { /* class request type */\n        switch (rq->bRequest) {\n            case USBRQ_HID_GET_REPORT:\n                dprint(\"GET_REPORT:\");\n                if (rq->wIndex.word == KEYBOARD_INTERFACE) {\n                    usbMsgPtr = (usbMsgPtr_t)&keyboard_report_sent;\n                    return sizeof(keyboard_report_sent);\n                }\n                break;\n            case USBRQ_HID_GET_IDLE:\n                dprint(\"GET_IDLE:\");\n                static uint8_t keyboard_idle;\n                keyboard_idle = usb_device_state_get_idle_rate();\n                usbMsgPtr     = (usbMsgPtr_t)&keyboard_idle;\n                return 1;\n            case USBRQ_HID_GET_PROTOCOL:\n                dprint(\"GET_PROTOCOL:\");\n                static uint8_t keyboard_protocol;\n                keyboard_protocol = usb_device_state_get_protocol();\n                usbMsgPtr         = (usbMsgPtr_t)&keyboard_protocol;\n                return 1;\n            case USBRQ_HID_SET_REPORT:\n                dprint(\"SET_REPORT:\");\n                // Report Type: 0x02(Out)/ReportID: 0x00(none) && Interface: 0(keyboard)\n                if (rq->wValue.word == 0x0200 && rq->wIndex.word == KEYBOARD_INTERFACE) {\n                    dprint(\"SET_LED:\");\n                    last_req.kind = SET_LED;\n                    last_req.len  = rq->wLength.word;\n                }\n                return USB_NO_MSG; // to get data in usbFunctionWrite\n            case USBRQ_HID_SET_IDLE:\n                usb_device_state_set_idle_rate(rq->wValue.word >> 8);\n                dprintf(\"SET_IDLE: %02X\", usb_device_state_get_idle_rate());\n                break;\n            case USBRQ_HID_SET_PROTOCOL:\n                if (rq->wIndex.word == KEYBOARD_INTERFACE) {\n                    usb_device_state_set_protocol(rq->wValue.word & 0xFF);\n                    dprintf(\"SET_PROTOCOL: %02X\", usb_device_state_get_protocol());\n                }\n                break;\n            default:\n                dprint(\"UNKNOWN:\");\n                break;\n        }\n    } else {\n        dprint(\"VENDOR:\");\n        /* no vendor specific requests implemented */\n    }\n    dprint(\"\\n\");\n    return 0; /* default for not implemented requests: return no data back to host */\n}\n\nuchar usbFunctionWrite(uchar *data, uchar len) {\n    if (last_req.len == 0) {\n        return -1;\n    }\n    switch (last_req.kind) {\n        case SET_LED:\n            usb_device_state_set_leds(data[0]);\n            dprintf(\"SET_LED: %02X\\n\", usb_device_state_get_leds());\n            last_req.len = 0;\n            return 1;\n            break;\n        case NONE:\n        default:\n            return -1;\n            break;\n    }\n    return 1;\n}\n\nvoid usbFunctionWriteOut(uchar *data, uchar len) {\n#ifdef RAW_ENABLE\n    // Data from host must be divided every 8bytes\n    if (len != 8) {\n        dprint(\"RAW: invalid length\\n\");\n        raw_output_received_bytes = 0;\n        return;\n    }\n\n    if (raw_output_received_bytes + len > RAW_BUFFER_SIZE) {\n        dprint(\"RAW: buffer full\\n\");\n        raw_output_received_bytes = 0;\n    } else {\n        for (uint8_t i = 0; i < 8; i++) {\n            raw_output_buffer[raw_output_received_bytes + i] = data[i];\n        }\n        raw_output_received_bytes += len;\n    }\n#endif\n}\n\n/*------------------------------------------------------------------*\n * Descriptors                                                      *\n *------------------------------------------------------------------*/\n\n#ifdef KEYBOARD_SHARED_EP\nconst PROGMEM uchar shared_hid_report[] = {\n#    define SHARED_REPORT_STARTED\n#else\nconst PROGMEM uchar keyboard_hid_report[] = {\n#endif\n    0x05, 0x01, // Usage Page (Generic Desktop)\n    0x09, 0x06, // Usage (Keyboard)\n    0xA1, 0x01, // Collection (Application)\n#ifdef KEYBOARD_SHARED_EP\n    0x85, REPORT_ID_KEYBOARD, // Report ID\n#endif\n    // Modifiers (8 bits)\n    0x05, 0x07, //   Usage Page (Keyboard/Keypad)\n    0x19, 0xE0, //   Usage Minimum (Keyboard Left Control)\n    0x29, 0xE7, //   Usage Maximum (Keyboard Right GUI)\n    0x15, 0x00, //   Logical Minimum (0)\n    0x25, 0x01, //   Logical Maximum (1)\n    0x95, 0x08, //   Report Count (8)\n    0x75, 0x01, //   Report Size (1)\n    0x81, 0x02, //   Input (Data, Variable, Absolute)\n    // Reserved (1 byte)\n    0x95, 0x01, //   Report Count (1)\n    0x75, 0x08, //   Report Size (8)\n    0x81, 0x03, //   Input (Constant)\n    // Keycodes (6 bytes)\n    0x05, 0x07,       //   Usage Page (Keyboard/Keypad)\n    0x19, 0x00,       //   Usage Minimum (0)\n    0x29, 0xFF,       //   Usage Maximum (255)\n    0x15, 0x00,       //   Logical Minimum (0)\n    0x26, 0xFF, 0x00, //   Logical Maximum (255)\n    0x95, 0x06,       //   Report Count (6)\n    0x75, 0x08,       //   Report Size (8)\n    0x81, 0x00,       //   Input (Data, Array, Absolute)\n\n    // Status LEDs (5 bits)\n    0x05, 0x08, //   Usage Page (LED)\n    0x19, 0x01, //   Usage Minimum (Num Lock)\n    0x29, 0x05, //   Usage Maximum (Kana)\n    0x15, 0x00, //   Logical Minimum (0)\n    0x25, 0x01, //   Logical Maximum (1)\n    0x95, 0x05, //   Report Count (5)\n    0x75, 0x01, //   Report Size (1)\n    0x91, 0x02, //   Output (Data, Variable, Absolute)\n    // LED padding (3 bits)\n    0x95, 0x01, //   Report Count (1)\n    0x75, 0x03, //   Report Size (3)\n    0x91, 0x03, //   Output (Constant)\n    0xC0,       // End Collection\n#ifndef KEYBOARD_SHARED_EP\n};\n#endif\n\n#if defined(SHARED_EP_ENABLE) && !defined(SHARED_REPORT_STARTED)\nconst PROGMEM uchar shared_hid_report[] = {\n#    define SHARED_REPORT_STARTED\n#endif\n\n#ifdef NKRO_ENABLE\n    // NKRO report descriptor\n    0x05, 0x01,           // Usage Page (Generic Desktop)\n    0x09, 0x06,           // Usage (Keyboard)\n    0xA1, 0x01,           // Collection (Application)\n    0x85, REPORT_ID_NKRO, //   Report ID\n    // Modifiers (8 bits)\n    0x05, 0x07, //   Usage Page (Keyboard/Keypad)\n    0x19, 0xE0, //   Usage Minimum (Keyboard Left Control)\n    0x29, 0xE7, //   Usage Maximum (Keyboard Right GUI)\n    0x15, 0x00, //   Logical Minimum (0)\n    0x25, 0x01, //   Logical Maximum (1)\n    0x95, 0x08, //   Report Count (8)\n    0x75, 0x01, //   Report Size (1)\n    0x81, 0x02, //   Input (Data, Variable, Absolute)\n    // Keycodes\n    0x05, 0x07,                     //   Usage Page (Keyboard/Keypad)\n    0x19, 0x00,                     //   Usage Minimum (0)\n    0x29, NKRO_REPORT_BITS * 8 - 1, //   Usage Maximum\n    0x15, 0x00,                     //   Logical Minimum (0)\n    0x25, 0x01,                     //   Logical Maximum (1)\n    0x95, NKRO_REPORT_BITS * 8,     //   Report Count\n    0x75, 0x01,                     //   Report Size (1)\n    0x81, 0x02,                     //   Input (Data, Variable, Absolute)\n\n    // Status LEDs (5 bits)\n    0x05, 0x08, //   Usage Page (LED)\n    0x19, 0x01, //   Usage Minimum (Num Lock)\n    0x29, 0x05, //   Usage Maximum (Kana)\n    0x95, 0x05, //   Report Count (5)\n    0x75, 0x01, //   Report Size (1)\n    0x91, 0x02, //   Output (Data, Variable, Absolute)\n    // LED padding (3 bits)\n    0x95, 0x01, //   Report Count (1)\n    0x75, 0x03, //   Report Size (3)\n    0x91, 0x03, //   Output (Constant)\n    0xC0,       // End Collection\n#endif\n\n#ifdef MOUSE_ENABLE\n    // Mouse report descriptor\n    0x05, 0x01,            // Usage Page (Generic Desktop)\n    0x09, 0x02,            // Usage (Mouse)\n    0xA1, 0x01,            // Collection (Application)\n    0x85, REPORT_ID_MOUSE, //   Report ID\n    0x09, 0x01,            //   Usage (Pointer)\n    0xA1, 0x00,            //   Collection (Physical)\n    // Buttons (8 bits)\n    0x05, 0x09, //     Usage Page (Button)\n    0x19, 0x01, //     Usage Minimum (Button 1)\n    0x29, 0x08, //     Usage Maximum (Button 8)\n    0x15, 0x00, //     Logical Minimum (0)\n    0x25, 0x01, //     Logical Maximum (1)\n    0x95, 0x08, //     Report Count (8)\n    0x75, 0x01, //     Report Size (1)\n    0x81, 0x02, //     Input (Data, Variable, Absolute)\n\n#    ifdef MOUSE_EXTENDED_REPORT\n    // Boot protocol XY ignored in Report protocol\n    0x95, 0x02, //     Report Count (2)\n    0x75, 0x08, //     Report Size (8)\n    0x81, 0x03, //     Input (Constant)\n#    endif\n\n    // X/Y position (2 or 4 bytes)\n    0x05, 0x01, //     Usage Page (Generic Desktop)\n    0x09, 0x30, //     Usage (X)\n    0x09, 0x31, //     Usage (Y)\n#    ifndef MOUSE_EXTENDED_REPORT\n    0x15, 0x81, //     Logical Minimum (-127)\n    0x25, 0x7F, //     Logical Maximum (127)\n    0x95, 0x02, //     Report Count (2)\n    0x75, 0x08, //     Report Size (8)\n#    else\n    0x16, 0x01, 0x80, // Logical Minimum (-32767)\n    0x26, 0xFF, 0x7F, // Logical Maximum (32767)\n    0x95, 0x02,       // Report Count (2)\n    0x75, 0x10,       // Report Size (16)\n#    endif\n    0x81, 0x06, //     Input (Data, Variable, Relative)\n\n#    ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE\n    // Feature report and padding (1 byte)\n    0xA1, 0x02,                                    //     Collection (Logical)\n    0x09, 0x48,                                    //       Usage (Resolution Multiplier)\n    0x95, 0x01,                                    //       Report Count (1)\n    0x75, 0x02,                                    //       Report Size (2)\n    0x15, 0x00,                                    //       Logical Minimum (0)\n    0x25, 0x01,                                    //       Logical Maximum (1)\n    0x35, 0x01,                                    //       Physical Minimum (1)\n    0x45, POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER, // Physical Maximum (POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER)\n    0x55, POINTING_DEVICE_HIRES_SCROLL_EXPONENT,   // Unit Exponent (POINTING_DEVICE_HIRES_SCROLL_EXPONENT)\n    0xB1, 0x02,                                    //       Feature (Data, Variable, Absolute)\n    0x35, 0x00,                                    //       Physical Minimum (0)\n    0x45, 0x00,                                    //       Physical Maximum (0)\n    0x75, 0x06,                                    //       Report Size (6)\n    0xB1, 0x03,                                    //       Feature (Constant)\n#    endif\n\n    // Vertical wheel (1 or 2 bytes)\n    0x09, 0x38, //     Usage (Wheel)\n#    ifndef WHEEL_EXTENDED_REPORT\n    0x15, 0x81, //     Logical Minimum (-127)\n    0x25, 0x7F, //     Logical Maximum (127)\n    0x95, 0x01, //     Report Count (1)\n    0x75, 0x08, //     Report Size (8)\n#    else\n    0x16, 0x01, 0x80, //     Logical Minimum (-32767)\n    0x26, 0xFF, 0x7F, //     Logical Maximum (32767)\n    0x95, 0x01,       //     Report Count (1)\n    0x75, 0x10,       //     Report Size (16)\n#    endif\n    0x81, 0x06, //     Input (Data, Variable, Relative)\n\n    // Horizontal wheel (1 or 2 bytes)\n    0x05, 0x0C,       //     Usage Page (Consumer)\n    0x0A, 0x38, 0x02, //     Usage (AC Pan)\n#    ifndef WHEEL_EXTENDED_REPORT\n    0x15, 0x81, //     Logical Minimum (-127)\n    0x25, 0x7F, //     Logical Maximum (127)\n    0x95, 0x01, //     Report Count (1)\n    0x75, 0x08, //     Report Size (8)\n#    else\n    0x16, 0x01, 0x80, //     Logical Minimum (-32767)\n    0x26, 0xFF, 0x7F, //     Logical Maximum (32767)\n    0x95, 0x01,       //     Report Count (1)\n    0x75, 0x10,       //     Report Size (16)\n#    endif\n    0x81, 0x06, //     Input (Data, Variable, Relative)\n\n#    ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE\n    0xC0, //   End Collection\n#    endif\n\n    0xC0, //   End Collection\n    0xC0, // End Collection\n#endif\n\n#ifdef EXTRAKEY_ENABLE\n    // Extrakeys report descriptor\n    0x05, 0x01,             // Usage Page (Generic Desktop)\n    0x09, 0x80,             // Usage (System Control)\n    0xA1, 0x01,             // Collection (Application)\n    0x85, REPORT_ID_SYSTEM, //   Report ID\n    0x19, 0x01,             //   Usage Minimum (Pointer)\n    0x2A, 0xB7, 0x00,       //   Usage Maximum (System Display LCD Autoscale)\n    0x15, 0x01,             //   Logical Minimum\n    0x26, 0xB7, 0x00,       //   Logical Maximum\n    0x95, 0x01,             //   Report Count (1)\n    0x75, 0x10,             //   Report Size (16)\n    0x81, 0x00,             //   Input (Data, Array, Absolute)\n    0xC0,                   // End Collection\n\n    0x05, 0x0C,               // Usage Page (Consumer)\n    0x09, 0x01,               // Usage (Consumer Control)\n    0xA1, 0x01,               // Collection (Application)\n    0x85, REPORT_ID_CONSUMER, //   Report ID\n    0x19, 0x01,               //   Usage Minimum (Consumer Control)\n    0x2A, 0xA0, 0x02,         //   Usage Maximum (AC Desktop Show All Applications)\n    0x15, 0x01,               //   Logical Minimum\n    0x26, 0xA0, 0x02,         //   Logical Maximum\n    0x95, 0x01,               //   Report Count (1)\n    0x75, 0x10,               //   Report Size (16)\n    0x81, 0x00,               //   Input (Data, Array, Absolute)\n    0xC0,                     // End Collection\n#endif\n\n#ifdef JOYSTICK_ENABLE\n    // Joystick report descriptor\n    0x05, 0x01,               // Usage Page (Generic Desktop)\n    0x09, 0x04,               // Usage (Joystick)\n    0xA1, 0x01,               // Collection (Application)\n    0x85, REPORT_ID_JOYSTICK, //   Report ID\n    0xA1, 0x00,               //   Collection (Physical)\n#    if JOYSTICK_AXIS_COUNT > 0\n    0x05, 0x01, //     Usage Page (Generic Desktop)\n    0x09, 0x30, //     Usage (X)\n#        if JOYSTICK_AXIS_COUNT > 1\n    0x09, 0x31, //     Usage (Y)\n#        endif\n#        if JOYSTICK_AXIS_COUNT > 2\n    0x09, 0x32, //     Usage (Z)\n#        endif\n#        if JOYSTICK_AXIS_COUNT > 3\n    0x09, 0x33, //     Usage (Rx)\n#        endif\n#        if JOYSTICK_AXIS_COUNT > 4\n    0x09, 0x34, //     Usage (Ry)\n#        endif\n#        if JOYSTICK_AXIS_COUNT > 5\n    0x09, 0x35, //     Usage (Rz)\n#        endif\n#        if JOYSTICK_AXIS_RESOLUTION == 8\n    0x15, -JOYSTICK_MAX_VALUE, //     Logical Minimum\n    0x25, JOYSTICK_MAX_VALUE,  //     Logical Maximum\n    0x95, JOYSTICK_AXIS_COUNT, //     Report Count\n    0x75, 0x08,                //     Report Size (8)\n#        else\n    0x16, HID_VALUE_16(-JOYSTICK_MAX_VALUE), //     Logical Minimum\n    0x26, HID_VALUE_16(JOYSTICK_MAX_VALUE),  //     Logical Maximum\n    0x95, JOYSTICK_AXIS_COUNT,               //     Report Count\n    0x75, 0x10,                              //     Report Size (16)\n#        endif\n    0x81, 0x02, //     Input (Data, Variable, Absolute)\n#    endif\n\n#    ifdef JOYSTICK_HAS_HAT\n    // Hat Switch (4 bits)\n    0x09, 0x39,       //     Usage (Hat Switch)\n    0x15, 0x00,       //     Logical Minimum (0)\n    0x25, 0x07,       //     Logical Maximum (7)\n    0x35, 0x00,       //     Physical Minimum (0)\n    0x46, 0x3B, 0x01, //     Physical Maximum (315)\n    0x65, 0x14,       //     Unit (Degree, English Rotation)\n    0x95, 0x01,       //     Report Count (1)\n    0x75, 0x04,       //     Report Size (4)\n    0x81, 0x42,       //     Input (Data, Variable, Absolute, Null State)\n    // Padding (4 bits)\n    0x95, 0x04, //     Report Count (4)\n    0x75, 0x01, //     Report Size (1)\n    0x81, 0x01, //     Input (Constant)\n#    endif\n\n#    if JOYSTICK_BUTTON_COUNT > 0\n    0x05, 0x09,                  //     Usage Page (Button)\n    0x19, 0x01,                  //     Usage Minimum (Button 1)\n    0x29, JOYSTICK_BUTTON_COUNT, //     Usage Maximum\n    0x15, 0x00,                  //     Logical Minimum (0)\n    0x25, 0x01,                  //     Logical Maximum (1)\n    0x95, JOYSTICK_BUTTON_COUNT, //     Report Count\n    0x75, 0x01,                  //     Report Size (1)\n    0x81, 0x02,                  //     Input (Data, Variable, Absolute)\n\n#        if (JOYSTICK_BUTTON_COUNT % 8) != 0\n    0x95, 8 - (JOYSTICK_BUTTON_COUNT % 8), //     Report Count\n    0x75, 0x01,                            //     Report Size (1)\n    0x81, 0x03,                            //     Input (Constant)\n#        endif\n#    endif\n    0xC0, //   End Collection\n    0xC0, // End Collection\n#endif\n\n#ifdef DIGITIZER_ENABLE\n    // Digitizer report descriptor\n    0x05, 0x0D,                // Usage Page (Digitizers)\n    0x09, 0x01,                // Usage (Digitizer)\n    0xA1, 0x01,                // Collection (Application)\n    0x85, REPORT_ID_DIGITIZER, //   Report ID\n    0x09, 0x20,                //   Usage (Stylus)\n    0xA1, 0x00,                //   Collection (Physical)\n    // In Range, Tip Switch & Barrel Switch (3 bits)\n    0x09, 0x32, //     Usage (In Range)\n    0x09, 0x42, //     Usage (Tip Switch)\n    0x09, 0x44, //     Usage (Barrel Switch)\n    0x15, 0x00, //     Logical Minimum\n    0x25, 0x01, //     Logical Maximum\n    0x95, 0x03, //     Report Count (3)\n    0x75, 0x01, //     Report Size (1)\n    0x81, 0x02, //     Input (Data, Variable, Absolute)\n    // Padding (5 bits)\n    0x95, 0x05, //     Report Count (5)\n    0x81, 0x03, //     Input (Constant)\n\n    // X/Y Position (4 bytes)\n    0x05, 0x01,       //     Usage Page (Generic Desktop)\n    0x09, 0x30,       //     Usage (X)\n    0x09, 0x31,       //     Usage (Y)\n    0x26, 0xFF, 0x7F, //     Logical Maximum (32767)\n    0x95, 0x02,       //     Report Count (2)\n    0x75, 0x10,       //     Report Size (16)\n    0x65, 0x13,       //     Unit (Inch, English Linear)\n    0x55, 0x0E,       //     Unit Exponent (-2)\n    0x81, 0x02,       //     Input (Data, Variable, Absolute)\n    0xC0,             //   End Collection\n    0xC0,             // End Collection\n#endif\n\n#ifdef PROGRAMMABLE_BUTTON_ENABLE\n    // Programmable buttons report descriptor\n    0x05, 0x0C,                          // Usage Page (Consumer)\n    0x09, 0x01,                          // Usage (Consumer Control)\n    0xA1, 0x01,                          // Collection (Application)\n    0x85, REPORT_ID_PROGRAMMABLE_BUTTON, //   Report ID\n    0x09, 0x03,                          //   Usage (Programmable Buttons)\n    0xA1, 0x04,                          //   Collection (Named Array)\n    0x05, 0x09,                          //     Usage Page (Button)\n    0x19, 0x01,                          //     Usage Minimum (Button 1)\n    0x29, 0x20,                          //     Usage Maximum (Button 32)\n    0x15, 0x00,                          //     Logical Minimum (0)\n    0x25, 0x01,                          //     Logical Maximum (1)\n    0x95, 0x20,                          //     Report Count (32)\n    0x75, 0x01,                          //     Report Size (1)\n    0x81, 0x02,                          //     Input (Data, Variable, Absolute)\n    0xC0,                                //   End Collection\n    0xC0,                                // End Collection\n#endif\n\n#ifdef SHARED_EP_ENABLE\n};\n#endif\n\n#ifdef RAW_ENABLE\nconst PROGMEM uchar raw_hid_report[] = {\n    0x06, HID_VALUE_16(RAW_USAGE_PAGE), // Usage Page (Vendor Defined)\n    0x09, RAW_USAGE_ID,                 // Usage (Vendor Defined)\n    0xA1, 0x01,                         // Collection (Application)\n    // Data to host\n    0x09, 0x62,            //   Usage (Vendor Defined)\n    0x15, 0x00,            //   Logical Minimum (0)\n    0x26, 0xFF, 0x00,      //   Logical Maximum (255)\n    0x95, RAW_BUFFER_SIZE, //   Report Count\n    0x75, 0x08,            //   Report Size (8)\n    0x81, 0x02,            //   Input (Data, Variable, Absolute)\n    // Data from host\n    0x09, 0x63,            //   Usage (Vendor Defined)\n    0x15, 0x00,            //   Logical Minimum (0)\n    0x26, 0xFF, 0x00,      //   Logical Maximum (255)\n    0x95, RAW_BUFFER_SIZE, //   Report Count\n    0x75, 0x08,            //   Report Size (8)\n    0x91, 0x02,            //   Output (Data, Variable, Absolute)\n    0xC0                   // End Collection\n};\n#endif\n\n#if defined(CONSOLE_ENABLE)\nconst PROGMEM uchar console_hid_report[] = {\n    0x06, 0x31, 0xFF, // Usage Page (Vendor Defined - PJRC Teensy compatible)\n    0x09, 0x74,       // Usage (Vendor Defined - PJRC Teensy compatible)\n    0xA1, 0x01,       // Collection (Application)\n    // Data to host\n    0x09, 0x75,                //   Usage (Vendor Defined)\n    0x15, 0x00,                //   Logical Minimum (0x00)\n    0x26, 0xFF, 0x00,          //   Logical Maximum (0x00FF)\n    0x95, CONSOLE_BUFFER_SIZE, //   Report Count\n    0x75, 0x08,                //   Report Size (8)\n    0x81, 0x02,                //   Input (Data, Variable, Absolute)\n    0xC0                       // End Collection\n};\n#endif\n\n#ifndef USB_MAX_POWER_CONSUMPTION\n#    define USB_MAX_POWER_CONSUMPTION 500\n#endif\n\n#ifndef USB_POLLING_INTERVAL_MS\n#    define USB_POLLING_INTERVAL_MS 1\n#endif\n\n// clang-format off\nconst PROGMEM usbStringDescriptor_t usbStringDescriptorZero = {\n    .header = {\n        .bLength         = 4,\n        .bDescriptorType = USBDESCR_STRING\n    },\n    .bString             = {0x0409} // US English\n};\n\nconst PROGMEM usbStringDescriptor_t usbStringDescriptorManufacturer = {\n    .header = {\n        .bLength         = sizeof(USBSTR(MANUFACTURER)),\n        .bDescriptorType = USBDESCR_STRING\n    },\n    .bString             = USBSTR(MANUFACTURER)\n};\n\nconst PROGMEM usbStringDescriptor_t usbStringDescriptorProduct = {\n    .header = {\n        .bLength         = sizeof(USBSTR(PRODUCT)),\n        .bDescriptorType = USBDESCR_STRING\n    },\n    .bString             = USBSTR(PRODUCT)\n};\n\n#if defined(SERIAL_NUMBER)\nconst PROGMEM usbStringDescriptor_t usbStringDescriptorSerial = {\n    .header = {\n        .bLength         = sizeof(USBSTR(SERIAL_NUMBER)),\n        .bDescriptorType = USBDESCR_STRING\n    },\n    .bString             = USBSTR(SERIAL_NUMBER)\n};\n#endif\n\n/*\n * Device descriptor\n */\nconst PROGMEM usbDeviceDescriptor_t usbDeviceDescriptor = {\n    .header = {\n        .bLength         = sizeof(usbDeviceDescriptor_t),\n        .bDescriptorType = USBDESCR_DEVICE\n    },\n    .bcdUSB              = 0x0110,\n    .bDeviceClass        = 0x00,\n    .bDeviceSubClass     = 0x00,\n    .bDeviceProtocol     = 0x00,\n    .bMaxPacketSize0     = 8,\n    .idVendor            = VENDOR_ID,\n    .idProduct           = PRODUCT_ID,\n    .bcdDevice           = DEVICE_VER,\n    .iManufacturer       = 0x01,\n    .iProduct            = 0x02,\n#if defined(SERIAL_NUMBER)\n    .iSerialNumber       = 0x03,\n#else\n    .iSerialNumber       = 0x00,\n#endif\n    .bNumConfigurations  = 1\n};\n\n/*\n * Configuration descriptors\n */\nconst PROGMEM usbConfigurationDescriptor_t usbConfigurationDescriptor = {\n    .header = {\n        .header = {\n            .bLength         = sizeof(usbConfigurationDescriptorHeader_t),\n            .bDescriptorType = USBDESCR_CONFIG\n        },\n        .wTotalLength        = sizeof(usbConfigurationDescriptor_t),\n        .bNumInterfaces      = TOTAL_INTERFACES,\n        .bConfigurationValue = 0x01,\n        .iConfiguration      = 0x00,\n        .bmAttributes        = (1 << 7) | USBATTR_REMOTEWAKE,\n        .bMaxPower           = USB_MAX_POWER_CONSUMPTION / 2\n    },\n\n#    ifndef KEYBOARD_SHARED_EP\n    /*\n     * Keyboard\n     */\n    .keyboardInterface = {\n        .header = {\n            .bLength         = sizeof(usbInterfaceDescriptor_t),\n            .bDescriptorType = USBDESCR_INTERFACE\n        },\n        .bInterfaceNumber    = KEYBOARD_INTERFACE,\n        .bAlternateSetting   = 0x00,\n        .bNumEndpoints       = 1,\n        .bInterfaceClass     = 0x03,\n        .bInterfaceSubClass  = 0x01,\n        .bInterfaceProtocol  = 0x01,\n        .iInterface          = 0x00\n    },\n    .keyboardHID = {\n        .header = {\n            .bLength         = sizeof(usbHIDDescriptor_t),\n            .bDescriptorType = USBDESCR_HID\n        },\n        .bcdHID              = 0x0101,\n        .bCountryCode        = 0x00,\n        .bNumDescriptors     = 1,\n        .bDescriptorType     = USBDESCR_HID_REPORT,\n        .wDescriptorLength   = sizeof(keyboard_hid_report)\n    },\n    .keyboardINEndpoint = {\n        .header = {\n            .bLength         = sizeof(usbEndpointDescriptor_t),\n            .bDescriptorType = USBDESCR_ENDPOINT\n        },\n        .bEndpointAddress    = (USBRQ_DIR_DEVICE_TO_HOST | 1),\n        .bmAttributes        = 0x03,\n        .wMaxPacketSize      = 8,\n        .bInterval           = USB_POLLING_INTERVAL_MS\n    },\n#    endif\n\n#    if defined(RAW_ENABLE)\n    /*\n     * RAW HID\n     */\n    .rawInterface = {\n        .header = {\n            .bLength         = sizeof(usbInterfaceDescriptor_t),\n            .bDescriptorType = USBDESCR_INTERFACE\n        },\n        .bInterfaceNumber    = RAW_INTERFACE,\n        .bAlternateSetting   = 0x00,\n        .bNumEndpoints       = 2,\n        .bInterfaceClass     = 0x03,\n        .bInterfaceSubClass  = 0x00,\n        .bInterfaceProtocol  = 0x00,\n        .iInterface          = 0x00\n    },\n    .rawHID = {\n        .header = {\n            .bLength         = sizeof(usbHIDDescriptor_t),\n            .bDescriptorType = USBDESCR_HID\n        },\n        .bcdHID              = 0x0101,\n        .bCountryCode        = 0x00,\n        .bNumDescriptors     = 1,\n        .bDescriptorType     = USBDESCR_HID_REPORT,\n        .wDescriptorLength   = sizeof(raw_hid_report)\n    },\n    .rawINEndpoint = {\n        .header = {\n            .bLength         = sizeof(usbEndpointDescriptor_t),\n            .bDescriptorType = USBDESCR_ENDPOINT\n        },\n        .bEndpointAddress    = (USBRQ_DIR_DEVICE_TO_HOST | USB_CFG_EP4_NUMBER),\n        .bmAttributes        = 0x03,\n        .wMaxPacketSize      = RAW_EPSIZE,\n        .bInterval           = USB_POLLING_INTERVAL_MS\n    },\n    .rawOUTEndpoint = {\n        .header = {\n            .bLength         = sizeof(usbEndpointDescriptor_t),\n            .bDescriptorType = USBDESCR_ENDPOINT\n        },\n        .bEndpointAddress    = (USBRQ_DIR_HOST_TO_DEVICE | USB_CFG_EP4_NUMBER),\n        .bmAttributes        = 0x03,\n        .wMaxPacketSize      = RAW_EPSIZE,\n        .bInterval           = USB_POLLING_INTERVAL_MS\n    },\n#    endif\n\n#    ifdef SHARED_EP_ENABLE\n    /*\n     * Shared\n     */\n    .sharedInterface = {\n        .header = {\n            .bLength         = sizeof(usbInterfaceDescriptor_t),\n            .bDescriptorType = USBDESCR_INTERFACE\n        },\n        .bInterfaceNumber    = SHARED_INTERFACE,\n        .bAlternateSetting   = 0x00,\n        .bNumEndpoints       = 1,\n        .bInterfaceClass     = 0x03,\n#        ifdef KEYBOARD_SHARED_EP\n        .bInterfaceSubClass  = 0x01,\n        .bInterfaceProtocol  = 0x01,\n#        else\n        .bInterfaceSubClass  = 0x00,\n        .bInterfaceProtocol  = 0x00,\n#        endif\n        .iInterface          = 0x00\n    },\n    .sharedHID = {\n        .header = {\n            .bLength         = sizeof(usbHIDDescriptor_t),\n            .bDescriptorType = USBDESCR_HID\n        },\n        .bcdHID              = 0x0101,\n        .bCountryCode        = 0x00,\n        .bNumDescriptors     = 1,\n        .bDescriptorType     = USBDESCR_HID_REPORT,\n        .wDescriptorLength   = sizeof(shared_hid_report)\n    },\n    .sharedINEndpoint = {\n        .header = {\n            .bLength         = sizeof(usbEndpointDescriptor_t),\n            .bDescriptorType = USBDESCR_ENDPOINT\n        },\n#        ifdef KEYBOARD_SHARED_EP\n        .bEndpointAddress    = (USBRQ_DIR_DEVICE_TO_HOST | 1),\n#        else\n        .bEndpointAddress    = (USBRQ_DIR_DEVICE_TO_HOST | USB_CFG_EP3_NUMBER),\n#        endif\n        .bmAttributes        = 0x03,\n        .wMaxPacketSize      = 8,\n        .bInterval           = USB_POLLING_INTERVAL_MS\n    },\n#    endif\n\n#    if defined(CONSOLE_ENABLE)\n    /*\n     * Console\n     */\n    .consoleInterface = {\n        .header = {\n            .bLength         = sizeof(usbInterfaceDescriptor_t),\n            .bDescriptorType = USBDESCR_INTERFACE\n        },\n        .bInterfaceNumber    = CONSOLE_INTERFACE,\n        .bAlternateSetting   = 0x00,\n        .bNumEndpoints       = 2,\n        .bInterfaceClass     = 0x03,\n        .bInterfaceSubClass  = 0x00,\n        .bInterfaceProtocol  = 0x00,\n        .iInterface          = 0x00\n    },\n    .consoleHID = {\n        .header = {\n            .bLength         = sizeof(usbHIDDescriptor_t),\n            .bDescriptorType = USBDESCR_HID\n        },\n        .bcdHID              = 0x0111,\n        .bCountryCode        = 0x00,\n        .bNumDescriptors     = 1,\n        .bDescriptorType     = USBDESCR_HID_REPORT,\n        .wDescriptorLength   = sizeof(console_hid_report)\n    },\n    .consoleINEndpoint = {\n        .header = {\n            .bLength         = sizeof(usbEndpointDescriptor_t),\n            .bDescriptorType = USBDESCR_ENDPOINT\n        },\n        .bEndpointAddress    = (USBRQ_DIR_DEVICE_TO_HOST | USB_CFG_EP3_NUMBER),\n        .bmAttributes        = 0x03,\n        .wMaxPacketSize      = CONSOLE_EPSIZE,\n        .bInterval           = 0x01\n    },\n#    endif\n};\n\n// clang-format on\n\nUSB_PUBLIC usbMsgLen_t usbFunctionDescriptor(struct usbRequest *rq) {\n    usbMsgLen_t len = 0;\n\n    switch (rq->wValue.bytes[1]) {\n        case USBDESCR_DEVICE:\n            usbMsgPtr = (usbMsgPtr_t)&usbDeviceDescriptor;\n            len       = sizeof(usbDeviceDescriptor_t);\n            break;\n        case USBDESCR_CONFIG:\n            usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor;\n            len       = sizeof(usbConfigurationDescriptor_t);\n            break;\n        case USBDESCR_STRING:\n            switch (rq->wValue.bytes[0]) {\n                case 0:\n                    usbMsgPtr = (usbMsgPtr_t)&usbStringDescriptorZero;\n                    len       = usbStringDescriptorZero.header.bLength;\n                    break;\n                case 1: // iManufacturer\n                    usbMsgPtr = (usbMsgPtr_t)&usbStringDescriptorManufacturer;\n                    len       = usbStringDescriptorManufacturer.header.bLength;\n                    break;\n                case 2: // iProduct\n                    usbMsgPtr = (usbMsgPtr_t)&usbStringDescriptorProduct;\n                    len       = usbStringDescriptorProduct.header.bLength;\n                    break;\n#if defined(SERIAL_NUMBER)\n                case 3: // iSerialNumber\n                    usbMsgPtr = (usbMsgPtr_t)&usbStringDescriptorSerial;\n                    len       = usbStringDescriptorSerial.header.bLength;\n                    break;\n#endif\n            }\n#ifdef OS_DETECTION_ENABLE\n            process_wlength(rq->wLength.word);\n#endif\n            break;\n        case USBDESCR_HID:\n            switch (rq->wIndex.word) {\n#ifndef KEYBOARD_SHARED_EP\n                case KEYBOARD_INTERFACE:\n                    usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.keyboardHID;\n                    len       = sizeof(usbHIDDescriptor_t);\n                    break;\n#endif\n\n#if defined(RAW_ENABLE)\n                case RAW_INTERFACE:\n                    usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.rawHID;\n                    len       = sizeof(usbHIDDescriptor_t);\n                    break;\n#endif\n\n#ifdef SHARED_EP_ENABLE\n                case SHARED_INTERFACE:\n                    usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.sharedHID;\n                    len       = sizeof(usbHIDDescriptor_t);\n                    break;\n#endif\n\n#if defined(CONSOLE_ENABLE)\n                case CONSOLE_INTERFACE:\n                    usbMsgPtr = (usbMsgPtr_t)&usbConfigurationDescriptor.consoleHID;\n                    len       = sizeof(usbHIDDescriptor_t);\n                    break;\n#endif\n            }\n            break;\n        case USBDESCR_HID_REPORT:\n            /* interface index */\n            switch (rq->wIndex.word) {\n#ifndef KEYBOARD_SHARED_EP\n                case KEYBOARD_INTERFACE:\n                    usbMsgPtr = (usbMsgPtr_t)keyboard_hid_report;\n                    len       = sizeof(keyboard_hid_report);\n                    break;\n#endif\n\n#if defined(RAW_ENABLE)\n                case RAW_INTERFACE:\n                    usbMsgPtr = (usbMsgPtr_t)raw_hid_report;\n                    len       = sizeof(raw_hid_report);\n                    break;\n#endif\n\n#ifdef SHARED_EP_ENABLE\n                case SHARED_INTERFACE:\n                    usbMsgPtr = (usbMsgPtr_t)shared_hid_report;\n                    len       = sizeof(shared_hid_report);\n                    break;\n#endif\n\n#if defined(CONSOLE_ENABLE)\n                case CONSOLE_INTERFACE:\n                    usbMsgPtr = (usbMsgPtr_t)console_hid_report;\n                    len       = sizeof(console_hid_report);\n                    break;\n#endif\n            }\n            break;\n    }\n    return len;\n}\n"
  },
  {
    "path": "tmk_core/protocol/vusb/vusb.h",
    "content": "/*\nCopyright 2011 Jun Wako <wakojun@gmail.com>\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 2 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n*/\n\n#pragma once\n\n#include \"host_driver.h\"\n#include <usbdrv/usbdrv.h>\n\ntypedef struct usbDescriptorHeader {\n    uchar bLength;\n    uchar bDescriptorType;\n} __attribute__((packed)) usbDescriptorHeader_t;\n\ntypedef struct usbDeviceDescriptor {\n    usbDescriptorHeader_t header;\n    unsigned              bcdUSB;\n    uchar                 bDeviceClass;\n    uchar                 bDeviceSubClass;\n    uchar                 bDeviceProtocol;\n    uchar                 bMaxPacketSize0;\n    unsigned              idVendor;\n    unsigned              idProduct;\n    unsigned              bcdDevice;\n    uchar                 iManufacturer;\n    uchar                 iProduct;\n    uchar                 iSerialNumber;\n    uchar                 bNumConfigurations;\n} __attribute__((packed)) usbDeviceDescriptor_t;\n\ntypedef struct usbConfigurationDescriptorHeader {\n    usbDescriptorHeader_t header;\n    unsigned              wTotalLength;\n    uchar                 bNumInterfaces;\n    uchar                 bConfigurationValue;\n    uchar                 iConfiguration;\n    uchar                 bmAttributes;\n    uchar                 bMaxPower;\n} __attribute__((packed)) usbConfigurationDescriptorHeader_t;\n\ntypedef struct usbStringDescriptor {\n    usbDescriptorHeader_t header;\n    int                   bString[];\n} __attribute__((packed)) usbStringDescriptor_t;\n\ntypedef struct usbInterfaceDescriptor {\n    usbDescriptorHeader_t header;\n    uchar                 bInterfaceNumber;\n    uchar                 bAlternateSetting;\n    uchar                 bNumEndpoints;\n    uchar                 bInterfaceClass;\n    uchar                 bInterfaceSubClass;\n    uchar                 bInterfaceProtocol;\n    uchar                 iInterface;\n} __attribute__((packed)) usbInterfaceDescriptor_t;\n\ntypedef struct usbEndpointDescriptor {\n    usbDescriptorHeader_t header;\n    uchar                 bEndpointAddress;\n    uchar                 bmAttributes;\n    unsigned              wMaxPacketSize;\n    uchar                 bInterval;\n} __attribute__((packed)) usbEndpointDescriptor_t;\n\ntypedef struct usbHIDDescriptor {\n    usbDescriptorHeader_t header;\n    unsigned              bcdHID;\n    uchar                 bCountryCode;\n    uchar                 bNumDescriptors;\n    uchar                 bDescriptorType;\n    unsigned              wDescriptorLength;\n} __attribute__((packed)) usbHIDDescriptor_t;\n\ntypedef struct usbConfigurationDescriptor {\n    usbConfigurationDescriptorHeader_t header;\n\n#ifndef KEYBOARD_SHARED_EP\n    usbInterfaceDescriptor_t keyboardInterface;\n    usbHIDDescriptor_t       keyboardHID;\n    usbEndpointDescriptor_t  keyboardINEndpoint;\n#else\n    usbInterfaceDescriptor_t sharedInterface;\n    usbHIDDescriptor_t       sharedHID;\n    usbEndpointDescriptor_t  sharedINEndpoint;\n#endif\n\n#if defined(RAW_ENABLE)\n    usbInterfaceDescriptor_t rawInterface;\n    usbHIDDescriptor_t       rawHID;\n    usbEndpointDescriptor_t  rawINEndpoint;\n    usbEndpointDescriptor_t  rawOUTEndpoint;\n#endif\n\n#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP)\n    usbInterfaceDescriptor_t sharedInterface;\n    usbHIDDescriptor_t       sharedHID;\n    usbEndpointDescriptor_t  sharedINEndpoint;\n#endif\n\n#if defined(CONSOLE_ENABLE)\n    usbInterfaceDescriptor_t consoleInterface;\n    usbHIDDescriptor_t       consoleHID;\n    usbEndpointDescriptor_t  consoleINEndpoint;\n#endif\n} __attribute__((packed)) usbConfigurationDescriptor_t;\n\nextern bool vusb_suspended;\n\nhost_driver_t *vusb_driver(void);\n"
  },
  {
    "path": "tmk_core/protocol/vusb/vusb.mk",
    "content": "VUSB_DIR = protocol/vusb\n\n# Path to the V-USB library\nVUSB_PATH = $(LIB_PATH)/vusb\n\nSRC += $(VUSB_DIR)/protocol.c \\\n\t$(VUSB_DIR)/vusb.c \\\n\t$(VUSB_DIR)/usb_util.c \\\n\t$(VUSB_PATH)/usbdrv/usbdrv.c \\\n\t$(VUSB_PATH)/usbdrv/usbdrvasm.S \\\n\t$(VUSB_PATH)/usbdrv/oddebug.c\n\n# Search Path\nVPATH += $(TMK_PATH)/$(VUSB_DIR)\nVPATH += $(VUSB_PATH)\n\nOPT_DEFS += -DPROTOCOL_VUSB\n"
  },
  {
    "path": "tmk_core/protocol.mk",
    "content": "SRC +=\t\\\n\t$(PROTOCOL_DIR)/host.c \\\n\t$(PROTOCOL_DIR)/report.c \\\n\t$(PROTOCOL_DIR)/usb_device_state.c \\\n\t$(PROTOCOL_DIR)/usb_util.c \\\n\nSHARED_EP_ENABLE = no\nMOUSE_SHARED_EP ?= yes\nifeq ($(strip $(KEYBOARD_SHARED_EP)), yes)\n    OPT_DEFS += -DKEYBOARD_SHARED_EP\n    SHARED_EP_ENABLE = yes\n    # With the current usb_descriptor.c code,\n    # you can't share kbd without sharing mouse;\n    # that would be a very unexpected use case anyway\n    MOUSE_SHARED_EP = yes\nendif\n\nifeq ($(strip $(MOUSE_ENABLE)), yes)\n    OPT_DEFS += -DMOUSE_ENABLE\n    ifeq ($(strip $(MOUSE_SHARED_EP)), yes)\n        OPT_DEFS += -DMOUSE_SHARED_EP\n        SHARED_EP_ENABLE = yes\n    endif\nendif\n\nifeq ($(strip $(EXTRAKEY_ENABLE)), yes)\n    OPT_DEFS += -DEXTRAKEY_ENABLE\n    SHARED_EP_ENABLE = yes\nendif\n\nifeq ($(strip $(PROGRAMMABLE_BUTTON_ENABLE)), yes)\n    OPT_DEFS += -DPROGRAMMABLE_BUTTON_ENABLE\n    SHARED_EP_ENABLE = yes\nendif\n\nifeq ($(strip $(CONSOLE_ENABLE)), yes)\n    OPT_DEFS += -DCONSOLE_ENABLE\nelse\n    # TODO: decouple this so other print backends can exist\n    OPT_DEFS += -DNO_PRINT\n    OPT_DEFS += -DNO_DEBUG\nendif\n\nifeq ($(strip $(NKRO_ENABLE)), yes)\n    OPT_DEFS += -DNKRO_ENABLE\n    SHARED_EP_ENABLE = yes\nendif\n\nifeq ($(strip $(NO_SUSPEND_POWER_DOWN)), yes)\n    OPT_DEFS += -DNO_SUSPEND_POWER_DOWN\nendif\n\nifeq ($(strip $(NO_USB_STARTUP_CHECK)), yes)\n    OPT_DEFS += -DNO_USB_STARTUP_CHECK\nendif\n\nifeq ($(strip $(USB_WAIT_FOR_ENUMERATION)), yes)\n    OPT_DEFS += -DUSB_WAIT_FOR_ENUMERATION\nendif\n\nifeq ($(strip $(JOYSTICK_SHARED_EP)), yes)\n    OPT_DEFS += -DJOYSTICK_SHARED_EP\n    SHARED_EP_ENABLE = yes\nendif\n\nifeq ($(strip $(JOYSTICK_ENABLE)), yes)\n    OPT_DEFS += -DJOYSTICK_ENABLE\n    ifeq ($(strip $(SHARED_EP_ENABLE)), yes)\n        OPT_DEFS += -DJOYSTICK_SHARED_EP\n        SHARED_EP_ENABLE = yes\n    endif\nendif\n\nifeq ($(strip $(DIGITIZER_SHARED_EP)), yes)\n    OPT_DEFS += -DDIGITIZER_SHARED_EP\n    SHARED_EP_ENABLE = yes\nendif\n\nifeq ($(strip $(DIGITIZER_ENABLE)), yes)\n    OPT_DEFS += -DDIGITIZER_ENABLE\n    ifeq ($(strip $(SHARED_EP_ENABLE)), yes)\n        OPT_DEFS += -DDIGITIZER_SHARED_EP\n        SHARED_EP_ENABLE = yes\n    endif\nendif\n\nifeq ($(strip $(SHARED_EP_ENABLE)), yes)\n    OPT_DEFS += -DSHARED_EP_ENABLE\nendif\n\nifeq ($(strip $(USB_HID_ENABLE)), yes)\n    include $(TMK_DIR)/protocol/usb_hid/usb_hid.mk\nendif\n\n# Search Path\nVPATH += $(TMK_DIR)/protocol\n"
  },
  {
    "path": "tmk_core/readme.md",
    "content": "TMK Keyboard Firmware Core Library\n==================================\nThis is a keyboard firmware library with some useful features for Atmel AVR and Cortex-M.\n\nSource code is available here: <https://github.com/tmk/tmk_keyboard/tree/master/tmk_core>\n\n\nUpdates\n-------\n#### 2016/02/10\nflabbergast's Chibios protocol was merged from <https://github.com/flabbergast/tmk_keyboard/tree/chibios>. See [protocol/chibios/README.md](protocol/chibios/README.md). Chibios protocol supports Cortex-M such as STM32 and Kinetis.\n\n#### 2015/04/22\nseparated with TMK Keyboard Firmware Collection\n\n\n\nFeatures\n--------\nThese features can be used in your keyboard.\n\n* Multi-layer Keymap  - Multiple keyboard layouts with layer switching\n* Mouse key           - Mouse control with keyboard\n* System Control Key  - Power Down, Sleep, Wake Up and USB Remote Wake up\n* Media Control Key   - Volume Down/Up, Mute, Next/Prev track, Play, Stop and etc\n* USB NKRO            - 248 keys(+ 8 modifiers) simultaneously\n* PS/2 mouse support  - PS/2 mouse(TrackPoint) as composite device\n* User Function       - Customizable function of key with writing code\n* Macro               - Very primitive at this time\n* Keyboard Tricks     - Oneshot modifier and modifier with tapping feature\n* Debug Console       - Messages for debug and interaction with firmware\n* Virtual DIP Switch  - Configurations stored EEPROM(Boot Magic)\n* Locking CapsLock    - Mechanical switch support for CapsLock\n* Breathing Sleep LED - Sleep indicator with charm during USB suspend\n* Backlight           - Control backlight levels\n\n\n\nTMK Keyboard Firmware Collection\n--------------------------------\nComplete firmwares for various keyboards and protocol converters.\n\n<https://github.com/tmk/tmk_keyboard>\n\n\n\nLicense\n-------\n**GPLv2** or later. Some protocol files are under **Modified BSD License**.\nChibiOS, LUFA and V-USB stack have their own license respectively.\n\n\n\nBuild Firmware and Program Controller\n-------------------------------------\nSee [doc/build.md](https://github.com/tmk/tmk_keyboard/blob/master/tmk_core/doc/build.md).\n\n\n\nStart Your Own Project\n-----------------------\n**TBD**\n### Config.h Options\n#### 1. USB vendor/product ID and device description\n    #define VENDOR_ID       0xFEED\n    #define PRODUCT_ID      0xBEEF\n    #define MANUFACTURER    t.m.k.\n    #define PRODUCT         Macway mod\n\n#### 2. Keyboard matrix configuration\n    #define MATRIX_ROWS 8\n    #define MATRIX_COLS 8\n    #define MATRIX_HAS_GHOST\n\n\n\nArchitecture\n------------\n    Architecture Diagram\n                               +---------------+---------------+-------------+\n                               |    Host       |   Keyboard    | Matrix, LED |\n       ___________             |-----------+-+ +-------------+ | +-----------|\n      /          /| Keys/Mouse | Protocol  |d| | Action      | | | Protocol  |\n     /__________/ |<-----------|  LUFA     |r| |  Layer, Tap | | |  Matrix   |\n     |.--------.| |   LED      |  V-USB    |i| |-------------| | |  PS/2,IBM |             __________________\n     ||        || |----------->|  UART     |v| | Keymap      | | |           |  Keys      / /_/_/_/_/_/_/_/ /|\n     ||  Host  || |  Console   |           |e| | Mousekey    | | |           |<----------/ /_/_/_/_/_/_/_/ / /\n     ||________||/.<-----------|           |r| | Report      | | |           | Control  / /_/_/_/_/_/_/_/ / /\n     `_========_'/|            |---------------------------------------------|-------->/___ /_______/ ___/ /\n     |_o______o_|/             | Sendchar, Print, Debug, Command, ...        |         |_________________|/\n                               +---------------------------------------------+              Keyboard\n\n\n\nDebugging\n--------\nUse PJRC's `hid_listen` to see debug messages. You can use the tool for debug even if firmware use LUFA stack.\n\nYou can use xprintf() to display debug info on `hid_listen`, see `common/xprintf.h`.\n\n\n\nFiles and Directories\n-------------------\n### Top\n* common/       - common codes\n* protocol/     - keyboard protocol support\n* doc/          - documents\n* common.mk     - Makefile for common\n* protocol.mk    - Makefile for protocol\n* rules.mk      - Makefile for build rules\n\n### Common\n* host.h\n* host_driver.h\n* keyboard.h\n* command.h\n* keymap.h\n* action.h\n* keycode.h\n* matrix.h\n* led.h\n* mousekey.h\n* report.h\n* debug.h\n* print.h\n* bootloader.h\n* sendchar.h\n* timer.h\n* util.h\n\n### Keyboard Protocols\n* lufa/     - LUFA USB stack\n* vusb/     - Objective Development V-USB\n* ps2.c     - PS/2 protocol\n* serial_soft.c - Asynchronous Serial protocol implemented by software\n\n\n\nCoding Style\n-------------\n- Doesn't use Tab to indent, use 4-spaces instead.\n"
  },
  {
    "path": "util/audio_generate_dac_lut.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright 2020 JohSchneider\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\n\nAUDIO_DAC_BUFFER_SIZE=256\nAUDIO_DAC_SAMPLE_MAX=4095\n\ndef plot(values):\n    for v in values:\n        print('0'* int(v * 80/AUDIO_DAC_SAMPLE_MAX))\n\ndef to_lut(values):\n    for v in values:\n        print(hex(int(v)), end=\", \")\n\n\nfrom math import sin, tau, pi\n\nsamples=[]\n\ndef sampleSine():\n    for s in range(AUDIO_DAC_BUFFER_SIZE):\n        samples.append((sin((s/AUDIO_DAC_BUFFER_SIZE)*tau - pi/2) + 1 )/2* AUDIO_DAC_SAMPLE_MAX)\n\ndef sampleTriangle():\n    for s in range(AUDIO_DAC_BUFFER_SIZE):\n        if s < AUDIO_DAC_BUFFER_SIZE/2:\n            samples.append(s/(AUDIO_DAC_BUFFER_SIZE/2) * AUDIO_DAC_SAMPLE_MAX)\n        else:\n            samples.append(AUDIO_DAC_SAMPLE_MAX - (s-AUDIO_DAC_BUFFER_SIZE/2)/(AUDIO_DAC_BUFFER_SIZE/2) * AUDIO_DAC_SAMPLE_MAX)\n\n#compromise between square and triangle wave,\ndef sampleTrapezoidal():\n    for i in range(AUDIO_DAC_BUFFER_SIZE):\n        a=3 #slope/inclination\n        if (i < AUDIO_DAC_BUFFER_SIZE/2):\n            s = a * (i * AUDIO_DAC_SAMPLE_MAX/(AUDIO_DAC_BUFFER_SIZE/2)) + (1-a)*AUDIO_DAC_SAMPLE_MAX/2\n        else:\n            i = i - AUDIO_DAC_BUFFER_SIZE/2\n            s = AUDIO_DAC_SAMPLE_MAX - a * (i * AUDIO_DAC_SAMPLE_MAX/(AUDIO_DAC_BUFFER_SIZE/2)) - (1-a)*AUDIO_DAC_SAMPLE_MAX/2\n\n        if s < 0:\n            s=0\n        if s> AUDIO_DAC_SAMPLE_MAX:\n            s=AUDIO_DAC_SAMPLE_MAX\n        samples.append(s)\n\n\n#sampleSine()\nsampleTrapezoidal()\n#print(samples)\nplot(samples)\nto_lut(samples)\n"
  },
  {
    "path": "util/chibios_conf_updater.sh",
    "content": "#!/usr/bin/env bash\n\nset -eEuo pipefail\numask 022\n\n#####################\n# You will need to get an older JDK -- JDK 8\n#\n# !!!!!!!! DO NOT INSTALL THIS IF YOU HAVE AN EXISTING JDK OR JRE INSTALLED !!!!!!!!\n#\n# For Debian 10-ish distro's:\n#   wget -qO - https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public | sudo apt-key add -\n#   sudo add-apt-repository --yes https://adoptopenjdk.jfrog.io/adoptopenjdk/deb/\n#   sudo apt-get update && sudo apt-get install adoptopenjdk-8-hotspot\n#\n# For Fedora 37-ish distros:\n#  sudo dnf install -y ant java-1.8.0-openjdk.x86_64\n\nsinfo() { echo \"$@\" >&2 ; }\nshead() { sinfo \"\" ; sinfo \"---------------------------------\" ; sinfo \"-- $@\" ; sinfo \"---------------------------------\" ; }\n\nthis_script=\"$(realpath \"${BASH_SOURCE[0]}\")\"\nscript_dir=\"$(realpath \"$(dirname \"$this_script\")\")\"\nqmk_firmware_dir=\"$(realpath \"$script_dir/../\")\"\n\nexport PATH=\"$PATH:$script_dir/fmpp/bin\"\n\nbuild_fmpp() {\n    [ -f \"$script_dir/fmpp.tar.gz\" ] \\\n        || wget -O\"$script_dir/fmpp.tar.gz\" https://github.com/freemarker/fmpp/archive/v0.9.16.tar.gz\n    [ -d \"$script_dir/fmpp\" ] \\\n        || { mkdir \"$script_dir/fmpp\" && tar xf \"$script_dir/fmpp.tar.gz\" -C \"$script_dir/fmpp\" --strip-components=1 ; }\n    pushd \"$script_dir/fmpp\" >/dev/null 2>&1\n    sed -e \"s#bootclasspath.path=.*#bootclasspath.path=$(find /usr/lib/jvm -name 'rt.jar' | sort | tail -n1)#g\" \\\n        -e \"s#ant.jar.path=.*#ant.jar.path=$(find /usr/share/java -name 'ant-1*.jar' -or -name 'ant.jar' | sort | tail -n1)#g\" \\\n        build.properties.sample > build.properties\n    sed -e 's#source=\"1.5\"#source=\"1.8\"#g' \\\n        -e 's#target=\"1.5\"#target=\"1.8\"#g' \\\n        build.xml > build.xml.new\n    mv build.xml.new build.xml\n    ant clean\n    ant\n    chmod +x \"$script_dir/fmpp/bin/fmpp\"\n    popd >/dev/null 2>&1\n}\n\nfind_chibi_files() {\n    local search_path=\"$1\"\n    shift\n    local conditions=( \"$@\" )\n    for file in $(find -L \"$search_path\" -not -path '*/lib/chibios*' -and -not -path '*/util/*' -and \\( \"${conditions[@]}\" \\) | sort) ; do\n        if [ -z \"$(grep 'include_next' \"$file\")\" ] ; then\n            echo $file\n        fi\n    done\n}\n\nupgrade_conf_files_generic() {\n    local search_filename=\"$1\"\n    local update_script=\"$2\"\n    shead \"Updating $search_filename files ($update_script)...\"\n    pushd \"$qmk_firmware_dir/lib/chibios/tools/updater\" >/dev/null 2>&1\n    for file in $(find_chibi_files \"$qmk_firmware_dir\" -name \"$search_filename\") ; do\n        cp -f \"$file\" \"$file.orig\"\n        clang-format --style='{IndentPPDirectives: None}' -i \"$file\"\n        cp -f \"$file\" \"$file.formatted\"\n        bash \"$update_script\" \"$file\"\n        if ! diff \"$file\" \"$file.formatted\" >/dev/null 2>&1 ; then\n            dos2unix \"$file\" >/dev/null 2>&1\n        else\n            cp -f \"$file.orig\" \"$file\"\n        fi\n        rm -f \"$file.orig\" \"$file.formatted\"\n    done\n    popd >/dev/null 2>&1\n}\n\nupgrade_chconf_files() {\n    upgrade_conf_files_generic chconf.h update_chconf_rt.sh\n}\n\nupgrade_halconf_files() {\n    upgrade_conf_files_generic halconf.h update_halconf.sh\n\n    OIFS=$IFS\n    IFS=$'\\n'\n    for file in $(find_chibi_files \"$qmk_firmware_dir\" -name halconf.h) ; do\n        echo $file\n        sed -i 's@#include \"mcuconf.h\"@#include <mcuconf.h>@g' \"$file\"\n    done\n    IFS=$OIFS\n}\n\nupgrade_mcuconf_files() {\n    pushd \"$qmk_firmware_dir/lib/chibios/tools/updater\" >/dev/null 2>&1\n    for f in $(find . -name 'update_mcuconf*') ; do\n        upgrade_conf_files_generic mcuconf.h $f\n    done\n    popd >/dev/null 2>&1\n}\n\nhash -r\n[[ -n \"$(which fmpp 2>/dev/null)\" ]] || build_fmpp\n\nupgrade_mcuconf_files\nupgrade_chconf_files\nupgrade_halconf_files\n"
  },
  {
    "path": "util/ci/discord-results.py",
    "content": "#!/usr/bin/env python3\n\nimport argparse\nimport os\nimport re\nimport sys\nfrom pathlib import Path\nfrom discord_webhook import DiscordWebhook, DiscordEmbed\n\nparser = argparse.ArgumentParser(prog='discord-results.py', description='Sends a Discord webhook notification at the end of a CI run.')\nparser.add_argument('-b', '--branch')\nparser.add_argument('-k', '--keymap')\nparser.add_argument('-u', '--url')\nparser.add_argument('-s', '--sha')\nargs = parser.parse_args()\n\nqmk_dir = Path(__file__).resolve().parents[2].resolve()\n\nkeyboard_re = re.compile(r'CI Metadata: KEYBOARD=(.*)$', re.MULTILINE)\nkeymap_re = re.compile(r'CI Metadata: KEYMAP=(.*)$', re.MULTILINE)\n\nsuccessful_builds = sum([len(list(qmk_dir.glob(f'*.{extension}'))) for extension in ['uf2', 'bin', 'hex']])\nfailures = list(sorted([f.resolve() for f in (qmk_dir / '.build/').glob('failed.log.*')]))\nfailed_builds = []\nfor f in failures:\n    with open(f) as fh:\n        data = fh.read()\n        kb = keyboard_re.search(data).group(1)\n        km = keymap_re.search(data).group(1)\n        failed_builds.append(f'{kb}:{km}')\n\nwebhook = DiscordWebhook(url=os.getenv('DISCORD_WEBHOOK'), username=\"QMK GitHub CI\")\nif len(failed_builds) > 0:\n    failstr = ''\n    for f in failed_builds:\n        if len(failstr) >= 1800:\n            failstr += '<<snip>>'\n            break\n        failstr += f'{f}\\n'\n\n    embed = DiscordEmbed(title=f':infinity: CI Build Failure ({args.branch}, {args.keymap})', description=f'**{successful_builds}** builds succeeded, **{len(failed_builds)}** builds failed:```{failstr}```', color='ff9999')\nelse:\n    embed = DiscordEmbed(title=f':infinity: CI Build Success ({args.branch}, {args.keymap})', description=f'**{successful_builds}** builds succeeded.', color='99ff99')\n\nembed.add_embed_field(name='Build Target', value=f'[**{args.branch}**](https://github.com/qmk/qmk_firmware/tree/{args.branch}) / **{args.keymap}** keymap')\nembed.add_embed_field(name='Workflow Run', value=f'[**Link**]({args.url})')\nembed.add_embed_field(name='Firmware Binaries', value=f'[**ci.qmk.fm**](https://ci.qmk.fm/{args.branch}/{args.sha}/index.html)')\nembed.set_timestamp()\n\nwebhook.add_embed(embed)\nwebhook.execute()\n"
  },
  {
    "path": "util/ci/firmware_list_generator.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport json\nfrom pathlib import Path\nfrom time import gmtime, strftime\n\nDATETIME_FORMAT = '%Y-%m-%d %H:%M:%S %Z'\n\n\ndef current_datetime():\n    return strftime(DATETIME_FORMAT, gmtime())\n\n\nqmk_firmware_dir = Path(os.path.realpath(__file__)).parents[2]\n\nbinaries = []\nbinaries.extend(qmk_firmware_dir.glob(\"*.bin\"))\nbinaries.extend(qmk_firmware_dir.glob(\"*.hex\"))\nbinaries.extend(qmk_firmware_dir.glob(\"*.uf2\"))\nbinaries = list(sorted(binaries))\n\ndata = []\nfor binary in binaries:\n    data.append(binary.name)\n\nkeyboard_all_json = json.dumps({'last_updated': current_datetime(), 'files': data}, separators=(',', ':'))\n\nprint(keyboard_all_json)\n"
  },
  {
    "path": "util/ci/generate_failure_markdown.sh",
    "content": "#!/bin/bash\n\nset -e\n\nthis_script=\"$(realpath \"${BASH_SOURCE[0]}\")\"\nscript_dir=\"$(realpath \"$(dirname \"$this_script\")\")\"\nqmk_firmware_dir=\"$(realpath \"$script_dir/../../\")\"\n\ndump_failure_info() {\n    local failure_file=\"$1\"\n    local keyboard=$(cat \"$failure_file\" | grep 'CI Metadata: KEYBOARD=' | cut -d= -f2)\n    local keymap=$(cat \"$failure_file\" | grep 'CI Metadata: KEYMAP=' | cut -d= -f2)\n    echo \"## ${keyboard}:${keymap}\"\n    echo \"\\`\\`\\`\"\n    cat \"$failure_file\" | sed -e $'s/\\x1b\\[[0-9;]*m//g' | grep -v \"CI Metadata:\" | grep -vP \"(Entering|Leaving) directory\"\n    echo \"\\`\\`\\`\"\n}\n\nfor failure_file in $(find \"$qmk_firmware_dir/.build\" -name 'failed.log.*' | sort); do\n    dump_failure_info \"$failure_file\"\ndone\n\nexit 0\n"
  },
  {
    "path": "util/ci/index_generator.py",
    "content": "#!/usr/bin/env python3\n\nimport os\nimport re\nimport shlex\nimport subprocess\nfrom pathlib import Path\n\nfrom ansi2html import Ansi2HTMLConverter\nfrom jinja2 import Environment, FileSystemLoader, select_autoescape\n\norig_cwd = os.getcwd()\nqmk_firmware_dir = Path(os.path.realpath(__file__)).parents[2]\nbuild_dir = qmk_firmware_dir / \".build\"\n\nKEYBOARD_PATTERN = re.compile(\"CI Metadata: KEYBOARD=(?P<keyboard>.*)\\r?\\n\")\nKEYMAP_PATTERN = re.compile(\"CI Metadata: KEYMAP=(?P<keymap>.*)\\r?\\n\")\n\nenv = Environment(\n    loader=FileSystemLoader(Path(os.path.realpath(__file__)).parent / \"templates\"),\n    autoescape=select_autoescape(),\n)\n\n\ndef _run(command, capture_output=True, combined_output=False, text=True, **kwargs):\n    if isinstance(command, str):\n        command = shlex.split(command)\n    if capture_output:\n        kwargs[\"stdout\"] = subprocess.PIPE\n        kwargs[\"stderr\"] = subprocess.PIPE\n    if combined_output:\n        kwargs[\"stderr\"] = subprocess.STDOUT\n    if \"stdin\" in kwargs and kwargs[\"stdin\"] is None:\n        del kwargs[\"stdin\"]\n    if text:\n        kwargs[\"universal_newlines\"] = True\n    return subprocess.run(command, **kwargs)\n\n\ndef _ansi2html(value):\n    return Ansi2HTMLConverter().convert(value, full=False)\n\n\ndef _ansi2html_styles():\n    from ansi2html.style import get_styles\n\n    styles = get_styles(scheme=\"dracula\")\n    return \"\\n\".join([str(s) for s in styles])\n\n\ndef _git_log(count=4):\n    os.chdir(qmk_firmware_dir)\n    ret = _run(f\"git log -n {count} --color=always --no-merges --topo-order --stat\").stdout.strip()\n    os.chdir(orig_cwd)\n    return ret\n\n\ndef _git_describe():\n    os.chdir(qmk_firmware_dir)\n    ret = _run(\"git describe --tags --always --dirty\").stdout.strip()\n    os.chdir(orig_cwd)\n    return ret\n\n\ndef _git_revision():\n    os.chdir(qmk_firmware_dir)\n    ret = _run(\"git rev-parse HEAD\").stdout.strip()\n    os.chdir(orig_cwd)\n    return ret\n\n\nenv.filters[\"ansi2html\"] = _ansi2html\n\nbinaries = []\nbinaries.extend(qmk_firmware_dir.glob(\"*.bin\"))\nbinaries.extend(qmk_firmware_dir.glob(\"*.hex\"))\nbinaries.extend(qmk_firmware_dir.glob(\"*.uf2\"))\nbinaries = list(sorted(binaries))\n\nfailures = []\nfor mdfile in list(sorted(build_dir.glob(\"failed.log.*\"))):\n    txt = Path(mdfile).read_text()\n\n    m_kb = KEYBOARD_PATTERN.search(txt)\n    if not m_kb:\n        raise Exception(\"Couldn't determine the keyboard from the failure output\")\n    m_km = KEYMAP_PATTERN.search(txt)\n    if not m_km:\n        raise Exception(\"Couldn't determine the keymap from the failure output\")\n\n    txt = KEYBOARD_PATTERN.sub(\"\", KEYMAP_PATTERN.sub(\"\", txt)).strip()\n\n    failures.append({\n        \"stdout\": txt,\n        \"keyboard\": m_kb.group(\"keyboard\"),\n        \"keymap\": m_km.group(\"keymap\"),\n    })\n\ntemplate = env.get_template(\"index.html.j2\")\nprint(template.render(\n    ansi2html_styles=_ansi2html_styles(),\n    git_log=_git_log(),\n    git_describe=_git_describe(),\n    git_revision=_git_revision(),\n    binaries=binaries,\n    failures=failures,\n))\n"
  },
  {
    "path": "util/ci/requirements.txt",
    "content": "discord-webhook\nJinja2\nansi2html\n"
  },
  {
    "path": "util/ci/templates/index.html.j2",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n    <link rel=\"icon\" href=\"https://qmk.fm/logo.png\" />\n    <style type=\"text/css\">\n        {{ ansi2html_styles }}\n\n        body {\n            color: #FFF;\n            background-color: #111;\n        }\n\n        h3 {\n            font-family: sans-serif;\n            margin-top: 0;\n        }\n\n        a {\n            color: #00e1ff;\n            text-decoration: none;\n        }\n\n        a:hover {\n            color: #f700ff;\n            text-decoration: underline;\n        }\n\n        a:visited {\n            color: #00e1ff;\n        }\n\n        .build-target {\n            background-color: #333;\n            font-family: monospace;\n            padding-left: 0.3em;\n            padding-right: 0.3em;\n        }\n\n        .binary-link {\n            margin: 0;\n            margin-top: 0.25em;\n        }\n\n        .container {\n            background-color: #222;\n            padding: 2.0em;\n            margin: 2.0em;\n            border-radius: 2.0em;\n        }\n\n        .header-container {\n            display: table-cell;\n            padding-left: 1.5em;\n            vertical-align: middle;\n            font-family: sans-serif;\n        }\n\n        .header-container div:not(:first-child) {\n            margin-top: 0.25em;\n        }\n    </style>\n</head>\n\n<body class=\"\" style=\"font-size: normal;\">\n    <div style=\"float: left\">\n        <div class=\"container\">\n            <div style=\"display: table-cell; vertical-align: middle;\">\n                <a href=\"https://qmk.fm/\"><img src=\"https://qmk.fm/badge-community-dark.svg\" style=\"width: 30em;\" /></a>\n            </div>\n            <div class=\"header-container\">\n                <div>\n                    <span style=\"font-size: 175%; font-weight: bold;\">CI Build {% if failures | length > 0 %}&#10060;{% else %}&#9989;{% endif %}</span>\n                </div>\n                <div>\n                    <span style=\"font-size: 100%; font-family: monospace;\">{{ git_describe }}</span>\n                </div>\n                <div>\n                    <span style=\"font-size: 100%; font-family: monospace;\">{{ git_revision }}</span>\n                </div>\n                <div>\n                    <span style=\"font-size: 80%; font-weight: bold; color: {% if failures | length > 0 %}#F00{% else %}#0F0{% endif %};\">{{ failures | length }} failure{% if failures | length != 1 %}s{% endif %}</span>\n                </div>\n                <div>\n                    <span style=\"font-size: 80%\">\n                    {% if binaries | length > 0 %}<a href=\"#firmwares\">Firmwares</a> | {% endif %}\n                    <a href=\"#commit_info\">Commit info</a>\n                    {% if failures | length > 0 %} | <a href=\"#failure_logs\">Failure Logs</a>{% endif %}\n                    </span>\n                </div>\n            </div>\n        </div>\n        <a name=\"commit_info\"></a>\n        <div class=\"container\">\n            <h3>Git commit</h3>\n            <div class=\"body_foreground body_background\"\n                style=\"display: table-cell; padding: 1.0em; border-radius: 1.0em;\">\n                <pre class=\"ansi2html-content\">{{ git_log | ansi2html }}</pre>\n            </div>\n        </div>\n        {% if failures | length > 0 %}\n        <a name=\"failure_logs\"></a>\n        <div class=\"container\">\n            <h3>Build failure logs</h3>\n            <ul>\n                {% for failure in failures %}\n                <li><a style=\"font-family: monospace;\" href=\"#build_failure_{{ failure.keyboard }}_{{ failure.keymap }}\">{{ failure.keyboard }}:{{ failure.keymap }}</a></li>\n                {% endfor %}\n            </ul>\n        </div>\n        {% for failure in failures %}\n        <a name=\"build_failure_{{ failure.keyboard }}_{{ failure.keymap }}\"></a>\n        <div class=\"container\">\n            <h3>Build failure &mdash; <span class=\"build-target\">{{ failure.keyboard }}:{{ failure.keymap }}</span></h3>\n            <div class=\"body_foreground body_background\"\n                style=\"display: table-cell; padding: 1.0em; border-radius: 1.0em;\">\n                <pre class=\"ansi2html-content\">{{ failure.stdout | ansi2html }}</pre>\n            </div>\n        </div>\n        {% endfor %}\n        {% endif %}\n    </div>\n    {% if binaries | length > 0 %}\n    <div style=\"float: right\">\n        <a name=\"firmwares\"></a>\n        <div class=\"container\">\n            <h3>Firmware downloads</h3>\n            <div class=\"body_foreground body_background\"\n                style=\"display: table-cell; padding: 1.0em; border-radius: 1.0em;\">\n                {% for binary in binaries %}\n                <p class=\"binary-link\" style=\"font-family: monospace;\"><a href=\"{{ binary.name }}\">{{ binary.name }}</a></p>\n                {%- endfor %}\n            </div>\n        </div>\n    </div>\n    {% endif %}\n</body>\n\n</html>\n"
  },
  {
    "path": "util/docker_build.sh",
    "content": "#!/bin/sh\n\n./util/docker_cmd.sh make \"$@\"\n"
  },
  {
    "path": "util/docker_cmd.sh",
    "content": "#!/bin/sh\n# vim: set ft=sh ts=4 sw=4 noexpandtab\n# NOTE: This script uses tabs for indentation\n\nerrcho() {\n\techo \"$@\" >&2\n}\n\nUSAGE=\"Usage: $0 <command>\"\n\n# Check preconditions\nfor arg; do\n\tif [ \"$arg\" = \"--help\" ]; then\n\t\techo \"$USAGE\"\n\t\texit 0\n\tfi\ndone\n\n# Allow $RUNTIME to be overridden by the user as an environment variable\n# Else check if either podman or docker exit and set them as runtime\n# if none are found error out\nif [ -z \"$RUNTIME\" ]; then\n\tif command -v podman >/dev/null 2>&1; then\n\t\tRUNTIME=\"podman\"\n\telif command -v docker >/dev/null 2>&1; then\n\t\tRUNTIME=\"docker\"\n\telse\n\t\terrcho \"Error: no compatible container runtime found.\"\n\t\terrcho \"Either podman or docker are required.\"\n\t\terrcho \"See https://podman.io/getting-started/installation\"\n\t\terrcho \"or https://docs.docker.com/install/#supported-platforms\"\n\t\terrcho \"for installation instructions.\"\n\t\texit 2\n\tfi\nfi\n\n# If SKIP_FLASHING_SUPPORT is defined, do not check for docker-machine and do not run a privileged container\nif [ -z \"$SKIP_FLASHING_SUPPORT\" ]; then\n  # IF we are using docker on non Linux and docker-machine isn't working print an error\n  # ELSE set usb_args\n  if [ ! \"$(uname)\" = \"Linux\" ] && [ \"$RUNTIME\" = \"docker\" ] && ! docker-machine active >/dev/null 2>&1; then\n    errcho \"Error: target requires docker-machine to work on your platform\"\n    errcho \"See http://gw.tnode.com/docker/docker-machine-with-usb-support-on-windows-macos\"\n    exit 3\n  else\n    usb_args=\"--privileged -v /dev:/dev\"\n  fi\nfi\n\nqmk_firmware_dir=$(pwd -W 2>/dev/null) || qmk_firmware_dir=$PWD  # Use Windows path if on Windows\nqmk_userspace_dir=\"\"\nuserspace_docker_args=\"\"\n\nif [ -n \"$QMK_USERSPACE\" ] && [ -e \"$QMK_USERSPACE/qmk.json\" ]; then\n\tqmk_userspace_dir=$(cd \"$QMK_USERSPACE\" && pwd -W 2>/dev/null) || qmk_userspace_dir=$QMK_USERSPACE  # Use Windows path if on Windows\nelif [ -n \"$(which qmk 2>/dev/null)\" ] && [ -n \"$(qmk userspace-path)\" ]; then\n\tqmk_userspace_dir=$(cd \"$(qmk userspace-path)\" && pwd -W 2>/dev/null) || qmk_userspace_dir=$(qmk userspace-path)  # Use Windows path if on Windows\nfi\n\nif [ -n \"$qmk_userspace_dir\" ]; then\n\tuserspace_docker_args=\"-v $qmk_userspace_dir:/qmk_userspace:z -e QMK_USERSPACE=/qmk_userspace\"\nfi\n\nif [ \"$RUNTIME\" = \"docker\" ]; then\n\tuid_arg=\"--user $(id -u):$(id -g)\"\nfi\n\n# Run container and build firmware\n\"$RUNTIME\" run --rm -it \\\n\t$usb_args \\\n\t$uid_arg \\\n\t-w /qmk_firmware \\\n\t-v \"$qmk_firmware_dir\":/qmk_firmware:z \\\n\t$userspace_docker_args \\\n\t-e SKIP_GIT=\"$SKIP_GIT\" \\\n\t-e SKIP_VERSION=\"$SKIP_VERSION\" \\\n\t-e MAKEFLAGS=\"$MAKEFLAGS\" \\\n\tghcr.io/qmk/qmk_cli \\\n\t\"$@\"\n"
  },
  {
    "path": "util/drivers.txt",
    "content": "# The format is\n# driver,desc,vid,pid,guid\n# Use a comma as a separator without spaces\n# Driver can be one of winusb,libusb,libusbk\n# Use Windows Powershell and type [guid]::NewGuid() to generate guids\nwinusb,STM32 Bootloader,0483,DF11,6d98a87f-4ecf-464d-89ed-8c684d857a75\nwinusb,APM32 Bootloader,314B,0106,9ff3cc31-6772-4a3f-a492-a80d91f7a853\nwinusb,WB32 Bootloader,342D,DFA0,89b0fdf0-3d22-4408-8393-32147ba508ce\nwinusb,GD32V Bootloader,28E9,0189,e1421fd6-f799-4b6c-97e6-39e87d37f858\nwinusb,STM32duino Bootloader,1EAF,0003,746915ec-99d8-4a90-a722-3c85ba31e4fe\nlibusbk,USBaspLoader,16C0,05DC,e69affdc-0ef0-427c-aefb-4e593c9d2724\nwinusb,Kiibohd DFU Bootloader,1C11,B007,aa5a3f86-b81e-4416-89ad-0c1ea1ed63af\nwinusb,ATmega16U2,03EB,2FEF,007274da-b75f-492e-a288-8fc0aff8339f\nwinusb,ATmega32U2,03EB,2FF0,ddc2c572-cb6e-4f61-a6cc-1a5de941f063\nwinusb,ATmega16U4,03EB,2FF3,3180d426-bf93-4578-a693-2efbc337da8e\nwinusb,ATmega32U4,03EB,2FF4,5f9726fd-f9de-487a-9fbd-8b3524a7a56a\nwinusb,AT90USB64,03EB,2FF9,c6a708ad-e97d-43cd-b04a-3180d737a71b\nwinusb,AT90USB162,03EB,2FFA,ef8546f0-ef09-4e7c-8fc2-ffbae1dcd84a\nwinusb,AT90USB128,03EB,2FFB,fd217df3-59d0-440a-a8f3-4c0c8c84daa3\n"
  },
  {
    "path": "util/env-bootstrap.sh",
    "content": "#!/usr/bin/env sh\n# Copyright 2025 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\n\n################################################################################\n# This script will install the QMK CLI, toolchains, and flashing utilities.\n################################################################################\n# Environment variables:\n#   CONFIRM: Skip the pre-install delay. (or: --confirm)\n#   QMK_DISTRIB_DIR: The directory to install the QMK distribution to. (or: --qmk-distrib-dir=...)\n#   UV_INSTALL_DIR: The directory to install `uv` to. (or: --uv-install-dir=...)\n#   UV_TOOL_DIR: The directory to install `uv` tools to. (or: --uv-tool-dir=...)\n#   SKIP_CLEAN: Skip cleaning the distribution directory. (or: --skip-clean)\n#   SKIP_PACKAGE_MANAGER: Skip installing the necessary packages for the package manager. (or: --skip-package-manager)\n#   SKIP_UV: Skip installing `uv`. (or: --skip-uv)\n#   SKIP_QMK_CLI: Skip installing the QMK CLI. (or: --skip-qmk-cli)\n#   SKIP_QMK_TOOLCHAINS: Skip installing the QMK toolchains. (or: --skip-qmk-toolchains)\n#   SKIP_QMK_FLASHUTILS: Skip installing the QMK flashing utilities. (or: --skip-qmk-flashutils)\n#   SKIP_UDEV_RULES: Skip installing the udev rules for Linux. (or: --skip-udev-rules)\n#   SKIP_WINDOWS_DRIVERS: Skip installing the Windows drivers for the flashing utilities. (or: --skip-windows-drivers)\n#\n# Arguments above may be negated by prefixing with `--no-` instead (e.g. `--no-skip-clean`).\n################################################################################\n# Usage:\n#   curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh\n#\n# Help:\n#   curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh -s -- --help\n#\n# An example which skips installing `uv` using environment variables:\n#   curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | SKIP_UV=1 sh\n#\n# ...or by using command line arguments:\n#   curl -fsSL https://raw.githubusercontent.com/qmk/qmk_firmware/master/util/env-bootstrap.sh | sh -s -- --skip-uv\n#\n# Any other configurable items listed above may be specified in the same way.\n################################################################################\n\n{ # this ensures the entire script is downloaded #\n    set -eu\n\n    BOOTSTRAP_TMPDIR=\"$(mktemp -d /tmp/qmk-bootstrap-failure.XXXXXX)\"\n    trap 'rm -rf \"$BOOTSTRAP_TMPDIR\" >/dev/null 2>&1 || true' EXIT\n    FAILURE_FILE=\"${BOOTSTRAP_TMPDIR}/fail\"\n\n    # Work out which `sed` to use\n    command -v gsed >/dev/null 2>&1 && SED=gsed || SED=sed\n\n    script_args() {\n        cat <<__EOT__\n    --help                    -- Shows this help text\n    --confirm                 -- Skips the delay before installation\n    --uv-install-dir={path}   -- The directory to install \\`uv\\` into\n    --uv-tool-dir={path}      -- The directory to install \\`uv\\` tools into\n    --qmk-distrib-dir={path}  -- The directory to install the QMK distribution into\n    --skip-clean              -- Skip cleaning the QMK distribution directory\n    --skip-package-manager    -- Skip installing the necessary packages for the package manager\n    --skip-uv                 -- Skip installing \\`uv\\`\n    --skip-qmk-cli            -- Skip installing the QMK CLI\n    --skip-qmk-toolchains     -- Skip installing the QMK toolchains\n    --skip-qmk-flashutils     -- Skip installing the QMK flashing utilities\n    --skip-udev-rules         -- Skip installing the udev rules for Linux\n    --skip-windows-drivers    -- Skip installing the Windows drivers for the flashing utilities\n__EOT__\n    # Hidden:\n    # --wsl-install           -- Installs the WSL variant of qmk_flashutils\n    }\n\n    signal_execution_failure() {\n        touch \"$FAILURE_FILE\" >/dev/null 2>&1 || true\n    }\n\n    exit_if_execution_failed() {\n        if [ -e \"$FAILURE_FILE\" ]; then\n            exit 1\n        fi\n    }\n\n    script_help() {\n        echo \"$(basename ${this_script:-qmk-install.sh}) $(script_args | sort | ${SED} -e 's@^\\s*@@g' -e 's@\\s\\+--.*@@g' -e 's@^@[@' -e 's@$@]@' | tr '\\n' ' ')\"\n        echo\n        echo \"Arguments:\"\n        script_args\n        echo\n        echo \"Switch arguments may be negated by prefixing with '--no-' (e.g. '--no-skip-clean').\"\n    }\n\n    script_parse_args() {\n        local N\n        local V\n        while [ ! -z \"${1:-}\" ]; do\n            case \"$1\" in\n            --help)\n                script_help\n                exit 0\n                ;;\n            --*=*)\n                N=${1%%=*}\n                N=${N##--}\n                N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z')\n                V=${1##*=}\n                export $N=\"$V\"\n                ;;\n            --no-*)\n                N=${1##--no-}\n                N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z')\n                unset $N\n                ;;\n            --*)\n                N=${1##--}\n                N=$(echo $N | tr '-' '_' | tr 'a-z' 'A-Z')\n                export $N=true\n                ;;\n            *)\n                echo \"Unknown argument: '$1'\" >&2\n                echo\n                script_help >&2\n                exit 1\n                ;;\n            esac\n            shift\n            unset N\n            unset V\n        done\n    }\n\n    nsudo() {\n        if [ \"$(fn_os)\" = \"windows\" ]; then\n            # No need for sudo under QMK MSYS\n            return\n        elif [ $(id -u) -ne 0 ]; then\n            if [ -n \"$(command -v sudo 2>/dev/null || true)\" ]; then\n                echo \"sudo\"\n            elif [ -n \"$(command -v doas 2>/dev/null || true)\" ]; then\n                echo \"doas\"\n            else\n                echo \"Please install 'sudo' or 'doas' to continue.\" >&2\n                exit 1\n            fi\n        fi\n        true\n    }\n\n    download_url() {\n        local url=$1\n        local filename=${2:-$(basename \"$url\")}\n        local quiet=''\n        if [ -n \"$(command -v curl 2>/dev/null || true)\" ]; then\n            [ \"$filename\" = \"-\" ] && quiet='-s' || echo \"Downloading '$url' => '$filename'\" >&2\n            curl -LSf $quiet -o \"$filename\" \"$url\"\n        elif [ -n \"$(command -v wget 2>/dev/null || true)\" ]; then\n            [ \"$filename\" = \"-\" ] && quiet='-q' || echo \"Downloading '$url' => '$filename'\" >&2\n            wget $quiet \"-O$filename\" \"$url\"\n        else\n            echo \"Please install 'curl' to continue.\" >&2\n            exit 1\n        fi\n    }\n\n    github_api_call() {\n        local url=\"$1\"\n        local token=\"${GITHUB_TOKEN:-${GH_TOKEN:-}}\"\n        if [ -n \"${token:-}\" ]; then\n            if [ -n \"$(command -v curl 2>/dev/null || true)\" ]; then\n                curl -fsSL -H \"Authorization: token $token\" -H \"Accept: application/vnd.github.v3+json\" \"https://api.github.com/$url\"\n            elif [ -n \"$(command -v wget 2>/dev/null || true)\" ]; then\n                wget -q --header=\"Authorization: token $token\" --header=\"Accept: application/vnd.github.v3+json\" \"https://api.github.com/$url\" -O -\n            fi\n        else\n            download_url \"https://api.github.com/$url\" -\n        fi\n    }\n\n    fn_os() {\n        local os_name=$(echo ${1:-} | tr 'A-Z' 'a-z')\n        if [ -z \"$os_name\" ]; then\n            os_name=$(uname -s | tr 'A-Z' 'a-z')\n        fi\n        case \"$os_name\" in\n        *darwin* | *macos* | *apple*)\n            echo macos\n            ;;\n        *windows* | *mingw* | *msys*)\n            echo windows\n            ;;\n        *linux*)\n            echo linux\n            ;;\n        *)\n            echo unknown\n            ;;\n        esac\n    }\n\n    fn_arch() {\n        local arch_name=$(echo ${1:-} | tr 'A-Z' 'a-z')\n        if [ -z \"$arch_name\" ]; then\n            arch_name=$(uname -m | tr 'A-Z' 'a-z')\n        fi\n        case \"$arch_name\" in\n        *arm64* | *aarch64*)\n            echo ARM64\n            ;;\n        *riscv64*)\n            echo RV64\n            ;;\n        *x86_64* | *x64*)\n            echo X64\n            ;;\n        *)\n            echo unknown\n            ;;\n        esac\n    }\n\n    preinstall_delay() {\n        [ -z \"${CONFIRM:-}\" ] || return 0\n        echo >&2\n        echo \"Waiting 10 seconds before proceeding. Press Ctrl+C to cancel installation.\" >&2\n        sleep 10\n    }\n\n    get_package_manager_deps() {\n        case $(fn_os) in\n        macos) echo \"zstd clang-format make hidapi libusb dos2unix git\" ;;\n        windows) echo \"base-devel: zstd:p toolchain:p clang:p hidapi:p dos2unix: git: unzip:\" ;;\n        linux)\n            case $(grep ID /etc/os-release) in\n            *arch* | *manjaro* | *cachyos*) echo \"zstd base-devel clang diffutils wget unzip zip hidapi dos2unix git\" ;;\n            *debian* | *ubuntu*) echo \"zstd build-essential clang-format diffutils wget unzip zip libhidapi-hidraw0 dos2unix git\" ;;\n            *fedora*) echo \"zstd clang diffutils which gcc git wget unzip zip hidapi dos2unix libusb-devel libusb1-devel libusb-compat-0.1-devel libusb0-devel git epel-release\" ;;\n            *suse*) echo \"zstd clang diffutils wget unzip zip libhidapi-hidraw0 dos2unix git libusb-1_0-devel gzip which\" ;;\n            *gentoo*) echo \"zstd sys-apps/diffutils wget unzip zip dev-libs/hidapi dos2unix dev-vcs/git dev-libs/libusb app-arch/gzip which\" ;;\n            *)\n                echo >&2\n                echo \"Sorry, we don't recognize your distribution.\" >&2\n                echo >&2\n                echo \"Proceeding with the installation, however you will need to install at least the following tools manually:\" >&2\n                echo \"  - make, git, curl, zstd, unzip, [lib]hidapi\" >&2\n                echo \"Other tools may be required depending on your distribution.\" >&2\n                echo >&2\n                echo \"Alternatively, if you prefer Docker, try using the docker image instead:\" >&2\n                echo \"  - https://docs.qmk.fm/#/getting_started_docker\" >&2\n                ;;\n            esac\n            ;;\n        *)\n            # We can only really support macOS, Windows, and Linux at this time due to `uv` requirements.\n            echo >&2\n            echo \"Sorry, we don't recognize your OS. Try using a compatible OS instead:\" >&2\n            echo \"  - https://docs.qmk.fm/newbs_getting_started#set-up-your-environment\" >&2\n            echo >&2\n            echo \"If you cannot use a compatible OS, you can try installing the \\`qmk\\` Python package manually using \\`pip\\`, most likely requiring a virtual environment:\" >&2\n            echo \"  % python3 -m pip install qmk\" >&2\n            echo >&2\n            echo \"All other dependencies will need to be installed manually, such as make, git, AVR and ARM toolchains, and associated flashing utilities.\" >&2\n            echo >&2\n            echo \"**NOTE**: QMK does not provide official support for your environment. Here be dragons, you are on your own.\" >&2\n            signal_execution_failure\n            ;;\n        esac\n    }\n\n    print_package_manager_deps_and_delay() {\n        get_package_manager_deps | tr ' ' '\\n' | sort | xargs -I'{}' echo \"    - {}\" >&2\n        exit_if_execution_failed\n        preinstall_delay || exit 1\n    }\n\n    install_package_manager_deps() {\n        # Install the necessary packages for the package manager\n        case $(fn_os) in\n        macos)\n            if [ -n \"$(command -v brew 2>/dev/null || true)\" ]; then\n                echo \"It will also install the following system packages using 'brew':\" >&2\n                print_package_manager_deps_and_delay\n\n                brew update\n\n                local existing=\"\"\n                local new=\"\"\n                for dep in $(get_package_manager_deps); do\n                if brew list --formula | grep -q \"^${dep}\\$\"; then\n                    existing=\"${existing:-} $dep\"\n                else\n                    new=\"${new:-} $dep\"\n                fi\n                done\n\n                if [ -n \"${existing:-}\" ]; then\n                    brew upgrade $existing\n                fi\n                if [ -n \"${new:-}\" ]; then\n                    brew install $new\n                fi\n            else\n                echo \"Please install 'brew' to continue. See https://brew.sh/ for more information.\" >&2\n                exit 1\n            fi\n            ;;\n        windows)\n            echo \"It will also install the following packages using 'pacman'/'pacboy':\" >&2\n            print_package_manager_deps_and_delay\n            $(nsudo) pacman --needed --noconfirm --disable-download-timeout -S pactoys\n            $(nsudo) pacboy sync --needed --noconfirm --disable-download-timeout $(get_package_manager_deps)\n            ;;\n        linux)\n            case $(grep ID /etc/os-release) in\n            *arch* | *manjaro* | *cachyos*)\n                echo \"It will also install the following system packages using 'pacman':\" >&2\n                print_package_manager_deps_and_delay\n                $(nsudo) pacman --needed --noconfirm -S $(get_package_manager_deps)\n                ;;\n            *debian* | *ubuntu*)\n                echo \"It will also install the following system packages using 'apt':\" >&2\n                print_package_manager_deps_and_delay\n                $(nsudo) apt-get update\n                DEBIAN_FRONTEND=noninteractive \\\n                    $(nsudo) apt-get --quiet --yes install $(get_package_manager_deps)\n                ;;\n            *fedora*)\n                echo \"It will also install the following system packages using 'dnf':\" >&2\n                print_package_manager_deps_and_delay\n                # Some RHEL-likes need EPEL for hidapi\n                $(nsudo) dnf -y install epel-release 2>/dev/null || true\n                # RHEL-likes have some naming differences in libusb packages, so manually handle those\n                $(nsudo) dnf -y install $(get_package_manager_deps | tr ' ' '\\n' | grep -v 'epel-release' | grep -v libusb | tr '\\n' ' ')\n                for pkg in $(get_package_manager_deps | tr ' ' '\\n' | grep libusb); do\n                    $(nsudo) dnf -y install \"$pkg\" 2>/dev/null || true\n                done\n                ;;\n            *opensuse* | *suse*)\n                echo \"It will also install development tools as well as the following system packages using 'zypper':\" >&2\n                print_package_manager_deps_and_delay\n                $(nsudo) zypper --non-interactive refresh\n                $(nsudo) zypper --non-interactive install -t pattern devel_basis devel_C_C++\n                $(nsudo) zypper --non-interactive install $(get_package_manager_deps)\n                ;;\n            *gentoo*)\n                echo \"It will also install the following system packages using 'emerge':\" >&2\n                print_package_manager_deps_and_delay\n                $(nsudo) emerge --sync\n                $(nsudo) emerge --noreplace --ask=n $(get_package_manager_deps | tr ' ' '\\n') || signal_execution_failure\n                exit_if_execution_failed\n                ;;\n            *)\n                print_package_manager_deps_and_delay\n                echo \"Proceeding with the installation, you will need to ensure prerequisites are installed.\" >&2\n                ;;\n            esac\n            ;;\n        *)\n            print_package_manager_deps_and_delay\n            ;;\n        esac\n    }\n\n    install_uv() {\n        # Install `uv` (or update as necessary)\n        download_url https://astral.sh/uv/install.sh - | TMPDIR=\"$(windows_ish_path \"${TMPDIR:-}\")\" UV_INSTALL_DIR=\"$(windows_ish_path \"${UV_INSTALL_DIR:-}\")\" sh\n    }\n\n    setup_paths() {\n        # Set up the paths for any of the locations `uv` expects\n        if [ -n \"${XDG_BIN_HOME:-}\" ]; then\n            export PATH=\"$XDG_BIN_HOME:$PATH\"\n        fi\n        if [ -n \"${XDG_DATA_HOME:-}\" ]; then\n            export PATH=\"$XDG_DATA_HOME/../bin:$PATH\"\n        fi\n        [ ! -d \"$HOME/.local/bin\" ] || export PATH=\"$HOME/.local/bin:$PATH\"\n\n        if [ -n \"${UV_INSTALL_DIR:-}\" ]; then\n            export PATH=\"$UV_INSTALL_DIR/bin:$UV_INSTALL_DIR:$PATH\" # cater for both \"flat\" and \"hierarchical\" installs of `uv`\n        fi\n\n        if [ -n \"${UV_TOOL_BIN_DIR:-}\" ]; then\n            export PATH=\"$UV_TOOL_BIN_DIR:$PATH\"\n        fi\n    }\n\n    uv_command() {\n        if [ \"$(fn_os)\" = \"windows\" ]; then\n            UV_TOOL_DIR=\"$(windows_ish_path \"${UV_TOOL_DIR:-}\")\" \\\n            UV_TOOL_BIN_DIR=\"$(windows_ish_path \"${UV_TOOL_BIN_DIR:-}\")\" \\\n            uv \"$@\"\n        else\n            uv \"$@\"\n        fi\n    }\n\n    install_qmk_cli() {\n        # Install the QMK CLI\n        uv_command tool install --force --with pip --upgrade --python $PYTHON_TARGET_VERSION qmk\n\n        # QMK is installed to...\n        local qmk_tooldir=\"$(posix_ish_path \"$(uv_command tool dir)/qmk\")\"\n\n        # Activate the environment\n        if [ -e \"$qmk_tooldir/bin\" ]; then\n            . \"$qmk_tooldir/bin/activate\"\n        elif [ -e \"$qmk_tooldir/Scripts\" ]; then\n            . \"$qmk_tooldir/Scripts/activate\"\n        else\n            echo \"Could not find the QMK environment to activate.\" >&2\n            exit 1\n        fi\n\n        # Install the QMK dependencies\n        uv_command pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements.txt\n        uv_command pip install --upgrade -r https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/requirements-dev.txt\n\n        # Deactivate the environment\n        deactivate\n    }\n\n    install_toolchains() {\n        # Get the latest toolchain release from https://github.com/qmk/qmk_toolchains\n        local latest_toolchains_release=$(github_api_call repos/qmk/qmk_toolchains/releases/latest - | grep -oE '\"tag_name\": \"[^\"]+' | grep -oE '[^\"]+$')\n        # Download the specific release asset with a matching keyword\n        local toolchain_url=$(github_api_call repos/qmk/qmk_toolchains/releases/tags/$latest_toolchains_release - | grep -oE '\"browser_download_url\": \"[^\"]+\"' | grep -oE 'https://[^\"]+' | grep $(fn_os)$(fn_arch))\n        if [ -z \"$toolchain_url\" ]; then\n            echo \"No toolchain found for this OS/Arch combination.\" >&2\n            exit 1\n        fi\n\n        # Download the toolchain release to the toolchains location\n        echo \"Downloading compiler toolchains...\" >&2\n        local target_file=\"$QMK_DISTRIB_DIR/$(basename \"$toolchain_url\")\"\n        download_url \"$toolchain_url\" \"$target_file\"\n\n        # Extract the toolchain\n        echo \"Extracting compiler toolchains to '$QMK_DISTRIB_DIR'...\" >&2\n        zstdcat \"$target_file\" | tar xf - -C \"$QMK_DISTRIB_DIR\" --strip-components=1\n    }\n\n    install_flashing_tools() {\n        local osarchvariant=\"$(fn_os)$(fn_arch)\"\n\n        # Special case for WSL\n        if [ -n \"${WSL_INSTALL:-}\" ] ||  [ -n \"${WSL_DISTRO_NAME:-}\" ] || [ -f /proc/sys/fs/binfmt_misc/WSLInterop ]; then\n            osarchvariant=\"windowsWSL\"\n        fi\n\n        # Get the latest flashing tools release from https://github.com/qmk/qmk_flashutils\n        local latest_flashutils_release=$(github_api_call repos/qmk/qmk_flashutils/releases/latest - | grep -oE '\"tag_name\": \"[^\"]+' | grep -oE '[^\"]+$')\n        # Download the specific release asset with a matching keyword\n        local flashutils_url=$(github_api_call repos/qmk/qmk_flashutils/releases/tags/$latest_flashutils_release - | grep -oE '\"browser_download_url\": \"[^\"]+\"' | grep -oE 'https://[^\"]+' | grep \"$osarchvariant\")\n        if [ -z \"$flashutils_url\" ]; then\n            echo \"No flashing tools found for this OS/Arch combination.\" >&2\n            exit 1\n        fi\n\n        # Download the flashing tools release to the toolchains location\n        echo \"Downloading flashing tools...\" >&2\n        local target_file=\"$QMK_DISTRIB_DIR/$(basename \"$flashutils_url\")\"\n        download_url \"$flashutils_url\" \"$target_file\"\n\n        # Extract the flashing tools\n        echo \"Extracting flashing tools to '$QMK_DISTRIB_DIR'...\" >&2\n        zstdcat \"$target_file\" | tar xf - -C \"$QMK_DISTRIB_DIR/bin\"\n        # Move the release file to etc\n        mv \"$QMK_DISTRIB_DIR/bin/flashutils_release\"* \"$QMK_DISTRIB_DIR/etc\"\n    }\n\n    install_linux_udev_rules() {\n        # Download the udev rules to the toolchains location\n        echo \"Downloading QMK udev rules file...\" >&2\n        local qmk_rules_target_file=\"$QMK_DISTRIB_DIR/50-qmk.rules\"\n        download_url \"https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/util/udev/50-qmk.rules\" \"$qmk_rules_target_file\"\n\n        # Install the udev rules -- path list is aligned with qmk doctor's linux.py\n        local udev_rules_paths=\"\n            /usr/lib/udev/rules.d\n            /usr/local/lib/udev/rules.d\n            /run/udev/rules.d\n            /etc/udev/rules.d\n        \"\n        for udev_rules_dir in $udev_rules_paths; do\n            if [ -d \"$udev_rules_dir\" ]; then\n                echo \"Installing udev rules to $udev_rules_dir/50-qmk.rules ...\" >&2\n                $(nsudo) mv \"$qmk_rules_target_file\" \"$udev_rules_dir\"\n                $(nsudo) chown 0:0 \"$udev_rules_dir/50-qmk.rules\"\n                $(nsudo) chmod 644 \"$udev_rules_dir/50-qmk.rules\"\n                break\n            fi\n        done\n\n        # Reload udev rules\n        if command -v udevadm >/dev/null 2>&1; then\n            echo \"Reloading udev rules...\" >&2\n            $(nsudo) udevadm control --reload-rules || true\n            $(nsudo) udevadm trigger || true\n        else\n            echo \"udevadm not found, skipping udev rules reload.\" >&2\n        fi\n    }\n\n    install_windows_drivers() {\n        # Get the latest driver installer release from https://github.com/qmk/qmk_driver_installer\n        local latest_driver_installer_release=$(github_api_call repos/qmk/qmk_driver_installer/releases/latest - | grep -oE '\"tag_name\": \"[^\"]+' | grep -oE '[^\"]+$')\n        # Download the specific release asset\n        local driver_installer_url=$(github_api_call repos/qmk/qmk_driver_installer/releases/tags/$latest_driver_installer_release - | grep -oE '\"browser_download_url\": \"[^\"]+\"' | grep -oE 'https://[^\"]+' | grep '\\.exe')\n        if [ -z \"$driver_installer_url\" ]; then\n            echo \"No driver installer found.\" >&2\n            exit 1\n        fi\n        # Download the driver installer release to the toolchains location\n        echo \"Downloading driver installer...\" >&2\n        local target_file=\"$QMK_DISTRIB_DIR/$(basename \"$driver_installer_url\")\"\n        download_url \"$driver_installer_url\" \"$target_file\"\n        # Download the drivers list\n        download_url \"https://raw.githubusercontent.com/qmk/qmk_firmware/refs/heads/master/util/drivers.txt\" \"$QMK_DISTRIB_DIR/drivers.txt\"\n        # Execute the driver installer\n        cd \"$QMK_DISTRIB_DIR\"\n        cmd.exe //c \"qmk_driver_installer.exe --all --force drivers.txt\"\n        cd -\n        # Remove the temporary files\n        rm -f \"$QMK_DISTRIB_DIR/qmk_driver_installer.exe\" \"$QMK_DISTRIB_DIR/drivers.txt\" || true\n    }\n\n    clean_tarballs() {\n        # Clean up the tarballs\n        rm -f \"$QMK_DISTRIB_DIR\"/*.tar.zst || true\n    }\n\n    windows_ish_path() {\n        [ -n \"$1\" ] || return 0\n        [ \"$(uname -o 2>/dev/null || true)\" = \"Msys\" ] && cygpath -w \"$1\" || echo \"$1\"\n    }\n\n    posix_ish_path() {\n        [ -n \"$1\" ] || return 0\n        [ \"$(uname -o 2>/dev/null || true)\" = \"Msys\" ] && cygpath -u \"$1\" || echo \"$1\"\n    }\n\n    # Set the Python version we want to use with the QMK CLI\n    export PYTHON_TARGET_VERSION=${PYTHON_TARGET_VERSION:-3.14}\n\n    # Windows/MSYS doesn't like `/tmp` so we need to set a different temporary directory.\n    # Also set the default `UV_INSTALL_DIR` and `QMK_DISTRIB_DIR` to locations which don't pollute the user's home directory, keeping the installation internal to MSYS.\n    if [ \"$(uname -o 2>/dev/null || true)\" = \"Msys\" ]; then\n        export TMPDIR=\"$(posix_ish_path \"$TMP\")\"\n        export UV_INSTALL_DIR=\"$(posix_ish_path \"${UV_INSTALL_DIR:-/opt/uv}\")\"\n        export QMK_DISTRIB_DIR=\"$(posix_ish_path \"${QMK_DISTRIB_DIR:-/opt/qmk}\")\"\n        export UV_TOOL_DIR=\"$(posix_ish_path \"${UV_TOOL_DIR:-\"$UV_INSTALL_DIR/tools\"}\")\"\n        export UV_TOOL_BIN_DIR=\"$(posix_ish_path \"$UV_TOOL_DIR/bin\")\"\n    fi\n\n    script_parse_args \"$@\"\n\n    echo \"This QMK CLI installation script will install \\`uv\\`, the QMK CLI, as well as QMK-supplied toolchains and flashing utilities.\" >&2\n    [ -z \"${SKIP_PACKAGE_MANAGER:-}\" ] || { preinstall_delay || exit 1; }\n    [ -n \"${SKIP_PACKAGE_MANAGER:-}\" ] || install_package_manager_deps\n    [ -n \"${SKIP_UV:-}\" ] || install_uv\n\n    # Make sure the usual `uv` and other associated directories are on the $PATH\n    setup_paths\n\n    # Work out where we want to install the distribution and tools now that `uv` is installed\n    export QMK_DISTRIB_DIR=\"$(posix_ish_path \"${QMK_DISTRIB_DIR:-$(printf 'import platformdirs\\nprint(platformdirs.user_data_dir(\"qmk\"))' | uv_command run --quiet --python $PYTHON_TARGET_VERSION --with platformdirs -)}\")\"\n\n    # Clear out the distrib directory if necessary\n    if [ -z \"${SKIP_CLEAN:-}\" ] || [ -z \"${SKIP_QMK_TOOLCHAINS:-}\" -a -z \"${SKIP_QMK_FLASHUTILS:-}\" ]; then\n        if [ -d \"$QMK_DISTRIB_DIR\" ]; then\n            echo \"Removing old QMK distribution...\" >&2\n            rm -rf \"$QMK_DISTRIB_DIR\"\n        fi\n    fi\n    mkdir -p \"$QMK_DISTRIB_DIR\"\n\n    [ -n \"${SKIP_QMK_CLI:-}\" ] || install_qmk_cli\n    [ -n \"${SKIP_QMK_TOOLCHAINS:-}\" ] || install_toolchains\n    [ -n \"${SKIP_QMK_FLASHUTILS:-}\" ] || install_flashing_tools\n    if [ \"$(uname -s 2>/dev/null || true)\" = \"Linux\" ]; then\n        [ -n \"${SKIP_UDEV_RULES:-}\" ] || install_linux_udev_rules\n    fi\n    if [ \"$(uname -o 2>/dev/null || true)\" = \"Msys\" ]; then\n        [ -n \"${SKIP_WINDOWS_DRIVERS:-}\" ] || install_windows_drivers\n    fi\n    clean_tarballs\n\n    # Notify the user that they may need to restart their shell to get the `qmk` command\n    echo >&2\n    echo \"QMK CLI installation complete.\" >&2\n    echo \"The QMK CLI has been installed to '$(posix_ish_path \"$(dirname \"$(command -v qmk)\")\")'.\" >&2\n    echo \"The QMK CLI venv has been created at '$(posix_ish_path \"$(uv_command tool dir)/qmk\")'.\" >&2\n    echo \"Toolchains and flashing utilities have been installed to '$QMK_DISTRIB_DIR'.\" >&2\n    echo >&2\n    echo \"You may need to restart your shell to gain access to the 'qmk' command.\" >&2\n    echo \"Alternatively, add \"$(posix_ish_path \"$(dirname \"$(command -v qmk)\")\")\" to your \\$PATH:\" >&2\n    echo \"    export PATH=\\\"$(posix_ish_path \"$(dirname \"$(command -v qmk)\")\"):\\$PATH\\\"\" >&2\n\n} # this ensures the entire script is downloaded #\n"
  },
  {
    "path": "util/install/arch.sh",
    "content": "#!/usr/bin/env bash\n\n_qmk_install() {\n    echo \"Installing dependencies\"\n\n    sudo pacman --needed  --noconfirm -S \\\n        base-devel clang diffutils gcc git unzip wget zip python-pip \\\n        avr-binutils arm-none-eabi-binutils arm-none-eabi-gcc \\\n        arm-none-eabi-newlib avrdude dfu-programmer dfu-util \\\n        riscv64-elf-binutils riscv64-elf-gcc riscv64-elf-newlib\n    sudo pacman --needed --noconfirm -U https://archive.archlinux.org/packages/a/avr-gcc/avr-gcc-8.3.0-1-x86_64.pkg.tar.xz\n    sudo pacman --needed --noconfirm -S avr-libc # Must be installed after the above, or it will bring in the latest avr-gcc instead\n\n    sudo pacman --needed  --noconfirm -S hidapi  # This will fail if the community repo isn't enabled\n\n    python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt\n}\n"
  },
  {
    "path": "util/install/debian.sh",
    "content": "#!/usr/bin/env bash\n\nDEBIAN_FRONTEND=noninteractive\nDEBCONF_NONINTERACTIVE_SEEN=true\nexport DEBIAN_FRONTEND DEBCONF_NONINTERACTIVE_SEEN\n\n_qmk_install_prepare() {\n    sudo apt-get update $SKIP_PROMPT\n}\n\n_qmk_install() {\n    echo \"Installing dependencies\"\n\n    sudo apt-get --quiet --yes install \\\n        build-essential clang-format diffutils gcc git unzip wget zip \\\n        python3-pip binutils-avr gcc-avr avr-libc binutils-arm-none-eabi \\\n        gcc-arm-none-eabi libnewlib-arm-none-eabi avrdude dfu-programmer \\\n        dfu-util teensy-loader-cli libhidapi-hidraw0 libusb-dev\n\n    # RISC-V toolchains with picolibc support are only available for distributions based on Debian 11+.\n    if sudo apt-get install --simulate --quiet --yes picolibc-riscv64-unknown-elf gcc-riscv64-unknown-elf binutils-riscv64-unknown-elf > /dev/null 2>&1; then\n        sudo apt-get --quiet --yes install picolibc-riscv64-unknown-elf \\\n            gcc-riscv64-unknown-elf \\\n            binutils-riscv64-unknown-elf\n    fi\n\n    python3 -m pip install --user -r \"$QMK_FIRMWARE_DIR\"/requirements.txt\n}\n"
  },
  {
    "path": "util/install/fedora.sh",
    "content": "#!/usr/bin/env bash\n\n_qmk_install() {\n    echo \"Installing dependencies\"\n\n    . /etc/os-release\n    if [ \"$VERSION_ID\" -ge \"39\" ]; then\n        sudo dnf copr -h >/dev/null 2>&1 || sudo dnf $SKIP_PROMPT install dnf-plugins-core\n        sudo dnf $SKIP_PROMPT copr enable erovia/dfu-programmer\n    fi\n\n    # TODO: Check whether devel/headers packages are really needed\n    sudo dnf $SKIP_PROMPT install \\\n        clang diffutils git gcc glibc-headers kernel-devel kernel-headers \\\n        make unzip wget zip python3 avr-binutils avr-gcc avr-gcc-c++ avr-libc \\\n        arm-none-eabi-binutils-cs arm-none-eabi-gcc-cs arm-none-eabi-gcc-cs-c++ \\\n        arm-none-eabi-newlib avrdude dfu-programmer dfu-util hidapi\n\n    # Handle discrepancies between different Fedora versions\n    sudo dnf $SKIP_PROMPT install libusb-devel \\\n        || sudo dnf $SKIP_PROMPT install libusb1-devel libusb-compat-0.1-devel \\\n        || sudo dnf $SKIP_PROMPT install libusb0-devel\n\n    python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt\n}\n"
  },
  {
    "path": "util/install/freebsd.sh",
    "content": "#!/usr/bin/env bash\n\n_qmk_install_prepare() {\n    sudo pkg update $SKIP_PROMPT\n}\n\n_qmk_install() {\n    echo \"Installing dependencies\"\n\n    sudo pkg install -y \\\n        git wget gmake gcc zip unzip diffutils \\\n        python3 \\\n        avr-binutils avr-gcc avr-libc \\\n        arm-none-eabi-binutils arm-none-eabi-gcc arm-none-eabi-newlib \\\n        avrdude dfu-programmer dfu-util\n\n    sudo python3 -m pip install -r $QMK_FIRMWARE_DIR/requirements.txt\n}\n"
  },
  {
    "path": "util/install/gentoo.sh",
    "content": "#!/usr/bin/env bash\n\n_qmk_install_prepare() {\n    echo \"This script will make a USE change in order to ensure that that QMK works on your system.\"\n    echo \"All changes will be sent to the file /etc/portage/package.use/qmkfirmware -- please review it, and read Portage's output carefully before installing any packages on your system.\"\n    echo \"You will also need to ensure that your kernel is compiled with support for the microcontroller that you are using (e.g. enable Arduino for the Pro Micro). Further information can be found on the Gentoo wiki.\"\n\n    read -p \"Proceed? [y/N] \" res\n    case $res in\n        [Yy]*)\n            return 0;;\n        *)\n            return 1;;\n    esac\n}\n\n_qmk_install() {\n    echo \"Installing dependencies\"\n\n    sudo touch /etc/portage/package.use/qmkfirmware\n    # tee is used here since sudo doesn't apply to >>\n    echo \"sys-devel/gcc multilib\\ncross-arm-none-eabi/newlib nano\" | sudo tee --append /etc/portage/package.use/qmkfirmware >/dev/null\n    sudo emerge -auN sys-devel/gcc\n    sudo emerge -au --noreplace \\\n        app-arch/unzip app-arch/zip net-misc/wget llvm-core/clang \\\n        sys-devel/crossdev \\>=dev-lang/python-3.7 dev-embedded/avrdude \\\n        dev-embedded/dfu-programmer app-mobilephone/dfu-util sys-apps/hwloc \\\n        dev-libs/hidapi\n\n    sudo crossdev -s4 --stable --g \\<9 --portage --verbose --target avr\n    sudo crossdev -s4 --stable --g \\<9 --portage --verbose --target arm-none-eabi\n\n    python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt\n}\n"
  },
  {
    "path": "util/install/linux_shared.sh",
    "content": "#!/usr/bin/env bash\n\n# For those distros that do not package bootloadHID\n_qmk_install_bootloadhid() {\n    if ! command -v bootloadHID > /dev/null; then\n        wget https://www.obdev.at/downloads/vusb/bootloadHID.2012-12-08.tar.gz -O - | tar -xz -C /tmp\n        pushd /tmp/bootloadHID.2012-12-08/commandline/ > /dev/null\n        if make; then\n            sudo cp bootloadHID /usr/local/bin\n        fi\n        popd > /dev/null\n    fi\n}\n"
  },
  {
    "path": "util/install/macos.sh",
    "content": "#!/usr/bin/env bash\n\n_qmk_install_prepare() {\n    echo \"Checking Homebrew installation\"\n\n    if ! brew --version >/dev/null 2>&1; then\n        echo \"Error! Homebrew is broken or not installed.\"\n        echo \"Please run \\`brew doctor\\` or follow the installation instructions at https://brew.sh/, then re-run this script.\"\n        return 1\n    fi\n\n    # Conflicts with arm-none-eabi toolchain from osx-cross\n    brew uninstall --ignore-dependencies --cask gcc-arm-embedded >/dev/null 2>&1\n    brew uninstall --ignore-dependencies homebrew/core/arm-none-eabi-gcc >/dev/null 2>&1\n    brew uninstall --ignore-dependencies homebrew/core/arm-none-eabi-binutils >/dev/null 2>&1\n    brew uninstall --ignore-dependencies osx-cross/arm/arm-gcc-bin@8 >/dev/null 2>&1\n\n    brew update && brew upgrade --formulae\n}\n\n_qmk_install() {\n    echo \"Installing dependencies\"\n\n    # All macOS & Python dependencies are managed in the Homebrew package:\n    #   https://github.com/qmk/homebrew-qmk\n    brew install qmk/qmk/qmk\n\n    # Keg-only, so need to be manually linked\n    brew link --force avr-gcc@8\n    brew link --force arm-none-eabi-binutils\n    brew link --force arm-none-eabi-gcc@8\n}\n"
  },
  {
    "path": "util/install/msys2.sh",
    "content": "#!/usr/bin/env bash\n\n_qmk_install_prepare() {\n    pacman -Syu $MSYS2_CONFIRM\n}\n\n_qmk_install() {\n    echo \"Installing dependencies\"\n\n    pacman --needed --noconfirm --disable-download-timeout -S pactoys\n    pacboy sync --needed --noconfirm --disable-download-timeout \\\n        base-devel: toolchain:x clang:x python-qmk:x hidapi:x \\\n        avr-binutils:x avr-gcc:x avr-libc:x \\\n        arm-none-eabi-binutils:x arm-none-eabi-gcc:x arm-none-eabi-newlib:x \\\n        avrdude:x bootloadhid:x dfu-programmer:x dfu-util:x hid-bootloader-cli:x mdloader:x teensy-loader-cli:x wb32-dfu-updater:x\n\n    _qmk_install_drivers\n}\n\n_qmk_install_drivers() {\n    echo \"Installing drivers\"\n\n    tmpdir=$(mktemp -d)\n    cp \"$QMK_FIRMWARE_UTIL_DIR/drivers.txt\" $tmpdir\n    pushd $tmpdir > /dev/null\n\n    wget \"https://github.com/qmk/qmk_driver_installer/releases/download/v1.01/qmk_driver_installer.exe\"\n\n    cmd.exe //c \"qmk_driver_installer.exe --all --force drivers.txt\"\n\n    popd > /dev/null\n    rm -r $tmpdir\n}\n"
  },
  {
    "path": "util/install/slackware.sh",
    "content": "#!/usr/bin/env bash\n\n_qmk_install_prepare() {\n    echo \"Before you continue, please ensure that your user is added to sudoers and that sboinstall is configured.\"\n    read -p \"Proceed? [y/N] \" res\n\n    case $res in\n        [Yy]*)\n            ;;\n        *)\n            return 1;;\n    esac\n}\n\n_qmk_install() {\n    echo \"Installing dependencies\"\n\n    sudo sboinstall \\\n        avr-binutils avr-gcc avr-libc \\\n        arm-binutils arm-gcc newlib \\\n        python3 \\\n        avrdude dfu-programmer dfu-util teensy_loader_cli\n\n    python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt\n}\n"
  },
  {
    "path": "util/install/solus.sh",
    "content": "#!/usr/bin/env bash\n\n_qmk_install_prepare() {\n    sudo eopkg -y update-repo\n    sudo eopkg -y upgrade\n}\n\n_qmk_install() {\n    echo \"Installing dependencies\"\n\n    sudo eopkg -y install \\\n        -c system.devel git wget zip unzip \\\n        python3 \\\n        avr-binutils avr-gcc avr-libc \\\n        arm-none-eabi-binutils arm-none-eabi-gcc arm-none-eabi-newlib \\\n        avrdude dfu-programmer dfu-util\n\n    python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt\n}\n"
  },
  {
    "path": "util/install/void.sh",
    "content": "#!/usr/bin/env bash\n\n_qmk_install() {\n    echo \"Installing dependencies\"\n\n    sudo xbps-install $SKIP_PROMPT \\\n        gcc git make wget unzip zip \\\n        python3-pip \\\n        avr-binutils avr-gcc avr-libc \\\n        cross-arm-none-eabi-binutils cross-arm-none-eabi-gcc cross-arm-none-eabi-newlib \\\n        avrdude dfu-programmer dfu-util teensy_loader_cli \\\n        libusb-compat-devel\n\n    python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt\n}\n"
  },
  {
    "path": "util/list-moved-keyboards.sh",
    "content": "#!/usr/bin/env bash\n\necho \"This might take a while... let it sit.\" >&2\n\ngit cherry upstream/master upstream/develop | awk '{print $2}' | while read sha1; do\n    git diff --name-status -M25 ${sha1}^..${sha1} | grep 'keyboards/' | grep '^R' | grep -E '(info|keyboard).json'\ndone | sed -e 's@keyboards/@@g' -e 's@/info.json@@g' -e 's@/keyboard.json@@g' | sort -k+2 | while IFS=$'\\n' read line; do\n    if [[ $(echo $line | awk '{print $2}') != $(echo $line | awk '{print $3}') ]]; then\n        echo $line\n    fi\ndone | sort -k+2 | awk '{printf \"| %s | %s |\\n\", $2, $3}' | column -t | sort\n"
  },
  {
    "path": "util/polling_rate.py",
    "content": "#!/usr/bin/env python3\n\nimport usb\n\nUSB_INTERFACE_CLASS_HID = 0x03\n\ndef usb_device_spec(spec):\n    major = spec >> 8\n    minor = (spec >> 4) & 0xF\n    return f\"{major}.{minor}\"\n\ndef usb_device_speed(speed):\n    if speed == 1:\n        return \"Low-speed\"\n    elif speed == 2:\n        return \"Full-speed\"\n    elif speed == 3:\n        return \"High-speed\"\n    elif speed == 4:\n        return \"SuperSpeed\"\n    elif speed == 5:\n        return \"SuperSpeed+\"\n\n    return \"Speed unknown\"\n\ndef usb_hid_interface_subclass(subclass):\n    if subclass == 0x00:\n        return \"None\"\n    elif subclass == 0x01:\n        return \"Boot\"\n    else:\n        return f\"Unknown (0x{subclass:02X})\"\n\ndef usb_hid_interface_protocol(subclass, protocol):\n    if subclass == 0x00 and protocol == 0x00:\n        return \"None\"\n    elif subclass == 0x01:\n        if protocol == 0x00:\n            return \"None\"\n        elif protocol == 0x01:\n            return \"Keyboard\"\n        elif protocol == 0x02:\n            return \"Mouse\"\n\n    return f\"Unknown (0x{protocol:02X})\"\n\ndef usb_interface_polling_rate(speed, interval):\n    if speed >= 3:\n        return f\"{interval * 125} μs ({8000 // interval} Hz)\"\n    else:\n        return f\"{interval} ms ({1000 // interval} Hz)\"\n\nif __name__ == '__main__':\n    devices = usb.core.find(find_all=True)\n\n    for device in devices:\n        try:\n            configuration = device.get_active_configuration()\n        except NotImplementedError:\n            continue\n\n        hid_interfaces = []\n        for interface in configuration.interfaces():\n            if interface.bInterfaceClass == USB_INTERFACE_CLASS_HID:\n                hid_interfaces.append(interface)\n\n        if len(hid_interfaces) > 0:\n            print(f\"{device.manufacturer} {device.product} ({device.idVendor:04X}:{device.idProduct:04X}:{device.bcdDevice:04X}), {usb_device_spec(device.bcdUSB)} {usb_device_speed(device.speed)}\")\n\n            for interface in hid_interfaces:\n                print(f\"└─ HID Interface {interface.bInterfaceNumber}\")\n                subclass = interface.bInterfaceSubClass\n                protocol = interface.bInterfaceProtocol\n                print(f\"   ├─ Subclass: {usb_hid_interface_subclass(subclass)}\")\n                print(f\"   ├─ Protocol: {usb_hid_interface_protocol(subclass, protocol)}\")\n\n                for endpoint in interface.endpoints():\n                    endpoint_address = endpoint.bEndpointAddress & 0xF\n                    endpoint_direction = \"IN\" if endpoint.bEndpointAddress & 0x80 else \"OUT\"\n                    print(f\"   └─ Endpoint {endpoint_address} {endpoint_direction}\")\n                    print(f\"      ├─ Endpoint Size: {endpoint.wMaxPacketSize} bytes\")\n                    print(f\"      └─ Polling Rate: {usb_interface_polling_rate(device.speed, endpoint.bInterval)}\")\n"
  },
  {
    "path": "util/qmk_install.sh",
    "content": "#!/usr/bin/env bash\n\nQMK_FIRMWARE_DIR=$(cd -P -- \"$(dirname -- \"$0\")/..\" >/dev/null && pwd -P)\nQMK_FIRMWARE_UTIL_DIR=$QMK_FIRMWARE_DIR/util\nif [ \"$1\" = \"-y\" ]; then\n    SKIP_PROMPT='-y'\n    MSYS2_CONFIRM='--noconfirm'\nelse\n    SKIP_PROMPT=''\n    MSYS2_CONFIRM=''\nfi\n\ncase $(uname -a) in\n    *Darwin*)\n        . \"$QMK_FIRMWARE_UTIL_DIR/install/macos.sh\";;\n    *FreeBSD*)\n        . \"$QMK_FIRMWARE_UTIL_DIR/install/freebsd.sh\";;\n    *MINGW64_NT*)\n        . \"$QMK_FIRMWARE_UTIL_DIR/install/msys2.sh\";;\n    *MSYS_NT*|*MINGW32_NT*)\n        echo \"Please open a MinGW64 terminal window and re-run this script.\"\n        exit 1;;\n    *Linux*)\n        . \"$QMK_FIRMWARE_UTIL_DIR/install/linux_shared.sh\"\n\n        case $(grep ID /etc/os-release) in\n            *arch*|*manjaro*|*cachyos*)\n                . \"$QMK_FIRMWARE_UTIL_DIR/install/arch.sh\";;\n            *debian*|*ubuntu*)\n                . \"$QMK_FIRMWARE_UTIL_DIR/install/debian.sh\";;\n            *fedora*)\n                . \"$QMK_FIRMWARE_UTIL_DIR/install/fedora.sh\";;\n            *gentoo*)\n                . \"$QMK_FIRMWARE_UTIL_DIR/install/gentoo.sh\";;\n            *slackware*)\n                . \"$QMK_FIRMWARE_UTIL_DIR/install/slackware.sh\";;\n            *solus*)\n                . \"$QMK_FIRMWARE_UTIL_DIR/install/solus.sh\";;\n            *void*)\n                . \"$QMK_FIRMWARE_UTIL_DIR/install/void.sh\";;\n            *)\n                echo \"Sorry, we don't recognize your distribution. Try using the docker image instead:\"\n                echo\n                echo \"https://docs.qmk.fm/#/getting_started_docker\"\n                exit 1;;\n        esac\n\n        if uname -a | grep -qi microsoft; then\n            echo \"********************************************************************************\"\n            echo \"* Detected Windows Subsystem for Linux.                                        *\"\n            echo \"* Currently, WSL has no access to USB devices and so flashing from within the  *\"\n            echo \"* WSL terminal will not work.                                                  *\"\n            echo \"*                                                                              *\"\n            echo \"* Please install the QMK Toolbox instead:                                      *\"\n            echo \"*    https://github.com/qmk/qmk_toolbox/releases                               *\"\n            echo \"********************************************************************************\"\n            echo\n        fi\n        ;;\n    *)\n        echo \"Sorry, we don't recognize your environment. Help us by contributing support!\"\n        echo\n        echo \"https://docs.qmk.fm/#/contributing\"\n        exit 1;;\nesac\n\nif type _qmk_install_prepare &>/dev/null; then\n    _qmk_install_prepare || exit 1\nfi\n\n_qmk_install\n\nif type _qmk_install_bootloadhid &>/dev/null; then\n    _qmk_install_bootloadhid\nfi\n"
  },
  {
    "path": "util/qmk_tab_complete.sh",
    "content": "# Register qmk with tab completion\neval \"$(register-python-argcomplete --no-defaults qmk)\"\n"
  },
  {
    "path": "util/regen.sh",
    "content": "#!/bin/bash\nset -e\n\nqmk generate-rgb-breathe-table -o quantum/rgblight/rgblight_breathe_table.h\nqmk generate-keycodes --version latest -o quantum/keycodes.h\n\nfor lang in $(find data/constants/keycodes/extras/ -type f -exec basename '{}' \\; | sed \"s/keycodes_\\(.*\\)_[0-9].*/\\1/\"); do\n  qmk generate-keycode-extras --version latest --lang $lang -o quantum/keymap_extras/keymap_$lang.h\ndone\n"
  },
  {
    "path": "util/rules_cleaner.sh",
    "content": "#!/usr/bin/env bash\n\n# This script finds all rules.mk files in keyboards/ subdirectories,\n# and deletes the build option filesize impacts from them.\n\n# Print an error message with the word \"ERROR\" in red.\necho_error() {\n    echo -e \"[\\033[0;91mERROR\\033[m]: $1\"\n}\n\n# If we've been started from util/, we want to be in qmk_firmware/\n[[ \"$PWD\" == *util ]] && cd ..\n\n# The root qmk_firmware/ directory should have a subdirectory called quantum/\nif [ ! -d \"quantum\" ]; then\n    echo_error \"Could not detect the QMK firmware directory!\"\n    echo_error \"Are you sure you're in the right place?\"\n    exit 1\nfi\n\n# Set the inplace editing parameter for sed.\n# macOS/BSD sed expects a file extension immediately following -i.\nset_sed_i() {\n    sed_i=(-i)\n\n    case $(uname -a) in\n        *Darwin*) sed_i=(-i \"\")\n    esac\n}\nset_sed_i\n\n# Exclude keyamps/ directories\nfiles=$(find keyboards -type f -name 'rules.mk' -not \\( -path '*/keymaps*' -prune \\))\n\n# Edit rules.mk files \nfor file in $files; do\n  sed \"${sed_i[@]}\" -e \"s/(+[0-9].*)$//g\" \"$file\"\ndone\n\necho \"Cleaned up rules.mk files.\"\n"
  },
  {
    "path": "util/sample_parser.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright 2019 Jack Humbert\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\n\nimport wave, struct, sys\n\nwaveFile = wave.open(sys.argv[1], 'r')\n# print(str(waveFile.getparams()))\n# sys.exit()\n\nif (waveFile.getsampwidth() != 2):\n    raise(Exception(\"This script currently only works with 16bit audio files\"))\n\nlength = waveFile.getnframes()\nout = \"#define DAC_SAMPLE_CUSTOM_LENGTH \" + str(length) + \"\\n\\n\"\nout += \"static const dacsample_t dac_sample_custom[\" + str(length) + \"] = {\"\nfor i in range(0,length):\n    if (i % 8 == 0):\n        out += \"\\n    \"\n    waveData = waveFile.readframes(1)\n    data = struct.unpack(\"<h\", waveData)\n    out += str(int((int(data[0]) + 0x8000) / 16)) + \", \"\nout = out[:-2]\nout += \"\\n};\"\nprint(out)\n"
  },
  {
    "path": "util/size_regression.sh",
    "content": "#!/bin/bash\n\n# Copyright 2021 Nick Brassel (@tzarc)\n# SPDX-License-Identifier: GPL-2.0-or-later\n\nset -eEuo pipefail\n\njob_count=$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 2)\nsource_ref=\"0.11.0\"\ndest_ref=\"develop\"\nignore_ref=\"master\"\nunset skip_zero\n\nexport SIZE_REGRESSION_EXECUTING=1\n\nfunction usage() {\n    echo \"Usage: $(basename \"$0\") [-h] [-j <jobs>] [-s <source>] [-d <dest>] [-n] planck/rev6:default\"\n    echo \"    -h           : Shows this usage page.\"\n    echo \"    -j <threads> : Change the number of threads to execute with. Defaults to \\`$job_count\\`.\"\n    echo \"    -s <source>  : Use source commit, branch, tag, or sha1 to start the search. Defaults to \\`$source_ref\\`.\"\n    echo \"    -d <dest>    : Use destination commit, branch, tag, or sha1 to end the search. Defaults to \\`$dest_ref\\`.\"\n    echo \"    -i <ignore>  : The branch to ignore refs from. Defaults to \\`$ignore_ref\\`.\"\n    echo \"    -n           : Skips printing changes if the delta is zero.\"\n    exit 1\n}\n\nif [[ ${#} -eq 0 ]]; then\n    usage\n    exit 0\nfi\n\nunset cleanup_completed\n_internal_cleanup() {\n    if [[ -z \"${cleanup_completed:-}\" ]] ; then\n        echo\n        echo\n        echo 'Your git repository is in an indeterminate state!' >&2\n        echo 'Make sure you swap to your intended branch.' >&2\n        echo\n        unset SIZE_REGRESSION_EXECUTING\n    fi\n    cleanup_completed=1\n}\ntrap _internal_cleanup EXIT HUP INT\n\nwhile getopts \"hj:s:d:i:n\" opt \"$@\" ; do\n    case \"$opt\" in\n        h) usage; exit 0;;\n        j) job_count=\"${OPTARG:-}\";;\n        s) source_ref=\"${OPTARG:-}\";;\n        d) dest_ref=\"${OPTARG:-}\";;\n        i) ignore_ref=\"${OPTARG:-}\";;\n        n) skip_zero=1;;\n        \\?) usage >&2; exit 1;;\n    esac\ndone\n\n# Work out the target board\nshift $((OPTIND-1))\nkeyboard_target=$1\n\n# Helper for resetting submodule existence\nfixup_submodules() {\n    [ -e lib/ugfx ] && rm -rf lib/ugfx\n    [ -e lib/pico-sdk ] && rm -rf lib/pico-sdk\n    [ -e lib/chibios-contrib/ext/mcux-sdk ] && rm -rf lib/chibios-contrib/ext/mcux-sdk\n    [ -e lib/lvgl ] && rm -rf lib/lvgl\n    make git-submodule\n}\n\nlast_size=0\nlast_line=\"\"\nfunction build_executor() {\n    git rev-list --oneline --no-merges ${source_ref}...${dest_ref} ^${ignore_ref} | while IFS= read -r line ; do\n        revision=$(echo $line | cut -d' ' -f1)\n\n        make distclean >/dev/null 2>&1\n\n        git checkout -f $revision >/dev/null 2>&1 || { echo \"Failed to check out revision ${revision}\" >&2 ; exit 1 ; }\n        fixup_submodules >/dev/null 2>&1\n        make -j${job_count} $keyboard_target >/dev/null 2>&1 || true\n        file_size=$(arm-none-eabi-size .build/*.elf 2>/dev/null | awk '/elf/ {print $1}' 2>/dev/null || true)\n\n        if [[ \"$last_size\" == 0 ]] ; then last_size=$file_size ; fi\n        if [[ -z \"$file_size\" ]] ; then file_size=0 ; fi\n\n        if [[ -n \"$last_line\" ]] ; then\n            size_delta=$(( $last_size - $file_size ))\n            if { [[ -n \"${skip_zero:-}\" ]] && [[ $size_delta -ne 0 ]] ; } || [[ -z \"${skip_zero:-}\" ]] || [[ $file_size -eq 0 ]] ; then\n                printf \"Size: %8d, delta: %+6d -- %s\\n\" \"$last_size\" \"$size_delta\" \"$last_line\"\n            fi\n        fi\n\n        last_size=$file_size\n        last_line=$line\n    done\n\n    if [ -n \"$last_line\" ] ; then\n        size_delta=0\n        printf \"Size: %8d, delta: %+6d -- %s\\n\" \"$last_size\" \"$size_delta\" \"$last_line\"\n    fi\n}\n\n# The actual execution of all the builds needs to be the last command in the entire script\n# - During builds, this script file will disappear, so we need the entire script to be\n#   loaded into the script interpreter at the time of execution. Do not refactor.\nbuild_executor\n"
  },
  {
    "path": "util/stm32eeprom_parser.py",
    "content": "#!/usr/bin/env python\n#\n# Copyright 2021 Don Kjer\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\n\nfrom __future__ import print_function\n\nimport argparse\nfrom struct import pack, unpack\nimport os, sys\n\nMAGIC_FEEA     = '\\xea\\xff\\xfe\\xff'\n\nMAGIC_FEE9     = '\\x16\\x01'\nEMPTY_WORD     = '\\xff\\xff'\nWORD_ENCODING  = 0x8000\nVALUE_NEXT     = 0x6000\nVALUE_RESERVED = 0x4000\nVALUE_ENCODED  = 0x2000\nBYTE_RANGE     = 0x80\n\nCHUNK_SIZE = 1024\n\nSTRUCT_FMTS = {\n    1: 'B',\n    2: 'H',\n    4: 'I'\n}\nPRINTABLE='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&\\'()*+,-./:;<=>?@[\\\\]^_`{|}~ '\n\nEECONFIG_V1 = [\n    (\"MAGIC\",                0, 2),\n    (\"DEBUG\",                2, 1),\n    (\"DEFAULT_LAYER\",        3, 1),\n    (\"KEYMAP\",               4, 1),\n    (\"MOUSEKEY_ACCEL\",       5, 1),\n    (\"BACKLIGHT\",            6, 1),\n    (\"AUDIO\",                7, 1),\n    (\"RGBLIGHT\",             8, 4),\n    (\"UNICODEMODE\",         12, 1),\n    (\"STENOMODE\",           13, 1),\n    (\"HANDEDNESS\",          14, 1),\n    (\"KEYBOARD\",            15, 4),\n    (\"USER\",                19, 4),\n    (\"VELOCIKEY\",           23, 1),\n    (\"HAPTIC\",              24, 4),\n    (\"MATRIX\",              28, 4),\n    (\"MATRIX_EXTENDED\",     32, 2),\n    (\"KEYMAP_UPPER_BYTE\",   34, 1),\n]\nVIABASE_V1 = 35\n\nVERBOSE = False\n\ndef parseArgs():\n    parser = argparse.ArgumentParser(description='Decode an STM32 emulated eeprom dump')\n    parser.add_argument('-s', '--size', type=int,\n                        help='Size of the emulated eeprom (default: input_size / 2)')\n    parser.add_argument('-o', '--output', help='File to write decoded eeprom to')\n    parser.add_argument('-y', '--layout-options-size', type=int,\n                        help='VIA layout options size (default: 1)', default=1)\n    parser.add_argument('-t', '--custom-config-size', type=int,\n                        help='VIA custom config size (default: 0)', default=0)\n    parser.add_argument('-l', '--layers', type=int,\n                        help='VIA keyboard layers (default: 4)', default=4)\n    parser.add_argument('-r', '--rows', type=int, help='VIA matrix rows')\n    parser.add_argument('-c', '--cols', type=int, help='VIA matrix columns')\n    parser.add_argument('-m', '--macros', type=int,\n                        help='VIA macro count (default: 16)', default=16)\n    parser.add_argument('-C', '--canonical', action='store_true',\n                        help='Canonical hex+ASCII display.')\n    parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output')\n    parser.add_argument('input', help='Raw contents of the STM32 flash area used to emulate eeprom')\n    return parser.parse_args()\n\n\ndef decodeEepromFEEA(in_file, size):\n    decoded=size*[None]\n    pos = 0\n    while True:\n        chunk = in_file.read(CHUNK_SIZE)\n        for i in range(0, len(chunk), 2):\n            decoded[pos] = unpack('B', chunk[i])[0]\n            pos += 1\n            if pos >= size:\n                break\n\n        if len(chunk) < CHUNK_SIZE or pos >= size:\n            break\n    return decoded\n\ndef decodeEepromFEE9(in_file, size):\n    decoded=size*[None]\n    pos = 0\n    # Read compacted flash \n    while True:\n        read_size = min(size - pos, CHUNK_SIZE)\n        chunk = in_file.read(read_size)\n        for i in range(len(chunk)):\n            decoded[pos] = unpack('B', chunk[i])[0] ^ 0xFF\n            pos += 1\n            if pos >= size:\n                break\n\n        if len(chunk) < read_size or pos >= size:\n            break\n    if VERBOSE:\n        print(\"COMPACTED EEPROM:\")\n        dumpBinary(decoded, True)\n        print(\"WRITE LOG:\")\n    # Read write log\n    while True:\n        entry = in_file.read(2)\n        if len(entry) < 2:\n            print(\"Partial log address at position 0x%04x\" % pos, file=sys.stderr)\n            break\n        pos += 2\n\n        if entry == EMPTY_WORD:\n            break\n\n        be_entry = unpack('>H', entry)[0]\n        entry = unpack('H', entry)[0]\n        if not (entry & WORD_ENCODING):\n            address = entry >> 8\n            decoded[address] = entry & 0xFF\n            if VERBOSE:\n                print(\"[0x%04x]: BYTE 0x%02x = 0x%02x\" % (be_entry, address, decoded[address]))\n        else:\n            if (entry & VALUE_NEXT) == VALUE_NEXT:\n                # Read next word as value\n                value = in_file.read(2)\n                if len(value) < 2:\n                    print(\"Partial log value at position 0x%04x\" % pos, file=sys.stderr)\n                    break\n                pos += 2\n                address = entry & 0x1FFF\n                address <<= 1\n                address += BYTE_RANGE\n                decoded[address]   = unpack('B', value[0])[0] ^ 0xFF\n                decoded[address+1] = unpack('B', value[1])[0] ^ 0xFF\n                be_value = unpack('>H', value)[0]\n                if VERBOSE:\n                    print(\"[0x%04x 0x%04x]: WORD 0x%04x = 0x%02x%02x\" % (be_entry, be_value, address, decoded[address+1], decoded[address]))\n            else:\n                # Reserved for future use\n                if entry & VALUE_RESERVED:\n                    if VERBOSE:\n                        print(\"[0x%04x]: RESERVED 0x%04x\" % (be_entry, address))\n                    continue\n                address = entry & 0x1FFF\n                address <<= 1\n                decoded[address]   = (entry & VALUE_ENCODED) >> 13\n                decoded[address+1] = 0\n                if VERBOSE:\n                    print(\"[0x%04x]: ENCODED 0x%04x = 0x%02x%02x\" % (be_entry, address, decoded[address+1], decoded[address]))\n\n    return decoded\n\ndef dumpBinary(data, canonical):\n    def display(pos, row):\n        print(\"%04x\" % pos, end='')\n        for i in range(len(row)):\n            if i % 8 == 0:\n                print(\" \", end='')\n            char = row[i]\n            if char is None:\n                print(\"   \", end='')\n            else:\n                print(\" %02x\" % row[i], end='')\n        if canonical:\n            print(\"  |\", end='')\n            for i in range(len(row)):\n                char = row[i]\n                if char is None:\n                    char = \" \"\n                else:\n                    char = chr(char)\n                if char not in PRINTABLE:\n                    char = \".\"\n                print(char, end='')\n            print(\"|\", end='')\n\n        print(\"\")\n\n    size = len(data)\n    prev_row = ''\n    first_repeat = True\n    for pos in range(0, size, 16):\n        row=data[pos:pos+16]\n        row[len(row):16] = (16-len(row))*[None]\n        if row == prev_row:\n            if first_repeat:\n                print(\"*\")\n                first_repeat = False\n        else:\n            first_repeat = True\n            display(pos, row)\n        prev_row = row\n    print(\"%04x\" % (pos+16))\n\ndef dumpEeconfig(data, eeconfig):\n    print(\"EECONFIG:\")\n    for (name, pos, length) in eeconfig:\n        fmt = STRUCT_FMTS[length]\n        value = unpack(fmt, ''.join([chr(x) for x in data[pos:pos+length]]))[0]\n        print((\"%%04x %%s = 0x%%0%dx\" % (length * 2)) % (pos, name, value))\n\ndef dumpVia(data, base, layers, cols, rows, macros,\n            layout_options_size, custom_config_size):\n    magicYear  = data[base + 0]\n    magicMonth = data[base + 1]\n    magicDay   = data[base + 2]\n    # Sanity check\n    if not 10 <= magicYear <= 0x99 or \\\n       not 0 <= magicMonth <= 0x12 or \\\n       not 0 <= magicDay <= 0x31:\n        print(\"ERROR: VIA Signature is not valid; Year:%x, Month:%x, Day:%x\" % (magicYear, magicMonth, magicDay))\n        return\n    if cols is None or rows is None:\n        print(\"ERROR: VIA dump requires specifying --rows and --cols\", file=sys.stderr)\n        return 2\n    print(\"VIA:\")\n    # Decode magic\n    print(\"%04x MAGIC = 20%02x-%02x-%02x\" % (base, magicYear, magicMonth, magicDay))\n    # Decode layout options\n    options = 0\n    pos = base + 3\n    for i in range(base+3, base+3+layout_options_size):\n        options = options << 8\n        options |= data[i]\n    print((\"%%04x LAYOUT_OPTIONS = 0x%%0%dx\" % (layout_options_size * 2)) % (pos, options))\n    pos += layout_options_size + custom_config_size\n    # Decode keycodes\n    keymap_size = layers * rows * cols * 2\n    if (pos + keymap_size) >= (len(data) - 1):\n        print(\"ERROR: VIA keymap requires %d bytes, but only %d available\" % (keymap_size, len(data) - pos))\n        return 3\n    for layer in range(layers):\n        print(\"%s LAYER %d %s\" % ('-'*int(cols*2.5), layer, '-'*int(cols*2.5)))\n        for row in range(rows):\n            print(\"%04x  | \" % pos, end='')\n            for col in range(cols):\n                keycode = (data[pos] << 8) | (data[pos+1])\n                print(\" %04x\" % keycode, end='')\n                pos += 2\n            print(\"\")\n    # Decode macros\n    for macro_num in range(macros):\n        macro = \"\"\n        macro_pos = pos\n        while pos < len(data):\n            char = chr(data[pos])\n            pos += 1\n            if char == '\\x00':\n                print(\"%04x MACRO[%d] = '%s'\" % (macro_pos, macro_num, macro))\n                break\n            else:\n                macro += char\n    return 0\n\n\ndef decodeSTM32Eeprom(input, canonical, size=None, output=None, **kwargs):\n    input_size = os.path.getsize(input)\n    if size is None:\n        size = input_size >> 1\n\n    # Read the first few bytes to check magic signature\n    with open(input, 'rb') as in_file:\n        magic=in_file.read(4)\n        in_file.seek(0)\n\n        if magic == MAGIC_FEEA:\n            decoded = decodeEepromFEEA(in_file, size)\n            eeconfig = EECONFIG_V1\n            via_base = VIABASE_V1\n        elif magic[:2] == MAGIC_FEE9:\n            decoded = decodeEepromFEE9(in_file, size)\n            eeconfig = EECONFIG_V1\n            via_base = VIABASE_V1\n        else:\n            print(\"Unknown magic signature: %s\" % \" \".join([\"0x%02x\" % ord(x) for x in magic]), file=sys.stderr)\n            return 1\n\n    if output is not None:\n        with open(output, 'wb') as out_file:\n            out_file.write(pack('%dB' % len(decoded), *decoded))\n    print(\"DECODED EEPROM:\")\n    dumpBinary(decoded, canonical)\n    dumpEeconfig(decoded, eeconfig)\n    if kwargs['rows'] is not None and kwargs['cols'] is not None:\n        return dumpVia(decoded, via_base, **kwargs)\n\n    return 0\n\ndef main():\n    global VERBOSE\n    kwargs = vars(parseArgs())\n    VERBOSE = kwargs.pop('verbose')\n    return decodeSTM32Eeprom(**kwargs)\n\nif __name__ == '__main__':\n    sys.exit(main())\n"
  },
  {
    "path": "util/udev/50-qmk.rules",
    "content": "# Atmel DFU\n### ATmega16U2\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"03eb\", ATTRS{idProduct}==\"2fef\", TAG+=\"uaccess\"\n### ATmega32U2\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"03eb\", ATTRS{idProduct}==\"2ff0\", TAG+=\"uaccess\"\n### ATmega16U4\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"03eb\", ATTRS{idProduct}==\"2ff3\", TAG+=\"uaccess\"\n### ATmega32U4\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"03eb\", ATTRS{idProduct}==\"2ff4\", TAG+=\"uaccess\"\n### AT90USB64\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"03eb\", ATTRS{idProduct}==\"2ff9\", TAG+=\"uaccess\"\n### AT90USB162\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"03eb\", ATTRS{idProduct}==\"2ffa\", TAG+=\"uaccess\"\n### AT90USB128\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"03eb\", ATTRS{idProduct}==\"2ffb\", TAG+=\"uaccess\"\n\n# Input Club\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1c11\", ATTRS{idProduct}==\"b007\", TAG+=\"uaccess\"\n\n# STM32duino\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1eaf\", ATTRS{idProduct}==\"0003\", TAG+=\"uaccess\"\n# STM32 DFU\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"0483\", ATTRS{idProduct}==\"df11\", TAG+=\"uaccess\"\n\n# BootloadHID\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"16c0\", ATTRS{idProduct}==\"05df\", TAG+=\"uaccess\"\n\n# USBAspLoader\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"16c0\", ATTRS{idProduct}==\"05dc\", TAG+=\"uaccess\"\n\n# USBtinyISP\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1782\", ATTRS{idProduct}==\"0c9f\", TAG+=\"uaccess\"\n\n# ModemManager should ignore the following devices\n# Atmel SAM-BA (Massdrop)\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"03eb\", ATTRS{idProduct}==\"6124\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n\n# Caterina (Pro Micro)\n## pid.codes shared PID\n### Keyboardio Atreus 2 Bootloader\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1209\", ATTRS{idProduct}==\"2302\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n## Spark Fun Electronics\n### Pro Micro 3V3/8MHz\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1b4f\", ATTRS{idProduct}==\"9203\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n### Pro Micro 5V/16MHz\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1b4f\", ATTRS{idProduct}==\"9205\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n### LilyPad 3V3/8MHz (and some Pro Micro clones)\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1b4f\", ATTRS{idProduct}==\"9207\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n## Pololu Electronics\n### A-Star 32U4\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"1ffb\", ATTRS{idProduct}==\"0101\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n## Arduino SA\n### Leonardo\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2341\", ATTRS{idProduct}==\"0036\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n### Micro\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2341\", ATTRS{idProduct}==\"0037\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n## Adafruit Industries LLC\n### Feather 32U4\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"239a\", ATTRS{idProduct}==\"000c\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n### ItsyBitsy 32U4 3V3/8MHz\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"239a\", ATTRS{idProduct}==\"000d\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n### ItsyBitsy 32U4 5V/16MHz\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"239a\", ATTRS{idProduct}==\"000e\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n## dog hunter AG\n### Leonardo\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2a03\", ATTRS{idProduct}==\"0036\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n### Micro\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2a03\", ATTRS{idProduct}==\"0037\", TAG+=\"uaccess\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n\n# hid_listen\nKERNEL==\"hidraw*\", MODE=\"0660\", GROUP=\"plugdev\", TAG+=\"uaccess\", TAG+=\"udev-acl\"\n\n# hid bootloaders\n## QMK HID\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"03eb\", ATTRS{idProduct}==\"2067\", TAG+=\"uaccess\"\n## PJRC's HalfKay\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"16c0\", ATTRS{idProduct}==\"0478\", TAG+=\"uaccess\"\n\n# APM32 DFU\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"314b\", ATTRS{idProduct}==\"0106\", TAG+=\"uaccess\"\n\n# GD32V DFU\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"28e9\", ATTRS{idProduct}==\"0189\", TAG+=\"uaccess\"\n\n# WB32 DFU\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"342d\", ATTRS{idProduct}==\"dfa0\", TAG+=\"uaccess\"\n\n# AT32 DFU\nSUBSYSTEMS==\"usb\", ATTRS{idVendor}==\"2e3c\", ATTRS{idProduct}==\"df11\", TAG+=\"uaccess\"\n"
  },
  {
    "path": "util/uf2conv.py",
    "content": "#!/usr/bin/env python3\n# yapf: disable\nimport sys\nimport struct\nimport subprocess\nimport re\nimport os\nimport os.path\nimport argparse\nimport json\nfrom time import sleep\n\n\nUF2_MAGIC_START0 = 0x0A324655 # \"UF2\\n\"\nUF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected\nUF2_MAGIC_END    = 0x0AB16F30 # Ditto\n\nINFO_FILE = \"/INFO_UF2.TXT\"\n\nappstartaddr = 0x2000\nfamilyid = 0x0\n\n\ndef is_uf2(buf):\n    w = struct.unpack(\"<II\", buf[0:8])\n    return w[0] == UF2_MAGIC_START0 and w[1] == UF2_MAGIC_START1\n\ndef is_hex(buf):\n    try:\n        w = buf[0:30].decode(\"utf-8\")\n    except UnicodeDecodeError:\n        return False\n    if w[0] == ':' and re.match(rb\"^[:0-9a-fA-F\\r\\n]+$\", buf):\n        return True\n    return False\n\ndef convert_from_uf2(buf):\n    global appstartaddr\n    global familyid\n    numblocks = len(buf) // 512\n    curraddr = None\n    currfamilyid = None\n    families_found = {}\n    prev_flag = None\n    all_flags_same = True\n    outp = []\n    for blockno in range(numblocks):\n        ptr = blockno * 512\n        block = buf[ptr:ptr + 512]\n        hd = struct.unpack(b\"<IIIIIIII\", block[0:32])\n        if hd[0] != UF2_MAGIC_START0 or hd[1] != UF2_MAGIC_START1:\n            print(\"Skipping block at \" + ptr + \"; bad magic\")\n            continue\n        if hd[2] & 1:\n            # NO-flash flag set; skip block\n            continue\n        datalen = hd[4]\n        if datalen > 476:\n            assert False, \"Invalid UF2 data size at \" + ptr\n        newaddr = hd[3]\n        if (hd[2] & 0x2000) and (currfamilyid == None):\n            currfamilyid = hd[7]\n        if curraddr == None or ((hd[2] & 0x2000) and hd[7] != currfamilyid):\n            currfamilyid = hd[7]\n            curraddr = newaddr\n            if familyid == 0x0 or familyid == hd[7]:\n                appstartaddr = newaddr\n        padding = newaddr - curraddr\n        if padding < 0:\n            assert False, \"Block out of order at \" + ptr\n        if padding > 10*1024*1024:\n            assert False, \"More than 10M of padding needed at \" + ptr\n        if padding % 4 != 0:\n            assert False, \"Non-word padding size at \" + ptr\n        while padding > 0:\n            padding -= 4\n            outp.append(b\"\\x00\\x00\\x00\\x00\")\n        if familyid == 0x0 or ((hd[2] & 0x2000) and familyid == hd[7]):\n            outp.append(block[32 : 32 + datalen])\n        curraddr = newaddr + datalen\n        if hd[2] & 0x2000:\n            if hd[7] in families_found.keys():\n                if families_found[hd[7]] > newaddr:\n                    families_found[hd[7]] = newaddr\n            else:\n                families_found[hd[7]] = newaddr\n        if prev_flag == None:\n            prev_flag = hd[2]\n        if prev_flag != hd[2]:\n            all_flags_same = False\n        if blockno == (numblocks - 1):\n            print(\"--- UF2 File Header Info ---\")\n            families = load_families()\n            for family_hex in families_found.keys():\n                family_short_name = \"\"\n                for name, value in families.items():\n                    if value == family_hex:\n                        family_short_name = name\n                print(\"Family ID is {:s}, hex value is 0x{:08x}\".format(family_short_name,family_hex))\n                print(\"Target Address is 0x{:08x}\".format(families_found[family_hex]))\n            if all_flags_same:\n                print(\"All block flag values consistent, 0x{:04x}\".format(hd[2]))\n            else:\n                print(\"Flags were not all the same\")\n            print(\"----------------------------\")\n            if len(families_found) > 1 and familyid == 0x0:\n                outp = []\n                appstartaddr = 0x0\n    return b\"\".join(outp)\n\ndef convert_to_carray(file_content):\n    outp = \"const unsigned long bindata_len = %d;\\n\" % len(file_content)\n    outp += \"const unsigned char bindata[] __attribute__((aligned(16))) = {\"\n    for i in range(len(file_content)):\n        if i % 16 == 0:\n            outp += \"\\n\"\n        outp += \"0x%02x, \" % file_content[i]\n    outp += \"\\n};\\n\"\n    return bytes(outp, \"utf-8\")\n\ndef convert_to_uf2(file_content):\n    global familyid\n    datapadding = b\"\"\n    while len(datapadding) < 512 - 256 - 32 - 4:\n        datapadding += b\"\\x00\\x00\\x00\\x00\"\n    numblocks = (len(file_content) + 255) // 256\n    outp = []\n    for blockno in range(numblocks):\n        ptr = 256 * blockno\n        chunk = file_content[ptr:ptr + 256]\n        flags = 0x0\n        if familyid:\n            flags |= 0x2000\n        hd = struct.pack(b\"<IIIIIIII\",\n            UF2_MAGIC_START0, UF2_MAGIC_START1,\n            flags, ptr + appstartaddr, 256, blockno, numblocks, familyid)\n        while len(chunk) < 256:\n            chunk += b\"\\x00\"\n        block = hd + chunk + datapadding + struct.pack(b\"<I\", UF2_MAGIC_END)\n        assert len(block) == 512\n        outp.append(block)\n    return b\"\".join(outp)\n\nclass Block:\n    def __init__(self, addr, default_data=0xFF):\n        self.addr = addr\n        self.bytes = bytearray([default_data] * 256)\n\n    def encode(self, blockno, numblocks):\n        global familyid\n        flags = 0x0\n        if familyid:\n            flags |= 0x2000\n        if devicetype:\n            flags |= 0x8000\n        hd = struct.pack(\"<IIIIIIII\",\n            UF2_MAGIC_START0, UF2_MAGIC_START1,\n            flags, self.addr, 256, blockno, numblocks, familyid)\n        hd += self.bytes[0:256]\n        if devicetype:\n            hd += bytearray(b'\\x08\\x29\\xa7\\xc8')\n            hd += bytearray(devicetype.to_bytes(4, 'little'))\n        while len(hd) < 512 - 4:\n            hd += b\"\\x00\"\n        hd += struct.pack(\"<I\", UF2_MAGIC_END)\n        return hd\n\ndef convert_from_hex_to_uf2(buf):\n    global appstartaddr\n    appstartaddr = None\n    upper = 0\n    currblock = None\n    blocks = []\n    for line in buf.split('\\n'):\n        if line[0] != \":\":\n            continue\n        i = 1\n        rec = []\n        while i < len(line) - 1:\n            rec.append(int(line[i:i+2], 16))\n            i += 2\n        tp = rec[3]\n        if tp == 4:\n            upper = ((rec[4] << 8) | rec[5]) << 16\n        elif tp == 2:\n            upper = ((rec[4] << 8) | rec[5]) << 4\n        elif tp == 1:\n            break\n        elif tp == 0:\n            addr = upper + ((rec[1] << 8) | rec[2])\n            if appstartaddr == None:\n                appstartaddr = addr\n            i = 4\n            while i < len(rec) - 1:\n                if not currblock or currblock.addr & ~0xff != addr & ~0xff:\n                    currblock = Block(addr & ~0xff)\n                    blocks.append(currblock)\n                currblock.bytes[addr & 0xff] = rec[i]\n                addr += 1\n                i += 1\n    numblocks = len(blocks)\n    resfile = b\"\"\n    for i in range(0, numblocks):\n        resfile += blocks[i].encode(i, numblocks)\n    return resfile\n\ndef to_str(b):\n    return b.decode(\"utf-8\")\n\ndef get_drives():\n    drives = []\n    if sys.platform == \"win32\":\n        r = subprocess.check_output([\n            \"powershell\",\n            \"-Command\",\n            '(Get-WmiObject Win32_LogicalDisk -Filter \"FileSystem=\\'FAT\\'\").DeviceID'\n            ])\n        drives = [drive.strip() for drive in to_str(r).splitlines()]\n    else:\n        searchpaths = [\"/mnt\", \"/media\"]\n        if sys.platform == \"darwin\":\n            searchpaths = [\"/Volumes\"]\n        elif sys.platform == \"linux\":\n            searchpaths += [\"/media/\" + os.environ[\"USER\"], \"/run/media/\" + os.environ[\"USER\"]]\n            if \"SUDO_USER\" in os.environ.keys():\n                searchpaths += [\"/media/\" + os.environ[\"SUDO_USER\"]]\n                searchpaths += [\"/run/media/\" + os.environ[\"SUDO_USER\"]]\n\n        for rootpath in searchpaths:\n            if os.path.isdir(rootpath):\n                for d in os.listdir(rootpath):\n                    if os.path.isdir(os.path.join(rootpath, d)):\n                        drives.append(os.path.join(rootpath, d))\n\n\n    def has_info(d):\n        try:\n            return os.path.isfile(d + INFO_FILE)\n        except:\n            return False\n\n    return list(filter(has_info, drives))\n\n\ndef board_id(path):\n    with open(path + INFO_FILE, mode='r') as file:\n        file_content = file.read()\n    return re.search(r\"Board-ID: ([^\\r\\n]*)\", file_content).group(1)\n\n\ndef list_drives():\n    for d in get_drives():\n        print(d, board_id(d))\n\n\ndef write_file(name, buf):\n    with open(name, \"wb\") as f:\n        f.write(buf)\n    print(\"Wrote %d bytes to %s\" % (len(buf), name))\n\n\ndef load_families():\n    # The expectation is that the `uf2families.json` file is in the same\n    # directory as this script. Make a path that works using `__file__`\n    # which contains the full path to this script.\n    filename = \"uf2families.json\"\n    pathname = os.path.join(os.path.dirname(os.path.abspath(__file__)), filename)\n    with open(pathname) as f:\n        raw_families = json.load(f)\n\n    families = {}\n    for family in raw_families:\n        families[family[\"short_name\"]] = int(family[\"id\"], 0)\n\n    return families\n\n\ndef main():\n    global appstartaddr, familyid\n    def error(msg):\n        print(msg, file=sys.stderr)\n        sys.exit(1)\n    parser = argparse.ArgumentParser(description='Convert to UF2 or flash directly.')\n    parser.add_argument('input', metavar='INPUT', type=str, nargs='?',\n                        help='input file (HEX, BIN or UF2)')\n    parser.add_argument('-b', '--base', dest='base', type=str,\n                        default=\"0x2000\",\n                        help='set base address of application for BIN format (default: 0x2000)')\n    parser.add_argument('-f', '--family', dest='family', type=str,\n                        default=\"0x0\",\n                        help='specify familyID - number or name (default: 0x0)')\n    parser.add_argument('-t' , '--device-type', dest='devicetype', type=str,\n                        help='specify deviceTypeID extension tag - number')\n    parser.add_argument('-o', '--output', metavar=\"FILE\", dest='output', type=str,\n                        help='write output to named file; defaults to \"flash.uf2\" or \"flash.bin\" where sensible')\n    parser.add_argument('-d', '--device', dest=\"device_path\",\n                        help='select a device path to flash')\n    parser.add_argument('-l', '--list', action='store_true',\n                        help='list connected devices')\n    parser.add_argument('-c', '--convert', action='store_true',\n                        help='do not flash, just convert')\n    parser.add_argument('-D', '--deploy', action='store_true',\n                        help='just flash, do not convert')\n    parser.add_argument('-w', '--wait', action='store_true',\n                        help='wait for device to flash')\n    parser.add_argument('-C', '--carray', action='store_true',\n                        help='convert binary file to a C array, not UF2')\n    parser.add_argument('-i', '--info', action='store_true',\n                        help='display header information from UF2, do not convert')\n    args = parser.parse_args()\n    appstartaddr = int(args.base, 0)\n\n    families = load_families()\n\n    if args.family.upper() in families:\n        familyid = families[args.family.upper()]\n    else:\n        try:\n            familyid = int(args.family, 0)\n        except ValueError:\n            error(\"Family ID needs to be a number or one of: \" + \", \".join(families.keys()))\n\n    global devicetype\n    devicetype = int(args.devicetype, 0) if args.devicetype else None\n\n    if args.list:\n        list_drives()\n    else:\n        if not args.input:\n            error(\"Need input file\")\n        with open(args.input, mode='rb') as f:\n            inpbuf = f.read()\n        from_uf2 = is_uf2(inpbuf)\n        ext = \"uf2\"\n        if args.deploy:\n            outbuf = inpbuf\n        elif from_uf2 and not args.info:\n            outbuf = convert_from_uf2(inpbuf)\n            ext = \"bin\"\n        elif from_uf2 and args.info:\n            outbuf = \"\"\n            convert_from_uf2(inpbuf)\n        elif is_hex(inpbuf):\n            outbuf = convert_from_hex_to_uf2(inpbuf.decode(\"utf-8\"))\n        elif args.carray:\n            outbuf = convert_to_carray(inpbuf)\n            ext = \"h\"\n        else:\n            outbuf = convert_to_uf2(inpbuf)\n        if not args.deploy and not args.info:\n            print(\"Converted to %s, output size: %d, start address: 0x%x\" %\n                  (ext, len(outbuf), appstartaddr))\n        if args.convert or ext != \"uf2\":\n            if args.output == None:\n                args.output = \"flash.\" + ext\n        if args.output:\n            write_file(args.output, outbuf)\n        if ext == \"uf2\" and not args.convert and not args.info:\n            drives = get_drives()\n            if len(drives) == 0:\n                if args.wait:\n                    print(\"Waiting for drive to deploy...\")\n                    while len(drives) == 0:\n                        sleep(0.1)\n                        drives = get_drives()\n                elif not args.output:\n                    error(\"No drive to deploy.\")\n            for d in drives:\n                print(\"Flashing %s (%s)\" % (d, board_id(d)))\n                write_file(d + \"/NEW.UF2\", outbuf)\n\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "util/uf2families.json",
    "content": "[\n    {\n        \"id\": \"0x16573617\",\n        \"short_name\": \"ATMEGA32\",\n        \"description\": \"Microchip (Atmel) ATmega32\"\n    },\n    {\n        \"id\": \"0x1851780a\",\n        \"short_name\": \"SAML21\",\n        \"description\": \"Microchip (Atmel) SAML21\"\n    },\n    {\n        \"id\": \"0x1b57745f\",\n        \"short_name\": \"NRF52\",\n        \"description\": \"Nordic NRF52\"\n    },\n    {\n        \"id\": \"0x1c5f21b0\",\n        \"short_name\": \"ESP32\",\n        \"description\": \"ESP32\"\n    },\n    {\n        \"id\": \"0x1e1f432d\",\n        \"short_name\": \"STM32L1\",\n        \"description\": \"ST STM32L1xx\"\n    },\n    {\n        \"id\": \"0x202e3a91\",\n        \"short_name\": \"STM32L0\",\n        \"description\": \"ST STM32L0xx\"\n    },\n    {\n        \"id\": \"0x21460ff0\",\n        \"short_name\": \"STM32WL\",\n        \"description\": \"ST STM32WLxx\"\n    },\n    {\n        \"id\": \"0x22e0d6fc\",\n        \"short_name\": \"RTL8710B\",\n        \"description\": \"Realtek AmebaZ RTL8710B\"\n    },\n    {\n        \"id\": \"0x2abc77ec\",\n        \"short_name\": \"LPC55\",\n        \"description\": \"NXP LPC55xx\"\n    },\n    {\n        \"id\": \"0x300f5633\",\n        \"short_name\": \"STM32G0\",\n        \"description\": \"ST STM32G0xx\"\n    },\n    {\n        \"id\": \"0x31d228c6\",\n        \"short_name\": \"GD32F350\",\n        \"description\": \"GD32F350\"\n    },\n    {\n        \"id\": \"0x3379CFE2\",\n        \"short_name\": \"RTL8720D\",\n        \"description\": \"Realtek AmebaD RTL8720D\"\n    },\n    {\n        \"id\": \"0x04240bdf\",\n        \"short_name\": \"STM32L5\",\n        \"description\": \"ST STM32L5xx\"\n    },\n    {\n        \"id\": \"0x4c71240a\",\n        \"short_name\": \"STM32G4\",\n        \"description\": \"ST STM32G4xx\"\n    },\n    {\n        \"id\": \"0x4fb2d5bd\",\n        \"short_name\": \"MIMXRT10XX\",\n        \"description\": \"NXP i.MX RT10XX\"\n    },\n    {\n        \"id\": \"0x51e903a8\",\n        \"short_name\": \"XR809\",\n        \"description\": \"Xradiotech 809\"\n    },\n    {\n        \"id\": \"0x53b80f00\",\n        \"short_name\": \"STM32F7\",\n        \"description\": \"ST STM32F7xx\"\n    },\n    {\n        \"id\": \"0x55114460\",\n        \"short_name\": \"SAMD51\",\n        \"description\": \"Microchip (Atmel) SAMD51\"\n    },\n    {\n        \"id\": \"0x57755a57\",\n        \"short_name\": \"STM32F4\",\n        \"description\": \"ST STM32F4xx\"\n    },\n    {\n        \"id\": \"0x5a18069b\",\n        \"short_name\": \"FX2\",\n        \"description\": \"Cypress FX2\"\n    },\n    {\n        \"id\": \"0x5d1a0a2e\",\n        \"short_name\": \"STM32F2\",\n        \"description\": \"ST STM32F2xx\"\n    },\n    {\n        \"id\": \"0x5ee21072\",\n        \"short_name\": \"STM32F1\",\n        \"description\": \"ST STM32F103\"\n    },\n    {\n        \"id\": \"0x621e937a\",\n        \"short_name\": \"NRF52833\",\n        \"description\": \"Nordic NRF52833\"\n    },\n    {\n        \"id\": \"0x647824b6\",\n        \"short_name\": \"STM32F0\",\n        \"description\": \"ST STM32F0xx\"\n    },\n    {\n        \"id\": \"0x675a40b0\",\n        \"short_name\": \"BK7231U\",\n        \"description\": \"Beken 7231U/7231T\"\n    },\n    {\n        \"id\": \"0x68ed2b88\",\n        \"short_name\": \"SAMD21\",\n        \"description\": \"Microchip (Atmel) SAMD21\"\n    },\n    {\n        \"id\": \"0x6a82cc42\",\n        \"short_name\": \"BK7251\",\n        \"description\": \"Beken 7251/7252\"\n    },\n    {\n        \"id\": \"0x6b846188\",\n        \"short_name\": \"STM32F3\",\n        \"description\": \"ST STM32F3xx\"\n    },\n    {\n        \"id\": \"0x6d0922fa\",\n        \"short_name\": \"STM32F407\",\n        \"description\": \"ST STM32F407\"\n    },\n    {\n        \"id\": \"0x4e8f1c5d\",\n        \"short_name\": \"STM32H5\",\n        \"description\": \"ST STM32H5xx\"\n    },\n    {\n        \"id\": \"0x6db66082\",\n        \"short_name\": \"STM32H7\",\n        \"description\": \"ST STM32H7xx\"\n    },\n    {\n        \"id\": \"0x70d16653\",\n        \"short_name\": \"STM32WB\",\n        \"description\": \"ST STM32WBxx\"\n    },\n    {\n        \"id\": \"0x7b3ef230\",\n        \"short_name\": \"BK7231N\",\n        \"description\": \"Beken 7231N\"\n    },\n    {\n        \"id\": \"0x7eab61ed\",\n        \"short_name\": \"ESP8266\",\n        \"description\": \"ESP8266\"\n    },\n    {\n        \"id\": \"0x7f83e793\",\n        \"short_name\": \"KL32L2\",\n        \"description\": \"NXP KL32L2x\"\n    },\n    {\n        \"id\": \"0x8fb060fe\",\n        \"short_name\": \"STM32F407VG\",\n        \"description\": \"ST STM32F407VG\"\n    },\n    {\n        \"id\": \"0x9fffd543\",\n        \"short_name\": \"RTL8710A\",\n        \"description\": \"Realtek Ameba1 RTL8710A\"\n    },\n    {\n        \"id\": \"0xada52840\",\n        \"short_name\": \"NRF52840\",\n        \"description\": \"Nordic NRF52840\"\n    },\n    {\n        \"id\": \"0x820d9a5f\",\n        \"short_name\": \"NRF52820\",\n        \"description\": \"Nordic NRF52820_xxAA\"\n    },\n    {\n        \"id\": \"0xbfdd4eee\",\n        \"short_name\": \"ESP32S2\",\n        \"description\": \"ESP32-S2\"\n    },\n    {\n        \"id\": \"0xc47e5767\",\n        \"short_name\": \"ESP32S3\",\n        \"description\": \"ESP32-S3\"\n    },\n    {\n        \"id\": \"0xd42ba06c\",\n        \"short_name\": \"ESP32C3\",\n        \"description\": \"ESP32-C3\"\n    },\n    {\n        \"id\": \"0x2b88d29c\",\n        \"short_name\": \"ESP32C2\",\n        \"description\": \"ESP32-C2\"\n    },\n    {\n        \"id\": \"0x332726f6\",\n        \"short_name\": \"ESP32H2\",\n        \"description\": \"ESP32-H2\"\n    },\n    {\n        \"id\": \"0x540ddf62\",\n        \"short_name\": \"ESP32C6\",\n        \"description\": \"ESP32-C6\"\n    },\n    {\n        \"id\": \"0x3d308e94\",\n        \"short_name\": \"ESP32P4\",\n        \"description\": \"ESP32-P4\"\n    },\n    {\n        \"id\": \"0xf71c0343\",\n        \"short_name\": \"ESP32C5\",\n        \"description\": \"ESP32-C5\"\n    },\n    {\n        \"id\": \"0x77d850c4\",\n        \"short_name\": \"ESP32C61\",\n        \"description\": \"ESP32-C61\"\n    },\n    {\n        \"id\": \"0xb6dd00af\",\n        \"short_name\": \"ESP32H21\",\n        \"description\": \"ESP32-H21\"\n    },\n    {\n        \"id\": \"0x9e0baa8a\",\n        \"short_name\": \"ESP32H4\",\n        \"description\": \"ESP32-H4\"\n    },\n    {\n        \"id\": \"0xde1270b7\",\n        \"short_name\": \"BL602\",\n        \"description\": \"Boufallo 602\"\n    },\n    {\n        \"id\": \"0xe08f7564\",\n        \"short_name\": \"RTL8720C\",\n        \"description\": \"Realtek AmebaZ2 RTL8720C\"\n    },\n    {\n        \"id\": \"0xe48bff56\",\n        \"short_name\": \"RP2040\",\n        \"description\": \"Raspberry Pi RP2040\"\n    },\n    {\n        \"id\": \"0xe48bff57\",\n        \"short_name\": \"RP2XXX_ABSOLUTE\",\n        \"description\": \"Raspberry Pi Microcontrollers: Absolute (unpartitioned) download\"\n    },\n    {\n        \"id\": \"0xe48bff58\",\n        \"short_name\": \"RP2XXX_DATA\",\n        \"description\": \"Raspberry Pi Microcontrollers: Data partition download\"\n    },\n    {\n        \"id\": \"0xe48bff59\",\n        \"short_name\": \"RP2350_ARM_S\",\n        \"description\": \"Raspberry Pi RP2350, Secure Arm image\"\n    },\n    {\n        \"id\": \"0xe48bff5a\",\n        \"short_name\": \"RP2350_RISCV\",\n        \"description\": \"Raspberry Pi RP2350, RISC-V image\"\n    },\n    {\n        \"id\": \"0xe48bff5b\",\n        \"short_name\": \"RP2350_ARM_NS\",\n        \"description\": \"Raspberry Pi RP2350, Non-secure Arm image\"\n    },\n    {\n        \"id\": \"0x00ff6919\",\n        \"short_name\": \"STM32L4\",\n        \"description\": \"ST STM32L4xx\"\n    },\n    {\n        \"id\": \"0x9af03e33\",\n        \"short_name\": \"GD32VF103\",\n        \"description\": \"GigaDevice GD32VF103\"\n    },\n    {\n        \"id\": \"0x4f6ace52\",\n        \"short_name\": \"CSK4\",\n        \"description\": \"LISTENAI CSK300x/400x\"\n    },\n    {\n        \"id\": \"0x6e7348a8\",\n        \"short_name\": \"CSK6\",\n        \"description\": \"LISTENAI CSK60xx\"\n    },\n    {\n        \"id\": \"0x11de784a\",\n        \"short_name\": \"M0SENSE\",\n        \"description\": \"M0SENSE BL702\"\n    },\n    {\n        \"id\": \"0x4b684d71\",\n        \"short_name\": \"MaixPlay-U4\",\n        \"description\": \"Sipeed MaixPlay-U4(BL618)\"\n    },\n    {\n        \"id\": \"0x9517422f\",\n        \"short_name\": \"RZA1LU\",\n        \"description\": \"Renesas RZ/A1LU (R7S7210xx)\"\n    },\n    {\n        \"id\": \"0x2dc309c5\",\n        \"short_name\": \"STM32F411xE\",\n        \"description\": \"ST STM32F411xE\"\n    },\n    {\n        \"id\": \"0x06d1097b\",\n        \"short_name\": \"STM32F411xC\",\n        \"description\": \"ST STM32F411xC\"\n    },\n    {\n        \"id\": \"0x72721d4e\",\n        \"short_name\": \"NRF52832xxAA\",\n        \"description\": \"Nordic NRF52832xxAA\"\n    },\n    {\n        \"id\": \"0x6f752678\",\n        \"short_name\": \"NRF52832xxAB\",\n        \"description\": \"Nordic NRF52832xxAB\"\n    },\n    {\n        \"id\": \"0xa0c97b8e\",\n        \"short_name\": \"AT32F415\",\n        \"description\": \"ArteryTek AT32F415\"\n    },\n    {\n        \"id\": \"0x699b62ec\",\n        \"short_name\": \"CH32V\",\n        \"description\": \"WCH CH32V2xx and CH32V3xx\"\n    },\n    {\n        \"id\": \"0x7be8976d\",\n        \"short_name\": \"RA4M1\",\n        \"description\": \"Renesas RA4M1\"\n    },\n    {\n        \"id\": \"0x7410520a\",\n        \"short_name\": \"MAX32690\",\n        \"description\": \"Analog Devices MAX32690\"\n    },\n    {\n        \"id\": \"0xd63f8632\",\n        \"short_name\": \"MAX32650\",\n        \"description\": \"Analog Devices MAX32650/1/2\"\n    },\n    {\n        \"id\": \"0xf0c30d71\",\n        \"short_name\": \"MAX32666\",\n        \"description\": \"Analog Devices MAX32665/6\"\n    },\n    {\n        \"id\": \"0x91d3fd18\",\n        \"short_name\": \"MAX78002\",\n        \"description\": \"Analog Devices MAX78002\"\n    }\n]\n"
  },
  {
    "path": "util/update_chibios_mirror.sh",
    "content": "#!/bin/bash\n\n################################\n# Configuration\n\n# The ChibiOS branches to mirror\nchibios_branches=\"trunk stable_21.11.x\"\n\n# The ChibiOS-Contrib branches to mirror\ncontrib_branches=\"chibios-21.11.x\"\n\n################################\n# Actions\n\nset -eEuo pipefail\numask 022\n\nthis_script=\"$(realpath \"${BASH_SOURCE[0]}\")\"\nscript_dir=\"$(realpath \"$(dirname \"$this_script\")\")\"\nqmk_firmware_dir=\"$(realpath \"$script_dir/../\")\"\nchibios_dir=\"$qmk_firmware_dir/lib/chibios\"\ncontrib_dir=\"$qmk_firmware_dir/lib/chibios-contrib\"\n\nchibios_git_location=$(realpath \"$chibios_dir/$(cat \"$chibios_dir/.git\" | awk '/gitdir:/ {print $2}')\")\nchibios_git_config=$(realpath \"$chibios_git_location/config\")\ncontrib_git_location=$(realpath \"$contrib_dir/$(cat \"$contrib_dir/.git\" | awk '/gitdir:/ {print $2}')\")\ncontrib_git_config=$(realpath \"$contrib_git_location/config\")\n\ncd \"$chibios_dir\"\n\nif [[ -z \"$(cat \"$chibios_git_config\" | grep '\\[svn-remote \"svn\"\\]')\" ]] ; then\n    git svn init --stdlayout --prefix='svn/' https://svn.code.sf.net/p/chibios/code/\nfi\n\nif [[ -z \"$(cat \"$chibios_git_config\" | grep '\\[remote \"qmk\"\\]')\" ]] ; then\n    git remote add qmk git@github.com:qmk/ChibiOS.git\n    git remote set-url qmk git@github.com:qmk/ChibiOS.git --push\nelse\n    git remote set-url qmk git@github.com:qmk/ChibiOS.git\n    git remote set-url qmk git@github.com:qmk/ChibiOS.git --push\nfi\n\necho \"Updating remotes...\"\ngit fetch --all --tags --prune\n\necho \"Ensure refs actually match up...\"\nfor branch in $chibios_branches ; do\n    echo \"Matching $branch...\"\n    git update-ref refs/remotes/svn/$branch refs/remotes/qmk/svn-mirror/$branch\ndone\n\necho \"Fetching latest from subversion...\"\ngit svn fetch\n\necho \"Updating ChibiOS branches...\"\nfor branch in $chibios_branches ; do\n    echo \"Creating branch 'svn-mirror/$branch' from 'svn/$branch'...\"\n    git branch -f svn-mirror/$branch svn/$branch \\\n        && git push qmk svn-mirror/$branch\ndone\n\ncd \"$contrib_dir\"\n\nif [[ -z \"$(cat \"$contrib_git_config\" | grep '\\[remote \"qmk\"\\]')\" ]] ; then\n    git remote add qmk git@github.com:qmk/ChibiOS-Contrib.git\n    git remote set-url qmk git@github.com:qmk/ChibiOS-Contrib.git --push\nelse\n    git remote set-url qmk git@github.com:qmk/ChibiOS-Contrib.git\n    git remote set-url qmk git@github.com:qmk/ChibiOS-Contrib.git --push\nfi\n\nif [[ -z \"$(cat \"$contrib_git_config\" | grep '\\[remote \"upstream\"\\]')\" ]] ; then\n    git remote add upstream git@github.com:ChibiOS/ChibiOS-Contrib.git\n    git remote set-url upstream git@github.com:ChibiOS/ChibiOS-Contrib.git --push\nelse\n    git remote set-url upstream git@github.com:ChibiOS/ChibiOS-Contrib.git\n    git remote set-url upstream git@github.com:ChibiOS/ChibiOS-Contrib.git --push\nfi\n\necho \"Updating remotes...\"\ngit fetch --all --tags --prune\n\necho \"Updating ChibiOS-Contrib branches...\"\nfor branch in $contrib_branches ; do\n    echo \"Creating branch 'mirror/$branch' from 'upstream/$branch'...\"\n    git branch -f mirror/$branch upstream/$branch \\\n        && git push qmk mirror/$branch || true # Allow for nonexistent ChibiOS-Contrib branches -- they'll turn up eventually.\ndone\n"
  },
  {
    "path": "util/usb_detach/Makefile",
    "content": "# the compiler: gcc for C program, define as g++ for C++\nCC = gcc\n\n# compiler flags:\n#  -g    adds debugging information to the executable file\n#  -Wall turns on most, but not all, compiler warnings\nCFLAGS  = -g -Wall\n\n# the build target executable:\nTARGET = usb_detach\n\nall: $(TARGET)\n\n$(TARGET): $(TARGET).c\n\t$(CC) $(CFLAGS) -o $(TARGET) $(TARGET).c\n\nclean:\n\t$(RM) $(TARGET)\n"
  },
  {
    "path": "util/usb_detach/readme.md",
    "content": "# usb_detach\n\nWhen trying to flash on Linux, you may encounter a \"Resource Unavailable\" error. This means that Linux's HID driver has taken exclusive control of the keyboard, and the program script can't flash it.\nThis program can force Linux to give up a device, so that the programming script can reset it.\n\n## To compile:\n```bash\nmake clean && make\n```\n\n## To run:\n1. Use `lsusb` to discover the Bus and Device numbers for your keyboard.\n2. Run the program: `sudo ./usb_detach /dev/bus/usb/<BUS>/<DEVICE>`.\n3. Build and program the firmware as normal.\n"
  },
  {
    "path": "util/usb_detach/usb_detach.c",
    "content": "/* Found at https://www.linuxquestions.org/questions/linux-hardware-18/how-to-unclaim-usb-device-558138/#post3406986 */\n#include <stdio.h>\n#include <sys/ioctl.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <linux/ioctl.h>\n#include <linux/usbdevice_fs.h>\n\nint main(int argc, char**argv)\n{\n        struct usbdevfs_ioctl command;\n        int ret;\n        int fd;\n        int i;\n        if (argc>1) {\n                fd = open(argv[1],O_RDWR);\n                if (fd<1){\n                        perror(\"unable to open file\");\n                        return 1;\n                }\n                for (i=0;i<255;i++){ // hack: should fetch how many interface there is.\n                        command.ifno = i;\n                        command.ioctl_code = USBDEVFS_DISCONNECT;\n                        command.data = NULL;\n                        ret = ioctl(fd, USBDEVFS_IOCTL, &command);\n                        if(ret!=-1)\n                                printf(\"un claimed interface %d %d\\n\",i,ret);\n                }\n        } else {\n                printf (\"usage: %s /dev/bus/usb/BUS/DEVICE\\n\",argv[0]);\n                printf(\"Release all interfaces of this usb device for usage in virtualisation\\n\");\n        }\n}\n"
  },
  {
    "path": "util/wavetable_parser.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright 2019 Jack Humbert\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 2 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with this program.  If not, see <http://www.gnu.org/licenses/>.\n#\n\nimport wave, struct, sys\n\nwaveFile = wave.open(sys.argv[1], 'r')\n\nlength = waveFile.getnframes()\nout = \"#define DAC_WAVETABLE_CUSTOM_LENGTH \" + str(int(length / 256)) + \"\\n\\n\"\nout += \"static const dacsample_t dac_wavetable_custom[\" + str(int(length / 256)) + \"][256] = {\"\nfor i in range(0,length):\n    if (i % 8 == 0):\n        out += \"\\n    \"\n    if (i % 256 == 0):\n        out = out[:-2]\n        out += \"{\\n    \"\n    waveData = waveFile.readframes(1)\n    data = struct.unpack(\"<h\", waveData)\n    out += str(int((int(data[0]) + 0x8000) / 16)) + \", \"\n    if (i % 256 == 255):\n        out = out[:-2]\n        out += \"\\n  },\"\nout = out[:-1]\nout += \"\\n};\"\nprint(out)\n"
  }
]